mod rk;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use serde_dhall::StaticType;
use crate::io::ConfigError;
use self::rk::*;
mod dormand;
use self::dormand::*;
mod verner;
use self::verner::*;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use super::PropagationError;
#[allow(clippy::upper_case_acronyms)]
trait RK
where
Self: Sized,
{
const ORDER: u8;
const STAGES: usize;
const A_COEFFS: &'static [f64];
const B_COEFFS: &'static [f64];
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, StaticType)]
#[cfg_attr(feature = "python", pyclass(from_py_object))]
pub enum IntegratorMethod {
#[default]
RungeKutta89,
DormandPrince78,
DormandPrince45,
RungeKutta4,
CashKarp45,
Verner56,
}
impl IntegratorMethod {
pub const fn order(self) -> u8 {
match self {
Self::RungeKutta89 => RK89::ORDER,
Self::DormandPrince78 => Dormand78::ORDER,
Self::DormandPrince45 => Dormand45::ORDER,
Self::RungeKutta4 => RK4Fixed::ORDER,
Self::CashKarp45 => CashKarp45::ORDER,
Self::Verner56 => Verner56::ORDER,
}
}
pub const fn stages(self) -> usize {
match self {
Self::RungeKutta89 => RK89::STAGES,
Self::DormandPrince78 => Dormand78::STAGES,
Self::DormandPrince45 => Dormand45::STAGES,
Self::RungeKutta4 => RK4Fixed::STAGES,
Self::CashKarp45 => CashKarp45::STAGES,
Self::Verner56 => Verner56::STAGES,
}
}
pub const fn a_coeffs(self) -> &'static [f64] {
match self {
Self::RungeKutta89 => RK89::A_COEFFS,
Self::DormandPrince78 => Dormand78::A_COEFFS,
Self::DormandPrince45 => Dormand45::A_COEFFS,
Self::RungeKutta4 => RK4Fixed::A_COEFFS,
Self::CashKarp45 => CashKarp45::A_COEFFS,
Self::Verner56 => Verner56::A_COEFFS,
}
}
pub const fn b_coeffs(self) -> &'static [f64] {
match self {
Self::RungeKutta89 => RK89::B_COEFFS,
Self::DormandPrince78 => Dormand78::B_COEFFS,
Self::DormandPrince45 => Dormand45::B_COEFFS,
Self::RungeKutta4 => RK4Fixed::B_COEFFS,
Self::CashKarp45 => CashKarp45::B_COEFFS,
Self::Verner56 => Verner56::B_COEFFS,
}
}
}
impl FromStr for IntegratorMethod {
type Err = PropagationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"rungekutta89" => Ok(Self::RungeKutta89),
"dormandprince78" => Ok(Self::DormandPrince78),
"dormandprince45" => Ok(Self::DormandPrince45),
"rungekutta4" => Ok(Self::RungeKutta4),
"cashkarp45" => Ok(Self::CashKarp45),
"verner56" => Ok(Self::Verner56),
_ => {
let valid = [
"RungeKutta89",
"DormandPrince78",
"DormandPrince45",
"RungeKutta4",
"CashKarp45",
"Verner56",
];
let valid_msg = valid.join(",");
Err(PropagationError::PropConfigError {
source: ConfigError::InvalidConfig {
msg: format!("unknow integration method `{s}`, must be one of {valid_msg}"),
},
})
}
}
}
}
#[cfg(test)]
mod ut_propagator {
use std::str::FromStr;
use super::IntegratorMethod;
#[test]
fn from_str_ok() {
let valid = [
"RungeKutta89",
"DormandPrince78",
"DormandPrince45",
"RungeKutta4",
"CashKarp45",
"Verner56",
];
for method in valid {
assert!(IntegratorMethod::from_str(method.to_uppercase().as_str()).is_ok());
}
assert!(IntegratorMethod::from_str("blah").is_err());
}
}