diff --git a/CHANGELOG.md b/CHANGELOG.md
index f68f10b8c..bc4f552b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,7 +32,7 @@ Attention: The newest changes should be on top -->
### Added
-
+- ENH: add structural to total mass ratio for motor and rocket [#713](https://github.com/RocketPy-Team/RocketPy/pull/713)
### Changed
diff --git a/README.md b/README.md
index 8942d9048..15127e966 100644
--- a/README.md
+++ b/README.md
@@ -7,18 +7,18 @@
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/RocketPy-Team/rocketpy/blob/master/docs/notebooks/getting_started_colab.ipynb)
-[![PyPI](https://img.shields.io/pypi/v/rocketpy?color=g)](https://pypi.org/project/rocketpy/)
[![Documentation Status](https://readthedocs.org/projects/rocketpyalpha/badge/?version=latest)](https://docs.rocketpy.org/en/latest/?badge=latest)
+[![PyPI](https://img.shields.io/pypi/v/rocketpy?color=g)](https://pypi.org/project/rocketpy/)
+![Conda Version](https://img.shields.io/conda/v/conda-forge/rocketpy?color=g)
[![codecov](https://codecov.io/gh/RocketPy-Team/RocketPy/graph/badge.svg?token=Ecc3bsHFeP)](https://codecov.io/gh/RocketPy-Team/RocketPy)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Contributors](https://img.shields.io/github/contributors/RocketPy-Team/rocketpy)](https://github.com/RocketPy-Team/RocketPy/graphs/contributors)
-[![Chat on Discord](https://img.shields.io/discord/765037887016140840?logo=discord)](https://discord.gg/b6xYnNh)
[![Sponsor RocketPy](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/RocketPy-Team)
+[![Chat on Discord](https://img.shields.io/discord/765037887016140840?logo=discord)](https://discord.gg/b6xYnNh)
[![Instagram](https://img.shields.io/badge/Instagram-E4405F?style=flat&logo=instagram&logoColor=white)](https://www.instagram.com/rocketpyteam)
[![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=flat&logo=linkedin&logoColor=white)](https://www.linkedin.com/company/rocketpy)
[![DOI](https://img.shields.io/badge/DOI-10.1061%2F%28ASCE%29AS.1943--5525.0001331-blue.svg)](http://dx.doi.org/10.1061/%28ASCE%29AS.1943-5525.0001331)
-
-
# RocketPy
RocketPy is the next-generation trajectory simulation solution for High-Power Rocketry. The code is written as a [Python](http://www.python.org) library and allows for a complete 6 degrees of freedom simulation of a rocket's flight trajectory, including high-fidelity variable mass effects as well as descent under parachutes. Weather conditions, such as wind profiles, can be imported from sophisticated datasets, allowing for realistic scenarios. Furthermore, the implementation facilitates complex simulations, such as multi-stage rockets, design and trajectory optimization and dispersion analysis.
@@ -80,6 +80,9 @@ Flight data and rocket parameters used in this comparison were kindly provided b
| NDRT launch vehicle | Apogee time (s) | 16.77 | 17.10 | **-1.90 %** |
| NDRT launch vehicle | Maximum velocity (m/s) | 172.86 | 168.95 | **2.31 %** |
+Over years of development and testing, RocketPy has been validated across an expanding range of flight scenarios.
+For more information on these validated flights, visit our [Flight Examples](https://docs.rocketpy.org/en/latest/examples/index.html) page in the documentation.
+
# Documentation
Check out documentation details using the links below:
@@ -352,7 +355,7 @@ You can also become a [sponsor](https://github.com/sponsors/RocketPy-Team) and h
If you are actively using RocketPy in one of your projects, reaching out to our core team via [Discord](https://discord.gg/b6xYnNh) and providing feedback can help improve RocketPy a lot!
-And if you are interested in going one step further, please read [CONTRIBUTING.md](https://github.com/RocketPy-Team/RocketPy/blob/master/CONTRIBUTING.md) for details on our code of conduct and learn more about how you can contribute to the development of this next-gen trajectory simulation solution for rocketry.
+And if you are interested in going one step further, please read the [development documentation](https://docs.rocketpy.org/en/latest/development/index.html) to learn more about how you can contribute to the development of this next-gen trajectory simulation solution for rocketry.
## License
diff --git a/rocketpy/motors/hybrid_motor.py b/rocketpy/motors/hybrid_motor.py
index 1a1bbb3db..2916486d0 100644
--- a/rocketpy/motors/hybrid_motor.py
+++ b/rocketpy/motors/hybrid_motor.py
@@ -72,6 +72,8 @@ class HybridMotor(Motor):
HybridMotor.propellant_mass : Function
Total propellant mass in kg as a function of time, this includes the
mass of fluids in each tank and the mass of the solid grains.
+ HybridMotor.structural_mass_ratio: float
+ Initial ratio between the dry mass and the total mass.
HybridMotor.total_mass_flow_rate : Function
Time derivative of propellant total mass in kg/s as a function
of time as obtained by the thrust source.
diff --git a/rocketpy/motors/liquid_motor.py b/rocketpy/motors/liquid_motor.py
index 9ec3d1130..cde0e9d03 100644
--- a/rocketpy/motors/liquid_motor.py
+++ b/rocketpy/motors/liquid_motor.py
@@ -47,6 +47,8 @@ class LiquidMotor(Motor):
LiquidMotor.propellant_mass : Function
Total propellant mass in kg as a function of time, includes fuel
and oxidizer.
+ LiquidMotor.structural_mass_ratio: float
+ Initial ratio between the dry mass and the total mass.
LiquidMotor.total_mass_flow_rate : Function
Time derivative of propellant total mass in kg/s as a function
of time as obtained by the tanks mass flow.
diff --git a/rocketpy/motors/motor.py b/rocketpy/motors/motor.py
index 5fa154d88..e0e6dfc9a 100644
--- a/rocketpy/motors/motor.py
+++ b/rocketpy/motors/motor.py
@@ -49,6 +49,8 @@ class Motor(ABC):
Motor.propellant_mass : Function
Total propellant mass in kg as a function of time, including solid,
liquid and gas phases.
+ Motor.structural_mass_ratio: float
+ Initial ratio between the dry mass and the total mass.
Motor.total_mass_flow_rate : Function
Time derivative of propellant total mass in kg/s as a function
of time as obtained by the thrust source.
@@ -497,6 +499,24 @@ def propellant_initial_mass(self):
Propellant initial mass in kg.
"""
+ @property
+ def structural_mass_ratio(self):
+ """Calculates the structural mass ratio. The ratio is defined as
+ the dry mass divided by the initial total mass.
+
+ Returns
+ -------
+ float
+ Initial structural mass ratio.
+ """
+ initial_total_mass = self.dry_mass + self.propellant_initial_mass
+ try:
+ return self.dry_mass / initial_total_mass
+ except ZeroDivisionError as e:
+ raise ValueError(
+ "Total motor mass (dry + propellant) cannot be zero"
+ ) from e
+
@funcify_method("Time (s)", "Motor center of mass (m)")
def center_of_mass(self):
"""Position of the center of mass as a function of time. The position
@@ -1502,6 +1522,7 @@ def __init__(self):
self.nozzle_radius = 0
self.thrust = Function(0, "Time (s)", "Thrust (N)")
self.propellant_mass = Function(0, "Time (s)", "Propellant Mass (kg)")
+ self.propellant_initial_mass = 0
self.total_mass = Function(0, "Time (s)", "Total Mass (kg)")
self.total_mass_flow_rate = Function(
0, "Time (s)", "Mass Depletion Rate (kg/s)"
diff --git a/rocketpy/motors/solid_motor.py b/rocketpy/motors/solid_motor.py
index 81faf453f..f6f09967e 100644
--- a/rocketpy/motors/solid_motor.py
+++ b/rocketpy/motors/solid_motor.py
@@ -70,6 +70,8 @@ class SolidMotor(Motor):
of propellant and dry mass.
SolidMotor.propellant_mass : Function
Total propellant mass in kg as a function of time.
+ SolidMotor.structural_mass_ratio: float
+ Initial ratio between the dry mass and the total mass.
SolidMotor.total_mass_flow_rate : Function
Time derivative of propellant total mass in kg/s as a function
of time as obtained by the thrust source.
diff --git a/rocketpy/prints/hybrid_motor_prints.py b/rocketpy/prints/hybrid_motor_prints.py
index 4dcd7b113..e69d291f5 100644
--- a/rocketpy/prints/hybrid_motor_prints.py
+++ b/rocketpy/prints/hybrid_motor_prints.py
@@ -77,6 +77,7 @@ def motor_details(self):
print(
f"Total Propellant Mass: {self.hybrid_motor.propellant_initial_mass:.3f} kg"
)
+ print(f"Structural Mass Ratio: {self.hybrid_motor.structural_mass_ratio:.3f}")
avg = self.hybrid_motor.exhaust_velocity.average(*self.hybrid_motor.burn_time)
print(f"Average Propellant Exhaust Velocity: {avg:.3f} m/s")
print(f"Average Thrust: {self.hybrid_motor.average_thrust:.3f} N")
diff --git a/rocketpy/prints/liquid_motor_prints.py b/rocketpy/prints/liquid_motor_prints.py
index fb493ed0a..4c80326ab 100644
--- a/rocketpy/prints/liquid_motor_prints.py
+++ b/rocketpy/prints/liquid_motor_prints.py
@@ -47,6 +47,7 @@ def motor_details(self):
print(
f"Total Propellant Mass: {self.liquid_motor.propellant_initial_mass:.3f} kg"
)
+ print(f"Structural Mass Ratio: {self.liquid_motor.structural_mass_ratio:.3f}")
avg = self.liquid_motor.exhaust_velocity.average(*self.liquid_motor.burn_time)
print(f"Average Propellant Exhaust Velocity: {avg:.3f} m/s")
print(f"Average Thrust: {self.liquid_motor.average_thrust:.3f} N")
diff --git a/rocketpy/prints/motor_prints.py b/rocketpy/prints/motor_prints.py
index d9b7fbc98..f5efff8e0 100644
--- a/rocketpy/prints/motor_prints.py
+++ b/rocketpy/prints/motor_prints.py
@@ -35,6 +35,7 @@ def motor_details(self):
print("Motor Details")
print("Total Burning Time: " + str(self.motor.burn_out_time) + " s")
print(f"Total Propellant Mass: {self.motor.propellant_initial_mass:.3f} kg")
+ print(f"Structural Mass Ratio: {self.motor.structural_mass_ratio:.3f}")
print(
"Average Propellant Exhaust Velocity: "
f"{self.motor.exhaust_velocity.average(*self.motor.burn_time):.3f} m/s"
diff --git a/rocketpy/prints/rocket_prints.py b/rocketpy/prints/rocket_prints.py
index c9f5585ac..7b768ea2f 100644
--- a/rocketpy/prints/rocket_prints.py
+++ b/rocketpy/prints/rocket_prints.py
@@ -36,6 +36,7 @@ def inertia_details(self):
print(f"Rocket Mass: {self.rocket.mass:.3f} kg (without motor)")
print(f"Rocket Dry Mass: {self.rocket.dry_mass:.3f} kg (with unloaded motor)")
print(f"Rocket Loaded Mass: {self.rocket.total_mass(0):.3f} kg")
+ print(f"Rocket Structural Mass Ratio: {self.rocket.structural_mass_ratio:.3f}")
print(
f"Rocket Inertia (with unloaded motor) 11: {self.rocket.dry_I_11:.3f} kg*m2"
)
diff --git a/rocketpy/prints/solid_motor_prints.py b/rocketpy/prints/solid_motor_prints.py
index c37a9b69e..6f4c28d5b 100644
--- a/rocketpy/prints/solid_motor_prints.py
+++ b/rocketpy/prints/solid_motor_prints.py
@@ -65,6 +65,7 @@ def motor_details(self):
print(
f"Total Propellant Mass: {self.solid_motor.propellant_initial_mass:.3f} kg"
)
+ print(f"Structural Mass Ratio: {self.solid_motor.structural_mass_ratio:.3f}")
average = self.solid_motor.exhaust_velocity.average(*self.solid_motor.burn_time)
print(f"Average Propellant Exhaust Velocity: {average:.3f} m/s")
print(f"Average Thrust: {self.solid_motor.average_thrust:.3f} N")
diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py
index 758447fde..ed376f582 100644
--- a/rocketpy/rocket/rocket.py
+++ b/rocketpy/rocket/rocket.py
@@ -89,6 +89,8 @@ class Rocket:
Function of time expressing the total mass of the rocket,
defined as the sum of the propellant mass and the rocket
mass without propellant.
+ Rocket.structural_mass_ratio: float
+ Initial ratio between the dry mass and the total mass.
Rocket.total_mass_flow_rate : Function
Time derivative of rocket's total mass in kg/s as a function
of time as obtained by the thrust source of the added motor.
@@ -361,6 +363,7 @@ def __init__( # pylint: disable=too-many-statements
# calculate dynamic inertial quantities
self.evaluate_dry_mass()
+ self.evaluate_structural_mass_ratio()
self.evaluate_total_mass()
self.evaluate_center_of_dry_mass()
self.evaluate_center_of_mass()
@@ -433,6 +436,28 @@ def evaluate_dry_mass(self):
return self.dry_mass
+ def evaluate_structural_mass_ratio(self):
+ """Calculates and returns the rocket's structural mass ratio.
+ It is defined as the ratio between of the dry mass
+ (Motor + Rocket) and the initial total mass
+ (Motor + Propellant + Rocket).
+
+ Returns
+ -------
+ self.structural_mass_ratio: float
+ Initial structural mass ratio dry mass (Rocket + Motor) (kg)
+ divided by total mass (Rocket + Motor + Propellant) (kg).
+ """
+ try:
+ self.structural_mass_ratio = self.dry_mass / (
+ self.dry_mass + self.motor.propellant_initial_mass
+ )
+ except ZeroDivisionError as e:
+ raise ValueError(
+ "Total rocket mass (dry + propellant) cannot be zero"
+ ) from e
+ return self.structural_mass_ratio
+
def evaluate_center_of_mass(self):
"""Evaluates rocket center of mass position relative to user defined
rocket reference system.
@@ -951,6 +976,7 @@ def add_motor(self, motor, position): # pylint: disable=too-many-statements
self.nozzle_position = self.motor.nozzle_position * _ + self.motor_position
self.total_mass_flow_rate = self.motor.total_mass_flow_rate
self.evaluate_dry_mass()
+ self.evaluate_structural_mass_ratio()
self.evaluate_total_mass()
self.evaluate_center_of_dry_mass()
self.evaluate_nozzle_to_cdm()