engine: a prescribed displacement or temperature satisfies the loads stage (#356)

The setup checklist asked for a load on rubber-seal, driven only by a
prescribed displacement, while model.check called it ready.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-15 08:55:25 -04:00
1 parent febcffefd6
commit 8819ff787c
2 files changed
+24 -1

No files matched your search

+14 -1
View File
@@ -128,6 +128,8 @@ impl Session {
// Models without elements (point masses, fluid domains) need no material or section.
let elementless = analysable && m.elements().is_empty();
let modal_only = !m.steps().is_empty() && m.steps().iter().all(|st| matches!(st.procedure, Procedure::Frequency { .. }));
// A model driven by a prescribed displacement or temperature needs no load either.
let driven = m.boundary_conditions().values().any(|bc| bc.drives());
let mut next_given = false;
Stage::ALL
.into_iter()
@@ -137,7 +139,7 @@ impl Session {
let optional = match stage {
Stage::Prepare => true,
Stage::Material | Stage::Section => elementless,
Stage::Loads => modal_only,
Stage::Loads => modal_only || driven,
_ => false,
};
let command = r.provider();
@@ -234,6 +236,17 @@ mod tests {
assert_eq!((status(&s, Stage::Results), to_json(&s.progress())["next"]["command"].clone()), (Next, json!("results.contour")));
}
/// A model driven by a prescribed displacement (rubber-seal) needs no load: the checklist
/// agrees with the model check and offers the solve next.
#[test]
fn a_displacement_driven_model_needs_no_load() {
let mut s = Session::default();
s.run_script(&crate::sample::rubber_seal()).unwrap();
assert!(s.model.loads().is_empty());
assert_eq!(status(&s, Stage::Loads), Optional);
assert_eq!(status(&s, Stage::Solve), Next, "{:?}", s.progress());
}
#[test]
fn model_check_returns_the_checklist() {
let mut s = Session::default();
+10
View File
@@ -172,6 +172,16 @@ impl BoundaryCondition {
}
}
/// Whether it drives the model on its own, as a load does: a non-zero prescribed
/// displacement or a non-zero prescribed temperature (a support only holds).
pub fn drives(&self) -> bool {
match self {
BoundaryCondition::Displacement { values, .. } => values.iter().flatten().any(|v| *v != 0.0),
BoundaryCondition::Temperature { value, .. } => *value != 0.0,
BoundaryCondition::Fixed { .. } | BoundaryCondition::Symmetry { .. } => false,
}
}
/// Whether it prescribes temperatures (rather than motion).
pub fn is_thermal(&self) -> bool {
matches!(self, BoundaryCondition::Temperature { .. })