From 1adac971507663143be0e9d7b73ce61ec7af98e1 Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Mon, 10 Sep 2018 18:28:55 +0200 Subject: [PATCH] ENH: parallel output for vtk::patchWriter (issue #926) --- .../vtk/output/foamVtkPatchWriter.C | 661 +++++++++++++----- .../vtk/output/foamVtkPatchWriter.H | 220 +++--- .../vtk/output/foamVtkPatchWriterTemplates.C | 363 ++++++++-- 3 files changed, 893 insertions(+), 351 deletions(-) diff --git a/src/conversion/vtk/output/foamVtkPatchWriter.C b/src/conversion/vtk/output/foamVtkPatchWriter.C index 3ca9500a84f..1bc43447065 100644 --- a/src/conversion/vtk/output/foamVtkPatchWriter.C +++ b/src/conversion/vtk/output/foamVtkPatchWriter.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd. + \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,19 +25,53 @@ License #include "foamVtkPatchWriter.H" #include "foamVtkOutput.H" - +#include "globalIndex.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::vtk::patchWriter::beginPiece() { - if (!legacy_) + // Basic sizes + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + + nLocalPoints_ = nLocalFaces_ = nLocalVerts_ = 0; + + for (const label patchId : patchIDs_) + { + const polyPatch& pp = patches[patchId]; + + nLocalPoints_ += pp.nPoints(); + nLocalFaces_ += pp.size(); + + for (const face& f : pp) + { + nLocalVerts_ += f.size(); + } + } + + numberOfPoints_ = nLocalPoints_; + numberOfCells_ = nLocalFaces_; + + if (parallel_) + { + reduce(numberOfPoints_, sumOp<label>()); + reduce(numberOfCells_, sumOp<label>()); + } + + + // Nothing else to do for legacy + if (legacy()) return; + + + if (format_) { format() - .openTag(vtk::fileTag::PIECE) - .xmlAttr(vtk::fileAttr::NUMBER_OF_POINTS, nPoints_) - .xmlAttr(vtk::fileAttr::NUMBER_OF_POLYS, nFaces_) - .closeTag(); + .tag + ( + vtk::fileTag::PIECE, + vtk::fileAttr::NUMBER_OF_POINTS, numberOfPoints_, + vtk::fileAttr::NUMBER_OF_POLYS, numberOfCells_ + ); } } @@ -46,135 +80,244 @@ void Foam::vtk::patchWriter::writePoints() { const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - const uint64_t payLoad = (nPoints_*3*sizeof(float)); - - if (legacy_) + if (format_) { - legacy::beginPoints(os_, nPoints_); + if (legacy()) + { + legacy::beginPoints(os_, numberOfPoints_); + } + else + { + const uint64_t payLoad = + vtk::sizeofData<float, 3>(numberOfPoints_); + + format() + .tag(vtk::fileTag::POINTS) + .beginDataArray<float, 3>(vtk::dataArrayAttr::POINTS); + + format().writeSize(payLoad); + } } - else + + + if (parallel_ ? Pstream::master() : true) { - format().tag(vtk::fileTag::POINTS) - .openDataArray<float, 3>(vtk::dataArrayAttr::POINTS) - .closeTag(); + for (const label patchId : patchIDs_) + { + const polyPatch& pp = patches[patchId]; + + vtk::writeList(format(), pp.localPoints()); + } } - format().writeSize(payLoad); - for (const label patchId : patchIDs_) + if (parallel_) { - const polyPatch& pp = patches[patchId]; + // Patch Ids are identical across all processes + const label nPatches = patchIDs_.size(); + + if (Pstream::master()) + { + pointField recv; + + // Receive each point field and write + for + ( + int slave=Pstream::firstSlave(); + slave<=Pstream::lastSlave(); + ++slave + ) + { + IPstream fromSlave(Pstream::commsTypes::blocking, slave); + + for (label i=0; i < nPatches; ++i) + { + fromSlave >> recv; - vtk::writeList(format(), pp.localPoints()); + vtk::writeList(format(), recv); + } + } + } + else + { + // Send each point field to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + for (const label patchId : patchIDs_) + { + const polyPatch& pp = patches[patchId]; + + toMaster << pp.localPoints(); + } + } } - format().flush(); - if (!legacy_) + + if (format_) { - format() - .endDataArray() - .endTag(vtk::fileTag::POINTS); + format().flush(); + format().endDataArray(); + + if (!legacy()) + { + format() + .endTag(vtk::fileTag::POINTS); + } } } -void Foam::vtk::patchWriter::writePolysLegacy() +void Foam::vtk::patchWriter::writePolysLegacy +( + const globalIndex& pointOffsets +) { const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - // connectivity count without additional storage (done internally) - uint64_t nConnectivity = 0; - for (const label patchId : patchIDs_) - { - const polyPatch& pp = patches[patchId]; + // Connectivity count without additional storage (done internally) - forAll(pp, facei) - { - nConnectivity += pp[facei].size(); - } + label nFaces = nLocalFaces_; + label nVerts = nLocalVerts_; + + if (parallel_) + { + reduce(nFaces, sumOp<label>()); + reduce(nVerts, sumOp<label>()); } - legacy::beginPolys(os_, nFaces_, nConnectivity); + if (nFaces != numberOfCells_) + { + FatalErrorInFunction + << "Expecting " << numberOfCells_ + << " faces, but found " << nFaces + << exit(FatalError); + } + legacy::beginPolys(os_, nFaces, nVerts); - // legacy: size + connectivity together - // [nPts, id1, id2, ..., nPts, id1, id2, ...] + labelList vertLabels(nLocalFaces_ + nLocalVerts_); - label off = 0; - for (const label patchId : patchIDs_) { - const polyPatch& pp = patches[patchId]; + // Legacy: size + connectivity together + // [nPts, id1, id2, ..., nPts, id1, id2, ...] - forAll(pp, facei) + auto iter = vertLabels.begin(); + + label off = pointOffsets.localStart(); + + for (const label patchId : patchIDs_) { - const face& f = pp.localFaces()[facei]; + const polyPatch& pp = patches[patchId]; - format().write(f.size()); // The size prefix - forAll(f, fi) + for (const face& f : pp.localFaces()) { - format().write(off + f[fi]); + *iter = f.size(); // The size prefix + ++iter; + + for (const label pfi : f) + { + *iter = pfi + off; // Face vertex label + ++iter; + } } + off += pp.nPoints(); } - off += pp.nPoints(); } - format().flush(); + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertLabels); + } + else + { + vtk::writeList(format(), vertLabels); + } + + if (format_) + { + format().flush(); + } } -void Foam::vtk::patchWriter::writePolys() +void Foam::vtk::patchWriter::writePolys +( + const globalIndex& pointOffsets +) { + if (format_) + { + format().tag(vtk::fileTag::POLYS); + } + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); // // 'connectivity' // + { + labelList vertLabels(nLocalVerts_); - format().tag(vtk::fileTag::POLYS); + label nVerts = nLocalVerts_; - // - // 'connectivity' - // - { - // payload count - uint64_t payLoad = 0; - for (const label patchId : patchIDs_) + if (parallel_) { - const polyPatch& pp = patches[patchId]; - - forAll(pp, facei) - { - payLoad += pp[facei].size(); - } + reduce(nVerts, sumOp<label>()); } - format().openDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY) - .closeTag(); + if (format_) + { + const uint64_t payLoad = + vtk::sizeofData<label>(nVerts); - // payload size - format().writeSize(payLoad * sizeof(label)); + format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY); + format().writeSize(payLoad); + } - label off = 0; - for (const label patchId : patchIDs_) { - const polyPatch& pp = patches[patchId]; + // XML: connectivity only + // [id1, id2, ..., id1, id2, ...] + + auto iter = vertLabels.begin(); + + label off = pointOffsets.localStart(); - forAll(pp, facei) + for (const label patchId : patchIDs_) { - const face& f = pp.localFaces()[facei]; - forAll(f, fi) + const polyPatch& pp = patches[patchId]; + + for (const face& f : pp.localFaces()) { - format().write(off + f[fi]); + for (const label pfi : f) + { + *iter = pfi + off; // Face vertex label + ++iter; + } } + off += pp.nPoints(); } - - off += pp.nPoints(); } - format().flush(); - format() - .endDataArray(); + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertLabels); + } + else + { + vtk::writeList(format(), vertLabels); + } + + if (format_) + { + format().flush(); + format().endDataArray(); + } } @@ -182,44 +325,64 @@ void Foam::vtk::patchWriter::writePolys() // 'offsets' (connectivity offsets) // { - format() - .openDataArray<label>(vtk::dataArrayAttr::OFFSETS) - .closeTag(); + labelList vertOffsets(nLocalFaces_); + label nOffs = vertOffsets.size(); + + // global connectivity offsets + const globalIndex procOffset(nLocalVerts_); + + if (parallel_) + { + reduce(nOffs, sumOp<label>()); + } + + if (format_) + { + const uint64_t payLoad = + vtk::sizeofData<label>(nOffs); + + format().beginDataArray<label>(vtk::dataArrayAttr::OFFSETS); + format().writeSize(payLoad); + } + + + label off = procOffset.localStart(); - // payload size - format().writeSize(nFaces_ * sizeof(label)); + auto iter = vertOffsets.begin(); - label off = 0; for (const label patchId : patchIDs_) { const polyPatch& pp = patches[patchId]; - forAll(pp, facei) + for (const face& f : pp) { - off += pp[facei].size(); - - format().write(off); + off += f.size(); // End offset + *iter = off; + ++iter; } } - format().flush(); - format().endDataArray(); - } - format().endTag(vtk::fileTag::POLYS); -} + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertOffsets); + } + else + { + vtk::writeList(format_.ref(), vertOffsets); + } -void Foam::vtk::patchWriter::writeMesh() -{ - writePoints(); - if (legacy_) - { - writePolysLegacy(); + if (format_) + { + format().flush(); + format().endDataArray(); + } } - else + + if (format_) { - writePolys(); + format().endTag(vtk::fileTag::POLYS); } } @@ -229,161 +392,273 @@ void Foam::vtk::patchWriter::writeMesh() Foam::vtk::patchWriter::patchWriter ( const fvMesh& mesh, - const fileName& baseName, - const vtk::outputOptions outOpts, - const bool nearCellValue, - const labelList& patchIDs + const labelList& patchIDs, + const vtk::outputOptions opts, + const bool useNearCellValue ) : + vtk::fileWriter(vtk::fileTag::POLY_DATA, opts), mesh_(mesh), - legacy_(outOpts.legacy()), - format_(), - nearCellValue_(nearCellValue), patchIDs_(patchIDs), - os_(), - nPoints_(0), - nFaces_(0) + useNearCellValue_(useNearCellValue), + numberOfPoints_(0), + numberOfCells_(0), + nLocalPoints_(0), + nLocalFaces_(0), + nLocalVerts_(0) { - outputOptions opts(outOpts); - opts.append(false); // No append supported + // We do not currently support append mode + opts_.append(false); +} - os_.open((baseName + (legacy_ ? ".vtk" : ".vtp")).c_str()); - format_ = opts.newFormatter(os_); - const polyBoundaryMesh& patches = mesh_.boundaryMesh(); +Foam::vtk::patchWriter::patchWriter +( + const fvMesh& mesh, + const labelList& patchIDs, + const fileName& file, + bool parallel +) +: + patchWriter(mesh, patchIDs) +{ + open(file, parallel); +} - const word& title = - ( - patchIDs_.size() == 1 - ? patches[patchIDs_.first()].name() - : "patches" - ); - // Basic sizes - nPoints_ = nFaces_ = 0; - for (const label patchId : patchIDs_) +Foam::vtk::patchWriter::patchWriter +( + const fvMesh& mesh, + const labelList& patchIDs, + const vtk::outputOptions opts, + const fileName& file, + bool parallel +) +: + patchWriter(mesh, patchIDs, opts) +{ + open(file, parallel); +} + + +Foam::vtk::patchWriter::patchWriter +( + const fvMesh& mesh, + const labelList& patchIDs, + const vtk::outputOptions opts, + const bool useNearCellValue, + const fileName& file, + bool parallel +) +: + patchWriter(mesh, patchIDs, opts, useNearCellValue) +{ + open(file, parallel); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::vtk::patchWriter::beginFile(std::string title) +{ + if (title.size()) { - const polyPatch& pp = patches[patchId]; + return vtk::fileWriter::beginFile(title); + } - nPoints_ += pp.nPoints(); - nFaces_ += pp.size(); + // Provide default title + + if (legacy()) + { + title = + ( + patchIDs_.size() == 1 + ? mesh_.boundaryMesh()[patchIDs_.first()].name() + : "patches" + ); + + return vtk::fileWriter::beginFile(title); } - if (legacy_) + // XML (inline) + + if (patchIDs_.size() == 1) { - legacy::fileHeader(format(), title, vtk::fileTag::POLY_DATA); + title = + ( + "patch='" + mesh_.boundaryMesh()[patchIDs_.first()].name() + "'" + ); } else { - // XML (inline) - - format() - .xmlHeader() - .xmlComment(title) - .beginVTKFile(vtk::fileTag::POLY_DATA, "0.1"); + title = + ( + "npatches='" + Foam::name(patchIDs_.size()) + "'" + ); } - beginPiece(); - writeMesh(); + title += + ( + " time='" + mesh_.time().timeName() + + "' index='" + Foam::name(mesh_.time().timeIndex()) + + "'" + ); + + return vtk::fileWriter::beginFile(title); } -// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // +bool Foam::vtk::patchWriter::writeGeometry() +{ + enter_Piece(); -Foam::vtk::patchWriter::~patchWriter() -{} + beginPiece(); + writePoints(); -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + const globalIndex globalPointOffset(nLocalPoints_); -void Foam::vtk::patchWriter::beginCellData(label nFields) -{ - if (legacy_) + if (legacy()) { - legacy::beginCellData(format(), nFaces_, nFields); + writePolysLegacy(globalPointOffset); } else { - format().beginCellData(); + writePolys(globalPointOffset); } + + return true; } -void Foam::vtk::patchWriter::endCellData() +bool Foam::vtk::patchWriter::beginCellData(label nFields) { - if (!legacy_) - { - format().endCellData(); - } + return enter_CellData(numberOfCells_, nFields); } -void Foam::vtk::patchWriter::beginPointData(label nFields) +bool Foam::vtk::patchWriter::beginPointData(label nFields) { - if (legacy_) - { - legacy::beginPointData(format(), nPoints_, nFields); - } - else - { - format().beginPointData(); - } + return enter_PointData(numberOfPoints_, nFields); } -void Foam::vtk::patchWriter::endPointData() +void Foam::vtk::patchWriter::writePatchIDs() { - if (!legacy_) + if (isState(outputState::CELL_DATA)) { - format().endPointData(); + ++nCellData_; } -} - - -void Foam::vtk::patchWriter::writeFooter() -{ - if (!legacy_) + else { - // slight cheat. </Piece> too - format().endTag(vtk::fileTag::PIECE); - - format().endTag(vtk::fileTag::POLY_DATA) - .endVTKFile(); + FatalErrorInFunction + << "Bad writer state (" << stateNames[state_] + << ") - should be (" << stateNames[outputState::CELL_DATA] + << ") for patchID field" << nl << endl + << exit(FatalError); } -} + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); -void Foam::vtk::patchWriter::writePatchIDs() -{ - // Patch ids first - const uint64_t payLoad = nFaces_ * sizeof(label); + label nFaces = nLocalFaces_; - if (legacy_) + if (parallel_) { - legacy::intField<1>(format(), "patchID", nFaces_); // 1 component + reduce(nFaces, sumOp<label>()); } - else + + if (format_) { - format().openDataArray<label>("patchID") - .closeTag(); + if (legacy()) + { + legacy::intField<1>(format(), "patchID", nFaces); // 1 component + } + else + { + const uint64_t payLoad = + vtk::sizeofData<label>(nFaces); + + format().beginDataArray<label>("patchID"); + format().writeSize(payLoad); + } } - format().writeSize(payLoad); + if (parallel_ ? Pstream::master() : true) + { + for (const label patchId : patchIDs_) + { + label count = patches[patchId].size(); + const label val = patchId; + + while (count--) + { + format().write(val); + } + } + } - for (const label patchId : patchIDs_) + if (parallel_) { - const label sz = mesh_.boundaryMesh()[patchId].size(); + if (Pstream::master()) + { + labelList recv; + + // Receive each pair + for + ( + int slave=Pstream::firstSlave(); + slave<=Pstream::lastSlave(); + ++slave + ) + { + IPstream fromSlave(Pstream::commsTypes::blocking, slave); + + fromSlave >> recv; - for (label facei = 0; facei < sz; ++facei) + for (label i=0; i < recv.size(); ++i) + { + label count = recv[i]; + ++i; + const label val = recv[i]; + + while (count--) + { + format().write(val); + } + } + } + } + else { - format().write(patchId); + // Send to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + + labelList send(2*patchIDs_.size()); + + // Encode as [size, id] pairs + label i = 0; + for (const label patchId : patchIDs_) + { + send[i] = patches[patchId].size(); + send[i+1] = patchId; + + i += 2; + } + + toMaster << send; } } - format().flush(); - if (!legacy_) + + if (format_) { + format().flush(); format().endDataArray(); } } diff --git a/src/conversion/vtk/output/foamVtkPatchWriter.H b/src/conversion/vtk/output/foamVtkPatchWriter.H index 86f1676a461..98ad8c32d2e 100644 --- a/src/conversion/vtk/output/foamVtkPatchWriter.H +++ b/src/conversion/vtk/output/foamVtkPatchWriter.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd. + \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,7 +25,17 @@ Class Foam::vtk::patchWriter Description - Write patch fields + Write OpenFOAM patches and patch fields in VTP or legacy vtk format. + + The file output states are managed by the Foam::vtk::fileWriter class. + FieldData (eg, TimeValue) must appear before any geometry pieces. + +Note + Parallel output is combined into a single Piece without point merging, + which is similar to using multi-piece data sets, but allows more + convenient creation as a streaming process. + In the future, the duplicate points at processor connections + may be addressed using ghost points. SourceFiles foamVtkPatchWriter.C @@ -36,66 +46,71 @@ SourceFiles #ifndef foamVtkPatchWriter_H #define foamVtkPatchWriter_H -#include "pointMesh.H" -#include "OFstream.H" +#include "foamVtkFileWriter.H" #include "volFields.H" #include "pointFields.H" -#include "indirectPrimitivePatch.H" #include "PrimitivePatchInterpolation.H" -#include "foamVtkOutputOptions.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { -class volPointInterpolation; + +// Forward declarations +class globalIndex; namespace vtk { /*---------------------------------------------------------------------------*\ - Class patchWriter Declaration + Class vtk::patchWriter Declaration \*---------------------------------------------------------------------------*/ class patchWriter +: + public vtk::fileWriter { // Private Member Data //- Reference to the OpenFOAM mesh (or subset) const fvMesh& mesh_; - //- Commonly used query - const bool legacy_; + //- The selected patch ids + labelList patchIDs_; - autoPtr<vtk::formatter> format_; + //- Use internal field value instead of patch value + bool useNearCellValue_; - const bool nearCellValue_; + //- The numer of field points for the current Piece + label numberOfPoints_; - const labelList patchIDs_; + //- The numer of field cells (faces) for the current Piece + label numberOfCells_; - std::ofstream os_; + //- Local number of points + label nLocalPoints_; - label nPoints_; + //- Local number of faces + label nLocalFaces_; - label nFaces_; + //- Local face vertices (connectivity) count. Sum of face sizes. + label nLocalVerts_; // Private Member Functions - //- Begin piece + //- Determing sizes (nLocalPoints_, nLocalFaces_, nLocalVerts_), + //- and begin piece. void beginPiece(); //- Write patch points void writePoints(); - //- Write patch faces - void writePolysLegacy(); + //- Write patch faces, legacy format + void writePolysLegacy(const globalIndex& pointOffsets); //- Write patch faces - void writePolys(); - - //- Write mesh topology - void writeMesh(); + void writePolys(const globalIndex& pointOffsets); //- No copy construct @@ -109,104 +124,137 @@ public: // Constructors - //- Construct from components + //- Construct from components (default format INLINE_BASE64) + // \param useNearCellValue to use cell instead of patch values + patchWriter + ( + const fvMesh& mesh, + const labelList& patchIDs, + const vtk::outputOptions opts = vtk::formatType::INLINE_BASE64, + const bool useNearCellValue = false + ); + + //- Construct from components (default format INLINE_BASE64), + //- and open the file for writing. + // The file name is with/without an extension. + patchWriter + ( + const fvMesh& mesh, + const labelList& patchIDs, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + //- Construct from components (default format INLINE_BASE64), + //- Construct from components and open the file for writing. + // The file name is with/without an extension. + patchWriter + ( + const fvMesh& mesh, + const labelList& patchIDs, + const vtk::outputOptions opts, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + //- Construct from components and open the file for writing. + // The file name is with/without an extension. + // \param useNearCellValue to use cell instead of patch values patchWriter ( const fvMesh& mesh, - const fileName& baseName, - const vtk::outputOptions outOpts, - const bool nearCellValue, - const labelList& patchIDs + const labelList& patchIDs, + const vtk::outputOptions opts, + const bool useNearCellValue, + const fileName& file, + bool parallel = Pstream::parRun() ); //- Destructor - ~patchWriter(); + virtual ~patchWriter() = default; // Member Functions - inline std::ofstream& os() - { - return os_; - } - - inline vtk::formatter& format() - { - return *format_; - } + //- File extension for current format type. + using vtk::fileWriter::ext; - inline label nPoints() const + //- File extension for given output type + inline static word ext(vtk::outputOptions opts) { - return nPoints_; + return opts.ext(vtk::fileTag::POLY_DATA); } - inline label nFaces() const + //- The patch IDs + inline const labelList& patchIDs() const { - return nFaces_; + return patchIDs_; } - //- Open write for CellData of count fields. - // The parameters are only used for the legacy format. - void beginCellData(label nFields); - - //- Close write for CellData - void endCellData(); - - //- Open write for PointData of count fields - // The parameters are only used for the legacy format. - void beginPointData(label nFields); - - //- Close write for PointData - void endPointData(); - - //- Write cellIDs + //- Write file header (non-collective) + // \note Expected calling states: (OPENED). + virtual bool beginFile(std::string title = ""); + + //- Write patch topology + // Also writes the file header if not previously written. + // \note Must be called prior to writing CellData or PointData + virtual bool writeGeometry(); + + //- Begin CellData output section for specified number of fields. + // Must be called prior to writing any cell data fields. + // \param nFields is for legacy format only. + // When nFields=0, this a no-op for legacy format. + // \note Expected calling states: (PIECE | POINT_DATA). + // + // \return True if the state changed + virtual bool beginCellData(label nFields = 0); + + //- Begin PointData for specified number of fields. + // Must be called prior to writing any point data fields. + // \param nFields is for legacy format only. + // When nFields=0, this a no-op for legacy format. + // \note Expected calling states: (PIECE | CELL_DATA). + // + // \return True if the state changed + virtual bool beginPointData(label nFields = 0); + + + //- Write patch ids as CellData. + // Must be called within the CELL_DATA state. void writePatchIDs(); - //- Write file footer - void writeFooter(); - - // Write fields (individually) + // Write - //- Write volume field + //- Write point field template<class Type, template<class> class PatchField> - void write(const GeometricField<Type, PatchField, volMesh>& field); + void write + ( + const GeometricField<Type, PatchField, pointMesh>& field + ); - //- Write point fields + //- Write volume field template<class Type, template<class> class PatchField> - void write(const GeometricField<Type, PatchField, pointMesh>& field); - - //- Write point-interpolated volume field - template<class Type> void write ( - const PrimitivePatchInterpolation<primitivePatch>& pInterp, - const GeometricField<Type, fvPatchField, volMesh>& field + const GeometricField<Type, PatchField, volMesh>& field ); - - // Write fields (collectively) - - //- Write multiple volume/point fields - template<class Type, template<class> class PatchField, class GeoMesh> + //- Write volume field with point interpolation + template<class Type> void write ( - const UPtrList - < - const GeometricField<Type, PatchField, GeoMesh> - >& flds + const GeometricField<Type, fvPatchField, volMesh>& field, + const PrimitivePatchInterpolation<primitivePatch>& pInterp ); - //- Write multiple point-interpolated volume fields + //- Write volume field with point interpolation template<class Type> void write ( - const PrimitivePatchInterpolation<primitivePatch>& pInterp, - const UPtrList - < - const GeometricField<Type, fvPatchField, volMesh> - >& flds + const GeometricField<Type, fvPatchField, volMesh>& field, + const PrimitivePatchInterpolation<primitivePatch>* pInterp ); }; diff --git a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C index 0bc96023904..d8d59a35e41 100644 --- a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C +++ b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation - \\/ M anipulation | Copyright (C) 2016-2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd. + \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -31,42 +31,109 @@ License template<class Type, template<class> class PatchField> void Foam::vtk::patchWriter::write ( - const GeometricField<Type, PatchField, volMesh>& field + const GeometricField<Type, PatchField, pointMesh>& field ) { - const int nCmpt(pTraits<Type>::nComponents); - const uint64_t payLoad(nFaces_ * nCmpt * sizeof(float)); - - if (legacy_) + if (isState(outputState::POINT_DATA)) { - legacy::floatField<nCmpt>(format(), field.name(), nFaces_); + ++nPointData_; } else { - format().openDataArray<float, nCmpt>(field.name()) - .closeTag(); + FatalErrorInFunction + << "Bad writer state (" << stateNames[state_] + << ") - should be (" << stateNames[outputState::POINT_DATA] + << ") for field " << field.name() << nl << endl + << exit(FatalError); + } + + const direction nCmpt(pTraits<Type>::nComponents); + + label nPoints = nLocalPoints_; + + if (parallel_) + { + reduce(nPoints, sumOp<label>()); } - format().writeSize(payLoad); - for (const label patchId : patchIDs_) + if (format_) { - const auto& pfld = field.boundaryField()[patchId]; + if (legacy()) + { + legacy::floatField<nCmpt>(format(), field.name(), nPoints); + } + else + { + const uint64_t payLoad = + vtk::sizeofData<float, nCmpt>(nPoints); + + format().beginDataArray<float, nCmpt>(field.name()); + format().writeSize(payLoad); + } + } - if (nearCellValue_) + + if (parallel_ ? Pstream::master() : true) + { + for (const label patchId : patchIDs_) { + const auto& pfld = field.boundaryField()[patchId]; + vtk::writeList(format(), pfld.patchInternalField()()); } + } + + + if (parallel_) + { + // Patch Ids are identical across all processes + const label nPatches = patchIDs_.size(); + + if (Pstream::master()) + { + Field<Type> recv; + + // Receive each patch field and write + for + ( + int slave=Pstream::firstSlave(); + slave<=Pstream::lastSlave(); + ++slave + ) + { + IPstream fromSlave(Pstream::commsTypes::blocking, slave); + + for (label i=0; i < nPatches; ++i) + { + fromSlave >> recv; + + vtk::writeList(format(), recv); + } + } + } else { - vtk::writeList(format(), pfld); + // Send each patch field to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + for (const label patchId : patchIDs_) + { + const auto& pfld = field.boundaryField()[patchId]; + + toMaster << pfld.patchInternalField()(); + } } } - format().flush(); - if (!legacy_) + if (format_) { + format().flush(); format().endDataArray(); } } @@ -75,35 +142,122 @@ void Foam::vtk::patchWriter::write template<class Type, template<class> class PatchField> void Foam::vtk::patchWriter::write ( - const GeometricField<Type, PatchField, pointMesh>& field + const GeometricField<Type, PatchField, volMesh>& field ) { - const int nCmpt(pTraits<Type>::nComponents); - const uint64_t payLoad(nPoints_ * nCmpt * sizeof(float)); - - if (legacy_) + if (isState(outputState::CELL_DATA)) { - legacy::floatField<nCmpt>(format(), field.name(), nPoints_); + ++nCellData_; } else { - format().openDataArray<float, nCmpt>(field.name()) - .closeTag(); + FatalErrorInFunction + << "Bad writer state (" << stateNames[state_] + << ") - should be (" << stateNames[outputState::CELL_DATA] + << ") for field " << field.name() << nl << endl + << exit(FatalError); + } + + const direction nCmpt(pTraits<Type>::nComponents); + + label nFaces = nLocalFaces_; + + if (parallel_) + { + reduce(nFaces, sumOp<label>()); + } + + + if (format_) + { + if (legacy()) + { + legacy::floatField<nCmpt>(format(), field.name(), nFaces); + } + else + { + const uint64_t payLoad = + vtk::sizeofData<float, nCmpt>(nFaces); + + format().beginDataArray<float, nCmpt>(field.name()); + format().writeSize(payLoad); + } } - format().writeSize(payLoad); - for (const label patchId : patchIDs_) + if (parallel_ ? Pstream::master() : true) + { + for (const label patchId : patchIDs_) + { + const auto& pfld = field.boundaryField()[patchId]; + + if (useNearCellValue_) + { + vtk::writeList(format(), pfld.patchInternalField()()); + } + else + { + vtk::writeList(format(), pfld); + } + } + } + + if (parallel_) { - const auto& pfld = field.boundaryField()[patchId]; + // Patch Ids are identical across all processes + const label nPatches = patchIDs_.size(); - vtk::writeList(format(), pfld.patchInternalField()()); + if (Pstream::master()) + { + Field<Type> recv; + + // Receive each patch field and write + for + ( + int slave=Pstream::firstSlave(); + slave<=Pstream::lastSlave(); + ++slave + ) + { + IPstream fromSlave(Pstream::commsTypes::blocking, slave); + + for (label i=0; i < nPatches; ++i) + { + fromSlave >> recv; + + vtk::writeList(format(), recv); + } + } + } + else + { + // Send each patch field to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + for (const label patchId : patchIDs_) + { + const auto& pfld = field.boundaryField()[patchId]; + + if (useNearCellValue_) + { + toMaster << pfld.patchInternalField()(); + } + else + { + toMaster << static_cast<const Field<Type>&>(pfld); + } + } + } } - format().flush(); - if (!legacy_) + if (format_) { + format().flush(); format().endDataArray(); } } @@ -112,76 +266,141 @@ void Foam::vtk::patchWriter::write template<class Type> void Foam::vtk::patchWriter::write ( - const PrimitivePatchInterpolation<primitivePatch>& pInter, - const GeometricField<Type, fvPatchField, volMesh>& field + const GeometricField<Type, fvPatchField, volMesh>& field, + const PrimitivePatchInterpolation<primitivePatch>& pInter ) { - const int nCmpt(pTraits<Type>::nComponents); - const uint64_t payLoad(nPoints_ * nCmpt * sizeof(float)); - - if (legacy_) + if (isState(outputState::POINT_DATA)) { - legacy::floatField<nCmpt>(format(), field.name(), nPoints_); + ++nPointData_; } else { - format().openDataArray<float, nCmpt>(field.name()) - .closeTag(); + FatalErrorInFunction + << "Bad writer state (" << stateNames[state_] + << ") - should be (" << stateNames[outputState::POINT_DATA] + << ") for field " << field.name() << nl << endl + << exit(FatalError); } - format().writeSize(payLoad); + const direction nCmpt(pTraits<Type>::nComponents); + + label nPoints = nLocalPoints_; - for (const label patchId : patchIDs_) + if (parallel_) { - const auto& pfld = field.boundaryField()[patchId]; + reduce(nPoints, sumOp<label>()); + } - if (nearCellValue_) - { - auto tfield = - pInter.faceToPointInterpolate(pfld.patchInternalField()()); - vtk::writeList(format(), tfield()); + if (format_) + { + if (legacy()) + { + legacy::floatField<nCmpt>(format(), field.name(), nPoints); } else { - auto tfield = pInter.faceToPointInterpolate(pfld); + const uint64_t payLoad = + vtk::sizeofData<float, nCmpt>(nPoints); - vtk::writeList(format(), tfield()); + format().beginDataArray<float, nCmpt>(field.name()); + format().writeSize(payLoad); } } - format().flush(); - if (!legacy_) + if (parallel_ ? Pstream::master() : true) { - format().endDataArray(); + for (const label patchId : patchIDs_) + { + const auto& pfld = field.boundaryField()[patchId]; + + if (useNearCellValue_) + { + auto tfield = + pInter.faceToPointInterpolate + ( + pfld.patchInternalField()() + ); + + vtk::writeList(format(), tfield()); + } + else + { + auto tfield = pInter.faceToPointInterpolate(pfld); + + vtk::writeList(format(), tfield()); + } + } } -} -template<class Type, template<class> class PatchField, class GeoMesh> -void Foam::vtk::patchWriter::write -( - const UPtrList<const GeometricField<Type, PatchField, GeoMesh>>& flds -) -{ - for (const auto& field : flds) + if (parallel_) { - write(field); + // Patch Ids are identical across all processes + const label nPatches = patchIDs_.size(); + + if (Pstream::master()) + { + Field<Type> recv; + + // Receive each patch field and write + for + ( + int slave=Pstream::firstSlave(); + slave<=Pstream::lastSlave(); + ++slave + ) + { + IPstream fromSlave(Pstream::commsTypes::blocking, slave); + + for (label i=0; i < nPatches; ++i) + { + fromSlave >> recv; + + vtk::writeList(format(), recv); + } + } + } + else + { + // Send each patch field to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + for (const label patchId : patchIDs_) + { + const auto& pfld = field.boundaryField()[patchId]; + + if (useNearCellValue_) + { + auto tfield = + pInter.faceToPointInterpolate + ( + pfld.patchInternalField()() + ); + + toMaster << tfield(); + } + else + { + auto tfield = pInter.faceToPointInterpolate(pfld); + + toMaster << tfield(); + } + } + } } -} -template<class Type> -void Foam::vtk::patchWriter::write -( - const PrimitivePatchInterpolation<primitivePatch>& pInter, - const UPtrList<const GeometricField<Type, fvPatchField, volMesh>>& flds -) -{ - for (const auto& field : flds) + if (format_) { - write(pInter, field); + format().flush(); + format().endDataArray(); } } -- GitLab