Skip to content

Simulations

machwave.simulations

BallisticSimulation

Bases: Simulation

Ballistic simulation class.

Attributes:

Name Type Description
rocket

Rocket object.

atmosphere

Atmosphere object.

params BallisticSimulationParameters

Simulation parameters.

t

Array of time values.

ballistic_state

Ballistic state object.

Source code in machwave/simulations/ballistics.py
class BallisticSimulation(Simulation):
    """Ballistic simulation class.

    Attributes:
        rocket: Rocket object.
        atmosphere: Atmosphere object.
        params: Simulation parameters.
        t: Array of time values.
        ballistic_state: Ballistic state object.
    """

    def __init__(
        self,
        rocket: Rocket,
        atmosphere: Atmosphere,
        params: BallisticSimulationParameters,
    ) -> None:
        """Initialize the BallisticSimulation instance.

        Args:
            rocket: Rocket object.
            atmosphere: Atmosphere object.
            params: Simulation parameters.
        """
        super().__init__(params=params)

        self.params: BallisticSimulationParameters = (
            params  # explicitly defining the type of params to avoid pyright error
        )

        self.rocket = rocket
        self.atmosphere = atmosphere
        self.t = np.array([0])
        self.ballistic_state = None

    def get_propellant_mass(self) -> np.ndarray:
        """Compute the propellant mass at each time step.

        Returns:
            Array of propellant mass values.
        """
        initial_propellant_mass = self.params.initial_propellant_mass
        prop_mass = np.array([])
        time = self.params.time

        for t in time:
            prop_mass = np.append(
                prop_mass, initial_propellant_mass * (time[-1] - t) / time[-1]
            )

        return prop_mass

    def run(self) -> tuple:
        """
        Runs the main loop of the simulation, returning the time array and
        the ballistic state object.

        Returns:
            tuple[np.array, Ballistic1DState]: A tuple containing the time
            array and the ballistic state object.
        """
        self.ballistic_state = Ballistic1DState(
            self.rocket,
            self.atmosphere,
            rail_length=self.params.rail_length,
            motor_dry_mass=self.rocket.propulsion.get_dry_mass(),
            initial_vehicle_mass=self.rocket.get_launch_mass(),
            initial_elevation_amsl=self.params.initial_elevation_amsl,
        )

        propellant_mass = self.get_propellant_mass()

        i = 0

        while self.ballistic_state.y[i] >= 0:
            self.t = np.append(self.t, self.t[i] + self.params.d_t)  # new time value

            thrust = np.interp(
                self.t[-1],
                self.params.time,
                self.params.thrust,
                left=0,
                right=0,
            )  # interpolating thrust with new time value

            self.ballistic_state.run_timestep(
                np.interp(
                    self.t[-1],
                    self.params.time,
                    propellant_mass,
                    left=0,
                    right=0,
                ),  # interpolating propellant mass with new time value
                thrust,
                self.params.d_t,
            )

            i += 1

        return (self.t, self.ballistic_state)

    def print_results(self):
        """
        Prints the results of the simulation.
        """
        print("\nINTERNAL BALLISTICS COUPLED SIMULATION RESULTS")

        if self.ballistic_state is not None:
            self.ballistic_state.print_results()
        else:
            print("Simulation not run yet. Try running the simulation first.")

__init__(rocket, atmosphere, params)

Initialize the BallisticSimulation instance.

Parameters:

Name Type Description Default
rocket Rocket

Rocket object.

required
atmosphere Atmosphere

Atmosphere object.

required
params BallisticSimulationParameters

Simulation parameters.

required
Source code in machwave/simulations/ballistics.py
def __init__(
    self,
    rocket: Rocket,
    atmosphere: Atmosphere,
    params: BallisticSimulationParameters,
) -> None:
    """Initialize the BallisticSimulation instance.

    Args:
        rocket: Rocket object.
        atmosphere: Atmosphere object.
        params: Simulation parameters.
    """
    super().__init__(params=params)

    self.params: BallisticSimulationParameters = (
        params  # explicitly defining the type of params to avoid pyright error
    )

    self.rocket = rocket
    self.atmosphere = atmosphere
    self.t = np.array([0])
    self.ballistic_state = None

