ENH: refactor coordSet writers (#2347)

- the very old 'writer' class was fully stateless and always templated
  on an particular output type.

  This is now replaced with a 'coordSetWriter' with similar concepts
  as previously introduced for surface writers (#1206).

  - writers change from being a generic state-less set of routines to
    more properly conforming to the normal notion of a writer.

  - Parallel data is done *outside* of the writers, since they are used
    in a wide variety of contexts and the caller is currently still in
    a better position for deciding how to combine parallel data.

ENH: update sampleSets to sample on per-field basis (#2347)

- sample/write a field in a single step.

- support for 'sampleOnExecute' to obtain values at execution
  intervals without writing.

- support 'sets' input as a dictionary entry (as well as a list),
  which is similar to the changes for sampled-surface and permits use
  of changeDictionary to modify content.

- globalIndex for gather to reduce parallel communication, less code

- qualify the sampleSet results (properties) with the name of the set.
  The sample results were previously without a qualifier, which meant
  that only the last property value was actually saved (previous ones
  overwritten).

  For example,
  ```
    sample1
    {
        scalar
        {
            average(line,T) 349.96521;
            min(line,T)     349.9544281;
            max(line,T)     350;
            average(cells,T) 349.9854619;
            min(cells,T)    349.6589286;
            max(cells,T)    350.4967271;
            average(line,epsilon) 0.04947733869;
            min(line,epsilon) 0.04449639927;
            max(line,epsilon) 0.06452856475;
        }
        label
        {
            size(line,T)    79;
            size(cells,T)   1720;
            size(line,epsilon) 79;
        }
    }
  ```

ENH: update particleTracks application

- use globalIndex to manage original parcel addressing and
  for gathering. Simplify code by introducing a helper class,
  storing intermediate fields in hash tables instead of
  separate lists.

ADDITIONAL NOTES:

- the regionSizeDistribution largely retains separate writers since
  the utility of placing sum/dev/count for all fields into a single file
  is questionable.

- the streamline writing remains a "soft" upgrade, which means that
  scalar and vector fields are still collected a priori and not
  on-the-fly.  This is due to how the streamline infrastructure is
  currently handled (should be upgraded in the future).
This commit is contained in:
Mark Olesen
2022-02-25 14:50:01 +01:00
parent 8a9ae839ce
commit c3e14ffdd5
96 changed files with 8735 additions and 5178 deletions

View File

@ -13,7 +13,6 @@ sampledSet/patchSeed/patchSeedSet.C
sampledSet/patchEdge/patchEdgeSet.C
sampledSet/sampledSet/sampledSet.C
sampledSet/sampledSets/sampledSets.C
sampledSet/sampledSets/sampledSetsGrouping.C
sampledSet/triSurfaceMeshPointSet/triSurfaceMeshPointSet.C
sampledSet/uniform/uniformSet.C
sampledSet/array/arraySet.C

View File

@ -57,11 +57,11 @@ void Foam::midPointAndFaceSet::genSamples()
while (size() > 0)
{
// Add first face
mpfSamplePoints[mpfSamplei] = operator[](samplei);
mpfSamplePoints[mpfSamplei] = points()[samplei];
mpfSampleCells[mpfSamplei] = cells_[samplei];
mpfSampleFaces[mpfSamplei] = faces_[samplei];
mpfSampleSegments[mpfSamplei] = segments_[samplei];
mpfSampleCurveDist[mpfSamplei] = curveDist_[samplei];
mpfSampleCurveDist[mpfSamplei] = distance_[samplei];
++mpfSamplei;
while
@ -70,7 +70,7 @@ void Foam::midPointAndFaceSet::genSamples()
&& (segments_[samplei] == segments_[samplei+1])
)
{
point midPoint(0.5*(operator[](samplei) + operator[](samplei+1)));
point midPoint(0.5*(points()[samplei] + points()[samplei+1]));
label cellm = pointInCell(midPoint, samplei);
if (cellm != -1)

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2018-2021 OpenCFD Ltd.
Copyright (C) 2018-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -30,7 +30,6 @@ License
#include "polyMesh.H"
#include "primitiveMesh.H"
#include "meshSearch.H"
#include "writer.H"
#include "particle.H"
#include "globalIndex.H"
@ -52,16 +51,16 @@ void Foam::sampledSet::checkDimensions() const
(cells_.size() != size())
|| (faces_.size() != size())
|| (segments_.size() != size())
|| (curveDist_.size() != size())
|| (distance_.size() != size())
)
{
FatalErrorInFunction
<< "sizes not equal : "
<< "Sizes not equal : "
<< " points:" << size()
<< " cells:" << cells_.size()
<< " faces:" << faces_.size()
<< " segments:" << segments_.size()
<< " curveDist:" << curveDist_.size()
<< " distance:" << distance_.size()
<< abort(FatalError);
}
}
@ -75,14 +74,12 @@ Foam::label Foam::sampledSet::getBoundaryCell(const label facei) const
Foam::label Foam::sampledSet::getNeighbourCell(const label facei) const
{
if (facei >= mesh().nInternalFaces())
{
return mesh().faceOwner()[facei];
}
else
if (facei < mesh().nInternalFaces())
{
return mesh().faceNeighbour()[facei];
}
return mesh().faceOwner()[facei];
}
@ -384,11 +381,11 @@ void Foam::sampledSet::setSamples
const labelList& samplingCells,
const labelList& samplingFaces,
const labelList& samplingSegments,
const scalarList& samplingCurveDist
const scalarList& samplingDistance
)
{
setPoints(samplingPts);
curveDist_ = samplingCurveDist;
setDistance(samplingDistance, false); // check=false
segments_ = samplingSegments;
cells_ = samplingCells;
@ -404,11 +401,11 @@ void Foam::sampledSet::setSamples
labelList&& samplingCells,
labelList&& samplingFaces,
labelList&& samplingSegments,
scalarList&& samplingCurveDist
scalarList&& samplingDistance
)
{
setPoints(std::move(samplingPts));
curveDist_ = std::move(samplingCurveDist);
setDistance(std::move(samplingDistance), false); // check=false
segments_ = std::move(samplingSegments);
cells_ = std::move(samplingCells);
@ -418,48 +415,6 @@ void Foam::sampledSet::setSamples
}
Foam::autoPtr<Foam::coordSet> Foam::sampledSet::gather
(
labelList& indexSet,
labelList& allSegments
) const
{
// Combine sampleSet from processors. Sort by curveDist. Return
// ordering in indexSet.
// Note: only master results are valid
List<point> allPts;
globalIndex::gatherOp(*this, allPts);
globalIndex::gatherOp(segments(), allSegments);
scalarList allCurveDist;
globalIndex::gatherOp(curveDist(), allCurveDist);
if (Pstream::master() && allCurveDist.empty())
{
WarningInFunction
<< "Sample set " << name()
<< " has zero points." << endl;
}
// Sort curveDist and use to fill masterSamplePts
Foam::sortedOrder(allCurveDist, indexSet); // uses stable sort
scalarList sortedDist(allCurveDist, indexSet); // with indices for mapping
allSegments = UIndirectList<label>(allSegments, indexSet)();
return autoPtr<coordSet>::New
(
name(),
axis(),
List<point>(UIndirectList<point>(allPts, indexSet)),
sortedDist
);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::sampledSet::sampledSet
@ -513,6 +468,25 @@ Foam::sampledSet::sampledSet
{}
// Foam::autoPtr<Foam::coordSet> Foam::sampledSet::gather
// (
// labelList& sortOrder,
// labelList& allSegments
// ) const
// {
// autoPtr<coordSet> result(coordSet::gatherSort(sortOrder));
//
// // Optional
// if (notNull(allSegments))
// {
// globalIndex::gatherOp(segments(), allSegments);
// allSegments = UIndirectList<label>(allSegments, sortOrder)();
// }
//
// return result;
// }
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::autoPtr<Foam::sampledSet> Foam::sampledSet::New

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2018 OpenCFD Ltd.
Copyright (C) 2017-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -50,20 +50,20 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef sampledSet_H
#define sampledSet_H
#ifndef Foam_sampledSet_H
#define Foam_sampledSet_H
#include "coordSet.H"
#include "autoPtr.H"
#include "typeInfo.H"
#include "runTimeSelectionTables.H"
#include "autoPtr.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declarations
// Forward Declarations
class polyMesh;
class meshSearch;
@ -75,7 +75,7 @@ class sampledSet
:
public coordSet
{
// Private data
// Private Data
//- Reference to mesh
const polyMesh& mesh_;
@ -86,6 +86,8 @@ class sampledSet
protected:
// Protected Data
//- Segment numbers
labelList segments_;
@ -154,7 +156,7 @@ protected:
const labelList& samplingCells,
const labelList& samplingFaces,
const labelList& samplingSegments,
const scalarList& samplingCurveDist
const scalarList& samplingDistance
);
//- Set sample data. Move list contents.
@ -164,7 +166,7 @@ protected:
labelList&& samplingCells,
labelList&& samplingFaces,
labelList&& samplingSegments,
scalarList&& samplingCurveDist
scalarList&& samplingDistance
);
public:
@ -194,22 +196,58 @@ public:
// PtrLists of sampledSet
class iNew
{
//- Reference to the volume mesh
const polyMesh& mesh_;
const meshSearch& searchEngine_;
const meshSearch& search_;
public:
iNew(const polyMesh& mesh, const meshSearch& searchEngine)
:
mesh_(mesh),
searchEngine_(searchEngine)
search_(searchEngine)
{}
autoPtr<sampledSet> operator()(Istream& is) const
{
word name(is);
dictionary dict(is);
return sampledSet::New(name, mesh_, searchEngine_, dict);
return sampledSet::New(name, mesh_, search_, dict);
}
};
//- PtrList read-construction helper that captures dictionaries used
//- during creation.
class iNewCapture
{
//- Reference to the volume mesh
const polyMesh& mesh_;
const meshSearch& search_;
//- Captured (recorded) dictionaries
DynamicList<dictionary>& capture_;
public:
iNewCapture
(
const polyMesh& mesh,
const meshSearch& searchEngine,
DynamicList<dictionary>& capture
)
:
mesh_(mesh),
search_(searchEngine),
capture_(capture)
{}
autoPtr<sampledSet> operator()(Istream& is) const
{
word name(is);
capture_.append(dictionary(is));
return sampledSet::New(name, mesh_, search_, capture_.last());
}
};
@ -269,27 +307,27 @@ public:
// Member Functions
const polyMesh& mesh() const
const polyMesh& mesh() const noexcept
{
return mesh_;
}
const meshSearch& searchEngine() const
const meshSearch& searchEngine() const noexcept
{
return searchEngine_;
}
const labelList& segments() const
const labelList& segments() const noexcept
{
return segments_;
}
const labelList& cells() const
const labelList& cells() const noexcept
{
return cells_;
}
const labelList& faces() const
const labelList& faces() const noexcept
{
return faces_;
}
@ -297,13 +335,16 @@ public:
//- Output for debugging
Ostream& write(Ostream&) const;
//- Helper: gather onto master and sort.
// \return (on master) gathered set and overall sort order
autoPtr<coordSet> gather
(
labelList& indexSet,
labelList& allSegments
) const;
// Other
/// //- Gather and sort.
/// \return (on master) gathered set and overall sort order
/// autoPtr<coordSet> gather
/// (
/// labelList& sortOrder,
/// labelList& allSegments
/// ) const;
};

