源代码:coalChemistryFoam.C
为了提高看代码的效率,先把英文的粘过来,之后抽空补充中文翻译
主程序
先来看coalChemistryFoam.C
首先给出了程序的简要说明
程序名:coalChemistryFoam
类别:grpLagrangianSolvers
简介:Transient solver for compressible, turbulent flow, with coal and limestone particle clouds, an energy source, and combustion.
头文件
#include "fvCFD.H"
#include "turbulenceModel.H"
#include "basicThermoCloud.H"
#include "coalCloud.H"
#include "psiCombustionModel.H"
#include "fvIOoptionList.H"
#include "radiationModel.H"
#include "SLGThermo.H"
#include "pimpleControl.H"
以上是v2.3.1,在v6和V1806中增加了如下头文件:
#include "pressureControl.H"
#include "localEulerDdtScheme.H"
#include "fvcSmooth.H"
main function
- addCheckCaseOptions.H includes predefined arguments to check case set-up.
- setRootCase.H will create an object from argList class to register and output informations according the case settings. argList is a very basic class defined in argList.H, and its function is to extract command arguments and options from the inputs.
- createTime.H will create the object runtime from the Time class. This class is used to control time loops during the calculation.
- createMesh.H will create the object mesh from the fvMesh class. fvMesh is a multiple inheritence class, and it inherits from polyMesh class and other classes related to finite volume method.
- createControl.H In createControl.H three different solver controllers are defined (SIMPLE, PISO and PIMPLE). In coalChemistryFoam, PIMPLE algorithm is used. These algorithm control class will supply convergence information checks for the calculation loop. The implementation of pressure correction algorithm is usually in the pEqn.H file.
- creatTimeControls.H Three variables adjustTimeStep, maxCo and maxDeltaT will be created and initialized from the controlDict
- creatFields.H In this file, the variables are created as fields data object. Two pointers, i.e. combustion and turbulence, are defined by autoPrt template type. The pointer created by New function is using for the ”Run Time Selection(RTS)” method, it allows to select the submodel class used in template after compiling of the source code. We will look into more detail in this file in the following sections.
After the object and variable declaration is almost done, the turbulent model is checked by the following code:
turbulence->validate();
Before entering the time loop, set initial time step and calculate Courant number. In general, as the courant number changes from small to large, the convergence speed gradually increases, but the stability gradually decreases.
if (!LTS)
{
#include "compressibleCourantNo.H"
#include "setInitialDeltaT.H"
}
Info<< "\nStarting time loop\n" << endl;
while (runTime.run())
{
#include "readTimeControls.H"
if (LTS)
{
#include "setRDeltaT.H"
}
else
{
#include "compressibleCourantNo.H"
#include "setDeltaT.H"
}
runTime++;
Info<< "Time = " << runTime.timeName() << nl << endl;
LTS is short for local time stepping. When the reactions and radiation model is activated, LTS is usually used. It needs to be specified according to the solver itself, so we can find the file ”setDeltaT.H” in the solver’s directory.
rhoEffLagrangian = coalParcels.rhoEff() + limestoneParcels.rhoEff();
pDyn = 0.5*rho*magSqr(U);
coalParcels.evolve();
limestoneParcels.evolve();
Then, the solver calculates and updates particles’ parameter by calling the evolve() function in the parcel class, the source terms from the particle to the gas phase equations will also be calculated. rhoEffLagrangian and pDyn is not used in the calculation. They are only calculated for post-process.
coalParcels is a coalCould class, as defined in createClouds.H and envolve() is a function of Class ThermoCloud.H
Info<< "\nConstructing coal cloud" << endl;
coalCloud coalParcels
(
"coalCloud1",
rho,
U,
g,
slgThermo
);
Info<< "\nConstructing limestone cloud" << endl;
basicThermoCloud limestoneParcels
(
"limestoneCloud1",
rho,
U,
g,
slgThermo
);
Next, solve the governing eqautions for the gas phase, and correct the pressure using PIMPLE algorithm.
#include "rhoEqn.H"
// --- Pressure-velocity PIMPLE corrector loop
while (pimple.loop())
{
#include "UEqn.H"
#include "YEqn.H"
#include "EEqn.H"
// --- Pressure corrector loop
while (pimple.correct())
{
#include "pEqn.H"
}
Then, solve the turbulence based on pimple algorithm.
if (pimple.turbCorr())
{
turbulence->correct();
}
}
At last, the density of gas phase is updated according to the "equation of state", and write data before entering the next time step.
rho = thermo.rho();
runTime.write();
runTime.printExecutionTime(Info);
}
Info<< "End\n" << endl;
return 0;
}
方程求解
下面以大涡模拟中气相控制方程为例
质量守恒方程
rhoEqn.H
{
fvScalarMatrix rhoEqn
(
fvm::ddt(rho)
+ fvc::div(phi)
==
coalParcels.Srho(rho)
+ fvOptions(rho)
);
rhoEqn.solve();
fvOptions.correct(rho);
}
【OF中的方程离散】微分方程是通过离散从而转化为代数方程组进行求解。对于一个代数方程组主要有两部分:系数矩阵A,右边值b。构造成类似Ax=b的代数方程形式。方程的离散有显式和隐式,隐式离散得到方程稀疏矩阵A的,显式离散得到右边的值b。一旦通过某种离散形式得到A和b就可以求解线性方程组了。
OpenFOAM的所有的显式离散都在fvc空间中,所以以fvc开头的都为显式,比如fvc::div(phi)。隐式离散在fvm空间中,所有的隐式离散都是以fvm开头,比如fvm::ddt, fvm::div(phi,U)等。所以,以fvm开头的项声称A,以fvc开头的项生成b。
对于质量守恒方程,fvm::ddt(rho)表示ddt(rho)是待求解的未知量,fvc::div(phi)表示div(phi)是已知量,即上一时刻的值。
这里可压缩流中,phi定义为rho*U,见compressibleCreatePhi_H:
surfaceScalarField phi
(
IOobject
(
"phi",
runTime.timeName(),
mesh,
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
linearInterpolate(rho*U) & mesh.Sf()
);
- coalParcels.Srho(rho)表示质量源项,Srho是ReactingCloud.H类的函数
- fvOptions(rho)表示附加源项,OpenFOAM 2.2.0: fvOptions,在system/fvOptions中设置,可用作点火源
动量方程
UEqn.H
MRF.correctBoundaryVelocity(U);
fvVectorMatrix UEqn
(
fvm::ddt(rho, U) + fvm::div(phi, U)
+ MRF.DDt(rho, U)
+ turbulence->divDevRhoReff(U)
==
rho()*g
+ coalParcels.SU(U)
+ limestoneParcels.SU(U)
+ fvOptions(rho, U)
);
UEqn.relax();
fvOptions.constrain(UEqn);
if (pimple.momentumPredictor())
{
solve(UEqn == -fvc::grad(p));
fvOptions.correct(U);
K = 0.5*magSqr(U);
}
- MRF is for rotating reference frame, MRF.DDt will return the contribution from the rotating reference frame effects.
- turbulence是creatFields.H中定义的湍流模型:
Info<< "Creating turbulence model\n" << endl;
autoPtr<compressible::turbulenceModel> turbulence
(
compressible::turbulenceModel::New
(
rho,
U,
phi,
thermo
)
);
- divDevRhoReff(U) function is determined by the turbulence model. It will return the source term for the momentum equation. 在RANS下如果couplingFactor为0(事实上,在ReynoldsStress.C中已经将其初始化为0, 它将对Reynold应力项的显式计算进行加权,详细信息可以在定义中找到)ReynoldsStress.C,在它的定义为:
fvc::laplacian
(
this->alpha_*rho*this->nut(),
U,
"laplacian(nuEff,U)"
)
+ fvc::div(this->alpha_*rho*R_)
- fvc::div(this->alpha_*rho*this->nu()*dev2(T(fvc::grad(U))))
- fvm::laplacian(this->alpha_*rho*this->nuEff(), U)
LES下(Smagorinsky模型),它的定义为linearViscousStress.C
Foam::linearViscousStress<BasicTurbulenceModel>::divDevRhoReff
(
volVectorField& U
) const
{
return
(
- fvc::div((this->alpha_*this->rho_*this->nuEff())*dev2(T(fvc::grad(U))))
- fvm::laplacian(this->alpha_*this->rho_*this->nuEff(), U)
);
}
最后,考虑压力梯度项,利用Pimple循环:
if (pimple.momentumPredictor())
{
solve(UEqn == -fvc::grad(p));
fvOptions.correct(U);
K = 0.5*magSqr(U);
}
完成动量方程求解
组分方程
YEqn.H
In the YEqn.H file, a convectionScheme, which is named mvConvection, is defined at the beginning. It is created on free store in random-access memory (RAM). When the number of gas species is large, it enhances the computational efficiency.
tmp<fv::convectionScheme<scalar>> mvConvection
(
fv::convectionScheme<scalar>::New
(
mesh,
fields,
phi,
mesh.divScheme("div(phi,Yi_h)")
)
);
Before calculate the species transport equation, the gas phase combustion is calculated and the heat effects of the reactions is stored in the parameter Qdot().
combustion->correct();
Qdot = combustion->Qdot();
A variable Yt is initialized
volScalarField Yt(0.0*Y[0]);
and used in the calculation loop to make sure all the species mass fraction together is 1. The error is made up by the inert gas. The transport equation is defined as:
fvScalarMatrix YiEqn
(
fvm::ddt(rho, Yi)
+ mvConvection->fvmDiv(phi, Yi)
- fvm::laplacian(turbulence->muEff(), Yi)
==
coalParcels.SYi(i, Yi)
+ combustion->R(Yi)
+ fvOptions(rho, Yi)
);
能量方程
EEqn.H
fvScalarMatrix EEqn
(
fvm::ddt(rho, he) + mvConvection->fvmDiv(phi, he)
+ fvc::ddt(rho, K) + fvc::div(phi, K)
+ (
he.name() == "e"
? fvc::div
(
fvc::absolute(phi/fvc::interpolate(rho), U),
p,
"div(phiv,p)"
)
: -dpdt
)
- fvm::laplacian(turbulence->alphaEff(), he)
==
rho*(U&g)
+ Qdot
+ coalParcels.Sh(he)
+ limestoneParcels.Sh(he)
+ radiation->Sh(thermo, he)
+ fvOptions(rho, he)
);
压力修正
pimple的基本思想是:将每个时间步长内用simple稳态算法求解(也就是将每个时间步内看成稳态流动),时间步长的步进用piso算法来完成。
在有限容积离散中,时间项的离散仍采用的差分格式,这样做可以得到某个时间点的流场信息,而非某个时间步长的内的平均值。采用传统的piso算法求解变化较快的流动的时候,需要的时间步长较小(因为相邻两个时间点的流场不能差别太大,否则会发散),这样会造成求解的某种流动需要的耗时过长。 pimple算法将每个时间步长内看成一种稳态的流动(采用亚松驰来解决相邻两个时间段变化大的情况),当按照稳态的求解器求解到一定的时候,则采用标准的piso做最后一步求解。
子模型
OpenFOAM通过RTS定义子模型,OpenFOAM中的RunTime Selection(RTS)机制——TypeName
createFields.H
createFields主要是用来加载各种子模型的,它包含了若干.H文件:
#include "createRDeltaT.H" //为RTS模型创建时间步长
#include "readGravitationalAcceleration.H"//重力加速度
#include "compressibleCreatePhi.H"//可压缩流
#include "createDpdt.H"
#include "createK.H"
#include "createMRF.H"
#include "createClouds.H"//多相流模型
#include "createRadiationModel.H"//辐射模型
#include "createFvOptions.H"
参考文献
- Zhang, J.: Modifying coalChemistryFoam for dense gas-solid simulation. In Proceedings of CFD with OpenSource Software, 2018, Edited by Nilsson. H., http://dx.doi.org/10.17196/OS_CFD#YEAR_2018
- 苏军伟,OpenFOAM中的神奇方程定义方式的背后. http://blog.sina.com.cn/s/blog_5fdfa7e60100d83c.html
- 苏军伟,pimple算法简述. http://blog.sina.com.cn/s/blog_5fdfa7e60100fbb1.html
- 王伟,OpenFOAM中的RunTime Selection(RTS)机制——TypeName,http://youmengtian.coding.me/2017/03/25/runtimeselection/