use evalexpr_jit::system::EquationSystem;
use nalgebra::U7;
use ode_solvers::dopri5::*;
use ode_solvers::*;
use rayon::prelude::*;
type State = OVector<f64, U7>;
struct ChemicalSystem {
system: &'static EquationSystem,
vmax: [f64; 7],
km: [f64; 7],
kie: [f64; 7],
}
impl ChemicalSystem {
fn new(system: &'static EquationSystem, vmax: [f64; 7], km: [f64; 7], kie: [f64; 7]) -> Self {
Self {
system,
vmax,
km,
kie,
}
}
}
impl System<f64, State> for ChemicalSystem {
#[inline(always)]
fn system(&self, _t: f64, y: &State, dy: &mut State) {
let mut params = [0.0; 25];
params[0] = y[0]; params[1] = y[1];
#[allow(clippy::manual_memcpy)]
{
let mut idx = 2;
for i in 0..7 {
params[idx] = self.vmax[i];
params[idx + 1] = self.km[i];
params[idx + 2] = self.kie[i];
idx += 3;
}
}
let mut buffer = [0.0; 21];
self.system.fun()(¶ms, &mut buffer);
for i in 0..7 {
dy[i] = buffer[i];
}
}
}
#[allow(clippy::too_many_arguments)]
#[inline]
fn run_simulation(
dt: f64,
s0: f64,
p0: f64,
e0: f64,
vmax: [f64; 7],
km: [f64; 7],
kie: [f64; 7],
system: &'static EquationSystem,
) -> Result<(), String> {
let mut y0 = State::zeros();
y0[0] = s0;
y0[1] = p0;
y0[2] = e0;
let t0 = 0.0;
let tf = 150.0;
let system = ChemicalSystem::new(system, vmax, km, kie);
let mut stepper = Dopri5::new(
system, t0, tf, dt, y0, 1.0e-4, 1.0e-8, );
match stepper.integrate() {
Ok(_) => Ok(()),
Err(e) => Err(e.to_string()),
}
}
fn main() {
if let Err(e) = rayon::ThreadPoolBuilder::new()
.num_threads(num_cpus::get())
.build_global()
{
eprintln!("Thread pool initialization warning (may be already initialized): {e}");
}
let system = Box::leak(Box::new(
EquationSystem::new({
let mut equations = Vec::new();
for i in 1..=7 {
equations.push(format!("-(vmax_{i} * S) / (km_{i} + S)"));
equations.push(format!("(vmax_{i} * S) / (km_{i} + S)"));
equations.push(format!("-kie_{i} * P"));
}
equations
})
.expect("Failed to create equation system"),
));
let dt = 1e-1;
let vmax = [0.85; 7]; let km = [150.0; 7]; let kie = [0.01; 7];
let initial_conditions: Vec<(f64, f64, f64)> = (100..=5000)
.into_par_iter() .map(|i| (i as f64 * 0.5, i as f64 * 0.1, i as f64 * 0.01))
.collect();
let start = std::time::Instant::now();
let results: Vec<_> = initial_conditions
.par_chunks(100) .flat_map(|chunk| {
chunk
.iter()
.map(|&(s0, p0, e0)| run_simulation(dt, s0, p0, e0, vmax, km, kie, system))
.collect::<Vec<_>>()
})
.collect();
let duration = start.elapsed();
println!(
"Total parallel execution time: {:?} for {} simulations in parallel with dt={} and {} equations",
duration,
initial_conditions.len(),
dt,
system.num_equations()
);
for result in results {
if let Err(e) = result {
eprintln!("Error: {e}");
}
}
}