nyx_space/propagators/
options.rs1use std::fmt;
20
21use crate::time::{Duration, Unit};
22
23use super::ErrorControl;
24use anise::frames::Frame;
25use serde::{Deserialize, Serialize};
26use typed_builder::TypedBuilder;
27
28#[derive(Clone, Copy, Debug, TypedBuilder, Serialize, Deserialize, PartialEq)]
35#[builder(doc)]
36pub struct IntegratorOptions {
37 #[builder(default_code = "60.0 * Unit::Second")]
38 pub init_step: Duration,
39 #[builder(default_code = "0.001 * Unit::Second")]
40 pub min_step: Duration,
41 #[builder(default_code = "2700.0 * Unit::Second")]
42 pub max_step: Duration,
43 #[builder(default = 1e-12)]
44 pub tolerance: f64,
45 #[builder(default = 50)]
46 pub attempts: u8,
47 #[builder(default = false)]
48 pub fixed_step: bool,
49 #[builder(default)]
50 pub error_ctrl: ErrorControl,
51 #[builder(default, setter(strip_option))]
54 pub integration_frame: Option<Frame>,
55}
56
57impl IntegratorOptions {
58 pub fn with_adaptive_step(
61 min_step: Duration,
62 max_step: Duration,
63 tolerance: f64,
64 error_ctrl: ErrorControl,
65 ) -> Self {
66 IntegratorOptions {
67 init_step: max_step,
68 min_step,
69 max_step,
70 tolerance,
71 attempts: 50,
72 fixed_step: false,
73 error_ctrl,
74 integration_frame: None,
75 }
76 }
77
78 pub fn with_adaptive_step_s(
79 min_step: f64,
80 max_step: f64,
81 tolerance: f64,
82 error_ctrl: ErrorControl,
83 ) -> Self {
84 Self::with_adaptive_step(
85 min_step * Unit::Second,
86 max_step * Unit::Second,
87 tolerance,
88 error_ctrl,
89 )
90 }
91
92 pub fn with_fixed_step(step: Duration) -> Self {
95 IntegratorOptions {
96 init_step: step,
97 min_step: step,
98 max_step: step,
99 tolerance: 0.0,
100 fixed_step: true,
101 attempts: 0,
102 error_ctrl: ErrorControl::RSSCartesianStep,
103 integration_frame: None,
104 }
105 }
106
107 pub fn with_fixed_step_s(step: f64) -> Self {
108 Self::with_fixed_step(step * Unit::Second)
109 }
110
111 #[allow(clippy::field_reassign_with_default)]
113 pub fn with_tolerance(tolerance: f64) -> Self {
114 let mut opts = Self::default();
115 opts.tolerance = tolerance;
116 opts
117 }
118
119 #[allow(clippy::field_reassign_with_default)]
121 pub fn with_max_step(max_step: Duration) -> Self {
122 let mut opts = Self::default();
123 opts.set_max_step(max_step);
124 opts
125 }
126
127 pub fn info(&self) -> String {
129 format!("{self}")
130 }
131
132 pub fn set_max_step(&mut self, max_step: Duration) {
134 if self.init_step > max_step {
135 self.init_step = max_step;
136 }
137 self.max_step = max_step;
138 }
139
140 pub fn set_min_step(&mut self, min_step: Duration) {
142 if self.init_step < min_step {
143 self.init_step = min_step;
144 }
145 self.min_step = min_step;
146 }
147}
148
149impl fmt::Display for IntegratorOptions {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 if self.fixed_step {
152 write!(f, "fixed step: {:e}", self.min_step,)
153 } else {
154 write!(
155 f,
156 "min_step: {:e}, max_step: {:e}, tol: {:e}, attempts: {}",
157 self.min_step, self.max_step, self.tolerance, self.attempts,
158 )
159 }
160 }
161}
162
163impl Default for IntegratorOptions {
164 fn default() -> IntegratorOptions {
166 IntegratorOptions {
167 init_step: 60.0 * Unit::Second,
168 min_step: 0.001 * Unit::Second,
169 max_step: 2700.0 * Unit::Second,
170 tolerance: 1e-12,
171 attempts: 50,
172 fixed_step: false,
173 error_ctrl: ErrorControl::RSSCartesianStep,
174 integration_frame: None,
175 }
176 }
177}
178
179#[cfg(test)]
180mod ut_integr_opts {
181 use hifitime::Unit;
182
183 use crate::propagators::{ErrorControl, IntegratorOptions};
184
185 #[test]
186 fn test_options() {
187 let opts = IntegratorOptions::with_fixed_step_s(1e-1);
188 assert_eq!(opts.min_step, 1e-1 * Unit::Second);
189 assert_eq!(opts.max_step, 1e-1 * Unit::Second);
190 assert!(opts.tolerance.abs() < f64::EPSILON);
191 assert!(opts.fixed_step);
192
193 let opts =
194 IntegratorOptions::with_adaptive_step_s(1e-2, 10.0, 1e-12, ErrorControl::RSSStep);
195 assert_eq!(opts.min_step, 1e-2 * Unit::Second);
196 assert_eq!(opts.max_step, 10.0 * Unit::Second);
197 assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
198 assert!(!opts.fixed_step);
199
200 let opts: IntegratorOptions = Default::default();
201 assert_eq!(opts.init_step, 60.0 * Unit::Second);
202 assert_eq!(opts.min_step, 0.001 * Unit::Second);
203 assert_eq!(opts.max_step, 2700.0 * Unit::Second);
204 assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
205 assert_eq!(opts.attempts, 50);
206 assert!(!opts.fixed_step);
207
208 let opts = IntegratorOptions::with_max_step(1.0 * Unit::Second);
209 assert_eq!(opts.init_step, 1.0 * Unit::Second);
210 assert_eq!(opts.min_step, 0.001 * Unit::Second);
211 assert_eq!(opts.max_step, 1.0 * Unit::Second);
212 assert!((opts.tolerance - 1e-12).abs() < f64::EPSILON);
213 assert_eq!(opts.attempts, 50);
214 assert!(!opts.fixed_step);
215 }
216
217 #[test]
218 fn test_serde() {
219 let opts = IntegratorOptions::default();
220 let serialized = toml::to_string(&opts).unwrap();
221 println!("{serialized}");
222 let deserd: IntegratorOptions = toml::from_str(&serialized).unwrap();
223 assert_eq!(deserd, opts);
224 }
225}