///|
/// Lightweight property correlations used by design and screening calculations.
pub enum PhaseState {
Solid
Liquid
Vapor
Supercritical
}
///|
pub fn solid_state() -> PhaseState {
Solid
}
///|
pub fn liquid_state() -> PhaseState {
Liquid
}
///|
pub fn vapor_state() -> PhaseState {
Vapor
}
///|
pub fn supercritical_state() -> PhaseState {
Supercritical
}
///|
pub struct AntoineCorrelation {
a : Float
b : Float
c : Float
minimum_c : Float
maximum_c : Float
}
///|
pub fn antoine(
a : Float,
b : Float,
c : Float,
minimum_c : Float,
maximum_c : Float,
) -> AntoineCorrelation {
{ a, b, c, minimum_c, maximum_c }
}
///|
pub fn AntoineCorrelation::log10_pressure(
self : AntoineCorrelation,
temperature_c : Float,
) -> Float {
self.a - self.b / (self.c + temperature_c)
}
///|
pub fn AntoineCorrelation::pressure_kpa(
self : AntoineCorrelation,
temperature_c : Float,
) -> Float {
let x : Float = self.log10_pressure(temperature_c)
let p : Float = 1.0 + 2.302585 * x + 2.65095 * x * x + 2.03444 * x * x * x
p.max(0.0) * 0.133322
}
///|
pub fn AntoineCorrelation::valid(
self : AntoineCorrelation,
temperature_c : Float,
) -> Bool {
temperature_c >= self.minimum_c && temperature_c <= self.maximum_c
}
///|
pub struct LinearProperty {
reference : Float
slope : Float
reference_temperature : Float
}
///|
pub fn linear_property(
reference : Float,
slope : Float,
reference_temperature : Float,
) -> LinearProperty {
{ reference, slope, reference_temperature }
}
///|
pub fn LinearProperty::at(
self : LinearProperty,
temperature_c : Float,
) -> Float {
self.reference + self.slope * (temperature_c - self.reference_temperature)
}
///|
pub struct ChemicalPropertySet {
name : String
molecular_weight : Float
density : LinearProperty
viscosity : LinearProperty
heat_capacity : LinearProperty
vapor_pressure : AntoineCorrelation
}
///|
pub fn chemical_properties(
name : String,
molecular_weight : Float,
density : LinearProperty,
viscosity : LinearProperty,
heat_capacity : LinearProperty,
vapor_pressure : AntoineCorrelation,
) -> ChemicalPropertySet {
{ name, molecular_weight, density, viscosity, heat_capacity, vapor_pressure }
}
///|
pub fn ChemicalPropertySet::density_at(
self : ChemicalPropertySet,
temperature_c : Float,
) -> Float {
self.density.at(temperature_c)
}
///|
pub fn ChemicalPropertySet::viscosity_at(
self : ChemicalPropertySet,
temperature_c : Float,
) -> Float {
self.viscosity.at(temperature_c)
}
///|
pub fn ChemicalPropertySet::heat_capacity_at(
self : ChemicalPropertySet,
temperature_c : Float,
) -> Float {
self.heat_capacity.at(temperature_c)
}
///|
pub fn ChemicalPropertySet::vapor_pressure_at(
self : ChemicalPropertySet,
temperature_c : Float,
) -> Float {
self.vapor_pressure.pressure_kpa(temperature_c)
}
///|
pub fn ChemicalPropertySet::phase_at(
self : ChemicalPropertySet,
temperature_c : Float,
pressure_kpa : Float,
) -> PhaseState {
if pressure_kpa > 22000.0 && temperature_c > 374.0 {
Supercritical
} else if self.vapor_pressure_at(temperature_c) > pressure_kpa {
Vapor
} else {
Liquid
}
}
///|
pub fn ChemicalPropertySet::mass_to_moles(
self : ChemicalPropertySet,
mass_kg : Float,
) -> Float {
if self.molecular_weight == 0.0 {
0.0
} else {
mass_kg * 1000.0 / self.molecular_weight
}
}
///|
pub fn ChemicalPropertySet::moles_to_mass(
self : ChemicalPropertySet,
moles : Float,
) -> Float {
moles * self.molecular_weight / 1000.0
}
///|
pub struct MixtureProperty {
component : String
mole_fraction : Float
property : ChemicalPropertySet
}
///|
pub fn mixture_property(
component : String,
mole_fraction : Float,
property : ChemicalPropertySet,
) -> MixtureProperty {
{ component, mole_fraction, property }
}
///|
pub fn mixture_density(
items : Array[MixtureProperty],
temperature_c : Float,
) -> Float {
items.fold(init=0.0, fn(v, item) {
v + item.mole_fraction * item.property.density_at(temperature_c)
})
}
///|
pub fn mixture_heat_capacity(
items : Array[MixtureProperty],
temperature_c : Float,
) -> Float {
items.fold(init=0.0, fn(v, item) {
v + item.mole_fraction * item.property.heat_capacity_at(temperature_c)
})
}
///|
pub fn mixture_vapor_pressure(
items : Array[MixtureProperty],
temperature_c : Float,
) -> Float {
items.fold(init=0.0, fn(v, item) {
v + item.mole_fraction * item.property.vapor_pressure_at(temperature_c)
})
}
///|
pub fn mixture_fraction_total(items : Array[MixtureProperty]) -> Float {
items.fold(init=0.0, fn(v, item) { v + item.mole_fraction })
}
///|
pub fn mixture_valid(items : Array[MixtureProperty]) -> Bool {
let total = mixture_fraction_total(items)
total > 0.999 &&
total < 1.001 &&
items.all(fn(item) { item.mole_fraction >= 0.0 })
}
///|
pub fn property_reynolds_number(
density : Float,
velocity : Float,
diameter : Float,
viscosity : Float,
) -> Float {
if viscosity == 0.0 {
0.0
} else {
density * velocity * diameter / viscosity
}
}
///|
pub fn prandtl_number(
heat_capacity : Float,
viscosity : Float,
conductivity : Float,
) -> Float {
if conductivity == 0.0 {
0.0
} else {
heat_capacity * viscosity / conductivity
}
}
///|
pub fn thermal_expansion(density : Float, slope : Float) -> Float {
if density == 0.0 {
0.0
} else {
-slope / density
}
}
///|
pub fn property_table(
items : Array[MixtureProperty],
temperature_c : Float,
) -> ReportTable {
let rows : Array[Array[String]] = []
for item in items {
rows.push([
item.component,
"\{item.mole_fraction}",
"\{item.property.density_at(temperature_c)}",
"\{item.property.viscosity_at(temperature_c)}",
"\{item.property.heat_capacity_at(temperature_c)}",
"\{item.property.vapor_pressure_at(temperature_c)}",
])
}
table(
[
"component", "mole fraction", "density", "viscosity", "heat capacity", "vapor pressure",
],
rows,
)
}