use std::fmt;
use crate::time::{Duration, Unit};
use super::ErrorControl;
use anise::frames::Frame;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
#[derive(Clone, Copy, Debug, TypedBuilder, Serialize, Deserialize, PartialEq)]
#[builder(doc)]
pub struct IntegratorOptions {
#[builder(default_code = "60.0 * Unit::Second")]
pub init_step: Duration,
#[builder(default_code = "0.001 * Unit::Second")]
pub min_step: Duration,
#[builder(default_code = "2700.0 * Unit::Second")]
pub max_step: Duration,
#[builder(default = 1e-12)]
pub tolerance: f64,
#[builder(default = 50)]
pub attempts: u8,
#[builder(default = false)]
pub fixed_step: bool,
#[builder(default)]
pub error_ctrl: ErrorControl,
#[builder(default, setter(strip_option))]
pub integration_frame: Option<Frame>,
}
impl IntegratorOptions {
pub fn with_adaptive_step(
min_step: Duration,
max_step: Duration,
tolerance: f64,
error_ctrl: ErrorControl,
) -> Self {
IntegratorOptions {
init_step: max_step,
min_step,
max_step,
tolerance,
attempts: 50,
fixed_step: false,
error_ctrl,
integration_frame: None,
}
}
pub fn with_adaptive_step_s(
min_step: f64,
max_step: f64,
tolerance: f64,
error_ctrl: ErrorControl,
) -> Self {
Self::with_adaptive_step(
min_step * Unit::Second,
max_step * Unit::Second,
tolerance,
error_ctrl,
)
}
pub fn with_fixed_step(step: Duration) -> Self {
IntegratorOptions {
init_step: step,
min_step: step,
max_step: step,
tolerance: 0.0,
fixed_step: true,
attempts: 0,
error_ctrl: ErrorControl::RSSCartesianStep,
integration_frame: None,
}
}
pub fn with_fixed_step_s(step: f64) -> Self {
Self::with_fixed_step(step * Unit::Second)
}
#[allow(clippy::field_reassign_with_default)]
pub fn with_tolerance(tolerance: f64) -> Self {
let mut opts = Self::default();
opts.tolerance = tolerance;
opts
}
#[allow(clippy::field_reassign_with_default)]
pub fn with_max_step(max_step: Duration) -> Self {
let mut opts = Self::default();
opts.set_max_step(max_step);
opts
}
pub fn info(&self) -> String {
format!("{self}")
}
pub fn set_max_step(&mut self, max_step: Duration) {
if self.init_step > max_step {
self.init_step = max_step;
}
self.max_step = max_step;
}
pub fn set_min_step(&mut self, min_step: Duration) {
if self.init_step < min_step {
self.init_step = min_step;
}
self.min_step = min_step;
}
}
impl fmt::Display for IntegratorOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.fixed_step {
write!(f, "fixed step: {:e}", self.min_step,)
} else {
write!(
f,
"min_step: {:e}, max_step: {:e}, tol: {:e}, attempts: {}",
self.min_step, self.max_step, self.tolerance, self.attempts,
)
}
}
}
impl Default for IntegratorOptions {
fn default() -> IntegratorOptions {
IntegratorOptions {
init_step: 60.0 * Unit::Second,
min_step: 0.001 * Unit::Second,
max_step: 2700.0 * Unit::Second,
tolerance: 1e-12,
attempts: 50,
fixed_step: false,
error_ctrl: ErrorControl::RSSCartesianStep,
integration_frame: None,
}
}
}
#[cfg(test)]
mod ut_integr_opts {
use hifitime::Unit;
use crate::propagators::{ErrorControl, IntegratorOptions};
#[test]
fn test_options() {
let opts = IntegratorOptions::with_fixed_step_s(1e-1);
assert_eq!(opts.min_step, 1e-1 * Unit::Second);
assert_eq!(opts.max_step, 1e-1 * Unit::Second);
assert!(opts.tolerance.abs() < f64::EPSILON);
assert!(opts.fixed_step);
let opts =
IntegratorOptions::with_adaptive_step_s(1e-2, 10.0, 1e-12, ErrorControl::RSSStep);
assert_eq!(opts.min_step, 1e-2 * Unit::Second);
assert_eq!(opts.max_step, 10.0 * Unit::Second);
assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
assert!(!opts.fixed_step);
let opts: IntegratorOptions = Default::default();
assert_eq!(opts.init_step, 60.0 * Unit::Second);
assert_eq!(opts.min_step, 0.001 * Unit::Second);
assert_eq!(opts.max_step, 2700.0 * Unit::Second);
assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
assert_eq!(opts.attempts, 50);
assert!(!opts.fixed_step);
let opts = IntegratorOptions::with_max_step(1.0 * Unit::Second);
assert_eq!(opts.init_step, 1.0 * Unit::Second);
assert_eq!(opts.min_step, 0.001 * Unit::Second);
assert_eq!(opts.max_step, 1.0 * Unit::Second);
assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
assert_eq!(opts.attempts, 50);
assert!(!opts.fixed_step);
}
#[test]
fn test_serde() {
let opts = IntegratorOptions::default();
let serialized = toml::to_string(&opts).unwrap();
println!("{serialized}");
let deserd: IntegratorOptions = toml::from_str(&serialized).unwrap();
assert_eq!(deserd, opts);
}
}