Skip to content
cfd-lab:~/zh/posts/2026-08-09-tv-flux-split…● online

本文译自作者的韩语原文。 阅读原文(한국어)

NOTE #125DAY SUN 논문리뷰DATE 2026.08.09UPDATED 2026.10.19READ 7 min read#Flux-Splitting#Baer-Nunziato#Riemann-Solver#Compressible#Multiphase#Paper-Review

[论文评述] 把对流和压力彻底拆开 — Baer–Nunziato flux 的 TV 分裂

Toro Test 4上密度L1大约比Rusanov小1.5倍,可Noh壁面第三步压力就到了−0.12。

Tokareva 和 Toro 2016 年的这篇论文,看起来三十分钟就能复现完。把 flux 一分为二,各自单独计算再相加,仅此而已。实现确实只用了 40 行。在两道强激波对撞的 Riemann 问题上,它比 Rusanov flux 精确 1.5 倍。然后换到 Noh 问题,第三步压力就变成了 −0.12。今天就用代码追一遍:这个分裂究竟做了什么,白拿到了什么,又在哪里付账。

把 a 滑块从 0.1 拉到 2.0,只有右边压力系统的扇形张开,中间对流系统的扇形角度不动。

Push a → 2.0 and watch: the outer fan of the middle panel does not move at all, while the right panel opens up. Every bit of the acoustic time-step restriction sits in the P-system.

看 |λ|max 条在哪一块面板变长,声学 CFL 就落在压力系统那一侧。把 ū 推到 ±2,下方标记会离开论文里的亚声速假定。

论文如何切开 flux#

  • 作者: S. A. Tokareva, E. F. Toro
  • 题目: A flux splitting method for the Baer–Nunziato equations of compressible two-phase flow
  • 出处: Journal of Computational Physics 323 (2016) 45–74
  • DOI: 10.1016/j.jcp.2016.07.019

一句话概括:把 Toro–Vázquez(TV) flux 分裂推广到 Baer–Nunziato(BN,给两相各自速度和压力的七方程模型)方程,只求解拆出来的廉价压力子系统,再拼出整体 flux。

BN 方程写不成守恒形式。一维 x-分裂形式把非守恒项单独带在身上。

∂tQ+∂xF(Q)+T(Q) ∂xαˉ=0\partial_t \mathbf{Q} + \partial_x \mathbf{F}(\mathbf{Q}) + \mathbf{T}(\mathbf{Q})\,\partial_x \bar\alpha = 0

Q\mathbf{Q} 是七个守恒量,F\mathbf{F} 是守恒 flux,T ∂xαˉ\mathbf{T}\,\partial_x\bar\alpha 是挂在体积分数梯度上的非守恒项。带横杠(¯)的是固相,不带的是气相。

论文的起手式,是把 F\mathbf{F} 的守恒部分切成两块。只写气相三个分量:

A=(αρuαρu212αρu3),P=(0αpαu(ρe+p))\mathbf{A} = \begin{pmatrix} \alpha\rho u \\ \alpha\rho u^2 \\ \tfrac12 \alpha\rho u^3 \end{pmatrix}, \qquad \mathbf{P} = \begin{pmatrix} 0 \\ \alpha p \\ \alpha u(\rho e + p) \end{pmatrix}

A\mathbf{A} 里一个压力项都没有。P\mathbf{P} 里一个 ρu\rho u 对流项都没有。两者相加,精确还原原来的 Euler flux。对流系统(A-system)与压力系统(P-system)由此而来,非守恒项归到压力系统一侧。

∂tQ+∂xA(Q)=0,∂tQ+∂xP(Q)+T(Q) ∂xαˉ=0\partial_t \mathbf{Q} + \partial_x \mathbf{A}(\mathbf{Q}) = 0, \qquad \partial_t \mathbf{Q} + \partial_x \mathbf{P}(\mathbf{Q}) + \mathbf{T}(\mathbf{Q})\,\partial_x \bar\alpha = 0

切开之后,声速只留在一侧

拆分的收益,看各子系统的特征速度就清楚了。对流系统的 Jacobian 第三列为零,因此特征值是 {0,u,u}\{0, u, u\}。没有声速。压力系统对原始变量 (ρ,u,p)(\rho, u, p) 给出

λ1,3=12(u∓A),λ2=0,A=u2+4hρep\lambda_{1,3} = \tfrac12\left(u \mp A\right), \quad \lambda_2 = 0, \qquad A = \sqrt{u^2 + \frac{4h}{\rho e_p}}

