///|
/// Tie lines ordered by increasing raffinate-phase solute fraction.
pub(all) struct TieLineTable {
lines : Array[@domain.TieLine]
} derive(Debug, Eq)
///|
pub fn TieLineTable::new(
lines : ArrayView[@domain.TieLine],
) -> TieLineTable raise @domain.ExtractionError {
if lines.length() < 2 {
raise @domain.ExtractionError::InsufficientData(
required=2,
actual=lines.length(),
)
}
for index in 1.. @domain.TernaryComposition raise @domain.ExtractionError {
@domain.TernaryComposition::from_amounts(
carrier=first.carrier + fraction * (second.carrier - first.carrier),
solute=first.solute + fraction * (second.solute - first.solute),
solvent=first.solvent + fraction * (second.solvent - first.solvent),
)
}
///|
pub fn TieLineTable::at_raffinate_solute(
self : TieLineTable,
solute_fraction : Double,
) -> @domain.TieLine raise @domain.ExtractionError {
@domain.require_fraction("raffinate solute fraction", solute_fraction)
let first = self.lines[0].raffinate.solute
let last = self.lines[self.lines.length() - 1].raffinate.solute
if solute_fraction < first || solute_fraction > last {
raise @domain.ExtractionError::OutOfRange(
name="raffinate solute fraction",
value=solute_fraction,
lower=first,
upper=last,
)
}
for index in 0..<(self.lines.length() - 1) {
let left = self.lines[index]
let right = self.lines[index + 1]
if solute_fraction >= left.raffinate.solute &&
solute_fraction <= right.raffinate.solute {
let fraction = (solute_fraction - left.raffinate.solute) /
(right.raffinate.solute - left.raffinate.solute)
return @domain.TieLine::new(
raffinate=interpolate_composition(
left.raffinate,
right.raffinate,
fraction,
),
extract=interpolate_composition(left.extract, right.extract, fraction),
)
}
}
self.lines[self.lines.length() - 1]
}
///|
/// Select the measured tie line whose segment is closest to an overall point.
pub fn TieLineTable::nearest(
self : TieLineTable,
overall : @domain.TernaryComposition,
) -> @domain.TieLine {
let mut best = self.lines[0]
let mut best_residual = 1.0e300
for line in self.lines {
let a = line.raffinate
let b = line.extract
let dc = a.carrier - b.carrier
let du = a.solute - b.solute
let ds = a.solvent - b.solvent
let denominator = dc * dc + du * du + ds * ds
let fraction = (if denominator > 0.0 {
(
(overall.carrier - b.carrier) * dc +
(overall.solute - b.solute) * du +
(overall.solvent - b.solvent) * ds
) /
denominator
} else {
0.0
}).clamp(min=0.0, max=1.0)
let pc = b.carrier + fraction * dc
let pu = b.solute + fraction * du
let ps = b.solvent + fraction * ds
let residual = (pc - overall.carrier) * (pc - overall.carrier) +
(pu - overall.solute) * (pu - overall.solute) +
(ps - overall.solvent) * (ps - overall.solvent)
if residual < best_residual {
best = line
best_residual = residual
}
}
best
}