Skip to content

Commit 95792f7

Browse files
committed
Merge branch 'enh/new-stability-margin' of https://github.com/RocketPy-Team/RocketPy into enh/new-stability-margin
2 parents 312f137 + 0036e17 commit 95792f7

File tree

7 files changed

+145
-39
lines changed

7 files changed

+145
-39
lines changed

rocketpy/mathutils/function.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,8 @@ def plot2D(
11521152
samples=[30, 30],
11531153
force_data=True,
11541154
disp_type="surface",
1155+
alpha=0.6,
1156+
cmap="viridis",
11551157
):
11561158
"""Plot 2-Dimensional Function, from a lower limit to an upper limit,
11571159
by sampling the Function several times in the interval. The title of
@@ -1185,6 +1187,12 @@ def plot2D(
11851187
disp_type : string, optional
11861188
Display type of plotted graph, which can be surface, wireframe,
11871189
contour, or contourf. Default value is surface.
1190+
alpha : float, optional
1191+
Transparency of plotted graph, which can be a value between 0 and
1192+
1. Default value is 0.6.
1193+
cmap : string, optional
1194+
Colormap of plotted graph, which can be any of the colormaps
1195+
available in matplotlib. Default value is viridis.
11881196
11891197
Returns
11901198
-------
@@ -1221,6 +1229,9 @@ def plot2D(
12211229
mesh = [[mesh_x_flat[i], mesh_y_flat[i]] for i in range(len(mesh_x_flat))]
12221230
# Evaluate function at all mesh nodes and convert it to matrix
12231231
z = np.array(self.get_value(mesh)).reshape(mesh_x.shape)
1232+
z_min, z_max = z.min(), z.max()
1233+
color_map = plt.cm.get_cmap(cmap)
1234+
norm = plt.Normalize(z_min, z_max)
12241235
# Plot function
12251236
if disp_type == "surface":
12261237
surf = axes.plot_surface(
@@ -1229,9 +1240,11 @@ def plot2D(
12291240
z,
12301241
rstride=1,
12311242
cstride=1,
1232-
# cmap=cm.coolwarm,
1243+
cmap=color_map,
12331244
linewidth=0,
1234-
alpha=0.6,
1245+
alpha=alpha,
1246+
vmin=z_min,
1247+
vmax=z_max,
12351248
)
12361249
figure.colorbar(surf)
12371250
elif disp_type == "wireframe":

rocketpy/plots/flight_plots.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -682,19 +682,11 @@ def fluid_mechanics_data(self):
682682

683683
ax4 = plt.subplot(414)
684684
ax4.plot(self.flight.angle_of_attack[:, 0], self.flight.angle_of_attack[:, 1])
685-
# Make sure bottom and top limits are different
686-
if (
687-
self.flight.out_of_rail_time
688-
* self.flight.angle_of_attack(self.flight.out_of_rail_time)
689-
!= 0
690-
):
691-
ax4.set_xlim(
692-
self.flight.out_of_rail_time, 10 * self.flight.out_of_rail_time + 1
693-
)
694-
ax4.set_ylim(0, self.flight.angle_of_attack(self.flight.out_of_rail_time))
695685
ax4.set_title("Angle of Attack")
696686
ax4.set_xlabel("Time (s)")
697687
ax4.set_ylabel("Angle of Attack (°)")
688+
ax4.set_xlim(self.flight.out_of_rail_time, self.first_event_time)
689+
ax4.set_ylim(0, self.flight.angle_of_attack(self.flight.out_of_rail_time) + 15)
698690
ax4.grid()
699691

700692
plt.subplots_adjust(hspace=0.5)
@@ -718,7 +710,28 @@ def stability_and_control_data(self):
718710
ax1.set_xlim(0, self.flight.stability_margin[:, 0][-1])
719711
ax1.set_title("Stability Margin")
720712
ax1.set_xlabel("Time (s)")
721-
ax1.set_ylabel("Static Margin (c)")
713+
ax1.set_ylabel("Stability Margin (c)")
714+
ax1.set_xlim(0, self.first_event_time)
715+
ax1.axvline(
716+
x=self.flight.out_of_rail_time,
717+
color="r",
718+
linestyle="--",
719+
label="Out of Rail Time",
720+
)
721+
ax1.axvline(
722+
x=self.flight.rocket.motor.burn_out_time,
723+
color="g",
724+
linestyle="--",
725+
label="Burn Out Time",
726+
)
727+
728+
ax1.axvline(
729+
x=self.flight.apogee_time,
730+
color="m",
731+
linestyle="--",
732+
label="Apogee Time",
733+
)
734+
ax1.legend()
722735
ax1.grid()
723736

724737
ax2 = plt.subplot(212)

rocketpy/plots/rocket_plots.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ def stability_margin(self):
7373
None
7474
"""
7575

