-
-
Notifications
You must be signed in to change notification settings - Fork 193
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
base: develop
Are you sure you want to change the base?
Changes from all commits
e557e17
a768648
9b00c57
87e9180
4524219
57b4732
81ce869
0673076
47c9f2f
f6ad658
33cb63a
1d2d4dc
fe21271
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,8 @@ | |
Parachute, | ||
RailButtons, | ||
Rocket, | ||
BaseRocket, | ||
PointMassRocket, | ||
Tail, | ||
TrapezoidalFins, | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
from functools import cached_property | ||
import numpy as np | ||
from ..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.""" | ||
|
||
def __init__( | ||
self, | ||
thrust_source, | ||
dry_mass, | ||
thrust_curve=None, | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
propellant_initial_mass=None, | ||
propellant_final_mass=None, | ||
burn_time=None, | ||
center_of_dry_mass_position=0, | ||
reshape_thrust_curve=False, | ||
interpolation_method="linear", | ||
coordinate_system_orientation="nozzle_to_combustion_chamber", | ||
): | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""Initialize the PointMassMotor class. | ||
|
||
Parameters | ||
---------- | ||
thrust_source : int, float, callable, string, array, Function | ||
Thrust source similar to the Motor class. | ||
dry_mass : float | ||
Total dry mass of the motor in kg. | ||
thrust_curve : Function, np.array, or str (csv file), optional | ||
Required if thrust_source is a csv file, Function, or np.array. | ||
propellant_initial_mass : float, optional | ||
Required if thrust_source is a csv file, Function, or np.array. | ||
propellant_final_mass : float, optional | ||
Required if thrust_source is callable. | ||
burn_time : float or tuple of float, optional | ||
Required if thrust_source is callable or if a thrust value is given. | ||
center_of_dry_mass_position : float, optional | ||
Initial position of the motor, default is 0. | ||
interpolation_method : string, optional | ||
Interpolation method for thrust curve, default is 'linear'. | ||
""" | ||
if isinstance(thrust_source, (Function, np.ndarray, str)): | ||
if thrust_curve is None or propellant_initial_mass is None: | ||
raise ValueError("thrust_curve and propellant_initial_mass are required for csv, Function, or np.array inputs.") | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
elif callable(thrust_source): | ||
if any(param is None for param in [thrust_curve, propellant_initial_mass, burn_time, propellant_final_mass]): | ||
raise ValueError("thrust_curve, propellant_initial_mass, burn_time, and propellant_final_mass are required for callable inputs.") | ||
elif isinstance(thrust_source, (int, float)): | ||
if any(param is None for param in [thrust_curve, propellant_initial_mass, burn_time]): | ||
raise ValueError("thrust_curve, propellant_initial_mass, and burn_time are required when a thrust value is given.") | ||
|
||
self._propellant_initial_mass = propellant_initial_mass | ||
super().__init__( | ||
thrust_source=thrust_source, | ||
dry_inertia=(0, 0, 0), | ||
nozzle_radius=0, | ||
center_of_dry_mass_position=center_of_dry_mass_position, | ||
dry_mass=dry_mass, | ||
nozzle_position=0, | ||
burn_time=burn_time, | ||
reshape_thrust_curve=reshape_thrust_curve, | ||
interpolation_method=interpolation_method, | ||
coordinate_system_orientation=coordinate_system_orientation, | ||
) | ||
@funcify_method("Time (s)", "Thrust (N)") | ||
Comment on lines
+55
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are not using the propellant_final_mass at all. Unless you think this is valuable to simulate when considering motors that end burning with propellant still in them (I guess some hybrids and liquids could have that) then I think you can delete it. Currently the |
||
def thrust(self): | ||
"""Returns the thrust of the motor as a function of time.""" | ||
return self.thrust_source | ||
|
||
@funcify_method("Time (s)", "Acceleration (m/s^2)") | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def total_mass(self): | ||
"""Returns the constant total mass of the point mass motor.""" | ||
return self.dry_mass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me it makes sense to evaluate the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I was confused about what to implement for mass flow rate. Like different types of motors have different ways to calculate this. When it comes to point mass motor the problem I faced was that the input can be any kind of data irrespective of propellant nature. Thus could not understand which equations to implement for mass flow rate. |
||
|
||
@funcify_method("Time (s)", "Acceleration (m/s^2)") | ||
def acceleration(self): | ||
"""Computes the acceleration of the motor as thrust divided by mass.""" | ||
return self.thrust() / self.total_mass | ||
|
||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@funcify_method("Time (s)", "Propellant Mass (kg)") | ||
def center_of_propellant_mass(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Exhaust Velocity (m/s)") | ||
def exhaust_velocity(self): | ||
return 2000 # m/s, estimated value | ||
Comment on lines
+87
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this should be calculate in the same way that SolidMotor does. @phmbressan can you confirm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar confusion as mass flow rate here. |
||
|
||
@funcify_method("Time (s)", "Propellant Mass (kg)") | ||
def propellant_initial_mass(self): | ||
return self._propellant_initial_mass | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@property | ||
def is_point_mass(self): | ||
aZira371 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return True | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_11(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_12(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_13(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_22(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_23(self): | ||
return 0 | ||
|
||
@funcify_method("Time (s)", "Inertia (kg·m²)") | ||
def propellant_I_33(self): | ||
return 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,88 @@ | |
from rocketpy.rocket.parachute import Parachute | ||
from rocketpy.tools import parallel_axis_theorem_from_com | ||
|
||
class BaseRocket: | ||
"""Base class for a rocket model with minimal attributes and methods.""" | ||
def __init__(self, mass): | ||
self.mass = mass | ||
self.motor = None | ||
def add_motor(self, motor): | ||
self.motor = motor | ||
self.evaluate_total_mass() | ||
def evaluate_total_mass(self): | ||
if self.motor: | ||
return self.mass + self.motor.total_mass | ||
return self.mass | ||
|
||
|
||
Comment on lines
+27
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class seems useless right now. You can just remove it |
||
class PointMassRocket(BaseRocket): | ||
"""Rocket modeled as a point mass for 3-DOF simulations.""" | ||
Comment on lines
+41
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe having this inherit from the Rocket class will make the implementation easier. Then all_info and plots and prints will already be defined |
||
|
||
def __init__(self, mass, drag_coefficient=0.4, radius=0.05): | ||
super().__init__(mass) | ||
|
||
# Basic configuration | ||
self.drag_coefficient = drag_coefficient | ||
self.radius = radius # in meters | ||
self.area = math.pi * self.radius**2 | ||
|
||
# Coordinate system configuration | ||
self.coordinate_system_orientation = "tail_to_nose" | ||
self.center_of_dry_mass_position = 0 # arbitrary for point mass | ||
self.dry_mass = mass # dry_mass = structure + dry motor, here it's same as base mass | ||
self.nozzle_position = 0 # or another reference point like -1 * length/2 | ||
|
||
# Components | ||
self.parachutes = [] | ||
self._controllers = [] | ||
self.air_brakes = [] | ||
self.sensors = Components() | ||
self.aerodynamic_surfaces = Components() | ||
self.rail_buttons = Components() | ||
self.surfaces_cp_to_cdm = {} | ||
|
||
# Drag models | ||
self.power_on_drag = Function( | ||
drag_coefficient, | ||
"Mach Number", | ||
"Power-On Drag Coefficient", | ||
interpolation="constant" | ||
) | ||
self.power_off_drag = Function( | ||
drag_coefficient * 1.2, | ||
"Mach Number", | ||
"Power-Off Drag Coefficient", | ||
interpolation="constant" | ||
) | ||
def add_parachute( | ||
self, name, cd_s, trigger, sampling_rate=100, lag=0, noise=(0, 0, 0) | ||
): | ||
parachute = Parachute(name, cd_s, trigger, sampling_rate, lag, noise) | ||
self.parachutes.append(parachute) | ||
self.total_mass = None | ||
self.evaluate_total_mass() | ||
return self.parachutes[-1] | ||
|
||
def evaluate_total_mass(self): | ||
"""Returns Function of total mass (dry + motor).""" | ||
if self.motor is None: | ||
print("Please associate this rocket with a motor!") | ||
return False | ||
|
||
motor_mass_func = ( | ||
self.motor.total_mass if hasattr(self.motor.total_mass, "get_value_opt") | ||
else Function(lambda t: self.motor.total_mass) | ||
) | ||
Comment on lines
+95
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check seems to be in place in order to convert to a |
||
|
||
self.total_mass = Function( | ||
lambda t: self.mass + motor_mass_func(t), | ||
inputs="Time (s)", | ||
outputs="Total Mass (Rocket + Motor + Propellant) (kg)", | ||
title="Total Mass (Rocket + Motor + Propellant)" | ||
) | ||
return self.total_mass | ||
|
||
|
||
# pylint: disable=too-many-instance-attributes, too-many-public-methods, too-many-instance-attributes | ||
class Rocket: | ||
"""Keeps rocket information. | ||
|
Uh oh!
There was an error while loading. Please reload this page.