View File

@ -5,8 +5,8 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2020 OpenCFD Ltd.
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -29,9 +29,12 @@ License
#include "sampledSets.H"
#include "dictionary.H"
#include "Time.H"
#include "globalIndex.H"
#include "volFields.H"
#include "volPointInterpolation.H"
#include "mapPolyMesh.H"
#include "IOobjectList.H"
#include "UIndirectList.H"
#include "ListOps.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -48,36 +51,287 @@ namespace Foam
);
}
bool Foam::sampledSets::verbose_ = false;
#include "sampledSetsImpl.C"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::sampledSets::combineSampledSets
Foam::autoPtr<Foam::coordSetWriter> Foam::sampledSets::newWriter
(
PtrList<coordSet>& masterSampledSets,
labelListList& indexSets
word writeType,
const dictionary& formatOptions,
const dictionary& setDict
)
{
// Combine sampleSets from processors. Sort by curveDist. Return
// ordering in indexSets.
// Note: only master results are valid
// Per-set adjustment
setDict.readIfPresent<word>("setFormat", writeType);
masterSampledSets_.clear();
masterSampledSets_.setSize(size());
indexSets_.setSize(size());
dictionary options = formatOptions.subOrEmptyDict(writeType);
const PtrList<sampledSet>& sampledSets = *this;
options.merge
(
setDict.subOrEmptyDict("formatOptions").subOrEmptyDict(writeType)
);
forAll(sampledSets, setI)
return coordSetWriter::New(writeType, options);
}
void Foam::sampledSets::gatherAllSets()
{
// Any writer references will become invalid
for (auto& writer : writers_)
{
labelList segments;
masterSampledSets.set
(
setI,
sampledSets[setI].gather(indexSets[setI], segments)
);
writer.expire();
}
const PtrList<sampledSet>& localSets = *this;
gatheredSets_.clear();
gatheredSets_.resize(localSets.size());
gatheredSorting_.resize_nocopy(localSets.size());
globalIndices_.resize_nocopy(localSets.size());
forAll(localSets, seti)
{
const coordSet& coords = localSets[seti];
globalIndices_[seti].reset(coords.size(), globalIndex::gatherOnly{});
gatheredSets_.set(seti, coords.gatherSort(gatheredSorting_[seti]));
}
}
Foam::IOobjectList Foam::sampledSets::preCheckFields()
{
wordList allFields; // Just needed for warnings
HashTable<wordHashSet> selected;
IOobjectList objects(0);
if (loadFromFiles_)
{
// Check files for a particular time
objects = IOobjectList(mesh_, mesh_.time().timeName());
allFields = objects.names();
selected = objects.classes(fieldSelection_);
}
else
{
// Check currently available fields
allFields = mesh_.names();
selected = mesh_.classes(fieldSelection_);
}
// Probably not needed...
// if (Pstream::parRun())
// {
// Pstream::mapCombineGather(selected, HashSetOps::plusEqOp<word>());
// Pstream::mapCombineScatter(selected);
// }
DynamicList<label> missed(fieldSelection_.size());
// Detect missing fields
forAll(fieldSelection_, i)
{
if
(
fieldSelection_[i].isLiteral()
&& !ListOps::found(allFields, fieldSelection_[i])
)
{
missed.append(i);
}
}
if (missed.size())
{
WarningInFunction
<< nl
<< "Cannot find "
<< (loadFromFiles_ ? "field file" : "registered field")
<< " matching "
<< UIndirectList<wordRe>(fieldSelection_, missed) << endl;
}
// The selected field names, ordered by (scalar, vector, ...)
// with internal sorting
selectedFieldNames_.clear();
do
{
#undef doLocalCode
#define doLocalCode(InputType) \
{ \
const auto iter = selected.find(InputType::typeName); \
if (iter.found()) \
{ \
selectedFieldNames_.append(iter.val().sortedToc()); \
} \
}
doLocalCode(volScalarField);
doLocalCode(volVectorField);
doLocalCode(volSphericalTensorField);
doLocalCode(volSymmTensorField);
doLocalCode(volTensorField);
#undef doLocalCode
}
while (false);
// Now propagate field counts (per surface)
// - can update writer even when not writing without problem
const label nFields = selectedFieldNames_.size();
forAll(writers_, seti)
{
coordSetWriter& writer = writers_[seti];
writer.nFields(nFields);
}
return objects;
}
void Foam::sampledSets::initDict(const dictionary& dict, const bool initial)
{
PtrList<sampledSet>::clear();
if (initial)
{
writers_.clear();
}
const entry* eptr = dict.findEntry("sets");
if (eptr && eptr->isDict())
{
PtrList<sampledSet> sampSets(eptr->dict().size());
if (initial)
{
writers_.resize(sampSets.size());
}
label seti = 0;
for (const entry& dEntry : eptr->dict())
{
if (!dEntry.isDict())
{
continue;
}
const dictionary& subDict = dEntry.dict();
autoPtr<sampledSet> sampSet =
sampledSet::New
(
dEntry.keyword(),
mesh_,
searchEngine_,
subDict
);
// if (!sampSet || !sampSet->enabled())
// {
// continue;
// }
// Define the set
sampSets.set(seti, sampSet);
// Define writer, but do not attached
if (initial)
{
writers_.set
(
seti,
newWriter(writeFormat_, writeFormatOptions_, subDict)
);
// Use outputDir/TIME/set-name
writers_[seti].useTimeDir(true);
writers_[seti].verbose(verbose_);
}
++seti;
}
sampSets.resize(seti);
if (initial)
{
writers_.resize(seti);
}
static_cast<PtrList<sampledSet>&>(*this).transfer(sampSets);
}
else if (eptr)
{
// This is slightly trickier.
// We want access to the individual dictionaries used for construction
DynamicList<dictionary> capture;
PtrList<sampledSet> input
(
eptr->stream(),
sampledSet::iNewCapture(mesh_, searchEngine_, capture)
);
PtrList<sampledSet> sampSets(input.size());
if (initial)
{
writers_.resize(sampSets.size());
}
label seti = 0;
forAll(input, inputi)
{
const dictionary& subDict = capture[inputi];
autoPtr<sampledSet> sampSet = input.release(inputi);
// if (!sampSet || !sampSet->enabled())
// {
// continue;
// }
// Define the set
sampSets.set(seti, sampSet);
// Define writer, but do not attached
if (initial)
{
writers_.set
(
seti,
newWriter(writeFormat_, writeFormatOptions_, subDict)
);
// Use outputDir/TIME/set-name
writers_[seti].useTimeDir(true);
writers_[seti].verbose(verbose_);
}
++seti;
}
sampSets.resize(seti);
if (initial)
{
writers_.resize(seti);
}
static_cast<PtrList<sampledSet>&>(*this).transfer(sampSets);
}
gatherAllSets();
needsCorrect_ = false;
}
@ -90,21 +344,27 @@ Foam::sampledSets::sampledSets
const dictionary& dict
)
:
functionObjects::regionFunctionObject(name, runTime, dict),
functionObjects::fvMeshFunctionObject(name, runTime, dict),
PtrList<sampledSet>(),
mesh_(refCast<const fvMesh>(obr_)),
dict_(dict),
loadFromFiles_(false),
outputPath_(fileName::null),
searchEngine_(mesh_),
interpolationScheme_(word::null),
writeFormat_(word::null),
writeFormatOptions_(dict.subOrEmptyDict("formatOptions"))
{
outputPath_ =
verbose_(false),
onExecute_(false),
needsCorrect_(false),
outputPath_
(
mesh_.time().globalPath()/functionObject::outputPrefix/name
);
time_.globalPath()/functionObject::outputPrefix/name
),
searchEngine_(mesh_),
samplePointScheme_(),
writeFormat_(),
writeFormatOptions_(dict.subOrEmptyDict("formatOptions")),
writers_(),
selectedFieldNames_(),
gatheredSets_(),
gatheredSorting_(),
globalIndices_()
{
if (mesh_.name() != polyMesh::defaultRegion)
{
outputPath_ /= mesh_.name();
@ -124,26 +384,31 @@ Foam::sampledSets::sampledSets
const bool loadFromFiles
)
:
functionObjects::regionFunctionObject(name, obr, dict),
functionObjects::fvMeshFunctionObject(name, obr, dict),
PtrList<sampledSet>(),
mesh_(refCast<const fvMesh>(obr)),
dict_(dict),
loadFromFiles_(loadFromFiles),
outputPath_(fileName::null),
searchEngine_(mesh_),
interpolationScheme_(word::null),
writeFormat_(word::null),
writeFormatOptions_(dict.subOrEmptyDict("formatOptions"))
{
outputPath_ =
verbose_(false),
onExecute_(false),
needsCorrect_(false),
outputPath_
(
mesh_.time().globalPath()/functionObject::outputPrefix/name
);
time_.globalPath()/functionObject::outputPrefix/name
),
searchEngine_(mesh_),
samplePointScheme_(),
writeFormat_(),
writeFormatOptions_(dict.subOrEmptyDict("formatOptions")),
writers_(),
selectedFieldNames_(),
gatheredSets_(),
gatheredSorting_(),
globalIndices_()
{
if (mesh_.name() != polyMesh::defaultRegion)
{
outputPath_ /= mesh_.name();
}
outputPath_.clean(); // Remove unneeded ".."
read(dict);
@ -152,7 +417,7 @@ Foam::sampledSets::sampledSets
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::sampledSets::verbose(const bool on)
bool Foam::sampledSets::verbose(const bool on) noexcept
{
bool old(verbose_);
verbose_ = on;
@ -160,122 +425,205 @@ bool Foam::sampledSets::verbose(const bool on)
}
bool Foam::sampledSets::read(const dictionary& dict)
{
if (&dict_ != &dict)
{
// Update local copy of dictionary
dict_ = dict;
}
fvMeshFunctionObject::read(dict);
PtrList<sampledSet>::clear();
writers_.clear();
fieldSelection_.clear();
selectedFieldNames_.clear();
gatheredSets_.clear();
gatheredSorting_.clear();
globalIndices_.clear();
verbose_ = dict.getOrDefault("verbose", false);
onExecute_ = dict.getOrDefault("sampleOnExecute", false);
samplePointScheme_ =
dict.getOrDefault<word>("interpolationScheme", "cellPoint");
const entry* eptr = dict.findEntry("sets");
if (eptr)
{
dict.readEntry("setFormat", writeFormat_);
}
// const dictionary formatOptions(dict.subOrEmptyDict("formatOptions"));
// Writer type and format options
// const word writerType =
// (eptr ? dict.get<word>("setFormat") : word::null);
// writerType_ = (eptr ? dict.get<word>("setFormat") : word::null);
initDict(dict, true);
// Have some sets, so sort out which fields are needed and report
if (this->size())
{
dict_.readEntry("fields", fieldSelection_);
fieldSelection_.uniq();
// Report
forAll(*this, seti)
{
const sampledSet& s = (*this)[seti];
if (!seti)
{
Info<< "Sampled set:" << nl;
}
Info<< " " << s.name() << " -> " << writers_[seti].type()
<< nl;
}
Info<< endl;
}
if (debug && Pstream::master())
{
Pout<< "sample fields:" << flatOutput(fieldSelection_) << nl
<< "sample sets:" << nl << '(' << nl;
for
(
const sampledSet& s
: static_cast<const PtrList<sampledSet>&>(*this)
)
{
Pout<< " " << s << endl;
}
Pout<< ')' << endl;
}
// FUTURE:
// Ensure all sets and merge information are expired
// expire(true);
return true;
}
bool Foam::sampledSets::performAction(unsigned request)
{
if (empty())
{
// Nothing to do
return true;
}
else if (needsCorrect_)
{
searchEngine_.correct();
initDict(dict_, false);
}
// FUTURE:
// Update sets and store
// ...
// Determine availability of fields.
// Count number of fields (only seems to be needed for VTK legacy)
IOobjectList objects = preCheckFields();
const label nFields = selectedFieldNames_.size();
if (!nFields)
{
// Nothing to do
return true;
}
// Update writers
forAll(*this, seti)
{
const coordSet& s = gatheredSets_[seti];
if (request & ACTION_WRITE)
{
coordSetWriter& writer = writers_[seti];
if (writer.needsUpdate())
{
writer.setCoordinates(s);
}
if (writer.buffering())
{
writer.open
(
outputPath_
/ word(s.name() + coordSetWriter::suffix(selectedFieldNames_))
);
}
else
{
writer.open(outputPath_/s.name());
}
writer.beginTime(mesh_.time());
}
}
// Sample fields
performAction<VolumeField<scalar>>(objects, request);
performAction<VolumeField<vector>>(objects, request);
performAction<VolumeField<sphericalTensor>>(objects, request);
performAction<VolumeField<symmTensor>>(objects, request);
performAction<VolumeField<tensor>>(objects, request);
// Finish this time step
forAll(writers_, seti)
{
// Write geometry if no fields were written so that we still
// can have something to look at
if (request & ACTION_WRITE)
{
/// if (!writers_[seti].wroteData())
/// {
/// writers_[seti].write();
/// }
writers_[seti].endTime();
}
}
return true;
}
bool Foam::sampledSets::execute()
{
if (onExecute_)
{
return performAction(ACTION_ALL & ~ACTION_WRITE);
}
return true;
}
bool Foam::sampledSets::write()
{
if (size())
{
const label nFields = classifyFields();
if (Pstream::master())
{
if (debug)
{
Pout<< "timeName = " << mesh_.time().timeName() << nl
<< "scalarFields " << scalarFields_ << nl
<< "vectorFields " << vectorFields_ << nl
<< "sphTensorFields " << sphericalTensorFields_ << nl
<< "symTensorFields " << symmTensorFields_ <<nl
<< "tensorFields " << tensorFields_ <<nl;
}
if (nFields)
{
if (debug)
{
Pout<< "Creating directory "
<< outputPath_/mesh_.time().timeName()
<< nl << endl;
}
mkDir(outputPath_/mesh_.time().timeName());
}
else
{
Info<< "No fields to sample" << endl;
}
}
if (nFields)
{
sampleAndWrite(scalarFields_);
sampleAndWrite(vectorFields_);
sampleAndWrite(sphericalTensorFields_);
sampleAndWrite(symmTensorFields_);
sampleAndWrite(tensorFields_);
}
}
return true;
}
bool Foam::sampledSets::read(const dictionary& dict)
{
dict_ = dict;
if (dict_.found("sets"))
{
dict_.readEntry("fields", fieldSelection_);
clearFieldGroups();
dict.readEntry("interpolationScheme", interpolationScheme_);
dict.readEntry("setFormat", writeFormat_);
PtrList<sampledSet> newList
(
dict_.lookup("sets"),
sampledSet::iNew(mesh_, searchEngine_)
);
transfer(newList);
combineSampledSets(masterSampledSets_, indexSets_);
if (this->size())
{
Info<< "Reading set description:" << nl;
forAll(*this, setI)
{
Info<< " " << operator[](setI).name() << nl;
}
Info<< endl;
}
}
if (Pstream::master() && debug)
{
Pout<< "sample fields:" << fieldSelection_ << nl
<< "sample sets:" << nl << "(" << nl;
forAll(*this, setI)
{
Pout<< " " << operator[](setI) << endl;
}
Pout<< ")" << endl;
}
return true;
return performAction(ACTION_ALL);
}
void Foam::sampledSets::correct()
{
if (dict_.found("sets"))
{
searchEngine_.correct();
PtrList<sampledSet> newList
(
dict_.lookup("sets"),
sampledSet::iNew(mesh_, searchEngine_)
);
transfer(newList);
combineSampledSets(masterSampledSets_, indexSets_);
}
needsCorrect_ = true;
}

View File

@ -5,8 +5,8 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2018 OpenCFD Ltd.
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -29,24 +29,86 @@ Class
Description
Set of sets to sample.
Call sampledSets.write() to sample and write files.
The write() method is used to sample and write files.
Example of function object specification:
\verbatim
surfaces
{
type sets;
libs (sampling);
// Write at same frequency as fields
writeControl writeTime;
writeInterval 1;
// Fields to be sampled
fields (p U);
// Scheme to obtain values
interpolationScheme cellPoint;
// Output format
surfaceFormat vtk;
formatOptions
{
vtk
{
precision 10;
}
}
sets
{
Uref
{
type cloud;
axis xyz;
points ((-0.0508 0.0508 0.01));
}
}
}
\endverbatim
Entries:
\table
Property | Description | Required | Default
type | Type-name: sets | yes |
sets | Dictionary or list of sample sets | expected |
fields | word/regex list of fields to sample | yes |
interpolationScheme | scheme to obtain values | no | cellPoint
setFormat | output format | yes |
sampleOnExecute | Sample (store) on execution as well | no | false
formatOptions | dictionary of format options | no |
\endtable
Additional per-set entries:
\table
Property | Description | Required | Default
store | Store surface/fields on registry | no |
setFormat | output format | no |
formatOptions | dictionary of format options | no |
\endtable
SourceFiles
sampledSets.C
sampledSetsImpl.C
\*---------------------------------------------------------------------------*/
#ifndef sampledSets_H
#define sampledSets_H
#ifndef Foam_sampledSets_H
#define Foam_sampledSets_H
#include "regionFunctionObject.H"
#include "fvMeshFunctionObject.H"
#include "sampledSet.H"
#include "volFieldsFwd.H"
#include "meshSearch.H"
#include "interpolation.H"
#include "coordSet.H"
#include "writer.H"
#include "wordRes.H"
#include "coordSetWriter.H"
#include "volFieldsFwd.H"
#include "IOobjectList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -55,9 +117,8 @@ namespace Foam
// Forward Declarations
class Time;
class objectRegistry;
class dictionary;
class fvMesh;
class globalIndex;
/*---------------------------------------------------------------------------*\
Class sampledSets Declaration
@ -65,97 +126,37 @@ class fvMesh;
class sampledSets
:
public functionObjects::regionFunctionObject,
public functionObjects::fvMeshFunctionObject,
public PtrList<sampledSet>
{
// Private Classes
//- Class used for grouping field types
template<class Type>
class fieldGroup
:
public DynamicList<word>
{
public:
//- The set formatter
autoPtr<writer<Type>> formatter;
//- Construct null
fieldGroup() = default;
//- Reset format and field list
void clear()
{
DynamicList<word>::clear();
formatter.clear();
}
void setFormatter(const word& writeFormat, const dictionary& dict)
{
formatter = writer<Type>::New(writeFormat, dict);
}
};
//- Class used for sampling volFields
template<class Type>
class volFieldSampler
:
public List<Field<Type>>
{
//- Name of this collection of values
const word name_;
public:
//- Construct interpolating field to the sampleSets
volFieldSampler
(
const word& interpolationScheme,
const GeometricField<Type, fvPatchField, volMesh>& field,
const PtrList<sampledSet>&
);
//- Construct mapping field to the sampleSets
volFieldSampler
(
const GeometricField<Type, fvPatchField, volMesh>& field,
const PtrList<sampledSet>&
);
//- Construct from components
volFieldSampler
(
const List<Field<Type>>& values,
const word& name
);
//- Return the field name
const word& name() const
{
return name_;
}
};
// Static Data Members
//- Output verbosity
static bool verbose_;
//- Local control for sampling actions
enum sampleActionType : unsigned
{
ACTION_NONE = 0,
ACTION_WRITE = 0x1,
ACTION_STORE = 0x2,
ACTION_ALL = 0xF
};
// Private Data
//- Const reference to fvMesh
const fvMesh& mesh_;
//- Keep the dictionary to recreate sets for moving mesh cases
dictionary dict_;
//- Load fields from files (not from objectRegistry)
bool loadFromFiles_;
//- Output verbosity
bool verbose_;
//- Perform sample/store actions on execute as well
bool onExecute_;
//- Correct meshSearch and update sets
bool needsCorrect_;
//- Output path
fileName outputPath_;
@ -168,8 +169,8 @@ class sampledSets
//- Names of fields to sample
wordRes fieldSelection_;
//- Interpolation scheme to use
word interpolationScheme_;
//- Interpolation/sample scheme to obtain values at the points
word samplePointScheme_;
//- Output format to use
word writeFormat_;
@ -178,61 +179,76 @@ class sampledSets
dictionary writeFormatOptions_;
// Categorized scalar/vector/tensor fields
// Output control
fieldGroup<scalar> scalarFields_;
fieldGroup<vector> vectorFields_;
fieldGroup<sphericalTensor> sphericalTensorFields_;
fieldGroup<symmTensor> symmTensorFields_;
fieldGroup<tensor> tensorFields_;
//- The coordSet writers (one per sampled set)
PtrList<coordSetWriter> writers_;
//- Current list of field names selected for sampling
DynamicList<word> selectedFieldNames_;
// Merging structures
// Merging
PtrList<coordSet> masterSampledSets_;
labelListList indexSets_;
//- Gathered coordSet. (Content only meaningful on master)
PtrList<coordSet> gatheredSets_;
//- Indexed sort order for gathered coordSet.
// (Content only meaningful on master)
List<labelList> gatheredSorting_;
//- The globalIndex for gathering. (Content only meaningful on master)
List<globalIndex> globalIndices_;
// Private Member Functions
//- Clear old field groups
void clearFieldGroups();
//- Classify field types, returns the number of fields
label classifyFields();
//- Combine points from all processors. Sort by curveDist and produce
//- index list. Valid result only on master processor.
void combineSampledSets
//- A new coordSet writer, with per-set formatOptions
static autoPtr<coordSetWriter> newWriter
(
PtrList<coordSet>& masterSampledSets,
labelListList& indexSets
word writeType,
const dictionary& formatOptions,
const dictionary& surfDict
);
//- Combine values from all processors.
// Valid result only on master processor.
template<class T>
void combineSampledValues
(
const PtrList<volFieldSampler<T>>& sampledFields,
const labelListList& indexSets,
PtrList<volFieldSampler<T>>& masterFields
);
//- Perform sampling action with store/write
bool performAction(unsigned request);
//- Write set on master, return fileName
//- Count selected/sampled fields
// Returns empty IOobjectList if loadFromFiles_ is not active
//
// Adjusts selectedFieldNames_
IOobjectList preCheckFields();
//- Setup the sets (optional writers)
void initDict(const dictionary& dict, const bool initial);
//- Combine points from all processors.
//- Sort by curve distance and produce index list.
//- Valid result only on master processor.
void gatherAllSets();
//- Write sampled fieldName on coordSet and on outputDir path
template<class Type>
fileName writeSampleFile
void writeCoordSet
(
const coordSet& masterSampleSet,
const PtrList<volFieldSampler<Type>>& masterFields,
const label setI,
const fileName& timeDir,
const writer<Type>& formatter
coordSetWriter& writer,
const Field<Type>& values,
const word& fieldName
);
template<class Type>
void sampleAndWrite(fieldGroup<Type>& fields);
//- Get from registry or load from disk
template<class GeoField>
tmp<GeoField> getOrLoadField(const word& fieldName) const;
//- Sample and store/write a specific volume field
template<class Type>
void performAction(const VolumeField<Type>& field, unsigned request);
//- Sample and write all applicable sampled fields
// Only uses IOobjectList when loadFromFiles_ is active
template<class GeoField>
void performAction(const IOobjectList& objects, unsigned request);
//- No copy construct
sampledSets(const sampledSets&) = delete;
@ -253,7 +269,7 @@ public:
sampledSets
(
const word& name,
const Time& time,
const Time& runTime,
const dictionary& dict
);
@ -262,8 +278,8 @@ public:
sampledSets
(
const word& name,
const objectRegistry&,
const dictionary&,
const objectRegistry& obr,
const dictionary& dict,
const bool loadFromFiles = false
);
@ -276,7 +292,7 @@ public:
//- Enable/disable verbose output
// \return old value
bool verbose(const bool on);
bool verbose(const bool on) noexcept;
//- Read the sampledSets
virtual bool read(const dictionary&);
@ -307,12 +323,6 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "sampledSetsTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,129 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017 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 "sampledSets.H"
#include "volFields.H"
#include "IOobjectList.H"
#include "UIndirectList.H"
#include "ListOps.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::sampledSets::clearFieldGroups()
{
scalarFields_.clear();
vectorFields_.clear();
sphericalTensorFields_.clear();
symmTensorFields_.clear();
tensorFields_.clear();
}
Foam::label Foam::sampledSets::classifyFields()
{
label nFields = 0;
clearFieldGroups();
wordList allFields; // Just needed for warnings
HashTable<wordHashSet> available;
if (loadFromFiles_)
{
// Check files for a particular time
IOobjectList objects(mesh_, mesh_.time().timeName());
allFields = objects.names();
available = objects.classes(fieldSelection_);
}
else
{
// Check currently available fields
allFields = mesh_.names();
available = mesh_.classes(fieldSelection_);
}
DynamicList<label> missed(fieldSelection_.size());
// Detect missing fields
forAll(fieldSelection_, i)
{
if (!ListOps::found(allFields, fieldSelection_[i]))
{
missed.append(i);
}
}
if (missed.size())
{
WarningInFunction
<< nl
<< "Cannot find "
<< (loadFromFiles_ ? "field file" : "registered field")
<< " matching "
<< UIndirectList<wordRe>(fieldSelection_, missed) << endl;
}
forAllConstIters(available, iter)
{
const word& fieldType = iter.key();
const wordList fieldNames = iter.val().sortedToc();
const label count = fieldNames.size(); // pre-filtered, so non-empty
if (fieldType == volScalarField::typeName)
{
scalarFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volVectorField::typeName)
{
vectorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volSphericalTensorField::typeName)
{
sphericalTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volSymmTensorField::typeName)
{
symmTensorFields_.append(fieldNames);
nFields += count;
}
else if (fieldType == volTensorField::typeName)
{
tensorFields_.append(fieldNames);
nFields += count;
}
}
return nFields;
}
// ************************************************************************* //

View File

@ -0,0 +1,241 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2015-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 "sampledSets.H"
#include "globalIndex.H"
#include "interpolation.H"
#include "volFields.H"
#include "volPointInterpolation.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class GeoField>
Foam::tmp<GeoField>
Foam::sampledSets::getOrLoadField(const word& fieldName) const
{
tmp<GeoField> tfield;
if (loadFromFiles_)
{
tfield.reset
(
new GeoField
(
IOobject
(
fieldName,
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
)
);
}
else
{
// Slightly paranoid here
tfield.cref(mesh_.cfindObject<GeoField>(fieldName));
}
return tfield;
}
template<class Type>
void Foam::sampledSets::writeCoordSet
(
coordSetWriter& writer,
const Field<Type>& values,
const word& fieldName
)
{
fileName outputName;
if (Pstream::master())
{
outputName = writer.write(fieldName, values);
}
Pstream::scatter(outputName);
if (outputName.size())
{
// Case-local file name with "<case>" to make relocatable
dictionary propsDict;
propsDict.add
(
"file",
time_.relativePath(outputName, true)
);
setProperty(fieldName, propsDict);
}
}
template<class Type>
void Foam::sampledSets::performAction
(
const VolumeField<Type>& fld,
unsigned request
)
{
const word& fieldName = fld.name();
// The interpolator for this field
autoPtr<interpolation<Type>> interpPtr;
if (!samplePointScheme_.empty() && samplePointScheme_ != "cell")
{
interpPtr.reset(interpolation<Type>::New(samplePointScheme_, fld));
}
forAll(*this, seti)
{
const sampledSet& s = (*this)[seti];
const globalIndex& globIdx = globalIndices_[seti];
const labelList& globOrder = gatheredSorting_[seti];
const word& setName = s.name();
Field<Type> values(s.size());
if (interpPtr)
{
forAll(s, samplei)
{
const point& p = s[samplei];
const label celli = s.cells()[samplei];
const label facei = s.faces()[samplei];
if (celli == -1 && facei == -1)
{
// Special condition for illegal sampling points
values[samplei] = pTraits<Type>::max;
}
else
{
values[samplei] = interpPtr().interpolate(p, celli, facei);
}
}
}
else
{
forAll(s, samplei)
{
const label celli = s.cells()[samplei];
if (celli == -1)
{
values[samplei] = pTraits<Type>::max;
}
else
{
values[samplei] = fld[celli];
}
}
}
// Collect data from all processors
globIdx.gatherInplace(values);
// Some values only available on master
Type avgValue, minValue, maxValue;
label sizeValue;
if (Pstream::master())
{
avgValue = average(values);
minValue = min(values);
maxValue = max(values);
sizeValue = values.size();
// Use sorted order
values = UIndirectList<Type>(values, globOrder)();
}
Pstream::scatter(avgValue);
Pstream::scatter(minValue);
Pstream::scatter(maxValue);
Pstream::scatter(sizeValue);
// Store results: min/max/average/size with the name of the set
// for scoping.
// Eg, average(lines,T) ...
const word resultArg('(' + setName + ',' + fieldName + ')');
this->setResult("average" + resultArg, avgValue);
this->setResult("min" + resultArg, minValue);
this->setResult("max" + resultArg, maxValue);
this->setResult("size" + resultArg, sizeValue);
if (verbose_)
{
Info<< name() << ' ' << setName << " : " << fieldName << nl
<< " avg: " << avgValue << nl
<< " min: " << minValue << nl
<< " max: " << maxValue << nl << nl;
}
if (request & ACTION_WRITE)
{
writeCoordSet<Type>(writers_[seti], values, fieldName);
}
}
}
template<class GeoField>
void Foam::sampledSets::performAction
(
const IOobjectList& objects,
unsigned request
)
{
wordList fieldNames;
if (loadFromFiles_)
{
fieldNames = objects.sortedNames<GeoField>(fieldSelection_);
}
else
{
fieldNames = mesh_.thisDb().sortedNames<GeoField>(fieldSelection_);
}
for (const word& fieldName : fieldNames)
{
tmp<GeoField> tfield = getOrLoadField<GeoField>(fieldName);
if (tfield)
{
performAction<typename GeoField::value_type>(tfield(), request);
}
}
}
// ************************************************************************* //

View File

@ -1,376 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2021 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 "sampledSets.H"
#include "volFields.H"
#include "globalIndex.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
Foam::sampledSets::volFieldSampler<Type>::volFieldSampler
(
const word& interpolationScheme,
const GeometricField<Type, fvPatchField, volMesh>& field,
const PtrList<sampledSet>& samplers
)
:
List<Field<Type>>(samplers.size()),
name_(field.name())
{
autoPtr<interpolation<Type>> interpolator
(
interpolation<Type>::New(interpolationScheme, field)
);
forAll(samplers, setI)
{
Field<Type>& values = this->operator[](setI);
const sampledSet& samples = samplers[setI];
values.setSize(samples.size());
forAll(samples, sampleI)
{
const point& samplePt = samples[sampleI];
label celli = samples.cells()[sampleI];
label facei = samples.faces()[sampleI];
if (celli == -1 && facei == -1)
{
// Special condition for illegal sampling points
values[sampleI] = pTraits<Type>::max;
}
else
{
values[sampleI] = interpolator().interpolate
(
samplePt,
celli,
facei
);
}
}
}
}
template<class Type>
Foam::sampledSets::volFieldSampler<Type>::volFieldSampler
(
const GeometricField<Type, fvPatchField, volMesh>& field,
const PtrList<sampledSet>& samplers
)
:
List<Field<Type>>(samplers.size()),
name_(field.name())
{
forAll(samplers, setI)
{
Field<Type>& values = this->operator[](setI);
const sampledSet& samples = samplers[setI];
values.setSize(samples.size());
forAll(samples, sampleI)
{
label celli = samples.cells()[sampleI];
if (celli ==-1)
{
values[sampleI] = pTraits<Type>::max;
}
else
{
values[sampleI] = field[celli];
}
}
}
}
template<class Type>
Foam::sampledSets::volFieldSampler<Type>::volFieldSampler
(
const List<Field<Type>>& values,
const word& name
)
:
List<Field<Type>>(values),
name_(name)
{}
template<class Type>
Foam::fileName Foam::sampledSets::writeSampleFile
(
const coordSet& masterSampleSet,
const PtrList<volFieldSampler<Type>>& masterFields,
const label setI,
const fileName& timeDir,
const writer<Type>& formatter
)
{
wordList valueSetNames(masterFields.size());
List<const Field<Type>*> valueSets(masterFields.size());
forAll(masterFields, fieldi)
{
const word& fieldName = masterFields[fieldi].name();
valueSetNames[fieldi] = fieldName;
// Values only available on master
Type averageValue, minValue, maxValue;
label sizeValue;
if (Pstream::master())
{
valueSets[fieldi] = &masterFields[fieldi][setI];
averageValue = average(*valueSets[fieldi]);
minValue = min(*valueSets[fieldi]);
maxValue = max(*valueSets[fieldi]);
sizeValue = valueSets[fieldi]->size();
}
Pstream::scatter(averageValue);
Pstream::scatter(minValue);
Pstream::scatter(maxValue);
Pstream::scatter(sizeValue);
// Set results
setResult("average(" + fieldName + ")", averageValue);
setResult("min(" + fieldName + ")", minValue);
setResult("max(" + fieldName + ")", maxValue);
setResult("size(" + fieldName + ")", sizeValue);
}
fileName fName;
if (Pstream::master())
{
fName = timeDir/formatter.getFileName(masterSampleSet, valueSetNames);
OFstream ofs(fName);
if (ofs.opened())
{
formatter.write
(
masterSampleSet,
valueSetNames,
valueSets,
ofs
);
}
else
{
WarningInFunction
<< "File " << ofs.name() << " could not be opened. "
<< "No data will be written" << endl;
}
}
Pstream::scatter(fName);
return fName;
}
template<class T>
void Foam::sampledSets::combineSampledValues
(
const PtrList<volFieldSampler<T>>& sampledFields,
const labelListList& indexSets,
PtrList<volFieldSampler<T>>& masterFields
)
{
forAll(sampledFields, fieldi)
{
List<Field<T>> masterValues(indexSets.size());
forAll(indexSets, setI)
{
// Collect data from all processors
Field<T> allData;
globalIndex::gatherOp(sampledFields[fieldi][setI], allData);
if (Pstream::master())
{
masterValues[setI] = UIndirectList<T>
(
allData,
indexSets[setI]
)();
}
}
masterFields.set
(
fieldi,
new volFieldSampler<T>
(
masterValues,
sampledFields[fieldi].name()
)
);
}
}
template<class Type>
void Foam::sampledSets::sampleAndWrite(fieldGroup<Type>& fields)
{
if (fields.size())
{
const bool interpolate = interpolationScheme_ != "cell";
// Create or use existing writer
if (!fields.formatter)
{
fields.setFormatter(writeFormat_, writeFormatOptions_);
}
// Storage for interpolated values
PtrList<volFieldSampler<Type>> sampledFields(fields.size());
forAll(fields, fieldi)
{
if (Pstream::master() && verbose_)
{
Pout<< "sampledSets::sampleAndWrite: "
<< fields[fieldi] << endl;
}
if (loadFromFiles_)
{
GeometricField<Type, fvPatchField, volMesh> vf
(
IOobject
(
fields[fieldi],
mesh_.time().timeName(),
mesh_,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
),
mesh_
);
if (interpolate)
{
sampledFields.set
(
fieldi,
new volFieldSampler<Type>
(
interpolationScheme_,
vf,
*this
)
);
}
else
{
sampledFields.set
(
fieldi,
new volFieldSampler<Type>(vf, *this)
);
}
}
else
{
if (interpolate)
{
sampledFields.set
(
fieldi,
new volFieldSampler<Type>
(
interpolationScheme_,
mesh_.lookupObject
<GeometricField<Type, fvPatchField, volMesh>>
(fields[fieldi]),
*this
)
);
}
else
{
sampledFields.set
(
fieldi,
new volFieldSampler<Type>
(
mesh_.lookupObject
<GeometricField<Type, fvPatchField, volMesh>>
(fields[fieldi]),
*this
)
);
}
}
}
// Combine sampled fields from processors.
// Note: only master results are valid
PtrList<volFieldSampler<Type>> masterFields(sampledFields.size());
combineSampledValues(sampledFields, indexSets_, masterFields);
forAll(masterSampledSets_, setI)
{
fileName sampleFile = writeSampleFile
(
masterSampledSets_[setI],
masterFields,
setI,
outputPath_/mesh_.time().timeName(),
fields.formatter()
);
if (sampleFile.size())
{
// Case-local file name with "<case>" to make relocatable
forAll(masterFields, fieldi)
{
dictionary propsDict;
propsDict.add
(
"file",
time_.relativePath(sampleFile, true)
);
const word& fieldName = masterFields[fieldi].name();
setProperty(fieldName, propsDict);
}
}
}
}
}
// ************************************************************************* //

View File

@ -940,7 +940,7 @@ void Foam::shortestPathSet::genSamples
// Get the target point
const label outsideCelli = mesh.findCell(outsidePoint);
// Maintain overall track length. Used to make curveDist continuous.
// Maintain overall track length. Used to make curve distance continuous.
scalar trackLength = 0;
List<topoDistanceData<label>> allFaceInfo(mesh.nFaces());