OpenFoam 5.x 中湍流模型具体是如何实现的呢?让我们一起来探讨不可压流体湍流模型的实现这个问题。
模型的源文件位于/src/TurbulenceModels/目录下。在这个目录下,共有5个文件夹。其中目录turbulenceModels里主要给出的是各个湍流模型的类模板。比如laminar,RAS和LES模型,各个模型下还进一步的细分,比如本期我们要介绍的kEpsilon模型,就属于RAS模型中的一种。这些类模板实例化为类都需要一个参数:BasicTurbulenceModel.而目录incompressible里就为我们介绍了BasicTurbulenceModel 都可以是些什么类。接下来我们就先带着这个参数到底是什么的问题谈谈湍流模型在OF中的实现。
图一. 目录 TurbulenceModels
图二. 子目录 turbulenceModels
图三. 子目录 incompressible
在文件
/src/TurbulenceModels/incompressible/turbulentTransportModels/turbulentTransportModels.H
中,有一段宏命令:
makeTurbulenceModelTypes
(
geometricOneField,
geometricOneField,
incompressibleTurbulenceModel,
IncompressibleTurbulenceModel,
transportModel
);
其中geometricOneField, incompressibleTurbulenceModel和transportModel都是类名, 而IncompressibleTurbulenceModel是类模板. OF为了区分类和类模板,在起名的时候通常将类名的首字母小写,而将类模板的首字母大写。
执行完这段宏命令以后,类模板就实例化生成了如下几个类:
transportModelIncompressibleTurbulenceModel
laminartransportModelIncompressibleTurbulenceModel
RAStransportModelIncompressibleTurbulenceModel
LEStransportModelIncompressibleTurbulenceModel
在文件
/src/TurbulenceModels/incompressible/turbulentTransportModels/turbulentTransportModels.C
中,也有一段宏命令:
makeBaseTurbulenceModel
(
geometricOneField,
geometricOneField,
incompressibleTurbulenceModel,
IncompressibleTurbulenceModel,
transportModel
);
该宏的定义在文件makeTurbulenceModel.H中:
#define makeBaseTurbulenceModel(Alpha, Rho, baseModel, BaseModel, Transport) \
这段宏操作首先创建了类
transportModelincompressibleTurbulenceModel,
其由类模板生成
TurbulenceModel
<
geometricOneField,
geometricOneField,
incompressibleTurbulenceModel,
transportModel
>
接下来为该类(此时作为基类)建立了一个哈希表 (OF自建一个称作runTimeSelection机制, 通过"虚构造函数"的方式来实现通过基类类型的指针调用派生类的功能——[1]):
defineTemplateRunTimeSelectionTable \
( \
Transport##baseModel, \
dictionary \
);
然后将派生类
RAS##Transport##BaseModel= RAStransportModelIncompressibleTurbulenceModel
的构造函数指针加入到该哈希表中:
addToRunTimeSelectionTable \
( \
Transport##baseModel, \
RAS##Transport##BaseModel, \
dictionary \
); \
而与此同时,也为类
RAS##Transport##BaseModel
(作为基类)建立了哈希表:
defineTemplateRunTimeSelectionTable \
(RAS##Transport##BaseModel, dictionary);
对于类
laminar##Transport##BaseModel 和 LES##Transport##BaseModel
的情况如法炮制。到这里,笔者起初也存在疑问:为何类
transportModelincompressibleTurbulenceModel
与类
RAStransportModelIncompressibleTurbulenceModel
是基类和派生类的关系呢? 我们还是回到生成该类的模板:
transportModelincompressibleTurbulenceModel=
TurbulenceModel
<
geometricOneField,
geometricOneField,
incompressibleTurbulenceModel,
transportModel
>
RAStransportModelIncompressibleTurbulenceModel=RASModel<IncompressibleTurbulenceModel<transportModel>>
注意到文件
/src/TurbulenceModels/incompressible/incompressibleTurbulenceModel.H
其中:
IncompressibleTurbulenceModel<transportModel>:
public TurbulenceModel
<
geometricOneField,
geometricOneField,
incompressibleTurbulenceModel,
transportModel
>
所以
IncompressibleTurbulenceModel<transportModel>
是
transportModelincompressibleTurbulenceModel
的派生类,那么,
RAStransportModelIncompressibleTurbulenceModel
就是
transportModelincompressibleTurbulenceModel
的派生类 —— 回到刚刚谈到的类的哈希表的问题上来:
RAStransportModelIncompressibleTurbulenceModel
建立好了哈希表之后(此时视该类为基类),还需要往这个哈希表里添加派生类的构造函数指针。由如下宏命令实现:
makeRASModel(kEpsilon);
这段宏定义如下:
#define makeRASModel(Type) \
makeTemplatedTurbulenceModel \
(transportModelIncompressibleTurbulenceModel, RAS, Type)
进一步的解析宏makeTemplatedTurbulenceModel:
#define makeTemplatedTurbulenceModel(BaseModel, SType, Type) \
defineNamedTemplateTypeNameAndDebug \
(Foam::SType##Models::Type<Foam::BaseModel>, 0); \
\
namespace Foam \
{ \
namespace SType##Models \
{ \
typedef Type<BaseModel> Type##SType##BaseModel; \
\
addToRunTimeSelectionTable \
( \
SType##BaseModel, \
Type##SType##BaseModel, \
dictionary \
); \
} \
}
这里面的派生类:
kEpsilonRAStransportModelIncompressibleTurbulenceModel
是由模板定义而来:
RASModels::kEpsilon<transportModelIncompressibleTurbulenceModel>
查看文件
src/TurbulenceModels/turbulenceModels/RAS/kEpsilon/kEpsilon.H
得知:类
RASModels::kEpsilon<transportModelIncompressibleTurbulenceModel>
是类
RASModel<transportModelIncompressibleTurbulenceModel>
的派生类:
template<class BasicTurbulenceModel>
class kEpsilon
:
public eddyViscosity<RASModel<BasicTurbulenceModel>>
最后通过代码
addToRunTimeSelectionTable \
( \
SType##BaseModel, \
Type##SType##BaseModel, \
dictionary \
); \
派生类
kEpsilonRAStransportModelIncompressibleTurbulenceModel
的构造函数指针加入到了基类
RAStransportModelIncompressibleTurbulenceModel
的哈希表中。至此,派生类
RASModels::kEpsilon<IncompressibleTurbulenceModel<transportModel>>
与基类
RAStransportModelIncompressibleTurbulenceModel
=RASModel<IncompressibleTurbulenceModel<transportModel>>
的runTimeSelection机制完成建立.
现在我们终于可以回答文章一开始提出的问题了!—— 参数BasicTurbulenceModel在这里指的就是类
transportModelIncompressibleTurbulenceModel=
IncompressibleTurbulenceModel<transportModel>
以上是代码部分,那么具体到算例中,我们是如何实现湍流模型的呢?例如程序是如何一步一步的读取:
tutorials/incompressible/porousSimpleFoam/angledDuctImplicit/constant/turbulenceProperties
这里面的信息的呢?
在文件
applications/solvers/incompressible/simpleFoam/createFields.H
中,湍流模型由如下代码建立
singlePhaseTransportModel laminarTransport(U, phi);
autoPtr<incompressible::turbulenceModel> turbulence
(
incompressible::turbulenceModel::New(U, phi, laminarTransport)
);
其中的
incompressible::turbulenceModel
就是类
IncompressibleTurbulenceModel<transportModel>:
namespace incompressible
{
typedef IncompressibleTurbulenceModel<transportModel> turbulenceModel;
那么该类只是一个很粗糙的基类。正如上文提及的,用基类的指针,通过"虚构造函数"操作派生类.该类有一个数据成员propertiesName. 在文件turbulenceModel.C中,该数据被赋值
const Foam::word Foam::turbulenceModel::propertiesName("turbulenceProperties");
在该类的父类
transportModelincompressibleTurbulenceModel
中, 要从名称为
propertiesName=turbulenceProperties
的字典中寻找关键词simulationType (见TurbulenceModel.C)
const word modelType
(
IOdictionary
(
IOobject
(
IOobject::groupName(propertiesName, U.group()),
U.time().constant(),
U.db(),
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE,
false
)
).lookup("simulationType")
);
在本算例中, 找到了关键词simulationType之后, modelType根据字典信息赋值RAS.
我们之前介绍过,类
transportModelIncompressibleTurbulenceModel
的哈希表中有三个派生类:
laminartransportModelIncompressibleTurbulenceModel
RAStransportModelIncompressibleTurbulenceModel
LEStransportModelIncompressibleTurbulenceModel
在找到modelType之后,
typename dictionaryConstructorTable::iterator cstrIter =
dictionaryConstructorTablePtr_->find(modelType);
程序就开始找哈希表中对应的那个派生类, 从而通过"虚构造函数"创建一个派生类对象,并将指向该对象的指针返还给基类类型的指针。这些步骤都是在基类的静态函数New 中完成的——[2]:
cstrIter()(alpha, rho, U, alphaRhoPhi, phi, transport, propertiesName)
该派生类就是
RAStransportModelIncompressibleTurbulenceModel;
同样的把该派生类看做基类, 等他读取关键词RASModel后面的内容并赋值modelType以后,根据modelType创建一个派生类对象,并返还一个基类类型的指针,该指针指向该派生类对象.见文件
src/TurbulenceModels/turbulenceModels/RAS/RASModel/RASModel.C:
const word modelType
(
IOdictionary
(
IOobject
(
IOobject::groupName(propertiesName, U.group()),
U.time().constant(),
U.db(),
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE,
false
)
).subDict("RAS").lookup("RASModel")
);
typename dictionaryConstructorTable::iterator cstrIter =
dictionaryConstructorTablePtr_->find(modelType);
cstrIter()(alpha, rho, U, alphaRhoPhi, phi, transport, propertiesName)
参考文件:
[1]OpenFOAM guide/runTimeSelection mechanism.
https://openfoamwiki.net/index.php/OpenFOAM_guide/runTimeSelection_mechanism
[2]OpenFOAM 中的运行时选择机制
https://marinecfd.xyz/post/openfoam-runtime-selection/