Skip to content

Commit df915d6

Browse files
feat: add nonlinear solver tolerance and timestep growth/shrink user configuration options (#233)
1 parent a88856e commit df915d6

File tree

7 files changed

+89
-17
lines changed

7 files changed

+89
-17
lines changed

diffsol/src/nonlinear_solver/convergence.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ impl<'a, V: Vector> Convergence<'a, V> {
4343
}
4444

4545
pub fn new(rtol: V::T, atol: &'a V) -> Self {
46-
let tol = V::T::from_f64(0.2).unwrap();
46+
Self::with_tolerance(rtol, atol, V::T::from_f64(0.2).unwrap())
47+
}
48+
49+
pub fn with_tolerance(rtol: V::T, atol: &'a V, tol: V::T) -> Self {
4750
Self {
4851
rtol,
4952
atol,

diffsol/src/ode_solver/bdf.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,11 @@ where
254254

255255
state.check_consistent_with_problem(problem)?;
256256

257-
let mut convergence = Convergence::new(problem.rtol, &problem.atol);
257+
let mut convergence = Convergence::with_tolerance(
258+
problem.rtol,
259+
&problem.atol,
260+
problem.ode_options.nonlinear_solver_tolerance,
261+
);
258262
convergence.set_max_iter(config.maximum_newton_iterations);
259263

260264
let op = if integrate_main_eqn {

diffsol/src/ode_solver/builder.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,18 @@ where
10171017
max_error_test_failures: ode_options.max_error_test_failures,
10181018
max_nonlinear_solver_iterations: ode_options.max_nonlinear_solver_iterations,
10191019
min_timestep: T::from_f64(ode_options.min_timestep).unwrap(),
1020+
max_timestep_growth: ode_options
1021+
.max_timestep_growth
1022+
.map(|value| T::from_f64(value).unwrap()),
1023+
min_timestep_growth: ode_options
1024+
.min_timestep_growth
1025+
.map(|value| T::from_f64(value).unwrap()),
1026+
max_timestep_shrink: ode_options
1027+
.max_timestep_shrink
1028+
.map(|value| T::from_f64(value).unwrap()),
1029+
min_timestep_shrink: ode_options
1030+
.min_timestep_shrink
1031+
.map(|value| T::from_f64(value).unwrap()),
10201032
update_jacobian_after_steps: ode_options.update_jacobian_after_steps,
10211033
update_rhs_jacobian_after_steps: ode_options.update_rhs_jacobian_after_steps,
10221034
threshold_to_update_jacobian: T::from_f64(ode_options.threshold_to_update_jacobian)
@@ -1026,6 +1038,8 @@ where
10261038
)
10271039
.unwrap(),
10281040
max_nonlinear_solver_failures: ode_options.max_nonlinear_solver_failures,
1041+
nonlinear_solver_tolerance: T::from_f64(ode_options.nonlinear_solver_tolerance)
1042+
.unwrap(),
10291043
}
10301044
}
10311045

diffsol/src/ode_solver/config.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,18 @@ impl<T: Scalar> BdfConfig<T> {
5656
minimum_timestep: ode_options.min_timestep,
5757
maximum_error_test_failures: ode_options.max_error_test_failures,
5858
maximum_newton_fails: ode_options.max_nonlinear_solver_failures,
59-
maximum_timestep_growth: T::from_f64(2.0).unwrap(),
60-
minimum_timestep_growth: T::from_f64(2.0).unwrap(),
61-
maximum_timestep_shrink: T::from_f64(0.9).unwrap(),
62-
minimum_timestep_shrink: T::from_f64(0.5).unwrap(),
59+
maximum_timestep_growth: ode_options
60+
.max_timestep_growth
61+
.unwrap_or_else(|| T::from_f64(2.0).unwrap()),
62+
minimum_timestep_growth: ode_options
63+
.min_timestep_growth
64+
.unwrap_or_else(|| T::from_f64(2.0).unwrap()),
65+
maximum_timestep_shrink: ode_options
66+
.max_timestep_shrink
67+
.unwrap_or_else(|| T::from_f64(0.9).unwrap()),
68+
minimum_timestep_shrink: ode_options
69+
.min_timestep_shrink
70+
.unwrap_or_else(|| T::from_f64(0.5).unwrap()),
6371
maximum_newton_iterations: ode_options.max_nonlinear_solver_iterations,
6472
}
6573
}
@@ -82,10 +90,18 @@ impl<T: Scalar> SdirkConfig<T> {
8290
Self {
8391
minimum_timestep: ode_options.min_timestep,
8492
maximum_error_test_failures: ode_options.max_error_test_failures,
85-
maximum_timestep_growth: T::from_f64(2.0).unwrap(),
86-
minimum_timestep_growth: T::from_f64(2.0).unwrap(),
87-
maximum_timestep_shrink: T::from_f64(0.9).unwrap(),
88-
minimum_timestep_shrink: T::from_f64(0.5).unwrap(),
93+
maximum_timestep_growth: ode_options
94+
.max_timestep_growth
95+
.unwrap_or_else(|| T::from_f64(2.0).unwrap()),
96+
minimum_timestep_growth: ode_options
97+
.min_timestep_growth
98+
.unwrap_or_else(|| T::from_f64(2.0).unwrap()),
99+
maximum_timestep_shrink: ode_options
100+
.max_timestep_shrink
101+
.unwrap_or_else(|| T::from_f64(0.9).unwrap()),
102+
minimum_timestep_shrink: ode_options
103+
.min_timestep_shrink
104+
.unwrap_or_else(|| T::from_f64(0.5).unwrap()),
89105
maximum_newton_iterations: ode_options.max_nonlinear_solver_iterations,
90106
maximum_newton_fails: ode_options.max_nonlinear_solver_failures,
91107
}
@@ -127,10 +143,18 @@ impl<T: Scalar> ExplicitRkConfig<T> {
127143
Self {
128144
minimum_timestep: ode_options.min_timestep,
129145
maximum_error_test_failures: ode_options.max_error_test_failures,
130-
maximum_timestep_growth: T::from_f64(2.0).unwrap(),
131-
minimum_timestep_growth: T::from_f64(1.0).unwrap(),
132-
maximum_timestep_shrink: T::from_f64(1.0).unwrap(),
133-
minimum_timestep_shrink: T::from_f64(0.5).unwrap(),
146+
maximum_timestep_growth: ode_options
147+
.max_timestep_growth
148+
.unwrap_or_else(|| T::from_f64(2.0).unwrap()),
149+
minimum_timestep_growth: ode_options
150+
.min_timestep_growth
151+
.unwrap_or_else(|| T::from_f64(1.0).unwrap()),
152+
maximum_timestep_shrink: ode_options
153+
.max_timestep_shrink
154+
.unwrap_or_else(|| T::from_f64(1.0).unwrap()),
155+
minimum_timestep_shrink: ode_options
156+
.min_timestep_shrink
157+
.unwrap_or_else(|| T::from_f64(0.5).unwrap()),
134158
}
135159
}
136160
}

