Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 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
c8096c5
MNT: Point mass motor and rocket fixes
aZira371 Aug 30, 2025
724f9dd
MNT: flight class fix on simulation mode detection
aZira371 Aug 30, 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
91 changes: 91 additions & 0 deletions rocketpy/motors/pointmassmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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):
"""Motor modeled as a point mass for 3-DOF simulations."""

def __init__(
self,
thrust_source,
dry_mass,
propellant_initial_mass,
burn_time=None,
propellant_final_mass=None,
reshape_thrust_curve=False,
interpolation_method="linear",
):
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),
nozzle_radius=0,
center_of_dry_mass_position=0,
dry_mass=dry_mass,
nozzle_position=0,
burn_time=burn_time,
reshape_thrust_curve=reshape_thrust_curve,
interpolation_method=interpolation_method,
coordinate_system_orientation="nozzle_to_combustion_chamber",
)

@property
def propellant_initial_mass(self):
return self._propellant_initial_mass

@funcify_method("Time (s)", "Exhaust velocity (m/s)")
def exhaust_velocity(self):
"""Assume constant exhaust velocity: total impulse / propellant mass"""
v_e = self.total_impulse / self.propellant_initial_mass
return Function(v_e).set_discrete_based_on_model(self.thrust)

@cached_property
def total_mass_flow_rate(self) -> Function:
"""Mass flow rate: -thrust / exhaust_velocity"""
return -self.thrust / self.exhaust_velocity

@cached_property
def center_of_propellant_mass(self):
"""Center of propellant mass is always zero"""
return Function(0.0)

# Propellant inertias: always zero, but return as Function objects
def _zero_inertia_func(self):
return Function(0.0)

@cached_property
def propellant_I_11(self): return self._zero_inertia_func()
@cached_property
def propellant_I_12(self): return self._zero_inertia_func()
@cached_property
def propellant_I_13(self): return self._zero_inertia_func()
@cached_property
def propellant_I_22(self): return self._zero_inertia_func()
@cached_property
def propellant_I_23(self): return self._zero_inertia_func()
@cached_property
def propellant_I_33(self): return self._zero_inertia_func()
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
90 changes: 90 additions & 0 deletions rocketpy/rocket/rocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2003,3 +2003,93 @@ def from_dict(cls, data):
)

return rocket

class PointMassRocket(Rocket):
def __init__(
self,
radius: float = 0.05,
mass: float = 0,
center_of_mass_without_motor: float = 0,
power_off_drag: float = 0.4,
power_on_drag: float = 0.4,
):
self._center_of_mass_without_motor_pointmass = center_of_mass_without_motor
self._center_of_dry_mass_position = center_of_mass_without_motor
self._center_of_mass = center_of_mass_without_motor
# Dry inertias are zero for point mass
self._dry_I_11 = 0.0
self._dry_I_22 = 0.0
self._dry_I_33 = 0.0
self._dry_I_12 = 0.0
self._dry_I_13 = 0.0
self._dry_I_23 = 0.0

# Call base init with safe defaults
super().__init__(
radius=radius,
mass=mass,
inertia=(0, 0, 0),
power_off_drag=power_off_drag,
power_on_drag=power_on_drag,
center_of_mass_without_motor=center_of_mass_without_motor,
)

# ------------------------------------------------------------------
# Center of Mass Properties
# ------------------------------------------------------------------
@property
def center_of_mass_without_motor(self):
return self._center_of_mass_without_motor_pointmass

@center_of_mass_without_motor.setter
def center_of_mass_without_motor(self, value):
self._center_of_mass_without_motor_pointmass = value

@property
def center_of_dry_mass_position(self):
return self._center_of_dry_mass_position

@center_of_dry_mass_position.setter
def center_of_dry_mass_position(self, value):
self._center_of_dry_mass_position = value

@property
def center_of_mass(self):
return self._center_of_mass

@center_of_mass.setter
def center_of_mass(self, value):
self._center_of_mass = value

# ------------------------------------------------------------------
# Inertia Properties (always zero)
# ------------------------------------------------------------------
@property
def dry_I_11(self): return 0.0
@dry_I_11.setter
def dry_I_11(self, value): self._dry_I_11 = 0.0

@property
def dry_I_22(self): return 0.0
@dry_I_22.setter
def dry_I_22(self, value): self._dry_I_22 = 0.0

@property
def dry_I_33(self): return 0.0
@dry_I_33.setter
def dry_I_33(self, value): self._dry_I_33 = 0.0

@property
def dry_I_12(self): return 0.0
@dry_I_12.setter
def dry_I_12(self, value): self._dry_I_12 = 0.0

@property
def dry_I_13(self): return 0.0
@dry_I_13.setter
def dry_I_13(self, value): self._dry_I_13 = 0.0

@property
def dry_I_23(self): return 0.0
@dry_I_23.setter
def dry_I_23(self, value): self._dry_I_23 = 0.0
Loading
Loading