use serde::{Deserialize, Serialize};
use crate::integrators::IntegratorConfig;
use crate::math::interpolation::InterpolationMethod;
use crate::math::jacobian::DifferenceMethod;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum IntegratorMethod {
RK4,
RKF45,
#[default]
DP54,
RKN1210,
}
impl IntegratorMethod {
pub fn is_adaptive(&self) -> bool {
!matches!(self, IntegratorMethod::RK4)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum TrajectoryMode {
#[default]
OutputStepsOnly,
AllSteps,
Disabled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VariationalConfig {
pub enable_stm: bool,
pub enable_sensitivity: bool,
pub store_stm_history: bool,
pub store_sensitivity_history: bool,
pub jacobian_method: DifferenceMethod,
pub sensitivity_method: DifferenceMethod,
}
impl Default for VariationalConfig {
fn default() -> Self {
Self {
enable_stm: false,
enable_sensitivity: false,
store_stm_history: false,
store_sensitivity_history: false,
jacobian_method: DifferenceMethod::Central,
sensitivity_method: DifferenceMethod::Central,
}
}
}
impl VariationalConfig {
pub fn new(
enable_stm: bool,
enable_sensitivity: bool,
store_stm_history: bool,
store_sensitivity_history: bool,
jacobian_method: DifferenceMethod,
sensitivity_method: DifferenceMethod,
) -> Self {
Self {
enable_stm,
enable_sensitivity,
store_stm_history,
store_sensitivity_history,
jacobian_method,
sensitivity_method,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NumericalPropagationConfig {
pub method: IntegratorMethod,
pub integrator: IntegratorConfig,
pub variational: VariationalConfig,
pub store_accelerations: bool,
pub interpolation_method: InterpolationMethod,
}
impl Default for NumericalPropagationConfig {
fn default() -> Self {
Self {
method: IntegratorMethod::default(),
integrator: IntegratorConfig::default(),
variational: VariationalConfig::default(),
store_accelerations: true,
interpolation_method: InterpolationMethod::Linear,
}
}
}
impl NumericalPropagationConfig {
pub fn with_method(method: IntegratorMethod) -> Self {
Self {
method,
..Default::default()
}
}
pub fn new(
method: IntegratorMethod,
integrator: IntegratorConfig,
variational: VariationalConfig,
) -> Self {
Self {
method,
integrator,
variational,
store_accelerations: true,
interpolation_method: InterpolationMethod::Linear,
}
}
pub fn high_precision() -> Self {
Self {
method: IntegratorMethod::RKN1210,
integrator: IntegratorConfig::adaptive(1e-10, 1e-8),
variational: VariationalConfig::default(),
store_accelerations: true,
interpolation_method: InterpolationMethod::Linear,
}
}
pub fn with_store_accelerations(mut self, store: bool) -> Self {
self.store_accelerations = store;
self
}
pub fn with_interpolation_method(mut self, method: InterpolationMethod) -> Self {
self.interpolation_method = method;
self
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn test_integrator_method_default() {
let method = IntegratorMethod::default();
assert_eq!(method, IntegratorMethod::DP54);
}
#[test]
fn test_integrator_method_is_adaptive() {
assert!(!IntegratorMethod::RK4.is_adaptive());
assert!(IntegratorMethod::RKF45.is_adaptive());
assert!(IntegratorMethod::DP54.is_adaptive());
assert!(IntegratorMethod::RKN1210.is_adaptive());
}
#[test]
fn test_numerical_propagation_config_default() {
let config = NumericalPropagationConfig::default();
assert_eq!(config.method, IntegratorMethod::DP54);
assert_eq!(
config.variational.jacobian_method,
DifferenceMethod::Central
);
assert_eq!(
config.variational.sensitivity_method,
DifferenceMethod::Central
);
}
#[test]
fn test_numerical_propagation_config_with_method() {
let config = NumericalPropagationConfig::with_method(IntegratorMethod::RKF45);
assert_eq!(config.method, IntegratorMethod::RKF45);
}
#[test]
fn test_numerical_propagation_config_high_precision() {
let config = NumericalPropagationConfig::high_precision();
assert_eq!(config.method, IntegratorMethod::RKN1210);
assert!(config.integrator.abs_tol <= 1e-9);
assert!(config.integrator.rel_tol <= 1e-7);
}
#[test]
fn test_variational_config_default() {
let config = VariationalConfig::default();
assert!(!config.enable_stm);
assert!(!config.enable_sensitivity);
assert!(!config.store_stm_history);
assert!(!config.store_sensitivity_history);
assert_eq!(config.jacobian_method, DifferenceMethod::Central);
assert_eq!(config.sensitivity_method, DifferenceMethod::Central);
}
#[test]
fn test_variational_config_new() {
let config = VariationalConfig::new(
true,
true,
true,
true,
DifferenceMethod::Forward,
DifferenceMethod::Backward,
);
assert!(config.enable_stm);
assert!(config.enable_sensitivity);
assert!(config.store_stm_history);
assert!(config.store_sensitivity_history);
assert_eq!(config.jacobian_method, DifferenceMethod::Forward);
assert_eq!(config.sensitivity_method, DifferenceMethod::Backward);
}
#[test]
fn test_variational_config_partial() {
let config = VariationalConfig::new(
true,
false,
true,
false,
DifferenceMethod::Central,
DifferenceMethod::Central,
);
assert!(config.enable_stm);
assert!(!config.enable_sensitivity);
assert!(config.store_stm_history);
assert!(!config.store_sensitivity_history);
assert_eq!(config.jacobian_method, DifferenceMethod::Central);
assert_eq!(config.sensitivity_method, DifferenceMethod::Central);
}
#[test]
fn test_numerical_propagation_config_default_variational() {
let config = NumericalPropagationConfig::default();
assert!(!config.variational.enable_stm);
assert!(!config.variational.enable_sensitivity);
}
#[test]
fn test_numerical_propagation_config_new() {
let config = NumericalPropagationConfig::new(
IntegratorMethod::RKN1210,
IntegratorConfig::adaptive(1e-12, 1e-10),
VariationalConfig {
enable_stm: true,
..Default::default()
},
);
assert_eq!(config.method, IntegratorMethod::RKN1210);
assert_eq!(config.integrator.abs_tol, 1e-12);
assert_eq!(config.integrator.rel_tol, 1e-10);
assert!(config.variational.enable_stm);
assert!(!config.variational.enable_sensitivity);
assert!(config.store_accelerations);
assert_eq!(config.interpolation_method, InterpolationMethod::Linear);
}
#[test]
fn test_numerical_propagation_config_default_accelerations() {
let config = NumericalPropagationConfig::default();
assert!(config.store_accelerations);
assert_eq!(config.interpolation_method, InterpolationMethod::Linear);
}
#[test]
fn test_numerical_propagation_config_with_store_accelerations() {
let config = NumericalPropagationConfig::default().with_store_accelerations(false);
assert!(!config.store_accelerations);
let config = NumericalPropagationConfig::default().with_store_accelerations(true);
assert!(config.store_accelerations);
}
#[test]
fn test_numerical_propagation_config_with_interpolation_method() {
let config = NumericalPropagationConfig::default()
.with_interpolation_method(InterpolationMethod::Lagrange { degree: 5 });
assert_eq!(
config.interpolation_method,
InterpolationMethod::Lagrange { degree: 5 }
);
let config = NumericalPropagationConfig::default()
.with_interpolation_method(InterpolationMethod::HermiteCubic);
assert_eq!(
config.interpolation_method,
InterpolationMethod::HermiteCubic
);
let config = NumericalPropagationConfig::default()
.with_interpolation_method(InterpolationMethod::HermiteQuintic);
assert_eq!(
config.interpolation_method,
InterpolationMethod::HermiteQuintic
);
}
#[test]
fn test_numerical_propagation_config_builder_chaining() {
let config = NumericalPropagationConfig::default()
.with_store_accelerations(false)
.with_interpolation_method(InterpolationMethod::Lagrange { degree: 7 });
assert!(!config.store_accelerations);
assert_eq!(
config.interpolation_method,
InterpolationMethod::Lagrange { degree: 7 }
);
}
#[test]
fn test_numerical_propagation_config_high_precision_new_fields() {
let config = NumericalPropagationConfig::high_precision();
assert!(config.store_accelerations);
assert_eq!(config.interpolation_method, InterpolationMethod::Linear);
}
}