ofsp2025-03
参考:
- https://github.com/UnnamedMoose/BasicOpenFOAMProgrammingTutorials
- https://www.topcfd.cn/simulation/solve/openfoam/openfoam-program/
- https://www.tfd.chalmers.se/~hani/kurser/OS_CFD/
- https://github.com/ParticulateFlow/OSCCAR-doc/blob/master/openFoamUserManual_PFM.pdf
- https://www.youtube.com/watch?v=KB9HhggUi_E&ab_channel=UCLOpenFOAMWorkshop
- http://dyfluid.com/#
- https://ss1.xrea.com/penguinitis.g1.xrea.com/study/OpenFOAM/index.html
感谢原作者们的无私引路和宝贵工作。
OpenFOAM 的很多应用都可以通过命令行来制定参数运行。虽然用户有时候会觉得有些陌生,但是用好命令行可以高效灵活的处理各种问题。
另外,新同学很难不注意到求解器总是有几个固定的头文件,包括 setRootCase.H
,createTime.H
和 createMesh.H
。网络上找到的代码解析常常只有一句注释介绍功能,也许无法消除困惑感。
本文同样会从 C++ 开始,简单介绍命令行参数,然后进一步讨论 setRootCase.H
头文件。
OpenFOAM 的命令行
OpenFOAM 命令行基础的使用格式如下
// terminal
<application> <options> <arguments>
比如典型的命令行使用如下
// terminal
blockMesh -case debug_case
使用 -help
选项可以查看更多的命令行选项,例如终端输入 blockMesh -help
,终端会输出如下内容
// terminal
Usage: blockMesh [OPTIONS]
Options:
-case <dir> Case directory (instead of current directory)
-dict <file> Alternative blockMeshDict
-merge-points Geometric point merging instead of topological merging
[default for 1912 and earlier].
-no-clean Do not remove polyMesh/ directory or files
-region <name> Specify mesh region (default: region0)
-sets Write cellZones as cellSets too (for processing purposes)
-time <time> Specify a time to write mesh to (default: constant)
-verbose Force verbose output. (Can be used multiple times)
-write-vtk Write topology as VTU file and exit
-doc Display documentation in browser
-help Display short help and exit
-help-compat Display compatibility options and exit
-help-full Display full help and exit
Block mesh generator.
The ordering of vertex and face labels within a block as shown below.
For the local vertex numbering in the sequence 0 to 7:
Faces 0, 1 (x-direction) are left, right.
Faces 2, 3 (y-direction) are front, back.
Faces 4, 5 (z-direction) are bottom, top.
7 ---- 6
f5 |\ :\ f3
| | 4 ---- 5 \
| 3.|....2 | \
| \| \| f2
f4 0 ---- 1
Y Z
\ | f0 ------ f1
\|
o--- X
Using: OpenFOAM-2406 (2406) - visit www.openfoam.com
Build: _9bfe8264-20241212 (patch=241212)
Arch: LSB;label=32;scalar=64
一般,数据处理和后处理的时候也会大量使用命令行。
例如,使用
// terminal
foamPostProcess -help
具体的使用技巧暂不展开,以后会专门讨论。
我们先简单回顾一下C++中命令行参数的使用。
C++ 实现
项目准备
建立项目文件夹并进入
// terminal
ofsp
mkdir ofsp_02_args_cpp
使用 vscode 的 C++/C Project Generator
打开此项目。
主函数参数
我们在初学 C++ 的时候,主函数的参数一般留空,即有如下形式
...
int main() // 省略参数列表
{
...
}
当进一步深入 C++ 开发时,了解到主函数的更一般写法为
...
int main(int argc, char *argv[]) {}
// 或者
int main(int argc, char **argv) {}
argc
是argument count
的缩写,保存程序运行时传递给主函数的参数个数argv
是argument vector
的缩写,保存程序运行时传递给主函数的具体参数的字符型指针,每个指针都指向一个具体的参数。argv[0]
指向程序运行时的全路径名称argv[1]
指向程序运行时命令行中执行程序名后第一个字符串argv[2]
指向程序运行时命令行中执行程序名后第二个字符串- 其他以此类推
主源码
主源码 src/main.cpp
如下所示
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Number of arguments = " << argc << std::endl;
for (int i=0; i<argc; ++i)
{
std::cout << "Argument " << i << ": "
<< argv[i] << std::endl;
}
return 0;
}
终端直接编译运行
// terminal
make run
运行结果如下
Number of arguments = 1
Argument 0: ./output/main
如果运行时增加参数,例如
// terminal
./output/main hi hey hello
运行结果如下
Number of arguments = 4
Argument 0: ./output/main
Argument 1: hi
Argument 2: hey
Argument 3: hello
通过运行结果可以看到,argc
当然就是参数的总个数,argv[0]
则是也就是应用名称本身,其他参数按顺序类推。
比如,对于以下命令行
blockMesh -case debug_case
其中,argc
等于 3
,而 argv[0]
是blockMesh
,argv[1]
是 -case
,argv[2]
是 debug_case
。
总结来说,命令行中的每一个参数其实都可以在程序中通过主函数参数被调用,以便参加程序运行和计算。
OpenFOAM 实现
基于主函数命令行参数的了解,下面讨论 OpenFOAM 中的主函数参数等内容。
项目准备
// terminal
ofsp
foamNewApp ofsp_02_args
cd ofsp_02_args
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_case
code .
文件结构如下
ofsp_02_args/
├── debug_case
│ ├── 0
│ │ ├── p
│ │ └── U
│ ├── constant
│ │ └── transportProperties
│ └── system
│ ├── blockMeshDict
│ ├── controlDict
│ ├── decomposeParDict
│ ├── fvSchemes
│ ├── fvSolution
│ └── PDRblockMeshDict
├── Make
│ ├── files
│ └── options
└── ofsp_02_args.C
以后非特别情况不再赘述简单的文件结构。
脚本和说明
新建脚本
// terminal
code caserun caseclean README.md
脚本和说明文档的内容略。
setRootCase.H
我们看一下主源码中的头文件 setRootCase.H
// termianl
find $FOAM_SRC -iname setRootCase.H
// $FOAM_SRC/OpenFOAM/include/setRootCase.H
代码具体为
// Construct from (int argc, char* argv[]),
// - use argList::argsMandatory() to decide on checking command arguments.
// - check validity of the options
Foam::argList args(argc, argv); // 基于argc和argv,构造argList类型的args对象
if (!args.checkRootCase()) // 如果检查命令行参数(算例路径)错误
{
Foam::FatalError.exit(); // 报错退出
}
// User can also perform checks on otherwise optional arguments.
// Eg,
//
// if (!args.check(true, false))
// {
// Foam::FatalError.exit();
// }
// Force dlOpen of FOAM_DLOPEN_LIBS (principally for Windows applications)
#include "foamDlOpenLibs.H" // 兼容性处理,无需深究
代码讨论
- 构造参数列表的对象
args
(所以此 H 文件需要放在所有和命令行参数相关的代码之后) - 该 H 文件检查了该应用的参数是否正确
- 如果参数列表检查不通过,则报错退出
构造的类型 argList
类是一个非常基础的类,具有很多的成员数据和成员方法。我们大概挑几处代码作为切入点简单了解一下 argList
类。
// terminal
find $FOAM_SRC -iname argList.H
// /usr/lib/openfoam/openfoam2306/src/OpenFOAM/global/argList/argList.H
打开类的声明 argList.H
,内容如下
...
class argList
{
...
public:
...
// 基于参数的构造函数
argList
(
int& argc, // 主函数参数个数
char**& argv, // 主函数参数的指针
bool checkArgs = argList::argsMandatory(),
bool checkOpts = true,
bool initialise = true
);
...
//- Check root path and case path // 检查根目录路径和算例路径
bool checkRootCase() const;
...
...
关于 checkRootCase()
成员函数的实现,需要去看同目录下的类的定义,即 argList.C
文件,摘取部分内容如下
...
bool Foam::argList::checkRootCase() const
{
if (!fileHandler().isDir(rootPath())) // 如果根目录路径有问题,则报错
{
FatalError
<< executable_
<< ": cannot open root directory " << rootPath()
<< endl;
return false;
}
const fileName pathDir(fileHandler().filePath(path(), false));
// 如果算例路径有问题,则报错
if (checkProcessorDirectories_ && pathDir.empty() && Pstream::master())
{
// Allow non-existent processor directories on sub-processes,
// to be created later (e.g. redistributePar)
FatalError
<< executable_
<< ": cannot open case directory " << path()
<< endl;
return false;
}
return true;
}
...
方法的实现较为复杂,对于现阶段来说,没有必要过于深入基础类代码。目前阶段只需要做到心里有数、看到不那么陌生即可。
在上一篇文章的讨论中,也可以深入看一下 OpenFOAM 的
IOdictionary
类,终端使用命find $FOAM_SRC -iname IOdictionary.H
,可以找到对应的构造函数原型。也可以查找到IOobject.H
中的构造函数原型。目前阶段,读者不宜深挖代码。更深入的代码讨论参见
ofsc(openfoam sharing coding)
系列。
主源码
我们一起写一个可以使用命令行参数的项目。
帮助信息
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"Demonstrates how to handle command line options.\n\n"
"Application arguments:\n"
"----------------------\n"
" mathLib - Eigen/GSL/Armodillo/BLAS\n"
" level - computation speedup\n"
);
#include "setRootCase.H" // 放在命令行相关代码的最后
#include "createTime.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //
编译后查看帮助信息
// terminal
wclean
wmake
ofsp_02_args -help
终端输出
Usage: ofsp_02_args [OPTIONS]
Options:
-case <dir> Case directory (instead of current directory)
-decomposeParDict <file>
Alternative decomposePar dictionary file
-parallel Run in parallel
-doc Display documentation in browser
-help Display short help and exit
-help-full Display full help and exit
Demonstrates how to handle command line options.
Application arguments:
----------------------
mathLib - Eigen/GSL/Armodillo/BLAS
level - computation speedup
Using: OpenFOAM-2306 (2306) - visit www.openfoam.com
Build: _fbf00d6b-20230626
Arch: LSB;label=32;scalar=64
可以看到其中有我们自定义的帮助信息,如下
Demonstrates how to handle command line options.
Application arguments:
----------------------
mathLib - Eigen/GSL/Armodillo/BLAS
level - computation speedup
应用参数
应用参数(arguments)在执行应用命令时是强制需要的,下一节的应用选项(option)是可选的。
给主源码增加应用参数如下
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"Demonstrates how to handle command line options.\n\n"
"Application arguments:\n"
"----------------------\n"
" mathLib - Eigen/GSL/Armodillo/BLAS\n"
" level - computation speedup\n"
);
argList::validArgs.append("mathLib");
argList::validArgs.append("level");
// 将应用参数增补到主函数参数列表里
// 在运行程序时,这两个参数是需要强制给定的
#include "setRootCase.H" // 构造OpenFOAM的argList类型的args对象
#include "createTime.H"
#include "createMesh.H"
// args的第0参数是程序名本身
const word args1 = args[1]; // args第1参数就是增补的第1个参数
const scalar args2 = args.get<scalar>(2); // 增补的第2个参数
// 显示参数
Info<< "Solver setup: " << nl
<< " use : " << args1 << nl
<< " speedup : " << args2 << nl
<< nl << endl;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //
终端运行是必须要提供相应类型的参数
编译后运行
// terminal
wclean
wmake
blockMesh -case debug_case
ofsp_02_args -case debug_case GSL 2
结果为
Create time
Create mesh for time = 0
Solver setup:
use : GSL
speedup : 2
ExecutionTime = 0 s ClockTime = 0 s
End
所以应用参数有什么用出呢?
当我们拿到应用参数,这些应用参数就可以进一步参与到主程序的计算中。
应用选项
应用选项在运行时可写可不写。
主源码如下
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"Demonstrates how to handle command line options.\n\n"
"Application arguments:\n"
"----------------------\n"
" mathLib - Eigen/GSL/Armodillo/BLAS\n"
" level - computation speedup\n"
);
// 应用参数要在 setRootCase.H 构造 args 之前
argList::validArgs.append("mathLib");
argList::validArgs.append("level");
// 应用选项也要在 setRootCase.H 构造 args 之前
argList::addOption
(
"dict",
"word",
"Use addtional dictionary (just for example)"
);
argList::addOption
(
"nPrecision",
"label",
"Set the precision level (just for example)"
);
argList::addBoolOption
(
"log",
"output the log"
);
// setRootCase.H 将构造argList类型的args对象
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
const word args1 = args[1];
const scalar args2 = args.get<scalar>(2);
Info<< "Solver setup: " << nl
<< " use : " << args1 << nl
<< " speedup : " << args2 << nl
<< nl << endl;
// 创建dict_并给默认初始值
fileName dict_("./system/myDict");
if (args.found("dict"))
{
args.readIfPresent("dict",dict_);
// 如果使用了 -dict 选项,则使用 -dict 后的值
// 也许会有同学疑问上面这句代码为什么能读取 -dict 后的值并传给 dict_
// 建议先不要深究,以后熟悉C++了可以翻阅OpenFOAM更底层的源代码
Info<< "Reading myDict " << endl;
}
Info<< "Dictionary from " << dict_ << nl << endl;
// 创建 label 类型的对象并给默认初始值
label nPrecision_(6);
args.readIfPresent("nPrecision",nPrecision_);
Info<< "Precision is " << nPrecision_ << endl;
const bool log_ = args.found("log");
if (log_)
{
Info<< "Output the logs" << endl;
}
// 只要使用 -log 选项,就会执行一些方法,比如这里的输出
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //
编译运行应用
// terminal
wclean
wmake
blockMesh -case debug_case
ofsp_02_args -case debug_case GSL 2 -dict ../constant/myDict -nPrecision 12
运行结果为
Create time
Create mesh for time = 0
Solver setup:
use : GSL
speedup : 2
Reading myDict
Dictionary from "../constant/myDict"
Precision is 12
ExecutionTime = 0 s ClockTime = 0 s
End
如果运行应用如下
ofsp_02_args -case debug_case GSL 2 -dict ../constant/myDict -log
运行结果为
Create time
Create mesh for time = 0
Solver setup:
use : GSL
speedup : 2
Reading myDict
Dictionary from "../constant/myDict"
Precision is 0
Output the logs
ExecutionTime = 0.01 s ClockTime = 0 s
End
应用选项 -dict
也有默认值,可以尝试省略并运行应用。
通过使用不同的应用选项,看到各行源代码的效果。
小结
从 C++ 基础出发, 简单讨论了主函数运行时的帮助、参数和选项。虽然上面的开发都是仅停留在表面做做样子,但目的也是使读者对 OpenFOAM 中运行应用的命令行方法有了一些了解,以后也可以在此基础上深入。更深入的开发见后续讨论。
本文基本搞明白了 OpenFOAM 求解器中必备的 setRootCase.H
到底是什么,那么 createTime.H
头文件到底是什么呢?