Ship the Next.js cycle editor with CAD chrome, technical HX symbols, Fixed/Free boundary guidance, and secondary water/air pressure drop support in the solver stack. Co-authored-by: Cursor <cursoragent@cursor.com>
191 lines
5.8 KiB
Rust
191 lines
5.8 KiB
Rust
//! Shell-and-tube heat exchanger rating via Bell–Delaware shell-side factors.
|
||
//!
|
||
//! Computes shell-side HTC `h_s = h_ideal · J_C · J_L · J_B · J_R · J_S` and a
|
||
//! combined UA with tube-side Gnielinski / Cooper / Shah as selected.
|
||
|
||
use super::bphx_correlation::{BphxCorrelation, CorrelationParams, FlowRegime};
|
||
use super::correlation_registry::CorrelationId;
|
||
use super::pool_boiling::{cooper_1984, PoolBoilingInput};
|
||
|
||
/// Geometric inputs for Bell–Delaware correction factors.
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct BellDelawareGeometry {
|
||
/// Fraction of tubes in cross-flow (F_C ≈ 1 − 2 F_W).
|
||
pub f_c: f64,
|
||
/// Leakage area ratio r_s = S_sb / (S_sb + S_tb).
|
||
pub r_s: f64,
|
||
/// Leakage/main stream area ratio r_lm = (S_sb + S_tb) / S_m.
|
||
pub r_lm: f64,
|
||
/// Bypass correction J_B (typical 0.7–0.9).
|
||
pub j_b: f64,
|
||
/// Laminar gradient correction J_R (1.0 if Re_s ≥ 100).
|
||
pub j_r: f64,
|
||
/// Unequal baffle spacing correction J_S (1.0 if uniform).
|
||
pub j_s: f64,
|
||
/// Ideal cross-flow HTC [W/(m²·K)] before corrections.
|
||
pub h_ideal: f64,
|
||
/// Shell-side area [m²].
|
||
pub area_shell_m2: f64,
|
||
/// Tube-side area [m²].
|
||
pub area_tube_m2: f64,
|
||
}
|
||
|
||
impl Default for BellDelawareGeometry {
|
||
fn default() -> Self {
|
||
Self {
|
||
f_c: 0.9,
|
||
r_s: 0.4,
|
||
r_lm: 0.3,
|
||
j_b: 0.8,
|
||
j_r: 1.0,
|
||
j_s: 1.0,
|
||
h_ideal: 2500.0,
|
||
area_shell_m2: 30.0,
|
||
area_tube_m2: 25.0,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Bell–Delaware correction factors.
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct BellDelawareFactors {
|
||
/// Configuration factor J_C.
|
||
pub j_c: f64,
|
||
/// Leakage factor J_L.
|
||
pub j_l: f64,
|
||
/// Bypass factor J_B.
|
||
pub j_b: f64,
|
||
/// Laminar factor J_R.
|
||
pub j_r: f64,
|
||
/// Spacing factor J_S.
|
||
pub j_s: f64,
|
||
}
|
||
|
||
/// Computes J-factors from geometry (Taborek / Delaware handbook forms).
|
||
pub fn bell_delaware_factors(geom: &BellDelawareGeometry) -> BellDelawareFactors {
|
||
let j_c = 0.55 + 0.72 * geom.f_c.clamp(0.0, 1.0);
|
||
let j_l = 0.44 * (1.0 - geom.r_s)
|
||
+ (1.0 - 0.44 * (1.0 - geom.r_s)) * (-2.2 * geom.r_lm).exp();
|
||
BellDelawareFactors {
|
||
j_c: j_c.clamp(0.65, 1.15),
|
||
j_l: j_l.clamp(0.2, 1.0),
|
||
j_b: geom.j_b.clamp(0.7, 0.9),
|
||
j_r: geom.j_r.clamp(0.7, 1.0),
|
||
j_s: geom.j_s.clamp(0.8, 1.0),
|
||
}
|
||
}
|
||
|
||
/// Shell-side HTC after Bell–Delaware corrections [W/(m²·K)].
|
||
pub fn shell_side_htc(geom: &BellDelawareGeometry) -> f64 {
|
||
let f = bell_delaware_factors(geom);
|
||
geom.h_ideal * f.j_c * f.j_l * f.j_b * f.j_r * f.j_s
|
||
}
|
||
|
||
/// Tube-side HTC using a registered correlation [W/(m²·K)].
|
||
pub fn tube_side_htc(
|
||
correlation: CorrelationId,
|
||
params: &CorrelationParams,
|
||
pool: Option<&PoolBoilingInput>,
|
||
) -> f64 {
|
||
match correlation {
|
||
CorrelationId::Cooper1984 => pool
|
||
.and_then(|p| cooper_1984(p).ok())
|
||
.unwrap_or(3000.0),
|
||
CorrelationId::Gnielinski1976 => BphxCorrelation::Gnielinski1976.compute_htc(params).h,
|
||
CorrelationId::Shah2009 => BphxCorrelation::Shah2009.compute_htc(params).h,
|
||
CorrelationId::Shah1979 => BphxCorrelation::Shah1979.compute_htc(params).h,
|
||
CorrelationId::Cavallini2006 => BphxCorrelation::Cavallini2006.compute_htc(params).h,
|
||
CorrelationId::Kandlikar1990 => BphxCorrelation::Kandlikar1990.compute_htc(params).h,
|
||
_ => BphxCorrelation::Gnielinski1976.compute_htc(params).h,
|
||
}
|
||
}
|
||
|
||
/// Combined UA [W/K] from shell and tube sides (wall resistance neglected).
|
||
pub fn shell_and_tube_ua(
|
||
geom: &BellDelawareGeometry,
|
||
h_tube: f64,
|
||
) -> f64 {
|
||
let h_shell = shell_side_htc(geom);
|
||
let r = 1.0 / (h_shell.max(1.0) * geom.area_shell_m2.max(1e-9))
|
||
+ 1.0 / (h_tube.max(1.0) * geom.area_tube_m2.max(1e-9));
|
||
1.0 / r
|
||
}
|
||
|
||
/// Rating helper wrapping Bell–Delaware + tube correlation.
|
||
#[derive(Debug, Clone)]
|
||
pub struct ShellAndTubeHx {
|
||
geom: BellDelawareGeometry,
|
||
tube_correlation: CorrelationId,
|
||
last_ua: f64,
|
||
}
|
||
|
||
impl ShellAndTubeHx {
|
||
/// Creates a shell-and-tube rater with default geometry.
|
||
pub fn new(geom: BellDelawareGeometry) -> Self {
|
||
Self {
|
||
geom,
|
||
tube_correlation: CorrelationId::Gnielinski1976,
|
||
last_ua: 0.0,
|
||
}
|
||
}
|
||
|
||
/// Selects tube-side correlation.
|
||
pub fn with_tube_correlation(mut self, id: CorrelationId) -> Self {
|
||
self.tube_correlation = id;
|
||
self
|
||
}
|
||
|
||
/// Rates UA from operating tube-side params.
|
||
pub fn rate_ua(&mut self, params: &CorrelationParams) -> f64 {
|
||
let h_tube = tube_side_htc(self.tube_correlation, params, None);
|
||
self.last_ua = shell_and_tube_ua(&self.geom, h_tube);
|
||
self.last_ua
|
||
}
|
||
|
||
/// Last computed UA [W/K].
|
||
pub fn ua(&self) -> f64 {
|
||
self.last_ua
|
||
}
|
||
|
||
/// Geometry accessor.
|
||
pub fn geometry(&self) -> &BellDelawareGeometry {
|
||
&self.geom
|
||
}
|
||
}
|
||
|
||
/// Default condensation CorrelationParams for tube-side rating.
|
||
pub fn default_tube_params(regime: FlowRegime) -> CorrelationParams {
|
||
CorrelationParams {
|
||
regime,
|
||
mass_flux: 200.0,
|
||
..CorrelationParams::default()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn factors_in_expected_bands() {
|
||
let f = bell_delaware_factors(&BellDelawareGeometry::default());
|
||
assert!(f.j_c >= 0.65 && f.j_c <= 1.15);
|
||
assert!(f.j_l >= 0.2 && f.j_l <= 1.0);
|
||
}
|
||
|
||
#[test]
|
||
fn shell_htc_less_than_ideal() {
|
||
let g = BellDelawareGeometry::default();
|
||
let h = shell_side_htc(&g);
|
||
assert!(h < g.h_ideal);
|
||
assert!(h > 500.0);
|
||
}
|
||
|
||
#[test]
|
||
fn rate_ua_positive() {
|
||
let mut hx = ShellAndTubeHx::new(BellDelawareGeometry::default());
|
||
let ua = hx.rate_ua(&default_tube_params(FlowRegime::SinglePhaseLiquid));
|
||
assert!(ua.is_finite() && ua > 1000.0);
|
||
}
|
||
}
|