1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "ReactingMultiphaseParcel.H"
28 #include "mathematicalConstants.H"
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 template<class ParcelType>
33 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::GAS(0);
35 template<class ParcelType>
36 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::LIQ(1);
38 template<class ParcelType>
39 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::SLD(2);
42 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
44 template<class ParcelType>
45 template<class TrackData>
46 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::cpEff
57 this->Y_[GAS]*td.cloud().composition().cp(idG, YGas_, p, T)
58 + this->Y_[LIQ]*td.cloud().composition().cp(idL, YLiquid_, p, T)
59 + this->Y_[SLD]*td.cloud().composition().cp(idS, YSolid_, p, T);
63 template<class ParcelType>
64 template<class TrackData>
65 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::HEff
76 this->Y_[GAS]*td.cloud().composition().H(idG, YGas_, p, T)
77 + this->Y_[LIQ]*td.cloud().composition().H(idL, YLiquid_, p, T)
78 + this->Y_[SLD]*td.cloud().composition().H(idS, YSolid_, p, T);
82 template<class ParcelType>
83 template<class TrackData>
84 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::LEff
95 this->Y_[GAS]*td.cloud().composition().L(idG, YGas_, p, T)
96 + this->Y_[LIQ]*td.cloud().composition().L(idL, YLiquid_, p, T)
97 + this->Y_[SLD]*td.cloud().composition().L(idS, YSolid_, p, T);
101 template<class ParcelType>
102 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::updateMassFractions
105 const scalarField& dMassGas,
106 const scalarField& dMassLiquid,
107 const scalarField& dMassSolid
110 scalarField& YMix = this->Y_;
113 this->updateMassFraction(mass0*YMix[GAS], dMassGas, YGas_);
115 this->updateMassFraction(mass0*YMix[LIQ], dMassLiquid, YLiquid_);
117 this->updateMassFraction(mass0*YMix[SLD], dMassSolid, YSolid_);
119 scalar massNew = max(massGas + massLiquid + massSolid, ROOTVSMALL);
121 YMix[GAS] = massGas/massNew;
122 YMix[LIQ] = massLiquid/massNew;
123 YMix[SLD] = 1.0 - YMix[GAS] - YMix[LIQ];
129 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
131 template<class ParcelType>
132 template<class TrackData>
133 void Foam::ReactingMultiphaseParcel<ParcelType>::setCellValues
140 ReactingParcel<ParcelType>::setCellValues(td, dt, cellI);
144 template<class ParcelType>
145 template<class TrackData>
146 void Foam::ReactingMultiphaseParcel<ParcelType>::cellValueSourceCorrection
153 scalar massCell = this->massCell(cellI);
155 scalar addedMass = 0.0;
156 forAll(td.cloud().rhoTrans(), i)
158 addedMass += td.cloud().rhoTrans(i)[cellI];
161 this->rhoc_ += addedMass/td.cloud().pMesh().cellVolumes()[cellI];
163 scalar massCellNew = massCell + addedMass;
164 this->Uc_ += td.cloud().UTrans()[cellI]/massCellNew;
167 if (addedMass > ROOTVSMALL)
169 forAll(td.cloud().rhoTrans(), i)
171 scalar Y = td.cloud().rhoTrans(i)[cellI]/addedMass;
173 Y*td.cloud().mcCarrierThermo().speciesData()[i].Cp(this->Tc_);
176 const scalar cpc = td.cpInterp().psi()[cellI];
177 this->cpc_ = (massCell*cpc + addedMass*cpEff)/massCellNew;
179 this->Tc_ += td.cloud().hsTrans()[cellI]/(this->cpc_*massCellNew);
183 template<class ParcelType>
184 template<class TrackData>
185 void Foam::ReactingMultiphaseParcel<ParcelType>::calc
192 // Define local properties at beginning of timestep
193 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194 const scalar np0 = this->nParticle_;
195 const scalar d0 = this->d_;
196 const vector& U0 = this->U_;
197 const scalar rho0 = this->rho_;
198 const scalar T0 = this->T_;
199 const scalar cp0 = this->cp_;
200 const scalar mass0 = this->mass();
202 const scalar pc = this->pc_;
204 const scalarField& YMix = this->Y_;
205 const label idG = td.cloud().composition().idGas();
206 const label idL = td.cloud().composition().idLiquid();
207 const label idS = td.cloud().composition().idSolid();
210 // Calc surface values
211 // ~~~~~~~~~~~~~~~~~~~
212 scalar Ts, rhos, mus, Pr, kappa;
213 ThermoParcel<ParcelType>::
214 calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Pr, kappa);
217 scalar Re = this->Re(U0, d0, rhos, mus);
223 // Explicit momentum source for particle
224 vector Su = vector::zero;
226 // Momentum transfer from the particle to the carrier phase
227 vector dUTrans = vector::zero;
229 // Explicit enthalpy source for particle
232 // Sensible enthalpy transfer from the particle to the carrier phase
233 scalar dhsTrans = 0.0;
236 // Phase change in liquid phase
237 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
239 // Mass transfer due to phase change
240 scalarField dMassPC(YLiquid_.size(), 0.0);
242 // Molar flux of species emitted from the particle (kmol/m^2/s)
245 // Sum Ni*Cpi*Wi of emission species
248 // Surface concentrations of emitted species
249 scalarField Cs(td.cloud().mcCarrierThermo().species().size(), 0.0);
251 // Calc mass and enthalpy transfer due to phase change
277 // Mass transfer due to devolatilisation
278 scalarField dMassDV(YGas_.size(), 0.0);
280 // Calc mass and enthalpy transfer due to devolatilisation
301 // Correct surface values due to emitted species
302 correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Pr, kappa);
308 // Change in carrier phase composition due to surface reactions
309 scalarField dMassSRGas(YGas_.size(), 0.0);
310 scalarField dMassSRLiquid(YLiquid_.size(), 0.0);
311 scalarField dMassSRSolid(YSolid_.size(), 0.0);
315 td.cloud().mcCarrierThermo().species().size(),
319 // Clac mass and enthalpy transfer due to surface reactions
343 // Update component mass fractions
344 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
346 scalarField dMassGas = dMassDV + dMassSRGas;
347 scalarField dMassLiquid = dMassPC + dMassSRLiquid;
348 scalarField dMassSolid = dMassSRSolid;
351 updateMassFractions(mass0, dMassGas, dMassLiquid, dMassSolid);
357 // Calculate new particle temperature
380 // Calculate new particle velocity
382 calcVelocity(td, dt, cellI, Re, mus, d0, U0, rho0, mass0, Su, dUTrans);
384 dUTrans += 0.5*(mass0 - mass1)*(U0 + U1);
387 // Accumulate carrier phase source terms
388 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
390 if (td.cloud().coupled())
392 // Transfer mass lost from particle to carrier mass source
395 label gid = td.cloud().composition().localToGlobalCarrierId(GAS, i);
396 td.cloud().rhoTrans(gid)[cellI] += np0*dMassGas[i];
400 label gid = td.cloud().composition().localToGlobalCarrierId(LIQ, i);
401 td.cloud().rhoTrans(gid)[cellI] += np0*dMassLiquid[i];
404 // No mapping between solid components and carrier phase
407 label gid = td.cloud().composition().localToGlobalCarrierId(SLD, i);
408 td.cloud().rhoTrans(gid)[cellI] += np0*dMassSolid[i];
411 forAll(dMassSRCarrier, i)
413 td.cloud().rhoTrans(i)[cellI] += np0*dMassSRCarrier[i];
416 // Update momentum transfer
417 td.cloud().UTrans()[cellI] += np0*dUTrans;
419 // Update sensible enthalpy transfer
420 td.cloud().hsTrans()[cellI] += np0*dhsTrans;
424 // Remove the particle when mass falls below minimum threshold
425 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
427 if (mass1 < td.constProps().minParticleMass())
429 td.keepParticle = false;
431 if (td.cloud().coupled())
433 // Absorb parcel into carrier phase
437 td.cloud().composition().localToGlobalCarrierId(GAS, i);
438 td.cloud().rhoTrans(gid)[cellI] += np0*mass1*YMix[GAS]*YGas_[i];
443 td.cloud().composition().localToGlobalCarrierId(LIQ, i);
444 td.cloud().rhoTrans(gid)[cellI] +=
445 np0*mass1*YMix[LIQ]*YLiquid_[i];
448 // No mapping between solid components and carrier phase
452 td.cloud().composition().localToGlobalCarrierId(SLD, i);
453 td.cloud().rhoTrans(gid)[cellI] +=
454 np0*mass1*YMix[SLD]*YSolid_[i];
457 td.cloud().UTrans()[cellI] += np0*mass1*U1;
458 td.cloud().hsTrans()[cellI] +=
459 np0*mass1*HEff(td, pc, T1, idG, idL, idS); // using total h
464 // Set new particle properties
465 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
469 this->cp_ = cpEff(td, pc, T1, idG, idL, idS);
473 // Update particle density or diameter
474 if (td.constProps().constantVolume())
476 this->rho_ = mass1/this->volume();
480 this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
486 template<class ParcelType>
487 template<class TrackData>
488 void Foam::ReactingMultiphaseParcel<ParcelType>::calcDevolatilisation
497 const label idVolatile,
498 const scalar YVolatileTot,
499 const scalarField& YVolatile,
501 scalarField& dMassDV,
508 // Check that model is active, and that the parcel temperature is
509 // within necessary limits for devolatilisation to occur
512 !td.cloud().devolatilisation().active()
513 || T < td.constProps().Tvap()
519 // Total mass of volatiles evolved
520 const scalar dMassTot = td.cloud().devolatilisation().calculate
526 td.cloud().composition().YMixture0()[idVolatile],
531 // Volatile mass transfer - equal components of each volatile specie
532 dMassDV = YVolatile*dMassTot;
534 td.cloud().addToMassDevolatilisation(this->nParticle_*dMassTot);
536 Sh -= dMassTot*td.constProps().LDevol()/dt;
538 // Molar average molecular weight of carrier mix
539 const scalar Wc = this->rhoc_*specie::RR*this->Tc_/this->pc_;
541 // Update molar emissions
544 // Note: hardcoded gaseous diffusivities for now
545 // TODO: add to carrier thermo
546 const scalar beta = sqr(cbrt(15.0) + cbrt(15.0));
548 td.cloud().composition().localToGlobalCarrierId(GAS, i);
549 const scalar Cp = td.cloud().mcCarrierThermo().speciesData()[id].Cp(Ts);
550 const scalar W = td.cloud().mcCarrierThermo().speciesData()[id].W();
551 const scalar Ni = dMassDV[i]/(this->areaS(d)*dt*W);
553 // Dab calc'd using API vapour mass diffusivity function
555 3.6059e-3*(pow(1.8*Ts, 1.75))*sqrt(1.0/W + 1.0/Wc)/(this->pc_*beta);
559 Cs[id] += Ni*d/(2.0*Dab);
564 template<class ParcelType>
565 template<class TrackData>
566 void Foam::ReactingMultiphaseParcel<ParcelType>::calcSurfaceReactions
574 const bool canCombust,
576 const scalarField& YMix,
577 const scalarField& YGas,
578 const scalarField& YLiquid,
579 const scalarField& YSolid,
580 scalarField& dMassSRGas,
581 scalarField& dMassSRLiquid,
582 scalarField& dMassSRSolid,
583 scalarField& dMassSRCarrier,
588 // Check that model is active
589 if (!td.cloud().surfaceReaction().active() || !canCombust)
594 // Update surface reactions
595 const scalar hReaction = td.cloud().surfaceReaction().calculate
616 td.cloud().addToMassSurfaceReaction
619 *(sum(dMassSRGas) + sum(dMassSRLiquid) + sum(dMassSRSolid))
622 const scalar xsi = min(T/5000.0, 1.0);
623 const scalar hRetentionCoeffMod =
624 (1.0 - xsi*xsi)*td.constProps().hRetentionCoeff();
626 Sh += hRetentionCoeffMod*hReaction/dt;
628 dhsTrans += (1.0 - hRetentionCoeffMod)*hReaction;
632 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
634 template <class ParcelType>
635 Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
637 const ReactingMultiphaseParcel<ParcelType>& p
640 ReactingParcel<ParcelType>(p),
642 YLiquid_(p.YLiquid_),
647 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
649 #include "ReactingMultiphaseParcelIO.C"
651 // ************************************************************************* //