Changing the rocket and launch¶
You change a simulation by calling the setters of its Java objects, and then run it again.
All snippets go inside the with orhelper.OpenRocketInstance() as instance: block, with
helper = orhelper.Helper(instance), a loaded document and simulation.
Launch conditions¶
The conditions live in the simulation's options:
import math
options = simulation.getOptions()
options.setLaunchRodAngle(math.radians(5)) # radians from vertical
options.setLaunchRodDirection(math.radians(90)) # radians
options.setWindSpeedAverage(4.0) # m/s
options.setWindDirection(math.radians(90)) # radians
options.setWindTurbulenceIntensity(0.1) # 0 is no turbulence
options.setLaunchLatitude(45.0) # degrees
options.setLaunchLongitude(10.0) # degrees
Remember: angles are in radians, except latitude and longitude, whose setters take degrees. Use
dir(options) to see everything else you can set, or read the OpenRocket source (see
How orhelper works).
The options stay as you set them. The next run_simulation uses them, so a
loop that changes one option and runs the simulation is a parameter sweep.
Components of the rocket¶
get_component_named finds a part of the rocket by its name, and
then you use its Java getters and setters, for example to override its mass (in kg):
rocket = document.getRocket()
nose = helper.get_component_named(rocket, "Nose cone")
nose.setOverrideMass(nose.getMass() + 0.020) # 20 g heavier
nose.setMassOverridden(True)
To go through every component, use JIterator:
Save your changes¶
The changes are made to the document in memory. To keep them, save it:
A complete example¶
This script runs the sample rocket, makes the nose cone heavier, changes the launch conditions, and saves the result. After each change it prints the apogee:
"""Change the rocket and the launch conditions, compare the results, and save a new .ork file.
Usage: python edit_and_save.py [output.ork] (default: a file in your temp directory)
OpenRocket objects are Java objects: ``get_component_named`` finds a component and you
call its Java getters and setters directly. Angles are in radians, masses in kg.
"""
import math
import sys
import tempfile
from pathlib import Path
import numpy as np
import orhelper
from orhelper import FlightDataType
output = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(tempfile.gettempdir()) / "simple_edited.ork"
with orhelper.OpenRocketInstance(log_level="ERROR") as instance:
orh = orhelper.Helper(instance)
doc = orh.load_doc(orhelper.sample_ork_path())
sim = doc.getSimulation(0)
rocket = doc.getRocket()
def apogee():
orh.run_simulation(sim)
altitude = orh.get_timeseries(sim, [FlightDataType.TYPE_ALTITUDE])[FlightDataType.TYPE_ALTITUDE]
return np.max(altitude)
print(f"Original rocket: apogee {apogee():.1f} m")
# Make the nose cone 20 g heavier by overriding its computed mass.
nose = orh.get_component_named(rocket, "Nose cone")
nose.setOverrideMass(nose.getMass() + 0.020)
nose.setMassOverridden(True)
print(f"With a heavier nose cone: apogee {apogee():.1f} m")
# Launch 10 degrees off vertical into a 5 m/s wind.
options = sim.getOptions()
options.setLaunchRodAngle(math.radians(10))
options.setWindSpeedAverage(5.0)
print(f"Tilted launch, 5 m/s wind: apogee {apogee():.1f} m")
# Save the modified document and load it back to check that the changes were kept.
orh.save_doc(str(output), doc)
reloaded = orh.load_doc(str(output))
reloaded_nose = orh.get_component_named(reloaded.getRocket(), "Nose cone")
print(f"Saved to {output}; nose cone mass in the saved file: {reloaded_nose.getMass() * 1000:.0f} g")
Parameter sweeps¶
To explore a range of conditions, change an option in a loop and collect the results. This example sweeps
the wind speed and the launch angle, and writes the results to a CSV file you can open in a spreadsheet or
with pandas.read_csv:
"""Sweep the wind speed and launch angle and write the results to a CSV file.
Usage: python parameter_sweep.py [results.csv] (default: a file in your temp directory)
Load the result with ``pandas.read_csv("results.csv")`` for further analysis.
"""
import csv
import math
import sys
import tempfile
from pathlib import Path
import numpy as np
import orhelper
from orhelper import FlightDataType
WIND_SPEEDS = [0, 2, 4, 6, 8, 10] # m/s
LAUNCH_ANGLES = [0, 5, 10] # degrees from vertical
output = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(tempfile.gettempdir()) / "parameter_sweep.csv"
rows = []
with orhelper.OpenRocketInstance(log_level="ERROR") as instance:
orh = orhelper.Helper(instance)
doc = orh.load_doc(orhelper.sample_ork_path())
sim = doc.getSimulation(0)
options = sim.getOptions()
for wind in WIND_SPEEDS:
for angle in LAUNCH_ANGLES:
options.setWindSpeedAverage(wind)
options.setLaunchRodAngle(math.radians(angle))
orh.run_simulation(sim)
data = orh.get_timeseries(sim, [FlightDataType.TYPE_TIME, FlightDataType.TYPE_ALTITUDE,
FlightDataType.TYPE_POSITION_X, FlightDataType.TYPE_POSITION_Y])
rows.append({
"wind_speed_m_s": wind,
"launch_angle_deg": angle,
"apogee_m": round(float(np.max(data[FlightDataType.TYPE_ALTITUDE])), 1),
"flight_time_s": round(float(data[FlightDataType.TYPE_TIME][-1]), 1),
"landing_distance_m": round(float(np.hypot(data[FlightDataType.TYPE_POSITION_X][-1],
data[FlightDataType.TYPE_POSITION_Y][-1])), 1),
})
with open(output, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0]))
writer.writeheader()
writer.writerows(rows)
print(f"{'wind':>6}{'angle':>7}{'apogee':>8}{'time':>7}{'landing':>9}")
for row in rows:
print(f"{row['wind_speed_m_s']:>6}{row['launch_angle_deg']:>7}{row['apogee_m']:>8}"
f"{row['flight_time_s']:>7}{row['landing_distance_m']:>9}")
print(f"\nWrote {len(rows)} simulations to {output}")