From 72df17ad6d1284553c7e24174c12d46a81f81ed9 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sun, 17 Dec 2023 17:39:29 -0300 Subject: [PATCH 01/25] ENH: Add savetxt method to Function class for saving data to a text file --- rocketpy/mathutils/function.py | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 251d58199..808a790b3 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -2817,6 +2817,76 @@ def compose(self, func, extrapolate=False): extrapolation=self.__extrapolation__, ) + def savetxt( + self, + filename, + lower=None, + upper=None, + samples=None, + fmt="%.6f", + delimiter=",", + newline="\n", + encoding=None, + ): + """Save a Function object to a text file. The first line is the header + with inputs and outputs. The following lines are the data. The text file + can have any extension, but it is recommended to use .csv or .txt. + + Parameters + ---------- + filename : str + The name of the file to be saved, with the extension. + lower : float or int, optional + The lower bound of the range for which data is to be generated. + This is required if the source is a callable function. + upper : float or int, optional + The upper bound of the range for which data is to be generated. + This is required if the source is a callable function. + samples : int, optional + The number of sample points to generate within the specified range. + This is required if the source is a callable function. + fmt : str, optional + The format string for each line of the file, by default "%.6f". + delimiter : str, optional + The string used to separate values, by default ",". + newline : str, optional + The string used to separate lines in the file, by default "\n". + encoding : str, optional + The encoding to be used for the file, by default None (which means + using the system default encoding). + + Raises + ------ + ValueError + Raised if `lower`, `upper`, and `samples` are not provided when + the source is a callable function. These parameters are necessary + to generate the data points for saving. + """ + # create the header + header_line = delimiter.join(self.__inputs__ + self.__outputs__) + + # create the datapoints + if callable(self.source): + if lower is None or upper is None or samples is None: + raise ValueError( + "If the source is a callable, lower, upper and samples" + + " must be provided." + ) + # Generate the data points using the callable + x = np.linspace(lower, upper, samples) + data_points = np.column_stack((x, self.source(x))) + else: + # If the source is already an array, use it as is + data_points = self.source + + if lower and upper and samples: + data_points = self.set_discrete(lower, upper, samples).source + + # export to a file + with open(filename, "w", encoding=encoding) as file: + file.write(header_line + newline) + np.savetxt(file, data_points, fmt=fmt, delimiter=delimiter, newline=newline) + @staticmethod def _check_user_input( source, From 69317c3dba69b705efe02e546dc5d50e693080ff Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sun, 17 Dec 2023 17:40:09 -0300 Subject: [PATCH 02/25] TST: add unit tests to cover the savetxt method --- tests/conftest.py | 15 +++++++++ tests/unit/test_function.py | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 0b72cd03d..d11652540 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1166,3 +1166,18 @@ def func_2d_from_csv(): source="tests/fixtures/function/2d.csv", ) return func + + +@pytest.fixture +def lambda_quad_func(): + """Create a lambda function based on a string. + + Returns + ------- + Function + A lambda function based on a string. + """ + func = lambda x: x**2 + return Function( + source=func, + ) diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index e41d30f04..de8a1f4ac 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -1,3 +1,9 @@ +"""Unit tests for the Function class. Each method in tis module tests an +individual method of the Function class. The tests are made on both the +expected behaviour and the return instances.""" + +import os + import numpy as np import pytest @@ -158,3 +164,63 @@ def test_get_value_opt(x, y, z): func = Function(source, interpolation="shepard", extrapolation="natural") assert isinstance(func.get_value_opt(x, y), float) assert np.isclose(func.get_value_opt(x, y), z, atol=1e-6) + + +@pytest.mark.parametrize( + "func", + [ + "linearly_interpolated_func", + "spline_interpolated_func", + "func_2d_from_csv", + "lambda_quad_func", + ], +) +def test_savetxt(request, func): + """Test the savetxt method of various Function objects. + + This test function verifies that the `savetxt` method correctly writes the + function's data to a CSV file and that a new function object created from + this file has the same data as the original function object. + + Notes + ----- + The test performs the following steps: + 1. It invokes the `savetxt` method of the given function object. + 2. It then reads this file to create a new function object. + 3. The test asserts that the data of the new function matches the original. + 4. Finally, the test cleans up by removing the created CSV file. + + Raises + ------ + AssertionError + If the `savetxt` method fails to save the file, or if the data of the + newly read function does not match the data of the original function. + """ + func = request.getfixturevalue(func) + assert ( + func.savetxt( + filename="test_func.csv", + lower=0, + upper=9, + samples=10, + fmt="%.6f", + delimiter=",", + newline="\n", + encoding=None, + ) + is None + ), "Couldn't save the file using the Function.savetxt method." + + read_func = Function( + "test_func.csv", interpolation="linear", extrapolation="natural" + ) + if callable(func.source): + source = np.column_stack( + (np.linspace(0, 9, 10), func.source(np.linspace(0, 9, 10))) + ) + assert np.allclose(source, read_func.source) + else: + assert np.allclose(func.source, read_func.source) + + # clean up the file + os.remove("test_func.csv") From 0247289824dbcfff9ff2c5e055e97ced682e5032 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sun, 17 Dec 2023 17:57:59 -0300 Subject: [PATCH 03/25] DOC: Add export functionality to Function class usage docs --- docs/user/function.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/user/function.rst b/docs/user/function.rst index ed78fdd13..3b4b66ed0 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -406,6 +406,38 @@ Here we have shown that we can integrate the gaussian function over a defined in # Compare the function with the integral Function.compare_plots([f, f_int], lower=-4, upper=4) +e. Export to a text file (CSV or TXT) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since rocketpy version 1.2.0, the ``Function`` class supports exporting the +source data to a CSV or TXT file. This is accomplished by the method +:meth:`rocketpy.Function.savetxt` and allows for easy data exportation for +further analysis. + +Let's export the gaussian function to a CSV file: + +.. jupyter-execute:: + + # Define the gaussian function + def gaussian(x): + return 1 / np.sqrt(2*np.pi) * np.exp(-x**2/2) + + f = Function(gaussian, inputs="x", outputs="f(x)") + + # Export to CSV + f.savetxt("gaussian.csv", lower=-4, upper=4, samples=20, fmt="%.2f") + + # Read the CSV file + import pandas as pd + pd.read_csv("gaussian.csv") + + +.. jupyter-execute:: + + # Delete the CSV file + import os + os.remove("gaussian.csv") + ........ This guide shows some of the capabilities of the ``Function`` class, but there are many other functionalities to enhance your analysis. Do not hesitate in tanking a look at the documentation :class:`rocketpy.Function`. From e3b468d68156a76970580ec0992cee916ee59049 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sun, 17 Dec 2023 17:59:32 -0300 Subject: [PATCH 04/25] MNT: updates the CHANGELOG after #514 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8cbb9ed6..4f5a606e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ straightforward as possible. ### Added -- +- ENH: adds new Function.savetxt method [#514](https://github.com/RocketPy-Team/RocketPy/pull/514) ### Changed @@ -59,8 +59,8 @@ You can install this version by running `pip install rocketpy==1.1.3` ### Fixed -- FIX: Broken Function.get_value_opt for N-Dimensional Functions [#492](https://github.com/RocketPy-Team/RocketPy/pull/492) -- FIX: Never ending Flight simulations when using a GenericMotor [#497](https://github.com/RocketPy-Team/RocketPy/pull/497) +- FIX: Broken Function.get_value_opt for N-Dimensional Functions [#492](https://github.com/RocketPy-Team/RocketPy/pull/492) +- FIX: Never ending Flight simulations when using a GenericMotor [#497](https://github.com/RocketPy-Team/RocketPy/pull/497) ## [v1.1.2] - 2023-11-25 From 56600895be53be2c17ed65cf01a04e0cebd7ac3d Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Thu, 18 Jan 2024 20:29:15 -0300 Subject: [PATCH 05/25] BUG: parachute pressures not being set before all info. --- rocketpy/plots/flight_plots.py | 1 - rocketpy/simulation/flight.py | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rocketpy/plots/flight_plots.py b/rocketpy/plots/flight_plots.py index 62c751a55..8871a7b4e 100644 --- a/rocketpy/plots/flight_plots.py +++ b/rocketpy/plots/flight_plots.py @@ -817,7 +817,6 @@ def pressure_signals(self): if len(self.flight.parachute_events) > 0: for parachute in self.flight.rocket.parachutes: print("\nParachute: ", parachute.name) - self.flight._calculate_pressure_signal() parachute.noise_signal_function() parachute.noisy_pressure_signal_function() parachute.clean_pressure_signal_function() diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index d843127ab..50ec95ca6 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1065,6 +1065,7 @@ def __init__( ) self.t_final = self.t + self._calculate_pressure_signal() if verbose: print("Simulation Completed at Time: {:3.4f} s".format(self.t)) @@ -3089,8 +3090,8 @@ def export_pressures(self, file_name, time_step): else: for parachute in self.rocket.parachutes: for t in time_points: - p_cl = parachute.clean_pressure_signal(t) - p_ns = parachute.noisy_pressure_signal(t) + p_cl = parachute.clean_pressure_signal_function(t) + p_ns = parachute.noisy_pressure_signal_function(t) file.write(f"{t:f}, {p_cl:.5f}, {p_ns:.5f}\n") # We need to save only 1 parachute data break From 60cae906f3de656a84ee9a250717594a70f5b7ab Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Thu, 18 Jan 2024 20:52:58 -0300 Subject: [PATCH 06/25] MNT: update CHANGELOG with fixes. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fe9d9f7e..1ef7658a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ straightforward as possible. ### Fixed +- BUG: Parachute Pressures not being Set before All Info. [#534](https://github.com/RocketPy-Team/RocketPy/pull/534) - BUG: Invalid Arguments on Two Dimensional Discretize. [#521](https://github.com/RocketPy-Team/RocketPy/pull/521) ## [v1.1.4] - 2023-12-07 From a3e49a24b593c0548fa209cc28f2cd80aef2d9ab Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 19 Jan 2024 23:17:02 -0500 Subject: [PATCH 07/25] TST: Add unit test for the Flight.export_pressures method --- .pylintrc | 1 + tests/test_flight.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/.pylintrc b/.pylintrc index 97a3abeb1..2611847f2 100644 --- a/.pylintrc +++ b/.pylintrc @@ -435,6 +435,7 @@ disable=raw-checker-failed, use-implicit-booleaness-not-comparison-to-zero, no-else-return, inconsistent-return-statements, + unspecified-encoding, # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/tests/test_flight.py b/tests/test_flight.py index f658acba1..90bad8942 100644 --- a/tests/test_flight.py +++ b/tests/test_flight.py @@ -949,3 +949,31 @@ def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_v test.M2(t), test.M3(t), ), f"Assertion error for moment vector at {expected_attr}." + + +def test_export_pressures(flight_calisto_robust): + """Tests if the method Flight.export_pressures is working as intended. + + Parameters + ---------- + flight_calisto_robust : Flight + Flight object to be tested. See the conftest.py file for more info + regarding this pytest fixture. + """ + file_name = "pressures.csv" + time_step = 0.5 + parachute = flight_calisto_robust.rocket.parachutes[0] + + flight_calisto_robust.export_pressures(file_name, time_step) + + with open(file_name, "r") as file: + contents = file.read() + + expected_data = "" + for t in np.arange(0, flight_calisto_robust.t_final, time_step): + p_cl = parachute.clean_pressure_signal_function(t) + p_ns = parachute.noisy_pressure_signal_function(t) + expected_data += f"{t:f}, {p_cl:.5f}, {p_ns:.5f}\n" + + assert contents == expected_data + os.remove(file_name) From 696e709945fea9d957a2615ef72c5f570218260f Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 19 Jan 2024 23:29:44 -0500 Subject: [PATCH 08/25] ENH: no longer mutates the Function obj when using savetxt() --- rocketpy/mathutils/function.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 15e47818e..f1b295e99 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -5,12 +5,12 @@ """ import warnings from collections.abc import Iterable +from copy import deepcopy from inspect import signature from pathlib import Path import matplotlib.pyplot as plt import numpy as np -from copy import deepcopy from scipy import integrate, linalg, optimize try: @@ -2918,7 +2918,9 @@ def savetxt( data_points = self.source if lower and upper and samples: - data_points = self.set_discrete(lower, upper, samples).source + data_points = self.set_discrete( + lower, upper, samples, mutate_self=False + ).source # export to a file with open(filename, "w", encoding=encoding) as file: From 5c1267f4db57198b997f1b993c2df0de332dc954 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Fri, 15 Dec 2023 20:16:18 +0100 Subject: [PATCH 09/25] ENH: add barometric_height to environment --- rocketpy/environment/environment.py | 56 ++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 63d24fa9e..adf030806 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -111,6 +111,10 @@ class Environment: Environment.pressure : Function Air pressure in Pa as a function of altitude. Can be accessed as regular array, or called as a Function. See Function for more information. + Environment.barometric_height : Function + Geometric height above sea level in m as a function of pressure. Can be + accessed as regular array, or called as a Function. See Function for + more information. Environment.temperature : Function Air temperature in K as a function of altitude. Can be accessed as regular array, or called as a Function. See Function for more @@ -1315,6 +1319,8 @@ def process_standard_atmosphere(self): # Save temperature, pressure and wind profiles self.pressure = self.pressure_ISA + self.barometric_height = self.barometric_height_ISA + self.temperature = self.temperature_ISA self.wind_direction = Function( 0, @@ -1433,6 +1439,7 @@ def process_custom_atmosphere( if pressure is None: # Use standard atmosphere self.pressure = self.pressure_ISA + self.barometric_height = self.barometric_height_ISA else: # Use custom input self.pressure = Function( @@ -1441,6 +1448,11 @@ def process_custom_atmosphere( outputs="Pressure (Pa)", interpolation="linear", ) + self.barometric_height = self.pressure.inverse_function().set_discrete( + 0, max_expected_height, 1000, extrapolation="constant" + ) + self.barometric_height.set_inputs("Pressure (Pa)") + self.barometric_height.set_outputs("Height Above Sea Level (m)") # Check maximum height of custom pressure input if not callable(self.pressure.source): max_expected_height = max(self.pressure[-1, 0], max_expected_height) @@ -1605,6 +1617,12 @@ def process_windy_atmosphere(self, model="ECMWF"): outputs="Pressure (Pa)", interpolation="linear", ) + self.barometric_height = Function( + data_array[:, (0, 1)], + inputs="Pressure (Pa)", + outputs="Height Above Sea Level (m)", + interpolation="linear", + ) self.temperature = Function( data_array[:, (1, 2)], inputs="Height Above Sea Level (m)", @@ -1732,6 +1750,12 @@ def process_wyoming_sounding(self, file): outputs="Pressure (Pa)", interpolation="linear", ) + self.barometric_height = Function( + data_array[:, (0, 1)], + inputs="Pressure (Pa)", + outputs="Height Above Sea Level (m)", + interpolation="linear", + ) # Retrieve temperature from data array data_array[:, 2] = data_array[:, 2] + 273.15 # Converts C to K @@ -1845,6 +1869,7 @@ def process_noaaruc_sounding(self, file): # Extract pressure as a function of height pressure_array = [] + barometric_height_array = [] for line in lines: # Split line into columns columns = re.split(" +", line)[1:] @@ -1858,7 +1883,9 @@ def process_noaaruc_sounding(self, file): if max(columns) != 99999: # Save value pressure_array.append(columns) + barometric_height_array.append([columns[1], columns[0]]) pressure_array = np.array(pressure_array) + barometric_height_array = np.array(barometric_height_array) # Extract temperature as a function of height temperature_array = [] @@ -1905,6 +1932,14 @@ def process_noaaruc_sounding(self, file): outputs="Pressure (Pa)", interpolation="linear", ) + # construct barometric height array from pressure array + barometric_height_array[:, 0] = 10 * barometric_height_array[:, 0] + self.barometric_height = Function( + barometric_height_array, + inputs="Pressure (Pa)", + outputs="Height Above Sea Level (m)", + interpolation="linear", + ) # Convert 10*C to K and save values temperature_array[:, 1] = ( @@ -2274,6 +2309,12 @@ def process_forecast_reanalysis(self, file, dictionary): outputs="Pressure (Pa)", interpolation="linear", ) + self.barometric_height = Function( + data_array[:, (0, 1)], + inputs="Pressure (Pa)", + outputs="Height Above Sea Level (m)", + interpolation="linear", + ) self.temperature = Function( data_array[:, (1, 2)], inputs="Height Above Sea Level (m)", @@ -2803,6 +2844,12 @@ def select_ensemble_member(self, member=0): outputs="Pressure (Pa)", interpolation="linear", ) + self.barometric_height = Function( + data_array[:, (0, 1)], + inputs="Pressure (Pa)", + outputs="Height Above Sea Level (m)", + interpolation="linear", + ) self.temperature = Function( data_array[:, (1, 2)], inputs="Height Above Sea Level (m)", @@ -2965,7 +3012,14 @@ def pressure_function(h): outputs="Pressure (Pa)", ) - return None + # Save international standard atmosphere height by pressure profile + # and discretize it. This is done to speed up the calculations in the + # trajectory simulation. + self.barometric_height_ISA = self.pressure_ISA.inverse_function().set_discrete( + pressure[-1], pressure[0], 1000, extrapolation="constant" + ) + self.barometric_height_ISA.set_inputs("Pressure (Pa)") + self.barometric_height_ISA.set_outputs("Height Above Sea Level (m)") def calculate_density_profile(self): """Compute the density of the atmosphere as a function of From f301352528c25edeb5137e474fe4d70b3f8186d7 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Fri, 15 Dec 2023 20:24:26 +0100 Subject: [PATCH 10/25] ENH: get hAGL from barometric_height --- rocketpy/simulation/flight.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index c4a719e88..299ddaead 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -697,9 +697,8 @@ def __init__( parachute.noisy_pressure_signal.append([node.t, pressure + noise]) # Gets height above ground level considering noise hAGL = ( - self.env.pressure.find_input( + self.env.barometric_height( pressure + noise, - self.y_sol[2], ) - self.env.elevation ) @@ -1006,9 +1005,8 @@ def __init__( ) # Gets height above ground level considering noise hAGL = ( - self.env.pressure.find_input( + self.env.barometric_height( pressure + noise, - overshootable_node.y[2], ) - self.env.elevation ) From ead932275e386997276cc8d1ccd7d97d09dd4e61 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Fri, 15 Dec 2023 20:39:55 +0100 Subject: [PATCH 11/25] MNT: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4cd4be85..4c5ad55ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ straightforward as possible. - MNT: Add repr method to Parachute class [#490](https://github.com/RocketPy-Team/RocketPy/pull/490) - ENH: Function Reverse Arithmetic Priority [#488](https://github.com/RocketPy-Team/RocketPy/pull/488) - DOC: Update Header Related Docs +- ENH Precalculate Barometric Height [#511](https://github.com/RocketPy-Team/RocketPy/pull/511) ### Fixed From 07d4c338ba6b5b0fcdbdac3bda81f87b0d92b136 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 18 Dec 2023 04:11:29 +0100 Subject: [PATCH 12/25] DOC: improve black formatting --- rocketpy/simulation/flight.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 299ddaead..85f0edc25 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -697,9 +697,7 @@ def __init__( parachute.noisy_pressure_signal.append([node.t, pressure + noise]) # Gets height above ground level considering noise hAGL = ( - self.env.barometric_height( - pressure + noise, - ) + self.env.barometric_height(pressure + noise) - self.env.elevation ) if parachute.triggerfunc(pressure + noise, hAGL, self.y_sol): @@ -1005,9 +1003,7 @@ def __init__( ) # Gets height above ground level considering noise hAGL = ( - self.env.barometric_height( - pressure + noise, - ) + self.env.barometric_height(pressure + noise) - self.env.elevation ) From 2b06a7e42f0353deb3a53c2e9ffb1b1d8f66c926 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 18 Dec 2023 19:26:50 +0100 Subject: [PATCH 13/25] ENH: add extrapolation to barometric_height --- rocketpy/environment/environment.py | 88 ++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 9 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index adf030806..94857a961 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -1449,7 +1449,7 @@ def process_custom_atmosphere( interpolation="linear", ) self.barometric_height = self.pressure.inverse_function().set_discrete( - 0, max_expected_height, 1000, extrapolation="constant" + 0, max_expected_height, 100, extrapolation="constant" ) self.barometric_height.set_inputs("Pressure (Pa)") self.barometric_height.set_outputs("Height Above Sea Level (m)") @@ -1617,8 +1617,21 @@ def process_windy_atmosphere(self, model="ECMWF"): outputs="Pressure (Pa)", interpolation="linear", ) + # Linearly extrapolate pressure to ground level + columns = data_array[:, (0, 1)] + pressure_ground = np.array( + [ + [ + columns[1][0] + + (-columns[1][1] / (columns[0][1] - columns[1][1])) + * (columns[0][0] - columns[1][0]), + 0, + ] + ] + ) + bar_height = np.concatenate((pressure_ground, columns)) self.barometric_height = Function( - data_array[:, (0, 1)], + bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", @@ -1750,8 +1763,21 @@ def process_wyoming_sounding(self, file): outputs="Pressure (Pa)", interpolation="linear", ) + # Linearly extrapolate pressure to ground level + columns = data_array[:, (0, 1)] + pressure_ground = np.array( + [ + [ + columns[1][0] + + (-columns[1][1] / (columns[0][1] - columns[1][1])) + * (columns[0][0] - columns[1][0]), + 0, + ] + ] + ) + bar_height = np.concatenate((pressure_ground, columns)) self.barometric_height = Function( - data_array[:, (0, 1)], + bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", @@ -1932,10 +1958,28 @@ def process_noaaruc_sounding(self, file): outputs="Pressure (Pa)", interpolation="linear", ) - # construct barometric height array from pressure array + # Converts 10*hPa to Pa and save values barometric_height_array[:, 0] = 10 * barometric_height_array[:, 0] + # Linearly extrapolate pressure to ground level + pressure_ground = np.array( + [ + [ + barometric_height_array[1][0] + + ( + -barometric_height_array[1][1] + / ( + barometric_height_array[0][1] + - barometric_height_array[1][1] + ) + ) + * (barometric_height_array[0][0] - barometric_height_array[1][0]), + 0, + ] + ] + ) + bar_height = np.concatenate((pressure_ground, barometric_height_array)) self.barometric_height = Function( - barometric_height_array, + bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", @@ -2309,8 +2353,21 @@ def process_forecast_reanalysis(self, file, dictionary): outputs="Pressure (Pa)", interpolation="linear", ) + # Linearly extrapolate pressure to ground level + columns = data_array[:, (0, 1)] + pressure_ground = np.array( + [ + [ + columns[1][0] + + (-columns[1][1] / (columns[0][1] - columns[1][1])) + * (columns[0][0] - columns[1][0]), + 0, + ] + ] + ) + bar_height = np.concatenate((pressure_ground, columns)) self.barometric_height = Function( - data_array[:, (0, 1)], + bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", @@ -2844,11 +2901,24 @@ def select_ensemble_member(self, member=0): outputs="Pressure (Pa)", interpolation="linear", ) + # Linearly extrapolate pressure to ground level + columns = data_array[:, (0, 1)] + pressure_ground = np.array( + [ + [ + columns[1][0] + + (-columns[1][1] / (columns[0][1] - columns[1][1])) + * (columns[0][0] - columns[1][0]), + 0, + ] + ] + ) + bar_height = np.concatenate((pressure_ground, columns)) self.barometric_height = Function( - data_array[:, (0, 1)], + bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", - interpolation="linear", + interpolation="linearS", ) self.temperature = Function( data_array[:, (1, 2)], @@ -3016,7 +3086,7 @@ def pressure_function(h): # and discretize it. This is done to speed up the calculations in the # trajectory simulation. self.barometric_height_ISA = self.pressure_ISA.inverse_function().set_discrete( - pressure[-1], pressure[0], 1000, extrapolation="constant" + pressure[-1], pressure[0], 100, extrapolation="constant" ) self.barometric_height_ISA.set_inputs("Pressure (Pa)") self.barometric_height_ISA.set_outputs("Height Above Sea Level (m)") From e787b1180c15bba8cd21d8b78f074937d271a76d Mon Sep 17 00:00:00 2001 From: MateusStano Date: Mon, 18 Dec 2023 19:27:03 +0100 Subject: [PATCH 14/25] TST: add barometric_height to tests --- tests/test_environment.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_environment.py b/tests/test_environment.py index 7a23828c2..db80eea6d 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -20,6 +20,7 @@ def test_standard_atmosphere(mock_show, example_env): assert example_env.info() == None assert example_env.all_info() == None assert abs(example_env.pressure(0) - 101325.0) < 1e-8 + assert example_env.barometric_height(101325.0) < 1e-2 assert example_env.prints.print_earth_details() == None @@ -43,6 +44,7 @@ def test_custom_atmosphere(mock_show, example_env): ) assert example_env.all_info() == None assert abs(example_env.pressure(0) - 101325.0) < 1e-8 + assert example_env.barometric_height(101325.0) < 1e-2 assert abs(example_env.wind_velocity_x(0) - 5) < 1e-8 assert abs(example_env.temperature(100) - 300) < 1e-8 @@ -71,6 +73,7 @@ def test_wyoming_sounding_atmosphere(mock_show, example_env): pass assert example_env.all_info() == None assert abs(example_env.pressure(0) - 93600.0) < 1e-8 + assert example_env.barometric_height(example_env.pressure(0)) == 722.0 assert abs(example_env.wind_velocity_x(0) - -2.9005178894925043) < 1e-8 assert abs(example_env.temperature(100) - 291.75) < 1e-8 From 0551317841b232f224876e84ce383f3f02a71962 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Thu, 18 Jan 2024 19:12:11 +0100 Subject: [PATCH 15/25] ENH: add use of natural extrapolation to barometric_height --- rocketpy/environment/environment.py | 79 ++++------------------------- 1 file changed, 11 insertions(+), 68 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 94857a961..2778f19fb 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -1618,23 +1618,13 @@ def process_windy_atmosphere(self, model="ECMWF"): interpolation="linear", ) # Linearly extrapolate pressure to ground level - columns = data_array[:, (0, 1)] - pressure_ground = np.array( - [ - [ - columns[1][0] - + (-columns[1][1] / (columns[0][1] - columns[1][1])) - * (columns[0][0] - columns[1][0]), - 0, - ] - ] - ) - bar_height = np.concatenate((pressure_ground, columns)) + bar_height = data_array[:, (0, 1)] self.barometric_height = Function( bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", + extrapolation="natural", ) self.temperature = Function( data_array[:, (1, 2)], @@ -1764,23 +1754,13 @@ def process_wyoming_sounding(self, file): interpolation="linear", ) # Linearly extrapolate pressure to ground level - columns = data_array[:, (0, 1)] - pressure_ground = np.array( - [ - [ - columns[1][0] - + (-columns[1][1] / (columns[0][1] - columns[1][1])) - * (columns[0][0] - columns[1][0]), - 0, - ] - ] - ) - bar_height = np.concatenate((pressure_ground, columns)) + bar_height = data_array[:, (0, 1)] self.barometric_height = Function( bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", + extrapolation="natural", ) # Retrieve temperature from data array @@ -1960,29 +1940,12 @@ def process_noaaruc_sounding(self, file): ) # Converts 10*hPa to Pa and save values barometric_height_array[:, 0] = 10 * barometric_height_array[:, 0] - # Linearly extrapolate pressure to ground level - pressure_ground = np.array( - [ - [ - barometric_height_array[1][0] - + ( - -barometric_height_array[1][1] - / ( - barometric_height_array[0][1] - - barometric_height_array[1][1] - ) - ) - * (barometric_height_array[0][0] - barometric_height_array[1][0]), - 0, - ] - ] - ) - bar_height = np.concatenate((pressure_ground, barometric_height_array)) self.barometric_height = Function( - bar_height, + barometric_height_array, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", + extrapolation="natural", ) # Convert 10*C to K and save values @@ -2354,23 +2317,13 @@ def process_forecast_reanalysis(self, file, dictionary): interpolation="linear", ) # Linearly extrapolate pressure to ground level - columns = data_array[:, (0, 1)] - pressure_ground = np.array( - [ - [ - columns[1][0] - + (-columns[1][1] / (columns[0][1] - columns[1][1])) - * (columns[0][0] - columns[1][0]), - 0, - ] - ] - ) - bar_height = np.concatenate((pressure_ground, columns)) + bar_height = data_array[:, (0, 1)] self.barometric_height = Function( bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", interpolation="linear", + extrapolation="natural", ) self.temperature = Function( data_array[:, (1, 2)], @@ -2902,23 +2855,13 @@ def select_ensemble_member(self, member=0): interpolation="linear", ) # Linearly extrapolate pressure to ground level - columns = data_array[:, (0, 1)] - pressure_ground = np.array( - [ - [ - columns[1][0] - + (-columns[1][1] / (columns[0][1] - columns[1][1])) - * (columns[0][0] - columns[1][0]), - 0, - ] - ] - ) - bar_height = np.concatenate((pressure_ground, columns)) + bar_height = data_array[:, (0, 1)] self.barometric_height = Function( bar_height, inputs="Pressure (Pa)", outputs="Height Above Sea Level (m)", - interpolation="linearS", + interpolation="linear", + extrapolation="natural", ) self.temperature = Function( data_array[:, (1, 2)], From 4225405d6ae10f78608c95d6c0ec6ef467844d7e Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 20 Jan 2024 00:10:40 -0500 Subject: [PATCH 16/25] MNT: Add new variable barometric_height_ISA to .pylintrc --- .pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintrc b/.pylintrc index 97a3abeb1..d6116873f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -187,6 +187,7 @@ function-naming-style=snake_case # Good variable names which should always be accepted, separated by a comma. good-names=FlightPhases, WindroseAxes, + barometric_height_ISA, # Good variable names regexes, separated by a comma. If names match any regex, From 629dc5aae06e11c8cb8fa695b837f2fe325276de Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sun, 21 Jan 2024 14:37:32 -0500 Subject: [PATCH 17/25] REL: Update RocketPy version to 1.1.5 --- CHANGELOG.md | 8 ++++++++ docs/conf.py | 2 +- docs/user/installation.rst | 2 +- setup.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ef7658a1..8bc9307e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,14 @@ straightforward as possible. ### Fixed +- + +## [v1.1.5] - 2023-01-21 + +You can install this version by running `pip install rocketpy==1.1.5` + +### Fixed + - BUG: Parachute Pressures not being Set before All Info. [#534](https://github.com/RocketPy-Team/RocketPy/pull/534) - BUG: Invalid Arguments on Two Dimensional Discretize. [#521](https://github.com/RocketPy-Team/RocketPy/pull/521) diff --git a/docs/conf.py b/docs/conf.py index 8881e55db..43dbd2d5c 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.1.4" +release = "1.1.5" # -- General configuration --------------------------------------------------- diff --git a/docs/user/installation.rst b/docs/user/installation.rst index 6884720d4..0822bde7d 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.1.4 + pip install rocketpy==1.1.5 Optional Installation Method: ``conda`` diff --git a/setup.py b/setup.py index a2a219faa..326f276ec 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setuptools.setup( name="rocketpy", - version="1.1.4", + version="1.1.5", install_requires=necessary_require, extras_require={ "env_analysis": env_analysis_require, From d86c7601bc05047d8e7f4745f754c3cb870a7f44 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:46:44 -0500 Subject: [PATCH 18/25] MNT: Update year at the CHANGELOG.md Co-authored-by: Pedro Henrique Marinho Bressan <87212571+phmbressan@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc9307e0..4284ef644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ straightforward as possible. - -## [v1.1.5] - 2023-01-21 +## [v1.1.5] - 2024-01-21 You can install this version by running `pip install rocketpy==1.1.5` From c1216b25f71a446c5c121fe79a027ec35613c61a Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 22 Jan 2024 15:50:41 -0500 Subject: [PATCH 19/25] MNT: Add quaternion conversion functions to rocketpy.tools and update Flight --- .pylintrc | 1 + CHANGELOG.md | 1 + rocketpy/simulation/flight.py | 36 +++++++++----------- rocketpy/tools.py | 64 +++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 20 deletions(-) diff --git a/.pylintrc b/.pylintrc index 2611847f2..8cebb954c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -436,6 +436,7 @@ disable=raw-checker-failed, no-else-return, inconsistent-return-statements, unspecified-encoding, + no-member, # because we use funcify_method decorator # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4ac536b..a3f73a2fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ straightforward as possible. - MNT: Add repr method to Parachute class [#490](https://github.com/RocketPy-Team/RocketPy/pull/490) - ENH: Function Reverse Arithmetic Priority [#488](https://github.com/RocketPy-Team/RocketPy/pull/488) - DOC: Update Header Related Docs +- MNT: Encapsulate quaternion conversions [#537](https://github.com/RocketPy-Team/RocketPy/pull/537) ### Fixed diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 1c2549dfa..0b6bf3c10 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -11,7 +11,12 @@ from ..mathutils.vector_matrix import Matrix, Vector from ..plots.flight_plots import _FlightPlots from ..prints.flight_prints import _FlightPrints -from ..tools import find_closest +from ..tools import ( + find_closest, + quaternions_to_phi, + quaternions_to_precession, + quaternions_to_theta, +) class Flight: @@ -2295,33 +2300,24 @@ def lateral_attitude_angle(self): @funcify_method("Time (s)", "Precession Angle - ψ (°)", "spline", "constant") def psi(self): """Precession angle as a Function of time.""" - psi = (180 / np.pi) * ( - np.arctan2(self.e3[:, 1], self.e0[:, 1]) - + np.arctan2(-self.e2[:, 1], -self.e1[:, 1]) - ) # Precession angle - psi = np.column_stack([self.time, psi]) # Precession angle - return psi + psi = quaternions_to_precession( + self.e0.y_array, self.e1.y_array, self.e2.y_array, self.e3.y_array + ) + return np.column_stack([self.time, psi]) @funcify_method("Time (s)", "Spin Angle - φ (°)", "spline", "constant") def phi(self): """Spin angle as a Function of time.""" - phi = (180 / np.pi) * ( - np.arctan2(self.e3[:, 1], self.e0[:, 1]) - - np.arctan2(-self.e2[:, 1], -self.e1[:, 1]) - ) # Spin angle - phi = np.column_stack([self.time, phi]) # Spin angle - return phi + phi = quaternions_to_phi( + self.e0.y_array, self.e1.y_array, self.e2.y_array, self.e3.y_array + ) + return np.column_stack([self.time, phi]) @funcify_method("Time (s)", "Nutation Angle - θ (°)", "spline", "constant") def theta(self): """Nutation angle as a Function of time.""" - theta = ( - (180 / np.pi) - * 2 - * np.arcsin(-((self.e1[:, 1] ** 2 + self.e2[:, 1] ** 2) ** 0.5)) - ) # Nutation angle - theta = np.column_stack([self.time, theta]) # Nutation angle - return theta + theta = quaternions_to_theta(self.e1.y_array, self.e2.y_array) + return np.column_stack([self.time, theta]) # Fluid Mechanics variables # Freestream Velocity diff --git a/rocketpy/tools.py b/rocketpy/tools.py index 6e765ca7a..21ac0006a 100644 --- a/rocketpy/tools.py +++ b/rocketpy/tools.py @@ -3,6 +3,7 @@ import re from bisect import bisect_left +import numpy as np import pytz from cftime import num2pydate from packaging import version as packaging_version @@ -381,6 +382,69 @@ def check_requirement_version(module_name, version): return True +# Flight + + +def quaternions_to_precession(e0, e1, e2, e3): + """Calculates the Precession angle + + Parameters + ---------- + e0 : float + Euler parameter 0, must be between -1 and 1 + e1 : float + Euler parameter 1, must be between -1 and 1 + e2 : float + Euler parameter 2, must be between -1 and 1 + e3 : float + Euler parameter 3, must be between -1 and 1 + Returns + ------- + float + Euler Precession angle in degrees + """ + return (180 / np.pi) * (np.arctan2(e3, e0) + np.arctan2(-e2, -e1)) + + +def quaternions_to_phi(e0, e1, e2, e3): + """Calculates the Spin angle from quaternions. + + Parameters + ---------- + e0 : float + Euler parameter 0, must be between -1 and 1 + e1 : float + Euler parameter 1, must be between -1 and 1 + e2 : float + Euler parameter 2, must be between -1 and 1 + e3 : float + Euler parameter 3, must be between -1 and 1 + + Returns + ------- + float + Euler Spin angle in degrees + """ + return (180 / np.pi) * (np.arctan2(e3, e0) - np.arctan2(-e2, -e1)) + + +def quaternions_to_theta(e1, e2): + """Calculates the Nutation angle from quaternions. + + Parameters + ---------- + e1 : float + Euler parameter 1, must be between -1 and 1 + e2 : float + Euler parameter 2, must be between -1 and 1 + Returns + ------- + float + Euler Nutation angle in degrees + """ + return (180 / np.pi) * 2 * np.arcsin(-((e1**2 + e2**2) ** 0.5)) + + if __name__ == "__main__": import doctest From 7895a1e7594194e47f397b7fc194a654f7f2024f Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Mon, 22 Jan 2024 23:30:36 -0500 Subject: [PATCH 20/25] MNT: improve the low pass filter and document an example - Persist the inputs, outputs and title of the original Function - Improve the method docstring - Add an example to the .rst file --- CHANGELOG.md | 1 + docs/user/function.rst | 24 ++++++++++++++++++++++++ rocketpy/mathutils/function.py | 13 +++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4ac536b..d8b82a641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ straightforward as possible. - MNT: Add repr method to Parachute class [#490](https://github.com/RocketPy-Team/RocketPy/pull/490) - ENH: Function Reverse Arithmetic Priority [#488](https://github.com/RocketPy-Team/RocketPy/pull/488) - DOC: Update Header Related Docs +- MNT: improve the low pass filter and document an example [#538](https://github.com/RocketPy-Team/RocketPy/pull/538) ### Fixed diff --git a/docs/user/function.rst b/docs/user/function.rst index 3b4b66ed0..b95496991 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -438,6 +438,30 @@ Let's export the gaussian function to a CSV file: import os os.remove("gaussian.csv") +f. Filter data +~~~~~~~~~~~~~~ + +Since rocketpy version 1.2.0, the ``Function`` class supports filtering the +source data. This is accomplished by the method :meth:`rocketpy.Function.low_pass_filter` +and allows for easy data filtering for further analysis. + +Let's filter an example function: + +.. jupyter-execute:: + + x = np.linspace(-4, 4, 1000) + y = np.sin(x) + np.random.normal(0, 0.1, 1000) + + f = Function(list(zip(x, y)), inputs="x", outputs="f(x)") + + # Filter the function + f_filtered = f.low_pass_filter(0.5) + + # Compare the function with the filtered function + Function.compare_plots( + [(f, "Original"), (f_filtered, "Filtered")], lower=-4, upper=4 + ) + ........ This guide shows some of the capabilities of the ``Function`` class, but there are many other functionalities to enhance your analysis. Do not hesitate in tanking a look at the documentation :class:`rocketpy.Function`. diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index f1b295e99..52ac95ea2 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -1118,7 +1118,10 @@ def to_frequency_domain(self, lower, upper, sampling_frequency, remove_dc=True): ) def low_pass_filter(self, alpha, file_path=None): - """Implements a low pass filter with a moving average filter + """Implements a low pass filter with a moving average filter. This does + not mutate the original Function object, but returns a new one with the + filtered source. The filtered source is also saved to a CSV file if a + file path is given. Parameters ---------- @@ -1128,7 +1131,7 @@ def low_pass_filter(self, alpha, file_path=None): filtered function returned will match the function the smaller alpha is, the smoother the filtered function returned will be (but with a phase shift) - file_path : string + file_path : string, optional File path or file name of the CSV to save. Don't save any CSV if if no argument is passed. Initiated to None. @@ -1146,14 +1149,16 @@ def low_pass_filter(self, alpha, file_path=None): alpha * self.source[i] + (1 - alpha) * filtered_signal[i - 1] ) - # Save the new csv file with filtered data if isinstance(file_path, str): - np.savetxt(file_path, filtered_signal, delimiter=",") + self.savetxt(file_path) return Function( source=filtered_signal, + inputs=self.__inputs__, + outputs=self.__outputs__, interpolation=self.__interpolation__, extrapolation=self.__extrapolation__, + title=self.title, ) # Define all presentation methods From cdb42ece907a26f6b941eb404118607396214f76 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:43:16 -0500 Subject: [PATCH 21/25] MNT: rename quaternions conversion functions --- rocketpy/simulation/flight.py | 8 ++++---- rocketpy/tools.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 0b6bf3c10..1d9017152 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -13,9 +13,9 @@ from ..prints.flight_prints import _FlightPrints from ..tools import ( find_closest, - quaternions_to_phi, + quaternions_to_spin, quaternions_to_precession, - quaternions_to_theta, + quaternions_to_nutation, ) @@ -2308,7 +2308,7 @@ def psi(self): @funcify_method("Time (s)", "Spin Angle - φ (°)", "spline", "constant") def phi(self): """Spin angle as a Function of time.""" - phi = quaternions_to_phi( + phi = quaternions_to_spin( self.e0.y_array, self.e1.y_array, self.e2.y_array, self.e3.y_array ) return np.column_stack([self.time, phi]) @@ -2316,7 +2316,7 @@ def phi(self): @funcify_method("Time (s)", "Nutation Angle - θ (°)", "spline", "constant") def theta(self): """Nutation angle as a Function of time.""" - theta = quaternions_to_theta(self.e1.y_array, self.e2.y_array) + theta = quaternions_to_nutation(self.e1.y_array, self.e2.y_array) return np.column_stack([self.time, theta]) # Fluid Mechanics variables diff --git a/rocketpy/tools.py b/rocketpy/tools.py index 21ac0006a..bcff91fb8 100644 --- a/rocketpy/tools.py +++ b/rocketpy/tools.py @@ -406,7 +406,7 @@ def quaternions_to_precession(e0, e1, e2, e3): return (180 / np.pi) * (np.arctan2(e3, e0) + np.arctan2(-e2, -e1)) -def quaternions_to_phi(e0, e1, e2, e3): +def quaternions_to_spin(e0, e1, e2, e3): """Calculates the Spin angle from quaternions. Parameters @@ -428,7 +428,7 @@ def quaternions_to_phi(e0, e1, e2, e3): return (180 / np.pi) * (np.arctan2(e3, e0) - np.arctan2(-e2, -e1)) -def quaternions_to_theta(e1, e2): +def quaternions_to_nutation(e1, e2): """Calculates the Nutation angle from quaternions. Parameters From d3e7e318cc76697d6d1af46d13355cdb44608b48 Mon Sep 17 00:00:00 2001 From: MateusStano <69485049+MateusStano@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:35:38 -0300 Subject: [PATCH 22/25] Update tests/test_environment.py Co-authored-by: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> --- tests/test_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_environment.py b/tests/test_environment.py index db80eea6d..c0876248c 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -73,7 +73,7 @@ def test_wyoming_sounding_atmosphere(mock_show, example_env): pass assert example_env.all_info() == None assert abs(example_env.pressure(0) - 93600.0) < 1e-8 - assert example_env.barometric_height(example_env.pressure(0)) == 722.0 + assert abs(example_env.barometric_height(example_env.pressure(0)) - 722.0) < 1e-8 assert abs(example_env.wind_velocity_x(0) - -2.9005178894925043) < 1e-8 assert abs(example_env.temperature(100) - 291.75) < 1e-8 From 5349743e136eae1989c85697497b45891768c990 Mon Sep 17 00:00:00 2001 From: MateusStano <69485049+MateusStano@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:36:29 -0300 Subject: [PATCH 23/25] Update tests/test_environment.py Co-authored-by: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> --- tests/test_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_environment.py b/tests/test_environment.py index c0876248c..608fe7e90 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -44,7 +44,7 @@ def test_custom_atmosphere(mock_show, example_env): ) assert example_env.all_info() == None assert abs(example_env.pressure(0) - 101325.0) < 1e-8 - assert example_env.barometric_height(101325.0) < 1e-2 + assert abs(example_env.barometric_height(101325.0)) < 1e-2 assert abs(example_env.wind_velocity_x(0) - 5) < 1e-8 assert abs(example_env.temperature(100) - 300) < 1e-8 From 09d0b4be2eaa423ff5b92178258624a21351ec8d Mon Sep 17 00:00:00 2001 From: MateusStano <69485049+MateusStano@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:38:00 -0300 Subject: [PATCH 24/25] Update tests/test_environment.py Co-authored-by: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> --- tests/test_environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_environment.py b/tests/test_environment.py index 608fe7e90..3067530b7 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -20,7 +20,7 @@ def test_standard_atmosphere(mock_show, example_env): assert example_env.info() == None assert example_env.all_info() == None assert abs(example_env.pressure(0) - 101325.0) < 1e-8 - assert example_env.barometric_height(101325.0) < 1e-2 + assert abs(example_env.barometric_height(101325.0)) < 1e-2 assert example_env.prints.print_earth_details() == None From 0de47560d35ae2cfe7da292fad3116a29b58e309 Mon Sep 17 00:00:00 2001 From: MateusStano <69485049+MateusStano@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:38:22 -0300 Subject: [PATCH 25/25] Update rocketpy/environment/environment.py Co-authored-by: Gui-FernandesBR <63590233+Gui-FernandesBR@users.noreply.github.com> --- rocketpy/environment/environment.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 2778f19fb..8b8d0498a 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -3025,9 +3025,7 @@ def pressure_function(h): outputs="Pressure (Pa)", ) - # Save international standard atmosphere height by pressure profile - # and discretize it. This is done to speed up the calculations in the - # trajectory simulation. + # Discretize Function to speed up the trajectory simulation. self.barometric_height_ISA = self.pressure_ISA.inverse_function().set_discrete( pressure[-1], pressure[0], 100, extrapolation="constant" )