forked from 1b-agent/fieldcraft-upstream
Implicit solver: fem kernels (tet4/10, hex8, hex20), sparse Cholesky, block Lanczos, solve.* commands (M4) (#23)
* Renumber the layer table for solver crates: fem at L1, solver at L2, fsi at L3 L0 model -> L1 geometry, mesh, fem, decks, render -> L2 solver (explicit, lbm later) -> L3 fsi (later) -> L4 doc -> L5 io -> L6 engine -> L7 ui-egui, mcp -> apps. UI_MIN_LAYER moves to 7, the testkit sits above it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * modelcraft-fem: element kernels for tet4, tet10, hex8 (full, incompatible modes), hex20 (full, reduced) Shape functions, Gauss and conical-product quadrature, stiffness, consistent and HRZ-lumped mass, body forces, consistent pressure loads on faces, integration-point stresses and their least-squares extrapolation to nodes, isotropic and orthotropic elasticity. The element table is one row per kind, so wedges, shells and beams are added locally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * modelcraft-solver: linear static and frequency analysis with an analytic verification suite Assembly from the resolved model on the node graph, boundary conditions by elimination, consistent pressure, gravity and body-force loads, nested-dissection ordering, sparse up-looking Cholesky with a factor-size cap, reactions, integration-point and nodal-averaged stresses, von Mises; shift-invert block Lanczos with full reorthogonalization for natural modes, participation and effective mass. ADR 0001 records the own-linear-algebra decision (determinism, wasm). Verified against closed forms: displacement and traction patch tests for every element, cantilever tip deflection (Timoshenko) with convergence rates, Lamé cylinder, Kirsch hole (Kt -> 3), cantilever and free-free beam frequencies with six rigid-body modes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Engine: solve.static, solve.frequency and solve.probe; results kept in the session Results are stored per step, restored on a failed command and cleared when the model changes. Caps (degrees of freedom, factor entries, Lanczos vectors) may only be lowered; hostile parameters are tested against a solvable session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs: implicit solver architecture, roadmap M4, parity and references Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * Simplify the solver: shared face integration, block assembly, cached extrapolation - model: one face integrator (nodal::face_normal_weights) for tractions and pressures - fem: mid-side tables from ElementKind::edges, orthotropic via inverse3, symmetric accumulation without a symmetrize pass, incompatible-mode recovery without K_uu, model's coordinate check, dead table rows removed - solver: one pattern search per node pair (block, scalar and diagonal adds), extrapolation built once per element type, reactions from the prescribed rows only, matrices dropped after restriction, the free-body shift keyed on the factorization error, step selection in the solver, Error::Element naming the element - engine: the solver owns the default step and the shift check; the model comparison runs once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs: results are stamped with the session revision 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
+4559
-53
No files matched your search
@@ -17,11 +17,13 @@ pub mod cmd;
|
||||
pub mod history;
|
||||
pub mod sample;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use modelcraft_geometry::Design;
|
||||
use modelcraft_geometry::tagged_mesh::TaggedSurface;
|
||||
use modelcraft_model::Model;
|
||||
use modelcraft_solver::StepResult;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::history::History;
|
||||
@@ -42,6 +44,8 @@ pub enum EngineError {
|
||||
File(#[from] modelcraft_io::Error),
|
||||
#[error(transparent)]
|
||||
Deck(#[from] modelcraft_decks::Error),
|
||||
#[error(transparent)]
|
||||
Solver(#[from] modelcraft_solver::Error),
|
||||
/// A command panicked; the model is unchanged.
|
||||
#[error("internal error in {command}: {message}")]
|
||||
Internal { command: String, message: String },
|
||||
@@ -110,6 +114,9 @@ pub struct Session {
|
||||
/// The revision last saved to (or opened from) `path`; `None` when the content was never
|
||||
/// saved, where only the empty start (revision 0) is clean.
|
||||
saved_revision: Option<u64>,
|
||||
/// The latest result of each solved step, by step name, with the revision it was solved at;
|
||||
/// [`Session::result`] hands out only those that describe the current state.
|
||||
results: BTreeMap<String, (u64, Arc<StepResult>)>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
@@ -163,6 +170,22 @@ impl Session {
|
||||
self.saved_revision.unwrap_or(0) != self.revision
|
||||
}
|
||||
|
||||
/// The stored result of a step, if it was solved in the current state (an edit makes it stale;
|
||||
/// undoing back to the solved state makes it current again).
|
||||
pub fn result(&self, step: &str) -> Option<&Arc<StepResult>> {
|
||||
self.results.get(step).filter(|(revision, _)| *revision == self.revision).map(|(_, r)| r)
|
||||
}
|
||||
|
||||
/// The steps with a current result.
|
||||
pub fn solved_steps(&self) -> impl Iterator<Item = &str> {
|
||||
self.results.iter().filter(|(_, (revision, _))| *revision == self.revision).map(|(name, _)| name.as_str())
|
||||
}
|
||||
|
||||
/// Keep a step's result for the current state.
|
||||
pub(crate) fn store_result(&mut self, result: StepResult) {
|
||||
self.results.insert(result.step().to_string(), (self.revision, Arc::new(result)));
|
||||
}
|
||||
|
||||
/// The current state is what `path` now holds.
|
||||
pub(crate) fn mark_saved(&mut self, path: String) {
|
||||
self.path = Some(path);
|
||||
|
||||
Reference in new issue
Block a user