OpenFOAM 中的壁面函数(二)

这篇来看看计算湍动能 $k$ 的壁面函数。

2. 湍流动能 $k$ 的壁面函数

OpenFOAM 中提供了两种 $k$ 的壁面函数, kqRWallFunctionkLowReWallFunction

  • kqRWallFunction
    其实就是 zeroGradient ,无需多言。除非使用 $v^2\text{-}f$ 模型,一般情况下 $k$ 应该使用这个边界条件。

  • kLowReWallFunction
    这个壁面函数应该是可以用于低雷诺数模型的。该壁面函数继承自 fixedValue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class kLowReWallFunctionFvPatchScalarField
    :
    public fixedValueFvPatchField<scalar>
    {
    protected:
    //- Cmu coefficient
    scalar Cmu_;

    //- Von Karman constant
    scalar kappa_;

    //- E coefficient
    scalar E_;

    //- Ceps2 coefficient
    scalar Ceps2_;

    //- Y+ at the edge of the laminar sublayer
    scalar yPlusLam_;
    ......

    kLowReWallFunctionFvPatchScalarField::kLowReWallFunctionFvPatchScalarField
    (
    const fvPatch& p,
    const DimensionedField<scalar, volMesh>& iF,
    const dictionary& dict
    )
    :
    fixedValueFvPatchField<scalar>(p, iF, dict),
    Cmu_(dict.lookupOrDefault<scalar>("Cmu", 0.09)),
    kappa_(dict.lookupOrDefault<scalar>("kappa", 0.41)),
    E_(dict.lookupOrDefault<scalar>("E", 9.8)),
    Ceps2_(dict.lookupOrDefault<scalar>("Ceps2", 1.9)),
    yPlusLam_(yPlusLam(kappa_, E_))
    {
    checkType();
    }

    }

核心的函数是以下两个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
scalar kLowReWallFunctionFvPatchScalarField::yPlusLam
(
const scalar kappa,
const scalar E
)
{
scalar ypl = 11.0;

for (int i=0; i<10; i++)
{
ypl = log(max(E*ypl, 1))/kappa;
}

return ypl;
}
void kLowReWallFunctionFvPatchScalarField::updateCoeffs()
{
if (updated())
{
return;
}

const label patchI = patch().index();

const turbulenceModel& turbulence =
db().lookupObject<turbulenceModel>("turbulenceModel");
const scalarField& y = turbulence.y()[patchI];

const tmp<volScalarField> tk = turbulence.k();
const volScalarField& k = tk();

const tmp<volScalarField> tnu = turbulence.nu();
const scalarField& nuw = tnu().boundaryField()[patchI];

const scalar Cmu25 = pow025(Cmu_);

scalarField& kw = *this; // 更新 kw 相当于更新壁面上的 k 值。

// Set k wall values
forAll(kw, faceI)
{
label faceCellI = patch().faceCells()[faceI];

scalar uTau = Cmu25*sqrt(k[faceCellI]);

scalar yPlus = uTau*y[faceI]/nuw[faceI];

if (yPlus > yPlusLam_)
{
scalar Ck = -0.416;
scalar Bk = 8.366;
kw[faceI] = Ck/kappa_*log(yPlus) + Bk;
}
else
{
scalar C = 11.0;
scalar Cf = (1.0/sqr(yPlus + C) + 2.0*yPlus/pow3(C) - 1.0/sqr(C));
kw[faceI] = 2400.0/sqr(Ceps2_)*Cf;
}

kw[faceI] *= sqr(uTau);
}

fixedValueFvPatchField<scalar>::updateCoeffs();

// TODO: perform averaging for cells sharing more than one boundary face
}

