1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2010-2011 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
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
460 scalar meanDiNum = 0.0;
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();
472 meanDiNum = average(KrhoCpbyDelta.internalField())*time().deltaTValue();
479 scalar reactingOneDim::maxDiff() const
485 const volScalarField& reactingOneDim::rho() const
491 const volScalarField& reactingOneDim::T() const
497 const tmp<volScalarField> reactingOneDim::Cp() const
499 return solidThermo_.Cp();
503 const volScalarField& reactingOneDim::kappa() const
509 const volScalarField& reactingOneDim::K() const
515 const surfaceScalarField& reactingOneDim::phiGas() const
521 void reactingOneDim::preEvolveRegion()
523 pyrolysisModel::preEvolveRegion();
525 // Initialise all cells as able to react
528 solidChemistry_->setCellReacting(cellI, true);
531 // De-activate reactions if pyrolysis region coupled to (valid) film
534 const volScalarField& filmDelta = filmDeltaPtr_();
536 forAll(intCoupledPatchIDs_, i)
538 const label patchI = intCoupledPatchIDs_[i];
539 const scalarField& filmDeltap = filmDelta.boundaryField()[patchI];
541 forAll(filmDeltap, faceI)
543 const scalar filmDelta0 = filmDeltap[faceI];
544 if (filmDelta0 > reactionDeltaMin_)
546 const labelList& cells = boundaryFaceCells_[faceI];
548 // TODO: only limit cell adjacent to film?
549 //solidChemistry_->setCellNoReacting(cells[0])
551 // Propagate flag through 1-D region
554 solidChemistry_->setCellReacting(cells[k], false);
563 void reactingOneDim::evolveRegion()
565 const scalarField mass0 = rho_*regionMesh().V();
567 solidChemistry_->solve
569 time().value() - time().deltaTValue(),
577 chemistrySh_ = solidChemistry_->Sh()();
583 for (int nonOrth=0; nonOrth<=nNonOrthCorr_; nonOrth++)
588 calculateMassTransfer();
590 solidThermo_.correct();
594 void reactingOneDim::info() const
596 Info<< "\nPyrolysis: " << type() << endl;
598 Info<< indent << "Total gas mass produced [kg] = "
599 << addedGasMass_.value() << nl
600 << indent << "Total solid mass lost [kg] = "
601 << lostSolidMass_.value() << nl
602 << indent << "Total pyrolysis gases [kg/s] = "
603 << totalGasMassFlux_ << nl
604 << indent << "Total heat release rate [J/s] = "
605 << totalHeatRR_.value() << nl;
609 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
611 } // End namespace Foam
612 } // End namespace regionModels
613 } // End namespace pyrolysisModels
615 // ************************************************************************* //