Conservation Laws and Riemann Problems

Pdes

A CONSERVATION LAW is a PDE of the form

expressing that a conserved quantity evolves through a flux in the -direction. The integral form — — says that is neither created nor destroyed, only transported. For NONLINEAR , smooth initial data can develop SHOCKS in finite time (see method of characteristics), and the classical PDE breaks down. The theory of WEAK SOLUTIONS, the RIEMANN PROBLEM, and modern shock-capturing FINITE-VOLUME methods all start from this point.

Two canonical examples drive the theory:

Weak solutions

A classical (smooth) solution to ceases to exist once characteristics intersect. To handle the post-shock regime, we relax the requirement to WEAK SOLUTIONS: functions (allowed to have jumps) that satisfy the equation in the integral sense. Test against any smooth compactly-supported function :

A weak solution may have JUMP DISCONTINUITIES at curves . Across such a discontinuity, the Rankine-Hugoniot condition relates the jump in to the jump in flux and the shock speed:

For Burgers' equation specifically, this becomes — the shock moves at the AVERAGE of the values on either side.

The catch: Rankine-Hugoniot alone admits MULTIPLE weak solutions for the same initial data. Some are physical (compressive shocks); some are mathematical ghosts (expansive "shocks" that violate the second law). The ENTROPY CONDITION (Lax, Oleinik) selects the physical solution:

Equivalently: characteristics on BOTH SIDES must run INTO the shock. The physical-shock criterion is the vanishing-viscosity limit: solutions of as always satisfy the entropy condition. Numerical methods that produce the correct entropy solution are called ENTROPY-CONSISTENT.

The Riemann problem

A RIEMANN PROBLEM is the initial-value problem with piecewise-constant initial data:

This is the FUNDAMENTAL BUILDING BLOCK of modern numerical methods for conservation laws. For Burgers' equation, the Riemann solution is:

The two cases are RELATED but qualitatively different: shocks are discontinuous, rarefactions are smooth. The entropy condition picks the right one in each case. Notice the SELF-SIMILAR structure: the solution depends only on , a consequence of the scale invariance of Burgers' equation under .

Godunov's method

The seminal idea (Sergei Godunov, 1959). Discretize as PIECEWISE CONSTANTS on a finite-volume grid: in each cell , the value is the cell average. At each time step, between adjacent cells with values and , the local situation is exactly a RIEMANN PROBLEM with left state and right state . SOLVE that Riemann problem analytically, read off the flux at the cell interface, and update via:

The flux is the value of where is the Riemann solution at the cell interface. For Burgers:

The Godunov flux is the COARSEST upwind reconstruction — first-order accurate in space but FORMALLY ENTROPY-CONSISTENT because it solves the local Riemann problem exactly. Higher-order schemes (Lax-Wendroff, MUSCL, WENO) extend this by using piecewise-LINEAR or piecewise-POLYNOMIAL reconstruction within each cell while keeping the Riemann-flux idea at cell interfaces.

Code: Riemann problems for Burgers

# Godunov scheme for the inviscid Burgers equation u_t + (u²/2)_x = 0.
# Tests:
#   (1) Shock-tube Riemann problem (u_L = 1, u_R = 0) — exact shock at
#       speed s = (u_L + u_R)/2 = 0.5; shock position at T = 1 is x = 0.5.
#   (2) Rarefaction-wave Riemann problem (u_L = 0, u_R = 1) — exact
#       solution u(x,t) = max(0, min(x/t, 1)), a fan opening at the origin.

import numpy as np

def godunov_flux(uL, uR):
    """Exact-Riemann flux for f(u) = u²/2."""
    if uL > uR:                          # compressive: shock
        s = 0.5 * (uL + uR)
        return 0.5 * uL**2 if s > 0 else 0.5 * uR**2
    else:                                # expansive: rarefaction
        if uL >= 0: return 0.5 * uL**2   # supersonic right
        if uR <= 0: return 0.5 * uR**2   # supersonic left
        return 0.0                       # sonic point at u = 0

def godunov_burgers(u_init, T, cfl=0.4):
    N = len(u_init)
    h = 4.0 / (N - 1)                    # we'll use x in [-2, 2]
    u = u_init.copy()
    dt = cfl * h / max(np.max(np.abs(u)), 1e-6)
    nsteps = int(T / dt)
    dt = T / nsteps
    for _ in range(nsteps):
        F = np.array([godunov_flux(u[i], u[i+1]) for i in range(N - 1)])
        u_new = u.copy()
        u_new[1:-1] = u[1:-1] - (dt / h) * (F[1:] - F[:-1])
        u = u_new
    return u

