forked from 1b-agent/fieldcraft-upstream
explicit: opt-in mass scaling to a target increment, the added mass reported
An explicit step may ask for mass scaling to a target time increment (on a set, or the whole model): elements whose stable increment is below it get the density that reaches it, with their lumped masses, frequency bound and hourglass scales; adiabatic heating keeps the physical density. The run reports the elements scaled, the added mass in all and per section with its fraction, a warning, and the added mass's kinetic energy in the energy account. Verified on a uniform bar scaled to twice its increment (added mass 3×, increment doubled, a quarter of the kinetic energy left to the physical mass). ROADMAP and parity note the hyperelastic materials and mass scaling. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
ce8e23d05b
commit
bc75fd08d1
14 files changed
+249
-14
No files matched your search
@@ -68,8 +68,8 @@ pub use sections::{BeamProfile, Extent, Formulation, Integration, Section, Secti
|
||||
pub use sets::Set;
|
||||
pub use steps::{
|
||||
ArcConstraint, ArcLength, BulkViscosity, DisplacementLimit, ExplicitDynamic, Hourglass, HourglassKind, Imperfection, InitialVelocity,
|
||||
MAX_LOAD_PATH_POINTS, MAX_NONLINEAR_INCREMENTS, MAX_NONLINEAR_ITERATIONS, NonlinearStatic, OutputVariable, Procedure, RigidWall, Step,
|
||||
StressRate, Tolerances, Transient,
|
||||
MAX_LOAD_PATH_POINTS, MAX_NONLINEAR_INCREMENTS, MAX_NONLINEAR_ITERATIONS, MassScaling, NonlinearStatic, OutputVariable, Procedure, RigidWall,
|
||||
Step, StressRate, Tolerances, Transient,
|
||||
};
|
||||
pub use units::UnitSystem;
|
||||
|
||||
|
||||
@@ -425,6 +425,22 @@ pub struct ExplicitDynamic {
|
||||
/// The objective stress rate of large deformation.
|
||||
#[serde(default)]
|
||||
pub stress_rate: StressRate,
|
||||
/// Opt-in mass scaling: the elements whose stable increment is below a target get the
|
||||
/// density that brings them to it (the added mass is reported).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub mass_scaling: Option<MassScaling>,
|
||||
}
|
||||
|
||||
/// Mass scaling to a target time increment: every element (of `set`, or of the whole model) whose
|
||||
/// stable increment, times the step's scale, is below `target_increment` has its density raised
|
||||
/// by (target / increment)², which brings it to the target. It changes the dynamics: the added
|
||||
/// mass is reported, per section, with its share of the kinetic energy.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MassScaling {
|
||||
pub target_increment: f64,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub set: Option<String>,
|
||||
}
|
||||
|
||||
fn yes() -> bool {
|
||||
@@ -467,6 +483,7 @@ impl ExplicitDynamic {
|
||||
bulk_viscosity: BulkViscosity::default(),
|
||||
nonlinear_geometry: true,
|
||||
stress_rate: StressRate::default(),
|
||||
mass_scaling: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,6 +504,7 @@ impl ExplicitDynamic {
|
||||
.map(String::as_str)
|
||||
.chain(self.initial_velocities.iter().map(|v| v.set.as_str()))
|
||||
.chain(self.rigid_walls.iter().map(|w| w.set.as_str()))
|
||||
.chain(self.mass_scaling.iter().filter_map(|m| m.set.as_deref()))
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
@@ -524,6 +542,12 @@ impl ExplicitDynamic {
|
||||
}
|
||||
w.friction.validate().map_err(|e| Error::Invalid(format!("rigid wall on set `{}`: {e}", w.set)))?;
|
||||
}
|
||||
if let Some(m) = &self.mass_scaling {
|
||||
positive("mass scaling target increment", m.target_increment)?;
|
||||
if m.target_increment > end {
|
||||
return Err(Error::Invalid(format!("the mass scaling target increment {} is longer than the step", m.target_increment)));
|
||||
}
|
||||
}
|
||||
self.hourglass.validate()?;
|
||||
self.bulk_viscosity.validate()
|
||||
}
|
||||
@@ -1012,6 +1036,9 @@ mod tests {
|
||||
},
|
||||
ExplicitDynamic { hourglass: Hourglass { kind: HourglassKind::Viscous, coefficient: 2.0 }, ..base.clone() },
|
||||
ExplicitDynamic { bulk_viscosity: BulkViscosity { linear: -1.0, quadratic: 1.0 }, ..base.clone() },
|
||||
ExplicitDynamic { mass_scaling: Some(MassScaling { target_increment: 0.0, set: None }), ..base.clone() },
|
||||
ExplicitDynamic { mass_scaling: Some(MassScaling { target_increment: f64::NAN, set: None }), ..base.clone() },
|
||||
ExplicitDynamic { mass_scaling: Some(MassScaling { target_increment: 2.0, set: None }), ..base.clone() },
|
||||
];
|
||||
for b in bad {
|
||||
assert!(Procedure::ExplicitDynamic(b.clone()).validate().is_err(), "{b:?}");
|
||||
|
||||
Reference in new issue
Block a user