CFD: 多相VOF算法 + 相变

VOF多相流方法可用于求解多相流的界面问题,如崩塌的堤坝、极少量的气泡界面、液体晃荡等。界面类存在两个关键问题:

  • 界面尽可能要薄;

  • 在界面失稳的情况下算法要尽可能的稳定;

本文从最基本的液膜压力降开始分析,一步一步推导VOF模型。对于大量气泡/液滴存在的情况,VOF模型对网格分辨率要求较高。在这种情况下可以使用多流体模型来计算。在有重力以及源项的情况下,有不可溶多相体系动量方程:

(1) ρ U t + ( ρ U U ) τ = p + ρ g + F

其中各项的含义可参考CFD:不可压瞬态PISO算法 F 表示表面张力。在无化学反应,无相变的情况下,连续性方程可以表示为

(2) ρ t + ( ρ U ) = 0

若考虑两种流体均为不可压缩的,也即考虑某种流体运动的流体微元,其密度 ρ 以及相分数随着 U 进行推进,因此其物质导数为 0 ,即

(3) D ρ D t = ρ t + U ρ = 0 D α D t = α t + U α = 0

将方程(3)代入到(2)有:

(4) U = 0

在这种情况下,方程(1)(4)组成了不可压缩多相界面类模型中关于速度和压力的方程。其中不同的相具有不同的密度。在速度 U 已知的情况下,方程(3)中的密度为纯对流方程可以进行求解。在Front-tracking方法中,方程(3)并没有直接进行求解而是通过某种界面重组的方法进行组建。在Volume of fluid(VOF)方法中则定义了一个变量 α 来表示流体的相分数。考虑某一个网格单元的气液两相系统,如果此网格单元内充满了流体,则 α = 1 ;如果此网格单元内充满了气体,则则 α = 0 。如果 α 的值介于 0 1 之间,则此网格单元内为气液混合。可见,CFD中VOF的界面是通过跟踪相分数来获得的,并不是直接算出来的,是通过计算每个网格内的相场值而后处理出来的。网格越密,VOF后处理出来的界面越薄。如下图所展示,VOF与Front-tracking这些模型均为界面重构的微观多相流模型。可以看作为多相计算流体力学领域的直接模拟。

![](/uploads/freecad/images/m_11f18590be8f616d63458833bcba3e4d_r.png)

表面张力模化

VOF方程在动量方程右侧存在一个表面张力项需要模化。表面张力最重要的特征即其会导致界面处存在一个尖锐的界面压力降 Δ p 。考虑如图(1)所示的一个曲面微元。其中 p 1 为曲面微元上方空气向下施加的压强, p 2 为曲面微元下方液体向上施加的压强,假想一个水中的圆形气泡,压力在气泡内要大于周围液体的压力。因此有压力降 Δ p = p 1 p 2 。这个效应需要在动量方程中有所体现。定义表面张力为 F σ ,其是一种为了保持界面平衡而作用在每单位长度上的力,是一种和界面相切的张力。表面张力的大小主要和流体的物理特性有关。在曲面中,均衡的流体表面张力和压力降 p 1 p 2 均衡。图示中的压力降方向向下,因此界面张力的作用方向向上。这个界面压力降主要取决于表面张力和曲面弧度。下图即为两相界面处的曲面微元受力示意图。

![](/uploads/freecad/images/m_23ebae4f6ab24c9afdf1bd26330dfed8_r.png)

图中 s 1 s 2 分别表示曲面微元的俩个边长。因此有曲面微元的面积为 d s 1 d s 2 ,曲面微元上的总压力的值为:

(5) F p = ( p 1 p 2 ) d s 1 d s 2

方程(5)中的 F p 为一个向下的力。基于表面张力的定义,其作用在曲面微元的四个边上,且合力向上和曲面微元上的总压力 F p 均衡。如果表面张力系数定义为 σ 。那么作用在 d s 1 上的表面张力为 σ d s 1 ,其在 y 方向上的分量为: σ d s 1 sin d β 2 σ d s 1 d β 2 。同理,作用在 d s 2 上的 y 方向分量为 σ d s 2 d α 2 。对四个边加和之后,有作用在曲面微元上的表面张力之和的大小为:

(6) F σ = σ ( d s 1 d β + d s 2 d α ) = σ ( d β d s 2 + d α d s 1 ) d s 1 d s 2

现用 n 来表示曲面微元的单位法向,其从较高的相分数指向较低的相分数,其模为 1 。有

(7) n x | x + Δ x / 2 n x | x Δ x / 2 | Δ x | 2 = n x x d x 2 = sin α 2 d α 2

也即:

(8) n x x = d α d x = 1 R 1

同理对于 s 2 方向有

(9) n y y = d β d y = 1 R 2

对于纵向, n z 并无增量,因此

(10) n z z = 0

其中 R 1 R 2 为图中 d s 1 (也即 d x ), d s 2 的(也即 d y )主曲率半径(单位为m)。将方程(8)(9)带入到(6)有:

(11) F σ = σ ( 1 R 1 + 1 R 2 ) d s 1 d s 2

综合 n 导数的各个分量,有

(12) n = n x + n y + n z = ( 1 R 1 + 1 R 2 )

即:

(13) F σ = σ n d s 1 d s 2

均衡状态下有 F p = F σ ,结合方程(13)(5)即:

(14) Δ p = p 1 p 2 = σ ( 1 R 1 + 1 R 2 ) = σ n

由于 n 为一个未知量,因此需要进行封闭。如上图所示,对于CFD中的自由表面,某一点的法向量为 α ,其为一个连续函数,在流体1和2区内部为0,仅仅在自由表面存在非零值。图(2)自由表面的单位法向量可表示为:

(15) n = α | α |

其中 α 表示相分数的梯度, α 表示与相分数界面垂直的方向。这是因为 α 的分量为 α x , α y , α z ,每个分量表示一个方向的梯度大小,这些矢量分量组合在一起就是与相界面垂直的方向矢量,具有明确的意义。在这种情况下有:

(16) Δ p = σ ( α | α | )

方程(16)表示界面两端处的压力降,为一个间断函数,只适用于非常尖锐的界面,从物理上更倾向于是一个在界面处施加一个边界条件。在实际计算中,这种情况很难达到且难以实施。Continuum Surface Force模型可以较为容易的模化表面张力。参考上图,在界面处的 y 方向,CSF模型将间断压力降处理为一个连续函数:

(17) p b p a = σ κ ( α b α a )

其中 κ 表示界面处的曲率,其大小为 n 。由于在界面处 p 为一个增量函数,由于 α b α a 0 ,因此界面处的曲率为:

(18) κ = n

方程(17) y 方向上的微分形式为:

(19) p y = σ κ α y

若考虑任意方向,在界面处方程(17)可以写为:

(20) p = σ κ α

方程(20)表示在任意位置、任意方向上表面张力与压力降相平衡。

VOF方程

方程(20)的右侧即为动量方程的表面张力项。方程(1)还需要做进一步的变化,以使得边界条件的定义更加简单(在双流体模型中也可以进行类似的数学操作)。

注意:

interFoam求解器当中仅仅需要方程(20)。也就是说方程(20)之前的内容只是为了推导出方程(20)作铺垫。

首先定义

(21) p rgh = p ρ g h

其中 h 表示网格单元体心的位置矢量。对方程(21)进行梯度操作:

(22) p rgh = p g h ρ ρ g

将其代入到方程(1)有:

(23) ρ U t + ( ρ U U ) τ = p rgh g h ρ + σ κ α

其中的 τ 需要进一步模化,考虑牛顿流体:

(24) τ = ( ν ( U + U T ) )

其中粘度为

(25) ν = α 1 ν 1 + ( 1 α 1 ) ν 2

即粘度为随着空间位置变化的量,其并非一个定值。因此,方程(24)可以简化为:

(26) τ = ( ν ( U + U T ) ) = ( ν U ) + U ν

将其代入到方程(23)有VOF模型的动量方程:

(27) ρ U t + ( ρ U U ) ( ν U ) U ν = p rgh g h ρ + σ κ α