get_propellant_mass()

Compute the propellant mass at each time step.

Returns:

Type Description
ndarray

Array of propellant mass values.

Source code in machwave/simulations/ballistics.py
def get_propellant_mass(self) -> np.ndarray:
    """Compute the propellant mass at each time step.

    Returns:
        Array of propellant mass values.
    """
    initial_propellant_mass = self.params.initial_propellant_mass
    prop_mass = np.array([])
    time = self.params.time

    for t in time:
        prop_mass = np.append(
            prop_mass, initial_propellant_mass * (time[-1] - t) / time[-1]
        )

    return prop_mass

print_results()

Prints the results of the simulation.

Source code in machwave/simulations/ballistics.py
def print_results(self):
    """
    Prints the results of the simulation.
    """
    print("\nINTERNAL BALLISTICS COUPLED SIMULATION RESULTS")

    if self.ballistic_state is not None:
        self.ballistic_state.print_results()
    else:
        print("Simulation not run yet. Try running the simulation first.")

run()

Runs the main loop of the simulation, returning the time array and the ballistic state object.

Returns:

Type Description
tuple

tuple[np.array, Ballistic1DState]: A tuple containing the time

tuple

array and the ballistic state object.

Source code in machwave/simulations/ballistics.py
def run(self) -> tuple:
    """
    Runs the main loop of the simulation, returning the time array and
    the ballistic state object.

    Returns:
        tuple[np.array, Ballistic1DState]: A tuple containing the time
        array and the ballistic state object.
    """
    self.ballistic_state = Ballistic1DState(
        self.rocket,
        self.atmosphere,
        rail_length=self.params.rail_length,
        motor_dry_mass=self.rocket.propulsion.get_dry_mass(),
        initial_vehicle_mass=self.rocket.get_launch_mass(),
        initial_elevation_amsl=self.params.initial_elevation_amsl,
    )

    propellant_mass = self.get_propellant_mass()

    i = 0

    while self.ballistic_state.y[i] >= 0:
        self.t = np.append(self.t, self.t[i] + self.params.d_t)  # new time value

        thrust = np.interp(
            self.t[-1],
            self.params.time,
            self.params.thrust,
            left=0,
            right=0,
        )  # interpolating thrust with new time value

        self.ballistic_state.run_timestep(
            np.interp(
                self.t[-1],
                self.params.time,
                propellant_mass,
                left=0,
                right=0,
            ),  # interpolating propellant mass with new time value
            thrust,
            self.params.d_t,
        )

        i += 1

    return (self.t, self.ballistic_state)

BallisticSimulationParameters

Bases: SimulationParameters

Parameters for a ballistic simulation.

Attributes:

Name Type Description
thrust

Array of thrust values.

motor_dry_mass

Dry mass of the motor.

initial_propellant_mass

Initial mass of the propellant.

time

Array of time values.

d_t

Time step.

initial_elevation_amsl

Initial elevation above mean sea level.

rail_length

Length of the launch rail.

Source code in machwave/simulations/ballistics.py
class BallisticSimulationParameters(SimulationParameters):
    """Parameters for a ballistic simulation.

    Attributes:
        thrust: Array of thrust values.
        motor_dry_mass: Dry mass of the motor.
        initial_propellant_mass: Initial mass of the propellant.
        time: Array of time values.
        d_t: Time step.
        initial_elevation_amsl: Initial elevation above mean sea level.
        rail_length: Length of the launch rail.
    """

    def __init__(
        self,
        thrust: np.ndarray,
        motor_dry_mass: float,
        initial_propellant_mass: float,
        time: np.ndarray,
        d_t: float,
        initial_elevation_amsl: float,
        rail_length: float,
    ):
        self.thrust = thrust
        self.motor_dry_mass = motor_dry_mass
        self.initial_propellant_mass = initial_propellant_mass
        self.time = time
        self.d_t = d_t
        self.initial_elevation_amsl = initial_elevation_amsl
        self.rail_length = rail_length

