Files
Will Bainbridge cebc401534 reacting*EulerFoam: populationBalanceModel: Improvements to updates of mass transfer rate and sources
The update of mass transfer rates in the population balance system is
now done at the same time as other source terms. This benefits
synchronisation of the mass transfer rate and the source terms and
prevents the system converging to an incorrect state.

Patch contributed by VTT Technical Research Centre of Finland Ltd and
Institute of Fluid Dynamics, Helmholtz-Zentrum Dresden - Rossendorf (HZDR).
2019-11-14 11:13:19 +00:00

1371 lines
32 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2017-2019 OpenFOAM Foundation
\\/ 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 "populationBalanceModel.H"
#include "coalescenceModel.H"
#include "breakupModel.H"
#include "binaryBreakupModel.H"
#include "driftModel.H"
#include "nucleationModel.H"
#include "phaseSystem.H"
#include "surfaceTensionModel.H"
#include "fvmDdt.H"
#include "fvcDdt.H"
#include "fvmSup.H"
#include "fvcSup.H"
#include "fvcDiv.H"
#include "phaseCompressibleTurbulenceModel.H"
#include "shapeModel.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace diameterModels
{
defineTypeNameAndDebug(populationBalanceModel, 0);
}
}
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
void Foam::diameterModels::populationBalanceModel::registerVelocityGroups()
{
forAll(fluid_.phases(), phasei)
{
if (isA<velocityGroup>(fluid_.phases()[phasei].dPtr()()))
{
const velocityGroup& velGroup =
refCast<const velocityGroup>(fluid_.phases()[phasei].dPtr()());
if (velGroup.popBalName() == this->name())
{
velocityGroups_.resize(velocityGroups_.size() + 1);
velocityGroups_.set
(
velocityGroups_.size() - 1,
&const_cast<velocityGroup&>(velGroup)
);
forAll(velGroup.sizeGroups(), i)
{
this->registerSizeGroups
(
const_cast<sizeGroup&>(velGroup.sizeGroups()[i])
);
}
}
}
}
}
void Foam::diameterModels::populationBalanceModel::registerSizeGroups
(
sizeGroup& group
)
{
if
(
sizeGroups_.size() != 0
&&
group.x().value() <= sizeGroups_.last().x().value()
)
{
FatalErrorInFunction
<< "Size groups must be entered according to their representative"
<< " size"
<< exit(FatalError);
}
sizeGroups_.resize(sizeGroups_.size() + 1);
sizeGroups_.set(sizeGroups_.size() - 1, &group);
// Grid generation over property space
if (sizeGroups_.size() == 1)
{
// Set the first sizeGroup boundary to the representative value of
// the first sizeGroup
v_.append
(
new dimensionedScalar
(
"v",
sizeGroups_.last().x()
)
);
// Set the last sizeGroup boundary to the representative size of the
// last sizeGroup
v_.append
(
new dimensionedScalar
(
"v",
sizeGroups_.last().x()
)
);
}
else
{
// Overwrite the next-to-last sizeGroup boundary to lie halfway between
// the last two sizeGroups
v_.last() =
0.5
*(
sizeGroups_[sizeGroups_.size()-2].x()
+ sizeGroups_.last().x()
);
// Set the last sizeGroup boundary to the representative size of the
// last sizeGroup
v_.append
(
new dimensionedScalar
(
"v",
sizeGroups_.last().x()
)
);
}
delta_.append(new PtrList<dimensionedScalar>());
Su_.append
(
new volScalarField
(
IOobject
(
"Su",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(inv(dimTime), 0)
)
);
SuSp_.append
(
new volScalarField
(
IOobject
(
"SuSp",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(inv(dimTime), 0)
)
);
}
void Foam::diameterModels::populationBalanceModel::createPhasePairs()
{
forAll(velocityGroups_, i)
{
const phaseModel& phasei = velocityGroups_[i].phase();
forAll(velocityGroups_, j)
{
const phaseModel& phasej = velocityGroups_[j].phase();
if (&phasei != &phasej)
{
const phasePairKey key
(
phasei.name(),
phasej.name(),
false
);
if (!phasePairs_.found(key))
{
phasePairs_.insert
(
key,
autoPtr<phasePair>
(
new phasePair
(
phasei,
phasej
)
)
);
}
}
}
}
}
void Foam::diameterModels::populationBalanceModel::correct()
{
calcDeltas();
forAll(velocityGroups_, v)
{
velocityGroups_[v].preSolve();
}
forAll(coalescence_, model)
{
coalescence_[model].correct();
}
forAll(breakup_, model)
{
breakup_[model].correct();
breakup_[model].dsdPtr()().correct();
}
forAll(binaryBreakup_, model)
{
binaryBreakup_[model].correct();
}
forAll(drift_, model)
{
drift_[model].correct();
}
forAll(nucleation_, model)
{
nucleation_[model].correct();
}
}
void Foam::diameterModels::populationBalanceModel::
birthByCoalescence
(
const label j,
const label k
)
{
const sizeGroup& fj = sizeGroups_[j];
const sizeGroup& fk = sizeGroups_[k];
dimensionedScalar Eta;
dimensionedScalar v = fj.x() + fk.x();
for (label i = j; i < sizeGroups_.size(); i++)
{
// Calculate fraction for intra-interval events
Eta = eta(i, v);
if (Eta.value() == 0) continue;
const sizeGroup& fi = sizeGroups_[i];
// Avoid double counting of events
if (j == k)
{
Sui_ =
0.5*fi.x()*coalescenceRate_()*Eta
*fj*fj.phase()/fj.x()
*fk*fk.phase()/fk.x();
}
else
{
Sui_ =
fi.x()*coalescenceRate_()*Eta
*fj*fj.phase()/fj.x()
*fk*fk.phase()/fk.x();
}
Su_[i] += Sui_;
const phasePairKey pairij
(
fi.phase().name(),
fj.phase().name()
);
if (pDmdt_.found(pairij))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
);
pDmdt_[pairij]->ref() += dmdtSign*fj.x()/v*Sui_*fi.phase().rho();
}
const phasePairKey pairik
(
fi.phase().name(),
fk.phase().name()
);
if (pDmdt_.found(pairik))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pairik).key(), pairik)
);
pDmdt_[pairik]->ref() += dmdtSign*fk.x()/v*Sui_*fi.phase().rho();
}
sizeGroups_[i].shapeModelPtr()->addCoalescence(Sui_, fj, fk);
}
}
void Foam::diameterModels::populationBalanceModel::
deathByCoalescence
(
const label i,
const label j
)
{
const sizeGroup& fi = sizeGroups_[i];
const sizeGroup& fj = sizeGroups_[j];
SuSp_[i] += coalescenceRate_()*fi.phase()*fj*fj.phase()/fj.x();
if (i != j)
{
SuSp_[j] += coalescenceRate_()*fj.phase()*fi*fi.phase()/fi.x();
}
}
void Foam::diameterModels::populationBalanceModel::
birthByBreakup
(
const label k,
const label model
)
{
const sizeGroup& fk = sizeGroups_[k];
for (label i = 0; i <= k; i++)
{
const sizeGroup& fi = sizeGroups_[i];
Sui_ =
fi.x()*breakupRate_()*breakup_[model].dsdPtr()().nik(i, k)
*fk*fk.phase()/fk.x();
Su_[i] += Sui_;
const phasePairKey pair
(
fi.phase().name(),
fk.phase().name()
);
if (pDmdt_.found(pair))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pair).key(), pair)
);
pDmdt_[pair]->ref() += dmdtSign*Sui_*fi.phase().rho();
}
sizeGroups_[i].shapeModelPtr()->addBreakup(Sui_, fk);
}
}
void Foam::diameterModels::populationBalanceModel::deathByBreakup(const label i)
{
const sizeGroup& fi = sizeGroups_[i];
SuSp_[i] += breakupRate_()*fi.phase();
}
void Foam::diameterModels::populationBalanceModel::calcDeltas()
{
forAll(sizeGroups_, i)
{
if (delta_[i].empty())
{
for (label j = 0; j <= sizeGroups_.size() - 1; j++)
{
delta_[i].append
(
new dimensionedScalar
(
"delta",
dimVolume,
v_[i+1].value() - v_[i].value()
)
);
if
(
v_[i].value() < 0.5*sizeGroups_[j].x().value()
&&
0.5*sizeGroups_[j].x().value() < v_[i+1].value()
)
{
delta_[i][j] = mag(0.5*sizeGroups_[j].x() - v_[i]);
}
else if (0.5*sizeGroups_[j].x().value() <= v_[i].value())
{
delta_[i][j].value() = 0;
}
}
}
}
}
void Foam::diameterModels::populationBalanceModel::
birthByBinaryBreakup
(
const label i,
const label j
)
{
const sizeGroup& fj = sizeGroups_[j];
const sizeGroup& fi = sizeGroups_[i];
Sui_ = fi.x()*binaryBreakupRate_()*delta_[i][j]*fj*fj.phase()/fj.x();
Su_[i] += Sui_;
sizeGroups_[i].shapeModelPtr()->addBreakup(Sui_, fj);
const phasePairKey pairij
(
fi.phase().name(),
fj.phase().name()
);
if (pDmdt_.found(pairij))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
);
pDmdt_[pairij]->ref() += dmdtSign*Sui_*fi.phase().rho();
}
dimensionedScalar Eta;
dimensionedScalar v = fj.x() - fi.x();
for (label k = 0; k <= j; k++)
{
// Calculate fraction for intra-interval events
Eta = eta(k, v);
if (Eta.value() == 0) continue;
const sizeGroup& fk = sizeGroups_[k];
volScalarField& Suk = Sui_;
Suk =
sizeGroups_[k].x()*binaryBreakupRate_()*delta_[i][j]*Eta
*fj*fj.phase()/fj.x();
Su_[k] += Suk;
const phasePairKey pairkj
(
fk.phase().name(),
fj.phase().name()
);
if (pDmdt_.found(pairkj))
{
const scalar dmdtSign
(
Pair<word>::compare
(
pDmdt_.find(pairkj).key(),
pairkj
)
);
pDmdt_[pairkj]->ref() += dmdtSign*Suk*fi.phase().rho();
}
sizeGroups_[i].shapeModelPtr()->addBreakup(Suk, fj);
}
}
void Foam::diameterModels::populationBalanceModel::
deathByBinaryBreakup
(
const label j,
const label i
)
{
const volScalarField& alphai = sizeGroups_[i].phase();
SuSp_[i] += alphai*binaryBreakupRate_()*delta_[j][i];
}
void Foam::diameterModels::populationBalanceModel::drift
(
const label i,
driftModel& model
)
{
model.addToDriftRate(driftRate_(), i);
const sizeGroup& fp = sizeGroups_[i];
if (i == 0)
{
rx_() = pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x();
}
else if (i == sizeGroups_.size() - 1)
{
rx_() = neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
}
else
{
rx_() =
pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x()
+ neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
}
SuSp_[i] +=
(neg(1 - rx_()) + neg(rx_() - rx_()/(1 - rx_())))*driftRate_()
*fp.phase()/((rx_() - 1)*fp.x());
rx_() = Zero;
rdx_() = Zero;
if (i == sizeGroups_.size() - 2)
{
rx_() = pos(driftRate_())*sizeGroups_[i+1].x()/sizeGroups_[i].x();
rdx_() =
pos(driftRate_())
*(sizeGroups_[i+1].x() - sizeGroups_[i].x())
/(sizeGroups_[i].x() - sizeGroups_[i-1].x());
}
else if (i < sizeGroups_.size() - 2)
{
rx_() = pos(driftRate_())*sizeGroups_[i+2].x()/sizeGroups_[i+1].x();
rdx_() =
pos(driftRate_())
*(sizeGroups_[i+2].x() - sizeGroups_[i+1].x())
/(sizeGroups_[i+1].x() - sizeGroups_[i].x());
}
if (i == 1)
{
rx_() += neg(driftRate_())*sizeGroups_[i-1].x()/sizeGroups_[i].x();
rdx_() +=
neg(driftRate_())
*(sizeGroups_[i].x() - sizeGroups_[i-1].x())
/(sizeGroups_[i+1].x() - sizeGroups_[i].x());
}
else if (i > 1)
{
rx_() += neg(driftRate_())*sizeGroups_[i-2].x()/sizeGroups_[i-1].x();
rdx_() +=
neg(driftRate_())
*(sizeGroups_[i-1].x() - sizeGroups_[i-2].x())
/(sizeGroups_[i].x() - sizeGroups_[i-1].x());
}
if (i != sizeGroups_.size() - 1)
{
const sizeGroup& fe = sizeGroups_[i+1];
volScalarField& Sue = Sui_;
Sue =
pos(driftRate_())*driftRate_()*rdx_()
*fp*fp.phase()/fp.x()
/(rx_() - 1);
Su_[i+1] += Sue;
const phasePairKey pairij
(
fp.phase().name(),
fe.phase().name()
);
if (pDmdt_.found(pairij))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pairij).key(), pairij)
);
pDmdt_[pairij]->ref() -= dmdtSign*Sue*fp.phase().rho();
}
sizeGroups_[i+1].shapeModelPtr()->addDrift(Sue, fp, model);
}
if (i != 0)
{
const sizeGroup& fw = sizeGroups_[i-1];
volScalarField& Suw = Sui_;
Suw =
neg(driftRate_())*driftRate_()*rdx_()
*fp*fp.phase()/fp.x()
/(rx_() - 1);
Su_[i-1] += Suw;
const phasePairKey pairih
(
fp.phase().name(),
fw.phase().name()
);
if (pDmdt_.found(pairih))
{
const scalar dmdtSign
(
Pair<word>::compare(pDmdt_.find(pairih).key(), pairih)
);
pDmdt_[pairih]->ref() -= dmdtSign*Suw*fp.phase().rho();
}
sizeGroups_[i-1].shapeModelPtr()->addDrift(Suw, fp, model);
}
}
void Foam::diameterModels::populationBalanceModel::
nucleation
(
const label i,
nucleationModel& model
)
{
const sizeGroup& fi = sizeGroups_[i];
model.addToNucleationRate(nucleationRate_(), i);
Sui_ = sizeGroups_[i].x()*nucleationRate_();
Su_[i] += Sui_;
sizeGroups_[i].shapeModelPtr()->addNucleation(Sui_, fi, model);
}
void Foam::diameterModels::populationBalanceModel::sources()
{
forAll(sizeGroups_, i)
{
sizeGroups_[i].shapeModelPtr()->reset();
Su_[i] = Zero;
SuSp_[i] = Zero;
}
forAllConstIter
(
phasePairTable,
phasePairs(),
phasePairIter
)
{
pDmdt_(phasePairIter())->ref() = Zero;
}
// Since the calculation of the rates is computationally expensive,
// they are calculated once for each sizeGroup pair and inserted into source
// terms as required
forAll(sizeGroups_, i)
{
if (coalescence_.size() != 0)
{
for (label j = 0; j <= i; j++)
{
coalescenceRate_() = Zero;
forAll(coalescence_, model)
{
coalescence_[model].addToCoalescenceRate
(
coalescenceRate_(),
i,
j
);
}
birthByCoalescence(i, j);
deathByCoalescence(i, j);
}
}
if (breakup_.size() != 0)
{
forAll(breakup_, model)
{
breakup_[model].setBreakupRate(breakupRate_(), i);
birthByBreakup(i, model);
deathByBreakup(i);
}
}
if (binaryBreakup_.size() != 0)
{
label j = 0;
while (delta_[j][i].value() != 0)
{
binaryBreakupRate_() = Zero;
forAll(binaryBreakup_, model)
{
binaryBreakup_[model].addToBinaryBreakupRate
(
binaryBreakupRate_(),
j,
i
);
}
birthByBinaryBreakup(j, i);
deathByBinaryBreakup(j, i);
j++;
}
}
if (drift_.size() != 0)
{
forAll(drift_, model)
{
driftRate_() = Zero;
drift(i, drift_[model]);
}
}
if (nucleation_.size() != 0)
{
forAll(nucleation_, model)
{
nucleationRate_() = Zero;
nucleation(i, nucleation_[model]);
}
}
}
}
void Foam::diameterModels::populationBalanceModel::dmdt()
{
forAll(velocityGroups_, v)
{
velocityGroup& velGroup = velocityGroups_[v];
velGroup.dmdtRef() = Zero;
forAll(sizeGroups_, i)
{
if (&sizeGroups_[i].phase() == &velGroup.phase())
{
sizeGroup& fi = sizeGroups_[i];
velGroup.dmdtRef() += fi.phase().rho()*(Su_[i] - SuSp_[i]*fi);
}
}
}
}
void Foam::diameterModels::populationBalanceModel::calcAlphas()
{
alphas_() = Zero;
forAll(velocityGroups_, v)
{
const phaseModel& phase = velocityGroups_[v].phase();
alphas_() += max(phase, phase.residualAlpha());
}
}
Foam::tmp<Foam::volScalarField>
Foam::diameterModels::populationBalanceModel::calcDsm()
{
tmp<volScalarField> tInvDsm
(
volScalarField::New
(
"invDsm",
mesh_,
dimensionedScalar(inv(dimLength), Zero)
)
);
volScalarField& invDsm = tInvDsm.ref();
forAll(velocityGroups_, v)
{
const phaseModel& phase = velocityGroups_[v].phase();
invDsm += max(phase, phase.residualAlpha())/(phase.d()*alphas_());
}
return 1.0/tInvDsm;
}
void Foam::diameterModels::populationBalanceModel::calcVelocity()
{
U_() = Zero;
forAll(velocityGroups_, v)
{
const phaseModel& phase = velocityGroups_[v].phase();
U_() += max(phase, phase.residualAlpha())*phase.U()/alphas_();
}
}
bool Foam::diameterModels::populationBalanceModel::updateSources()
{
const bool result = sourceUpdateCounter_ % sourceUpdateInterval() == 0;
++ sourceUpdateCounter_;
return result;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::diameterModels::populationBalanceModel::populationBalanceModel
(
const phaseSystem& fluid,
const word& name,
HashPtrTable<volScalarField, phasePairKey, phasePairKey::hash>& pDmdt
)
:
regIOobject
(
IOobject
(
name,
fluid.time().constant(),
fluid.mesh()
)
),
fluid_(fluid),
pDmdt_(pDmdt),
mesh_(fluid_.mesh()),
name_(name),
dict_
(
fluid_.subDict("populationBalanceCoeffs").subDict(name_)
),
pimple_(mesh_.lookupObject<pimpleControl>("solutionControl")),
continuousPhase_
(
mesh_.lookupObject<phaseModel>
(
IOobject::groupName("alpha", dict_.lookup("continuousPhase"))
)
),
velocityGroups_(),
sizeGroups_(),
v_(),
delta_(),
Su_(),
SuSp_(),
Sui_
(
IOobject
(
"Sui",
mesh_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(inv(dimTime), Zero)
),
coalescence_
(
dict_.lookup("coalescenceModels"),
coalescenceModel::iNew(*this)
),
coalescenceRate_(),
breakup_
(
dict_.lookup("breakupModels"),
breakupModel::iNew(*this)
),
breakupRate_(),
binaryBreakup_
(
dict_.lookup("binaryBreakupModels"),
binaryBreakupModel::iNew(*this)
),
binaryBreakupRate_(),
drift_
(
dict_.lookup("driftModels"),
driftModel::iNew(*this)
),
driftRate_(),
rx_(),
rdx_(),
nucleation_
(
dict_.lookup("nucleationModels"),
nucleationModel::iNew(*this)
),
nucleationRate_(),
alphas_(),
dsm_(),
U_(),
sourceUpdateCounter_
(
(mesh_.time().timeIndex()*nCorr())%sourceUpdateInterval()
)
{
this->registerVelocityGroups();
this->createPhasePairs();
if (sizeGroups_.size() < 3)
{
FatalErrorInFunction
<< "The populationBalance " << name_
<< " requires a minimum number of three sizeGroups to be specified."
<< exit(FatalError);
}
if (coalescence_.size() != 0)
{
coalescenceRate_.set
(
new volScalarField
(
IOobject
(
"coalescenceRate",
mesh_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(dimVolume/dimTime, Zero)
)
);
}
if (breakup_.size() != 0)
{
breakupRate_.set
(
new volScalarField
(
IOobject
(
"breakupRate",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(inv(dimTime), Zero)
)
);
}
if (binaryBreakup_.size() != 0)
{
binaryBreakupRate_.set
(
new volScalarField
(
IOobject
(
"binaryBreakupRate",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar
(
"binaryBreakupRate",
inv(dimVolume*dimTime),
Zero
)
)
);
}
if (drift_.size() != 0)
{
driftRate_.set
(
new volScalarField
(
IOobject
(
"driftRate",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(dimVolume/dimTime, Zero)
)
);
rx_.set
(
new volScalarField
(
IOobject
(
"r",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(dimless, Zero)
)
);
rdx_.set
(
new volScalarField
(
IOobject
(
"r",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar(dimless, Zero)
)
);
}
if (nucleation_.size() != 0)
{
nucleationRate_.set
(
new volScalarField
(
IOobject
(
"nucleationRate",
fluid_.time().timeName(),
mesh_
),
mesh_,
dimensionedScalar
(
"nucleationRate",
inv(dimTime*dimVolume),
Zero
)
)
);
}
if (velocityGroups_.size() > 1)
{
alphas_.set
(
new volScalarField
(
IOobject
(
IOobject::groupName("alpha", this->name()),
fluid_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh_,
dimensionedScalar(dimless, Zero)
)
);
dsm_.set
(
new volScalarField
(
IOobject
(
IOobject::groupName("d", this->name()),
fluid_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh_,
dimensionedScalar(dimLength, Zero)
)
);
U_.set
(
new volVectorField
(
IOobject
(
IOobject::groupName("U", this->name()),
fluid_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh_,
dimensionedVector(dimVelocity, Zero)
)
);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::diameterModels::populationBalanceModel::~populationBalanceModel()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::autoPtr<Foam::diameterModels::populationBalanceModel>
Foam::diameterModels::populationBalanceModel::clone() const
{
notImplemented("populationBalance::clone() const");
return autoPtr<populationBalanceModel>(nullptr);
}
bool Foam::diameterModels::populationBalanceModel::writeData(Ostream& os) const
{
return os.good();
}
const Foam::dimensionedScalar
Foam::diameterModels::populationBalanceModel::eta
(
const label i,
const dimensionedScalar& v
) const
{
const dimensionedScalar& x0 = sizeGroups_[0].x();
const dimensionedScalar& xi = sizeGroups_[i].x();
const dimensionedScalar& xm = sizeGroups_.last().x();
dimensionedScalar lowerBoundary(x0);
dimensionedScalar upperBoundary(xm);
if (i != 0) lowerBoundary = sizeGroups_[i-1].x();
if (i != sizeGroups_.size() - 1) upperBoundary = sizeGroups_[i+1].x();
if ((i == 0 && v < x0) || (i == sizeGroups_.size() - 1 && v > xm))
{
return v/xi;
}
else if (v < lowerBoundary || v > upperBoundary)
{
return 0;
}
else if (v.value() == xi.value())
{
return 1.0;
}
else if (v > xi)
{
return (upperBoundary - v)/(upperBoundary - xi);
}
else
{
return (v - lowerBoundary)/(xi - lowerBoundary);
}
}
const Foam::tmp<Foam::volScalarField>
Foam::diameterModels::populationBalanceModel::sigmaWithContinuousPhase
(
const phaseModel& dispersedPhase
) const
{
return
fluid_.lookupSubModel<surfaceTensionModel>
(
phasePair(dispersedPhase, continuousPhase_)
).sigma();
}
const Foam::phaseCompressibleTurbulenceModel&
Foam::diameterModels::populationBalanceModel::continuousTurbulence() const
{
return
mesh_.lookupObject<phaseCompressibleTurbulenceModel>
(
IOobject::groupName
(
turbulenceModel::propertiesName,
continuousPhase_.name()
)
);
}
void Foam::diameterModels::populationBalanceModel::solve()
{
const dictionary& solutionControls = mesh_.solverDict(name_);
bool solveOnFinalIterOnly =
solutionControls.lookupOrDefault<bool>("solveOnFinalIterOnly", false);
if (!solveOnFinalIterOnly || pimple_.finalPimpleIter())
{
const label nCorr = this->nCorr();
const scalar tolerance =
readScalar(solutionControls.lookup("tolerance"));
if (nCorr > 0)
{
correct();
}
int iCorr = 0;
scalar maxInitialResidual = 1;
while (++iCorr <= nCorr && maxInitialResidual > tolerance)
{
Info<< "populationBalance "
<< this->name()
<< ": Iteration "
<< iCorr
<< endl;
if (updateSources())
{
sources();
dmdt();
}
maxInitialResidual = 0;
forAll(sizeGroups_, i)
{
sizeGroup& fi = sizeGroups_[i];
const phaseModel& phase = fi.phase();
const volScalarField& alpha = phase;
const dimensionedScalar& residualAlpha = phase.residualAlpha();
const volScalarField& rho = phase.thermo().rho();
fvScalarMatrix sizeGroupEqn
(
fvm::ddt(alpha, rho, fi)
+ fi.VelocityGroup().mvConvection()->fvmDiv
(
phase.alphaRhoPhi(),
fi
)
+ fvm::SuSp
(
fi.VelocityGroup().dmdt()
- phase.continuityErrorFlow(),
fi
)
==
fvc::Su(Su_[i]*rho, fi)
- fvm::SuSp(SuSp_[i]*rho, fi)
+ fvc::ddt(residualAlpha*rho, fi)
- fvm::ddt(residualAlpha*rho, fi)
);
sizeGroupEqn.relax();
maxInitialResidual = max
(
sizeGroupEqn.solve().initialResidual(),
maxInitialResidual
);
}
}
if (nCorr > 0)
{
forAll(sizeGroups_, i)
{
sizeGroups_[i].correct();
}
forAll(velocityGroups_, i)
{
velocityGroups_[i].postSolve();
}
}
if (velocityGroups_.size() > 1)
{
calcAlphas();
dsm_() = calcDsm();
calcVelocity();
}
volScalarField fAlpha0
(
sizeGroups_.first()*sizeGroups_.first().phase()
);
volScalarField fAlphaN
(
sizeGroups_.last()*sizeGroups_.last().phase()
);
Info<< this->name() << " sizeGroup phase fraction first, last = "
<< fAlpha0.weightedAverage(this->mesh().V()).value()
<< ' ' << fAlphaN.weightedAverage(this->mesh().V()).value()
<< endl;
}
}
// ************************************************************************* //