# this line makes figures interactive in Jupyter notebooks
%matplotlib inline
from matplotlib import pyplot as plt
import numpy as np
# we can use this to solve nonlinear/transcendental equations
from scipy.optimize import root_scalar
# this provides access to many physical constants
from scipy import constants
# provides the 1976 US Standard Atmosphere model
from fluids.atmosphere import ATMOSPHERE_1976
# Module used to parse and work with units
from pint import UnitRegistry
ureg = UnitRegistry()
Q_ = ureg.Quantity
# for convenience:
def to_si(quant):
'''Converts a Pint Quantity to magnitude at base SI units.
'''
return quant.to_base_units().magnitudeSource
# 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'We previously derived the expression for rocket thrust , based on conservation of momentum:
where is the mass flow rate of propellant, is the exit/exhaust velocity, is the nozzle exit area, is the exit pressure, and is the ambient pressure. While mass flow rate, nozzle area, and ambient pressure are either design choices or determined by the mission, the values of and are less clear. We can analyze the flow and thermodynamic properties of an idealized rocket to determine these based on other key parameters.
Figure 1:Diagram of a rocket, showing the combustion chamber (or heating chamber) with heat addition , with stagnation properties , , and ; and converging-diverging nozzle, with mass flow rate . The nozzle has throat area and throat velocity ; and exit area , velocity , and pressure .
Figure 1 shows an idealized rocket with a combustion chamber and nozzle; we make the following assumptions:
ideal gas
one-dimensional isentropic flow in the nozzle
“combustion” is simply heat addition, with the resulting products at and , and can be a stand-in for other energy deposition mechanisms.
Thermodynamic properties¶
First, a quick review of static versus stagnation properties:
Static properties: Conditions measured in a moving flow
Stagnation properties: Properties if the flow were isentropically brought to rest () at zero potential ()
Starting from the steady-flow energy equation (neglecting potential energy changes and work/heat transfer in the nozzle):
The stagnation enthalpy (also called total enthalpy) is defined as:
with respect to static properties and somewhere in the flow. Assuming constant specific heat , we can also define the stagnation temperature
which is often more useful than stagnation enthalpy.
Derivation of exit velocity¶
To derive the exit velocity, let’s examine each of the main subsystems in turn. First, examining the combustion chamber, with the propellent entering with enthalpy , heat release from combustion , and leaving with enthalpy . The flow velocity is approximately zero ().
Then, an energy balance on the combustion chamber with negligible kinetic energy:
where is the heat released by combustion, or otherwise added by another mechanism (e.g., nuclear fission, electrical heating). The state at the exit of the combustion chamber is the state at the entrance to the nozzle.
Examining the nozzle portion of Figure 1, we move from location 0 at the nozzle inlet (where ), through the throat (), to the exit (). Flow accelerates from subsonic to supersonic.
In the nozzle, (the inlet velocity much smaller than exit velocity):
Therefore
and we can solve for exit velocity:
We can see that there are two contributions here: the stagnation enthlapy resulting from combustion or heat addition , and the reduction of temperature through the nozzle ().
Recall that, for ideal gases, we can express relationships between the specific heats:
where is the constant pressure specific heat, is the constant volume specific heat, is the ratio of specific heats, and is the specific gas constant. ( is going to be used a lot moving forward!) For an isentropic flow through the nozzle, we can also express the temperature ratio with a pressure ratio:
Using these, we can finally express the exit velocity as
Let’s examine the two main contributions in Equation (11):
pressure term: . This is a weak function of the propellant, via , but a strong function of the nozzle geometry. The further we can expand the flow (meaning, reduce the pressure) in the nozzle, the greater the exit velocity.
enthalpy term: . This term is maximized with a low molecular weight and high stagnation temperature—meaning, from our combustion/heating process, we want a smaller but hot molecule as our working fluid moving through the nozzle and exhausting.
The pressure term is essentially controlled by the nozzle, but the enthalpy term depends on how we are adding heat to the propellant. In a chemical rocket, which relies on combustion of one or more propellants, molecular weight of the products and the resulting temperature are not independent, and we have to balance these competing goals. For example, water (HO) and carbon dioxide (CO) are common combustion products from hydrocarbons, with greater carbon content in the fuel leading to more CO in the products. If our propellant mixture is only hydrogen (H) and oxygen (O), then water is the primary product species. The molecular weight of HO is approximately 18 kg/kmol, while that of CO is 44 kg/kmol. We will discuss this more in later modules.
However, in a rocket relying on electric or nuclear heating, we can choose our propellant to have a low molecular weight, and the resulting temperature from heating is essentially independent of this choice and only limited by other materials. As a result, past research into nuclear thermal rockets has used hydrogen (H) as the propellant, due to its extremely low molecular weight (about 2 km/kmol).
Characteristic velocity: c*¶
The characteristic velocity (pronounced “c-star”) is defined as:
where is the combustion chamber pressure / the stagnation pressure, is the nozzle throat area, and is the total mass flow rate of propellant. characterizes the energetics of the combustion process—it measures how effectively the propellant’s chemical energy is converted to gas thermal energy in the chamber. It has units of velocity/speed, but does not actually represent a real velocity.
We can relate the above empirical definition of to thermodynamic properties inside the rocket. T derive, start by applying conservation of mass at the throat:
but, at the throat the Mach number , so:
From gas dynamics, we have the relationships between static and stagnation properties:
which we can apply at the throat, where :
Therefore,
and using the ideal gas law we can express as:
Inserting the mass flow rate at the throat into the expression for , and after substitution and simplification, we get
Looking at Equation (19), we can make some key observations:
only; it is the primary energetic performance parameter of a rocket and is not a function of nozzle performance.
increases with
efficiency¶
The efficiency compares measured to theoretical characteristic velocity:
Typically, values of .
Thrust coefficient: ¶
The thrust coefficient characterizes nozzle performance:
and typically ranges from around one to just over two, depending on the nozzle and ambient pressure. Like , we can derive a theoretical expression based on nozzle properties. Starting from the thrust equation:
Then, recalling the definition of , we can rewrite the first term as
and after inserting the full expressions for and (from Equations (11) and ), canceling terms and simplifying, we get
The first term represents the contribution to thrust by the exit velocity/momentum, and the second term the contribution to thrust by the exit pressure.
Some key takeaways:
is not a function of chamber temperature at all.
is only a function of the nozzle and ambient pressure
Examining Equation , we can see that, for a given nozzle design, only the term on the far right with will vary. Often, is split into the constant and variable terms:
where
Other commonly used formulations include the thrust coefficient for an optimally expanded nozzle, where :
and thrust coefficient in vacuum, where :
Relationships between thrust, specific impulse, and effective exhaust velocity¶
Using characteristic velocity () and thrust coefficient (), we can express thrust, specific impulse (), and effective exhause velocity ():
, , and characterize the overall performance of a rocket, and are combined functions of the energetic and nozzle performance; and split these.
Nozzle area ratio¶
Our expression for given in Equation (24) has both the area ratio of the nozzle ( to ) and the pressure ratio ( to ). However, the expansion of the flow (resulting in at the exit) depends on the area ratio, and we can derive this relationship.
For isentropic 1D flow in the nozzle, with an ideal gas, applying conservation of mass between the throat (where ) and exit (where ) tells us
which can be rearranged to
but we know
which we can insert into the final expression in Equation (30) above to get
Almost there! We just need to use our isentropic relationship between pressure and temperature:
and we can finally express the area ratio as a function of the pressure ratio:
where we can see that only. In other words, to achieve a desired pressure ratio of the nozzle, the required area ratio of the nozzle depends only on that pressure ratio and , not the temperature in the combustion chamber (i.e., the stagnation temperature).
Exit Mach number¶
We can also express the relationship between the area ratio and exit Mach number . From conservation of mass between the throat and exit:
but, again, , , and we can relate static properties to stagnation properties based on the local Mach number. Rearranging, we obtain
Simplifying, we get
Designing rocket nozzles¶
Rocket scientists (... rocket engineers) have used the above equations for thrust coefficient, area ratio, and Mach number to design rockets for many years.
For example, the following figures show area ratio vs. pressure ratio, and thrust coefficient (at optimum expansion conditions, where ) vs. pressure ratio, both for various specific heat ratios:
## area ratio as a function of pressure ratio
gammas = [1.1, 1.25, 1.4, 1.7]
pressure_ratios = np.logspace(1, 4, num=50)
labels = [r'$\gamma$ = 1.1', '1.25', '1.4', '1.7']
# let's define a function to calculate area ratio based on gamma and the pressure ratio:
def calc_area_ratio(gamma, pressure_ratio):
'''Calculates area ratio based on specific heat ratio and pressure ratio.
pressure ratio: chamber / exit
area ratio: exit / throat
'''
return (
np.power(2 / (gamma + 1), 1/(gamma-1)) *
np.power(pressure_ratio, 1 / gamma) *
np.sqrt((gamma - 1) / (gamma + 1) /
(1 - np.power(pressure_ratio, (1 - gamma)/gamma))
)
)
for gamma, label in zip(gammas, labels):
area_ratios = calc_area_ratio(gamma, pressure_ratios)
plt.plot(pressure_ratios, area_ratios)
plt.text(
0.9*pressure_ratios[-10], 1.01*area_ratios[-10],
label,
horizontalalignment='right', fontsize=8
)
plt.xlim([10, 1e4])
plt.ylim([1, 500])
plt.xlabel(r'Pressure ratio, $p_0 / p_e$')
plt.ylabel(r'Area ratio, $\epsilon = \frac{A_e}{A_t}$')
plt.grid(True)
plt.xscale('log')
plt.yscale('log')
plt.title('Area ratio vs. pressure ratio')
plt.show()# Define a function to calculate thrust coefficient, assuming optimum expansion
# (exit pressure = ambient pressure), based on gamma and pressure ratio:
def calc_thrust_coeff(gamma, pressure_ratio):
''' Calculates thrust coefficient for optimum expansion.
pressure ratio: chamber / exit
area ratio: exit / throat
'''
return np.sqrt(
2 * np.power(gamma, 2) / (gamma - 1) *
np.power(2 / (gamma + 1), (gamma + 1)/(gamma - 1)) *
(1 - np.power(1.0 / pressure_ratio, (gamma - 1)/gamma))
)
# This function returns zero for a given area ratio, pressure ratio, and gamma,
#and is used to numerically calculate pressure ratio given the other two values.
def root_area_ratio(pressure_ratio, gamma, area_ratio):
''' pressure ratio: chamber / exit
area ratio: exit / throat
'''
return area_ratio - calc_area_ratio(gamma, pressure_ratio)
gammas = [1.1, 1.2, 1.3, 1.4]
labels = [r'$\gamma$ = 1.1', '1.2', '1.3', '1.4']
area_ratios = [3, 5, 10, 20, 50, 100]
pressure_ratios = np.logspace(1, 4, num=50)
for gamma, label in zip(gammas, labels):
thrust_coeffs = calc_thrust_coeff(gamma, pressure_ratios)
plt.plot(pressure_ratios, thrust_coeffs)
plt.text(
0.9*pressure_ratios[-1], 1.01*thrust_coeffs[-1],
label, horizontalalignment='right'
)
for area_ratio in area_ratios:
pressure_ratios2 = np.zeros(len(gammas))
thrust_coeffs2 = np.zeros(len(gammas))
for idx, gamma in enumerate(gammas):
sol = root_scalar(root_area_ratio, x0=20, x1=100, args=(gamma, area_ratio))
pressure_ratios2[idx] = sol.root
thrust_coeffs2[idx] = calc_thrust_coeff(gamma, sol.root)
plt.plot(pressure_ratios2, thrust_coeffs2, '--')
plt.text(
pressure_ratios2[-1], thrust_coeffs2[-1],
r'$\epsilon =$' + f'{area_ratio}',
horizontalalignment='left', verticalalignment='top', fontsize=8
)
plt.xlim([10, 1e4])
plt.ylim([1.2, 2.3])
plt.xlabel(r'Pressure ratio, $p_0/p_e$')
plt.ylabel(r'Thrust coefficient, $C_F$')
plt.grid(True)
plt.xscale('log')
plt.title('Thrust coefficient vs. pressure ratio, at optimum expansion')
plt.show()Example: using equations to design optimal rocket nozzle¶
Using the above equations, design a rocket nozzle optimally for these conditions: = 70 atm, = 1 atm, and = 1.2. Find the nozzle area ratio, and the rocket thrust as a function of nozzle exit area.
We know the pressure ratio:
so we can directly calculate the nozzle area ratio using Equation (34).
# set the given constants
chamber_pressure = Q_(70, 'atm')
exit_pressure = Q_(1, 'atm')
gamma = 1.2
pressure_ratio = chamber_pressure / exit_pressure
area_ratio = (
np.power(2 / (gamma + 1), 1/(gamma-1)) *
np.power(pressure_ratio, 1 / gamma) *
np.sqrt((gamma - 1) / (gamma + 1) /
(1 - np.power(pressure_ratio, (1 - gamma)/gamma))
)
)
print(f'Nozzle area ratio: {area_ratio: 3.2f~P}')Nozzle area ratio: 9.06
Since by design (for an optimal nozzle), we can calculate the thrust coefficient using Equation (24).
and then thrust using:
thrust_coeff = np.sqrt(
2 * np.power(gamma, 2) / (gamma - 1) *
np.power(2 / (gamma + 1), (gamma + 1)/(gamma - 1)) *
(1 - np.power(1.0 / pressure_ratio, (gamma - 1)/gamma))
)
print(f'Thrust coefficient = {thrust_coeff: 3.2f~P}')
thrust_per_area = thrust_coeff * chamber_pressure / area_ratio
print(f'Thrust / A_e = {thrust_per_area.to("kN/m^2"): 5.1f~P}')Thrust coefficient = 1.60
Thrust / A_e = 1252.5 kN/m²
We can now examine the thrust for a range of exit areas:
exit_areas = Q_(np.linspace(0.1, 5, num=50), 'm^2')
plt.plot(exit_areas.to('m^2').magnitude, (thrust_per_area * exit_areas).to('kN').magnitude)
plt.xlabel('Nozzle exit area ' + r'(m$^2$)')
plt.ylabel('Thrust (kN)')
plt.grid(True)
plt.show()Example: conceptual design of a rocket¶
Now, let’s design a rocket for horizontal flight at an altitude of 10 km. The rocket must generate 100 kN of thrust for 5 seconds.
You are given this other information:
= 1500 m/s
= 1.2
MW = 25 kg/kmol
= 70 bar
(In reality, these would come from the propellant combination or experience, but these are typical values one might start a design using, if the propellant combination is not yet known.)
Find these design parameters:
Nozzle area ratio ()
Mass flow rate of propellant ()
Mass of propellant ()
Specific impulse () and total impulse ()
Nozzle throat diameter () and exit diameter ()
Chamber temperature ()
Sea-level thrust ()
Total impulse is defined as the integral of thrust over the burn time:
# given constants
altitude = Q_(10, 'km')
c_star = Q_(1500, 'm/s')
gamma = 1.2
MW = Q_(25, 'kg/kmol')
chamber_pressure = Q_(70, 'bar')
thrust_10k = Q_(100, 'kN')
burn_time = Q_(5, 's')First, we need to calculate the nozzle pressure ratio, which requires obtaining the pressure at the rocket’s flight altitude.
The 1976 U.S. Standard Atmosphere NOAA, NASA, and USAF, 1976 is a model for how pressure, temperature, density, etc., vary with altitude in the atmosphere, and provides reasonable answers for up to about 86 km.
The fluids package provides a convenient interface to this model in Python Bell, 2020, along with other models such as the NRLMSISE-00 model Picone et al., 2002, which applies up to 1000 km.
We can use this to obtain the pressure at the flight altitude, and calculate pressure ratio:
# use model for 1976 US Standard Atmosphere to get pressure at altitude
ten_km = ATMOSPHERE_1976(to_si(altitude))
exit_pressure = Q_(ten_km.P, 'Pa')
print(f'Pressure at {altitude.to("km")}: {exit_pressure: .0f}')
pressure_ratio = chamber_pressure / exit_pressure
print(f'Pressure ratio (p_0/p_e): {pressure_ratio.to_base_units(): .1f}')Pressure at 10 kilometer: 26500 pascal
Pressure ratio (p_0/p_e): 264.2 dimensionless
Next, we can calculate the nozzle area ratio and thrust coefficient. Since we are designing for operation at a specific altitude, the nozzle should be optimally expanded, or :
which are defined in Equations (34) and (24) above—and we have already written functions to evaluate them!
area_ratio = calc_area_ratio(gamma, pressure_ratio)
print(f'Area ratio: {area_ratio.to_base_units(): .2f~P}')
thrust_coeff = calc_thrust_coeff(gamma, pressure_ratio)
print(f'Thrust coefficient: {thrust_coeff.to_base_units(): .2f~P}')Area ratio: 25.10
Thrust coefficient: 1.75
Then, we can find the throat area and diameter using:
throat_area = thrust_10k / (thrust_coeff * chamber_pressure)
print(f'Throat area: {throat_area.to("m^2"): .4f~P}')
throat_diameter = 2 * np.sqrt(throat_area / np.pi)
print(f'Throat diameter: {throat_diameter.to("m"): .3f~P}')Throat area: 0.0082 m²
Throat diameter: 0.102 m
And, the exit area and exit diameter using
exit_area = area_ratio * throat_area
print(f'Exit area: {exit_area.to("m^2"): .4f~P}')
exit_diameter = 2 * np.sqrt(exit_area / np.pi)
print(f'Exit diameter: {exit_diameter.to("m"): .3f~P}')Exit area: 0.2051 m²
Exit diameter: 0.511 m
The mass flow rate is then
mass_flow_rate = chamber_pressure * throat_area / c_star
print(f'Mass flow rate: {mass_flow_rate.to("kg/s"): .2f~P}')Mass flow rate: 38.14 kg/s
And specific impulse can be found using
specific_impulse = c_star * thrust_coeff / Q_(constants.g, 'm/s^2')
print(f'Specific impulse: {specific_impulse.to("s"): .1f~P}')Specific impulse: 267.3 s
Assuming a constant thrust and mass flow rate over the burn time, the propellant mass is
propellant_mass = mass_flow_rate * burn_time
print(f'Propellant mass: {propellant_mass.to("kg"): .1f~P}')Propellant mass: 190.7 kg
The chamber temperature can be found by rearranging the expression:
# constant.R is given in J/(K mol), need J/(K kmol)
chamber_temperature = (
c_star**2 * gamma * (MW / Q_(constants.R, 'J/(K*mol)')) *
np.power(2 / (gamma+1), (gamma+1)/(gamma-1))
)
print(f'Chamber temperature: {chamber_temperature.to("K"): .1f~P}')Chamber temperature: 2845.4 K
And, finally, to find the thrust at sea level, we need to calculate a new thrust coefficient, since is now nonzero. We can just calculate the new term and modify the original calculation of .
# Thrust at sea level
thrust_coeff_sea_level = thrust_coeff + area_ratio * (
1/pressure_ratio - Q_(1, 'atm')/chamber_pressure
)
thrust_sea_level = thrust_coeff_sea_level * chamber_pressure * throat_area
print(f'Thrust at sea level: {thrust_sea_level.to("kN"): .1f~P}')Thrust at sea level: 84.7 kN
- NOAA, NASA, and USAF. (1976). U.S. Standard Atmosphere, 1976. https://ntrs.nasa.gov/citations/19770009539
- Bell, C. (2020). fluids: Fluid dynamics component of Chemical Engineering Design Library (ChEDL). https://github.com/CalebBell/fluids
- Picone, J. M., Hedin, A. E., Drob, D. P., & Aikin, A. C. (2002). NRLMSISE-00 empirical model of the atmosphere: Statistical comparisons and scientific issues. Journal of Geophysical Research: Space Physics, 107(A12), SIA 15-1-SIA 15-16. 10.1029/2002ja009430