InternalBallistics

Bases: Simulation

Internal ballistics simulation class.

Attributes:

Name Type Description
motor Motor

Motor object.

params InternalBallisticsParams

Simulation parameters.

t ndarray

Array of time values.

motor_state MotorState | None

Motor state object.

Source code in machwave/simulations/internal_ballistics.py
class InternalBallistics(Simulation):
    """Internal ballistics simulation class.

    Attributes:
        motor: Motor object.
        params: Simulation parameters.
        t: Array of time values.
        motor_state: Motor state object.
    """

    def __init__(
        self,
        motor: Motor,
        params: InternalBallisticsParams,
    ) -> None:
        super().__init__(params=params)
        self.motor: Motor = motor
        self.params: InternalBallisticsParams = params
        self.t: np.ndarray = np.array([0])
        self.motor_state: MotorState | None = None

    def get_motor_state(self) -> MotorState:
        """
        Returns the motor state object based on the type of the motor.
        """
        motor_state_class = get_motor_state_class(self.motor)
        return motor_state_class(
            motor=self.motor,
            initial_pressure=self.params.igniter_pressure,
            initial_atmospheric_pressure=self.params.external_pressure,
        )

    def run(self) -> tuple[np.ndarray, MotorState]:
        """
        Runs the main loop of the simulation, returning the time array and
        the motor state object.
        """
        self.motor_state = self.get_motor_state()

        i = 0
        while not self.motor_state.end_thrust:
            self.t = np.append(self.t, self.t[i] + self.params.d_t)

            self.motor_state.run_timestep(
                self.params.d_t,
                self.params.external_pressure,
            )
            i += 1

        return (self.t, self.motor_state)

    def print_results(self) -> None:
        """
        Prints the results of the simulation.
        """
        if self.motor_state is None:
            print("No motor state results available. Try running the simulation first.")
            return

        print("\nINTERNAL BALLISTICS SIMULATION RESULTS")
        self.motor_state.print_results()

get_motor_state()

Returns the motor state object based on the type of the motor.

Source code in machwave/simulations/internal_ballistics.py
def get_motor_state(self) -> MotorState:
    """
    Returns the motor state object based on the type of the motor.
    """
    motor_state_class = get_motor_state_class(self.motor)
    return motor_state_class(
        motor=self.motor,
        initial_pressure=self.params.igniter_pressure,
        initial_atmospheric_pressure=self.params.external_pressure,
    )

print_results()

Prints the results of the simulation.

Source code in machwave/simulations/internal_ballistics.py
def print_results(self) -> None:
    """
    Prints the results of the simulation.
    """
    if self.motor_state is None:
        print("No motor state results available. Try running the simulation first.")
        return

    print("\nINTERNAL BALLISTICS SIMULATION RESULTS")
    self.motor_state.print_results()

run()

Runs the main loop of the simulation, returning the time array and the motor state object.

Source code in machwave/simulations/internal_ballistics.py
def run(self) -> tuple[np.ndarray, MotorState]:
    """
    Runs the main loop of the simulation, returning the time array and
    the motor state object.
    """
    self.motor_state = self.get_motor_state()

    i = 0
    while not self.motor_state.end_thrust:
        self.t = np.append(self.t, self.t[i] + self.params.d_t)

        self.motor_state.run_timestep(
            self.params.d_t,
            self.params.external_pressure,
        )
        i += 1

    return (self.t, self.motor_state)

InternalBallisticsCoupled

Bases: Simulation

Coupled internal ballistics simulation class.

Attributes:

Name Type Description
rocket

Rocket object.

params InternalBallisticsCoupledParams