N = 401
x = np.linspace(-2, 2, N)
T = 1.0

# ─── Test 1: shock-tube Riemann problem ────────────────────────────────
u0_shock = np.where(x < 0, 1.0, 0.0)
u_shock  = godunov_burgers(u0_shock, T)

# Locate the numerical shock as the midpoint between u > 0.95 and u < 0.05
idx_high = np.where(u_shock > 0.95)[0]
idx_low  = np.where(u_shock < 0.05)[0]
shock_num = 0.5 * (x[idx_high[-1]] + x[idx_low[0]])

print(f"(1) Shock-tube Riemann (u_L=1, u_R=0), T = {T}:")
print(f"    Analytical shock position:  x_s = 0.5")
print(f"    Numerical shock position:   x_s = {shock_num:.4f}")
print(f"    Error: {abs(shock_num - 0.5):.4f}")

# ─── Test 2: rarefaction-wave Riemann problem ──────────────────────────
u0_rare = np.where(x < 0, 0.0, 1.0)
u_rare  = godunov_burgers(u0_rare, T)
exact = np.clip(x / T, 0, 1)
exact[x < 0] = 0
err = np.max(np.abs(u_rare - exact))

print(f"\n(2) Rarefaction Riemann (u_L=0, u_R=1), T = {T}:")
print(f"    Exact solution: u(x,t) = max(0, min(x/t, 1))")
print(f"    ||u_num - u_exact||_inf = {err:.4f}")
print(f"    (Godunov resolves the fan; small error is from grid spacing.)")

Output:

(1) Shock-tube Riemann (u_L=1, u_R=0), T = 1.0:
    Analytical shock position:  x_s = 0.5
    Numerical shock position:   x_s = 0.4950
    Error: 0.0050

(2) Rarefaction Riemann (u_L=0, u_R=1), T = 1.0:
    Exact solution: u(x,t) = max(0, min(x/t, 1))
    ||u_num - u_exact||_inf = 0.0513
    (Godunov resolves the fan; small error is from grid spacing.)

Two outcomes. The shock-tube Riemann problem (compressive, ) produces a clean numerical shock at vs the analytical — 0.005 error from 401 grid points over , i.e. essentially exactly resolved at this discretization. The shock speed is captured exactly by Rankine-Hugoniot; the small position error is from the finite cell width. The rarefaction problem (expansive, ) produces a smooth fan profile, with max error 0.05 (5%) — first-order Godunov is somewhat diffusive at the fan edges, and higher-order reconstruction (MUSCL with limiters) would resolve this more sharply.

Beyond Burgers: systems of conservation laws

The scalar Burgers theory generalizes to SYSTEMS:

The Jacobian has real eigenvalues (assuming the system is HYPERBOLIC), each giving a characteristic FIELD. The Riemann problem decomposes into a sequence of waves — each either a shock, a rarefaction, or a contact discontinuity. The full Riemann solution is the composition of these.

For the EULER EQUATIONS (compressible gas dynamics with conserved variables ), the three characteristic fields are: a sound wave moving LEFT ( with the speed of sound), a CONTACT discontinuity moving at fluid speed , and a sound wave moving RIGHT (). The Riemann problem is the classical "shock tube": initial data with different gas states on the two sides produces a left-moving rarefaction, a middle contact, and a right-moving shock — the structure observed in shock-tube experiments and the standard CFD test case (the SOD shock tube).

Exact Riemann solvers for the Euler equations are non-trivial (involving a nonlinear iteration to find the pressure in the "star region" between the left and right waves), so most practical CFD codes use APPROXIMATE Riemann solvers — Roe, HLL, HLLC, AUSM — that capture the essential wave structure while being cheap to evaluate at each cell interface. The choice of approximate Riemann solver is a major tuning knob in production CFD codes.

The MUSCL / WENO upgrade

First-order Godunov is robust but diffusive. The MUSCL framework (van Leer 1979) extends it to second order by piecewise-LINEAR reconstruction within each cell, with SLOPE LIMITERS (minmod, van Leer, superbee, MC) that prevent spurious oscillations near discontinuities. WENO (weighted essentially non-oscillatory, Shu 1989) reaches arbitrarily high order via weighted polynomial reconstruction. Both are the standard tools for modern shock-capturing CFD: keep the Riemann-flux idea, just feed it more accurately-reconstructed states.

Connections