From b533722d13c9cb7903feac7a08ee186efa4120a0 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 6 Jul 2024 00:45:38 -0300 Subject: [PATCH 1/8] MNT: fix pylint errors in the `tests` module --- .github/workflows/linters.yml | 2 +- Makefile | 2 +- rocketpy/utilities.py | 1 + tests/acceptance/test_ndrt_2020_rocket.py | 28 ++- tests/fixtures/function/function_fixtures.py | 2 +- tests/fixtures/motor/solid_motor_fixtures.py | 25 ++ tests/integration/test_environment.py | 17 +- .../integration/test_environment_analysis.py | 4 +- tests/integration/test_flight.py | 170 +++++++------ tests/integration/test_function.py | 10 +- tests/integration/test_genericmotor.py | 5 +- tests/integration/test_hybridmotor.py | 3 +- tests/integration/test_monte_carlo.py | 3 +- tests/integration/test_plots.py | 5 +- tests/integration/test_rocket.py | 8 +- tests/unit/test_environment.py | 20 +- tests/unit/test_environment_analysis.py | 8 +- tests/unit/test_flight.py | 61 ++--- tests/unit/test_flight_time_nodes.py | 4 +- tests/unit/test_function.py | 151 ++++++----- tests/unit/test_genericmotor.py | 79 +++--- tests/unit/test_hybridmotor.py | 86 +++---- tests/unit/test_liquidmotor.py | 70 +++--- tests/unit/test_monte_carlo.py | 3 - tests/unit/test_plots.py | 6 +- tests/unit/test_rocket.py | 159 ++++++------ tests/unit/test_solidmotor.py | 2 +- tests/unit/test_tank.py | 235 +++++++++++------- tests/unit/test_tools_matrix.py | 76 +++--- tests/unit/test_tools_vector.py | 4 +- tests/unit/test_utilities.py | 39 +-- 31 files changed, 657 insertions(+), 631 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 0c20918ab..1eee717d2 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -40,4 +40,4 @@ jobs: run: flake8 rocketpy/ tests/ - name: Run pylint run: | - pylint rocketpy/ + pylint rocketpy/ tests/ diff --git a/Makefile b/Makefile index d0c198873..07c620ade 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ flake8: flake8 rocketpy/ tests/ pylint: - -pylint rocketpy --output=.pylint-report.txt + -pylint rocketpy/ tests/ --output=.pylint-report.txt build-docs: cd docs && $(PYTHON) -m pip install -r requirements.txt && make html diff --git a/rocketpy/utilities.py b/rocketpy/utilities.py index 3a724e46f..adb925eee 100644 --- a/rocketpy/utilities.py +++ b/rocketpy/utilities.py @@ -424,6 +424,7 @@ def _flutter_prints( print(f"Altitude of minimum Safety Factor: {altitude_min_sf:.3f} m (AGL)\n") +# TODO: deprecate and delete this function. Never used and now we have Monte Carlo. def create_dispersion_dictionary(filename): """Creates a dictionary with the rocket data provided by a .csv file. File should be organized in four columns: attribute_class, parameter_name, diff --git a/tests/acceptance/test_ndrt_2020_rocket.py b/tests/acceptance/test_ndrt_2020_rocket.py index 9cc66c897..aa4e737d4 100644 --- a/tests/acceptance/test_ndrt_2020_rocket.py +++ b/tests/acceptance/test_ndrt_2020_rocket.py @@ -64,19 +64,19 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): } # Environment conditions - Env23 = Environment( + env = Environment( gravity=9.81, latitude=41.775447, longitude=-86.572467, date=(2020, 2, 23, 16), elevation=206, ) - Env23.set_atmospheric_model( + env.set_atmospheric_model( type="Reanalysis", file="tests/fixtures/acceptance/NDRT_2020/ndrt_2020_weather_data_ERA5.nc", dictionary="ECMWF", ) - Env23.max_expected_height = 2000 + env.max_expected_height = 2000 # motor information L1395 = SolidMotor( @@ -134,13 +134,13 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): ) # Parachute set-up - def drogue_trigger(p, h, y): + def drogue_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate drogue when vz < 0 m/s. return True if y[5] < 0 else False - def main_trigger(p, h, y): + def main_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate main when vz < 0 m/s and z < 167.64 m (AGL) or 550 ft (AGL) @@ -164,17 +164,19 @@ def main_trigger(p, h, y): ) # Flight - Flight23 = Flight( + rocketpy_flight = Flight( rocket=NDRT2020, - environment=Env23, + environment=env, rail_length=parameters.get("rail_length")[0], inclination=parameters.get("inclination")[0], heading=parameters.get("heading")[0], ) - df_ndrt_rocketpy = pd.DataFrame(Flight23.z[:, :], columns=["Time", "Altitude"]) - df_ndrt_rocketpy["Vertical Velocity"] = Flight23.vz[:, 1] - # df_ndrt_rocketpy["Vertical Acceleration"] = Flight23.az[:, 1] - df_ndrt_rocketpy["Altitude"] -= Env23.elevation + df_ndrt_rocketpy = pd.DataFrame( + rocketpy_flight.z[:, :], columns=["Time", "Altitude"] + ) + df_ndrt_rocketpy["Vertical Velocity"] = rocketpy_flight.vz[:, 1] + # df_ndrt_rocketpy["Vertical Acceleration"] = rocketpy_flight.az[:, 1] + df_ndrt_rocketpy["Altitude"] -= env.elevation # Reading data from the flightData (sensors: Raven) df_ndrt_raven = pd.read_csv( @@ -205,14 +207,14 @@ def main_trigger(p, h, y): apogee_time_measured = df_ndrt_raven.loc[ df_ndrt_raven[" Altitude (Ft-AGL)"].idxmax(), " Time (s)" ] - apogee_time_simulated = Flight23.apogee_time + apogee_time_simulated = rocketpy_flight.apogee_time assert ( abs(max(df_ndrt_raven[" Altitude (m-AGL)"]) - max(df_ndrt_rocketpy["Altitude"])) / max(df_ndrt_raven[" Altitude (m-AGL)"]) < 0.015 ) - assert (max(velocity_raven_filt) - Flight23.max_speed) / max( + assert (max(velocity_raven_filt) - rocketpy_flight.max_speed) / max( velocity_raven_filt ) < 0.06 assert ( diff --git a/tests/fixtures/function/function_fixtures.py b/tests/fixtures/function/function_fixtures.py index 5b195c16b..7cba3699e 100644 --- a/tests/fixtures/function/function_fixtures.py +++ b/tests/fixtures/function/function_fixtures.py @@ -134,7 +134,7 @@ def lambda_quad_func(): Function A lambda function based on a string. """ - func = lambda x: x**2 # pylint: disable=unnecessary-lambda + func = lambda x: x**2 # pylint: disable=unnecessary-lambda-assignment return Function( source=func, ) diff --git a/tests/fixtures/motor/solid_motor_fixtures.py b/tests/fixtures/motor/solid_motor_fixtures.py index 587d5e970..eff7d65d5 100644 --- a/tests/fixtures/motor/solid_motor_fixtures.py +++ b/tests/fixtures/motor/solid_motor_fixtures.py @@ -117,3 +117,28 @@ def dimensionless_cesaroni_m1670(kg, m): # old name: dimensionless_motor coordinate_system_orientation="nozzle_to_combustion_chamber", ) return example_motor + + +@pytest.fixture +def dummy_empty_motor(): + # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant + # TODO: why don t we use these same values to create EmptyMotor class? + return SolidMotor( + thrust_source=1e-300, + burn_time=1e-10, + dry_mass=1.815, + dry_inertia=(0.125, 0.125, 0.002), + center_of_dry_mass_position=0.317, + grains_center_of_mass_position=0.397, + grain_number=5, + grain_separation=5 / 1000, + grain_density=1e-300, + grain_outer_radius=33 / 1000, + grain_initial_inner_radius=15 / 1000, + grain_initial_height=120 / 1000, + nozzle_radius=33 / 1000, + throat_radius=11 / 1000, + nozzle_position=0, + interpolation_method="linear", + coordinate_system_orientation="nozzle_to_combustion_chamber", + ) diff --git a/tests/integration/test_environment.py b/tests/integration/test_environment.py index 6f0d3fc09..3013d879c 100644 --- a/tests/integration/test_environment.py +++ b/tests/integration/test_environment.py @@ -100,7 +100,7 @@ def test_gefs_atmosphere( @patch("matplotlib.pyplot.show") def test_custom_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the custom atmosphere model in the environment object. Parameters @@ -127,7 +127,7 @@ def test_custom_atmosphere( @patch("matplotlib.pyplot.show") def test_standard_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the standard atmosphere model in the environment object. Parameters @@ -148,7 +148,7 @@ def test_standard_atmosphere( @patch("matplotlib.pyplot.show") def test_wyoming_sounding_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Asserts whether the Wyoming sounding model in the environment object behaves as expected with respect to some attributes such as pressure, barometric_height, wind_velocity and temperature. @@ -163,15 +163,14 @@ def test_wyoming_sounding_atmosphere( # TODO:: this should be added to the set_atmospheric_model() method as a # "file" option, instead of receiving the URL as a string. - URL = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" + url = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" # give it at least 5 times to try to download the file for i in range(5): try: - example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=URL) + example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=url) break - except: - time.sleep(1) # wait 1 second before trying again - pass + except Exception: # pylint: disable=broad-except + time.sleep(2**i) assert example_plain_env.all_info() is None assert abs(example_plain_env.pressure(0) - 93600.0) < 1e-8 assert ( @@ -227,7 +226,7 @@ def test_hiresw_ensemble_atmosphere( @patch("matplotlib.pyplot.show") def test_cmc_atmosphere( mock_show, example_spaceport_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the Ensemble model with the CMC file. Parameters diff --git a/tests/integration/test_environment_analysis.py b/tests/integration/test_environment_analysis.py index 17129e6f1..1be33fe96 100644 --- a/tests/integration/test_environment_analysis.py +++ b/tests/integration/test_environment_analysis.py @@ -10,7 +10,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, env_analysis): +def test_all_info(mock_show, env_analysis): # pylint: disable=unused-argument """Test the EnvironmentAnalysis.all_info() method, which already invokes several other methods. It is a good way to test the whole class in a first view. However, if it fails, it is hard to know which method is failing. @@ -32,7 +32,7 @@ def test_all_info(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_exports(mock_show, env_analysis): +def test_exports(mock_show, env_analysis): # pylint: disable=unused-argument """Check the export methods of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the files are correct, as this would require a lot of work and would be diff --git a/tests/integration/test_flight.py b/tests/integration/test_flight.py index fd8625435..8ac6e2936 100644 --- a/tests/integration/test_flight.py +++ b/tests/integration/test_flight.py @@ -11,7 +11,7 @@ @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused-argument +def test_all_info(mock_show, flight_calisto_robust): # pylint: disable=unused-argument """Test that the flight class is working as intended. This basically calls the all_info() method and checks if it returns None. It is not testing if the values are correct, but whether the method is working without errors. @@ -27,65 +27,61 @@ def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused- assert flight_calisto_robust.all_info() is None -def test_export_data(flight_calisto): - """Tests wether the method Flight.export_data is working as intended - - Parameters: - ----------- - flight_calisto : rocketpy.Flight - Flight object to be tested. See the conftest.py file for more info - regarding this pytest fixture. - """ - test_flight = flight_calisto - - # Basic export - test_flight.export_data("test_export_data_1.csv") - - # Custom export - test_flight.export_data( - "test_export_data_2.csv", - "z", - "vz", - "e1", - "w3", - "angle_of_attack", - time_step=0.1, - ) - - # Load exported files and fixtures and compare them - test_1 = np.loadtxt("test_export_data_1.csv", delimiter=",") - test_2 = np.loadtxt("test_export_data_2.csv", delimiter=",") - - # Delete files - os.remove("test_export_data_1.csv") - os.remove("test_export_data_2.csv") - - # Check if basic exported content matches data - assert np.allclose(test_flight.x[:, 0], test_1[:, 0], atol=1e-5) - assert np.allclose(test_flight.x[:, 1], test_1[:, 1], atol=1e-5) - assert np.allclose(test_flight.y[:, 1], test_1[:, 2], atol=1e-5) - assert np.allclose(test_flight.z[:, 1], test_1[:, 3], atol=1e-5) - assert np.allclose(test_flight.vx[:, 1], test_1[:, 4], atol=1e-5) - assert np.allclose(test_flight.vy[:, 1], test_1[:, 5], atol=1e-5) - assert np.allclose(test_flight.vz[:, 1], test_1[:, 6], atol=1e-5) - assert np.allclose(test_flight.e0[:, 1], test_1[:, 7], atol=1e-5) - assert np.allclose(test_flight.e1[:, 1], test_1[:, 8], atol=1e-5) - assert np.allclose(test_flight.e2[:, 1], test_1[:, 9], atol=1e-5) - assert np.allclose(test_flight.e3[:, 1], test_1[:, 10], atol=1e-5) - assert np.allclose(test_flight.w1[:, 1], test_1[:, 11], atol=1e-5) - assert np.allclose(test_flight.w2[:, 1], test_1[:, 12], atol=1e-5) - assert np.allclose(test_flight.w3[:, 1], test_1[:, 13], atol=1e-5) - - # Check if custom exported content matches data - time_points = np.arange(test_flight.t_initial, test_flight.t_final, 0.1) - assert np.allclose(time_points, test_2[:, 0], atol=1e-5) - assert np.allclose(test_flight.z(time_points), test_2[:, 1], atol=1e-5) - assert np.allclose(test_flight.vz(time_points), test_2[:, 2], atol=1e-5) - assert np.allclose(test_flight.e1(time_points), test_2[:, 3], atol=1e-5) - assert np.allclose(test_flight.w3(time_points), test_2[:, 4], atol=1e-5) - assert np.allclose( - test_flight.angle_of_attack(time_points), test_2[:, 5], atol=1e-5 - ) +class TestExportData: + """Tests the export_data method of the Flight class.""" + + def test_basic_export(self, flight_calisto): + """Tests basic export functionality""" + file_name = "test_export_data_1.csv" + flight_calisto.export_data(file_name) + self.validate_basic_export(flight_calisto, file_name) + os.remove(file_name) + + def test_custom_export(self, flight_calisto): + """Tests custom export functionality""" + file_name = "test_export_data_2.csv" + flight_calisto.export_data( + file_name, + "z", + "vz", + "e1", + "w3", + "angle_of_attack", + time_step=0.1, + ) + self.validate_custom_export(flight_calisto, file_name) + os.remove(file_name) + + def validate_basic_export(self, flight_calisto, file_name): + """Validates the basic export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + assert np.allclose(flight_calisto.x[:, 0], test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.x[:, 1], test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.y[:, 1], test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.z[:, 1], test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.vx[:, 1], test_data[:, 4], atol=1e-5) + assert np.allclose(flight_calisto.vy[:, 1], test_data[:, 5], atol=1e-5) + assert np.allclose(flight_calisto.vz[:, 1], test_data[:, 6], atol=1e-5) + assert np.allclose(flight_calisto.e0[:, 1], test_data[:, 7], atol=1e-5) + assert np.allclose(flight_calisto.e1[:, 1], test_data[:, 8], atol=1e-5) + assert np.allclose(flight_calisto.e2[:, 1], test_data[:, 9], atol=1e-5) + assert np.allclose(flight_calisto.e3[:, 1], test_data[:, 10], atol=1e-5) + assert np.allclose(flight_calisto.w1[:, 1], test_data[:, 11], atol=1e-5) + assert np.allclose(flight_calisto.w2[:, 1], test_data[:, 12], atol=1e-5) + assert np.allclose(flight_calisto.w3[:, 1], test_data[:, 13], atol=1e-5) + + def validate_custom_export(self, flight_calisto, file_name): + """Validates the custom export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + time_points = np.arange(flight_calisto.t_initial, flight_calisto.t_final, 0.1) + assert np.allclose(time_points, test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.z(time_points), test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.vz(time_points), test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.e1(time_points), test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.w3(time_points), test_data[:, 4], atol=1e-5) + assert np.allclose( + flight_calisto.angle_of_attack(time_points), test_data[:, 5], atol=1e-5 + ) def test_export_kml(flight_calisto_robust): @@ -106,14 +102,13 @@ def test_export_kml(flight_calisto_robust): ) # Load exported files and fixtures and compare them - test_1 = open("test_export_data_1.kml", "r") - - for row in test_1: - if row[:29] == " ": - r = row[29:-15] - r = r.split(",") - for i, j in enumerate(r): - r[i] = j.split(" ") + with open("test_export_data_1.kml", "r") as test_1: + for row in test_1: + if row[:29] == " ": + r = row[29:-15] + r = r.split(",") + for i, j in enumerate(r): + r[i] = j.split(" ") lon, lat, z, coords = [], [], [], [] for i in r: for j in i: @@ -122,9 +117,6 @@ def test_export_kml(flight_calisto_robust): lon.append(float(coords[i])) lat.append(float(coords[i + 1])) z.append(float(coords[i + 2])) - - # Delete temporary test file - test_1.close() os.remove("test_export_data_1.kml") assert np.allclose(test_flight.latitude[:, 1], lat, atol=1e-3) @@ -161,7 +153,9 @@ def test_export_pressures(flight_calisto_robust): @patch("matplotlib.pyplot.show") -def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): +def test_hybrid_motor_flight( + mock_show, calisto_hybrid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a hybrid motor. This test only validates that a flight simulation can be performed with a hybrid motor; it does not validate the results. @@ -186,7 +180,9 @@ def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): @patch("matplotlib.pyplot.show") -def test_liquid_motor_flight(mock_show, calisto_liquid_modded): +def test_liquid_motor_flight( + mock_show, calisto_liquid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a liquid motor. This test only validates that a flight simulation can be performed with a liquid motor; it does not validate the results. @@ -212,7 +208,9 @@ def test_liquid_motor_flight(mock_show, calisto_liquid_modded): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): +def test_time_overshoot( + mock_show, calisto_robust, example_spaceport_env +): # pylint: disable=unused-argument """Test the time_overshoot parameter of the Flight class. This basically calls the all_info() method for a simulation without time_overshoot and checks if it returns None. It is not testing if the values are correct, @@ -241,7 +239,9 @@ def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): @patch("matplotlib.pyplot.show") -def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust): +def test_simpler_parachute_triggers( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests different types of parachute triggers. This is important to ensure the code is working as intended, since the parachute triggers can have very different format definitions. It will add 3 parachutes using different @@ -313,8 +313,8 @@ def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust @patch("matplotlib.pyplot.show") -def test_rolling_flight( - mock_show, # pylint: disable: unused-argument +def test_rolling_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -352,8 +352,8 @@ def test_rolling_flight( @patch("matplotlib.pyplot.show") -def test_eccentricity_on_flight( - mock_show, # pylint: disable: unused-argument +def test_eccentricity_on_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -383,7 +383,9 @@ def test_eccentricity_on_flight( @patch("matplotlib.pyplot.show") -def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): +def test_air_brakes_flight( + mock_show, flight_calisto_air_brakes +): # pylint: disable=unused-argument """Test the flight of a rocket with air brakes. This test only validates that a flight simulation can be performed with air brakes; it does not validate the results. @@ -403,7 +405,9 @@ def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): @patch("matplotlib.pyplot.show") -def test_initial_solution(mock_show, example_plain_env, calisto_robust): +def test_initial_solution( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests the initial_solution option of the Flight class. This test simply simulates the flight using the initial_solution option and checks if the all_info method returns None. @@ -448,7 +452,9 @@ def test_initial_solution(mock_show, example_plain_env, calisto_robust): @patch("matplotlib.pyplot.show") -def test_empty_motor_flight(mock_show, example_plain_env, calisto_motorless): +def test_empty_motor_flight( + mock_show, example_plain_env, calisto_motorless +): # pylint: disable=unused-argument flight = Flight( rocket=calisto_motorless, environment=example_plain_env, diff --git a/tests/integration/test_function.py b/tests/integration/test_function.py index 7b6f204eb..a7e3144e5 100644 --- a/tests/integration/test_function.py +++ b/tests/integration/test_function.py @@ -112,15 +112,15 @@ def test_func_from_csv_with_header(csv_file): line. It tests cases where the fields are separated by quotes and without quotes.""" f = Function(csv_file) - assert f.__repr__() == "'Function from R1 to R1 : (time) → (value)'" + assert repr(f) == "'Function from R1 to R1 : (time) → (value)'" assert np.isclose(f(0), 100) assert np.isclose(f(0) + f(1), 300), "Error summing the values of the function" @patch("matplotlib.pyplot.show") -def test_plots( +def test_plots( # pylint: disable=unused-argument mock_show, func_from_csv, func_2d_from_csv -): # pylint: disable: unused-argument +): """Test different plot methods of the Function class. Parameters @@ -150,7 +150,7 @@ def test_plots( @patch("matplotlib.pyplot.show") -def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_dataset_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable dataset.""" # Test plane f(x,y) = x - y source = [ @@ -171,7 +171,7 @@ def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argum @patch("matplotlib.pyplot.show") -def test_multivariable_function_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_function_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable function.""" def source(x, y): diff --git a/tests/integration/test_genericmotor.py b/tests/integration/test_genericmotor.py index 8b5a18a15..6373fc055 100644 --- a/tests/integration/test_genericmotor.py +++ b/tests/integration/test_genericmotor.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_generic_motor_info( - mock_show, generic_motor -): # pylint: disable: unused-argument +def test_generic_motor_info(mock_show, generic_motor): """Tests the GenericMotor.all_info() method. Parameters diff --git a/tests/integration/test_hybridmotor.py b/tests/integration/test_hybridmotor.py index 59f343132..1c7ed5cc8 100644 --- a/tests/integration/test_hybridmotor.py +++ b/tests/integration/test_hybridmotor.py @@ -1,8 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_hybrid_motor_info(mock_show, hybrid_motor): # pylint: disable: unused-argument +def test_hybrid_motor_info(mock_show, hybrid_motor): """Tests the HybridMotor.all_info() method. Parameters diff --git a/tests/integration/test_monte_carlo.py b/tests/integration/test_monte_carlo.py index 91838c828..5f11a9b25 100644 --- a/tests/integration/test_monte_carlo.py +++ b/tests/integration/test_monte_carlo.py @@ -1,3 +1,4 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch @@ -85,7 +86,7 @@ def test_monte_carlo_prints(monte_carlo_calisto): monte_carlo_calisto.info() -@patch("matplotlib.pyplot.show") +@patch("matplotlib.pyplot.show") # pylint: disable=unused-argument def test_monte_carlo_plots(mock_show, monte_carlo_calisto_pre_loaded): """Tests the plots methods of the MonteCarlo class.""" assert monte_carlo_calisto_pre_loaded.all_info() is None diff --git a/tests/integration/test_plots.py b/tests/integration/test_plots.py index edb8fad09..232ef71c6 100644 --- a/tests/integration/test_plots.py +++ b/tests/integration/test_plots.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch -import matplotlib.pyplot as plt - from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import CompareFlights @patch("matplotlib.pyplot.show") diff --git a/tests/integration/test_rocket.py b/tests/integration/test_rocket.py index db7eafeff..4d5daf7a6 100644 --- a/tests/integration/test_rocket.py +++ b/tests/integration/test_rocket.py @@ -80,7 +80,9 @@ def test_air_brakes_clamp_on( @patch("matplotlib.pyplot.show") -def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): +def test_air_brakes_clamp_off( # pylint: disable=unused-argument + mock_show, calisto_air_brakes_clamp_off +): """Test the air brakes class with clamp off configuration. This test checks the basic attributes and the deployment_level setter. It also checks the all_info method. @@ -115,7 +117,7 @@ def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): @patch("matplotlib.pyplot.show") -def test_rocket(mock_show, calisto_robust): +def test_rocket(mock_show, calisto_robust): # pylint: disable=unused-argument test_rocket = calisto_robust static_margin = test_rocket.static_margin(0) # Check if all_info and static_method methods are working properly @@ -123,7 +125,7 @@ def test_rocket(mock_show, calisto_robust): @patch("matplotlib.pyplot.show") -def test_aero_surfaces_infos( +def test_aero_surfaces_infos( # pylint: disable=unused-argument mock_show, calisto_nose_cone, calisto_tail, calisto_trapezoidal_fins ): assert calisto_nose_cone.all_info() is None diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index a06b92fdb..c4217331c 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -3,9 +3,9 @@ from unittest.mock import patch import numpy as np -import numpy.ma as ma import pytest import pytz +from numpy import ma from rocketpy import Environment @@ -73,18 +73,20 @@ def test_location_set_topographic_profile_computes_elevation( def test_geodesic_coordinate_geodesic_to_utm_converts_coordinate(): """Tests the conversion from geodesic to UTM coordinates.""" - x, y, utm_zone, utm_letter, hemis, EW = Environment.geodesic_to_utm( - lat=32.990254, - lon=-106.974998, - semi_major_axis=6378137.0, # WGS84 - flattening=1 / 298.257223563, # WGS84 + x, y, utm_zone, utm_letter, north_south_hemis, east_west_hemis = ( + Environment.geodesic_to_utm( + lat=32.990254, + lon=-106.974998, + semi_major_axis=6378137.0, # WGS84 + flattening=1 / 298.257223563, # WGS84 + ) ) assert np.isclose(x, 315468.64, atol=1e-5) assert np.isclose(y, 3651938.65, atol=1e-5) assert utm_zone == 13 assert utm_letter == "S" - assert hemis == "N" - assert EW == "W" + assert north_south_hemis == "N" + assert east_west_hemis == "W" def test_utm_to_geodesic_converts_coordinates(): @@ -159,7 +161,7 @@ def test_decimal_degrees_to_arc_seconds_computes_correct_values( @patch("matplotlib.pyplot.show") -def test_info_returns(mock_show, example_plain_env): +def test_info_returns(mock_show, example_plain_env): # pylint: disable=unused-argument """Tests the all_info_returned() all_plot_info_returned() and methods of the Environment class. diff --git a/tests/unit/test_environment_analysis.py b/tests/unit/test_environment_analysis.py index ed5fbc952..caa8fb847 100644 --- a/tests/unit/test_environment_analysis.py +++ b/tests/unit/test_environment_analysis.py @@ -11,7 +11,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_distribution_plots(mock_show, env_analysis): +def test_distribution_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the distribution plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -42,7 +42,7 @@ def test_distribution_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_average_plots(mock_show, env_analysis): +def test_average_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the average plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -68,7 +68,7 @@ def test_average_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_profile_plots(mock_show, env_analysis): +def test_profile_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the profile plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -138,7 +138,7 @@ def test_values(env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_animation_plots(mock_show, env_analysis): +def test_animation_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the animation plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 2f438c78c..078c682c9 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -5,7 +5,7 @@ import pytest from scipy import optimize -from rocketpy import Components, Environment, Flight, Function, Rocket, SolidMotor +from rocketpy import Components, Flight, Function, Rocket plt.rcParams.update({"figure.max_open_warning": 0}) @@ -190,13 +190,13 @@ def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_v The expected values of the aerodynamic moments vector at the point to be tested. """ - expected_attr, expected_M = flight_time, expected_values + expected_attr, expected_moment = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_M, abs=atol) == ( + assert pytest.approx(expected_moment, abs=atol) == ( test.M1(t), test.M2(t), test.M3(t), @@ -229,13 +229,13 @@ def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_va The expected values of the aerodynamic forces vector at the point to be tested. """ - expected_attr, expected_R = flight_time, expected_values + expected_attr, expected_forces = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_R, abs=atol) == ( + assert pytest.approx(expected_forces, abs=atol) == ( test.R1(t), test.R2(t), test.R3(t), @@ -507,7 +507,9 @@ def test_lat_lon_conversion_from_origin( "static_margin, max_time", [(-0.1, 2), (-0.01, 5), (0, 5), (0.01, 20), (0.1, 20), (1.0, 20)], ) -def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): +def test_stability_static_margins( + wind_u, wind_v, static_margin, max_time, example_plain_env, dummy_empty_motor +): """Test stability margins for a constant velocity flight, 100 m/s, wind a lateral wind speed of 10 m/s. Rocket has infinite mass to prevent side motion. Check if a restoring moment exists depending on static margins. @@ -522,11 +524,14 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): Static margin to be tested max_time : float Maximum time to be simulated + example_plain_env : rocketpy.Environment + This is a fixture. + dummy_empty_motor : rocketpy.SolidMotor + This is a fixture. """ # Create an environment with ZERO gravity to keep the rocket's speed constant - env = Environment(gravity=0, latitude=0, longitude=0, elevation=0) - env.set_atmospheric_model( + example_plain_env.set_atmospheric_model( type="custom_atmosphere", wind_u=wind_u, wind_v=wind_v, @@ -535,29 +540,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): ) # Make sure that the free_stream_mach will always be 0, so that the rocket # behaves as the STATIC (free_stream_mach=0) margin predicts - env.speed_of_sound = Function(1e16) - - # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant - # TODO: why don t we use these same values to create EmptyMotor class? - dummy_motor = SolidMotor( - thrust_source=1e-300, - burn_time=1e-10, - dry_mass=1.815, - dry_inertia=(0.125, 0.125, 0.002), - center_of_dry_mass_position=0.317, - grains_center_of_mass_position=0.397, - grain_number=5, - grain_separation=5 / 1000, - grain_density=1e-300, - grain_outer_radius=33 / 1000, - grain_initial_inner_radius=15 / 1000, - grain_initial_height=120 / 1000, - nozzle_radius=33 / 1000, - throat_radius=11 / 1000, - nozzle_position=0, - interpolation_method="linear", - coordinate_system_orientation="nozzle_to_combustion_chamber", - ) + example_plain_env.speed_of_sound = Function(1e16) # create a rocket with zero drag and huge mass to keep the rocket's speed constant dummy_rocket = Rocket( @@ -569,7 +552,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): center_of_mass_without_motor=0, ) dummy_rocket.set_rail_buttons(0.082, -0.618) - dummy_rocket.add_motor(dummy_motor, position=-1.373) + dummy_rocket.add_motor(dummy_empty_motor, position=-1.373) setup_rocket_with_given_static_margin(dummy_rocket, static_margin) @@ -582,13 +565,12 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): test_flight = Flight( rocket=dummy_rocket, rail_length=1, - environment=env, + environment=example_plain_env, initial_solution=initial_solution, max_time=max_time, max_time_step=1e-2, verbose=False, ) - test_flight.post_process(interpolation="linear") # Check stability according to static margin if wind_u == 0: @@ -598,8 +580,9 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): moments = test_flight.M2.get_source()[:, 1] wind_sign = -np.sign(wind_u) - assert ( - (static_margin > 0 and np.max(moments) * np.min(moments) < 0) - or (static_margin < 0 and np.all(moments / wind_sign <= 0)) - or (static_margin == 0 and np.all(np.abs(moments) <= 1e-10)) - ) + if static_margin > 0: + assert np.max(moments) * np.min(moments) < 0 + elif static_margin < 0: + assert np.all(moments / wind_sign <= 0) + else: # static_margin == 0 + assert np.all(np.abs(moments) <= 1e-10) diff --git a/tests/unit/test_flight_time_nodes.py b/tests/unit/test_flight_time_nodes.py index 10f6b6c30..1e2661210 100644 --- a/tests/unit/test_flight_time_nodes.py +++ b/tests/unit/test_flight_time_nodes.py @@ -2,9 +2,7 @@ TimeNode. """ -import pytest - -from rocketpy.rocket import Parachute, _Controller +# from rocketpy.rocket import Parachute, _Controller def test_time_nodes_init(flight_calisto): diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index 3c1934f9f..9efb64c0c 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -359,89 +359,78 @@ def test_setters(func_from_csv, func_2d_from_csv): assert func_2d_from_csv.get_extrapolation_method() == "natural" -def test_interpolation_methods(linear_func): - """Tests some of the interpolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test Akima - assert isinstance(linear_func.set_interpolation("akima"), Function) - linear_func.set_interpolation("akima") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "akima" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - # Test polynomial - - assert isinstance(linear_func.set_interpolation("polynomial"), Function) - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "polynomial" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - -def test_extrapolation_methods(linear_func): - """Test some of the extrapolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test zero - linear_func.set_extrapolation("zero") - assert linear_func.get_extrapolation_method() == "zero" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test constant - assert isinstance(linear_func.set_extrapolation("constant"), Function) - linear_func.set_extrapolation("constant") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "constant" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test natural for linear interpolation - linear_func.set_interpolation("linear") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for spline interpolation - linear_func.set_interpolation("spline") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for akima interpolation - linear_func.set_interpolation("akima") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for polynomial interpolation - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) +class TestInterpolationMethods: + """Tests some of the interpolation methods of the Function class.""" + + def test_akima_interpolation(self, linear_func): + """Tests Akima interpolation method""" + assert isinstance(linear_func.set_interpolation("akima"), Function) + linear_func.set_interpolation("akima") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "akima" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + def test_polynomial_interpolation(self, linear_func): + """Tests polynomial interpolation method""" + assert isinstance(linear_func.set_interpolation("polynomial"), Function) + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "polynomial" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + +class TestExtrapolationMethods: + """Test some of the extrapolation methods of the Function class.""" + + def test_zero_extrapolation(self, linear_func): + linear_func.set_extrapolation("zero") + assert linear_func.get_extrapolation_method() == "zero" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_constant_extrapolation(self, linear_func): + assert isinstance(linear_func.set_extrapolation("constant"), Function) + linear_func.set_extrapolation("constant") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "constant" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_natural_extrapolation_linear(self, linear_func): + linear_func.set_interpolation("linear") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_spline(self, linear_func): + linear_func.set_interpolation("spline") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_akima(self, linear_func): + linear_func.set_interpolation("akima") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_polynomial(self, linear_func): + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) @pytest.mark.parametrize("a", [-1, 0, 1]) @pytest.mark.parametrize("b", [-1, 0, 1]) -def test_multivariable_dataset(a, b): - """Test the Function class with a multivariable dataset.""" +def test_multivariate_dataset(a, b): + """Test the Function class with a multivariate dataset.""" # Test plane f(x,y) = x + y source = [ (-1, -1, -2), @@ -515,8 +504,8 @@ def test_3d_shepard_interpolation(x, y, z, w_expected): @pytest.mark.parametrize("a", [-1, -0.5, 0, 0.5, 1]) @pytest.mark.parametrize("b", [-1, -0.5, 0, 0.5, 1]) -def test_multivariable_function(a, b): - """Test the Function class with a multivariable function.""" +def test_multivariate_function(a, b): + """Test the Function class with a multivariate function.""" def source(x, y): return np.sin(x + y) diff --git a/tests/unit/test_genericmotor.py b/tests/unit/test_genericmotor.py index 98bc5664f..c6321ae4d 100644 --- a/tests/unit/test_genericmotor.py +++ b/tests/unit/test_genericmotor.py @@ -2,16 +2,21 @@ import pytest import scipy.integrate -burn_time = (2, 7) -thrust_source = lambda t: 2000 - 100 * (t - 2) -chamber_height = 0.5 -chamber_radius = 0.075 -chamber_position = -0.25 -propellant_initial_mass = 5.0 -nozzle_position = -0.5 -nozzle_radius = 0.075 -dry_mass = 8.0 -dry_inertia = (0.2, 0.2, 0.08) +BURN_TIME = (2, 7) + + +def thrust_source(t): + return 2000 - 100 * (t - 2) + + +CHAMBER_HEIGHT = 0.5 +CHAMBER_RADIUS = 0.075 +CHAMBER_POSITION = -0.25 +PROPELLANT_INITIAL_MASS = 5.0 +NOZZLE_POSITION = -0.5 +NOZZLE_RADIUS = 0.075 +DRY_MASS = 8.0 +DRY_INERTIA = (0.2, 0.2, 0.08) def test_generic_motor_basic_parameters(generic_motor): @@ -22,19 +27,19 @@ def test_generic_motor_basic_parameters(generic_motor): generic_motor : rocketpy.GenericMotor The GenericMotor object to be used in the tests. """ - assert generic_motor.burn_time == burn_time - assert generic_motor.dry_mass == dry_mass + assert generic_motor.burn_time == BURN_TIME + assert generic_motor.dry_mass == DRY_MASS assert ( generic_motor.dry_I_11, generic_motor.dry_I_22, generic_motor.dry_I_33, - ) == dry_inertia - assert generic_motor.nozzle_position == nozzle_position - assert generic_motor.nozzle_radius == nozzle_radius - assert generic_motor.chamber_position == chamber_position - assert generic_motor.chamber_radius == chamber_radius - assert generic_motor.chamber_height == chamber_height - assert generic_motor.propellant_initial_mass == propellant_initial_mass + ) == DRY_INERTIA + assert generic_motor.nozzle_position == NOZZLE_POSITION + assert generic_motor.nozzle_radius == NOZZLE_RADIUS + assert generic_motor.chamber_position == CHAMBER_POSITION + assert generic_motor.chamber_radius == CHAMBER_RADIUS + assert generic_motor.chamber_height == CHAMBER_HEIGHT + assert generic_motor.propellant_initial_mass == PROPELLANT_INITIAL_MASS def test_generic_motor_thrust_parameters(generic_motor): @@ -46,20 +51,20 @@ def test_generic_motor_thrust_parameters(generic_motor): The GenericMotor object to be used in the tests. """ expected_thrust = np.array( - [(t, thrust_source(t)) for t in np.linspace(*burn_time, 50)] + [(t, thrust_source(t)) for t in np.linspace(*BURN_TIME, 50)] ) expected_total_impulse = scipy.integrate.trapezoid( expected_thrust[:, 1], expected_thrust[:, 0] ) - expected_exhaust_velocity = expected_total_impulse / propellant_initial_mass + expected_exhaust_velocity = expected_total_impulse / PROPELLANT_INITIAL_MASS expected_mass_flow_rate = -1 * expected_thrust[:, 1] / expected_exhaust_velocity # Discretize mass flow rate for testing purposes - mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*burn_time, 50) + mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*BURN_TIME, 50) assert generic_motor.thrust.y_array == pytest.approx(expected_thrust[:, 1]) assert generic_motor.total_impulse == pytest.approx(expected_total_impulse) - assert generic_motor.exhaust_velocity.average(*burn_time) == pytest.approx( + assert generic_motor.exhaust_velocity.average(*BURN_TIME) == pytest.approx( expected_exhaust_velocity ) assert mass_flow_rate.y_array == pytest.approx(expected_mass_flow_rate) @@ -78,8 +83,8 @@ def test_generic_motor_center_of_mass(generic_motor): center_of_mass = -0.25 # Discretize center of mass for testing purposes - generic_motor.center_of_propellant_mass.set_discrete(*burn_time, 50) - generic_motor.center_of_mass.set_discrete(*burn_time, 50) + generic_motor.center_of_propellant_mass.set_discrete(*BURN_TIME, 50) + generic_motor.center_of_mass.set_discrete(*BURN_TIME, 50) assert generic_motor.center_of_propellant_mass.y_array == pytest.approx( center_of_propellant_mass @@ -99,24 +104,24 @@ def test_generic_motor_inertia(generic_motor): The GenericMotor object to be used in the tests. """ # Tests the inertia formulation from the propellant mass - propellant_mass = generic_motor.propellant_mass.set_discrete(*burn_time, 50).y_array + propellant_mass = generic_motor.propellant_mass.set_discrete(*BURN_TIME, 50).y_array - propellant_I_11 = propellant_mass * (chamber_radius**2 / 4 + chamber_height**2 / 12) + propellant_I_11 = propellant_mass * (CHAMBER_RADIUS**2 / 4 + CHAMBER_HEIGHT**2 / 12) propellant_I_22 = propellant_I_11 - propellant_I_33 = propellant_mass * (chamber_radius**2 / 2) + propellant_I_33 = propellant_mass * (CHAMBER_RADIUS**2 / 2) # Centers of mass coincide, so no translation is needed - I_11 = propellant_I_11 + dry_inertia[0] - I_22 = propellant_I_22 + dry_inertia[1] - I_33 = propellant_I_33 + dry_inertia[2] + I_11 = propellant_I_11 + DRY_INERTIA[0] + I_22 = propellant_I_22 + DRY_INERTIA[1] + I_33 = propellant_I_33 + DRY_INERTIA[2] # Discretize inertia for testing purposes - generic_motor.propellant_I_11.set_discrete(*burn_time, 50) - generic_motor.propellant_I_22.set_discrete(*burn_time, 50) - generic_motor.propellant_I_33.set_discrete(*burn_time, 50) - generic_motor.I_11.set_discrete(*burn_time, 50) - generic_motor.I_22.set_discrete(*burn_time, 50) - generic_motor.I_33.set_discrete(*burn_time, 50) + generic_motor.propellant_I_11.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_22.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_33.set_discrete(*BURN_TIME, 50) + generic_motor.I_11.set_discrete(*BURN_TIME, 50) + generic_motor.I_22.set_discrete(*BURN_TIME, 50) + generic_motor.I_33.set_discrete(*BURN_TIME, 50) assert generic_motor.propellant_I_11.y_array == pytest.approx(propellant_I_11) assert generic_motor.propellant_I_22.y_array == pytest.approx(propellant_I_22) diff --git a/tests/unit/test_hybridmotor.py b/tests/unit/test_hybridmotor.py index acf4b3e54..ef03a1998 100644 --- a/tests/unit/test_hybridmotor.py +++ b/tests/unit/test_hybridmotor.py @@ -1,26 +1,28 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -thrust_function = lambda t: 2000 - 100 * t -burn_time = 10 -center_of_dry_mass = 0 -dry_inertia = (4, 4, 0.1) -dry_mass = 8 -grain_density = 1700 -grain_number = 4 -grain_initial_height = 0.1 -grain_separation = 0 -grain_initial_inner_radius = 0.04 -grain_outer_radius = 0.1 -nozzle_position = -0.4 -nozzle_radius = 0.07 -grains_center_of_mass_position = -0.1 -oxidizer_tank_position = 0.3 + +def thrust_function(t): + return 2000 - 100 * t + + +BURN_TIME = 10 +CENTER_OF_DRY_MASS = 0 +DRY_INERTIA = (4, 4, 0.1) +DRY_MASS = 8 +GRAIN_DENSITY = 1700 +GRAIN_NUMBER = 4 +GRAIN_INITIAL_HEIGHT = 0.1 +GRAIN_SEPARATION = 0 +GRAIN_INITIAL_INNER_RADIUS = 0.04 +GRAIN_OUTER_RADIUS = 0.1 +NOZZLE_POSITION = -0.4 +NOZZLE_RADIUS = 0.07 +GRAINS_CENTER_OF_MASS_POSITION = -0.1 +OXIDIZER_TANK_POSITION = 0.3 def test_hybrid_motor_basic_parameters(hybrid_motor): @@ -31,25 +33,25 @@ def test_hybrid_motor_basic_parameters(hybrid_motor): hybrid_motor : rocketpy.HybridMotor The HybridMotor object to be used in the tests. """ - assert hybrid_motor.burn_time == (0, burn_time) - assert hybrid_motor.dry_mass == dry_mass + assert hybrid_motor.burn_time == (0, BURN_TIME) + assert hybrid_motor.dry_mass == DRY_MASS assert ( hybrid_motor.dry_I_11, hybrid_motor.dry_I_22, hybrid_motor.dry_I_33, - ) == dry_inertia - assert hybrid_motor.center_of_dry_mass_position == center_of_dry_mass - assert hybrid_motor.nozzle_position == nozzle_position - assert hybrid_motor.nozzle_radius == nozzle_radius - assert hybrid_motor.solid.grain_number == grain_number - assert hybrid_motor.solid.grain_density == grain_density - assert hybrid_motor.solid.grain_initial_height == grain_initial_height - assert hybrid_motor.solid.grain_separation == grain_separation - assert hybrid_motor.solid.grain_initial_inner_radius == grain_initial_inner_radius - assert hybrid_motor.solid.grain_outer_radius == grain_outer_radius + ) == DRY_INERTIA + assert hybrid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert hybrid_motor.nozzle_position == NOZZLE_POSITION + assert hybrid_motor.nozzle_radius == NOZZLE_RADIUS + assert hybrid_motor.solid.grain_number == GRAIN_NUMBER + assert hybrid_motor.solid.grain_density == GRAIN_DENSITY + assert hybrid_motor.solid.grain_initial_height == GRAIN_INITIAL_HEIGHT + assert hybrid_motor.solid.grain_separation == GRAIN_SEPARATION + assert hybrid_motor.solid.grain_initial_inner_radius == GRAIN_INITIAL_INNER_RADIUS + assert hybrid_motor.solid.grain_outer_radius == GRAIN_OUTER_RADIUS assert ( hybrid_motor.solid.grains_center_of_mass_position - == grains_center_of_mass_position + == GRAINS_CENTER_OF_MASS_POSITION ) assert hybrid_motor.liquid.positioned_tanks[0]["position"] == 0.3 @@ -69,11 +71,11 @@ def test_hybrid_motor_thrust_parameters(hybrid_motor, spherical_oxidizer_tank): expected_total_impulse = scipy.integrate.quad(expected_thrust, 0, 10)[0] initial_grain_mass = ( - grain_density + GRAIN_DENSITY * np.pi - * (grain_outer_radius**2 - grain_initial_inner_radius**2) - * grain_initial_height - * grain_number + * (GRAIN_OUTER_RADIUS**2 - GRAIN_INITIAL_INNER_RADIUS**2) + * GRAIN_INITIAL_HEIGHT + * GRAIN_NUMBER ) initial_oxidizer_mass = spherical_oxidizer_tank.fluid_mass(0) initial_mass = initial_grain_mass + initial_oxidizer_mass @@ -111,13 +113,13 @@ def test_hybrid_motor_center_of_mass(hybrid_motor, spherical_oxidizer_tank): oxidizer_mass = spherical_oxidizer_tank.fluid_mass grain_mass = hybrid_motor.solid.propellant_mass - propellant_balance = grain_mass * grains_center_of_mass_position + oxidizer_mass * ( - oxidizer_tank_position + spherical_oxidizer_tank.center_of_mass + propellant_balance = grain_mass * GRAINS_CENTER_OF_MASS_POSITION + oxidizer_mass * ( + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / (grain_mass + oxidizer_mass) - center_of_mass = balance / (grain_mass + oxidizer_mass + dry_mass) + center_of_mass = balance / (grain_mass + oxidizer_mass + DRY_MASS) for t in np.linspace(0, 100, 100): assert pytest.approx( @@ -145,12 +147,12 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): # Validate parallel axis theorem translation grain_inertia += ( grain_mass - * (grains_center_of_mass_position - hybrid_motor.center_of_propellant_mass) ** 2 + * (GRAINS_CENTER_OF_MASS_POSITION - hybrid_motor.center_of_propellant_mass) ** 2 ) oxidizer_inertia += ( oxidizer_mass * ( - oxidizer_tank_position + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass - hybrid_motor.center_of_propellant_mass ) @@ -164,8 +166,8 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): propellant_inertia + propellant_mass * (hybrid_motor.center_of_propellant_mass - hybrid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-hybrid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-hybrid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) for t in np.linspace(0, 100, 100): diff --git a/tests/unit/test_liquidmotor.py b/tests/unit/test_liquidmotor.py index ed4fe0ab3..6208a7dc0 100644 --- a/tests/unit/test_liquidmotor.py +++ b/tests/unit/test_liquidmotor.py @@ -1,20 +1,18 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -burn_time = (8, 20) -dry_mass = 10 -dry_inertia = (5, 5, 0.2) -center_of_dry_mass = 0 -nozzle_position = -1.364 -nozzle_radius = 0.069 / 2 -pressurant_tank_position = 2.007 -fuel_tank_position = -1.048 -oxidizer_tank_position = 0.711 +BURN_TIME = (8, 20) +DRY_MASS = 10 +DRY_INERTIA = (5, 5, 0.2) +CENTER_OF_DRY_MASS = 0 +NOZZLE_POSITION = -1.364 +NOZZLE_RADIUS = 0.069 / 2 +PRESSURANT_TANK_POSITION = 2.007 +FUEL_TANK_POSITION = -1.048 +OXIDIZER_TANK_POSITION = 0.711 def test_liquid_motor_basic_parameters(liquid_motor): @@ -25,19 +23,19 @@ def test_liquid_motor_basic_parameters(liquid_motor): liquid_motor : rocketpy.LiquidMotor The LiquidMotor object to be used in the tests. """ - assert liquid_motor.burn_time == burn_time - assert liquid_motor.dry_mass == dry_mass + assert liquid_motor.burn_time == BURN_TIME + assert liquid_motor.dry_mass == DRY_MASS assert ( liquid_motor.dry_I_11, liquid_motor.dry_I_22, liquid_motor.dry_I_33, - ) == dry_inertia - assert liquid_motor.center_of_dry_mass_position == center_of_dry_mass - assert liquid_motor.nozzle_position == nozzle_position - assert liquid_motor.nozzle_radius == nozzle_radius - assert liquid_motor.positioned_tanks[0]["position"] == pressurant_tank_position - assert liquid_motor.positioned_tanks[1]["position"] == fuel_tank_position - assert liquid_motor.positioned_tanks[2]["position"] == oxidizer_tank_position + ) == DRY_INERTIA + assert liquid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert liquid_motor.nozzle_position == NOZZLE_POSITION + assert liquid_motor.nozzle_radius == NOZZLE_RADIUS + assert liquid_motor.positioned_tanks[0]["position"] == PRESSURANT_TANK_POSITION + assert liquid_motor.positioned_tanks[1]["position"] == FUEL_TANK_POSITION + assert liquid_motor.positioned_tanks[2]["position"] == OXIDIZER_TANK_POSITION def test_liquid_motor_thrust_parameters( @@ -125,12 +123,12 @@ def test_liquid_motor_mass_volume( ) # Perform default discretization - expected_pressurant_mass.set_discrete(*burn_time, 100) - expected_fuel_mass.set_discrete(*burn_time, 100) - expected_oxidizer_mass.set_discrete(*burn_time, 100) - expected_pressurant_volume.set_discrete(*burn_time, 100) - expected_fuel_volume.set_discrete(*burn_time, 100) - expected_oxidizer_volume.set_discrete(*burn_time, 100) + expected_pressurant_mass.set_discrete(*BURN_TIME, 100) + expected_fuel_mass.set_discrete(*BURN_TIME, 100) + expected_oxidizer_mass.set_discrete(*BURN_TIME, 100) + expected_pressurant_volume.set_discrete(*BURN_TIME, 100) + expected_fuel_volume.set_discrete(*BURN_TIME, 100) + expected_oxidizer_volume.set_discrete(*BURN_TIME, 100) assert ( pytest.approx(expected_pressurant_mass.y_array, 0.01) @@ -180,14 +178,14 @@ def test_liquid_motor_center_of_mass( propellant_mass = pressurant_mass + fuel_mass + oxidizer_mass propellant_balance = ( - pressurant_mass * (pressurant_tank.center_of_mass + pressurant_tank_position) - + fuel_mass * (fuel_tank.center_of_mass + fuel_tank_position) - + oxidizer_mass * (oxidizer_tank.center_of_mass + oxidizer_tank_position) + pressurant_mass * (pressurant_tank.center_of_mass + PRESSURANT_TANK_POSITION) + + fuel_mass * (fuel_tank.center_of_mass + FUEL_TANK_POSITION) + + oxidizer_mass * (oxidizer_tank.center_of_mass + OXIDIZER_TANK_POSITION) ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / propellant_mass - center_of_mass = balance / (propellant_mass + dry_mass) + center_of_mass = balance / (propellant_mass + DRY_MASS) assert ( pytest.approx(liquid_motor.center_of_propellant_mass.y_array) @@ -223,7 +221,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( pressurant_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + pressurant_tank_position + + PRESSURANT_TANK_POSITION ) ** 2 ) @@ -232,7 +230,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( fuel_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + fuel_tank_position + + FUEL_TANK_POSITION ) ** 2 ) @@ -241,7 +239,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( oxidizer_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + oxidizer_tank_position + + OXIDIZER_TANK_POSITION ) ** 2 ) @@ -253,8 +251,8 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer propellant_inertia + propellant_mass * (liquid_motor.center_of_propellant_mass - liquid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-liquid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-liquid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) assert ( diff --git a/tests/unit/test_monte_carlo.py b/tests/unit/test_monte_carlo.py index 7af6a5db5..0e1ad22cc 100644 --- a/tests/unit/test_monte_carlo.py +++ b/tests/unit/test_monte_carlo.py @@ -1,8 +1,5 @@ -from unittest.mock import patch - import matplotlib as plt import numpy as np -import pytest plt.rcParams.update({"figure.max_open_warning": 0}) diff --git a/tests/unit/test_plots.py b/tests/unit/test_plots.py index db36264d8..cd35f8d11 100644 --- a/tests/unit/test_plots.py +++ b/tests/unit/test_plots.py @@ -1,14 +1,12 @@ -import os from unittest.mock import patch import matplotlib.pyplot as plt -from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import Compare @patch("matplotlib.pyplot.show") -def test_compare(mock_show, flight_calisto): +def test_compare(mock_show, flight_calisto): # pylint: disable=unused-argument """Here we want to test the 'x_attributes' argument, which is the only one that is not tested in the other tests. diff --git a/tests/unit/test_rocket.py b/tests/unit/test_rocket.py index 876f5024d..a984466ee 100644 --- a/tests/unit/test_rocket.py +++ b/tests/unit/test_rocket.py @@ -10,7 +10,7 @@ @patch("matplotlib.pyplot.show") def test_elliptical_fins( mock_show, calisto_robust, calisto_trapezoidal_fins -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument test_rocket = calisto_robust calisto_robust.aerodynamic_surfaces.remove(calisto_trapezoidal_fins) test_rocket.add_elliptical_fins(4, span=0.100, root_chord=0.120, position=-1.168) @@ -449,9 +449,9 @@ def test_evaluate_com_to_cdm_function(calisto): def test_get_inertia_tensor_at_time(calisto): # Expected values (for t = 0) # TODO: compute these values by hand or using CAD. - Ixx = 10.31379 - Iyy = 10.31379 - Izz = 0.039942 + I_11 = 10.31379 + I_22 = 10.31379 + I_33 = 0.039942 # Set tolerance threshold atol = 1e-5 @@ -460,9 +460,9 @@ def test_get_inertia_tensor_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_at_time(0) # Check if the values are close to the expected ones - assert pytest.approx(Ixx, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -475,9 +475,9 @@ def test_get_inertia_tensor_at_time(calisto): def test_get_inertia_tensor_derivative_at_time(calisto): # Expected values (for t = 2s) # TODO: compute these values by hand or using CAD. - Ixx_dot = -0.634805230901143 - Iyy_dot = -0.634805230901143 - Izz_dot = -0.000671493662305 + I_11_dot = -0.634805230901143 + I_22_dot = -0.634805230901143 + I_33_dot = -0.000671493662305 # Set tolerance threshold atol = 1e-3 @@ -486,9 +486,9 @@ def test_get_inertia_tensor_derivative_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_derivative_at_time(2) # Check if the values are close to the expected ones - assert pytest.approx(Ixx_dot, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy_dot, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz_dot, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11_dot, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22_dot, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33_dot, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -514,77 +514,72 @@ def test_add_cm_eccentricity(calisto): assert calisto.thrust_eccentricity_y == 0.1 -def test_add_surfaces_different_noses(calisto): +class TestAddSurfaces: """Test the add_surfaces method with different nose cone configurations. More specifically, this will check the static margin of the rocket with - different nose cone configurations. - - Parameters - ---------- - calisto : Rocket - Pytest fixture for the calisto rocket. - """ - length = 0.55829 - kind = "vonkarman" - position = 1.16 - bluffness = 0 - base_radius = 0.0635 - rocket_radius = 0.0635 - - # Case 1: base_radius == rocket_radius - nose1 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 1", - ) - calisto.add_surfaces(nose1, position) - assert nose1.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 2: base_radius == rocket_radius / 2 - calisto.aerodynamic_surfaces.remove(nose1) - nose2 = NoseCone( - length, - kind, - base_radius=base_radius / 2, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 2", - ) - calisto.add_surfaces(nose2, position) - assert nose2.radius_ratio == pytest.approx(0.5, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 3: base_radius is None - calisto.aerodynamic_surfaces.remove(nose2) - nose3 = NoseCone( - length, - kind, - base_radius=None, - bluffness=bluffness, - rocket_radius=rocket_radius * 2, - name="Nose Cone 3", - ) - calisto.add_surfaces(nose3, position) - assert nose3.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 4: rocket_radius is None - calisto.aerodynamic_surfaces.remove(nose3) - nose4 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=None, - name="Nose Cone 4", - ) - calisto.add_surfaces(nose4, position) - assert nose4.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + different nose cone configurations.""" + + @pytest.fixture(autouse=True) + def setup(self, calisto): + self.calisto = calisto + self.length = 0.55829 + self.kind = "vonkarman" + self.position = 1.16 + self.bluffness = 0 + self.base_radius = 0.0635 + self.rocket_radius = 0.0635 + + def test_add_surfaces_base_equals_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 1", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_half_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius / 2, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 2", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(0.5, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=None, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius * 2, + name="Nose Cone 3", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_rocket_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=None, + name="Nose Cone 4", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) def test_coordinate_system_orientation( diff --git a/tests/unit/test_solidmotor.py b/tests/unit/test_solidmotor.py index 6c5d4d4b1..064c8210e 100644 --- a/tests/unit/test_solidmotor.py +++ b/tests/unit/test_solidmotor.py @@ -20,7 +20,7 @@ @patch("matplotlib.pyplot.show") -def test_motor(mock_show, cesaroni_m1670): +def test_motor(mock_show, cesaroni_m1670): # pylint: disable=unused-argument """Tests the SolidMotor.all_info() method. Parameters diff --git a/tests/unit/test_tank.py b/tests/unit/test_tank.py index 13c7b6cb8..3a77a8bca 100644 --- a/tests/unit/test_tank.py +++ b/tests/unit/test_tank.py @@ -1,3 +1,5 @@ +# TODO: This file must be refactored to improve readability and maintainability. +# pylint: disable=too-many-statements import os from math import isclose @@ -202,6 +204,7 @@ def bottom_endcap(y): ) # Assert volume bounds + # pylint: disable=comparison-with-callable assert (real_tank_lox.gas_height <= real_tank_lox.geometry.top).all assert (real_tank_lox.fluid_volume <= real_tank_lox.geometry.total_volume).all assert (example_tank_lox.gas_height <= example_tank_lox.geometry.top).all @@ -231,17 +234,22 @@ def test(calculated, expected, t, real=False): def test_mass(): """Test mass function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: initial_liquid_mass - + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) - + initial_gas_mass - + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) - ) + + def example_expected(t): + return ( + initial_liquid_mass + + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) + + initial_gas_mass + + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) + ) + example_calculated = example_tank_lox.fluid_mass lox_vals = Function(lox_masses).y_array - real_expected = lambda t: lox_vals[t] + def real_expected(t): + return lox_vals[t] + real_calculated = real_tank_lox.fluid_mass test(example_calculated, example_expected, 5) @@ -249,19 +257,24 @@ def test_mass(): def test_net_mfr(): """Test net_mass_flow_rate function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: liquid_mass_flow_rate_in - - liquid_mass_flow_rate_out - + gas_mass_flow_rate_in - - gas_mass_flow_rate_out - ) + + def example_expected(_): + return ( + liquid_mass_flow_rate_in + - liquid_mass_flow_rate_out + + gas_mass_flow_rate_in + - gas_mass_flow_rate_out + ) + example_calculated = example_tank_lox.net_mass_flow_rate liquid_mfrs = Function(example_liquid_masses).y_array gas_mfrs = Function(example_gas_masses).y_array - real_expected = lambda t: (liquid_mfrs[t] + gas_mfrs[t]) / t + def real_expected(t): + return (liquid_mfrs[t] + gas_mfrs[t]) / t + real_calculated = real_tank_lox.net_mass_flow_rate test(example_calculated, example_expected, 10) @@ -280,8 +293,12 @@ def test_level_based_tank(): test_dir = "./data/berkeley/" - top_endcap = lambda y: np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) - bottom_endcap = lambda y: np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + def top_endcap(y): + return np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) + + def bottom_endcap(y): + return np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + tank_geometry = TankGeometry( { (0, 0.0559): bottom_endcap, @@ -291,7 +308,7 @@ def test_level_based_tank(): ) ullage_data = Function(os.path.abspath(test_dir + "loxUllage.csv")).get_source() - levelTank = LevelBasedTank( + level_tank = LevelBasedTank( name="LevelTank", geometry=tank_geometry, flux_time=(0, 10), @@ -318,18 +335,18 @@ def align_time_series(small_source, large_source): for val in small_source: time = val[0] delta_time_vector = abs(time - large_source[:, 0]) - largeIndex = np.argmin(delta_time_vector) - delta_time = abs(time - large_source[largeIndex][0]) + large_index = np.argmin(delta_time_vector) + delta_time = abs(time - large_source[large_index][0]) if delta_time < tolerance: - result_larger_source[curr_ind] = large_source[largeIndex] + result_larger_source[curr_ind] = large_source[large_index] result_smaller_source[curr_ind] = val curr_ind += 1 return result_larger_source, result_smaller_source - assert np.allclose(levelTank.liquid_height, ullage_data) + assert np.allclose(level_tank.liquid_height, ullage_data) - calculated_mass = levelTank.liquid_mass.set_discrete( + calculated_mass = level_tank.liquid_mass.set_discrete( mass_data[0][0], mass_data[0][-1], len(mass_data[0]) ) calculated_mass, mass_data = align_time_series( @@ -337,7 +354,7 @@ def align_time_series(small_source, large_source): ) assert np.allclose(calculated_mass, mass_data, rtol=1, atol=2) - calculated_mfr = levelTank.net_mass_flow_rate.set_discrete( + calculated_mfr = level_tank.net_mass_flow_rate.set_discrete( mass_flow_rate_data[0][0], mass_flow_rate_data[0][-1], len(mass_flow_rate_data[0]), @@ -358,91 +375,133 @@ def test(t, a, tol=1e-4): assert isclose(t.get_value(i), a(i), abs_tol=tol) def test_nmfr(): - nmfr = ( - lambda x: liquid_mass_flow_rate_in - + gas_mass_flow_rate_in - - liquid_mass_flow_rate_out - - gas_mass_flow_rate_out - ) + def nmfr(_): + return ( + liquid_mass_flow_rate_in + + gas_mass_flow_rate_in + - liquid_mass_flow_rate_out + - gas_mass_flow_rate_out + ) + test(t.net_mass_flow_rate, nmfr) def test_mass(): - m = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) + (initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x) + def m(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + lm = t.fluid_mass test(lm, m) def test_liquid_height(): - alv = ( - lambda x: ( + def alv(x): + return ( initial_liquid_mass + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) - / lox.density - ) - alh = lambda x: alv(x) / (np.pi) + ) / lox.density + + def alh(x): + return alv(x) / (np.pi) + tlh = t.liquid_height test(tlh, alh) def test_com(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) tcom = t.center_of_mass test(tcom, acom) def test_inertia(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) r = 1 - ixy_gas = ( - lambda x: 1 / 4 * gas_mass(x) * r**2 - + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 - + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 - ) - ixy_liq = ( - lambda x: 1 / 4 * liquid_mass(x) * r**2 - + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 - + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 - ) - ixy = lambda x: ixy_gas(x) + ixy_liq(x) + + def ixy_gas(x): + return ( + 1 / 4 * gas_mass(x) * r**2 + + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 + + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 + ) + + def ixy_liq(x): + return ( + 1 / 4 * liquid_mass(x) * r**2 + + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 + + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 + ) + + def ixy(x): + return ixy_gas(x) + ixy_liq(x) + test(t.gas_inertia, ixy_gas, tol=1e-3) test(t.liquid_inertia, ixy_liq, tol=1e-3) test(t.inertia, ixy, tol=1e-3) diff --git a/tests/unit/test_tools_matrix.py b/tests/unit/test_tools_matrix.py index a6edb5278..f2b476fdc 100644 --- a/tests/unit/test_tools_matrix.py +++ b/tests/unit/test_tools_matrix.py @@ -97,7 +97,7 @@ def test_matrix_inverse(components): matrix = Matrix(components) if matrix.det == 0: with pytest.raises(ZeroDivisionError): - matrix.inverse + assert matrix.inverse else: assert matrix.inverse == np.linalg.inv(matrix) @@ -115,64 +115,64 @@ def test_matrix_neg(components): @pytest.mark.parametrize("A_c", test_matrices) @pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_add(A_c, B_c): - expected_result = np.array(A_c) + np.array(B_c) - assert Matrix(A_c) + Matrix(B_c) == expected_result +def test_matrix_add(A, B): + expected_result = np.array(A) + np.array(B) + assert Matrix(A) + Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_sub(A_c, B_c): - expected_result = np.array(A_c) - np.array(B_c) - assert Matrix(A_c) - Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_sub(A, B): + expected_result = np.array(A) - np.array(B) + assert Matrix(A) - Matrix(B) == expected_result @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_mul(A_c, k): - A = Matrix(A_c) - assert A * k == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_mul(A, k): + A = Matrix(A) + assert A * k == k * np.array(A) @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_rmul(A_c, k): - A = Matrix(A_c) - assert k * A == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_rmul(A, k): + np_array = np.array(A) + A = Matrix(A) + assert k * A == k * np_array -@pytest.mark.parametrize("A_c", test_matrices) +@pytest.mark.parametrize("A", test_matrices) @pytest.mark.parametrize("k", [-1, 1, np.pi, np.e]) -def test_matrix_truediv(A_c, k): - A = Matrix(A_c) +def test_matrix_truediv(A, k): + A = Matrix(A) assert A / k == np.array(A) / k -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_matmul_matrices(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_matmul_matrices(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) -def test_matrix_matmul_vectors(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Vector(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) +def test_matrix_matmul_vectors(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Vector(B) == expected_result @pytest.mark.parametrize("k", [0, 1, 2, 3, 4, 5]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_pow(A_c, k): - A = Matrix(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_pow(A, k): + A = Matrix(A) assert A**k == np.linalg.matrix_power(A, k) @pytest.mark.parametrize("matrix_components", test_matrices) def test_matrix_eq(matrix_components): matrix = Matrix(matrix_components) - assert matrix == matrix assert matrix == matrix_components assert (matrix == 2 * matrix) is False @@ -191,10 +191,10 @@ def test_matrix_element_wise(matrix_components, operation): ) -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_dot(A_c, B_c): - A, B = Matrix(A_c), Matrix(B_c) +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_dot(A, B): + A, B = Matrix(A), Matrix(B) assert A.dot(B) == np.dot(A, B) diff --git a/tests/unit/test_tools_vector.py b/tests/unit/test_tools_vector.py index c9b617c97..f9ded7161 100644 --- a/tests/unit/test_tools_vector.py +++ b/tests/unit/test_tools_vector.py @@ -69,7 +69,7 @@ def test_vector_cross_matrix(vector_components): def test_vector_abs(vector_components): vector = Vector(vector_components) vector_magnitude = abs(vector) - assert vector_magnitude == sum([i**2 for i in vector_components]) ** 0.5 + assert vector_magnitude == sum(i**2 for i in vector_components) ** 0.5 @pytest.mark.parametrize("vector_components", test_vectors) @@ -199,12 +199,14 @@ def test_vector_proj(u_c, v_c): @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_str(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval("Vector(" + str(vector) + ")") == vector @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_repr(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval(repr(vector).replace("(", "((").replace(")", "))")) == vector diff --git a/tests/unit/test_utilities.py b/tests/unit/test_utilities.py index 25bae57cf..a6d1972a7 100644 --- a/tests/unit/test_utilities.py +++ b/tests/unit/test_utilities.py @@ -1,4 +1,3 @@ -import csv from unittest.mock import patch import numpy as np @@ -21,7 +20,7 @@ (40, 21, 1.04, 0.2475236), ], ) -def test_compute_CdS_from_drop_test( +def test_compute_cd_s_from_drop_test( terminal_velocity, rocket_mass, air_density, result ): """Test if the function `compute_cd_s_from_drop_test` returns the correct @@ -45,42 +44,6 @@ def test_compute_CdS_from_drop_test( assert abs(cds - result) < 1e-6 -@pytest.mark.skip(reason="legacy tests") # it is not wokring -def test_create_dispersion_dictionary(): - """Test if the function returns a dictionary with the correct keys. - It reads the keys from the dictionary generated by the utilities function - and compares them to the expected. - Be careful if you change the "fixtures/monte_carlo/Valetudo_inputs.csv" file. - """ - - returned_dict = utilities.create_dispersion_dictionary( - "tests/fixtures/monte_carlo/Valetudo_inputs.csv" - ) - - test_dict = {} - with open("tests/fixtures/monte_carlo/Valetudo_inputs.csv", mode='r') as csvfile: - reader = csv.reader(csvfile, delimiter=';') - next(reader) # Skip header - for row in reader: - key, value, std_dev = row[1].strip(), row[2].strip(), row[3].strip() - if key: - if std_dev: - try: - test_dict[key] = (float(value), float(std_dev)) - except ValueError: - test_dict[key] = (value, std_dev) - else: - try: - test_dict[key] = float(value) - except ValueError: - try: - test_dict[key] = eval(value) - except SyntaxError: - test_dict[key] = value - - assert returned_dict == test_dict - - # Tests not passing in the CI, but passing locally due to # different values in the ubuntu and windows machines From 4f03baab1031860bb1704a0969f8b6d938b3ed84 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 5 Jul 2024 22:07:40 -0300 Subject: [PATCH 2/8] DEV: configure flake8 for RocketPy repo --- Makefile | 9 ++++++++- pyproject.toml | 13 ++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 4ec6513b3..d0c198873 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,19 @@ install: pip install -r requirements-optional.txt pip install -e . +format: isort black + isort: isort --profile black rocketpy/ tests/ docs/ black: black rocketpy/ tests/ docs/ - + +lint: flake8 pylint + +flake8: + flake8 rocketpy/ tests/ + pylint: -pylint rocketpy --output=.pylint-report.txt diff --git a/pyproject.toml b/pyproject.toml index 1fcb31707..5e541a8ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,11 +75,14 @@ exclude_also = [ max-line-length = 88 max-module-lines = 3000 ignore = [ - 'W503', # conflicts with black - 'E203', # conflicts with black - 'E501', # line too long, already checked by black and pylint - 'E266', # too many leading '#' for block comment, this is pointless - 'F401', # imported but unused, already checked by pylint + 'W503', # conflicts with black (line break before binary operator) + 'E203', # conflicts with black (whitespace before ':') + 'E501', # ignored now because it is hard to fix the whole code (line too long) + 'E266', # this is pointless (too many leading '#' for block comment) + 'F401', # too many errors on __init__.py files (imported but unused) + 'E722', # pylint already checks for bare except + 'E226', # black does not adjust errors like this + 'E731', # pylint already checks for this (lambda functions) ] exclude = [ '.git,__pycache__', From b9fef199d73b5acc2df6ee5cff8e925756a8de02 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 5 Jul 2024 22:08:56 -0300 Subject: [PATCH 3/8] MNT: fixes flake8 warnings --- rocketpy/environment/environment.py | 8 ++++---- rocketpy/mathutils/function.py | 2 +- rocketpy/prints/environment_prints.py | 2 +- rocketpy/prints/hybrid_motor_prints.py | 6 ++++-- rocketpy/simulation/flight.py | 6 +++--- rocketpy/simulation/monte_carlo.py | 8 ++++---- tests/acceptance/test_bella_lui_rocket.py | 10 +++++----- tests/acceptance/test_ndrt_2020_rocket.py | 17 ++++++----------- tests/fixtures/rockets/rocket_fixtures.py | 8 ++++---- tests/integration/test_flight.py | 2 +- tests/unit/test_environment.py | 8 ++++---- tests/unit/test_function.py | 2 +- tests/unit/test_plots.py | 2 +- tests/unit/test_rocket.py | 2 +- tests/unit/test_tank.py | 4 ---- 15 files changed, 40 insertions(+), 47 deletions(-) diff --git a/rocketpy/environment/environment.py b/rocketpy/environment/environment.py index 63705a65d..8e4fb6fc4 100644 --- a/rocketpy/environment/environment.py +++ b/rocketpy/environment/environment.py @@ -3658,14 +3658,14 @@ def geodesic_to_utm( F = (1 - e2 / 4 - 3 * A / 64 - 5 * B / 256) * lat G = (3 * e2 / 8 + 3 * A / 32 + 45 * B / 1024) * C H = (15 * A / 256 + 45 * B / 1024) * D - I = (35 * B / 3072) * E + aux_i = (35 * B / 3072) * E # Evaluate other reference parameters n = semi_major_axis / ((1 - e2 * (np.sin(lat) ** 2)) ** 0.5) t = np.tan(lat) ** 2 c = e2lin * (np.cos(lat) ** 2) ag = (lon - lon_mc) * np.cos(lat) - m = semi_major_axis * (F - G + H - I) + m = semi_major_axis * (F - G + H - aux_i) # Evaluate new auxiliary parameters J = (1 - t + c) * ag * ag * ag / 6 @@ -3764,7 +3764,7 @@ def utm_to_geodesic( d = (x - 500000) / (n1 * K0) # Calculate other auxiliary values - I = (5 + 3 * t1 + 10 * c1 - 4 * c1 * c1 - 9 * e2lin) * d * d * d * d / 24 + aux_i = (5 + 3 * t1 + 10 * c1 - 4 * c1 * c1 - 9 * e2lin) * d * d * d * d / 24 J = ( (61 + 90 * t1 + 298 * c1 + 45 * t1 * t1 - 252 * e2lin - 3 * c1 * c1) * (d**6) @@ -3778,7 +3778,7 @@ def utm_to_geodesic( ) # Finally calculate the coordinates in lat/lot - lat = lat1 - (n1 * np.tan(lat1) / r1) * (d * d / 2 - I + J) + lat = lat1 - (n1 * np.tan(lat1) / r1) * (d * d / 2 - aux_i + J) lon = central_meridian * np.pi / 180 + (K + L) / np.cos(lat1) # Convert final lat/lon to Degrees diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 9c6dc388f..d10ffe89a 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -3034,7 +3034,7 @@ def __validate_inputs(self, inputs): ) if self.__dom_dim__ > 1: if inputs is None: - return [f"Input {i+1}" for i in range(self.__dom_dim__)] + return [f"Input {i + 1}" for i in range(self.__dom_dim__)] if isinstance(inputs, list): if len(inputs) == self.__dom_dim__ and all( isinstance(i, str) for i in inputs diff --git a/rocketpy/prints/environment_prints.py b/rocketpy/prints/environment_prints.py index 6838559b4..ecbdcab7c 100644 --- a/rocketpy/prints/environment_prints.py +++ b/rocketpy/prints/environment_prints.py @@ -39,7 +39,7 @@ def gravity_details(self): print("\nGravity Details\n") print(f"Acceleration of gravity at surface level: {surface_gravity:9.4f} m/s²") print( - f"Acceleration of gravity at {max_expected_height/1000:7.3f} " + f"Acceleration of gravity at {max_expected_height / 1000:7.3f} " f"km (ASL): {ceiling_gravity:.4f} m/s²\n" ) diff --git a/rocketpy/prints/hybrid_motor_prints.py b/rocketpy/prints/hybrid_motor_prints.py index e73f96c7b..4dcd7b113 100644 --- a/rocketpy/prints/hybrid_motor_prints.py +++ b/rocketpy/prints/hybrid_motor_prints.py @@ -39,8 +39,10 @@ def nozzle_details(self): print("Nozzle Details") print(f"Outlet Radius: {self.hybrid_motor.nozzle_radius} m") print(f"Throat Radius: {self.hybrid_motor.solid.throat_radius} m") - print(f"Outlet Area: {np.pi*self.hybrid_motor.nozzle_radius**2:.6f} m²") - print(f"Throat Area: {np.pi*self.hybrid_motor.solid.throat_radius**2:.6f} m²") + print(f"Outlet Area: {np.pi * self.hybrid_motor.nozzle_radius ** 2:.6f} m²") + print( + f"Throat Area: {np.pi * self.hybrid_motor.solid.throat_radius ** 2:.6f} m²" + ) print(f"Position: {self.hybrid_motor.nozzle_position} m\n") def grain_details(self): diff --git a/rocketpy/simulation/flight.py b/rocketpy/simulation/flight.py index 99e21f00d..f5bfc8666 100644 --- a/rocketpy/simulation/flight.py +++ b/rocketpy/simulation/flight.py @@ -1670,13 +1670,13 @@ def u_dot_generalized( ## Nozzle gyration tensor S_nozzle = self.rocket.nozzle_gyration_tensor ## Inertia tensor - I = self.rocket.get_inertia_tensor_at_time(t) + inertia_tensor = self.rocket.get_inertia_tensor_at_time(t) ## Inertia tensor time derivative in the body frame I_dot = self.rocket.get_inertia_tensor_derivative_at_time(t) # Calculate the Inertia tensor relative to CM H = (r_CM.cross_matrix @ -r_CM.cross_matrix) * total_mass - I_CM = I - H + I_CM = inertia_tensor - H # Prepare transformation matrices K = Matrix.transformation(e) @@ -1820,7 +1820,7 @@ def u_dot_generalized( ) T21 = ( - ((I @ w) ^ w) + ((inertia_tensor @ w) ^ w) + T05 @ w - (weight_in_body_frame ^ r_CM) + Vector([M1, M2, M3]) diff --git a/rocketpy/simulation/monte_carlo.py b/rocketpy/simulation/monte_carlo.py index 8b7e7f0f0..0e3a06bd3 100644 --- a/rocketpy/simulation/monte_carlo.py +++ b/rocketpy/simulation/monte_carlo.py @@ -2,14 +2,14 @@ Monte Carlo Simulation Module for RocketPy This module defines the `MonteCarlo` class, which is used to perform Monte Carlo -simulations of rocket flights. The Monte Carlo simulation is a powerful tool for -understanding the variability and uncertainty in the performance of rocket flights +simulations of rocket flights. The Monte Carlo simulation is a powerful tool for +understanding the variability and uncertainty in the performance of rocket flights by running multiple simulations with varied input parameters. Notes ----- -This module is still under active development, and some features or attributes may -change in future versions. Users are encouraged to check for updates and read the +This module is still under active development, and some features or attributes may +change in future versions. Users are encouraged to check for updates and read the latest documentation. """ diff --git a/tests/acceptance/test_bella_lui_rocket.py b/tests/acceptance/test_bella_lui_rocket.py index 0041074a8..42041bf42 100644 --- a/tests/acceptance/test_bella_lui_rocket.py +++ b/tests/acceptance/test_bella_lui_rocket.py @@ -4,6 +4,7 @@ # Importing libraries import matplotlib as mpl import numpy as np +from scipy.signal import savgol_filter from rocketpy import Environment, Flight, Function, Rocket, SolidMotor @@ -103,20 +104,20 @@ def test_bella_lui_rocket_data_asserts_acceptance(): ) BellaLui.set_rail_buttons(0.1, -0.5) BellaLui.add_motor(K828FJ, parameters.get("distance_rocket_nozzle")[0]) - NoseCone = BellaLui.add_nose( + BellaLui.add_nose( length=parameters.get("nose_length")[0], kind="tangent", position=parameters.get("nose_distance_to_cm")[0] + parameters.get("nose_length")[0], ) - fin_set = BellaLui.add_trapezoidal_fins( + BellaLui.add_trapezoidal_fins( 3, span=parameters.get("fin_span")[0], root_chord=parameters.get("fin_root_chord")[0], tip_chord=parameters.get("fin_tip_chord")[0], position=parameters.get("fin_distance_to_cm")[0], ) - tail = BellaLui.add_tail( + BellaLui.add_tail( top_radius=parameters.get("tail_top_radius")[0], bottom_radius=parameters.get("tail_bottom_radius")[0], length=parameters.get("tail_length")[0], @@ -130,7 +131,7 @@ def drogue_trigger(p, h, y): # activate drogue when vz < 0 m/s. return True if y[5] < 0 else False - Drogue = BellaLui.add_parachute( + BellaLui.add_parachute( "Drogue", cd_s=parameters.get("CdS_drogue")[0], trigger=drogue_trigger, @@ -213,7 +214,6 @@ def drogue_trigger(p, h, y): acceleration_rcp.append(test_flight.az(test_flight.t_final)) # Acceleration comparison (will not be used in our publication) - from scipy.signal import savgol_filter # Calculate the acceleration as a velocity derivative acceleration_kalt = [0] diff --git a/tests/acceptance/test_ndrt_2020_rocket.py b/tests/acceptance/test_ndrt_2020_rocket.py index a0b812f10..9cc66c897 100644 --- a/tests/acceptance/test_ndrt_2020_rocket.py +++ b/tests/acceptance/test_ndrt_2020_rocket.py @@ -2,7 +2,7 @@ import pandas as pd from scipy.signal import savgol_filter -from rocketpy import Environment, Flight, Function, Rocket, SolidMotor +from rocketpy import Environment, Flight, Rocket, SolidMotor def test_ndrt_2020_rocket_data_asserts_acceptance(): @@ -17,11 +17,6 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): # Drift: 2275 ft # Importing libraries - import numpy as np - import pandas as pd - from scipy.signal import savgol_filter - - from rocketpy import Environment, Flight, Function, Rocket, SolidMotor # Defining all parameters parameters = { @@ -118,20 +113,20 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): ) NDRT2020.set_rail_buttons(0.2, -0.5, 45) NDRT2020.add_motor(L1395, parameters.get("distance_rocket_nozzle")[0]) - nose_cone = NDRT2020.add_nose( + NDRT2020.add_nose( length=parameters.get("nose_length")[0], kind="tangent", position=parameters.get("nose_distance_to_cm")[0] + parameters.get("nose_length")[0], ) - fin_set = NDRT2020.add_trapezoidal_fins( + NDRT2020.add_trapezoidal_fins( 3, span=parameters.get("fin_span")[0], root_chord=parameters.get("fin_root_chord")[0], tip_chord=parameters.get("fin_tip_chord")[0], position=parameters.get("fin_distance_to_cm")[0], ) - transition = NDRT2020.add_tail( + NDRT2020.add_tail( top_radius=parameters.get("transition_top_radius")[0], bottom_radius=parameters.get("transition_bottom_radius")[0], length=parameters.get("transition_length")[0], @@ -151,7 +146,7 @@ def main_trigger(p, h, y): # activate main when vz < 0 m/s and z < 167.64 m (AGL) or 550 ft (AGL) return True if y[5] < 0 and h < 167.64 else False - Drogue = NDRT2020.add_parachute( + NDRT2020.add_parachute( "Drogue", cd_s=parameters.get("cd_s_drogue")[0], trigger=drogue_trigger, @@ -159,7 +154,7 @@ def main_trigger(p, h, y): lag=parameters.get("lag_rec")[0], noise=(0, 8.3, 0.5), ) - Main = NDRT2020.add_parachute( + NDRT2020.add_parachute( "Main", cd_s=parameters.get("cd_s_main")[0], trigger=main_trigger, diff --git a/tests/fixtures/rockets/rocket_fixtures.py b/tests/fixtures/rockets/rocket_fixtures.py index bfc4c2473..702506d06 100644 --- a/tests/fixtures/rockets/rocket_fixtures.py +++ b/tests/fixtures/rockets/rocket_fixtures.py @@ -335,8 +335,8 @@ def prometheus_cd_at_ma(mach): prometheus.set_rail_buttons(0.69, 0.21, 60) prometheus.add_motor(motor=generic_motor_cesaroni_M1520, position=0) - nose_cone = prometheus.add_nose(length=0.742, kind="Von Karman", position=2.229) - fin_set = prometheus.add_trapezoidal_fins( + prometheus.add_nose(length=0.742, kind="Von Karman", position=2.229) + prometheus.add_trapezoidal_fins( n=3, span=0.13, root_chord=0.268, @@ -344,12 +344,12 @@ def prometheus_cd_at_ma(mach): position=0.273, sweep_length=0.066, ) - drogue_chute = prometheus.add_parachute( + prometheus.add_parachute( "Drogue", cd_s=1.6 * np.pi * 0.3048**2, # Cd = 1.6, D_chute = 24 in trigger="apogee", ) - main_chute = prometheus.add_parachute( + prometheus.add_parachute( "Main", cd_s=2.2 * np.pi * 0.9144**2, # Cd = 2.2, D_chute = 72 in trigger=457.2, # 1500 ft diff --git a/tests/integration/test_flight.py b/tests/integration/test_flight.py index b119e69bb..fd8625435 100644 --- a/tests/integration/test_flight.py +++ b/tests/integration/test_flight.py @@ -327,7 +327,7 @@ def test_rolling_flight( test_rocket.set_rail_buttons(0.082, -0.618) test_rocket.add_motor(cesaroni_m1670, position=-1.373) - fin_set = test_rocket.add_trapezoidal_fins( + test_rocket.add_trapezoidal_fins( 4, span=0.100, root_chord=0.120, diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index 58c0203cd..a06b92fdb 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -79,8 +79,8 @@ def test_geodesic_coordinate_geodesic_to_utm_converts_coordinate(): semi_major_axis=6378137.0, # WGS84 flattening=1 / 298.257223563, # WGS84 ) - assert np.isclose(x, 315468.64, atol=1e-5) == True - assert np.isclose(y, 3651938.65, atol=1e-5) == True + assert np.isclose(x, 315468.64, atol=1e-5) + assert np.isclose(y, 3651938.65, atol=1e-5) assert utm_zone == 13 assert utm_letter == "S" assert hemis == "N" @@ -101,8 +101,8 @@ class and checks the conversion results from UTM to geodesic semi_major_axis=6378137.0, # WGS84 flattening=1 / 298.257223563, # WGS84 ) - assert np.isclose(lat, 32.99025, atol=1e-5) == True - assert np.isclose(lon, -106.9750, atol=1e-5) == True + assert np.isclose(lat, 32.99025, atol=1e-5) + assert np.isclose(lon, -106.9750, atol=1e-5) @pytest.mark.parametrize( diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index 101fb81ff..3c1934f9f 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -1,4 +1,4 @@ -"""Unit tests for the Function class. Each method in tis module tests an +"""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.""" diff --git a/tests/unit/test_plots.py b/tests/unit/test_plots.py index cafd7cf8b..db36264d8 100644 --- a/tests/unit/test_plots.py +++ b/tests/unit/test_plots.py @@ -39,4 +39,4 @@ def test_compare(mock_show, flight_calisto): x_attributes=["time"], ) - assert isinstance(fig, plt.Figure) == True + assert isinstance(fig, plt.Figure) diff --git a/tests/unit/test_rocket.py b/tests/unit/test_rocket.py index 06839603f..876f5024d 100644 --- a/tests/unit/test_rocket.py +++ b/tests/unit/test_rocket.py @@ -245,7 +245,7 @@ def test_add_fins_assert_cp_cm_plus_fins(calisto, dimensionless_calisto, m): @pytest.mark.parametrize( - """cdm_position, grain_cm_position, nozzle_position, coord_direction, + """cdm_position, grain_cm_position, nozzle_position, coord_direction, motor_position, expected_motor_cdm, expected_motor_cpp""", [ (0.317, 0.397, 0, "nozzle_to_combustion_chamber", -1.373, -1.056, -0.976), diff --git a/tests/unit/test_tank.py b/tests/unit/test_tank.py index 7d4b3884f..13c7b6cb8 100644 --- a/tests/unit/test_tank.py +++ b/tests/unit/test_tank.py @@ -131,10 +131,6 @@ def test_mass_based_tank(): tank and a simplified tank. """ lox = Fluid(name="LOx", density=1141.7) - propane = Fluid( - name="Propane", - density=493, - ) n2 = Fluid( name="Nitrogen Gas", density=51.75, From 92e29ada7ccb43145ad08f11416adf598316b3e9 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Fri, 5 Jul 2024 22:10:27 -0300 Subject: [PATCH 4/8] DEV: modifies CI to execute flake8 on PR updates --- .github/workflows/linters.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 4c7f81140..0c20918ab 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -28,7 +28,7 @@ jobs: python -m pip install --upgrade pip pip install .[all] pip install .[tests] - pip install pylint isort + pip install pylint isort flake8 black - name: Run isort run: isort --check-only rocketpy/ tests/ docs/ --profile black - name: Run black @@ -36,6 +36,8 @@ jobs: with: options: "--check rocketpy/ tests/ docs/" jupyter: true + - name: Run flake8 + run: flake8 rocketpy/ tests/ - name: Run pylint run: | pylint rocketpy/ From 0d01bf010aad02a0ccbe72181a7a870fa5e00814 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 6 Jul 2024 00:45:38 -0300 Subject: [PATCH 5/8] MNT: fix pylint errors in the `tests` module --- .github/workflows/linters.yml | 2 +- Makefile | 2 +- rocketpy/utilities.py | 1 + tests/acceptance/test_ndrt_2020_rocket.py | 28 ++- tests/fixtures/function/function_fixtures.py | 2 +- tests/fixtures/motor/solid_motor_fixtures.py | 25 ++ tests/integration/test_environment.py | 17 +- .../integration/test_environment_analysis.py | 4 +- tests/integration/test_flight.py | 170 +++++++------ tests/integration/test_function.py | 10 +- tests/integration/test_genericmotor.py | 5 +- tests/integration/test_hybridmotor.py | 3 +- tests/integration/test_monte_carlo.py | 3 +- tests/integration/test_plots.py | 5 +- tests/integration/test_rocket.py | 8 +- tests/unit/test_environment.py | 20 +- tests/unit/test_environment_analysis.py | 8 +- tests/unit/test_flight.py | 61 ++--- tests/unit/test_flight_time_nodes.py | 4 +- tests/unit/test_function.py | 151 ++++++----- tests/unit/test_genericmotor.py | 79 +++--- tests/unit/test_hybridmotor.py | 86 +++---- tests/unit/test_liquidmotor.py | 70 +++--- tests/unit/test_monte_carlo.py | 3 - tests/unit/test_plots.py | 6 +- tests/unit/test_rocket.py | 159 ++++++------ tests/unit/test_solidmotor.py | 2 +- tests/unit/test_tank.py | 235 +++++++++++------- tests/unit/test_tools_matrix.py | 76 +++--- tests/unit/test_tools_vector.py | 4 +- tests/unit/test_utilities.py | 39 +-- 31 files changed, 657 insertions(+), 631 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 0c20918ab..1eee717d2 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -40,4 +40,4 @@ jobs: run: flake8 rocketpy/ tests/ - name: Run pylint run: | - pylint rocketpy/ + pylint rocketpy/ tests/ diff --git a/Makefile b/Makefile index d0c198873..07c620ade 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ flake8: flake8 rocketpy/ tests/ pylint: - -pylint rocketpy --output=.pylint-report.txt + -pylint rocketpy/ tests/ --output=.pylint-report.txt build-docs: cd docs && $(PYTHON) -m pip install -r requirements.txt && make html diff --git a/rocketpy/utilities.py b/rocketpy/utilities.py index 3a724e46f..adb925eee 100644 --- a/rocketpy/utilities.py +++ b/rocketpy/utilities.py @@ -424,6 +424,7 @@ def _flutter_prints( print(f"Altitude of minimum Safety Factor: {altitude_min_sf:.3f} m (AGL)\n") +# TODO: deprecate and delete this function. Never used and now we have Monte Carlo. def create_dispersion_dictionary(filename): """Creates a dictionary with the rocket data provided by a .csv file. File should be organized in four columns: attribute_class, parameter_name, diff --git a/tests/acceptance/test_ndrt_2020_rocket.py b/tests/acceptance/test_ndrt_2020_rocket.py index 9cc66c897..aa4e737d4 100644 --- a/tests/acceptance/test_ndrt_2020_rocket.py +++ b/tests/acceptance/test_ndrt_2020_rocket.py @@ -64,19 +64,19 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): } # Environment conditions - Env23 = Environment( + env = Environment( gravity=9.81, latitude=41.775447, longitude=-86.572467, date=(2020, 2, 23, 16), elevation=206, ) - Env23.set_atmospheric_model( + env.set_atmospheric_model( type="Reanalysis", file="tests/fixtures/acceptance/NDRT_2020/ndrt_2020_weather_data_ERA5.nc", dictionary="ECMWF", ) - Env23.max_expected_height = 2000 + env.max_expected_height = 2000 # motor information L1395 = SolidMotor( @@ -134,13 +134,13 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): ) # Parachute set-up - def drogue_trigger(p, h, y): + def drogue_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate drogue when vz < 0 m/s. return True if y[5] < 0 else False - def main_trigger(p, h, y): + def main_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate main when vz < 0 m/s and z < 167.64 m (AGL) or 550 ft (AGL) @@ -164,17 +164,19 @@ def main_trigger(p, h, y): ) # Flight - Flight23 = Flight( + rocketpy_flight = Flight( rocket=NDRT2020, - environment=Env23, + environment=env, rail_length=parameters.get("rail_length")[0], inclination=parameters.get("inclination")[0], heading=parameters.get("heading")[0], ) - df_ndrt_rocketpy = pd.DataFrame(Flight23.z[:, :], columns=["Time", "Altitude"]) - df_ndrt_rocketpy["Vertical Velocity"] = Flight23.vz[:, 1] - # df_ndrt_rocketpy["Vertical Acceleration"] = Flight23.az[:, 1] - df_ndrt_rocketpy["Altitude"] -= Env23.elevation + df_ndrt_rocketpy = pd.DataFrame( + rocketpy_flight.z[:, :], columns=["Time", "Altitude"] + ) + df_ndrt_rocketpy["Vertical Velocity"] = rocketpy_flight.vz[:, 1] + # df_ndrt_rocketpy["Vertical Acceleration"] = rocketpy_flight.az[:, 1] + df_ndrt_rocketpy["Altitude"] -= env.elevation # Reading data from the flightData (sensors: Raven) df_ndrt_raven = pd.read_csv( @@ -205,14 +207,14 @@ def main_trigger(p, h, y): apogee_time_measured = df_ndrt_raven.loc[ df_ndrt_raven[" Altitude (Ft-AGL)"].idxmax(), " Time (s)" ] - apogee_time_simulated = Flight23.apogee_time + apogee_time_simulated = rocketpy_flight.apogee_time assert ( abs(max(df_ndrt_raven[" Altitude (m-AGL)"]) - max(df_ndrt_rocketpy["Altitude"])) / max(df_ndrt_raven[" Altitude (m-AGL)"]) < 0.015 ) - assert (max(velocity_raven_filt) - Flight23.max_speed) / max( + assert (max(velocity_raven_filt) - rocketpy_flight.max_speed) / max( velocity_raven_filt ) < 0.06 assert ( diff --git a/tests/fixtures/function/function_fixtures.py b/tests/fixtures/function/function_fixtures.py index 5b195c16b..7cba3699e 100644 --- a/tests/fixtures/function/function_fixtures.py +++ b/tests/fixtures/function/function_fixtures.py @@ -134,7 +134,7 @@ def lambda_quad_func(): Function A lambda function based on a string. """ - func = lambda x: x**2 # pylint: disable=unnecessary-lambda + func = lambda x: x**2 # pylint: disable=unnecessary-lambda-assignment return Function( source=func, ) diff --git a/tests/fixtures/motor/solid_motor_fixtures.py b/tests/fixtures/motor/solid_motor_fixtures.py index 587d5e970..eff7d65d5 100644 --- a/tests/fixtures/motor/solid_motor_fixtures.py +++ b/tests/fixtures/motor/solid_motor_fixtures.py @@ -117,3 +117,28 @@ def dimensionless_cesaroni_m1670(kg, m): # old name: dimensionless_motor coordinate_system_orientation="nozzle_to_combustion_chamber", ) return example_motor + + +@pytest.fixture +def dummy_empty_motor(): + # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant + # TODO: why don t we use these same values to create EmptyMotor class? + return SolidMotor( + thrust_source=1e-300, + burn_time=1e-10, + dry_mass=1.815, + dry_inertia=(0.125, 0.125, 0.002), + center_of_dry_mass_position=0.317, + grains_center_of_mass_position=0.397, + grain_number=5, + grain_separation=5 / 1000, + grain_density=1e-300, + grain_outer_radius=33 / 1000, + grain_initial_inner_radius=15 / 1000, + grain_initial_height=120 / 1000, + nozzle_radius=33 / 1000, + throat_radius=11 / 1000, + nozzle_position=0, + interpolation_method="linear", + coordinate_system_orientation="nozzle_to_combustion_chamber", + ) diff --git a/tests/integration/test_environment.py b/tests/integration/test_environment.py index 6f0d3fc09..3013d879c 100644 --- a/tests/integration/test_environment.py +++ b/tests/integration/test_environment.py @@ -100,7 +100,7 @@ def test_gefs_atmosphere( @patch("matplotlib.pyplot.show") def test_custom_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the custom atmosphere model in the environment object. Parameters @@ -127,7 +127,7 @@ def test_custom_atmosphere( @patch("matplotlib.pyplot.show") def test_standard_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the standard atmosphere model in the environment object. Parameters @@ -148,7 +148,7 @@ def test_standard_atmosphere( @patch("matplotlib.pyplot.show") def test_wyoming_sounding_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Asserts whether the Wyoming sounding model in the environment object behaves as expected with respect to some attributes such as pressure, barometric_height, wind_velocity and temperature. @@ -163,15 +163,14 @@ def test_wyoming_sounding_atmosphere( # TODO:: this should be added to the set_atmospheric_model() method as a # "file" option, instead of receiving the URL as a string. - URL = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" + url = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" # give it at least 5 times to try to download the file for i in range(5): try: - example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=URL) + example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=url) break - except: - time.sleep(1) # wait 1 second before trying again - pass + except Exception: # pylint: disable=broad-except + time.sleep(2**i) assert example_plain_env.all_info() is None assert abs(example_plain_env.pressure(0) - 93600.0) < 1e-8 assert ( @@ -227,7 +226,7 @@ def test_hiresw_ensemble_atmosphere( @patch("matplotlib.pyplot.show") def test_cmc_atmosphere( mock_show, example_spaceport_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the Ensemble model with the CMC file. Parameters diff --git a/tests/integration/test_environment_analysis.py b/tests/integration/test_environment_analysis.py index 17129e6f1..1be33fe96 100644 --- a/tests/integration/test_environment_analysis.py +++ b/tests/integration/test_environment_analysis.py @@ -10,7 +10,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, env_analysis): +def test_all_info(mock_show, env_analysis): # pylint: disable=unused-argument """Test the EnvironmentAnalysis.all_info() method, which already invokes several other methods. It is a good way to test the whole class in a first view. However, if it fails, it is hard to know which method is failing. @@ -32,7 +32,7 @@ def test_all_info(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_exports(mock_show, env_analysis): +def test_exports(mock_show, env_analysis): # pylint: disable=unused-argument """Check the export methods of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the files are correct, as this would require a lot of work and would be diff --git a/tests/integration/test_flight.py b/tests/integration/test_flight.py index fd8625435..8ac6e2936 100644 --- a/tests/integration/test_flight.py +++ b/tests/integration/test_flight.py @@ -11,7 +11,7 @@ @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused-argument +def test_all_info(mock_show, flight_calisto_robust): # pylint: disable=unused-argument """Test that the flight class is working as intended. This basically calls the all_info() method and checks if it returns None. It is not testing if the values are correct, but whether the method is working without errors. @@ -27,65 +27,61 @@ def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused- assert flight_calisto_robust.all_info() is None -def test_export_data(flight_calisto): - """Tests wether the method Flight.export_data is working as intended - - Parameters: - ----------- - flight_calisto : rocketpy.Flight - Flight object to be tested. See the conftest.py file for more info - regarding this pytest fixture. - """ - test_flight = flight_calisto - - # Basic export - test_flight.export_data("test_export_data_1.csv") - - # Custom export - test_flight.export_data( - "test_export_data_2.csv", - "z", - "vz", - "e1", - "w3", - "angle_of_attack", - time_step=0.1, - ) - - # Load exported files and fixtures and compare them - test_1 = np.loadtxt("test_export_data_1.csv", delimiter=",") - test_2 = np.loadtxt("test_export_data_2.csv", delimiter=",") - - # Delete files - os.remove("test_export_data_1.csv") - os.remove("test_export_data_2.csv") - - # Check if basic exported content matches data - assert np.allclose(test_flight.x[:, 0], test_1[:, 0], atol=1e-5) - assert np.allclose(test_flight.x[:, 1], test_1[:, 1], atol=1e-5) - assert np.allclose(test_flight.y[:, 1], test_1[:, 2], atol=1e-5) - assert np.allclose(test_flight.z[:, 1], test_1[:, 3], atol=1e-5) - assert np.allclose(test_flight.vx[:, 1], test_1[:, 4], atol=1e-5) - assert np.allclose(test_flight.vy[:, 1], test_1[:, 5], atol=1e-5) - assert np.allclose(test_flight.vz[:, 1], test_1[:, 6], atol=1e-5) - assert np.allclose(test_flight.e0[:, 1], test_1[:, 7], atol=1e-5) - assert np.allclose(test_flight.e1[:, 1], test_1[:, 8], atol=1e-5) - assert np.allclose(test_flight.e2[:, 1], test_1[:, 9], atol=1e-5) - assert np.allclose(test_flight.e3[:, 1], test_1[:, 10], atol=1e-5) - assert np.allclose(test_flight.w1[:, 1], test_1[:, 11], atol=1e-5) - assert np.allclose(test_flight.w2[:, 1], test_1[:, 12], atol=1e-5) - assert np.allclose(test_flight.w3[:, 1], test_1[:, 13], atol=1e-5) - - # Check if custom exported content matches data - time_points = np.arange(test_flight.t_initial, test_flight.t_final, 0.1) - assert np.allclose(time_points, test_2[:, 0], atol=1e-5) - assert np.allclose(test_flight.z(time_points), test_2[:, 1], atol=1e-5) - assert np.allclose(test_flight.vz(time_points), test_2[:, 2], atol=1e-5) - assert np.allclose(test_flight.e1(time_points), test_2[:, 3], atol=1e-5) - assert np.allclose(test_flight.w3(time_points), test_2[:, 4], atol=1e-5) - assert np.allclose( - test_flight.angle_of_attack(time_points), test_2[:, 5], atol=1e-5 - ) +class TestExportData: + """Tests the export_data method of the Flight class.""" + + def test_basic_export(self, flight_calisto): + """Tests basic export functionality""" + file_name = "test_export_data_1.csv" + flight_calisto.export_data(file_name) + self.validate_basic_export(flight_calisto, file_name) + os.remove(file_name) + + def test_custom_export(self, flight_calisto): + """Tests custom export functionality""" + file_name = "test_export_data_2.csv" + flight_calisto.export_data( + file_name, + "z", + "vz", + "e1", + "w3", + "angle_of_attack", + time_step=0.1, + ) + self.validate_custom_export(flight_calisto, file_name) + os.remove(file_name) + + def validate_basic_export(self, flight_calisto, file_name): + """Validates the basic export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + assert np.allclose(flight_calisto.x[:, 0], test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.x[:, 1], test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.y[:, 1], test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.z[:, 1], test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.vx[:, 1], test_data[:, 4], atol=1e-5) + assert np.allclose(flight_calisto.vy[:, 1], test_data[:, 5], atol=1e-5) + assert np.allclose(flight_calisto.vz[:, 1], test_data[:, 6], atol=1e-5) + assert np.allclose(flight_calisto.e0[:, 1], test_data[:, 7], atol=1e-5) + assert np.allclose(flight_calisto.e1[:, 1], test_data[:, 8], atol=1e-5) + assert np.allclose(flight_calisto.e2[:, 1], test_data[:, 9], atol=1e-5) + assert np.allclose(flight_calisto.e3[:, 1], test_data[:, 10], atol=1e-5) + assert np.allclose(flight_calisto.w1[:, 1], test_data[:, 11], atol=1e-5) + assert np.allclose(flight_calisto.w2[:, 1], test_data[:, 12], atol=1e-5) + assert np.allclose(flight_calisto.w3[:, 1], test_data[:, 13], atol=1e-5) + + def validate_custom_export(self, flight_calisto, file_name): + """Validates the custom export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + time_points = np.arange(flight_calisto.t_initial, flight_calisto.t_final, 0.1) + assert np.allclose(time_points, test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.z(time_points), test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.vz(time_points), test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.e1(time_points), test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.w3(time_points), test_data[:, 4], atol=1e-5) + assert np.allclose( + flight_calisto.angle_of_attack(time_points), test_data[:, 5], atol=1e-5 + ) def test_export_kml(flight_calisto_robust): @@ -106,14 +102,13 @@ def test_export_kml(flight_calisto_robust): ) # Load exported files and fixtures and compare them - test_1 = open("test_export_data_1.kml", "r") - - for row in test_1: - if row[:29] == " ": - r = row[29:-15] - r = r.split(",") - for i, j in enumerate(r): - r[i] = j.split(" ") + with open("test_export_data_1.kml", "r") as test_1: + for row in test_1: + if row[:29] == " ": + r = row[29:-15] + r = r.split(",") + for i, j in enumerate(r): + r[i] = j.split(" ") lon, lat, z, coords = [], [], [], [] for i in r: for j in i: @@ -122,9 +117,6 @@ def test_export_kml(flight_calisto_robust): lon.append(float(coords[i])) lat.append(float(coords[i + 1])) z.append(float(coords[i + 2])) - - # Delete temporary test file - test_1.close() os.remove("test_export_data_1.kml") assert np.allclose(test_flight.latitude[:, 1], lat, atol=1e-3) @@ -161,7 +153,9 @@ def test_export_pressures(flight_calisto_robust): @patch("matplotlib.pyplot.show") -def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): +def test_hybrid_motor_flight( + mock_show, calisto_hybrid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a hybrid motor. This test only validates that a flight simulation can be performed with a hybrid motor; it does not validate the results. @@ -186,7 +180,9 @@ def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): @patch("matplotlib.pyplot.show") -def test_liquid_motor_flight(mock_show, calisto_liquid_modded): +def test_liquid_motor_flight( + mock_show, calisto_liquid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a liquid motor. This test only validates that a flight simulation can be performed with a liquid motor; it does not validate the results. @@ -212,7 +208,9 @@ def test_liquid_motor_flight(mock_show, calisto_liquid_modded): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): +def test_time_overshoot( + mock_show, calisto_robust, example_spaceport_env +): # pylint: disable=unused-argument """Test the time_overshoot parameter of the Flight class. This basically calls the all_info() method for a simulation without time_overshoot and checks if it returns None. It is not testing if the values are correct, @@ -241,7 +239,9 @@ def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): @patch("matplotlib.pyplot.show") -def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust): +def test_simpler_parachute_triggers( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests different types of parachute triggers. This is important to ensure the code is working as intended, since the parachute triggers can have very different format definitions. It will add 3 parachutes using different @@ -313,8 +313,8 @@ def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust @patch("matplotlib.pyplot.show") -def test_rolling_flight( - mock_show, # pylint: disable: unused-argument +def test_rolling_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -352,8 +352,8 @@ def test_rolling_flight( @patch("matplotlib.pyplot.show") -def test_eccentricity_on_flight( - mock_show, # pylint: disable: unused-argument +def test_eccentricity_on_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -383,7 +383,9 @@ def test_eccentricity_on_flight( @patch("matplotlib.pyplot.show") -def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): +def test_air_brakes_flight( + mock_show, flight_calisto_air_brakes +): # pylint: disable=unused-argument """Test the flight of a rocket with air brakes. This test only validates that a flight simulation can be performed with air brakes; it does not validate the results. @@ -403,7 +405,9 @@ def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): @patch("matplotlib.pyplot.show") -def test_initial_solution(mock_show, example_plain_env, calisto_robust): +def test_initial_solution( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests the initial_solution option of the Flight class. This test simply simulates the flight using the initial_solution option and checks if the all_info method returns None. @@ -448,7 +452,9 @@ def test_initial_solution(mock_show, example_plain_env, calisto_robust): @patch("matplotlib.pyplot.show") -def test_empty_motor_flight(mock_show, example_plain_env, calisto_motorless): +def test_empty_motor_flight( + mock_show, example_plain_env, calisto_motorless +): # pylint: disable=unused-argument flight = Flight( rocket=calisto_motorless, environment=example_plain_env, diff --git a/tests/integration/test_function.py b/tests/integration/test_function.py index 7b6f204eb..a7e3144e5 100644 --- a/tests/integration/test_function.py +++ b/tests/integration/test_function.py @@ -112,15 +112,15 @@ def test_func_from_csv_with_header(csv_file): line. It tests cases where the fields are separated by quotes and without quotes.""" f = Function(csv_file) - assert f.__repr__() == "'Function from R1 to R1 : (time) → (value)'" + assert repr(f) == "'Function from R1 to R1 : (time) → (value)'" assert np.isclose(f(0), 100) assert np.isclose(f(0) + f(1), 300), "Error summing the values of the function" @patch("matplotlib.pyplot.show") -def test_plots( +def test_plots( # pylint: disable=unused-argument mock_show, func_from_csv, func_2d_from_csv -): # pylint: disable: unused-argument +): """Test different plot methods of the Function class. Parameters @@ -150,7 +150,7 @@ def test_plots( @patch("matplotlib.pyplot.show") -def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_dataset_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable dataset.""" # Test plane f(x,y) = x - y source = [ @@ -171,7 +171,7 @@ def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argum @patch("matplotlib.pyplot.show") -def test_multivariable_function_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_function_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable function.""" def source(x, y): diff --git a/tests/integration/test_genericmotor.py b/tests/integration/test_genericmotor.py index 8b5a18a15..6373fc055 100644 --- a/tests/integration/test_genericmotor.py +++ b/tests/integration/test_genericmotor.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_generic_motor_info( - mock_show, generic_motor -): # pylint: disable: unused-argument +def test_generic_motor_info(mock_show, generic_motor): """Tests the GenericMotor.all_info() method. Parameters diff --git a/tests/integration/test_hybridmotor.py b/tests/integration/test_hybridmotor.py index 59f343132..1c7ed5cc8 100644 --- a/tests/integration/test_hybridmotor.py +++ b/tests/integration/test_hybridmotor.py @@ -1,8 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_hybrid_motor_info(mock_show, hybrid_motor): # pylint: disable: unused-argument +def test_hybrid_motor_info(mock_show, hybrid_motor): """Tests the HybridMotor.all_info() method. Parameters diff --git a/tests/integration/test_monte_carlo.py b/tests/integration/test_monte_carlo.py index 91838c828..5f11a9b25 100644 --- a/tests/integration/test_monte_carlo.py +++ b/tests/integration/test_monte_carlo.py @@ -1,3 +1,4 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch @@ -85,7 +86,7 @@ def test_monte_carlo_prints(monte_carlo_calisto): monte_carlo_calisto.info() -@patch("matplotlib.pyplot.show") +@patch("matplotlib.pyplot.show") # pylint: disable=unused-argument def test_monte_carlo_plots(mock_show, monte_carlo_calisto_pre_loaded): """Tests the plots methods of the MonteCarlo class.""" assert monte_carlo_calisto_pre_loaded.all_info() is None diff --git a/tests/integration/test_plots.py b/tests/integration/test_plots.py index edb8fad09..232ef71c6 100644 --- a/tests/integration/test_plots.py +++ b/tests/integration/test_plots.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch -import matplotlib.pyplot as plt - from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import CompareFlights @patch("matplotlib.pyplot.show") diff --git a/tests/integration/test_rocket.py b/tests/integration/test_rocket.py index db7eafeff..4d5daf7a6 100644 --- a/tests/integration/test_rocket.py +++ b/tests/integration/test_rocket.py @@ -80,7 +80,9 @@ def test_air_brakes_clamp_on( @patch("matplotlib.pyplot.show") -def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): +def test_air_brakes_clamp_off( # pylint: disable=unused-argument + mock_show, calisto_air_brakes_clamp_off +): """Test the air brakes class with clamp off configuration. This test checks the basic attributes and the deployment_level setter. It also checks the all_info method. @@ -115,7 +117,7 @@ def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): @patch("matplotlib.pyplot.show") -def test_rocket(mock_show, calisto_robust): +def test_rocket(mock_show, calisto_robust): # pylint: disable=unused-argument test_rocket = calisto_robust static_margin = test_rocket.static_margin(0) # Check if all_info and static_method methods are working properly @@ -123,7 +125,7 @@ def test_rocket(mock_show, calisto_robust): @patch("matplotlib.pyplot.show") -def test_aero_surfaces_infos( +def test_aero_surfaces_infos( # pylint: disable=unused-argument mock_show, calisto_nose_cone, calisto_tail, calisto_trapezoidal_fins ): assert calisto_nose_cone.all_info() is None diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index a06b92fdb..c4217331c 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -3,9 +3,9 @@ from unittest.mock import patch import numpy as np -import numpy.ma as ma import pytest import pytz +from numpy import ma from rocketpy import Environment @@ -73,18 +73,20 @@ def test_location_set_topographic_profile_computes_elevation( def test_geodesic_coordinate_geodesic_to_utm_converts_coordinate(): """Tests the conversion from geodesic to UTM coordinates.""" - x, y, utm_zone, utm_letter, hemis, EW = Environment.geodesic_to_utm( - lat=32.990254, - lon=-106.974998, - semi_major_axis=6378137.0, # WGS84 - flattening=1 / 298.257223563, # WGS84 + x, y, utm_zone, utm_letter, north_south_hemis, east_west_hemis = ( + Environment.geodesic_to_utm( + lat=32.990254, + lon=-106.974998, + semi_major_axis=6378137.0, # WGS84 + flattening=1 / 298.257223563, # WGS84 + ) ) assert np.isclose(x, 315468.64, atol=1e-5) assert np.isclose(y, 3651938.65, atol=1e-5) assert utm_zone == 13 assert utm_letter == "S" - assert hemis == "N" - assert EW == "W" + assert north_south_hemis == "N" + assert east_west_hemis == "W" def test_utm_to_geodesic_converts_coordinates(): @@ -159,7 +161,7 @@ def test_decimal_degrees_to_arc_seconds_computes_correct_values( @patch("matplotlib.pyplot.show") -def test_info_returns(mock_show, example_plain_env): +def test_info_returns(mock_show, example_plain_env): # pylint: disable=unused-argument """Tests the all_info_returned() all_plot_info_returned() and methods of the Environment class. diff --git a/tests/unit/test_environment_analysis.py b/tests/unit/test_environment_analysis.py index ed5fbc952..caa8fb847 100644 --- a/tests/unit/test_environment_analysis.py +++ b/tests/unit/test_environment_analysis.py @@ -11,7 +11,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_distribution_plots(mock_show, env_analysis): +def test_distribution_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the distribution plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -42,7 +42,7 @@ def test_distribution_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_average_plots(mock_show, env_analysis): +def test_average_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the average plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -68,7 +68,7 @@ def test_average_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_profile_plots(mock_show, env_analysis): +def test_profile_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the profile plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -138,7 +138,7 @@ def test_values(env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_animation_plots(mock_show, env_analysis): +def test_animation_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the animation plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 2f438c78c..078c682c9 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -5,7 +5,7 @@ import pytest from scipy import optimize -from rocketpy import Components, Environment, Flight, Function, Rocket, SolidMotor +from rocketpy import Components, Flight, Function, Rocket plt.rcParams.update({"figure.max_open_warning": 0}) @@ -190,13 +190,13 @@ def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_v The expected values of the aerodynamic moments vector at the point to be tested. """ - expected_attr, expected_M = flight_time, expected_values + expected_attr, expected_moment = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_M, abs=atol) == ( + assert pytest.approx(expected_moment, abs=atol) == ( test.M1(t), test.M2(t), test.M3(t), @@ -229,13 +229,13 @@ def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_va The expected values of the aerodynamic forces vector at the point to be tested. """ - expected_attr, expected_R = flight_time, expected_values + expected_attr, expected_forces = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_R, abs=atol) == ( + assert pytest.approx(expected_forces, abs=atol) == ( test.R1(t), test.R2(t), test.R3(t), @@ -507,7 +507,9 @@ def test_lat_lon_conversion_from_origin( "static_margin, max_time", [(-0.1, 2), (-0.01, 5), (0, 5), (0.01, 20), (0.1, 20), (1.0, 20)], ) -def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): +def test_stability_static_margins( + wind_u, wind_v, static_margin, max_time, example_plain_env, dummy_empty_motor +): """Test stability margins for a constant velocity flight, 100 m/s, wind a lateral wind speed of 10 m/s. Rocket has infinite mass to prevent side motion. Check if a restoring moment exists depending on static margins. @@ -522,11 +524,14 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): Static margin to be tested max_time : float Maximum time to be simulated + example_plain_env : rocketpy.Environment + This is a fixture. + dummy_empty_motor : rocketpy.SolidMotor + This is a fixture. """ # Create an environment with ZERO gravity to keep the rocket's speed constant - env = Environment(gravity=0, latitude=0, longitude=0, elevation=0) - env.set_atmospheric_model( + example_plain_env.set_atmospheric_model( type="custom_atmosphere", wind_u=wind_u, wind_v=wind_v, @@ -535,29 +540,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): ) # Make sure that the free_stream_mach will always be 0, so that the rocket # behaves as the STATIC (free_stream_mach=0) margin predicts - env.speed_of_sound = Function(1e16) - - # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant - # TODO: why don t we use these same values to create EmptyMotor class? - dummy_motor = SolidMotor( - thrust_source=1e-300, - burn_time=1e-10, - dry_mass=1.815, - dry_inertia=(0.125, 0.125, 0.002), - center_of_dry_mass_position=0.317, - grains_center_of_mass_position=0.397, - grain_number=5, - grain_separation=5 / 1000, - grain_density=1e-300, - grain_outer_radius=33 / 1000, - grain_initial_inner_radius=15 / 1000, - grain_initial_height=120 / 1000, - nozzle_radius=33 / 1000, - throat_radius=11 / 1000, - nozzle_position=0, - interpolation_method="linear", - coordinate_system_orientation="nozzle_to_combustion_chamber", - ) + example_plain_env.speed_of_sound = Function(1e16) # create a rocket with zero drag and huge mass to keep the rocket's speed constant dummy_rocket = Rocket( @@ -569,7 +552,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): center_of_mass_without_motor=0, ) dummy_rocket.set_rail_buttons(0.082, -0.618) - dummy_rocket.add_motor(dummy_motor, position=-1.373) + dummy_rocket.add_motor(dummy_empty_motor, position=-1.373) setup_rocket_with_given_static_margin(dummy_rocket, static_margin) @@ -582,13 +565,12 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): test_flight = Flight( rocket=dummy_rocket, rail_length=1, - environment=env, + environment=example_plain_env, initial_solution=initial_solution, max_time=max_time, max_time_step=1e-2, verbose=False, ) - test_flight.post_process(interpolation="linear") # Check stability according to static margin if wind_u == 0: @@ -598,8 +580,9 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): moments = test_flight.M2.get_source()[:, 1] wind_sign = -np.sign(wind_u) - assert ( - (static_margin > 0 and np.max(moments) * np.min(moments) < 0) - or (static_margin < 0 and np.all(moments / wind_sign <= 0)) - or (static_margin == 0 and np.all(np.abs(moments) <= 1e-10)) - ) + if static_margin > 0: + assert np.max(moments) * np.min(moments) < 0 + elif static_margin < 0: + assert np.all(moments / wind_sign <= 0) + else: # static_margin == 0 + assert np.all(np.abs(moments) <= 1e-10) diff --git a/tests/unit/test_flight_time_nodes.py b/tests/unit/test_flight_time_nodes.py index 10f6b6c30..1e2661210 100644 --- a/tests/unit/test_flight_time_nodes.py +++ b/tests/unit/test_flight_time_nodes.py @@ -2,9 +2,7 @@ TimeNode. """ -import pytest - -from rocketpy.rocket import Parachute, _Controller +# from rocketpy.rocket import Parachute, _Controller def test_time_nodes_init(flight_calisto): diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index 3c1934f9f..9efb64c0c 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -359,89 +359,78 @@ def test_setters(func_from_csv, func_2d_from_csv): assert func_2d_from_csv.get_extrapolation_method() == "natural" -def test_interpolation_methods(linear_func): - """Tests some of the interpolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test Akima - assert isinstance(linear_func.set_interpolation("akima"), Function) - linear_func.set_interpolation("akima") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "akima" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - # Test polynomial - - assert isinstance(linear_func.set_interpolation("polynomial"), Function) - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "polynomial" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - -def test_extrapolation_methods(linear_func): - """Test some of the extrapolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test zero - linear_func.set_extrapolation("zero") - assert linear_func.get_extrapolation_method() == "zero" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test constant - assert isinstance(linear_func.set_extrapolation("constant"), Function) - linear_func.set_extrapolation("constant") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "constant" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test natural for linear interpolation - linear_func.set_interpolation("linear") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for spline interpolation - linear_func.set_interpolation("spline") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for akima interpolation - linear_func.set_interpolation("akima") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for polynomial interpolation - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) +class TestInterpolationMethods: + """Tests some of the interpolation methods of the Function class.""" + + def test_akima_interpolation(self, linear_func): + """Tests Akima interpolation method""" + assert isinstance(linear_func.set_interpolation("akima"), Function) + linear_func.set_interpolation("akima") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "akima" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + def test_polynomial_interpolation(self, linear_func): + """Tests polynomial interpolation method""" + assert isinstance(linear_func.set_interpolation("polynomial"), Function) + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "polynomial" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + +class TestExtrapolationMethods: + """Test some of the extrapolation methods of the Function class.""" + + def test_zero_extrapolation(self, linear_func): + linear_func.set_extrapolation("zero") + assert linear_func.get_extrapolation_method() == "zero" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_constant_extrapolation(self, linear_func): + assert isinstance(linear_func.set_extrapolation("constant"), Function) + linear_func.set_extrapolation("constant") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "constant" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_natural_extrapolation_linear(self, linear_func): + linear_func.set_interpolation("linear") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_spline(self, linear_func): + linear_func.set_interpolation("spline") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_akima(self, linear_func): + linear_func.set_interpolation("akima") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_polynomial(self, linear_func): + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) @pytest.mark.parametrize("a", [-1, 0, 1]) @pytest.mark.parametrize("b", [-1, 0, 1]) -def test_multivariable_dataset(a, b): - """Test the Function class with a multivariable dataset.""" +def test_multivariate_dataset(a, b): + """Test the Function class with a multivariate dataset.""" # Test plane f(x,y) = x + y source = [ (-1, -1, -2), @@ -515,8 +504,8 @@ def test_3d_shepard_interpolation(x, y, z, w_expected): @pytest.mark.parametrize("a", [-1, -0.5, 0, 0.5, 1]) @pytest.mark.parametrize("b", [-1, -0.5, 0, 0.5, 1]) -def test_multivariable_function(a, b): - """Test the Function class with a multivariable function.""" +def test_multivariate_function(a, b): + """Test the Function class with a multivariate function.""" def source(x, y): return np.sin(x + y) diff --git a/tests/unit/test_genericmotor.py b/tests/unit/test_genericmotor.py index 98bc5664f..c6321ae4d 100644 --- a/tests/unit/test_genericmotor.py +++ b/tests/unit/test_genericmotor.py @@ -2,16 +2,21 @@ import pytest import scipy.integrate -burn_time = (2, 7) -thrust_source = lambda t: 2000 - 100 * (t - 2) -chamber_height = 0.5 -chamber_radius = 0.075 -chamber_position = -0.25 -propellant_initial_mass = 5.0 -nozzle_position = -0.5 -nozzle_radius = 0.075 -dry_mass = 8.0 -dry_inertia = (0.2, 0.2, 0.08) +BURN_TIME = (2, 7) + + +def thrust_source(t): + return 2000 - 100 * (t - 2) + + +CHAMBER_HEIGHT = 0.5 +CHAMBER_RADIUS = 0.075 +CHAMBER_POSITION = -0.25 +PROPELLANT_INITIAL_MASS = 5.0 +NOZZLE_POSITION = -0.5 +NOZZLE_RADIUS = 0.075 +DRY_MASS = 8.0 +DRY_INERTIA = (0.2, 0.2, 0.08) def test_generic_motor_basic_parameters(generic_motor): @@ -22,19 +27,19 @@ def test_generic_motor_basic_parameters(generic_motor): generic_motor : rocketpy.GenericMotor The GenericMotor object to be used in the tests. """ - assert generic_motor.burn_time == burn_time - assert generic_motor.dry_mass == dry_mass + assert generic_motor.burn_time == BURN_TIME + assert generic_motor.dry_mass == DRY_MASS assert ( generic_motor.dry_I_11, generic_motor.dry_I_22, generic_motor.dry_I_33, - ) == dry_inertia - assert generic_motor.nozzle_position == nozzle_position - assert generic_motor.nozzle_radius == nozzle_radius - assert generic_motor.chamber_position == chamber_position - assert generic_motor.chamber_radius == chamber_radius - assert generic_motor.chamber_height == chamber_height - assert generic_motor.propellant_initial_mass == propellant_initial_mass + ) == DRY_INERTIA + assert generic_motor.nozzle_position == NOZZLE_POSITION + assert generic_motor.nozzle_radius == NOZZLE_RADIUS + assert generic_motor.chamber_position == CHAMBER_POSITION + assert generic_motor.chamber_radius == CHAMBER_RADIUS + assert generic_motor.chamber_height == CHAMBER_HEIGHT + assert generic_motor.propellant_initial_mass == PROPELLANT_INITIAL_MASS def test_generic_motor_thrust_parameters(generic_motor): @@ -46,20 +51,20 @@ def test_generic_motor_thrust_parameters(generic_motor): The GenericMotor object to be used in the tests. """ expected_thrust = np.array( - [(t, thrust_source(t)) for t in np.linspace(*burn_time, 50)] + [(t, thrust_source(t)) for t in np.linspace(*BURN_TIME, 50)] ) expected_total_impulse = scipy.integrate.trapezoid( expected_thrust[:, 1], expected_thrust[:, 0] ) - expected_exhaust_velocity = expected_total_impulse / propellant_initial_mass + expected_exhaust_velocity = expected_total_impulse / PROPELLANT_INITIAL_MASS expected_mass_flow_rate = -1 * expected_thrust[:, 1] / expected_exhaust_velocity # Discretize mass flow rate for testing purposes - mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*burn_time, 50) + mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*BURN_TIME, 50) assert generic_motor.thrust.y_array == pytest.approx(expected_thrust[:, 1]) assert generic_motor.total_impulse == pytest.approx(expected_total_impulse) - assert generic_motor.exhaust_velocity.average(*burn_time) == pytest.approx( + assert generic_motor.exhaust_velocity.average(*BURN_TIME) == pytest.approx( expected_exhaust_velocity ) assert mass_flow_rate.y_array == pytest.approx(expected_mass_flow_rate) @@ -78,8 +83,8 @@ def test_generic_motor_center_of_mass(generic_motor): center_of_mass = -0.25 # Discretize center of mass for testing purposes - generic_motor.center_of_propellant_mass.set_discrete(*burn_time, 50) - generic_motor.center_of_mass.set_discrete(*burn_time, 50) + generic_motor.center_of_propellant_mass.set_discrete(*BURN_TIME, 50) + generic_motor.center_of_mass.set_discrete(*BURN_TIME, 50) assert generic_motor.center_of_propellant_mass.y_array == pytest.approx( center_of_propellant_mass @@ -99,24 +104,24 @@ def test_generic_motor_inertia(generic_motor): The GenericMotor object to be used in the tests. """ # Tests the inertia formulation from the propellant mass - propellant_mass = generic_motor.propellant_mass.set_discrete(*burn_time, 50).y_array + propellant_mass = generic_motor.propellant_mass.set_discrete(*BURN_TIME, 50).y_array - propellant_I_11 = propellant_mass * (chamber_radius**2 / 4 + chamber_height**2 / 12) + propellant_I_11 = propellant_mass * (CHAMBER_RADIUS**2 / 4 + CHAMBER_HEIGHT**2 / 12) propellant_I_22 = propellant_I_11 - propellant_I_33 = propellant_mass * (chamber_radius**2 / 2) + propellant_I_33 = propellant_mass * (CHAMBER_RADIUS**2 / 2) # Centers of mass coincide, so no translation is needed - I_11 = propellant_I_11 + dry_inertia[0] - I_22 = propellant_I_22 + dry_inertia[1] - I_33 = propellant_I_33 + dry_inertia[2] + I_11 = propellant_I_11 + DRY_INERTIA[0] + I_22 = propellant_I_22 + DRY_INERTIA[1] + I_33 = propellant_I_33 + DRY_INERTIA[2] # Discretize inertia for testing purposes - generic_motor.propellant_I_11.set_discrete(*burn_time, 50) - generic_motor.propellant_I_22.set_discrete(*burn_time, 50) - generic_motor.propellant_I_33.set_discrete(*burn_time, 50) - generic_motor.I_11.set_discrete(*burn_time, 50) - generic_motor.I_22.set_discrete(*burn_time, 50) - generic_motor.I_33.set_discrete(*burn_time, 50) + generic_motor.propellant_I_11.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_22.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_33.set_discrete(*BURN_TIME, 50) + generic_motor.I_11.set_discrete(*BURN_TIME, 50) + generic_motor.I_22.set_discrete(*BURN_TIME, 50) + generic_motor.I_33.set_discrete(*BURN_TIME, 50) assert generic_motor.propellant_I_11.y_array == pytest.approx(propellant_I_11) assert generic_motor.propellant_I_22.y_array == pytest.approx(propellant_I_22) diff --git a/tests/unit/test_hybridmotor.py b/tests/unit/test_hybridmotor.py index acf4b3e54..ef03a1998 100644 --- a/tests/unit/test_hybridmotor.py +++ b/tests/unit/test_hybridmotor.py @@ -1,26 +1,28 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -thrust_function = lambda t: 2000 - 100 * t -burn_time = 10 -center_of_dry_mass = 0 -dry_inertia = (4, 4, 0.1) -dry_mass = 8 -grain_density = 1700 -grain_number = 4 -grain_initial_height = 0.1 -grain_separation = 0 -grain_initial_inner_radius = 0.04 -grain_outer_radius = 0.1 -nozzle_position = -0.4 -nozzle_radius = 0.07 -grains_center_of_mass_position = -0.1 -oxidizer_tank_position = 0.3 + +def thrust_function(t): + return 2000 - 100 * t + + +BURN_TIME = 10 +CENTER_OF_DRY_MASS = 0 +DRY_INERTIA = (4, 4, 0.1) +DRY_MASS = 8 +GRAIN_DENSITY = 1700 +GRAIN_NUMBER = 4 +GRAIN_INITIAL_HEIGHT = 0.1 +GRAIN_SEPARATION = 0 +GRAIN_INITIAL_INNER_RADIUS = 0.04 +GRAIN_OUTER_RADIUS = 0.1 +NOZZLE_POSITION = -0.4 +NOZZLE_RADIUS = 0.07 +GRAINS_CENTER_OF_MASS_POSITION = -0.1 +OXIDIZER_TANK_POSITION = 0.3 def test_hybrid_motor_basic_parameters(hybrid_motor): @@ -31,25 +33,25 @@ def test_hybrid_motor_basic_parameters(hybrid_motor): hybrid_motor : rocketpy.HybridMotor The HybridMotor object to be used in the tests. """ - assert hybrid_motor.burn_time == (0, burn_time) - assert hybrid_motor.dry_mass == dry_mass + assert hybrid_motor.burn_time == (0, BURN_TIME) + assert hybrid_motor.dry_mass == DRY_MASS assert ( hybrid_motor.dry_I_11, hybrid_motor.dry_I_22, hybrid_motor.dry_I_33, - ) == dry_inertia - assert hybrid_motor.center_of_dry_mass_position == center_of_dry_mass - assert hybrid_motor.nozzle_position == nozzle_position - assert hybrid_motor.nozzle_radius == nozzle_radius - assert hybrid_motor.solid.grain_number == grain_number - assert hybrid_motor.solid.grain_density == grain_density - assert hybrid_motor.solid.grain_initial_height == grain_initial_height - assert hybrid_motor.solid.grain_separation == grain_separation - assert hybrid_motor.solid.grain_initial_inner_radius == grain_initial_inner_radius - assert hybrid_motor.solid.grain_outer_radius == grain_outer_radius + ) == DRY_INERTIA + assert hybrid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert hybrid_motor.nozzle_position == NOZZLE_POSITION + assert hybrid_motor.nozzle_radius == NOZZLE_RADIUS + assert hybrid_motor.solid.grain_number == GRAIN_NUMBER + assert hybrid_motor.solid.grain_density == GRAIN_DENSITY + assert hybrid_motor.solid.grain_initial_height == GRAIN_INITIAL_HEIGHT + assert hybrid_motor.solid.grain_separation == GRAIN_SEPARATION + assert hybrid_motor.solid.grain_initial_inner_radius == GRAIN_INITIAL_INNER_RADIUS + assert hybrid_motor.solid.grain_outer_radius == GRAIN_OUTER_RADIUS assert ( hybrid_motor.solid.grains_center_of_mass_position - == grains_center_of_mass_position + == GRAINS_CENTER_OF_MASS_POSITION ) assert hybrid_motor.liquid.positioned_tanks[0]["position"] == 0.3 @@ -69,11 +71,11 @@ def test_hybrid_motor_thrust_parameters(hybrid_motor, spherical_oxidizer_tank): expected_total_impulse = scipy.integrate.quad(expected_thrust, 0, 10)[0] initial_grain_mass = ( - grain_density + GRAIN_DENSITY * np.pi - * (grain_outer_radius**2 - grain_initial_inner_radius**2) - * grain_initial_height - * grain_number + * (GRAIN_OUTER_RADIUS**2 - GRAIN_INITIAL_INNER_RADIUS**2) + * GRAIN_INITIAL_HEIGHT + * GRAIN_NUMBER ) initial_oxidizer_mass = spherical_oxidizer_tank.fluid_mass(0) initial_mass = initial_grain_mass + initial_oxidizer_mass @@ -111,13 +113,13 @@ def test_hybrid_motor_center_of_mass(hybrid_motor, spherical_oxidizer_tank): oxidizer_mass = spherical_oxidizer_tank.fluid_mass grain_mass = hybrid_motor.solid.propellant_mass - propellant_balance = grain_mass * grains_center_of_mass_position + oxidizer_mass * ( - oxidizer_tank_position + spherical_oxidizer_tank.center_of_mass + propellant_balance = grain_mass * GRAINS_CENTER_OF_MASS_POSITION + oxidizer_mass * ( + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / (grain_mass + oxidizer_mass) - center_of_mass = balance / (grain_mass + oxidizer_mass + dry_mass) + center_of_mass = balance / (grain_mass + oxidizer_mass + DRY_MASS) for t in np.linspace(0, 100, 100): assert pytest.approx( @@ -145,12 +147,12 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): # Validate parallel axis theorem translation grain_inertia += ( grain_mass - * (grains_center_of_mass_position - hybrid_motor.center_of_propellant_mass) ** 2 + * (GRAINS_CENTER_OF_MASS_POSITION - hybrid_motor.center_of_propellant_mass) ** 2 ) oxidizer_inertia += ( oxidizer_mass * ( - oxidizer_tank_position + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass - hybrid_motor.center_of_propellant_mass ) @@ -164,8 +166,8 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): propellant_inertia + propellant_mass * (hybrid_motor.center_of_propellant_mass - hybrid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-hybrid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-hybrid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) for t in np.linspace(0, 100, 100): diff --git a/tests/unit/test_liquidmotor.py b/tests/unit/test_liquidmotor.py index ed4fe0ab3..6208a7dc0 100644 --- a/tests/unit/test_liquidmotor.py +++ b/tests/unit/test_liquidmotor.py @@ -1,20 +1,18 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -burn_time = (8, 20) -dry_mass = 10 -dry_inertia = (5, 5, 0.2) -center_of_dry_mass = 0 -nozzle_position = -1.364 -nozzle_radius = 0.069 / 2 -pressurant_tank_position = 2.007 -fuel_tank_position = -1.048 -oxidizer_tank_position = 0.711 +BURN_TIME = (8, 20) +DRY_MASS = 10 +DRY_INERTIA = (5, 5, 0.2) +CENTER_OF_DRY_MASS = 0 +NOZZLE_POSITION = -1.364 +NOZZLE_RADIUS = 0.069 / 2 +PRESSURANT_TANK_POSITION = 2.007 +FUEL_TANK_POSITION = -1.048 +OXIDIZER_TANK_POSITION = 0.711 def test_liquid_motor_basic_parameters(liquid_motor): @@ -25,19 +23,19 @@ def test_liquid_motor_basic_parameters(liquid_motor): liquid_motor : rocketpy.LiquidMotor The LiquidMotor object to be used in the tests. """ - assert liquid_motor.burn_time == burn_time - assert liquid_motor.dry_mass == dry_mass + assert liquid_motor.burn_time == BURN_TIME + assert liquid_motor.dry_mass == DRY_MASS assert ( liquid_motor.dry_I_11, liquid_motor.dry_I_22, liquid_motor.dry_I_33, - ) == dry_inertia - assert liquid_motor.center_of_dry_mass_position == center_of_dry_mass - assert liquid_motor.nozzle_position == nozzle_position - assert liquid_motor.nozzle_radius == nozzle_radius - assert liquid_motor.positioned_tanks[0]["position"] == pressurant_tank_position - assert liquid_motor.positioned_tanks[1]["position"] == fuel_tank_position - assert liquid_motor.positioned_tanks[2]["position"] == oxidizer_tank_position + ) == DRY_INERTIA + assert liquid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert liquid_motor.nozzle_position == NOZZLE_POSITION + assert liquid_motor.nozzle_radius == NOZZLE_RADIUS + assert liquid_motor.positioned_tanks[0]["position"] == PRESSURANT_TANK_POSITION + assert liquid_motor.positioned_tanks[1]["position"] == FUEL_TANK_POSITION + assert liquid_motor.positioned_tanks[2]["position"] == OXIDIZER_TANK_POSITION def test_liquid_motor_thrust_parameters( @@ -125,12 +123,12 @@ def test_liquid_motor_mass_volume( ) # Perform default discretization - expected_pressurant_mass.set_discrete(*burn_time, 100) - expected_fuel_mass.set_discrete(*burn_time, 100) - expected_oxidizer_mass.set_discrete(*burn_time, 100) - expected_pressurant_volume.set_discrete(*burn_time, 100) - expected_fuel_volume.set_discrete(*burn_time, 100) - expected_oxidizer_volume.set_discrete(*burn_time, 100) + expected_pressurant_mass.set_discrete(*BURN_TIME, 100) + expected_fuel_mass.set_discrete(*BURN_TIME, 100) + expected_oxidizer_mass.set_discrete(*BURN_TIME, 100) + expected_pressurant_volume.set_discrete(*BURN_TIME, 100) + expected_fuel_volume.set_discrete(*BURN_TIME, 100) + expected_oxidizer_volume.set_discrete(*BURN_TIME, 100) assert ( pytest.approx(expected_pressurant_mass.y_array, 0.01) @@ -180,14 +178,14 @@ def test_liquid_motor_center_of_mass( propellant_mass = pressurant_mass + fuel_mass + oxidizer_mass propellant_balance = ( - pressurant_mass * (pressurant_tank.center_of_mass + pressurant_tank_position) - + fuel_mass * (fuel_tank.center_of_mass + fuel_tank_position) - + oxidizer_mass * (oxidizer_tank.center_of_mass + oxidizer_tank_position) + pressurant_mass * (pressurant_tank.center_of_mass + PRESSURANT_TANK_POSITION) + + fuel_mass * (fuel_tank.center_of_mass + FUEL_TANK_POSITION) + + oxidizer_mass * (oxidizer_tank.center_of_mass + OXIDIZER_TANK_POSITION) ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / propellant_mass - center_of_mass = balance / (propellant_mass + dry_mass) + center_of_mass = balance / (propellant_mass + DRY_MASS) assert ( pytest.approx(liquid_motor.center_of_propellant_mass.y_array) @@ -223,7 +221,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( pressurant_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + pressurant_tank_position + + PRESSURANT_TANK_POSITION ) ** 2 ) @@ -232,7 +230,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( fuel_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + fuel_tank_position + + FUEL_TANK_POSITION ) ** 2 ) @@ -241,7 +239,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( oxidizer_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + oxidizer_tank_position + + OXIDIZER_TANK_POSITION ) ** 2 ) @@ -253,8 +251,8 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer propellant_inertia + propellant_mass * (liquid_motor.center_of_propellant_mass - liquid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-liquid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-liquid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) assert ( diff --git a/tests/unit/test_monte_carlo.py b/tests/unit/test_monte_carlo.py index 7af6a5db5..0e1ad22cc 100644 --- a/tests/unit/test_monte_carlo.py +++ b/tests/unit/test_monte_carlo.py @@ -1,8 +1,5 @@ -from unittest.mock import patch - import matplotlib as plt import numpy as np -import pytest plt.rcParams.update({"figure.max_open_warning": 0}) diff --git a/tests/unit/test_plots.py b/tests/unit/test_plots.py index db36264d8..cd35f8d11 100644 --- a/tests/unit/test_plots.py +++ b/tests/unit/test_plots.py @@ -1,14 +1,12 @@ -import os from unittest.mock import patch import matplotlib.pyplot as plt -from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import Compare @patch("matplotlib.pyplot.show") -def test_compare(mock_show, flight_calisto): +def test_compare(mock_show, flight_calisto): # pylint: disable=unused-argument """Here we want to test the 'x_attributes' argument, which is the only one that is not tested in the other tests. diff --git a/tests/unit/test_rocket.py b/tests/unit/test_rocket.py index 876f5024d..a984466ee 100644 --- a/tests/unit/test_rocket.py +++ b/tests/unit/test_rocket.py @@ -10,7 +10,7 @@ @patch("matplotlib.pyplot.show") def test_elliptical_fins( mock_show, calisto_robust, calisto_trapezoidal_fins -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument test_rocket = calisto_robust calisto_robust.aerodynamic_surfaces.remove(calisto_trapezoidal_fins) test_rocket.add_elliptical_fins(4, span=0.100, root_chord=0.120, position=-1.168) @@ -449,9 +449,9 @@ def test_evaluate_com_to_cdm_function(calisto): def test_get_inertia_tensor_at_time(calisto): # Expected values (for t = 0) # TODO: compute these values by hand or using CAD. - Ixx = 10.31379 - Iyy = 10.31379 - Izz = 0.039942 + I_11 = 10.31379 + I_22 = 10.31379 + I_33 = 0.039942 # Set tolerance threshold atol = 1e-5 @@ -460,9 +460,9 @@ def test_get_inertia_tensor_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_at_time(0) # Check if the values are close to the expected ones - assert pytest.approx(Ixx, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -475,9 +475,9 @@ def test_get_inertia_tensor_at_time(calisto): def test_get_inertia_tensor_derivative_at_time(calisto): # Expected values (for t = 2s) # TODO: compute these values by hand or using CAD. - Ixx_dot = -0.634805230901143 - Iyy_dot = -0.634805230901143 - Izz_dot = -0.000671493662305 + I_11_dot = -0.634805230901143 + I_22_dot = -0.634805230901143 + I_33_dot = -0.000671493662305 # Set tolerance threshold atol = 1e-3 @@ -486,9 +486,9 @@ def test_get_inertia_tensor_derivative_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_derivative_at_time(2) # Check if the values are close to the expected ones - assert pytest.approx(Ixx_dot, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy_dot, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz_dot, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11_dot, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22_dot, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33_dot, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -514,77 +514,72 @@ def test_add_cm_eccentricity(calisto): assert calisto.thrust_eccentricity_y == 0.1 -def test_add_surfaces_different_noses(calisto): +class TestAddSurfaces: """Test the add_surfaces method with different nose cone configurations. More specifically, this will check the static margin of the rocket with - different nose cone configurations. - - Parameters - ---------- - calisto : Rocket - Pytest fixture for the calisto rocket. - """ - length = 0.55829 - kind = "vonkarman" - position = 1.16 - bluffness = 0 - base_radius = 0.0635 - rocket_radius = 0.0635 - - # Case 1: base_radius == rocket_radius - nose1 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 1", - ) - calisto.add_surfaces(nose1, position) - assert nose1.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 2: base_radius == rocket_radius / 2 - calisto.aerodynamic_surfaces.remove(nose1) - nose2 = NoseCone( - length, - kind, - base_radius=base_radius / 2, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 2", - ) - calisto.add_surfaces(nose2, position) - assert nose2.radius_ratio == pytest.approx(0.5, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 3: base_radius is None - calisto.aerodynamic_surfaces.remove(nose2) - nose3 = NoseCone( - length, - kind, - base_radius=None, - bluffness=bluffness, - rocket_radius=rocket_radius * 2, - name="Nose Cone 3", - ) - calisto.add_surfaces(nose3, position) - assert nose3.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 4: rocket_radius is None - calisto.aerodynamic_surfaces.remove(nose3) - nose4 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=None, - name="Nose Cone 4", - ) - calisto.add_surfaces(nose4, position) - assert nose4.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + different nose cone configurations.""" + + @pytest.fixture(autouse=True) + def setup(self, calisto): + self.calisto = calisto + self.length = 0.55829 + self.kind = "vonkarman" + self.position = 1.16 + self.bluffness = 0 + self.base_radius = 0.0635 + self.rocket_radius = 0.0635 + + def test_add_surfaces_base_equals_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 1", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_half_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius / 2, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 2", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(0.5, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=None, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius * 2, + name="Nose Cone 3", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_rocket_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=None, + name="Nose Cone 4", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) def test_coordinate_system_orientation( diff --git a/tests/unit/test_solidmotor.py b/tests/unit/test_solidmotor.py index 6c5d4d4b1..064c8210e 100644 --- a/tests/unit/test_solidmotor.py +++ b/tests/unit/test_solidmotor.py @@ -20,7 +20,7 @@ @patch("matplotlib.pyplot.show") -def test_motor(mock_show, cesaroni_m1670): +def test_motor(mock_show, cesaroni_m1670): # pylint: disable=unused-argument """Tests the SolidMotor.all_info() method. Parameters diff --git a/tests/unit/test_tank.py b/tests/unit/test_tank.py index 13c7b6cb8..3a77a8bca 100644 --- a/tests/unit/test_tank.py +++ b/tests/unit/test_tank.py @@ -1,3 +1,5 @@ +# TODO: This file must be refactored to improve readability and maintainability. +# pylint: disable=too-many-statements import os from math import isclose @@ -202,6 +204,7 @@ def bottom_endcap(y): ) # Assert volume bounds + # pylint: disable=comparison-with-callable assert (real_tank_lox.gas_height <= real_tank_lox.geometry.top).all assert (real_tank_lox.fluid_volume <= real_tank_lox.geometry.total_volume).all assert (example_tank_lox.gas_height <= example_tank_lox.geometry.top).all @@ -231,17 +234,22 @@ def test(calculated, expected, t, real=False): def test_mass(): """Test mass function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: initial_liquid_mass - + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) - + initial_gas_mass - + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) - ) + + def example_expected(t): + return ( + initial_liquid_mass + + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) + + initial_gas_mass + + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) + ) + example_calculated = example_tank_lox.fluid_mass lox_vals = Function(lox_masses).y_array - real_expected = lambda t: lox_vals[t] + def real_expected(t): + return lox_vals[t] + real_calculated = real_tank_lox.fluid_mass test(example_calculated, example_expected, 5) @@ -249,19 +257,24 @@ def test_mass(): def test_net_mfr(): """Test net_mass_flow_rate function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: liquid_mass_flow_rate_in - - liquid_mass_flow_rate_out - + gas_mass_flow_rate_in - - gas_mass_flow_rate_out - ) + + def example_expected(_): + return ( + liquid_mass_flow_rate_in + - liquid_mass_flow_rate_out + + gas_mass_flow_rate_in + - gas_mass_flow_rate_out + ) + example_calculated = example_tank_lox.net_mass_flow_rate liquid_mfrs = Function(example_liquid_masses).y_array gas_mfrs = Function(example_gas_masses).y_array - real_expected = lambda t: (liquid_mfrs[t] + gas_mfrs[t]) / t + def real_expected(t): + return (liquid_mfrs[t] + gas_mfrs[t]) / t + real_calculated = real_tank_lox.net_mass_flow_rate test(example_calculated, example_expected, 10) @@ -280,8 +293,12 @@ def test_level_based_tank(): test_dir = "./data/berkeley/" - top_endcap = lambda y: np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) - bottom_endcap = lambda y: np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + def top_endcap(y): + return np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) + + def bottom_endcap(y): + return np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + tank_geometry = TankGeometry( { (0, 0.0559): bottom_endcap, @@ -291,7 +308,7 @@ def test_level_based_tank(): ) ullage_data = Function(os.path.abspath(test_dir + "loxUllage.csv")).get_source() - levelTank = LevelBasedTank( + level_tank = LevelBasedTank( name="LevelTank", geometry=tank_geometry, flux_time=(0, 10), @@ -318,18 +335,18 @@ def align_time_series(small_source, large_source): for val in small_source: time = val[0] delta_time_vector = abs(time - large_source[:, 0]) - largeIndex = np.argmin(delta_time_vector) - delta_time = abs(time - large_source[largeIndex][0]) + large_index = np.argmin(delta_time_vector) + delta_time = abs(time - large_source[large_index][0]) if delta_time < tolerance: - result_larger_source[curr_ind] = large_source[largeIndex] + result_larger_source[curr_ind] = large_source[large_index] result_smaller_source[curr_ind] = val curr_ind += 1 return result_larger_source, result_smaller_source - assert np.allclose(levelTank.liquid_height, ullage_data) + assert np.allclose(level_tank.liquid_height, ullage_data) - calculated_mass = levelTank.liquid_mass.set_discrete( + calculated_mass = level_tank.liquid_mass.set_discrete( mass_data[0][0], mass_data[0][-1], len(mass_data[0]) ) calculated_mass, mass_data = align_time_series( @@ -337,7 +354,7 @@ def align_time_series(small_source, large_source): ) assert np.allclose(calculated_mass, mass_data, rtol=1, atol=2) - calculated_mfr = levelTank.net_mass_flow_rate.set_discrete( + calculated_mfr = level_tank.net_mass_flow_rate.set_discrete( mass_flow_rate_data[0][0], mass_flow_rate_data[0][-1], len(mass_flow_rate_data[0]), @@ -358,91 +375,133 @@ def test(t, a, tol=1e-4): assert isclose(t.get_value(i), a(i), abs_tol=tol) def test_nmfr(): - nmfr = ( - lambda x: liquid_mass_flow_rate_in - + gas_mass_flow_rate_in - - liquid_mass_flow_rate_out - - gas_mass_flow_rate_out - ) + def nmfr(_): + return ( + liquid_mass_flow_rate_in + + gas_mass_flow_rate_in + - liquid_mass_flow_rate_out + - gas_mass_flow_rate_out + ) + test(t.net_mass_flow_rate, nmfr) def test_mass(): - m = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) + (initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x) + def m(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + lm = t.fluid_mass test(lm, m) def test_liquid_height(): - alv = ( - lambda x: ( + def alv(x): + return ( initial_liquid_mass + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) - / lox.density - ) - alh = lambda x: alv(x) / (np.pi) + ) / lox.density + + def alh(x): + return alv(x) / (np.pi) + tlh = t.liquid_height test(tlh, alh) def test_com(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) tcom = t.center_of_mass test(tcom, acom) def test_inertia(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) r = 1 - ixy_gas = ( - lambda x: 1 / 4 * gas_mass(x) * r**2 - + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 - + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 - ) - ixy_liq = ( - lambda x: 1 / 4 * liquid_mass(x) * r**2 - + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 - + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 - ) - ixy = lambda x: ixy_gas(x) + ixy_liq(x) + + def ixy_gas(x): + return ( + 1 / 4 * gas_mass(x) * r**2 + + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 + + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 + ) + + def ixy_liq(x): + return ( + 1 / 4 * liquid_mass(x) * r**2 + + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 + + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 + ) + + def ixy(x): + return ixy_gas(x) + ixy_liq(x) + test(t.gas_inertia, ixy_gas, tol=1e-3) test(t.liquid_inertia, ixy_liq, tol=1e-3) test(t.inertia, ixy, tol=1e-3) diff --git a/tests/unit/test_tools_matrix.py b/tests/unit/test_tools_matrix.py index a6edb5278..f2b476fdc 100644 --- a/tests/unit/test_tools_matrix.py +++ b/tests/unit/test_tools_matrix.py @@ -97,7 +97,7 @@ def test_matrix_inverse(components): matrix = Matrix(components) if matrix.det == 0: with pytest.raises(ZeroDivisionError): - matrix.inverse + assert matrix.inverse else: assert matrix.inverse == np.linalg.inv(matrix) @@ -115,64 +115,64 @@ def test_matrix_neg(components): @pytest.mark.parametrize("A_c", test_matrices) @pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_add(A_c, B_c): - expected_result = np.array(A_c) + np.array(B_c) - assert Matrix(A_c) + Matrix(B_c) == expected_result +def test_matrix_add(A, B): + expected_result = np.array(A) + np.array(B) + assert Matrix(A) + Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_sub(A_c, B_c): - expected_result = np.array(A_c) - np.array(B_c) - assert Matrix(A_c) - Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_sub(A, B): + expected_result = np.array(A) - np.array(B) + assert Matrix(A) - Matrix(B) == expected_result @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_mul(A_c, k): - A = Matrix(A_c) - assert A * k == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_mul(A, k): + A = Matrix(A) + assert A * k == k * np.array(A) @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_rmul(A_c, k): - A = Matrix(A_c) - assert k * A == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_rmul(A, k): + np_array = np.array(A) + A = Matrix(A) + assert k * A == k * np_array -@pytest.mark.parametrize("A_c", test_matrices) +@pytest.mark.parametrize("A", test_matrices) @pytest.mark.parametrize("k", [-1, 1, np.pi, np.e]) -def test_matrix_truediv(A_c, k): - A = Matrix(A_c) +def test_matrix_truediv(A, k): + A = Matrix(A) assert A / k == np.array(A) / k -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_matmul_matrices(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_matmul_matrices(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) -def test_matrix_matmul_vectors(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Vector(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) +def test_matrix_matmul_vectors(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Vector(B) == expected_result @pytest.mark.parametrize("k", [0, 1, 2, 3, 4, 5]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_pow(A_c, k): - A = Matrix(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_pow(A, k): + A = Matrix(A) assert A**k == np.linalg.matrix_power(A, k) @pytest.mark.parametrize("matrix_components", test_matrices) def test_matrix_eq(matrix_components): matrix = Matrix(matrix_components) - assert matrix == matrix assert matrix == matrix_components assert (matrix == 2 * matrix) is False @@ -191,10 +191,10 @@ def test_matrix_element_wise(matrix_components, operation): ) -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_dot(A_c, B_c): - A, B = Matrix(A_c), Matrix(B_c) +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_dot(A, B): + A, B = Matrix(A), Matrix(B) assert A.dot(B) == np.dot(A, B) diff --git a/tests/unit/test_tools_vector.py b/tests/unit/test_tools_vector.py index c9b617c97..f9ded7161 100644 --- a/tests/unit/test_tools_vector.py +++ b/tests/unit/test_tools_vector.py @@ -69,7 +69,7 @@ def test_vector_cross_matrix(vector_components): def test_vector_abs(vector_components): vector = Vector(vector_components) vector_magnitude = abs(vector) - assert vector_magnitude == sum([i**2 for i in vector_components]) ** 0.5 + assert vector_magnitude == sum(i**2 for i in vector_components) ** 0.5 @pytest.mark.parametrize("vector_components", test_vectors) @@ -199,12 +199,14 @@ def test_vector_proj(u_c, v_c): @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_str(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval("Vector(" + str(vector) + ")") == vector @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_repr(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval(repr(vector).replace("(", "((").replace(")", "))")) == vector diff --git a/tests/unit/test_utilities.py b/tests/unit/test_utilities.py index 25bae57cf..a6d1972a7 100644 --- a/tests/unit/test_utilities.py +++ b/tests/unit/test_utilities.py @@ -1,4 +1,3 @@ -import csv from unittest.mock import patch import numpy as np @@ -21,7 +20,7 @@ (40, 21, 1.04, 0.2475236), ], ) -def test_compute_CdS_from_drop_test( +def test_compute_cd_s_from_drop_test( terminal_velocity, rocket_mass, air_density, result ): """Test if the function `compute_cd_s_from_drop_test` returns the correct @@ -45,42 +44,6 @@ def test_compute_CdS_from_drop_test( assert abs(cds - result) < 1e-6 -@pytest.mark.skip(reason="legacy tests") # it is not wokring -def test_create_dispersion_dictionary(): - """Test if the function returns a dictionary with the correct keys. - It reads the keys from the dictionary generated by the utilities function - and compares them to the expected. - Be careful if you change the "fixtures/monte_carlo/Valetudo_inputs.csv" file. - """ - - returned_dict = utilities.create_dispersion_dictionary( - "tests/fixtures/monte_carlo/Valetudo_inputs.csv" - ) - - test_dict = {} - with open("tests/fixtures/monte_carlo/Valetudo_inputs.csv", mode='r') as csvfile: - reader = csv.reader(csvfile, delimiter=';') - next(reader) # Skip header - for row in reader: - key, value, std_dev = row[1].strip(), row[2].strip(), row[3].strip() - if key: - if std_dev: - try: - test_dict[key] = (float(value), float(std_dev)) - except ValueError: - test_dict[key] = (value, std_dev) - else: - try: - test_dict[key] = float(value) - except ValueError: - try: - test_dict[key] = eval(value) - except SyntaxError: - test_dict[key] = value - - assert returned_dict == test_dict - - # Tests not passing in the CI, but passing locally due to # different values in the ubuntu and windows machines From 98eaf199e97c70b07ae2616087a294556e252feb Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 6 Jul 2024 01:20:47 -0300 Subject: [PATCH 6/8] TST: Fix test --- tests/unit/test_tools_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_tools_matrix.py b/tests/unit/test_tools_matrix.py index f2b476fdc..89e75de0f 100644 --- a/tests/unit/test_tools_matrix.py +++ b/tests/unit/test_tools_matrix.py @@ -113,8 +113,8 @@ def test_matrix_neg(components): assert -Matrix(components) + Matrix(components) == Matrix.zeros() -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) def test_matrix_add(A, B): expected_result = np.array(A) + np.array(B) assert Matrix(A) + Matrix(B) == expected_result From 2c19b7c5f464c8cc4c82df62ad7ed8f9bbf5de73 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 6 Jul 2024 00:45:38 -0300 Subject: [PATCH 7/8] MNT: fix pylint errors in the `tests` module --- .github/workflows/linters.yml | 2 +- Makefile | 2 +- rocketpy/utilities.py | 1 + tests/acceptance/test_ndrt_2020_rocket.py | 28 ++- tests/fixtures/function/function_fixtures.py | 2 +- tests/fixtures/motor/solid_motor_fixtures.py | 25 ++ tests/integration/test_environment.py | 17 +- .../integration/test_environment_analysis.py | 4 +- tests/integration/test_flight.py | 170 +++++++------ tests/integration/test_function.py | 10 +- tests/integration/test_genericmotor.py | 5 +- tests/integration/test_hybridmotor.py | 3 +- tests/integration/test_monte_carlo.py | 3 +- tests/integration/test_plots.py | 5 +- tests/integration/test_rocket.py | 8 +- tests/unit/test_environment.py | 20 +- tests/unit/test_environment_analysis.py | 8 +- tests/unit/test_flight.py | 61 ++--- tests/unit/test_flight_time_nodes.py | 4 +- tests/unit/test_function.py | 151 ++++++----- tests/unit/test_genericmotor.py | 79 +++--- tests/unit/test_hybridmotor.py | 86 +++---- tests/unit/test_liquidmotor.py | 70 +++--- tests/unit/test_monte_carlo.py | 3 - tests/unit/test_plots.py | 6 +- tests/unit/test_rocket.py | 159 ++++++------ tests/unit/test_solidmotor.py | 2 +- tests/unit/test_tank.py | 235 +++++++++++------- tests/unit/test_tools_matrix.py | 76 +++--- tests/unit/test_tools_vector.py | 4 +- tests/unit/test_utilities.py | 39 +-- 31 files changed, 657 insertions(+), 631 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 0c20918ab..1eee717d2 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -40,4 +40,4 @@ jobs: run: flake8 rocketpy/ tests/ - name: Run pylint run: | - pylint rocketpy/ + pylint rocketpy/ tests/ diff --git a/Makefile b/Makefile index d0c198873..07c620ade 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ flake8: flake8 rocketpy/ tests/ pylint: - -pylint rocketpy --output=.pylint-report.txt + -pylint rocketpy/ tests/ --output=.pylint-report.txt build-docs: cd docs && $(PYTHON) -m pip install -r requirements.txt && make html diff --git a/rocketpy/utilities.py b/rocketpy/utilities.py index 3a724e46f..adb925eee 100644 --- a/rocketpy/utilities.py +++ b/rocketpy/utilities.py @@ -424,6 +424,7 @@ def _flutter_prints( print(f"Altitude of minimum Safety Factor: {altitude_min_sf:.3f} m (AGL)\n") +# TODO: deprecate and delete this function. Never used and now we have Monte Carlo. def create_dispersion_dictionary(filename): """Creates a dictionary with the rocket data provided by a .csv file. File should be organized in four columns: attribute_class, parameter_name, diff --git a/tests/acceptance/test_ndrt_2020_rocket.py b/tests/acceptance/test_ndrt_2020_rocket.py index 9cc66c897..aa4e737d4 100644 --- a/tests/acceptance/test_ndrt_2020_rocket.py +++ b/tests/acceptance/test_ndrt_2020_rocket.py @@ -64,19 +64,19 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): } # Environment conditions - Env23 = Environment( + env = Environment( gravity=9.81, latitude=41.775447, longitude=-86.572467, date=(2020, 2, 23, 16), elevation=206, ) - Env23.set_atmospheric_model( + env.set_atmospheric_model( type="Reanalysis", file="tests/fixtures/acceptance/NDRT_2020/ndrt_2020_weather_data_ERA5.nc", dictionary="ECMWF", ) - Env23.max_expected_height = 2000 + env.max_expected_height = 2000 # motor information L1395 = SolidMotor( @@ -134,13 +134,13 @@ def test_ndrt_2020_rocket_data_asserts_acceptance(): ) # Parachute set-up - def drogue_trigger(p, h, y): + def drogue_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate drogue when vz < 0 m/s. return True if y[5] < 0 else False - def main_trigger(p, h, y): + def main_trigger(p, h, y): # pylint: disable=unused-argument # p = pressure # y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3] # activate main when vz < 0 m/s and z < 167.64 m (AGL) or 550 ft (AGL) @@ -164,17 +164,19 @@ def main_trigger(p, h, y): ) # Flight - Flight23 = Flight( + rocketpy_flight = Flight( rocket=NDRT2020, - environment=Env23, + environment=env, rail_length=parameters.get("rail_length")[0], inclination=parameters.get("inclination")[0], heading=parameters.get("heading")[0], ) - df_ndrt_rocketpy = pd.DataFrame(Flight23.z[:, :], columns=["Time", "Altitude"]) - df_ndrt_rocketpy["Vertical Velocity"] = Flight23.vz[:, 1] - # df_ndrt_rocketpy["Vertical Acceleration"] = Flight23.az[:, 1] - df_ndrt_rocketpy["Altitude"] -= Env23.elevation + df_ndrt_rocketpy = pd.DataFrame( + rocketpy_flight.z[:, :], columns=["Time", "Altitude"] + ) + df_ndrt_rocketpy["Vertical Velocity"] = rocketpy_flight.vz[:, 1] + # df_ndrt_rocketpy["Vertical Acceleration"] = rocketpy_flight.az[:, 1] + df_ndrt_rocketpy["Altitude"] -= env.elevation # Reading data from the flightData (sensors: Raven) df_ndrt_raven = pd.read_csv( @@ -205,14 +207,14 @@ def main_trigger(p, h, y): apogee_time_measured = df_ndrt_raven.loc[ df_ndrt_raven[" Altitude (Ft-AGL)"].idxmax(), " Time (s)" ] - apogee_time_simulated = Flight23.apogee_time + apogee_time_simulated = rocketpy_flight.apogee_time assert ( abs(max(df_ndrt_raven[" Altitude (m-AGL)"]) - max(df_ndrt_rocketpy["Altitude"])) / max(df_ndrt_raven[" Altitude (m-AGL)"]) < 0.015 ) - assert (max(velocity_raven_filt) - Flight23.max_speed) / max( + assert (max(velocity_raven_filt) - rocketpy_flight.max_speed) / max( velocity_raven_filt ) < 0.06 assert ( diff --git a/tests/fixtures/function/function_fixtures.py b/tests/fixtures/function/function_fixtures.py index 5b195c16b..7cba3699e 100644 --- a/tests/fixtures/function/function_fixtures.py +++ b/tests/fixtures/function/function_fixtures.py @@ -134,7 +134,7 @@ def lambda_quad_func(): Function A lambda function based on a string. """ - func = lambda x: x**2 # pylint: disable=unnecessary-lambda + func = lambda x: x**2 # pylint: disable=unnecessary-lambda-assignment return Function( source=func, ) diff --git a/tests/fixtures/motor/solid_motor_fixtures.py b/tests/fixtures/motor/solid_motor_fixtures.py index 587d5e970..eff7d65d5 100644 --- a/tests/fixtures/motor/solid_motor_fixtures.py +++ b/tests/fixtures/motor/solid_motor_fixtures.py @@ -117,3 +117,28 @@ def dimensionless_cesaroni_m1670(kg, m): # old name: dimensionless_motor coordinate_system_orientation="nozzle_to_combustion_chamber", ) return example_motor + + +@pytest.fixture +def dummy_empty_motor(): + # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant + # TODO: why don t we use these same values to create EmptyMotor class? + return SolidMotor( + thrust_source=1e-300, + burn_time=1e-10, + dry_mass=1.815, + dry_inertia=(0.125, 0.125, 0.002), + center_of_dry_mass_position=0.317, + grains_center_of_mass_position=0.397, + grain_number=5, + grain_separation=5 / 1000, + grain_density=1e-300, + grain_outer_radius=33 / 1000, + grain_initial_inner_radius=15 / 1000, + grain_initial_height=120 / 1000, + nozzle_radius=33 / 1000, + throat_radius=11 / 1000, + nozzle_position=0, + interpolation_method="linear", + coordinate_system_orientation="nozzle_to_combustion_chamber", + ) diff --git a/tests/integration/test_environment.py b/tests/integration/test_environment.py index 6f0d3fc09..3013d879c 100644 --- a/tests/integration/test_environment.py +++ b/tests/integration/test_environment.py @@ -100,7 +100,7 @@ def test_gefs_atmosphere( @patch("matplotlib.pyplot.show") def test_custom_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the custom atmosphere model in the environment object. Parameters @@ -127,7 +127,7 @@ def test_custom_atmosphere( @patch("matplotlib.pyplot.show") def test_standard_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the standard atmosphere model in the environment object. Parameters @@ -148,7 +148,7 @@ def test_standard_atmosphere( @patch("matplotlib.pyplot.show") def test_wyoming_sounding_atmosphere( mock_show, example_plain_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Asserts whether the Wyoming sounding model in the environment object behaves as expected with respect to some attributes such as pressure, barometric_height, wind_velocity and temperature. @@ -163,15 +163,14 @@ def test_wyoming_sounding_atmosphere( # TODO:: this should be added to the set_atmospheric_model() method as a # "file" option, instead of receiving the URL as a string. - URL = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" + url = "http://weather.uwyo.edu/cgi-bin/sounding?region=samer&TYPE=TEXT%3ALIST&YEAR=2019&MONTH=02&FROM=0500&TO=0512&STNM=83779" # give it at least 5 times to try to download the file for i in range(5): try: - example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=URL) + example_plain_env.set_atmospheric_model(type="wyoming_sounding", file=url) break - except: - time.sleep(1) # wait 1 second before trying again - pass + except Exception: # pylint: disable=broad-except + time.sleep(2**i) assert example_plain_env.all_info() is None assert abs(example_plain_env.pressure(0) - 93600.0) < 1e-8 assert ( @@ -227,7 +226,7 @@ def test_hiresw_ensemble_atmosphere( @patch("matplotlib.pyplot.show") def test_cmc_atmosphere( mock_show, example_spaceport_env -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument """Tests the Ensemble model with the CMC file. Parameters diff --git a/tests/integration/test_environment_analysis.py b/tests/integration/test_environment_analysis.py index 17129e6f1..1be33fe96 100644 --- a/tests/integration/test_environment_analysis.py +++ b/tests/integration/test_environment_analysis.py @@ -10,7 +10,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, env_analysis): +def test_all_info(mock_show, env_analysis): # pylint: disable=unused-argument """Test the EnvironmentAnalysis.all_info() method, which already invokes several other methods. It is a good way to test the whole class in a first view. However, if it fails, it is hard to know which method is failing. @@ -32,7 +32,7 @@ def test_all_info(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_exports(mock_show, env_analysis): +def test_exports(mock_show, env_analysis): # pylint: disable=unused-argument """Check the export methods of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the files are correct, as this would require a lot of work and would be diff --git a/tests/integration/test_flight.py b/tests/integration/test_flight.py index fd8625435..8ac6e2936 100644 --- a/tests/integration/test_flight.py +++ b/tests/integration/test_flight.py @@ -11,7 +11,7 @@ @patch("matplotlib.pyplot.show") -def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused-argument +def test_all_info(mock_show, flight_calisto_robust): # pylint: disable=unused-argument """Test that the flight class is working as intended. This basically calls the all_info() method and checks if it returns None. It is not testing if the values are correct, but whether the method is working without errors. @@ -27,65 +27,61 @@ def test_all_info(mock_show, flight_calisto_robust): # pylint: disable: unused- assert flight_calisto_robust.all_info() is None -def test_export_data(flight_calisto): - """Tests wether the method Flight.export_data is working as intended - - Parameters: - ----------- - flight_calisto : rocketpy.Flight - Flight object to be tested. See the conftest.py file for more info - regarding this pytest fixture. - """ - test_flight = flight_calisto - - # Basic export - test_flight.export_data("test_export_data_1.csv") - - # Custom export - test_flight.export_data( - "test_export_data_2.csv", - "z", - "vz", - "e1", - "w3", - "angle_of_attack", - time_step=0.1, - ) - - # Load exported files and fixtures and compare them - test_1 = np.loadtxt("test_export_data_1.csv", delimiter=",") - test_2 = np.loadtxt("test_export_data_2.csv", delimiter=",") - - # Delete files - os.remove("test_export_data_1.csv") - os.remove("test_export_data_2.csv") - - # Check if basic exported content matches data - assert np.allclose(test_flight.x[:, 0], test_1[:, 0], atol=1e-5) - assert np.allclose(test_flight.x[:, 1], test_1[:, 1], atol=1e-5) - assert np.allclose(test_flight.y[:, 1], test_1[:, 2], atol=1e-5) - assert np.allclose(test_flight.z[:, 1], test_1[:, 3], atol=1e-5) - assert np.allclose(test_flight.vx[:, 1], test_1[:, 4], atol=1e-5) - assert np.allclose(test_flight.vy[:, 1], test_1[:, 5], atol=1e-5) - assert np.allclose(test_flight.vz[:, 1], test_1[:, 6], atol=1e-5) - assert np.allclose(test_flight.e0[:, 1], test_1[:, 7], atol=1e-5) - assert np.allclose(test_flight.e1[:, 1], test_1[:, 8], atol=1e-5) - assert np.allclose(test_flight.e2[:, 1], test_1[:, 9], atol=1e-5) - assert np.allclose(test_flight.e3[:, 1], test_1[:, 10], atol=1e-5) - assert np.allclose(test_flight.w1[:, 1], test_1[:, 11], atol=1e-5) - assert np.allclose(test_flight.w2[:, 1], test_1[:, 12], atol=1e-5) - assert np.allclose(test_flight.w3[:, 1], test_1[:, 13], atol=1e-5) - - # Check if custom exported content matches data - time_points = np.arange(test_flight.t_initial, test_flight.t_final, 0.1) - assert np.allclose(time_points, test_2[:, 0], atol=1e-5) - assert np.allclose(test_flight.z(time_points), test_2[:, 1], atol=1e-5) - assert np.allclose(test_flight.vz(time_points), test_2[:, 2], atol=1e-5) - assert np.allclose(test_flight.e1(time_points), test_2[:, 3], atol=1e-5) - assert np.allclose(test_flight.w3(time_points), test_2[:, 4], atol=1e-5) - assert np.allclose( - test_flight.angle_of_attack(time_points), test_2[:, 5], atol=1e-5 - ) +class TestExportData: + """Tests the export_data method of the Flight class.""" + + def test_basic_export(self, flight_calisto): + """Tests basic export functionality""" + file_name = "test_export_data_1.csv" + flight_calisto.export_data(file_name) + self.validate_basic_export(flight_calisto, file_name) + os.remove(file_name) + + def test_custom_export(self, flight_calisto): + """Tests custom export functionality""" + file_name = "test_export_data_2.csv" + flight_calisto.export_data( + file_name, + "z", + "vz", + "e1", + "w3", + "angle_of_attack", + time_step=0.1, + ) + self.validate_custom_export(flight_calisto, file_name) + os.remove(file_name) + + def validate_basic_export(self, flight_calisto, file_name): + """Validates the basic export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + assert np.allclose(flight_calisto.x[:, 0], test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.x[:, 1], test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.y[:, 1], test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.z[:, 1], test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.vx[:, 1], test_data[:, 4], atol=1e-5) + assert np.allclose(flight_calisto.vy[:, 1], test_data[:, 5], atol=1e-5) + assert np.allclose(flight_calisto.vz[:, 1], test_data[:, 6], atol=1e-5) + assert np.allclose(flight_calisto.e0[:, 1], test_data[:, 7], atol=1e-5) + assert np.allclose(flight_calisto.e1[:, 1], test_data[:, 8], atol=1e-5) + assert np.allclose(flight_calisto.e2[:, 1], test_data[:, 9], atol=1e-5) + assert np.allclose(flight_calisto.e3[:, 1], test_data[:, 10], atol=1e-5) + assert np.allclose(flight_calisto.w1[:, 1], test_data[:, 11], atol=1e-5) + assert np.allclose(flight_calisto.w2[:, 1], test_data[:, 12], atol=1e-5) + assert np.allclose(flight_calisto.w3[:, 1], test_data[:, 13], atol=1e-5) + + def validate_custom_export(self, flight_calisto, file_name): + """Validates the custom export file content""" + test_data = np.loadtxt(file_name, delimiter=",") + time_points = np.arange(flight_calisto.t_initial, flight_calisto.t_final, 0.1) + assert np.allclose(time_points, test_data[:, 0], atol=1e-5) + assert np.allclose(flight_calisto.z(time_points), test_data[:, 1], atol=1e-5) + assert np.allclose(flight_calisto.vz(time_points), test_data[:, 2], atol=1e-5) + assert np.allclose(flight_calisto.e1(time_points), test_data[:, 3], atol=1e-5) + assert np.allclose(flight_calisto.w3(time_points), test_data[:, 4], atol=1e-5) + assert np.allclose( + flight_calisto.angle_of_attack(time_points), test_data[:, 5], atol=1e-5 + ) def test_export_kml(flight_calisto_robust): @@ -106,14 +102,13 @@ def test_export_kml(flight_calisto_robust): ) # Load exported files and fixtures and compare them - test_1 = open("test_export_data_1.kml", "r") - - for row in test_1: - if row[:29] == " ": - r = row[29:-15] - r = r.split(",") - for i, j in enumerate(r): - r[i] = j.split(" ") + with open("test_export_data_1.kml", "r") as test_1: + for row in test_1: + if row[:29] == " ": + r = row[29:-15] + r = r.split(",") + for i, j in enumerate(r): + r[i] = j.split(" ") lon, lat, z, coords = [], [], [], [] for i in r: for j in i: @@ -122,9 +117,6 @@ def test_export_kml(flight_calisto_robust): lon.append(float(coords[i])) lat.append(float(coords[i + 1])) z.append(float(coords[i + 2])) - - # Delete temporary test file - test_1.close() os.remove("test_export_data_1.kml") assert np.allclose(test_flight.latitude[:, 1], lat, atol=1e-3) @@ -161,7 +153,9 @@ def test_export_pressures(flight_calisto_robust): @patch("matplotlib.pyplot.show") -def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): +def test_hybrid_motor_flight( + mock_show, calisto_hybrid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a hybrid motor. This test only validates that a flight simulation can be performed with a hybrid motor; it does not validate the results. @@ -186,7 +180,9 @@ def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded): @patch("matplotlib.pyplot.show") -def test_liquid_motor_flight(mock_show, calisto_liquid_modded): +def test_liquid_motor_flight( + mock_show, calisto_liquid_modded +): # pylint: disable=unused-argument """Test the flight of a rocket with a liquid motor. This test only validates that a flight simulation can be performed with a liquid motor; it does not validate the results. @@ -212,7 +208,9 @@ def test_liquid_motor_flight(mock_show, calisto_liquid_modded): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): +def test_time_overshoot( + mock_show, calisto_robust, example_spaceport_env +): # pylint: disable=unused-argument """Test the time_overshoot parameter of the Flight class. This basically calls the all_info() method for a simulation without time_overshoot and checks if it returns None. It is not testing if the values are correct, @@ -241,7 +239,9 @@ def test_time_overshoot(mock_show, calisto_robust, example_spaceport_env): @patch("matplotlib.pyplot.show") -def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust): +def test_simpler_parachute_triggers( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests different types of parachute triggers. This is important to ensure the code is working as intended, since the parachute triggers can have very different format definitions. It will add 3 parachutes using different @@ -313,8 +313,8 @@ def test_simpler_parachute_triggers(mock_show, example_plain_env, calisto_robust @patch("matplotlib.pyplot.show") -def test_rolling_flight( - mock_show, # pylint: disable: unused-argument +def test_rolling_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -352,8 +352,8 @@ def test_rolling_flight( @patch("matplotlib.pyplot.show") -def test_eccentricity_on_flight( - mock_show, # pylint: disable: unused-argument +def test_eccentricity_on_flight( # pylint: disable=unused-argument + mock_show, example_plain_env, cesaroni_m1670, calisto, @@ -383,7 +383,9 @@ def test_eccentricity_on_flight( @patch("matplotlib.pyplot.show") -def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): +def test_air_brakes_flight( + mock_show, flight_calisto_air_brakes +): # pylint: disable=unused-argument """Test the flight of a rocket with air brakes. This test only validates that a flight simulation can be performed with air brakes; it does not validate the results. @@ -403,7 +405,9 @@ def test_air_brakes_flight(mock_show, flight_calisto_air_brakes): @patch("matplotlib.pyplot.show") -def test_initial_solution(mock_show, example_plain_env, calisto_robust): +def test_initial_solution( + mock_show, example_plain_env, calisto_robust +): # pylint: disable=unused-argument """Tests the initial_solution option of the Flight class. This test simply simulates the flight using the initial_solution option and checks if the all_info method returns None. @@ -448,7 +452,9 @@ def test_initial_solution(mock_show, example_plain_env, calisto_robust): @patch("matplotlib.pyplot.show") -def test_empty_motor_flight(mock_show, example_plain_env, calisto_motorless): +def test_empty_motor_flight( + mock_show, example_plain_env, calisto_motorless +): # pylint: disable=unused-argument flight = Flight( rocket=calisto_motorless, environment=example_plain_env, diff --git a/tests/integration/test_function.py b/tests/integration/test_function.py index 7b6f204eb..a7e3144e5 100644 --- a/tests/integration/test_function.py +++ b/tests/integration/test_function.py @@ -112,15 +112,15 @@ def test_func_from_csv_with_header(csv_file): line. It tests cases where the fields are separated by quotes and without quotes.""" f = Function(csv_file) - assert f.__repr__() == "'Function from R1 to R1 : (time) → (value)'" + assert repr(f) == "'Function from R1 to R1 : (time) → (value)'" assert np.isclose(f(0), 100) assert np.isclose(f(0) + f(1), 300), "Error summing the values of the function" @patch("matplotlib.pyplot.show") -def test_plots( +def test_plots( # pylint: disable=unused-argument mock_show, func_from_csv, func_2d_from_csv -): # pylint: disable: unused-argument +): """Test different plot methods of the Function class. Parameters @@ -150,7 +150,7 @@ def test_plots( @patch("matplotlib.pyplot.show") -def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_dataset_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable dataset.""" # Test plane f(x,y) = x - y source = [ @@ -171,7 +171,7 @@ def test_multivariable_dataset_plot(mock_show): # pylint: disable: unused-argum @patch("matplotlib.pyplot.show") -def test_multivariable_function_plot(mock_show): # pylint: disable: unused-argument +def test_multivariable_function_plot(mock_show): # pylint: disable=unused-argument """Test the plot method of the Function class with a multivariable function.""" def source(x, y): diff --git a/tests/integration/test_genericmotor.py b/tests/integration/test_genericmotor.py index 8b5a18a15..6373fc055 100644 --- a/tests/integration/test_genericmotor.py +++ b/tests/integration/test_genericmotor.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_generic_motor_info( - mock_show, generic_motor -): # pylint: disable: unused-argument +def test_generic_motor_info(mock_show, generic_motor): """Tests the GenericMotor.all_info() method. Parameters diff --git a/tests/integration/test_hybridmotor.py b/tests/integration/test_hybridmotor.py index 59f343132..1c7ed5cc8 100644 --- a/tests/integration/test_hybridmotor.py +++ b/tests/integration/test_hybridmotor.py @@ -1,8 +1,9 @@ +# pylint: disable=unused-argument from unittest.mock import patch @patch("matplotlib.pyplot.show") -def test_hybrid_motor_info(mock_show, hybrid_motor): # pylint: disable: unused-argument +def test_hybrid_motor_info(mock_show, hybrid_motor): """Tests the HybridMotor.all_info() method. Parameters diff --git a/tests/integration/test_monte_carlo.py b/tests/integration/test_monte_carlo.py index 91838c828..5f11a9b25 100644 --- a/tests/integration/test_monte_carlo.py +++ b/tests/integration/test_monte_carlo.py @@ -1,3 +1,4 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch @@ -85,7 +86,7 @@ def test_monte_carlo_prints(monte_carlo_calisto): monte_carlo_calisto.info() -@patch("matplotlib.pyplot.show") +@patch("matplotlib.pyplot.show") # pylint: disable=unused-argument def test_monte_carlo_plots(mock_show, monte_carlo_calisto_pre_loaded): """Tests the plots methods of the MonteCarlo class.""" assert monte_carlo_calisto_pre_loaded.all_info() is None diff --git a/tests/integration/test_plots.py b/tests/integration/test_plots.py index edb8fad09..232ef71c6 100644 --- a/tests/integration/test_plots.py +++ b/tests/integration/test_plots.py @@ -1,10 +1,9 @@ +# pylint: disable=unused-argument import os from unittest.mock import patch -import matplotlib.pyplot as plt - from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import CompareFlights @patch("matplotlib.pyplot.show") diff --git a/tests/integration/test_rocket.py b/tests/integration/test_rocket.py index db7eafeff..4d5daf7a6 100644 --- a/tests/integration/test_rocket.py +++ b/tests/integration/test_rocket.py @@ -80,7 +80,9 @@ def test_air_brakes_clamp_on( @patch("matplotlib.pyplot.show") -def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): +def test_air_brakes_clamp_off( # pylint: disable=unused-argument + mock_show, calisto_air_brakes_clamp_off +): """Test the air brakes class with clamp off configuration. This test checks the basic attributes and the deployment_level setter. It also checks the all_info method. @@ -115,7 +117,7 @@ def test_air_brakes_clamp_off(mock_show, calisto_air_brakes_clamp_off): @patch("matplotlib.pyplot.show") -def test_rocket(mock_show, calisto_robust): +def test_rocket(mock_show, calisto_robust): # pylint: disable=unused-argument test_rocket = calisto_robust static_margin = test_rocket.static_margin(0) # Check if all_info and static_method methods are working properly @@ -123,7 +125,7 @@ def test_rocket(mock_show, calisto_robust): @patch("matplotlib.pyplot.show") -def test_aero_surfaces_infos( +def test_aero_surfaces_infos( # pylint: disable=unused-argument mock_show, calisto_nose_cone, calisto_tail, calisto_trapezoidal_fins ): assert calisto_nose_cone.all_info() is None diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index a06b92fdb..c4217331c 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -3,9 +3,9 @@ from unittest.mock import patch import numpy as np -import numpy.ma as ma import pytest import pytz +from numpy import ma from rocketpy import Environment @@ -73,18 +73,20 @@ def test_location_set_topographic_profile_computes_elevation( def test_geodesic_coordinate_geodesic_to_utm_converts_coordinate(): """Tests the conversion from geodesic to UTM coordinates.""" - x, y, utm_zone, utm_letter, hemis, EW = Environment.geodesic_to_utm( - lat=32.990254, - lon=-106.974998, - semi_major_axis=6378137.0, # WGS84 - flattening=1 / 298.257223563, # WGS84 + x, y, utm_zone, utm_letter, north_south_hemis, east_west_hemis = ( + Environment.geodesic_to_utm( + lat=32.990254, + lon=-106.974998, + semi_major_axis=6378137.0, # WGS84 + flattening=1 / 298.257223563, # WGS84 + ) ) assert np.isclose(x, 315468.64, atol=1e-5) assert np.isclose(y, 3651938.65, atol=1e-5) assert utm_zone == 13 assert utm_letter == "S" - assert hemis == "N" - assert EW == "W" + assert north_south_hemis == "N" + assert east_west_hemis == "W" def test_utm_to_geodesic_converts_coordinates(): @@ -159,7 +161,7 @@ def test_decimal_degrees_to_arc_seconds_computes_correct_values( @patch("matplotlib.pyplot.show") -def test_info_returns(mock_show, example_plain_env): +def test_info_returns(mock_show, example_plain_env): # pylint: disable=unused-argument """Tests the all_info_returned() all_plot_info_returned() and methods of the Environment class. diff --git a/tests/unit/test_environment_analysis.py b/tests/unit/test_environment_analysis.py index ed5fbc952..caa8fb847 100644 --- a/tests/unit/test_environment_analysis.py +++ b/tests/unit/test_environment_analysis.py @@ -11,7 +11,7 @@ @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_distribution_plots(mock_show, env_analysis): +def test_distribution_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the distribution plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -42,7 +42,7 @@ def test_distribution_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_average_plots(mock_show, env_analysis): +def test_average_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Tests the average plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -68,7 +68,7 @@ def test_average_plots(mock_show, env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_profile_plots(mock_show, env_analysis): +def test_profile_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the profile plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be @@ -138,7 +138,7 @@ def test_values(env_analysis): @pytest.mark.slow @patch("matplotlib.pyplot.show") -def test_animation_plots(mock_show, env_analysis): +def test_animation_plots(mock_show, env_analysis): # pylint: disable=unused-argument """Check the animation plots method of the EnvironmentAnalysis class. It only checks if the method runs without errors. It does not check if the plots are correct, as this would require a lot of work and would be diff --git a/tests/unit/test_flight.py b/tests/unit/test_flight.py index 2f438c78c..078c682c9 100644 --- a/tests/unit/test_flight.py +++ b/tests/unit/test_flight.py @@ -5,7 +5,7 @@ import pytest from scipy import optimize -from rocketpy import Components, Environment, Flight, Function, Rocket, SolidMotor +from rocketpy import Components, Flight, Function, Rocket plt.rcParams.update({"figure.max_open_warning": 0}) @@ -190,13 +190,13 @@ def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_v The expected values of the aerodynamic moments vector at the point to be tested. """ - expected_attr, expected_M = flight_time, expected_values + expected_attr, expected_moment = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_M, abs=atol) == ( + assert pytest.approx(expected_moment, abs=atol) == ( test.M1(t), test.M2(t), test.M3(t), @@ -229,13 +229,13 @@ def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_va The expected values of the aerodynamic forces vector at the point to be tested. """ - expected_attr, expected_R = flight_time, expected_values + expected_attr, expected_forces = flight_time, expected_values test = flight_calisto_custom_wind t = getattr(test, expected_attr) atol = 5e-3 - assert pytest.approx(expected_R, abs=atol) == ( + assert pytest.approx(expected_forces, abs=atol) == ( test.R1(t), test.R2(t), test.R3(t), @@ -507,7 +507,9 @@ def test_lat_lon_conversion_from_origin( "static_margin, max_time", [(-0.1, 2), (-0.01, 5), (0, 5), (0.01, 20), (0.1, 20), (1.0, 20)], ) -def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): +def test_stability_static_margins( + wind_u, wind_v, static_margin, max_time, example_plain_env, dummy_empty_motor +): """Test stability margins for a constant velocity flight, 100 m/s, wind a lateral wind speed of 10 m/s. Rocket has infinite mass to prevent side motion. Check if a restoring moment exists depending on static margins. @@ -522,11 +524,14 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): Static margin to be tested max_time : float Maximum time to be simulated + example_plain_env : rocketpy.Environment + This is a fixture. + dummy_empty_motor : rocketpy.SolidMotor + This is a fixture. """ # Create an environment with ZERO gravity to keep the rocket's speed constant - env = Environment(gravity=0, latitude=0, longitude=0, elevation=0) - env.set_atmospheric_model( + example_plain_env.set_atmospheric_model( type="custom_atmosphere", wind_u=wind_u, wind_v=wind_v, @@ -535,29 +540,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): ) # Make sure that the free_stream_mach will always be 0, so that the rocket # behaves as the STATIC (free_stream_mach=0) margin predicts - env.speed_of_sound = Function(1e16) - - # Create a motor with ZERO thrust and ZERO mass to keep the rocket's speed constant - # TODO: why don t we use these same values to create EmptyMotor class? - dummy_motor = SolidMotor( - thrust_source=1e-300, - burn_time=1e-10, - dry_mass=1.815, - dry_inertia=(0.125, 0.125, 0.002), - center_of_dry_mass_position=0.317, - grains_center_of_mass_position=0.397, - grain_number=5, - grain_separation=5 / 1000, - grain_density=1e-300, - grain_outer_radius=33 / 1000, - grain_initial_inner_radius=15 / 1000, - grain_initial_height=120 / 1000, - nozzle_radius=33 / 1000, - throat_radius=11 / 1000, - nozzle_position=0, - interpolation_method="linear", - coordinate_system_orientation="nozzle_to_combustion_chamber", - ) + example_plain_env.speed_of_sound = Function(1e16) # create a rocket with zero drag and huge mass to keep the rocket's speed constant dummy_rocket = Rocket( @@ -569,7 +552,7 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): center_of_mass_without_motor=0, ) dummy_rocket.set_rail_buttons(0.082, -0.618) - dummy_rocket.add_motor(dummy_motor, position=-1.373) + dummy_rocket.add_motor(dummy_empty_motor, position=-1.373) setup_rocket_with_given_static_margin(dummy_rocket, static_margin) @@ -582,13 +565,12 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): test_flight = Flight( rocket=dummy_rocket, rail_length=1, - environment=env, + environment=example_plain_env, initial_solution=initial_solution, max_time=max_time, max_time_step=1e-2, verbose=False, ) - test_flight.post_process(interpolation="linear") # Check stability according to static margin if wind_u == 0: @@ -598,8 +580,9 @@ def test_stability_static_margins(wind_u, wind_v, static_margin, max_time): moments = test_flight.M2.get_source()[:, 1] wind_sign = -np.sign(wind_u) - assert ( - (static_margin > 0 and np.max(moments) * np.min(moments) < 0) - or (static_margin < 0 and np.all(moments / wind_sign <= 0)) - or (static_margin == 0 and np.all(np.abs(moments) <= 1e-10)) - ) + if static_margin > 0: + assert np.max(moments) * np.min(moments) < 0 + elif static_margin < 0: + assert np.all(moments / wind_sign <= 0) + else: # static_margin == 0 + assert np.all(np.abs(moments) <= 1e-10) diff --git a/tests/unit/test_flight_time_nodes.py b/tests/unit/test_flight_time_nodes.py index 10f6b6c30..1e2661210 100644 --- a/tests/unit/test_flight_time_nodes.py +++ b/tests/unit/test_flight_time_nodes.py @@ -2,9 +2,7 @@ TimeNode. """ -import pytest - -from rocketpy.rocket import Parachute, _Controller +# from rocketpy.rocket import Parachute, _Controller def test_time_nodes_init(flight_calisto): diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index 3c1934f9f..9efb64c0c 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -359,89 +359,78 @@ def test_setters(func_from_csv, func_2d_from_csv): assert func_2d_from_csv.get_extrapolation_method() == "natural" -def test_interpolation_methods(linear_func): - """Tests some of the interpolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test Akima - assert isinstance(linear_func.set_interpolation("akima"), Function) - linear_func.set_interpolation("akima") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "akima" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - # Test polynomial - - assert isinstance(linear_func.set_interpolation("polynomial"), Function) - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.get_interpolation_method(), str) - assert linear_func.get_interpolation_method() == "polynomial" - assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) - - -def test_extrapolation_methods(linear_func): - """Test some of the extrapolation methods of the Function class. Methods - not tested here are already being called in other tests. - - Parameters - ---------- - linear_func : rocketpy.Function - A Function object created from a list of values. - """ - # Test zero - linear_func.set_extrapolation("zero") - assert linear_func.get_extrapolation_method() == "zero" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test constant - assert isinstance(linear_func.set_extrapolation("constant"), Function) - linear_func.set_extrapolation("constant") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "constant" - assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) - - # Test natural for linear interpolation - linear_func.set_interpolation("linear") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for spline interpolation - linear_func.set_interpolation("spline") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for akima interpolation - linear_func.set_interpolation("akima") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) - - # Test natural for polynomial interpolation - linear_func.set_interpolation("polynomial") - assert isinstance(linear_func.set_extrapolation("natural"), Function) - linear_func.set_extrapolation("natural") - assert isinstance(linear_func.get_extrapolation_method(), str) - assert linear_func.get_extrapolation_method() == "natural" - assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) +class TestInterpolationMethods: + """Tests some of the interpolation methods of the Function class.""" + + def test_akima_interpolation(self, linear_func): + """Tests Akima interpolation method""" + assert isinstance(linear_func.set_interpolation("akima"), Function) + linear_func.set_interpolation("akima") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "akima" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + def test_polynomial_interpolation(self, linear_func): + """Tests polynomial interpolation method""" + assert isinstance(linear_func.set_interpolation("polynomial"), Function) + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.get_interpolation_method(), str) + assert linear_func.get_interpolation_method() == "polynomial" + assert np.isclose(linear_func.get_value(0), 0.0, atol=1e-6) + + +class TestExtrapolationMethods: + """Test some of the extrapolation methods of the Function class.""" + + def test_zero_extrapolation(self, linear_func): + linear_func.set_extrapolation("zero") + assert linear_func.get_extrapolation_method() == "zero" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_constant_extrapolation(self, linear_func): + assert isinstance(linear_func.set_extrapolation("constant"), Function) + linear_func.set_extrapolation("constant") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "constant" + assert np.isclose(linear_func.get_value(-1), 0, atol=1e-6) + + def test_natural_extrapolation_linear(self, linear_func): + linear_func.set_interpolation("linear") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_spline(self, linear_func): + linear_func.set_interpolation("spline") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_akima(self, linear_func): + linear_func.set_interpolation("akima") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) + + def test_natural_extrapolation_polynomial(self, linear_func): + linear_func.set_interpolation("polynomial") + assert isinstance(linear_func.set_extrapolation("natural"), Function) + linear_func.set_extrapolation("natural") + assert isinstance(linear_func.get_extrapolation_method(), str) + assert linear_func.get_extrapolation_method() == "natural" + assert np.isclose(linear_func.get_value(-1), -1, atol=1e-6) @pytest.mark.parametrize("a", [-1, 0, 1]) @pytest.mark.parametrize("b", [-1, 0, 1]) -def test_multivariable_dataset(a, b): - """Test the Function class with a multivariable dataset.""" +def test_multivariate_dataset(a, b): + """Test the Function class with a multivariate dataset.""" # Test plane f(x,y) = x + y source = [ (-1, -1, -2), @@ -515,8 +504,8 @@ def test_3d_shepard_interpolation(x, y, z, w_expected): @pytest.mark.parametrize("a", [-1, -0.5, 0, 0.5, 1]) @pytest.mark.parametrize("b", [-1, -0.5, 0, 0.5, 1]) -def test_multivariable_function(a, b): - """Test the Function class with a multivariable function.""" +def test_multivariate_function(a, b): + """Test the Function class with a multivariate function.""" def source(x, y): return np.sin(x + y) diff --git a/tests/unit/test_genericmotor.py b/tests/unit/test_genericmotor.py index 98bc5664f..c6321ae4d 100644 --- a/tests/unit/test_genericmotor.py +++ b/tests/unit/test_genericmotor.py @@ -2,16 +2,21 @@ import pytest import scipy.integrate -burn_time = (2, 7) -thrust_source = lambda t: 2000 - 100 * (t - 2) -chamber_height = 0.5 -chamber_radius = 0.075 -chamber_position = -0.25 -propellant_initial_mass = 5.0 -nozzle_position = -0.5 -nozzle_radius = 0.075 -dry_mass = 8.0 -dry_inertia = (0.2, 0.2, 0.08) +BURN_TIME = (2, 7) + + +def thrust_source(t): + return 2000 - 100 * (t - 2) + + +CHAMBER_HEIGHT = 0.5 +CHAMBER_RADIUS = 0.075 +CHAMBER_POSITION = -0.25 +PROPELLANT_INITIAL_MASS = 5.0 +NOZZLE_POSITION = -0.5 +NOZZLE_RADIUS = 0.075 +DRY_MASS = 8.0 +DRY_INERTIA = (0.2, 0.2, 0.08) def test_generic_motor_basic_parameters(generic_motor): @@ -22,19 +27,19 @@ def test_generic_motor_basic_parameters(generic_motor): generic_motor : rocketpy.GenericMotor The GenericMotor object to be used in the tests. """ - assert generic_motor.burn_time == burn_time - assert generic_motor.dry_mass == dry_mass + assert generic_motor.burn_time == BURN_TIME + assert generic_motor.dry_mass == DRY_MASS assert ( generic_motor.dry_I_11, generic_motor.dry_I_22, generic_motor.dry_I_33, - ) == dry_inertia - assert generic_motor.nozzle_position == nozzle_position - assert generic_motor.nozzle_radius == nozzle_radius - assert generic_motor.chamber_position == chamber_position - assert generic_motor.chamber_radius == chamber_radius - assert generic_motor.chamber_height == chamber_height - assert generic_motor.propellant_initial_mass == propellant_initial_mass + ) == DRY_INERTIA + assert generic_motor.nozzle_position == NOZZLE_POSITION + assert generic_motor.nozzle_radius == NOZZLE_RADIUS + assert generic_motor.chamber_position == CHAMBER_POSITION + assert generic_motor.chamber_radius == CHAMBER_RADIUS + assert generic_motor.chamber_height == CHAMBER_HEIGHT + assert generic_motor.propellant_initial_mass == PROPELLANT_INITIAL_MASS def test_generic_motor_thrust_parameters(generic_motor): @@ -46,20 +51,20 @@ def test_generic_motor_thrust_parameters(generic_motor): The GenericMotor object to be used in the tests. """ expected_thrust = np.array( - [(t, thrust_source(t)) for t in np.linspace(*burn_time, 50)] + [(t, thrust_source(t)) for t in np.linspace(*BURN_TIME, 50)] ) expected_total_impulse = scipy.integrate.trapezoid( expected_thrust[:, 1], expected_thrust[:, 0] ) - expected_exhaust_velocity = expected_total_impulse / propellant_initial_mass + expected_exhaust_velocity = expected_total_impulse / PROPELLANT_INITIAL_MASS expected_mass_flow_rate = -1 * expected_thrust[:, 1] / expected_exhaust_velocity # Discretize mass flow rate for testing purposes - mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*burn_time, 50) + mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*BURN_TIME, 50) assert generic_motor.thrust.y_array == pytest.approx(expected_thrust[:, 1]) assert generic_motor.total_impulse == pytest.approx(expected_total_impulse) - assert generic_motor.exhaust_velocity.average(*burn_time) == pytest.approx( + assert generic_motor.exhaust_velocity.average(*BURN_TIME) == pytest.approx( expected_exhaust_velocity ) assert mass_flow_rate.y_array == pytest.approx(expected_mass_flow_rate) @@ -78,8 +83,8 @@ def test_generic_motor_center_of_mass(generic_motor): center_of_mass = -0.25 # Discretize center of mass for testing purposes - generic_motor.center_of_propellant_mass.set_discrete(*burn_time, 50) - generic_motor.center_of_mass.set_discrete(*burn_time, 50) + generic_motor.center_of_propellant_mass.set_discrete(*BURN_TIME, 50) + generic_motor.center_of_mass.set_discrete(*BURN_TIME, 50) assert generic_motor.center_of_propellant_mass.y_array == pytest.approx( center_of_propellant_mass @@ -99,24 +104,24 @@ def test_generic_motor_inertia(generic_motor): The GenericMotor object to be used in the tests. """ # Tests the inertia formulation from the propellant mass - propellant_mass = generic_motor.propellant_mass.set_discrete(*burn_time, 50).y_array + propellant_mass = generic_motor.propellant_mass.set_discrete(*BURN_TIME, 50).y_array - propellant_I_11 = propellant_mass * (chamber_radius**2 / 4 + chamber_height**2 / 12) + propellant_I_11 = propellant_mass * (CHAMBER_RADIUS**2 / 4 + CHAMBER_HEIGHT**2 / 12) propellant_I_22 = propellant_I_11 - propellant_I_33 = propellant_mass * (chamber_radius**2 / 2) + propellant_I_33 = propellant_mass * (CHAMBER_RADIUS**2 / 2) # Centers of mass coincide, so no translation is needed - I_11 = propellant_I_11 + dry_inertia[0] - I_22 = propellant_I_22 + dry_inertia[1] - I_33 = propellant_I_33 + dry_inertia[2] + I_11 = propellant_I_11 + DRY_INERTIA[0] + I_22 = propellant_I_22 + DRY_INERTIA[1] + I_33 = propellant_I_33 + DRY_INERTIA[2] # Discretize inertia for testing purposes - generic_motor.propellant_I_11.set_discrete(*burn_time, 50) - generic_motor.propellant_I_22.set_discrete(*burn_time, 50) - generic_motor.propellant_I_33.set_discrete(*burn_time, 50) - generic_motor.I_11.set_discrete(*burn_time, 50) - generic_motor.I_22.set_discrete(*burn_time, 50) - generic_motor.I_33.set_discrete(*burn_time, 50) + generic_motor.propellant_I_11.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_22.set_discrete(*BURN_TIME, 50) + generic_motor.propellant_I_33.set_discrete(*BURN_TIME, 50) + generic_motor.I_11.set_discrete(*BURN_TIME, 50) + generic_motor.I_22.set_discrete(*BURN_TIME, 50) + generic_motor.I_33.set_discrete(*BURN_TIME, 50) assert generic_motor.propellant_I_11.y_array == pytest.approx(propellant_I_11) assert generic_motor.propellant_I_22.y_array == pytest.approx(propellant_I_22) diff --git a/tests/unit/test_hybridmotor.py b/tests/unit/test_hybridmotor.py index acf4b3e54..ef03a1998 100644 --- a/tests/unit/test_hybridmotor.py +++ b/tests/unit/test_hybridmotor.py @@ -1,26 +1,28 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -thrust_function = lambda t: 2000 - 100 * t -burn_time = 10 -center_of_dry_mass = 0 -dry_inertia = (4, 4, 0.1) -dry_mass = 8 -grain_density = 1700 -grain_number = 4 -grain_initial_height = 0.1 -grain_separation = 0 -grain_initial_inner_radius = 0.04 -grain_outer_radius = 0.1 -nozzle_position = -0.4 -nozzle_radius = 0.07 -grains_center_of_mass_position = -0.1 -oxidizer_tank_position = 0.3 + +def thrust_function(t): + return 2000 - 100 * t + + +BURN_TIME = 10 +CENTER_OF_DRY_MASS = 0 +DRY_INERTIA = (4, 4, 0.1) +DRY_MASS = 8 +GRAIN_DENSITY = 1700 +GRAIN_NUMBER = 4 +GRAIN_INITIAL_HEIGHT = 0.1 +GRAIN_SEPARATION = 0 +GRAIN_INITIAL_INNER_RADIUS = 0.04 +GRAIN_OUTER_RADIUS = 0.1 +NOZZLE_POSITION = -0.4 +NOZZLE_RADIUS = 0.07 +GRAINS_CENTER_OF_MASS_POSITION = -0.1 +OXIDIZER_TANK_POSITION = 0.3 def test_hybrid_motor_basic_parameters(hybrid_motor): @@ -31,25 +33,25 @@ def test_hybrid_motor_basic_parameters(hybrid_motor): hybrid_motor : rocketpy.HybridMotor The HybridMotor object to be used in the tests. """ - assert hybrid_motor.burn_time == (0, burn_time) - assert hybrid_motor.dry_mass == dry_mass + assert hybrid_motor.burn_time == (0, BURN_TIME) + assert hybrid_motor.dry_mass == DRY_MASS assert ( hybrid_motor.dry_I_11, hybrid_motor.dry_I_22, hybrid_motor.dry_I_33, - ) == dry_inertia - assert hybrid_motor.center_of_dry_mass_position == center_of_dry_mass - assert hybrid_motor.nozzle_position == nozzle_position - assert hybrid_motor.nozzle_radius == nozzle_radius - assert hybrid_motor.solid.grain_number == grain_number - assert hybrid_motor.solid.grain_density == grain_density - assert hybrid_motor.solid.grain_initial_height == grain_initial_height - assert hybrid_motor.solid.grain_separation == grain_separation - assert hybrid_motor.solid.grain_initial_inner_radius == grain_initial_inner_radius - assert hybrid_motor.solid.grain_outer_radius == grain_outer_radius + ) == DRY_INERTIA + assert hybrid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert hybrid_motor.nozzle_position == NOZZLE_POSITION + assert hybrid_motor.nozzle_radius == NOZZLE_RADIUS + assert hybrid_motor.solid.grain_number == GRAIN_NUMBER + assert hybrid_motor.solid.grain_density == GRAIN_DENSITY + assert hybrid_motor.solid.grain_initial_height == GRAIN_INITIAL_HEIGHT + assert hybrid_motor.solid.grain_separation == GRAIN_SEPARATION + assert hybrid_motor.solid.grain_initial_inner_radius == GRAIN_INITIAL_INNER_RADIUS + assert hybrid_motor.solid.grain_outer_radius == GRAIN_OUTER_RADIUS assert ( hybrid_motor.solid.grains_center_of_mass_position - == grains_center_of_mass_position + == GRAINS_CENTER_OF_MASS_POSITION ) assert hybrid_motor.liquid.positioned_tanks[0]["position"] == 0.3 @@ -69,11 +71,11 @@ def test_hybrid_motor_thrust_parameters(hybrid_motor, spherical_oxidizer_tank): expected_total_impulse = scipy.integrate.quad(expected_thrust, 0, 10)[0] initial_grain_mass = ( - grain_density + GRAIN_DENSITY * np.pi - * (grain_outer_radius**2 - grain_initial_inner_radius**2) - * grain_initial_height - * grain_number + * (GRAIN_OUTER_RADIUS**2 - GRAIN_INITIAL_INNER_RADIUS**2) + * GRAIN_INITIAL_HEIGHT + * GRAIN_NUMBER ) initial_oxidizer_mass = spherical_oxidizer_tank.fluid_mass(0) initial_mass = initial_grain_mass + initial_oxidizer_mass @@ -111,13 +113,13 @@ def test_hybrid_motor_center_of_mass(hybrid_motor, spherical_oxidizer_tank): oxidizer_mass = spherical_oxidizer_tank.fluid_mass grain_mass = hybrid_motor.solid.propellant_mass - propellant_balance = grain_mass * grains_center_of_mass_position + oxidizer_mass * ( - oxidizer_tank_position + spherical_oxidizer_tank.center_of_mass + propellant_balance = grain_mass * GRAINS_CENTER_OF_MASS_POSITION + oxidizer_mass * ( + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / (grain_mass + oxidizer_mass) - center_of_mass = balance / (grain_mass + oxidizer_mass + dry_mass) + center_of_mass = balance / (grain_mass + oxidizer_mass + DRY_MASS) for t in np.linspace(0, 100, 100): assert pytest.approx( @@ -145,12 +147,12 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): # Validate parallel axis theorem translation grain_inertia += ( grain_mass - * (grains_center_of_mass_position - hybrid_motor.center_of_propellant_mass) ** 2 + * (GRAINS_CENTER_OF_MASS_POSITION - hybrid_motor.center_of_propellant_mass) ** 2 ) oxidizer_inertia += ( oxidizer_mass * ( - oxidizer_tank_position + OXIDIZER_TANK_POSITION + spherical_oxidizer_tank.center_of_mass - hybrid_motor.center_of_propellant_mass ) @@ -164,8 +166,8 @@ def test_hybrid_motor_inertia(hybrid_motor, spherical_oxidizer_tank): propellant_inertia + propellant_mass * (hybrid_motor.center_of_propellant_mass - hybrid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-hybrid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-hybrid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) for t in np.linspace(0, 100, 100): diff --git a/tests/unit/test_liquidmotor.py b/tests/unit/test_liquidmotor.py index ed4fe0ab3..6208a7dc0 100644 --- a/tests/unit/test_liquidmotor.py +++ b/tests/unit/test_liquidmotor.py @@ -1,20 +1,18 @@ -from unittest.mock import patch - import numpy as np import pytest import scipy.integrate from rocketpy import Function -burn_time = (8, 20) -dry_mass = 10 -dry_inertia = (5, 5, 0.2) -center_of_dry_mass = 0 -nozzle_position = -1.364 -nozzle_radius = 0.069 / 2 -pressurant_tank_position = 2.007 -fuel_tank_position = -1.048 -oxidizer_tank_position = 0.711 +BURN_TIME = (8, 20) +DRY_MASS = 10 +DRY_INERTIA = (5, 5, 0.2) +CENTER_OF_DRY_MASS = 0 +NOZZLE_POSITION = -1.364 +NOZZLE_RADIUS = 0.069 / 2 +PRESSURANT_TANK_POSITION = 2.007 +FUEL_TANK_POSITION = -1.048 +OXIDIZER_TANK_POSITION = 0.711 def test_liquid_motor_basic_parameters(liquid_motor): @@ -25,19 +23,19 @@ def test_liquid_motor_basic_parameters(liquid_motor): liquid_motor : rocketpy.LiquidMotor The LiquidMotor object to be used in the tests. """ - assert liquid_motor.burn_time == burn_time - assert liquid_motor.dry_mass == dry_mass + assert liquid_motor.burn_time == BURN_TIME + assert liquid_motor.dry_mass == DRY_MASS assert ( liquid_motor.dry_I_11, liquid_motor.dry_I_22, liquid_motor.dry_I_33, - ) == dry_inertia - assert liquid_motor.center_of_dry_mass_position == center_of_dry_mass - assert liquid_motor.nozzle_position == nozzle_position - assert liquid_motor.nozzle_radius == nozzle_radius - assert liquid_motor.positioned_tanks[0]["position"] == pressurant_tank_position - assert liquid_motor.positioned_tanks[1]["position"] == fuel_tank_position - assert liquid_motor.positioned_tanks[2]["position"] == oxidizer_tank_position + ) == DRY_INERTIA + assert liquid_motor.center_of_dry_mass_position == CENTER_OF_DRY_MASS + assert liquid_motor.nozzle_position == NOZZLE_POSITION + assert liquid_motor.nozzle_radius == NOZZLE_RADIUS + assert liquid_motor.positioned_tanks[0]["position"] == PRESSURANT_TANK_POSITION + assert liquid_motor.positioned_tanks[1]["position"] == FUEL_TANK_POSITION + assert liquid_motor.positioned_tanks[2]["position"] == OXIDIZER_TANK_POSITION def test_liquid_motor_thrust_parameters( @@ -125,12 +123,12 @@ def test_liquid_motor_mass_volume( ) # Perform default discretization - expected_pressurant_mass.set_discrete(*burn_time, 100) - expected_fuel_mass.set_discrete(*burn_time, 100) - expected_oxidizer_mass.set_discrete(*burn_time, 100) - expected_pressurant_volume.set_discrete(*burn_time, 100) - expected_fuel_volume.set_discrete(*burn_time, 100) - expected_oxidizer_volume.set_discrete(*burn_time, 100) + expected_pressurant_mass.set_discrete(*BURN_TIME, 100) + expected_fuel_mass.set_discrete(*BURN_TIME, 100) + expected_oxidizer_mass.set_discrete(*BURN_TIME, 100) + expected_pressurant_volume.set_discrete(*BURN_TIME, 100) + expected_fuel_volume.set_discrete(*BURN_TIME, 100) + expected_oxidizer_volume.set_discrete(*BURN_TIME, 100) assert ( pytest.approx(expected_pressurant_mass.y_array, 0.01) @@ -180,14 +178,14 @@ def test_liquid_motor_center_of_mass( propellant_mass = pressurant_mass + fuel_mass + oxidizer_mass propellant_balance = ( - pressurant_mass * (pressurant_tank.center_of_mass + pressurant_tank_position) - + fuel_mass * (fuel_tank.center_of_mass + fuel_tank_position) - + oxidizer_mass * (oxidizer_tank.center_of_mass + oxidizer_tank_position) + pressurant_mass * (pressurant_tank.center_of_mass + PRESSURANT_TANK_POSITION) + + fuel_mass * (fuel_tank.center_of_mass + FUEL_TANK_POSITION) + + oxidizer_mass * (oxidizer_tank.center_of_mass + OXIDIZER_TANK_POSITION) ) - balance = propellant_balance + dry_mass * center_of_dry_mass + balance = propellant_balance + DRY_MASS * CENTER_OF_DRY_MASS propellant_center_of_mass = propellant_balance / propellant_mass - center_of_mass = balance / (propellant_mass + dry_mass) + center_of_mass = balance / (propellant_mass + DRY_MASS) assert ( pytest.approx(liquid_motor.center_of_propellant_mass.y_array) @@ -223,7 +221,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( pressurant_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + pressurant_tank_position + + PRESSURANT_TANK_POSITION ) ** 2 ) @@ -232,7 +230,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( fuel_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + fuel_tank_position + + FUEL_TANK_POSITION ) ** 2 ) @@ -241,7 +239,7 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer * ( oxidizer_tank.center_of_mass - liquid_motor.center_of_propellant_mass - + oxidizer_tank_position + + OXIDIZER_TANK_POSITION ) ** 2 ) @@ -253,8 +251,8 @@ def test_liquid_motor_inertia(liquid_motor, pressurant_tank, fuel_tank, oxidizer propellant_inertia + propellant_mass * (liquid_motor.center_of_propellant_mass - liquid_motor.center_of_mass) ** 2 - + dry_inertia[0] - + dry_mass * (-liquid_motor.center_of_mass + center_of_dry_mass) ** 2 + + DRY_INERTIA[0] + + DRY_MASS * (-liquid_motor.center_of_mass + CENTER_OF_DRY_MASS) ** 2 ) assert ( diff --git a/tests/unit/test_monte_carlo.py b/tests/unit/test_monte_carlo.py index 7af6a5db5..0e1ad22cc 100644 --- a/tests/unit/test_monte_carlo.py +++ b/tests/unit/test_monte_carlo.py @@ -1,8 +1,5 @@ -from unittest.mock import patch - import matplotlib as plt import numpy as np -import pytest plt.rcParams.update({"figure.max_open_warning": 0}) diff --git a/tests/unit/test_plots.py b/tests/unit/test_plots.py index db36264d8..cd35f8d11 100644 --- a/tests/unit/test_plots.py +++ b/tests/unit/test_plots.py @@ -1,14 +1,12 @@ -import os from unittest.mock import patch import matplotlib.pyplot as plt -from rocketpy import Flight -from rocketpy.plots.compare import Compare, CompareFlights +from rocketpy.plots.compare import Compare @patch("matplotlib.pyplot.show") -def test_compare(mock_show, flight_calisto): +def test_compare(mock_show, flight_calisto): # pylint: disable=unused-argument """Here we want to test the 'x_attributes' argument, which is the only one that is not tested in the other tests. diff --git a/tests/unit/test_rocket.py b/tests/unit/test_rocket.py index 876f5024d..a984466ee 100644 --- a/tests/unit/test_rocket.py +++ b/tests/unit/test_rocket.py @@ -10,7 +10,7 @@ @patch("matplotlib.pyplot.show") def test_elliptical_fins( mock_show, calisto_robust, calisto_trapezoidal_fins -): # pylint: disable: unused-argument +): # pylint: disable=unused-argument test_rocket = calisto_robust calisto_robust.aerodynamic_surfaces.remove(calisto_trapezoidal_fins) test_rocket.add_elliptical_fins(4, span=0.100, root_chord=0.120, position=-1.168) @@ -449,9 +449,9 @@ def test_evaluate_com_to_cdm_function(calisto): def test_get_inertia_tensor_at_time(calisto): # Expected values (for t = 0) # TODO: compute these values by hand or using CAD. - Ixx = 10.31379 - Iyy = 10.31379 - Izz = 0.039942 + I_11 = 10.31379 + I_22 = 10.31379 + I_33 = 0.039942 # Set tolerance threshold atol = 1e-5 @@ -460,9 +460,9 @@ def test_get_inertia_tensor_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_at_time(0) # Check if the values are close to the expected ones - assert pytest.approx(Ixx, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -475,9 +475,9 @@ def test_get_inertia_tensor_at_time(calisto): def test_get_inertia_tensor_derivative_at_time(calisto): # Expected values (for t = 2s) # TODO: compute these values by hand or using CAD. - Ixx_dot = -0.634805230901143 - Iyy_dot = -0.634805230901143 - Izz_dot = -0.000671493662305 + I_11_dot = -0.634805230901143 + I_22_dot = -0.634805230901143 + I_33_dot = -0.000671493662305 # Set tolerance threshold atol = 1e-3 @@ -486,9 +486,9 @@ def test_get_inertia_tensor_derivative_at_time(calisto): inertia_tensor = calisto.get_inertia_tensor_derivative_at_time(2) # Check if the values are close to the expected ones - assert pytest.approx(Ixx_dot, atol) == inertia_tensor.x[0] - assert pytest.approx(Iyy_dot, atol) == inertia_tensor.y[1] - assert pytest.approx(Izz_dot, atol) == inertia_tensor.z[2] + assert pytest.approx(I_11_dot, atol) == inertia_tensor.x[0] + assert pytest.approx(I_22_dot, atol) == inertia_tensor.y[1] + assert pytest.approx(I_33_dot, atol) == inertia_tensor.z[2] # Check if products of inertia are zero assert pytest.approx(0, atol) == inertia_tensor.x[1] assert pytest.approx(0, atol) == inertia_tensor.x[2] @@ -514,77 +514,72 @@ def test_add_cm_eccentricity(calisto): assert calisto.thrust_eccentricity_y == 0.1 -def test_add_surfaces_different_noses(calisto): +class TestAddSurfaces: """Test the add_surfaces method with different nose cone configurations. More specifically, this will check the static margin of the rocket with - different nose cone configurations. - - Parameters - ---------- - calisto : Rocket - Pytest fixture for the calisto rocket. - """ - length = 0.55829 - kind = "vonkarman" - position = 1.16 - bluffness = 0 - base_radius = 0.0635 - rocket_radius = 0.0635 - - # Case 1: base_radius == rocket_radius - nose1 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 1", - ) - calisto.add_surfaces(nose1, position) - assert nose1.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 2: base_radius == rocket_radius / 2 - calisto.aerodynamic_surfaces.remove(nose1) - nose2 = NoseCone( - length, - kind, - base_radius=base_radius / 2, - bluffness=bluffness, - rocket_radius=rocket_radius, - name="Nose Cone 2", - ) - calisto.add_surfaces(nose2, position) - assert nose2.radius_ratio == pytest.approx(0.5, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 3: base_radius is None - calisto.aerodynamic_surfaces.remove(nose2) - nose3 = NoseCone( - length, - kind, - base_radius=None, - bluffness=bluffness, - rocket_radius=rocket_radius * 2, - name="Nose Cone 3", - ) - calisto.add_surfaces(nose3, position) - assert nose3.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) - - # Case 4: rocket_radius is None - calisto.aerodynamic_surfaces.remove(nose3) - nose4 = NoseCone( - length, - kind, - base_radius=base_radius, - bluffness=bluffness, - rocket_radius=None, - name="Nose Cone 4", - ) - calisto.add_surfaces(nose4, position) - assert nose4.radius_ratio == pytest.approx(1, 1e-8) - assert calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + different nose cone configurations.""" + + @pytest.fixture(autouse=True) + def setup(self, calisto): + self.calisto = calisto + self.length = 0.55829 + self.kind = "vonkarman" + self.position = 1.16 + self.bluffness = 0 + self.base_radius = 0.0635 + self.rocket_radius = 0.0635 + + def test_add_surfaces_base_equals_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 1", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_half_rocket_radius(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius / 2, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius, + name="Nose Cone 2", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(0.5, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_base_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=None, + bluffness=self.bluffness, + rocket_radius=self.rocket_radius * 2, + name="Nose Cone 3", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) + + def test_add_surfaces_rocket_radius_none(self): + nose = NoseCone( + self.length, + self.kind, + base_radius=self.base_radius, + bluffness=self.bluffness, + rocket_radius=None, + name="Nose Cone 4", + ) + self.calisto.add_surfaces(nose, self.position) + assert nose.radius_ratio == pytest.approx(1, 1e-8) + assert self.calisto.static_margin(0) == pytest.approx(-8.9053, 0.01) def test_coordinate_system_orientation( diff --git a/tests/unit/test_solidmotor.py b/tests/unit/test_solidmotor.py index 6c5d4d4b1..064c8210e 100644 --- a/tests/unit/test_solidmotor.py +++ b/tests/unit/test_solidmotor.py @@ -20,7 +20,7 @@ @patch("matplotlib.pyplot.show") -def test_motor(mock_show, cesaroni_m1670): +def test_motor(mock_show, cesaroni_m1670): # pylint: disable=unused-argument """Tests the SolidMotor.all_info() method. Parameters diff --git a/tests/unit/test_tank.py b/tests/unit/test_tank.py index 13c7b6cb8..3a77a8bca 100644 --- a/tests/unit/test_tank.py +++ b/tests/unit/test_tank.py @@ -1,3 +1,5 @@ +# TODO: This file must be refactored to improve readability and maintainability. +# pylint: disable=too-many-statements import os from math import isclose @@ -202,6 +204,7 @@ def bottom_endcap(y): ) # Assert volume bounds + # pylint: disable=comparison-with-callable assert (real_tank_lox.gas_height <= real_tank_lox.geometry.top).all assert (real_tank_lox.fluid_volume <= real_tank_lox.geometry.total_volume).all assert (example_tank_lox.gas_height <= example_tank_lox.geometry.top).all @@ -231,17 +234,22 @@ def test(calculated, expected, t, real=False): def test_mass(): """Test mass function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: initial_liquid_mass - + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) - + initial_gas_mass - + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) - ) + + def example_expected(t): + return ( + initial_liquid_mass + + t * (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) + + initial_gas_mass + + t * (gas_mass_flow_rate_in - gas_mass_flow_rate_out) + ) + example_calculated = example_tank_lox.fluid_mass lox_vals = Function(lox_masses).y_array - real_expected = lambda t: lox_vals[t] + def real_expected(t): + return lox_vals[t] + real_calculated = real_tank_lox.fluid_mass test(example_calculated, example_expected, 5) @@ -249,19 +257,24 @@ def test_mass(): def test_net_mfr(): """Test net_mass_flow_rate function of MassBasedTank subclass of Tank""" - example_expected = ( - lambda t: liquid_mass_flow_rate_in - - liquid_mass_flow_rate_out - + gas_mass_flow_rate_in - - gas_mass_flow_rate_out - ) + + def example_expected(_): + return ( + liquid_mass_flow_rate_in + - liquid_mass_flow_rate_out + + gas_mass_flow_rate_in + - gas_mass_flow_rate_out + ) + example_calculated = example_tank_lox.net_mass_flow_rate liquid_mfrs = Function(example_liquid_masses).y_array gas_mfrs = Function(example_gas_masses).y_array - real_expected = lambda t: (liquid_mfrs[t] + gas_mfrs[t]) / t + def real_expected(t): + return (liquid_mfrs[t] + gas_mfrs[t]) / t + real_calculated = real_tank_lox.net_mass_flow_rate test(example_calculated, example_expected, 10) @@ -280,8 +293,12 @@ def test_level_based_tank(): test_dir = "./data/berkeley/" - top_endcap = lambda y: np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) - bottom_endcap = lambda y: np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + def top_endcap(y): + return np.sqrt(0.0775**2 - (y - 0.692300000000001) ** 2) + + def bottom_endcap(y): + return np.sqrt(0.0775**2 - (0.0775 - y) ** 2) + tank_geometry = TankGeometry( { (0, 0.0559): bottom_endcap, @@ -291,7 +308,7 @@ def test_level_based_tank(): ) ullage_data = Function(os.path.abspath(test_dir + "loxUllage.csv")).get_source() - levelTank = LevelBasedTank( + level_tank = LevelBasedTank( name="LevelTank", geometry=tank_geometry, flux_time=(0, 10), @@ -318,18 +335,18 @@ def align_time_series(small_source, large_source): for val in small_source: time = val[0] delta_time_vector = abs(time - large_source[:, 0]) - largeIndex = np.argmin(delta_time_vector) - delta_time = abs(time - large_source[largeIndex][0]) + large_index = np.argmin(delta_time_vector) + delta_time = abs(time - large_source[large_index][0]) if delta_time < tolerance: - result_larger_source[curr_ind] = large_source[largeIndex] + result_larger_source[curr_ind] = large_source[large_index] result_smaller_source[curr_ind] = val curr_ind += 1 return result_larger_source, result_smaller_source - assert np.allclose(levelTank.liquid_height, ullage_data) + assert np.allclose(level_tank.liquid_height, ullage_data) - calculated_mass = levelTank.liquid_mass.set_discrete( + calculated_mass = level_tank.liquid_mass.set_discrete( mass_data[0][0], mass_data[0][-1], len(mass_data[0]) ) calculated_mass, mass_data = align_time_series( @@ -337,7 +354,7 @@ def align_time_series(small_source, large_source): ) assert np.allclose(calculated_mass, mass_data, rtol=1, atol=2) - calculated_mfr = levelTank.net_mass_flow_rate.set_discrete( + calculated_mfr = level_tank.net_mass_flow_rate.set_discrete( mass_flow_rate_data[0][0], mass_flow_rate_data[0][-1], len(mass_flow_rate_data[0]), @@ -358,91 +375,133 @@ def test(t, a, tol=1e-4): assert isclose(t.get_value(i), a(i), abs_tol=tol) def test_nmfr(): - nmfr = ( - lambda x: liquid_mass_flow_rate_in - + gas_mass_flow_rate_in - - liquid_mass_flow_rate_out - - gas_mass_flow_rate_out - ) + def nmfr(_): + return ( + liquid_mass_flow_rate_in + + gas_mass_flow_rate_in + - liquid_mass_flow_rate_out + - gas_mass_flow_rate_out + ) + test(t.net_mass_flow_rate, nmfr) def test_mass(): - m = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) + (initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x) + def m(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + lm = t.fluid_mass test(lm, m) def test_liquid_height(): - alv = ( - lambda x: ( + def alv(x): + return ( initial_liquid_mass + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) - / lox.density - ) - alh = lambda x: alv(x) / (np.pi) + ) / lox.density + + def alh(x): + return alv(x) / (np.pi) + tlh = t.liquid_height test(tlh, alh) def test_com(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) tcom = t.center_of_mass test(tcom, acom) def test_inertia(): - liquid_mass = lambda x: ( - initial_liquid_mass - + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x - ) # liquid mass - liquid_volume = lambda x: liquid_mass(x) / lox.density # liquid volume - liquid_height = lambda x: liquid_volume(x) / (np.pi) # liquid height - gas_mass = lambda x: ( - initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x - ) # gas mass - gas_volume = lambda x: gas_mass(x) / n2.density - gas_height = lambda x: gas_volume(x) / np.pi + liquid_height(x) - - liquid_com = lambda x: liquid_height(x) / 2 # liquid com - gas_com = lambda x: (gas_height(x) - liquid_height(x)) / 2 + liquid_height( - x - ) # gas com - acom = lambda x: (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( - liquid_mass(x) + gas_mass(x) - ) + def liquid_mass(x): + return ( + initial_liquid_mass + + (liquid_mass_flow_rate_in - liquid_mass_flow_rate_out) * x + ) + + def liquid_volume(x): + return liquid_mass(x) / lox.density + + def liquid_height(x): + return liquid_volume(x) / (np.pi) + + def gas_mass(x): + return ( + initial_gas_mass + (gas_mass_flow_rate_in - gas_mass_flow_rate_out) * x + ) + + def gas_volume(x): + return gas_mass(x) / n2.density + + def gas_height(x): + return gas_volume(x) / np.pi + liquid_height(x) + + def liquid_com(x): + return liquid_height(x) / 2 + + def gas_com(x): + return (gas_height(x) - liquid_height(x)) / 2 + liquid_height(x) + + def acom(x): + return (liquid_mass(x) * liquid_com(x) + gas_mass(x) * gas_com(x)) / ( + liquid_mass(x) + gas_mass(x) + ) r = 1 - ixy_gas = ( - lambda x: 1 / 4 * gas_mass(x) * r**2 - + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 - + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 - ) - ixy_liq = ( - lambda x: 1 / 4 * liquid_mass(x) * r**2 - + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 - + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 - ) - ixy = lambda x: ixy_gas(x) + ixy_liq(x) + + def ixy_gas(x): + return ( + 1 / 4 * gas_mass(x) * r**2 + + 1 / 12 * gas_mass(x) * (gas_height(x) - liquid_height(x)) ** 2 + + gas_mass(x) * (gas_com(x) - acom(x)) ** 2 + ) + + def ixy_liq(x): + return ( + 1 / 4 * liquid_mass(x) * r**2 + + 1 / 12 * liquid_mass(x) * (liquid_height(x) - t.geometry.bottom) ** 2 + + liquid_mass(x) * (liquid_com(x) - acom(x)) ** 2 + ) + + def ixy(x): + return ixy_gas(x) + ixy_liq(x) + test(t.gas_inertia, ixy_gas, tol=1e-3) test(t.liquid_inertia, ixy_liq, tol=1e-3) test(t.inertia, ixy, tol=1e-3) diff --git a/tests/unit/test_tools_matrix.py b/tests/unit/test_tools_matrix.py index a6edb5278..f2b476fdc 100644 --- a/tests/unit/test_tools_matrix.py +++ b/tests/unit/test_tools_matrix.py @@ -97,7 +97,7 @@ def test_matrix_inverse(components): matrix = Matrix(components) if matrix.det == 0: with pytest.raises(ZeroDivisionError): - matrix.inverse + assert matrix.inverse else: assert matrix.inverse == np.linalg.inv(matrix) @@ -115,64 +115,64 @@ def test_matrix_neg(components): @pytest.mark.parametrize("A_c", test_matrices) @pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_add(A_c, B_c): - expected_result = np.array(A_c) + np.array(B_c) - assert Matrix(A_c) + Matrix(B_c) == expected_result +def test_matrix_add(A, B): + expected_result = np.array(A) + np.array(B) + assert Matrix(A) + Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_sub(A_c, B_c): - expected_result = np.array(A_c) - np.array(B_c) - assert Matrix(A_c) - Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_sub(A, B): + expected_result = np.array(A) - np.array(B) + assert Matrix(A) - Matrix(B) == expected_result @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_mul(A_c, k): - A = Matrix(A_c) - assert A * k == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_mul(A, k): + A = Matrix(A) + assert A * k == k * np.array(A) @pytest.mark.parametrize("k", [-1, 0, 1, np.pi]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_rmul(A_c, k): - A = Matrix(A_c) - assert k * A == k * np.array(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_rmul(A, k): + np_array = np.array(A) + A = Matrix(A) + assert k * A == k * np_array -@pytest.mark.parametrize("A_c", test_matrices) +@pytest.mark.parametrize("A", test_matrices) @pytest.mark.parametrize("k", [-1, 1, np.pi, np.e]) -def test_matrix_truediv(A_c, k): - A = Matrix(A_c) +def test_matrix_truediv(A, k): + A = Matrix(A) assert A / k == np.array(A) / k -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_matmul_matrices(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Matrix(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_matmul_matrices(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Matrix(B) == expected_result -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) -def test_matrix_matmul_vectors(A_c, B_c): - expected_result = np.dot(A_c, B_c) - assert Matrix(A_c) @ Vector(B_c) == expected_result +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", [[1, 2, 3], [-np.pi, 1, np.e], [3 * 1j, -2j, 0j]]) +def test_matrix_matmul_vectors(A, B): + expected_result = np.dot(A, B) + assert Matrix(A) @ Vector(B) == expected_result @pytest.mark.parametrize("k", [0, 1, 2, 3, 4, 5]) -@pytest.mark.parametrize("A_c", test_matrices) -def test_matrix_pow(A_c, k): - A = Matrix(A_c) +@pytest.mark.parametrize("A", test_matrices) +def test_matrix_pow(A, k): + A = Matrix(A) assert A**k == np.linalg.matrix_power(A, k) @pytest.mark.parametrize("matrix_components", test_matrices) def test_matrix_eq(matrix_components): matrix = Matrix(matrix_components) - assert matrix == matrix assert matrix == matrix_components assert (matrix == 2 * matrix) is False @@ -191,10 +191,10 @@ def test_matrix_element_wise(matrix_components, operation): ) -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) -def test_matrix_dot(A_c, B_c): - A, B = Matrix(A_c), Matrix(B_c) +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) +def test_matrix_dot(A, B): + A, B = Matrix(A), Matrix(B) assert A.dot(B) == np.dot(A, B) diff --git a/tests/unit/test_tools_vector.py b/tests/unit/test_tools_vector.py index c9b617c97..f9ded7161 100644 --- a/tests/unit/test_tools_vector.py +++ b/tests/unit/test_tools_vector.py @@ -69,7 +69,7 @@ def test_vector_cross_matrix(vector_components): def test_vector_abs(vector_components): vector = Vector(vector_components) vector_magnitude = abs(vector) - assert vector_magnitude == sum([i**2 for i in vector_components]) ** 0.5 + assert vector_magnitude == sum(i**2 for i in vector_components) ** 0.5 @pytest.mark.parametrize("vector_components", test_vectors) @@ -199,12 +199,14 @@ def test_vector_proj(u_c, v_c): @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_str(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval("Vector(" + str(vector) + ")") == vector @pytest.mark.parametrize("vector_components", test_vectors) def test_vector_repr(vector_components): vector = Vector(vector_components) + # pylint: disable=eval-used assert eval(repr(vector).replace("(", "((").replace(")", "))")) == vector diff --git a/tests/unit/test_utilities.py b/tests/unit/test_utilities.py index 25bae57cf..a6d1972a7 100644 --- a/tests/unit/test_utilities.py +++ b/tests/unit/test_utilities.py @@ -1,4 +1,3 @@ -import csv from unittest.mock import patch import numpy as np @@ -21,7 +20,7 @@ (40, 21, 1.04, 0.2475236), ], ) -def test_compute_CdS_from_drop_test( +def test_compute_cd_s_from_drop_test( terminal_velocity, rocket_mass, air_density, result ): """Test if the function `compute_cd_s_from_drop_test` returns the correct @@ -45,42 +44,6 @@ def test_compute_CdS_from_drop_test( assert abs(cds - result) < 1e-6 -@pytest.mark.skip(reason="legacy tests") # it is not wokring -def test_create_dispersion_dictionary(): - """Test if the function returns a dictionary with the correct keys. - It reads the keys from the dictionary generated by the utilities function - and compares them to the expected. - Be careful if you change the "fixtures/monte_carlo/Valetudo_inputs.csv" file. - """ - - returned_dict = utilities.create_dispersion_dictionary( - "tests/fixtures/monte_carlo/Valetudo_inputs.csv" - ) - - test_dict = {} - with open("tests/fixtures/monte_carlo/Valetudo_inputs.csv", mode='r') as csvfile: - reader = csv.reader(csvfile, delimiter=';') - next(reader) # Skip header - for row in reader: - key, value, std_dev = row[1].strip(), row[2].strip(), row[3].strip() - if key: - if std_dev: - try: - test_dict[key] = (float(value), float(std_dev)) - except ValueError: - test_dict[key] = (value, std_dev) - else: - try: - test_dict[key] = float(value) - except ValueError: - try: - test_dict[key] = eval(value) - except SyntaxError: - test_dict[key] = value - - assert returned_dict == test_dict - - # Tests not passing in the CI, but passing locally due to # different values in the ubuntu and windows machines From b8ae06adec88a3f8b999370161715f2efed75661 Mon Sep 17 00:00:00 2001 From: Gui-FernandesBR Date: Sat, 6 Jul 2024 01:20:47 -0300 Subject: [PATCH 8/8] TST: Fix test --- tests/unit/test_tools_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_tools_matrix.py b/tests/unit/test_tools_matrix.py index f2b476fdc..89e75de0f 100644 --- a/tests/unit/test_tools_matrix.py +++ b/tests/unit/test_tools_matrix.py @@ -113,8 +113,8 @@ def test_matrix_neg(components): assert -Matrix(components) + Matrix(components) == Matrix.zeros() -@pytest.mark.parametrize("A_c", test_matrices) -@pytest.mark.parametrize("B_c", test_matrices) +@pytest.mark.parametrize("A", test_matrices) +@pytest.mark.parametrize("B", test_matrices) def test_matrix_add(A, B): expected_result = np.array(A) + np.array(B) assert Matrix(A) + Matrix(B) == expected_result