湍流模型中的 RTS 机制分析

有了上一篇博文的基础,就可以来填一个了,即分析 OpenFOAM 中湍流模型框架中的 RTS 。上一篇博文,使用的程序比较简单,这里通过一个实际使用 RTS 机制的例子来加深对 RTS 的理解。

经过前面对那段简单代码的分析,可以知道, declareRunTimeSelectionTable 宏函数的主要功能是声明了一个 hashTable,并定义了一个指向这个hashTable 的指针, 然后还声明了几个辅助的类。 defineRunTimeSelectionTable 这个宏函数的主要作用是对 declareRunTimeSelectionTable 中的 hashTable 指针进行了初始化。 addToRunTimeSelectionTable 的主要作用是将当前类的类名以及返回当前类的对象的一个函数分别作为 hashTable 的 key 和 value 插入到 hashTable 中。下面来看这些宏函数在湍流模型框架中是怎么使用的。

1. turbulenceModel 类

类体中,调用 declareRunTimeNewSelectionTable 宏函数

1
2
3
4
5
6
7
8
9
10
11
12
13
 declareRunTimeNewSelectionTable
(
autoPtr,
turbulenceModel,
turbulenceModel,
(
const volVectorField& U,
const surfaceScalarField& phi,
transportModel& transport,
const word& turbulenceModelName
),

(U, phi, transport, turbulenceModelName)
);

注意这里用的是 declareRunTimeNewSelectionTable!与 declareRunTimeSelectionTable 区别在于,declareRunTimeNewSelectionTable 这个宏函数定义的插入到 hashTable 中的那个函数,返回值不是派生类的对象,而是派生类中的 New 函数的返回值!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static autoPtr< baseType > New##baseType argList                      \
{ \
return autoPtr< baseType >(baseType##Type::New parList.ptr()); \
}

......
if \
( \
!argNames##ConstructorTablePtr_->insert \
( \
lookup, \
New##baseType \
) \
) \

这说明,这里的派生类 RASModel 将不会作为一个具体的湍流模型来使用。而是用来选择 RAS类型的具体湍流模型的一个跳板。

类体外,调用

1
2
defineTypeNameAndDebug(turbulenceModel, 0);
defineRunTimeSelectionTable(turbulenceModel, turbulenceModel);

注意,这里没有调用 addToRunTimeSelectionTable 宏函数, turbulenceModel 类是基类,也不会作为具体的湍流模型来调用,所以不需要将它自己添加到 hashTable

2. RASModel

turbulenceModel 类下一层的派生类是 RASModelLESModel 。先来看 RASModel,这个类类体里调用了 declareRunTimeSelectionTable

1
2
3
4
5
6
7
8
9
10
11
12
13
declareRunTimeSelectionTable
(
autoPtr,
RASModel,
dictionary,
(
const volVectorField& U,
const surfaceScalarField& phi,
transportModel& transport,
const word& turbulenceModelName
),

(U, phi, transport, turbulenceModelName)
);

类体外调用了

1
2
3
defineTypeNameAndDebug(RASModel, 0);
defineRunTimeSelectionTable(RASModel, dictionary);
addToRunTimeSelectionTable(turbulenceModel, RASModel, turbulenceModel);

根据前面的分析,这里 RASModel 又创建了一个新的 hashTable, 用的是 declareRunTimeSelectionTabledefineRunTimeSelectionTable(RASModel, dictionary); ,同时,RASModel类 本身又添加到了 turbulenceModel 中建立的 hashTable 里: addToRunTimeSelectionTable(turbulenceModel, RASModel, turbulenceModel);

RASModel 类之下的派生类,就是具体的湍流模型了,这里以 kEpsilon 模型为例:

  • kEpsilon
    具体的湍流模型,如kEpsilon ,只需要添加到上面基类中创建的 hashTable 中,就能保证其能被调用到。
    1
    addToRunTimeSelectionTable(RASModel, kEpsilon, dictionary);

这里是添加到了 RASModel 中创建的 hashTable 里。

3. LESModel

LESModel 类也是继承自 turbulenceModel 的,所以其处理方法跟 RASModel 是一样的。不过 LES 类的模型的继承关系略比 RAS 类的复杂一点(参看这篇中的 LES 模型继承关系图)。在这个图中,中间层的 GenEddyVisc 等4个虚线框中的类不是作为具体的湍流模型来调用的,这里有必要看一下这样的中间类在 RTS 机制中是怎么处理的。检查这几个类的代码,可以发现 GenEddyViscGenSGSStress 中只是在类体外调用了 defineTypeNameWithName(GenEddyVisc, "GenEddyVisc");scaleSimilarity 在类体中调用了 TypeName,类体外调用了 defineTypeNameAndDebugDESModel 中没有任何处理。可见这些类只是对 typeName 做了处理,并没有调用 addToRunTimeSelectionTable
RASModel 一样, LESModel 之下派生的具体的湍流模型则需要调用 addToRunTimeSelectionTable 来将自己添加到 LESModel 中定义的 hashTable 中。

由上可知,湍流模型的调用过程大致是这样的:
求解器里创建一个 turbulenceModel 类型的 autoPtr,并调用 turbulenceModel::New 来初始化。 turbulenceModel::New 从 turbulenceProperties 文件中读取关键字,假设读取到 simulationType 为 RASModel ,则 turbulenceModel::NewcstrIter() 返回的是 RASModel::New,于是 cstrIter()(U, phi, transport, turbulenceModelName) 则是在调用 RASModel::New。然后, RASModel::New 从 RASProperties 文件里读取关键字,并根据读取到的内容,从 RASModel 类中创建的 hashTable 里查找对应的湍流模型,假设从 RASProperties 中读取到的是 kEpsilon, 则返回一个 kEpsilon 模型的对象。最终结果是,求解器里创建的turbulenceModel 类型的 autoPtr 指向了 kEpsilon 类的对象,这就实现了对 kEpsilon 模型的调用。