先在函数里计算 ypl 的值, updateCoeffs 函数里根据 yPlus 与这个 ypl 的值来相对大小而采取不同的方法来计算壁面上的 $k_w$。 ypl 的计算是一个迭代过程
$$
ypl = \frac{\log(\max(E*ypl,1.0))}{\kappa}
$$
初始值为 ypl = 11.0,迭代10次,最终结果应该是 ypl = 11.5301073043272
$y^+$ 定义为:
$$
u_\tau = C_\mu^{1/4 }\sqrt{k_c} \
y^+ = \frac{u_\tau \cdot y}{\nu_w}
$$
壁面上的k计算方法如下:如果 $y^+ > ypl$,则
$$
k^+ _w = \frac{C_k}{\kappa}\ln(y^+) + B_k
$$
否则
$$
k^+ _w = \frac{2400}{C_{eps2}^2}\cdot \left[ \frac{1}{(y^+ + C)^2} + \frac{2y^+}{C^3} - \frac{1}{C^2}\right ]
$$
最终,壁面上的值为 $k_w=k^+ _w u_\tau ^2 =k^+ _w C_\mu^{1/2}k_c$ 。
以上公式中,下标 $c$ 表示壁面单元所述网格的值,下标 $w$ 表示当前壁面上的值。
这个壁面函数参考文献 “Kalitzin, G., Medic, G., Iaccarino, G., Durbin, P., 2005. Near-wall behavior of RANS turbulence models and implications for wall functions. J. Comput. Phys. 204, 265–291. doi:10.1016/j.jcp.2004.10.018”,是为 $v^2\text{-}f$ 模型设计的。

$v^2$ 和 $f$ 的壁面函数

上面提到了 $v^2\text{-}f$ 模型,所以这里顺便来看看$v^2$ 和 $f$ 的壁面函数。这里参考的也是上面提到的那篇参考文献。

  • $v^2$ 的壁函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    forAll(v2, faceI)
    {
    label faceCellI = patch().faceCells()[faceI];

    scalar uTau = Cmu25*sqrt(k[faceCellI]);

    scalar yPlus = uTau*y[faceI]/nuw[faceI];

    if (yPlus > yPlusLam_)
    {
    scalar Cv2 = 0.193;
    scalar Bv2 = -0.94;
    v2[faceI] = Cv2/kappa_*log(yPlus) + Bv2;
    }
    else
    {
    scalar Cv2 = 0.193;
    v2[faceI] = Cv2*pow4(yPlus);
    }

    v2[faceI] *= sqr(uTau);
    }

    fixedValueFvPatchField<scalar>::updateCoeffs();

yPlus > yPlusLam_ 时,
$$
v^2 = u_\tau^2 \cdot \left[ \frac{C_{v2}}{\kappa}\ln(y^+) + B_{v2} \right]
$$
与文献中的无量纲形式 $(\overline{v^2})^{^+} = \frac{C_{v2}}{\kappa}\ln(y^+) + B_{v2} $ 一致。

yPlus < yPlusLam\_ 时,
$$
v^2 = u_\tau^2 \cdot C_{v2}(y^+)^2
$$
与无量纲形式 $(\overline{v^2})^{^+} = C_{v2}(y^+)^2$ 一致。

  • $f$ 的壁函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    forAll(f, faceI)
    {
    label faceCellI = patch().faceCells()[faceI];

    scalar uTau = Cmu25*sqrt(k[faceCellI]);

    scalar yPlus = uTau*y[faceI]/nuw[faceI];

    if (yPlus > yPlusLam_)
    {
    scalar N = 6.0;
    scalar v2c = v2[faceCellI];
    scalar epsc = epsilon[faceCellI];
    scalar kc = k[faceCellI];

    f[faceI] = N*v2c*epsc/(sqr(kc) + ROOTVSMALL);
    f[faceI] /= sqr(uTau) + ROOTVSMALL;
    }
    else
    {
    f[faceI] = 0.0;
    }
    }

yPlus > yPlusLam_ 时,
$$
f = \frac{N \cdot v^2\cdot \varepsilon}{k^2 u_\tau^2}
$$
这似乎与文献中的无量纲形式
$$
f^+ = N \frac{(\overline{v^2})^{^+}}{(k^+)^2}\varepsilon^+
$$
不一致!是 bug 还是我推导出错了?存疑…

yPlus < yPlusLam_ 时,文献给出的公式是
$$
f^+ = \frac{-4(6-N)(\overline{v^2})^{^+}}{\varepsilon^+ (y^+)^4}
$$
N=6 时,可以得到 $f^+ = 0$ 。

按理说,$v^2$ 和 $f$ 应该跟 $\varepsilon$ 和 $\omega$ 那样(见后文),计算第一层网格内的值,并且考虑一个网格有多个边界面的情形。OpenFOAM 目前计算的是每一个边界面元上的值,不知道这两种方式对结果有多大影响。