OpenFOAM 中的热物理类之 hashTable 的创建

这一篇来看一下热物理类是如何编译并创建储存了可选模型的 hashTable 的。

根据以前的经验,编译和构建 hashTable 肯定是跟这个源文件有关:src/thermophysicalModels/basic/rhoThermo/rhoThermos
这个文件里,全部都是在调用宏函数。而且,从宏函数的参数来看,似乎就是个排列组合的游戏,把所有可用的组合都写了一遍。

1
2
3
4
5
6
7
8
9
10
11
12
makeThermo
(
rhoThermo,
heRhoThermo,
pureMixture,
constTransport,
sensibleInternalEnergy,
hConstThermo,
perfectGas,
specie
);
......

为了弄清这部分内容,需要先理解 makeThermo 这个宏函数的定义,见 src/thermophysicalModels/basic/fluidThermo/makeThermo.H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#define makeThermoTypedefs(BaseThermo,Cthermo,Mixture,Transport,Type,Thermo,EqnOfState,Specie)\
\
typedef \
Transport \
< \
species::thermo \
< \
Thermo \
< \
EqnOfState \
< \
Specie \
> \
>, \
Type \
> \
> Transport##Type##Thermo##EqnOfState##Specie; \
\
typedef \
Cthermo \
< \
BaseThermo, \
Mixture<Transport##Type##Thermo##EqnOfState##Specie> \
> Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie; \
\
defineTemplateTypeNameAndDebugWithName \
( \
Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie, \
( \
#Cthermo"<"#Mixture"<" \
+ Transport##Type##Thermo##EqnOfState##Specie::typeName() \
+ ">>" \
).c_str(), \
0 \
);


#define makeThermo(BaseThermo,Cthermo,Mixture,Transport,Type,Thermo,EqnOfState,Specie)\
\
makeThermoTypedefs \
( \
BaseThermo, \
Cthermo, \
Mixture, \
Transport, \
Type, \
Thermo, \
EqnOfState, \
Specie \
) \
\
addToRunTimeSelectionTable \
( \
basicThermo, \
Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie, \
fvMesh \
); \
\
addToRunTimeSelectionTable \
( \
fluidThermo, \
Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie, \
fvMesh \
); \
\
addToRunTimeSelectionTable \
( \
BaseThermo, \
Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie, \
fvMesh \
);

可见,在 makeThermo 这个宏函数里,先调用了 makeThermoTypedefs 宏函数,然后调用 addToRunTimeSelectionTable 函数。按照之前对 RTS 机制的理解,调用 addToRunTimeSelectionTable 函数的作用是往 hashTable 里插入元素,细节不需再赘述。这里主要来看看 makeThermoTypedefs 函数的功能,以上文列举的这个实例为例。先来看第一个 typedef:将实例中的参数代入后,

1
2
3
typedef                                                                                       \
constTransport<species::thermo<hConstThermo<perfectGas<specie>>,sensibleInternalEnergy>> \
constTransportsensibleEnthalpyhConstThermoperfectGasspecie;

第二个 typedef

1
2
3
4
5
6
typedef                                                                                          \
heRhoThermo \
< \
rhoThermo, \
pureMixture<constTransportsensibleEnthalpyhConstThermoperfectGasspecie> \
> heRhoThermopureMixtureconstTransportsensibleEnthalpyhConstThermoperfectGasspecie;

除了这两个 typedef,还调用了 defineTemplateTypeNameAndDebugWithName 宏函数,这个函数的定义在 src/OpenFOAM/db/typeInfo/className.H

1
2
3
4
5
6
7
8
9
#define defineTemplateTypeNameAndDebugWithName(Type, Name, DebugSwitch)       \
defineTemplateTypeNameWithName(Type, Name); \
defineTemplateDebugSwitchWithName(Type, Name, DebugSwitch)

#define defineTemplateTypeNameWithName(Type, Name) \
defineTypeNameWithName(Type, Name)

#define defineTypeNameWithName(Type, Name) \
const ::Foam::word Type::typeName(Name)

很显然,这个宏函数的作用是修改类对应的 typeName 和 debug 选项。在 OpenFOAM 中,很多类中都会调用 TypeName("typename"),这里的 TypeName 也是一个宏函数,定义在 src/OpenFOAM/db/typeInfo/className.H:

1
2
3
4
5
6
7
8
9
10
11
#define TypeName(TypeNameString)                                              \
ClassName(TypeNameString); \
virtual const word& type() const { return typeName; }

#define ClassName(TypeNameString) \
ClassNameNoDebug(TypeNameString); \
static int debug

#define ClassNameNoDebug(TypeNameString) \
static const char* typeName_() { return TypeNameString; } \
static const ::Foam::word typeName

可见, TypeName 这个宏函数,声明了一个类静态变量 typeName,定义了一个函数 type 用于返回 typeName 的值,并定义了一个静态变量 debug 用于存储 debug 选项。这里与 RTS 机制有关的是 typeName 这个变量。

绕了半圈,回到 defineTemplateTypeNameAndDebugWithName 函数。了解了 typeName 这个变量的定义,很容易就能看出来, defineTemplateTypeNameAndDebugWithName 这个函数其实就是在对类的静态变量 typeName 进行赋值。根据上文的实例提供的参数,宏函数 defineTemplateTypeNameAndDebugWithName 可以理解为:对 heRhoThermopureMixtureconstTransportsensibleEnthalpyhConstThermoperfectGasspecie 对应的类的静态变量 typeName 进行赋值,赋值结果为:
heRhoThermo<pureMixture< + constTransport<species::thermo<hConstThermo<perfectGas<specie>>,sensibleInternalEnergy>>::typeName() + >>
。这里调用了 constTransport 类的成员函数 typeName()
经过一番冗长的函数调用,得到的最终结果是:将 heRhoThermopureMixtureconstTransportsensibleEnthalpyhConstThermoperfectGasspecie 对应的类的静态变量 typeName 赋值为:heRhoThermo<pureMixture<const<hConst<perfectGas<specie>>,sensiblesensibleEnthalpy>>>

至此,经过一番宏函数的调用,得到了 addToRunTimeSelectionTable 宏函数的参数。前面 RTS 机制部分讲过,这个函数的作用就是对 hashTable 增加元素,以

1
2
3
4
5
6
addToRunTimeSelectionTable                                                    \
( \
BaseThermo, \
Cthermo##Mixture##Transport##Type##Thermo##EqnOfState##Specie, \
fvMesh \
);

为例,第一个参数,表示元素将增加到 BaseThermo 类(这里是 rhoThermo )中声明的 hashTable,第二个参数,表示将要添加的类,添加成功以后,这个类的 typeName 将是 hashTable 的 key,而返回这个类的对象的一个函数,将是 hashTable 的 value。第三个参数对应着 hashTable 对象的名字,fvMesh 对应的 hashTable 对象名为 fvMeshConstructorTable,这与在 rhoThermo 中声明的名字是对应的。

最后总结如下:
宏函数

1
2
3
4
5
6
7
8
9
10
11
makeThermo
(
rhoThermo,
heRhoThermo,
pureMixture,
constTransport,
sensibleInternalEnergy,
hConstThermo,
perfectGas,
specie
);

调用以后,向 rhoThermo 类中声明的 hashTable 中增加了一组元素,其 key 为 heRhoThermo<pureMixture<const<hConst<perfectGas<specie>>,sensibleInternalEnergy>>> ,value 对应的函数返回的是类

1
2
3
4
5
6
7
8
heRhoThermo                                                                 
<
rhoThermo,
pureMixture
<
constTransport<species::thermo<hConstThermo<perfectGas<specie>>,sensibleInternalEnergy>>
>

>

的对象。

每调用一次 makeThermo 函数,就增加了一个新组元素,也即增加了一个可选的模型。不同的参数,其实对应的是不同的模板实例。

至此,就知道了在 twoPhaseEulerFoamphaseModel 中定义的热物理类接口 thermo_ 最终指向的是 heRhoThermo 类的对象。虽然代入的模板数很复杂,但整个架构仍然是基于 RTS 机制的。

接下来,要想理解能量方程,理解温度,粘度,压力等这些热物理相关的量是怎么计算更新的,就需要仔细看一下 heRhoThermo 类的继承派生关系了。