Source code for empyrean.orbits.thrust

"""Continuous-thrust inputs: steering law, thrust arcs, and thrust
parameters for low-thrust / finite-burn propagation.

These dataclasses are the Python mirror of the engine's thrust surface.
Attach a :class:`ThrustParams` to an orbit at propagation time via the
``thrust_arcs`` keyword of :func:`empyrean.propagate` — one entry per
orbit, positionally aligned with the orbit batch (pass ``None`` for the
gravity / non-grav-only orbits). Field names, units, and semantics match
the engine one-for-one so a thrust arc round-trips without renaming or
reshaping.

The acceleration during an arc is

.. math::

    \\mathbf{a}(t) = \\sigma(t)\\,\\frac{F}{m(t)}\\,\\hat{d}

with a smooth :math:`\\tanh` switch :math:`\\sigma(t)` whose width is set
by :attr:`ThrustArc.sharpness`, steering direction :math:`\\hat{d}` from
:attr:`ThrustArc.steering`, and mass :math:`m(t)` that depletes when
:attr:`ThrustArc.isp_s` is set:

.. math::

    m(t) = m_0 - \\frac{F}{I_{sp}\\,g_0}\\,(t - t_\\text{start}).

Example
-------
>>> from empyrean import Origin
>>> from empyrean.orbits.thrust import (
...     ConstantRTN,
...     ThrustArc,
...     ThrustParams,
... )
>>> arc = ThrustArc(
...     start_mjd_tdb=59000.0,
...     end_mjd_tdb=59010.0,
...     thrust_n=1000.0,
...     mass_kg=1000.0,
...     steering=ConstantRTN(alpha_rad=0.1, beta_rad=0.2),
...     sharpness=100.0,
...     central_body=Origin.SUN,
... )
>>> params = ThrustParams(arcs=[arc])
"""

from __future__ import annotations

from dataclasses import dataclass, field

from empyrean.coordinates.enums import Origin

__all__ = [
    "ConstantRTN",
    "InertialFixed",
    "SteeringLaw",
    "ThrustArc",
    "ThrustParams",
    "VelocityTangent",
]


[docs] @dataclass(frozen=True) class ConstantRTN: """Constant RTN steering angles relative to the arc's central body. The thrust direction is .. math:: \\hat{d} = \\cos\\beta\\cos\\alpha\\;\\hat{R} + \\cos\\beta\\sin\\alpha\\;\\hat{T} + \\sin\\beta\\;\\hat{N} where :math:`\\alpha` is the in-plane angle (radial toward transverse) and :math:`\\beta` is the out-of-plane angle (toward orbit normal). """ alpha_rad: float """In-plane angle from radial toward transverse (radians).""" beta_rad: float """Out-of-plane angle toward orbit normal (radians)."""
[docs] @dataclass(frozen=True) class VelocityTangent: """Thrust aligned with the velocity vector relative to the central body: :math:`\\hat{d} = \\hat{v}_\\text{body}`."""
[docs] @dataclass(frozen=True) class InertialFixed: """Fixed direction in the inertial frame (normalized internally).""" direction: tuple[float, float, float] """Direction vector; normalized by the engine."""
# The steering law is one of the three variants above. The Python class # name of the variant is the discriminant the binding reads when it # rebuilds the engine's `SteeringLaw` enum. SteeringLaw = ConstantRTN | VelocityTangent | InertialFixed
[docs] @dataclass class ThrustArc: """A single continuous-thrust arc with smooth on/off switching and optional mass depletion.""" start_mjd_tdb: float """Arc start epoch (MJD TDB).""" end_mjd_tdb: float """Arc end epoch (MJD TDB).""" thrust_n: float """Engine thrust force in Newtons.""" mass_kg: float """Spacecraft mass at arc start in kilograms.""" steering: SteeringLaw """Steering law for this arc.""" sharpness: float r""":math:`\tanh` switching sharpness (1/days). Higher values give sharper on/off transitions (closer to bang-bang). Typical values: 1000-10000 for burns of minutes, 100 for multi-hour arcs.""" central_body: Origin """Central body for the RTN / velocity-tangent frame reference. The spacecraft state is expressed relative to this body before the thrust direction is computed — e.g. ``Origin.EARTH`` for geocentric arcs, ``Origin.SUN`` for heliocentric ones.""" isp_s: float | None = None r"""Specific impulse in seconds. When set, mass depletes linearly during the burn at :math:`\dot m = F/(I_{sp}\,g_0)`; ``None`` holds mass constant."""
[docs] @dataclass class ThrustParams: """Thrust parameters for one orbit: thrust arcs plus optional :math:`\\Delta v` targeting corrections. When :attr:`correction_covariances` is non-empty its length MUST equal :attr:`dv_corrections`; a non-empty covariance triggers the wide-Jet burn-sensitivity propagation and its solved segments appear in the propagated :attr:`~empyrean.TaggedCovariance.thrust_segments`. Length or arc/correction mismatches surface loudly as an exception during propagation — never silently repaired or dropped. """ arcs: list[ThrustArc] """Ordered list of thrust arcs. May overlap in time.""" dv_corrections: list[tuple[float, float, float]] = field(default_factory=list) r"""Per-arc :math:`\Delta v` corrections (AU/day) for targeting, positional with :attr:`arcs`. Each is applied as a constant inertial acceleration during its arc's window; when seeded as Jet variables it provides :math:`\partial\text{state}/\partial\Delta v`. Empty = no corrections.""" correction_covariances: list[list[tuple[float, float, float]]] = field(default_factory=list) r"""3x3 covariance (AU/day):math:`^2` per :math:`\Delta v` correction, positional with :attr:`dv_corrections`. When non-empty its length must equal ``dv_corrections``. Empty = no burn-sensitivity propagation."""