Skip to main content

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}