1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd.
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"
29 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
31 template<class ParcelType>
32 template<class TrackData>
33 void Foam::ReactingParcel<ParcelType>::updateCellQuantities
40 ThermoParcel<ParcelType>::updateCellQuantities(td, dt, celli);
42 pc_ = td.pInterp().interpolate(this->position(), celli);
46 template<class ParcelType>
47 template<class TrackData>
48 void Foam::ReactingParcel<ParcelType>::calcCoupled
55 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56 // Define local properties at beginning of timestep
57 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58 const vector U0 = this->U_;
59 const scalar mass0 = this->mass();
60 const scalar np0 = this->nParticle_;
61 const scalar T0 = this->T_;
63 // ~~~~~~~~~~~~~~~~~~~~~~~~~
64 // Initialise transfer terms
65 // ~~~~~~~~~~~~~~~~~~~~~~~~~
67 // Momentum transfer from the particle to the carrier phase
68 vector dUTrans = vector::zero;
70 // Enthalpy transfer from the particle to the carrier phase
73 // Mass transfer from particle to carrier phase
74 // - components exist in particle already
75 scalarList dMassMT(td.cloud().gases().size(), 0.0);
77 // Mass transfer due to surface reactions from particle to carrier phase
78 // - components do not necessarily exist in particle already
79 scalarList dMassSR(td.cloud().gases().size(), 0.0);
82 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83 // Calculate velocity - update U
84 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
86 const vector U1 = calcVelocity(td, dt, Cud, dUTrans);
89 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90 // Calculate heat transfer - update T
91 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93 scalar T1 = calcHeatTransfer(td, dt, celli, htc, dhTrans);
96 // ~~~~~~~~~~~~~~~~~~~~~~~
97 // Calculate mass transfer
98 // ~~~~~~~~~~~~~~~~~~~~~~~
99 calcMassTransfer(td, dt, T0, T1, dMassMT);
102 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
103 // Calculate surface reactions
104 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
106 // Initialise enthalpy retention to zero
109 calcSurfaceReactions(td, dt, celli, T0, T1, dMassMT, dMassSR, dhRet);
112 const scalar mass1 = mass0 - sum(dMassMT) - sum(dMassSR);
114 // Correct particle temperature to account for latent heat of
117 td.constProps().Ldevol()
119 /(0.5*(mass0 + mass1)*this->cp_);
121 // Add retained enthalpy from surface reaction to particle and remove
123 T1 += dhRet/(0.5*(mass0 + mass1)*this->cp_);
126 // Correct dhTrans to account for enthalpy of evolved volatiles
129 *td.cloud().composition().HGas(YGas_, 0.5*(T0 + T1));
131 // Correct dhTrans to account for enthalpy of consumed solids
134 *td.cloud().composition().HSolid(YSolid_, 0.5*(T0 + T1));
137 // ~~~~~~~~~~~~~~~~~~~~~~~
138 // Accumulate source terms
139 // ~~~~~~~~~~~~~~~~~~~~~~~
141 // Transfer mass lost from particle to carrier mass source
144 td.cloud().rhoTrans(i)[celli] += np0*(dMassMT[i] + dMassSR[i]);
147 // Update momentum transfer
148 td.cloud().UTrans()[celli] += np0*dUTrans;
150 // Accumulate coefficient to be applied in carrier phase momentum coupling
151 td.cloud().UCoeff()[celli] += np0*mass0*Cud;
153 // Update enthalpy transfer
154 // - enthalpy of lost solids already accounted for
155 td.cloud().hTrans()[celli] += np0*dhTrans;
157 // Accumulate coefficient to be applied in carrier phase enthalpy coupling
158 td.cloud().hCoeff()[celli] += np0*htc*this->areaS();
161 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162 // Remove the particle when mass falls below minimum threshold
163 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164 if (mass1 < td.constProps().minParticleMass())
166 td.keepParticle = false;
168 // Absorb particle(s) into carrier phase
171 td.cloud().rhoTrans(i)[celli] += np0*dMassMT[i];
173 td.cloud().hTrans()[celli] +=
176 YMixture_[0]*td.cloud().composition().HGas(YGas_, T1)
177 + YMixture_[2]*td.cloud().composition().HSolid(YSolid_, T1)
179 td.cloud().UTrans()[celli] += np0*mass1*U1;
181 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
182 // Set new particle properties
183 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
189 YMixture_[0]*td.cloud().composition().cpGas(YGas_, T1)
190 + YMixture_[1]*td.cloud().composition().cpLiquid(YLiquid_, pc_, T1)
191 + YMixture_[2]*td.cloud().composition().cpSolid(YSolid_);
193 // Update particle density or diameter
194 if (td.cloud().massTransfer().changesVolume())
196 this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
200 this->rho_ = mass1/this->volume();
206 template<class ParcelType>
207 template<class TrackData>
208 void Foam::ReactingParcel<ParcelType>::calcUncoupled
215 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216 // Define local properties at beginning of timestep
217 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
218 const scalar T0 = this->T_;
219 const scalar mass0 = this->mass();
220 const scalar cp0 = this->cp_;
222 // ~~~~~~~~~~~~~~~~~~~~~~~~~
223 // Initialise transfer terms
224 // ~~~~~~~~~~~~~~~~~~~~~~~~~
226 // Momentum transfer from the particle to the carrier phase
227 vector dUTrans = vector::zero;
229 // Enthalpy transfer from the particle to the carrier phase
230 scalar dhTrans = 0.0;
232 // Mass transfer from particle to carrier phase
233 // - components exist in particle already
234 scalarList dMassMT(td.cloud().gases().size(), 0.0);
236 // Mass transfer due to surface reactions from particle to carrier phase
237 // - components do not necessarily exist in particle already
238 scalarList dMassSR(td.cloud().gases().size(), 0.0);
241 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
242 // Calculate velocity - update U
243 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
245 const vector U1 = calcVelocity(td, dt, Cud, dUTrans);
248 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
249 // Calculate heat transfer - update T
250 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
252 scalar T1 = calcHeatTransfer(td, dt, celli, htc, dhTrans);
255 // ~~~~~~~~~~~~~~~~~~~~~~~
256 // Calculate mass transfer
257 // ~~~~~~~~~~~~~~~~~~~~~~~
258 calcMassTransfer(td, dt, T0, T1, dMassMT);
261 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
262 // Calculate surface reactions
263 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
265 // Initialise enthalpy retention to zero
268 calcSurfaceReactions(td, dt, celli, T0, T1, dMassMT, dMassSR, dhRet);
271 const scalar mass1 = mass0 - sum(dMassMT) - sum(dMassSR);
273 // New specific heat capacity
275 YMixture_[0]*td.cloud().composition().cpGas(YGas_, T1)
276 + YMixture_[1]*td.cloud().composition().cpLiquid(YLiquid_, pc_, T1)
277 + YMixture_[2]*td.cloud().composition().cpSolid(YSolid_);
279 // Add retained enthalpy to particle
280 T1 += dhRet/(mass0*0.5*(cp0 + cp1));
282 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
283 // Remove the particle when mass falls below minimum threshold
284 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
285 if (mass1 < td.constProps().minParticleMass())
287 td.keepParticle = false;
289 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
290 // Set new particle properties
291 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
298 // Update particle density or diameter
299 if (td.cloud().massTransfer().changesVolume())
301 this->d_ = cbrt(mass1/this->rho_*6.0/mathematicalConstant::pi);
305 this->rho_ = mass1/this->volume();
311 template<class ParcelType>
312 template<class TrackData>
313 void Foam::ReactingParcel<ParcelType>::calcMassTransfer
322 if (td.cloud().composition().YMixture0()[1]>SMALL)
326 "void Foam::ReactingParcel<ParcelType>::"
327 "calcMassTransfer(...): no treatment currently "
328 "available for particles containing liquid species"
332 // Check that model is active, and that the parcel temperature is
333 // within necessary limits for mass transfer to occur
336 !td.cloud().massTransfer().active()
337 || this->T_<td.constProps().Tvap()
338 || this->T_<td.constProps().Tbp()
344 // Determine mass to add to carrier phase
345 const scalar mass = this->mass();
346 const scalar dMassTot = td.cloud().massTransfer().calculate
351 td.cloud().composition().YMixture0(),
357 // Update (total) mass fractions
358 YMixture_[0] = (YMixture_[0]*mass - dMassTot)/(mass - dMassTot);
359 YMixture_[1] = YMixture_[1]*mass/(mass - dMassTot);
360 YMixture_[2] = 1.0 - YMixture_[0] - YMixture_[1];
362 // Add to cummulative mass transfer
365 label id = td.cloud().composition().gasGlobalIds()[i];
368 scalar volatileMass = YGas_[i]*dMassTot;
369 dMassMT[id] += volatileMass;
374 template<class ParcelType>
375 template<class TrackData>
376 void Foam::ReactingParcel<ParcelType>::calcSurfaceReactions
383 const scalarList& dMassMT,
388 // Check that model is active
389 if (!td.cloud().surfaceReaction().active() || !canCombust_)
394 // Update mass transfer(s)
395 // - Also updates Y()'s
396 td.cloud().surfaceReaction().calculate
418 // * * * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * //
420 #include "ReactingParcelIO.C"
422 // ************************************************************************* //