feat: implement mass balance validation for Story 7.1

- Added port_mass_flows to Component trait and implements for core components.
- Added System::check_mass_balance and integrated it into the solver.
- Restored connect methods for ExpansionValve, Compressor, and Pipe to fix integration tests.
- Updated Python and C bindings for validation errors.
- Updated sprint status and story documentation.
This commit is contained in:
Sepehr
2026-02-21 23:21:34 +01:00
parent 4440132b0a
commit fa480ed303
55 changed files with 5987 additions and 31 deletions

View File

@@ -652,6 +652,39 @@ impl Compressor<Disconnected> {
pub fn set_operational_state(&mut self, state: OperationalState) {
self.operational_state = state;
}
/// Connects the compressor to suction and discharge ports.
///
/// This consumes the disconnected compressor and returns a connected one,
/// transitioning the state at compile time.
pub fn connect(
self,
suction: Port<Disconnected>,
discharge: Port<Disconnected>,
) -> Result<Compressor<Connected>, ComponentError> {
let (p_suction, _) = self
.port_suction
.connect(suction)
.map_err(|e| ComponentError::InvalidState(e.to_string()))?;
let (p_discharge, _) = self
.port_discharge
.connect(discharge)
.map_err(|e| ComponentError::InvalidState(e.to_string()))?;
Ok(Compressor {
model: self.model,
port_suction: p_suction,
port_discharge: p_discharge,
speed_rpm: self.speed_rpm,
displacement_m3_per_rev: self.displacement_m3_per_rev,
mechanical_efficiency: self.mechanical_efficiency,
calib: self.calib,
calib_indices: self.calib_indices,
fluid_id: self.fluid_id,
circuit_id: self.circuit_id,
operational_state: self.operational_state,
_state: PhantomData,
})
}
}
impl Compressor<Connected> {
@@ -1217,6 +1250,22 @@ impl Component for Compressor<Connected> {
2 // Mass flow residual and energy residual
}
fn port_mass_flows(&self, state: &SystemState) -> Result<Vec<entropyk_core::MassFlow>, ComponentError> {
if state.len() < 4 {
return Err(ComponentError::InvalidStateDimensions {
expected: 4,
actual: state.len(),
});
}
let m = entropyk_core::MassFlow::from_kg_per_s(state[0]);
// Suction (inlet), Discharge (outlet), Oil (no flow modeled yet)
Ok(vec![
m,
entropyk_core::MassFlow::from_kg_per_s(-m.to_kg_per_s()),
entropyk_core::MassFlow::from_kg_per_s(0.0)
])
}
fn get_ports(&self) -> &[ConnectedPort] {
// NOTE: This returns an empty slice due to lifetime constraints.
// Use `get_ports_slice()` method on Compressor<Connected> for actual port access.