From 33d78673569761dd2d7160773d65cd83a07a4a44 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 11:34:43 -0300 Subject: [PATCH 01/15] enh: Flight plots moved to plots/flight_plots.py --- rocketpy/Flight.py | 774 +-------------------------- rocketpy/plots/__init__.py | 2 +- rocketpy/plots/flight_plots.py | 935 ++++++++++++++++++++++++++++++++- 3 files changed, 944 insertions(+), 767 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 49a473e33..f5d99118d 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -15,6 +15,7 @@ from scipy import integrate from .Function import Function +from .plots.flight_plots import _FlightPlots class Flight: @@ -603,6 +604,7 @@ def __init__( self.initialSolution = initialSolution self.timeOvershoot = timeOvershoot self.terminateOnApogee = terminateOnApogee + self.plots = _FlightPlots(self) # Modifying Rail Length for a better out of rail condition upperRButton = max(self.rocket.railButtons[0]) @@ -2523,649 +2525,6 @@ def calculateStallWindVelocity(self, stallAngle): return None - def plot3dTrajectory(self): - """Plot a 3D graph of the trajectory - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get max and min x and y - maxZ = max(self.z[:, 1] - self.env.elevation) - maxX = max(self.x[:, 1]) - minX = min(self.x[:, 1]) - maxY = max(self.y[:, 1]) - minY = min(self.y[:, 1]) - maxXY = max(maxX, maxY) - minXY = min(minX, minY) - - # Create figure - fig1 = plt.figure(figsize=(9, 9)) - ax1 = plt.subplot(111, projection="3d") - ax1.plot(self.x[:, 1], self.y[:, 1], zs=0, zdir="z", linestyle="--") - ax1.plot( - self.x[:, 1], - self.z[:, 1] - self.env.elevation, - zs=minXY, - zdir="y", - linestyle="--", - ) - ax1.plot( - self.y[:, 1], - self.z[:, 1] - self.env.elevation, - zs=minXY, - zdir="x", - linestyle="--", - ) - ax1.plot( - self.x[:, 1], self.y[:, 1], self.z[:, 1] - self.env.elevation, linewidth="2" - ) - ax1.scatter(0, 0, 0) - ax1.set_xlabel("X - East (m)") - ax1.set_ylabel("Y - North (m)") - ax1.set_zlabel("Z - Altitude Above Ground Level (m)") - ax1.set_title("Flight Trajectory") - ax1.set_zlim3d([0, maxZ]) - ax1.set_ylim3d([minXY, maxXY]) - ax1.set_xlim3d([minXY, maxXY]) - ax1.view_init(15, 45) - plt.show() - - return None - - def plotLinearKinematicsData(self): - """Prints out all Kinematics graphs available about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Velocity and acceleration plots - fig2 = plt.figure(figsize=(9, 12)) - - ax1 = plt.subplot(414) - ax1.plot(self.vx[:, 0], self.vx[:, 1], color="#ff7f0e") - ax1.set_xlim(0, self.tFinal) - ax1.set_title("Velocity X | Acceleration X") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Velocity X (m/s)", color="#ff7f0e") - ax1.tick_params("y", colors="#ff7f0e") - ax1.grid(True) - - ax1up = ax1.twinx() - ax1up.plot(self.ax[:, 0], self.ax[:, 1], color="#1f77b4") - ax1up.set_ylabel("Acceleration X (m/s²)", color="#1f77b4") - ax1up.tick_params("y", colors="#1f77b4") - - ax2 = plt.subplot(413) - ax2.plot(self.vy[:, 0], self.vy[:, 1], color="#ff7f0e") - ax2.set_xlim(0, self.tFinal) - ax2.set_title("Velocity Y | Acceleration Y") - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Velocity Y (m/s)", color="#ff7f0e") - ax2.tick_params("y", colors="#ff7f0e") - ax2.grid(True) - - ax2up = ax2.twinx() - ax2up.plot(self.ay[:, 0], self.ay[:, 1], color="#1f77b4") - ax2up.set_ylabel("Acceleration Y (m/s²)", color="#1f77b4") - ax2up.tick_params("y", colors="#1f77b4") - - ax3 = plt.subplot(412) - ax3.plot(self.vz[:, 0], self.vz[:, 1], color="#ff7f0e") - ax3.set_xlim(0, self.tFinal) - ax3.set_title("Velocity Z | Acceleration Z") - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("Velocity Z (m/s)", color="#ff7f0e") - ax3.tick_params("y", colors="#ff7f0e") - ax3.grid(True) - - ax3up = ax3.twinx() - ax3up.plot(self.az[:, 0], self.az[:, 1], color="#1f77b4") - ax3up.set_ylabel("Acceleration Z (m/s²)", color="#1f77b4") - ax3up.tick_params("y", colors="#1f77b4") - - ax4 = plt.subplot(411) - ax4.plot(self.speed[:, 0], self.speed[:, 1], color="#ff7f0e") - ax4.set_xlim(0, self.tFinal) - ax4.set_title("Velocity Magnitude | Acceleration Magnitude") - ax4.set_xlabel("Time (s)") - ax4.set_ylabel("Velocity (m/s)", color="#ff7f0e") - ax4.tick_params("y", colors="#ff7f0e") - ax4.grid(True) - - ax4up = ax4.twinx() - ax4up.plot(self.acceleration[:, 0], self.acceleration[:, 1], color="#1f77b4") - ax4up.set_ylabel("Acceleration (m/s²)", color="#1f77b4") - ax4up.tick_params("y", colors="#1f77b4") - - plt.subplots_adjust(hspace=0.5) - plt.show() - return None - - def plotAttitudeData(self): - """Prints out all Angular position graphs available about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of time before parachute event - if len(self.parachuteEvents) > 0: - eventTime = self.parachuteEvents[0][0] + self.parachuteEvents[0][1].lag - eventTimeIndex = np.nonzero(self.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.tFinal - eventTimeIndex = -1 - - # Angular position plots - fig3 = plt.figure(figsize=(9, 12)) - - ax1 = plt.subplot(411) - ax1.plot(self.e0[:, 0], self.e0[:, 1], label="$e_0$") - ax1.plot(self.e1[:, 0], self.e1[:, 1], label="$e_1$") - ax1.plot(self.e2[:, 0], self.e2[:, 1], label="$e_2$") - ax1.plot(self.e3[:, 0], self.e3[:, 1], label="$e_3$") - ax1.set_xlim(0, eventTime) - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Euler Parameters") - ax1.set_title("Euler Parameters") - ax1.legend() - ax1.grid(True) - - ax2 = plt.subplot(412) - ax2.plot(self.psi[:, 0], self.psi[:, 1]) - ax2.set_xlim(0, eventTime) - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("ψ (°)") - ax2.set_title("Euler Precession Angle") - ax2.grid(True) - - ax3 = plt.subplot(413) - ax3.plot(self.theta[:, 0], self.theta[:, 1], label="θ - Nutation") - ax3.set_xlim(0, eventTime) - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("θ (°)") - ax3.set_title("Euler Nutation Angle") - ax3.grid(True) - - ax4 = plt.subplot(414) - ax4.plot(self.phi[:, 0], self.phi[:, 1], label="φ - Spin") - ax4.set_xlim(0, eventTime) - ax4.set_xlabel("Time (s)") - ax4.set_ylabel("φ (°)") - ax4.set_title("Euler Spin Angle") - ax4.grid(True) - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - - def plotFlightPathAngleData(self): - """Prints out Flight path and Rocket Attitude angle graphs available - about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of time before parachute event - if len(self.parachuteEvents) > 0: - eventTime = self.parachuteEvents[0][0] + self.parachuteEvents[0][1].lag - eventTimeIndex = np.nonzero(self.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.tFinal - eventTimeIndex = -1 - - # Path, Attitude and Lateral Attitude Angle - # Angular position plots - fig5 = plt.figure(figsize=(9, 6)) - - ax1 = plt.subplot(211) - ax1.plot(self.pathAngle[:, 0], self.pathAngle[:, 1], label="Flight Path Angle") - ax1.plot( - self.attitudeAngle[:, 0], - self.attitudeAngle[:, 1], - label="Rocket Attitude Angle", - ) - ax1.set_xlim(0, eventTime) - ax1.legend() - ax1.grid(True) - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Angle (°)") - ax1.set_title("Flight Path and Attitude Angle") - - ax2 = plt.subplot(212) - ax2.plot(self.lateralAttitudeAngle[:, 0], self.lateralAttitudeAngle[:, 1]) - ax2.set_xlim(0, eventTime) - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Lateral Attitude Angle (°)") - ax2.set_title("Lateral Attitude Angle") - ax2.grid(True) - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - - def plotAngularKinematicsData(self): - """Prints out all Angular velocity and acceleration graphs available - about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of time before parachute event - if len(self.parachuteEvents) > 0: - eventTime = self.parachuteEvents[0][0] + self.parachuteEvents[0][1].lag - eventTimeIndex = np.nonzero(self.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.tFinal - eventTimeIndex = -1 - - # Angular velocity and acceleration plots - fig4 = plt.figure(figsize=(9, 9)) - ax1 = plt.subplot(311) - ax1.plot(self.w1[:, 0], self.w1[:, 1], color="#ff7f0e") - ax1.set_xlim(0, eventTime) - ax1.set_xlabel("Time (s)") - ax1.set_ylabel(r"Angular Velocity - ${\omega_1}$ (rad/s)", color="#ff7f0e") - ax1.set_title( - r"Angular Velocity ${\omega_1}$ | Angular Acceleration ${\alpha_1}$" - ) - ax1.tick_params("y", colors="#ff7f0e") - ax1.grid(True) - - ax1up = ax1.twinx() - ax1up.plot(self.alpha1[:, 0], self.alpha1[:, 1], color="#1f77b4") - ax1up.set_ylabel( - r"Angular Acceleration - ${\alpha_1}$ (rad/s²)", color="#1f77b4" - ) - ax1up.tick_params("y", colors="#1f77b4") - - ax2 = plt.subplot(312) - ax2.plot(self.w2[:, 0], self.w2[:, 1], color="#ff7f0e") - ax2.set_xlim(0, eventTime) - ax2.set_xlabel("Time (s)") - ax2.set_ylabel(r"Angular Velocity - ${\omega_2}$ (rad/s)", color="#ff7f0e") - ax2.set_title( - r"Angular Velocity ${\omega_2}$ | Angular Acceleration ${\alpha_2}$" - ) - ax2.tick_params("y", colors="#ff7f0e") - ax2.grid(True) - - ax2up = ax2.twinx() - ax2up.plot(self.alpha2[:, 0], self.alpha2[:, 1], color="#1f77b4") - ax2up.set_ylabel( - r"Angular Acceleration - ${\alpha_2}$ (rad/s²)", color="#1f77b4" - ) - ax2up.tick_params("y", colors="#1f77b4") - - ax3 = plt.subplot(313) - ax3.plot(self.w3[:, 0], self.w3[:, 1], color="#ff7f0e") - ax3.set_xlim(0, eventTime) - ax3.set_xlabel("Time (s)") - ax3.set_ylabel(r"Angular Velocity - ${\omega_3}$ (rad/s)", color="#ff7f0e") - ax3.set_title( - r"Angular Velocity ${\omega_3}$ | Angular Acceleration ${\alpha_3}$" - ) - ax3.tick_params("y", colors="#ff7f0e") - ax3.grid(True) - - ax3up = ax3.twinx() - ax3up.plot(self.alpha3[:, 0], self.alpha3[:, 1], color="#1f77b4") - ax3up.set_ylabel( - r"Angular Acceleration - ${\alpha_3}$ (rad/s²)", color="#1f77b4" - ) - ax3up.tick_params("y", colors="#1f77b4") - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - - def plotTrajectoryForceData(self): - """Prints out all Forces and Moments graphs available about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero(self.x[:, 0] == self.outOfRailTime) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) - - # Get index of time before parachute event - if len(self.parachuteEvents) > 0: - eventTime = self.parachuteEvents[0][0] + self.parachuteEvents[0][1].lag - eventTimeIndex = np.nonzero(self.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.tFinal - eventTimeIndex = -1 - - # Rail Button Forces - fig6 = plt.figure(figsize=(9, 6)) - - ax1 = plt.subplot(211) - ax1.plot( - self.railButton1NormalForce[:outOfRailTimeIndex, 0], - self.railButton1NormalForce[:outOfRailTimeIndex, 1], - label="Upper Rail Button", - ) - ax1.plot( - self.railButton2NormalForce[:outOfRailTimeIndex, 0], - self.railButton2NormalForce[:outOfRailTimeIndex, 1], - label="Lower Rail Button", - ) - ax1.set_xlim(0, self.outOfRailTime if self.outOfRailTime > 0 else self.tFinal) - ax1.legend() - ax1.grid(True) - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Normal Force (N)") - ax1.set_title("Rail Buttons Normal Force") - - ax2 = plt.subplot(212) - ax2.plot( - self.railButton1ShearForce[:outOfRailTimeIndex, 0], - self.railButton1ShearForce[:outOfRailTimeIndex, 1], - label="Upper Rail Button", - ) - ax2.plot( - self.railButton2ShearForce[:outOfRailTimeIndex, 0], - self.railButton2ShearForce[:outOfRailTimeIndex, 1], - label="Lower Rail Button", - ) - ax2.set_xlim(0, self.outOfRailTime if self.outOfRailTime > 0 else self.tFinal) - ax2.legend() - ax2.grid(True) - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Shear Force (N)") - ax2.set_title("Rail Buttons Shear Force") - - plt.subplots_adjust(hspace=0.5) - plt.show() - - # Aerodynamic force and moment plots - fig7 = plt.figure(figsize=(9, 12)) - - ax1 = plt.subplot(411) - ax1.plot( - self.aerodynamicLift[:eventTimeIndex, 0], - self.aerodynamicLift[:eventTimeIndex, 1], - label="Resultant", - ) - ax1.plot(self.R1[:eventTimeIndex, 0], self.R1[:eventTimeIndex, 1], label="R1") - ax1.plot(self.R2[:eventTimeIndex, 0], self.R2[:eventTimeIndex, 1], label="R2") - ax1.set_xlim(0, eventTime) - ax1.legend() - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Lift Force (N)") - ax1.set_title("Aerodynamic Lift Resultant Force") - ax1.grid() - - ax2 = plt.subplot(412) - ax2.plot( - self.aerodynamicDrag[:eventTimeIndex, 0], - self.aerodynamicDrag[:eventTimeIndex, 1], - ) - ax2.set_xlim(0, eventTime) - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Drag Force (N)") - ax2.set_title("Aerodynamic Drag Force") - ax2.grid() - - ax3 = plt.subplot(413) - ax3.plot( - self.aerodynamicBendingMoment[:eventTimeIndex, 0], - self.aerodynamicBendingMoment[:eventTimeIndex, 1], - label="Resultant", - ) - ax3.plot(self.M1[:eventTimeIndex, 0], self.M1[:eventTimeIndex, 1], label="M1") - ax3.plot(self.M2[:eventTimeIndex, 0], self.M2[:eventTimeIndex, 1], label="M2") - ax3.set_xlim(0, eventTime) - ax3.legend() - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("Bending Moment (N m)") - ax3.set_title("Aerodynamic Bending Resultant Moment") - ax3.grid() - - ax4 = plt.subplot(414) - ax4.plot( - self.aerodynamicSpinMoment[:eventTimeIndex, 0], - self.aerodynamicSpinMoment[:eventTimeIndex, 1], - ) - ax4.set_xlim(0, eventTime) - ax4.set_xlabel("Time (s)") - ax4.set_ylabel("Spin Moment (N m)") - ax4.set_title("Aerodynamic Spin Moment") - ax4.grid() - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - - def plotEnergyData(self): - """Prints out all Energy components graphs available about the Flight - - Returns - ------- - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero(self.x[:, 0] == self.outOfRailTime) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) - - # Get index of time before parachute event - if len(self.parachuteEvents) > 0: - eventTime = self.parachuteEvents[0][0] + self.parachuteEvents[0][1].lag - eventTimeIndex = np.nonzero(self.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.tFinal - eventTimeIndex = -1 - - fig8 = plt.figure(figsize=(9, 9)) - - ax1 = plt.subplot(411) - ax1.plot( - self.kineticEnergy[:, 0], self.kineticEnergy[:, 1], label="Kinetic Energy" - ) - ax1.plot( - self.rotationalEnergy[:, 0], - self.rotationalEnergy[:, 1], - label="Rotational Energy", - ) - ax1.plot( - self.translationalEnergy[:, 0], - self.translationalEnergy[:, 1], - label="Translational Energy", - ) - ax1.set_xlim(0, self.apogeeTime if self.apogeeTime != 0.0 else self.tFinal) - ax1.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax1.set_title("Kinetic Energy Components") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Energy (J)") - - ax1.legend() - ax1.grid() - - ax2 = plt.subplot(412) - ax2.plot(self.totalEnergy[:, 0], self.totalEnergy[:, 1], label="Total Energy") - ax2.plot( - self.kineticEnergy[:, 0], self.kineticEnergy[:, 1], label="Kinetic Energy" - ) - ax2.plot( - self.potentialEnergy[:, 0], - self.potentialEnergy[:, 1], - label="Potential Energy", - ) - ax2.set_xlim(0, self.apogeeTime if self.apogeeTime != 0.0 else self.tFinal) - ax2.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax2.set_title("Total Mechanical Energy Components") - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Energy (J)") - ax2.legend() - ax2.grid() - - ax3 = plt.subplot(413) - ax3.plot(self.thrustPower[:, 0], self.thrustPower[:, 1], label="|Thrust Power|") - ax3.set_xlim(0, self.rocket.motor.burnOutTime) - ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax3.set_title("Thrust Absolute Power") - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("Power (W)") - ax3.legend() - ax3.grid() - - ax4 = plt.subplot(414) - ax4.plot(self.dragPower[:, 0], -self.dragPower[:, 1], label="|Drag Power|") - ax4.set_xlim(0, self.apogeeTime if self.apogeeTime != 0.0 else self.tFinal) - ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax4.set_title("Drag Absolute Power") - ax4.set_xlabel("Time (s)") - ax4.set_ylabel("Power (W)") - ax4.legend() - ax4.grid() - - plt.subplots_adjust(hspace=1) - plt.show() - - return None - - def plotFluidMechanicsData(self): - """Prints out a summary of the Fluid Mechanics graphs available about - the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero(self.x[:, 0] == self.outOfRailTime) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) - - # Trajectory Fluid Mechanics Plots - fig10 = plt.figure(figsize=(9, 12)) - - ax1 = plt.subplot(411) - ax1.plot(self.MachNumber[:, 0], self.MachNumber[:, 1]) - ax1.set_xlim(0, self.tFinal) - ax1.set_title("Mach Number") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Mach Number") - ax1.grid() - - ax2 = plt.subplot(412) - ax2.plot(self.ReynoldsNumber[:, 0], self.ReynoldsNumber[:, 1]) - ax2.set_xlim(0, self.tFinal) - ax2.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax2.set_title("Reynolds Number") - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Reynolds Number") - ax2.grid() - - ax3 = plt.subplot(413) - ax3.plot( - self.dynamicPressure[:, 0], - self.dynamicPressure[:, 1], - label="Dynamic Pressure", - ) - ax3.plot( - self.totalPressure[:, 0], self.totalPressure[:, 1], label="Total Pressure" - ) - ax3.plot(self.pressure[:, 0], self.pressure[:, 1], label="Static Pressure") - ax3.set_xlim(0, self.tFinal) - ax3.legend() - ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) - ax3.set_title("Total and Dynamic Pressure") - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("Pressure (Pa)") - ax3.grid() - - ax4 = plt.subplot(414) - ax4.plot(self.angleOfAttack[:, 0], self.angleOfAttack[:, 1]) - # Make sure bottom and top limits are different - if self.outOfRailTime * self.angleOfAttack(self.outOfRailTime) != 0: - ax4.set_xlim(self.outOfRailTime, 10 * self.outOfRailTime + 1) - ax4.set_ylim(0, self.angleOfAttack(self.outOfRailTime)) - ax4.set_title("Angle of Attack") - ax4.set_xlabel("Time (s)") - ax4.set_ylabel("Angle of Attack (°)") - ax4.grid() - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - def calculateFinFlutterAnalysis(self, finThickness, shearModulus): """Calculate, create and plot the Fin Flutter velocity, based on the pressure profile provided by Atmospheric model selected. It considers the @@ -3323,117 +2682,6 @@ def calculateFinFlutterAnalysis(self, finThickness, shearModulus): return None - def plotStabilityAndControlData(self): - """Prints out Rocket Stability and Control parameters graphs available - about the Flight - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - fig9 = plt.figure(figsize=(9, 6)) - - ax1 = plt.subplot(211) - ax1.plot(self.staticMargin[:, 0], self.staticMargin[:, 1]) - ax1.set_xlim(0, self.staticMargin[:, 0][-1]) - ax1.set_title("Static Margin") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Static Margin (c)") - ax1.grid() - - ax2 = plt.subplot(212) - maxAttitude = max(self.attitudeFrequencyResponse[:, 1]) - maxAttitude = maxAttitude if maxAttitude != 0 else 1 - ax2.plot( - self.attitudeFrequencyResponse[:, 0], - self.attitudeFrequencyResponse[:, 1] / maxAttitude, - label="Attitude Angle", - ) - maxOmega1 = max(self.omega1FrequencyResponse[:, 1]) - maxOmega1 = maxOmega1 if maxOmega1 != 0 else 1 - ax2.plot( - self.omega1FrequencyResponse[:, 0], - self.omega1FrequencyResponse[:, 1] / maxOmega1, - label=r"$\omega_1$", - ) - maxOmega2 = max(self.omega2FrequencyResponse[:, 1]) - maxOmega2 = maxOmega2 if maxOmega2 != 0 else 1 - ax2.plot( - self.omega2FrequencyResponse[:, 0], - self.omega2FrequencyResponse[:, 1] / maxOmega2, - label=r"$\omega_2$", - ) - maxOmega3 = max(self.omega3FrequencyResponse[:, 1]) - maxOmega3 = maxOmega3 if maxOmega3 != 0 else 1 - ax2.plot( - self.omega3FrequencyResponse[:, 0], - self.omega3FrequencyResponse[:, 1] / maxOmega3, - label=r"$\omega_3$", - ) - ax2.set_title("Frequency Response") - ax2.set_xlabel("Frequency (Hz)") - ax2.set_ylabel("Amplitude Magnitude Normalized") - ax2.set_xlim(0, 5) - ax2.legend() - ax2.grid() - - plt.subplots_adjust(hspace=0.5) - plt.show() - - return None - - def plotPressureSignals(self): - """Prints out all Parachute Trigger Pressure Signals. - This function can be called also for plot pressure data for flights - without Parachutes, in this case the Pressure Signals will be simply - the pressure provided by the atmosphericModel, at Flight z positions. - This means that no noise will be considered if at least one parachute - has not been added. - - This function aims to help the engineer to visually check if there - are anomalies with the Flight Simulation. - - Parameters - ---------- - None - - Return - ------ - None - """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - if len(self.rocket.parachutes) == 0: - plt.figure() - ax1 = plt.subplot(111) - ax1.plot(self.z[:, 0], self.env.pressure(self.z[:, 1])) - ax1.set_title("Pressure at Rocket's Altitude") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Pressure (Pa)") - ax1.set_xlim(0, self.tFinal) - ax1.grid() - - plt.show() - - else: - for parachute in self.rocket.parachutes: - print("Parachute: ", parachute.name) - parachute.noiseSignalFunction() - parachute.noisyPressureSignalFunction() - parachute.cleanPressureSignalFunction() - - return None - def exportPressures(self, fileName, timeStep): """Exports the pressure experienced by the rocket during the flight to an external file, the '.csv' format is recommended, as the columns will @@ -3692,31 +2940,31 @@ def allInfo(self): self.printNumericalIntegrationSettings() print("\n\nTrajectory 3d Plot\n") - self.plot3dTrajectory() + self.plots.plot3dTrajectory() print("\n\nTrajectory Kinematic Plots\n") - self.plotLinearKinematicsData() + self.plots.plotLinearKinematicsData() print("\n\nAngular Position Plots\n") - self.plotFlightPathAngleData() + self.plots.plotFlightPathAngleData() print("\n\nPath, Attitude and Lateral Attitude Angle plots\n") - self.plotAttitudeData() + self.plots.plotAttitudeData() print("\n\nTrajectory Angular Velocity and Acceleration Plots\n") - self.plotAngularKinematicsData() + self.plots.plotAngularKinematicsData() print("\n\nTrajectory Force Plots\n") - self.plotTrajectoryForceData() + self.plots.plotTrajectoryForceData() print("\n\nTrajectory Energy Plots\n") - self.plotEnergyData() + self.plots.plotEnergyData() print("\n\nTrajectory Fluid Mechanics Plots\n") - self.plotFluidMechanicsData() + self.plots.plotFluidMechanicsData() print("\n\nTrajectory Stability and Control Plots\n") - self.plotStabilityAndControlData() + self.plots.plotStabilityAndControlData() return None diff --git a/rocketpy/plots/__init__.py b/rocketpy/plots/__init__.py index f6f83def9..096bf0d0e 100644 --- a/rocketpy/plots/__init__.py +++ b/rocketpy/plots/__init__.py @@ -1 +1 @@ -from . import plots +from .flight_plots import _FlightPlots diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 6470e2641..3d58ff9b3 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -1,8 +1,937 @@ -__author__ = " " +__author__ = "Guilherme Fernandes Alves, Mateus Stano Junqueira" __copyright__ = "Copyright 20XX, RocketPy Team" __license__ = "MIT" +import matplotlib.pyplot as plt +import numpy as np + + class _FlightPlots: - def __init__(self) -> None: - pass + """class to plot flight data + Here you also can: + - Print important information about the flight + - See animations of the flight + - Compare plots from different flights + - Compare flights from different rocket simulators + """ + + def __init__(self, flight): + """_summary_ + + Parameters + ---------- + flight : Flight + Instance of the Flight class + + Returns + ------- + None + """ + self.flight = flight + return None + + # Start definition of 'basic' plots methods, the traditional RocketPy plots + + def plot3dTrajectory(self): + """Plot a 3D graph of the trajectory + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get max and min x and y + maxZ = max(self.flight.z[:, 1] - self.flight.env.elevation) + maxX = max(self.flight.x[:, 1]) + minX = min(self.flight.x[:, 1]) + maxY = max(self.flight.y[:, 1]) + minY = min(self.flight.y[:, 1]) + maxXY = max(maxX, maxY) + minXY = min(minX, minY) + + # Create figure + fig1 = plt.figure(figsize=(9, 9)) + ax1 = plt.subplot(111, projection="3d") + ax1.plot( + self.flight.x[:, 1], self.flight.y[:, 1], zs=0, zdir="z", linestyle="--" + ) + ax1.plot( + self.flight.x[:, 1], + self.flight.z[:, 1] - self.flight.env.elevation, + zs=minXY, + zdir="y", + linestyle="--", + ) + ax1.plot( + self.flight.y[:, 1], + self.flight.z[:, 1] - self.flight.env.elevation, + zs=minXY, + zdir="x", + linestyle="--", + ) + ax1.plot( + self.flight.x[:, 1], + self.flight.y[:, 1], + self.flight.z[:, 1] - self.flight.env.elevation, + linewidth="2", + ) + ax1.scatter(0, 0, 0) + ax1.set_xlabel("X - East (m)") + ax1.set_ylabel("Y - North (m)") + ax1.set_zlabel("Z - Altitude Above Ground Level (m)") + ax1.set_title("Flight Trajectory") + ax1.set_zlim3d([0, maxZ]) + ax1.set_ylim3d([minXY, maxXY]) + ax1.set_xlim3d([minXY, maxXY]) + ax1.view_init(15, 45) + plt.show() + + return None + + def plotLinearKinematicsData(self): + """Prints out all Kinematics graphs available about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Velocity and acceleration plots + fig2 = plt.figure(figsize=(9, 12)) + + ax1 = plt.subplot(414) + ax1.plot(self.flight.vx[:, 0], self.flight.vx[:, 1], color="#ff7f0e") + ax1.set_xlim(0, self.flight.tFinal) + ax1.set_title("Velocity X | Acceleration X") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Velocity X (m/s)", color="#ff7f0e") + ax1.tick_params("y", colors="#ff7f0e") + ax1.grid(True) + + ax1up = ax1.twinx() + ax1up.plot(self.flight.ax[:, 0], self.flight.ax[:, 1], color="#1f77b4") + ax1up.set_ylabel("Acceleration X (m/s²)", color="#1f77b4") + ax1up.tick_params("y", colors="#1f77b4") + + ax2 = plt.subplot(413) + ax2.plot(self.flight.vy[:, 0], self.flight.vy[:, 1], color="#ff7f0e") + ax2.set_xlim(0, self.flight.tFinal) + ax2.set_title("Velocity Y | Acceleration Y") + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Velocity Y (m/s)", color="#ff7f0e") + ax2.tick_params("y", colors="#ff7f0e") + ax2.grid(True) + + ax2up = ax2.twinx() + ax2up.plot(self.flight.ay[:, 0], self.flight.ay[:, 1], color="#1f77b4") + ax2up.set_ylabel("Acceleration Y (m/s²)", color="#1f77b4") + ax2up.tick_params("y", colors="#1f77b4") + + ax3 = plt.subplot(412) + ax3.plot(self.flight.vz[:, 0], self.flight.vz[:, 1], color="#ff7f0e") + ax3.set_xlim(0, self.flight.tFinal) + ax3.set_title("Velocity Z | Acceleration Z") + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("Velocity Z (m/s)", color="#ff7f0e") + ax3.tick_params("y", colors="#ff7f0e") + ax3.grid(True) + + ax3up = ax3.twinx() + ax3up.plot(self.flight.az[:, 0], self.flight.az[:, 1], color="#1f77b4") + ax3up.set_ylabel("Acceleration Z (m/s²)", color="#1f77b4") + ax3up.tick_params("y", colors="#1f77b4") + + ax4 = plt.subplot(411) + ax4.plot(self.flight.speed[:, 0], self.flight.speed[:, 1], color="#ff7f0e") + ax4.set_xlim(0, self.flight.tFinal) + ax4.set_title("Velocity Magnitude | Acceleration Magnitude") + ax4.set_xlabel("Time (s)") + ax4.set_ylabel("Velocity (m/s)", color="#ff7f0e") + ax4.tick_params("y", colors="#ff7f0e") + ax4.grid(True) + + ax4up = ax4.twinx() + ax4up.plot( + self.flight.acceleration[:, 0], + self.flight.acceleration[:, 1], + color="#1f77b4", + ) + ax4up.set_ylabel("Acceleration (m/s²)", color="#1f77b4") + ax4up.tick_params("y", colors="#1f77b4") + + plt.subplots_adjust(hspace=0.5) + plt.show() + return None + + def plotAttitudeData(self): + """Prints out all Angular position graphs available about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + eventTime = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = self.flight.tFinal + eventTimeIndex = -1 + + # Angular position plots + fig3 = plt.figure(figsize=(9, 12)) + + ax1 = plt.subplot(411) + ax1.plot(self.flight.e0[:, 0], self.flight.e0[:, 1], label="$e_0$") + ax1.plot(self.flight.e1[:, 0], self.flight.e1[:, 1], label="$e_1$") + ax1.plot(self.flight.e2[:, 0], self.flight.e2[:, 1], label="$e_2$") + ax1.plot(self.flight.e3[:, 0], self.flight.e3[:, 1], label="$e_3$") + ax1.set_xlim(0, eventTime) + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Euler Parameters") + ax1.set_title("Euler Parameters") + ax1.legend() + ax1.grid(True) + + ax2 = plt.subplot(412) + ax2.plot(self.flight.psi[:, 0], self.flight.psi[:, 1]) + ax2.set_xlim(0, eventTime) + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("ψ (°)") + ax2.set_title("Euler Precession Angle") + ax2.grid(True) + + ax3 = plt.subplot(413) + ax3.plot(self.flight.theta[:, 0], self.flight.theta[:, 1], label="θ - Nutation") + ax3.set_xlim(0, eventTime) + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("θ (°)") + ax3.set_title("Euler Nutation Angle") + ax3.grid(True) + + ax4 = plt.subplot(414) + ax4.plot(self.flight.phi[:, 0], self.flight.phi[:, 1], label="φ - Spin") + ax4.set_xlim(0, eventTime) + ax4.set_xlabel("Time (s)") + ax4.set_ylabel("φ (°)") + ax4.set_title("Euler Spin Angle") + ax4.grid(True) + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotFlightPathAngleData(self): + """Prints out Flight path and Rocket Attitude angle graphs available + about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + eventTime = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = self.flight.tFinal + eventTimeIndex = -1 + + # Path, Attitude and Lateral Attitude Angle + # Angular position plots + fig5 = plt.figure(figsize=(9, 6)) + + ax1 = plt.subplot(211) + ax1.plot( + self.flight.pathAngle[:, 0], + self.flight.pathAngle[:, 1], + label="Flight Path Angle", + ) + ax1.plot( + self.flight.attitudeAngle[:, 0], + self.flight.attitudeAngle[:, 1], + label="Rocket Attitude Angle", + ) + ax1.set_xlim(0, eventTime) + ax1.legend() + ax1.grid(True) + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Angle (°)") + ax1.set_title("Flight Path and Attitude Angle") + + ax2 = plt.subplot(212) + ax2.plot( + self.flight.lateralAttitudeAngle[:, 0], + self.flight.lateralAttitudeAngle[:, 1], + ) + ax2.set_xlim(0, eventTime) + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Lateral Attitude Angle (°)") + ax2.set_title("Lateral Attitude Angle") + ax2.grid(True) + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotAngularKinematicsData(self): + """Prints out all Angular velocity and acceleration graphs available + about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + eventTime = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = self.flight.tFinal + eventTimeIndex = -1 + + # Angular velocity and acceleration plots + fig4 = plt.figure(figsize=(9, 9)) + ax1 = plt.subplot(311) + ax1.plot(self.flight.w1[:, 0], self.flight.w1[:, 1], color="#ff7f0e") + ax1.set_xlim(0, eventTime) + ax1.set_xlabel("Time (s)") + ax1.set_ylabel(r"Angular Velocity - ${\omega_1}$ (rad/s)", color="#ff7f0e") + ax1.set_title( + r"Angular Velocity ${\omega_1}$ | Angular Acceleration ${\alpha_1}$" + ) + ax1.tick_params("y", colors="#ff7f0e") + ax1.grid(True) + + ax1up = ax1.twinx() + ax1up.plot(self.flight.alpha1[:, 0], self.flight.alpha1[:, 1], color="#1f77b4") + ax1up.set_ylabel( + r"Angular Acceleration - ${\alpha_1}$ (rad/s²)", color="#1f77b4" + ) + ax1up.tick_params("y", colors="#1f77b4") + + ax2 = plt.subplot(312) + ax2.plot(self.flight.w2[:, 0], self.flight.w2[:, 1], color="#ff7f0e") + ax2.set_xlim(0, eventTime) + ax2.set_xlabel("Time (s)") + ax2.set_ylabel(r"Angular Velocity - ${\omega_2}$ (rad/s)", color="#ff7f0e") + ax2.set_title( + r"Angular Velocity ${\omega_2}$ | Angular Acceleration ${\alpha_2}$" + ) + ax2.tick_params("y", colors="#ff7f0e") + ax2.grid(True) + + ax2up = ax2.twinx() + ax2up.plot(self.flight.alpha2[:, 0], self.flight.alpha2[:, 1], color="#1f77b4") + ax2up.set_ylabel( + r"Angular Acceleration - ${\alpha_2}$ (rad/s²)", color="#1f77b4" + ) + ax2up.tick_params("y", colors="#1f77b4") + + ax3 = plt.subplot(313) + ax3.plot(self.flight.w3[:, 0], self.flight.w3[:, 1], color="#ff7f0e") + ax3.set_xlim(0, eventTime) + ax3.set_xlabel("Time (s)") + ax3.set_ylabel(r"Angular Velocity - ${\omega_3}$ (rad/s)", color="#ff7f0e") + ax3.set_title( + r"Angular Velocity ${\omega_3}$ | Angular Acceleration ${\alpha_3}$" + ) + ax3.tick_params("y", colors="#ff7f0e") + ax3.grid(True) + + ax3up = ax3.twinx() + ax3up.plot(self.flight.alpha3[:, 0], self.flight.alpha3[:, 1], color="#1f77b4") + ax3up.set_ylabel( + r"Angular Acceleration - ${\alpha_3}$ (rad/s²)", color="#1f77b4" + ) + ax3up.tick_params("y", colors="#1f77b4") + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotTrajectoryForceData(self): + """Prints out all Forces and Moments graphs available about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero( + self.flight.x[:, 0] == self.flight.outOfRailTime + ) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + eventTime = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = self.flight.tFinal + eventTimeIndex = -1 + + # Rail Button Forces + fig6 = plt.figure(figsize=(9, 6)) + + ax1 = plt.subplot(211) + ax1.plot( + self.flight.railButton1NormalForce[:outOfRailTimeIndex, 0], + self.flight.railButton1NormalForce[:outOfRailTimeIndex, 1], + label="Upper Rail Button", + ) + ax1.plot( + self.flight.railButton2NormalForce[:outOfRailTimeIndex, 0], + self.flight.railButton2NormalForce[:outOfRailTimeIndex, 1], + label="Lower Rail Button", + ) + ax1.set_xlim( + 0, + self.flight.outOfRailTime + if self.flight.outOfRailTime > 0 + else self.flight.tFinal, + ) + ax1.legend() + ax1.grid(True) + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Normal Force (N)") + ax1.set_title("Rail Buttons Normal Force") + + ax2 = plt.subplot(212) + ax2.plot( + self.flight.railButton1ShearForce[:outOfRailTimeIndex, 0], + self.flight.railButton1ShearForce[:outOfRailTimeIndex, 1], + label="Upper Rail Button", + ) + ax2.plot( + self.flight.railButton2ShearForce[:outOfRailTimeIndex, 0], + self.flight.railButton2ShearForce[:outOfRailTimeIndex, 1], + label="Lower Rail Button", + ) + ax2.set_xlim( + 0, + self.flight.outOfRailTime + if self.flight.outOfRailTime > 0 + else self.flight.tFinal, + ) + ax2.legend() + ax2.grid(True) + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Shear Force (N)") + ax2.set_title("Rail Buttons Shear Force") + + plt.subplots_adjust(hspace=0.5) + plt.show() + + # Aerodynamic force and moment plots + fig7 = plt.figure(figsize=(9, 12)) + + ax1 = plt.subplot(411) + ax1.plot( + self.flight.aerodynamicLift[:eventTimeIndex, 0], + self.flight.aerodynamicLift[:eventTimeIndex, 1], + label="Resultant", + ) + ax1.plot( + self.flight.R1[:eventTimeIndex, 0], + self.flight.R1[:eventTimeIndex, 1], + label="R1", + ) + ax1.plot( + self.flight.R2[:eventTimeIndex, 0], + self.flight.R2[:eventTimeIndex, 1], + label="R2", + ) + ax1.set_xlim(0, eventTime) + ax1.legend() + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Lift Force (N)") + ax1.set_title("Aerodynamic Lift Resultant Force") + ax1.grid() + + ax2 = plt.subplot(412) + ax2.plot( + self.flight.aerodynamicDrag[:eventTimeIndex, 0], + self.flight.aerodynamicDrag[:eventTimeIndex, 1], + ) + ax2.set_xlim(0, eventTime) + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Drag Force (N)") + ax2.set_title("Aerodynamic Drag Force") + ax2.grid() + + ax3 = plt.subplot(413) + ax3.plot( + self.flight.aerodynamicBendingMoment[:eventTimeIndex, 0], + self.flight.aerodynamicBendingMoment[:eventTimeIndex, 1], + label="Resultant", + ) + ax3.plot( + self.flight.M1[:eventTimeIndex, 0], + self.flight.M1[:eventTimeIndex, 1], + label="M1", + ) + ax3.plot( + self.flight.M2[:eventTimeIndex, 0], + self.flight.M2[:eventTimeIndex, 1], + label="M2", + ) + ax3.set_xlim(0, eventTime) + ax3.legend() + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("Bending Moment (N m)") + ax3.set_title("Aerodynamic Bending Resultant Moment") + ax3.grid() + + ax4 = plt.subplot(414) + ax4.plot( + self.flight.aerodynamicSpinMoment[:eventTimeIndex, 0], + self.flight.aerodynamicSpinMoment[:eventTimeIndex, 1], + ) + ax4.set_xlim(0, eventTime) + ax4.set_xlabel("Time (s)") + ax4.set_ylabel("Spin Moment (N m)") + ax4.set_title("Aerodynamic Spin Moment") + ax4.grid() + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotEnergyData(self): + """Prints out all Energy components graphs available about the Flight + + Returns + ------- + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero( + self.flight.x[:, 0] == self.flight.outOfRailTime + ) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + eventTime = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = self.flight.tFinal + eventTimeIndex = -1 + + fig8 = plt.figure(figsize=(9, 9)) + + ax1 = plt.subplot(411) + ax1.plot( + self.flight.kineticEnergy[:, 0], + self.flight.kineticEnergy[:, 1], + label="Kinetic Energy", + ) + ax1.plot( + self.flight.rotationalEnergy[:, 0], + self.flight.rotationalEnergy[:, 1], + label="Rotational Energy", + ) + ax1.plot( + self.flight.translationalEnergy[:, 0], + self.flight.translationalEnergy[:, 1], + label="Translational Energy", + ) + ax1.set_xlim( + 0, + self.flight.apogeeTime + if self.flight.apogeeTime != 0.0 + else self.flight.tFinal, + ) + ax1.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax1.set_title("Kinetic Energy Components") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Energy (J)") + + ax1.legend() + ax1.grid() + + ax2 = plt.subplot(412) + ax2.plot( + self.flight.totalEnergy[:, 0], + self.flight.totalEnergy[:, 1], + label="Total Energy", + ) + ax2.plot( + self.flight.kineticEnergy[:, 0], + self.flight.kineticEnergy[:, 1], + label="Kinetic Energy", + ) + ax2.plot( + self.flight.potentialEnergy[:, 0], + self.flight.potentialEnergy[:, 1], + label="Potential Energy", + ) + ax2.set_xlim( + 0, + self.flight.apogeeTime + if self.flight.apogeeTime != 0.0 + else self.flight.tFinal, + ) + ax2.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax2.set_title("Total Mechanical Energy Components") + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Energy (J)") + ax2.legend() + ax2.grid() + + ax3 = plt.subplot(413) + ax3.plot( + self.flight.thrustPower[:, 0], + self.flight.thrustPower[:, 1], + label="|Thrust Power|", + ) + ax3.set_xlim(0, self.flight.rocket.motor.burnOutTime) + ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax3.set_title("Thrust Absolute Power") + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("Power (W)") + ax3.legend() + ax3.grid() + + ax4 = plt.subplot(414) + ax4.plot( + self.flight.dragPower[:, 0], + -self.flight.dragPower[:, 1], + label="|Drag Power|", + ) + ax4.set_xlim( + 0, + self.flight.apogeeTime + if self.flight.apogeeTime != 0.0 + else self.flight.tFinal, + ) + ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax4.set_title("Drag Absolute Power") + ax4.set_xlabel("Time (s)") + ax4.set_ylabel("Power (W)") + ax4.legend() + ax4.grid() + + plt.subplots_adjust(hspace=1) + plt.show() + + return None + + def plotFluidMechanicsData(self): + """Prints out a summary of the Fluid Mechanics graphs available about + the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero( + self.flight.x[:, 0] == self.flight.outOfRailTime + ) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Trajectory Fluid Mechanics Plots + fig10 = plt.figure(figsize=(9, 12)) + + ax1 = plt.subplot(411) + ax1.plot(self.flight.MachNumber[:, 0], self.flight.MachNumber[:, 1]) + ax1.set_xlim(0, self.flight.tFinal) + ax1.set_title("Mach Number") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Mach Number") + ax1.grid() + + ax2 = plt.subplot(412) + ax2.plot(self.flight.ReynoldsNumber[:, 0], self.flight.ReynoldsNumber[:, 1]) + ax2.set_xlim(0, self.flight.tFinal) + ax2.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax2.set_title("Reynolds Number") + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Reynolds Number") + ax2.grid() + + ax3 = plt.subplot(413) + ax3.plot( + self.flight.dynamicPressure[:, 0], + self.flight.dynamicPressure[:, 1], + label="Dynamic Pressure", + ) + ax3.plot( + self.flight.totalPressure[:, 0], + self.flight.totalPressure[:, 1], + label="Total Pressure", + ) + ax3.plot( + self.flight.pressure[:, 0], + self.flight.pressure[:, 1], + label="Static Pressure", + ) + ax3.set_xlim(0, self.flight.tFinal) + ax3.legend() + ax3.ticklabel_format(style="sci", axis="y", scilimits=(0, 0)) + ax3.set_title("Total and Dynamic Pressure") + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("Pressure (Pa)") + ax3.grid() + + ax4 = plt.subplot(414) + ax4.plot(self.flight.angleOfAttack[:, 0], self.flight.angleOfAttack[:, 1]) + # Make sure bottom and top limits are different + if ( + self.flight.outOfRailTime + * self.flight.angleOfAttack(self.flight.outOfRailTime) + != 0 + ): + ax4.set_xlim(self.flight.outOfRailTime, 10 * self.flight.outOfRailTime + 1) + ax4.set_ylim(0, self.flight.angleOfAttack(self.flight.outOfRailTime)) + ax4.set_title("Angle of Attack") + ax4.set_xlabel("Time (s)") + ax4.set_ylabel("Angle of Attack (°)") + ax4.grid() + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotStabilityAndControlData(self): + """Prints out Rocket Stability and Control parameters graphs available + about the Flight + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + fig9 = plt.figure(figsize=(9, 6)) + + ax1 = plt.subplot(211) + ax1.plot(self.flight.staticMargin[:, 0], self.flight.staticMargin[:, 1]) + ax1.set_xlim(0, self.flight.staticMargin[:, 0][-1]) + ax1.set_title("Static Margin") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Static Margin (c)") + ax1.grid() + + ax2 = plt.subplot(212) + maxAttitude = max(self.flight.attitudeFrequencyResponse[:, 1]) + maxAttitude = maxAttitude if maxAttitude != 0 else 1 + ax2.plot( + self.flight.attitudeFrequencyResponse[:, 0], + self.flight.attitudeFrequencyResponse[:, 1] / maxAttitude, + label="Attitude Angle", + ) + maxOmega1 = max(self.flight.omega1FrequencyResponse[:, 1]) + maxOmega1 = maxOmega1 if maxOmega1 != 0 else 1 + ax2.plot( + self.flight.omega1FrequencyResponse[:, 0], + self.flight.omega1FrequencyResponse[:, 1] / maxOmega1, + label=r"$\omega_1$", + ) + maxOmega2 = max(self.flight.omega2FrequencyResponse[:, 1]) + maxOmega2 = maxOmega2 if maxOmega2 != 0 else 1 + ax2.plot( + self.flight.omega2FrequencyResponse[:, 0], + self.flight.omega2FrequencyResponse[:, 1] / maxOmega2, + label=r"$\omega_2$", + ) + maxOmega3 = max(self.flight.omega3FrequencyResponse[:, 1]) + maxOmega3 = maxOmega3 if maxOmega3 != 0 else 1 + ax2.plot( + self.flight.omega3FrequencyResponse[:, 0], + self.flight.omega3FrequencyResponse[:, 1] / maxOmega3, + label=r"$\omega_3$", + ) + ax2.set_title("Frequency Response") + ax2.set_xlabel("Frequency (Hz)") + ax2.set_ylabel("Amplitude Magnitude Normalized") + ax2.set_xlim(0, 5) + ax2.legend() + ax2.grid() + + plt.subplots_adjust(hspace=0.5) + plt.show() + + return None + + def plotPressureSignals(self): + """Prints out all Parachute Trigger Pressure Signals. + This function can be called also for plot pressure data for flights + without Parachutes, in this case the Pressure Signals will be simply + the pressure provided by the atmosphericModel, at Flight z positions. + This means that no noise will be considered if at least one parachute + has not been added. + + This function aims to help the engineer to visually check if there + are anomalies with the Flight Simulation. + + Parameters + ---------- + None + + Return + ------ + None + """ + # Post-process results + if self.flight.postProcessed is False: + self.flight.postProcess() + + if len(self.flight.rocket.parachutes) == 0: + plt.figure() + ax1 = plt.subplot(111) + ax1.plot(self.flight.z[:, 0], self.flight.env.pressure(self.flight.z[:, 1])) + ax1.set_title("Pressure at Rocket's Altitude") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Pressure (Pa)") + ax1.set_xlim(0, self.flight.tFinal) + ax1.grid() + + plt.show() + + else: + for parachute in self.flight.rocket.parachutes: + print("Parachute: ", parachute.name) + parachute.noiseSignalFunction() + parachute.noisyPressureSignalFunction() + parachute.cleanPressureSignalFunction() + + return None + + def allPlots(self): + """Prints out all plots available about the Flight. + It call info() and then all the plots available. + + Parameters + ---------- + None + + Return + ------ + None + """ + + # Plot flight trajectory in a 3D plot + self.plot3dTrajectory() + + # Plot + self.plotLinearKinematicsData() + + # Plot + self.plotFlightPathAngleData() + + # Plot + self.plotAttitudeData() + + # Plot + self.plotAngularKinematicsData() + + # Plot + self.plotTrajectoryForceData() + + # Plot + self.plotEnergyData() + + # Plot + self.plotFluidMechanicsData() + + # Plot pressure signals recorded by the sensors + self.plotPressureSignals() + + # Plot Stability and Control Data + self.plotStabilityAndControlData() + + return None From d4e6a96341dac9a5e1e2c18e4a60389514fcc7a6 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 13:15:25 -0300 Subject: [PATCH 02/15] updated docs --- rocketpy/plots/flight_plots.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 3d58ff9b3..1ec1c3465 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -8,16 +8,16 @@ class _FlightPlots: - """class to plot flight data - Here you also can: - - Print important information about the flight - - See animations of the flight - - Compare plots from different flights - - Compare flights from different rocket simulators + """Class that holds plot methods for Flight class. + + Attributes + ---------- + _FlightPlots.flight : Flight + Flight object that will be used for the plots. """ def __init__(self, flight): - """_summary_ + """Initializes _FlightPlots class. Parameters ---------- @@ -893,7 +893,6 @@ def plotPressureSignals(self): def allPlots(self): """Prints out all plots available about the Flight. - It call info() and then all the plots available. Parameters ---------- From 42f7cf070392f78bf8d38b606106ec3728552cac Mon Sep 17 00:00:00 2001 From: Lint Action Date: Sun, 13 Nov 2022 16:44:24 +0000 Subject: [PATCH 03/15] Fix code style issues with Black --- rocketpy/plots/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/plots/__init__.py b/rocketpy/plots/__init__.py index bf21b6fcf..eabe117ba 100644 --- a/rocketpy/plots/__init__.py +++ b/rocketpy/plots/__init__.py @@ -1,4 +1,4 @@ from .flight_plots import _FlightPlots from .environment_plots import _EnvironmentPlots from .rocket_plots import _RocketPlots -from .compare_plots import _ComparePlots \ No newline at end of file +from .compare_plots import _ComparePlots From aa54bd67080d53ab6c1dd5a86eaf4389bee1eb9c Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 14:41:01 -0300 Subject: [PATCH 04/15] maint: removed 'plot' from methods names --- rocketpy/Flight.py | 18 +++++++-------- rocketpy/plots/flight_plots.py | 42 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index f5d99118d..cf386eaa0 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -2940,31 +2940,31 @@ def allInfo(self): self.printNumericalIntegrationSettings() print("\n\nTrajectory 3d Plot\n") - self.plots.plot3dTrajectory() + self.plots.trajectory_3d() print("\n\nTrajectory Kinematic Plots\n") - self.plots.plotLinearKinematicsData() + self.plots.linear_kinematics_data() print("\n\nAngular Position Plots\n") - self.plots.plotFlightPathAngleData() + self.plots.flight_path_angle_data() print("\n\nPath, Attitude and Lateral Attitude Angle plots\n") - self.plots.plotAttitudeData() + self.plots.attitude_data() print("\n\nTrajectory Angular Velocity and Acceleration Plots\n") - self.plots.plotAngularKinematicsData() + self.plots.angular_kinematics_data() print("\n\nTrajectory Force Plots\n") - self.plots.plotTrajectoryForceData() + self.plots.trajectory_force_data() print("\n\nTrajectory Energy Plots\n") - self.plots.plotEnergyData() + self.plots.energy_data() print("\n\nTrajectory Fluid Mechanics Plots\n") - self.plots.plotFluidMechanicsData() + self.plots.fluid_mechanics_data() print("\n\nTrajectory Stability and Control Plots\n") - self.plots.plotStabilityAndControlData() + self.plots.stability_and_control_data() return None diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 1ec1c3465..c985df482 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -33,7 +33,7 @@ def __init__(self, flight): # Start definition of 'basic' plots methods, the traditional RocketPy plots - def plot3dTrajectory(self): + def trajectory_3d(self): """Plot a 3D graph of the trajectory Parameters @@ -96,7 +96,7 @@ def plot3dTrajectory(self): return None - def plotLinearKinematicsData(self): + def linear_kinematics_data(self): """Prints out all Kinematics graphs available about the Flight Parameters @@ -178,7 +178,7 @@ def plotLinearKinematicsData(self): plt.show() return None - def plotAttitudeData(self): + def attitude_data(self): """Prints out all Angular position graphs available about the Flight Parameters @@ -248,7 +248,7 @@ def plotAttitudeData(self): return None - def plotFlightPathAngleData(self): + def flight_path_angle_data(self): """Prints out Flight path and Rocket Attitude angle graphs available about the Flight @@ -313,7 +313,7 @@ def plotFlightPathAngleData(self): return None - def plotAngularKinematicsData(self): + def angular_kinematics_data(self): """Prints out all Angular velocity and acceleration graphs available about the Flight @@ -401,7 +401,7 @@ def plotAngularKinematicsData(self): return None - def plotTrajectoryForceData(self): + def trajectory_force_data(self): """Prints out all Forces and Moments graphs available about the Flight Parameters @@ -563,7 +563,7 @@ def plotTrajectoryForceData(self): return None - def plotEnergyData(self): + def energy_data(self): """Prints out all Energy components graphs available about the Flight Returns @@ -692,7 +692,7 @@ def plotEnergyData(self): return None - def plotFluidMechanicsData(self): + def fluid_mechanics_data(self): """Prints out a summary of the Fluid Mechanics graphs available about the Flight @@ -780,7 +780,7 @@ def plotFluidMechanicsData(self): return None - def plotStabilityAndControlData(self): + def stability_and_control_data(self): """Prints out Rocket Stability and Control parameters graphs available about the Flight @@ -847,7 +847,7 @@ def plotStabilityAndControlData(self): return None - def plotPressureSignals(self): + def pressure_signals(self): """Prints out all Parachute Trigger Pressure Signals. This function can be called also for plot pressure data for flights without Parachutes, in this case the Pressure Signals will be simply @@ -891,7 +891,7 @@ def plotPressureSignals(self): return None - def allPlots(self): + def all(self): """Prints out all plots available about the Flight. Parameters @@ -904,33 +904,33 @@ def allPlots(self): """ # Plot flight trajectory in a 3D plot - self.plot3dTrajectory() + self.trajectory_3d() # Plot - self.plotLinearKinematicsData() + self.linear_kinematics_data() # Plot - self.plotFlightPathAngleData() + self.flight_path_angle_data() # Plot - self.plotAttitudeData() + self.attitude_data() # Plot - self.plotAngularKinematicsData() + self.angular_kinematics_data() # Plot - self.plotTrajectoryForceData() + self.trajectory_force_data() # Plot - self.plotEnergyData() + self.energy_data() # Plot - self.plotFluidMechanicsData() + self.fluid_mechanics_data() # Plot pressure signals recorded by the sensors - self.plotPressureSignals() + self.pressure_signals() # Plot Stability and Control Data - self.plotStabilityAndControlData() + self.stability_and_control_data() return None From 8517ceb0305d0d3a901565bfa946cd25a95f2cf3 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 16:05:37 -0300 Subject: [PATCH 05/15] maint: deleted unnecessary comment --- rocketpy/plots/flight_plots.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index c985df482..5d26908f2 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -31,8 +31,6 @@ def __init__(self, flight): self.flight = flight return None - # Start definition of 'basic' plots methods, the traditional RocketPy plots - def trajectory_3d(self): """Plot a 3D graph of the trajectory From 3b3e845bb72f99b22a0f71219b68c50625785aa1 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 16:08:07 -0300 Subject: [PATCH 06/15] maint: removed unused variables --- rocketpy/plots/flight_plots.py | 44 ---------------------------------- 1 file changed, 44 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 5d26908f2..193b483e2 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -42,9 +42,6 @@ def trajectory_3d(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get max and min x and y maxZ = max(self.flight.z[:, 1] - self.flight.env.elevation) @@ -105,9 +102,6 @@ def linear_kinematics_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Velocity and acceleration plots fig2 = plt.figure(figsize=(9, 12)) @@ -187,9 +181,6 @@ def attitude_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of time before parachute event if len(self.flight.parachuteEvents) > 0: @@ -197,10 +188,8 @@ def attitude_data(self): self.flight.parachuteEvents[0][0] + self.flight.parachuteEvents[0][1].lag ) - eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] else: eventTime = self.flight.tFinal - eventTimeIndex = -1 # Angular position plots fig3 = plt.figure(figsize=(9, 12)) @@ -258,9 +247,6 @@ def flight_path_angle_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of time before parachute event if len(self.flight.parachuteEvents) > 0: @@ -268,10 +254,8 @@ def flight_path_angle_data(self): self.flight.parachuteEvents[0][0] + self.flight.parachuteEvents[0][1].lag ) - eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] else: eventTime = self.flight.tFinal - eventTimeIndex = -1 # Path, Attitude and Lateral Attitude Angle # Angular position plots @@ -323,9 +307,6 @@ def angular_kinematics_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of time before parachute event if len(self.flight.parachuteEvents) > 0: @@ -333,10 +314,8 @@ def angular_kinematics_data(self): self.flight.parachuteEvents[0][0] + self.flight.parachuteEvents[0][1].lag ) - eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] else: eventTime = self.flight.tFinal - eventTimeIndex = -1 # Angular velocity and acceleration plots fig4 = plt.figure(figsize=(9, 9)) @@ -410,9 +389,6 @@ def trajectory_force_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of out of rail time outOfRailTimeIndexes = np.nonzero( @@ -568,17 +544,11 @@ def energy_data(self): ------- None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of out of rail time outOfRailTimeIndexes = np.nonzero( self.flight.x[:, 0] == self.flight.outOfRailTime ) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) # Get index of time before parachute event if len(self.flight.parachuteEvents) > 0: @@ -586,10 +556,8 @@ def energy_data(self): self.flight.parachuteEvents[0][0] + self.flight.parachuteEvents[0][1].lag ) - eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] else: eventTime = self.flight.tFinal - eventTimeIndex = -1 fig8 = plt.figure(figsize=(9, 9)) @@ -702,17 +670,11 @@ def fluid_mechanics_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() # Get index of out of rail time outOfRailTimeIndexes = np.nonzero( self.flight.x[:, 0] == self.flight.outOfRailTime ) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) # Trajectory Fluid Mechanics Plots fig10 = plt.figure(figsize=(9, 12)) @@ -790,9 +752,6 @@ def stability_and_control_data(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() fig9 = plt.figure(figsize=(9, 6)) @@ -864,9 +823,6 @@ def pressure_signals(self): ------ None """ - # Post-process results - if self.flight.postProcessed is False: - self.flight.postProcess() if len(self.flight.rocket.parachutes) == 0: plt.figure() From bb0ce506d6f45683d56968d67503a7dd54a10533 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 16:14:02 -0300 Subject: [PATCH 07/15] maint: replaced self.plots in __init__ --- rocketpy/Flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 9c5389645..f662b5608 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -609,7 +609,6 @@ def __init__( self.initialSolution = initialSolution self.timeOvershoot = timeOvershoot self.terminateOnApogee = terminateOnApogee - self.plots = _FlightPlots(self) # Modifying Rail Length for a better out of rail condition upperRButton = max(self.rocket.railButtons[0]) @@ -642,6 +641,7 @@ def __init__( # Initialize prints and plots objects self.prints = _FlightPrints(self) + self.plots = _FlightPlots(self) # Initialize solver monitors self.functionEvaluations = [] From 46a4397ead5c6dcc7af37d3db8c235ee8375b670 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 13 Nov 2022 21:10:48 -0300 Subject: [PATCH 08/15] enh: added event time attributes definition in __init__ --- rocketpy/plots/flight_plots.py | 170 +++++++++++++-------------------- 1 file changed, 68 insertions(+), 102 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 193b483e2..292fe1b65 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -14,6 +14,15 @@ class _FlightPlots: ---------- _FlightPlots.flight : Flight Flight object that will be used for the plots. + + _FlightPlots.out_of_rail_time_index : int + Time index of out of rail event. + + _FlightPlots.first_event_time : float + Time of first event. + + _FlightPlots.first_event_time_index : int + Time index of first event. """ def __init__(self, flight): @@ -29,6 +38,28 @@ def __init__(self, flight): None """ self.flight = flight + + # Get index of out of rail time + out_of_rail_time_indexes = np.nonzero( + self.flight.x[:, 0] == self.flight.outOfRailTime + ) + self.out_of_rail_time_index = ( + -1 if len(out_of_rail_time_indexes) == 0 else out_of_rail_time_indexes[0][0] + ) + + # Get index of time before parachute event + if len(self.flight.parachuteEvents) > 0: + self.first_event_time = ( + self.flight.parachuteEvents[0][0] + + self.flight.parachuteEvents[0][1].lag + ) + self.first_event_time_index = np.nonzero( + self.flight.x[:, 0] == self.first_event_time + )[0][0] + else: + self.first_event_time = self.flight.tFinal + self.first_event_time_index = -1 + return None def trajectory_3d(self): @@ -182,15 +213,6 @@ def attitude_data(self): None """ - # Get index of time before parachute event - if len(self.flight.parachuteEvents) > 0: - eventTime = ( - self.flight.parachuteEvents[0][0] - + self.flight.parachuteEvents[0][1].lag - ) - else: - eventTime = self.flight.tFinal - # Angular position plots fig3 = plt.figure(figsize=(9, 12)) @@ -199,7 +221,7 @@ def attitude_data(self): ax1.plot(self.flight.e1[:, 0], self.flight.e1[:, 1], label="$e_1$") ax1.plot(self.flight.e2[:, 0], self.flight.e2[:, 1], label="$e_2$") ax1.plot(self.flight.e3[:, 0], self.flight.e3[:, 1], label="$e_3$") - ax1.set_xlim(0, eventTime) + ax1.set_xlim(0, self.first_event_time) ax1.set_xlabel("Time (s)") ax1.set_ylabel("Euler Parameters") ax1.set_title("Euler Parameters") @@ -208,7 +230,7 @@ def attitude_data(self): ax2 = plt.subplot(412) ax2.plot(self.flight.psi[:, 0], self.flight.psi[:, 1]) - ax2.set_xlim(0, eventTime) + ax2.set_xlim(0, self.first_event_time) ax2.set_xlabel("Time (s)") ax2.set_ylabel("ψ (°)") ax2.set_title("Euler Precession Angle") @@ -216,7 +238,7 @@ def attitude_data(self): ax3 = plt.subplot(413) ax3.plot(self.flight.theta[:, 0], self.flight.theta[:, 1], label="θ - Nutation") - ax3.set_xlim(0, eventTime) + ax3.set_xlim(0, self.first_event_time) ax3.set_xlabel("Time (s)") ax3.set_ylabel("θ (°)") ax3.set_title("Euler Nutation Angle") @@ -224,7 +246,7 @@ def attitude_data(self): ax4 = plt.subplot(414) ax4.plot(self.flight.phi[:, 0], self.flight.phi[:, 1], label="φ - Spin") - ax4.set_xlim(0, eventTime) + ax4.set_xlim(0, self.first_event_time) ax4.set_xlabel("Time (s)") ax4.set_ylabel("φ (°)") ax4.set_title("Euler Spin Angle") @@ -248,15 +270,6 @@ def flight_path_angle_data(self): None """ - # Get index of time before parachute event - if len(self.flight.parachuteEvents) > 0: - eventTime = ( - self.flight.parachuteEvents[0][0] - + self.flight.parachuteEvents[0][1].lag - ) - else: - eventTime = self.flight.tFinal - # Path, Attitude and Lateral Attitude Angle # Angular position plots fig5 = plt.figure(figsize=(9, 6)) @@ -272,7 +285,7 @@ def flight_path_angle_data(self): self.flight.attitudeAngle[:, 1], label="Rocket Attitude Angle", ) - ax1.set_xlim(0, eventTime) + ax1.set_xlim(0, self.first_event_time) ax1.legend() ax1.grid(True) ax1.set_xlabel("Time (s)") @@ -284,7 +297,7 @@ def flight_path_angle_data(self): self.flight.lateralAttitudeAngle[:, 0], self.flight.lateralAttitudeAngle[:, 1], ) - ax2.set_xlim(0, eventTime) + ax2.set_xlim(0, self.first_event_time) ax2.set_xlabel("Time (s)") ax2.set_ylabel("Lateral Attitude Angle (°)") ax2.set_title("Lateral Attitude Angle") @@ -308,20 +321,11 @@ def angular_kinematics_data(self): None """ - # Get index of time before parachute event - if len(self.flight.parachuteEvents) > 0: - eventTime = ( - self.flight.parachuteEvents[0][0] - + self.flight.parachuteEvents[0][1].lag - ) - else: - eventTime = self.flight.tFinal - # Angular velocity and acceleration plots fig4 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(311) ax1.plot(self.flight.w1[:, 0], self.flight.w1[:, 1], color="#ff7f0e") - ax1.set_xlim(0, eventTime) + ax1.set_xlim(0, self.first_event_time) ax1.set_xlabel("Time (s)") ax1.set_ylabel(r"Angular Velocity - ${\omega_1}$ (rad/s)", color="#ff7f0e") ax1.set_title( @@ -339,7 +343,7 @@ def angular_kinematics_data(self): ax2 = plt.subplot(312) ax2.plot(self.flight.w2[:, 0], self.flight.w2[:, 1], color="#ff7f0e") - ax2.set_xlim(0, eventTime) + ax2.set_xlim(0, self.first_event_time) ax2.set_xlabel("Time (s)") ax2.set_ylabel(r"Angular Velocity - ${\omega_2}$ (rad/s)", color="#ff7f0e") ax2.set_title( @@ -357,7 +361,7 @@ def angular_kinematics_data(self): ax3 = plt.subplot(313) ax3.plot(self.flight.w3[:, 0], self.flight.w3[:, 1], color="#ff7f0e") - ax3.set_xlim(0, eventTime) + ax3.set_xlim(0, self.first_event_time) ax3.set_xlabel("Time (s)") ax3.set_ylabel(r"Angular Velocity - ${\omega_3}$ (rad/s)", color="#ff7f0e") ax3.set_title( @@ -390,37 +394,18 @@ def trajectory_force_data(self): None """ - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero( - self.flight.x[:, 0] == self.flight.outOfRailTime - ) - outOfRailTimeIndex = ( - -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] - ) - - # Get index of time before parachute event - if len(self.flight.parachuteEvents) > 0: - eventTime = ( - self.flight.parachuteEvents[0][0] - + self.flight.parachuteEvents[0][1].lag - ) - eventTimeIndex = np.nonzero(self.flight.x[:, 0] == eventTime)[0][0] - else: - eventTime = self.flight.tFinal - eventTimeIndex = -1 - # Rail Button Forces fig6 = plt.figure(figsize=(9, 6)) ax1 = plt.subplot(211) ax1.plot( - self.flight.railButton1NormalForce[:outOfRailTimeIndex, 0], - self.flight.railButton1NormalForce[:outOfRailTimeIndex, 1], + self.flight.railButton1NormalForce[: self.out_of_rail_time_index, 0], + self.flight.railButton1NormalForce[: self.out_of_rail_time_index, 1], label="Upper Rail Button", ) ax1.plot( - self.flight.railButton2NormalForce[:outOfRailTimeIndex, 0], - self.flight.railButton2NormalForce[:outOfRailTimeIndex, 1], + self.flight.railButton2NormalForce[: self.out_of_rail_time_index, 0], + self.flight.railButton2NormalForce[: self.out_of_rail_time_index, 1], label="Lower Rail Button", ) ax1.set_xlim( @@ -437,13 +422,13 @@ def trajectory_force_data(self): ax2 = plt.subplot(212) ax2.plot( - self.flight.railButton1ShearForce[:outOfRailTimeIndex, 0], - self.flight.railButton1ShearForce[:outOfRailTimeIndex, 1], + self.flight.railButton1ShearForce[: self.out_of_rail_time_index, 0], + self.flight.railButton1ShearForce[: self.out_of_rail_time_index, 1], label="Upper Rail Button", ) ax2.plot( - self.flight.railButton2ShearForce[:outOfRailTimeIndex, 0], - self.flight.railButton2ShearForce[:outOfRailTimeIndex, 1], + self.flight.railButton2ShearForce[: self.out_of_rail_time_index, 0], + self.flight.railButton2ShearForce[: self.out_of_rail_time_index, 1], label="Lower Rail Button", ) ax2.set_xlim( @@ -466,21 +451,21 @@ def trajectory_force_data(self): ax1 = plt.subplot(411) ax1.plot( - self.flight.aerodynamicLift[:eventTimeIndex, 0], - self.flight.aerodynamicLift[:eventTimeIndex, 1], + self.flight.aerodynamicLift[: self.first_event_time_index, 0], + self.flight.aerodynamicLift[: self.first_event_time_index, 1], label="Resultant", ) ax1.plot( - self.flight.R1[:eventTimeIndex, 0], - self.flight.R1[:eventTimeIndex, 1], + self.flight.R1[: self.first_event_time_index, 0], + self.flight.R1[: self.first_event_time_index, 1], label="R1", ) ax1.plot( - self.flight.R2[:eventTimeIndex, 0], - self.flight.R2[:eventTimeIndex, 1], + self.flight.R2[: self.first_event_time_index, 0], + self.flight.R2[: self.first_event_time_index, 1], label="R2", ) - ax1.set_xlim(0, eventTime) + ax1.set_xlim(0, self.first_event_time) ax1.legend() ax1.set_xlabel("Time (s)") ax1.set_ylabel("Lift Force (N)") @@ -489,10 +474,10 @@ def trajectory_force_data(self): ax2 = plt.subplot(412) ax2.plot( - self.flight.aerodynamicDrag[:eventTimeIndex, 0], - self.flight.aerodynamicDrag[:eventTimeIndex, 1], + self.flight.aerodynamicDrag[: self.first_event_time_index, 0], + self.flight.aerodynamicDrag[: self.first_event_time_index, 1], ) - ax2.set_xlim(0, eventTime) + ax2.set_xlim(0, self.first_event_time) ax2.set_xlabel("Time (s)") ax2.set_ylabel("Drag Force (N)") ax2.set_title("Aerodynamic Drag Force") @@ -500,21 +485,21 @@ def trajectory_force_data(self): ax3 = plt.subplot(413) ax3.plot( - self.flight.aerodynamicBendingMoment[:eventTimeIndex, 0], - self.flight.aerodynamicBendingMoment[:eventTimeIndex, 1], + self.flight.aerodynamicBendingMoment[: self.first_event_time_index, 0], + self.flight.aerodynamicBendingMoment[: self.first_event_time_index, 1], label="Resultant", ) ax3.plot( - self.flight.M1[:eventTimeIndex, 0], - self.flight.M1[:eventTimeIndex, 1], + self.flight.M1[: self.first_event_time_index, 0], + self.flight.M1[: self.first_event_time_index, 1], label="M1", ) ax3.plot( - self.flight.M2[:eventTimeIndex, 0], - self.flight.M2[:eventTimeIndex, 1], + self.flight.M2[: self.first_event_time_index, 0], + self.flight.M2[: self.first_event_time_index, 1], label="M2", ) - ax3.set_xlim(0, eventTime) + ax3.set_xlim(0, self.first_event_time) ax3.legend() ax3.set_xlabel("Time (s)") ax3.set_ylabel("Bending Moment (N m)") @@ -523,10 +508,10 @@ def trajectory_force_data(self): ax4 = plt.subplot(414) ax4.plot( - self.flight.aerodynamicSpinMoment[:eventTimeIndex, 0], - self.flight.aerodynamicSpinMoment[:eventTimeIndex, 1], + self.flight.aerodynamicSpinMoment[: self.first_event_time_index, 0], + self.flight.aerodynamicSpinMoment[: self.first_event_time_index, 1], ) - ax4.set_xlim(0, eventTime) + ax4.set_xlim(0, self.first_event_time) ax4.set_xlabel("Time (s)") ax4.set_ylabel("Spin Moment (N m)") ax4.set_title("Aerodynamic Spin Moment") @@ -545,20 +530,6 @@ def energy_data(self): None """ - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero( - self.flight.x[:, 0] == self.flight.outOfRailTime - ) - - # Get index of time before parachute event - if len(self.flight.parachuteEvents) > 0: - eventTime = ( - self.flight.parachuteEvents[0][0] - + self.flight.parachuteEvents[0][1].lag - ) - else: - eventTime = self.flight.tFinal - fig8 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(411) @@ -671,11 +642,6 @@ def fluid_mechanics_data(self): None """ - # Get index of out of rail time - outOfRailTimeIndexes = np.nonzero( - self.flight.x[:, 0] == self.flight.outOfRailTime - ) - # Trajectory Fluid Mechanics Plots fig10 = plt.figure(figsize=(9, 12)) From 3256dd87fdc0b27a8bbd52a3ca53367b5c322ed3 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 14 Nov 2022 15:15:23 -0300 Subject: [PATCH 09/15] bug: created __calculate_indexes method to fix @cached_property bug --- rocketpy/plots/flight_plots.py | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 292fe1b65..ea1820eca 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -38,7 +38,11 @@ def __init__(self, flight): None """ self.flight = flight + self.calculated_index = False + return None + + def __calculate_indexes(self): # Get index of out of rail time out_of_rail_time_indexes = np.nonzero( self.flight.x[:, 0] == self.flight.outOfRailTime @@ -60,6 +64,8 @@ def __init__(self, flight): self.first_event_time = self.flight.tFinal self.first_event_time_index = -1 + self.calculated_index = True + return None def trajectory_3d(self): @@ -74,6 +80,9 @@ def trajectory_3d(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Get max and min x and y maxZ = max(self.flight.z[:, 1] - self.flight.env.elevation) maxX = max(self.flight.x[:, 1]) @@ -134,6 +143,9 @@ def linear_kinematics_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Velocity and acceleration plots fig2 = plt.figure(figsize=(9, 12)) @@ -213,6 +225,9 @@ def attitude_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Angular position plots fig3 = plt.figure(figsize=(9, 12)) @@ -270,6 +285,9 @@ def flight_path_angle_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Path, Attitude and Lateral Attitude Angle # Angular position plots fig5 = plt.figure(figsize=(9, 6)) @@ -321,6 +339,9 @@ def angular_kinematics_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Angular velocity and acceleration plots fig4 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(311) @@ -394,6 +415,9 @@ def trajectory_force_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Rail Button Forces fig6 = plt.figure(figsize=(9, 6)) @@ -530,6 +554,9 @@ def energy_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + fig8 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(411) @@ -642,6 +669,9 @@ def fluid_mechanics_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + # Trajectory Fluid Mechanics Plots fig10 = plt.figure(figsize=(9, 12)) @@ -719,6 +749,9 @@ def stability_and_control_data(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + fig9 = plt.figure(figsize=(9, 6)) ax1 = plt.subplot(211) @@ -771,7 +804,7 @@ def stability_and_control_data(self): return None def pressure_signals(self): - """Prints out all Parachute Trigger Pressure Signals. + """Plots out all Parachute Trigger Pressure Signals. This function can be called also for plot pressure data for flights without Parachutes, in this case the Pressure Signals will be simply the pressure provided by the atmosphericModel, at Flight z positions. @@ -790,6 +823,9 @@ def pressure_signals(self): None """ + if not self.calculated_index: + self.__calculate_indexes() + if len(self.flight.rocket.parachutes) == 0: plt.figure() ax1 = plt.subplot(111) From c2dfade6dcedb1773fa2313f8b749552d8e63089 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 14 Nov 2022 15:18:53 -0300 Subject: [PATCH 10/15] maint: __calculate_indexes documentation --- rocketpy/plots/flight_plots.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index ea1820eca..b57c1b011 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -15,6 +15,10 @@ class _FlightPlots: _FlightPlots.flight : Flight Flight object that will be used for the plots. + _FlightPlots.calculated_index : bool + Flag that informs if events time indexes have been + calculated. + _FlightPlots.out_of_rail_time_index : int Time index of out of rail event. @@ -43,6 +47,17 @@ def __init__(self, flight): return None def __calculate_indexes(self): + """Calculates necessary events time index for plots settings. + + Parameters + ---------- + None + + Returns + ------- + None + """ + # Get index of out of rail time out_of_rail_time_indexes = np.nonzero( self.flight.x[:, 0] == self.flight.outOfRailTime From 83c311f2968c8feb62929d3429e3b97884dabdae Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 14 Nov 2022 15:26:50 -0300 Subject: [PATCH 11/15] enh: added self.plots.all() to allInfo --- rocketpy/Flight.py | 27 +-------------------------- rocketpy/plots/flight_plots.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index f662b5608..821bec7ce 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -3743,32 +3743,7 @@ def allInfo(self): # Print a summary of data about the flight self.info() - print("\n\nTrajectory 3d Plot\n") - self.plots.trajectory_3d() - - print("\n\nTrajectory Kinematic Plots\n") - self.plots.linear_kinematics_data() - - print("\n\nAngular Position Plots\n") - self.plots.flight_path_angle_data() - - print("\n\nPath, Attitude and Lateral Attitude Angle plots\n") - self.plots.attitude_data() - - print("\n\nTrajectory Angular Velocity and Acceleration Plots\n") - self.plots.angular_kinematics_data() - - print("\n\nTrajectory Force Plots\n") - self.plots.trajectory_force_data() - - print("\n\nTrajectory Energy Plots\n") - self.plots.energy_data() - - print("\n\nTrajectory Fluid Mechanics Plots\n") - self.plots.fluid_mechanics_data() - - print("\n\nTrajectory Stability and Control Plots\n") - self.plots.stability_and_control_data() + self.plots.all() return None diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index b57c1b011..70969f9ae 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -874,34 +874,34 @@ def all(self): None """ - # Plot flight trajectory in a 3D plot + print("\n\nTrajectory 3d Plot\n") self.trajectory_3d() - # Plot + print("\n\nTrajectory Kinematic Plots\n") self.linear_kinematics_data() - # Plot + print("\n\nAngular Position Plots\n") self.flight_path_angle_data() - # Plot + print("\n\nPath, Attitude and Lateral Attitude Angle plots\n") self.attitude_data() - # Plot + print("\n\nTrajectory Angular Velocity and Acceleration Plots\n") self.angular_kinematics_data() - # Plot + print("\n\nTrajectory Force Plots\n") self.trajectory_force_data() - # Plot + print("\n\nTrajectory Energy Plots\n") self.energy_data() - # Plot + print("\n\nTrajectory Fluid Mechanics Plots\n") self.fluid_mechanics_data() - # Plot pressure signals recorded by the sensors - self.pressure_signals() - - # Plot Stability and Control Data + print("\n\nTrajectory Stability and Control Plots\n") self.stability_and_control_data() + print("\n\nParachute Pressure Signal Plots\n") + self.pressure_signals() + return None From 3f0c7df3a6a9a5e06cb0c5084f6bf09e73fddaa0 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Wed, 16 Nov 2022 21:30:09 -0300 Subject: [PATCH 12/15] initial changes to parachute --- rocketpy/Flight.py | 15 ++++++++++++--- rocketpy/Parachute.py | 6 ++++++ rocketpy/plots/flight_plots.py | 12 +++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 821bec7ce..3d3604a87 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -3234,7 +3234,6 @@ def calculate_rail_button_forces(self): return F11, F12, F21, F22 - @cached_property def __calculate_pressure_signal(self): """Calculate the pressure signal from the pressure sensor. It creates a SignalFunction attribute in the parachute object. @@ -3534,8 +3533,18 @@ def exportPressures(self, fileName, timeStep): else: for parachute in self.rocket.parachutes: for i in range(0, timePoints.size, 1): - pCl = parachute.cleanPressureSignalFunction(timePoints[i]) - pNs = parachute.noisyPressureSignalFunction(timePoints[i]) + pCl = Function( + parachute.cleanPressureSignal, + "Time (s)", + "Pressure - Without Noise (Pa)", + "linear", + )(timePoints[i]) + pNs = Function( + parachute.noisyPressureSignal, + "Time (s)", + "Pressure - With Noise (Pa)", + "linear", + )(timePoints[i]) file.write("{:f}, {:.5f}, {:.5f}\n".format(timePoints[i], pCl, pNs)) # We need to save only 1 parachute data pass diff --git a/rocketpy/Parachute.py b/rocketpy/Parachute.py index 5be11a815..cc45f0624 100644 --- a/rocketpy/Parachute.py +++ b/rocketpy/Parachute.py @@ -4,6 +4,8 @@ import numpy as np +from .Function import Function + class Parachute: """Keeps parachute information. @@ -107,6 +109,10 @@ def __init__( self.noiseBias = noise[0] self.noiseDeviation = noise[1] self.noiseCorr = (noise[2], (1 - noise[2] ** 2) ** 0.5) + self.cleanPressureSignalFunction = Function(0) + self.noisyPressureSignalFunction = Function(0) + self.noiseSignalFunction = Function(0) + alpha, beta = self.noiseCorr self.noiseFunction = lambda: alpha * self.noiseSignal[-1][ 1 diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 70969f9ae..f31fd8875 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -856,9 +856,15 @@ def pressure_signals(self): else: for parachute in self.flight.rocket.parachutes: print("Parachute: ", parachute.name) - parachute.noiseSignalFunction() - parachute.noisyPressureSignalFunction() - parachute.cleanPressureSignalFunction() + try: + parachute.noiseSignalFunction() + parachute.noisyPressureSignalFunction() + parachute.cleanPressureSignalFunction() + except: + self.flight.__calculate_pressure_signal() + parachute.noiseSignalFunction() + parachute.noisyPressureSignalFunction() + parachute.cleanPressureSignalFunction() return None From c520f7bb8ab9b2980083b34c24aee02d9be3e322 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Thu, 29 Dec 2022 03:21:57 -0300 Subject: [PATCH 13/15] enh: added events cached property and fixed pressure plots --- rocketpy/Flight.py | 2 +- rocketpy/plots/flight_plots.py | 161 ++++++++++++--------------------- 2 files changed, 61 insertions(+), 102 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 71ef2d94a..9611395ed 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -2487,7 +2487,7 @@ def calculate_rail_button_forces(self): return F11, F12, F21, F22 - def __calculate_pressure_signal(self): + def _calculate_pressure_signal(self): """Calculate the pressure signal from the pressure sensor. It creates a SignalFunction attribute in the parachute object. Parachute works as a subclass of Rocket class. diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index f31fd8875..0f6e9e3ff 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -6,6 +6,11 @@ import matplotlib.pyplot as plt import numpy as np +try: + from functools import cached_property +except ImportError: + from ..tools import cached_property + class _FlightPlots: """Class that holds plot methods for Flight class. @@ -15,13 +20,6 @@ class _FlightPlots: _FlightPlots.flight : Flight Flight object that will be used for the plots. - _FlightPlots.calculated_index : bool - Flag that informs if events time indexes have been - calculated. - - _FlightPlots.out_of_rail_time_index : int - Time index of out of rail event. - _FlightPlots.first_event_time : float Time of first event. @@ -42,46 +40,26 @@ def __init__(self, flight): None """ self.flight = flight - self.calculated_index = False - return None - def __calculate_indexes(self): - """Calculates necessary events time index for plots settings. - - Parameters - ---------- - None - - Returns - ------- - None - """ - - # Get index of out of rail time - out_of_rail_time_indexes = np.nonzero( - self.flight.x[:, 0] == self.flight.outOfRailTime - ) - self.out_of_rail_time_index = ( - -1 if len(out_of_rail_time_indexes) == 0 else out_of_rail_time_indexes[0][0] - ) - - # Get index of time before parachute event + @cached_property + def first_event_time(self): + """Time of the first flight event.""" if len(self.flight.parachuteEvents) > 0: - self.first_event_time = ( + return ( self.flight.parachuteEvents[0][0] + self.flight.parachuteEvents[0][1].lag ) - self.first_event_time_index = np.nonzero( - self.flight.x[:, 0] == self.first_event_time - )[0][0] else: - self.first_event_time = self.flight.tFinal - self.first_event_time_index = -1 - - self.calculated_index = True + return self.flight.tFinal - return None + @cached_property + def first_event_time_index(self): + """Time index of the first flight event.""" + if len(self.flight.parachuteEvents) > 0: + return np.nonzero(self.flight.x[:, 0] == self.first_event_time)[0][0] + else: + return -1 def trajectory_3d(self): """Plot a 3D graph of the trajectory @@ -95,9 +73,6 @@ def trajectory_3d(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Get max and min x and y maxZ = max(self.flight.z[:, 1] - self.flight.env.elevation) maxX = max(self.flight.x[:, 1]) @@ -158,9 +133,6 @@ def linear_kinematics_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Velocity and acceleration plots fig2 = plt.figure(figsize=(9, 12)) @@ -240,9 +212,6 @@ def attitude_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Angular position plots fig3 = plt.figure(figsize=(9, 12)) @@ -300,9 +269,6 @@ def flight_path_angle_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Path, Attitude and Lateral Attitude Angle # Angular position plots fig5 = plt.figure(figsize=(9, 6)) @@ -354,9 +320,6 @@ def angular_kinematics_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Angular velocity and acceleration plots fig4 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(311) @@ -430,21 +393,18 @@ def trajectory_force_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Rail Button Forces fig6 = plt.figure(figsize=(9, 6)) ax1 = plt.subplot(211) ax1.plot( - self.flight.railButton1NormalForce[: self.out_of_rail_time_index, 0], - self.flight.railButton1NormalForce[: self.out_of_rail_time_index, 1], + self.flight.railButton1NormalForce[: self.flight.outOfRailTimeIndex, 0], + self.flight.railButton1NormalForce[: self.flight.outOfRailTimeIndex, 1], label="Upper Rail Button", ) ax1.plot( - self.flight.railButton2NormalForce[: self.out_of_rail_time_index, 0], - self.flight.railButton2NormalForce[: self.out_of_rail_time_index, 1], + self.flight.railButton2NormalForce[: self.flight.outOfRailTimeIndex, 0], + self.flight.railButton2NormalForce[: self.flight.outOfRailTimeIndex, 1], label="Lower Rail Button", ) ax1.set_xlim( @@ -461,13 +421,13 @@ def trajectory_force_data(self): ax2 = plt.subplot(212) ax2.plot( - self.flight.railButton1ShearForce[: self.out_of_rail_time_index, 0], - self.flight.railButton1ShearForce[: self.out_of_rail_time_index, 1], + self.flight.railButton1ShearForce[: self.flight.outOfRailTimeIndex, 0], + self.flight.railButton1ShearForce[: self.flight.outOfRailTimeIndex, 1], label="Upper Rail Button", ) ax2.plot( - self.flight.railButton2ShearForce[: self.out_of_rail_time_index, 0], - self.flight.railButton2ShearForce[: self.out_of_rail_time_index, 1], + self.flight.railButton2ShearForce[: self.flight.outOfRailTimeIndex, 0], + self.flight.railButton2ShearForce[: self.flight.outOfRailTimeIndex, 1], label="Lower Rail Button", ) ax2.set_xlim( @@ -569,9 +529,6 @@ def energy_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - fig8 = plt.figure(figsize=(9, 9)) ax1 = plt.subplot(411) @@ -684,9 +641,6 @@ def fluid_mechanics_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - # Trajectory Fluid Mechanics Plots fig10 = plt.figure(figsize=(9, 12)) @@ -764,9 +718,6 @@ def stability_and_control_data(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - fig9 = plt.figure(figsize=(9, 6)) ax1 = plt.subplot(211) @@ -818,6 +769,31 @@ def stability_and_control_data(self): return None + def pressure_rocket_altitude(self): + """Plots out pressure at rocket's altitude. + + Parameters + ---------- + None + + Return + ------ + None + """ + + plt.figure() + ax1 = plt.subplot(111) + ax1.plot(self.flight.z[:, 0], self.flight.env.pressure(self.flight.z[:, 1])) + ax1.set_title("Pressure at Rocket's Altitude") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Pressure (Pa)") + ax1.set_xlim(0, self.flight.tFinal) + ax1.grid() + + plt.show() + + return None + def pressure_signals(self): """Plots out all Parachute Trigger Pressure Signals. This function can be called also for plot pressure data for flights @@ -838,33 +814,15 @@ def pressure_signals(self): None """ - if not self.calculated_index: - self.__calculate_indexes() - - if len(self.flight.rocket.parachutes) == 0: - plt.figure() - ax1 = plt.subplot(111) - ax1.plot(self.flight.z[:, 0], self.flight.env.pressure(self.flight.z[:, 1])) - ax1.set_title("Pressure at Rocket's Altitude") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Pressure (Pa)") - ax1.set_xlim(0, self.flight.tFinal) - ax1.grid() - - plt.show() - - else: + if len(self.flight.parachuteEvents) > 0: for parachute in self.flight.rocket.parachutes: - print("Parachute: ", parachute.name) - try: - parachute.noiseSignalFunction() - parachute.noisyPressureSignalFunction() - parachute.cleanPressureSignalFunction() - except: - self.flight.__calculate_pressure_signal() - parachute.noiseSignalFunction() - parachute.noisyPressureSignalFunction() - parachute.cleanPressureSignalFunction() + print("\nParachute: ", parachute.name) + self.flight._calculate_pressure_signal() + parachute.noiseSignalFunction() + parachute.noisyPressureSignalFunction() + parachute.cleanPressureSignalFunction() + else: + print("\nRocket has no parachutes. No parachute plots available") return None @@ -907,7 +865,8 @@ def all(self): print("\n\nTrajectory Stability and Control Plots\n") self.stability_and_control_data() - print("\n\nParachute Pressure Signal Plots\n") + print("\n\nRocket and Parachute Pressure Plots\n") + self.pressure_rocket_altitude() self.pressure_signals() return None From 8a7c74a480bf83f5996f097e81d6793722529f31 Mon Sep 17 00:00:00 2001 From: Guilherme Date: Thu, 29 Dec 2022 12:01:39 +0100 Subject: [PATCH 14/15] FIX: pressure_rocket_altitude not working --- rocketpy/plots/flight_plots.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 0f6e9e3ff..df0042ca6 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -781,16 +781,7 @@ def pressure_rocket_altitude(self): None """ - plt.figure() - ax1 = plt.subplot(111) - ax1.plot(self.flight.z[:, 0], self.flight.env.pressure(self.flight.z[:, 1])) - ax1.set_title("Pressure at Rocket's Altitude") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("Pressure (Pa)") - ax1.set_xlim(0, self.flight.tFinal) - ax1.grid() - - plt.show() + self.flight.pressure() return None From 7094d0ae50569a15c3e1875b6ecffea5bfeaae79 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sun, 1 Jan 2023 23:25:11 -0300 Subject: [PATCH 15/15] enh: add title and formatting to pressure at rockets altitude plots --- rocketpy/plots/flight_plots.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index df0042ca6..b2f6f5c0e 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -781,7 +781,18 @@ def pressure_rocket_altitude(self): None """ - self.flight.pressure() + # self.flight.pressure() + + plt.figure() + ax1 = plt.subplot(111) + ax1.plot(self.flight.pressure[:, 0], self.flight.pressure[:, 1]) + ax1.set_title("Pressure at Rocket's Altitude") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Pressure (Pa)") + ax1.set_xlim(0, self.flight.tFinal) + ax1.grid() + + plt.show() return None