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);
52 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
54 bool reactingOneDim::read()
56 if (pyrolysisModel::read())
58 const dictionary& solution = this->solution().subDict("SIMPLE");
59 solution.lookup("nNonOrthCorr") >> nNonOrthCorr_;
60 time_.controlDict().lookup("maxDi") >> maxDiff_;
62 coeffs().lookup("radFluxName") >> primaryRadFluxName_;
63 coeffs().lookup("minimumDelta") >> minimumDelta_;
73 void reactingOneDim::updateQr()
75 // Retrieve field from coupled region using mapped boundary conditions
76 QrCoupled_.correctBoundaryConditions();
78 // Update local Qr from coupled Qr field
79 Qr_ == dimensionedScalar("zero", Qr_.dimensions(), 0.0);
80 forAll(intCoupledPatchIDs_, i)
82 const label patchI = intCoupledPatchIDs_[i];
84 scalarField& Qrp = Qr_.boundaryField()[patchI];
86 // Qr is negative going out the solid
87 // If the surface is emitting the radiative flux is set to zero
88 Qrp = max(Qrp, scalar(0.0));
91 // Propagate Qr through 1-D regions
92 forAll(intCoupledPatchIDs_, i)
94 const label patchI = intCoupledPatchIDs_[i];
96 const scalarField& Qrp = Qr_.boundaryField()[patchI];
97 const vectorField& Cf = regionMesh().Cf().boundaryField()[patchI];
101 const scalar Qr0 = Qrp[faceI];
102 point Cf0 = Cf[faceI];
103 const labelList& cells = boundaryFaceCells_[faceI];
104 scalar kappaInt = 0.0;
107 const label cellI = cells[k];
108 const point& Cf1 = regionMesh().cellCentres()[cellI];
109 const scalar delta = mag(Cf1 - Cf0);
110 kappaInt += kappa_[cellI]*delta;
111 Qr_[cellI] = Qr0*exp(-kappaInt);
117 Qr_.correctBoundaryConditions();
121 void reactingOneDim::updatePhiGas()
123 phiHsGas_ == dimensionedScalar("zero", phiHsGas_.dimensions(), 0.0);
124 phiGas_ == dimensionedScalar("zero", phiGas_.dimensions(), 0.0);
126 const speciesTable& gasTable = solidChemistry_->gasTable();
128 forAll(gasTable, gasI)
130 tmp<volScalarField> tHsiGas = solidChemistry_->gasHs(T_, gasI);
131 tmp<volScalarField> tRRiGas = solidChemistry_->RRg(gasI);
133 const volScalarField& HsiGas = tHsiGas();
134 const volScalarField& RRiGas = tRRiGas();
136 const surfaceScalarField HsiGasf(fvc::interpolate(HsiGas));
137 const surfaceScalarField RRiGasf(fvc::interpolate(RRiGas));
139 forAll(intCoupledPatchIDs_, i)
141 const label patchI = intCoupledPatchIDs_[i];
142 const scalarField& phiGasp = phiHsGas_.boundaryField()[patchI];
144 forAll(phiGasp, faceI)
146 const labelList& cells = boundaryFaceCells_[faceI];
147 scalar massInt = 0.0;
148 forAllReverse(cells, k)
150 const label cellI = cells[k];
151 massInt += RRiGas[cellI]*regionMesh().V()[cellI];
152 phiHsGas_[cellI] += massInt*HsiGas[cellI];
155 phiGas_.boundaryField()[patchI][faceI] += massInt;
159 Info<< " Gas : " << gasTable[gasI]
160 << " on patch : " << patchI
161 << " mass produced at face(local) : "
163 << " is : " << massInt
164 << " [kg/s] " << endl;
173 void reactingOneDim::updateFields()
181 void reactingOneDim::updateMesh(const scalarField& mass0)
188 const scalarField newV(mass0/rho_);
190 Info<< "Initial/final volumes = " << gSum(regionMesh().V()) << ", "
191 << gSum(newV) << " [m3]" << endl;
194 const labelList moveMap = moveMesh(regionMesh().V() - newV, minimumDelta_);
196 // flag any cells that have not moved as non-reacting
201 solidChemistry_->setCellReacting(i, false);
207 void reactingOneDim::solveContinuity()
211 Info<< "reactingOneDim::solveContinuity()" << endl;
218 - solidChemistry_->RRg()
223 void reactingOneDim::solveSpeciesMass()
227 Info<< "reactingOneDim::solveSpeciesMass()" << endl;
230 volScalarField Yt(0.0*Ys_[0]);
232 for (label i=0; i<Ys_.size()-1; i++)
234 volScalarField& Yi = Ys_[i];
240 solidChemistry_->RRs(i)
245 surfaceScalarField phiRhoMesh
247 fvc::interpolate(Yi*rho_)*regionMesh().phi()
250 YiEqn -= fvc::div(phiRhoMesh);
253 YiEqn.solve(regionMesh().solver("Yi"));
258 Ys_[Ys_.size() - 1] = 1.0 - Yt;
262 void reactingOneDim::solveEnergy()
266 Info<< "reactingOneDim::solveEnergy()" << endl;
269 const volScalarField rhoCp(rho_*solidThermo_.Cp());
271 const surfaceScalarField phiQr(fvc::interpolate(Qr_)*nMagSf());
273 const surfaceScalarField phiGas(fvc::interpolate(phiHsGas_));
278 - fvm::laplacian(K_, T_)
287 surfaceScalarField phiMesh
289 fvc::interpolate(rhoCp*T_)*regionMesh().phi()
292 TEqn -= fvc::div(phiMesh);
298 Info<< "pyrolysis min/max(T) = " << min(T_).value() << ", "
299 << max(T_).value() << endl;
303 void reactingOneDim::calculateMassTransfer()
305 totalGasMassFlux_ = 0;
306 forAll(intCoupledPatchIDs_, i)
308 const label patchI = intCoupledPatchIDs_[i];
309 totalGasMassFlux_ += gSum(phiGas_.boundaryField()[patchI]);
314 totalHeatRR_ = fvc::domainIntegrate(chemistrySh_);
317 fvc::domainIntegrate(solidChemistry_->RRg())*time_.deltaT();
319 fvc::domainIntegrate(solidChemistry_->RRs())*time_.deltaT();
324 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
326 reactingOneDim::reactingOneDim(const word& modelType, const fvMesh& mesh)
328 pyrolysisModel(modelType, mesh),
329 solidChemistry_(solidChemistryModel::New(regionMesh())),
330 solidThermo_(solidChemistry_->solidThermo()),
331 kappa_(solidThermo_.kappa()),
332 K_(solidThermo_.K()),
333 rho_(solidThermo_.rho()),
334 Ys_(solidThermo_.composition().Y()),
335 T_(solidThermo_.T()),
336 primaryRadFluxName_(coeffs().lookupOrDefault<word>("radFluxName", "Qr")),
352 dimensionedScalar("zero", dimMass/dimTime, 0.0)
366 dimensionedScalar("zero", dimEnergy/dimTime, 0.0)
380 dimensionedScalar("zero", dimEnergy/dimTime/dimVolume, 0.0)
407 dimensionedScalar("zero", dimEnergy/dimArea/dimTime, 0.0),
408 zeroGradientFvPatchVectorField::typeName
411 lostSolidMass_(dimensionedScalar("zero", dimMass, 0.0)),
412 addedGasMass_(dimensionedScalar("zero", dimMass, 0.0)),
413 totalGasMassFlux_(0.0),
414 totalHeatRR_(dimensionedScalar("zero", dimEnergy/dimTime, 0.0))
423 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
425 reactingOneDim::~reactingOneDim()
429 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
431 scalar reactingOneDim::addMassSources(const label patchI, const label faceI)
434 forAll(primaryPatchIDs_, i)
436 if (primaryPatchIDs_[i] == patchI)
443 const label localPatchId = intCoupledPatchIDs_[index];
445 const scalar massAdded = phiGas_.boundaryField()[localPatchId][faceI];
449 Info<< "\nPyrolysis region: " << type() << "added mass : "
450 << massAdded << endl;
457 scalar reactingOneDim::solidRegionDiffNo() const
461 if (regionMesh().nInternalFaces() > 0)
463 surfaceScalarField KrhoCpbyDelta
465 regionMesh().surfaceInterpolation::deltaCoeffs()
466 * fvc::interpolate(K_)
467 / fvc::interpolate(Cp()*rho_)
470 DiNum = max(KrhoCpbyDelta.internalField())*time_.deltaTValue();
477 scalar reactingOneDim::maxDiff() const
483 const volScalarField& reactingOneDim::rho() const
489 const volScalarField& reactingOneDim::T() const
495 const tmp<volScalarField> reactingOneDim::Cp() const
497 return solidThermo_.Cp();
501 const volScalarField& reactingOneDim::kappa() const
507 const volScalarField& reactingOneDim::K() const
513 const surfaceScalarField& reactingOneDim::phiGas() const
519 void reactingOneDim::preEvolveRegion()
521 pyrolysisModel::preEvolveRegion();
523 // Initialise all cells as able to react
526 solidChemistry_->setCellReacting(cellI, true);
529 // De-activate reactions if pyrolysis region coupled to (valid) film
532 const volScalarField& filmDelta = filmDeltaPtr_();
534 forAll(intCoupledPatchIDs_, i)
536 const label patchI = intCoupledPatchIDs_[i];
537 const scalarField& filmDeltap = filmDelta.boundaryField()[patchI];
539 forAll(filmDeltap, faceI)
541 const scalar filmDelta0 = filmDeltap[faceI];
542 if (filmDelta0 > reactionDeltaMin_)
544 const labelList& cells = boundaryFaceCells_[faceI];
546 // TODO: only limit cell adjacent to film?
547 //solidChemistry_->setCellNoReacting(cells[0])
549 // Propagate flag through 1-D region
552 solidChemistry_->setCellReacting(cells[k], false);
561 void reactingOneDim::evolveRegion()
563 const scalarField mass0 = rho_*regionMesh().V();
565 solidChemistry_->solve
567 time().value() - time().deltaTValue(),
575 chemistrySh_ = solidChemistry_->Sh()();
581 for (int nonOrth=0; nonOrth<=nNonOrthCorr_; nonOrth++)
586 calculateMassTransfer();
588 solidThermo_.correct();
592 void reactingOneDim::info() const
594 Info<< "\nPyrolysis: " << type() << endl;
596 Info<< indent << "Total gas mass produced [kg] = "
597 << addedGasMass_.value() << nl
598 << indent << "Total solid mass lost [kg] = "
599 << lostSolidMass_.value() << nl
600 << indent << "Total pyrolysis gases [kg/s] = "
601 << totalGasMassFlux_ << nl
602 << indent << "Total heat release rate [J/s] = "
603 << totalHeatRR_.value() << nl;
607 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
609 } // End namespace Foam
610 } // End namespace regionModels
611 } // End namespace pyrolysisModels
613 // ************************************************************************* //