Files
OneCAD-Simulation/crates/explicit/src/result.rs
T
d45a2da5b5 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>
2026-10-13 18:28:32 -04:00

100 lines
3.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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>,
}