Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Isentropic, variable-area flows

# Necessary modules to solve problems
import numpy as np
import pandas as pd
from scipy.optimize import root_scalar

%matplotlib inline
from matplotlib import pyplot as plt
Notebook Cell
# 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'

Varying-area adiabatic flows

Area change is one of the important factors that can adjust flow properties in compressible flow systems. (The others are friction and heat transfer, which we will cover briefly later.)

For now, let’s proceed to analyze flows with the following conditions/assumptions:

  • steady, one-dimensional flow

  • adiabatic: δq=0 \delta q = 0 , dse=0 d s_e = 0

  • no shaft work: δws=0 \delta w_s = 0

  • no or negligible change in potential energy: dz=0 dz = 0

  • no losses (i.e., reversible): dsi=0d s_i = 0

As a result of the flow being adiabatic and reversible, it is also isentropic: ds=0ds = 0.

Our goal is now to see how changes in pressure, density, and velocity relate with changing area.

Starting with the energy equation, apply our conditions:

δq=δws+dh+dV22+gdzdh=VdV\begin{gather*} \delta q = \delta w_s + dh + \frac{dV^2}{2} + g dz \\ dh = -V dV \end{gather*}

We also have our thermodynamic relationships derived from Gibbs’ identities:

Tds=dhdpρdh=dpρ\begin{gather*} T ds = dh - \frac{dp}{\rho} \\ dh = \frac{dp}{\rho} \end{gather*}

Combined together, we have

dV=dpρV  .dV = - \frac{dp}{\rho V} \;.

Substituting this, and the speed of sound (dp=a2dρ dp = a^2 d\rho ) into the continuity equation:

0=dρρ+dAA+dAAdpρ=V2(dρρ+dAA)dρρ=M2(dρρ+dAA)\begin{align*} 0 &= \frac{d\rho}{\rho} + \frac{dA}{A} + \frac{dA}{A} \\ \frac{dp}{\rho} &= V^2 \left( \frac{d\rho}{\rho} + \frac{dA}{A} \right) \\ \frac{d\rho}{\rho} &= M^2 \left( \frac{d\rho}{\rho} + \frac{dA}{A} \right) \end{align*}

we obtain a relationship between area change and density change:

dρρ=(M21M2)dAA  .\frac{d\rho}{\rho} = \left( \frac{M^2}{1-M^2} \right) \frac{dA}{A} \;.

Substituting this back into the continuity equation, we obtain a relationship between area change and velocity change:

dVV=(11M2)dAA  .\frac{dV}{V} = -\left( \frac{1}{1-M^2} \right) \frac{dA}{A} \;.

And, finally, recalling that dV=dp/ρV dV = -dp/\rho V , we substitute that into Equation (90) to get a relationsihp between area change and pressure change:

dp=ρV2(11M2)dAA  .dp = \rho V^2 \left( \frac{1}{1-M^2} \right) \frac{dA}{A} \;.

If we focus on situations where the pressure is decreasing (dp<0 dp < 0 ), going from a high-pressure reservoir to a low-pressure receiver, we can examine how changes in the other properties must occur.

Examining Equation (91), we see that dp<0 dp < 0 results in either a positive or negative area change for the subsonic and supersonic regimes:

()=11M2dAA  ,(-) = \frac{1}{1-M^2} \frac{dA}{A} \;,

so if M<1 M < 1 then dA<0 dA < 0 , and if M>1 M > 1 then dA>0 dA > 0 . Using Equations (89) and (90) we can see how changes in density and velocity occur in the different regimes as well:

M<1:dρρ=(+)()dρ<0M>1:dρρ=()(+)dρ<0\begin{gather*} M < 1: \quad \frac{d\rho}{\rho} = (+)(-) \rightarrow d\rho < 0 \\ M > 1: \quad \frac{d\rho}{\rho} = (-)(+) \rightarrow d\rho < 0 \end{gather*}

and

M<1:dVV=(+)()dV>0M>1:dVV=()(+)dV>0\begin{gather*} M < 1: \quad \frac{dV}{V} = -(+)(-) \rightarrow dV > 0 \\ M > 1: \quad \frac{dV}{V} = -(-)(+) \rightarrow dV > 0 \end{gather*}

