Merge branch 'feature-ensightCloudFO' into 'develop'

ensight cloud functionObject

See merge request Development/openfoam!666
This commit is contained in:
Andrew Heather
2024-02-02 13:31:18 +00:00
20 changed files with 1206 additions and 176 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2023 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -221,7 +221,7 @@ Foam::Ostream& Foam::ensightFile::write
}
Foam::Ostream& Foam::ensightFile::write(const int32_t val)
void Foam::ensightFile::writeInt(const int32_t val, const int fieldWidth)
{
if (format() == IOstreamOption::BINARY)
{
@ -233,24 +233,22 @@ Foam::Ostream& Foam::ensightFile::write(const int32_t val)
}
else
{
stdStream().width(10);
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const int64_t val)
void Foam::ensightFile::writeInt(const int64_t val, const int fieldWidth)
{
int32_t ivalue(narrowInt32(val));
int32_t work(narrowInt32(val));
return write(ivalue);
writeInt(work, fieldWidth);
}
Foam::Ostream& Foam::ensightFile::write(const float val)
void Foam::ensightFile::writeFloat(const float val, const int fieldWidth)
{
if (format() == IOstreamOption::BINARY)
{
@ -262,40 +260,45 @@ Foam::Ostream& Foam::ensightFile::write(const float val)
}
else
{
stdStream().width(12);
stdStream().width(fieldWidth);
stdStream() << val;
syncState();
}
}
void Foam::ensightFile::writeFloat(const double val, const int fieldWidth)
{
float work(narrowFloat(val));
writeFloat(work, fieldWidth);
}
Foam::Ostream& Foam::ensightFile::write(const int32_t val)
{
writeInt(val, 10);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const int64_t val)
{
writeInt(val, 10);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const float val)
{
writeFloat(val, 12);
return *this;
}
Foam::Ostream& Foam::ensightFile::write(const double val)
{
float fvalue(narrowFloat(val));
return write(fvalue);
}
Foam::Ostream& Foam::ensightFile::write
(
const label value,
const label fieldWidth
)
{
if (format() == IOstreamOption::BINARY)
{
write(value);
}
else
{
stdStream().width(fieldWidth);
stdStream() << value;
syncState();
}
writeFloat(val, 12);
return *this;
}
@ -376,7 +379,7 @@ void Foam::ensightFile::beginParticleCoordinates(const label nparticles)
{
writeString("particle coordinates");
newline();
write(nparticles, 8); // unusual width
writeInt(nparticles, 8); // Warning: unusual width
newline();
}

View File

@ -163,6 +163,18 @@ public:
//- Write string as "%79s" or as binary (max 80 chars)
void writeString(const std::string& str);
//- Write integer value with specified width or as binary
void writeInt(const int32_t val, const int fieldWidth);
//- Write (narrowed) integer value with specified width or as binary
void writeInt(const int64_t val, const int fieldWidth);
//- Write floating-point with specified width or as binary
void writeFloat(const float val, const int fieldWidth);
//- Write (narrowed) floating-point with specified width or as binary
void writeFloat(const double val, const int fieldWidth);
//- Write undef value
void writeUndef();
@ -197,15 +209,12 @@ public:
//- Write string, uses writeString()
virtual Ostream& write(const std::string& str) override;
//- Write integer as "%10d" or as binary
//- Write integer value as "%10d" or as binary
virtual Ostream& write(const int32_t val) override;
//- Write integer as "%10d" or as binary
//- Write integer value as "%10d" or as binary (narrowed to int32_t)
virtual Ostream& write(const int64_t val) override;
//- Write integer with specified width or as binary
Ostream& write(const label value, const label fieldWidth);
//- Write floating-point as "%12.5e" or as binary
virtual Ostream& write(const float val) override;

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -449,6 +449,19 @@ void Foam::ensightMesh::write
{
faceZoneParts_[id].write(os, mesh_, parallel);
}
// No geometry parts written?
// - with lagrangian-only output the VTK EnsightReader still
// needs a volume geometry, and ensight usually does too
if
(
cellZoneParts_.empty()
&& boundaryParts_.empty()
&& faceZoneParts_.empty()
)
{
ensightCells::writeBox(os, mesh_.bounds());
}
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -216,18 +216,22 @@ public:
// Output
//- Write geometry to file. Normally in parallel
//- Write geometry to file (normally in parallel).
// If all geometry is disabled, it will simply writes the mesh
// bounding box (to ensure that the geometry file is non-empty)
void write
(
ensightGeoFile& os,
bool parallel = Pstream::parRun()
bool parallel = UPstream::parRun()
) const;
//- Write geometry to file. Normally in parallel
//- Write geometry to file (normally in parallel).
// If all geometry is disabled, it will simply writes the mesh
// bounding box (to ensure that the geometry file is non-empty)
inline void write
(
autoPtr<ensightGeoFile>& os,
bool parallel = Pstream::parRun()
bool parallel = UPstream::parRun()
) const;
};

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -46,6 +46,7 @@ namespace Foam
// Forward Declarations
class bitSet;
class boundBox;
class polyMesh;
template<class T> class InfoProxy;
@ -279,6 +280,16 @@ public:
const polyMesh& mesh,
bool parallel
) const;
//- Write bounding box geometry.
//- All parameters are only relevant on master
static void writeBox
(
ensightGeoFile& os,
const boundBox& bb,
const label partIndex = 0,
const word& partName = "geometry-box"
);
};

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020 OpenCFD Ltd.
Copyright (C) 2020-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,7 +28,9 @@ License
#include "ensightCells.H"
#include "ensightOutput.H"
#include "InfoProxy.H"
#include "boundBox.H"
#include "polyMesh.H"
#include "cellModel.H"
#include "globalIndex.H"
#include "globalMeshData.H"
#include "manifoldCellsMeshObject.H"
@ -330,6 +332,44 @@ void Foam::ensightCells::write
}
void Foam::ensightCells::writeBox
(
ensightGeoFile& os,
const boundBox& bb,
const label partIndex,
const word& partName
)
{
pointField points;
cellShapeList shapes;
if (UPstream::master())
{
points = bb.hexCorners();
shapes.emplace_back(cellModel::HEX, identity(8));
}
ensightOutput::Detail::writeCoordinates
(
os,
partIndex,
partName,
8, // nPoints (global)
points,
false // serial only! (parallel=false)
);
if (UPstream::master())
{
os.writeKeyword(ensightCells::key(ensightCells::elemType::HEXA8));
os.write(shapes.size()); // one cell (global)
os.newline();
ensightOutput::writeCellShapes(os, shapes);
}
}
// * * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * //
template<>

