Wire BPHX channel pressure drop on both sides with selectable correlations.
Some checks failed
CI / check (push) Has been cancelled

Replace isobaric 4-port closures with SimplifiedChannel (default) and Martin1996 DP models so z_dp and UI dp_correlation actually affect the Newton solve.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-19 16:52:25 +02:00
parent 5bd180b5b8
commit 44f793a583
19 changed files with 1287 additions and 106 deletions

View File

@@ -163,6 +163,15 @@ impl HxSideConditions {
}
}
/// Live four-port edge indices: each triple is `(ṁ_idx, P_idx, h_idx)`.
#[derive(Debug, Clone, Copy)]
pub(crate) struct FourPortEdgeIndices {
pub hot_in: (usize, usize, usize),
pub hot_out: (usize, usize, usize),
pub cold_in: (usize, usize, usize),
pub cold_out: (usize, usize, usize),
}
/// Generic heat exchanger component with 4 ports.
///
/// Uses the Strategy Pattern for heat transfer calculations via the
@@ -516,7 +525,7 @@ impl<Model: HeatTransferModel + 'static> HeatExchanger<Model> {
}
/// `true` when all 4 edges are wired (Modelica-style 4-port mode).
fn edges_ready(&self) -> bool {
pub(crate) fn edges_ready(&self) -> bool {
self.hot_in_idx.is_some()
&& self.hot_out_idx.is_some()
&& self.cold_in_idx.is_some()
@@ -525,6 +534,60 @@ impl<Model: HeatTransferModel + 'static> HeatExchanger<Model> {
&& !self.cold_fluid_id_str.is_empty()
}
/// Live four-port edge index triples `(ṁ, P, h)` when wired.
pub(crate) fn four_port_edges(&self) -> Option<FourPortEdgeIndices> {
if !self.edges_ready() {
return None;
}
Some(FourPortEdgeIndices {
hot_in: self.hot_in_idx?,
hot_out: self.hot_out_idx?,
cold_in: self.cold_in_idx?,
cold_out: self.cold_out_idx?,
})
}
/// Thermal-model equation count (excludes the two pressure-closure rows).
pub(crate) fn model_n_equations(&self) -> usize {
self.model.n_equations()
}
/// Calibration state indices (e.g. free `z_dp` actuator).
pub(crate) fn calib_indices_ref(&self) -> &entropyk_core::CalibIndices {
&self.calib_indices
}
/// Hot-side fluid id used for live property queries.
pub(crate) fn hot_fluid_id_str(&self) -> &str {
&self.hot_fluid_id_str
}
/// Cold-side fluid id used for live property queries.
pub(crate) fn cold_fluid_id_str(&self) -> &str {
&self.cold_fluid_id_str
}
/// Inlet density [kg/m³] from the attached fluid backend at `(P, h)`.
pub(crate) fn side_density(
&self,
side: &str,
fluid_id: &str,
p_pa: f64,
h_jkg: f64,
) -> Result<f64, ComponentError> {
self.query_live_property(side, fluid_id, Property::Density, p_pa, h_jkg)
.and_then(|rho| {
if rho.is_finite() && rho > 1e-10 {
Ok(rho)
} else {
Err(ComponentError::DomainViolation(DomainViolation {
component: Some(self.name.clone()),
detail: format!("{} {}-side density is invalid: {}", self.name, side, rho),
}))
}
})
}
fn live_state_required_error(&self) -> ComponentError {
ComponentError::InvalidState(format!(
"{} requires live four-port edge state (hot_inlet, hot_outlet, cold_inlet, cold_outlet); inlet-only boundary conditions cannot define outlet states",