@@ -593,7 +593,6 @@ def __init__(
593
593
if self .rail_length <= 0 :
594
594
raise ValueError ("Rail length must be a positive value." )
595
595
self .parachutes = self .rocket .parachutes [:]
596
- self ._controllers = self .rocket ._controllers [:]
597
596
self .inclination = inclination
598
597
self .heading = heading
599
598
self .max_time = max_time
@@ -607,6 +606,13 @@ def __init__(
607
606
self .name = name
608
607
self .equations_of_motion = equations_of_motion
609
608
609
+ # Controller initialization
610
+ self ._controllers = self .rocket ._controllers [:]
611
+ if self ._controllers :
612
+ # reset controllable object to initial state (only airbrakes for now)
613
+ for air_brakes in self .rocket .air_brakes :
614
+ air_brakes .reset ()
615
+
610
616
# Flight initialization
611
617
self .__init_post_process_variables ()
612
618
self .__init_solution_monitors ()
@@ -1076,8 +1082,14 @@ def __init__(
1076
1082
[self .t , parachute ]
1077
1083
)
1078
1084
1085
+ # If controlled flight, post process must be done on sim time
1086
+ if self ._controllers :
1087
+ phase .derivative (self .t , self .y_sol , post_processing = True )
1088
+
1079
1089
self .t_final = self .t
1080
1090
self ._calculate_pressure_signal ()
1091
+ if self ._controllers :
1092
+ self .__cache_post_process_variables ()
1081
1093
if verbose :
1082
1094
print ("Simulation Completed at Time: {:3.4f} s" .format (self .t ))
1083
1095
@@ -1089,6 +1101,25 @@ def __init_post_process_variables(self):
1089
1101
self ._bearing = Function (0 )
1090
1102
self ._latitude = Function (0 )
1091
1103
self ._longitude = Function (0 )
1104
+ # Initialize state derivatives, force and atmospheric arrays
1105
+ self .ax_list = []
1106
+ self .ay_list = []
1107
+ self .az_list = []
1108
+ self .alpha1_list = []
1109
+ self .alpha2_list = []
1110
+ self .alpha3_list = []
1111
+ self .R1_list = []
1112
+ self .R2_list = []
1113
+ self .R3_list = []
1114
+ self .M1_list = []
1115
+ self .M2_list = []
1116
+ self .M3_list = []
1117
+ self .pressure_list = []
1118
+ self .density_list = []
1119
+ self .dynamic_viscosity_list = []
1120
+ self .speed_of_sound_list = []
1121
+ self .wind_velocity_x_list = []
1122
+ self .wind_velocity_y_list = []
1092
1123
1093
1124
def __init_solution_monitors (self ):
1094
1125
# Initialize solution monitors
@@ -1181,10 +1212,28 @@ def __init_equations_of_motion(self):
1181
1212
if self .equations_of_motion == "solid_propulsion" :
1182
1213
self .u_dot_generalized = self .u_dot
1183
1214
1184
- def __init_equations_of_motion (self ):
1185
- """Initialize equations of motion."""
1186
- if self .equations_of_motion == "solid_propulsion" :
1187
- self .u_dot_generalized = self .u_dot
1215
+ def __cache_post_process_variables (self ):
1216
+ """Cache post-process variables for simulations with controllers."""
1217
+ self .__retrieve_arrays = [
1218
+ self .ax_list ,
1219
+ self .ay_list ,
1220
+ self .az_list ,
1221
+ self .alpha1_list ,
1222
+ self .alpha2_list ,
1223
+ self .alpha3_list ,
1224
+ self .R1_list ,
1225
+ self .R2_list ,
1226
+ self .R3_list ,
1227
+ self .M1_list ,
1228
+ self .M2_list ,
1229
+ self .M3_list ,
1230
+ self .pressure_list ,
1231
+ self .density_list ,
1232
+ self .dynamic_viscosity_list ,
1233
+ self .speed_of_sound_list ,
1234
+ self .wind_velocity_x_list ,
1235
+ self .wind_velocity_y_list ,
1236
+ ]
1188
1237
1189
1238
@cached_property
1190
1239
def effective_1rl (self ):
@@ -1261,11 +1310,6 @@ def udot_rail1(self, t, u, post_processing=False):
1261
1310
e0dot, e1dot, e2dot, e3dot, alpha1, alpha2, alpha3].
1262
1311
1263
1312
"""
1264
- # Check if post processing mode is on
1265
- if post_processing :
1266
- # Use u_dot post processing code
1267
- return self .u_dot_generalized (t , u , True )
1268
-
1269
1313
# Retrieve integration data
1270
1314
x , y , z , vx , vy , vz , e0 , e1 , e2 , e3 , omega1 , omega2 , omega3 = u
1271
1315
@@ -1296,6 +1340,17 @@ def udot_rail1(self, t, u, post_processing=False):
1296
1340
else :
1297
1341
ax , ay , az = 0 , 0 , 0
1298
1342
1343
+ if post_processing :
1344
+ # Use u_dot post processing code for forces, moments and env data
1345
+ self .u_dot_generalized (t , u , post_processing = True )
1346
+ # Save feasible accelerations
1347
+ self .ax_list [- 1 ] = [t , ax ]
1348
+ self .ay_list [- 1 ] = [t , ay ]
1349
+ self .az_list [- 1 ] = [t , az ]
1350
+ self .alpha1_list [- 1 ] = [t , 0 ]
1351
+ self .alpha2_list [- 1 ] = [t , 0 ]
1352
+ self .alpha3_list [- 1 ] = [t , 0 ]
1353
+
1299
1354
return [vx , vy , vz , ax , ay , az , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
1300
1355
1301
1356
def udot_rail2 (self , t , u , post_processing = False ):
@@ -1585,6 +1640,13 @@ def u_dot(self, t, u, post_processing=False):
1585
1640
]
1586
1641
1587
1642
if post_processing :
1643
+ # Accelerations
1644
+ self .ax_list .append ([t , ax ])
1645
+ self .ay_list .append ([t , ay ])
1646
+ self .az_list .append ([t , az ])
1647
+ self .alpha1_list .append ([t , alpha1 ])
1648
+ self .alpha2_list .append ([t , alpha2 ])
1649
+ self .alpha3_list .append ([t , alpha3 ])
1588
1650
# Dynamics variables
1589
1651
self .R1_list .append ([t , R1 ])
1590
1652
self .R2_list .append ([t , R2 ])
@@ -1860,6 +1922,13 @@ def u_dot_generalized(self, t, u, post_processing=False):
1860
1922
u_dot = [* r_dot , * v_dot , * e_dot , * w_dot ]
1861
1923
1862
1924
if post_processing :
1925
+ # Accelerations
1926
+ self .ax_list .append ([t , v_dot [0 ]])
1927
+ self .ay_list .append ([t , v_dot [1 ]])
1928
+ self .az_list .append ([t , v_dot [2 ]])
1929
+ self .alpha1_list .append ([t , w_dot [0 ]])
1930
+ self .alpha2_list .append ([t , w_dot [1 ]])
1931
+ self .alpha3_list .append ([t , w_dot [2 ]])
1863
1932
# Dynamics variables
1864
1933
self .R1_list .append ([t , R1 ])
1865
1934
self .R2_list .append ([t , R2 ])
@@ -1944,6 +2013,13 @@ def u_dot_parachute(self, t, u, post_processing=False):
1944
2013
az = (Dz - 9.8 * mp ) / (mp + ma )
1945
2014
1946
2015
if post_processing :
2016
+ # Accelerations
2017
+ self .ax_list .append ([t , ax ])
2018
+ self .ay_list .append ([t , ay ])
2019
+ self .az_list .append ([t , az ])
2020
+ self .alpha1_list .append ([t , 0 ])
2021
+ self .alpha2_list .append ([t , 0 ])
2022
+ self .alpha3_list .append ([t , 0 ])
1947
2023
# Dynamics variables
1948
2024
self .R1_list .append ([t , Dx ])
1949
2025
self .R2_list .append ([t , Dy ])
@@ -1952,13 +2028,20 @@ def u_dot_parachute(self, t, u, post_processing=False):
1952
2028
self .M2_list .append ([t , 0 ])
1953
2029
self .M3_list .append ([t , 0 ])
1954
2030
# Atmospheric Conditions
1955
- self .wind_velocity_x_list .append ([t , self .env .wind_velocity_x (z )])
1956
- self .wind_velocity_y_list .append ([t , self .env .wind_velocity_y (z )])
1957
- self .density_list .append ([t , self .env .density (z )])
1958
- self .dynamic_viscosity_list .append ([t , self .env .dynamic_viscosity (z )])
1959
- self .pressure_list .append ([t , self .env .pressure (z )])
1960
- self .speed_of_sound_list .append ([t , self .env .speed_of_sound (z )])
1961
-
2031
+ self .wind_velocity_x_list .append (
2032
+ [t , self .env .wind_velocity_x .get_value_opt (z )]
2033
+ )
2034
+ self .wind_velocity_y_list .append (
2035
+ [t , self .env .wind_velocity_y .get_value_opt (z )]
2036
+ )
2037
+ self .density_list .append ([t , self .env .density .get_value_opt (z )])
2038
+ self .dynamic_viscosity_list .append (
2039
+ [t , self .env .dynamic_viscosity .get_value_opt (z )]
2040
+ )
2041
+ self .pressure_list .append ([t , self .env .pressure .get_value_opt (z )])
2042
+ self .speed_of_sound_list .append (
2043
+ [t , self .env .speed_of_sound .get_value_opt (z )]
2044
+ )
1962
2045
return [vx , vy , vz , ax , ay , az , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
1963
2046
1964
2047
@cached_property
@@ -2790,13 +2873,64 @@ def longitude(self):
2790
2873
2791
2874
return np .column_stack ((self .time , longitude ))
2792
2875
2876
+ @cached_property
2877
+ def __retrieve_arrays (self ):
2878
+ """post processing function to retrieve arrays from the integration
2879
+ scheme and store them in lists for further analysis.
2880
+
2881
+ Returns
2882
+ -------
2883
+ temp_values: list
2884
+ List containing the following arrays: ``ax`` , ``ay`` , ``az`` ,
2885
+ ``alpha1`` , ``alpha2`` , ``alpha3`` , ``R1`` , ``R2`` , ``R3`` ,
2886
+ ``M1`` , ``M2`` , ``M3`` , ``pressure`` , ``density`` ,
2887
+ ``dynamic_viscosity`` , ``speed_of_sound`` , ``wind_velocity_x`` ,
2888
+ ``wind_velocity_y``.
2889
+ """
2890
+ # Go through each time step and calculate forces and atmospheric values
2891
+ # Get flight phases
2892
+ for phase_index , phase in self .time_iterator (self .FlightPhases ):
2893
+ init_time = phase .t
2894
+ final_time = self .FlightPhases [phase_index + 1 ].t
2895
+ current_derivative = phase .derivative
2896
+ # Call callback functions
2897
+ for callback in phase .callbacks :
2898
+ callback (self )
2899
+ # Loop through time steps in flight phase
2900
+ for step in self .solution : # Can be optimized
2901
+ if init_time < step [0 ] <= final_time or (
2902
+ init_time == self .t_initial and step [0 ] == self .t_initial
2903
+ ):
2904
+ # Call derivatives in post processing mode
2905
+ current_derivative (step [0 ], step [1 :], post_processing = True )
2906
+
2907
+ temp_values = [
2908
+ self .ax_list ,
2909
+ self .ay_list ,
2910
+ self .az_list ,
2911
+ self .alpha1_list ,
2912
+ self .alpha2_list ,
2913
+ self .alpha3_list ,
2914
+ self .R1_list ,
2915
+ self .R2_list ,
2916
+ self .R3_list ,
2917
+ self .M1_list ,
2918
+ self .M2_list ,
2919
+ self .M3_list ,
2920
+ self .pressure_list ,
2921
+ self .density_list ,
2922
+ self .dynamic_viscosity_list ,
2923
+ self .speed_of_sound_list ,
2924
+ self .wind_velocity_x_list ,
2925
+ self .wind_velocity_y_list ,
2926
+ ]
2927
+
2928
+ return temp_values
2929
+
2793
2930
@cached_property
2794
2931
def retrieve_acceleration_arrays (self ):
2795
2932
"""Retrieve acceleration arrays from the integration scheme
2796
2933
2797
- Parameters
2798
- ----------
2799
-
2800
2934
Returns
2801
2935
-------
2802
2936
ax: list
@@ -2812,35 +2946,7 @@ def retrieve_acceleration_arrays(self):
2812
2946
alpha3: list
2813
2947
angular acceleration in z direction
2814
2948
"""
2815
- # Initialize acceleration arrays
2816
- ax , ay , az = [[0 , 0 ]], [[0 , 0 ]], [[0 , 0 ]]
2817
- alpha1 , alpha2 , alpha3 = [[0 , 0 ]], [[0 , 0 ]], [[0 , 0 ]]
2818
- # Go through each time step and calculate accelerations
2819
- # Get flight phases
2820
- for phase_index , phase in self .time_iterator (self .FlightPhases ):
2821
- init_time = phase .t
2822
- final_time = self .FlightPhases [phase_index + 1 ].t
2823
- current_derivative = phase .derivative
2824
- # Call callback functions
2825
- for callback in phase .callbacks :
2826
- callback (self )
2827
- # Loop through time steps in flight phase
2828
- for step in self .solution : # Can be optimized
2829
- if init_time < step [0 ] <= final_time :
2830
- # Get derivatives
2831
- u_dot = current_derivative (step [0 ], step [1 :])
2832
- # Get accelerations
2833
- ax_value , ay_value , az_value = u_dot [3 :6 ]
2834
- alpha1_value , alpha2_value , alpha3_value = u_dot [10 :]
2835
- # Save accelerations
2836
- ax .append ([step [0 ], ax_value ])
2837
- ay .append ([step [0 ], ay_value ])
2838
- az .append ([step [0 ], az_value ])
2839
- alpha1 .append ([step [0 ], alpha1_value ])
2840
- alpha2 .append ([step [0 ], alpha2_value ])
2841
- alpha3 .append ([step [0 ], alpha3_value ])
2842
-
2843
- return ax , ay , az , alpha1 , alpha2 , alpha3
2949
+ return self .__retrieve_arrays [:6 ]
2844
2950
2845
2951
@cached_property
2846
2952
def retrieve_temporary_values_arrays (self ):
@@ -2877,54 +2983,7 @@ def retrieve_temporary_values_arrays(self):
2877
2983
self.wind_velocity_y_list: list
2878
2984
Wind velocity in y direction at each time step.
2879
2985
"""
2880
-
2881
- # Initialize force and atmospheric arrays
2882
- self .R1_list = []
2883
- self .R2_list = []
2884
- self .R3_list = []
2885
- self .M1_list = []
2886
- self .M2_list = []
2887
- self .M3_list = []
2888
- self .pressure_list = []
2889
- self .density_list = []
2890
- self .dynamic_viscosity_list = []
2891
- self .speed_of_sound_list = []
2892
- self .wind_velocity_x_list = []
2893
- self .wind_velocity_y_list = []
2894
-
2895
- # Go through each time step and calculate forces and atmospheric values
2896
- # Get flight phases
2897
- for phase_index , phase in self .time_iterator (self .FlightPhases ):
2898
- init_time = phase .t
2899
- final_time = self .FlightPhases [phase_index + 1 ].t
2900
- current_derivative = phase .derivative
2901
- # Call callback functions
2902
- for callback in phase .callbacks :
2903
- callback (self )
2904
- # Loop through time steps in flight phase
2905
- for step in self .solution : # Can be optimized
2906
- if init_time < step [0 ] <= final_time or (
2907
- init_time == self .t_initial and step [0 ] == self .t_initial
2908
- ):
2909
- # Call derivatives in post processing mode
2910
- u_dot = current_derivative (step [0 ], step [1 :], post_processing = True )
2911
-
2912
- temporary_values = [
2913
- self .R1_list ,
2914
- self .R2_list ,
2915
- self .R3_list ,
2916
- self .M1_list ,
2917
- self .M2_list ,
2918
- self .M3_list ,
2919
- self .pressure_list ,
2920
- self .density_list ,
2921
- self .dynamic_viscosity_list ,
2922
- self .speed_of_sound_list ,
2923
- self .wind_velocity_x_list ,
2924
- self .wind_velocity_y_list ,
2925
- ]
2926
-
2927
- return temporary_values
2986
+ return self .__retrieve_arrays [6 :]
2928
2987
2929
2988
def get_controller_observed_variables (self ):
2930
2989
"""Retrieve the observed variables related to air brakes from the
0 commit comments