Simulation parameters.

t

Array of time values.

motor_state

Motor state object.

ballistic_state

Ballistic state object.

Source code in machwave/simulations/internal_balistics_coupled.py
class InternalBallisticsCoupled(Simulation):
    """Coupled internal ballistics simulation class.

    Attributes:
        rocket: Rocket object.
        params: Simulation parameters.
        t: Array of time values.
        motor_state: Motor state object.
        ballistic_state: Ballistic state object.
    """

    def __init__(
        self,
        rocket: Rocket,
        params: InternalBallisticsCoupledParams,
    ) -> None:
        """Initialize the InternalBallisticsCoupled instance.

        Args:
            rocket: Rocket object.
            params: Simulation parameters.
        """
        super().__init__(params=params)

        self.params: InternalBallisticsCoupledParams = (
            params  # explicitly defining the type of params to avoid type errors
        )

        self.rocket = rocket
        self.t = np.array([0])
        self.motor_state = None
        self.ballistic_state = None

    def get_motor_state(self) -> MotorState:
        """Return the motor state object based on the type of the motor."""
        motor_state_class = get_motor_state_class(self.rocket.propulsion)
        return motor_state_class(
            motor=self.rocket.propulsion,
            initial_pressure=self.params.igniter_pressure,
            initial_atmospheric_pressure=self.params.atmosphere.get_pressure(
                self.params.initial_elevation_amsl
            ),
        )

    def run(self) -> tuple:
        """Run the main loop of the simulation.

        Returns:
            List containing motor state object and ballistic state object.
        """
        self.motor_state = self.get_motor_state()
        self.ballistic_state = Ballistic1DState(
            self.rocket,
            self.params.atmosphere,
            rail_length=self.params.rail_length,
            motor_dry_mass=self.rocket.propulsion.get_dry_mass(),
            initial_vehicle_mass=self.rocket.get_launch_mass(),
            initial_elevation_amsl=self.params.initial_elevation_amsl,
        )

        i = 0

        while self.ballistic_state.y[i] >= 0 or self.motor_state.m_prop[-1] > 0:
            self.t = np.append(self.t, self.t[i] + self.params.d_t)  # new time value

            if not self.motor_state.end_thrust:
                self.motor_state.run_timestep(
                    self.params.d_t,
                    self.ballistic_state.P_ext[i],
                )
                propellant_mass = self.motor_state.m_prop[i]
                thrust = self.motor_state.thrust[i]
                d_t = self.params.d_t
            else:
                propellant_mass = 0
                thrust = 0
                d_t = self.params.d_t * self.params.dd_t
                self.t[-1] = self.t[-2] + self.params.dd_t * self.params.d_t

            self.ballistic_state.run_timestep(propellant_mass, thrust, d_t)
            i += 1

        return (self.motor_state, self.ballistic_state)

    def print_results(self) -> None:
        print("\nINTERNAL BALLISTICS COUPLED SIMULATION RESULTS")

        if self.motor_state is not None:
            self.motor_state.print_results()
        else:
            print("No motor state results available. Try running the simulation first.")

        if self.ballistic_state is not None:
            self.ballistic_state.print_results()
        else:
            print(
                "No ballistic state results available. Try running the simulation first."
            )

__init__(rocket, params)

Initialize the InternalBallisticsCoupled instance.

Parameters:

Name Type Description Default
rocket Rocket

Rocket object.

required
params InternalBallisticsCoupledParams

Simulation parameters.

required
Source code in machwave/simulations/internal_balistics_coupled.py
def __init__(
    self,
    rocket: Rocket,
    params: InternalBallisticsCoupledParams,
) -> None:
    """Initialize the InternalBallisticsCoupled instance.

    Args:
        rocket: Rocket object.
        params: Simulation parameters.
    """
    super().__init__(params=params)

    self.params: InternalBallisticsCoupledParams = (
        params  # explicitly defining the type of params to avoid type errors
    )

    self.rocket = rocket
    self.t = np.array([0])
    self.motor_state = None
    self.ballistic_state = None

get_motor_state()

Return the motor state object based on the type of the motor.

Source code in machwave/simulations/internal_balistics_coupled.py
def get_motor_state(self) -> MotorState:
    """Return the motor state object based on the type of the motor."""
    motor_state_class = get_motor_state_class(self.rocket.propulsion)
    return motor_state_class(
        motor=self.rocket.propulsion,
        initial_pressure=self.params.igniter_pressure,
        initial_atmospheric_pressure=self.params.atmosphere.get_pressure(
            self.params.initial_elevation_amsl
        ),
    )

run()

Run the main loop of the simulation.

Returns:

Type Description
tuple

List containing motor state object and ballistic state object.

Source code in machwave/simulations/internal_balistics_coupled.py
def run(self) -> tuple:
    """Run the main loop of the simulation.

    Returns:
        List containing motor state object and ballistic state object.
    """
    self.motor_state = self.get_motor_state()
    self.ballistic_state = Ballistic1DState(
        self.rocket,
        self.params.atmosphere,
        rail_length=self.params.rail_length,
        motor_dry_mass=self.rocket.propulsion.get_dry_mass(),
        initial_vehicle_mass=self.rocket.get_launch_mass(),
        initial_elevation_amsl=self.params.initial_elevation_amsl,
    )

    i = 0

    while self.ballistic_state.y[i] >= 0 or self.motor_state.m_prop[-1] > 0:
        self.t = np.append(self.t, self.t[i] + self.params.d_t)  # new time value

        if not self.motor_state.end_thrust:
            self.motor_state.run_timestep(
                self.params.d_t,
                self.ballistic_state.P_ext[i],
            )
            propellant_mass = self.motor_state.m_prop[i]
            thrust = self.motor_state.thrust[i]
            d_t = self.params.d_t
        else:
            propellant_mass = 0
            thrust = 0
            d_t = self.params.d_t * self.params.dd_t
            self.t[-1] = self.t[-2] + self.params.dd_t * self.params.d_t

        self.ballistic_state.run_timestep(propellant_mass, thrust, d_t)
        i += 1

    return (self.motor_state, self.ballistic_state)

InternalBallisticsCoupledParams

Bases: SimulationParameters

Parameters for a coupled internal ballistics simulation.

Attributes:

Name Type Description
atmosphere

Atmosphere object.

d_t

Time step for the ballistic simulation.

dd_t

Time step factor for the motor simulation.

initial_elevation_amsl

Initial elevation above mean sea level.

igniter_pressure

Igniter pressure.

rail_length

Length of the launch rail.

Source code in machwave/simulations/internal_balistics_coupled.py
class InternalBallisticsCoupledParams(SimulationParameters):
    """Parameters for a coupled internal ballistics simulation.

    Attributes:
        atmosphere: Atmosphere object.
        d_t: Time step for the ballistic simulation.
        dd_t: Time step factor for the motor simulation.
        initial_elevation_amsl: Initial elevation above mean sea level.
        igniter_pressure: Igniter pressure.
        rail_length: Length of the launch rail.
    """

    def __init__(
        self,
        atmosphere: Atmosphere,
        d_t: float,
        dd_t: float,
        initial_elevation_amsl: float,
        igniter_pressure: float,
        rail_length: float,
    ) -> None:
        super().__init__()
        self.atmosphere = atmosphere
        self.d_t = d_t
        self.dd_t = dd_t
        self.initial_elevation_amsl = initial_elevation_amsl
        self.igniter_pressure = igniter_pressure
        self.rail_length = rail_length

InternalBallisticsParams

Bases: SimulationParameters

Parameters for an internal ballistics simulation.

Attributes:

Name Type Description
d_t

Time step.

igniter_pressure

Igniter pressure.

external_pressure

External pressure.