hh 是比焓,ep=∂e/∂pe_p = \partial e/\partial p。代入理想气体:ρep=1/(γ−1)\rho e_p = 1/(\gamma-1),于是 h/(ρep)=γp/ρ=a2h/(\rho e_p) = \gamma p/\rho = a^2,即 A=u2+4a2A = \sqrt{u^2 + 4a^2}。固相的 stiffened EOS 同样化为 Aˉ=uˉ2+4aˉ2\bar A = \sqrt{\bar u^2 + 4\bar a^2}。

代入 u=0u = 0 得到 λ1,3=∓a\lambda_{1,3} = \mp a。也就是说,声速整个落在压力系统里。锁住时间步长的不是对流系统。

锁住时间步的 ∣λ∣|\lambda| 在哪一侧,上面面板的 |λ|max 已经拆开给看了。u=0u=0 时 λ1,3=∓a\lambda_{1,3}=\mp a,所以变长的那根条才是声速。

BN 只多加了一条 λ₇ = ū#

气相、固相各三个共六个,再加上第七个特征值 λ7=uˉ\lambda_7 = \bar u。论文展开特征向量得到的结论在工程上很关键:体积分数 αˉ\bar\alpha 只在穿过 λ7\lambda_7 场时跳变。气相密度穿过 λ1,3\lambda_{1,3} 不变,气相压力在 λ2\lambda_2 上为常数。

这让 Godunov 状态的采样变便宜。假定亚声速配置 SL<uˉ<SRS_L < \bar u < S_R,则 λ1<λ2=0<λ3\lambda_1 < \lambda_2 = 0 < \lambda_3 恒成立,界面状态仅由 λ7=uˉ\lambda_7 = \bar u 的符号决定。也不需要熵修正。这正是论文声称节省 CPU 时间之处。上面模拟底部的绿/红标记会实时检查该亚声速条件;把 ū 推到 ±2,就走出了论文覆盖的范围。

体积分数的跳跃只搭在 λ7\lambda_7 上,这和把非守恒项挂错波之后压力立刻乱跳的 Abgrall 准则 是同一件事。亚声速标记变红的地方,就是这条假定的边界。

两条直线搞定的 P-system Riemann solver#

压力系统非线性波上的广义 Riemann 不变量是:

dρ0=du2=dpρ(u−A)\frac{d\rho}{0} = \frac{du}{2} = \frac{dp}{\rho(u - A)}

第一个等号给出 ρ=const\rho = \text{const}。剩下的 ODE du/dp=2/[ρ(u−A)]du/dp = 2/[\rho(u-A)],论文把系数冻结在特征线的脚上来线性化。固定 CL=ρL(uL−AL)C_L = \rho_L(u_L - A_L),积分就塌缩成一条直线。

pL∗=pL+12CL(u∗−uL),pR∗=pR+12CR(u∗−uR)p_L^{*} = p_L + \tfrac12 C_L\left(u^{*} - u_L\right), \qquad p_R^{*} = p_R + \tfrac12 C_R\left(u^{*} - u_R\right)

其中 CR=ρR(uR+AR)C_R = \rho_R(u_R + A_R)。两条直线联立,无需迭代即得闭式解:

u∗=2(pR−pL)+CLuL−CRuRCL−CRu^{*} = \frac{2(p_R - p_L) + C_L u_L - C_R u_R}{C_L - C_R}

由于 A>∣u∣A > |u|,恒有 CL<0<CRC_L < 0 < C_R。分母在结构上不可能为零。也就是说没有地方需要放除零保护,实际上也确实不必放。

不过这个线性化有多粗,得亲眼看。在下面拖动左右状态。

Slide p_L down to ~1.1 and the two star states nearly coincide (~2%). At the default ratio of 3 the gap is already ~12%; push p_L to 40 and it passes 65% — the tangent never reaches where the curve went.

白色曲线是用 RK4 老老实实积分的不变量,虚线是论文真正使用的切线。把 p_L/p_R 降到 1.1,两个星状态在 2% 以内重合。默认比值 3 时已经是 12%,把 p_L 提到 40 则张开到 67%。切线到不了曲线去的地方。

代码 — 40 行结束的 TV flux#

缩到理想气体单相来实现。压力系统状态不需要 ρ∗\rho^*,这点很省事:因为 ρe=p/(γ−1)\rho e = p/(\gamma-1),P-flux 第三个分量整理成 γ/(γ−1) u∗p∗\gamma/(\gamma-1)\,u^* p^*。

