Download this page as a Matlab LiveScript
Table of contents
So we want to approximate the function
using piecewise elements of order 1 or 2.
This means finding all nodal values to the approximation (the blue circles in the figure above). This problem can be formulated using Galerkin as such: Find such that
with
Now, this is the same old Galerkin expression as we had in the previous section. In order to apply a piecewise approximation, we shall further expand the expressions to formulate a matrix system!
Expanding the expression in the integral we get
move out
which is equivalent to the matrix system
or
Solving this system gets us all nodal values for .
To describe any mesh we need a coordinate matrix (vector in 1D)
and a nodal connectivity matrix
4 linear elements will lead to 5 equations to be solved for
An element between nodes and contributes only to equations and . We can use the 'th row of to see which nodes are associated with the element .
Example: Element mass matrix for element 3 between node 3 and 4:
We can use local node numbering to simplify things:
Now, for each element we define and , compute and then compute and
Element stiffness matrix
or
and element load vector
or
Utilizing a piecewise approximation leads to the element mass matrix and element load vector:
and
which need to be assembled into the global mass matrix and load vectors.
For each element , we add and to the correct place in and .
Element 1: M([1,2],[1,2])
Element 2: M([2,3],[2,3])
Element 3: M([3,4],[3,4])
Element 4: M([4,5],[4,5])
We need to add each element contribution to the global mass matrix.
or on matrix form
where row denotes the array containing the row indices and col the column indices. Thus we could write
to mean a sub-matrix of with rows and and columns and .
The corresponding Matlab (or Julia) syntax is
M([i,j],[i,j]) = M([i,j],[i,j]) + Me
All nodes that have several elements will get contributions from each element. We are "accumulating" mass to these nodes from the elements.
Contribution from element 1
Contribution from element 2
etc
For four linear elements, this leads to the equation system
We end up with the linear system of equations
which is solved by using a linear equations solver, in Matlab we do this by
u = M\f
We will do this in code now using linear element and symbolic integration.
clear
syms x
ue(x) = 2*x*sin(2*pi*x)+3;
N = 4;
nnod = N+1;
xnod = linspace(0,1,N+1);
nodes = [(1:nnod-1)', (2:nnod)'];
Phi = @(x,x1,x2) [(x2-x)/(x2-x1), (x-x1)/(x2-x1)];
M = zeros(nnod,nnod); F = zeros(nnod,1);
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
x1 = xc(1); x2 = xc(2);
phi = @(x)Phi(x,x1, x2);
Me = int(phi(x).'*phi(x), x,x1,x2);
fe = int(phi(x).'*ue(x), x,x1,x2);
M(inod,inod) = M(inod,inod) + Me;
F(inod) = F(inod) + fe;
end
u = M\F;
figure
fplot(ue,[0,1],'r','LineWidth',2); hold on
plot(xnod,u,'-bo')
Now, we will compute the average error between the exact function and our Galerkin approximation, we shall use the norm as a measure of error. This basicaly means squaring the difference between the functions over the entire domain and taking the square root.
So we need to compute the difference between the function for each element and adding it all up.
where
L2err = 0;
for iel = 1:N
inod = nodes(iel,:);
x1 = xnod(inod(1)); x2 = xnod(inod(2));
phi = @(x)Phi(x,x1, x2);
U = phi(x)*u(inod);
L2err = L2err + double(int((U-ue)^2, x,x1,x2));
end
L2err = sqrt(L2err)
L2err = 0.1106
Now we can start changing to see how the error goes down. If we plot versus the error we get a convergence plot.
clear
syms x
ue(x) = 2*x*sin(2*pi*x)+3;
NN = [2,4,8,16,32];
L2Err = [];
Time = [];
figure
fplot(ue,[0,1],'r','LineWidth',2); hold on
hp = plot(NaN,NaN,'-b.');
for N = NN
tic
nnod = N+1;
xnod = linspace(0,1,N+1);
nodes = [(1:nnod-1)', (2:nnod)'];
Phi = @(x,x1,x2) [(x2-x)/(x2-x1), (x-x1)/(x2-x1)];
M = zeros(nnod,nnod); F = zeros(nnod,1);
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
x1 = xc(1); x2 = xc(2);
phi = @(x)Phi(x,x1, x2);
Me = vpaintegral(phi(x).'*phi(x), x,x1,x2);
fe = vpaintegral(phi(x).'*ue(x), x,x1,x2);
M(inod,inod) = M(inod,inod) + Me;
F(inod) = F(inod) + fe;
end
u = M\F;
t1 = toc;
Time(end+1) = t1;
L2err = 0;
for iel = 1:N
inod = nodes(iel,:);
x1 = xnod(inod(1)); x2 = xnod(inod(2));
phi = @(x)Phi(x,x1, x2);
U = phi(x)*u(inod);
L2err = L2err + double(vpaintegral((U-ue)^2, x,x1,x2));
end
L2err = sqrt(L2err);
L2Err(end+1) = L2err;
set(hp,'XData',xnod, 'YData',u);
title(sprintf("Approximation using %d linear elements",N))
pause(0.1)
drawnow
end
syms x1 x2 x3 x
A = [1 x1 x1^2
1 x2 x2^2
1 x3 x3^2];
C = A\eye(3);
Phi = simplify([1 x x^2]*C);
Phi = matlabFunction(Phi);
Time2 = []; L2Err2 = [];
figure;
for N = NN
tic
nnod = N*2+1;
xnod = linspace(0,1,nnod);
nodes = [(1:2:nnod-2)', (2:2:nnod-1)', (3:2:nnod)'];
neq = nnod*1;
M = zeros(nnod,nnod); F = zeros(nnod,1);
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
x1 = xc(1); x2 = xc(2); x3 = xc(3);
phi = @(x)Phi(x,x1, x2, x3);
Me = vpaintegral(phi(x).'*phi(x), x,x1,x3);
fe = vpaintegral(phi(x).'*ue(x), x,x1,x3);
M(inod,inod) = M(inod,inod) + Me;
F(inod) = F(inod) + fe;
end
u = M\F;
t1 = toc;
Time2(end+1) = t1;
L2err = 0;
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
x1 = xc(1); x2 = xc(2); x3 = xc(3);
phi = @(x)Phi(x,x1, x2, x3);
U = phi(x)*u(inod);
L2err = L2err + double(vpaintegral((U-ue)^2, x,x1,x3));
end
L2err = sqrt(L2err);
L2Err2(end+1) = L2err;
clf
fplot(ue,[0,1],'r','LineWidth',1);
hold on
for iel = 1:N
inod = nodes(iel,:);
xc = xnod(inod);
x1 = xc(1); x2 = xc(2); x3 = xc(3);
phi = @(x)Phi(x,x1, x2, x3);
U = phi(x)*u(inod);
fplot(U, [x1,x3])
plot(xc,u(inod),'.b');
end
title(sprintf("Approximation using %d quadratic elements",N))
% if N == 2
% exportgraphics(gca,"PiecewiseQuadratic.gif","Append",false)
% end
% for i = 1:10
% exportgraphics(gca,"PiecewiseQuadratic.gif","Append",true)
% end
end
figure; hold on
plot(NN,L2Err,'-ko','DisplayName','Linear')
plot(NN,L2Err2,'-ks','DisplayName','Quadratic')
set(gca,'xscale','log','yscale','log')
xlabel("N"); ylabel("L_2 error")
xticks(NN)
xticklabels({'2','4','8','16','32'})
grid on
legend show
Note how the quadratic approximation not only has a lower initial error but also a steeper slope, meaning it converges faster.
figure; hold on
plot(NN,Time,'-ko','DisplayName','Linear')
plot(NN,Time2,'-ks','DisplayName','Quadratic')
set(gca,'xscale','log','yscale','log')
xlabel("N"); ylabel("Time (s)")
xticks(NN)
xticklabels({'2','4','8','16','32'})
yticks([0.5,1,2,4,8,16])
grid on
legend("show",location="northwest")