differential_equations/solout/default.rs
1//! Default Solout Implementation, e.g. outputting solutions at calculated steps.
2//!
3//! This module provides the default output strategy, which returns solution points
4//! at each step taken by the solver without any interpolation.
5
6use super::*;
7
8/// The default output handler that returns solution values at each solver step.
9///
10/// # Overview
11///
12/// `DefaultSolout` is the simplest output handler that captures the solution
13/// at each internal step calculated by the solver. It doesn't perform any
14/// interpolation or filtering - it simply records the exact points that the
15/// solver naturally computes during integration.
16///
17/// # Features
18///
19/// - Captures all solver steps in the output
20/// - No interpolation overhead
21/// - Gives the raw, unmodified solver trajectory
22///
23/// # Example
24///
25/// ```rust
26/// use differential_equations::prelude::*;
27/// use differential_equations::solout::DefaultSolout;
28///
29/// // Simple exponential growth
30/// struct ExponentialGrowth;
31///
32/// impl ODE for ExponentialGrowth {
33/// fn diff(&self, _t: f64, y: &f64, dydt: &mut f64) {
34/// *dydt = *y; // dy/dt = y
35/// }
36/// }
37///
38/// // Create the system and solver
39/// let system = ExponentialGrowth;
40/// let t0 = 0.0;
41/// let tf = 2.0;
42/// let y0 = 1.0;
43/// let solver = ExplicitRungeKutta::dop853().rtol(1e-6).atol(1e-8);
44///
45/// // Use the default output handler explicitly
46/// let default_output = DefaultSolout::new();
47///
48/// // Solve with default output
49/// let solution = IVP::ode(&system, t0, tf, y0)
50/// .solout(default_output)
51/// .method(solver)
52/// .solve()
53/// .unwrap();
54///
55/// // Note: This is equivalent to the default behavior
56/// let solution2 = IVP::ode(&system, t0, tf, y0)
57/// .method(ExplicitRungeKutta::dop853().rtol(1e-6).atol(1e-8))
58/// .solve()
59/// .unwrap();
60/// ```
61///
62/// # Output Characteristics
63///
64/// The output will contain only the actual steps computed by the solver,
65/// which may not be evenly spaced in time. The spacing depends on the solver's
66/// adaptive step size control.
67///
68/// For evenly spaced output points, consider using `EvenSolout` instead.
69///
70#[derive(Clone, Debug)]
71pub struct DefaultSolout {}
72
73impl<T, Y> Solout<T, Y> for DefaultSolout
74where
75 T: Real,
76 Y: State<T>,
77{
78 fn solout<I>(
79 &mut self,
80 t_curr: T,
81 _t_prev: T,
82 y_curr: &Y,
83 _y_prev: &Y,
84 _interpolator: &mut I,
85 solution: &mut Solution<T, Y>,
86 ) -> ControlFlag<T, Y>
87 where
88 I: Interpolation<T, Y> + ?Sized,
89 {
90 // Output the current time and state to the vectors
91 solution.push(t_curr, y_curr.clone());
92
93 // Continue the integration
94 ControlFlag::Continue
95 }
96}
97
98impl Default for DefaultSolout {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104impl DefaultSolout {
105 /// Creates a new DefaultSolout instance.
106 ///
107 /// This is the simplest output handler that captures solution values
108 /// at each step naturally taken by the solver.
109 ///
110 /// # Returns
111 /// * A new `DefaultSolout` instance
112 ///
113 pub fn new() -> Self {
114 DefaultSolout {}
115 }
116}