Thrust and the rocket equation#
# this line makes figures interactive in Jupyter notebooks
%matplotlib inline
from matplotlib import pyplot as plt
Show code cell content
# these lines are only for helping improve the display
import matplotlib_inline.backend_inline
matplotlib_inline.backend_inline.set_matplotlib_formats('pdf', 'png')
plt.rcParams['figure.dpi']= 300
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['mathtext.fontset'] = 'cm'
Example: Numerical integration of vertical launch#
Determine the burnout velocity and maximum height achieved by launching the German V-2 rocket, assuming a vertical launch. Take into account drag, the variation of atmospheric density with altitude, and the variation of gravity with altitude. Use a forward difference numerical scheme (i.e., forward Euler) to solve the system of equations.
Given data:
specific impulse: \(I_{\text{sp}}\) = 250 sec
initial mass: \(m_0\) = 12700 kg
propellant mass: \(m_p\) = 8610 kg
burn time: \(t_b\) = 60 sec
maximum body diameter: \(D\) = 5 feet 4 inches (5.333 ft)
height: \(H\) = 46 feet
Fig. 1 shows how the coefficients of lift and drag vary with Mach number and angle of attack.

Fig. 1 Variation of coefficients of drag and lift with Mach number and angle of attack, for the German V-2 rocket. Source: Sutton and Biblarz [SB16].#
Let’s set up the system of ordinary differential equations for velocity (\(m\)), altitude (\(h\)), and mass (\(m\)) that describes this problem:
where we obtained the last equation by recognizing that
for steady burning. In the above, \(g_0\) is the reference acceleration due to gravity (9.80065 m/s2), \(\rho(h)\) is the atmospheric density, \(A\) is the cross-sectional area of the body, \(C_D\) is the drag coefficient, and \(g(h)\) is the (varying) acceleration due to gravity.
Density and gravity: To move forward, we need to describe how to evaluate density, gravitational acceleration, and the coefficient of drag at any instant in time. The first two are known functions of altitude:
where \(\rho_0\) = 1.225 kg/m3 is the density at sea level, \(H_n\) = 10.4 km is the height scale of the exponential fall, \(R_0\) = 6378.388 km is the mean radius of Earth, and \(g_0\) = 9.80665 m/s2 is the sea-level acceleration due to gravity.
Coefficient of drag#
Coefficient of drag is more difficult to handle, since it depends on angle of attack and Mach number: \(C_D = C_D(M, \alpha)\). For vertical flight, the angle of attack is zero (\(\alpha = 0\)), but the Mach number will be changing both as the vehicle speed changes and the atmospheric conditions change with altitude:
Option 1: As a first approximation, we could simply assume a constant value for coefficient of drag. For example, before and after the transonic regime (where Mach number approaches 1), \(C_D \approx 0.15\).
Option 2: If we want to more-accurately capture the increase in coefficient of drag as the vehicle approaches a sonic velocity, and then the relaxation when supersonic, we need to capture the dependence of \(C_D\) on Mach number.
First, we already have an expression for \(\rho(h)\), but we also need an expression for pressure. We can use a similar exponential approximation:
where \(p_0\) = 101325 Pa is the sea-level standard atmospheric pressure and \(H_p\) = 8.4 km. With \(\gamma = 1.4\) being a reasonable constant, we can now calculate Mach number based on velocity and altitude.
Now, we need to obtain a functional dependence of \(C_D (M)\); based on Fig. 1, we see that the relationship is fairly complex. The “easiest” way to create a function for \(C_D (M)\) is to:
Extract data points from the plot manually, using a tool such as WebPlotDigitizer
Perform a spline interpolation on the data points, using a function such as
spline
in Matlab orscipy.interpolate.UnivariateSpline
in Python.
Let’s see this in action, using data extracted manually from the source figure:
import numpy as np
from scipy.interpolate import UnivariateSpline
data = np.genfromtxt('coefficient-drag.csv', delimiter=',')
coefficient_drag = UnivariateSpline(data[:,0], data[:,1], s=0, ext='const')
mach = np.linspace(0.2, 6, num=200, endpoint=True)
plt.plot(data[:,0], data[:,1], 'o', mach, coefficient_drag(mach), '-')
plt.xlabel('Mach number')
plt.ylabel('Coefficient of drag')
plt.legend(['Data', 'Cubic spline'])
plt.show()