下面来推导VOF中的相方程。方程(1)中的密度 ρ 可以表示为:

(28) ρ = α 1 ρ 1 + ( 1 α 1 ) ρ 2

即:

(29) D ρ D t = D ( α ( ρ 1 ρ 2 ) + ρ 2 ) D t = D α D t = α t + U α = 0

方程(29)即为不可压缩VOF模型中的相方程。综合考虑,不可压缩VOF模型中的方程为:

(30) α t + U α = 0
(31) ρ U t + ( ρ U U ) ( ν U ) U ν = p rgh g h ρ + σ κ α
(32) U = 0

可见,VOF方程与单相流方程并无本质区别,均包含一个动量方程、一个连续型方程。其中连续性方程完全一致。动量方程,VOF模型中附加了随空间变化的粘度,以及附加表面张力项。在宏观流动中,附加的表面张力项 σ κ α 可以忽略。

界面压缩

除此之外,包含了一个相传输方程,其可以看做是一个标量传输方程。但最重要的是,这里的标量传输,容不得半点的假扩散,否则会引起严重的数值问题。考虑网格非常细密的情况下,相分数应该只存在两个数值:0或者1。这种非常致密的网格会导致异常高的计算资源需求,因此VOF模型中不可避免的会存在 α 0 1 之间的情况。但VOF应尽可能的使相界面足够的尖锐。在动网格算法中,界面的尖锐可以依附于网格的变化。在Front-tracking方法中,界面尖锐可以通过示踪颗粒来获得。历史上存在不同的可用于VOF模型中保证界面尖锐的方法。在OpenFOAM中采用的是Weller提出的方法,其通过一种人工的对流项来对相界面附近的相分数进行挤压来抗衡这种数值耗散带来的相界面模糊性,并且这一人工对流项在数值上需要保证非相界面处为零。依据添加人工对流项的思想,VOF模型可以表示为

(33) α t + ( α U ) + ( α ( 1 α ) U c ) = α U

方程(33)中的第三项为人工添加的可压缩项,其在纯相(非界面处)计算域为 0 。仅仅在 0 α 1 处存在值。 U c 为需要模化的速度。其应该在界面的法向上进行压缩而不是切向,否则引起虚假的扩散。因此 U c 的方向应与 n 同向。因此有

(34) U c = f ( α | α | )

另一个问题是压缩速度的大小问题。很明显压缩速度不能过分大,这并不符合物理。因此压缩速度最大值也只不过是 U ,则有:

(35) U c = c | U | α | α |

其中的 c 表示可控的压缩因子。当 c = 0 ,无压缩效果。 c 越大,压缩效应越快也越明显。最终有相方程:

(36) α t + ( α U ) + ( α ( 1 α ) c | U | α | α | ) = α U

离散化与MULES

VOF模型的速度方程、连续性方程的离散化与单相流大同小异。详细的步骤可参考CFD:不可压瞬态PISO算法。在这里对其做简述。首先对方程(31),省略方程右侧的项,进行有限体积离散有:

(37) A P U P r + A N U N r = S P n

求解后有预测速度 U P r 。预测速度并不符合连续性方程,考虑最终收敛的情况,对连续性方程进行离散有:

(38) ( U P , f n + 1 S f ) = 0

如果能用压力表示方程(38)中的 U P , f n + 1 ,则压力泊松方程即可构建。首先,收敛情况下方程(37)可以写为

(39) A P U P n + 1 + A N U N n + 1 = S P n p rgh , P g h ρ P + σ κ α P

定义

(40) HbyA P n + 1 = 1 A P ( A N U N n + 1 + S P n )

(41) U P n + 1 = HbyA P n + 1 1 A P ( p rgh , P n + 1 + g h ρ P n + 1 σ κ α P n + 1 )

插值后,面上的速度为

(42) U P , f n + 1 = HbyA P , f n + 1 1 A P , f ( f p rgh , P n + 1 + g h f ρ P n + 1 σ κ f α P n + 1 )

将方程(42)代入到(38)

