///|
/// Absorption, stripping, and contacting calculations.
pub struct EquilibriumLine {
slope : Float
intercept : Float
}
///|
pub fn equilibrium_line(slope : Float, intercept : Float) -> EquilibriumLine {
{ slope, intercept }
}
///|
pub fn EquilibriumLine::gas_fraction(
self : EquilibriumLine,
liquid_fraction : Float,
) -> Float {
self.slope * liquid_fraction + self.intercept
}
///|
pub fn EquilibriumLine::valid(self : EquilibriumLine) -> Bool {
self.slope >= 0.0 && self.intercept >= 0.0
}
///|
pub struct AbsorberDesign {
gas_flow_mol_h : Float
liquid_flow_mol_h : Float
inlet_gas_fraction : Float
outlet_gas_fraction : Float
equilibrium : EquilibriumLine
overall_kya_h : Float
}
///|
pub fn absorber_design(
gas_flow_mol_h : Float,
liquid_flow_mol_h : Float,
inlet_gas_fraction : Float,
outlet_gas_fraction : Float,
equilibrium : EquilibriumLine,
overall_kya_h : Float,
) -> AbsorberDesign {
{
gas_flow_mol_h,
liquid_flow_mol_h,
inlet_gas_fraction,
outlet_gas_fraction,
equilibrium,
overall_kya_h,
}
}
///|
pub fn AbsorberDesign::removal_fraction(self : AbsorberDesign) -> Float {
if self.inlet_gas_fraction == 0.0 {
0.0
} else {
(self.inlet_gas_fraction - self.outlet_gas_fraction) /
self.inlet_gas_fraction
}
}
///|
pub fn AbsorberDesign::solute_load_mol_h(self : AbsorberDesign) -> Float {
self.gas_flow_mol_h * (self.inlet_gas_fraction - self.outlet_gas_fraction)
}
///|
pub fn AbsorberDesign::liquid_to_gas(self : AbsorberDesign) -> Float {
if self.gas_flow_mol_h == 0.0 {
0.0
} else {
self.liquid_flow_mol_h / self.gas_flow_mol_h
}
}
///|
pub fn AbsorberDesign::ntu(self : AbsorberDesign) -> Float {
if self.outlet_gas_fraction <= 0.0 ||
self.inlet_gas_fraction <= self.outlet_gas_fraction {
0.0
} else {
(self.inlet_gas_fraction - self.outlet_gas_fraction) /
self.outlet_gas_fraction
}
}
///|
pub fn AbsorberDesign::height(self : AbsorberDesign, htu_m : Float) -> Float {
self.ntu() * htu_m
}
///|
pub struct PackedColumn {
diameter_m : Float
packing_height_m : Float
packing_factor : Float
gas_velocity_m_s : Float
flooding_velocity_m_s : Float
}
///|
pub fn packed_column(
diameter_m : Float,
packing_height_m : Float,
packing_factor : Float,
gas_velocity_m_s : Float,
flooding_velocity_m_s : Float,
) -> PackedColumn {
{
diameter_m,
packing_height_m,
packing_factor,
gas_velocity_m_s,
flooding_velocity_m_s,
}
}
///|
pub fn PackedColumn::area_m2(self : PackedColumn) -> Float {
0.785398 * self.diameter_m * self.diameter_m
}
///|
pub fn PackedColumn::flood_fraction(self : PackedColumn) -> Float {
if self.flooding_velocity_m_s == 0.0 {
0.0
} else {
self.gas_velocity_m_s / self.flooding_velocity_m_s
}
}
///|
pub fn PackedColumn::capacity_mol_h(
self : PackedColumn,
gas_density_mol_m3 : Float,
) -> Float {
self.area_m2() * self.gas_velocity_m_s * gas_density_mol_m3 * 3600.0
}
///|
pub fn PackedColumn::acceptable(
self : PackedColumn,
maximum_fraction : Float,
) -> Bool {
self.flood_fraction() <= maximum_fraction && self.packing_height_m > 0.0
}
///|
pub struct TrayStage {
number : Int
efficiency : Float
liquid_flow : Float
vapor_flow : Float
}
///|
pub fn tray_stage(
number : Int,
efficiency : Float,
liquid_flow : Float,
vapor_flow : Float,
) -> TrayStage {
{ number, efficiency, liquid_flow, vapor_flow }
}
///|
pub fn TrayStage::effective_stage(self : TrayStage) -> Float {
self.efficiency.max(0.0).min(1.0)
}
///|
pub fn TrayStage::capacity_ratio(self : TrayStage) -> Float {
if self.vapor_flow == 0.0 {
0.0
} else {
self.liquid_flow / self.vapor_flow
}
}
///|
pub fn trays_required(theoretical_stages : Float, efficiency : Float) -> Float {
if efficiency <= 0.0 {
0.0
} else {
theoretical_stages / efficiency
}
}
///|
pub fn stage_efficiency_table(stages : Array[TrayStage]) -> ReportTable {
let rows : Array[Array[String]] = []
for stage in stages {
rows.push([
"\{stage.number}",
"\{stage.efficiency}",
"\{stage.effective_stage()}",
"\{stage.capacity_ratio()}",
])
}
table(["stage", "efficiency", "effective", "liquid/vapor"], rows)
}
///|
pub fn mass_transfer_coefficient(
diffusivity_m2_s : Float,
film_thickness_m : Float,
) -> Float {
if film_thickness_m == 0.0 {
0.0
} else {
diffusivity_m2_s / film_thickness_m
}
}
///|
pub fn sherwood_number(
coefficient_m_s : Float,
length_m : Float,
diffusivity_m2_s : Float,
) -> Float {
if diffusivity_m2_s == 0.0 {
0.0
} else {
coefficient_m_s * length_m / diffusivity_m2_s
}
}
///|
pub fn transfer_duty(
flow_mol_h : Float,
inlet_fraction : Float,
outlet_fraction : Float,
) -> Float {
flow_mol_h * (inlet_fraction - outlet_fraction).max(0.0)
}
///|
pub fn transfer_gate(removal : Float, target : Float) -> Bool {
removal >= target
}