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

@@ -62,8 +62,11 @@ fn mock(n: usize) -> Box<dyn Component> {
/// Build a minimal 2-component cycle: compressor → evaporator → compressor.
fn build_two_component_cycle() -> System {
let mut sys = System::new();
let comp = sys.add_component(mock(2)); // compressor
let evap = sys.add_component(mock(2)); // evaporator
// CM1.4: 2-edge series cycle → 1 branch + 4 P,h = 5 unknowns.
// Compressor provides a pressure reference (3 equations); evaporator drops
// the redundant mass-conservation row (2 equations). Total: 3+2=5 = balanced.
let comp = sys.add_component(mock(3)); // compressor (pressure reference: 3 eqs)
let evap = sys.add_component(mock(2)); // evaporator (series branch: 2 eqs)
sys.add_edge(comp, evap).unwrap();
sys.add_edge(evap, comp).unwrap();
sys.register_component_name("compressor", comp);
@@ -280,7 +283,8 @@ fn test_full_residual_vector_includes_constraint_rows() {
.traverse_for_jacobian()
.map(|(_, c, _)| c.n_equations())
.sum::<usize>()
+ sys.constraint_residual_count();
+ sys.constraint_residual_count()
+ sys.mass_flow_closure_count();
let state_len = sys.full_state_vector_len();
assert!(
full_eq_count >= 4,
@@ -563,9 +567,12 @@ fn test_multi_variable_control_with_real_components() {
#[test]
fn test_three_constraints_and_three_controls() {
let mut sys = System::new();
let comp = sys.add_component(mock(2)); // compressor
let evap = sys.add_component(mock(2)); // evaporator
let cond = sys.add_component(mock(2)); // condenser
// CM1.4: 3-edge series cycle → 1 branch + 6 P,h = 7 unknowns.
// Compressor: 3 equations (pressure reference); evaporator + condenser: 2 each.
// Total: 3+2+2=7 equations = balanced.
let comp = sys.add_component(mock(3)); // compressor (pressure reference: 3 eqs)
let evap = sys.add_component(mock(2)); // evaporator (series branch: 2 eqs)
let cond = sys.add_component(mock(2)); // condenser (series branch: 2 eqs)
sys.add_edge(comp, evap).unwrap();
sys.add_edge(evap, cond).unwrap();
sys.add_edge(cond, comp).unwrap();
@@ -860,20 +867,9 @@ fn test_2x2_jacobian_block_is_fully_dense() {
5.0,
))
.unwrap();
let bv1 = BoundedVariable::new(
BoundedVariableId::new("compressor_speed"),
50.0,
20.0,
80.0,
)
.unwrap();
let bv2 = BoundedVariable::new(
BoundedVariableId::new("valve_opening"),
0.5,
0.1,
1.0,
)
.unwrap();
let bv1 =
BoundedVariable::new(BoundedVariableId::new("compressor_speed"), 50.0, 20.0, 80.0).unwrap();
let bv2 = BoundedVariable::new(BoundedVariableId::new("valve_opening"), 0.5, 0.1, 1.0).unwrap();
sys.add_bounded_variable(bv1).unwrap();
sys.add_bounded_variable(bv2).unwrap();
sys.link_constraint_to_control(
@@ -912,8 +908,7 @@ fn test_2x2_jacobian_block_is_fully_dense() {
assert!(
found[i][j],
"Jacobian entry ({},{}) is missing or zero — expected dense block",
i,
j
i, j
);
}
}
@@ -948,27 +943,10 @@ fn test_3x3_jacobian_block_is_fully_dense() {
2000000.0,
))
.unwrap();
let bv1 = BoundedVariable::new(
BoundedVariableId::new("compressor_speed"),
50.0,
20.0,
80.0,
)
.unwrap();
let bv2 = BoundedVariable::new(
BoundedVariableId::new("valve_opening"),
0.5,
0.1,
1.0,
)
.unwrap();
let bv3 = BoundedVariable::new(
BoundedVariableId::new("fan_speed"),
0.8,
0.2,
1.0,
)
.unwrap();
let bv1 =
BoundedVariable::new(BoundedVariableId::new("compressor_speed"), 50.0, 20.0, 80.0).unwrap();
let bv2 = BoundedVariable::new(BoundedVariableId::new("valve_opening"), 0.5, 0.1, 1.0).unwrap();
let bv3 = BoundedVariable::new(BoundedVariableId::new("fan_speed"), 0.8, 0.2, 1.0).unwrap();
sys.add_bounded_variable(bv1).unwrap();
sys.add_bounded_variable(bv2).unwrap();
sys.add_bounded_variable(bv3).unwrap();
@@ -1012,8 +990,7 @@ fn test_3x3_jacobian_block_is_fully_dense() {
assert!(
found[i][j],
"3x3 Jacobian entry ({},{}) is missing or zero — expected dense block",
i,
j
i, j
);
}
}
@@ -1041,20 +1018,9 @@ fn test_mimo_cross_derivatives_have_consistent_signs() {
5.0,
))
.unwrap();
let bv1 = BoundedVariable::new(
BoundedVariableId::new("compressor_speed"),
50.0,
20.0,
80.0,
)
.unwrap();
let bv2 = BoundedVariable::new(
BoundedVariableId::new("valve_opening"),
0.5,
0.1,
1.0,
)
.unwrap();
let bv1 =
BoundedVariable::new(BoundedVariableId::new("compressor_speed"), 50.0, 20.0, 80.0).unwrap();
let bv2 = BoundedVariable::new(BoundedVariableId::new("valve_opening"), 0.5, 0.1, 1.0).unwrap();
sys.add_bounded_variable(bv1).unwrap();
sys.add_bounded_variable(bv2).unwrap();
sys.link_constraint_to_control(
@@ -1119,9 +1085,9 @@ fn test_mimo_cross_derivatives_have_consistent_signs() {
/// Helper: builds a three-component system for 3x3 MIMO testing.
fn build_three_component_system() -> System {
let mut sys = System::new();
let comp = sys.add_component(mock(2)); // compressor
let evap = sys.add_component(mock(2)); // evaporator
let cond = sys.add_component(mock(2)); // condenser
let comp = sys.add_component(mock(3)); // compressor
let evap = sys.add_component(mock(3)); // evaporator
let cond = sys.add_component(mock(3)); // condenser
sys.add_edge(comp, evap).unwrap();
sys.add_edge(evap, cond).unwrap();
sys.add_edge(cond, comp).unwrap();