forked from 1b-agent/fieldcraft-upstream
Explicit dynamics core: central differences, one-point hex8 with hourglass control, nodal-pressure tet4, solve.explicit (M10 X1) (#27)
* Model: explicit dynamic steps (end time, increment scale and cap, output intervals, initial velocities, rigid walls, hourglass and bulk viscosity controls); deck writers refuse them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * fem: uniform-strain hex8 and tet4 kinematics, Flanagan–Belytschko hourglass shape vectors, element frequency bound Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Explicit solver crate (L2): central differences with lumped mass, one-point hex8 with Flanagan–Belytschko hourglass control, Bonet–Burton tet4, bulk viscosity, rigid walls, energy account; wave and impact verification Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Engine: solve.explicit and solve.history commands, explicit results in the session and solve.probe; cantilever period against the modal solver Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Docs: explicit solver in the architecture, roadmap (M10 started), parity and literature (Part H) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Simplify: Model::select_step shared by both solvers, Procedure::sets/is_dynamic, fused per-increment passes without allocation, shared element tail, generic command caps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Catalogue: solve.explicit is ready; solve.history beside solve.probe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Rebase onto the implicit couplings work: Procedure is no longer Copy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm --------- Co-authored-by: jyviko <2501648+jyviko@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
33 files changed
+2757
-65
No files matched your search
@@ -0,0 +1,99 @@
|
||||
//! What an explicit dynamic run keeps: histories sampled at the history interval and field
|
||||
//! frames at the output interval.
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
/// The energy account at one time (Belytschko, Liu, Moran, Elkhodary 2014, §6.2.3). The works of
|
||||
/// the internal and external forces are integrated with the trapezoidal rule over each
|
||||
/// increment's displacement; the constraints' with their impulses (see [`crate::integrate`]).
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize)]
|
||||
pub struct Energies {
|
||||
/// ½ vᵀ M v.
|
||||
pub kinetic: f64,
|
||||
/// Work of the material stresses: the strain energy for an elastic material.
|
||||
pub internal: f64,
|
||||
/// Work of the hourglass forces (stored in the stiffness form, dissipated in the viscous one).
|
||||
pub hourglass: f64,
|
||||
/// Work of the bulk viscosity (dissipated).
|
||||
pub viscous: f64,
|
||||
/// Work of the loads.
|
||||
pub external: f64,
|
||||
/// Work of the boundary conditions and rigid walls on the model.
|
||||
pub constraint: f64,
|
||||
/// kinetic + internal + hourglass + viscous − external − constraint − the initial kinetic
|
||||
/// energy: zero when energy is conserved.
|
||||
pub balance: f64,
|
||||
}
|
||||
|
||||
impl Energies {
|
||||
/// The balance relative to the largest energy in the account.
|
||||
pub fn relative_error(&self, initial_kinetic: f64) -> f64 {
|
||||
let scale = [initial_kinetic, self.kinetic, self.internal + self.hourglass + self.viscous, self.external.abs(), self.constraint.abs()]
|
||||
.into_iter()
|
||||
.fold(0.0, f64::max);
|
||||
if scale > 0.0 { self.balance.abs() / scale } else { 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields at one time. Vectors that the step's output does not request are empty.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Frame {
|
||||
pub time: f64,
|
||||
/// Per node in [`ExplicitResult::nodes`] order.
|
||||
pub displacement: Vec<[f64; 3]>,
|
||||
pub velocity: Vec<[f64; 3]>,
|
||||
/// Per element in [`ExplicitResult::elements`] order (one-point elements: one value each):
|
||||
/// xx, yy, zz, xy, xz, yz, engineering shear strains.
|
||||
pub stress: Vec<[f64; 6]>,
|
||||
pub strain: Vec<[f64; 6]>,
|
||||
}
|
||||
|
||||
/// Quantities sampled through the run.
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct History {
|
||||
pub time: Vec<f64>,
|
||||
/// The time increment taken at each sample.
|
||||
pub increment: Vec<f64>,
|
||||
pub energies: Vec<Energies>,
|
||||
/// Total momentum M v.
|
||||
pub momentum: Vec<[f64; 3]>,
|
||||
/// Sum of the forces the boundary conditions and rigid walls exert on the model.
|
||||
pub constraint_force: Vec<[f64; 3]>,
|
||||
/// The nodes with histories, and per sample their displacement and velocity (in this order).
|
||||
pub nodes: Vec<u32>,
|
||||
pub displacement: Vec<Vec<[f64; 3]>>,
|
||||
pub velocity: Vec<Vec<[f64; 3]>>,
|
||||
}
|
||||
|
||||
/// An element that limits the stable time increment.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
|
||||
pub struct ControllingElement {
|
||||
pub label: u32,
|
||||
/// Its critical increment at rest (no bulk viscosity), before the scale factor.
|
||||
pub increment: f64,
|
||||
}
|
||||
|
||||
/// The result of an explicit dynamic step.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExplicitResult {
|
||||
pub step: String,
|
||||
/// Node labels with degrees of freedom, sorted.
|
||||
pub nodes: Vec<u32>,
|
||||
/// Element labels, sorted.
|
||||
pub elements: Vec<u32>,
|
||||
pub frames: Vec<Frame>,
|
||||
pub history: History,
|
||||
/// The elements with the smallest critical increments (at most ten), smallest first.
|
||||
pub controlling: Vec<ControllingElement>,
|
||||
/// The first time increment taken (the scale times the smallest critical increment).
|
||||
pub initial_increment: f64,
|
||||
pub increments: u64,
|
||||
/// The time reached: the step's end time unless the run stopped at the increment cap.
|
||||
pub time: f64,
|
||||
pub completed: bool,
|
||||
pub total_mass: f64,
|
||||
/// The energy account at the end.
|
||||
pub energy: Energies,
|
||||
pub initial_kinetic: f64,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
Reference in new issue
Block a user