第二章:CFD算例之后向台阶流

后向台阶流为一个二维的、等温的、不可压缩流动。其计算域由以下特点组成:

  • 左边存在一个进口;

  • 右边存在一个出口;

  • 上下均为边界;

流体从左侧进口以10米/秒的速度流入。在OpenFOAM-11中,将调用incompressibleFluid模块。

前处理

OpenFOAM所有自带算例都存储在$FOAM_TUTORIALS文件夹。用户需要首先将相应的算例,复制到一个文件夹下。在本算例中,我们将自带算例复制到$FOAM_RUN文件夹下。用户需要分3次运行下述代码:

cd $FOAM_RUN
cp -r $FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady .
cd pitzDailySteady

在输入第一行代码的时候,如果输出:

dyfluid@dyfluid-virtual-machine:~$ cd $FOAM_RUN
bash: cd: /home/dyfluid/OpenFOAM/dyfluid-11/run: No such file or directory

用户需要手动创立一个文件夹:

cd /home/dyfluid/OpenFOAM
mkdir dyfluid-11
cd dyfluid-11
mkdir run

注意:

上方代码中的dyfluid为你的用户名。如果你的ubuntu系统的用户名为pangmao,则需要把相应的dyfluid改为pangmao。

然后执行

cd $FOAM_RUN
cp -r $FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady .
cd pitzDailySteady

即可。这样在pitzDailySteady会发现3个文件夹。所有OpenFOAM算例文件夹都主要分为3个,一个是0文件夹,一个是constant文件夹,一个是system文件夹。在0文件夹里,可以设置算例的初始条件。在constant文件夹里,可以设置算例的物理性质。在system文件夹,主要设置算例的算法控制。该算例下还存在一个Allrun文件。其主要用于自动处理脚本。

网格生成

OpenFOAM使用blockMesh来进行网格生成。blockMesh在生成网格的时候需要指定特别复杂的点位置信息。因此其只能用于生成一些基本的网格。当然了,其可以生成非常复杂的网格,但在这种情况下需要花费大量的时间去写点位置信息。OpenFOAM中的blockMesh主要是结合snappyHexMesh来做网格。snappyHexMesh为OpenFOAM内置的另外一个网格生成程序。将会在以后介绍。本算例采用blockMesh来生成一个简单的后向台阶流网格。

下面是blockMesh的字典文件。其名称为blockMeshDict,其位于system文件夹下:

1/*--------------------------------*- C++ -*----------------------------------*\
2  =========                 |
3  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4   \\    /   O peration     | Website:  https://openfoam.org
5    \\  /    A nd           | Version:  dev
6     \\/     M anipulation  |
7\*---------------------------------------------------------------------------*/
8FoamFile
9{
10    format      ascii;
11    class       dictionary;
12    object      blockMeshDict;
13}
14// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
15
16// Note: this file is a Copy of $FOAM_TUTORIALS/resources/blockMesh/pitzDaily
17
18convertToMeters 0.001;
19
20vertices
21(
22    (-20.6 0 -0.5)
23    (-20.6 25.4 -0.5)
24    (0 -25.4 -0.5)
25    (0 0 -0.5)
26    (0 25.4 -0.5)
27    (206 -25.4 -0.5)
28    (206 0 -0.5)
29    (206 25.4 -0.5)
30    (290 -16.6 -0.5)
31    (290 0 -0.5)
32    (290 16.6 -0.5)
33
34    (-20.6 0 0.5)
35    (-20.6 25.4 0.5)
36    (0 -25.4 0.5)
37    (0 0 0.5)
38    (0 25.4 0.5)
39    (206 -25.4 0.5)
40    (206 0 0.5)
41    (206 25.4 0.5)
42    (290 -16.6 0.5)
43    (290 0 0.5)
44    (290 16.6 0.5)
45);
46
47negY
48(
49    (2 4 1)
50    (1 3 0.3)
51);
52
53posY
54(
55    (1 4 2)
56    (2 3 4)
57    (2 4 0.25)
58);
59
60posYR
61(
62    (2 1 1)
63    (1 1 0.25)
64);
65
66
67blocks
68(
69    hex (0 3 4 1 11 14 15 12)
70    (18 30 1)
71    simpleGrading (0.5 $posY 1)
72
73    hex (2 5 6 3 13 16 17 14)
74    (180 27 1)
75    edgeGrading (4 4 4 4 $negY 1 1 $negY 1 1 1 1)
76
77    hex (3 6 7 4 14 17 18 15)
78    (180 30 1)
79    edgeGrading (4 4 4 4 $posY $posYR $posYR $posY 1 1 1 1)
80
81    hex (5 8 9 6 16 19 20 17)
82    (25 27 1)
83    simpleGrading (2.5 1 1)
84
85    hex (6 9 10 7 17 20 21 18)
86    (25 30 1)
87    simpleGrading (2.5 $posYR 1)
88);
89
90boundary
91(
92    inlet
93    {
94        type patch;
95        faces
96        (
97            (0 1 12 11)
98        );
99    }
100    outlet
101    {
102        type patch;
103        faces
104        (
105            (8 9 20 19)
106            (9 10 21 20)
107        );
108    }
109    upperWall
110    {
111        type wall;
112        faces
113        (
114            (1 4 15 12)
115            (4 7 18 15)
116            (7 10 21 18)
117        );
118    }
119    lowerWall
120    {
121        type wall;
122        faces
123        (
124            (0 3 14 11)
125            (3 2 13 14)
126            (2 5 16 13)
127            (5 8 19 16)
128        );
129    }
130    frontAndBack
131    {
132        type empty;
133        faces
134        (
135            (0 3 4 1)
136            (2 5 6 3)
137            (3 6 7 4)
138            (5 8 9 6)
139            (6 9 10 7)
140            (11 14 15 12)
141            (13 16 17 14)
142            (14 17 18 15)
143            (16 19 20 17)
144            (17 20 21 18)
145        );
146    }
147);
148
149// ************************************************************************* //

