From c605a62f76b55765df7183bdabe1c397e6233956 Mon Sep 17 00:00:00 2001 From: Kutalmis Bercin <kutalmis.bercin@esi-group.com> Date: Thu, 4 Jun 2020 15:40:14 +0100 Subject: [PATCH] INT: integrate planeToFaceZone topoSetSource --- src/meshTools/Make/files | 1 + .../planeToFaceZone/planeToFaceZone.C | 413 ++++++++++++++++++ .../planeToFaceZone/planeToFaceZone.H | 208 +++++++++ 3 files changed, 622 insertions(+) create mode 100644 src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.C create mode 100644 src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.H diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files index 1d46037d254..31fe65adff5 100644 --- a/src/meshTools/Make/files +++ b/src/meshTools/Make/files @@ -211,6 +211,7 @@ $(faceZoneSources)/setsToFaceZone/setsToFaceZone.C $(faceZoneSources)/setToFaceZone/setToFaceZone.C $(faceZoneSources)/setAndNormalToFaceZone/setAndNormalToFaceZone.C $(faceZoneSources)/searchableSurfaceToFaceZone/searchableSurfaceToFaceZone.C +$(faceZoneSources)/planeToFaceZone/planeToFaceZone.C cellZoneSources = sets/cellZoneSources $(cellZoneSources)/setToCellZone/setToCellZone.C diff --git a/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.C b/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.C new file mode 100644 index 00000000000..53de3eb39f8 --- /dev/null +++ b/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.C @@ -0,0 +1,413 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenFOAM Foundation + Copyright (C) 2020 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 "planeToFaceZone.H" +#include "polyMesh.H" +#include "faceZoneSet.H" +#include "uindirectPrimitivePatch.H" +#include "PatchTools.H" +#include "syncTools.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(planeToFaceZone, 0); + addToRunTimeSelectionTable(topoSetSource, planeToFaceZone, word); + addToRunTimeSelectionTable(topoSetSource, planeToFaceZone, istream); +} + + +Foam::topoSetSource::addToUsageTable Foam::planeToFaceZone::usage_ +( + planeToFaceZone::typeName, + "\n Usage: planeToFaceZone (px py pz) (nx ny nz) include\n\n" + " Select faces for which the adjacent cell centres lie on opposite " + " of a plane\n\n" +); + + +const Foam::Enum +< + Foam::planeToFaceZone::faceZoneAction +> +Foam::plainToFaceZone::faceZoneActionNames_ +({ + { faceZoneAction::ALL, "all" }, + { faceZoneAction::CLOSEST, "closest" }, +}); + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::planeToFaceZone::combine(faceZoneSet& fzSet, const bool add) const +{ + // Mark all cells with centres above the plane + boolList cellIsAbovePlane(mesh_.nCells()); + forAll(mesh_.cells(), celli) + { + cellIsAbovePlane[celli] = + ((mesh_.cellCentres()[celli] - point_) & normal_) > 0; + } + + // Mark all faces that sit between cells above and below the plane + boolList faceIsOnPlane(mesh_.nFaces()); + forAll(mesh_.faceNeighbour(), facei) + { + faceIsOnPlane[facei] = + cellIsAbovePlane[mesh_.faceOwner()[facei]] + != cellIsAbovePlane[mesh_.faceNeighbour()[facei]]; + } + forAll(mesh_.boundaryMesh(), patchi) + { + const polyPatch& patch = mesh_.boundaryMesh()[patchi]; + forAll(patch, patchFacei) + { + const label facei = patch.start() + patchFacei; + faceIsOnPlane[facei] = + patch.coupled() && cellIsAbovePlane[mesh_.faceOwner()[facei]]; + } + } + syncTools::syncFaceList(mesh_, faceIsOnPlane, notEqOp<bool>()); + + // Convert marked faces to a list of indices + labelList newSetFaces(findIndices(faceIsOnPlane, true)); + + // If constructing a single contiguous set, remove all faces except those + // connected to the contiguous region closest to the specified point + if (option_ == faceZoneAction::closest) + { + // Step 1: Get locally contiguous regions for the new face set and the + // total number of regions across all processors. + labelList newSetFaceRegions(newSetFaces.size(), -1); + label nRegions = -1; + { + // Create a patch of the set faces + const uindirectPrimitivePatch newSetPatch + ( + UIndirectList<face>(mesh_.faces(), newSetFaces), + mesh_.points() + ); + + // Get the region ID-s and store the total number of regions on + // each processor + labelList procNRegions(Pstream::nProcs(), -1); + procNRegions[Pstream::myProcNo()] = + PatchTools::markZones + ( + newSetPatch, + boolList(newSetPatch.nEdges(), false), + newSetFaceRegions + ); + Pstream::gatherList(procNRegions); + Pstream::scatterList(procNRegions); + + // Cumulative sum the number of regions on each processor to get an + // offset which makes the local region ID-s globally unique + labelList procRegionOffset(Pstream::nProcs(), 0); + for (label proci = 1; proci < Pstream::nProcs(); ++ proci) + { + procRegionOffset[proci] += + procRegionOffset[proci - 1] + + procNRegions[proci - 1]; + } + + // Apply the offset + forAll(newSetFaces, newSetFacei) + { + newSetFaceRegions[newSetFacei] += + procRegionOffset[Pstream::myProcNo()]; + } + + // Store the total number of regions across all processors + nRegions = procRegionOffset.last() + procNRegions.last(); + } + + // Step 2: Create a region map which combines regions which are + // connected across coupled interfaces + labelList regionMap(identity(nRegions)); + { + // Put region labels on connected boundary edges and synchronise to + // create a list of all regions connected to a given edge + labelListList meshEdgeRegions(mesh_.nEdges(), labelList()); + forAll(newSetFaces, newSetFacei) + { + const label facei = newSetFaces[newSetFacei]; + const label regioni = newSetFaceRegions[newSetFacei]; + forAll(mesh_.faceEdges()[facei], faceEdgei) + { + const label edgei = mesh_.faceEdges()[facei][faceEdgei]; + meshEdgeRegions[edgei] = labelList(1, regioni); + } + } + syncTools::syncEdgeList + ( + mesh_, + meshEdgeRegions, + globalMeshData::ListPlusEqOp<labelList>(), + labelList() + ); + + // Combine edge regions to create a list of what regions a given + // region is connected to + List<labelHashSet> regionRegions(nRegions); + forAll(newSetFaces, newSetFacei) + { + const label facei = newSetFaces[newSetFacei]; + const label regioni = newSetFaceRegions[newSetFacei]; + forAll(mesh_.faceEdges()[facei], faceEdgei) + { + const label edgei = mesh_.faceEdges()[facei][faceEdgei]; + forAll(meshEdgeRegions[edgei], edgeRegioni) + { + if (meshEdgeRegions[edgei][edgeRegioni] != regioni) + { + regionRegions[regioni].insert + ( + meshEdgeRegions[edgei][edgeRegioni] + ); + } + } + } + } + Pstream::listCombineGather(regionRegions, plusEqOp<labelHashSet>()); + Pstream::listCombineScatter(regionRegions); + + // Collapse the region connections into a map between each region + // and the lowest numbered region that it connects to + forAll(regionRegions, regioni) + { + forAllConstIter(labelHashSet, regionRegions[regioni], iter) + { + regionMap[iter.key()] = + min(regionMap[iter.key()], regionMap[regioni]); + } + } + } + + // Step 3: Combine connected regions + labelList regionNFaces; + { + // Remove duplicates from the region map + label regioni0 = 0; + forAll(regionMap, regioni) + { + if (regionMap[regioni] > regioni0) + { + ++ regioni0; + regionMap[regioni] = regioni0; + } + } + + // Recompute the number of regions + nRegions = regioni0 + 1; + + // Renumber the face region ID-s + newSetFaceRegions = + IndirectList<label>(regionMap, newSetFaceRegions); + + // Report the final number and size of the regions + regionNFaces = labelList(nRegions, 0); + forAll(newSetFaces, newSetFacei) + { + regionNFaces[newSetFaceRegions[newSetFacei]] ++; + } + Pstream::listCombineGather(regionNFaces, plusEqOp<label>()); + Pstream::listCombineScatter(regionNFaces); + Info<< " Found " << nRegions << " contiguous regions with " + << regionNFaces << " faces" << endl; + } + + // Step 4: Choose the closest region to output + label selectedRegioni = -1; + { + // Compute the region centres + scalarField regionMagAreas(nRegions, 0); + pointField regionCentres(nRegions, Zero); + forAll(newSetFaces, newSetFacei) + { + const label facei = newSetFaces[newSetFacei]; + const label regioni = newSetFaceRegions[newSetFacei]; + + const vector& a = mesh_.faceAreas()[facei]; + const point& c = mesh_.faceCentres()[facei]; + + regionMagAreas[regioni] += mag(a); + regionCentres[regioni] += mag(a)*c; + } + Pstream::listCombineGather(regionMagAreas, plusEqOp<scalar>()); + Pstream::listCombineGather(regionCentres, plusEqOp<point>()); + Pstream::listCombineScatter(regionMagAreas); + Pstream::listCombineScatter(regionCentres); + regionCentres /= regionMagAreas; + + // Find the region centroid closest to the reference point + selectedRegioni = + returnReduce + ( + findMin(mag(regionCentres - point_)()), + minOp<label>() + ); + + // Report the selection + Info<< " Selecting region " << selectedRegioni << " with " + << regionNFaces[selectedRegioni] + << " faces as the closest to point " << point_ << endl; + } + + // Step 5: Remove any faces from the set list not in the selected region + { + // Remove faces from the list by shuffling up and resizing + label newSetFacei0 = 0; + forAll(newSetFaces, newSetFacei) + { + newSetFaces[newSetFacei0] = newSetFaces[newSetFacei]; + + if (newSetFaceRegions[newSetFacei] == selectedRegioni) + { + ++ newSetFacei0; + } + } + newSetFaces.resize(newSetFacei0); + } + } + + // Modify the face zone set + DynamicList<label> newAddressing; + DynamicList<bool> newFlipMap; + if (add) + { + // Start from copy + newAddressing = DynamicList<label>(fzSet.addressing()); + newFlipMap = DynamicList<bool>(fzSet.flipMap()); + + // Add anything from the new set that is not already in the zone set + forAll(newSetFaces, newSetFacei) + { + const label facei = newSetFaces[newSetFacei]; + + if (!fzSet.found(facei)) + { + newAddressing.append(facei); + newFlipMap.append(cellIsAbovePlane[mesh_.faceOwner()[facei]]); + } + } + } + else + { + // Start from empty + newAddressing = DynamicList<label>(fzSet.addressing().size()); + newFlipMap = DynamicList<bool>(fzSet.flipMap().size()); + + // Add everything from the zone set that is not also in the new set + labelHashSet newSet(newSetFaces); + forAll(fzSet.addressing(), i) + { + const label facei = fzSet.addressing()[i]; + + if (!newSet.found(facei)) + { + newAddressing.append(facei); + newFlipMap.append(cellIsAbovePlane[mesh_.faceOwner()[facei]]); + } + } + } + fzSet.addressing().transfer(newAddressing); + fzSet.flipMap().transfer(newFlipMap); + fzSet.updateSet(); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::planeToFaceZone::planeToFaceZone +( + const polyMesh& mesh, + const dictionary& dict +) +: + topoSetSource(mesh), + point_(dict.get<vector>("point")), + normal_(dict.get<vector>("normal")), + option_ + ( + faceZoneActionNames_.getOrDefault("option", dict, faceZoneAction::ALL) + ) +{} + + +Foam::planeToFaceZone::planeToFaceZone +( + const polyMesh& mesh, + Istream& is +) +: + topoSetSource(mesh), + point_(checkIs(is)), + normal_(checkIs(is)), + option_(faceZoneActionNames_.read(checkIs(is))) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::planeToFaceZone::applyToSet +( + const topoSetSource::setAction action, + topoSet& set +) const +{ + if (!isA<faceZoneSet>(set)) + { + WarningInFunction + << "Operation only allowed on a faceZoneSet." << endl; + } + else + { + faceZoneSet& fzSet = refCast<faceZoneSet>(set); + + if ((action == topoSetSource::NEW) || (action == topoSetSource::ADD)) + { + Info<< " Adding faces which form a plane at " << point_ + << " with normal " << normal_ << endl; + + combine(fzSet, true); + } + else if (action == topoSetSource::DELETE) + { + Info<< " Removing faces which form a plane at " << point_ + << " with normal " << normal_ << endl; + + combine(fzSet, false); + } + } +} + + +// ************************************************************************* // diff --git a/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.H b/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.H new file mode 100644 index 00000000000..04ecc718570 --- /dev/null +++ b/src/meshTools/sets/faceZoneSources/planeToFaceZone/planeToFaceZone.H @@ -0,0 +1,208 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenFOAM Foundation + Copyright (C) 2020 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::planeToFaceZone + +Description + A \c topoSetSource to select faces based on the adjacent cell centres + spanning a given plane. The plane is defined by a point and normal vector. + + Operands: + \table + Operand | Type | Location + output 1 | faceSet | $FOAM_CASE/constant/polyMesh/sets/\<set\> + output 2 | faceZone | $FOAM_CASE/constant/polyMesh/faceZones + \endtable + +Usage + Minimal example by using \c system/topoSetDict.actions: + \verbatim + { + // Mandatory (inherited) entries + name <name>; + type faceZoneSet; + action <action>; + + // Mandatory entries + source planeToFaceZone; + point (<px> <py> <pz>); + normal (<nx> <ny> <nz>); + + // Optional entries + option <option>; + } + \endverbatim + + where the entries mean: + \table + Property | Description | Type | Req'd | Dflt + name | Name of faceZone | word | yes | - + type | Type name: faceZoneSet | word | yes | - + action | Action applied on faces - see below | word | yes | - + source | Source name: planeToFaceZone | word | yes | - + point | A point on the plane | vector | yes | - + normal | The plane normal for orientation | vector | yes | - + option | Selection method - see below | word | no | all + \endtable + + Options for the \c action entry: + \verbatim + new | Create a new faceZone from selected faces of a faceZone + add | Add selected faces of a faceZone into this faceZone + subtract | Remove selected faces of a faceZone from this faceZone + \endverbatim + + Options for the \c option entry: + \verbatim + all | + closest | + \endverbatim + +Note + Additionally, an include entry can be specified. When omitted or set to + "all", then all faces that meet the criteria are included in the set. When + set to "closest", just the faces that belong to the closest contiguous + region to the plane point are included. This latter setting is useful when + defining face zones through channels on which the flow rate is to be + computed, as it keeps the set local to a single channel. + +See also + - Foam::topoSetSource + +SourceFiles + planeToFaceZone.C + +\*---------------------------------------------------------------------------*/ + +#ifndef planeToFaceZone_H +#define planeToFaceZone_H + +#include "topoSetSource.H" +#include "Enum.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +class faceZoneSet; + +/*---------------------------------------------------------------------------*\ + Class planeToFaceZone Declaration +\*---------------------------------------------------------------------------*/ + +class planeToFaceZone +: + public topoSetSource +{ +public: + + //- Enumeration defining the valid options + enum faceZoneAction + { + ALL, + CLOSEST + }; + + +private: + + // Private Data + + //- Add usage string + static addToUsageTable usage_; + + //- Included region names + //static const NamedEnum<include, 2> includeNames_; + static const Enum<faceZoneAction> faceZoneActionNames_; + + //- Point on the plane + const vector point_; + + //- Normal to the plane + const vector normal_; + + //- Included regions + const faceZoneAction option_; + + + // Private Member Functions + + void combine(faceZoneSet& set, const bool add) const; + + +public: + + //- Runtime type information + TypeName("planeToFaceZone"); + + + // Constructors + + //- Construct from dictionary + planeToFaceZone + ( + const polyMesh& mesh, + const dictionary& dict + ); + + //- Construct from Istream + planeToFaceZone + ( + const polyMesh& mesh, + Istream& + ); + + + //- Destructor + virtual ~planeToFaceZone() = default; + + + // Member Functions + + virtual sourceType setType() const + { + return FACESETSOURCE; + } + + virtual void applyToSet + ( + const topoSetSource::setAction action, + topoSet& + ) const; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // -- GitLab