Skip to content

RocketPy Integration

Machwave can drive RocketPy flight simulations by adapting a Machwave motor and its simulation result into a RocketPy-compatible motor object. The integration is intended for workflows where Machwave computes the internal ballistics and RocketPy handles the 6-DOF trajectory, aerodynamics, and recovery simulation.

Installation

Install the optional RocketPy dependency:

pip install machwave[rocketpy]

Integration Workflow

The adapter sits after the internal ballistics simulation:

  1. Build a Machwave motor model.
  2. Run InternalBallisticsSimulation to obtain a SimulationResult.
  3. Wrap the motor and result with RocketPySolidMotorAdapter(motor=motor, simulation_result=result).
  4. Pass the adapted motor into a RocketPy Rocket.

Example

from machwave import (
    formulations,
    grain as grain_models,
    motors,
    simulation,
    thrust_chamber as thrust_chamber_models,
)
from machwave.adapters.rocketpy import RocketPySolidMotorAdapter
from rocketpy import Environment, Flight, Rocket

propellant = formulations.solid.KNSB_NAKKA

grain = grain_models.Grain(spacing=0.01)
segment = grain_models.geometries.BatesSegment(
    outer_diameter=0.085,
    core_diameter=0.035,
    length=0.150,
)
for _ in range(4):
    grain.add_segment(segment)

nozzle = thrust_chamber_models.Nozzle(
    inlet_diameter=0.080,
    throat_diameter=0.022,
    divergent_angle=12,
    convergent_angle=45,
    expansion_ratio=8,
)

combustion_chamber = thrust_chamber_models.CombustionChamber(
    casing_inner_diameter=95.25e-3,
    casing_outer_diameter=101.6e-3,
    thermal_liner_thickness=3e-3,
    internal_length=grain.total_length + 0.01,
)

thrust_chamber = thrust_chamber_models.SolidMotorThrustChamber(
    dry_mass=6.0,
    nozzle=nozzle,
    combustion_chamber=combustion_chamber,
    nozzle_exit_to_grain_port_distance=0.01,
    center_of_gravity_coordinate=(0.35, 0.0, 0.0),
)

motor = motors.SolidMotor(
    grain=grain,
    propellant=propellant,
    thrust_chamber=thrust_chamber,
)

params = simulation.InternalBallisticsSimulationParams(
    d_t=0.01,
    igniter_pressure=1e6,
    external_pressure=1.013e5,
)

result = simulation.InternalBallisticsSimulation(
    motor=motor,
    params=params,
).run()

rocketpy_motor = RocketPySolidMotorAdapter(motor=motor, simulation_result=result)

rocket = Rocket(
    radius=0.0508,
    mass=15.0,
    inertia=(6.0, 6.0, 0.035),
    power_off_drag=0.5,
    power_on_drag=0.5,
    center_of_mass_without_motor=0.0,
)
rocket.add_motor(rocketpy_motor, position=-0.6)

env = Environment(latitude=32.99, longitude=-106.975, elevation=1400)
env.set_atmospheric_model(type="standard_atmosphere")

flight = Flight(
    rocket=rocket,
    environment=env,
    rail_length=5.2,
    inclination=85,
    heading=0,
)

For a full runnable script, see examples/rocketpy_srm_simulation.py.

Data Passed To RocketPy

RocketPyMotorAdapter builds the RocketPy motor inputs from the Machwave motor and its simulation result. The adapter currently provides:

  • thrust curve from Machwave time history
  • burn-time interval
  • dry mass and dry-mass center of gravity
  • nozzle radius and throat radius
  • propellant initial mass
  • propellant center of mass over time
  • propellant inertia tensor components over time
  • reference pressure from the exit-pressure trace

Current Scope And Limitations

  • RocketPySolidMotorAdapter is the currently implemented adapter.
  • RocketPy must be installed, otherwise adapter import or initialization raises ImportError.
  • The solid-motor adapter assumes RocketPy's segmented-grain model and uses the first Machwave grain segment as the representative geometry.
  • Dry inertia is currently passed as (0.0, 0.0, 0.0).
  • The coordinate system orientation is nozzle_to_combustion_chamber.

API Reference

machwave.adapters.rocketpy

RocketPySolidMotorAdapter

Bases: RocketPyMotorAdapter['solid_results.SolidSimulationResult']

Adapter to use a simulation result and motor as a RocketPy SolidMotor.

Source code in machwave/adapters/rocketpy/solid_motor.py
class RocketPySolidMotorAdapter(
    rocketpy_base.RocketPyMotorAdapter["solid_results.SolidSimulationResult"]
):
    """Adapter to use a simulation result and motor as a RocketPy SolidMotor."""

    _rocketpy_motor_class = "SolidMotor"

    def _get_rocketpy_attributes(self) -> dict[str, typing.Any]:
        base_attrs = super()._get_rocketpy_attributes()

        motor = typing.cast("motors_models.SolidMotor", self.motor)
        grain = motor.grain

        if not grain.segments:
            raise ValueError("Grain must have at least one segment")

        # RocketPy's SolidMotor assumes all segments are identical
        mismatches = grain.get_segment_mismatches()
        if mismatches:
            raise ValueError(
                "RocketPy SolidMotor requires all grain segments to be "
                "identical, but the following mismatches were found: "
                + "; ".join(mismatches)
            )

        first_segment = grain.segments[0]

        # RocketPy's SolidMotor only supports a BATES geometry
        if not isinstance(first_segment, grain_geometries.BatesSegment):
            raise ValueError(
                "RocketPy SolidMotor only supports BATES grain geometry, got "
                f"{type(first_segment).__name__}"
            )

        grain_number = grain.segment_count
        grain_separation = grain.spacing
        grain_outer_radius = first_segment.outer_diameter / 2
        grain_initial_height = first_segment.length
        grain_initial_inner_radius = first_segment.core_diameter / 2
        throat_radius = motor.thrust_chamber.nozzle.throat_diameter / 2
        grains_center_of_mass_position = motor.grain.get_center_of_gravity(
            web_distance=0.0
        )[0]

        # RocketPy handles real density internally
        grain_density = motor.propellant.ideal_density

        # Combine base attributes with grain specific parameters
        solid_motor_attrs = {
            **base_attrs,
            "grain_number": grain_number,
            "grain_density": grain_density,
            "grain_outer_radius": grain_outer_radius,
            "grain_initial_inner_radius": grain_initial_inner_radius,
            "grain_initial_height": grain_initial_height,
            "grain_separation": grain_separation,
            "grains_center_of_mass_position": grains_center_of_mass_position,
            "throat_radius": throat_radius,
        }

        return solid_motor_attrs