4. 一维标量守恒律的通量格式
有限体积法的核心在于把守恒律写成“单元内守恒量变化 = 边界通量之差”的形式。对一维标量守恒律
∂u/∂t + ∂f(u)/∂x = 0, f(u) = a·u(线性对流,a 为常数波速)
在单元 i 上积分并离散时间后,得到半离散格式:
du_i/dt = -(F_{i+1/2} - F_{i-1/2}) / Δx
其中 F_{i+1/2} 是界面处的数值通量。不同格式的差别,全部体现在“如何用界面左右两侧的状态 u_L、u_R 计算 F”上。本节以线性对流方程 f(u)=a·u 为统一试验对象,推导三种经典通量格式,并说明如何用代码实现与验证。
三种数值通量的推导
Godunov 格式的思路是:在每个界面求解以 (u_L, u_R) 为初值的黎曼问题,取精确解在 x/t=0 处的通量。对线性对流,若 a>0,信息从左向右传播,界面通量只由左态决定,即 F = a·u_L;若 a<0,则 F = a·u_R。可统一写成 F = a·(a>0 ? u_L : u_R),等价于 F = a⁺u_L + a⁻u_R,其中 a⁺=max(a,0),a⁻=min(a,0)。Godunov 格式对线性问题是精确的迎风格式,单调、无振荡,但需要判断波速方向。
Roe 格式通过构造 Roe 平均状态把非线性问题线性化。对线性标量方程,Roe 平均波速就是 a 本身,Roe 通量为
F_Roe = 0.5·(f(u_L) + f(u_R)) - 0.5·|a|·(u_R - u_L)
它由中心平均通量减去耗散项构成,耗散系数 |a| 恰好等于最大特征速度。对线性问题,Roe 格式与 Godunov 格式完全等价,都是精确迎风;对非线性问题,Roe 格式需要熵修正才能避免非物理膨胀激波。
Lax-Friedrichs 格式采用全局最大波速 α = max|a| 作为耗散强度:
F_LF = 0.5·(f(u_L) + f(u_R)) - 0.5·α·(u_R - u_L)
它不区分波的传播方向,耗散量由 α 统一控制。对线性问题 α=|a|,因此 LF 与 Roe 形式相同;但对变系数或非线性问题,LF 的 α 取全局上界,耗散偏大,界面分辨率较低,却换来了极强的鲁棒性。
用一维线性对流方程实现并验证
取计算域 [0,1],周期边界,a=1,初值为方波:u₀(x)=1(0.2≤x≤0.4),其余为 0。网格数 N=100,CFL 数取 0.5,即 Δt=0.5·Δx/|a|。推进到 t=0.5 时,方波应整体右移 0.5,形状保持不变(周期域内回到原位置附近)。下面给出三种通量的统一实现骨架:
import numpy as np
N, a, cfl = 100, 1.0, 0.5
dx = 1.0 / N
dt = cfl * dx / abs(a)
x = (np.arange(N) + 0.5) * dx
u = np.where((x >= 0.2) & (x <= 0.4), 1.0, 0.0)
def flux_godunov(uL, uR, a):
return a * (uL if a > 0 else uR)
def flux_roe(uL, uR, a):
return 0.5*(a*uL + a*uR) - 0.5*abs(a)*(uR - uL)
def flux_lf(uL, uR, a, alpha):
return 0.5*(a*uL + a*uR) - 0.5*alpha*(uR - uL)
def step(u, flux_fn):
uL = u # 左态:单元 i
uR = np.roll(u, -1) # 右态:单元 i+1(周期)
F = flux_fn(uL, uR)
F_left = np.roll(F, 1) # 界面 i-1/2 的通量
return u - dt/dx * (F - F_left)
for n in range(int(0.5/dt)):
u = step(u, lambda L,R: flux_godunov(L,R,a))
验证时关注三点。第一,守恒性:周期域内总质量 Σu·Δx 应保持不变,三种格式都应满足到机器精度,这是有限体积法的基本要求。第二,形状保持:Godunov 与 Roe 在 CFL≤1 时对方波给出无振荡的陡峭界面;LF 由于耗散偏大,方波边缘会被抹平,出现明显数值扩散。第三,收敛性:加密网格(N=100→200→400)后,用 L1 误差对比,Godunov/Roe 应表现出一阶收敛(方波含间断,格式在间断处降为一阶),LF 同样一阶但误差常数更大。
格式选择的实践判断
三种格式的取舍可以归纳为:Godunov 适合需要精确迎风、追求单调性的标量问题,但推广到方程组需逐特征分解;Roe 以通量差分分裂的形式给出统一框架,便于推广到 Euler 方程组,代价是需要熵修正与特征值处理;Lax-Friedrichs 实现最简单、鲁棒性最好,适合作为复杂问题的兜底格式或初学验证的基准。实际代码中,建议先用 LF 跑通守恒性与边界处理,再切换到 Roe/Godunov 观察分辨率提升,并用总质量守恒与 L1 误差两条指标确认实现正确。