forked from 1b-agent/fieldcraft-upstream
Implicit solver: reference points, couplings, moments, wedge6 (M4) (#26)
* fem: wedge6 element (triangle × interval, 3 × 2 points), patch and cantilever tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * solver: reference points, kinematic and distributing couplings, moments by constraint elimination Coupled reference points get six degrees of freedom; kinematic couplings are rigid links, distributing couplings least-squares interpolation constraints. Every constraint row and prescribed value is eliminated master-slave (Abel-Shephard pivot choice), so the reduced matrix stays SPD; no coupling gives the old elimination bit for bit. Verified: rigid motion of a coupled block, clamping through a point, distributing tension vs pressure, bending and tube torsion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * engine: reference point results in solve.static and solve.probe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * docs: couplings, moments and wedge6 in the solver docs, literature and roadmap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * solver: simplify constraint elimination and reference point plumbing Rows combine by one sort; T_f transposed once; K and M reduced in one pass on a shared pattern; prescribed motion computed once; reference point loads and BCs go through Resolved like node ones (Mesh::label_dofs); one dof-direction rule; StepResult::locate for probes; dead Csr::restrict removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm * solver: refuse oversized reductions before allocating them A held distributing coupling point makes a dense (3N)^2 block; estimate it from the constraint rows and refuse it against the factorization's memory budget with a clear message, before anything is built. Other reductions with dependents count their entries first (stopping at the budget) and reserve fallibly. 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:
18 files changed
+1502
-199
No files matched your search
@@ -1,13 +1,13 @@
|
||||
//! Structured meshes generated in code for the analytic verification suite: a block of
|
||||
//! hexahedra (hex8, hex20) or Kuhn-split tetrahedra (tet4, tet10) on [0, 1]³, mapped to the part
|
||||
//! hexahedra (hex8, hex20), of wedges (two per cell) or of Kuhn-split tetrahedra (tet4, tet10) on [0, 1]³, mapped to the part
|
||||
//! by a function, and helpers to define the model on it.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use modelcraft_model::{
|
||||
BoundaryCondition, Elastic, ElementFace, ElementKind, Formulation, Integration, Load, Material, Model, Procedure, Section, SectionKind, Set,
|
||||
Step, Symmetry, Target,
|
||||
BoundaryCondition, Coupling, CouplingKind, Dofs, Elastic, ElementFace, ElementKind, Formulation, Integration, Load, Material, Model, Procedure,
|
||||
ReferencePoint, Section, SectionKind, Set, Step, Symmetry, Target,
|
||||
};
|
||||
|
||||
pub struct Grid {
|
||||
@@ -54,6 +54,15 @@ pub fn grid(n: [usize; 3], kind: ElementKind, map: impl Fn([f64; 3]) -> [f64; 3]
|
||||
model.set_element(next, kind, nodes).unwrap();
|
||||
next += 1;
|
||||
}
|
||||
ElementKind::Wedge6 => {
|
||||
// Two prisms along z, split by the cell's (x, y) diagonal, both counter-clockwise from above.
|
||||
for tri in [[[0, 0], [2, 0], [2, 2]], [[0, 0], [2, 2], [0, 2]]] {
|
||||
let nodes: Vec<u32> =
|
||||
[0, 2].iter().flat_map(|&z| tri.map(|t| [t[0], t[1], z])).map(|c| node(&mut model, at(c))).collect();
|
||||
model.set_element(next, kind, nodes).unwrap();
|
||||
next += 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Six tetrahedra around the cell's main diagonal (Kuhn), conforming across cells.
|
||||
for perm in [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]] {
|
||||
@@ -144,6 +153,15 @@ impl Grid {
|
||||
self.bc(name, BoundaryCondition::Symmetry { target: Target::Set(name.into()), plane });
|
||||
}
|
||||
|
||||
/// A reference point `name` at `xyz`, coupled to the nodes where `f` holds.
|
||||
pub fn couple(&mut self, name: &str, xyz: [f64; 3], kind: CouplingKind, dofs: &[u8], f: impl Fn([f64; 3]) -> bool) {
|
||||
let set = format!("{name}_nodes");
|
||||
self.node_set(&set, f);
|
||||
self.model.add_reference_point(name, ReferencePoint { xyz, label: 0 }).unwrap();
|
||||
let dofs = Dofs::try_from(dofs.to_vec()).unwrap();
|
||||
self.model.add_coupling(&format!("{name}_coupling"), Coupling { reference_point: name.into(), set, kind, dofs }).unwrap();
|
||||
}
|
||||
|
||||
pub fn fixed(&mut self, name: &str, f: impl Fn([f64; 3]) -> bool) {
|
||||
self.node_set(name, f);
|
||||
self.bc(name, BoundaryCondition::Fixed { target: Target::Set(name.into()) });
|
||||
@@ -171,6 +189,7 @@ pub fn variants() -> Vec<(ElementKind, Formulation, &'static str)> {
|
||||
vec![
|
||||
(ElementKind::Tet4, full(), "tet4"),
|
||||
(ElementKind::Tet10, full(), "tet10"),
|
||||
(ElementKind::Wedge6, full(), "wedge6"),
|
||||
(ElementKind::Hex8, full(), "hex8"),
|
||||
(ElementKind::Hex8, incompatible(), "hex8 incompatible"),
|
||||
(ElementKind::Hex20, full(), "hex20"),
|
||||
|
||||
Reference in new issue
Block a user