import numpy as np
 
def prim(q, g):
    rho = q[0]; u = q[1] / rho
    return rho, u, (g - 1.0) * (q[2] - 0.5 * rho * u * u)
 
def pressure_wave_speed(rho, u, p, g):
    """A = sqrt(u^2 + 4h/(rho e_p)) -> sqrt(u^2 + 4a^2)   [论文 eq. 9]"""
    return np.sqrt(u * u + 4.0 * g * p / rho)
 
def advection_flux(rho, u):
    """A(Q): 一个压力项都没有   [论文 eq. 5]"""
    return np.array([rho * u, rho * u * u, 0.5 * rho * u ** 3])
 
def p_system_star(rhoL, uL, pL, rhoR, uR, pR, g):
    """压力系统的线性化 Riemann solver   [论文 eq. 15, 19]"""
    CL = rhoL * (uL - pressure_wave_speed(rhoL, uL, pL, g))   # 恒 < 0
    CR = rhoR * (uR + pressure_wave_speed(rhoR, uR, pR, g))   # 恒 > 0
    us = (2.0 * (pR - pL) + CL * uL - CR * uR) / (CL - CR)
    return us, pL + 0.5 * CL * (us - uL)
 
def tv_face_flux(qL, qR, g):
    rhoL, uL, pL = prim(qL, g)
    rhoR, uR, pR = prim(qR, g)
    us, ps = p_system_star(rhoL, uL, pL, rhoR, uR, pR, g)
    # 对流系统特征值是 {0, u, u} -> 一个速度符号就定完迎风
    fa = advection_flux(rhoL, uL) if us >= 0.0 else advection_flux(rhoR, uR)
    # 压力系统: rho e = p/(g-1),所以根本不需要 rho*
    fp = np.array([0.0, ps, g / (g - 1.0) * us * ps])
    return fa + fp

在 Toro Test 4 上跟 Rusanov 对打#

玩具问题选了两道强激波迎面对撞的 Toro Test 4。左 (ρ,u,p)=(5.99924, 19.5975, 460.894)(\rho,u,p) = (5.99924,\ 19.5975,\ 460.894),右 (5.99242, −6.19633, 46.0950)(5.99242,\ -6.19633,\ 46.0950),γ=1.4\gamma = 1.4,t=0.035t = 0.035,CFL 0.9。没有滞止区,迎风方向不会摇摆。精确解另外实现了 Toro 的迭代解法,得到 p∗=1691.647p^* = 1691.647、u∗=8.6898u^* = 8.6898。

NNTV 分裂 L1(ρ)L_1(\rho)Rusanov L1(ρ)L_1(\rho)TV 收敛率
1000.95231.4303—
2000.59360.93490.68
4000.38790.61870.61
8000.26950.42170.53

同样一阶精度、同一成本量级,TV 分裂稳定地精确 1.5 倍。有意思的是前面看到的星状态误差:此处压力比为 10,压力系统星状态偏离精确值 65%。可格式照样赢。一阶 FV 里主导误差是 O(Δx)O(\Delta x) 抹平,而 P-flux 不需要准确,只要相容且耗散就够了。

在 Noh 问题上分道扬镳的地方#

换到 Noh 问题。γ=5/3\gamma = 5/3,ρ=1\rho = 1、u=−1u = -1 向壁面涌来,精确解是 ρ=4\rho = 4、p=4/3p = 4/3、u=0u = 0,激波立在 x=t/3x = t/3。整个波后区域都是 u≈0u \approx 0 的滞止区。

TV 分裂以两种方式死掉。若按 u∗u^* 的符号对对流 flux 迎风,N=200N = 200 时第 3 步、距壁第二个单元出现 p=−0.12p = -0.12。改按 12(uL+uR)\tfrac12(u_L + u_R) 的符号迎风则能活下来,但网格越细,密度锯齿越严重。

NNTV 分裂 TV(ρ)\mathrm{TV}(\rho)Rusanov TV(ρ)\mathrm{TV}(\rho)
10012.010.445
20014.180.194
40031.380.104
80034.450.072

TV(ρ)\mathrm{TV}(\rho) 是波后区域密度的全变差。网格加密 8 倍,Rusanov 降为六分之一,TV 分裂却涨了三倍。它不收敛。平均值本身两边都对——TV 是 ρˉ=4.11\bar\rho = 4.11,Rusanov 是 3.99——但剖面不一样。