In summary, for decreasing pressure (dp<0 dp < 0 ), we have:

propertyM<1 M < 1 M>1 M > 1
A A
ρ \rho
V V

This particular example shows how properties change in a nozzle, which converts pressure/enthalpy to kinetic energy. We can see that a subsonic nozzle has a converging shape (i.e., has a decreasing area) while a supersonic nozzle is diverging (with an increasing area).

In contrast, a diffuser converts kinetic energy into enthalpy/pressure, and is associated with increasing pressure (dp>0 dp > 0 ). A subsonic diffuser is diverging, while a supersonic diffuser is converging.

For propulsion applications, when we need to accelerate a gas from low speed to supersonic speeds, we will need a converging-diverging nozzle. This will be the focus of deeper analysis later.

Equations for perfect gases

Using our governing equations, we can generate working equations that apply to more-general flows of perfect/ideal gases, where losses (i.e., irreversibilities) may be present.

The flow assumptions are:

  • steady, one-dimensional flow

  • adiabatic

  • no shaft work

  • perfect/ideal gas

  • no, or negligible, potential energy changes

Our goal is to find relations between properties at two points in the flow that are only a function of the gas, Mach numbers at both locations, and entropy change between the two locations: f(M1,M2,γ,δs) f \left( M_1, M_2, \gamma, \delta s \right) .

Start with the continuity equation applied to a control volume, with flow entering at location 1 and leaving at location 2:

ρ1A1V1=ρ2A2V2\rho_1 A_1 V_1 = \rho_2 A_2 V_2
A2A1=ρ1V1ρ2V2=p1p2M1M2(T2T1)1/2  ,\rightarrow \frac{A_2}{A_1} = \frac{\rho_1 V_1}{\rho_2 V_2} = \frac{p_1}{p_2} \frac{M_1}{M_2} \left( \frac{T_2}{T_1} \right)^{1/2} \;,

where we got the final relation by using the ideal gas law (p=ρRT p = \rho R T ), Mach number definition (V=Ma V = M a ), and speed of sound expression in an ideal gas (a2=γRT a^2 = \gamma R T ).

We can simplify this expression even further by finding ways to express the pressure and temperature ratios as functions of the Mach numbers and gas properties.

For the temperature ratio, we can use conservation of energy and our expression for stagnation temperature:

ht1+q=ht2+wsTt1=Tt2Tt=T(1+γ12M2)\begin{gather*} h_{t1} + q = h_{t2} + w_s \\ \rightarrow T_{t1} = T_{t2} \\ T_t = T \left(1 + \frac{\gamma - 1}{2} M^2 \right) \\ \end{gather*}
T2T1=1+γ12M121+γ12M22\therefore \frac{T_2}{T_1} = \frac{1 + \frac{\gamma-1}{2} M_1^2}{1 + \frac{\gamma-1}{2} M_2^2}

For pressure, recall our relationship for the ratio of stagnation pressures between two locations:

pt2pt1=eΔs/Rpt=p(1+γ12M2)γ/(γ1)pt2pt1=p2p1[1+γ12M221+γ12M12]γ/(γ1)=eΔs/R\begin{gather*} \frac{p_{t2}}{p_{t1}} = e^{-\Delta s / R} \\ p_t = p \left(1 + \frac{\gamma - 1}{2} M^2 \right)^{\gamma / (\gamma-1)} \\ \frac{p_{t2}}{p_{t1}} = \frac{p_2}{p_1} \left[ \frac{1 + \frac{\gamma - 1}{2} M_2^2}{1 + \frac{\gamma - 1}{2} M_1^2} \right]^{\gamma / (\gamma-1)} = e^{-\Delta s / R} \end{gather*}
p1p2=[1+γ12M221+γ12M12]γ/(γ1)eΔs/R  .\therefore \frac{p_1}{p_2} = \left[ \frac{1 + \frac{\gamma - 1}{2} M_2^2}{1 + \frac{\gamma - 1}{2} M_1^2} \right]^{\gamma / (\gamma-1)} e^{\Delta s / R} \;.

