Skip to content

models.motors

Top-level motor/engine definitions that assemble all physical sub-components into a complete propulsion unit.

  • SolidMotor — Combines a Grain, SolidPropellant, and SolidMotorThrustChamber. Provides launch/dry mass, free chamber volume, CoG (mass-weighted across grain and hardware), and thrust coefficient with loss corrections.
  • BiliquidEngine — Combines a BiliquidPropellant, BiliquidEngineThrustChamber, and FeedSystem. Tracks CoG shift as propellant is consumed from the tanks.

Both inherit from the generic Motor[P, T] base class. A motor instance is the primary input to InternalBallisticsSimulation.

machwave.models.motors

BiliquidEngine

Bases: Motor[BiliquidPropellant, BiliquidEngineThrustChamber]

Biliquid rocket engine with a bipropellant feed system.

Source code in machwave/models/motors/biliquid.py
class BiliquidEngine(
    motor_base.Motor[
        propellants.BiliquidPropellant,
        thrust_chamber_models.BiliquidEngineThrustChamber,
    ]
):
    """Biliquid rocket engine with a bipropellant feed system."""

    def __init__(
        self,
        propellant: propellants.BiliquidPropellant,
        thrust_chamber: thrust_chamber_models.BiliquidEngineThrustChamber,
        feed_system: feed_system_base.FeedSystem,
        oxidizer_tank_cog: float | None = None,
        fuel_tank_cog: float | None = None,
        combustion_efficiency: float = 0.95,
        nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
    ) -> None:
        """
        Initialize a biliquid rocket engine.

        Args:
            propellant: Biliquid propellant properties (oxidizer + fuel).
            thrust_chamber: Thrust chamber assembly (nozzle, combustion chamber,
                injector).
            feed_system: Propellant feed system (tanks, lines, pumps or
                pressurization).
            oxidizer_tank_cog: Axial position of the oxidizer propellant center
                of gravity, measured from the nozzle exit [m]. If None, uses a
                default estimate.
            fuel_tank_cog: Axial position of the fuel propellant center of
                gravity, measured from the nozzle exit [m]. If None, uses a
                default estimate.
            combustion_efficiency: Ratio of the actual flame temperature to the ideal
                adiabatic flame temperature (0, 1].
            nozzle_loss_model: Nozzle loss model.
        """
        super().__init__(
            propellant,
            thrust_chamber,
            combustion_efficiency,
            nozzle_loss_model=nozzle_loss_model
            or nozzle_losses.presets.constant_efficiency_loss_model(
                mixture_type=propellants.MixtureType.BILIQUID
            ),
        )
        self.feed_system = feed_system
        self.oxidizer_tank_cog = oxidizer_tank_cog
        self.fuel_tank_cog = fuel_tank_cog

    @property
    def initial_propellant_mass(self) -> float:
        """Return the initial propellant mass [kg]."""
        return self.feed_system.get_initial_propellant_mass()

    def get_launch_mass(self) -> float:
        """Return the launch mass (dry mass + initial propellant) [kg]."""
        return self.thrust_chamber.dry_mass + self.initial_propellant_mass

    def get_dry_mass(self) -> float:
        """Return the dry mass [kg]."""
        return self.thrust_chamber.dry_mass

    def get_center_of_gravity(
        self, propellant_fraction: float = 0.0
    ) -> np.typing.NDArray[np.float64]:
        """
        Return the engine center of gravity.

        Combines structural dry mass, oxidizer, and fuel via a mass-weighted
        average. Origin is at the nozzle exit on the chamber axis with
        positive x pointing forward (toward bulkhead).

        Args:
            propellant_fraction: Fraction of propellant consumed (0.0 = full,
                1.0 = empty).

        Returns:
            Center of gravity as `(x, y, z)` [m].

        Raises:
            ValueError: If `thrust_chamber.center_of_gravity_coordinate`,
                `oxidizer_tank_cog`, or `fuel_tank_cog` is not defined.
        """
        initial_ox_mass = self.feed_system.oxidizer_tank.initial_fluid_mass
        initial_fuel_mass = self.feed_system.fuel_tank.initial_fluid_mass

        ox_mass = initial_ox_mass * (1.0 - propellant_fraction)
        fuel_mass = initial_fuel_mass * (1.0 - propellant_fraction)

        dry_mass = self.thrust_chamber.dry_mass

        if self.thrust_chamber.center_of_gravity_coordinate is None:
            raise ValueError(
                "Thrust chamber center of gravity coordinate is not defined."
            )

        dry_cog = self.thrust_chamber.center_of_gravity_coordinate

        if self.oxidizer_tank_cog is None:
            raise ValueError("Oxidizer tank center of gravity is not defined.")

        ox_cog = np.array([self.oxidizer_tank_cog, 0.0, 0.0], dtype=np.float64)

        if self.fuel_tank_cog is None:
            raise ValueError("Fuel tank center of gravity is not defined.")

        fuel_cog = np.array([self.fuel_tank_cog, 0.0, 0.0], dtype=np.float64)

        total_mass = dry_mass + ox_mass + fuel_mass

        if total_mass <= 0:
            return dry_cog

        weighted_cog = (
            dry_cog * dry_mass + ox_cog * ox_mass + fuel_cog * fuel_mass
        ) / total_mass

        return weighted_cog.astype(np.float64)

