Some checks failed
CI / check (push) Has been cancelled
Capture uncommitted solver robustness work (regularization, domain errors, linear solver lifecycle, tube DP/MSH), web workbench updates, and synced BMAD skills across IDE agent folders before starting BPHX pressure-drop. Co-authored-by: Cursor <cursoragent@cursor.com>
108 lines
3.9 KiB
Rust
108 lines
3.9 KiB
Rust
//! Quick test: EXV OperationalState Off path.
|
||
//! Validates that the EXV with `Off` produces ṁ = 0 in its residual and keeps
|
||
//! the Jacobian non-singular. Run with:
|
||
//! cargo test --release -p entropyk-components --test test_exv_off -- --nocapture
|
||
use entropyk_components::isenthalpic_expansion_valve::IsenthalpicExpansionValve;
|
||
use entropyk_components::state_machine::{OperationalState, StateManageable};
|
||
use entropyk_components::{Component, JacobianBuilder};
|
||
|
||
fn make_exv() -> IsenthalpicExpansionValve {
|
||
let mut exv = IsenthalpicExpansionValve::new(278.15)
|
||
.with_refrigerant("R134a")
|
||
.with_emergent_pressure()
|
||
.with_orifice_fixed(2.0e-6, 1.0);
|
||
// Layout: [m_inlet=0, m_outlet=1, p_inlet=2, h_inlet=3, p_outlet=4, h_outlet=5]
|
||
exv.set_system_context(0, &[(0, 2, 3), (1, 4, 5)]);
|
||
exv
|
||
}
|
||
|
||
#[test]
|
||
fn exv_off_produces_zero_flow_residual() {
|
||
let mut exv = make_exv();
|
||
|
||
// Turn the valve OFF.
|
||
exv.set_operational_state_unchecked(OperationalState::Off);
|
||
assert!(exv.state().is_off());
|
||
|
||
// State where m_outlet = 0.05 (non-zero flow) — the Off residual must drive it to 0.
|
||
let state: Vec<f64> = vec![0.05, 0.05, 1.5e6, 250_000.0, 0.4e6, 250_000.0];
|
||
let mut r = vec![0.0_f64; exv.n_equations()];
|
||
exv.compute_residuals(&state, &mut r).expect("residuals");
|
||
|
||
// The orifice equation is the last one (after isenthalpic + mass conservation).
|
||
// r_orifice should equal m_outlet - 0 = 0.05 (non-zero → Newton will push it to 0).
|
||
let orifice_residual = *r.last().unwrap();
|
||
assert!(
|
||
(orifice_residual - 0.05).abs() < 1e-12,
|
||
"Off residual should be ṁ = 0.05 (forcing flow to 0), got {}",
|
||
orifice_residual
|
||
);
|
||
|
||
// Jacobian: ∂r_orifice/∂m_outlet = 1 (keeps Newton coupled on the mass flow).
|
||
let mut jb = JacobianBuilder::new();
|
||
exv.jacobian_entries(&state, &mut jb).expect("jacobian");
|
||
let entries = jb.entries();
|
||
let m_out_idx = 1;
|
||
let orifice_row = exv.n_equations() - 1;
|
||
let dm_out: f64 = entries
|
||
.iter()
|
||
.filter(|(row, col, _)| *row == orifice_row && *col == m_out_idx)
|
||
.map(|(_, _, v)| *v)
|
||
.sum();
|
||
assert!(
|
||
(dm_out - 1.0).abs() < 1e-12,
|
||
"Off Jacobian ∂r/∂m_outlet should be 1.0, got {}",
|
||
dm_out
|
||
);
|
||
|
||
println!(
|
||
"EXV Off: residual={:.4e} (target 0.05), ∂r/∂m_out={:.3} (target 1.0) — OK",
|
||
orifice_residual, dm_out
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn exv_on_still_uses_orifice_equation() {
|
||
let mut exv = make_exv();
|
||
// Default state is On.
|
||
assert!(exv.state().is_on());
|
||
|
||
// Use a backend so ρ_in can be evaluated.
|
||
let backend: std::sync::Arc<dyn entropyk_fluids::FluidBackend> =
|
||
std::sync::Arc::new(entropyk_fluids::TestBackend::new());
|
||
exv.set_fluid_backend_from_builder(backend);
|
||
|
||
// State with a physical ΔP across the valve: P_in = 13 bar, P_out = 3.5 bar.
|
||
let state: Vec<f64> = vec![0.05, 0.05, 1.3e6, 250_000.0, 0.35e6, 250_000.0];
|
||
let mut r = vec![0.0_f64; exv.n_equations()];
|
||
exv.compute_residuals(&state, &mut r).expect("residuals");
|
||
|
||
let orifice_residual = *r.last().unwrap();
|
||
// Orifice equation is active: residual is non-trivial (not just ṁ).
|
||
assert!(
|
||
orifice_residual.abs() > 1e-6,
|
||
"On residual should be non-trivial (orifice equation active), got {}",
|
||
orifice_residual
|
||
);
|
||
println!(
|
||
"EXV On: residual={:.4e} (orifice equation active) — OK",
|
||
orifice_residual
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn exv_state_transitions_are_validated() {
|
||
let mut exv = IsenthalpicExpansionValve::new(278.15);
|
||
assert!(exv.state().is_on());
|
||
|
||
// On → Off is a legal transition.
|
||
assert!(exv.can_transition_to(OperationalState::Off));
|
||
exv.set_state(OperationalState::Off).expect("On → Off");
|
||
assert!(exv.state().is_off());
|
||
|
||
// Off → On is legal.
|
||
assert!(exv.can_transition_to(OperationalState::On));
|
||
exv.set_state(OperationalState::On).expect("Off → On");
|
||
assert!(exv.state().is_on());
|
||
}
|