Skip to main content

conspire/math/integrate/field/state/
mod.rs

1use super::{
2    Integrable,
3    adaptive::{integrate_rkmk, integrate_rkmk_adaptive},
4};
5use crate::math::{
6    Derivative, Differentiable, Quantity, Scalar, TensorVec,
7    integrate::{ButcherTableau, EmbeddedTableau, IntegrationError, Times},
8};
9use crate::units::Time;
10use std::ops::Mul;
11
12/// The `Point` type of a [`StateEvolution`] model's field.
13pub type EvolvedState<M, T = Time, Y = Quantity> =
14    <<M as StateEvolution<T, Y>>::Field as Integrable>::Point;
15
16/// The `Increment` (Lie-algebra) type of a [`StateEvolution`] model's field.
17pub type EvolvedIncrement<M, T = Time, Y = Quantity> =
18    <<M as StateEvolution<T, Y>>::Field as Integrable>::Increment;
19
20/// A model whose internal state evolves as a product of Lie-algebra rates,
21/// ready for the field drivers. [`Self::Drive`] is the externally-imposed input
22/// the rate needs beside the state (e.g. the total deformation gradient).
23///
24/// `Y` is only a discriminant: a model type (e.g. `Canonical`) that could carry
25/// several kinds of internal state selects one impl per `Y`, so it appears here
26/// even though nothing in the trait names it.
27pub trait StateEvolution<T = Time, Y = Quantity>
28where
29    <Self::Field as Integrable>::Increment: Differentiable<T>,
30{
31    /// Geometry of the composite internal state.
32    type Field: Integrable;
33    /// The externally-imposed driving input.
34    type Drive;
35    /// The initial internal state.
36    fn initial_state(&self) -> <Self::Field as Integrable>::Point;
37    /// The product of Lie-algebra rates at `(time, drive, state)`.
38    fn state_rate(
39        &self,
40        time: Quantity<T>,
41        drive: &Self::Drive,
42        state: &<Self::Field as Integrable>::Point,
43    ) -> Result<Derivative<<Self::Field as Integrable>::Increment, T>, String>;
44}
45
46/// Runs [`super::integrate_rkmk`] over a [`StateEvolution`] model, sampling
47/// `drive` at each stage time and starting from the model's own initial state.
48pub fn integrate_rkmk_state<M, Tab, U, T, Y>(
49    model: &M,
50    mut drive: impl FnMut(Quantity<T>) -> M::Drive,
51    time: &[Quantity<T>],
52) -> Result<(Times<T>, U), IntegrationError>
53where
54    M: StateEvolution<T, Y>,
55    Tab: ButcherTableau,
56    EvolvedState<M, T, Y>: Clone,
57    EvolvedIncrement<M, T, Y>: Clone + Differentiable<T>,
58    T: Copy,
59    Quantity<T>: Mul<Scalar, Output = Quantity<T>>,
60    for<'a> &'a Derivative<EvolvedIncrement<M, T, Y>, T>:
61        Mul<Quantity<T>, Output = EvolvedIncrement<M, T, Y>>,
62    U: TensorVec<Item = EvolvedState<M, T, Y>>,
63{
64    let initial = model.initial_state();
65    integrate_rkmk::<M::Field, Tab, U, T>(
66        |t, state| model.state_rate(t, &drive(t), state),
67        time,
68        initial,
69    )
70}
71
72/// Runs [`super::integrate_rkmk_adaptive`] over a [`StateEvolution`] model,
73/// sampling `drive` at each stage time and starting from the model's own
74/// initial state. `time` supplies only the span `[time[0], time[last]]`; the
75/// returned times are the steps the controller accepted.
76pub fn integrate_rkmk_state_adaptive<M, Tab, U, T, Y>(
77    model: &M,
78    mut drive: impl FnMut(Quantity<T>) -> M::Drive,
79    time: &[Quantity<T>],
80    abs_tol: Scalar,
81    rel_tol: Scalar,
82) -> Result<(Times<T>, U), IntegrationError>
83where
84    M: StateEvolution<T, Y>,
85    Tab: EmbeddedTableau,
86    EvolvedState<M, T, Y>: Clone,
87    EvolvedIncrement<M, T, Y>: Clone + Differentiable<T>,
88    T: Copy,
89    Quantity<T>: Mul<Scalar, Output = Quantity<T>>,
90    for<'a> &'a Derivative<EvolvedIncrement<M, T, Y>, T>:
91        Mul<Quantity<T>, Output = EvolvedIncrement<M, T, Y>>,
92    U: TensorVec<Item = EvolvedState<M, T, Y>>,
93{
94    let initial = model.initial_state();
95    integrate_rkmk_adaptive::<M::Field, Tab, U, T>(
96        |t, state| model.state_rate(t, &drive(t), state),
97        time,
98        initial,
99        abs_tol,
100        rel_tol,
101    )
102}
103
104// A per-state StateStep seam for overriding the RK march can't work here:
105// Differentiate admits exactly one Derivative per state, but RKMK needs a
106// second slope type (the algebra element, not the group velocity) for the
107// same group-valued state. Dispatch on the field (IntegrableField) instead.