ENH: Added new particleZoneInfo cloud function object.

Reports cloud information for particles passing through a specified cell
zone.

Example usage:

    cloudFunctions
    {
        particleZoneInfo1
        {
            type            particleZoneInfo;
            cellZone        leftFluid;

            // Optional entries
            //writer          vtk;
        }
    }

Results are written to file:
- \<case\>/postProcessing/lagrangian/\<cloudName\>/\<functionName\>/\<time\>

    \# cellZone        : leftFluid
    \# time            : 1.0000000000e+00
    \#
    \# origID    origProc    (x y z)    time0    age    d0    d    mass0    mass

Where
- origID : particle ID
- origProc : processor ID
- (x y z) : Cartesian co-ordinates
- time0 : time particle enters the cellZone
- age : time spent in the cellZone
- d0 : diameter on entry to the cellZone
- d : current diameter
- mass0 : mass on entry to the cellZone
- mass : current mass

If the optional \c writer entry is supplied, cloud data is written in the
specified format.

During the run, output statistics are reported after the cloud solution,
e.g.:

    particleZoneInfo:
        Cell zone                       = leftFluid
        Contributions                   = 257

Here, 'Contributions' refers to the number of incremental particle-move
contributions recorded during this time step. At write times, the output
is extended, e.g.:

    particleZoneInfo:
        Cell zone                       = leftFluid
        Contributions                   = 822
        Number of particles             = 199
        Written data to "postProcessing/lagrangian/reactingCloud1/

TUT: filter: add an example for the particleZoneInfo function object
This commit is contained in:
Andrew Heather 2022-05-23 20:34:31 +01:00 committed by Sergio Ferraris
parent 8d90b16518
commit 5cb0dc9d8a
6 changed files with 808 additions and 3 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2018 OpenFOAM Foundation
Copyright (C) 2020-2021 OpenCFD Ltd.
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -37,6 +37,7 @@ License
#include "ParticleErosion.H"
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
@ -57,6 +58,7 @@ License
makeCloudFunctionObjectType(ParticleErosion, CloudType); \
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2018 OpenFOAM Foundation
Copyright (C) 2020-2021 OpenCFD Ltd.
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -37,6 +37,7 @@ License
#include "ParticleErosion.H"
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
@ -60,6 +61,7 @@ License
makeCloudFunctionObjectType(ParticleErosion, CloudType); \
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -36,6 +36,7 @@ License
#include "ParticleErosion.H"
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
@ -58,6 +59,7 @@ License
makeCloudFunctionObjectType(ParticleErosion, CloudType); \
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \

View File

@ -0,0 +1,458 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "ParticleZoneInfo.H"
#include "DynamicField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
struct particleInfoCombineOp
{
void operator()(particleInfo& p1, const particleInfo& p2) const
{
// p2 not set
if (p2.origID == -1)
{
return;
}
// p1 not set - initialise with p2
if (p1.origID == -1)
{
p1 = p2;
return;
}
// Set initial values
if (p2.time0 < p1.time0)
{
p1.time0 = p2.time0;
p1.d0 = p2.d0;
p1.mass0 = p2.mass0;
}
// Accumulate age
p1.age += p2.age;
// Set latest available values
if (p2.isOlderThan(p1))
{
p1.position = p2.position;
p1.d = p2.d;
p1.mass = p2.mass;
}
}
};
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class Type>
Foam::Field<Type> getData
(
const Foam::UList<Foam::particleInfo>& data,
Type Foam::particleInfo::* field
)
{
Field<Type> result(data.size());
forAll(data, i)
{
result[i] = data[i].*field;
}
return result;
}
template<class Type>
Foam::Field<Type> getParData
(
const Foam::List<Foam::List<Foam::particleInfo>>& parData,
Type Foam::particleInfo::* field
)
{
DynamicField<Type> result;
for (const auto& particles : parData)
{
for (const auto& p : particles)
{
if (p.origID != -1)
{
result.append(p.*field);
}
}
}
return std::move(result);
}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::writeWriter
(
const DynamicList<particleInfo>& data
)
{
coordSet coords
(
"zoneParticles",
"xyz",
getData(data_, &particleInfo::position),
scalarList(data.size(), Zero)
);
writerPtr_->open(coords, this->baseTimeDir() / "zoneParticles");
writerPtr_->beginTime(this->owner().time());
#undef writeLocal
#define writeLocal(field) \
writerPtr_->write(#field, getData(data, &particleInfo::field));
writeLocal(origID);
writeLocal(origProc);
writeLocal(time0);
writeLocal(age);
writeLocal(d0);
writeLocal(d);
writeLocal(mass0);
writeLocal(mass);
#undef writeLocal
writerPtr_->endTime();
writerPtr_->close();
}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::writeWriter
(
const List<List<particleInfo>>& procData
)
{
vectorField points(getParData(procData, &particleInfo::position));
coordSet coords
(
"zoneParticles",
"xyz",
std::move(points),
scalarList(points.size(), Zero)
);
writerPtr_->open(coords, this->baseTimeDir() / "zoneParticles");
writerPtr_->beginTime(this->owner().time());
#undef writeLocal
#define writeLocal(field) \
writerPtr_->write(#field, getParData(procData, &particleInfo::field));
writeLocal(origID);
writeLocal(origProc);
writeLocal(time0);
writeLocal(age);
writeLocal(d0);
writeLocal(d);
writeLocal(mass0);
writeLocal(mass);
#undef writeLocal
writerPtr_->endTime();
writerPtr_->close();
}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::writeFileHeader(Ostream& os) const
{
this->writeHeaderValue(os, "cellZone", cellZoneName_);
this->writeHeaderValue(os, "time", this->owner().time().timeOutputValue());
this->writeHeader(os, "");
this->writeCommented(os, "origID");
os << tab << "origProc"
<< tab << "(x y z)"
<< tab << "time0"
<< tab << "age"
<< tab << "d0"
<< tab << "d"
<< tab << "mass0"
<< tab << "mass"
<< endl;
}
template<class CloudType>
bool Foam::ParticleZoneInfo<CloudType>::inZone(const label celli) const
{
return this->owner().mesh().cellZones()[cellZoneId_].whichCell(celli) != -1;
}
template<class CloudType>
Foam::label Foam::ParticleZoneInfo<CloudType>::getParticleID
(
const particleInfo& p
) const
{
forAll(data_, i)
{
const auto& d = data_[i];
if ((d.origProc == p.origProc) && (d.origID == p.origID))
{
return i;
}
}
return -1;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class CloudType>
Foam::ParticleZoneInfo<CloudType>::ParticleZoneInfo
(
const dictionary& dict,
CloudType& owner,
const word& modelName
)
:
CloudFunctionObject<CloudType>(dict, owner, modelName, typeName),
functionObjects::writeFile
(
owner,
this->localPath(),
typeName,
this->coeffDict()
),
cellZoneName_(this->coeffDict().getWord("cellZone")),
cellZoneId_(-1),
data_(),
movedParticles_(),
maxIDs_(Pstream::nProcs(), Zero),
writerPtr_
(
Pstream::master()
? coordSetWriter::New
(
this->coeffDict().getWord("writer"),
this->coeffDict().subOrEmptyDict("formatOptions")
)
: nullptr
)
{
const auto& cellZones = owner.mesh().cellZones();
cellZoneId_ = cellZones.findZoneID(cellZoneName_);
if (cellZoneId_ == -1)
{
FatalIOErrorInFunction(this->coeffDict())
<< "Unable to find cellZone " << cellZoneName_
<< ". Available cellZones are:" << cellZones.names()
<< exit(FatalIOError);
}
Info<< " Processing cellZone" << cellZoneName_ << " with id "
<< cellZoneId_ << endl;
}
template<class CloudType>
Foam::ParticleZoneInfo<CloudType>::ParticleZoneInfo
(
const ParticleZoneInfo<CloudType>& pzi
)
:
CloudFunctionObject<CloudType>(pzi),
writeFile(pzi),
cellZoneName_(pzi.cellZoneName_),
cellZoneId_(pzi.cellZoneId_),
data_(pzi.data_),
movedParticles_(pzi.movedParticles_),
maxIDs_(Pstream::nProcs()),
writerPtr_(nullptr)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::preEvolve
(
const typename parcelType::trackingData& td
)
{}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::postEvolve
(
const typename parcelType::trackingData& td
)
{
Info<< this->type() << ":" << nl
<< " Cell zone = " << cellZoneName_ << nl
<< " Contributions = "
<< returnReduce(movedParticles_.size(), sumOp<label>())
<< endl;
if (!this->writeTime())
{
Info<< endl;
}
for (const auto& p : movedParticles_)
{
const label id = getParticleID(p);
if (id == -1)
{
// New particle
data_.append(p);
maxIDs_[p.origProc] = max(maxIDs_[p.origProc], p.origID);
}
else
{
// Add to existing particle
data_[id] += p;
}
}
movedParticles_.clear();
// Calls write
CloudFunctionObject<CloudType>::postEvolve(td);
}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::postMove
(
parcelType& p,
const scalar dt,
const point&,
bool&
)
{
if (inZone(p.cell()))
{
particleInfo newData;
newData.origID = p.origId();
newData.origProc = p.origProc();
newData.position = p.position();
newData.time0 = this->owner().time().value() + dt;
newData.age = dt;
newData.d0 = p.d();
newData.d = p.d();
newData.mass0 = p.mass();
newData.mass = newData.mass0;
movedParticles_.append(newData);
}
}
template<class CloudType>
void Foam::ParticleZoneInfo<CloudType>::write()
{
autoPtr<OFstream> osPtr =
this->createFile("particles", this->owner().time().timeOutputValue());
if (Pstream::parRun())
{
// Find number of particles per proc
labelList allMaxIDs(maxIDs_);
Pstream::listCombineGather(allMaxIDs, maxEqOp<label>());
Pstream::scatterList(allMaxIDs);
List<List<particleInfo>> procParticles(Pstream::nProcs());
forAll(procParticles, proci)
{
procParticles[proci].resize(allMaxIDs[proci] + 1);
}
// Insert into bins for accumulation
for (const auto& d : data_)
{
procParticles[d.origProc][d.origID] = d;
}
for (auto& particles : procParticles)
{
Pstream::listCombineGather(particles, particleInfoCombineOp());
}
if (Pstream::master())
{
writeWriter(procParticles);
auto& os = osPtr();
writeFileHeader(os);
label nData = 0;
for (const auto& particles : procParticles)
{
for (const auto& p : particles)
{
if (p.origID != -1)
{
os << p << endl;
++nData;
}
}
}
Info<< " Number of particles = " << nData << nl
<< " Written data to " << os.name() << endl;
}
}
else
{
writeWriter(data_);
auto& os = osPtr();
writeFileHeader(os);
for (const auto& p : data_)
{
os << p << nl;
}
Info<< " Number of particles = " << data_.size() << nl
<< " Written data to " << os.name() << endl;
}
Info<< endl;
}
// ************************************************************************* //

View File

@ -0,0 +1,334 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
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/>.
Class
Foam::ParticleZoneInfo
Group
grpLagrangianIntermediateFunctionObjects
Description
Reports cloud information for particles passing through a specified cell
zone.
Usage
Example usage:
\verbatim
cloudFunctions
{
particleZoneInfo1
{
// Mandatory entries
type particleZoneInfo;
cellZone leftFluid;
// Optional entries
writer vtk;
}
}
\endverbatim
Results are written to file:
- \<case\>/postProcessing/lagrangian/\<cloudName\>/\<functionName\>/\<time\>
\verbatim
# cellZone : leftFluid
# time : 1.0000000000e+00
#
# origID origProc (x y z) time0 age d0 d mass0 mass
\endverbatim
Where
- origID : particle ID
- origProc : processor ID
- (x y z) : Cartesian co-ordinates
- time0 : time particle enters the cellZone
- age : time spent in the cellZone
- d0 : diameter on entry to the cellZone
- d : current diameter
- mass0 : mass on entry to the cellZone
- mass : current mass
If the optional \c writer entry is supplied, cloud data is written in the
specified format.
During the run, output statistics are reported after the cloud solution,
e.g.:
\verbatim
particleZoneInfo:
Cell zone = leftFluid
Contributions = 257
\endverbatim
Here, 'Contributions' refers to the number of incremental particle-move
contributions recorded during this time step. At write times, the output
is extended, e.g.:
\verbatim
particleZoneInfo:
Cell zone = leftFluid
Contributions = 822
Number of particles = 199
Written data to "postProcessing/lagrangian/reactingCloud1/
\endverbatim
SourceFiles
ParticleZoneInfo.C
\*---------------------------------------------------------------------------*/
#ifndef ParticleZoneInfo_H
#define ParticleZoneInfo_H
#include "CloudFunctionObject.H"
#include "writeFile.H"
#include "coordSetWriter.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
struct particleInfo Declaration
\*---------------------------------------------------------------------------*/
struct particleInfo
{
label origID = -1;
label origProc = -1;
vector position = Zero;
scalar time0 = 0;
scalar age = 0;
scalar d0 = 0;
scalar d = 0;
scalar mass0 = 0;
scalar mass = 0;
void operator+=(const particleInfo& p)
{
// Increment age
age += p.age;
// Set current values
position = p.position;
d = p.d;
mass = p.mass;
}
scalar isOlderThan(const particleInfo& p) const
{
// Cannot just use time0 - particle may leave/re-enter and
// so age is decoupled
return (p.time0 + p.age) < (time0 + age);
}
friend bool operator==(const particleInfo& a, const particleInfo& b)
{
return
a.origID == b.origID
&& a.origProc == b.origProc
&& a.position == b.position
&& a.time0 == b.time0
&& a.age == b.age
&& a.d0 == b.d0
&& a.d == b.d
&& a.mass0 == b.mass0
&& a.mass == b.mass;
}
friend bool operator!=(const particleInfo& a, const particleInfo& b)
{
return !(a == b);
}
// IOstream Operators
friend Istream& operator>>(Istream& is, particleInfo& pi)
{
is >> pi.origID
>> pi.origProc
>> pi.position
>> pi.time0
>> pi.age
>> pi.d0
>> pi.d
>> pi.mass0
>> pi.mass;
return is;
}
friend Ostream& operator<<(Ostream& os, const particleInfo& pi)
{
os << pi.origID
<< " " << pi.origProc
<< " " << pi.position
<< " " << pi.time0
<< " " << pi.age
<< " " << pi.d0
<< " " << pi.d
<< " " << pi.mass0
<< " " << pi.mass;
return os;
}
};
/*---------------------------------------------------------------------------*\
Class ParticleZoneInfo Declaration
\*---------------------------------------------------------------------------*/
template<class CloudType>
class ParticleZoneInfo
:
public CloudFunctionObject<CloudType>,
public functionObjects::writeFile
{
// Private Data
// Typedefs
//- Convenience typedef for parcel type
typedef typename CloudType::parcelType parcelType;
//- Cell zone name
word cellZoneName_;
//- Cell zone index
label cellZoneId_;
//- Stored data
DynamicList<particleInfo> data_;
//- Work storage
DynamicList<particleInfo> movedParticles_;
//- Maximum particle ID per processor
labelList maxIDs_;
//- Set writer
autoPtr<coordSetWriter> writerPtr_;
// Private Member Functions
//- Write output file header
void writeFileHeader(Ostream& os) const;
//- Return true if celli is in the cellZone
bool inZone(const label celli) const;
//- Return the index of the particle in the storage (data_)
//- Returns -1 if not found
label getParticleID(const particleInfo& p) const;
//- Write fields using writerPtr_ for serial runs
void writeWriter(const DynamicList<particleInfo>& data);
//- Write fields using writerPtr_ for parallel runs
void writeWriter(const List<List<particleInfo>>& procData);
public:
//- Runtime type information
TypeName("particleZoneInfo");
// Constructors
//- Construct from dictionary
ParticleZoneInfo
(
const dictionary& dict,
CloudType& owner,
const word& modelName
);
//- Construct copy
ParticleZoneInfo(const ParticleZoneInfo<CloudType>& pe);
//- Construct and return a clone
virtual autoPtr<CloudFunctionObject<CloudType>> clone() const
{
return autoPtr<CloudFunctionObject<CloudType>>
(
new ParticleZoneInfo<CloudType>(*this)
);
}
//- Destructor
virtual ~ParticleZoneInfo() = default;
// Member Functions
//- Pre-evolve hook
virtual void preEvolve
(
const typename parcelType::trackingData& td
);
//- Post-evolve hook
virtual void postEvolve
(
const typename parcelType::trackingData& td
);
//- Post-move hook
virtual void postMove
(
parcelType& p,
const scalar dt,
const point& position0,
bool& keepParticle
);
//- Write
virtual void write();
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "ParticleZoneInfo.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -160,6 +160,13 @@ subModels
cloudFunctions
{
particleZoneInfo1
{
type particleZoneInfo;
cellZone leftFluid;
writer vtk;
}
patchParticleHistogram1
{
type patchParticleHistogram;