Putting the expressions for p1p2 \frac{p_1}{p_2} and T2T1 \frac{T_2}{T_1} back into Equation (96), we can obtain a final expression for the area ratio between two locations as a function of the Mach numbers at the locations, the specific heat ratio of the gas, and any entropy increase between the locations:

A2A1=M1M2[1+γ12M221+γ12M12]γ+12(γ1)eΔs/R  .\frac{A_2}{A_1} = \frac{M_1}{M_2} \left[ \frac{1 + \frac{\gamma-1}{2} M_2^2}{1 + \frac{\gamma-1}{2} M_1^2} \right]^{\frac{\gamma+1}{2(\gamma-1)}} e^{\Delta s/R} \;.

Combining Equation (101) with (100) and (98), along with the stagnation relationships

Tt2=Tt1pt2pt1=eΔs/R  ,\begin{gather*} T_{t2} = T_{t1} \\ \frac{p_{t2}}{p_{t1}} = e^{-\Delta s / R} \;, \end{gather*}

we have relationships between properties at two locations in any steady, one-dimensional flow with varying area, as long as there is no heat transfer or shaft work. We could also combine Equations (100) and (98) with the ideal gas law to get a density ratio:

ρ2ρ1=[1+γ12M121+γ12M22]1/(γ1)eΔs/R  .\frac{\rho_2}{\rho_1} = \left[ \frac{1 + \frac{\gamma - 1}{2} M_1^2}{1 + \frac{\gamma - 1}{2} M_2^2} \right]^{1 /(\gamma-1)} e^{-\Delta s / R} \;.

Sonic reference state

If we know the Mach numbers at two locations, it is pretty easy to find the associated ratios of area, temperature, pressure, and/or density using Equations (98) through (103).

However, if we instead know the area ratio or the properties at the two locations, plus one Mach number, and want to find the unknown Mach number, these equations are a bit tougher to solve. We can solve them directly using numerical methods, as you will see in a bit, but we can also take advantage of a helpful concept to ease the calculations.

Similar to the stagnation reference state, or the state associated with decelerating a fluid flow to rest and zero potential, isentropically, we can introduce the sonic reference state, indicated using a * superscript. This is the state associated with either decelerating or accelerating a fluid to sonic velocity (i.e., Mach = 1.0) by some process; currently, that will be through isentropic area change.

This reference state may not physically exist in the system, but could through appropriate area change. As a result, they are legitimate locations in a flow system, and so we can use all of our developed equations to consider flow from a real location to this reference location.

In particular, we can write Equation (101) between the reference locations associated with two real locations: A1 A_1 and A1 A_1^* , then A2 A_2 and A2 A_2^* . By definition, the Mach numbers are 1 at the two reference states. So, the area ratio equation becomes:

A2A1=eΔs/R  .\frac{A_2^*}{A_1^*} = e^{\Delta s / R} \;.

This equation expresses how the area associated with reference state changes in a flow system in the presence of losses; for isentropic flow, the reference area A A^* remains constant.

Recall our relationship for the stagnation pressure ratio between two locations:

pt2pt1=eΔs/R  .\frac{p_{t2}}{p_{t1}} = e^{-\Delta s / R} \;.

If we take the product of these two equations, we get

pt1A1=pt2A2  ,p_{t1} A_1^* = p_{t2} A_2^* \;,

which gives us a quantity that is conserved in any adiabatic flow.

Isentropic relations

For isentropic flows, where Δs=0\Delta s = 0, Equation (101) reduces to

A2A1=M1M2(1+γ12M221+γ12M12)γ+12(γ1)  .\frac{A_2}{A_1} = \frac{M_1}{M_2} \left( \frac{1+\frac{\gamma-1}{2} M_2^2}{1+\frac{\gamma-1}{2} M_1^2} \right)^{\frac{\gamma+1}{2(\gamma-1)}} \;.

If we have M1M_1 and M2M_2, and know the ideal gas (and therefore γ\gamma) then finding the area ratio between two sections is straightforward using the equation. Working backwards takes slightly more effort, since the equation cannot simply be inverted.

