From 61c1ca3753bb66f479fa56c6f29d11cace8962df Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Tue, 4 Oct 2022 19:09:32 +0200 Subject: [PATCH 01/14] ENH: moving flight plots to the new flight_plots --- rocketpy/Flight.py | 969 +------------------ rocketpy/plots/flight_plots.py | 1626 ++++++++++++++++++++++++++++++++ rocketpy/utilities.py | 149 --- 3 files changed, 1649 insertions(+), 1095 deletions(-) create mode 100644 rocketpy/plots/flight_plots.py diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index e23bda0a8..36e8d5f43 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 flight_plots class Flight: @@ -1631,7 +1632,7 @@ def uDotParachute(self, t, u, postProcessing=False): return [vx, vy, vz, ax, ay, az, 0, 0, 0, 0, 0, 0, 0] - def _initialize_quaternion_functions(self, interpolation, extrapolation): + def __initialize_quaternion_functions(self, interpolation, extrapolation): """# Check when e0, e1, e2, e3, is outside the valid (-1, 1) # Created to deal with numerical error problems, which implied in # breaking errors when defining self.theta and others. @@ -1727,7 +1728,7 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): self.vz = Function( sol[:, [0, 6]], "Time (s)", "Vz (m/s)", interpolation, extrapolation ) - self._initialize_quaternion_functions(interpolation, extrapolation) + self.__initialize_quaternion_functions(interpolation, extrapolation) self.w1 = Function( sol[:, [0, 11]], "Time (s)", "ω1 (rad/s)", interpolation, extrapolation ) @@ -2394,189 +2395,7 @@ def info(self): ------ 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 - - # Print surface wind conditions - print("Surface Wind Conditions\n") - print("Frontal Surface Wind Speed: {:.2f} m/s".format(self.frontalSurfaceWind)) - print("Lateral Surface Wind Speed: {:.2f} m/s".format(self.lateralSurfaceWind)) - - # Print out of rail conditions - print("\n\n Rail Departure State\n") - print("Rail Departure Time: {:.3f} s".format(self.outOfRailTime)) - print("Rail Departure Velocity: {:.3f} m/s".format(self.outOfRailVelocity)) - print( - "Rail Departure Static Margin: {:.3f} c".format( - self.staticMargin(self.outOfRailTime) - ) - ) - print( - "Rail Departure Angle of Attack: {:.3f}°".format( - self.angleOfAttack(self.outOfRailTime) - ) - ) - print( - "Rail Departure Thrust-Weight Ratio: {:.3f}".format( - self.rocket.thrustToWeight(self.outOfRailTime) - ) - ) - print( - "Rail Departure Reynolds Number: {:.3e}".format( - self.ReynoldsNumber(self.outOfRailTime) - ) - ) - - # Print burnOut conditions - print("\n\nBurnOut State\n") - print("BurnOut time: {:.3f} s".format(self.rocket.motor.burnOutTime)) - print( - "Altitude at burnOut: {:.3f} m (AGL)".format( - self.z(self.rocket.motor.burnOutTime) - self.env.elevation - ) - ) - print( - "Rocket velocity at burnOut: {:.3f} m/s".format( - self.speed(self.rocket.motor.burnOutTime) - ) - ) - print( - "Freestream velocity at burnOut: {:.3f} m/s".format( - ( - self.streamVelocityX(self.rocket.motor.burnOutTime) ** 2 - + self.streamVelocityY(self.rocket.motor.burnOutTime) ** 2 - + self.streamVelocityZ(self.rocket.motor.burnOutTime) ** 2 - ) - ** 0.5 - ) - ) - print( - "Mach Number at burnOut: {:.3f}".format( - self.MachNumber(self.rocket.motor.burnOutTime) - ) - ) - print( - "Kinetic energy at burnOut: {:.3e} J".format( - self.kineticEnergy(self.rocket.motor.burnOutTime) - ) - ) - - # Print apogee conditions - print("\n\nApogee\n") - print( - "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( - self.apogee, self.apogee - self.env.elevation - ) - ) - print("Apogee Time: {:.3f} s".format(self.apogeeTime)) - print("Apogee Freestream Speed: {:.3f} m/s".format(self.apogeeFreestreamSpeed)) - - # Print events registered - print("\n\nEvents\n") - if len(self.parachuteEvents) == 0: - print("No Parachute Events Were Triggered.") - for event in self.parachuteEvents: - triggerTime = event[0] - parachute = event[1] - openTime = triggerTime + parachute.lag - velocity = self.freestreamSpeed(openTime) - altitude = self.z(openTime) - name = parachute.name.title() - print(name + " Ejection Triggered at: {:.3f} s".format(triggerTime)) - print(name + " Parachute Inflated at: {:.3f} s".format(openTime)) - print( - name - + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( - velocity - ) - ) - print( - name - + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( - altitude - self.env.elevation - ) - ) - - # Print impact conditions - if len(self.impactState) != 0: - print("\n\nImpact\n") - print("X Impact: {:.3f} m".format(self.xImpact)) - print("Y Impact: {:.3f} m".format(self.yImpact)) - print("Time of Impact: {:.3f} s".format(self.tFinal)) - print("Velocity at Impact: {:.3f} m/s".format(self.impactVelocity)) - elif self.terminateOnApogee is False: - print("\n\nEnd of Simulation\n") - print("Time: {:.3f} s".format(self.solution[-1][0])) - print("Altitude: {:.3f} m".format(self.solution[-1][3])) - - # Print maximum values - print("\n\nMaximum Values\n") - print( - "Maximum Speed: {:.3f} m/s at {:.2f} s".format( - self.maxSpeed, self.maxSpeedTime - ) - ) - print( - "Maximum Mach Number: {:.3f} Mach at {:.2f} s".format( - self.maxMachNumber, self.maxMachNumberTime - ) - ) - print( - "Maximum Reynolds Number: {:.3e} at {:.2f} s".format( - self.maxReynoldsNumber, self.maxReynoldsNumberTime - ) - ) - print( - "Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format( - self.maxDynamicPressure, self.maxDynamicPressureTime - ) - ) - print( - "Maximum Acceleration: {:.3f} m/s² at {:.2f} s".format( - self.maxAcceleration, self.maxAccelerationTime - ) - ) - print( - "Maximum Gs: {:.3f} g at {:.2f} s".format( - self.maxAcceleration / self.env.g, self.maxAccelerationTime - ) - ) - print( - "Maximum Upper Rail Button Normal Force: {:.3f} N".format( - self.maxRailButton1NormalForce - ) - ) - print( - "Maximum Upper Rail Button Shear Force: {:.3f} N".format( - self.maxRailButton1ShearForce - ) - ) - print( - "Maximum Lower Rail Button Normal Force: {:.3f} N".format( - self.maxRailButton2NormalForce - ) - ) - print( - "Maximum Lower Rail Button Shear Force: {:.3f} N".format( - self.maxRailButton2ShearForce - ) - ) - + flight_plots([self]).info() return None def printInitialConditionsData(self): @@ -2590,35 +2409,7 @@ def printInitialConditionsData(self): ------ None """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - print( - "Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format( - self.x(0), self.y(0), self.z(0) - ) - ) - print( - "Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format( - self.vx(0), self.vy(0), self.vz(0) - ) - ) - print( - "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format( - self.e0(0), self.e1(0), self.e2(0), self.e3(0) - ) - ) - print( - "Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format( - self.phi(0), self.theta(0), self.psi(0) - ) - ) - print( - "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format( - self.w1(0), self.w2(0), self.w3(0) - ) - ) + flight_plots([self]).printInitialConditionsData() return None def printNumericalIntegrationSettings(self): @@ -2632,23 +2423,7 @@ def printNumericalIntegrationSettings(self): ------ None """ - print("Maximum Allowed Flight Time: {:f} s".format(self.maxTime)) - print("Maximum Allowed Time Step: {:f} s".format(self.maxTimeStep)) - print("Minimum Allowed Time Step: {:e} s".format(self.minTimeStep)) - print("Relative Error Tolerance: ", self.rtol) - print("Absolute Error Tolerance: ", self.atol) - print("Allow Event Overshoot: ", self.timeOvershoot) - print("Terminate Simulation on Apogee: ", self.terminateOnApogee) - print("Number of Time Steps Used: ", len(self.timeSteps)) - print( - "Number of Derivative Functions Evaluation: ", - sum(self.functionEvaluationsPerTimeStep), - ) - print( - "Average Function Evaluations per Time Step: {:3f}".format( - sum(self.functionEvaluationsPerTimeStep) / len(self.timeSteps) - ) - ) + flight_plots([self]).printNumericalIntegrationSettings() return None @@ -2706,50 +2481,7 @@ def plot3dTrajectory(self): ------ 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() + flight_plots([self]).plot3dTrajectory() return None @@ -2764,71 +2496,7 @@ def plotLinearKinematicsData(self): ------ 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() + flight_plots([self]).plotLinearKinematicsData() return None def plotAttitudeData(self): @@ -2842,59 +2510,7 @@ def plotAttitudeData(self): ------ 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() + flight_plots([self]).plotAttitudeData() return None @@ -2910,47 +2526,7 @@ def plotFlightPathAngleData(self): ------ 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() - + flight_plots([self]).plotFlightPathAngleData() return None def plotAngularKinematicsData(self): @@ -2966,76 +2542,7 @@ def plotAngularKinematicsData(self): 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() - + flight_plots([self]).plotAngularKinematicsData() return None def plotTrajectoryForceData(self): @@ -3049,128 +2556,7 @@ def plotTrajectoryForceData(self): ------ 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 - if self.rocket.railButtons is not None: - 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() + flight_plots([self]).plotTrajectoryForceData() return None @@ -3181,90 +2567,7 @@ def plotEnergyData(self): ------- 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() - + flight_plots([self]).plotEnergyData() return None def plotFluidMechanicsData(self): @@ -3279,67 +2582,7 @@ def plotFluidMechanicsData(self): ------ 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() + flight_plots([self]).plotFluidMechanicsData() return None @@ -3352,6 +2595,7 @@ def calculateFinFlutterAnalysis(self, finThickness, shearModulus): not be useful for fins made from non-isotropic materials. These results should not be used as a way to fully prove the safety of any rocket’s fins. IMPORTANT: This function works if only a single set of fins is added + TODO: Separate this into calculation and plot, the method is too large. Parameters ---------- @@ -3512,58 +2756,7 @@ def plotStabilityAndControlData(self): ------ 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() + flight_plots([self]).plotStabilityAndControlData() return None @@ -3586,31 +2779,12 @@ def plotPressureSignals(self): ------ 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() + flight_plots([self]).plotPressureSignals() return None + # Comment: Maybe we need a file for exportFlight methods... + 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 @@ -3849,51 +3023,7 @@ def allInfo(self): ------ None """ - # Post-process results - if self.postProcessed is False: - self.postProcess() - - # Print initial conditions - print("Initial Conditions\n") - self.printInitialConditionsData() - - # Print launch rail orientation - print("\n\nLaunch Rail Orientation\n") - print("Launch Rail Inclination: {:.2f}°".format(self.inclination)) - print("Launch Rail Heading: {:.2f}°\n\n".format(self.heading)) - - # Print a summary of data about the flight - self.info() - - print("\n\nNumerical Integration Information\n") - self.printNumericalIntegrationSettings() - - print("\n\nTrajectory 3d Plot\n") - self.plot3dTrajectory() - - print("\n\nTrajectory Kinematic Plots\n") - self.plotLinearKinematicsData() - - print("\n\nAngular Position Plots\n") - self.plotFlightPathAngleData() - - print("\n\nPath, Attitude and Lateral Attitude Angle plots\n") - self.plotAttitudeData() - - print("\n\nTrajectory Angular Velocity and Acceleration Plots\n") - self.plotAngularKinematicsData() - - print("\n\nTrajectory Force Plots\n") - self.plotTrajectoryForceData() - - print("\n\nTrajectory Energy Plots\n") - self.plotEnergyData() - - print("\n\nTrajectory Fluid Mechanics Plots\n") - self.plotFluidMechanicsData() - - print("\n\nTrajectory Stability and Control Plots\n") - self.plotStabilityAndControlData() + flight_plots([self]).allInfo() return None @@ -3901,62 +3031,9 @@ def animate(self, start=0, stop=None, fps=12, speed=4, elev=None, azim=None): """Plays an animation of the flight. Not implemented yet. Only kinda works outside notebook. """ - # Set up stopping time - stop = self.tFinal if stop is None else stop - # Speed = 4 makes it almost real time - matplotlib is way to slow - # Set up graph - fig = plt.figure(figsize=(18, 15)) - axes = fig.gca(projection="3d") - # Initialize time - timeRange = np.linspace(start, stop, fps * (stop - start)) - # Initialize first frame - axes.set_title("Trajectory and Velocity Animation") - axes.set_xlabel("X (m)") - axes.set_ylabel("Y (m)") - axes.set_zlabel("Z (m)") - axes.view_init(elev, azim) - R = axes.quiver(0, 0, 0, 0, 0, 0, color="r", label="Rocket") - V = axes.quiver(0, 0, 0, 0, 0, 0, color="g", label="Velocity") - W = axes.quiver(0, 0, 0, 0, 0, 0, color="b", label="Wind") - S = axes.quiver(0, 0, 0, 0, 0, 0, color="black", label="Freestream") - axes.legend() - # Animate - for t in timeRange: - R.remove() - V.remove() - W.remove() - S.remove() - # Calculate rocket position - Rx, Ry, Rz = self.x(t), self.y(t), self.z(t) - Ru = 1 * (2 * (self.e1(t) * self.e3(t) + self.e0(t) * self.e2(t))) - Rv = 1 * (2 * (self.e2(t) * self.e3(t) - self.e0(t) * self.e1(t))) - Rw = 1 * (1 - 2 * (self.e1(t) ** 2 + self.e2(t) ** 2)) - # Calculate rocket Mach number - Vx = self.vx(t) / 340.40 - Vy = self.vy(t) / 340.40 - Vz = self.vz(t) / 340.40 - # Calculate wind Mach Number - z = self.z(t) - Wx = self.env.windVelocityX(z) / 20 - Wy = self.env.windVelocityY(z) / 20 - # Calculate freestream Mach Number - Sx = self.streamVelocityX(t) / 340.40 - Sy = self.streamVelocityY(t) / 340.40 - Sz = self.streamVelocityZ(t) / 340.40 - # Plot Quivers - R = axes.quiver(Rx, Ry, Rz, Ru, Rv, Rw, color="r") - V = axes.quiver(Rx, Ry, Rz, -Vx, -Vy, -Vz, color="g") - W = axes.quiver(Rx - Vx, Ry - Vy, Rz - Vz, Wx, Wy, 0, color="b") - S = axes.quiver(Rx, Ry, Rz, Sx, Sy, Sz, color="black") - # Adjust axis - axes.set_xlim(Rx - 1, Rx + 1) - axes.set_ylim(Ry - 1, Ry + 1) - axes.set_zlim(Rz - 1, Rz + 1) - # plt.pause(1/(fps*speed)) - try: - plt.pause(1 / (fps * speed)) - except: - time.sleep(1 / (fps * speed)) + flight_plots([self]).animate(self, start, stop, fps, speed, elev, azim) + + return None def timeIterator(self, nodeList): i = 0 diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py new file mode 100644 index 000000000..d83ddcc63 --- /dev/null +++ b/rocketpy/plots/flight_plots.py @@ -0,0 +1,1626 @@ +# -*- coding: utf-8 -*- + +import time +import warnings + +import matplotlib.pyplot as plt +import numpy as np + +__author__ = "Guilherme Fernandes Alves" +__copyright__ = "Copyright 20XX, RocketPy Team" +__license__ = "MIT" + +# TODO: Usage must be documented and examples must be added +# TODO: Ok, hard challenge part is to include the possibility of plotting multiple flights in the same plot, but without loosing the ability to plot a single flight as well +# TODO: Allow user to choose the units of the plots +# TODO: Allow user to choose the color pallet of the plots +# TODO: Add masses plots since it can vary significantly for multi-stage rockets + +# TODO: Add unit tests + +# Major obs.: MAYBE I should call Function to do comparison plots, and improving colors functionality directly inside function + + +class flight_plots: + """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, + trajectory_list, + names_list=None, + ): + """_summary_ + + Parameters + ---------- + trajectory_list : list + List of Flight objects + names_list : list, optional + (the default is None, which [default_description]) + + Returns + ------- + _type_ + _description_ + """ + self.names_list = names_list + + if isinstance(trajectory_list, list): + self.trajectory_list = trajectory_list + # elif isinstance(trajectory_list, Flight): + # self.trajectory_list = [trajectory_list] + # self.names_list = [trajectory_list.__name__] + else: + raise TypeError("trajectory_list must be a list of Flight objects") + + self.names_list = ( + [("Trajectory " + str(i + 1)) for i in range(len(self.trajectory_list))] + if names_list == None + else names_list + ) + + # Start definition of Prints methods, no plots here for now + + def printInitialConditionsData(self): + """Prints all initial conditions data available about the flights passed + by the trajectory_list. + + Parameters + ---------- + None + + Return + ------ + None + """ + + for index, flight in enumerate(self.trajectory_list): + + print("Initial Conditions for Flight: ", self.names_list[index]) + + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + print( + "Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format( + flight.x(0), flight.y(0), flight.z(0) + ) + ) + print( + "Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format( + flight.vx(0), flight.vy(0), flight.vz(0) + ) + ) + print( + "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format( + flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0) + ) + ) + print( + "Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format( + flight.phi(0), flight.theta(0), flight.psi(0) + ) + ) + print( + "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s \n".format( + flight.w1(0), flight.w2(0), flight.w3(0) + ) + ) + + return None + + def printNumericalIntegrationSettings(self): + """Prints out the Numerical Integration settings available about the + flights passed by the trajectory_list. + + Parameters + ---------- + None + + Return + ------ + None + """ + for index, flight in enumerate(self.trajectory_list): + print("Numerical Integration Settings of Flight: ", self.names_list[index]) + print("Maximum Allowed Flight Time: {:f} s".format(flight.maxTime)) + print("Maximum Allowed Time Step: {:f} s".format(flight.maxTimeStep)) + print("Minimum Allowed Time Step: {:e} s".format(flight.minTimeStep)) + print("Relative Error Tolerance: ", flight.rtol) + print("Absolute Error Tolerance: ", flight.atol) + print("Allow Event Overshoot: ", flight.timeOvershoot) + print("Terminate Simulation on Apogee: ", flight.terminateOnApogee) + print("Number of Time Steps Used: ", len(flight.timeSteps)) + print( + "Number of Derivative Functions Evaluation: ", + sum(flight.functionEvaluationsPerTimeStep), + ) + print( + "Average Function Evaluations per Time Step: {:3f}".format( + sum(flight.functionEvaluationsPerTimeStep) / len(flight.timeSteps) + ) + ) + + return None + + def printSurfaceWindConditions(self): + """Prints out the Surface Wind Conditions available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + print("Surface Wind Conditions of Flight: ", self.names_list[index]) + print( + "Frontal Surface Wind Speed: {:.2f} m/s".format( + flight.frontalSurfaceWind + ) + ) + print( + "Lateral Surface Wind Speed: {:.2f} m/s".format( + flight.lateralSurfaceWind + ) + ) + + return None + + def printLaunchRailConditions(self): + """Prints out the Launch Rail Conditions available about the flights + passed by the trajectory_list. + + Parameters + ---------- + None + + Returns + ------- + None + """ + + for index, flight in enumerate(self.trajectory_list): + print("Launch Rail Orientation of Flight: ", self.names_list[index]) + print("Launch Rail Inclination: {:.2f}°".format(flight.inclination)) + print("Launch Rail Heading: {:.2f}°\n\n".format(flight.heading)) + return None + + def printOutOfRailConditions(self): + """Prints out the Out of Rail Conditions available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + print("Rail Departure State of Flight: ", self.names_list[index]) + print("Rail Departure Time: {:.3f} s".format(flight.outOfRailTime)) + print( + "Rail Departure Velocity: {:.3f} m/s".format(flight.outOfRailVelocity) + ) + print( + "Rail Departure Static Margin: {:.3f} c".format( + flight.staticMargin(flight.outOfRailTime) + ) + ) + print( + "Rail Departure Angle of Attack: {:.3f}°".format( + flight.angleOfAttack(flight.outOfRailTime) + ) + ) + print( + "Rail Departure Thrust-Weight Ratio: {:.3f}".format( + flight.rocket.thrustToWeight(flight.outOfRailTime) + ) + ) + print( + "Rail Departure Reynolds Number: {:.3e}".format( + flight.ReynoldsNumber(flight.outOfRailTime) + ) + ) + + return None + + def printBurnOutConditions(self): + """Prints out the Burn Out Conditions available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + print("BurnOut State of Flight: ", self.names_list[index]) + print("BurnOut time: {:.3f} s".format(flight.rocket.motor.burnOutTime)) + print( + "Altitude at burnOut: {:.3f} m (AGL)".format( + flight.z(flight.rocket.motor.burnOutTime) - flight.env.elevation + ) + ) + print( + "Rocket velocity at burnOut: {:.3f} m/s".format( + flight.speed(flight.rocket.motor.burnOutTime) + ) + ) + print( + "Freestream velocity at burnOut: {:.3f} m/s".format( + ( + flight.streamVelocityX(flight.rocket.motor.burnOutTime) ** 2 + + flight.streamVelocityY(flight.rocket.motor.burnOutTime) ** 2 + + flight.streamVelocityZ(flight.rocket.motor.burnOutTime) ** 2 + ) + ** 0.5 + ) + ) + print( + "Mach Number at burnOut: {:.3f}".format( + flight.MachNumber(flight.rocket.motor.burnOutTime) + ) + ) + print( + "Kinetic energy at burnOut: {:.3e} J".format( + flight.kineticEnergy(flight.rocket.motor.burnOutTime) + ) + ) + + return None + + def printApogeeConditions(self): + """Prints out the Apogee Conditions available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + print("Apogee State of Flight: ", self.names_list[index]) + print( + "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( + flight.apogee, flight.apogee - flight.env.elevation + ) + ) + print("Apogee Time: {:.3f} s".format(flight.apogeeTime)) + print( + "Apogee Freestream Speed: {:.3f} m/s".format( + flight.apogeeFreestreamSpeed + ) + ) + + return None + + def printEventsRegistered(self): + """Prints out the Events Registered available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + print("Parachute Events of Flight: ", self.names_list[index]) + if len(flight.parachuteEvents) == 0: + print("No Parachute Events Were Triggered.") + for event in flight.parachuteEvents: + triggerTime = event[0] + parachute = event[1] + openTime = triggerTime + parachute.lag + velocity = flight.freestreamSpeed(openTime) + altitude = flight.z(openTime) + name = parachute.name.title() + print(name + " Ejection Triggered at: {:.3f} s".format(triggerTime)) + print(name + " Parachute Inflated at: {:.3f} s".format(openTime)) + print( + name + + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( + velocity + ) + ) + print( + name + + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( + altitude - flight.env.elevation + ) + ) + return None + + def printImpactConditions(self): + """Prints out the Impact Conditions available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + if len(flight.impactState) != 0: + print("Impact Conditions of Flight: ", self.names_list[index]) + print("X Impact: {:.3f} m".format(flight.xImpact)) + print("Y Impact: {:.3f} m".format(flight.yImpact)) + print("Time of Impact: {:.3f} s".format(flight.tFinal)) + print("Velocity at Impact: {:.3f} m/s".format(flight.impactVelocity)) + elif flight.terminateOnApogee is False: + print("End of Simulation of Flight: ", flight.names_list[index]) + print("Time: {:.3f} s".format(flight.solution[-1][0])) + print("Altitude: {:.3f} m".format(flight.solution[-1][3])) + + return None + + def printMaximumValues(self): + """Prints out the Maximum Values available about the flights + passed by the trajectory_list. + + Returns + ------- + None + """ + for index, flight in enumerate(self.trajectory_list): + print("Maximum Values of Flight: ", self.names_list[index]) + print( + "Maximum Speed: {:.3f} m/s at {:.2f} s".format( + flight.maxSpeed, flight.maxSpeedTime + ) + ) + print( + "Maximum Mach Number: {:.3f} Mach at {:.2f} s".format( + flight.maxMachNumber, flight.maxMachNumberTime + ) + ) + print( + "Maximum Reynolds Number: {:.3e} at {:.2f} s".format( + flight.maxReynoldsNumber, flight.maxReynoldsNumberTime + ) + ) + print( + "Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format( + flight.maxDynamicPressure, flight.maxDynamicPressureTime + ) + ) + print( + "Maximum Acceleration: {:.3f} m/s² at {:.2f} s".format( + flight.maxAcceleration, flight.maxAccelerationTime + ) + ) + print( + "Maximum Gs: {:.3f} g at {:.2f} s".format( + flight.maxAcceleration / flight.env.g, flight.maxAccelerationTime + ) + ) + print( + "Maximum Upper Rail Button Normal Force: {:.3f} N".format( + flight.maxRailButton1NormalForce + ) + ) + print( + "Maximum Upper Rail Button Shear Force: {:.3f} N".format( + flight.maxRailButton1ShearForce + ) + ) + print( + "Maximum Lower Rail Button Normal Force: {:.3f} N".format( + flight.maxRailButton2NormalForce + ) + ) + print( + "Maximum Lower Rail Button Shear Force: {:.3f} N".format( + flight.maxRailButton2ShearForce + ) + ) + return None + + # Start definition of 'basic' plots methods, the traditional RocketPy plots + + def plot3dTrajectory(self, savefig=False): + """Plot a 3D graph of the trajectory + + Parameters + ---------- + savefig: str, optional + If a string is passed, the figure will be saved with the name passed. + Default is False. + + Return + ------ + None + """ + + warnings.warn( + "plot3dTrajectory is going to be deprecated, use compareFlightTrajectories3D instead.", + ) + self.compareFlightTrajectories3D(legend=False, savefig=savefig) + + return None + + def plotLinearKinematicsData(self): + """Prints out all Kinematics graphs available about the Flight + + # TODO: Separate into velocity and acceleration plots for various flights + + Parameters + ---------- + None + + Return + ------ + None + """ + + for index, flight in enumerate(self.trajectory_list): + + if flight.postProcessed is False: + flight.postProcess() + + # Velocity and acceleration plots + fig2 = plt.figure(figsize=(9, 12)) + fig2.suptitle( + "Linear Kinematics Data of Flight: {}".format(self.names_list[index]) + ) + + ax1 = plt.subplot(414) + ax1.plot(flight.vx[:, 0], flight.vx[:, 1], color="#ff7f0e") + ax1.set_xlim(0, 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(flight.ax[:, 0], 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(flight.vy[:, 0], flight.vy[:, 1], color="#ff7f0e") + ax2.set_xlim(0, 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(flight.ay[:, 0], 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(flight.vz[:, 0], flight.vz[:, 1], color="#ff7f0e") + ax3.set_xlim(0, 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(flight.az[:, 0], 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(flight.speed[:, 0], flight.speed[:, 1], color="#ff7f0e") + ax4.set_xlim(0, 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( + flight.acceleration[:, 0], 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 + """ + + for index, flight in enumerate(self.trajectory_list): + + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of time before parachute event + if len(flight.parachuteEvents) > 0: + eventTime = ( + flight.parachuteEvents[0][0] + flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = flight.tFinal + eventTimeIndex = -1 + + # Angular position plots + fig3 = plt.figure(figsize=(9, 12)) + fig3.suptitle("Euler Angles of Flight: {}".format(self.names_list[index])) + + ax1 = plt.subplot(411) + ax1.plot(flight.e0[:, 0], flight.e0[:, 1], label="$e_0$") + ax1.plot(flight.e1[:, 0], flight.e1[:, 1], label="$e_1$") + ax1.plot(flight.e2[:, 0], flight.e2[:, 1], label="$e_2$") + ax1.plot(flight.e3[:, 0], 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(flight.psi[:, 0], 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(flight.theta[:, 0], 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(flight.phi[:, 0], 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 + """ + for index, flight in enumerate(self.trajectory_list): + + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of time before parachute event + if len(flight.parachuteEvents) > 0: + eventTime = ( + flight.parachuteEvents[0][0] + flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = flight.tFinal + eventTimeIndex = -1 + + # Path, Attitude and Lateral Attitude Angle + # Angular position plots + fig5 = plt.figure(figsize=(9, 6)) + fig5.suptitle( + "Flight Path and Attitude Data of Flight: {}".format( + self.names_list[index] + ) + ) + + ax1 = plt.subplot(211) + ax1.plot( + flight.pathAngle[:, 0], + flight.pathAngle[:, 1], + label="Flight Path Angle", + ) + ax1.plot( + flight.attitudeAngle[:, 0], + 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( + flight.lateralAttitudeAngle[:, 0], 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 + """ + + for index, flight in enumerate(self.trajectory_list): + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of time before parachute event + if len(flight.parachuteEvents) > 0: + eventTime = ( + flight.parachuteEvents[0][0] + flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = flight.tFinal + eventTimeIndex = -1 + + # Angular velocity and acceleration plots + fig4 = plt.figure(figsize=(9, 9)) + fig4.suptitle( + "Angular Kinematics Data of Flight: {}".format(self.names_list[index]) + ) + + ax1 = plt.subplot(311) + ax1.plot(flight.w1[:, 0], 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(flight.alpha1[:, 0], 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(flight.w2[:, 0], 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(flight.alpha2[:, 0], 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(flight.w3[:, 0], 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(flight.alpha3[:, 0], 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 + """ + for index, flight in enumerate(self.trajectory_list): + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero(flight.x[:, 0] == flight.outOfRailTime) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Get index of time before parachute event + if len(flight.parachuteEvents) > 0: + eventTime = ( + flight.parachuteEvents[0][0] + flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = flight.tFinal + eventTimeIndex = -1 + + # Rail Button Forces + if flight.rocket.railButtons is not None: + fig6 = plt.figure(figsize=(9, 6)) + fig6.suptitle( + "Rail Button Forces of Flight: {}".format(self.names_list[index]) + ) + + ax1 = plt.subplot(211) + ax1.plot( + flight.railButton1NormalForce[:outOfRailTimeIndex, 0], + flight.railButton1NormalForce[:outOfRailTimeIndex, 1], + label="Upper Rail Button", + ) + ax1.plot( + flight.railButton2NormalForce[:outOfRailTimeIndex, 0], + flight.railButton2NormalForce[:outOfRailTimeIndex, 1], + label="Lower Rail Button", + ) + ax1.set_xlim( + 0, + flight.outOfRailTime if flight.outOfRailTime > 0 else 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( + flight.railButton1ShearForce[:outOfRailTimeIndex, 0], + flight.railButton1ShearForce[:outOfRailTimeIndex, 1], + label="Upper Rail Button", + ) + ax2.plot( + flight.railButton2ShearForce[:outOfRailTimeIndex, 0], + flight.railButton2ShearForce[:outOfRailTimeIndex, 1], + label="Lower Rail Button", + ) + ax2.set_xlim( + 0, + flight.outOfRailTime if flight.outOfRailTime > 0 else 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)) + fig7.suptitle( + "Aerodynamic Forces and Moments of Flight: {}".format( + self.names_list[index] + ) + ) + + ax1 = plt.subplot(411) + ax1.plot( + flight.aerodynamicLift[:eventTimeIndex, 0], + flight.aerodynamicLift[:eventTimeIndex, 1], + label="Resultant", + ) + ax1.plot( + flight.R1[:eventTimeIndex, 0], flight.R1[:eventTimeIndex, 1], label="R1" + ) + ax1.plot( + flight.R2[:eventTimeIndex, 0], 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( + flight.aerodynamicDrag[:eventTimeIndex, 0], + 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( + flight.aerodynamicBendingMoment[:eventTimeIndex, 0], + flight.aerodynamicBendingMoment[:eventTimeIndex, 1], + label="Resultant", + ) + ax3.plot( + flight.M1[:eventTimeIndex, 0], flight.M1[:eventTimeIndex, 1], label="M1" + ) + ax3.plot( + flight.M2[:eventTimeIndex, 0], 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( + flight.aerodynamicSpinMoment[:eventTimeIndex, 0], + 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 + """ + for index, flight in enumerate(self.trajectory_list): + + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero(flight.x[:, 0] == flight.outOfRailTime) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Get index of time before parachute event + if len(flight.parachuteEvents) > 0: + eventTime = ( + flight.parachuteEvents[0][0] + flight.parachuteEvents[0][1].lag + ) + eventTimeIndex = np.nonzero(flight.x[:, 0] == eventTime)[0][0] + else: + eventTime = flight.tFinal + eventTimeIndex = -1 + + fig8 = plt.figure(figsize=(9, 9)) + fig8.suptitle( + "Energy Components of Flight: {}".format(self.names_list[index]) + ) + + ax1 = plt.subplot(411) + ax1.plot( + flight.kineticEnergy[:, 0], + flight.kineticEnergy[:, 1], + label="Kinetic Energy", + ) + ax1.plot( + flight.rotationalEnergy[:, 0], + flight.rotationalEnergy[:, 1], + label="Rotational Energy", + ) + ax1.plot( + flight.translationalEnergy[:, 0], + flight.translationalEnergy[:, 1], + label="Translational Energy", + ) + ax1.set_xlim( + 0, flight.apogeeTime if flight.apogeeTime != 0.0 else 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( + flight.totalEnergy[:, 0], flight.totalEnergy[:, 1], label="Total Energy" + ) + ax2.plot( + flight.kineticEnergy[:, 0], + flight.kineticEnergy[:, 1], + label="Kinetic Energy", + ) + ax2.plot( + flight.potentialEnergy[:, 0], + flight.potentialEnergy[:, 1], + label="Potential Energy", + ) + ax2.set_xlim( + 0, flight.apogeeTime if flight.apogeeTime != 0.0 else 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( + flight.thrustPower[:, 0], + flight.thrustPower[:, 1], + label="|Thrust Power|", + ) + ax3.set_xlim(0, 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( + flight.dragPower[:, 0], -flight.dragPower[:, 1], label="|Drag Power|" + ) + ax4.set_xlim( + 0, flight.apogeeTime if flight.apogeeTime != 0.0 else 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 + """ + for index, flight in enumerate(self.trajectory_list): + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + # Get index of out of rail time + outOfRailTimeIndexes = np.nonzero(flight.x[:, 0] == flight.outOfRailTime) + outOfRailTimeIndex = ( + -1 if len(outOfRailTimeIndexes) == 0 else outOfRailTimeIndexes[0][0] + ) + + # Trajectory Fluid Mechanics Plots + fig10 = plt.figure(figsize=(9, 12)) + fig10.suptitle( + "Fluid Mechanics Components of Flight: {}".format( + self.names_list[index] + ) + ) + + ax1 = plt.subplot(411) + ax1.plot(flight.MachNumber[:, 0], flight.MachNumber[:, 1]) + ax1.set_xlim(0, 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(flight.ReynoldsNumber[:, 0], flight.ReynoldsNumber[:, 1]) + ax2.set_xlim(0, 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( + flight.dynamicPressure[:, 0], + flight.dynamicPressure[:, 1], + label="Dynamic Pressure", + ) + ax3.plot( + flight.totalPressure[:, 0], + flight.totalPressure[:, 1], + label="Total Pressure", + ) + ax3.plot( + flight.pressure[:, 0], flight.pressure[:, 1], label="Static Pressure" + ) + ax3.set_xlim(0, 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(flight.angleOfAttack[:, 0], flight.angleOfAttack[:, 1]) + # Make sure bottom and top limits are different + if flight.outOfRailTime * flight.angleOfAttack(flight.outOfRailTime) != 0: + ax4.set_xlim(flight.outOfRailTime, 10 * flight.outOfRailTime + 1) + ax4.set_ylim(0, flight.angleOfAttack(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 + """ + for index, flight in enumerate(self.trajectory_list): + print( + "Stability And Control Data of Flight: ".format(self.names_list[index]) + ) + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + fig9 = plt.figure(figsize=(9, 6)) + fig9.suptitle( + "Stability and Control Components of Flight: {}".format( + self.names_list[index] + ) + ) + + ax1 = plt.subplot(211) + ax1.plot(flight.staticMargin[:, 0], flight.staticMargin[:, 1]) + ax1.set_xlim(0, 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(flight.attitudeFrequencyResponse[:, 1]) + maxAttitude = maxAttitude if maxAttitude != 0 else 1 + ax2.plot( + flight.attitudeFrequencyResponse[:, 0], + flight.attitudeFrequencyResponse[:, 1] / maxAttitude, + label="Attitude Angle", + ) + maxOmega1 = max(flight.omega1FrequencyResponse[:, 1]) + maxOmega1 = maxOmega1 if maxOmega1 != 0 else 1 + ax2.plot( + flight.omega1FrequencyResponse[:, 0], + flight.omega1FrequencyResponse[:, 1] / maxOmega1, + label=r"$\omega_1$", + ) + maxOmega2 = max(flight.omega2FrequencyResponse[:, 1]) + maxOmega2 = maxOmega2 if maxOmega2 != 0 else 1 + ax2.plot( + flight.omega2FrequencyResponse[:, 0], + flight.omega2FrequencyResponse[:, 1] / maxOmega2, + label=r"$\omega_2$", + ) + maxOmega3 = max(flight.omega3FrequencyResponse[:, 1]) + maxOmega3 = maxOmega3 if maxOmega3 != 0 else 1 + ax2.plot( + flight.omega3FrequencyResponse[:, 0], + 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 + """ + for index, flight in enumerate(self.trajectory_list): + # Post-process results + if flight.postProcessed is False: + flight.postProcess() + + if len(flight.rocket.parachutes) == 0: + plt.figure() + ax1 = plt.subplot(111) + ax1.plot(flight.z[:, 0], flight.env.pressure(flight.z[:, 1])) + ax1.set_title( + "Pressure at Rocket's Altitude, Flight: {}".format( + self.names_list[index] + ) + ) + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("Pressure (Pa)") + ax1.set_xlim(0, flight.tFinal) + ax1.grid() + + plt.show() + + else: + for parachute in flight.rocket.parachutes: + print("Parachute: ", parachute.name) + parachute.noiseSignalFunction() + parachute.noisyPressureSignalFunction() + parachute.cleanPressureSignalFunction() + + return None + + # Start definition of 'compare' plots methods + + def comparePositions(self): + + return None + + def compareVelocities(self): + + return None + + def compareAccelerations(self): + + return None + + def compareEulerAngles(self): + + return None + + def compareQuaternions(self): + + return None + + def compareAngularVelocities(self): + + return None + + def compareAngularAccelerations(self): + + return None + + def compareForces(self): + + return None + + def compareMoments(self): + + return None + + def compareRailButtonsForces(self): + + return None + + def compareAnglesOfAttack(self): + + return None + + def compareStaticMargins(self): + + return None + + def compareAttitudeFrequencyResponses(self): + + return None + + # def compare + + @staticmethod + def compareTrajectories3D(trajectory_list, names_list, legend=None): + """Creates a trajectory plot combining the trajectories listed. + This function was created based two source-codes: + - Mateus Stano: https://github.com/RocketPy-Team/Hackathon_2020/pull/123 + - Dyllon Preston: https://github.com/Dyllon-P/MBS-Template/blob/main/MBS.py + Also, some of the credits go to Georgia Tech Experimental Rocketry Club (GTXR) + as well. + The final function was created by the RocketPy Team. + + Parameters + ---------- + trajectory_list : list, array + List of trajectories. Must be in the form of [trajectory_1, trajectory_2, ..., trajectory_n] + where each element is a list with the arrays regarding positions in x, y and z [x, y, z]. + The trajectories must be in the same reference frame. The z coordinate must be referenced + to the ground or to the sea level, but it is important that all trajectories are passed + in the same reference. + names_list : list, optional + List of strings with the name of each trajectory inputted. The names must be in + the same order as the trajectories in trajectory_list. If no names are passed, the + trajectories will be named as "Trajectory 1", "Trajectory 2", ..., "Trajectory n". + legend : boolean, optional + Whether legend will or will not be plotted. Default is True + + Returns + ------- + None + """ + + # TODO: Allow the user to set the colors or color style + # TODO: Allow the user to set the line style + + # Initialize variables + maxX, maxY, maxZ, minX, minY, minZ, maxXY, minXY = 0, 0, 0, 0, 0, 0, 0, 0 + + # Create the figure + fig1 = plt.figure(figsize=(9, 9)) + ax1 = plt.subplot(111, projection="3d") + + # Iterate through trajectories + for index, flight in enumerate(trajectory_list): + + x, y, z = flight + + # Find max/min values for each component + maxX = max(x) if max(x) > maxX else maxX + maxY = max(y) if max(y) > maxY else maxY + maxZ = max(z) if max(z) > maxZ else maxZ + minX = min(x) if min(x) < minX else minX + minY = min(x) if min(x) < minX else minX + minZ = min(z) if min(z) < minZ else minZ + maxXY = max(maxX, maxY) if max(maxX, maxY) > maxXY else maxXY + minXY = min(minX, minY) if min(minX, minY) > minXY else minXY + + # Add Trajectory as a plot in main figure + ax1.plot(x, y, z, linewidth="2", label=names_list[index]) + + # Plot settings + ax1.scatter(0, 0, 0) + ax1.set_xlabel("X - East (m)") + ax1.set_ylabel("Y - North (m)") + ax1.set_zlabel("Z - Altitude (m)") + ax1.set_title("Flight Trajectories Comparison") + ax1.set_zlim3d([minZ, maxZ]) + ax1.set_ylim3d([minXY, maxXY]) + ax1.set_xlim3d([minXY, maxXY]) + ax1.view_init(15, 45) + if legend: + plt.legend() + plt.show() + + return None + + def compareFlightTrajectories3D(self, legend=None, savefig=None): + """Creates a trajectory plot that is the combination of the trajectories of + the Flight objects passed via a Python list. + + Parameters + ---------- + legend : boolean, optional + Whether legend will or will not be included. Default is True + savefig : string, optional + If a string is passed, the figure will be saved in the path passed. + + Returns + ------- + None + + """ + + # Iterate through Flight objects and create a list of trajectories + trajectory_list = [] + for index, flight in enumerate(self.trajectory_list): + + # Check post process + if flight.postProcessed is False: + flight.postProcess() + + # Get trajectories + x = flight.x[:, 1] + y = flight.y[:, 1] + z = flight.z[:, 1] - flight.env.elevation + trajectory_list.append([x, y, z]) + + # Call compareTrajectories3D function to do the hard work + self.compareTrajectories3D(trajectory_list, self.names_list, legend=legend) + + return None + + def compareFlightTrajectories2D(self, legend=None): + """... + Let it chose the two planes... + - XY projection plot + - XZ projection plot + - YZ projection plot + """ + pass + + @staticmethod + def compareFlightSimulators(): + """Allow the user to compare a flight from RocketPy (or more than one) + against a flight from another simulator (e.g. OpenRocket, Cambridge, etc.) + Still not implemented yet. + Should also allow comparison between RocketPy and actual flight data. + """ + pass + + # Start definition of animations methods + + def animate(self, start=0, stop=None, fps=12, speed=4, elev=None, azim=None): + """Plays an animation of the flight. Not implemented yet. Only + kinda works outside notebook. + """ + for index, flight in enumerate(self.trajectory_list): + # Set up stopping time + stop = flight.tFinal if stop is None else stop + # Speed = 4 makes it almost real time - matplotlib is way to slow + # Set up graph + fig = plt.figure(figsize=(18, 15)) + fig.suptitle("Flight: {}".format(self.names_list[index])) + axes = fig.gca(projection="3d") + # Initialize time + timeRange = np.linspace(start, stop, fps * (stop - start)) + # Initialize first frame + axes.set_title("Trajectory and Velocity Animation") + axes.set_xlabel("X (m)") + axes.set_ylabel("Y (m)") + axes.set_zlabel("Z (m)") + axes.view_init(elev, azim) + R = axes.quiver(0, 0, 0, 0, 0, 0, color="r", label="Rocket") + V = axes.quiver(0, 0, 0, 0, 0, 0, color="g", label="Velocity") + W = axes.quiver(0, 0, 0, 0, 0, 0, color="b", label="Wind") + S = axes.quiver(0, 0, 0, 0, 0, 0, color="black", label="Freestream") + axes.legend() + # Animate + for t in timeRange: + R.remove() + V.remove() + W.remove() + S.remove() + # Calculate rocket position + Rx, Ry, Rz = flight.x(t), flight.y(t), flight.z(t) + Ru = 1 * ( + 2 * (flight.e1(t) * flight.e3(t) + flight.e0(t) * flight.e2(t)) + ) + Rv = 1 * ( + 2 * (flight.e2(t) * flight.e3(t) - flight.e0(t) * flight.e1(t)) + ) + Rw = 1 * (1 - 2 * (flight.e1(t) ** 2 + flight.e2(t) ** 2)) + # Calculate rocket Mach number + Vx = flight.vx(t) / 340.40 + Vy = flight.vy(t) / 340.40 + Vz = flight.vz(t) / 340.40 + # Calculate wind Mach Number + z = flight.z(t) + Wx = flight.env.windVelocityX(z) / 20 + Wy = flight.env.windVelocityY(z) / 20 + # Calculate freestream Mach Number + Sx = flight.streamVelocityX(t) / 340.40 + Sy = flight.streamVelocityY(t) / 340.40 + Sz = flight.streamVelocityZ(t) / 340.40 + # Plot Quivers + R = axes.quiver(Rx, Ry, Rz, Ru, Rv, Rw, color="r") + V = axes.quiver(Rx, Ry, Rz, -Vx, -Vy, -Vz, color="g") + W = axes.quiver(Rx - Vx, Ry - Vy, Rz - Vz, Wx, Wy, 0, color="b") + S = axes.quiver(Rx, Ry, Rz, Sx, Sy, Sz, color="black") + # Adjust axis + axes.set_xlim(Rx - 1, Rx + 1) + axes.set_ylim(Ry - 1, Ry + 1) + axes.set_zlim(Rz - 1, Rz + 1) + # plt.pause(1/(fps*speed)) + try: + plt.pause(1 / (fps * speed)) + except: + time.sleep(1 / (fps * speed)) + + def info(self): + """Prints out a summary of the data available about the Flight. + + Parameters + ---------- + None + + Return + ------ + None + """ + + # Print initial conditions + self.printInitialConditionsData() + + # Print surface wind conditions + self.printSurfaceWindConditions() + + # Print launch rail orientation + self.printLaunchRailConditions() + + # Print out of rail conditions + self.printOutOfRailConditions() + + # Print burnOut conditions + self.printBurnOutConditions() + + # Print apogee conditions + self.printApogeeConditions() + + # Print events registered + self.printEventsRegistered() + + # Print impact conditions + self.printImpactConditions() + + # Print maximum values + self.printMaximumValues() + + # Print Numerical Integration Information + self.printNumericalIntegrationSettings() + + return None + + def allInfo(self, mode="basic"): + """Prints out all data and graphs available about the Flight. + It call info() and then all the plots available. + + Parameters + ---------- + mode : str, optional + The level of detail to print. The default is "basic". + Options are "compare" and "basic". + "compare" prints all data and graphs available. + "basic" prints will basically repeat the code inside a for loop. + + Return + ------ + None + """ + if mode == "basic": + + # Print a summary of data about the flight + self.info() + + # 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() + + elif mode == "compare": + print("Compare mode not yet implemented") + + self.info() + + self.compareFlightTrajectories3D() + + self.comparePositions() + + self.compareVelocities() + + self.compareAccelerations() + + else: + raise ValueError("Mode must be 'basic' or 'compare'") + + return None diff --git a/rocketpy/utilities.py b/rocketpy/utilities.py index 7671b414b..61073b2b9 100644 --- a/rocketpy/utilities.py +++ b/rocketpy/utilities.py @@ -201,155 +201,6 @@ def du(z, u): return altitudeFunction, velocityFunction, final_sol -def compareTrajectories( - trajectory_list, - names=None, - legend=True, -): - """Creates a trajectory plot combining the trajectories listed. - This function was created based two source-codes: - - Mateus Stano: https://github.com/RocketPy-Team/Hackathon_2020/pull/123 - - Dyllon Preston: https://github.com/Dyllon-P/MBS-Template/blob/main/MBS.py - - Parameters - ---------- - trajectory_list : list, array - List of trajectories. Must be in the form of [trajectory_1, trajectory_2, ..., trajectory_n] - where each element is a list with the arrays regarding positions in x, y and z [x, y, z]. - The trajectories must be in the same reference frame. The z coordinate must be referenced - to the ground or to the sea level, but it is important that all trajectories are passed - in the same reference. - names : list, optional - List of strings with the name of each trajectory inputted. The names must be in - the same order as the trajectories in trajectory_list. If no names are passed, the - trajectories will be named as "Trajectory 1", "Trajectory 2", ..., "Trajectory n". - legend : boolean, optional - Whether legend will or will not be plotted. Default is True - - Returns - ------- - None - - """ - # TODO: Allow the user to catch different planes (xy, yz, xz) from the main plot (this can be done in a separate function) - # TODO: Allow the user to set the colors or color style - # TODO: Allow the user to set the line style - - # Initialize variables - maxX, maxY, maxZ, minX, minY, minZ, maxXY, minXY = 0, 0, 0, 0, 0, 0, 0, 0 - - names = ( - [("Trajectory " + str(i + 1)) for i in range(len(trajectory_list))] - if names == None - else names - ) - - # Create the figure - fig1 = plt.figure(figsize=(9, 9)) - ax1 = plt.subplot(111, projection="3d") - - # Iterate through trajectories - for i, trajectory in enumerate(trajectory_list): - - x, y, z = trajectory - - # Find max/min values for each component - maxX = max(x) if max(x) > maxX else maxX - maxY = max(y) if max(y) > maxY else maxY - maxZ = max(z) if max(z) > maxZ else maxZ - minX = min(x) if min(x) < minX else minX - minY = min(x) if min(x) < minX else minX - minZ = min(z) if min(z) < minZ else minZ - maxXY = max(maxX, maxY) if max(maxX, maxY) > maxXY else maxXY - minXY = min(minX, minY) if min(minX, minY) > minXY else minXY - - # Add Trajectory as a plot in main figure - ax1.plot(x, y, z, linewidth="2", label=names[i]) - - # Plot settings - ax1.scatter(0, 0, 0) - ax1.set_xlabel("X - East (m)") - ax1.set_ylabel("Y - North (m)") - ax1.set_zlabel("Z - Altitude (m)") - ax1.set_title("Flight Trajectories Comparison") - ax1.set_zlim3d([minZ, maxZ]) - ax1.set_ylim3d([minXY, maxXY]) - ax1.set_xlim3d([minXY, maxXY]) - ax1.view_init(15, 45) - if legend: - plt.legend() - plt.show() - - return None - - -def compareFlightTrajectories( - flight_list, - names=None, - legend=True, -): - """Creates a trajectory plot that is the combination of the trajectories of - the Flight objects passed via a Python list. - - Parameters - ---------- - flight_list : list, array - List of FLight objects. The flights must be in the same reference frame. - names : list, optional - List of strings with the name of each trajectory inputted. The names must be in - the same order as the trajectories in flight_list - legend : boolean, optional - Whether legend will or will not be included. Default is True - - Returns - ------- - None - - """ - # TODO: Allow the user to catch different planes (xy, yz, xz) from the main plot - # TODO: Allow the user to set the colors or color style - # TODO: Allow the user to set the line style - - # Iterate through Flight objects and create a list of trajectories - trajectory_list = [] - for flight in flight_list: - - # Check post process - if flight.postProcessed is False: - flight.postProcess() - - # Get trajectories - x = flight.x[:, 1] - y = flight.y[:, 1] - z = flight.z[:, 1] - flight.env.elevation - trajectory_list.append([x, y, z]) - - # Call compareTrajectories function to do the hard work - compareTrajectories(trajectory_list, names, legend) - - return None - - -def compareAllInfo(flight_list, names=None): - """Creates a plot with the altitude, velocity and acceleration of the - Flight objects passed via a Python list. - - Parameters - ---------- - flight_list : list, array - List of FLight objects. The flights must be in the same reference frame. - names : list, optional - List of strings with the name of each trajectory inputted. The names must be in - the same order as the trajectories in flight_list - - Returns - ------- - None - - """ - return None - - def create_dispersion_dictionary(filename): """Creates a dictionary with the rocket data provided by a .csv file. File should be organized in four columns: attribute_class, parameter_name, From dff75be202c1bb1d17af0e1650a86ced449dc0b1 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 12:30:22 +0200 Subject: [PATCH 02/14] ENH: Adding first two compare functions --- rocketpy/plots/flight_plots.py | 189 +++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index d83ddcc63..d48e0d46a 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from cProfile import label import time import warnings @@ -1262,14 +1263,202 @@ def plotPressureSignals(self): # Start definition of 'compare' plots methods def comparePositions(self): + """_summary_ + + Returns + ------- + None + """ + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Rocket Position Comparison", fontsize=16, y=1.02, x=0.5) + + ax1 = plt.subplot(312) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.x[:, 0], + flight.x[:, 1], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title("X (m) x Time (s)") + ax1.set_xlabel("Time (s)") + ax1.set_ylabel("X (m)") + ax1.grid(True) + + ax2 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.y[:, 0], + flight.y[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title("Y (m) x Time (s)") + ax2.set_xlabel("Time (s)") + ax2.set_ylabel("Y (m)") + ax2.grid(True) + + ax3 = plt.subplot(311) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.z[:, 0], + flight.z[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title("Z (m) x Time (s)") + ax3.set_xlabel("Time (s)") + ax3.set_ylabel("Z (m)") + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 0.995), + ) + fig.tight_layout() return None def compareVelocities(self): + """_summary_ + + Returns + ------- + None + """ + + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Rocket Velocity Comparison", fontsize=16, y=1.02, x=0.5) + + ax1 = plt.subplot(312) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.vx[:, 0], + flight.vx[:, 1], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title("Velocity X (m/s) x Time (s)") + ax1.set_xlabel(flight.vy.getInputs()[0]) + ax1.set_ylabel(flight.vx.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.vy[:, 0], + flight.vy[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title("Velocity Y (m/s) x Time (s)") + ax2.set_xlabel(flight.vy.getInputs()[0]) + ax2.set_ylabel(flight.vy.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(311) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.vz[:, 0], + flight.vz[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title("Velocity Z (m/s) x Time (s)") + ax3.set_xlabel(flight.vz.getInputs()[0]) + ax3.set_ylabel(flight.vz.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), # TODO: Need to be more flexible here, changing the number of rows as well + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 0.995), + ) + fig.tight_layout() return None def compareAccelerations(self): + """_summary_ + + Returns + ------- + None + """ + + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Rocket Acceleration Comparison", fontsize=16, y=1.02, x=0.5) + + ax1 = plt.subplot(312) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.ax[:, 0], + flight.ax[:, 1], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title("Acceleration X (m/s²) x Time (s)") + ax1.set_xlabel(flight.ax.getInputs()[0]) + ax1.set_ylabel(flight.ax.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.vy[:, 0], + flight.vy[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title("Acceleration Y (m/s²) x Time (s)") + ax2.set_xlabel(flight.ay.getInputs()[0]) + ax2.set_ylabel(flight.ay.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(311) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.vz[:, 0], + flight.vz[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title("Acceleration Z (m/s²) x Time (s)") + ax3.set_xlabel(flight.vz.getInputs()[0]) + ax3.set_ylabel(flight.vz.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), # TODO: Need to be more flexible here, changing the number of rows as well + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 0.995), + ) + fig.tight_layout() return None From 702d98664beb91a407fd0a9a78cc6f350cf58d18 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 13:29:50 +0200 Subject: [PATCH 03/14] Merge enh/complex_simulations into refactor_plots --- .../deployable_payload_example.ipynb | 187 +++++++++--------- docs/requirements.txt | 2 +- docs/user/index.rst | 2 + rocketpy/Flight.py | 147 +++----------- tests/test_flight.py | 6 +- 5 files changed, 122 insertions(+), 222 deletions(-) diff --git a/docs/notebooks/deployable_payload_example.ipynb b/docs/notebooks/deployable_payload_example.ipynb index 44c4659cd..50f15525f 100644 --- a/docs/notebooks/deployable_payload_example.ipynb +++ b/docs/notebooks/deployable_payload_example.ipynb @@ -60,7 +60,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -71,17 +71,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you are using Jupyter Notebooks, it is recommended to run the following line to make matplotlib plots which will be shown later interactive and higher quality." + "If you are using a version of Jupyter Notebooks, it is recommended to run the following lines of code to make plots which will be shown later interactive and/or higher quality." ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ + "# Using Google Colab? Uncomment the following line:\n", "%config InlineBackend.figure_formats = ['svg']\n", - "%matplotlib inline" + "%matplotlib inline\n", + "\n", + "# Using Jupyter Notebook/Lab or VSCode? Uncomment the following line:\n", + "# %matplotlib widget" ] }, { @@ -100,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -114,12 +118,12 @@ "metadata": {}, "source": [ "To get weather data from the GFS forecast, available online, we run the following lines.\n", - "See \"environment_class_usage.ipynb\" for more information on how to use the Environment class." + "See [Environment Class Usage](environment_class_usage.ipynb) for more information on how to use the Environment class." ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -135,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -145,7 +149,7 @@ "Launch Site Details\n", "\n", "Launch Rail Length: 5.2 m\n", - "Launch Date: 2022-10-04 12:00:00 UTC\n", + "Launch Date: 2022-10-06 12:00:00 UTC\n", "Launch Site Latitude: 32.99025°\n", "Launch Site Longitude: -106.97500°\n", "Reference Datum: SIRGAS2000\n", @@ -158,7 +162,7 @@ "\n", "Atmospheric Model Type: Forecast\n", "Forecast Maximum Height: 8.000 km\n", - "Forecast Time Period: From 2022-10-02 18:00:00 to 2022-10-18 18:00:00 UTC\n", + "Forecast Time Period: From 2022-10-05 00:00:00 to 2022-10-21 00:00:00 UTC\n", "Forecast Hour Interval: 3 hrs\n", "Forecast Latitude Range: From -90.0 ° To 90.0 °\n", "Forecast Longitude Range: From 0.0 ° To 359.75 °\n", @@ -166,13 +170,13 @@ "\n", "Surface Atmospheric Conditions\n", "\n", - "Surface Wind Speed: 1.17 m/s\n", - "Surface Wind Direction: 12.92°\n", - "Surface Wind Heading: 192.92°\n", - "Surface Pressure: 854.38 hPa\n", - "Surface Temperature: 290.45 K\n", - "Surface Air Density: 1.025 kg/m³\n", - "Surface Speed of Sound: 341.65 m/s\n", + "Surface Wind Speed: 3.53 m/s\n", + "Surface Wind Direction: 9.63°\n", + "Surface Wind Heading: 189.63°\n", + "Surface Pressure: 858.08 hPa\n", + "Surface Temperature: 287.52 K\n", + "Surface Air Density: 1.040 kg/m³\n", + "Surface Speed of Sound: 339.92 m/s\n", "\n", "\n", "Atmospheric Model Plots\n" @@ -180,7 +184,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-03T01:44:53.202869\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:00:32.147998\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -199,12 +203,12 @@ "source": [ "### Creating a Motor\n", "\n", - "A solid rocket motor is used in this case. See \"solid_motor_class_usage.ipynb\" for more information on how to use the Motor class." + "A solid rocket motor is used in this case. See [Solid Motor Class Usage](solid_motor_class_usage.ipynb) for more information on how to use the Motor class." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -225,7 +229,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -246,7 +250,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-03T01:44:54.793738\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:00:32.756529\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -263,30 +267,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Simulating the first Flight stage" + "## Simulating the First Flight Stage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Let's start to simulate our rocket flight. We will use the Environment and Motor\n", - "objects we created before.\n", + "Let's start to simulate our rocket's flight. We will use the Environment and Motor objects we created before.\n", "\n", - "We will make the hypothesis that the payload will be ejected at the apogee.\n", - "This can be modified if needed." + "We will assume that the payload is be ejected at apogee, however, this can be modified if needed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Start by defining the masses values, always ensuring the are correct before going on." + "We start by defining the value of each relevant mass, ensuring the are correct before continuing." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -302,27 +304,29 @@ ], "source": [ "# 16.241 is the mass of the rocket including the payload but without the propellant\n", - "PayloadMass = 4.5 # in kg\n", + "PayloadMass = 4.5 # in kg\n", "RocketMass = 16.241 - PayloadMass # in kg\n", "\n", "print(\"Rocket dry mass: {:.4} kg (with Payload)\".format(RocketMass + PayloadMass))\n", "print(\"Propellant Mass: {:.4} kg\".format(Pro75M1670.mass(0)))\n", "print(\"Payload Mass: {:.4} kg\".format(PayloadMass))\n", - "print(\"Fully loaded Rocket Mass: {:.4} kg\".format(\n", - " RocketMass + Pro75M1670.mass(0) + PayloadMass)\n", - " )\n" + "print(\n", + " \"Fully loaded Rocket Mass: {:.4} kg\".format(\n", + " RocketMass + Pro75M1670.mass(0) + PayloadMass\n", + " )\n", + ")" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "Rocket1 = Rocket(\n", " motor=Pro75M1670,\n", " radius=127 / 2000,\n", - " mass= RocketMass+PayloadMass,\n", + " mass=RocketMass + PayloadMass,\n", " inertiaI=6.60,\n", " inertiaZ=0.0351,\n", " distanceRocketNozzle=-1.255,\n", @@ -333,7 +337,9 @@ "\n", "Rocket1.setRailButtons([0.2, -0.5])\n", "\n", - "NoseCone_Rocket1 = Rocket1.addNose(length=0.55829, kind=\"vonKarman\", distanceToCM=0.71971)\n", + "NoseCone_Rocket1 = Rocket1.addNose(\n", + " length=0.55829, kind=\"vonKarman\", distanceToCM=0.71971\n", + ")\n", "\n", "FinSet_Rocket1 = Rocket1.addFins(\n", " 4, span=0.100, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956\n", @@ -346,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 50, "metadata": {}, "outputs": [ { @@ -369,7 +375,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-03T01:44:55.991112\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:32.265013\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -384,24 +390,20 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "RocketFlight1 = Flight(\n", - " rocket=Rocket1, \n", - " environment=Env, \n", - " inclination=85, \n", - " heading=0, \n", - " terminateOnApogee=True\n", - " )" + " rocket=Rocket1, environment=Env, inclination=85, heading=25, terminateOnApogee=True\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Start the second Flight" + "## Start the Second Flight Stage" ] }, { @@ -410,19 +412,19 @@ "source": [ "Now we will simulate the second flight stage, which is the landing phase of our Rocket.\n", "Here we will consider that the payload was ejected at the apogee of the first stage.\n", - "Therefore we should be careful with the masses values." + "Therefore we should be careful with the value of its mass." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "Rocket2 = Rocket(\n", " motor=Pro75M1670, # This motor will not be used\n", " radius=127 / 2000,\n", - " mass= RocketMass,\n", + " mass=RocketMass,\n", " inertiaI=6.60,\n", " inertiaZ=0.0351,\n", " distanceRocketNozzle=-1.255,\n", @@ -431,6 +433,7 @@ " powerOnDrag=1,\n", ")\n", "\n", + "\n", "def drogueTrigger(p, y):\n", " # p = pressure\n", " # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3]\n", @@ -444,10 +447,11 @@ " # activate main when vz < 0 m/s and z < 800 + 1400 m (+1400 due to surface elevation).\n", " return True if y[5] < 0 and y[2] < 800 + 1400 else False\n", "\n", + "\n", "# Define Parachutes for the rocket\n", "Main_Rocket2 = Rocket2.addParachute(\n", " \"Main\",\n", - " CdS= 7.2,\n", + " CdS=7.2,\n", " trigger=mainTrigger,\n", " samplingRate=105,\n", " lag=1.5,\n", @@ -466,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 53, "metadata": {}, "outputs": [ { @@ -495,7 +499,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-03T01:44:58.348221\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:34.682136\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -512,30 +516,30 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The magic line `initialSolution=RocketFlight1,` will make the simulation start from the end of the first stage." + "The magic line `initialSolution=RocketFlight1` will make the simulation start from the end of the first stage." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "RocketFlight2 = Flight(\n", - " rocket=Rocket2,\n", - " environment=Env,\n", - " inclination=0,\n", - " heading=0,\n", - " maxTime=600,\n", - " initialSolution=RocketFlight1,\n", - " )" + " rocket=Rocket2,\n", + " environment=Env,\n", + " inclination=0,\n", + " heading=0,\n", + " maxTime=600,\n", + " initialSolution=RocketFlight1,\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Simulating the 3 flight stage - Payload Flight" + "## Simulating the 3 Flight Stage - Payload Flight" ] }, { @@ -543,12 +547,14 @@ "metadata": {}, "source": [ "Here we will simulate the payload flight, which is the third flight stage of our Rocket.\n", - "The Payload will be ejected at the apogee of the first stage." + "The Payload will be ejected at the apogee of the first stage.\n", + "Here, it will be modelled as a \"dummy\" rocket, which does not have any aerodynamic surfaces to stabilize it, neither a motor which ignites.\n", + "It does, however, have parachutes." ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -564,7 +570,8 @@ " distanceRocketPropellant=-0.85704,\n", " powerOffDrag=0.5,\n", " powerOnDrag=0.5,\n", - " )\n", + ")\n", + "\n", "\n", "def drogueTrigger(p, y):\n", " # p = pressure\n", @@ -572,12 +579,14 @@ " # activate drogue when vz < 0 m/s.\n", " return True if y[5] < 0 else False\n", "\n", + "\n", "def mainTrigger(p, y):\n", " # p = pressure\n", " # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3]\n", " # activate main when vz < 0 m/s and z < 800 + 1400 m (+1400 due to surface elevation).\n", " return True if y[5] < 0 and y[2] < 800 + 1400 else False\n", "\n", + "\n", "PayloadDrogue = PayloadRocket.addParachute(\n", " \"Drogue\",\n", " CdS=0.35,\n", @@ -589,7 +598,7 @@ "\n", "PayloadMain = PayloadRocket.addParachute(\n", " \"Main\",\n", - " CdS= 4.0,\n", + " CdS=4.0,\n", " trigger=mainTrigger,\n", " samplingRate=105,\n", " lag=1.5,\n", @@ -601,22 +610,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The magic line `initialSolution=RocketFlight1,` will make the simulation start from the end of the first stage." + "The magic line `initialSolution=RocketFlight1` will make the simulation start from the end of the first stage." ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ - "PayloadFlight = Flight(rocket=PayloadRocket,\n", - " environment=Env,\n", - " inclination=0,\n", - " heading=0,\n", - " maxTime=600,\n", - " initialSolution=RocketFlight1,\n", - " )" + "PayloadFlight = Flight(\n", + " rocket=PayloadRocket,\n", + " environment=Env,\n", + " inclination=0,\n", + " heading=0,\n", + " maxTime=600,\n", + " initialSolution=RocketFlight1,\n", + ")" ] }, { @@ -636,12 +646,12 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 57, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-03T01:45:05.801473\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:44.814876\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -654,28 +664,7 @@ "utilities.compareFlightTrajectories(\n", " flight_list=[RocketFlight1, RocketFlight2, PayloadFlight],\n", " names=[\"Rocket - 1st Stage\", \"Rocket - 2nd Stage\", \"Payload Flight\"],\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Investigating the Results" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [], - "source": [ - "# TODO: Check why the RocketFlight2.allInfo() is not working\n", - "# TODO: Check why the RocketFlight2.plotAngularKinematicsData() is not working properly\n", - "\n", - "# TODO: Understand wether we can run Monte Carlo simulations in this case or not.\n", - "# TODO: Should we define a Payload Class?\n", - "# TODO: Move Everything together from this notebook inside the utilities module? This would make things easier to use, but I have to think" + ")" ] } ], diff --git a/docs/requirements.txt b/docs/requirements.txt index 2caba5aec..64fbb52dc 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ nbsphinx>=0.8.0 -pydata-sphinx-theme==0.6.3 +pydata-sphinx-theme==0.10.1 m2r2>=0.2.1 diff --git a/docs/user/index.rst b/docs/user/index.rst index 0d0dbad82..867ecaac3 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -10,7 +10,9 @@ Welcome to RocketPy's user documentation! ../notebooks/getting_started.ipynb ../notebooks/environment_class_usage.ipynb ../notebooks/environment_analysis_class_usage.ipynb + ../notebooks/solid_motor_class_usage.ipynb ../notebooks/dispersion_analysis/dispersion_analysis.ipynb ../notebooks/utilities_usage.ipynb + ../notebooks/deployable_payload_example.ipynb ../matlab/matlab.rst diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index 36e8d5f43..b2c3a7bd7 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -756,6 +756,10 @@ def __init__( while phase.solver.status == "running": # Step phase.solver.step() + # Normalize quaternions + phase.solver.y[6:10] = phase.solver.y[6:10] / sum( + phase.solver.y[6:10] ** 2 + ) # Save step result self.solution += [[phase.solver.t, *phase.solver.y]] # Step step metrics @@ -1059,18 +1063,8 @@ def __init_post_process_variables(self): """Initialize post-process variables.""" # Initialize all variables created during Flight.postProcess() # Important to do so that MATLAB® can access them - self.windVelocityX = Function( - 0, - inputs="Time (s)", - outputs="Wind Velocity X (m/s)", - interpolation="linear", - ) - self.windVelocityY = Function( - 0, - inputs="Time (s)", - outputs="Wind Velocity Y (m/s)", - interpolation="linear", - ) + self.windVelocityX = Function(0) + self.windVelocityY = Function(0) self.density = Function(0) self.pressure = Function(0) self.dynamicViscosity = Function(0) @@ -1632,65 +1626,6 @@ def uDotParachute(self, t, u, postProcessing=False): return [vx, vy, vz, ax, ay, az, 0, 0, 0, 0, 0, 0, 0] - def __initialize_quaternion_functions(self, interpolation, extrapolation): - """# Check when e0, e1, e2, e3, is outside the valid (-1, 1) - # Created to deal with numerical error problems, which implied in - # breaking errors when defining self.theta and others. - To be used inside postProcess method. - - Parameters - ---------- - interpolation: string - Interpolation method to be used in the Function objects. - extrapolation: string - Extrapolation method to be used in the Function objects. - - Return - ------- - None - """ - - grid = self.x[:, 0] - e0 = np.fmin(np.array(self.solution)[:, 7], np.ones(len(grid))) - e0 = np.fmax(e0, -1 * np.ones(len(e0))) - e1 = np.fmin(np.array(self.solution)[:, 8], np.ones(len(grid))) - e1 = np.fmax(e1, -1 * np.ones(len(e1))) - e2 = np.fmin(np.array(self.solution)[:, 9], np.ones(len(grid))) - e2 = np.fmax(e2, -1 * np.ones(len(e2))) - e3 = np.fmin(np.array(self.solution)[:, 10], np.ones(len(grid))) - e3 = np.fmax(e3, -1 * np.ones(len(e3))) - - self.e0 = Function( - np.column_stack((grid, e0)), - "Time (s)", - "e0", - interpolation, - extrapolation, - ) - self.e1 = Function( - np.column_stack((grid, e1)), - "Time (s)", - "e1", - interpolation, - extrapolation, - ) - self.e2 = Function( - np.column_stack((grid, e2)), - "Time (s)", - "e2", - interpolation, - extrapolation, - ) - self.e3 = Function( - np.column_stack((grid, e3)), - "Time (s)", - "e3", - interpolation, - extrapolation, - ) - - return None - def postProcess(self, interpolation="spline", extrapolation="natural"): """Post-process all Flight information produced during simulation. Includes the calculation of maximum values, @@ -1703,6 +1638,7 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): Interpolation method to be used in the Function objects. extrapolation: string Extrapolation method to be used in the Function objects. + Return ------ None @@ -1728,7 +1664,18 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): self.vz = Function( sol[:, [0, 6]], "Time (s)", "Vz (m/s)", interpolation, extrapolation ) - self.__initialize_quaternion_functions(interpolation, extrapolation) + self.e0 = Function( + sol[:, [0, 7]], "Time (s)", "e0", interpolation, extrapolation + ) + self.e1 = Function( + sol[:, [0, 8]], "Time (s)", "e1", interpolation, extrapolation + ) + self.e2 = Function( + sol[:, [0, 9]], "Time (s)", "e2", interpolation, extrapolation + ) + self.e3 = Function( + sol[:, [0, 10]], "Time (s)", "e3", interpolation, extrapolation + ) self.w1 = Function( sol[:, [0, 11]], "Time (s)", "ω1 (rad/s)", interpolation, extrapolation ) @@ -1754,7 +1701,9 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): callback(self) # Loop through time steps in flight phase for step in self.solution: # Can be optimized - if initTime < step[0] <= finalTime: + if initTime < step[0] <= finalTime or ( + initTime == self.tInitial and step[0] == self.tInitial + ): # Get derivatives uDot = currentDerivative(step[0], step[1:]) # Get accelerations @@ -1796,7 +1745,9 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): callback(self) # Loop through time steps in flight phase for step in self.solution: # Can be optimized - if initTime < step[0] <= finalTime or (initTime == 0 and step[0] == 0): + if initTime < step[0] <= finalTime or ( + initTime == self.tInitial and step[0] == self.tInitial + ): # Call derivatives in post processing mode uDot = currentDerivative(step[0], step[1:], postProcessing=True) # Convert forces and atmospheric arrays to functions @@ -1831,46 +1782,6 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): self.speedOfSound, "Time (s)", "Speed of Sound (m/s)", interpolation ) - # Redefine grids for the atmospheric functions - # Important to ensure the code works properly when using initialSolution - grid = self.vx[:, 0] - self.windVelocityX = Function( - np.column_stack([grid, self.windVelocityX(grid)]), - "Time (s)", - "Wind Velocity X (East) (m/s)", - interpolation, - ) - self.windVelocityY = Function( - np.column_stack([grid, self.windVelocityY(grid)]), - "Time (s)", - "Wind Velocity Y (North) (m/s)", - interpolation, - ) - self.density = Function( - np.column_stack([grid, self.density(grid)]), - "Time (s)", - "Density (kg/m³)", - interpolation, - ) - self.pressure = Function( - np.column_stack([grid, self.pressure(grid)]), - "Time (s)", - "Pressure (Pa)", - interpolation, - ) - self.dynamicViscosity = Function( - np.column_stack([grid, self.dynamicViscosity(grid)]), - "Time (s)", - "Dynamic Viscosity (Pa s)", - interpolation, - ) - self.speedOfSound = Function( - np.column_stack([grid, self.speedOfSound(grid)]), - "Time (s)", - "Speed of Sound (m/s)", - interpolation, - ) - # Process fourth type of output - values calculated from previous outputs # Kinematics functions and values @@ -1957,7 +1868,9 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): theta = ( (180 / np.pi) * 2 - * np.arcsin(-((self.e1[:, 1] ** 2 + self.e2[:, 1] ** 2) ** 0.5)) + * np.arcsin( + np.clip(-((self.e1[:, 1] ** 2 + self.e2[:, 1] ** 2) ** 0.5), -1, 1) + ) ) # Nutation angle theta = np.column_stack([self.e1[:, 0], theta]) # Nutation angle self.theta = Function( @@ -2193,10 +2106,8 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): # Fluid Mechanics variables # Freestream Velocity self.streamVelocityX = self.windVelocityX - self.vx - self.streamVelocityX.setInputs("Time (s)") self.streamVelocityX.setOutputs("Freestream Velocity X (m/s)") self.streamVelocityY = self.windVelocityY - self.vy - self.streamVelocityY.setInputs("Time (s)") self.streamVelocityY.setOutputs("Freestream Velocity Y (m/s)") self.streamVelocityZ = -1 * self.vz self.streamVelocityZ.setOutputs("Freestream Velocity Z (m/s)") @@ -2206,13 +2117,11 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): + self.streamVelocityZ**2 ) ** 0.5 self.freestreamSpeed.setOutputs("Freestream Speed (m/s)") - self.freestreamSpeed.setInputs("Time (s)") # Apogee Freestream speed self.apogeeFreestreamSpeed = self.freestreamSpeed(self.apogeeTime) # Mach Number self.MachNumber = self.freestreamSpeed / self.speedOfSound self.MachNumber.setOutputs("Mach Number") - self.MachNumber.setInputs("Time (s)") maxMachNumberTimeIndex = np.argmax(self.MachNumber[:, 1]) self.maxMachNumberTime = self.MachNumber[maxMachNumberTimeIndex, 0] self.maxMachNumber = self.MachNumber[maxMachNumberTimeIndex, 1] diff --git a/tests/test_flight.py b/tests/test_flight.py index 71e51b3e4..b08763339 100644 --- a/tests/test_flight.py +++ b/tests/test_flight.py @@ -249,9 +249,9 @@ def mainTrigger(p, y): 10, 0.0, 0.0, - 0.0, - 0.0, - 0.0, + 0.9990482215818579, + -0.043619387365336, + -0.0, 0.0, 0.0, 0.0, From f929db2c09d9b20be7875bbf412f4fcaa6946146 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 14:13:44 +0200 Subject: [PATCH 04/14] ENH: 2nd part of comparison functions --- rocketpy/plots/flight_plots.py | 351 +++++++++++++++++++++++++++++++-- 1 file changed, 331 insertions(+), 20 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index d48e0d46a..569015f2b 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -1284,9 +1284,11 @@ def comparePositions(self): ) max_time = flight.tFinal if flight.tFinal > max_time else max_time ax1.set_xlim(0, max_time) - ax1.set_title("X (m) x Time (s)") - ax1.set_xlabel("Time (s)") - ax1.set_ylabel("X (m)") + ax1.set_title( + "{} x {}".format(flight.x.getOutputs()[0], flight.x.getInputs()[0]) + ) + ax1.set_xlabel(flight.x.getInputs()[0]) + ax1.set_ylabel(flight.x.getOutputs()[0]) ax1.grid(True) ax2 = plt.subplot(313) @@ -1297,9 +1299,11 @@ def comparePositions(self): # color=self.colors_scale[index], ) ax2.set_xlim(0, max_time) - ax2.set_title("Y (m) x Time (s)") - ax2.set_xlabel("Time (s)") - ax2.set_ylabel("Y (m)") + ax2.set_title( + "{} x {}".format(flight.y.getOutputs()[0], flight.y.getInputs()[0]) + ) + ax2.set_xlabel(flight.y.getInputs()[0]) + ax2.set_ylabel(flight.y.getOutputs()[0]) ax2.grid(True) ax3 = plt.subplot(311) @@ -1311,9 +1315,11 @@ def comparePositions(self): # color=self.colors_scale[index], ) ax3.set_xlim(0, max_time) - ax3.set_title("Z (m) x Time (s)") - ax3.set_xlabel("Time (s)") - ax3.set_ylabel("Z (m)") + ax3.set_title( + "{} x {}".format(flight.z.getOutputs()[0], flight.z.getInputs()[0]) + ) + ax3.set_xlabel(flight.z.getInputs()[0]) + ax3.set_ylabel(flight.z.getInputs()[0]) ax3.grid(True) fig.legend( @@ -1351,8 +1357,10 @@ def compareVelocities(self): ) max_time = flight.tFinal if flight.tFinal > max_time else max_time ax1.set_xlim(0, max_time) - ax1.set_title("Velocity X (m/s) x Time (s)") - ax1.set_xlabel(flight.vy.getInputs()[0]) + ax1.set_title( + "{} x {}".format(flight.vx.getOutputs()[0], flight.vx.getInputs()[0]) + ) + ax1.set_xlabel(flight.vx.getInputs()[0]) ax1.set_ylabel(flight.vx.getOutputs()[0]) ax1.grid(True) @@ -1364,7 +1372,9 @@ def compareVelocities(self): # color=self.colors_scale[index], ) ax2.set_xlim(0, max_time) - ax2.set_title("Velocity Y (m/s) x Time (s)") + ax2.set_title( + "{} x {}".format(flight.vy.getOutputs()[0], flight.vy.getInputs()[0]) + ) ax2.set_xlabel(flight.vy.getInputs()[0]) ax2.set_ylabel(flight.vy.getOutputs()[0]) ax2.grid(True) @@ -1378,14 +1388,18 @@ def compareVelocities(self): # color=self.colors_scale[index], ) ax3.set_xlim(0, max_time) - ax3.set_title("Velocity Z (m/s) x Time (s)") + ax3.set_title( + "{} x {}".format(flight.vz.getOutputs()[0], flight.vz.getInputs()[0]) + ) ax3.set_xlabel(flight.vz.getInputs()[0]) ax3.set_ylabel(flight.vz.getOutputs()[0]) ax3.grid(True) fig.legend( loc="upper center", - ncol=len(self.names_list), # TODO: Need to be more flexible here, changing the number of rows as well + ncol=len( + self.names_list + ), # TODO: Need to be more flexible here, changing the number of rows as well fancybox=True, shadow=True, fontsize=10, @@ -1418,7 +1432,9 @@ def compareAccelerations(self): ) max_time = flight.tFinal if flight.tFinal > max_time else max_time ax1.set_xlim(0, max_time) - ax1.set_title("Acceleration X (m/s²) x Time (s)") + ax1.set_title( + "{} x {}".format(flight.ax.getOutputs()[0], flight.ax.getInputs()[0]) + ) ax1.set_xlabel(flight.ax.getInputs()[0]) ax1.set_ylabel(flight.ax.getOutputs()[0]) ax1.grid(True) @@ -1431,7 +1447,9 @@ def compareAccelerations(self): # color=self.colors_scale[index], ) ax2.set_xlim(0, max_time) - ax2.set_title("Acceleration Y (m/s²) x Time (s)") + ax2.set_title( + "{} x {}".format(flight.ay.getOutputs()[0], flight.ay.getInputs()[0]) + ) ax2.set_xlabel(flight.ay.getInputs()[0]) ax2.set_ylabel(flight.ay.getOutputs()[0]) ax2.grid(True) @@ -1445,14 +1463,18 @@ def compareAccelerations(self): # color=self.colors_scale[index], ) ax3.set_xlim(0, max_time) - ax3.set_title("Acceleration Z (m/s²) x Time (s)") + ax3.set_title( + "{} x {}".format(flight.az.getOutputs()[0], flight.az.getInputs()[0]) + ) ax3.set_xlabel(flight.vz.getInputs()[0]) ax3.set_ylabel(flight.vz.getOutputs()[0]) ax3.grid(True) fig.legend( loc="upper center", - ncol=len(self.names_list), # TODO: Need to be more flexible here, changing the number of rows as well + ncol=len( + self.names_list + ), # TODO: Need to be more flexible here, changing the number of rows as well fancybox=True, shadow=True, fontsize=10, @@ -1463,18 +1485,309 @@ def compareAccelerations(self): return None def compareEulerAngles(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Euler Angles Comparison", fontsize=16, y=1.02, x=0.5) + + ax1 = plt.subplot(311) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.phi[:, 0], + flight.phi[:, 1], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format(flight.phi.getOutputs()[0], flight.phi.getInputs()[0]) + ) + ax1.set_xlabel(flight.phi.getInputs()[0]) + ax1.set_ylabel(flight.phi.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(312) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.theta[:, 0], + flight.theta[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format(flight.theta.getOutputs()[0], flight.theta.getInputs()[0]) + ) + ax2.set_xlabel(flight.theta.getInputs()[0]) + ax2.set_ylabel(flight.theta.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.psi[:, 0], + flight.psi[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format(flight.psi.getOutputs()[0], flight.psi.getInputs()[0]) + ) + ax3.set_xlabel(flight.psi.getInputs()[0]) + ax3.set_ylabel(flight.psi.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 0.995), + ) + fig.tight_layout() return None def compareQuaternions(self): + fig = plt.figure(figsize=(10, 20 / 3)) # width, height + fig.suptitle("Quaternions Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(221) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.e0[:, 0], + flight.e0[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format(flight.e0.getOutputs()[0], flight.e0.getInputs()[0]) + ) + ax1.set_xlabel(flight.e0.getInputs()[0]) + ax1.set_ylabel(flight.e0.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(222) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.e1[:, 0], + flight.e1[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format(flight.e1.getOutputs()[0], flight.e1.getInputs()[0]) + ) + ax2.set_xlabel(flight.e1.getInputs()[0]) + ax2.set_ylabel(flight.e1.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(223) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.e2[:, 0], + flight.e2[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format(flight.e2.getOutputs()[0], flight.e2.getInputs()[0]) + ) + ax3.set_xlabel(flight.e2.getInputs()[0]) + ax3.set_ylabel(flight.e2.getOutputs()[0]) + ax3.grid(True) + + ax4 = plt.subplot(224) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.e3[:, 0], + flight.e3[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format(flight.e3.getOutputs()[0], flight.e3.getInputs()[0]) + ) + ax4.set_xlabel(flight.e3.getInputs()[0]) + ax4.set_ylabel(flight.e3.getOutputs()[0]) + ax4.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + return None def compareAngularVelocities(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Angular Velocities Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(311) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.w1[:, 0], + flight.w1[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format(flight.w1.getOutputs()[0], flight.w1.getInputs()[0]) + ) + ax1.set_xlabel(flight.w1.getInputs()[0]) + ax1.set_ylabel(flight.w1.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(312) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.w2[:, 0], + flight.w2[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format(flight.w2.getOutputs()[0], flight.w2.getInputs()[0]) + ) + ax2.set_xlabel(flight.w2.getInputs()[0]) + ax2.set_ylabel(flight.w2.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.w3[:, 0], + flight.w3[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format(flight.w3.getOutputs()[0], flight.w3.getInputs()[0]) + ) + ax3.set_xlabel(flight.w3.getInputs()[0]) + ax3.set_ylabel(flight.w3.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() return None def compareAngularAccelerations(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Angular Accelerations Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(311) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.alpha1[:, 0], + flight.alpha1[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.alpha1.getOutputs()[0], flight.alpha1.getInputs()[0] + ) + ) + ax1.set_xlabel(flight.alpha1.getInputs()[0]) + ax1.set_ylabel(flight.alpha1.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(312) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.alpha2[:, 0], + flight.alpha2[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.alpha2.getOutputs()[0], flight.alpha2.getInputs()[0] + ) + ) + ax2.set_xlabel(flight.alpha2.getInputs()[0]) + ax2.set_ylabel(flight.alpha2.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.alpha3[:, 0], + flight.alpha3[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.alpha3.getOutputs()[0], flight.alpha3.getInputs()[0] + ) + ) + ax3.set_xlabel(flight.alpha3.getInputs()[0]) + ax3.set_ylabel(flight.alpha3.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() return None @@ -1502,8 +1815,6 @@ def compareAttitudeFrequencyResponses(self): return None - # def compare - @staticmethod def compareTrajectories3D(trajectory_list, names_list, legend=None): """Creates a trajectory plot combining the trajectories listed. From fc6a2492b341dfedbb0b26603f543eabcf74edf5 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 16:52:43 +0200 Subject: [PATCH 05/14] ENH: Add 3rd part of compare Functions --- rocketpy/Flight.py | 3 +- rocketpy/plots/flight_plots.py | 773 ++++++++++++++++++++++++++++++++- 2 files changed, 759 insertions(+), 17 deletions(-) diff --git a/rocketpy/Flight.py b/rocketpy/Flight.py index b2c3a7bd7..8da8da6ed 100644 --- a/rocketpy/Flight.py +++ b/rocketpy/Flight.py @@ -261,7 +261,7 @@ class Flight: Rocket's velocity magnitude in the horizontal (North-East) plane in m/s as a function of time. Can be called or accessed as array. - Flight.Acceleration : Function + Flight.acceleration : Function Rocket acceleration magnitude in m/s² relative to ground as a function of time. Can be called or accessed as array. Flight.maxAcceleration : float @@ -2000,6 +2000,7 @@ def postProcess(self, interpolation="spline", extrapolation="natural"): # Potential Energy self.potentialEnergy = totalMass * self.env.g * self.z self.potentialEnergy.setInputs("Time (s)") + self.potentialEnergy.setOutputs("Potential Energy (J)") # Total Mechanical Energy self.totalEnergy = self.kineticEnergy + self.potentialEnergy self.totalEnergy.setOutputs("Total Mechanical Energy (J)") diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 569015f2b..41bcdee62 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -19,7 +19,7 @@ # TODO: Add unit tests -# Major obs.: MAYBE I should call Function to do comparison plots, and improving colors functionality directly inside function +# Major obs.: MAYBE I should call Function to do comparison plots, and improving colors functionality directly inside Function.py class flight_plots: @@ -1345,7 +1345,7 @@ def compareVelocities(self): fig = plt.figure(figsize=(7, 10)) # width, height fig.suptitle("Rocket Velocity Comparison", fontsize=16, y=1.02, x=0.5) - ax1 = plt.subplot(312) + ax1 = plt.subplot(412) max_time = 0 for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: @@ -1364,7 +1364,7 @@ def compareVelocities(self): ax1.set_ylabel(flight.vx.getOutputs()[0]) ax1.grid(True) - ax2 = plt.subplot(313) + ax2 = plt.subplot(413) for index, flight in enumerate(self.trajectory_list): ax2.plot( flight.vy[:, 0], @@ -1379,7 +1379,7 @@ def compareVelocities(self): ax2.set_ylabel(flight.vy.getOutputs()[0]) ax2.grid(True) - ax3 = plt.subplot(311) + ax3 = plt.subplot(414) for index, flight in enumerate(self.trajectory_list): ax3.plot( flight.vz[:, 0], @@ -1395,6 +1395,21 @@ def compareVelocities(self): ax3.set_ylabel(flight.vz.getOutputs()[0]) ax3.grid(True) + ax4 = plt.subplot(411) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.speed[:, 0], + flight.speed[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format(flight.speed.getOutputs()[0], flight.speed.getInputs()[0]) + ) + ax4.set_xlabel(flight.speed.getInputs()[0]) + ax4.set_ylabel(flight.speed.getOutputs()[0]) + ax4.grid(True) + fig.legend( loc="upper center", ncol=len( @@ -1409,6 +1424,119 @@ def compareVelocities(self): return None + def compareStreamVelocities(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle( + "Rocket Freestream Velocity Comparison", fontsize=16, y=1.02, x=0.5 + ) + + ax1 = plt.subplot(411) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.freestreamSpeed[:, 0], + flight.freestreamSpeed[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.freestreamSpeed.getOutputs()[0], + flight.freestreamSpeed.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.freestreamSpeed.getInputs()[0]) + ax1.set_ylabel(flight.freestreamSpeed.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(412) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.streamVelocityX[:, 0], + flight.streamVelocityX[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.streamVelocityX.getOutputs()[0], + flight.streamVelocityX.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.streamVelocityX.getInputs()[0]) + ax2.set_ylabel(flight.streamVelocityX.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(413) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.streamVelocityY[:, 0], + flight.streamVelocityY[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.streamVelocityY.getOutputs()[0], + flight.streamVelocityY.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.streamVelocityY.getInputs()[0]) + ax3.set_ylabel(flight.streamVelocityY.getOutputs()[0]) + ax3.grid(True) + + ax4 = plt.subplot(414) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.streamVelocityZ[:, 0], + flight.streamVelocityZ[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format( + flight.streamVelocityZ.getOutputs()[0], + flight.streamVelocityZ.getInputs()[0], + ) + ) + ax4.set_xlabel(flight.streamVelocityZ.getInputs()[0]) + ax4.set_ylabel(flight.streamVelocityZ.getOutputs()[0]) + ax4.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 0.995), + ) + # TODO: Create a option to insert watermark or not, including RocketPy logo + # fig.text( + # x=0.8, + # y=0, + # s="created with RocketPy", + # fontsize=10, + # color="black", + # alpha=1, + # ha="center", + # va="center", + # rotation=0, + # ) + fig.tight_layout() + return None + def compareAccelerations(self): """_summary_ @@ -1420,7 +1548,7 @@ def compareAccelerations(self): fig = plt.figure(figsize=(7, 10)) # width, height fig.suptitle("Rocket Acceleration Comparison", fontsize=16, y=1.02, x=0.5) - ax1 = plt.subplot(312) + ax1 = plt.subplot(412) max_time = 0 for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: @@ -1439,7 +1567,7 @@ def compareAccelerations(self): ax1.set_ylabel(flight.ax.getOutputs()[0]) ax1.grid(True) - ax2 = plt.subplot(313) + ax2 = plt.subplot(413) for index, flight in enumerate(self.trajectory_list): ax2.plot( flight.vy[:, 0], @@ -1454,7 +1582,7 @@ def compareAccelerations(self): ax2.set_ylabel(flight.ay.getOutputs()[0]) ax2.grid(True) - ax3 = plt.subplot(311) + ax3 = plt.subplot(414) for index, flight in enumerate(self.trajectory_list): ax3.plot( flight.vz[:, 0], @@ -1470,6 +1598,23 @@ def compareAccelerations(self): ax3.set_ylabel(flight.vz.getOutputs()[0]) ax3.grid(True) + ax4 = plt.subplot(411) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.acceleration[:, 0], + flight.acceleration[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format( + flight.acceleration.getOutputs()[0], flight.acceleration.getInputs()[0] + ) + ) + ax4.set_xlabel(flight.acceleration.getInputs()[0]) + ax4.set_ylabel(flight.acceleration.getOutputs()[0]) + ax4.grid(True) + fig.legend( loc="upper center", ncol=len( @@ -1639,6 +1784,89 @@ def compareQuaternions(self): return None + def compareAttitudeAngles(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + + fig = plt.figure(figsize=(7, 10)) # width, height + fig.suptitle("Attitude Angles Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(311) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.pathAngle[:, 0], + flight.pathAngle[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.pathAngle.getOutputs()[0], flight.pathAngle.getInputs()[0] + ) + ) + ax1.set_xlabel(flight.pathAngle.getInputs()[0]) + ax1.set_ylabel(flight.pathAngle.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(312) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.attitudeAngle[:, 0], + flight.attitudeAngle[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.attitudeAngle.getOutputs()[0], + flight.attitudeAngle.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.attitudeAngle.getInputs()[0]) + ax2.set_ylabel(flight.attitudeAngle.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(313) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.lateralAttitudeAngle[:, 0], + flight.lateralAttitudeAngle[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.lateralAttitudeAngle.getOutputs()[0], + flight.lateralAttitudeAngle.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.lateralAttitudeAngle.getInputs()[0]) + ax3.set_ylabel(flight.lateralAttitudeAngle.getOutputs()[0]) + ax3.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + def compareAngularVelocities(self): """_summary_ @@ -1792,29 +2020,542 @@ def compareAngularAccelerations(self): return None def compareForces(self): + """_summary_ - return None + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 20 / 3)) # width, height + fig.suptitle("Aerodynamic Forces Comparison", fontsize=16, y=1.06, x=0.5) - def compareMoments(self): + ax1 = plt.subplot(211) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.aerodynamicDrag[:, 0], + flight.aerodynamicDrag[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.aerodynamicDrag.getOutputs()[0], + flight.aerodynamicDrag.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.aerodynamicDrag.getInputs()[0]) + ax1.set_ylabel(flight.aerodynamicDrag.getOutputs()[0]) + ax1.grid(True) - return None + ax2 = plt.subplot(212) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.aerodynamicLift[:, 0], + flight.aerodynamicLift[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.aerodynamicLift.getOutputs()[0], + flight.aerodynamicLift.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.aerodynamicLift.getInputs()[0]) + ax2.set_ylabel(flight.aerodynamicLift.getOutputs()[0]) + ax2.grid(True) - def compareRailButtonsForces(self): + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() return None - def compareAnglesOfAttack(self): + def compareMoments(self): + """_summary_ - return None + Returns + ------- + None + """ + fig = plt.figure(figsize=(7, 20 / 3)) # width, height + fig.suptitle("Aerodynamic Moments Comparison", fontsize=16, y=1.06, x=0.5) - def compareStaticMargins(self): + ax1 = plt.subplot(211) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.aerodynamicBendingMoment[:, 0], + flight.aerodynamicBendingMoment[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.aerodynamicBendingMoment.getOutputs()[0], + flight.aerodynamicBendingMoment.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.aerodynamicBendingMoment.getInputs()[0]) + ax1.set_ylabel(flight.aerodynamicBendingMoment.getOutputs()[0]) + ax1.grid(True) - return None + ax2 = plt.subplot(212) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.aerodynamicSpinMoment[:, 0], + flight.aerodynamicSpinMoment[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.aerodynamicSpinMoment.getOutputs()[0], + flight.aerodynamicSpinMoment.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.aerodynamicSpinMoment.getInputs()[0]) + ax2.set_ylabel(flight.aerodynamicSpinMoment.getOutputs()[0]) + ax2.grid(True) - def compareAttitudeFrequencyResponses(self): + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() return None + def compareEnergies(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 50 / 3)) # width, height + fig.suptitle("Energies Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(511) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.kineticEnergy[:, 0], + flight.kineticEnergy[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.kineticEnergy.getOutputs()[0], + flight.kineticEnergy.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.kineticEnergy.getInputs()[0]) + ax1.set_ylabel(flight.kineticEnergy.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(512) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.rotationalEnergy[:, 0], + flight.rotationalEnergy[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.rotationalEnergy.getOutputs()[0], + flight.rotationalEnergy.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.rotationalEnergy.getInputs()[0]) + ax2.set_ylabel(flight.rotationalEnergy.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(513) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.translationalEnergy[:, 0], + flight.translationalEnergy[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.translationalEnergy.getOutputs()[0], + flight.translationalEnergy.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.translationalEnergy.getInputs()[0]) + ax3.set_ylabel(flight.translationalEnergy.getOutputs()[0]) + ax3.grid(True) + + ax4 = plt.subplot(514) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.potentialEnergy[:, 0], + flight.potentialEnergy[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format( + flight.potentialEnergy.getOutputs()[0], + flight.potentialEnergy.getInputs()[0], + ) + ) + ax4.set_xlabel(flight.potentialEnergy.getInputs()[0]) + ax4.set_ylabel(flight.potentialEnergy.getOutputs()[0]) + ax4.grid(True) + + ax5 = plt.subplot(515) + for index, flight in enumerate(self.trajectory_list): + ax5.plot( + flight.totalEnergy[:, 0], + flight.totalEnergy[:, 1], + # color=self.colors_scale[index], + ) + ax5.set_xlim(0, max_time) + ax5.set_title( + "{} x {}".format( + flight.totalEnergy.getOutputs()[0], + flight.totalEnergy.getInputs()[0], + ) + ) + ax5.set_xlabel(flight.totalEnergy.getInputs()[0]) + ax5.set_ylabel(flight.totalEnergy.getOutputs()[0]) + ax5.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + + def comparePowers(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 20 / 3)) # width, height + fig.suptitle("Powers Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(211) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.thrustPower[:, 0], + flight.thrustPower[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.thrustPower.getOutputs()[0], + flight.thrustPower.getInputs()[0], + ) + ) + + ax1.set_xlabel(flight.thrustPower.getInputs()[0]) + ax1.set_ylabel(flight.thrustPower.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(212) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.dragPower[:, 0], + flight.dragPower[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.dragPower.getOutputs()[0], + flight.dragPower.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.dragPower.getInputs()[0]) + ax2.set_ylabel(flight.dragPower.getOutputs()[0]) + ax2.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + + def compareRailButtonsForces(self): + """_summary_ + + Returns + ------- + None + """ + fig = plt.figure(figsize=(10, 20 / 3)) # width, height + fig.suptitle("Rail Buttons Forces Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(221) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.railButton1NormalForce[:, 0], + flight.railButton1NormalForce[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.railButton1NormalForce.getOutputs()[0], + flight.railButton1NormalForce.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.railButton1NormalForce.getInputs()[0]) + ax1.set_ylabel(flight.railButton1NormalForce.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(223) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.railButton2NormalForce[:, 0], + flight.railButton2NormalForce[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.railButton2NormalForce.getOutputs()[0], + flight.railButton2NormalForce.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.railButton2NormalForce.getInputs()[0]) + ax2.set_ylabel(flight.railButton2NormalForce.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(222) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.railButton1ShearForce[:, 0], + flight.railButton1ShearForce[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.railButton1ShearForce.getOutputs()[0], + flight.railButton1ShearForce.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.railButton1ShearForce.getInputs()[0]) + ax3.set_ylabel(flight.railButton1ShearForce.getOutputs()[0]) + ax3.grid(True) + + ax4 = plt.subplot(224) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.railButton2ShearForce[:, 0], + flight.railButton2ShearForce[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format( + flight.railButton2ShearForce.getOutputs()[0], + flight.railButton2ShearForce.getInputs()[0], + ) + ) + ax4.set_xlabel(flight.railButton2ShearForce.getInputs()[0]) + ax4.set_ylabel(flight.railButton2ShearForce.getOutputs()[0]) + ax4.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + + def compareAnglesOfAttack(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10 / 3)) # width, height + fig.suptitle("Angles of Attack Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(111) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.angleOfAttack[:, 0], + flight.angleOfAttack[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.angleOfAttack.getOutputs()[0], + flight.angleOfAttack.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.angleOfAttack.getInputs()[0]) + ax1.set_ylabel(flight.angleOfAttack.getOutputs()[0]) + ax1.grid(True) + + # TODO: Maybe simplify this code with the use of a function to add legend + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + + # TODO: Static Margin is not working properly, we need to understand why! + def compareStaticMargins(self): + """_summary_ + + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(7, 10 / 3)) # width, height + fig.suptitle("Static Margins Comparison", fontsize=16, y=1.06, x=0.5) + + ax1 = plt.subplot(111) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.staticMargin[:, 0], + flight.staticMargin[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.staticMargin.getOutputs()[0], + flight.staticMargin.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.staticMargin.getInputs()[0]) + ax1.set_ylabel(flight.staticMargin.getOutputs()[0]) + ax1.grid(True) + + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() + + return None + + def compareFluidMechanics(self): + + return None + + # Flight.MachNumber : Function + + # Flight.ReynoldsNumber : Function + + # Flight.dynamicPressure : Function + + # Flight.totalPressure : Function + + def compareAttitudeFrequencyResponses(self): + + return None + + # Stability and Control: + # Flight.attitudeFrequencyResponse : Function + + # Flight.omega1FrequencyResponse : Function + + # Flight.omega2FrequencyResponse : Function + + # Flight.omega3FrequencyResponse : Function + + def compareFinFlutterAnalysis(self): + # Should only work if the fin flutter analysis was ran before. # TODO: Add boolean! + return None + + # Fin Flutter Analysis: + # Flight.flutterMachNumber: Function + # Flight.difference: Function + # Flight.safetyFactor: Function + @staticmethod def compareTrajectories3D(trajectory_list, names_list, legend=None): """Creates a trajectory plot combining the trajectories listed. From ae4fd7159c71c4bd534124dbf23bfff493521b82 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 17:36:13 +0200 Subject: [PATCH 06/14] ENH: Add final comparison functions --- rocketpy/plots/flight_plots.py | 252 +++++++++++++++++++++++++++++---- 1 file changed, 225 insertions(+), 27 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 41bcdee62..ceb3bcd18 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from cProfile import label import time import warnings @@ -17,7 +16,7 @@ # TODO: Allow user to choose the color pallet of the plots # TODO: Add masses plots since it can vary significantly for multi-stage rockets -# TODO: Add unit tests +# TODO: Add unit tests to verify if the code is working properly # Major obs.: MAYBE I should call Function to do comparison plots, and improving colors functionality directly inside Function.py @@ -2271,7 +2270,7 @@ def comparePowers(self): ------- _type_ _description_ - """ + """ fig = plt.figure(figsize=(7, 20 / 3)) # width, height fig.suptitle("Powers Comparison", fontsize=16, y=1.06, x=0.5) @@ -2435,7 +2434,7 @@ def compareAnglesOfAttack(self): ------- _type_ _description_ - """ + """ fig = plt.figure(figsize=(7, 10 / 3)) # width, height fig.suptitle("Angles of Attack Comparison", fontsize=16, y=1.06, x=0.5) @@ -2483,7 +2482,7 @@ def compareStaticMargins(self): ------- _type_ _description_ - """ + """ fig = plt.figure(figsize=(7, 10 / 3)) # width, height fig.suptitle("Static Margins Comparison", fontsize=16, y=1.06, x=0.5) @@ -2523,39 +2522,212 @@ def compareStaticMargins(self): return None def compareFluidMechanics(self): + """_summary_ - return None + Returns + ------- + None + """ + fig = plt.figure(figsize=(10, 20 / 3)) # width, height + fig.suptitle("Fluid Mechanics Comparison", fontsize=16, y=1.06, x=0.5) - # Flight.MachNumber : Function + ax1 = plt.subplot(221) + max_time = 0 + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.MachNumber[:, 0], + flight.MachNumber[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + max_time = flight.tFinal if flight.tFinal > max_time else max_time + ax1.set_xlim(0, max_time) + ax1.set_title( + "{} x {}".format( + flight.MachNumber.getOutputs()[0], flight.MachNumber.getInputs()[0] + ) + ) + ax1.set_xlabel(flight.MachNumber.getInputs()[0]) + ax1.set_ylabel(flight.MachNumber.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(222) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.ReynoldsNumber[:, 0], + flight.ReynoldsNumber[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_xlim(0, max_time) + ax2.set_title( + "{} x {}".format( + flight.ReynoldsNumber.getOutputs()[0], + flight.ReynoldsNumber.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.ReynoldsNumber.getInputs()[0]) + ax2.set_ylabel(flight.ReynoldsNumber.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(223) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.dynamicPressure[:, 0], + flight.dynamicPressure[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_xlim(0, max_time) + ax3.set_title( + "{} x {}".format( + flight.dynamicPressure.getOutputs()[0], + flight.dynamicPressure.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.dynamicPressure.getInputs()[0]) + ax3.set_ylabel(flight.dynamicPressure.getOutputs()[0]) + ax3.grid(True) - # Flight.ReynoldsNumber : Function + ax4 = plt.subplot(224) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.totalPressure[:, 0], + flight.totalPressure[:, 1], + # color=self.colors_scale[index], + ) + ax4.set_xlim(0, max_time) + ax4.set_title( + "{} x {}".format( + flight.totalPressure.getOutputs()[0], + flight.totalPressure.getInputs()[0], + ) + ) + ax4.set_xlabel(flight.totalPressure.getInputs()[0]) + ax4.set_ylabel(flight.totalPressure.getOutputs()[0]) + ax4.grid(True) - # Flight.dynamicPressure : Function + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) + fig.tight_layout() - # Flight.totalPressure : Function + return None def compareAttitudeFrequencyResponses(self): + """_summary_ - return None + Returns + ------- + _type_ + _description_ + """ + fig = plt.figure(figsize=(10, 20 / 3)) # width, height + fig.suptitle( + "Attitude Frequency Responses Comparison", fontsize=16, y=1.06, x=0.5 + ) + + ax1 = plt.subplot(221) + for index, flight in enumerate(self.trajectory_list): + if flight.postProcessed is False: + flight.postProcess() + ax1.plot( + flight.attitudeFrequencyResponse[:, 0], + flight.attitudeFrequencyResponse[:, 1], + label=self.names_list[index], + # color=self.colors_scale[index], + ) + ax1.set_title( + "{} x {}".format( + flight.attitudeFrequencyResponse.getOutputs()[0], + flight.attitudeFrequencyResponse.getInputs()[0], + ) + ) + ax1.set_xlabel(flight.attitudeFrequencyResponse.getInputs()[0]) + ax1.set_ylabel(flight.attitudeFrequencyResponse.getOutputs()[0]) + ax1.grid(True) + + ax2 = plt.subplot(222) + for index, flight in enumerate(self.trajectory_list): + ax2.plot( + flight.omega1FrequencyResponse[:, 0], + flight.omega1FrequencyResponse[:, 1], + # color=self.colors_scale[index], + ) + ax2.set_title( + "{} x {}".format( + flight.omega1FrequencyResponse.getOutputs()[0], + flight.omega1FrequencyResponse.getInputs()[0], + ) + ) + ax2.set_xlabel(flight.omega1FrequencyResponse.getInputs()[0]) + ax2.set_ylabel(flight.omega1FrequencyResponse.getOutputs()[0]) + ax2.grid(True) + + ax3 = plt.subplot(223) + for index, flight in enumerate(self.trajectory_list): + ax3.plot( + flight.omega2FrequencyResponse[:, 0], + flight.omega2FrequencyResponse[:, 1], + # color=self.colors_scale[index], + ) + ax3.set_title( + "{} x {}".format( + flight.omega2FrequencyResponse.getOutputs()[0], + flight.omega2FrequencyResponse.getInputs()[0], + ) + ) + ax3.set_xlabel(flight.omega2FrequencyResponse.getInputs()[0]) + ax3.set_ylabel(flight.omega2FrequencyResponse.getOutputs()[0]) + ax3.grid(True) - # Stability and Control: - # Flight.attitudeFrequencyResponse : Function + ax4 = plt.subplot(224) + for index, flight in enumerate(self.trajectory_list): + ax4.plot( + flight.omega3FrequencyResponse[:, 0], + flight.omega3FrequencyResponse[:, 1], + # color=self.colors_scale[index], + ) - # Flight.omega1FrequencyResponse : Function + ax4.set_title( + "{} x {}".format( + flight.omega3FrequencyResponse.getOutputs()[0], + flight.omega3FrequencyResponse.getInputs()[0], + ) + ) + ax4.set_xlabel(flight.omega3FrequencyResponse.getInputs()[0]) + ax4.set_ylabel(flight.omega3FrequencyResponse.getOutputs()[0]) + ax4.grid(True) - # Flight.omega2FrequencyResponse : Function + fig.legend( + loc="upper center", + ncol=len(self.names_list), + fancybox=True, + shadow=True, + fontsize=10, + bbox_to_anchor=(0.5, 1), + ) - # Flight.omega3FrequencyResponse : Function + fig.tight_layout() + + return None + + def comparePressureSignals(self): + """_summary_ + """ + print("Still not implemented") + pass def compareFinFlutterAnalysis(self): # Should only work if the fin flutter analysis was ran before. # TODO: Add boolean! + print("Still not implemented yet!") return None - # Fin Flutter Analysis: - # Flight.flutterMachNumber: Function - # Flight.difference: Function - # Flight.safetyFactor: Function - @staticmethod def compareTrajectories3D(trajectory_list, names_list, legend=None): """Creates a trajectory plot combining the trajectories listed. @@ -2593,8 +2765,9 @@ def compareTrajectories3D(trajectory_list, names_list, legend=None): maxX, maxY, maxZ, minX, minY, minZ, maxXY, minXY = 0, 0, 0, 0, 0, 0, 0, 0 # Create the figure - fig1 = plt.figure(figsize=(9, 9)) - ax1 = plt.subplot(111, projection="3d") + fig1 = plt.figure(figsize=(7, 7)) + fig1.suptitle("Flight Trajectories Comparison", fontsize=16, y=0.95, x=0.5) + ax1 = plt.subplot(111, projection="3d", ) # Iterate through trajectories for index, flight in enumerate(trajectory_list): @@ -2615,18 +2788,21 @@ def compareTrajectories3D(trajectory_list, names_list, legend=None): ax1.plot(x, y, z, linewidth="2", label=names_list[index]) # Plot settings - ax1.scatter(0, 0, 0) + # TODO: Don't know why, but tha z_label is not working properly + ax1.scatter(0, 0, 0, color="black", s=10, marker="o") ax1.set_xlabel("X - East (m)") ax1.set_ylabel("Y - North (m)") ax1.set_zlabel("Z - Altitude (m)") - ax1.set_title("Flight Trajectories Comparison") ax1.set_zlim3d([minZ, maxZ]) ax1.set_ylim3d([minXY, maxXY]) ax1.set_xlim3d([minXY, maxXY]) ax1.view_init(15, 45) + + # Add legend if legend: - plt.legend() - plt.show() + fig1.legend() + + fig1.tight_layout() return None @@ -2673,6 +2849,7 @@ def compareFlightTrajectories2D(self, legend=None): - XZ projection plot - YZ projection plot """ + print("Still not implemented yet!") pass @staticmethod @@ -2682,6 +2859,7 @@ def compareFlightSimulators(): Still not implemented yet. Should also allow comparison between RocketPy and actual flight data. """ + print("Still not implemented yet!") pass # Start definition of animations methods @@ -2851,6 +3029,8 @@ def allInfo(self, mode="basic"): elif mode == "compare": print("Compare mode not yet implemented") + # TODO: Add more comparison functions here + self.info() self.compareFlightTrajectories3D() @@ -2861,6 +3041,24 @@ def allInfo(self, mode="basic"): self.compareAccelerations() + self.compareAngularAccelerations() + + self.compareForces() + + self.compareMoments() + + self.compareEnergies() + + self.comparePowers() + + self.compareFluidMechanics() + + # self.comparePressureSignals() + + self.compareStabilityAndControlData() + + + else: raise ValueError("Mode must be 'basic' or 'compare'") From 83e9d16276d79f8b4066c34e0ed6f0fb92d57385 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 22:10:40 +0200 Subject: [PATCH 07/14] ENH: Fix rail buttons and .info() --- rocketpy/plots/flight_plots.py | 137 ++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 55 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index ceb3bcd18..c4de9e6bb 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -18,8 +18,6 @@ # TODO: Add unit tests to verify if the code is working properly -# Major obs.: MAYBE I should call Function to do comparison plots, and improving colors functionality directly inside Function.py - class flight_plots: """class to plot flight data @@ -82,7 +80,7 @@ def printInitialConditionsData(self): for index, flight in enumerate(self.trajectory_list): - print("Initial Conditions for Flight: ", self.names_list[index]) + print("\nInitial Conditions for Flight: ", self.names_list[index]) # Post-process results if flight.postProcessed is False: @@ -108,7 +106,7 @@ def printInitialConditionsData(self): ) ) print( - "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s \n".format( + "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format( flight.w1(0), flight.w2(0), flight.w3(0) ) ) @@ -128,7 +126,7 @@ def printNumericalIntegrationSettings(self): None """ for index, flight in enumerate(self.trajectory_list): - print("Numerical Integration Settings of Flight: ", self.names_list[index]) + print("\nNumerical Integration Settings of Flight: ", self.names_list[index]) print("Maximum Allowed Flight Time: {:f} s".format(flight.maxTime)) print("Maximum Allowed Time Step: {:f} s".format(flight.maxTimeStep)) print("Minimum Allowed Time Step: {:e} s".format(flight.minTimeStep)) @@ -160,7 +158,7 @@ def printSurfaceWindConditions(self): for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: flight.postProcess() - print("Surface Wind Conditions of Flight: ", self.names_list[index]) + print("\nSurface Wind Conditions of Flight: ", self.names_list[index]) print( "Frontal Surface Wind Speed: {:.2f} m/s".format( flight.frontalSurfaceWind @@ -188,9 +186,9 @@ def printLaunchRailConditions(self): """ for index, flight in enumerate(self.trajectory_list): - print("Launch Rail Orientation of Flight: ", self.names_list[index]) + print("\nLaunch Rail Orientation of Flight: ", self.names_list[index]) print("Launch Rail Inclination: {:.2f}°".format(flight.inclination)) - print("Launch Rail Heading: {:.2f}°\n\n".format(flight.heading)) + print("Launch Rail Heading: {:.2f}°".format(flight.heading)) return None def printOutOfRailConditions(self): @@ -204,7 +202,7 @@ def printOutOfRailConditions(self): for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: flight.postProcess() - print("Rail Departure State of Flight: ", self.names_list[index]) + print("\nRail Departure State of Flight: ", self.names_list[index]) print("Rail Departure Time: {:.3f} s".format(flight.outOfRailTime)) print( "Rail Departure Velocity: {:.3f} m/s".format(flight.outOfRailVelocity) @@ -243,7 +241,7 @@ def printBurnOutConditions(self): for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: flight.postProcess() - print("BurnOut State of Flight: ", self.names_list[index]) + print("\nBurnOut State of Flight: ", self.names_list[index]) print("BurnOut time: {:.3f} s".format(flight.rocket.motor.burnOutTime)) print( "Altitude at burnOut: {:.3f} m (AGL)".format( @@ -289,7 +287,7 @@ def printApogeeConditions(self): for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: flight.postProcess() - print("Apogee State of Flight: ", self.names_list[index]) + print("\nApogee State of Flight: ", self.names_list[index]) print( "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( flight.apogee, flight.apogee - flight.env.elevation @@ -315,7 +313,7 @@ def printEventsRegistered(self): for index, flight in enumerate(self.trajectory_list): if flight.postProcessed is False: flight.postProcess() - print("Parachute Events of Flight: ", self.names_list[index]) + print("\nParachute Events of Flight: ", self.names_list[index]) if len(flight.parachuteEvents) == 0: print("No Parachute Events Were Triggered.") for event in flight.parachuteEvents: @@ -353,7 +351,7 @@ def printImpactConditions(self): if flight.postProcessed is False: flight.postProcess() if len(flight.impactState) != 0: - print("Impact Conditions of Flight: ", self.names_list[index]) + print("\nImpact Conditions of Flight: ", self.names_list[index]) print("X Impact: {:.3f} m".format(flight.xImpact)) print("Y Impact: {:.3f} m".format(flight.yImpact)) print("Time of Impact: {:.3f} s".format(flight.tFinal)) @@ -374,7 +372,7 @@ def printMaximumValues(self): None """ for index, flight in enumerate(self.trajectory_list): - print("Maximum Values of Flight: ", self.names_list[index]) + print("\nMaximum Values of Flight: ", self.names_list[index]) print( "Maximum Speed: {:.3f} m/s at {:.2f} s".format( flight.maxSpeed, flight.maxSpeedTime @@ -2018,7 +2016,7 @@ def compareAngularAccelerations(self): return None - def compareForces(self): + def compareAerodynamicForces(self): """_summary_ Returns @@ -2082,7 +2080,7 @@ def compareForces(self): return None - def compareMoments(self): + def compareAerodynamicMoments(self): """_summary_ Returns @@ -2341,6 +2339,8 @@ def compareRailButtonsForces(self): ax1 = plt.subplot(221) max_time = 0 for index, flight in enumerate(self.trajectory_list): + if flight.rocket.railButtons is None: + continue if flight.postProcessed is False: flight.postProcess() ax1.plot( @@ -2350,69 +2350,78 @@ def compareRailButtonsForces(self): # color=self.colors_scale[index], ) max_time = flight.tFinal if flight.tFinal > max_time else max_time - ax1.set_xlim(0, max_time) - ax1.set_title( - "{} x {}".format( - flight.railButton1NormalForce.getOutputs()[0], - flight.railButton1NormalForce.getInputs()[0], + + ax1.set_title( + "{} x {}".format( + flight.railButton1NormalForce.getOutputs()[0], + flight.railButton1NormalForce.getInputs()[0], + ) ) - ) - ax1.set_xlabel(flight.railButton1NormalForce.getInputs()[0]) - ax1.set_ylabel(flight.railButton1NormalForce.getOutputs()[0]) + ax1.set_xlabel(flight.railButton1NormalForce.getInputs()[0]) + ax1.set_ylabel(flight.railButton1NormalForce.getOutputs()[0]) + ax1.set_xlim(0, max_time) ax1.grid(True) ax2 = plt.subplot(223) for index, flight in enumerate(self.trajectory_list): + if flight.rocket.railButtons is None: + continue ax2.plot( flight.railButton2NormalForce[:, 0], flight.railButton2NormalForce[:, 1], # color=self.colors_scale[index], ) - ax2.set_xlim(0, max_time) - ax2.set_title( - "{} x {}".format( - flight.railButton2NormalForce.getOutputs()[0], - flight.railButton2NormalForce.getInputs()[0], + ax2.set_title( + "{} x {}".format( + flight.railButton2NormalForce.getOutputs()[0], + flight.railButton2NormalForce.getInputs()[0], + ) ) - ) - ax2.set_xlabel(flight.railButton2NormalForce.getInputs()[0]) - ax2.set_ylabel(flight.railButton2NormalForce.getOutputs()[0]) + ax2.set_xlabel(flight.railButton2NormalForce.getInputs()[0]) + ax2.set_ylabel(flight.railButton2NormalForce.getOutputs()[0]) + ax2.set_xlim(0, max_time) ax2.grid(True) ax3 = plt.subplot(222) for index, flight in enumerate(self.trajectory_list): + if flight.rocket.railButtons is None: + continue ax3.plot( flight.railButton1ShearForce[:, 0], flight.railButton1ShearForce[:, 1], # color=self.colors_scale[index], ) - ax3.set_xlim(0, max_time) - ax3.set_title( - "{} x {}".format( - flight.railButton1ShearForce.getOutputs()[0], - flight.railButton1ShearForce.getInputs()[0], + + ax3.set_title( + "{} x {}".format( + flight.railButton1ShearForce.getOutputs()[0], + flight.railButton1ShearForce.getInputs()[0], + ) ) - ) - ax3.set_xlabel(flight.railButton1ShearForce.getInputs()[0]) - ax3.set_ylabel(flight.railButton1ShearForce.getOutputs()[0]) + ax3.set_xlabel(flight.railButton1ShearForce.getInputs()[0]) + ax3.set_ylabel(flight.railButton1ShearForce.getOutputs()[0]) + ax3.set_xlim(0, max_time) ax3.grid(True) ax4 = plt.subplot(224) for index, flight in enumerate(self.trajectory_list): + if flight.rocket.railButtons is None: + continue ax4.plot( flight.railButton2ShearForce[:, 0], flight.railButton2ShearForce[:, 1], # color=self.colors_scale[index], ) - ax4.set_xlim(0, max_time) - ax4.set_title( - "{} x {}".format( - flight.railButton2ShearForce.getOutputs()[0], - flight.railButton2ShearForce.getInputs()[0], + + ax4.set_title( + "{} x {}".format( + flight.railButton2ShearForce.getOutputs()[0], + flight.railButton2ShearForce.getInputs()[0], + ) ) - ) - ax4.set_xlabel(flight.railButton2ShearForce.getInputs()[0]) - ax4.set_ylabel(flight.railButton2ShearForce.getOutputs()[0]) + ax4.set_xlabel(flight.railButton2ShearForce.getInputs()[0]) + ax4.set_ylabel(flight.railButton2ShearForce.getOutputs()[0]) + ax4.set_xlim(0, max_time) ax4.grid(True) fig.legend( @@ -2718,8 +2727,7 @@ def compareAttitudeFrequencyResponses(self): return None def comparePressureSignals(self): - """_summary_ - """ + """_summary_""" print("Still not implemented") pass @@ -2767,7 +2775,10 @@ def compareTrajectories3D(trajectory_list, names_list, legend=None): # Create the figure fig1 = plt.figure(figsize=(7, 7)) fig1.suptitle("Flight Trajectories Comparison", fontsize=16, y=0.95, x=0.5) - ax1 = plt.subplot(111, projection="3d", ) + ax1 = plt.subplot( + 111, + projection="3d", + ) # Iterate through trajectories for index, flight in enumerate(trajectory_list): @@ -3039,13 +3050,29 @@ def allInfo(self, mode="basic"): self.compareVelocities() + self.compareStreamVelocities() + self.compareAccelerations() + self.compareAngularVelocities() + self.compareAngularAccelerations() - self.compareForces() + self.compareEulerAngles() + + self.compareQuaternions() + + self.compareAttitudeAngles() - self.compareMoments() + self.compareAnglesOfAttack() + + self.compareStaticMargins() + + self.compareAerodynamicForces() + + self.compareAerodynamicMoments() + + self.compareRailButtonsForces() self.compareEnergies() @@ -3055,9 +3082,9 @@ def allInfo(self, mode="basic"): # self.comparePressureSignals() - self.compareStabilityAndControlData() - + # self.compareFinFlutterAnalysis() + self.compareAttitudeFrequencyResponses() else: raise ValueError("Mode must be 'basic' or 'compare'") From 7db1ba0ec1de60da2f3aa2249a82c475dbd3a3f6 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 22:11:04 +0200 Subject: [PATCH 08/14] ENH: Improving deployable payload example --- .../deployable_payload_example.ipynb | 787 +++++++++++++++++- 1 file changed, 750 insertions(+), 37 deletions(-) diff --git a/docs/notebooks/deployable_payload_example.ipynb b/docs/notebooks/deployable_payload_example.ipynb index 50f15525f..7c6661723 100644 --- a/docs/notebooks/deployable_payload_example.ipynb +++ b/docs/notebooks/deployable_payload_example.ipynb @@ -60,11 +60,11 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "from rocketpy import Environment, SolidMotor, Rocket, Flight, Function, utilities" + "from rocketpy import Environment, SolidMotor, Rocket, Flight, Function" ] }, { @@ -76,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -104,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -123,7 +123,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -139,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -162,7 +162,7 @@ "\n", "Atmospheric Model Type: Forecast\n", "Forecast Maximum Height: 8.000 km\n", - "Forecast Time Period: From 2022-10-05 00:00:00 to 2022-10-21 00:00:00 UTC\n", + "Forecast Time Period: From 2022-10-05 06:00:00 to 2022-10-21 06:00:00 UTC\n", "Forecast Hour Interval: 3 hrs\n", "Forecast Latitude Range: From -90.0 ° To 90.0 °\n", "Forecast Longitude Range: From 0.0 ° To 359.75 °\n", @@ -170,13 +170,13 @@ "\n", "Surface Atmospheric Conditions\n", "\n", - "Surface Wind Speed: 3.53 m/s\n", - "Surface Wind Direction: 9.63°\n", - "Surface Wind Heading: 189.63°\n", - "Surface Pressure: 858.08 hPa\n", - "Surface Temperature: 287.52 K\n", - "Surface Air Density: 1.040 kg/m³\n", - "Surface Speed of Sound: 339.92 m/s\n", + "Surface Wind Speed: 4.03 m/s\n", + "Surface Wind Direction: 61.94°\n", + "Surface Wind Heading: 182.37°\n", + "Surface Pressure: 858.27 hPa\n", + "Surface Temperature: 287.78 K\n", + "Surface Air Density: 1.039 kg/m³\n", + "Surface Speed of Sound: 340.08 m/s\n", "\n", "\n", "Atmospheric Model Plots\n" @@ -184,7 +184,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:00:32.147998\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T18:56:18.405065\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -208,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -229,7 +229,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -250,7 +250,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:00:32.756529\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T18:56:19.481274\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -288,7 +288,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -319,7 +319,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -352,7 +352,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -375,7 +375,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:32.265013\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T18:56:20.705799\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -390,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -417,7 +417,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -470,7 +470,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -499,7 +499,7 @@ }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:34.682136\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T18:56:22.555743\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ "
" ] @@ -521,7 +521,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -554,7 +554,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -615,7 +615,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -640,20 +640,696 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will invoke a method from RocketPy's utilities class in order to visualize \n", + "We will invoke a method from RocketPy's flight_plots module in order to visualize \n", "the trajectory." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "from rocketpy.plots import flight_plots" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By initializing the flight_plots class, we can plot the trajectory ad other\n", + "important information about the flights. " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "flight_plotter = flight_plots.flight_plots(\n", + " trajectory_list=[RocketFlight1, RocketFlight2, PayloadFlight],\n", + " names_list=[\"Rocket - 1st Stage\", \"Rocket - 2nd Stage\", \"Payload\"],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Numerical information are available by calling `.info()` method." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Initial Conditions for Flight: Rocket - 1st Stage\n", + "Position - x: 0.00 m | y: 0.00 m | z: 1471.47 m\n", + "Velocity - Vx: 0.00 m/s | Vy: 0.00 m/s | Vz: 0.00 m/s\n", + "Attitude - e0: 0.975 | e1: -0.043 | e2: 0.009 | e3: -0.216\n", + "Euler Angles - Spin φ : 0.00° | Nutation θ: -5.00° | Precession ψ: -25.00°\n", + "Angular Velocity - ω1: 0.00 rad/s | ω2: 0.00 rad/s| ω3: 0.00 rad/s\n", + "\n", + "Initial Conditions for Flight: Rocket - 2nd Stage\n", + "Position - x: 39485.37 m | y: 133869.35 m | z: 4345914.80 m\n", + "Velocity - Vx: -191.43 m/s | Vy: -723.16 m/s | Vz: -47595.99 m/s\n", + "Attitude - e0: -67496.659 | e1: 63232.013 | e2: -4205.673 | e3: 15122.327\n", + "Euler Angles - Spin φ : -8.82° | Nutation θ: -84.72° | Precession ψ: -16.43°\n", + "Angular Velocity - ω1: -0.35 rad/s | ω2: -0.05 rad/s| ω3: 0.00 rad/s\n", + "\n", + "Initial Conditions for Flight: Payload\n", + "Position - x: 51501.13 m | y: 174607.13 m | z: 4349500.80 m\n", + "Velocity - Vx: -176.89 m/s | Vy: -685.68 m/s | Vz: -62960.94 m/s\n", + "Attitude - e0: -67498.155 | e1: 63230.993 | e2: -4205.605 | e3: 15122.661\n", + "Euler Angles - Spin φ : -8.82° | Nutation θ: -84.72° | Precession ψ: -16.43°\n", + "Angular Velocity - ω1: -0.35 rad/s | ω2: -0.05 rad/s| ω3: 0.00 rad/s\n", + "\n", + "Surface Wind Conditions of Flight: Rocket - 1st Stage\n", + "Frontal Surface Wind Speed: -3.72 m/s\n", + "Lateral Surface Wind Speed: -1.54 m/s\n", + "\n", + "Surface Wind Conditions of Flight: Rocket - 2nd Stage\n", + "Frontal Surface Wind Speed: -4.02 m/s\n", + "Lateral Surface Wind Speed: 0.17 m/s\n", + "\n", + "Surface Wind Conditions of Flight: Payload\n", + "Frontal Surface Wind Speed: -4.02 m/s\n", + "Lateral Surface Wind Speed: 0.17 m/s\n", + "\n", + "Launch Rail Orientation of Flight: Rocket - 1st Stage\n", + "Launch Rail Inclination: 85.00°\n", + "Launch Rail Heading: 25.00°\n", + "\n", + "Launch Rail Orientation of Flight: Rocket - 2nd Stage\n", + "Launch Rail Inclination: 0.00°\n", + "Launch Rail Heading: 0.00°\n", + "\n", + "Launch Rail Orientation of Flight: Payload\n", + "Launch Rail Inclination: 0.00°\n", + "Launch Rail Heading: 0.00°\n", + "\n", + "Rail Departure State of Flight: Rocket - 1st Stage\n", + "Rail Departure Time: 0.415 s\n", + "Rail Departure Velocity: 30.476 m/s\n", + "Rail Departure Static Margin: 2.148 c\n", + "Rail Departure Angle of Attack: 7.440°\n", + "Rail Departure Thrust-Weight Ratio: 10.308\n", + "Rail Departure Reynolds Number: 2.292e+05\n", + "\n", + "Rail Departure State of Flight: Rocket - 2nd Stage\n", + "Rail Departure Time: 25.834 s\n", + "Rail Departure Velocity: 0.000 m/s\n", + "Rail Departure Static Margin: -0.000 c\n", + "Rail Departure Angle of Attack: 5.277°\n", + "Rail Departure Thrust-Weight Ratio: 0.000\n", + "Rail Departure Reynolds Number: 1.578e+05\n", + "\n", + "Rail Departure State of Flight: Payload\n", + "Rail Departure Time: 25.834 s\n", + "Rail Departure Velocity: 0.000 m/s\n", + "Rail Departure Static Margin: -0.000 c\n", + "Rail Departure Angle of Attack: 5.277°\n", + "Rail Departure Thrust-Weight Ratio: 0.000\n", + "Rail Departure Reynolds Number: 1.578e+05\n", + "\n", + "BurnOut State of Flight: Rocket - 1st Stage\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 654.629 m (AGL)\n", + "Rocket velocity at burnOut: 280.268 m/s\n", + "Freestream velocity at burnOut: 280.291 m/s\n", + "Mach Number at burnOut: 0.831\n", + "Kinetic energy at burnOut: 6.379e+05 J\n", + "\n", + "BurnOut State of Flight: Rocket - 2nd Stage\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 2660233.688 m (AGL)\n", + "Rocket velocity at burnOut: 30.180 m/s\n", + "Freestream velocity at burnOut: 28.069 m/s\n", + "Mach Number at burnOut: 0.086\n", + "Kinetic energy at burnOut: 5.347e+03 J\n", + "\n", + "BurnOut State of Flight: Payload\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 2662428.447 m (AGL)\n", + "Rocket velocity at burnOut: 30.180 m/s\n", + "Freestream velocity at burnOut: 28.069 m/s\n", + "Mach Number at burnOut: 0.086\n", + "Kinetic energy at burnOut: 2.050e+03 J\n", + "\n", + "Apogee State of Flight: Rocket - 1st Stage\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "\n", + "Apogee State of Flight: Rocket - 2nd Stage\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "\n", + "Apogee State of Flight: Payload\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "\n", + "Parachute Events of Flight: Rocket - 1st Stage\n", + "No Parachute Events Were Triggered.\n", + "\n", + "Parachute Events of Flight: Rocket - 2nd Stage\n", + "Drogue Ejection Triggered at: 25.838 s\n", + "Drogue Parachute Inflated at: 27.338 s\n", + "Drogue Parachute Inflated with Freestream Speed of: 31.228 m/s\n", + "Drogue Parachute Inflated at Height of: 3285.072 m (AGL)\n", + "Main Ejection Triggered at: 160.562 s\n", + "Main Parachute Inflated at: 162.062 s\n", + "Main Parachute Inflated with Freestream Speed of: 18.171 m/s\n", + "Main Parachute Inflated at Height of: 701.231 m (AGL)\n", + "\n", + "Parachute Events of Flight: Payload\n", + "Drogue Ejection Triggered at: 25.838 s\n", + "Drogue Parachute Inflated at: 27.338 s\n", + "Drogue Parachute Inflated with Freestream Speed of: 31.083 m/s\n", + "Drogue Parachute Inflated at Height of: 3285.082 m (AGL)\n", + "Main Ejection Triggered at: 177.676 s\n", + "Main Parachute Inflated at: 179.176 s\n", + "Main Parachute Inflated with Freestream Speed of: 16.153 m/s\n", + "Main Parachute Inflated at Height of: 704.169 m (AGL)\n", + "\n", + "Impact Conditions of Flight: Rocket - 1st Stage\n", + "X Impact: 0.000 m\n", + "Y Impact: 0.000 m\n", + "Time of Impact: 25.834 s\n", + "Velocity at Impact: 0.000 m/s\n", + "\n", + "Impact Conditions of Flight: Rocket - 2nd Stage\n", + "X Impact: 263.439 m\n", + "Y Impact: 902.739 m\n", + "Time of Impact: 285.440 s\n", + "Velocity at Impact: -5.548 m/s\n", + "\n", + "Impact Conditions of Flight: Payload\n", + "X Impact: 270.859 m\n", + "Y Impact: 929.654 m\n", + "Time of Impact: 327.907 s\n", + "Velocity at Impact: -4.608 m/s\n", + "\n", + "Maximum Values of Flight: Rocket - 1st Stage\n", + "Maximum Speed: 286.303 m/s at 3.38 s\n", + "Maximum Mach Number: 0.847 Mach at 3.39 s\n", + "Maximum Reynolds Number: 2.034e+06 at 3.33 s\n", + "Maximum Dynamic Pressure: 4.062e+04 Pa at 3.35 s\n", + "Maximum Acceleration: 105.099 m/s² at 0.15 s\n", + "Maximum Gs: 10.717 g at 0.15 s\n", + "Maximum Upper Rail Button Normal Force: 0.383 N\n", + "Maximum Upper Rail Button Shear Force: 0.383 N\n", + "Maximum Lower Rail Button Normal Force: 0.383 N\n", + "Maximum Lower Rail Button Shear Force: 0.383 N\n", + "\n", + "Maximum Values of Flight: Rocket - 2nd Stage\n", + "Maximum Speed: 33.104 m/s at 27.34 s\n", + "Maximum Mach Number: 0.095 Mach at 27.34 s\n", + "Maximum Reynolds Number: 1.757e+05 at 27.34 s\n", + "Maximum Dynamic Pressure: 3.644e+02 Pa at 27.34 s\n", + "Maximum Acceleration: 40.250 m/s² at 162.06 s\n", + "Maximum Gs: 4.104 g at 162.06 s\n", + "Maximum Upper Rail Button Normal Force: 0.000 N\n", + "Maximum Upper Rail Button Shear Force: 0.000 N\n", + "Maximum Lower Rail Button Normal Force: 0.000 N\n", + "Maximum Lower Rail Button Shear Force: 0.000 N\n", + "\n", + "Maximum Values of Flight: Payload\n", + "Maximum Speed: 32.959 m/s at 27.34 s\n", + "Maximum Mach Number: 0.095 Mach at 27.34 s\n", + "Maximum Reynolds Number: 1.749e+05 at 27.34 s\n", + "Maximum Dynamic Pressure: 3.611e+02 Pa at 27.34 s\n", + "Maximum Acceleration: 25.033 m/s² at 179.18 s\n", + "Maximum Gs: 2.553 g at 179.18 s\n", + "Maximum Upper Rail Button Normal Force: 0.000 N\n", + "Maximum Upper Rail Button Shear Force: 0.000 N\n", + "Maximum Lower Rail Button Normal Force: 0.000 N\n", + "Maximum Lower Rail Button Shear Force: 0.000 N\n", + "\n", + "Numerical Integration Settings of Flight: Rocket - 1st Stage\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: True\n", + "Number of Time Steps Used: 505\n", + "Number of Derivative Functions Evaluation: 1518\n", + "Average Function Evaluations per Time Step: 3.005941\n", + "\n", + "Numerical Integration Settings of Flight: Rocket - 2nd Stage\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: False\n", + "Number of Time Steps Used: 230\n", + "Number of Derivative Functions Evaluation: 743\n", + "Average Function Evaluations per Time Step: 3.230435\n", + "\n", + "Numerical Integration Settings of Flight: Payload\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: False\n", + "Number of Time Steps Used: 219\n", + "Number of Derivative Functions Evaluation: 639\n", + "Average Function Evaluations per Time Step: 2.917808\n" + ] + } + ], + "source": [ + "flight_plotter.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will mode straight forward and plot all the available charts at once by\n", + "calling the `.allInfo()` method. But you can also plot each chart individually\n", + "by calling any of the following methods:\n", + "- `.compareFlightTrajectories3D()`\n", + "- `.compareVelocities()`\n", + "- `.compareAerodynamicForces()`\n", + "- `.compareStaticMargins()`\n", + "- etc.\n", + "\n", + "You can use `dir(flight_plotter)`to check all the available methods." + ] + }, + { + "cell_type": "code", + "execution_count": 34, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compare mode not yet implemented\n", + "Initial Conditions for Flight: Rocket - 1st Stage\n", + "Position - x: 0.00 m | y: 0.00 m | z: 1471.47 m\n", + "Velocity - Vx: 0.00 m/s | Vy: 0.00 m/s | Vz: 0.00 m/s\n", + "Attitude - e0: 0.975 | e1: -0.043 | e2: 0.009 | e3: -0.216\n", + "Euler Angles - Spin φ : 0.00° | Nutation θ: -5.00° | Precession ψ: -25.00°\n", + "Angular Velocity - ω1: 0.00 rad/s | ω2: 0.00 rad/s| ω3: 0.00 rad/s \n", + "\n", + "Initial Conditions for Flight: Rocket - 2nd Stage\n", + "Position - x: 39485.37 m | y: 133869.35 m | z: 4345914.80 m\n", + "Velocity - Vx: -191.43 m/s | Vy: -723.16 m/s | Vz: -47595.99 m/s\n", + "Attitude - e0: -67496.659 | e1: 63232.013 | e2: -4205.673 | e3: 15122.327\n", + "Euler Angles - Spin φ : -8.82° | Nutation θ: -84.72° | Precession ψ: -16.43°\n", + "Angular Velocity - ω1: -0.35 rad/s | ω2: -0.05 rad/s| ω3: 0.00 rad/s \n", + "\n", + "Initial Conditions for Flight: Payload\n", + "Position - x: 51501.13 m | y: 174607.13 m | z: 4349500.80 m\n", + "Velocity - Vx: -176.89 m/s | Vy: -685.68 m/s | Vz: -62960.94 m/s\n", + "Attitude - e0: -67498.155 | e1: 63230.993 | e2: -4205.605 | e3: 15122.661\n", + "Euler Angles - Spin φ : -8.82° | Nutation θ: -84.72° | Precession ψ: -16.43°\n", + "Angular Velocity - ω1: -0.35 rad/s | ω2: -0.05 rad/s| ω3: 0.00 rad/s \n", + "\n", + "Surface Wind Conditions of Flight: Rocket - 1st Stage\n", + "Frontal Surface Wind Speed: -3.72 m/s\n", + "Lateral Surface Wind Speed: -1.54 m/s\n", + "Surface Wind Conditions of Flight: Rocket - 2nd Stage\n", + "Frontal Surface Wind Speed: -4.02 m/s\n", + "Lateral Surface Wind Speed: 0.17 m/s\n", + "Surface Wind Conditions of Flight: Payload\n", + "Frontal Surface Wind Speed: -4.02 m/s\n", + "Lateral Surface Wind Speed: 0.17 m/s\n", + "Launch Rail Orientation of Flight: Rocket - 1st Stage\n", + "Launch Rail Inclination: 85.00°\n", + "Launch Rail Heading: 25.00°\n", + "\n", + "\n", + "Launch Rail Orientation of Flight: Rocket - 2nd Stage\n", + "Launch Rail Inclination: 0.00°\n", + "Launch Rail Heading: 0.00°\n", + "\n", + "\n", + "Launch Rail Orientation of Flight: Payload\n", + "Launch Rail Inclination: 0.00°\n", + "Launch Rail Heading: 0.00°\n", + "\n", + "\n", + "Rail Departure State of Flight: Rocket - 1st Stage\n", + "Rail Departure Time: 0.415 s\n", + "Rail Departure Velocity: 30.476 m/s\n", + "Rail Departure Static Margin: 2.148 c\n", + "Rail Departure Angle of Attack: 7.440°\n", + "Rail Departure Thrust-Weight Ratio: 10.308\n", + "Rail Departure Reynolds Number: 2.292e+05\n", + "Rail Departure State of Flight: Rocket - 2nd Stage\n", + "Rail Departure Time: 25.834 s\n", + "Rail Departure Velocity: 0.000 m/s\n", + "Rail Departure Static Margin: -0.000 c\n", + "Rail Departure Angle of Attack: 5.277°\n", + "Rail Departure Thrust-Weight Ratio: 0.000\n", + "Rail Departure Reynolds Number: 1.578e+05\n", + "Rail Departure State of Flight: Payload\n", + "Rail Departure Time: 25.834 s\n", + "Rail Departure Velocity: 0.000 m/s\n", + "Rail Departure Static Margin: -0.000 c\n", + "Rail Departure Angle of Attack: 5.277°\n", + "Rail Departure Thrust-Weight Ratio: 0.000\n", + "Rail Departure Reynolds Number: 1.578e+05\n", + "BurnOut State of Flight: Rocket - 1st Stage\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 654.629 m (AGL)\n", + "Rocket velocity at burnOut: 280.268 m/s\n", + "Freestream velocity at burnOut: 280.291 m/s\n", + "Mach Number at burnOut: 0.831\n", + "Kinetic energy at burnOut: 6.379e+05 J\n", + "BurnOut State of Flight: Rocket - 2nd Stage\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 2660233.688 m (AGL)\n", + "Rocket velocity at burnOut: 30.180 m/s\n", + "Freestream velocity at burnOut: 28.069 m/s\n", + "Mach Number at burnOut: 0.086\n", + "Kinetic energy at burnOut: 5.347e+03 J\n", + "BurnOut State of Flight: Payload\n", + "BurnOut time: 3.900 s\n", + "Altitude at burnOut: 2662428.447 m (AGL)\n", + "Rocket velocity at burnOut: 30.180 m/s\n", + "Freestream velocity at burnOut: 28.069 m/s\n", + "Mach Number at burnOut: 0.086\n", + "Kinetic energy at burnOut: 2.050e+03 J\n", + "Apogee State of Flight: Rocket - 1st Stage\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "Apogee State of Flight: Rocket - 2nd Stage\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "Apogee State of Flight: Payload\n", + "Apogee Altitude: 4767.601 m (ASL) | 3296.135 m (AGL)\n", + "Apogee Time: 25.834 s\n", + "Apogee Freestream Speed: 28.069 m/s\n", + "Parachute Events of Flight: Rocket - 1st Stage\n", + "No Parachute Events Were Triggered.\n", + "Parachute Events of Flight: Rocket - 2nd Stage\n", + "Drogue Ejection Triggered at: 25.838 s\n", + "Drogue Parachute Inflated at: 27.338 s\n", + "Drogue Parachute Inflated with Freestream Speed of: 31.228 m/s\n", + "Drogue Parachute Inflated at Height of: 3285.072 m (AGL)\n", + "Main Ejection Triggered at: 160.562 s\n", + "Main Parachute Inflated at: 162.062 s\n", + "Main Parachute Inflated with Freestream Speed of: 18.171 m/s\n", + "Main Parachute Inflated at Height of: 701.231 m (AGL)\n", + "Parachute Events of Flight: Payload\n", + "Drogue Ejection Triggered at: 25.838 s\n", + "Drogue Parachute Inflated at: 27.338 s\n", + "Drogue Parachute Inflated with Freestream Speed of: 31.083 m/s\n", + "Drogue Parachute Inflated at Height of: 3285.082 m (AGL)\n", + "Main Ejection Triggered at: 177.676 s\n", + "Main Parachute Inflated at: 179.176 s\n", + "Main Parachute Inflated with Freestream Speed of: 16.153 m/s\n", + "Main Parachute Inflated at Height of: 704.169 m (AGL)\n", + "Impact Conditions of Flight: Rocket - 1st Stage\n", + "X Impact: 0.000 m\n", + "Y Impact: 0.000 m\n", + "Time of Impact: 25.834 s\n", + "Velocity at Impact: 0.000 m/s\n", + "Impact Conditions of Flight: Rocket - 2nd Stage\n", + "X Impact: 263.439 m\n", + "Y Impact: 902.739 m\n", + "Time of Impact: 285.440 s\n", + "Velocity at Impact: -5.548 m/s\n", + "Impact Conditions of Flight: Payload\n", + "X Impact: 270.859 m\n", + "Y Impact: 929.654 m\n", + "Time of Impact: 327.907 s\n", + "Velocity at Impact: -4.608 m/s\n", + "Maximum Values of Flight: Rocket - 1st Stage\n", + "Maximum Speed: 286.303 m/s at 3.38 s\n", + "Maximum Mach Number: 0.847 Mach at 3.39 s\n", + "Maximum Reynolds Number: 2.034e+06 at 3.33 s\n", + "Maximum Dynamic Pressure: 4.062e+04 Pa at 3.35 s\n", + "Maximum Acceleration: 105.099 m/s² at 0.15 s\n", + "Maximum Gs: 10.717 g at 0.15 s\n", + "Maximum Upper Rail Button Normal Force: 0.383 N\n", + "Maximum Upper Rail Button Shear Force: 0.383 N\n", + "Maximum Lower Rail Button Normal Force: 0.383 N\n", + "Maximum Lower Rail Button Shear Force: 0.383 N\n", + "Maximum Values of Flight: Rocket - 2nd Stage\n", + "Maximum Speed: 33.104 m/s at 27.34 s\n", + "Maximum Mach Number: 0.095 Mach at 27.34 s\n", + "Maximum Reynolds Number: 1.757e+05 at 27.34 s\n", + "Maximum Dynamic Pressure: 3.644e+02 Pa at 27.34 s\n", + "Maximum Acceleration: 40.250 m/s² at 162.06 s\n", + "Maximum Gs: 4.104 g at 162.06 s\n", + "Maximum Upper Rail Button Normal Force: 0.000 N\n", + "Maximum Upper Rail Button Shear Force: 0.000 N\n", + "Maximum Lower Rail Button Normal Force: 0.000 N\n", + "Maximum Lower Rail Button Shear Force: 0.000 N\n", + "Maximum Values of Flight: Payload\n", + "Maximum Speed: 32.959 m/s at 27.34 s\n", + "Maximum Mach Number: 0.095 Mach at 27.34 s\n", + "Maximum Reynolds Number: 1.749e+05 at 27.34 s\n", + "Maximum Dynamic Pressure: 3.611e+02 Pa at 27.34 s\n", + "Maximum Acceleration: 25.033 m/s² at 179.18 s\n", + "Maximum Gs: 2.553 g at 179.18 s\n", + "Maximum Upper Rail Button Normal Force: 0.000 N\n", + "Maximum Upper Rail Button Shear Force: 0.000 N\n", + "Maximum Lower Rail Button Normal Force: 0.000 N\n", + "Maximum Lower Rail Button Shear Force: 0.000 N\n", + "Numerical Integration Settings of Flight: Rocket - 1st Stage\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: True\n", + "Number of Time Steps Used: 505\n", + "Number of Derivative Functions Evaluation: 1518\n", + "Average Function Evaluations per Time Step: 3.005941\n", + "Numerical Integration Settings of Flight: Rocket - 2nd Stage\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: False\n", + "Number of Time Steps Used: 230\n", + "Number of Derivative Functions Evaluation: 743\n", + "Average Function Evaluations per Time Step: 3.230435\n", + "Numerical Integration Settings of Flight: Payload\n", + "Maximum Allowed Flight Time: 600.000000 s\n", + "Maximum Allowed Time Step: inf s\n", + "Minimum Allowed Time Step: 0.000000e+00 s\n", + "Relative Error Tolerance: 1e-06\n", + "Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]\n", + "Allow Event Overshoot: True\n", + "Terminate Simulation on Apogee: False\n", + "Number of Time Steps Used: 219\n", + "Number of Derivative Functions Evaluation: 639\n", + "Average Function Evaluations per Time Step: 2.917808\n" + ] + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:20.044937\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:20.380565\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:20.876126\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:21.421663\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:22.227653\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { - "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T13:01:44.814876\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:22.657046\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", "text/plain": [ - "
" + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:23.180644\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:23.749305\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:24.698796\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:25.314778\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:25.590042\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:25.815438\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:26.053799\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:26.397878\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:26.849669\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:27.561764\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:28.300787\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:29.002908\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:53:29.683087\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" ] }, "metadata": {}, @@ -661,10 +1337,47 @@ } ], "source": [ - "utilities.compareFlightTrajectories(\n", - " flight_list=[RocketFlight1, RocketFlight2, PayloadFlight],\n", - " names=[\"Rocket - 1st Stage\", \"Rocket - 2nd Stage\", \"Payload Flight\"],\n", - ")" + "flight_plotter.allInfo(mode=\"compare\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "NoneType" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(RocketFlight2.rocket.railButtons)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": "\n\n\n \n \n \n \n 2022-10-05T21:52:43.226437\n image/svg+xml\n \n \n Matplotlib v3.6.0, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "flight_plotter.compareRailButtonsForces()" ] } ], From 854cefb696c3538481e8dbff6c48f5cf2c63e994 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 22:24:40 +0200 Subject: [PATCH 09/14] ENH: adding unit test --- tests/test_flight_plots.py | 100 +++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 tests/test_flight_plots.py diff --git a/tests/test_flight_plots.py b/tests/test_flight_plots.py new file mode 100644 index 000000000..8b4bda93b --- /dev/null +++ b/tests/test_flight_plots.py @@ -0,0 +1,100 @@ +from unittest.mock import patch + +import matplotlib as plt +import numpy as np +import pytest +from rocketpy import Environment, Flight, Rocket, SolidMotor +from rocketpy.plots import flight_plots + +plt.rcParams.update({"figure.max_open_warning": 0}) + + +@patch("matplotlib.pyplot.show") +def test_flight_plots(mock_show): + + test_env = Environment(railLength=5) + test_env.setAtmosphericModel(type="CustomAtmosphere", wind_u=0.3, wind_v=-1) + + test_motor = SolidMotor( + thrustSource=2000, + burnOut=3.9, + grainNumber=5, + grainSeparation=5 / 1000, + grainDensity=1815, + grainOuterRadius=33 / 1000, + grainInitialInnerRadius=15 / 1000, + grainInitialHeight=120 / 1000, + nozzleRadius=33 / 1000, + throatRadius=11 / 1000, + interpolationMethod="linear", + ) + + test_rocket = Rocket( + motor=test_motor, + radius=127 / 2000, + mass=19.197 - 2.956, + inertiaI=6.60, + inertiaZ=0.0351, + distanceRocketNozzle=-1.255, + distanceRocketPropellant=-0.85704, + powerOffDrag=0.6, + powerOnDrag=0.6, + ) + test_rocket.setRailButtons([0.2, -0.5]) + + NoseCone = test_rocket.addNose( + length=0.55829, kind="vonKarman", distanceToCM=0.71971 + ) + FinSet = test_rocket.addTrapezoidalFins( + 4, span=0.100, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956 + ) + Tail = test_rocket.addTail( + topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656 + ) + + def drogueTrigger(p, y): + # p = pressure + # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] + # activate drogue when vz < 0 m/s. + return True if y[5] < 0 else False + + def mainTrigger(p, y): + # p = pressure + # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] + # activate main when vz < 0 m/s and z < 800 m. + return True if y[5] < 0 and y[2] < 800 else False + + Main = test_rocket.addParachute( + "Main", + CdS=10.0, + trigger=mainTrigger, + samplingRate=105, + lag=1.5, + noise=(0, 8.3, 0.5), + ) + + Drogue = test_rocket.addParachute( + "Drogue", + CdS=1.0, + trigger=drogueTrigger, + samplingRate=105, + lag=1.5, + noise=(0, 8.3, 0.5), + ) + + test_flight1 = Flight( + rocket=test_rocket, environment=test_env, inclination=90, heading=30 + ) + test_flight2 = Flight( + rocket=test_rocket, environment=test_env, inclination=85, heading=0 + ) + test_flight3 = Flight( + rocket=test_rocket, environment=test_env, inclination=80, heading=60 + ) + + flight_plotter = flight_plots.flight_plots( + [test_flight1, test_flight2, test_flight3] + ) + + assert flight_plotter.allInfo(mode="basic") == None + assert flight_plotter.allInfo(mode="compare") == None From 99953605cf7cd14b525ff06630b37379280c0acf Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Wed, 5 Oct 2022 22:27:35 +0200 Subject: [PATCH 10/14] MAINT: removing TODOs that were solved --- rocketpy/plots/flight_plots.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index c4de9e6bb..b7cc76b21 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -10,13 +10,10 @@ __copyright__ = "Copyright 20XX, RocketPy Team" __license__ = "MIT" -# TODO: Usage must be documented and examples must be added -# TODO: Ok, hard challenge part is to include the possibility of plotting multiple flights in the same plot, but without loosing the ability to plot a single flight as well -# TODO: Allow user to choose the units of the plots -# TODO: Allow user to choose the color pallet of the plots -# TODO: Add masses plots since it can vary significantly for multi-stage rockets - -# TODO: Add unit tests to verify if the code is working properly +## Future improvements: +# Allow user to choose the units of the plots +# Allow user to choose the color pallet of the plots +# Add masses plots since it can vary significantly for multi-stage rockets class flight_plots: @@ -126,7 +123,9 @@ def printNumericalIntegrationSettings(self): None """ for index, flight in enumerate(self.trajectory_list): - print("\nNumerical Integration Settings of Flight: ", self.names_list[index]) + print( + "\nNumerical Integration Settings of Flight: ", self.names_list[index] + ) print("Maximum Allowed Flight Time: {:f} s".format(flight.maxTime)) print("Maximum Allowed Time Step: {:f} s".format(flight.maxTimeStep)) print("Minimum Allowed Time Step: {:e} s".format(flight.minTimeStep)) @@ -451,8 +450,6 @@ def plot3dTrajectory(self, savefig=False): def plotLinearKinematicsData(self): """Prints out all Kinematics graphs available about the Flight - # TODO: Separate into velocity and acceleration plots for various flights - Parameters ---------- None @@ -1614,9 +1611,7 @@ def compareAccelerations(self): fig.legend( loc="upper center", - ncol=len( - self.names_list - ), # TODO: Need to be more flexible here, changing the number of rows as well + ncol=len(self.names_list), fancybox=True, shadow=True, fontsize=10, @@ -2391,7 +2386,7 @@ def compareRailButtonsForces(self): flight.railButton1ShearForce[:, 1], # color=self.colors_scale[index], ) - + ax3.set_title( "{} x {}".format( flight.railButton1ShearForce.getOutputs()[0], @@ -2412,7 +2407,7 @@ def compareRailButtonsForces(self): flight.railButton2ShearForce[:, 1], # color=self.colors_scale[index], ) - + ax4.set_title( "{} x {}".format( flight.railButton2ShearForce.getOutputs()[0], @@ -3038,9 +3033,6 @@ def allInfo(self, mode="basic"): self.plotStabilityAndControlData() elif mode == "compare": - print("Compare mode not yet implemented") - - # TODO: Add more comparison functions here self.info() From eb78f8011e7e133db5ad38df3a75ec5edb247d8e Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Sat, 22 Oct 2022 22:38:45 +0000 Subject: [PATCH 11/14] FIX: hotfix for issue #261 --- .github/workflows/test_pytest.yaml | 2 +- rocketpy/EnvironmentAnalysis.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_pytest.yaml b/.github/workflows/test_pytest.yaml index 65fbf269e..8bb5bfc03 100644 --- a/.github/workflows/test_pytest.yaml +++ b/.github/workflows/test_pytest.yaml @@ -12,7 +12,7 @@ jobs: - macos-latest - windows-latest python-version: - - 3.8 + - 3.7 steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/rocketpy/EnvironmentAnalysis.py b/rocketpy/EnvironmentAnalysis.py index 2ae57a231..4d24043c0 100644 --- a/rocketpy/EnvironmentAnalysis.py +++ b/rocketpy/EnvironmentAnalysis.py @@ -2040,7 +2040,7 @@ def init(): ax.set_ylabel("Probability") ax.set_title("Wind Gust Distribution") # ax.grid(True) - return ln, *bar_container.patches, tx + return (ln, *bar_container.patches, tx) # Define function which sets each animation frame def update(frame): @@ -2056,7 +2056,7 @@ def update(frame): ln.set_data(xdata, ydata) # Update hour text tx.set_text(f"{float(frame[0]):05.2f}".replace(".", ":")) - return ln, *bar_container.patches, tx + return (ln, *bar_container.patches, tx) for frame in wind_gusts_at_given_hour.items(): update(frame) @@ -2243,7 +2243,7 @@ def init(): label="SAcup wind speed constraints", ) # Plot SAcup wind speed constraints - return ln, *bar_container.patches, tx + return (ln, *bar_container.patches, tx) # Define function which sets each animation frame def update(frame): @@ -2261,7 +2261,7 @@ def update(frame): ln.set_data(xdata, ydata) # Update hour text tx.set_text(f"{float(frame[0]):05.2f}".replace(".", ":")) - return ln, *bar_container.patches, tx + return (ln, *bar_container.patches, tx) for frame in surface_wind_speeds_at_given_hour.items(): update(frame) From 5fa47a490be85fec9d857116204c41d2f440b615 Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Sat, 22 Oct 2022 22:38:58 +0000 Subject: [PATCH 12/14] FIX: hotfix for issue #251 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 767c8d76b..677880f46 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,9 @@ "numpy>=1.0", "scipy>=1.0", "matplotlib>=3.0", - "netCDF4>=1.4", + "netCDF4>=1.4,<1.6", "windrose>=1.6.8", + "ipywidgets>=7.6.3", "requests", "pytz", "timezonefinder", From d0b3601ddaa9629c36d5a35dc344660b01f6792d Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Sat, 22 Oct 2022 22:45:57 +0000 Subject: [PATCH 13/14] REL: update version to 0.12.1 --- docs/conf.py | 2 +- docs/user/installation.rst | 2 +- rocketpy/__init__.py | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 95f4e0ec8..614f78a0a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ author = "Giovani Hidalgo Ceotto" # The full version, including alpha/beta/rc tags -release = "0.12.0" +release = "0.12.1" # -- General configuration --------------------------------------------------- diff --git a/docs/user/installation.rst b/docs/user/installation.rst index f5f22f824..f80f6e2a1 100644 --- a/docs/user/installation.rst +++ b/docs/user/installation.rst @@ -19,7 +19,7 @@ If you want to choose a specific version to guarantee compatibility, you may ins .. code-block:: shell - pip install rocketpy==0.12.0 + pip install rocketpy==0.12.1 Optional Installation Method: ``conda`` diff --git a/rocketpy/__init__.py b/rocketpy/__init__.py index ecca8a231..7aa6f0454 100644 --- a/rocketpy/__init__.py +++ b/rocketpy/__init__.py @@ -16,7 +16,7 @@ __copyright__ = "Copyright 20XX, Projeto Jupiter" __credits__ = ["Matheus Marques Araujo", "Rodrigo Schmitt", "Guilherme Tavares"] __license__ = "MIT" -__version__ = "0.12.0" +__version__ = "0.12.1" __maintainer__ = "Giovani Hidalgo Ceotto" __email__ = "ghceotto@gmail.com" __status__ = "Production" diff --git a/setup.py b/setup.py index 677880f46..6237e49a4 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="rocketpy", - version="0.12.0", + version="0.12.1", install_requires=[ "numpy>=1.0", "scipy>=1.0", From d267724158f70797099e06a5475c030fbb85b30f Mon Sep 17 00:00:00 2001 From: Giovani Hidalgo Ceotto Date: Sun, 23 Oct 2022 21:12:29 -0300 Subject: [PATCH 14/14] REL: automate release to PyPI with GitHub Actions --- .github/workflows/publish-to-pypi.yml | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/publish-to-pypi.yml diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 000000000..e35ef8fd0 --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,34 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ + +name: Publish New Version to PyPI + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.7' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }}