FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

ReactingMultiphaseParcel.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002   =========                 |
00003   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
00004    \\    /   O peration     |
00005     \\  /    A nd           | Copyright (C) 2008-2010 OpenCFD Ltd.
00006      \\/     M anipulation  |
00007 -------------------------------------------------------------------------------
00008 License
00009     This file is part of OpenFOAM.
00010 
00011     OpenFOAM is free software: you can redistribute it and/or modify it
00012     under the terms of the GNU General Public License as published by
00013     the Free Software Foundation, either version 3 of the License, or
00014     (at your option) any later version.
00015 
00016     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
00017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00019     for more details.
00020 
00021     You should have received a copy of the GNU General Public License
00022     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
00023 
00024 \*---------------------------------------------------------------------------*/
00025 
00026 #include "ReactingMultiphaseParcel.H"
00027 #include <OpenFOAM/mathematicalConstants.H>
00028 
00029 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
00030 
00031 template<class ParcelType>
00032 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::GAS(0);
00033 
00034 template<class ParcelType>
00035 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::LIQ(1);
00036 
00037 template<class ParcelType>
00038 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::SLD(2);
00039 
00040 
00041 // * * * * * * * * * * * *  Private Member Functions * * * * * * * * * * * * //
00042 
00043 template<class ParcelType>
00044 template<class TrackData>
00045 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::cpEff
00046 (
00047     TrackData& td,
00048     const scalar p,
00049     const scalar T,
00050     const label idG,
00051     const label idL,
00052     const label idS
00053 ) const
00054 {
00055     return
00056         this->Y_[GAS]*td.cloud().composition().cp(idG, YGas_, p, T)
00057       + this->Y_[LIQ]*td.cloud().composition().cp(idL, YLiquid_, p, T)
00058       + this->Y_[SLD]*td.cloud().composition().cp(idS, YSolid_, p, T);
00059 }
00060 
00061 
00062 template<class ParcelType>
00063 template<class TrackData>
00064 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::HEff
00065 (
00066     TrackData& td,
00067     const scalar p,
00068     const scalar T,
00069     const label idG,
00070     const label idL,
00071     const label idS
00072 ) const
00073 {
00074     return
00075         this->Y_[GAS]*td.cloud().composition().H(idG, YGas_, p, T)
00076       + this->Y_[LIQ]*td.cloud().composition().H(idL, YLiquid_, p, T)
00077       + this->Y_[SLD]*td.cloud().composition().H(idS, YSolid_, p, T);
00078 }
00079 
00080 
00081 template<class ParcelType>
00082 template<class TrackData>
00083 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::LEff
00084 (
00085     TrackData& td,
00086     const scalar p,
00087     const scalar T,
00088     const label idG,
00089     const label idL,
00090     const label idS
00091 ) const
00092 {
00093     return
00094         this->Y_[GAS]*td.cloud().composition().L(idG, YGas_, p, T)
00095       + this->Y_[LIQ]*td.cloud().composition().L(idL, YLiquid_, p, T)
00096       + this->Y_[SLD]*td.cloud().composition().L(idS, YSolid_, p, T);
00097 }
00098 
00099 
00100 template<class ParcelType>
00101 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::updateMassFractions
00102 (
00103     const scalar mass0,
00104     const scalarField& dMassGas,
00105     const scalarField& dMassLiquid,
00106     const scalarField& dMassSolid
00107 )
00108 {
00109     scalarField& YMix = this->Y_;
00110 
00111     scalar massGas =
00112         this->updateMassFraction(mass0*YMix[GAS], dMassGas, YGas_);
00113     scalar massLiquid =
00114         this->updateMassFraction(mass0*YMix[LIQ], dMassLiquid, YLiquid_);
00115     scalar massSolid =
00116         this->updateMassFraction(mass0*YMix[SLD], dMassSolid, YSolid_);
00117 
00118     scalar massNew = max(massGas + massLiquid + massSolid, ROOTVSMALL);
00119 
00120     YMix[GAS] = massGas/massNew;
00121     YMix[LIQ] = massLiquid/massNew;
00122     YMix[SLD] = 1.0 - YMix[GAS] - YMix[LIQ];
00123 
00124     return massNew;
00125 }
00126 
00127 
00128 // * * * * * * * * * * *  Protected Member Functions * * * * * * * * * * * * //
00129 
00130 template<class ParcelType>
00131 template<class TrackData>
00132 void Foam::ReactingMultiphaseParcel<ParcelType>::setCellValues
00133 (
00134     TrackData& td,
00135     const scalar dt,
00136     const label cellI
00137 )
00138 {
00139     ReactingParcel<ParcelType>::setCellValues(td, dt, cellI);
00140 }
00141 
00142 
00143 template<class ParcelType>
00144 template<class TrackData>
00145 void Foam::ReactingMultiphaseParcel<ParcelType>::cellValueSourceCorrection
00146 (
00147     TrackData& td,
00148     const scalar dt,
00149     const label cellI
00150 )
00151 {
00152     scalar massCell = this->massCell(cellI);
00153 
00154     scalar addedMass = 0.0;
00155     forAll(td.cloud().rhoTrans(), i)
00156     {
00157         addedMass += td.cloud().rhoTrans(i)[cellI];
00158     }
00159 
00160     this->rhoc_ += addedMass/td.cloud().pMesh().cellVolumes()[cellI];
00161 
00162     scalar massCellNew = massCell + addedMass;
00163     this->Uc_ += td.cloud().UTrans()[cellI]/massCellNew;
00164 
00165     scalar cpEff = 0;
00166     if (addedMass > ROOTVSMALL)
00167     {
00168         forAll(td.cloud().rhoTrans(), i)
00169         {
00170             scalar Y = td.cloud().rhoTrans(i)[cellI]/addedMass;
00171             cpEff +=
00172                 Y*td.cloud().mcCarrierThermo().speciesData()[i].Cp(this->Tc_);
00173         }
00174     }
00175     const scalar cpc = td.cpInterp().psi()[cellI];
00176     this->cpc_ = (massCell*cpc + addedMass*cpEff)/massCellNew;
00177 
00178     this->Tc_ += td.cloud().hsTrans()[cellI]/(this->cpc_*massCellNew);
00179 }
00180 
00181 
00182 template<class ParcelType>
00183 template<class TrackData>
00184 void Foam::ReactingMultiphaseParcel<ParcelType>::calc
00185 (
00186     TrackData& td,
00187     const scalar dt,
00188     const label cellI
00189 )
00190 {
00191     // Define local properties at beginning of timestep
00192     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00193     const scalar np0 = this->nParticle_;
00194     const scalar d0 = this->d_;
00195     const vector& U0 = this->U_;
00196     const scalar rho0 = this->rho_;
00197     const scalar T0 = this->T_;
00198     const scalar cp0 = this->cp_;
00199     const scalar mass0 = this->mass();
00200 
00201     const scalar pc = this->pc_;
00202 
00203     const scalarField& YMix = this->Y_;
00204     const label idG = td.cloud().composition().idGas();
00205     const label idL = td.cloud().composition().idLiquid();
00206     const label idS = td.cloud().composition().idSolid();
00207 
00208 
00209     // Calc surface values
00210     // ~~~~~~~~~~~~~~~~~~~
00211     scalar Ts, rhos, mus, Pr, kappa;
00212     ThermoParcel<ParcelType>::
00213         calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Pr, kappa);
00214 
00215     // Reynolds number
00216     scalar Re = this->Re(U0, d0, rhos, mus);
00217 
00218 
00219     // Sources
00220     //~~~~~~~~
00221 
00222     // Explicit momentum source for particle
00223     vector Su = vector::zero;
00224 
00225     // Momentum transfer from the particle to the carrier phase
00226     vector dUTrans = vector::zero;
00227 
00228     // Explicit enthalpy source for particle
00229     scalar Sh = 0.0;
00230 
00231     // Sensible enthalpy transfer from the particle to the carrier phase
00232     scalar dhsTrans = 0.0;
00233 
00234 
00235     // Phase change in liquid phase
00236     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00237 
00238     // Mass transfer due to phase change
00239     scalarField dMassPC(YLiquid_.size(), 0.0);
00240 
00241     // Molar flux of species emitted from the particle (kmol/m^2/s)
00242     scalar Ne = 0.0;
00243 
00244     // Sum Ni*Cpi*Wi of emission species
00245     scalar NCpW = 0.0;
00246 
00247     // Surface concentrations of emitted species
00248     scalarField Cs(td.cloud().mcCarrierThermo().species().size(), 0.0);
00249 
00250     // Calc mass and enthalpy transfer due to phase change
00251     this->calcPhaseChange
00252     (
00253         td,
00254         dt,
00255         cellI,
00256         Re,
00257         Ts,
00258         mus/rhos,
00259         d0,
00260         T0,
00261         mass0,
00262         idL,
00263         YMix[LIQ],
00264         YLiquid_,
00265         dMassPC,
00266         Sh,
00267         Ne,
00268         NCpW,
00269         Cs
00270     );
00271 
00272 
00273     // Devolatilisation
00274     // ~~~~~~~~~~~~~~~~
00275 
00276     // Mass transfer due to devolatilisation
00277     scalarField dMassDV(YGas_.size(), 0.0);
00278 
00279     // Calc mass and enthalpy transfer due to devolatilisation
00280     calcDevolatilisation
00281     (
00282         td,
00283         dt,
00284         Ts,
00285         d0,
00286         T0,
00287         mass0,
00288         this->mass0_,
00289         idG,
00290         YMix[GAS],
00291         YGas_,
00292         canCombust_,
00293         dMassDV,
00294         Sh,
00295         Ne,
00296         NCpW,
00297         Cs
00298     );
00299 
00300     // Correct surface values due to emitted species
00301     this->correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Pr, kappa);
00302 
00303 
00304     // Surface reactions
00305     // ~~~~~~~~~~~~~~~~~
00306 
00307     // Change in carrier phase composition due to surface reactions
00308     scalarField dMassSRGas(YGas_.size(), 0.0);
00309     scalarField dMassSRLiquid(YLiquid_.size(), 0.0);
00310     scalarField dMassSRSolid(YSolid_.size(), 0.0);
00311     scalarField
00312         dMassSRCarrier
00313         (
00314             td.cloud().mcCarrierThermo().species().size(),
00315             0.0
00316         );
00317 
00318     // Clac mass and enthalpy transfer due to surface reactions
00319     calcSurfaceReactions
00320     (
00321         td,
00322         dt,
00323         cellI,
00324         d0,
00325         T0,
00326         mass0,
00327         canCombust_,
00328         Ne,
00329         YMix,
00330         YGas_,
00331         YLiquid_,
00332         YSolid_,
00333         dMassSRGas,
00334         dMassSRLiquid,
00335         dMassSRSolid,
00336         dMassSRCarrier,
00337         Sh,
00338         dhsTrans
00339     );
00340 
00341 
00342     // Update component mass fractions
00343     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00344 
00345     scalarField dMassGas = dMassDV + dMassSRGas;
00346     scalarField dMassLiquid = dMassPC + dMassSRLiquid;
00347     scalarField dMassSolid = dMassSRSolid;
00348 
00349     scalar mass1 =
00350         updateMassFractions(mass0, dMassGas, dMassLiquid, dMassSolid);
00351 
00352 
00353     // Heat transfer
00354     // ~~~~~~~~~~~~~
00355 
00356     // Calculate new particle temperature
00357     scalar T1 =
00358         this->calcHeatTransfer
00359         (
00360             td,
00361             dt,
00362             cellI,
00363             Re,
00364             Pr,
00365             kappa,
00366             d0,
00367             rho0,
00368             T0,
00369             cp0,
00370             NCpW,
00371             Sh,
00372             dhsTrans
00373         );
00374 
00375 
00376     // Motion
00377     // ~~~~~~
00378 
00379     // Calculate new particle velocity
00380     vector U1 =
00381         this->calcVelocity
00382         (
00383             td,
00384             dt,
00385             cellI,
00386             Re,
00387             mus,
00388             d0,
00389             U0,
00390             rho0,
00391             mass0,
00392             Su,
00393             dUTrans
00394         );
00395 
00396     dUTrans += 0.5*(mass0 - mass1)*(U0 + U1);
00397 
00398 
00399     // Accumulate carrier phase source terms
00400     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00401 
00402     if (td.cloud().coupled())
00403     {
00404         // Transfer mass lost from particle to carrier mass source
00405         forAll(YGas_, i)
00406         {
00407             label gid = td.cloud().composition().localToGlobalCarrierId(GAS, i);
00408             td.cloud().rhoTrans(gid)[cellI] += np0*dMassGas[i];
00409         }
00410         forAll(YLiquid_, i)
00411         {
00412             label gid = td.cloud().composition().localToGlobalCarrierId(LIQ, i);
00413             td.cloud().rhoTrans(gid)[cellI] += np0*dMassLiquid[i];
00414         }
00415 /*
00416         // No mapping between solid components and carrier phase
00417         forAll(YSolid_, i)
00418         {
00419             label gid = td.cloud().composition().localToGlobalCarrierId(SLD, i);
00420             td.cloud().rhoTrans(gid)[cellI] += np0*dMassSolid[i];
00421         }
00422 */
00423         forAll(dMassSRCarrier, i)
00424         {
00425             td.cloud().rhoTrans(i)[cellI] += np0*dMassSRCarrier[i];
00426         }
00427 
00428         // Update momentum transfer
00429         td.cloud().UTrans()[cellI] += np0*dUTrans;
00430 
00431         // Update sensible enthalpy transfer
00432         td.cloud().hsTrans()[cellI] += np0*dhsTrans;
00433     }
00434 
00435 
00436     // Remove the particle when mass falls below minimum threshold
00437     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00438 
00439     if (mass1 < td.constProps().minParticleMass())
00440     {
00441         td.keepParticle = false;
00442 
00443         if (td.cloud().coupled())
00444         {
00445             // Absorb parcel into carrier phase
00446             forAll(YGas_, i)
00447             {
00448                 label gid =
00449                     td.cloud().composition().localToGlobalCarrierId(GAS, i);
00450                 td.cloud().rhoTrans(gid)[cellI] += np0*mass1*YMix[GAS]*YGas_[i];
00451             }
00452             forAll(YLiquid_, i)
00453             {
00454                 label gid =
00455                     td.cloud().composition().localToGlobalCarrierId(LIQ, i);
00456                 td.cloud().rhoTrans(gid)[cellI] +=
00457                     np0*mass1*YMix[LIQ]*YLiquid_[i];
00458             }
00459 /*
00460             // No mapping between solid components and carrier phase
00461             forAll(YSolid_, i)
00462             {
00463                 label gid =
00464                     td.cloud().composition().localToGlobalCarrierId(SLD, i);
00465                 td.cloud().rhoTrans(gid)[cellI] +=
00466                     np0*mass1*YMix[SLD]*YSolid_[i];
00467             }
00468 */
00469             td.cloud().UTrans()[cellI] += np0*mass1*U1;
00470             td.cloud().hsTrans()[cellI] +=
00471                 np0*mass1*HEff(td, pc, T1, idG, idL, idS); // using total h
00472         }
00473     }
00474 
00475 
00476     // Set new particle properties
00477     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
00478 
00479     else
00480     {
00481         this->cp_ = cpEff(td, pc, T1, idG, idL, idS);
00482         this->T_ = T1;
00483         this->U_ = U1;
00484 
00485         // Update particle density or diameter
00486         if (td.constProps().constantVolume())
00487         {
00488             this->rho_ = mass1/this->volume();
00489         }
00490         else
00491         {
00492             this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
00493         }
00494     }
00495 }
00496 
00497 
00498 template<class ParcelType>
00499 template<class TrackData>
00500 void Foam::ReactingMultiphaseParcel<ParcelType>::calcDevolatilisation
00501 (
00502     TrackData& td,
00503     const scalar dt,
00504     const scalar Ts,
00505     const scalar d,
00506     const scalar T,
00507     const scalar mass,
00508     const scalar mass0,
00509     const label idVolatile,
00510     const scalar YVolatileTot,
00511     const scalarField& YVolatile,
00512     bool& canCombust,
00513     scalarField& dMassDV,
00514     scalar& Sh,
00515     scalar& N,
00516     scalar& NCpW,
00517     scalarField& Cs
00518 ) const
00519 {
00520     // Check that model is active, and that the parcel temperature is
00521     // within necessary limits for devolatilisation to occur
00522     if
00523     (
00524         !td.cloud().devolatilisation().active()
00525      || T < td.constProps().Tvap()
00526     )
00527     {
00528         return;
00529     }
00530 
00531     // Total mass of volatiles evolved
00532     const scalar dMassTot = td.cloud().devolatilisation().calculate
00533     (
00534         dt,
00535         mass0,
00536         mass,
00537         T,
00538         td.cloud().composition().YMixture0()[idVolatile],
00539         YVolatileTot,
00540         canCombust
00541     );
00542 
00543     // Volatile mass transfer - equal components of each volatile specie
00544     dMassDV = YVolatile*dMassTot;
00545 
00546     td.cloud().addToMassDevolatilisation(this->nParticle_*dMassTot);
00547 
00548     Sh -= dMassTot*td.constProps().LDevol()/dt;
00549 
00550     // Molar average molecular weight of carrier mix
00551     const scalar Wc = this->rhoc_*specie::RR*this->Tc_/this->pc_;
00552 
00553     // Update molar emissions
00554     forAll(dMassDV, i)
00555     {
00556         // Note: hardcoded gaseous diffusivities for now
00557         // TODO: add to carrier thermo
00558         const scalar beta = sqr(cbrt(15.0) + cbrt(15.0));
00559         const label id =
00560             td.cloud().composition().localToGlobalCarrierId(GAS, i);
00561         const scalar Cp = td.cloud().mcCarrierThermo().speciesData()[id].Cp(Ts);
00562         const scalar W = td.cloud().mcCarrierThermo().speciesData()[id].W();
00563         const scalar Ni = dMassDV[i]/(this->areaS(d)*dt*W);
00564 
00565         // Dab calc'd using API vapour mass diffusivity function
00566         const scalar Dab =
00567             3.6059e-3*(pow(1.8*Ts, 1.75))*sqrt(1.0/W + 1.0/Wc)/(this->pc_*beta);
00568 
00569         N += Ni;
00570         NCpW += Ni*Cp*W;
00571         Cs[id] += Ni*d/(2.0*Dab);
00572      }
00573 }
00574 
00575 
00576 template<class ParcelType>
00577 template<class TrackData>
00578 void Foam::ReactingMultiphaseParcel<ParcelType>::calcSurfaceReactions
00579 (
00580     TrackData& td,
00581     const scalar dt,
00582     const label cellI,
00583     const scalar d,
00584     const scalar T,
00585     const scalar mass,
00586     const bool canCombust,
00587     const scalar N,
00588     const scalarField& YMix,
00589     const scalarField& YGas,
00590     const scalarField& YLiquid,
00591     const scalarField& YSolid,
00592     scalarField& dMassSRGas,
00593     scalarField& dMassSRLiquid,
00594     scalarField& dMassSRSolid,
00595     scalarField& dMassSRCarrier,
00596     scalar& Sh,
00597     scalar& dhsTrans
00598 ) const
00599 {
00600     // Check that model is active
00601     if (!td.cloud().surfaceReaction().active() || !canCombust)
00602     {
00603         return;
00604     }
00605 
00606     // Update surface reactions
00607     const scalar hReaction = td.cloud().surfaceReaction().calculate
00608     (
00609         dt,
00610         cellI,
00611         d,
00612         T,
00613         this->Tc_,
00614         this->pc_,
00615         this->rhoc_,
00616         mass,
00617         YGas,
00618         YLiquid,
00619         YSolid,
00620         YMix,
00621         N,
00622         dMassSRGas,
00623         dMassSRLiquid,
00624         dMassSRSolid,
00625         dMassSRCarrier
00626     );
00627 
00628     td.cloud().addToMassSurfaceReaction
00629     (
00630         this->nParticle_
00631        *(sum(dMassSRGas) + sum(dMassSRLiquid) + sum(dMassSRSolid))
00632     );
00633 
00634     const scalar xsi = min(T/5000.0, 1.0);
00635     const scalar hRetentionCoeffMod =
00636         (1.0 - xsi*xsi)*td.constProps().hRetentionCoeff();
00637 
00638     Sh += hRetentionCoeffMod*hReaction/dt;
00639 
00640     dhsTrans += (1.0 - hRetentionCoeffMod)*hReaction;
00641 }
00642 
00643 
00644 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00645 
00646 template <class ParcelType>
00647 Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
00648 (
00649     const ReactingMultiphaseParcel<ParcelType>& p
00650 )
00651 :
00652     ReactingParcel<ParcelType>(p),
00653     YGas_(p.YGas_),
00654     YLiquid_(p.YLiquid_),
00655     YSolid_(p.YSolid_)
00656 {}
00657 
00658 
00659 // * * * * * * * * * * * * * * IOStream operators  * * * * * * * * * * * * * //
00660 
00661 #include "ReactingMultiphaseParcelIO.C"
00662 
00663 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines