To understand the how P1
model is used and implemented in coalChemistryFoam
, we use tutorial simplifiedSiwek
as an example.
P1 model’s euqtion:
In the code, it looks like this:
1 | solve |
which is defined in Foam::radiationModels::P1::calculate
in P1.C
. Here, gamma
(the diffusivity), a_
(the absorption coefficient ), G_
, e_
(the emission coefficient), pow4(T_)
, E_
(the emission contribution).
where
Before starting, the radiationProperties
file used in tutorial simplifiedSiwek
will be listed here:
1 | solverFreq 1; |
In coalChemistryFoam.C
createFields.H is included. In createFields.H, createRadiationModel.H is included. In createRadiationModel.H:
1 | autoPtr<radiationModel> radiation(radiationModel::New(thermo.T())); |
a object called radiation is constructed, which has type of a pointer to class type radiationModel. autoPtr is similar to a smart pointer in C++. This radiationModel object is called in EEqn.H radiation->correct();
Of course, in calss radiationModel, there is a member function called virtual void correct()
which is a virtual function. In its correct()
function, it calls calculate()
which in this class is a pure virtual function. It means this function will be called in its sub classes. Of course, P1::calculate()
will be called in this case. In EEqn.H, radiation->Sh(thermo, he)
is also called.
In radiationModel
folder, there is a file called radiationModelNew.C. In this file, Foam::autoPtr<Foam::radiationModel> Foam::radiationModel::New(const volScalarField& T)
is defined.
1 | Foam::autoPtr<Foam::radiationModel> Foam::radiationModel::New |
The modelType
here has a type of word and is initialised as "P1"
. This is done by IOdictionary(radIO).lookup("radiationModel") >> modelType
. Finally, the constructor of the radiationModel will be called in radiationModel.C. This is done by the last line of the Foam::autoPtr<Foam::radiationModel> Foam::radiationModel::New
. Here a smart pointer with a type radiationModel
is returned. This pointer is initialized by cstrIter()(T)
. Here, cstrIter
has a type of TConstructorTable::iterator
where TConstructorTable
is exactly the HashTable
type, defined by run time selection Macro. Therefore, cstrIter
has a type of HashTable<dictionaryConstructorPtr, word, string::hash>::iterator
. The iterator
class is defined inside the HashTable
class, it has an operator()
defined in the class body which is called here (cstrIter()(T)
). Here, the cstrIter()
actually represent the P1
model. Therefore, return autoPtr<radiationModel>(cstrIter()(T));
will call the constructor of radiationModels::P1
class, which is a subclass of Foam::radiationModel
:
1 | Foam::radiationModels::P1::P1(const volScalarField& T) |
In the constructor of radiationModels::P1
class, it will first initialize its parent class radiationModel
by calling its constructor(shown below). Then, its members (G_, qr_, a_, e_, E_
) will be initialized. The definitions of those variables are shown below:
1 | //- Incident radiation / [W/m^2] |
The constructor of the Foam::radiationModel
is shown here:
1 | Foam::radiationModel::radiationModel(const word& type, const volScalarField& T) |
The use of IOobject
class has been explained here. There is a initialise()
function called in the constructor which is also defined in radiationModel.C.
1 | void Foam::radiationModel::initialise() |
Then we go back to initialise()
. It shoud be noted here, the class radiationModel
is a subclass of IOdictionary
which is a subclass of dictionary
. Therefore when we called e.g. radiationModels::absorptionEmissionModel::New(*this, mesh_)
which is defined in absorptionEmissionModelNew.C
:
1 | Foam::autoPtr<Foam::radiationModels::absorptionEmissionModel> |
the *this
which is a radiationModel
object is substituted. absorptionEmission_, scatter_, scatter_
are defined in radiationModel.H
:
1 | autoPtr<radiationModels::absorptionEmissionModel> |
In the initialise()
function, the three autoPtr
smart pointers will call each respected constructors by calling the reset function. The first one:
1 | absorptionEmission_.reset |
will call the absorptionEmissionModel::New
, which is quite similar to Foam::radiationModel::New
:
1 | Foam::autoPtr<Foam::radiationModels::absorptionEmissionModel> |
Here in the radiationProperties
file, const word modelType(dict.lookup("absorptionEmissionModel"));
will result in binary
. Therefore, return autoPtr<absorptionEmissionModel>(cstrIter()(dict, mesh));
will call the constructor of class binary
in binary.C
as binary
is a subclass of absorptionEmissionModel
:
1 | Foam::radiationModels::absorptionEmissionModels::binary::binary |
Here the absorptionEmissionModel::New
will be called again. The use of subDict
and optionalSubDict
has been explained here. The typeName
here is binary
, which means typeName + "Coeffs"
is binaryCoeffs
. Therefore, coeffsDict_
will be:
1 | { |
So, coeffsDict_.subDict("model1")
in this case will be a dictionary of:
1 | { |
Vice versa the coeffsDict_.subDict("model2")
. Let’s go forward. The binary
constructor will also initialize its members model1_
and model2_
, which are defined in binary.H
:
1 | //- First absorption model |
These 2 members have a type of pointer to absorptionEmissionModel
. When we initialize it, it will call the absorptionEmissionModel::New
function again. Now we will go through this function again to make everything clear. When we call const word modelType(dict.lookup("absorptionEmissionModel"));
in absorptionEmissionModel::New
function, it will return a constant
to modelType
. Then, return autoPtr<absorptionEmissionModel>(cstrIter()(dict, mesh));
will call the constructor of class constant
in constantAbsorptionEmission.C:
1 | Foam::radiationModels::absorptionEmissionModels::constant::constant |
Here, we will first call the constructor of absorptionEmissionModel
. This is the first time we call the constructor of the base class absorptionEmissionModel
:
1 | Foam::radiationModels::absorptionEmissionModel::absorptionEmissionModel |
This constructor will initialize two protected members declared in absorptionEmissionModel.H
:
1 | protected: |
Those protected members can be used in its subclass e.g. constant
. Then the coeffsDict_
will be initialized as coeffsDict_(dict.optionalSubDict(typeName + "Coeffs"))
, where typeName
here is constant
. Therefore coeffsDict_
is:
1 | constantCoeffs |
Then, a_, e_ E_
will be initialized with the values in absorptivity, emissivity, E
. For the initialization of model2_
, there is only a little bit difference from that of model1_
. Because the model2_
is initialized with type cloud
. It is defined in a totally different folder (src/lagrangian/intermediate/submodels/addOns/radiation/absorptionEmission)
. This class which is called cloud
is also a subclass of absorptionEmissionModel
defined in cloudAbsorptionEmission.H
. In cloudAbsorptionEmission.C
:
1 | Foam::radiationModels::absorptionEmissionModels::cloud::cloud |
Here, no further explanation is needed for how the constructor is working. Due to its similarity to the constructor of class constant
. For scatter
model, it will be similar to absorptionEmissionModel
, we won’t go through it again.
In coalChemistryFoam.C
we include EEqn.H
. In EEqn.H
, the radiation
object which was constructed will be used. It is used as radiation->Sh(thermo, he)
and radiation->correct()
. The radiation->correct()
calls void Foam::radiationModels::P1::calculate()
. One thing here we have to be pretty clear about is where the pointer absorptionEmission_
is pointing to !!!!!!! As we do the .reset
in the initialise()
function, we call the radiationModels::absorptionEmissionModel::New(*this, mesh_)
which return a pointer points to binary
class. Therefore, here the absorptionEmission_
pointing to the binary
class. So I will note here once more, the absorptionEmission_
points to the binary
class. Here, I will show the Foam::radiationModels::P1::calculate()
of P1.C
:
1 | void Foam::radiationModels::P1::calculate() |
First, the calculate
function initialize the volScalarField
members we need e.g. a_, e_, E_, a0, gamma
. Then we will solve the P1 equation in solve
. I will explain here how a_
is initialized in this calculate()
function to represent how these volScalarField
are initialized. Here, I will note it once more, the absorptionEmission_
is pointing to binary
class, which is a subclass of absorptionEmission
class. absorptionEmission_->a()
will call function a()
. But a()
only exist in absorptionEmission
class. Therefore we will call this a()
in absorptionEmission
class first. In absorptionEmissionModel.H
:
1 | //- Absorption coefficient (net) |
1 | Foam::tmp<Foam::volScalarField> |
In aDisp(bandI) + aCont(bandI)
, function aDisp
and aCont
in class binary
will be called. function aDisp
and aCont
are virtual functions firstly defined in absorptionEmissionModel
class. As we mentioned, binary
is a subclass of absorptionEmissionModel
and it also has the aDisp
and aCont
functions. As absorptionEmission_
is pointing to class binary
, the aDisp
and aCont
function in binary
class will be called. I will show aCont
function as an example here to see how the function is called. In binary.H
:
1 | //- Absorption coefficient for continuous phase |
In binary.C
:
1 | Foam::tmp<Foam::volScalarField> |
Members model1_
and model2_
are defined here. Both are smart pointers. model1_
points to class constant
while model2_
points to class radiationModels::absorptionEmissionModels::cloud
. In constantAbsorptionEmission.C
:
1 | Foam::tmp<Foam::volScalarField> |
It means aCont
will be initialized with the a_
we input in the radiationProperties
file. a_
is initialized here (in the constant
constructor). But in class Foam::radiationModels::absorptionEmissionModels::cloud
, there is no function called aCont
. It is reasonable, because in particle field, there shouldn’t be a aCont
function. As virtual function’s polymorphism in C++, model2_->aCont
will call the aCont()
function in its base class absorptionEmissionModel
:
1 | Foam::tmp<Foam::volScalarField> |
As you see, it will be initialized with zeros in this volScalarField
.
After all these initialization, we will solve the P1 model’s equation in solve()
and return a source term to the energy equation in EEqn.H
:
1 | { |
The Sh()
is defined in radiationModel
, which is not existing in P1
class:
1 | Foam::tmp<Foam::fvScalarMatrix> Foam::radiationModel::Sh |
The calculation of Ru
and Rp
are defined in P1
as they are virtual functions both in radiationModel
and P1
class:
1 | Foam::tmp<Foam::DimensionedField<Foam::scalar, Foam::volMesh>> |
and
1 | Foam::tmp<Foam::volScalarField> Foam::radiationModels::P1::Rp() const |
Here we will explain the model’s equations e.g. how the radiation term acts on the energy transport equation. The energy source term returned to the heat transport equation here is the Sh()
. It is calculated as(in the code):
where,
results in,
In ANSYS FLUENT, the form of the equation is actually the same:
It should be mentioned here,