I have a Hermite Cubic Finite Element Space on a computer in the form of Matlab m-files. More specifically, I can evaluate four "shape functions" $N_1, N_2, N_3,$ and $N_4$, for which the following holds on the boundary of the "reference element" $K_R= [-1,1]$: \begin{align*} N_1(-1) = 1, N_1(1) = 0, N_1'(-1) = 0, N_1'(1) = 0,\\ N_2(-1) = 0, N_2(1) = 1, N_2'(-1) = 0, N_2'(1) = 0,\\ N_3(-1) = 0, N_3(1) = 0, N_3'(-1) = 1, N_3'(1) = 0,\\ N_4(-1) = 0, N_4(1) = 0, N_4'(-1) = 0, N_4'(1) = 1. \end{align*} Here is what they look like:
I can map an arbitrary element $K = [a,b]$ to $K_R$, evaluate the shape functions over $K_R$, then "map back", so that the above conditions on my shape functions hold on the boundary of arbitrary elements.
I want to use this Finite Element Space to approximate $u(x) = \sin(2\pi x)$ for $x \in \Omega = [2,3]$. To do so, I complete the following steps:
- Make an evenly spaced Mesh of the domain.
- Make relevant data structures needed to access the shape functions.
- Create an array of values of $u(x)$ evaluated at the Mesh nodes.
- Loop over each element: Extract the values of $u(x)$ at the Mesh node for this element $K$ with $u_1 = u(a)$ and $u_2 = u(b)$, where $a$ and $b$ are the endpoints of the element. Then do $u(x) \approx u_1 N_1(x) + u_2 N_2(x) + u_1 N_3(x) + u_2 N_4(x)$ for x = linspace(a,b,resolution). Then plot the result.
My problem is the Hermite Cubic Finite Element Space doesn't approximate $\sin(2 \pi x)$ very well using this method; the approximation wiggles a lot. Below is my plot using a Mesh with 100 elements, and using a resolution of 21.
I repeated the same procedure as above except using a Lagrange Cubic Finite Element Space, so that the shape functions over $K_R$ satisfy \begin{align*} N_1(-1) = 1, N_1(-1/3) = 0, N_1(1/3) = 0, N_1(1) = 0, \\ N_2(-1) = 0, N_2(-1/3) = 1, N_2(1/3) = 0, N_2(1) = 0, \\ N_3(-1) = 0, N_3(-1/3) = 0, N_3(1/3) = 1, N_3(1) = 0, \\ N_4(-1) = 0, N_4(-1/3) = 0, N_4(1/3) = 0, N_4(1) = 1. \end{align*}
Here is what the Lagrange shape functions look like:
With $u_1, u_2, u_3, u_4$ corresponding to $-1, -1/3, 1/3, 1$, the approximation I used on each element was $u(x) \approx u_1 N_1(x) + u_2 N_2(x) + u_3 N_3(x) + u_4 N_4(x)$. Doing this gets me much nicer results:
What is the deal? Why does the Lagrange FE space work so nice, and the Hermite FE space fail? This might be more of a "numerical analysis" question rather than a "scientific computing" question, but I thought I would try here first.
EDIT:
One of the comments suggested to use the formula $u(x) \approx u_1 N_1(x) + u_2 N_2(x) + u_1' N_3(x) + u_2' N_4(x)$ instead of $u(x) \approx u_1 N_1(x) + u_2 N_2(x) + u_1 N_3(x) + u_2 N_4 (x)$. I agree that using this formula is what I should have done in this first place. I implement this by computing $u_1' = 2\pi \cos(2\pi a)$ and $u_2' = 2\pi \cos(2\pi b)$ for each element $K = [a,b]$. Then my Hermite approximation to $\sin(2 \pi x)$ looks like:
Which is clearly very wrong. In case it helps, here is the MATLAB code for my problem. I use a few of my own finite element computer programs here.
% Make the mesh:
domain = [2,3]; nElement = 100;
Mesh = mesh_generator_1d(domain, nElement);
% Form a data structure to access the finite element basis:
iDegree = 3; Fem = femherm1d(Mesh, iDegree); hDegree = 1;
% Create an array with the first 100 entries corresponding to the values
% of sin(2*pi*x) evaluated at the Mesh nodes:
uGlobal = sin(2*pi*Fem.point(1:nElement+1)); iDerivative = 0;
% Extend the array so that the last 100 entries correspond to the values of
% 2*pi*cos(2*pi*x) evaluated at the Mesh nodes:
uGlobal(nElement+2:2*(nElement+1)) = 2*pi*cos(2*pi*Fem.point(nElement+2:2*(nElement+1)));
% Set the resolution and prepare the figure:
reso = 20; figure(1); clf; hold on;
uInterpolate = zeros(1,(reso+1)*size(Mesh.element,2));
for k = 1:size(Mesh.element,2) % Loop over each element in the mesh
element = Mesh.node(Mesh.element(:,k)); % Extract element endpoints
uLocal = uGlobal(Fem.T(:,k)); % uLocal(1) = u1, uLocal(2) = u2, uLocal(3) = u1', uLocal(4) = u2'
x = linspace(element(1), element(2), reso+1); % x contains the points we are evaluating the shape functions at
uInterpolate(((k-1)*(reso+1)+1):(k*(reso+1))) = evalfeherm1d(x, uLocal, ...
element, hDegree, iDerivative); % uInterpolate = u1*N1 + u2*N2 + u1'*N3 + u2'*N4
plot(x,uInterpolate(((k-1)*(reso+1)+1):(k*(reso+1)))); % Plot the result
end
hold off