From 9d8af49c1661af00c280a1a9b7f036ccefed1c4d Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Tue, 16 Oct 2018 13:50:13 +0200 Subject: [PATCH] ENH: parallel and xml output for surfaces (issue #926) --- src/meshTools/Make/files | 1 + src/meshTools/output/foamVtkSurfaceWriter.C | 477 ++++++++++++++++++ src/meshTools/output/foamVtkSurfaceWriter.H | 239 +++++++++ .../output/foamVtkSurfaceWriterTemplates.C | 130 +++++ 4 files changed, 847 insertions(+) create mode 100644 src/meshTools/output/foamVtkSurfaceWriter.C create mode 100644 src/meshTools/output/foamVtkSurfaceWriter.H create mode 100644 src/meshTools/output/foamVtkSurfaceWriterTemplates.C diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files index e89cf987fcd..4c1116e0b60 100644 --- a/src/meshTools/Make/files +++ b/src/meshTools/Make/files @@ -298,6 +298,7 @@ meshStructure/topoDistanceData.C meshStructure/pointTopoDistanceData.C output/foamVtkIndPatchWriter.C +output/foamVtkSurfaceWriter.C output/foamVtkWriteTopoSet.C output/foamVtkWriteFaceSet.C output/foamVtkWritePointSet.C diff --git a/src/meshTools/output/foamVtkSurfaceWriter.C b/src/meshTools/output/foamVtkSurfaceWriter.C new file mode 100644 index 00000000000..7bf887d232f --- /dev/null +++ b/src/meshTools/output/foamVtkSurfaceWriter.C @@ -0,0 +1,477 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2018 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +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 "foamVtkSurfaceWriter.H" +#include "foamVtkOutput.H" +#include "globalIndex.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::vtk::surfaceWriter::beginPiece() +{ + // Basic sizes + nLocalPoints_ = points_.size(); + nLocalFaces_ = faces_.size(); + nLocalVerts_ = 0; + + for (const face& f : faces_) + { + 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().tag + ( + vtk::fileTag::PIECE, + vtk::fileAttr::NUMBER_OF_POINTS, numberOfPoints_, + vtk::fileAttr::NUMBER_OF_POLYS, numberOfCells_ + ); + } +} + + +void Foam::vtk::surfaceWriter::writePoints() +{ + if (format_) + { + 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); + } + } + + + if (parallel_ ? Pstream::master() : true) + { + { + vtk::writeList(format(), points_); + } + } + + + if (parallel_) + { + 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); + + { + fromSlave >> recv; + + vtk::writeList(format(), recv); + } + } + } + else + { + // Send to master + OPstream toMaster + ( + Pstream::commsTypes::blocking, + Pstream::masterNo() + ); + + { + toMaster << points_; + } + } + } + + + if (format_) + { + format().flush(); + format().endDataArray(); + + if (!legacy()) + { + format() + .endTag(vtk::fileTag::POINTS); + } + } +} + + +void Foam::vtk::surfaceWriter::writePolysLegacy +( + const globalIndex& pointOffsets +) +{ + // Connectivity count without additional storage (done internally) + + label nFaces = nLocalFaces_; + label nVerts = nLocalVerts_; + + if (parallel_) + { + reduce(nFaces, sumOp<label>()); + reduce(nVerts, sumOp<label>()); + } + + if (nFaces != numberOfCells_) + { + FatalErrorInFunction + << "Expecting " << numberOfCells_ + << " faces, but found " << nFaces + << exit(FatalError); + } + + legacy::beginPolys(os_, nFaces, nVerts); + + labelList vertLabels(nLocalFaces_ + nLocalVerts_); + + { + // Legacy: size + connectivity together + // [nPts, id1, id2, ..., nPts, id1, id2, ...] + + auto iter = vertLabels.begin(); + + label off = pointOffsets.localStart(); + + { + for (const face& f : faces_) + { + *iter = f.size(); // The size prefix + ++iter; + + for (const label pfi : f) + { + *iter = pfi + off; // Face vertex label + ++iter; + } + } + off += points_.size(); + } + } + + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertLabels); + } + else + { + vtk::writeList(format(), vertLabels); + } + + if (format_) + { + format().flush(); + } +} + + +void Foam::vtk::surfaceWriter::writePolys +( + const globalIndex& pointOffsets +) +{ + if (format_) + { + format().tag(vtk::fileTag::POLYS); + } + + // + // 'connectivity' + // + { + labelList vertLabels(nLocalVerts_); + + label nVerts = nLocalVerts_; + + if (parallel_) + { + reduce(nVerts, sumOp<label>()); + } + + if (format_) + { + const uint64_t payLoad = vtk::sizeofData<label>(nVerts); + + format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY); + format().writeSize(payLoad * sizeof(label)); + } + + { + // XML: connectivity only + // [id1, id2, ..., id1, id2, ...] + + auto iter = vertLabels.begin(); + + label off = pointOffsets.localStart(); + + { + for (const face& f : faces_) + { + for (const label pfi : f) + { + *iter = pfi + off; // Face vertex label + ++iter; + } + } + off += points_.size(); + } + } + + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertLabels); + } + else + { + vtk::writeList(format(), vertLabels); + } + + if (format_) + { + format().flush(); + format().endDataArray(); + } + } + + + // + // 'offsets' (connectivity offsets) + // + { + 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(); + + auto iter = vertOffsets.begin(); + + { + for (const face& f : faces_) + { + off += f.size(); // End offset + *iter = off; + ++iter; + } + } + + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertOffsets); + } + else + { + vtk::writeList(format_.ref(), vertOffsets); + } + + + if (format_) + { + format().flush(); + format().endDataArray(); + } + } + + if (format_) + { + format().endTag(vtk::fileTag::POLYS); + } +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::vtk::surfaceWriter::surfaceWriter +( + const pointField& points, + const faceList& faces, + const vtk::outputOptions opts +) +: + vtk::fileWriter(vtk::fileTag::POLY_DATA, opts), + points_(points), + faces_(faces), + numberOfPoints_(0), + numberOfCells_(0), + nLocalPoints_(0), + nLocalFaces_(0), + nLocalVerts_(0), + instant_() +{ + // We do not currently support append mode + opts_.append(false); +} + + +Foam::vtk::surfaceWriter::surfaceWriter +( + const pointField& points, + const faceList& faces, + const fileName& file, + bool parallel +) +: + surfaceWriter(points, faces) +{ + open(file, parallel); +} + + +Foam::vtk::surfaceWriter::surfaceWriter +( + const pointField& points, + const faceList& faces, + const vtk::outputOptions opts, + const fileName& file, + bool parallel +) +: + surfaceWriter(points, faces, opts) +{ + open(file, parallel); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::vtk::surfaceWriter::setTime(const instant& inst) +{ + instant_ = inst; +} + + +bool Foam::vtk::surfaceWriter::beginFile(std::string title) +{ + if (title.size()) + { + return vtk::fileWriter::beginFile(title); + } + + if (instant_.name().size()) + { + return vtk::fileWriter::beginFile + ( + "time='" + instant_.name() + "'" + ); + } + + // Provide default title + return vtk::fileWriter::beginFile("surface"); +} + + +bool Foam::vtk::surfaceWriter::writeGeometry() +{ + enter_Piece(); + + beginPiece(); + + writePoints(); + + const globalIndex globalPointOffset(nLocalPoints_); + + if (legacy()) + { + writePolysLegacy(globalPointOffset); + } + else + { + writePolys(globalPointOffset); + } + + return true; +} + + +bool Foam::vtk::surfaceWriter::beginCellData(label nFields) +{ + return enter_CellData(numberOfCells_, nFields); +} + + +bool Foam::vtk::surfaceWriter::beginPointData(label nFields) +{ + return enter_PointData(numberOfPoints_, nFields); +} + + +void Foam::vtk::surfaceWriter::writeTimeValue() +{ + if (instant_.name().size()) + { + vtk::fileWriter::writeTimeValue(instant_.value()); + } +} + + +// ************************************************************************* // diff --git a/src/meshTools/output/foamVtkSurfaceWriter.H b/src/meshTools/output/foamVtkSurfaceWriter.H new file mode 100644 index 00000000000..f6c373559c8 --- /dev/null +++ b/src/meshTools/output/foamVtkSurfaceWriter.H @@ -0,0 +1,239 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2018 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +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::vtk::surfaceWriter + +Description + Write faces/points (optionally with fields) + as a vtp file or a legacy vtk file. + + 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 + foamVtkSurfaceWriter.C + foamVtkSurfaceWriterTemplates.C + +\*---------------------------------------------------------------------------*/ + +#ifndef foamVtkSurfaceWriter_H +#define foamVtkSurfaceWriter_H + +#include "foamVtkFileWriter.H" +#include "pointField.H" +#include "faceList.H" +#include "instant.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Forward declarations +class globalIndex; + +namespace vtk +{ + +/*---------------------------------------------------------------------------*\ + Class vtk::surfaceWriter Declaration +\*---------------------------------------------------------------------------*/ + +class surfaceWriter +: + public vtk::fileWriter +{ + // Private Member Data + + //- Reference to the points + const pointField& points_; + + //- Reference to the faces + const faceList& faces_; + + //- The numer of field points for the current Piece + label numberOfPoints_; + + //- The numer of field cells (faces) for the current Piece + label numberOfCells_; + + //- Local number of points + label nLocalPoints_; + + //- Local number of faces + label nLocalFaces_; + + //- Local face vertices (connectivity) count. Sum of face sizes. + label nLocalVerts_; + + //- Time name/value + instant instant_; + + + // Private Member Functions + + //- Determing sizes (nLocalPoints_, nLocalFaces_, nLocalVerts_), + //- and begin piece + void beginPiece(); + + //- Write patch points + void writePoints(); + + //- Write patch faces, legacy format + void writePolysLegacy(const globalIndex& pointOffsets); + + //- Write patch faces + void writePolys(const globalIndex& pointOffsets); + + + //- No copy construct + surfaceWriter(const surfaceWriter&) = delete; + + //- No copy assignment + void operator=(const surfaceWriter&) = delete; + + +public: + + // Constructors + + //- Construct from components (default format INLINE_BASE64) + surfaceWriter + ( + const pointField& pts, + const faceList& faces, + const vtk::outputOptions opts = vtk::formatType::INLINE_BASE64 + ); + + //- Construct from components (default format INLINE_BASE64), + //- and open the file for writing. + // The file name is with/without an extension. + surfaceWriter + ( + const pointField& pts, + const faceList& faces, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + //- Construct from components and open the file for writing. + // The file name is with/without an extension. + surfaceWriter + ( + const pointField& pts, + const faceList& faces, + const vtk::outputOptions opts, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + + //- Destructor + virtual ~surfaceWriter() = default; + + + // Member Functions + + //- File extension for current format type. + using vtk::fileWriter::ext; + + //- File extension for given output type + inline static word ext(vtk::outputOptions opts) + { + return opts.ext(vtk::fileTag::POLY_DATA); + } + + //- Define a time name/value for the output + virtual void setTime(const instant& inst); + + //- 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 "TimeValue" FieldData (name as per Catalyst output) + // Must be called within the FIELD_DATA state. + // \note As a convenience this can also be called from + // (OPENED | DECLARED) states, in which case it invokes + // beginFieldData(1) internally. + using vtk::fileWriter::writeTimeValue; + + //- Write the currently set time as "TimeValue" FieldData + void writeTimeValue(); + + + // Write + + //- Write a list of Cell (Face) or Point values + template<class Type> + void write(const word& fieldName, const UList<Type>& field); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace vtk +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#ifdef NoRepository + #include "foamVtkSurfaceWriterTemplates.C" +#endif + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/meshTools/output/foamVtkSurfaceWriterTemplates.C b/src/meshTools/output/foamVtkSurfaceWriterTemplates.C new file mode 100644 index 00000000000..ace78f733f1 --- /dev/null +++ b/src/meshTools/output/foamVtkSurfaceWriterTemplates.C @@ -0,0 +1,130 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2018 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +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/>. + +\*---------------------------------------------------------------------------*/ + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +template<class Type> +void Foam::vtk::surfaceWriter::write +( + const word& fieldName, + const UList<Type>& field +) +{ + if (isState(outputState::CELL_DATA)) + { + ++nCellData_; + } + else if (isState(outputState::POINT_DATA)) + { + ++nPointData_; + } + else + { + FatalErrorInFunction + << "Bad writer state (" << stateNames[state_] + << ") - should be (" << stateNames[outputState::CELL_DATA] + << ") or (" << stateNames[outputState::POINT_DATA] + << ") for field " << fieldName << nl << endl + << exit(FatalError); + } + + static_assert + ( + ( + std::is_same<typename pTraits<Type>::cmptType,label>::value + || std::is_floating_point<typename pTraits<Type>::cmptType>::value + ), + "Label and Floating-point vector space only" + ); + + const bool isLabel = + std::is_same<typename pTraits<Type>::cmptType(), label>::value; + + + const direction nCmpt(pTraits<Type>::nComponents); + + label nValues = field.size(); + + // Could check sizes: + // nValues == nLocalFaces (CELL_DATA) + // nValues == nLocalPoints (POINT_DATA) + + if (parallel_) + { + reduce(nValues, sumOp<label>()); + } + + if (format_) + { + if (isLabel) + { + if (legacy()) + { + legacy::intField<nCmpt>(format(), fieldName, nValues); + } + else + { + const uint64_t payLoad = vtk::sizeofData<label, nCmpt>(nValues); + + format().beginDataArray<label, nCmpt>(fieldName); + format().writeSize(payLoad); + } + } + else + { + if (legacy()) + { + legacy::floatField<nCmpt>(format(), fieldName, nValues); + } + else + { + const uint64_t payLoad = vtk::sizeofData<float, nCmpt>(nValues); + + format().beginDataArray<float, nCmpt>(fieldName); + format().writeSize(payLoad); + } + } + } + + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), field); + } + else + { + vtk::writeList(format(), field); + } + + + if (format_) + { + format().flush(); + format().endDataArray(); + } +} + + +// ************************************************************************* // -- GitLab