Space Enterprise at Berkeley (Liquid Motor)#
Here we go with another flight simulation example using RocketPy. This time, we use data from the Space Enterprise at Berkeley team to perform a trajectory simulation using the LiquidMotor. Let’s check it out!
[1]:
# These lines are here for debugging purposes only
%load_ext autoreload
%autoreload 2
[2]:
from rocketpy import (
Function,
LiquidMotor,
UllageBasedTank,
MassBasedTank,
Fluid,
Rocket,
Flight,
Environment,
CylindricalTank,
)
Input Curves#
First, we need to load the curves generated by the team during the motor testings.
The information that we are using are basically the volume of LOX and Propane in the tanks
[5]:
LOX_Volume_Liters = Function(
"../../data/SEBLM/test124_Lox_Volume.csv", extrapolation="zero"
)
LOX_Volume = LOX_Volume_Liters * 0.001 # convert to m^3
LOX_Volume.set_discrete(8.003, 19.984, 40, interpolation="linear")
LOX_Volume.plot(force_data=True)
LOX_tank_ullage = 0.013167926436231077 - LOX_Volume
LOX_tank_ullage.plot(8, 8.5, force_data=True)
[7]:
Propane_Volume_Liters = Function("../../data/SEBLM/test124_Propane_Volume.csv")
Propane_Volume = Propane_Volume_Liters * 0.001 # convert to m^3
Propane_Volume.set_discrete(8.003, 19.984, 40, "linear")
Propane_Volume.plot(force_data=True)
Propane_tank_ullage = 0.013167926436231077 - Propane_Volume
Propane_tank_ullage.plot(force_data=True)
Fluids#
Now it’s time to define the fluids that we are going to use in the simulation.
[9]:
LOX = Fluid(name="LOX", density=1024)
Propane = Fluid(name="Propane", density=566)
LOXTankPressurizingGas = Fluid(name="N2", density=31.3 / 28) # 450 PSI
PropaneTankPressurizingGas = Fluid(name="N2", density=313 * 300 / 4500 / 28) # 300 PSI
PressurizingGas = Fluid(name="N2", density=313 / 28) # 4500 PSI
Tanks#
After the fluids, it is time to define all the 3 tanks that we have in the motor.
LOX Tank#
We first start defining the tank geometry, which is a cylinder with a spherical head.
[10]:
LOX_tank_geometry = CylindricalTank(0.0744, 0.658, spherical_caps=True)
Next, we use the tank geometry to define the tank itself.
[11]:
LOX_tank = UllageBasedTank(
name="LOX Tank",
flux_time=(8, 20),
geometry=LOX_tank_geometry,
gas=LOXTankPressurizingGas,
liquid=LOX,
ullage=LOX_tank_ullage,
)
After defining, we stop for a minute to appreciate the evolution of mass and mass flow rate that were calculated by RocketPy.
Isn’t it beautiful?
[12]:
LOX_tank.fluid_mass()
LOX_tank.net_mass_flow_rate()
LOX_tank.liquid_height()
LOX_tank.gas_height()
LOX_tank.center_of_mass()
LOX_tank.inertia()
Propane Tank#
Our setup work is not done yet. We still need to define the propane tank, Which is a cylinder with a spherical head.
The propane has the role of pressurizing the LOX tank.
[13]:
Propane_tank_geometry = CylindricalTank(0.0744, 0.658, spherical_caps=True)
Propane_tank = UllageBasedTank(
name="Propane Tank",
flux_time=(8, 20),
geometry=Propane_tank_geometry,
gas=PropaneTankPressurizingGas,
liquid=Propane,
ullage=Propane_tank_ullage,
)
Again, let’s visualize the partial results.
[14]:
Propane_tank.fluid_mass()
Propane_tank.net_mass_flow_rate()
Propane_tank.liquid_height()
Propane_tank.gas_height()
Propane_tank.center_of_mass()
Propane_tank.inertia()
Pressure Tank#
The third tank is the pressure tank, which is a cylinder with a spherical head.
[15]:
Pressure_tank_geometry = CylindricalTank(0.135 / 2, 0.846, spherical_caps=True)
Pressure_tank = MassBasedTank(
name="Pressure Tank",
geometry=Pressure_tank_geometry,
liquid_mass=0,
flux_time=(8, 20),
gas_mass="../../data/SEBLM/pressurantMassFiltered.csv",
gas=PressurizingGas,
liquid=PressurizingGas,
)
[16]:
Pressure_tank.fluid_mass()
Pressure_tank.net_mass_flow_rate()
Pressure_tank.liquid_height()
Pressure_tank.gas_height()
Pressure_tank.center_of_mass()
Pressure_tank.inertia()
Liquid Motor#
After defining the three tanks, we can finally define the liquid motor object, just check how simple it is!
[18]:
SEBLM = LiquidMotor(
thrust_source="../../data/SEBLM/test124_Thrust_Curve.csv",
center_of_dry_mass_position=0,
dry_inertia=(0, 0, 0),
dry_mass=0,
burn_time=(8, 20),
nozzle_radius=0.069 / 2,
nozzle_position=-1.364,
coordinate_system_orientation="nozzle_to_combustion_chamber",
)
SEBLM.add_tank(Propane_tank, position=-1.048)
SEBLM.add_tank(LOX_tank, position=0.711)
SEBLM.add_tank(Pressure_tank, position=2.007)
After defining it, we can check all the important information about the motor, such as the thrust curve, the mass flow rate curve, the specific impulse curve, and the burn time.
[19]:
SEBLM.all_info()
Nozzle Details
Nozzle Radius: 0.0345 m
Motor Details
Total Burning Time: 12 s
Total Propellant Mass: 19.852 kg
Average Propellant Exhaust Velocity: 1743.503 m/s
Average Thrust: 2064.885 N
Maximum Thrust: 2428.4243134124745 N at 17.666 s after ignition.
Total Impulse: 24778.624 Ns
c:\mateus\github\rocketpy\rocketpy\Function.py:2487: IntegrationWarning: The occurrence of roundoff error is detected, which prevents
the requested tolerance from being achieved. The error may be
underestimated.
ans, _ = integrate.quad(self, a, b, epsabs=0.001, limit=10000)
Rocket Definition#
The motor is ready to go, but we still need to define the rocket itself. Let’s see how it is done:
[23]:
SEBRocket = Rocket(
radius=0.098,
mass=63.4,
inertia=(25, 25, 1),
power_off_drag="../../data/SEBLM/drag.csv",
power_on_drag="../../data/SEBLM/drag.csv",
center_of_mass_without_motor=3.23,
coordinate_system_orientation="nose_to_tail",
)
SEBRocket.add_motor(SEBLM, position=5.75)
SEBRocket.add_nose(length=0.7, kind="vonKarman", position=0)
SEBRocket.add_tail(
top_radius=0.098, bottom_radius=0.058, length=0.198, position=5.69 - 0.198
)
SEBRocket.add_trapezoidal_fins(
n=4,
root_chord=0.355,
tip_chord=0.0803,
span=0.156,
position=5.25,
cant_angle=0,
)
SEBRocket.set_rail_buttons(lower_button_position=-1, upper_button_position=1)
[23]:
<rocketpy.AeroSurface.RailButtons at 0x241c353b2b0>
[24]:
SEBRocket.all_info()
Inertia Details
Rocket Mass: 63.400 kg
Rocket Dry Mass: 63.400 kg (With Motor)
Rocket Mass: 83.252 kg (With Propellant)
Rocket Inertia (with motor, but without propellant) 11: 25.000 kg*m2
Rocket Inertia (with motor, but without propellant) 22: 25.000 kg*m2
Rocket Inertia (with motor, but without propellant) 33: 1.000 kg*m2
Rocket Inertia (with motor, but without propellant) 12: 0.000 kg*m2
Rocket Inertia (with motor, but without propellant) 13: 0.000 kg*m2
Rocket Inertia (with motor, but without propellant) 23: 0.000 kg*m2
Geometrical Parameters
Rocket Maximum Radius: 0.098 m
Rocket Frontal Area: 0.030172 m2
Rocket Distances
Rocket Center of Dry Mass - Center of Mass withour Motor: 0.000 m
Rocket Center of Dry Mass - Nozzle Exit Distance: 2.520 m
Rocket Center of Dry Mass - Center of Propellant Mass: 0.959 m
Rocket Center of Mass - Rocket Loaded Center of Mass: 0.229 m
Aerodynamics Lift Coefficient Derivatives
Nosecone Lift Coefficient Derivative: 2.000/rad
Tail Lift Coefficient Derivative: -1.299/rad
Fins Lift Coefficient Derivative: 5.895/rad
Aerodynamics Center of Pressure
Nosecone Center of Pressure to CM: 0.350 m
Tail Center of Pressure to CM: 5.583 m
Fins Center of Pressure to CM: 5.420 m
Distance - Center of Pressure to Center of Dry Mass: -0.392 m
Initial Static Margin: 2.000 c
Final Static Margin: 2.688 c
Mass Plots
Aerodynamics Plots
Environment#
I swear this is the last step before actually flying the rocket. We need to define the environment in which the rocket will fly.
[25]:
env = Environment(
latitude=35.347122986338356, longitude=-117.80893423073582
)
env.set_date((2022, 12, 3, 14 + 7, 0, 0)) # UTC
env.set_atmospheric_model(
type="custom_atmosphere",
pressure=None,
temperature=None,
wind_u=[(0, 1), (500, 0), (1000, 5), (2500, 5.0), (5000, 10)],
wind_v=[(0, 0), (500, 3), (1600, 2), (2500, -3), (5000, 10)],
)
env.maxExpectedHeight = 8000
[26]:
env.info()
Gravity Details
Acceleration of Gravity at Lauch Site: 9.797631455625396 m/s²
Launch Site Details
Launch Date: 2022-12-03 21:00:00 UTC
Launch Site Latitude: 35.34712°
Launch Site Longitude: -117.80893°
Reference Datum: SIRGAS2000
Launch Site UTM coordinates: 426495.69 W 3911838.99 N
Launch Site UTM zone: 11S
Launch Site Surface Elevation: 0.0 m
Atmospheric Model Details
Atmospheric Model Type: custom_atmosphere
custom_atmosphere Maximum Height: 5.000 km
Surface Atmospheric Conditions
Surface Wind Speed: 1.00 m/s
Surface Wind Direction: 270.00°
Surface Wind Heading: 90.00°
Surface Pressure: 1013.25 hPa
Surface Temperature: 288.15 K
Surface Air Density: 1.225 kg/m³
Surface Speed of Sound: 340.29 m/s
Atmospheric Model Plots
Flight Simulation#
Finally, here we go with our flight simulation. We are going to run the simulation only until the apogee, since we are not interested in the landing phase.
The maxTimeStep parameter was set to a low value to ensure there won’t be any numerical instability during the launch rail phase.
[28]:
test_flight = Flight(
rocket=SEBRocket,
environment=env,
rail_length=18.28,
inclination=90,
heading=23,
max_time_step=0.1,
terminate_on_apogee=True,
)
[30]:
test_flight.angle_of_attack.plot(test_flight.out_of_rail_time, 15)
[31]:
test_flight.all_info()
Initial Conditions
Position - x: 0.00 m | y: 0.00 m | z: 0.00 m
Velocity - Vx: 0.00 m/s | Vy: 0.00 m/s | Vz: 0.00 m/s
Attitude - e0: 0.980 | e1: 0.000 | e2: -0.000 | e3: -0.199
Euler Angles - Spin φ : -191.50° | Nutation θ: -0.00° | Precession ψ: 168.50°
Angular Velocity - ω1: 0.00 rad/s | ω2: 0.00 rad/s| ω3: 0.00 rad/s
Surface Wind Conditions
Frontal Surface Wind Speed: 0.39 m/s
Lateral Surface Wind Speed: -0.92 m/s
Launch Rail
Launch Rail Length: 18.28 m
Launch Rail Inclination: 90.00°
Launch Rail Heading: 23.00°
Rail Departure State
Rail Departure Time: 9.434 s
Rail Departure Velocity: 19.708 m/s
Rail Departure Static Margin: 2.029 c
Rail Departure Angle of Attack: 2.831°
Rail Departure Thrust-Weight Ratio: 2.252
Rail Departure Reynolds Number: 2.645e+05
Burn out State
Burn out time: 20.000 s
Altitude at burn out: 1094.550 m (AGL)
Rocket velocity at burn out: 199.214 m/s
Freestream velocity at burn out: 199.811 m/s
Mach Number at burn out: 0.595
Kinetic energy at burn out: 1.328e+06 J
Apogee State
Apogee Altitude: 2799.897 m (ASL) | 2799.897 m (AGL)
Apogee Time: 38.205 s
Apogee Freestream Speed: 18.930 m/s
Parachute Events
No Parachute Events Were Triggered.
Impact Conditions
X Impact: 0.000 m
Y Impact: 0.000 m
Time of Impact: 38.205 s
Velocity at Impact: 0.000 m/s
Maximum Values
Maximum Speed: 199.214 m/s at 20.00 s
Maximum Mach Number: 0.595 Mach at 20.00 s
Maximum Reynolds Number: 2.461e+06 at 20.00 s
Maximum Dynamic Pressure: 2.199e+04 Pa at 20.00 s
Maximum Acceleration: 21.624 m/s² at 17.38 s
Maximum Gs: 2.207 g at 17.38 s
Maximum Upper Rail Button Normal Force: 1.606 N
Maximum Upper Rail Button Shear Force: -0.045 N
Maximum Lower Rail Button Normal Force: 0.636 N
Maximum Lower Rail Button Shear Force: -0.017 N
Numerical Integration Settings
Maximum Allowed Flight Time: 600.000000 s
Maximum Allowed Time Step: 0.100000 s
Minimum Allowed Time Step: 0.000000e+00 s
Relative Error Tolerance: 1e-06
Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]
Allow Event Overshoot: True
Terminate Simulation on Apogee: True
Number of Time Steps Used: 707
Number of Derivative Functions Evaluation: 1653
Average Function Evaluations per Time Step: 2.338048
Trajectory 3d Plot
Trajectory Kinematic Plots
Angular Position Plots
Path, Attitude and Lateral Attitude Angle plots
Trajectory Angular Velocity and Acceleration Plots
Aerodynamic Forces Plots
Rail Buttons Forces Plots
Trajectory Energy Plots
Trajectory Fluid Mechanics Plots
Trajectory Stability and Control Plots
Rocket and Parachute Pressure Plots
Rocket has no parachutes. No parachute plots available