Skip to content

Commit

Permalink
Add integration frame and serialization to integrator options
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherRabotin committed Sep 30, 2024
1 parent 450e821 commit ef56369
Show file tree
Hide file tree
Showing 25 changed files with 236 additions and 110 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ typed-builder = "0.20.0"
pythonize = { version = "0.21", optional = true }
snafu = { version = "0.8.3", features = ["backtrace"] }
serde_dhall = "0.12"
toml = "0.8.14"


[dev-dependencies]
polars = { version = "0.42.0", features = ["parquet"] }
rstest = "0.22.0"
pretty_env_logger = "0.5"
toml = "0.8.14"

[build-dependencies]
shadow-rs = "0.35.0"
Expand Down
6 changes: 6 additions & 0 deletions src/cosmic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ where
fn set_value(&mut self, param: StateParameter, _val: f64) -> Result<(), StateError> {
Err(StateError::Unavailable { param })
}

/// Returns a copy of the orbit
fn orbit(&self) -> Orbit;

/// Modifies this state's orbit
fn set_orbit(&mut self, _orbit: Orbit) {}
}

pub fn assert_orbit_eq_or_abs(left: &Orbit, right: &Orbit, epsilon: f64, msg: &str) {
Expand Down
8 changes: 8 additions & 0 deletions src/cosmic/spacecraft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,14 @@ impl State for Spacecraft {
fn unset_stm(&mut self) {
self.stm = None;
}

fn orbit(&self) -> Orbit {
self.orbit
}

fn set_orbit(&mut self, orbit: Orbit) {
self.orbit = orbit;
}
}

impl Add<OVector<f64, Const<6>>> for Spacecraft {
Expand Down
1 change: 1 addition & 0 deletions src/dynamics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ pub trait AccelModel: Send + Sync + fmt::Display {

/// Stores dynamical model errors
#[derive(Debug, PartialEq, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum DynamicsError {
/// Fuel exhausted at the provided spacecraft state
#[snafu(display("fuel exhausted at {sc}"))]
Expand Down
4 changes: 2 additions & 2 deletions src/md/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use snafu::prelude::*;

pub mod prelude {
pub use super::{
optimizer::*,
targeter::*,
trajectory::{ExportCfg, Interpolatable, Traj},
Event, ScTraj, StateParameter,
};
Expand All @@ -52,7 +52,7 @@ pub use events::{Event, EventEvaluator};

pub mod objective;
pub mod opti;
pub use opti::optimizer;
pub use opti::targeter;
pub type ScTraj = trajectory::Traj<Spacecraft>;
// pub type Ephemeris = trajectory::Traj<Orbit>;

Expand Down
8 changes: 4 additions & 4 deletions src/md/opti/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@
// pub mod convert_impulsive;
pub mod multipleshooting;
pub use multipleshooting::{ctrlnodes, multishoot};
/// Uses a Levenberg Marquardt minimizer to solve the damped least squares problem.
// #[cfg(feature = "broken-donotuse")]
// pub mod minimize_lm;
pub mod optimizer;
/// Uses a [Newton Raphson](https://en.wikipedia.org/wiki/Newton%27s_method_in_optimization) method where the Jacobian is computed via finite differencing.
pub mod raphson_finite_diff;
/// Uses a [Newton Raphson](https://en.wikipedia.org/wiki/Newton%27s_method_in_optimization) method where the Jacobian is computed via hyperdual numbers.
pub mod raphson_hyperdual;
pub mod solution;
pub mod target_variable;
/// Uses a Levenberg Marquardt minimizer to solve the damped least squares problem.
// #[cfg(feature = "broken-donotuse")]
// pub mod minimize_lm;
pub mod targeter;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DiffMethod {
Expand Down
14 changes: 7 additions & 7 deletions src/md/opti/multipleshooting/multishoot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub use super::CostFunction;
use super::{MultipleShootingError, TargetingSnafu};
use crate::linalg::{DMatrix, DVector, SVector};
use crate::md::opti::solution::TargeterSolution;
use crate::md::optimizer::Optimizer;
use crate::md::targeter::Targeter;
use crate::md::{prelude::*, TargetingError};
use crate::pseudo_inverse;
use crate::{Orbit, Spacecraft};
Expand Down Expand Up @@ -81,7 +81,7 @@ impl<'a, T: MultishootNode<OT>, const VT: usize, const OT: usize> MultipleShooti
/* ***
** 1. Solve the delta-v differential corrector between each node
** *** */
let tgt = Optimizer {
let tgt = Targeter {
prop: self.prop,
objectives: self.targets[i].into(),
variables: self.variables,
Expand Down Expand Up @@ -125,7 +125,7 @@ impl<'a, T: MultishootNode<OT>, const VT: usize, const OT: usize> MultipleShooti
** Note that because the first initial_state is x0, the i-th "initial state"
** is the initial state to reach the i-th node.
** *** */
let inner_tgt_a = Optimizer::delta_v(self.prop, next_node);
let inner_tgt_a = Targeter::delta_v(self.prop, next_node);
let inner_sol_a = inner_tgt_a
.try_achieve_dual(
initial_states[i],
Expand All @@ -151,7 +151,7 @@ impl<'a, T: MultishootNode<OT>, const VT: usize, const OT: usize> MultipleShooti
/* ***
** 2.C. Rerun the targeter from the new state at the perturbed node to the next unpertubed node
** *** */
let inner_tgt_b = Optimizer::delta_v(self.prop, self.targets[i + 1].into());
let inner_tgt_b = Targeter::delta_v(self.prop, self.targets[i + 1].into());
let inner_sol_b = inner_tgt_b
.try_achieve_dual(
inner_sol_a.achieved_state,
Expand Down Expand Up @@ -241,7 +241,7 @@ impl<'a, T: MultishootNode<OT>, const VT: usize, const OT: usize> MultipleShooti

for (i, node) in self.targets.iter().enumerate() {
// Run the unpertubed targeter
let tgt = Optimizer::delta_v(self.prop, (*node).into());
let tgt = Targeter::delta_v(self.prop, (*node).into());
let sol = tgt
.try_achieve_dual(
initial_states[i],
Expand Down Expand Up @@ -355,8 +355,8 @@ impl<T: MultishootNode<O>, const O: usize> MultipleShootingSolution<T, O> {
) -> Result<Vec<ScTraj>, MultipleShootingError> {
let mut trajz = Vec::with_capacity(self.nodes.len());

for (i, node) in self.nodes.iter().enumerate() {
let (_, traj) = Optimizer::delta_v(prop, (*node).into())
for (i, node) in self.nodes.iter().copied().enumerate() {
let (_, traj) = Targeter::delta_v(prop, node.into())
.apply_with_traj(&self.solutions[i], almanac.clone())
.context(TargetingSnafu { segment: i })?;
trajz.push(traj);
Expand Down
4 changes: 2 additions & 2 deletions src/md/opti/raphson_finite_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use super::optimizer::Optimizer;
use super::targeter::Targeter;
use super::solution::TargeterSolution;
use crate::cosmic::{AstroAlmanacSnafu, AstroPhysicsSnafu};
use crate::dynamics::guidance::{GuidanceError, LocalFrame, Mnvr};
Expand All @@ -33,7 +33,7 @@ use snafu::{ensure, ResultExt};
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;

impl<'a, const V: usize, const O: usize> Optimizer<'a, V, O> {
impl<'a, const V: usize, const O: usize> Targeter<'a, V, O> {
/// Differential correction using finite differencing
#[allow(clippy::comparison_chain)]
pub fn try_achieve_fd(
Expand Down
2 changes: 1 addition & 1 deletion src/md/opti/raphson_hyperdual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::utils::are_eigenvalues_stable;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;

impl<'a, const V: usize, const O: usize> Optimizer<'a, V, O> {
impl<'a, const V: usize, const O: usize> Targeter<'a, V, O> {
/// Differential correction using hyperdual numbers for the objectives
#[allow(clippy::comparison_chain)]
pub fn try_achieve_dual(
Expand Down
16 changes: 7 additions & 9 deletions src/md/opti/optimizer.rs → src/md/opti/targeter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ use std::fmt;

use super::solution::TargeterSolution;

// TODO(now): rename to Differential Controller

/// An optimizer structure with V control variables and O objectives.
#[derive(Clone)]
pub struct Optimizer<'a, const V: usize, const O: usize> {
pub struct Targeter<'a, const V: usize, const O: usize> {
/// The propagator setup (kind, stages, etc.)
pub prop: &'a Propagator<SpacecraftDynamics>,
/// The list of objectives of this targeter
Expand All @@ -50,7 +48,7 @@ pub struct Optimizer<'a, const V: usize, const O: usize> {
pub iterations: usize,
}

impl<'a, const V: usize, const O: usize> fmt::Display for Optimizer<'a, V, O> {
impl<'a, const V: usize, const O: usize> fmt::Display for Targeter<'a, V, O> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut objmsg = String::from("");
for obj in &self.objectives {
Expand All @@ -66,7 +64,7 @@ impl<'a, const V: usize, const O: usize> fmt::Display for Optimizer<'a, V, O> {
}
}

impl<'a, const O: usize> Optimizer<'a, 3, O> {
impl<'a, const O: usize> Targeter<'a, 3, O> {
/// Create a new Targeter which will apply an impulsive delta-v correction.
pub fn delta_v(prop: &'a Propagator<SpacecraftDynamics>, objectives: [Objective; O]) -> Self {
Self {
Expand Down Expand Up @@ -116,7 +114,7 @@ impl<'a, const O: usize> Optimizer<'a, 3, O> {
}
}

impl<'a, const O: usize> Optimizer<'a, 4, O> {
impl<'a, const O: usize> Targeter<'a, 4, O> {
/// Create a new Targeter which will apply a continuous thrust for the whole duration of the segment
pub fn thrust_dir(
prop: &'a Propagator<SpacecraftDynamics>,
Expand All @@ -138,7 +136,7 @@ impl<'a, const O: usize> Optimizer<'a, 4, O> {
}
}

impl<'a, const O: usize> Optimizer<'a, 7, O> {
impl<'a, const O: usize> Targeter<'a, 7, O> {
/// Create a new Targeter which will apply a continuous thrust for the whole duration of the segment
pub fn thrust_dir_rate(
prop: &'a Propagator<SpacecraftDynamics>,
Expand All @@ -163,7 +161,7 @@ impl<'a, const O: usize> Optimizer<'a, 7, O> {
}
}

impl<'a, const O: usize> Optimizer<'a, 10, O> {
impl<'a, const O: usize> Targeter<'a, 10, O> {
/// Create a new Targeter which will apply a continuous thrust for the whole duration of the segment
pub fn thrust_profile(
prop: &'a Propagator<SpacecraftDynamics>,
Expand Down Expand Up @@ -191,7 +189,7 @@ impl<'a, const O: usize> Optimizer<'a, 10, O> {
}
}

impl<'a, const V: usize, const O: usize> Optimizer<'a, V, O> {
impl<'a, const V: usize, const O: usize> Targeter<'a, V, O> {
/// Create a new Targeter which will apply an impulsive delta-v correction.
pub fn new(
prop: &'a Propagator<SpacecraftDynamics>,
Expand Down
7 changes: 0 additions & 7 deletions src/md/trajectory/interpolatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ where

/// List of state parameters that will be exported to a trajectory file in addition to the epoch (provided in this different formats).
fn export_params() -> Vec<StateParameter>;

/// Returns the orbit
fn orbit(&self) -> &Orbit;
}

impl Interpolatable for Spacecraft {
Expand Down Expand Up @@ -156,8 +153,4 @@ impl Interpolatable for Spacecraft {
]
.concat()
}

fn orbit(&self) -> &Orbit {
&self.orbit
}
}
4 changes: 2 additions & 2 deletions src/md/trajectory/traj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,8 @@ where
// Build an array of all the RIC differences
let mut ric_diff = Vec::with_capacity(other_states.len());
for (ii, other_state) in other_states.iter().enumerate() {
let self_orbit = *self_states[ii].orbit();
let other_orbit = *other_state.orbit();
let self_orbit = self_states[ii].orbit();
let other_orbit = other_state.orbit();

let this_ric_diff = self_orbit.ric_difference(&other_orbit).map_err(Box::new)?;

Expand Down
6 changes: 6 additions & 0 deletions src/propagators/error_ctrl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ impl ErrorControl {
}
}

impl Default for ErrorControl {
fn default() -> Self {
Self::RSSCartesianStep
}
}

/// An RSS step error control which effectively computes the L2 norm of the provided Vector of size 3
///
/// Note that this error controller should be preferably be used only with slices of a state with the same units.
Expand Down
65 changes: 59 additions & 6 deletions src/propagators/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

use super::{DynamicsSnafu, IntegrationDetails, PropagationError, Propagator};
use crate::dynamics::Dynamics;
use crate::dynamics::{Dynamics, DynamicsAlmanacSnafu};
use crate::linalg::allocator::Allocator;
use crate::linalg::{DefaultAllocator, OVector};
use crate::md::trajectory::{Interpolatable, Traj};
Expand Down Expand Up @@ -94,11 +94,6 @@ where
}
let stop_time = self.state.epoch() + duration;

#[cfg(not(target_arch = "wasm32"))]
let tick = Instant::now();
#[cfg(not(target_arch = "wasm32"))]
let mut prev_tick = Instant::now();

if self.log_progress {
// Prevent the print spam for orbit determination cases
info!("Propagating for {} until {}", duration, stop_time);
Expand All @@ -114,6 +109,39 @@ where
if backprop {
self.step_size = -self.step_size; // Invert the step size
}

// Transform the state if needed
let mut original_frame = None;
if let Some(integration_frame) = self.prop.opts.integration_frame {
if integration_frame != self.state.orbit().frame {
original_frame = Some(self.state.orbit().frame);
let mut new_orbit = self
.almanac
.transform_to(self.state.orbit(), integration_frame, None)
.context(DynamicsAlmanacSnafu {
action: "transforming state into desired integration frame",
})
.context(DynamicsSnafu)?;
// If the integration frame has parameters, we set them here.
if let Some(mu_km3_s2) = integration_frame.mu_km3_s2 {
new_orbit.frame.mu_km3_s2 = Some(mu_km3_s2);
}
// If the integration frame has parameters, we set them here.
if let Some(shape) = integration_frame.shape {
new_orbit.frame.shape = Some(shape);
}
if self.log_progress {
info!("State transformed to the integration frame {integration_frame}");
}
self.state.set_orbit(new_orbit);
}
}

#[cfg(not(target_arch = "wasm32"))]
let tick = Instant::now();
#[cfg(not(target_arch = "wasm32"))]
let mut prev_tick = Instant::now();

loop {
let epoch = self.state.epoch();
if (!backprop && epoch + self.step_size > stop_time)
Expand All @@ -128,6 +156,19 @@ where
debug!("Done in {}", tock);
}
}

// Rotate back if needed
if let Some(original_frame) = original_frame {
let new_orbit = self
.almanac
.transform_to(self.state.orbit(), original_frame, None)
.context(DynamicsAlmanacSnafu {
action: "transforming state from desired integration frame",
})
.context(DynamicsSnafu)?;
self.state.set_orbit(new_orbit);
}

return Ok(self.state);
}
// Take one final step of exactly the needed duration until the stop time
Expand Down Expand Up @@ -159,6 +200,18 @@ where
}
}

// Rotate back if needed
if let Some(original_frame) = original_frame {
let new_orbit = self
.almanac
.transform_to(self.state.orbit(), original_frame, None)
.context(DynamicsAlmanacSnafu {
action: "transforming state from desired integration frame",
})
.context(DynamicsSnafu)?;
self.state.set_orbit(new_orbit);
}

return Ok(self.state);
} else {
#[cfg(not(target_arch = "wasm32"))]
Expand Down
Loading

0 comments on commit ef56369

Please sign in to comment.