Skip to content

feat: hobby/model-rocket simulator with multi-body support, design validator, and CSV/JSON export#2

Merged
rramboer merged 3 commits into
mainfrom
feat/hobby-rocket-pivot
May 26, 2026
Merged

feat: hobby/model-rocket simulator with multi-body support, design validator, and CSV/JSON export#2
rramboer merged 3 commits into
mainfrom
feat/hobby-rocket-pivot

Conversation

@rramboer

@rramboer rramboer commented May 6, 2026

Copy link
Copy Markdown
Owner

Summary

Repositions the package from a heavy-lift launch-vehicle simulator (Saturn V, Falcon 9, etc.) to a single-stage Estes/Quest-class hobby-rocket simulator with multi-body launch support, then adds a pre-launch design validator, CSV/JSON time-series export, and the missing 1/2A6-2 motor preset. Two commits, one PR.

The previous framing was pedagogically misleading: in a 1-D vertical, constant-mass, drag-free model, real heavy-lift vehicles can never reach orbit. The 1-D model is physically correct for hobby model rockets, so that is the appropriate scope. Multi-body support is preserved across Earth, Moon, Mars, Venus, and Titan — the same physics is meaningful in any atmospheric and gravitational environment.


Commit 1 — feat: pivot to hobby/model-rocket simulator with multi-body support

Substantive physics changes

  • Real, sampled thrust curves with linear-time interpolation. Built-in motor presets cover A8-3, B6-4, C6-3, C6-5, D12-5, E9-6, F15-6 with manufacturer-consistent total impulse, burn time, and peak thrust.
  • Mass loss during burn via a constant-Isp flow model (dm/dt = thrust(t) / (Isp · g₀)). Propellant is 30–40 % of total mass for hobby rockets, so constant-mass produces visibly wrong apogees.
  • Atmospheric drag in BOOST and COAST (using the airframe's Cd · A) and in DESCENT (using the recovery device's Cd · A). Each body has its own exponential atmosphere; Moon is vacuum.
  • Semi-implicit drag treatment in the integrator, so the simulator stays stable when a parachute deploys at high speed.
  • Recovery deployment timing based on the motor's delay-grain (burn_time + delay_seconds, the realistic default), with an opt-in deploy_mode="apogee" for design-exploration runs.

New public API

Type Purpose
Motor Sampled thrust curve, propellant mass, total mass, delay grain.
Atmosphere Exponential-density model with .earth(), .mars(), .venus(), .titan() factories.
Parachute(diameter, Cd) / Streamer(length, width, Cd) Recovery devices. None = ballistic.
Rocket(name, dry_mass_kg, motor, diameter_m, drag_coefficient, recovery, body) Composition of airframe + motor + recovery + launch body.
FlightPhase enum Phase tracking on SimulationState.
SimulationResult Reports apogee, burnout altitude/velocity/time, max acceleration, recovery deployment time, landing velocity, and a deployed_below_ground flag.

get_kit(name) and get_motor(name) return mutation-isolated copies. parse_eng_file(text) and load_motor_file(path) accept the standard .eng motor-file format used by Thrustcurve.org and OpenRocket.

Built-in presets

  • 7 motor presets covering the popular Estes range (A through F).
  • 4 rocket-kit presets (Estes Alpha III, Big Bertha, Mosquito, V-2).
  • 5 celestial-body presets: EARTH, MOON, MARS, VENUS, TITAN.

CLI

Rewritten flag set: --kit, --motor, --motor-file, --dry-mass, --diameter, --cd, --recovery, --body, --launch-altitude, --deploy-mode. --list-motors/--list-kits replace the old --list-presets.

Removals

  • The old Engine, RocketConfig, IntegrationMethod types
  • The 13 heavy-lift rocket presets
  • Physics.drag_force / Physics.atmospheric_density standalone helpers (drag and density are wired into the integrator and the Atmosphere class)
  • The dead PlotConfig dataclass
  • SimulationConfig.log_level

Physics.escape_velocity and Physics.orbital_velocity are kept as standalone utilities for educational comparisons.


Commit 2 — feat: design validator, CSV/JSON export, 1/2A6-2 motor preset

Pre-launch design validator

validate_design(rocket) -> list[DesignWarning] runs heuristic checks on a configured Rocket and returns advisories. The CLI runs it automatically (skip with --no-validate) and exits with code 2 if any error-level warning fires.

Code Severity Trigger
motor_too_big error Motor diameter exceeds airframe diameter
will_not_lift error Peak thrust-to-weight ratio < 1
lawn_dart error Ground impact before recovery deploys
low_twr warning Peak TWR between 1 and 5
low_apogee warning Predicted apogee < 30 m
transonic warning Predicted max velocity > 343 m/s
ballistic_descent warning No recovery system configured
delay_too_short info Ejection fires more than 2 s before apogee
delay_too_long info Ejection fires more than 2 s after apogee

CSV / JSON export

