Skip to content
cfd-lab:~/en/posts/2026-05-01-burgers-chara…● online

Translated from the author's Korean original. Read the original (한국어)

NOTE #028DAY FRI CFD기법DATE 2026.05.01UPDATED 2026.10.19READ 6 min read#Burgers#hyperbolic#shock#Characteristics#Numerical-Analysis

When Characteristics Collide, a Shock Is Born — Burgers and the (x,t) Plane

Shock speed is the arithmetic mean of the two states. A smaller left state means a rarefaction, not a shock.

In 1948 Burgers tossed out a one-line model of turbulence. A smooth curve becomes a cliff in finite time, with no external force. The birthday of that cliff is the instant characteristics (spacetime curves that carry information) cross. A weak solution — the integral form that survives after differentiability dies — cuts the overlap with one line.

Raise amplitude AA or wavenumber kk and the red dashed breaking time moves up.

q0(x) = 0.55 + A sin(2 pi k x)

That dashed line is the first time a crest line steps on the valley line ahead. Larger AA pulls the meeting forward. At k=2k = 2 the crossings come in pairs.

Two Lines Meet at One Point#

Hyperbolic equations are about signal propagation. As long as information flows along characteristics at fixed speeds, life is peaceful. For linear advection ∂tq+u ∂xq=0\partial_t q + u\,\partial_x q = 0 with constant uu, the characteristics are parallel straight lines and the initial profile simply slides sideways. There is no room for a shock in this picture.

Nonlinearity changes everything. When uu depends on qq itself, large values move fast and small values move slow. The instant a fast piece overtakes a slow piece, two values of qq arrive at the same (x,t)(x,t) point. The function becomes multi-valued — physically nonsense — and that nonsense is the birth of a shock.

A Self-Advecting Equation#

The conservative form of Burgers' equation reads:

∂tq+∂x(12q2)=0\partial_t q + \partial_x \left(\tfrac{1}{2} q^2\right) = 0

qq is the conserved scalar and f(q)=q2/2f(q) = q^2/2 is the flux. The chain rule gives the non-conservative form:

∂tq+q ∂xq=0\partial_t q + q\,\partial_x q = 0

The advection speed is u(x,t)=q(x,t)u(x,t) = q(x,t). The quantity transports itself. That single nonlinearity is the source of every drama to follow. The Jacobian ∂f/∂q=q\partial f/\partial q = q is positive where signals run rightward and negative where they run leftward. An initial profile whose sign varies in space — a sine wave is the canonical example — forces fast points to chase slow ones.

The (x,t) Plane Drawn by Characteristics#

A characteristic is a curve obeying dx/dt=qdx/dt = q. Since the comoving derivative Dq/DtDq/Dt vanishes on it, qq is frozen at its starting value along that curve. The characteristic is a straight line in the (x,t) plane with slope 1/q0(x0)1/q_0(x_0); the starting value q0(x0)q_0(x_0) is the line's velocity.

For the initial condition q0(x0)=0.55+Asin⁡(2πkx0)q_0(x_0) = 0.55 + A\sin(2\pi k x_0):

  • Lines launched from peaks (q0>0.55q_0 > 0.55) tilt sharply rightward (fast).
  • Lines launched from valleys (q0<0.55q_0 < 0.55) tilt gently (slow).

A peak line will eventually step on the toe of the valley line right ahead of it. The point where the two lines meet is the first shock.

Breaking Time t* — The First Collision#

Two neighboring characteristics from x0x_0 and x0+Δxx_0 + \Delta x meet at time tt when:

x0+q0(x0) t=(x0+Δx)+q0(x0+Δx) tx_0 + q_0(x_0)\,t = (x_0 + \Delta x) + q_0(x_0 + \Delta x)\,t

Taking the limit Δx→0\Delta x \to 0:

1+q0′(x0) t=0    ⟹    t=−1q0′(x0)1 + q_0'(x_0)\,t = 0 \;\;\Longrightarrow\;\; t = -\frac{1}{q_0'(x_0)}

