BUG: UListIO: byteSize overflowing on really big faceLists
[OpenFOAM-2.0.x.git] / src / regionModels / pyrolysisModels / reactingOneDim / reactingOneDim.C
blob87c9c7644d39f52fc7aab91bde83e723627705ea
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
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
19     for more details.
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 "reactingOneDim.H"
27 #include "addToRunTimeSelectionTable.H"
28 #include "directMappedPatchBase.H"
29 #include "mapDistribute.H"
30 #include "zeroGradientFvPatchFields.H"
31 #include "surfaceInterpolate.H"
32 #include "fvm.H"
33 #include "fvcDiv.H"
34 #include "fvcVolumeIntegrate.H"
35 #include "fvMatrices.H"
37 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
39 namespace Foam
41 namespace regionModels
43 namespace pyrolysisModels
46 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
48 defineTypeNameAndDebug(reactingOneDim, 0);
50 addToRunTimeSelectionTable(pyrolysisModel, reactingOneDim, mesh);
51 addToRunTimeSelectionTable(pyrolysisModel, reactingOneDim, dictionary);
53 // * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
55 void reactingOneDim::readReactingOneDimControls()
57     const dictionary& solution = this->solution().subDict("SIMPLE");
58     solution.lookup("nNonOrthCorr") >> nNonOrthCorr_;
59     time_.controlDict().lookup("maxDi") >> maxDiff_;
61     coeffs().lookup("radFluxName") >> primaryRadFluxName_;
62     coeffs().lookup("minimumDelta") >> minimumDelta_;
66 bool reactingOneDim::read()
68     if (pyrolysisModel::read())
69     {
70         readReactingOneDimControls();
71         return true;
72     }
73     else
74     {
75         return false;
76     }
80 bool reactingOneDim::read(const dictionary& dict)
82     if (pyrolysisModel::read(dict))
83     {
84         readReactingOneDimControls();
85         return true;
86     }
87     else
88     {
89         return false;
90     }
94 void reactingOneDim::updateQr()
96     // Retrieve field from coupled region using mapped boundary conditions
97     QrCoupled_.correctBoundaryConditions();
99     // Update local Qr from coupled Qr field
100     Qr_ == dimensionedScalar("zero", Qr_.dimensions(), 0.0);
101     forAll(intCoupledPatchIDs_, i)
102     {
103         const label patchI = intCoupledPatchIDs_[i];
105         scalarField& Qrp = Qr_.boundaryField()[patchI];
107         // Qr is negative going out the solid
108         // If the surface is emitting the radiative flux is set to zero
109         Qrp = max(Qrp, scalar(0.0));
110     }
112     // Propagate Qr through 1-D regions
113     forAll(intCoupledPatchIDs_, i)
114     {
115         const label patchI = intCoupledPatchIDs_[i];
117         const scalarField& Qrp = Qr_.boundaryField()[patchI];
118         const vectorField& Cf = regionMesh().Cf().boundaryField()[patchI];
120         forAll(Qrp, faceI)
121         {
122             const scalar Qr0 = Qrp[faceI];
123             point Cf0 = Cf[faceI];
124             const labelList& cells = boundaryFaceCells_[faceI];
125             scalar kappaInt = 0.0;
126             forAll(cells, k)
127             {
128                 const label cellI = cells[k];
129                 const point& Cf1 = regionMesh().cellCentres()[cellI];
130                 const scalar delta = mag(Cf1 - Cf0);
131                 kappaInt += kappa_[cellI]*delta;
132                 Qr_[cellI] = Qr0*exp(-kappaInt);
133                 Cf0 = Cf1;
134             }
135         }
136     }
138     Qr_.correctBoundaryConditions();
142 void reactingOneDim::updatePhiGas()
144     phiHsGas_ ==  dimensionedScalar("zero", phiHsGas_.dimensions(), 0.0);
145     phiGas_ == dimensionedScalar("zero", phiGas_.dimensions(), 0.0);
147     const speciesTable& gasTable = solidChemistry_->gasTable();
149     forAll(gasTable, gasI)
150     {
151         tmp<volScalarField> tHsiGas = solidChemistry_->gasHs(T_, gasI);
152         tmp<volScalarField> tRRiGas = solidChemistry_->RRg(gasI);
154         const volScalarField& HsiGas = tHsiGas();
155         const volScalarField& RRiGas = tRRiGas();
157         const surfaceScalarField HsiGasf(fvc::interpolate(HsiGas));
158         const surfaceScalarField RRiGasf(fvc::interpolate(RRiGas));
160         forAll(intCoupledPatchIDs_, i)
161         {
162             const label patchI = intCoupledPatchIDs_[i];
163             const scalarField& phiGasp = phiHsGas_.boundaryField()[patchI];
165             forAll(phiGasp, faceI)
166             {
167                 const labelList& cells = boundaryFaceCells_[faceI];
168                 scalar massInt = 0.0;
169                 forAllReverse(cells, k)
170                 {
171                     const label cellI = cells[k];
172                     massInt += RRiGas[cellI]*regionMesh().V()[cellI];
173                     phiHsGas_[cellI] += massInt*HsiGas[cellI];
174                 }
176                 phiGas_.boundaryField()[patchI][faceI] += massInt;
178                 if (debug)
179                 {
180                     Info<< " Gas : " << gasTable[gasI]
181                         << " on patch : " << patchI
182                         << " mass produced at face(local) : "
183                         <<  faceI
184                         << " is : " << massInt
185                         << " [kg/s] " << endl;
186                 }
187             }
188         }
189         tHsiGas().clear();
190     }
194 void reactingOneDim::updateFields()
196     updateQr();
198     updatePhiGas();
202 void reactingOneDim::updateMesh(const scalarField& mass0)
204     if (!moveMesh_)
205     {
206         return;
207     }
209     const scalarField newV(mass0/rho_);
211     Info<< "Initial/final volumes = " << gSum(regionMesh().V()) << ", "
212         << gSum(newV) << " [m3]" << endl;
214     // move the mesh
215     const labelList moveMap = moveMesh(regionMesh().V() - newV, minimumDelta_);
217     // flag any cells that have not moved as non-reacting
218     forAll(moveMap, i)
219     {
220         if (moveMap[i] == 0)
221         {
222             solidChemistry_->setCellReacting(i, false);
223         }
224     }
228 void reactingOneDim::solveContinuity()
230     if (debug)
231     {
232         Info<< "reactingOneDim::solveContinuity()" << endl;
233     }
235     solve
236     (
237         fvm::ddt(rho_)
238      ==
239       - solidChemistry_->RRg()
240     );
244 void reactingOneDim::solveSpeciesMass()
246     if (debug)
247     {
248         Info<< "reactingOneDim::solveSpeciesMass()" << endl;
249     }
251     volScalarField Yt(0.0*Ys_[0]);
253     for (label i=0; i<Ys_.size()-1; i++)
254     {
255         volScalarField& Yi = Ys_[i];
257         fvScalarMatrix YiEqn
258         (
259             fvm::ddt(rho_, Yi)
260          ==
261             solidChemistry_->RRs(i)
262         );
264         if (moveMesh_)
265         {
266             surfaceScalarField phiRhoMesh
267             (
268                 fvc::interpolate(Yi*rho_)*regionMesh().phi()
269             );
271             YiEqn -= fvc::div(phiRhoMesh);
272         }
274         YiEqn.solve(regionMesh().solver("Yi"));
275         Yi.max(0.0);
276         Yt += Yi;
277     }
279     Ys_[Ys_.size() - 1] = 1.0 - Yt;
283 void reactingOneDim::solveEnergy()
285     if (debug)
286     {
287         Info<< "reactingOneDim::solveEnergy()" << endl;
288     }
290     const volScalarField rhoCp(rho_*solidThermo_.Cp());
292     const surfaceScalarField phiQr(fvc::interpolate(Qr_)*nMagSf());
294     const surfaceScalarField phiGas(fvc::interpolate(phiHsGas_));
296     fvScalarMatrix TEqn
297     (
298         fvm::ddt(rhoCp, T_)
299       - fvm::laplacian(K_, T_)
300      ==
301         chemistrySh_
302       + fvc::div(phiQr)
303       + fvc::div(phiGas)
304     );
306     if (moveMesh_)
307     {
308         surfaceScalarField phiMesh
309         (
310             fvc::interpolate(rhoCp*T_)*regionMesh().phi()
311         );
313         TEqn -= fvc::div(phiMesh);
314     }
316     TEqn.relax();
317     TEqn.solve();
319     Info<< "pyrolysis min/max(T) = " << min(T_).value() << ", "
320         << max(T_).value() << endl;
324 void reactingOneDim::calculateMassTransfer()
326     totalGasMassFlux_ = 0;
327     forAll(intCoupledPatchIDs_, i)
328     {
329         const label patchI = intCoupledPatchIDs_[i];
330         totalGasMassFlux_ += gSum(phiGas_.boundaryField()[patchI]);
331     }
333     if (infoOutput_)
334     {
335         totalHeatRR_ = fvc::domainIntegrate(chemistrySh_);
337         addedGasMass_ +=
338             fvc::domainIntegrate(solidChemistry_->RRg())*time_.deltaT();
339         lostSolidMass_ +=
340             fvc::domainIntegrate(solidChemistry_->RRs())*time_.deltaT();
341     }
345 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
347 reactingOneDim::reactingOneDim(const word& modelType, const fvMesh& mesh)
349     pyrolysisModel(modelType, mesh),
350     solidChemistry_(solidChemistryModel::New(regionMesh())),
351     solidThermo_(solidChemistry_->solidThermo()),
352     kappa_(solidThermo_.kappa()),
353     K_(solidThermo_.K()),
354     rho_(solidThermo_.rho()),
355     Ys_(solidThermo_.composition().Y()),
356     T_(solidThermo_.T()),
357     primaryRadFluxName_(coeffs().lookupOrDefault<word>("radFluxName", "Qr")),
358     nNonOrthCorr_(-1),
359     maxDiff_(10),
360     minimumDelta_(1e-4),
362     phiGas_
363     (
364         IOobject
365         (
366             "phiGas",
367             time().timeName(),
368             regionMesh(),
369             IOobject::NO_READ,
370             IOobject::AUTO_WRITE
371         ),
372         regionMesh(),
373         dimensionedScalar("zero", dimMass/dimTime, 0.0)
374     ),
376     phiHsGas_
377     (
378         IOobject
379         (
380             "phiHsGas",
381             time().timeName(),
382             regionMesh(),
383             IOobject::NO_READ,
384             IOobject::NO_WRITE
385         ),
386         regionMesh(),
387         dimensionedScalar("zero", dimEnergy/dimTime, 0.0)
388     ),
390     chemistrySh_
391     (
392         IOobject
393         (
394             "chemistrySh",
395             time().timeName(),
396             regionMesh(),
397             IOobject::NO_READ,
398             IOobject::AUTO_WRITE
399         ),
400         regionMesh(),
401         dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0)
402     ),
404     QrCoupled_
405     (
406         IOobject
407         (
408             primaryRadFluxName_,
409             time().timeName(),
410             regionMesh(),
411             IOobject::MUST_READ,
412             IOobject::AUTO_WRITE
413         ),
414         regionMesh()
415     ),
417     Qr_
418     (
419         IOobject
420         (
421             "QrPyr",
422             time().timeName(),
423             regionMesh(),
424             IOobject::NO_READ,
425             IOobject::AUTO_WRITE
426         ),
427         regionMesh(),
428         dimensionedScalar("zero", dimEnergy/dimArea/dimTime, 0.0),
429         zeroGradientFvPatchVectorField::typeName
430     ),
432     lostSolidMass_(dimensionedScalar("zero", dimMass, 0.0)),
433     addedGasMass_(dimensionedScalar("zero", dimMass, 0.0)),
434     totalGasMassFlux_(0.0),
435     totalHeatRR_(dimensionedScalar("zero", dimEnergy/dimTime, 0.0))
437     if (active_)
438     {
439         read();
440     }
444 reactingOneDim::reactingOneDim
446     const word& modelType,
447     const fvMesh& mesh,
448     const dictionary& dict
451     pyrolysisModel(modelType, mesh, dict),
452     solidChemistry_(solidChemistryModel::New(regionMesh())),
453     solidThermo_(solidChemistry_->solidThermo()),
454     kappa_(solidThermo_.kappa()),
455     K_(solidThermo_.K()),
456     rho_(solidThermo_.rho()),
457     Ys_(solidThermo_.composition().Y()),
458     T_(solidThermo_.T()),
459     primaryRadFluxName_(dict.lookupOrDefault<word>("radFluxName", "Qr")),
460     nNonOrthCorr_(-1),
461     maxDiff_(10),
462     minimumDelta_(1e-4),
464     phiGas_
465     (
466         IOobject
467         (
468             "phiGas",
469             time().timeName(),
470             regionMesh(),
471             IOobject::NO_READ,
472             IOobject::AUTO_WRITE
473         ),
474         regionMesh(),
475         dimensionedScalar("zero", dimMass/dimTime, 0.0)
476     ),
478     phiHsGas_
479     (
480         IOobject
481         (
482             "phiHsGas",
483             time().timeName(),
484             regionMesh(),
485             IOobject::NO_READ,
486             IOobject::NO_WRITE
487         ),
488         regionMesh(),
489         dimensionedScalar("zero", dimEnergy/dimTime, 0.0)
490     ),
492     chemistrySh_
493     (
494         IOobject
495         (
496             "chemistrySh",
497             time().timeName(),
498             regionMesh(),
499             IOobject::NO_READ,
500             IOobject::AUTO_WRITE
501         ),
502         regionMesh(),
503         dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0)
504     ),
506     QrCoupled_
507     (
508         IOobject
509         (
510             primaryRadFluxName_,
511             time().timeName(),
512             regionMesh(),
513             IOobject::MUST_READ,
514             IOobject::AUTO_WRITE
515         ),
516         regionMesh()
517     ),
519     Qr_
520     (
521         IOobject
522         (
523             "QrPyr",
524             time().timeName(),
525             regionMesh(),
526             IOobject::NO_READ,
527             IOobject::AUTO_WRITE
528         ),
529         regionMesh(),
530         dimensionedScalar("zero", dimEnergy/dimArea/dimTime, 0.0),
531         zeroGradientFvPatchVectorField::typeName
532     ),
534     lostSolidMass_(dimensionedScalar("zero", dimMass, 0.0)),
535     addedGasMass_(dimensionedScalar("zero", dimMass, 0.0)),
536     totalGasMassFlux_(0.0),
537     totalHeatRR_(dimensionedScalar("zero", dimEnergy/dimTime, 0.0))
539     if (active_)
540     {
541         read(dict);
542     }
546 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
548 reactingOneDim::~reactingOneDim()
552 // * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
554 scalar reactingOneDim::addMassSources(const label patchI, const label faceI)
556     label index = 0;
557     forAll(primaryPatchIDs_, i)
558     {
559         if (primaryPatchIDs_[i] == patchI)
560         {
561             index = i;
562             break;
563         }
564     }
566     const label localPatchId =  intCoupledPatchIDs_[index];
568     const scalar massAdded = phiGas_.boundaryField()[localPatchId][faceI];
570     if (debug)
571     {
572         Info<< "\nPyrolysis region: " << type() << "added mass : "
573             << massAdded << endl;
574     }
576     return massAdded;
580 scalar reactingOneDim::solidRegionDiffNo() const
582     scalar DiNum = 0.0;
584     if (regionMesh().nInternalFaces() > 0)
585     {
586         surfaceScalarField KrhoCpbyDelta
587         (
588             regionMesh().surfaceInterpolation::deltaCoeffs()
589           * fvc::interpolate(K_)
590           / fvc::interpolate(Cp()*rho_)
591         );
593         DiNum = max(KrhoCpbyDelta.internalField())*time_.deltaTValue();
594     }
596     return DiNum;
600 scalar reactingOneDim::maxDiff() const
602     return maxDiff_;
606 const volScalarField& reactingOneDim::rho() const
608     return rho_;
612 const volScalarField& reactingOneDim::T() const
614     return T_;
618 const tmp<volScalarField> reactingOneDim::Cp() const
620     return solidThermo_.Cp();
624 const volScalarField& reactingOneDim::kappa() const
626     return kappa_;
630 const volScalarField& reactingOneDim::K() const
632     return K_;
636 const surfaceScalarField& reactingOneDim::phiGas() const
638     return phiGas_;
642 void reactingOneDim::preEvolveRegion()
644     pyrolysisModel::preEvolveRegion();
646     // Initialise all cells as able to react
647     forAll(T_, cellI)
648     {
649         solidChemistry_->setCellReacting(cellI, true);
650     }
652     // De-activate reactions if pyrolysis region coupled to (valid) film
653     if (filmCoupled_)
654     {
655         const volScalarField& filmDelta = filmDeltaPtr_();
657         forAll(intCoupledPatchIDs_, i)
658         {
659             const label patchI = intCoupledPatchIDs_[i];
660             const scalarField& filmDeltap = filmDelta.boundaryField()[patchI];
662             forAll(filmDeltap, faceI)
663             {
664                 const scalar filmDelta0 = filmDeltap[faceI];
665                 if (filmDelta0 > reactionDeltaMin_)
666                 {
667                     const labelList& cells = boundaryFaceCells_[faceI];
669                     // TODO: only limit cell adjacent to film?
670                     //solidChemistry_->setCellNoReacting(cells[0])
672                     // Propagate flag through 1-D region
673                     forAll(cells, k)
674                     {
675                         solidChemistry_->setCellReacting(cells[k], false);
676                     }
677                 }
678             }
679         }
680     }
684 void reactingOneDim::evolveRegion()
687     Info<< "\nEvolving pyrolysis in region: " << regionMesh().name() << endl;
688     const scalarField mass0 = rho_*regionMesh().V();
690     solidChemistry_->solve
691     (
692         time().value() - time().deltaTValue(),
693         time().deltaTValue()
694     );
696     solveContinuity();
698     updateMesh(mass0);
700     chemistrySh_ = solidChemistry_->Sh()();
702     updateFields();
704     solveSpeciesMass();
706     for (int nonOrth=0; nonOrth<=nNonOrthCorr_; nonOrth++)
707     {
708         solveEnergy();
709     }
711     calculateMassTransfer();
713     solidThermo_.correct();
717 void reactingOneDim::info() const
719     Info<< "\nPyrolysis in region: " << regionMesh().name() << endl;
721     Info<< indent << "Total gas mass produced  [kg] = "
722         << addedGasMass_.value() << nl
723         << indent << "Total solid mass lost    [kg] = "
724         << lostSolidMass_.value() << nl
725         << indent << "Total pyrolysis gases  [kg/s] = "
726         << totalGasMassFlux_ << nl
727         << indent << "Total heat release rate [J/s] = "
728         << totalHeatRR_.value() << nl;
732 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
734 } // End namespace Foam
735 } // End namespace regionModels
736 } // End namespace pyrolysisModels
738 // ************************************************************************* //