Compressible model
Compressible modelΒΆ
In this demo we show how to make a custom model e.g a compressible
model. The default model in pulse
is an incompressible model
implemented using a two-field variational approach with Taylor-Hood
finite elements. In this demo we use a pentaly-based compressible
model where the term
\[\kappa (J \mathrm{ln}J - J + 1)\]
is added as a penalty to the strain energy denisty function, and we use \(\mathbb{P}1\) elements for the displacement
import dolfin
import pulse
# Make sure to use dolfin-adjoint version of object if using dolfin_adjoint
try:
from dolfin_adjoint import Constant, DirichletBC, Function, Mesh
except ImportError:
from dolfin import Function, Constant, DirichletBC, Mesh
from fenics_plotly import plot
from pulse import DeformationGradient, Jacobian
class CompressibleProblem(pulse.MechanicsProblem):
"""
This class implements a compressbile model with a penalized
compressibility term, solving for the displacement only.
"""
def _init_spaces(self):
mesh = self.geometry.mesh
element = dolfin.VectorElement("P", mesh.ufl_cell(), 1)
self.state_space = dolfin.FunctionSpace(mesh, element)
self.state = Function(self.state_space)
self.state_test = dolfin.TestFunction(self.state_space)
# Add penalty factor
self.kappa = Constant(1e3)
def _init_forms(self):
u = self.state
v = self.state_test
F = dolfin.variable(DeformationGradient(u))
J = Jacobian(F)
dx = self.geometry.dx
# Add penalty term
internal_energy = self.material.strain_energy(F) + self.kappa * (
J * dolfin.ln(J) - J + 1
)
self._virtual_work = dolfin.derivative(
internal_energy * dx,
self.state,
self.state_test,
)
self._virtual_work += self._external_work(u, v)
self._jacobian = dolfin.derivative(
self._virtual_work,
self.state,
dolfin.TrialFunction(self.state_space),
)
self._set_dirichlet_bc()
self._init_solver()
geometry = pulse.Geometry.from_file(pulse.mesh_paths["simple_ellipsoid"])
# geometry = pulse.geometries.prolate_ellipsoid_geometry(mesh_size_factor=3.0)
2022-06-05 11:29:39,906 [260] INFO pulse.geometry_utils:
Load mesh from h5
activation = Function(dolfin.FunctionSpace(geometry.mesh, "R", 0))
activation.assign(Constant(0.2))
matparams = pulse.HolzapfelOgden.default_parameters()
material = pulse.HolzapfelOgden(
activation=activation,
parameters=matparams,
f0=geometry.f0,
s0=geometry.s0,
n0=geometry.n0,
)
# LV Pressure
lvp = Constant(1.0)
lv_marker = geometry.markers["ENDO"][0]
lv_pressure = pulse.NeumannBC(traction=lvp, marker=lv_marker, name="lv")
neumann_bc = [lv_pressure]
# Add spring term at the base with stiffness 1.0 kPa/cm^2
base_spring = 1.0
robin_bc = [
pulse.RobinBC(value=Constant(base_spring), marker=geometry.markers["BASE"][0]),
]
# Fix the basal plane in the longitudinal direction
# 0 in V.sub(0) refers to x-direction, which is the longitudinal direction
def fix_basal_plane(W):
V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0)
bc = DirichletBC(
V.sub(0),
Constant(0.0),
geometry.ffun,
geometry.markers["BASE"][0],
)
return bc
dirichlet_bc = [fix_basal_plane]
# Collect boundary conditions
bcs = pulse.BoundaryConditions(
dirichlet=dirichlet_bc,
neumann=neumann_bc,
robin=robin_bc,
)
# Create the problem
problem = CompressibleProblem(
geometry,
material,
bcs,
solver_parameters={"verbose": True},
)
# Solve the problem
problem.solve()
0 SNES Function norm 2.519067246589e+02
0 KSP Residual norm 2.519067246589e+02
1 KSP Residual norm 2.576329114451e-13
1 SNES Function norm 9.748359125273e+01
0 KSP Residual norm 9.748359125273e+01
1 KSP Residual norm 4.490887848751e-13
2 SNES Function norm 3.975800138311e+01
0 KSP Residual norm 3.975800138311e+01
1 KSP Residual norm 1.198177072249e-13
3 SNES Function norm 1.524612677179e+01
0 KSP Residual norm 1.524612677179e+01
1 KSP Residual norm 8.842876672027e-14
4 SNES Function norm 5.552440626749e+00
0 KSP Residual norm 5.552440626749e+00
1 KSP Residual norm 2.104523803800e-13
5 SNES Function norm 3.938627895857e+00
0 KSP Residual norm 3.938627895857e+00
1 KSP Residual norm 1.504842630906e-14
6 SNES Function norm 5.952075845229e-01
0 KSP Residual norm 5.952075845229e-01
1 KSP Residual norm 2.779326471712e-13
7 SNES Function norm 8.235832907681e+00
0 KSP Residual norm 8.235832907681e+00
1 KSP Residual norm 1.095202311642e-14
8 SNES Function norm 7.105207978572e-02
0 KSP Residual norm 7.105207978572e-02
1 KSP Residual norm 2.700781466640e-14
9 SNES Function norm 1.075841070074e-01
0 KSP Residual norm 1.075841070074e-01
1 KSP Residual norm 1.122863367764e-15
10 SNES Function norm 1.774565600292e-04
(10, True)
u = problem.state
mesh = Mesh(geometry.mesh)
dolfin.ALE.move(mesh, u)
fig = plot(geometry.mesh, opacity=0.4, show=False)
fig.add_plot(plot(mesh, color="red", show=False))
fig.show()