(43) ( HbyA P , f n + 1 + 1 A P , f ( σ κ f α P n + 1 g h f ρ P n + 1 ) ) = 1 A P , f ( f p rgh , P n + 1 )

(44) ( 1 A p rgh n + 1 ) = ( HbyA n + 1 + 1 A ( σ κ α n + 1 g h ρ n + 1 ) )

求解方程(44)既有收敛的压力。另外,interFoam中最重要的代码为 α 方程的求解。在求解过程中,为了保证 α 方程的严格有界。interFoam调用了FCT算法。在OpenFOAM中,FCT算法被称之为MULES。由于篇幅所限,在这里,MULES算法不会被详细的讨论,感兴趣的读者可以参考OpenFOAM中的MULES一节。

相变

若气相和液相存在相变,则控制方程会存在额外的源相来处理这些关系。在不存在相变的情况下,存在关系:

(45) ρ 1 α 1 t + ( ρ 1 U α 1 ) = 0
(46) ρ 2 α 2 t + ( ρ 2 U α 2 ) = 0

Warning

在interPhaseChangeFoam求解器中,一般认为 α 1 是水。在interFoam求解器中,一般认为 α 1 是空气。

方程(45)(46)相加,同时进一步考虑不可压缩,即为方程(2)。若存在相变,方程(45)(46)要考虑相变的影响:

(47) ρ 1 α 1 t + ( ρ 1 U α 1 ) = m ˙
(48) ρ 2 α 2 t + ( ρ 2 U α 2 ) = m ˙

其中 m ˙ 表示相变的速率,表示单位时间内单位体积内质量的变化,单位是 kg m 3 s 1 。现存很多模型可以用来模化 m ˙ ,同时考虑不可压缩的情况,有:

(49) α 1 t + ( U α 1 ) = m ˙ ρ 1
(50) α 2 t + ( U α 2 ) = m ˙ ρ 2

二者加和后有:

(51) U = m ˙ ρ 1 m ˙ ρ 2

方程(51)可以用来处理存在相变的压力方程。

由于相变一般由俩种机理构成,因此 m ˙ 可以进一步的区分为蒸发与凝结,如果 α 1 表示水相,那么

(52) m ˙ = α 2 m ˙ c + α 1 m ˙ v

其中 m ˙ v 表示蒸发速率(小于0), m ˙ c 表示冷凝速率(大于0)。冷凝表示水相的生成,蒸发表示水相的消失。进一步的:

