Files
Entropyk/crates/components/tests/test_exv_off.rs
sepehr 5bd180b5b8
Some checks failed
CI / check (push) Has been cancelled
Snapshot WIP: solver HP epic progress, BPHX/HX physics, BMAD skill refresh.
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>
2026-07-19 16:35:31 +02:00

108 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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());
}