Supersonic Flow Can't See Upstream — Characteristics and Sound Waves in the Euler Equations
Why a small disturbance splits into three characteristic speeds
A supersonic fighter jet arrives before the sound of its own engine does. The roar only catches up after it has already passed overhead. Why this happens is, in fact, already written into the Euler equations. This post derives how a small disturbance splits into three separate signals. We'll trace the characteristic curves those signals draw in the plane (the tracks a signal travels along) in an interactive figure, and finish by pulling out the three characteristic speeds directly in Python.
The compressible equations are signal propagation#
One-dimensional inviscid compressible flow is written as three conservation laws.
Here is density, is velocity, is pressure, and is the total specific energy. On the surface these look like equations for a fluid that "flows." But the essence of these equations is not transport — it's signal propagation. All the dynamics of the flow decompose into a handful of signals, each running at a fixed speed. To see what those speeds are, we just need to linearize the equations for a moment.
A small disturbance builds a wave equation#
Lay a very small disturbance on top of a background state . Set and , use , and keep only first-order terms. Define the derivative that moves with the background, (the co-moving derivative). Combining the two disturbance equations yields a single wave equation.
Here is the speed of sound.
is the specific-heat ratio. Plugging into this equation drops out the dispersion relation.
is the angular frequency and is the wavenumber. The disturbance rides along with the background flow and spreads left and right at the sound speed . In other words, a disturbance at one point splits into two speeds: and .
Characteristics that split three ways#
The two sound waves aren't the whole story. The third signal is the fluid itself. Suppose we dye the fluid. If at we color the left side blue and the right side red, the color boundary is simply carried along with the flow.
This signal moves at speed . This is the entropy (or contact) characteristic. To summarize, a one-dimensional flow has three characteristic speeds.
The lines in the plane that satisfy are called characteristic curves. The are the sound waves, and is the transport of material. Along each acoustic characteristic the Riemann invariant stays constant, and along the material characteristic the entropy is conserved.
In the animation below, try adjusting the background flow yourself.
The blue tint marks the upstream region the left-going sound has reached. Push u₀ past 1 and the blue tint vanishes: the C− signal now has a positive speed, so every signal runs downstream and the source can no longer talk to what lies upstream.
With set to 0.4, the blue signal (the left-running sound wave) stretches to the left of the source — that is, upstream. Raise above 1 and that blue track disappears. Because has turned positive, all three signals now run only downstream.
Above Mach 1, the cone of influence tips over#
The same story is sharper on a spacetime diagram. The wedge between the three characteristic curves emanating from the source is the domain of influence. It's the set of spacetime events the source can send a signal to.
When , this wedge opens on both sides of the vertical line. A signal can travel either upstream or downstream. When , the whole wedge tips over to the right. Even the characteristic leans to the right, so no upstream event is inside the domain of influence. This is why, in supersonic flow, "upstream can't see downstream." Numerically, this is exactly the reason you must not impose a boundary condition at a supersonic outlet.
In the diagram below, drag the round probe around.
Drag the probe across the plane. At M = 0.4 the wedge straddles the vertical, so events both upstream and downstream are reachable. Slide u₀ above 1 and the whole wedge leans right — drop the probe anywhere to the left of the source and it stays red.
Place the probe inside the wedge and it turns green (reachable); place it outside and it turns red (unreachable). Raise above 1, and no matter where you put the probe to the left of the source, it stays red. Upstream is completely cut off from the signal.
Checking the characteristic speeds in Python#
Let's confirm that the characteristic speeds really are and . Writing the 1D Euler equations in primitive variables gives the quasilinear form , and the eigenvalues of the coefficient matrix are exactly the characteristic speeds.
import numpy as np
def characteristic_speeds(rho, u, p, gamma=1.4):
"""Jacobian A(W) of the 1D Euler primitive variables, W=(rho,u,p).
Eigenvalues of A = the three characteristic speeds."""
c = np.sqrt(gamma * p / rho) # speed of sound
A = np.array([[u, rho, 0.0 ],
[0.0, u, 1/rho],
[0.0, rho*c**2, u ]])
lam = np.sort(np.linalg.eigvals(A).real)
return c, lam
rho, u, p = 1.225, 220.0, 1.0e5 # air, u = 220 m/s
c, lam = characteristic_speeds(rho, u, p)
print(f"c0 = {c:8.2f} m/s")
print(f"eig(A) = {np.round(lam, 2)}")
print(f"u-c,u,u+c = {np.round([u-c, u, u+c], 2)}")
print(f"Mach = {u/c:5.3f} -> {'supersonic' if u > c else 'subsonic'}")The output is as follows.
c0 = 338.06 m/s
eig(A) = [-118.06 220. 558.06]
u-c,u,u+c = [-118.06 220. 558.06]
Mach = 0.651 -> subsonicThe eigenvalues match exactly. This time, let's actually advance a disturbance in time and measure the speed of its leading front. The acoustic Riemann invariants are scalars carried at respectively, so we can transport them separately with upwinding.
N, L = 400, 1.0
x = np.linspace(0, L, N, endpoint=False)
dx = L / N
rho0, c0 = 1.0, 1.0
u0 = 0.6 * c0 # background Mach 0.6
bump = np.exp(-((x - 0.5) ** 2) / (2 * 0.01 ** 2))
wp = bump.copy() # right-going invariant (u0 + c0)
wm = bump.copy() # left-going invariant (u0 - c0)
def upwind_step(w, a, dt):
if a > 0:
dwdx = (w - np.roll(w, 1)) / dx
else:
dwdx = (np.roll(w, -1) - w) / dx
return w - a * dt * dwdx
CFL = 0.4
dt = CFL * dx / (abs(u0) + c0)
steps = int(0.25 / dt)
for _ in range(steps): # time-advance loop
wp = upwind_step(wp, u0 + c0, dt)
wm = upwind_step(wm, u0 - c0, dt)
T = steps * dt
def front_speed(w):
return (x[np.argmax(w)] - 0.5) / T
print(f"u0+c0: target {u0 + c0:+.3f}, measured {front_speed(wp):+.3f}")
print(f"u0-c0: target {u0 - c0:+.3f}, measured {front_speed(wm):+.3f}")u0+c0: target +1.600, measured +1.600
u0-c0: target -0.400, measured -0.400The two fronts move at and respectively. The right-going invariant heads downstream, the left-going invariant heads upstream. Raise above and the measured value on the second line flips positive too.
What the characteristics leave us with#
- The dynamics of compressible flow are the propagation of signals running at three characteristic speeds, . Two are sound waves, one is the transport of material.
- The moment the domain-of-influence wedge crosses the vertical line is . Above it, no signal travels upstream.
- At a supersonic boundary, when all characteristics point one way, the number of boundary conditions you can impose is fixed accordingly. Getting into the habit of counting the signs of the characteristics keeps you from botching boundary conditions.
Share if you found it helpful.