Skip to content

Commit

Permalink
Merge pull request #510 from RocketPy-Team/bug/fix-fin-flutter
Browse files Browse the repository at this point in the history
BUG: fin_flutter_analysis doesn't find any fin set
  • Loading branch information
Gui-FernandesBR authored Dec 20, 2023
2 parents 08dae44 + 6f4c3c3 commit 086a924
Show file tree
Hide file tree
Showing 5 changed files with 1,178 additions and 65 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ straightforward as possible.
### Fixed

- ENH: Parachute trigger doesn't work if "Apogee" is used instead of "apogee" [#489](https://github.com/RocketPy-Team/RocketPy/pull/489)
- BUG: fin_flutter_analysis doesn't find any fin set [#510](https://github.com/RocketPy-Team/RocketPy/pull/510)
- FIX: EmptyMotor is breaking the Rocket.draw() method [#516](https://github.com/RocketPy-Team/RocketPy/pull/516)

## [v1.1.4] - 2023-12-07
Expand Down
86 changes: 34 additions & 52 deletions rocketpy/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def fin_flutter_analysis(
The fin thickness, in meters
shear_modulus : float
Shear Modulus of fins' material, must be given in Pascal
flight : rocketpy.Flight
flight : Flight
Flight object containing the rocket's flight data
see_prints : boolean, optional
True if you want to see the prints, False otherwise.
Expand All @@ -229,52 +229,52 @@ def fin_flutter_analysis(
------
None
"""
found_fin = False

# First, we need identify if there is at least a fin set in the rocket
for aero_surface in flight.rocket.aerodynamic_surfaces:
# First, we need identify if there is at least one fin set in the rocket
for aero_surface in flight.rocket.fins:
if isinstance(aero_surface, TrapezoidalFins):
# s: surface area; ar: aspect ratio; la: lambda
root_chord = aero_surface.root_chord
s = (aero_surface.tip_chord + root_chord) * aero_surface.span / 2
ar = aero_surface.span * aero_surface.span / s
la = aero_surface.tip_chord / root_chord
if not found_fin:
found_fin = True
else:
warnings.warn("More than one fin set found. The last one will be used.")
if not found_fin:
raise AttributeError(
"There is no TrapezoidalFins in the rocket, can't run Flutter Analysis."
)

# This ensures that a fin set was found in the rocket, if not, break
try:
s = s
except NameError:
print("There is no fin set in the rocket, can't run a Flutter Analysis.")
return None

# Calculate the Fin Flutter Mach Number
flutter_mach = (
(shear_modulus * 2 * (ar + 2) * (fin_thickness / root_chord) ** 3)
/ (1.337 * (ar**3) * (la + 1) * flight.pressure)
) ** 0.5

# Calculate variables
flutter_mach = _flutter_mach_number(
fin_thickness, shear_modulus, flight, root_chord, ar, la
)
safety_factor = _flutter_safety_factor(flight, flutter_mach)

# Prints everything
# Prints and plots
if see_prints:
_flutter_prints(
fin_thickness,
shear_modulus,
s,
ar,
la,
flutter_mach,
safety_factor,
flight,
fin_thickness, shear_modulus, s, ar, la, flutter_mach, safety_factor, flight
)

# Plots everything
if see_graphs:
_flutter_plots(flight, flutter_mach, safety_factor)
return None
else:
return flutter_mach, safety_factor


def _flutter_mach_number(fin_thickness, shear_modulus, flight, root_chord, ar, la):
flutter_mach = (
(shear_modulus * 2 * (ar + 2) * (fin_thickness / root_chord) ** 3)
/ (1.337 * (ar**3) * (la + 1) * flight.pressure)
) ** 0.5
flutter_mach.set_title("Fin Flutter Mach Number")
flutter_mach.set_outputs("Mach")
return flutter_mach


def _flutter_safety_factor(flight, flutter_mach):
"""Calculates the safety factor for the fin flutter analysis.
Expand All @@ -291,25 +291,9 @@ def _flutter_safety_factor(flight, flutter_mach):
rocketpy.Function
The safety factor for the fin flutter analysis.
"""
safety_factor = [[t, 0] for t in flutter_mach[:, 0]]
for i in range(len(flutter_mach)):
try:
safety_factor[i][1] = flutter_mach[i][1] / flight.mach_number[i][1]
except ZeroDivisionError:
safety_factor[i][1] = np.nan

# Function needs to remove NaN and Inf values from the source
safety_factor = np.array(safety_factor)
safety_factor = safety_factor[~np.isnan(safety_factor).any(axis=1)]
safety_factor = safety_factor[~np.isinf(safety_factor).any(axis=1)]

safety_factor = Function(
source=safety_factor,
inputs="Time (s)",
outputs="Fin Flutter Safety Factor",
interpolation="linear",
)

safety_factor = flutter_mach / flight.mach_number
safety_factor.set_title("Fin Flutter Safety Factor")
safety_factor.set_outputs("Safety Factor")
return safety_factor


Expand All @@ -331,7 +315,8 @@ def _flutter_plots(flight, flutter_mach, safety_factor):
-------
None
"""
fig = plt.figure(figsize=(6, 6))
# TODO: move to rocketpy.plots submodule
_ = plt.figure(figsize=(6, 6))
ax1 = plt.subplot(211)
ax1.plot(
flutter_mach[:, 0],
Expand Down Expand Up @@ -362,8 +347,6 @@ def _flutter_plots(flight, flutter_mach, safety_factor):
plt.subplots_adjust(hspace=0.5)
plt.show()

return None


def _flutter_prints(
fin_thickness,
Expand Down Expand Up @@ -404,6 +387,7 @@ def _flutter_prints(
-------
None
"""
# TODO: move to rocketpy.prints submodule
time_index = np.argmin(flutter_mach[:, 1])
time_min_mach = flutter_mach[time_index, 0]
min_mach = flutter_mach[time_index, 1]
Expand All @@ -427,8 +411,6 @@ def _flutter_prints(
print(f"Minimum Safety Factor: {min_sf:.3f} at {time_min_sf:.2f} s")
print(f"Altitude of minimum Safety Factor: {altitude_min_sf:.3f} m (AGL)\n")

return None


def create_dispersion_dictionary(filename):
"""Creates a dictionary with the rocket data provided by a .csv file.
Expand Down
Loading

0 comments on commit 086a924

Please sign in to comment.