Only negative slopes produce a meeting. The earliest collision happens where q0′(x0)q_0'(x_0) is most negative:

t∗=1max⁡x0(−q0′(x0))t^* = \frac{1}{\max_{x_0}\big(-q_0'(x_0)\big)}

Larger amplitude or higher wavenumber breaks faster. For the sine above, q0′(x0)=2πkAcos⁡(2πkx0)q_0'(x_0) = 2\pi k A\cos(2\pi k x_0), with minimum −2πkA-2\pi k A:

t∗=12πkAt^* = \frac{1}{2\pi k A}

With A=0.35A = 0.35, k=1k = 1, t∗≈0.455t^* \approx 0.455. Before that moment the curve smoothly skews. The instant t∗t^* is crossed, a vertical wall stands up.

The most negative slope is −2πkA-2\pi k A. Since 2π≈6.282\pi \approx 6.28, 2π×0.35≈2.202\pi \times 0.35 \approx 2.20, and the reciprocal is 0.4550.455. The crest is 0.55+0.35=0.900.55 + 0.35 = 0.90 and the valley is 0.200.20, so the initial jump is 0.700.70. That matches the later shock height of about 0.70.7. Out to t=1.2t = 1.2 the jump does not grow. Only the corners round off.

Push AA to 0.450.45 and the denominator rises to about 2.832.83, so t∗t^* drops near 0.350.35. Set the wavenumber to 22 as well and the denominator doubles, t∗≈0.227t^* \approx 0.227. Same amplitude, shorter wavelength, earlier crossing.

Weak Solutions and Rankine–Hugoniot — The Knife That Cuts#

Past t∗t^*, the straight reading hands you three values of qq at one xx — impossible. The fix is a weak solution: weaken the definition by dropping the differential form for the integral conservation law.

ddt∫xLxRq dx  =  f(q(xL))−f(q(xR))\frac{d}{dt}\int_{x_L}^{x_R} q\, dx \;=\; f\big(q(x_L)\big) - f\big(q(x_R)\big)

This makes sense even when qq is discontinuous. If two flat states qLq_L and qRq_R are joined by a shock moving at speed ss:

s=f(qL)−f(qR)qL−qR=qL+qR2s = \frac{f(q_L) - f(q_R)}{q_L - q_R} = \frac{q_L + q_R}{2}

This is the Rankine–Hugoniot condition. The shock speed is the arithmetic mean of the two states. The condition slices the multivalued region with a single line and arranges it so the two cut areas are equal — the equal-area rule.

A separate entropy condition is needed too. A shock is physical only when qL>qRq_L > q_R. If qL<qRq_L < q_R, the answer is a fan-shaped rarefaction. Violating this leaves you with a mathematically valid but unphysical expansion shock.

Plug 0.900.90 and 0.200.20 into s=(qL+qR)/2s = (q_L + q_R)/2 and s=0.55s = 0.55, the background of the sine. The shock slides at that mean speed and leaves only the two states. Reverse the order and the speed formula still returns 0.550.55. Only the answer changes, from shock to rarefaction. One arithmetic mean does not tell the two waves apart.

The mesh spacing is 1/256≈0.003911/256 \approx 0.00391. With CFL 0.40.4 and the initial max⁡∣q∣=0.90\max|q| = 0.90, Δt≈0.00174\Delta t \approx 0.00174. Reaching t=1.2t = 1.2 takes about 690690 steps. After the shock, a local spike in max⁡∣q∣\max|q| makes the same formula chop the step smaller. That knife at each face is the same one as in a shock tube in a single cell.

Python: From Tracing Characteristics to Godunov Shocks#

Trace characteristics and capture the weak shock with a first-order Godunov scheme.

import numpy as np
 
def burgers_breaking_time(q0_prime_min):
    # q0_prime_min: minimum of q0'(x) over x (must be negative for a shock)
    if q0_prime_min >= 0:
        return float('inf')
    return -1.0 / q0_prime_min
 
def trace_characteristics(q0_func, x0_arr, t_arr):
    # x[i, j] = x0_arr[i] + q0(x0_arr[i]) * t_arr[j]
    return x0_arr[:, None] + q0_func(x0_arr)[:, None] * t_arr[None, :]
 
def rankine_hugoniot_speed(qL, qR):
    # shock speed s = (qL + qR) / 2
    return 0.5 * (qL + qR)
 
def godunov_burgers_step(q, dx, dt):
    # conservation form: q^{n+1} = q^n - (dt/dx) * (F_{i+1/2} - F_{i-1/2})
    qL = q                          # left state at right interface
    qR = np.roll(q, -1)             # right state at right interface
    s = 0.5 * (qL + qR)
    f_shock = np.where(s >= 0, 0.5 * qL**2, 0.5 * qR**2)
    f_rare  = np.where(qL >= 0, 0.5 * qL**2,
              np.where(qR <= 0, 0.5 * qR**2, 0.0))
    F_iph = np.where(qL > qR, f_shock, f_rare)   # interface flux on the right
    F_imh = np.roll(F_iph, 1)                    # left interface = shift
    return q - (dt / dx) * (F_iph - F_imh)
 
# Simulation parameters --------------------------------------
N, L = 256, 1.0
A, k = 0.35, 1
x = (np.arange(N) + 0.5) * (L / N)
q0 = lambda xx: 0.55 + A * np.sin(2 * np.pi * k * xx)
q  = q0(x)
 
t_star = burgers_breaking_time(-2 * np.pi * k * A)
print(f"breaking time t* = {t_star:.3f}")
 
t_end, t = 1.2, 0.0
while t < t_end:
    dt = 0.4 * (L / N) / np.max(np.abs(q))    # CFL = 0.4
    q  = godunov_burgers_step(q, L / N, dt)
    t += dt
print(f"shock height (max - min) at t = {t:.2f}: {q.max() - q.min():.3f}")

You should see breaking time t* = 0.455 and a shock height of about 0.7. The Godunov scheme automatically slices the multivalued region into a single line — the knife is the line np.where(qL > qR, f_shock, f_rare). That one expression solves the exact Riemann problem at every cell interface and guarantees the weak solution.

Watch the Lines Collide#

In the figure above, the cyan curve and the faint initial dashes enclose equal areas on each side of the shock. That is the equal-area rule. At k=2k = 2 the two crossings soon merge, and the jump grows.

Equal areas mean that, after the cut, ∫q dx\int q\,dx still matches the flux difference at the two ends. If the shock walks at the mean of 0.900.90 and 0.200.20, which is 0.550.55, the two clipped triangles cancel. The height stays 0.700.70. Only the location moves at s=0.55s = 0.55.

Two Reasons Numerical Shock Capture Fails#

(1) CFL violation. Break Δt≤Δx/max⁡∣q∣\Delta t \le \Delta x / \max|q| and the numerical domain outruns the physical one. If amplitude appears to grow with time, suspect dtdt first. The code above recomputes dtdt every step from np.max(np.abs(q)), which matters even more once a shock forms because qq can spike locally.

(2) Wrong entropy. Rankine–Hugoniot alone admits unphysical expansion shocks. Simple linearized solvers like Roe make an "entropy glitch" in transonic rarefactions (where the fan crosses zero). Godunov, HLLE, and exact Riemann solvers enforce the entropy condition automatically, but Roe needs an explicit Harten-style entropy fix.

When a compressible LES code shows shock positions oscillating or negative pressures, almost always it is one of these two.

Whether a jam counts as a shock#

Cars pack into one lane and slow down. The one ahead is slow, the one behind is fast. The bumper contact is a crossing in the (x,t) plane. That head is a Burgers shock, and until it clears it drifts upstream at the Rankine–Hugoniot speed.

Hold the jam as a continuous curve and the multi-valued fold stays. Whether to cut it with one line or open it as a rarefaction is decided by which state is larger. If a step starts ringing, the first place to look is a flux limiter.

Share if you found it helpful.