In Matlab, the equivalent fit can be done with:
data = readmatrix('coefficient-drag.csv');
plot(data(:,1), data(:,2), 'o'); hold on
coef_drag = spline(data(:,1), data(:,2));
xx = linspace(0.2, 5, 500);
plot(xx, ppval(coef_drag,xx), '-');
Now that we have a fully defined system of equations, we need a numerical method to integrate.
We can first use a forward difference scheme, also known as the forward Euler method:
where \(\mathbf{z}_{i}\) is the vector of state variables at time \(t_i\), \(\Delta t\) is the time step size, and \(\mathbf{f}\) represents the vector of time derivatives.
To implement, we need to create a function to evaluate the time derivatives, and then specify initial conditions for velocity, altitude, and mass: \(v(t = 0) = 0\), \(h(t = 0) = 0\), and \(m(t = 0) = m_0\).
from scipy.integrate import solve_ivp
def vertical_launch(t, y, spec_impulse, mass_prop, time_burn, diameter, coef_drag):
'''Evaluates system of time derivatives for velocity, altitude, and mass.
'''
radius_earth = 6378.388 * 1e3
gravity_ref = 9.80665
density_ref = 1.225
pressure_ref = 101325
gamma = 1.4
height_den = 10400
height_pres = 8400
v = y[0]
h = y[1]
m = y[2]
gravity = gravity_ref * (radius_earth / (radius_earth + h))**2
density = density_ref * np.exp(-h / height_den)
pressure = pressure_ref * np.exp(-h / height_pres)
mach = v / np.sqrt(gamma * pressure / density)
area = np.pi * diameter**2 / 4
drag = 0.5 * density * v**2 * area * coef_drag(mach)
dmdt = -mass_prop / time_burn
dvdt = (-spec_impulse * gravity_ref / m) * dmdt - drag / m - gravity
dhdt = v
return [dvdt, dhdt, dmdt]
# given constants
spec_impulse = 250
mass_initial = 12700
mass_propellant = 8610
time_burn = 60
diameter = 1.626
sol = solve_ivp(
vertical_launch, [0, time_burn], [0, 0, mass_initial], method='RK45',
args=(spec_impulse, mass_propellant, time_burn, diameter, coefficient_drag),
dense_output=True
)
time = np.linspace(0, time_burn, 100)
z = sol.sol(time)
fig, axes = plt.subplots(2, 1)
axes[0].plot(time, z[0, :])
axes[0].set_ylabel('Velocity (m/s)')
axes[0].grid(True)
axes[1].plot(time, z[1, :] / 1000)
axes[1].set_ylabel('Altitude (km)')
axes[1].set_xlabel('Time (s)')
axes[1].grid(True)
plt.show()
print(f'Burnout velocity: {z[0,-1]: 5.2f} m/s')
print(f'Burnout altitude: {z[1,-1]/1000: 5.2f} km')

Burnout velocity: 1956.11 m/s
Burnout altitude: 44.54 km
In Matlab, the above could be done with:
clear; clc;
g0 = 9.80665;
Re = 6378.388 * 1e3;
Isp = 250;
m0 = 12700;
mp = 8610;
tb = 60;
D = 1.626;
data = readmatrix('coefficient-drag.csv');
plot(data(:,1), data(:,2), 'o'); hold on
coef_drag = spline(data(:,1), data(:,2));
xx = linspace(0.2, 5, 500);
plot(xx, ppval(coef_drag,xx), '-');
f = @(t,z) rocket(t, z, Isp, mp, tb, D, coef_drag);
[T, Z] = ode45(f, [0 tb], [0 0 m0]);
function dzdt = rocket(t, z, Isp, mp, tb, D, coef_drag)
g0 = 9.80665; Re = 6378.388 * 1e3;
mdot = mp / tb;
v = z(1);
h = z(2);
m = z(3);
g = g0 * (Re / (Re + h))^2;
rho0 = 1.225;
p0 = 101325;
Hn = 10400;
Hp = 8400;
gamma = 1.4;
rho = rho0 * exp(-h / Hn);
pres = p0 * exp(-h / Hp);
area = pi * D^2 / 4;
M = v / sqrt(gamma * pres / rho);
C_D = ppval(coef_drag, M);
drag = 0.5 * rho * v^2 * area * C_D;
dzdt = zeros(3,1);
dzdt(3) = -mdot;
dzdt(1) = (-Isp*g0/m)*dzdt(3) - drag/m - g;
dzdt(2) = v;
end