Source code in machwave/simulations/internal_ballistics.py
class InternalBallisticsParams(SimulationParameters):
    """Parameters for an internal ballistics simulation.

    Attributes:
        d_t: Time step.
        igniter_pressure: Igniter pressure.
        external_pressure: External pressure.
    """

    def __init__(
        self,
        d_t: float,
        igniter_pressure: float,
        external_pressure: float,
    ) -> None:
        super().__init__()
        self.d_t = d_t
        self.igniter_pressure = igniter_pressure
        self.external_pressure = external_pressure

Simulation

Bases: ABC

Abstract class that represents a simulation.

Subclasses of Simulation implement specific simulation logic.

NOTE: Instances of this class should not store any simulation state. Storing and analyzing simulation data should be done only by the State class.

Attributes:

Name Type Description
params

Object containing simulation parameters.

Source code in machwave/simulations/base.py
class Simulation(ABC):
    """Abstract class that represents a simulation.

    Subclasses of Simulation implement specific simulation logic.

    NOTE: Instances of this class should not store any simulation state.
    Storing and analyzing simulation data should be done only by the State
    class.

    Attributes:
        params: Object containing simulation parameters.
    """

    def __init__(self, params: SimulationParameters) -> None:
        """Initialize the Simulation instance with the provided parameters.

        Args:
            params: Object containing simulation parameters.
        """
        self.params = params

    @abstractmethod
    def run(self) -> tuple:
        """Run the simulation.

        This method should be implemented by subclasses. It typically
        contains a loop that iterates over time or distance.

        Returns:
            List of State instances representing the states performed during
            the simulation.
        """
        pass

    @abstractmethod
    def print_results(self, *args, **kwargs):
        """Print the results of the simulation.

        Calls the print_results method on the State instances of the
        simulation.

        Args:
            *args: Additional positional arguments.
            **kwargs: Additional keyword arguments.
        """
        pass

__init__(params)

Initialize the Simulation instance with the provided parameters.

Parameters:

Name Type Description Default
params SimulationParameters

Object containing simulation parameters.

required
Source code in machwave/simulations/base.py
def __init__(self, params: SimulationParameters) -> None:
    """Initialize the Simulation instance with the provided parameters.

    Args:
        params: Object containing simulation parameters.
    """
    self.params = params

print_results(*args, **kwargs) abstractmethod

Print the results of the simulation.

Calls the print_results method on the State instances of the simulation.

Parameters:

Name Type Description Default
*args

Additional positional arguments.

()
**kwargs

Additional keyword arguments.

{}
Source code in machwave/simulations/base.py
@abstractmethod
def print_results(self, *args, **kwargs):
    """Print the results of the simulation.

    Calls the print_results method on the State instances of the
    simulation.

    Args:
        *args: Additional positional arguments.
        **kwargs: Additional keyword arguments.
    """
    pass

run() abstractmethod

Run the simulation.

This method should be implemented by subclasses. It typically contains a loop that iterates over time or distance.

Returns:

Type Description
tuple

List of State instances representing the states performed during

tuple

the simulation.

Source code in machwave/simulations/base.py
@abstractmethod
def run(self) -> tuple:
    """Run the simulation.

    This method should be implemented by subclasses. It typically
    contains a loop that iterates over time or distance.

    Returns:
        List of State instances representing the states performed during
        the simulation.
    """
    pass

SimulationParameters

Bases: ABC

Abstract class that stores simulation parameters and should be subclassed for specific simulations.

Examples of simulation parameters: - time step for a time-based iterative simulation - initial elevation in the case of a rocket launch - igniter pressure in the case of an internal ballistic simulation

Source code in machwave/simulations/base.py
class SimulationParameters(ABC):
    """
    Abstract class that stores simulation parameters and should be subclassed
    for specific simulations.

    Examples of simulation parameters:
    - time step for a time-based iterative simulation
    - initial elevation in the case of a rocket launch
    - igniter pressure in the case of an internal ballistic simulation
    """

    pass