astrodyn_dynamics
Rigid-body dynamics, integrators (RK4, RKF45, Gauss-Jackson, ABM4), the
mass tree, and body initialization for the
astrodyn_bevy workspace.
Ports
models/dynamics/
and
models/utils/integration/
from NASA JEOD v5.4.0. Decomposes JEOD's
1200-line DynBody god-class into ~10 narrow components.
When to use
- Propagating a rigid body's state — translational (3-DOF) or full 6-DOF — through an RK4, RKF45, Adams-Bashforth-Moulton, or Gauss-Jackson integrator step.
- Composing mass trees with parallel-axis-theorem (Steiner) contributions from offset child masses, and re-deriving the composite center of mass / inertia tensor after attach / detach.
- Initializing a body from orbital elements, mean anomaly,
time-since-periapsis, LVLH, or NED — all five JEOD
BodyActionpathways are ported. - Pre-/post-step frame propagation — moving state between a
body's
structure,composite_body, andcore_bodyframes, and applying holonomic constraints (tethers, articulated joints).
Mission code rarely calls into this crate directly; the
VehicleBuilder typestate in astrodyn and the astrodyn_bevy
components wrap these primitives. Reach for astrodyn_dynamics
directly when porting JEOD BodyAction subclasses, when adding a new
integrator, or when implementing a custom constraint.
Key concepts
Translational state is stored absolute in the integration frame
(typically J2000 ECI), not parent-relative — this is the JEOD
convention and it's enforced at the type level via
TranslationalStateTyped<IntegrationFrame>. Consumers that require
root-inertial coordinates (gravity, SRP, solar beta, Earth lighting)
must apply the integration-origin shift through
body.trans.to_inertial(&integ_origin); the compiler refuses to pass
integration-frame state where root-inertial is required. See RF.10
in docs/JEOD_invariants.md for the structural rationale.
Mass properties carry both mass and the pre-computed
inverse_mass (similarly inertia and inverse_inertia), refreshed
once per step so the inner force-to-acceleration loop is
multiply-only. The MassTree keeps subtree composition in Steiner
form, so a leaf re-attach updates the composite CoM / inertia at the
root via the same algorithm JEOD uses. Integrators (rk4_*,
rkf45, abm4, gauss_jackson) all reduce to the same per-stage
derivative call (compute_translational_derivatives /
compute_frame_derivatives), so adding a new integrator is purely
adding a new dispatch arm to IntegrationMethod.
Layered architecture
astrodyn_bevy (Bevy ECS adapter, mission code)
↓
astrodyn (orchestration, recipes)
↓
astrodyn_dynamics ← this crate (pure Rust, zero Bevy)
↓
astrodyn_math, astrodyn_frames, astrodyn_quantities
Public surface
state::TranslationalState,rotational::RotationalState,rotational::SixDofState— body state in the integration frame.mass::MassProperties— mass, inertia, CoM offset, plus the parallel-axis (Steiner) composition for mass-tree subtrees.forces::TotalForce,forces::FrameDerivatives,forces::DynamicsConfig,forces::GravityAcceleration— per-step force / derivative accumulators.integration::IntegrationMethod— RK4 / RKF45 / GJ / ABM4 dispatch.propagation,subtree,attach,body_init,constraints,mass_body— the rest of theDynBodydecomposition.
JEOD conventions
- Translational state is stored in the integration frame (typically
J2000 ECI), absolute (not parent-frame-relative). Typed clients use
TranslationalStateTyped<F>; the runner pinsF = astrodyn_quantities::IntegrationFrameso consumers requiring root-inertial coordinates (gravity, SRP, solar beta, earth lighting) must apply the integration-origin shift viabody.trans.to_inertial(&integ_origin)— a compile error otherwise. See issue #255 /RF.10indocs/JEOD_invariants.md. - Quaternions are scalar-first left-transformation
(
astrodyn_math::JeodQuat). inverse_massandinverse_inertiaare pre-computed once per step to keep the inner loop multiply-only.
See also
docs/JEOD_invariants.md—DB.*,MS.*,IN.*,RK.*,AB.*,GJ.*invariants.- Project README and
CLAUDE.md— workspace-level architecture. - Rendered rustdoc: https://docs.rs/astrodyn_dynamics