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 "ReactingParcel.H"
28 #include "mathematicalConstants.H"
31 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
33 template<class ParcelType>
34 template<class TrackData>
35 void Foam::ReactingParcel<ParcelType>::setCellValues
42 ThermoParcel<ParcelType>::setCellValues(td, dt, cellI);
44 pc_ = td.pInterp().interpolate(this->position(), cellI);
45 if (pc_ < td.constProps().pMin())
49 "void Foam::ReactingParcel<ParcelType>::setCellValues"
55 ) << "Limiting observed pressure in cell " << cellI << " to "
56 << td.constProps().pMin() << nl << endl;
58 pc_ = td.constProps().pMin();
63 template<class ParcelType>
64 template<class TrackData>
65 void Foam::ReactingParcel<ParcelType>::cellValueSourceCorrection
72 scalar massCell = this->massCell(cellI);
74 scalar addedMass = 0.0;
75 forAll(td.cloud().rhoTrans(), i)
77 addedMass += td.cloud().rhoTrans(i)[cellI];
80 this->rhoc_ += addedMass/td.cloud().pMesh().cellVolumes()[cellI];
82 scalar massCellNew = massCell + addedMass;
83 this->Uc_ += td.cloud().UTrans()[cellI]/massCellNew;
86 if (addedMass > ROOTVSMALL)
88 forAll(td.cloud().rhoTrans(), i)
90 scalar Y = td.cloud().rhoTrans(i)[cellI]/addedMass;
92 Y*td.cloud().mcCarrierThermo().speciesData()[i].Cp(this->Tc_);
95 const scalar cpc = td.cpInterp().psi()[cellI];
96 this->cpc_ = (massCell*cpc + addedMass*cpEff)/massCellNew;
98 this->Tc_ += td.cloud().hsTrans()[cellI]/(this->cpc_*massCellNew);
102 template<class ParcelType>
103 template<class TrackData>
104 void Foam::ReactingParcel<ParcelType>::correctSurfaceValues
109 const scalarField& Cs,
116 // No correction if total concentration of emitted species is small
122 // Far field carrier molar fractions
123 scalarField Xinf(td.cloud().mcCarrierThermo().speciesData().size());
128 td.cloud().mcCarrierThermo().Y(i)[cellI]
129 /td.cloud().mcCarrierThermo().speciesData()[i].W();
133 // Molar fraction of far field species at particle surface
134 const scalar Xsff = 1.0 - min(sum(Cs)*specie::RR*this->T_/pc_, 1.0);
136 // Surface carrier total molar concentration
137 const scalar CsTot = pc_/(specie::RR*this->T_);
139 // Surface carrier composition (molar fraction)
140 scalarField Xs(Xinf.size());
142 // Surface carrier composition (mass fraction)
143 scalarField Ys(Xinf.size());
147 // Molar concentration of species at particle surface
148 const scalar Csi = Cs[i] + Xsff*Xinf[i]*CsTot;
150 Xs[i] = (2.0*Csi + Xinf[i]*CsTot)/3.0;
151 Ys[i] = Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].W();
161 scalar sumYiSqrtW = 0;
162 scalar sumYiCbrtW = 0;
167 sqrt(td.cloud().mcCarrierThermo().speciesData()[i].W());
169 cbrt(td.cloud().mcCarrierThermo().speciesData()[i].W());
171 rhos += Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].W();
172 mus += Ys[i]*sqrtW*td.cloud().mcCarrierThermo().speciesData()[i].mu(T);
174 Ys[i]*cbrtW*td.cloud().mcCarrierThermo().speciesData()[i].kappa(T);
175 cps += Xs[i]*td.cloud().mcCarrierThermo().speciesData()[i].Cp(T);
177 sumYiSqrtW += Ys[i]*sqrtW;
178 sumYiCbrtW += Ys[i]*cbrtW;
181 rhos *= pc_/(specie::RR*T);
188 template<class ParcelType>
189 Foam::scalar Foam::ReactingParcel<ParcelType>::updateMassFraction
192 const scalarField& dMass,
196 scalar mass1 = mass0 - sum(dMass);
198 // only update the mass fractions if the new particle mass is finite
199 if (mass1 > ROOTVSMALL)
203 Y[i] = (Y[i]*mass0 - dMass[i])/mass1;
211 template<class ParcelType>
212 template<class TrackData>
213 void Foam::ReactingParcel<ParcelType>::calc
220 // Define local properties at beginning of time step
221 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222 const scalar np0 = this->nParticle_;
223 const scalar d0 = this->d_;
224 const vector& U0 = this->U_;
225 const scalar rho0 = this->rho_;
226 const scalar T0 = this->T_;
227 const scalar cp0 = this->cp_;
228 const scalar mass0 = this->mass();
231 // Calc surface values
232 // ~~~~~~~~~~~~~~~~~~~
233 scalar Ts, rhos, mus, Pr, kappa;
234 this->calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Pr, kappa);
237 scalar Re = this->Re(U0, d0, rhos, mus);
243 // Explicit momentum source for particle
244 vector Su = vector::zero;
246 // Momentum transfer from the particle to the carrier phase
247 vector dUTrans = vector::zero;
249 // Explicit enthalpy source for particle
252 // Sensible enthalpy transfer from the particle to the carrier phase
253 scalar dhsTrans = 0.0;
259 // Mass transfer due to phase change
260 scalarField dMassPC(Y_.size(), 0.0);
262 // Molar flux of species emitted from the particle (kmol/m^2/s)
265 // Sum Ni*Cpi*Wi of emission species
268 // Surface concentrations of emitted species
269 scalarField Cs(td.cloud().mcCarrierThermo().species().size(), 0.0);
271 // Calc mass and enthalpy transfer due to phase change
293 // Correct surface values due to emitted species
294 correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Pr, kappa);
296 // Update particle component mass and mass fractions
297 scalar mass1 = updateMassFraction(mass0, dMassPC, Y_);
303 // Calculate new particle temperature
326 // Calculate new particle velocity
328 calcVelocity(td, dt, cellI, Re, mus, d0, U0, rho0, mass0, Su, dUTrans);
330 dUTrans += 0.5*(mass0 - mass1)*(U0 + U1);
332 // Accumulate carrier phase source terms
333 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334 if (td.cloud().coupled())
336 // Transfer mass lost from particle to carrier mass source
339 label gid = td.cloud().composition().localToGlobalCarrierId(0, i);
340 td.cloud().rhoTrans(gid)[cellI] += np0*dMassPC[i];
343 // Update momentum transfer
344 td.cloud().UTrans()[cellI] += np0*dUTrans;
346 // Update sensible enthalpy transfer
347 td.cloud().hsTrans()[cellI] += np0*dhsTrans;
351 // Remove the particle when mass falls below minimum threshold
352 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
353 if (mass1 < td.constProps().minParticleMass())
355 td.keepParticle = false;
357 if (td.cloud().coupled())
359 // Absorb parcel into carrier phase
363 td.cloud().composition().localToGlobalCarrierId(0, i);
364 td.cloud().rhoTrans(gid)[cellI] += np0*mass1*Y_[i];
366 td.cloud().UTrans()[cellI] += np0*mass1*U1;
367 td.cloud().hsTrans()[cellI] +=
368 np0*mass1*td.cloud().composition().H(0, Y_, pc_, T1);
373 // Set new particle properties
374 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
378 this->cp_ = td.cloud().composition().cp(0, Y_, pc_, T1);
382 // Update particle density or diameter
383 if (td.constProps().constantVolume())
385 this->rho_ = mass1/this->volume();
389 this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
395 template<class ParcelType>
396 template<class TrackData>
397 void Foam::ReactingParcel<ParcelType>::calcPhaseChange
410 const scalarField& YComponents,
411 scalarField& dMassPC,
418 typedef PhaseChangeModel
420 typename ReactingParcel<ParcelType>::trackData::cloudType
421 > phaseChangeModelType;
425 !td.cloud().phaseChange().active()
426 || T < td.constProps().Tvap()
433 // Calculate mass transfer due to phase change
434 td.cloud().phaseChange().calculate
447 // Limit phase change mass by availability of each specie
448 dMassPC = min(mass*YPhase*YComponents, dMassPC);
450 scalar dMassTot = sum(dMassPC);
452 // Add to cumulative phase change mass
453 td.cloud().addToMassPhaseChange(this->nParticle_*dMassTot);
455 // Average molecular weight of carrier mix - assumes perfect gas
456 scalar Wc = this->rhoc_*specie::RR*this->Tc_/this->pc_;
458 forAll(YComponents, i)
461 td.cloud().composition().localToGlobalCarrierId(idPhase, i);
462 const label idl = td.cloud().composition().globalIds(idPhase)[i];
464 // Calculate enthalpy transfer
467 td.cloud().phaseChange().enthalpyTransfer()
468 == phaseChangeModelType::etLatentHeat
472 td.cloud().composition().liquids().properties()[idl].hl(pc_, T);
474 Sh -= dMassPC[i]*hlp/dt;
478 // Note: enthalpies of both phases must use the same reference
479 scalar hc = td.cloud().mcCarrierThermo().speciesData()[idc].H(T);
481 td.cloud().composition().liquids().properties()[idl].h(pc_, T);
483 Sh -= dMassPC[i]*(hc - hp)/dt;
486 // Update particle surface thermo properties
488 td.cloud().composition().liquids().properties()[idl].D(pc_, Ts, Wc);
491 td.cloud().mcCarrierThermo().speciesData()[idc].Cp(Ts);
492 const scalar W = td.cloud().mcCarrierThermo().speciesData()[idc].W();
493 const scalar Ni = dMassPC[i]/(this->areaS(d)*dt*W);
495 // Molar flux of species coming from the particle (kmol/m^2/s)
498 // Sum of Ni*Cpi*Wi of emission species
501 // Concentrations of emission species
502 Cs[idc] += Ni*d/(2.0*Dab);
507 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
509 template <class ParcelType>
510 Foam::ReactingParcel<ParcelType>::ReactingParcel
512 const ReactingParcel<ParcelType>& p
516 ThermoParcel<ParcelType>(p),
523 // * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
525 #include "ReactingParcelIO.C"
527 // ************************************************************************* //