Skip to content

ENH: Implementing 3-dof-simulation #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e557e17
DRAFT: for ENH/3-dof-simulation (See #655)
aZira371 Dec 3, 2024
a768648
ENH/3-dof-simulation (See RocketPy-Team#655)
aZira371 Dec 6, 2024
9b00c57
MNT: cleaned up new functions and
aZira371 Dec 6, 2024
87e9180
Rebase: Merge branch 'develop' of https://github.com/aZira371/RocketP…
aZira371 Feb 25, 2025
4524219
ENH: Addition of point mass classes to rocketpy.rocket and rocketpy.m…
aZira371 Feb 25, 2025
57b4732
ENH: PointMassMotor and PointMassRocket working as intended after som…
aZira371 Apr 11, 2025
81ce869
MNT: Removing unnecessary files added by mistake.
aZira371 Apr 11, 2025
0673076
Merge branch 'RocketPy-Team:master' into enh/3-dof-simulation
aZira371 Apr 24, 2025
47c9f2f
MNT: Cleaned up PointMassMotor and PointMassRocket class
aZira371 May 10, 2025
f6ad658
Merge branch 'enh/3-dof-simulation' of https://github.com/aZira371/Ro…
aZira371 May 10, 2025
33cb63a
MNT: Cleaning up flight class and PointMassMotor class
aZira371 May 10, 2025
1d2d4dc
Merge branch 'enh/3-dof-simulation' into develop
aZira371 Jun 10, 2025
fe21271
Merge pull request #1 from aZira371/develop
aZira371 Jun 10, 2025
b18b241
MNT: point mass motor cleanup
aZira371 Jun 26, 2025
8aa1016
ENH: restructuring rocket class
aZira371 Jun 30, 2025
87e7dce
MNT: fixing certain calculations on point mass motor
aZira371 Jul 11, 2025
0e4d8a4
Rename PointMassMotor.py to pointmassmotor.py
aZira371 Jul 11, 2025
41e94f1
Merge branch 'develop' into enh/3-dof-simulation
aZira371 Jul 11, 2025
e299a30
MNT: updates to 3dof example
aZira371 Jul 11, 2025
d4dc989
MNT: lint cleanup and adding 3dof to init
aZira371 Jul 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@
"outerboundaryis",
"overshootable",
"planform",
"pointmassmotor",
"polystyle",
"powerseries",
"Prandtl",
Expand Down
245 changes: 245 additions & 0 deletions docs/examples/3_DOF_TRIAL.ipynb

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions rocketpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
MassFlowRateBasedTank,
Motor,
SolidMotor,
PointMassMotor,
SphericalTank,
Tank,
TankGeometry,
Expand All @@ -37,6 +38,7 @@
Parachute,
RailButtons,
Rocket,
PointMassRocket,
Tail,
TrapezoidalFins,
)
Expand Down
1 change: 1 addition & 0 deletions rocketpy/motors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .liquid_motor import LiquidMotor
from .motor import GenericMotor, Motor
from .solid_motor import SolidMotor
from .pointmassmotor import PointMassMotor
from .tank import (
LevelBasedTank,
MassBasedTank,
Expand Down
266 changes: 266 additions & 0 deletions rocketpy/motors/pointmassmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
from functools import cached_property

import numpy as np

from typing import Callable

from rocketpy.mathutils.function import Function, funcify_method
from .motor import Motor

class PointMassMotor(Motor):
"""Class representing a motor modeled as a point mass.

Inherits from the Motor class and simplifies the model to a thrust-producing
object without detailed structural components. The total mass of the motor
will vary with propellant consumption, similar to a standard motor. However,
its inertia components and the center of propellant mass are considered zero
and fixed at the motor's reference point, respectively.
"""

def __init__(
self,
thrust_source,
dry_mass,
propellant_initial_mass,
burn_time = None,
propellant_final_mass = None,
reshape_thrust_curve = False,
interpolation_method = "linear",
):
"""Initialize the PointMassMotor class.

This motor simplifies the physical model by considering its mass to be
concentrated at a single point, effectively setting all its inertia
components to zero. Propellant mass variation and exhaust velocity
are still simulated and derived from the thrust and propellant consumption
characteristics, similar to the base Motor class.

Parameters
----------
thrust_source : int, float, callable, str, numpy.ndarray, Function
Thrust source. Can be a constant value (int, float), a callable
function of time, a path to a CSV file, a NumPy array, or a
RocketPy `Function` object.
dry_mass : float
Total dry mass of the motor in kg.
propellant_initial_mass : float
Initial mass of the propellant in kg. This is a required parameter
as the point mass motor will still simulate propellant consumption.
burn_time : float, optional
Total burn time of the motor in seconds. Required if `thrust_source`
is a constant value or a callable function, and `propellant_final_mass`
is not provided. If `thrust_source` is a CSV, array, or Function,
the burn time is derived from it.
propellant_final_mass : float, optional
Final mass of the propellant in kg. Required if `thrust_source`
is a callable function and `burn_time` is not provided. If not
provided, it is calculated by the base Motor class.
reshape_thrust_curve : bool, optional
Whether to reshape the thrust curve to start at t=0 and end at
burn_time. Defaults to False.
interpolation_method : str, optional
Interpolation method for the thrust curve, if applicable.
Defaults to 'linear'.

Raises
------
ValueError
If insufficient data is provided for mass flow rate calculation.
TypeError
If an invalid type is provided for `thrust_source`.
"""
if isinstance(thrust_source, (int, float, Callable)):
if propellant_initial_mass is None:
raise ValueError(
"For constant or callable thrust, 'propellant_initial_mass' is required."
)
if burn_time is None and propellant_final_mass is None:
raise ValueError(
"For constant or callable thrust, either 'burn_time' or "
"'propellant_final_mass' must be provided."
)

elif isinstance(thrust_source, (Function, np.ndarray, str)):
if propellant_initial_mass is None:
raise ValueError(
"For thrust from a Function, NumPy array, or CSV, 'propellant_initial_mass' is required."
)
else:
raise TypeError(
"Invalid 'thrust_source' type. Must be int, float, callable, str, numpy.ndarray, or Function."
)

self._propellant_initial_mass = propellant_initial_mass
self.propellant_final_mass = propellant_final_mass

super().__init__(
thrust_source=thrust_source,
dry_inertia=(0, 0, 0), # Inertia is zero for a point mass
nozzle_radius=0, # Nozzle radius is irrelevant for a point mass model
center_of_dry_mass_position=0, # Pass 0 directly to the superclass
dry_mass=dry_mass,
nozzle_position=0, # Nozzle is at the motor's origin
burn_time=burn_time,
reshape_thrust_curve=reshape_thrust_curve,
interpolation_method=interpolation_method,
coordinate_system_orientation="nozzle_to_combustion_chamber", # Standard orientation
)

# Removed the thrust method. It will now be inherited directly from the Motor base class,
# which already correctly handles the conversion of thrust_source to a Function
# and exposes it as a cached property.

# Removed the total_mass override. The base Motor class's total_mass property
# will now correctly calculate dry_mass + propellant_mass(t), which is the desired
# varying mass behavior for the point mass motor.

# Removed the center_of_dry_mass_position override. It is now passed directly
# to the super().__init__ as 0.
@property
def propellant_initial_mass(self):
"""Returns the initial propellant mass for a point mass motor.

This property retrieves the value set during initialization. This implementation
is required as 'propellant_initial_mass' is an abstract method in the parent Motor class.

Returns
-------
float
Propellant initial mass in kg.
"""
return self._propellant_initial_mass

@funcify_method("Time (s)", "Exhaust velocity (m/s)")
def exhaust_velocity(self):
"""Exhaust velocity by assuming it as a constant. The formula used is
total impulse/propellant initial mass.

Returns
-------
self.exhaust_velocity : Function
Gas exhaust velocity of the motor.

Notes
-----
This corresponds to the actual exhaust velocity only when the nozzle
exit pressure equals the atmospheric pressure.
"""
return Function(
self.total_impulse / self.propellant_initial_mass
).set_discrete_based_on_model(self.thrust)

@cached_property
@funcify_method("Time (s)", "Mass flow rate (kg/s)", extrapolation="zero")
def total_mass_flow_rate(self) -> Function:
"""Time derivative of the propellant mass as a function of time.

It calculates mass flow rate as the negative of thrust divided by exhaust velocity,
consistent with the fundamental rocket equation.

Returns
-------
Function
Time derivative of total propellant mass a function of time.
"""

exhaust_vel_func = self.exhaust_velocity
return -self.thrust / exhaust_vel_func

@cached_property
@funcify_method("Time (s)", "Propellant Mass (kg)")
def center_of_propellant_mass(self):
"""Returns the position of the center of mass of the propellant.

For a point mass motor, the propellant's center of mass is considered
to be at the origin (0) of the motor's coordinate system.

Returns
-------
Function
A Function object representing the center of propellant mass (always 0).
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_11(self):
"""Returns the propellant moment of inertia around the x-axis.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_12(self):
"""Returns the propellant product of inertia I_xy.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_13(self):
"""Returns the propellant product of inertia I_xz.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_22(self):
"""Returns the propellant moment of inertia around the y-axis.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_23(self):
"""Returns the propellant product of inertia I_yz.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0

@cached_property
@funcify_method("Time (s)", "Inertia (kg·m²)")
def propellant_I_33(self):
"""Returns the propellant moment of inertia around the z-axis.

For a point mass motor, this is always zero.

Returns
-------
Function
A Function object representing zero propellant inertia.
"""
return 0
1 change: 1 addition & 0 deletions rocketpy/rocket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
from rocketpy.rocket.components import Components
from rocketpy.rocket.parachute import Parachute
from rocketpy.rocket.rocket import Rocket
from rocketpy.rocket.rocket import PointMassRocket
Loading
Loading