Instead, it either needs to be solved numerically using a root-finding algorithm, or we can take advantage of the * (sonic) reference state to provide an easy way to solve problems.

Let’s apply Equation (107) between any point in the flow and its sonic reference state, such that A2AA_2 \rightarrow A (so M2MM_2 \rightarrow M) and A1AA_1 \rightarrow A^* (and M11M_1 \rightarrow 1):

AA=1M(1+γ12M2γ+12)γ+12(γ1)  ,\frac{A}{A^*} = \frac{1}{M} \left( \frac{1+\frac{\gamma-1}{2} M^2}{\frac{\gamma+1}{2}} \right)^{\frac{\gamma+1}{2(\gamma-1)}} \;,

which shows that AA=f(γ,M)\frac{A}{A^*} = f(\gamma, M). For a given value of γ\gamma (such as 1.4, used for air), it is easy to precalculate and tabulate the reference area ratio vs. values of Mach number. Similarly, the quantities p/ptp/p_t, T/TtT/T_t, and pA/ptApA/p_t A^* can be tabulated as well:

ppt=(1+γ12M2)γ/(γ1)\frac{p}{p_t} = \left(1 + \frac{\gamma-1}{2} M^2 \right)^{-\gamma/(\gamma-1)}
TTt=(1+γ12M2)1\frac{T}{T_t} = \left(1 + \frac{\gamma-1}{2} M^2 \right)^{-1}

For example, let’s tabulate the values from M=M = 0 to 0.3:

gamma = 1.4
machs = np.arange(0, 0.31, 0.01)
# hiding divide by zero warning
with np.errstate(divide='ignore'):
    area_ratios = (
        (1.0/machs)*(2*(1.0 + 0.5*(gamma-1)*machs**2)/(gamma+1))**(0.5*(gamma+1)/(gamma-1))
        )
pressure_ratios = (1.0/(1 + 0.5*(gamma-1)*machs**2))**(gamma/(gamma-1))
temperature_ratios = 1.0 / (1 + 0.5*(gamma-1)*machs**2)
df = pd.DataFrame({
    r'$M$': machs, r'$p/p_t$': pressure_ratios, r'$T/T_t$': temperature_ratios,
    r'$A/A^*$': area_ratios
    })
df.style.\
    hide_index().\
    format(formatter={(r'$M$'): "{:.2f}"})
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[3], line 15
     11     r'$M$': machs, r'$p/p_t$': pressure_ratios, r'$T/T_t$': temperature_ratios,
     12     r'$A/A^*$': area_ratios
     13     })
     14 df.style.\
---> 15     hide_index().\
     16     format(formatter={(r'$M$'): "{:.2f}"})

AttributeError: 'Styler' object has no attribute 'hide_index'

With tabulated data like this, we can then solve problems by constructing an appropriate property ratio based on known information and looking up the corresponding Mach number.

For example, given a gas (γ\gamma), the areas at two locations (A1A_1 and A2A_2), and the Mach number at one location (M1M_1), we can find the Mach number at the second location (M2M_2) by constructing the reference area ratio:

A2A2=A2A1A1A1A1A2  ,\frac{A_2}{A_2^*} = \frac{A_2}{A_1} \frac{A_1}{A_1^*} \frac{A_1^*}{A_2^*} \;,

where A2A2=f(M2)\frac{A_2}{A_2^*} = f(M_2), A2A1\frac{A_2}{A_1} is known, A1A1=f(M1)\frac{A_1}{A_1^*} = f(M_1), and A1A2=1.0\frac{A_1^*}{A_2^*} = 1.0 for isentropic flow between the two sections. So, we can find M2M_2.

To determine which Mach number is the correct solution, you need to apply other knowledge and problem context. For example, if the initial Mach number is subsonic and the two sections are only connected by a diverging or converging area duct, then the second Mach number must also be subsonic.

Example: isentropic flow problem

