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,9 +5,12 @@ use entropyk_components::{
ResidualVector, ScrewEconomizerCompressor, ScrewPerformanceCurves, StateSlice,
};
use entropyk_core::{Enthalpy, MassFlow, Power, Pressure};
use entropyk_solver::inverse::{BoundedVariable, BoundedVariableId, ComponentOutput, Constraint, ConstraintId};
use entropyk_solver::inverse::{
BoundedVariable, BoundedVariableId, ComponentOutput, Constraint, ConstraintId,
};
use entropyk_solver::system::System;
#[allow(dead_code)] // Convenience alias kept for readability in this fixture.
type CP = Port<Connected>;
fn make_port(fluid: &str, p_bar: f64, h_kj_kg: f64) -> ConnectedPort {
@@ -34,6 +37,7 @@ fn make_screw_curves() -> ScrewPerformanceCurves {
struct Mock {
n: usize,
#[allow(dead_code)] // Stored for fixture completeness; not asserted in this test.
circuit_id: CircuitId,
}
@@ -91,7 +95,7 @@ fn test_real_cycle_inverse_control_integration() {
let comp_suc = make_port("R134a", 3.2, 400.0);
let comp_dis = make_port("R134a", 12.8, 440.0);
let comp_eco = make_port("R134a", 6.4, 260.0);
let comp = ScrewEconomizerCompressor::new(
make_screw_curves(),
"R134a",
@@ -100,17 +104,28 @@ fn test_real_cycle_inverse_control_integration() {
comp_suc,
comp_dis,
comp_eco,
).unwrap();
)
.unwrap();
let coil = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
let exv = Mock::new(2, 0); // Expansion Valve
let evap = Mock::new(2, 0); // Evaporator
// CM1.4 DoF balance for a 4-edge series cycle (state_len=10: 1 branch + 8 P,h + 1 eco embed):
// ScrewEco (6 eqs) + MchxCoil (2 eqs with same_branch_m) + exv (1) + evap (1) = 10 ✓
let exv = Mock::new(1, 0); // Expansion Valve — 1 equation (simplified pass-through)
let evap = Mock::new(1, 0); // Evaporator — 1 equation (simplified pass-through)
// 2. Add components to system
let comp_node = sys.add_component_to_circuit(Box::new(comp), CircuitId::ZERO).unwrap();
let coil_node = sys.add_component_to_circuit(Box::new(coil), CircuitId::ZERO).unwrap();
let exv_node = sys.add_component_to_circuit(Box::new(exv), CircuitId::ZERO).unwrap();
let evap_node = sys.add_component_to_circuit(Box::new(evap), CircuitId::ZERO).unwrap();
let comp_node = sys
.add_component_to_circuit(Box::new(comp), CircuitId::ZERO)
.unwrap();
let coil_node = sys
.add_component_to_circuit(Box::new(coil), CircuitId::ZERO)
.unwrap();
let exv_node = sys
.add_component_to_circuit(Box::new(exv), CircuitId::ZERO)
.unwrap();
let evap_node = sys
.add_component_to_circuit(Box::new(evap), CircuitId::ZERO)
.unwrap();
sys.register_component_name("compressor", comp_node);
sys.register_component_name("condenser", coil_node);
@@ -131,7 +146,8 @@ fn test_real_cycle_inverse_control_integration() {
component_id: "evaporator".to_string(),
},
5.0,
)).unwrap();
))
.unwrap();
// Constraint 2: Capacity at compressor = 50000 W
sys.add_constraint(Constraint::new(
@@ -140,7 +156,8 @@ fn test_real_cycle_inverse_control_integration() {
component_id: "compressor".to_string(),
},
50000.0,
)).unwrap();
))
.unwrap();
// Control 1: Valve Opening
let bv_valve = BoundedVariable::with_component(
@@ -149,7 +166,8 @@ fn test_real_cycle_inverse_control_integration() {
0.5,
0.0,
1.0,
).unwrap();
)
.unwrap();
sys.add_bounded_variable(bv_valve).unwrap();
// Control 2: Compressor Speed
@@ -159,19 +177,22 @@ fn test_real_cycle_inverse_control_integration() {
0.7,
0.3,
1.0,
).unwrap();
)
.unwrap();
sys.add_bounded_variable(bv_comp).unwrap();
// Link constraints to controls
sys.link_constraint_to_control(
&ConstraintId::new("superheat_control"),
&BoundedVariableId::new("valve_opening"),
).unwrap();
)
.unwrap();
sys.link_constraint_to_control(
&ConstraintId::new("capacity_control"),
&BoundedVariableId::new("compressor_speed"),
).unwrap();
)
.unwrap();
// 5. Finalize the system
sys.finalize().unwrap();
@@ -179,31 +200,36 @@ fn test_real_cycle_inverse_control_integration() {
// Verify system state size and degrees of freedom
assert_eq!(sys.constraint_count(), 2);
assert_eq!(sys.bounded_variable_count(), 2);
// Validate DoF
sys.validate_inverse_control_dof().expect("System should be balanced for inverse control");
sys.validate_inverse_control_dof()
.expect("System should be balanced for inverse control");
// Evaluate the total system residual and jacobian capability
let state_len = sys.state_vector_len();
assert!(state_len > 0, "System should have state variables");
// Create mock state and control values
let state = vec![400_000.0; state_len];
let control_values = vec![0.5, 0.7]; // Valve, Compressor speeds
let mut residuals = vec![0.0; state_len + 2];
// Evaluate constraints
let measured = sys.extract_constraint_values_with_controls(&state, &control_values);
let count = sys.compute_constraint_residuals(&state, &mut residuals[state_len..], &measured)
let count = sys
.compute_constraint_residuals(&state, &mut residuals[state_len..], &measured)
.expect("constraint residuals should compute");
assert_eq!(count, 2, "Should have computed 2 constraint residuals");
// Evaluate jacobian
let jacobian_entries = sys.compute_inverse_control_jacobian(&state, state_len, &control_values);
assert!(!jacobian_entries.is_empty(), "Jacobian should have entries for inverse control");
assert!(
!jacobian_entries.is_empty(),
"Jacobian should have entries for inverse control"
);
println!("System integration with inverse control successful!");
}