This is a read only copy of the old FEniCS QA forum. Please visit the new QA forum to ask questions

Non-convergence in solving linear elasticity problem obtained by differentiating the free energy function

0 votes

I am trying to solve a small-strain linear elasticity problem, where I am attempting to obtain the stress expression from the Helmholtz free energy

$$\psi\,(\varepsilon) = \frac{1}{2} \kappa\, \mathrm{tr}\,(\varepsilon)^2 + \mu \, \mathrm{dev}\,(\varepsilon) : \mathrm{dev}\,(\varepsilon)$$

where the Green-Lagrange strain tensor is defined by

$$\varepsilon = \mathrm{sym}\,(\mathrm{grad}\,(u))$$

When I call solve, the nonlinear solver is activated, and cannot converge to a solution. Nevertheless, when I directly input the stress and not enclose eps in a variable(), I obtain the solution as normal (you can test by uncommenting the relevant lines below). What am I doing wrong?

Another question: When I look into the Fenics Book, I see some finite strain examples that build the problem by differentiating the free energy with respect to a corresponding strain, e.g. "Figure 17.1: UFL implementation of hyperelasticity equations with a Mooney-Rivlin material law.".

In the demos of the most recent version (2016.1), however, I did not come upon any problems that utilizes this technique. In the hyperelasticity demo, for example, the Gateaux derivative of the total potential energy form Pi = psi*dx ... is taken to obtain the bilinear form for the solution, rather than the bilinear form being constructed from the stress and the strain. Is this way more preferred now in Fenics programs?

from __future__ import print_function
from dolfin import *

# Sub domain for clamp at left end
def left(x, on_boundary):
    return x[0] < 0.001 and on_boundary

# Sub domain for rotation at right end
def right(x, on_boundary):
    return x[0] > 0.99 and on_boundary

# Load mesh and define function space
mesh = UnitCubeMesh(15, 15, 15)

# Define function space
V = VectorFunctionSpace(mesh, "CG", 1)
scalar_space = FunctionSpace(mesh,'CG',1)
tensor_space = TensorFunctionSpace(mesh, "CG", 1)

# Test and trial functions

v = TestFunction(V)
u = TrialFunction(V)
u_ = Function(V)

kappa = 10.
mu = 10.

# External forces (body and applied tractions
f  = Constant((10.0, 0.0, 0.0))

boundary_subdomains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary_subdomains.set_all(0)
force_boundary = AutoSubDomain(right)
force_boundary.mark(boundary_subdomains, 3)

# Define measure for boundary condition integral
dss = ds(subdomain_data=boundary_subdomains)

# Set up boundary condition at left end
zero = Constant((0.0, 0.0, 0.0))
bcs = DirichletBC(V, zero, left)

eps = variable(sym(grad(u)))
psi =  kappa / 2. * tr(eps) ** 2 + mu * inner(dev(eps), dev(eps))
sigma = diff(psi, eps)

# Uncommenting this yields the correct solution
# eps = sym(grad(u))
# sigma = kappa * tr(eps) * Identity(len(u)) + 2.0 * mu * dev(eps)

a = inner(sigma, sym(grad(v)))*dx
L = inner(f, v)*dss(3)

F = a - L
F = action(F, u_)

solve(F == 0, u_, bcs, solver_parameters={"newton_solver": {"linear_solver": "gmres"}},)

# Output displacement

xdmf_file = XDMFFile(mesh.mpi_comm(), "nonlinear_elasticity.xdmf")
u_.rename("u", "displacement")
xdmf_file.write(u_, 0.0)
asked Oct 21, 2016 by hosolmaz FEniCS User (1,510 points)

Hi, I am not an expert but the matrices you get by assembling a in the two cases are not identical

eps = variable(sym(grad(u)))
psi =  kappa / 2. * tr(eps) ** 2 + mu * inner(dev(eps), dev(eps))
sigma = diff(psi, eps)

# Uncommenting this yields the correct solution
eps1 = sym(grad(u))
sigma1 = kappa * tr(eps1) * Identity(len(u)) + 2.0 * mu * dev(eps1)

a = inner(sigma, sym(grad(v)))*dx
a1 = inner(sigma1, sym(grad(v)))*dx

A = assemble(a)
A1 = assemble(a1)

print (A-A1).norm('linf')

But you claim that they should be, correct?

I cannot run your code, because the norm function appears to have been deprecated in the version I am using; but the matrices ought to be the same indeed. If that is not the case it explains why it does not converge.

So why is that? Because the expressions should be identical. Either there is an error in diff, or I am using it wrong.

Just to be sure, could you try with PETSc, i.e.

A, A1 = map(lambda form: as_backend_type(assemble(form)).mat(), (a, a1))
A.axpy(-1., A1)
print A.norm(0)

Sorry I don't really have time to investigate the issue. But yes, this could be an issue with diff.

...