1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "ReactingMultiphaseParcel.H"
27 #include "mathematicalConstants.H"
29 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
31 template<class ParcelType>
32 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::GAS(0);
34 template<class ParcelType>
35 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::LIQ(1);
37 template<class ParcelType>
38 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::SLD(2);
41 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
43 template<class ParcelType>
44 template<class TrackData>
45 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::cpEff
56 this->Y_[GAS]*td.cloud().composition().cp(idG, YGas_, p, T)
57 + this->Y_[LIQ]*td.cloud().composition().cp(idL, YLiquid_, p, T)
58 + this->Y_[SLD]*td.cloud().composition().cp(idS, YSolid_, p, T);
62 template<class ParcelType>
63 template<class TrackData>
64 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::HEff
75 this->Y_[GAS]*td.cloud().composition().H(idG, YGas_, p, T)
76 + this->Y_[LIQ]*td.cloud().composition().H(idL, YLiquid_, p, T)
77 + this->Y_[SLD]*td.cloud().composition().H(idS, YSolid_, p, T);
81 template<class ParcelType>
82 template<class TrackData>
83 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::LEff
94 this->Y_[GAS]*td.cloud().composition().L(idG, YGas_, p, T)
95 + this->Y_[LIQ]*td.cloud().composition().L(idL, YLiquid_, p, T)
96 + this->Y_[SLD]*td.cloud().composition().L(idS, YSolid_, p, T);
100 template<class ParcelType>
101 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::updateMassFractions
104 const scalarField& dMassGas,
105 const scalarField& dMassLiquid,
106 const scalarField& dMassSolid
109 scalarField& YMix = this->Y_;
112 this->updateMassFraction(mass0*YMix[GAS], dMassGas, YGas_);
114 this->updateMassFraction(mass0*YMix[LIQ], dMassLiquid, YLiquid_);
116 this->updateMassFraction(mass0*YMix[SLD], dMassSolid, YSolid_);
118 scalar massNew = max(massGas + massLiquid + massSolid, ROOTVSMALL);
120 YMix[GAS] = massGas/massNew;
121 YMix[LIQ] = massLiquid/massNew;
122 YMix[SLD] = 1.0 - YMix[GAS] - YMix[LIQ];
128 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
130 template<class ParcelType>
131 template<class TrackData>
132 void Foam::ReactingMultiphaseParcel<ParcelType>::setCellValues
139 ReactingParcel<ParcelType>::setCellValues(td, dt, cellI);
143 template<class ParcelType>
144 template<class TrackData>
145 void Foam::ReactingMultiphaseParcel<ParcelType>::cellValueSourceCorrection
152 scalar massCell = this->massCell(cellI);
154 scalar addedMass = 0.0;
155 forAll(td.cloud().rhoTrans(), i)
157 addedMass += td.cloud().rhoTrans(i)[cellI];
160 this->rhoc_ += addedMass/td.cloud().pMesh().cellVolumes()[cellI];
162 scalar massCellNew = massCell + addedMass;
163 this->Uc_ += td.cloud().UTrans()[cellI]/massCellNew;
166 if (addedMass > ROOTVSMALL)
168 forAll(td.cloud().rhoTrans(), i)
170 scalar Y = td.cloud().rhoTrans(i)[cellI]/addedMass;
172 Y*td.cloud().mcCarrierThermo().speciesData()[i].Cp(this->Tc_);
175 const scalar cpc = td.cpInterp().psi()[cellI];
176 this->cpc_ = (massCell*cpc + addedMass*cpEff)/massCellNew;
178 this->Tc_ += td.cloud().hsTrans()[cellI]/(this->cpc_*massCellNew);
182 template<class ParcelType>
183 template<class TrackData>
184 void Foam::ReactingMultiphaseParcel<ParcelType>::calc
191 // Define local properties at beginning of timestep
192 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193 const scalar np0 = this->nParticle_;
194 const scalar d0 = this->d_;
195 const vector& U0 = this->U_;
196 const scalar rho0 = this->rho_;
197 const scalar T0 = this->T_;
198 const scalar cp0 = this->cp_;
199 const scalar mass0 = this->mass();
201 const scalar pc = this->pc_;
203 const scalarField& YMix = this->Y_;
204 const label idG = td.cloud().composition().idGas();
205 const label idL = td.cloud().composition().idLiquid();
206 const label idS = td.cloud().composition().idSolid();
209 // Calc surface values
210 // ~~~~~~~~~~~~~~~~~~~
211 scalar Ts, rhos, mus, Pr, kappa;
212 ThermoParcel<ParcelType>::
213 calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Pr, kappa);
216 scalar Re = this->Re(U0, d0, rhos, mus);
222 // Explicit momentum source for particle
223 vector Su = vector::zero;
225 // Momentum transfer from the particle to the carrier phase
226 vector dUTrans = vector::zero;
228 // Explicit enthalpy source for particle
231 // Sensible enthalpy transfer from the particle to the carrier phase
232 scalar dhsTrans = 0.0;
235 // Phase change in liquid phase
236 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238 // Mass transfer due to phase change
239 scalarField dMassPC(YLiquid_.size(), 0.0);
241 // Molar flux of species emitted from the particle (kmol/m^2/s)
244 // Sum Ni*Cpi*Wi of emission species
247 // Surface concentrations of emitted species
248 scalarField Cs(td.cloud().mcCarrierThermo().species().size(), 0.0);
250 // Calc mass and enthalpy transfer due to phase change
251 this->calcPhaseChange
276 // Mass transfer due to devolatilisation
277 scalarField dMassDV(YGas_.size(), 0.0);
279 // Calc mass and enthalpy transfer due to devolatilisation
280 this->calcDevolatilisation
300 // Correct surface values due to emitted species
301 this->correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Pr, kappa);
307 // Change in carrier phase composition due to surface reactions
308 scalarField dMassSRGas(YGas_.size(), 0.0);
309 scalarField dMassSRLiquid(YLiquid_.size(), 0.0);
310 scalarField dMassSRSolid(YSolid_.size(), 0.0);
314 td.cloud().mcCarrierThermo().species().size(),
318 // Clac mass and enthalpy transfer due to surface reactions
319 this->calcSurfaceReactions
342 // Update component mass fractions
343 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
345 scalarField dMassGas = dMassDV + dMassSRGas;
346 scalarField dMassLiquid = dMassPC + dMassSRLiquid;
347 scalarField dMassSolid = dMassSRSolid;
350 updateMassFractions(mass0, dMassGas, dMassLiquid, dMassSolid);
356 // Calculate new particle temperature
358 this->calcHeatTransfer
379 // Calculate new particle velocity
381 this->calcVelocity(td, dt, cellI, Re, mus, d0, U0, rho0, mass0, Su, dUTrans);
383 dUTrans += 0.5*(mass0 - mass1)*(U0 + U1);
386 // Accumulate carrier phase source terms
387 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
389 if (td.cloud().coupled())
391 // Transfer mass lost from particle to carrier mass source
394 label gid = td.cloud().composition().localToGlobalCarrierId(GAS, i);
395 td.cloud().rhoTrans(gid)[cellI] += np0*dMassGas[i];
399 label gid = td.cloud().composition().localToGlobalCarrierId(LIQ, i);
400 td.cloud().rhoTrans(gid)[cellI] += np0*dMassLiquid[i];
403 // No mapping between solid components and carrier phase
406 label gid = td.cloud().composition().localToGlobalCarrierId(SLD, i);
407 td.cloud().rhoTrans(gid)[cellI] += np0*dMassSolid[i];
410 forAll(dMassSRCarrier, i)
412 td.cloud().rhoTrans(i)[cellI] += np0*dMassSRCarrier[i];
415 // Update momentum transfer
416 td.cloud().UTrans()[cellI] += np0*dUTrans;
418 // Update sensible enthalpy transfer
419 td.cloud().hsTrans()[cellI] += np0*dhsTrans;
423 // Remove the particle when mass falls below minimum threshold
424 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
426 if (mass1 < td.constProps().minParticleMass())
428 td.keepParticle = false;
430 if (td.cloud().coupled())
432 // Absorb parcel into carrier phase
436 td.cloud().composition().localToGlobalCarrierId(GAS, i);
437 td.cloud().rhoTrans(gid)[cellI] += np0*mass1*YMix[GAS]*YGas_[i];
442 td.cloud().composition().localToGlobalCarrierId(LIQ, i);
443 td.cloud().rhoTrans(gid)[cellI] +=
444 np0*mass1*YMix[LIQ]*YLiquid_[i];
447 // No mapping between solid components and carrier phase
451 td.cloud().composition().localToGlobalCarrierId(SLD, i);
452 td.cloud().rhoTrans(gid)[cellI] +=
453 np0*mass1*YMix[SLD]*YSolid_[i];
456 td.cloud().UTrans()[cellI] += np0*mass1*U1;
457 td.cloud().hsTrans()[cellI] +=
458 np0*mass1*HEff(td, pc, T1, idG, idL, idS); // using total h
463 // Set new particle properties
464 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
468 this->cp_ = cpEff(td, pc, T1, idG, idL, idS);
472 // Update particle density or diameter
473 if (td.constProps().constantVolume())
475 this->rho_ = mass1/this->volume();
479 this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
485 template<class ParcelType>
486 template<class TrackData>
487 void Foam::ReactingMultiphaseParcel<ParcelType>::calcDevolatilisation
496 const label idVolatile,
497 const scalar YVolatileTot,
498 const scalarField& YVolatile,
500 scalarField& dMassDV,
507 // Check that model is active, and that the parcel temperature is
508 // within necessary limits for devolatilisation to occur
511 !td.cloud().devolatilisation().active()
512 || T < td.constProps().Tvap()
518 // Total mass of volatiles evolved
519 const scalar dMassTot = td.cloud().devolatilisation().calculate
525 td.cloud().composition().YMixture0()[idVolatile],
530 // Volatile mass transfer - equal components of each volatile specie
531 dMassDV = YVolatile*dMassTot;
533 td.cloud().addToMassDevolatilisation(this->nParticle_*dMassTot);
535 Sh -= dMassTot*td.constProps().LDevol()/dt;
537 // Molar average molecular weight of carrier mix
538 const scalar Wc = this->rhoc_*specie::RR()*this->Tc_/this->pc_;
540 // Update molar emissions
543 // Note: hardcoded gaseous diffusivities for now
544 // TODO: add to carrier thermo
545 const scalar beta = sqr(cbrt(15.0) + cbrt(15.0));
547 td.cloud().composition().localToGlobalCarrierId(GAS, i);
548 const scalar Cp = td.cloud().mcCarrierThermo().speciesData()[id].Cp(Ts);
549 const scalar W = td.cloud().mcCarrierThermo().speciesData()[id].W();
550 const scalar Ni = dMassDV[i]/(this->areaS(d)*dt*W);
552 // Dab calc'd using API vapour mass diffusivity function
554 3.6059e-3*(pow(1.8*Ts, 1.75))*sqrt(1.0/W + 1.0/Wc)/(this->pc_*beta);
558 Cs[id] += Ni*d/(2.0*Dab);
563 template<class ParcelType>
564 template<class TrackData>
565 void Foam::ReactingMultiphaseParcel<ParcelType>::calcSurfaceReactions
573 const bool canCombust,
575 const scalarField& YMix,
576 const scalarField& YGas,
577 const scalarField& YLiquid,
578 const scalarField& YSolid,
579 scalarField& dMassSRGas,
580 scalarField& dMassSRLiquid,
581 scalarField& dMassSRSolid,
582 scalarField& dMassSRCarrier,
587 // Check that model is active
588 if (!td.cloud().surfaceReaction().active() || !canCombust)
593 // Update surface reactions
594 const scalar hReaction = td.cloud().surfaceReaction().calculate
615 td.cloud().addToMassSurfaceReaction
618 *(sum(dMassSRGas) + sum(dMassSRLiquid) + sum(dMassSRSolid))
621 const scalar xsi = min(T/5000.0, 1.0);
622 const scalar hRetentionCoeffMod =
623 (1.0 - xsi*xsi)*td.constProps().hRetentionCoeff();
625 Sh += hRetentionCoeffMod*hReaction/dt;
627 dhsTrans += (1.0 - hRetentionCoeffMod)*hReaction;
631 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
633 template <class ParcelType>
634 Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
636 const ReactingMultiphaseParcel<ParcelType>& p
639 ReactingParcel<ParcelType>(p),
641 YLiquid_(p.YLiquid_),
646 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
648 #include "ReactingMultiphaseParcelIO.C"
650 // ************************************************************************* //