initial_propellant_mass property

Return the initial propellant mass [kg].

__init__(propellant, thrust_chamber, feed_system, oxidizer_tank_cog=None, fuel_tank_cog=None, combustion_efficiency=0.95, nozzle_loss_model=None)

Initialize a biliquid rocket engine.

Parameters:

Name Type Description Default
propellant BiliquidPropellant

Biliquid propellant properties (oxidizer + fuel).

required
thrust_chamber BiliquidEngineThrustChamber

Thrust chamber assembly (nozzle, combustion chamber, injector).

required
feed_system FeedSystem

Propellant feed system (tanks, lines, pumps or pressurization).

required
oxidizer_tank_cog float | None

Axial position of the oxidizer propellant center of gravity, measured from the nozzle exit [m]. If None, uses a default estimate.

None
fuel_tank_cog float | None

Axial position of the fuel propellant center of gravity, measured from the nozzle exit [m]. If None, uses a default estimate.

None
combustion_efficiency float

Ratio of the actual flame temperature to the ideal adiabatic flame temperature (0, 1].

0.95
nozzle_loss_model NozzleLossModel | None

Nozzle loss model.

None
Source code in machwave/models/motors/biliquid.py
def __init__(
    self,
    propellant: propellants.BiliquidPropellant,
    thrust_chamber: thrust_chamber_models.BiliquidEngineThrustChamber,
    feed_system: feed_system_base.FeedSystem,
    oxidizer_tank_cog: float | None = None,
    fuel_tank_cog: float | None = None,
    combustion_efficiency: float = 0.95,
    nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
) -> None:
    """
    Initialize a biliquid rocket engine.

    Args:
        propellant: Biliquid propellant properties (oxidizer + fuel).
        thrust_chamber: Thrust chamber assembly (nozzle, combustion chamber,
            injector).
        feed_system: Propellant feed system (tanks, lines, pumps or
            pressurization).
        oxidizer_tank_cog: Axial position of the oxidizer propellant center
            of gravity, measured from the nozzle exit [m]. If None, uses a
            default estimate.
        fuel_tank_cog: Axial position of the fuel propellant center of
            gravity, measured from the nozzle exit [m]. If None, uses a
            default estimate.
        combustion_efficiency: Ratio of the actual flame temperature to the ideal
            adiabatic flame temperature (0, 1].
        nozzle_loss_model: Nozzle loss model.
    """
    super().__init__(
        propellant,
        thrust_chamber,
        combustion_efficiency,
        nozzle_loss_model=nozzle_loss_model
        or nozzle_losses.presets.constant_efficiency_loss_model(
            mixture_type=propellants.MixtureType.BILIQUID
        ),
    )
    self.feed_system = feed_system
    self.oxidizer_tank_cog = oxidizer_tank_cog
    self.fuel_tank_cog = fuel_tank_cog

get_center_of_gravity(propellant_fraction=0.0)

Return the engine center of gravity.

Combines structural dry mass, oxidizer, and fuel via a mass-weighted average. Origin is at the nozzle exit on the chamber axis with positive x pointing forward (toward bulkhead).

Parameters:

Name Type Description Default
propellant_fraction float

Fraction of propellant consumed (0.0 = full, 1.0 = empty).

0.0

Returns:

Type Description
NDArray[float64]

Center of gravity as (x, y, z) [m].

Raises:

Type Description
ValueError

If thrust_chamber.center_of_gravity_coordinate, oxidizer_tank_cog, or fuel_tank_cog is not defined.

Source code in machwave/models/motors/biliquid.py
def get_center_of_gravity(
    self, propellant_fraction: float = 0.0
) -> np.typing.NDArray[np.float64]:
    """
    Return the engine center of gravity.

    Combines structural dry mass, oxidizer, and fuel via a mass-weighted
    average. Origin is at the nozzle exit on the chamber axis with
    positive x pointing forward (toward bulkhead).

    Args:
        propellant_fraction: Fraction of propellant consumed (0.0 = full,
            1.0 = empty).

    Returns:
        Center of gravity as `(x, y, z)` [m].

    Raises:
        ValueError: If `thrust_chamber.center_of_gravity_coordinate`,
            `oxidizer_tank_cog`, or `fuel_tank_cog` is not defined.
    """
    initial_ox_mass = self.feed_system.oxidizer_tank.initial_fluid_mass
    initial_fuel_mass = self.feed_system.fuel_tank.initial_fluid_mass

    ox_mass = initial_ox_mass * (1.0 - propellant_fraction)
    fuel_mass = initial_fuel_mass * (1.0 - propellant_fraction)

    dry_mass = self.thrust_chamber.dry_mass

    if self.thrust_chamber.center_of_gravity_coordinate is None:
        raise ValueError(
            "Thrust chamber center of gravity coordinate is not defined."
        )

    dry_cog = self.thrust_chamber.center_of_gravity_coordinate

    if self.oxidizer_tank_cog is None:
        raise ValueError("Oxidizer tank center of gravity is not defined.")

    ox_cog = np.array([self.oxidizer_tank_cog, 0.0, 0.0], dtype=np.float64)

    if self.fuel_tank_cog is None:
        raise ValueError("Fuel tank center of gravity is not defined.")

    fuel_cog = np.array([self.fuel_tank_cog, 0.0, 0.0], dtype=np.float64)

    total_mass = dry_mass + ox_mass + fuel_mass

    if total_mass <= 0:
        return dry_cog

    weighted_cog = (
        dry_cog * dry_mass + ox_cog * ox_mass + fuel_cog * fuel_mass
    ) / total_mass

    return weighted_cog.astype(np.float64)

get_dry_mass()

Return the dry mass [kg].

Source code in machwave/models/motors/biliquid.py
def get_dry_mass(self) -> float:
    """Return the dry mass [kg]."""
    return self.thrust_chamber.dry_mass

get_launch_mass()

Return the launch mass (dry mass + initial propellant) [kg].

Source code in machwave/models/motors/biliquid.py
def get_launch_mass(self) -> float:
    """Return the launch mass (dry mass + initial propellant) [kg]."""
    return self.thrust_chamber.dry_mass + self.initial_propellant_mass

Motor

Bases: Generic[P, T], ABC

Abstract rocket motor/engine for solid, hybrid, or biliquid systems.

Source code in machwave/models/motors/base.py
class Motor(Generic[P, T], ABC):
    """Abstract rocket motor/engine for solid, hybrid, or biliquid systems."""

    def __init__(
        self,
        propellant: P,
        thrust_chamber: T,
        combustion_efficiency: float = 0.95,
        nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
    ) -> None:
        """
        Initialize attributes common to any motor or engine.

        Args:
            propellant: Propellant used in the motor.
            thrust_chamber: Thrust chamber of the motor.
            combustion_efficiency: Ratio of the actual flame temperature to the ideal
                adiabatic flame temperature (0, 1].
            nozzle_loss_model: Nozzle thrust coefficient loss model. Motor
                subclasses supply an engine-appropriate default.

        Raises:
            ValueError: If `combustion_efficiency` is not in (0, 1], if
                `nozzle_loss_model` is missing, or if its mixture type does not
                match the propellant.
        """
        if not 0.0 < combustion_efficiency <= 1.0:
            raise ValueError(
                "combustion_efficiency must be in the range (0, 1], got "
                f"{combustion_efficiency}"
            )

        if nozzle_loss_model is None:
            raise ValueError(
                "nozzle_loss_model must be provided; motor subclasses supply a default."
            )

        if nozzle_loss_model.mixture_type != propellant.mixture_type:
            raise ValueError(
                "nozzle_loss_model mixture type "
                f"{nozzle_loss_model.mixture_type} does not match propellant "
                f"mixture type {propellant.mixture_type}."
            )

        self.propellant = propellant
        self.thrust_chamber = thrust_chamber
        self.combustion_efficiency = combustion_efficiency
        self.nozzle_loss_model = nozzle_loss_model

    @abstractmethod
    def get_launch_mass(self) -> float:
        """Return the total mass of the motor before launch [kg]."""
        pass

    @abstractmethod
    def get_dry_mass(self) -> float:
        """Return the dry mass of the motor [kg]."""
        pass

    @abstractmethod
    def get_center_of_gravity(self, *args, **kwargs) -> np.typing.NDArray[np.float64]:
        """
        Return the center of gravity of the propulsion system.

        The coordinate system origin corresponds to the combustion chamber axis
        at the nozzle exit plane, with positive x pointing toward the bulkhead.

        Returns:
            1D array of shape `(3,)` containing the `[x, y, z]` coordinates of
            the center of gravity [m].
        """
        pass

    @property
    @abstractmethod
    def initial_propellant_mass(self) -> float:
        """Return the initial propellant mass [kg]."""
        pass

initial_propellant_mass abstractmethod property

Return the initial propellant mass [kg].

__init__(propellant, thrust_chamber, combustion_efficiency=0.95, nozzle_loss_model=None)

Initialize attributes common to any motor or engine.

Parameters:

Name Type Description Default
propellant P

Propellant used in the motor.

required
thrust_chamber T

Thrust chamber of the motor.

required
combustion_efficiency float

Ratio of the actual flame temperature to the ideal adiabatic flame temperature (0, 1].

0.95
nozzle_loss_model NozzleLossModel | None

Nozzle thrust coefficient loss model. Motor subclasses supply an engine-appropriate default.

None

Raises:

Type Description
ValueError

If combustion_efficiency is not in (0, 1], if nozzle_loss_model is missing, or if its mixture type does not match the propellant.

Source code in machwave/models/motors/base.py
def __init__(
    self,
    propellant: P,
    thrust_chamber: T,
    combustion_efficiency: float = 0.95,
    nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
) -> None:
    """
    Initialize attributes common to any motor or engine.

    Args:
        propellant: Propellant used in the motor.
        thrust_chamber: Thrust chamber of the motor.
        combustion_efficiency: Ratio of the actual flame temperature to the ideal
            adiabatic flame temperature (0, 1].
        nozzle_loss_model: Nozzle thrust coefficient loss model. Motor
            subclasses supply an engine-appropriate default.

    Raises:
        ValueError: If `combustion_efficiency` is not in (0, 1], if
            `nozzle_loss_model` is missing, or if its mixture type does not
            match the propellant.
    """
    if not 0.0 < combustion_efficiency <= 1.0:
        raise ValueError(
            "combustion_efficiency must be in the range (0, 1], got "
            f"{combustion_efficiency}"
        )

    if nozzle_loss_model is None:
        raise ValueError(
            "nozzle_loss_model must be provided; motor subclasses supply a default."
        )

    if nozzle_loss_model.mixture_type != propellant.mixture_type:
        raise ValueError(
            "nozzle_loss_model mixture type "
            f"{nozzle_loss_model.mixture_type} does not match propellant "
            f"mixture type {propellant.mixture_type}."
        )

    self.propellant = propellant
    self.thrust_chamber = thrust_chamber
    self.combustion_efficiency = combustion_efficiency
    self.nozzle_loss_model = nozzle_loss_model

get_center_of_gravity(*args, **kwargs) abstractmethod

Return the center of gravity of the propulsion system.

The coordinate system origin corresponds to the combustion chamber axis at the nozzle exit plane, with positive x pointing toward the bulkhead.

Returns:

Type Description
NDArray[float64]

1D array of shape (3,) containing the [x, y, z] coordinates of

NDArray[float64]

the center of gravity [m].

Source code in machwave/models/motors/base.py
@abstractmethod
def get_center_of_gravity(self, *args, **kwargs) -> np.typing.NDArray[np.float64]:
    """
    Return the center of gravity of the propulsion system.

    The coordinate system origin corresponds to the combustion chamber axis
    at the nozzle exit plane, with positive x pointing toward the bulkhead.

    Returns:
        1D array of shape `(3,)` containing the `[x, y, z]` coordinates of
        the center of gravity [m].
    """
    pass

get_dry_mass() abstractmethod

Return the dry mass of the motor [kg].

Source code in machwave/models/motors/base.py
@abstractmethod
def get_dry_mass(self) -> float:
    """Return the dry mass of the motor [kg]."""
    pass

get_launch_mass() abstractmethod

Return the total mass of the motor before launch [kg].

Source code in machwave/models/motors/base.py
@abstractmethod
def get_launch_mass(self) -> float:
    """Return the total mass of the motor before launch [kg]."""
    pass

SolidMotor

