forked from 1b-agent/fieldcraft-upstream
explicit: hyperelastic materials on the shared finite-strain material point
Neo-Hookean, Mooney–Rivlin and Yeoh materials in explicit dynamics, in total form from the deformation gradient (objective by construction): the one-point hex8 takes its mean F over the undeformed element and the stress τ/J; tet4 takes the isochoric stress of its own F with the average nodal pressure of the decoupled volumetric energy (Bonet–Burton at finite strain). Each element's frequency bound follows the tangent's largest normal stiffness. Small-displacement steps and thermal strain of hyperelastic elements are refused by name. Verified by uniaxial strain to λ = 1.5 of all three laws on hex8 and tet4 against the material point (to rounding), and the oracle case explicit-neo-hookean-implicit: a quasi-static explicit pull against the implicit nonlinear solver (lateral contraction to 0.09%, stress to 0.03%). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
94f7e8533a
commit
ce8e23d05b
9 files changed
+600
-10
No files matched your search
@@ -3,6 +3,7 @@
|
||||
//! ([`crate::discrete`]), prescribed velocities, rigid walls, initial velocities and the load
|
||||
//! vector of an explicit dynamic step.
|
||||
|
||||
use fieldcraft_fem::constitutive::MaterialModel;
|
||||
use fieldcraft_fem::material::Elasticity;
|
||||
use fieldcraft_fem::plasticity::J2;
|
||||
use fieldcraft_fem::uniform::{Hex8, Tet4, modulus_bound};
|
||||
@@ -38,6 +39,9 @@ pub struct Material {
|
||||
/// The hypoelastic(–plastic) update of an isotropic material; `None` for orthotropic
|
||||
/// elasticity, updated with [`Material::elasticity`].
|
||||
pub j2: Option<J2>,
|
||||
/// The finite-strain material point of a hyperelastic material (its stress from the
|
||||
/// deformation gradient; `None` for the rate-form materials).
|
||||
pub hyperelastic: Option<MaterialModel>,
|
||||
/// The equivalent plastic strain at which elements erode.
|
||||
pub erosion: Option<f64>,
|
||||
/// The (isotropic) expansion coefficient and reference temperature, when the material
|
||||
@@ -143,9 +147,6 @@ fn invalid(m: impl Into<String>) -> Error {
|
||||
fn material(model: &Model, name: &str) -> Result<Material> {
|
||||
let m = model.material(name).ok_or_else(|| invalid(format!("material `{name}` is missing")))?;
|
||||
let elastic = m.elastic.as_ref().ok_or_else(|| invalid(format!("material `{name}` has no elastic constants")))?;
|
||||
if let Some(h) = m.hyperelastic {
|
||||
return Err(Error::NotSupported(format!("{} hyperelastic material `{name}` in explicit dynamics", h.name())));
|
||||
}
|
||||
let density = m
|
||||
.density
|
||||
.filter(|d| *d > 0.0 && d.is_finite())
|
||||
@@ -174,6 +175,12 @@ fn material(model: &Model, name: &str) -> Result<Material> {
|
||||
stiffest,
|
||||
j2,
|
||||
erosion: m.erosion.map(|e| e.plastic_strain),
|
||||
hyperelastic: m
|
||||
.hyperelastic
|
||||
.as_ref()
|
||||
.map(|h| MaterialModel::from_model(elastic, Some(h)))
|
||||
.transpose()
|
||||
.map_err(|e| invalid(format!("material `{name}`: {e}")))?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -368,9 +375,33 @@ impl System {
|
||||
sys.load(model, resolved, step)?;
|
||||
sys.temperatures(model, resolved, step, settings.end_time)?;
|
||||
sys.conditions(resolved, settings)?;
|
||||
sys.check_hyperelastic()?;
|
||||
Ok(sys)
|
||||
}
|
||||
|
||||
/// Refuse what hyperelastic elements cannot take yet: small-displacement steps (the
|
||||
/// material is a finite-strain law) and thermal strain.
|
||||
fn check_hyperelastic(&self) -> Result<()> {
|
||||
let solids =
|
||||
self.hexes.iter().map(|h| (h.label, h.material, h.thermal_rate)).chain(self.tets.iter().map(|t| (t.label, t.material, t.thermal_rate)));
|
||||
for (label, material, thermal) in solids {
|
||||
let Some(m) = self.materials.get(material).filter(|m| m.hyperelastic.is_some()) else { continue };
|
||||
if !self.nonlinear {
|
||||
return Err(Error::NotSupported(format!(
|
||||
"hyperelastic material `{}` (element {label}) without nonlinear geometry; hyperelastic laws are finite-strain, set the step's nonlinear_geometry to true",
|
||||
m.name
|
||||
)));
|
||||
}
|
||||
if thermal != 0.0 {
|
||||
return Err(Error::NotSupported(format!(
|
||||
"thermal strain in hyperelastic material `{}` (element {label}) in explicit dynamics",
|
||||
m.name
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add an element's row-sum lumped masses.
|
||||
fn lump(&mut self, nodes: &[usize], mass: &[f64]) {
|
||||
for (node, v) in nodes.iter().zip(mass) {
|
||||
|
||||
Reference in new issue
Block a user