1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "ReactingMultiphaseParcel.H"
27 #include "mathematicalConstants.H"
29 using namespace Foam::constant::mathematical;
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 template<class ParcelType>
34 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::GAS(0);
36 template<class ParcelType>
37 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::LIQ(1);
39 template<class ParcelType>
40 const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::SLD(2);
43 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
45 template<class ParcelType>
46 template<class TrackData>
47 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::CpEff
58 this->Y_[GAS]*td.cloud().composition().Cp(idG, YGas_, p, T)
59 + this->Y_[LIQ]*td.cloud().composition().Cp(idL, YLiquid_, p, T)
60 + this->Y_[SLD]*td.cloud().composition().Cp(idS, YSolid_, p, T);
64 template<class ParcelType>
65 template<class TrackData>
66 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::HsEff
77 this->Y_[GAS]*td.cloud().composition().Hs(idG, YGas_, p, T)
78 + this->Y_[LIQ]*td.cloud().composition().Hs(idL, YLiquid_, p, T)
79 + this->Y_[SLD]*td.cloud().composition().Hs(idS, YSolid_, p, T);
83 template<class ParcelType>
84 template<class TrackData>
85 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::LEff
96 this->Y_[GAS]*td.cloud().composition().L(idG, YGas_, p, T)
97 + this->Y_[LIQ]*td.cloud().composition().L(idL, YLiquid_, p, T)
98 + this->Y_[SLD]*td.cloud().composition().L(idS, YSolid_, p, T);
102 template<class ParcelType>
103 Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::updateMassFractions
106 const scalarField& dMassGas,
107 const scalarField& dMassLiquid,
108 const scalarField& dMassSolid
111 scalarField& YMix = this->Y_;
114 this->updateMassFraction(mass0*YMix[GAS], dMassGas, YGas_);
116 this->updateMassFraction(mass0*YMix[LIQ], dMassLiquid, YLiquid_);
118 this->updateMassFraction(mass0*YMix[SLD], dMassSolid, YSolid_);
120 scalar massNew = max(massGas + massLiquid + massSolid, ROOTVSMALL);
122 YMix[GAS] = massGas/massNew;
123 YMix[LIQ] = massLiquid/massNew;
124 YMix[SLD] = 1.0 - YMix[GAS] - YMix[LIQ];
130 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
132 template<class ParcelType>
133 template<class TrackData>
134 void Foam::ReactingMultiphaseParcel<ParcelType>::setCellValues
141 ParcelType::setCellValues(td, dt, cellI);
145 template<class ParcelType>
146 template<class TrackData>
147 void Foam::ReactingMultiphaseParcel<ParcelType>::cellValueSourceCorrection
154 // Re-use correction from reacting parcel
155 ParcelType::cellValueSourceCorrection(td, dt, cellI);
159 template<class ParcelType>
160 template<class TrackData>
161 void Foam::ReactingMultiphaseParcel<ParcelType>::calc
168 typedef typename TrackData::cloudType::reactingCloudType reactingCloudType;
169 const CompositionModel<reactingCloudType>& composition =
170 td.cloud().composition();
173 // Define local properties at beginning of timestep
174 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
176 const scalar np0 = this->nParticle_;
177 const scalar d0 = this->d_;
178 const vector& U0 = this->U_;
179 const scalar T0 = this->T_;
180 const scalar mass0 = this->mass();
182 const scalar pc = this->pc_;
184 const scalarField& YMix = this->Y_;
185 const label idG = composition.idGas();
186 const label idL = composition.idLiquid();
187 const label idS = composition.idSolid();
190 // Calc surface values
191 scalar Ts, rhos, mus, Prs, kappas;
192 this->calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Prs, kappas);
193 scalar Res = this->Re(U0, d0, rhos, mus);
199 // Explicit momentum source for particle
200 vector Su = vector::zero;
202 // Linearised momentum source coefficient
205 // Momentum transfer from the particle to the carrier phase
206 vector dUTrans = vector::zero;
208 // Explicit enthalpy source for particle
211 // Linearised enthalpy source coefficient
214 // Sensible enthalpy transfer from the particle to the carrier phase
215 scalar dhsTrans = 0.0;
218 // 1. Compute models that contribute to mass transfer - U, T held constant
219 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
221 // Phase change in liquid phase
222 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224 // Mass transfer due to phase change
225 scalarField dMassPC(YLiquid_.size(), 0.0);
227 // Molar flux of species emitted from the particle (kmol/m^2/s)
230 // Sum Ni*Cpi*Wi of emission species
233 // Surface concentrations of emitted species
234 scalarField Cs(composition.carrier().species().size(), 0.0);
236 // Calc mass and enthalpy transfer due to phase change
237 this->calcPhaseChange
262 // Mass transfer due to devolatilisation
263 scalarField dMassDV(YGas_.size(), 0.0);
265 // Calc mass and enthalpy transfer due to devolatilisation
288 // Change in carrier phase composition due to surface reactions
289 scalarField dMassSRGas(YGas_.size(), 0.0);
290 scalarField dMassSRLiquid(YLiquid_.size(), 0.0);
291 scalarField dMassSRSolid(YSolid_.size(), 0.0);
292 scalarField dMassSRCarrier(composition.carrier().species().size(), 0.0);
294 // Calc mass and enthalpy transfer due to surface reactions
318 // 2. Update the parcel properties due to change in mass
319 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321 scalarField dMassGas(dMassDV + dMassSRGas);
322 scalarField dMassLiquid(dMassPC + dMassSRLiquid);
323 scalarField dMassSolid(dMassSRSolid);
325 updateMassFractions(mass0, dMassGas, dMassLiquid, dMassSolid);
327 this->Cp_ = CpEff(td, pc, T0, idG, idL, idS);
329 // Update particle density or diameter
330 if (td.cloud().constProps().constantVolume())
332 this->rho_ = mass1/this->volume();
336 this->d_ = cbrt(mass1/this->rho_*6.0/pi);
339 // Remove the particle when mass falls below minimum threshold
340 if (np0*mass1 < td.cloud().constProps().minParticleMass())
342 td.keepParticle = false;
344 if (td.cloud().solution().coupled())
346 scalar dm = np0*mass1;
348 // Absorb parcel into carrier phase
351 label gid = composition.localToGlobalCarrierId(GAS, i);
352 td.cloud().rhoTrans(gid)[cellI] += dm*YMix[GAS]*YGas_[i];
356 label gid = composition.localToGlobalCarrierId(LIQ, i);
357 td.cloud().rhoTrans(gid)[cellI] += dm*YMix[LIQ]*YLiquid_[i];
360 // No mapping between solid components and carrier phase
363 label gid = composition.localToGlobalCarrierId(SLD, i);
364 td.cloud().rhoTrans(gid)[cellI] += dm*YMix[SLD]*YSolid_[i];
367 td.cloud().UTrans()[cellI] += dm*U0;
369 td.cloud().hsTrans()[cellI] += dm*HsEff(td, pc, T0, idG, idL, idS);
371 td.cloud().addToMassPhaseChange(dm);
377 // Correct surface values due to emitted species
378 this->correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Prs, kappas);
379 Res = this->Re(U0, this->d_, rhos, mus);
382 // 3. Compute heat- and momentum transfers
383 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
388 // Calculate new particle temperature
390 this->calcHeatTransfer
405 this->Cp_ = CpEff(td, pc, this->T_, idG, idL, idS);
411 // Calculate new particle velocity
413 this->calcVelocity(td, dt, cellI, Res, mus, mass1, Su, dUTrans, Spu);
416 // 4. Accumulate carrier phase source terms
417 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
419 if (td.cloud().solution().coupled())
421 // Transfer mass lost to carrier mass, momentum and enthalpy sources
424 scalar dm = np0*dMassGas[i];
425 label gid = composition.localToGlobalCarrierId(GAS, i);
426 scalar hs = composition.carrier().Hs(gid, T0);
427 td.cloud().rhoTrans(gid)[cellI] += dm;
428 td.cloud().UTrans()[cellI] += dm*U0;
429 td.cloud().hsTrans()[cellI] += dm*hs;
433 scalar dm = np0*dMassLiquid[i];
434 label gid = composition.localToGlobalCarrierId(LIQ, i);
435 scalar hs = composition.carrier().Hs(gid, T0);
436 td.cloud().rhoTrans(gid)[cellI] += dm;
437 td.cloud().UTrans()[cellI] += dm*U0;
438 td.cloud().hsTrans()[cellI] += dm*hs;
441 // No mapping between solid components and carrier phase
444 scalar dm = np0*dMassSolid[i];
445 label gid = composition.localToGlobalCarrierId(SLD, i);
446 scalar hs = composition.carrier().Hs(gid, T0);
447 td.cloud().rhoTrans(gid)[cellI] += dm;
448 td.cloud().UTrans()[cellI] += dm*U0;
449 td.cloud().hsTrans()[cellI] += dm*hs;
452 forAll(dMassSRCarrier, i)
454 scalar dm = np0*dMassSRCarrier[i];
455 scalar hs = composition.carrier().Hs(i, T0);
456 td.cloud().rhoTrans(i)[cellI] += dm;
457 td.cloud().UTrans()[cellI] += dm*U0;
458 td.cloud().hsTrans()[cellI] += dm*hs;
461 // Update momentum transfer
462 td.cloud().UTrans()[cellI] += np0*dUTrans;
463 td.cloud().UCoeff()[cellI] += np0*Spu;
465 // Update sensible enthalpy transfer
466 td.cloud().hsTrans()[cellI] += np0*dhsTrans;
467 td.cloud().hsCoeff()[cellI] += np0*Sph;
472 template<class ParcelType>
473 template<class TrackData>
474 void Foam::ReactingMultiphaseParcel<ParcelType>::calcDevolatilisation
483 const scalarField& YGasEff,
485 scalarField& dMassDV,
492 // Check that model is active, and that the parcel temperature is
493 // within necessary limits for devolatilisation to occur
496 !td.cloud().devolatilisation().active()
497 || T < td.cloud().constProps().Tvap()
503 typedef typename TrackData::cloudType::reactingCloudType reactingCloudType;
504 const CompositionModel<reactingCloudType>& composition =
505 td.cloud().composition();
508 // Total mass of volatiles evolved
509 td.cloud().devolatilisation().calculate
520 scalar dMassTot = sum(dMassDV);
522 td.cloud().addToMassDevolatilisation(this->nParticle_*dMassTot);
524 Sh -= dMassTot*td.cloud().constProps().LDevol()/dt;
526 // Update molar emissions
527 if (td.cloud().heatTransfer().BirdCorrection())
529 // Molar average molecular weight of carrier mix
531 max(SMALL, this->rhoc_*specie::RR*this->Tc_/this->pc_);
533 // Note: hardcoded gaseous diffusivities for now
534 // TODO: add to carrier thermo
535 const scalar beta = sqr(cbrt(15.0) + cbrt(15.0));
539 const label id = composition.localToGlobalCarrierId(GAS, i);
540 const scalar Cp = composition.carrier().Cp(id, Ts);
541 const scalar W = composition.carrier().W(id);
542 const scalar Ni = dMassDV[i]/(this->areaS(d)*dt*W);
544 // Dab calc'd using API vapour mass diffusivity function
546 3.6059e-3*(pow(1.8*Ts, 1.75))
547 *sqrt(1.0/W + 1.0/Wc)
552 Cs[id] += Ni*d/(2.0*Dab);
558 template<class ParcelType>
559 template<class TrackData>
560 void Foam::ReactingMultiphaseParcel<ParcelType>::calcSurfaceReactions
568 const bool canCombust,
570 const scalarField& YMix,
571 const scalarField& YGas,
572 const scalarField& YLiquid,
573 const scalarField& YSolid,
574 scalarField& dMassSRGas,
575 scalarField& dMassSRLiquid,
576 scalarField& dMassSRSolid,
577 scalarField& dMassSRCarrier,
582 // Check that model is active
583 if (!td.cloud().surfaceReaction().active() || !canCombust)
588 // Update surface reactions
589 const scalar hReaction = td.cloud().surfaceReaction().calculate
610 td.cloud().addToMassSurfaceReaction
613 *(sum(dMassSRGas) + sum(dMassSRLiquid) + sum(dMassSRSolid))
616 const scalar xsi = min(T/5000.0, 1.0);
618 (1.0 - xsi*xsi)*td.cloud().constProps().hRetentionCoeff();
620 Sh += coeff*hReaction/dt;
622 dhsTrans += (1.0 - coeff)*hReaction;
626 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
628 template<class ParcelType>
629 Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
631 const ReactingMultiphaseParcel<ParcelType>& p
636 YLiquid_(p.YLiquid_),
641 template<class ParcelType>
642 Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
644 const ReactingMultiphaseParcel<ParcelType>& p,
650 YLiquid_(p.YLiquid_),
655 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
657 #include "ReactingMultiphaseParcelIO.C"
659 // ************************************************************************* //