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

ReactingParcel_.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) 1991-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 "ReactingParcel_.H"
00027 #include <OpenFOAM/mathematicalConstants.H>
00028 #include <specie/specie.H>
00029 
00030 // * * * * * * * * * * *  Protected Member Functions * * * * * * * * * * * * //
00031 
00032 template<class ParcelType>
00033 template<class TrackData>
00034 void Foam::ReactingParcel<ParcelType>::setCellValues
00035 (
00036     TrackData& td,
00037     const scalar dt,
00038     const label cellI
00039 )
00040 {
00041     ThermoParcel<ParcelType>::setCellValues(td, dt, cellI);
00042 
00043     pc_ = td.pInterp().interpolate(this->position(), cellI);
00044     if (pc_ < td.constProps().pMin())
00045     {
00046         WarningIn
00047         (
00048             "void Foam::ReactingParcel<ParcelType>::setCellValues"
00049             "("
00050                 "TrackData&, "
00051                 "const scalar, "
00052                 "const label"
00053             ")"
00054         )   << "Limiting observed pressure in cell " << cellI << " to "
00055             << td.constProps().pMin() <<  nl << endl;
00056 
00057         pc_ = td.constProps().pMin();
00058     }
00059 }
00060 
00061 
00062 template<class ParcelType>
00063 template<class TrackData>
00064 void Foam::ReactingParcel<ParcelType>::cellValueSourceCorrection
00065 (
00066     TrackData& td,
00067     const scalar dt,
00068     const label cellI
00069 )
00070 {
00071     scalar massCell = this->massCell(cellI);
00072 
00073     scalar addedMass = 0.0;
00074     forAll(td.cloud().rhoTrans(), i)
00075     {
00076         addedMass += td.cloud().rhoTrans(i)[cellI];
00077     }
00078 
00079     this->rhoc_ += addedMass/td.cloud().pMesh().cellVolumes()[cellI];
00080 
00081     scalar massCellNew = massCell + addedMass;
00082     this->Uc_ += td.cloud().UTrans()[cellI]/massCellNew;
00083 
00084     scalar cpEff = 0;
00085     if (addedMass > ROOTVSMALL)
00086     {
00087         forAll(td.cloud().rhoTrans(), i)
00088         {
00089             scalar Y = td.cloud().rhoTrans(i)[cellI]/addedMass;
00090             cpEff +=
00091                 Y*td.cloud().mcCarrierThermo().speciesData()[i].Cp(this->Tc_);
00092         }
00093     }
00094     const scalar cpc = td.cpInterp().psi()[cellI];
00095     this->cpc_ = (massCell*cpc + addedMass*cpEff)/massCellNew;
00096 
00097     this->Tc_ += td.cloud().hsTrans()[cellI]/(this->cpc_*massCellNew);
00098 }
00099 
00100 
00101 template<class ParcelType>
00102 template<class TrackData>
00103 void Foam::ReactingParcel<ParcelType>::correctSurfaceValues
00104 (
00105     TrackData& td,
00106     const label cellI,
00107     const scalar T,
00108     const scalarField& Cs,
00109     scalar& rhos,
00110     scalar& mus,
00111     scalar& Pr,
00112     scalar& kappa
00113 )
00114 {
00115     // No correction if total concentration of emitted species is small
00116     if (sum(Cs) < SMALL)
00117     {
00118         return;
00119     }
00120 
00121     // Far field carrier  molar fractions
00122     scalarField Xinf(td.cloud().mcCarrierThermo().speciesData().size());
00123 
00124     forAll(Xinf, i)
00125     {
00126         Xinf[i] =
00127             td.cloud().mcCarrierThermo().Y(i)[cellI]
00128            /td.cloud().mcCarrierThermo().speciesData()[i].W();
00129     }
00130     Xinf /= sum(Xinf);
00131 
00132     // Molar fraction of far field species at particle surface
00133     const scalar Xsff = 1.0 - min(sum(Cs)*specie::RR*this->T_/pc_, 1.0);
00134 
00135     // Surface carrier total molar concentration
00136     const scalar CsTot = pc_/(specie::RR*this->T_);
00137 
00138     // Surface carrier composition (molar fraction)
00139     scalarField Xs(Xinf.size());
00140 
00141     // Surface carrier composition (mass fraction)
00142     scalarField Ys(Xinf.size());
00143 
00144     forAll(Xs, i)
00145     {
00146         // Molar concentration of species at particle surface
00147         const scalar Csi = Cs[i] + Xsff*Xinf[i]*CsTot;
00148 
00149         Xs[i] = (2.0*Csi + Xinf[i]*CsTot)/3.0;
00150         Ys[i] = Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].W();
00151     }
00152     Xs /= sum(Xs);
00153     Ys /= sum(Ys);
00154 
00155 
00156     rhos = 0;
00157     mus = 0;
00158     kappa = 0;
00159     scalar cps = 0;
00160     scalar sumYiSqrtW = 0;
00161     scalar sumYiCbrtW = 0;
00162 
00163     forAll(Ys, i)
00164     {
00165         const scalar sqrtW =
00166             sqrt(td.cloud().mcCarrierThermo().speciesData()[i].W());
00167         const scalar cbrtW =
00168             cbrt(td.cloud().mcCarrierThermo().speciesData()[i].W());
00169 
00170         rhos += Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].W();
00171         mus += Ys[i]*sqrtW*td.cloud().mcCarrierThermo().speciesData()[i].mu(T);
00172         kappa +=
00173             Ys[i]*cbrtW*td.cloud().mcCarrierThermo().speciesData()[i].kappa(T);
00174         cps += Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].Cp(T);
00175 
00176         sumYiSqrtW += Ys[i]*sqrtW;
00177         sumYiCbrtW += Ys[i]*cbrtW;
00178     }
00179 
00180     rhos *= pc_/(specie::RR*T);
00181     mus /= sumYiSqrtW;
00182     kappa /= sumYiCbrtW;
00183     Pr = cps*mus/kappa;
00184 }
00185 
00186 
00187 template<class ParcelType>
00188 Foam::scalar Foam::ReactingParcel<ParcelType>::updateMassFraction
00189 (
00190     const scalar mass0,
00191     const scalarField& dMass,
00192     scalarField& Y
00193 ) const
00194 {
00195     scalar mass1 = mass0 - sum(dMass);
00196 
00197     // only update the mass fractions if the new particle mass is finite
00198     if (mass1 > ROOTVSMALL)
00199     {
00200         forAll(Y, i)
00201         {
00202             Y[i] = (Y[i]*mass0 - dMass[i])/mass1;
00203         }
00204     }
00205 
00206     return mass1;
00207 }
00208 
00209 
00210 template<class ParcelType>
00211 template<class TrackData>
00212 void Foam::ReactingParcel<ParcelType>::calc
00213 (
00214     TrackData& td,
00215     const scalar dt,
00216     const label cellI
00217 )
00218 {
00219     // Define local properties at beginning of time step
00220     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00221     const scalar np0 = this->nParticle_;
00222     const scalar d0 = this->d_;
00223     const vector& U0 = this->U_;
00224     const scalar rho0 = this->rho_;
00225     const scalar T0 = this->T_;
00226     const scalar cp0 = this->cp_;
00227     const scalar mass0 = this->mass();
00228 
00229 
00230     // Calc surface values
00231     // ~~~~~~~~~~~~~~~~~~~
00232     scalar Ts, rhos, mus, Pr, kappa;
00233     this->calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Pr, kappa);
00234 
00235     // Reynolds number
00236     scalar Re = this->Re(U0, d0, rhos, mus);
00237 
00238 
00239     // Sources
00240     //~~~~~~~~
00241 
00242     // Explicit momentum source for particle
00243     vector Su = vector::zero;
00244 
00245     // Momentum transfer from the particle to the carrier phase
00246     vector dUTrans = vector::zero;
00247 
00248     // Explicit enthalpy source for particle
00249     scalar Sh = 0.0;
00250 
00251     // Sensible enthalpy transfer from the particle to the carrier phase
00252     scalar dhsTrans = 0.0;
00253 
00254 
00255     // Phase change
00256     // ~~~~~~~~~~~~
00257 
00258     // Mass transfer due to phase change
00259     scalarField dMassPC(Y_.size(), 0.0);
00260 
00261     // Molar flux of species emitted from the particle (kmol/m^2/s)
00262     scalar Ne = 0.0;
00263 
00264     // Sum Ni*Cpi*Wi of emission species
00265     scalar NCpW = 0.0;
00266 
00267     // Surface concentrations of emitted species
00268     scalarField Cs(td.cloud().mcCarrierThermo().species().size(), 0.0);
00269 
00270     // Calc mass and enthalpy transfer due to phase change
00271     calcPhaseChange
00272     (
00273         td,
00274         dt,
00275         cellI,
00276         Re,
00277         Ts,
00278         mus/rhos,
00279         d0,
00280         T0,
00281         mass0,
00282         0,
00283         1.0,
00284         Y_,
00285         dMassPC,
00286         Sh,
00287         Ne,
00288         NCpW,
00289         Cs
00290     );
00291 
00292     // Correct surface values due to emitted species
00293     correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Pr, kappa);
00294 
00295     // Update particle component mass and mass fractions
00296     scalar mass1 = updateMassFraction(mass0, dMassPC, Y_);
00297 
00298 
00299     // Heat transfer
00300     // ~~~~~~~~~~~~~
00301 
00302     // Calculate new particle temperature
00303     scalar T1 =
00304         this->calcHeatTransfer
00305         (
00306             td,
00307             dt,
00308             cellI,
00309             Re,
00310             Pr,
00311             kappa,
00312             d0,
00313             rho0,
00314             T0,
00315             cp0,
00316             NCpW,
00317             Sh,
00318             dhsTrans
00319         );
00320 
00321 
00322     // Motion
00323     // ~~~~~~
00324 
00325     // Calculate new particle velocity
00326     vector U1 =
00327         this->calcVelocity
00328         (
00329             td,
00330             dt,
00331             cellI,
00332             Re,
00333             mus,
00334             d0,
00335             U0,
00336             rho0,
00337             mass0,
00338             Su,
00339             dUTrans
00340         );
00341 
00342     dUTrans += 0.5*(mass0 - mass1)*(U0 + U1);
00343 
00344     // Accumulate carrier phase source terms
00345     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00346     if (td.cloud().coupled())
00347     {
00348         // Transfer mass lost from particle to carrier mass source
00349         forAll(dMassPC, i)
00350         {
00351             label gid = td.cloud().composition().localToGlobalCarrierId(0, i);
00352             td.cloud().rhoTrans(gid)[cellI] += np0*dMassPC[i];
00353         }
00354 
00355         // Update momentum transfer
00356         td.cloud().UTrans()[cellI] += np0*dUTrans;
00357 
00358         // Update sensible enthalpy transfer
00359         td.cloud().hsTrans()[cellI] += np0*dhsTrans;
00360     }
00361 
00362 
00363     // Remove the particle when mass falls below minimum threshold
00364     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00365     if (mass1 < td.constProps().minParticleMass())
00366     {
00367         td.keepParticle = false;
00368 
00369         if (td.cloud().coupled())
00370         {
00371             // Absorb parcel into carrier phase
00372             forAll(Y_, i)
00373             {
00374                 label gid =
00375                     td.cloud().composition().localToGlobalCarrierId(0, i);
00376                 td.cloud().rhoTrans(gid)[cellI] += np0*mass1*Y_[i];
00377             }
00378             td.cloud().UTrans()[cellI] += np0*mass1*U1;
00379             td.cloud().hsTrans()[cellI] +=
00380                 np0*mass1*td.cloud().composition().H(0, Y_, pc_, T1);
00381         }
00382     }
00383 
00384 
00385     // Set new particle properties
00386     // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
00387 
00388     else
00389     {
00390         this->cp_ = td.cloud().composition().cp(0, Y_, pc_, T1);
00391         this->T_ = T1;
00392         this->U_ = U1;
00393 
00394         // Update particle density or diameter
00395         if (td.constProps().constantVolume())
00396         {
00397             this->rho_ = mass1/this->volume();
00398         }
00399         else
00400         {
00401             this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
00402         }
00403     }
00404 }
00405 
00406 
00407 template<class ParcelType>
00408 template<class TrackData>
00409 void Foam::ReactingParcel<ParcelType>::calcPhaseChange
00410 (
00411     TrackData& td,
00412     const scalar dt,
00413     const label cellI,
00414     const scalar Re,
00415     const scalar Ts,
00416     const scalar nus,
00417     const scalar d,
00418     const scalar T,
00419     const scalar mass,
00420     const label idPhase,
00421     const scalar YPhase,
00422     const scalarField& YComponents,
00423     scalarField& dMassPC,
00424     scalar& Sh,
00425     scalar& N,
00426     scalar& NCpW,
00427     scalarField& Cs
00428 )
00429 {
00430     typedef PhaseChangeModel
00431     <
00432         typename ReactingParcel<ParcelType>::trackData::cloudType
00433     > phaseChangeModelType;
00434 
00435     if
00436     (
00437         !td.cloud().phaseChange().active()
00438      || T < td.constProps().Tvap()
00439      || YPhase < SMALL
00440     )
00441     {
00442         return;
00443     }
00444 
00445     // Calculate mass transfer due to phase change
00446     td.cloud().phaseChange().calculate
00447     (
00448         dt,
00449         cellI,
00450         Re,
00451         d,
00452         nus,
00453         T,
00454         Ts,
00455         pc_,
00456         dMassPC
00457     );
00458 
00459     // Limit phase change mass by availability of each specie
00460     dMassPC = min(mass*YPhase*YComponents, dMassPC);
00461 
00462     scalar dMassTot = sum(dMassPC);
00463 
00464     // Add to cumulative phase change mass
00465     td.cloud().addToMassPhaseChange(this->nParticle_*dMassTot);
00466 
00467     // Average molecular weight of carrier mix - assumes perfect gas
00468     scalar Wc = this->rhoc_*specie::RR*this->Tc_/this->pc_;
00469 
00470     forAll(YComponents, i)
00471     {
00472         const label idc =
00473             td.cloud().composition().localToGlobalCarrierId(idPhase, i);
00474         const label idl = td.cloud().composition().globalIds(idPhase)[i];
00475 
00476         // Calculate enthalpy transfer
00477         if
00478         (
00479             td.cloud().phaseChange().enthalpyTransfer()
00480          == phaseChangeModelType::etLatentHeat
00481         )
00482         {
00483             scalar hlp =
00484                 td.cloud().composition().liquids().properties()[idl].hl(pc_, T);
00485 
00486             Sh -= dMassPC[i]*hlp/dt;
00487         }
00488         else
00489         {
00490             // Note: enthalpies of both phases must use the same reference
00491             scalar hc = td.cloud().mcCarrierThermo().speciesData()[idc].H(T);
00492             scalar hp =
00493                 td.cloud().composition().liquids().properties()[idl].h(pc_, T);
00494 
00495             Sh -= dMassPC[i]*(hc - hp)/dt;
00496         }
00497 
00498         // Update particle surface thermo properties
00499         const scalar Dab =
00500             td.cloud().composition().liquids().properties()[idl].D(pc_, Ts, Wc);
00501 
00502         const scalar Cp =
00503             td.cloud().mcCarrierThermo().speciesData()[idc].Cp(Ts);
00504         const scalar W = td.cloud().mcCarrierThermo().speciesData()[idc].W();
00505         const scalar Ni = dMassPC[i]/(this->areaS(d)*dt*W);
00506 
00507         // Molar flux of species coming from the particle (kmol/m^2/s)
00508         N += Ni;
00509 
00510         // Sum of Ni*Cpi*Wi of emission species
00511         NCpW += Ni*Cp*W;
00512 
00513         // Concentrations of emission species
00514         Cs[idc] += Ni*d/(2.0*Dab);
00515     }
00516 }
00517 
00518 
00519 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
00520 
00521 template <class ParcelType>
00522 Foam::ReactingParcel<ParcelType>::ReactingParcel
00523 (
00524     const ReactingParcel<ParcelType>& p
00525 )
00526 :
00527     ThermoParcel<ParcelType>(p),
00528     mass0_(p.mass0_),
00529     Y_(p.Y_),
00530     pc_(p.pc_)
00531 {}
00532 
00533 
00534 // * * * * * * * * * * * * * * IOStream operators  * * * * * * * * * * * * * //
00535 
00536 #include "ReactingParcelIO.C"
00537 
00538 // ************************ vim: set sw=4 sts=4 et: ************************ //
00539 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines