Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TST: new set of tests to the Flight class #408

Merged
merged 12 commits into from
Nov 3, 2023
102 changes: 102 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,43 @@ def calisto(calisto_motorless, cesaroni_m1670): # old name: rocket
return calisto


@pytest.fixture
def calisto_nose_to_tail(cesaroni_m1670):
"""Create a simple object of the Rocket class to be used in the tests. This
is the same as the calisto fixture, but with the coordinate system
orientation set to "nose_to_tail" instead of "tail_to_nose". This allows to
check if the coordinate system orientation is being handled correctly in
the code.

Parameters
----------
cesaroni_m1670 : rocketpy.SolidMotor
An object of the SolidMotor class. This is a pytest fixture too.

Returns
-------
rocketpy.Rocket
The Calisto rocket with the coordinate system orientation set to
"nose_to_tail". Rail buttons are already set, as well as the motor.
"""
calisto = Rocket(
radius=0.0635,
mass=14.426,
inertia=(6.321, 6.321, 0.034),
power_off_drag="data/calisto/powerOffDragCurve.csv",
power_on_drag="data/calisto/powerOnDragCurve.csv",
center_of_mass_without_motor=0,
coordinate_system_orientation="nose_to_tail",
)
calisto.add_motor(cesaroni_m1670, position=1.373)
calisto.set_rail_buttons(
upper_button_position=-0.082,
lower_button_position=0.618,
angular_position=45,
)
return calisto


@pytest.fixture
def calisto_liquid_modded(calisto_motorless, liquid_motor):
"""Create a simple object of the Rocket class to be used in the tests. This
Expand Down Expand Up @@ -735,6 +772,35 @@ def flight_calisto(calisto, example_env): # old name: flight
)


@pytest.fixture
def flight_calisto_nose_to_tail(calisto_nose_to_tail, example_env):
"""A rocketpy.Flight object of the Calisto rocket. This uses the calisto
with "nose_to_tail" coordinate system orientation, just as described in the
calisto_nose_to_tail fixture.

Parameters
----------
calisto_nose_to_tail : rocketpy.Rocket
An object of the Rocket class. This is a pytest fixture too.
example_env : rocketpy.Environment
An object of the Environment class. This is a pytest fixture too.

Returns
-------
rocketpy.Flight
The Calisto rocket with the coordinate system orientation set to
"nose_to_tail".
"""
return Flight(
environment=example_env,
rocket=calisto_nose_to_tail,
rail_length=5.2,
inclination=85,
heading=0,
terminate_on_apogee=False,
)


@pytest.fixture
def flight_calisto_robust(calisto_robust, example_env_robust):
"""A rocketpy.Flight object of the Calisto rocket. This uses the calisto
Expand Down Expand Up @@ -767,6 +833,42 @@ def flight_calisto_robust(calisto_robust, example_env_robust):
)


@pytest.fixture
def flight_calisto_custom_wind(calisto_robust, example_env_robust):
"""A rocketpy.Flight object of the Calisto rocket. This uses the calisto
with the aerodynamic surfaces and parachutes. The environment is a bit more
complex than the one in the flight_calisto_robust fixture. Now the wind is
set to 5m/s (x direction) and 2m/s (y direction), constant with altitude.

Parameters
----------
calisto_robust : rocketpy.Rocket
An object of the Rocket class. This is a pytest fixture too.
example_env_robust : rocketpy.Environment
An object of the Environment class. This is a pytest fixture too.

Returns
-------
rocketpy.Flight

"""
env = example_env_robust
env.set_atmospheric_model(
type="custom_atmosphere",
temperature=300,
wind_u=[(0, 5), (4000, 5)],
wind_v=[(0, 2), (4000, 2)],
)
return Flight(
environment=env,
rocket=calisto_robust,
rail_length=5.2,
inclination=85,
heading=0,
terminate_on_apogee=False,
)


## Dimensionless motors and rockets


Expand Down
261 changes: 261 additions & 0 deletions tests/test_flight.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def compute_static_margin_error_given_distance(position, static_margin, rocket):
return rocket


# Tests


@patch("matplotlib.pyplot.show")
def test_all_info(mock_show, flight_calisto_robust):
"""Test that the flight class is working as intended. This basically calls
Expand Down Expand Up @@ -656,3 +659,261 @@ def test_hybrid_motor_flight(mock_show, calisto_hybrid_modded):
)

assert test_flight.all_info() == None


def test_surface_wind(flight_calisto_custom_wind):
"""Tests the surface wind of the flight simulation. The expected values
are provided by the definition of the 'light_calisto_custom_wind' fixture.
If the fixture changes, this test must be updated.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info
regarding this pytest fixture.
"""
test = flight_calisto_custom_wind
atol = 1e-8
assert pytest.approx(2.0, abs=atol) == test.frontal_surface_wind
assert pytest.approx(-5.0, abs=atol) == test.lateral_surface_wind


def test_effective_rail_length(flight_calisto_robust, flight_calisto_nose_to_tail):
"""Tests the effective rail length of the flight simulation. The expected
values are calculated by hand, and should be valid as long as the rail
length and the position of the buttons and nozzle do not change in the
fixtures. If the fixtures change, this test must be updated. It is important
to keep

Parameters
----------
flight_calisto_robust : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info
regarding this pytest fixture.
flight_calisto_nose_to_tail : rocketpy.Flight
Flight object to be tested. The difference here is that the rocket is
defined with the "nose_to_tail" orientation instead of the
"tail_to_nose" orientation. See the conftest.py file for more info
regarding this pytest fixture.
"""
test1 = flight_calisto_robust
test2 = flight_calisto_nose_to_tail
atol = 1e-8

rail_length = 5.2
upper_button_position = 0.082
lower_button_position = -0.618
nozzle_position = -1.373

effective_1rl = rail_length - abs(upper_button_position - nozzle_position)
effective_2rl = rail_length - abs(lower_button_position - nozzle_position)

# test 1: Rocket with "tail_to_nose" orientation
assert pytest.approx(test1.effective_1rl, abs=atol) == effective_1rl
assert pytest.approx(test1.effective_2rl, abs=atol) == effective_2rl
# test 2: Rocket with "nose_to_tail" orientation
assert pytest.approx(test2.effective_1rl, abs=atol) == effective_1rl
assert pytest.approx(test2.effective_2rl, abs=atol) == effective_2rl
phmbressan marked this conversation as resolved.
Show resolved Hide resolved


def test_max_values(flight_calisto_robust):
"""Test the max values of the flight. This tests if the max values are
close to the expected values. However, the expected values were NOT
calculated by hand, it was just copied from the test results. This is
because the expected values are not easy to calculate by hand, and the
results are not expected to change. If the results change, the test will
fail, and the expected values must be updated. If if want to update the
values, always double check if the results are really correct. Acceptable
reasons for changes in the results are: 1) changes in the code that
improve the accuracy of the simulation, 2) a bug was found and fixed. Keep
in mind that other tests may be more accurate than this one, for example,
the acceptance tests, which are based on the results of real flights.