Bases: Motor[SolidPropellant, SolidMotorThrustChamber]

Solid rocket motor with a propellant grain and thrust chamber.

Source code in machwave/models/motors/solid.py
class SolidMotor(
    motor_base.Motor[
        propellants.SolidPropellant, thrust_chamber.SolidMotorThrustChamber
    ]
):
    """Solid rocket motor with a propellant grain and thrust chamber."""

    def __init__(
        self,
        grain: grain.Grain,
        propellant: propellants.SolidPropellant,
        thrust_chamber: thrust_chamber.SolidMotorThrustChamber,
        combustion_efficiency: float = 0.95,
        nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
    ) -> None:
        """
        Initialize a solid rocket motor.

        Args:
            grain: Grain geometry configuration.
            propellant: Solid propellant properties.
            thrust_chamber: Thrust chamber model.
            combustion_efficiency: Ratio of the actual flame temperature to the ideal
                adiabatic flame temperature (0, 1].
            nozzle_loss_model: Nozzle loss model.
        """
        super().__init__(
            propellant,
            thrust_chamber,
            combustion_efficiency,
            nozzle_loss_model=nozzle_loss_model
            or nozzle_losses.presets.spp1975_solid_loss_model(),
        )

        self.grain = grain
        self.propellant: propellants.SolidPropellant = propellant

    def get_free_chamber_volume(self, propellant_volume: float) -> float:
        """
        Return the chamber volume without any propellant.

        Args:
            propellant_volume: Propellant volume [m^3].

        Returns:
            Free chamber volume [m^3].
        """
        return (
            self.thrust_chamber.combustion_chamber.internal_volume - propellant_volume
        )

    @property
    def initial_propellant_mass(self) -> float:
        """Return the initial propellant mass [kg]."""
        return self.grain.get_propellant_mass(
            web_distance=0, ideal_density=self.propellant.ideal_density
        )

    def get_launch_mass(self) -> float:
        """Return the launch mass (dry mass + initial propellant) [kg]."""
        return self.thrust_chamber.dry_mass + self.initial_propellant_mass

    def get_dry_mass(self) -> float:
        """Return the dry mass [kg]."""
        return self.thrust_chamber.dry_mass

    def get_center_of_gravity(
        self, web_distance: float = 0.0
    ) -> np.typing.NDArray[np.float64]:
        """
        Return the solid motor center of gravity.

        Combines the propellant grain (wet mass) and the thrust chamber dry
        mass via a mass-weighted average. Dry mass center of gravity is
        considered constant.

        Args:
            web_distance: Web distance traveled [m]. Defaults to ignition state.

        Returns:
            Center of gravity as `(x, y, z)` [m].

        Raises:
            ValueError: If the thrust chamber dry mass center of gravity is not
                defined or if the total mass is not strictly positive.
        """
        grain_cog_port = self.grain.get_center_of_gravity(web_distance=web_distance)
        propellant_mass = self.grain.get_propellant_mass(
            web_distance=web_distance, ideal_density=self.propellant.ideal_density
        )

        dry_mass = self.thrust_chamber.dry_mass
        dry_mass_cog = self.thrust_chamber.center_of_gravity_coordinate
        nozzle_exit_to_port = self.thrust_chamber.nozzle_exit_to_grain_port_distance

        # Transform grain CoG from port origin to nozzle exit origin
        grain_cog = grain_cog_port.copy()
        grain_cog[0] = nozzle_exit_to_port + grain_cog_port[0]

        if dry_mass_cog is None:
            raise ValueError("Dry mass center of gravity coordinate is not defined.")

        total_mass = propellant_mass + dry_mass
        if total_mass <= 0:
            raise ValueError("Total mass must be greater than zero to calculate CoG.")

        weighted_cog = (
            grain_cog * propellant_mass + dry_mass_cog * dry_mass
        ) / total_mass

        return weighted_cog.astype(np.float64)

initial_propellant_mass property

Return the initial propellant mass [kg].

__init__(grain, propellant, thrust_chamber, combustion_efficiency=0.95, nozzle_loss_model=None)

Initialize a solid rocket motor.

Parameters:

Name Type Description Default
grain Grain

Grain geometry configuration.

required
propellant SolidPropellant

Solid propellant properties.

required
thrust_chamber SolidMotorThrustChamber

Thrust chamber model.

required
combustion_efficiency float

Ratio of the actual flame temperature to the ideal adiabatic flame temperature (0, 1].

0.95
nozzle_loss_model NozzleLossModel | None

Nozzle loss model.

None
Source code in machwave/models/motors/solid.py
def __init__(
    self,
    grain: grain.Grain,
    propellant: propellants.SolidPropellant,
    thrust_chamber: thrust_chamber.SolidMotorThrustChamber,
    combustion_efficiency: float = 0.95,
    nozzle_loss_model: nozzle_losses.NozzleLossModel | None = None,
) -> None:
    """
    Initialize a solid rocket motor.

    Args:
        grain: Grain geometry configuration.
        propellant: Solid propellant properties.
        thrust_chamber: Thrust chamber model.
        combustion_efficiency: Ratio of the actual flame temperature to the ideal
            adiabatic flame temperature (0, 1].
        nozzle_loss_model: Nozzle loss model.
    """
    super().__init__(
        propellant,
        thrust_chamber,
        combustion_efficiency,
        nozzle_loss_model=nozzle_loss_model
        or nozzle_losses.presets.spp1975_solid_loss_model(),
    )

    self.grain = grain
    self.propellant: propellants.SolidPropellant = propellant

get_center_of_gravity(web_distance=0.0)

Return the solid motor center of gravity.

Combines the propellant grain (wet mass) and the thrust chamber dry mass via a mass-weighted average. Dry mass center of gravity is considered constant.

Parameters:

Name Type Description Default
web_distance float

Web distance traveled [m]. Defaults to ignition state.

0.0

Returns:

Type Description
NDArray[float64]

Center of gravity as (x, y, z) [m].

Raises:

Type Description
ValueError

If the thrust chamber dry mass center of gravity is not defined or if the total mass is not strictly positive.

Source code in machwave/models/motors/solid.py
def get_center_of_gravity(
    self, web_distance: float = 0.0
) -> np.typing.NDArray[np.float64]:
    """
    Return the solid motor center of gravity.

    Combines the propellant grain (wet mass) and the thrust chamber dry
    mass via a mass-weighted average. Dry mass center of gravity is
    considered constant.

    Args:
        web_distance: Web distance traveled [m]. Defaults to ignition state.

    Returns:
        Center of gravity as `(x, y, z)` [m].

    Raises:
        ValueError: If the thrust chamber dry mass center of gravity is not
            defined or if the total mass is not strictly positive.
    """
    grain_cog_port = self.grain.get_center_of_gravity(web_distance=web_distance)
    propellant_mass = self.grain.get_propellant_mass(
        web_distance=web_distance, ideal_density=self.propellant.ideal_density
    )

    dry_mass = self.thrust_chamber.dry_mass
    dry_mass_cog = self.thrust_chamber.center_of_gravity_coordinate
    nozzle_exit_to_port = self.thrust_chamber.nozzle_exit_to_grain_port_distance

    # Transform grain CoG from port origin to nozzle exit origin
    grain_cog = grain_cog_port.copy()
    grain_cog[0] = nozzle_exit_to_port + grain_cog_port[0]

    if dry_mass_cog is None:
        raise ValueError("Dry mass center of gravity coordinate is not defined.")

    total_mass = propellant_mass + dry_mass
    if total_mass <= 0:
        raise ValueError("Total mass must be greater than zero to calculate CoG.")

    weighted_cog = (
        grain_cog * propellant_mass + dry_mass_cog * dry_mass
    ) / total_mass

    return weighted_cog.astype(np.float64)

get_dry_mass()

Return the dry mass [kg].

Source code in machwave/models/motors/solid.py
def get_dry_mass(self) -> float:
    """Return the dry mass [kg]."""
    return self.thrust_chamber.dry_mass

get_free_chamber_volume(propellant_volume)

Return the chamber volume without any propellant.

Parameters:

Name Type Description Default
propellant_volume float

Propellant volume [m^3].

required

Returns:

Type Description
float

Free chamber volume [m^3].

Source code in machwave/models/motors/solid.py
def get_free_chamber_volume(self, propellant_volume: float) -> float:
    """
    Return the chamber volume without any propellant.

    Args:
        propellant_volume: Propellant volume [m^3].

    Returns:
        Free chamber volume [m^3].
    """
    return (
        self.thrust_chamber.combustion_chamber.internal_volume - propellant_volume
    )

get_launch_mass()

Return the launch mass (dry mass + initial propellant) [kg].

Source code in machwave/models/motors/solid.py
def get_launch_mass(self) -> float:
    """Return the launch mass (dry mass + initial propellant) [kg]."""
    return self.thrust_chamber.dry_mass + self.initial_propellant_mass