///|
pub(all) struct SearchRange {
min : Double
max : Double
steps : Int
} derive(Debug, ToJson)
///|
pub(all) struct OptimizationResult {
volume : Double
conversion : Double
temperature : Double
objective : Double
feasible : Bool
} derive(Debug, ToJson)
///|
pub fn SearchRange::new(
min~ : Double,
max~ : Double,
steps~ : Int,
) -> SearchRange {
{ min, max, steps: steps.max(2) }
}
///|
pub fn maximize_conversion_under_temperature(
reaction : Reaction,
feed : Feed,
range : SearchRange,
max_temperature : Double,
thermal_mode? : ThermalMode = Adiabatic,
exchange? : HeatExchange,
) -> OptimizationResult {
scan_volume_range(range, fn(v) {
let point = match exchange {
Some(hx) => design_cstr(reaction, feed, v, thermal_mode~, exchange=hx)
None => design_cstr(reaction, feed, v, thermal_mode~)
}
if point.outlet_temperature <= max_temperature {
{
volume: v,
conversion: point.conversion,
temperature: point.outlet_temperature,
objective: point.conversion,
feasible: true,
}
} else {
{
volume: v,
conversion: point.conversion,
temperature: point.outlet_temperature,
objective: -1.0,
feasible: false,
}
}
})
}
///|
pub fn minimize_volume_for_conversion(
reaction : Reaction,
feed : Feed,
range : SearchRange,
target_conversion : Double,
kind? : ReactorKind = Pfr,
) -> OptimizationResult {
scan_volume_range(range, fn(v) {
let point = match kind {
Cstr => design_cstr(reaction, feed, v)
Pfr => design_pfr(reaction, feed, v)
Batch =>
design_batch(reaction, feed, v / feed.volumetric_flow.max(1.0e-12))
}
if point.conversion >= target_conversion {
{
volume: v,
conversion: point.conversion,
temperature: point.outlet_temperature,
objective: -v,
feasible: true,
}
} else {
{
volume: v,
conversion: point.conversion,
temperature: point.outlet_temperature,
objective: -1.0e99,
feasible: false,
}
}
})
}
///|
fn scan_volume_range(
range : SearchRange,
score : (Double) -> OptimizationResult,
) -> OptimizationResult {
let n = range.steps.max(2)
for i = 0, best = {
volume: range.min,
conversion: 0.0,
temperature: 0.0,
objective: -1.0e99,
feasible: false,
}
i < n
i = i + 1 {
let v = range.min +
(range.max - range.min) * Double::from_int(i) / Double::from_int(n - 1)
let candidate = score(v)
if candidate.feasible &&
(!best.feasible || candidate.objective > best.objective) {
continue i + 1, candidate
} else {
continue i + 1, best
}
} nobreak {
best
}
}