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:
244
crates/solver/tests/_tmp_analytic.rs
Normal file
244
crates/solver/tests/_tmp_analytic.rs
Normal file
@@ -0,0 +1,244 @@
|
||||
//! End-to-end integration test for the **emergent-pressure** refrigeration cycle.
|
||||
//!
|
||||
//! This test assembles the REAL thermodynamic components
|
||||
//! (`IsentropicCompressor`, `Condenser`, `IsenthalpicExpansionValve`,
|
||||
//! `Evaporator`) — not mocks — with a real CoolProp fluid backend and solves the
|
||||
//! canonical 4-component loop with the Newton solver.
|
||||
//!
|
||||
//! Unlike the fixed-design-point path (where the compressor pins
|
||||
//! `P_cond = P_sat(t_cond_k)` and the EXV pins `P_evap = P_sat(t_evap_k)`), every
|
||||
//! component here runs in **emergent-pressure mode**:
|
||||
//!
|
||||
//! | Component | emergent equations | pins |
|
||||
//! |-----------|--------------------|------|
|
||||
//! | Compressor | ṁ = ρ_suc·V_s·N·η_vol ; h_dis(P_suc,h_suc,P_dis) | ṁ, h_dis |
|
||||
//! | Condenser | P2=P1 ; ṁ(h1−h2)=ε·C·(T_cond(P1)−T_sec,in) ; h2=h_satliq(P1)−cp·ΔT_sc | **P_cond** |
|
||||
//! | EXV | h3=h2 (isenthalpic only) | h3 |
|
||||
//! | Evaporator | P4=P3 ; ṁ(h4−h3)=ε·C·(T_sec,in−T_evap(P3)) ; h4=h(P3,T_evap+SH) | **P_evap** |
|
||||
//!
|
||||
//! DoF (same-branch series loop): 2 + 3 + 1 + 3 = **9 equations / 9 unknowns**.
|
||||
//! The condensing/evaporating pressures are therefore EMERGENT: they are
|
||||
//! determined by the heat-exchanger ↔ secondary balance, not imposed by the
|
||||
//! compressor/EXV design points. The test verifies that varying the secondary
|
||||
//! (water) inlet temperature genuinely moves the emergent pressures and COP.
|
||||
//!
|
||||
//! Requires the `coolprop` feature (entropy + saturation properties), which the
|
||||
//! mock `TestBackend` does not provide:
|
||||
//! cargo test -p entropyk-solver --features coolprop --test emergent_pressure_cycle
|
||||
#![cfg(feature = "coolprop")]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use entropyk_components::isentropic_compressor::VolumetricEfficiency;
|
||||
use entropyk_components::{Condenser, Evaporator, IsenthalpicExpansionValve, IsentropicCompressor};
|
||||
use entropyk_fluids::{CoolPropBackend, FluidBackend};
|
||||
use entropyk_solver::solver::{NewtonConfig, Solver};
|
||||
use entropyk_solver::system::System;
|
||||
|
||||
/// State-vector layout (CM1.4 same-branch series loop, 9 unknowns):
|
||||
/// `[ṁ, P0,h0, P1,h1, P2,h2, P3,h3]` where
|
||||
/// E0 comp→cond, E1 cond→exv, E2 exv→evap, E3 evap→comp.
|
||||
const N_STATE: usize = 9;
|
||||
|
||||
/// Result of a converged emergent-pressure solve, in engineering units.
|
||||
struct CycleResult {
|
||||
m_dot: f64, // kg/s
|
||||
p_cond: f64, // Pa (emergent condensing pressure, edge E0)
|
||||
p_evap: f64, // Pa (emergent evaporating pressure, edge E3)
|
||||
w_comp: f64, // W (compression power)
|
||||
q_evap: f64, // W (cooling capacity)
|
||||
cop: f64, // - (Q_evap / W_comp)
|
||||
}
|
||||
|
||||
/// Assembles and solves the emergent-pressure cycle for the given secondary
|
||||
/// (water) inlet temperatures and returns the converged operating point.
|
||||
fn solve_emergent_cycle(cond_sec_temp_k: f64, evap_sec_temp_k: f64) -> CycleResult {
|
||||
let backend: Arc<dyn FluidBackend> = Arc::new(CoolPropBackend::new());
|
||||
let fluid = "R134a";
|
||||
|
||||
// ── Compressor: emergent ṁ via volumetric displacement ────────────────────
|
||||
// ṁ = ρ_suc · V_s · N · η_vol. V_s·N ≈ 3.25e-3 m³/s ⇒ ṁ ≈ 0.05 kg/s.
|
||||
let comp = Box::new(
|
||||
IsentropicCompressor::new(0.70, 318.15, 278.15, 5.0)
|
||||
.with_refrigerant(fluid)
|
||||
.with_fluid_backend(backend.clone())
|
||||
.with_displacement(6.5e-5, 50.0, VolumetricEfficiency::Constant(0.92)),
|
||||
);
|
||||
|
||||
// ── Condenser: emergent P_cond via subcooling outlet closure ──────────────
|
||||
let cond = Box::new(
|
||||
Condenser::new(766.0)
|
||||
.with_refrigerant(fluid)
|
||||
.with_fluid_backend(backend.clone())
|
||||
.with_secondary_stream(cond_sec_temp_k, 1500.0)
|
||||
.with_emergent_pressure(5.0),
|
||||
);
|
||||
|
||||
// ── EXV: emergent (isenthalpic only, drops the P_evap fix) ────────────────
|
||||
let exv = Box::new(
|
||||
IsenthalpicExpansionValve::new(278.15)
|
||||
.with_refrigerant(fluid)
|
||||
.with_fluid_backend(backend.clone())
|
||||
.with_emergent_pressure(),
|
||||
);
|
||||
|
||||
// ── Evaporator: emergent P_evap via superheat outlet closure ──────────────
|
||||
let evap = Box::new(
|
||||
Evaporator::new(1468.0)
|
||||
.with_refrigerant(fluid)
|
||||
.with_fluid_backend(backend.clone())
|
||||
.with_secondary_stream(evap_sec_temp_k, 2000.0)
|
||||
.with_emergent_pressure(),
|
||||
);
|
||||
|
||||
let mut system = System::new();
|
||||
let n_comp = system.add_component(comp);
|
||||
let n_cond = system.add_component(cond);
|
||||
let n_exv = system.add_component(exv);
|
||||
let n_evap = system.add_component(evap);
|
||||
|
||||
system.add_edge(n_comp, n_cond).unwrap(); // E0 comp→cond
|
||||
system.add_edge(n_cond, n_exv).unwrap(); // E1 cond→exv
|
||||
system.add_edge(n_exv, n_evap).unwrap(); // E2 exv→evap
|
||||
system.add_edge(n_evap, n_comp).unwrap(); // E3 evap→comp
|
||||
|
||||
system.finalize().unwrap();
|
||||
|
||||
// DoF must be exactly balanced (2+3+1+3 = 9 == 9 unknowns).
|
||||
assert_eq!(
|
||||
system.full_state_vector_len(),
|
||||
N_STATE,
|
||||
"emergent same-branch loop must be 1 ṁ + 4×2(P,h) = 9 unknowns"
|
||||
);
|
||||
|
||||
// Physically-consistent seed near the expected operating point.
|
||||
// P_sat(R134a): 5 °C ≈ 3.50 bar, 45 °C ≈ 11.6 bar.
|
||||
let initial_state = vec![
|
||||
0.05, // ṁ [kg/s]
|
||||
11.6e5, 445e3, // E0 comp→cond : P_cond, h_dis
|
||||
11.6e5, 262e3, // E1 cond→exv : P_cond, h_liq
|
||||
3.50e5, 262e3, // E2 exv→evap : P_evap, h (isenthalpic)
|
||||
3.50e5, 405e3, // E3 evap→comp : P_evap, h_suction (superheated)
|
||||
];
|
||||
|
||||
let mut config = NewtonConfig {
|
||||
max_iterations: 200,
|
||||
tolerance: 1e-6,
|
||||
line_search: true,
|
||||
use_numerical_jacobian: false,
|
||||
initial_state: Some(initial_state),
|
||||
..NewtonConfig::default()
|
||||
};
|
||||
|
||||
let converged = config
|
||||
.solve(&mut system)
|
||||
.expect("emergent-pressure cycle must converge");
|
||||
|
||||
let sv = &converged.state;
|
||||
let m_dot = sv[0];
|
||||
let (p_cond, h_dis) = (sv[1], sv[2]);
|
||||
let h_cond_out = sv[4];
|
||||
let (p_evap, h_suc) = (sv[7], sv[8]);
|
||||
let h_evap_in = sv[6];
|
||||
let h_evap_out = sv[8];
|
||||
|
||||
let w_comp = m_dot * (h_dis - h_suc);
|
||||
let q_evap = m_dot * (h_evap_out - h_evap_in);
|
||||
|
||||
// Sanity: subcooled liquid at condenser outlet, superheated vapour at suction.
|
||||
assert!(h_dis > h_suc, "discharge enthalpy must exceed suction");
|
||||
assert!(
|
||||
h_cond_out < h_suc,
|
||||
"condenser outlet must be subcooled liquid"
|
||||
);
|
||||
assert!(w_comp > 0.0, "compression power must be positive");
|
||||
assert!(q_evap > 0.0, "cooling capacity must be positive");
|
||||
|
||||
CycleResult {
|
||||
m_dot,
|
||||
p_cond,
|
||||
p_evap,
|
||||
w_comp,
|
||||
q_evap,
|
||||
cop: q_evap / w_comp,
|
||||
}
|
||||
}
|
||||
|
||||
/// The emergent-pressure loop must converge and produce a physical operating
|
||||
/// point (positive capacity, positive power, plausible pressures/COP).
|
||||
#[test]
|
||||
fn test_emergent_cycle_converges_to_physical_point() {
|
||||
let r = solve_emergent_cycle(303.15, 285.15); // cond water 30 °C, evap water 12 °C
|
||||
|
||||
// Emergent pressures land in a physically reasonable R134a window.
|
||||
assert!(
|
||||
(5.0e5..20.0e5).contains(&r.p_cond),
|
||||
"emergent P_cond out of range: {:.0} Pa",
|
||||
r.p_cond
|
||||
);
|
||||
assert!(
|
||||
(1.5e5..6.0e5).contains(&r.p_evap),
|
||||
"emergent P_evap out of range: {:.0} Pa",
|
||||
r.p_evap
|
||||
);
|
||||
assert!(
|
||||
r.p_cond > r.p_evap,
|
||||
"condensing must exceed evaporating pressure"
|
||||
);
|
||||
assert!(r.m_dot > 0.0, "mass flow must be positive: {}", r.m_dot);
|
||||
assert!(
|
||||
(1.5..12.0).contains(&r.cop),
|
||||
"COP out of physical range: {:.2}",
|
||||
r.cop
|
||||
);
|
||||
}
|
||||
|
||||
/// **Core emergence claim**: warming the condenser secondary (water) inlet must
|
||||
/// raise the emergent condensing pressure and reduce COP — the machine
|
||||
/// performance is genuinely qualified by the secondary conditions, not fixed by
|
||||
/// compressor design points.
|
||||
#[test]
|
||||
fn test_warmer_condenser_water_raises_pcond_and_lowers_cop() {
|
||||
let cool = solve_emergent_cycle(303.15, 285.15); // 30 °C condenser water
|
||||
let warm = solve_emergent_cycle(313.15, 285.15); // 40 °C condenser water
|
||||
|
||||
assert!(
|
||||
warm.p_cond > cool.p_cond + 1.0e4,
|
||||
"warmer condenser water must raise emergent P_cond: {:.0} → {:.0} Pa",
|
||||
cool.p_cond,
|
||||
warm.p_cond
|
||||
);
|
||||
assert!(
|
||||
warm.w_comp > cool.w_comp,
|
||||
"higher lift must increase compression power: {:.0} → {:.0} W",
|
||||
cool.w_comp,
|
||||
warm.w_comp
|
||||
);
|
||||
assert!(
|
||||
warm.cop < cool.cop,
|
||||
"warmer condenser water must lower COP: {:.2} → {:.2}",
|
||||
cool.cop,
|
||||
warm.cop
|
||||
);
|
||||
}
|
||||
|
||||
/// Warming the evaporator secondary (water/brine) inlet must raise the emergent
|
||||
/// evaporating pressure and increase cooling capacity.
|
||||
#[test]
|
||||
fn test_warmer_evaporator_water_raises_pevap_and_capacity() {
|
||||
let cold = solve_emergent_cycle(303.15, 283.15); // 10 °C evaporator water
|
||||
let warm = solve_emergent_cycle(303.15, 291.15); // 18 °C evaporator water
|
||||
|
||||
assert!(
|
||||
warm.p_evap > cold.p_evap + 1.0e4,
|
||||
"warmer evaporator water must raise emergent P_evap: {:.0} → {:.0} Pa",
|
||||
cold.p_evap,
|
||||
warm.p_evap
|
||||
);
|
||||
assert!(
|
||||
warm.q_evap > cold.q_evap,
|
||||
"warmer evaporator water must increase capacity: {:.0} → {:.0} W",
|
||||
cold.q_evap,
|
||||
warm.q_evap
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user