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:
@@ -44,6 +44,7 @@ use entropyk_solver::{system::System, TopologyError};
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[allow(dead_code)] // Convenience alias kept for readability in this fixture.
|
||||
type CP = Port<Connected>;
|
||||
|
||||
/// Creates a connected port pair — returns the first (connected) port.
|
||||
@@ -87,6 +88,7 @@ fn make_screw_curves() -> ScrewPerformanceCurves {
|
||||
/// Generic mock component: all residuals = 0, n_equations configurable.
|
||||
struct Mock {
|
||||
n: usize,
|
||||
#[allow(dead_code)] // Stored for fixture completeness; not asserted in this test.
|
||||
circuit_id: CircuitId,
|
||||
}
|
||||
|
||||
@@ -150,17 +152,20 @@ fn test_screw_compressor_creation_and_residuals() {
|
||||
ScrewEconomizerCompressor::new(make_screw_curves(), "R134a", 50.0, 0.92, suc, dis, eco)
|
||||
.expect("compressor creation ok");
|
||||
|
||||
assert_eq!(comp.n_equations(), 5);
|
||||
assert_eq!(comp.n_equations(), 6);
|
||||
|
||||
// CM1.3: ṁ values are edge unknowns at indices 0,1,2; W_shaft is internal at index 3.
|
||||
// set_system_context(global_state_offset=3, [(suc:m=0,p,h), (eco:m=1,p,h), (dis:m=2,p,h)])
|
||||
let mut comp = comp;
|
||||
comp.set_system_context(3, &[(0, 0, 0), (1, 0, 0), (2, 0, 0)]);
|
||||
|
||||
// Compute residuals at a plausible operating state
|
||||
let state = vec![
|
||||
1.2, // ṁ_suc [kg/s]
|
||||
0.144, // ṁ_eco [kg/s] = 12% × 1.2
|
||||
400_000.0, // h_suc [J/kg]
|
||||
440_000.0, // h_dis [J/kg]
|
||||
55_000.0, // W_shaft [W]
|
||||
1.2, // state[0] = ṁ_suc [kg/s]
|
||||
0.144, // state[1] = ṁ_eco [kg/s] = 12% × 1.2
|
||||
1.344, // state[2] = ṁ_dis [kg/s] = ṁ_suc + ṁ_eco
|
||||
55_000.0, // state[3] = W_shaft [W] (internal state at offset 3)
|
||||
];
|
||||
let mut residuals = vec![0.0; 5];
|
||||
let mut residuals = vec![0.0; 6];
|
||||
comp.compute_residuals(&state, &mut residuals)
|
||||
.expect("residuals computed");
|
||||
|
||||
@@ -169,8 +174,13 @@ fn test_screw_compressor_creation_and_residuals() {
|
||||
assert!(r.is_finite(), "residual[{}] = {} not finite", i, r);
|
||||
}
|
||||
|
||||
// residuals[5] (mass balance: ṁ_dis - ṁ_suc - ṁ_eco) should be ≈ 0
|
||||
assert!(
|
||||
residuals[5].abs() < 1e-10,
|
||||
"Mass balance residual: {}",
|
||||
residuals[5]
|
||||
);
|
||||
// Residual[4] (shaft power balance): W_calc - W_state
|
||||
// Polynomial at SST~276K, SDT~323K gives ~55000 W → residual ≈ 0
|
||||
println!("Screw residuals: {:?}", residuals);
|
||||
}
|
||||
|
||||
@@ -188,9 +198,12 @@ fn test_screw_vfd_scaling() {
|
||||
ScrewEconomizerCompressor::new(make_screw_curves(), "R134a", 50.0, 0.92, suc, dis, eco)
|
||||
.unwrap();
|
||||
|
||||
// CM1.3: set_system_context so ṁ indices are 0,1,2 and W_shaft is at index 3
|
||||
comp.set_system_context(3, &[(0, 0, 0), (1, 0, 0), (2, 0, 0)]);
|
||||
|
||||
// At full speed (50 Hz): compute mass flow residual
|
||||
let state_full = vec![1.2, 0.144, 400_000.0, 440_000.0, 55_000.0];
|
||||
let mut r_full = vec![0.0; 5];
|
||||
let state_full = vec![1.2, 0.144, 1.344, 55_000.0];
|
||||
let mut r_full = vec![0.0; 6];
|
||||
comp.compute_residuals(&state_full, &mut r_full).unwrap();
|
||||
let m_error_full = r_full[0].abs();
|
||||
|
||||
@@ -198,8 +211,8 @@ fn test_screw_vfd_scaling() {
|
||||
comp.set_frequency_hz(40.0).unwrap();
|
||||
assert!((comp.frequency_ratio() - 0.8).abs() < 1e-10);
|
||||
|
||||
let state_reduced = vec![0.96, 0.115, 400_000.0, 440_000.0, 44_000.0];
|
||||
let mut r_reduced = vec![0.0; 5];
|
||||
let state_reduced = vec![0.96, 0.115, 1.075, 44_000.0];
|
||||
let mut r_reduced = vec![0.0; 6];
|
||||
comp.compute_residuals(&state_reduced, &mut r_reduced)
|
||||
.unwrap();
|
||||
let m_error_reduced = r_reduced[0].abs();
|
||||
@@ -263,7 +276,7 @@ fn test_mchx_ua_correction_with_fan_speed() {
|
||||
|
||||
#[test]
|
||||
fn test_mchx_ua_ambient_temperature_effect() {
|
||||
let mut coil_35 = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
|
||||
let coil_35 = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
|
||||
let mut coil_45 = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
|
||||
|
||||
coil_45.set_air_temperature_celsius(45.0);
|
||||
@@ -503,9 +516,11 @@ fn test_screw_compressor_off_state_zero_flow() {
|
||||
.unwrap();
|
||||
|
||||
comp.set_state(OperationalState::Off).unwrap();
|
||||
// CM1.3: ṁ at indices 0,1,2; W_shaft at index 3
|
||||
comp.set_system_context(3, &[(0, 0, 0), (1, 0, 0), (2, 0, 0)]);
|
||||
|
||||
let state = vec![0.0; 5];
|
||||
let mut residuals = vec![0.0; 5];
|
||||
let state = vec![0.0; 4];
|
||||
let mut residuals = vec![0.0; 6];
|
||||
comp.compute_residuals(&state, &mut residuals).unwrap();
|
||||
|
||||
// In Off state: r[0]=ṁ_suc=0, r[1]=ṁ_eco=0, r[4]=W=0
|
||||
@@ -606,13 +621,17 @@ fn test_screw_energy_balance() {
|
||||
let w_fluid = w_shaft * eta_mech; // == delta_h
|
||||
println!(
|
||||
"Shaft power: {:.0} W = {:.1} kW, Fluid power: {:.0} W",
|
||||
w_shaft, w_shaft / 1000.0, w_fluid
|
||||
w_shaft,
|
||||
w_shaft / 1000.0,
|
||||
w_fluid
|
||||
);
|
||||
|
||||
// Verify: W_shaft closes the energy balance via residual[2]
|
||||
// State layout: [m_suc, m_eco, w_shaft] — enthalpies come from ports, not state
|
||||
let state = vec![m_suc, m_eco, w_shaft];
|
||||
let mut residuals = vec![0.0; 5];
|
||||
// CM1.3 state layout: [m_suc, m_eco, m_dis, w_shaft] — enthalpies come from ports
|
||||
let mut comp = comp;
|
||||
comp.set_system_context(3, &[(0, 0, 0), (1, 0, 0), (2, 0, 0)]);
|
||||
let state = vec![m_suc, m_eco, m_suc + m_eco, w_shaft];
|
||||
let mut residuals = vec![0.0; 6];
|
||||
comp.compute_residuals(&state, &mut residuals).unwrap();
|
||||
|
||||
// residual[2] = (ṁ_suc×h_suc + ṁ_eco×h_eco + W_shaft×η) - ṁ_total×h_dis
|
||||
|
||||
Reference in New Issue
Block a user