View File

@ -148,14 +148,14 @@ public:
void write
(
ensightGeoFile& os,
bool parallel = Pstream::parRun()
bool parallel = UPstream::parRun()
) const;
//- Write geometry to file. Normally in parallel
inline void write
(
autoPtr<ensightGeoFile>& os,
bool parallel = Pstream::parRun()
bool parallel = UPstream::parRun()
) const;
};

View File

@ -5,5 +5,6 @@ icoUncoupledKinematicCloud/icoUncoupledKinematicCloud.C
dsmcFields/dsmcFields.C
vtkCloud/vtkCloud.C
ensightCloud/ensightCloudWriteObject.cxx
LIB = $(FOAM_LIBBIN)/liblagrangianFunctionObjects

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd.
Copyright (C) 2018-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -52,12 +52,16 @@ bool Foam::functionObjects::dataCloud::writeCloud
const word& cloudName
)
{
const auto* objPtr = mesh_.findObject<cloud>(cloudName);
if (!objPtr)
applyFilter_ = false;
const auto* cloudPtr = mesh_.findObject<cloud>(cloudName);
if (!cloudPtr)
{
return false;
}
const auto& currCloud = *cloudPtr;
objectRegistry obrTmp
(
IOobject
@ -71,7 +75,7 @@ bool Foam::functionObjects::dataCloud::writeCloud
)
);
objPtr->writeObjects(obrTmp);
currCloud.writeObjects(obrTmp);
const auto* pointsPtr = cloud::findIOPosition(obrTmp);
@ -86,7 +90,10 @@ bool Foam::functionObjects::dataCloud::writeCloud
// Number of parcels (locally)
label nParcels = (applyFilter_ ? parcelAddr_.count() : pointsPtr->size());
const label nParcels
(
applyFilter_ ? parcelAddr_.count() : pointsPtr->size()
);
// Total number of parcels on all processes
const label nTotParcels = returnReduce(nParcels, sumOp<label>());
@ -104,9 +111,9 @@ bool Foam::functionObjects::dataCloud::writeCloud
return false;
}
if (Pstream::master())
if (UPstream::master())
{
mkDir(outputName.path());
Foam::mkDir(outputName.path());
}
return
@ -163,12 +170,15 @@ bool Foam::functionObjects::dataCloud::read(const dictionary& dict)
selectClouds_.clear();
dict.readIfPresent("clouds", selectClouds_);
selectClouds_.uniq();
if (selectClouds_.empty())
{
selectClouds_.resize(1);
selectClouds_.first() =
dict.getOrDefault<word>("cloud", cloud::defaultName);
word cloudName;
if (dict.readIfPresent("cloud", cloudName))
{
selectClouds_.push_back(std::move(cloudName));
}
}
dict.readEntry("field", fieldName_);
@ -209,7 +219,12 @@ bool Foam::functionObjects::dataCloud::execute()
bool Foam::functionObjects::dataCloud::write()
{
const wordList cloudNames(mesh_.sortedNames<cloud>(selectClouds_));
const wordList cloudNames
(
selectClouds_.empty()
? mesh_.sortedNames<cloud>()
: mesh_.sortedNames<cloud>(selectClouds_)
);
if (cloudNames.empty())
{

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2020 OpenCFD Ltd.
Copyright (C) 2018-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -48,10 +48,10 @@ Description
\heading Basic Usage
\table
Property | Description | Required | Default
type | Type name: dataCloud | yes |
type | Type name: dataCloud | yes |
clouds | List of clouds (name or regex) | no |
cloud | Cloud name | no | defaultCloud
field | Name of the field | yes |
cloud | Cloud name | no |
field | Name of the field | yes |
selection | Parcel selection control | no | empty-dict
\endtable
@ -103,7 +103,7 @@ class dataCloud
public fvMeshFunctionObject,
public Foam::Detail::parcelSelection
{
// Private data
// Private Data
//- The printf format for zero-padding names
string printf_;
@ -177,7 +177,7 @@ class dataCloud
bool writeField
(
const fileName& outputName,
const objectRegistry& obrTmp
const objectRegistry& obr
) const;

View File

@ -171,10 +171,10 @@ template<class Type>
bool Foam::functionObjects::dataCloud::writeField
(
const fileName& outputName,
const objectRegistry& obrTmp
const objectRegistry& obr
) const
{
const auto* pointsPtr = cloud::findIOPosition(obrTmp);
const auto* pointsPtr = cloud::findIOPosition(obr);
if (!pointsPtr)
{
@ -185,8 +185,8 @@ bool Foam::functionObjects::dataCloud::writeField
// Fields are not always on all processors (eg, multi-component parcels).
// Thus need to resolve between all processors.
const List<Type>* fldPtr = obrTmp.findObject<IOField<Type>>(fieldName_);
const List<Type>& values = (fldPtr ? *fldPtr : List<Type>());
const List<Type>* fldPtr = obr.findObject<IOField<Type>>(fieldName_);
const List<Type>& values = (fldPtr ? *fldPtr : List<Type>::null());
if (!returnReduceOr(fldPtr != nullptr))
{

View File

@ -0,0 +1,266 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2024 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::functionObjects::ensightCloudWriteObject
Group
grpLagrangianFunctionObjects
Description
This functionObject writes cloud(s) in ensight format
Example of function object specification:
\verbatim
cloudWrite1
{
type ensightCloud;
libs (lagrangianFunctionObjects);
writeControl writeTime;
writeInterval 1;
format ascii;
timeFormat scientific;
timePrecision 5;
cloud myCloud;
fields (T U rho);
width 4; // file-padding
selection
{
stride
{
// every 10th parcelId
action add;
source stride;
stride 10;
}
Umin
{
// Remove slow parcels
action subtract;
source field;
field U;
accept (less 1e-3);
}
diam
{
// Only particular diameter ranges
action subset;
source field;
field d;
accept (greater 1e-3) and (less 1e-3);
}
}
}
\endverbatim
\heading Basic Usage
\table
Property | Description | Required | Default
type | Type name: ensightCloud | yes |
clouds | List of clouds (name or regex) | no |
cloud | Cloud name | no |
fields | List of fields (name or regex) | no |
selection | Parcel selection control | no | empty-dict
\endtable
\heading Output Options
\table
Property | Description | Required | Default
format | Format as ascii or binary | no | binary
width | Mask width for \c data/XXXX | no | 8
directory | The output directory name | no | postProcessing/NAME
overwrite | Remove existing directory | no | false
consecutive | Consecutive output numbering | no | false
width | Padding width for file name | no | 8
prune | Suppress writing of empty clouds | no | false
timeFormat | Time format (ensight case) | no | scientific
timePrecision | Time precision (ensight case) | no | 5
writeControl | Output control | recommended | timeStep
\endtable
The output filename and fields are added to the functionObjectProperties
information. For the previous example specification:
\verbatim
cloudWrite1
{
myCloud
{
file "<case>/simulation.case";
fields (T U rho);
}
}
\endverbatim
Note
The selection dictionary can be used for finer control of the parcel
output. It contains a set of (add,subtract,subset,clear,invert)
selection actions and sources.
Omitting the selection dictionary is the same as specifying the
conversion of all parcels (in the selected clouds).
More syntax details are to be found in the corresponding
Foam::Detail::parcelSelection class.
See also
Foam::Detail::parcelSelection
Foam::functionObjects::vtkCloud
Foam::functionObjects::ensightWrite
Foam::functionObjects::fvMeshFunctionObject
Foam::functionObjects::timeControl
SourceFiles
ensightCloudWriteObject.cxx
ensightCloudWriteObjectImpl.cxx
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_ensightCloudWriteObject_H
#define functionObjects_ensightCloudWriteObject_H
#include "fvMeshFunctionObject.H"
#include "ensightCase.H"
#include "globalIndex.H"
#include "parcelSelectionDetail.H"
#include "wordRes.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class ensightCloudWriteObject Declaration
\*---------------------------------------------------------------------------*/
class ensightCloudWriteObject
:
public fvMeshFunctionObject,
public Foam::Detail::parcelSelection
{
// Private Data
//- Ensight output options
ensightCase::options caseOpts_;
//- Output directory
fileName outputDir_;
//- Consecutive output numbering
bool consecutive_;
//- Suppress writing of empty clouds
bool pruneEmpty_;
//- Apply output filter (for the current cloud)
bool applyFilter_;
//- Sizing of selected parcels (including any filtering)
globalIndex procAddr_;
//- Requested names of clouds to process
wordRes selectClouds_;
//- Subset of cloud fields to process
wordRes selectFields_;
//- Ensight case handler
autoPtr<ensightCase> ensCase_;
// Private Member Functions
//- Ensight case handler
ensightCase& ensCase() { return *ensCase_; }
//- Write a cloud to disk (creates parent directory),
//- and record on the cloud OutputProperties.
// \param file is the output file name, with extension.
bool writeCloud(const word& cloudName);
//- Write fields of IOField<Type>
template<class Type>
wordList writeFields
(
const word& cloudName,
const objectRegistry& obrTmp
);
//- No copy construct
ensightCloudWriteObject(const ensightCloudWriteObject&) = delete;
//- No copy assignment
void operator=(const ensightCloudWriteObject&) = delete;
public:
//- Runtime type information
TypeName("ensightCloud");
// Constructors
//- Construct from Time and dictionary
ensightCloudWriteObject
(
const word& name,
const Time& runTime,
const dictionary& dict
);
//- Destructor
virtual ~ensightCloudWriteObject() = default;
// Member Functions
//- Read the ensightCloud specification
virtual bool read(const dictionary& dict);
//- Execute, currently does nothing
virtual bool execute();
//- Write fields
virtual bool write();
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,425 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2024 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 "ensightCloudWriteObject.H"
#include "ensightCells.H"
#include "Cloud.H"
#include "dictionary.H"
#include "fvMesh.H"
#include "ensightOutputCloud.H"
#include "addToRunTimeSelectionTable.H"
#include "pointList.H"
#include "stringOps.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(ensightCloudWriteObject, 0);
addToRunTimeSelectionTable
(
functionObject,
ensightCloudWriteObject,
dictionary
);
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Implementation
#include "ensightCloudWriteObjectImpl.cxx"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bool Foam::functionObjects::ensightCloudWriteObject::writeCloud
(
const word& cloudName
)
{
applyFilter_ = false;
procAddr_.clear();
const auto* cloudPtr = mesh_.cfindObject<cloud>(cloudName);
if (!cloudPtr)
{
return false;
}
const auto& currCloud = *cloudPtr;
objectRegistry obrTmp
(
IOobject
(
"ensight::ensightCloud::" + cloudName,
mesh_.time().constant(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
)
);
currCloud.writeObjects(obrTmp);
const auto* pointsPtr = cloud::findIOPosition(obrTmp);
if (!pointsPtr)
{
// This should be impossible
return false;
}
applyFilter_ = calculateFilter(obrTmp, log);
Pstream::reduceOr(applyFilter_);
// Number of parcels (locally)
const label nParcels =
(
applyFilter_ ? parcelAddr_.count() : pointsPtr->size()
);
// Gather sizes (offsets irrelevant)
procAddr_.reset(globalIndex::gatherOnly{}, nParcels);
bool noCloud(!procAddr_.totalSize());
Pstream::broadcast(noCloud);
if (applyFilter_)
{
// Report filtered/unfiltered count
Log << "After filtering using "
<< procAddr_.totalSize() << '/'
<< (returnReduce(pointsPtr->size(), sumOp<label>()))
<< " parcels" << nl;
}
if (pruneEmpty_ && noCloud)
{
return false;
}
// Copy positions (for simplicity and for filtering).
// Store as floatVector, since that is what Ensight will write anyhow
DynamicList<floatVector> positions;
positions.reserve(UPstream::master() ? procAddr_.maxSize() : nParcels);
{
const auto& points = *pointsPtr;
positions.resize_nocopy(nParcels);
auto iter = positions.begin();
if (applyFilter_)
{
if (std::is_same<float, vector::cmptType>::value)
{
for (const label idx : parcelAddr_)
{
*iter = points[idx];
++iter;
}
}
else
{
for (const label idx : parcelAddr_)
{
const auto& pos = points[idx];
(*iter).x() = narrowFloat(pos.x());
(*iter).y() = narrowFloat(pos.y());
(*iter).z() = narrowFloat(pos.z());
++iter;
}
}
}
else
{
if (std::is_same<float, vector::cmptType>::value)
{
for (const auto& pos : points)
{
*iter = pos;
++iter;
}
}
else
{
for (const auto& pos : points)
{
(*iter).x() = narrowFloat(pos.x());
(*iter).y() = narrowFloat(pos.y());
(*iter).z() = narrowFloat(pos.z());
++iter;
}
}
}
}
// Write positions
{
autoPtr<ensightFile> os = ensCase().newCloud(cloudName);
ensightOutput::writeCloudPositions
(
os.ref(),
positions,
procAddr_
);
}
// Prevent any possible conversion of positions as a field
obrTmp.filterKeys
(
[](const word& k)
{
return k.starts_with("position") || k.starts_with("coordinate");
},
true // prune
);
// Write fields
DynamicList<word> written(obrTmp.size() + currCloud.objectRegistry::size());
written.push_back
(
writeFields<label>(cloudName, obrTmp)
);
written.push_back
(
writeFields<scalar>(cloudName, obrTmp)
);
written.push_back
(
writeFields<vector>(cloudName, obrTmp)
);
// Any cloudFunctions results
written.push_back
(
writeFields<scalar>(cloudName, currCloud)
);
// Record information into the state (all processors)
//
// foName
// {
// cloudName
// {
// file "<case>/postProcessing/name/casename.case";
// fields (U T rho);
// }
// }
const fileName& file = ensCase().path();
// Case-local file name with "<case>" to make relocatable
dictionary propsDict;
propsDict.add
(
"file",
time_.relativePath(file, true)
);
propsDict.add("fields", written);
setObjectProperty(name(), cloudName, propsDict);
return true;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::ensightCloudWriteObject::ensightCloudWriteObject
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
fvMeshFunctionObject(name, runTime, dict),
caseOpts_("format", dict, IOstreamOption::BINARY),
outputDir_(),
consecutive_(false),
pruneEmpty_(false),
applyFilter_(false),
procAddr_()
{
// May still want this?
// if (postProcess)
// {
// // Disable for post-process mode.
// // Emit as FatalError for the try/catch in the caller.
// FatalError
// << type() << " disabled in post-process mode"
// << exit(FatalError);
// }
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::ensightCloudWriteObject::read
(
const dictionary& dict
)
{
fvMeshFunctionObject::read(dict);
// Case/writer options
consecutive_ = dict.getOrDefault("consecutive", false);
caseOpts_.width(dict.getOrDefault<label>("width", 8));
caseOpts_.overwrite(dict.getOrDefault("overwrite", false));
caseOpts_.timeFormat("timeFormat", dict);
caseOpts_.timePrecision("timePrecision", dict);
pruneEmpty_ = dict.getOrDefault("prune", false);
selectClouds_.clear();
dict.readIfPresent("clouds", selectClouds_);
selectClouds_.uniq();
if (selectClouds_.empty())
{
word cloudName;
if (dict.readIfPresent("cloud", cloudName))
{
selectClouds_.push_back(std::move(cloudName));
}
}
selectFields_.clear();
dict.readIfPresent("fields", selectFields_);
selectFields_.uniq();
// Actions to define selection
parcelSelect_ = dict.subOrEmptyDict("selection");
// Output directory
outputDir_.clear();
dict.readIfPresent("directory", outputDir_);
if (outputDir_.size())
{
// User-defined output directory
outputDir_.expand();
if (!outputDir_.isAbsolute())
{
outputDir_ = time_.globalPath()/outputDir_;
}
}
else
{
// Standard postProcessing/ naming
outputDir_ = time_.globalPath()/functionObject::outputPrefix/name();
}
outputDir_.clean(); // Remove unneeded ".."
return true;
}
bool Foam::functionObjects::ensightCloudWriteObject::execute()
{
return true;
}
bool Foam::functionObjects::ensightCloudWriteObject::write()
{
const wordList cloudNames
(
selectClouds_.empty()
? mesh_.sortedNames<cloud>()
: mesh_.sortedNames<cloud>(selectClouds_)
);
if (cloudNames.empty())
{
return true; // skip - nothing available
}
if (!ensCase_)
{
ensCase_.reset
(
new ensightCase(outputDir_, time_.globalCaseName(), caseOpts_)
);
// Generate a (non-moving) dummy geometry
// - ParaView ensight-reader needs this, and usually ensight does too
autoPtr<ensightGeoFile> os = ensCase().newGeometry(false);
ensightCells::writeBox(os.ref(), mesh_.bounds());
}
if (consecutive_)
{
ensCase().nextTime(time_.value());
}
else
{
ensCase().setTime(time_.value(), time_.timeIndex());
}
Log << type() << ' ' << name() << " write" << nl;
// Each cloud separately
for (const word& cloudName : cloudNames)
{
// writeCloud() includes mkDir (on master)
if (writeCloud(cloudName))
{
Log << " cloud : " << endl;
}
}
ensCase().write(); // Flush case information
return true;
}
// ************************************************************************* //

View File

@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2024 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 "IOField.H"
#include "ensightOutputCloud.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
Foam::wordList Foam::functionObjects::ensightCloudWriteObject::writeFields
(
const word& cloudName,
const objectRegistry& obrTmp
)
{
static_assert
(
(
std::is_same<label, typename pTraits<Type>::cmptType>::value
|| std::is_floating_point<typename pTraits<Type>::cmptType>::value
),
"Label and Floating-point vector space only"
);
// Other integral types (eg, bool etc) would need cast/convert to label.
// Similarly for labelVector etc.
// Fields are not always on all processors (eg, multi-component parcels).
// Thus need to resolve names between all processors.
wordList fieldNames =
(
selectFields_.size()
? obrTmp.names<IOField<Type>>(selectFields_)
: obrTmp.names<IOField<Type>>()
);
Pstream::combineReduce(fieldNames, ListOps::uniqueEqOp<word>());
Foam::sort(fieldNames); // Consistent order
DynamicList<Type> scratch;
for (const word& fieldName : fieldNames)
{
const List<Type>* fldPtr = obrTmp.findObject<IOField<Type>>(fieldName);
const List<Type>& values = (fldPtr ? *fldPtr : List<Type>::null());
autoPtr<ensightFile> os =
ensCase().newCloudData<Type>(cloudName, fieldName);
if (applyFilter_)
{
scratch.resize_nocopy(parcelAddr_.count());
auto iter = scratch.begin();
for (const label idx : parcelAddr_)
{
*iter = values[idx];
++iter;
}
// TBD:
// recalculate globalIndex instead of relying on procAddr_ ?
ensightOutput::writeCloudField(os.ref(), scratch, procAddr_);
}
else
{
// TBD:
// recalculate globalIndex instead of relying on procAddr_ ?
ensightOutput::writeCloudField(os.ref(), values, procAddr_);
}
}
return fieldNames;
}
// ************************************************************************* //

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd.
Copyright (C) 2018-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -101,12 +101,16 @@ bool Foam::functionObjects::vtkCloud::writeCloud
const word& cloudName
)
{
const auto* objPtr = mesh_.findObject<cloud>(cloudName);
if (!objPtr)
applyFilter_ = false;
const auto* cloudPtr = mesh_.cfindObject<cloud>(cloudName);
if (!cloudPtr)
{
return false;
}
const auto& currCloud = *cloudPtr;
objectRegistry obrTmp
(
IOobject
@ -120,7 +124,7 @@ bool Foam::functionObjects::vtkCloud::writeCloud
)
);
objPtr->writeObjects(obrTmp);
currCloud.writeObjects(obrTmp);
const auto* pointsPtr = cloud::findIOPosition(obrTmp);
@ -135,7 +139,10 @@ bool Foam::functionObjects::vtkCloud::writeCloud
// Number of parcels (locally)
label nParcels = (applyFilter_ ? parcelAddr_.count() : pointsPtr->size());
const label nParcels
(
applyFilter_ ? parcelAddr_.count() : pointsPtr->size()
);
// Total number of parcels on all processes
const label nTotParcels = returnReduce(nParcels, sumOp<label>());
@ -163,9 +170,9 @@ bool Foam::functionObjects::vtkCloud::writeCloud
<< exit(FatalError);
}
if (Pstream::master())
if (UPstream::master())
{
mkDir(file.path());
Foam::mkDir(file.path());
os.open(file);
format = writeOpts_.newFormatter(os);
@ -238,7 +245,7 @@ bool Foam::functionObjects::vtkCloud::writeCloud
}
if (Pstream::master())
if (UPstream::master())
{
format().flush();
format().endDataArray();
@ -270,7 +277,7 @@ bool Foam::functionObjects::vtkCloud::writeCloud
// Write fields
if (Pstream::master())
if (UPstream::master())
{
if (useVerts_)
{
@ -282,13 +289,28 @@ bool Foam::functionObjects::vtkCloud::writeCloud
}
}
DynamicList<word> written(obrTmp.size());
DynamicList<word> written(obrTmp.size() + currCloud.objectRegistry::size());
written.append(writeFields<label>(format, obrTmp, nTotParcels));
written.append(writeFields<scalar>(format, obrTmp, nTotParcels));
written.append(writeFields<vector>(format, obrTmp, nTotParcels));
written.push_back
(
writeFields<label>(format, obrTmp, nTotParcels)
);
written.push_back
(
writeFields<scalar>(format, obrTmp, nTotParcels)
);
written.push_back
(
writeFields<vector>(format, obrTmp, nTotParcels)
);
if (Pstream::master())
// Any cloudFunctions results
written.push_back
(
writeFields<scalar>(format, currCloud, nTotParcels)
);
if (UPstream::master())
{
if (useVerts_)
{
@ -415,12 +437,15 @@ bool Foam::functionObjects::vtkCloud::read(const dictionary& dict)
selectClouds_.clear();
dict.readIfPresent("clouds", selectClouds_);
selectClouds_.uniq();
if (selectClouds_.empty())
{
selectClouds_.resize(1);
selectClouds_.first() =
dict.getOrDefault<word>("cloud", cloud::defaultName);
word cloudName;
if (dict.readIfPresent("cloud", cloudName))
{
selectClouds_.push_back(std::move(cloudName));
}
}
selectFields_.clear();
@ -463,7 +488,12 @@ bool Foam::functionObjects::vtkCloud::execute()
bool Foam::functionObjects::vtkCloud::write()
{
const wordList cloudNames(mesh_.sortedNames<cloud>(selectClouds_));
const wordList cloudNames
(
selectClouds_.empty()
? mesh_.sortedNames<cloud>()
: mesh_.sortedNames<cloud>(selectClouds_)
);
if (cloudNames.empty())
{
@ -498,7 +528,7 @@ bool Foam::functionObjects::vtkCloud::write()
Log << " cloud : "
<< time_.relativePath(outputName) << endl;
if (Pstream::master())
if (UPstream::master())
{
// Add to file-series and emit as JSON
fileName seriesName(vtk::seriesWriter::base(outputName));

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2020 OpenCFD Ltd.
Copyright (C) 2018-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -81,7 +81,7 @@ Description
Property | Description | Required | Default
type | Type name: vtkCloud | yes |
clouds | List of clouds (name or regex) | no |
cloud | Cloud name | no | defaultCloud
cloud | Cloud name | no |
fields | List of fields (name or regex) | no |
selection | Parcel selection control | no | empty-dict
\endtable
@ -160,7 +160,7 @@ class vtkCloud
public fvMeshFunctionObject,
public Foam::Detail::parcelSelection
{
// Private data
// Private Data
//- Writer options
vtk::outputOptions writeOpts_;
@ -209,7 +209,7 @@ class vtkCloud
wordList writeFields
(
autoPtr<vtk::formatter>& format,
const objectRegistry& obrTmp,
const objectRegistry& obr,
const label nTotParcels
) const;

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2022 OpenCFD Ltd.
Copyright (C) 2018-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -33,7 +33,7 @@ template<class Type>
Foam::wordList Foam::functionObjects::vtkCloud::writeFields
(
autoPtr<vtk::formatter>& format,
const objectRegistry& obrTmp,
const objectRegistry& obr,
const label nTotParcels
) const
{
@ -55,16 +55,22 @@ Foam::wordList Foam::functionObjects::vtkCloud::writeFields
// Fields are not always on all processors (eg, multi-component parcels).
// Thus need to resolve names between all processors.
wordList fieldNames(obrTmp.names<IOField<Type>>());
wordList fieldNames =
(
selectFields_.size()
? obr.names<IOField<Type>>(selectFields_)
: obr.names<IOField<Type>>()
);
Pstream::combineReduce(fieldNames, ListOps::uniqueEqOp<word>());
Foam::sort(fieldNames); // Consistent order
for (const word& fieldName : fieldNames)
{
const List<Type>* fldPtr = obrTmp.findObject<IOField<Type>>(fieldName);
const List<Type>& values = (fldPtr ? *fldPtr : List<Type>());
const List<Type>* fldPtr = obr.findObject<IOField<Type>>(fieldName);
const List<Type>& values = (fldPtr ? *fldPtr : List<Type>::null());
if (Pstream::master())
if (UPstream::master())
{
if (std::is_same<label, typename pTraits<Type>::cmptType>::value)
{
@ -93,7 +99,7 @@ Foam::wordList Foam::functionObjects::vtkCloud::writeFields
vtk::writeListParallel(format.ref(), values);
}
if (Pstream::master())
if (UPstream::master())
{
// Non-legacy
format().flush();

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -43,7 +43,7 @@ static inline void writeMeasured_binary
const UList<floatVector>& points
)
{
for (const floatVector& p : points)
for (const auto& p : points)
{
os.write(p.x());
os.write(p.y());
@ -59,9 +59,9 @@ static inline label writeMeasured_ascii
const UList<floatVector>& points
)
{
for (const floatVector& p : points)
for (const auto& p : points)
{
os.write(++pointId, 8); // 1-index and an unusual width
os.writeInt(++pointId, 8); // 1-index and an unusual width
os.write(p.x());
os.write(p.y());
os.write(p.z());
@ -79,75 +79,24 @@ static inline label writeMeasured_ascii
bool Foam::ensightOutput::writeCloudPositions
(
ensightFile& os,
const fvMesh& mesh,
const word& cloudName,
bool exists
DynamicList<floatVector>& positions,
const globalIndex& procAddr
)
{
label nLocalParcels(0);
autoPtr<Cloud<passiveParticle>> parcelsPtr;
// Total number of parcels across all ranks
const label nTotParcels = procAddr.totalSize();
if (exists)
{
parcelsPtr.reset(new Cloud<passiveParticle>(mesh, cloudName, false));
nLocalParcels = parcelsPtr().size();
}
// Total number of parcels on all processes
const label nTotParcels = returnReduce(nLocalParcels, sumOp<label>());
bool noCloud(!procAddr.totalSize());
Pstream::broadcast(noCloud);
if (UPstream::master())
{
os.beginParticleCoordinates(nTotParcels);
}
if (!nTotParcels)
if (noCloud)
{
return false; // DONE
}
// Gather sizes (offsets irrelevant)
const globalIndex procAddr(globalIndex::gatherOnly{}, nLocalParcels);
DynamicList<floatVector> positions;
positions.reserve(UPstream::master() ? procAddr.maxSize() : nLocalParcels);
// Extract positions from parcel.
// Store as floatVector, since that is what Ensight will write anyhow
if (parcelsPtr)
{
const auto& parcels = *parcelsPtr;
positions.resize_nocopy(parcels.size()); // same as nLocalParcels
auto outIter = positions.begin();
if (std::is_same<float, vector::cmptType>::value)
{
for (const passiveParticle& p : parcels)
{
*outIter = p.position();
++outIter;
}
}
else
{
for (const passiveParticle& p : parcels)
{
vector pos(p.position());
(*outIter).x() = narrowFloat(pos.x());
(*outIter).y() = narrowFloat(pos.y());
(*outIter).z() = narrowFloat(pos.z());
++outIter;
}
}
parcelsPtr.reset(nullptr);
return false; // All empty
}
if (UPstream::master())
@ -178,6 +127,9 @@ bool Foam::ensightOutput::writeCloudPositions
}
positions.clear();
positions.reserve_nocopy(procAddr.maxNonLocalSize());
// Receive and write
for (const label proci : procAddr.subProcs())
{
@ -186,6 +138,7 @@ bool Foam::ensightOutput::writeCloudPositions
if (procSize)
{
positions.resize_nocopy(procSize);
UIPstream::read
(
UPstream::commsTypes::scheduled,
@ -205,7 +158,7 @@ bool Foam::ensightOutput::writeCloudPositions
}
}
}
else
else if (UPstream::is_subrank())
{
if (positions.size())
{
@ -223,4 +176,86 @@ bool Foam::ensightOutput::writeCloudPositions
}
bool Foam::ensightOutput::writeCloudPositions
(
ensightFile& os,
DynamicList<floatVector>& positions
)
{
return ensightOutput::writeCloudPositions
(
os,
positions,
// Gather sizes (offsets irrelevant)
globalIndex(globalIndex::gatherOnly{}, positions.size())
);
}
bool Foam::ensightOutput::writeCloudPositions
(
ensightFile& os,
const fvMesh& mesh,
const word& cloudName,
bool exists
)
{
autoPtr<Cloud<passiveParticle>> parcelsPtr;
if (exists)
{
parcelsPtr.reset(new Cloud<passiveParticle>(mesh, cloudName, false));
}
const label nLocalParcels
(
parcelsPtr ? parcelsPtr->size() : 0
);
// Gather sizes (offsets irrelevant)
// and total number of parcels (all processes)
const globalIndex procAddr(globalIndex::gatherOnly{}, nLocalParcels);
// Extract positions from parcel.
// Store as floatVector, since that is what Ensight will write anyhow
DynamicList<floatVector> positions;
positions.reserve(UPstream::master() ? procAddr.maxSize() : nLocalParcels);
if (parcelsPtr)
{
const auto& parcels = *parcelsPtr;
positions.resize_nocopy(parcels.size()); // same as nLocalParcels
auto iter = positions.begin();
if (std::is_same<float, vector::cmptType>::value)
{
for (const auto& p : parcels)
{
*iter = p.position();
++iter;
}
}
else
{
for (const auto& p : parcels)
{
const vector pos(p.position());
(*iter).x() = narrowFloat(pos.x());
(*iter).y() = narrowFloat(pos.y());
(*iter).z() = narrowFloat(pos.z());
++iter;
}
}
parcelsPtr.reset(nullptr);
}
return ensightOutput::writeCloudPositions(os, positions, procAddr);
}
// ************************************************************************* //

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -40,6 +40,8 @@ SourceFiles
#include "ensightFile.H"
#include "IOField.H"
#include "DynamicList.H"
#include "vector.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -48,6 +50,7 @@ namespace Foam
// Forward Declarations
class fvMesh;
class globalIndex;
namespace ensightOutput
{
@ -55,6 +58,34 @@ namespace ensightOutput
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Write cloud positions
bool writeCloudPositions
(
//! Output file (must be valid on master)
ensightFile& os,
//! The positions (measured data) to write.
//! Also used as intermediate buffer (on master)
DynamicList<floatVector>& positions,
//! The global sizes of \p positions (must be valid on master)
//! and consistent with \p positions dimensions
const globalIndex& procAddr
);
//- Write cloud positions
bool writeCloudPositions
(
//! Output file (must be valid on master)
ensightFile& os,
//! The positions (measured data) to write.
//! Also used as intermediate buffer (on master)
DynamicList<floatVector>& positions
);
//- Write cloud positions
bool writeCloudPositions
(
@ -80,7 +111,23 @@ bool writeCloudField
ensightFile& os,
//! The cloud field
const IOField<Type>& field
const UList<Type>& field,
//! The global sizes of \p field (must be valid on master)
//! and consistent with \p field dimensions
const globalIndex& procAddr
);
//- Write cloud field, returning true if the field is non-empty.
template<class Type>
bool writeCloudField
(
//! Output file (must be valid on master)
ensightFile& os,
//! The cloud field
const UList<Type>& field
);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2022 OpenCFD Ltd.
Copyright (C) 2016-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -39,7 +39,6 @@ Foam::label Foam::ensightOutput::Detail::writeCloudFieldContent
label count
)
{
// Write master data
for (Type val : field) // <-- working on a copy!
{
if (mag(val) < 1e-90) // approximately root(ROOTVSMALL)
@ -70,18 +69,20 @@ template<class Type>
bool Foam::ensightOutput::writeCloudField
(
ensightFile& os,
const IOField<Type>& field
const UList<Type>& field,
const globalIndex& procAddr
)
{
if (returnReduceAnd(field.empty()))
bool allEmpty(!procAddr.totalSize());
Pstream::broadcast(allEmpty);
if (allEmpty)
{
return false;
return false; // All empty
}
// Gather sizes (offsets irrelevant)
const globalIndex procAddr(globalIndex::gatherOnly{}, field.size());
if (Pstream::master())
if (UPstream::master())
{
// 6 values per line
label count = 0;
@ -128,7 +129,7 @@ bool Foam::ensightOutput::writeCloudField
os.newline();
}
}
else
else if (UPstream::is_subrank())
{
if (field.size())
{
@ -146,6 +147,23 @@ bool Foam::ensightOutput::writeCloudField
}
template<class Type>
bool Foam::ensightOutput::writeCloudField
(
ensightFile& os,
const UList<Type>& field
)
{
return ensightOutput::writeCloudField
(
os,
field,
// Gather sizes (offsets irrelevant)
globalIndex(globalIndex::gatherOnly{}, field.size())
);
}
template<class Type>
bool Foam::ensightOutput::readWriteCloudField
(
@ -162,10 +180,11 @@ bool Foam::ensightOutput::readWriteCloudField
IOobject io(fieldObject);
io.readOpt(IOobject::READ_IF_PRESENT);
io.registerObject(IOobject::NO_REGISTER);
IOField<Type> field(io);
writeCloudField(os, field);
ensightOutput::writeCloudField(os, field);
}
return true;