76-
self.rocket.stability_margin()
76+
self.rocket.stability_margin.plot2D(
77+
lower=0,
78+
upper=[2, self.rocket.motor.burn_out_time], # Mach 2 and burnout
79+
samples=[20, 20],
80+
disp_type="surface",
81+
alpha=1,
82+
)
7783

7884
return None
7985

@@ -125,15 +131,31 @@ def all(self):
125131
None
126132
"""
127133

128-
# Show plots
134+
# Mass Plots
129135
print("\nMass Plots")
136+
print("-" * 40)
130137
self.total_mass()
131138
self.reduced_mass()
139+
140+
# Aerodynamics Plots
132141
print("\nAerodynamics Plots")
133-
self.static_margin()
134-
self.stability_margin()
142+
print("-" * 40)
143+
144+
# Drag Plots
145+
print("Drag Plots")
146+
print("-" * 20) # Separator for Drag Plots
135147
self.power_on_drag()
136148
self.power_off_drag()
149+
150+
# Stability Plots
151+
print("\nStability Plots")
152+
print("-" * 20) # Separator for Stability Plots
153+
self.static_margin()
154+
self.stability_margin()
155+
156+
# Thrust-to-Weight Plot
157+
print("\nThrust-to-Weight Plot")
158+
print("-" * 40)
137159
self.thrust_to_weight()
138160

139161
return None

rocketpy/prints/flight_prints.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ def out_of_rail_conditions(self):
155155
)
156156
)
157157
print(
158-
"Rail Departure Static Margin: {:.3f} c".format(
159-
self.flight.static_margin(self.flight.out_of_rail_time)
158+
"Rail Departure Stability Margin: {:.3f} c".format(
159+
self.flight.stability_margin(self.flight.out_of_rail_time)
160160
)
161161
)
162162
print(
@@ -301,12 +301,19 @@ def impact_conditions(self):
301301
print("\nImpact Conditions\n")
302302
print("X Impact: {:.3f} m".format(self.flight.x_impact))
303303
print("Y Impact: {:.3f} m".format(self.flight.y_impact))
304+
print("Latitude: {:.7f}°".format(self.flight.latitude(self.flight.t_final)))
305+
print(
306+
"Longitude: {:.7f}°".format(self.flight.longitude(self.flight.t_final))
307+
)
304308
print("Time of Impact: {:.3f} s".format(self.flight.t_final))
305309
print("Velocity at Impact: {:.3f} m/s".format(self.flight.impact_velocity))
306310
elif self.flight.terminate_on_apogee is False:
307311
print("End of Simulation")
308-
print("Time: {:.3f} s".format(self.flight.solution[-1][0]))
312+
t_final = self.flight.solution[-1][0]
313+
print("Time: {:.3f} s".format(t_final))
309314
print("Altitude: {:.3f} m".format(self.flight.solution[-1][3]))
315+
print("Latitude: {:.3f}°".format(self.flight.latitude(t_final)))
316+
print("Longitude: {:.3f}°".format(self.flight.longitude(t_final)))
310317

311318
return None
312319

@@ -362,6 +369,11 @@ def maximum_values(self):
362369
self.flight.max_acceleration_power_off_time,
363370
)
364371
)
372+
print(
373+
"Maximum Stability Margin: {:.3f} c at {:.2f} s".format(
374+
self.flight.max_stability_margin, self.flight.max_stability_margin_time
375+
)
376+
)
365377

366378
if (
367379
len(self.flight.rocket.rail_buttons) == 0
@@ -391,6 +403,22 @@ def maximum_values(self):
391403
)
392404
return None
393405

406+
def stability_margin(self):
407+
"""Prints out the maximum and minimum stability margin available
408+
about the flight."""
409+
print("\nStability Margin\n")
410+
print(
411+
"Maximum Stability Margin: {:.3f} c at {:.2f} s".format(
412+
self.flight.max_stability_margin, self.flight.max_stability_margin_time
413+
)
414+
)
415+
print(
416+
"Minimum Stability Margin: {:.3f} c at {:.2f} s".format(
417+
self.flight.min_stability_margin, self.flight.min_stability_margin_time
418+
)
419+
)
420+
return None
421+
394422
def all(self):
395423
"""Prints out all data available about the Flight.
396424
@@ -431,6 +459,10 @@ def all(self):
431459
self.impact_conditions()
432460
print()
433461