Parameters
----------
flight_calisto_robust : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info
regarding this pytest fixture.
"""
test = flight_calisto_robust
phmbressan marked this conversation as resolved.
Show resolved Hide resolved
atol = 5e-3
assert pytest.approx(105.2774, abs=atol) == test.max_acceleration_power_on
assert pytest.approx(105.2774, abs=atol) == test.max_acceleration
assert pytest.approx(0.85999, abs=atol) == test.max_mach_number
assert pytest.approx(285.90240, abs=atol) == test.max_speed


def test_rail_buttons_forces(flight_calisto_custom_wind):
"""Test the rail buttons forces. This tests if the rail buttons forces are
close to the expected values. However, the expected values were NOT
calculated by hand, it was just copied from the test results. The results
are not expected to change, unless the code is changed for bug fixes or
accuracy improvements.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info
regarding this pytest fixture.
"""
test = flight_calisto_custom_wind
atol = 5e-3
assert pytest.approx(3.80358, abs=atol) == test.max_rail_button1_normal_force
assert pytest.approx(1.63602, abs=atol) == test.max_rail_button1_shear_force
assert pytest.approx(1.19353, abs=atol) == test.max_rail_button2_normal_force
assert pytest.approx(0.51337, abs=atol) == test.max_rail_button2_shear_force


@pytest.mark.parametrize(
"flight_time, expected_values",
[
("t_initial", (0, 0, 0)),
("out_of_rail_time", (0, 7.8068, 89.2325)),
("apogee_time", (0.07534, -0.058127, -9.614386)),
("t_final", (0, 0, 0.0017346294117130806)),
],
)
def test_accelerations(flight_calisto_custom_wind, flight_time, expected_values):
"""Tests if the acceleration in some particular points of the trajectory is
correct. The expected values were NOT calculated by hand, it was just
copied from the test results. The results are not expected to change,
unless the code is changed for bug fixes or accuracy improvements.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info.
flight_time : str
The name of the attribute of the flight object that contains the time
of the point to be tested.
expected_values : tuple
The expected values of the acceleration vector at the point to be
tested.
"""
expected_attr, expected_acc = flight_time, expected_values

test = flight_calisto_custom_wind
t = getattr(test, expected_attr)
atol = 5e-3

assert pytest.approx(expected_acc, abs=atol) == (
test.ax(t),
test.ay(t),
test.az(t),
), f"Assertion error for acceleration vector at {expected_attr}."


@pytest.mark.parametrize(
"flight_time, expected_values",
[
("t_initial", (0, 0, 0)),
("out_of_rail_time", (0, 2.248727, 25.703072)),
("apogee_time", (-13.209436, 16.05115, -0.000257)),
("t_final", (5, 2, -5.334289)),
],
)
def test_velocities(flight_calisto_custom_wind, flight_time, expected_values):
"""Tests if the velocity in some particular points of the trajectory is
correct. The expected values were NOT calculated by hand, it was just
copied from the test results. The results are not expected to change,
unless the code is changed for bug fixes or accuracy improvements.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info.
flight_time : str
The name of the attribute of the flight object that contains the time
of the point to be tested.
expected_values : tuple
The expected values of the velocity vector at the point to be tested.
"""
expected_attr, expected_vel = flight_time, expected_values

test = flight_calisto_custom_wind
t = getattr(test, expected_attr)
atol = 5e-3

assert pytest.approx(expected_vel, abs=atol) == (
test.vx(t),
test.vy(t),
test.vz(t),
), f"Assertion error for velocity vector at {expected_attr}."


@pytest.mark.parametrize(
"flight_time, expected_values",
[
("t_initial", (1.6542528, 0.65918, -0.067107)),
("out_of_rail_time", (5.05334, 2.01364, -1.7541)),
("apogee_time", (2.35291, -1.8275, -0.87851)),
("t_final", (0, 0, 141.42421)),
],
)
def test_aerodynamic_forces(flight_calisto_custom_wind, flight_time, expected_values):
"""Tests if the aerodynamic forces in some particular points of the
trajectory is correct. The expected values were NOT calculated by hand, it
was just copied from the test results. The results are not expected to
change, unless the code is changed for bug fixes or accuracy improvements.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info.
flight_time : str
The name of the attribute of the flight object that contains the time
of the point to be tested.
expected_values : tuple
The expected values of the aerodynamic forces vector at the point to be
tested.
"""
expected_attr, expected_R = flight_time, expected_values

test = flight_calisto_custom_wind
t = getattr(test, expected_attr)
atol = 5e-3

assert pytest.approx(expected_R, abs=atol) == (
test.R1(t),
test.R2(t),
test.R3(t),
), f"Assertion error for aerodynamic forces vector at {expected_attr}."


@pytest.mark.parametrize(
"flight_time, expected_values",
[
("t_initial", (0.17179073815516033, -0.431117, 0)),
("out_of_rail_time", (0.547026, -1.3727895, 0)),
("apogee_time", (-0.5874848151271623, -0.7563596, 0)),
("t_final", (0, 0, 0)),
],
)
def test_aerodynamic_moments(flight_calisto_custom_wind, flight_time, expected_values):
"""Tests if the aerodynamic moments in some particular points of the
trajectory is correct. The expected values were NOT calculated by hand, it
was just copied from the test results. The results are not expected to
change, unless the code is changed for bug fixes or accuracy improvements.

Parameters
----------
flight_calisto_custom_wind : rocketpy.Flight
Flight object to be tested. See the conftest.py file for more info.
flight_time : str
The name of the attribute of the flight object that contains the time
of the point to be tested.
expected_values : tuple
The expected values of the aerodynamic moments vector at the point to
be tested.
"""
expected_attr, expected_M = flight_time, expected_values

test = flight_calisto_custom_wind
t = getattr(test, expected_attr)
atol = 5e-3

assert pytest.approx(expected_M, abs=atol) == (
test.M1(t),
test.M2(t),
test.M3(t),
), f"Assertion error for moment vector at {expected_attr}."
Loading