|
[Sponsors] |
March 7, 2016, 09:41 |
multicomponentMixture
|
#1 |
New Member
Simone Colucci
Join Date: Mar 2016
Location: Pisa (Italy)
Posts: 23
Rep Power: 10 |
Hi,
I'm looking into thermophysicalModels to understand how mixture properties are calculated in multiphase solvers (in particular in reactingTwoPhaseEulerFoam). I suppose that the mixture thermophysical properties are calculated in multicomponentMixture.C: -------------------------------------------------------------------------------------------------------------------- template<class ThermoType> const ThermoType& Foam::multiComponentMixture<ThermoType>::cellMixtu re ( const label celli ) const { mixture_ = Y_[0][celli]/speciesData_[0].W()*speciesData_[0]; for (label n=1; n<Y_.size(); n++) { mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; } return mixture_; } -------------------------------------------------------------------------------------------------------------------- There are few things that I do not understand: 1) if you print speciesData (as well as mixture) you find an array made by 7 elements, the 1st is the component's (or phase for mixture) name, 2nd and 3rd, respectively, the number of moles and the molecular weight (nMoles and MolWeight in thermophysicalProperties.*), what about the other ones? 2) I do not understand why the mass (or volume?) fraction of each component (Y_[n]) is divided by the molecular weight (mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]). How can it be dimensionally consistent? Thanks in advance Simone |
|
March 8, 2016, 12:36 |
|
#2 |
Senior Member
David Gaden
Join Date: Apr 2009
Location: Winnipeg, Canada
Posts: 437
Rep Power: 22 |
Hi Simone,
OpenFOAM is designed to be easy to develop for on the higher levels, e.g. the application, or top level libraries. But when you delve deep into it, you will find areas where things get really complex to try to make the top level more intuitive. speciesData_ is a list of "ThermoType"s. mixture_ and mixuterVol_ are also ThermoTypes. The nature of a ThermoType varies depending on the thermo package selected at run time (there are hundreds). So it will not be consistent, what data it holds. It is implemented as a curiously recurring template pattern, which is designed to be fast and efficient... but it makes it difficult to tell what anything does. A ThermoType contains data about a specie of fluid (or solid also), and also contains functions that calculate different values, given different inputs, such as pressure and density (equation of state), viscosity, and so on. ThermoTypes are only relevant for a single cell. That means, you will find somewhere on a higher level, a forAll(cells) when calculating thermodynamic properties (see hePsiThermo, for instance). The speciesData_ is just a holder for a bunch of ThermoTypes. Depending on the mixture that inherits this (e.g. homogeneousMixture, reactingMixture), these species could be different things. species_[0] could be the raw fuel, species_[1] could be the air. They do not necessarily mean that this mixture is made up of these species. In reactingMixture, they are the constituent components of the mixture. mixture_ and mixtureVol_ are temporary memory space holding the current mixture properties. multiComponentMixture (or the derived type, eg homogeneousMixture) will fill these, and then the top level thermo package (eg hePsiThermo) will use the total mixture to produce relevant fluid fields. mixture_ mixes by molar fraction, and mixtureVol_ mixes by volume fraction, I believe. Now to your question, you are looking at the mixture code. mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; These are all overloaded operators, designed to make it look like we're simply mixing them together. Working on the ThermoType we have +=, and *. The / only operates on scalars. The net effect is: ThermoType += scalar * ThermoType You have to look through the various components in the specie directory to see how these operator overloads work. But the multiplication only works on the moles, (specie:perator*) I believe. And the += applies a before-and-after molar fraction calculation (e.g. rhoConst:perator+=). Best of luck!
__________________
~~~ Follow me on twitter @DavidGaden |
|
March 1, 2017, 16:32 |
|
#3 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Dear OF users,
I have exactly the same question as Simone had. David explained it allready, and gave a lot of very useful information, especially about the basic concept of how ThermoTypes work. However, due to my poor C++ and OF knowledge I still have problems with units multiComponentMixture.C. And again, this is about this code part: Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n] cellMixture: multi-component mixture As far as I know Y is the mass fraction ([kg/kg]) and is dimensionless. speciesData_[n].W() gives me the molecular weight of the species[n] in a cell. The units of the molecular weight W are [kg/kmol]. If I need a mixture for say energy E, which is given in [J/kg], I get the following units for my mixture: cellMixture += [dimless] / [kg/kmol] * [J/kg]. I get [(kmol J)/(kg^2)]. So, obviously, I understand something wrong here. If I get my energy/enthalpy from the janafThermo, the units of cp are in [J/(kmol K)] and for e/h in [J/kmol]. This is because RR(), defined in specie.C, is in [J/(kmol K)]. In specieThermo e is converted to E by e/W (W beeng molecular weight here), so in units [J/kmol]/[kg/kmol] = [J/kg]. So, my mixture must have [J/kg] as well. David says, that Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n] ThermoType += scalar * ThermoType But my scalar has wrong units, namely the units of Y/W. In the thread, in the link above, Maria said that the key-point of understanding this problem lies in the definition of the operator "+=" in janafThermoI.H. As far as I understand David said the similar thing. My OF knowledge is not jet sufficient to understand the code part where this "+=" operator is defined: Code:
template<class equationOfState> inline void Foam::janafThermo<equationOfState>::operator+= ( const janafThermo<equationOfState>& jt ) { scalar molr1 = this->nMoles(); equationOfState::operator+=(jt); molr1 /= this->nMoles(); scalar molr2 = jt.nMoles()/this->nMoles(); Tlow_ = max(Tlow_, jt.Tlow_); Thigh_ = min(Thigh_, jt.Thigh_); Tcommon_ = molr1*Tcommon_ + molr2*jt.Tcommon_; for ( register label coefLabel=0; coefLabel<janafThermo<equationOfState>::nCoeffs_; coefLabel++ ) { highCpCoeffs_[coefLabel] = molr1*highCpCoeffs_[coefLabel] + molr2*jt.highCpCoeffs_[coefLabel]; lowCpCoeffs_[coefLabel] = molr1*lowCpCoeffs_[coefLabel] + molr2*jt.lowCpCoeffs_[coefLabel]; } } Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n] Alex |
|
March 2, 2017, 01:39 |
|
#4 |
Member
Zhiheng Wang
Join Date: Mar 2016
Posts: 72
Rep Power: 10 |
Hi,
I want to calculate the species diffusivity D (fick law) on basis of binary diffusivity I am knowing the formulation , My problem is how to extract the data. As in creatFields.H spicies defined as basicMultiComponentMixture& composition = thermo.composition(); PtrList<volScalarField>& Y = composition.Y(); every property kappa, mu, alpha,or rho for example is called as composition.rho(i,1e5,T) How can I implement D (molecular diffusion in same form). Or if any one suggest how to create PtrList<volScalrField>& D = some variable which can calculate this. |
|
March 2, 2017, 08:52 |
|
#5 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Hello Zhiheng,
I think your question is wrong in this thread. I've seen you opend allready a thread for your question yesterday: Calcolation of spicies diffusivity So, why do you post it here again. |
|
March 7, 2017, 05:33 |
|
#6 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Dear OF users,
after spending some time with delving into OF operator definitions and asking for some help of an advanced OF user, I would like to post, what I’ve found out concerning my question above. Now (I hope) I understand much better the answer of David Gaden in post #2. BTW, I use foam-extend and in my concrete case I use the thermoType, which includes multiComponentMixture, janafThermo and perfectGas classes. But I think It should be very similar for OpenFOAM. Let’s say I would like to have an enthalpy for a species mixture. In foam-extend in e.g. in hPsiMixtureThermo.C that would be: In multiComponentMixture we can find the definition of the cellMixture: Code:
template<class ThermoType> const ThermoType& Foam::multiComponentMixture<ThermoType>::cellMixture ( const label celli ) const { mixture_ = Y_[0][celli]/speciesData_[0].W()*speciesData_[0]; for (label n=1; n<Y_.size(); n++) { mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; } return mixture_; } Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; Code:
template<class equationOfState> inline void Foam::janafThermo<equationOfState>::operator+= ( const janafThermo<equationOfState>& jt ) { scalar molr1 = this->nMoles(); equationOfState::operator+=(jt); molr1 /= this->nMoles(); scalar molr2 = jt.nMoles()/this->nMoles(); Tlow_ = max(Tlow_, jt.Tlow_); Thigh_ = min(Thigh_, jt.Thigh_); Tcommon_ = molr1*Tcommon_ + molr2*jt.Tcommon_; for ( register label coefLabel=0; coefLabel<janafThermo<equationOfState>::nCoeffs_; coefLabel++ ) { highCpCoeffs_[coefLabel] = molr1*highCpCoeffs_[coefLabel] + molr2*jt.highCpCoeffs_[coefLabel]; lowCpCoeffs_[coefLabel] = molr1*lowCpCoeffs_[coefLabel] + molr2*jt.lowCpCoeffs_[coefLabel]; } } Code:
const janafThermo<equationOfState>& jt Code:
inline void specie::operator+=(const specie& st) { scalar sumNmoles_ = max(nMoles_ + st.nMoles_, SMALL); molWeight_ = nMoles_/sumNmoles_*molWeight_ + st.nMoles_/sumNmoles_*st.molWeight_; nMoles_ = sumNmoles_ } - W (molecular weight), - Tlow_, - Thigh_, - Tcommon_, - highCpCoeffs_ - lowCpCoeffs_ This is all I need, not more. And I need to do it on the mole fraction basis, meaning, the mole fraction X_i = nMoles_i/sumNmoles. So, the calculation of the h_mix happens via coefficients! And W_mix (for calculating H_mix) is calculated on the mole fraction basis in the += operator of specie class (molWeight_ = nMoles/sumNmoles_*….). David Gaden said, that the net effect is: Code:
thermoType += scalar*thermoType And because the friend operator * of specie only works on nMoles_, I think this “scalar” does not participate in the enthalpy calculation for a mixture! Maybe it is used in some other thermoTypes, but not in mine (janaf…, idealGas… and so on). I am really not sure about it! So please, if someone of the advanced users could confirm my vague “theory” – that would be very kind. Thanks in advance, Alex |
|
March 7, 2017, 05:39 |
|
#7 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Small correction to the post above:
… In foam-extend in e.g. in hPsiMixtureThermo.C that would be: Code:
forAll(hCells, celli) { hCells[celli] = this->cellMixture(celli).H(TCells[celli]); } |
|
April 27, 2017, 13:22 |
|
#8 |
New Member
Join Date: Dec 2013
Posts: 11
Rep Power: 12 |
Dear Alex,
I am looking into the same issue right now and also have some troubles in understanding how the mixtures are calculated exactly. I think you are right with your statement that the calculation is done with coefficients that are averaged based on the components' mole fractions. As far as I got it, in the code this happens here: forAll (i) { cellMixture += Y[i] / species[i].W() * speciesData[i] } As I understand, speciesData[i] and cellMixture may for example be the coefficients of the JANAF polynomials: speciesData[i] for component i and cellMixture for the mixture. However, I feel like the molar mass of the mixture is missing in the equation. As it is now, the value of the coefficient related to the mixture should be scaled by the molar mass of the mixture or do I forget something? Moreover, the dimensions of "speciesData" and "cellMixture" should not be the same. Is this corrected at a later stage within the mixture property calculation? Thanks already! |
|
May 1, 2017, 19:27 |
|
#9 |
Senior Member
Join Date: Jan 2013
Posts: 372
Rep Power: 14 |
Hello,
It seems that I am using the same solver as Simone's (reactingTwoPhaseEulerFoam) and thinking about the same questions. My case is gas-particle flows. gas phase is the continuous phase, while particle phase is dispersed phase. For the particle phase, it contains three differerent components of species, so I need to use multiComponentMixture model. My specification in the thermophysicalProperties.particles is as follows: Code:
thermoType { type heRhoThermo; mixture multiComponentMixture; transport const; thermo hRefConst; equationOfState rhoConst; specie specie; energy sensibleInternalEnergy; } species ( char volatile ash ); inertSpecie ash; Thank you so much |
|
May 3, 2017, 02:08 |
|
#10 |
Member
Zhiheng Wang
Join Date: Mar 2016
Posts: 72
Rep Power: 10 |
Hi,
As you say it seems more Lagrangian-Eulerian flow. why dont you try reactingParcelFilmFoam or reactingParcelFoam with reactingCloud option active , As per my knowledge for char burning or oxidation you can also use FireFoam check in $WM_PROJECT_DIR\solvers\combustion and $WM_PROJECT_DIR\solvers\Lagrangian\ |
|
May 3, 2017, 11:36 |
|
#11 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Hello Daniel,
here is what I've found out: 1) Everything which is defined in the specie directory is dimensionless (scalar) and defined per cell. 2) The averaged thermodynamic values (Cp, h, s…) are definitely calculated on the mole weighted basis (if you calculate a gas mixture). Actually, David Gaden’s post heats the spot. 3) In each class you have the operators defined, which are used by the members of this class. That means you can define whatever you want under e.g. plus-operator. In the janaf class under += operator the whole law of mixtures on the mole weighted basis is defined and not just += operation. 4) Moreover, the += operator in janaf includes the += operator of the equationOfState-class e.g.: Code:
equationOfState::operator+=(jt); Code:
specie::operator+=(pg); I’m far away of beeng a professional OpenFOAM user and much less a programmer, but here is how I see it: Let’s stay with this code snippet: Code:
forAll (i) { cellMixture += Y[i] / species[i].W() * speciesData[i] } Code:
s[i]*st.nMoles[i] = nMoles[i] Basically I get a new nMoles[i]. The operator * in janafThermo gives me as output: Code:
Tlow[i], Thigh[i], Tcommon[i], lowCpCoeffs[i], highCpCoeffs[i] Then, this whole information goes into e.g. the H-Function(enthalpy) in e.g. Janaf, like h[mix] = RR()*(NASA-polynomial with mole averaged coefficients). Correct me if I’m wrong. But one thing still bothers me. The scalar is as I said s = Y[i] / species[i].W(). That means during mixing I already divide by W to get my enthalpy in terms of mass. So actually, if I calculate H[mix] I don’t need the operation H = h/W anymore. But this operation still happens in specieThermo-class (I use foam-extend). Why? I hope I could help. Regards, Alex |
|
June 9, 2017, 09:43 |
|
#12 |
New Member
Join Date: Dec 2013
Posts: 11
Rep Power: 12 |
Hello Alex,
thank you very much for your reply and excuse my really late answer. I have been out of office for some time and did not find the time to reply earlier... :-/ Your explanations really clarified some things for me. However, I am still not sure on my initial question. Let's start again at the code snippet: Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; Y[i] is the massfraction and speciesData_[i].W() the molar mass of component i. From general mixing theory, we know that the ratio of both equals molar fraction phi divided by molar mass of the mixture, i.e.: Y[i] / M[i] = phi[i] / M_mix . Thus, we have a molar-based averaging on the right-hand side of the code snippet. However, this would mean that the variable "mixture" corresponds to a specific value whereas the variable "speciesData" is molar-based. Is this correct? Looking for example at the specific heat capacity cp_mix of a binary mixture (components '1' and '2') this would yield: cp_mix = 1 / M_mix * [phi_1 * cp_1 + phi_2 * cp_2] , with the dimensions cp_mix [J/(kg K)], M_mix [kg/mol], phi [-] and cp_1/2 [J/(mol K)]. Moreover, as far as I got it (and as you state), one has to be careful with the operator "+=" as it is defined differently for the various properties in the corresponding classes. As an example, I looked into the files for the Sutherland coefficients As and Ts. There we have a "mixing rule" which is given by Code:
template<class Thermo> inline void Foam::sutherlandTransport<Thermo>::operator+= ( const sutherlandTransport<Thermo>& st ) { scalar molr1 = this->nMoles(); Thermo::operator+=(st); molr1 /= this->nMoles(); scalar molr2 = st.nMoles()/this->nMoles(); As_ = molr1*As_ + molr2*st.As_; Ts_ = molr1*Ts_ + molr2*st.Ts_; } Are you sure that nMoles describes the actual number of moles in a cell, i.e. "nMoles_species[i] / sumNmoles" which would be equal to the molar fraction phi? As far as I understood it up to now, the usage of nMoles is limited to combustion models and for simple mixing processes without reactions we have nMoles=1 for all species. Is this not the case? With respect to the operator '*' I find the following code in specieI.h (OF 2.3.x): Code:
inline specie operator*(const scalar s, const specie& st) { return specie ( s*st.nMoles_, st.molWeight_ ); } Where did you find the definition of the scalar s which you gave as s[i]]=Y[i]/W[i] ? You see I am a bit lost in understanding the code. For the simulations I am doing, the mixture property calculation is working the way it should, so I am fine from that end. Still, I would really like to understand better what is done in the code... Thank you one more time for taking the time to write your previous post. I hope you can help me out again! Regards, Daniel |
|
June 26, 2017, 06:29 |
|
#13 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Hello Daniel,
Sorry for the delay, I was out of office and have currently a lot of things to do. In the specie directory all things (thermodynamic properties …) are defined as scalars (thus dimensionless). You can see it if you open e.g. specieThermo.H. The dimensioning happens later, when fields are created. By the way, I use foam-extend, so it could be that some class names differ a bit from OpenFOAM. As far as I know the /-operator works only on scalars, thus Y[i]/W[i] is a scalar and I replaced it in my explanation with s. Thus: Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; can be seen as: Code:
mixture_ += scalar*speciesData_[n]; In the second post of this thread David says, that you can see it as: Code:
ThermoType += scalar * ThermoType We have two operators in the expression: Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; In OpenFoam the thermoType consists of several classes. An example taken from: /OpenFOAM/OpenFOAM-2.3.0/tutorials/combustion/reactingFoam/ras/counterFlowFlame2D/constant/thermophysicalProperties shows this: thermoType { type hePsiThermo; mixture reactingMixture; transport sutherland; thermo janaf; energy sensibleEnthalpy; equationOfState perfectGas; specie specie; } That means, that in the classes hePsiThermo, reactingMixture, sutherland, janaf, sensibleEnthalpy, perfectGas and specie the operators (* and += for lhs = thermoType and rhs = scalar and thermoType) have to be defined. In other words, it is defined what must happened if those operators are called. As you remember, I told you, that I think, that the definition of those operators is spread over several classes. What I mean is that if I calculate h_mix, than the janafThermo-class is actually responsible for the * and += operator definitions, because h is a member of janafThermo-class. But when I take a look at how they are defined, I find for the e.g. * operator the following line in janafThermo: Code:
s*static_cast<const equationOfState>(jt), Code:
return perfectGas(s*static_cast<const specie&>(pg)); Now let’s take a look at specie-class. The member functions of the specie-class are: W(), nMoles() and R() (see specie.H). They can be read on different ways e.g. via chemkinLexer. The mechanism for reading is at the end of specie.C file. So, we have the information about name, nMoles and molWeight for each species. Now let’s take a look at * operator in the specieI.H: Code:
inline specie operator*(const scalar s, const specie& st) { return specie ( s*st.nMoles_, st.molWeight_ ); } That means to me that if I have nMoles of specie1, than after calling * operator first time I’ll get back nMoles_specie1*(Y[1]/W[1]) and the units would be (later) [kmol/kg]. The rest of the *operator in janafThermo loads Tlow, Thigh, Tcommon, lowCpCoeffs, highCpCoeffs of this specie. So, this is how we get the necessary information of species for making a mixture. The mixture is made by the += operator in the loop. First you get specie1, than you get a mixture of specie1 and specie2 = mixture1_2. In the next loop you load specie3 and make mixture1_2 + specie3 = mixture1_2_3 and so on… Am I right, or is there a mistake? Let’s continue: The definition of the += operator in janafThermo starts with: Code:
template<class equationOfState> inline void Foam::janafThermo<equationOfState>::operator+= ( const janafThermo<equationOfState>& jt ) { scalar molr1 = this->nMoles(); equationOfState::operator+=(jt); … … Code:
inline void specie::operator+=(const specie& st) { scalar sumNmoles_ = max(nMoles_ + st.nMoles_, SMALL); molWeight_ = nMoles_/sumNmoles_*molWeight_ + st.nMoles_/sumNmoles_*st.molWeight_; nMoles_ = sumNmoles_; } Code:
nMoles_ = sumNmoles_; Let’s continue with the operator +=: … Code:
… molr1 /= this->nMoles(); scalar molr2 = jt.nMoles()/this->nMoles(); … … Code:
… … Tlow_ = max(Tlow_, jt.Tlow_); Thigh_ = min(Thigh_, jt.Thigh_); TcommonL_ = molr1*TcommonL_ + molr2*jt.TcommonL_; TcommonH_ = molr1*TcommonH_ + molr2*jt.TcommonH_; for ( register label coefLabel=0; coefLabel<janafThermo<equationOfState>::nCoeffs_; coefLabel++ ) { highCpCoeffs_[coefLabel] = molr1*highCpCoeffs_[coefLabel] + molr2*jt.highCpCoeffs_[coefLabel]; middleCpCoeffs_[coefLabel] = molr1*middleCpCoeffs_[coefLabel] + molr2*jt.middleCpCoeffs_[coefLabel]; lowCpCoeffs_[coefLabel] = molr1*lowCpCoeffs_[coefLabel] + molr2*jt.lowCpCoeffs_[coefLabel]; } } I know, I didn’t answer all your questions, but let’s first clarify this. This is approximately how I see it. Please correct me when I’m wrong. I myself still couldn’t answer all questions (especially concerning units consistency). So, please keep me posted, so we can carry out this discussion till all questions are clarified. Regards, Alex |
|
June 29, 2017, 13:07 |
|
#14 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Hello Daniel,
just an addendum to my last post. I more and more think, that the scalar „s“ is actually nMoles_. I think, that nMoles is by default = 1, and what happens during calling * operator in specie-class is nMoles = s*nMoles, thus nMoles = s*1. The dimension of s = Y/W would be [ kmol/kg], which is the number of moles of this component in one kilogram of the mixture. What do you think? That would clarify everything for me and I wouldn’t see any unit inconsistency in the mixing procedure. Regards, Alex |
|
July 12, 2017, 12:35 |
|
#15 |
New Member
Join Date: Dec 2013
Posts: 11
Rep Power: 12 |
Hello Alex,
thanks for your reply and your explanations. I think you are right with respect to the operators, i.e. " * " or " += ". We need to look at the respective class (for example janafThermo) and then might have to go one level deeper (specie). Moreover, I agree with your last posts. I am not entirely sure though where the assignment " nMoles=s*nMoles=s*1 " is happening. Summing up and repeating what I hope to understand, the code is performing 4 steps starting from the following code: Code:
mixture_ += Y_[n][celli]/speciesData_[n].W()*speciesData_[n]; Operator * is taken from specieI.H: Code:
inline specie operator*(const scalar s, const specie& st) { return specie ( s*st.nMoles_, st.molWeight_ ); } nMoles is equal to 1 per default. This could be where the new nMoles is assigned and the code returns nMoles=Yi/Wi. Do you agree? ### Step 2: The operator += from specie class determines the sum of molar fractions of species1 and species2 for the first step in the mixing calculation. As you said, first a mixture of species1 and species2 is done, then a mixture of mixedSpecies1_2 and species_3 is done. This is continued until all species are included in the mixture. Code:
inline void specie::operator+=(const specie& st) { scalar sumNmoles = max(nMoles_ + st.nMoles_, SMALL); molWeight_ = nMoles_/sumNmoles*molWeight_ + st.nMoles_/sumNmoles*st.molWeight_; nMoles_ = sumNmoles; } As an example, we have a mixture of gas_A, gas_B and gas_C in our cell and each has the molar fraction of 1/3 and the same molar weight W. After the first step of mixing gas_A and gas_B, we would have something like sumNmoles=1/3+1/3=2/3 (here the molar weight is taken as 1 for all gases). ### Step 3: The += operator in janafThermoI.H includes: Code:
scalar molr1 = this->nMoles(); EquationOfState::operator+=(jt); molr1 /= this->nMoles(); scalar molr2 = jt.nMoles()/this->nMoles(); Note that I took the molar weight equal to 1 again to simplify the writing. In my example, molr1=molr2=1/2. ### Step 4: Then the actual property calculation is done by molar averaging of gas_A and gas_B: Code:
{ highCpCoeffs_[coefLabel] = molr1*highCpCoeffs_[coefLabel] + molr2*jt.highCpCoeffs_[coefLabel]; lowCpCoeffs_[coefLabel] = molr1*lowCpCoeffs_[coefLabel] + molr2*jt.lowCpCoeffs_[coefLabel]; } The process is done until all species have contributed to the mixture. In my example Step 1 to 4 would be performed twice. 1) Mixing of gas_A and gas_B --> mixture_AB 2) Mixing of mixture_AB and gas_C --> mixture_ABC I am really not sure about these explanations but this is how far I understood it up to now from the code and your posts. Can you confirm this? Kind regards, Daniel |
|
July 24, 2017, 10:35 |
|
#16 |
Member
Alexander Nekris
Join Date: Feb 2015
Location: France
Posts: 32
Rep Power: 11 |
Hello Daniel,
sounds about right to me. It would be nice though if someone of advanced users could confirm this. |
|
August 27, 2018, 02:01 |
|
#17 |
Member
Join Date: May 2017
Posts: 44
Rep Power: 9 |
Dear Foamers,
I added YEqn.H to compressibleMultiphaseInterFoam and relate that to basicMultiComponentMixture. However by changing Y, I am not observing any changes in viscosity and density. Thanks. |
|
September 23, 2018, 06:15 |
|
#18 |
Member
Zhiheng Wang
Join Date: Mar 2016
Posts: 72
Rep Power: 10 |
Can u send detail how did u do it or atleast some tar or zip folder of solver ???
|
|
October 11, 2018, 00:30 |
|
#19 |
Member
Join Date: May 2017
Posts: 44
Rep Power: 9 |
It is very similar to adding CEqn.H to interFoam based on Haroun et al. (2010) model.
|
|
September 1, 2021, 07:37 |
|
#20 |
New Member
Hosam Alrefaie
Join Date: Jul 2021
Posts: 24
Rep Power: 5 |
Hi openfoammaofnepo,
can you please explain how or where to add the mass or mole fractions for these three species. I am trying to model a gas mixture using multiComponentMixture & buoyantSimpleFoam, but I don't know how to specify the fractions. |
|
Tags |
mixture, mutiphase, openfoam |
|
|