feat: hobby/model-rocket simulator with multi-body support, design validator, and CSV/JSON export#2
Merged
Merged
Conversation
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.
9 tasks
- 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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 supportSubstantive physics changes
Ispflow model (dm/dt = thrust(t) / (Isp · g₀)). Propellant is 30–40 % of total mass for hobby rockets, so constant-mass produces visibly wrong apogees.BOOSTandCOAST(using the airframe'sCd · A) and inDESCENT(using the recovery device'sCd · A). Each body has its own exponential atmosphere; Moon is vacuum.burn_time + delay_seconds, the realistic default), with an opt-indeploy_mode="apogee"for design-exploration runs.New public API
MotorAtmosphere.earth(),.mars(),.venus(),.titan()factories.Parachute(diameter, Cd)/Streamer(length, width, Cd)None= ballistic.Rocket(name, dry_mass_kg, motor, diameter_m, drag_coefficient, recovery, body)FlightPhaseenumSimulationState.SimulationResultdeployed_below_groundflag.get_kit(name)andget_motor(name)return mutation-isolated copies.parse_eng_file(text)andload_motor_file(path)accept the standard.engmotor-file format used by Thrustcurve.org and OpenRocket.Built-in 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-kitsreplace the old--list-presets.Removals
Engine,RocketConfig,IntegrationMethodtypesPhysics.drag_force/Physics.atmospheric_densitystandalone helpers (drag and density are wired into the integrator and theAtmosphereclass)PlotConfigdataclassSimulationConfig.log_levelPhysics.escape_velocityandPhysics.orbital_velocityare kept as standalone utilities for educational comparisons.Commit 2 —
feat: design validator, CSV/JSON export, 1/2A6-2 motor presetPre-launch design validator
validate_design(rocket) -> list[DesignWarning]runs heuristic checks on a configuredRocketand returns advisories. The CLI runs it automatically (skip with--no-validate) and exits with code2if anyerror-level warning fires.motor_too_bigwill_not_liftlawn_dartlow_twrlow_apogeetransonicballistic_descentdelay_too_shortdelay_too_longCSV / JSON export
SimulationResultgains.to_csv(path),.to_dict(),.to_json(path). CLI flags--csv FILEand--json FILE. Output formats:time_s, altitude_m, velocity_ms, acceleration_ms2, mass_kg, thrust_n, drag_n, phase.{ "rocket_name", "summary": {...}, "states": [...] }.1/2A6-2motor presetThe Mosquito kit was previously documented as using
1/2A6-2, but the motor was missing fromMOTORSso the kit silently fell back toA8-3via a_KIT_MOTOR_FALLBACKStable. The preset is now present and the fallback plumbing is gone.Bonus fix
SimulationResult.deployed_below_groundpreviously 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 newlawn_dartvalidator check needs.Modelling caveats (documented in README)
The README points users to OpenRocket for higher-fidelity simulation.
Test plan
pytest— 114 tests passruff check src tests— cleanruff format --check src tests— cleanmypy src— no issuespython -m build— producesrocket_sim-0.1.0.tar.gzand the wheeltwine check dist/*— both passrocket-sim --kit alpha-iii --motor c6-5 --no-plot --csv f.csv --json f.jsonruns validation, prints summary, writes both filesmotor_too_bigerror and exits non-zero