如上图所示,在blockMeshDict字典文件中,第22行至第44行,为上图中编号从0只21的位置点信息。convertToMeters关键词用于单位缩放。比如其指定为1时,则所有的数字的单位均为米。如果其指定为10时,则所有的数字需要乘以10(单位同样为米)。

上图中很明显的计算域被分解成了5个block,因此在blockMesh中的block关键词下存在5个block的指定。blockMeshDict文件的内容将在此进行简要回顾,但相关的详细信息,请参阅以后的章节(待更新)。

blockMeshDict以block的顶点的坐标开始。然后,该文件定义了block(在这里是5个)。每个块都是一个六面体形状,由hex关键词给出。hex关键词后面列出了八个顶点标签。每个block的各个方向上的单元数量由三个整数组成的向量来指定。 例如,第一个块指定 了(18 30 1),表明在 x方向划分出18个单元格,在 y方向上生成30个单元,在z方向(未使用的方向)上仅给出1个单元格。

这些block包含网格非均一分布。它还包含多级的非均一分布,这在后续会进行详细描述。

网格区分为入口、出口和壁面等边界,具体包含以下几个部分:upperWall和lowerWall代表壁面;inlet和outlet代表进出口。在z法线方向(即垂直于z轴的方向)上的边界,被归入一个名为frontAndBack的单独区域中。其为一个2D算例,因此是一个empty的边界条件类型。

具体来说,通过在blockMeshDict文件上运行blockMesh来生成网格。在案例目录中,只需在终端输入以下内容即可:

blockMesh

执行命令后,blockMesh的运行状态会在终端窗口中显示。如果blockMeshDict文件中有任何错误,blockMesh会输出相应的错误消息。

查看网格

在计算之前需要先看一下网格。用户只需在案例目录中执行paraFoam命令,就可以轻松地启动ParaView进行后处理。

所有linux程序都可以有两种方式运行:

  • 前端运行:即终端会等待该程序执行完毕后才结束;

  • 后端运行:在运行的同时,可以执行其他程序;

在这里,用户希望运行ParaView的同时,同时可以运行一些别的程序。那可以这样执行:

paraFoam &

然后在“Properties”窗口的“Mesh Parts”面板中选择要查看的几何图形。点击“Apply”。如果网格的边界数量比较少,您可以直接选中“Mesh Parts”面板上面的复选框快速选择所有边界。完成选择后可点击“Apply”。

ParaView顶部的第二行按钮可以展示网格的不同形式。其也可以通过下面的Display面板来进行设置。 用户可以这样操作:

  1. 选择Solid Color确定颜色;

  2. 点击 Edit按钮(位于“着色”部分内),选择一个合适的颜色,比如黑色;

  3. 在Representation菜单中选择Wireframe模式,这样网格看起来比较清楚;背景颜色也可以在 Properties窗口底部的 View(Render View)面板中进行设置;

另外,属性窗口中的很多高级设置,需要通过点击窗口顶部搜索框旁边的“高级属性”齿轮按钮(⚙️)才能显示出来。

边界和初始条件

网格分完后,可以对初始场进行配置。该案例从时间t = 0s开始,因此初始场数据被存储在cavity文件夹的0文件夹中。这个0文件夹包含几个文件,其中包括压力p和速度U。首先我们来看p:

16dimensions   [0 2 -2 0 0 0 0];
17
18internalField  uniform 0;
19
20boundaryField
21{
22        inlet
23        {
24             type        zeroGradient;
25        }
26
27        outlet
28        {
29              type        zeroGradient;
30              value       uniform 0;
31        }
32 
33        upperWall
34        {
35              type        zeroGradient;
36        }
37 
38        lowerWall
39        {
40              type        zeroGradient;
41        }
42 
43        frontAndBack
44        {
45              type        empty;
46        }
47}

该文件主要有三个关键词:

  • dimensions 单位:这里是运动压力,即\(\mathrm{m}^2 \mathrm{s}^{-2}\)(除掉了密度的单位);

  • internalField 内部场的值:其可以是均一值,也可以是非均一的,这种情况下必须为所有网格指定具体值;

  • boundaryField 边界场的值:包括边界条件和所有边界块的数据;

对于pitzDailySteady案例,初始场是均一的。

Note

由于是不可压缩流,这里的压力是运动压力,且其绝对值并不重要,因此为方便起见,将其设置为uniform 0。

边界包括upperWall, lowerWall, inlet和outlet。壁面和入口的压力p的边界均为zeroGradient,这意味着“压力的法向梯度为零”。出口使用了固定值fixedValue边界条件,压力值设为uniform 0。二维模拟的两侧平面的frontAndBack边界面被指定为empty。

对于速度U。内部场初始化设为零,但是速度必须通过3个分量来表示,即(0 0 0)。速度在壁面上应用无滑移条件noSlip。入口流速U在x方向为10m/s,通过fixedValue条件进行设置,具体数值为uniform (10 0 0)。出口使用zeroGradient。frontAndBack面设为empty。

物性

物性配置都存储在constant目录下的字典文件中。具体物性在physicalProperties文件中指定:

17viscosityModel     constant;
18
19nu                 1e-05;

其中的viscosityModel设置为常数。nu关键词来指定运动粘度,运动粘度用希腊符号\(\nu\)表示。其值设定为1e-05。

湍流模型

为了确定流动是否为湍流,需要估算雷诺数。雷诺数的定义为:

(1)\[\begin{equation} Re=\frac{\left | \bfU \right | L}{\nu} \end{equation}\]

\(\bfU\)\(L\)分别代表特征速度和特征长度,\(\nu\)表示运动粘度。本算例入口台阶高度\(L =25.4\)毫米,当速度为10米每秒时,雷诺数\(Re=25400\)。对于管道中的流动,当雷诺数\(Re > 2000\)时,流动状态通常会由层流转为湍流,所以该算例为湍流。

momentumTransport文件中可以设置牛顿流体、非牛顿流体、湍流以及粘弹性等。本算例包含了如下湍流模型配置:

17simulationType    RAS;
18
19RAS
20{
21    // Tested with kEpsilon, realizableKE, kOmega, kOmega2006, kOmegaSST, v2f,
22    // ShihQuadraticKE, LienCubicKE.
23    model                  kEpsilon;
24
25    turbulence           on;
26
27    printCoeffs           on;
28
29    viscosityModel      Newtonian;
30}

simulationType需指定层流或湍流。RAS表示雷诺平均湍流模型。RAS子字典中包含一个model关键词,该关键词若为kEpsilon,则表示其为\(k-\varepsilon\)模型。turbulence关键字如果是on,表示开启湍流模型。所有湍流模型都有默认值,这些默认值可以在momentumTransport文件中通过指定关键词来进行覆盖。当printCoeffs开关打开时,系数将会出现到终端。

Note

viscosityModel关键词表示流体为牛顿流体,牛顿流体是默认的流体类型,因此其可以省略。

\(k-\varepsilon\)模型需要求解湍流动能\(k\)和湍流耗散率\(\varepsilon\)的传输方程。这些场的初始和边界条件分别在0文件夹下的k和epsilon文件中进行设置。

湍流变量的边界场要尽可能的准确,这样计算才好收敛。湍流动能\(k\)可以通过以下公式计算:

(2)\[\begin{equation} k=\frac{2}{3} \left ( \left | \bfU \right | I\right ) ^{2} \end{equation}\]

