Add diagram workbench UI with Modelica DoF coaching and ISO glyphs.

Ship the Next.js cycle editor with CAD chrome, technical HX symbols, Fixed/Free boundary guidance, and secondary water/air pressure drop support in the solver stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-17 22:46:46 +02:00
parent 62efea0646
commit 3358b74342
275 changed files with 70187 additions and 5230 deletions

View File

@@ -5,6 +5,7 @@
use entropyk::{
BoundedVariable, BoundedVariableId, ComponentOutput, Constraint, ConstraintId, SystemBuilder,
ThermoError, TopologyError,
};
use entropyk_components::{
Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector,
@@ -49,18 +50,13 @@ fn test_builder_constraints_link_and_validate_dof() {
},
5.0,
);
let valve = BoundedVariable::new(
BoundedVariableId::new("valve"),
0.5,
0.0,
1.0,
)
.expect("valid bounds");
let valve =
BoundedVariable::new(BoundedVariableId::new("valve"), 0.5, 0.0, 1.0).expect("valid bounds");
// Minimal topology: 2 nodes, 1 edge → 2 edge unknowns (P,h). With 1 constraint and 1 control
// we need 3 equations total: 2 component eqs + 1 constraint = 3 = 2 + 1 unknowns.
// Minimal topology: 2 nodes, 1 edge → 3 edge unknowns (ṁ,P,h). With 1 constraint and 1 control
// we need 4 equations total: 3 component eqs + 1 constraint = 4 = 3 + 1 unknowns.
let system = SystemBuilder::new()
.component("evap", Box::new(MockComponent { n_eqs: 1 }))
.component("evap", Box::new(MockComponent { n_eqs: 2 }))
.unwrap()
.component("other", Box::new(MockComponent { n_eqs: 1 }))
.unwrap()
@@ -103,16 +99,16 @@ fn test_builder_dof_imbalance_two_constraints_one_control() {
},
3.0,
);
let valve = BoundedVariable::new(
BoundedVariableId::new("valve"),
0.5,
0.0,
1.0,
)
.expect("valid bounds");
let valve =
BoundedVariable::new(BoundedVariableId::new("valve"), 0.5, 0.0, 1.0).expect("valid bounds");
let system = SystemBuilder::new()
.component("evap", Box::new(MockComponent { n_eqs: 1 }))
// Same topology as test 1 (3 component eqs total), but 2 constraints and 1 control →
// 3+2=5 equations vs 3+1=4 unknowns → over-constrained. Since `System::finalize()`
// enforces the DoF gate, `build()` itself now rejects the system with
// `TopologyError::DofImbalance` (the post-build `validate_inverse_control_dof()`
// check is unreachable for over-constrained systems).
let result = SystemBuilder::new()
.component("evap", Box::new(MockComponent { n_eqs: 2 }))
.unwrap()
.component("other", Box::new(MockComponent { n_eqs: 1 }))
.unwrap()
@@ -129,14 +125,17 @@ fn test_builder_dof_imbalance_two_constraints_one_control() {
&BoundedVariableId::new("valve"),
)
.unwrap()
.build()
.expect("build should succeed");
.build();
// DoF validation should fail: 2 constraints but only 1 control (unbalanced).
let dof_result = system.validate_inverse_control_dof();
assert!(
dof_result.is_err(),
"validate_inverse_control_dof should fail with 2 constraints and 1 control, got: {:?}",
dof_result
);
// DoF gate should reject the build: 2 constraints but only 1 control (unbalanced).
match result {
Err(ThermoError::Topology(TopologyError::DofImbalance { message })) => {
assert!(
message.contains("over-constrained"),
"expected an over-constrained DoF report, got: {message}"
);
}
Err(other) => panic!("expected DofImbalance error from build(), got: {other}"),
Ok(_) => panic!("build() should fail with DofImbalance (2 constraints, 1 control)"),
}
}