OpenFOAM 使用Time 控制程序主循环

接着上一个post OpenFOAM创建应用程序讲。上次的应用程序出了Info输出流是OpenFOAM提供的,其他的东西就是平白的C语言代码,跟OpenFOAM没有啥关系。这次来试试case目录里面的system/controlDict文件是如何跟我们的应用程序关联起来的。

先看一个例子$FOAM_SOLVERS/basic/laplacianFoam这个求解Laplace方程的标准求解器里面的是怎么弄的。

可以发现laplacianFoam.C 开头是这样的(其实大多数求解器都是这样开头的):

1
2
3
4
5
6
7
8
9
10
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H"
...
}

后面的两行应该是创建网格和物理场,这里我们暂时不讨论。主要看setRootCase.HcreateTime.H
这两个文件在当前目录$FOAM_SOLVERS/basic/laplacianFoam目录里面找不到、去Make/options里面看看头文件位置发现应该是在$FOAM_SRC/finiteVoluem/lnInclude下面,或者直接使用文件查找命令find在$FOAM_SRC目录按文件名查找它们。比如这个setRootCase.H文件

1
2
3
$ find $FOAM_SRC -name setRootCase.H
$FOAM_SRC/OpenFOAM/include/setRootCase.H
$FOAM_SRC/OpenFOAM/lnInclude/setRootCase.H

我这里的输出把用$FOAM_SRC替换了路径前缀/home/lhzhu/OpenFOAM/OpenFOAM-2.4.0/src。这里输出有两行,其实第二个文件只是第一个文件的链接(对应Windows的快捷方式)。下面这个命令可以验证。

1
$ ls -l $FOAM_SRC/OpenFOAM/lnInclude/setRootCase.H

我们可以看看这个setRootCase.HcreateTime.H的内容:

1
$ cat $FOAM_SRC/OpenFOAM/lnInclude/setRootCase.H

1
2
3
4
5
6
7
8
9
//
// setRootCase.H
// ~~~~~~~~~~~~~
Foam::argList args(argc, argv);
if (!args.checkRootCase())
{
Foam::FatalError.exit();
}
1
$ cat $FOAM_SRC/OpenFOAM/lnInclude/createTime.H
1
2
3
4
5
6
7
//
// createTime.H
// ~~~~~~~~~~~~
Foam::Info<< "Create time\n" << Foam::endl;
Foam::Time runTime(Foam::Time::controlDictName, args);

可以发现这两个文件非常简单,总共加起来就是几行。OpenFOAM把这两个文件放在这里是因为基本所有的求解器都要来这么几行。其实我们也完全可以手动把这几行手动加到main函数开头。#include XXX.H这种语句原本就是告诉编译器预处理时把XXX.H文件的内容贴到此处

#include "createTime.H" 创建一个Time对象。我们通过这个对象和system/controlDict文件来让程序输出10句话。这跟让显式求解器程序往前推进10个时间步概念上类似。写显式求解器,去参考典型的可压缩求解器rhoCentralFoam, 看看里面是怎么推进时间步的。这个求解器在$FOAM_SOLVERS/compressible/rhoCentralFoam。可以发现是在main函数里面用while(runTime.run()){runTime++; ...} 这段代码来循环推进时间步的。

现在我们接着改我们的HelloWorld程序。在HelloWorld.C的基础上加上前面讨论的这些行,让程序每一个都输出”Running…” 和当前的物理时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
int main(int argc, char* argv[])
{
#include "setRootCase.H"
#include "createTime.H"
...
// use runTime
Info << "Starting time looping" << endl;
while(runTime.run())
{
runTime++;
Info << "Running... " << "Time = " << runTime.timeName() << endl;
}
Info << "end!" << endl;
return 0;
}

物理时间可以用runTime.timeName()得到。

再次wmake编译HelloWorld。

接着我们来准备一个case目录来运行这个HelloWorld。前面开发程序的终端先不管, 另外开个ssh终端(命令窗口),

1
2
3
4
5
$ of240
$ run
$ mkdir -p HelloWorld/system
$ cd HelloWorld
$ cp $FOAM_TUTORIALS/basic/laplacianFoam/flange/system/controlDict system

这里我们直接从一个教程算例中复制了controDict文件过来。接下来修改里面里面的endTime为1,deltaTime为0.1,表示时间步长为0.1,到时间为1时结束运行,这样就可以让程序主循环跑10次了。

然后就可以运行HelloWorld看看结果了:

1
$ HelloWorld

程序输出的主要部分如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Create time
Hello world!
Dear FOAMers,
I come from Wuhan, China
I'm from MyLib, funA()
I'm from MyLib, funB()
Starting time looping
Running... Time = 0.1
Running... Time = 0.2
Running... Time = 0.3
Running... Time = 0.4
Running... Time = 0.5
Running... Time = 0.6
Running... Time = 0.7
Running... Time = 0.8
Running... Time = 0.9
Running... Time = 1
end!

现在可以用controlDict控制我们的HelloWorld的主循环了。注意这里用到的时固定时间步长循环的方法,后面我们会讲到利用CFL数(Conant数)控制可变时间步长的方法。

访问当前步的时间步长可以用Time.deltaValue()函数, 如

1
const scalar dt = runTime.deltaValue();

我们如果想在system/controlDict自定义新的控制参数,可以使用Time.controlDict()访问这些参数。下面我们分别在controlDict里面增加一个浮点数(testParameter)和一个整数参数(labelTp):

1
2
3
4
5
6
7
8
9
10
11
$ tail system/controlDict
timePrecision 6;
runTimeModifiable true;
testParameter 1.4;
labelTp 2;
// ************************************************************************* //

在HelloWorld程序里面通过Time.controlDict()对象可以访问这些参数,

1
2
3
4
5
// self-defined control parameter
scalar tp = runTime.controlDict().lookupOrDefault<scalar>("testParameter", 2.0);
Info << "testParameter = " << tp << endl;
label ltp = runTime.controlDict().lookupOrDefault<label>("labelTp", 2.0);
Info << "labelTp = " << ltp << endl;

注意: OpenFOAM里面使用label表示正整数、使用scalar表示浮点数,而scalar默认就是double类型的。

当然Time.controlDict也可以访问其他通用的控制参数。 另外,虽然这个Time.controlDict()机制可以往程序里面传递参数,但是我们一般不把时间步长控制以外的参数比如类似物理粘性放到这个system/controlDict里面,而是单独弄一个输出输出字典文件(IOdictionary)来读参数。本身system/controlDict在OpenFOAM内部也表示为IOdictionary对象。

DONE!!!