use crate::structs::bindings::Bindings;
use crate::structs::methods::Methods;
use super::runge_kutta::runge_kutta_4;
use super::runge_kutta_fehlberg::runge_kutta_fehlberg_45;
use sbml_rs::Model;
use std::collections::HashMap;
use std::io::stdin;
pub fn integrate(
model: &Model,
duration: f64,
steps: i32,
init_step_size: f64,
method: Methods,
rtol: f64,
atol: f64,
print_amounts: bool,
DEBUG: bool,
) -> Result<Vec<HashMap<String, f64>>, String> {
let mut results = Vec::new();
let mut bindings = Bindings::from(&model);
let mut initial_results = bindings.results(print_amounts);
initial_results.insert("t".to_string(), 0.0);
results.push(initial_results);
let mut t = 0.0;
let result_interval = duration / (steps as f64);
let mut t_next_result = ((t + result_interval) * 1000000.0).round() / 1000000.0;
let mut current_step_size = init_step_size;
let mut next_step_size = init_step_size;
if method == Methods::RK4 {
current_step_size = current_step_size / 4096.0;
next_step_size = next_step_size / 4096.0;
}
let mut deltas;
let mut cached_step_size = None;
while duration - t > f64::EPSILON {
if DEBUG {
println!();
println!("Integrating from {} to {}", t, t + current_step_size);
println!("Calling rkf45 with dt = {}", current_step_size);
}
match method {
Methods::RKF45 => {
let (a, b, c) = runge_kutta_fehlberg_45(
&bindings,
current_step_size,
rtol,
atol,
DEBUG,
false,
)?;
deltas = a;
current_step_size = b;
next_step_size = c;
if DEBUG {
println!("Integrated from t = {} to {}", t, t + ¤t_step_size);
}
if next_step_size > current_step_size {
if let Some(cached_step_size_value) = cached_step_size {
if next_step_size < cached_step_size_value {
next_step_size = cached_step_size_value;
if DEBUG {
println!(
"Will use cached step size of {} for next step",
next_step_size
);
}
}
cached_step_size = None;
}
}
}
Methods::RK4 => {
deltas = runge_kutta_4(&bindings, current_step_size)?;
if DEBUG {
println!("Integrated from t = {} to {}", t, t + ¤t_step_size);
}
}
}
for (key, val) in deltas.iter() {
bindings.update_delta(key, *val);
}
bindings.evaluate_assignment_rules();
if t_next_result - (t + current_step_size) < f64::EPSILON {
if DEBUG {
println!("Reached t = {}, storing results", t + current_step_size);
}
t_next_result += result_interval;
if t_next_result > duration {
t_next_result = duration;
}
t_next_result = (t_next_result * 1000000.0).round() / 1000000.0;
let mut iteration_result = bindings.results(print_amounts);
iteration_result.insert("t".to_string(), t + current_step_size);
results.push(iteration_result);
}
t += current_step_size;
if method == Methods::RKF45 {
if (t + next_step_size) - t_next_result >= f64::EPSILON {
cached_step_size = Some(next_step_size);
next_step_size = t_next_result - t;
}
current_step_size = next_step_size;
if DEBUG {
println!(
"Next step will be from t = {} to {} with step size {}",
t,
t + current_step_size,
current_step_size
);
}
}
if DEBUG {
println!("Press return to continue.");
let mut input_string = String::new();
stdin()
.read_line(&mut input_string)
.ok()
.expect("Failed to read line");
}
}
Ok(results)
}