区分实际网格几何图形和由计算辅助设计(CAD)程序生成的几何图形非常重要。虽然在上一章中已经用了一些关于一般网格连接的内容,但这里还是给出了实际网格是如何存储在文件系统中的概述。在标准的OpenFOAM案例中,有三个主要目录:0、constant和system。0文件夹存储网格生成过程中不需要的场的初始条件,system目录存储与模拟的数值和整体执行有关的设置。本章考虑的是constant目录,因为其存储网格,包括所有与空间和连接相关的数据。有关OpenFOAM案例结构的其他详细信息将在第3章中提供。

只要使用静态网格,计算网格始终存储在Constant/PolyMesh目录中。此处的静态网格是指在模拟过程中不会发生改变的网格,即使点位移或连接性发生变化。网格数据自然位于此处,因为假设它是恒定的,因此是Constant文件夹。从编程的角度来看,它被描述为PolyMesh,这是对OpenFOAM网格及其所有功能和限制的一般描述。对于给定的静态网格情况,网格数据将存储在常量/多边形网格中。这里找到的典型网格数据文件包括:points、faces、owner、neighbour及boundary。当然,包含的数据必须有效,才能正确定义网格。

在下面的讨论中,以potentialFoam求解器的PitzDaily教程为例,可以通过发出以下命令找到该示例

?>  tut
?>  cd basic/potentialFoam/pitzDaily

检查PolyMesh目录的内容后,很明显其还不包含所需的网格数据。本教程中仅提供了blockMeshDict,在case目录中执行blockMesh会生成网格和关联的连接数据:

?>  ls constant/polyMesh
blockMeshDict boundary
?>  blockMesh
?>  ls constant/polyMesh
blockMeshDict boundary faces neighbour owner points

以前使用过CFD代码的用户,特别是使用基于结构化网格的代码的用户,可能会错过每个网格单元的寻址。

OpenFOAM中的非结构化FVM方法不是基于每个单元构建网格,而是基于每个网格面构建网格。以下列表说明了Constant/PolyMesh中每个文件的用途。

1、points

定义向量场中网格的所有节点,其中它们在空间中的位置以米为单位。这些点并非细胞中心,而是网格的角点。要将网格在正x方向上平移1米,必须相应地更改每个节点。不需要为此更改polyMesh子目录中的任何其他结构,节2.4介绍了这一点。

通过使用文本编辑器打开相应的文件,可以更仔细地查看这些点。为了限制输出,忽略标题,只显示前几行:

?>  head -25 constant/polyMesh/points | tail -7
25012 // 节点数量
((-0.0206 0 -0.0005) // 点0的坐标
(-0.01901716308 0 -0.0005) // 点1的坐标
(-0.01749756573 0 -0.0005)
(-0.01603868134 0 -0.0005)
(-0.01463808421 0 -0.0005)

该文件包含一个包含25012个点的列表。此列表不需要以任何方式排序。此外,列表中的所有节点都是唯一的,这意味着相同的点坐标不能多次出现。访问和寻址这些点是通过vectorField中的列表位置从0开始执行的。该位置存储为label。

2、faces

根据点在点向量场中的位置合成网格面,并将其存储在labelListList中。这是一个嵌套列表,每个面包含一个元素。这些元素中的每一个都有自己的labelList,存储用于构造面的points的标签。图2.1显示了labelListList的结构。

每个网格面必须至少由三个节点组成,其大小后面紧跟一个点标签列表。在网格面上,每个节点都通过一条直边与其相邻的点相连[4]。使用定义网格面的节点,可以计算表面积矢量,其方向由右手定则确定。

同样,仅显示faces文件的前几行以保持简短:

?>  head -25 constant/polyMesh/faces | tail -7
49180 // 网格面的数量
(
4(1 20 172 153) // 网格面0包含4个节点,其标签为(1 20 172 153)
4(19 171 172 20)
4(2 21 173 154)
4(20 172 173 21)
4(3 22 174 155)
...
)

从输出的第一行可以看出,网格由49180个网格面组成,上面只显示了其中的一个子集。与面列表的长度49180类似,每个labelList的长度在列表开始之前声明。因此,此处显示的所有面都是从4个点构建的,这些点由它们在点列表中的位置表示。

3、owner

Owner也是一个与存储面的列表具有相同维度的labelList。由于面已构建并存储在面列表中,因此必须定义它们与体网格的从属关系。根据定义,一个网格面只能在两个相邻的网格之间共享。owner列表存储哪个面属于哪个网格,而这是根据网格标签来决定的。具有较低单元格标签的网格拥有该面,其另一个面被视为neighbor。它指示代码第一个面(列表中的索引0)属于标签存储在该位置的网格。

查看下面的owner文件可知,网格面0、1、2、3分别归属于网格单元0、0、1、1。owner文件中的网格面数量与faces文件中的网格面数量一致。

?>  head -25 constant/polyMesh/owner | tail -7

49180
(0
0
1
1

同样,上一章解释了owner-neighbour寻址的工作原理。

4、neighbour

neighbour必须与owner列表结合起来一起考虑,其与owner列表相反。neighbour存储相邻的网格,而不是定义哪个网格拥有每个特定的面。将owner文件与neighbour文件进行比较,可以发现它们的主要区别:owner列表要短得多。这是因为边界面没有相邻的网格。

?>  head -25 constant/polyMesh/neighbour | tail -7

24170
(1
18
2
19

5、boundary

边界包含有关嵌套子字典列表中网格边界的所有信息。边界通常被称为patch或边界patch。与之前的网格组件类似,仅显示了一些相关行:

?>  head -25 constant/polyMesh/boundary | tail -8

5
(
    inlet 
    {
        type        patch;
        nFaces        30;
        startFace    24170;
    }

对于本节中使用的 pitzDaily 示例,边界文件包含 5 个patch的列表。每个patch由一个字典表示,由patch名开始。字典中包含的信息包括:patch类型、面数和起始面。由于面列表的排序,可以使用此约定快速轻松地处理属于某个面片的面。

图 2.2:OpenFOAM 中边界寻址的工作原理

边界面的寻址方法如图 2.2 所示。根据设计,所有没有neighbor的面都被存放在faces列表的末尾,根据他们的owner patch对其进行排序。所有作为边界面的面都必须被边界描述所覆盖。

从用户的角度来看,点和面、owner和neighbor都不需要手动接触或操作。如果手动更改它们,这肯定会破坏网格。但是,根据工作流程,可能需要针对某些设置更改边界文件。更改边界文件的最可能原因是更改patch名称或类型。在此处进行此更改可能比重新运行相应的网格生成器要容易得多。

现在解释了 OpenFOAM 网格的基本结构,接下来将回顾边界类型。有几种类型可以分配给边界,其中一些比其他更常见。区分边界(或patch)和边界条件(见图 2.3)很重要。

图2.3 CAD 几何、计算网格及其边界以及应用于这些边界的边界条件之间的差异和关系的图示

patch是计算域的外边界,其在边界文件中指定,因此是一种拓扑属性。边界和 CAD 几何之间的逻辑联系是两者的表面应尽可能相同。用网格拓扑表示,它是一组面,只有一个owner网格,没有neighbor网格。与patch相反,边界条件分别应用于每个物理场(U、p 等)的patch。patch类型有:

  • Patch。大多数patch(边界)可以用patch类型来描述,因为它是最一般的描述。 Neumann、Dirichlet 或 Cauchy 边界条件都可以应用于这种类型的边界。
  • wall。如果patch被定义为wall,这并不意味着没有流体通过过该边界。它仅使湍流模型能够正确地将壁面函数应用于该patch(参见第 7 章),仍然需要通过速度边界条件明确定义防止流体通过类型为wall的patch。
  • symmetryPlane。将patch类型设置为 SymmetryPlane 声明它充当对称平面。除了 SymmetryPlane 之外,不能对其应用其他边界条件,并且必须将其应用于所有物理场。
  • empty。在二维模拟的情况下,这种类型应该应用于“平面内”的patch。与 SymmetryPlane 类型类似,这些patch的边界条件也必须为所有物理场设置为empty。不会对这些patch应用其他边界条件。两个empty边界之间的所有网格边必须平行,否则无法进行精确的二维模拟。
  • cyclic。如果一个几何结构由多个相同的部件组成(例如螺旋桨叶片或涡轮叶片),则只需将其中一个组件离散化并将其视为位于相同组件之间。对于四叶片螺旋桨,这意味着只有一个叶片是网格化的(90° 网格),并通过将cyclic边界类型分配给具有切线方向法线的patch。然后这些patch将充当物理耦合。
  • wedge。这种边界类型类似于cyclic边界,只是专门为形成小的(例如≤5°)楔形的循环边界设计的。

从执行和兼容性的角度来看,polyMesh 结构的创建方式并不重要,只要网格数据本身是有效的即可。虽然 OpenFOAM 打包了各种网格生成工具,但只要可以进行有效的转换或输出,就可以使用外部第三方网格生成器。

除了上面提到的 OpenFOAM 网格的基本核心组件之外,还有各种可选的网格结构,它们只能用于特定的应用程序。由于它们是可选的,因此无论案例设置如何,它们都可以存在。 OpenFOAM 应用程序会根据需要读取它们,并在它们丢失时向用户报告。

6、Sets及Zones

作为用户,当用户担心时,很容易被 OpenFOAM 中的区域(zones)和集合(sets)以及两者都非常相似的事实所混淆:他们选择网格实体。对使用哪个问题的非常简短的回答是:使用区域,正如 Hrvoje Jasak 通过 Twitter 简要解释的那样(参见图 2.4)。

图2.4:Hrvoje Jasak 解释何时在 OpenFOAM 中使用区域

然而,这仅与求解器应用程序真正相关。如果应用程序以预处理或后处理为中心,则任何一个都可以。Set本质上是 labelHashSets,而Zone继承自 labelLists。两者都可以将任何网格实体(点、面或单元)存储在类似于列表的数据结构中。主要区别在于网格实体的内部处理,特别是在具有拓扑网格变化的并行模拟的情况下。在这种情况下,必须相应地更新列表中的地址,并且只有Zone提供这种方法。

选择通常由工具 setSet 或 topoSet 执行,它们都可以选择网格的子集并对其进行布尔运算。一般来说,这两个实用程序都可以将区域转换为集合,反之亦然。可以为任何网格实体(单元、点或面)创建集合或区域,但 cellSet 和 cellZone 是最常用的两个。区域作为普通字典存储在 constant/polyMesh 中,而集合存储在 constant/polyMesh 的 sets 子目录中。区域和集合以相同的方式存储在文件系统中:作为相应网格实体的标签的长列表。

我们已经发布了一些博客文章,其中包含有关区域和集合如何组装的一定程度的信息 [2, 1]。

2.1.1 CAD几何

导入外部 CAD 软件中生成的几何图形是CFD 工程师的常规任务。在 OpenFOAM 中,这通常使用 snappyHexMesh 执行,但是,稍后将解释这个网格生成器的用法。目前唯一重要的概念是本节仅处理Stereolithography(STL) 文件的导入。支持其他文件类型并以类似的方式工作。 STL 是一种文件格式,可以以三角面片方式存储几何图形的表面。二进制和 ASCII 编码文件都是可能的,但为了简单起见,我们将使用 ASCII 编码。

作为 STL 文件的示例,以下代码段显示了仅由一个三角形组成的 STL 曲面:

solid TRIANGLE
    facet normal -8.55322e-19 -0.950743 0.30998
        outer loop
            vertex -0.439394 1.29391e-18 -0.0625
            vertex -0.442762 0.00226415 -0.0555556
            vertex -0.442762 1.29694e-18 -0.0625
        endloop
    endfacet
endsolid TRIANGLE

在此示例中,仅定义了一个名为 TRIANGLE 的实体。一个 STL 文件可能包含多个实体,这些实体一个接一个地定义。组成表面的每个三角形都有一个法线向量和三个点。

使用 ASCII STL 文件的缺点是它们的文件大小会随着表面分辨率的增加而迅速增长。边没有明确包含,因为文件中只存储了三角形。因此,从 STL 中识别和提取特征边缘有时是一项具有挑战性的任务。

使用 STL 作为文件格式的一个优点是可以获得三角形表面网格,根据定义,它总是具有平面表面组件(三角形)。