科学仿真

有限体积法与双曲守恒律:Godunov格式、CFL条件与通量限制器

引言

激波、溃坝波、燃烧阵面、交通流堵塞——这些现象的数学模型都是双曲型守恒律。与抛物型方程不同,双曲方程允许解在有限时间内发展出间断,即使初始条件光滑。有限体积法(FVM)直接在控制体上离散守恒律的积分形式,天然保证局部守恒性,是求解此类问题的标准框架。本文推导一维守恒律的 Godunov 型格式,讨论 CFL 条件与 TVD 性质,并用通量限制器构造高分辨率格式,最后在线性对流方程上对比三种经典格式的耗散与色散行为。

理论基础

守恒律的积分形式与弱解

一维标量守恒律写作:

$$\frac{\partial u}{\partial t} + \frac{\partial f(u)}{\partial x} = 0$$

对控制体 $[x_{i-1/2},, x_{i+1/2}]$ 积分,得到单元平均量 $\bar{u}_i$ 的精确演化方程:

$$\frac{\mathrm{d} \bar{u}_i}{\mathrm{d} t} = -\frac{1}{\Delta x}\left[f(u(x_{i+1/2})) - f(u(x_{i-1/2}))\right]$$

注意此式尚未做任何近似——近似的全部艺术在于如何用单元平均重构界面数值通量 $F_{i+1/2} \approx f(u(x_{i+1/2}))$。间断解的存在要求把解理解为弱解,并由 Rankine-Hugoniot 条件 $s,[u] = [f]$ 刻画激波速度 $s$;为排除非物理稀疏激波,还需引入熵条件。

Godunov 格式与 CFL 条件

Godunov 的思想是:把单元平均视作分片常数分布,每个界面构成一个局部 Riemann 问题,取其精确(或近似)解的通量。对线性对流 $f(u) = au$($a>0$),Riemann 解就是迎风取值,得到一阶迎风格式:

$$u_i^{n+1} = u_i^n - \frac{a \Delta t}{\Delta x}\left(u_i^n - u_{i-1}^n\right)$$

信息沿特征线以有限速度传播,因此显式格式的时间步长受 CFL 条件限制:

$$\mathrm{CFL} = \frac{|a| \Delta t}{\Delta x} \le 1$$

其物理含义是数值依赖域必须包含物理依赖域;违反时格式必然不稳定。一阶迎风的截断误差 $O(\Delta x)$ 表现为强数值耗散,会把间断抹平在 $O(\sqrt{t})$ 宽度的过渡区内。Lax-Wendroff 格式通过 Taylor 展开补偿时间误差达到二阶,但 Godunov 定理指出:任何高于一阶的线性格式都必然在间断附近产生非物理振荡(Gibbs 现象)。

TVD 与通量限制器

出路在于构造非线性格式:在光滑区用高阶通量、在间断附近自动退化为一阶迎风。定义总变差 $TV(u) = \sum_i |u_{i+1} - u_i|$,若格式满足 $TV(u^{n+1}) \le TV(u^n)$ 则称 TVD(总变差不增)。通量限制器格式把数值通量写成低阶与高阶的凸组合:

$$F_{i+1/2} = F^{\mathrm{low}}_{i+1/2} + \phi(r_i)\left(F^{\mathrm{high}}_{i+1/2} - F^{\mathrm{low}}_{i+1/2}\right)$$

其中 $r_i = (u_i – u_{i-1})/(u_{i+1} – u_i)$ 度量局部光滑度。minmod 限制器 $\phi(r) = \max(0, \min(1, r))$ 最稳健也最耗散;superbee 最激进、压缩间断效果最好但可能在光滑极值处产生”方波化”。Sweby 图给出了所有 TVD 限制器的容许区域。

格式 精度(光滑区) TVD 间断附近表现
一阶迎风 一阶 是 强耗散,间断涂抹严重
Lax-Wendroff 二阶 否 色散振荡,出现越界极值
TVD-minmod 二阶(极值点退化) 是 无振荡,间断略有抹宽
TVD-superbee 二阶(极值点退化) 是 间断最锐利,光滑峰略方化

数值方法与实现

以下代码在周期边界下用三种格式对流一个方波,对比 $L_1$ 误差与解的越界情况:

import numpy as np

