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.HcreateTime.HcreateMesh.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) {}
  • argcargument count 的缩写,保存程序运行时传递给主函数的参数个数
  • argvargument 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

默认 最新
当前暂无评论,小编等你评论哦!
点赞 评论 收藏
关注