chore: sync project state and current artifacts
This commit is contained in:
@@ -47,7 +47,7 @@
|
||||
use crate::port::{Connected, Disconnected, FluidId, Port};
|
||||
use crate::{
|
||||
CircuitId, Component, ComponentError, ConnectedPort, JacobianBuilder, OperationalState,
|
||||
ResidualVector, SystemState,
|
||||
ResidualVector, StateSlice,
|
||||
};
|
||||
use entropyk_core::Calib;
|
||||
use std::marker::PhantomData;
|
||||
@@ -284,25 +284,35 @@ impl ExpansionValve<Connected> {
|
||||
}
|
||||
|
||||
/// Computes the full thermodynamic state at the inlet port.
|
||||
pub fn inlet_state(&self, backend: &impl entropyk_fluids::FluidBackend) -> Result<entropyk_fluids::ThermoState, ComponentError> {
|
||||
pub fn inlet_state(
|
||||
&self,
|
||||
backend: &impl entropyk_fluids::FluidBackend,
|
||||
) -> Result<entropyk_fluids::ThermoState, ComponentError> {
|
||||
backend
|
||||
.full_state(
|
||||
entropyk_fluids::FluidId::new(self.port_inlet.fluid_id().as_str()),
|
||||
self.port_inlet.pressure(),
|
||||
self.port_inlet.enthalpy(),
|
||||
)
|
||||
.map_err(|e| ComponentError::CalculationFailed(format!("Failed to compute inlet state: {}", e)))
|
||||
.map_err(|e| {
|
||||
ComponentError::CalculationFailed(format!("Failed to compute inlet state: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the full thermodynamic state at the outlet port.
|
||||
pub fn outlet_state(&self, backend: &impl entropyk_fluids::FluidBackend) -> Result<entropyk_fluids::ThermoState, ComponentError> {
|
||||
pub fn outlet_state(
|
||||
&self,
|
||||
backend: &impl entropyk_fluids::FluidBackend,
|
||||
) -> Result<entropyk_fluids::ThermoState, ComponentError> {
|
||||
backend
|
||||
.full_state(
|
||||
entropyk_fluids::FluidId::new(self.port_outlet.fluid_id().as_str()),
|
||||
self.port_outlet.pressure(),
|
||||
self.port_outlet.enthalpy(),
|
||||
)
|
||||
.map_err(|e| ComponentError::CalculationFailed(format!("Failed to compute outlet state: {}", e)))
|
||||
.map_err(|e| {
|
||||
ComponentError::CalculationFailed(format!("Failed to compute outlet state: {}", e))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the optional opening parameter (0.0 to 1.0).
|
||||
@@ -534,7 +544,7 @@ impl ExpansionValve<Connected> {
|
||||
impl Component for ExpansionValve<Connected> {
|
||||
fn compute_residuals(
|
||||
&self,
|
||||
state: &SystemState,
|
||||
state: &StateSlice,
|
||||
residuals: &mut ResidualVector,
|
||||
) -> Result<(), ComponentError> {
|
||||
if residuals.len() != self.n_equations() {
|
||||
@@ -585,7 +595,11 @@ impl Component for ExpansionValve<Connected> {
|
||||
// Mass flow: ṁ_out = f_m × ṁ_in (calibration factor on inlet flow)
|
||||
let mass_flow_in = state[0];
|
||||
let mass_flow_out = state[1];
|
||||
let f_m = self.calib_indices.f_m.map(|idx| state[idx]).unwrap_or(self.calib.f_m);
|
||||
let f_m = self
|
||||
.calib_indices
|
||||
.f_m
|
||||
.map(|idx| state[idx])
|
||||
.unwrap_or(self.calib.f_m);
|
||||
residuals[1] = mass_flow_out - f_m * mass_flow_in;
|
||||
|
||||
Ok(())
|
||||
@@ -593,7 +607,7 @@ impl Component for ExpansionValve<Connected> {
|
||||
|
||||
fn jacobian_entries(
|
||||
&self,
|
||||
_state: &SystemState,
|
||||
_state: &StateSlice,
|
||||
jacobian: &mut JacobianBuilder,
|
||||
) -> Result<(), ComponentError> {
|
||||
if self.is_effectively_off() {
|
||||
@@ -613,7 +627,11 @@ impl Component for ExpansionValve<Connected> {
|
||||
OperationalState::On | OperationalState::Off => {}
|
||||
}
|
||||
|
||||
let f_m = self.calib_indices.f_m.map(|idx| _state[idx]).unwrap_or(self.calib.f_m);
|
||||
let f_m = self
|
||||
.calib_indices
|
||||
.f_m
|
||||
.map(|idx| _state[idx])
|
||||
.unwrap_or(self.calib.f_m);
|
||||
jacobian.add_entry(0, 0, 0.0);
|
||||
jacobian.add_entry(0, 1, 0.0);
|
||||
jacobian.add_entry(1, 0, -f_m);
|
||||
@@ -633,7 +651,10 @@ impl Component for ExpansionValve<Connected> {
|
||||
2
|
||||
}
|
||||
|
||||
fn port_mass_flows(&self, state: &SystemState) -> Result<Vec<entropyk_core::MassFlow>, ComponentError> {
|
||||
fn port_mass_flows(
|
||||
&self,
|
||||
state: &StateSlice,
|
||||
) -> Result<Vec<entropyk_core::MassFlow>, ComponentError> {
|
||||
if state.len() < MIN_STATE_DIMENSIONS {
|
||||
return Err(ComponentError::InvalidStateDimensions {
|
||||
expected: MIN_STATE_DIMENSIONS,
|
||||
@@ -645,6 +666,45 @@ impl Component for ExpansionValve<Connected> {
|
||||
Ok(vec![m_in, m_out])
|
||||
}
|
||||
|
||||
/// Returns the enthalpies at the inlet and outlet ports.
|
||||
///
|
||||
/// For an expansion valve (isenthalpic device), the inlet and outlet
|
||||
/// enthalpies should be equal: h_in ≈ h_out.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A vector containing `[h_inlet, h_outlet]` in order.
|
||||
fn port_enthalpies(
|
||||
&self,
|
||||
_state: &StateSlice,
|
||||
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
|
||||
Ok(vec![
|
||||
self.port_inlet.enthalpy(),
|
||||
self.port_outlet.enthalpy(),
|
||||
])
|
||||
}
|
||||
|
||||
/// Returns the energy transfers for the expansion valve.
|
||||
///
|
||||
/// An expansion valve is an isenthalpic throttling device:
|
||||
/// - **Heat (Q)**: 0 W (adiabatic - no heat exchange with environment)
|
||||
/// - **Work (W)**: 0 W (no moving parts - no mechanical work)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Some((Q=0, W=0))` always, since expansion valves are passive devices.
|
||||
fn energy_transfers(
|
||||
&self,
|
||||
_state: &StateSlice,
|
||||
) -> Option<(entropyk_core::Power, entropyk_core::Power)> {
|
||||
match self.operational_state {
|
||||
OperationalState::Off | OperationalState::Bypass | OperationalState::On => Some((
|
||||
entropyk_core::Power::from_watts(0.0),
|
||||
entropyk_core::Power::from_watts(0.0),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ports(&self) -> &[ConnectedPort] {
|
||||
&[]
|
||||
}
|
||||
@@ -1019,8 +1079,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_circuit_id() {
|
||||
let mut valve = create_disconnected_valve();
|
||||
valve.set_circuit_id(CircuitId::new("primary"));
|
||||
assert_eq!(valve.circuit_id().as_str(), "primary");
|
||||
valve.set_circuit_id(CircuitId::from_number(5));
|
||||
assert_eq!(valve.circuit_id().as_number(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1237,14 +1297,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_state_manageable_circuit_id() {
|
||||
let valve = create_test_valve();
|
||||
assert_eq!(valve.circuit_id().as_str(), "default");
|
||||
assert_eq!(*valve.circuit_id(), CircuitId::ZERO);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_manageable_set_circuit_id() {
|
||||
let mut valve = create_test_valve();
|
||||
valve.set_circuit_id(CircuitId::new("secondary"));
|
||||
assert_eq!(valve.circuit_id().as_str(), "secondary");
|
||||
valve.set_circuit_id(CircuitId::from_number(2));
|
||||
assert_eq!(valve.circuit_id().as_number(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1503,4 +1563,141 @@ mod tests {
|
||||
assert!(PhaseRegion::TwoPhase.is_two_phase() == true);
|
||||
assert!(PhaseRegion::Superheated.is_two_phase() == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_energy_transfers_zero() {
|
||||
let valve = create_test_valve();
|
||||
let state = vec![0.05, 0.05];
|
||||
|
||||
let (heat, work) = valve.energy_transfers(&state).unwrap();
|
||||
|
||||
assert_relative_eq!(heat.to_watts(), 0.0, epsilon = 1e-10);
|
||||
assert_relative_eq!(work.to_watts(), 0.0, epsilon = 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_energy_transfers_off_mode() {
|
||||
let mut valve = create_test_valve();
|
||||
valve.set_operational_state(OperationalState::Off);
|
||||
let state = vec![0.05, 0.05];
|
||||
|
||||
let (heat, work) = valve.energy_transfers(&state).unwrap();
|
||||
|
||||
assert_relative_eq!(heat.to_watts(), 0.0, epsilon = 1e-10);
|
||||
assert_relative_eq!(work.to_watts(), 0.0, epsilon = 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_energy_transfers_bypass_mode() {
|
||||
let inlet = Port::new(
|
||||
FluidId::new("R134a"),
|
||||
Pressure::from_bar(10.0),
|
||||
Enthalpy::from_joules_per_kg(250000.0),
|
||||
);
|
||||
let outlet = Port::new(
|
||||
FluidId::new("R134a"),
|
||||
Pressure::from_bar(10.0),
|
||||
Enthalpy::from_joules_per_kg(250000.0),
|
||||
);
|
||||
let (inlet_conn, outlet_conn) = inlet.connect(outlet).unwrap();
|
||||
|
||||
let valve = ExpansionValve {
|
||||
calib_indices: entropyk_core::CalibIndices::default(),
|
||||
port_inlet: inlet_conn,
|
||||
port_outlet: outlet_conn,
|
||||
calib: Calib::default(),
|
||||
operational_state: OperationalState::Bypass,
|
||||
opening: Some(1.0),
|
||||
fluid_id: FluidId::new("R134a"),
|
||||
circuit_id: CircuitId::default(),
|
||||
_state: PhantomData,
|
||||
};
|
||||
|
||||
let state = vec![0.05, 0.05];
|
||||
let (heat, work) = valve.energy_transfers(&state).unwrap();
|
||||
|
||||
assert_relative_eq!(heat.to_watts(), 0.0, epsilon = 1e-10);
|
||||
assert_relative_eq!(work.to_watts(), 0.0, epsilon = 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_port_enthalpies_returns_two_values() {
|
||||
let valve = create_test_valve();
|
||||
let state = vec![0.05, 0.05];
|
||||
|
||||
let enthalpies = valve.port_enthalpies(&state).unwrap();
|
||||
|
||||
assert_eq!(enthalpies.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_port_enthalpies_isenthalpic() {
|
||||
let valve = create_test_valve();
|
||||
let state = vec![0.05, 0.05];
|
||||
|
||||
let enthalpies = valve.port_enthalpies(&state).unwrap();
|
||||
|
||||
assert_relative_eq!(
|
||||
enthalpies[0].to_joules_per_kg(),
|
||||
enthalpies[1].to_joules_per_kg(),
|
||||
epsilon = 1e-10
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_port_enthalpies_inlet_value() {
|
||||
let inlet = Port::new(
|
||||
FluidId::new("R134a"),
|
||||
Pressure::from_bar(10.0),
|
||||
Enthalpy::from_joules_per_kg(300000.0),
|
||||
);
|
||||
let outlet = Port::new(
|
||||
FluidId::new("R134a"),
|
||||
Pressure::from_bar(10.0),
|
||||
Enthalpy::from_joules_per_kg(300000.0),
|
||||
);
|
||||
let (inlet_conn, mut outlet_conn) = inlet.connect(outlet).unwrap();
|
||||
outlet_conn.set_pressure(Pressure::from_bar(3.5));
|
||||
|
||||
let valve = ExpansionValve {
|
||||
calib_indices: entropyk_core::CalibIndices::default(),
|
||||
port_inlet: inlet_conn,
|
||||
port_outlet: outlet_conn,
|
||||
calib: Calib::default(),
|
||||
operational_state: OperationalState::On,
|
||||
opening: Some(1.0),
|
||||
fluid_id: FluidId::new("R134a"),
|
||||
circuit_id: CircuitId::default(),
|
||||
_state: PhantomData,
|
||||
};
|
||||
|
||||
let state = vec![0.05, 0.05];
|
||||
let enthalpies = valve.port_enthalpies(&state).unwrap();
|
||||
|
||||
assert_relative_eq!(enthalpies[0].to_joules_per_kg(), 300000.0, epsilon = 1e-10);
|
||||
assert_relative_eq!(enthalpies[1].to_joules_per_kg(), 300000.0, epsilon = 1e-10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expansion_valve_energy_balance() {
|
||||
let valve = create_test_valve();
|
||||
let state = vec![0.05, 0.05];
|
||||
|
||||
let energy = valve.energy_transfers(&state);
|
||||
let mass_flows = valve.port_mass_flows(&state);
|
||||
let enthalpies = valve.port_enthalpies(&state);
|
||||
|
||||
assert!(energy.is_some());
|
||||
assert!(mass_flows.is_ok());
|
||||
assert!(enthalpies.is_ok());
|
||||
|
||||
let (heat, work) = energy.unwrap();
|
||||
let m_flows = mass_flows.unwrap();
|
||||
let h_flows = enthalpies.unwrap();
|
||||
|
||||
assert_eq!(m_flows.len(), h_flows.len());
|
||||
|
||||
assert_relative_eq!(heat.to_watts(), 0.0, epsilon = 1e-10);
|
||||
assert_relative_eq!(work.to_watts(), 0.0, epsilon = 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user