diff --git a/applications/utilities/mesh/manipulation/stitchMesh/stitchMesh.C b/applications/utilities/mesh/manipulation/stitchMesh/stitchMesh.C index 15dd2c30435eae153633c06de9a373c1fb7534b9..930163451e1c6184f66c595c92a145fd5964810a 100644 --- a/applications/utilities/mesh/manipulation/stitchMesh/stitchMesh.C +++ b/applications/utilities/mesh/manipulation/stitchMesh/stitchMesh.C @@ -622,7 +622,7 @@ int main(int argc, char *argv[]) } // Execute all polyMeshModifiers - autoPtr<mapPolyMesh> morphMap = stitcher.changeMesh(true); + autoPtr<mapPolyMesh> morphMap = stitcher.changeMesh(); mesh.movePoints(morphMap->preMotionPoints()); diff --git a/src/dynamicMesh/Make/files b/src/dynamicMesh/Make/files index d87341be3e23180d22edb7b0099ca6721b3d8d45..ea83ff7cc4ad5fd2be8937fa9c949e49c37654b2 100644 --- a/src/dynamicMesh/Make/files +++ b/src/dynamicMesh/Make/files @@ -22,6 +22,7 @@ $(polyMeshModifier)/polyMeshModifier.C $(polyMeshModifier)/polyMeshModifierNew.C polyTopoChange/polyTopoChanger/polyTopoChanger.C +polyTopoChange/polyTopoChanger/polyTopoChangerChangeMesh.C polyTopoChange/polyTopoChange/addPatchCellLayer.C polyTopoChange/polyTopoChange/pointEdgeCollapse/pointEdgeCollapse.C polyTopoChange/polyTopoChange/edgeCollapser.C @@ -76,6 +77,10 @@ meshCut/refineCell/refineCell.C meshCut/wallLayerCells/wallLayerCells.C meshCut/wallLayerCells/wallNormalInfo/wallNormalInfo.C +refinement/refinement/refinement.C +refinement/polyhedralRefinement/polyhedralRefinement.C +refinement/prismatic2DRefinement/prismatic2DRefinement.C + polyTopoChange/attachPolyTopoChanger/attachPolyTopoChanger.C polyTopoChange/repatchPolyTopoChanger/repatchPolyTopoChanger.C diff --git a/src/dynamicMesh/attachDetach/attachDetach.C b/src/dynamicMesh/attachDetach/attachDetach.C index 51e9143730d19a5acad610e299c5b6d207794914..0a3c6d529f126da51d5feb99afb1451a4cdec051 100644 --- a/src/dynamicMesh/attachDetach/attachDetach.C +++ b/src/dynamicMesh/attachDetach/attachDetach.C @@ -31,7 +31,7 @@ License #include "polyMesh.H" #include "Time.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "addToRunTimeSelectionTable.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -365,7 +365,7 @@ bool Foam::attachDetach::changeTopology() const } -void Foam::attachDetach::setRefinement(polyTopoChange& ref) const +void Foam::attachDetach::setRefinement(batchPolyTopoChange& ref) const { // Insert the attach/detach instructions into the topological change diff --git a/src/dynamicMesh/attachDetach/attachDetach.H b/src/dynamicMesh/attachDetach/attachDetach.H index eecc3f1ba07e4b340216869e34183af01f04c6e5..0d4c6465208921d3e110b9b1a7c5790f1d93fecc 100644 --- a/src/dynamicMesh/attachDetach/attachDetach.H +++ b/src/dynamicMesh/attachDetach/attachDetach.H @@ -122,10 +122,10 @@ class attachDetach // Topological changes //- Attach interface - void attachInterface(polyTopoChange&) const; + void attachInterface(batchPolyTopoChange&) const; //- Detach interface - void detachInterface(polyTopoChange&) const; + void detachInterface(batchPolyTopoChange&) const; //- Calculate point match addressing void calcPointMatchMap() const; @@ -215,7 +215,7 @@ public: //- Insert the layer addition/removal instructions // into the topological change - virtual void setRefinement(polyTopoChange&) const; + virtual void setRefinement(batchPolyTopoChange&) const; //- Modify motion points to comply with the topological change virtual void modifyMotionPoints(pointField& motionPoints) const; diff --git a/src/dynamicMesh/attachDetach/attachInterface.C b/src/dynamicMesh/attachDetach/attachInterface.C index 70e996d297e1f9ff4e7aa0a6959b5efc8a230381..1da8367b0d7c72becf4e1a2b77d38c65966dd236 100644 --- a/src/dynamicMesh/attachDetach/attachInterface.C +++ b/src/dynamicMesh/attachDetach/attachInterface.C @@ -29,7 +29,7 @@ License #include "attachDetach.H" #include "polyMesh.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyTopoChanger.H" #include "polyRemovePoint.H" #include "polyRemoveFace.H" @@ -43,7 +43,7 @@ const Foam::scalar Foam::attachDetach::positionDifference_ = 1e-8; void Foam::attachDetach::attachInterface ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { // Algorithm: @@ -62,7 +62,7 @@ void Foam::attachDetach::attachInterface if (debug) { Pout<< "void attachDetach::attachInterface(" - << "polyTopoChange& ref) const " + << "batchPolyTopoChange& ref) const " << " for object " << name() << " : " << "Attaching interface" << endl; } @@ -266,7 +266,7 @@ void Foam::attachDetach::attachInterface if (debug) { Pout<< "void attachDetach::attachInterface(" - << "polyTopoChange& ref) const " + << "batchPolyTopoChange& ref) const " << " for object " << name() << " : " << "Finished attaching interface" << endl; } diff --git a/src/dynamicMesh/attachDetach/detachInterface.C b/src/dynamicMesh/attachDetach/detachInterface.C index 34f94d4cad4bc5f08ea8b6cff8260aa3e45c7127..35d06ec76124474396b77dddbc1e9cd3084fa822 100644 --- a/src/dynamicMesh/attachDetach/detachInterface.C +++ b/src/dynamicMesh/attachDetach/detachInterface.C @@ -28,7 +28,7 @@ License #include "attachDetach.H" #include "polyMesh.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyTopoChanger.H" #include "polyAddPoint.H" #include "polyModifyFace.H" @@ -38,7 +38,7 @@ License void Foam::attachDetach::detachInterface ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { // Algorithm: @@ -66,7 +66,7 @@ void Foam::attachDetach::detachInterface if (debug) { Pout<< "void attachDetach::detachInterface(" - << "polyTopoChange& ref) const " + << "batchPolyTopoChange& ref) const " << " for object " << name() << " : " << "Detaching interface" << endl; } @@ -75,7 +75,7 @@ void Foam::attachDetach::detachInterface const faceZoneMesh& zoneMesh = mesh.faceZones(); // Check that zone is in increasing order (needed since adding faces - // in same order - otherwise polyTopoChange face ordering will mess up + // in same order - otherwise batchPolyTopoChange face ordering will mess up // correspondence) if (debug) { @@ -467,7 +467,7 @@ void Foam::attachDetach::detachInterface if (debug) { Pout<< "void attachDetach::detachInterface(" - << "polyTopoChange& ref) const " + << "batchPolyTopoChange& ref) const " << " for object " << name() << " : " << "Finished detaching interface" << endl; } diff --git a/src/dynamicMesh/layerAdditionRemoval/addCellLayer.C b/src/dynamicMesh/layerAdditionRemoval/addCellLayer.C index 2c6eb27b362e40754ea701c63d82a6653fa5e597..569f3891865ac7ed5f98c34ad9b7cba7ff767089 100644 --- a/src/dynamicMesh/layerAdditionRemoval/addCellLayer.C +++ b/src/dynamicMesh/layerAdditionRemoval/addCellLayer.C @@ -29,7 +29,7 @@ License #include "layerAdditionRemoval.H" #include "polyMesh.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyTopoChanger.H" #include "polyAddPoint.H" #include "polyAddCell.H" @@ -88,7 +88,7 @@ Foam::tmp<Foam::vectorField> Foam::layerAdditionRemoval::extrusionDir() const void Foam::layerAdditionRemoval::addCellLayer ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { // Insert the layer addition instructions into the topological change @@ -108,7 +108,7 @@ void Foam::layerAdditionRemoval::addCellLayer if (debug) { Pout<< "void layerAdditionRemoval::addCellLayer(" - << "polyTopoChange& ref) const for object " << name() << " : " + << "batchPolyTopoChange& ref) const for object " << name() << " : " << "Adding cell layer" << endl; } @@ -682,7 +682,7 @@ void Foam::layerAdditionRemoval::addCellLayer if (debug) { - Pout<< "void layerAdditionRemoval::addCellLayer(polyTopoChange&) const " + Pout<< "void layerAdditionRemoval::addCellLayer(batchPolyTopoChange&) const " << " for object " << name() << ": " << "Finished adding cell layer" << endl; } diff --git a/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.C b/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.C index d7baa813f4c85d505a70dcc0031f24bb57c25719..507d559c9871eadb58f4ff7e0098b35f975a7694 100644 --- a/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.C +++ b/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.C @@ -355,7 +355,7 @@ bool Foam::layerAdditionRemoval::changeTopology() const } -void Foam::layerAdditionRemoval::setRefinement(polyTopoChange& ref) const +void Foam::layerAdditionRemoval::setRefinement(batchPolyTopoChange& ref) const { // Insert the layer addition/removal instructions // into the topological change @@ -367,7 +367,7 @@ void Foam::layerAdditionRemoval::setRefinement(polyTopoChange& ref) const // Clear addressing. This also resets the addition/removal data if (debug) { - Pout<< "layerAdditionRemoval::setRefinement(polyTopoChange&) " + Pout<< "layerAdditionRemoval::setRefinement(batchPolyTopoChange&) " << "for object " << name() << " : " << "Clearing addressing after layer removal" << endl; } @@ -383,7 +383,7 @@ void Foam::layerAdditionRemoval::setRefinement(polyTopoChange& ref) const // Clear addressing. This also resets the addition/removal data if (debug) { - Pout<< "layerAdditionRemoval::setRefinement(polyTopoChange&) " + Pout<< "layerAdditionRemoval::setRefinement(batchPolyTopoChange&) " << "for object " << name() << " : " << "Clearing addressing after layer addition" << endl; } diff --git a/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.H b/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.H index 25e5e68327fc2e19adef268b049364b15ca204e2..45c983b5dc3a54f901d930807488fb229fe85b98 100644 --- a/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.H +++ b/src/dynamicMesh/layerAdditionRemoval/layerAdditionRemoval.H @@ -119,10 +119,10 @@ class layerAdditionRemoval tmp<vectorField> extrusionDir() const; //- Add a layer of cells - void addCellLayer(polyTopoChange&) const; + void addCellLayer(batchPolyTopoChange&) const; //- Remove a layer of cells - void removeCellLayer(polyTopoChange&) const; + void removeCellLayer(batchPolyTopoChange&) const; //- Clear addressing void clearAddressing() const; @@ -179,7 +179,7 @@ public: //- Insert the layer addition/removal instructions // into the topological change - virtual void setRefinement(polyTopoChange&) const; + virtual void setRefinement(batchPolyTopoChange&) const; //- Modify motion points to comply with the topological change virtual void modifyMotionPoints(pointField& motionPoints) const; diff --git a/src/dynamicMesh/layerAdditionRemoval/removeCellLayer.C b/src/dynamicMesh/layerAdditionRemoval/removeCellLayer.C index e04075d2e0c8def7d9369ae02fa149d62877dd6c..9dd6fa51cfef5716625ea9b23c6b1eadd43206b6 100644 --- a/src/dynamicMesh/layerAdditionRemoval/removeCellLayer.C +++ b/src/dynamicMesh/layerAdditionRemoval/removeCellLayer.C @@ -29,7 +29,7 @@ License #include "layerAdditionRemoval.H" #include "polyMesh.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "oppositeFace.H" #include "polyTopoChanger.H" #include "polyRemoveCell.H" @@ -83,7 +83,7 @@ bool Foam::layerAdditionRemoval::validCollapse() const void Foam::layerAdditionRemoval::removeCellLayer ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { // Algorithm for layer removal. Second phase: topological change diff --git a/src/dynamicMesh/perfectInterface/perfectInterface.C b/src/dynamicMesh/perfectInterface/perfectInterface.C index da1cc5130f2e6212dffc9e4f42e5e8af86cfbd57..b4c973f8e8fb7461e9f863197700811746514d6b 100644 --- a/src/dynamicMesh/perfectInterface/perfectInterface.C +++ b/src/dynamicMesh/perfectInterface/perfectInterface.C @@ -33,7 +33,7 @@ Description #include "perfectInterface.H" #include "polyTopoChanger.H" #include "polyMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "addToRunTimeSelectionTable.H" #include "mapPolyMesh.H" #include "matchPoints.H" @@ -154,7 +154,7 @@ void Foam::perfectInterface::setRefinement ( const indirectPrimitivePatch& pp0, const indirectPrimitivePatch& pp1, - polyTopoChange& ref + batchPolyTopoChange& ref ) const { const polyMesh& mesh = topoChanger().mesh(); @@ -423,11 +423,11 @@ void Foam::perfectInterface::setRefinement } -void Foam::perfectInterface::setRefinement(polyTopoChange& ref) const +void Foam::perfectInterface::setRefinement(batchPolyTopoChange& ref) const { if (debug) { - Pout<< "bool perfectInterface::setRefinement(polyTopoChange&) const : " + Pout<< "bool perfectInterface::setRefinement(batchPolyTopoChange&) const : " << "for object " << name() << " : " << "masterPatchID_:" << masterPatchID_ << " slavePatchID_:" << slavePatchID_ diff --git a/src/dynamicMesh/perfectInterface/perfectInterface.H b/src/dynamicMesh/perfectInterface/perfectInterface.H index 20352c4bee1710333000cc79e3bc43cab557097b..f5895723dc8458c023f03390f0eec6890c261c19 100644 --- a/src/dynamicMesh/perfectInterface/perfectInterface.H +++ b/src/dynamicMesh/perfectInterface/perfectInterface.H @@ -129,7 +129,7 @@ public: //- Insert the layer addition/removal instructions // into the topological change - virtual void setRefinement(polyTopoChange&) const; + virtual void setRefinement(batchPolyTopoChange&) const; //- Insert the layer addition/removal instructions // into the topological change. Uses only mesh, not any of the @@ -139,7 +139,7 @@ public: ( const indirectPrimitivePatch& pp0, const indirectPrimitivePatch& pp1, - polyTopoChange& + batchPolyTopoChange& ) const; //- Modify motion points to comply with the topological change diff --git a/src/dynamicMesh/polyTopoChange/attachPolyTopoChanger/attachPolyTopoChanger.C b/src/dynamicMesh/polyTopoChange/attachPolyTopoChanger/attachPolyTopoChanger.C index a88b03e5d5a25d45e70a6b1c5d3019e0140e605e..a11a743f3220c527c5165d874d72c8bd35c8ac43 100644 --- a/src/dynamicMesh/polyTopoChange/attachPolyTopoChanger/attachPolyTopoChanger.C +++ b/src/dynamicMesh/polyTopoChange/attachPolyTopoChanger/attachPolyTopoChanger.C @@ -65,7 +65,10 @@ void Foam::attachPolyTopoChanger::attach(const bool removeEmptyPatches) const fileName oldInst = mesh_.facesInstance(); // Execute all polyMeshModifiers - changeMesh(false); // no inflation + // DISABLED - this functionality is lost + // Bad rewrite by Mattijs Janssens - completely misunderstood the interface + // Needs to use polyTopoChange, and not changer. + // changeMesh(false); // no inflation const pointField p = mesh_.oldPoints(); diff --git a/src/dynamicMesh/polyTopoChange/polyMeshModifier/polyMeshModifier.H b/src/dynamicMesh/polyTopoChange/polyMeshModifier/polyMeshModifier.H index 62bc93bcfc5fc8e4da9c717636a60a080938a6a4..48f9936795e8400402a1f5ef8cf2ce8d47eae48c 100644 --- a/src/dynamicMesh/polyTopoChange/polyMeshModifier/polyMeshModifier.H +++ b/src/dynamicMesh/polyTopoChange/polyMeshModifier/polyMeshModifier.H @@ -55,7 +55,7 @@ namespace Foam // Forward Declarations class polyTopoChanger; -class polyTopoChange; +class batchPolyTopoChange; class mapPolyMesh; class polyMeshModifier; @@ -166,7 +166,7 @@ public: virtual bool changeTopology() const = 0; //- Insert the topological change instructions - virtual void setRefinement(polyTopoChange&) const = 0; + virtual void setRefinement(batchPolyTopoChange&) const = 0; //- Modify motion points to comply with the topological change virtual void modifyMotionPoints(pointField& motionPoints) const = 0; diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.C index 2c4cae838da812e90a188963908eadd266c7e42b..36f7ff456c58a33a6c8a99338dbc1f4ad25bd622 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.C @@ -77,6 +77,62 @@ void Foam::removeFaces::changeCellRegion } +Foam::label Foam::removeFaces::changeFaceRegion +( + const labelList& cellRegion, + const boolList& removedFace, + const labelList& nFacesPerEdge, + const label faceI, + const label newRegion, + + labelList& faceRegion +) const +{ + // Count number of changed faces + label nChanged = 0; + + if (faceRegion[faceI] == -1 && !removedFace[faceI]) + { + faceRegion[faceI] = newRegion; + + nChanged = 1; + + // Get mesh data + const labelListList& meshFaceEdges = mesh_.faceEdges(); + const labelListList& meshEdgeFaces = mesh_.edgeFaces(); + + // Step to neighbouring faces across edges that will get removed + const labelList& fEdges = meshFaceEdges[faceI]; + + forAll(fEdges, i) + { + const label& edgeI = fEdges[i]; + + if (nFacesPerEdge[edgeI] >= 0 && nFacesPerEdge[edgeI] <= 2) + { + const labelList& eFaces = meshEdgeFaces[edgeI]; + + forAll(eFaces, j) + { + nChanged += changeFaceRegion + ( + cellRegion, + removedFace, + nFacesPerEdge, + eFaces[j], + newRegion, + + faceRegion + ); + } + } + } + } + + return nChanged; +} + + // Changes region of connected set of faces. Returns number of changed faces. Foam::label Foam::removeFaces::changeFaceRegion ( @@ -231,187 +287,6 @@ void Foam::removeFaces::writeOBJ } -// Inserts commands to merge faceLabels into one face. -void Foam::removeFaces::mergeFaces -( - const labelList& cellRegion, - const labelList& cellRegionMaster, - const labelHashSet& pointsToRemove, - const labelList& faceLabels, - polyTopoChange& meshMod -) const -{ - // Construct addressing engine from faceLabels (in order of faceLabels as - // well) - indirectPrimitivePatch fp - ( - IndirectList<face> - ( - mesh_.faces(), - faceLabels - ), - mesh_.points() - ); - - // Get outside vertices (in local vertex numbering) - - if (fp.edgeLoops().size() != 1) - { - writeOBJ(fp, mesh_.time().path()/"facesToBeMerged.obj"); - FatalErrorInFunction - << "Cannot merge faces " << faceLabels - << " into single face since outside vertices " << fp.edgeLoops() - << " do not form single loop but form " << fp.edgeLoops().size() - << " loops instead." << abort(FatalError); - } - - const labelList& edgeLoop = fp.edgeLoops()[0]; - - // Get outside vertices in order of one of the faces in faceLabels. - // (this becomes the master face) - // Find the first face that uses edgeLoop[0] and edgeLoop[1] as consecutive - // vertices. - - label masterIndex = -1; - bool reverseLoop = false; - - const labelList& pFaces = fp.pointFaces()[edgeLoop[0]]; - - // Find face among pFaces which uses edgeLoop[1] - forAll(pFaces, i) - { - label facei = pFaces[i]; - - const face& f = fp.localFaces()[facei]; - - label index1 = f.find(edgeLoop[1]); - - if (index1 != -1) - { - // Check whether consecutive to edgeLoop[0] - label index0 = f.find(edgeLoop[0]); - - if (index0 != -1) - { - if (index1 == f.fcIndex(index0)) - { - masterIndex = facei; - reverseLoop = false; - break; - } - else if (index1 == f.rcIndex(index0)) - { - masterIndex = facei; - reverseLoop = true; - break; - } - } - } - } - - if (masterIndex == -1) - { - writeOBJ(fp, mesh_.time().path()/"facesToBeMerged.obj"); - FatalErrorInFunction - << "Problem" << abort(FatalError); - } - - - // Modify the master face. - // ~~~~~~~~~~~~~~~~~~~~~~~ - - // Modify first face. - label facei = faceLabels[masterIndex]; - - label own = mesh_.faceOwner()[facei]; - - if (cellRegion[own] != -1) - { - own = cellRegionMaster[cellRegion[own]]; - } - - label patchID, zoneID, zoneFlip; - - getFaceInfo(facei, patchID, zoneID, zoneFlip); - - label nei = -1; - - if (mesh_.isInternalFace(facei)) - { - nei = mesh_.faceNeighbour()[facei]; - - if (cellRegion[nei] != -1) - { - nei = cellRegionMaster[cellRegion[nei]]; - } - } - - - DynamicList<label> faceVerts(edgeLoop.size()); - - forAll(edgeLoop, i) - { - label pointi = fp.meshPoints()[edgeLoop[i]]; - - if (pointsToRemove.found(pointi)) - { - //Pout<< "**Removing point " << pointi << " from " - // << edgeLoop << endl; - } - else - { - faceVerts.append(pointi); - } - } - - face mergedFace; - mergedFace.transfer(faceVerts); - - if (reverseLoop) - { - reverse(mergedFace); - } - - //{ - // Pout<< "Modifying masterface " << facei - // << " from faces:" << faceLabels - // << " old verts:" << UIndirectList<face>(mesh_.faces(), faceLabels) - // << " for new verts:" - // << mergedFace - // << " possibly new owner " << own - // << " or new nei " << nei - // << endl; - //} - - modFace - ( - mergedFace, // modified face - facei, // label of face being modified - own, // owner - nei, // neighbour - false, // face flip - patchID, // patch for face - false, // remove from zone - zoneID, // zone for face - zoneFlip, // face flip in zone - - meshMod - ); - - - // Remove all but master face. - forAll(faceLabels, patchFacei) - { - if (patchFacei != masterIndex) - { - //Pout<< "Removing face " << faceLabels[patchFacei] << endl; - - meshMod.setAction(polyRemoveFace(faceLabels[patchFacei], facei)); - } - } -} - - // Get patch, zone info for facei void Foam::removeFaces::getFaceInfo ( @@ -471,91 +346,6 @@ Foam::face Foam::removeFaces::filterFace } -// Wrapper for meshMod.modifyFace. Reverses face if own>nei. -void Foam::removeFaces::modFace -( - const face& f, - const label masterFaceID, - const label own, - const label nei, - const bool flipFaceFlux, - const label newPatchID, - const bool removeFromZone, - const label zoneID, - const bool zoneFlip, - - polyTopoChange& meshMod -) const -{ - if ((nei == -1) || (own < nei)) - { -// if (debug) -// { -// Pout<< "ModifyFace (unreversed) :" -// << " facei:" << masterFaceID -// << " f:" << f -// << " own:" << own -// << " nei:" << nei -// << " flipFaceFlux:" << flipFaceFlux -// << " newPatchID:" << newPatchID -// << " removeFromZone:" << removeFromZone -// << " zoneID:" << zoneID -// << " zoneFlip:" << zoneFlip -// << endl; -// } - - meshMod.setAction - ( - polyModifyFace - ( - f, // modified face - masterFaceID, // label of face being modified - own, // owner - nei, // neighbour - flipFaceFlux, // face flip - newPatchID, // patch for face - removeFromZone, // remove from zone - zoneID, // zone for face - zoneFlip // face flip in zone - ) - ); - } - else - { -// if (debug) -// { -// Pout<< "ModifyFace (!reversed) :" -// << " facei:" << masterFaceID -// << " f:" << f.reverseFace() -// << " own:" << nei -// << " nei:" << own -// << " flipFaceFlux:" << flipFaceFlux -// << " newPatchID:" << newPatchID -// << " removeFromZone:" << removeFromZone -// << " zoneID:" << zoneID -// << " zoneFlip:" << zoneFlip -// << endl; -// } - - meshMod.setAction - ( - polyModifyFace - ( - f.reverseFace(),// modified face - masterFaceID, // label of face being modified - nei, // owner - own, // neighbour - flipFaceFlux, // face flip - newPatchID, // patch for face - removeFromZone, // remove from zone - zoneID, // zone for face - zoneFlip // face flip in zone - ) - ); - } -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // Construct from mesh @@ -759,766 +549,4 @@ Foam::label Foam::removeFaces::compatibleRemoves } -void Foam::removeFaces::setRefinement -( - const labelList& faceLabels, - const labelList& cellRegion, - const labelList& cellRegionMaster, - polyTopoChange& meshMod -) const -{ - if (debug) - { - faceSet facesToRemove(mesh_, "facesToRemove", faceLabels); - Pout<< "Writing faces to remove to faceSet " << facesToRemove.name() - << endl; - facesToRemove.write(); - } - - // Make map of all faces to be removed - boolList removedFace(mesh_.nFaces(), false); - - forAll(faceLabels, i) - { - label facei = faceLabels[i]; - - if (!mesh_.isInternalFace(facei)) - { - FatalErrorInFunction - << "Face to remove is not internal face:" << facei - << abort(FatalError); - } - - removedFace[facei] = true; - } - - - // Edges to be removed - // ~~~~~~~~~~~~~~~~~~~ - - - // Edges to remove - labelHashSet edgesToRemove(faceLabels.size()); - - // Per face the region it is in. -1 for removed faces, -2 for regions - // consisting of single face only. - labelList faceRegion(mesh_.nFaces(), -1); - - // Number of connected face regions - label nRegions = 0; - - // Storage for on-the-fly addressing - DynamicList<label> fe; - DynamicList<label> ef; - - - { - const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - - // Usage of edges by non-removed faces. - // See below about initialization. - labelList nFacesPerEdge(mesh_.nEdges(), -1); - - // Count usage of edges by non-removed faces. - forAll(faceLabels, i) - { - label facei = faceLabels[i]; - - const labelList& fEdges = mesh_.faceEdges(facei, fe); - - forAll(fEdges, i) - { - label edgeI = fEdges[i]; - - if (nFacesPerEdge[edgeI] == -1) - { - nFacesPerEdge[edgeI] = mesh_.edgeFaces(edgeI, ef).size()-1; - } - else - { - nFacesPerEdge[edgeI]--; - } - } - } - - // Count usage for edges not on faces-to-be-removed. - // Note that this only needs to be done for possibly coupled edges - // so we could choose to loop only over boundary faces and use faceEdges - // of those. - - forAll(mesh_.edges(), edgeI) - { - if (nFacesPerEdge[edgeI] == -1) - { - // Edge not yet handled in loop above so is not used by any - // face to be removed. - - const labelList& eFaces = mesh_.edgeFaces(edgeI, ef); - - if (eFaces.size() > 2) - { - nFacesPerEdge[edgeI] = eFaces.size(); - } - else if (eFaces.size() == 2) - { - // nFacesPerEdge already -1 so do nothing. - } - else - { - const edge& e = mesh_.edges()[edgeI]; - - FatalErrorInFunction - << "Problem : edge has too few face neighbours:" - << eFaces << endl - << "edge:" << edgeI - << " vertices:" << e - << " coords:" << mesh_.points()[e[0]] - << mesh_.points()[e[1]] - << abort(FatalError); - } - } - } - - - - if (debug) - { - OFstream str(mesh_.time().path()/"edgesWithTwoFaces.obj"); - Pout<< "Dumping edgesWithTwoFaces to " << str.name() << endl; - label vertI = 0; - - forAll(nFacesPerEdge, edgeI) - { - if (nFacesPerEdge[edgeI] == 2) - { - // Edge will get removed. - const edge& e = mesh_.edges()[edgeI]; - - meshTools::writeOBJ(str, mesh_.points()[e[0]]); - vertI++; - meshTools::writeOBJ(str, mesh_.points()[e[1]]); - vertI++; - str<< "l " << vertI-1 << ' ' << vertI << nl; - } - } - } - - - // Now all unaffected edges will have labelMax, all affected edges the - // number of unremoved faces. - - // Filter for edges inbetween two remaining boundary faces that - // make too big an angle. - forAll(nFacesPerEdge, edgeI) - { - if (nFacesPerEdge[edgeI] == 2) - { - // Get the two face labels - label f0 = -1; - label f1 = -1; - - const labelList& eFaces = mesh_.edgeFaces(edgeI, ef); - - forAll(eFaces, i) - { - label facei = eFaces[i]; - - if (!removedFace[facei]) - { - if (f0 == -1) - { - f0 = facei; - } - else - { - f1 = facei; - break; - } - } - } - - if (!mesh_.isInternalFace(f0) && !mesh_.isInternalFace(f1)) - { - // Edge has two boundary faces remaining. - // See if should be merged. - - label patch0 = patches.whichPatch(f0); - label patch1 = patches.whichPatch(f1); - - if (patch0 != patch1) - { - // Different patches. Do not merge edge. - WarningInFunction - << "not merging faces " << f0 << " and " - << f1 << " across patch boundary edge " << edgeI - << endl; - - // Mark so it gets preserved - nFacesPerEdge[edgeI] = 3; - } - else if (minCos_ < 1 && minCos_ > -1) - { - const polyPatch& pp0 = patches[patch0]; - const vectorField& n0 = pp0.faceNormals(); - - if - ( - mag - ( - n0[f0 - pp0.start()] - & n0[f1 - pp0.start()] - ) - < minCos_ - ) - { - WarningInFunction - << "not merging faces " << f0 << " and " - << f1 << " across edge " << edgeI - << endl; - - // Angle between two remaining faces too large. - // Mark so it gets preserved - nFacesPerEdge[edgeI] = 3; - } - } - } - else if (mesh_.isInternalFace(f0) != mesh_.isInternalFace(f1)) - { - const edge& e = mesh_.edges()[edgeI]; - - // Only found one boundary face. Problem. - FatalErrorInFunction - << "Problem : edge would have one boundary face" - << " and one internal face using it." << endl - << "Your remove pattern is probably incorrect." << endl - << "edge:" << edgeI - << " nFaces:" << nFacesPerEdge[edgeI] - << " vertices:" << e - << " coords:" << mesh_.points()[e[0]] - << mesh_.points()[e[1]] - << " face0:" << f0 - << " face1:" << f1 - << abort(FatalError); - } - else - { - // Both kept faces are internal. Mark edge for preserving - // if inbetween different cells. If inbetween same cell - // pair we probably want to merge them to - // - avoid upper-triangular ordering problems - // - allow hex unrefinement (expects single face inbetween - // cells) - - const edge ownEdge - ( - cellRegion[mesh_.faceOwner()[f0]], - cellRegion[mesh_.faceNeighbour()[f0]] - ); - const edge neiEdge - ( - cellRegion[mesh_.faceOwner()[f1]], - cellRegion[mesh_.faceNeighbour()[f1]] - ); - - if (ownEdge != neiEdge) - { - nFacesPerEdge[edgeI] = 3; - } - } - } - } - - - - // Check locally (before synchronizing) for strangeness - forAll(nFacesPerEdge, edgeI) - { - if (nFacesPerEdge[edgeI] == 1) - { - const edge& e = mesh_.edges()[edgeI]; - - FatalErrorInFunction - << "Problem : edge would get 1 face using it only" - << " edge:" << edgeI - << " nFaces:" << nFacesPerEdge[edgeI] - << " vertices:" << e - << " coords:" << mesh_.points()[e[0]] - << ' ' << mesh_.points()[e[1]] - << abort(FatalError); - } - // Could check here for boundary edge with <=1 faces remaining. - } - - - // Synchronize edge usage. This is to make sure that both sides remove - // (or not remove) an edge on the boundary at the same time. - // - // Coupled edges (edge0, edge1 are opposite each other) - // a. edge not on face to be removed, edge has >= 3 faces - // b. ,, edge has 2 faces - // c. edge has >= 3 remaining faces - // d. edge has 2 remaining faces (assume angle>minCos already handled) - // - // - a + a: do not remove edge - // - a + b: do not remove edge - // - a + c: do not remove edge - // - a + d: do not remove edge - // - // - b + b: do not remove edge - // - b + c: do not remove edge - // - b + d: remove edge - // - // - c + c: do not remove edge - // - c + d: do not remove edge - // - d + d: remove edge - // - // - // So code situation a. with >= 3 - // b. with -1 - // c. with >=3 - // d. with 2 - // then do max and check result. - // - // a+a : max(3,3) = 3. do not remove - // a+b : max(3,-1) = 3. do not remove - // a+c : max(3,3) = 3. do not remove - // a+d : max(3,2) = 3. do not remove - // - // b+b : max(-1,-1) = -1. do not remove - // b+c : max(-1,3) = 3. do not remove - // b+d : max(-1,2) = 2. remove - // - // c+c : max(3,3) = 3. do not remove - // c+d : max(3,2) = 3. do not remove - // - // d+d : max(2,2) = 2. remove - - syncTools::syncEdgeList - ( - mesh_, - nFacesPerEdge, - maxEqOp<label>(), - labelMin // guaranteed to be overridden by maxEqOp - ); - - // Convert to labelHashSet - forAll(nFacesPerEdge, edgeI) - { - if (nFacesPerEdge[edgeI] == 0) - { - // 0: edge not used anymore. - edgesToRemove.insert(edgeI); - } - else if (nFacesPerEdge[edgeI] == 1) - { - // 1: illegal. Tested above. - } - else if (nFacesPerEdge[edgeI] == 2) - { - // 2: merge faces. - edgesToRemove.insert(edgeI); - } - } - - if (debug) - { - OFstream str(mesh_.time().path()/"edgesToRemove.obj"); - Pout<< "Dumping edgesToRemove to " << str.name() << endl; - label vertI = 0; - - for (const label edgei : edgesToRemove) - { - // Edge will get removed. - const edge& e = mesh_.edges()[edgei]; - - meshTools::writeOBJ(str, mesh_.points()[e[0]]); - vertI++; - meshTools::writeOBJ(str, mesh_.points()[e[1]]); - vertI++; - str<< "l " << vertI-1 << ' ' << vertI << nl; - } - } - - - // Walk to fill faceRegion with faces that will be connected across - // edges that will be removed. - - label startFacei = 0; - - while (true) - { - // Find unset region. - for (; startFacei < mesh_.nFaces(); startFacei++) - { - if (faceRegion[startFacei] == -1 && !removedFace[startFacei]) - { - break; - } - } - - if (startFacei == mesh_.nFaces()) - { - break; - } - - // Start walking face-edge-face, crossing edges that will get - // removed. Every thus connected region will get single region - // number. - label nRegion = changeFaceRegion - ( - cellRegion, - removedFace, - nFacesPerEdge, - startFacei, - nRegions, - mesh_.faceEdges(startFacei, fe), - faceRegion - ); - - if (nRegion < 1) - { - FatalErrorInFunction << "Problem" << abort(FatalError); - } - else if (nRegion == 1) - { - // Reset face to be single region. - faceRegion[startFacei] = -2; - } - else - { - nRegions++; - } - } - - - // Check we're deciding the same on both sides. Since the regioning - // is done based on nFacesPerEdge (which is synced) this should - // indeed be the case. - - labelList nbrFaceRegion(faceRegion); - - syncTools::swapFaceList - ( - mesh_, - nbrFaceRegion - ); - - labelList toNbrRegion(nRegions, -1); - - for - ( - label facei = mesh_.nInternalFaces(); - facei < mesh_.nFaces(); - facei++ - ) - { - // Get the neighbouring region. - label nbrRegion = nbrFaceRegion[facei]; - label myRegion = faceRegion[facei]; - - if (myRegion <= -1 || nbrRegion <= -1) - { - if (nbrRegion != myRegion) - { - FatalErrorInFunction - << "Inconsistent face region across coupled patches." - << endl - << "This side has for facei:" << facei - << " region:" << myRegion << endl - << "The other side has region:" << nbrRegion - << endl - << "(region -1 means face is to be deleted)" - << abort(FatalError); - } - } - else if (toNbrRegion[myRegion] == -1) - { - // First visit of region. Store correspondence. - toNbrRegion[myRegion] = nbrRegion; - } - else - { - // Second visit of this region. - if (toNbrRegion[myRegion] != nbrRegion) - { - FatalErrorInFunction - << "Inconsistent face region across coupled patches." - << endl - << "This side has for facei:" << facei - << " region:" << myRegion - << " with coupled neighbouring regions:" - << toNbrRegion[myRegion] << " and " - << nbrRegion - << abort(FatalError); - } - } - } - } - - //if (debug) - //{ - // labelListList regionToFaces(invertOneToMany(nRegions, faceRegion)); - // - // forAll(regionToFaces, regionI) - // { - // Pout<< " " << regionI << " faces:" << regionToFaces[regionI] - // << endl; - // } - //} - - - // Points to be removed - // ~~~~~~~~~~~~~~~~~~~~ - - labelHashSet pointsToRemove(4*faceLabels.size()); - - - // Per point count the number of unremoved edges. Store the ones that - // are only used by 2 unremoved edges. - { - // Usage of points by non-removed edges. - labelList nEdgesPerPoint(mesh_.nPoints()); - - const labelListList& pointEdges = mesh_.pointEdges(); - - forAll(pointEdges, pointi) - { - nEdgesPerPoint[pointi] = pointEdges[pointi].size(); - } - - for (const label edgei : edgesToRemove) - { - // Edge will get removed. - const edge& e = mesh_.edges()[edgei]; - - forAll(e, i) - { - nEdgesPerPoint[e[i]]--; - } - } - - // Check locally (before synchronizing) for strangeness - forAll(nEdgesPerPoint, pointi) - { - if (nEdgesPerPoint[pointi] == 1) - { - FatalErrorInFunction - << "Problem : point would get 1 edge using it only." - << " pointi:" << pointi - << " coord:" << mesh_.points()[pointi] - << abort(FatalError); - } - } - - // Synchronize point usage. This is to make sure that both sides remove - // (or not remove) a point on the boundary at the same time. - syncTools::syncPointList - ( - mesh_, - nEdgesPerPoint, - maxEqOp<label>(), - labelMin - ); - - forAll(nEdgesPerPoint, pointi) - { - if (nEdgesPerPoint[pointi] == 0) - { - pointsToRemove.insert(pointi); - } - else if (nEdgesPerPoint[pointi] == 1) - { - // Already checked before - } - else if (nEdgesPerPoint[pointi] == 2) - { - // Remove point and merge edges. - pointsToRemove.insert(pointi); - } - } - } - - - if (debug) - { - OFstream str(mesh_.time().path()/"pointsToRemove.obj"); - Pout<< "Dumping pointsToRemove to " << str.name() << endl; - - for (const label pointi : pointsToRemove) - { - meshTools::writeOBJ(str, mesh_.points()[pointi]); - } - } - - - // All faces affected in any way - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Get all faces affected in any way by removal of points/edges/faces/cells - boolList affectedFace - ( - getFacesAffected - ( - cellRegion, - cellRegionMaster, - faceLabels, - edgesToRemove, - pointsToRemove - ) - ); - - // - // Now we know - // - faceLabels : faces to remove (sync since no boundary faces) - // - cellRegion/Master : cells to remove (sync since cells) - // - pointsToRemove : points to remove (sync) - // - faceRegion : connected face region of faces to be merged (sync) - // - affectedFace : faces with points removed and/or owner/neighbour - // changed (non sync) - - - // Start modifying mesh and keep track of faces changed. - - - // Do all removals - // ~~~~~~~~~~~~~~~ - - // Remove split faces. - forAll(faceLabels, labelI) - { - label facei = faceLabels[labelI]; - - // Remove face if not yet uptodate (which is never; but want to be - // consistent with rest of face removals/modifications) - if (affectedFace[facei]) - { - affectedFace[facei] = false; - - meshMod.setAction(polyRemoveFace(facei, -1)); - } - } - - - // Remove points. - for (const label pointi : pointsToRemove) - { - meshMod.setAction(polyRemovePoint(pointi, -1)); - } - - - // Remove cells. - forAll(cellRegion, celli) - { - label region = cellRegion[celli]; - - if (region != -1 && (celli != cellRegionMaster[region])) - { - meshMod.setAction(polyRemoveCell(celli, cellRegionMaster[region])); - } - } - - - - // Merge faces across edges to be merged - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Invert faceRegion so we get region to faces. - { - labelListList regionToFaces(invertOneToMany(nRegions, faceRegion)); - - forAll(regionToFaces, regionI) - { - const labelList& rFaces = regionToFaces[regionI]; - - if (rFaces.size() <= 1) - { - FatalErrorInFunction - << "Region:" << regionI - << " contains only faces " << rFaces - << abort(FatalError); - } - - // rFaces[0] is master, rest gets removed. - mergeFaces - ( - cellRegion, - cellRegionMaster, - pointsToRemove, - rFaces, - meshMod - ); - - forAll(rFaces, i) - { - affectedFace[rFaces[i]] = false; - } - } - } - - - // Remaining affected faces - // ~~~~~~~~~~~~~~~~~~~~~~~~ - - // Check if any remaining faces have not been updated for new slave/master - // or points removed. - forAll(affectedFace, facei) - { - if (affectedFace[facei]) - { - affectedFace[facei] = false; - - face f(filterFace(pointsToRemove, facei)); - - label own = mesh_.faceOwner()[facei]; - - if (cellRegion[own] != -1) - { - own = cellRegionMaster[cellRegion[own]]; - } - - label patchID, zoneID, zoneFlip; - - getFaceInfo(facei, patchID, zoneID, zoneFlip); - - label nei = -1; - - if (mesh_.isInternalFace(facei)) - { - nei = mesh_.faceNeighbour()[facei]; - - if (cellRegion[nei] != -1) - { - nei = cellRegionMaster[cellRegion[nei]]; - } - } - -// if (debug) -// { -// Pout<< "Modifying " << facei -// << " old verts:" << mesh_.faces()[facei] -// << " for new verts:" << f -// << " or for new owner " << own << " or for new nei " -// << nei -// << endl; -// } - - modFace - ( - f, // modified face - facei, // label of face being modified - own, // owner - nei, // neighbour - false, // face flip - patchID, // patch for face - false, // remove from zone - zoneID, // zone for face - zoneFlip, // face flip in zone - - meshMod - ); - } - } -} - - // ************************************************************************* // diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.H b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.H index 20652eb4db8bff1a4c5891a2abbd05c8ed9ed650..4844fc77ece5626a71f6bbaa5fff2f88ce9b35c9 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.H +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFaces.H @@ -86,6 +86,19 @@ class removeFaces labelList& cellRegion ) const; + //- Changes region of connected set of faces. Returns number of changed + // faces. For use with batch topo change + label changeFaceRegion + ( + const labelList& cellRegion, + const boolList& removedFace, + const labelList& nFacesPerEdge, + const label faceI, + const label newRegion, + + labelList& faceRegion + ) const; + //- Changes region of connected set of faces label changeFaceRegion ( @@ -119,13 +132,15 @@ class removeFaces ); //- Merge faceLabels into single face. + template<class TopoChangeEngine> void mergeFaces ( const labelList& cellRegion, const labelList& cellRegionMaster, const labelHashSet& pointsToRemove, const labelList& faceLabels, - polyTopoChange& meshMod + + TopoChangeEngine& meshMod ) const; //- Get patch, zone info for facei @@ -141,6 +156,7 @@ class removeFaces face filterFace(const labelHashSet&, const label) const; //- Wrapper for meshMod.modifyFace. Reverses face if own>nei. + template<class TopoChangeEngine> void modFace ( const face& f, @@ -153,11 +169,10 @@ class removeFaces const label zoneID, const bool zoneFlip, - polyTopoChange& meshMod + TopoChangeEngine& meshMod ) const; - //- No copy construct removeFaces(const removeFaces&) = delete; @@ -199,12 +214,15 @@ public: //- Play commands into polyTopoChange to remove faces. + template<class TopoChangeEngine> void setRefinement ( const labelList& piercedFaces, const labelList& cellRegion, - const labelList& cellRegionMaster, - polyTopoChange& + // const labelList& pointRegionMaster, + labelList& cellRegionMaster, + + TopoChangeEngine& ) const; //- Force recalculation of locally stored data on topological change @@ -221,6 +239,10 @@ public: } // End namespace Foam +#ifdef NoRepository +# include "removeFacesTemplates.C" +#endif + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFacesTemplates.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFacesTemplates.C new file mode 100644 index 0000000000000000000000000000000000000000..856ab2fb416826c4b8145c54fb08afeac20d6de1 --- /dev/null +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/removeFacesTemplates.C @@ -0,0 +1,1112 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2008 Hrvoje Jasak +------------------------------------------------------------------------------- +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 "removeFaces.H" +#include "polyMesh.H" +#include "polyTopoChange.H" +#include "meshTools.H" +#include "polyModifyFace.H" +#include "polyRemoveFace.H" +#include "polyRemoveCell.H" +#include "polyRemovePoint.H" +#include "syncTools.H" +#include "OFstream.H" +#include "indirectPrimitivePatch.H" +#include "Time.H" +#include "faceSet.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +template<class TopoChangeEngine> +void Foam::removeFaces::mergeFaces +( + const labelList& cellRegion, + const labelList& cellRegionMaster, + const labelHashSet& pointsToRemove, + const labelList& faceLabels, + + TopoChangeEngine& ref +) const +{ + // Construct addressing engine from faceLabels (in order of faceLabels as + // well) + indirectPrimitivePatch fp + ( + IndirectList<face> + ( + mesh_.faces(), + faceLabels + ), + mesh_.points() + ); + + // Get outside vertices (in local vertex numbering) + if (fp.edgeLoops().size() != 1) + { + writeOBJ(fp, mesh_.time().path()/"facesToBeMerged.obj"); + + FatalErrorInFunction + << "Cannot merge faces " << faceLabels + << " into single face since outside vertices " << fp.edgeLoops() + << " do not form single loop but form " << fp.edgeLoops().size() + << " loops instead." << abort(FatalError); + } + + const labelList& edgeLoop = fp.edgeLoops()[0]; + + // Get outside vertices in order of one of the faces in faceLabels. + // (this becomes the master face) + // Find the first face that uses edgeLoop[0] and edgeLoop[1] as consecutive + // vertices. + + label masterIndex = -1; + bool reverseLoop = false; + + const labelList& pFaces = fp.pointFaces()[edgeLoop[0]]; + + // Find face among pFaces which uses edgeLoop[1] + forAll(pFaces, i) + { + const label& faceI = pFaces[i]; + + const face& f = fp.localFaces()[faceI]; + + label index1 = f.find(edgeLoop[1]); + + if (index1 != -1) + { + // Check whether consecutive to edgeLoop[0] + label index0 = f.find(edgeLoop[0]); + + if (index0 != -1) + { + if (index1 == f.fcIndex(index0)) + { + masterIndex = faceI; + reverseLoop = false; + break; + } + else if (index1 == f.rcIndex(index0)) + { + masterIndex = faceI; + reverseLoop = true; + break; + } + } + } + } + + if (masterIndex == -1) + { + writeOBJ(fp, mesh_.time().path()/"facesToBeMerged.obj"); + + FatalErrorInFunction + << "Problem." << abort(FatalError); + } + + + // Modify the master face + + // Modify first face + label faceI = faceLabels[masterIndex]; + + label own = mesh_.faceOwner()[faceI]; + + if (cellRegion[own] != -1) + { + own = cellRegionMaster[cellRegion[own]]; + } + + // Set face information + label patchID, zoneID, zoneFlip; + meshTools::setFaceInfo(mesh_, faceI, patchID, zoneID, zoneFlip); + + label nei = -1; + + if (mesh_.isInternalFace(faceI)) + { + nei = mesh_.faceNeighbour()[faceI]; + + if (cellRegion[nei] != -1) + { + nei = cellRegionMaster[cellRegion[nei]]; + } + } + + // Collect non-removed face vertices + DynamicList<label> faceVerts(edgeLoop.size()); + + // Get mesh points + const labelList& meshPoints = fp.meshPoints(); + + forAll(edgeLoop, i) + { + const label& pointI = meshPoints[edgeLoop[i]]; + + if (pointsToRemove.found(pointI)) + { + // Point should be removed, nothing to do + } + else + { + faceVerts.append(pointI); + } + } + + face mergedFace; + mergedFace.transfer(faceVerts); + faceVerts.clear(); + + if (reverseLoop) + { + // Reverse the merged face + reverse(mergedFace); + } + + // Finally modify merged face + modFace + ( + mergedFace, // modified face + faceI, // label of face being modified + own, // owner + nei, // neighbour + false, // face flip + patchID, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip, // face flip in zone + + ref // topo change engine + ); + + + // Remove all but master face + forAll(faceLabels, patchFaceI) + { + if (patchFaceI != masterIndex) + { + ref.setAction(polyRemoveFace(faceLabels[patchFaceI], faceI)); + } + } +} + + +template<class TopoChangeEngine> +void Foam::removeFaces::modFace +( + const face& f, + const label masterFaceID, + const label own, + const label nei, + const bool flipFaceFlux, + const label newPatchID, + const bool removeFromZone, + const label zoneID, + const bool zoneFlip, + + TopoChangeEngine& ref +) const +{ + // Print info with deep debug level only + if (debug > 1) + { + if (mesh_.isInternalFace(masterFaceID)) + { + Pout<< "Modifying face " << masterFaceID + << ", old verts: " << mesh_.faces()[masterFaceID] + << " for new verts:" << f + << nl + << " or for new owner " << own + << " (old owner: " << mesh_.faceOwner()[masterFaceID] << ")" + << nl + << " or for new nei " << nei << " (old neighbour: " + << mesh_.faceNeighbour()[masterFaceID] << ")" + << endl; + } + } + + if ((nei == -1) || (own < nei)) + { + // No need to revert the face, use original owner/neighbour + ref.setAction + ( + polyModifyFace + ( + f, // modified face + masterFaceID, // label of face being modified + own, // owner + nei, // neighbour + flipFaceFlux, // face flip + newPatchID, // patch for face + removeFromZone, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); + } + else + { + // Revert the face and flip owner/neighbour + ref.setAction + ( + polyModifyFace + ( + f.reverseFace(),// modified face + masterFaceID, // label of face being modified + nei, // owner + own, // neighbour + flipFaceFlux, // face flip + newPatchID, // patch for face + removeFromZone, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); + } +} + + +template<class TopoChangeEngine> +void Foam::removeFaces::setRefinement +( + const labelList& faceLabels, + const labelList& cellRegion, + // const labelList& pointRegionMaster, + labelList& cellRegionMaster, + + TopoChangeEngine& ref +) const +{ + if (debug) + { + const faceSet facesToRemove + ( + mesh_, + "facesToRemove", + labelHashSet(faceLabels) + ); + + Pout<< "Writing faces to remove to faceSet " << facesToRemove.name() + << endl; + + facesToRemove.write(); + } + + // Get number of mesh faces + const label nFaces = mesh_.nFaces(); + + // Mark-up field for all faces that need to be removed + boolList removedFace(nFaces, false); + forAll(faceLabels, i) + { + const label& faceI = faceLabels[i]; + + if (!mesh_.isInternalFace(faceI)) + { + FatalErrorInFunction + << "Face " << faceI << " is not an internal faces, therefore" + << " it cannot be removed. Check faceLabels argument." + << abort(FatalError); + } + + removedFace[faceI] = true; + } + + + // PART 1: Collect edges to be removed + labelHashSet edgesToRemove(faceLabels.size()); + + // Region for each face: + // -1 = removed faces + // -2 = regions consisting of single face only + labelList faceRegion(nFaces, -1); + + // Number of connected face regions + label nRegions = 0; + + { + // Number of edges per non-removed face. See below regarding + // initialization + labelList nFacesPerEdge(mesh_.nEdges(), -1); + + // Get necessary mesh data + const labelListList& meshFaceEdges = mesh_.faceEdges(); + const labelListList& meshEdgeFaces = mesh_.edgeFaces(); + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + + // Count usage of edges by non-removed faces. + forAll(faceLabels, i) + { + // Get face Index and edges of this face + const label& faceI = faceLabels[i]; + const labelList& fEdges = meshFaceEdges[faceI]; + + forAll(fEdges, j) + { + // Get edge index + const label& edgeI = fEdges[j]; + + if (nFacesPerEdge[edgeI] == -1) + { + // Number of faces for this edge is not set, set to size - 1 + nFacesPerEdge[edgeI] = meshEdgeFaces[edgeI].size() - 1; + } + else + { + // Decrement number of faces for this edge + --nFacesPerEdge[edgeI]; + } + } + } + + // Count usage for edges not on faces-to-be-removed. + // Note that this only needs to be done for possibly coupled edges + // so we could choose to loop only over boundary faces and use faceEdges + // of those. + forAll(meshEdgeFaces, edgeI) + { + if (nFacesPerEdge[edgeI] == -1) + { + // Edge not yet handled in loop above so is not used by any + // face to be removed. Get edge faces + const labelList& eFaces = meshEdgeFaces[edgeI]; + + // Number of faces for this edge is greater than 2, set size + if (eFaces.size() > 2) + { + nFacesPerEdge[edgeI] = eFaces.size(); + } + else if (eFaces.size() == 2) + { + // nFacesPerEdge already -1 so do nothing + } + else + { + // Error: edge has less than two faces. Print additional + // information and issue an error + + // Get edge and all its cells + const edge& e = mesh_.edges()[edgeI]; + const labelListList& ec = mesh_.edgeCells(); + + // Get point cells + const labelListList& pc = mesh_.pointCells(); + + // Write mesh before termination + mesh_.write(); + + // Write data for debugging + FatalErrorInFunction + << "Problem : edge has too few face neighbours:" + << eFaces << endl + << "edge:" << edgeI + << " vertices:" << e + << " coords:" << mesh_.points()[e[0]] + << mesh_.points()[e[1]] + << endl + << "Edge cells: " << ec[edgeI] << nl + << "First point cells: " << pc[e[0]] << nl + << "Second point cells: " << pc[e[1]] << nl + << abort(FatalError); + } + } + } + + + if (debug) + { + // Write edges with two faces + OFstream str(mesh_.time().path()/"edgesWithTwoFaces.obj"); + + Pout<< "Writing edgesWithTwoFaces to " << str.name() << endl; + + // Vertex counter + label vertI = 0; + + // Get mesh points and edges + const pointField& meshPoints = mesh_.points(); + const edgeList& meshEdges = mesh_.edges(); + + forAll(nFacesPerEdge, edgeI) + { + if (nFacesPerEdge[edgeI] == 2) + { + // Edge will get removed, write data + const edge& e = meshEdges[edgeI]; + + meshTools::writeOBJ(str, meshPoints[e[0]]); + ++vertI; + meshTools::writeOBJ(str, meshPoints[e[1]]); + ++vertI; + str<< "l " << vertI - 1 << ' ' << vertI << nl; + } + } + } + + + // At this point, all affected edges have the number of unremoved faces + + // Filter for edges inbetween two remaining boundary faces that + // make too big an angle. + forAll(nFacesPerEdge, edgeI) + { + if (nFacesPerEdge[edgeI] == 2) + { + // See if these are two boundary faces + label f0 = -1; + label f1 = -1; + + const labelList& eFaces = meshEdgeFaces[edgeI]; + + forAll(eFaces, i) + { + const label& faceI = eFaces[i]; + + if (!removedFace[faceI] && !mesh_.isInternalFace(faceI)) + { + if (f0 == -1) + { + f0 = faceI; + } + else + { + f1 = faceI; + break; + } + } + } + + if (f0 != -1 && f1 != -1) + { + // Edge has two boundary faces remaining, see if they should + // be merged. + const label patch0 = patches.whichPatch(f0); + const label patch1 = patches.whichPatch(f1); + + if (patch0 != patch1) + { + // Different patches. Do not merge edge. + WarningIn("removeFaces::setRefinement") + << "Not merging faces " << f0 << " and " + << f1 << " across patch boundary edge " << edgeI + << " since they are not on the same patch." + << endl; + + // Set number of faces to 3 to preserve the face + nFacesPerEdge[edgeI] = 3; + } + else if (minCos_ < 1 && minCos_ > -1) + { + const polyPatch& pp0 = patches[patch0]; + const vectorField& n0 = pp0.faceNormals(); + + if + ( + mag + ( + n0[f0 - pp0.start()] + & n0[f1 - pp0.start()] + ) + < minCos_ + ) + { + WarningIn("removeFaces::setRefinement") + << "Not merging faces " << f0 << " and " + << f1 << " across edge " << edgeI + << " since the angle between them is too large." + << endl; + + // Set number of faces to 3 to preserve the face + nFacesPerEdge[edgeI] = 3; + } + } + } + else if (f0 != -1 || f1 != -1) + { + const edge& e = mesh_.edges()[edgeI]; + + // Only found one boundary face. Problem + FatalErrorInFunction + << "Problem : edge would have one boundary face" + << " and one internal face using it." << endl + << "Your remove pattern is probably incorrect." << endl + << "edge:" << edgeI + << " nFaces:" << nFacesPerEdge[edgeI] + << " vertices:" << e + << " coords:" << mesh_.points()[e[0]] + << mesh_.points()[e[1]] + << " face0:" << f0 + << " face1:" << f1 + << abort(FatalError); + } + } + } + + + // Check locally (before synchronizing) for strangeness + forAll(nFacesPerEdge, edgeI) + { + if (nFacesPerEdge[edgeI] == 1) + { + const edge& e = mesh_.edges()[edgeI]; + + FatalErrorInFunction + << "Problem : edge would get 1 face using it only" + << " edge:" << edgeI + << " nFaces:" << nFacesPerEdge[edgeI] + << " vertices:" << e + << " coords:" << mesh_.points()[e[0]] + << ' ' << mesh_.points()[e[1]] + << abort(FatalError); + } + + // Could check here for boundary edge with <= 1 faces remaining + } + + + // Synchronize edge usage. This is to make sure that both sides remove + // (or not remove) an edge on the boundary at the same time. + // + // Coupled edges (edge0, edge1 are opposite each other) + // a. edge not on face to be removed, edge has >= 3 faces + // b. ,, edge has 2 faces + // c. edge has >= 3 remaining faces + // d. edge has 2 remaining faces (assume angle>minCos already handled) + // + // - a + a: do not remove edge + // - a + b: do not remove edge + // - a + c: do not remove edge + // - a + d: do not remove edge + // + // - b + b: do not remove edge + // - b + c: do not remove edge + // - b + d: remove edge + // + // - c + c: do not remove edge + // - c + d: do not remove edge + // - d + d: remove edge + // + // + // So code situation a. with >= 3 + // b. with -1 + // c. with >=3 + // d. with 2 + // then do max and check result. + // + // a+a : max(3,3) = 3. do not remove + // a+b : max(3,-1) = 3. do not remove + // a+c : max(3,3) = 3. do not remove + // a+d : max(3,2) = 3. do not remove + // + // b+b : max(-1,-1) = -1. do not remove + // b+c : max(-1,3) = 3. do not remove + // b+d : max(-1,2) = 2. remove + // + // c+c : max(3,3) = 3. do not remove + // c+d : max(3,2) = 3. do not remove + // + // d+d : max(2,2) = 2. remove + + syncTools::syncEdgeList + ( + mesh_, + nFacesPerEdge, + maxEqOp<label>(), + labelMin // guaranteed to be overridden by maxEqOp + ); + + // Convert to labelHashSet + forAll(nFacesPerEdge, edgeI) + { + if (nFacesPerEdge[edgeI] == 0) + { + // 0: edge not used anymore. + edgesToRemove.insert(edgeI); + } + else if (nFacesPerEdge[edgeI] == 1) + { + // 1: illegal. Tested above. + } + else if (nFacesPerEdge[edgeI] == 2) + { + // 2: merge faces. + edgesToRemove.insert(edgeI); + } + } + + if (debug) + { + // Write edges to remove + OFstream str(mesh_.time().path()/"edgesToRemove.obj"); + + Pout<< "Writing edgesToRemove to " << str.name() << endl; + + // Vertex counter + label vertI = 0; + + // Get mesh points + const pointField& meshPoints = mesh_.points(); + + forAllConstIter(labelHashSet, edgesToRemove, iter) + { + // Edge will get removed. + const edge& e = mesh_.edges()[iter.key()]; + + meshTools::writeOBJ(str, meshPoints[e[0]]); + ++vertI; + meshTools::writeOBJ(str, meshPoints[e[1]]); + ++vertI; + str<< "l " << vertI - 1 << ' ' << vertI << nl; + } + } + + // Walk to fill faceRegion with faces that will be connected across + // edges that will be removed + + label startFaceI = 0; + + while (true) + { + // Find unset region + for (; startFaceI < nFaces; ++startFaceI) + { + if (faceRegion[startFaceI] == -1 && !removedFace[startFaceI]) + { + break; + } + } + + if (startFaceI == nFaces) + { + break; + } + + // Start walking face-edge-face, crossing edges that will get + // removed. Every thus connected region will get single region + // number. + label nRegion = changeFaceRegion + ( + cellRegion, + removedFace, + nFacesPerEdge, + startFaceI, + nRegions, + + faceRegion + ); + + if (nRegion < 1) + { + FatalErrorInFunction + << "Problem with region number." << abort(FatalError); + } + else if (nRegion == 1) + { + // Reset face to be single region + faceRegion[startFaceI] = -2; + } + else + { + ++nRegions; + } + } + + // Check we're deciding the same on both sides. Since the regioning + // is done based on nFacesPerEdge (which is synced) this should + // indeed be the case. + + // Create a copy of face region list and swap it + labelList nbrFaceRegion(faceRegion); + syncTools::swapFaceList + ( + mesh_, + nbrFaceRegion + ); + + // Store data from neighbouring region + labelList toNbrRegion(nRegions, -1); + + // Get number of internal faces + const label nInternalFaces = mesh_.nInternalFaces(); + + for + ( + label faceI = nInternalFaces; + faceI < nFaces; + ++faceI + ) + { + // Get the neighbouring region + const label& nbrRegion = nbrFaceRegion[faceI]; + const label& myRegion = faceRegion[faceI]; + + if (myRegion <= -1 || nbrRegion <= -1) + { + if (nbrRegion != myRegion) + { + FatalErrorInFunction + << "Inconsistent face region across coupled patches." + << endl + << "This side has for faceI:" << faceI + << " region:" << myRegion << endl + << "The other side has region:" << nbrRegion + << endl + << "(region - 1 means face is to be deleted)" + << abort(FatalError); + } + } + else if (toNbrRegion[myRegion] == -1) + { + // First visit of region. Store correspondence. + toNbrRegion[myRegion] = nbrRegion; + } + else + { + // Second visit of this region + if (toNbrRegion[myRegion] != nbrRegion) + { + FatalErrorInFunction + << "Inconsistent face region across coupled patches." + << endl + << "This side has for faceI:" << faceI + << " region:" << myRegion + << " with coupled neighbouring regions:" + << toNbrRegion[myRegion] << " and " + << nbrRegion + << abort(FatalError); + } + } + } + } // End memory management + + + // PART 2: Collect points to remove + + // Create a hash set of points to remove, assuming that each face has 4 + // points to prevent excessive resizing + labelHashSet pointsToRemove(4*faceLabels.size()); + + // Memory management + { + // For each point, count the number of edges that will be kept + // (unremoved edges). Store the ones that are only used by exactly two + // unremoved edges. + + // Get necessary mesh data + const label nPoints = mesh_.nPoints(); + const labelListList& meshPointEdges = mesh_.pointEdges(); + const edgeList& meshEdges = mesh_.edges(); + + // List containing number of nonremoved edges for each point + labelList nEdgesPerPoint(nPoints); + + // Initialise to number of edges per point + forAll(meshPointEdges, pointI) + { + nEdgesPerPoint[pointI] = meshPointEdges[pointI].size(); + } + + // Loop through edges to remove + forAllConstIter(labelHashSet, edgesToRemove, iter) + { + // Get edge to be removed + const edge& e = meshEdges[iter.key()]; + + // Loop through both points of the edge and decrement the counter of + // unremoved edges per point + forAll(e, i) + { + --nEdgesPerPoint[e[i]]; + } + } + + // Check locally (before synchronizing) for strangeness + forAll(nEdgesPerPoint, pointI) + { + if (nEdgesPerPoint[pointI] == 1) + { + FatalErrorInFunction + << "Problem : point would get 1 edge using it only." + << " pointI:" << pointI + << " coord:" << mesh_.points()[pointI] + << abort(FatalError); + } + } + + // Synchronize point usage. This is to make sure that both sides remove + // (or don't remove) a point at the boundary at the same time. + syncTools::syncPointList + ( + mesh_, + nEdgesPerPoint, + maxEqOp<label>(), + labelMin + ); + + forAll(nEdgesPerPoint, pointI) + { + if (nEdgesPerPoint[pointI] == 0) + { + pointsToRemove.insert(pointI); + } + else if (nEdgesPerPoint[pointI] == 1) + { + // Already checked before + } + else if (nEdgesPerPoint[pointI] == 2) + { + // Remove point and merge edges + pointsToRemove.insert(pointI); + } + } + } + + + if (debug) + { + // Write points to remove + OFstream str(mesh_.time().path()/"pointsToRemove.obj"); + Pout<< "Writing pointsToRemove to " << str.name() << endl; + + // Get mesh points + const pointField& meshPoints = mesh_.points(); + + forAllConstIter(labelHashSet, pointsToRemove, iter) + { + meshTools::writeOBJ(str, meshPoints[iter.key()]); + } + } + + + // PART 3: Collect all faces affected in any way by removal of points, + // edges, faces and cells + + // Note: return type of affectedFaces is Xfer<boolList>, so there is no + // unnecessary copying + boolList affectedFace + ( + getFacesAffected + ( + cellRegion, + cellRegionMaster, + faceLabels, + edgesToRemove, + pointsToRemove + ) + ); + + // Now the data is complete: + // - faceLabels : faces to remove (sync since no boundary faces) + // - cellRegion/Master : cells to remove (sync since cells) + // - pointsToRemove : points to remove (sync) + // - faceRegion : connected face region of faces to be merged (sync) + // - affectedFace : faces with points removed and/or owner/neighbour + // changed (non sync) + // We can start inserting mesh modifier instructions and keep track of + // changed faces. + + + // PART 4: Do all removals first + + // Remove split faces + forAll(faceLabels, labelI) + { + // Get face index + const label& faceI = faceLabels[labelI]; + + // Remove face if not yet uptodate (which can't happen here; but want to + // be consistent with rest of face removals/modifications) + if (affectedFace[faceI]) + { + // Mark face as unaffected + affectedFace[faceI] = false; + + // Insert face removal instruction into topo change engine + ref.setAction(polyRemoveFace(faceI, -1)); + } + } + + // Remove points + forAllConstIter(labelHashSet, pointsToRemove, iter) + { + // Get point index + const label& pointI = iter.key(); + + // Inser point removal instruction into topo change engine + ref.setAction(polyRemovePoint(pointI, -1)); + } + +/* + OpenFOAM does not support adequate mapping. + HJ, 20/Jul/2023 + // Add master cells for correct mapping + forAll (cellRegionMaster, regionI) + { + // Note: it is legal to have cellRegionMaster = -1 if the region + // has been created and them marged into another region. + // Such masters will also have pointRegionMaster = -1 and should + // be ignored. HJ, 6/Sep/2019 + + // Additionally protect for old directTopoChangers which do not + // identify points for mapping. Non-existent pointRegionMaster + // is rejected + if (cellRegionMaster[regionI] > -1 && pointRegionMaster[regionI] > -1) + { + // Add master cell from master point for correct mapping + cellRegionMaster[regionI] = + ref.setAction + ( + polyAddCell + ( + pointRegionMaster[regionI], // masterPointID + -1, // masterEdgeID + -1, // masterFaceID + -1, // masterCellID + mesh_.cellZones().whichZone(cellRegionMaster[regionI]) + ) + ); + } + } + */ + + // Remove cells + forAll(cellRegion, cellI) + { + label region = cellRegion[cellI]; + + // Old check is acceptable: for mapping from point, the cellRegionMaster + // has been replaced in polyAddPoint + // HJ, 6/Sep/2019 + if (region != -1 && (cellI != cellRegionMaster[region])) + { + ref.setAction(polyRemoveCell(cellI, cellRegionMaster[region])); + } + } + + + // PART 5: Merge faces across edges to be merged + + // Memory management + { + // Invert faceRegion so we get region to faces + labelListList regionToFaces(invertOneToMany(nRegions, faceRegion)); + + // Loop through regions + forAll(regionToFaces, regionI) + { + // Get region faces + const labelList& rFaces = regionToFaces[regionI]; + + if (rFaces.size() <= 1) + { + FatalErrorInFunction + << "Region: " << regionI + << " contains only these faces: " << rFaces + << abort(FatalError); + } + + // rFaces[0] is master, rest get removed + mergeFaces + ( + cellRegion, + cellRegionMaster, + pointsToRemove, + rFaces, + ref + ); + + // Mark region faces as visited (non affected anymore) + forAll(rFaces, i) + { + affectedFace[rFaces[i]] = false; + } + } + } + + + // PART 6: Remaining affected faces + + // Get necessary mesh data + const labelList& owner = mesh_.faceOwner(); + const labelList& neighbour = mesh_.faceNeighbour(); + + + // Check any remaining faces that have not been updated for either new + // owner/neighbour or points removed + forAll(affectedFace, faceI) + { + if (affectedFace[faceI]) + { + // Mark face as unaffected + affectedFace[faceI] = false; + + // Get filtered face: without points that will be removed + const face f(filterFace(pointsToRemove, faceI)); + + // Get owner of the face and change it + label own = owner[faceI]; + if (cellRegion[own] != -1) + { + own = cellRegionMaster[cellRegion[own]]; + } + + // Set face information + label patchID, zoneID, zoneFlip; + meshTools::setFaceInfo(mesh_, faceI, patchID, zoneID, zoneFlip); + + // Get neighbour of the face and change it + label nei = -1; + if (mesh_.isInternalFace(faceI)) + { + nei = neighbour[faceI]; + if (cellRegion[nei] != -1) + { + nei = cellRegionMaster[cellRegion[nei]]; + } + } + + // Finally modify the face + modFace + ( + f, // modified face + faceI, // label of face being modified + own, // owner + nei, // neighbour + false, // face flip + patchID, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip, // face flip in zone + + ref // topo change engine + ); + } + } +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.C b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.C index 022de1d90db2fa53fc1212a4ca3f59c653381be9..5006832f60f042858c55b49c0bc17ecf220428b0 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.C @@ -28,7 +28,9 @@ License #include "polyTopoChanger.H" #include "polyMesh.H" +#include "mapPolyMesh.H" #include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "Time.H" #include "PtrListOps.H" @@ -192,14 +194,14 @@ bool Foam::polyTopoChanger::changeTopology() const } -Foam::autoPtr<Foam::polyTopoChange> +Foam::autoPtr<Foam::batchPolyTopoChange> Foam::polyTopoChanger::topoChangeRequest() const { // Collect changes from all modifiers const PtrList<polyMeshModifier>& topoChanges = *this; - auto ptr = autoPtr<polyTopoChange>::New(mesh()); - polyTopoChange& ref = ptr.ref(); + auto ptr = autoPtr<batchPolyTopoChange>::New(mesh()); + batchPolyTopoChange& ref = ptr.ref(); forAll(topoChanges, morphI) { @@ -245,35 +247,35 @@ void Foam::polyTopoChanger::update(const mapPolyMesh& m) } -Foam::autoPtr<Foam::mapPolyMesh> Foam::polyTopoChanger::changeMesh -( - const bool inflate, - const bool syncParallel, - const bool orderCells, - const bool orderPoints -) -{ - if (changeTopology()) - { - autoPtr<polyTopoChange> ref = topoChangeRequest(); - - autoPtr<mapPolyMesh> topoChangeMap = ref().changeMesh - ( - mesh_, - inflate, - syncParallel, - orderCells, - orderPoints - ); - - update(topoChangeMap()); - mesh_.updateMesh(topoChangeMap()); - return topoChangeMap; - } - - mesh_.topoChanging(false); - return nullptr; -} +// Foam::autoPtr<Foam::mapPolyMesh> Foam::polyTopoChanger::changeMesh +// ( +// const bool inflate, +// const bool syncParallel, +// const bool orderCells, +// const bool orderPoints +// ) +// { +// if (changeTopology()) +// { +// autoPtr<polyTopoChange> ref = topoChangeRequest(); + +// autoPtr<mapPolyMesh> topoChangeMap = ref().changeMesh +// ( +// mesh_, +// inflate, +// syncParallel, +// orderCells, +// orderPoints +// ); + +// update(topoChangeMap()); +// mesh_.updateMesh(topoChangeMap()); +// return topoChangeMap; +// } + +// mesh_.topoChanging(false); +// return nullptr; +// } void Foam::polyTopoChanger::addTopologyModifiers @@ -326,6 +328,13 @@ Foam::label Foam::polyTopoChanger::findModifierID } +Foam::label Foam::polyTopoChanger::morphIndex() const +{ + return morphIndex_; +} + + +// writeData member function required by regIOobject bool Foam::polyTopoChanger::writeData(Ostream& os) const { os << *this; diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.H b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.H index 8ed5efdda1bbdd29d5bb97952b663b3227ba6cc9..be9d14c98973c942f488aa0022e3c5ad5873bbff 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.H +++ b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChanger.H @@ -51,6 +51,7 @@ namespace Foam class polyMesh; class mapPolyMesh; class polyBoundaryMesh; +class polyTopoChange; class polyTopoChanger; Ostream& operator<<(Ostream&, const polyTopoChanger&); @@ -65,6 +66,12 @@ class polyTopoChanger public PtrList<polyMeshModifier>, public regIOobject { + // Private data + + //- Current time index for mesh morph + label morphIndex_; + + // Private Member Functions //- Read if IOobject flags set, set modifiers. Return true if read. @@ -84,6 +91,33 @@ protected: //- Reference to mesh polyMesh& mesh_; + + // Protected Member Functions + + // Topology changes + + //- Rotate a face nShift positions in anticlockwise direction + static face rotateFace(const face& f, const label nShift); + + //- Determine ordering of faces in coupled patches. + // Calculate mapping to shuffle faces inside coupled patches and + // rotation to make 0th vertex in faces align. + static bool reorderCoupledPatches + ( + const polyBoundaryMesh& boundary, + const labelList& patchStarts, + const labelList& patchSizes, + const faceList& faces, + const pointField& points, + labelList& faceMap, + labelList& rotation + ); + + //- Sync communications required for couple patch reordering when + // there is no local topological change + void syncCoupledPatches(); + + public: //- Runtime type information @@ -125,19 +159,27 @@ public: bool changeTopology() const; //- Return topology change request - autoPtr<polyTopoChange> topoChangeRequest() const; + autoPtr<batchPolyTopoChange> topoChangeRequest() const; //- Modify point motion void modifyMotionPoints(pointField&) const; - autoPtr<mapPolyMesh> changeMesh + // autoPtr<mapPolyMesh> changeMesh + // ( + // const bool inflate, + // const bool syncParallel = true, + // const bool orderCells = false, + // const bool orderPoints = false + // ); + + static autoPtr<mapPolyMesh> changeMesh ( - const bool inflate, - const bool syncParallel = true, - const bool orderCells = false, - const bool orderPoints = false + polyMesh&, + const batchPolyTopoChange& ); + autoPtr<mapPolyMesh> changeMesh(); + //- Force recalculation of locally stored data on topological change void update(const mapPolyMesh& m); @@ -147,6 +189,9 @@ public: //- Find modifier given a name label findModifierID(const word& modName) const; + //- Return morph index + label morphIndex() const; + //- writeData member function required by regIOobject bool writeData(Ostream&) const; diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChangerChangeMesh.C b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChangerChangeMesh.C new file mode 100644 index 0000000000000000000000000000000000000000..30f2bdce0e6529f065f92ac81ef1a38edc89c1dc --- /dev/null +++ b/src/dynamicMesh/polyTopoChange/polyTopoChanger/polyTopoChangerChangeMesh.C @@ -0,0 +1,2431 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2002 Hrvoje Jasak +------------------------------------------------------------------------------- +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 "polyTopoChanger.H" +#include "polyMesh.H" +#include "Time.H" +#include "faceZone.H" +#include "batchPolyTopoChange.H" +#include "pointField.H" +#include "polyAddPoint.H" +#include "polyModifyPoint.H" +#include "polyAddFace.H" +#include "polyModifyFace.H" +#include "Map.H" +#include "DynamicList.H" +#include "primitiveMesh.H" +#include "mapPolyMesh.H" +#include "objectMap.H" +#include "globalMeshData.H" +#include "ListOps.H" + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::face Foam::polyTopoChanger::rotateFace +( + const face& f, + const label nPos +) +{ + face newF(f.size()); + + forAll (f, fp) + { + label fp1 = (fp + nPos) % f.size(); + + if (fp1 < 0) + { + fp1 += f.size(); + } + + newF[fp1] = f[fp]; + } + + return newF; +} + + +// Determine ordering of faces in coupled patches. +// Calculate mapping to shuffle faces inside coupled patches and +// rotation to make 0th vertex in faces align. +bool Foam::polyTopoChanger::reorderCoupledPatches +( + const polyBoundaryMesh& boundary, + const labelList& patchStarts, + const labelList& patchSizes, + const faceList& faces, + const pointField& points, + + labelList& faceMap, + labelList& rotation +) +{ + // Mapping for faces (old to new). Extends over all mesh faces for + // convenience (could be just the external faces) + + // faceMap = identity(faces.size()); + // faceMap for boundary faces will be forwarded to polyPatch::order(...) + for (label i = 0; i < patchStarts[0]; i++) + { + faceMap[i] = i; + } + + + // Rotation on new faces. + rotation.setSize(faces.size()); + rotation = 0; + + PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); + + // Send ordering + forAll (boundary, patchI) + { + boundary[patchI].initOrder + ( + pBufs, + primitivePatch + ( + SubList<face> + ( + faces, + patchSizes[patchI], + patchStarts[patchI] + ), + points + ) + ); + } + + pBufs.finishedSends(); + + // Receive and calculate ordering + + bool anyChanged = false; + + forAll (boundary, patchI) + { + labelList patchFaceMap(patchSizes[patchI], -1); + labelList patchFaceRotation(patchSizes[patchI], 0); + + // Forward boundary faceMap to polyPatch::order(...) + label start = patchStarts[patchI]; + forAll (patchFaceMap, patchFaceI) + { + patchFaceMap[patchFaceI] = + faceMap[patchFaceI + start]; + + faceMap[patchFaceI + start] = patchFaceI + start; + } + + bool changed = boundary[patchI].order + ( + pBufs, + primitivePatch + ( + SubList<face> + ( + faces, + patchSizes[patchI], + patchStarts[patchI] + ), + points + ), + patchFaceMap, + patchFaceRotation + ); + + if (changed) + { + // Merge patch face reordering into mesh face reordering table + label start = patchStarts[patchI]; + + forAll (patchFaceMap, patchFaceI) + { + if (patchFaceMap[patchFaceI] == -1) + { + SeriousErrorInFunction + << "Could not determine correspondence for coupled " + << " face " << start+patchFaceI + << " on patch " << patchI << endl + << "Continuing with incorrect ordering." << endl; + return false; + } + + faceMap[patchFaceI + start] = start + patchFaceMap[patchFaceI]; + } + + forAll (patchFaceRotation, patchFaceI) + { + rotation[patchFaceI + start] = patchFaceRotation[patchFaceI]; + } + + anyChanged = true; + } + } + + return anyChanged; +} + + +// void Foam::polyTopoChanger::syncCoupledPatches() +// { +// // Sync communications required for couple patch reordering when +// // there is no local topological change + +// // Send ordering +// const polyPatchList& boundary = mesh_.boundaryMesh(); + +// PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking); + +// forAll (boundary, patchI) +// { +// boundary[patchI].initOrder(pBufs, boundary[patchI]); +// } + +// pBufs.finishedSends(); + +// // Receive ordering + +// // Note: HJ using syncOrder +// forAll (boundary, patchI) +// { +// boundary[patchI].order(); +// } +// } + + +Foam::autoPtr<Foam::mapPolyMesh> Foam::polyTopoChanger::changeMesh +( + polyMesh& mesh, + const batchPolyTopoChange& ref +) +{ + // Create a new list of points + // Note. Modified points are only influenced in the mesh motion stage + if (debug) + { + Pout<< "polyTopoChanger::changeMesh" << nl + << '(' << nl + << " const batchPolyTopoChange& ref" << nl + << ") : started executing topological change." << nl << endl; + + // Check topological change request for consistency and report + // the statistics of the refinement request + if (ref.check()) + { + FatalErrorInFunction + << "Inconsistent topological change request." + << abort(FatalError); + } + } + + const pointField& points = mesh.points(); + const faceList& faces = mesh.faces(); + const cellList& cells = mesh.cells(); + const polyBoundaryMesh& boundary = mesh.boundaryMesh(); + pointZoneMesh& pointZones = mesh.pointZones(); + faceZoneMesh& faceZones = mesh.faceZones(); + cellZoneMesh& cellZones = mesh.cellZones(); + + + // Grab old live mesh sizes + const label nOldPoints = mesh.nPoints(); + const label nOldFaces = mesh.nFaces(); + const label nOldCells = mesh.nCells(); + + // Keep the old patch start labels + labelList oldPatchStarts(boundary.size()); + forAll (boundary, patchi) + { + oldPatchStarts[patchi] = boundary[patchi].start(); + } + + pointField newPointsZeroVol(points.size() + ref.pointBalance()); + pointField newPointsMotion(points.size() + ref.pointBalance()); + + // renumberPoints contains the new point label for every old + // and added point + labelList renumberPoints(points.size() + ref.addedPoints().size(), -1); + + // pointMap contains the old point label or the master point label + // for all new points + labelList pointMap(points.size() + ref.addedPoints().size(), -1); + label nNewPoints = 0; + + const labelHashSet& removedPoints = ref.removedPoints(); + const labelHashSet& removedFaces = ref.removedFaces(); + const labelHashSet& removedCells = ref.removedCells(); + + // Grab the untouched live points + for (label pointI = 0; pointI < nOldPoints; pointI++) + { + // Check if the point has been removed; if not add it to the list + if (!removedPoints.found(pointI)) + { + // Grab a point + newPointsZeroVol[nNewPoints] = points[pointI]; + newPointsMotion[nNewPoints] = points[pointI]; + + // Grab addressing + renumberPoints[pointI] = nNewPoints; + pointMap[nNewPoints] = pointI; + + nNewPoints++; + } + } + + label debugPointCounter = 0; + + if (debug) + { + Pout<< "Added untouched points. Point count = " + << nNewPoints << endl; + + debugPointCounter = nNewPoints; + } + + // Change the modified points in two passes: first points + // supporting the cells and then auxiliary points + const DynamicList<polyModifyPoint>& mp = ref.modifiedPoints(); + + forAll (mp, mpI) + { + if (mp[mpI].inCell()) + { + // Grab a point + newPointsZeroVol[nNewPoints] = points[mp[mpI].pointID()]; + newPointsMotion[nNewPoints] = mp[mpI].newPoint(); + + // Grab addressing + renumberPoints[mp[mpI].pointID()] = nNewPoints; + pointMap[nNewPoints] = mp[mpI].pointID(); + nNewPoints++; + } + } + + if (debug) + { + Pout<< "Added live points: modified = " + << nNewPoints - debugPointCounter; + + debugPointCounter = nNewPoints; + } + + // Add the new points in two passes: first points supporting the cells + // and then auxiliary points + const DynamicList<polyAddPoint>& ap = ref.addedPoints(); + + // Follow the location in the renumbering list + const label np = points.size(); + + // Grab points supporting cells + forAll (ap, apI) + { + if (ap[apI].inCell()) + { + // Grab a point + if (ap[apI].appended()) + { + // Point appended directly; no motion + newPointsZeroVol[nNewPoints] = ap[apI].newPoint(); + } + else + { + // Point added on top of another point + newPointsZeroVol[nNewPoints] = points[ap[apI].masterPointID()]; + } + + newPointsMotion[nNewPoints] = ap[apI].newPoint(); + + // Grab addressing + renumberPoints[np + apI] = nNewPoints; + pointMap[nNewPoints] = ap[apI].masterPointID(); + nNewPoints++; + } + } + + if (debug) + { + Pout<< " added = " << nNewPoints - debugPointCounter + << ". Point count = " + << nNewPoints << endl; + + debugPointCounter = nNewPoints; + } + + // Grab the untouched auxiliary points + for (label pointI = nOldPoints; pointI < points.size(); pointI++) + { + // Check if the point has been removed; if not add it to the list + if (!removedPoints.found(pointI)) + { + // Grab a point + newPointsZeroVol[nNewPoints] = points[pointI]; + newPointsMotion[nNewPoints] = points[pointI]; + + // Grab addressing + renumberPoints[pointI] = nNewPoints; + pointMap[nNewPoints] = pointI; + + nNewPoints++; + } + } + + if (debug) + { + Pout<< "Added retired points: untouched = " + << nNewPoints - debugPointCounter; + + debugPointCounter = nNewPoints; + } + + // Grab auxiliary points + forAll (mp, mpI) + { + if (!mp[mpI].inCell()) + { + // Grab a point + newPointsZeroVol[nNewPoints] = points[mp[mpI].pointID()]; + newPointsMotion[nNewPoints] = mp[mpI].newPoint(); + + // Grab addressing + renumberPoints[mp[mpI].pointID()] = nNewPoints; + pointMap[nNewPoints] = mp[mpI].pointID(); + nNewPoints++; + } + } + + if (debug) + { + Pout<< " modified = " + << nNewPoints - debugPointCounter; + + debugPointCounter = nNewPoints; + } + + forAll (ap, apI) + { + if (!ap[apI].inCell()) + { + // Grab a point + if (ap[apI].appended()) + { + // Point appended directly; no motion + newPointsZeroVol[nNewPoints] = ap[apI].newPoint(); + } + else + { + // Point added on top of another point + newPointsZeroVol[nNewPoints] = points[ap[apI].masterPointID()]; + } + + newPointsMotion[nNewPoints] = ap[apI].newPoint(); + + // Grab addressing + renumberPoints[np + apI] = nNewPoints; + pointMap[nNewPoints] = ap[apI].masterPointID(); + nNewPoints++; + } + } + + if (debug) + { + Pout<< " added = " << nNewPoints - debugPointCounter + << ". Point count = " + << nNewPoints << endl; + + debugPointCounter = nNewPoints; + } + + // Reset the size of point map + pointMap.setSize(nNewPoints); + + if (debug) + { + Pout<< "Added all points. Final point count = " + << nNewPoints << nl << endl; + } + + // Create a new list of faces + // Algorithm: go throught the list of original faces and + // distribute them to the cells, skipping the ones marked as + // removed. Then add the new and modified faces to the cells. + // Renumber the faces using the point renumbering map. + // Gather the internal faces by looping through the cell list and + // collecting faces + + const labelList& allOwn = mesh.faceOwner(); + const labelList& allNei = mesh.faceNeighbour(); + + List<DynamicList<face> > + cf(cells.size() + ref.addedCells().size()); + + List<DynamicList<label> > + cfLabels(cells.size() + ref.addedCells().size()); + + // Insert untouched internal faces + for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) + { + if (!removedFaces.found(faceI)) + { + cf[allOwn[faceI]].append(faces[faceI]); + cfLabels[allOwn[faceI]].append(faceI); + + cf[allNei[faceI]].append(faces[faceI]); + cfLabels[allNei[faceI]].append(faceI); + } + } + + if (debug) + { + Pout<< "Inserted untouched faces into cells" << endl; + } + + // Add the modified internal faces + const DynamicList<polyModifyFace>& mf = ref.modifiedFaces(); + + forAll (mf, mfI) + { + if (!mf[mfI].isInPatch() && !mf[mfI].onlyInZone()) + { + if (debug) + { + // Check if the internal face is defined properly + if + ( + min(mf[mfI].owner(), mf[mfI].neighbour()) < 0 + || max(mf[mfI].owner(), mf[mfI].neighbour()) >= cf.size() + ) + { + FatalErrorInFunction + << "Invalid modified face " << mf[mfI].faceID() + << ". Declared as internal but owner or neighbour " + << "are invalid." << nl + << "Owner: " << mf[mfI].owner() + << " Neighbour: " << mf[mfI].neighbour() + << " Max number of cells: " << cf.size() + << abort(FatalError); + } + } + + // Grab face and face label + cf[mf[mfI].owner()].append(mf[mfI].newFace()); + cfLabels[mf[mfI].owner()].append(mf[mfI].faceID()); + + cf[mf[mfI].neighbour()].append(mf[mfI].newFace()); + cfLabels[mf[mfI].neighbour()].append(mf[mfI].faceID()); + } + } + + if (debug) + { + Pout<< "Inserted modified faces into cells" << endl; + } + + // Add the new faces + const DynamicList<polyAddFace>& af = ref.addedFaces(); + + forAll (af, afI) + { + if (!af[afI].isInPatch() && !af[afI].onlyInZone()) + { + if (debug) + { + // Check if the internal face is defined properly + if + ( + min(af[afI].owner(), af[afI].neighbour()) < 0 + || max(af[afI].owner(), af[afI].neighbour()) >= cf.size() + ) + { + FatalErrorInFunction + << "Invalid added face " << faces.size() + afI + << ". Declared as internal but owner or neighbour " + << "are invalid." << nl + << "Owner: " << af[afI].owner() + << " Neighbour: " << af[afI].neighbour() + << " Max number of cells: " << cf.size() + << abort(FatalError); + } + } + + // Grab face and face label + cf[af[afI].owner()].append(af[afI].newFace()); + cfLabels[af[afI].owner()].append(faces.size() + afI); + + cf[af[afI].neighbour()].append(af[afI].newFace()); + cfLabels[af[afI].neighbour()].append(faces.size() + afI); + } + } + + if (debug) + { + Pout<< "Inserted added faces into cells" << endl; + } + + // All internal faces now exist on the list. Create the new face + // list using upper triangular search + + // Create partial point-cell addressing + List<DynamicList<label, primitiveMesh::facesPerPoint_> > PointCells + ( + points.size() + ref.addedPoints().size() + ); + + boolListList usedCellFaces(cf.size()); + + forAll (cf, cellI) + { + if (!removedCells.found(cellI)) + { + const DynamicList<face>& + curFaces = cf[cellI]; + + // Resize and reset the cell-face list at the same time + usedCellFaces[cellI].setSize(curFaces.size()); + usedCellFaces[cellI] = false; + + // Add the cell as a neighbour to all of the points + // of all of its faces + forAll (curFaces, faceI) + { + const labelList& curFacePoints = curFaces[faceI]; + + forAll (curFacePoints, pointI) + { + bool found = false; + + DynamicList<label, primitiveMesh::facesPerPoint_>& + curPointCells = PointCells[curFacePoints[pointI]]; + + forAll (curPointCells, i) + { + if (curPointCells[i] == cellI) + { + // Cell already a neighbour of this point + found = true; + break; + } + } + + if (!found) + { + curPointCells.append(cellI); + } + } + } + } + else + { + // Check if the removed cell has got any faces + if (cf[cellI].size() > 0) + { + FatalErrorInFunction + << "Cell " << cellI << " is marked as removed but still " + << "has faces. Cell faces: " << cf[cellI] + << abort(FatalError); + } + } + } + + List<DynamicList<label> > + newCellFaces(cf.size()); + + faceList newFaces(faces.size() + ref.faceBalance()); + + // renumberFaces contains a new face label for every old and added face + labelList renumberFaces(faces.size() + ref.addedFaces().size(), -1); + + // faceMap contains the old face label for every new face. For + // inserted faces, the label will be -1. Use with care! + labelList faceMap(faces.size() + ref.faceBalance(), -1); + label nNewFaces = 0; + + // Create ordered list of faces + + // Note: + // Insertion cannot be done in one go as the faces need to be + // added into the list in the increasing order of neighbour + // cells. Therefore, all neighbours will be detected first + // and then added in the correct order. + // Watch out. Subtly different from createPolyMesh! + + forAll (cf, cellI) + { + const DynamicList<face>& + curFaces = cf[cellI]; + labelList neiCells(curFaces.size(), -1); + label nNeighbours = 0; + + // For all faces ... + forAll (curFaces, faceI) + { + // Skip faces that have already been matched + if (usedCellFaces[cellI][faceI]) continue; + + bool found = false; + + const face& curFace = curFaces[faceI]; + + // Get the list of labels + const labelList& curPoints = curFace; + + // For all points + forAll (curPoints, pointI) + { + // Get the list of cells sharing this point + const DynamicList<label, primitiveMesh::facesPerPoint_>& + curNeighbours = PointCells[curPoints[pointI]]; + + // For all neighbours + forAll (curNeighbours, neiI) + { + label curNei = curNeighbours[neiI]; + + // Reject neighbours with the lower label + if (curNei > cellI) + { + // Get the list of search faces + const DynamicList<face>& + searchFaces = cf[curNei]; + + forAll (searchFaces, neiFaceI) + { + if (searchFaces[neiFaceI] == curFace) + { + // Match!! + found = true; + + // Record the neighbour cell and face + neiCells[faceI] = curNei; + nNeighbours++; + + break; + } + } + if (found) break; + } + if (found) break; + } + if (found) break; + } // End of current points + } // End of current faces + + // Add the faces in the increasing order of neighbours + for + ( + label neiSearch = 0; + neiSearch < nNeighbours; + neiSearch++ + ) + { + // Find the lowest neighbour which is still valid + label nextNei = -1; + label minNei = cf.size(); + + forAll (neiCells, ncI) + { + if (neiCells[ncI] > -1 && neiCells[ncI] < minNei) + { + nextNei = ncI; + minNei = neiCells[ncI]; + } + } + + if (nextNei > -1) + { + // Add the face to the list of faces + newFaces[nNewFaces] = curFaces[nextNei]; + + // Set cell-face and cell-neighbour-face to current face label + newCellFaces[cellI].append(nNewFaces); + newCellFaces[minNei].append(nNewFaces); + + // Grab the renumbering index + renumberFaces[cfLabels[cellI][nextNei]] = nNewFaces; + + if (cfLabels[cellI][nextNei] < faces.size()) + { + faceMap[nNewFaces] = + cfLabels[cellI][nextNei]; + } + + // Stop the neighbour from being used again + neiCells[nextNei] = -1; + + // Increment number of faces counter + nNewFaces++; + } + else + { + FatalErrorInFunction + << "Error in internal face insertion" + << abort(FatalError); + } + } + } + + label debugFaceCounter = 0; + + if (debug) + { + Pout<< "Added internal faces. Face count = " + << nNewFaces << endl; + + debugFaceCounter = nNewFaces; + } + + // Redistribute modified and newly added patch faces per patch + List<DynamicList<label, 10> > patchModifiedFaces(boundary.size()); + List<DynamicList<label, 10> > patchAddedFaces(boundary.size()); + + forAll (mf, mfI) + { + if (mf[mfI].isInPatch()) + { + patchModifiedFaces[mf[mfI].patchID()].append(mfI); + } + } + + forAll (af, afI) + { + if (af[afI].isInPatch()) + { + patchAddedFaces[af[afI].patchID()].append(afI); + } + } + + // For every patch add original patch faces which have not been removed + // Remember the new patch starts and sizes + labelList patchSizes(boundary.size(), 0); + labelList patchStarts(boundary.size(), -1); + + forAll (boundary, patchI) + { + // Add original patch faces + const label curSize = boundary[patchI].size(); + const label curStart = boundary[patchI].start(); + + const UList<face>& curFaces = boundary[patchI]; + + // Grab patch start + patchStarts[patchI] = nNewFaces; + + for (label faceI = curStart; faceI < curStart + curSize; faceI++) + { + if (!removedFaces.found(faceI)) + { + newFaces[nNewFaces] = curFaces[faceI - curStart]; + renumberFaces[faceI] = nNewFaces; + + faceMap[nNewFaces] = faceI; + + // Add the face to the owner cell + if (allOwn[faceI] >= 0) + { + newCellFaces[allOwn[faceI]].append(nNewFaces); + } + + nNewFaces++; + } + } + + if (debug) + { + Pout<< "Patch " << patchI + << ": added faces: untouched = " + << nNewFaces - debugFaceCounter; + + debugFaceCounter = nNewFaces; + } + + // Add new faces belonging to this patch + const DynamicList<label, 10>& modPatchFaces = + patchModifiedFaces[patchI]; + + forAll (modPatchFaces, faceI) + { + newFaces[nNewFaces] = mf[modPatchFaces[faceI]].newFace(); + renumberFaces[mf[modPatchFaces[faceI]].faceID()] = nNewFaces; + + faceMap[nNewFaces]= mf[modPatchFaces[faceI]].faceID(); + + label newOwner = mf[modPatchFaces[faceI]].owner(); + + // Add the face to the owner cell + if (newOwner >= 0) + { + newCellFaces[newOwner].append(nNewFaces); + } + + nNewFaces++; + } + + if (debug) + { + Pout<< " modified = " << nNewFaces - debugFaceCounter; + + debugFaceCounter = nNewFaces; + } + + // Add new faces belonging to this patch + const DynamicList<label, 10>& newPatchFaces = patchAddedFaces[patchI]; + + forAll (newPatchFaces, faceI) + { + newFaces[nNewFaces] = af[newPatchFaces[faceI]].newFace(); + renumberFaces[faces.size() + newPatchFaces[faceI]] = nNewFaces; + + label newOwner = af[newPatchFaces[faceI]].owner(); + + // Add the face to the owner cell + if (newOwner >= 0) + { + newCellFaces[newOwner].append(nNewFaces); + } + + nNewFaces++; + } + + if (debug) + { + Pout<< " added = " << nNewFaces - debugFaceCounter + << ". Face count = " << nNewFaces << endl; + + debugFaceCounter = nNewFaces; + } + + // Grab the new patch size + patchSizes[patchI] = nNewFaces - patchStarts[patchI]; + } + + // Add freely-standing faces to the back of the list + + // Freely-standing faces from the original mesh + // Insert untouched internal faces + for (label faceI = nOldFaces; faceI < faces.size(); faceI++) + { + if (!removedFaces.found(faceI)) + { + newFaces[nNewFaces] = faces[faceI]; + renumberFaces[faceI] = nNewFaces; + faceMap[nNewFaces]= faceI; + nNewFaces++; + } + } + + if (debug) + { + Pout<< "Added zone-only faces: untouched = " + << nNewFaces - debugFaceCounter; + + debugFaceCounter = nNewFaces; + } + + // Freely-standing modified faces + forAll (mf, mfI) + { + if (mf[mfI].onlyInZone()) + { + newFaces[nNewFaces] = mf[mfI].newFace(); + renumberFaces[mf[mfI].faceID()] = nNewFaces; + faceMap[nNewFaces]= mf[mfI].faceID(); + nNewFaces++; + } + } + + if (debug) + { + Pout<< " modified = " << nNewFaces - debugFaceCounter; + + debugFaceCounter = nNewFaces; + } + + // Freely-standing added faces + forAll (af, afI) + { + if (af[afI].onlyInZone()) + { + newFaces[nNewFaces] = af[afI].newFace(); + renumberFaces[faces.size() + afI] = nNewFaces; + nNewFaces++; + } + } + + if (debug) + { + Pout<< " added = " << nNewFaces - debugFaceCounter + << ". Final face count = " + << nNewFaces << nl << endl; + + debugFaceCounter = nNewFaces; + } + + if (debug) + { + if (nNewFaces != faces.size() + ref.faceBalance()) + { + FatalErrorInFunction + << "Error in face insertion. Number of inserted faces: " + << nNewFaces << ". Expected " + << faces.size() + ref.faceBalance() + << " faces." + << abort(FatalError); + } + } + + // Face list and cell faces completed. + // Renumber the faces using the point renumber list + forAll (newFaces, faceI) + { + face oldFace = newFaces[faceI]; + face& renumberedFace = newFaces[faceI]; + + forAll (renumberedFace, pointI) + { + renumberedFace[pointI] = renumberPoints[oldFace[pointI]]; + } + + if (debug) + { + // Check if the face has been mapped correctly + if + ( + renumberedFace.size() == 0 + || min(renumberedFace) < 0 + || max(renumberedFace) >= newPointsZeroVol.size() + ) + { + FatalErrorInFunction + << "Face " << faceI << " in the new mesh is not " + << "mapped correctly." << nl + << "It uses a removed or a non-existing vertex or " + << "has been skipped." << nl + << "Face before mapping: " << oldFace << " with points " + << oldFace.points(newPointsZeroVol) << nl + << mesh.points().size() << nl + << "Face after mapping: " << renumberedFace << nl + << "Max new vertex index: " + << newPointsZeroVol.size() - 1 << "." << nl + << "Are there extra faces in the face list that do not " + << "belong to a face zone? This is not allowed." + << abort(FatalError); + } + } + } + + + + // Coupled face ordering + // ~~~~~~~~~~~~~~~~~~~~~ + + // Update faceMap for added faces which have master face + forAll (af, afI) + { + if (af[afI].isFaceMaster()) // Face mastered by another face + { + faceMap[renumberFaces[faces.size() + afI]] = + af[afI].masterFaceID(); + } + } + + // At this point we have + // - newFaces: faces in upper-triangular and patch order + // - patchSizes, patchStarts: valid on newFaces + // - newCellFaces: consistent cell-face addressing + // - renumberFaces: from old to new face + // - faceMap: from new to old face + + // Calculate face map and rotation so coupled faces are matched + // correctly + + // labelList localFaceMap(newFaces.size()); + // Forward faceMap to polyPatch::order() (needed by cohesivePolyPatch) + labelList localFaceMap = faceMap; + labelList rotation(newFaces.size(), 0); + + bool anyChange = reorderCoupledPatches + ( + boundary, + patchStarts, + patchSizes, + newFaces, // new faces + newPointsMotion, // points after inflation + + localFaceMap, // for every face the new position + rotation // amount face needs to be rotated + ); + + // Reorder everything referring to faces (not patchStarts/sizes since + // faces only get reshuffled inside patches) + if (anyChange) + { + // Reorder newFaces according to localFaceMap + inplaceReorder(localFaceMap, newFaces); + + // Renumber newCellFaces so they refer to the new face ordering + forAll (newCellFaces, cellI) + { + DynamicList<label>& cFaces = newCellFaces[cellI]; + + cFaces.shrink(); + inplaceRenumber(localFaceMap, cFaces); + } + + // Renumber renumberFaces so they refer to the new face ordering + inplaceRenumber(localFaceMap, renumberFaces); + + // Reorder faceMap + inplaceReorder(localFaceMap, faceMap); + + // Rotate faces (rotation is already in new face indices). + forAll (rotation, faceI) + { + label rotate = rotation[faceI]; + + if (rotate != 0) + { + newFaces[faceI] = rotateFace(newFaces[faceI], rotate); + } + } + } + + + // Build the face-from maps + + List<objectMap> faceFromPoint(af.size()); + label nFaceFromPoint = 0; + List<objectMap> faceFromEdge(af.size()); + label nFaceFromEdge = 0; + + forAll (af, afI) + { + if (af[afI].isPointMaster()) + { + if (debug) + { + // Check that the master point index is in range + if + ( + af[afI].masterPointID() < 0 + || af[afI].masterPointID() >= mesh.nPoints() + ) + { + FatalErrorInFunction + << "Master point for face " << faces.size() + afI + << " is out of range: " << af[afI].masterPointID() + << ".\n Number of valid master points: " + << mesh.nPoints() + << abort(FatalError); + } + } + + if (af[afI].isInPatch()) + { + // Grab faces neighbouring the point which are in the + // same patch as the newly added face. + const labelList& pf = + mesh.pointFaces()[af[afI].masterPointID()]; + + labelList facesAroundPoint(pf.size()); + label nfap = 0; + + forAll (pf, pfI) + { + label wp = boundary.whichPatch(pf[pfI]); + if (wp == af[afI].patchID()) + { + facesAroundPoint[nfap] = pf[pfI]; + nfap++; + } + } + + if (debug) + { + if (nfap == 0) + { + FatalErrorInFunction + << "No patch face neighbours found for added " + << "patch face " << afI + << ".\nThere are no faces from patch " + << af[afI].patchID() + << " around the master point " + << af[afI].masterPointID() + << ".\n Bad choice of master point: " + << "error in mesh mapping." + << abort(FatalError); + } + } + + facesAroundPoint.setSize(nfap); + + faceFromPoint[nFaceFromPoint] = + objectMap + ( + renumberFaces[faces.size() + afI], + facesAroundPoint + ); + + nFaceFromPoint++; + } + else + { + // Grab internal faces around the point + const labelList& pf = + mesh.pointFaces()[af[afI].masterPointID()]; + + labelList facesAroundPoint(pf.size()); + label nfap = 0; + + forAll (pf, pfI) + { + if (mesh.isInternalFace(pf[pfI])) + { + facesAroundPoint[nfap] = pf[pfI]; + nfap++; + } + } + + if (debug) + { + if (nfap == 0 && mesh.nInternalFaces() > 0) + { + FatalErrorInFunction + << "No face neighbours found for added " + << "internal face " << afI + << ".\nThere are no internal faces " + << "around the master point " + << af[afI].masterPointID() + << ".\n Bad choice of master point: " + << "error in mesh mapping." + << abort(FatalError); + } + } + + facesAroundPoint.setSize(nfap); + + faceFromPoint[nFaceFromPoint] = + objectMap + ( + renumberFaces[faces.size() + afI], + facesAroundPoint + ); + + nFaceFromPoint++; + } + } + else if (af[afI].isEdgeMaster()) + { + if (debug) + { + // Check that the master edge index is in range + if + ( + af[afI].masterEdgeID() < 0 + || af[afI].masterEdgeID() >= mesh.nEdges() + ) + { + FatalErrorInFunction + << "Master edge for face " << faces.size() + afI + << " is out of range: " << af[afI].masterEdgeID() + << ".\n Number of valid master edges: " + << mesh.nEdges() + << abort(FatalError); + } + } + + if (af[afI].isInPatch()) + { + // Grab faces neighbouring the point which are in the + // same patch as the newly added face Note: the + // addressing is now into the patch instead of the + // global face list + const labelList& pe = + mesh.edgeFaces()[af[afI].masterEdgeID()]; + + labelList facesAroundEdge(pe.size()); + label nfae = 0; + + forAll (pe, peI) + { + label wp = boundary.whichPatch(pe[peI]); + if (wp == af[afI].patchID()) + { + facesAroundEdge[nfae] = pe[peI]; + nfae++; + } + } + + if (debug) + { + if (nfae == 0) + { + FatalErrorInFunction + << "No patch face neighbours found for added " + << "patch face " << afI + << ". Error in mesh mapping." + << abort(FatalError); + } + } + + facesAroundEdge.setSize(nfae); + + faceFromEdge[nFaceFromEdge] = + objectMap + ( + renumberFaces[faces.size() + afI], + facesAroundEdge + ); + + nFaceFromEdge++; + } + else + { + // Grab internal faces around the edge + const labelList& pe = + mesh.edgeFaces()[af[afI].masterEdgeID()]; + + labelList facesAroundEdge(pe.size()); + label nfae = 0; + + forAll (pe, peI) + { + if (mesh.isInternalFace(pe[peI])) + { + facesAroundEdge[nfae] = pe[peI]; + nfae++; + } + } + + if (debug) + { + if (nfae == 0) + { + FatalErrorInFunction + << "No patch face neighbours found for added " + << "internal face " << afI + << ". Error in mesh mapping." + << abort(FatalError); + } + } + + facesAroundEdge.setSize(nfae); + + faceFromEdge[nFaceFromEdge] = + objectMap + ( + renumberFaces[faces.size() + afI], + facesAroundEdge + ); + + nFaceFromEdge++; + } + } + else if (af[afI].isFaceMaster()) // Face mastered by another face + { + faceMap[renumberFaces[faces.size() + afI]] = + af[afI].masterFaceID(); + } + } + + // Reset the size of face mapping lists + faceFromPoint.setSize(nFaceFromPoint); + faceFromEdge.setSize(nFaceFromEdge); + + // Check face maps + if (debug) + { + boolList mappedFaces(faceMap.size(), false); + + // Fill in faces mapped from the face map + forAll (faceMap, faceI) + { + if (faceMap[faceI] >= 0) + { + mappedFaces[faceI] = true; + } + } + + // Fill in point and edge maps + forAll (faceFromPoint, faceI) + { + mappedFaces[faceFromPoint[faceI].index()] = true; + } + + forAll (faceFromEdge, faceI) + { + mappedFaces[faceFromEdge[faceI].index()] = true; + } + + // Check if all the faces are mapped + label nUnmappedFaces = 0; + + forAll (mappedFaces, faceI) + { + if (!mappedFaces[faceI]) + { + nUnmappedFaces++; + } + } + + if (nUnmappedFaces > 0) + { + Pout<< "void polyMesh::morph(const batchPolyTopoChange& ref) : " + << "unmapped data for " << nUnmappedFaces << " faces." << endl; + } + } + + labelHashSet flipFaceFlux(mf.size() + af.size()); + + // Build the flip flux map + forAll (mf, mfI) + { + if (mf[mfI].flipFaceFlux()) + { + flipFaceFlux.insert(renumberFaces[mf[mfI].faceID()]); + } + } + + forAll (af, afI) + { + if (af[afI].isFaceMaster() && af[afI].flipFaceFlux()) + { + flipFaceFlux.insert(renumberFaces[faces.size() + afI]); + } + } + + // Renumber the cells + + cellList newCells(cells.size() + ref.cellBalance()); + + // renumberCells holds the new cell label for all old and added cells + labelList renumberCells(cells.size() + ref.addedCells().size(), -1); + + // cellMap holds the old cell label for every preserved cell + labelList cellMap(cells.size() + ref.cellBalance(), -1); + + label nNewCells = 0; + + forAll (newCellFaces, cellI) + { + if (!removedCells.found(cellI)) + { + if (newCellFaces[cellI].size() < 4) + { + FatalErrorInFunction + << "Cell " << cellI << " has got three or less faces " + << "and has not been removed. " + << "This is not a valid cell." << endl + << "Cell faces: " << newCellFaces[cellI] + << abort(FatalError); + } + + // Make a cell + newCells[nNewCells].transfer(newCellFaces[cellI].shrink()); + + renumberCells[cellI] = nNewCells; + + // Add the cell into cell map if it is preserved + if (cellI < nOldCells) + { + cellMap[nNewCells] = cellI; + } + + nNewCells++; + } + } + + if (debug) + { + Pout<< "Added all cells. Final cell count = " + << nNewCells << nl << endl; + } + + label nPreservedCells = cells.size() - removedCells.size(); + + // Build the cell-from maps + + const DynamicList<polyAddCell>& ac = ref.addedCells(); + const DynamicList<polyModifyCell>& mc = ref.modifiedCells(); + + List<objectMap> cellFromPoint(ac.size()); + label nCellFromPoint = 0; + + List<objectMap> cellFromEdge(ac.size()); + label nCellFromEdge = 0; + + List<objectMap> cellFromFace(ac.size()); + label nCellFromFace = 0; + + forAll (ac, acI) + { + if (ac[acI].isPointMaster()) + { + if (debug) + { + // Check that the master point index is in range + if + ( + ac[acI].masterPointID() < 0 + || ac[acI].masterPointID() >= mesh.nPoints() + ) + { + FatalErrorInFunction + << "Master point for cell " << nPreservedCells + acI + << " is out of range: " << ac[acI].masterPointID() + << ".\n Number of valid master points: " + << mesh.nPoints() + << abort(FatalError); + } + } + + cellFromPoint[nCellFromPoint] = + objectMap + ( + nPreservedCells + acI, + mesh.pointCells()[ac[acI].masterPointID()] + ); + + nCellFromPoint++; + } + else if (ac[acI].isEdgeMaster()) + { + if (debug) + { + // Check that the master edge index is in range + if + ( + ac[acI].masterEdgeID() < 0 + || ac[acI].masterEdgeID() >= mesh.nEdges() + ) + { + FatalErrorInFunction + << "Master edge for cell " << nPreservedCells + acI + << " is out of range: " << ac[acI].masterEdgeID() + << ".\n Number of valid master edges: " + << mesh.nEdges() + << abort(FatalError); + } + } + + cellFromEdge[nCellFromEdge] = + objectMap + ( + nPreservedCells + acI, + mesh.edgeCells()[ac[acI].masterEdgeID()] + ); + + nCellFromEdge++; + } + else if (ac[acI].isFaceMaster()) // Cell mastered by a face + { + if (debug) + { + // Check that the master face index is in range + if + ( + ac[acI].masterFaceID() < 0 + || ac[acI].masterFaceID() >= mesh.nFaces() + ) + { + FatalErrorInFunction + << "Master face for cell " << nPreservedCells + acI + << " is out of range: " << ac[acI].masterFaceID() + << ".\n Number of valid master faces: " + << mesh.nFaces() + << abort(FatalError); + } + } + + labelList cellsAroundFace(2, label(-1)); + + cellsAroundFace[0] = mesh.faceOwner()[ac[acI].masterFaceID()]; + + if (mesh.isInternalFace(ac[acI].masterFaceID())) + { + cellsAroundFace[1] = + mesh.faceNeighbour()[ac[acI].masterFaceID()]; + } + else + { + cellsAroundFace.setSize(1); + } + + cellFromFace[nCellFromFace] = + objectMap + ( + nPreservedCells + acI, + cellsAroundFace + ); + + nCellFromFace++; + } + else if (ac[acI].isCellMaster()) // Cell mastered by another cell + { + cellMap[renumberCells[cells.size() + acI]] = + ac[acI].masterCellID(); + } + } + + // Reset the size of cell mapping lists + cellFromPoint.setSize(nCellFromPoint); + cellFromEdge.setSize(nCellFromEdge); + cellFromFace.setSize(nCellFromFace); + + // Check cell maps + if (debug) + { + boolList mappedCells(newCells.size(), false); + + // Fill in cells mapped from the cell map + forAll (cellMap, cellI) + { + if (cellMap[cellI] >= 0) + { + mappedCells[cellI] = true; + } + } + + // Fill in point and edge maps + forAll (cellFromPoint, cellI) + { + mappedCells[cellFromPoint[cellI].index()] = true; + } + + forAll (cellFromEdge, cellI) + { + mappedCells[cellFromEdge[cellI].index()] = true; + } + + forAll (cellFromFace, cellI) + { + mappedCells[cellFromFace[cellI].index()] = true; + } + + // Check if all the cells are mapped + label nUnmappedCells = 0; + + forAll (mappedCells, cellI) + { + if (!mappedCells[cellI]) + { + nUnmappedCells++; + } + } + + if (nUnmappedCells > 0) + { + Pout<< "void polyMesh::morph(const batchPolyTopoChange& ref) : " + << "unmapped data for " << nUnmappedCells << " cells." << endl; + } + } + + + // Rebuild the mesh + // ~~~~~~~~~~~~~~~~ + + // Grab patch mesh point maps + + List<Map<label> > oldPatchMeshPointMaps(boundary.size()); + labelList oldPatchNMeshPoints(boundary.size()); + + forAll (boundary, patchI) + { + // Copy old face zone mesh point maps + oldPatchMeshPointMaps[patchI] = boundary[patchI].meshPointMap(); + oldPatchNMeshPoints[patchI] = boundary[patchI].meshPoints().size(); + } + + // Re-do the point zones + + // Make a map of points to be removed from zones + labelHashSet removePointFromZone(2*ref.modifiedPoints().size()); + + forAll (mp, mpI) + { + if (mp[mpI].removeFromZone()) + { + removePointFromZone.insert(mp[mpI].pointID()); + } + } + + labelListList newPointZoneAddr(pointZones.size()); + labelList nPointsInZone(pointZones.size(), 0); + + forAll (pointZones, pzI) + { + // Get the list of old points + const labelList& oldAddr = pointZones[pzI]; + + // Create new addressing, over-estimating the size + labelList& newAddr = newPointZoneAddr[pzI]; + label& curNPoints = nPointsInZone[pzI]; + + newAddr.setSize + ( + oldAddr.size() + + ref.modifiedPoints().size() + + ref.addedPoints().size() + ); + + // Add the original points that have not been removed or re-zoned + forAll (oldAddr, pointI) + { + if + ( + !removedPoints.found(oldAddr[pointI]) + && !removePointFromZone.found(oldAddr[pointI]) + ) + { + // The point is still alive. Add its renumbered label + newAddr[curNPoints] = renumberPoints[oldAddr[pointI]]; + curNPoints++; + } + } + } + + labelList debugPointsInZone(pointZones.size(), 0); + + if (debug) + { + Pout<< "Added zone points: untouched = " << nPointsInZone; + + debugPointsInZone = nPointsInZone; + } + + // Distribute modified zone points + forAll (mp, mpI) + { + if (mp[mpI].isInZone()) + { + const label zoneID = mp[mpI].zoneID(); + + newPointZoneAddr[zoneID][nPointsInZone[zoneID]] = + renumberPoints[mp[mpI].pointID()]; + + nPointsInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " modified = " << nPointsInZone - debugPointsInZone; + } + + // Distribute added zone points + forAll (ap, apI) + { + if (ap[apI].isInZone()) + { + const label zoneID = ap[apI].zoneID(); + + newPointZoneAddr[zoneID][nPointsInZone[zoneID]] = + renumberPoints[points.size() + apI]; + + nPointsInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " added = " << nPointsInZone - debugPointsInZone + << ". Points per zone = " << nPointsInZone << endl; + } + + // Reset the sizes of the point zone addressing + forAll (newPointZoneAddr, pzI) + { + newPointZoneAddr[pzI].setSize(nPointsInZone[pzI]); + } + + // Build the point zone renumbering + labelListList pzRenumber(pointZones.size()); + + forAll (pointZones, pzI) + { + pointZone& oldZone = pointZones[pzI]; + const labelList& newZoneAddr = newPointZoneAddr[pzI]; + + labelList& curPzRnb = pzRenumber[pzI]; + curPzRnb.setSize(newZoneAddr.size()); + + forAll (newZoneAddr, pointI) + { + if (newZoneAddr[pointI] < pointMap.size()) + { + curPzRnb[pointI] = + oldZone.whichPoint(pointMap[newZoneAddr[pointI]]); + } + else + { + curPzRnb[pointI] = -1; + } + } + } + + + // Re-do the face zones + + // Make a map of faces to be removed from zones + labelHashSet removeFaceFromZone(2*ref.modifiedFaces().size()); + + forAll (mf, mfI) + { + if (mf[mfI].removeFromZone()) + { + removeFaceFromZone.insert(mf[mfI].faceID()); + } + } + + labelListList newFaceZoneAddr(faceZones.size()); + boolListList newFaceZoneFaceFlip(faceZones.size()); + labelList nFacesInZone(faceZones.size(), 0); + + forAll (faceZones, fzI) + { + // Get the list of old faces + const labelList& oldAddr = faceZones[fzI]; + const boolList& oldFlip = faceZones[fzI].flipMap(); + + // Create new addressing, over-estimating the size + labelList& newAddr = newFaceZoneAddr[fzI]; + boolList& newFlip = newFaceZoneFaceFlip[fzI]; + label& curNFaces = nFacesInZone[fzI]; + + newAddr.setSize + ( + oldAddr.size() + + ref.modifiedFaces().size() + + ref.addedFaces().size() + ); + + newFlip.setSize(newAddr.size()); + newFlip = false; + + // Reset the face flip to false. None of the preserved faces will + // be flipped + + // Add the original faces that have not been removed or re-zoned + forAll (oldAddr, faceI) + { + if + ( + !removedFaces.found(oldAddr[faceI]) + && !removeFaceFromZone.found(oldAddr[faceI]) + ) + { + // The face is still alive. Add its renumbered label + newAddr[curNFaces] = renumberFaces[oldAddr[faceI]]; + newFlip[curNFaces] = oldFlip[faceI]; + + curNFaces++; + } + } + } + + labelList debugFacesInZone(faceZones.size(), 0); + + if (debug) + { + Pout<< "Added zone faces: untouched = " << nFacesInZone; + + debugFacesInZone = nFacesInZone; + } + + // Distribute modified zone faces + forAll (mf, mfI) + { + if (mf[mfI].isInZone()) + { + const label zoneID = mf[mfI].zoneID(); + + // Grab the face index + newFaceZoneAddr[zoneID][nFacesInZone[zoneID]] = + renumberFaces[mf[mfI].faceID()]; + + // Grab the face flip + newFaceZoneFaceFlip[zoneID][nFacesInZone[zoneID]] = + mf[mfI].zoneFlip(); + + nFacesInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " modified = " << nFacesInZone - debugFacesInZone; + + debugFacesInZone = nFacesInZone; + } + + // Distribute added zone faces + forAll (af, afI) + { + if (af[afI].isInZone()) + { + const label zoneID = af[afI].zoneID(); + + // Grab the face index + newFaceZoneAddr[zoneID][nFacesInZone[zoneID]] = + renumberFaces[faces.size() + afI]; + + newFaceZoneFaceFlip[zoneID][nFacesInZone[zoneID]] = + af[afI].zoneFlip(); + + nFacesInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " added = " << nFacesInZone - debugFacesInZone + << ". Faces per zone = " << nFacesInZone << endl; + } + + // Reset the sizes of the face zone addressing and face flip + forAll (newFaceZoneAddr, fzI) + { + newFaceZoneAddr[fzI].setSize(nFacesInZone[fzI]); + newFaceZoneFaceFlip[fzI].setSize(nFacesInZone[fzI]); + } + + // Build the face zone renumbering + labelListList fzFaceRenumber(faceZones.size()); + + List<Map<label> > oldFaceZoneMeshPointMaps(faceZones.size()); + + forAll (faceZones, fzI) + { + faceZone& oldZone = faceZones[fzI]; + + // Point renumbering + // Point renumbering gives the old point location of every new + // point in the face zone. If the point is new to the zone, + // the index will be -1. + // The problem is that the renumbering cannot be built at this stage + // as the order of points in the new face zone is not yet known: + // (the new zone needs to be set on the updated list of faces to + // be able to create the mapping). Therefore, the old meshPoint maps + // will be copied here and will be used later to re-create the + // addressing. + + // Copy old face zone mesh point maps + oldFaceZoneMeshPointMaps[fzI] = faceZones[fzI]().meshPointMap(); + + // Face renumbering + const labelList& newZoneAddr = newFaceZoneAddr[fzI]; + + labelList& curFzFaceRnb = fzFaceRenumber[fzI]; + + curFzFaceRnb.setSize(newZoneAddr.size()); + + forAll (newZoneAddr, faceI) + { + // HJ, change: can this index legally be -1? + // HJ, 9/Jan/2009 + if (newZoneAddr[faceI] > -1 && newZoneAddr[faceI] < faceMap.size()) + { + curFzFaceRnb[faceI] = + oldZone.whichFace(faceMap[newZoneAddr[faceI]]); + } + else + { + curFzFaceRnb[faceI] = -1; + } + } + } + + + // Re-do the cell zones + + // Make a map of cells to be removed from zones + labelHashSet removeCellFromZone(2*ref.modifiedCells().size()); + + forAll (mc, mcI) + { + if (mc[mcI].removeFromZone()) + { + removeCellFromZone.insert(mc[mcI].cellID()); + } + } + + labelListList newCellZoneAddr(cellZones.size()); + labelList nCellsInZone(cellZones.size(), 0); + + forAll (cellZones, czI) + { + // Get the list of old cells + const labelList& oldAddr = cellZones[czI]; + + // Create new addressing, over-estimating the size + labelList& newAddr = newCellZoneAddr[czI]; + label& curNCells = nCellsInZone[czI]; + + newAddr.setSize + ( + oldAddr.size() + + ref.modifiedCells().size() + + ref.addedCells().size() + ); + + // Add the original cells that have not been removed or re-zoned + forAll (oldAddr, cellI) + { + if + ( + !removedCells.found(oldAddr[cellI]) + && !removeCellFromZone.found(oldAddr[cellI]) + ) + { + // The cell is still alive. Add its renumbered label + newAddr[curNCells] = renumberCells[oldAddr[cellI]]; + curNCells++; + } + } + } + + labelList debugCellsInZone(cellZones.size(), 0); + + if (debug) + { + Pout<< "Added zone cells: untouched = " << nCellsInZone; + + debugCellsInZone = nCellsInZone; + } + + // Distribute modified zone cells + forAll (mc, mcI) + { + if (mc[mcI].isInZone()) + { + const label zoneID = mc[mcI].zoneID(); + + newCellZoneAddr[zoneID][nCellsInZone[zoneID]] = + renumberCells[mc[mcI].cellID()]; + + nCellsInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " modified = " << nCellsInZone - debugCellsInZone; + + debugCellsInZone = nCellsInZone; + } + + // Distribute added zone cells + forAll (ac, acI) + { + if (ac[acI].isInZone()) + { + const label zoneID = ac[acI].zoneID(); + + newCellZoneAddr[zoneID][nCellsInZone[zoneID]] = + renumberCells[cells.size() + acI]; + + nCellsInZone[zoneID]++; + } + } + + if (debug) + { + Pout<< " added = " << nCellsInZone - debugCellsInZone + << ". Cells per zone = " << nCellsInZone << endl; + } + + // Reset the sizes of the cell zone addressing + forAll (newCellZoneAddr, czI) + { + newCellZoneAddr[czI].setSize(nCellsInZone[czI]); + } + + // Build the cell zone renumbering + labelListList czRenumber(cellZones.size()); + + forAll (cellZones, czI) + { + cellZone& oldZone = cellZones[czI]; + const labelList& newZoneAddr = newCellZoneAddr[czI]; + + labelList& curCzRnb = czRenumber[czI]; + + curCzRnb.setSize(newZoneAddr.size()); + + forAll (newZoneAddr, cellI) + { + if (newZoneAddr[cellI] < cellMap.size()) + { + curCzRnb[cellI] = + oldZone.whichCell(cellMap[newZoneAddr[cellI]]); + } + else + { + curCzRnb[cellI] = -1; + } + } + } + + + // Calculate (all)owner and (all)neighbour and reset the mesh. + { + labelList allOwn(newFaces.size(), -1); + labelList allNei(newFaces.size(), -1); + + boolList markedFaces(newFaces.size(), false); + + forAll (newCells, cellI) + { + // get reference to face labels for current cell + const cell& cellfaces = newCells[cellI]; + + forAll (cellfaces, faceI) + { + if (!markedFaces[cellfaces[faceI]]) + { + // First visit: owner + allOwn[cellfaces[faceI]] = cellI; + + markedFaces[cellfaces[faceI]] = true; + } + else + { + // Second visit: neighbour + allNei[cellfaces[faceI]] = cellI; + } + } + } + + // Count the number of real faces. + // Note: if there are unused faces in the mesh, they should be + // clustered at the end of the list + + label nUsedFaces = allOwn.size(); + + forAll (allOwn, faceI) + { + if (allOwn[faceI] < 0) + { + nUsedFaces = faceI; + break; + } + } + + // Truncate owner array to the number of used faces + // HJ, 23/Oct/2008 + allOwn.setSize(nUsedFaces); + + // Count internal faces + label nInternalFaces = allOwn.size(); + + forAll (allNei, faceI) + { + if (allNei[faceI] < 0) + { + nInternalFaces = faceI; + break; + } + } + + // Truncate neighbour array to the size of internal faces + // HJ, 23/Oct/2008 + allNei.setSize(nInternalFaces); + + if (debug) + { + Pout<< " nOldPoints: " << points.size() + << " nPoints: " << newPointsZeroVol.size() + << " nUsedFaces: " << nUsedFaces + << " newFaces: " << newFaces.size() + << " allOwn: " << allOwn.size() + << " allNei: " << allNei.size() + << " patchSizes: " << patchSizes + << " patchStarts: " << patchStarts << endl; + } + + // HJ, HACKED + mesh.resetPrimitives + ( + autoPtr<pointField>(&newPointsZeroVol), + autoPtr<faceList>(&newFaces), + autoPtr<labelList>(&allOwn), + autoPtr<labelList>(&allNei), + patchSizes, + patchStarts, + false // The mesh is not complete: no parallel comms + // HJ, 27/Nov/2009 + ); + } + + + // Reset the zones + + forAll (pointZones, pzI) + { + pointZones[pzI] = newPointZoneAddr[pzI]; + } + // // OpenFOAM does not have an updateMesh mechanism. HJ, 1/Aug/2023 + // pointZones.updateMesh(); + + forAll (faceZones, fzI) + { + faceZones[fzI].resetAddressing + ( + newFaceZoneAddr[fzI], + newFaceZoneFaceFlip[fzI] + ); + } + // OpenFOAM does not have an updateMesh mechanism. HJ, 1/Aug/2023 + // faceZones.updateMesh(); + + forAll (cellZones, czI) + { + cellZones[czI] = newCellZoneAddr[czI]; + } + // OpenFOAM does not have an updateMesh mechanism. HJ, 1/Aug/2023 + // cellZones.updateMesh(); + + + // Create the patch mesh point renumbering + + labelListList patchPointRenumber(boundary.size()); + + forAll (boundary, patchI) + { + const labelList& newPatchMeshPoints = boundary[patchI].meshPoints(); + + const Map<label>& oldZoneMeshPointMap = oldPatchMeshPointMaps[patchI]; + const label oldSize = oldPatchNMeshPoints[patchI]; + + labelList& curPatchPointRnb = patchPointRenumber[patchI]; + + curPatchPointRnb.setSize(newPatchMeshPoints.size()); + + forAll (newPatchMeshPoints, pointI) + { + if (newPatchMeshPoints[pointI] < oldSize) + { + Map<label>::const_iterator ozmpmIter = + oldZoneMeshPointMap.find + ( + pointMap[newPatchMeshPoints[pointI]] + ); + + if (ozmpmIter != oldZoneMeshPointMap.end()) + { + curPatchPointRnb[pointI] = ozmpmIter(); + } + else + { + curPatchPointRnb[pointI] = -1; + } + } + else + { + curPatchPointRnb[pointI] = -1; + } + } + } + + // Create the face zone mesh point renumbering + + labelListList fzPointRenumber(faceZones.size()); + + forAll (faceZones, fzI) + { + const labelList& newZoneMeshPoints = faceZones[fzI]().meshPoints(); + + const Map<label>& oldZoneMeshPointMap = oldFaceZoneMeshPointMaps[fzI]; + + labelList& curFzPointRnb = fzPointRenumber[fzI]; + + curFzPointRnb.setSize(newZoneMeshPoints.size()); + + forAll (newZoneMeshPoints, pointI) + { + if (newZoneMeshPoints[pointI] < pointMap.size()) + { + Map<label>::const_iterator ozmpmIter = + oldZoneMeshPointMap.find + ( + pointMap[newZoneMeshPoints[pointI]] + ); + + if (ozmpmIter != oldZoneMeshPointMap.end()) + { + curFzPointRnb[pointI] = ozmpmIter(); + } + else + { + curFzPointRnb[pointI] = -1; + } + } + else + { + curFzPointRnb[pointI] = -1; + } + } + } + + + if (debug) + { + Pout<< "polyTopoChanger::changeMesh" << nl + << '(' << nl + << " const batchPolyTopoChange& ref" << nl + << ") : completed topological change." << nl << endl; + } + + // Comatibility with direct poly topo change: + // - pointsFromPoints + // - facesFromFaces + // - cellsFromCells + // All currently inactive in a direct map and not used + // HJ, 1/Sep/2007 + List<objectMap> pointsFromPoints; + List<objectMap> facesFromFaces; + List<objectMap> cellsFromCells; + + // Patch reset map is currently dummy: does not support change in number + // of boundary patches + // HJ, 23/Apr/2018 + // boolList resetPatchFlag(boundary.size(), false); + + // Dummy old cell volumes + autoPtr<scalarField> oldCellVolumes; + + autoPtr<mapPolyMesh> topoChangeMap + ( + new mapPolyMesh + ( + mesh, + nOldPoints, + nOldFaces, + nOldCells, + + pointMap, + pointsFromPoints, + + faceMap, + faceFromPoint, + faceFromEdge, + facesFromFaces, + + cellMap, + cellFromPoint, + cellFromEdge, + cellFromFace, + cellsFromCells, + + renumberPoints, + renumberFaces, + renumberCells, + + flipFaceFlux, + + patchPointRenumber, + + pzRenumber, + fzPointRenumber, + fzFaceRenumber, + czRenumber, + + // resetPatchFlag, + + newPointsMotion, + oldPatchStarts, + oldPatchNMeshPoints, + oldCellVolumes, + true // Re-use storage + ) + ); + + return topoChangeMap; +} + + +Foam::autoPtr<Foam::mapPolyMesh> Foam::polyTopoChanger::changeMesh() +{ + bool localUpdate = changeTopology(); + + bool globalUpdate = returnReduce(localUpdate, orOp<bool>()); + + if (debug) + { + Pout<< "Local mesh update: " << localUpdate + << " global update: " << globalUpdate << endl; + } + + // if (localUpdate) + { + autoPtr<mapPolyMesh> topoChangeMap = changeMesh + ( + mesh_, + topoChangeRequest()() + ); + + // Mesh data needs to be updated before the update of polyMeshModifiers + // because polyMeshModifiers might need all the new polyMesh data (see + // below for further comments). VV, 19/Feb/2019 + mesh_.updateMesh(topoChangeMap()); + + // Bugfix: call to polyTopoChanger::update must happen after + // polyMesh::updateMesh where all the relevant mesh bits for parallel + // comms are updated. First noticed when the syncying of pointLevel in + // refinement::updateMesh was not syncying properly. VV, 19/Feb/2019 + update(topoChangeMap()); + + // Increment the morph index + morphIndex_++; + + // Mark the mesh as changing + mesh_.topoChanging(true); + + return topoChangeMap; + } + // else + // { + // // If other processors perform topology change, communications + // // between domains need to be executed. HJ, 27/Nov/2009 + // if (globalUpdate) + // { + // // Sync couple patch update + // syncCoupledPatches(); + + // // Sync mesh update + // mesh_.syncUpdateMesh(); + + // // Mark the mesh as changing + // mesh_.changing(true); + // } + + // return autoPtr<mapPolyMesh>(new mapPolyMesh(mesh_)); + // } +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.C b/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.C new file mode 100644 index 0000000000000000000000000000000000000000..65021a8cbb518116e374e7eb6ac01905e0ad7659 --- /dev/null +++ b/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.C @@ -0,0 +1,2442 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + +\*---------------------------------------------------------------------------*/ + +#include "polyhedralRefinement.H" +#include "cellSet.H" +#include "faceSet.H" +#include "pointSet.H" +#include "syncTools.H" +#include "meshTools.H" +#include "OFstream.H" +#include "Time.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(polyhedralRefinement, 0); + addToRunTimeSelectionTable + ( + polyMeshModifier, + polyhedralRefinement, + dictionary + ); +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::label Foam::polyhedralRefinement::getAnchorLevel +( + const label faceI +) const +{ + const face& f = mesh_.faces()[faceI]; + + if (f.size() <= 3) + { + return pointLevel_[f[findMaxLevel(f)]]; + } + else + { + const label& ownLevel = cellLevel_[mesh_.faceOwner()[faceI]]; + + if (countAnchors(f, ownLevel) >= 3) + { + return ownLevel; + } + else if (countAnchors(f, ownLevel + 1) >= 3) + { + return ownLevel + 1; + } + else + { + return -1; + } + } +} + + +void Foam::polyhedralRefinement::createInternalFaces +( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const labelList& cellMidPoint, + const labelList& faceMidPoint, + const labelList& faceAnchorLevel, + const labelList& edgeMidPoint, + const label cellI, + + batchPolyTopoChange& ref +) const +{ + // Find in every face the cellLevel + 1 points (from edge subdivision) + // and the anchor points + + // Get current cell and its level + const cell& curCell = mesh_.cells()[cellI]; + const label& cLevel = cellLevel_[cellI]; + + // Get mesh faces and face edges + const faceList& meshFaces = mesh_.faces(); + const labelListList& meshFaceEdges = mesh_.faceEdges(); + + // Get mesh cell points + const labelListList& meshCellPoints = mesh_.cellPoints(); + + // Map from edge mid to anchor points + Map<edge> midPointToAnchors(24); + // Map from edge mid to face mids + Map<edge> midPointToFaceMids(24); + + // Running count of number of internal faces added so far + label nFacesAdded = 0; + + // Loop through faces of the cell + forAll(curCell, i) + { + // Get face index + const label& faceI = curCell[i]; + + // Get current face and its edges + const face& f = meshFaces[faceI]; + const labelList& fEdges = meshFaceEdges[faceI]; + + // We are on the cellI side of face f. The face will have 1 or n + // cLevel points (where n is the number of points/edges of a face) + // and lots of higher numbered ones + + // Index of face mid point + label faceMidPointI = -1; + + // Get number of anchors for the face + const label nAnchors = countAnchors(f, cLevel); + + if (nAnchors == 1) + { + // Only one anchor point. So the other side of the face has already + // been split using cLevel + 1 and cLevel + 2 points + + // Find the one anchor + label anchorFp = -1; + + // Loop through face points + forAll(f, fp) + { + if (pointLevel_[f[fp]] <= cLevel) + { + // Point level is smaller than cLevel + 1, this is the + // anchor point + anchorFp = fp; + break; + } + } + + // Now the face mid point is the second cLevel + 1 point + label edgeMid = findLevel(f, f.fcIndex(anchorFp), true, cLevel + 1); + label faceMid = findLevel(f, f.fcIndex(edgeMid), true, cLevel + 1); + + // Set face mid point index + faceMidPointI = f[faceMid]; + } + else + { + // There is no face middle yet but the face will be split. Set face + // mid point index + faceMidPointI = faceMidPoint[faceI]; + } + + + // Now loop over all the anchors (might be just one) and store + // the edge mids connected to it. storeMidPointInfo will collect + // all the info and combine it all + forAll(f, fp0) + { + // Get point index + const label& point0 = f[fp0]; + + if (pointLevel_[point0] <= cLevel) + { + // This is anchor point + + // Walk forward to cLevel + 1 or edgeMidPoint of this level + label edgeMidPointI = -1; + + const label fp1 = f.fcIndex(fp0); + + if (pointLevel_[f[fp1]] <= cLevel) + { + // Another anchor: edge will be split + const label& edgeI = fEdges[fp0]; + + edgeMidPointI = edgeMidPoint[edgeI]; + + // Sanity check + if (edgeMidPointI == -1) + { + const labelList& cPoints = meshCellPoints[cellI]; + + FatalErrorInFunction + << "cell:" << cellI << " cLevel:" << cLevel + << " cell points:" << cPoints + << " pointLevel:" + << IndirectList<label>(pointLevel_, cPoints)() + << " face:" << faceI + << " f:" << f + << " pointLevel:" + << IndirectList<label>(pointLevel_, f)() + << " faceAnchorLevel:" << faceAnchorLevel[faceI] + << " faceMidPoint:" << faceMidPoint[faceI] + << " faceMidPointI:" << faceMidPointI + << " fp:" << fp0 + << abort(FatalError); + } + } + else + { + // Search forward in face to clevel + 1 + const label edgeMid = findLevel(f, fp1, true, cLevel + 1); + + edgeMidPointI = f[edgeMid]; + } + + label newFaceI = storeMidPointInfo + ( + cellAnchorPoints, + cellAddedCells, + cellMidPoint, + edgeMidPoint, + + cellI, + faceI, + true, // mid point after anchor + edgeMidPointI, // edgemid + point0, // anchor + faceMidPointI, + + midPointToAnchors, + midPointToFaceMids, + ref + ); + + if (newFaceI != -1) + { + ++nFacesAdded; + } + + + // Now walk backward + + label fpMin1 = f.rcIndex(fp0); + + if (pointLevel_[f[fpMin1]] <= cLevel) + { + // Another anchor: edge will be split + const label& edgeI = fEdges[fpMin1]; + + edgeMidPointI = edgeMidPoint[edgeI]; + + // Sanity check + if (edgeMidPointI == -1) + { + const labelList& cPoints = meshCellPoints[cellI]; + + FatalErrorInFunction + << "cell:" << cellI << " cLevel:" << cLevel + << " cell points:" << cPoints + << " pointLevel:" + << IndirectList<label>(pointLevel_, cPoints)() + << " face:" << faceI + << " f:" << f + << " pointLevel:" + << IndirectList<label>(pointLevel_, f)() + << " faceAnchorLevel:" << faceAnchorLevel[faceI] + << " faceMidPoint:" << faceMidPoint[faceI] + << " faceMidPointI:" << faceMidPointI + << " fp:" << fp0 + << abort(FatalError); + } + } + else + { + // Search back in face to clevel + 1 + const label edgeMid = findLevel(f, fpMin1, false, cLevel + 1); + + edgeMidPointI = f[edgeMid]; + } + + newFaceI = storeMidPointInfo + ( + cellAnchorPoints, + cellAddedCells, + cellMidPoint, + edgeMidPoint, + + cellI, + faceI, + false, // mid point before anchor + edgeMidPointI, // edgemid + point0, // anchor + faceMidPointI, + + midPointToAnchors, + midPointToFaceMids, + ref + ); + + if (newFaceI != -1) + { + ++nFacesAdded; + } + } // End for this anchor point + } // End for all face points + } // End for all cell faces +} + + +Foam::label Foam::polyhedralRefinement::getAnchorCell +( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const label cellI, + const label faceI, + const label pointI +) const +{ + // Check whether pointI is an anchor of cellI. If it is not, check whether + // any other point on the face is an anchor for the cell + + if (cellAnchorPoints[cellI].size() > 0) + { + const label index = cellAnchorPoints[cellI].find(pointI); + + if (index != -1) + { + return cellAddedCells[cellI][index]; + } + + + // pointI is not an anchor for the cell. Maybe we already refined the + // face so check all the face vertices + const face& f = mesh_.faces()[faceI]; + + forAll(f, fp) + { + const label index = cellAnchorPoints[cellI].find(f[fp]); + + if (index != -1) + { + return cellAddedCells[cellI][index]; + } + } + + // Problem: the point does not seem to be an anchor for cell + + // Pick up points of the cell + const labelList& cPoints = mesh_.cellPoints()[cellI]; + + Perr<< "cell: " << cellI << ", points: " << endl; + forAll(cPoints, i) + { + const label pointI = cPoints[i]; + + Perr<< " " << pointI << " coord: " << mesh_.points()[pointI] + << nl; + } + + Perr<< "cell: " << cellI << " anchorPoints: " << cellAnchorPoints[cellI] + << endl; + + FatalErrorInFunction + << "Could not find point " << pointI + << " in the anchorPoints for cell " << cellI << endl + << "Does your original mesh obey the 2:1 constraint and" + << " did you use consistentRefinement to make your cells to refine" + << " obey this constraint as well?" + << abort(FatalError); + + return -1; + } + else + { + return cellI; + } +} + + +void Foam::polyhedralRefinement::setNewFaceNeighbours +( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const label faceI, + const label pointI, + + label& own, + label& nei +) const +{ + // Get anchor cell for this anchor point on owner side + own = getAnchorCell + ( + cellAnchorPoints, + cellAddedCells, + mesh_.faceOwner()[faceI], + faceI, + pointI + ); + + if (mesh_.isInternalFace(faceI)) + { + // Get anchor cell for this anchor point on neighbour side + nei = getAnchorCell + ( + cellAnchorPoints, + cellAddedCells, + mesh_.faceNeighbour()[faceI], + faceI, + pointI + ); + } + else + { + // Boundary face: set neighbour to -1 + nei = -1; + } +} + + +Foam::label Foam::polyhedralRefinement::findLevel +( + const face& f, + const label startFp, + const bool searchForward, + const label wantedLevel +) const +{ + label fp = startFp; + + forAll(f, i) + { + label pointI = f[fp]; + + if (pointLevel_[pointI] < wantedLevel) + { + FatalErrorInFunction + << "face:" << f + << " level:" << IndirectList<label>(pointLevel_, f)() + << " startFp:" << startFp + << " wantedLevel:" << wantedLevel + << abort(FatalError); + } + else if (pointLevel_[pointI] == wantedLevel) + { + return fp; + } + + if (searchForward) + { + fp = f.fcIndex(fp); + } + else + { + fp = f.rcIndex(fp); + } + } + + FatalErrorInFunction + << "face:" << f + << " level:" << IndirectList<label>(pointLevel_, f)() + << " startFp:" << startFp + << " wantedLevel:" << wantedLevel + << abort(FatalError); + + return -1; +} + + +Foam::label Foam::polyhedralRefinement::storeMidPointInfo +( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const labelList& cellMidPoint, + const labelList& edgeMidPoint, + const label cellI, + const label faceI, + const bool faceOrder, + const label edgeMidPointI, + const label anchorPointI, + const label faceMidPointI, + + Map<edge>& midPointToAnchors, + Map<edge>& midPointToFaceMids, + batchPolyTopoChange& ref +) const +{ + // A single internal face is added per edge inbetween anchor points, + // i.e. one face per midPoint between anchor points. The information is + // stored on the midPoint and if we have enough information (finished + // collecting two anchors and two face mid points), we add the face. + // Note that this member function can get called anywhere from + // two times (two unrefined faces) to four times (two refined faces) so + // the first call that adds the information creates the face + + + // See if need to store anchors + bool changed = false; + bool haveTwoAnchors = false; + + Map<edge>::iterator edgeMidFnd = midPointToAnchors.find(edgeMidPointI); + + if (edgeMidFnd == midPointToAnchors.end()) + { + midPointToAnchors.insert(edgeMidPointI, edge(anchorPointI, -1)); + } + else + { + edge& e = edgeMidFnd(); + + if (anchorPointI != e[0]) + { + if (e[1] == -1) + { + e[1] = anchorPointI; + changed = true; + } + } + + if (e[0] != -1 && e[1] != -1) + { + haveTwoAnchors = true; + } + } + + bool haveTwoFaceMids = false; + + Map<edge>::iterator faceMidFnd = midPointToFaceMids.find(edgeMidPointI); + + if (faceMidFnd == midPointToFaceMids.end()) + { + midPointToFaceMids.insert(edgeMidPointI, edge(faceMidPointI, -1)); + } + else + { + edge& e = faceMidFnd(); + + if (faceMidPointI != e[0]) + { + if (e[1] == -1) + { + e[1] = faceMidPointI; + changed = true; + } + } + + if (e[0] != -1 && e[1] != -1) + { + haveTwoFaceMids = true; + } + } + + // Check if this call of storeMidPointInfo is the one that completed all + // the nessecary information + + if (changed && haveTwoAnchors && haveTwoFaceMids) + { + const edge& anchors = midPointToAnchors[edgeMidPointI]; + const edge& faceMids = midPointToFaceMids[edgeMidPointI]; + + label otherFaceMidPointI = faceMids.otherVertex(faceMidPointI); + + // Create face consistent with anchorI being the owner. + // Note that the edges between the edge mid point and the face mids + // might be marked for splitting. Note that these edge splits cannot + // be between cell mid and face mids + + DynamicList<label> newFaceVerts(4); + if (faceOrder == (mesh_.faceOwner()[faceI] == cellI)) + { + newFaceVerts.append(faceMidPointI); + + // Check and insert edge split if any + insertEdgeSplit + ( + edgeMidPoint, + faceMidPointI, // edge between faceMid and + edgeMidPointI, // edgeMid + newFaceVerts + ); + + newFaceVerts.append(edgeMidPointI); + + insertEdgeSplit + ( + edgeMidPoint, + edgeMidPointI, + otherFaceMidPointI, + newFaceVerts + ); + + newFaceVerts.append(otherFaceMidPointI); + newFaceVerts.append(cellMidPoint[cellI]); + } + else + { + newFaceVerts.append(otherFaceMidPointI); + + insertEdgeSplit + ( + edgeMidPoint, + otherFaceMidPointI, + edgeMidPointI, + newFaceVerts + ); + + newFaceVerts.append(edgeMidPointI); + + insertEdgeSplit + ( + edgeMidPoint, + edgeMidPointI, + faceMidPointI, + newFaceVerts + ); + + newFaceVerts.append(faceMidPointI); + newFaceVerts.append(cellMidPoint[cellI]); + } + + face newFace; + newFace.transfer(newFaceVerts.shrink()); + newFaceVerts.clear(); + + label anchorCell0 = getAnchorCell + ( + cellAnchorPoints, + cellAddedCells, + cellI, + faceI, + anchorPointI + ); + label anchorCell1 = getAnchorCell + ( + cellAnchorPoints, + cellAddedCells, + cellI, + faceI, + anchors.otherVertex(anchorPointI) + ); + + // Get mesh points + const pointField& meshPoints = mesh_.points(); + + label own, nei; + point ownPt, neiPt; + + if (anchorCell0 < anchorCell1) + { + own = anchorCell0; + nei = anchorCell1; + + ownPt = meshPoints[anchorPointI]; + neiPt = meshPoints[anchors.otherVertex(anchorPointI)]; + + } + else + { + own = anchorCell1; + nei = anchorCell0; + newFace = newFace.reverseFace(); + + ownPt = meshPoints[anchors.otherVertex(anchorPointI)]; + neiPt = meshPoints[anchorPointI]; + } + + if (debug) + { + point ownPt, neiPt; + + if (anchorCell0 < anchorCell1) + { + ownPt = meshPoints[anchorPointI]; + neiPt = meshPoints[anchors.otherVertex(anchorPointI)]; + } + else + { + ownPt = meshPoints[anchors.otherVertex(anchorPointI)]; + neiPt = meshPoints[anchorPointI]; + } + + checkInternalOrientation + ( + ref, + cellI, + faceI, + ownPt, + neiPt, + newFace + ); + } + + return addInternalFace + ( + ref, + faceI, + anchorPointI, + newFace, + own, + nei + ); + } + else + { + return -1; + } +} + + +void Foam::polyhedralRefinement::insertEdgeSplit +( + const labelList& edgeMidPoint, + const label p0, + const label p1, + DynamicList<label>& verts +) const +{ + // Get number of points + const label nPoints = mesh_.nPoints(); + + if (p0 < nPoints && p1 < nPoints) + { + label edgeI = meshTools::findEdge(mesh_, p0, p1); + + if (edgeI != -1 && edgeMidPoint[edgeI] != -1) + { + verts.append(edgeMidPoint[edgeI]); + } + } +} + + +// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * // + +void Foam::polyhedralRefinement::setRefinementInstruction +( + batchPolyTopoChange& ref +) const +{ + // Note: assumes that cellsToRefine_ are set prior to the function call + + // Reset refinementLevelIndicator field. Note: the list is cleared in + // updateMesh member function after updating cell and point levels + if (refinementLevelIndicator_.empty()) + { + // List has been resetted correctly, initialise it for this iteration + refinementLevelIndicator_.setSize(mesh_.nCells(), UNCHANGED); + } + else + { + // List has not been reset correctly, issue an error + FatalErrorInFunction + << "Refinement level indicator list has not been" + << " resetted properly." << nl + << "Either the call to updateMesh() after performing" + << " refinement has not been made or the call to" + << " setRefinementInstruction(...) and" + << " setUnrefinementInstruction(...) has not been made in" + << " correct order." << nl + << "Make sure to set refinement, set unrefinement and call" + << " updateMesh after performing the topo change." + << abort(FatalError); + } + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Allocating " << cellsToRefine_.size() << " cell midpoints." + << endl; + } + + + // PART 1: Mark cells for refinement and add points at their cell centres + + // Get necessary mesh data + const faceList& meshFaces = mesh_.faces(); + const cellList& meshCells = mesh_.cells(); + const vectorField& meshCellCentres = mesh_.cellCentres(); + + // Mid point for refined cell (points at cell centres): + // Not refined = -1 + // Shall be refined > -1 (label of added mid point) + labelList cellMidPoint(mesh_.nCells(), -1); + + // Loop through cells to refine + forAll(cellsToRefine_, i) + { + // Get cell idnex + const label& cellI = cellsToRefine_[i]; + + cellMidPoint[cellI] = ref.setAction + ( + polyAddPoint + ( + meshCellCentres[cellI], // Point to add (cell centre) + -1, // Appended point: no master ID + -1, // Zone for point + true // Supports a cell + ) + ); + } + + // Write out split cells as a cell set for debug + if (debug) + { + // Note: cellSet is actually a hash table of labels + cellSet splitCells(mesh_, "splitCells", cellsToRefine_.size()); + + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + // Cell is marked for refinement, insert into cellSet + splitCells.insert(cellI); + } + } + + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Writing " << splitCells.size() + << " cells to split to cellSet " << splitCells.objectPath() + << endl; + + splitCells.write(); + } + + + // PART 2: Mark edges for refinement and add points to edge centres + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Allocating edge midpoints." + << endl; + } + + // Refined edges are defined by having both their point levels lower than + // the cell level, i.e. if any cell that gets split uses this edge, the edge + // needs to be split as well + + // Get necessary mesh data + const labelListList& meshCellEdges = mesh_.cellEdges(); + const edgeList& meshEdges = mesh_.edges(); + + // Mid points for refined edge: + // No need to split edge = -1 + // Label of introduced mid point > -1 + labelList edgeMidPoint(mesh_.nEdges(), -1); + + // Note: Loop over all cells instead of all edges + forAll(cellMidPoint, cellI) + { + // Point is marked for refinement, proceed to look at the edges + if (cellMidPoint[cellI] > -1) + { + // Get edges of this cell + const labelList& cEdges = meshCellEdges[cellI]; + + forAll(cEdges, i) + { + // Get edge index and edge + const label& edgeI = cEdges[i]; + const edge& e = meshEdges[edgeI]; + + if + ( + pointLevel_[e[0]] <= cellLevel_[cellI] + && pointLevel_[e[1]] <= cellLevel_[cellI] + ) + { + // Point levels of both edge points are <= cell level, mark + // the edge for splitting + edgeMidPoint[edgeI] = 12345; + } + } + } + } + + // Synchronize edgeMidPoint across coupled patches. Note: use max so that + // any split takes precedence. + syncTools::syncEdgeList + ( + mesh_, + edgeMidPoint, + maxEqOp<label>(), + labelMin + ); + + // Introduce edge points + + // Get necessary mesh data + const pointField& meshPoints = mesh_.points(); + + // Memory management + { + // Phase 1: calculate midpoints and sync. This is necessary if we don't + // use binary format for writing and we slowly get differences. + + // Allocate storage for edge points + pointField edgeMids(mesh_.nEdges(), point(-GREAT, -GREAT, -GREAT)); + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // Edge marked to be split. Get edge centre + edgeMids[edgeI] = meshEdges[edgeI].centre(meshPoints); + } + } + + // Sync across processor boundaries + syncTools::syncEdgeList + ( + mesh_, + edgeMids, + maxEqOp<vector>(), + point(-GREAT, -GREAT, -GREAT) + ); + + // Phase 2: introduce points at the synced locations. + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + edgeMidPoint[edgeI] = ref.setAction + ( + polyAddPoint + ( + edgeMids[edgeI], // Point + -1, // Appended point, no master ID + -1, // Zone for point + true // Supports a cell + ) + ); + } + } + } // End memory management for syncing/adding edge points + + // Write out edge mid points for split edges for debugging + if (debug) + { + OFstream str(mesh_.time().path()/"edgeMidPoint.obj"); + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // Get edge and write its cell centre + const edge& e = meshEdges[edgeI]; + meshTools::writeOBJ(str, e.centre(meshPoints)); + } + } + + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Writing centres of edges to split to file " << str.name() + << endl; + } + + + // PART 3: Calculate face level (after selected cells splitting) + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction" << nl + << "Allocating face midpoints." + << endl; + } + + // Face anchor level. There are guaranteed at least 3 points with level + // <= anchorLevel. These are the corner points. + labelList faceAnchorLevel(mesh_.nFaces()); + + for (label faceI = 0; faceI < mesh_.nFaces(); ++faceI) + { + faceAnchorLevel[faceI] = getAnchorLevel(faceI); + } + + // Mid points for refined face (points at face centres): + // Not refined = -1 + // Shall be refined > -1 (label of added mid point) + labelList faceMidPoint(mesh_.nFaces(), -1); + + // Get necessary mesh data + const labelList& meshFaceOwner = mesh_.faceOwner(); + const labelList& meshFaceNeighbour = mesh_.faceNeighbour(); + + const label nFaces = mesh_.nFaces(); + const label nInternalFaces = mesh_.nInternalFaces(); + + // Internal faces: look at cells on both sides. Uniquely determined since + // the face itself is guaranteed to be same level as most refined neighbour + for (label faceI = 0; faceI < nInternalFaces; ++faceI) + { + // Note: no need to check whether the face has valid anchor level since + // all faces can be split + const label& own = meshFaceOwner[faceI]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = ownLevel + (cellMidPoint[own] > -1 ? 1 : 0); + + const label& nei = meshFaceNeighbour[faceI]; + const label& neiLevel = cellLevel_[nei]; + const label newNeiLevel = neiLevel + (cellMidPoint[nei] > -1 ? 1 : 0); + + if + ( + newOwnLevel > faceAnchorLevel[faceI] + || newNeiLevel > faceAnchorLevel[faceI] + ) + { + // New level is higher than the face anchor level, mark for + // splitting + faceMidPoint[faceI] = 12345; + } + } + + // Coupled patches handled like internal faces except now all information + // from neighbour comes from across processor. + // Boundary faces are more complicated since the boundary face can + // be more refined than its owner (or neighbour for coupled patches) + // (does not happen if refining/unrefining only, but does e.g. when + // refinining and subsetting) + + // Memory management + { + // Create list for swapping boundary data + labelList newNeiLevel(nFaces - nInternalFaces); + + forAll(newNeiLevel, i) + { + const label& own = meshFaceOwner[i + nInternalFaces]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = + ownLevel + (cellMidPoint[own] > -1 ? 1 : 0); + + newNeiLevel[i] = newOwnLevel; + } + + // Swap the list which now contains data from the other side + syncTools::swapBoundaryFaceList(mesh_, newNeiLevel); + + forAll(newNeiLevel, i) + { + // Get face index + const label faceI = i + nInternalFaces; + + // Note: no need to check whether the face has valid anchor level + // since all faces can be split + const label& own = meshFaceOwner[faceI]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = + ownLevel + (cellMidPoint[own] > -1 ? 1 : 0); + + if + ( + newOwnLevel > faceAnchorLevel[faceI] + || newNeiLevel[i] > faceAnchorLevel[faceI] + ) + { + // New level is higher than the face anchor level, mark for + // splitting + faceMidPoint[faceI] = 12345; + } + } + } // End memory management for syncing owner/neighbour face levels + + // Note: synronisation of faceMidPoints across coupled patches is not + // necessary since we have exchanged the neighbour data above using + // swapBoundaryFaceList, thus the faceMidPoint has to be the same on both + // sides. VV, 5/Jan/2018. +// // Synchronize faceMidPoint across coupled patches +// syncTools::syncFaceList +// ( +// mesh_, +// faceMidPoint, +// maxEqOp<label>(), +// false +// ); + + + // Introduce face points + + // Get face centres + const vectorField& meshFaceCentres = mesh_.faceCentres(); + + // Memory management + { + // Phase 1: determine mid points and sync. Note: the same procedure has + // been used for syncing edge mid points + + // Allocate storage for boundary face points + pointField bFaceMids + ( + nFaces - nInternalFaces, + point(-GREAT, -GREAT, -GREAT) + ); + + // Loop through boundary face mids + forAll(bFaceMids, i) + { + // Get face index + const label faceI = i + nInternalFaces; + + if (faceMidPoint[faceI] > -1) + { + // This is a valid face mid, get the face centre + bFaceMids[i] = meshFaceCentres[faceI]; + } + } + + // Sync across coupled boundaries. Note: uses maximum of the components + // of the vector. This is completely arbitrary but it doesn't matter as + // long as we have same points on each side. VV, 5/Jan/2018. + syncTools::syncBoundaryFaceList + ( + mesh_, + bFaceMids, + maxEqOp<vector>() + ); + + // Loop through faces + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + // Face marked to be split. Add the point at face centre and + // replace faceMidPoint with actual point label + + faceMidPoint[faceI] = ref.setAction + ( + polyAddPoint + ( + ( + faceI < nInternalFaces + ? meshFaceCentres[faceI] + : bFaceMids[faceI - nInternalFaces] + ), // Point + -1, // Appended point, no master ID + -1, // Zone for point + true // Supports a cell + ) + ); + } + } + } // End memory management for syncing boundary data and adding face mids + + // Write out split faces as a face set for debugging + if (debug) + { + // Create a faceSet with 3*cell sizes to prevent excessive resizing + faceSet splitFaces(mesh_, "splitFaces", 3*cellsToRefine_.size()); + + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + splitFaces.insert(faceI); + } + } + + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Writing " << splitFaces.size() + << " faces to split to faceSet " << splitFaces.objectPath() + << endl; + + splitFaces.write(); + } + + + // Now we have all the information we need to perform the refinement and we + // no longer need to refer to cellsToRefine_. The information is: + // - cellMidPoint >= 0 : cell needs to be split + // - faceMidPoint >= 0 : face needs to be split + // - edgeMidPoint >= 0 : edge needs to be split + + + // PART 4: Get corner and anchor points for all cells + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Finding cell anchorPoints" << endl; + } + + // Get anchor points for each cell: points that have the same or lower + // refinement level as the cell + List<DynamicList<label>> cellAnchorPointsDynamic(mesh_.nCells()); + + // Loop through all cells + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + // This cell shall be cut. Set capacity to 16 to prevent excessive + // resizing + cellAnchorPointsDynamic[cellI].setCapacity(16); + } + } + + // Get necessary mesh data + const labelListList& meshPointCells = mesh_.pointCells(); + + // Loop through all points + forAll(pointLevel_, pointI) + { + // Get point cells + const labelList& pCells = meshPointCells[pointI]; + + // Loop through all cells sharing this point + forAll(pCells, pCellI) + { + // Get current cell index + const label& cellI = pCells[pCellI]; + + if + ( + cellMidPoint[cellI] > -1 + && pointLevel_[pointI] <= cellLevel_[cellI] + ) + { + // This point cells is marked for refinement and its point level + // is smaller or equal to cell level, append the point + cellAnchorPointsDynamic[cellI].append(pointI); + } + } + } + + // Loop through all cells and check whether at least 4 anchor points + // have been found (minimum requirement for a tet cell) + + // Get cell points for error output + const labelListList& meshCellPoints = mesh_.cellPoints(); + + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + // Cell selected for refinement + if (cellAnchorPointsDynamic[cellI].size() < 4) + { + // Cell has less than 4 anchor points. Issue an error and report + // cell points + const labelList& cPoints = meshCellPoints[cellI]; + + FatalErrorInFunction + << "Cell " << cellI + << " of level " << cellLevel_[cellI] + << " does not seem to have enough points of " + << " lower level" << endl + << "cellPoints:" << cPoints << endl + << "pointLevels:" + << IndirectList<label>(pointLevel_, cPoints)() << endl + << abort(FatalError); + } + } + } + + // Collect cellAnchorPoints into a List<labelList> instead of + // List<dynamicList> + labelListList cellAnchorPoints(mesh_.nCells()); + + forAll(cellAnchorPointsDynamic, cellI) + { + // Tranfer the dynamic list for each cell into an ordinary list + cellAnchorPoints[cellI].transfer(cellAnchorPointsDynamic[cellI]); + } + + // PART 5: Add the cells + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << " Adding cells." + << endl; + } + + // We should have exactly n new cells per each split cell, where n is the + // number of anchor points in a cell + labelListList cellAddedCells(mesh_.nCells()); + + // Get cell zone mesh + const cellZoneMesh& cellZones = mesh_.cellZones(); + + forAll(cellAnchorPoints, cellI) + { + // Check whether this is a split cell + if (cellMidPoint[cellI] > -1) + { + // Get cell anchors + const labelList& cAnchors = cellAnchorPoints[cellI]; + + // Set the total number of added cells to number of anchors + labelList& cAdded = cellAddedCells[cellI]; + cAdded.setSize(cAnchors.size()); + + // Original cell has index 0 + cAdded[0] = cellI; + + // Update refinement level indicator field to 1 since this original + // cell will be refined + refinementLevelIndicator_[cellI] = REFINED; + + // Add other cells + for (label i = 1; i < cAdded.size(); ++i) + { + cAdded[i] = ref.setAction + ( + polyAddCell + ( + -1, // Master point + -1, // Master edge + -1, // Master face + cellI, // Master cell + cellZones.whichZone(cellI) // Zone for cell + ) + ); + } + } + } + + + // PART 6: Adding faces + + // 6.1. Existing faces that get split (into n faces where n is the number of + // points or edges) + // 6.2. Existing faces that do not get split but only edges get split + // 6.3. Existing faces that do not get split but get new owner/neighbour + // 6.4. New internal faces inside split cells. + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << " Marking faces to be handled" + << endl; + } + + // Get all faces to split: + // a) All faces of a cell being split + // b) All faces that are being split + // c) Both faces of an edge that is being split + boolList facesToSplit(mesh_.nFaces(), false); + + // Get edge faces + const labelListList& meshEdgeFaces = mesh_.edgeFaces(); + + // a) All faces of a cell that is being split + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + const cell& cFaces = meshCells[cellI]; + + forAll(cFaces, i) + { + facesToSplit[cFaces[i]] = true; + } + } + } + + // b) All faces that are being split + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + facesToSplit[faceI] = true; + } + } + + // c) Both faces of an edge that are being split + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + const labelList& eFaces = meshEdgeFaces[edgeI]; + + forAll(eFaces, i) + { + facesToSplit[eFaces[i]] = true; + } + } + } + + // Note: after splitting a certain face during parts 6.1. to 6.4., + // facesToSplit for that face will be set back to 0, i.e. marked as finished + + + // PART 6.1. Add/modify faces for each face being split + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << endl + << "Splitting faces..." << endl; + } + + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1 && facesToSplit[faceI]) + { + // Face has not been split. + // Note: although facesToSplit can't be different than 1 here and + // the second check can be ommitted, it is left for clarity + + // Get the face + const face& f = meshFaces[faceI]; + + // Flag to control whether the original faceI has been used + // Note: original face gets modified, other n - 1 faces are added, + // where n is the number of points/edges of a face + bool modifiedFace = false; + + // Get anchor level for the face + const label& anchorLevel = faceAnchorLevel[faceI]; + + // New face always has four points/edges for arbitrary polygon + face newFace(4); + + // Loop through all points of original face + forAll(f, fp) + { + const label& pointI = f[fp]; + + if (pointLevel_[pointI] <= anchorLevel) + { + // This point is anchor, start collecting face + + // Create dynamic list (because of append) for face vertices + // and append the first (anchor) point + DynamicList<label> faceVerts(4); + faceVerts.append(pointI); + + // Walk forward to mid point. + // - if next is +2 midpoint is +1 + // - if next is +1 it is midpoint + // - if next is +0 there has to be edgeMidPoint + + // Appends all points from this point to face mid point + walkFaceToMid + ( + edgeMidPoint, + anchorLevel, + faceI, + fp, + faceVerts + ); + + // Append face mid point + faceVerts.append(faceMidPoint[faceI]); + + // Append all points from face mid point to starting point + walkFaceFromMid + ( + edgeMidPoint, + anchorLevel, + faceI, + fp, + faceVerts + ); + + // Transfer dynamic list to a face (ordinary list) + newFace.transfer(faceVerts); + faceVerts.clear(); + + // Set new owner/neighbour indices based on split cells + label own, nei; + setNewFaceNeighbours + ( + cellAnchorPoints, + cellAddedCells, + faceI, + pointI, // Anchor point + + own, + nei + ); + + + if (debug) + { + // Get mesh cell centres + const vectorField& meshCellCentres = + mesh_.cellCentres(); + + if (mesh_.isInternalFace(faceI)) + { + const label oldOwn = meshFaceOwner[faceI]; + const label oldNei = meshFaceNeighbour[faceI]; + + // Print info only with deep debug level + if (debug > 1) + { + Pout<< "Split internal face: " << faceI + << ", verts: " << f + << ", into quad: " << newFace << nl + << "owner: " << oldOwn + << ", neighbour: " << oldNei << endl; + } + + checkInternalOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshCellCentres[oldNei], + newFace + ); + } + else + { + const label oldOwn = meshFaceOwner[faceI]; + + // Print info only with deep debug level + if (debug > 1) + { + Pout<< "Split boundary face: " << faceI + << ", verts: " << f + << ", into quad: " << newFace << nl + << "owner: " << oldOwn << endl; + } + + checkBoundaryOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshFaceCentres[faceI], + newFace + ); + } + } // End debug + + + if (!modifiedFace) + { + // Modify first face + modifiedFace = true; + modifyFace(ref, faceI, newFace, own, nei); + } + else + { + // Add additional faces + addFace(ref, faceI, newFace, own, nei); + } + } // End point anchor check + } // End for all points + + // Mark face as handled + facesToSplit[faceI] = false; + } + } + + + // PART 6.2. Modify faces that do not get split but have edges that are + // being split + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << "Modifying faces with split edges..." + << endl; + } + + // Get face edges + const labelListList& meshFaceEdges = mesh_.faceEdges(); + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // This is an edge that is going to be split, get edge faces + const labelList& eFaces = meshEdgeFaces[edgeI]; + + // Loop through all faces of an edge + forAll(eFaces, i) + { + // Get face index + const label faceI = eFaces[i]; + + // Check whether this is not a face that has been marked for + // splitting and that the face has not been handled yet. The + // second check is necessary since we go through edge faces + // instead of just faces + if (faceMidPoint[faceI] < 0 && facesToSplit[faceI]) + { + // This is unsplit face that has not been handled + + // Get face and face edges + const face& f = meshFaces[faceI]; + const labelList& fEdges = meshFaceEdges[faceI]; + + // Create a dynamic list containing new face vertices + DynamicList<label> newFaceVerts(f.size()); + + // Append all original points and all edge mid points + forAll(f, fp) + { + newFaceVerts.append(f[fp]); + + const label edgeI = fEdges[fp]; + + if (edgeMidPoint[edgeI] > -1) + { + newFaceVerts.append(edgeMidPoint[edgeI]); + } + } + + face newFace; + newFace.transfer(newFaceVerts.shrink()); + + + // The point with the lowest level should be an anchor + // point of the neighbouring cells. + const label anchorFp = findMinLevel(f); + + label own, nei; + setNewFaceNeighbours + ( + cellAnchorPoints, + cellAddedCells, + faceI, + f[anchorFp], // Anchor point + + own, + nei + ); + + + if (debug) + { + // Get mesh cell centres + const vectorField& meshCellCentres = + mesh_.cellCentres(); + + if (mesh_.isInternalFace(faceI)) + { + const label oldOwn = meshFaceOwner[faceI]; + const label oldNei = meshFaceNeighbour[faceI]; + + checkInternalOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshCellCentres[oldNei], + newFace + ); + } + else + { + const label oldOwn = meshFaceOwner[faceI]; + + checkBoundaryOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshFaceCentres[faceI], + newFace + ); + } + } // End debug + + // Modify the face + modifyFace(ref, faceI, newFace, own, nei); + + // Mark face as handled + facesToSplit[faceI] = false; + + }// End if unsplit, unhandled face + } // End for all edge faces + } // End if edge has been cut + } // End for all edges + + + // PART 6.3.: Modify faces that do not get split but whose owner/neighbour + // change due to splitting + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << " Changing owner/neighbour for otherwise unaffected faces..." + << endl; + } + + forAll(facesToSplit, faceI) + { + // All remaining unnaffected faces are the ones whose owner/neighbour + // changed + if (facesToSplit[faceI]) + { + // Get the face + const face& f = meshFaces[faceI]; + + // The point with the lowest level should be an anchor point of the + // neighbouring cells + label anchorFp = findMaxLevel(f); + + label own, nei; + setNewFaceNeighbours + ( + cellAnchorPoints, + cellAddedCells, + faceI, + f[anchorFp], // Anchor point + + own, + nei + ); + + // Modify the face, changing owner and neighbour + modifyFace(ref, faceI, f, own, nei); + + // Mark face as handled + facesToSplit[faceI] = false; + } + } + + + // PART 6.4. Add new internal faces inside split cells + + // We have to find the splitting points between the anchor points. But the + // edges between the anchor points might have been split (into two, three or + // four edges) + + if (debug) + { + Pout<< "polyhedralRefinement::setRefinementInstruction(...)" << nl + << " Adding new internal faces for split cells..." + << endl; + } + + // Loop through cells + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + // Cell has been split, create internal faces + createInternalFaces + ( + cellAnchorPoints, + cellAddedCells, + cellMidPoint, + faceMidPoint, + faceAnchorLevel, + edgeMidPoint, + cellI, + ref + ); + } + } + + // Debug: check minimum point index of added points, needs to be equal to + // number of points in the original mesh + if (debug) + { + label minPointI = labelMax; + label maxPointI = labelMin; + + forAll(cellMidPoint, cellI) + { + if (cellMidPoint[cellI] > -1) + { + minPointI = min(minPointI, cellMidPoint[cellI]); + maxPointI = max(maxPointI, cellMidPoint[cellI]); + } + } + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + minPointI = min(minPointI, faceMidPoint[faceI]); + maxPointI = max(maxPointI, faceMidPoint[faceI]); + } + } + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + minPointI = min(minPointI, edgeMidPoint[edgeI]); + maxPointI = max(maxPointI, edgeMidPoint[edgeI]); + } + } + + if (minPointI != labelMax && minPointI != mesh_.nPoints()) + { + FatalErrorInFunction + << "Added point labels not consecutive to existing mesh points." + << nl + << "mesh_.nPoints():" << mesh_.nPoints() + << " minPointI: " << minPointI + << " maxPointI: " << maxPointI + << abort(FatalError); + } + } +} + + +void Foam::polyhedralRefinement::setUnrefinementInstruction +( + batchPolyTopoChange& ref +) const +{ + // Note: assumes that splitPointsToUnrefine_ are set prior to the function + // call + + // Check whether the refinementLevelIndicator is valid + if (refinementLevelIndicator_.size() != mesh_.nCells()) + { + FatalErrorInFunction + << "Refinement level indicator list has invalid size: " + << refinementLevelIndicator_.size() + << ", number of cells: " << mesh_.nCells() + << nl + << "Make sure to call setRefinementInstruction(...) before" + << " calling setPolyhedralRefinement(...)." + << abort(FatalError); + } + + // Get point cells necessary for debug and face removal + const labelListList& meshPointCells = mesh_.pointCells(); + + if (debug) + { + Pout<< "polyhedralRefinement::setUnrefinementInstruction" + << "(batchPolyTopoChange& ref)" + << nl + << "Checking validity of cellLevel before setting unrefinement." + << endl; + + forAll(cellLevel_, cellI) + { + if (cellLevel_[cellI] < 0) + { + FatalErrorInFunction + << "Illegal cell level " << cellLevel_[cellI] + << " for cell " << cellI + << abort(FatalError); + } + } + + // Write split points into a point set + pointSet pSet + ( + mesh_, + "splitPoints", + labelHashSet(splitPointsToUnrefine_) + ); + pSet.write(); + + // Write split point cells into a cell set + cellSet cSet + ( + mesh_, + "splitPointCells", + splitPointsToUnrefine_.size() + ); + + forAll(splitPointsToUnrefine_, i) + { + // Get point cells and insert them into cell set + const labelList& pCells = meshPointCells[splitPointsToUnrefine_[i]]; + + forAll(pCells, j) + { + cSet.insert(pCells[j]); + } + } + cSet.write(); + + Pout<< "polyhedralRefinement::setUnrefinementInstruction" + << "(batchPolyTopoChange& ref)" + << nl + << "Writing " << pSet.size() + << " points and " + << cSet.size() << " cells for unrefinement to" << nl + << "pointSet " << pSet.objectPath() << nl + << "cellSet " << cSet.objectPath() + << endl; + } + + // Update refinementLevelIndicator for all cells that will be unrefined + forAll(splitPointsToUnrefine_, i) + { + // Get point cells and mark them for unrefinement + const labelList& pCells = meshPointCells[splitPointsToUnrefine_[i]]; + + forAll(pCells, j) + { + refinementLevelIndicator_[pCells[j]] = UNREFINED; + } + } + + // Create lists needed by face remover + labelList cellRegion; + labelList cellRegionMaster; + labelList facesToRemove; + + // Memory management + { + // Collect split faces in the hash set, guess size to prevent excessive + // resizing + labelHashSet splitFaces(12*splitPointsToUnrefine_.size()); + + // Get point faces + const labelListList& meshPointFaces = mesh_.pointFaces(); + + forAll(splitPointsToUnrefine_, i) + { + // Loop through all faces of this point and insert face index + const labelList& pFaces = meshPointFaces[splitPointsToUnrefine_[i]]; + + forAll(pFaces, j) + { + splitFaces.insert(pFaces[j]); + } + } + + // Check with faceRemover what faces will get removed. Note that this + // can be more (but never less) than splitFaces provided. + faceRemover_.compatibleRemoves + ( + splitFaces.toc(), // Pierced faces + + cellRegion, // Region merged into (-1 for no region) + cellRegionMaster, // Master cell for region + facesToRemove // List of faces to be removed + ); + + if (facesToRemove.size() != splitFaces.size()) + { + FatalErrorInFunction + << "Either the initial set of split points to unrefine does not" + << " seem to be consistent or there are no mid points of" + << " refined cells." + << abort(FatalError); + } + } + + // Find point region master for every cell region. This is the central point + // from which the coarse cell will be made + // The property of the point region master is that all cells that touch it + // have the same cell region index + // HJ, 6/Sep/2019 + labelList pointRegionMaster(cellRegionMaster.size(), label(-1)); + + // Get point-cell addressing + const labelListList& pc = mesh_.pointCells(); + + forAll (splitPointsToUnrefine_, i) + { + const labelList& curPc = pc[splitPointsToUnrefine_[i]]; + + label curRegion = -1; + + forAll (curPc, curPcI) + { + if (curRegion == -1) + { + // First region found. Grab it + curRegion = cellRegion[curPc[curPcI]]; + } + else + { + // Region already found. Check that all other cells that + // touch this point have the same region + if (curRegion != cellRegion[curPc[curPcI]]) + { + // Error: different region cells touching in split point + // This is not a valid unrefinement pattern + FatalErrorInFunction + << "Different region cells touching in split point." + << abort(FatalError); + } + } + } + + // Record point region master + if (curRegion > -1) + { + pointRegionMaster[curRegion] = splitPointsToUnrefine_[i]; + } + else + { + // Error: Cannot find region for point + FatalErrorInFunction + << "Different region cells touching in split point." + << abort(FatalError); + } + } + + // Insert all commands to combine cells + faceRemover_.setRefinement + ( + facesToRemove, + cellRegion, + // pointRegionMaster, // OpenFOAM does not support master point + cellRegionMaster, + ref + ); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::polyhedralRefinement::polyhedralRefinement +( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme +) +: + refinement(name, dict, index, mme) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::polyhedralRefinement::setCellsToRefine +( + const labelList& refinementCellCandidates +) +{ + if (debug) + { + Info<< "polyhedralRefinement::setCellsToRefine" + << "(const labelList& refinementCellCandidates)" << nl + << "Setting cells to refine" << endl; + } + + // Create a mark-up field for cells to refine + boolList refineCell(mesh_.nCells(), false); + + // Roughly count how many cells we are going to end up with + label roughCellCountAfterRefinement = mesh_.nCells(); + + // Get cell points to count number of additional cells + const labelListList& meshCellPoints = mesh_.cellPoints(); + + // Mark initial refinement candidates for refinement only if the cell level + // is smaller than the maximum refinement level. Note: stop marking them if + // we exceed the rough cell count + forAll (refinementCellCandidates, i) + { + // Get cell index + const label& cellI = refinementCellCandidates[i]; + + if (roughCellCountAfterRefinement < maxCells_) + { + // Mark cell for refinement + refineCell[cellI] = true; + + // Increment number of cells (nPoints - 1 new cells per cell) + roughCellCountAfterRefinement += meshCellPoints[cellI].size() - 1; + } + } + + // Extend cells across faces using a specified number of refinement buffer + // layers + for (label i = 0; i < nRefinementBufferLayers_; ++i) + { + meshTools::extendMarkedCellsAcrossFaces(mesh_, refineCell); + } + + // Remove all cells that would exceed the maximum refinement level + forAll (refineCell, cellI) + { + if (refineCell[cellI] && (cellLevel_[cellI] + 1 > maxRefinementLevel_)) + { + refineCell[cellI] = false; + } + } + + // Make sure that the refinement is face consistent (2:1 consistency) and + // point consistent (4:1 consistency) if necessary + + // Counter for additional cells to refine due to consistency in each + // iteration and number of iterations + label nAddCells = 0; + label nIters = 0; + label nTotalAddCells = 0; + + do + { + // Reset counter at the beginning of each iteration + nAddCells = 0; + + if (edgeBasedConsistency_) + { + // Check for 4:1 edge based consistent refinement. Updates + // cellsToRefine and returns number of cells added in this iteration + nAddCells += edgeConsistentRefinement(refineCell); + } + + // Check for 2:1 face based consistent refinement. Updates cellsToRefine + // and returns number of cells added in this iteration + nAddCells += faceConsistentRefinement(refineCell); + + // Global reduction + reduce(nAddCells, sumOp<label>()); + + // Increment number of iterations and total number of added cells + ++nIters; + nTotalAddCells += nAddCells; + + if (debug) + { + Info<< "Added " << nAddCells << " cells in iteration " + << nIters << nl; + } + + } while (nAddCells > 0); + + Info<< "Added " << nTotalAddCells // nTotalAddCells already reduced + << " cells in " << returnReduce(nIters, maxOp<label>()) + << " iterations to obtain consistent refinement." + << endl; + + // Collect all cells to refine in a dynamic list + DynamicList<label> cellsToRefineDynamic(mesh_.nCells()); + + forAll (refineCell, cellI) + { + if (refineCell[cellI]) + { + // Cell marked for refinement, append it + cellsToRefineDynamic.append(cellI); + } + } + + // Transfer the contents into the data member (ordinary list) + cellsToRefine_.transfer(cellsToRefineDynamic); + + Info<< "Selected " << returnReduce(cellsToRefine_.size(), sumOp<label>()) + << " cells to refine." << endl; +} + + +void Foam::polyhedralRefinement::setSplitPointsToUnrefine +( + const labelList& unrefinementPointCandidates +) +{ + if (debug) + { + Info<< "polyhedralRefinement::setSplitPointsToUnrefine" + << "(const labelList& unrefinementPointCandidates)" << nl + << "Setting split points to unrefine." << endl; + } + + // Get necessary mesh data + const label nPoints = mesh_.nPoints(); + const labelListList& meshCellPoints = mesh_.cellPoints(); + + // PART 1: Mark all split points in the mesh (points that can be unrefined) + boolList splitPointsMarkup(nPoints, false); + + // Algorithm: split point is uniquely defined as a point that: + // 1. Has pointLevel_ > 0 (obviously), + // 2. A point that has the same pointLevel_ as ALL of the points of its + // faces. In other words, for each point, we will look through all the + // faces of the point. For each face, we will visit points and check the + // point level of all of these points. All point levels must be the same + // for this point candidate to be split point. This is quite useful since + // there is no need to store the refinement history + + // Get necessary mesh data + const faceList& meshFaces = mesh_.faces(); + const labelListList& meshPointFaces = mesh_.pointFaces(); + + // Loop through all points + forAll (meshPointFaces, pointI) + { + // Get point level of this point + const label& centralPointLevel = pointLevel_[pointI]; + + if (centralPointLevel < 1) + { + // Point can't be unrefined as its level is either 0 or + // invalid. Continue immediately + continue; + } + + // Flag to see whether this is a split point candidate + bool splitPointCandidate = true; + + // Get face labels for this point + const labelList& pFaces = meshPointFaces[pointI]; + + // Loop through all point faces + forAll (pFaces, i) + { + // Get face index and the face + const label& faceI = pFaces[i]; + const face& curFace = meshFaces[faceI]; + + // Loop through points of the face + forAll (curFace, j) + { + // Get point index + const label& pointJ = curFace[j]; + + if (pointLevel_[pointJ] != centralPointLevel) + { + // Point levels are different, this can't be a split point, + // set flag to false and break immediatelly + splitPointCandidate = false; + break; + } + // else: this is still potential split point candidate so + // there's nothing to do + } // End for all points of this face + + // Check whether this can't be a split point already and break out + // immediately + if (!splitPointCandidate) + { + break; + } + } // End for all point faces + + // At this point, if the flag is still true, this is a split point + if (splitPointCandidate) + { + splitPointsMarkup[pointI] = true; + } + } + + // Note: if there is no dynamic load balancing, points at the boundary + // cannot be split points by definition. However, in dynamic load balancing + // runs, it is possible that a split point ends on processor boundary, in + // which case we will simply avoid (actually delay) unrefining until this + // becomes internal point again. VV, 4/Jul/2018. + const label nInternalFaces = mesh_.nInternalFaces(); + const label nFaces = mesh_.nFaces(); + + for (label faceI = nInternalFaces; faceI < nFaces; ++faceI) + { + // Get the face and make sure that the points are unmarked + const face& f = meshFaces[faceI]; + + forAll (f, fpI) + { + splitPointsMarkup[f[fpI]] = false; + } + } + + // PART 2: Mark all unrefinement point candidates that are split points at + // the same time (basically the intersection of split points and candidates) + + // Create markup field of split points to unrefine + // True: this is a split point which should be unrefined + // False: this is either not a split point or it shouldn't be unrefined + boolList splitPointsToUnrefine(nPoints, false); + + // Loop through all unrefinement candidates + forAll (unrefinementPointCandidates, i) + { + // Get point index + const label& pointI = unrefinementPointCandidates[i]; + + if (splitPointsMarkup[pointI]) + { + // This is a split point, mark it for unrefinement + splitPointsToUnrefine[pointI] = true; + } + } + + + // PART 3: Make sure that we skip unrefining around split points that + // possibly have cells around that will be refined + + // Mark cells that need to be protected (will be refined in this iteration) + boolList protectedCell(mesh_.nCells(), false); + + // Loop through cells to refine and mark them + forAll (cellsToRefine_, i) + { + protectedCell[cellsToRefine_[i]] = true; + } + + // Extend protected cells across points using a specified number of + // unrefinement buffer layers + for (label i = 0; i < nUnrefinementBufferLayers_; ++i) + { + meshTools::extendMarkedCellsAcrossPoints(mesh_, protectedCell); + } + + // Loop through all cells and if the cell should be protected, protect all + // of its points from unrefinement + forAll (protectedCell, cellI) + { + if (protectedCell[cellI]) + { + // Get list of cell points for this protected cell + const labelList& cPoints = meshCellPoints[cellI]; + + // Loop through cell points and make sure that they are not marked + // for unrefinement + forAll (cPoints, j) + { + splitPointsToUnrefine[cPoints[j]] = false; + } + } + } + + + // PART 4: Ensure face consistent (2:1 constraint) and possibly point + // consistent (4:1 constraint) unrefinement + + // Get necessary mesh data + const label nCells = mesh_.nCells(); + const labelListList& meshPointCells = mesh_.pointCells(); + + // Count number of removed cells from unrefinement (cells that will not be + // unrefined) in each iteration and number of iterations + label nRemCells = 0; + label nIters = 0; + label nTotalRemCells = 0; + + do + { + // First, create cells to unrefine (all cells sharing point to unrefine) + boolList cellsToUnrefine(nCells, false); + + // Loop through all split points to unrefine + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // This split point is marked for unrefinement, collect all of + // its cells + const labelList& pCells = meshPointCells[pointI]; + forAll (pCells, i) + { + cellsToUnrefine[pCells[i]] = true; + } + } + } + + // Reset number of removed cells from unrefinement for this iteration + nRemCells = 0; + + if (edgeBasedConsistency_) + { + // Check for 4:1 edge based consistent unrefinement. Updates + // cellsToUnrefine and returns number of removed cells from + // unrefinement in this iteration + nRemCells += edgeConsistentUnrefinement(cellsToUnrefine); + } + + // Check for 2:1 face based consistent unrefinement. Updates + // cellsToUnrefine and returns number of removed cells from unrefinement + // in this iteration + nRemCells += faceConsistentUnrefinement(cellsToUnrefine); + + // Global reduction + reduce(nRemCells, sumOp<label>()); + + // If we have removed at least one cell from unrefinement, we need to + // protect its split points as well from unrefinement + if (nRemCells > 0) + { + // Get point cells + const labelListList& meshPointCells = mesh_.pointCells(); + + // Loop through all split points to unrefine + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // This is a split point for unrefinement, get the cells + const labelList& pCells = meshPointCells[pointI]; + + // Loop through all point cells + forAll (pCells, i) + { + if (!cellsToUnrefine[pCells[i]]) + { + // Cell must not be refined, remove point from + // unrefinement as well + splitPointsToUnrefine[pointI] = false; + break; + } + } + } + } + } + + // Increment number of iterations and number of total removed cells + ++nIters; + nTotalRemCells += nRemCells; + + if (debug) + { + Info<< "Removed " << nRemCells << " cells in iteration " + << nIters << nl; + } + + } while (nRemCells > 0); + + Info<< "Removed " << nTotalRemCells // nTotalRemCells already reduced + << " cells in " << returnReduce(nIters, maxOp<label>()) + << " iterations to obtain consistent unrefinement." + << endl; + + // Collect all split points to unrefine in a dynamic list + DynamicList<label> splitPointsToUnrefineDynamic(nPoints); + + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // Split point marked for unrefinement, append it + splitPointsToUnrefineDynamic.append(pointI); + } + } + + // Transfer the contents into the data member (ordinary list) + splitPointsToUnrefine_.transfer(splitPointsToUnrefineDynamic); + + Info<< "Selected " + << returnReduce(splitPointsToUnrefine_.size(), sumOp<label>()) + << " split points to unrefine." << endl; +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.H b/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.H new file mode 100644 index 0000000000000000000000000000000000000000..3a3f8140bbf0edfe09e35e093241f65bbd04f7d3 --- /dev/null +++ b/src/dynamicMesh/refinement/polyhedralRefinement/polyhedralRefinement.H @@ -0,0 +1,246 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Class + Foam::polyhedralRefinement + +Description + Isotropic refinement of polyhedral cells using the mesh modifier engine + + Each polyhedral cell is split by the following procedure: + 1. Adding points at the edge centre, face centre and cell centre, + 2. Adding cells n cells where n is the number of points of the cell, + 3. Splitting each face into multiple faces going from: + existing corner point -> new edge centre point -> new face centre + point -> other new edge centre point (sharing the same corner point) + 4. Adding internal faces going from: + new edge centre point -> new face centre point -> new cell centre + point -> other new face centre point (sharing the same edge) + +SourceFiles + polyhedralRefinement.C + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + Rewrite, porting and changes by Hrvoje Jasak, Wikki Ltd. All rights reserved. + +\*---------------------------------------------------------------------------*/ + +#ifndef polyhedralRefinement_H +#define polyhedralRefinement_H + +#include "refinement.H" +#include "polyMesh.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class polyhedralRefinement Declaration +\*---------------------------------------------------------------------------*/ + +class polyhedralRefinement +: + public refinement +{ +private: + + // Private Member Functions + + // Helper functions + + //- Get least cell level such that the face has at least three + // points smaller than the level + label getAnchorLevel(const label faceI) const; + + + // Local topology modification functions (operate on cells/faces) + + //- Create all internal faces of split cellI into n cells + // where n is the number of cell points + void createInternalFaces + ( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const labelList& cellMidPoint, + const labelList& faceMidPoint, + const labelList& faceAnchorLevel, + const labelList& edgeMidPoint, + const label cellI, + batchPolyTopoChange& ref + ) const; + + + // Topological change helper functions + + //- Get cell added to point of cellI (if any) + label getAnchorCell + ( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const label cellI, + const label faceI, + const label pointI + ) const; + + //- Set new owner and neighbour (in unspecified order) of pointI + // on faceI + void setNewFaceNeighbours + ( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const label faceI, + const label pointI, + + label& own, + label& nei + ) const; + + //- Find index of point with wantedLevel, starting from fp + label findLevel + ( + const face& f, + const label startFp, + const bool searchForward, + const label wantedLevel + ) const; + + //- Store in maps correspondence from midpoint to anchors and + // faces. Used when creating internal faces + label storeMidPointInfo + ( + const labelListList& cellAnchorPoints, + const labelListList& cellAddedCells, + const labelList& cellMidPoint, + const labelList& edgeMidPoint, + const label cellI, + const label faceI, + const bool faceOrder, + const label midPointI, + const label anchorPointI, + const label faceMidPointI, + + Map<edge>& midPointToAnchors, + Map<edge>& midPointToFaceMids, + batchPolyTopoChange& ref + ) const; + + //- If p0 and p1 are existing vertices check if edge is split + // and insert splitPoint. Used with storing mid point + void insertEdgeSplit + ( + const labelList& edgeMidPoint, + const label p0, + const label p1, + DynamicList<label>& verts + ) const; + + + // Copy control + + //- Disallow default bitwise copy construct + polyhedralRefinement(const polyhedralRefinement&) = delete; + + //- Disallow default bitwise assignment + void operator=(const polyhedralRefinement&) = delete; + + +protected: + + // Protected Pure Virtual Member Functions + + // Global topology modification functions (operate on whole polyMesh) + + //- Set refinement instruction + virtual void setRefinementInstruction + ( + batchPolyTopoChange& ref + ) const; + + //- Set unrefinement instruction + virtual void setUnrefinementInstruction + ( + batchPolyTopoChange& ref + ) const; + + +public: + + //- Runtime type information + TypeName("polyhedralRefinement"); + + + // Constructors + + //- Construct from dictionary + polyhedralRefinement + ( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme + ); + + + //- Destructor + virtual ~polyhedralRefinement() = default; + + + // Member Functions + + // Edit + + //- Set cells to refine given a list of refinement + // candidates. Refinement candidates are extended within the + // function due to possible 4:1 conflicts and specified number of + // buffer layers. + // Note: must be called BEFORE setSplitPointsToUnrefine + virtual void setCellsToRefine + ( + const labelList& refinementCellCandidates + ); + + //- Set split points to unrefine given a list of all mesh points + // that are candidates for unrefinement. Split points are + // determined as a subset of unrefinement candidates, avoiding + // splitting points of cells that are going to be refined at the + // same time and ensuring consistent unrefinement. + // Note: must be called AFTER setCellsToRefine + virtual void setSplitPointsToUnrefine + ( + const labelList& unrefinementPointCandidates + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.C b/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.C new file mode 100644 index 0000000000000000000000000000000000000000..87595e58cbcfc80f4d1a3e82b4ce09b6941299e1 --- /dev/null +++ b/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.C @@ -0,0 +1,2978 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + +\*---------------------------------------------------------------------------*/ + +#include "prismatic2DRefinement.H" +#include "cellSet.H" +#include "faceSet.H" +#include "pointSet.H" +#include "emptyPolyPatch.H" +#include "wedgePolyPatch.H" +#include "addToRunTimeSelectionTable.H" +#include "meshTools.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(prismatic2DRefinement, 0); + addToRunTimeSelectionTable + ( + polyMeshModifier, + prismatic2DRefinement, + dictionary + ); +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::label Foam::prismatic2DRefinement::getAnchorLevel +( + const label faceI, + const label nPoints +) const +{ + // Get the face + const face& f = mesh_.faces()[faceI]; + + // Sanity check for expected number of points + if (nPoints != 3 && nPoints != 4) + { + FatalErrorInFunction + << "Trying to find anchor level with " << nPoints << " points" + << " smaller than anchor level. Only nPoints = 3 and 4 are" + << " supported." + << abort(FatalError); + } + + // Sanity check: if we are expecting to find 4 points on a face which is not + // on special patch (empty or wedge) and we find a face with 3 points, issue + // an error + if (f.size() <= 3 && nPoints == 4) + { + FatalErrorInFunction + << "Expected to find at least 4 points with level lower than" + << " anchor level." + << nl + << "Make sure to call this function with nPoints = 4 only if" + << " you do not expect triangular faces." + << abort(FatalError); + } + + if (f.size() <= nPoints) + { + return pointLevel_[f[findMaxLevel(f)]]; + } + else + { + const label& ownLevel = cellLevel_[mesh_.faceOwner()[faceI]]; + + if (countAnchors(f, ownLevel) >= nPoints) + { + return ownLevel; + } + else if (countAnchors(f, ownLevel + 1) >= nPoints) + { + return ownLevel + 1; + } + else + { + return -1; + } + } +} + + +void Foam::prismatic2DRefinement::appendFaceSplitInfo +( + const label& faceI, + const boolList& edgeOnPatchToCut, + const labelList& edgeMidPoint, + DynamicList<label>& splitFacesIntoTwo, + DynamicList<Pair<label> >& splitFacesEmptyEdges +) const +{ + // First append the face into the list + splitFacesIntoTwo.append(faceI); + + // Grab all edges of the face + const labelList& curEdges = mesh_.faceEdges()[faceI]; + + // Create placeholders for two edge levels. Initialise with -1 + // for sanity checks later on + label specialPatchEdgeI = -1; + label specialPatchEdgeJ = -1; + + // Count number of edges for sanity checks + label nEdgesOnSpecialPatch = 0; + + // Collect the two edge labels found on the special patch (empty or wedge) + forAll (curEdges, i) + { + // Get edge index + const label edgeI = curEdges[i]; + + if (edgeOnPatchToCut[edgeI]) + { + // This edge is on special patch (empty or wedge), check whether it + // is first, second or invalid + switch (nEdgesOnSpecialPatch) + { + case 0: + specialPatchEdgeI = edgeI; + break; + case 1: + specialPatchEdgeJ = edgeI; + break; + default: + FatalErrorInFunction + << "Found more than two edges on face " << faceI + << " on the special patch (empty or wedge)." + << nl + << "Either this is not a valid 2D mesh or" + << " we are visiting wrong faces." + << abort(FatalError); + } + + // Increment the counter + ++nEdgesOnSpecialPatch; + + } // End if edge on special patch (empty or wedge) + + } // End loop over all edges + + // Debug: additional check whether the two edges are marked for + // refinement (they should be) + if (debug) + { + if (edgeMidPoint[specialPatchEdgeI] == -1) + { + FatalErrorInFunction + << "Empty patch edge with index: " << specialPatchEdgeI + << " not marked for splitting" + << nl + << "Check edgeMidPoint selection algorithm." + << abort(FatalError); + } + + if (edgeMidPoint[specialPatchEdgeI] == -1) + { + FatalErrorInFunction + << "Empty patch edge with index: " << specialPatchEdgeJ + << " not marked for splitting" + << nl + << "Check edgeMidPoint selection algorithm." + << abort(FatalError); + } + } + + // At this point, we should have the two edges we were looking + // for, collect them into the list with additional sanity check + if (specialPatchEdgeI > -1 && specialPatchEdgeJ > -1) + { + // Append the list of two edges in increasing order (just in + // case we end up needing this information + if (specialPatchEdgeI < specialPatchEdgeJ) + { + splitFacesEmptyEdges.append + ( + Pair<label>(specialPatchEdgeI, specialPatchEdgeJ) + ); + } + else + { + splitFacesEmptyEdges.append + ( + Pair<label>(specialPatchEdgeJ, specialPatchEdgeI) + ); + } + } + else + { + FatalErrorInFunction + << "Found invalid indices for edges on special patches:" + << nl + << "specialPatchEdgeI: " << specialPatchEdgeI + << ", specialPatchEdgeJ: " << specialPatchEdgeJ + << nl + << "Something went wrong. Check face edges." + << abort(FatalError); + } +} + + +void Foam::prismatic2DRefinement::setNewFaceNeighbours +( + const HashTable + < + label, + Pair<label>, + Hash<FixedList<label, 2> > + >& pointCellToAddedCellMap, + const labelListList& cellAddedCells, + const label& faceI, + const label& pointI, + + label& own, + label& nei +) const +{ + // Get anchor cell for this anchor point on owner side + const label ownCellI = mesh_.faceOwner()[faceI]; + + // Get cell added cells for owner cell + const labelList& cAddedOwn = cellAddedCells[ownCellI]; + + // If the cell added cells list is not empty, return the necessary cell + // index fetched with pointCellToAddedCellMap + const Pair<label> pointOwnerCellPair(pointI, ownCellI); + + if (cAddedOwn.empty()) + { + // Cell added cells is empty for owner, fetch original owner + own = ownCellI; + } + else if (!pointCellToAddedCellMap.found(pointOwnerCellPair)) + { + // Point-cell pair not found, meaning that we need to search for the + // closest point which has lower level than this point. This happens if + // we are splitting a face that has already been split but has different + // owner/neighbour levels (e.g. owner was refined, but the neighbour was + // not in the previous time step). It is possible that we end up with + // splitting this face with point levels e.g. (1 1 0 0). Therefore, we + // need to search for the point with minimum level on the edges sharing + // this point. + + // Find point in the local faces addressing + const face& f = mesh_.faces()[faceI]; + const label fpI = f.find(pointI); + + if (fpI == -1) + { + FatalErrorInFunction + << "Point: " << pointI << " not found in face: " << f + << ", with face index: " << faceI + << nl + << "Point must belong to the face." + << nl + << "Error encounted when dealing with owner cell: " + << ownCellI + << abort(FatalError); + } + + // Find the global point index with minimum edge connected level + const label anchorPointI = findMinEdgeConnectedLevel + ( + fpI, // current face point + faceI, // face index + f, // face + mesh_.faceEdges()[faceI], // face edges + mesh_.edges() // mesh edges + ); + + // If the point is the same as pointI, we did not find any valid point + if (anchorPointI == pointI) + { + FatalErrorInFunction + << "Could not find different adjacent anchor point." + << nl + << "pointI: " << pointI << " faceI: " << faceI + << "Error encounted when dealing with owner cell: " + << ownCellI + << abort(FatalError); + } + + // Set owner cell + own = cAddedOwn + [ + pointCellToAddedCellMap[Pair<label>(anchorPointI, ownCellI)] + ]; + } + else + { + // Cell added cells is not empty and the mapping is found. Set owner + // from mapping + own = cAddedOwn[pointCellToAddedCellMap[pointOwnerCellPair]]; + } + + if (mesh_.isInternalFace(faceI)) + { + // Get anchor cell for this anchor point on neighbour side + const label neiCellI = mesh_.faceNeighbour()[faceI]; + + // Get cell added cells for neighbour cell + const labelList& cAddedNei = cellAddedCells[neiCellI]; + + // If the cell added cells list is not empty, return the necessary cell + // index fetched with pointCellToAddedCellMap + + const Pair<label> pointNeighbourCellPair(pointI, neiCellI); + + if (cAddedNei.empty()) + { + // Cell added cells is empty for neighbour, fetch original neighbour + nei = neiCellI; + } + else if (!pointCellToAddedCellMap.found(pointNeighbourCellPair)) + { + // Point-cell pair not found, meaning that we need to search for the + // closest point which has lower level than this point. This happens + // if we are splitting a face that has already been split but has + // different owner/neighbour levels (e.g. owner was refined, but the + // neighbour was not in the previous time step). It is possible that + // we end up with splitting this face with point levels e.g. (1 1 0 + // 0). Therefore, we need to search for the point with minimum level + // on the edges sharing this point. + + // Find point in the local faces addressing + const face& f = mesh_.faces()[faceI]; + const label fpI = f.find(pointI); + + if (fpI == -1) + { + FatalErrorInFunction + << "Point: " << pointI << " not found in face: " << f + << ", with face index: " << faceI + << nl + << "Point must belong to the face." + << nl + << "Error encounted when dealing with neighbour cell: " + << neiCellI + << abort(FatalError); + } + + // Find the global point index with minimum edge connected level + const label anchorPointI = findMinEdgeConnectedLevel + ( + fpI, // current face point + faceI, // face index + f, // face + mesh_.faceEdges()[faceI], // face edges + mesh_.edges() // mesh edges + ); + + // If the point is the same as pointI, we did not find any valid point + if (anchorPointI == pointI) + { + FatalErrorInFunction + << "Could not find different adjacent anchor point." + << nl + << "pointI: " << pointI << " faceI: " << faceI + << "Error encounted when dealing with neighbour cell: " + << neiCellI + << abort(FatalError); + } + + // Set owner cell + nei = cAddedNei + [ + pointCellToAddedCellMap[Pair<label>(anchorPointI, neiCellI)] + ]; + } + else + { + // Cell added cells is not empty and the mapping is found. Set + // neighbour from mapping + nei = cAddedNei[pointCellToAddedCellMap[pointNeighbourCellPair]]; + } + } + else + { + // Boundary face: set neighbour to -1 + nei = -1; + } +} + + +Foam::label Foam::prismatic2DRefinement::findMinEdgeConnectedLevel +( + const label& fpI, + const label& faceI, + const face& f, + const labelList& fEdges, + const edgeList& meshEdges +) const +{ + // Get point index and initialize anchor point + const label& pointI = f[fpI]; + + label anchorPointI = pointI; + + // Check the other point on edge starting with pointI + const label& edgeIndexAfterPoint = fEdges[fpI]; + const edge& edgeAfter = meshEdges[edgeIndexAfterPoint]; + + // Get other point on edge after + const label& pointAfter = edgeAfter.otherVertex(pointI); + + if (pointLevel_[pointAfter] < pointLevel_[anchorPointI]) + { + anchorPointI = pointAfter; + } + + // Check the other point on edge ending with pointI + const label& edgeIndexBeforePoint = fEdges[f.rcIndex(fpI)]; + const edge& edgeBefore = meshEdges[edgeIndexBeforePoint]; + + // Get other point on edge before + const label& pointBefore = edgeBefore.otherVertex(pointI); + + if (pointLevel_[pointBefore] < pointLevel_[anchorPointI]) + { + anchorPointI = pointBefore; + } + + return anchorPointI; +} + + +void Foam::prismatic2DRefinement::addFaceMids +( + const labelList& faceMidPoint, + const boolList& faceOnPatchToCut, + const label& faceI, + const label& cellI, + face& newFace +) const +{ + // b) faceMidPoint for this face + newFace[1] = faceMidPoint[faceI]; + + // c) faceMidPoint for the face on the other side + // The other face is uniquely defined as the other face of the same cell + // which is on special patch (empty or wedge) + + // Get the cell + const cell& cFaces = mesh_.cells()[cellI]; + + // Loop through cell faces + forAll(cFaces, i) + { + // Get face index + const label& faceJ = cFaces[i]; + + if (faceOnPatchToCut[faceJ] && (faceI != faceJ)) + { + // This is the face we're looking for, add its + // midpoint and double check if it is valid + if (faceMidPoint[faceJ] > -1) + { + newFace[2] = faceMidPoint[faceJ]; + break; + } + else + { + FatalErrorInFunction + << "Other face: " + << faceJ + << " has not been selected for splitting," + << " while the face on original side: " + << faceI + <<" has been selected." + << abort(FatalError); + } + } // End if this is our face + } // End for all cell faces +} + + +void Foam::prismatic2DRefinement::checkNewFaceOrientation +( + batchPolyTopoChange& ref, + const label& faceI, + const face& newFace +) const +{ + // Get mesh cell centres + const vectorField& meshCellCentres = mesh_.cellCentres(); + + if (mesh_.isInternalFace(faceI)) + { + // Get old owner/neighbour indices + const label oldOwn = mesh_.faceOwner()[faceI]; + const label oldNei = mesh_.faceNeighbour()[faceI]; + + // Print info only with deep debug level + if (debug > 1) + { + Pout<< "Split infternal face: " << faceI + << ", into quad: " << newFace << nl + << "owner: " << oldOwn + << ", neighbour: " << oldNei << endl; + } + + checkInternalOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshCellCentres[oldNei], + newFace + ); + } + else + { + // Get face centres and old owner + const vectorField& meshFaceCentres = mesh_.faceCentres(); + const label oldOwn = mesh_.faceOwner()[faceI]; + + // Print info only with deep debug level + if (debug > 1) + { + Pout<< "Split boundary face: " << faceI + << ", into quad: " << newFace << nl + << "owner: " << oldOwn << endl; + } + + checkBoundaryOrientation + ( + ref, + oldOwn, + faceI, + meshCellCentres[oldOwn], + meshFaceCentres[faceI], + newFace + ); + } +} + + +// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * // + +void Foam::prismatic2DRefinement::setRefinementInstruction +( + batchPolyTopoChange& ref +) const +{ + // Note: assumes that cellsToRefine_ are set prior to the function call + + // Reset refinementLevelIndicator field. Note: the list is cleared in + // updateMesh member function after updating cell and point levels + if (refinementLevelIndicator_.empty()) + { + // List has been reset correctly, initialise it for this iteration + refinementLevelIndicator_.setSize(mesh_.nCells(), UNCHANGED); + } + else + { + // List has not been reset correctly, issue an error + FatalErrorInFunction + << "Refinement level indicator list has not been" + << " reset properly." << nl + << "Either the call to updateMesh() after performing" + << " refinement has not been made or the call to" + << " setRefinementInstruction(...) and" + << " setUnrefinementInstruction(...) has not been made in" + << " correct order." << nl + << "Make sure to set refinement, set unrefinement and call" + << " updateMesh after performing the topo change." + << abort(FatalError); + } + + + // PART 1: Mark cells for refinement + + // Bool list that marks cells which will be refined + boolList refineCellsMask(mesh_.nCells(), false); + forAll (cellsToRefine_, i) + { + // Simply mark the cell as refined, there are no additional points to + // add (in cell centre for example) + refineCellsMask[cellsToRefine_[i]] = true; + } + + // Write out cells to refine as a cell set for debug + if (debug) + { + // Note: cellSet is actually a hash table of labels + cellSet splitCells(mesh_, "splitCells", cellsToRefine_.size()); + + forAll(refineCellsMask, cellI) + { + if (refineCellsMask[cellI]) + { + // Cell is marked for refinement, insert into cellSet + splitCells.insert(cellI); + } + } + + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Writing " << splitCells.size() + << " cells to split to cellSet " << splitCells.objectPath() + << endl; + + splitCells.write(); + } + + + // PART 2: Mark edges for refinement and add points to edge centres + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Allocating edge midpoints." + << endl; + } + + // First mark faces and edges on special patches (empty or wedge). This data + // is used in PART 2 (collecting edges) and also PART 3 (collecting faces) + boolList faceOnPatchToCut(mesh_.nFaces(), false); + boolList edgeOnPatchToCut(mesh_.nEdges(), false); + + // Get boundary + const polyBoundaryMesh& boundaryMesh = mesh_.boundaryMesh(); + + // Get face-edge addressing (for each face, a list of edges) + const labelListList& meshFaceEdges = mesh_.faceEdges(); + + // Loop through all patches + forAll (boundaryMesh, patchI) + { + // Get current patch + const polyPatch& curPatch = boundaryMesh[patchI]; + + // Check whether this patch is special (empty or wedge) + if (isA<emptyPolyPatch>(curPatch) || isA<wedgePolyPatch>(curPatch)) + { + // Get start and end face labels + const label startFaceI = curPatch.start(); + const label endFaceI = startFaceI + curPatch.size(); + + // Mark all the faces and edges on the patch + for (label faceI = startFaceI; faceI < endFaceI; ++faceI) + { + // Mark face + faceOnPatchToCut[faceI] = true; + + // Get edges of this face + const labelList& curEdges = meshFaceEdges[faceI]; + + // Mark all edges + forAll (curEdges, i) + { + edgeOnPatchToCut[curEdges[i]] = true; + } + } + } + } + + // Now that we have marked faces and edges on special patches (empty or + // wedge), let's collect refined edges. Refined edges are defined by having + // both their point levels <= cell level, i.e. if any cell that gets split + // uses this edge and the edge is on special patch (empty or wedge), the + // edge needs to be split + + // Get necessary mesh data + const labelListList& meshCellEdges = mesh_.cellEdges(); + const edgeList& meshEdges = mesh_.edges(); + + // Mid points for refined edge: + // No need to split edge = -1 + // Label of introduced mid point > -1 + labelList edgeMidPoint(mesh_.nEdges(), -1); + + // Note: Loop over refined cells + forAll (cellsToRefine_, i) + { + // Get cell index + const label& cellI = cellsToRefine_[i]; + + // Get edges of this cell + const labelList& cEdges = meshCellEdges[cellI]; + + forAll (cEdges, j) + { + // Get edge index and edge + const label& edgeI = cEdges[j]; + const edge& e = meshEdges[edgeI]; + + if (edgeOnPatchToCut[edgeI]) + { + // Edge is on special patch (empty or wedge), check whether it + // needs to be split + if + ( + pointLevel_[e[0]] <= cellLevel_[cellI] + && pointLevel_[e[1]] <= cellLevel_[cellI] + ) + { + // Point levels of both edge points are <= cell level, mark + // edge for splitting + edgeMidPoint[edgeI] = 12345; + } + } + // Else nothing to do: can't split edges that are not on special + // patch (empty or wedge) + } // End for all edges of the refined cell + } // End for all cells + + // Synchronize edgeMidPoint across coupled patches. Note: use max so that + // any split takes precedence. + syncTools::syncEdgeList + ( + mesh_, + edgeMidPoint, + maxEqOp<label>(), + labelMin + ); + + // Now that the refinement trigger is synced, introduce edge points + + // Get necessary mesh data + const pointField& meshPoints = mesh_.points(); + + // Memory management + { + // Phase 1: calculate midpoints and sync. This is necessary if we don't + // use binary format for writing and we slowly get differences. + + // Allocate storage for edge points + pointField edgeMids(mesh_.nEdges(), point(-GREAT, -GREAT, -GREAT)); + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // Edge marked to be split. Get edge centre + edgeMids[edgeI] = meshEdges[edgeI].centre(meshPoints); + } + } + + // Sync across processor boundaries + syncTools::syncEdgeList + ( + mesh_, + edgeMids, + maxEqOp<vector>(), + point(-GREAT, -GREAT, -GREAT) + ); + + // Phase 2: introduce points at the synced locations. + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + edgeMidPoint[edgeI] = ref.setAction + ( + polyAddPoint + ( + edgeMids[edgeI], // Point + -1, // Appended point, no master ID + -1, // Zone for point + true // Supports a cell + ) + ); + } + } + } // End memory management for syncing/adding edge points + + // Write out edge mid points for split edges for debugging + if (debug) + { + OFstream str(mesh_.time().path()/"edgeMidPoint.obj"); + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // Get edge and write its cell centre + const edge& e = meshEdges[edgeI]; + meshTools::writeOBJ(str, e.centre(meshPoints)); + } + } + + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Writing centres of edges to split to file " << str.name() + << endl; + } + + + // PART 3: Collect faces for refinement. Faces need to be collected in two + // distinct categories: + // 1. Faces found on special patches (empty or wedge) that will be split + // into n faces (where n is the number of edges per face), + // 2. Faces not on special patches (empty or wedge) that will be always + // split into two faces. For each of these faces, collect the two edges + // found on opposing sides of the special patch (empty or wedge). + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction" << nl + << "Allocating face midpoints and collecting faces that are" + << " not on special patch (empty or wedge)." + << endl; + } + + // Get face anchor level based on the face type. For split face found on + // special patch (empty or wedge), it is guaranteeed that we will have at + // least 3 points with level <= anchor level. For split face not on special + // (empty or wedge patch), it is guaranteed that we will have at least 4 + // points with level <= anchor level. These are the corner points. + labelList faceAnchorLevel(mesh_.nFaces()); + for (label faceI = 0; faceI < mesh_.nFaces(); ++faceI) + { + if (faceOnPatchToCut[faceI]) + { + // Face on special patch, at least 3 points need to have + // level <= anchor level + faceAnchorLevel[faceI] = getAnchorLevel(faceI, 3); + } + else + { + // Face not on special patch, at least 4 points need to have + // level <= anchor level + faceAnchorLevel[faceI] = getAnchorLevel(faceI, 4); + } + } + + // Split faces on special patches (empty or wedge) will be collected in + // faceMidPoint list: + // Not refined = -1 + // Shall be refined > -1 (label of added mid point) + labelList faceMidPoint(mesh_.nFaces(), -1); + + // Split faces not on special patches (empty or wedge) will be collected + // into splitFacesIntoTwo dynamic list. For each of these faces, we also + // need to collect its two edges that are found on special patch (empty or + // wedge) + + // Allocate enough storage to prevent excessive resizing + const label nSplitFacesIntoTwo = 3*cellsToRefine_.size(); + DynamicList<label> splitFacesIntoTwo(nSplitFacesIntoTwo); + DynamicList<Pair<label> > splitFacesEmptyEdges(nSplitFacesIntoTwo); + + // Get necessary mesh data + const labelList& meshFaceOwner = mesh_.faceOwner(); + const labelList& meshFaceNeighbour = mesh_.faceNeighbour(); + + const label nFaces = mesh_.nFaces(); + const label nInternalFaces = mesh_.nInternalFaces(); + + // Internal faces: look at cells on both sides. Uniquely determined since + // the face itself is guaranteed to be same level as most refined neighbour + for (label faceI = 0; faceI < nInternalFaces; ++faceI) + { + // Get owner data + const label& own = meshFaceOwner[faceI]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = ownLevel + (refineCellsMask[own] ? 1 : 0); + + // Get neighbour data + const label& nei = meshFaceNeighbour[faceI]; + const label& neiLevel = cellLevel_[nei]; + const label newNeiLevel = neiLevel + (refineCellsMask[nei] ? 1 : 0); + + if + ( + newOwnLevel > faceAnchorLevel[faceI] + || newNeiLevel > faceAnchorLevel[faceI] + ) + { + // Note: this is internal face so we don't need to check whether the + // face is on special patch (empty or wedge). It can't be by + // definition + + // Does two things: + // 1. Appends the face to splitFacesIntoTwo list + // 2. Append the two edges on special patch (empty or wedge) to + // splitFaceEmptyEdges list + appendFaceSplitInfo + ( + faceI, + edgeOnPatchToCut, + edgeMidPoint, + splitFacesIntoTwo, + splitFacesEmptyEdges + ); + } // End whether the face needs to be considered (split) + } // End loop over all internal faces + + // Coupled patches handled like internal faces except now all information + // from neighbour comes from across processor. + // Boundary faces are more complicated since the boundary face can + // be more refined than its owner (or neighbour for coupled patches) + // (does not happen if refining/unrefining only, but does e.g. when + // refinining and subsetting) + + // Memory management + { + // Create list for swapping boundary data + labelList newNeiLevel(nFaces - nInternalFaces); + + forAll(newNeiLevel, i) + { + const label& own = meshFaceOwner[i + nInternalFaces]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = ownLevel + (refineCellsMask[own] ? 1 : 0); + + newNeiLevel[i] = newOwnLevel; + } + + // Swap the list which now contains data from the other side + syncTools::swapBoundaryFaceList(mesh_, newNeiLevel); + + forAll(newNeiLevel, i) + { + // Get face index + const label faceI = i + nInternalFaces; + + // Get owner data (neighbour is available from before) + const label& own = meshFaceOwner[faceI]; + const label& ownLevel = cellLevel_[own]; + const label newOwnLevel = ownLevel + (refineCellsMask[own] ? 1 : 0); + + if + ( + newOwnLevel > faceAnchorLevel[faceI] + || newNeiLevel[i] > faceAnchorLevel[faceI] + ) + { + if (faceOnPatchToCut[faceI]) + { + // This face is on the special patch (empty or wedge) and + // will be split into n faces (n is the number of edges for + // this face) and the face mid point will be added. Mark the + // face for splitting + faceMidPoint[faceI] = 12345; + } + else + { + // Does two things: + // 1. Appends the face to splitFacesIntoTwo list + // 2. Append the two edges on special patch (empty or wedge) + // to splitFaceEmptyEdges list + appendFaceSplitInfo + ( + faceI, + edgeOnPatchToCut, + edgeMidPoint, + splitFacesIntoTwo, + splitFacesEmptyEdges + ); + } // End if the face is not on special patch (empty or wedge) + } // End whether the face needs to be considered + } // End loop over all boundary faces + } // End memory management for syncing owner/neighbour face levels + + + // Add face points. Note: no need to sync face mid points (as we did for + // edge mid points) since processor faces do not introduce new points, only + // faces on special patch (empty or wedge) do + + // Get face centres + const vectorField& meshFaceCentres = mesh_.faceCentres(); + + // Loop through faces on special patches (empty or wedge) only + forAll (boundaryMesh, patchI) + { + // Get current patch + const polyPatch& curPatch = boundaryMesh[patchI]; + + // Check whether this patch is special (empty or wedge) + if (isA<emptyPolyPatch>(curPatch) || isA<wedgePolyPatch>(curPatch)) + { + // Get start and face labels + const label startFaceI = curPatch.start(); + const label endFaceI = startFaceI + curPatch.size(); + + // Loop through all special patch faces (global indexing) + for (label faceI = startFaceI; faceI < endFaceI; ++faceI) + { + if (faceMidPoint[faceI] > -1) + { + // Face on special patch (empty or wedge) marked to be + // split. Add the point at face centre and replace + // faceMidPoint with new point label + + faceMidPoint[faceI] = ref.setAction + ( + polyAddPoint + ( + meshFaceCentres[faceI], // Point + -1, // No master ID + -1, // Zone for point + true // Supports a cell + ) + ); + } // End if face marked for splitting + } // End loop over all faces on special patch (empty or wedge) + } // End if special patch check + } // End loop for all patches + + // Write out all split faces as a face set for debugging + if (debug) + { + // Create faceSet containing all faces that need to be split into n + // faces (n is the number of edges on the face) + faceSet splitNFaces(mesh_, "splitNFaces", 3*cellsToRefine_.size()); + + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + splitNFaces.insert(faceI); + } + } + + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Writing " << splitNFaces.size() + << " faces to split in N to faceSet " << splitNFaces.objectPath() + << endl; + + splitNFaces.write(); + + // Create faceSet containing all faces that need to be split into 2 + // faces (faces not on special patch: empty or wedge) + faceSet splitTwoFaces(mesh_, "splitTwoFaces", 3*cellsToRefine_.size()); + + forAll (splitFacesIntoTwo, i) + { + // Insert face index into splitTwoFaces + splitTwoFaces.insert(splitFacesIntoTwo[i]); + } + + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Writing " << splitTwoFaces.size() + << " faces to split in two to faceSet " + << splitTwoFaces.objectPath() << endl; + + splitTwoFaces.write(); + } + + + // Now we have all the information we need to perform the refinement and we + // no longer need to refer to cellsToRefine_. The information is in: + // - refineCellsMask = true : cell needs to be split + // - edgeMidPoint >= 0 : edge on special patch that needs to be split + // - faceMidPoint >= 0 : face on special patch that needs to be split + // into n faces (where n is the number of edges) + // - splitFacesIntoTwo : list of faces that need to be split into two + // (face not on special patch) + // - splitFacesEmptyEdges : holds the two edges of the face which needs to + // be split into two + + + // PART 4: Get corner and anchor points for all cells + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Finding cell anchorPoints" << endl; + } + + // Get anchor points for each cell: points that have the same or lower + // refinement level as the cell + List<DynamicList<label>> cellAnchorPointsDynamic(mesh_.nCells()); + + // Loop through all cells + forAll(refineCellsMask, cellI) + { + if (refineCellsMask[cellI]) + { + // The cell will be refined, set capacity to 8 to prevent excessive + // resizing + cellAnchorPointsDynamic[cellI].setCapacity(8); + } + } + + // Get necessary mesh data + const labelListList& meshPointCells = mesh_.pointCells(); + + // Loop through all points + forAll(pointLevel_, pointI) + { + // Get point cells + const labelList& pCells = meshPointCells[pointI]; + + // Loop through all cells sharing this point + forAll(pCells, pCellI) + { + // Get current cell index + const label& cellI = pCells[pCellI]; + + if + ( + refineCellsMask[cellI] + && pointLevel_[pointI] <= cellLevel_[cellI] + ) + { + // This point cell is marked for refinement and its point level + // is smaller or equal to cell level, append the point + cellAnchorPointsDynamic[cellI].append(pointI); + } + } + } + + // Loop through all cells and check whether at least 6 anchor points + // have been found (minimum requirement for a triangular prism) + + // Collect cellAnchorPoint into a List<labelList> instead of + // List<dynamicList> + labelListList cellAnchorPoints(mesh_.nCells()); + + // Get cell points for error output + const labelListList& meshCellPoints = mesh_.cellPoints(); + + forAll(refineCellsMask, cellI) + { + // First some sanity checks + if (refineCellsMask[cellI]) + { + // Cell selected for refinement + if (cellAnchorPointsDynamic[cellI].size() < 6) + { + // Cell has less than 6 anchor points. Issue an error and report + // cell points + const labelList& cPoints = meshCellPoints[cellI]; + + FatalErrorInFunction + << "Cell " << cellI + << " of level " << cellLevel_[cellI] + << " does not seem to have enough points of " + << " lower level" << endl + << "cellPoints:" << cPoints << endl + << "pointLevels:" + << IndirectList<label>(pointLevel_, cPoints)() << endl + << abort(FatalError); + } + else if (cellAnchorPointsDynamic[cellI].size() % 2 != 0) + { + // Cell has odd number of anchor points. This is not allowed and + // indicates an invalid mesh + const labelList& cPoints = meshCellPoints[cellI]; + + FatalErrorInFunction + << "Cell " << cellI + << " of level " << cellLevel_[cellI] + << " has odd number of anchor points" + << " (should be even for 2D mesh). " + << "cellPoints:" << cPoints << endl + << "pointLevels:" + << IndirectList<label>(pointLevel_, cPoints)() << endl + << abort(FatalError); + } + } + + // Tranfer the dynamic list for each cell into an ordinary list + cellAnchorPoints[cellI].transfer(cellAnchorPointsDynamic[cellI]); + } + + + // PART 5: Add the cells + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << " Adding cells." + << endl; + } + + // We should have exactly n new cells per each split cell, where n is the + // number of anchor points in a cell divided by two. In order to determine + // owner/neighbours of new and modified faces, we need to know which cell + // came from which point. The mapping is not uniquely defined as in + // polyhedralRefinement when we had 1 point = 1 cell. Here, we have two + // points that correspond to a single cell, one on one side of the special + // patch and the other on other side. This information will be collected in + // a HashTable<label, Pair<label> >, where the key will be a pair of + // global point index and global cell index, while the value is local index + // into cellAddedCells list + labelListList cellAddedCells(mesh_.nCells()); + HashTable<label, Pair<label>, Hash<FixedList<label, 2> > > + pointCellToAddedCellMap(6*cellsToRefine_.size()); + + // Get mesh data + const cellZoneMesh& cellZones = mesh_.cellZones(); + const cellList& meshCells = mesh_.cells(); + const faceList& meshFaces = mesh_.faces(); + const labelListList& meshPointEdges = mesh_.pointEdges(); + + + // Loop through all faces + forAll(faceMidPoint, faceI) + { + // Check whether this face needs to be split into n faces + if (faceMidPoint[faceI] > -1) + { + // This is a face that will be split and is on special patch (empty + // or wedge) by definition, get the cell index by looking at owner + // only + const label& cellI = meshFaceOwner[faceI]; + + // Get cell added cells + labelList& cAdded = cellAddedCells[cellI]; + + // Check whether the cell added cells are empty. This means that we + // haven't visited the first face yet. If it is not empty, we have + // already visited one face, which is enough + if (cAdded.empty()) + { + // First face that hasn't been visited. Start adding cells + // point-by-point and keep track of mapping necessary for + // splitting other (not on special patch: empty or wedge) faces + // into two + + // Set the total number of added cells to number of anchors + // divided by two. Note: number of anchors needs to be an even + // number (6 for triangular prism, 8 for hex, 10 for pentagonal + // prism, etc.) + const labelList& cAnchors = cellAnchorPoints[cellI]; + cAdded.setSize(cAnchors.size()/2); + + // Helper variable to distinguish between first and successive + // cells (first will have the original index) + label cellCounter = 0; + + // Get current face + const face& f = meshFaces[faceI]; + + // Loop through face points + forAll (f, fpI) + { + // Get point index + const label& pointI = f[fpI]; + + // Find anchor point in local list if present + const label anchorI = cAnchors.find(pointI); + + if (anchorI != -1) + { + // This point is anchor, add the cell + + if (cellCounter == 0) + { + // This is first cell, simply set the existing index + cAdded[cellCounter] = cellI; + + // Update refinement level indicator field to 1 + // since the original cell will be refined + refinementLevelIndicator_[cellI] = REFINED; + } + else + { + // Other cells, need to add the cells + cAdded[cellCounter] = ref.setAction + ( + polyAddCell + ( + -1, // M. point + -1, // M. edge + -1, // M. face + cellI, // M. cell + cellZones.whichZone(cellI) // M. zone + ) + ); + } + + // Collect the point-cell mapping into local index + // of cell added cells for point on this side + pointCellToAddedCellMap.insert + ( + Pair<label>(pointI, cellI), + cellCounter + ); + + // This is only one side, we need to also collect + // the other point on the other side. Get point edges + // for this point + const labelList& pEdges = + meshPointEdges[pointI]; + + // Loop through point edges + forAll (pEdges, peI) + { + // Get the edge index + const label& edgeI = pEdges[peI]; + + if (!edgeOnPatchToCut[edgeI]) + { + // Edge is not on special patch (empty or + // wedge), therefore this is the edge we're + // looking for. Collect the other point of the + // edge + const label pointJ = + meshEdges[edgeI].otherVertex(pointI); + + // Collect the point-cell mapping into local + // index of cell added cells for this point + pointCellToAddedCellMap.insert + ( + Pair<label>(pointJ, cellI), + cellCounter + ); + } + } + + // Now we have finished adding the cell and also + // adding the necessary mapping for this added cell + + // Increment cell counter + ++cellCounter; + + } // Else point is not anchor: nothing to do + } // End loop over all face points + + // Sanity check: number of counted cells must be + // equal to size of cellAddedCells. This means that + // we have correctly marked the anchor points + if (cellCounter != cAdded.size()) + { + FatalErrorInFunction + << "Problem while adding cells." + << nl + << "Going through base face on special patch" + << " (empty or wedge) and adding cells, we collected: " + << cellCounter << " cells." + << nl + << "While the number of anchor points is: " + << cAnchors.size() + << nl + << "The number of added cells based on number of anchor" + << " points is: " + << cAdded.size() + << nl + << "Additional information: " + << nl + << "cellI: " << cellI << ", faceI: " << faceI + << abort(FatalError); + } + + } // End if cell added cells empty + } // End if face needs to be split into n + } // End loop over all faces + + + // PART 6: Adding faces + + // 6.1. Existing faces on special patches (empty or wedge) that get split + // (into n faces where n is the number of points or edges) + // 6.2. Existing faces not on special patches that get split into two + // 6.3. Existing faces that do not get split but only edges get split + // 6.4. Existing faces that do not get split but get new owner/neighbour + // 6.5. New internal faces inside split cells + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << " Marking faces to be handled" + << endl; + } + + // Get all faces to split: + // a) All faces of a cell being split + // b) All faces on special patch that are being split + // c) All faces not on special patch that are being split + // d) Both faces of an edge that is being split + // Note: although a bit redundant, loop over everything above + boolList facesToSplit(mesh_.nFaces(), false); + + // Also collect all split faces which will be needed in 6.3 + boolList allSplitFaces(mesh_.nFaces(), false); + + // Get edge faces + const labelListList& meshEdgeFaces = mesh_.edgeFaces(); + + // a) All faces of a cell that is being split + forAll(refineCellsMask, cellI) + { + if (refineCellsMask[cellI]) + { + const cell& cFaces = meshCells[cellI]; + + forAll(cFaces, i) + { + facesToSplit[cFaces[i]] = true; + } + } + } + + // b) All faces on special patch that are being split + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + // Mark face in both lists + facesToSplit[faceI] = true; // Used through 6.1-6.5 + allSplitFaces[faceI] = true; // Used in 6.3 + } + } + + // c) All faces not on special patch that are being split + forAll(splitFacesIntoTwo, i) + { + // Get face index + const label& faceI = splitFacesIntoTwo[i]; + + // Mark face in both lists + facesToSplit[faceI] = true; + allSplitFaces[faceI] = true; + } + + // d) Both faces of an edge that is being split + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + const labelList& eFaces = meshEdgeFaces[edgeI]; + + forAll(eFaces, i) + { + facesToSplit[eFaces[i]] = true; + } + } + } + + // Note: after splitting a certain face during parts 6.1. to 6.4., + // facesToSplit for that face will be set back to 0, i.e. marked as finished + + + // PART 6.1. Add/modify faces for each face on special patch that is being + // split + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << endl + << "Splitting faces on special patches (empty or wedge)..." << endl; + } + + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1 && facesToSplit[faceI]) + { + // Face has not been split. + // Note: although facesToSplit can't be different than 1 here and + // the second check can be ommitted, it is left for clarity + + // Get the face + const face& f = meshFaces[faceI]; + + // Flag to control whether the original faceI has been used + // Note: original face gets modified, other n - 1 faces are added, + // where n is the number of points/edges of a face + bool modifiedFace = false; + + // Get anchor level for the face + const label& anchorLevel = faceAnchorLevel[faceI]; + + // New face always has four points/edges for arbitrary polygon + face newFace(4); + + // Loop through all points of original face + forAll(f, fpI) + { + const label& pointI = f[fpI]; + + if (pointLevel_[pointI] <= anchorLevel) + { + // This point is anchor, start collecting face + + // Create dynamic list (because of append) for face vertices + // and append the first (anchor) point + DynamicList<label> faceVerts(4); + faceVerts.append(pointI); + + // Walk forward to mid point. + // - if next is +2 midpoint is +1 + // - if next is +1 it is midpoint + // - if next is +0 there has to be edgeMidPoint + + // Appends all points from this point to face mid point + walkFaceToMid + ( + edgeMidPoint, + anchorLevel, + faceI, + fpI, + faceVerts + ); + + // Append face mid point + faceVerts.append(faceMidPoint[faceI]); + + // Append all points from face mid point to starting point + walkFaceFromMid + ( + edgeMidPoint, + anchorLevel, + faceI, + fpI, + faceVerts + ); + + // Transfer dynamic list to a face (ordinary list) + newFace.transfer(faceVerts); + faceVerts.clear(); + + // Set new owner/neighbour indices based on split cells + label own, nei; + setNewFaceNeighbours + ( + pointCellToAddedCellMap, + cellAddedCells, + faceI, + pointI, // Anchor point index + + own, + nei + ); + + if (debug) + { + // Check orientation of the split face for debugging + checkNewFaceOrientation(ref, faceI, newFace); + } + + + // Finally insert the modification/addition instruction into + // the topo changer engine + if (!modifiedFace) + { + // Modify first face + modifiedFace = true; + modifyFace(ref, faceI, newFace, own, nei); + } + else + { + // Add additional faces + addFace(ref, faceI, newFace, own, nei); + } + } // End point anchor check + } // End for all points + + // Mark face as handled + facesToSplit[faceI] = false; + + } // End if this face needs to be split + } // End for all faces + + + // PART 6.2. Add/modify faces for each face not on special patch that is + // being split into two + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Splitting faces not on special patches (empty or wedge)..." + << endl; + } + + // Loop through faces that are not on special patch. These will be split + // into two faces only + forAll (splitFacesIntoTwo, i) + { + // Get face index + const label& faceI = splitFacesIntoTwo[i]; + + // Check whether the face is marked for splitting. A bit redundant but + // will be left for clarity + if (facesToSplit[faceI]) + { + // Face has not been split, grab the face + const face& f = meshFaces[faceI]; + + // Additional sanity check + if (f.size() != 4) + { + FatalErrorInFunction + << "The original face has: " << f.size() << " points," + << " while it should have exactly 4 points in order" + << " to split it in two." + << " faceI: " << faceI + << abort(FatalError); + } + + // Flag to control whether the original faceI has been used + // Note: original face gets modified, other face gets added + bool modifiedFace = false; + + // Get anchor level for the face + const label& anchorLevel = faceAnchorLevel[faceI]; + + // Mark visited points to avoid adding the face twice + boolList visitedPoint(f.size(), false); + + // New face always has four points/edges for 2D face splitting of + // a face that is not on special patch + face newFace(4); + + // Loop through all points of original face + forAll (f, fpI) + { + // Get point index + const label& pointI = f[fpI]; + + if + ( + !visitedPoint[fpI] + && pointLevel_[pointI] <= anchorLevel + ) + { + // This point is anchor and it hasn't been visited yet, + // start collecting face + + // Collect the new face in the following order: + // 1. This point + // 2. Edge mid points for the edge that contains this point + // and the edge that is on the other side + // 3. Other point (on the other side) + + // 1. Set this point and mark it as visited + newFace[0] = pointI; + visitedPoint[fpI] = true; + + // 2. Get edge mid point for edge containing this point + // Fetch the two edges on both sides + const Pair<label>& edgesOnOppositeSides = + splitFacesEmptyEdges[i]; + + // Get the edge indices and edges + const label& edgeIndexI = edgesOnOppositeSides.first(); + const label& edgeIndexJ = edgesOnOppositeSides.second(); + + const edge& edgeI = meshEdges[edgeIndexI]; + const edge& edgeJ = meshEdges[edgeIndexJ]; + + // Additional sanity check + if + ( + (edgeMidPoint[edgeIndexI] == -1) + || (edgeMidPoint[edgeIndexJ] == -1) + ) + { + // Edges are not marked for refinement, issue an error + FatalErrorInFunction + << "Trying to split a face into two, but" + << " edges on special patches (empty or wedge)" + << " are not properly set." + << nl + << "Edge: " << edgeIndexI << " with new point: " + << edgeMidPoint[edgeIndexI] + << nl + << "Edge: " << edgeIndexJ << " with new point: " + << edgeMidPoint[edgeIndexJ] + << abort(FatalError); + } + + if ((edgeI.start() == pointI) || (edgeI.end() == pointI)) + { + // Current point is on edgeI, set edgeI midpoint and + // then edgeJ midpoint + newFace[1] = edgeMidPoint[edgeIndexI]; + newFace[2] = edgeMidPoint[edgeIndexJ]; + } + else if + ( + (edgeJ.start() == pointI) || (edgeJ.end() == pointI) + ) + { + // Current is on edgeJ, set edgeJ midpoint and then + // edgeI midpoint + newFace[1] = edgeMidPoint[edgeIndexJ]; + newFace[2] = edgeMidPoint[edgeIndexI]; + } + else + { + // Point not on either of edges, issue an error + FatalErrorInFunction + << "Trying to split a face into two, but" + << " the point: " << pointI << " can't be found" + << " on either of edges. " + << nl + << "Edge: " << edgeIndexI << " with new point: " + << edgeMidPoint[edgeIndexI] + << nl + << "Edge: " << edgeIndexJ << " with new point: " + << edgeMidPoint[edgeIndexJ] + << abort(FatalError); + } + + // At this point, we have added three points: original + // point, first edge mid point and second edge mid point. + + // 3. Add the other point + // Get point edges for this point + const labelList& pEdges = meshPointEdges[pointI]; + + // Loop through all edges + forAll (pEdges, peI) + { + // Get the edge index + const label& pointEdgeI = pEdges[peI]; + + if (!edgeOnPatchToCut[pointEdgeI]) + { + // Edge is not on special patch (empty or wedge), + // therefore this is the edge we're looking for. + // Collect the other point + const label pointJ = + meshEdges[pointEdgeI].otherVertex(pointI); + + // Insert the point into the face at the last + // location + newFace[3] = pointJ; + + // Mask local point index as visited by going + // through the face again + forAll (f, fpJ) + { + if (f[fpJ] == pointJ) + { + // Found local index of the point, mask it + visitedPoint[fpJ] = true; + } + } + } + } + + // The face is now complete, set new owner/neighbour indices + // based on split cells + label own, nei; + + // Set new face owner/neighbour pair + setNewFaceNeighbours + ( + pointCellToAddedCellMap, + cellAddedCells, + faceI, + pointI, // Anchor point index + + own, + nei + ); + + // We need to revert the face if the edge between this point + // and the next point is not split. This follows from + // definition of face as ordered set of points (defining + // orientation) and the splitting procedure. Note: edge + // ordering in face is the same as point ordering so the + // point index can be used as first face edge index + if (edgeMidPoint[meshFaceEdges[faceI][fpI]] == -1) + { + newFace = newFace.reverseFace(); + } + + if (debug) + { + // Check orientation of the split face for debugging + checkNewFaceOrientation(ref, faceI, newFace); + } + + + // Finally insert the modification/addition instruction into + // the topo changer engine + if (!modifiedFace) + { + // Modify first face + modifiedFace = true; + modifyFace(ref, faceI, newFace, own, nei); + } + else + { + // Add additional faces + addFace(ref, faceI, newFace, own, nei); + } + } // End if point is anchored and has not been visited + } // End loop over all face points + + // Mark face as handled + facesToSplit[faceI] = false; + + } // End if face is split + } // End for all faces that should be split into two + + + // PART 6.3. Modify faces that do not get split but have edges that are + // being split + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << "Modifying faces with split edges..." + << endl; + } + + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + // This is an edge that is going to be split, get edge faces + const labelList& eFaces = meshEdgeFaces[edgeI]; + + // Loop through all faces of an edge + forAll(eFaces, i) + { + // Get face index + const label faceI = eFaces[i]; + + // Check whether this is not a face that's been split and that + // the face has not been handled yet. The second check is + // necessary since we go through edge faces instead of just + // faces + if (!allSplitFaces[faceI] && facesToSplit[faceI]) + { + // This is unsplit face that has not been handled + + // Get face and face edges + const face& f = meshFaces[faceI]; + const labelList& fEdges = meshFaceEdges[faceI]; + + // Create a dynamic list containing new face vertices + DynamicList<label> newFaceVerts(f.size()); + + // Append all original points and all edge mid points + forAll(f, fpI) + { + newFaceVerts.append(f[fpI]); + + const label edgeI = fEdges[fpI]; + + if (edgeMidPoint[edgeI] > -1) + { + newFaceVerts.append(edgeMidPoint[edgeI]); + } + } + + // Create a face from dynamic list by transfer + face newFace(newFaceVerts); + + + // The point with the lowest level should be an anchor + // point of the neighbouring cells. + const label anchorFpI = findMinLevel(f); + + label own, nei; + setNewFaceNeighbours + ( + pointCellToAddedCellMap, + cellAddedCells, + faceI, + f[anchorFpI], // Anchor point index + + own, + nei + ); + + + if (debug) + { + // Check orientation of the new face for debugging + checkNewFaceOrientation(ref, faceI, newFace); + } + + // Modify the face + modifyFace(ref, faceI, newFace, own, nei); + + // Mark face as handled + facesToSplit[faceI] = false; + + } // End if unsplit, unhandled face + } // End for all edge faces + } // End if edge has been cut + } // End for all edges + + + // PART 6.4: Modify faces that do not get split but whose owner/neighbour + // change due to splitting + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << " Changing owner/neighbour for otherwise unaffected faces..." + << endl; + } + + forAll(facesToSplit, faceI) + { + // All remaining unnaffected faces are the ones whose owner/neighbour + // changed + if (facesToSplit[faceI]) + { + // Get the face + const face& f = meshFaces[faceI]; + + // The point with the lowest level should be an anchor point of the + // neighbouring cells + label anchorFpI = findMinLevel(f); + + label own, nei; + setNewFaceNeighbours + ( + pointCellToAddedCellMap, + cellAddedCells, + faceI, + f[anchorFpI], // Anchor point + + own, + nei + ); + + // Modify the face, changing owner and neighbour + modifyFace(ref, faceI, f, own, nei); + + // Mark face as handled + facesToSplit[faceI] = false; + + } // End if the face needs to be handled + } // End for all faces + + + // PART 6.5. Add new internal faces inside split cells + + if (debug) + { + Pout<< "prismatic2DRefinement::setRefinementInstruction(...)" << nl + << " Adding new internal faces for split cells..." + << endl; + } + + // Mark-up filed for visited cells (since we are going through faces) + boolList cellsToSplit(mesh_.nCells(), true); + + // Loop through faces in the same way as we did when we were adding + // cells. This order is important since it ensures easy determination of + // owner/neighbour cells for new faces + forAll(faceMidPoint, faceI) + { + // Get owner of the face. For face on special patch (empty or wedge) + const label& cellI = meshFaceOwner[faceI]; + + // Check whether this face has been split and whether the cell has been + // handled (internal faces already created for this cell) + if (faceMidPoint[faceI] > -1 && cellsToSplit[cellI]) + { + // Face is split and hasn't been visited yet. Get the face and edges + const face& f = meshFaces[faceI]; + const labelList& fEdges = meshFaceEdges[faceI]; + + // Get anchor points for this cell and cell added cells + const labelList& cAnchors = cellAnchorPoints[cellI]; + const labelList& cAdded = cellAddedCells[cellI]; + + // Count number of added faces (helper variable to determine + // owner/neighbour) + label nAddedFaces = 0; + + // Loop through face points + forAll (f, fpI) + { + // Get point index + const label& pointI = f[fpI]; + + // If this point is not an anchor, it has already been handled + // (by going through anchors), skip + if (cAnchors.find(pointI) == -1) + { + continue; + } + + // Get corresponding edge index and edge (between fpI and + // fpI + 1) by definition of faceEdges + const label& edgeI = fEdges[fpI]; + const edge& e = meshEdges[edgeI]; + + // Grab other point + const label& pointJ = e.otherVertex(pointI); + + if (pointJ == -1) + { + // If pointJ is equal to -1, this means that the pointI + // was not found on edge, something went wrong + FatalErrorInFunction + << "Point: " << pointI << " not found on edge: " + << edgeI << nl + << "Looping through face points and face edges did" + << " not ensure synchronous behaviour." + << abort(FatalError); + } + + // Create the new face + face newFace(4); + + // Note: there are three possible variants: + // i) Edge is split and the other point is anchor. Collection + // of the face starts from edgeMidPoint + // ii) Edge is split and the other point is not an + // anchor. Collection of the face starts from other point + // iii) Edge is not split and the other point is not an + // anchor. Collection of the face starts from other point + // Variants ii) and iii) can be handled together, while variant + // i) has to be handled separately. + + // Whether the edge is split + const bool isEdgeSplit = edgeMidPoint[edgeI] > -1; + + // Whether the other point is anchor or not + const bool isOtherEdgePointAnchor + = cAnchors.find(pointJ) > -1; + + // Check if the edge is split and whether the other edge point + // is an anchor + if (isEdgeSplit && isOtherEdgePointAnchor) + { + // Variant i) Edge is split and other edge point is anchor + + // Create the new face and start collecting points + // a) edgeMidPoint for this edge + // b) faceMidPoint for this face + // c) faceMidPoint for the face on the other side + // d) edgeMidPoint for the edge on the other side + + // a) edgeMidPoint for this edge + newFace[0] = edgeMidPoint[edgeI]; + + // b) and c): adding both face mids + addFaceMids + ( + faceMidPoint, + faceOnPatchToCut, + faceI, + cellI, + newFace + ); + + // d) edgeMidPoint for the edge on the other side + // The other edge is uniquely defined as the edge on special + // patch (empty or wedge) sharing the same face as this edge + + // Get the edge faces + const labelList& eFaces = meshEdgeFaces[edgeI]; + + // Loop through edge faces + forAll(eFaces, i) + { + // Get the face and check whether it is on special patch + const label& faceK = eFaces[i]; + + if (!faceOnPatchToCut[faceK]) + { + // Found the face, need to search its edges + const labelList& otherFaceEdges = + meshFaceEdges[faceK]; + + forAll(otherFaceEdges, j) + { + // Get the edge + const label& edgeJ = otherFaceEdges[j]; + + if (edgeOnPatchToCut[edgeJ] && (edgeI != edgeJ)) + { + // Edge is on special patch (empty or + // wedge), this must be the one we are + // looking for. Add its midpoint and double + // check if it is valid + if (edgeMidPoint[edgeJ] > -1) + { + newFace[3] = edgeMidPoint[edgeJ]; + break; + } + else + { + FatalErrorInFunction + << "Other edge: " + << edgeJ + << " has not been selected for" + << " splitting, while the edge on" + << " original side: " + << edgeI + << " has been selected." + << abort(FatalError); + } + } // End if this is our "other" edge + } // End for all other (non special patch: empty or + // wedge) face edges + + // Break out since we must have found the candidate + break; + + } // End if face not on special patch (empty or wedge) + + } // End for all edge faces + + } // End if this edge is split and the other point is anchor + else if (!isOtherEdgePointAnchor) + { + // Variants ii) and iii). Either the edge is split and the + // other point is not an anchor or the edge is not split and + // the other point is not an anchor + + // Create the new face and start collecting points + // a) other point of this edge + // b) faceMidPoint for this face + // c) faceMidPoint for the face on the other side + // d) other point on the other side + + // a) other point of this edge + newFace[0] = pointJ; + + // b) and c): adding both face mids + addFaceMids + ( + faceMidPoint, + faceOnPatchToCut, + faceI, + cellI, + newFace + ); + + // d) other point on the other side + // The other point is uniquely defined as the other point of + // the edge of this point which is not on special patch + + // Get point edges + const labelList& pEdges = meshPointEdges[pointJ]; + + // Loop through all edges + forAll(pEdges, i) + { + // Get the edge index + const label& edgeJ = pEdges[i]; + + if (!edgeOnPatchToCut[edgeJ]) + { + // Found our edge, set the point on the other side + // of the edge as the last point in face + newFace[3] = meshEdges[edgeJ].otherVertex(pointJ); + break; + } + } // End loop over all point edges + + } // End if the other point is not an anchor + else + { + // The edge is not split and the other point is an + // anchor. This should never happen + FatalErrorInFunction + << "Attempted to create internal face for an edge that" + << " is not split and the other point that is an anchor." + << nl + << "Cell: " << cellI + << ", point: " << pointI + << ", other edge point: " << pointJ + << nl + << "Anchor points for cell are: " << cAnchors + << abort(FatalError); + } + + // Now we have the face defined, set owner and neighbour. + // Note: owner and neighbour are uniquely defined since we have + // gone through the face in the same way as we did while adding + // cells. This ensured easy definition of owner/neighbour cells + const label own = cAdded[nAddedFaces]; + const label nei = + nAddedFaces < cAdded.size() - 1 + ? cAdded[nAddedFaces + 1] + : cAdded[0]; + + // According to the definition of adding faces, the first n - 1 + // faces need to be reverted, while the last one is correctly + // oriented + if (nAddedFaces < cAdded.size() - 1) + { + newFace = newFace.reverseFace(); + } + + + // Debug: check orientation + if (debug) + { + // Get owner/neighbour points + point ownPt, neiPt; + + if (nAddedFaces < cAdded.size() - 1) + { + // Original owner/neighbour + ownPt = meshPoints[pointI]; + neiPt = meshPoints[pointJ]; + } + else + { + // Flipped owner/neighbour for last face + ownPt = meshPoints[pointJ]; + neiPt = meshPoints[pointI]; + } + + checkInternalOrientation + ( + ref, + cellI, + faceI, + ownPt, + neiPt, + newFace + ); + } + + // Finally, add the face. Note: ignoring return of new face + // index from ref.setAction(polyAddFace(...)) call + ref.setAction + ( + polyAddFace + ( + newFace, // face + own, // owner + nei, // neighbour + -1, // master point + -1, // master edge + 0, // master face for addition + false, // flux flip + -1, // patch for face + -1, // zone for face + false // face zone flip + ) + ); + + // Increment number of added faces + ++nAddedFaces; + + } // End loop over all point (and edges) of the face + + // Finished adding internal faces. Mark the cell as handled + cellsToSplit[cellI] = false; + + } // End if face is split into n and cell has not been handled + } // End for all faces + + // Debug: check minimum point index of added points, needs to be equal to + // number of points in the original mesh + if (debug) + { + label minPointI = labelMax; + label maxPointI = labelMin; + + forAll(faceMidPoint, faceI) + { + if (faceMidPoint[faceI] > -1) + { + minPointI = min(minPointI, faceMidPoint[faceI]); + maxPointI = max(maxPointI, faceMidPoint[faceI]); + } + } + forAll(edgeMidPoint, edgeI) + { + if (edgeMidPoint[edgeI] > -1) + { + minPointI = min(minPointI, edgeMidPoint[edgeI]); + maxPointI = max(maxPointI, edgeMidPoint[edgeI]); + } + } + + if (minPointI != labelMax && minPointI != mesh_.nPoints()) + { + FatalErrorInFunction + << "Added point labels not consecutive to existing mesh points." + << nl + << "mesh_.nPoints():" << mesh_.nPoints() + << " minPointI: " << minPointI + << " maxPointI: " << maxPointI + << abort(FatalError); + } + } +} + + +void Foam::prismatic2DRefinement::setUnrefinementInstruction +( + batchPolyTopoChange& ref +) const +{ + // Note: assumes that splitPointsToUnrefine_ are set prior to the function + // call + + // Check whether the refinementLevelIndicator is valid + if (refinementLevelIndicator_.size() != mesh_.nCells()) + { + FatalErrorInFunction + << "Refinement level indicator list has invalid size: " + << refinementLevelIndicator_.size() + << ", number of cells: " << mesh_.nCells() + << nl + << "Make sure to call setRefinementInstruction(...) before" + << " calling setUnrefinementInstruction(...)." + << abort(FatalError); + } + + // Get point cells necessary for debug and face removal + const labelListList& meshPointCells = mesh_.pointCells(); + + if (debug) + { + Pout<< "prismatic2DRefinement::setUnrefinementInstruction" + << "(batchPolyTopoChange& ref)" + << nl + << "Checking validity of cellLevel before setting unrefinement." + << endl; + + forAll(cellLevel_, cellI) + { + if (cellLevel_[cellI] < 0) + { + FatalErrorInFunction + << "Illegal cell level " << cellLevel_[cellI] + << " for cell " << cellI + << abort(FatalError); + } + } + + // Write split points into a point set + pointSet pSet + ( + mesh_, + "splitPoints", + labelHashSet(splitPointsToUnrefine_) + ); + pSet.write(); + + // Write split point cells into a cell set + cellSet cSet + ( + mesh_, + "splitPointCells", + splitPointsToUnrefine_.size() + ); + + forAll(splitPointsToUnrefine_, i) + { + // Get point cells and insert them into cell set + const labelList& pCells = meshPointCells[splitPointsToUnrefine_[i]]; + + forAll(pCells, j) + { + cSet.insert(pCells[j]); + } + } + cSet.write(); + + Pout<< "prismatic2DRefinement::setUnrefinementInstruction" + << "(batchPolyTopoChange& ref)" + << nl + << "Writing " << pSet.size() + << " points and " + << cSet.size() << " cells for unrefinement to" << nl + << "pointSet " << pSet.objectPath() << nl + << "cellSet " << cSet.objectPath() + << endl; + } + + // Update refinementLevelIndicator for all cells that will be unrefined + forAll(splitPointsToUnrefine_, i) + { + // Get point cells and mark them for unrefinement + const labelList& pCells = meshPointCells[splitPointsToUnrefine_[i]]; + + forAll(pCells, j) + { + refinementLevelIndicator_[pCells[j]] = UNREFINED; + } + } + + // Create lists needed by face remover + labelList cellRegion; + labelList cellRegionMaster; + labelList facesToRemove; + + // Memory management + { + // Mark faces on special patches (empty or wedge) to exclude them + boolList faceOnPatchToCut(mesh_.nFaces(), false); + + // Get boundary + const polyBoundaryMesh& boundaryMesh = mesh_.boundaryMesh(); + + // Loop through all patches + forAll (boundaryMesh, patchI) + { + // Get current patch + const polyPatch& curPatch = boundaryMesh[patchI]; + + // Check whether this patch is special (empty or wedge) + if (isA<emptyPolyPatch>(curPatch) || isA<wedgePolyPatch>(curPatch)) + { + // Get start and end face labels + const label startFaceI = curPatch.start(); + const label endFaceI = startFaceI + curPatch.size(); + + // Mark all the faces and edges on the patch + for (label faceI = startFaceI; faceI < endFaceI; ++faceI) + { + // Mark face + faceOnPatchToCut[faceI] = true; + } + } + } + + + // Collect split faces in the hash set, guess size to prevent excessive + // resizing + labelHashSet splitFaces(12*splitPointsToUnrefine_.size()); + + // Get point faces + const labelListList& meshPointFaces = mesh_.pointFaces(); + + forAll(splitPointsToUnrefine_, i) + { + // Loop through all faces of this point and insert face index + const labelList& pFaces = meshPointFaces[splitPointsToUnrefine_[i]]; + + forAll(pFaces, j) + { + // Get face index + const label& faceI = pFaces[j]; + + if (!faceOnPatchToCut[faceI]) + { + // Face is not on special patch, insert it into hash set + splitFaces.insert(faceI); + } + } + } + + // Check with faceRemover what faces will get removed. Note that this + // can be more (but never less) than splitFaces provided. + faceRemover_.compatibleRemoves + ( + splitFaces.toc(), // Pierced faces + + cellRegion, // Region merged into (-1 for no region) + cellRegionMaster, // Master cell for region + facesToRemove // List of faces to be removed + ); + + if (facesToRemove.size() != splitFaces.size()) + { + FatalErrorInFunction + << "Either the initial set of split points to unrefine does not" + << " seem to be consistent or there are no mid points of" + << " refined cells." + << abort(FatalError); + } + } + + // Find point region master for every cell region. This is the central point + // from which the coarse cell will be made + // The property of the point region master is that all cells that touch it + // have the same cell region index + // HJ, 6/Sep/2019 + labelList pointRegionMaster(cellRegionMaster.size(), label(-1)); + + // Get point-cell addressing + const labelListList& pc = mesh_.pointCells(); + + forAll (splitPointsToUnrefine_, i) + { + const labelList& curPc = pc[splitPointsToUnrefine_[i]]; + + label curRegion = -1; + + forAll (curPc, curPcI) + { + if (curRegion == -1) + { + // First region found. Grab it + curRegion = cellRegion[curPc[curPcI]]; + } + else + { + // Region already found. Check that all other cells that + // touch this point have the same region + if (curRegion != cellRegion[curPc[curPcI]]) + { + // Error: different region cells touching in split point + // This is not a valid unrefinement pattern + FatalErrorInFunction + << "Different region cells touching in split point." + << abort(FatalError); + } + } + } + + // Record point region master + if (curRegion > -1) + { + pointRegionMaster[curRegion] = splitPointsToUnrefine_[i]; + } + else + { + // Error: Cannot find region for point + FatalErrorInFunction + << "Different region cells touching in split point." + << abort(FatalError); + } + } + + // Insert all commands to combine cells + faceRemover_.setRefinement + ( + facesToRemove, + cellRegion, + // pointRegionMaster, // OpenFOAM does not support master point + cellRegionMaster, + ref + ); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::prismatic2DRefinement::prismatic2DRefinement +( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme +) +: + refinement(name, dict, index, mme) +{} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::prismatic2DRefinement::~prismatic2DRefinement() +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::prismatic2DRefinement::setCellsToRefine +( + const labelList& refinementCellCandidates +) +{ + if (debug) + { + Info<< "prismatic2DRefinement::setCellsToRefine" + << "(const labelList& refinementCellCandidates)" << nl + << "Setting cells to refine" << endl; + } + + // Create a mark-up field for cells to refine + boolList refineCell(mesh_.nCells(), false); + + // Roughly count how many cells we are going to end up with + label roughCellCountAfterRefinement = mesh_.nCells(); + + // Get cell points to count number of additional cells + const labelListList& meshCellPoints = mesh_.cellPoints(); + + // Mark initial refinement candidates for refinement only if the cell level + // is smaller than the maximum refinement level. Note: stop marking them if + // we exceed the rough cell count + forAll (refinementCellCandidates, i) + { + // Get cell index + const label& cellI = refinementCellCandidates[i]; + + if (roughCellCountAfterRefinement < maxCells_) + { + // Mark cell for refinement + refineCell[cellI] = true; + + // Increment number of cells (nPoints/2 - 1 new cells per cell) + roughCellCountAfterRefinement += meshCellPoints[cellI].size()/2 - 1; + } + } + + // Extend cells across faces using a specified number of refinement buffer + // layers + for (label i = 0; i < nRefinementBufferLayers_; ++i) + { + meshTools::extendMarkedCellsAcrossFaces(mesh_, refineCell); + } + + // Remove all cells that exceed the maximum refinement level + forAll (refineCell, cellI) + { + if (refineCell[cellI] && (cellLevel_[cellI] + 1 > maxRefinementLevel_)) + { + refineCell[cellI] = false; + } + } + + // Make sure that the refinement is face consistent (2:1 consistency) and + // point consistent (4:1 consistency) if necessary + + // Counter for additional cells to refine due to consistency in each + // iteration and number of iterations + label nAddCells = 0; + label nIters = 0; + label nTotalAddCells = 0; + + do + { + // Reset counter at the beginning of each iteration + nAddCells = 0; + + if (edgeBasedConsistency_) + { + // Check for 4:1 edge based consistent refinement. Updates + // cellsToRefine and returns number of cells added in this iteration + nAddCells += edgeConsistentRefinement(refineCell); + } + + // Check for 2:1 face based consistent refinement. Updates cellsToRefine + // and returns number of cells added in this iteration + nAddCells += faceConsistentRefinement(refineCell); + + // Global reduction + reduce(nAddCells, sumOp<label>()); + + // Increment number of iterations and total number of added cells + ++nIters; + nTotalAddCells += nAddCells; + + } while (nAddCells > 0); + + Info<< "Added " << nTotalAddCells // nTotalAddCells already reduced + << " cells in " << returnReduce(nIters, maxOp<label>()) + << " iterations to obtain consistent refinement." + << endl; + + // Collect all cells to refine in a dynamic list + DynamicList<label> cellsToRefineDynamic(mesh_.nCells()); + + forAll (refineCell, cellI) + { + if (refineCell[cellI]) + { + // Cell marked for refinement, append it + cellsToRefineDynamic.append(cellI); + } + } + + // Transfer the contents into the data member (ordinary list) + cellsToRefine_.transfer(cellsToRefineDynamic); + + Info<< "Selected " << returnReduce(cellsToRefine_.size(), sumOp<label>()) + << " cells to refine." << endl; +} + + +void Foam::prismatic2DRefinement::setSplitPointsToUnrefine +( + const labelList& unrefinementPointCandidates +) +{ + if (debug) + { + Info<< "prismatic2DRefinement::setSplitPointsToUnrefine" + << "(const labelList& unrefinementPointCandidates)" << nl + << "Setting split points to unrefine." << endl; + } + + // Get necessary mesh data + const label nPoints = mesh_.nPoints(); + const labelListList& meshCellPoints = mesh_.cellPoints(); + + // PART 1: Mark all split points in the mesh (points that can be unrefined) + boolList splitPointsMarkup(nPoints, false); + + // Algorithm: split point is uniquely defined as a point that: + // 1. Has pointLevel_ > 0 (obviously), + // 2. A point that has the same pointLevel_ as ALL of the points of its + // edges. In other words, for each point, we will look through all the + // edges of the point. For each edge, we will visit both points and + // check point levels. All point levels must be the same for this point + // candidate to be a split point. This is quite useful since there is no + // need to store the refinement history + + // Get necessary mesh data + const edgeList& meshEdges = mesh_.edges(); + const labelListList& meshPointEdges = mesh_.pointEdges(); + + // Loop through all points + forAll (meshPointEdges, pointI) + { + // Get point level of this point + const label& centralPointLevel = pointLevel_[pointI]; + + if (centralPointLevel < 1) + { + // Point can't be unrefined as its level is either 0 or + // invalid. Continue immediately + continue; + } + + // Flag to see whether this is a split point candidate + bool splitPointCandidate = true; + + // Get all edge labels for this point + const labelList& pEdges = meshPointEdges[pointI]; + + // Loop through all point edges + forAll (pEdges, i) + { + // Get edge index and the edge + const label& edgeI = pEdges[i]; + const edge& curEdge = meshEdges[edgeI]; + + // Loop through both points of the edge + forAll (curEdge, j) + { + // Get point index + const label& pointJ = curEdge[j]; + + if (pointLevel_[pointJ] != centralPointLevel) + { + // Point levels are different, this can't be a split point, + // set flag to false and break immediatelly + splitPointCandidate = false; + break; + } + // else: this is still potential split point candidate so + // there's nothing to do + } // End for both points of this edge + + // Check whether this can't be a split point already and break out + // immediately + if (!splitPointCandidate) + { + break; + } + } // End for all point faces + + // At this point, if the flag is still true, this is a split point + if (splitPointCandidate) + { + splitPointsMarkup[pointI] = true; + } + } + + // Note: if there is no dynamic load balancing, points at the processor + // boundary cannot be split points by definition. However, in dynamic load + // balancing runs, it is possible that a split point end on processor + // boundary, in which case we will simply avoid (actually delay) unrefining + // until this becomes internal point again. VV, 4/Jul/2018. + + // Get boundary mesh and mesh faces + const polyBoundaryMesh& bMesh = mesh_.boundaryMesh(); + const faceList& meshFaces = mesh_.faces(); + + // Loop through all patches + forAll (bMesh, patchI) + { + const polyPatch& patch = bMesh[patchI]; + + if (isA<processorPolyPatch>(patch)) + { + // Get patch start + const label startIndex = patch.start(); + + // Loop through all the faces + forAll (patch, i) + { + // Get global face index and face + const label faceI = startIndex + i; + const face& f = meshFaces[faceI]; + + // Make sure that we don't split around point at all points of + // the processor patch faces + forAll (f, fpI) + { + splitPointsMarkup[f[fpI]] = false; + } + } + } + } + + + // PART 2: Mark all unrefinement point candidates that are split points at + // the same time (basically the intersection of split points and candidates) + + // Create markup field of split points to unrefine + // True: this is a split point which should be unrefined + // False: this is either not a split point or it shouldn't be unrefined + boolList splitPointsToUnrefine(nPoints, false); + + // Loop through all unrefinement candidates + forAll (unrefinementPointCandidates, i) + { + // Get point index + const label& pointI = unrefinementPointCandidates[i]; + + if (splitPointsMarkup[pointI]) + { + // This is a split point, mark it for unrefinement + splitPointsToUnrefine[pointI] = true; + } + } + + + // PART 3: Make sure that we skip unrefining around split points that + // possibly have cells around that will be refined + + // Mark cells that need to be protected (will be refined in this iteration) + boolList protectedCell(mesh_.nCells(), false); + + // Loop through cells to refine and mark them + forAll (cellsToRefine_, i) + { + protectedCell[cellsToRefine_[i]] = true; + } + + // Extend protected cells across points using a specified number of + // unrefinement buffer layers + for (label i = 0; i < nUnrefinementBufferLayers_; ++i) + { + meshTools::extendMarkedCellsAcrossPoints(mesh_, protectedCell); + } + + // Loop through all cells and if the cell should be protected, protect all + // of its points from unrefinement + forAll (protectedCell, cellI) + { + if (protectedCell[cellI]) + { + // Get list of cell points for this protected cell + const labelList& cPoints = meshCellPoints[cellI]; + + // Loop through cell points and make sure that they are not marked + // for unrefinement + forAll (cPoints, j) + { + splitPointsToUnrefine[cPoints[j]] = false; + } + } + } + + + // PART 4: Ensure face consistent (2:1 constraint) and possibly edge + // consistent (4:1 constraint) unrefinement + + // Get necessary mesh data + const label nCells = mesh_.nCells(); + const labelListList& meshPointCells = mesh_.pointCells(); + + // Count number of removed cells from unrefinement (cells that will not be + // unrefined) in each iteration and number of iterations + label nRemCells = 0; + label nIters = 0; + label nTotalRemCells = 0; + + do + { + // First, create cells to unrefine (all cells sharing point to unrefine) + boolList cellsToUnrefine(nCells, false); + + // Loop through all split points to unrefine + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // This split point is marked for unrefinement, collect all of + // its cells + const labelList& pCells = meshPointCells[pointI]; + forAll (pCells, i) + { + cellsToUnrefine[pCells[i]] = true; + } + } + } + + // Reset number of removed cells from unrefinement for this iteration + nRemCells = 0; + + if (edgeBasedConsistency_) + { + // Check for 4:1 edge based consistent unrefinement. Updates + // cellsToUnrefine and returns number of removed cells from + // unrefinement in this iteration + nRemCells += edgeConsistentUnrefinement(cellsToUnrefine); + } + + // Check for 2:1 face based consistent unrefinement. Updates + // cellsToUnrefine and returns number of removed cells from unrefinement + // in this iteration + nRemCells += faceConsistentUnrefinement(cellsToUnrefine); + + // Global reduction + reduce(nRemCells, sumOp<label>()); + + // If we have removed at least one cell from unrefinement, we need to + // protect its split points as well from unrefinement + if (nRemCells > 0) + { + // Get point cells + const labelListList& meshPointCells = mesh_.pointCells(); + + // Loop through all split points to unrefine + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // This is a split point for unrefinement, get the cells + const labelList& pCells = meshPointCells[pointI]; + + // Loop through all point cells + forAll (pCells, i) + { + if (!cellsToUnrefine[pCells[i]]) + { + // Cell must not be refined, remove point from + // unrefinement as well + splitPointsToUnrefine[pointI] = false; + break; + } + } + } + } + } + + // Increment number of iterations and number of total removed cells + ++nIters; + nTotalRemCells += nRemCells; + + } while (nRemCells > 0); + + Info<< "Removed " << nTotalRemCells // nTotalRemCells already reduced + << " cells in " << returnReduce(nIters, maxOp<label>()) + << " iterations to obtain consistent unrefinement." + << endl; + + // Collect all split points to unrefine in a dynamic list + DynamicList<label> splitPointsToUnrefineDynamic(nPoints); + + forAll (splitPointsToUnrefine, pointI) + { + if (splitPointsToUnrefine[pointI]) + { + // Split point marked for unrefinement, append it + splitPointsToUnrefineDynamic.append(pointI); + } + } + + // Transfer the contents into the data member (ordinary list) + splitPointsToUnrefine_.transfer(splitPointsToUnrefineDynamic); + + Info<< "Selected " + << returnReduce(splitPointsToUnrefine_.size(), sumOp<label>()) + << " split points to unrefine." << endl; +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.H b/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.H new file mode 100644 index 0000000000000000000000000000000000000000..368aaad2fb5f981947c2b674785563c33296bd45 --- /dev/null +++ b/src/dynamicMesh/refinement/prismatic2DRefinement/prismatic2DRefinement.H @@ -0,0 +1,254 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Class + Foam::prismatic2DRefinement + +Description + Isotropic refinement of prismatic cells in 2D using the mesh modifier + engine. Used for 2D cases instead of polyhedralRefinement which carries + unnecessary overhead in terms of number of cells since it splits the cell in + all directions. + + Each prismatic cell is split by the following procedure: + 1. Adding points at the face centres and edge centres of all faces found on + an special patch: empty or wedge. + 2. Adding n cells per existing cell where n is the number of corner points + at the face on special patch (empty or wedge). + 3. Splitting each of the faces on special patch (empty or wedge) into + multiple faces going from: existing corner point -> new edge centre point + -> new face centre point -> other new edge centre point (sharing the same + corner point) + 4. Spliiting each of the faces not on an special patch (empty or wedge) into + two faces going from: existing corner point -> existing corner point on + the other side -> new edge centre point on the other side -> new edge + centre point on my side + 4. Adding internal faces going from: + new edge centre point -> new face centre point -> new other face + centre point on the other side -> new other edge mid point on the other + side + + It is an error to try and run this on anything except a 2D mesh. + +SourceFiles + prismatic2DRefinement.C + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + +\*---------------------------------------------------------------------------*/ + +#ifndef prismatic2DRefinement_H +#define prismatic2DRefinement_H + +#include "refinement.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class prismatic2DRefinement Declaration +\*---------------------------------------------------------------------------*/ + +class prismatic2DRefinement +: + public refinement +{ +private: + + // Private Member Functions + + // Helper functions + + //- Get least cell level such that the face has at least nPoints + // points smaller than the level + label getAnchorLevel + ( + const label faceI, + const label nPoints + ) const; + + //- Append given face into a dynamic list containing split faces + // that will be split into two faces (third parameter). Additionaly + // append information on which of the two edges of the face are on + // special patch into a dynamic list (fourth parameter) + void appendFaceSplitInfo + ( + const label& faceI, + const boolList& edgeOnEmptyPatch, + const labelList& edgeMidPoint, + DynamicList<label>& splitFacesIntoTwo, + DynamicList<Pair<label> >& splitFacesEmptyEdges + ) const; + + + // Topological change helper functions + + //- Set new owner and neighbour given anchor pointI, faceI and the + // necessary mapping + void setNewFaceNeighbours + ( + const HashTable + < + label, + Pair<label>, + Hash<FixedList<label, 2> > + >& pointCellToAddedCellMap, + const labelListList& cellAddedCells, + const label& faceI, + const label& pointI, + + label& own, + label& nei + ) const; + + //- Get index of point with minimum point level of a face across two + // connected edges starting from a local point index. + // Example: starting from point with level 1 in the upper left + // corner, finds point index of the point with level 0 which is on + // the same face, connected with edge to original point + // 1------1 + // | + // | + // 0 + // Note: passing face edges and mesh edges as parameters to avoid + // fetching them from mesh due to lazy evaluation + label findMinEdgeConnectedLevel + ( + const label& fpI, + const label& faceI, + const face& f, + const labelList& fEdges, + const edgeList& meshEdges + ) const; + + //- Store two face mids when adding internal faces + void addFaceMids + ( + const labelList& faceMidPoint, + const boolList& faceOnEmptyPatch, + const label& faceI, + const label& cellI, + face& newFace + ) const; + + + // Debug functions + + //- Check orientation of a split face + void checkNewFaceOrientation + ( + batchPolyTopoChange& ref, + const label& faceI, + const face& newFace + ) const; + + + // Copy control + + //- Disallow default bitwise copy construct + prismatic2DRefinement(const prismatic2DRefinement&) = delete; + + //- Disallow default bitwise assignment + void operator=(const prismatic2DRefinement&) = delete; + + +protected: + + // Protected Pure Virtual Member Functions + + // Global topology modification functions (operate on whole polyMesh) + + //- Set refinement instruction + virtual void setRefinementInstruction + ( + batchPolyTopoChange& ref + ) const; + + //- Set unrefinement instruction + virtual void setUnrefinementInstruction + ( + batchPolyTopoChange& ref + ) const; + + +public: + + //- Runtime type information + TypeName("prismatic2DRefinement"); + + + // Constructors + + //- Construct from dictionary + prismatic2DRefinement + ( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme + ); + + + //- Destructor + virtual ~prismatic2DRefinement(); + + + // Member Functions + + // Edit + + //- Set cells to refine given a list of refinement + // candidates. Refinement candidates are extended within the + // function due to possible 4:1 conflicts and specified number of + // buffer layers. + // Note: must be called BEFORE setSplitPointsToUnrefine + virtual void setCellsToRefine + ( + const labelList& refinementCellCandidates + ); + + //- Set split points to unrefine given a list of all mesh points + // that are candidates for unrefinement. Split points are + // determined as a subset of unrefinement candidates, avoiding + // splitting points of cells that are going to be refined at the + // same time and ensuring consistent unrefinement. + // Note: must be called AFTER setCellsToRefine + void setSplitPointsToUnrefine + ( + const labelList& unrefinementPointCandidates + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/refinement/refinement.C b/src/dynamicMesh/refinement/refinement/refinement.C new file mode 100644 index 0000000000000000000000000000000000000000..75f8b2eaaac4d7e1fed931ac23e20a621738aed9 --- /dev/null +++ b/src/dynamicMesh/refinement/refinement/refinement.C @@ -0,0 +1,1489 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + Hrvoje Jasak, Wikki Ltd. + +Notes + Generalisation of hexRef8 for polyhedral cells and refactorisation into mesh + modifier engine. + +\*---------------------------------------------------------------------------*/ + +#include "refinement.H" +#include "polyTopoChanger.H" +#include "polyMesh.H" +#include "batchPolyTopoChange.H" +#include "syncTools.H" +#include "meshTools.H" +#include "polyAddFace.H" +#include "polyAddPoint.H" +#include "polyAddCell.H" +#include "polyModifyFace.H" +#include "mapPolyMesh.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(refinement, 0); + + +} + +const Foam::scalar Foam::refinement::nonOrthThreshold_ = 70; + + +// * * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * // + +void Foam::refinement::setInstance(const fileName& inst) const +{ + if (debug) + { + Pout<< "refinement::setInstance(const fileName& inst)" + << nl + << "Resetting file instance of refinement data to " << inst + << endl; + } + + cellLevel_.instance() = inst; + pointLevel_.instance() = inst; +} + + +Foam::label Foam::refinement::addFace +( + batchPolyTopoChange& ref, + const label faceI, + const face& newFace, + const label own, + const label nei +) const +{ + // Set face information + label patchID, zoneID, zoneFlip; + meshTools::setFaceInfo(mesh_, faceI, patchID, zoneID, zoneFlip); + + // Set new face index to -1 + label newFaceI = -1; + + if ((nei == -1) || (own < nei)) + { + // Ordering is ok, add the face + newFaceI = ref.setAction + ( + polyAddFace + ( + newFace, // face + own, // owner + nei, // neighbour + -1, // master point + -1, // master edge + faceI, // master face for addition + false, // flux flip + patchID, // patch for face + zoneID, // zone for face + zoneFlip // face zone flip + ) + ); + } + else + { + // Ordering is flipped, reverse face and flip owner/neighbour + newFaceI = ref.setAction + ( + polyAddFace + ( + newFace.reverseFace(), // face + nei, // owner + own, // neighbour + -1, // master point + -1, // master edge + faceI, // master face for addition + false, // flux flip + patchID, // patch for face + zoneID, // zone for face + zoneFlip // face zone flip + ) + ); + } + + return newFaceI; +} + + +Foam::label Foam::refinement::addInternalFace +( + batchPolyTopoChange& ref, + const label meshFaceI, + const label meshPointI, + const face& newFace, + const label own, + const label nei +) const +{ + // Check whether this is an internal face + if (mesh_.isInternalFace(meshFaceI)) + { + return ref.setAction + ( + polyAddFace + ( + newFace, // face + own, // owner + nei, // neighbour + -1, // master point + -1, // master edge + meshFaceI, // master face for addition + false, // flux flip + -1, // patch for face + -1, // zone for face + false // face zone flip + ) + ); + } + else + { + // This is not an internal face. Add face out of nothing + return ref.setAction + ( + polyAddFace + ( + newFace, // face + own, // owner + nei, // neighbour + -1, // master point + -1, // master edge + 0, // master face for addition + false, // flux flip + -1, // patch for face + -1, // zone for face + false // face zone flip + ) + ); + } +} + + +void Foam::refinement::modifyFace +( + batchPolyTopoChange& ref, + const label faceI, + const face& newFace, + const label own, + const label nei +) const +{ + // Set face inforomation + label patchID, zoneID, zoneFlip; + meshTools::setFaceInfo(mesh_, faceI, patchID, zoneID, zoneFlip); + + // Get owner/neighbour addressing and mesh faces + const labelList& owner = mesh_.faceOwner(); + const labelList& neighbour = mesh_.faceNeighbour(); + + const faceList& meshFaces = mesh_.faces(); + + if + ( + (own != owner[faceI]) + || ( + mesh_.isInternalFace(faceI) + && (nei != neighbour[faceI]) + ) + || (newFace != meshFaces[faceI]) + ) + { + // Either: + // 1. Owner index does not correspond to mesh owner, + // 2. Neighbour index does not correspond to mesh neighbour, + // 3. New face does not correspond to mesh face + // So we need to modify this face + if ((nei == -1) || (own < nei)) + { + // Ordering is ok, add the face + ref.setAction + ( + polyModifyFace + ( + newFace, // modified face + faceI, // label of face being modified + own, // owner + nei, // neighbour + false, // face flip + patchID, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); + } + else + { + // Ordering is flipped, reverse face and flip owner/neighbour + ref.setAction + ( + polyModifyFace + ( + newFace.reverseFace(), // modified face + faceI, // label of face being modified + nei, // owner + own, // neighbour + false, // face flip + patchID, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); + } + } +} + + +void Foam::refinement::walkFaceToMid +( + const labelList& edgeMidPoint, + const label cLevel, + const label faceI, + const label startFp, + DynamicList<label>& faceVerts +) const +{ + // Get the face and its edges + const face& f = mesh_.faces()[faceI]; + const labelList& fEdges = mesh_.faceEdges()[faceI]; + + label fp = startFp; + + // Starting from fp store all (1 or 2) vertices until where the face + // gets split + while (true) + { + if (edgeMidPoint[fEdges[fp]] > -1) + { + // Edge is split, append its mid point + faceVerts.append(edgeMidPoint[fEdges[fp]]); + } + + fp = f.fcIndex(fp); + + if (pointLevel_[f[fp]] <= cLevel) + { + // Found next anchor. Already appended the split point just above + return; + } + else if (pointLevel_[f[fp]] == cLevel + 1) + { + // Mid level, append and return + faceVerts.append(f[fp]); + + return; + } + else if (pointLevel_[f[fp]] == cLevel + 2) + { + // Store and continue to cLevel + 1 + faceVerts.append(f[fp]); + } + } +} + + +void Foam::refinement::walkFaceFromMid +( + const labelList& edgeMidPoint, + const label cLevel, + const label faceI, + const label startFp, + DynamicList<label>& faceVerts +) const +{ + // Get the face and its edges + const face& f = mesh_.faces()[faceI]; + const labelList& fEdges = mesh_.faceEdges()[faceI]; + + label fp = f.rcIndex(startFp); + + while (true) + { + if (pointLevel_[f[fp]] <= cLevel) + { + // Anchor point, break + break; + } + else if (pointLevel_[f[fp]] == cLevel + 1) + { + // Mid level, append and break + faceVerts.append(f[fp]); + break; + } + else if (pointLevel_[f[fp]] == cLevel + 2) + { + // Continue to cLevel + 1 + } + fp = f.rcIndex(fp); + } + + // Store + while (true) + { + if (edgeMidPoint[fEdges[fp]] > -1) + { + // Edge is split, append its mid point + faceVerts.append(edgeMidPoint[fEdges[fp]]); + } + + fp = f.fcIndex(fp); + + if (fp == startFp) + { + break; + } + faceVerts.append(f[fp]); + } +} + + +Foam::label Foam::refinement::findMinLevel(const labelList& f) const +{ + // Initialise minimum level to large value + label minLevel = labelMax; + + // Initialise point label at which min level is reached to -1 + label pointIMin = -1; + + forAll(f, fp) + { + const label& level = pointLevel_[f[fp]]; + + if (level < minLevel) + { + minLevel = level; + pointIMin = fp; + } + } + + return pointIMin; +} + + +Foam::label Foam::refinement::findMaxLevel(const labelList& f) const +{ + // Initialise maximum level to small value + label maxLevel = labelMin; + + // Initialise point label at which max level is reached to -1 + label pointIMax = -1; + + forAll(f, fp) + { + const label& level = pointLevel_[f[fp]]; + + if (level > maxLevel) + { + maxLevel = level; + pointIMax = fp; + } + } + + return pointIMax; +} + + +Foam::label Foam::refinement::countAnchors +( + const labelList& f, + const label anchorLevel +) const +{ + label nAnchors = 0; + + forAll(f, fp) + { + if (pointLevel_[f[fp]] <= anchorLevel) + { + ++nAnchors; + } + } + return nAnchors; +} + +void Foam::refinement::adjustRefLevel +( + label& curNewCellLevel, + const label oldCellI +) +{ + if (oldCellI == -1) + { + // This cell is inflated (does not originate from other cell), set + // cell level to -1 + curNewCellLevel = -1; + } + else + { + // This cell has either been added based on another cell or it + // hasn't changed. Update new cell level according to refinement + // level indicator and old cell level + + // Get refinement status of the old cell + const label& refStatus = refinementLevelIndicator_[oldCellI]; + + if (refStatus == UNREFINED) + { + // New cell has been obtained by unrefining other cells - this + // is the remaining "master" cell. Decrement cell level + curNewCellLevel = cellLevel_[oldCellI] - 1; + } + else if (refStatus == UNCHANGED) + { + // Cell hasn't been changed during this refinement, copy old + // cell level + curNewCellLevel = cellLevel_[oldCellI]; + } + else if (refStatus == REFINED) + { + // Cell has been refined, increment cell level + curNewCellLevel = cellLevel_[oldCellI] + 1; + } + else + { + FatalErrorInFunction + << "Invalid refinement status detected: " + << refStatus << nl + << "Old cell index: " << oldCellI << abort(FatalError); + } + } +} + + +void Foam::refinement::checkInternalOrientation +( + batchPolyTopoChange& ref, + const label cellI, + const label faceI, + const point& ownPt, + const point& neiPt, + const face& newFace +) const +{ + const face compactFace(identity(newFace.size())); + + // Get list of polyAddPoint objects + const DynamicList<polyAddPoint>& polyAddedPoints(ref.addedPoints()); + + // Create a list of all points + const label nOrigPoints = mesh_.nPoints(); + pointField allPoints(nOrigPoints + polyAddedPoints.size()); + + // Set ordinary points first + const pointField& meshPoints = mesh_.points(); + forAll (meshPoints, i) + { + allPoints[i] = meshPoints[i]; + } + + // Set newly added points next + forAll (polyAddedPoints, i) + { + allPoints[i + nOrigPoints] = polyAddedPoints[i].newPoint(); + } + + // Get compact points + const pointField compactPoints + ( + IndirectList<point>(allPoints, newFace)() + ); + + const vector n(compactFace.unitNormal(compactPoints)); + const vector dir(neiPt - ownPt); + + // Check orientation error + if ((dir & n) < 0) + { + FatalErrorInFunction + << "cell:" << cellI << " old face:" << faceI + << " newFace:" << newFace << endl + << " coords:" << compactPoints + << " ownPt:" << ownPt + << " neiPt:" << neiPt + << abort(FatalError); + } + + // Note: report significant non-orthogonality error + const scalar severeNonOrthogonalityThreshold = + ::cos + ( + nonOrthThreshold_/180.0*constant::mathematical::pi + ); + + const vector fcToOwn(compactFace.centre(compactPoints) - ownPt); + + // Note: normal vector already normalised + const scalar dDotN = (fcToOwn & n)/(mag(fcToOwn) + VSMALL); + + if (dDotN > severeNonOrthogonalityThreshold) + { + WarningIn + ( + "refinement::checkInternalOrientation(...)" + ) + << "Detected severely non-orthogonal face with non-orthogonality: " + << ::acos(dDotN)/constant::mathematical::pi*180.0 + << "cell:" << cellI << " old face:" << faceI + << " newFace:" << newFace << endl + << " coords:" << compactPoints + << " ownPt:" << ownPt + << " neiPt:" << neiPt + << " dDotN:" << dDotN + << endl; + } +} + + +void Foam::refinement::checkBoundaryOrientation +( + batchPolyTopoChange& ref, + const label cellI, + const label faceI, + const point& ownPt, + const point& boundaryPt, + const face& newFace +) const +{ + const face compactFace(identity(newFace.size())); + + // Get list of polyAddPoint objects + const DynamicList<polyAddPoint>& polyAddedPoints(ref.addedPoints()); + + // Create a list of all points + const label nOrigPoints = mesh_.nPoints(); + pointField allPoints(nOrigPoints + polyAddedPoints.size()); + + // Set ordinary points first + const pointField& meshPoints = mesh_.points(); + forAll (meshPoints, i) + { + allPoints[i] = meshPoints[i]; + } + + // Set newly added points next + forAll (polyAddedPoints, i) + { + allPoints[i + nOrigPoints] = polyAddedPoints[i].newPoint(); + } + + // Get compact points + const pointField compactPoints + ( + IndirectList<point>(allPoints, newFace)() + ); + + const vector n(compactFace.unitNormal(compactPoints)); + const vector dir(boundaryPt - ownPt); + + // Check orientation error + if ((dir & n) < 0) + { + FatalErrorInFunction + << "cell:" << cellI << " old face:" << faceI + << " newFace:" << newFace + << " coords:" << compactPoints + << " ownPt:" << ownPt + << " boundaryPt:" << boundaryPt + << abort(FatalError); + } + + // Note: report significant non-orthogonality error + const scalar severeNonOrthogonalityThreshold = + ::cos + ( + nonOrthThreshold_/180.0*constant::mathematical::pi + ); + + const vector fcToOwn(compactFace.centre(compactPoints) - ownPt); + + // Note: normal vector already normalised + const scalar dDotN = (fcToOwn & n)/(mag(fcToOwn) + VSMALL); + + if (dDotN > severeNonOrthogonalityThreshold) + { + WarningInFunction + << "Detected severely non-orthogonal face with non-orthogonality: " + << ::acos(dDotN)/constant::mathematical::pi*180.0 + << "cell:" << cellI << " old face:" << faceI + << " newFace:" << newFace + << " coords:" << compactPoints + << " ownPt:" << ownPt + << " boundaryPt:" << boundaryPt + << " dDotN:" << dDotN + << endl; + } +} + + +Foam::label Foam::refinement::faceConsistentRefinement +( + boolList& cellsToRefine +) const +{ + // Count number of cells that will be added + label nAddCells = 0; + + // Get necessary mesh data + const label nFaces = mesh_.nFaces(); + const label nInternalFaces = mesh_.nInternalFaces(); + + const labelList& owner = mesh_.faceOwner(); + const labelList& neighbour = mesh_.faceNeighbour(); + + // Loop through internal faces and check consistency + for (label faceI = 0; faceI < nInternalFaces; ++faceI) + { + // Get owner and neighbour labels + const label& own = owner[faceI]; + const label& nei = neighbour[faceI]; + + // Get owner and neighbour cell levels + // Note: If the cell is marked for refinement, the level is current + // level + 1, otherwise it is equal to the current level + const label ownLevel = + cellsToRefine[own] ? cellLevel_[own] + 1 : cellLevel_[own]; + const label neiLevel = + cellsToRefine[nei] ? cellLevel_[nei] + 1 : cellLevel_[nei]; + + if (ownLevel > (neiLevel + 1)) + { + // Owner level is higher than neighbour level + 1, neighbour must be + // marked for refinement + cellsToRefine[nei] = true; + ++nAddCells; + } + else if (neiLevel > (ownLevel + 1)) + { + // Neighbour level is higher than owner level + 1, owner must be + // marked for refinement + cellsToRefine[own] = true; + ++nAddCells; + } + } + + // Create owner level for boundary faces to prepare for swapping on coupled + // boundaries + labelList ownLevel(nFaces - nInternalFaces); + forAll (ownLevel, i) + { + // Get owner of the face and update owner cell levels + const label& own = owner[i + nInternalFaces]; + ownLevel[i] = + cellsToRefine[own] ? cellLevel_[own] + 1 : cellLevel_[own]; + } + + // Swap boundary face lists (coupled boundary update) + syncTools::swapBoundaryFaceList(mesh_, ownLevel); + + // Note: now the ownLevel list actually contains the neighbouring level + // (from the other side), use alias (reference) for clarity from now on + const labelList& neiLevel = ownLevel; + + // Loop through boundary faces + forAll (neiLevel, i) + { + // Get owner of the face and owner level + const label& own = owner[i + nInternalFaces]; + const label curOwnLevel = + cellsToRefine[own] ? cellLevel_[own] + 1 : cellLevel_[own]; + + // Note: we are using more stringent 1:1 consistency across coupled + // boundaries in order to simplify handling of edge based consistency + // checks for parallel runs + // Bugfix related to PLB: Check whether owner is already marked for + // refinement. Will allow 2:1 consistency across certain processor faces + // where we have a new processor boundary. VV, 23/Jan/2019. + if + ( + (neiLevel[i] > curOwnLevel) + && !cellsToRefine[own] + ) + { + // Neighbour level is higher than owner level, owner must be + // marked for refinement + cellsToRefine[own] = true; + ++nAddCells; + } + + // Note: other possibility (that owner level is higher than neighbour + // level) is taken into account on the other side automatically + } + + // Return number of added cells + return nAddCells; +} + + +Foam::label Foam::refinement::edgeConsistentRefinement +( + boolList& cellsToRefine +) const +{ + // Count number of cells that will be added + label nAddCells = 0; + + // Algorithm: loop over all edges and visit all unique cell pairs sharing + // this particular edge. Then, ensure 2:1 edge consistency by marking + // cell with lower level for refinement + + // Get edge cells + const labelListList& meshEdgeCells = mesh_.edgeCells(); + + // Loop through all mesh edges + forAll (meshEdgeCells, edgeI) + { + // Get current edge cells + const labelList& curEdgeCells = meshEdgeCells[edgeI]; + + // Loop through all edge cells + forAll (curEdgeCells, i) + { + // Get first cell index + const label& cellI = curEdgeCells[i]; + + // Loop through remaining edge cells + for (label j = i + 1; j < curEdgeCells.size(); ++j) + { + // Get second cell index + const label& cellJ = curEdgeCells[j]; + + // Get levels of the two cells. If the cell is marked for + // refinement, the level is current level + 1, otherwise it is + // equal to the current level + + // Note: cellsToRefine flag for both cellI and cellJ might + // change, this is why we need to recalculate cellI level here + const label cellILevel = + cellsToRefine[cellI] + ? cellLevel_[cellI] + 1 + : cellLevel_[cellI]; + + const label cellJLevel = + cellsToRefine[cellJ] + ? cellLevel_[cellJ] + 1 + : cellLevel_[cellJ]; + + if (cellILevel > cellJLevel + 1) + { + // Level of cellI is higher than level of cellJ + 1, cellJ + // must be marked for refinement + cellsToRefine[cellJ] = true; + ++nAddCells; + } + else if (cellJLevel > cellILevel + 1) + { + // Level of cellJ is higher than level of cellI + 1, cellI + // must be marked for refinement + cellsToRefine[cellI] = true; + ++nAddCells; + } + } + } + } + + // Note: in order to avoid very difficult and time-consuming parallelisation + // of edge cell connectivity and edge cell values, we enforce a more + // stringent face-based consistency across processor boundaries. Basically, + // if a face-based consistency of 1:1 (not 2:1 as for ordinary faces) is + // ensured, the edge-based consistency becomes a local operation (I'm not + // 100% sure to be honest since there are countless variants when dealing + // with arbitrary polyhedral cells). + // See faceConsistentRefinement for details. VV, 17/Apr/2018. + + // Return number of added cells + return nAddCells; +} + + +Foam::label Foam::refinement::faceConsistentUnrefinement +( + boolList& cellsToUnrefine +) const +{ + // Count number of removed cells from unrefinement + label nRemCells = 0; + + // Get necessary mesh data + const label nFaces = mesh_.nFaces(); + const label nInternalFaces = mesh_.nInternalFaces(); + + const labelList& owner = mesh_.faceOwner(); + const labelList& neighbour = mesh_.faceNeighbour(); + + // Loop through internal faces and check consistency + for (label faceI = 0; faceI < nInternalFaces; ++faceI) + { + // Get owner and neighbour labels + const label& own = owner[faceI]; + const label& nei = neighbour[faceI]; + + // Get owner and neighbour cell levels + // Note: If the cell is marked for unrefinement, the level is current + // level - 1, otherwise it is equal to the current level + const label ownLevel = + cellsToUnrefine[own] ? cellLevel_[own] - 1 : cellLevel_[own]; + const label neiLevel = + cellsToUnrefine[nei] ? cellLevel_[nei] - 1 : cellLevel_[nei]; + + if (ownLevel < (neiLevel - 1)) + { + // Owner level is smaller than neighbour level - 1, we must not + // unrefine owner + + // Check whether the cell has not been marked for unrefinement + if (!cellsToUnrefine[own]) + { + FatalErrorInFunction + << "Cell not marked for unrefinement, indicating a" + << " previous unnoticed problem with unrefinement." + << nl + << "Owner: " << own << ", neighbour: " << nei + << nl + << "Owner level: " << ownLevel + << ", neighbour level: " << neiLevel << nl + << "This is probably because the refinement and " + << "unrefinement regions are very close." << nl + << "Try increasing nUnrefinementBufferLayers. " + << abort(FatalError); + } + else + { + cellsToUnrefine[own] = false; + ++nRemCells; + } + } + else if (neiLevel < (ownLevel - 1)) + { + // Neighbour level is smaller than owner level - 1, we must not + // unrefine neighbour + + // Check whether the cell has not been marked for unrefinement + if (!cellsToUnrefine[nei]) + { + FatalErrorInFunction + << "Cell not marked for unrefinement, indicating a" + << " previous unnoticed problem with unrefinement." + << nl + << "Owner: " << own << ", neighbour: " << nei + << nl + << "Owner level: " << ownLevel + << ", neighbour level: " << neiLevel << nl + << "This is probably because the refinement and " + << "unrefinement regions are very close." << nl + << "Try increasing nUnrefinementBufferLayers. " + << abort(FatalError); + } + else + { + cellsToUnrefine[nei] = false; + ++nRemCells; + } + } + } + + // Create owner level for boundary faces to prepare for swapping on coupled + // boundaries + labelList ownLevel(nFaces - nInternalFaces); + forAll (ownLevel, i) + { + // Get owner of the face and update owner cell levels + const label& own = owner[i + nInternalFaces]; + ownLevel[i] = + cellsToUnrefine[own] ? cellLevel_[own] - 1 : cellLevel_[own]; + } + + // Swap boundary face lists (coupled boundary update) + syncTools::swapBoundaryFaceList(mesh_, ownLevel); + + // Note: now the ownLevel list actually contains the neighbouring level + // (from the other side), use alias (reference) for clarity from now on + const labelList& neiLevel = ownLevel; + + // Loop through boundary faces + forAll (neiLevel, i) + { + // Get owner of the face and owner level + const label& own = owner[i + nInternalFaces]; + const label curOwnLevel = + cellsToUnrefine[own] ? cellLevel_[own] - 1 : cellLevel_[own]; + + // Note: we are using more stringent 1:1 consistency across coupled + // boundaries in order to simplify handling of edge based consistency + // checks for parallel runs + if (curOwnLevel < neiLevel[i]) + { + // Owner level is smaller than neighbour level, we must not + // unrefine owner + + // Check whether the cell has not been marked for unrefinement + // Note: this "redundancy" check should not be performed if we are + // running with dynamic load balancing. If an ordinary face with + // standard consistency (2:1) becomes a processor face with more + // stringent consistency (1:1), the refinement still remains valid, + // even though the 1:1 consistency is not achieved for this time + // step. Doing further refinement will make sure that this does not + // exceed at least 2:1 consistency and therefore 2:1 edge + // consistency as well. Instead of issuing a FatalError, issue a + // Warning and wrap it into debug + if (!cellsToUnrefine[own]) + { + if (debug) + { + WarningInFunction + << "Boundary cell not marked for unrefinement," + << " indicating a previous unnoticed problem with" + << " unrefinement." + << nl + << "Owner: " << own + << nl + << "Owner level: " << curOwnLevel + << ", neighbour level: " << neiLevel[i] << nl + << "This is probably because the refinement and " + << "unrefinement regions are very close." << nl + << "Try increasing nUnrefinementBufferLayers. " + << nl + << "Another possibility is that you are running " + << "with Dynamic Load Balancing, in which case " + << "this should be fine." + << endl; + } + } + else + { + cellsToUnrefine[own] = false; + ++nRemCells; + } + } + + // Note: other possibility (that neighbour level is smaller than owner + // level) is taken into account on the other side automatically + } + + // Return number of local cells removed from unrefinement + return nRemCells; +} + + +Foam::label Foam::refinement::edgeConsistentUnrefinement +( + boolList& cellsToUnrefine +) const +{ + // Count number of cells that will be removed + label nRemCells = 0; + + // Algorithm: loop over all edges and visit all unique cell pairs sharing + // this particular edge. Then, ensure 2:1 edge consistency by protecting the + // cell with lower level from unrefinement + + // Get edge cells + const labelListList& meshEdgeCells = mesh_.edgeCells(); + + // Loop through all mesh edges + forAll (meshEdgeCells, edgeI) + { + // Get current edge cells + const labelList& curEdgeCells = meshEdgeCells[edgeI]; + + // Loop through all edge cells + forAll (curEdgeCells, i) + { + // Get first cell index + const label& cellI = curEdgeCells[i]; + + // Loop through remaining edge cells + for (label j = i + 1; j < curEdgeCells.size(); ++j) + { + // Get second cell index + const label& cellJ = curEdgeCells[j]; + + // Get levels of the two cells. If the cell is marked for + // unrefinement, the level is current level - 1, otherwise it is + // equal to the current level + + // Note: cellsToUnrefine flag for both cellI and cellJ might + // change, this is why we need to recalculate cellI level here + const label cellILevel = + cellsToUnrefine[cellI] + ? cellLevel_[cellI] - 1 + : cellLevel_[cellI]; + + const label cellJLevel = + cellsToUnrefine[cellJ] + ? cellLevel_[cellJ] - 1 + : cellLevel_[cellJ]; + + if (cellILevel < cellJLevel - 1) + { + // Level of cellI is smaller than level of cellJ - 1, cellI + // must be protected from unrefinement + + // Check whether the cell has not been marked for + // unrefinement + if (!cellsToUnrefine[cellI]) + { + if (debug) + { + WarningInFunction + << "Cell not marked for unrefinement, indicating a" + << " previous unnoticed problem with unrefinement." + << nl + << "cellI: " << cellI << ", cellJ: " << cellJ + << nl + << "Level of cellI: " << cellILevel + << ", level of cellJ: " << cellJLevel << nl + << "This is probably because the refinement and " + << "unrefinement regions are very close." << nl + << "Try increasing nUnrefinementBufferLayers. " + << endl; + } + } + else + { + cellsToUnrefine[cellI] = false; + ++nRemCells; + } + } + else if (cellJLevel < cellILevel - 1) + { + // Level of cellJ is smaller than level of cellI - 1, cellJ + // must be protected from unrefinement + + // Check whether the cell has not been marked for + // unrefinement + if (!cellsToUnrefine[cellJ]) + { + if (debug) + { + WarningInFunction + << "Cell not marked for unrefinement, indicating a" + << " previous unnoticed problem with unrefinement." + << nl + << "cellI: " << cellI << ", cellJ: " << cellJ + << nl + << "Level of cellI: " << cellILevel + << ", level of cellJ: " << cellJLevel << nl + << "This is probably because the refinement and " + << "unrefinement regions are very close." << nl + << "Try increasing nUnrefinementBufferLayers. " + << endl; + } + } + else + { + cellsToUnrefine[cellJ] = false; + ++nRemCells; + } + } + } + } + } + + // Note: in order to avoid very difficult and time-consuming parallelisation + // of edge cell connectivity and edge cell values, we enforce a more + // stringent face-based consistency across processor boundaries. Basically, + // if a face-based consistency of 1:1 (not 2:1 as for ordinary faces) is + // ensured, the edge-based consistency becomes a local operation (I'm not + // 100% sure to be honest whether this is true all the time since there are + // countless variants when dealing with arbitrary polyhedral cells). + // See faceConsistentRefinement for details. VV, 3/Apr/2018. + + // Return number of removed cells + return nRemCells; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::refinement::refinement +( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme +) +: + polyMeshModifier(name, index, mme, Switch(dict.lookup("active"))), + mesh_(mme.mesh()), + cellsToRefine_(), + splitPointsToUnrefine_(), + cellLevel_ + ( + IOobject + ( + "cellLevel", + mesh_.facesInstance(), + polyMesh::meshSubDir, + mesh_, + IOobject::READ_IF_PRESENT, + IOobject::AUTO_WRITE + ), + labelField(mesh_.nCells(), 0) + ), + pointLevel_ + ( + IOobject + ( + "pointLevel", + mesh_.facesInstance(), + polyMesh::meshSubDir, + mesh_, + IOobject::READ_IF_PRESENT, + IOobject::AUTO_WRITE + ), + labelField(mesh_.nPoints(), 0) + ), + refinementLevelIndicator_(0), // Must be empty before setting refinement + faceRemover_(mesh_, GREAT), // Merge boundary faces wherever possible + maxCells_(readLabel(dict.lookup("maxCells"))), + maxRefinementLevel_(readLabel(dict.lookup("maxRefinementLevel"))), + edgeBasedConsistency_ + ( + dict.lookupOrDefault<Switch>("edgeBasedConsistency", true) + ), + nRefinementBufferLayers_ + ( + readScalar(dict.lookup("nRefinementBufferLayers")) + ), + nUnrefinementBufferLayers_ + ( + readScalar(dict.lookup("nUnrefinementBufferLayers")) + ) +{ + Info<< "refinement::refinement: " << "Created pointLevel and cellLevel" + << endl; + + // Check consistency between cellLevel and number of cells and pointLevel + // and number of points in the mesh + if + ( + cellLevel_.size() != mesh_.nCells() + || pointLevel_.size() != mesh_.nPoints() + ) + { + FatalErrorInFunction + << "Restarted from inconsistent cellLevel or pointLevel files." + << endl + << "Number of cells in mesh: " << mesh_.nCells() + << " does not equal size of cellLevel: " << cellLevel_.size() << nl + << "Number of points in mesh: " << mesh_.nPoints() + << " does not equal size of pointLevel: " << pointLevel_.size() + << abort(FatalError); + } + + // Check specified number of maximum cells + if (maxCells_ < 1) + { + FatalErrorInFunction + << "Specified zero or negative maxCells." + << nl + << "This is not allowed." + << abort(FatalError); + } + + // Check maximum refinement level + if (maxRefinementLevel_ < 0) + { + FatalErrorInFunction + << "Negative maxRefinementLevel specified." + << nl + << "This is not allowed." + << abort(FatalError); + } + + // If the maximum refinementLevel is greater than 2 and the user insists on + // not using point based refinement strategy, issue a warning + if (!edgeBasedConsistency_ && maxRefinementLevel_ > 2) + { + WarningInFunction + << "You are not using point based consistency for dynamic" + << " refinement." + << nl + << "Since you are allowing more than two maximum refinement" + << " refinement levels, this might produce erroneous mesh due to" + << " 8:1 point conflicts." + << nl + << "In order to supress this message and use point based" + << " consistency checks, set edgeBasedConsistency to true." + << endl; + } + + // Check number of refinement buffer layers + if (nRefinementBufferLayers_ < 0) + { + FatalErrorInFunction + << "Negative nRefinementBufferLayers specified." + << nl + << "This is not allowed." + << abort(FatalError); + } + + // Check number of unrefinement buffer layers + if (nUnrefinementBufferLayers_ < 0) + { + FatalErrorInFunction + << "Negative nUnrefinementBufferLayers specified." + << nl + << "This is not allowed." + << abort(FatalError); + } + + // Check whether the number of unrefinement buffer layers is smaller than + // number of refinement buffer layers + 2 + if (nUnrefinementBufferLayers_ < nRefinementBufferLayers_ + 2) + { + WarningInFunction + << "Using " << nUnrefinementBufferLayers_ + << " unrefinement buffer layers and " << nRefinementBufferLayers_ + << " refinement buffer layers." + << nl + << "Make sure that the number of unrefinement buffer layers is " + << "at least nRefinementBufferLayers + 2" << nl + << "in order to avoid problems with edge level inconsistency when " + << "refinement and unrefinement are performed in same iteration." + << endl; + } +} + + +// * * * * * * * * * * * * * Public Member Functions * * * * * * * * * * * * // + +bool Foam::refinement::changeTopology() const +{ + if (!active()) + { + // Modifier is inactive, skip topo change + if (debug) + { + Pout<< "bool refinement::changeTopology() const" + << "for object " << name() << " : " + << "Inactive" << endl; + } + + return false; + } + else + { + return true; + } +} + + +void Foam::refinement::setRefinement(batchPolyTopoChange& ref) const +{ + // Set refinement and unrefinement + this->setRefinementInstruction(ref); + this->setUnrefinementInstruction(ref); + + // Clear the list of cells to refine and split points to unrefine since the + // refinement/unrefinement instructions have been set + cellsToRefine_.clear(); + splitPointsToUnrefine_.clear(); +} + + +void Foam::refinement::modifyMotionPoints +( + pointField& motionPoints +) const +{ + if (debug) + { + Pout<< "void refinement::modifyMotionPoints(" + << "pointField& motionPoints) const for object " + << name() << " : "; + } + + if (debug) + { + Pout << "No motion point adjustment" << endl; + } +} + + + +void Foam::refinement::updateMesh(const mapPolyMesh& map) +{ + if (debug) + { + Info<< "refinement::updateMesh(const mapPolyMesh&) " + << " for object " << name() << " : " + << "Updating cell and point levels." + << endl; + } + + // Mesh has changed topologically, we need to update cell and point levels + // and optionally face removal object + + // Get cell map: from current mesh cells to previous mesh cells + const labelList& cellMap = map.cellMap(); + + // Create new cell level + labelList newCellLevel(cellMap.size()); + + // Loop through all new cells + forAll (cellMap, newCellI) + { + adjustRefLevel(newCellLevel[newCellI], cellMap[newCellI]); + } + + // Do cells from points: unrefinement + // Note: should other types be done as well? + // HJ, 9/Sep/2019 + const List<objectMap>& cellsFromPoints = map.cellsFromPointsMap(); + + forAll (cellsFromPoints, cfpI) + { + adjustRefLevel + ( + newCellLevel[cellsFromPoints[cfpI].index()], + cellsFromPoints[cfpI].masterObjects()[0] + ); + } + + // Transfer the new cell level into the data member + cellLevel_.transfer(newCellLevel); + + // Clear out refinementLevelIndicator_ field for next refinement step + refinementLevelIndicator_.clear(); + + + // Point level will be updated based on already updated cell level. Level + // for newly added points has to be equal to the maximum cell level of + // surrounding points. At this point, mesh is fully topologically valid so + // it is safe to use pointCells + + // Get point map: from current mesh points to previous mesh points + const labelList& pointMap = map.pointMap(); + + // Get point cell + const labelListList& meshPointCells = mesh_.pointCells(); + + // Create new point level + labelList newPointLevel(pointMap.size()); + + // Loop through all new points + forAll (pointMap, newPointI) + { + // Get index of the corresponding old point + const label& oldPointI = pointMap[newPointI]; + + if (oldPointI == -1) + { + // This point has been appended without any master point, use + // surrounding cells to determine new point level + + // Get new cells surrounding this new point + const labelList& pCells = meshPointCells[newPointI]; + + // Find maximum cell level for this point + label maxCellLevel = 0; + forAll (pCells, i) + { + maxCellLevel = max(maxCellLevel, cellLevel_[pCells[i]]); + } + + // Set new point level as the maximum of the surrounding cells + newPointLevel[newPointI] = maxCellLevel; + } + else + { + // This point is either old point or it has been added in terms of + // another point. New point level is equal to the old point level + newPointLevel[newPointI] = pointLevel_[oldPointI]; + } + } + + // Sync the new point level. Note: here, we assume that the call to + // updateMesh happened after polyBoundaryMesh::updateMesh where the + // processor data is fully rebuilt. If this is not the case, the point + // level will remain unsynced and will cause all kinds of trouble that + // are extremely difficult to spot. See the change in + // polyTopoChanger::changeMesh order of calling polyMesh::updateMesh and + // polyTopoChanger::update. VV, 19/Feb/2019. + syncTools::syncPointList + ( + mesh_, + newPointLevel, + maxEqOp<label>(), + label(0) // Null value + ); + + // Transfer the new point level into the data member + pointLevel_.transfer(newPointLevel); + + // Mark files as changed + setInstance(mesh_.facesInstance()); + + // Update face remover + faceRemover_.updateMesh(map); +} + + +void Foam::refinement::write(Ostream& os) const +{ + os << nl << type() << nl + << name() << nl + << maxCells_ << nl + << maxRefinementLevel_ << nl + << edgeBasedConsistency_ << nl + << nRefinementBufferLayers_ << nl + << nUnrefinementBufferLayers_ << endl; +} + + +void Foam::refinement::writeDict(Ostream& os) const +{ + // Write necessary data before writing dictionary + cellLevel_.write(); + pointLevel_.write(); + + os << nl << name() << nl << token::BEGIN_BLOCK << nl + << " type " << type() + << token::END_STATEMENT << nl + << " maxCells " << maxCells_ + << token::END_STATEMENT << nl + << " maxRefinementLevel " << maxRefinementLevel_ + << token::END_STATEMENT << nl + << " edgeBasedConsistency " << edgeBasedConsistency_ + << token::END_STATEMENT << nl + << " nRefinementBufferLayers " << nRefinementBufferLayers_ + << token::END_STATEMENT << nl + << " nUnrefinementBufferLayers " << nUnrefinementBufferLayers_ + << token::END_STATEMENT << nl + << " active " << active() + << token::END_STATEMENT << nl + << token::END_BLOCK << endl; +} + + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/refinement/refinement.H b/src/dynamicMesh/refinement/refinement/refinement.H new file mode 100644 index 0000000000000000000000000000000000000000..a9754c5c851dc6bf94e4c436f7e929cdcb1cea98 --- /dev/null +++ b/src/dynamicMesh/refinement/refinement/refinement.H @@ -0,0 +1,392 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Class + Foam::refinement + +Description + Abstract base class for adaptive mesh refinement using the mesh modifier + engine. The class provides common interface and functionalities for 3D + polyhedral refinement and 2D prismatic refinement. + + The common interface includes (pure virtuals) following member functions: + - setCellsToRefine + - setSplitPointsToUnrefine + + With a lot of ordinary protected member functions which are used by both + derived classes. + + Note: I've written it this way in order to avoid unnecesasry code + duplication, but I'm 99% sure that if someone else wants to write additional + refinement strategy (e.g. directional refinement) as derived class, the + interface will need to change. + +SourceFiles + refinement.C + +Author + Vuko Vukcevic, Wikki Ltd. All rights reserved. + +Notes + Generalisation of hexRef8 for polyhedral cells and refactorisation using + polyMesh modifier engine. + +\*---------------------------------------------------------------------------*/ + +#ifndef refinement_H +#define refinement_H + +#include "polyMeshModifier.H" +#include "labelIOField.H" +#include "removeFaces.H" +#include "batchPolyTopoChange.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class refinement Declaration +\*---------------------------------------------------------------------------*/ + +class refinement +: + public polyMeshModifier +{ +protected: + + // Protected enumeration for refinement status + enum refinementStatus + { + UNREFINED = -1, + UNCHANGED = 0, + REFINED = 1 + }; + + static const scalar nonOrthThreshold_; + + // Protected data + + //- Reference to polyMesh for easy access in helper functions + const polyMesh& mesh_; + + + // Refinement control and handling + + //- List of cells to refine in this time step + mutable labelList cellsToRefine_; + + //- List of split point labels to unrefine in this time step + mutable labelList splitPointsToUnrefine_; + + //- Cell refinement level + mutable labelIOField cellLevel_; + + //- Point refinement level + mutable labelIOField pointLevel_; + + //- Helper list for original (old) cells that will be refined or + // unrefined. The list is updated in setPolyhedralRefinement and + // setPolyhedralUnfereinement and is used in updateMesh to update + // the cellLevel on the new mesh. + // Values stored in the list are: + // a) UNREFINED = -1 = cell is unrefined + // b) UNCHANGED = 0 = cell is untouched + // c) REFINED = +1 = cell is refined + mutable labelList refinementLevelIndicator_; + + //- Face remover engine + mutable removeFaces faceRemover_; + + //- Maximum number of cells in the mesh. Note: not strictly enforced + label maxCells_; + + //- Maximum number of refinement levels for a given cell + label maxRefinementLevel_; + + //- Switch whether to use edge based consistency on refinement + Switch edgeBasedConsistency_; + + //- Number of buffer layers for refinement + label nRefinementBufferLayers_; + + //- Number of buffer layers for unrefinement, controlling how far + // the unrefinement region needs to be from current refinement + // region + label nUnrefinementBufferLayers_; + + + // Protected Pure Virtual Member Functions + + // Global topology modification functions (operate on whole polyMesh) + + //- Set refinement instruction + virtual void setRefinementInstruction + ( + batchPolyTopoChange& ref + ) const = 0; + + //- Set unrefinement instruction + virtual void setUnrefinementInstruction + ( + batchPolyTopoChange& ref + ) const = 0; + + + // Protected Member Functions + + // Useful helper functions used by derived classes + + //- Set file instance for cellLevel_ and pointLevel_ + void setInstance(const fileName& inst) const; + + + // Local topology modification functions (operate on cells/faces) + + //- Adds a face on top of existing faceI. Reverses if nessecary + label addFace + ( + batchPolyTopoChange& ref, + const label faceI, + const face& newFace, + const label own, + const label nei + ) const; + + //- Adds internal face from point. No checks on reversal + label addInternalFace + ( + batchPolyTopoChange& ref, + const label meshFaceI, + const label meshPointI, + const face& newFace, + const label own, + const label nei + ) const; + + //- Modifies existing faceI for either new owner/neighbour or new + // face points. Reverses if nessecary + void modifyFace + ( + batchPolyTopoChange& ref, + const label faceI, + const face& newFace, + const label own, + const label nei + ) const; + + + // Topological change helper functions + + //- Store vertices from startFp up to face split point. + // Used when splitting face into n faces where n is the number of + // points in a face (or number of edges) + void walkFaceToMid + ( + const labelList& edgeMidPoint, + const label cLevel, + const label faceI, + const label startFp, + DynamicList<label>& faceVerts + ) const; + + //- Same as walkFaceToMid but now walk back + void walkFaceFromMid + ( + const labelList& edgeMidPoint, + const label cLevel, + const label faceI, + const label startFp, + DynamicList<label>& faceVerts + ) const; + + //- Get index of point with minimum point level + label findMinLevel(const labelList& f) const; + + //- Get index of point with maximum point level + label findMaxLevel(const labelList& f) const; + + //- Count number of vertices <= anchorLevel for a given face + label countAnchors + ( + const labelList& f, + const label anchorLevel + ) const; + + //- Adjust cell refinement level after topo change + void adjustRefLevel + ( + label& curNewCellLevel, + const label oldCellI + ); + + + // Debug functions + + //- Check orientation of added internal face + void checkInternalOrientation + ( + batchPolyTopoChange& ref, + const label cellI, + const label faceI, + const point& ownPt, + const point& neiPt, + const face& newFace + ) const; + + //- Check orientation of a new boundary face + void checkBoundaryOrientation + ( + batchPolyTopoChange& ref, + const label cellI, + const label faceI, + const point& ownPt, + const point& boundaryPt, + const face& newFace + ) const; + + + // Refinement/unrefinement consistency checks + + //- Updates cellsToRefine such that a face consistent 2:1 refinement + // is obtained. Returns local number of cells changed + label faceConsistentRefinement(boolList& cellsToRefine) const; + + //- Updates cellsToRefine such that an edge consistent 4:1 refinement + // is obtained. Returns local number of cells changed + label edgeConsistentRefinement(boolList& cellsToRefine) const; + + //- Updates cellsToUnrefine such that a face consistent 2:1 + // unrefinement is obtained. Returns local number of cells changed + label faceConsistentUnrefinement(boolList& cellsToUnrefine) const; + + //- Updates cellsToUnrefine such that an edge consistent 4:1 + // unrefinement is obtained. Returns local number of cells changed + label edgeConsistentUnrefinement(boolList& cellsToUnrefine) const; + + + // Copy control + + //- Disallow default bitwise copy construct + refinement(const refinement&); + + //- Disallow default bitwise assignment + void operator=(const refinement&); + + +public: + + //- Runtime type information + TypeName("refinement"); + + + // Constructors + + //- Construct from dictionary + refinement + ( + const word& name, + const dictionary& dict, + const label index, + const polyTopoChanger& mme + ); + + + //- Destructor + virtual ~refinement() = default; + + + // Member Functions + + // Access + + //- Return refinement cell level + const labelIOField& cellLevel() const + { + return cellLevel_; + } + + //- Return refinement point level + const labelIOField& pointLevel() const + { + return pointLevel_; + } + + + // Edit + + //- Set cells to refine given a list of refinement + // candidates. Refinement candidates are extended within the + // function due to possible 4:1 conflicts and specified number of + // buffer layers. + // Note: must be called BEFORE setSplitPointsToUnrefine + virtual void setCellsToRefine + ( + const labelList& refinementCellCandidates + ) = 0; + + //- Set split points to unrefine given a list of all mesh points + // that are candidates for unrefinement. Split points are + // determined as a subset of unrefinement candidates, avoiding + // splitting points of cells that are going to be refined at the + // same time and ensuring consistent unrefinement. + // Note: must be called AFTER setCellsToRefine + virtual void setSplitPointsToUnrefine + ( + const labelList& unrefinementPointCandidates + ) = 0; + + + // Inherited interface from polyMeshModifier + + //- Check for topology change + virtual bool changeTopology() const; + + //- Insert the polyhedral refinement/unrefinement into the + // topological change + virtual void setRefinement(batchPolyTopoChange&) const; + + //- Modify motion points to comply with the topological change + virtual void modifyMotionPoints(pointField& motionPoints) const; + + //- Force recalculation of locally stored data on topological change + virtual void updateMesh(const mapPolyMesh&); + + //- Write + virtual void write(Ostream&) const; + + //- Write dictionary + virtual void writeDict(Ostream&) const; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/dynamicMesh/refinement/refinement/refinementTools.H b/src/dynamicMesh/refinement/refinement/refinementTools.H new file mode 100644 index 0000000000000000000000000000000000000000..00f0a112e2a495da17117043e6a2f9e2995a0dee --- /dev/null +++ b/src/dynamicMesh/refinement/refinement/refinementTools.H @@ -0,0 +1,351 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Namespace + Foam::refinementTools + +Description + Collection of static functions to do various simple mesh related things. + +SourceFiles + refinementTools.C + +\*---------------------------------------------------------------------------*/ + +#ifndef refinementTools_H +#define refinementTools_H + +#include "vector.H" +#include "label.H" +#include "labelList.H" +#include "pointField.H" +#include "faceList.H" +#include "cellList.H" +#include "primitivePatch.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +class primitiveMesh; +class polyMesh; + +/*---------------------------------------------------------------------------*\ + Namespace refinementTools Declaration +\*---------------------------------------------------------------------------*/ + +namespace refinementTools +{ + // Bit identifiers for octants (p=plus, m=min e.g. plusXminYminZ) + + static const label mXmYmZ = 0; + static const label pXmYmZ = 1; + static const label mXpYmZ = 2; + static const label pXpYmZ = 3; + + static const label mXmYpZ = 4; + static const label pXmYpZ = 5; + static const label mXpYpZ = 6; + static const label pXpYpZ = 7; + + static const label mXmYmZMask = 1 << mXmYmZ; + static const label pXmYmZMask = 1 << pXmYmZ; + static const label mXpYmZMask = 1 << mXpYmZ; + static const label pXpYmZMask = 1 << pXpYmZ; + + static const label mXmYpZMask = 1 << mXmYpZ; + static const label pXmYpZMask = 1 << pXmYpZ; + static const label mXpYpZMask = 1 << mXpYpZ; + static const label pXpYpZMask = 1 << pXpYpZ; + + + // Normal handling + + //- Check if n is in same direction as normals of all faceLabels + bool visNormal + ( + const vector& n, + const vectorField& faceNormals, + const labelList& faceLabels + ); + + //- Calculate point normals on a 'box' mesh (all edges aligned with + // coordinate axes) + vectorField calcBoxPointNormals(const primitivePatch& pp); + + //- Normalized edge vector + vector normEdgeVec(const primitiveMesh&, const label edgeI); + + + // OBJ writing + + //- Write obj representation of point + void writeOBJ + ( + Ostream& os, + const point& pt + ); + + //- Write obj representation of faces subset + void writeOBJ + ( + Ostream& os, + const faceList&, + const pointField&, + const labelList& faceLabels + ); + + //- Write obj representation of faces + void writeOBJ + ( + Ostream& os, + const faceList&, + const pointField& + ); + + //- Write obj representation of cell subset + void writeOBJ + ( + Ostream& os, + const cellList&, + const faceList&, + const pointField&, + const labelList& cellLabels + ); + + + // Cell/face/edge walking + + //- Is edge used by cell + bool edgeOnCell + ( + const primitiveMesh&, + const label cellI, + const label edgeI + ); + + //- Is edge used by face + bool edgeOnFace + ( + const primitiveMesh&, + const label faceI, + const label edgeI + ); + + //- Is face used by cell + bool faceOnCell + ( + const primitiveMesh&, + const label cellI, + const label faceI + ); + + //- Return edge among candidates that uses the two vertices. + label findEdge + ( + const edgeList& edges, + const labelList& candidates, + const label v0, + const label v1 + ); + + //- Return edge between two vertices. Returns -1 if no edge. + label findEdge + ( + const primitiveMesh&, + const label v0, + const label v1 + ); + + //- Return edge shared by two faces. Throws error if no edge found. + label getSharedEdge + ( + const primitiveMesh&, + const label f0, + const label f1 + ); + + //- Return face shared by two cells. Throws error if none found. + label getSharedFace + ( + const primitiveMesh&, + const label cell0, + const label cell1 + ); + + //- Get faces on cell using edgeI. Throws error if no two found. + void getEdgeFaces + ( + const primitiveMesh&, + const label cellI, + const label edgeI, + label& face0, + label& face1 + ); + + //- Return label of other edge (out of candidates edgeLabels) + // connected to vertex but not edgeI. Throws error if none found. + label otherEdge + ( + const primitiveMesh&, + const labelList& edgeLabels, + const label edgeI, + const label vertI + ); + + //- Return face on cell using edgeI but not faceI. Throws error + // if none found. + label otherFace + ( + const primitiveMesh&, + const label cellI, + const label faceI, + const label edgeI + ); + + //- Return cell on other side of face. Throws error + // if face not internal. + label otherCell + ( + const primitiveMesh&, + const label cellI, + const label faceI + ); + + //- Returns label of edge nEdges away from startEdge (in the direction + // of startVertI) + label walkFace + ( + const primitiveMesh&, + const label faceI, + const label startEdgeI, + const label startVertI, + const label nEdges + ); + + + // Constraints on position + + //- Set the constrained components of position to mesh centre + void constrainToMeshCentre + ( + const polyMesh& mesh, + point& pt + ); + void constrainToMeshCentre + ( + const polyMesh& mesh, + pointField& pt + ); + + //- Set the constrained components of directions/velocity to zero + void constrainDirection + ( + const polyMesh& mesh, + const Vector<label>& dirs, + vector& d + ); + void constrainDirection + ( + const polyMesh& mesh, + const Vector<label>& dirs, + vectorField& d + ); + + + // Hex only functionality + + //- Given edge on hex find other 'parallel', non-connected edges. + void getParallelEdges + ( + const primitiveMesh&, + const label cellI, + const label e0, + label&, + label&, + label& + ); + + //- Given edge on hex find all 'parallel' (i.e. non-connected) + // edges and average direction of them + vector edgeToCutDir + ( + const primitiveMesh&, + const label cellI, + const label edgeI + ); + + //- Reverse of edgeToCutDir: given direction find edge bundle and + // return one of them. + label cutDirToEdge + ( + const primitiveMesh&, + const label cellI, + const vector& cutDir + ); + + + // Face information + + //- Set face information: patch, zone and zone flip for a face + void setFaceInfo + ( + const polyMesh& mesh, + const label faceI, + label& patchID, + label& zoneID, + label& zoneFlip + ); + + + // Mark-up of mesh bits. Relocated from refinement polyMeshModifier + + //- Extend marked cells across faces given a bool list of already marked + // cells + void extendMarkedCellsAcrossFaces + ( + const polyMesh& mesh, + boolList& markedCell + ); + + //- Extend marked cells across points given a bool list of already + // marked cells + void extendMarkedCellsAcrossPoints + ( + const polyMesh& mesh, + boolList& markedCell + ); + +} // End namespace refinementTools + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/dynamicMesh/setUpdater/setUpdater.C b/src/dynamicMesh/setUpdater/setUpdater.C index 5c93427521ca674c4498a9330a9ba4fb06678338..19ffc8db45ac94ce8dec5b099cbad352c300d6e8 100644 --- a/src/dynamicMesh/setUpdater/setUpdater.C +++ b/src/dynamicMesh/setUpdater/setUpdater.C @@ -27,7 +27,7 @@ License #include "setUpdater.H" #include "polyTopoChanger.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "addToRunTimeSelectionTable.H" #include "mapPolyMesh.H" #include "cellSet.H" @@ -71,7 +71,7 @@ bool Foam::setUpdater::changeTopology() const } -void Foam::setUpdater::setRefinement(polyTopoChange&) const +void Foam::setUpdater::setRefinement(batchPolyTopoChange&) const {} diff --git a/src/dynamicMesh/setUpdater/setUpdater.H b/src/dynamicMesh/setUpdater/setUpdater.H index 3fd01c7909bf573d49a8b8311b3a06f58bd4bccd..3106403ab6b03fc45536c9c6167850bfb82c1ce0 100644 --- a/src/dynamicMesh/setUpdater/setUpdater.H +++ b/src/dynamicMesh/setUpdater/setUpdater.H @@ -97,7 +97,7 @@ public: //- Insert the layer addition/removal instructions // into the topological change - virtual void setRefinement(polyTopoChange&) const; + virtual void setRefinement(batchPolyTopoChange&) const; //- Modify motion points to comply with the topological change virtual void modifyMotionPoints(pointField& motionPoints) const; diff --git a/src/dynamicMesh/slidingInterface/coupleSlidingInterface.C b/src/dynamicMesh/slidingInterface/coupleSlidingInterface.C index 40ca37ea7ec1cdff61e16a9bd68ef1cbfcb941eb..5274c2705b71b7d464c06f09f735e3993768b92a 100644 --- a/src/dynamicMesh/slidingInterface/coupleSlidingInterface.C +++ b/src/dynamicMesh/slidingInterface/coupleSlidingInterface.C @@ -27,7 +27,7 @@ License \*---------------------------------------------------------------------------*/ #include "slidingInterface.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyMesh.H" #include "primitiveMesh.H" #include "enrichedPatch.H" @@ -69,7 +69,7 @@ const Foam::scalar Foam::slidingInterface::edgeCoPlanarTolDefault_ = 0.8; // - - missed master edge in cut // u - edge already used in cutting -void Foam::slidingInterface::coupleInterface(polyTopoChange& ref) const +void Foam::slidingInterface::coupleInterface(batchPolyTopoChange& ref) const { if (debug) { diff --git a/src/dynamicMesh/slidingInterface/decoupleSlidingInterface.C b/src/dynamicMesh/slidingInterface/decoupleSlidingInterface.C index 64fe0f573cbb304080882cf0778e5c7c1d71c958..0c22ba2f244e3c3a7015f8b50da8d02db0cd71e8 100644 --- a/src/dynamicMesh/slidingInterface/decoupleSlidingInterface.C +++ b/src/dynamicMesh/slidingInterface/decoupleSlidingInterface.C @@ -29,7 +29,7 @@ License #include "slidingInterface.H" #include "polyMesh.H" #include "primitiveMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyTopoChanger.H" #include "polyModifyFace.H" #include "polyModifyPoint.H" @@ -38,7 +38,7 @@ License void Foam::slidingInterface::decoupleInterface ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { if (debug) diff --git a/src/dynamicMesh/slidingInterface/slidingInterface.C b/src/dynamicMesh/slidingInterface/slidingInterface.C index cf25565ba1f5fa4e49f3331fa0364eb95af130b6..e41eeef90344dae2cd3d8d525129294d122bb8f8 100644 --- a/src/dynamicMesh/slidingInterface/slidingInterface.C +++ b/src/dynamicMesh/slidingInterface/slidingInterface.C @@ -29,7 +29,7 @@ License #include "slidingInterface.H" #include "polyTopoChanger.H" #include "polyMesh.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "addToRunTimeSelectionTable.H" #include "plane.H" @@ -367,7 +367,7 @@ bool Foam::slidingInterface::changeTopology() const } -void Foam::slidingInterface::setRefinement(polyTopoChange& ref) const +void Foam::slidingInterface::setRefinement(batchPolyTopoChange& ref) const { if (coupleDecouple_) { diff --git a/src/dynamicMesh/slidingInterface/slidingInterface.H b/src/dynamicMesh/slidingInterface/slidingInterface.H index f3ac02bf527aa4cf9f4a15c9de41f639899e0d77..d5742cc904ff2a2deeca82a1556944001b1036ed 100644 --- a/src/dynamicMesh/slidingInterface/slidingInterface.H +++ b/src/dynamicMesh/slidingInterface/slidingInterface.H @@ -264,19 +264,19 @@ private: bool projectPoints() const; //- Couple sliding interface - void coupleInterface(polyTopoChange& ref) const; + void coupleInterface(batchPolyTopoChange& ref) const; //- Clear projection void clearPointProjection() const; //- Clear old couple - void clearCouple(polyTopoChange& ref) const; + void clearCouple(batchPolyTopoChange& ref) const; //- Decouple interface (returns it to decoupled state) // Note: this should not be used in normal operation of the // sliding mesh, but only to return the mesh to its // original state - void decoupleInterface(polyTopoChange& ref) const; + void decoupleInterface(batchPolyTopoChange& ref) const; // Static data members @@ -364,7 +364,7 @@ public: //- Insert the layer addition/removal instructions // into the topological change - virtual void setRefinement(polyTopoChange&) const; + virtual void setRefinement(batchPolyTopoChange&) const; //- Modify motion points to comply with the topological change virtual void modifyMotionPoints(pointField& motionPoints) const; diff --git a/src/dynamicMesh/slidingInterface/slidingInterfaceClearCouple.C b/src/dynamicMesh/slidingInterface/slidingInterfaceClearCouple.C index 8d5318dce950f2d949c8da2c77aa1b2aecae2a09..bafd41ae652d1b746fa42abf4d051d98cb5a646c 100644 --- a/src/dynamicMesh/slidingInterface/slidingInterfaceClearCouple.C +++ b/src/dynamicMesh/slidingInterface/slidingInterfaceClearCouple.C @@ -27,7 +27,7 @@ License \*---------------------------------------------------------------------------*/ #include "slidingInterface.H" -#include "polyTopoChange.H" +#include "batchPolyTopoChange.H" #include "polyMesh.H" #include "polyTopoChanger.H" #include "polyRemovePoint.H" @@ -37,7 +37,7 @@ License void Foam::slidingInterface::clearCouple ( - polyTopoChange& ref + batchPolyTopoChange& ref ) const { if (debug) diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files index 4a571b5fc28034d5f804073cc2099b7d1a5713a2..d7125f4b4063ff38efa04fe1509bc4c635cb9f08 100644 --- a/src/meshTools/Make/files +++ b/src/meshTools/Make/files @@ -325,6 +325,8 @@ mappedPatches/mappedPolyPatch/mappedVariableThicknessWallPolyPatch.C mappedPatches/mappedPointPatch/mappedPointPatch.C mappedPatches/mappedPointPatch/mappedWallPointPatch.C +batchPolyTopoChange/batchPolyTopoChange.C + polyTopoChange/topoAction/topoActions.C polyTopoChange/polyTopoChange.C diff --git a/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.C b/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.C new file mode 100644 index 0000000000000000000000000000000000000000..3a53b6942016cf5c533891ae61cd036cfd9be2b1 --- /dev/null +++ b/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.C @@ -0,0 +1,417 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + +#include "batchPolyTopoChange.H" +#include "polyMesh.H" +#include "primitiveMesh.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +const Foam::label Foam::batchPolyTopoChange::minListSize = 100; +const Foam::label Foam::batchPolyTopoChange::pointFraction = 10; +const Foam::label Foam::batchPolyTopoChange::faceFraction = 10; +const Foam::label Foam::batchPolyTopoChange::cellFraction = 10; + +namespace Foam +{ + defineTypeNameAndDebug(batchPolyTopoChange, 0); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +// Construct from mesh reference +Foam::batchPolyTopoChange::batchPolyTopoChange(const polyMesh& mesh) +: + mesh_(mesh), + addedPoints_(minListSize), + modifiedPoints_(minListSize), + removedPoints_ + ( + Foam::max(mesh.points().size()/pointFraction, minListSize) + ), + addedFaces_(minListSize), + modifiedFaces_(minListSize), + removedFaces_(Foam::max(mesh.faces().size()/faceFraction, minListSize)), + addedCells_(minListSize), + removedCells_(Foam::max(mesh.nCells()/cellFraction, minListSize)) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::label Foam::batchPolyTopoChange::setAction(const topoAction& action) +{ + if (isType<polyAddPoint>(action)) + { + addedPoints_.append(refCast<const polyAddPoint>(action)); + + return mesh_.points().size() + addedPoints_.size() - 1; + } + else if (isType<polyModifyPoint>(action)) + { + const polyModifyPoint& pmp = refCast<const polyModifyPoint>(action); + + if (debug) + { + if (removedPoints_.find(pmp.pointID()) != removedPoints_.end()) + { + FatalErrorInFunction + << "Modifying point " << pmp.pointID() + << " for the second time or modifying a removed point. " + << "This is not allowed." + << abort(FatalError); + } + } + + modifiedPoints_.append(pmp); + removedPoints_.insert(pmp.pointID()); + + return -1; + } + else if (isType<polyRemovePoint>(action)) + { + const polyRemovePoint& prp = refCast<const polyRemovePoint>(action); + + if (debug) + { + if (removedPoints_.find(prp.pointID()) != removedPoints_.end()) + { + FatalErrorInFunction + << "Removing point " << prp.pointID() + << " for the second time or removing a modified point. " + << "This is not allowed." + << abort(FatalError); + } + } + + removedPoints_.insert(prp.pointID()); + + return -1; + } + else if (isType<polyAddFace>(action)) + { + addedFaces_.append(refCast<const polyAddFace>(action)); + + return mesh_.faces().size() + addedFaces_.size() - 1; + } + else if (isType<polyModifyFace>(action)) + { + const polyModifyFace& pmf = refCast<const polyModifyFace>(action); + + if (debug) + { + if (removedFaces_.find(pmf.faceID()) != removedFaces_.end()) + { + FatalErrorInFunction + << "Modifying face " << pmf.faceID() + << " for the second time or modifying a removed face. " + << "This is not allowed." + << abort(FatalError); + } + } + + modifiedFaces_.append(pmf); + removedFaces_.insert(pmf.faceID()); + + return -1; + } + else if (isType<polyRemoveFace>(action)) + { + const polyRemoveFace& prf = refCast<const polyRemoveFace>(action); + + if (debug) + { + if (removedFaces_.find(prf.faceID()) != removedFaces_.end()) + { + FatalErrorInFunction + << "Removing face " << prf.faceID() + << " for the second time or removing a modified face. " + << "This is not allowed." + << abort(FatalError); + } + } + + removedFaces_.insert(prf.faceID()); + + return -1; + } + else if (isType<polyAddCell>(action)) + { + addedCells_.append(refCast<const polyAddCell>(action)); + + return mesh_.nCells() + addedCells_.size() - 1; + } + else if (isType<polyModifyCell>(action)) + { + const polyModifyCell& pmc = refCast<const polyModifyCell>(action); + + modifiedCells_.append(pmc); + + return -1; + } + else if (isType<polyRemoveCell>(action)) + { + const polyRemoveCell& prc = refCast<const polyRemoveCell>(action); + + if (debug) + { + if (removedCells_.find(prc.cellID()) != removedCells_.end()) + { + FatalErrorInFunction + << "Removing cell " << prc.cellID() + << " for the second time. This is not allowed." + << abort(FatalError); + } + } + + removedCells_.insert(prc.cellID()); + + return -1; + } + else + { + FatalErrorInFunction + << "Unknown type of topoChange: " << action.type() + << abort(FatalError); + + // Dummy return to keep compiler happy + return -1; + } +} + + +bool Foam::batchPolyTopoChange::check() const +{ + if (debug) + { + Info<< "Points: added = " << addedPoints_.size() + << " modified = " << modifiedPoints_.size() + << " removed = " << removedPoints_.size() - modifiedPoints_.size() + << " balance = " << pointBalance() + << endl; + + Info<< "Faces: added = " << addedFaces_.size() + << " modified = " << modifiedFaces_.size() + << " removed = " << removedFaces_.size() - modifiedFaces_.size() + << " balance = " << faceBalance() + << endl; + + Info<< "Cells: added = " << addedCells_.size() + << " modified = " << modifiedCells_.size() + << " removed = " << removedCells_.size() - modifiedCells_.size() + << " balance = " << cellBalance() + << endl; + } + + label nErrors = 0; + + // Check points + + if (mesh_.points().size() + pointBalance() < 4) + { + WarningInFunction + << "Less than 4 points in the mesh. This cannot be a valid mesh." + << endl; + + nErrors++; + } + + // Collect zone-only and removed points + labelHashSet zoneOnlyPoints(removedPoints_); + + forAll (modifiedPoints_, mpI) + { + if (modifiedPoints_[mpI].inCell()) + { + // Point is live; remove from lookup + zoneOnlyPoints.erase + ( + zoneOnlyPoints.find(modifiedPoints_[mpI].pointID()) + ); + } + } + + forAll (addedPoints_, apI) + { + if (!addedPoints_[apI].inCell()) + { + zoneOnlyPoints.insert(mesh_.points().size() + apI); + } + } + + // Check faces + + label nFaceErrors = 0; + label nCurFaceError = 0; + + // For all live modified and added faces, check that points are also live + + // Collect zone-only faces + labelHashSet zoneOnlyFaces(removedFaces_); + + // Modified faces + forAll (modifiedFaces_, mfI) + { + if (!modifiedFaces_[mfI].onlyInZone()) + { + // Face is live; remove from lookup + zoneOnlyFaces.erase + ( + zoneOnlyFaces.find(modifiedFaces_[mfI].faceID()) + ); + + // Count errors in current face + nCurFaceError = 0; + + const face& curFace = modifiedFaces_[mfI].newFace(); + + forAll (curFace, pointI) + { + if (zoneOnlyPoints.found(curFace[pointI])) + { + nCurFaceError++; + } + } + + if (nCurFaceError > 0) + { + if (debug) + { + Info<< "Modified face no " << mfI << " is active but uses " + << nCurFaceError << " zone-only or removed points. " + << "Face: " << curFace << " Problem points:"; + + forAll (curFace, pointI) + { + if (zoneOnlyPoints.found(curFace[pointI])) + { + Info << " " << curFace[pointI]; + } + } + + Info << endl; + } + + nFaceErrors++; + } + } + } + + // Added faces + forAll (addedFaces_, afI) + { + if (addedFaces_[afI].onlyInZone()) + { + // Zone-only face; add to hash set + zoneOnlyFaces.insert(mesh_.faces().size() + afI); + } + else + { + // Count errors in current face + nCurFaceError = 0; + + const face& curFace = addedFaces_[afI].newFace(); + + forAll (curFace, pointI) + { + if (zoneOnlyPoints.found(curFace[pointI])) + { + nCurFaceError++; + } + } + + if (nCurFaceError > 0) + { + if (debug) + { + Info<< "Added face no " << afI << " is active but uses " + << nCurFaceError << " zone-only or removed points. " + << "Face: " << curFace << " Problem points:"; + + forAll (curFace, pointI) + { + if (zoneOnlyPoints.found(curFace[pointI])) + { + Info << " " << curFace[pointI]; + } + } + + Info << endl; + } + + nFaceErrors++; + } + } + } + + // Check cells? + + if (nErrors == 0) + { + if (debug) + { + Info << "batchPolyTopoChange OK" << endl; + } + + return false; + } + else + { + if (debug) + { + Info<< "bool batchPolyTopoChange::check() const : " + << "Detected " << nErrors << " errors." + << endl; + } + + return true; + } +} + + +void Foam::batchPolyTopoChange::clear() +{ + if (debug) + { + Info<< "void Foam::batchPolyTopoChange::clear() : " + << "clearing topological change request" << endl; + } + + // Clear all contents + addedPoints_.clear(); + modifiedPoints_.clear(); + removedPoints_.clear(); + + addedFaces_.clear(); + modifiedFaces_.clear(); + removedFaces_.clear(); + + addedCells_.clear(); + removedCells_.clear(); +} + + +// ************************************************************************* // diff --git a/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.H b/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.H new file mode 100644 index 0000000000000000000000000000000000000000..ca8876b6a0b14762e9a28fdadeed3331469287ae --- /dev/null +++ b/src/meshTools/batchPolyTopoChange/batchPolyTopoChange.H @@ -0,0 +1,256 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +Class + batchPolyTopoChange + +Description + Class accumulates information on how to perform mesh refinement. + Once the refinement request is completed, polyMesh modifies the mesh + topology. + +SourceFiles + batchPolyTopoChange.C + +\*---------------------------------------------------------------------------*/ + +#ifndef batchPolyTopoChange_H +#define batchPolyTopoChange_H + +#include "DynamicList.H" +#include "HashSet.H" +#include "polyAddPoint.H" +#include "polyModifyPoint.H" +#include "polyRemovePoint.H" +#include "polyAddFace.H" +#include "polyModifyFace.H" +#include "polyRemoveFace.H" +#include "polyAddCell.H" +#include "polyModifyCell.H" +#include "polyRemoveCell.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +class polyMesh; + +/*---------------------------------------------------------------------------*\ + Class batchPolyTopoChange Declaration +\*---------------------------------------------------------------------------*/ + +class batchPolyTopoChange +{ + // Private data + + //- Reference to mesh to be refined + const polyMesh& mesh_; + + //- Points to add + DynamicList<polyAddPoint> addedPoints_; + + //- Points to modify + DynamicList<polyModifyPoint> modifiedPoints_; + + //- Points to remove + labelHashSet removedPoints_; + + //- Faces to add + DynamicList<polyAddFace> addedFaces_; + + //- Faces to modify + DynamicList<polyModifyFace> modifiedFaces_; + + //- Faces to remove + labelHashSet removedFaces_; + + //- Number of cells to add + DynamicList<polyAddCell> addedCells_; + + //- Cells to modify + DynamicList<polyModifyCell> modifiedCells_; + + //- Cells to remove + labelHashSet removedCells_; + + + // Private Member Functions + + //- Disallow default bitwise copy construct + batchPolyTopoChange(const batchPolyTopoChange&) = delete; + + //- Disallow default bitwise assignment + void operator=(const batchPolyTopoChange&) = delete; + + + // Private static data + + //- Minimum dynamic list size for object insertion + static const label minListSize; + + //- Estimated fraction of removed points + static const label pointFraction; + + //- Estimated fraction of removed faces + static const label faceFraction; + + //- Estimated fraction of removed cells + static const label cellFraction; + + +public: + + //- Runtime type information + ClassName("batchPolyTopoChange"); + + + // Constructors + + //- Construct from mesh reference + batchPolyTopoChange(const polyMesh&); + + + //- Destructor + ~batchPolyTopoChange() = default; + + + // Member Functions + + // Definition of topological change + + //- Set topological action + label setAction(const topoAction& action); + + + // Topology morph data + + //- Is point removed? + inline bool pointRemoved(const label pointI) const; + + //- Is face removed? + inline bool faceRemoved(const label faceI) const; + + //- Is cell removed? + inline bool cellRemoved(const label cellI) const; + + //- Point balance (added - removed) + label pointBalance() const + { + return + addedPoints_.size() + + modifiedPoints_.size() + - removedPoints_.size(); + } + + //- Face balance (added - removed) + label faceBalance() const + { + return + addedFaces_.size() + + modifiedFaces_.size() + - removedFaces_.size(); + } + + //- Cell balance (added - removed) + label cellBalance() const + { + return addedCells_.size() - removedCells_.size(); + } + + //- Added points + const DynamicList<polyAddPoint>& addedPoints() const + { + return addedPoints_; + } + + //- Modified points + const DynamicList<polyModifyPoint>& modifiedPoints() const + { + return modifiedPoints_; + } + + //- Map of removed points + const labelHashSet& removedPoints() const + { + return removedPoints_; + } + + //- Added faces + const DynamicList<polyAddFace>& addedFaces() const + { + return addedFaces_; + } + + //- Modified faces + const DynamicList<polyModifyFace>& modifiedFaces() const + { + return modifiedFaces_; + } + + //- Map of removed faces + const labelHashSet& removedFaces() const + { + return removedFaces_; + } + + //- Added cells + const DynamicList<polyAddCell>& addedCells() const + { + return addedCells_; + } + + //- Modified cells + const DynamicList<polyModifyCell>& modifiedCells() const + { + return modifiedCells_; + } + + //- Map of removed cells + const labelHashSet& removedCells() const + { + return removedCells_; + } + + //- Check for consistency and report status + // Returns false for no error. + bool check() const; + + //- Clear all contents + void clear(); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "batchPolyTopoChangeI.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/meshTools/batchPolyTopoChange/batchPolyTopoChangeI.H b/src/meshTools/batchPolyTopoChange/batchPolyTopoChangeI.H new file mode 100644 index 0000000000000000000000000000000000000000..79c94c025b878968a1c67347386e49ec5598b8fd --- /dev/null +++ b/src/meshTools/batchPolyTopoChange/batchPolyTopoChangeI.H @@ -0,0 +1,47 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | foam-extend: Open Source CFD + \\ / O peration | Version: 5.0 + \\ / A nd | Web: http://www.foam-extend.org + \\/ M anipulation | For copyright notice see file Copyright +------------------------------------------------------------------------------- +License + This file is part of foam-extend. + + foam-extend 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. + + foam-extend 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 foam-extend. If not, see <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +inline bool Foam::batchPolyTopoChange::pointRemoved(const label pointI) const +{ + return removedPoints().found(pointI); +} + + +inline bool Foam::batchPolyTopoChange::faceRemoved(const label faceI) const +{ + return removedFaces().found(faceI); +} + + +inline bool Foam::batchPolyTopoChange::cellRemoved(const label cellI) const +{ + return removedCells().found(cellI); +} + + +// ************************************************************************* // diff --git a/src/meshTools/meshTools/meshTools.C b/src/meshTools/meshTools/meshTools.C index 50ee9d84882449b8fab2bf83dbe8cdb6e6123710..56fd949e34788fad011f26444b9ffa88ff56af86 100644 --- a/src/meshTools/meshTools/meshTools.C +++ b/src/meshTools/meshTools/meshTools.C @@ -30,6 +30,7 @@ License #include "polyMesh.H" #include "hexMatcher.H" #include "treeBoundBox.H" +#include "syncTools.H" // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // @@ -878,4 +879,157 @@ Foam::label Foam::meshTools::cutDirToEdge } +void Foam::meshTools::setFaceInfo +( + const polyMesh& mesh, + const label faceI, + label& patchID, + label& zoneID, + label& zoneFlip +) +{ + patchID = -1; + + if (!mesh.isInternalFace(faceI)) + { + patchID = mesh.boundaryMesh().whichPatch(faceI); + } + + zoneID = mesh.faceZones().whichZone(faceI); + + zoneFlip = false; + + if (zoneID > -1) + { + const faceZone& fZone = mesh.faceZones()[zoneID]; + + zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)]; + } +} + + +void Foam::meshTools::extendMarkedCellsAcrossFaces +( + const polyMesh& mesh, + boolList& markedCell +) +{ + // Mark all faces for all marked cells + const label nFaces = mesh.nFaces(); + boolList markedFace(nFaces, false); + + // Get mesh cells + const cellList& meshCells = mesh.cells(); + + // Loop through all cells + forAll (markedCell, cellI) + { + if (markedCell[cellI]) + { + // This cell is marked, get its faces + const cell& cFaces = meshCells[cellI]; + + forAll (cFaces, i) + { + markedFace[cFaces[i]] = true; + } + } + } + + // Snyc the face list across processor boundaries + syncTools::syncFaceList(mesh, markedFace, orEqOp<bool>());//HJ + + // Get necessary mesh data + const label nInternalFaces = mesh.nInternalFaces(); + const labelList& owner = mesh.faceOwner(); + const labelList& neighbour = mesh.faceNeighbour(); + + // Internal faces + for (label faceI = 0; faceI < nInternalFaces; ++faceI) + { + if (markedFace[faceI]) + { + // Face is marked, mark both owner and neighbour + const label& own = owner[faceI]; + const label& nei = neighbour[faceI]; + + // Mark owner and neighbour cells + markedCell[own] = true; + markedCell[nei] = true; + } + } + + // Boundary faces + for (label faceI = nInternalFaces; faceI < nFaces; ++faceI) + { + if (markedFace[faceI]) + { + // Face is marked, mark owner + const label& own = owner[faceI]; + + // Mark owner + markedCell[own] = true; + } + } +} + + +void Foam::meshTools::extendMarkedCellsAcrossPoints +( + const polyMesh& mesh, + boolList& markedCell +) +{ + // Mark all points for all marked cells + const label nPoints = mesh.nPoints(); + boolList markedPoint(nPoints, false); + + // Get cell points + const labelListList& meshCellPoints = mesh.cellPoints(); + + // Loop through all cells + forAll (markedCell, cellI) + { + if (markedCell[cellI]) + { + // This cell is marked, get its points + const labelList& cPoints = meshCellPoints[cellI]; + + forAll (cPoints, i) + { + markedPoint[cPoints[i]] = true; + } + } + } + + // Snyc point list across processor boundaries + syncTools::syncPointList + ( + mesh, + markedPoint, + orEqOp<bool>(), + true // Default value + // true // Apply separation for parallel cyclics //HJ + ); + + // Get point cells + const labelListList& meshPointCells = mesh.pointCells(); + + // Loop through all points + forAll (markedPoint, pointI) + { + if (markedPoint[pointI]) + { + // This point is marked, mark all of its cells + const labelList& pCells = meshPointCells[pointI]; + + forAll (pCells, i) + { + markedCell[pCells[i]] = true; + } + } + } +} + + // ************************************************************************* // diff --git a/src/meshTools/meshTools/meshTools.H b/src/meshTools/meshTools/meshTools.H index 007d3b519dea26a1dfbcfb6ea0e17ab4c35cd5a4..04303e78c5515b63a143cb2813a9e786d81be229 100644 --- a/src/meshTools/meshTools/meshTools.H +++ b/src/meshTools/meshTools/meshTools.H @@ -352,6 +352,37 @@ namespace meshTools const vector& cutDir ); + + // Face information + + //- Set face information: patch, zone and zone flip for a face + void setFaceInfo + ( + const polyMesh& mesh, + const label faceI, + label& patchID, + label& zoneID, + label& zoneFlip + ); + + + // Mark-up of mesh bits. Relocated from refinement polyMeshModifier + + //- Extend marked cells across faces given a bool list of already marked + // cells + void extendMarkedCellsAcrossFaces + ( + const polyMesh& mesh, + boolList& markedCell + ); + + //- Extend marked cells across points given a bool list of already + // marked cells + void extendMarkedCellsAcrossPoints + ( + const polyMesh& mesh, + boolList& markedCell + ); } // End namespace meshTools diff --git a/src/topoChangerFvMesh/Make/files b/src/topoChangerFvMesh/Make/files index 8a0e7e686a3d9a747732c386884131eae3a67354..a4381293a4498e052698b6e835b1c6185c412920 100644 --- a/src/topoChangerFvMesh/Make/files +++ b/src/topoChangerFvMesh/Make/files @@ -11,4 +11,14 @@ movingConeTopoFvMesh/movingConeTopoFvMesh.C mixerFvMesh/mixerFvMesh.C */ +dynamicPolyRefinementFvMesh/dynamicPolyRefinementFvMesh.C +dynamicPolyRefinementFvMesh/refinementSelection/refinementSelection/refinementSelection.C +dynamicPolyRefinementFvMesh/refinementSelection/fieldBoundsRefinement/fieldBoundsRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/minCellVolumeRefinement/minCellVolumeRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/minCellSizeRefinement/minCellSizeRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/minPatchDistanceRefinement/minPatchDistanceRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/protectedInitialRefinement/protectedInitialRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/minFaceArea2DRefinement/minFaceArea2DRefinement.C +dynamicPolyRefinementFvMesh/refinementSelection/compositeRefinementSelection/compositeRefinementSelection.C + LIB = $(FOAM_LIBBIN)/libtopoChangerFvMesh diff --git a/src/topoChangerFvMesh/Make/options b/src/topoChangerFvMesh/Make/options index e1f704d20e75c5434d18c1077afd31c2d906cab7..8362887c0af209ecd5d8a6584aafd3f6da18a07c 100644 --- a/src/topoChangerFvMesh/Make/options +++ b/src/topoChangerFvMesh/Make/options @@ -1,5 +1,6 @@ EXE_INC = \ -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/optimisation/adjointOptimisation/adjoint/lnInclude \ -I$(LIB_SRC)/fileFormats/lnInclude \ -I$(LIB_SRC)/surfMesh/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude \ @@ -8,6 +9,7 @@ EXE_INC = \ LIB_LIBS = \ -lfiniteVolume \ + -l adjointOptimisation \ -lfileFormats \ -lsurfMesh \ -lmeshTools \ diff --git a/src/topoChangerFvMesh/dynamicMotionSolverTopoFvMesh/dynamicMotionSolverTopoFvMesh.C b/src/topoChangerFvMesh/dynamicMotionSolverTopoFvMesh/dynamicMotionSolverTopoFvMesh.C index 9f4fe646cae492a8459e2a8ae69e14d4809a3709..132349d277a5e9b07cb042c2ed35a9e8a3816ddf 100644 --- a/src/topoChangerFvMesh/dynamicMotionSolverTopoFvMesh/dynamicMotionSolverTopoFvMesh.C +++ b/src/topoChangerFvMesh/dynamicMotionSolverTopoFvMesh/dynamicMotionSolverTopoFvMesh.C @@ -100,7 +100,7 @@ bool Foam::dynamicMotionSolverTopoFvMesh::update() // (TBD: should be in changeMesh if no inflation?) moving(false); // Do mesh changes (not using inflation - points added directly into mesh) - autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(false); + autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(); if (topoChangeMap) { diff --git a/src/topoChangerFvMesh/movingConeTopoFvMesh/movingConeTopoFvMesh.C b/src/topoChangerFvMesh/movingConeTopoFvMesh/movingConeTopoFvMesh.C index 51221b8fadfe0287f10ecd6ef0e79b3d3b5fb543..d35a3161dbcc659f71e9c482d9e63aefd9ea4e64 100644 --- a/src/topoChangerFvMesh/movingConeTopoFvMesh/movingConeTopoFvMesh.C +++ b/src/topoChangerFvMesh/movingConeTopoFvMesh/movingConeTopoFvMesh.C @@ -332,7 +332,7 @@ Foam::movingConeTopoFvMesh::~movingConeTopoFvMesh() bool Foam::movingConeTopoFvMesh::update() { // Do mesh changes (use inflation - put new points in topoChangeMap) - autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(true); + autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(); // Calculate the new point positions depending on whether the // topological change has happened or not diff --git a/src/topoChangerFvMesh/rawTopoChangerFvMesh/rawTopoChangerFvMesh.C b/src/topoChangerFvMesh/rawTopoChangerFvMesh/rawTopoChangerFvMesh.C index 9c2dae87361e7d824e2b9bdc8f40f45583da38f9..91326a22f856457b138d9a702c822cac55cf8928 100644 --- a/src/topoChangerFvMesh/rawTopoChangerFvMesh/rawTopoChangerFvMesh.C +++ b/src/topoChangerFvMesh/rawTopoChangerFvMesh/rawTopoChangerFvMesh.C @@ -83,8 +83,8 @@ bool Foam::rawTopoChangerFvMesh::update() moving(false); topoChanging(false); - // Do any topology changes. Sets topoChanging (through polyTopoChange) - autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(true); + // Do any topology changes. Sets topoChanging (through batchPolyTopoChange) + autoPtr<mapPolyMesh> topoChangeMap = topoChanger_.changeMesh(); const bool hasChanged = bool(topoChangeMap);