Add model embeddings for Z-factor DoF, separate from SaturatedController.
Some checks failed
CI / check (push) Has been cancelled
Some checks failed
CI / check (push) Has been cancelled
Fixed/Free Probe calibration now emits embeddings[] (unknown + equation) instead of controls[], keeping SaturatedController for physical regulation only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1083,6 +1083,23 @@ impl Compressor<Connected> {
|
||||
pub fn set_operational_state(&mut self, state: OperationalState) {
|
||||
self.operational_state = state;
|
||||
}
|
||||
|
||||
/// Live energy-retention factor \(f_w\): fraction of shaft work kept in the
|
||||
/// refrigerant (`1` = adiabatic, `0` = all work lost to ambient).
|
||||
///
|
||||
/// Reads `state[calib_indices.f_w]` when free; otherwise stored calib.
|
||||
/// Clamped to [0, 1].
|
||||
fn live_f_w(&self, state: Option<&StateSlice>) -> f64 {
|
||||
let raw = if let Some(st) = state {
|
||||
self.calib_indices
|
||||
.f_w
|
||||
.map(|idx| st.get(idx).copied().unwrap_or(self.calib.f_w))
|
||||
.unwrap_or(self.calib.f_w)
|
||||
} else {
|
||||
self.calib.f_w
|
||||
};
|
||||
raw.clamp(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Compressor<Connected> {
|
||||
@@ -1194,9 +1211,13 @@ impl Component for Compressor<Connected> {
|
||||
// ṁ_calc - ṁ_state = 0
|
||||
residuals[0] = mass_flow_calc - mass_flow_state;
|
||||
|
||||
// Residual 1: Energy balance
|
||||
// Power_calc - ṁ × (h_discharge - h_suction) / η_mech = 0
|
||||
// Residual 1: Energy balance with retention factor f_w
|
||||
// (fraction of shaft work kept in the refrigerant):
|
||||
// ṁ·Δh = Ẇ·f_w / η_mech ⇒ Ẇ·f_w − ṁ·Δh/η_mech = 0
|
||||
// so h_dis ≈ h_suc + f_w·Ẇ/(ṁ·η_mech).
|
||||
// f_w = 1 → adiabatic (default); f_w = 0 → all work lost to ambient.
|
||||
let enthalpy_change = h_discharge - h_suction;
|
||||
let f_w = self.live_f_w(Some(state));
|
||||
|
||||
// Prevent division by zero
|
||||
if self.mechanical_efficiency.abs() < 1e-10 {
|
||||
@@ -1205,7 +1226,8 @@ impl Component for Compressor<Connected> {
|
||||
));
|
||||
}
|
||||
|
||||
residuals[1] = power_calc - mass_flow_state * enthalpy_change / self.mechanical_efficiency;
|
||||
residuals[1] =
|
||||
power_calc * f_w - mass_flow_state * enthalpy_change / self.mechanical_efficiency;
|
||||
|
||||
// r2: ṁ_discharge − ṁ_suction = 0 (mass conservation, CM1.3)
|
||||
// CM1.4: skip when same_branch_m — ṁ_dis == ṁ_suc (same state index),
|
||||
@@ -1265,7 +1287,8 @@ impl Component for Compressor<Connected> {
|
||||
)?;
|
||||
jacobian.add_entry(0, suc_h_idx, dr0_dh_suction);
|
||||
|
||||
// Row 1: Energy residual r1 = power_calc − ṁ × Δh / η_mech
|
||||
// Row 1: Energy residual r1 = power·f_w − ṁ·Δh/η_mech
|
||||
let f_w = self.live_f_w(Some(state));
|
||||
// ∂r1/∂ṁ_suction = −(h_discharge − h_suction) / η_mech
|
||||
let dr1_dm = -(h_discharge - h_suction) / self.mechanical_efficiency;
|
||||
jacobian.add_entry(1, suc_m_idx, dr1_dm);
|
||||
@@ -1280,7 +1303,7 @@ impl Component for Compressor<Connected> {
|
||||
Temperature::from_kelvin(t),
|
||||
Temperature::from_kelvin(t_discharge),
|
||||
None,
|
||||
))
|
||||
) * f_w)
|
||||
},
|
||||
h_suction,
|
||||
1.0,
|
||||
@@ -1296,7 +1319,7 @@ impl Component for Compressor<Connected> {
|
||||
Temperature::from_kelvin(t_suction),
|
||||
Temperature::from_kelvin(t),
|
||||
None,
|
||||
))
|
||||
) * f_w)
|
||||
},
|
||||
h_discharge,
|
||||
1.0,
|
||||
@@ -1326,7 +1349,18 @@ impl Component for Compressor<Connected> {
|
||||
Temperature::from_kelvin(t_discharge_k),
|
||||
None,
|
||||
);
|
||||
jacobian.add_entry(1, z_power_idx, p_nominal);
|
||||
// r1 = (z_power·Ẇ_nom)·f_w − … ⇒ ∂r1/∂z_power = Ẇ_nom·f_w
|
||||
jacobian.add_entry(1, z_power_idx, p_nominal * f_w);
|
||||
}
|
||||
|
||||
if let Some(f_w_idx) = self.calib_indices.f_w {
|
||||
let p_live = self.power_consumption_cooling(
|
||||
Temperature::from_kelvin(t_suction_k),
|
||||
Temperature::from_kelvin(t_discharge_k),
|
||||
Some(state),
|
||||
);
|
||||
// r1 = Ẇ·f_w − … ⇒ ∂r1/∂f_w = +Ẇ
|
||||
jacobian.add_entry(1, f_w_idx, p_live);
|
||||
}
|
||||
|
||||
// ∂r0/∂f_etav (AHRI 540 only): ṁ_calc = f_m · f_etav · base with
|
||||
|
||||
@@ -797,17 +797,30 @@ impl<Model: HeatTransferModel + 'static> HeatExchanger<Model> {
|
||||
h_jkg: f64,
|
||||
) -> Result<f64, ComponentError> {
|
||||
if !p_pa.is_finite() || p_pa <= 0.0 {
|
||||
return Err(ComponentError::InvalidState(format!(
|
||||
"{} {} side has invalid pressure: {} Pa",
|
||||
self.name, side, p_pa
|
||||
)));
|
||||
return Err(ComponentError::DomainViolation(DomainViolation {
|
||||
component: Some(self.name.clone()),
|
||||
detail: format!(
|
||||
"{} {} side has invalid pressure: {} Pa",
|
||||
self.name, side, p_pa
|
||||
),
|
||||
}));
|
||||
}
|
||||
if !h_jkg.is_finite() {
|
||||
return Err(ComponentError::InvalidState(format!(
|
||||
"{} {} side has invalid enthalpy: {} J/kg",
|
||||
self.name, side, h_jkg
|
||||
)));
|
||||
return Err(ComponentError::DomainViolation(DomainViolation {
|
||||
component: Some(self.name.clone()),
|
||||
detail: format!(
|
||||
"{} {} side has invalid enthalpy: {} J/kg",
|
||||
self.name, side, h_jkg
|
||||
),
|
||||
}));
|
||||
}
|
||||
// Clamp wild Newton trial enthalpies into a broad physical envelope so
|
||||
// CoolProp is not queried with absurd (P,h) that yield T=inf. Exact
|
||||
// result is preserved for states already inside the clamp.
|
||||
const H_MIN_JKG: f64 = -5.0e5;
|
||||
const H_MAX_JKG: f64 = 3.0e6;
|
||||
let h_query = h_jkg.clamp(H_MIN_JKG, H_MAX_JKG);
|
||||
let p_query = p_pa.clamp(1.0e3, 5.0e7);
|
||||
let backend = self.fluid_backend.as_ref().ok_or_else(|| {
|
||||
ComponentError::InvalidState(format!(
|
||||
"{} {} side fluid '{}' requires a FluidBackend; no simulation fallback is allowed",
|
||||
@@ -819,15 +832,19 @@ impl<Model: HeatTransferModel + 'static> HeatExchanger<Model> {
|
||||
FluidsFluidId::new(fluid_id),
|
||||
property,
|
||||
entropyk_fluids::FluidState::PressureEnthalpy(
|
||||
Pressure::from_pascals(p_pa),
|
||||
entropyk_core::Enthalpy::from_joules_per_kg(h_jkg),
|
||||
Pressure::from_pascals(p_query),
|
||||
entropyk_core::Enthalpy::from_joules_per_kg(h_query),
|
||||
),
|
||||
)
|
||||
.map_err(|e| {
|
||||
ComponentError::CalculationFailed(format!(
|
||||
"{} failed to evaluate {:?} for {} side fluid '{}': {}",
|
||||
self.name, property, side, fluid_id, e
|
||||
))
|
||||
// Off-envelope CoolProp failures during Newton are recoverable.
|
||||
ComponentError::DomainViolation(DomainViolation {
|
||||
component: Some(self.name.clone()),
|
||||
detail: format!(
|
||||
"{} failed to evaluate {:?} for {} side fluid '{}': {}",
|
||||
self.name, property, side, fluid_id, e
|
||||
),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -641,10 +641,22 @@ impl FloodedEvaporator {
|
||||
.map_err(|e| ComponentError::CalculationFailed(e.to_string()))
|
||||
}
|
||||
|
||||
/// Live UA = UA_nominal × z_ua (reads `state[calib_indices.z_ua]` when free).
|
||||
fn live_ua(&self, state: Option<&StateSlice>) -> f64 {
|
||||
let z = state
|
||||
.and_then(|st| {
|
||||
self.inner
|
||||
.calib_indices_ref()
|
||||
.z_ua
|
||||
.and_then(|idx| st.get(idx).copied())
|
||||
})
|
||||
.unwrap_or_else(|| self.calib().z_ua);
|
||||
self.inner.ua_nominal() * z.max(0.0)
|
||||
}
|
||||
|
||||
/// Effectiveness for a phase-changing refrigerant: `C_min = C_sec`, `C_r → 0`,
|
||||
/// so `ε = 1 − exp(−UA / C_sec)`.
|
||||
fn effectiveness(&self, c_sec: f64) -> f64 {
|
||||
let ua = self.ua();
|
||||
fn effectiveness(&self, c_sec: f64, ua: f64) -> f64 {
|
||||
if c_sec <= 1e-10 || ua <= 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
@@ -659,9 +671,10 @@ impl FloodedEvaporator {
|
||||
p_in_pa: f64,
|
||||
t_sec_in: f64,
|
||||
c_sec: f64,
|
||||
ua: f64,
|
||||
) -> Result<f64, ComponentError> {
|
||||
let t_evap = self.evap_temperature(p_in_pa)?;
|
||||
let eps = self.effectiveness(c_sec);
|
||||
let eps = self.effectiveness(c_sec, ua);
|
||||
Ok(eps * c_sec * (t_sec_in - t_evap))
|
||||
}
|
||||
|
||||
@@ -677,7 +690,7 @@ impl FloodedEvaporator {
|
||||
"coupled_duty requires rating-mode secondary inlet temperature".into(),
|
||||
)
|
||||
})?;
|
||||
self.coupled_duty_with(p_in_pa, t_sec_in, c_sec)
|
||||
self.coupled_duty_with(p_in_pa, t_sec_in, c_sec, self.live_ua(None))
|
||||
}
|
||||
|
||||
/// Rates the evaporator at a fixed refrigerant regime (constant evaporating
|
||||
@@ -717,7 +730,8 @@ impl FloodedEvaporator {
|
||||
));
|
||||
}
|
||||
let t_evap = self.evap_temperature(p_in_pa)?;
|
||||
let eps = self.effectiveness(c_sec);
|
||||
let ua = self.live_ua(None);
|
||||
let eps = self.effectiveness(c_sec, ua);
|
||||
let q = self.coupled_duty(p_in_pa)?;
|
||||
let secondary_outlet_k = if c_sec > 1e-10 {
|
||||
t_sec_in - q / c_sec
|
||||
@@ -1089,7 +1103,8 @@ impl Component for FloodedEvaporator {
|
||||
let h_in = state[inlet_h_idx];
|
||||
let h_out = state[outlet_h_idx];
|
||||
let (t_sec_in, c_sec) = self.resolve_secondary_stream(state)?;
|
||||
let q = self.coupled_duty_with(p_in, t_sec_in, c_sec)?;
|
||||
let ua = self.live_ua(Some(state));
|
||||
let q = self.coupled_duty_with(p_in, t_sec_in, c_sec, ua)?;
|
||||
|
||||
// System mode: C∞ zero-flow gating on both streams (staging / Newton trials).
|
||||
// Rating mode: secondary is a fixed boundary (C_sec > 0). Do NOT multiply Q by
|
||||
@@ -1188,10 +1203,10 @@ impl Component for FloodedEvaporator {
|
||||
let h_in = state[inlet_h_idx];
|
||||
let h_out = state[outlet_h_idx];
|
||||
let (t_sec_in, c_sec) = self.resolve_secondary_stream(state)?;
|
||||
let eps = self.effectiveness(c_sec);
|
||||
let q = self.coupled_duty_with(p_in, t_sec_in, c_sec)?;
|
||||
let ua = self.live_ua(Some(state));
|
||||
let eps = self.effectiveness(c_sec, ua);
|
||||
let q = self.coupled_duty_with(p_in, t_sec_in, c_sec, ua)?;
|
||||
let t_evap = self.evap_temperature(p_in)?;
|
||||
let ua = self.ua();
|
||||
|
||||
// System mode exposes secondary edge unknowns; rating mode freezes (T,C).
|
||||
let system = self.system_secondary_ready();
|
||||
@@ -1262,6 +1277,22 @@ impl Component for FloodedEvaporator {
|
||||
jacobian.add_entry(row, m_s, -d_qeff_dm_sec);
|
||||
jacobian.add_entry(row, h_s, -d_qeff_dh_sec);
|
||||
}
|
||||
// Live z_ua column: UA = UA_nom·z_ua, ε = 1−e^(−UA/C), Q = ε·C·ΔT
|
||||
if let Some(z_ua_idx) = self.inner.calib_indices_ref().z_ua {
|
||||
let d_eps_dua = if c_sec > 1e-12 {
|
||||
(-ua / c_sec).exp() / c_sec
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let d_q_dua = d_eps_dua * c_sec * (t_sec_in - t_evap);
|
||||
let alpha_r = if system {
|
||||
flow_activity(m_ref, DEFAULT_M_EPS_KG_S)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let d_qeff_dz = alpha_r * alpha_s * d_q_dua * self.inner.ua_nominal();
|
||||
jacobian.add_entry(row, z_ua_idx, -d_qeff_dz);
|
||||
}
|
||||
row += 1;
|
||||
|
||||
// r2 outlet closure
|
||||
|
||||
@@ -210,7 +210,11 @@ pub struct IsentropicCompressor {
|
||||
/// Inverse-control calibration state indices. When `f_m` is `Some(i)`, the
|
||||
/// volumetric mass-flow closure is scaled by the control variable at
|
||||
/// `state[i]`, turning the compressor into a capacity/mass-flow actuator.
|
||||
/// When `f_w` is `Some(i)`, discharge enthalpy uses the live energy-
|
||||
/// retention factor at `state[i]` (`1` = adiabatic, `0` = all lost).
|
||||
calib_indices: CalibIndices,
|
||||
/// Nominal energy-retention factor when not a free unknown. Default 1.
|
||||
f_w: f64,
|
||||
/// When `true`, a screw-compressor slide valve modulates the effective swept
|
||||
/// volume to hold a target suction saturated temperature (SST). The slide
|
||||
/// position `σ ∈ [σ_min, 1]` is a free actuator that scales the displacement
|
||||
@@ -279,12 +283,19 @@ impl IsentropicCompressor {
|
||||
circuit_id: CircuitId::default(),
|
||||
operational_state: OperationalState::default(),
|
||||
calib_indices: CalibIndices::default(),
|
||||
f_w: 1.0,
|
||||
slide_valve: false,
|
||||
sst_target_k: None,
|
||||
liquid_injection: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the nominal energy-retention factor `f_w` (`1` = adiabatic).
|
||||
pub fn with_f_w(mut self, f_w: f64) -> Self {
|
||||
self.f_w = f_w.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Attaches a refrigerant identifier for property lookups.
|
||||
pub fn with_refrigerant(mut self, refrigerant: &str) -> Self {
|
||||
self.refrigerant_id = refrigerant.to_string();
|
||||
@@ -597,6 +608,17 @@ impl IsentropicCompressor {
|
||||
/// solver state when this compressor is used as an actuator, or `1.0` when
|
||||
/// no control variable is linked. Non-finite or non-positive values fall
|
||||
/// back to `1.0` to keep the closure well-posed during early iterations.
|
||||
fn control_f_w(&self, state: &StateSlice) -> f64 {
|
||||
match self.calib_indices.f_w {
|
||||
Some(idx) => state
|
||||
.get(idx)
|
||||
.copied()
|
||||
.unwrap_or(self.f_w)
|
||||
.clamp(0.0, 1.0),
|
||||
None => self.f_w.clamp(0.0, 1.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn control_f_m(&self, state: &StateSlice) -> f64 {
|
||||
match self.calib_indices.z_flow {
|
||||
Some(i) if i < state.len() => {
|
||||
@@ -642,6 +664,7 @@ impl IsentropicCompressor {
|
||||
p_suc_pa: f64,
|
||||
h_suc_jkg: f64,
|
||||
p_dis_pa: f64,
|
||||
state: &StateSlice,
|
||||
) -> Result<f64, ComponentError> {
|
||||
let s_suc = backend
|
||||
.property(
|
||||
@@ -660,7 +683,11 @@ impl IsentropicCompressor {
|
||||
FluidState::PressureEntropy(Pressure::from_pascals(p_dis_pa), Entropy(s_suc)),
|
||||
)
|
||||
.map_err(|e| ComponentError::CalculationFailed(format!("H_dis_isen: {}", e)))?;
|
||||
Ok(h_suc_jkg + (h_dis_isen - h_suc_jkg) / self.effective_isentropic_efficiency())
|
||||
// Retention: h_dis = h_suc + f_w·Δh_is/η_is
|
||||
// f_w = 1 → adiabatic; f_w = 0 → all work lost (h_dis → h_suc).
|
||||
let dh = (h_dis_isen - h_suc_jkg) / self.effective_isentropic_efficiency();
|
||||
let f_w = self.control_f_w(state);
|
||||
Ok(h_suc_jkg + f_w * dh)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,7 +756,7 @@ impl Component for IsentropicCompressor {
|
||||
));
|
||||
}
|
||||
let h_dis =
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid, p_suc, h_suc, p_dis)?;
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid, p_suc, h_suc, p_dis, state)?;
|
||||
residuals[0] = state[dis_h] - h_dis;
|
||||
if !self.same_branch_m {
|
||||
residuals[1] = match (self.suction_m_idx, self.discharge_m_idx) {
|
||||
@@ -776,7 +803,7 @@ impl Component for IsentropicCompressor {
|
||||
let m_calc =
|
||||
self.swept_mass_flow(backend.as_ref(), fluid.clone(), p_suc, h_suc, p_dis)?;
|
||||
let h_dis =
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid, p_suc, h_suc, p_dis)?;
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid, p_suc, h_suc, p_dis, state)?;
|
||||
// Inverse-control actuator: scale the swept mass flow by the
|
||||
// linked control variable f_m (1.0 when no control is attached)
|
||||
// and the slide-valve position σ (1.0 when no slide valve).
|
||||
@@ -824,9 +851,15 @@ impl Component for IsentropicCompressor {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
return Err(ComponentError::InvalidState(
|
||||
"IsentropicCompressor displacement closure requires live physical suction and discharge states".to_string(),
|
||||
));
|
||||
// Soft domain penalty — never abort the whole system residual
|
||||
// evaluation. A hard InvalidState here kills Picard/homotopy recovery
|
||||
// after a singular-J Newton step wanders into P≈0 / h≈0.
|
||||
residuals[0] = 1.0e6;
|
||||
residuals[1] = 1.0e6;
|
||||
if !self.same_branch_m && residuals.len() > 2 {
|
||||
residuals[2] = 1.0e6;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let (Some(backend), Some(dis_p), Some(dis_h)) = (
|
||||
@@ -859,7 +892,7 @@ impl Component for IsentropicCompressor {
|
||||
p_suc,
|
||||
h_suc,
|
||||
p_cond_sat,
|
||||
)?
|
||||
state)?
|
||||
} else {
|
||||
return Err(ComponentError::InvalidState(
|
||||
"IsentropicCompressor requires physical live suction pressure/enthalpy"
|
||||
@@ -931,7 +964,7 @@ impl Component for IsentropicCompressor {
|
||||
let dph = h_suc * 1e-4 + 10.0;
|
||||
let dpd = p_dis * 1e-4 + 100.0;
|
||||
let h = |ps: f64, hs: f64, pd: f64| {
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid.clone(), ps, hs, pd)
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid.clone(), ps, hs, pd, state)
|
||||
};
|
||||
if let (Ok(a), Ok(b)) =
|
||||
(h(p_suc + dpp, h_suc, p_dis), h(p_suc - dpp, h_suc, p_dis))
|
||||
@@ -1030,7 +1063,7 @@ impl Component for IsentropicCompressor {
|
||||
let inj_ready = self.injection_ready();
|
||||
let hd = |ps: f64, hs: f64, pd: f64| -> Result<f64, ComponentError> {
|
||||
let h =
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid.clone(), ps, hs, pd)?;
|
||||
self.compute_h_dis_from_state(backend.as_ref(), fluid.clone(), ps, hs, pd, state)?;
|
||||
if inj_ready {
|
||||
let h_liq = self.liquid_enthalpy(pd)?;
|
||||
Ok(h - phi_inj * (h - h_liq))
|
||||
@@ -1053,6 +1086,22 @@ impl Component for IsentropicCompressor {
|
||||
{
|
||||
jacobian.add_entry(1, dis_p, -(a - b) / (2.0 * dpd));
|
||||
}
|
||||
// ∂r1/∂f_w = −dh (h_dis = h_suc + f_w·dh ⇒ r = h − h_dis)
|
||||
if let Some(f_w_idx) = self.calib_indices.f_w {
|
||||
if let Ok(h_full) = self.compute_h_dis_from_state(
|
||||
backend.as_ref(),
|
||||
fluid.clone(),
|
||||
p_suc,
|
||||
h_suc,
|
||||
p_dis,
|
||||
state,
|
||||
) {
|
||||
// Reconstruct dh from h_full at current f_w: h = h_suc + fw·dh
|
||||
let fw = self.control_f_w(state).max(1e-9);
|
||||
let dh = (h_full - h_suc) / fw;
|
||||
jacobian.add_entry(1, f_w_idx, -dh);
|
||||
}
|
||||
}
|
||||
// ∂r1/∂φ_inj = +(h_dis − h_liq): the injection actuator couples
|
||||
// into the energy balance so a controls[] loop has a plant to act
|
||||
// on (higher injection ⇒ lower discharge enthalpy ⇒ lower DGT).
|
||||
@@ -1064,7 +1113,7 @@ impl Component for IsentropicCompressor {
|
||||
p_suc,
|
||||
h_suc,
|
||||
p_dis,
|
||||
);
|
||||
state);
|
||||
let h_liq = self.liquid_enthalpy(p_dis);
|
||||
if let (Ok(h_dis), Ok(h_liq)) = (h_dis, h_liq) {
|
||||
jacobian.add_entry(1, inj_idx, h_dis - h_liq);
|
||||
@@ -1123,14 +1172,14 @@ impl Component for IsentropicCompressor {
|
||||
p_suc + dp,
|
||||
h_suc,
|
||||
p_cond_sat,
|
||||
);
|
||||
state);
|
||||
let hm = self.compute_h_dis_from_state(
|
||||
backend.as_ref(),
|
||||
fluid.clone(),
|
||||
p_suc - dp,
|
||||
h_suc,
|
||||
p_cond_sat,
|
||||
);
|
||||
state);
|
||||
if let (Ok(hp), Ok(hm)) = (hp, hm) {
|
||||
jacobian.add_entry(1, suc_p, -(hp - hm) / (2.0 * dp));
|
||||
}
|
||||
@@ -1142,14 +1191,14 @@ impl Component for IsentropicCompressor {
|
||||
p_suc,
|
||||
h_suc + dh,
|
||||
p_cond_sat,
|
||||
);
|
||||
state);
|
||||
let hm = self.compute_h_dis_from_state(
|
||||
backend.as_ref(),
|
||||
fluid,
|
||||
p_suc,
|
||||
h_suc - dh,
|
||||
p_cond_sat,
|
||||
);
|
||||
state);
|
||||
if let (Ok(hp), Ok(hm)) = (hp, hm) {
|
||||
jacobian.add_entry(1, suc_h, -(hp - hm) / (2.0 * dh));
|
||||
}
|
||||
@@ -1259,7 +1308,7 @@ impl Component for IsentropicCompressor {
|
||||
state[sp],
|
||||
h_suc,
|
||||
state[dp],
|
||||
)
|
||||
state)
|
||||
.unwrap_or(h_dis)
|
||||
}
|
||||
_ => h_dis,
|
||||
|
||||
Reference in New Issue
Block a user