Skip to content

Commit d985eab

Browse files
author
MateusStano
committed
ENH: add coriolis to parachute
1 parent 07d1a6f commit d985eab

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

rocketpy/simulation/flight.py

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,10 @@ def __init_equations_of_motion(self):
11971197
if self.equations_of_motion == "solid_propulsion":
11981198
# NOTE: The u_dot is faster, but only works for solid propulsion
11991199
self.u_dot_generalized = self.u_dot
1200+
if self.equations_of_motion == "2":
1201+
self.u_dot_generalized = self.u_dot_generalized_std
1202+
if self.equations_of_motion == "3":
1203+
self.u_dot_generalized = self.u_dot_generalized_moving_omega
12001204

12011205
def __init_controllers(self):
12021206
"""Initialize controllers and sensors"""
@@ -1935,6 +1939,216 @@ def u_dot_generalized(self, t, u, post_processing=False): # pylint: disable=too
19351939

19361940
return u_dot
19371941

1942+
def u_dot_generalized_std(self, t, u, post_processing=False): # pylint: disable=too-many-locals,too-many-statements
1943+
"""Calculates derivative of u state vector with respect to time when the
1944+
rocket is flying in 6 DOF motion in space and significant mass variation
1945+
effects exist. Typical flight phases include powered ascent after launch
1946+
rail.
1947+
1948+
Parameters
1949+
----------
1950+
t : float
1951+
Time in seconds
1952+
u : list
1953+
State vector defined by u = [x, y, z, vx, vy, vz, q0, q1,
1954+
q2, q3, omega1, omega2, omega3].
1955+
post_processing : bool, optional
1956+
If True, adds flight data information directly to self variables
1957+
such as self.angle_of_attack, by default False.
1958+
1959+
Returns
1960+
-------
1961+
u_dot : list
1962+
State vector defined by u_dot = [vx, vy, vz, ax, ay, az,
1963+
e0_dot, e1_dot, e2_dot, e3_dot, alpha1, alpha2, alpha3].
1964+
"""
1965+
# Retrieve integration data
1966+
_, _, z, vx, vy, vz, e0, e1, e2, e3, omega1, omega2, omega3 = u
1967+
1968+
# Create necessary vectors
1969+
# r = Vector([x, y, z]) # CDM position vector
1970+
v = Vector([vx, vy, vz]) # CDM velocity vector
1971+
e = [e0, e1, e2, e3] # Euler parameters/quaternions
1972+
w = Vector([omega1, omega2, omega3]) # Angular velocity vector
1973+
1974+
# Retrieve necessary quantities
1975+
## Rocket mass
1976+
total_mass = self.rocket.total_mass.get_value_opt(t)
1977+
total_mass_dot = self.rocket.total_mass_flow_rate.get_value_opt(t)
1978+
total_mass_ddot = self.rocket.total_mass_flow_rate.differentiate_complex_step(t)
1979+
## CM position vector and time derivatives relative to CDM in body frame
1980+
r_CM_z = self.rocket.com_to_cdm_function
1981+
r_CM_t = r_CM_z.get_value_opt(t)
1982+
r_CM = Vector([0, 0, r_CM_t])
1983+
r_CM_dot = Vector([0, 0, r_CM_z.differentiate_complex_step(t)])
1984+
r_CM_ddot = Vector([0, 0, r_CM_z.differentiate(t, order=2)])
1985+
## Nozzle position vector
1986+
r_NOZ = Vector([0, 0, self.rocket.nozzle_to_cdm])
1987+
## Nozzle gyration tensor
1988+
S_nozzle = self.rocket.nozzle_gyration_tensor
1989+
## Inertia tensor
1990+
inertia_tensor = self.rocket.get_inertia_tensor_at_time(t)
1991+
## Inertia tensor time derivative in the body frame
1992+
I_dot = self.rocket.get_inertia_tensor_derivative_at_time(t)
1993+
1994+
# Calculate the Inertia tensor relative to CM
1995+
H = (r_CM.cross_matrix @ -r_CM.cross_matrix) * total_mass
1996+
I_CM = inertia_tensor - H
1997+
1998+
# Prepare transformation matrices
1999+
K = Matrix.transformation(e)
2000+
Kt = K.transpose
2001+
2002+
# Compute aerodynamic forces and moments
2003+
R1, R2, R3, M1, M2, M3 = 0, 0, 0, 0, 0, 0
2004+
2005+
## Drag force
2006+
rho = self.env.density.get_value_opt(z)
2007+
wind_velocity_x = self.env.wind_velocity_x.get_value_opt(z)
2008+
wind_velocity_y = self.env.wind_velocity_y.get_value_opt(z)
2009+
wind_velocity = Vector([wind_velocity_x, wind_velocity_y, 0])
2010+
free_stream_speed = abs((wind_velocity - Vector(v)))
2011+
speed_of_sound = self.env.speed_of_sound.get_value_opt(z)
2012+
free_stream_mach = free_stream_speed / speed_of_sound
2013+
2014+
if self.rocket.motor.burn_start_time < t < self.rocket.motor.burn_out_time:
2015+
pressure = self.env.pressure.get_value_opt(z)
2016+
net_thrust = max(
2017+
self.rocket.motor.thrust.get_value_opt(t)
2018+
+ self.rocket.motor.pressure_thrust(pressure),
2019+
0,
2020+
)
2021+
drag_coeff = self.rocket.power_on_drag.get_value_opt(free_stream_mach)
2022+
else:
2023+
net_thrust = 0
2024+
drag_coeff = self.rocket.power_off_drag.get_value_opt(free_stream_mach)
2025+
R3 += -0.5 * rho * (free_stream_speed**2) * self.rocket.area * drag_coeff
2026+
for air_brakes in self.rocket.air_brakes:
2027+
if air_brakes.deployment_level > 0:
2028+
air_brakes_cd = air_brakes.drag_coefficient.get_value_opt(
2029+
air_brakes.deployment_level, free_stream_mach
2030+
)
2031+
air_brakes_force = (
2032+
-0.5
2033+
* rho
2034+
* (free_stream_speed**2)
2035+
* air_brakes.reference_area
2036+
* air_brakes_cd
2037+
)
2038+
if air_brakes.override_rocket_drag:
2039+
R3 = air_brakes_force # Substitutes rocket drag coefficient
2040+
else:
2041+
R3 += air_brakes_force
2042+
# Get rocket velocity in body frame
2043+
velocity_in_body_frame = Kt @ v
2044+
# Calculate lift and moment for each component of the rocket
2045+
for aero_surface, _ in self.rocket.aerodynamic_surfaces:
2046+
# Component cp relative to CDM in body frame
2047+
comp_cp = self.rocket.surfaces_cp_to_cdm[aero_surface]
2048+
# Component absolute velocity in body frame
2049+
comp_vb = velocity_in_body_frame + (w ^ comp_cp)
2050+
# Wind velocity at component altitude
2051+
comp_z = z + (K @ comp_cp).z
2052+
comp_wind_vx = self.env.wind_velocity_x.get_value_opt(comp_z)
2053+
comp_wind_vy = self.env.wind_velocity_y.get_value_opt(comp_z)
2054+
# Component freestream velocity in body frame
2055+
comp_wind_vb = Kt @ Vector([comp_wind_vx, comp_wind_vy, 0])
2056+
comp_stream_velocity = comp_wind_vb - comp_vb
2057+
comp_stream_speed = abs(comp_stream_velocity)
2058+
comp_stream_mach = comp_stream_speed / speed_of_sound
2059+
# Reynolds at component altitude
2060+
# TODO: Reynolds is only used in generic surfaces. This calculation
2061+
# should be moved to the surface class for efficiency
2062+
comp_reynolds = (
2063+
self.env.density.get_value_opt(comp_z)
2064+
* comp_stream_speed
2065+
* aero_surface.reference_length
2066+
/ self.env.dynamic_viscosity.get_value_opt(comp_z)
2067+
)
2068+
# Forces and moments
2069+
X, Y, Z, M, N, L = aero_surface.compute_forces_and_moments(
2070+
comp_stream_velocity,
2071+
comp_stream_speed,
2072+
comp_stream_mach,
2073+
rho,
2074+
comp_cp,
2075+
w,
2076+
comp_reynolds,
2077+
)
2078+
R1 += X
2079+
R2 += Y
2080+
R3 += Z
2081+
M1 += M
2082+
M2 += N
2083+
M3 += L
2084+
2085+
# Off center moment
2086+
M1 += (
2087+
self.rocket.cp_eccentricity_y * R3
2088+
+ self.rocket.thrust_eccentricity_y * net_thrust
2089+
)
2090+
M2 -= (
2091+
self.rocket.cp_eccentricity_x * R3
2092+
+ self.rocket.thrust_eccentricity_x * net_thrust
2093+
)
2094+
M3 += self.rocket.cp_eccentricity_x * R2 - self.rocket.cp_eccentricity_y * R1
2095+
2096+
weight_in_body_frame = Kt @ Vector(
2097+
[0, 0, -total_mass * self.env.gravity.get_value_opt(z)]
2098+
)
2099+
2100+
T00 = total_mass * r_CM
2101+
T03 = 2 * total_mass_dot * (r_NOZ - r_CM) - 2 * total_mass * r_CM_dot
2102+
T04 = (
2103+
Vector([0, 0, net_thrust])
2104+
- total_mass * r_CM_ddot
2105+
- 2 * total_mass_dot * r_CM_dot
2106+
+ total_mass_ddot * (r_NOZ - r_CM)
2107+
)
2108+
T05 = total_mass_dot * S_nozzle - I_dot
2109+
2110+
T20 = (
2111+
((w ^ T00) ^ w)
2112+
+ (w ^ T03)
2113+
+ T04
2114+
+ weight_in_body_frame
2115+
+ Vector([R1, R2, R3])
2116+
)
2117+
2118+
T21 = (
2119+
((inertia_tensor @ w) ^ w)
2120+
+ T05 @ w
2121+
- (weight_in_body_frame ^ r_CM)
2122+
+ Vector([M1, M2, M3])
2123+
)
2124+
2125+
# Angular velocity derivative
2126+
w_dot = I_CM.inverse @ (T21 + (T20 ^ r_CM))
2127+
2128+
# Velocity vector derivative + Coriolis acceleration
2129+
v_dot = K @ (T20 / total_mass - (r_CM ^ w_dot))
2130+
2131+
# Euler parameters derivative
2132+
e_dot = [
2133+
0.5 * (-omega1 * e1 - omega2 * e2 - omega3 * e3),
2134+
0.5 * (omega1 * e0 + omega3 * e2 - omega2 * e3),
2135+
0.5 * (omega2 * e0 - omega3 * e1 + omega1 * e3),
2136+
0.5 * (omega3 * e0 + omega2 * e1 - omega1 * e2),
2137+
]
2138+
2139+
# Position vector derivative
2140+
r_dot = [vx, vy, vz]
2141+
2142+
# Create u_dot
2143+
u_dot = [*r_dot, *v_dot, *e_dot, *w_dot]
2144+
2145+
if post_processing:
2146+
self.__post_processed_variables.append(
2147+
[t, *v_dot, *w_dot, R1, R2, R3, M1, M2, M3, net_thrust]
2148+
)
2149+
2150+
return u_dot
2151+
19382152
def u_dot_parachute(self, t, u, post_processing=False):
19392153
"""Calculates derivative of u state vector with respect to time
19402154
when rocket is flying under parachute. A 3 DOF approximation is
@@ -2001,6 +2215,12 @@ def u_dot_parachute(self, t, u, post_processing=False):
20012215
ay = Dy / (mp + ma)
20022216
az = (Dz - 9.8 * mp) / (mp + ma)
20032217

2218+
# Add coriolis acceleration
2219+
_, w_earth_y, w_earth_z = self.env.earth_rotation_vector
2220+
ax -= 2 * (vz * w_earth_y - vy * w_earth_z)
2221+
ay -= 2 * (vx * w_earth_z)
2222+
az -= 2 * (-vx * w_earth_y)
2223+
20042224
if post_processing:
20052225
self.__post_processed_variables.append(
20062226
[t, ax, ay, az, 0, 0, 0, Dx, Dy, Dz, 0, 0, 0, 0]

0 commit comments

Comments
 (0)