forked from 1b-agent/fieldcraft-upstream
model: nonlinear static steps and neo-Hookean hyperelasticity, mapped by the deck writers
A nonlinear_static procedure (nonlinear geometry, initial/min/max increments, increment and iteration caps, force/displacement/energy tolerances, line search), validated with caps against hostile values. Materials gain an explicit hyperelastic option (decoupled compressible neo-Hookean on the isotropic constants). Abaqus writes *STEP, NLGEOM, INC / *STATIC with the increments and *HYPERELASTIC, NEO HOOKE; LS-DYNA writes the ramped implicit run with full Newton and *CONTROL_IMPLICIT_AUTO; Nastran refuses SOL 400 by name, as LS-DYNA does hyperelastic materials in nonlinear steps. Explicit dynamics refuses hyperelastic materials by name; solve.run names the solver still to come. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
d2fe8a8303
commit
bc9fe2191a
13 files changed
+408
-19
No files matched your search
@@ -737,3 +737,37 @@ fn plasticity_is_written_as_plastic_materials() {
|
||||
let Err(Error::Unsupported { what, .. }) = lsdyna::write(&with(jc, None)) else { panic!() };
|
||||
assert!(what.contains("Johnson–Cook"), "{what}");
|
||||
}
|
||||
|
||||
/// A nonlinear static step: the ramped implicit run with full Newton and automatic increments;
|
||||
/// what LS-DYNA cannot take is named.
|
||||
#[test]
|
||||
fn nonlinear_static_steps() {
|
||||
let mut m = cantilever();
|
||||
let controls =
|
||||
NonlinearStatic { initial_increment: 0.05, min_increment: 1e-4, max_increment: 0.5, max_iterations: 12, ..NonlinearStatic::default() };
|
||||
m.replace_step(step("Bend", Procedure::NonlinearStatic(controls.clone()), &["clamp"], &["tip_load"])).unwrap();
|
||||
let deck = lsdyna::write(&m).unwrap().text;
|
||||
let after = |keyword: &str| -> Vec<String> {
|
||||
let mut lines = deck.lines().skip_while(|l| *l != keyword).skip(1).filter(|l| !l.starts_with('$'));
|
||||
lines.next().unwrap().split_whitespace().map(str::to_string).collect()
|
||||
};
|
||||
let num = |s: &String| s.parse::<f64>().unwrap();
|
||||
let general = after("*CONTROL_IMPLICIT_GENERAL");
|
||||
assert_eq!((num(&general[0]), num(&general[1])), (1.0, 0.05), "{general:?}");
|
||||
let solution = after("*CONTROL_IMPLICIT_SOLUTION");
|
||||
assert_eq!(solution.iter().map(num).collect::<Vec<_>>(), [2.0, 1.0, 12.0], "{solution:?}");
|
||||
let auto = after("*CONTROL_IMPLICIT_AUTO");
|
||||
assert_eq!(auto.iter().map(num).collect::<Vec<_>>(), [1.0, 1e-4, 0.5], "blank fields are skipped: {auto:?}");
|
||||
assert!(deck.contains("*DEFINE_CURVE_TITLE\nBend ramp\n"), "the loads follow the ramp");
|
||||
assert!(deck.contains("*LOAD_NODE"), "{deck}");
|
||||
|
||||
m.replace_step(step("Bend", Procedure::NonlinearStatic(NonlinearStatic { nonlinear_geometry: false, ..controls }), &["clamp"], &["tip_load"]))
|
||||
.unwrap();
|
||||
unsupported(&m, "a nonlinear step without nonlinear geometry");
|
||||
m.replace_step(step("Bend", Procedure::NonlinearStatic(NonlinearStatic::default()), &["clamp"], &["tip_load"])).unwrap();
|
||||
m.replace_material("Steel", Material { hyperelastic: Some(Hyperelastic::NeoHookean {}), ..steel() }).unwrap();
|
||||
unsupported(&m, "hyperelastic material `Steel` in a nonlinear step");
|
||||
// In a linear step the elastic constants are the whole of a hyperelastic material.
|
||||
m.replace_step(step("Bend", Procedure::Static {}, &["clamp"], &["tip_load"])).unwrap();
|
||||
assert!(lsdyna::write(&m).unwrap().text.contains("*MAT_ELASTIC_TITLE"));
|
||||
}
|
||||
Reference in new issue
Block a user