sim_lib_numbers_numeric/traits.rs
1//! Backend plugin traits and option types: `Differentiator`, `Quadrature`, and
2//! `OdeSolver`, plus the `DiffOpts`/`QuadOpts`/`OdeOpts` and problem records.
3
4use sim_kernel::{Cx, Result, Symbol, Value};
5use sim_lib_numbers_func::Func;
6
7/// Common interface for every numeric backend plugin: it reports its method
8/// name and the kind of operation it implements.
9pub trait NumericPlugin: Send + Sync + 'static {
10 /// The method name this plugin registers under (for example, `central` or `rk4`).
11 fn name(&self) -> Symbol;
12 /// The numeric operation kind this plugin implements.
13 fn kind(&self) -> NumericKind;
14}
15
16/// The category of numeric backend, used to route a plugin to the right slot in
17/// the registry.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum NumericKind {
20 /// A differentiator backend (numeric derivative at a point).
21 Differentiator,
22 /// A fixed-rule quadrature backend (definite integral with a set rule).
23 QuadratureFixed,
24 /// An adaptive quadrature backend (definite integral to a tolerance).
25 QuadratureAdaptive,
26 /// A fixed-step ODE solver backend.
27 OdeFixed,
28 /// An adaptive-step ODE solver backend.
29 OdeAdaptive,
30 /// An implicit differential-algebraic equation solver backend.
31 DaeImplicit,
32}
33
34/// Options controlling a numeric-differentiation call.
35#[derive(Clone, Debug)]
36pub struct DiffOpts {
37 /// The differentiator method to use, or `auto` to let the registry choose.
38 pub method: Symbol,
39 /// The finite-difference step size.
40 pub h: f64,
41}
42
43impl DiffOpts {
44 /// Returns default options: the `auto` method with a small default step.
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// use sim_lib_numbers_numeric::DiffOpts;
50 ///
51 /// let opts = DiffOpts::auto();
52 /// assert_eq!(opts.method.to_string(), "auto");
53 /// assert!(opts.h > 0.0);
54 /// ```
55 pub fn auto() -> Self {
56 Self {
57 method: Symbol::new("auto"),
58 h: 1.0e-6,
59 }
60 }
61}
62
63/// Options controlling an integration (quadrature) call.
64#[derive(Clone, Debug)]
65pub struct QuadOpts {
66 /// The quadrature method to use, or `auto` to let the registry choose.
67 pub method: Symbol,
68 /// The number of subdivisions, for fixed-rule quadrature.
69 pub n: Option<usize>,
70 /// The error tolerance, for adaptive quadrature.
71 pub tol: Option<f64>,
72}
73
74impl QuadOpts {
75 /// Returns default options for fixed-rule integration (`auto`, no tolerance).
76 pub fn fixed_default() -> Self {
77 Self {
78 method: Symbol::new("auto"),
79 n: None,
80 tol: None,
81 }
82 }
83
84 /// Returns default options for adaptive integration (`auto` with a tolerance).
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use sim_lib_numbers_numeric::QuadOpts;
90 ///
91 /// let fixed = QuadOpts::fixed_default();
92 /// assert!(fixed.tol.is_none());
93 ///
94 /// let adaptive = QuadOpts::adaptive_default();
95 /// assert!(adaptive.tol.is_some());
96 /// ```
97 pub fn adaptive_default() -> Self {
98 Self {
99 method: Symbol::new("auto"),
100 n: None,
101 tol: Some(1.0e-10),
102 }
103 }
104}
105
106/// Options controlling an ODE-solve call.
107#[derive(Clone, Debug)]
108pub struct OdeOpts {
109 /// The ODE solver method to use, or `auto` to let the registry choose.
110 pub method: Symbol,
111 /// The fixed step size, for fixed-step solvers.
112 pub h: Option<f64>,
113 /// The error tolerance, for adaptive solvers.
114 pub tol: Option<f64>,
115 /// An optional cap on the number of integration steps.
116 pub max_steps: Option<usize>,
117}
118
119impl OdeOpts {
120 /// Returns default options for an adaptive ODE solve (`auto` with a tolerance).
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use sim_lib_numbers_numeric::OdeOpts;
126 ///
127 /// let opts = OdeOpts::default_adaptive();
128 /// assert_eq!(opts.method.to_string(), "auto");
129 /// assert!(opts.tol.is_some());
130 /// assert!(opts.h.is_none());
131 /// ```
132 pub fn default_adaptive() -> Self {
133 Self {
134 method: Symbol::new("auto"),
135 h: None,
136 tol: Some(1.0e-8),
137 max_steps: None,
138 }
139 }
140}
141
142/// A first-order initial-value ODE problem `dy/dx = f(x, y)` handed to an
143/// [`OdeSolver`].
144pub struct OdeProblem<'a> {
145 /// The right-hand-side function giving the derivative.
146 pub dy: &'a Func,
147 /// The independent-variable symbol (typically `x`).
148 pub var: &'a Symbol,
149 /// The dependent-variable symbol (typically `y`).
150 pub y_var: &'a Symbol,
151 /// The initial value of the independent variable.
152 pub x0: &'a Value,
153 /// The initial value of the dependent variable.
154 pub y0: &'a Value,
155 /// The end value of the independent variable to integrate toward.
156 pub x_end: &'a Value,
157}
158
159/// A numeric differentiation backend: computes `df/dvar` at a point.
160pub trait Differentiator: NumericPlugin {
161 /// Evaluates the numeric derivative of `f` with respect to `var` at `point`.
162 fn diff_at(
163 &self,
164 cx: &mut Cx,
165 f: &Func,
166 var: &Symbol,
167 point: &Value,
168 opt: DiffOpts,
169 ) -> Result<Value>;
170}
171
172/// A numeric integration (quadrature) backend: computes a definite integral.
173pub trait Quadrature: NumericPlugin {
174 /// Integrates `f` over `var` from `lo` to `hi`.
175 fn integrate(
176 &self,
177 cx: &mut Cx,
178 f: &Func,
179 var: &Symbol,
180 lo: &Value,
181 hi: &Value,
182 opt: QuadOpts,
183 ) -> Result<Value>;
184}
185
186/// A numeric ODE-solving backend: integrates an initial-value problem.
187pub trait OdeSolver: NumericPlugin {
188 /// Solves `problem`, returning the sampled `(x, y)` points of the trajectory.
189 fn solve(
190 &self,
191 cx: &mut Cx,
192 problem: OdeProblem<'_>,
193 opt: OdeOpts,
194 ) -> Result<Vec<(Value, Value)>>;
195}