use nalgebra::{DMatrix, DVector};
pub(crate) struct StiffIvpCase {
pub name: &'static str,
pub t_end: f64,
pub step: f64,
pub initial: &'static [f64],
pub reference: Option<&'static [f64]>,
pub abs_tolerance: f64,
pub rel_tolerance: f64,
pub rhs: fn(f64, &DVector<f64>) -> DVector<f64>,
pub jacobian: Option<fn(f64, &DVector<f64>) -> DMatrix<f64>>,
}
pub(crate) fn robertson() -> StiffIvpCase {
StiffIvpCase {
name: "robertson",
t_end: 1.0,
step: 1.0e-4,
initial: &[1.0, 0.0, 0.0],
reference: Some(&[0.9664597405, 3.074626580e-5, 0.03350951681]),
abs_tolerance: 2.0e-5,
rel_tolerance: 2.0e-4,
rhs: robertson_rhs,
jacobian: Some(robertson_jacobian),
}
}
pub(crate) fn hires() -> StiffIvpCase {
StiffIvpCase {
name: "hires",
t_end: 1.0,
step: 0.001,
initial: &[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0057],
reference: None,
abs_tolerance: 1.0e-5,
rel_tolerance: 5.0e-2,
rhs: hires_rhs,
jacobian: None,
}
}
pub(crate) fn combustion_chain_initial() -> DVector<f64> {
DVector::from_vec(vec![1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.5, 300.0])
}
pub(crate) fn combustion_chain_rhs(_: f64, y: &DVector<f64>) -> DVector<f64> {
let mut dy = DVector::zeros(y.len());
let temperature_factor = (0.003 * (y[9] - 300.0)).exp();
let oxygen = y[8].max(0.0);
let rates = [
80.0 * temperature_factor * y[0].max(0.0) * oxygen,
60.0 * temperature_factor * y[1].max(0.0) * oxygen,
45.0 * temperature_factor * y[2].max(0.0) * oxygen,
35.0 * temperature_factor * y[3].max(0.0) * oxygen,
28.0 * temperature_factor * y[4].max(0.0) * oxygen,
22.0 * temperature_factor * y[5].max(0.0) * oxygen,
18.0 * temperature_factor * y[6].max(0.0) * oxygen,
14.0 * temperature_factor * y[7].max(0.0) * oxygen,
];
dy[0] = -rates[0];
for index in 1..8 {
dy[index] = rates[index - 1] - rates[index];
}
dy[8] = -0.5 * rates.iter().sum::<f64>();
dy[9] = 0.04 * rates.iter().sum::<f64>() - 0.7 * (y[9] - 300.0);
dy
}
pub(crate) fn integrate_rk4(
rhs: fn(f64, &DVector<f64>) -> DVector<f64>,
t0: f64,
t_end: f64,
initial: &DVector<f64>,
step: f64,
) -> DVector<f64> {
assert!(step.is_finite() && step > 0.0);
let steps = ((t_end - t0) / step).ceil() as usize;
let mut t = t0;
let mut y = initial.clone();
for index in 0..steps {
let h = (t_end - t).min(step);
let k1 = rhs(t, &y);
let k2 = rhs(t + 0.5 * h, &(&y + 0.5 * h * &k1));
let k3 = rhs(t + 0.5 * h, &(&y + 0.5 * h * &k2));
let k4 = rhs(t + h, &(&y + h * &k3));
y += (h / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
t = if index + 1 == steps { t_end } else { t + h };
}
y
}
fn robertson_rhs(_: f64, y: &DVector<f64>) -> DVector<f64> {
let r1 = 0.04 * y[0];
let r2 = 1.0e4 * y[1] * y[2];
let r3 = 3.0e7 * y[1] * y[1];
DVector::from_vec(vec![-r1 + r2, r1 - r2 - r3, r3])
}
fn robertson_jacobian(_: f64, y: &DVector<f64>) -> DMatrix<f64> {
DMatrix::from_row_slice(
3,
3,
&[
-0.04,
1.0e4 * y[2],
1.0e4 * y[1],
0.04,
-1.0e4 * y[2] - 6.0e7 * y[1],
-1.0e4 * y[1],
0.0,
6.0e7 * y[1],
0.0,
],
)
}
fn hires_rhs(_: f64, y: &DVector<f64>) -> DVector<f64> {
DVector::from_vec(vec![
-1.71 * y[0] + 0.43 * y[1] + 8.32 * y[2] - 7.0e-4,
1.71 * y[0] - 8.75 * y[1],
-10.03 * y[2] + 0.43 * y[3] + 0.035 * y[4],
8.32 * y[1] + 1.71 * y[2] - 1.12 * y[3],
-1.745 * y[4] + 0.43 * y[5] + 0.43 * y[6],
-280.0 * y[5] * y[7] + 0.69 * y[3] + 1.71 * y[4] - 0.43 * y[5] + 0.69 * y[6],
280.0 * y[5] * y[7] - 1.81 * y[6],
-1.81 * y[6] + 1.81 * y[7],
])
}