原因就在分裂本身。质量 flux 只有 A\mathbf{A} 的第一分量 ρu\rho u 这一项,而 P\mathbf{P} 的第一分量为零。也就是说,压力系统根本不碰密度。 滞止区里 u→0u \to 0,质量 flux 也跟着归零,密度场上就没有数值耗散可留。无论迎风取哪一侧结果都相同,于是奇偶单元解耦。同样这个对流 flux 的选择,在 Toro Test 4 上只把 L1L_1 从 0.594 改成 0.568。在 u≠0u \neq 0 处几乎无关紧要,在 u≈0u \approx 0 处却是生死攸关。

把表里的比原样除开,分叉的位置会更清楚。N=100N=100 时 TV 分裂的 L1(ρ)L_1(\rho) 0.9523 是 Rusanov 1.4303 的 0.666 倍。N=200N=200 时 0.5936/0.9349 约为 0.635,N=800N=800 时 0.2695/0.4217 约为 0.639。所谓 1.5 倍,是三个网格上的比值都卡在 0.63 到 0.67 之间。收敛率顺着 0.68、0.61、0.53 往下掉,斜率不到一阶。这和压力比等于 10 时星状态偏了 65%、L1L_1 却仍然赢,是对得上的。O(Δx)O(\Delta x) 的抹平比星状态误差更大。Noh 那边的全变差正好相反。从 12.01 到 34.45,网格放大 8 倍的过程里大约涨了 2.87 倍。Rusanov 从 0.445 降到 0.072,大约缩了 6.2 倍。平均密度 ρˉ=4.11\bar\rho=4.11 和 3.99 都靠近精确解 4。积分量是对的,长出来的只有锯齿。对流 flux 的取法一换,Toro Test 4 的 L1L_1 从 0.594 挪到 0.568,幅度只有 0.026。同一个选择在 Noh 上,要么第三步就打出 p=−0.12p=-0.12,要么活下来却把锯齿养大。

论文跳过的东西

论文的六个测试问题全是 Riemann 问题,跨越初始间断的速度都不为零。没有一个会制造滞止区、壁面反射或定常流动。上面的结果表明这个盲区确实存在。论文把对流系统 flux 甩给 Toro–Vázquez 原文也很遗憾——那里只写了「straightforward」,可实际上这个选择决定了滞止区的生死。

亚声速限制也还在。SL<uˉ<SRS_L < \bar u < S_R 的引入理由是「许多作者认为它在物理上更合理」,但固相超过气相声速的配置,在爆燃-爆轰转变(DDT)中确实会出现。固相接触面处仍需用迭代法求解薄层(thin-layer)非线性方程组,这一点也和「无需迭代」的印象不符——尽管论文明确表示未遇到收敛问题。

效率数字很有说服力。线性化 solver 最快,最接近的竞争者慢 8 倍;而 HLLEM-TV 贵 125 倍,数值 Roe 贵 67 倍。作者的结论——一旦需要评估特征向量,TV 分裂的吸引力就荡然无存——直接体现在这组数字里。若要在 OpenFOAM 一类代码里模仿这个结构,做法是建两个独立的 surfaceScalarField 再相加,而只用专门的 Riemann solver 去填压力系统那一半,完全可行。

壁面单元的迎风交给 u*,还是交给平均速度#

用 u∗u^* 的符号给对流做迎风,N=200N=200 时第三步、离壁第二个单元的压力会到 −0.12-0.12。改成平均速度的符号可以躲开负值,但网格加密 8 倍时 TV(ρ)\mathrm{TV}(\rho) 会从 12.01 涨到 34.45。Rusanov 则从 0.445 降到 0.072。滞止单元的质量 flux 放在哪里,就是在收敛和负压之间做选择。

复现到分裂定义(式 3–5)、压力系统特征结构(式 9–10)、线性化(式 15、19、23、26)为止,都和式子一致。u=0u=0 时 A/2=aA/2=a 在机器精度上成立,切线是精确曲线的 O(Δp2)O(\Delta p^2)。缺的两处,一是对流系统 flux 根本不在这篇论文里,二是薄层推导压得太紧,还得对照原文 [1] 和 [4]。界面上声波几乎全被反射回去的水–气配置,声阻抗另有一笔。这个分裂的对流 flux 最早写在 Toro & Vázquez (2012), Computers & Fluids 70, 1–12。

如果对您有帮助,请分享。