diff --git a/applications/test/foamCellZoneToVTK/Make/files b/applications/test/foamCellZoneToVTK/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..a4be1f0e87117526b908dddf157a154f09296645 --- /dev/null +++ b/applications/test/foamCellZoneToVTK/Make/files @@ -0,0 +1,3 @@ +foamCellZoneToVTK.C + +EXE = $(FOAM_USER_APPBIN)/foamCellZoneToVTK diff --git a/applications/test/foamCellZoneToVTK/Make/options b/applications/test/foamCellZoneToVTK/Make/options new file mode 100644 index 0000000000000000000000000000000000000000..d820876f443dc7df696fce2681559e9249c223bc --- /dev/null +++ b/applications/test/foamCellZoneToVTK/Make/options @@ -0,0 +1,7 @@ +EXE_INC = \ + -I$(LIB_SRC)/fileFormats/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude + +EXE_LIBS = \ + -lfileFormats \ + -lmeshTools diff --git a/applications/test/foamCellZoneToVTK/foamCellZoneToVTK.C b/applications/test/foamCellZoneToVTK/foamCellZoneToVTK.C new file mode 100644 index 0000000000000000000000000000000000000000..a2f265db03001af8cf62564cd36e3821677a6a43 --- /dev/null +++ b/applications/test/foamCellZoneToVTK/foamCellZoneToVTK.C @@ -0,0 +1,161 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Application + foamCellZoneToVTK.C + +Description + Write tet-decomposed OpenFOAM mesh in VTK format. + For diagnostic purposes. + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "timeSelector.H" +#include "Time.H" +#include "polyMesh.H" +#include "foamVtkInternalMeshWriter.H" + +using namespace Foam; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + argList::addNote + ( + "Write OpenFOAM cellZone mesh to VTK" + ); + argList::addOption + ( + "cellZone", + "name", + "Convert mesh subset corresponding to specified cellZone" + ); + argList::addBoolOption + ( + "list", + "List names of cellZones and exit" + ); + timeSelector::addOptions(); + + #include "setRootCase.H" + + word cellZoneName; + args.readIfPresent("cellZone", cellZoneName); + + const bool optList = args.found("list"); + + if (optList) + { + if (!cellZoneName.empty()) + { + Info<< "specify -list or -cellZone, but not both!" << nl; + return 1; + } + } + else if (cellZoneName.empty()) + { + Info<< "Did not specify a cellZone!!" << nl; + return 1; + } + + #include "createTime.H" + + instantList timeDirs = timeSelector::select0(runTime, args); + + fileName exportName("zonemesh-" + cellZoneName); + + #include "createPolyMesh.H" + + forAll(timeDirs, timei) + { + runTime.setTime(timeDirs[timei], timei); + + polyMesh::readUpdateState state = mesh.readUpdate(); + + if (!timei || state != polyMesh::UNCHANGED) + { + fileName meshName(exportName); + if (state != polyMesh::UNCHANGED) + { + meshName += '_' + runTime.timeName(); + } + + if (optList) + { + Info<< "cellZones:" << nl; + for (const word& name : mesh.cellZones().names()) + { + Info<< " " << name << nl; + } + } + else + { + const cellZone* zonePtr = + mesh.cellZones().cfindZone(cellZoneName); + + Info<< "cellZone " << cellZoneName; + if (!zonePtr) + { + Info<< " ... not found" << nl; + continue; + } + Info<< nl; + + const cellZone& zn = *zonePtr; + + // Define a subset + vtk::vtuCells vtuCells; + vtuCells.reset(mesh, zn); + + vtk::internalMeshWriter writer + ( + mesh, + vtuCells, + fileName + ( + mesh.time().globalPath() / meshName + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + Info<< "Wrote " << writer.output().name() << nl; + } + } + } + + Info<< "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/applications/test/foamMeshToTet-vtk/Make/files b/applications/test/foamMeshToTet-vtk/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..576d08e8c11d9d6d7396a0ee9ac2a9213f159ac0 --- /dev/null +++ b/applications/test/foamMeshToTet-vtk/Make/files @@ -0,0 +1,4 @@ +foamMeshToTet-vtk.C +writeVTKtetMesh.C + +EXE = $(FOAM_USER_APPBIN)/foamMeshToTet-vtk diff --git a/applications/test/foamMeshToTet-vtk/Make/options b/applications/test/foamMeshToTet-vtk/Make/options new file mode 100644 index 0000000000000000000000000000000000000000..d820876f443dc7df696fce2681559e9249c223bc --- /dev/null +++ b/applications/test/foamMeshToTet-vtk/Make/options @@ -0,0 +1,7 @@ +EXE_INC = \ + -I$(LIB_SRC)/fileFormats/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude + +EXE_LIBS = \ + -lfileFormats \ + -lmeshTools diff --git a/applications/test/foamMeshToTet-vtk/foamMeshToTet-vtk.C b/applications/test/foamMeshToTet-vtk/foamMeshToTet-vtk.C new file mode 100644 index 0000000000000000000000000000000000000000..d1d078949e7ced50204708b6c83bb94936d2dc2b --- /dev/null +++ b/applications/test/foamMeshToTet-vtk/foamMeshToTet-vtk.C @@ -0,0 +1,96 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Application + foamMeshToTet-vtk + +Description + Write tet-decomposed OpenFOAM mesh in VTK format. + For diagnostic purposes. + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "timeSelector.H" +#include "Time.H" +#include "polyMesh.H" + +namespace Foam +{ + void writeVTKtetMesh(const fileName& output, const polyMesh& mesh); +} + +using namespace Foam; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + argList::addNote + ( + "Write tet-decomposed OpenFOAM mesh in VTK" + ); + argList::noParallel(); + timeSelector::addOptions(); + + #include "setRootCase.H" + #include "createTime.H" + + instantList timeDirs = timeSelector::select0(runTime, args); + + fileName exportName = "tetmesh"; + if (args.found("case")) + { + exportName += '-' + args.globalCaseName(); + } + + #include "createPolyMesh.H" + + forAll(timeDirs, timei) + { + runTime.setTime(timeDirs[timei], timei); + + polyMesh::readUpdateState state = mesh.readUpdate(); + + if (!timei || state != polyMesh::UNCHANGED) + { + fileName meshName(exportName); + if (state != polyMesh::UNCHANGED) + { + meshName += '_' + runTime.timeName(); + } + + writeVTKtetMesh(meshName, mesh); + } + } + + Info<< "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/applications/test/foamMeshToTet-vtk/writeVTKtetMesh.C b/applications/test/foamMeshToTet-vtk/writeVTKtetMesh.C new file mode 100644 index 0000000000000000000000000000000000000000..ea7157fac0df70a94724d595d06083d3eccc5c3d --- /dev/null +++ b/applications/test/foamMeshToTet-vtk/writeVTKtetMesh.C @@ -0,0 +1,204 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + +#include "polyMesh.H" +#include "Fstream.H" +#include "tetMatcher.H" +#include "foamVtkInternalMeshWriter.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +void writeVTKtetMesh(const fileName& output, const polyMesh& mesh_) +{ + #if WM_LABEL_SIZE == 64 + # define FOAM_VTK_LABEL_NAME "vtktypeint64" + #else + # define FOAM_VTK_LABEL_NAME "int" + #endif + + const faceList& faces = mesh_.faces(); + const labelList& faceOwner = mesh_.faceOwner(); + + label nTets = 0; + + for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei) + { + nTets += 2 * faces[facei].nTriangles(); + } + for (label facei = mesh_.nInternalFaces(); facei < mesh_.nFaces(); ++facei) + { + nTets += faces[facei].nTriangles(); + } + const label nPoints = (mesh_.nPoints() + mesh_.nCells()); + + OFstream os(output + ".vtk"); + + Info<< "Write: " << os.name() << nl; + + os << "# vtk DataFile Version 5.1" << nl + << "tet-mesh" << nl + << "ASCII" << nl + << "DATASET UNSTRUCTURED_GRID" << nl + << nl; + + os << "POINTS " << nPoints << " float" << nl; + for (const point& p : mesh_.points()) + { + os << float(p.x()) << ' ' + << float(p.y()) << ' ' + << float(p.z()) << nl; + } + for (const point& p : mesh_.cellCentres()) + { + os << float(p.x()) << ' ' + << float(p.y()) << ' ' + << float(p.z()) << nl; + } + os << nl; + + os << "CELLS " << (1 + nTets) << ' ' << (4 * nTets) << nl; + + os << "OFFSETS " << FOAM_VTK_LABEL_NAME << nl + << 0; // begin offset = 0 + { + label offset = 0; + for (label teti = 0; teti < nTets; ++teti) + { + offset += 4; + os << ' ' << offset; + } + os << nl << nl; + } + + labelList nLocalTets(mesh_.nCells(), Zero); + + os << nl + << "CONNECTIVITY " << FOAM_VTK_LABEL_NAME << nl; + + for (label celli = 0; celli < mesh_.nCells(); ++celli) + { + const cell& cFaces = mesh_.cells()[celli]; + + if (tetMatcher::test(mesh_, celli)) + { + // Tet: no cell-centre decomposition + + const label facei = cFaces[0]; + const face& f0 = faces[facei]; + + // Get the other point from f1. Tbd: check if not duplicate face + // (ACMI / ignoreBoundaryFaces_). + const face& f1 = faces[cFaces[1]]; + label apexi = -1; + forAll(f1, fp) + { + apexi = f1[fp]; + if (!f0.found(apexi)) + { + break; + } + } + + const label p0 = f0[0]; + label p1 = f0[1]; + label p2 = f0[2]; + + if (faceOwner[facei] == celli) + { + std::swap(p1, p2); + } + + ++nLocalTets[celli]; + os << p0 << ' ' << p1 << ' ' << p2 << ' ' << apexi << nl; + } + else + { + for (const label facei : cFaces) + { + const face& f = faces[facei]; + + label fp0 = mesh_.tetBasePtIs()[facei]; + + // Fallback + if (fp0 < 0) + { + fp0 = 0; + } + + const label p0 = f[fp0]; + label fp = f.fcIndex(fp0); + for (label i = 2; i < f.size(); ++i) + { + label p1 = f[fp]; + fp = f.fcIndex(fp); + label p2 = f[fp]; + + if (faceOwner[facei] == celli) + { + std::swap(p1, p2); + } + + ++nLocalTets[celli]; + os << p0 << ' ' << p1 << ' ' << p2 << ' ' + << (mesh_.nPoints() + celli) << nl; + } + } + } + } + + os << nl + << "CELL_TYPES " << nTets << nl; + + for (label teti = 0; teti < nTets; ++teti) + { + if (teti) os << ' '; + os << vtk::cellType::VTK_TETRA; + } + os << nl; + + os << nl << "CELL_DATA " << nTets << nl + << "FIELD FieldData " << 1 << nl; + + os << "cellID " << 1 << ' ' << nTets << " int" << nl; + forAll(nLocalTets, celli) + { + label n = nLocalTets[celli]; + while (n--) + { + os << ' ' << celli; + } + os << nl; + } +} + +} // End namespace Foam + + +// ************************************************************************* // diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H index c98ba864565ee06686d1bf0464af2b63ac6b2499..add182f7ecb52cd1e7e5f5efa7aee1ff4d2ecf63 100644 --- a/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H +++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/convertVolumeFields.H @@ -264,7 +264,7 @@ Description // Begin CellData if (internalWriter) { - // Optionally with cellID and procID fields + // Optionally with (cellID, procID) fields internalWriter->beginCellData ( (withMeshIds ? 1 + (internalWriter->parallel() ? 1 : 0) : 0) @@ -278,20 +278,21 @@ Description } } - if (nVolFields) + if (nVolFields || withMeshIds) { for (vtk::patchWriter& writer : patchWriters) { - // Optionally with patchID field + // Optionally with (patchID, procID) fields writer.beginCellData ( - (withMeshIds ? 1 : 0) + (withMeshIds ? 2 : 0) + nVolFields ); if (withMeshIds) { writer.writePatchIDs(); + writer.writeProcIDs(); } } } diff --git a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C index 9dc41948e464dc4554445e4effd93cc33d1d2dd0..0219a3b835c49e3dccd8c0f5fcd4b765aada333b 100644 --- a/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C +++ b/src/conversion/vtk/output/foamVtkPatchWriterTemplates.C @@ -149,7 +149,7 @@ void Foam::vtk::patchWriter::write << exit(FatalError); } - label nFaces = nLocalFaces_; + label nFaces = nLocalPolys_; if (parallel_) { diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files index 2686cefc430ea733feeaf2dbad5fa790cb82ce5f..f7fd57b8538c9aec0c911c605476ddcf1b4f46fe 100644 --- a/src/fileFormats/Make/files +++ b/src/fileFormats/Make/files @@ -59,6 +59,7 @@ vtk/part/foamVtuCells.C vtk/part/foamVtuSizing.C vtk/read/vtkUnstructuredReader.C +vtk/write/foamVtkLineWriter.C vtk/write/foamVtkPolyWriter.C vtk/write/foamVtkSurfaceWriter.C diff --git a/src/fileFormats/vtk/base/foamVtkCore.C b/src/fileFormats/vtk/base/foamVtkCore.C index 14f832fec6e404c22783bd86b065839310fb2f45..b9488e8824a12c9900dfb52f853293e02a008b3e 100644 --- a/src/fileFormats/vtk/base/foamVtkCore.C +++ b/src/fileFormats/vtk/base/foamVtkCore.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2018 OpenCFD Ltd. + Copyright (C) 2017-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -113,6 +113,8 @@ Foam::vtk::dataArrayAttrNames }); +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + // Legacy const Foam::word Foam::vtk::legacy::fileExtension("vtk"); @@ -132,11 +134,27 @@ const Foam::Enum < Foam::vtk::fileTag > -Foam::vtk::legacy::dataTypeNames +Foam::vtk::legacy::fileTagNames ({ - { vtk::fileTag::CELL_DATA, "CELL_DATA" }, + { vtk::fileTag::POINTS, "POINTS" }, + { vtk::fileTag::CELLS, "CELLS" }, + { vtk::fileTag::POLYS, "POLYGONS" }, + { vtk::fileTag::VERTS, "VERTICES" }, + { vtk::fileTag::LINES, "LINES" }, + { vtk::fileTag::CELL_DATA, "CELL_DATA" }, { vtk::fileTag::POINT_DATA, "POINT_DATA" }, }); +const Foam::Enum +< + Foam::vtk::dataArrayAttr +> +Foam::vtk::legacy::dataArrayAttrNames +({ + { vtk::dataArrayAttr::OFFSETS, "OFFSETS" }, + { vtk::dataArrayAttr::CONNECTIVITY, "CONNECTIVITY" }, +}); + + // ************************************************************************* // diff --git a/src/fileFormats/vtk/base/foamVtkCore.H b/src/fileFormats/vtk/base/foamVtkCore.H index 2524964a0af013697a45bac3f4c7d2f5da744349..08615850ecbcf8b00368a20fef27c851228bd2db 100644 --- a/src/fileFormats/vtk/base/foamVtkCore.H +++ b/src/fileFormats/vtk/base/foamVtkCore.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2018 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -183,8 +183,12 @@ namespace legacy //- Legacy content names (POLYDATA, UNSTRUCTURED_GRID) extern const Foam::Enum<vtk::fileTag> contentNames; - //- Legacy data type names (CELL_DATA, POINT_DATA) - extern const Foam::Enum<vtk::fileTag> dataTypeNames; + //- Legacy file tags (eg, LINES, CELL_DATA, POINT_DATA, ...) + extern const Foam::Enum<vtk::fileTag> fileTagNames; + + //- Legacy attributes (eg, OFFSETS) + extern const Foam::Enum<dataArrayAttr> dataArrayAttrNames; + } // End namespace legacy diff --git a/src/fileFormats/vtk/file/foamVtkFileWriter.C b/src/fileFormats/vtk/file/foamVtkFileWriter.C index aff45b901776f2b7aae9541ca12d947b4f7c409a..3cc8b7e72a529ed650fe8ef0446bcfa6c26cb1c8 100644 --- a/src/fileFormats/vtk/file/foamVtkFileWriter.C +++ b/src/fileFormats/vtk/file/foamVtkFileWriter.C @@ -26,6 +26,7 @@ License \*---------------------------------------------------------------------------*/ #include "foamVtkFileWriter.H" +#include "globalIndex.H" #include "OSspecific.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -342,7 +343,7 @@ bool Foam::vtk::fileWriter::open(const fileName& file, bool parallel) // Open a file and attach a formatter // - on master (always) - // - on slave if not parallel + // - on subproc (if not parallel) // // This means we can always check if format_ is defined to know if output // is desired on any particular process. @@ -515,7 +516,7 @@ void Foam::vtk::fileWriter::writeTimeValue(scalar timeValue) << exit(FatalError); } - // No collectives - can skip on slave processors + // No collectives - can skip on sub-procs if (!format_) return; if (legacy()) @@ -529,4 +530,66 @@ void Foam::vtk::fileWriter::writeTimeValue(scalar timeValue) } +bool Foam::vtk::fileWriter::writeProcIDs(const label nValues) +{ + // Write procIDs whenever running in parallel + + if (!Pstream::parRun()) + { + return false; // Non-parallel: skip + } + + if (isState(outputState::CELL_DATA)) + { + ++nCellData_; + } + else + { + reportBadState(FatalErrorInFunction, outputState::CELL_DATA) + << " for procID field" << nl << endl + << exit(FatalError); + + return false; + } + + + const globalIndex procSizes + ( + parallel_ + ? globalIndex(nValues) + : globalIndex() + ); + + const label totalCount = (parallel_ ? procSizes.size() : nValues); + + this->beginDataArray<label>("procID", totalCount); + + bool good = false; + + if (parallel_) + { + if (Pstream::master()) + { + // Per-processor ids + for (const int proci : Pstream::allProcs()) + { + vtk::write(format(), label(proci), procSizes.localSize(proci)); + } + good = true; + } + } + else + { + vtk::write(format(), label(Pstream::myProcNo()), totalCount); + good = true; + } + + + this->endDataArray(); + + // MPI barrier + return parallel_ ? returnReduce(good, orOp<bool>()) : good; +} + + // ************************************************************************* // diff --git a/src/fileFormats/vtk/file/foamVtkFileWriter.H b/src/fileFormats/vtk/file/foamVtkFileWriter.H index 19a1d4e1500b5c7c067c24956695c5657652e727..ea25b303232e9b08928a9258f279f28400a2c754 100644 --- a/src/fileFormats/vtk/file/foamVtkFileWriter.H +++ b/src/fileFormats/vtk/file/foamVtkFileWriter.H @@ -107,10 +107,10 @@ protected: //- The output file name fileName outputFile_; - //- The VTK formatter in use (master process) + //- The VTK formatter in use (only valid on master process) autoPtr<vtk::formatter> format_; - //- The backend ostream in use (master process) + //- The backend ostream in use (only opened on master process) std::ofstream os_; @@ -123,16 +123,16 @@ protected: Ostream& reportBadState(Ostream&, outputState, outputState) const; //- The backend ostream in use - inline std::ofstream& os(); + inline std::ofstream& os() noexcept; //- The VTK formatter in use inline vtk::formatter& format(); - //- True if the output state corresponds to the test state. - inline bool isState(outputState test) const; + //- True if output state corresponds to the test state. + inline bool isState(outputState test) const noexcept; - //- True if the output state does not correspond to the test state. - inline bool notState(outputState test) const; + //- True if output state does not correspond to the test state. + inline bool notState(outputState test) const noexcept; //- Start of a field or DataArray output (legacy or non-legacy). template<class Type> @@ -151,26 +151,6 @@ protected: //- End of a POINTS DataArray void endPoints(); - - //- Write uniform field content. - // No context checking (eg, file-open, CellData, PointData, etc) - template<class Type> - void writeUniform - ( - const word& fieldName, - const Type& val, - const label nValues - ); - - //- Write basic (primitive) field content - // No context checking (eg, file-open, CellData, PointData, etc) - template<class Type> - void writeBasicField - ( - const word& fieldName, - const UList<Type>& field - ); - //- Trigger change state to Piece. Resets nCellData_, nPointData_. bool enter_Piece(); @@ -194,6 +174,34 @@ protected: bool exit_File(); + // Field writing + + //- Write uniform field content. + // No context checking (eg, file-open, CellData, PointData, etc) + // The value and count can be different on each processor + template<class Type> + void writeUniform + ( + const word& fieldName, + const Type& val, + const label nValues + ); + + //- Write basic (primitive) field content + // No context checking (eg, file-open, CellData, PointData, etc) + template<class Type> + void writeBasicField + ( + const word& fieldName, + const UList<Type>& field + ); + + //- Write nValues of processor ids as CellData (no-op in serial) + bool writeProcIDs(const label nValues); + + + // Other + //- No copy construct fileWriter(const fileWriter&) = delete; diff --git a/src/fileFormats/vtk/file/foamVtkFileWriterI.H b/src/fileFormats/vtk/file/foamVtkFileWriterI.H index 62c7d86600e98f4101c8b83d2d2b89992ec157be..192eac45a758a70614fe1e44b597ad3969a0022e 100644 --- a/src/fileFormats/vtk/file/foamVtkFileWriterI.H +++ b/src/fileFormats/vtk/file/foamVtkFileWriterI.H @@ -27,7 +27,7 @@ License // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // -inline std::ofstream& Foam::vtk::fileWriter::os() +inline std::ofstream& Foam::vtk::fileWriter::os() noexcept { return os_; } @@ -39,13 +39,13 @@ inline Foam::vtk::formatter& Foam::vtk::fileWriter::format() } -inline bool Foam::vtk::fileWriter::isState(outputState test) const +inline bool Foam::vtk::fileWriter::isState(outputState test) const noexcept { return (test == state_); } -inline bool Foam::vtk::fileWriter::notState(outputState test) const +inline bool Foam::vtk::fileWriter::notState(outputState test) const noexcept { return (test != state_); } diff --git a/src/fileFormats/vtk/file/foamVtkFileWriterTemplates.C b/src/fileFormats/vtk/file/foamVtkFileWriterTemplates.C index 79a774fce91e229f2419e73963852443888a25e3..21ec488823fbbc0730c028f5890d1faf90aa39cb 100644 --- a/src/fileFormats/vtk/file/foamVtkFileWriterTemplates.C +++ b/src/fileFormats/vtk/file/foamVtkFileWriterTemplates.C @@ -89,14 +89,25 @@ void Foam::vtk::fileWriter::writeUniform ( const word& fieldName, const Type& val, - const label nValues + const label nLocalValues ) { - this->beginDataArray<Type>(fieldName, nValues); + label nTotal = nLocalValues; - if (format_) + if (parallel_) + { + reduce(nTotal, sumOp<label>()); + } + + this->beginDataArray<Type>(fieldName, nTotal); + + if (parallel_) + { + vtk::writeValueParallel(format_.ref(), val, nLocalValues); + } + else { - vtk::write(format(), val, nValues); + vtk::write(format(), val, nLocalValues); } this->endDataArray(); diff --git a/src/fileFormats/vtk/output/foamVtkOutput.C b/src/fileFormats/vtk/output/foamVtkOutput.C index 7e32b87dcf01275ad8a4c78027c82b011669a478..08a00cf6e40f49917cc31f082ff013e60dec6ba5 100644 --- a/src/fileFormats/vtk/output/foamVtkOutput.C +++ b/src/fileFormats/vtk/output/foamVtkOutput.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -39,7 +39,7 @@ License #include "globalIndex.H" #include "instant.H" #include "Fstream.H" -#include "Pstream.H" +#include "PstreamBuffers.H" #include "OSspecific.H" // * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * * // @@ -99,7 +99,7 @@ void Foam::vtk::writeIdentity label start ) { - // No nComponents for label, so use fmt.write() directly + // No nComponents for label, can use fmt.write() directly for (label i=0; i < len; ++i) { fmt.write(start); @@ -114,7 +114,7 @@ void Foam::vtk::writeList const UList<uint8_t>& values ) { - // No nComponents for char, so use fmt.write() directly + // No nComponents for char, can use fmt.write() directly for (const uint8_t val : values) { fmt.write(val); @@ -125,85 +125,59 @@ void Foam::vtk::writeList void Foam::vtk::writeListParallel ( vtk::formatter& fmt, - const UList<uint8_t>& values + const labelUList& values, + const globalIndex& procOffset ) { - if (Pstream::master()) - { - vtk::writeList(fmt, values); - - List<uint8_t> recv; - - // Receive and write - for (const int slave : Pstream::subProcs()) - { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); + // List sizes + const globalIndex sizes(values.size()); - fromSlave >> recv; + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); - vtk::writeList(fmt, recv); - } - } - else + // Send to master + if (!Pstream::master()) { - // Send to master - OPstream toMaster + UOPstream os(Pstream::masterNo(), pBufs); + os.write ( - Pstream::commsTypes::blocking, - Pstream::masterNo() + reinterpret_cast<const char*>(values.cdata()), + values.size_bytes() ); - - toMaster << values; } -} + pBufs.finishedSends(); -void Foam::vtk::writeListParallel -( - vtk::formatter& fmt, - const labelUList& values, - const globalIndex& procOffset -) -{ if (Pstream::master()) { + // Master data + // Write with offset const label offsetId = procOffset.offset(0); - for (const label val : values) { vtk::write(fmt, val + offsetId); } - labelList recv; - // Receive and write - for (const int slave : Pstream::subProcs()) + for (const int proci : Pstream::subProcs()) { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); + List<label> recv(sizes.localSize(proci)); - fromSlave >> recv; - - const label offsetId = procOffset.offset(slave); + UIPstream is(proci, pBufs); + is.read + ( + reinterpret_cast<char*>(recv.data()), + recv.size_bytes() + ); // Write with offset + const label offsetId = procOffset.offset(proci); for (const label val : recv) { vtk::write(fmt, val + offsetId); } } } - else - { - // Send to master - OPstream toMaster - ( - Pstream::commsTypes::blocking, - Pstream::masterNo() - ); - - toMaster << values; - } } @@ -219,6 +193,9 @@ void Foam::vtk::legacy::fileHeader // Line 1: os << "# vtk DataFile Version 2.0" << nl; + // OR + // os << "# vtk DataFile Version 5.1" << nl; + // Line 2: title const auto truncate = title.find('\n'); diff --git a/src/fileFormats/vtk/output/foamVtkOutput.H b/src/fileFormats/vtk/output/foamVtkOutput.H index ac8c3b68520cc19d5771d647a54d96306f5c0063..ad7d6bf77477c1815d1a25f37eafa15f05176e6c 100644 --- a/src/fileFormats/vtk/output/foamVtkOutput.H +++ b/src/fileFormats/vtk/output/foamVtkOutput.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2019 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -59,7 +59,7 @@ SourceFiles namespace Foam { -// Forward declarations +// Forward Declarations class instant; class globalIndex; @@ -94,14 +94,20 @@ namespace vtk // The output does not include the payload size. void writeList(vtk::formatter& fmt, const UList<uint8_t>& values); - //- Write a list of uint8_t values. - // The output does not include the payload size. - void writeListParallel(vtk::formatter& fmt, const UList<uint8_t>& values); - //- Component-wise write of a value (N times) template<class Type> inline void write(vtk::formatter& fmt, const Type& val, const label n=1); + //- Component-wise write of a value (N times) in parallel + // The value and count may differ on each processor + template<class Type> + inline void writeValueParallel + ( + vtk::formatter& fmt, + const Type& val, + const label count=1 + ); + //- Write a list of values. // The output does not include the payload size. @@ -215,7 +221,7 @@ namespace legacy // Functions - //- Emit header for legacy file. + //- Emit header for legacy file (vtk DataFile Version 2.0) // Writes "ASCII" or "BINARY" depending on specified type. void fileHeader(std::ostream& os, const std::string& title, bool binary); @@ -248,6 +254,12 @@ namespace legacy //- Emit header for POINTS (with trailing newline). inline void beginPoints(std::ostream& os, label nPoints); + //- Emit header for LINES (with trailing newline). + // The nConnectivity is the sum of all connectivity points used, + // but \b without additional space for the size prefixes. + // The additional prefix sizes are added internally. + inline void beginLines(std::ostream& os, label nLines, label nConnectivity); + //- Emit header for POLYGONS (with trailing newline). // The nConnectivity is the sum of all connectivity points used, // but \b without additional space for the size prefixes. diff --git a/src/fileFormats/vtk/output/foamVtkOutputI.H b/src/fileFormats/vtk/output/foamVtkOutputI.H index 036432e3c091590eefb53cb4cb1b266ceceac566..9789eecc41f0e57ea31a1fac3d342bdddf0b19ab 100644 --- a/src/fileFormats/vtk/output/foamVtkOutputI.H +++ b/src/fileFormats/vtk/output/foamVtkOutputI.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2019 OpenCFD Ltd. + Copyright (C) 2017-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -113,7 +113,23 @@ inline void Foam::vtk::legacy::fileHeader inline void Foam::vtk::legacy::beginPoints(std::ostream& os, label nPoints) { os << nl - << "POINTS " << nPoints << " float" << nl; + << legacy::fileTagNames[vtk::fileTag::POINTS] + << ' ' << nPoints + << " float" << nl; +} + + +inline void Foam::vtk::legacy::beginLines +( + std::ostream& os, + label nLines, + label nConnectivity +) +{ + os << nl + << legacy::fileTagNames[vtk::fileTag::LINES] + << ' ' << nLines + << ' ' << (nLines + nConnectivity) << nl; } @@ -125,7 +141,9 @@ inline void Foam::vtk::legacy::beginPolys ) { os << nl - << "POLYGONS " << nPolys << ' ' << (nPolys + nConnectivity) << nl; + << legacy::fileTagNames[vtk::fileTag::POLYS] + << ' ' << nPolys + << ' ' << (nPolys + nConnectivity) << nl; } @@ -159,7 +177,7 @@ inline void Foam::vtk::legacy::beginCellData { fmt.os() << nl - << legacy::dataTypeNames[vtk::fileTag::CELL_DATA] + << legacy::fileTagNames[vtk::fileTag::CELL_DATA] << ' ' << nCells << nl; legacy::fieldData(fmt, nFields); } @@ -174,7 +192,7 @@ inline void Foam::vtk::legacy::beginPointData { fmt.os() << nl - << legacy::dataTypeNames[vtk::fileTag::POINT_DATA] + << legacy::fileTagNames[vtk::fileTag::POINT_DATA] << ' ' << nPoints << nl; legacy::fieldData(fmt, nFields); } diff --git a/src/fileFormats/vtk/output/foamVtkOutputTemplates.C b/src/fileFormats/vtk/output/foamVtkOutputTemplates.C index e296a2b2b56ca5788104aeb9c811f4888d2928fa..59bdb0e88fe227c59f4d5cddc89277217170921b 100644 --- a/src/fileFormats/vtk/output/foamVtkOutputTemplates.C +++ b/src/fileFormats/vtk/output/foamVtkOutputTemplates.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,7 +25,8 @@ License \*---------------------------------------------------------------------------*/ -#include "Pstream.H" +#include "globalIndex.H" +#include "PstreamBuffers.H" #include "ListOps.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -123,38 +124,39 @@ void Foam::vtk::writeLists template<class Type> -void Foam::vtk::writeListParallel +void Foam::vtk::writeValueParallel ( vtk::formatter& fmt, - const UList<Type>& values + const Type& val, + const label count ) { if (Pstream::master()) { - vtk::writeList(fmt, values); + vtk::write(fmt, val, count); - List<Type> recv; + label subCount; + Type subValue; - // Receive and write - for (const int slave : Pstream::subProcs()) + // Receive each [size, value] tuple + for (const int proci : Pstream::subProcs()) { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); - - fromSlave >> recv; + IPstream is(Pstream::commsTypes::blocking, proci); + is >> subCount >> subValue; - vtk::writeList(fmt, recv); + vtk::write(fmt, subValue, subCount); } } else { - // Send to master - OPstream toMaster + OPstream os ( Pstream::commsTypes::blocking, Pstream::masterNo() ); - toMaster << values; + // Send [size, value] tuple + os << count << val; } } @@ -163,36 +165,106 @@ template<class Type> void Foam::vtk::writeListParallel ( vtk::formatter& fmt, - const UList<Type>& values, - const labelUList& addressing + const UList<Type>& values ) { - if (Pstream::master()) + // List sizes + const globalIndex sizes(values.size()); + + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); + + // Send to master + if (!Pstream::master()) { - vtk::writeList(fmt, values, addressing); + UOPstream os(Pstream::masterNo(), pBufs); + if (is_contiguous<Type>::value) + { + os.write + ( + reinterpret_cast<const char*>(values.cdata()), + values.size_bytes() + ); + } + else + { + os << values; + } + } - List<Type> recv; + pBufs.finishedSends(); + + if (Pstream::master()) + { + // Write master data + vtk::writeList(fmt, values); // Receive and write - for (const int slave : Pstream::subProcs()) + for (const int proci : Pstream::subProcs()) { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); + UIPstream is(proci, pBufs); + + { + List<Type> recv(sizes.localSize(proci)); + + if (is_contiguous<Type>::value) + { + is.read + ( + reinterpret_cast<char*>(recv.data()), + recv.size_bytes() + ); + } + else + { + is >> recv; + } + vtk::writeList(fmt, recv); + } + } + } +} - fromSlave >> recv; - vtk::writeList(fmt, recv); - } +template<class Type> +void Foam::vtk::writeListParallel +( + vtk::formatter& fmt, + const UList<Type>& values, + const labelUList& addressing +) +{ + UIndirectList<Type> send(values, addressing); + + // List sizes + const globalIndex sizes(send.size()); + + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); + + // Send to master + if (!Pstream::master()) + { + UOPstream os(Pstream::masterNo(), pBufs); + os << send; } - else + + pBufs.finishedSends(); + + if (Pstream::master()) { - // Send to master - OPstream toMaster - ( - Pstream::commsTypes::blocking, - Pstream::masterNo() - ); + // Write master data + vtk::writeList(fmt, values, addressing); + + // Receive and write + for (const int proci : Pstream::subProcs()) + { + UIPstream is(proci, pBufs); - toMaster << List<Type>(values, addressing); + { + List<Type> recv; + is >> recv; + vtk::writeList(fmt, recv); + } + } } } @@ -205,32 +277,66 @@ void Foam::vtk::writeListParallel const bitSet& selected ) { - if (Pstream::master()) + List<Type> send; + if (!Pstream::master()) { - vtk::writeList(fmt, values, selected); - - List<Type> recv; + send = subset(selected, values); + } - // Receive and write - for (const int slave : Pstream::subProcs()) - { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); + // List sizes. + // NOTE okay to skip proc0 since we only need sizes (not offsets) + const globalIndex sizes(send.size()); - fromSlave >> recv; + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); - vtk::writeList(fmt, recv); + // Send to master + if (!Pstream::master()) + { + UOPstream os(Pstream::masterNo(), pBufs); + if (is_contiguous<Type>::value) + { + os.write + ( + reinterpret_cast<const char*>(send.cdata()), + send.size_bytes() + ); + } + else + { + os << send; } } - else + + pBufs.finishedSends(); + + if (Pstream::master()) { - // Send to master - OPstream toMaster - ( - Pstream::commsTypes::blocking, - Pstream::masterNo() - ); + // Write master data + vtk::writeList(fmt, values, selected); - toMaster << subset(selected, values); + // Receive and write + for (const int proci : Pstream::subProcs()) + { + UIPstream is(proci, pBufs); + + { + List<Type> recv(sizes.localSize(proci)); + + if (is_contiguous<Type>::value) + { + is.read + ( + reinterpret_cast<char*>(recv.data()), + recv.size_bytes() + ); + } + else + { + is >> recv; + } + vtk::writeList(fmt, recv); + } + } } } @@ -243,34 +349,90 @@ void Foam::vtk::writeListsParallel const UList<Type>& values2 ) { - if (Pstream::master()) - { - vtk::writeList(fmt, values1); - vtk::writeList(fmt, values2); + // List sizes + const globalIndex sizes1(values1.size()); + const globalIndex sizes2(values2.size()); - List<Type> recv1, recv2; + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); - // Receive and write - for (const int slave : Pstream::subProcs()) + // Send to master + if (!Pstream::master()) + { + UOPstream os(Pstream::masterNo(), pBufs); + if (is_contiguous<Type>::value) { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); - - fromSlave >> recv1 >> recv2; - - vtk::writeList(fmt, recv1); - vtk::writeList(fmt, recv2); + os.write + ( + reinterpret_cast<const char*>(values1.cdata()), + values1.size_bytes() + ); + os.write + ( + reinterpret_cast<const char*>(values2.cdata()), + values2.size_bytes() + ); + } + else + { + os << values1 << values2; } } - else + + pBufs.finishedSends(); + + if (Pstream::master()) { - // Send to master - OPstream toMaster + // Write master data + vtk::writeList(fmt, values1); + vtk::writeList(fmt, values2); + + // Reserve max receive size + DynamicList<Type> recv ( - Pstream::commsTypes::blocking, - Pstream::masterNo() + max(sizes1.maxNonLocalSize(), sizes2.maxNonLocalSize()) ); - toMaster << values1 << values2; + // Receive and write + for (const int proci : Pstream::subProcs()) + { + UIPstream is(proci, pBufs); + + // values1 + { + List<Type> recv(sizes1.localSize(proci)); + if (is_contiguous<Type>::value) + { + is.read + ( + reinterpret_cast<char*>(recv.data()), + recv.size_bytes() + ); + } + else + { + is >> recv; + } + vtk::writeList(fmt, recv); + } + + // values2 + { + List<Type> recv(sizes2.localSize(proci)); + if (is_contiguous<Type>::value) + { + is.read + ( + reinterpret_cast<char*>(recv.data()), + recv.size_bytes() + ); + } + else + { + is >> recv; + } + vtk::writeList(fmt, recv); + } + } } } @@ -284,35 +446,45 @@ void Foam::vtk::writeListsParallel const labelUList& addressing ) { + UIndirectList<Type> send2(values2, addressing); + + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); + + // Send to master + if (!Pstream::master()) + { + UOPstream os(Pstream::masterNo(), pBufs); + os << values1 << send2; + } + + pBufs.finishedSends(); + if (Pstream::master()) { + // Write master data vtk::writeList(fmt, values1); vtk::writeList(fmt, values2, addressing); - List<Type> recv1, recv2; - // Receive and write - for (const int slave : Pstream::subProcs()) + for (const int proci : Pstream::subProcs()) { - IPstream fromSlave(Pstream::commsTypes::blocking, slave); - - fromSlave >> recv1 >> recv2; - - vtk::writeList(fmt, recv1); - vtk::writeList(fmt, recv2); + UIPstream is(proci, pBufs); + + // values1 + { + List<Type> recv; + is >> recv; + vtk::writeList(fmt, recv); + } + + // values2 (send2) + { + List<Type> recv; + is >> recv; + vtk::writeList(fmt, recv); + } } } - else - { - // Send to master - OPstream toMaster - ( - Pstream::commsTypes::blocking, - Pstream::masterNo() - ); - - toMaster << values1 << List<Type>(values2, addressing); - } } diff --git a/src/fileFormats/vtk/part/foamVtkMeshMaps.H b/src/fileFormats/vtk/part/foamVtkMeshMaps.H index c2c54cc0498606581145ab38c1202f162fa26611..474c2d158bdf9de0d8f254f6b8e55bae04149f01 100644 --- a/src/fileFormats/vtk/part/foamVtkMeshMaps.H +++ b/src/fileFormats/vtk/part/foamVtkMeshMaps.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2020 OpenCFD Ltd. + Copyright (C) 2017-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -75,11 +75,8 @@ public: // Constructors - //- Default construct: zero-sized, no reserved size - inline foamVtkMeshMaps(); - - //- Construct with reserved size - inline explicit foamVtkMeshMaps(const label size); + //- Default construct: zero-sized, no reserved sizes + foamVtkMeshMaps() = default; // Member Functions @@ -87,26 +84,26 @@ public: // Access //- Original cell ids for all cells (regular and decomposed). - // A regular mesh comprising only primitive cell types, this will just + // For regular mesh comprising only primitive cell types, this will // be an identity list. However, for subsetted meshes and decomposed // cells this becomes a useful means of mapping from the original mesh. - inline const labelList& cellMap() const; + inline const labelList& cellMap() const noexcept; //- Write access to original cell ids - inline DynamicList<label>& cellMap(); + inline DynamicList<label>& cellMap() noexcept; //- Point labels for subsetted meshes - inline const labelList& pointMap() const; + inline const labelList& pointMap() const noexcept; //- Write access to point labels for subsetted meshes - inline DynamicList<label>& pointMap(); + inline DynamicList<label>& pointMap() noexcept; //- Any additional (user) labels. // Eg, cell-centre labels for additional points of decomposed cells - inline const labelList& additionalIds() const; + inline const labelList& additionalIds() const noexcept; //- Write access to additional (user) labels. - inline DynamicList<label>& additionalIds(); + inline DynamicList<label>& additionalIds() noexcept; // Edit @@ -115,7 +112,7 @@ public: inline void clear(); //- Renumber cell ids (cellMap and additionalIds) to account for - // subset meshes + //- subset meshes void renumberCells(const labelUList& mapping); //- Renumber point ids (pointMap) to account for subset meshes diff --git a/src/fileFormats/vtk/part/foamVtkMeshMapsI.H b/src/fileFormats/vtk/part/foamVtkMeshMapsI.H index 3934a7f9461e5ce08df0a882b44fb445fc13df2c..2e39034ab1ee17b9dd2286352d221effdf1f2289 100644 --- a/src/fileFormats/vtk/part/foamVtkMeshMapsI.H +++ b/src/fileFormats/vtk/part/foamVtkMeshMapsI.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2020 OpenCFD Ltd. + Copyright (C) 2017-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,24 +27,6 @@ License #include "foamVtkMeshMaps.H" -// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // - -inline Foam::foamVtkMeshMaps::foamVtkMeshMaps() -: - cellMap_(0), - pointMap_(0), - additionalIds_(0) -{} - - -inline Foam::foamVtkMeshMaps::foamVtkMeshMaps(const label size) -: - cellMap_(size), - pointMap_(size), - additionalIds_(size) -{} - - // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // inline void Foam::foamVtkMeshMaps::clear() @@ -56,42 +38,42 @@ inline void Foam::foamVtkMeshMaps::clear() inline const Foam::labelList& -Foam::foamVtkMeshMaps::cellMap() const +Foam::foamVtkMeshMaps::cellMap() const noexcept { return cellMap_; } inline Foam::DynamicList<Foam::label>& -Foam::foamVtkMeshMaps::cellMap() +Foam::foamVtkMeshMaps::cellMap() noexcept { return cellMap_; } inline const Foam::labelList& -Foam::foamVtkMeshMaps::pointMap() const +Foam::foamVtkMeshMaps::pointMap() const noexcept { return pointMap_; } inline Foam::DynamicList<Foam::label>& -Foam::foamVtkMeshMaps::pointMap() +Foam::foamVtkMeshMaps::pointMap() noexcept { return pointMap_; } inline const Foam::labelList& -Foam::foamVtkMeshMaps::additionalIds() const +Foam::foamVtkMeshMaps::additionalIds() const noexcept { return additionalIds_; } inline Foam::DynamicList<Foam::label>& -Foam::foamVtkMeshMaps::additionalIds() +Foam::foamVtkMeshMaps::additionalIds() noexcept { return additionalIds_; } diff --git a/src/fileFormats/vtk/part/foamVtuCells.C b/src/fileFormats/vtk/part/foamVtuCells.C index 1bbf7c8d520f75d237f73e730b8d0fd4dd7c6b1a..8248de41c6b455cbe4e0a3abc816fd581528995c 100644 --- a/src/fileFormats/vtk/part/foamVtuCells.C +++ b/src/fileFormats/vtk/part/foamVtuCells.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -89,30 +89,23 @@ Foam::vtk::vtuCells::vtuCells } -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // -void Foam::vtk::vtuCells::clear() +void Foam::vtk::vtuCells::resize_all() { - vtuSizing::clear(); - cellTypes_.clear(); - vertLabels_.clear(); - vertOffset_.clear(); - faceLabels_.clear(); - faceOffset_.clear(); - - maps_.clear(); -} - - -void Foam::vtk::vtuCells::repopulate(const polyMesh& mesh) -{ - // vtuSizing::reset() called prior to this method - cellTypes_.resize(nFieldCells()); vertLabels_.resize(sizeOf(output_, slotType::CELLS)); vertOffset_.resize(sizeOf(output_, slotType::CELLS_OFFSETS)); faceLabels_.resize(sizeOf(output_, slotType::FACES)); faceOffset_.resize(sizeOf(output_, slotType::FACES_OFFSETS)); +} + + +void Foam::vtk::vtuCells::populateOutput(const polyMesh& mesh) +{ + // Already called + // - vtuSizing::reset + // - resize_all(); switch (output_) { @@ -163,10 +156,103 @@ void Foam::vtk::vtuCells::repopulate(const polyMesh& mesh) } +void Foam::vtk::vtuCells::populateOutput(const UList<cellShape>& shapes) +{ + if (output_ != contentType::LEGACY && output_ != contentType::XML) + { + WarningInFunction + << "Internal formats not supported for shape cells - using XML" + << nl << nl; + + output_ = contentType::XML; + } + + vtuSizing::resetShapes(shapes); + + maps_.clear(); + resize_all(); + // Done in populate routine: + /// maps_.cellMap() = identity(vtuSizing::nCells()); + + switch (output_) + { + case contentType::LEGACY: + { + populateShapesLegacy + ( + shapes, + cellTypes_, + vertLabels_, + maps_ + ); + break; + } + + case contentType::XML: + { + populateShapesXml + ( + shapes, + cellTypes_, + vertLabels_, + vertOffset_, + faceLabels_, + faceOffset_, + maps_ + ); + break; + } + + default: + { + FatalErrorInFunction + << "Unhandled VTK format " << int(output_) << nl + << exit(FatalError); + break; + } + } +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::vtk::vtuCells::clear() +{ + vtuSizing::clear(); + cellTypes_.clear(); + vertLabels_.clear(); + vertOffset_.clear(); + faceLabels_.clear(); + faceOffset_.clear(); + + maps_.clear(); +} + + void Foam::vtk::vtuCells::reset(const polyMesh& mesh) { vtuSizing::reset(mesh, decomposeRequest_); - repopulate(mesh); + resize_all(); + + populateOutput(mesh); +} + + +void Foam::vtk::vtuCells::reset +( + const polyMesh& mesh, + const labelUList& subsetCellsIds +) +{ + vtuSizing::reset(mesh, subsetCellsIds, decomposeRequest_); + resize_all(); + + if (selectionMode() == selectionModeType::SUBSET_MESH) + { + maps_.cellMap() = subsetCellsIds; + } + + populateOutput(mesh); } @@ -184,6 +270,75 @@ void Foam::vtk::vtuCells::reset } +void Foam::vtk::vtuCells::resetShapes +( + const UList<cellShape>& shapes +) +{ + if (output_ != contentType::LEGACY && output_ != contentType::XML) + { + WarningInFunction + << "VTK internal format is not supported for shape cells" + << " switching to xml" << nl << nl; + + output_ = contentType::XML; + } + + decomposeRequest_ = false; + + vtuSizing::resetShapes(shapes); + + maps_.clear(); + resize_all(); + maps_.cellMap() = identity(vtuSizing::nCells()); + + switch (output_) + { + case contentType::LEGACY: + { + populateShapesLegacy + ( + shapes, + cellTypes_, + vertLabels_, + maps_ + ); + break; + } + + case contentType::XML: + { + populateShapesXml + ( + shapes, + cellTypes_, + vertLabels_, + vertOffset_, + faceLabels_, + faceOffset_, + maps_ + ); + break; + } + + default: + { + FatalErrorInFunction + << "Unhandled VTK format " << int(output_) << nl + << exit(FatalError); + break; + } + } +} + + +void Foam::vtk::vtuCells::addPointCellLabels(const labelUList& cellIds) +{ + maps_.additionalIds() = cellIds; + setNumAddPoints(maps_.additionalIds().size()); +} + + void Foam::vtk::vtuCells::renumberCells(const labelUList& mapping) { maps_.renumberCells(mapping); diff --git a/src/fileFormats/vtk/part/foamVtuCells.H b/src/fileFormats/vtk/part/foamVtuCells.H index 8718caa796103d8023924ded4d7fc3d4025e178a..197190c6d5245a8410803432f0a7f6448f86d51c 100644 --- a/src/fileFormats/vtk/part/foamVtuCells.H +++ b/src/fileFormats/vtk/part/foamVtuCells.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2014-2020 OpenCFD Ltd. + Copyright (C) 2014-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -59,9 +59,6 @@ SourceFiles namespace Foam { -// Forward Declarations -class polyMesh; - namespace vtk { // Forward Declarations @@ -110,9 +107,14 @@ class vtuCells // Private Member Functions - //- Create the geometry using the previously requested output and - //- decomposition types. - void repopulate(const polyMesh& mesh); + //- Resize storage + void resize_all(); + + //- Create the geometry entries + void populateOutput(const polyMesh& mesh); + + //- Create the geometry entries from shapes + void populateOutput(const UList<cellShape>& shapes); //- No copy construct vtuCells(const vtuCells&) = delete; @@ -162,10 +164,10 @@ public: // Access //- The output content type - inline enum contentType content() const; + inline enum contentType content() const noexcept; //- Query the polyhedral decompose requested flag. - inline bool decomposeRequested() const; + inline bool decomposeRequested() const noexcept; //- True if no cellTypes are populated. inline bool empty() const noexcept; @@ -183,6 +185,14 @@ public: //- decomposition types. void reset(const polyMesh& mesh); + //- Create the geometry for a mesh subset, + //- using previously requested output and decomposition types. + void reset + ( + const polyMesh& mesh, + const labelUList& subsetCellsIds + ); + //- Respecify requested output and decomposition type prior to //- creating the geometry void reset @@ -192,35 +202,42 @@ public: const bool decompose ); + //- Reset sizing using primitive shapes only (ADVANCED USAGE) + // Effectively removes any polyhedrals! + void resetShapes(const UList<cellShape>& shapes); + //- Renumber cell ids to account for subset meshes void renumberCells(const labelUList& mapping); //- Renumber point ids to account for subset meshes void renumberPoints(const labelUList& mapping); + //- Define which additional cell-centres are to be used (ADVANCED!) + void addPointCellLabels(const labelUList& cellIds); + // Storage Access //- Values for "types" (XML) and "CELL_TYPES" (legacy) - inline const List<uint8_t>& cellTypes() const; + inline const List<uint8_t>& cellTypes() const noexcept; //- Values for "connectivity" (XML) or "CELLS" (legacy) - inline const labelList& vertLabels() const; + inline const labelList& vertLabels() const noexcept; //- Values for "offsets" (XML only) - inline const labelList& vertOffsets() const; + inline const labelList& vertOffsets() const noexcept; //- Values for "faces" (XML only) - inline const labelList& faceLabels() const; + inline const labelList& faceLabels() const noexcept; //- Values for "faceoffset" (XML only) - inline const labelList& faceOffsets() const; + inline const labelList& faceOffsets() const noexcept; //- Additional point addressing (from added point to original cell) - inline const labelList& addPointCellLabels() const; + inline const labelList& addPointCellLabels() const noexcept; //- Original cell ids for all cells (regular and decomposed). - inline const labelList& cellMap() const; + inline const labelList& cellMap() const noexcept; }; diff --git a/src/fileFormats/vtk/part/foamVtuCellsI.H b/src/fileFormats/vtk/part/foamVtuCellsI.H index e6c4d91436e0e041bf3d470eaba1eb9fad9b5f41..2734351e54389e26ec51beaef309aa951a60aea0 100644 --- a/src/fileFormats/vtk/part/foamVtuCellsI.H +++ b/src/fileFormats/vtk/part/foamVtuCellsI.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -30,13 +30,13 @@ License // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // inline enum Foam::vtk::vtuCells::contentType -Foam::vtk::vtuCells::content() const +Foam::vtk::vtuCells::content() const noexcept { return output_; } -inline bool Foam::vtk::vtuCells::decomposeRequested() const +inline bool Foam::vtk::vtuCells::decomposeRequested() const noexcept { return decomposeRequest_; } @@ -55,49 +55,49 @@ inline Foam::label Foam::vtk::vtuCells::size() const noexcept inline const Foam::List<uint8_t>& -Foam::vtk::vtuCells::cellTypes() const +Foam::vtk::vtuCells::cellTypes() const noexcept { return cellTypes_; } inline const Foam::labelList& -Foam::vtk::vtuCells::vertLabels() const +Foam::vtk::vtuCells::vertLabels() const noexcept { return vertLabels_; } inline const Foam::labelList& -Foam::vtk::vtuCells::vertOffsets() const +Foam::vtk::vtuCells::vertOffsets() const noexcept { return vertOffset_; } inline const Foam::labelList& -Foam::vtk::vtuCells::faceLabels() const +Foam::vtk::vtuCells::faceLabels() const noexcept { return faceLabels_; } inline const Foam::labelList& -Foam::vtk::vtuCells::faceOffsets() const +Foam::vtk::vtuCells::faceOffsets() const noexcept { return faceOffset_; } inline const Foam::labelList& -Foam::vtk::vtuCells::addPointCellLabels() const +Foam::vtk::vtuCells::addPointCellLabels() const noexcept { return maps_.additionalIds(); } inline const Foam::labelList& -Foam::vtk::vtuCells::cellMap() const +Foam::vtk::vtuCells::cellMap() const noexcept { return maps_.cellMap(); } diff --git a/src/fileFormats/vtk/part/foamVtuSizing.C b/src/fileFormats/vtk/part/foamVtuSizing.C index 369b358b1ea16d39245896eae9fb928c5b1527f8..c1a1688e7abe907e25e5e6749cf651dbfc5fe5b1 100644 --- a/src/fileFormats/vtk/part/foamVtuSizing.C +++ b/src/fileFormats/vtk/part/foamVtuSizing.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -31,7 +31,7 @@ License #include "cellShape.H" // Only used in this file -#include "foamVtuSizingTemplates.C" +#include "foamVtuSizingImpl.C" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -42,6 +42,162 @@ void Foam::vtk::vtuSizing::presizeMaps(foamVtkMeshMaps& maps) const } +void Foam::vtk::vtuSizing::checkSizes +( + const vtk::vtuSizing& sizing, + + const label cellTypes_size, + const label vertLabels_size, + const label vertOffset_size, + const label faceLabels_size, + const label faceOffset_size, + + const enum contentType output, + const label cellMap_size, + const label addPointsIds_size +) +{ + label nErrors = 0; + + #undef CHECK_SIZING + #define CHECK_SIZING(what, sizeInput, sizeExpected) \ + if (sizeInput != sizeExpected) \ + { \ + if (!nErrors++) \ + { \ + FatalErrorInFunction << "VTK sizing error" << nl; \ + } \ + FatalError \ + << " " << what << " size=" << sizeInput \ + << " expected " << sizeExpected << nl; \ + } + + + CHECK_SIZING("cellTypes", cellTypes_size, sizing.nFieldCells()); + CHECK_SIZING("cellMap", cellMap_size, sizing.nFieldCells()); + CHECK_SIZING("addPointsIds", addPointsIds_size, sizing.nAddPoints()); + + switch (output) + { + case contentType::LEGACY: + { + CHECK_SIZING("legacy", vertLabels_size, sizing.sizeLegacy()); + break; + } + + case contentType::XML: + { + // XML uses connectivity/offset pair. + CHECK_SIZING + ( + "connectivity", + vertLabels_size, + sizing.sizeXml(slotType::CELLS) + ); + CHECK_SIZING + ( + "offsets", + vertOffset_size, + sizing.sizeXml(slotType::CELLS_OFFSETS) + ); + if (sizing.nFaceLabels()) + { + CHECK_SIZING + ( + "faces", + faceLabels_size, + sizing.sizeXml(slotType::FACES) + ); + + CHECK_SIZING + ( + "faceOffsets", + faceOffset_size, + sizing.sizeXml(slotType::FACES_OFFSETS) + ); + } + break; + } + + case contentType::INTERNAL1: + { + // VTK-internal1 connectivity/offset pair. + CHECK_SIZING + ( + "connectivity", + vertLabels_size, + sizing.sizeInternal1(slotType::CELLS) + ); + CHECK_SIZING + ( + "offsets", + vertOffset_size, + sizing.sizeInternal1(slotType::CELLS_OFFSETS) + ); + if (sizing.nFaceLabels()) + { + CHECK_SIZING + ( + "faces", + faceLabels_size, + sizing.sizeInternal1(slotType::FACES) + ); + CHECK_SIZING + ( + "faceOffsets", + faceOffset_size, + sizing.sizeInternal1(slotType::FACES_OFFSETS) + ); + } + break; + } + + case contentType::INTERNAL2: + { + // VTK-internal2 connectivity/offset pair. + CHECK_SIZING + ( + "connectivity", + vertLabels_size, + sizing.sizeInternal2(slotType::CELLS) + ); + CHECK_SIZING + ( + "offsets", + vertOffset_size, + sizing.sizeInternal2(slotType::CELLS_OFFSETS) + ); + if (sizing.nFaceLabels()) + { + CHECK_SIZING + ( + "faces", + faceLabels_size, + sizing.sizeInternal2(slotType::FACES) + ); + CHECK_SIZING + ( + "faceOffsets", + faceOffset_size, + sizing.sizeInternal2(slotType::FACES_OFFSETS) + ); + } + break; + } + } + + if (nErrors) + { + FatalError + << nl + << "Total of " << nErrors << " sizing errors encountered!" + << exit(FatalError); + } + + #undef CHECK_SIZING +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::vtk::vtuSizing::vtuSizing() noexcept @@ -66,6 +222,7 @@ Foam::vtk::vtuSizing::vtuSizing void Foam::vtk::vtuSizing::clear() noexcept { decompose_ = false; + selectionMode_ = FULL_MESH; nCells_ = 0; nPoints_ = 0; nVertLabels_ = 0; @@ -86,20 +243,56 @@ void Foam::vtk::vtuSizing::reset const bool decompose ) { - const cellModel& tet = cellModel::ref(cellModel::TET); - const cellModel& pyr = cellModel::ref(cellModel::PYR); - const cellModel& prism = cellModel::ref(cellModel::PRISM); + reset(mesh, labelUList::null(), decompose); +} + + +void Foam::vtk::vtuSizing::reset +( + const polyMesh& mesh, + const labelUList& subsetCellsIds, + const bool decompose +) +{ + // References to cell shape models + const cellModel& tet = cellModel::ref(cellModel::TET); + const cellModel& pyr = cellModel::ref(cellModel::PYR); + const cellModel& prism = cellModel::ref(cellModel::PRISM); + const cellModel& hex = cellModel::ref(cellModel::HEX); const cellModel& wedge = cellModel::ref(cellModel::WEDGE); const cellModel& tetWedge = cellModel::ref(cellModel::TETWEDGE); - const cellModel& hex = cellModel::ref(cellModel::HEX); const cellShapeList& shapes = mesh.cellShapes(); // Unique vertex labels per polyhedral labelHashSet hashUniqId(2*256); - decompose_ = decompose; - nCells_ = mesh.nCells(); + + // Special treatment for mesh subsets. + const bool isSubsetMesh + ( + notNull(subsetCellsIds) + ); + + if (isSubsetMesh) + { + decompose_ = false; // Disallow decomposition for subset mode + selectionMode_ = selectionModeType::SUBSET_MESH; + } + else + { + decompose_ = decompose; // Disallow decomposition + selectionMode_ = selectionModeType::FULL_MESH; + } + + const label nInputCells = + ( + isSubsetMesh + ? subsetCellsIds.size() + : shapes.size() + ); + + nCells_ = nInputCells; nPoints_ = mesh.nPoints(); nAddCells_ = 0; nAddVerts_ = 0; @@ -109,10 +302,10 @@ void Foam::vtk::vtuSizing::reset nFaceLabels_ = 0; nVertPoly_ = 0; - const label len = mesh.nCells(); - - for (label celli=0; celli < len; ++celli) + for (label inputi = 0; inputi < nInputCells; ++inputi) { + const label celli(isSubsetMesh ? subsetCellsIds[inputi] : inputi); + const cellShape& shape = shapes[celli]; const cellModel& model = shape.model(); @@ -128,15 +321,15 @@ void Foam::vtk::vtuSizing::reset --nCellsPoly_; nVertLabels_ += shape.size(); } - else if (model == tetWedge && decompose) + else if (model == tetWedge && decompose_) { nVertLabels_ += 6; // Treat as squeezed prism (VTK_WEDGE) } - else if (model == wedge && decompose) + else if (model == wedge && decompose_) { nVertLabels_ += 8; // Treat as squeezed hex } - else if (decompose) + else if (decompose_) { // Polyhedral: Decompose into tets + pyramids. ++nAddPoints_; @@ -199,7 +392,78 @@ void Foam::vtk::vtuSizing::reset } // Requested and actually required - decompose_ = (decompose && nCellsPoly_); + decompose_ = (decompose_ && nCellsPoly_); +} + + +// Synchronize changes here with the following: +// - vtuSizing::resetShapes +// - vtuSizing::populateArrays +// +void Foam::vtk::vtuSizing::resetShapes +( + const UList<cellShape>& shapes +) +{ + const cellModel& tet = cellModel::ref(cellModel::TET); + const cellModel& pyr = cellModel::ref(cellModel::PYR); + const cellModel& prism = cellModel::ref(cellModel::PRISM); + const cellModel& hex = cellModel::ref(cellModel::HEX); + + decompose_ = false; // Disallow decomposition + selectionMode_ = SHAPE_MESH; + + const label nInputCells = shapes.size(); + + nCells_ = nInputCells; + nPoints_ = 0; + nAddCells_ = 0; + nAddVerts_ = 0; + + nCellsPoly_ = 0; + nVertLabels_ = 0; + nFaceLabels_ = 0; + nVertPoly_ = 0; + + label nIgnored = 0; + + for (label inputi = 0; inputi < nInputCells; ++inputi) + { + const cellShape& shape = shapes[inputi]; + const cellModel& model = shape.model(); + + if + ( + model == tet + || model == pyr + || model == prism + || model == hex + ) + { + nVertLabels_ += shape.size(); + + // Guess for number of addressed points + nPoints_ = max(nPoints_, max(shape)); + } + else + { + --nCells_; + ++nIgnored; + } + } + + if (nIgnored) + { + FatalErrorInFunction + << "Encountered " << nIgnored << " unsupported cell shapes" + << " ... this is likely not good" << nl + << exit(FatalError); + } + + if (nCells_) + { + ++nPoints_; + } } @@ -339,6 +603,35 @@ void Foam::vtk::vtuSizing::populateLegacy } +void Foam::vtk::vtuSizing::populateShapesLegacy +( + const UList<cellShape>& shapes, + UList<uint8_t>& cellTypes, + labelUList& vertLabels, + foamVtkMeshMaps& maps +) const +{ + // Leave as zero-sized so that populateArrays doesn't fill it. + List<label> unused; + + presizeMaps(maps); + + populateArrays + ( + shapes, + *this, + cellTypes, + vertLabels, + unused, // offsets + unused, // faces + unused, // facesOffsets + contentType::LEGACY, + maps.cellMap(), + maps.additionalIds() + ); +} + + void Foam::vtk::vtuSizing::populateXml ( const polyMesh& mesh, @@ -368,6 +661,38 @@ void Foam::vtk::vtuSizing::populateXml } +void Foam::vtk::vtuSizing::populateShapesXml +( + const UList<cellShape>& shapes, + UList<uint8_t>& cellTypes, + labelUList& connectivity, + labelUList& offsets, + labelUList& faces, + labelUList& facesOffsets, + foamVtkMeshMaps& maps +) const +{ + // Leave as zero-sized so that populateArrays doesn't fill it. + List<label> unused; + + presizeMaps(maps); + + populateArrays + ( + shapes, + *this, + cellTypes, + connectivity, + offsets, + unused, // faces + unused, // facesOffsets + contentType::XML, + maps.cellMap(), + maps.additionalIds() + ); +} + + #undef definePopulateInternalMethod #define definePopulateInternalMethod(Type) \ \ diff --git a/src/fileFormats/vtk/part/foamVtuSizing.H b/src/fileFormats/vtk/part/foamVtuSizing.H index 826d7f7a023eab47768ee38cf4322fbfa5d4783f..7b2c5a6081a9f6694b272b22ca7f8370b1e32ded 100644 --- a/src/fileFormats/vtk/part/foamVtuSizing.H +++ b/src/fileFormats/vtk/part/foamVtuSizing.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -49,7 +49,7 @@ Description These are the storage entries normally associate with each output-type: \table - legacy output + legacy output (vtk DataFile Version 2.0) \c types | vtk cell type (1-255) \c cells | nLabels and unique vertex labels used by the cell, or | [nLabels nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...] @@ -118,6 +118,7 @@ namespace Foam { // Forward Declarations +class cellShape; class polyMesh; namespace vtk @@ -134,7 +135,7 @@ public: // Public Data //- Types of content that the storage may represent - enum contentType + enum contentType : char { LEGACY, //!< Legacy VTK content XML, //!< XML (VTU) content @@ -143,7 +144,7 @@ public: }; //- The possible storage 'slots' that can be used - enum slotType + enum slotType : char { CELLS, //!< Cell connectivity (ALL) CELLS_OFFSETS, //!< Cell end-offsets (XML), locations (INTERNAL1) @@ -152,6 +153,14 @@ public: FACES_OFFSETS //!< Faces end-offsets (XML) or locations (INTERNAL1) }; + //- How the mesh cells have been selected or defined + enum selectionModeType : char + { + FULL_MESH, + SUBSET_MESH, + SHAPE_MESH + }; + private: @@ -160,6 +169,9 @@ private: //- Polyhedral decomposition requested bool decompose_; + //- How the mesh cells have been selected or defined + selectionModeType selectionMode_; + //- Number of cells in the mesh label nCells_; @@ -199,8 +211,35 @@ private: //- set-size for cellMap and additionalIds void presizeMaps(foamVtkMeshMaps& maps) const; + //- Verify list sizes + static void checkSizes + ( + const vtk::vtuSizing& sizing, + + const label cellTypes_size, + const label vertLabels_size, + const label vertOffset_size, + const label faceLabels_size, + const label faceOffset_size, + + const enum contentType output, + + const label cellMap_size, + const label addPointsIds_size + ); + + //- Adjust cell/face offsets + template<class LabelType> + static void adjustOffsets + ( + UList<LabelType>& vertOffset, + UList<LabelType>& faceOffset, + const enum contentType output, + const bool hasFaceStream + ); + //- Populate lists. For (legacy | xml | internal) VTK representations - template<class LabelType, class LabelType2> + template<class LabelType> static void populateArrays ( const polyMesh& mesh, @@ -211,11 +250,53 @@ private: UList<LabelType>& faceLabels, UList<LabelType>& faceOffset, const enum contentType output, - UList<LabelType2>& cellMap, - UList<LabelType2>& addPointsIds + labelUList& cellMap, + labelUList& addPointsIds + ); + + //- Populate lists - Primitive mesh shapes only!! + template<class LabelType> + static void populateArrays + ( + const UList<cellShape>& shapes, + const vtk::vtuSizing& sizing, + UList<uint8_t>& cellTypes, + UList<LabelType>& vertLabels, + UList<LabelType>& vertOffset, + UList<LabelType>& faceLabels, // unused + UList<LabelType>& faceOffset, // unused + const enum contentType output, + labelUList& cellMap, + labelUList& addPointsIds // unused ); +protected: + + // Protected Member Functions + + //- Reset list for primitive shapes only (ADVANCED USAGE) + void populateShapesLegacy + ( + const UList<cellShape>& shapes, + UList<uint8_t>& cellTypes, + labelUList& connectivity, + foamVtkMeshMaps& maps + ) const; + + //- Reset list for primitive shapes only (ADVANCED USAGE) + void populateShapesXml + ( + const UList<cellShape>& shapes, + UList<uint8_t>& cellTypes, + labelUList& connectivity, + labelUList& offsets, + labelUList& faces, + labelUList& facesOffsets, + foamVtkMeshMaps& maps + ) const; + + public: // Constructors @@ -240,6 +321,20 @@ public: // Optionally with polyhedral decomposition. void reset(const polyMesh& mesh, const bool decompose=false); + //- Reset sizing by analyzing a subset of the mesh. + // A labelUList::null() selection corresponds to the entire mesh. + // Polyhedral decomposition is disabled for subsets. + void reset + ( + const polyMesh& mesh, + const labelUList& subsetCellsIds, + const bool decompose = false + ); + + //- Reset sizing using primitive shapes only (ADVANCED USAGE) + // Effectively removes any polyhedrals! + void resetShapes(const UList<cellShape>& shapes); + //- Reset all sizes to zero. void clear() noexcept; @@ -247,44 +342,56 @@ public: // Access //- Query the decompose flag (normally off) - inline bool decompose() const; + inline bool decompose() const noexcept; + + //- Query how the mesh cells have been selected or defined + inline selectionModeType selectionMode() const noexcept; //- Number of cells for the mesh - inline label nCells() const; + inline label nCells() const noexcept; //- Number of points for the mesh - inline label nPoints() const; + inline label nPoints() const noexcept; //- Number of vertex labels for the mesh - inline label nVertLabels() const; + inline label nVertLabels() const noexcept; //- Number of polyhedral face labels for the mesh - inline label nFaceLabels() const; + inline label nFaceLabels() const noexcept; //- Number of polyhedral cells for the mesh - inline label nCellsPoly() const; + inline label nCellsPoly() const noexcept; //- Number of vertex labels for polyhedral cells of the mesh - inline label nVertPoly() const; + inline label nVertPoly() const noexcept; //- Number of additional (decomposed) cells for the mesh - inline label nAddCells() const; + inline label nAddCells() const noexcept; //- Number of additional (decomposed) points for the mesh - inline label nAddPoints() const; + inline label nAddPoints() const noexcept; //- Number of additional (decomposed) vertices for the mesh - inline label nAddVerts() const; + inline label nAddVerts() const noexcept; //- Number of field cells = nCells + nAddCells - inline label nFieldCells() const; + inline label nFieldCells() const noexcept; //- Number of field points = nPoints + nAddPoints - inline label nFieldPoints() const; + inline label nFieldPoints() const noexcept; + + + // Edit + + //- Alter number of mesh points (ADVANCED USAGE) + void setNumPoints(label n) noexcept { nPoints_ = n; } + + //- Alter number of additional (cell-centre) points (ADVANCED USAGE) + void setNumAddPoints(label n) noexcept { nAddPoints_ = n; } - // Derived sizes + // Derived Sizes //- Return the required size for the storage slot label sizeOf diff --git a/src/fileFormats/vtk/part/foamVtuSizingI.H b/src/fileFormats/vtk/part/foamVtuSizingI.H index e3c2cc55e8083d19d5d9f4b665cc605ada94f9be..6c205714879d1eee3f4a840a17cc0fae87ad044a 100644 --- a/src/fileFormats/vtk/part/foamVtuSizingI.H +++ b/src/fileFormats/vtk/part/foamVtuSizingI.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2020 OpenCFD Ltd. + Copyright (C) 2017-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -29,75 +29,82 @@ License // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // -inline bool Foam::vtk::vtuSizing::decompose() const +inline bool Foam::vtk::vtuSizing::decompose() const noexcept { return decompose_; } -inline Foam::label Foam::vtk::vtuSizing::nCells() const +inline Foam::vtk::vtuSizing::selectionModeType +Foam::vtk::vtuSizing::selectionMode() const noexcept +{ + return selectionMode_; +} + + +inline Foam::label Foam::vtk::vtuSizing::nCells() const noexcept { return nCells_; } -inline Foam::label Foam::vtk::vtuSizing::nPoints() const +inline Foam::label Foam::vtk::vtuSizing::nPoints() const noexcept { return nPoints_; } -inline Foam::label Foam::vtk::vtuSizing::nVertLabels() const +inline Foam::label Foam::vtk::vtuSizing::nVertLabels() const noexcept { return nVertLabels_; } -inline Foam::label Foam::vtk::vtuSizing::nFaceLabels() const +inline Foam::label Foam::vtk::vtuSizing::nFaceLabels() const noexcept { return nFaceLabels_; } -inline Foam::label Foam::vtk::vtuSizing::nCellsPoly() const +inline Foam::label Foam::vtk::vtuSizing::nCellsPoly() const noexcept { return nCellsPoly_; } -inline Foam::label Foam::vtk::vtuSizing::nVertPoly() const +inline Foam::label Foam::vtk::vtuSizing::nVertPoly() const noexcept { return nVertPoly_; } -inline Foam::label Foam::vtk::vtuSizing::nAddCells() const +inline Foam::label Foam::vtk::vtuSizing::nAddCells() const noexcept { return nAddCells_; } -inline Foam::label Foam::vtk::vtuSizing::nAddPoints() const +inline Foam::label Foam::vtk::vtuSizing::nAddPoints() const noexcept { return nAddPoints_; } -inline Foam::label Foam::vtk::vtuSizing::nAddVerts() const +inline Foam::label Foam::vtk::vtuSizing::nAddVerts() const noexcept { return nAddVerts_; } -inline Foam::label Foam::vtk::vtuSizing::nFieldCells() const +inline Foam::label Foam::vtk::vtuSizing::nFieldCells() const noexcept { - return nCells_ + nAddCells_; + return (nCells_ + nAddCells_); } -inline Foam::label Foam::vtk::vtuSizing::nFieldPoints() const +inline Foam::label Foam::vtk::vtuSizing::nFieldPoints() const noexcept { - return nPoints_ + nAddPoints_; + return (nPoints_ + nAddPoints_); } diff --git a/src/fileFormats/vtk/part/foamVtuSizingTemplates.C b/src/fileFormats/vtk/part/foamVtuSizingImpl.C similarity index 68% rename from src/fileFormats/vtk/part/foamVtuSizingTemplates.C rename to src/fileFormats/vtk/part/foamVtuSizingImpl.C index 7770f2a2579c202cd1746347b5efbff1703cf19c..69abf42df56203f6d8496da6c8a5dbe883fe7631 100644 --- a/src/fileFormats/vtk/part/foamVtuSizingTemplates.C +++ b/src/fileFormats/vtk/part/foamVtuSizingImpl.C @@ -32,127 +32,54 @@ License // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // -template<class LabelType, class LabelType2> -void Foam::vtk::vtuSizing::populateArrays +template<class LabelType> +void Foam::vtk::vtuSizing::adjustOffsets ( - const polyMesh& mesh, - const vtk::vtuSizing& sizing, - UList<uint8_t>& cellTypes, - UList<LabelType>& vertLabels, UList<LabelType>& vertOffset, - UList<LabelType>& faceLabels, UList<LabelType>& faceOffset, const enum contentType output, - UList<LabelType2>& cellMap, - UList<LabelType2>& addPointsIds + const bool hasFaceStream ) { - // Characteristics - - // Are vertLabels prefixed with the size? - // Also use as the size of the prefixed information - const int prefix = - ( - output == contentType::LEGACY - || output == contentType::INTERNAL1 - ) ? 1 : 0; - - - // STAGE 1: Verify storage sizes - - if (cellTypes.size() != sizing.nFieldCells()) - { - FatalErrorInFunction - << " cellTypes size=" << cellTypes.size() - << " expected " << sizing.nFieldCells() - << exit(FatalError); - } - - if (cellMap.size() != sizing.nFieldCells()) - { - FatalErrorInFunction - << " cellMap size=" << cellMap.size() - << " expected " << sizing.nFieldCells() - << exit(FatalError); - } - - if (addPointsIds.size() != sizing.nAddPoints()) - { - FatalErrorInFunction - << " addPointsIds size=" << addPointsIds.size() - << " expected " << sizing.nAddPoints() - << exit(FatalError); - } + // =========================================== + // Adjust vertOffset for all cells + // A second pass is needed for several reasons. + // - Additional (decomposed) cells are placed out of sequence + // - INTERNAL1 connectivity has size prefixed + // + // Cell offsets: + // - XML format expects end-offsets, + // - INTERNAL1 expects begin-offsets + // - INTERNAL2 expects begin/end-offsets switch (output) { - case contentType::LEGACY: - { - if (vertLabels.size() != sizing.sizeLegacy()) - { - FatalErrorInFunction - << " legacy size=" << vertLabels.size() - << " expected " << sizing.sizeLegacy() - << exit(FatalError); - } + case contentType::LEGACY: // Nothing to do break; - } case contentType::XML: { - // XML uses connectivity/offset pair. - if - ( - vertLabels.size() - != sizing.sizeXml(slotType::CELLS) - ) - { - FatalErrorInFunction - << " connectivity size=" << vertLabels.size() - << " expected " - << sizing.sizeXml(slotType::CELLS) - << exit(FatalError); - } - - if - ( - vertOffset.size() - != sizing.sizeXml(slotType::CELLS_OFFSETS) - ) - { - FatalErrorInFunction - << " offsets size=" << vertOffset.size() - << " expected " - << sizing.sizeXml(slotType::CELLS_OFFSETS) - << exit(FatalError); - } - - if (sizing.nFaceLabels()) - { - if - ( - faceLabels.size() - != sizing.sizeXml(slotType::FACES) - ) - { - FatalErrorInFunction - << " faces size=" << faceLabels.size() - << " expected " - << sizing.sizeXml(slotType::FACES) - << exit(FatalError); - } + // Transform cell sizes (vertOffset) into begin offsets + + // vertOffset[0] already contains its size, leave untouched + for (label i = 1; i < vertOffset.size(); ++i) + { + vertOffset[i] += vertOffset[i-1]; + } + + // The end face offsets, leaving -1 untouched + if (hasFaceStream) + { + LabelType prev(0); - if - ( - faceOffset.size() - != sizing.sizeXml(slotType::FACES_OFFSETS) - ) + for (LabelType& off : faceOffset) { - FatalErrorInFunction - << " facesOffsets size=" << faceOffset.size() - << " expected " - << sizing.sizeXml(slotType::FACES_OFFSETS) - << exit(FatalError); + const LabelType sz(off); + if (sz > 0) + { + prev += sz; + off = prev; + } } } break; @@ -160,59 +87,31 @@ void Foam::vtk::vtuSizing::populateArrays case contentType::INTERNAL1: { - // VTK-internal1 connectivity/offset pair. - if - ( - vertLabels.size() - != sizing.sizeInternal1(slotType::CELLS) - ) - { - FatalErrorInFunction - << " connectivity size=" << vertLabels.size() - << " expected " - << sizing.sizeInternal1(slotType::CELLS) - << exit(FatalError); - } - - if - ( - vertOffset.size() - != sizing.sizeInternal1(slotType::CELLS_OFFSETS) - ) - { - FatalErrorInFunction - << " offsets size=" << vertOffset.size() - << " expected " - << sizing.sizeInternal1(slotType::CELLS_OFFSETS) - << exit(FatalError); - } - - if (sizing.nFaceLabels()) - { - if - ( - faceLabels.size() - != sizing.sizeInternal1(slotType::FACES) - ) + // Transform cell sizes (vertOffset) into begin offsets + { + LabelType beg(0); + + for (LabelType& off : vertOffset) { - FatalErrorInFunction - << " faces size=" << faceLabels.size() - << " expected " - << sizing.sizeInternal1(slotType::FACES) - << exit(FatalError); + const LabelType sz(off); + off = beg; + beg += 1 + sz; // Additional 1 to skip embedded prefix } + } + + // The begin face offsets, leaving -1 untouched + if (hasFaceStream) + { + LabelType beg(0); - if - ( - faceOffset.size() - != sizing.sizeInternal1(slotType::FACES_OFFSETS) - ) + for (LabelType& off : faceOffset) { - FatalErrorInFunction - << " facesOffsets size=" << faceOffset.size() - << " expected " - << sizing.sizeInternal1(slotType::FACES_OFFSETS) - << exit(FatalError); + const LabelType sz(off); + if (sz > 0) + { + off = beg; + beg += sz; + } } } break; @@ -220,64 +119,92 @@ void Foam::vtk::vtuSizing::populateArrays case contentType::INTERNAL2: { - // VTK-internal2 connectivity/offset pair. - if - ( - vertLabels.size() - != sizing.sizeInternal2(slotType::CELLS) - ) - { - FatalErrorInFunction - << " connectivity size=" << vertLabels.size() - << " expected " - << sizing.sizeInternal2(slotType::CELLS) - << exit(FatalError); - } - - if - ( - vertOffset.size() - != sizing.sizeInternal2(slotType::CELLS_OFFSETS) - ) - { - FatalErrorInFunction - << " offsets size=" << vertOffset.size() - << " expected " - << sizing.sizeInternal2(slotType::CELLS_OFFSETS) - << exit(FatalError); - } - - if (sizing.nFaceLabels()) - { - if - ( - faceLabels.size() - != sizing.sizeInternal2(slotType::FACES) - ) + // Transform cell sizes (vertOffset) into begin/end offsets + // input [n1, n2, n3, ..., 0] + // becomes [0, n1, n1+n2, n1+n2+n3, ..., nTotal] + + // The last entry of vertOffset was initialized as zero and + // never revisited, so the following loop is OK + { + LabelType total(0); + + for (LabelType& off : vertOffset) { - FatalErrorInFunction - << " faces size=" << faceLabels.size() - << " expected " - << sizing.sizeInternal2(slotType::FACES) - << exit(FatalError); + const LabelType sz(off); + off = total; + total += sz; } + } + + // The begin face offsets, leaving -1 untouched + if (hasFaceStream) + { + LabelType beg(0); - if - ( - faceOffset.size() - != sizing.sizeInternal2(slotType::FACES_OFFSETS) - ) + for (LabelType& off : faceOffset) { - FatalErrorInFunction - << " facesOffsets size=" << faceOffset.size() - << " expected " - << sizing.sizeInternal2(slotType::FACES_OFFSETS) - << exit(FatalError); + const LabelType sz(off); + if (sz > 0) + { + off = beg; + beg += sz; + } } } break; } } +} + + +template<class LabelType> +void Foam::vtk::vtuSizing::populateArrays +( + const polyMesh& mesh, + const vtk::vtuSizing& sizing, + + UList<uint8_t>& cellTypes, + UList<LabelType>& vertLabels, + UList<LabelType>& vertOffset, + UList<LabelType>& faceLabels, + UList<LabelType>& faceOffset, + const enum contentType output, + labelUList& cellMap, + labelUList& addPointsIds +) +{ + if (sizing.selectionMode() == selectionModeType::SHAPE_MESH) + { + FatalErrorInFunction + << "Programming error ... attempting to populate a VTU mesh" + << " but it was originally sized using independent cell shapes" + << exit(FatalError); + } + + // Verify storage sizes + checkSizes + ( + sizing, + + cellTypes.size(), + vertLabels.size(), vertOffset.size(), + faceLabels.size(), faceOffset.size(), + + output, + + cellMap.size(), + addPointsIds.size() + ); + + // Characteristics + + // Are vertLabels prefixed with the size? + // Also use as the size of the prefixed information + const int prefix = + ( + output == contentType::LEGACY + || output == contentType::INTERNAL1 + ) ? 1 : 0; // Initialization @@ -297,9 +224,9 @@ void Foam::vtk::vtuSizing::populateArrays const cellModel& tet = cellModel::ref(cellModel::TET); const cellModel& pyr = cellModel::ref(cellModel::PYR); const cellModel& prism = cellModel::ref(cellModel::PRISM); + const cellModel& hex = cellModel::ref(cellModel::HEX); const cellModel& wedge = cellModel::ref(cellModel::WEDGE); const cellModel& tetWedge = cellModel::ref(cellModel::TETWEDGE); - const cellModel& hex = cellModel::ref(cellModel::HEX); const cellShapeList& shapes = mesh.cellShapes(); @@ -316,11 +243,11 @@ void Foam::vtk::vtuSizing::populateArrays // Index into vertLabels for decomposed polys label nVertDecomp = sizing.nVertLabels() + prefix*sizing.nCells(); - // Placement of decomposed cells + // Placement of additional decomposed cells label nCellDecomp = mesh.nCells(); // Placement of additional point labels - label nPointDecomp = 0; + label nPointDecomp = mesh.nPoints(); // Non-decomposed polyhedral are represented as a face-stream. // For legacy format, this stream replaces the normal connectivity @@ -346,23 +273,48 @@ void Foam::vtk::vtuSizing::populateArrays // the per-cell vertLabels entries, and the faceOffset contains the *size* // associated with the per-cell faceLabels. - const label len = shapes.size(); - for (label celli=0; celli < len; ++celli) + // Special treatment for mesh subsets + // Here the cellMap is the list of input cells! + + const bool isSubsetMesh + ( + sizing.selectionMode() == selectionModeType::SUBSET_MESH + ); + + const label nInputCells = + ( + isSubsetMesh + ? cellMap.size() + : shapes.size() + ); + + + for + ( + label inputi = 0, cellIndex = 0; // cellIndex: the ouput location + inputi < nInputCells; + ++inputi, ++cellIndex + ) { + const label celli(isSubsetMesh ? cellMap[inputi] : inputi); + const cellShape& shape = shapes[celli]; const cellModel& model = shape.model(); - cellMap[celli] = celli; + if (!isSubsetMesh) + { + cellMap[cellIndex] = celli; + } if (model == tet) { - cellTypes[celli] = vtk::cellType::VTK_TETRA; + cellTypes[cellIndex] = vtk::cellType::VTK_TETRA; constexpr label nShapePoints = 4; // OR shape.size(); if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -376,12 +328,12 @@ void Foam::vtk::vtuSizing::populateArrays } else if (model == pyr) { - cellTypes[celli] = vtk::cellType::VTK_PYRAMID; + cellTypes[cellIndex] = vtk::cellType::VTK_PYRAMID; constexpr label nShapePoints = 5; // OR shape.size(); if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -395,12 +347,12 @@ void Foam::vtk::vtuSizing::populateArrays } else if (model == hex) { - cellTypes[celli] = vtk::cellType::VTK_HEXAHEDRON; + cellTypes[cellIndex] = vtk::cellType::VTK_HEXAHEDRON; constexpr label nShapePoints = 8; // OR shape.size(); if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -414,12 +366,12 @@ void Foam::vtk::vtuSizing::populateArrays } else if (model == prism) { - cellTypes[celli] = vtk::cellType::VTK_WEDGE; + cellTypes[cellIndex] = vtk::cellType::VTK_WEDGE; constexpr label nShapePoints = 6; // OR shape.size(); if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -437,12 +389,12 @@ void Foam::vtk::vtuSizing::populateArrays else if (model == tetWedge && sizing.decompose()) { // Treat as squeezed prism - cellTypes[celli] = vtk::cellType::VTK_WEDGE; + cellTypes[cellIndex] = vtk::cellType::VTK_WEDGE; constexpr label nShapePoints = 6; if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -459,12 +411,12 @@ void Foam::vtk::vtuSizing::populateArrays else if (model == wedge && sizing.decompose()) { // Treat as squeezed hex - cellTypes[celli] = vtk::cellType::VTK_HEXAHEDRON; + cellTypes[cellIndex] = vtk::cellType::VTK_HEXAHEDRON; constexpr label nShapePoints = 8; if (vertOffset.size()) { - vertOffset[celli] = nShapePoints; + vertOffset[cellIndex] = nShapePoints; } if (prefix) { @@ -492,7 +444,7 @@ void Foam::vtk::vtuSizing::populateArrays // Mapping from additional point to cell, and the new vertex from // the cell-centre - const label newVertexLabel = mesh.nPoints() + nPointDecomp; + const label newVertexLabel = nPointDecomp; addPointsIds[nPointDecomp++] = celli; @@ -526,7 +478,7 @@ void Foam::vtk::vtuSizing::populateArrays if (firstCell) { firstCell = false; - celLoc = celli; + celLoc = cellIndex; vrtLoc = nVertLabels; nVertLabels += prefix + nShapePoints; } @@ -578,7 +530,7 @@ void Foam::vtk::vtuSizing::populateArrays if (firstCell) { firstCell = false; - celLoc = celli; + celLoc = cellIndex; vrtLoc = nVertLabels; nVertLabels += prefix + nShapePoints; } @@ -626,7 +578,7 @@ void Foam::vtk::vtuSizing::populateArrays // face-stream // [nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...] - cellTypes[celli] = vtk::cellType::VTK_POLYHEDRON; + cellTypes[cellIndex] = vtk::cellType::VTK_POLYHEDRON; const labelList& cFaces = mesh.cells()[celli]; const label startLabel = faceIndexer; @@ -676,9 +628,9 @@ void Foam::vtk::vtuSizing::populateArrays else { // Size for face stream - faceOffset[celli] = (faceIndexer - startLabel); + faceOffset[cellIndex] = (faceIndexer - startLabel); - vertOffset[celli] = hashUniqId.size(); + vertOffset[cellIndex] = hashUniqId.size(); if (prefix) { vertLabels[nVertLabels++] = hashUniqId.size(); @@ -703,108 +655,224 @@ void Foam::vtk::vtuSizing::populateArrays // - INTERNAL1 expects begin-offsets // - INTERNAL2 expects begin/end-offsets - switch (output) + adjustOffsets<LabelType> + ( + vertOffset, + faceOffset, + output, + sizing.nFaceLabels() // hasFaceStream + ); +} + + + +// Synchronize changes here with the following: +// - vtuSizing::resetShapes +// - vtuSizing::populateArrays + +template<class LabelType> +void Foam::vtk::vtuSizing::populateArrays +( + const UList<cellShape>& shapes, + const vtk::vtuSizing& sizing, + + UList<uint8_t>& cellTypes, + UList<LabelType>& vertLabels, + UList<LabelType>& vertOffset, + UList<LabelType>& faceLabels, + UList<LabelType>& faceOffset, + const enum contentType output, + labelUList& cellMap, + labelUList& addPointsIds +) +{ + if (sizing.selectionMode() != selectionModeType::SHAPE_MESH) { - case contentType::LEGACY: // nothing to do - break; + FatalErrorInFunction + << "Programming error ... attempting to populate a VTU mesh" + << " from cell shapes, but sizing originated from a different" + << " representation" << nl + << exit(FatalError); + } - case contentType::XML: + // Verify storage sizes + checkSizes + ( + sizing, + + cellTypes.size(), + vertLabels.size(), vertOffset.size(), + faceLabels.size(), faceOffset.size(), + + output, + + cellMap.size(), + addPointsIds.size() + ); + + // Characteristics + + // Are vertLabels prefixed with the size? + // Also use as the size of the prefixed information + const int prefix = + ( + output == contentType::LEGACY + || output == contentType::INTERNAL1 + ) ? 1 : 0; + + + // Initialization + + faceOffset = -1; + + // For INTERNAL2, the vertOffset is (nFieldCells+1), which means that + // the last entry is never visited. Set as zero now. + + if (vertOffset.size()) + { + vertOffset.first() = 0; + vertOffset.last() = 0; + } + + + const cellModel& tet = cellModel::ref(cellModel::TET); + const cellModel& pyr = cellModel::ref(cellModel::PYR); + const cellModel& prism = cellModel::ref(cellModel::PRISM); + const cellModel& hex = cellModel::ref(cellModel::HEX); + + // Index into vertLabels for normal cells + label nVertLabels = 0; + + // =========================================== + // STAGE 2: Rewrite in VTK form + // During this stage, the vertOffset contains the *size* associated with + // the per-cell vertLabels entries, and the faceOffset contains the *size* + // associated with the per-cell faceLabels. + + const label nInputCells = shapes.size(); + + label nIgnored = 0; + + for + ( + label inputi = 0, cellIndex = 0; // cellIndex: the ouput location + inputi < nInputCells; + ++inputi, ++cellIndex + ) + { + const cellShape& shape = shapes[inputi]; + const cellModel& model = shape.model(); + + if (model == tet) { - // Transform cell sizes (vertOffset) into begin offsets + cellTypes[cellIndex] = vtk::cellType::VTK_TETRA; + constexpr label nShapePoints = 4; // OR shape.size(); - // vertOffset[0] already contains its size, leave untouched - for (label i = 1; i < vertOffset.size(); ++i) + if (vertOffset.size()) { - vertOffset[i] += vertOffset[i-1]; + vertOffset[cellIndex] = nShapePoints; } - - // The end face offsets, leaving -1 untouched - if (sizing.nFaceLabels()) + if (prefix) { - LabelType prev(0); + vertLabels[nVertLabels++] = nShapePoints; + } - for (LabelType& off : faceOffset) - { - const LabelType sz(off); - if (sz > 0) - { - prev += sz; - off = prev; - } - } + for (const label cpi : shape) + { + vertLabels[nVertLabels++] = cpi; } - break; } - - case contentType::INTERNAL1: + else if (model == pyr) { - // Transform cell sizes (vertOffset) into begin offsets - { - LabelType beg(0); + cellTypes[cellIndex] = vtk::cellType::VTK_PYRAMID; + constexpr label nShapePoints = 5; // OR shape.size(); - for (LabelType& off : vertOffset) - { - const LabelType sz(off); - off = beg; - beg += 1 + sz; // Additional 1 to skip embedded prefix - } + if (vertOffset.size()) + { + vertOffset[cellIndex] = nShapePoints; } - - // The begin face offsets, leaving -1 untouched - if (sizing.nFaceLabels()) + if (prefix) { - LabelType beg(0); + vertLabels[nVertLabels++] = nShapePoints; + } - for (LabelType& off : faceOffset) - { - const LabelType sz(off); - if (sz > 0) - { - off = beg; - beg += sz; - } - } + for (const label cpi : shape) + { + vertLabels[nVertLabels++] = cpi; } - break; } - - case contentType::INTERNAL2: + else if (model == hex) { - // Transform cell sizes (vertOffset) into begin/end offsets - // input [n1, n2, n3, ..., 0] - // becomes [0, n1, n1+n2, n1+n2+n3, ..., nTotal] + cellTypes[cellIndex] = vtk::cellType::VTK_HEXAHEDRON; + constexpr label nShapePoints = 8; // OR shape.size(); - // The last entry of vertOffset was initialized as zero and - // never revisited, so the following loop is OK + if (vertOffset.size()) { - LabelType total(0); - - for (LabelType& off : vertOffset) - { - const LabelType sz(off); - off = total; - total += sz; - } + vertOffset[cellIndex] = nShapePoints; + } + if (prefix) + { + vertLabels[nVertLabels++] = nShapePoints; } - // The begin face offsets, leaving -1 untouched - if (sizing.nFaceLabels()) + for (const label cpi : shape) { - LabelType beg(0); + vertLabels[nVertLabels++] = cpi; + } + } + else if (model == prism) + { + cellTypes[cellIndex] = vtk::cellType::VTK_WEDGE; + constexpr label nShapePoints = 6; // OR shape.size(); - for (LabelType& off : faceOffset) - { - const LabelType sz(off); - if (sz > 0) - { - off = beg; - beg += sz; - } - } + if (vertOffset.size()) + { + vertOffset[cellIndex] = nShapePoints; } - break; + if (prefix) + { + vertLabels[nVertLabels++] = nShapePoints; + } + + // VTK_WEDGE triangles point outwards (swap 1<->2, 4<->5) + vertLabels[nVertLabels++] = shape[0]; + vertLabels[nVertLabels++] = shape[2]; + vertLabels[nVertLabels++] = shape[1]; + vertLabels[nVertLabels++] = shape[3]; + vertLabels[nVertLabels++] = shape[5]; + vertLabels[nVertLabels++] = shape[4]; + } + else + { + // Silent here. + // - already complained (and skipped) during initial sizing + --cellIndex; + ++nIgnored; } } + + // May have been done by caller, + // but for additional safety set an identity mapping + ListOps::identity(cellMap); + + // =========================================== + // Adjust vertOffset for all cells + // A second pass is needed for several reasons. + // - Additional (decomposed) cells are placed out of sequence + // - INTERNAL1 connectivity has size prefixed + // + // Cell offsets: + // - XML format expects end-offsets, + // - INTERNAL1 expects begin-offsets + // - INTERNAL2 expects begin/end-offsets + + adjustOffsets<LabelType> + ( + vertOffset, + faceOffset, + output, + sizing.nFaceLabels() // hasFaceStream + ); } diff --git a/src/fileFormats/vtk/write/foamVtkLineWriter.C b/src/fileFormats/vtk/write/foamVtkLineWriter.C new file mode 100644 index 0000000000000000000000000000000000000000..a5f42b5ad98c82882fb20c960cbbbd42cb2af51c --- /dev/null +++ b/src/fileFormats/vtk/write/foamVtkLineWriter.C @@ -0,0 +1,138 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + +#include "foamVtkLineWriter.H" + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::vtk::lineWriter::lineWriter +( + const pointField& points, + const edgeList& edges, + const vtk::outputOptions opts +) +: + vtk::polyWriter(opts), + + points_(std::cref<pointField>(points)), + edges_(std::cref<edgeList>(edges)), + instant_() +{} + + +Foam::vtk::lineWriter::lineWriter +( + const pointField& points, + const edgeList& edges, + const fileName& file, + bool parallel +) +: + lineWriter(points, edges) +{ + open(file, parallel); +} + + +Foam::vtk::lineWriter::lineWriter +( + const pointField& points, + const edgeList& edges, + const vtk::outputOptions opts, + const fileName& file, + bool parallel +) +: + lineWriter(points, edges, opts) +{ + open(file, parallel); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::vtk::lineWriter::setTime(const instant& inst) +{ + instant_ = inst; +} + + +bool Foam::vtk::lineWriter::beginFile(std::string title) +{ + if (title.size()) + { + return vtk::fileWriter::beginFile(title); + } + + if (!instant_.name().empty()) + { + return vtk::fileWriter::beginFile + ( + "time='" + instant_.name() + "'" + ); + } + + // Provide default title + return vtk::fileWriter::beginFile("edges"); +} + + +bool Foam::vtk::lineWriter::writeGeometry() +{ + return writeLineGeometry(points_.get(), edges_.get()); +} + + +void Foam::vtk::lineWriter::writeTimeValue() +{ + if (!instant_.name().empty()) + { + vtk::fileWriter::writeTimeValue(instant_.value()); + } +} + + +void Foam::vtk::lineWriter::piece +( + const pointField& points, + const edgeList& edges +) +{ + endPiece(); + + points_ = std::cref<pointField>(points); + edges_ = std::cref<edgeList>(edges); +} + + +bool Foam::vtk::lineWriter::writeProcIDs() +{ + return vtk::fileWriter::writeProcIDs(nLocalLines_); +} + + +// ************************************************************************* // diff --git a/src/fileFormats/vtk/write/foamVtkLineWriter.H b/src/fileFormats/vtk/write/foamVtkLineWriter.H new file mode 100644 index 0000000000000000000000000000000000000000..36245558f238734f9352e455bf8d1e42d3dc2cb7 --- /dev/null +++ b/src/fileFormats/vtk/write/foamVtkLineWriter.H @@ -0,0 +1,180 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Class + Foam::vtk::lineWriter + +Description + Write edge/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 + foamVtkLineWriter.C + +\*---------------------------------------------------------------------------*/ + +#ifndef Foam_vtk_lineWriter_H +#define Foam_vtk_lineWriter_H + +#include "foamVtkPolyWriter.H" +#include "instant.H" +#include <functional> + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace vtk +{ + +/*---------------------------------------------------------------------------*\ + Class vtk::lineWriter Declaration +\*---------------------------------------------------------------------------*/ + +class lineWriter +: + public vtk::polyWriter +{ + // Private Member Data + + //- Reference to the points + std::reference_wrapper<const pointField> points_; + + //- Reference to the edges + std::reference_wrapper<const edgeList> edges_; + + //- Time name/value + instant instant_; + + + // Private Member Functions + + //- No copy construct + lineWriter(const lineWriter&) = delete; + + //- No copy assignment + void operator=(const lineWriter&) = delete; + + +public: + + // Constructors + + //- Construct from components (default format INLINE_BASE64) + lineWriter + ( + const pointField& pts, + const edgeList& edges, + 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. + lineWriter + ( + const pointField& pts, + const edgeList& edges, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + //- Construct from components and open the file for writing. + // The file name is with/without an extension. + lineWriter + ( + const pointField& pts, + const edgeList& edges, + const vtk::outputOptions opts, + const fileName& file, + bool parallel = Pstream::parRun() + ); + + + //- Destructor + virtual ~lineWriter() = default; + + + // Member Functions + + //- 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(); + + //- 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(); + + //- Reset point/edge references to begin a new piece + void piece(const pointField& points, const edgeList& edges); + + + //- Write processor ids for each line as CellData + //- (no-op in serial) + bool writeProcIDs(); + + //- Write a uniform field of Cell (Line) or Point values + template<class Type> + void writeUniform(const word& fieldName, const Type& val) + { + polyWriter::writeUniformValue<Type>(nLocalLines_, fieldName, val); + } +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace vtk +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/fileFormats/vtk/write/foamVtkPolyWriter.C b/src/fileFormats/vtk/write/foamVtkPolyWriter.C index f11894a554d76e763221317fabd45bc1df5e073d..28de456d9c5016090a780cdd334117b58e188e45 100644 --- a/src/fileFormats/vtk/write/foamVtkPolyWriter.C +++ b/src/fileFormats/vtk/write/foamVtkPolyWriter.C @@ -29,26 +29,86 @@ License #include "foamVtkOutput.H" #include "globalIndex.H" +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// The connectivity count for a list of edges +static inline label countConnectivity(const edgeList& edges) +{ + return 2 * edges.size(); // An edge always has two ends +} + + +// The connectivity count for a list of faces +static label countConnectivity(const faceList& faces) +{ + label nConnectivity = 0; + + for (const face& f : faces) + { + nConnectivity += f.size(); + } + + return nConnectivity; +} + +} // End namespace Foam + + // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::vtk::polyWriter::beginPiece ( const pointField& points, - const faceList& faces + const edgeList& edges ) { // Basic sizes nLocalPoints_ = points.size(); - nLocalFaces_ = faces.size(); - nLocalVerts_ = 0; + nLocalLines_ = edges.size(); + nLocalPolys_ = 0; - for (const face& f : faces) + numberOfPoints_ = nLocalPoints_; + numberOfCells_ = nLocalLines_; + + if (parallel_) + { + reduce(numberOfPoints_, sumOp<label>()); + reduce(numberOfCells_, sumOp<label>()); + } + + + // Nothing else to do for legacy + if (legacy()) return; + + if (format_) { - nLocalVerts_ += f.size(); + format().tag + ( + vtk::fileTag::PIECE, + vtk::fileAttr::NUMBER_OF_POINTS, numberOfPoints_, + vtk::fileAttr::NUMBER_OF_LINES, numberOfCells_ + // AND: vtk::fileAttr::NUMBER_OF_POLYS, 0 + ); } +} + + +void Foam::vtk::polyWriter::beginPiece +( + const pointField& points, + const faceList& faces +) +{ + // Basic sizes + nLocalPoints_ = points.size(); + nLocalLines_ = 0; + nLocalPolys_ = faces.size(); numberOfPoints_ = nLocalPoints_; - numberOfCells_ = nLocalFaces_; + numberOfCells_ = nLocalPolys_; if (parallel_) { @@ -67,6 +127,7 @@ void Foam::vtk::polyWriter::beginPiece vtk::fileTag::PIECE, vtk::fileAttr::NUMBER_OF_POINTS, numberOfPoints_, vtk::fileAttr::NUMBER_OF_POLYS, numberOfCells_ + // AND: vtk::fileAttr::NUMBER_OF_LINES, 0 ); } } @@ -79,48 +140,219 @@ void Foam::vtk::polyWriter::writePoints { this->beginPoints(numberOfPoints_); - if (parallel_ ? Pstream::master() : true) + if (parallel_) + { + vtk::writeListParallel(format_.ref(), points); + } + else { + vtk::writeList(format(), points); + + } + + this->endPoints(); +} + + +void Foam::vtk::polyWriter::writeLinesLegacy +( + const edgeList& edges, + const label pointOffset +) +{ + // Connectivity count without additional storage (done internally) + const label nLocalConns = countConnectivity(edges); + + label nLines = nLocalLines_; + label nConns = nLocalConns; + + if (parallel_) + { + reduce(nLines, sumOp<label>()); + reduce(nConns, sumOp<label>()); + } + + if (nLines != numberOfCells_) + { + FatalErrorInFunction + << "Expecting " << numberOfCells_ + << " edges, but found " << nLines + << exit(FatalError); + } + + legacy::beginLines(os_, nLines, nConns); + + labelList vertLabels(nLocalLines_ + nLocalConns); + + { + // Legacy: size + connectivity together + // [nPts, id1, id2, ..., nPts, id1, id2, ...] + + auto iter = vertLabels.begin(); + + const label off = pointOffset; + + for (const edge& e : edges) { - vtk::writeList(format(), points); + *iter = e.size(); // The size prefix (always 2 for an edge) + ++iter; + + *iter = off + e.first(); // Vertex labels + ++iter; + + *iter = off + e.second(); + ++iter; } } + if (parallel_) { - if (Pstream::master()) + vtk::writeListParallel(format_.ref(), vertLabels); + } + else + { + vtk::writeList(format(), vertLabels); + } + + if (format_) + { + format().flush(); + } +} + + +void Foam::vtk::polyWriter::writeLines +( + const edgeList& edges, + const label pointOffset +) +{ + // Connectivity count without additional storage (done internally) + const label nLocalConns = countConnectivity(edges); + + if (format_) + { + format().tag(vtk::fileTag::LINES); + } + + // + // 'connectivity' + // + { + labelList vertLabels(nLocalConns); + + label nConns = nLocalConns; + + if (parallel_) { - pointField recv; + reduce(nConns, sumOp<label>()); + } - // Receive each point field and write - for (const int subproci : Pstream::subProcs()) - { - IPstream fromProc(Pstream::commsTypes::blocking, subproci); + if (format_) + { + const uint64_t payLoad = vtk::sizeofData<label>(nConns); - { - fromProc >> recv; + format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY); + format().writeSize(payLoad * sizeof(label)); + } - vtk::writeList(format(), recv); - } + { + // XML: connectivity only + // [id1, id2, ..., id1, id2, ...] + + auto iter = vertLabels.begin(); + + const label off = pointOffset; + + for (const edge& e : edges) + { + // Edge vertex labels + *iter = off + e.first(); + ++iter; + + *iter = off + e.second(); + ++iter; } } + + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), vertLabels); + } else { - // Send - OPstream toProc - ( - Pstream::commsTypes::blocking, - Pstream::masterNo() - ); + vtk::writeList(format(), vertLabels); + } - { - toProc << points; - } + if (format_) + { + format().flush(); + format().endDataArray(); } } - this->endPoints(); + // + // 'offsets' (connectivity offsets) + // + { + labelList vertOffsets(nLocalLines_); + label nOffs = vertOffsets.size(); + + 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); + } + + + // processor-local connectivity offsets + label off = + ( + parallel_ ? globalIndex(nLocalConns).localStart() : 0 + ); + + + auto iter = vertOffsets.begin(); + + for (const edge& e : edges) + { + off += e.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::LINES); + } } @@ -131,27 +363,28 @@ void Foam::vtk::polyWriter::writePolysLegacy ) { // Connectivity count without additional storage (done internally) + const label nLocalConns = countConnectivity(faces); - label nFaces = nLocalFaces_; - label nVerts = nLocalVerts_; + label nPolys = nLocalPolys_; + label nConns = nLocalConns; if (parallel_) { - reduce(nFaces, sumOp<label>()); - reduce(nVerts, sumOp<label>()); + reduce(nPolys, sumOp<label>()); + reduce(nConns, sumOp<label>()); } - if (nFaces != numberOfCells_) + if (nPolys != numberOfCells_) { FatalErrorInFunction << "Expecting " << numberOfCells_ - << " faces, but found " << nFaces + << " faces, but found " << nPolys << exit(FatalError); } - legacy::beginPolys(os_, nFaces, nVerts); + legacy::beginPolys(os_, nPolys, nConns); - labelList vertLabels(nLocalFaces_ + nLocalVerts_); + labelList vertLabels(nLocalPolys_ + nLocalConns); { // Legacy: size + connectivity together @@ -159,21 +392,18 @@ void Foam::vtk::polyWriter::writePolysLegacy auto iter = vertLabels.begin(); - label off = pointOffset; + const label off = pointOffset; + for (const face& f : faces) { - for (const face& f : faces) + *iter = f.size(); // The size prefix + ++iter; + + for (const label id : f) { - *iter = f.size(); // The size prefix + *iter = id + off; // Vertex label ++iter; - - for (const label pfi : f) - { - *iter = pfi + off; // Face vertex label - ++iter; - } } - // off += points.size(); } } @@ -200,6 +430,9 @@ void Foam::vtk::polyWriter::writePolys const label pointOffset ) { + // Connectivity count without additional storage (done internally) + const label nLocalConns = countConnectivity(faces); + if (format_) { format().tag(vtk::fileTag::POLYS); @@ -209,18 +442,18 @@ void Foam::vtk::polyWriter::writePolys // 'connectivity' // { - labelList vertLabels(nLocalVerts_); + labelList vertLabels(nLocalConns); - label nVerts = nLocalVerts_; + label nConns = nLocalConns; if (parallel_) { - reduce(nVerts, sumOp<label>()); + reduce(nConns, sumOp<label>()); } if (format_) { - const uint64_t payLoad = vtk::sizeofData<label>(nVerts); + const uint64_t payLoad = vtk::sizeofData<label>(nConns); format().beginDataArray<label>(vtk::dataArrayAttr::CONNECTIVITY); format().writeSize(payLoad * sizeof(label)); @@ -234,16 +467,13 @@ void Foam::vtk::polyWriter::writePolys label off = pointOffset; + for (const face& f : faces) { - for (const face& f : faces) + for (const label id : f) { - for (const label pfi : f) - { - *iter = pfi + off; // Face vertex label - ++iter; - } + *iter = id + off; // Face vertex label + ++iter; } - // off += points.size(); } } @@ -269,7 +499,7 @@ void Foam::vtk::polyWriter::writePolys // 'offsets' (connectivity offsets) // { - labelList vertOffsets(nLocalFaces_); + labelList vertOffsets(nLocalPolys_); label nOffs = vertOffsets.size(); if (parallel_) @@ -289,19 +519,17 @@ void Foam::vtk::polyWriter::writePolys // processor-local connectivity offsets label off = ( - parallel_ ? globalIndex(nLocalVerts_).localStart() : 0 + parallel_ ? globalIndex(nLocalConns).localStart() : 0 ); auto iter = vertOffsets.begin(); + for (const face& f : faces) { - for (const face& f : faces) - { - off += f.size(); // End offset - *iter = off; - ++iter; - } + off += f.size(); // End offset + *iter = off; + ++iter; } @@ -340,8 +568,8 @@ Foam::vtk::polyWriter::polyWriter numberOfPoints_(0), numberOfCells_(0), nLocalPoints_(0), - nLocalFaces_(0), - nLocalVerts_(0) + nLocalLines_(0), + nLocalPolys_(0) { // We do not currently support append mode opts_.append(false); @@ -387,6 +615,36 @@ bool Foam::vtk::polyWriter::writeGeometry() } +bool Foam::vtk::polyWriter::writeLineGeometry +( + const pointField& points, + const edgeList& edges +) +{ + enter_Piece(); + + beginPiece(points, edges); + + writePoints(points); + + const label pointOffset = + ( + parallel_ ? globalIndex(nLocalPoints_).localStart() : 0 + ); + + if (legacy()) + { + writeLinesLegacy(edges, pointOffset); + } + else + { + writeLines(edges, pointOffset); + } + + return true; +} + + bool Foam::vtk::polyWriter::writePolyGeometry ( const pointField& points, diff --git a/src/fileFormats/vtk/write/foamVtkPolyWriter.H b/src/fileFormats/vtk/write/foamVtkPolyWriter.H index 8dadb17f7547b172934a7ade4f0c4cd45be5e3f7..4c10b0f2bf571bd8668d942f1bc236f7df217041 100644 --- a/src/fileFormats/vtk/write/foamVtkPolyWriter.H +++ b/src/fileFormats/vtk/write/foamVtkPolyWriter.H @@ -51,6 +51,7 @@ SourceFiles #include "foamVtkFileWriter.H" #include "pointField.H" +#include "edgeList.H" #include "faceList.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -75,30 +76,53 @@ protected: //- The number of field points for the current Piece label numberOfPoints_; - //- The number of field cells (faces) for the current Piece + //- The number of field cells (edges or faces) for the current Piece label numberOfCells_; //- Local number of points label nLocalPoints_; - //- Local number of faces - label nLocalFaces_; + //- Local number of lines (edges) + label nLocalLines_; - //- Local face vertices (connectivity) count. Sum of face sizes. - label nLocalVerts_; + //- Local number of polys (faces) + label nLocalPolys_; + // Protected Member Functions + + //- Write a uniform field of Cell (Poly or Line) or Point values + template<class Type> + void writeUniformValue + ( + const label nCellValues, // Could be Poly or Line! + const word& fieldName, + const Type& val + ); + private: // Private Member Functions - //- Determine sizes (nLocalPoints_, nLocalFaces_, nLocalVerts_), + //- Determine sizes (nLocalPoints_, nLocalLines_), + //- and begin piece + void beginPiece(const pointField& points, const edgeList& edges); + + //- Determine sizes (nLocalPoints_, nLocalPolys_), //- and begin piece void beginPiece(const pointField& points, const faceList& faces); //- Write points void writePoints(const pointField& points); + //- Write lines, legacy format + // \param pointOffset processor-local point offset + void writeLinesLegacy(const edgeList& edges, const label pointOffset); + + //- Write lines + // \param pointOffset processor-local point offset + void writeLines(const edgeList& edges, const label pointOffset); + //- Write faces, legacy format // \param pointOffset processor-local point offset void writePolysLegacy(const faceList& faces, const label pointOffset); @@ -163,6 +187,16 @@ public: // their own geomety, but use writePolyGeometry() directly virtual bool writeGeometry(); + //- Low-level write edge/point topology. + //- Normally used by writeGeometry() in a derived class + // Also writes the file header if not previously written. + // \note Must be called prior to writing CellData or PointData + bool writeLineGeometry + ( + const pointField& points, + const edgeList& edges + ); + //- Low-level write face/point topology. //- Normally used by writeGeometry() in a derived class // Also writes the file header if not previously written. @@ -194,11 +228,7 @@ public: // Write - //- Write a uniform field of Cell (Face) or Point values - template<class Type> - void writeUniform(const word& fieldName, const Type& val); - - //- Write a list of Cell (Face) or Point values + //- Write a list of Cell (Poly or Line) or Point values template<class Type> void write(const word& fieldName, const UList<Type>& field); }; diff --git a/src/fileFormats/vtk/write/foamVtkPolyWriterTemplates.C b/src/fileFormats/vtk/write/foamVtkPolyWriterTemplates.C index b0db6d1cfeca1d1f317ac77223193b4825b709c8..319836b4637f5ba8333e8551d5571378c09995b6 100644 --- a/src/fileFormats/vtk/write/foamVtkPolyWriterTemplates.C +++ b/src/fileFormats/vtk/write/foamVtkPolyWriterTemplates.C @@ -25,24 +25,27 @@ License \*---------------------------------------------------------------------------*/ -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // template<class Type> -void Foam::vtk::polyWriter::writeUniform +void Foam::vtk::polyWriter::writeUniformValue ( + const label nCellValues, const word& fieldName, const Type& val ) { + label nValues(0); + if (isState(outputState::CELL_DATA)) { ++nCellData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfCells_); + nValues = nCellValues; } else if (isState(outputState::POINT_DATA)) { ++nPointData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfPoints_); + nValues = nLocalPoints_; } else { @@ -54,10 +57,16 @@ void Foam::vtk::polyWriter::writeUniform ) << " for uniform field " << fieldName << nl << endl << exit(FatalError); + + return; } + + vtk::fileWriter::writeUniform<Type>(fieldName, val, nValues); } +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + template<class Type> void Foam::vtk::polyWriter::write ( @@ -66,18 +75,18 @@ void Foam::vtk::polyWriter::write ) { // Could check sizes: - // nValues == nLocalFaces (CELL_DATA) - // nValues == nLocalPoints (POINT_DATA) + // CELL_DATA: nValues == (nLocalPolys | nLocalLines) + // POINT_DATA: nValues == nLocalPoints + + // const label nValues = field.size(); if (isState(outputState::CELL_DATA)) { ++nCellData_; - vtk::fileWriter::writeBasicField<Type>(fieldName, field); } else if (isState(outputState::POINT_DATA)) { ++nPointData_; - vtk::fileWriter::writeBasicField<Type>(fieldName, field); } else { @@ -89,7 +98,10 @@ void Foam::vtk::polyWriter::write ) << " for field " << fieldName << nl << endl << exit(FatalError); + return; } + + vtk::fileWriter::writeBasicField<Type>(fieldName, field); } diff --git a/src/fileFormats/vtk/write/foamVtkSurfaceWriter.C b/src/fileFormats/vtk/write/foamVtkSurfaceWriter.C index 9ee9709d861b96fa41f529946cef542bdf946ca3..7b07cace360c8c01b6fbacb65fddb2a329351229 100644 --- a/src/fileFormats/vtk/write/foamVtkSurfaceWriter.C +++ b/src/fileFormats/vtk/write/foamVtkSurfaceWriter.C @@ -88,7 +88,7 @@ bool Foam::vtk::surfaceWriter::beginFile(std::string title) return vtk::fileWriter::beginFile(title); } - if (instant_.name().size()) + if (!instant_.name().empty()) { return vtk::fileWriter::beginFile ( @@ -129,4 +129,10 @@ void Foam::vtk::surfaceWriter::piece } +bool Foam::vtk::surfaceWriter::writeProcIDs() +{ + return vtk::fileWriter::writeProcIDs(nLocalPolys_); +} + + // ************************************************************************* // diff --git a/src/fileFormats/vtk/write/foamVtkSurfaceWriter.H b/src/fileFormats/vtk/write/foamVtkSurfaceWriter.H index 830ee6132d204efff2e42fa2ad1534d5fddf6325..0a9d2ccdf363d4538564fe74fd17deb66da19e16 100644 --- a/src/fileFormats/vtk/write/foamVtkSurfaceWriter.H +++ b/src/fileFormats/vtk/write/foamVtkSurfaceWriter.H @@ -42,7 +42,6 @@ Note SourceFiles foamVtkSurfaceWriter.C - foamVtkSurfaceWriterTemplates.C \*---------------------------------------------------------------------------*/ @@ -152,8 +151,20 @@ public: //- Write the currently set time as "TimeValue" FieldData void writeTimeValue(); - //- Reset point, face references to begin a new piece + //- Reset point/face references to begin a new piece void piece(const pointField& points, const faceList& faces); + + + //- Write processor ids for each poly as CellData + //- (no-op in serial) + bool writeProcIDs(); + + //- Write a uniform field of Cell (Poly) or Point values + template<class Type> + void writeUniform(const word& fieldName, const Type& val) + { + polyWriter::writeUniformValue<Type>(nLocalPolys_, fieldName, val); + } }; diff --git a/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.C b/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.C index 7f1951a97116c9f3b1ecbad337a68828e11985ca..782ce50b0ea708f75e8945672f6aff600439881a 100644 --- a/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.C +++ b/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2019 OpenCFD Ltd. + Copyright (C) 2019-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -28,50 +28,9 @@ License #include "VTKedgeFormat.H" #include "Fstream.H" -#include "clock.H" -#include "vtkUnstructuredReader.H" #include "Time.H" - -// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // - -void Foam::fileFormats::VTKedgeFormat::writeHeader -( - Ostream& os, - const pointField& pointLst -) -{ - // Write header - os << "# vtk DataFile Version 2.0" << nl - << "featureEdgeMesh written " << clock::dateTime().c_str() << nl - << "ASCII" << nl - << nl - << "DATASET POLYDATA" << nl; - - // Write vertex coords - os << "POINTS " << pointLst.size() << " double" << nl; - for (const point& pt : pointLst) - { - os << float(pt.x()) << ' ' - << float(pt.y()) << ' ' - << float(pt.z()) << nl; - } -} - - -void Foam::fileFormats::VTKedgeFormat::writeEdges -( - Ostream& os, - const UList<edge>& edgeLst -) -{ - os << "LINES " << edgeLst.size() << ' ' << 3*edgeLst.size() << nl; - - for (const edge& e : edgeLst) - { - os << "2 " << e[0] << ' ' << e[1] << nl; - } -} - +#include "foamVtkLineWriter.H" +#include "vtkUnstructuredReader.H" // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // @@ -122,19 +81,21 @@ bool Foam::fileFormats::VTKedgeFormat::read storedPoints().transfer(reader.points()); label nEdges = 0; - forAll(reader.lines(), lineI) + for (const auto& lineVerts : reader.lines()) { - nEdges += reader.lines()[lineI].size()-1; + if (lineVerts.size() > 1) + { + nEdges += (lineVerts.size()-1); + } } - storedEdges().setSize(nEdges); + storedEdges().resize(nEdges); nEdges = 0; - forAll(reader.lines(), lineI) + for (const auto& lineVerts : reader.lines()) { - const labelList& verts = reader.lines()[lineI]; - for (label i = 1; i < verts.size(); i++) + for (label i = 1; i < lineVerts.size(); ++i) { - storedEdges()[nEdges++] = edge(verts[i-1], verts[i]); + storedEdges()[nEdges++] = edge(lineVerts[i-1], lineVerts[i]); } } @@ -148,16 +109,20 @@ void Foam::fileFormats::VTKedgeFormat::write const edgeMesh& eMesh ) { - OFstream os(filename); - if (!os.good()) - { - FatalErrorInFunction - << "Cannot open file for writing " << filename - << exit(FatalError); - } + // NB: restrict output to legacy ascii so that we are still able + // to read it with vtkUnstructuredReader + + vtk::lineWriter writer + ( + eMesh.points(), + eMesh.edges(), + vtk::formatType::LEGACY_ASCII, + filename, + false // non-parallel write (edgeMesh already serialized) + ); - writeHeader(os, eMesh.points()); - writeEdges(os, eMesh.edges()); + writer.beginFile("OpenFOAM edgeMesh"); + writer.writeGeometry(); } diff --git a/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.H b/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.H index ac88e0bc8db5766e217fa34a10b70fd8f1bbfbaa..084828d639c373daf2e2b6288336e7cc4ee6afb1 100644 --- a/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.H +++ b/src/meshTools/edgeMesh/edgeMeshFormats/vtk/VTKedgeFormat.H @@ -54,37 +54,12 @@ class VTKedgeFormat : public edgeMesh { - // Private Member Functions - - //- No copy construct - VTKedgeFormat(const VTKedgeFormat&) = delete; - - //- No copy assignment - void operator=(const VTKedgeFormat&) = delete; - - -protected: - - // Protected Member Functions - - //- Write header information with points - static void writeHeader - ( - Ostream&, - const pointField& - ); - - //- Write edges - static void writeEdges(Ostream&, const UList<edge>&); - - public: - // Constructors - //- Construct from file name - VTKedgeFormat(const fileName&); + //- Read construct from file name + explicit VTKedgeFormat(const fileName& filename); // Selectors @@ -103,12 +78,12 @@ public: // Member Functions //- Write surface mesh components by proxy - static void write(const fileName&, const edgeMesh&); + static void write(const fileName& name, const edgeMesh&); //- Read from file - virtual bool read(const fileName&); + virtual bool read(const fileName& name); - //- Write object file + //- Write object to file virtual void write(const fileName& name) const { write(name, *this); diff --git a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.C b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.C index be2bf99fa4914ce9f2d66d141269455658699b0e..a5ee5abd525f71d960702c418a7f9700d47266cb 100644 --- a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.C +++ b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.C @@ -45,16 +45,24 @@ void Foam::vtk::internalMeshWriter::beginPiece() if (parallel_) { + if (debug > 1) + { + PoutInFunction + << ": nPoints=" << numberOfPoints_ + << " nCells=" << numberOfCells_ << nl; + } + reduce(numberOfPoints_, sumOp<label>()); reduce(numberOfCells_, sumOp<label>()); } + DebugInFunction + << "nPoints=" << numberOfPoints_ + << " nCells=" << numberOfCells_ << nl; + // Nothing else to do for legacy if (legacy()) return; - DebugInFunction - << "nPoints=" << numberOfPoints_ << " nCells=" << numberOfCells_ << nl; - if (format_) { format() @@ -178,7 +186,10 @@ void Foam::vtk::internalMeshWriter::writeCellsLegacy(const label pointOffset) } -void Foam::vtk::internalMeshWriter::writeCellsConnectivity(const label pointOffset) +void Foam::vtk::internalMeshWriter::writeCellsConnectivity +( + const label pointOffset +) { // // 'connectivity' @@ -316,7 +327,10 @@ void Foam::vtk::internalMeshWriter::writeCellsConnectivity(const label pointOffs } -void Foam::vtk::internalMeshWriter::writeCellsFaces(const label pointOffset) +void Foam::vtk::internalMeshWriter::writeCellsFaces +( + const label pointOffset +) { label nFaceLabels = vtuCells_.faceLabels().size(); @@ -628,39 +642,7 @@ bool Foam::vtk::internalMeshWriter::writeProcIDs() return false; } - if (isState(outputState::CELL_DATA)) - { - ++nCellData_; - } - else - { - reportBadState(FatalErrorInFunction, outputState::CELL_DATA) - << " for procID field" << nl << endl - << exit(FatalError); - } - - const globalIndex procMaps(vtuCells_.nFieldCells()); - - this->beginDataArray<label>("procID", procMaps.size()); - - bool good = false; - - if (Pstream::master()) - { - // Per-processor ids - for (const int proci : Pstream::allProcs()) - { - vtk::write(format(), label(proci), procMaps.localSize(proci)); - } - - good = true; - } - - this->endDataArray(); - - - // MPI barrier - return returnReduce(good, orOp<bool>()); + return vtk::fileWriter::writeProcIDs(vtuCells_.nFieldCells()); } diff --git a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.H b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.H index 17c69893038a8eeed7f4ba8b4776dab64d29bc18..00da9739a7a0ac919a4b7713ff563af005b5c479 100644 --- a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.H +++ b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriter.H @@ -225,6 +225,10 @@ public: //- Write primitive field of CellData template<class Type> void writeCellData(const word& fieldName, const UList<Type>& field); + + //- Write primitive field of PointData + template<class Type> + void writePointData(const word& fieldName, const UList<Type>& field); }; diff --git a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriterTemplates.C b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriterTemplates.C index 866117286a79e58e4a75199a1e56ddbc026d31f7..ecf419c903ec2399182e2c7b6e01699c73726087 100644 --- a/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriterTemplates.C +++ b/src/meshTools/output/vtk/mesh/foamVtkInternalMeshWriterTemplates.C @@ -37,15 +37,17 @@ void Foam::vtk::internalMeshWriter::writeUniform const Type& val ) { + label nValues(0); + if (isState(outputState::CELL_DATA)) { ++nCellData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfCells_); + nValues = vtuCells_.nFieldCells(); } else if (isState(outputState::POINT_DATA)) { ++nPointData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfPoints_); + nValues = vtuCells_.nFieldPoints(); } else { @@ -54,9 +56,14 @@ void Foam::vtk::internalMeshWriter::writeUniform FatalErrorInFunction, outputState::CELL_DATA, outputState::POINT_DATA - ) << " for field " << fieldName << nl << endl + ) + << " for uniform field " << fieldName << nl << endl << exit(FatalError); + + return; } + + vtk::fileWriter::writeUniform<Type>(fieldName, val, nValues); } @@ -95,4 +102,37 @@ void Foam::vtk::internalMeshWriter::writeCellData } +template<class Type> +void Foam::vtk::internalMeshWriter::writePointData +( + const word& fieldName, + const UList<Type>& field +) +{ + if (isState(outputState::POINT_DATA)) + { + ++nPointData_; + } + else + { + reportBadState(FatalErrorInFunction, outputState::POINT_DATA) + << " for field " << fieldName << nl << endl + << exit(FatalError); + } + + this->beginDataArray<Type>(fieldName, numberOfPoints_); + + if (parallel_) + { + vtk::writeListParallel(format_.ref(), field); + } + else + { + vtk::writeList(format(), field); + } + + this->endDataArray(); +} + + // ************************************************************************* // diff --git a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.C b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.C index acec853d6b372f66de476cd417ff68d0a61294ae..63f85246a6dce7af70749e0760c2b16eeab28479 100644 --- a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.C +++ b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.C @@ -38,14 +38,15 @@ void Foam::vtk::patchMeshWriter::beginPiece() // Basic sizes const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - nLocalPoints_ = nLocalFaces_ = nLocalVerts_ = 0; + nLocalPoints_ = nLocalPolys_ = 0; + nLocalVerts_ = 0; for (const label patchId : patchIDs_) { const polyPatch& pp = patches[patchId]; nLocalPoints_ += pp.nPoints(); - nLocalFaces_ += pp.size(); + nLocalPolys_ += pp.size(); for (const face& f : pp) { @@ -54,7 +55,7 @@ void Foam::vtk::patchMeshWriter::beginPiece() } numberOfPoints_ = nLocalPoints_; - numberOfCells_ = nLocalFaces_; + numberOfCells_ = nLocalPolys_; if (parallel_) { @@ -148,26 +149,26 @@ void Foam::vtk::patchMeshWriter::writePolysLegacy(const label pointOffset) // Connectivity count without additional storage (done internally) - label nFaces = nLocalFaces_; + label nPolys = nLocalPolys_; label nVerts = nLocalVerts_; if (parallel_) { - reduce(nFaces, sumOp<label>()); + reduce(nPolys, sumOp<label>()); reduce(nVerts, sumOp<label>()); } - if (nFaces != numberOfCells_) + if (nPolys != numberOfCells_) { FatalErrorInFunction << "Expecting " << numberOfCells_ - << " faces, but found " << nFaces + << " faces, but found " << nPolys << exit(FatalError); } - legacy::beginPolys(os_, nFaces, nVerts); + legacy::beginPolys(os_, nPolys, nVerts); - labelList vertLabels(nLocalFaces_ + nLocalVerts_); + labelList vertLabels(nLocalPolys_ + nLocalVerts_); { // Legacy: size + connectivity together @@ -186,9 +187,9 @@ void Foam::vtk::patchMeshWriter::writePolysLegacy(const label pointOffset) *iter = f.size(); // The size prefix ++iter; - for (const label pfi : f) + for (const label id : f) { - *iter = pfi + off; // Face vertex label + *iter = id + off; // Face vertex label ++iter; } } @@ -258,9 +259,9 @@ void Foam::vtk::patchMeshWriter::writePolys(const label pointOffset) for (const face& f : pp.localFaces()) { - for (const label pfi : f) + for (const label id : f) { - *iter = pfi + off; // Face vertex label + *iter = id + off; // Face vertex label ++iter; } } @@ -290,7 +291,7 @@ void Foam::vtk::patchMeshWriter::writePolys(const label pointOffset) // 'offsets' (connectivity offsets) // { - labelList vertOffsets(nLocalFaces_); + labelList vertOffsets(nLocalPolys_); label nOffs = vertOffsets.size(); if (parallel_) @@ -367,7 +368,7 @@ Foam::vtk::patchMeshWriter::patchMeshWriter numberOfPoints_(0), numberOfCells_(0), nLocalPoints_(0), - nLocalFaces_(0), + nLocalPolys_(0), nLocalVerts_(0), mesh_(mesh), @@ -512,15 +513,15 @@ void Foam::vtk::patchMeshWriter::writePatchIDs() const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - label nFaces = nLocalFaces_; + label nPolys = nLocalPolys_; if (parallel_) { - reduce(nFaces, sumOp<label>()); + reduce(nPolys, sumOp<label>()); } - this->beginDataArray<label>("patchID", nFaces); + this->beginDataArray<label>("patchID", nPolys); if (parallel_ ? Pstream::master() : true) { @@ -584,66 +585,7 @@ void Foam::vtk::patchMeshWriter::writePatchIDs() bool Foam::vtk::patchMeshWriter::writeProcIDs() { - // This is different than for internalWriter. - // Here we allow procIDs whenever running in parallel, even if the - // output is serial. This allow diagnosis of processor patches. - - if (!Pstream::parRun()) - { - // Skip in non-parallel - return false; - } - - if (isState(outputState::CELL_DATA)) - { - ++nCellData_; - } - else - { - reportBadState(FatalErrorInFunction, outputState::CELL_DATA) - << " for patchID field" << nl << endl - << exit(FatalError); - } - - label nFaces = nLocalFaces_; - - if (parallel_) - { - reduce(nFaces, sumOp<label>()); - } - - - this->beginDataArray<label>("procID", nFaces); - - bool good = false; - - if (parallel_) - { - globalIndex procSizes(nLocalFaces_); - - if (Pstream::master()) - { - // Per-processor ids - for (const int proci : Pstream::allProcs()) - { - vtk::write(format(), label(proci), procSizes.localSize(proci)); - } - - good = true; - } - } - else - { - vtk::write(format(), label(Pstream::myProcNo()), nLocalFaces_); - - good = true; - } - - - this->endDataArray(); - - // MPI barrier - return parallel_ ? returnReduce(good, orOp<bool>()) : good; + return vtk::fileWriter::writeProcIDs(nLocalPolys_); } @@ -668,15 +610,15 @@ bool Foam::vtk::patchMeshWriter::writeNeighIDs() const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - label nFaces = nLocalFaces_; + label nPolys = nLocalPolys_; if (parallel_) { - reduce(nFaces, sumOp<label>()); + reduce(nPolys, sumOp<label>()); } - this->beginDataArray<label>("neighID", nFaces); + this->beginDataArray<label>("neighID", nPolys); bool good = false; diff --git a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.H b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.H index 2a26482be14406ac8590a8cc11b0411a4079c2c7..0622d66d802f5d33c92b565f5a8dd7c4cf3950d6 100644 --- a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.H +++ b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriter.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2019 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -82,8 +82,8 @@ protected: //- Local number of points label nLocalPoints_; - //- Local number of faces - label nLocalFaces_; + //- Local number of polys (faces) + label nLocalPolys_; //- Local face vertices (connectivity) count. Sum of face sizes. label nLocalVerts_; @@ -97,7 +97,7 @@ protected: // Private Member Functions - //- Determine sizes (nLocalPoints_, nLocalFaces_, nLocalVerts_), + //- Determine sizes (nLocalPoints_, nLocalPolys_), //- and begin piece. void beginPiece(); @@ -172,7 +172,7 @@ public: } //- The patch IDs - inline const labelList& patchIDs() const + const labelList& patchIDs() const noexcept { return patchIDs_; } diff --git a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriterTemplates.C b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriterTemplates.C index ef81dcb7822c7ebff5d97ae4bf8f139c69863028..349a6b28fcda5a6eaaae710bdbe215b8a2548d7b 100644 --- a/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriterTemplates.C +++ b/src/meshTools/output/vtk/patch/foamVtkPatchMeshWriterTemplates.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2016-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -37,23 +37,33 @@ void Foam::vtk::patchMeshWriter::writeUniform const Type& val ) { + label nValues(0); + if (isState(outputState::CELL_DATA)) { ++nCellData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfCells_); + nValues = nLocalPolys_; } else if (isState(outputState::POINT_DATA)) { ++nPointData_; - vtk::fileWriter::writeUniform<Type>(fieldName, val, numberOfPoints_); + nValues = nLocalPoints_; } else { - WarningInFunction - << "Ignore bad writer state (" << stateNames[state_] - << ") for field " << fieldName << nl << endl + reportBadState + ( + FatalErrorInFunction, + outputState::CELL_DATA, + outputState::POINT_DATA + ) + << " for uniform field " << fieldName << nl << endl << exit(FatalError); + + return; } + + vtk::fileWriter::writeUniform<Type>(fieldName, val, nValues); } diff --git a/src/sampling/surface/isoSurface/isoSurfaceBase.H b/src/sampling/surface/isoSurface/isoSurfaceBase.H index 3e29657bd111202d18d4016a15835396a8515874..a856f06f263e1b763d1a9298e6e423226f2e4ca1 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceBase.H +++ b/src/sampling/surface/isoSurface/isoSurfaceBase.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2019-2020 OpenCFD Ltd. + Copyright (C) 2019-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -127,26 +127,6 @@ protected: // Protected Member Functions - //- Check for tet values above/below given (iso) value - // Result encoded as a single integer - inline static constexpr int getTetCutIndex - ( - const scalar a, - const scalar b, - const scalar c, - const scalar d, - const scalar isoval - ) noexcept - { - return - ( - (a < isoval ? 1 : 0) - | (b < isoval ? 2 : 0) - | (c < isoval ? 4 : 0) - | (d < isoval ? 8 : 0) - ); - } - //- Count the number of cuts matching the mask type // Checks as bitmask or as zero. static label countCutType diff --git a/src/sampling/surface/isoSurface/isoSurfaceParams.C b/src/sampling/surface/isoSurface/isoSurfaceParams.C index cf9494ed5d39d9167e93134ae7c1f568e9923a75..60b003dcb414d76d88f510361c84b945bd76a76e 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceParams.C +++ b/src/sampling/surface/isoSurface/isoSurfaceParams.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -52,10 +52,12 @@ const Foam::Enum Foam::isoSurfaceParams::filterNames ({ { filterType::NONE, "none" }, - { filterType::CELL, "cell" }, - { filterType::DIAGCELL, "diagcell" }, { filterType::PARTIAL, "partial" }, { filterType::FULL, "full" }, + { filterType::CLEAN, "clean" }, + + { filterType::CELL, "cell" }, + { filterType::DIAGCELL, "diagcell" }, }); @@ -137,6 +139,7 @@ Foam::isoSurfaceParams::isoSurfaceParams : algo_(algo), filter_(filter), + snap_(true), mergeTol_(1e-6), clipBounds_(boundBox::invertedBox) {} @@ -152,6 +155,7 @@ Foam::isoSurfaceParams::isoSurfaceParams { algo_ = getAlgorithmType(dict, algo_); filter_ = getFilterType(dict, filter_); + snap_ = dict.getOrDefault("snap", true); dict.readIfPresent("mergeTol", mergeTol_); dict.readIfPresent("bounds", clipBounds_); } diff --git a/src/sampling/surface/isoSurface/isoSurfaceParams.H b/src/sampling/surface/isoSurface/isoSurfaceParams.H index 1b794f70595cbcb5efbdb934f996d73bf0687d1b..d73da00c2749d6eb30625bafcd76d64a4af7e265 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceParams.H +++ b/src/sampling/surface/isoSurface/isoSurfaceParams.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -35,12 +35,22 @@ Description isoMethod | Algorithm (cell/topo/point/default) | no | default regularise | Face simplification (enum or bool) | no | true mergeTol | Point merge tolerance (cell/point) | no | 1e-6 + snap | Point snapping (topo) | no | true bounds | Optional clip bounds | no | inverted \endtable The default algorithm denotes the use of the current \em standard algorithm. +Filtering types (for topological iso-surface) + - \c none : leave tet cuts untouched + - \c partial , \c cell : Combine intra-cell faces + - \c full , \c diagcell : Perform \c partial and remove face-diagonal + points + - \c clean : Perform \c full and eliminate open edges as well. + (<b>May cause excessive erosion!</b>) + . + SourceFiles isoSurfaceParams.C @@ -85,8 +95,12 @@ public: NONE = 0, //!< No filtering CELL, //!< Remove pyramid edge points DIAGCELL, //!< Remove pyramid edge points, face-diagonals + NONMANIFOLD, //!< Remove pyramid edge points, face-diagonals + //!< and non-manifold faces + PARTIAL = CELL, //!< Same as CELL FULL = DIAGCELL, //!< Same as DIAGCELL + CLEAN = NONMANIFOLD //!< Same as NONMANIFOLD }; @@ -100,6 +114,9 @@ private: //- Filtering for iso-surface faces/points filterType filter_; + //- Point snapping enabled + bool snap_; + //- Merge tolerance for cell/point (default: 1e-6) scalar mergeTol_; @@ -186,6 +203,18 @@ public: filter_ = fltr; } + //- Get point snapping flag + bool snap() const noexcept + { + return snap_; + } + + //- Set point snapping flag + void snap(bool on) noexcept + { + snap_ = on; + } + //- Get current merge tolerance scalar mergeTol() const noexcept { diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.C b/src/sampling/surface/isoSurface/isoSurfaceTopo.C index e8ff86266f7fb65c87ce560f6fd9317f3bfe43d3..c0fe1a80775c351fa8724b5c654e88b9c8b43830 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.C @@ -29,13 +29,16 @@ License #include "isoSurfaceTopo.H" #include "polyMesh.H" #include "volFields.H" +#include "edgeHashes.H" #include "tetCell.H" -#include "tetMatcher.H" #include "tetPointRef.H" #include "DynamicField.H" #include "syncTools.H" #include "uindirectPrimitivePatch.H" #include "polyMeshTetDecomposition.H" +#include "foamVtkInternalMeshWriter.H" +#include "foamVtkLineWriter.H" +#include "foamVtkSurfaceWriter.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -51,452 +54,624 @@ namespace Foam } -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // +// Get/set snapIndex (0, 1 or 2) at given position +// 0 = no snap +// 1 = snap to first edge end +// 2 = snap to second edge end +// NB: 4 lower bits left free for regular tet-cut information + +#undef SNAP_END_VALUE +#undef SNAP_END_ENCODE +#define SNAP_END_ENCODE(pos, val) (((val) << (4 + 2 * pos))) +#define SNAP_END_VALUE(pos, val) (((val) >> (4 + 2 * pos)) & 0x3) + -Foam::label Foam::isoSurfaceTopo::generatePoint +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Check for tet values above/below given (iso) value +// Result encoded as an integer, with possible snapping information too +inline static int getTetCutIndex ( - const label facei, - const bool edgeIsDiag, - const edge& vertices, + scalar p0, + scalar p1, + scalar p2, + scalar p3, + const scalar val, + const bool doSnap +) noexcept +{ + int cutIndex + ( + (p0 < val ? 1 : 0) // point 0 + | (p1 < val ? 2 : 0) // point 1 + | (p2 < val ? 4 : 0) // point 2 + | (p3 < val ? 8 : 0) // point 3 + ); - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - EdgeMap<label>& vertsToPoint -) const + if (doSnap && cutIndex && cutIndex != 0xF) + { + // Not all below or all + + // Calculate distances (for snapping) + p0 -= val; if (cutIndex & 1) p0 *= -1; + p1 -= val; if (cutIndex & 2) p1 *= -1; + p2 -= val; if (cutIndex & 4) p2 *= -1; + p3 -= val; if (cutIndex & 8) p3 *= -1; + + // Add snap index into regular edge cut index + // Snap to end if less than approx 1% of the distance. + // - only valid if there is also a corresponding sign change + #undef ADD_SNAP_INDEX + #define ADD_SNAP_INDEX(pos, d1, d2, idx1, idx2) \ + switch (cutIndex & (idx1 | idx2)) \ + { \ + case idx1 : /* first below, second above */ \ + case idx2 : /* first above, second below */ \ + cutIndex |= SNAP_END_ENCODE \ + ( \ + pos, \ + ((d1 * 100 < d2) ? 1 : (d2 * 100 < d1) ? 2 : 0) \ + ); \ + break; \ + } + + ADD_SNAP_INDEX(0, p0, p1, 1, 2); // Edge 0: 0 -> 1 + ADD_SNAP_INDEX(1, p0, p2, 1, 4); // Edge 1: 0 -> 2 + ADD_SNAP_INDEX(2, p0, p3, 1, 8); // Edge 2: 0 -> 3 + ADD_SNAP_INDEX(3, p3, p1, 8, 2); // Edge 3: 3 -> 1 + ADD_SNAP_INDEX(4, p1, p2, 2, 4); // Edge 4: 1 -> 2 + ADD_SNAP_INDEX(5, p3, p2, 8, 4); // Edge 5: 3 -> 2 + #undef ADD_SNAP_INDEX + } + + return cutIndex; +} + + +// Append three labels to list. +// Filter out degenerate (eg, snapped) tris. Flip face as requested +inline static void appendTriLabels +( + DynamicList<label>& verts, + const label a, + const label b, + const label c, + const bool flip // Flip normals +) +{ + if (a != b && b != c && c != a) + { + verts.append(a); + if (flip) + { + verts.append(c); + verts.append(b); + } + else + { + verts.append(b); + verts.append(c); + } + } +} + + +// Return point reference to mesh points or cell-centres +inline static const point& getMeshPointRef +( + const polyMesh& mesh, + const label pointi +) +{ + return + ( + pointi < mesh.nPoints() + ? mesh.points()[pointi] + : mesh.cellCentres()[pointi - mesh.nPoints()] + ); +} + +} // End namespace Foam + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::isoSurfaceTopo::tetCutAddressing::tetCutAddressing +( + const label nCutCells, + const bool useSnap, + const bool useDebugCuts +) +: + vertsToPointLookup_(12*nCutCells), + snapVertsLookup_(0), + + pointToFace_(10*nCutCells), + pointFromDiag_(10*nCutCells), + + pointToVerts_(10*nCutCells), + cutPoints_(12*nCutCells), + + debugCutTets_(), + debugCutTetsOn_(useDebugCuts) +{ + // Per cell: 5 pyramids cut, each generating 2 triangles + + // Per cell: number of intersected edges: + // - four faces cut so 4 mesh edges + 4 face-diagonal edges + // - 4 of the pyramid edges + + if (useSnap) + { + // Some, but not all, cells may have point snapping + snapVertsLookup_.resize(4*nCutCells); + } + if (debugCutTetsOn_) + { + debugCutTets_.reserve(6*nCutCells); + } +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::isoSurfaceTopo::tetCutAddressing::clearDebug() +{ + debugCutTets_.clearStorage(); +} + + +void Foam::isoSurfaceTopo::tetCutAddressing::clearDiagonal() +{ + pointToFace_.clearStorage(); + pointFromDiag_.clearStorage(); +} + + +void Foam::isoSurfaceTopo::tetCutAddressing::clearHashes() +{ + vertsToPointLookup_.clear(); + snapVertsLookup_.clear(); +} + + +Foam::label Foam::isoSurfaceTopo::tetCutAddressing::generatePoint +( + label facei, + bool edgeIsDiagonal, + const int snapEnd, + const edge& vertices +) { // Generate new point, unless it already exists for edge + // or corresponds to a snapped point (from another edge) - label pointi = vertsToPoint.lookup(vertices, -1); + label pointi = vertsToPointLookup_.lookup(vertices, -1); if (pointi == -1) { - pointi = pointToVerts.size(); + bool addNewPoint(true); + + const label snapPointi = + ( + (snapEnd == 1) ? vertices.first() + : (snapEnd == 2) ? vertices.second() + : -1 + ); + + if (snapPointi == -1) + { + // No snapped point + pointi = pointToVerts_.size(); + pointToVerts_.append(vertices); + } + else + { + // Snapped point. No corresponding face or diagonal + facei = -1; + edgeIsDiagonal = false; + + pointi = snapVertsLookup_.lookup(snapPointi, -1); + addNewPoint = (pointi == -1); + if (addNewPoint) + { + pointi = pointToVerts_.size(); + snapVertsLookup_.insert(snapPointi, pointi); + pointToVerts_.append(edge(snapPointi, snapPointi)); + } + } - pointToVerts.append(vertices); - pointToFace.append(facei); - pointFromDiag.append(edgeIsDiag); - vertsToPoint.insert(vertices, pointi); + if (addNewPoint) + { + pointToFace_.append(facei); + pointFromDiag_.append(edgeIsDiagonal); + } + + vertsToPointLookup_.insert(vertices, pointi); } return pointi; } -void Foam::isoSurfaceTopo::generateTriPoints +bool Foam::isoSurfaceTopo::tetCutAddressing::generatePoints ( const label facei, const int tetCutIndex, const tetCell& tetLabels, - const FixedList<bool, 6>& edgeIsDiag,// Per tet edge whether is face diag - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts // Every three verts is new triangle -) const + // Per tet edge whether is face diag etc + const FixedList<bool, 6>& edgeIsDiagonal +) { + bool flip(false); + const label nCutPointsOld(cutPoints_.size()); + // Form the vertices of the triangles for each case - switch (tetCutIndex) + switch (tetCutIndex & 0x0F) { case 0x00: case 0x0F: break; - case 0x01: - case 0x0E: + // Cut point 0 + case 0x0E: flip = true; [[fallthrough]]; // Point 0 above cut + case 0x01: // Point 0 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - if (tetCutIndex == 0x0E) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x02: - case 0x0D: + // Cut point 1 + case 0x0D: flip = true; [[fallthrough]]; // Point 1 above cut + case 0x02: // Point 1 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.reverseEdge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - if (tetCutIndex == 0x0D) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x03: - case 0x0C: + // Cut point 0/1 | 2/3 + case 0x0C: flip = true; [[fallthrough]]; // Point 0/1 above cut + case 0x03: // Point 0/1 below cut { - const label p0p2 + const label cutA ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - const label p1p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - verts.append(p1p3); - verts.append(p0p2); - verts.append(p1p3); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - verts.append(p0p2); - if (tetCutIndex == 0x0C) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x04: - case 0x0B: + // Cut point 2 + case 0x0B: flip = true; [[fallthrough]]; // Point 2 above cut + case 0x04: // Point 2 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.reverseEdge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.reverseEdge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - if (tetCutIndex == 0x0B) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; - case 0x05: - case 0x0A: + // Cut point 0/2 | 1/3 + case 0x0A: flip = true; [[fallthrough]]; // Point 0/2 above cut + case 0x05: // Point 0/2 below cut { - const label p0p1 + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - const label p2p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[4], + SNAP_END_VALUE(4, tetCutIndex), + tetLabels.edge(4) // 1 -> 2 ) ); - - verts.append(p0p1); - verts.append(p2p3); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.edge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append(p0p1); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[4], - tetLabels.edge(4), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - verts.append(p2p3); - if (tetCutIndex == 0x0A) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x06: - case 0x09: + // Cut point 1/2 | 0/3 + case 0x09: flip = true; [[fallthrough]]; // Point 1/2 above cut + case 0x06: // Point 1/2 below cut { - const label p0p1 + const label cutA ( generatePoint ( facei, - edgeIsDiag[0], - tetLabels.edge(0), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[0], + SNAP_END_VALUE(0, tetCutIndex), + tetLabels.edge(0) // 0 -> 1 ) ); - const label p2p3 + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - - verts.append(p0p1); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.reverseEdge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append(p2p3); - verts.append(p0p1); - verts.append(p2p3); - verts.append + const label cutD ( generatePoint ( facei, - edgeIsDiag[1], - tetLabels.edge(1), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[1], + SNAP_END_VALUE(1, tetCutIndex), + tetLabels.edge(1) // 0 -> 2 ) ); - if (tetCutIndex == 0x09) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-5], verts[sz-4]); - std::swap(verts[sz-2], verts[sz-1]); - } + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); + appendTriLabels(cutPoints_, cutA, cutC, cutD, flip); } break; - case 0x08: - case 0x07: + // Cut point 3 + case 0x07: flip = true; [[fallthrough]]; // Point 3 above cut + case 0x08: // Point 3 below cut { - verts.append + const label cutA ( generatePoint ( facei, - edgeIsDiag[2], - tetLabels.reverseEdge(2), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[2], + SNAP_END_VALUE(2, tetCutIndex), + tetLabels.edge(2) // 0 -> 3 ) ); - verts.append + const label cutB ( generatePoint ( facei, - edgeIsDiag[5], - tetLabels.reverseEdge(5), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[5], + SNAP_END_VALUE(5, tetCutIndex), + tetLabels.edge(5) // 3 -> 2 ) ); - verts.append + const label cutC ( generatePoint ( facei, - edgeIsDiag[3], - tetLabels.edge(3), - pointToVerts, pointToFace, pointFromDiag, vertsToPoint + edgeIsDiagonal[3], + SNAP_END_VALUE(3, tetCutIndex), + tetLabels.edge(3) // 3 -> 1 ) ); - if (tetCutIndex == 0x07) - { - // Flip normals - const label sz = verts.size(); - std::swap(verts[sz-2], verts[sz-1]); - } + + appendTriLabels(cutPoints_, cutA, cutB, cutC, flip); } break; } + + const bool added(nCutPointsOld != cutPoints_.size()); + + if (added && debugCutTetsOn_) + { + debugCutTets_.append(tetLabels.shape()); + } + + return added; } +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +// Requires mesh_, tetBasePtIs void Foam::isoSurfaceTopo::generateTriPoints ( const label celli, const bool isTet, - - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts, - DynamicList<label>& faceLabels + const labelList& tetBasePtIs, + tetCutAddressing& tetCutAddr ) const { const faceList& faces = mesh_.faces(); const labelList& faceOwner = mesh_.faceOwner(); const cell& cFaces = mesh_.cells()[celli]; + const bool doSnap = this->snap(); if (isTet) { // For tets don't do cell-centre decomposition, just use the // tet points and values - const label startTrii = verts.size(); - const label facei = cFaces[0]; const face& f0 = faces[facei]; // Get the other point from f1. Tbd: check if not duplicate face // (ACMI / ignoreBoundaryFaces_). const face& f1 = faces[cFaces[1]]; - label oppositeI = -1; + label apexi = -1; forAll(f1, fp) { - oppositeI = f1[fp]; - if (!f0.found(oppositeI)) + apexi = f1[fp]; + if (!f0.found(apexi)) { break; } } - label p0 = f0[0]; + const label p0 = f0[0]; label p1 = f0[1]; label p2 = f0[2]; @@ -505,31 +680,27 @@ void Foam::isoSurfaceTopo::generateTriPoints std::swap(p1, p2); } - generateTriPoints + const tetCell tetLabels(p0, p1, p2, apexi); + const int tetCutIndex ( - facei, getTetCutIndex ( pVals_[p0], pVals_[p1], pVals_[p2], - pVals_[oppositeI], - iso_ - ), - tetCell(p0, p1, p2, oppositeI), - FixedList<bool, 6>(false), // edgeIsDiag = false - - pointToVerts, - pointToFace, - pointFromDiag, - vertsToPoint, - verts // Every three verts is new triangle + pVals_[apexi], + iso_, + doSnap + ) ); - for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) - { - faceLabels.append(facei); - } + tetCutAddr.generatePoints + ( + facei, + tetCutIndex, + tetLabels, + FixedList<bool, 6>(false) // Not face diagonal + ); } else { @@ -544,11 +715,9 @@ void Foam::isoSurfaceTopo::generateTriPoints continue; } - const label startTrii = verts.size(); - const face& f = faces[facei]; - label fp0 = tetBasePtIs_[facei]; + label fp0 = tetBasePtIs[facei]; // Fallback if (fp0 < 0) @@ -556,55 +725,48 @@ void Foam::isoSurfaceTopo::generateTriPoints fp0 = 0; } + const label p0 = f[fp0]; label fp = f.fcIndex(fp0); for (label i = 2; i < f.size(); ++i) { - const label nextFp = f.fcIndex(fp); - - FixedList<bool, 6> edgeIsDiag(false); - - label p0 = f[fp0]; label p1 = f[fp]; - label p2 = f[nextFp]; + fp = f.fcIndex(fp); + label p2 = f[fp]; + + FixedList<bool, 6> edgeIsDiagonal(false); if (faceOwner[facei] == celli) { std::swap(p1, p2); - if (i != 2) edgeIsDiag[1] = true; - if (i != f.size()-1) edgeIsDiag[0] = true; + if (i != 2) edgeIsDiagonal[1] = true; + if (i != f.size()-1) edgeIsDiagonal[0] = true; } else { - if (i != 2) edgeIsDiag[0] = true; - if (i != f.size()-1) edgeIsDiag[1] = true; + if (i != 2) edgeIsDiagonal[0] = true; + if (i != f.size()-1) edgeIsDiagonal[1] = true; } - generateTriPoints + const tetCell tetLabels(p0, p1, p2, mesh_.nPoints()+celli); + const int tetCutIndex ( - facei, getTetCutIndex ( pVals_[p0], pVals_[p1], pVals_[p2], cVals_[celli], - iso_ - ), - tetCell(p0, p1, p2, mesh_.nPoints()+celli), - edgeIsDiag, - - pointToVerts, - pointToFace, - pointFromDiag, - vertsToPoint, - verts // Every three verts is new triangle + iso_, + doSnap + ) ); - fp = nextFp; - } - - for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) - { - faceLabels.append(facei); + tetCutAddr.generatePoints + ( + facei, + tetCutIndex, + tetLabels, + edgeIsDiagonal + ); } } } @@ -708,11 +870,11 @@ void Foam::isoSurfaceTopo::removeInsidePoints const pointField& points = s.points(); - DynamicList<face> compactFaces(s.size()/8); + DynamicList<face> compactFaces(s.size()/4); for (label celli = 0; celli < start.size()-1; ++celli) { - // All triangles for the current cell + // Triangles for the current cell const label nTris = start[celli+1]-start[celli]; @@ -730,7 +892,6 @@ void Foam::isoSurfaceTopo::removeInsidePoints pp, pointFromDiag, pointToFace, - //protectedFace, celli, compactFaces, @@ -791,9 +952,11 @@ Foam::isoSurfaceTopo::isoSurfaceTopo const bitSet& ignoreCells ) : - isoSurfaceBase(mesh, cellValues, pointValues, iso, params), - cellCutType_(mesh.nCells(), cutType::UNVISITED) + isoSurfaceBase(mesh, cellValues, pointValues, iso, params) { + // The cell cut type + List<cutType> cellCutType_(mesh.nCells(), cutType::UNVISITED); + if (debug) { Pout<< "isoSurfaceTopo:" << nl @@ -819,9 +982,11 @@ Foam::isoSurfaceTopo::isoSurfaceTopo nBlockedCells += blockCells(cellCutType_, params.getClipBounds(), volumeType::OUTSIDE); - - // Adjust tet base points to improve tet quality - tetBasePtIs_ = polyMeshTetDecomposition::adjustTetBasePtIs(mesh_, debug); + // Adjusted tet base points to improve tet quality + labelList tetBasePtIs + ( + polyMeshTetDecomposition::adjustTetBasePtIs(mesh_, debug) + ); // Determine cell cuts @@ -851,7 +1016,7 @@ Foam::isoSurfaceTopo::isoSurfaceTopo false ), fvmesh, - dimensionedScalar(dimless, Zero) + dimensionedScalar(dimless) ); auto& debugFld = debugField.primitiveFieldRef(); @@ -861,68 +1026,133 @@ Foam::isoSurfaceTopo::isoSurfaceTopo debugFld[celli] = cellCutType_[celli]; } - Pout<< "Writing cut types:" - << debugField.objectPath() << endl; - + Info<< "Writing cut types: " << debugField.objectRelPath() << nl; debugField.write(); } + // Additional debugging + if (debug & 8) + { + const word timeDesc + ( + word::printf("%08d", mesh_.time().timeIndex()) + ); - // Per cell: 5 pyramids cut, each generating 2 triangles - // - pointToVerts : from generated iso point to originating mesh verts - DynamicList<edge> pointToVerts(10*nCutCells); - // - pointToFace : from generated iso point to originating mesh face - DynamicList<label> pointToFace(10*nCutCells); - // - pointFromDiag : if generated iso point is on face diagonal - DynamicList<bool> pointFromDiag(10*nCutCells); + // Write debug cuts cells in VTK format + { + constexpr uint8_t realCut(cutType::CUT | cutType::TETCUT); + labelList debugCutCells(nCutCells, Zero); - // Per cell: number of intersected edges: - // - four faces cut so 4 mesh edges + 4 face-diagonal edges - // - 4 of the pyramid edges - EdgeMap<label> vertsToPoint(12*nCutCells); - DynamicList<label> verts(12*nCutCells); - // Per cell: 5 pyramids cut (since only one pyramid not cut) - DynamicList<label> faceLabels(5*nCutCells); - DynamicList<label> cellLabels(5*nCutCells); + label nout = 0; + forAll(cellCutType_, celli) + { + if ((cellCutType_[celli] & realCut) != 0) + { + debugCutCells[nout] = celli; + ++nout; + if (nout >= nCutCells) break; + } + } + // The mesh subset cut + vtk::vtuCells vtuCells; + vtuCells.reset(mesh_, debugCutCells); - labelList startTri(mesh_.nCells()+1, Zero); + vtk::internalMeshWriter writer + ( + mesh_, + vtuCells, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-cutCells") + ) + ); + + writer.writeGeometry(); + + // Cell qualities + writer.beginCellData(); + writer.writeProcIDs(); + writer.writeCellData("cutField", cVals_); + // Point qualities + writer.beginPointData(); + writer.writePointData("cutField", pVals_); + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(nCutCells, sumOp<label>()) + << " cut cells: " + << writer.output().name() << nl; + } + } + + + tetCutAddressing tetCutAddr + ( + nCutCells, + this->snap(), + (debug & 8) // Enable debug tets + ); + + labelList startTri(mesh_.nCells()+1, Zero); for (label celli = 0; celli < mesh_.nCells(); ++celli) { - startTri[celli] = faceLabels.size(); + startTri[celli] = tetCutAddr.nFaces(); if ((cellCutType_[celli] & cutType::ANYCUT) != 0) { generateTriPoints ( celli, - // Same as tetMatcher::test(mesh_, celli), - bool(cellCutType_[celli] & cutType::TETCUT), // isTet - - pointToVerts, - pointToFace, - pointFromDiag, + bool(cellCutType_[celli] & cutType::TETCUT), - vertsToPoint, - verts, - faceLabels + tetBasePtIs, + tetCutAddr ); + } + } + startTri.last() = tetCutAddr.nFaces(); - for (label i = startTri[celli]; i < faceLabels.size(); ++i) - { - cellLabels.append(celli); - } + // Information not needed anymore: + tetBasePtIs.clear(); + tetCutAddr.clearHashes(); + + + // From list of vertices -> triangular faces + faceList allTriFaces(startTri.last()); + { + auto& verts = tetCutAddr.cutPoints(); + + label verti = 0; + for (face& f : allTriFaces) + { + f.resize(3); + f[0] = verts[verti++]; + f[1] = verts[verti++]; + f[2] = verts[verti++]; } + verts.clearStorage(); // Not needed anymore } - startTri[mesh_.nCells()] = faceLabels.size(); - pointToVerts_.transfer(pointToVerts); - meshCells_.transfer(cellLabels); - pointToFace_.transfer(pointToFace); + // The cells cut by the triangular faces + meshCells_.resize(startTri.last()); + for (label celli = 0; celli < startTri.size()-1; ++celli) + { + // All triangles for the current cell + labelList::subList + ( + meshCells_, + (startTri[celli+1] - startTri[celli]), + startTri[celli] + ) = celli; + } + + + pointToVerts_.transfer(tetCutAddr.pointToVerts()); - pointField allPoints + pointField allTriPoints ( this->interpolateTemplate ( @@ -933,37 +1163,16 @@ Foam::isoSurfaceTopo::isoSurfaceTopo // Assign to MeshedSurface - faceList allTris(faceLabels.size()); - label verti = 0; - for (face& allTri : allTris) - { - allTri.resize(3); - allTri[0] = verts[verti++]; - allTri[1] = verts[verti++]; - allTri[2] = verts[verti++]; - } - - - surfZoneList allZones(one{}, surfZone("allFaces", allTris.size())); Mesh::clear(); Mesh updated ( - std::move(allPoints), - std::move(allTris), - std::move(allZones) + std::move(allTriPoints), + std::move(allTriFaces), + surfZoneList(one{}, surfZone("allFaces", allTriFaces.size())) ); Mesh::transfer(updated); - // Now: - // - generated faces and points are assigned to *this - // - per point we know: - // - pointOnDiag: whether it is on a face-diagonal edge - // - pointToFace_: from what pyramid (cell+face) it was produced - // (note that the pyramid faces are shared between multiple mesh faces) - // - pointToVerts_ : originating mesh vertex or cell centre - - if (debug) { Pout<< "isoSurfaceTopo : generated " @@ -972,6 +1181,16 @@ Foam::isoSurfaceTopo::isoSurfaceTopo } + // Now: + // - generated faces and points are assigned to *this + // - per point we know: + // - pointOnDiag: whether it is on a face-diagonal edge + // - pointToFace: from what pyramid (cell+face) it was produced + // (note that the pyramid faces are shared between multiple mesh faces) + // - pointToVerts_ : originating mesh vertex or cell centre + + // Basic filtering + if (params.filter() != filterType::NONE) { // Triangulate outside (filter edges to cell centres and optionally @@ -982,22 +1201,21 @@ Foam::isoSurfaceTopo::isoSurfaceTopo removeInsidePoints ( *this, - (params.filter() == filterType::DIAGCELL ? true : false), - - pointFromDiag, - pointToFace_, + // Filter face diagonal + ( + params.filter() == filterType::DIAGCELL + || params.filter() == filterType::NONMANIFOLD + ), + tetCutAddr.pointFromDiag(), + tetCutAddr.pointToFace(), startTri, pointCompactMap, compactCellIDs ); pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointCompactMap)(); - pointToFace_ = UIndirectList<label>(pointToFace_, pointCompactMap)(); meshCells_.transfer(compactCellIDs); - pointCompactMap.clearStorage(); - compactCellIDs.clearStorage(); - if (debug) { Pout<< "isoSurfaceTopo :" @@ -1008,33 +1226,35 @@ Foam::isoSurfaceTopo::isoSurfaceTopo } } - // Not required after this stage - pointFromDiag.clearStorage(); + // Diagonal filter information not needed anymore + tetCutAddr.clearDiagonal(); + + // For more advanced filtering (eg, removal of open edges) + // need the boundary and other 'protected' points - if (params.filter() == filterType::DIAGCELL) + bitSet isProtectedPoint; + if + ( + (params.filter() == filterType::NONMANIFOLD) + || tetCutAddr.debugCutTetsOn() + ) { - // We remove verts on face diagonals. This is in fact just - // straightening the edges of the face through the cell. This can - // close off 'pockets' of triangles and create open or - // multiply-connected triangles + // Mark points on mesh outside as 'protected' + // - never erode these edges - // Solved by eroding open-edges - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + isProtectedPoint.resize(mesh_.nPoints()); - // Mark points on mesh outside. - bitSet isBoundaryPoint(mesh.nPoints()); for ( - label facei = mesh.nInternalFaces(); - facei < mesh.nFaces(); + label facei = mesh_.nInternalFaces(); + facei < mesh_.nFaces(); ++facei ) { - isBoundaryPoint.set(mesh.faces()[facei]); + isProtectedPoint.set(mesh_.faces()[facei]); } - // Include faces that would be exposed from mesh subset if (nBlockedCells) { @@ -1051,12 +1271,227 @@ Foam::isoSurfaceTopo::isoSurfaceTopo != (cellCutType_[faceNei[facei]] == cutType::BLOCKED) ) { - isBoundaryPoint.set(mesh.faces()[facei]); + isProtectedPoint.set(mesh_.faces()[facei]); + } + } + } + } + + // Initial cell cut information not needed anymore + cellCutType_.clear(); + + + // Additional debugging + if (tetCutAddr.debugCutTetsOn()) + { + const word timeDesc + ( + word::printf("%08d", mesh_.time().timeIndex()) + ); + + // Write debug cut tets in VTK format + { + const auto& debugCuts = tetCutAddr.debugCutTets(); + + // The TET shapes, using the mesh_ points information + vtk::vtuCells vtuCells; + vtuCells.resetShapes(debugCuts); + + // Use all points and all cell-centres + vtuCells.setNumPoints(mesh_.nPoints()); + vtuCells.addPointCellLabels(identity(mesh_.nCells())); + + vtk::internalMeshWriter writer + ( + mesh_, + vtuCells, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-cutTets") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + // Quality of the cut tets + { + Field<scalar> cutTetQuality(debugCuts.size()); + forAll(cutTetQuality, teti) + { + cutTetQuality[teti] = tetPointRef + ( + getMeshPointRef(mesh_, debugCuts[teti][0]), + getMeshPointRef(mesh_, debugCuts[teti][1]), + getMeshPointRef(mesh_, debugCuts[teti][2]), + getMeshPointRef(mesh_, debugCuts[teti][3]) + ).quality(); + } + writer.writeCellData("tetQuality", cutTetQuality); + } + + if (this->snap()) + { + labelList snapped(vtuCells.nFieldPoints(), Zero); + + for (const edge& verts : pointToVerts_) + { + if (verts.first() == verts.second()) + { + // Duplicate index (ie, snapped) + snapped[verts.first()] = 1; + } + } + + writer.beginPointData(); + writer.writePointData("snapped", snapped); + } + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(debugCuts.size(), sumOp<label>()) + << " cut tets: " + << writer.output().name() << nl; + } + + // Determining open edges. Same logic as used later... + + labelHashSet openEdgeIds(0); + + { + const Mesh& s = *this; + + const labelList& mp = s.meshPoints(); + const edgeList& surfEdges = s.edges(); + const labelListList& edgeFaces = s.edgeFaces(); + openEdgeIds.resize(2*s.size()); + + forAll(edgeFaces, edgei) + { + const labelList& eFaces = edgeFaces[edgei]; + if (eFaces.size() == 1) + { + // Open edge (not originating from a boundary face) + + const edge& e = surfEdges[edgei]; + const edge& verts0 = pointToVerts_[mp[e.first()]]; + const edge& verts1 = pointToVerts_[mp[e.second()]]; + + if + ( + isProtectedPoint.test(verts0.first()) + && isProtectedPoint.test(verts0.second()) + && isProtectedPoint.test(verts1.first()) + && isProtectedPoint.test(verts1.second()) + ) + { + // Open edge on boundary face. Keep + } + else + { + // Open edge + openEdgeIds.insert(edgei); + } + } + } + + const label nOpenEdges + ( + returnReduce(openEdgeIds.size(), sumOp<label>()) + ); + + if (nOpenEdges) + { + const edgeList debugEdges + ( + surfEdges, + openEdgeIds.sortedToc() + ); + + vtk::lineWriter writer + ( + s.points(), + debugEdges, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-openEdges") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + Info<< "isoSurfaceTopo : (debug) wrote " + << nOpenEdges << " open edges: " + << writer.output().name() << nl; + } + else + { + Info<< "isoSurfaceTopo : no open edges" << nl; + } + } + + // Write debug surface with snaps + if (this->snap()) + { + const Mesh& s = *this; + + vtk::surfaceWriter writer + ( + s.points(), + s, + fileName + ( + mesh_.time().globalPath() + / ("isoSurfaceTopo." + timeDesc + "-surface") + ) + ); + + writer.writeGeometry(); + + writer.beginCellData(); + writer.writeProcIDs(); + + { + labelList snapped(s.nPoints(), Zero); + forAll(pointToVerts_, i) + { + const edge& verts =pointToVerts_[i]; + if (verts.first() == verts.second()) + { + // Duplicate index (ie, snapped) + snapped[i] = 1; + } } + + writer.beginPointData(); + writer.write("snapped", snapped); } + + Info<< "isoSurfaceTopo : (debug) wrote " + << returnReduce(s.size(), sumOp<label>()) + << " faces : " + << writer.output().name() << nl; } + } + tetCutAddr.clearDebug(); + if (params.filter() == filterType::NONMANIFOLD) + { + // We remove verts on face diagonals. This is in fact just + // straightening the edges of the face through the cell. This can + // close off 'pockets' of triangles and create open or + // multiply-connected triangles + + // Solved by eroding open-edges + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The list of surface faces that should be retained after erosion Mesh& surf = *this; labelList faceAddr(identity(surf.size())); @@ -1086,19 +1521,18 @@ Foam::isoSurfaceTopo::isoSurfaceTopo const labelList& eFaces = edgeFaces[edgei]; if (eFaces.size() == 1) { - // Open edge. Check that vertices do not originate - // from a boundary face - const edge& e = surfEdges[edgei]; + // Open edge (not originating from a boundary face) + const edge& e = surfEdges[edgei]; const edge& verts0 = pointToVerts_[mp[e.first()]]; const edge& verts1 = pointToVerts_[mp[e.second()]]; if ( - isBoundaryPoint.test(verts0.first()) - && isBoundaryPoint.test(verts0.second()) - && isBoundaryPoint.test(verts1.first()) - && isBoundaryPoint.test(verts1.second()) + isProtectedPoint.test(verts0.first()) + && isProtectedPoint.test(verts0.second()) + && isProtectedPoint.test(verts1.first()) + && isProtectedPoint.test(verts1.second()) ) { // Open edge on boundary face. Keep @@ -1160,7 +1594,6 @@ void Foam::isoSurfaceTopo::inplaceSubsetMesh(const bitSet& include) meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)(); - pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)(); } diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.H b/src/sampling/surface/isoSurface/isoSurfaceTopo.H index 29fd36d8d4d2454a58985a16440a9093d7e9a69f..cd5886d135887577c42714186feb990c20a5b616 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.H +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.H @@ -50,6 +50,7 @@ namespace Foam { // Forward Declarations +class cellShape; class polyMesh; /*---------------------------------------------------------------------------*\ @@ -62,63 +63,103 @@ class isoSurfaceTopo { // Private Data - //- Corrected version of tetBasePtIs - labelList tetBasePtIs_; - - //- Per point: originating mesh vertex/cc. See encoding above + //- Per point: originating mesh vertex/cell centre combination. + // Vertices less than nPoints are mesh vertices, + // duplicate vertices indicate a snapped point edgeList pointToVerts_; - //- For every point the originating face in mesh - labelList pointToFace_; - //- The cell cut type - List<cutType> cellCutType_; + // Private Classes + //- Handling, bookkeeping for tet cuts + class tetCutAddressing + { + // Bookkeeping hashes used during construction + EdgeMap<label> vertsToPointLookup_; + Map<label> snapVertsLookup_; - // Private Member Functions + // Filter information for face diagonals + DynamicList<label> pointToFace_; + DynamicList<bool> pointFromDiag_; - //- Generate single point on edge - label generatePoint - ( - const label facei, - const bool edgeIsDiag, - const edge& vertices, - - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - EdgeMap<label>& vertsToPoint - ) const; + // Final output + DynamicList<edge> pointToVerts_; + DynamicList<label> cutPoints_; - //- Generate triangles from tet - void generateTriPoints - ( - const label facei, - const int tetCutIndex, //!< Encoded tet cuts. getTetCutIndex() - const tetCell& tetLabels, - const FixedList<bool, 6>& edgeIsDiag, + //- List of cut (decomposed) cell tets. Debug use only. + DynamicList<cellShape> debugCutTets_; - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, + bool debugCutTetsOn_; - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts - ) const; - //- Generate triangles from cell + public: + + // Constructors + + //- Construct with reserved sizes + tetCutAddressing + ( + const label nCutCells, + const bool useSnap, + const bool useDebugCuts = false + ); + + + // Member Functions + + //- Effective number of faces + label nFaces() const { return cutPoints_.size()/3; } + + DynamicList<label>& pointToFace() { return pointToFace_; } + DynamicList<bool>& pointFromDiag() { return pointFromDiag_; } + + DynamicList<edge>& pointToVerts() { return pointToVerts_; } + DynamicList<label>& cutPoints() { return cutPoints_; } + DynamicList<cellShape>& debugCutTets() { return debugCutTets_; } + + //- Number of debug cut tets + label nDebugTets() const { return debugCutTets_.size(); } + + //- Debugging cut tets active + bool debugCutTetsOn() const { return debugCutTetsOn_; } + + void clearDebug(); + void clearDiagonal(); + void clearHashes(); + + //- Generate single point on edge + label generatePoint + ( + label facei, //!< Originating mesh face for cut-point + bool edgeIsDiagonal, //!< Edge on face diagonal + + // 0: no snap, 1: snap first, 2: snap second + const int snapEnd, + + const edge& vertices + ); + + //- Generate triangles from tet cut + bool generatePoints + ( + const label facei, + const int tetCutIndex, //!< Encoded tet cut + tet snapping + const tetCell& tetLabels, + const FixedList<bool, 6>& edgeIsDiagonal + ); + }; + + + // Private Member Functions + + //- Generate triangle points from cell void generateTriPoints ( const label celli, const bool isTet, + const labelList& tetBasePtIs, - DynamicList<edge>& pointToVerts, - DynamicList<label>& pointToFace, - DynamicList<bool>& pointFromDiag, - - EdgeMap<label>& vertsToPoint, - DynamicList<label>& verts, - DynamicList<label>& faceLabels + tetCutAddressing& tetCutAddr ) const; @@ -165,12 +206,12 @@ protected: // Sampling - //- Interpolates cCoords,pCoords. + //- Interpolates cellData and pointData fields template<class Type> tmp<Field<Type>> interpolateTemplate ( - const Field<Type>& cCoords, - const Field<Type>& pCoords + const Field<Type>& cellData, + const Field<Type>& pointData ) const; public: @@ -205,14 +246,10 @@ public: // Member Functions - //- For every point originating face (pyramid) in mesh - const labelList& pointToFace() const - { - return pointToFace_; - } - - //- Per point: originating mesh vertex/cc. See encoding above - const edgeList& pointToVerts() const + //- Per point: originating mesh vertex/cell centre combination. + // Vertices less than nPoints are mesh vertices (encoding above), + // duplicate vertices indicate a snapped point + const edgeList& pointToVerts() const noexcept { return pointToVerts_; } diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C index 8ffec74e8db56b714118824d188edb711456ef95..5056fb38a5e0b646cdfba93e45fb3aac4b1e1208 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2018 OpenFOAM Foundation - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -32,8 +32,8 @@ template<class Type> Foam::tmp<Foam::Field<Type>> Foam::isoSurfaceTopo::interpolateTemplate ( - const Field<Type>& cellCoords, - const Field<Type>& pointCoords + const Field<Type>& cellData, + const Field<Type>& pointData ) const { auto tfld = tmp<Field<Type>>::New(pointToVerts_.size()); @@ -41,41 +41,50 @@ Foam::isoSurfaceTopo::interpolateTemplate forAll(pointToVerts_, i) { + const edge& verts = pointToVerts_[i]; + Type& val = fld[i]; + scalar s0; - Type p0; + Type v0; { - label idx = pointToVerts_[i].first(); + label idx = verts.first(); if (idx < mesh_.nPoints()) { // Point index s0 = pVals_[idx]; - p0 = pointCoords[idx]; + v0 = pointData[idx]; } else { // Cell index idx -= mesh_.nPoints(); s0 = cVals_[idx]; - p0 = cellCoords[idx]; + v0 = cellData[idx]; } } scalar s1; - Type p1; + Type v1; { - label idx = pointToVerts_[i].second(); - if (idx < mesh_.nPoints()) + label idx = verts.second(); + if (idx == verts.first()) + { + // Duplicate index (ie, snapped) + val = v0; + continue; + } + else if (idx < mesh_.nPoints()) { // Point index s1 = pVals_[idx]; - p1 = pointCoords[idx]; + v1 = pointData[idx]; } else { // Cell index idx -= mesh_.nPoints(); s1 = cVals_[idx]; - p1 = cellCoords[idx]; + v1 = cellData[idx]; } } @@ -83,11 +92,11 @@ Foam::isoSurfaceTopo::interpolateTemplate if (mag(d) > VSMALL) { const scalar s = (iso_-s0)/d; - fld[i] = s*p1+(1.0-s)*p0; + val = s*v1+(1.0-s)*v0; } else { - fld[i] = 0.5*(p0+p1); + val = 0.5*(v0+v1); } }