OpenFOAM开发编程基础02 主函数参数列表类初步
参考:
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
。网络上找到的代码解析常常只有一句注释介绍功能,也许无法消除困惑感。
更新和勘误详见 aerosand.cn。
本文同样从 C++ 开始,简单介绍主函数参数,在此基础上讨论 setRootCase.H
头文件。
建立本文的项目文件夹并进入
// terminal
cd /home/aerosand/aerosand/ofsp
mkdir 02_args
cd 02_args
C++ 实现
主函数参数
我们在初学 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]
指向程序运行时命令行中执行程序名后第二个字符串 -
其他以此类推
项目实现
新建项目 02_arg/02_01_args/
主源码 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
基于主函数参数列表的了解,下面讨论 OpenFOAM 中的主函数参数等内容。
OpenFOAM 实现
应用准备
// terminal
cd /home/aerosand/aerosand/ofsp/02_args/
foamNewApp 02_02_args
cd 02_02_args
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_case
code .
文件结构如下
|- 02_02_args/
|- debug_case/
|- 0/
|- constant/
|- system/
|- Make/
|- files
|- options
|- 02_02_args.C
以后非特别情况不再赘述文件结构。
脚本和说明
新建脚本
// terminal
code _appmake.sh _appclean.sh _caserun.sh _caseclean.sh 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); // 构造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" // 兼容性处理,无需深究
代码讨论
-
该 H 文件检查了该应用的参数是否正确 -
构造参数列表的对象 args
(所以此 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
);
...
...
方法的实现较为复杂,对于现阶段来说,基础类没有必要去深入讨论代码的实现部分,目前只需要做到心里有数、看到不那么陌生即可。
在上一篇文章的讨论中,也可以深入看一下 OpenFOAM 的
IOdictionary
类,终端使用命find $FOAM_SRC -iname IOdictionary.H
,可以找到对应的构造函数原型。也可以查找到IOobject.H
中的构造函数原型。
主源码
帮助信息
#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
02_02_args -help
终端输出
Usage: 02_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
可以看到我们自定义的帮助信息。
应用参数
应用参数(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
blockMesh -case debug_case
02_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
blockMesh -case debug_case
02_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
如果运行应用如下
02_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
头文件到底是什么呢?
文章来源: Aerosand