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 "reactingOneDim.H"
27 #include "addToRunTimeSelectionTable.H"
28 #include "directMappedPatchBase.H"
29 #include "mapDistribute.H"
30 #include "zeroGradientFvPatchFields.H"
31 #include "surfaceInterpolate.H"
34 #include "fvcVolumeIntegrate.H"
35 #include "fvMatrices.H"
37 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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())
70 readReactingOneDimControls();
80 bool reactingOneDim::read(const dictionary& dict)
82 if (pyrolysisModel::read(dict))
84 readReactingOneDimControls();
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)
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));
112 // Propagate Qr through 1-D regions
113 forAll(intCoupledPatchIDs_, i)
115 const label patchI = intCoupledPatchIDs_[i];
117 const scalarField& Qrp = Qr_.boundaryField()[patchI];
118 const vectorField& Cf = regionMesh().Cf().boundaryField()[patchI];
122 const scalar Qr0 = Qrp[faceI];
123 point Cf0 = Cf[faceI];
124 const labelList& cells = boundaryFaceCells_[faceI];
125 scalar kappaInt = 0.0;
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);
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)
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)
162 const label patchI = intCoupledPatchIDs_[i];
163 const scalarField& phiGasp = phiHsGas_.boundaryField()[patchI];
165 forAll(phiGasp, faceI)
167 const labelList& cells = boundaryFaceCells_[faceI];
168 scalar massInt = 0.0;
169 forAllReverse(cells, k)
171 const label cellI = cells[k];
172 massInt += RRiGas[cellI]*regionMesh().V()[cellI];
173 phiHsGas_[cellI] += massInt*HsiGas[cellI];
176 phiGas_.boundaryField()[patchI][faceI] += massInt;
180 Info<< " Gas : " << gasTable[gasI]
181 << " on patch : " << patchI
182 << " mass produced at face(local) : "
184 << " is : " << massInt
185 << " [kg/s] " << endl;
194 void reactingOneDim::updateFields()
202 void reactingOneDim::updateMesh(const scalarField& mass0)
209 const scalarField newV(mass0/rho_);
211 Info<< "Initial/final volumes = " << gSum(regionMesh().V()) << ", "
212 << gSum(newV) << " [m3]" << endl;
215 const labelList moveMap = moveMesh(regionMesh().V() - newV, minimumDelta_);
217 // flag any cells that have not moved as non-reacting
222 solidChemistry_->setCellReacting(i, false);
228 void reactingOneDim::solveContinuity()
232 Info<< "reactingOneDim::solveContinuity()" << endl;
239 - solidChemistry_->RRg()
244 void reactingOneDim::solveSpeciesMass()
248 Info<< "reactingOneDim::solveSpeciesMass()" << endl;
251 volScalarField Yt(0.0*Ys_[0]);
253 for (label i=0; i<Ys_.size()-1; i++)
255 volScalarField& Yi = Ys_[i];
261 solidChemistry_->RRs(i)
266 surfaceScalarField phiRhoMesh
268 fvc::interpolate(Yi*rho_)*regionMesh().phi()
271 YiEqn -= fvc::div(phiRhoMesh);
274 YiEqn.solve(regionMesh().solver("Yi"));
279 Ys_[Ys_.size() - 1] = 1.0 - Yt;
283 void reactingOneDim::solveEnergy()
287 Info<< "reactingOneDim::solveEnergy()" << endl;
290 const volScalarField rhoCp(rho_*solidThermo_.Cp());
292 const surfaceScalarField phiQr(fvc::interpolate(Qr_)*nMagSf());
294 const surfaceScalarField phiGas(fvc::interpolate(phiHsGas_));
299 - fvm::laplacian(K_, T_)
308 surfaceScalarField phiMesh
310 fvc::interpolate(rhoCp*T_)*regionMesh().phi()
313 TEqn -= fvc::div(phiMesh);
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)
329 const label patchI = intCoupledPatchIDs_[i];
330 totalGasMassFlux_ += gSum(phiGas_.boundaryField()[patchI]);
335 totalHeatRR_ = fvc::domainIntegrate(chemistrySh_);
338 fvc::domainIntegrate(solidChemistry_->RRg())*time_.deltaT();
340 fvc::domainIntegrate(solidChemistry_->RRs())*time_.deltaT();
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")),
373 dimensionedScalar("zero", dimMass/dimTime, 0.0)
387 dimensionedScalar("zero", dimEnergy/dimTime, 0.0)
401 dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0)
428 dimensionedScalar("zero", dimEnergy/dimArea/dimTime, 0.0),
429 zeroGradientFvPatchVectorField::typeName
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))
444 reactingOneDim::reactingOneDim
446 const word& modelType,
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")),
475 dimensionedScalar("zero", dimMass/dimTime, 0.0)
489 dimensionedScalar("zero", dimEnergy/dimTime, 0.0)
503 dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0)
530 dimensionedScalar("zero", dimEnergy/dimArea/dimTime, 0.0),
531 zeroGradientFvPatchVectorField::typeName
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))
546 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
548 reactingOneDim::~reactingOneDim()
552 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
554 scalar reactingOneDim::addMassSources(const label patchI, const label faceI)
557 forAll(primaryPatchIDs_, i)
559 if (primaryPatchIDs_[i] == patchI)
566 const label localPatchId = intCoupledPatchIDs_[index];
568 const scalar massAdded = phiGas_.boundaryField()[localPatchId][faceI];
572 Info<< "\nPyrolysis region: " << type() << "added mass : "
573 << massAdded << endl;
580 scalar reactingOneDim::solidRegionDiffNo() const
584 if (regionMesh().nInternalFaces() > 0)
586 surfaceScalarField KrhoCpbyDelta
588 regionMesh().surfaceInterpolation::deltaCoeffs()
589 * fvc::interpolate(K_)
590 / fvc::interpolate(Cp()*rho_)
593 DiNum = max(KrhoCpbyDelta.internalField())*time_.deltaTValue();
600 scalar reactingOneDim::maxDiff() const
606 const volScalarField& reactingOneDim::rho() const
612 const volScalarField& reactingOneDim::T() const
618 const tmp<volScalarField> reactingOneDim::Cp() const
620 return solidThermo_.Cp();
624 const volScalarField& reactingOneDim::kappa() const
630 const volScalarField& reactingOneDim::K() const
636 const surfaceScalarField& reactingOneDim::phiGas() const
642 void reactingOneDim::preEvolveRegion()
644 pyrolysisModel::preEvolveRegion();
646 // Initialise all cells as able to react
649 solidChemistry_->setCellReacting(cellI, true);
652 // De-activate reactions if pyrolysis region coupled to (valid) film
655 const volScalarField& filmDelta = filmDeltaPtr_();
657 forAll(intCoupledPatchIDs_, i)
659 const label patchI = intCoupledPatchIDs_[i];
660 const scalarField& filmDeltap = filmDelta.boundaryField()[patchI];
662 forAll(filmDeltap, faceI)
664 const scalar filmDelta0 = filmDeltap[faceI];
665 if (filmDelta0 > reactionDeltaMin_)
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
675 solidChemistry_->setCellReacting(cells[k], false);
684 void reactingOneDim::evolveRegion()
687 Info<< "\nEvolving pyrolysis in region: " << regionMesh().name() << endl;
688 const scalarField mass0 = rho_*regionMesh().V();
690 solidChemistry_->solve
692 time().value() - time().deltaTValue(),
700 chemistrySh_ = solidChemistry_->Sh()();
706 for (int nonOrth=0; nonOrth<=nNonOrthCorr_; nonOrth++)
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 // ************************************************************************* //