Running simulations¶
All snippets on this page go inside the with orhelper.OpenRocketInstance() as instance: block, with
helper = orhelper.Helper(instance); see How orhelper works.
Load a document and pick a simulation¶
document = helper.load_doc("my_rocket.ork") # a str or a pathlib.Path
rocket = document.getRocket()
simulation = document.getSimulation(0) # the first simulation in the file
A .ork file can hold several simulations: document.getSimulationCount() tells you how many, and
document.getSimulation(n) returns the n-th. For a missing or unreadable file, OpenRocket raises its Java
RocketLoadException.
Run it¶
The simulation uses the options it has at that moment (see Changing the rocket and launch). The results stay in the simulation object. Running it again replaces them.
Read the results as arrays¶
get_timeseries returns a NumPy array for each variable you ask for,
with one value for every time step:
from orhelper import FlightDataType
data = helper.get_timeseries(simulation, [FlightDataType.TYPE_TIME,
FlightDataType.TYPE_ALTITUDE,
FlightDataType.TYPE_VELOCITY_TOTAL])
time = data[FlightDataType.TYPE_TIME]
altitude = data[FlightDataType.TYPE_ALTITUDE]
Choose variables from FlightDataType; the
reference lists all of them, with their unit. You can also pass the name as
a string ("TYPE_ALTITUDE"). Asking for a variable that your OpenRocket version doesn't have raises an
AttributeError.
Statistics: nan
Some series contain nan, for example velocities at the first time step. Use np.nanmax(...) and
np.nanmin(...), because max() of an array with a nan is nan.
get_final_values gives you just the last value of each variable,
for example where the rocket landed:
landing = helper.get_final_values(simulation, [FlightDataType.TYPE_POSITION_X,
FlightDataType.TYPE_POSITION_Y])
Events¶
get_events tells you when things happened. It returns a dictionary from
FlightEvent to the list of times, in seconds, at which the event occurred:
from orhelper import FlightEvent
events = helper.get_events(simulation)
apogee_time = events[FlightEvent.APOGEE][0]
OpenRocket's own statistics¶
The simulation holds the summary numbers that OpenRocket shows in its user interface. They are Java
methods of simulation.getSimulatedData():
flight = simulation.getSimulatedData()
flight.getMaxAltitude() # m
flight.getMaxVelocity() # m/s
flight.getMaxAcceleration() # m/s²
flight.getTimeToApogee() # s
flight.getFlightTime() # s
flight.getLaunchRodVelocity() # m/s
flight.getGroundHitVelocity() # m/s
A complete example¶
This script prints a table of events with the altitude at each, plus these statistics and the landing distance:
"""Print the flight events and a few summary numbers for a simulation.
Shows how to read the events (``get_events``), look up a value at a given time, and
use OpenRocket's own summary statistics from the Java ``FlightData`` object.
"""
import numpy as np
import orhelper
from orhelper import FlightDataType
TIME = FlightDataType.TYPE_TIME
ALTITUDE = FlightDataType.TYPE_ALTITUDE
with orhelper.OpenRocketInstance(log_level="ERROR") as instance:
orh = orhelper.Helper(instance)
doc = orh.load_doc(orhelper.sample_ork_path())
sim = doc.getSimulation(0)
orh.run_simulation(sim)
data = orh.get_timeseries(sim, [TIME, ALTITUDE])
events = orh.get_events(sim) # {FlightEvent: [time in s, ...]}; an event can happen more than once
# One row per event occurrence, in chronological order, with the altitude at that time.
occurrences = sorted(((t, event) for event, times in events.items() for t in times), key=lambda row: row[0])
print(f"{'Event':<28}{'Time (s)':>9}{'Altitude (m)':>14}")
for t, event in occurrences:
altitude = np.interp(t, data[TIME], data[ALTITUDE])
print(f"{event.name:<28}{t:>9.2f}{altitude:>14.1f}")
# OpenRocket's own summary statistics, straight from the Java FlightData object.
flight = sim.getSimulatedData()
print()
print(f"Max altitude: {flight.getMaxAltitude():.1f} m")
print(f"Max velocity: {flight.getMaxVelocity():.1f} m/s")
print(f"Max acceleration: {flight.getMaxAcceleration():.1f} m/s^2")
print(f"Time to apogee: {flight.getTimeToApogee():.2f} s")
print(f"Ground hit velocity: {flight.getGroundHitVelocity():.2f} m/s")
# Where did it land? The final value of the position series.
landing = orh.get_final_values(sim, [FlightDataType.TYPE_POSITION_X, FlightDataType.TYPE_POSITION_Y])
distance = np.hypot(landing[FlightDataType.TYPE_POSITION_X], landing[FlightDataType.TYPE_POSITION_Y])
print(f"Landing distance: {distance:.1f} m from the launch pad")
Multistage rockets¶
A multistage flight has one branch per separated part: branch 0 is the sustainer and the others are
boosters. get_timeseries,
get_final_values and
get_events all take a branch_number:
flight = simulation.getSimulatedData()
for branch_number in range(flight.getBranchCount()):
data = helper.get_timeseries(simulation, [FlightDataType.TYPE_ALTITUDE], branch_number)
events = helper.get_events(simulation, branch_number)
The full example below uses a two-stage rocket from the examples that come with OpenRocket:
"""Simulate a multistage rocket and read the data of each flight branch.
A multistage simulation has one *branch* per separated part: branch 0 is the sustainer,
the others are boosters. ``get_timeseries``, ``get_final_values`` and ``get_events`` all
take a ``branch_number``.
The example rocket is one of the examples bundled inside the OpenRocket jar. Use your
own multistage .ork file with ``orh.load_doc("my_rocket.ork")``.
"""
import tempfile
from pathlib import Path
from zipfile import ZipFile
import numpy as np
import orhelper
from orhelper import FlightDataType
def extract_two_stage_example(jar, directory):
"""Copy the 'Two stage' example rocket out of the OpenRocket jar and return its path."""
with ZipFile(str(jar)) as archive:
name = next(n for n in archive.namelist()
if n.startswith("datafiles/examples/") and n.endswith(".ork")
and ("Two stage" in n or "Two-stage" in n))
path = Path(directory) / "two_stage.ork"
path.write_bytes(archive.read(name))
return path
def branch_name(branch):
# The Java method was renamed from getBranchName() to getName() in OpenRocket 24.12.
getter = getattr(branch, "getName", None) or branch.getBranchName
return str(getter())
with orhelper.OpenRocketInstance(log_level="ERROR") as instance:
orh = orhelper.Helper(instance)
with tempfile.TemporaryDirectory() as directory:
doc = orh.load_doc(str(extract_two_stage_example(instance.jar, directory)))
sim = doc.getSimulation(0)
orh.run_simulation(sim)
flight = sim.getSimulatedData()
for branch_number in range(flight.getBranchCount()):
data = orh.get_timeseries(sim, [FlightDataType.TYPE_ALTITUDE], branch_number)
events = orh.get_events(sim, branch_number)
print(f"Branch {branch_number} ({branch_name(flight.getBranch(branch_number))}): "
f"apogee {np.nanmax(data[FlightDataType.TYPE_ALTITUDE]):.0f} m")
for event, times in events.items():
print(f" {event.name:<28} t = {times[0]:.1f} s")