multiphaseEulerFoam: revised sizeDistribution functionObject
Following the addition of the new moments functionObject, all related
functionality was removed from sizeDistribution.
In its revised version, sizeDistribution allows for different kinds of
weighted region averaging in case of field-dependent representative
particle properties.
A packaged function has also been added to allow for command line solver
post-processing.
For example, the following function object specification returns the
volume-based number density function:
numberDensity
{
type sizeDistribution;
libs ("libmultiphaseEulerFoamFunctionObjects.so");
writeControl writeTime;
populationBalance bubbles;
functionType numberDensity;
coordinateType volume;
setFormat raw;
}
The same can be achieved using a packaged function:
#includeFunc sizeDistribution
(
populationBalance=bubbles,
functionType=numberDensity,
coordinateType=volume,
funcName=numberDensity
)
Or on the command line:
multiphaseEulerFoam -postProcess -func "
sizeDistribution
(
populationBalance=bubbles,
functionType=numberDensity,
coordinateType=volume,
funcName=numberDensity
)"
Patch contributed by Institute of Fluid Dynamics,
Helmholtz-Zentrum Dresden - Rossendorf (HZDR)
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration | Website: https://openfoam.org
|
||||
\\ / A nd | Copyright (C) 2017-2021 OpenFOAM Foundation
|
||||
\\ / A nd | Copyright (C) 2017-2022 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
@ -24,10 +24,7 @@ License
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "sizeDistribution.H"
|
||||
#include "Time.H"
|
||||
#include "fvMesh.H"
|
||||
#include "addToRunTimeSelectionTable.H"
|
||||
#include "mathematicalConstants.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
@ -45,19 +42,21 @@ const char*
|
||||
Foam::NamedEnum
|
||||
<
|
||||
Foam::functionObjects::sizeDistribution::functionType,
|
||||
4
|
||||
6
|
||||
>::names[] =
|
||||
{
|
||||
"moments",
|
||||
"standardDeviation",
|
||||
"number",
|
||||
"volume"
|
||||
"numberConcentration",
|
||||
"numberDensity",
|
||||
"volumeConcentration",
|
||||
"volumeDensity",
|
||||
"areaConcentration",
|
||||
"areaDensity"
|
||||
};
|
||||
|
||||
const Foam::NamedEnum
|
||||
<
|
||||
Foam::functionObjects::sizeDistribution::functionType,
|
||||
4
|
||||
6
|
||||
> Foam::functionObjects::sizeDistribution::functionTypeNames_;
|
||||
|
||||
template<>
|
||||
@ -80,10 +79,122 @@ const Foam::NamedEnum
|
||||
4
|
||||
> Foam::functionObjects::sizeDistribution::coordinateTypeNames_;
|
||||
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
template<>
|
||||
const char* NamedEnum
|
||||
<
|
||||
Foam::functionObjects::sizeDistribution::weightType,
|
||||
4
|
||||
>::names[] =
|
||||
{
|
||||
"numberConcentration",
|
||||
"volumeConcentration",
|
||||
"areaConcentration",
|
||||
"cellVolume"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const Foam::NamedEnum
|
||||
<
|
||||
Foam::functionObjects::sizeDistribution::weightType,
|
||||
4
|
||||
>
|
||||
Foam::functionObjects::sizeDistribution::weightTypeNames_;
|
||||
|
||||
using Foam::constant::mathematical::pi;
|
||||
|
||||
|
||||
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
|
||||
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
|
||||
|
||||
Foam::word Foam::functionObjects::sizeDistribution::functionTypeSymbolicName()
|
||||
{
|
||||
word functionTypeSymbolicName(word::null);
|
||||
|
||||
switch (functionType_)
|
||||
{
|
||||
case functionType::numberConcentration:
|
||||
{
|
||||
functionTypeSymbolicName = "N";
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::numberDensity:
|
||||
{
|
||||
functionTypeSymbolicName = "n";
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::volumeConcentration:
|
||||
{
|
||||
functionTypeSymbolicName = "V";
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::volumeDensity:
|
||||
{
|
||||
functionTypeSymbolicName = "v";
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::areaConcentration:
|
||||
{
|
||||
functionTypeSymbolicName = "A";
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::areaDensity:
|
||||
{
|
||||
functionTypeSymbolicName = "a";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return functionTypeSymbolicName;
|
||||
}
|
||||
|
||||
|
||||
Foam::word Foam::functionObjects::sizeDistribution::coordinateTypeSymbolicName
|
||||
(
|
||||
const coordinateType& cType
|
||||
)
|
||||
{
|
||||
word coordinateTypeSymbolicName(word::null);
|
||||
|
||||
switch (cType)
|
||||
{
|
||||
case coordinateType::volume:
|
||||
{
|
||||
coordinateTypeSymbolicName = "v";
|
||||
|
||||
break;
|
||||
}
|
||||
case coordinateType::area:
|
||||
{
|
||||
coordinateTypeSymbolicName = "a";
|
||||
|
||||
break;
|
||||
}
|
||||
case coordinateType::diameter:
|
||||
{
|
||||
coordinateTypeSymbolicName = "d";
|
||||
|
||||
break;
|
||||
}
|
||||
case coordinateType::projectedAreaDiameter:
|
||||
{
|
||||
coordinateTypeSymbolicName = "dPa";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return coordinateTypeSymbolicName;
|
||||
}
|
||||
|
||||
|
||||
Foam::tmp<Foam::scalarField>
|
||||
Foam::functionObjects::sizeDistribution::filterField
|
||||
@ -102,252 +213,120 @@ Foam::functionObjects::sizeDistribution::filterField
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::sizeDistribution::correctVolAverages()
|
||||
Foam::scalar Foam::functionObjects::sizeDistribution::averageCoordinateValue
|
||||
(
|
||||
const Foam::diameterModels::sizeGroup& fi,
|
||||
const coordinateType& cType
|
||||
)
|
||||
{
|
||||
forAll(N_, i)
|
||||
scalar averageCoordinateValue(Zero);
|
||||
|
||||
switch (cType)
|
||||
{
|
||||
const Foam::diameterModels::sizeGroup& fi = popBal_.sizeGroups()[i];
|
||||
|
||||
scalarField Ni(filterField(fi*fi.phase()/fi.x()));
|
||||
scalarField V(filterField(mesh_.V()));
|
||||
scalarField ai(filterField(fi.a()));
|
||||
scalarField di(Ni.size());
|
||||
|
||||
switch (coordinateType_)
|
||||
case coordinateType::volume:
|
||||
{
|
||||
case ctProjectedAreaDiameter:
|
||||
{
|
||||
di = sqrt(ai/pi);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
di = fi.d();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
N_[i] = gSum(V*Ni)/this->V();
|
||||
V_[i] = fi.x().value();
|
||||
a_[i] = gSum(V*ai)/this->V();
|
||||
d_[i] = gSum(V*di)/this->V();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::sizeDistribution::writeMoments()
|
||||
{
|
||||
logFiles::write();
|
||||
|
||||
Log << " writing moments of size distribution." << endl;
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
writeTime(file());
|
||||
}
|
||||
|
||||
const scalarField& bin = this->bin();
|
||||
|
||||
for (label k = 0; k <= maxOrder_; k++)
|
||||
{
|
||||
scalar result = 0;
|
||||
|
||||
forAll(N_, i)
|
||||
{
|
||||
result += pow(bin[i], k)*N_[i];
|
||||
}
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
file() << tab << result;
|
||||
}
|
||||
}
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
file() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::sizeDistribution::writeStdDev()
|
||||
{
|
||||
logFiles::write();
|
||||
|
||||
Log << " writing standard deviation of size distribution."
|
||||
<< endl;
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
writeTime(file());
|
||||
}
|
||||
|
||||
scalar stdDev = 0;
|
||||
scalar mean = 0;
|
||||
scalar var = 0;
|
||||
|
||||
const scalarField& bin = this->bin();
|
||||
|
||||
if (sum(N_) != 0)
|
||||
{
|
||||
if (geometric_)
|
||||
{
|
||||
mean = exp(sum(Foam::log(bin)*N_/sum(N_)));
|
||||
|
||||
var =
|
||||
sum(sqr(Foam::log(bin) - Foam::log(mean))
|
||||
*N_/sum(N_));
|
||||
|
||||
stdDev = exp(sqrt(var));
|
||||
}
|
||||
else
|
||||
{
|
||||
mean = sum(bin*N_/sum(N_));
|
||||
|
||||
var = sum(sqr(bin - mean)*N_/sum(N_));
|
||||
|
||||
stdDev = sqrt(var);
|
||||
}
|
||||
}
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
file() << tab << stdDev << tab << mean << tab << var << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::sizeDistribution::writeDistribution()
|
||||
{
|
||||
scalarField result(N_);
|
||||
|
||||
const scalarField& bin = this->bin();
|
||||
|
||||
switch (functionType_)
|
||||
{
|
||||
case ftNumber:
|
||||
{
|
||||
Log << " writing number distribution. "
|
||||
<< endl;
|
||||
averageCoordinateValue = fi.x().value();
|
||||
|
||||
break;
|
||||
}
|
||||
case ftVolume:
|
||||
case coordinateType::area:
|
||||
{
|
||||
Log << " writing volume distribution. "
|
||||
<< endl;
|
||||
|
||||
result *= V_;
|
||||
averageCoordinateValue =
|
||||
weightedAverage(fi.a(), fi);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case coordinateType::diameter:
|
||||
{
|
||||
averageCoordinateValue =
|
||||
weightedAverage(fi.d(), fi);
|
||||
|
||||
break;
|
||||
}
|
||||
case coordinateType::projectedAreaDiameter:
|
||||
{
|
||||
averageCoordinateValue =
|
||||
weightedAverage(sqrt(fi.a()/pi), fi);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalise_)
|
||||
return averageCoordinateValue;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::functionObjects::sizeDistribution::weightedAverage
|
||||
(
|
||||
const Foam::scalarField& fld,
|
||||
const Foam::diameterModels::sizeGroup& fi
|
||||
)
|
||||
{
|
||||
scalar weightedAverage(Zero);
|
||||
|
||||
switch (weightType_)
|
||||
{
|
||||
if(sum(result) != 0)
|
||||
case weightType::numberConcentration:
|
||||
{
|
||||
result /= sum(result);
|
||||
}
|
||||
scalarField Ni(filterField(fi*fi.phase()/fi.x().value()));
|
||||
|
||||
}
|
||||
|
||||
if (densityFunction_)
|
||||
{
|
||||
List<scalar> bndrs(N_.size() + 1);
|
||||
|
||||
bndrs.first() = bin.first();
|
||||
bndrs.last() = bin.last();
|
||||
|
||||
for (label i = 1; i < N_.size(); i++)
|
||||
{
|
||||
bndrs[i] = (bin[i]+ bin[i-1])/2.0;
|
||||
}
|
||||
|
||||
forAll(result, i)
|
||||
{
|
||||
if (geometric_)
|
||||
if (gSum(Ni) == 0)
|
||||
{
|
||||
result[i] /=
|
||||
(Foam::log(bndrs[i+1]) - Foam::log(bndrs[i]));
|
||||
weightedAverage =
|
||||
gSum(filterField(mesh_.V()*fld))/this->V();
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] /= (bndrs[i+1] - bndrs[i]);
|
||||
weightedAverage =
|
||||
gSum(Ni*filterField(fld))/gSum(Ni);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
formatterPtr_->write
|
||||
(
|
||||
file_.baseTimeDir(),
|
||||
name(),
|
||||
coordSet(true, "volume", V_),
|
||||
"area",
|
||||
a_,
|
||||
"diameter",
|
||||
d_,
|
||||
word(functionTypeNames_[functionType_])
|
||||
+ (densityFunction_ ? "Density" : "Concentration"),
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::functionObjects::sizeDistribution::writeFileHeader
|
||||
(
|
||||
const label i
|
||||
)
|
||||
{
|
||||
volRegion::writeFileHeader(*this, file());
|
||||
|
||||
writeHeaderValue
|
||||
(
|
||||
file(),
|
||||
"Coordinate",
|
||||
word(coordinateTypeNames_[coordinateType_])
|
||||
);
|
||||
|
||||
word str("Time");
|
||||
|
||||
switch (functionType_)
|
||||
{
|
||||
case ftMoments:
|
||||
case weightType::volumeConcentration:
|
||||
{
|
||||
for (label k = 0; k <= maxOrder_; k++)
|
||||
scalarField Vi(filterField(fi*fi.phase()));
|
||||
|
||||
if (gSum(Vi) == 0)
|
||||
{
|
||||
str += (" k=" + std::to_string(k));
|
||||
weightedAverage =
|
||||
gSum(filterField(mesh_.V()*fld))/this->V();
|
||||
}
|
||||
else
|
||||
{
|
||||
weightedAverage =
|
||||
gSum(Vi*filterField(fld))/gSum(Vi);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ftStdDev:
|
||||
case weightType::areaConcentration:
|
||||
{
|
||||
str += " standardDeviation mean variance";
|
||||
scalarField Ai(filterField(fi.a().ref()*fi.phase()));
|
||||
|
||||
if (gSum(Ai) == 0)
|
||||
{
|
||||
weightedAverage =
|
||||
gSum(filterField(mesh_.V()*fld))/this->V();
|
||||
}
|
||||
else
|
||||
{
|
||||
weightedAverage =
|
||||
gSum(Ai*filterField(fld))/gSum(Ai);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case weightType::cellVolume:
|
||||
{
|
||||
weightedAverage =
|
||||
gSum(filterField(mesh_.V()*fld))/this->V();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writeCommented(file(), str);
|
||||
|
||||
file() << endl;
|
||||
return weightedAverage;
|
||||
}
|
||||
|
||||
|
||||
@ -362,9 +341,8 @@ Foam::functionObjects::sizeDistribution::sizeDistribution
|
||||
:
|
||||
fvMeshFunctionObject(name, runTime, dict),
|
||||
volRegion(fvMeshFunctionObject::mesh_, dict),
|
||||
logFiles(obr_, name),
|
||||
mesh_(fvMeshFunctionObject::mesh_),
|
||||
file_(obr_, name),
|
||||
mesh_(fvMeshFunctionObject::mesh_),
|
||||
popBal_
|
||||
(
|
||||
obr_.lookupObject<Foam::diameterModels::populationBalanceModel>
|
||||
@ -374,10 +352,26 @@ Foam::functionObjects::sizeDistribution::sizeDistribution
|
||||
),
|
||||
functionType_(functionTypeNames_.read(dict.lookup("functionType"))),
|
||||
coordinateType_(coordinateTypeNames_.read(dict.lookup("coordinateType"))),
|
||||
N_(popBal_.sizeGroups().size(), 0),
|
||||
V_(popBal_.sizeGroups().size(), 0),
|
||||
a_(popBal_.sizeGroups().size(), 0),
|
||||
d_(popBal_.sizeGroups().size(), 0)
|
||||
allCoordinates_
|
||||
(
|
||||
dict.lookupOrDefault<Switch>("allCoordinates", false)
|
||||
),
|
||||
normalise_(dict.lookupOrDefault<Switch>("normalise", false)),
|
||||
logTransform_
|
||||
(
|
||||
dict.lookupOrDefaultBackwardsCompatible<Switch>
|
||||
(
|
||||
{"logTransform", "geometric"},
|
||||
false
|
||||
)
|
||||
),
|
||||
weightType_
|
||||
(
|
||||
dict.found("weightType")
|
||||
? weightTypeNames_.read(dict.lookup("weightType"))
|
||||
: weightType::numberConcentration
|
||||
),
|
||||
formatterPtr_(nullptr)
|
||||
{
|
||||
read(dict);
|
||||
}
|
||||
@ -393,17 +387,12 @@ Foam::functionObjects::sizeDistribution::~sizeDistribution()
|
||||
|
||||
bool Foam::functionObjects::sizeDistribution::read(const dictionary& dict)
|
||||
{
|
||||
Log << type() << " " << name() << ":" << nl;
|
||||
|
||||
fvMeshFunctionObject::read(dict);
|
||||
|
||||
normalise_ = dict.lookupOrDefault<Switch>("normalise", false);
|
||||
densityFunction_ = dict.lookupOrDefault<Switch>("densityFunction", false);
|
||||
geometric_ = dict.lookupOrDefault<Switch>("geometric", false);
|
||||
maxOrder_ = dict.lookupOrDefault("maxOrder", 3);
|
||||
|
||||
formatterPtr_ = setWriter::New(dict.lookup("setFormat"), dict);
|
||||
|
||||
resetName(name());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -414,43 +403,255 @@ bool Foam::functionObjects::sizeDistribution::execute()
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::sizeDistribution::end()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::sizeDistribution::write()
|
||||
{
|
||||
Log << type() << " " << name() << " write:" << nl;
|
||||
|
||||
correctVolAverages();
|
||||
const UPtrList<diameterModels::sizeGroup>& sizeGroups =
|
||||
popBal_.sizeGroups();
|
||||
|
||||
scalarField coordinateValues(sizeGroups.size());
|
||||
scalarField boundaryValues(sizeGroups.size() + 1);
|
||||
scalarField resultValues(sizeGroups.size());
|
||||
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
coordinateValues[i] = averageCoordinateValue(fi, coordinateType_);
|
||||
}
|
||||
|
||||
if
|
||||
(
|
||||
functionType_ == functionType::numberDensity
|
||||
|| functionType_ == functionType::volumeDensity
|
||||
|| functionType_ == functionType::areaDensity
|
||||
)
|
||||
{
|
||||
if (logTransform_)
|
||||
{
|
||||
boundaryValues.first() = Foam::log(coordinateValues.first());
|
||||
boundaryValues.last() = Foam::log(coordinateValues.last());
|
||||
|
||||
for (label i = 1; i < boundaryValues.size() - 1; i++)
|
||||
{
|
||||
boundaryValues[i] =
|
||||
0.5
|
||||
*(
|
||||
Foam::log(coordinateValues[i])
|
||||
+ Foam::log(coordinateValues[i-1])
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boundaryValues.first() = coordinateValues.first();
|
||||
boundaryValues.last() = coordinateValues.last();
|
||||
|
||||
for (label i = 1; i < boundaryValues.size() - 1; i++)
|
||||
{
|
||||
boundaryValues[i] =
|
||||
(coordinateValues[i] + coordinateValues[i-1])/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (functionType_)
|
||||
{
|
||||
case ftMoments:
|
||||
case functionType::numberConcentration:
|
||||
{
|
||||
writeMoments();
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum(filterField(mesh_.V()*fi*fi.phase()/fi.x()))*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ftStdDev:
|
||||
case functionType::numberDensity:
|
||||
{
|
||||
writeStdDev();
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum(filterField(mesh_.V()*fi*fi.phase()/fi.x()))*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
forAll(resultValues, i)
|
||||
{
|
||||
resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
case functionType::volumeConcentration:
|
||||
{
|
||||
writeDistribution();
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum(filterField(mesh_.V()*fi*fi.phase()))*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::volumeDensity:
|
||||
{
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum(filterField(mesh_.V()*fi*fi.phase()))*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
forAll(resultValues, i)
|
||||
{
|
||||
resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::areaConcentration:
|
||||
{
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum
|
||||
(
|
||||
filterField(mesh_.V()*fi.a().ref()*fi*fi.phase()/fi.x())
|
||||
)
|
||||
*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case functionType::areaDensity:
|
||||
{
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
resultValues[i] =
|
||||
gSum
|
||||
(
|
||||
filterField(mesh_.V()*fi.a().ref()*fi*fi.phase()/fi.x())
|
||||
)
|
||||
*this->V();
|
||||
}
|
||||
|
||||
if (normalise_ && sum(resultValues) != 0)
|
||||
{
|
||||
resultValues /= sum(resultValues);
|
||||
}
|
||||
|
||||
forAll(resultValues, i)
|
||||
{
|
||||
resultValues[i] /= (boundaryValues[i+1] - boundaryValues[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Log << endl;
|
||||
|
||||
if (allCoordinates_)
|
||||
{
|
||||
wordList otherCoordinateSymbolicNames(coordinateTypeNames_.size());
|
||||
PtrList<scalarField> otherCoordinateValues(coordinateTypeNames_.size());
|
||||
typedef NamedEnum<coordinateType, 4> namedEnumCoordinateType;
|
||||
|
||||
forAllConstIter(namedEnumCoordinateType, coordinateTypeNames_, iter)
|
||||
{
|
||||
const coordinateType cType = coordinateTypeNames_[iter.key()];
|
||||
|
||||
otherCoordinateSymbolicNames[cType] =
|
||||
coordinateTypeSymbolicName(cType);
|
||||
|
||||
otherCoordinateValues.set
|
||||
(
|
||||
cType,
|
||||
new scalarField(popBal_.sizeGroups().size())
|
||||
);
|
||||
|
||||
forAll(sizeGroups, i)
|
||||
{
|
||||
const diameterModels::sizeGroup& fi = sizeGroups[i];
|
||||
|
||||
otherCoordinateValues[cType][i] =
|
||||
averageCoordinateValue(fi, cType);
|
||||
}
|
||||
}
|
||||
|
||||
if (Pstream::master())
|
||||
{
|
||||
formatterPtr_->write
|
||||
(
|
||||
file_.baseTimeDir(),
|
||||
name(),
|
||||
coordSet
|
||||
(
|
||||
true,
|
||||
coordinateTypeSymbolicName(coordinateType_),
|
||||
coordinateValues
|
||||
),
|
||||
functionTypeSymbolicName(),
|
||||
resultValues,
|
||||
otherCoordinateSymbolicNames,
|
||||
otherCoordinateValues
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Pstream::master())
|
||||
{
|
||||
formatterPtr_->write
|
||||
(
|
||||
file_.baseTimeDir(),
|
||||
name(),
|
||||
coordSet
|
||||
(
|
||||
true,
|
||||
coordinateTypeSymbolicName(coordinateType_),
|
||||
coordinateValues
|
||||
),
|
||||
functionTypeSymbolicName(),
|
||||
resultValues
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration | Website: https://openfoam.org
|
||||
\\ / A nd | Copyright (C) 2017-2021 OpenFOAM Foundation
|
||||
\\ / A nd | Copyright (C) 2017-2022 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
@ -25,42 +25,42 @@ Class
|
||||
Foam::functionObjects::sizeDistribution
|
||||
|
||||
Description
|
||||
This function object calculates and outputs volume-averaged information
|
||||
about the size distribution of the dispersed phase, such as the number
|
||||
density function or its moments. It is designed to be used exclusively with
|
||||
the population balance modeling functionality of the multiphaseEulerFoam
|
||||
solver. It can be applied to a specific cellZone or the entire domain. The
|
||||
function type determines whether the density function and its moments are
|
||||
based on the number of dispersed phase elements in a size group or their
|
||||
total volume.
|
||||
Writes out the size distribution computed with multiphaseEulerFoam for the
|
||||
entire domain or a volume region. Requires solver post-processing.
|
||||
|
||||
The following function object specification for example returns the volume-
|
||||
based number density function:
|
||||
|
||||
Example of function object specification:
|
||||
\verbatim
|
||||
numberDensity
|
||||
{
|
||||
type sizeDistribution;
|
||||
libs ("libmultiphaseEulerFoamFunctionObjects.so");
|
||||
...
|
||||
type sizeDistribution;
|
||||
libs ("libmultiphaseEulerFoamFunctionObjects.so");
|
||||
writeControl writeTime;
|
||||
populationBalance bubbles;
|
||||
regionType cellZone;
|
||||
name zone0;
|
||||
functionType number;
|
||||
functionType numberDensity;
|
||||
coordinateType volume;
|
||||
densityFunction yes;
|
||||
setFormat raw;
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
Usage
|
||||
\table
|
||||
Property | Description | Required | Default value
|
||||
type | type name: sizeDistribution | yes |
|
||||
populationBalance | corresponding populationBalance | yes |
|
||||
functionType | number/volume/moments/stdDev | yes |
|
||||
coordinateType | used for density/moment calculation | yes |
|
||||
normalise | normalise concentrations | no | no
|
||||
densityFunction | compute densityFunction | no | no
|
||||
logBased | use log of coordinate for density | no | no
|
||||
maxOrder | maxim order of moment output | no | 3
|
||||
Property | Description | Required | Default
|
||||
populationBalance | population balance name | yes |
|
||||
functionType | function type | yes |
|
||||
coordinateType | particle property | yes |
|
||||
allCoordinates | write all coordinate values | no | false
|
||||
normalise | divide by total concentration | no | false
|
||||
logTransform | class width based on log of coordinate\\
|
||||
| no | false
|
||||
weightType | weighting in case of field-dependent particle\\
|
||||
properties | no\\
|
||||
| numberConcentration
|
||||
regionType | cellZone or all | no | all
|
||||
name | name of cellZone if required | no |
|
||||
setFormat | output format | yes |
|
||||
\endtable
|
||||
|
||||
SourceFiles
|
||||
@ -73,7 +73,6 @@ SourceFiles
|
||||
|
||||
#include "fvMeshFunctionObject.H"
|
||||
#include "volRegion.H"
|
||||
#include "logFiles.H"
|
||||
#include "populationBalanceModel.H"
|
||||
#include "writeFile.H"
|
||||
#include "setWriter.H"
|
||||
@ -92,8 +91,7 @@ namespace functionObjects
|
||||
class sizeDistribution
|
||||
:
|
||||
public fvMeshFunctionObject,
|
||||
public volRegion,
|
||||
public logFiles
|
||||
public volRegion
|
||||
{
|
||||
public:
|
||||
|
||||
@ -102,111 +100,101 @@ public:
|
||||
//- Function type enumeration
|
||||
enum functionType
|
||||
{
|
||||
ftMoments,
|
||||
ftStdDev,
|
||||
ftNumber,
|
||||
ftVolume
|
||||
numberConcentration,
|
||||
numberDensity,
|
||||
volumeConcentration,
|
||||
volumeDensity,
|
||||
areaConcentration,
|
||||
areaDensity
|
||||
};
|
||||
|
||||
//- Ordinate type names
|
||||
static const NamedEnum<functionType, 4> functionTypeNames_;
|
||||
//- Function type names
|
||||
static const NamedEnum<functionType, 6> functionTypeNames_;
|
||||
|
||||
//- Coordinate type enumeration
|
||||
enum coordinateType
|
||||
{
|
||||
ctVolume,
|
||||
ctArea,
|
||||
ctDiameter,
|
||||
ctProjectedAreaDiameter
|
||||
volume,
|
||||
area,
|
||||
diameter,
|
||||
projectedAreaDiameter
|
||||
};
|
||||
|
||||
//- Coordinate type names
|
||||
static const NamedEnum<coordinateType, 4> coordinateTypeNames_;
|
||||
|
||||
//- Enumeration for the weight types
|
||||
enum class weightType
|
||||
{
|
||||
numberConcentration,
|
||||
volumeConcentration,
|
||||
areaConcentration,
|
||||
cellVolume
|
||||
};
|
||||
|
||||
protected:
|
||||
//- Names of the weight types
|
||||
static const NamedEnum<weightType, 4> weightTypeNames_;
|
||||
|
||||
// Protected Data
|
||||
|
||||
//- Reference to fvMesh
|
||||
const fvMesh& mesh_;
|
||||
private:
|
||||
|
||||
//- File containing data for all functionTypes except moments
|
||||
// Private Data
|
||||
|
||||
//- Write file
|
||||
writeFile file_;
|
||||
|
||||
//- Output formatter
|
||||
autoPtr<setWriter> formatterPtr_;
|
||||
//- Reference to mesh
|
||||
const fvMesh& mesh_;
|
||||
|
||||
//- Reference to populationBalanceModel
|
||||
//- Reference to population balance
|
||||
const Foam::diameterModels::populationBalanceModel& popBal_;
|
||||
|
||||
//- Function to evaluate
|
||||
//- Function type
|
||||
functionType functionType_;
|
||||
|
||||
//- Abscissa type
|
||||
//- Coordinate type
|
||||
coordinateType coordinateType_;
|
||||
|
||||
//- List of volume-averaged number concentrations
|
||||
scalarField N_;
|
||||
//- Add values for all coordinate types to output
|
||||
Switch allCoordinates_;
|
||||
|
||||
//- ???
|
||||
scalarField V_;
|
||||
|
||||
//- List of volume-averaged surface areas
|
||||
scalarField a_;
|
||||
|
||||
//- List of volume-averaged diameters
|
||||
scalarField d_;
|
||||
|
||||
//- Normalise number/volume concentrations
|
||||
//- Normalise result through division by sum
|
||||
Switch normalise_;
|
||||
|
||||
//- Determines whether density function is calculated
|
||||
Switch densityFunction_;
|
||||
//- Log transform
|
||||
Switch logTransform_;
|
||||
|
||||
//- Geometric standard deviation/density function
|
||||
Switch geometric_;
|
||||
//- Weight types, relevant if particle properties are field dependent
|
||||
weightType weightType_;
|
||||
|
||||
//- Highest moment order
|
||||
label maxOrder_;
|
||||
//- Set formatter
|
||||
autoPtr<setWriter> formatterPtr_;
|
||||
|
||||
|
||||
// Protected Member Functions
|
||||
// Private Member Functions
|
||||
|
||||
//- Filter field according to cellIds
|
||||
//- Function type symbolic name for shorter file header
|
||||
word functionTypeSymbolicName();
|
||||
|
||||
//- Coordinate type symbolic name for shorter file header
|
||||
word coordinateTypeSymbolicName(const coordinateType& cType);
|
||||
|
||||
//- Filter a field according to cellIds
|
||||
tmp<scalarField> filterField(const scalarField& field) const;
|
||||
|
||||
//- Bin component used according to chosen coordinate type
|
||||
inline const scalarField& bin() const
|
||||
{
|
||||
switch (coordinateType_)
|
||||
{
|
||||
case ctVolume:
|
||||
return V_;
|
||||
case ctArea:
|
||||
return a_;
|
||||
case ctDiameter:
|
||||
return d_;
|
||||
case ctProjectedAreaDiameter:
|
||||
return d_;
|
||||
}
|
||||
return scalarField::null();
|
||||
}
|
||||
//- Field averaged coordinate value
|
||||
scalar averageCoordinateValue
|
||||
(
|
||||
const diameterModels::sizeGroup& fi,
|
||||
const coordinateType& cType
|
||||
);
|
||||
|
||||
//- Correct volume averages
|
||||
void correctVolAverages();
|
||||
|
||||
//- Write moments
|
||||
void writeMoments();
|
||||
|
||||
//- Write standard deviation
|
||||
void writeStdDev();
|
||||
|
||||
//- Write distribution
|
||||
void writeDistribution();
|
||||
|
||||
//- Output file header information for functionType moments
|
||||
virtual void writeFileHeader(const label i);
|
||||
//- Weighted average
|
||||
scalar weightedAverage
|
||||
(
|
||||
const scalarField& fld,
|
||||
const diameterModels::sizeGroup& fi
|
||||
);
|
||||
|
||||
|
||||
public:
|
||||
@ -235,21 +223,18 @@ public:
|
||||
|
||||
// Member Functions
|
||||
|
||||
//- Read the sizeDistribution data
|
||||
virtual bool read(const dictionary&);
|
||||
|
||||
//- Return the list of fields required
|
||||
virtual wordList fields() const
|
||||
{
|
||||
return wordList::null();
|
||||
}
|
||||
|
||||
//- Read the sizeDistribution data
|
||||
virtual bool read(const dictionary&);
|
||||
|
||||
//- Execute, currently does nothing
|
||||
virtual bool execute();
|
||||
|
||||
//- Execute at the final time-loop, currently does nothing
|
||||
virtual bool end();
|
||||
|
||||
//- Calculate and write the size distribution
|
||||
virtual bool write();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user