44import warnings
55from copy import deepcopy
66from functools import cached_property
7-
87import numpy as np
98import simplekml
109from scipy .integrate import BDF , DOP853 , LSODA , RK23 , RK45 , OdeSolver , Radau
11-
1210from ..mathutils .function import Function , funcify_method
1311from ..mathutils .vector_matrix import Matrix , Vector
1412from ..plots .flight_plots import _FlightPlots
@@ -1010,8 +1008,7 @@ def __simulate(self, verbose):
10101008 i += 1
10111009 # Create flight phase for time after inflation
10121010 callbacks = [
1013- lambda self ,
1014- parachute_cd_s = parachute .cd_s : setattr (
1011+ lambda self , parachute_cd_s = parachute .cd_s : setattr (
10151012 self , "parachute_cd_s" , parachute_cd_s
10161013 )
10171014 ]
@@ -1404,7 +1401,9 @@ def udot_rail2(self, t, u, post_processing=False): # pragma: no cover
14041401 # Hey! We will finish this function later, now we just can use u_dot
14051402 return self .u_dot_generalized (t , u , post_processing = post_processing )
14061403
1407- def u_dot (self , t , u , post_processing = False ): # pylint: disable=too-many-locals,too-many-statements
1404+ def u_dot (
1405+ self , t , u , post_processing = False
1406+ ): # pylint: disable=too-many-locals,too-many-statements
14081407 """Calculates derivative of u state vector with respect to time
14091408 when rocket is flying in 6 DOF motion during ascent out of rail
14101409 and descent without parachute.
@@ -1718,173 +1717,210 @@ def u_dot(self, t, u, post_processing=False): # pylint: disable=too-many-locals
17181717
17191718 return u_dot
17201719
1721- # Dans flight.py, dans la classe Flight
1722-
17231720 def u_dot_generalized (self , t , u , post_processing = False ):
17241721 """
1725- Calcule la dérivée du vecteur d'état u (6-DOF) en utilisant un formalisme généralisé
1726- adapté pour les clusters moteurs et incluant toutes les forces physiques.
1722+ Calculates the derivative of the state vector u (6-DOF) using a
1723+ generalized formalism suitable for motor clusters and including all
1724+ physical forces.
17271725 """
1728- # Extraction des variables d'état
1729- x , y , z , vx , vy , vz , e0 , e1 , e2 , e3 , w1 , w2 , w3 = u
1726+ # State variable extraction
1727+ _ , _ , z , vx , vy , vz , e0 , e1 , e2 , e3 , w1 , w2 , w3 = u
17301728
1731- # Création des vecteurs pour les calculs
1729+ # Create vectors for calculations
17321730 velocity_vector = Vector ([vx , vy , vz ])
17331731 euler_params = [e0 , e1 , e2 , e3 ]
17341732 angular_velocity_vector = Vector ([w1 , w2 , w3 ])
17351733 w = angular_velocity_vector
17361734
1737- # Propriétés de la fusée à l'instant t
1735+ # Rocket properties at time t
17381736 total_mass = self .rocket .total_mass .get_value_opt (t )
1739-
1740- # Matrice de transformation du repère corps au repère inertiel
1737+
1738+ # Transformation matrix from body frame to inertial frame
17411739 K = Matrix .transformation (euler_params )
17421740 Kt = K .transpose
17431741
1744- # --- Calcul des forces et moments ---
1745-
1746- # Gravité dans le repère corps
1747- gravity_force_inertial = Vector ([0 , 0 , - total_mass * self .env .gravity .get_value_opt (z )])
1742+ # --- Forces and Moments Calculation ---
1743+
1744+ # Gravity in body frame
1745+ gravity_force_inertial = Vector (
1746+ [0 , 0 , - total_mass * self .env .gravity .get_value_opt (z )]
1747+ )
17481748 gravity_force_body = Kt @ gravity_force_inertial
17491749
1750- # --- DÉBUT DU BLOC DE CALCUL AÉRODYNAMIQUE INTÉGRÉ ---
1750+ # --- START OF INTEGRATED AERODYNAMIC CALCULATION BLOCK ---
17511751 R1 , R2 , R3 , M1 , M2 , M3 = 0 , 0 , 0 , 0 , 0 , 0
1752-
1752+
17531753 # Get freestream speed
17541754 wind_velocity_x = self .env .wind_velocity_x .get_value_opt (z )
17551755 wind_velocity_y = self .env .wind_velocity_y .get_value_opt (z )
17561756 speed_of_sound = self .env .speed_of_sound .get_value_opt (z )
17571757 rho = self .env .density .get_value_opt (z )
17581758
1759- # Vitesse relative au vent dans le repère inertiel
1760- stream_velocity_inertial = Vector ([wind_velocity_x - vx , wind_velocity_y - vy , - vz ])
1759+ # Relative wind speed in inertial frame
1760+ stream_velocity_inertial = Vector (
1761+ [wind_velocity_x - vx , wind_velocity_y - vy , - vz ]
1762+ )
17611763 free_stream_speed = abs (stream_velocity_inertial )
17621764 free_stream_mach = free_stream_speed / speed_of_sound
1763-
1764- # Déterminer la traînée de base de la fusée
1765+
1766+ # Determine the base drag of the rocket
17651767 if t < self .rocket .motor .burn_out_time :
17661768 drag_coeff = self .rocket .power_on_drag .get_value_opt (free_stream_mach )
17671769 else :
17681770 drag_coeff = self .rocket .power_off_drag .get_value_opt (free_stream_mach )
1769-
1770- R3_base_drag = - 0.5 * rho * (free_stream_speed ** 2 ) * self .rocket .area * drag_coeff
1771+
1772+ R3_base_drag = (
1773+ - 0.5 * rho * (free_stream_speed ** 2 ) * self .rocket .area * drag_coeff
1774+ )
17711775 R3 += R3_base_drag
17721776
1773- # Ajouter la traînée des aérofreins si actifs
1777+ # Add air brakes drag if active
17741778 for air_brakes in self .rocket .air_brakes :
17751779 if air_brakes .deployment_level > 0 :
1776- air_brakes_cd = air_brakes .drag_coefficient .get_value_opt (air_brakes .deployment_level , free_stream_mach )
1777- air_brakes_force = - 0.5 * rho * (free_stream_speed ** 2 ) * air_brakes .reference_area * air_brakes_cd
1780+ air_brakes_cd = air_brakes .drag_coefficient .get_value_opt (
1781+ air_brakes .deployment_level , free_stream_mach
1782+ )
1783+ air_brakes_force = (
1784+ - 0.5
1785+ * rho
1786+ * (free_stream_speed ** 2 )
1787+ * air_brakes .reference_area
1788+ * air_brakes_cd
1789+ )
17781790 if air_brakes .override_rocket_drag :
17791791 R3 = air_brakes_force
17801792 else :
17811793 R3 += air_brakes_force
1782-
1783- # Moment dû à l'excentricité du CdP
1794+
1795+ # Moment due to CoP eccentricity
17841796 M1 += self .rocket .cp_eccentricity_y * R3
17851797 M2 -= self .rocket .cp_eccentricity_x * R3
17861798
1787- # Vitesse de la fusée dans le repère corps
1799+ # Rocket velocity in body frame
17881800 velocity_body_frame = Kt @ velocity_vector
1789-
1790- # Calculer la portance et le moment pour chaque surface aérodynamique
1791- for aero_surface , position in self .rocket .aerodynamic_surfaces :
1801+
1802+ # Calculate lift and moment for each aerodynamic surface
1803+ for aero_surface , _ in self .rocket .aerodynamic_surfaces :
17921804 comp_cp = self .rocket .surfaces_cp_to_cdm [aero_surface ]
1793- # Vitesse absolue du composant dans le repère corps
1805+ # Component absolute velocity in body frame
17941806 comp_vb = velocity_body_frame + (w ^ comp_cp )
1795- # Vitesse du vent à l' altitude du composant
1807+ # Wind velocity at component altitude
17961808 comp_z = z + (K @ comp_cp ).z
17971809 comp_wind_vx = self .env .wind_velocity_x .get_value_opt (comp_z )
17981810 comp_wind_vy = self .env .wind_velocity_y .get_value_opt (comp_z )
1799- # Vitesse du vent dans le repère corps
1811+ # Wind velocity in body frame
18001812 comp_wind_vb = Kt @ Vector ([comp_wind_vx , comp_wind_vy , 0 ])
18011813 comp_stream_velocity = comp_wind_vb - comp_vb
18021814 comp_stream_speed = abs (comp_stream_velocity )
18031815 comp_stream_mach = comp_stream_speed / speed_of_sound
1804-
1816+
18051817 comp_reynolds = (
18061818 self .env .density .get_value_opt (comp_z )
18071819 * comp_stream_speed
18081820 * aero_surface .reference_length
18091821 / self .env .dynamic_viscosity .get_value_opt (comp_z )
18101822 )
18111823
1812- # Calcul des forces et moments pour la surface
1824+ # Calculate forces and moments for the surface
18131825 X , Y , Z , M , N , L = aero_surface .compute_forces_and_moments (
1814- comp_stream_velocity , comp_stream_speed , comp_stream_mach , rho , comp_cp , w , comp_reynolds
1826+ comp_stream_velocity ,
1827+ comp_stream_speed ,
1828+ comp_stream_mach ,
1829+ rho ,
1830+ comp_cp ,
1831+ w ,
1832+ comp_reynolds ,
18151833 )
18161834 R1 += X
18171835 R2 += Y
18181836 R3 += Z
18191837 M1 += M
18201838 M2 += N
18211839 M3 += L
1822-
1823- # Moment dû à l'excentricité du CdP
1840+
1841+ # Moment due to CoP eccentricity
18241842 M3 += self .rocket .cp_eccentricity_x * R2 - self .rocket .cp_eccentricity_y * R1
1825-
1826- # Création des vecteurs finaux pour les forces et moments aéro
1843+
1844+ # Create final vectors for aero forces and moments
18271845 R_aero_body = Vector ([R1 , R2 , R3 ])
18281846 M_aero_body = Vector ([M1 , M2 , M3 ])
1829- # --- FIN DU BLOC DE CALCUL AÉRODYNAMIQUE ---
1847+ # --- END OF AERODYNAMIC CALCULATION BLOCK ---
18301848
1831- # Poussée et moment du cluster/moteur
1832- if hasattr (self .rocket .motor , ' get_total_thrust_vector' ): # C'est un ClusterMotor
1849+ # Thrust and moment from cluster/motor
1850+ if hasattr (self .rocket .motor , " get_total_thrust_vector" ): # It's a ClusterMotor
18331851 thrust_vector_body = self .rocket .motor .get_total_thrust_vector (t )
18341852 rocket_cm = self .rocket .center_of_mass .get_value_opt (t )
18351853 moment_vector_body = self .rocket .motor .get_total_moment (t , rocket_cm )
18361854 net_thrust_scalar = abs (thrust_vector_body )
1837- else : # C'est un moteur standard
1855+ else : # It's a standard motor
18381856 pressure = self .env .pressure .get_value_opt (z )
1839- net_thrust_scalar = max (self .rocket .motor .thrust .get_value_opt (t ) + self .rocket .motor .pressure_thrust (pressure ), 0 )
1840- thrust_vector_body = Vector ([0 , 0 , net_thrust_scalar ]) # Poussée axiale
1841- moment_vector_body = Vector ([ # Moment dû à l'excentricité
1842- self .rocket .thrust_eccentricity_y * net_thrust_scalar ,
1843- - self .rocket .thrust_eccentricity_x * net_thrust_scalar ,
1844- 0
1845- ])
1846-
1847- # Force et moment totaux dans le repère corps
1857+ net_thrust_scalar = max (
1858+ self .rocket .motor .thrust .get_value_opt (t )
1859+ + self .rocket .motor .pressure_thrust (pressure ),
1860+ 0 ,
1861+ )
1862+ thrust_vector_body = Vector ([0 , 0 , net_thrust_scalar ]) # Axial thrust
1863+ moment_vector_body = Vector (
1864+ [ # Moment due to eccentricity
1865+ self .rocket .thrust_eccentricity_y * net_thrust_scalar ,
1866+ - self .rocket .thrust_eccentricity_x * net_thrust_scalar ,
1867+ 0 ,
1868+ ]
1869+ )
1870+
1871+ # Total force and moment in body frame
18481872 total_force_body = gravity_force_body + thrust_vector_body + R_aero_body
18491873 total_moment_body = moment_vector_body + M_aero_body
18501874
1851- # --- Équations du mouvement ---
1875+ # --- Equations of Motion ---
18521876
1853- # Accélération linéaire
1877+ # Linear acceleration
18541878 linear_acceleration_body = total_force_body / total_mass
18551879 linear_acceleration_inertial = K @ linear_acceleration_body
1856-
1857- # Accélération angulaire (Équation d' Euler pour corps rigide à masse variable)
1880+
1881+ # Angular acceleration ( Euler's equation for rigid body with variable mass )
18581882 inertia_tensor = self .rocket .get_inertia_tensor_at_time (t )
18591883 I_w = inertia_tensor @ angular_velocity_vector
18601884 w_cross_Iw = angular_velocity_vector .cross (I_w )
18611885 I_dot = self .rocket .get_inertia_tensor_derivative_at_time (t )
1862-
1863- angular_acceleration_body = inertia_tensor .inverse @ (total_moment_body - w_cross_Iw - (I_dot @ angular_velocity_vector ))
1864-
1865- # Dérivées des paramètres d'Euler
1866- e0dot = 0.5 * (- w1 * e1 - w2 * e2 - w3 * e3 )
1867- e1dot = 0.5 * ( w1 * e0 + w3 * e2 - w2 * e3 )
1868- e2dot = 0.5 * ( w2 * e0 - w3 * e1 + w1 * e3 )
1869- e3dot = 0.5 * ( w3 * e0 + w2 * e1 - w1 * e2 )
1870-
1871- # Assemblage du vecteur dérivé
1886+
1887+ angular_acceleration_body = inertia_tensor .inverse @ (
1888+ total_moment_body - w_cross_Iw - (I_dot @ angular_velocity_vector )
1889+ )
1890+
1891+ # Euler parameters derivatives
1892+ e0dot = 0.5 * (- w1 * e1 - w2 * e2 - w3 * e3 )
1893+ e1dot = 0.5 * (w1 * e0 + w3 * e2 - w2 * e3 )
1894+ e2dot = 0.5 * (w2 * e0 - w3 * e1 + w1 * e3 )
1895+ e3dot = 0.5 * (w3 * e0 + w2 * e1 - w1 * e2 )
1896+
1897+ # Assemble the derivative vector
18721898 u_dot = [
1873- vx , vy , vz ,
1899+ vx ,
1900+ vy ,
1901+ vz ,
18741902 * linear_acceleration_inertial ,
1875- e0dot , e1dot , e2dot , e3dot ,
1876- * angular_acceleration_body
1903+ e0dot ,
1904+ e1dot ,
1905+ e2dot ,
1906+ e3dot ,
1907+ * angular_acceleration_body ,
18771908 ]
1878-
1879- # Post-processing (si nécessaire )
1909+
1910+ # Post-processing (if necessary )
18801911 if post_processing :
1881- self .__post_processed_variables .append ([
1882- t , * linear_acceleration_inertial , * angular_acceleration_body ,
1883- * R_aero_body , * total_moment_body , net_thrust_scalar
1884- ])
1885-
1886- return u_dot
1912+ self .__post_processed_variables .append (
1913+ [
1914+ t ,
1915+ * linear_acceleration_inertial ,
1916+ * angular_acceleration_body ,
1917+ * R_aero_body ,
1918+ * total_moment_body ,
1919+ net_thrust_scalar ,
1920+ ]
1921+ )
18871922
1923+ return u_dot
18881924
18891925 def u_dot_parachute (self , t , u , post_processing = False ):
18901926 """Calculates derivative of u state vector with respect to time
@@ -1924,8 +1960,8 @@ def u_dot_parachute(self, t, u, post_processing=False):
19241960 mp = self .rocket .dry_mass
19251961
19261962 # Define constants
1927- ka = 1 # Added mass coefficient (depends on parachute's porosity)
1928- R = 1.5 # Parachute radius
1963+ # ka = 1 # Added mass coefficient (depends on parachute's porosity)
1964+ # R = 1.5 # Parachute radius
19291965 # to = 1.2
19301966 # eta = 1
19311967 # Rdot = (6 * R * (1 - eta) / (1.2**6)) * (
@@ -1934,7 +1970,7 @@ def u_dot_parachute(self, t, u, post_processing=False):
19341970 # Rdot = 0
19351971
19361972 # Calculate added mass
1937- ma = ka * rho * ( 4 / 3 ) * np . pi * R ** 3
1973+ ma = 0
19381974
19391975 # Calculate freestream speed
19401976 freestream_x = vx - wind_velocity_x
@@ -1950,7 +1986,7 @@ def u_dot_parachute(self, t, u, post_processing=False):
19501986 Dz = pseudo_drag * freestream_z
19511987 ax = Dx / (mp + ma )
19521988 ay = Dy / (mp + ma )
1953- az = (Dz - 9.8 * mp ) / (mp + ma )
1989+ az = (Dz - self . env . gravity . get_value_opt ( z ) * mp ) / (mp + ma )
19541990
19551991 if post_processing :
19561992 self .__post_processed_variables .append (
@@ -2028,7 +2064,7 @@ def x(self):
20282064
20292065 @funcify_method ("Time (s)" , "Y (m)" , "spline" , "constant" )
20302066 def y (self ):
2031- """Rocket y position relative to the lauch pad as a Function of
2067+ """Rocket y position relative to the launch pad as a Function of
20322068 time."""
20332069 return self .solution_array [:, [0 , 2 ]]
20342070
@@ -2924,7 +2960,7 @@ def max_rail_button2_shear_force(self):
29242960 "Time (s)" , "Horizontal Distance to Launch Point (m)" , "spline" , "constant"
29252961 )
29262962 def drift (self ):
2927- """Rocket horizontal distance to tha launch point, in meters, as a
2963+ """Rocket horizontal distance to the launch point, in meters, as a
29282964 Function of time."""
29292965 return np .column_stack (
29302966 (self .time , (self .x [:, 1 ] ** 2 + self .y [:, 1 ] ** 2 ) ** 0.5 )
@@ -3473,7 +3509,7 @@ def time_iterator(self, node_list):
34733509 yield i , node_list [i ]
34743510 i += 1
34753511
3476- def to_dict (self , include_outputs = False ):
3512+ def to_dict (self , include_outputs = False , ** _kwargs ):
34773513 data = {
34783514 "rocket" : self .rocket ,
34793515 "env" : self .env ,
@@ -3696,9 +3732,7 @@ def add(self, flight_phase, index=None): # TODO: quite complex method
36963732 new_index = (
36973733 index - 1
36983734 if flight_phase .t < previous_phase .t
3699- else index + 1
3700- if flight_phase .t > next_phase .t
3701- else index
3735+ else index + 1 if flight_phase .t > next_phase .t else index
37023736 )
37033737 flight_phase .t += adjust
37043738 self .add (flight_phase , new_index )
0 commit comments