462+
# Print stability margin
463+
self.stability_margin()
464+
print()
465+
434466
# Print maximum values
435467
self.maximum_values()
436468
print()

rocketpy/prints/rocket_prints.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def inertia_details(self):
6363
)
6464
)
6565
print(
66-
"Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format(
66+
"Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2\n".format(
6767
self.rocket.dry_I_23
6868
)
6969
)
@@ -82,7 +82,7 @@ def rocket_geometrical_parameters(self):
8282
print("Rocket Frontal Area: " + "{:.6f}".format(self.rocket.area) + " m2")
8383
print("\nRocket Distances")
8484
print(
85-
"Rocket Center of Dry Mass - Center of Mass withour Motor: "
85+
"Rocket Center of Dry Mass - Center of Mass without Motor: "
8686
+ "{:.3f} m".format(
8787
abs(
8888
self.rocket.center_of_mass_without_motor
@@ -91,7 +91,7 @@ def rocket_geometrical_parameters(self):
9191
)
9292
)
9393
print(
94-
"Rocket Center of Dry Mass - Nozzle Exit Distance: "
94+
"Rocket Center of Dry Mass - Nozzle Exit: "
9595
+ "{:.3f} m".format(
9696
abs(
9797
self.rocket.center_of_dry_mass_position - self.rocket.motor_position
@@ -109,7 +109,7 @@ def rocket_geometrical_parameters(self):
109109
)
110110
print(
111111
"Rocket Center of Mass - Rocket Loaded Center of Mass: "
112-
+ "{:.3f} m".format(
112+
+ "{:.3f} m\n".format(
113113
abs(
114114
self.rocket.center_of_mass(0)
115115
- self.rocket.center_of_dry_mass_position
@@ -135,36 +135,40 @@ def rocket_aerodynamics_quantities(self):
135135
+ "/rad"
136136
)
137137

138-
print("\nAerodynamics Center of Pressure\n")
138+
print("\nCenter of Pressure\n")
139139
for surface, position in self.rocket.aerodynamic_surfaces:
140140
name = surface.name
141-
cpz = surface.cp[2]
141+
cpz = surface.cp[2] # relative to the user defined coordinate system
142142
print(
143143
name
144-
+ " Center of Pressure to CM: {:.3f}".format(
144+
+ " Center of Pressure position: {:.3f}".format(
145145
position - self.rocket._csys * cpz
146146
)
147147
+ " m"
148148
)
149+
print("\nStability\n")
149150
print(
150-
"Distance - Center of Pressure to Center of Dry Mass: "
151-
+ "{:.3f}".format(
152-
self.rocket.center_of_mass(0) - self.rocket.cp_position(0)
153-
)
154-
+ " m"
151+
f"Center of Mass position (time=0): {self.rocket.center_of_mass(0):.3f} m"
155152
)
156153
print(
157-
"Initial Static Margin: "
154+
"Initial Static Margin (mach=0, time=0): "
158155
+ "{:.3f}".format(self.rocket.static_margin(0))
159156
+ " c"
160157
)
161158
print(
162-
"Final Static Margin: "
159+
"Final Static Margin (mach=0, time=burn_out): "
163160
+ "{:.3f}".format(
164161
self.rocket.static_margin(self.rocket.motor.burn_out_time)
165162
)
166163
+ " c"
167164
)
165+
print(
166+
"Rocket Center of Mass (time=0) - Center of Pressure (mach=0): "
167+
+ "{:.3f}".format(
168+
abs(self.rocket.center_of_mass(0) - self.rocket.cp_position(0))
169+
)
170+
+ " m\n"
171+
)
168172

169173
return None
170174

@@ -188,18 +192,14 @@ def all(self):
188192
"""
189193
# Print inertia details
190194
self.inertia_details()
191-
print()
192195

193196
# Print rocket geometrical parameters
194197
self.rocket_geometrical_parameters()
195-
print()
196198

197199
# Print rocket aerodynamics quantities
198200
self.rocket_aerodynamics_quantities()
199-
print()
200201

201202
# Print parachute data
202203
self.parachute_data()
203-
print()
204204

205205
return None

rocketpy/rocket/rocket.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def evaluate_total_mass(self):
327327
# Calculate total mass by summing up propellant and dry mass
328328
self.total_mass = self.mass + self.motor.total_mass
329329
self.total_mass.set_outputs("Total Mass (Rocket + Propellant) (kg)")
330+
self.total_mass.set_title("Total Mass (Rocket + Propellant) (kg) x Time (s)")
330331

331332
# Return total mass
332333
return self.total_mass
@@ -427,6 +428,7 @@ def evaluate_reduced_mass(self):
427428
# calculate reduced mass
428429
self.reduced_mass = motor_mass * mass / (motor_mass + mass)
429430
self.reduced_mass.set_outputs("Reduced Mass (kg)")
431+
self.reduced_mass.set_title("Reduced Mass (kg) x Time (s)")
430432

431433
# Return reduced mass
432434
return self.reduced_mass
@@ -443,6 +445,7 @@ def evaluate_thrust_to_weight(self):
443445
self.thrust_to_weight = self.motor.thrust / (9.80665 * self.total_mass)
444446
self.thrust_to_weight.set_inputs("Time (s)")
445447
self.thrust_to_weight.set_outputs("Thrust/Weight")
448+
self.thrust_to_weight.set_title(None)
446449

447450
def evaluate_center_of_pressure(self):
448451
"""Evaluates rocket center of pressure position relative to user defined

rocketpy/simulation/flight.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ class Flight:
417417
of frequency in Hz. Can be called or accessed as array.
418418
Flight.static_margin : Function
419419
Rocket's static margin during flight in calibers.
420-
Flight.stabilityMargin : rocketpy.Function
420+
Flight.stability_margin : rocketpy.Function
421421
Rocket's stability margin during flight, in calibers.
422422
Flight.stream_velocity_x : Function
423423
Freestream velocity x (East) component, in m/s, as a function of
@@ -2334,6 +2334,29 @@ def max_mach_number(self):
23342334
"""Maximum Mach number."""
23352335
return self.mach_number(self.max_mach_number_time)
23362336

2337+
# Stability Margin
2338+
@cached_property
2339+
def max_stability_margin_time(self):
2340+
"""Time of maximum stability margin."""
2341+
max_stability_margin_time_index = np.argmax(self.stability_margin[:, 1])
2342+
return self.stability_margin[max_stability_margin_time_index, 0]
2343+
2344+
@cached_property
2345+
def max_stability_margin(self):
2346+
"""Maximum stability margin."""
2347+
return self.stability_margin(self.max_stability_margin_time)
2348+
2349+
@cached_property
2350+
def min_stability_margin_time(self):
2351+
"""Time of minimum stability margin."""
2352+
min_stability_margin_time_index = np.argmin(self.stability_margin[:, 1])
2353+
return self.stability_margin[min_stability_margin_time_index, 0]
2354+
2355+
@cached_property
2356+
def min_stability_margin(self):
2357+
"""Minimum stability margin."""
2358+
return self.stability_margin(self.min_stability_margin_time)
2359+
23372360
# Reynolds Number
23382361
@funcify_method("Time (s)", "Reynolds Number", "spline", "zero")
23392362
def reynolds_number(self):

0 commit comments

Comments
 (0)