Skip to content

Repository files navigation

Lunar Descent Simulation & Burn-Time Optimizer

Simulates and optimizes powered descent for a non-returning lunar lander. The terminal-descent model is a 2D flat-Moon plane (downrange x, altitude h) with constant g = 1.62 m/s²; optional --from-orbit mode adds a curvilinear 2D polar descent from parking orbit. Python 3.11+, NumPy/SciPy (matplotlib for plots).

Quick start

# single optimization (fixed propellant load)
python -m lunar_lander --h0 2500 --vx0 150 --m-prop 250 --plot

# outer propellant-sizing loop (finds the m_prop_usable that lands with
# exactly the reserve remaining)
python -m lunar_lander --h0 2500 --vx0 150 --size

# tests
python -m pytest

Mission profile

Five phases coast | burn 1 (braking) | coast | burn 2 | linear ramp-down, parameterized by durations x = [t0, d1, dc, d2, dr]. SLSQP minimizes the propellant fraction consumed. During optimization it integrates the smooth model to scheduled terminal time t4, enforces h(t4) = 0, touchdown velocity windows, the propellant reserve, and a minimum-altitude path constraint. Final acceptance is verified with the ground-contact event. A Nelder–Mead penalty formulation is available with --penalty-fallback for debugging.

Mass model

The mass model is ported from the audited reference in mass_estimate/ (2026-07-24 plausibility audit against Huzel & Huang 1992, Sutton 2017, SMAD 3rd ed. and Gamgami 2024 — see mass_estimate/README.md). Tanks are a thin-wall Ti-sphere physics build-up (the earlier linear coefficient 299.8·V was a helium-COPV mass class, ~8–10× too heavy for 16 bar liquid tanks); structure/thermal/harness are fractions of the pre-margin dry mass, gear 6 % of MECO mass, margin 20 %, plus bottom-up pressurization, RCS, avionics and power. Validation anchors (200 kg payload, 800 kg usable): dry 612.8 kg, wet 1452.8 kg, Δv 2447.7 m/s, σ = 0.491 — pinned in tests/test_mass_model.py together with an exact cross-check against mass_estimate/main.py.

Physical caveats

  • Payload sets feasibility. The default mission carries 80 kg (feasible range ≈ 70–90 kg). At the full 1000 kg propellant load this gives T/W(ignition) ≈ 1.02 — hover-marginal, a known open design point: the intended resolutions are a sixth engine or letting the sizing loop trade propellant. A 500 kg payload with 1000 kg of propellant (the original spec guess) has T/W(ignition) ≈ 0.70 and cannot land from 2500 m under any schedule: the hover mass is T/g ≈ 1512 kg and the vehicle would need ≈ 660 kg burned (≈ 825 s at full throttle) before it can arrest its own descent, while it reaches the ground in well under two minutes even thrusting straight up.
  • The flat-Moon terminal model provides no centrifugal relief at near-orbital speed; use --from-orbit for the curvilinear 2D descent-from-orbit model.

The tool handles infeasibility honestly:

  • mass_model.estimate_mass warns loudly when T/W(ignition) < 1;
  • optimize.feasibility_precheck integrates a dominating full-vertical-thrust profile (an upper bound on every steering/throttle policy's ability to arrest descent while it is still burning) and reports a clear infeasibility message instead of a bad trajectory;
  • the outer sizing loop (--size) shrinks m_prop_usable until the mission closes: smaller propellant load → lighter vehicle → higher T/W.

For a comfortable fixed-load run at the default payload, use e.g. --m-prop 250 (T/W(ignition) ≈ 2.4).

Runtime expectations

The optimizer now uses rtol/atol = 1e-8, an SLSQP finite-difference step tied to that tolerance, shared finite-difference caching, and deterministic warm starts. Fixed-load and sizing runs should complete in seconds on the benchmark cases; full descent-from-orbit runs in tens of seconds. See OPTIMIZER_PERFORMANCE.md for measured before/after timings and the remaining cold-start limitation near the critical orbit load.

Touchdown acceptance

The optimizer enforces h(t4) = 0 on a smooth no-ground-event terminal run and treats touchdown velocities as acceptance-window inequalities. Final acceptance is judged on a separate ground-contact verification run: a trajectory that touches down softly (|vh| ≤ 1 m/s, |vx| ≤ 0.5 m/s, reserve intact) before the scheduled t4 is a landed mission. Engines cut at contact, and any unflown ramp tail is reported as schedule_tail_s.

Steering law

Default steering is retrograde with a smooth blend to vertical thrust below v_min = 3 m/s. Two deliberate refinements over a naive angle interpolation (both documented in dynamics.RetrogradeSteering): the retrograde vertical component is clamped to ≥ 0 (never thrust toward the ground), and the low-speed blend interpolates the direction vector with a Lipschitz-bounded normalization. Without these, optimizer trial schedules that burn past descent arrest (vehicle briefly ascending) hit a 180° thrust-direction discontinuity at vx = 0, which stalls the integrator. In every descending state the law is exactly retrograde / vertical-guard per the spec.

Layout

lunar_lander/
  config.py           # frozen Config dataclass (all defaults from the spec)
  mass_model.py       # fixed-point mass estimate (tanks, gear, margin)
  dynamics.py         # RHS, steering laws, throttle schedule
  simulate.py         # phase-wise RK45 driver, Trajectory object
  optimize.py         # SLSQP wrapper, analytic guesses, scaling, diagnostics
  size_propellant.py  # outer propellant sizing (margin walk, warm starts)
  full_descent.py     # circular-orbit deorbit + curvilinear descent model
  plots.py            # trajectory, time-history and thrust-angle figures
  cli.py              # argparse entry point
  tests/              # pytest suite (analytic pins and regressions)

TODO / non-goals

  • No throttle floor yet: real hypergolic engines cannot throttle continuously to 0 — a minimum-throttle constraint on the ramp phase is a likely follow-up.
  • Neither model includes Moon rotation, attitude dynamics, gimbal limits, 3D, or dispersion analysis.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages