Skip to content

Commit e6a3d2b

Browse files
DOCS: Update file paths in flight_comparator example and improve formatting in simulation files
1 parent 45a891e commit e6a3d2b

6 files changed

Lines changed: 174 additions & 158 deletions

File tree

docs/user/flight_comparator.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ First, let's create the standard RocketPy simulation that will serve as our
7474
radius=127 / 2000,
7575
mass=19.197 - 2.956,
7676
inertia=(6.321, 6.321, 0.034),
77-
power_off_drag="../data/calisto/powerOffDragCurve.csv",
78-
power_on_drag="../data/calisto/powerOnDragCurve.csv",
77+
power_off_drag="../data/rockets/calisto/powerOffDragCurve.csv",
78+
power_on_drag="../data/rockets/calisto/powerOnDragCurve.csv",
7979
center_of_mass_without_motor=0,
8080
coordinate_system_orientation="tail_to_nose",
8181
)
@@ -101,17 +101,17 @@ First, let's create the standard RocketPy simulation that will serve as our
101101
position=-1.194656,
102102
)
103103

104-
# 4. Simulate
105-
flight = Flight(
106-
rocket=calisto,
107-
environment=env,
108-
rail_length=5.2,
109-
inclination=85,
110-
heading=0,
111-
)
104+
# 4. Simulate
105+
flight = Flight(
106+
rocket=calisto,
107+
environment=env,
108+
rail_length=5.2,
109+
inclination=85,
110+
heading=0,
111+
)
112112

113-
# 5. Create FlightComparator instance
114-
comparator = FlightComparator(flight)
113+
# 5. Create FlightComparator instance
114+
comparator = FlightComparator(flight)
115115

116116
Adding Another Flight Object
117117
----------------------------
@@ -156,7 +156,7 @@ simulation:
156156

157157
# Add the external data to our comparator
158158
comparator.add_data(
159-
"External Simulator",
159+
"External Simulator",
160160
{
161161
"altitude": (time_external, external_altitude),
162162
"vz": (time_external, external_velocity),

rocketpy/plots/motor_plots.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import matplotlib.pyplot as plt
22
import numpy as np
3-
from matplotlib.patches import Polygon
43
from matplotlib.animation import FuncAnimation
4+
from matplotlib.patches import Polygon
55

6-
from ..plots.plot_helpers import show_or_save_plot, show_or_save_animation
6+
from ..plots.plot_helpers import show_or_save_animation, show_or_save_plot
77

88

99
class _MotorPlots:

rocketpy/plots/tank_plots.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import matplotlib.pyplot as plt
22
import numpy as np
3-
from matplotlib.patches import Polygon
43
from matplotlib.animation import FuncAnimation
4+
from matplotlib.patches import Polygon
55

66
from rocketpy.mathutils.function import Function
77

8-
from .plot_helpers import show_or_save_plot, show_or_save_animation
8+
from .plot_helpers import show_or_save_animation, show_or_save_plot
99

1010

1111
class _TankPlots:

rocketpy/simulation/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
from .flight import Flight
2+
from .flight_comparator import FlightComparator
23
from .flight_data_exporter import FlightDataExporter
34
from .flight_data_importer import FlightDataImporter
45
from .monte_carlo import MonteCarlo
56
from .multivariate_rejection_sampler import MultivariateRejectionSampler
7+
8+
__all__ = [
9+
"Flight",
10+
"FlightDataExporter",
11+
"FlightDataImporter",
12+
"FlightComparator",
13+
"MonteCarlo",
14+
"MultivariateRejectionSampler",
15+
]

rocketpy/simulation/flight.py

Lines changed: 145 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -3720,6 +3720,151 @@ def max_rail_button2_shear_force(self):
37203720
"""Maximum lower rail button shear force, in Newtons."""
37213721
return np.abs(self.rail_button2_shear_force.y_array).max()
37223722

3723+
@cached_property
3724+
def calculate_rail_button_bending_moments(self):
3725+
"""Calculate internal bending moments at rail button attachment points.
3726+
3727+
This method uses beam theory to determine the internal structural
3728+
moments for stress analysis of the rail button attachments (fasteners
3729+
and airframe).
3730+
3731+
The bending moment at each button attachment consists of:
3732+
3733+
1. Bending from shear force at button contact point: $M = S \\times h$,
3734+
where $S$ is the shear (tangential) force and $h$ is button height.
3735+
2. Direct moment contribution from the button's reaction forces.
3736+
3737+
Returns
3738+
-------
3739+
tuple
3740+
rail_button1_bending_moment : Function
3741+
Bending moment at upper rail button as a function of time (N·m).
3742+
max_rail_button1_bending_moment : float
3743+
Maximum upper rail button bending moment (N·m).
3744+
rail_button2_bending_moment : Function
3745+
Bending moment at lower rail button as a function of time (N·m).
3746+
max_rail_button2_bending_moment : float
3747+
Maximum lower rail button bending moment (N·m).
3748+
3749+
Notes
3750+
-----
3751+
This calculation is meaningful only during the rail phase of flight.
3752+
Maximum values use absolute values for worst-case stress analysis.
3753+
The bending moments represent internal stresses in the rocket airframe
3754+
at the rail button attachment points.
3755+
3756+
**Assumptions:**
3757+
3758+
- Rail buttons act as simple supports: provide reaction forces (normal
3759+
and shear) but no moment reaction at the rail contact point.
3760+
- The rocket acts as a beam supported at two points (rail buttons).
3761+
- Bending moments arise from the lever arm effect of reaction forces
3762+
and the cantilever moment from button standoff height.
3763+
- Normal force moment: M = N x d, where N is normal reaction force
3764+
and d is distance from button to center of dry mass.
3765+
- Shear force cantilever moment: M = S x h, where S is shear force
3766+
and h is button standoff height.
3767+
3768+
Examples
3769+
--------
3770+
>>> moments = flight.calculate_rail_button_bending_moments
3771+
>>> print(moments[1]) # max rail button 1 bending moment
3772+
>>> print(moments[3]) # max rail button 2 bending moment
3773+
"""
3774+
# Check if rail buttons exist
3775+
null_moment = Function(0)
3776+
if len(self.rocket.rail_buttons) == 0:
3777+
warnings.warn(
3778+
"Trying to calculate rail button bending moments without "
3779+
"rail buttons defined. Setting moments to zero.",
3780+
UserWarning,
3781+
)
3782+
return (null_moment, 0.0, null_moment, 0.0)
3783+
3784+
# Get rail button geometry
3785+
rail_buttons_tuple = self.rocket.rail_buttons[0]
3786+
# Rail button standoff height
3787+
h_button = rail_buttons_tuple.component.button_height
3788+
if h_button is None:
3789+
warnings.warn(
3790+
"Rail button height not defined. Bending moments cannot be "
3791+
"calculated. Setting moments to zero.",
3792+
UserWarning,
3793+
)
3794+
return (null_moment, 0.0, null_moment, 0.0)
3795+
upper_button_position = (
3796+
rail_buttons_tuple.component.buttons_distance
3797+
+ rail_buttons_tuple.position.z
3798+
)
3799+
lower_button_position = rail_buttons_tuple.position.z
3800+
3801+
# Get center of dry mass (handle both callable and property)
3802+
if callable(self.rocket.center_of_dry_mass_position):
3803+
cdm = self.rocket.center_of_dry_mass_position(self.rocket._csys)
3804+
else:
3805+
cdm = self.rocket.center_of_dry_mass_position
3806+
3807+
# Distances from buttons to center of dry mass
3808+
d1 = abs(upper_button_position - cdm)
3809+
d2 = abs(lower_button_position - cdm)
3810+
3811+
# forces
3812+
N1 = self.rail_button1_normal_force
3813+
N2 = self.rail_button2_normal_force
3814+
S1 = self.rail_button1_shear_force
3815+
S2 = self.rail_button2_shear_force
3816+
t = N1.source[:, 0]
3817+
3818+
# Calculate bending moments at attachment points
3819+
# Primary contribution from shear force acting at button height
3820+
# Secondary contribution from normal force creating moment about attachment
3821+
m1_values = N2.source[:, 1] * d2 + S1.source[:, 1] * h_button
3822+
m2_values = N1.source[:, 1] * d1 + S2.source[:, 1] * h_button
3823+
3824+
rail_button1_bending_moment = Function(
3825+
np.column_stack([t, m1_values]),
3826+
inputs="Time (s)",
3827+
outputs="Bending Moment (N·m)",
3828+
interpolation="linear",
3829+
)
3830+
rail_button2_bending_moment = Function(
3831+
np.column_stack([t, m2_values]),
3832+
inputs="Time (s)",
3833+
outputs="Bending Moment (N·m)",
3834+
interpolation="linear",
3835+
)
3836+
3837+
# Maximum bending moments (absolute value for stress calculations)
3838+
max_rail_button1_bending_moment = float(np.max(np.abs(m1_values)))
3839+
max_rail_button2_bending_moment = float(np.max(np.abs(m2_values)))
3840+
3841+
return (
3842+
rail_button1_bending_moment,
3843+
max_rail_button1_bending_moment,
3844+
rail_button2_bending_moment,
3845+
max_rail_button2_bending_moment,
3846+
)
3847+
3848+
@property
3849+
def rail_button1_bending_moment(self):
3850+
"""Upper rail button bending moment as a Function of time."""
3851+
return self.calculate_rail_button_bending_moments[0]
3852+
3853+
@property
3854+
def max_rail_button1_bending_moment(self):
3855+
"""Maximum upper rail button bending moment, in N·m."""
3856+
return self.calculate_rail_button_bending_moments[1]
3857+
3858+
@property
3859+
def rail_button2_bending_moment(self):
3860+
"""Lower rail button bending moment as a Function of time."""
3861+
return self.calculate_rail_button_bending_moments[2]
3862+
3863+
@property
3864+
def max_rail_button2_bending_moment(self):
3865+
"""Maximum lower rail button bending moment, in N·m."""
3866+
return self.calculate_rail_button_bending_moments[3]
3867+
37233868
@funcify_method(
37243869
"Time (s)", "Horizontal Distance to Launch Point (m)", "spline", "constant"
37253870
)
@@ -4516,142 +4661,3 @@ def __lt__(self, other):
45164661
otherwise.
45174662
"""
45184663
return self.t < other.t
4519-
4520-
@cached_property
4521-
def calculate_rail_button_bending_moments(self):
4522-
"""
4523-
Calculate internal bending moments at rail button attachment points.
4524-
4525-
Uses beam theory to determine internal structural moments for stress
4526-
analysis of the rail button attachments (fasteners and airframe).
4527-
4528-
The bending moment at each button attachment consists of:
4529-
1. Bending from shear force at button contact point: M = S × h
4530-
where S is the shear (tangential) force and h is button height
4531-
2. Direct moment contribution from the button's reaction forces
4532-
4533-
Assumptions
4534-
-----------
4535-
- Rail buttons act as simple supports: provide reaction forces (normal
4536-
and shear) but no moment reaction at the rail contact point.
4537-
- The rocket acts as a beam supported at two points (rail buttons).
4538-
- Bending moments arise from the lever arm effect of reaction forces
4539-
and the cantilever moment from button standoff height.
4540-
4541-
The bending moment at each button attachment consists of:
4542-
1. Normal force moment: M = N x d, where N is normal reaction force
4543-
and d is distance from button to center of dry mass
4544-
2. Shear force cantilever moment: M = S x h, where S is shear force
4545-
and h is button standoff height
4546-
4547-
Notes
4548-
-----
4549-
- Calculated only during the rail phase of flight
4550-
- Maximum values use absolute values for worst-case stress analysis
4551-
- The bending moments represent internal stresses in the rocket
4552-
airframe at the rail button attachment points
4553-
4554-
Returns
4555-
-------
4556-
tuple
4557-
(rail_button1_bending_moment : Function,
4558-
max_rail_button1_bending_moment : float,
4559-
rail_button2_bending_moment : Function,
4560-
max_rail_button2_bending_moment : float)
4561-
4562-
Where rail_button1/2_bending_moment are Function objects of time
4563-
in N·m, and max values are floats in N·m.
4564-
"""
4565-
# Check if rail buttons exist
4566-
null_moment = Function(0)
4567-
if len(self.rocket.rail_buttons) == 0:
4568-
warnings.warn(
4569-
"Trying to calculate rail button bending moments without "
4570-
"rail buttons defined. Setting moments to zero.",
4571-
UserWarning,
4572-
)
4573-
return (null_moment, 0.0, null_moment, 0.0)
4574-
4575-
# Get rail button geometry
4576-
rail_buttons_tuple = self.rocket.rail_buttons[0]
4577-
# Rail button standoff height
4578-
h_button = rail_buttons_tuple.component.button_height
4579-
if h_button is None:
4580-
warnings.warn(
4581-
"Rail button height not defined. Bending moments cannot be "
4582-
"calculated. Setting moments to zero.",
4583-
UserWarning,
4584-
)
4585-
return (null_moment, 0.0, null_moment, 0.0)
4586-
upper_button_position = (
4587-
rail_buttons_tuple.component.buttons_distance
4588-
+ rail_buttons_tuple.position.z
4589-
)
4590-
lower_button_position = rail_buttons_tuple.position.z
4591-
4592-
# Get center of dry mass (handle both callable and property)
4593-
if callable(self.rocket.center_of_dry_mass_position):
4594-
cdm = self.rocket.center_of_dry_mass_position(self.rocket._csys)
4595-
else:
4596-
cdm = self.rocket.center_of_dry_mass_position
4597-
4598-
# Distances from buttons to center of dry mass
4599-
d1 = abs(upper_button_position - cdm)
4600-
d2 = abs(lower_button_position - cdm)
4601-
4602-
# forces
4603-
N1 = self.rail_button1_normal_force
4604-
N2 = self.rail_button2_normal_force
4605-
S1 = self.rail_button1_shear_force
4606-
S2 = self.rail_button2_shear_force
4607-
t = N1.source[:, 0]
4608-
4609-
# Calculate bending moments at attachment points
4610-
# Primary contribution from shear force acting at button height
4611-
# Secondary contribution from normal force creating moment about attachment
4612-
m1_values = N2.source[:, 1] * d2 + S1.source[:, 1] * h_button
4613-
m2_values = N1.source[:, 1] * d1 + S2.source[:, 1] * h_button
4614-
4615-
rail_button1_bending_moment = Function(
4616-
np.column_stack([t, m1_values]),
4617-
inputs="Time (s)",
4618-
outputs="Bending Moment (N·m)",
4619-
interpolation="linear",
4620-
)
4621-
rail_button2_bending_moment = Function(
4622-
np.column_stack([t, m2_values]),
4623-
inputs="Time (s)",
4624-
outputs="Bending Moment (N·m)",
4625-
interpolation="linear",
4626-
)
4627-
4628-
# Maximum bending moments (absolute value for stress calculations)
4629-
max_rail_button1_bending_moment = float(np.max(np.abs(m1_values)))
4630-
max_rail_button2_bending_moment = float(np.max(np.abs(m2_values)))
4631-
4632-
return (
4633-
rail_button1_bending_moment,
4634-
max_rail_button1_bending_moment,
4635-
rail_button2_bending_moment,
4636-
max_rail_button2_bending_moment,
4637-
)
4638-
4639-
@property
4640-
def rail_button1_bending_moment(self):
4641-
"""Upper rail button bending moment as a Function of time."""
4642-
return self.calculate_rail_button_bending_moments[0]
4643-
4644-
@property
4645-
def max_rail_button1_bending_moment(self):
4646-
"""Maximum upper rail button bending moment, in N·m."""
4647-
return self.calculate_rail_button_bending_moments[1]
4648-
4649-
@property
4650-
def rail_button2_bending_moment(self):
4651-
"""Lower rail button bending moment as a Function of time."""
4652-
return self.calculate_rail_button_bending_moments[2]
4653-
4654-
@property
4655-
def max_rail_button2_bending_moment(self):
4656-
"""Maximum lower rail button bending moment, in N·m."""
4657-
return self.calculate_rail_button_bending_moments[3]

0 commit comments

Comments
 (0)