pyrolysisChemistryModel.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2013-2017 OpenFOAM Foundation
9 Copyright (C) 2019 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
30#include "solidReaction.H"
31#include "basicThermo.H"
32
33// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
34
35template<class CompType, class SolidThermo, class GasThermo>
38(
40)
41:
42 solidChemistryModel<CompType, SolidThermo>(thermo),
43 pyrolisisGases_(this->reactions_[0].gasSpecies()),
44 gasThermo_(pyrolisisGases_.size()),
45 nGases_(pyrolisisGases_.size()),
46 nSpecie_(this->Ys_.size() + nGases_),
47 RRg_(nGases_),
48 Ys0_(this->nSolids_),
49 cellCounter_(0)
50{
51 // create the fields for the chemistry sources
52 forAll(this->RRs_, fieldi)
53 {
54 IOobject header
55 (
56 this->Ys_[fieldi].name() + "0",
57 this->mesh().time().timeName(),
58 this->mesh(),
60 );
61
62 // check if field exists and can be read
63 if (header.typeHeaderOk<volScalarField>(true))
64 {
65 Ys0_.set
66 (
67 fieldi,
69 (
71 (
72 this->Ys_[fieldi].name() + "0",
73 this->mesh().time().timeName(),
74 this->mesh(),
77 ),
78 this->mesh()
79 )
80 );
81 }
82 else
83 {
84 volScalarField Y0Default
85 (
87 (
88 "Y0Default",
89 this->mesh().time().timeName(),
90 this->mesh(),
93 ),
94 this->mesh()
95 );
96
97 Ys0_.set
98 (
99 fieldi,
101 (
103 (
104 this->Ys_[fieldi].name() + "0",
105 this->mesh().time().timeName(),
106 this->mesh(),
109 ),
110 Y0Default
111 )
112 );
113
114 // Calculate initial values of Ysi0 = rho*delta*Yi
115 Ys0_[fieldi].primitiveFieldRef() =
116 this->solidThermo().rho()
117 *max(this->Ys_[fieldi], scalar(0.001))*this->mesh().V();
118 }
119 }
120
121 forAll(RRg_, fieldi)
122 {
123 RRg_.set
124 (
125 fieldi,
127 (
129 (
130 "RRg." + pyrolisisGases_[fieldi],
131 this->mesh().time().timeName(),
132 this->mesh(),
135 ),
136 this->mesh(),
138 )
139 );
140 }
141
142 forAll(gasThermo_, gasI)
143 {
145 this->mesh().template lookupObject<dictionary>
146 (
148 ).subDict(pyrolisisGases_[gasI]);
149
151 (
152 gasI,
153 new GasThermo(thermoDict)
154 );
155 }
156
157 Info<< "pyrolysisChemistryModel: " << nl;
158 Info<< indent << "Number of solids = " << this->nSolids_ << nl;
159 Info<< indent << "Number of gases = " << nGases_ << nl;
160 forAll(this->reactions_, i)
161 {
162 Info<< dynamic_cast<const solidReaction<SolidThermo>&>
163 (
164 this->reactions_[i]
165 ) << nl;
166 }
167}
168
169
170// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
171
172template<class CompType, class SolidThermo, class GasThermo>
175{}
176
177
178// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
179
180template<class CompType, class SolidThermo, class GasThermo>
183(
184 const scalarField& c,
185 const scalar T,
186 const scalar p,
187 const bool updateC0
188) const
189{
190 scalar pf, cf, pr, cr;
191 label lRef, rRef;
192
193 const label celli = cellCounter_;
194
195 scalarField om(nEqns(), Zero);
196
197 forAll(this->reactions_, i)
198 {
199 const Reaction<SolidThermo>& R = this->reactions_[i];
200
201 scalar omegai = omega
202 (
203 R, c, T, p, pf, cf, lRef, pr, cr, rRef
204 );
205 scalar rhoL = 0.0;
206 forAll(R.lhs(), s)
207 {
208 label si = R.lhs()[s].index;
209 om[si] -= omegai;
210 rhoL = this->solidThermo_[si].rho(p, T);
211 }
212 scalar sr = 0.0;
213 forAll(R.rhs(), s)
214 {
215 label si = R.rhs()[s].index;
216 scalar rhoR = this->solidThermo_[si].rho(p, T);
217 sr = rhoR/rhoL;
218 om[si] += sr*omegai;
219
220 if (updateC0)
221 {
222 Ys0_[si][celli] += sr*omegai;
223 }
224 }
225 forAll(R.grhs(), g)
226 {
227 label gi = R.grhs()[g].index;
228 om[gi + this->nSolids_] += (1.0 - sr)*omegai;
229 }
230 }
231
232 return om;
233}
234
235
236template<class CompType, class SolidThermo, class GasThermo>
237Foam::scalar
239(
241 const scalarField& c,
242 const scalar T,
243 const scalar p,
244 scalar& pf,
245 scalar& cf,
246 label& lRef,
247 scalar& pr,
248 scalar& cr,
249 label& rRef
250) const
251{
252 scalarField c1(nSpecie_, Zero);
253
254 label celli = cellCounter_;
255
256 for (label i=0; i<nSpecie_; i++)
257 {
258 c1[i] = max(0.0, c[i]);
259 }
260
261 scalar kf = R.kf(p, T, c1);
262
263 const label Nl = R.lhs().size();
264
265 for (label s=0; s<Nl; s++)
266 {
267 label si = R.lhs()[s].index;
268 const scalar exp = R.lhs()[s].exponent;
269
270 kf *=
271 pow(c1[si]/Ys0_[si][celli], exp)
272 *(Ys0_[si][celli]);
273 }
274
275 return kf;
276}
277
278
279template<class CompType, class SolidThermo, class GasThermo>
281omegaI
282(
283 const label index,
284 const scalarField& c,
285 const scalar T,
286 const scalar p,
287 scalar& pf,
288 scalar& cf,
289 label& lRef,
290 scalar& pr,
291 scalar& cr,
292 label& rRef
293) const
294{
295
296 const Reaction<SolidThermo>& R = this->reactions_[index];
297 scalar w = omega(R, c, T, p, pf, cf, lRef, pr, cr, rRef);
298 return(w);
299}
300
301
302template<class CompType, class SolidThermo, class GasThermo>
305(
306 const scalar time,
307 const scalarField &c,
308 scalarField& dcdt
309) const
310{
311 const scalar T = c[nSpecie_];
312 const scalar p = c[nSpecie_ + 1];
313
314 dcdt = 0.0;
315
316 dcdt = omega(c, T, p);
317
318 //Total mass concentration
319 scalar cTot = 0.0;
320 for (label i=0; i<this->nSolids_; i++)
321 {
322 cTot += c[i];
323 }
324
325 scalar newCp = 0.0;
326 scalar newhi = 0.0;
327 for (label i=0; i<this->nSolids_; i++)
328 {
329 scalar dYidt = dcdt[i]/cTot;
330 scalar Yi = c[i]/cTot;
331 newCp += Yi*this->solidThermo_[i].Cp(p, T);
332 newhi -= dYidt*this->solidThermo_[i].Hc();
333 }
334
335 scalar dTdt = newhi/newCp;
336 scalar dtMag = min(500.0, mag(dTdt));
337 dcdt[nSpecie_] = dTdt*dtMag/(mag(dTdt) + 1.0e-10);
338
339 // dp/dt = ...
340 dcdt[nSpecie_ + 1] = 0.0;
341}
342
343
344template<class CompType, class SolidThermo, class GasThermo>
347(
348 const scalar t,
349 const scalarField& c,
350 scalarField& dcdt,
352) const
353{
354 const scalar T = c[nSpecie_];
355 const scalar p = c[nSpecie_ + 1];
356
357 scalarField c2(nSpecie_, Zero);
358
359 for (label i=0; i<this->nSolids_; i++)
360 {
361 c2[i] = max(c[i], 0.0);
362 }
363
364 for (label i=0; i<nEqns(); i++)
365 {
366 for (label j=0; j<nEqns(); j++)
367 {
368 dfdc(i, j) = 0.0;
369 }
370 }
371
372 // length of the first argument must be nSolids
373 dcdt = omega(c2, T, p);
374
375 for (label ri=0; ri<this->reactions_.size(); ri++)
376 {
377 const Reaction<SolidThermo>& R = this->reactions_[ri];
378
379 scalar kf0 = R.kf(p, T, c2);
380
381 forAll(R.lhs(), j)
382 {
383 label sj = R.lhs()[j].index;
384 scalar kf = kf0;
385 forAll(R.lhs(), i)
386 {
387 label si = R.lhs()[i].index;
388 scalar exp = R.lhs()[i].exponent;
389 if (i == j)
390 {
391 if (exp < 1.0)
392 {
393 if (c2[si]>SMALL)
394 {
395 kf *= exp*pow(c2[si], exp - 1.0);
396 }
397 else
398 {
399 kf = 0.0;
400 }
401 }
402 else
403 {
404 kf *= exp*pow(c2[si], exp - 1.0);
405 }
406 }
407 else
408 {
409 Info<< "Solid reactions have only elements on slhs"
410 << endl;
411 kf = 0.0;
412 }
413 }
414
415 forAll(R.lhs(), i)
416 {
417 label si = R.lhs()[i].index;
418 dfdc[si][sj] -= kf;
419 }
420 forAll(R.rhs(), i)
421 {
422 label si = R.rhs()[i].index;
423 dfdc[si][sj] += kf;
424 }
425 }
426 }
427
428 // calculate the dcdT elements numerically
429 scalar delta = 1.0e-8;
430 scalarField dcdT0 = omega(c2, T - delta, p);
431 scalarField dcdT1 = omega(c2, T + delta, p);
432
433 for (label i=0; i<nEqns(); i++)
434 {
435 dfdc[i][nSpecie_] = 0.5*(dcdT1[i] - dcdT0[i])/delta;
436 }
437
438}
439
440
441template<class CompType, class SolidThermo, class GasThermo>
442Foam::label Foam::
444{
445 // nEqns = number of solids + gases + temperature + pressure
446 return (nSpecie_ + 2);
447}
448
449
450template<class CompType, class SolidThermo, class GasThermo>
452calculate()
453{
454 if (!this->chemistry_)
455 {
456 return;
457 }
458
459 const volScalarField rho
460 (
462 (
463 "rho",
464 this->time().timeName(),
465 this->mesh(),
468 false
469 ),
470 this->solidThermo().rho()
471 );
472
473 forAll(this->RRs_, i)
474 {
475 this->RRs_[i].field() = 0.0;
476 }
477
478 forAll(RRg_, i)
479 {
480 RRg_[i].field() = 0.0;
481 }
482
483 forAll(rho, celli)
484 {
485 cellCounter_ = celli;
486
487 const scalar delta = this->mesh().V()[celli];
488
489 if (this->reactingCells_[celli])
490 {
491 scalar rhoi = rho[celli];
492 scalar Ti = this->solidThermo().T()[celli];
493 scalar pi = this->solidThermo().p()[celli];
494
495 scalarField c(nSpecie_, Zero);
496 for (label i=0; i<this->nSolids_; i++)
497 {
498 c[i] = rhoi*this->Ys_[i][celli]*delta;
499 }
500
501 const scalarField dcdt = omega(c, Ti, pi, true);
502
503 forAll(this->RRs_, i)
504 {
505 this->RRs_[i][celli] = dcdt[i]/delta;
506 }
507
508 forAll(RRg_, i)
509 {
510 RRg_[i][celli] = dcdt[this->nSolids_ + i]/delta;
511 }
512 }
513 }
514}
515
516
517template<class CompType, class SolidThermo, class GasThermo>
518Foam::scalar
520(
521 const scalar deltaT
522)
523{
524 scalar deltaTMin = GREAT;
525
526 if (!this->chemistry_)
527 {
528 return deltaTMin;
529 }
530
531 const volScalarField rho
532 (
534 (
535 "rho",
536 this->time().timeName(),
537 this->mesh(),
540 false
541 ),
542 this->solidThermo().rho()
543 );
544
545 forAll(this->RRs_, i)
546 {
547 this->RRs_[i].field() = 0.0;
548 }
549 forAll(RRg_, i)
550 {
551 RRg_[i].field() = 0.0;
552 }
553
554 const scalarField& T = this->solidThermo().T();
555 const scalarField& p = this->solidThermo().p();
556
557 scalarField c(nSpecie_, Zero);
558 scalarField c0(nSpecie_, Zero);
559 scalarField dc(nSpecie_, Zero);
560 scalarField delta(this->mesh().V());
561
562 forAll(rho, celli)
563 {
564 if (this->reactingCells_[celli])
565 {
566 cellCounter_ = celli;
567
568 scalar rhoi = rho[celli];
569 scalar pi = p[celli];
570 scalar Ti = T[celli];
571
572 for (label i=0; i<this->nSolids_; i++)
573 {
574 c[i] = rhoi*this->Ys_[i][celli]*delta[celli];
575 }
576
577 c0 = c;
578
579 // Initialise time progress
580 scalar timeLeft = deltaT;
581
582 // calculate the chemical source terms
583 while (timeLeft > SMALL)
584 {
585 scalar dt = timeLeft;
586 this->solve(c, Ti, pi, dt, this->deltaTChem_[celli]);
587 timeLeft -= dt;
588 }
589
590 deltaTMin = min(this->deltaTChem_[celli], deltaTMin);
591 dc = c - c0;
592
593 forAll(this->RRs_, i)
594 {
595 this->RRs_[i][celli] = dc[i]/(deltaT*delta[celli]);
596 }
597
598 forAll(RRg_, i)
599 {
600 RRg_[i][celli] = dc[this->nSolids_ + i]/(deltaT*delta[celli]);
601 }
602
603 // Update Ys0_
604 dc = omega(c0, Ti, pi, true);
605 }
606 }
607
608 // Don't allow the time-step to change more than a factor of 2
609 deltaTMin = min(deltaTMin, 2*deltaT);
610
611 return deltaTMin;
612}
613
614
615template<class CompType, class SolidThermo,class GasThermo>
618(
619 const volScalarField& p,
620 const volScalarField& T,
621 const label index
622) const
623{
625 (
627 (
629 (
630 "Hs_" + pyrolisisGases_[index],
631 this->mesh_.time().timeName(),
632 this->mesh_,
635 false
636 ),
637 this->mesh_,
639 )
640 );
641
642 volScalarField::Internal& gasHs = tHs.ref();
643
644 const GasThermo& mixture = gasThermo_[index];
645
646 forAll(gasHs, celli)
647 {
648 gasHs[celli] = mixture.Hs(p[celli], T[celli]);
649 }
650
651 return tHs;
652}
653
654
655template<class CompType, class SolidThermo, class GasThermo>
657(
658 scalarField &c,
659 scalar& T,
660 scalar& p,
661 scalar& deltaT,
662 scalar& subDeltaT
663) const
664{
666}
667
668
669// ************************************************************************* //
scalar delta
#define R(A, B, C, D, E, F, K, M)
const uniformDimensionedVectorField & g
ReactionThermo reactionThermo
Thermo type.
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
bool typeHeaderOk(const bool checkType=true, const bool search=true, const bool verbose=true)
Read header (uses typeFilePath to find file) and check its info.
const T * set(const label i) const
Definition: PtrList.H:138
Simple extension of ReactionThermo to handle reaction kinetics in addition to the equilibrium thermod...
Definition: Reaction.H:73
virtual const volScalarField & T() const
Temperature [K].
Definition: basicThermo.C:622
virtual volScalarField & p()
Pressure [Pa].
Definition: basicThermo.C:610
static const word dictName
Definition: basicThermo.H:256
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
Definition: dictionary.C:460
const DimensionedField< scalar, volMesh > & V() const
Return cell volumes.
Pyrolysis chemistry model. It includes gas phase in the solid reaction.
PtrList< GasThermo > gasThermo_
Thermodynamic data of gases.
virtual tmp< volScalarField > gasHs(const volScalarField &p, const volScalarField &T, const label i) const
Return sensible enthalpy for gas i [J/Kg].
virtual void derivatives(const scalar t, const scalarField &c, scalarField &dcdt) const
Calculate the derivatives in dydx.
label nGases_
Number of gas species.
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.
virtual label nEqns() const
Number of ODE's to solve.
speciesTable pyrolisisGases_
List of gas species present in reaction system.
virtual ~pyrolysisChemistryModel()
Destructor.
PtrList< volScalarField > Ys0_
List of accumulative solid concentrations.
PtrList< volScalarField::Internal > RRg_
List of reaction rate per gas [kg/m3/s].
virtual void jacobian(const scalar t, const scalarField &c, scalarField &dcdt, scalarSquareMatrix &dfdc) const
Calculate the Jacobian of the system.
virtual void calculate()
Calculates the reaction rates.
const volScalarField & omega() const
Access functions.
Extends base solid chemistry model by adding a thermo package, and ODE functions.
PtrList< volScalarField > & Ys_
Reference to solid mass fractions.
const PtrList< Reaction< SolidThermo > > & reactions_
Reactions.
label nSolids_
Number of solid components.
PtrList< volScalarField::Internal > RRs_
List of reaction rate per solid [kg/m3/s].
Fundamental solid thermodynamic properties.
Definition: solidThermo.H:55
virtual tmp< volScalarField > rho() const
Density [kg/m^3].
Definition: solidThermo.C:121
Basic thermodynamics type based on the use of fitting functions for cp, h, s obtained from the templa...
A class for managing temporary objects.
Definition: tmp.H:65
T & ref() const
Definition: tmpI.H:227
volScalarField & p
const volScalarField & T
const dictionary & thermoDict
Definition: EEqn.H:16
dynamicFvMesh & mesh
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:517
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.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))
word timeName
Definition: getTimeIndex.H:3
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
dimensionedScalar exp(const dimensionedScalar &ds)
const dimensionSet dimEnergy
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:53
messageStream Info
Information stream (stdout output on master, null elsewhere)
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:342
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:60
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
const dimensionSet dimMass(1, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:51
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
CEqn solve()
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
optimisationManager & om
Definition: createFields.H:6