(53) m ˙ c = [ C c α 1 p c o e f f ( p p s a c ) , p > p s a c 0 , p < p s a c
(54) m ˙ v = [ C v ( 1 + α n u c α 1 ) p c o e f f ( p p s a c ) , p < p s a c 0 , p > p s a c
(55) p c o e f f = 3 ρ 1 ρ 2 ρ 1 R b 2 3 ρ 1 1 | p p s a c |
(56) 1 R b = ( 4 π n 3 α 1 1 + α n u c α 1 ) 1 / 3
(57) α n u c = V n u c V n u c + 1

V n u c 表示单位立方米的体积下,存在多少个多少个可能成核蒸发的小气泡:

V n u c = n π d 3 6

其中 n 表示单位立方米的体积下,存在多少个数量的气泡核。 V n u c 在算例中,一般是一个比较小的数,因此 α n u c 也是一个比较小的数。

OpenFOAM的相变模型中,对相方程的源项进行了数值处理,首先定义:

V ˙ v = ( α 2 ρ 1 + α 1 ρ 2 ) m ˙ v = ( 1 ρ 1 α 1 ( 1 ρ 1 1 ρ 2 ) ) m ˙ v
V ˙ c = ( α 2 ρ 1 + α 1 ρ 2 ) m ˙ c = ( 1 ρ 1 α 1 ( 1 ρ 1 1 ρ 2 ) ) m ˙ c

相方程右侧的源项可以表示为:

(58) ( V ˙ v V ˙ c ) α 1 + α 1 U + V ˙ c = α 1 V ˙ v + α 2 V ˙ c + α 1 ( 1 ρ 1 1 ρ 2 ) m ˙ = α 1 V ˙ v + α 2 V ˙ c + α 1 ( 1 ρ 1 1 ρ 2 ) ( α 2 m ˙ c + α 1 m ˙ v ) = α 1 ( 1 ρ 1 α 1 ( 1 ρ 1 1 ρ 2 ) ) m ˙ v + α 2 ( 1 ρ 1 α 1 ( 1 ρ 1 1 ρ 2 ) ) m ˙ c + α 1 ( 1 ρ 1 1 ρ 2 ) α 2 m ˙ c + α 1 ( 1 ρ 1 1 ρ 2 ) α 1 m ˙ v = 1 ρ 1 α 1 m ˙ v + 1 ρ 1 α 2 m ˙ c = m ˙ ρ 1

因此,方程(49)可以表示为:

(59) α 1 t + ( U α 1 ) = ( V ˙ v V ˙ c ) α 1 + α 1 U + V ˙ c

那么问题是为什么做这个数值处理?这是因为 m ˙ ρ 1 是一个显性的源相,在特别大的情况下会形成刚性方程组。若写成方程(59)的形式,其中 ( V ˙ v V ˙ c ) α 1 必然是小于零的,因此可以增加相方程矩阵的对角线系数,减少方程刚性。

现在回到方程(51),其可以用于组建压力方程。由于速度的散度不为零,因此压力方程其存在相变源相。

(60) m ˙ ρ 1 m ˙ ρ 2 = ( 1 ρ 1 1 ρ 2 ) ( α 2 m ˙ c + α 1 m ˙ v ) = [ ( 1 ρ 1 1 ρ 2 ) α 1 m ˙ v , p < p s a c ( 1 ρ 1 1 ρ 2 ) α 2 m ˙ c , p > p s a c = [ ( 1 ρ 1 1 ρ 2 ) α 1 C v ( α 2 + α n u c ) p c o e f f ( p p s a c ) , p < p s a c ( 1 ρ 1 1 ρ 2 ) α 2 C c α 1 p c o e f f ( p p s a c ) , p > p s a c = [ ( 1 ρ 1 1 ρ 2 ) α 1 ( C v ) ( α 2 + α n u c ) p c o e f f ( p p s a c ) , p < p s a c ( 1 ρ 1 1 ρ 2 ) α 2 C c α 1 p c o e f f ( p p s a c ) , p > p s a c

定义:

V ˙ c p = ( 1 ρ 1 1 ρ 2 ) α 2 C c α 1 p c o e f f
V ˙ v p = ( 1 ρ 1 1 ρ 2 ) α 1 ( C v ) ( α 2 + α n u c ) p c o e f f

很明显有 V ˙ c p > 0 , V ˙ v p < 0 ,同时有

m ˙ ρ 1 m ˙ ρ 2 = ( V ˙ c p V ˙ v p ) ( p p s a c ) = ( V ˙ c p V ˙ v p ) ( p rgh + ρ g h p s a c )

很明显, V ˙ c p V ˙ v p > 0 。为了保证压力方程的稳定性,在植入的过程中,

( V ˙ c p V ˙ v p ) ( p rgh + ρ g h p s a c ) = ( V ˙ v p V ˙ c p ) ( p rgh + ρ g h p s a c )

其中 p rgh 部分可以在压力方程中隐性离散,剩余的部分显性离散。

关键代码

interFoam中的速度方程通过下面的代码组建:

solve
(
    UEqn
 ==
    fvc::reconstruct
    (
        (
            mixture.surfaceTensionForce()
          - ghf*fvc::snGrad(rho)
          - fvc::snGrad(p_rgh)
        ) * mesh.magSf()
    )
);

其中的mixture.surfaceTensionForce()表示网格面上的表面张力,ghf*fvc::snGrad(rho)表示定义在网格面上的浮力项,顺气而然的,fvc::sngrad(p_rgh)表示定义在网格单元面上的压力项。这三项对应于方程(42)右边括号内的三项。之所以将这些力定义在面上,是为了消除可能的振荡的分布。

作者:秦晓川  创建时间:2026-08-09 07:20
最后编辑:秦晓川  更新时间:2026-08-11 11:23