Skip to content

Commit a4b201b

Browse files
authored
Add Example 55: Updated Lagrange hyperelasticity with documentation a… (#1207)
1 parent 4315b1e commit a4b201b

4 files changed

Lines changed: 215 additions & 4 deletions

File tree

docs/examples/ex43.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
.. math::
6767
6868
\Delta\delta\psi(\boldsymbol{F}) = \mu \Delta\boldsymbol{F} :
69-
\delta\boldsymbol{F} + (\lambda \ln(J) - \mu)
69+
\delta\boldsymbol{F} - (\lambda \ln(J) - \mu)
7070
\text{tr}(\Delta\boldsymbol{F} \boldsymbol{F}^{-1}
7171
\delta\boldsymbol{F} \boldsymbol{F}^{-1})
7272
+ \lambda \text{tr}(\delta\boldsymbol{F} \boldsymbol{F}^{-1})

docs/examples/ex55.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
r"""Hyperelasticity in Updated-Lagrange formulation.
2+
3+
This example implements the hyperelastic problem of excercise 43 in the Updated Lagrange formulation.
4+
The strain energy density function per unit undeformed volume of the
5+
isotropic hyperelastic Neo-Hookean material is given by
6+
7+
.. math::
8+
9+
\psi(\boldsymbol{F}) = \frac{\mu}{2} ( I_1 - 3) - \mu \ln(J) +
10+
\frac{\lambda}{2} \ln^2(J)
11+
12+
This time we formulate the first invariant in terms of the left Cauchy-Green deformation tensor
13+
and the determinant of the deformation gradient (volume change).
14+
15+
.. math::
16+
17+
I_1 &= \text{Tr}(\boldsymbol{F} \boldsymbol{F}^T) = \text{Tr}(\boldsymbol{b}) \\
18+
J &= \det(\boldsymbol{F}) = \sqrt{\det(\boldsymbol{b})}
19+
20+
For the Updated Lagrange formulation we formulate the weak form in terms of the current configuration
21+
22+
.. math::
23+
24+
0 = \int_{\omega} \frac{1}{J}\boldsymbol{\tau}(\vec{u}) : \vec{\nabla}(\vec{v}) \, dv
25+
26+
With the Kirchhoff stress
27+
28+
.. math::
29+
30+
\boldsymbol{\tau} = 2 \frac{\partial \psi}{\partial \boldsymbol{b}}\cdot\boldsymbol{b}
31+
= \mu \left[\boldsymbol{b}-\boldsymbol{1}\right]+\lambda\ln(J)\boldsymbol{1}
32+
33+
The linearization of the weak form in the current configuration can be derived as
34+
35+
.. math::
36+
37+
\int_{\omega}
38+
\frac{1}{J}
39+
\vec{\nabla}(\vec{v})
40+
:\left[
41+
\frac{\partial \boldsymbol{\tau}}{\partial \boldsymbol{F}} \cdot \boldsymbol{F}^T
42+
-
43+
\left[\boldsymbol{\tau}\square\boldsymbol{1}\right]^{T_{R}}
44+
\right]:
45+
\vec{\nabla}(\Delta\vec{u})
46+
\, dv
47+
=
48+
\int_{\omega}
49+
\frac{1}{J}
50+
v_{i,j} \left[
51+
\frac{\partial \tau_{ij}}{\partial F_{kN}} F_{lN} - \tau_{il} \delta_{kj}
52+
\right] \Delta u_{k,l}
53+
\, dv
54+
55+
with the stiffness contribution
56+
57+
.. math::
58+
59+
\left[\frac{\partial \boldsymbol{\tau}}{\partial \boldsymbol{F}}\right]_{ijkN}
60+
&=
61+
\mu\left[\delta_{ik} F_{jN}+\delta_{jk}F_{iN}\right] + \lambda \delta_{ij}F_{Nk}^{-1} \\
62+
\left[\frac{\partial \boldsymbol{\tau}}{\partial \boldsymbol{F}} \cdot \boldsymbol{F}^T\right]_{ijkl}
63+
&=
64+
\mu\left[\delta_{ik} b_{lj}+b_{il}\delta_{jk}\right]+ \lambda \delta_{ij}\delta_{kl} \\
65+
\frac{1}{J}\vec{\nabla}(\vec{v}):\left[\frac{\partial \boldsymbol{\tau}}{\partial \boldsymbol{F}} \cdot \boldsymbol{F}^T\right]:\vec{\nabla}(\Delta\vec{u})
66+
&=\frac{1}{J}\left[\mu \vec{\nabla}(\vec{v}):[\vec{\nabla}(\Delta\vec{u})\cdot\boldsymbol{b} + \boldsymbol{b}\cdot\vec{\nabla}(\Delta\vec{u})^T]
67+
+ \lambda \text{Tr}(\vec{\nabla}(\vec{v}))\text{Tr}(\vec{\nabla}(\Delta\vec{u}))\right]
68+
69+
and the geometric contribution
70+
71+
.. math::
72+
-
73+
\frac{1}{J}\vec{\nabla}(\vec{v}):\left[\boldsymbol{\tau}\square\boldsymbol{1}\right]^{T_{R}}:\vec{\nabla}(\Delta\vec{u})
74+
=-\frac{1}{J}\vec{\nabla}(\vec{v}):[\boldsymbol{\tau}\cdot\vec{\nabla}(\Delta\vec{u})^T]
75+
76+
77+
The gradients of the test function :math:`\vec{\eta}` and the displacement increment :math:`\Delta\vec{u}` are taken
78+
w.r.t. the current configuration.
79+
The Deformation gradient :math:`\boldsymbol{F}=\boldsymbol{1}+\vec{\nabla}_X(\vec{u})` needs the gradient of the displacement field \:math:`vec{u}` w.r.t. the reference configuration.
80+
To get the gradients w.r.t. the current configuration we need to update the mesh and basis functions in every
81+
Newton iteration.
82+
However, the deformation gradient is always computed w.r.t. the reference configuration, so we also need to compute
83+
and interpolate these gradients by the total deformation :math:`u` w.r.t. the initial mesh and basis in every iteration.
84+
85+
"""
86+
import numpy as np
87+
from skfem import *
88+
from skfem.helpers import grad, identity, ddot, det, transpose, inv, trace, mul
89+
# note: rough mesh to make tests fast
90+
mesh = MeshHex.init_tensor(
91+
np.linspace(0, 1, 10),
92+
np.linspace(-.1, .1, 3),
93+
np.linspace(-.1, .1, 3),
94+
).with_boundaries({
95+
'left': lambda x: x[0] == 0.,
96+
'right': lambda x: x[0] == 1.,
97+
})
98+
element = ElementVector(ElementHex1())
99+
basis = Basis(mesh, element, intorder=1)
100+
gradbasis = Basis(mesh, element, intorder=1)
101+
102+
mu, lmbda = 1., 2.
103+
104+
@LinearForm
105+
def L(v, w):
106+
dudX = w['orig_gradu']
107+
F = dudX + identity(dudX)
108+
J = det(F)
109+
lnJ = np.log(J)
110+
I = identity(F)
111+
b = mul(F,transpose(F))
112+
tau = mu * (b - I) + lmbda * lnJ * I
113+
return 1/J*ddot(tau,grad(v))
114+
@BilinearForm
115+
def a(u, v, w):
116+
dudX = w['orig_gradu']
117+
F = dudX + identity(dudX)
118+
J = det(F)
119+
lnJ = np.log(J)
120+
I = identity(F)
121+
b = mul(F,transpose(F))
122+
tau = mu * (b - I) + lmbda * lnJ * I
123+
124+
stiff = 1/J*( mu*ddot( grad(v), mul(grad(u),b) + mul(b,transpose(grad(u))) ) + lmbda*trace(grad(v))*trace(grad(u)) )
125+
geo = -1/J*ddot(grad(v), mul(tau, transpose(grad(u))))
126+
return stiff+geo
127+
# Dirichlet boundary conditions
128+
dofs = basis.get_dofs('right')
129+
dirichlet = basis.get_dofs({'right', 'left'})
130+
right = -0.1
131+
tol = 1e-10
132+
nsteps = 7
133+
# Initialization
134+
u = basis.zeros()
135+
x = mesh.p.copy()
136+
X0= mesh.p.copy()
137+
current_mesh = MeshHex(x,mesh.t).with_boundaries(mesh.boundaries)
138+
for step in range(nsteps):
139+
lam = (step + 1.) / nsteps # load factor
140+
theat = lam*np.pi
141+
for iteration in range(10):
142+
# update mesh and basis for current configuration
143+
current_mesh = MeshHex(X0+u[basis.nodal_dofs],current_mesh.t).with_boundaries(mesh.boundaries)
144+
current_basis= Basis(current_mesh, element, intorder=1)
145+
# SET DIRICHLET_BC w.r.t. the displacemnts of the reference basis
146+
du_D = u.copy()
147+
x, y, z = basis.doflocs[:, dofs.nodal['u^1']]
148+
du_D[dofs.nodal['u^1']] = lam * right - du_D[dofs.nodal['u^1']]
149+
du_D[dofs.nodal['u^2']] = (y * np.cos(theat) - z * np.sin(theat) - y- du_D[dofs.nodal['u^2']])
150+
du_D[dofs.nodal['u^3']] = (y * np.sin(theat) + z * np.cos(theat) - z- du_D[dofs.nodal['u^3']])
151+
# compute residual and stiffness matrix with current basis but with displacment gradient to reference configuration
152+
grad_u = gradbasis.interpolate(u).grad
153+
f = L.assemble(current_basis, orig_gradu=grad_u)
154+
K = a.assemble(current_basis, orig_gradu=grad_u)
155+
# solve for newton direction
156+
du = solve(*condense(K, -f, x=du_D, D=dirichlet))
157+
norm_du = np.linalg.norm(du)
158+
u += du
159+
160+
print(1 + iteration, norm_du)
161+
if norm_du < tol: break
162+
163+
if __name__ == '__main__':
164+
import vedo
165+
p = (mesh.translated(u[basis.nodal_dofs])
166+
.draw('vedo', point_data={'uy': u[basis.nodal_dofs[1]]}))
167+
uy = u[basis.nodal_dofs[1]]
168+
p.pointdata.select('uy')
169+
p.cmap('rainbow', uy, vmin=uy.min(), vmax=uy.max())
170+
p.properties.SetAmbient(0.85)
171+
p.properties.SetDiffuse(0.0)
172+
p.lighting('off')
173+
edges = p.clone().wireframe(True).color('black').lw(1.5)
174+
# save image
175+
plt = vedo.Plotter(offscreen=True)
176+
plt.add(p, edges)
177+
plt.reset_camera()
178+
plt.screenshot('ex55.png')
179+
# show interactively
180+
plt = vedo.Plotter()
181+
plt.add(p, edges)
182+
plt.show()
183+
184+

docs/listofexamples.rst

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,11 @@ for nearly incompressible Neo-Hookean solids.
330330
See the source code of :exlink:`36` for more information.
331331

332332

333-
Example 43: Hyperelasticity
334-
---------------------------
333+
Example 43: Hyperelasticity - Total Lagrangian formulation
334+
----------------------------------------------------------
335335

336336
This example demonstrates Newton's method applied to the classical formulation
337-
of a hyperelastic Neo-Hookean solid.
337+
of a hyperelastic Neo-Hookean solid using the Total Lagrangian formulation.
338338

339339
.. figure:: https://user-images.githubusercontent.com/973268/147790182-64f4abf4-3909-4ec0-89ac-2add304b133d.png
340340

@@ -343,6 +343,26 @@ of a hyperelastic Neo-Hookean solid.
343343

344344
See the source code of :exlink:`43` for more information.
345345

346+
Example 55: Hyperelasticity - Updated Lagrangian formulation
347+
------------------------------------------------------------
348+
349+
This example demonstrates Newton's method applied to the classical formulation
350+
of a hyperelastic Neo-Hookean solid using the Updated Lagrangian formulation where the
351+
weak form and derivatives therein are evaluated w.r.t. the current configuration. The
352+
non-linearity of the material model relies on the deformatoin gradient which needs
353+
the spatial derivatives w.r.t. the reference configuration.
354+
To that end we use two meshes whereas one is updated in every iteration to provide the spatial
355+
deriavtives w.r.t. the current configuration and the other mesh is used to provide the derivatives
356+
w.r.t. to the reference configuration.
357+
358+
.. figure:: https://user-images.githubusercontent.com/973268/147790182-64f4abf4-3909-4ec0-89ac-2add304b133d.png
359+
360+
The deformed mesh of Example 55 matches the one of example 43 exactly.
361+
The figure was created using `vedo <https://github.com/marcomusy/vedo>`__.
362+
363+
See the source code of :exlink:`55` for more information.
364+
365+
346366
Example 51: Contact problem
347367
---------------------------
348368

tests/test_examples.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,10 @@ def runTest(self):
426426
0.15983570088457588)
427427
self.assertAlmostEqual(ex51.x2.max(),
428428
0.4062806125571157)
429+
430+
class TestEx55(TestCase):
431+
432+
def runTest(self):
433+
import docs.examples.ex55 as ex
434+
435+
self.assertAlmostEqual(ex.u.max(), 0.2466622622014594, delta=1e-8)

0 commit comments

Comments
 (0)