conspire/math/integrate/ode/implicit/
mod.rs1#[cfg(test)]
2mod test;
3
4use crate::{
5 math::{
6 Derivative, Differentiable, Quantity, Scalar, Tensor, TensorVec,
7 integrate::{FixedStep, IntegrationError, OdeIntegrator, Times},
8 optimize::{EqualityConstraint, FirstOrderRootFinding, ZerothOrderRootFinding},
9 },
10 units::Time,
11};
12
13pub(crate) mod backward_euler;
14pub(crate) mod midpoint;
15pub(crate) mod trapezoidal;
16
17pub trait ImplicitZerothOrder<Y, U, V, T = Time>
19where
20 Self: FixedStep<T> + OdeIntegrator<Y, U>,
21 Y: Differentiable<T> + Tensor,
22 U: TensorVec<Item = Y>,
23 V: TensorVec<Item = Derivative<Y, T>>,
24{
25 #[doc = include_str!("doc.md")]
26 fn integrate(
27 &self,
28 mut function: impl FnMut(Quantity<T>, &Y) -> Result<Derivative<Y, T>, IntegrationError>,
29 time: &[Quantity<T>],
30 initial_condition: Y,
31 solver: impl ZerothOrderRootFinding<Y, Y>,
32 ) -> Result<(Times<T>, U, V), IntegrationError> {
33 let t_0 = time[0];
34 let t_f = time[time.len() - 1];
35 let mut t_sol: Times<T>;
36 if time.len() < 2 {
37 return Err(IntegrationError::LengthTimeLessThanTwo);
38 } else if t_0 >= t_f {
39 return Err(IntegrationError::InitialTimeNotLessThanFinalTime);
40 } else if time.len() == 2 {
41 if self.dt() <= Quantity::default() || self.dt().is_nan() {
42 return Err(IntegrationError::TimeStepNotSet(
43 time[0].value(),
44 time[1].value(),
45 format!("{self:?}"),
46 ));
47 } else {
48 let max_steps = ((t_f - t_0).value() / self.dt().value()).ceil() as usize;
49 t_sol = (0..max_steps)
50 .map(|step| t_0 + self.dt() * (step as Scalar))
51 .collect();
52 t_sol.push(t_f);
53 }
54 } else {
55 t_sol = time.iter().copied().collect();
56 }
57 let mut index = 0;
58 let mut t = t_0;
59 let mut dt;
60 let mut t_trial;
61 let mut y = initial_condition.clone();
62 let mut y_sol = U::new();
63 y_sol.push(initial_condition.clone());
64 let mut dydt_sol = V::new();
65 dydt_sol.push(function(t, &y.clone())?);
66 let mut y_trial;
67 while t < t_f {
68 t_trial = t_sol[index + 1];
69 dt = t_trial - t;
70 y_trial = match solver.root(
71 |y_trial: &Y| self.residual(&mut function, t, &y, t_trial, y_trial, dt),
72 y.clone(),
73 EqualityConstraint::None,
74 ) {
75 Ok(solution) => solution,
76 Err(error) => {
77 return Err(IntegrationError::upstream(error, self));
78 }
79 };
80 t = t_trial;
81 y = y_trial;
82 y_sol.push(y.clone());
83 dydt_sol.push(function(t, &y)?);
84 index += 1;
85 }
86 Ok((t_sol, y_sol, dydt_sol))
87 }
88 fn residual(
89 &self,
90 function: impl FnMut(Quantity<T>, &Y) -> Result<Derivative<Y, T>, IntegrationError>,
91 t: Quantity<T>,
92 y: &Y,
93 t_trial: Quantity<T>,
94 y_trial: &Y,
95 dt: Quantity<T>,
96 ) -> Result<Y, String>;
97}
98
99pub trait ImplicitFirstOrder<Y, J, U, V, T = Time>
101where
102 Self: ImplicitZerothOrder<Y, U, V, T>,
103 Y: Differentiable<T> + Tensor,
104 J: Differentiable<T> + Tensor,
105 U: TensorVec<Item = Y>,
106 V: TensorVec<Item = Derivative<Y, T>>,
107{
108 #[doc = include_str!("doc.md")]
109 fn integrate(
110 &self,
111 mut function: impl FnMut(Quantity<T>, &Y) -> Result<Derivative<Y, T>, IntegrationError>,
112 mut jacobian: impl FnMut(Quantity<T>, &Y) -> Result<Derivative<J, T>, IntegrationError>,
113 time: &[Quantity<T>],
114 initial_condition: Y,
115 solver: impl FirstOrderRootFinding<Y, J, Y>,
116 ) -> Result<(Times<T>, U, V), IntegrationError> {
117 let t_0 = time[0];
118 let t_f = time[time.len() - 1];
119 let mut t_sol: Times<T>;
120 if time.len() < 2 {
121 return Err(IntegrationError::LengthTimeLessThanTwo);
122 } else if t_0 >= t_f {
123 return Err(IntegrationError::InitialTimeNotLessThanFinalTime);
124 } else if time.len() == 2 {
125 if self.dt() <= Quantity::default() || self.dt().is_nan() {
126 return Err(IntegrationError::TimeStepNotSet(
127 time[0].value(),
128 time[1].value(),
129 format!("{self:?}"),
130 ));
131 } else {
132 let max_steps = ((t_f - t_0).value() / self.dt().value()).ceil() as usize;
133 t_sol = (0..max_steps)
134 .map(|step| t_0 + self.dt() * (step as Scalar))
135 .collect();
136 t_sol.push(t_f);
137 }
138 } else {
139 t_sol = time.iter().copied().collect();
140 }
141 let mut index = 0;
142 let mut t = t_0;
143 let mut dt;
144 let mut t_trial;
145 let mut y = initial_condition.clone();
146 let mut y_sol = U::new();
147 y_sol.push(initial_condition.clone());
148 let mut dydt_sol = V::new();
149 dydt_sol.push(function(t, &y.clone())?);
150 let mut y_trial;
151 while t < t_f {
152 t_trial = t_sol[index + 1];
153 dt = t_trial - t;
154 y_trial = match solver.root(
155 |y_trial: &Y| self.residual(&mut function, t, &y, t_trial, y_trial, dt),
156 |y_trial: &Y| self.hessian(&mut jacobian, t, &y, t_trial, y_trial, dt),
157 y.clone(),
158 EqualityConstraint::None,
159 None,
160 ) {
161 Ok(solution) => solution,
162 Err(error) => {
163 return Err(IntegrationError::upstream(error, self));
164 }
165 };
166 t = t_trial;
167 y = y_trial;
168 y_sol.push(y.clone());
169 dydt_sol.push(function(t, &y)?);
170 index += 1;
171 }
172 Ok((t_sol, y_sol, dydt_sol))
173 }
174 fn hessian(
175 &self,
176 jacobian: impl FnMut(Quantity<T>, &Y) -> Result<Derivative<J, T>, IntegrationError>,
177 t: Quantity<T>,
178 y: &Y,
179 t_trial: Quantity<T>,
180 y_trial: &Y,
181 dt: Quantity<T>,
182 ) -> Result<J, String>;
183}