mod flat;
mod linear;
mod smith_wilson;
pub use flat::FlatExtrapolator;
pub use linear::LinearExtrapolator;
pub use smith_wilson::SmithWilson;
pub trait Extrapolator: Send + Sync {
fn extrapolate(&self, t: f64, last_t: f64, last_value: f64, last_derivative: f64) -> f64;
fn name(&self) -> &'static str;
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ExtrapolationMethod {
None,
#[default]
Flat,
Linear,
SmithWilson {
ufr: f64,
alpha: f64,
},
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_flat_extrapolator() {
let extrap = FlatExtrapolator;
let last_t = 10.0;
let last_value = 0.05;
let last_deriv = 0.001;
let value = extrap.extrapolate(15.0, last_t, last_value, last_deriv);
assert_relative_eq!(value, last_value, epsilon = 1e-10);
let value = extrap.extrapolate(100.0, last_t, last_value, last_deriv);
assert_relative_eq!(value, last_value, epsilon = 1e-10);
}
#[test]
fn test_linear_extrapolator() {
let extrap = LinearExtrapolator;
let last_t = 10.0;
let last_value = 0.05;
let last_deriv = 0.001;
let value = extrap.extrapolate(15.0, last_t, last_value, last_deriv);
let expected = last_value + last_deriv * (15.0 - last_t);
assert_relative_eq!(value, expected, epsilon = 1e-10);
}
#[test]
fn test_smith_wilson_convergence() {
let sw = SmithWilson::new(0.042, 0.1, 20.0);
let last_t = 20.0;
let last_value = 0.035;
let last_deriv = 0.0005;
let value_30 = sw.extrapolate(30.0, last_t, last_value, last_deriv);
let value_60 = sw.extrapolate(60.0, last_t, last_value, last_deriv);
let value_100 = sw.extrapolate(100.0, last_t, last_value, last_deriv);
assert!(value_30 > last_value); assert!(value_60 > value_30);
assert!((value_100 - 0.042).abs() < (value_60 - 0.042).abs());
}
}