/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2016-2025 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 . \*---------------------------------------------------------------------------*/ #include "ensightMesh.H" #include "ensightGeoFile.H" #include "polyMesh.H" #include "emptyPolyPatch.H" #include "processorPolyPatch.H" #include "ListOps.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // const Foam::label Foam::ensightMesh::internalZone = -1; // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::ensightMesh::clear() { cellZoneParts_.clear(); faceZoneParts_.clear(); boundaryParts_.clear(); } void Foam::ensightMesh::renumber() { label partNo = 0; for (const label id : cellZoneParts_.sortedToc()) { cellZoneParts_[id].index() = partNo++; } for (const label id : boundaryParts_.sortedToc()) { boundaryParts_[id].index() = partNo++; } for (const label id : faceZoneParts_.sortedToc()) { faceZoneParts_[id].index() = partNo++; } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::ensightMesh::ensightMesh ( const polyMesh& mesh ) : ensightMesh(mesh, ensightMesh::options()) {} Foam::ensightMesh::ensightMesh ( const polyMesh& mesh, const ensightMesh::options& opts ) : options_(new options(opts)), mesh_(mesh), needsUpdate_(true), verbose_(0) { if (!option().lazy()) { correct(); } } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // int Foam::ensightMesh::verbose() const noexcept { return verbose_; } int Foam::ensightMesh::verbose(const int level) noexcept { int old(verbose_); verbose_ = level; return old; } void Foam::ensightMesh::correct() { clear(); const auto& pbm = mesh_.boundaryMesh(); // Selected patch indices labelList patchIds; if (option().useBoundaryMesh()) { patchIds = pbm.indices ( option().patchSelection(), option().patchExclude() ); // Prune undesirable patches - empty and processor patches label count = 0; for (const label patchi : patchIds) { const auto& pp = pbm[patchi]; if (isType(pp)) { continue; } else if (isA(pp)) { break; // No processor patches } patchIds[count] = patchi; ++count; } patchIds.resize(count); } // Selection of cellZones const auto& czMatcher = option().cellZoneSelection(); // Selected cell zone indices labelList czoneIds; if (option().useCellZones()) { // Use allow/deny to have desired behaviour with empty selection czoneIds = mesh_.cellZones().indices ( option().cellZoneSelection(), option().cellZoneExclude() ); } // Selected face zone indices labelList fzoneIds; if (option().useFaceZones()) { // Use allow/deny to have desired behaviour with empty selection fzoneIds = mesh_.faceZones().indices ( option().faceZoneSelection(), option().faceZoneExclude() ); } // Track which cells are in a zone or not bitSet cellSelection; // Faces to be excluded from export bitSet excludeFace; // cellZones first for (const label zoneId : czoneIds) { const auto& zn = mesh_.cellZones()[zoneId]; const auto& zoneName = zn.name(); if (returnReduceOr(!zn.empty())) { // Ensure full mesh coverage cellSelection.resize(mesh_.nCells()); cellSelection.set(zn); ensightCells& part = cellZoneParts_(zoneId); part.clear(); part.identifier() = zoneId; part.rename(zoneName); part.classify(mesh_, zn); // Finalize part.reduce(); if (verbose_) { Info<< part.info(); } } } if (option().useInternalMesh() && czMatcher.empty()) { // The internal mesh: // Either the entire mesh (if no zones specified) // or whatever is leftover as unzoned if (cellZoneParts_.empty()) { ensightCells& part = cellZoneParts_(internalZone); part.clear(); part.identifier() = internalZone; part.rename("internalMesh"); part.classify(mesh_); // Finalize part.reduce(); if (verbose_) { Info<< part.info(); } } else { // Unzoned cells - flip selection from zoned to unzoned cellSelection.flip(); if (returnReduceOr(cellSelection.any())) { ensightCells& part = cellZoneParts_(internalZone); part.clear(); part.identifier() = internalZone; part.rename("internalMesh"); part.classify(mesh_, cellSelection); // Finalize part.reduce(); if (verbose_) { Info<< part.info(); } } } // Handled all cells cellSelection.clearStorage(); } else if (cellSelection.none()) { // No internal, no cellZones selected - just ignore cellSelection.clearStorage(); } // Face exclusion based on cellZones if (returnReduceOr(!cellSelection.empty())) { // Ensure full mesh coverage excludeFace.resize(mesh_.nFaces()); const labelList& owner = mesh_.faceOwner(); forAll(owner, facei) { const label celli = owner[facei]; if (!cellSelection.test(celli)) { excludeFace.set(facei); } } } // Face exclusion for empty types and neighbour side of coupled // so they are ignored for face zones if (fzoneIds.size()) { // Ensure full mesh coverage excludeFace.resize(mesh_.nFaces()); for (const polyPatch& p : pbm) { const auto* cpp = isA(p); if ( isA(p) || (cpp && !cpp->owner()) ) { // Ignore empty patch // Ignore neighbour side of coupled excludeFace.set(p.range()); } } } // Patches for (const label patchId : patchIds) { const auto& p = pbm[patchId]; const auto& patchName = p.name(); if (isA(p)) { // Skip empty patch types continue; } else if (isA(p)) { // Only real (non-processor) boundaries. break; } ensightFaces& part = boundaryParts_(patchId); part.clear(); part.identifier() = patchId; part.rename(patchName); part.classify ( mesh_.faces(), identity(p.range()), boolList(), // no flip map excludeFace ); // Finalize part.reduce(); if (verbose_) { Info<< part.info(); } if (!part.total()) { boundaryParts_.erase(patchId); } } // Face zones for (const label zoneId : fzoneIds) { const auto& zn = mesh_.faceZones()[zoneId]; const auto& zoneName = zn.name(); ensightFaces& part = faceZoneParts_(zoneId); part.clear(); part.identifier() = zoneId; part.rename(zoneName); if (zn.size()) { part.classify ( mesh_.faces(), zn, zn.flipMap(), excludeFace ); } // Finalize part.reduce(); if (verbose_) { Info<< part.info(); } if (!part.total()) { faceZoneParts_.erase(zoneId); } } renumber(); needsUpdate_ = false; } void Foam::ensightMesh::write ( ensightGeoFile& os, bool parallel ) const { if (UPstream::master()) { os.beginGeometry(); } // The internalMesh, cellZones for (const label id : cellZoneParts_.sortedToc()) { cellZoneParts_[id].write(os, mesh_, parallel); } // Patches - sorted by index for (const label id : boundaryParts_.sortedToc()) { boundaryParts_[id].write(os, mesh_, parallel); } // Requested faceZones - sorted by index for (const label id : faceZoneParts_.sortedToc()) { faceZoneParts_[id].write(os, mesh_, parallel); } // No geometry parts written? // - with lagrangian-only output the VTK EnsightReader still // needs a volume geometry, and ensight usually does too if ( cellZoneParts_.empty() && boundaryParts_.empty() && faceZoneParts_.empty() ) { ensightCells::writeBox(os, mesh_.bounds()); } } // ************************************************************************* //