Stress divergence after elastic deformation does not equal 0, but it should


According to the analytical solution (link) for a deflection of a cantilever beam, the divergence of the stress field should equal 0, but my FEM simulations do not result in zero.


A simple two-dimensional cantilever beam is taken. Displacements on one side of the beam are fixed using the analytical solution, similar applies to the other side of the beam where traction boundary is assigned using the analytical solution.

FreeFEM solution

I have used this example to get the freefem code. The only changes I did is that I wrote the full expressions for the ux and uy.

 // Parameters
 real E = 3.0e7, nu = 0.3;
 real P = -1000;
 real L = 48, D = 12;
 real I = D^3/12;
 // Mesh
 mesh Th = square(40,10,[L*x,-D/2+D*y]);
 // Macros
 macro u [ux,uy] // displacement
 macro v [vx,vy] // test function
 macro div(u) (dx(u[0])+dy(u[1])) // divergence
 macro eM(u) [dx(u[0]), dy(u[1]), sqrt(2)*(dx(u[1]) + dy(u[0]))/2] // strain tensor
 // Finite element space
 fespace Vh(Th,[P2,P2]);
 Vh u, v;
 // Boundary conditions
 func uxb = -P*y/(6.*E*I)*(3.*(x*x - L*L) - (2.+nu)*y*y + 6.*(1.+nu)*D*D/4.);
 func uyb = P/(6.*E*I)*(3. * nu * x * y * y + x*x*x - 3.*L*L*x + 2.*L*L*L);
 func ty = -P/(2*I)*(D^2/4-y^2);
 // Convert E and nu to plane stress
 E = E/(1-nu^2); nu = nu/(1-nu);
 // Lame parameters
 real mu = E/(2*(1+nu));
 real lambda = E*nu/((1+nu)*(1-2*nu));
 // Solve problem
 solve cantileverBeam(u,v) =
    int2d(Th)(lambda*div(u)*div(v) + 2*mu*(eM(u)'*eM(v)))
    - int1d(Th,4)([0,-ty]'*v)
    + on(2,ux=uxb,uy=uyb);
 // Plot solution
 real coef = 1000;
 Th = movemesh(Th,[x+ux*coef,y+uy*coef]);

This results in a deflected beam that seems to be ok.

Modifications to the code

So to compute the divergence of the stress field I have to be able to compute the stresses sxx, syy and sxy. Therefore, I added a bit of macro, including the definition of the divergence of the tensor field.

 fespace Nh(Th, P2);
 Nh sxx, sxy, syy;
 Nh rx, ry, r;

 macro e(u)
		0.5 * (dy(u[0]) + dx(u[1])),
		0.5 * (dx(u[1]) + dy(u[0])), 
	] // EOM
 macro sigma(u)
		(lambda + 2. * mu) * e(u)[0] + lambda * e(u)[3],
		2. * mu * e(u)[2],
		lambda * e(u)[0] + (lambda + 2. * mu) * e(u)[3]
	] // EOM
macro stressdiv(Sxx, Syy, Sxy) [dx(Sxx) + dy(Sxy), 
                                dx(Sxy) + dy(Syy)] // EOM

sxx = sigma(u)[0];
sxy = sigma(u)[1];
syy = sigma(u)[2];

rx = stressdiv(sxx, syy, sxy)[0];
ry = stressdiv(sxx, syy, sxy)[1];

for (int i = 0; i < rx.n; i++){
    r[](i) = dist(rx[](i), ry[](i));
cout << r[] << endl;

plot(sxx, wait=1, cmm="sxx");
plot(syy, wait=1, cmm="syy");
plot(sxy, wait=1, cmm="sxy");
plot([rx, ry], wait=1, cmm="residuum");


However, the results are far from the expected. The analytical solution claims the divergence of the stress field should be zero, but the numerical simulations using the code above result in non-zero values:

I am also not sure if these numbers are “small” or “negliable”. I think the divergence equals to the density of the internal forces. If that is the case, I don’t think these numbers are small unless it is safe to compare them to the loading force P.


So, of course, this makes little to no sense to me. I would like to understand if there is a bug in my code or understand the reason why the divergence is not zero?
Since the deformation is elastic, I think the divergence equals to density of internal forces which in stationary state should most likely be zero, but again, simulations show otherwise.