This question is a follow-up of this previous one. I decided to solve the linear elasticity \begin{cases}- \nabla \sigma(u)=f \\ u=0 \text{ on } \partial \Omega\end{cases}
with P1 Lagrangian finite elements on the square $[-1,1]^2$. Here I want to highlight and ask for a confirmation the crucial steps, that were totally new to me. The convergence plot seems to indicate order $2$ in $L^2$ and $1$ in $H^1$, w.r.t the number of DoFs. I'll attach as less code as possible, but what I want to be sure is if my design is correct.
Discaimer: I know it's not efficient, etc. The purpose of this is to understand how a vector valued problem can be solved.
I decided to use vector basis functions where each vector function has only a non-zero component. Therefore, in the reference triangle I have, for each node, 2 basis functions. Take as example the first node, indexed by $1$ (I'm using MatLab). There, I have $\Phi_1=[\phi_1(\boldsymbol{x}),0]^T$ and $\Phi_2=[0,\phi_1(\boldsymbol{x})]^T$. Since I have three nodes for $P1$, I have 6 basis functions in the reference.
I know I have to use a linear index in order to enumerate all these functions. In particular, now I have twice the DoFs. In C++ (or Python), the choice I am aware is that the new indices are given by $$n=2N +d$$ where $d \in\{0,1\}$, $N$ is the global node number.
In MatLab, I changed this with $$n=2(N-1) + d \qquad (\star)$$ with $d \in \{1,2\}$
- The contributions coming from nodes $(i,j)$ are the classical ones: $$\int_\Omega \bar{\varepsilon}(\Phi_i) C \bar{\varepsilon}(\Phi_j)$$ where $C$ is the matrix $$C = \frac{E}{ 1 - \nu^2}\begin{bmatrix} 1 & \nu & 0 \\ \nu & 1 & 0 \\ 0& 0& \frac{1 - \nu}{2}\end{bmatrix}$$
and $ \bar{\varepsilon}=[ \varepsilon_{xx},\varepsilon_{yy},\varepsilon_{xy}]$ where we have the usual relations between strain and displacement $u=[u_1,u_2]$, namely: $$\varepsilon_{xx}(u) = \partial_x u_1 \\ \varepsilon_{yy}(u)= \partial_y u_2 \\ \varepsilon_{xy}(u)= \partial_{x} u_2 + \partial_{y} u_1$$
Assembly routine
I loop over all triangles, and for each triangle $K$ I get the global indices for the three DoFs. Then, I convert those indices using $(\star)$ and hence my local_to_global has 6 components that have to be distributed.
Then, inside the quadrature loop, I collect the values and the gradients of each vector basis function $\Phi_i$. To achieve what I wrote at the beginning, I basically have $$\Phi_i = \phi_{i//2}(\boldsymbol{x}) \boldsymbol{e}_{i\%2}$$ In code, this is (
fix
does the integer division)%S are the values of shape functions at quadrature points %dSdx,dSdy gradients of shape functions compute via isoperimetric map for comp=1:6 if(mod(comp,2)==0) value = [0;S(fix(comp/2))]; %test function is [0,v_comp] gradient = [0,0;dSdx(fix(comp/2)),dSdy(fix(comp/2))]; elseif(mod(comp,2)==1) value = [S(fix(comp/2)+1);0]; %test function is [v_comp,0] gradient = [dSdx(fix(comp/2)+1),dSdy(fix(comp/2)+1);0,0]; end Phis = [Phis,value]; Gradients = [Gradients,gradient]; end
So, $\Phi_3=[\phi_2,0]$ and $\Phi_4=[0,\phi_2]$ are the two shape functions associated to the 2nd node on the reference triangle.
Then, I looped over $i,j$ running both from 1 to 6 and distributed the contributions. In MatLab code, it looks like this:
for i=1:6 phi_i = Phis(:,i); grad_phi_i = Gradients(:,[2*(i-1)+1,2*(i-1)+2]); grad_v_1 = grad_phi_i(1,:); grad_v_2 = grad_phi_i(2,:); for j=1:6 grad_phi_j = Gradients(:,[2*(j-1)+1,2*(j-1)+2]); grad_u_1 = grad_phi_j(1,:); grad_u_2 = grad_phi_j(2,:); %here sigma, sigma_2 are E/(1-nu^2) and (1-nu)/2 coefficients val = sigma * (... grad_v_1(1) * (grad_u_1(1) + nu * grad_u_2(2))... + ... grad_v_2(2) * (nu * grad_u_1(1) + grad_u_2(2)) ... + ... (grad_v_1(2) + grad_v_2(1)) * ... (sigma_2 * (grad_u_1(2) + grad_u_2(1)))... ) * JxW; A(l2g(i),l2g(j)) = A(l2g(i),l2g(j)) + val; end val_rhs = dot(force(xp,yp),phi_i)*JxW; F(l2g(i)) = F(l2g(i)) + val_rhs; end
After the global matrix has been built, I just zero-out rows and columns corresponding to boundary DoFs and place a $1$ in the diagonal and a $0$ in the forcing term as I have homogeneous Dirichlet.
Test
I chose the well known test-case for which the solution is $u=[(x^2-1)(y^2-1), (x^2-1)(y^2 - 1)]$, i.e. $$\boldsymbol{f}=\frac{E}{1 - \nu^2}[-2y^2-x^2+ \nu x^2 - 2 \nu xy - 2 xy + 3 - \nu,-2x^2 - y^2+ \nu y^2 - 2 \nu xy - 2 xy + 3 - \nu]$$
and here's the EOC in $L^2, H^1$ for the first component.