1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "ODESolidChemistryModel.H"
27 #include "reactingSolidMixture.H"
29 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
31 template<class CompType, class SolidThermo, class GasThermo>
32 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::
33 ODESolidChemistryModel
36 const word& compTypeName,
37 const word& solidThermoName
40 CompType(mesh, solidThermoName),
42 Ys_(this->solidThermo().composition().Y()),
45 mesh.lookupObject<dictionary>
46 ("chemistryProperties").lookup("species")
50 static_cast<const reactingSolidMixture<SolidThermo>& >
51 (this->solidThermo().composition())
55 static_cast<const reactingSolidMixture<SolidThermo>& >
56 (this->solidThermo().composition()).solidData()
58 gasThermo_(pyrolisisGases_.size()),
59 nGases_(pyrolisisGases_.size()),
60 nSpecie_(Ys_.size() + nGases_),
62 nReaction_(reactions_.size()),
67 reactingCells_(mesh.nCells(), true)
69 // create the fields for the chemistry sources
75 new scalarField(mesh.nCells(), 0.0)
81 Ys_[fieldI].name() + "0",
82 mesh.time().timeName(),
87 // check if field exists and can be read
88 if (header.headerOk())
97 Ys_[fieldI].name() + "0",
98 mesh.time().timeName(),
109 volScalarField Y0Default
114 mesh.time().timeName(),
129 Ys_[fieldI].name() + "0",
130 mesh.time().timeName(),
139 // Calculate inital values of Ysi0 = rho*delta*Yi
140 Ys0_[fieldI].internalField() =
141 this->solidThermo().rho()
142 *max(Ys_[fieldI], scalar(0.001))*mesh.V();
148 RRg_.set(fieldI, new scalarField(mesh.nCells(), 0.0));
151 forAll(gasThermo_, gasI)
153 dictionary thermoDict =
154 mesh.lookupObject<dictionary>
156 "chemistryProperties"
157 ).subDict(pyrolisisGases_[gasI]);
162 new GasThermo(thermoDict)
166 Info<< "ODESolidChemistryModel: Number of solids = " << nSolids_
167 << " and reactions = " << nReaction_ << endl;
169 Info<< "Number of gases from pyrolysis = " << nGases_ << endl;
171 forAll(reactions_, i)
173 Info<< indent << "Reaction " << i << nl << reactions_[i] << nl;
179 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
181 template<class CompType, class SolidThermo, class GasThermo>
182 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::
183 ~ODESolidChemistryModel()
187 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
189 template<class CompType, class SolidThermo, class GasThermo>
190 Foam::scalarField Foam::
191 ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::omega
193 const scalarField& c,
199 scalar pf, cf, pr, cr;
202 const label cellI = cellCounter_;
204 scalarField om(nEqns(), 0.0);
206 forAll(reactions_, i)
208 const solidReaction& R = reactions_[i];
210 scalar omegai = omega
212 R, c, T, 0.0, pf, cf, lRef, pr, cr, rRef
217 label si = R.slhs()[s];
219 rhoL = solidThermo_[si].rho(T);
224 label si = R.srhs()[s];
225 scalar rhoR = solidThermo_[si].rho(T);
231 Ys0_[si][cellI] += sr*omegai;
236 label gi = R.grhs()[g];
237 om[gi + nSolids_] += (1.0 - sr)*omegai;
245 template<class CompType, class SolidThermo, class GasThermo>
247 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::omega
249 const solidReaction& R,
250 const scalarField& c,
261 scalarField c1(nSpecie_, 0.0);
263 label cellI = cellCounter_;
265 for (label i=0; i<nSpecie_; i++)
267 c1[i] = max(0.0, c[i]);
270 scalar kf = R.kf(T, 0.0, c1);
272 scalar exponent = R.nReact();
274 const label Nl = R.slhs().size();
276 for (label s=0; s<Nl; s++)
278 label si = R.slhs()[s];
281 pow(c1[si]/Ys0_[si][cellI], exponent)
289 template<class CompType, class SolidThermo, class GasThermo>
290 void Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::derivatives
293 const scalarField &c,
297 scalar T = c[nSpecie_];
301 dcdt = omega(c, T, 0);
303 //Total mass concentration
305 for (label i=0; i<nSolids_; i++)
312 for (label i=0; i<nSolids_; i++)
314 scalar dYidt = dcdt[i]/cTot;
315 scalar Yi = c[i]/cTot;
316 newCp += Yi*solidThermo_[i].Cp(T);
317 newhi -= dYidt*solidThermo_[i].hf();
320 scalar dTdt = newhi/newCp;
321 scalar dtMag = min(500.0, mag(dTdt));
322 dcdt[nSpecie_] = dTdt*dtMag/(mag(dTdt) + 1.0e-10);
325 dcdt[nSpecie_ + 1] = 0.0;
329 template<class CompType, class SolidThermo, class GasThermo>
330 void Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::jacobian
333 const scalarField& c,
335 scalarSquareMatrix& dfdc
338 scalar T = c[nSpecie_];
340 scalarField c2(nSpecie_, 0.0);
342 for (label i=0; i<nSolids_; i++)
344 c2[i] = max(c[i], 0.0);
347 for (label i=0; i<nEqns(); i++)
349 for (label j=0; j<nEqns(); j++)
355 // length of the first argument must be nSolids
356 dcdt = omega(c2, T, 0.0);
358 for (label ri=0; ri<reactions_.size(); ri++)
360 const solidReaction& R = reactions_[ri];
362 scalar kf0 = R.kf(T, 0.0, c2);
366 label sj = R.slhs()[j];
370 label si = R.slhs()[i];
371 scalar exp = R.nReact();
378 kf *= exp*pow(c2[si] + VSMALL, exp - 1.0);
387 kf *= exp*pow(c2[si], exp - 1.0);
392 Info<< "Solid reactions have only elements on slhs"
400 label si = R.slhs()[i];
405 label si = R.srhs()[i];
411 // calculate the dcdT elements numerically
412 scalar delta = 1.0e-8;
413 scalarField dcdT0 = omega(c2, T - delta, 0);
414 scalarField dcdT1 = omega(c2, T + delta, 0);
416 for (label i=0; i<nEqns(); i++)
418 dfdc[i][nSpecie_] = 0.5*(dcdT1[i] - dcdT0[i])/delta;
424 template<class CompType, class SolidThermo, class GasThermo>
425 Foam::tmp<Foam::volScalarField>
426 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::tc() const
430 "ODESolidChemistryModel::tc()"
433 return volScalarField::null();
437 template<class CompType, class SolidThermo, class GasThermo>
438 Foam::tmp<Foam::volScalarField>
439 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::Sh() const
441 tmp<volScalarField> tSh
448 this->mesh_.time().timeName(),
451 IOobject::AUTO_WRITE,
455 dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0),
456 zeroGradientFvPatchScalarField::typeName
460 if (this->chemistry_)
462 scalarField& Sh = tSh();
468 scalar hf = solidThermo_[i].hf();
469 Sh[cellI] -= hf*RRs_[i][cellI];
478 template<class CompType, class SolidThermo, class GasThermo>
479 Foam::tmp<Foam::volScalarField>
480 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::dQ() const
482 tmp<volScalarField> tdQ
489 this->mesh_.time().timeName(),
496 dimensionedScalar("dQ", dimEnergy/dimTime, 0.0),
497 zeroGradientFvPatchScalarField::typeName
501 if (this->chemistry_)
503 volScalarField& dQ = tdQ();
504 dQ.dimensionedInternalField() = this->mesh_.V()*Sh()();
511 template<class CompType, class SolidThermo, class GasThermo>
513 ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::nEqns() const
515 // nEqns = number of solids + gases + temperature + pressure
516 return (nSpecie_ + 2);
520 template<class CompType, class SolidThermo, class GasThermo>
521 void Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::
525 const volScalarField rho
530 this->time().timeName(),
536 this->solidThermo().rho()
539 if (this->mesh().changing())
543 RRs_[i].setSize(rho.size());
547 RRg_[i].setSize(rho.size());
560 if (this->chemistry_)
564 cellCounter_ = celli;
566 const scalar delta = this->mesh().V()[celli];
568 if (reactingCells_[celli])
570 scalar rhoi = rho[celli];
571 scalar Ti = this->solidThermo().T()[celli];
573 scalarField c(nSpecie_, 0.0);
574 for (label i=0; i<nSolids_; i++)
576 c[i] = rhoi*Ys_[i][celli]*delta;
579 const scalarField dcdt = omega(c, Ti, 0.0, true);
583 RRs_[i][celli] = dcdt[i]/delta;
588 RRg_[i][celli] = dcdt[nSolids_ + i]/delta;
596 template<class CompType, class SolidThermo, class GasThermo>
598 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::solve
604 const volScalarField rho
609 this->time().timeName(),
615 this->solidThermo().rho()
618 if (this->mesh().changing())
622 RRs_[i].setSize(rho.size());
626 RRg_[i].setSize(rho.size());
639 if (!this->chemistry_)
644 scalar deltaTMin = GREAT;
648 if (reactingCells_[celli])
650 cellCounter_ = celli;
652 scalar rhoi = rho[celli];
653 scalar Ti = this->solidThermo().T()[celli];
655 scalarField c(nSpecie_, 0.0);
656 scalarField c0(nSpecie_, 0.0);
657 scalarField dc(nSpecie_, 0.0);
659 scalar delta = this->mesh().V()[celli];
661 for (label i=0; i<nSolids_; i++)
663 c[i] = rhoi*Ys_[i][celli]*delta;
669 scalar tauC = this->deltaTChem_[celli];
670 scalar dt = min(deltaT, tauC);
671 scalar timeLeft = deltaT;
673 // calculate the chemical source terms
674 while (timeLeft > SMALL)
676 tauC = this->solve(c, Ti, 0.0, t, dt);
679 // update the temperature
682 //Total mass concentration
683 for (label i=0; i<nSolids_; i++)
691 scalarList dcdt = (c - c0)/dt;
693 for (label i=0; i<nSolids_; i++)
695 scalar dYi = dcdt[i]/cTot;
696 scalar Yi = c[i]/cTot;
697 newCp += Yi*solidThermo_[i].Cp(Ti);
698 newhi -= dYi*solidThermo_[i].hf();
699 invRho += Yi/solidThermo_[i].rho(Ti);
702 scalar dTi = (newhi/newCp)*dt;
707 this->deltaTChem_[celli] = tauC;
708 dt = min(timeLeft, tauC);
712 deltaTMin = min(tauC, deltaTMin);
717 RRs_[i][celli] = dc[i]/(deltaT*delta);
722 RRg_[i][celli] = dc[nSolids_ + i]/(deltaT*delta);
726 dc = omega(c0, Ti, 0.0, true);
730 // Don't allow the time-step to change more than a factor of 2
731 deltaTMin = min(deltaTMin, 2*deltaT);
737 template<class CompType, class SolidThermo,class GasThermo>
738 Foam::tmp<Foam::volScalarField>
739 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::gasHs
741 const volScalarField& T,
746 tmp<volScalarField> tHs
752 "Hs_" + pyrolisisGases_[index],
753 this->mesh_.time().timeName(),
759 dimensionedScalar("zero", dimEnergy/dimMass, 0.0),
760 zeroGradientFvPatchScalarField::typeName
764 volScalarField& gasHs = tHs();
766 const GasThermo& mixture = gasThermo_[index];
768 forAll(gasHs.internalField(), cellI)
770 gasHs[cellI] = mixture.Hs(T[cellI]);
777 template<class CompType, class SolidThermo,class GasThermo>
779 Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::solve
790 "ODESolidChemistryModel::solve"
803 template<class CompType, class SolidThermo,class GasThermo>
804 void Foam::ODESolidChemistryModel<CompType, SolidThermo, GasThermo>::
805 setCellReacting(const label cellI, const bool active)
807 reactingCells_[cellI] = active;
811 // ************************************************************************* //