Problem: Air flows isentropically through a duct (γ=1.4\gamma = 1.4) where the area is changing from point 1 to 2, with no heat transfer or shaft work. The area ratio is A2A1=2.5\frac{A_2}{A_1} = 2.5, the flow starts at M1=0.5M_1 = 0.5 and 4 bar. Find the Mach number and pressure at the second point in the duct.

We can solve this using the classical approach (pre-calculated isentropic tables) or a numerical approach; both follow the same general approach:

  1. Find M2M_2 associated with the area ratio A2/A2A_2 / A_2^*, then

  2. Use that to find the stagnation pressure ratio p2/pt2p_2 / p_{t2}.

A2A2=A2A1A1A1A1A2  ,\frac{A_2}{A_2^*} = \frac{A_2}{A_1} \frac{A_1}{A_1^*} \frac{A_1^*}{A_2^*} \;,

where A2A1=2.5\frac{A_2}{A_1} = 2.5 is given, we can find A1A1\frac{A_1}{A_1^*} using

AA=1M(1+γ12M2γ+12)γ+12(γ1)  ,\frac{A}{A^*} = \frac{1}{M} \left( \frac{1 + \frac{\gamma - 1}{2} M^2}{\frac{\gamma+1}{2}} \right)^{\frac{\gamma+1}{2(\gamma-1)}} \;,

(either by calculating or looking up in the γ=1.4\gamma = 1.4 table) and A1A2=1\frac{A_1^*}{A_2^*} = 1 because the flow is isentropic.

gamma = 1.4
mach_1 = 0.5

A2_A1 = 2.5
A1star_A2star = 1.0 # isentropic

A1_A1star = (1.0/mach_1) * (
    (1 + 0.5*(gamma-1)*mach_1**2) / ((gamma + 1)/2)
    )**((gamma+1) / (2*(gamma-1)))
print(f'A1/A1^* = {A1_A1star:.4f}')
A2_A2star = A2_A1 * A1_A1star * A1star_A2star
print(f'A2/A2star = {A2_A2star:.4f}')

We can then find M2M_2, because A2A2=f(M2)\frac{A_2}{A_2*} = f(M_2). Our options are to use the γ=1.4\gamma = 1.4 tables and interpolate, or solve the associated equation numerically.

Option 1: using tables

We can find in the tables that:

  • at M=0.17M=0.17, A/A=3.46351A/A^* = 3.46351

  • at M=0.18M = 0.18, A/A=3.27793A/A^* = 3.27793

and interpolate to find the precise M2M_2:

machs = np.array([0.17, 0.18])
areas = np.array([3.46351, 3.27793])
mach_2 = (
    machs[0] * (areas[1] - A2_A2star) + machs[1] * (A2_A2star - areas[0])
    ) / (areas[1] - areas[0])
print(f'M2 = {mach_2:.4f}')

This is probably sufficient, but we could get a more-accurate result by interpolating using more points and using the numpy.interp() function:

machs = np.array([0.15, 0.16, 0.17, 0.18, 0.19])
areas = np.array([3.91034, 3.67274, 3.46351, 3.27793, 3.11226])

mach_2 = np.interp(A2_A2star, areas[::-1], machs[::-1])
print(f'M2 = {mach_2:.4f}')

Note that we have to reverse the order of the values, since interp expects the x-values to be increasing. Also, we could easily generate these values ourselves for a different value of γ\gamma, but it is likely easier to just solve the equation directly in that case

Option 2: solving the equation

Alternately, we can solve the A2A2=f(M2,γ)\frac{A_2}{A_2*} = f(M_2, \gamma) equation directly using scipy.optimize.root_scalar. We need to give the function initial guesses x0 and x1 that are subsonic, to ensure we get a subsonic solution.

def area_function(mach, gamma, area_ratio):
    '''Function for area ratio, solving for M2'''
    return (
        area_ratio - (
            (1.0/mach) * ((1 + 0.5*(gamma-1)*mach**2) / 
            ((gamma + 1)/2))**((gamma+1) / (2*(gamma-1)))
            )
        )

sol = root_scalar(area_function, args=(gamma, A2_A2star), x0=0.1, x1=0.5)
print(f'M2 = {sol.root:.4f}')

Option 3: solving the full equation

