use crate::IntegrateFloat;
use scirs2_core::ndarray::{Array1, ArrayView1};
#[allow(dead_code)]
pub fn error_norm<F: IntegrateFloat>(error: &Array1<F>, y: &Array1<F>, rtol: F, atol: F) -> F {
let scale = y
.iter()
.zip(error.iter())
.map(|(y_i_, _)| rtol * y_i_.abs() + atol)
.collect::<Array1<F>>();
let mut sum_sq = F::zero();
for (e, s) in error.iter().zip(scale.iter()) {
sum_sq += (*e / *s).powi(2);
}
let n = F::from_usize(error.len()).expect("Operation failed");
(sum_sq / n).sqrt()
}
#[allow(dead_code)]
pub fn calculate_new_step_size<F: IntegrateFloat>(
h_current: F,
error_norm: F,
error_order: usize,
safety: F,
) -> F {
if error_norm == F::zero() {
return h_current * F::from_f64(10.0).expect("Operation failed");
}
let _order = F::from_usize(error_order).expect("Operation failed");
let error_ratio = F::one() / error_norm;
let factor = safety * error_ratio.powf(F::one() / _order);
let factor_max = F::from_f64(10.0).expect("Operation failed");
let factor_min = F::from_f64(0.1).expect("Operation failed");
let factor = if factor > factor_max {
factor_max
} else if factor < factor_min {
factor_min
} else {
factor
};
h_current * factor
}
#[allow(dead_code)]
pub fn select_initial_step<F, Func>(
f: &Func,
t: F,
y: &Array1<F>,
direction: F,
rtol: F,
atol: F,
) -> F
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>) -> Array1<F>,
{
let scale = y
.iter()
.map(|y_i| rtol * y_i.abs() + atol)
.collect::<Array1<F>>();
let f0 = f(t, y.view());
let d0 = f0
.iter()
.zip(scale.iter())
.map(|(f, s)| *f / *s)
.fold(F::zero(), |acc, x| acc + x * x);
let d0 = d0.sqrt()
/ F::from_f64(y.len() as f64)
.expect("Operation failed")
.sqrt();
let step_size = if d0 < F::from_f64(1.0e-5).expect("Operation failed") {
F::from_f64(1.0e-6).expect("Operation failed")
} else {
F::from_f64(0.01).expect("Operation failed") / d0
};
step_size * direction.signum()
}