Devolatilization Model in OpenFOAM
We are going to talk about how the devolatilization model used in OpenFOAM. It is defined in the constant/coalCloudProperties
. E.g. in the tutorial simplifiedSiwek
, which has used the coalChemistryFoam
.
First, we find that in coalChemistryFoam.C
, it includes the coalCloud.H
. In coalCloud.H
, it defines:
1 | typedef ReactingMultiphaseCloud |
which is a highly inherited template (see template inheritance). The inner most class inside the angle brackets is called coalParcel
which is defined in coalParcel.H
:
1 | typedef ReactingMultiphaseParcel |
, which is also a template inheritance class. The inner most part is called particle
which is a class defined in particle.H
.
Let’s go back to the coalChemistyFoam
. In coalChemistyFoam
, it includes a header called createFields.H
, which includes createClouds.H
:
1 | Info<< "\nConstructing coal cloud" << endl; |
In the header above, the coalCloud
class is used by defining an object called coalParcels
. This object will call the constructor of ReactingMultiphaseCloud
(Remember here, it can only call the constructor of outer most class of the inherited template) in ReactingMultiphaseCloud.H
:
1 | ReactingMultiphaseCloud |
and in ReactingMultiphaseCloud.C
:
1 | template<class CloudType> |
In the constructor of the ReactingMultiphaseCloud
, it will initialize the next constructor in one layer inner of the angle bracket. This is why OpenFOAM use this template inheritance method. The outer class can not only inherit the inner class functions but also its type. If you check each layer of the template, you will find out its relationship to each class layer.
The Devolatilisation
model object devolatilisationModel_
is declared in ReactingMultiphaseCloud.H
as a smart pointer (autoPtr
):
1 | // References to the cloud sub-models |
One question remained here, which I didn’t figure out is why the Devolatilization
class is forward declared:
1 | template<class CloudType> |
I would prefer to include the header file here instead of the forward declaration. In the constructor of the ReactingMultiphaseCloud
, the devolatilisationModel_
is initialized as a nullptr. In the constructor body, the function setModels()
will be called.
1 | template<class CloudType> |
In this function, the devolatilizationModel_
, which is a smart pointer will be reset
, (the reset
is a function used in the shared pointer class, therefore it can be called using .) with a new pointer. The DevolatilisationModel<ReactingMultiphaseCloud<CloudType>>::New
is defined in DevolatilisationModelNew.C
:
1 | template<class CloudType> |
This kind of New
function is very common in OpenFOAM, we have discussed about this in previous posts e.g. P1 Model. In below, we will study how the two parameters are implemented in this New
fuction.
The first input parameter in the New
function is the this->subModelProperties(),
. The subModelProperties()
is defined in KinematicCloudI.H
:
1 | template<class CloudType> |
The reason why the subModelProperties()
can be called is because the KinematicCloud
is a also inside the template inheritance bracket. Don’t forget that the CloudType
here is the ReactingCloud
, this can be found in the definition of the inherited template defined above. The subModelProperties_
is defined in KinematicCloud.H
:
1 | //- Sub-models dictionary |
It should be noted here the IOdictionary
is a subclass of the dictionary
class. In the constructor of the IOdictionary
, the IOobjective
can be an input parameter:
1 | //- Construct given an IOobject |
In the constructor of KinematicCloud
, the subModelProperties_
is initialized as:
1 | subModelProperties_ |
Then we have to see how particleProperties_
is initialized:
1 | particleProperties_ |
The particleProperties_
is defined in KinematicCloud.H
:
1 | //- Dictionary of particle properties |
The cloudName
here is coalCloud1
as we initialized the coalParcels
. Therefore, this particleProperties
will find the file coalCloud1Properties
in the constant
folder. The subOrEmptyDict
is defined in dictionary.H
:
1 | //- Find and return a sub-dictionary as a copy, or |
This subOrEmptyDict
function will return the sub-directory, which is the subModels
in this case, of the file coalCloud1Properties
in the constant
folder.
Then we can go back to DevolatilisationModel<CloudType>::New
, which has 2 input parameters with types of dictionary
and CloudType
. It will first lookup the devolatilization model in the subModels
:
1 | const word modelType(dict.lookup("devolatilisationModel")); |
Then find this model in the prepared hash table (use the runtime selection mechanism):
1 | typename dictionaryConstructorTable::iterator cstrIter = |
If the model is successfully found in the prepared hash table, this new function will return a pointer and the corresponding constructor of the devolatilization model will be called:
1 | return autoPtr<DevolatilisationModel<CloudType>>(cstrIter()(dict, owner)); |
Here in the tutorial, the devolatilization
model used is called constantRateDevolatilisation
. Therefore the construcXDMDgxlf001!!tor of constantRateDevolatilisation
, which is defined in ConstantRateDevolatilisation.C
will be called:
1 | template<class CloudType> |
Because the ConstantRateDevolatilisation
class inherits the DevolatilisationModel
class whose default constructor does not exist, this constructor needs to be initialized first. The constructor of the DevolatilisationModel
is:
1 | template<class CloudType> |
Because DevolatilisationModel
inherits from the CloudSubModelBase
, the constructor of the CloudSubModelBase
needs to be initialized. Here the typeName
is the devolatilisationModel
as defined in DevolatilisationModel.H
. The constructor of the CloudSubModelBase
is:
1 | //- Construct from owner cloud without name |
and
1 | template<class CloudType> |
The subModelBase
constructor:
1 | //- Construct from components without name |
and
1 | Foam::subModelBase::subModelBase |
Till here, all the constructor initialization is finished. All those initialization is due to the inheritance of each class to its parent class and in their parent class, there is not default constructor. If the default constructor of the parent class is not existed, it is necessary to initialize the parent class in the constructor. Then we will go to these four initializations:
1 | volatileData_(this->coeffDict().lookup("volatileData")), |
They are defined in ConstantRateDevolatilisation.H
:
1 | // Model constants |
1 |
|
The coeffDict_
defined in subModelBase.H
has a type of dictionary
. It is initialized in the constructor of subModelBase
. The dict
here is the subModels
dictionary in the constant folder coalCloud1Properties
. The modelType
(type
) here is the constantRateDevolatilisation
. The modelType + dictExt
is constantRateDevolatilisationCoeffs
. In the simplifiedSiwek
tutorial, the dict.subDict(modelType + dictExt)
will be:
1 | { |
The this->coeffDict().lookup("volatileData")
will be:
1 | (CH4 12) |
, which are three tuples. As volatileData_
is a list, its size
here will be 3. YVolatile0_
and volatileToGasMap_
are initialized by size. This can be found here. The initialization of residualCoeff_
is similar to that of volatileData_
, it will not be discussed here again. Then we dig into how they are used:
1 | const label idGas = owner.composition().idGas(); |
The owner
here is the *this
of the ReactingMultiphaseCloud
class. The composition()
function is defined in ReactingCloud.H
:
1 | //- Return const access to reacting composition model |
and in ReactingCloudI.H
:
1 | template<class CloudType> |
The compositionModel
is defined in ReactingCloud.H
:
1 | //- Reacting composition model |
which is initialized in the constructor of ReactingCloud
as compositionModel_(nullptr),
and then in the body of this constructor, it is reset by using function setModels()
. This is quite similar to the setModels()
we used in the constructor body of ReactingMultiphaseCloud
, therefore it will not be read through again here. One interesting thing should be noted here is a conversion operator used in autoPtr
. As the return type of the function composition()
is const Foam::CompositionModel<Foam::ReactingCloud<CloudType>>&
, which is not a pointer. We dig into the class autoPtr
and find out that it use the conversion operator:
1 | template<class T> |
When the conversion operator is used, the conversion type should be mentioned after the “operator” sign as something like:
1 | operator Type(){} |
The conversion operator is used to transform the type of the class to some other types. Read this more in C++ books.
The compositionModel
used in the simplifiedSiwek
is called singleMixtureFraction
. During the reset of the compositionModel_
object, in the related New
function, a pointer to the class singleMixtureFraction
will be returned. The constructor of singleMixtureFraction
is show as:
1 | template<class CloudType> |
The function constuctIds()
is defined as:
1 | template<class CloudType> |
In the constructor of CompositionModel
, the phaseProps_
is initialized as:
1 | phaseProps_ |
The phaseProps_
is defined as a phasePropertiesList
:
1 | //- List of phase properties |
In the constructor of phasePropertiesList
:
1 | Foam::phasePropertiesList::phasePropertiesList |
The props_
is defined as a List
object:
1 | //- List of phase properties |
This List
will be initialized by the Istream
object is
.
1 | void Foam::phaseProperties::reorder |
1 | void Foam::phaseProperties::setCarrierIds |
1 |
|
1 |