use std::collections::HashMap;
use crate::structs::bindings::Bindings;
#[allow(non_snake_case)]
pub fn runge_kutta_fehlberg_45(
bindings: &Bindings,
step_size: f64,
rtol: f64,
atol: f64,
debug: bool,
prev_step_failed: bool,
) -> Result<(HashMap<String, f64>, f64, f64), String> {
let b21 = 2.500000000000000e-01; let b31 = 9.375000000000000e-02; let b32 = 2.812500000000000e-01; let b41 = 8.793809740555303e-01; let b42 = -3.277196176604461e+00; let b43 = 3.320892125625853e+00; let b51 = 2.032407407407407e+00; let b52 = -8.000000000000000e+00; let b53 = 7.173489278752436e+00; let b54 = -2.058966861598441e-01; let b61 = -2.962962962962963e-01; let b62 = 2.000000000000000e+00; let b63 = -1.381676413255361e+00; let b64 = 4.529727095516569e-01; let b65 = -2.750000000000000e-01;
let r1 = 2.777777777777778e-03; let r3 = -2.994152046783626e-02; let r4 = -2.919989367357789e-02; let r5 = 2.000000000000000e-02; let r6 = 3.636363636363636e-02;
let c1 = 1.157407407407407e-01; let c3 = 5.489278752436647e-01; let c4 = 5.353313840155945e-01; let c5 = -2.000000000000000e-01;
let original_bindings = bindings.values();
let mut k: HashMap<String, Vec<f64>> = HashMap::new();
let k1_assignments = HashMap::<String, f64>::new();
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
k.insert(dependent_variable.clone(), Vec::new());
let ode_value = ODE.evaluate(&k1_assignments, bindings)?;
let k1 = step_size * ode_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k1));
}
drop(k1_assignments);
let mut k2_assignments: HashMap<String, f64> = HashMap::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
if let Some(original_value) = original_bindings.get(dependent_variable) {
k2_assignments.insert(dependent_variable.to_owned(), original_value + k1 * b21);
}
}
for (key, val) in bindings.emulate_assignment_rules(&k2_assignments) {
k2_assignments.insert(key, val);
}
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
let ode_value = ODE.evaluate(&k2_assignments, bindings)?;
let k2 = step_size * ode_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k2));
}
drop(k2_assignments);
let mut k3_assignments = HashMap::<String, f64>::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k2 = k_current[1];
if let Some(original_value) = original_bindings.get(dependent_variable) {
k3_assignments.insert(
dependent_variable.to_owned(),
original_value + k1 * b31 + k2 * b32,
);
}
}
for (key, val) in bindings.emulate_assignment_rules(&k3_assignments) {
k3_assignments.insert(key, val);
}
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
let ode_value = ODE.evaluate(&k3_assignments, bindings)?;
let k3 = step_size * ode_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k3));
}
drop(k3_assignments);
let mut k4_assignments = HashMap::<String, f64>::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k2 = k_current[1];
let k3 = k_current[2];
if let Some(original_value) = original_bindings.get(dependent_variable) {
k4_assignments.insert(
dependent_variable.to_owned(),
original_value + k1 * b41 + k2 * b42 + k3 * b43,
);
}
}
for (key, val) in bindings.emulate_assignment_rules(&k4_assignments) {
k4_assignments.insert(key, val);
}
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
let ode_value = ODE.evaluate(&k4_assignments, bindings)?;
let k4 = step_size * ode_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k4));
}
drop(k4_assignments);
let mut k5_assignments = HashMap::<String, f64>::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k2 = k_current[1];
let k3 = k_current[2];
let k4 = k_current[3];
if let Some(original_value) = original_bindings.get(dependent_variable) {
k5_assignments.insert(
dependent_variable.to_owned(),
original_value + k1 * b51 + k2 * b52 + k3 * b53 + k4 * b54,
);
}
}
for (key, val) in bindings.emulate_assignment_rules(&k5_assignments) {
k5_assignments.insert(key, val);
}
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
let ode_value = ODE.evaluate(&k5_assignments, bindings)?;
let k5 = step_size * ode_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k5));
}
drop(k5_assignments);
let mut k6_assignments = HashMap::<String, f64>::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k2 = k_current[1];
let k3 = k_current[2];
let k4 = k_current[3];
let k5 = k_current[4];
if let Some(original_value) = original_bindings.get(dependent_variable) {
k6_assignments.insert(
dependent_variable.to_owned(),
original_value + k1 * b61 + k2 * b62 + k3 * b63 + k4 * b64 + k5 * b65,
);
}
}
for (key, val) in bindings.emulate_assignment_rules(&k6_assignments) {
k6_assignments.insert(key, val);
}
for ODE in &bindings.ODEs {
let dependent_variable = &ODE.id;
let derivative_value = ODE.evaluate(&k6_assignments, bindings)?;
let k6 = step_size * derivative_value;
k.entry(dependent_variable.clone())
.and_modify(|v| v.push(k6));
}
drop(k6_assignments);
let mut deltas: HashMap<String, f64> = HashMap::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k3 = k_current[2];
let k4 = k_current[3];
let k5 = k_current[4];
let delta = c1 * k1 + c3 * k3 + c4 * k4 + c5 * k5;
deltas.insert(dependent_variable.to_owned(), delta);
}
let mut local_errors: HashMap<String, f64> = HashMap::new();
for (dependent_variable, k_current) in &k {
let k1 = k_current[0];
let k3 = k_current[2];
let k4 = k_current[3];
let k5 = k_current[4];
let k6 = k_current[5];
let local_error = (r1 * k1 + r3 * k3 + r4 * k4 + r5 * k5 + r6 * k6).abs();
local_errors.insert(dependent_variable.clone(), local_error);
}
let mut max_error_factor = 0.0;
for (dependent_variable, delta) in &deltas {
if let Some(original_value) = original_bindings.get(dependent_variable) {
if let Some(local_error) = local_errors.get(dependent_variable) {
let final_value = original_value + delta;
let error_tolerance = rtol * f64::max(*original_value, final_value) + atol;
let error_factor = step_size * local_error / error_tolerance;
if debug {
println!("species_id = {:?}", dependent_variable);
println!("original_value = {:?}", original_value);
println!("final_value = {:?}", final_value);
println!("error_tolerance = {:?}", error_tolerance);
println!("local_error = {:?}", local_error);
println!("error_factor = {:?}", error_factor);
}
max_error_factor = f64::max(max_error_factor, error_factor);
}
}
}
let mut step_change_factor = 0.9 * 0.84 * (max_error_factor).powf(-0.25);
if debug {
println!("max_err_factor = {:?}", max_error_factor);
println!("step_change_factor = {:?}", step_change_factor);
}
if max_error_factor < 1.0 {
if debug {
println!("Error acceptable");
}
if prev_step_failed {
if debug {
println!("Not changing step size");
}
Ok((deltas, step_size, step_size))
} else {
if step_change_factor > 5.0 {
step_change_factor = 5.0;
}
if step_change_factor < 1.0 {
step_change_factor = 1.0;
}
if debug {
println!(
"Increasing step size by a factor of {} to {}",
step_change_factor,
step_size * step_change_factor
);
}
Ok((deltas, step_size, step_size * step_change_factor))
}
} else {
if debug {
println!("Current step failed, reducing step size");
}
if step_change_factor < 0.1 {
step_change_factor = 0.1;
}
if step_change_factor > 1.0 {
step_change_factor = 1.0;
}
if debug {
println!(
"Calling rkf45 again with step_size = {}",
step_size * step_change_factor
);
}
runge_kutta_fehlberg_45(
bindings,
step_size * step_change_factor,
rtol,
atol,
debug,
true,
)
}
}