As a third option, we could actually bypass the ratio approach and just numerically solve the full Equation (101) directly, using the known A2/A1 A_2/A_1 , M1 M_1 , γ \gamma , and Δs=0 \Delta s = 0 !

def full_area_function(mach2, mach1, area_ratio, gamma=1.4, delta_s=0.0, R=287.0):
    '''Function for full area ratio equation, solving for M2'''
    # gamma, delta_s, and R have default values
    return (
        area_ratio - (mach1 / mach2) * (
            (1 + 0.5*(gamma-1)*mach2**2) / (1 + 0.5*(gamma-1)*mach1**2)
            )**((gamma+1) / (2*(gamma-1))) * np.exp(-delta_s / R)
        )

sol = root_scalar(full_area_function, args=(mach_1, A2_A1, gamma, 0.0), x0=0.1, x1=0.5)
print(f'M2 = {sol.root:.4f}')

This approach is the most direct.

Example: isentropic flow (using Matlab)

If you prefer to use Matlab, we can also solve the same example using the three different strategies.

gamma = 1.4;
mach_1 = 0.5;

A2_A1 = 2.5;
A1star_A2star = 1.0;

A1_A1star = (1.0/mach_1)*((1+0.5*(gamma-1)*mach_1^2)/((gamma+1)/2))^((gamma+1)/(2*(gamma-1)));
fprintf('A1/A1^* = %.4f\n', A1_A1star)

A2_A2star = A2_A1 * A1_A1star * A1star_A2star;
fprintf('A2/A2star = %.4f\n', A2_A2star)
A1/A1^* = 1.3398
A2/A2star = 3.3496

Option 1: using the tables

We find in the tables that:

  • at M=0.17M=0.17, A/A=3.46351A/A^* = 3.46351

  • at M=0.18M = 0.18, A/A=3.27793A/A^* = 3.27793

and linearly interpolate to find the precise M2M_2:

machs = [0.17, 0.18];
areas = [3.46351, 3.27793];
mach_2 = (machs(1)*(areas(2)-A2_A2star) + machs(2)*(A2_A2star-areas(1)))/(areas(2)-areas(1));
fprintf('M2 = %.4f\n', mach_2)
M2 = 0.1761

We can also use Matlab’s built-in interp1 function to interpolate with more data points:

machs = [0.15, 0.16, 0.17, 0.18, 0.19];
areas = [3.91034, 3.67274, 3.46351, 3.27793, 3.11226];

mach_2 = interp1(areas, machs, A2_A2star);
fprintf('M2 = %.4f\n', mach_2)
M2 = 0.1761

Option 2: solving the equation

Alternately, we can solve the A2A2=f(M2,γ)\frac{A_2}{A_2*} = f(M_2, \gamma) equation directly using the function fzero:

f = @(M) (A2_A2star - ((1.0/M)*((1+0.5*(gamma-1)*M^2) ...
    / ((gamma+1)/2))^((gamma+1)/(2*(gamma-1)))));

M2 = fzero(f, 0.1);
fprintf('M2 = %.4f\n', M2)
M2 = 0.1760

Note that, in this example, the values of γ\gamma and A2/A2A_2 / A_2^* are hard-coded into the function.

Option 3: solving the full equation

As a third option, we can bypass the ratio approach and just numerically solve the full Equation (101) directly, using the known A2/A1 A_2/A_1 , M1 M_1 , γ \gamma , and Δs=0 \Delta s = 0 !

To do this, we create two functions: one that depends on all the variables, and a parameterized function that only depends on M2M_2.

fun = @(M2, M1, A2_A1, gamma, delta_s, R) (A2_A1 - (M1/M2)*( ...
      (1 + 0.5*(gamma-1)*M2^2) / (1 + 0.5*(gamma-1)*M1^2) ...
            )^((gamma+1)/(2*(gamma-1)))*exp(-delta_s/R));

% parameterized function
f = @(M2) fun(M2, mach_1, A2_A1, gamma, 0.0, 287);
M2 = fzero(f, 0.1);
fprintf('M2 = %.4f\n', M2)
M2 = 0.1760