use super::FlashError;
use super::bubble::SaturationResult;
use super::incipient::{Point, solve_pressure, solve_temperature};
use super::system::SystemSpec;
pub fn dew_pressure(
spec: &SystemSpec,
t: f64,
y: &[f64],
tol: f64,
max_iter: usize,
) -> Result<SaturationResult, FlashError> {
check_len(spec, y)?;
let sp = solve_pressure(spec, t, y, Point::Dew, tol, max_iter)?;
Ok(SaturationResult {
value: sp.var,
incipient: sp.incipient,
k: sp.k,
})
}
pub fn dew_temperature(
spec: &SystemSpec,
p: f64,
y: &[f64],
tol: f64,
max_iter: usize,
) -> Result<SaturationResult, FlashError> {
check_len(spec, y)?;
let sp = solve_temperature(spec, p, y, Point::Dew, tol, max_iter)?;
Ok(SaturationResult {
value: sp.var,
incipient: sp.incipient,
k: sp.k,
})
}
fn check_len(spec: &SystemSpec, phase: &[f64]) -> Result<(), FlashError> {
if phase.len() != spec.n() {
return Err(FlashError::Dimension(format!(
"components={}, composition={}",
spec.n(),
phase.len()
)));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::activity::ActivityModel;
use crate::eos::{CubicEos, LiquidModel, VaporModel};
use crate::flash::bubble::bubble_pressure;
use crate::flash::system::k_values;
use crate::mixing::MixingRule;
use crate::types::Component;
fn n_butane() -> Component {
Component {
name: "n-butane".into(),
tc: 425.12,
pc: 3796.0,
omega: 0.200,
tb: 272.65,
psat_coeffs: vec![4.35, 2277.0, -30.0],
..Component::default()
}
}
fn n_heptane() -> Component {
Component {
name: "n-heptane".into(),
tc: 540.2,
pc: 2740.0,
omega: 0.350,
tb: 371.6,
psat_coeffs: vec![4.02, 2911.0, -56.0],
..Component::default()
}
}
fn rks(components: &[Component]) -> SystemSpec<'_> {
SystemSpec {
components,
vapor: VaporModel::Cubic(CubicEos::RKS1972),
liquid: LiquidModel::Cubic(CubicEos::RKS1972),
mixing_rule: MixingRule::Classical,
kij: &[],
aij: &[],
alpha: &[],
vl: &[],
delta: &[],
sat_models: &[],
ge_model: None,
}
}
#[test]
fn dew_pressure_satisfies_saturation_condition() {
let comps = [n_butane(), n_heptane()];
let spec = rks(&comps);
let y = [0.6, 0.4];
let res = dew_pressure(&spec, 400.0, &y, 1e-10, 200).unwrap();
let k = k_values(&spec, 400.0, res.value, &res.incipient, &y).unwrap();
let s: f64 = (0..2).map(|i| y[i] / k[i]).sum();
assert!((s - 1.0).abs() < 1e-7, "Σ y/K = {s}");
assert!((res.incipient.iter().sum::<f64>() - 1.0).abs() < 1e-9);
}
#[test]
fn dew_temperature_satisfies_saturation_condition() {
let comps = [n_butane(), n_heptane()];
let spec = rks(&comps);
let y = [0.6, 0.4];
let res = dew_temperature(&spec, 1000.0, &y, 1e-9, 200).unwrap();
let k = k_values(&spec, res.value, 1000.0, &res.incipient, &y).unwrap();
let s: f64 = (0..2).map(|i| y[i] / k[i]).sum();
assert!((s - 1.0).abs() < 1e-5, "Σ y/K = {s} at T={}", res.value);
}
#[test]
fn dew_pressure_at_or_below_bubble_pressure() {
let comps = [n_butane(), n_heptane()];
let spec = rks(&comps);
let t = 400.0;
let z = [0.5, 0.5];
let bub = bubble_pressure(&spec, t, &z, 1e-10, 200).unwrap();
let dew = dew_pressure(&spec, t, &z, 1e-10, 200).unwrap();
assert!(
dew.value <= bub.value + 1e-6,
"dew P {} should be ≤ bubble P {}",
dew.value,
bub.value
);
}
#[test]
fn dew_temperature_gamma_phi_wilson() {
let a = Component {
name: "2-propanol".into(),
tc: 508.3,
pc: 4762.0,
omega: 0.665,
liquid_volume: 76.8,
psat_coeffs: vec![5.31, 3100.0, -60.0],
..Component::default()
};
let b = Component {
name: "water".into(),
tc: 647.1,
pc: 22064.0,
omega: 0.344,
liquid_volume: 18.07,
psat_coeffs: vec![5.11, 3800.0, -46.0],
..Component::default()
};
let comps = [a, b];
let aij = vec![vec![0.0, 1100.0], vec![-250.0, 0.0]];
let vl = [76.8, 18.07];
let spec = SystemSpec {
components: &comps,
vapor: VaporModel::IdealGas,
liquid: LiquidModel::Activity(ActivityModel::Wilson),
mixing_rule: MixingRule::Classical,
kij: &[],
aij: &aij,
alpha: &[],
vl: &vl,
delta: &[],
sat_models: &[],
ge_model: None,
};
let y = [0.5, 0.5];
let res = dew_temperature(&spec, 101.325, &y, 1e-8, 200).unwrap();
let k = k_values(&spec, res.value, 101.325, &res.incipient, &y).unwrap();
let s: f64 = (0..2).map(|i| y[i] / k[i]).sum();
assert!((s - 1.0).abs() < 1e-4, "Σ y/K = {s} at T={}", res.value);
}
}