17_field
Important
访问 https://aerosand.cn 以获取最近更新。
0. 前言
回忆前文,我们已经讨论了输入输出、命令行参数、时间和网格。OpenFOAM 的计算绝大多数是基于物理场的,本项目开始讨论 OpenFOAM 中的“场”。
本文主要讨论
- 理解场的代码结构
- 理解场的计算参与
- 编译运行 field 项目
1. 项目准备
终端输入命令,建立项目
ofsp
foamNewApp ofsp_17_field
cd ofsp_17_field
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_case
code .测试初始求解器,提供脚本和说明。
2. 场的创建
2.1. dimensionSet
前文用到了 OpenFOAM 的单位预设,其定义在 $FOAM_SRC/OpenFOAM/dimensionSet/dimensionSets.C ,文件中还定义了很多预设单位
再次摘阅如下
| |
同样暂不深究代码实现细节。
我们可以设置自定义变量的物理单位,如
dimensionSet dimAerosand(1,2,3,4,5,6,7); // just for example
2.2. dimensionedType
查找类的定义
API 页面 https://api.openfoam.com/2506/dimensionedType_8H.html
终端输入命令,查找源代码
find $FOAM_SRC -iname dimensionedType.H找到并摘取其中的构造函数如下
| |
相应的,在 OpenFOAM 代码中可以看到如下类似的构造
| |
在 dimensionedType.H 中也定义了配套的成员函数,比如
| |
相应的,我们可以便捷的使用 value() 成员函数,例如对上面的 airPressure 取值,再计算最大值。
maxP = max(airPressure.value());当然 OpenFOAM 中还提供了其他的构造函数和成员函数,我们可以在不同情况下构造自己的有单位物理量。目前阶段来说,我们不需要继续深挖源代码。点到为止,明白确实可以这样用,并且以后按照这种方式去用就可以了。
注意,OpenFOAM 会检查数学计算式等号两遍的最终单位是否相同,如果不同则会强制报错并中断程序,后续代码会面临并解决这个问题。
2.3. volFields
我们会注意到 OpenFOAM 中有典型的语句如下
| |
那么 volScalarField 是什么呢?又是如何构造场的呢?
API 页面
终端输入命令,查阅源文件
find $FOAM_SRC -iname volFieldsFwd.HTip
也许有同学还有疑惑“我怎么知道要查看这个文件”。参考 06_tensor 的讨论,安装 OFextension 插件后,直接在 volScalarField 关键字上右键跳转即可。也可以在 API 页面搜索关键词,以阅读相关页面。
代码 volFieldsFwd.H 中有类型定义如下
...
typedef GeometricField<scalar, fvPatchField, volMesh> volScalarField;
typedef GeometricField<vector, fvPatchField, volMesh> volVectorField;
...可以看到,volFields 包括常见的 volScalarField 和 volVectorField,是体积场量。无论是 volScalarField 还是 volVectorField 其实都是 GeometricField 的模板的不同情况。所以, volFields 只是一种模板类特化分组的称呼,而不是真正的类。
2.4. surfaceFields
同样的,面场量 surfaceFields 包括常见的 surfaceScalarField 和 surfaceVectorField 。
API 页面 https://api.openfoam.com/2506/surfaceFieldsFwd_8H.html
终端输入命令,查阅源文件
find $FOAM_SRC -iname surfaceFieldsFwd.H代码 surfaceFieldsFwd.H 中有类型定义如下
...
typedef GeometricField<scalar, fvsPatchField, surfaceMesh> surfaceScalarField;
typedef GeometricField<vector, fvsPatchField, surfaceMesh> surfaceVectorField;
...可见,surfaceFields 也是 GeometricField 的模板的不同情况。
Tip
surfaceFields 的 surface 都可以写完整,为什么 volFields 的 volume 要简写成 vol 呢?也许是历史代码的原因吧。。。
2.5. GeometricField
基于上文的讨论,我们查阅 GeometricField.H 代码
API 页面 https://api.openfoam.com/2506/GeometricField_8H.html
终端输入命令,查阅源文件
find $FOAM_SRC -iname GeometricField.H摘取类的主要结构和几个常用构造函数如下
| |
上述构造函数,对应实际的场的构造,分别举例如下
| |
这三个例子对应了最常见的场的构造函数,其他构造函数以及更深入的代码层面的解析在以后会陆续介绍,无需担心。读者也可以尝试自行多阅读几个。
3. 场的时间推进
我们以压力场为例,结合场的创建,讨论一下场的时间推进计算。
3.1. 单时间步计算
主源码 ofsp_17_field.C 内容如下
| |
Tip
注意到主源码中的场的覆写是不区分初始时间步和后续时间步的,而且 foamCleanTutorials 命令不能恢复初始时间步文件夹内的场文件的内容。所以,为了避免初始条件被覆盖,强烈推荐备份初始条件。一旦初始场被覆盖,可以随时复原。
3.1.1. 脚本
脚本 caserun 修改为
| |
脚本 caseclean 修改为
| |
可以看到修改后的脚本更加通用。后续如非特别说明,可以直接拷贝使用此脚本。
3.1.2. 编译运行
终端输入命令,编译运行项目
wclean
wmake
./caseclean
./caserun终端输出如下
Create time
Create mesh for time = 0
Max p = 1.995
Min p = 0
Average p = 0.9975
Sum p = 399
ExecutionTime = 0.01 s ClockTime = 0 s
End查看 debug_case/ 文件夹下,多了 0.005/ 文件夹,其中的 0.005/p 文件中写入了计算的场。最后的输出就是最新时间步的场的值。
3.2. 多时间步计算
如何在多个时间步上推进计算呢?
主源码修改如下
| |
Tip
关于写成 ++runTime 而不是 runTime++ 的讨论
- 原则上来说,前置递增 ++a 比 后置递增 a++ 更高效
- 前置递增 ++a 直接递增并返回自身,避免潜在拷贝
- 后置递增则需要创建临时副本再增加并返回临时副本
- 对于内置类型(int, float, 指针等)现代编译器一般会优化掉差异
- 对于复杂类型(类、迭代器)前置更高效
编译运行项目
终端输出如下
Create time
Create mesh for time = 0
Max p = 5.985
Min p = 0
Average p = 2.9925
Sum p = 1197
ExecutionTime = 0.01 s ClockTime = 0 s
End观察 debug_case/ 文件夹下多了三个时间步的文件夹,现在的测试算例文件结构如下
tree debug_case/
debug_case/
├── 0
│ ├── p
│ └── U
├── 0.005
│ └── p
├── 0.01
│ └── p
├── 0.015
│ └── p
├── 0.org
│ ├── p
│ └── U
├── constant
│ ├── polyMesh/...
│ └── transportProperties
├── log.run
└── system/...查看各个时间步的压力场值,可以知道终端最后输出的值为最新时间步的计算值。
3.3. 字典控制计算
即使是多时间步计算,我们也是直接修改主源码的计算时间步个数,每次修改都要重新编译整个应用。如果是大型应用,重新编译会相当麻烦。
在实际工作中,我们希望在应用编译完成后,也可以通过外部文件去控制计算,也就是使用 OpenFOAM 的字典文件 controlDict 控制计算。
主源码修改为
| |
编译运行项目
终端输出如下
Create time
Create mesh for time = 0
Max p = 199.5
Min p = 0
Average p = 99.75
Sum p = 39900
ExecutionTime = 0 s ClockTime = 0 s
End测试算例 debug_case/ 文件多了几个时间步文件夹,分别是 0.1, 0.2, 0.3, 0.4, 0.5,写入的时间步由 system/controlDict 控制。
字典 controlDict 的部分参数如下(每 0.005*20 = 0.1 写入一次)
| |
这些参数都可以随时在字典中修改测试,无需重新编译项目。
4. 小结
本文介绍了 OpenFOAM 中的“场”。从代码和项目练习中,读者可以理解场是如何参与到计算项目中的。
本文完成讨论
- 理解场的代码结构
- 理解场的计算参与
- 编译运行 field 项目
支持我们
Tip
希望这里的分享可以对坚持、热爱又勇敢的您有所帮助。
如果这里的分享对您有帮助,您的评论、转发和赞助将对本系列以及后续其他系列的更新、勘误、迭代和完善都有很大的意义,这些行动也会为后来的新同学的学习有很大的助益。
赞助打赏时的信息和留言将用于展示和感谢。