SimulationResult gains .to_csv(path), .to_dict(), .to_json(path). CLI flags --csv FILE and --json FILE. Output formats:

  • CSV: one row per simulation step, columns time_s, altitude_m, velocity_ms, acceleration_ms2, mass_kg, thrust_n, drag_n, phase.
  • JSON: { "rocket_name", "summary": {...}, "states": [...] }.

1/2A6-2 motor preset

The Mosquito kit was previously documented as using 1/2A6-2, but the motor was missing from MOTORS so the kit silently fell back to A8-3 via a _KIT_MOTOR_FALLBACKS table. The preset is now present and the fallback plumbing is gone.

Bonus fix

SimulationResult.deployed_below_ground previously only fired when the ejection charge tried to deploy below ground level. It now also fires when the rocket lands before recovery ever deploys — which is what the new lawn_dart validator check needs.


Modelling caveats (documented in README)

  • Motion is 1-D vertical only — no wind, no horizontal velocity, no weathercocking
  • Single stage only
  • Built-in motor curves are simplified ~5–7-sample approximations; expect apogee predictions accurate to ~30 % vs. a properly-instrumented real flight
  • No CG/CP stability analysis

The README points users to OpenRocket for higher-fidelity simulation.

Test plan

  • pytest — 114 tests pass
  • ruff check src tests — clean
  • ruff format --check src tests — clean
  • mypy src — no issues
  • python -m build — produces rocket_sim-0.1.0.tar.gz and the wheel
  • twine check dist/* — both pass
  • CLI smoke: rocket-sim --kit alpha-iii --motor c6-5 --no-plot --csv f.csv --json f.json runs validation, prints summary, writes both files
  • CLI smoke: motor-too-big-for-airframe triggers motor_too_big error and exits non-zero
  • Multi-body sanity: same kit on Moon (vacuum, low gravity) reaches ~12 km; on Mars (thin atmosphere, low gravity) reaches ~1.3 km
  • Lawn-dart sanity: a small rocket with a 30-second delay grain on a 1-second motor lands before recovery deploys
  • CI green on this PR

rramboer added 2 commits May 6, 2026 17:29
The package was previously framed as a heavy-lift launch-vehicle
simulator (Saturn V, Falcon 9, etc.) using a 1-D vertical point-mass
model with constant mass and no atmospheric drag — a model under which
those vehicles can never reach orbit. This rewrite pivots `rocket-sim`
to its appropriate scope: single-stage Estes/Quest-class hobby rockets,
where the 1-D model is physically correct, while preserving multi-body
support across Earth, Moon, Mars, Venus, and Titan.

The substantive physics that the previous version was missing or only
exposed as unwired utilities is now integrated into the trajectory loop:

- Real, sampled motor thrust curves with linear-time interpolation
  (constant thrust was wrong for the small high-gradient profiles of
  hobby motors)
- Constant-Isp mass-flow during burn (propellant is 30-40% of total
  mass for hobby rockets, so constant-mass is wrong here)
- Atmospheric drag in the BOOST/COAST/DESCENT phases, using each body's
  exponential atmosphere model (Earth, Mars, Venus, Titan; Moon is
  vacuum)
- Recovery deployment timed off the motor's delay grain (matches real
  hardware) with an opt-in apogee-detection mode for design exploration
- Semi-implicit drag treatment in the integrator so the simulation
  stays stable when a parachute deploys at high speed

New core types:

- `Motor`: sampled thrust curve, propellant mass, total mass, delay
  grain time. Methods: thrust_at(t), mass_at(t), propellant_burned_at(t).
  Properties: burn_time, total_impulse, average_thrust, peak_thrust,
  specific_impulse.
- `Atmosphere`: exponential-density model with `.earth()`, `.mars()`,
  `.venus()`, `.titan()` factory classmethods.
- `Parachute`, `Streamer` recovery types; `None` for ballistic descent.
- `Rocket`: airframe + motor + recovery + body composition. Replaces
  the previous (dry-mass, thrust, burn_time) triple.
- `FlightPhase` enum and `SimulationState.phase` for phase tracking.
- `SimulationResult` now reports apogee, burnout altitude/velocity/time,
  max acceleration, recovery deployment time, landing velocity, and
  a `deployed_below_ground` flag (the "lawn dart" failure mode).

Built-in presets:

- 7 motor presets (A8-3, B6-4, C6-3, C6-5, D12-5, E9-6, F15-6) with
  approximated thrust curves consistent with manufacturer-published
  total impulse, burn time, and peak thrust.
- 4 rocket-kit presets (Estes Alpha III, Big Bertha, Mosquito, V-2)
  pre-configured with their recommended motor and recovery system.
- 5 celestial-body presets (Earth, Moon, Mars, Venus, Titan) with
  per-body atmospheres and gravity.

`.eng` motor file loader (`load_motor_file`, `parse_eng_file`)
compatible with files from Thrustcurve.org and OpenRocket, so users
can drop in any motor whose data is available publicly.

CLI: rewritten flag set with `--kit`, `--motor`, `--motor-file`,
`--body`, `--deploy-mode`, `--launch-altitude`. Replaces `--all-presets`
with `--list-motors` and `--list-kits`.

Visualization: phase-coloured trajectory plots, thrust-curve plots,
expanded dashboard with mass-vs-time, and apogee annotations.

Tests: 96 tests covering motors (interpolation, total-impulse classes,
.eng parsing), atmosphere (density at scale heights, vacuum), models
(Rocket, Parachute, Streamer validation), presets (all kits + mutation
isolation), simulation (phase transitions, multi-body apogee ordering,
analytic vacuum case, lawn-dart deployment), recovery (terminal
velocity bounds, ballistic vs parachute), and config (JSON round-trip).

Documentation:

- README rewritten around the new positioning. Explicit modelling
  caveats (1-D vertical, single-stage, no wind, simplified curves
  approx ~30% accurate). References OpenRocket as the gold-standard
  alternative for users who outgrow this.
- CHANGELOG v0.1.0 entry rewritten.
- pyproject.toml description and keywords updated.
- All 4 example scripts rewritten around the new API.

All 96 tests pass; ruff + mypy clean; build + twine check pass; smoke
install + CLI run end-to-end on Earth, Moon, and Mars.
Three small but high-value additions, stacking on the hobby-rocket pivot
(PR #2):

1. Pre-launch design validator (validate_design, DesignWarning,
   format_warnings). Heuristic checks for marginal thrust-to-weight,
   underpowered configurations, transonic flight without reinforced
   airframe, motor too big for the airframe, ballistic descent, and
   "lawn dart" timing. Wired into the CLI by default; --no-validate
   disables it. CLI returns exit code 2 if any error-level warning fires
   so it's usable in CI / scripts.

2. CSV and JSON export on SimulationResult (.to_csv, .to_dict, .to_json).
   CLI flags --csv FILE / --json FILE. Unlocks downstream notebook /
   spreadsheet workflows without forcing users to reach into the states
   list themselves.

3. 1/2A6-2 motor preset added to MOTORS, with the Mosquito kit now
   pointing at it directly. Removes the previous silent fallback that
   substituted A8-3 when 1/2A6-2 was requested — that fallback is gone,
   along with the _resolve_motor / _KIT_MOTOR_FALLBACKS plumbing.

Also fixes a missed case in the simulator: the lawn-dart flag
(SimulationResult.deployed_below_ground) was only set when the recovery
charge fired below ground, not when the rocket landed before recovery
ever deployed. Now set in both cases.

Tests: +18 (114 total). All ruff + mypy + pytest + build + twine clean.
README and CHANGELOG updated with new flags, exports, and motor row.
@rramboer rramboer changed the title feat: pivot to hobby/model-rocket simulator with multi-body support feat: hobby/model-rocket simulator with multi-body support, design validator, and CSV/JSON export May 8, 2026
- CONTRIBUTING.md: replace the stale "Adding New Rocket Presets" section
  (which still used the removed RocketConfig(mass/thrust/burn_time) type)
  with separate "Adding a Motor Preset" and "Adding a Rocket-Kit Preset"
  sections matching the current Motor/Rocket/kit model. Refresh the
  project-structure tree (adds motors.py, validation.py; corrects
  module descriptions).
- Add .github/workflows/release.yml: Trusted Publishing (OIDC, no API
  tokens). Pre-release tags (v*rc/a/b) publish to TestPyPI only; final
  tags (v0.1.0) publish to TestPyPI then PyPI. Uses GitHub environments
  `testpypi` and `pypi`.
@rramboer rramboer merged commit b43e397 into main May 26, 2026
7 checks passed
rramboer added a commit that referenced this pull request May 26, 2026
Three small but high-value additions, stacking on the hobby-rocket pivot
(PR #2):

1. Pre-launch design validator (validate_design, DesignWarning,
   format_warnings). Heuristic checks for marginal thrust-to-weight,
   underpowered configurations, transonic flight without reinforced
   airframe, motor too big for the airframe, ballistic descent, and
   "lawn dart" timing. Wired into the CLI by default; --no-validate
   disables it. CLI returns exit code 2 if any error-level warning fires
   so it's usable in CI / scripts.

2. CSV and JSON export on SimulationResult (.to_csv, .to_dict, .to_json).
   CLI flags --csv FILE / --json FILE. Unlocks downstream notebook /
   spreadsheet workflows without forcing users to reach into the states
   list themselves.

3. 1/2A6-2 motor preset added to MOTORS, with the Mosquito kit now
   pointing at it directly. Removes the previous silent fallback that
   substituted A8-3 when 1/2A6-2 was requested — that fallback is gone,
   along with the _resolve_motor / _KIT_MOTOR_FALLBACKS plumbing.

Also fixes a missed case in the simulator: the lawn-dart flag
(SimulationResult.deployed_below_ground) was only set when the recovery
charge fired below ground, not when the rocket landed before recovery
ever deployed. Now set in both cases.

Tests: +18 (114 total). All ruff + mypy + pytest + build + twine clean.
README and CHANGELOG updated with new flags, exports, and motor row.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant