1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2009-2009 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 "ODEChemistryModel.H"
28 #include "chemistrySolver.H"
29 #include "reactingMixture.H"
31 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
33 template<class CompType, class ThermoType>
34 Foam::ODEChemistryModel<CompType, ThermoType>::ODEChemistryModel
37 const word& compTypeName,
38 const word& thermoTypeName
41 CompType(mesh, thermoTypeName),
45 Y_(this->thermo().composition().Y()),
49 dynamic_cast<const reactingMixture<ThermoType>&>(this->thermo())
53 dynamic_cast<const reactingMixture<ThermoType>&>
54 (this->thermo()).speciesData()
58 nReaction_(reactions_.size()),
62 chemistrySolver<CompType, ThermoType>::New
72 // create the fields for the chemistry sources
78 new scalarField(mesh.nCells(), 0.0)
82 Info<< "ODEChemistryModel: Number of species = " << nSpecie_
83 << " and reactions = " << nReaction_ << endl;
87 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
89 template<class CompType, class ThermoType>
90 Foam::ODEChemistryModel<CompType, ThermoType>::~ODEChemistryModel()
94 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
96 template<class CompType, class ThermoType>
97 Foam::scalarField Foam::ODEChemistryModel<CompType, ThermoType>::omega
104 scalar pf, cf, pr, cr;
107 scalarField om(nEqns(), 0.0);
109 forAll(reactions_, i)
111 const Reaction<ThermoType>& R = reactions_[i];
113 scalar omegai = omega
115 R, c, T, p, pf, cf, lRef, pr, cr, rRef
120 label si = R.lhs()[s].index;
121 scalar sl = R.lhs()[s].stoichCoeff;
127 label si = R.rhs()[s].index;
128 scalar sr = R.rhs()[s].stoichCoeff;
137 template<class CompType, class ThermoType>
138 Foam::scalar Foam::ODEChemistryModel<CompType, ThermoType>::omega
140 const Reaction<ThermoType>& R,
141 const scalarField& c,
152 scalarField c2(nSpecie_, 0.0);
153 for (label i=0; i<nSpecie_; i++)
155 c2[i] = max(0.0, c[i]);
158 scalar kf = R.kf(T, p, c2);
159 scalar kr = R.kr(kf, T, p, c2);
164 label Nl = R.lhs().size();
165 label Nr = R.rhs().size();
168 lRef = R.lhs()[slRef].index;
171 for (label s=1; s<Nl; s++)
173 label si = R.lhs()[s].index;
177 scalar exp = R.lhs()[slRef].exponent;
178 pf *= pow(max(0.0, c[lRef]), exp);
184 scalar exp = R.lhs()[s].exponent;
185 pf *= pow(max(0.0, c[si]), exp);
188 cf = max(0.0, c[lRef]);
191 scalar exp = R.lhs()[slRef].exponent;
196 pf *= pow(cf, exp - 1.0);
205 pf *= pow(cf, exp - 1.0);
210 rRef = R.rhs()[srRef].index;
212 // find the matrix element and element position for the rhs
214 for (label s=1; s<Nr; s++)
216 label si = R.rhs()[s].index;
219 scalar exp = R.rhs()[srRef].exponent;
220 pr *= pow(max(0.0, c[rRef]), exp);
226 scalar exp = R.rhs()[s].exponent;
227 pr *= pow(max(0.0, c[si]), exp);
230 cr = max(0.0, c[rRef]);
233 scalar exp = R.rhs()[srRef].exponent;
238 pr *= pow(cr, exp - 1.0);
247 pr *= pow(cr, exp - 1.0);
251 return pf*cf - pr*cr;
255 template<class CompType, class ThermoType>
256 void Foam::ODEChemistryModel<CompType, ThermoType>::derivatives
259 const scalarField &c,
263 scalar T = c[nSpecie_];
264 scalar p = c[nSpecie_ + 1];
266 dcdt = omega(c, T, p);
272 for (label i=0; i<nSpecie_; i++)
274 scalar W = specieThermo_[i].W();
278 scalar mw = rho/cSum;
280 for (label i=0; i<nSpecie_; i++)
282 scalar cpi = specieThermo_[i].cp(T);
283 scalar Xi = c[i]/rho;
289 for (label i=0; i<nSpecie_; i++)
291 scalar hi = specieThermo_[i].h(T);
296 // limit the time-derivative, this is more stable for the ODE
297 // solver when calculating the allowed time step
298 scalar dtMag = min(500.0, mag(dT));
299 dcdt[nSpecie_] = -dT*dtMag/(mag(dT) + 1.0e-10);
302 dcdt[nSpecie_+1] = 0.0;
306 template<class CompType, class ThermoType>
307 void Foam::ODEChemistryModel<CompType, ThermoType>::jacobian
310 const scalarField& c,
312 scalarSquareMatrix& dfdc
315 scalar T = c[nSpecie_];
316 scalar p = c[nSpecie_ + 1];
318 scalarField c2(nSpecie_, 0.0);
319 for (label i=0; i<nSpecie_; i++)
321 c2[i] = max(c[i], 0.0);
324 for (label i=0; i<nEqns(); i++)
326 for (label j=0; j<nEqns(); j++)
332 // length of the first argument must be nSpecie()
333 dcdt = omega(c2, T, p);
335 for (label ri=0; ri<reactions_.size(); ri++)
337 const Reaction<ThermoType>& R = reactions_[ri];
339 scalar kf0 = R.kf(T, p, c2);
340 scalar kr0 = R.kr(T, p, c2);
344 label sj = R.lhs()[j].index;
348 label si = R.lhs()[i].index;
349 scalar el = R.lhs()[i].exponent;
356 kf *= el*pow(c2[si] + VSMALL, el - 1.0);
365 kf *= el*pow(c2[si], el - 1.0);
370 kf *= pow(c2[si], el);
376 label si = R.lhs()[i].index;
377 scalar sl = R.lhs()[i].stoichCoeff;
378 dfdc[si][sj] -= sl*kf;
382 label si = R.rhs()[i].index;
383 scalar sr = R.rhs()[i].stoichCoeff;
384 dfdc[si][sj] += sr*kf;
390 label sj = R.rhs()[j].index;
394 label si = R.rhs()[i].index;
395 scalar er = R.rhs()[i].exponent;
402 kr *= er*pow(c2[si] + VSMALL, er - 1.0);
411 kr *= er*pow(c2[si], er - 1.0);
416 kr *= pow(c2[si], er);
422 label si = R.lhs()[i].index;
423 scalar sl = R.lhs()[i].stoichCoeff;
424 dfdc[si][sj] += sl*kr;
428 label si = R.rhs()[i].index;
429 scalar sr = R.rhs()[i].stoichCoeff;
430 dfdc[si][sj] -= sr*kr;
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);
440 for (label i=0; i<nEqns(); i++)
442 dfdc[i][nSpecie()] = 0.5*(dcdT1[i] - dcdT0[i])/delta;
448 template<class CompType, class ThermoType>
449 Foam::tmp<Foam::volScalarField>
450 Foam::ODEChemistryModel<CompType, ThermoType>::tc() const
455 const volScalarField rho
460 this->time().timeName(),
469 tmp<volScalarField> tsource
476 this->time().timeName(),
482 dimensionedScalar("zero", dimTime, SMALL),
483 zeroGradientFvPatchScalarField::typeName
487 scalarField& t = tsource();
489 label nReaction = reactions_.size();
491 if (this->chemistry_)
495 scalar rhoi = rho[celli];
496 scalar Ti = this->thermo().T()[celli];
497 scalar pi = this->thermo().p()[celli];
498 scalarField c(nSpecie_);
501 for (label i=0; i<nSpecie_; i++)
503 scalar Yi = Y_[i][celli];
504 c[i] = rhoi*Yi/specieThermo_[i].W();
508 forAll(reactions_, i)
510 const Reaction<ThermoType>& R = reactions_[i];
514 R, c, Ti, pi, pf, cf, lRef, pr, cr, rRef
519 scalar sr = R.rhs()[s].stoichCoeff;
520 t[celli] += sr*pf*cf;
523 t[celli] = nReaction*cSum/t[celli];
528 tsource().correctBoundaryConditions();
534 template<class CompType, class ThermoType>
535 Foam::tmp<Foam::volScalarField>
536 Foam::ODEChemistryModel<CompType, ThermoType>::dQ() const
538 tmp<volScalarField> tdQ
545 this->mesh_.time().timeName(),
554 dimensionSet(1, -3, -1 , 0, 0, 0, 0),
560 if (this->chemistry_)
562 scalarField& dQ = tdQ();
564 scalarField cp(dQ.size(), 0.0);
570 scalar Ti = this->thermo().T()[cellI];
571 cp[cellI] += Y_[i][cellI]*specieThermo_[i].Cp(Ti);
572 scalar hi = specieThermo_[i].h(Ti);
573 dQ[cellI] -= hi*RR_[i][cellI];
584 template<class CompType, class ThermoType>
585 Foam::label Foam::ODEChemistryModel<CompType, ThermoType>::nEqns() const
587 // nEqns = number of species + temperature + pressure
592 template<class CompType, class ThermoType>
593 void Foam::ODEChemistryModel<CompType, ThermoType>::calculate()
595 const volScalarField rho
600 this->time().timeName(),
609 for (label i=0; i<nSpecie_; i++)
611 RR_[i].setSize(rho.size());
614 if (this->chemistry_)
618 for (label i=0; i<nSpecie_; i++)
623 scalar rhoi = rho[celli];
624 scalar Ti = this->thermo().T()[celli];
625 scalar pi = this->thermo().p()[celli];
627 scalarField c(nSpecie_);
628 scalarField dcdt(nEqns(), 0.0);
630 for (label i=0; i<nSpecie_; i++)
632 scalar Yi = Y_[i][celli];
633 c[i] = rhoi*Yi/specieThermo_[i].W();
636 dcdt = omega(c, Ti, pi);
638 for (label i=0; i<nSpecie_; i++)
640 RR_[i][celli] = dcdt[i]*specieThermo_[i].W();
647 template<class CompType, class ThermoType>
648 Foam::scalar Foam::ODEChemistryModel<CompType, ThermoType>::solve
654 const volScalarField rho
659 this->time().timeName(),
668 for (label i=0; i<nSpecie_; i++)
670 RR_[i].setSize(rho.size());
673 if (!this->chemistry_)
678 scalar deltaTMin = GREAT;
682 for (label i=0; i<nSpecie_; i++)
687 scalar rhoi = rho[celli];
688 scalar Ti = this->thermo().T()[celli];
689 scalar hi = this->thermo().h()[celli];
690 scalar pi = this->thermo().p()[celli];
692 scalarField c(nSpecie_);
693 scalarField c0(nSpecie_);
694 scalarField dc(nSpecie_, 0.0);
696 for (label i=0; i<nSpecie_; i++)
698 c[i] = rhoi*Y_[i][celli]/specieThermo_[i].W();
703 scalar tauC = this->deltaTChem_[celli];
704 scalar dt = min(deltaT, tauC);
705 scalar timeLeft = deltaT;
707 // calculate the chemical source terms
710 while(timeLeft > SMALL)
712 tauC = solver().solve(c, Ti, pi, t, dt);
715 // update the temperature
717 ThermoType mixture(0.0*specieThermo_[0]);
718 for (label i=0; i<nSpecie_; i++)
720 mixture += (c[i]/cTot)*specieThermo_[i];
722 Ti = mixture.TH(hi, Ti);
725 this->deltaTChem_[celli] = tauC;
726 dt = min(timeLeft, tauC);
729 deltaTMin = min(tauC, deltaTMin);
733 for (label i=0; i<nSpecie_; i++)
735 WTot += c[i]*specieThermo_[i].W();
739 for (label i=0; i<nSpecie_; i++)
741 RR_[i][celli] = dc[i]*specieThermo_[i].W()/deltaT;
745 // Don't allow the time-step to change more than a factor of 2
746 deltaTMin = min(deltaTMin, 2*deltaT);
752 // ************************************************************************* //