Source code for genesis.engine.materials.MPM.sand

import math
from typing import Any, Literal

import quadrants as qd
from pydantic import Field

import genesis as gs
from genesis.typing import PositiveFloat, ValidFloat

from .base import Base, SamplerType


[docs]@qd.data_oriented class Sand(Base): """ The sand material class for MPM. Parameters ---------- E : float, optional Young's modulus. Default is 1e6. nu : float, optional Poisson ratio. Default is 0.2. rho : float, optional Density (kg/m³). Default is 1000. sampler : str, optional Particle sampler. Default is 'random'. friction_angle : float, optional Friction angle in degrees, used to compute internal pressure-dependent plasticity. Default is 45. """ sampler: SamplerType = "random" friction_angle: PositiveFloat = 45.0 # Derived from friction_angle, set in model_post_init. alpha: ValidFloat = Field(default=0.0, exclude=True)
[docs] def model_post_init(self, context: Any) -> None: super().model_post_init(context) self._default_Jp = 0.0 sin_phi = math.sin(math.radians(self.friction_angle)) self.alpha = math.sqrt(2 / 3) * 2 * sin_phi / (3 - sin_phi) self.update_F_S_Jp = self._update_F_S_Jp_sand self.update_stress = self._update_stress_sand
@qd.func def _sand_projection(self, S, Jp): S_out = qd.Matrix.zero(gs.qd_float, 3, 3) epsilon = qd.Vector.zero(gs.qd_float, 3) for i in qd.static(range(3)): epsilon[i] = qd.log(max(abs(S[i, i]), 1e-4)) S_out[i, i] = 1 tr = epsilon.sum() + Jp epsilon_hat = epsilon - tr / 3 epsilon_hat_norm = epsilon_hat.norm(gs.EPS) Jp_new = gs.qd_float(0.0) if tr >= 0.0: Jp_new = tr else: Jp_new = 0.0 delta_gamma = epsilon_hat_norm + (3 * self.lam + 2 * self.mu) / (2 * self.mu) * tr * self.alpha for i in qd.static(range(3)): S_out[i, i] = qd.exp(epsilon[i] - max(0, delta_gamma) / epsilon_hat_norm * epsilon_hat[i]) return S_out, Jp_new @qd.func def _update_F_S_Jp_sand(self, J, F_tmp, U, S, V, Jp): S_new, Jp_new = self._sand_projection(S, Jp) F_new = U @ S_new @ V.transpose() return F_new, S_new, Jp_new @qd.func def _update_stress_sand(self, U, S, V, F_tmp, F_new, J, Jp, actu, m_dir): log_S_sum = gs.qd_float(0.0) center = qd.Matrix.zero(gs.qd_float, 3, 3) for i in qd.static(range(3)): log_S_sum += qd.log(S[i, i]) center[i, i] = 2.0 * self.mu * qd.log(S[i, i]) * (1 / S[i, i]) for i in qd.static(range(3)): center[i, i] += self.lam * log_S_sum * (1 / S[i, i]) stress = U @ center @ V.transpose() @ F_new.transpose() return stress