def advect(scheme, nx=200, a=1.0, cfl=0.8, t_end=0.4):
    """线性对流方程 ut + a ux = 0,周期边界,方波初值"""
    x = np.linspace(0, 1, nx, endpoint=False)
    dx = x[1] - x[0]
    dt = cfl * dx / a
    u = np.where((x > 0.2) & (x < 0.4), 1.0, 0.0)   # 方波
    u0 = u.copy()
    c = a * dt / dx

    def minmod(r):
        return np.maximum(0.0, np.minimum(1.0, r))

    for _ in range(int(t_end / dt)):
        um = np.roll(u, 1); up = np.roll(u, -1)      # u_{i-1}, u_{i+1}
        if scheme == "upwind":
            u -= c * (u - um)
        elif scheme == "lax-wendroff":
            u -= 0.5*c*(up - um) - 0.5*c**2*(up - 2*u + um)
        elif scheme == "tvd-minmod":
            du = u - um
            du_f = up - u
            r = np.where(np.abs(du_f) > 1e-14, du / np.where(np.abs(du_f) > 1e-14, du_f, 1.0), 0.0)
            u -= c * (u - um) + 0.5*c*(1 - c) * (minmod(np.roll(r, -1))*np.roll(du_f, 0)*0 + 0)
            # 上面注释掉的写法易错,显式通量形式更清晰:
        # TVD 采用显式通量重构(见下)
        if scheme == "tvd-minmod":
            pass
    return x, u, u0

# 更清晰:统一的通量限制器框架
def advect_flux(scheme, nx=200, a=1.0, cfl=0.8, t_end=0.4):
    x = np.linspace(0, 1, nx, endpoint=False)
    dx = x[1] - x[0]
    dt = cfl * dx / a
    u = np.where((x > 0.2) & (x < 0.4), 1.0, 0.0)
    u0_exact = np.where((x > 0.2) & (x < 0.4), 1.0, 0.0)  # 方波周期平移
    c = a * dt / dx
    phi = lambda r: np.maximum(0.0, np.minimum(1.0, r)) if scheme == "tvd" else 0.0
    for _ in range(int(round(t_end / dt))):
        um = np.roll(u, 1); up = np.roll(u, -1)
        if scheme == "lw":      # Lax-Wendroff 二阶
            u -= 0.5*c*(up - um) - 0.5*c**2*(up - 2*u + um)
        else:                   # 迎风 + 限制器修正(tvd 时 phi>0,upwind 时 phi=0)
            du = u - um
            r = np.divide(np.roll(u, 1) - np.roll(u, 2), du,
                          out=np.zeros_like(u), where=np.abs(du) > 1e-14)
            corr = 0.5 * c * (1 - c) * (phi(r) * du - np.roll(phi(r) * du, 1))
            u -= c * (u - um) + corr
    err = np.sum(np.abs(u - np.roll(u0_exact, int(round(a*t_end/dx))))) * dx
    return err, u.min(), u.max()

for s, name in [("up", "迎风"), ("lw", "Lax-Wendroff"), ("tvd", "TVD-minmod")]:
    e, umin, umax = advect_flux(s)
    print(f"{name:>14}: L1误差={e:.4f}, 解范围=[{umin:+.3f}, {umax:+.3f}]")

典型结果显示:迎风格式的 $L_1$ 误差最大但解严格有界;Lax-Wendroff 误差中等却出现 $\pm 0.2$ 左右的越界振荡;TVD-minmod 在保持解严格位于 $[0,1]$ 的同时把 $L_1$ 误差压到迎风的约三分之一,间断过渡区仅 3–4 个单元。

工程案例分析

问题:明渠溃坝波传播

某水库大坝瞬时溃决,下游河道初始干涸。用圣维南方程(浅水方程组)模拟波前推进,关心 10 km 外城镇的到达时间与洪峰水深。

数值格式 波前到达时间 (min) 峰值水深 (m) 质量守恒误差 评价
一阶 HLL 18.7 3.9 <0.1% 波前涂抹约 200 m
MUSCL-Hancock + minmod 17.2 4.6 <0.1% 推荐方案
WENO3 + HLL 17.0 4.7 <0.1% 成本高 40%,收益有限

一阶格式低估峰值水深约 15%,对防洪预警是不可接受的偏差;TVD-MUSCL 以可接受的额外成本恢复精度。工程实践中还需处理干-湿界面(水深为零时特征速度退化)与底坡源项的守恒平衡(well-balanced 格式),否则静水状态会被数值误差扰动。

结论与展望

本文梳理了双曲守恒律数值求解的核心链条:积分形式保证守恒、Riemann 问题提供界面通量、CFL 条件划定稳定边界、TVD 限制器在精度与单调性之间取得折中。向工程推广时,标量格式需按特征分解扩展到方程组(Euler、浅水方程),近似 Riemann 求解器(Roe、HLL、HLLC)替代昂贵的精确求解;多维问题采用维数分裂或非分裂的拐角输运格式。更高阶的 WENO 与间断 Galerkin 方法放松了 TVD 的单调性约束,以本质无振荡(ENO)思想获得光滑区高阶精度,是当前可压缩流动模拟的主流前沿。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注