///|
pub fn ClausiusClapeyron::pressure_bar(
self : ClausiusClapeyron,
temperature_k : Double,
) -> Double raise VleError {
assert_temperature(temperature_k)
assert_pressure(self.reference_pressure_bar)
if self.enthalpy_vaporization_j_per_mol <= 0.0 {
raise VleError::InvalidParameter(
"enthalpy of vaporization must be positive",
)
}
@math.exp(
@math.ln(self.reference_pressure_bar) -
self.enthalpy_vaporization_j_per_mol /
gas_constant_j_per_mol_k *
(1.0 / temperature_k - 1.0 / self.reference_temperature_k),
)
}
///|
pub fn ClausiusClapeyron::is_in_range(
self : ClausiusClapeyron,
temperature_k : Double,
) -> Bool {
temperature_k > 0.0 && self.reference_temperature_k > 0.0
}
///|
pub fn ClausiusClapeyron::pressure_change_ratio(
self : ClausiusClapeyron,
low_temperature_k : Double,
high_temperature_k : Double,
) -> Double raise VleError {
self.pressure_bar(high_temperature_k) / self.pressure_bar(low_temperature_k)
}
///|
pub fn ClausiusClapeyron::boiling_temperature_k(
self : ClausiusClapeyron,
pressure_bar : Double,
low_k? : Double = 200.0,
high_k? : Double = 600.0,
) -> Double raise VleError {
assert_pressure(pressure_bar)
let options = SolverOptions::new(tolerance=1.0E-8, max_iterations=120)
let report = solve_bisection(low=low_k, high=high_k, options~, fn(
temperature_k : Double,
) -> Double raise VleError {
self.pressure_bar(temperature_k) - pressure_bar
})
report.root
}
///|
pub fn ExtendedAntoine::is_in_range(
self : ExtendedAntoine,
temperature_k : Double,
) -> Bool {
temperature_k >= self.t_min_k && temperature_k <= self.t_max_k
}
///|
pub fn ExtendedAntoine::pressure_bar(
self : ExtendedAntoine,
temperature_k : Double,
) -> Double raise VleError {
assert_temperature(temperature_k)
let temperature_c = temperature_k - 273.15
let denominator = temperature_c + self.c
if denominator == 0.0 {
raise VleError::InvalidParameter(
"extended Antoine denominator cannot be zero",
)
}
let log_pressure = self.a -
self.b / denominator +
self.d * @math.pow(temperature_c, self.e)
@math.pow(10.0, log_pressure) * 0.001333223684
}
///|
pub fn ExtendedAntoine::pressure_bar_checked(
self : ExtendedAntoine,
temperature_k : Double,
) -> Double raise VleError {
if !self.is_in_range(temperature_k) {
raise VleError::InvalidParameter(
"temperature is outside extended Antoine range",
)
}
self.pressure_bar(temperature_k)
}
///|
pub fn ExtendedAntoine::boiling_temperature_k(
self : ExtendedAntoine,
pressure_bar : Double,
low_k? : Double = 200.0,
high_k? : Double = 600.0,
) -> Double raise VleError {
assert_pressure(pressure_bar)
let options = SolverOptions::new(tolerance=1.0E-8, max_iterations=120)
let report = solve_bisection(low=low_k, high=high_k, options~, fn(
temperature_k : Double,
) -> Double raise VleError {
self.pressure_bar(temperature_k) - pressure_bar
})
report.root
}
///|
fn compare_single_correlation(
reference : Component,
temperatures_k : Array[Double],
correlation : ExtendedAntoine,
) -> CorrelationComparison raise VleError {
if temperatures_k.length() == 0 {
raise VleError::EmptyMixture
}
let (total, maximum) = for i = 0, total = 0.0, maximum = 0.0; i <
temperatures_k.length(); {
let reference_pressure = reference.saturation_pressure_bar(
temperatures_k[i],
)
let candidate_pressure = correlation.pressure_bar(temperatures_k[i])
let difference = abs_double(candidate_pressure - reference_pressure) /
clamp_positive(reference_pressure)
continue i + 1,
total + difference,
if difference > maximum {
difference
} else {
maximum
}
} nobreak {
(total, maximum)
}
CorrelationComparison::{
name: correlation.source,
points: temperatures_k.length(),
mean_relative_difference: total / temperatures_k.length().to_double(),
maximum_relative_difference: maximum,
}
}
///|
pub fn compare_pressure_correlations(
reference : Component,
temperatures_k : Array[Double],
correlations : Array[ExtendedAntoine],
) -> Array[CorrelationComparison] raise VleError {
if correlations.length() == 0 {
raise VleError::EmptyMixture
}
[
for correlation in correlations => {
compare_single_correlation(reference, temperatures_k, correlation)
}
]
}
///|
pub fn correlation_relative_difference(
reference_pressure_bar : Double,
candidate_pressure_bar : Double,
) -> Double raise VleError {
if reference_pressure_bar <= 0.0 || candidate_pressure_bar <= 0.0 {
raise VleError::InvalidParameter("correlation pressures must be positive")
}
abs_double(candidate_pressure_bar - reference_pressure_bar) /
reference_pressure_bar
}
///|
pub fn correlation_mean_relative_difference(
reference_pressures_bar : Array[Double],
candidate_pressures_bar : Array[Double],
) -> Double raise VleError {
assert_same_length(
reference_pressures_bar.length(),
candidate_pressures_bar.length(),
)
if reference_pressures_bar.length() == 0 {
raise VleError::EmptyMixture
}
for i = 0, value = 0.0; i < reference_pressures_bar.length(); {
continue i + 1,
value +
correlation_relative_difference(
reference_pressures_bar[i],
candidate_pressures_bar[i],
)
} nobreak {
value / reference_pressures_bar.length().to_double()
}
}
///|
pub fn correlation_maximum_relative_difference(
reference_pressures_bar : Array[Double],
candidate_pressures_bar : Array[Double],
) -> Double raise VleError {
assert_same_length(
reference_pressures_bar.length(),
candidate_pressures_bar.length(),
)
if reference_pressures_bar.length() == 0 {
raise VleError::EmptyMixture
}
for i = 0, value = 0.0; i < reference_pressures_bar.length(); {
let difference = correlation_relative_difference(
reference_pressures_bar[i],
candidate_pressures_bar[i],
)
continue i + 1, if difference > value { difference } else { value }
} nobreak {
value
}
}
///|
pub fn compare_antoine_to_dippr(
component : Component,
dippr : Dippr101,
temperatures_k : Array[Double],
) -> Double raise VleError {
let reference = [
for temperature in temperatures_k => {
component.saturation_pressure_bar(temperature)
}
]
let candidate = [
for temperature in temperatures_k => dippr.pressure_bar(temperature)
]
correlation_mean_relative_difference(reference, candidate)
}
///|
pub fn compare_antoine_to_wagner(
component : Component,
wagner : Wagner,
temperatures_k : Array[Double],
) -> Double raise VleError {
let reference = [
for temperature in temperatures_k => {
component.saturation_pressure_bar(temperature)
}
]
let candidate = [
for temperature in temperatures_k => wagner.pressure_bar(temperature)
]
correlation_mean_relative_difference(reference, candidate)
}