其中\(I\)表示湍流强度。在本例中估算\(I=0.05\),因此 \(k=1.5\times \left ( 10\times 0.05 \right ) ^{2}=0.375 \rm m^{2} \rm s^{-2}\)。因此在k文件中,0.375可以同时用作初始内部场和入口值。

湍流耗散率 \(\varepsilon\) 可计算为

(3)\[\begin{equation} \varepsilon =C_{\mu }^{0.75} \frac{k^{1.5} }{l_{\rm m} } \end{equation}\]

湍流耗散率 \(\varepsilon\) 可以通过 \(C_{\mu}=0.09\) 和普朗特混合长度 \(l_{\rm m} \) 的估值来计算。在这个例子中,\(l_{m}\)为台阶高度的0.1倍,即2.54毫米, 从而得到\(\varepsilon =0.09^{0.75} \times 0.375^{1.5} /0.00254=14.855 \rm m^{2} \rm s^{-3} \)。在epsilon文件中,14.855同时用作初始内部场和入口值。

Note

\(l_{m}\)\(I\)是用户完全需要自行预估的变量。

在模拟壁面的时候,可使用壁面函数。壁面函数通过0文件夹下的nut、k以及epsilon文件来指定。首先看nut:

16 
17dimensions           [0 2 -1 0 0 0 0];
18
19internalField          uniform 0;
20
21boundaryField
22{
23        inlet
24        {
25            type calculated;
26            value uniform 0; 
27        }
28        outlet
29        {
30            type calculated;
31            value uniform 0; 
32        }
33        upperWall
34        {
35            type nutkWallFunction;
36            value uniform 0; 
37        }
38        lowerWall
39        {
40            type nutkWallFunction;
41            value uniform 0; 
42        }
43        frontAndBack
44        {
45            type empty;
46        }
47}

本案例使用标准壁面函数,分别在upperWall和lowerWall通过nutkWallFunction类型进行指定。其他可选的壁面函数模型还包括粗糙壁面函数,其可以通过nutRoughWallFunction关键字进行指定。同时,epsilon相应的壁面需指定为epsilonWallFunction,k需要指定为kqRWallFunction。

Note

kqRWallFunction是一种通用的边界条件,可应用于任何湍流动能类型的场,例如\(k\)\(q\) 或雷诺应力\(\bfR\)

求解控制

controlDict文件可以进行求解层面的相应设置。其位于system目录下:

16
17application              foamRun; 
18
19solver                   incompressibleFluid;
20
21startFrom                startTime;
22
23startTime                0;
24
25stopAt                   endTime;
26 
27endTime                  2000;
28
29deltaT                   1;
30
31writeControl             timeStep;
32
33writeInterval            100;
34
35purgeWrite               0;
36
37writeFormat              ascii;
38
39writePrecision           6;
40
41writeCompression         off;
42
43timeFormat               general;
44
45timePrecision            6;
46
47runTimeModifiable        true;
48
49cacheTemporaryObjects
50(
51       kEpsilon:G
52);
53
54functions
55{
56      #includeFunc streamlinesLine
57      (
58            name=streamlines,
59            start=(-0.0205 0.001 0.00001),
60            end=(-0.0205 0.0251 0.00001),
61            nPoints=10,
62            fields=(p k U)
63      )     
64
65     #includeFunc writeObjects(kEpsilon:G)
66}
  • 关键词incompressibleFluid表示模拟不可压缩等温流体的稳态或瞬态流动,同时支持动网格;

  • 本算例的模拟从0时刻开始,因此求解器从0文件夹中读取数据。这样需要将startFrom关键字设为startTime,并将startTime关键字设定为0;

  • 因为其是一个稳态模拟。所有方程中的时间导数 \(∂/∂t\) 均为0,其可以在fvSchemes文件中,将ddtSchemes设置为steadyState来实现;在稳态下,关键字deltaT表示的时间步长仅用作时间增量(因为它不再用于离散化 \(∂/∂t\))。它的值不会影响结果,因此对于稳态解决方案,它被设置为1,时间步仅仅表示代表求解步数;

  • endTime用于设置求解停止的时间。设为2000可以提供足够数量的求解步数,使稳态解能够收敛;

  • 随着计算的进行,通常希望在特定的时间间隔内记录结果。writeControl提供了多种选项来设定记录结果的时间点;这里的timeStep选项指定了每隔n个时间步记录一次结果,n的值由writeInterval关键字指定。如果间隔设为100,那么结果将在第100步、第200步等时间点被记录;OpenFOAM在每次写入一组数据时,都会创建一个以当前时间命名的新目录,例如100。其包含所有的场的信息;