conspire/math/integrate/bogacki_shampine/
mod.rs1#[cfg(test)]
2mod test;
3
4use super::{
5 super::{Tensor, TensorRank0, TensorVec, Vector, interpolate::InterpolateSolution},
6 Explicit, IntegrationError,
7};
8use crate::{ABS_TOL, REL_TOL};
9use std::ops::{Mul, Sub};
10
11#[derive(Debug)]
43pub struct BogackiShampine {
44 pub abs_tol: TensorRank0,
46 pub rel_tol: TensorRank0,
48 pub dt_beta: TensorRank0,
50 pub dt_expn: TensorRank0,
52 pub dt_init: TensorRank0,
54}
55
56impl Default for BogackiShampine {
57 fn default() -> Self {
58 Self {
59 abs_tol: ABS_TOL,
60 rel_tol: REL_TOL,
61 dt_beta: 0.9,
62 dt_expn: 3.0,
63 dt_init: 0.1,
64 }
65 }
66}
67
68impl<Y, U> Explicit<Y, U> for BogackiShampine
69where
70 Self: InterpolateSolution<Y, U>,
71 Y: Tensor,
72 for<'a> &'a Y: Mul<TensorRank0, Output = Y> + Sub<&'a Y, Output = Y>,
73 U: TensorVec<Item = Y>,
74{
75 fn integrate(
76 &self,
77 function: impl Fn(TensorRank0, &Y) -> Result<Y, IntegrationError>,
78 time: &[TensorRank0],
79 initial_condition: Y,
80 ) -> Result<(Vector, U, U), IntegrationError> {
81 if time.len() < 2 {
82 return Err(IntegrationError::LengthTimeLessThanTwo);
83 } else if time[0] >= time[time.len() - 1] {
84 return Err(IntegrationError::InitialTimeNotLessThanFinalTime);
85 }
86 let mut t = time[0];
87 let mut dt = self.dt_init * time[time.len() - 1];
88 let mut e;
89 let mut k_1 = function(t, &initial_condition)?;
90 let mut k_2;
91 let mut k_3;
92 let mut k_4;
93 let mut t_sol = Vector::zero(0);
94 t_sol.push(time[0]);
95 let mut y = initial_condition.clone();
96 let mut y_sol = U::zero(0);
97 y_sol.push(initial_condition.clone());
98 let mut dydt_sol = U::zero(0);
99 dydt_sol.push(k_1.clone());
100 let mut y_trial;
101 while t < time[time.len() - 1] {
102 k_2 = function(t + 0.5 * dt, &(&k_1 * (0.5 * dt) + &y))?;
103 k_3 = function(t + 0.75 * dt, &(&k_2 * (0.75 * dt) + &y))?;
104 y_trial = (&k_1 * 2.0 + &k_2 * 3.0 + &k_3 * 4.0) * (dt / 9.0) + &y;
105 k_4 = function(t + dt, &y_trial)?;
106 e = ((&k_1 * -5.0 + k_2 * 6.0 + k_3 * 8.0 + &k_4 * -9.0) * (dt / 72.0)).norm_inf();
107 if e < self.abs_tol || e / y_trial.norm_inf() < self.rel_tol {
108 k_1 = k_4;
109 t += dt;
110 y = y_trial;
111 t_sol.push(t);
112 y_sol.push(y.clone());
113 dydt_sol.push(k_1.clone());
114 }
115 dt *= self.dt_beta * (self.abs_tol / e).powf(1.0 / self.dt_expn);
116 }
117 if time.len() > 2 {
118 let t_int = Vector::new(time);
119 let (y_int, dydt_int) = self.interpolate(&t_int, &t_sol, &y_sol, function)?;
120 Ok((t_int, y_int, dydt_int))
121 } else {
122 Ok((t_sol, y_sol, dydt_sol))
123 }
124 }
125}
126
127impl<Y, U> InterpolateSolution<Y, U> for BogackiShampine
128where
129 Y: Tensor,
130 for<'a> &'a Y: Mul<TensorRank0, Output = Y> + Sub<&'a Y, Output = Y>,
131 U: TensorVec<Item = Y>,
132{
133 fn interpolate(
134 &self,
135 time: &Vector,
136 tp: &Vector,
137 yp: &U,
138 function: impl Fn(TensorRank0, &Y) -> Result<Y, IntegrationError>,
139 ) -> Result<(U, U), IntegrationError> {
140 let mut dt;
141 let mut i;
142 let mut k_1;
143 let mut k_2;
144 let mut k_3;
145 let mut t;
146 let mut y;
147 let mut y_int = U::zero(0);
148 let mut dydt_int = U::zero(0);
149 let mut y_trial;
150 for time_k in time.iter() {
151 i = tp.iter().position(|tp_i| tp_i > time_k).unwrap();
152 t = tp[i - 1];
153 y = yp[i - 1].clone();
154 dt = time_k - t;
155 k_1 = function(t, &y)?;
156 k_2 = function(t + 0.5 * dt, &(&k_1 * (0.5 * dt) + &y))?;
157 k_3 = function(t + 0.75 * dt, &(&k_2 * (0.75 * dt) + &y))?;
158 y_trial = (&k_1 * 2.0 + &k_2 * 3.0 + &k_3 * 4.0) * (dt / 9.0) + &y;
159 dydt_int.push(function(t + dt, &y_trial)?);
160 y_int.push(y_trial);
161 }
162 Ok((y_int, dydt_int))
163 }
164}