Source code for phasefieldx.Element.Phase_Field_Fracture.split_energy_stress_tangent_functions

r"""
Energy Decomposition
====================

Regarding energy degradation, there are two main descriptions of the constitutive free energy functional.
The isotropic model degrades all the energy; in the other case, the anisotropic model only degrades the energy related to tension efforts, trying to account that cracks only are generated under tension efforts.

In the Isotropic constitutive free energy functional,

.. math::

   E(u,\phi)=\int \left[g(\phi)+k\right] \psi(\boldsymbol \epsilon(\boldsymbol u)) dV,


the degradation function $g(\phi)$ degrades all the energy $\psi(\boldsymbol \epsilon(\boldsymbol u))$. In the case of the anisotropic constitutive free energy functional,

.. math::

   E(u,\phi)=\int \left(\left[g(\phi)+k\right] \psi_a(\boldsymbol \epsilon(\boldsymbol u)) + \psi_b(\boldsymbol \epsilon(\boldsymbol u))\right) dV,

the energy $\psi(\boldsymbol \epsilon(\boldsymbol u))$ is split in two components, $\psi=\psi_a+\psi_b$, in which the component $\psi_a$ attains to the energy relative to tensions, so the degradation function only applied to it, avoiding the creation of crack due to compression effort.

There are several methods to carry out the energy slip. PhaseFieldX code implements the Spectral [Miehe]_ and volumetric-deviatoric [Amor]_ decomposition.


"""

import ufl
from phasefieldx.Math.functions import macaulay_bracket_positive, macaulay_bracket_negative
from phasefieldx.Materials.elastic_isotropic import epsilon, sigma, psi
from phasefieldx.Math.tensor_decomposition import deviatoric_part, spectral_positive_part, spectral_negative_part


[docs] def psi_a_spectral(u, lambda_, mu): """ Evaluate the spectral positive part of the strain energy density function. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- float The spectral positive part of the strain energy density. """ eps = epsilon(u) eps_P = spectral_positive_part(eps) return 0.5 * lambda_ * \ macaulay_bracket_positive(ufl.tr(eps))**2 + \ mu * ufl.inner(eps_P, eps_P)
[docs] def psi_b_spectral(u, lambda_, mu): """ Evaluate the spectral negative part of the strain energy density function. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- float The spectral negative part of the strain energy density. """ eps = epsilon(u) eps_N = spectral_negative_part(eps) return 0.5 * lambda_ * \ macaulay_bracket_negative(ufl.tr(eps))**2 + \ mu * ufl.inner(eps_N, eps_N)
[docs] def sigma_a_spectral(u, lambda_, mu): """ Evaluate the spectral positive part of the stress tensor. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- ufl.Expr The spectral positive part of the stress tensor. """ eps = epsilon(u) eps_P = spectral_positive_part(eps) return lambda_ * \ macaulay_bracket_positive(ufl.tr(eps)) * \ ufl.Identity(len(u)) + 2 * mu * eps_P
[docs] def sigma_b_spectral(u, lambda_, mu): """ Evaluate the spectral negative part of the stress tensor. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- ufl.Expr The spectral negative part of the stress tensor. """ # eps = epsilon(u) # eps_N = spectral_negative_part(eps) # return lambda_ * # macaulay_bracket_negative(ufl.tr(eps))*ufl.Identity(len(u)) + 2*mu*eps_N return sigma(u, lambda_, mu) - sigma_a_spectral(u, lambda_, mu)
[docs] def psi_a_deviatoric(u, lambda_, mu): """ Evaluate the deviatoric positive part of the strain energy density function. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- float The deviatoric positive part of the strain energy density. """ eps = epsilon(u) epsD = deviatoric_part(eps) # epsD = ufl.dev(eps) k0 = lambda_ + 2 / len(u) * mu return 0.5 * k0 * \ macaulay_bracket_positive(ufl.tr(eps))**2 + mu * ufl.inner(epsD, epsD)
[docs] def psi_b_deviatoric(u, lambda_, mu): """ Evaluate the deviatoric negative part of the strain energy density function. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- float The deviatoric negative part of the strain energy density. """ eps = epsilon(u) k0 = lambda_ + 2 / len(u) * mu return 0.5 * k0 * macaulay_bracket_negative(ufl.tr(eps))**2
[docs] def sigma_a_deviatoric(u, lambda_, mu): """ Evaluate the deviatoric positive part of the stress tensor. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- ufl.Expr The deviatoric positive part of the stress tensor. """ eps = epsilon(u) epsD = deviatoric_part(eps) # epsD= ufl.dev(eps) k0 = lambda_ + 2 / len(u) * mu return k0 * \ macaulay_bracket_positive(ufl.tr(eps)) * \ ufl.Identity(len(u)) + 2 * mu * epsD
[docs] def sigma_b_deviatoric(u, lambda_, mu): """ Evaluate the deviatoric negative part of the stress tensor. Parameters ---------- u : ufl.Function The displacement field. lambda_ : float The first Lamé parameter. mu : float The second Lamé parameter. Returns ------- ufl.Expr The deviatoric negative part of the stress tensor. """ # eps = epsilon(u) # k0 = lambda_+2/len(u)*mu # return k0 * macaulay_bracket_negative(ufl.tr(eps))*ufl.Identity(len(u)) return sigma(u, lambda_, mu) - sigma_a_deviatoric(u, lambda_, mu)
[docs] def psi_a(u, DataSimulation): """ Evaluate the positive part of the strain energy density function based on the specified degradation type. Parameters ---------- u : ufl.Function The displacement field. DataSimulation : object An object containing simulation parameters, including degradation type and split energy type. Returns ------- float The positive part of the strain energy density. """ if DataSimulation.degradation == "isotropic": return psi(u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.degradation == "anisotropic" or DataSimulation.degradation == "hybrid": if DataSimulation.split_energy == "deviatoric": return psi_a_deviatoric( u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.split_energy == "spectral": return psi_a_spectral(u, DataSimulation.lambda_, DataSimulation.mu) else: raise ValueError("Invalid")
[docs] def psi_b(u, DataSimulation): """ Evaluate the negative part of the strain energy density function based on the specified degradation type. Parameters ---------- u : ufl.Function The displacement field. DataSimulation : object An object containing simulation parameters, including degradation type and split energy type. Returns ------- float The negative part of the strain energy density. """ if DataSimulation.degradation == "isotropic": return psi(u, DataSimulation.lambda_, DataSimulation.mu) - \ psi(u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.degradation == "anisotropic" or DataSimulation.degradation == "hybrid": if DataSimulation.split_energy == "deviatoric": return psi_b_deviatoric( u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.split_energy == "spectral": return psi_b_spectral(u, DataSimulation.lambda_, DataSimulation.mu) else: raise ValueError("Invalid")
[docs] def sigma_a(u, DataSimulation): """ Evaluate the positive part of the stress tensor based on the specified degradation type. Parameters ---------- u : ufl.Function The displacement field. DataSimulation : object An object containing simulation parameters, including degradation type and split energy type. Returns ------- ufl.Expr The positive part of the stress tensor. """ if DataSimulation.degradation == "isotropic" or DataSimulation.degradation == "hybrid": return sigma(u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.degradation == "anisotropic": if DataSimulation.split_energy == "deviatoric": return sigma_a_deviatoric( u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.split_energy == "spectral": return sigma_a_spectral( u, DataSimulation.lambda_, DataSimulation.mu) else: raise ValueError("Invalid")
[docs] def sigma_b(u, DataSimulation): """ Evaluate the negative part of the stress tensor based on the specified degradation type. Parameters ---------- u : ufl.Function The displacement field. DataSimulation : object An object containing simulation parameters, including degradation type and split energy type. Returns ------- ufl.Expr The negative part of the stress tensor. """ if DataSimulation.degradation == "isotropic" or DataSimulation.degradation == "hybrid": return 0 * sigma(u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.degradation == "anisotropic": if DataSimulation.split_energy == "deviatoric": return sigma_b_deviatoric( u, DataSimulation.lambda_, DataSimulation.mu) elif DataSimulation.split_energy == "spectral": return sigma_b_spectral( u, DataSimulation.lambda_, DataSimulation.mu) else: raise ValueError("Invalid")