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 |