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 的 α 取全局上界,耗散偏大,界面分辨率较低,却换来了极强的鲁棒性。

三种数值通量的界面信息使用方式(a > 0 时) Godunov(迎风) 界面 i+1/2 u_L u_R 波向右传,只用左态 F = a·u_L 单调、无振荡 线性问题精确 Roe(近似黎曼) 界面 i+1/2 u_L u_R 中心平均 + |a| 耗散 F = ½(f_L+f_R) − ½|a|Δu 线性问题与 Godunov 等价 非线性需熵修正 Lax-Friedrichs 界面 i+1/2 u_L u_R 双向耗散,α = max|a| F = ½(f_L+f_R) − ½αΔu 鲁棒性强 耗散偏大、界面模糊

用一维线性对流方程实现并验证

取计算域 [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 的数值通量在数学上完全相同,代码中可互相验证;LF 只有在 α=|a| 时才与它们一致。因此本节示例中三者对线性问题的差异主要来自实现细节,真正体现格式差异应改用变系数或非线性通量(如 Burgers 方程)测试。

格式选择的实践判断

三种格式的取舍可以归纳为:Godunov 适合需要精确迎风、追求单调性的标量问题,但推广到方程组需逐特征分解;Roe 以通量差分分裂的形式给出统一框架,便于推广到 Euler 方程组,代价是需要熵修正与特征值处理;Lax-Friedrichs 实现最简单、鲁棒性最好,适合作为复杂问题的兜底格式或初学验证的基准。实际代码中,建议先用 LF 跑通守恒性与边界处理,再切换到 Roe/Godunov 观察分辨率提升,并用总质量守恒与 L1 误差两条指标确认实现正确。

实现时注意界面通量的索引一致性:F_{i+1/2} 由 u_i 与 u_{i+1} 计算,更新单元 i 时需同时用到 F_{i+1/2} 与 F_{i-1/2}。周期边界用 np.roll 处理最简洁,但若改为固定边界,必须在边界外设置虚拟单元并明确其取值,否则守恒性会被破坏。