From c33592fbc9cf21c9e31c0b7c682849edc50e0632 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 02:17:25 -0500 Subject: [PATCH 01/20] BUG: use rocket.dry_mass instead of rocket.mass in the u_dot_parachute --- rocketpy/simulation/flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 326b5db04..48f94d156 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1911,7 +1911,7 @@ def u_dot_parachute(self, t, u, post_processing=False): rho = self.env.density.get_value_opt(u[2]) to = 1.2 ma = ka * rho * (4 / 3) * np.pi * R**3 - mp = self.rocket.mass + mp = self.rocket.dry_mass eta = 1 Rdot = (6 * R * (1 - eta) / (1.2**6)) * ( (1 - eta) * t**5 + eta * (to**3) * (t**2) From 54fb66e3e50264407158db18d2f32cf1afc065c9 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 02:18:08 -0500 Subject: [PATCH 02/20] DOC: improve Rocket.dry_mass documentation (prevent future mistakes) --- rocketpy/rocket/rocket.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/rocketpy/rocket/rocket.py b/rocketpy/rocket/rocket.py index ffcf3b1c8..8154f2967 100644 --- a/rocketpy/rocket/rocket.py +++ b/rocketpy/rocket/rocket.py @@ -61,6 +61,9 @@ class Rocket: pointing from the rocket's nose cone to the rocket's tail. Rocket.mass : float Rocket's mass without motor and propellant, measured in kg. + Rocket.dry_mass : float + Rocket's mass without propellant, measured in kg. It does include the + motor mass. Rocket.center_of_mass : Function Position of the rocket's center of mass, including propellant, relative to the user defined rocket reference system. @@ -394,22 +397,18 @@ def evaluate_total_mass(self): def evaluate_dry_mass(self): """Calculates and returns the rocket's dry mass. The dry mass is defined as the sum of the motor's dry mass and the - rocket mass without motor. The function returns an object - of the Function class and is defined as a function of time. + rocket mass without motor. Returns ------- - self.total_mass : Function - Function of time expressing the total mass of the rocket, - defined as the sum of the propellant mass and the rocket - mass without propellant. + self.dry_mass : float + Rocket's dry mass (Rocket + Motor) (kg) """ # Make sure there is a motor associated with the rocket if self.motor is None: print("Please associate this rocket with a motor!") return False - # Calculate total dry mass: motor (without propellant) + rocket mass self.dry_mass = self.mass + self.motor.dry_mass return self.dry_mass From 9ab1629ae831a729627ab45b5c9db65c67cfbf03 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 02:18:59 -0500 Subject: [PATCH 03/20] TST: update tests expected values after last bug solving. - See the last 3 commits to understand. --- tests/test_flight.py | 4 ++-- tests/unit/test_utilities.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_flight.py b/tests/test_flight.py index a2e42281c..f9a939667 100644 --- a/tests/test_flight.py +++ b/tests/test_flight.py @@ -691,7 +691,7 @@ def test_accelerations(flight_calisto_custom_wind, flight_time, expected_values) ("t_initial", (0, 0, 0)), ("out_of_rail_time", (0, 2.248727, 25.703072)), ("apogee_time", (-13.209436, 16.05115, -0.000257)), - ("t_final", (5, 2, -5.334289)), + ("t_final", (5, 2, -5.65998)), ], ) def test_velocities(flight_calisto_custom_wind, flight_time, expected_values): @@ -729,7 +729,7 @@ def test_velocities(flight_calisto_custom_wind, flight_time, expected_values): ("t_initial", (1.6542528, 0.65918, -0.067107)), ("out_of_rail_time", (5.05334, 2.01364, -1.7541)), ("apogee_time", (2.35291, -1.8275, -0.87851)), - ("t_final", (0, 0, 141.42421)), + ("t_final", (0, 0, 159.2212)), ], ) def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_values): diff --git a/tests/unit/test_utilities.py b/tests/unit/test_utilities.py index 68dbc3d7f..b07064906 100644 --- a/tests/unit/test_utilities.py +++ b/tests/unit/test_utilities.py @@ -153,7 +153,7 @@ def test_fin_flutter_analysis(flight_calisto_custom_wind): assert np.isclose(flutter_mach(np.inf), 1.0048188594647927, atol=5e-3) assert np.isclose(safety_factor(0), 64.78797, atol=5e-3) assert np.isclose(safety_factor(10), 2.1948620401502072, atol=5e-3) - assert np.isclose(safety_factor(np.inf), 65.40588722032527, atol=5e-3) + assert np.isclose(safety_factor(np.inf), 61.64222220469224, atol=5e-3) def test_flutter_prints(flight_calisto_custom_wind): From feb1791b46245422f26cb60e9c5a0ee221d769c9 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 02:39:01 -0500 Subject: [PATCH 04/20] TST: Add assertion for impact velocity in test_bella_lui_rocket.py --- tests/acceptance/test_bella_lui_rocket.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/acceptance/test_bella_lui_rocket.py b/tests/acceptance/test_bella_lui_rocket.py index 69669c66f..a1297e1fb 100644 --- a/tests/acceptance/test_bella_lui_rocket.py +++ b/tests/acceptance/test_bella_lui_rocket.py @@ -242,3 +242,10 @@ def drogue_trigger(p, h, y): assert ( abs(apogee_time_measured - apogee_time_simulated) / apogee_time_simulated < 0.02 ) + # Guarantee the impact velocity is within 30% of the real data. + # Use the last 5 real points to avoid outliers + assert ( + abs(test_flight.impact_velocity - np.mean(vert_vel_kalt[-5:])) + / abs(test_flight.impact_velocity) + < 0.30 + ) From 0829a779ed27c567f50b670a8106c1a3277d4de2 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 02:39:31 -0500 Subject: [PATCH 05/20] DEV: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ab36ca79..0aec0e229 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [v1.2.2] - 2024-03-dd + +You can install this version by running `pip install rocketpy==1.2.2` + +- BUG: wrong rocket mass in parachute u dot method [#569](https://github.com/RocketPy-Team/RocketPy/pull/569) ## [v1.2.1] - 2024-02-22 From 014e633aae170219da41e9b2156b917fcafafde3 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:37:08 -0500 Subject: [PATCH 06/20] ENH: Add new stability margin properties to Flight class --- rocketpy/simulation/flight.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 326b5db04..c642d1123 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -2426,6 +2426,26 @@ def min_stability_margin(self): """Minimum stability margin.""" return self.stability_margin(self.min_stability_margin_time) + @property + def initial_stability_margin(self): + """Stability margin at time 0. + + Returns + ------- + float + """ + return self.stability_margin(0) + + @property + def out_of_rail_stability_margin(self): + """Stability margin at the time the rocket leaves the rail. + + Returns + ------- + float + """ + return self.stability_margin(self.out_of_rail_time) + # Reynolds Number @funcify_method("Time (s)", "Reynolds Number", "spline", "zero") def reynolds_number(self): From 6779a3e191430ee1491d598af80184eb3c4de791 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:37:20 -0500 Subject: [PATCH 07/20] TST: Add tests for initial and out-of-rail stability margins in Flight class --- tests/unit/test_flight.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 87249cf44..e6ab6b8b8 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -258,3 +258,31 @@ def test_get_controller_observed_variables(flight_calisto_air_brakes): obs_vars = flight_calisto_air_brakes.get_controller_observed_variables() assert isinstance(obs_vars, list) assert len(obs_vars) == 0 + + +def test_initial_stability_margin(flight_calisto_custom_wind): + """Test the initial_stability_margin method of the Flight class. + + Parameters + ---------- + flight_calisto_custom_wind : rocketpy.Flight + """ + res = flight_calisto_custom_wind.initial_stability_margin + assert isinstance(res, float) + assert res == flight_calisto_custom_wind.stability_margin(0) + assert np.isclose(res, 2.05, atol=0.1) + + +def test_out_of_rail_stability_margin(flight_calisto_custom_wind): + """Test the out_of_rail_stability_margin method of the Flight class. + + Parameters + ---------- + flight_calisto_custom_wind : rocketpy.Flight + """ + res = flight_calisto_custom_wind.out_of_rail_stability_margin + assert isinstance(res, float) + assert res == flight_calisto_custom_wind.stability_margin( + flight_calisto_custom_wind.out_of_rail_time + ) + assert np.isclose(res, 2.14, atol=0.1) From 55e5cc25cd70c367ea62898fe1fc0e198cb2a4e5 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:38:42 -0500 Subject: [PATCH 08/20] MNT: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff32bd8d..6345de19f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- ENH: Add new stability margin properties to Flight class [#572](https://github.com/RocketPy-Team/RocketPy/pull/572) - ENH: adds `Function.remove_outliers` method [#554](https://github.com/RocketPy-Team/RocketPy/pull/554) ### Changed From c8b26b63f28271d415e79b9e566ed7ee01af1d47 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:43:22 -0500 Subject: [PATCH 09/20] DOC: Add initial and out-of-rail stability margins to Flight class documentation --- rocketpy/simulation/flight.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index c642d1123..ff0ee1840 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -426,6 +426,10 @@ class Flight: Rocket's static margin during flight in calibers. Flight.stability_margin : Function Rocket's stability margin during flight, in calibers. + Flight.initial_stability_margin : float + Rocket's initial stability margin in calibers. + Flight.out_of_rail_stability_margin : float + Rocket's stability margin in calibers when it leaves the rail. Flight.stream_velocity_x : Function Freestream velocity x (East) component, in m/s, as a function of time. Can be called or accessed as array. @@ -2429,7 +2433,7 @@ def min_stability_margin(self): @property def initial_stability_margin(self): """Stability margin at time 0. - + Returns ------- float @@ -2439,7 +2443,7 @@ def initial_stability_margin(self): @property def out_of_rail_stability_margin(self): """Stability margin at the time the rocket leaves the rail. - + Returns ------- float From 864fd6d0355232989c4fe7ded5b132e500515ef2 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 9 Mar 2024 07:09:40 -0500 Subject: [PATCH 10/20] REL: Update version to 1.2.2 --- CHANGELOG.md | 2 +- docs/conf.py | 2 +- docs/user/installation.rst | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aec0e229..9732e38f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed -## [v1.2.2] - 2024-03-dd +## [v1.2.2] - 2024-03-09 You can install this version by running `pip install rocketpy==1.2.2` diff --git a/docs/conf.py b/docs/conf.py index f2397dfa8..75f7a18a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ author = "RocketPy Team" # The full version, including alpha/beta/rc tags -release = "1.2.1" +release = "1.2.2" # -- General configuration --------------------------------------------------- diff --git a/docs/user/installation.rst b/docs/user/installation.rst index 16dec6886..328a251a4 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==1.2.1 + pip install rocketpy==1.2.2 Optional Installation Method: ``conda`` diff --git a/setup.py b/setup.py index 73d989de4..96f21a8d3 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setuptools.setup( name="rocketpy", - version="1.2.1", + version="1.2.2", install_requires=necessary_require, extras_require={ "env_analysis": env_analysis_require, From 1aecd491ddc1f455046567164b23fe1c8bd9a0be Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:37:08 -0500 Subject: [PATCH 11/20] ENH: Add new stability margin properties to Flight class --- rocketpy/simulation/flight.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 326b5db04..c642d1123 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -2426,6 +2426,26 @@ def min_stability_margin(self): """Minimum stability margin.""" return self.stability_margin(self.min_stability_margin_time) + @property + def initial_stability_margin(self): + """Stability margin at time 0. + + Returns + ------- + float + """ + return self.stability_margin(0) + + @property + def out_of_rail_stability_margin(self): + """Stability margin at the time the rocket leaves the rail. + + Returns + ------- + float + """ + return self.stability_margin(self.out_of_rail_time) + # Reynolds Number @funcify_method("Time (s)", "Reynolds Number", "spline", "zero") def reynolds_number(self): From c36d27f9e3d1cd3c0487d1f68d39ade8fa39572a Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:37:20 -0500 Subject: [PATCH 12/20] TST: Add tests for initial and out-of-rail stability margins in Flight class --- tests/unit/test_flight.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 87249cf44..e6ab6b8b8 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -258,3 +258,31 @@ def test_get_controller_observed_variables(flight_calisto_air_brakes): obs_vars = flight_calisto_air_brakes.get_controller_observed_variables() assert isinstance(obs_vars, list) assert len(obs_vars) == 0 + + +def test_initial_stability_margin(flight_calisto_custom_wind): + """Test the initial_stability_margin method of the Flight class. + + Parameters + ---------- + flight_calisto_custom_wind : rocketpy.Flight + """ + res = flight_calisto_custom_wind.initial_stability_margin + assert isinstance(res, float) + assert res == flight_calisto_custom_wind.stability_margin(0) + assert np.isclose(res, 2.05, atol=0.1) + + +def test_out_of_rail_stability_margin(flight_calisto_custom_wind): + """Test the out_of_rail_stability_margin method of the Flight class. + + Parameters + ---------- + flight_calisto_custom_wind : rocketpy.Flight + """ + res = flight_calisto_custom_wind.out_of_rail_stability_margin + assert isinstance(res, float) + assert res == flight_calisto_custom_wind.stability_margin( + flight_calisto_custom_wind.out_of_rail_time + ) + assert np.isclose(res, 2.14, atol=0.1) From 36fb6b5dca9343dbb9d3483546ade4ec9a8709f7 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:38:42 -0500 Subject: [PATCH 13/20] MNT: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8476ad6..3586d54e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- ENH: Add new stability margin properties to Flight class [#572](https://github.com/RocketPy-Team/RocketPy/pull/572) - ENH: adds `Function.remove_outliers` method [#554](https://github.com/RocketPy-Team/RocketPy/pull/554) ### Changed From 1264a67b8f5e7386f650132ccef67406dd2446a0 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 7 Mar 2024 18:43:22 -0500 Subject: [PATCH 14/20] DOC: Add initial and out-of-rail stability margins to Flight class documentation --- rocketpy/simulation/flight.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index c642d1123..ff0ee1840 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -426,6 +426,10 @@ class Flight: Rocket's static margin during flight in calibers. Flight.stability_margin : Function Rocket's stability margin during flight, in calibers. + Flight.initial_stability_margin : float + Rocket's initial stability margin in calibers. + Flight.out_of_rail_stability_margin : float + Rocket's stability margin in calibers when it leaves the rail. Flight.stream_velocity_x : Function Freestream velocity x (East) component, in m/s, as a function of time. Can be called or accessed as array. @@ -2429,7 +2433,7 @@ def min_stability_margin(self): @property def initial_stability_margin(self): """Stability margin at time 0. - + Returns ------- float @@ -2439,7 +2443,7 @@ def initial_stability_margin(self): @property def out_of_rail_stability_margin(self): """Stability margin at the time the rocket leaves the rail. - + Returns ------- float From ee01e9dd9f159d463e642465244ebe08f4038565 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Thu, 21 Mar 2024 15:29:08 -0400 Subject: [PATCH 15/20] MNT: Fix initial stability margin calculation in Flight class --- rocketpy/simulation/flight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index ff0ee1840..344a831fb 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -2438,7 +2438,7 @@ def initial_stability_margin(self): ------- float """ - return self.stability_margin(0) + return self.stability_margin(self.time[0]) @property def out_of_rail_stability_margin(self): From c9cc0deb6393ed38b3dc18b9d0c8dc59ee840e42 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 22 Mar 2024 06:12:41 -0400 Subject: [PATCH 16/20] ENH: add new stability Flight properties to flight prints --- rocketpy/prints/flight_prints.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/rocketpy/prints/flight_prints.py b/rocketpy/prints/flight_prints.py index 5b1f9b914..ec702a12c 100644 --- a/rocketpy/prints/flight_prints.py +++ b/rocketpy/prints/flight_prints.py @@ -67,8 +67,7 @@ def initial_conditions(self): self.flight.w1(0), self.flight.w2(0), self.flight.w3(0) ) ) - - return None + print(f"Initial Stability Margin: {self.flight.initial_stability_margin:.3f} c") def numerical_integration_settings(self): """Prints out the Numerical Integration settings available about the @@ -150,9 +149,8 @@ def out_of_rail_conditions(self): print("\nRail Departure State\n") print("Rail Departure Time: {:.3f} s".format(self.flight.out_of_rail_time)) print( - "Rail Departure Velocity: {:.3f} m/s".format( - self.flight.out_of_rail_velocity - ) + "Rail Departure Stability Margin: " + f"{self.flight.out_of_rail_stability_margin:.3f} c" ) print( "Rail Departure Stability Margin: {:.3f} c".format( @@ -408,9 +406,13 @@ def stability_margin(self): about the flight.""" print("\nStability Margin\n") print( - "Maximum Stability Margin: {:.3f} c at {:.2f} s".format( - self.flight.max_stability_margin, self.flight.max_stability_margin_time - ) + f"Initial Stability Margin: {self.flight.initial_stability_margin:.3f} c " + f"at {self.flight.time[0]:.2f} s" + ) + print( + "Out of Rail Stability Margin: " + f"{self.flight.out_of_rail_stability_margin:.3f} c " + f"at {self.flight.out_of_rail_time:.2f} s" ) print( "Minimum Stability Margin: {:.3f} c at {:.2f} s".format( From 2456b119baccafff2df4fcea5a9a37fa0b8d55d4 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 22 Mar 2024 06:14:47 -0400 Subject: [PATCH 17/20] BUG: refactor the initial_conditions _FlightPrints.method to use correct initial time - This also adopts f-string interpolation for the initial_conditions prints --- rocketpy/prints/flight_prints.py | 41 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/rocketpy/prints/flight_prints.py b/rocketpy/prints/flight_prints.py index ec702a12c..b9be3f6ee 100644 --- a/rocketpy/prints/flight_prints.py +++ b/rocketpy/prints/flight_prints.py @@ -33,39 +33,36 @@ def initial_conditions(self): ------- None """ - print("\nInitial Conditions\n") - # Post-process results - if self.flight.post_processed is False: - self.flight.post_process() + t_init = self.flight.time[0] + + print(f"Initial time: {t_init:.3f} s") print( - "Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format( - self.flight.x(0), self.flight.y(0), self.flight.z(0) - ) + f"Position - x: {self.flight.x(t_init):.2f} m | " + f"y: {self.flight.y(t_init):.2f} m | " + f"z: {self.flight.z(t_init):.2f} m" ) print( - "Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format( - self.flight.vx(0), self.flight.vy(0), self.flight.vz(0) - ) + f"Velocity - Vx: {self.flight.vx(t_init):.2f} m/s | " + f"Vy: {self.flight.vy(t_init):.2f} m/s | " + f"Vz: {self.flight.vz(t_init):.2f} m/s" ) print( - "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format( - self.flight.e0(0), - self.flight.e1(0), - self.flight.e2(0), - self.flight.e3(0), - ) + f"Attitude (quaternions) - e0: {self.flight.e0(t_init):.3f} | " + f"e1: {self.flight.e1(t_init):.3f} | " + f"e2: {self.flight.e2(t_init):.3f} | " + f"e3: {self.flight.e3(t_init):.3f}" ) print( - "Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format( - self.flight.phi(0), self.flight.theta(0), self.flight.psi(0) - ) + f"Euler Angles - Spin φ : {self.flight.phi(t_init):.2f}° | " + f"Nutation θ: {self.flight.theta(t_init):.2f}° | " + f"Precession ψ: {self.flight.psi(t_init):.2f}°" ) print( - "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format( - self.flight.w1(0), self.flight.w2(0), self.flight.w3(0) - ) + f"Angular Velocity - ω1: {self.flight.w1(t_init):.2f} rad/s | " + f"ω2: {self.flight.w2(t_init):.2f} rad/s | " + f"ω3: {self.flight.w3(t_init):.2f} rad/s" ) print(f"Initial Stability Margin: {self.flight.initial_stability_margin:.3f} c") From 844b36399c3b2e5cfa927df37c9a2b80f77bcd4f Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 22 Mar 2024 07:25:09 -0400 Subject: [PATCH 18/20] MNT: Refactor flight prints module - F-strings as faster in python, let's use them! - Added a docstring to the module! - useless comments were removed - docstrings were updated - no code line is longer than 88 columns (pylint and flake8 restriction) - improve parachute printing --- CHANGELOG.md | 1 + rocketpy/prints/flight_prints.py | 369 ++++++++++++++----------------- 2 files changed, 165 insertions(+), 205 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3586d54e3..f366f5abd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- MNT: Refactor flight prints module [#758](https://github.com/RocketPy-Team/RocketPy/pull/578) - DOC: Convert CompareFlights example notebooks to .rst files [#576](https://github.com/RocketPy-Team/RocketPy/pull/576) - ENH: Optional argument to show the plot in Function.compare_plots [#563](https://github.com/RocketPy-Team/RocketPy/pull/563) diff --git a/rocketpy/prints/flight_prints.py b/rocketpy/prints/flight_prints.py index b9be3f6ee..4a31b5c28 100644 --- a/rocketpy/prints/flight_prints.py +++ b/rocketpy/prints/flight_prints.py @@ -1,3 +1,17 @@ +"""rocketpy/prints/flight_prints.py + +This module contains the _FlightPrints class, which is responsible for printing +flight information in a user-friendly manner. + +Notes +----- +- This module does not have any external dependencies (avoid importing libraries). +- We assume that all flight information is valid, no validation checks is run. +- Avoid calculating values here, only print the values from the Flight class. +- The _FlightPrints is a private class, it is subjected to change without notice. +""" + + class _FlightPrints: """Class that holds prints methods for Flight class. @@ -5,7 +19,6 @@ class _FlightPrints: ---------- _FlightPrints.flight : Flight Flight object that will be used for the prints. - """ def __init__( @@ -24,10 +37,11 @@ def __init__( None """ self.flight = flight - return None def initial_conditions(self): - """Prints all initial conditions data available about the Flight. + """Prints initial conditions data available about the flight, including + position, velocity, attitude, euler angles, angular velocity, and + stability margin. Returns ------- @@ -67,187 +81,146 @@ def initial_conditions(self): print(f"Initial Stability Margin: {self.flight.initial_stability_margin:.3f} c") def numerical_integration_settings(self): - """Prints out the Numerical Integration settings available about the - flight. + """Prints out the numerical integration settings available about the + flight, this includes the maximum allowed flight time, maximum allowed + time step, and other settings. Returns ------- None """ - print("\nNumerical Integration Settings\n") - print("Maximum Allowed Flight Time: {:f} s".format(self.flight.max_time)) - print("Maximum Allowed Time Step: {:f} s".format(self.flight.max_time_step)) - print("Minimum Allowed Time Step: {:e} s".format(self.flight.min_time_step)) - print("Relative Error Tolerance: ", self.flight.rtol) - print("Absolute Error Tolerance: ", self.flight.atol) - print("Allow Event Overshoot: ", self.flight.time_overshoot) - print("Terminate Simulation on Apogee: ", self.flight.terminate_on_apogee) - print("Number of Time Steps Used: ", len(self.flight.time_steps)) + print(f"Maximum Allowed Flight Time: {self.flight.max_time:.2f} s") + print(f"Maximum Allowed Time Step: {self.flight.max_time_step:.2f} s") + print(f"Minimum Allowed Time Step: {self.flight.min_time_step:.2e} s") + print(f"Relative Error Tolerance: {self.flight.rtol}") + print(f"Absolute Error Tolerance: {self.flight.atol}") + print(f"Allow Event Overshoot: {self.flight.time_overshoot}") + print(f"Terminate Simulation on Apogee: {self.flight.terminate_on_apogee}") + print(f"Number of Time Steps Used: {len(self.flight.time_steps)}") print( - "Number of Derivative Functions Evaluation: ", - sum(self.flight.function_evaluations_per_time_step), + "Number of Derivative Functions Evaluation: " + f"{sum(self.flight.function_evaluations_per_time_step)}" ) + avg_func_evals_per_step = sum( + self.flight.function_evaluations_per_time_step + ) / len(self.flight.time_steps) print( - "Average Function Evaluations per Time Step: {:3f}".format( - sum(self.flight.function_evaluations_per_time_step) - / len(self.flight.time_steps) - ) + f"Average Function Evaluations per Time Step: {avg_func_evals_per_step:.3f}" ) - return None - def surface_wind_conditions(self): - """Prints out the Surface Wind Conditions available about the flight. + """Prints out the Surface Wind Conditions for the flight. Returns ------- None """ - if self.flight.post_processed is False: - self.flight.post_process() print("\nSurface Wind Conditions\n") - print( - "Frontal Surface Wind Speed: {:.2f} m/s".format( - self.flight.frontal_surface_wind - ) - ) - print( - "Lateral Surface Wind Speed: {:.2f} m/s".format( - self.flight.lateral_surface_wind - ) - ) - - return None + print(f"Frontal Surface Wind Speed: {self.flight.frontal_surface_wind:.2f} m/s") + print(f"Lateral Surface Wind Speed: {self.flight.lateral_surface_wind:.2f} m/s") def launch_rail_conditions(self): - """Prints out the Launch Rail Conditions available about the flight. + """Prints out the Launch Rail Conditions available about the flight, + including the length, inclination, and heading of the launch rail. Returns ------- None """ - print("\nLaunch Rail\n") - print("Launch Rail Length:", self.flight.rail_length, " m") - print("Launch Rail Inclination: {:.2f}°".format(self.flight.inclination)) - print("Launch Rail Heading: {:.2f}°".format(self.flight.heading)) - return None + print(f"Launch Rail Length: {self.flight.rail_length} m") + print(f"Launch Rail Inclination: {self.flight.inclination:.2f}°") + print(f"Launch Rail Heading: {self.flight.heading:.2f}°") def out_of_rail_conditions(self): - """Prints out the Out of Rail Conditions available about the flight. + """Prints out the Out of Rail Conditions available about the flight, + including the time, velocity, stability margin, angle of attack, thrust + to weight ratio, and Reynolds number. Returns ------- None """ - if self.flight.post_processed is False: - self.flight.post_process() print("\nRail Departure State\n") - print("Rail Departure Time: {:.3f} s".format(self.flight.out_of_rail_time)) + print(f"Rail Departure Time: {self.flight.out_of_rail_time:.3f} s") + print(f"Rail Departure Velocity: {self.flight.out_of_rail_velocity:.3f} m/s") print( "Rail Departure Stability Margin: " f"{self.flight.out_of_rail_stability_margin:.3f} c" ) print( - "Rail Departure Stability Margin: {:.3f} c".format( - self.flight.stability_margin(self.flight.out_of_rail_time) - ) - ) - print( - "Rail Departure Angle of Attack: {:.3f}°".format( - self.flight.angle_of_attack(self.flight.out_of_rail_time) - ) + "Rail Departure Angle of Attack: " + f"{self.flight.angle_of_attack(self.flight.out_of_rail_time):.3f}°" ) print( - "Rail Departure Thrust-Weight Ratio: {:.3f}".format( - self.flight.rocket.thrust_to_weight(self.flight.out_of_rail_time) - ) + "Rail Departure Thrust-Weight Ratio: " + f"{self.flight.rocket.thrust_to_weight(self.flight.out_of_rail_time):.3f}" ) print( - "Rail Departure Reynolds Number: {:.3e}".format( - self.flight.reynolds_number(self.flight.out_of_rail_time) - ) + "Rail Departure Reynolds Number: " + f"{self.flight.reynolds_number(self.flight.out_of_rail_time):.3e}" ) - return None - def burn_out_conditions(self): - """Prints out the Burn Out Conditions available about the flight. + """Prints out the Burn Out Conditions available about the flight, + including the burn out time, altitude, speed, freestream speed, Mach + number, and kinetic energy. Returns ------- None """ print("\nBurn out State\n") - print("Burn out time: {:.3f} s".format(self.flight.rocket.motor.burn_out_time)) + print(f"Burn out time: {self.flight.rocket.motor.burn_out_time:.3f} s") print( - "Altitude at burn out: {:.3f} m (AGL)".format( - self.flight.z(self.flight.rocket.motor.burn_out_time) - - self.flight.env.elevation - ) + "Altitude at burn out: " + f"{self.flight.altitude(self.flight.rocket.motor.burn_out_time):.3f} " + "m (AGL)" ) print( - "Rocket velocity at burn out: {:.3f} m/s".format( - self.flight.speed(self.flight.rocket.motor.burn_out_time) - ) + "Rocket speed at burn out: " + f"{self.flight.speed(self.flight.rocket.motor.burn_out_time):.3f} m/s" ) - print( - "Freestream velocity at burn out: {:.3f} m/s".format( - ( - self.flight.stream_velocity_x( - self.flight.rocket.motor.burn_out_time - ) - ** 2 - + self.flight.stream_velocity_y( - self.flight.rocket.motor.burn_out_time - ) - ** 2 - + self.flight.stream_velocity_z( - self.flight.rocket.motor.burn_out_time - ) - ** 2 - ) - ** 0.5 - ) + + stream_velocity = self.flight.free_stream_speed( + self.flight.rocket.motor.burn_out_time ) + print(f"Freestream velocity at burn out: {stream_velocity:.3f} m/s") + print( - "Mach Number at burn out: {:.3f}".format( - self.flight.mach_number(self.flight.rocket.motor.burn_out_time) - ) + "Mach Number at burn out: " + f"{self.flight.mach_number(self.flight.rocket.motor.burn_out_time):.3f}" ) print( - "Kinetic energy at burn out: {:.3e} J".format( - self.flight.kinetic_energy(self.flight.rocket.motor.burn_out_time) - ) + "Kinetic energy at burn out: " + f"{self.flight.kinetic_energy(self.flight.rocket.motor.burn_out_time):.3e} " + "J" ) - return None - def apogee_conditions(self): - """Prints out the Apogee Conditions available about the flight. + """Prints out the Apogee Conditions available about the flight, + including the apogee time, altitude, freestream speed, latitude, and + longitude. Returns ------- None """ - if self.flight.post_processed is False: - self.flight.post_process() print("\nApogee State\n") + print(f"Apogee Time: {self.flight.apogee_time:.3f} s") print( - "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( - self.flight.apogee, self.flight.apogee - self.flight.env.elevation - ) + f"Apogee Altitude: {self.flight.apogee:.3f} m (ASL) | " + f"{self.flight.altitude(self.flight.apogee_time):.3f} m (AGL)" ) - print("Apogee Time: {:.3f} s".format(self.flight.apogee_time)) + print(f"Apogee Freestream Speed: {self.flight.apogee_freestream_speed:.3f} m/s") + print(f"Apogee X position: {self.flight.x(self.flight.apogee_time):.3f} m") + print(f"Apogee Y position: {self.flight.y(self.flight.apogee_time):.3f} m") + print(f"Apogee latitude: {self.flight.latitude(self.flight.apogee_time):.7f}°") print( - "Apogee Freestream Speed: {:.3f} m/s".format( - self.flight.apogee_freestream_speed - ) + f"Apogee longitude: {self.flight.longitude(self.flight.apogee_time):.7f}°" ) - return None - def events_registered(self): """Prints out the Events Registered available about the flight. @@ -255,8 +228,6 @@ def events_registered(self): ------- None """ - if self.flight.post_processed is False: - self.flight.post_process() print("\nParachute Events\n") if len(self.flight.parachute_events) == 0: print("No Parachute Events Were Triggered.") @@ -264,24 +235,17 @@ def events_registered(self): trigger_time = event[0] parachute = event[1] open_time = trigger_time + parachute.lag - velocity = self.flight.free_stream_speed(open_time) + speed = self.flight.free_stream_speed(open_time) altitude = self.flight.z(open_time) name = parachute.name.title() - print(name + " Ejection Triggered at: {:.3f} s".format(trigger_time)) - print(name + " Parachute Inflated at: {:.3f} s".format(open_time)) - print( - name - + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( - velocity - ) - ) + print(f"Parachute: {name}") + print(f"\tEjection time: {trigger_time:.3f} s") + print(f"\tInflation time: {open_time:.3f} s") + print(f"\tFreestream speed at inflation: {speed:.3f} m/s") print( - name - + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( - altitude - self.flight.env.elevation - ) + f"\tAltitude at inflation: {altitude:.3f} m (ASL) | " + f"{self.flight.altitude(trigger_time):.3f} m (AGL)" ) - return None def impact_conditions(self): """Prints out the Impact Conditions available about the flight. @@ -290,27 +254,36 @@ def impact_conditions(self): ------- None """ - if self.flight.post_processed is False: - self.flight.post_process() if len(self.flight.impact_state) != 0: print("\nImpact Conditions\n") - print("X Impact: {:.3f} m".format(self.flight.x_impact)) - print("Y Impact: {:.3f} m".format(self.flight.y_impact)) - print("Latitude: {:.7f}°".format(self.flight.latitude(self.flight.t_final))) + print(f"Time of impact: {self.flight.t_final:.3f} s") + print(f"X impact: {self.flight.x_impact:.3f} m") + print(f"Y impact: {self.flight.y_impact:.3f} m") print( - "Longitude: {:.7f}°".format(self.flight.longitude(self.flight.t_final)) + f"Altitude impact: {self.flight.z(self.flight.t_final):.3f} m (ASL) | " + f"{self.flight.altitude(self.flight.t_final):.3f} m (AGL) " + ) + print(f"Latitude: {self.flight.latitude(self.flight.t_final):.7f}°") + print(f"Longitude: {self.flight.longitude(self.flight.t_final):.7f}°") + print(f"Vertical velocity at impact: {self.flight.impact_velocity:.3f} m/s") + num_parachute_events = sum( + 1 + for event in self.flight.parachute_events + if event[0] < self.flight.t_final + ) + print( + f"Number of parachutes triggered until impact: {num_parachute_events}" ) - print("Time of Impact: {:.3f} s".format(self.flight.t_final)) - print("Velocity at Impact: {:.3f} m/s".format(self.flight.impact_velocity)) elif self.flight.terminate_on_apogee is False: print("End of Simulation") - t_final = self.flight.solution[-1][0] - print("Time: {:.3f} s".format(t_final)) - print("Altitude: {:.3f} m".format(self.flight.solution[-1][3])) - print("Latitude: {:.3f}°".format(self.flight.latitude(t_final))) - print("Longitude: {:.3f}°".format(self.flight.longitude(t_final))) - - return None + t_final = self.flight.time[-1] + print(f"Time: {t_final:.3f} s") + print( + f"Altitude: {self.flight.z(t_final)} m (ASL) | " + f"{self.flight.altitude(t_final):.3f} m (AGL)" + ) + print(f"Latitude: {self.flight.latitude(t_final):.7f}°") + print(f"Longitude: {self.flight.longitude(t_final):.7f}°") def maximum_values(self): """Prints out the Maximum Values available about the flight. @@ -321,53 +294,44 @@ def maximum_values(self): """ print("\nMaximum Values\n") print( - "Maximum Speed: {:.3f} m/s at {:.2f} s".format( - self.flight.max_speed, self.flight.max_speed_time - ) + f"Maximum Speed: {self.flight.max_speed:.3f} m/s " + f"at {self.flight.max_speed_time:.2f} s" ) print( - "Maximum Mach Number: {:.3f} Mach at {:.2f} s".format( - self.flight.max_mach_number, self.flight.max_mach_number_time - ) + f"Maximum Mach Number: {self.flight.max_mach_number:.3f} Mach " + f"at {self.flight.max_mach_number_time:.2f} s" ) print( - "Maximum Reynolds Number: {:.3e} at {:.2f} s".format( - self.flight.max_reynolds_number, self.flight.max_reynolds_number_time - ) + f"Maximum Reynolds Number: {self.flight.max_reynolds_number:.3e} " + f"at {self.flight.max_reynolds_number_time:.2f} s" ) print( - "Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format( - self.flight.max_dynamic_pressure, self.flight.max_dynamic_pressure_time - ) + f"Maximum Dynamic Pressure: {self.flight.max_dynamic_pressure:.3e} Pa " + f"at {self.flight.max_dynamic_pressure_time:.2f} s" ) print( - "Maximum Acceleration During Motor Burn: {:.3f} m/s² at {:.2f} s".format( - self.flight.max_acceleration_power_on, - self.flight.max_acceleration_power_on_time, - ) + "Maximum Acceleration During Motor Burn: " + f"{self.flight.max_acceleration_power_on:.3f} m/s² " + f"at {self.flight.max_acceleration_power_on_time:.2f} s" ) print( - "Maximum Gs During Motor Burn: {:.3f} g at {:.2f} s".format( - self.flight.max_acceleration_power_on / self.flight.env.standard_g, - self.flight.max_acceleration_power_on_time, - ) + "Maximum Gs During Motor Burn: " + f"{self.flight.max_acceleration_power_on / self.flight.env.standard_g:.3f} " + f"g at {self.flight.max_acceleration_power_on_time:.2f} s" ) print( - "Maximum Acceleration After Motor Burn: {:.3f} m/s² at {:.2f} s".format( - self.flight.max_acceleration_power_off, - self.flight.max_acceleration_power_off_time, - ) + "Maximum Acceleration After Motor Burn: " + f"{self.flight.max_acceleration_power_off:.3f} m/s² " + f"at {self.flight.max_acceleration_power_off_time:.2f} s" ) print( - "Maximum Gs After Motor Burn: {:.3f} g at {:.2f} s".format( - self.flight.max_acceleration_power_off / self.flight.env.standard_g, - self.flight.max_acceleration_power_off_time, - ) + "Maximum Gs After Motor Burn: " + f"{self.flight.max_acceleration_power_off / self.flight.env.standard_g:.3f}" + f" Gs at {self.flight.max_acceleration_power_off_time:.2f} s" ) print( - "Maximum Stability Margin: {:.3f} c at {:.2f} s".format( - self.flight.max_stability_margin, self.flight.max_stability_margin_time - ) + f"Maximum Stability Margin: {self.flight.max_stability_margin:.3f} c " + f"at {self.flight.max_stability_margin_time:.2f} s" ) if ( @@ -377,30 +341,35 @@ def maximum_values(self): pass else: print( - "Maximum Upper Rail Button Normal Force: {:.3f} N".format( - self.flight.max_rail_button1_normal_force - ) + "Maximum Upper Rail Button Normal Force: " + f"{self.flight.max_rail_button1_normal_force:.3f} N" ) print( - "Maximum Upper Rail Button Shear Force: {:.3f} N".format( - self.flight.max_rail_button1_shear_force - ) + "Maximum Upper Rail Button Shear Force: " + f"{self.flight.max_rail_button1_shear_force:.3f} N" ) print( - "Maximum Lower Rail Button Normal Force: {:.3f} N".format( - self.flight.max_rail_button2_normal_force - ) + "Maximum Lower Rail Button Normal Force: " + f"{self.flight.max_rail_button2_normal_force:.3f} N" ) print( - "Maximum Lower Rail Button Shear Force: {:.3f} N".format( - self.flight.max_rail_button2_shear_force - ) + "Maximum Lower Rail Button Shear Force: " + f"{self.flight.max_rail_button2_shear_force:.3f} N" ) - return None def stability_margin(self): - """Prints out the maximum and minimum stability margin available - about the flight.""" + """Prints out the stability margins of the flight at different times. + + This method prints the following: Initial Stability Margin, Out of Rail + Stability Margin, Maximum Stability Margin, and Minimum Stability Margin + + Each stability margin is printed along with the time it occurred. + + Notes + ----- + The stability margin is typically measured in calibers (c), where 1 + caliber is the diameter of the rocket. + """ print("\nStability Margin\n") print( f"Initial Stability Margin: {self.flight.initial_stability_margin:.3f} c " @@ -412,62 +381,52 @@ def stability_margin(self): f"at {self.flight.out_of_rail_time:.2f} s" ) print( - "Minimum Stability Margin: {:.3f} c at {:.2f} s".format( - self.flight.min_stability_margin, self.flight.min_stability_margin_time - ) + f"Maximum Stability Margin: {self.flight.max_stability_margin:.3f} c " + f"at {self.flight.max_stability_margin_time:.2f} s" + ) + print( + f"Minimum Stability Margin: {self.flight.min_stability_margin:.3f} c " + f"at {self.flight.min_stability_margin_time:.2f} s" ) - return None def all(self): - """Prints out all data available about the Flight. + """Prints out all data available about the Flight. This method invokes + all other print methods in the class. Returns ------- None """ - # Print initial conditions self.initial_conditions() print() - # Print surface wind conditions self.surface_wind_conditions() print() - # Print launch rail orientation self.launch_rail_conditions() print() - # Print out of rail conditions self.out_of_rail_conditions() print() - # Print burn out conditions self.burn_out_conditions() print() - # Print apogee conditions self.apogee_conditions() print() - # Print events registered self.events_registered() print() - # Print impact conditions self.impact_conditions() print() - # Print stability margin self.stability_margin() print() - # Print maximum values self.maximum_values() print() - # Print Numerical Integration Information self.numerical_integration_settings() print() - - return None From ad189a8a16c4e7f7f2039105e1e6630d226ed12e Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 23 Mar 2024 22:26:15 -0400 Subject: [PATCH 19/20] MNT: fix number in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f366f5abd..a0d80e57c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- MNT: Refactor flight prints module [#758](https://github.com/RocketPy-Team/RocketPy/pull/578) +- MNT: Refactor flight prints module [#579](https://github.com/RocketPy-Team/RocketPy/pull/579) - DOC: Convert CompareFlights example notebooks to .rst files [#576](https://github.com/RocketPy-Team/RocketPy/pull/576) - ENH: Optional argument to show the plot in Function.compare_plots [#563](https://github.com/RocketPy-Team/RocketPy/pull/563) From 2a85ce8a40584d7a8f5cd95da12d3fed15fbc64f Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 23 Mar 2024 22:26:56 -0400 Subject: [PATCH 20/20] ENH: add altitude ASL and AGL to burn out state prints --- rocketpy/prints/flight_prints.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rocketpy/prints/flight_prints.py b/rocketpy/prints/flight_prints.py index 4a31b5c28..06db442f8 100644 --- a/rocketpy/prints/flight_prints.py +++ b/rocketpy/prints/flight_prints.py @@ -175,6 +175,7 @@ def burn_out_conditions(self): print(f"Burn out time: {self.flight.rocket.motor.burn_out_time:.3f} s") print( "Altitude at burn out: " + f"{self.flight.z(self.flight.rocket.motor.burn_out_time):.3f} m (ASL) | " f"{self.flight.altitude(self.flight.rocket.motor.burn_out_time):.3f} " "m (AGL)" )