OpenFOAM传输模型库由两个主要组件组成:传输模型和粘度模型。粘度模型提供了直接面向对象的访问粘度相关的计算和数据。本节使用主要类的简化UML图来介绍OpenFOAM中粘度模型的类关系,如图11.2所示。

图11.2 传输模型的类协作关系图

基类为transportModel,它继承自IOobject。transportModel类在构造函数中初始化,以读取相应OpenFOAM案例的cosntant/transportProperties字典。清单65显示了transportModel的构造函数和IOobject的初始化:

Foam::transportModel::transportModel
(
    const volVectorField& U,
    const surfaceScalarField& phi
)
:
    IOdictionary
    (
        IOobject
        (
            "transportProperties",
            U.time().constant(),
            U.db(),
            IOobject::MUST_READ_IF_MODIFIED,
            IOobject::NO_WRITE
        )
    )
    {}

传输模型的实现将单相流模型与两相流模型分离为transportModel类的两个派生类:singlePhaseTransportModel和incompressibleTwoPhaseMixture。 这种划分是由于单相流和两相流之间的天然差异。

在OpenFOAM,所有的单相求解器都使用singlePhaseTransportModel类来获取和加载运动粘度Nu。 此参数必须由用户在constant/transportProperties字典中提供。 singlePhaseTransportModel和其他传输模型委托读取和更新该字典的过程,这有助于保持代码的干净。 在单阶段求解器中,singlePhaseTransportModel实例化如下:

singlePhaseTransportModel laminarTransport(U, phi);

请注意,湍流模型的构造函数需要laminarTransport对象作为参数,因为湍流模型需要访问层流粘度,而层流粘度又由singlePhaseTransportModel描述。

transportModel和singlePhaseTransportModel都定义了一个公共成员函数nu(),该函数将粘度作为volScalarField返回。因为transportModel是一个抽象基类,它不实现这个成员函数,而singlePhaseTransportModel类必须实现这个成员函数(见图11.2)。然而,它的实现将功能委托给viscosityModel:

Foam::tmp<Foam::volScalarField> 
Foam::singlePhaseTransportModel::nu() const
{
    return viscosityModelPtr_-> nu();
}

viscosityModelPtr_是singlePhaseTransportModel的私有成员,并且被定义为viscosityModel的autoPtr。此类属性在头文件中定义为:

autoPtr<viscosityModel>  viscosityModelPtr_;

现在,唯一重要的是viscosityModel以某种方式确定运动粘度。下面讨论其实际实现以及如何选择viscosityModel。

两相传输模型由不可压缩的TwoPhaseMixture类建模,由OpenFOAM中不涉及相变的interFoam类型解算器使用。与大多数单相解算器类似,interFoam类型解算器以下列方式实例化该类的对象:

incompressibleTwoPhaseMixture twoPhaseProperties(U, phi);

不可压缩两相混合物模型的课堂协作如图11.3所示。该实例化类似于已经用于singlePhaseTransportModel的方法,但是,不可压缩的TwoPhaseMixture类存储附加数据。不是具有一个viscosityModel,而是由该类实例化并存储两个viscosityModel以说明每个流体相。

图11.3 不可压缩两相混合物模型的类协作图

incompressibleTwoPhaseMixture的类定义如清单66所示。使用autoPtr存储viscosityModel的任何派生类的对象是强制性的,因为从viscosityModel派生的不同流体类型有几个不同的类。它们中的每一个都是运行时可选择的,并且最终确定选择哪种流体,这又定义了粘度,并且因此定义了公共成员函数nu的返回值。为了区分两种流体相,引入了新的volScalarField对象alpha1_。

// 清单66    
class incompressibleTwoPhaseMixture
:
    public transportModel,
    public twoPhaseMixture
    {
        protected:
            // Protected data
            autoPtr<viscosityModel>  nuModel1_;
            autoPtr<viscosityModel>  nuModel2_;
            dimensionedScalar rho1_;
            dimensionedScalar rho2_;
            const volVectorField& U_;
            const surfaceScalarField& phi_;
            volScalarField nu_;

该字段用于VoF成员函数,是两相属性(如粘度和密度)之间的有效混合值。与singlePhaseTransportModel不同的是,在singlePhaseTransportModel中,对公共成员函数nu的调用被直接委托给所选择的viscosityModel,该公共成员函数的返回值以不同的方式构成。返回私有成员nu_的副本,其是基于两个相的viscosityModels和当前alpha1_字段计算的。此计算由私有成员calcNu执行,并在调用公共成员函数correct时触发,如清单67所示。

//清单67
void Foam::incompressibleTwoPhaseMixture::calcNu()
{
    nuModel1_-> correct();
    nuModel2_-> correct();
    const volScalarField limitedAlpha1
    (
        "limitedAlpha1",
        min(max(alpha1_, scalar(0)), scalar(1))
    );
    // Average kinematic viscosity calculated from dynamic viscosity
    nu_ = mu()/(limitedAlpha1*rho1_ + (scalar(1) - limitedAlpha1)*rho2_);
}

更新两个viscosityModel,并且将体积分数alpha_1限制在0和1之间。最后,使用动态粘度mu和密度分布计算运动粘度。后者是用混合物在各相密度和体积分数alpha_1的项中的规则计算的。使用有界的alpha1_和每个viscosityModel的运动粘度nu,类似地计算动力粘度mu。

到目前为止,重点放在transportModel及其衍生模型上,而viscosityModel只是简单提及。viscosityModels类负责建模和计算粘度,遵循OOD模式单一责任原则(SRP)。粘度模型列表及其描述见第11.1节。与transportModel的结构类似,viscosityModel类是实际粘度模型的抽象基类。

如图11.2所示,viscosityModel实现了一个公共成员函数nu,它最终返回运动粘度。此成员函数提供对任何transportModel中粘度的访问。在基类viscosityModel中,这是一个虚拟成员函数,需要由每个派生类实现:

virtual tmp<volScalarField>  nu() const = 0;

将singlePhaseTransportModel和incompressibleTwoPhaseMixture硬编码到特定解算器中,viscosityModel是用户选择的最终类型。因此,必须使用constant/transportProperties字典中的特定条目在运行时选择它们。因此,基底类别必须实作OpenFOAM RTS机制。

选择Newtonian类作为viscosityModel的示例。它描述不可压缩牛顿流体的粘度,并且仅继承自viscosityModel。其他数据存储在以下私有成员变量中:

dimensionedScalar nu0_;
volScalarField nu_;

nu成员函数必须由该类实现,因为它的基类定义是虚拟的。这个实作是简短的,并传回nu_。构造函数如清单68所示。

Foam::viscosityModels::Newtonian::Newtonian
(
    const word& name,
    const dictionary& viscosityProperties,
    const volVectorField& U,
    const surfaceScalarField& phi
)
:
    viscosityModel(name, viscosityProperties, U, phi),
    nu0_("nu", dimViscosity, viscosityProperties_),
    nu_
    (
        IOobject
        (
            name,
            U_.time().timeName(),
            U_.db(),
            IOobject::NO_READ,
            IOobject::NO_WRITE
            ),
            U_.mesh(),
            nu0_
        )
    {}

当然,基类的构造函数在初始化列表的第一个位置被调用,然后是nu0_和nu的初始化。

注意:即使transportModel是所有运输模型的抽象基类,专业的求解器也会选择层次结构中的其他模型类。一个例子是不可压缩的TwoPhaseMixture,它是一个传输模型,但也是一个twoPhaseMixture,特别是要由两相求解器使用。