Source code for 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, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
"""Strain energy density function"""
pass
[docs]
def register(self, *args, **kwargs) -> None:
pass
[docs]
def S(self, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
"""Cauchy stress tensor for the compressibility model."""
return 2.0 * ufl.diff(self.strain_energy(C), C)
[docs]
def P(self, F: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
"""First Piola-Kirchhoff stress tensor for the compressibility model."""
C = F.T * F
return ufl.diff(self.strain_energy(C), F)
[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 __post_init__(self):
logger.debug("Created Incompressible compressibility model")
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, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
J = ufl.sqrt(ufl.det(C))
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),
)
logger.debug(f"Created Compressible compressibility model: {str(self)}")
logger.debug(f"Material parameters: kappa = {self.kappa}")
def __str__(self) -> str:
return "\u03ba (J ln(J) - J + 1)"
[docs]
def strain_energy(self, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
J = ufl.sqrt(ufl.det(C))
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 / 4 (J^2 - 1 - 2 \ln(J))
"""
kappa: Variable = field(default_factory=lambda: Variable(1e6, "Pa"))
def __str__(self) -> str:
return "\u03ba / 4 (J ** 2 - 1 - 2 ln(J))"
[docs]
def strain_energy(self, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
J = ufl.sqrt(ufl.det(C))
kappa = self.kappa.to_base_units()
return 0.25 * kappa * (J**2 - 1 - 2 * ufl.ln(J))
[docs]
@dataclass(slots=True)
class Compressible3(Compressible):
r"""Compressible material model used in Usyk et al. 2002
Strain energy density function is given by
.. math::
\Psi = \kappa / 2 (J - 1) * \ln(J)
"""
kappa: Variable = field(default_factory=lambda: Variable(5e4, "Pa"))
def __str__(self) -> str:
return "\u03ba / 2 * (J - 1) * ln(J)"
[docs]
def strain_energy(self, C: ufl.core.expr.Expr) -> ufl.core.expr.Expr:
J = ufl.sqrt(ufl.det(C))
kappa = self.kappa.to_base_units()
return 0.5 * kappa * (J - 1) * ufl.ln(J)