pyrolysisChemistryModel.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2013-2018 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
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.
15 
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
19  for more details.
20 
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/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
27 #include "SolidReaction.H"
28 #include "basicThermo.H"
29 
30 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
31 
32 template<class CompType, class SolidThermo, class GasThermo>
35 (
36  typename CompType::reactionThermo& thermo
37 )
38 :
40  pyrolisisGases_
41  (
42  dynamic_cast<const SolidReaction<SolidThermo>&>
43  (
44  this->reactions_[0]
45  ).gasSpecies()
46  ),
47  gasThermo_(pyrolisisGases_.size()),
48  nGases_(pyrolisisGases_.size()),
49  nSpecie_(this->Ys_.size() + nGases_),
50  RRg_(nGases_),
51  Ys0_(this->nSolids_),
52  cellCounter_(0)
53 {
54  // create the fields for the chemistry sources
55  forAll(this->RRs_, fieldi)
56  {
57  IOobject header
58  (
59  this->Ys_[fieldi].name() + "0",
60  this->mesh().time().timeName(),
61  this->mesh(),
62  IOobject::NO_READ
63  );
64 
65  // check if field exists and can be read
66  if (header.typeHeaderOk<volScalarField>(true))
67  {
68  Ys0_.set
69  (
70  fieldi,
71  new volScalarField
72  (
73  IOobject
74  (
75  this->Ys_[fieldi].name() + "0",
76  this->mesh().time().timeName(),
77  this->mesh(),
78  IOobject::MUST_READ,
79  IOobject::AUTO_WRITE
80  ),
81  this->mesh()
82  )
83  );
84  }
85  else
86  {
87  volScalarField Y0Default
88  (
89  IOobject
90  (
91  "Y0Default",
92  this->mesh().time().timeName(),
93  this->mesh(),
94  IOobject::MUST_READ,
95  IOobject::NO_WRITE
96  ),
97  this->mesh()
98  );
99 
100  Ys0_.set
101  (
102  fieldi,
103  new volScalarField
104  (
105  IOobject
106  (
107  this->Ys_[fieldi].name() + "0",
108  this->mesh().time().timeName(),
109  this->mesh(),
110  IOobject::NO_READ,
111  IOobject::AUTO_WRITE
112  ),
113  Y0Default
114  )
115  );
116 
117  // Calculate initial values of Ysi0 = rho*delta*Yi
118  Ys0_[fieldi].primitiveFieldRef() =
119  this->solidThermo().rho()
120  *max(this->Ys_[fieldi], scalar(0.001))*this->mesh().V();
121  }
122  }
123 
124  forAll(RRg_, fieldi)
125  {
126  RRg_.set
127  (
128  fieldi,
130  (
131  IOobject
132  (
133  "RRg." + pyrolisisGases_[fieldi],
134  this->mesh().time().timeName(),
135  this->mesh(),
136  IOobject::NO_READ,
137  IOobject::NO_WRITE
138  ),
139  this->mesh(),
141  )
142  );
143  }
144 
145  forAll(gasThermo_, gasI)
146  {
147  dictionary thermoDict =
148  this->mesh().template lookupObject<dictionary>
149  (
151  ).subDict(pyrolisisGases_[gasI]);
152 
153  gasThermo_.set
154  (
155  gasI,
156  new GasThermo(thermoDict)
157  );
158  }
159 
160  Info<< "pyrolysisChemistryModel: " << nl;
161  Info<< indent << "Number of solids = " << this->nSolids_ << nl;
162  Info<< indent << "Number of gases = " << nGases_ << nl;
163  forAll(this->reactions_, i)
164  {
165  Info<< dynamic_cast<const SolidReaction<SolidThermo>&>
166  (
167  this->reactions_[i]
168  ) << nl;
169  }
170 }
171 
172 
173 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
174 
175 template<class CompType, class SolidThermo, class GasThermo>
178 {}
179 
180 
181 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
182 
183 template<class CompType, class SolidThermo, class GasThermo>
186 (
187  const scalarField& c,
188  const scalar T,
189  const scalar p,
190  const bool updateC0
191 ) const
192 {
193  scalar pf, cf, pr, cr;
194  label lRef, rRef;
195 
196  const label celli = cellCounter_;
197 
198  scalarField om(nEqns(), 0.0);
199 
200  forAll(this->reactions_, i)
201  {
202  const SolidReaction<SolidThermo>& R =
203  dynamic_cast<const SolidReaction<SolidThermo>&>
204  (
205  this->reactions_[i]
206  );
207 
208  scalar omegai = omega
209  (
210  R, c, T, p, pf, cf, lRef, pr, cr, rRef
211  );
212  scalar rhoL = 0.0;
213  forAll(R.lhs(), s)
214  {
215  label si = R.lhs()[s].index;
216  om[si] -= omegai;
217  rhoL = this->solidThermo_[si].rho(p, T);
218  }
219  scalar sr = 0.0;
220  forAll(R.rhs(), s)
221  {
222  label si = R.rhs()[s].index;
223  scalar rhoR = this->solidThermo_[si].rho(p, T);
224  sr = rhoR/rhoL;
225  om[si] += sr*omegai;
226 
227  if (updateC0)
228  {
229  Ys0_[si][celli] += sr*omegai;
230  }
231  }
232  forAll(R.grhs(), g)
233  {
234  label gi = R.grhs()[g].index;
235  om[gi + this->nSolids_] += (1.0 - sr)*omegai;
236  }
237  }
238 
239  return om;
240 }
241 
242 
243 template<class CompType, class SolidThermo, class GasThermo>
244 Foam::scalar
246 (
247  const Reaction<SolidThermo>& R,
248  const scalarField& c,
249  const scalar T,
250  const scalar p,
251  scalar& pf,
252  scalar& cf,
253  label& lRef,
254  scalar& pr,
255  scalar& cr,
256  label& rRef
257 ) const
258 {
259  scalarField c1(nSpecie_, 0.0);
260 
261  label celli = cellCounter_;
262 
263  for (label i=0; i<nSpecie_; i++)
264  {
265  c1[i] = max(0.0, c[i]);
266  }
267 
268  scalar kf = R.kf(p, T, c1);
269 
270  const label Nl = R.lhs().size();
271 
272  for (label s=0; s<Nl; s++)
273  {
274  label si = R.lhs()[s].index;
275  const scalar exp = R.lhs()[si].exponent;
276 
277  kf *=
278  pow(c1[si]/Ys0_[si][celli], exp)
279  *(Ys0_[si][celli]);
280  }
281 
282  return kf;
283 }
284 
285 
286 template<class CompType, class SolidThermo, class GasThermo>
288 omegaI
289 (
290  const label index,
291  const scalarField& c,
292  const scalar T,
293  const scalar p,
294  scalar& pf,
295  scalar& cf,
296  label& lRef,
297  scalar& pr,
298  scalar& cr,
299  label& rRef
300 ) const
301 {
302 
303  const Reaction<SolidThermo>& R = this->reactions_[index];
304  scalar w = omega(R, c, T, p, pf, cf, lRef, pr, cr, rRef);
305  return(w);
306 }
307 
308 
309 template<class CompType, class SolidThermo, class GasThermo>
312 (
313  const scalar time,
314  const scalarField &c,
315  scalarField& dcdt
316 ) const
317 {
318  const scalar T = c[nSpecie_];
319  const scalar p = c[nSpecie_ + 1];
320 
321  dcdt = 0.0;
322 
323  dcdt = omega(c, T, p);
324 
325  // Total mass concentration
326  scalar cTot = 0.0;
327  for (label i=0; i<this->nSolids_; i++)
328  {
329  cTot += c[i];
330  }
331 
332  scalar newCp = 0.0;
333  scalar newhi = 0.0;
334  for (label i=0; i<this->nSolids_; i++)
335  {
336  scalar dYidt = dcdt[i]/cTot;
337  scalar Yi = c[i]/cTot;
338  newCp += Yi*this->solidThermo_[i].Cp(p, T);
339  newhi -= dYidt*this->solidThermo_[i].Hc();
340  }
341 
342  scalar dTdt = newhi/newCp;
343  scalar dtMag = min(500.0, mag(dTdt));
344  dcdt[nSpecie_] = dTdt*dtMag/(mag(dTdt) + 1.0e-10);
345 
346  // dp/dt = ...
347  dcdt[nSpecie_ + 1] = 0.0;
348 }
349 
350 
351 template<class CompType, class SolidThermo, class GasThermo>
353 jacobian
354 (
355  const scalar t,
356  const scalarField& c,
357  scalarField& dcdt,
358  scalarSquareMatrix& dfdc
359 ) const
360 {
361  const scalar T = c[nSpecie_];
362  const scalar p = c[nSpecie_ + 1];
363 
364  scalarField c2(nSpecie_, 0.0);
365 
366  for (label i=0; i<this->nSolids_; i++)
367  {
368  c2[i] = max(c[i], 0.0);
369  }
370 
371  for (label i=0; i<nEqns(); i++)
372  {
373  for (label j=0; j<nEqns(); j++)
374  {
375  dfdc(i, j) = 0.0;
376  }
377  }
378 
379  // length of the first argument must be nSolids
380  dcdt = omega(c2, T, p);
381 
382  for (label ri=0; ri<this->reactions_.size(); ri++)
383  {
384  const Reaction<SolidThermo>& R = this->reactions_[ri];
385 
386  scalar kf0 = R.kf(p, T, c2);
387 
388  forAll(R.lhs(), j)
389  {
390  label sj = R.lhs()[j].index;
391  scalar kf = kf0;
392  forAll(R.lhs(), i)
393  {
394  label si = R.lhs()[i].index;
395  scalar exp = R.lhs()[i].exponent;
396  if (i == j)
397  {
398  if (exp < 1.0)
399  {
400  if (c2[si] > small)
401  {
402  kf *= exp*pow(c2[si], exp - 1.0);
403  }
404  else
405  {
406  kf = 0.0;
407  }
408  }
409  else
410  {
411  kf *= exp*pow(c2[si], exp - 1.0);
412  }
413  }
414  else
415  {
416  Info<< "Solid reactions have only elements on slhs"
417  << endl;
418  kf = 0.0;
419  }
420  }
421 
422  forAll(R.lhs(), i)
423  {
424  label si = R.lhs()[i].index;
425  dfdc[si][sj] -= kf;
426  }
427  forAll(R.rhs(), i)
428  {
429  label si = R.rhs()[i].index;
430  dfdc[si][sj] += kf;
431  }
432  }
433  }
434 
435  // calculate the dcdT elements numerically
436  scalar delta = 1.0e-8;
437  scalarField dcdT0 = omega(c2, T - delta, p);
438  scalarField dcdT1 = omega(c2, T + delta, p);
439 
440  for (label i=0; i<nEqns(); i++)
441  {
442  dfdc[i][nSpecie_] = 0.5*(dcdT1[i] - dcdT0[i])/delta;
443  }
444 
445 }
446 
447 
448 template<class CompType, class SolidThermo, class GasThermo>
451 {
452  // nEqns = number of solids + gases + temperature + pressure
453  return (nSpecie_ + 2);
454 }
455 
456 
457 template<class CompType, class SolidThermo, class GasThermo>
460 {
461  if (!this->chemistry_)
462  {
463  return;
464  }
465 
466  const volScalarField rho
467  (
468  IOobject
469  (
470  "rho",
471  this->time().timeName(),
472  this->mesh(),
473  IOobject::NO_READ,
474  IOobject::NO_WRITE,
475  false
476  ),
477  this->solidThermo().rho()
478  );
479 
480  forAll(this->RRs_, i)
481  {
482  this->RRs_[i].field() = 0.0;
483  }
484 
485  forAll(RRg_, i)
486  {
487  RRg_[i].field() = 0.0;
488  }
489 
490  forAll(rho, celli)
491  {
492  cellCounter_ = celli;
493 
494  const scalar delta = this->mesh().V()[celli];
495 
496  if (this->reactingCells_[celli])
497  {
498  scalar rhoi = rho[celli];
499  scalar Ti = this->solidThermo().T()[celli];
500  scalar pi = this->solidThermo().p()[celli];
501 
502  scalarField c(nSpecie_, 0.0);
503  for (label i=0; i<this->nSolids_; i++)
504  {
505  c[i] = rhoi*this->Ys_[i][celli]*delta;
506  }
507 
508  const scalarField dcdt = omega(c, Ti, pi, true);
509 
510  forAll(this->RRs_, i)
511  {
512  this->RRs_[i][celli] = dcdt[i]/delta;
513  }
514 
515  forAll(RRg_, i)
516  {
517  RRg_[i][celli] = dcdt[this->nSolids_ + i]/delta;
518  }
519  }
520  }
521 }
522 
523 
524 template<class CompType, class SolidThermo, class GasThermo>
525 Foam::scalar
527 (
528  const scalar deltaT
529 )
530 {
531  scalar deltaTMin = great;
532 
533  if (!this->chemistry_)
534  {
535  return deltaTMin;
536  }
537 
538  const volScalarField rho
539  (
540  IOobject
541  (
542  "rho",
543  this->time().timeName(),
544  this->mesh(),
545  IOobject::NO_READ,
546  IOobject::NO_WRITE,
547  false
548  ),
549  this->solidThermo().rho()
550  );
551 
552  forAll(this->RRs_, i)
553  {
554  this->RRs_[i].field() = 0.0;
555  }
556  forAll(RRg_, i)
557  {
558  RRg_[i].field() = 0.0;
559  }
560 
561  const scalarField& T = this->solidThermo().T();
562  const scalarField& p = this->solidThermo().p();
563 
564  scalarField c(nSpecie_, 0.0);
565  scalarField c0(nSpecie_, 0.0);
566  scalarField dc(nSpecie_, 0.0);
567  scalarField delta(this->mesh().V());
568 
569  forAll(rho, celli)
570  {
571  if (this->reactingCells_[celli])
572  {
573  cellCounter_ = celli;
574 
575  scalar rhoi = rho[celli];
576  scalar pi = p[celli];
577  scalar Ti = T[celli];
578 
579  for (label i=0; i<this->nSolids_; i++)
580  {
581  c[i] = rhoi*this->Ys_[i][celli]*delta[celli];
582  }
583 
584  c0 = c;
585 
586  // Initialise time progress
587  scalar timeLeft = deltaT;
588 
589  // calculate the chemical source terms
590  while (timeLeft > small)
591  {
592  scalar dt = timeLeft;
593  this->solve(c, Ti, pi, dt, this->deltaTChem_[celli]);
594  timeLeft -= dt;
595  }
596 
597  deltaTMin = min(this->deltaTChem_[celli], deltaTMin);
598  dc = c - c0;
599 
600  forAll(this->RRs_, i)
601  {
602  this->RRs_[i][celli] = dc[i]/(deltaT*delta[celli]);
603  }
604 
605  forAll(RRg_, i)
606  {
607  RRg_[i][celli] = dc[this->nSolids_ + i]/(deltaT*delta[celli]);
608  }
609 
610  // Update Ys0_
611  dc = omega(c0, Ti, pi, true);
612  }
613  }
614 
615  // Don't allow the time-step to change more than a factor of 2
616  deltaTMin = min(deltaTMin, 2*deltaT);
617 
618  return deltaTMin;
619 }
620 
621 
622 template<class CompType, class SolidThermo,class GasThermo>
625 (
626  const volScalarField& p,
627  const volScalarField& T,
628  const label index
629 ) const
630 {
632  (
633  new volScalarField
634  (
635  IOobject
636  (
637  "Hs_" + pyrolisisGases_[index],
638  this->mesh_.time().timeName(),
639  this->mesh_,
640  IOobject::NO_READ,
641  IOobject::NO_WRITE,
642  false
643  ),
644  this->mesh_,
645  dimensionedScalar("zero", dimEnergy/dimMass, 0.0)
646  )
647  );
648 
649  volScalarField::Internal& gasHs = tHs.ref();
650 
651  const GasThermo& mixture = gasThermo_[index];
652 
653  forAll(gasHs, celli)
654  {
655  gasHs[celli] = mixture.Hs(p[celli], T[celli]);
656  }
657 
658  return tHs;
659 }
660 
661 
662 template<class CompType, class SolidThermo, class GasThermo>
664 (
665  scalarField &c,
666  scalar& T,
667  scalar& p,
668  scalar& deltaT,
669  scalar& subDeltaT
670 ) const
671 {
673 }
674 
675 
676 // ************************************************************************* //
scalar delta
virtual ~pyrolysisChemistryModel()
Destructor.
virtual const speciesTable & gasSpecies() const
Access to gas specie list.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
bool typeHeaderOk(const bool checkType=true)
Read header (uses typeFilePath to find file) and check header.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:226
const List< specieCoeffs > & lhs() const
Return the components of the left hand side.
Definition: ReactionI.H:58
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
virtual scalar omegaI(label iReaction, const scalarField &c, const scalar T, const scalar p, scalar &pf, scalar &cf, label &lRef, scalar &pr, scalar &cr, label &rRef) const
Return the reaction rate for iReaction.
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:174
virtual tmp< volScalarField > rho() const
Density [kg/m^3].
Definition: solidThermo.C:120
const dimensionedScalar c2
Second radiation constant: default SI units: [m.K].
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:256
virtual void derivatives(const scalar t, const scalarField &c, scalarField &dcdt) const
Calculate the derivatives in dydx.
virtual const volScalarField & T() const
Temperature [K].
Definition: basicThermo.C:499
rhoReactionThermo & thermo
Definition: createFields.H:28
Extends base solid chemistry model by adding a thermo package, and ODE functions. Introduces chemistr...
Simple extension of ReactionThermo to handle reaction kinetics in addition to the equilibrium thermod...
Definition: Reaction.H:55
virtual volScalarField & p()
Pressure [Pa].
Definition: basicThermo.C:487
virtual const List< specieCoeffs > & grhs() const
Access to the gas components of the left hand side.
Definition: SolidReaction.C:96
virtual scalarField omega(const scalarField &c, const scalar T, const scalar p, const bool updateC0=false) const
dc/dt = omega, rate of change in concentration, for each species
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:58
virtual label nEqns() const
Number of ODE&#39;s to solve.
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
dynamicFvMesh & mesh
dimensionedScalar exp(const dimensionedScalar &ds)
virtual tmp< volScalarField > gasHs(const volScalarField &p, const volScalarField &T, const label i) const
Return sensible enthalpy for gas i [J/Kg].
virtual scalar solve(const scalar deltaT)
Solve the reaction system for the given time step.
rhoEqn solve()
word timeName
Definition: getTimeIndex.H:3
const word dictName("particleTrackDict")
static const char nl
Definition: Ostream.H:265
Fundamental solid thermodynamic properties.
Definition: solidThermo.H:48
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
const dimensionSet dimEnergy
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
pyrolysisChemistryModel(typename CompType::reactionThermo &thermo)
Construct from thermo.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
const dimensionedScalar c
Speed of light in a vacuum.
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Read solid reactions of the type S1 = S2 + G1.
Definition: SolidReaction.H:49
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:51
const dimensionedScalar c1
First radiation constant: default SI units: [W/m2].
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
const dimensionSet dimMass(1, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:49
A class for managing temporary objects.
Definition: PtrList.H:53
const dimensionedVector & g
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:366
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:92
virtual void calculate()
Calculates the reaction rates.
virtual void jacobian(const scalar t, const scalarField &c, scalarField &dcdt, scalarSquareMatrix &dfdc) const
Calculate the Jacobian of the system.
virtual scalar kf(const scalar p, const scalar T, const scalarField &c) const =0
Forward rate constant.
const List< specieCoeffs > & rhs() const
Return the components of the right hand side.
Definition: ReactionI.H:66