Source code for genesis.engine.materials.FEM.elastic

import gstaichi as ti

import genesis as gs

from .base import Base


@ti.func
def partialJpartialF(F):
    pJpF0 = F[:, 1].cross(F[:, 2])
    pJpF1 = F[:, 2].cross(F[:, 0])
    pJpF2 = F[:, 0].cross(F[:, 1])
    pJpF = ti.Matrix.cols([pJpF0, pJpF1, pJpF2])
    return pJpF


[docs] @ti.data_oriented class Elastic(Base): """ The elastic material class for FEM. Parameters ---------- E: float, optional Young's modulus, which controls stiffness. Default is 1e6. nu: float, optional Poisson ratio, describing the material's volume change under stress. Default is 0.2. rho: float, optional Material density (kg/m^3). Default is 1000. hydroelastic_modulus: float, optional Hydroelastic modulus for hydroelastic contact. Default is 1e7. friction_mu: float, optional Friction coefficient. Default is 0.1. model: str, optional Constitutive model to use for stress computation. Options are: - 'linear': Linear elasticity model - 'stable_neohookean': A numerically stable Neo-Hookean model - 'linear_corotated': Linear corotated elasticity model Default is 'linear'. """ def __init__( self, E=1e6, # Young's modulus nu=0.2, # Poisson's ratio rho=1000.0, # density (kg/m^3) hydroelastic_modulus=1e7, # hydroelastic_modulus for hydroelastic contact friction_mu=0.1, model="linear", ): super().__init__(E, nu, rho, hydroelastic_modulus, friction_mu) if model == "linear": self.update_stress = self.update_stress_linear self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_linear self.compute_energy_gradient = self.compute_energy_gradient_linear self.compute_energy = self.compute_energy_linear self.hessian_invariant = True elif model in ("stable_neohookean", "stable_neohooken"): self.update_stress = self.update_stress_stable_neohookean self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_stable_neohookean self.compute_energy_gradient = self.compute_energy_gradient_stable_neohookean self.compute_energy = self.compute_energy_stable_neohookean self.hessian_invariant = False if model == "stable_neohooken": gs.logger.warning("The 'stable_neohooken' model is deprecated. Use 'stable_neohookean' instead.") elif model == "linear_corotated": self.build = self.build_linear_corotated self.pre_compute = self.pre_compute_linear_corotated self.update_stress = self.update_stress_linear_corotated self.compute_energy_gradient_hessian = self.compute_energy_gradient_hessian_linear_corotated self.compute_energy_gradient = self.compute_energy_gradient_linear_corotated self.compute_energy = self.compute_energy_linear_corotated self.hessian_static = False else: gs.raise_exception(f"Unrecognized constitutive model: {model}") self._model = model
[docs] def build_linear_corotated(self, fem_solver): self.R = ti.field(dtype=gs.ti_mat3, shape=(fem_solver._B, fem_solver.n_elements))
[docs] @ti.func def pre_compute_linear_corotated(self, J, F, i_e, i_b): # Computing Polar Decomposition instead of calling `R, P = ti.polar_decompose(F)` since `P` is not needed here U, S, V = ti.svd(F) R = U @ V.transpose() self.R[i_b, i_e] = R
[docs] @ti.func def update_stress_linear(self, mu, lam, J, F, actu, m_dir): I = ti.Matrix.identity(dt=gs.ti_float, n=3) stress = mu * (F + F.transpose() - 2 * I) + lam * (F - I).trace() * I return stress
[docs] @ti.func def update_stress_stable_neohookean(self, mu, lam, J, F, actu, m_dir): IC = F.norm_sqr() dJdF0 = F[:, 1].cross(F[:, 2]) dJdF1 = F[:, 2].cross(F[:, 0]) dJdF2 = F[:, 0].cross(F[:, 1]) dJdF = ti.Matrix.cols([dJdF0, dJdF1, dJdF2]) alpha = 1 + 0.75 * mu / lam stress = mu * (1 - 1 / (IC + 1)) * F + lam * (J - alpha) * dJdF return stress
[docs] @ti.func def update_stress_linear_corotated(self, mu, lam, J, F, actu, m_dir): gs.raise_exception("Linear corotated stress update is not implemented yet.")
[docs] @ti.func def compute_energy_gradient_hessian_linear(self, mu, lam, J, F, actu, m_dir, i_e, i_b, hessian_field): """ Compute the energy, gradient, and Hessian for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). hessian_field: ti.Matrix The Hessian of the energy with respect to the deformation gradient F. Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/LINEAR.cpp """ I = ti.Matrix.identity(dt=gs.ti_float, n=3) eps = 0.5 * (F + F.transpose()) - I trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 gradient = 2.0 * mu * eps + lam * trEps * I # Zero out the matrix for i in ti.static(ti.grouped(ti.ndrange(3, 3))): hessian_field[i_b, i, i_e].fill(0.0) # Identity part for i, k in ti.static(ti.ndrange(3, 3)): hessian_field[i_b, i, i, i_e][k, k] = mu # Diagonal terms hessian_field[i_b, 0, 0, i_e][0, 0] += mu + lam hessian_field[i_b, 1, 1, i_e][1, 1] += mu + lam hessian_field[i_b, 2, 2, i_e][2, 2] += mu + lam # Off-diagonal terms hessian_field[i_b, 0, 1, i_e][1, 0] = hessian_field[i_b, 1, 0, i_e][0, 1] = mu hessian_field[i_b, 0, 2, i_e][2, 0] = hessian_field[i_b, 2, 0, i_e][0, 2] = mu hessian_field[i_b, 1, 2, i_e][2, 1] = hessian_field[i_b, 2, 1, i_e][1, 2] = mu # Pressure coupling terms hessian_field[i_b, 0, 1, i_e][0, 1] = hessian_field[i_b, 0, 2, i_e][0, 2] = lam hessian_field[i_b, 1, 0, i_e][1, 0] = hessian_field[i_b, 2, 0, i_e][2, 0] = lam hessian_field[i_b, 1, 2, i_e][1, 2] = hessian_field[i_b, 2, 1, i_e][2, 1] = lam return energy, gradient
[docs] @ti.func def compute_energy_gradient_linear(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy, gradient for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/LINEAR.cpp """ I = ti.Matrix.identity(dt=gs.ti_float, n=3) eps = 0.5 * (F + F.transpose()) - I trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 gradient = 2.0 * mu * eps + lam * trEps * I return energy, gradient
[docs] @ti.func def compute_energy_linear(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). Returns ------- energy: float The computed energy. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/LINEAR.cpp """ I = ti.Matrix.identity(dt=gs.ti_float, n=3) eps = 0.5 * (F + F.transpose()) - I trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 return energy
[docs] @ti.func def compute_energy_gradient_hessian_stable_neohookean(self, mu, lam, J, F, actu, m_dir, i_e, i_b, hessian_field): """ Compute the energy, gradient, and Hessian for the stable Neo-Hookean model. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in stable Neo-Hookean). m_dir: ti.Matrix The material direction (not used in stable Neo-Hookean). hessian_field: ti.Matrix The Hessian of the energy with respect to the deformation gradient F. Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Raises ------- NotImplementedError This implementation does not compute the Hessian for the stable Neo-Hookean model. The Hessian needs SVD decomposition for accurate computation, which is not implemented here. Notes ------- This implementation is adapted from the HOBAKv1 stable Neo-Hookean model: https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/SNH.cpp """ raise NotImplementedError("Hessian computation is not implemented for stable_neohookean model.")
[docs] @ti.func def compute_energy_gradient_stable_neohookean(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy, gradient for the stable Neo-Hookean model. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in stable Neo-Hookean). m_dir: ti.Matrix The material direction (not used in stable Neo-Hookean). Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Raises ------- NotImplementedError This implementation does not compute the Gradient for the stable Neo-Hookean model. Notes ------- This implementation is adapted from the HOBAKv1 stable Neo-Hookean model: https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/SNH.cpp """ gs.raise_exception("Gradient computation is not implemented for stable_neohookean model.")
[docs] @ti.func def compute_energy_stable_neohookean(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy for the stable Neo-Hookean model. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in stable Neo-Hookean). m_dir: ti.Matrix The material direction (not used in stable Neo-Hookean). Returns ------- energy: float The computed energy. Notes ------- This implementation is adapted from the HOBAKv1 stable Neo-Hookean model: https://github.com/theodorekim/HOBAKv1/blob/8420c51b795735d8fb912e0f8810f935d96fb636/src/Hyperelastic/Volume/SNH.cpp """ _lambda = lam + mu _alpha = 1.0 + mu / _lambda Ic = F.norm_sqr() Jminus1 = J - _alpha energy = 0.5 * (mu * (Ic - 3.0) + _lambda * Jminus1**2) return energy
[docs] @ti.func def compute_energy_gradient_hessian_linear_corotated(self, mu, lam, J, F, actu, m_dir, i_e, i_b, hessian_field): """ Compute the energy, gradient, and Hessian for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). hessian_field: ti.Matrix The Hessian of the energy with respect to the deformation gradient F. Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/LINEAR.cpp """ R = self.R[i_b, i_e] F_hat = R.transpose() @ F # E = 1/2(F_hat + F_hat.transpose()) - I eps = 0.5 * (F_hat + F_hat.transpose()) for i in ti.static(range(3)): eps[i, i] -= 1.0 trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 gradient = 2.0 * mu * R @ eps + lam * trEps * R # Zero out the matrix for i in ti.static(ti.grouped(ti.ndrange(3, 3))): hessian_field[i_b, i, i_e].fill(0.0) # Identity part for i, k in ti.static(ti.ndrange(3, 3)): hessian_field[i_b, i, i, i_e][k, k] = mu for i, j, alpha, beta in ti.ndrange(3, 3, 3, 3): hessian_field[i_b, j, beta, i_e][i, alpha] += mu * R[i, beta] * R[alpha, j] + lam * R[alpha, beta] * R[i, j] return energy, gradient
[docs] @ti.func def compute_energy_gradient_linear_corotated(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy, gradient for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). Returns ------- energy: float The computed energy. gradient: ti.Matrix The gradient of the energy with respect to the deformation gradient F. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/LINEAR.cpp """ F_hat = self.R[i_b, i_e].transpose() @ F # E = 1/2(F_hat + F_hat.transpose()) - I eps = 0.5 * (F_hat + F_hat.transpose()) for i in ti.static(range(3)): eps[i, i] -= 1.0 trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 gradient = 2.0 * mu * self.R[i_b, i_e] @ eps + lam * trEps * self.R[i_b, i_e] return energy, gradient
[docs] @ti.func def compute_energy_linear_corotated(self, mu, lam, J, F, actu, m_dir, i_e, i_b): """ Compute the energy for linear elasticity. Parameters ---------- mu: float The first Lame parameter (shear modulus). lam: float The second Lame parameter (related to volume change). J: float The determinant of the deformation gradient F. F: ti.Matrix The deformation gradient matrix. actu: ti.Matrix The activation matrix (not used in linear elasticity). m_dir: ti.Matrix The material direction (not used in linear elasticity). Returns ------- energy: float The computed energy. Notes ------- This implementation assumes small deformations and linear stress-strain relationship. It is adapted from the HOBAKv1 implementation for linear elasticity: https://github.com/theodorekim/HOBAKv1/blob/main/src/Hyperelastic/Volume/LINEAR.cpp """ F_hat = self.R[i_b, i_e].transpose() @ F # E = 1/2(F_hat + F_hat.transpose()) - I eps = 0.5 * (F_hat + F_hat.transpose()) for i in ti.static(range(3)): eps[i, i] -= 1.0 trEps = eps.trace() energy = mu * eps.norm_sqr() + 0.5 * lam * trEps**2 return energy
@property def model(self): """The name of the constitutive model ('linear' or 'stable_neohookean').""" return self._model