在查看interFoam求解器时,会发现其中引入了LTS
:
if (LTS)
{
#include "setRDeltaT.H"
}
那么它的含义和功能是什么呢?
LTS(locall time-step)
是一种局部时间步求解器。该求解器建立于局部时间步下,它会提取pimple
算法控制字典t里面的maxCo
与maxAlphaCo
,并以此为依据,根据本地计算条件,计算当前网格的时间步长deltaT
。- 由于每个网格有不同的 deltaT,这会使质量失衡(瞬态问题每个时间步的质量守恒),因此需要迭代求解。在最终稳态的情况下,时间项的导数为 0 ,这时输出的结果和最终瞬态达到稳定状况下的结果相同。
LTS与库朗数是息息相关的,为此,我们先分析interFoam
中的 CourantNo.H
,了解其中库朗数的定义:
scalar CoNum = 0.0; //定义标量 CoNum 并赋初值
scalar meanCoNum = 0.0;//定义标量 meanCoNum 并赋初值
if (mesh.nInternalFaces())
{
scalarField sumPhi
(
fvc::surfaceSum(mag(phi))().primitiveField()
);
CoNum = 0.5*gMax(sumPhi/mesh.V().field())*runTime.deltaTValue();
meanCoNum =
0.5*(gSum(sumPhi)/gSum(mesh.V().field()))*runTime.deltaTValue();
}
Info<< "Courant Number mean: " << meanCoNum << " max: " << CoNum << endl;
s
u
m
P
h
i
=
∑
∣
U
⃗
f
⋅
S
⃗
f
∣
(1)
sumPhi=\sum|\vec{U}_f\cdot\vec{ S}_f| \tag{1}
sumPhi=∑∣Uf⋅Sf∣(1)
C
o
N
u
m
=
1
2
m
a
x
(
∣
U
⃗
f
⋅
S
⃗
f
∣
V
Δ
T
)
(2)
CoNum=\frac 12 max(\frac{|\vec{U}_f\cdot\vec{ S}_f| }{V}\Delta T )\tag 2
CoNum=21max(V∣Uf⋅Sf∣ΔT)(2)
m
e
a
n
C
o
N
u
m
=
1
2
s
u
m
(
∣
U
⃗
f
⋅
S
⃗
f
∣
V
Δ
T
)
(3)
meanCoNum=\frac 12 sum(\frac{|\vec{U}_f\cdot\vec{ S}_f| }{V}\Delta T )\tag 3
meanCoNum=21sum(V∣Uf⋅Sf∣ΔT)(3)
注:在 OpenFOAM 中,若为六面体网格,则其对应的面通量值
ϕ
\phi
ϕ加和加了两次,因此公式 (2) 和(3) 需要除 2。
库朗数一般定义如下:
C
o
=
∣
U
⃗
f
⋅
Δ
T
∣
Δ
x
(4)
Co=\frac {|\vec U_f \cdot \Delta T| } {\Delta x}\tag 4
Co=Δx∣Uf⋅ΔT∣(4)
获得了库朗数后,进一步求取alpha 库郎数,参见 alphaCourantNo.H
:
scalar maxAlphaCo
(
//查找controlDict字典文件中的maxAlphaCo并进行读取
readScalar(runTime.controlDict().lookup("maxAlphaCo"))
);
//声明变量赋初值
scalar alphaCoNum = 0.0;
scalar meanAlphaCoNum = 0.0;
//求解公式
if (mesh.nInternalFaces())
{
scalarField sumPhi
(
mixture.nearInterface()().primitiveField()
*fvc::surfaceSum(mag(phi))().primitiveField()
);
alphaCoNum = 0.5*gMax(sumPhi/mesh.V().field())*runTime.deltaTValue();
meanAlphaCoNum =
0.5*(gSum(sumPhi)/gSum(mesh.V().field()))*runTime.deltaTValue();
}
Info<< "Interface Courant Number mean: " << meanAlphaCoNum
<< " max: " << alphaCoNum << endl;
上述代码用数学公式可表述为:
s
u
m
P
h
i
=
(
ϕ
1
−
0.01
)
(
0.99
−
ϕ
1
)
∑
∣
U
⃗
f
⋅
S
⃗
f
∣
,
0.01
<
ϕ
1
<
0.99
(5)
sumPhi=(\phi_1-0.01)(0.99-\phi_1)\sum|\vec{U}_f\cdot\vec{ S}_f|,0.01<\phi_1<0.99 \tag5
sumPhi=(ϕ1−0.01)(0.99−ϕ1)∑∣Uf⋅Sf∣,0.01<ϕ1<0.99(5)
则两相库朗数计算结果为:
s
u
m
P
h
i
≈
ϕ
1
p
h
i
2
∑
∣
U
⃗
f
⋅
S
⃗
f
∣
,
0.01
<
ϕ
1
<
0.99
(6)
sumPhi\approx \phi_1phi_2\sum|\vec{U}_f\cdot\vec{ S}_f|,0.01<\phi_1<0.99 \tag6
sumPhi≈ϕ1phi2∑∣Uf⋅Sf∣,0.01<ϕ1<0.99(6)
同理可求得alphaCoNum
和meanAlphaCoNum
。
在了解了库朗数和两相库朗数后,我们进入setDeltaT.H
文件
if (adjustTimeStep)//如果调节时间步
{
scalar maxDeltaTFact =
min(maxCo/(CoNum + SMALL), maxAlphaCo/(alphaCoNum + SMALL));
//在没有达到设定的库朗数值的时候,deltaTFact每次扩大1.2倍
scalar deltaTFact = min(min(maxDeltaTFact, 1.0 + 0.1*maxDeltaTFact), 1.2);
//与controlDict字典中的 maxDeltaT比较,将DeltaT设置成较小的那个
runTime.setDeltaT
(
min
(
deltaTFact*runTime.deltaTValue(),
maxDeltaT
)
);
Info<< "deltaT = " << runTime.deltaTValue() << endl;
}
下面是setRDeltaT.H
,这里面声明了计算所需要的场量,以及如何计算局部时间步.由于该文件较长,这里只将其中的计算局部时间步的代码提取出来,
rDeltaT.ref() = max
(
1/dimensionedScalar("maxDeltaT", dimTime, maxDeltaT),
fvc::surfaceSum(mag(rhoPhi))()()
/((2*maxCo)*mesh.V()*rho())
);
若第二项的值相对较大,则其数学公式表述如下:
Δ
T
=
∑
∣
ρ
f
U
⃗
f
⋅
S
⃗
f
∣
2
∗
C
o
m
a
x
⋅
V
⋅
ρ
(7)
\Delta T =\frac {\sum|\rho_f \vec{U}_f \cdot \vec{S}_f|} {2*Co_{max} \cdot V \cdot \rho} \tag 7
ΔT=2∗Comax⋅V⋅ρ∑∣ρfUf⋅Sf∣(7)
if (maxAlphaCo < maxCo)
{
// Further limit the reciprocal time-step
// in the vicinity of the interface
volScalarField alpha1Bar(fvc::average(alpha1));
rDeltaT.ref() = max
(
rDeltaT(),
pos(alpha1Bar() - alphaSpreadMin)
*pos(alphaSpreadMax - alpha1Bar())
*fvc::surfaceSum(mag(phi))()()
/((2*maxAlphaCo)*mesh.V())
);
}
这里,我们有
Δ
T
=
(
ϕ
1
−
0.01
)
(
0.99
−
ϕ
1
)
∑
∣
U
⃗
f
⋅
S
⃗
f
∣
2
∗
C
o
α
m
a
x
⋅
V
,
0.01
<
ϕ
1
<
0.99
(8)
\Delta T =(\phi_1-0.01)(0.99-\phi_1) \frac {\sum|\vec{U}_f\cdot\vec{ S}_f|}{2*Co_{\alpha _{max}}\cdot V},0.01<\phi_1<0.99 \tag8
ΔT=(ϕ1−0.01)(0.99−ϕ1)2∗Coαmax⋅V∑∣Uf⋅Sf∣,0.01<ϕ1<0.99(8)
以下,通过fvc::smooth
、fvc::spread
、fvc::sweep
继续光顺局部时间步。
alpha
方程求解:
fvScalarMatrix alpha1Eqn
(
(
LTS
? fv::localEulerDdtScheme<scalar>(mesh).fvmDdt(alpha1)
: fv::EulerDdtScheme<scalar>(mesh).fvmDdt(alpha1)
)
+ fv::gaussConvectionScheme<scalar>
(
mesh,
phiCN,
upwind<scalar>(mesh, phiCN)
).fvmDiv(phiCN, alpha1)
);
alpha1Eqn.solve();
从这段代码我们可以看出,interFoam
采用的是欧拉法进行时间离散,而LTS
,则对时间项采用局部欧拉法进行离散。
注:
LTS
是一种可用可不用的自动调节步长方式,它是一种单纯的计算加速技术,能够加快收敛进程,但准确性还有待考究。- LTS主要解决稳态问题
本文内容参考了东岳流体,关于求解器的相关知识可以查阅该网站。