1use nalgebra::SVector;
2use roots::{find_root_brent, SimpleConvergency};
3
4use super::callback::Callback;
5use super::controller::{Controller, PIController, TryStep};
6use super::integrator::Integrator;
7use super::ode::ODE;
8
9#[derive(Clone)]
10pub struct Problem<'a, const D: usize, S, P>
11where
12 S: Integrator<D>,
13{
14 ode: ODE<'a, D, P>,
15 integrator: S,
16 controller: PIController,
17 callbacks: Vec<Callback<'a, D, P>>,
18}
19
20impl<'a, const D: usize, S, P> Problem<'a, D, S, P>
21where
22 S: Integrator<D> + Copy,
23{
24 pub fn new(ode: ODE<'a, D, P>, integrator: S, controller: PIController) -> Self {
25 Problem {
26 ode,
27 integrator,
28 controller,
29 callbacks: Vec::new(),
30 }
31 }
32 pub fn solve(&mut self) -> Solution<S, D> {
33 let mut convergency = SimpleConvergency {
34 eps: 1e-12,
35 max_iter: 1000,
36 };
37 let mut times: Vec<f64> = vec![self.ode.t];
38 let mut states: Vec<SVector<f64, D>> = vec![self.ode.y];
39 let mut dense_coefficients: Vec<Vec<SVector<f64, D>>> = Vec::new();
40 while self.ode.t < self.ode.t_end {
41 if self.ode.t + self.controller.next_step_guess.extract() > self.ode.t_end {
42 self.controller.next_step_guess = TryStep::NotYetAccepted(
44 self.ode.t_end - self.ode.t,
45 );
46 }
47 let (mut new_y, mut curr_step, mut dense_option) = if S::ADAPTIVE {
48 let (mut trial_y, mut err_option, mut dense_option) =
50 self.integrator.step(&self.ode, self.controller.next_step_guess.extract());
51 let mut err = err_option.unwrap();
52 let initial_guess = self.controller.next_step_guess.extract();
55 let mut next_step_guess = <PIController as Controller<D>>::determine_step(
56 &mut self.controller,
57 initial_guess,
58 err,
59 );
60 while !next_step_guess.is_accepted() {
61 (trial_y, err_option, dense_option) =
63 self.integrator.step(&self.ode, next_step_guess.extract());
64 next_step_guess = <PIController as Controller<D>>::determine_step(
65 &mut self.controller,
66 next_step_guess.extract(),
67 err,
68 );
69 err = err_option.unwrap();
70 }
71 self.controller.next_step_guess = next_step_guess.reset().unwrap();
73 (trial_y, next_step_guess.extract(), dense_option)
74 } else {
75 let (trial_y, _, dense_option) = self.integrator.step(&self.ode, self.controller.next_step_guess.extract());
77 (trial_y, self.controller.next_step_guess.extract(), dense_option)
78 };
79 if !self.callbacks.is_empty() {
80 for callback in &self.callbacks {
82 if (callback.event)(self.ode.t, self.ode.y, &self.ode.params)
83 * (callback.event)(self.ode.t + curr_step, new_y, &self.ode.params)
84 < 0.0
85 {
86 let f = |test_t| {
88 let test_y = self.integrator.step(&self.ode, test_t).0;
89 (callback.event)(self.ode.t + test_t, test_y, &self.ode.params)
90 };
91 let root = find_root_brent(0.0, curr_step, &f, &mut convergency).unwrap();
92 curr_step = root;
93 (new_y, _, dense_option) = self.integrator.step(&self.ode, curr_step);
94 (callback.effect)(&mut self.ode);
95 }
96 }
97 }
98 self.ode.y = new_y;
99 self.ode.t += curr_step;
100 times.push(self.ode.t);
101 states.push(self.ode.y);
102 dense_coefficients.push(dense_option.unwrap());
104 }
105 Solution {
106 integrator: self.integrator,
107 times,
108 states,
109 dense: dense_coefficients,
110 }
111 }
112
113 pub fn with_callback(mut self, callback: Callback<'a, D, P>) -> Self {
114 self.callbacks.push(callback);
115 Self {
116 ode: self.ode,
117 integrator: self.integrator,
118 controller: self.controller,
119 callbacks: self.callbacks,
120 }
121 }
122}
123
124pub struct Solution<S, const D: usize>
125where
126 S: Integrator<D>,
127{
128 pub integrator: S,
129 pub times: Vec<f64>,
130 pub states: Vec<SVector<f64, D>>,
131 pub dense: Vec<Vec<SVector<f64, D>>>,
132}
133
134impl<S, const D: usize> Solution<S, D>
135where
136 S: Integrator<D>,
137{
138 pub fn interpolate(&self, t: f64) -> SVector<f64, D> {
139 let last = self.times.last().unwrap();
141 let first = self.times.first().unwrap();
142
143 let mut times = self.times.clone();
145 if *first > *last {
146 times.reverse();
147 }
148 if t < *first || t > *last {
149 panic!();
150 }
151
152 match times.binary_search_by(|x| x.total_cmp(&t)) {
154 Ok(index) => self.states[index],
155 Err(end_index) => {
156 let t_start = times[end_index - 1];
158 let t_end = times[end_index];
159 self.integrator
160 .interpolate(t_start, t_end, &self.dense[end_index - 1], t)
161 }
162 }
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use crate::callback::stop;
170 use crate::controller::PIController;
171 use crate::integrator::dormand_prince::DormandPrince45;
172 use approx::assert_relative_eq;
173 use nalgebra::Vector3;
174
175 #[test]
176 fn test_problem() {
177 type Params = ();
178 fn derivative(_t: f64, y: Vector3<f64>, _p: &Params) -> Vector3<f64> {
179 y
180 }
181 let y0 = Vector3::new(1.0, 1.0, 1.0);
182
183 let ode = ODE::new(&derivative, 0.0, 1.0, y0, ());
184 let dp45 = DormandPrince45::new().a_tol(1e-12).r_tol(1e-5);
185 let controller = PIController::default();
186
187 let mut problem = Problem::new(ode, dp45, controller);
188
189 let solution = problem.solve();
190 solution
191 .times
192 .iter()
193 .zip(solution.states.iter())
194 .for_each(|(time, state)| {
195 assert_relative_eq!(state[0], time.exp(), max_relative = 1e-2);
196 })
197 }
198
199 #[test]
200 fn test_with_callback() {
201 type Params = ();
202 fn derivative(_t: f64, y: Vector3<f64>, _p: &Params) -> Vector3<f64> {
203 y
204 }
205 let y0 = Vector3::new(1.0, 1.0, 1.0);
206
207 let ode = ODE::new(&derivative, 0.0, 10.0, y0, ());
208 let dp45 = DormandPrince45::new().a_tol(1e-12).r_tol(1e-5);
209 let controller = PIController::default();
210
211 let value_too_high = Callback {
212 event: &|_: f64, y: SVector<f64, 3>, _: &Params| 10.0 - y[0],
213 effect: &stop,
214 };
215
216 let mut problem = Problem::new(ode, dp45, controller).with_callback(value_too_high);
217 let solution = problem.solve();
218
219 assert_relative_eq!(
220 solution.states.last().unwrap()[0],
221 10.0,
222 max_relative = 1e-11
223 );
224 }
225
226 #[test]
227 fn test_with_interpolation() {
228 type Params = ();
229 fn derivative(_t: f64, y: Vector3<f64>, _p: &Params) -> Vector3<f64> {
230 y
231 }
232 let y0 = Vector3::new(1.0, 1.0, 1.0);
233
234 let ode = ODE::new(&derivative, 0.0, 10.0, y0, ());
235 let dp45 = DormandPrince45::new().a_tol(1e-12).r_tol(1e-6);
236 let controller = PIController::default();
237
238 let mut problem = Problem::new(ode, dp45, controller);
239 let solution = problem.solve();
240
241 assert_relative_eq!(
242 solution.interpolate(8.8)[0],
243 8.8_f64.exp(),
244 max_relative = 1e-6
245 );
246 }
247}