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
@@ -216,6 +216,9 @@ fn check_expressible(model: &Model) -> Result<()> {
|
||||
));
|
||||
}
|
||||
}
|
||||
for step in model.steps().iter().filter(|s| matches!(s.procedure, Procedure::ExplicitDynamic(_))) {
|
||||
bad.insert(format!("step `{}`: explicit dynamic steps (not written to Abaqus input yet)", step.name));
|
||||
}
|
||||
for (name, c) in model.couplings() {
|
||||
if c.kind == CouplingKind::Distributing && !(1..=3).all(|d| c.dofs.contains(d)) {
|
||||
bad.insert(format!("coupling `{name}`: an Abaqus distributing coupling always couples translations 1-3"));
|
||||
@@ -787,6 +790,9 @@ impl Deck {
|
||||
self.comment("loads have no effect on natural frequencies and are not written");
|
||||
}
|
||||
}
|
||||
Procedure::ExplicitDynamic(_) => {
|
||||
return Err(Error::Unsupported { format: FORMAT, what: format!("step `{}`: explicit dynamic steps", step.name) });
|
||||
}
|
||||
}
|
||||
self.output(step);
|
||||
self.line("*END STEP");
|
||||
|
||||
@@ -235,6 +235,9 @@ fn plan<'a>(model: &'a Model, r: &Resolved) -> Result<Plan<'a>> {
|
||||
}
|
||||
let mut gravity_sets: BTreeSet<Option<&str>> = BTreeSet::new();
|
||||
for step in model.steps() {
|
||||
if matches!(step.procedure, Procedure::ExplicitDynamic(_)) {
|
||||
refused.insert(format!("step `{}`: explicit dynamic steps (this writer writes implicit analyses so far)", step.name));
|
||||
}
|
||||
let is_static = matches!(step.procedure, Procedure::Static {});
|
||||
for b in &step.boundary_conditions {
|
||||
let Some(bc) = model.boundary_conditions().get(b) else { continue };
|
||||
@@ -584,6 +587,9 @@ fn write_step(d: &mut Deck, model: &Model, r: &Resolved, plan: &Plan, step: &Ste
|
||||
d.card([Field::Blank, Field::Blank, Field::Blank, Field::Blank, Field::from(1)]);
|
||||
}
|
||||
}
|
||||
Procedure::ExplicitDynamic(_) => {
|
||||
return Err(Error::Unsupported { format: FORMAT, what: format!("step `{}`: explicit dynamic steps", step.name) });
|
||||
}
|
||||
}
|
||||
d.line("*CONTROL_TERMINATION");
|
||||
d.header(&["endtim"]);
|
||||
|
||||
@@ -234,6 +234,9 @@ struct Deck<'m> {
|
||||
|
||||
/// The solution sequence of a list of steps, or why there is none.
|
||||
fn solution(steps: &[&Step]) -> std::result::Result<u32, String> {
|
||||
if let Some(s) = steps.iter().find(|s| matches!(s.procedure, Procedure::ExplicitDynamic(_))) {
|
||||
return Err(format!("step `{}`: explicit dynamic steps (Nastran's linear solutions have none)", s.name));
|
||||
}
|
||||
let statics = steps.iter().find(|s| matches!(s.procedure, Procedure::Static {}));
|
||||
let modes = steps.iter().find(|s| matches!(s.procedure, Procedure::Frequency { .. }));
|
||||
match (statics, modes) {
|
||||
@@ -581,8 +584,8 @@ impl<'m> Deck<'m> {
|
||||
comment(&mut out, "eigenvalue extraction");
|
||||
}
|
||||
for sub in &self.subcases {
|
||||
if let (Some(sid), Procedure::Frequency { modes, min_hz, max_hz }) = (sub.method, sub.step.procedure) {
|
||||
Card::new("EIGRL").int(sid).opt(min_hz).opt(max_hz).int(modes).write(&mut out)?;
|
||||
if let (Some(sid), Procedure::Frequency { modes, min_hz, max_hz }) = (sub.method, &sub.step.procedure) {
|
||||
Card::new("EIGRL").int(sid).opt(*min_hz).opt(*max_hz).int(*modes).write(&mut out)?;
|
||||
}
|
||||
}
|
||||
out.push_str("ENDDATA\n");
|
||||
|
||||
Reference in new issue
Block a user