This document provides a detailed, step-by-step plan for migrating from PyROPS v2.3.12 to RocketPy. It is designed to be:
- Actionable: Clear steps that can be executed immediately
- Comprehensive: Covers all aspects of migration
- Resumable: Can pause and resume at any checkpoint
- Validated: Ensures physics accuracy is preserved
CRITICAL: This migration must preserve all validated physics. Validation tests are mandatory.
- Prerequisites
- Phase 1: Environment Setup
- Phase 2: Benchmark Extraction
- Phase 3: Data Conversion
- Phase 4: Core Implementation
- Phase 5: Validation
- Phase 6: Extensions & Optimization
- Phase 7: Documentation & Handoff
- Checkpoints & Recovery
- Troubleshooting
- Basic Python programming
- Understanding of PyROPS workflow (how to run simulations)
- Access to PyROPS validated test cases
- Familiarity with command line / terminal
- Python 3.8 or higher
- Git (for version control)
- Text editor or IDE (VS Code, PyCharm, etc.)
- Excel or LibreOffice (for viewing data files)
- PyROPS installation and all input files
- Historical simulation results (for validation)
- Engineering team for physics validation support
- Git repository initialized ✓
- Folder structure created ✓
- PyROPS is currently functional
- Identify 3-5 "golden" test cases (well-validated simulations)
- Back up all current PyROPS data
Objective: Set up Python environment with RocketPy
Steps:
-
Verify Python version
python --version # Should be 3.8 or higherIf not installed, download from python.org
-
Create virtual environment (recommended)
cd /Users/anban/opt/share/engineering-projects/pyrops-v0.1 python -m venv venv_rocketpy # Activate (macOS/Linux) source venv_rocketpy/bin/activate # Activate (Windows) # venv_rocketpy\Scripts\activate
-
Install RocketPy and dependencies
pip install --upgrade pip pip install rocketpy pip install pandas numpy scipy matplotlib pip install pytest # For testing pip install jupyter # Optional, for notebooks
-
Verify installation
python -c "import rocketpy; print(rocketpy.__version__)"Expected output: Version number (e.g., 1.10.0)
Checkpoint 1.1: RocketPy installed successfully
- Virtual environment created
- RocketPy imported without errors
- Version number displayed
Deliverable: rocketpy_migration/docs/environment_setup.log with installation details
Objective: Run example RocketPy simulations to understand API
Steps:
-
Run first example
Create file:
rocketpy_migration/src/example_01_basic.py"""Example 1: Basic RocketPy simulation""" from rocketpy import Environment, SolidMotor, Rocket, Flight # Create environment env = Environment(latitude=32.990254, longitude=-106.974998, elevation=1400) # Create motor motor = SolidMotor( thrust_source="data/motors/Cesaroni_M1670.eng", burn_out_time=3.9, dry_mass=1.815, dry_inertia=(0.125, 0.125, 0.002), center_of_dry_mass=0.317, grains_center_of_mass_position=0.397, grain_number=5, grain_separation=0.005, grain_density=1815, grain_outer_radius=0.033, grain_initial_inner_radius=0.015, grain_initial_height=0.120, nozzle_radius=0.033, throat_radius=0.011, nozzle_position=0.001, interpolation_method="linear", ) # Create rocket rocket = Rocket( radius=0.0635, mass=14.426, inertia=(6.321, 6.321, 0.034), power_off_drag="data/calisto/powerOffDragCurve.csv", power_on_drag="data/calisto/powerOnDragCurve.csv", center_of_mass_without_motor=0, coordinate_system_orientation="tail_to_nose", ) rocket.add_motor(motor, position=-1.373) # Add fins rocket.add_trapezoidal_fins( n=4, span=0.100, root_chord=0.120, tip_chord=0.040, position=-1.168, ) # Add parachute rocket.add_parachute( name="Main", cd_s=10.0, trigger="apogee", sampling_rate=105, lag=1.5, noise=(0, 8.3, 0.5), ) # Simulate flight flight = Flight( rocket=rocket, environment=env, rail_length=5.2, inclination=85, heading=0, ) # Display results print(f"Apogee: {flight.apogee:.2f} m") print(f"Max velocity: {flight.max_velocity:.2f} m/s") print(f"Impact: ({flight.latitude_impact:.6f}, {flight.longitude_impact:.6f})")
Run:
python example_01_basic.py -
Explore RocketPy classes
- Read documentation: https://docs.rocketpy.org/
- Study Environment, Motor, Rocket, Flight classes
- Note differences from PyROPS structure
-
Run Jupyter notebook examples
jupyter notebook
- Navigate to RocketPy examples (if downloaded repository)
- Run basic trajectory example
- Run Monte Carlo example
Checkpoint 1.2: RocketPy basics understood
- Example simulation runs successfully
- Understand Environment, Motor, Rocket, Flight classes
- Can modify parameters and see results change
Deliverable: Notes on RocketPy API patterns in rocketpy_migration/docs/rocketpy_notes.md
Objective: Commit initial state and establish workflow
Steps:
-
Create initial commit
cd /Users/anban/opt/share/engineering-projects/pyrops-v0.1 # Stage documentation git add README.md git add .gitignore git add REQUIREMENTS_AND_SPECIFICATIONS.md git add EXISTING_SOLUTIONS_RESEARCH.md git add FINAL_RECOMMENDATION.md git add MIGRATION_ROADMAP.md # Stage folder structure git add rocketpy_migration/ # Commit git commit -m "Initial commit: Project analysis and migration setup - Add comprehensive requirements and specifications documentation - Add research on existing solutions (RocketPy, OpenRocket, MAPLEAF) - Add final recommendation to adopt RocketPy - Add migration roadmap - Initialize rocketpy_migration folder structure - Add .gitignore for Python projects"
-
Create development branch
git checkout -b feature/rocketpy-migration
-
Create branch strategy document
File:
.github/branching-strategy.md(ordocs/branching-strategy.md)# Git Branching Strategy ## Branches - `main`: Stable, production-ready code - `develop`: Integration branch for features - `feature/*`: Individual feature branches - `bugfix/*`: Bug fixes - `hotfix/*`: Emergency fixes for production ## Workflow 1. Create feature branch from `develop` 2. Work on feature, commit regularly 3. Test thoroughly 4. Merge back to `develop` when complete 5. Merge `develop` to `main` for releases ## Commit Message Format
:
```Types: feat, fix, docs, test, refactor, chore
-
Commit regularly
- Commit after each major step
- Use descriptive commit messages
- Push to remote repository (if using GitHub/GitLab)
Checkpoint 1.3: Version control established
- Initial commit created
- Feature branch created
- Branching strategy documented
- Understand Git workflow
Deliverable: Git repository with documented commit history
CRITICAL PHASE: This establishes validation criteria
Objective: Select representative test cases that cover all physics
Steps:
-
Select "Golden" Test Cases
Choose 3-5 simulations that:
- Have been validated against real flight data (if available)
- Cover different flight regimes
- Exercise all major features
Recommended test cases:
Test ID Description Why Selected BM-001 Baseline nominal flight Standard configuration, well-validated BM-002 High wind scenario Tests wind/aerodynamic interaction BM-003 Multi-stage separation Tests staging events BM-004 Drogue + main parachute Tests recovery system BM-005 Monte Carlo (100 runs) Tests statistical analysis -
Document test case parameters
Create:
rocketpy_migration/benchmarks/test_cases.md# Benchmark Test Cases ## BM-001: Baseline Nominal Flight **Description**: Standard Phoenix 1BIIr hybrid rocket simulation **Input Parameters**: - Launch Site: - Latitude: -34.600° - Longitude: 20.300° - Altitude: 0 m - Launch Conditions: - Elevation: 80.0° - Azimuth: -100.0° - Rail length: 7.0 m - Rocket Configuration: - Body radius: 0.087 m - Body length: 4.920 m - Dry mass: 49.35224149 kg - COM (dry): 1.386632 m - MOI (dry): Ixx=0.04023116, Iyy=180.8297, Izz=180.8297 kg⋅m² - Propulsion: - Motor type: Hybrid - Burn time: 17.1 s - Fuel density: 1065 kg/m³ - Oxidizer density: 880 kg/m³ - [Additional parameters...] - Aerodynamics: - Data source: RASAeroII.xlsx - Environment: - Atmosphere: atmosphere_data.xlsx - Wind: wind.xlsx - Recovery: - Main parachute CD: 2.2 - Main parachute diameter: 1.22 m - Deployment altitude: 13500 m (or time: 5 s after apogee) **Input Files** (archived in benchmarks/BM-001/): - thrust_curve.xlsx - mass_properties.xlsx - RASAeroII.xlsx - atmosphere_data.xlsx - wind.xlsx **Expected Outputs** (from PyROPS validation run): - Apogee altitude: [TBD - run PyROPS] - Max velocity: [TBD] - Max Mach: [TBD] - Max acceleration: [TBD] - Burnout altitude: [TBD] - Landing position: [TBD] - Landing velocity: [TBD] - Flight time: [TBD] **Acceptance Criteria**: - Apogee: Within 0.1% of PyROPS - Max velocity: Within 0.1% - Landing position: Within 10 m
Repeat for BM-002 through BM-005.
-
Archive input files for each test case
mkdir -p rocketpy_migration/benchmarks/BM-001 mkdir -p rocketpy_migration/benchmarks/BM-002 # etc. # Copy input files for BM-001 cp Inputs/thrust_curve_hybrid.xlsx rocketpy_migration/benchmarks/BM-001/ cp Inputs/mass_properties.xlsx rocketpy_migration/benchmarks/BM-001/ cp Inputs/RASAeroII.xlsx rocketpy_migration/benchmarks/BM-001/ cp Inputs/atmosphere_data.xlsx rocketpy_migration/benchmarks/BM-001/ cp Inputs/wind.xlsx rocketpy_migration/benchmarks/BM-001/
Checkpoint 2.1: Test cases selected and documented
- 3-5 test cases identified
- Parameters documented
- Input files archived
- Expected outputs defined (will be filled in Day 4)
Objective: Generate reference outputs from PyROPS
Steps:
-
Run PyROPS for each test case
IMPORTANT: Run PyROPS with EXACT parameters as documented
For BM-001:
- Launch PyROPS UI
- Load parameters from
benchmarks/BM-001/test_cases.md - Run simulation
- Record ALL outputs
-
Extract and record outputs
Create:
rocketpy_migration/benchmarks/BM-001/pyrops_reference.json{ "test_id": "BM-001", "test_name": "Baseline Nominal Flight", "pyrops_version": "v2.3.12", "run_date": "2025-10-19", "solver": "RK45 adaptive", "time_step": 0.02, "run_time_seconds": 245.3, "key_events": { "rail_departure": { "time": 0.83, "velocity": 24.5, "altitude": 7.0 }, "burnout": { "time": 17.1, "velocity": 325.4, "altitude": 2456.7 }, "apogee": { "time": 42.8, "altitude": 12543.2, "latitude": -34.5989, "longitude": 20.3012 }, "parachute_deployment": { "time": 47.8, "altitude": 11234.5 }, "landing": { "time": 487.2, "altitude": 0.0, "latitude": -34.5945, "longitude": 20.3034, "velocity": 6.5 } }, "maximum_values": { "max_velocity": 325.4, "max_acceleration": 45.2, "max_mach": 0.95, "max_dynamic_pressure": 15234.5, "max_angle_of_attack": 2.3 }, "landing": { "latitude": -34.5945, "longitude": 20.3034, "range_from_launch": 1234.5, "bearing_from_launch": 185.3, "impact_velocity": 6.5 }, "validation_data": { "minimum_stability_margin": 1.85, "rail_stability": "stable", "flight_regime": "subsonic", "warnings": [] } } -
Export full trajectory data
From PyROPS, export CSV with time-series data:
rocketpy_migration/benchmarks/BM-001/pyrops_trajectory.csvColumns should include:
- time
- altitude
- latitude, longitude
- velocity (N, E, D)
- velocity magnitude
- acceleration magnitude
- Mach number
- angle of attack
- etc.
-
Generate reference plots
Save plots from PyROPS:
- Altitude vs. time
- Velocity vs. time
- Trajectory (Lat/Lon)
Save in:
rocketpy_migration/benchmarks/BM-001/plots/ -
Repeat for all test cases
Checkpoint 2.2: PyROPS benchmarks complete
- All test cases run in PyROPS
- Key outputs recorded in JSON files
- Full trajectory CSV files exported
- Reference plots saved
- No warnings or errors in PyROPS runs
Deliverable: Complete benchmark dataset in rocketpy_migration/benchmarks/
Objective: Convert PyROPS data formats to RocketPy-compatible formats
Steps:
-
Create conversion script for RASAero data
File:
rocketpy_migration/src/convert_rasaero.py"""Convert RASAero Excel files to RocketPy CSV format""" import pandas as pd import numpy as np def convert_rasaero_to_csv(input_excel, output_csv): """ Convert RASAero Excel format to RocketPy CSV format RASAero columns (PyROPS): 0: Mach 1: Alpha (deg) 5: CD (axial coefficient) 8: CN (normal coefficient) 12: COP (inches) RocketPy CSV format: mach, alpha, CD, CL, COP (meters) """ print(f"Reading {input_excel}...") df = pd.read_excel(input_excel, header=0, engine='openpyxl') # Extract columns mach = df.iloc[:, 0].values alpha_deg = df.iloc[:, 1].values CA = df.iloc[:, 5].values # Axial coefficient CN = df.iloc[:, 8].values # Normal coefficient cop_inches = df.iloc[:, 12].values # Convert units cop_meters = cop_inches * 0.0254 # inches to meters # Create output dataframe output = pd.DataFrame({ 'Mach': mach, 'Alpha': alpha_deg, 'CA': CA, 'CN': CN, 'COP': cop_meters }) # Remove duplicates (if any) output = output.drop_duplicates() # Save to CSV output.to_csv(output_csv, index=False) print(f"Converted {len(output)} rows to {output_csv}") # Summary print(f" Mach range: {mach.min():.2f} to {mach.max():.2f}") print(f" Alpha range: {alpha_deg.min():.1f}° to {alpha_deg.max():.1f}°") print(f" COP range: {cop_meters.min():.3f} to {cop_meters.max():.3f} m") return output if __name__ == "__main__": # Convert standard RASAero files convert_rasaero_to_csv( '../../Inputs/RASAeroII.xlsx', '../data/aerodynamics/rasaero_0deg.csv' ) convert_rasaero_to_csv( '../../Inputs/RASAeroII15.xlsx', '../data/aerodynamics/rasaero_15deg.csv' ) # Convert nose and booster (if used) convert_rasaero_to_csv( '../../Inputs/RASAeroIINose.xlsx', '../data/aerodynamics/rasaero_nose.csv' ) convert_rasaero_to_csv( '../../Inputs/RASAeroIIBooster.xlsx', '../data/aerodynamics/rasaero_booster.csv' ) print("\nAerodynamic data conversion complete!")
Run:
python convert_rasaero.py -
Validate converted aerodynamic data
Create:
rocketpy_migration/tests/test_aero_conversion.py"""Test that aerodynamic data conversion is correct""" import pandas as pd import numpy as np def test_rasaero_conversion(): # Load original df_orig = pd.read_excel('../../Inputs/RASAeroII.xlsx', header=0) # Load converted df_conv = pd.read_csv('../data/aerodynamics/rasaero_0deg.csv') # Check lengths match assert len(df_orig) == len(df_conv), "Row count mismatch" # Check Mach values match mach_orig = df_orig.iloc[:, 0].values mach_conv = df_conv['Mach'].values np.testing.assert_array_almost_equal(mach_orig, mach_conv, decimal=5) # Check COP conversion (inches to meters) cop_orig_inches = df_orig.iloc[:, 12].values cop_conv_meters = df_conv['COP'].values cop_orig_meters = cop_orig_inches * 0.0254 np.testing.assert_array_almost_equal(cop_orig_meters, cop_conv_meters, decimal=6) print("✓ Aerodynamic data conversion validated") if __name__ == "__main__": test_rasaero_conversion()
Run:
python test_aero_conversion.py
Checkpoint 3.1: Aerodynamic data converted
- Conversion script created and tested
- CSV files generated
- Validation test passes
- Data spot-checked manually
Steps:
-
Convert thrust curves
"""Convert thrust curve data to RocketPy format""" import pandas as pd # Hybrid motor thrust curve df_thrust = pd.read_excel('../../Inputs/thrust_curve_hybrid.xlsx', header=0) # RocketPy expects: time (s), thrust (N), chamber_pressure (Pa) output = pd.DataFrame({ 'time': df_thrust['Time (s)'], 'thrust': df_thrust['Thrust (N)'], 'chamber_pressure': df_thrust['Chamber Pressure (Pa)'] }) output.to_csv('../data/propulsion/thrust_curve.csv', index=False)
-
Convert atmospheric data
"""Convert atmosphere data to RocketPy format""" import pandas as pd df_atm = pd.read_excel('../../Inputs/atmosphere_data.xlsx', header=None) # RocketPy Environment can use custom atmosphere output = pd.DataFrame({ 'altitude': df_atm.iloc[:, 0], 'temperature': df_atm.iloc[:, 1], 'pressure': df_atm.iloc[:, 2], 'density': df_atm.iloc[:, 3] }) output.to_csv('../data/environment/atmosphere.csv', index=False)
-
Convert wind profile
"""Convert wind data to RocketPy format""" import pandas as pd import numpy as np df_wind = pd.read_excel('../../Inputs/wind.xlsx', header=0) # PyROPS uses bearing (degrees), RocketPy uses direction # Convert magnitude and bearing to u (east) and v (north) components altitude = df_wind['altitude (m)'].values magnitude = df_wind['magnitude (m/s)'].values bearing = df_wind['bearing (degrees)'].values # Convert bearing to direction (meteorological convention) # PyROPS: counter-clockwise from North # RocketPy: standard meteorological (direction wind is FROM) u = magnitude * np.sin(np.radians(bearing)) # East component v = magnitude * np.cos(np.radians(bearing)) # North component output = pd.DataFrame({ 'altitude': altitude, 'wind_u': u, 'wind_v': v }) output.to_csv('../data/environment/wind_profile.csv', index=False)
-
Convert mass properties (if needed)
RocketPy calculates this automatically, but we can pre-compute if needed:
"""Convert mass properties data""" import pandas as pd df_mass = pd.read_excel('../../Inputs/mass_properties.xlsx', header=0) # RocketPy uses this during motor burn output = pd.DataFrame({ 'time': df_mass['time'], 'mass': df_mass['mass'], 'Ixx': df_mass['MOIx'], 'Iyy': df_mass['MOIy'], 'Izz': df_mass['MOIz'], 'center_of_mass': df_mass['centre-of-mass'] }) output.to_csv('../data/propulsion/mass_properties.csv', index=False)
Checkpoint 3.2: All data converted
- Thrust curve CSV created
- Atmosphere CSV created
- Wind profile CSV created
- Mass properties CSV created (if needed)
- All CSVs validated (spot-check values)
Deliverable: Complete dataset in rocketpy_migration/data/ folder
Objective: Implement standard simulation workflow in RocketPy
Steps:
-
Create main simulation script
File:
rocketpy_migration/src/simulate_rocket.py""" RocketPy simulation matching PyROPS BM-001 baseline """ from rocketpy import Environment, HybridMotor, Rocket, Flight import json def create_environment(): """Create environment matching PyROPS setup""" env = Environment( latitude=-34.600, longitude=20.300, elevation=0, date=(2025, 10, 19, 12) # Adjust as needed ) # Load custom atmosphere env.set_atmospheric_model( type='custom_atmosphere', pressure='../data/environment/atmosphere.csv', temperature='../data/environment/atmosphere.csv', ) # Load wind profile env.set_wind_velocity_model( file='../data/environment/wind_profile.csv' ) return env def create_motor(): """Create hybrid motor matching PyROPS setup""" motor = HybridMotor( thrust_source='../data/propulsion/thrust_curve.csv', burn_time=17.1, dry_mass=49.35224149, # Dry rocket mass dry_inertia=(180.8297, 180.8297, 0.04023116), # (I_yy, I_zz, I_xx) center_of_dry_mass_position=1.386632, grain_number=1, grain_density=1065, # Fuel density grain_outer_radius=0.0735, grain_initial_inner_radius=0.0735 - 0.0375, # R - thickness grain_initial_height=0.510, grain_separation=0, grains_center_of_mass_position=0.5395, nozzle_radius=0.0475, # Derived from exit area throat_radius=0.03, # Estimate oxidizer_tank_position=2.20745, # Additional parameters... ) return motor def create_rocket(motor): """Create rocket matching PyROPS geometry""" rocket = Rocket( radius=0.087, mass=49.35224149 + 15.0, # Dry + propellant (estimate) inertia=(180.8297, 180.8297, 0.04023116), power_off_drag='../data/aerodynamics/rasaero_0deg.csv', power_on_drag='../data/aerodynamics/rasaero_0deg.csv', center_of_mass_without_motor=1.386632, coordinate_system_orientation="nose_to_tail", ) # Add motor rocket.add_motor(motor, position=0) # Adjust position # Add fins (matching PyROPS geometry) rocket.add_trapezoidal_fins( n=4, span=0.12, # FinSpan from PyROPS root_chord=0.25, # FinRootChord tip_chord=0.10, # FinTipChord sweep_length=0.05, # FinSweep position=-3.5, # FinLocation cant_angle=0.0, # FinCantAngle (degrees) ) # Add parachutes rocket.add_parachute( name='Main', cd_s=2.2 * (1.22/2)**2 * 3.14159, # CD * area trigger='apogee', sampling_rate=105, lag=5.0, # Delay after apogee noise=(0, 0, 0), ) return rocket def run_simulation(): """Run complete simulation""" print("Creating environment...") env = create_environment() print("Creating motor...") motor = create_motor() print("Creating rocket...") rocket = create_rocket(motor) print("Running flight simulation...") flight = Flight( rocket=rocket, environment=env, rail_length=7.0, inclination=80.0, # Launch elevation heading=-100.0, # Launch azimuth (PyROPS convention) max_time=1200, # 20 minutes ) # Extract results results = { 'apogee': flight.apogee, 'apogee_time': flight.apogee_time, 'max_velocity': flight.max_velocity, 'max_acceleration': flight.max_acceleration, 'latitude_impact': flight.latitude_impact, 'longitude_impact': flight.longitude_impact, 'impact_velocity': flight.impact_velocity, } print("\n" + "="*50) print("SIMULATION RESULTS") print("="*50) for key, value in results.items(): print(f"{key}: {value}") # Export data flight.export_data('../outputs/flight_data.csv') # Save results with open('../outputs/results.json', 'w') as f: json.dump(results, f, indent=2) return flight, results if __name__ == "__main__": flight, results = run_simulation()
Run:
python simulate_rocket.py -
Debug and iterate
- Fix import errors
- Adjust parameters as needed
- Verify simulation completes without errors
Checkpoint 4.1: Baseline simulation runs
- Script executes without errors
- Simulation completes
- Results file generated
- Results seem physically reasonable (apogee > 0, etc.)
Steps:
-
Compare with PyROPS benchmark BM-001
Create:
rocketpy_migration/tests/validate_bm001.py"""Validate RocketPy results against PyROPS benchmark BM-001""" import json import pandas as pd # Load PyROPS reference with open('../benchmarks/BM-001/pyrops_reference.json') as f: pyrops = json.load(f) # Load RocketPy results with open('../outputs/results.json') as f: rocketpy = json.load(f) def compare_value(name, pyrops_val, rocketpy_val, tolerance_pct=0.1): """Compare two values and report difference""" diff = abs(pyrops_val - rocketpy_val) diff_pct = (diff / pyrops_val) * 100 if pyrops_val != 0 else 0 status = "✓ PASS" if diff_pct <= tolerance_pct else "✗ FAIL" print(f"{name:30s} | PyROPS: {pyrops_val:12.2f} | RocketPy: {rocketpy_val:12.2f} | Diff: {diff_pct:6.3f}% | {status}") return diff_pct <= tolerance_pct print("="*100) print("VALIDATION: RocketPy vs PyROPS Benchmark BM-001") print("="*100) print(f"{'Parameter':<30s} | {'PyROPS':<12s} | {'RocketPy':<12s} | {'Diff %':<6s} | Status") print("-"*100) tests = [ ("Apogee (m)", pyrops['key_events']['apogee']['altitude'], rocketpy['apogee']), ("Max Velocity (m/s)", pyrops['maximum_values']['max_velocity'], rocketpy['max_velocity']), ("Max Acceleration (m/s²)", pyrops['maximum_values']['max_acceleration'], rocketpy['max_acceleration']), ("Landing Latitude (deg)", pyrops['landing']['latitude'], rocketpy['latitude_impact']), ("Landing Longitude (deg)", pyrops['landing']['longitude'], rocketpy['longitude_impact']), ("Impact Velocity (m/s)", pyrops['landing']['impact_velocity'], rocketpy['impact_velocity']), ] results = [compare_value(name, pval, rval) for name, pval, rval in tests] print("="*100) if all(results): print("✓✓✓ ALL TESTS PASSED ✓✓✓") print("RocketPy validation successful! Physics preserved.") else: print("✗✗✗ SOME TESTS FAILED ✗✗✗") print("Investigate differences. May need parameter tuning.") print("="*100)
Run:
python validate_bm001.py -
Iterate until validation passes
If tests fail:
- Review parameter mapping (PyROPS → RocketPy)
- Check unit conversions
- Verify data file conversions
- Consult RocketPy documentation
- Ask RocketPy community if needed
DO NOT PROCEED until validation passes (or differences are explained and accepted)
Checkpoint 4.2: Validation passes
- All key metrics within 0.1% of PyROPS
- Differences documented and explained
- Physics team approves results
CRITICAL GATE: Do not proceed to Day 9 until validation passes
Steps:
-
Create Monte Carlo simulation script
File:
rocketpy_migration/src/monte_carlo_sim.py"""Monte Carlo simulation matching PyROPS setup""" from rocketpy import Environment, HybridMotor, Rocket, Flight, MonteCarlo import json # Import environment, motor, rocket from simulate_rocket.py from simulate_rocket import create_environment, create_motor, create_rocket def run_monte_carlo(n_simulations=100): """Run Monte Carlo with uncertainties matching PyROPS""" # Base environment, motor, rocket env = create_environment() motor = create_motor() rocket = create_rocket(motor) # Create Monte Carlo mc = MonteCarlo( filename='../outputs/monte_carlo', environment=env, rocket=rocket, rail_length=(7.0, 0.1), # (mean, std) inclination=(80.0, 2.0), # Launch elevation uncertainty heading=(-100.0, 5.0), # Launch azimuth uncertainty ) # Add parameter uncertainties (match PyROPS MC parameters) # Example: motor thrust variation mc.set_motor_thrust_variation(variation=0.05) # 5% variation # Run simulations print(f"Running {n_simulations} Monte Carlo simulations...") mc.simulate(number_of_simulations=n_simulations) # Generate plots mc.plots.ellipses() # Landing dispersion mc.plots.apogee() # Apogee distribution # Export results mc.export_data('../outputs/monte_carlo_results.csv') print(f"Monte Carlo complete! Results saved.") return mc if __name__ == "__main__": mc = run_monte_carlo(n_simulations=100)
Run:
python monte_carlo_sim.py -
Validate Monte Carlo results
- Compare landing dispersion with PyROPS
- Compare apogee distribution
- Verify statistics (mean, std dev)
Checkpoint 4.3: Monte Carlo implemented
- Monte Carlo simulation runs
- Results statistically similar to PyROPS
- Landing dispersion plot generated
- Performance acceptable (should be much faster than PyROPS)
Objective: Complete validation across all benchmark test cases
Steps:
-
Create validation test suite
File:
rocketpy_migration/tests/run_all_validations.py"""Run all benchmark validations""" import os import json # Test cases test_cases = ['BM-001', 'BM-002', 'BM-003', 'BM-004', 'BM-005'] results = {} for test_id in test_cases: print(f"\n{'='*80}") print(f"Running validation for {test_id}") print('='*80) # Load test configuration # Run simulation # Compare with benchmark # Record results # Placeholder (implement actual validation) results[test_id] = { 'passed': True, # or False 'differences': {} } # Summary report print(f"\n{'='*80}") print("VALIDATION SUMMARY") print('='*80) for test_id, result in results.items(): status = "✓ PASS" if result['passed'] else "✗ FAIL" print(f"{test_id}: {status}") # Save report with open('../outputs/validation_report.json', 'w') as f: json.dump(results, f, indent=2)
-
Run validation suite
cd rocketpy_migration/tests python run_all_validations.py -
Document results
- Create validation report
- Note any discrepancies
- Get physics team approval
Checkpoint 5.1: All benchmarks validated
- All test cases run successfully
- Results within acceptance criteria
- Validation report generated
- Physics team sign-off obtained
Steps:
-
Measure performance improvements
"""Performance comparison: RocketPy vs PyROPS""" import time # Time single simulation start = time.time() flight = run_simulation() single_time = time.time() - start print(f"Single simulation: {single_time:.2f} seconds") # Time Monte Carlo (100 runs) start = time.time() mc = run_monte_carlo(100) mc_time = time.time() - start print(f"Monte Carlo (100 runs): {mc_time:.2f} seconds") print(f" Per simulation: {mc_time/100:.2f} seconds")
-
Compare with PyROPS
- PyROPS: ~1-5 minutes per simulation
- RocketPy: Expected <10 seconds
- Speedup: 10-30x
Checkpoint 5.2: Performance validated
- RocketPy is faster than PyROPS
- Monte Carlo runs in reasonable time
- Performance meets expectations
If needed: Implement RCS thruster control or other PyROPS-specific features
File: rocketpy_migration/src/rcs_controller.py
"""Custom RCS thruster roll control for RocketPy"""
# Implementation of RCS control matching PyROPS behavior
# (Only if needed - may not be required for initial deployment)Steps:
- Create plotting utilities matching PyROPS style
- Optimize data loading and caching
- Create batch simulation scripts
- Performance tuning if needed
Steps:
-
Create user guide
File:
rocketpy_migration/docs/USER_GUIDE.md# RocketPy User Guide for [Team Name] ## Quick Start ### Running a Standard Simulation ```bash cd rocketpy_migration/src python simulate_rocket.py
python monte_carlo_sim.py
[How to modify parameters...]
[Common issues and solutions...]
-
Conduct team training
- Hands-on session
- Q&A
- Distribute documentation
-
Final Git commit
git add . git commit -m "feat: Complete RocketPy migration - Implement all benchmark test cases - Validate against PyROPS (all tests pass) - Achieve 10-30x performance improvement - Complete documentation and training Ready for production use." git checkout main git merge feature/rocketpy-migration git tag v1.0.0 -m "RocketPy migration complete"
| Checkpoint | Description | Recovery Action if Failed |
|---|---|---|
| 1.3 | Git setup complete | Review Git tutorial, get help |
| 2.2 | PyROPS benchmarks complete | Cannot proceed - must have reference data |
| 3.2 | Data conversion complete | Fix conversion scripts, re-run |
| 4.2 | CRITICAL: Validation passes | STOP. Debug until passing. Do not proceed. |
| 5.1 | All benchmarks validated | Investigate failures, adjust parameters |
| 5.2 | Performance validated | Acceptable - proceed even if not optimal |
If validation fails (Checkpoint 4.2):
- Check parameter mapping (PyROPS → RocketPy)
- Verify unit conversions (inches → meters, etc.)
- Review data file conversions (re-run conversion scripts)
- Compare coordinate systems and sign conventions
- Consult RocketPy documentation
- Post question to RocketPy GitHub issues
- Consult with physics team
- DO NOT SKIP VALIDATION
If stuck on any phase:
- Review this roadmap section
- Check RocketPy documentation: https://docs.rocketpy.org/
- Search RocketPy GitHub issues: https://github.com/RocketPy-Team/RocketPy/issues
- Ask RocketPy community (very responsive)
- Document issue for team discussion
Issue: RocketPy simulation crashes
- Solution: Check for NaN or Inf values in data files, verify units
Issue: Results don't match PyROPS
- Solution: Verify parameter mapping, check coordinate system conventions
Issue: Monte Carlo takes too long
- Solution: Reduce number of simulations for testing, optimize later
Issue: Aerodynamic data import error
- Solution: Verify CSV format, check for missing values
- All benchmark tests pass validation (within 0.1%)
- Physics team approves results
- Performance improvement demonstrated (faster than PyROPS)
- Documentation complete
- Version control established
- Monte Carlo results match PyROPS statistics
- User guide created
- Team trained
- All code committed to Git
- RCS control implemented
- Custom plotting utilities
- Optimization for maximum performance
| Phase | Days | Critical Path | Can Skip if Time-Limited? |
|---|---|---|---|
| 1. Setup | 1-2 | Yes | No |
| 2. Benchmarking | 3-4 | Yes | No - Critical |
| 3. Data Conversion | 5-6 | Yes | No |
| 4. Implementation | 7-9 | Yes | No - Critical |
| 5. Validation | 10-11 | Yes | No - Critical |
| 6. Extensions | 12-14 | No | Yes (defer to later) |
| 7. Documentation | 15 | No | No (but can be brief) |
Minimum Viable Migration: Phases 1-5 (11 days) Full Migration: Phases 1-7 (15 days)
Immediate Actions (right now):
- Review this roadmap with team
- Assign roles (who does what)
- Schedule kickoff meeting
- Begin Day 1: Install RocketPy
Before Starting:
- Team has read and understood this roadmap
- Calendar time blocked (2-3 weeks)
- Stakeholders informed
- PyROPS is currently functional (for benchmarking)
- Commitment obtained to complete validation rigorously
Let's begin! Start with Phase 1, Day 1 Morning. Good luck!
Document Version: 1.0 Created: 2025-10-19 Status: Active Migration Plan