oxicuda_solver/dense/ode_pde/
implicit.rs1use crate::error::{SolverError, SolverResult};
4
5use super::types::{OdeConfig, OdeSolution, OdeSystem};
6use super::utils::{numerical_jacobian, solve_dense_system, validate_ode_inputs, vec_norm};
7
8pub struct ImplicitEulerSolver;
14
15impl ImplicitEulerSolver {
16 const MAX_NEWTON: usize = 50;
18 const NEWTON_TOL: f64 = 1e-12;
20
21 pub fn solve(
23 system: &dyn OdeSystem,
24 y0: &[f64],
25 config: &OdeConfig,
26 ) -> SolverResult<OdeSolution> {
27 let n = system.dim();
28 validate_ode_inputs(n, y0, config)?;
29
30 let mut t = config.t_start;
31 let dt = config.dt;
32 let mut y = y0.to_vec();
33
34 let mut times = vec![t];
35 let mut states = vec![y.clone()];
36 let mut num_steps = 0_usize;
37 let mut num_rhs = 0_usize;
38
39 while t < config.t_end - dt * 1e-10 && num_steps < config.max_steps {
40 let h = dt.min(config.t_end - t);
41 let t_new = t + h;
42
43 let mut y_new = y.clone();
46 let mut f_val = vec![0.0; n];
47 system.rhs(t, &y, &mut f_val)?;
48 num_rhs += 1;
49 for i in 0..n {
50 y_new[i] = y[i] + h * f_val[i];
51 }
52
53 let mut converged = false;
54 for _ in 0..Self::MAX_NEWTON {
55 system.rhs(t_new, &y_new, &mut f_val)?;
57 num_rhs += 1;
58
59 let mut residual = vec![0.0; n];
60 for i in 0..n {
61 residual[i] = y_new[i] - y[i] - h * f_val[i];
62 }
63
64 let res_norm = vec_norm(&residual);
65 if res_norm < Self::NEWTON_TOL {
66 converged = true;
67 break;
68 }
69
70 let jac_f = numerical_jacobian(system, t_new, &y_new, 1e-8)?;
72 num_rhs += n; let mut jac_g = vec![vec![0.0; n]; n];
76 for i in 0..n {
77 for j in 0..n {
78 jac_g[i][j] = -h * jac_f[i][j];
79 if i == j {
80 jac_g[i][j] += 1.0;
81 }
82 }
83 }
84
85 let delta =
87 solve_dense_system(&jac_g, &residual.iter().map(|r| -r).collect::<Vec<_>>())?;
88
89 for i in 0..n {
90 y_new[i] += delta[i];
91 }
92 }
93
94 if !converged {
95 return Err(SolverError::ConvergenceFailure {
96 iterations: Self::MAX_NEWTON as u32,
97 residual: {
98 system.rhs(t_new, &y_new, &mut f_val)?;
99 let mut res = vec![0.0; n];
100 for i in 0..n {
101 res[i] = y_new[i] - y[i] - h * f_val[i];
102 }
103 vec_norm(&res)
104 },
105 });
106 }
107
108 y.copy_from_slice(&y_new);
109 t = t_new;
110 num_steps += 1;
111
112 times.push(t);
113 states.push(y.clone());
114 }
115
116 Ok(OdeSolution {
117 times,
118 states,
119 num_steps,
120 num_rejected: 0,
121 num_rhs_evals: num_rhs,
122 })
123 }
124}
125
126pub struct Bdf2Solver;
132
133impl Bdf2Solver {
134 const MAX_NEWTON: usize = 50;
136 const NEWTON_TOL: f64 = 1e-12;
138
139 pub fn solve(
143 system: &dyn OdeSystem,
144 y0: &[f64],
145 config: &OdeConfig,
146 ) -> SolverResult<OdeSolution> {
147 let n = system.dim();
148 validate_ode_inputs(n, y0, config)?;
149
150 let dt = config.dt;
151 let mut t = config.t_start;
152 let mut y_prev = y0.to_vec();
153
154 let mut times = vec![t];
155 let mut states = vec![y_prev.clone()];
156 let mut num_steps = 0_usize;
157 let mut num_rhs = 0_usize;
158
159 let h = dt.min(config.t_end - t);
161 if t >= config.t_end - dt * 1e-10 {
162 return Ok(OdeSolution {
163 times,
164 states,
165 num_steps: 0,
166 num_rejected: 0,
167 num_rhs_evals: 0,
168 });
169 }
170
171 let mut y_cur = y_prev.clone();
172 let mut f_val = vec![0.0; n];
173 {
174 system.rhs(t, &y_prev, &mut f_val)?;
176 num_rhs += 1;
177 for i in 0..n {
178 y_cur[i] = y_prev[i] + h * f_val[i];
179 }
180
181 let t_new = t + h;
182 let mut converged = false;
183 for _ in 0..Self::MAX_NEWTON {
184 system.rhs(t_new, &y_cur, &mut f_val)?;
185 num_rhs += 1;
186
187 let mut residual = vec![0.0; n];
188 for i in 0..n {
189 residual[i] = y_cur[i] - y_prev[i] - h * f_val[i];
190 }
191
192 if vec_norm(&residual) < Self::NEWTON_TOL {
193 converged = true;
194 break;
195 }
196
197 let jac_f = numerical_jacobian(system, t_new, &y_cur, 1e-8)?;
198 num_rhs += n;
199
200 let mut jac_g = vec![vec![0.0; n]; n];
201 for i in 0..n {
202 for j in 0..n {
203 jac_g[i][j] = -h * jac_f[i][j];
204 if i == j {
205 jac_g[i][j] += 1.0;
206 }
207 }
208 }
209
210 let neg_res: Vec<f64> = residual.iter().map(|r| -r).collect();
211 let delta = solve_dense_system(&jac_g, &neg_res)?;
212 for i in 0..n {
213 y_cur[i] += delta[i];
214 }
215 }
216
217 if !converged {
218 return Err(SolverError::ConvergenceFailure {
219 iterations: Self::MAX_NEWTON as u32,
220 residual: 1.0,
221 });
222 }
223
224 t = t_new;
225 num_steps += 1;
226 times.push(t);
227 states.push(y_cur.clone());
228 }
229
230 while t < config.t_end - dt * 1e-10 && num_steps < config.max_steps {
232 let h_step = dt.min(config.t_end - t);
233 let t_new = t + h_step;
234
235 let coeff_h = 2.0 / 3.0 * h_step;
238
239 let mut y_new = vec![0.0; n];
241 for i in 0..n {
242 y_new[i] = 2.0 * y_cur[i] - y_prev[i];
243 }
244
245 let mut converged = false;
246 for _ in 0..Self::MAX_NEWTON {
247 system.rhs(t_new, &y_new, &mut f_val)?;
248 num_rhs += 1;
249
250 let mut residual = vec![0.0; n];
251 for i in 0..n {
252 residual[i] = y_new[i] - (4.0 / 3.0) * y_cur[i] + (1.0 / 3.0) * y_prev[i]
253 - coeff_h * f_val[i];
254 }
255
256 if vec_norm(&residual) < Self::NEWTON_TOL {
257 converged = true;
258 break;
259 }
260
261 let jac_f = numerical_jacobian(system, t_new, &y_new, 1e-8)?;
262 num_rhs += n;
263
264 let mut jac_g = vec![vec![0.0; n]; n];
265 for i in 0..n {
266 for j in 0..n {
267 jac_g[i][j] = -coeff_h * jac_f[i][j];
268 if i == j {
269 jac_g[i][j] += 1.0;
270 }
271 }
272 }
273
274 let neg_res: Vec<f64> = residual.iter().map(|r| -r).collect();
275 let delta = solve_dense_system(&jac_g, &neg_res)?;
276 for i in 0..n {
277 y_new[i] += delta[i];
278 }
279 }
280
281 if !converged {
282 return Err(SolverError::ConvergenceFailure {
283 iterations: Self::MAX_NEWTON as u32,
284 residual: 1.0,
285 });
286 }
287
288 y_prev.copy_from_slice(&y_cur);
289 y_cur.copy_from_slice(&y_new);
290 t = t_new;
291 num_steps += 1;
292 times.push(t);
293 states.push(y_cur.clone());
294 }
295
296 Ok(OdeSolution {
297 times,
298 states,
299 num_steps,
300 num_rejected: 0,
301 num_rhs_evals: num_rhs,
302 })
303 }
304}