diff --git a/src/fileFormats/ensight/file/ensightFile.C b/src/fileFormats/ensight/file/ensightFile.C index dca789e4c15d0f4a59757cb466a1b0ffbd90fec7..6ee8f464baf4340391d4c300e13fe03274cafb91 100644 --- a/src/fileFormats/ensight/file/ensightFile.C +++ b/src/fileFormats/ensight/file/ensightFile.C @@ -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(); } +} - return *this; + +void Foam::ensightFile::writeFloat(const double val, const int fieldWidth) +{ + float work(narrowFloat(val)); + + writeFloat(work, fieldWidth); } -Foam::Ostream& Foam::ensightFile::write(const double val) +Foam::Ostream& Foam::ensightFile::write(const int32_t val) { - float fvalue(narrowFloat(val)); + writeInt(val, 10); + return *this; +} + - return write(fvalue); +Foam::Ostream& Foam::ensightFile::write(const int64_t val) +{ + writeInt(val, 10); + return *this; } -Foam::Ostream& Foam::ensightFile::write -( - const label value, - const label fieldWidth -) +Foam::Ostream& Foam::ensightFile::write(const float val) { - if (format() == IOstreamOption::BINARY) - { - write(value); - } - else - { - stdStream().width(fieldWidth); - stdStream() << value; - syncState(); - } + writeFloat(val, 12); + return *this; +} + +Foam::Ostream& Foam::ensightFile::write(const double val) +{ + 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(); } diff --git a/src/fileFormats/ensight/file/ensightFile.H b/src/fileFormats/ensight/file/ensightFile.H index 2e16f0e95d291c80f0214a7276060d3adb0f7b9e..79ed2f0a7b5b1a4f113832287c247b1456437e9b 100644 --- a/src/fileFormats/ensight/file/ensightFile.H +++ b/src/fileFormats/ensight/file/ensightFile.H @@ -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; diff --git a/src/fileFormats/ensight/mesh/ensightMesh.C b/src/fileFormats/ensight/mesh/ensightMesh.C index 8681c14abbcb1ba9eaeba5c8c2b38ce46e09fb19..d57478fa2d82a44342354cc4d015f8d40c2ae96d 100644 --- a/src/fileFormats/ensight/mesh/ensightMesh.C +++ b/src/fileFormats/ensight/mesh/ensightMesh.C @@ -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()); + } } diff --git a/src/fileFormats/ensight/mesh/ensightMesh.H b/src/fileFormats/ensight/mesh/ensightMesh.H index fb61d716ebd2efce8df315a32ed0ab17138ebb03..a0cc1cbf54da6d55af6e542da23d0928afc15ddd 100644 --- a/src/fileFormats/ensight/mesh/ensightMesh.H +++ b/src/fileFormats/ensight/mesh/ensightMesh.H @@ -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; }; diff --git a/src/fileFormats/ensight/part/cells/ensightCells.H b/src/fileFormats/ensight/part/cells/ensightCells.H index ac64cd0d81c6fa3f7aec5b420e3825d332df6a56..5b2197a20690ed3b698c738baa4e5ffa1be9a723 100644 --- a/src/fileFormats/ensight/part/cells/ensightCells.H +++ b/src/fileFormats/ensight/part/cells/ensightCells.H @@ -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" + ); }; diff --git a/src/fileFormats/ensight/part/cells/ensightCellsIO.C b/src/fileFormats/ensight/part/cells/ensightCellsIO.C index f45f70a4176983ec5e246b833d2a6836331569d3..2223861df624a51b668cc99db7fec5ff3625b312 100644 --- a/src/fileFormats/ensight/part/cells/ensightCellsIO.C +++ b/src/fileFormats/ensight/part/cells/ensightCellsIO.C @@ -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<> diff --git a/src/finiteArea/output/ensight/ensightFaMesh.H b/src/finiteArea/output/ensight/ensightFaMesh.H index 394b4d5463eaff73c661d63fa0a4f17be24b5608..ce3deb7534a5307dc37a111154ac09f02110b91f 100644 --- a/src/finiteArea/output/ensight/ensightFaMesh.H +++ b/src/finiteArea/output/ensight/ensightFaMesh.H @@ -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; }; diff --git a/src/functionObjects/lagrangian/Make/files b/src/functionObjects/lagrangian/Make/files index e349d4f5454fbc240faf2ceb27ef1a052c0ea176..51e5c3b1128b7d52fb022c2488b0628c5cc3740a 100644 --- a/src/functionObjects/lagrangian/Make/files +++ b/src/functionObjects/lagrangian/Make/files @@ -5,5 +5,6 @@ icoUncoupledKinematicCloud/icoUncoupledKinematicCloud.C dsmcFields/dsmcFields.C vtkCloud/vtkCloud.C +ensightCloud/ensightCloudWriteObject.cxx LIB = $(FOAM_LIBBIN)/liblagrangianFunctionObjects diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.C b/src/functionObjects/lagrangian/dataCloud/dataCloud.C index e34be5bc705d6697564e9131e2fe63d2a06c9831..8ff65489f56bccbc022631dff7b9be9f9dccf44d 100644 --- a/src/functionObjects/lagrangian/dataCloud/dataCloud.C +++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.C @@ -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()) { diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.H b/src/functionObjects/lagrangian/dataCloud/dataCloud.H index 4943ccf1c7040c6637314b31429556a0db05c9cd..33b2791b6bb9a6a6d8861eb0abddb7cb72623bcc 100644 --- a/src/functionObjects/lagrangian/dataCloud/dataCloud.H +++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.H @@ -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; diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C index d2b6e16b1304d3d497c3eafa8438acfb18d8a669..67319c08efeb2b610ce61016d06be549316ef987 100644 --- a/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C +++ b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C @@ -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)) { diff --git a/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.H b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.H new file mode 100644 index 0000000000000000000000000000000000000000..ea10e6bcd748ef0258ccea902eac08d0f705690f --- /dev/null +++ b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.H @@ -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 + +// ************************************************************************* // diff --git a/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.cxx b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.cxx new file mode 100644 index 0000000000000000000000000000000000000000..01b6cdb083dd0f66fa80817d51961c3e819ff54a --- /dev/null +++ b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObject.cxx @@ -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; +} + + +// ************************************************************************* // diff --git a/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObjectImpl.cxx b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObjectImpl.cxx new file mode 100644 index 0000000000000000000000000000000000000000..01fcf08b14891f9d8347309bd2db1ebf6663e88f --- /dev/null +++ b/src/functionObjects/lagrangian/ensightCloud/ensightCloudWriteObjectImpl.cxx @@ -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; +} + + +// ************************************************************************* // diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C index 08557fd4014a2dc2a7ff875280ad46d4e72f0cfa..223fa2feab4f4d391e6f907b997bf1606c2dd91e 100644 --- a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C +++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C @@ -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) + ); + + // Any cloudFunctions results + written.push_back + ( + writeFields<scalar>(format, currCloud, nTotParcels) + ); - if (Pstream::master()) + 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)); diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.H b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.H index 9511de0db04920d6f56f9baf26240ec60b9c7191..648414eb2c388a3e005d9641e0b53d1111af3623 100644 --- a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.H +++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.H @@ -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; diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloudTemplates.C b/src/functionObjects/lagrangian/vtkCloud/vtkCloudTemplates.C index c28bc1fe4c7a062b0afb21e07bcdf3c8f33ffd8b..f61c1de41e6679de0c8c88179ba00f9280bc353c 100644 --- a/src/functionObjects/lagrangian/vtkCloud/vtkCloudTemplates.C +++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloudTemplates.C @@ -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(); diff --git a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.C b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.C index d81b259444bb9c813aa19fb64312f365acb6a765..720ad68a457ed125b8e89bdeeef5738ea342e63e 100644 --- a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.C +++ b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.C @@ -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) - { - 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) + if (noCloud) { - 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); +} + + // ************************************************************************* // diff --git a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.H b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.H index e83fbecec0cbf7e582206856ddf079135b4ab1b6..323296eb261c59da3de3156a1605708f8c833732 100644 --- a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.H +++ b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloud.H @@ -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 ); diff --git a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloudTemplates.C b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloudTemplates.C index cc865cc7d3bd0a21b7916f782a25d42993d3f3d4..85b5d8a898a51c65a5973819648f1b896ef029a0 100644 --- a/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloudTemplates.C +++ b/src/lagrangian/intermediate/conversion/ensight/ensightOutputCloudTemplates.C @@ -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;