Skip to main content

nyx_space/dynamics/
mod.rs

1/*
2    Nyx, blazing fast astrodynamics
3    Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use crate::State;
20use crate::cosmic::{AstroError, Orbit};
21use crate::linalg::allocator::Allocator;
22use crate::linalg::{DefaultAllocator, DimName, Matrix3, Matrix4x3, OMatrix, OVector, Vector3};
23use anise::almanac::Almanac;
24use anise::almanac::planetary::PlanetaryDataError;
25use anise::errors::AlmanacError;
26use hyperdual::Owned;
27use snafu::Snafu;
28
29use std::fmt;
30
31pub use crate::errors::NyxError;
32
33/// The orbital module handles all Cartesian based orbital dynamics.
34///
35/// It is up to the engineer to ensure that the coordinate frames of the different dynamics borrowed
36/// from this module match, or perform the appropriate coordinate transformations.
37pub mod orbital;
38use self::guidance::GuidanceError;
39pub use self::orbital::*;
40
41/// The spacecraft module allows for simulation of spacecraft dynamics in general, including propulsion/maneuvers.
42pub mod spacecraft;
43pub use self::spacecraft::*;
44
45/// Defines a few examples of guidance laws.
46pub mod guidance;
47
48/// Defines some velocity change controllers.
49pub mod deltavctrl;
50
51/// Defines solar radiation pressure models
52pub mod solarpressure;
53pub use self::solarpressure::*;
54
55/// The drag module handles drag in a very basic fashion. Do not use for high fidelity dynamics.
56pub mod drag;
57pub use self::drag::*;
58
59/// Define the gravity field models.
60/// This module allows loading gravity models from [PDS](http://pds-geosciences.wustl.edu/), [EGM2008](http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008/) and GMAT's own COF files.
61pub mod gravity_field;
62pub use self::gravity_field::*;
63
64/// Define the solid tide models.
65#[cfg(feature = "premium")]
66pub mod solid_tides;
67#[cfg(feature = "premium")]
68pub use self::solid_tides::*;
69
70pub mod sequence;
71
72/// The `Dynamics` trait handles and stores any equation of motion *and* the state is integrated.
73///
74/// Its design is such that several of the provided dynamics can be combined fairly easily. However,
75/// when combining the dynamics (e.g. integrating both the attitude of a spaceraft and its orbital
76///  parameters), it is up to the implementor to handle time and state organization correctly.
77/// For time management, I highly recommend using `hifitime` which is thoroughly validated.
78#[allow(clippy::type_complexity)]
79pub trait Dynamics: Clone + Sync + Send
80where
81    DefaultAllocator: Allocator<<Self::StateType as State>::Size>
82        + Allocator<<Self::StateType as State>::VecLength>
83        + Allocator<<Self::StateType as State>::Size, <Self::StateType as State>::Size>,
84{
85    /// The state of the associated hyperdual state, almost always StateType + U1
86    type HyperdualSize: DimName;
87    type StateType: State;
88
89    /// Defines the equations of motion for these dynamics, or a combination of provided dynamics.
90    /// The time delta_t is in **seconds** PAST the context epoch. The state vector is the state which
91    /// changes for every intermediate step of the integration. The state context is the state of
92    /// what is being propagated, it should allow rebuilding a new state context from the
93    /// provided state vector.
94    fn eom(
95        &self,
96        delta_t: f64,
97        state_vec: &OVector<f64, <Self::StateType as State>::VecLength>,
98        state_ctx: &Self::StateType,
99        almanac: &Almanac,
100    ) -> Result<OVector<f64, <Self::StateType as State>::VecLength>, DynamicsError>
101    where
102        DefaultAllocator: Allocator<<Self::StateType as State>::VecLength>;
103
104    /// Defines the equations of motion for Dual numbers for these dynamics.
105    /// _All_ dynamics need to allow for automatic differentiation. However, if differentiation is not supported,
106    /// then the dynamics should prevent initialization with a context which has an STM defined.
107    fn dual_eom(
108        &self,
109        _delta_t: f64,
110        _osculating_state: &Self::StateType,
111        _almanac: &Almanac,
112    ) -> Result<
113        (
114            OVector<f64, <Self::StateType as State>::Size>,
115            OMatrix<f64, <Self::StateType as State>::Size, <Self::StateType as State>::Size>,
116        ),
117        DynamicsError,
118    >
119    where
120        DefaultAllocator: Allocator<Self::HyperdualSize>
121            + Allocator<<Self::StateType as State>::Size>
122            + Allocator<<Self::StateType as State>::Size, <Self::StateType as State>::Size>,
123        Owned<f64, Self::HyperdualSize>: Copy,
124    {
125        Err(DynamicsError::StateTransitionMatrixUnset)
126    }
127
128    /// Optionally performs some final changes after each successful integration of the equations of motion.
129    /// For example, this can be used to update the Guidance mode.
130    /// NOTE: This function is also called just prior to very first integration step in order to update the initial state if needed.
131    fn finally(
132        &self,
133        next_state: Self::StateType,
134        _almanac: &Almanac,
135    ) -> Result<Self::StateType, DynamicsError> {
136        Ok(next_state)
137    }
138}
139
140/// Evaluates mass-dependent forces acting on a spacecraft.
141///
142/// Implementations of `ForceModel` operate on a full [`Spacecraft`] context to account for
143/// physical properties such as mass, cross-sectional area, and surface coefficients (e.g.,
144/// aerodynamic drag, solar radiation pressure). The evaluated force vector $\mathbf{F}$ is
145/// scaled by the inverse spacecraft mass ($1/m$) and unit conversions during numerical
146/// integration to yield acceleration in $\text{km/s}^2$.
147pub trait ForceModel: Send + Sync + fmt::Display {
148    /// Returns the state-vector index of an estimable parameter associated with this model, if configured.
149    ///
150    /// For example, if a drag coefficient ($C_D$) or radiation pressure coefficient ($C_R$) is
151    /// actively estimated in the filter state, this returns its corresponding index in the state vector.
152    fn estimation_index(&self) -> Option<usize>;
153
154    /// Evaluates the force vector $\mathbf{F}$ at the provided state and epoch. Must be in kg*km/s^2 (or kN).
155    fn eom(&self, ctx: &Spacecraft, almanac: &Almanac) -> Result<Vector3<f64>, DynamicsError>;
156
157    /// Evaluates the nominal force vector $\mathbf{F}$ and its partial derivatives for State Transition Matrix (STM) propagation.
158    ///
159    /// Returns a tuple containing:
160    /// 1. The nominal force vector $\mathbf{F}$.
161    /// 2. The $4 \times 3$ Jacobian matrix containing spatial partial derivatives ($\partial \mathbf{F}/\partial \mathbf{r}$)
162    ///    in the first three rows, and parameter partial derivatives ($\partial \mathbf{F}/\partial p$) in the fourth row.
163    fn gradient(
164        &self,
165        osc_ctx: &Spacecraft,
166        almanac: &Almanac,
167    ) -> Result<(Vector3<f64>, Matrix4x3<f64>), DynamicsError>;
168}
169
170/// Evaluates mass-independent accelerations acting directly on an orbit.
171///
172/// Unlike [`ForceModel`], implementations of `AccelModel` operate strictly on an [`Orbit`]
173/// state because the evaluated acceleration vector $\mathbf{a}$ is independent of spacecraft mass
174/// or surface geometry (e.g., central-body spherical harmonics, point-mass third-body gravity).
175pub trait AccelModel: Send + Sync + fmt::Display {
176    /// Evaluates the acceleration vector $\mathbf{a}$ ($\text{km/s}^2$) in the integration frame at the provided orbital state and epoch.
177    fn eom(&self, osc: &Orbit, almanac: &Almanac) -> Result<Vector3<f64>, DynamicsError>;
178
179    /// Evaluates the nominal acceleration vector $\mathbf{a}$ and its spatial partial derivatives for State Transition Matrix (STM) propagation.
180    ///
181    /// Returns a tuple containing:
182    /// 1. The nominal acceleration vector $\mathbf{a}$ ($\text{km/s}^2$).
183    /// 2. The $3 \times 3$ Jacobian matrix of spatial partial derivatives ($\partial \mathbf{a}/\partial \mathbf{r}$, in $\text{s}^{-2}$).
184    fn gradient(
185        &self,
186        osc_ctx: &Orbit,
187        almanac: &Almanac,
188    ) -> Result<(Vector3<f64>, Matrix3<f64>), DynamicsError>;
189}
190
191/// Stores dynamical model errors
192#[derive(Debug, PartialEq, Snafu)]
193#[snafu(visibility(pub(crate)))]
194pub enum DynamicsError {
195    #[snafu(display("spacecraft total mass is zero, cannot compute any force model"))]
196    MasslessSpacecraft,
197    /// Fuel exhausted at the provided spacecraft state
198    #[snafu(display("fuel exhausted at {sc}"))]
199    FuelExhausted { sc: Box<Spacecraft> },
200    #[snafu(display("expected STM to be set"))]
201    StateTransitionMatrixUnset,
202    #[snafu(display("dynamical model encountered an astro error: {source}"))]
203    DynamicsAstro { source: AstroError },
204    #[snafu(display("dynamical model encountered an issue with the guidance: {source}"))]
205    DynamicsGuidance { source: GuidanceError },
206    #[snafu(display("dynamical model issue due to Almanac: {action} {source}"))]
207    DynamicsAlmanacError {
208        action: &'static str,
209        #[snafu(source(from(AlmanacError, Box::new)))]
210        source: Box<AlmanacError>,
211    },
212    #[snafu(display("dynamical model issue due to planetary data: {action} {source}"))]
213    DynamicsPlanetaryError {
214        action: &'static str,
215        #[snafu(source(from(PlanetaryDataError, Box::new)))]
216        source: Box<PlanetaryDataError>,
217    },
218}