//! FieldCraft's explicit dynamics solver: transient response of a model's explicit dynamic step //! by central-difference time integration. //! //! The method is the explicit central-difference scheme of T. Belytschko, W. K. Liu, B. Moran, //! K. Elkhodary, *Nonlinear Finite Elements for Continua and Structures*, 2nd ed., Wiley 2014 //! (ch. 6, the flowchart of Box 6.1): with a diagonal (lumped) mass M, //! a = M⁻¹(f_ext − f_int), v^{n+½} = v^{n−½} + Δt^n a^n, u^{n+1} = u^n + Δt^{n+½} v^{n+½}, //! so a time increment is one pass over the elements and a division per degree of freedom. //! //! - **System** ([`system`]): the model is resolved; one-point hexahedra and nodal-pressure //! tetrahedra with their materials, row-sum lumped masses, prescribed velocities, rigid walls, //! initial velocities and the constant load vector of the step. //! - **Elements** ([`elements`]): the uniform-strain hexahedron with Flanagan–Belytschko //! hourglass control (viscous or stiffness), the Bonet–Burton average nodal pressure //! tetrahedron, von Neumann–Richtmyer/Wilkins bulk viscosity, and the element critical time //! increments. //! - **Contact** ([`contact`]): penalty contact between element faces (pairs both ways, single //! surface and general contact) with Coulomb friction by a regularised return map, a bucket //! grid search, and the stiffness from the masses and the stable increment. //! - **Integration** ([`integrate`]): the time loop, kinematic constraints (rigid walls with //! friction), energy accounting with a balance check, history samples and field frames //! ([`result`]). //! //! With nonlinear geometry (the default) the elements are updated Lagrangian and the material //! state is integrated in rate form with an objective stress rate (Jaumann by Hughes–Winget, //! Green–Naghdi by Flanagan–Taylor); materials are hypoelastic or J2 plastic (bilinear, //! tabulated, Johnson–Cook) with Johnson–Cook damage and element erosion ([`elements`]; //! docs/solvers/literature.md, Parts F and J). Without it the gradients stay those of the //! undeformed mesh (small displacements). //! //! Elements are updated on threads ([`fieldcraft_parallel`]), each owning its state, and their //! nodal forces are added in label order as a sequential loop adds them, with no parallel //! reductions, so the same model gives the same numbers bit for bit on any number of threads; //! sizes, increments and output are capped by [`Limits`]. #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] #![forbid(unsafe_code)] // Small fixed-size vector kernels read as the textbook formulas with index loops. #![allow(clippy::needless_range_loop)] pub mod contact; pub mod discrete; pub mod elements; pub mod integrate; pub mod result; pub mod system; pub use elements::Failure; pub use integrate::solve_explicit; pub use result::{ContactSummary, ControllingElement, Energies, ErodedElement, ExplicitResult, Frame, History, MassScalingSummary}; #[derive(Debug, Clone, PartialEq, thiserror::Error)] pub enum Error { #[error(transparent)] Model(#[from] fieldcraft_model::Error), #[error("invalid input: {0}")] Invalid(String), #[error("not supported yet: {0}")] NotSupported(String), /// An element kernel failed (inverted geometry, …). #[error("element {label}: {source}")] Element { label: u32, source: fieldcraft_fem::Error }, #[error("limit reached: {0}")] Limit(String), /// The solution stopped being finite. #[error("the solution became unstable: {0}")] Unstable(String), /// A broken internal invariant (a bug), reported instead of a panic. #[error("internal error: {0}")] Internal(String), } pub type Result = std::result::Result; impl Error { /// A kernel error, naming the element. pub fn element(label: u32, source: fieldcraft_fem::Error) -> Error { Error::Element { label, source } } } /// Caps on the size and length of a run. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Limits { /// Degrees of freedom of the mesh. pub max_dofs: usize, /// Time increments; the step's own cap applies too, whichever is lower. pub max_increments: u64, /// Numbers kept in field frames and histories together (8 bytes each). pub max_output_values: usize, /// Element faces of every contact surface together. pub max_contact_faces: usize, } impl Limits { pub const MAX_DOFS: usize = 6_000_000; pub const MAX_INCREMENTS: u64 = fieldcraft_model::steps::MAX_INCREMENTS; pub const MAX_OUTPUT_VALUES: usize = 50_000_000; pub const MAX_CONTACT_FACES: usize = 10_000_000; } impl Default for Limits { fn default() -> Limits { Limits { max_dofs: Limits::MAX_DOFS, max_increments: Limits::MAX_INCREMENTS, max_output_values: Limits::MAX_OUTPUT_VALUES, max_contact_faces: Limits::MAX_CONTACT_FACES, } } }