Skip to content

Proposal: powered descent / retropropulsion module #952

@hahahuy

Description

@hahahuy

Hi, I'd like to contribute a powered descent / retropropulsion module to RocketPy — the ability to simulate a rocket re-igniting its motor (or a dedicated descent motor) after apogee to decelerate before landing, similar to what SpaceX does with Falcon 9 booster recovery.

This is currently missing from RocketPy. With 500+ university teams using the simulator, adding this capability would directly support vehicle recovery research and range safety analysis. I intend to use it for a peer-reviewed paper with a Vietnamese sounding rocket (VNSC-01/VNSC-02) as the worked example.


Proposed Architecture

After reading Flight.py, Rocket.py, Motor.py, and Environment.py, I believe the cleanest integration is a new flight phase inserted into the existing FlightPhases state machine (Option A below), rather than a subclass or a two-leg replay.

New Flight parameters

Flight(
    rocket=...,
    environment=...,
    rail_length=...,
    # ... existing params unchanged ...
    descent_motor=None,          # Motor instance, or None for ballistic descent
    descent_trigger="apogee",    # "apogee" | float (altitude m) | callable(t, y)
    guidance_schedule=None,      # pre-solved thrust schedule: Function(t) or array
)
  • guidance_schedule is intentionally pre-solved offline (e.g. with CasADi) and replayed during integration — no optimizer inside the ODE loop, so Monte Carlo runs stay fast.
  • Flights without descent_motor behave identically to today. Fully backward compatible.

New ODE derivative: u_dot_powered_descent

Structurally identical to u_dot_generalized, except:

  • net_thrust = descent_motor.thrust(t - t_ignition) (time-shifted to descent ignition)
  • Thrust direction: body-frame −Z (retro) or guidance-commanded unit vector
  • Mass: rocket.dry_mass + descent_motor.mass(t - t_ignition)

Phase insertion (inside __check_simulation_events)

After apogee detection (currently flight.py:1113–1159), insert:

self.flight_phases.add_phase(
    t_ignition,
    derivative=self.u_dot_powered_descent,
    callbacks=[self._start_descent_motor],
    clear=True,
)

Minimal file footprint

File Change
rocketpy/simulation/flight.py New params + u_dot_powered_descent + descent trigger
rocketpy/rocket/rocket.py Optional add_descent_motor() helper
tests/test_flight.py Validation cases (3-DOF vacuum, analytical check)
Motor, Environment, math utils No changes

Implementation Plan

  • now: Architecture discussion with maintainers (this issue)
  • 3-DOF vacuum descent — constant thrust, point mass, no atmosphere; validate against Tsiolkovsky + gravity-turn analytical solution; write tests
  • Open draft PR with 3-DOF implementation
  • Atmosphere, 6-DOF, guidance schedule support

And if possible, please help me figure some question out,

  1. Preferred integration point — New Flight arguments (Option A above) vs. a PoweredDescentFlight subclass vs. a composable FlightSegment approach? Subclassing is fragile given the __ private methods; I lean toward Option A.

  2. Motor API — Should we support a separate descent_motor argument on Flight, or is there appetite for multi-burn support directly in Motor (i.e. discontinuous burn windows)?

  3. Thrust direction convention — Body-frame −Z (opposing body axis) or inertial −V (opposing velocity vector)? The latter is correct for a hover-slam but requires a guidance input.

  4. enh/events branch — I see an open enh/events branch on upstream. Will this affect the event/trigger system in ways I should design around now?

  5. equations_of_motion parameter — Would equations_of_motion="powered_descent" be the right way to select this path, consistent with the existing "standard" / "solid_propulsion" options?


Background

I have a background in Aero/CFD, Propulsion, and GNC. The full design note (with class hierarchy analysis, ODE breakdown, and implementation sequence) is available — happy to share or attach.

Happy to hear if the maintainers prefer a different approach entirely before I invest in a deep implementation. Thanks for building such a clean simulator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions