mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
661 lines
16 KiB
C
661 lines
16 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | Copyright (C) 2008-2011 OpenCFD Ltd.
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
License
|
|
This file is part of OpenFOAM.
|
|
|
|
OpenFOAM is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "ReactingMultiphaseParcel.H"
|
|
#include "mathematicalConstants.H"
|
|
|
|
using namespace Foam::constant::mathematical;
|
|
|
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
|
|
|
template<class ParcelType>
|
|
const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::GAS(0);
|
|
|
|
template<class ParcelType>
|
|
const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::LIQ(1);
|
|
|
|
template<class ParcelType>
|
|
const Foam::label Foam::ReactingMultiphaseParcel<ParcelType>::SLD(2);
|
|
|
|
|
|
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::CpEff
|
|
(
|
|
TrackData& td,
|
|
const scalar p,
|
|
const scalar T,
|
|
const label idG,
|
|
const label idL,
|
|
const label idS
|
|
) const
|
|
{
|
|
return
|
|
this->Y_[GAS]*td.cloud().composition().Cp(idG, YGas_, p, T)
|
|
+ this->Y_[LIQ]*td.cloud().composition().Cp(idL, YLiquid_, p, T)
|
|
+ this->Y_[SLD]*td.cloud().composition().Cp(idS, YSolid_, p, T);
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::HEff
|
|
(
|
|
TrackData& td,
|
|
const scalar p,
|
|
const scalar T,
|
|
const label idG,
|
|
const label idL,
|
|
const label idS
|
|
) const
|
|
{
|
|
return
|
|
this->Y_[GAS]*td.cloud().composition().H(idG, YGas_, p, T)
|
|
+ this->Y_[LIQ]*td.cloud().composition().H(idL, YLiquid_, p, T)
|
|
+ this->Y_[SLD]*td.cloud().composition().H(idS, YSolid_, p, T);
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::LEff
|
|
(
|
|
TrackData& td,
|
|
const scalar p,
|
|
const scalar T,
|
|
const label idG,
|
|
const label idL,
|
|
const label idS
|
|
) const
|
|
{
|
|
return
|
|
this->Y_[GAS]*td.cloud().composition().L(idG, YGas_, p, T)
|
|
+ this->Y_[LIQ]*td.cloud().composition().L(idL, YLiquid_, p, T)
|
|
+ this->Y_[SLD]*td.cloud().composition().L(idS, YSolid_, p, T);
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
Foam::scalar Foam::ReactingMultiphaseParcel<ParcelType>::updateMassFractions
|
|
(
|
|
const scalar mass0,
|
|
const scalarField& dMassGas,
|
|
const scalarField& dMassLiquid,
|
|
const scalarField& dMassSolid
|
|
)
|
|
{
|
|
scalarField& YMix = this->Y_;
|
|
|
|
scalar massGas =
|
|
this->updateMassFraction(mass0*YMix[GAS], dMassGas, YGas_);
|
|
scalar massLiquid =
|
|
this->updateMassFraction(mass0*YMix[LIQ], dMassLiquid, YLiquid_);
|
|
scalar massSolid =
|
|
this->updateMassFraction(mass0*YMix[SLD], dMassSolid, YSolid_);
|
|
|
|
scalar massNew = max(massGas + massLiquid + massSolid, ROOTVSMALL);
|
|
|
|
YMix[GAS] = massGas/massNew;
|
|
YMix[LIQ] = massLiquid/massNew;
|
|
YMix[SLD] = 1.0 - YMix[GAS] - YMix[LIQ];
|
|
|
|
return massNew;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
void Foam::ReactingMultiphaseParcel<ParcelType>::setCellValues
|
|
(
|
|
TrackData& td,
|
|
const scalar dt,
|
|
const label cellI
|
|
)
|
|
{
|
|
ParcelType::setCellValues(td, dt, cellI);
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
void Foam::ReactingMultiphaseParcel<ParcelType>::cellValueSourceCorrection
|
|
(
|
|
TrackData& td,
|
|
const scalar dt,
|
|
const label cellI
|
|
)
|
|
{
|
|
// Re-use correction from reacting parcel
|
|
ParcelType::cellValueSourceCorrection(td, dt, cellI);
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
void Foam::ReactingMultiphaseParcel<ParcelType>::calc
|
|
(
|
|
TrackData& td,
|
|
const scalar dt,
|
|
const label cellI
|
|
)
|
|
{
|
|
typedef typename TrackData::cloudType::reactingCloudType reactingCloudType;
|
|
const CompositionModel<reactingCloudType>& composition =
|
|
td.cloud().composition();
|
|
|
|
|
|
// Define local properties at beginning of timestep
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
const scalar np0 = this->nParticle_;
|
|
const scalar d0 = this->d_;
|
|
const vector& U0 = this->U_;
|
|
const scalar rho0 = this->rho_;
|
|
const scalar T0 = this->T_;
|
|
const scalar Cp0 = this->Cp_;
|
|
const scalar mass0 = this->mass();
|
|
|
|
const scalar pc = this->pc_;
|
|
|
|
const scalarField& YMix = this->Y_;
|
|
const label idG = composition.idGas();
|
|
const label idL = composition.idLiquid();
|
|
const label idS = composition.idSolid();
|
|
|
|
|
|
// Calc surface values
|
|
// ~~~~~~~~~~~~~~~~~~~
|
|
scalar Ts, rhos, mus, Prs, kappas;
|
|
this->calcSurfaceValues(td, cellI, T0, Ts, rhos, mus, Prs, kappas);
|
|
|
|
// Reynolds number
|
|
scalar Res = this->Re(U0, d0, rhos, mus);
|
|
|
|
|
|
// Sources
|
|
//~~~~~~~~
|
|
|
|
// Explicit momentum source for particle
|
|
vector Su = vector::zero;
|
|
|
|
// Momentum transfer from the particle to the carrier phase
|
|
vector dUTrans = vector::zero;
|
|
|
|
// Explicit enthalpy source for particle
|
|
scalar Sh = 0.0;
|
|
|
|
// Sensible enthalpy transfer from the particle to the carrier phase
|
|
scalar dhsTrans = 0.0;
|
|
|
|
|
|
// Phase change in liquid phase
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
// Mass transfer due to phase change
|
|
scalarField dMassPC(YLiquid_.size(), 0.0);
|
|
|
|
// Molar flux of species emitted from the particle (kmol/m^2/s)
|
|
scalar Ne = 0.0;
|
|
|
|
// Sum Ni*Cpi*Wi of emission species
|
|
scalar NCpW = 0.0;
|
|
|
|
// Surface concentrations of emitted species
|
|
scalarField Cs(composition.carrier().species().size(), 0.0);
|
|
|
|
// Calc mass and enthalpy transfer due to phase change
|
|
this->calcPhaseChange
|
|
(
|
|
td,
|
|
dt,
|
|
cellI,
|
|
Res,
|
|
Ts,
|
|
mus/rhos,
|
|
d0,
|
|
T0,
|
|
mass0,
|
|
idL,
|
|
YMix[LIQ],
|
|
YLiquid_,
|
|
dMassPC,
|
|
Sh,
|
|
Ne,
|
|
NCpW,
|
|
Cs
|
|
);
|
|
|
|
|
|
// Devolatilisation
|
|
// ~~~~~~~~~~~~~~~~
|
|
|
|
// Mass transfer due to devolatilisation
|
|
scalarField dMassDV(YGas_.size(), 0.0);
|
|
|
|
// Calc mass and enthalpy transfer due to devolatilisation
|
|
calcDevolatilisation
|
|
(
|
|
td,
|
|
dt,
|
|
Ts,
|
|
d0,
|
|
T0,
|
|
mass0,
|
|
this->mass0_,
|
|
YMix[GAS]*YGas_,
|
|
canCombust_,
|
|
dMassDV,
|
|
Sh,
|
|
Ne,
|
|
NCpW,
|
|
Cs
|
|
);
|
|
|
|
|
|
// Surface reactions
|
|
// ~~~~~~~~~~~~~~~~~
|
|
|
|
// Change in carrier phase composition due to surface reactions
|
|
scalarField dMassSRGas(YGas_.size(), 0.0);
|
|
scalarField dMassSRLiquid(YLiquid_.size(), 0.0);
|
|
scalarField dMassSRSolid(YSolid_.size(), 0.0);
|
|
scalarField dMassSRCarrier(composition.carrier().species().size(), 0.0);
|
|
|
|
// Calc mass and enthalpy transfer due to surface reactions
|
|
calcSurfaceReactions
|
|
(
|
|
td,
|
|
dt,
|
|
cellI,
|
|
d0,
|
|
T0,
|
|
mass0,
|
|
canCombust_,
|
|
Ne,
|
|
YMix,
|
|
YGas_,
|
|
YLiquid_,
|
|
YSolid_,
|
|
dMassSRGas,
|
|
dMassSRLiquid,
|
|
dMassSRSolid,
|
|
dMassSRCarrier,
|
|
Sh,
|
|
dhsTrans
|
|
);
|
|
|
|
|
|
// Correct surface values due to emitted species
|
|
this->correctSurfaceValues(td, cellI, Ts, Cs, rhos, mus, Prs, kappas);
|
|
Res = this->Re(U0, d0, rhos, mus);
|
|
|
|
|
|
// Update component mass fractions
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
scalarField dMassGas(dMassDV + dMassSRGas);
|
|
scalarField dMassLiquid(dMassPC + dMassSRLiquid);
|
|
scalarField dMassSolid(dMassSRSolid);
|
|
|
|
scalar mass1 =
|
|
updateMassFractions(mass0, dMassGas, dMassLiquid, dMassSolid);
|
|
|
|
|
|
|
|
// Heat transfer
|
|
// ~~~~~~~~~~~~~
|
|
|
|
// Calculate new particle temperature
|
|
scalar Sph = 0.0;
|
|
scalar T1 =
|
|
this->calcHeatTransfer
|
|
(
|
|
td,
|
|
dt,
|
|
cellI,
|
|
Res,
|
|
Prs,
|
|
kappas,
|
|
d0,
|
|
rho0,
|
|
T0,
|
|
Cp0,
|
|
NCpW,
|
|
Sh,
|
|
dhsTrans,
|
|
Sph
|
|
);
|
|
|
|
|
|
// Motion
|
|
// ~~~~~~
|
|
|
|
// Calculate new particle velocity
|
|
scalar Spu = 0;
|
|
vector U1 =
|
|
this->calcVelocity
|
|
(
|
|
td,
|
|
dt,
|
|
cellI,
|
|
Res,
|
|
mus,
|
|
d0,
|
|
U0,
|
|
rho0,
|
|
0.5*(mass0 + mass1),
|
|
Su,
|
|
dUTrans,
|
|
Spu
|
|
);
|
|
|
|
|
|
// Accumulate carrier phase source terms
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
if (td.cloud().solution().coupled())
|
|
{
|
|
// Transfer mass lost from particle to carrier mass source
|
|
forAll(YGas_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(GAS, i);
|
|
td.cloud().rhoTrans(gid)[cellI] += np0*dMassGas[i];
|
|
}
|
|
forAll(YLiquid_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(LIQ, i);
|
|
td.cloud().rhoTrans(gid)[cellI] += np0*dMassLiquid[i];
|
|
}
|
|
/*
|
|
// No mapping between solid components and carrier phase
|
|
forAll(YSolid_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(SLD, i);
|
|
td.cloud().rhoTrans(gid)[cellI] += np0*dMassSolid[i];
|
|
}
|
|
*/
|
|
forAll(dMassSRCarrier, i)
|
|
{
|
|
td.cloud().rhoTrans(i)[cellI] += np0*dMassSRCarrier[i];
|
|
}
|
|
|
|
// Update momentum transfer
|
|
td.cloud().UTrans()[cellI] += np0*dUTrans;
|
|
|
|
// Update momentum transfer coefficient
|
|
td.cloud().UCoeff()[cellI] += np0*Spu;
|
|
|
|
// Update sensible enthalpy transfer
|
|
td.cloud().hsTrans()[cellI] += np0*dhsTrans;
|
|
|
|
// Update sensible enthalpy coefficient
|
|
td.cloud().hsCoeff()[cellI] += np0*Sph;
|
|
}
|
|
|
|
|
|
// Remove the particle when mass falls below minimum threshold
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
if (mass1 < td.cloud().constProps().minParticleMass())
|
|
{
|
|
td.keepParticle = false;
|
|
|
|
if (td.cloud().solution().coupled())
|
|
{
|
|
// Absorb parcel into carrier phase
|
|
forAll(YGas_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(GAS, i);
|
|
td.cloud().rhoTrans(gid)[cellI] += np0*mass1*YMix[GAS]*YGas_[i];
|
|
}
|
|
forAll(YLiquid_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(LIQ, i);
|
|
td.cloud().rhoTrans(gid)[cellI] +=
|
|
np0*mass1*YMix[LIQ]*YLiquid_[i];
|
|
}
|
|
/*
|
|
// No mapping between solid components and carrier phase
|
|
forAll(YSolid_, i)
|
|
{
|
|
label gid = composition.localToGlobalCarrierId(SLD, i);
|
|
td.cloud().rhoTrans(gid)[cellI] +=
|
|
np0*mass1*YMix[SLD]*YSolid_[i];
|
|
}
|
|
*/
|
|
td.cloud().UTrans()[cellI] += np0*mass1*U1;
|
|
|
|
// enthalpy transfer accounted for via change in mass fractions
|
|
}
|
|
}
|
|
|
|
|
|
// Set new particle properties
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
else
|
|
{
|
|
this->Cp_ = CpEff(td, pc, T1, idG, idL, idS);
|
|
this->T_ = T1;
|
|
this->U_ = U1;
|
|
|
|
// Update particle density or diameter
|
|
if (td.cloud().constProps().constantVolume())
|
|
{
|
|
this->rho_ = mass1/this->volume();
|
|
}
|
|
else
|
|
{
|
|
this->d_ = cbrt(mass1/this->rho_*6.0/pi);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
void Foam::ReactingMultiphaseParcel<ParcelType>::calcDevolatilisation
|
|
(
|
|
TrackData& td,
|
|
const scalar dt,
|
|
const scalar Ts,
|
|
const scalar d,
|
|
const scalar T,
|
|
const scalar mass,
|
|
const scalar mass0,
|
|
const scalarField& YGasEff,
|
|
bool& canCombust,
|
|
scalarField& dMassDV,
|
|
scalar& Sh,
|
|
scalar& N,
|
|
scalar& NCpW,
|
|
scalarField& Cs
|
|
) const
|
|
{
|
|
// Check that model is active, and that the parcel temperature is
|
|
// within necessary limits for devolatilisation to occur
|
|
if
|
|
(
|
|
!td.cloud().devolatilisation().active()
|
|
|| T < td.cloud().constProps().Tvap()
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
typedef typename TrackData::cloudType::reactingCloudType reactingCloudType;
|
|
const CompositionModel<reactingCloudType>& composition =
|
|
td.cloud().composition();
|
|
|
|
|
|
// Total mass of volatiles evolved
|
|
td.cloud().devolatilisation().calculate
|
|
(
|
|
dt,
|
|
mass0,
|
|
mass,
|
|
T,
|
|
YGasEff,
|
|
canCombust,
|
|
dMassDV
|
|
);
|
|
|
|
scalar dMassTot = sum(dMassDV);
|
|
|
|
td.cloud().addToMassDevolatilisation(this->nParticle_*dMassTot);
|
|
|
|
Sh -= dMassTot*td.cloud().constProps().LDevol()/dt;
|
|
|
|
// Molar average molecular weight of carrier mix
|
|
const scalar Wc = this->rhoc_*specie::RR*this->Tc_/this->pc_;
|
|
|
|
// Update molar emissions
|
|
forAll(dMassDV, i)
|
|
{
|
|
// Note: hardcoded gaseous diffusivities for now
|
|
// TODO: add to carrier thermo
|
|
const scalar beta = sqr(cbrt(15.0) + cbrt(15.0));
|
|
const label id = composition.localToGlobalCarrierId(GAS, i);
|
|
const scalar Cp = composition.carrier().Cp(id, Ts);
|
|
const scalar W = composition.carrier().W(id);
|
|
const scalar Ni = dMassDV[i]/(this->areaS(d)*dt*W);
|
|
|
|
// Dab calc'd using API vapour mass diffusivity function
|
|
const scalar Dab =
|
|
3.6059e-3*(pow(1.8*Ts, 1.75))*sqrt(1.0/W + 1.0/Wc)/(this->pc_*beta);
|
|
|
|
N += Ni;
|
|
NCpW += Ni*Cp*W;
|
|
Cs[id] += Ni*d/(2.0*Dab);
|
|
}
|
|
}
|
|
|
|
|
|
template<class ParcelType>
|
|
template<class TrackData>
|
|
void Foam::ReactingMultiphaseParcel<ParcelType>::calcSurfaceReactions
|
|
(
|
|
TrackData& td,
|
|
const scalar dt,
|
|
const label cellI,
|
|
const scalar d,
|
|
const scalar T,
|
|
const scalar mass,
|
|
const bool canCombust,
|
|
const scalar N,
|
|
const scalarField& YMix,
|
|
const scalarField& YGas,
|
|
const scalarField& YLiquid,
|
|
const scalarField& YSolid,
|
|
scalarField& dMassSRGas,
|
|
scalarField& dMassSRLiquid,
|
|
scalarField& dMassSRSolid,
|
|
scalarField& dMassSRCarrier,
|
|
scalar& Sh,
|
|
scalar& dhsTrans
|
|
) const
|
|
{
|
|
// Check that model is active
|
|
if (!td.cloud().surfaceReaction().active() || !canCombust)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Update surface reactions
|
|
const scalar hReaction = td.cloud().surfaceReaction().calculate
|
|
(
|
|
dt,
|
|
cellI,
|
|
d,
|
|
T,
|
|
this->Tc_,
|
|
this->pc_,
|
|
this->rhoc_,
|
|
mass,
|
|
YGas,
|
|
YLiquid,
|
|
YSolid,
|
|
YMix,
|
|
N,
|
|
dMassSRGas,
|
|
dMassSRLiquid,
|
|
dMassSRSolid,
|
|
dMassSRCarrier
|
|
);
|
|
|
|
td.cloud().addToMassSurfaceReaction
|
|
(
|
|
this->nParticle_
|
|
*(sum(dMassSRGas) + sum(dMassSRLiquid) + sum(dMassSRSolid))
|
|
);
|
|
|
|
const scalar xsi = min(T/5000.0, 1.0);
|
|
const scalar coeff =
|
|
(1.0 - xsi*xsi)*td.cloud().constProps().hRetentionCoeff();
|
|
|
|
Sh += coeff*hReaction/dt;
|
|
|
|
dhsTrans += (1.0 - coeff)*hReaction;
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
|
|
|
template<class ParcelType>
|
|
Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
|
|
(
|
|
const ReactingMultiphaseParcel<ParcelType>& p
|
|
)
|
|
:
|
|
ParcelType(p),
|
|
YGas_(p.YGas_),
|
|
YLiquid_(p.YLiquid_),
|
|
YSolid_(p.YSolid_)
|
|
{}
|
|
|
|
|
|
template<class ParcelType>
|
|
Foam::ReactingMultiphaseParcel<ParcelType>::ReactingMultiphaseParcel
|
|
(
|
|
const ReactingMultiphaseParcel<ParcelType>& p,
|
|
const polyMesh& mesh
|
|
)
|
|
:
|
|
ParcelType(p, mesh),
|
|
YGas_(p.YGas_),
|
|
YLiquid_(p.YLiquid_),
|
|
YSolid_(p.YSolid_)
|
|
{}
|
|
|
|
|
|
// * * * * * * * * * * * * * * IOStream operators * * * * * * * * * * * * * //
|
|
|
|
#include "ReactingMultiphaseParcelIO.C"
|
|
|
|
// ************************************************************************* //
|