22
33import numpy as np
44
5- from rocketpy import Environment , Flight , Function , Rocket , SolidMotor
5+ from rocketpy import Flight , Function , Rocket
66
77
8- def test_flight_with_1d_drag (example_plain_env , calisto ):
8+ def test_flight_with_1d_drag (flight_calisto ):
99 """Test that flights with 1D drag curves still work (backward compatibility)."""
1010
11- flight = Flight (
12- rocket = calisto ,
13- environment = example_plain_env ,
14- rail_length = 5.2 ,
15- inclination = 85 ,
16- heading = 0 ,
17- )
11+ # `flight_calisto` is a fixture that already runs the simulation
12+ flight = flight_calisto
1813
1914 # Check that flight completed successfully
2015 assert flight .t_final > 0
2116 assert flight .apogee > 0
2217 assert flight .apogee_time > 0
2318
2419
25- def test_flight_with_3d_drag_basic ():
20+ def test_flight_with_3d_drag_basic (example_plain_env , cesaroni_m1670 ):
2621 """Test that a simple 3D drag function works."""
27- # Create environment
28- env = Environment ( gravity = 9.81 )
22+ # Use fixtures for environment and motor
23+ env = example_plain_env
2924 env .set_atmospheric_model (type = "standard_atmosphere" )
30-
31- # Create motor with simple constant thrust
32- motor = SolidMotor (
33- thrust_source = lambda t : 2000 if t < 3 else 0 ,
34- burn_time = 3.0 ,
35- nozzle_radius = 0.033 ,
36- dry_mass = 1.815 ,
37- dry_inertia = (0.125 , 0.125 , 0.002 ),
38- center_of_dry_mass_position = 0.317 ,
39- grains_center_of_mass_position = 0.397 ,
40- grain_number = 5 ,
41- grain_separation = 0.005 ,
42- grain_density = 1815 ,
43- grain_outer_radius = 0.033 ,
44- grain_initial_inner_radius = 0.015 ,
45- grain_initial_height = 0.120 ,
46- nozzle_position = 0 ,
47- throat_radius = 0.011 ,
48- )
25+ motor = cesaroni_m1670
4926
5027 # Create 3D drag
5128 mach = np .array ([0.0 , 0.5 , 1.0 , 1.5 , 2.0 ])
@@ -97,7 +74,7 @@ def test_flight_with_3d_drag_basic():
9774 assert hasattr (flight , "angle_of_attack" )
9875
9976
100- def test_3d_drag_with_varying_alpha ():
77+ def test_3d_drag_with_varying_alpha (example_plain_env , cesaroni_m1670 ):
10178 """Test that 3D drag responds to angle of attack changes."""
10279 # Create drag function with strong alpha dependency
10380 mach = np .array ([0.0 , 0.5 , 1.0 , 1.5 ])
@@ -116,11 +93,110 @@ def test_3d_drag_with_varying_alpha():
11693 outputs = "Cd" ,
11794 )
11895
119- # Test at different angles of attack
96+ # Test at different angles of attack (direct function call)
12097 # At zero alpha, Cd should be lower
12198 cd_0 = drag_func (0.8 , 5e5 , 0.0 )
12299 cd_10 = drag_func (0.8 , 5e5 , 10.0 )
123100
124101 # Cd should increase with alpha
125102 assert cd_10 > cd_0
126103 assert cd_10 - cd_0 > 0.2 # Should show significant difference
104+
105+ # --- Integration test: verify flight uses alpha-dependent Cd ---
106+ # Create a flat (alpha-agnostic) drag by averaging over alpha
107+ cd_flat = cd_data .mean (axis = 2 )
108+ drag_flat = Function .from_grid (
109+ cd_flat , [mach , reynolds ], inputs = ["Mach" , "Reynolds" ], outputs = "Cd" ,
110+ )
111+
112+ # Use fixtures for environment and motor
113+ env = example_plain_env
114+ env .set_atmospheric_model (type = "standard_atmosphere" )
115+ motor = cesaroni_m1670
116+
117+ # Build two rockets: one that uses alpha-sensitive drag, one flat
118+ rocket_alpha = Rocket (
119+ radius = 0.0635 ,
120+ mass = 16.24 ,
121+ inertia = (6.321 , 6.321 , 0.034 ),
122+ power_off_drag = drag_func ,
123+ power_on_drag = drag_func ,
124+ center_of_mass_without_motor = 0 ,
125+ coordinate_system_orientation = "tail_to_nose" ,
126+ )
127+ rocket_alpha .set_rail_buttons (0.2 , - 0.5 , 30 )
128+ rocket_alpha .add_motor (motor , position = - 1.255 )
129+
130+ rocket_flat = Rocket (
131+ radius = 0.0635 ,
132+ mass = 16.24 ,
133+ inertia = (6.321 , 6.321 , 0.034 ),
134+ power_off_drag = drag_flat ,
135+ power_on_drag = drag_flat ,
136+ center_of_mass_without_motor = 0 ,
137+ coordinate_system_orientation = "tail_to_nose" ,
138+ )
139+ rocket_flat .set_rail_buttons (0.2 , - 0.5 , 30 )
140+ rocket_flat .add_motor (motor , position = - 1.255 )
141+
142+ # Run flights
143+ flight_alpha = Flight (
144+ rocket = rocket_alpha ,
145+ environment = env ,
146+ rail_length = 5.2 ,
147+ inclination = 85 ,
148+ heading = 0 ,
149+ )
150+
151+ flight_flat = Flight (
152+ rocket = rocket_flat ,
153+ environment = env ,
154+ rail_length = 5.2 ,
155+ inclination = 85 ,
156+ heading = 0 ,
157+ )
158+
159+ # Flights should both launch
160+ assert flight_alpha .apogee > 100
161+ assert flight_flat .apogee > 100
162+
163+ # The two rockets should behave differently since one depends on alpha
164+ # while the other uses a flat-averaged Cd. Do not assume which direction
165+ # is larger (depends on encountered alpha vs averaged alpha) but ensure
166+ # the apogees differ.
167+ assert flight_alpha .apogee != flight_flat .apogee
168+
169+ # Additionally, sample Cd during flight from aerodynamic state and
170+ # compare values computed from each rocket's drag function at the
171+ # same time index to ensure alpha actually affects the evaluated Cd.
172+ # Use mid-ascent index (avoid t=0). Find a time index where speed > 5 m/s
173+ speeds = flight_alpha .free_stream_speed [:, 1 ]
174+ times = flight_alpha .time
175+ idx_candidates = np .where (speeds > 5 )[0 ]
176+ assert idx_candidates .size > 0
177+ idx = idx_candidates [len (idx_candidates ) // 2 ]
178+ t_sample = times [idx ]
179+
180+ mach_sample = flight_alpha .mach_number .get_value_opt (t_sample )
181+ rho_sample = flight_alpha .density .get_value_opt (t_sample )
182+ mu_sample = flight_alpha .dynamic_viscosity .get_value_opt (t_sample )
183+ V_sample = flight_alpha .free_stream_speed .get_value_opt (t_sample )
184+ reynolds_sample = rho_sample * V_sample * (2 * rocket_alpha .radius ) / mu_sample
185+ alpha_sample = flight_alpha .angle_of_attack .get_value_opt (t_sample )
186+
187+ cd_alpha_sample = rocket_alpha .power_on_drag .get_value_opt (
188+ mach_sample , reynolds_sample , alpha_sample
189+ )
190+ cd_flat_sample = rocket_flat .power_on_drag .get_value_opt (
191+ mach_sample , reynolds_sample
192+ )
193+
194+ # Alpha-sensitive Cd should differ from the flat Cd at the sampled time
195+ assert cd_alpha_sample != cd_flat_sample
196+
197+ # Ensure the sign of the Cd difference is consistent with the apogee
198+ # ordering: larger Cd -> lower apogee
199+ if cd_alpha_sample > cd_flat_sample :
200+ assert flight_alpha .apogee < flight_flat .apogee
201+ else :
202+ assert flight_alpha .apogee > flight_flat .apogee
0 commit comments