///|
/// A reproducible engineering casebook. Values are dimensionless on the same
/// basis as the liquid package and are intentionally kept in source control.
pub(all) struct BenchmarkCase {
id : String
carrier_flow : Double
feed_concentration : Double
solvent_flow : Double
distribution : Double
} derive(Debug, Eq, ToJson)
///|
pub fn BenchmarkCase::new(
id~ : String,
carrier_flow~ : Double,
feed_concentration~ : Double,
solvent_flow~ : Double,
distribution~ : Double,
) -> BenchmarkCase raise @domain.ExtractionError {
@domain.require_positive("benchmark carrier flow", carrier_flow)
@domain.require_non_negative(
"benchmark feed concentration", feed_concentration,
)
@domain.require_positive("benchmark solvent flow", solvent_flow)
@domain.require_positive("benchmark distribution", distribution)
{ id, carrier_flow, feed_concentration, solvent_flow, distribution }
}
///|
pub(all) struct BenchmarkResult {
case_id : String
recovery : Double
raffinate : Double
residual : Double
extraction_factor : Double
} derive(Debug, Eq, ToJson)
///|
pub fn run(
case : BenchmarkCase,
) -> BenchmarkResult raise @domain.ExtractionError {
let result = @liquid.single_stage_constant_k(
feed_flow=case.carrier_flow,
feed_concentration=case.feed_concentration,
solvent_flow=case.solvent_flow,
distribution=case.distribution,
)
{
case_id: case.id,
recovery: result.recovery,
raffinate: result.raffinate_concentration,
residual: result.balance_residual,
extraction_factor: @liquid.extraction_factor(
distribution=case.distribution,
solvent_flow=case.solvent_flow,
carrier_flow=case.carrier_flow,
),
}
}
///|
pub fn casebook() -> Array[BenchmarkCase] raise @domain.ExtractionError {
[
BenchmarkCase::new(
id="water-acetone-01",
carrier_flow=100.0,
feed_concentration=0.010,
solvent_flow=25.0,
distribution=1.20,
),
BenchmarkCase::new(
id="water-acetone-02",
carrier_flow=100.0,
feed_concentration=0.025,
solvent_flow=25.0,
distribution=1.20,
),
BenchmarkCase::new(
id="water-acetone-03",
carrier_flow=100.0,
feed_concentration=0.050,
solvent_flow=25.0,
distribution=1.20,
),
BenchmarkCase::new(
id="water-acetone-04",
carrier_flow=100.0,
feed_concentration=0.100,
solvent_flow=25.0,
distribution=1.20,
),
BenchmarkCase::new(
id="water-acetone-05",
carrier_flow=100.0,
feed_concentration=0.200,
solvent_flow=25.0,
distribution=1.20,
),
BenchmarkCase::new(
id="water-acetone-06",
carrier_flow=100.0,
feed_concentration=0.010,
solvent_flow=50.0,
distribution=1.80,
),
BenchmarkCase::new(
id="water-acetone-07",
carrier_flow=100.0,
feed_concentration=0.025,
solvent_flow=50.0,
distribution=1.80,
),
BenchmarkCase::new(
id="water-acetone-08",
carrier_flow=100.0,
feed_concentration=0.050,
solvent_flow=50.0,
distribution=1.80,
),
BenchmarkCase::new(
id="water-acetone-09",
carrier_flow=100.0,
feed_concentration=0.100,
solvent_flow=50.0,
distribution=1.80,
),
BenchmarkCase::new(
id="water-acetone-10",
carrier_flow=100.0,
feed_concentration=0.200,
solvent_flow=50.0,
distribution=1.80,
),
BenchmarkCase::new(
id="water-acetone-11",
carrier_flow=250.0,
feed_concentration=0.010,
solvent_flow=50.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-12",
carrier_flow=250.0,
feed_concentration=0.025,
solvent_flow=50.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-13",
carrier_flow=250.0,
feed_concentration=0.050,
solvent_flow=50.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-14",
carrier_flow=250.0,
feed_concentration=0.100,
solvent_flow=50.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-15",
carrier_flow=250.0,
feed_concentration=0.200,
solvent_flow=50.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-16",
carrier_flow=250.0,
feed_concentration=0.010,
solvent_flow=100.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-17",
carrier_flow=250.0,
feed_concentration=0.025,
solvent_flow=100.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-18",
carrier_flow=250.0,
feed_concentration=0.050,
solvent_flow=100.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-19",
carrier_flow=250.0,
feed_concentration=0.100,
solvent_flow=100.0,
distribution=2.40,
),
BenchmarkCase::new(
id="water-acetone-20",
carrier_flow=250.0,
feed_concentration=0.200,
solvent_flow=100.0,
distribution=2.40,
),
]
}
///|
pub fn run_casebook(
cases : ArrayView[BenchmarkCase],
) -> Array[BenchmarkResult] raise @domain.ExtractionError {
if cases.length() == 0 {
raise @domain.ExtractionError::InsufficientData(required=1, actual=0)
}
[
for case in cases => run(case)
]
}
///|
pub fn maximum_residual(results : ArrayView[BenchmarkResult]) -> Double {
let mut maximum = 0.0
for result in results {
maximum = maximum.max(result.residual.abs())
}
maximum
}
///|
pub fn average_recovery(results : ArrayView[BenchmarkResult]) -> Double {
if results.length() == 0 {
0.0
} else {
let mut sum = 0.0
for result in results {
sum += result.recovery
}
sum / results.length().to_double()
}
}