diffsol/src/ode_solver/problem.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,18 @@ pub struct OdeSolverOptions<T: Scalar> {
5151
pub max_error_test_failures: usize,
5252
/// maximum number of nonlinear solver failures before aborting the solve and returning an error (default: 50)
5353
pub max_nonlinear_solver_failures: usize,
54+
/// nonlinear solver convergence tolerance (default: 0.2)
55+
pub nonlinear_solver_tolerance: T,
5456
/// minimum allowed timestep size (default: 1e-13)
5557
pub min_timestep: T,
58+
/// optional maximum timestep growth factor (solver-specific default when `None`)
59+
pub max_timestep_growth: Option<T>,
60+
/// optional minimum timestep growth factor (solver-specific default when `None`)
61+
pub min_timestep_growth: Option<T>,
62+
/// optional maximum timestep shrink factor (solver-specific default when `None`)
63+
pub max_timestep_shrink: Option<T>,
64+
/// optional minimum timestep shrink factor (solver-specific default when `None`)
65+
pub min_timestep_shrink: Option<T>,
5666
/// maximum number of steps after which to update the Jacobian (default: 20).
5767
/// This only requires an additional linear solver setup, not evaluation of the full Jacobian.
5868
pub update_jacobian_after_steps: usize,
@@ -71,7 +81,12 @@ impl<T: Scalar> Default for OdeSolverOptions<T> {
7181
max_nonlinear_solver_iterations: 10,
7282
max_error_test_failures: 40,
7383
max_nonlinear_solver_failures: 50,
84+
nonlinear_solver_tolerance: T::from_f64(0.2).unwrap(),
7485
min_timestep: T::from_f64(1e-13).unwrap(),
86+
max_timestep_growth: None,
87+
min_timestep_growth: None,
88+
max_timestep_shrink: None,
89+
min_timestep_shrink: None,
7590
update_jacobian_after_steps: 20,
7691
update_rhs_jacobian_after_steps: 50,
7792
threshold_to_update_jacobian: T::from_f64(0.3).unwrap(),

diffsol/src/ode_solver/sdirk.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ where
161161
let nonlinear_solver = NewtonNonlinearSolver::new(linear_solver, NoLineSearch);
162162

163163
// set max iterations for nonlinear solver
164-
let mut convergence = Convergence::new(problem.rtol, &problem.atol);
164+
let mut convergence = Convergence::with_tolerance(
165+
problem.rtol,
166+
&problem.atol,
167+
problem.ode_options.nonlinear_solver_tolerance,
168+
);
165169
convergence.set_max_iter(config.maximum_newton_iterations);
166170

167171
let gamma = rk.tableau().a().get_index(1, 1);

diffsol/src/ode_solver/state.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,11 @@ pub trait OdeSolverState<V: Vector>: Clone + Sized {
464464
let mut y_tmp = state.dy.clone();
465465
y_tmp.copy_from_indices(state.y, &f.algebraic_indices);
466466
let mut yerr = y_tmp.clone();
467-
let mut convergence = Convergence::new(rtol, atol);
467+
let mut convergence = Convergence::with_tolerance(
468+
rtol,
469+
atol,
470+
ode_problem.ode_options.nonlinear_solver_tolerance,
471+
);
468472
convergence.set_max_iter(ode_problem.ic_options.max_newton_iterations);
469473
let mut result = Ok(());
470474
debug!("Setting consistent initial conditions at t = {}", state.t);
@@ -519,7 +523,11 @@ pub trait OdeSolverState<V: Vector>: Clone + Sized {
519523
return Ok(());
520524
}
521525

522-
let mut convergence = Convergence::new(ode_problem.rtol, &ode_problem.atol);
526+
let mut convergence = Convergence::with_tolerance(
527+
ode_problem.rtol,
528+
&ode_problem.atol,
529+
ode_problem.ode_options.nonlinear_solver_tolerance,
530+
);
523531
convergence.set_max_iter(ode_problem.ic_options.max_newton_iterations);
524532
let (algebraic_indices, _) = ode_problem
525533
.eqn

0 commit comments

Comments
 (0)