Source code for fenicsx_pulse.compressibility
r"""This module defines compressibility models for the material models.
We define two compressibility models: `Incompressible` and `Compressible`.
An incompressible material is a material that does not change its volume under
deformation. The volume change is described by the Jacobian :math:`J = \det(F)`,
where :math:`F` is the deformation gradient.
The `Incompressible` model is defined by the strain energy density function
:math:`\Psi = p (J - 1)`, where :math:`p` is a function representing the
Lagrange multiplier. The `Compressible` model is defined by the strain energy
density function :math:`\Psi = \kappa (J \ln(J) - J + 1)`, where :math:`\kappa`
is a material parameter representing the bulk modulus. Higher values of
:math:`\kappa` correspond to more incompressible material.
"""
import abc
import logging
from dataclasses import dataclass, field
import dolfinx
import numpy as np
import ufl
from . import exceptions
from .units import Variable
logger = logging.getLogger(__name__)
[docs]
class Compressibility(abc.ABC):
"""Base class for compressibility models."""
[docs]
@abc.abstractmethod
def strain_energy(self, J: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
"""Strain energy density function"""
pass
[docs]
def register(self, *args, **kwargs) -> None:
pass
[docs]
@abc.abstractmethod
def is_compressible(self) -> bool:
"""Returns True if the material model is compressible."""
pass
[docs]
@dataclass(slots=True)
class Incompressible(Compressibility):
r"""Incompressible material model
Strain energy density function is given by
.. math::
\Psi = p (J - 1)
"""
p: dolfinx.fem.Function = field(default=None, init=False)
def __str__(self) -> str:
return "p (J - 1)"
[docs]
def register(self, p: dolfinx.fem.Function) -> None:
self.p = p
[docs]
def strain_energy(self, J: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
if self.p is None:
raise exceptions.MissingModelAttribute(attr="p", model=type(self).__name__)
return self.p * (J - 1.0)
[docs]
def is_compressible(self) -> bool:
return False
[docs]
@dataclass(slots=True)
class Compressible(Compressibility):
r"""Compressible material model
Strain energy density function is given by
.. math::
\Psi = \kappa (J \ln(J) - J + 1)
"""
kappa: Variable = field(default_factory=lambda: Variable(1e6, "Pa"))
def __post_init__(self):
if not isinstance(self.kappa, Variable):
unit = "kPa"
logger.warning("Setting mu to %s %s", self.kappa, unit)
self.kappa = Variable(self.kappa, unit)
# Check that value are positive
if not exceptions.check_value_greater_than(
self.kappa.value,
0.0,
inclusive=True,
):
raise exceptions.InvalidRangeError(
name="kappa",
expected_range=(0.0, np.inf),
)
def __str__(self) -> str:
return "\u03ba (J ln(J) - J + 1)"
[docs]
def strain_energy(self, J: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
kappa = self.kappa.to_base_units()
return kappa * (J * ufl.ln(J) - J + 1)
[docs]
def is_compressible(self) -> bool:
return True
[docs]
@dataclass(slots=True)
class Compressible2(Compressible):
r"""Compressible material model
Strain energy density function is given by
.. math::
\Psi = \kappa (J^2 - 1 - 2 \ln(J))
"""
kappa: Variable = field(default_factory=lambda: Variable(1e6, "Pa"))
def __str__(self) -> str:
return "\u03ba (J ** 2 - 1 - 2 ln(J))"
[docs]
def strain_energy(self, J: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
kappa = self.kappa.to_base_units()
return 0.25 * kappa * (J**2 - 1 - 2 * ufl.ln(J))