From 5e2d8d6ed2f190b0a29e1fb72427797c127c7421 Mon Sep 17 00:00:00 2001 From: mattijs <mattijs> Date: Thu, 16 Jul 2020 17:05:33 +0100 Subject: [PATCH] ENH: snappyHexMesh: multi-stage layer addition, automatic hole closure Automatic hole closure: - introduces 'holeToFace' topoSet source - used when detecting a 'leak-path' - creates additional baffles to close the leak Multi-stage layer addition: - Can add layers in multiple passes See issues: #2403, #2404 --- .../extrude/extrudeMesh/extrudeMesh.C | 83 +- etc/caseDicts/annotated/extrudeMeshDict | 6 +- etc/caseDicts/annotated/topoSetSourcesDict | 22 + .../primitiveMesh/PatchTools/PatchTools.H | 8 +- .../PatchTools/PatchToolsNormals.C | 22 +- .../polyTopoChange/addPatchCellLayer.C | 768 ++++- .../polyTopoChange/addPatchCellLayer.H | 2 + src/mesh/snappyHexMesh/Make/files | 1 + .../meshRefinement/meshRefinement.C | 657 ++-- .../meshRefinement/meshRefinement.H | 155 +- .../meshRefinement/meshRefinementBaffles.C | 659 +++- .../meshRefinement/meshRefinementBlock.C | 1034 +++++++ .../meshRefinementProblemCells.C | 61 +- .../meshRefinement/meshRefinementRefine.C | 1 + .../refinementSurfaces/refinementSurfaces.C | 11 + .../refinementSurfaces/refinementSurfaces.H | 11 + .../refinementSurfaces/surfaceZonesInfo.C | 72 +- .../layerParameters/layerParameters.C | 53 +- .../layerParameters/layerParameters.H | 39 +- .../refinementParameters.C | 42 + .../refinementParameters.H | 9 + .../snappyHexMeshDriver/snappyLayerDriver.C | 2731 ++++++++++------- .../snappyHexMeshDriver/snappyLayerDriver.H | 106 +- .../snappyLayerDriverSinglePass.C | 500 +++ .../snappyHexMeshDriver/snappyRefineDriver.C | 122 +- .../snappyHexMeshDriver/snappyRefineDriver.H | 3 +- .../snappyHexMeshDriver/snappySnapDriver.C | 79 + .../snappyHexMeshDriver/snappySnapDriver.H | 11 + src/meshTools/Make/files | 1 + src/meshTools/polyTopoChange/polyTopoChange.C | 29 +- src/meshTools/polyTopoChange/polyTopoChange.H | 4 + .../faceSources/holeToFace/holeToFace.C | 1359 ++++++++ .../faceSources/holeToFace/holeToFace.H | 220 ++ .../extrudeMesh/faceZoneExtrusion/Allrun.pre | 25 + .../mesh/extrudeMesh/faceZoneExtrusion/README | 9 + .../constant/transportProperties | 21 + .../faceZoneExtrusion/system/blockMeshDict | 71 + .../faceZoneExtrusion/system/controlDict | 49 + .../faceZoneExtrusion/system/decomposeParDict | 27 + .../faceZoneExtrusion/system/extrudeMeshDict | 30 + .../system/extrudeMeshDict.flip | 30 + .../faceZoneExtrusion/system/fvSchemes | 51 + .../faceZoneExtrusion/system/fvSolution | 52 + .../faceZoneExtrusion/system/topoSetDict | 82 + .../faceZoneExtrusion/system/topoSetDict.flip | 50 + .../snappyHexMesh/addLayersToFaceZone/Allrun | 2 + .../system/decomposeParDict | 22 +- .../addLayersToFaceZone/system/fvSolution | 2 +- .../system/snappyHexMeshDict | 64 +- .../sphere_gapClosure/system/controlDict | 54 + .../system/snappyHexMeshDict | 311 ++ 51 files changed, 8218 insertions(+), 1615 deletions(-) create mode 100644 src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriverSinglePass.C create mode 100644 src/meshTools/topoSet/faceSources/holeToFace/holeToFace.C create mode 100644 src/meshTools/topoSet/faceSources/holeToFace/holeToFace.H create mode 100755 tutorials/mesh/extrudeMesh/faceZoneExtrusion/Allrun.pre create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/README create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/constant/transportProperties create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/blockMeshDict create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/controlDict create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/decomposeParDict create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict.flip create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSchemes create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSolution create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict create mode 100644 tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict.flip create mode 100644 tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/controlDict create mode 100644 tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/snappyHexMeshDict diff --git a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C index bdb9ce92703..680eb5815e5 100644 --- a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C +++ b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2015-2020 OpenCFD Ltd. + Copyright (C) 2015-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -124,6 +124,38 @@ labelList patchFaces(const polyBoundaryMesh& patches, const wordList& names) } +void zoneFaces +( + const faceZoneMesh& fzs, + const wordList& names, + labelList& faceLabels, + bitSet& faceFlip +) +{ + label n = 0; + + forAll(names, i) + { + const auto& pp = fzs[fzs.findZoneID(names[i])]; + n += pp.size(); + } + faceLabels.setSize(n); + faceFlip.setSize(n); + n = 0; + forAll(names, i) + { + const auto& pp = fzs[fzs.findZoneID(names[i])]; + const boolList& ppFlip = pp.flipMap(); + forAll(pp, i) + { + faceLabels[n] = pp[i]; + faceFlip[n] = ppFlip[i]; + n++; + } + } +} + + void updateFaceLabels(const mapPolyMesh& map, labelList& faceLabels) { const labelList& reverseMap = map.reverseFaceMap(); @@ -233,7 +265,7 @@ int main(int argc, char *argv[]) } else { - regionName = polyMesh::defaultRegion; + regionName = fvMesh::defaultRegion; Info<< "Create mesh for time = " << runTimeExtruded.timeName() << nl << endl; } @@ -317,16 +349,34 @@ int main(int argc, char *argv[]) sourceCaseDir/("processor" + Foam::name(Pstream::myProcNo())); } wordList sourcePatches; - dict.readEntry("sourcePatches", sourcePatches); + wordList sourceFaceZones; + if + ( + dict.readIfPresent + ( + "sourcePatches", + sourcePatches, + keyType::LITERAL + ) + ) + { + if (sourcePatches.size() == 1) + { + frontPatchName = sourcePatches[0]; + } - if (sourcePatches.size() == 1) + Info<< "Extruding patches " << sourcePatches + << " on mesh " << sourceCasePath << nl + << endl; + } + else { - frontPatchName = sourcePatches[0]; + dict.readEntry("sourceFaceZones", sourceFaceZones); + Info<< "Extruding faceZones " << sourceFaceZones + << " on mesh " << sourceCasePath << nl + << endl; } - Info<< "Extruding patches " << sourcePatches - << " on mesh " << sourceCasePath << nl - << endl; Time runTime ( @@ -344,7 +394,17 @@ int main(int argc, char *argv[]) // creating separate mesh. addPatchCellLayer layerExtrude(mesh, (mode == MESH)); - const labelList meshFaces(patchFaces(patches, sourcePatches)); + labelList meshFaces; + bitSet faceFlips; + if (sourceFaceZones.size()) + { + zoneFaces(mesh.faceZones(), sourceFaceZones, meshFaces, faceFlips); + } + else + { + meshFaces = patchFaces(patches, sourcePatches); + faceFlips.setSize(meshFaces.size()); + } if (mode == PATCH && flipNormals) { @@ -411,7 +471,7 @@ int main(int argc, char *argv[]) // Determine extrudePatch normal pointField extrudePatchPointNormals ( - PatchTools::pointNormals(mesh, extrudePatch) + PatchTools::pointNormals(mesh, extrudePatch, faceFlips) ); @@ -642,6 +702,7 @@ int main(int argc, char *argv[]) ratio, // expansion ratio extrudePatch, // patch faces to extrude + faceFlips, // side to extrude (for internal/coupled faces) edgePatchID, // if boundary edge: patch for extruded face edgeZoneID, // optional zone for extruded face @@ -787,7 +848,7 @@ int main(int argc, char *argv[]) ( IOobject ( - polyMesh::defaultRegion, + extrudedMesh::defaultRegion, runTimeExtruded.constant(), runTimeExtruded ), diff --git a/etc/caseDicts/annotated/extrudeMeshDict b/etc/caseDicts/annotated/extrudeMeshDict index 422f598be09..486d8d6298d 100644 --- a/etc/caseDicts/annotated/extrudeMeshDict +++ b/etc/caseDicts/annotated/extrudeMeshDict @@ -24,8 +24,10 @@ constructFrom patch; //constructFrom surface; // If construct from patch/mesh: -sourceCase "<case>"; -sourcePatches (movingWall); +sourceCase "<case>"; +// and one of sourcePatches or sourceFaceZones (but not both): +sourceFaceZones (someFacesZone); +sourcePatches (movingWall); // If construct from patch: patch to use for back (can be same as sourcePatch) exposedPatchName movingWall; diff --git a/etc/caseDicts/annotated/topoSetSourcesDict b/etc/caseDicts/annotated/topoSetSourcesDict index ce5cc7ac4b2..c1ad42711ac 100644 --- a/etc/caseDicts/annotated/topoSetSourcesDict +++ b/etc/caseDicts/annotated/topoSetSourcesDict @@ -384,6 +384,28 @@ faceSet_doc } + //- Faces to close connection between points in different 'zones' + { + source holeToFace; + points + ( + ((0.2 0.2 -10) (1.3 0.4 -0.1)) // points for zone 0 + ((10 10 10)) // points for zone 1 + ); + + // optional blocked faces (all uncoupled boundary faces are always + // blocked) + faceSet blockedFaces; + + // optional subset of cells to operate in (default is all cells) + cellSet candidateCells; + + // optional erosion of resulting set. This does some local optimisations + // to minimise the set of faces + erode true; + } + + //- All faces of faceZone { source zoneToFace; diff --git a/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchTools.H b/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchTools.H index 3579c952ae3..a50b0fdf326 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchTools.H +++ b/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchTools.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2020 OpenCFD Ltd. + Copyright (C) 2020,2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -199,7 +199,8 @@ public: static tmp<pointField> pointNormals ( const polyMesh&, - const PrimitivePatch<FaceList, PointField>& + const PrimitivePatch<FaceList, PointField>&, + const bitSet& flipMap = bitSet::null() ); @@ -211,7 +212,8 @@ public: const polyMesh&, const PrimitivePatch<FaceList, PointField>&, const labelList& patchEdges, - const labelList& coupledEdges + const labelList& coupledEdges, + const bitSet& flipMap = bitSet::null() ); diff --git a/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchToolsNormals.C b/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchToolsNormals.C index 7d14c99d7bf..f606cbc50d8 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchToolsNormals.C +++ b/src/OpenFOAM/meshes/primitiveMesh/PatchTools/PatchToolsNormals.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2019-2020 OpenCFD Ltd. + Copyright (C) 2019-2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -38,7 +38,8 @@ Foam::tmp<Foam::pointField> Foam::PatchTools::pointNormals ( const polyMesh& mesh, - const PrimitivePatch<FaceList, PointField>& p + const PrimitivePatch<FaceList, PointField>& p, + const bitSet& pFlip ) { const globalMeshData& globalData = mesh.globalData(); @@ -72,7 +73,9 @@ Foam::PatchTools::pointNormals pNormals.setSize(pFaces.size()); forAll(pFaces, i) { - pNormals[i] = p.faceNormals()[pFaces[i]]; + const label facei = pFaces[i]; + const vector& n = p.faceNormals()[facei]; + pNormals[i] = ((pFlip.empty() || !pFlip[facei]) ? n : -n); } } } @@ -165,7 +168,7 @@ Foam::PatchTools::pointNormals const vector& n = faceNormals[facei]; forAll(f, fp) { - extrudeN[f[fp]] += n; + extrudeN[f[fp]] += ((pFlip.empty() || !pFlip[facei]) ? n : -n); } } extrudeN /= mag(extrudeN)+VSMALL; @@ -196,7 +199,8 @@ Foam::PatchTools::edgeNormals const polyMesh& mesh, const PrimitivePatch<FaceList, PointField>& p, const labelList& patchEdges, - const labelList& coupledEdges + const labelList& coupledEdges, + const bitSet& pFlip ) { // 1. Start off with local normals @@ -213,7 +217,13 @@ Foam::PatchTools::edgeNormals const labelList& eFaces = edgeFaces[edgei]; for (const label facei : eFaces) { - edgeNormals[edgei] += faceNormals[facei]; + const vector& n = faceNormals[facei]; + edgeNormals[edgei] += + ( + (pFlip.empty() || !pFlip[facei]) + ? n + : -n + ); } } edgeNormals /= mag(edgeNormals)+VSMALL; diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C index f2eda1b767d..b0efd6df4b9 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C @@ -45,6 +45,40 @@ License namespace Foam { defineTypeNameAndDebug(addPatchCellLayer, 0); + + // Reduction class to get minimum value over face. + class minEqOpFace + { + public: + + void operator()(face& x, const face& y) const + { + if (x.size()) + { + if (y.size()) + { + label j = 0; + forAll(x, i) + { + x[i] = min(x[i], y[j]); + + j = y.rcIndex(j); + } + } + } + else if (y.size()) + { + x.setSize(y.size()); + label j = 0; + forAll(x, i) + { + x[i] = y[j]; + j = y.rcIndex(j); + } + } + } + }; + } @@ -300,6 +334,8 @@ Foam::label Foam::addPatchCellLayer::addSideFace //Pout<< "Added boundary face:" << newFace + // << " atfc:" << newFace.centre(meshMod.points()) + // << " n:" << newFace.unitNormal(meshMod.points()) // << " own:" << addedCells[ownFacei][layerOwn] // << " patch:" << newPatchID // << endl; @@ -405,6 +441,8 @@ Foam::label Foam::addPatchCellLayer::addSideFace ); //Pout<< "Added internal face:" << newFace + // << " atfc:" << newFace.centre(meshMod.points()) + // << " n:" << newFace.unitNormal(meshMod.points()) // << " own:" << addedCells[ownFacei][layerOwn] // << " nei:" << addedCells[nbrFacei][layerNbr] // << endl; @@ -920,36 +958,37 @@ void Foam::addPatchCellLayer::globalEdgeInfo meshEdgeToFace[edgei] = globalFaces.toGlobal(facei); + } + + // Override any patch info. Note that + // meshEdgeToFace might be an internal face. + if (meshEdgeToPatch[edgei] == -1) + { + meshEdgeToPatch[edgei] = pp.index(); + } - // Override any patch info - if (meshEdgeToPatch[edgei] == -1) + // Override any zone info + if (meshEdgeToZone[edgei] == -1) + { + meshEdgeToZone[edgei] = + faceToZone[facei]; + const edge& meshE = mesh.edges()[edgei]; + const int d = edge::compare(e, meshE); + if (d == 1) { - meshEdgeToPatch[edgei] = pp.index(); + meshEdgeToFlip[edgei] = + faceToFlip[facei]; } - - // Override any zone info - if (meshEdgeToZone[edgei] == -1) + else if (d == -1) + { + meshEdgeToFlip[edgei] = + !faceToFlip[facei]; + } + else { - meshEdgeToZone[edgei] = - faceToZone[facei]; - const edge& meshE = mesh.edges()[edgei]; - const int d = edge::compare(e, meshE); - if (d == 1) - { - meshEdgeToFlip[edgei] = - faceToFlip[facei]; - } - else if (d == -1) - { - meshEdgeToFlip[edgei] = - !faceToFlip[facei]; - } - else - { - FatalErrorInFunction - << "big problem" - << exit(FatalError); - } + FatalErrorInFunction + << "big problem" + << exit(FatalError); } } } @@ -962,6 +1001,7 @@ void Foam::addPatchCellLayer::globalEdgeInfo } } + // Synchronise across coupled edges. Max patch/face/faceZone wins syncTools::syncEdgeList ( @@ -1420,6 +1460,7 @@ void Foam::addPatchCellLayer::setRefinement const labelListList& globalEdgeFaces, const scalarField& expansionRatio, const indirectPrimitivePatch& pp, + const bitSet& ppFlip, const labelList& edgePatchID, const labelList& edgeZoneID, @@ -1446,6 +1487,7 @@ void Foam::addPatchCellLayer::setRefinement pp.nPoints() != firstLayerDisp.size() || pp.nPoints() != nPointLayers.size() || pp.size() != nFaceLayers.size() + || pp.size() != ppFlip.size() ) { FatalErrorInFunction @@ -1455,9 +1497,89 @@ void Foam::addPatchCellLayer::setRefinement << " displacement:" << firstLayerDisp.size() << " nPointLayers:" << nPointLayers.size() << nl << " patch.nFaces:" << pp.size() + << " flip map:" << ppFlip.size() << " nFaceLayers:" << nFaceLayers.size() << abort(FatalError); } + if (!addToMesh_) + { + // flip map should be false + if (ppFlip.count()) + { + FatalErrorInFunction + << "In generating stand-alone mesh the flip map should be empty" + << ". Instead it is " << ppFlip.count() + << abort(FatalError); + } + } + else + { + // Maybe check for adding to neighbour of boundary faces? How about + // coupled faces where the faceZone flipMap is negated + + // For all boundary faces: + // -1 : not extruded + // 0 : extruded from owner outwards (flip = false) + // 1 : extrude from neighbour outwards + labelList stateAndFlip(mesh_.nBoundaryFaces(), 0); + forAll(pp.addressing(), patchFacei) + { + if (nFaceLayers[patchFacei] > 0) + { + const label meshFacei = pp.addressing()[patchFacei]; + const label bFacei = meshFacei-mesh_.nInternalFaces(); + if (bFacei >= 0) + { + stateAndFlip[bFacei] = label(ppFlip[patchFacei]); + } + } + } + // Make sure uncoupled patches do not trigger the error below + for (const auto& patch : mesh_.boundaryMesh()) + { + if (!patch.coupled()) + { + forAll(patch, i) + { + label& state = stateAndFlip[patch.offset()+i]; + state = (state == 0 ? 1 : 0); + } + } + } + syncTools::swapBoundaryFaceList(mesh_, stateAndFlip); + + forAll(pp.addressing(), patchFacei) + { + if (nFaceLayers[patchFacei] > 0) + { + const label meshFacei = pp.addressing()[patchFacei]; + const label bFacei = meshFacei-mesh_.nInternalFaces(); + if (bFacei >= 0) + { + if (stateAndFlip[bFacei] == -1) + { + FatalErrorInFunction + << "At extruded face:" << meshFacei + << " at:" << mesh_.faceCentres()[meshFacei] + << " locally have nLayers:" + << nFaceLayers[patchFacei] + << " but remotely have none" << exit(FatalError); + } + else if (stateAndFlip[bFacei] == label(ppFlip[patchFacei])) + { + FatalErrorInFunction + << "At extruded face:" << meshFacei + << " at:" << mesh_.faceCentres()[meshFacei] + << " locally have flip:" << ppFlip[patchFacei] + << " which is not the opposite of coupled version " + << stateAndFlip[bFacei] + << exit(FatalError); + } + } + } + } + } + forAll(nPointLayers, i) { @@ -1503,6 +1625,19 @@ void Foam::addPatchCellLayer::setRefinement const labelList& meshPoints = pp.meshPoints(); + + // Determine which points are on which side of the extrusion + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + bitSet isBlockedFace(mesh_.nFaces()); + forAll(nFaceLayers, patchFacei) + { + if (nFaceLayers[patchFacei] > 0) + { + isBlockedFace.set(pp.addressing()[patchFacei]); + } + } + // Some storage for edge-face-addressing. DynamicList<label> ef; @@ -1636,49 +1771,51 @@ void Foam::addPatchCellLayer::setRefinement << abort(FatalError); } - label myFacei = pp.addressing()[eFaces[0]]; - - label meshEdgei = meshEdges[edgei]; - - // Mesh faces using edge - const labelList& meshFaces = mesh_.edgeFaces(meshEdgei, ef); - - // Check that there is only one patchface using edge. - const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - - label bFacei = -1; - - forAll(meshFaces, i) - { - label facei = meshFaces[i]; - - if (facei != myFacei) - { - if (!mesh_.isInternalFace(facei)) - { - if (bFacei == -1) - { - bFacei = facei; - } - else - { - FatalErrorInFunction - << "boundary-edge-to-be-extruded:" - << pp.points()[meshPoints[e[0]]] - << pp.points()[meshPoints[e[1]]] - << " has more than two boundary faces" - << " using it:" - << bFacei << " fc:" - << mesh_.faceCentres()[bFacei] - << " patch:" << patches.whichPatch(bFacei) - << " and " << facei << " fc:" - << mesh_.faceCentres()[facei] - << " patch:" << patches.whichPatch(facei) - << abort(FatalError); - } - } - } - } + //label myFacei = pp.addressing()[eFaces[0]]; + // + //label meshEdgei = meshEdges[edgei]; + // + //// Mesh faces using edge + //const labelList& meshFaces = mesh_.edgeFaces(meshEdgei, ef); + // + //// Check that there is only one patchface using edge. + //const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + // + //label bFacei = -1; + // + //forAll(meshFaces, i) + //{ + // label facei = meshFaces[i]; + // + // if (facei != myFacei) + // { + // if (!mesh_.isInternalFace(facei)) + // { + // if (bFacei == -1) + // { + // bFacei = facei; + // } + // else + // { + // //FatalErrorInFunction + // WarningInFunction + // << "boundary-edge-to-be-extruded:" + // << pp.points()[meshPoints[e[0]]] + // << pp.points()[meshPoints[e[1]]] + // << " has more than one boundary faces" + // << " using it:" + // << bFacei << " fc:" + // << mesh_.faceCentres()[bFacei] + // << " patch:" << patches.whichPatch(bFacei) + // << " and " << facei << " fc:" + // << mesh_.faceCentres()[facei] + // << " patch:" << patches.whichPatch(facei) + // << endl; + // //abort(FatalError); + // } + // } + // } + //} } } } @@ -1723,6 +1860,198 @@ void Foam::addPatchCellLayer::setRefinement } + // Store per face whether it uses the duplicated point or the original one + // Also mark any affected cells. We could transport the duplicated point + // itself but since it is a processor-local index only we only transport + // a boolean. + + // Per face, per index in face either labelMax or a valid index. Note: + // most faces are not affected in which case the face will be zero size + // and only have a nullptr and a size. + faceList baseFaces(mesh_.nFaces()); + bitSet isAffectedCell(mesh_.nCells()); + { + const faceList& localFaces = pp.localFaces(); + forAll(localFaces, patchFacei) + { + const face& f = localFaces[patchFacei]; + forAll(f, fp) + { + const label patchPointi = f[fp]; + if (nPointLayers[patchPointi] > 0) + { + const label meshFacei = pp.addressing()[patchFacei]; + face& baseF = baseFaces[meshFacei]; + // Initialise to labelMax if not yet sized + baseF.setSize(f.size(), labelMax); + baseF[fp] = pp.meshPoints()[patchPointi]; + + if (ppFlip[patchFacei]) + { + // Neighbour stays. Affected points on the owner side. + const label celli = mesh_.faceOwner()[meshFacei]; + isAffectedCell.set(celli); + } + else if (mesh_.isInternalFace(meshFacei)) + { + // Owner unaffected. Unaffected points on neighbour side + const label celli = mesh_.faceNeighbour()[meshFacei]; + isAffectedCell.set(celli); + } + } + } + } + } + + // Transport affected side across faces. Could do across edges: say we have + // a loose cell edge-(but not face-)connected to face-to-be-extruded do + // we want it to move with the extrusion or stay connected to the original? + // For now just keep it connected to the original. + { + // Work space + Map<label> minPointValue(128); + faceList oldBoundaryFaces(mesh_.nBoundaryFaces()); + + while (true) + { + bitSet newIsAffectedCell(mesh_.nCells()); + + label nChanged = 0; + for (const label celli : isAffectedCell) + { + const cell& cFaces = mesh_.cells()[celli]; + + // 1. Determine marked base points. Inside a single cell all + // faces use the same 'instance' of a point. + minPointValue.clear(); + for (const label facei : cFaces) + { + const face& baseF = baseFaces[facei]; + const face& f = mesh_.faces()[facei]; + + if (baseF.size()) + { + forAll(f, fp) + { + if (baseF[fp] != labelMax) + { + // Could check here for inconsistent patchPoint + // e.g. cell using both sides of a + // face-to-be-extruded. Is not possible! + minPointValue.insert(f[fp], baseF[fp]); + } + } + } + } + + //Pout<< "For cell:" << celli + // << " at:" << mesh_.cellCentres()[celli] + // << " have minPointValue:" << minPointValue + // << endl; + + // 2. Transport marked points on all cell points + for (const label facei : cFaces) + { + const face& f = mesh_.faces()[facei]; + face& baseF = baseFaces[facei]; + + const label oldNChanged = nChanged; + forAll(f, fp) + { + const auto fnd = minPointValue.find(f[fp]); + if (fnd.found()) + { + baseF.setSize(f.size(), labelMax); + if (baseF[fp] == labelMax) + { + baseF[fp] = fnd(); + nChanged++; + + //Pout<< "For cell:" << celli + // << " at:" << mesh_.cellCentres()[celli] + // << " on face:" << facei + // << " points:" + // << UIndirectList<point>(mesh_.points(), f) + // << " now have baseFace:" << baseF + // << endl; + } + } + } + + if (!isBlockedFace(facei) && nChanged > oldNChanged) + { + // Mark neighbouring cells + const label own = mesh_.faceOwner()[facei]; + if (!isAffectedCell[own]) + { + newIsAffectedCell.set(own); + } + if (mesh_.isInternalFace(facei)) + { + const label nei = mesh_.faceNeighbour()[facei]; + if (!isAffectedCell[nei]) + { + newIsAffectedCell.set(nei); + } + } + } + } + } + + if (debug) + { + Pout<< "isAffectedCell:" << isAffectedCell.count() << endl; + Pout<< "newIsAffectedCell:" << newIsAffectedCell.count() + << endl; + Pout<< "nChanged:" << nChanged << endl; + } + + if (returnReduce(nChanged, sumOp<label>()) == 0) + { + break; + } + + + // Transport minimum across coupled faces + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + SubList<face> l + ( + baseFaces, + mesh_.nBoundaryFaces(), + mesh_.nInternalFaces() + ); + oldBoundaryFaces = l; + syncTools::syncBoundaryFaceList + ( + mesh_, + l, + minEqOpFace(), + Foam::dummyTransform() // dummy transformation + ); + + forAll(l, bFacei) + { + // Note: avoid special handling of comparing zero-sized faces + // (see face::operator==). Review. + const labelUList& baseVts = l[bFacei]; + const labelUList& oldVts = oldBoundaryFaces[bFacei]; + if (baseVts != oldVts) + { + const label facei = mesh_.nInternalFaces()+bFacei; + const label own = mesh_.faceOwner()[facei]; + if (!isAffectedCell[own]) + { + newIsAffectedCell.set(own); + } + } + } + + isAffectedCell = newIsAffectedCell; + } + } + + // // Create new points // @@ -1734,18 +2063,21 @@ void Foam::addPatchCellLayer::setRefinement copiedPatchPoints.setSize(firstLayerDisp.size()); forAll(firstLayerDisp, patchPointi) { - label meshPointi = meshPoints[patchPointi]; - label zoneI = mesh_.pointZones().whichZone(meshPointi); - copiedPatchPoints[patchPointi] = meshMod.setAction - ( - polyAddPoint + if (addedPoints_[patchPointi].size()) + { + label meshPointi = meshPoints[patchPointi]; + label zoneI = mesh_.pointZones().whichZone(meshPointi); + copiedPatchPoints[patchPointi] = meshMod.setAction ( - mesh_.points()[meshPointi], // point - -1, // master point - zoneI, // zone for point - true // supports a cell - ) - ); + polyAddPoint + ( + mesh_.points()[meshPointi], // point + -1, // master point + zoneI, // zone for point + true // supports a cell + ) + ); + } } } @@ -1755,9 +2087,8 @@ void Foam::addPatchCellLayer::setRefinement { if (addedPoints_[patchPointi].size()) { - label meshPointi = meshPoints[patchPointi]; - - label zoneI = mesh_.pointZones().whichZone(meshPointi); + const label meshPointi = meshPoints[patchPointi]; + const label zoneI = mesh_.pointZones().whichZone(meshPointi); point pt = mesh_.points()[meshPointi]; @@ -1767,7 +2098,7 @@ void Foam::addPatchCellLayer::setRefinement { pt += disp; - label addedVertI = meshMod.setAction + const label addedVertI = meshMod.setAction ( polyAddPoint ( @@ -1778,6 +2109,12 @@ void Foam::addPatchCellLayer::setRefinement ) ); + + //Pout<< "Adding point:" << addedVertI << " at:" << pt + // << " from point:" << meshPointi + // << " at:" << mesh_.points()[meshPointi] + // << endl; + addedPoints_[patchPointi][i] = addedVertI; disp *= expansionRatio[patchPointi]; @@ -1796,31 +2133,56 @@ void Foam::addPatchCellLayer::setRefinement { if (nFaceLayers[patchFacei] > 0) { - addedCells[patchFacei].setSize(nFaceLayers[patchFacei]); - - label meshFacei = pp.addressing()[patchFacei]; + const label meshFacei = pp.addressing()[patchFacei]; - label ownZoneI = mesh_.cellZones().whichZone - ( - mesh_.faceOwner()[meshFacei] - ); + label extrudeCelli = -2; + label extrudeZonei; + if (!addToMesh_) + { + extrudeCelli = -1; + const label ownCelli = mesh_.faceOwner()[meshFacei]; + extrudeZonei = mesh_.cellZones().whichZone(ownCelli); + } + else if (!ppFlip[patchFacei]) + { + // Normal: extrude from owner face + extrudeCelli = mesh_.faceOwner()[meshFacei]; + extrudeZonei = mesh_.cellZones().whichZone(extrudeCelli); + } + else if (mesh_.isInternalFace(meshFacei)) + { + // Extrude from neighbour face (if internal). Might be + // that it is a coupled face and the other side is + // extruded + extrudeCelli = mesh_.faceNeighbour()[meshFacei]; + extrudeZonei = mesh_.cellZones().whichZone(extrudeCelli); + } - for (label i = 0; i < nFaceLayers[patchFacei]; i++) + if (extrudeCelli != -2) { - // Note: add from cell (owner of patch face) or from face? - // for now add from cell so we can map easily. - addedCells[patchFacei][i] = meshMod.setAction - ( - polyAddCell + addedCells[patchFacei].setSize(nFaceLayers[patchFacei]); + + for (label i = 0; i < nFaceLayers[patchFacei]; i++) + { + // Note: add from cell (owner of patch face) or from face? + // for now add from cell so we can map easily. + addedCells[patchFacei][i] = meshMod.setAction ( - -1, // master point - -1, // master edge - -1, // master face - (addToMesh_ ? mesh_.faceOwner()[meshFacei] : -1), - //master - ownZoneI // zone for cell - ) - ); + polyAddCell + ( + -1, // master point + -1, // master edge + -1, // master face + extrudeCelli, // master cell + extrudeZonei // zone for cell + ) + ); + + //Pout<< "Added cell:" << addedCells[patchFacei][i] + // << " from master:" << extrudeCelli + // << " at:" << mesh_.cellCentres()[extrudeCelli] + // << endl; + } } } } @@ -1870,20 +2232,50 @@ void Foam::addPatchCellLayer::setRefinement newFace[fp] = addedPoints_[f[fp]][i+offset]; } } - + //Pout<< " newFace:" << newFace << endl; + //Pout<< " coords:" + // << UIndirectList<point>(meshMod.points(), newFace) + // << " normal:" << newFace.unitNormal(meshMod.points()) + // << endl; // Get new neighbour + label own = addedCells[patchFacei][i]; label nei; label patchi; label zoneI = -1; bool flip = false; - + bool fluxFlip = false; if (i == addedCells[patchFacei].size()-1) { - // Top layer so is patch face. - nei = -1; + // Top layer so is either patch face or connects to + // the other cell patchi = patchID[patchFacei]; + if (patchi == -1) + { + // Internal face + nei = + ( + !ppFlip[patchFacei] + ? mesh_.faceNeighbour()[meshFacei] + : mesh_.faceOwner()[meshFacei] + ); + + if (ppFlip[patchFacei]) + { + newFace = newFace.reverseFace(); + } + //Pout<< "** adding top (internal) face:" + // << " at:" << mesh_.faceCentres()[meshFacei] + // << " own:" << own << " nei:" << nei + // << " patchi:" << patchi + // << " newFace:" << newFace + // << endl; + } + else + { + nei = -1; + } zoneI = mesh_.faceZones().whichZone(meshFacei); if (zoneI != -1) { @@ -1898,29 +2290,55 @@ void Foam::addPatchCellLayer::setRefinement patchi = -1; } + if (nei != -1 && nei < own) + { + // Wrongly oriented internal face + newFace = newFace.reverseFace(); + std::swap(own, nei); + flip = !flip; + fluxFlip = true; + + //Pout<< "Flipped newFace:" + // << newFace.unitNormal(meshMod.points()) + // << " own:" << own + // << " nei:" << nei + // << endl; + } + + layerFaces_[patchFacei][i+1] = meshMod.setAction ( polyAddFace ( newFace, // face - addedCells[patchFacei][i], // owner + own, // owner nei, // neighbour -1, // master point -1, // master edge (addToMesh_ ? meshFacei : -1), // master face - false, // flux flip + fluxFlip, // flux flip patchi, // patch for face zoneI, // zone for face flip // face zone flip ) ); + + //Pout<< "added layer face:" << layerFaces_[patchFacei][i+1] + // << " verts:" << newFace + // << " newFc:" << newFace.centre(meshMod.points()) + // << " originalFc:" << mesh_.faceCentres()[meshFacei] + // << nl + // << " n:" << newFace.unitNormal(meshMod.points()) + // << " own:" << own << " nei:" << nei + // << " patchi:" << patchi + // << endl; } } } // - // Modify old patch faces to be on the inside + // Modify owner faces to have addedCells as neighbour // if (addToMesh_) @@ -1932,22 +2350,39 @@ void Foam::addPatchCellLayer::setRefinement label meshFacei = pp.addressing()[patchFacei]; layerFaces_[patchFacei][0] = meshFacei; + const face& f = pp[patchFacei]; + + const label own = + ( + !ppFlip[patchFacei] + ? mesh_.faceOwner()[meshFacei] + : mesh_.faceNeighbour()[meshFacei] + ); + const label nei = addedCells[patchFacei][0]; meshMod.setAction ( polyModifyFace ( - pp[patchFacei], // modified face + (ppFlip[patchFacei] ? f.reverseFace() : f),// verts meshFacei, // label of face - mesh_.faceOwner()[meshFacei], // owner - addedCells[patchFacei][0], // neighbour - false, // face flip + own, // owner + nei, // neighbour + ppFlip[patchFacei], // face flip -1, // patch for face true, //false, // remove from zone -1, //zoneI, // zone for face false // face flip in zone ) ); + + //Pout<< "Modified bottom face " << meshFacei + // << " at:" << mesh_.faceCentres()[meshFacei] + // << " new own:" << own << " new nei:" << nei + // << " verts:" << meshMod.faces()[meshFacei] + // << " n:" + // << meshMod.faces()[meshFacei].unitNormal(meshMod.points()) + // << endl; } } } @@ -1957,7 +2392,7 @@ void Foam::addPatchCellLayer::setRefinement // in the exposed patch ID. forAll(pp, patchFacei) { - if (nFaceLayers[patchFacei] > 0) + if (addedCells[patchFacei].size()) { label meshFacei = pp.addressing()[patchFacei]; label zoneI = mesh_.faceZones().whichZone(meshFacei); @@ -2283,6 +2718,13 @@ void Foam::addPatchCellLayer::setRefinement newFace.setSize(newFp); + // Walked edges as if owner face was extruded. Reverse + // for neighbour face extrusion. + if (ppFlip[patchFacei]) + { + newFace = newFace.reverseFace(); + } + if (debug) { labelHashSet verts(2*newFace.size()); @@ -2372,6 +2814,98 @@ void Foam::addPatchCellLayer::setRefinement } } } + + + // Adjust side faces if they're on the side where points were duplicated + // (i.e. adding to internal faces). Like duplicatePoints::setRefinement. + if (addToMesh_) + { + face newFace; + + forAll(baseFaces, facei) + { + const face& f = mesh_.faces()[facei]; + const face& baseF = baseFaces[facei]; + + if (isBlockedFace(facei) || baseF.empty()) + { + // Either part of patch or no duplicated points on face + continue; + } + + // Start off from original face + newFace = f; + forAll(f, fp) + { + const label meshPointi = f[fp]; + if (baseF[fp] != labelMax) + { + // Duplicated point + const label patchPointi = pp.meshPointMap()[meshPointi]; + const label addedPointi = addedPoints_[patchPointi].last(); + + //Pout<< " For point:" << meshPointi + // << " at:" << mesh_.points()[meshPointi] + // << " at:" << pp.localPoints()[patchPointi] + // << " using addedpoint:" << addedPointi + // << " at:" << meshMod.points()[addedPointi] + // << endl; + + newFace[fp] = addedPointi; + } + } + + //Pout<< "for face:" << facei << nl + // << " old:" << f + // << " n:" << newFace.unitNormal(meshMod.points()) + // << " coords:" << UIndirectList<point>(mesh_.points(), f) + // << nl + // << " new:" << newFace + // << " n:" << newFace.unitNormal(meshMod.points()) + // << " coords:" + // << UIndirectList<point>(meshMod.points(), newFace) + // << endl; + + // Get current zone info + label zoneID = mesh_.faceZones().whichZone(facei); + bool zoneFlip = false; + if (zoneID >= 0) + { + const faceZone& fZone = mesh_.faceZones()[zoneID]; + zoneFlip = fZone.flipMap()[fZone.whichFace(facei)]; + } + + + if (mesh_.isInternalFace(facei)) + { + meshMod.modifyFace + ( + newFace, // modified face + facei, // label of modified face + mesh_.faceOwner()[facei], // owner + mesh_.faceNeighbour()[facei], // neighbour + false, // face flip + -1, // patch for face + zoneID, // zone for face + zoneFlip // face flip in zone + ); + } + else + { + meshMod.modifyFace + ( + newFace, // modified face + facei, // label of modified face + mesh_.faceOwner()[facei], // owner + -1, // neighbour + false, // face flip + patches.whichPatch(facei), // patch for face + zoneID, // zone for face + zoneFlip // face flip in zone + ); + } + } + } } diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H index fac5288dad4..b0d12af7ed6 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H @@ -399,6 +399,7 @@ public: const labelListList& globalEdgeFaces, const scalarField& expansionRatio, const indirectPrimitivePatch& pp, + const bitSet& flip, const labelList& sidePatchID, const labelList& sideZoneID, @@ -431,6 +432,7 @@ public: globalEdgeFaces, scalarField(pp.nPoints(), 1.0), // expansion ration pp, + bitSet(pp.size()), // flip sidePatchID, labelList(pp.nEdges(), -1), // zoneID diff --git a/src/mesh/snappyHexMesh/Make/files b/src/mesh/snappyHexMesh/Make/files index d29863fbc00..28be73f7c19 100644 --- a/src/mesh/snappyHexMesh/Make/files +++ b/src/mesh/snappyHexMesh/Make/files @@ -1,4 +1,5 @@ snappyHexMeshDriver/snappyLayerDriver.C +snappyHexMeshDriver/snappyLayerDriverSinglePass.C snappyHexMeshDriver/snappySnapDriver.C snappyHexMeshDriver/snappySnapDriverFeature.C snappyHexMeshDriver/snappyRefineDriver.C diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C index 7064665f442..d9e7af0d7ef 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.C @@ -61,6 +61,7 @@ License #include "faceSet.H" #include "topoDistanceData.H" #include "FaceCellWave.H" +#include "PackedBoolList.H" // Leak path #include "shortestPathSet.H" @@ -436,9 +437,107 @@ void Foam::meshRefinement::updateIntersections(const labelList& changedFaces) } -Foam::labelList Foam::meshRefinement::nearestPatch +void Foam::meshRefinement::nearestFace ( - const labelList& adaptPatchIDs + const labelUList& startFaces, + const bitSet& isBlockedFace, + + autoPtr<mapDistribute>& mapPtr, + labelList& faceToStart, + const label nIter +) const +{ + // From startFaces walk out (but not through isBlockedFace). Returns + // faceToStart which is the index into startFaces (or rather distributed + // version of it). E.g. + // pointField startFc(mesh.faceCentres(), startFaces); + // mapPtr().distribute(startFc); + // forAll(faceToStart, facei) + // { + // const label sloti = faceToStart[facei]; + // if (sloti != -1) + // { + // Pout<< "face:" << mesh.faceCentres()[facei] + // << " nearest:" << startFc[sloti] + // << endl; + // } + // } + + + // Consecutive global numbering for start elements + const globalIndex globalStart(startFaces.size()); + + // Field on cells and faces. + List<topoDistanceData<label>> cellData(mesh_.nCells()); + List<topoDistanceData<label>> faceData(mesh_.nFaces()); + + // Mark blocked faces to there not visited + for (const label facei : isBlockedFace) + { + faceData[facei] = topoDistanceData<label>(0, -1); + } + + List<topoDistanceData<label>> startData(startFaces.size()); + forAll(startFaces, i) + { + const label facei = startFaces[i]; + if (isBlockedFace[facei]) + { + FatalErrorInFunction << "Start face:" << facei + << " at:" << mesh_.faceCentres()[facei] + << " is also blocked" << exit(FatalError); + } + startData[i] = topoDistanceData<label>(0, globalStart.toGlobal(i)); + } + + + // Propagate information inwards + FaceCellWave<topoDistanceData<label>> deltaCalc + ( + mesh_, + startFaces, + startData, + faceData, + cellData, + 0 + ); + deltaCalc.iterate(nIter); + + // And extract + + faceToStart.setSize(mesh_.nFaces()); + faceToStart = -1; + bool haveWarned = false; + forAll(faceData, facei) + { + if (!faceData[facei].valid(deltaCalc.data())) + { + if (!haveWarned) + { + WarningInFunction + << "Did not visit some faces, e.g. face " << facei + << " at " << mesh_.faceCentres()[facei] + << endl; + haveWarned = true; + } + } + else + { + faceToStart[facei] = faceData[facei].data(); + } + } + + // Compact + List<Map<label>> compactMap; + mapPtr.reset(new mapDistribute(globalStart, faceToStart, compactMap)); +} + + +void Foam::meshRefinement::nearestPatch +( + const labelList& adaptPatchIDs, + labelList& nearestPatch, + labelList& nearestZone ) const { // Determine nearest patch for all mesh faces. Used when removing cells @@ -446,11 +545,21 @@ Foam::labelList Foam::meshRefinement::nearestPatch const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - labelList nearestAdaptPatch; + nearestZone.setSize(mesh_.nFaces(), -1); if (adaptPatchIDs.size()) { - nearestAdaptPatch.setSize(mesh_.nFaces(), adaptPatchIDs[0]); + nearestPatch.setSize(mesh_.nFaces(), adaptPatchIDs[0]); + + + // Get per-face the zone or -1 + labelList faceToZone(mesh_.nFaces(), -1); + { + for (const faceZone& fz : mesh_.faceZones()) + { + UIndirectList<label>(faceToZone, fz) = fz.index(); + } + } // Count number of faces in adaptPatchIDs @@ -462,12 +571,12 @@ Foam::labelList Foam::meshRefinement::nearestPatch } // Field on cells and faces. - List<topoDistanceData<label>> cellData(mesh_.nCells()); - List<topoDistanceData<label>> faceData(mesh_.nFaces()); + List<topoDistanceData<labelPair>> cellData(mesh_.nCells()); + List<topoDistanceData<labelPair>> faceData(mesh_.nFaces()); // Start of changes labelList patchFaces(nFaces); - List<topoDistanceData<label>> patchData(nFaces); + List<topoDistanceData<labelPair>> patchData(nFaces); nFaces = 0; forAll(adaptPatchIDs, i) { @@ -477,13 +586,21 @@ Foam::labelList Foam::meshRefinement::nearestPatch forAll(pp, i) { patchFaces[nFaces] = pp.start()+i; - patchData[nFaces] = topoDistanceData<label>(0, patchi); + patchData[nFaces] = topoDistanceData<labelPair> + ( + 0, + labelPair + ( + patchi, + faceToZone[pp.start()+i] + ) + ); nFaces++; } } // Propagate information inwards - FaceCellWave<topoDistanceData<label>> deltaCalc + FaceCellWave<topoDistanceData<labelPair>> deltaCalc ( mesh_, patchFaces, @@ -513,31 +630,45 @@ Foam::labelList Foam::meshRefinement::nearestPatch } else { - nearestAdaptPatch[facei] = faceData[facei].data(); + const labelPair& data = faceData[facei].data(); + nearestPatch[facei] = data.first(); + nearestZone[facei] = data.second(); } } } else { // Use patch 0 - nearestAdaptPatch.setSize(mesh_.nFaces(), 0); + nearestPatch.setSize(mesh_.nFaces(), 0); } +} + +Foam::labelList Foam::meshRefinement::nearestPatch +( + const labelList& adaptPatchIDs +) const +{ + labelList nearestAdaptPatch; + labelList nearestAdaptZone; + nearestPatch(adaptPatchIDs, nearestAdaptPatch, nearestAdaptZone); return nearestAdaptPatch; } -Foam::labelList Foam::meshRefinement::nearestIntersection +void Foam::meshRefinement::nearestIntersection ( const labelList& surfacesToTest, - const label defaultRegion + const labelList& testFaces, + + labelList& surface1, + List<pointIndexHit>& hit1, + labelList& region1, + labelList& surface2, + List<pointIndexHit>& hit2, + labelList& region2 ) const { - // Determine nearest intersection for all mesh faces. Used when removing - // cells to give some reasonable patch to exposed faces. Use this - // function instead of nearestPatch if you don't have patches yet. - - // Swap neighbouring cell centres and cell level labelList neiLevel(mesh_.nBoundaryFaces()); pointField neiCc(mesh_.nBoundaryFaces()); @@ -545,9 +676,6 @@ Foam::labelList Foam::meshRefinement::nearestIntersection // Collect segments - // ~~~~~~~~~~~~~~~~ - - const labelList testFaces(intersectedFaces()); pointField start(testFaces.size()); pointField end(testFaces.size()); @@ -564,17 +692,48 @@ Foam::labelList Foam::meshRefinement::nearestIntersection ); // Do tests in one go + + surfaces_.findNearestIntersection + ( + surfacesToTest, + start, + end, + + surface1, + hit1, + region1, + surface2, + hit2, + region2 + ); +} + + +Foam::labelList Foam::meshRefinement::nearestIntersection +( + const labelList& surfacesToTest, + const label defaultRegion +) const +{ + // Determine nearest intersection for all mesh faces. Used when removing + // cells to give some reasonable patch to exposed faces. Use this + // function instead of nearestPatch if you don't have patches yet. + + + // All faces with any intersection + const labelList testFaces(intersectedFaces()); + + // Find intersection (if any) labelList surface1; List<pointIndexHit> hit1; labelList region1; labelList surface2; List<pointIndexHit> hit2; labelList region2; - surfaces_.findNearestIntersection + nearestIntersection ( surfacesToTest, - start, - end, + testFaces, surface1, hit1, @@ -584,6 +743,7 @@ Foam::labelList Foam::meshRefinement::nearestIntersection region2 ); + labelList nearestRegion(mesh_.nFaces(), defaultRegion); // Field on cells and faces. @@ -591,9 +751,9 @@ Foam::labelList Foam::meshRefinement::nearestIntersection List<topoDistanceData<label>> faceData(mesh_.nFaces()); // Start walking from all intersected faces - DynamicList<label> patchFaces(start.size()); - DynamicList<topoDistanceData<label>> patchData(start.size()); - forAll(start, i) + DynamicList<label> patchFaces(testFaces.size()); + DynamicList<topoDistanceData<label>> patchData(testFaces.size()); + forAll(testFaces, i) { label facei = testFaces[i]; if (surface1[i] != -1) @@ -2490,6 +2650,33 @@ bool Foam::meshRefinement::getFaceZoneInfo } +Foam::label Foam::meshRefinement::addPointZone(const word& name) +{ + pointZoneMesh& pointZones = mesh_.pointZones(); + + label zoneI = pointZones.findZoneID(name); + + if (zoneI == -1) + { + zoneI = pointZones.size(); + pointZones.clearAddressing(); + pointZones.setSize(zoneI+1); + pointZones.set + ( + zoneI, + new pointZone + ( + name, // name + labelList(0), // addressing + zoneI, // index + pointZones // pointZoneMesh + ) + ); + } + return zoneI; +} + + void Foam::meshRefinement::selectSeparatedCoupledFaces(boolList& selected) const { for (const polyPatch& pp : mesh_.boundaryMesh()) @@ -2505,6 +2692,83 @@ void Foam::meshRefinement::selectSeparatedCoupledFaces(boolList& selected) const } +Foam::labelList Foam::meshRefinement::countEdgeFaces +( + const uindirectPrimitivePatch& pp +) const +{ + // Count number of faces per edge. Parallel consistent. + + const labelListList& edgeFaces = pp.edgeFaces(); + labelList nEdgeFaces(edgeFaces.size()); + forAll(edgeFaces, edgei) + { + nEdgeFaces[edgei] = edgeFaces[edgei].size(); + } + + // Sync across processor patches + if (Pstream::parRun()) + { + const globalMeshData& globalData = mesh_.globalData(); + const mapDistribute& map = globalData.globalEdgeSlavesMap(); + const indirectPrimitivePatch& cpp = globalData.coupledPatch(); + + // Match pp edges to coupled edges + labelList patchEdges; + labelList coupledEdges; + PackedBoolList sameEdgeOrientation; + PatchTools::matchEdges + ( + pp, + cpp, + patchEdges, + coupledEdges, + sameEdgeOrientation + ); + + // Convert patch-edge data into cpp-edge data + labelList coupledNEdgeFaces(map.constructSize(), Zero); + UIndirectList<label>(coupledNEdgeFaces, coupledEdges) = + UIndirectList<label>(nEdgeFaces, patchEdges); + + // Synchronise + globalData.syncData + ( + coupledNEdgeFaces, + globalData.globalEdgeSlaves(), + globalData.globalEdgeTransformedSlaves(), + map, + plusEqOp<label>() + ); + + // Convert back from cpp-edge to patch-edge + UIndirectList<label>(nEdgeFaces, patchEdges) = + UIndirectList<label>(coupledNEdgeFaces, coupledEdges); + } + return nEdgeFaces; +} + + +Foam::label Foam::meshRefinement::findCell +( + const polyMesh& mesh, + const vector& perturbVec, + const point& p +) +{ + // Force calculation of base points (needs to be synchronised) + (void)mesh.tetBasePtIs(); + + label celli = mesh.findCell(p, findCellMode); + if (returnReduce(celli, maxOp<label>()) == -1) + { + // See if we can perturb a bit + celli = mesh.findCell(p+perturbVec, findCellMode); + } + return celli; +} + + Foam::label Foam::meshRefinement::findRegion ( const polyMesh& mesh, @@ -2539,6 +2803,176 @@ Foam::label Foam::meshRefinement::findRegion } +Foam::fileName Foam::meshRefinement::writeLeakPath +( + const polyMesh& mesh, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh, + const writer<scalar>& leakPathFormatter, + const boolList& blockedFace +) +{ + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + + fileName outputDir; + if (Pstream::master()) + { + outputDir = + mesh.time().globalPath() + / functionObject::outputPrefix + / mesh.pointsInstance(); + outputDir.clean(); + mkDir(outputDir); + } + + + // Write the leak path + + meshSearch searchEngine(mesh); + shortestPathSet leakPath + ( + "leakPath", + mesh, + searchEngine, + coordSet::coordFormatNames[coordSet::coordFormat::DISTANCE], + false, //true, + 50, // tbd. Number of iterations + pbm.groupPatchIDs()["wall"], + locationsInMesh, + locationsOutsideMesh, + blockedFace + ); + + // Split leak path according to segment. Note: segment index + // is global (= index in locationsInsideMesh) + List<pointList> segmentPoints; + List<scalarList> segmentDist; + { + label nSegments = 0; + if (leakPath.segments().size()) + { + nSegments = max(leakPath.segments())+1; + } + reduce(nSegments, maxOp<label>()); + + labelList nElemsPerSegment(nSegments, Zero); + for (label segmenti : leakPath.segments()) + { + nElemsPerSegment[segmenti]++; + } + segmentPoints.setSize(nElemsPerSegment.size()); + segmentDist.setSize(nElemsPerSegment.size()); + forAll(nElemsPerSegment, i) + { + segmentPoints[i].setSize(nElemsPerSegment[i]); + segmentDist[i].setSize(nElemsPerSegment[i]); + } + nElemsPerSegment = 0; + + forAll(leakPath, elemi) + { + label segmenti = leakPath.segments()[elemi]; + pointList& points = segmentPoints[segmenti]; + scalarList& dist = segmentDist[segmenti]; + label& n = nElemsPerSegment[segmenti]; + + points[n] = leakPath[elemi]; + dist[n] = leakPath.curveDist()[elemi]; + n++; + } + } + + PtrList<coordSet> allLeakPaths(segmentPoints.size()); + forAll(allLeakPaths, segmenti) + { + // Collect data from all processors + List<pointList> gatheredPts(Pstream::nProcs()); + gatheredPts[Pstream::myProcNo()] = + std::move(segmentPoints[segmenti]); + Pstream::gatherList(gatheredPts); + + List<scalarList> gatheredDist(Pstream::nProcs()); + gatheredDist[Pstream::myProcNo()] = + std::move(segmentDist[segmenti]); + Pstream::gatherList(gatheredDist); + + // Combine processor lists into one big list. + pointList allPts + ( + ListListOps::combine<pointList> + ( + gatheredPts, accessOp<pointList>() + ) + ); + scalarList allDist + ( + ListListOps::combine<scalarList> + ( + gatheredDist, accessOp<scalarList>() + ) + ); + + // Sort according to curveDist + labelList indexSet(Foam::sortedOrder(allDist)); + + allLeakPaths.set + ( + segmenti, + new coordSet + ( + leakPath.name(), + leakPath.axis(), + pointList(allPts, indexSet), + //scalarList(allDist, indexSet) + scalarList(allPts.size(), scalar(segmenti)) + ) + ); + } + + fileName fName; + if (Pstream::master()) + { + List<List<scalarField>> allLeakData(1); + List<scalarField>& varData = allLeakData[0]; + varData.setSize(allLeakPaths.size()); + forAll(allLeakPaths, segmenti) + { + varData[segmenti] = allLeakPaths[segmenti].curveDist(); + } + + const wordList valueSetNames(1, "leakPath"); + + fName = + outputDir + /leakPathFormatter.getFileName + ( + allLeakPaths[0], + valueSetNames + ); + + // Note scope to force writing to finish before + // FatalError exit + OFstream ofs(fName); + if (ofs.opened()) + { + leakPathFormatter.write + ( + true, // write tracks + List<scalarField>(), // times + allLeakPaths, + valueSetNames, + allLeakData, + ofs + ); + } + } + + Pstream::scatter(fName); + + return fName; +} + + // Modify cellRegion to be consistent with locationsInMesh. // - all regions not in locationsInMesh are set to -1 // - check that all regions inside locationsOutsideMesh are set to -1 @@ -2549,7 +2983,7 @@ Foam::label Foam::meshRefinement::findRegions const pointField& locationsInMesh, const pointField& locationsOutsideMesh, const bool exitIfLeakPath, - const writer<scalar>& leakPathFormatter, + const refPtr<writer<scalar>>& leakPathFormatter, const label nRegions, labelList& cellRegion, const boolList& blockedFace @@ -2604,165 +3038,22 @@ Foam::label Foam::meshRefinement::findRegions label index = insideRegions.find(regioni); if (index != -1) { - const polyBoundaryMesh& pbm = mesh.boundaryMesh(); - - fileName outputDir; - if (Pstream::master()) - { - outputDir = - ( - mesh.time().globalPath() - / functionObject::outputPrefix - / mesh.pointsInstance() - ); - outputDir.clean(); // Remove unneeded ".." - mkDir(outputDir); - } - - - // Write the leak path - - meshSearch searchEngine(mesh); - shortestPathSet leakPath - ( - "leakPath", - mesh, - searchEngine, - coordSet::coordFormatNames[coordSet::coordFormat::DISTANCE], - false, //true, - 50, // tbd. Number of iterations - pbm.groupPatchIDs()["wall"], - locationsInMesh, - locationsOutsideMesh, - blockedFace - ); - - // Split leak path according to segment. Note: segment index - // is global (= index in locationsInsideMesh) - List<pointList> segmentPoints; - List<scalarList> segmentDist; + if (leakPathFormatter.valid()) { - label nSegments = 0; - if (leakPath.segments().size()) - { - nSegments = max(leakPath.segments())+1; - } - reduce(nSegments, maxOp<label>()); - - labelList nElemsPerSegment(nSegments, Zero); - for (label segmenti : leakPath.segments()) - { - nElemsPerSegment[segmenti]++; - } - segmentPoints.setSize(nElemsPerSegment.size()); - segmentDist.setSize(nElemsPerSegment.size()); - forAll(nElemsPerSegment, i) - { - segmentPoints[i].setSize(nElemsPerSegment[i]); - segmentDist[i].setSize(nElemsPerSegment[i]); - } - nElemsPerSegment = 0; - - forAll(leakPath, elemi) - { - label segmenti = leakPath.segments()[elemi]; - pointList& points = segmentPoints[segmenti]; - scalarList& dist = segmentDist[segmenti]; - label& n = nElemsPerSegment[segmenti]; - - points[n] = leakPath[elemi]; - dist[n] = leakPath.curveDist()[elemi]; - n++; - } - } - - PtrList<coordSet> allLeakPaths(segmentPoints.size()); - forAll(allLeakPaths, segmenti) - { - // Collect data from all processors - List<pointList> gatheredPts(Pstream::nProcs()); - gatheredPts[Pstream::myProcNo()] = - std::move(segmentPoints[segmenti]); - Pstream::gatherList(gatheredPts); - - List<scalarList> gatheredDist(Pstream::nProcs()); - gatheredDist[Pstream::myProcNo()] = - std::move(segmentDist[segmenti]); - Pstream::gatherList(gatheredDist); - - // Combine processor lists into one big list. - pointList allPts - ( - ListListOps::combine<pointList> - ( - gatheredPts, accessOp<pointList>() - ) - ); - scalarList allDist - ( - ListListOps::combine<scalarList> - ( - gatheredDist, accessOp<scalarList>() - ) - ); - - // Sort according to curveDist - labelList indexSet(Foam::sortedOrder(allDist)); - - allLeakPaths.set + const fileName fName ( - segmenti, - new coordSet + writeLeakPath ( - leakPath.name(), - leakPath.axis(), - pointList(allPts, indexSet), - //scalarList(allDist, indexSet) - scalarList(allPts.size(), scalar(segmenti)) + mesh, + locationsInMesh, + locationsOutsideMesh, + leakPathFormatter, + blockedFace ) ); + Info<< "Dumped leak path to " << fName << endl; } - fileName fName; - if (Pstream::master()) - { - List<List<scalarField>> allLeakData(1); - List<scalarField>& varData = allLeakData[0]; - varData.setSize(allLeakPaths.size()); - forAll(allLeakPaths, segmenti) - { - varData[segmenti] = allLeakPaths[segmenti].curveDist(); - } - - const wordList valueSetNames(1, "leakPath"); - - fName = - outputDir - /leakPathFormatter.getFileName - ( - allLeakPaths[0], - valueSetNames - ); - - // Note scope to force writing to finish before - // FatalError exit - OFstream ofs(fName); - if (ofs.opened()) - { - leakPathFormatter.write - ( - true, // write tracks - List<scalarField>(), // times - allLeakPaths, - valueSetNames, - allLeakData, - ofs - ); - } - } - - Pstream::scatter(fName); - if (exitIfLeakPath) { FatalErrorInFunction @@ -2770,7 +3061,6 @@ Foam::label Foam::meshRefinement::findRegions << " is inside same mesh region " << regioni << " as one of the locations outside mesh " << locationsOutsideMesh - << nl << " Dumped leak path to " << fName << exit(FatalError); } else @@ -2779,8 +3069,7 @@ Foam::label Foam::meshRefinement::findRegions << "Location in mesh " << locationsInMesh[index] << " is inside same mesh region " << regioni << " as one of the locations outside mesh " - << locationsOutsideMesh - << nl << "Dumped leak path to " << fName << endl; + << locationsOutsideMesh << endl; } } } @@ -2814,7 +3103,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMeshRegions const pointField& locationsInMesh, const pointField& locationsOutsideMesh, const bool exitIfLeakPath, - const writer<scalar>& leakPathFormatter + const refPtr<writer<scalar>>& leakPathFormatter ) { // Force calculation of face decomposition (used in findCell) diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H index 66fbf372fce..db8a65ab171 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinement.H @@ -52,6 +52,7 @@ SourceFiles #include "autoPtr.H" #include "labelPairHashes.H" #include "indirectPrimitivePatch.H" +#include "uindirectPrimitivePatch.H" #include "pointFieldsFwd.H" #include "Tuple2.H" #include "pointIndexHit.H" @@ -69,6 +70,7 @@ namespace Foam // Forward Declarations class fvMesh; class mapDistributePolyMesh; +class mapDistribute; class decompositionMethod; class refinementSurfaces; class refinementFeatures; @@ -584,6 +586,8 @@ private: const labelList& globalToMasterPatch, const pointField& locationsInMesh, const wordList& regionsInMesh, + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, const labelList& neiLevel, const pointField& neiCc, @@ -612,6 +616,17 @@ private: polyTopoChange& meshMod ) const; + //- Write leak path + static fileName writeLeakPath + ( + const polyMesh& mesh, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh, + const writer<scalar>& leakPathFormatter, + const boolList& blockedFace + ); + + // Problem cell handling //- Helper function to mark face as being on 'boundary'. Used by @@ -658,12 +673,37 @@ private: //- Returns list with for every internal face -1 or the patch // they should be baffled into. If removeEdgeConnectedCells is set // removes cells based on perpendicularAngle. - labelList markFacesOnProblemCells + void markFacesOnProblemCells ( const dictionary& motionDict, const bool removeEdgeConnectedCells, const scalarField& perpendicularAngle, - const labelList& globalToMasterPatch + const labelList& globalToMasterPatch, + + labelList& facePatch, + labelList& faceZone + ) const; + + //- Calculates for every face the nearest 'start' face. Any + // unreached face does not get set (faceToStart[facei] = -1) + void nearestFace + ( + const labelUList& startFaces, + const bitSet& isBlockedFace, + + autoPtr<mapDistribute>& mapPtr, + labelList& faceToStart, + const label nIter = labelMax + ) const; + + //- Calculates for every face the label of the nearest + // patch its zone. Any unreached face (disconnected mesh?) becomes + // adaptPatchIDs[0] + void nearestPatch + ( + const labelList& adaptPatchIDs, + labelList& nearestPatch, + labelList& nearestZone ) const; //- Returns list with for every face the label of the nearest @@ -682,12 +722,15 @@ private: //- Returns list with for every internal face -1 or the patch // they should be baffled into. - labelList markFacesOnProblemCellsGeometric + void markFacesOnProblemCellsGeometric ( const snapParameters& snapParams, const dictionary& motionDict, const labelList& globalToMasterPatch, - const labelList& globalToSlavePatch + const labelList& globalToSlavePatch, + + labelList& facePatch, + labelList& faceZone ) const; @@ -802,6 +845,8 @@ private: const label backgroundZoneID, const pointField& locationsInMesh, const wordList& zonesInMesh, + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, labelList& cellToZone, labelList& unnamedRegion1, @@ -1156,6 +1201,47 @@ public: const label growIter ); + // Blocking leaks (by blocking cells) + + //- Faces currently on boundary or intersected by surface + void selectIntersectedFaces + ( + const labelList& surfaces, + boolList& isBlockedFace + ) const; + + //- Return list of cells to block by walking from the seedCells + // until reaching a leak face + labelList detectLeakCells + ( + const boolList& isBlockedFace, + const labelList& leakFaces, + const labelList& seedCells + ) const; + + //- Remove minimum amount of cells to break any leak from + // inside to outside + autoPtr<mapPolyMesh> removeLeakCells + ( + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh, + const labelList& selectedSurfaces + ); + + //- Baffle faces to break any leak from inside to outside + autoPtr<mapPolyMesh> blockLeakFaces + ( + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh, + const labelList& selectedSurfaces + ); + + //- Refine some cells autoPtr<mapPolyMesh> refine(const labelList& cellsToRefine); @@ -1217,7 +1303,7 @@ public: const pointField& locationsInMesh, const wordList& regionsInMesh, const pointField& locationsOutsideMesh, - const writer<scalar>& leakPathFormatter + const refPtr<writer<scalar>>& leakPathFormatter ); //- Merge free-standing baffles @@ -1233,8 +1319,7 @@ public: const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, const pointField& locationsInMesh, - const pointField& locationsOutsideMesh, - const writer<scalar>& leakPathFormatter + const pointField& locationsOutsideMesh ); //- Split off (with optional buffer layers) unreachable areas @@ -1260,7 +1345,8 @@ public: const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, const pointField& locationsInMesh, - const wordList& regionsInMesh + const wordList& regionsInMesh, + const pointField& locationsOutsideMesh ); //- Find boundary points that connect to more than one cell @@ -1349,6 +1435,8 @@ public: const label nErodeCellZones, const pointField& locationsInMesh, const wordList& regionsInMesh, + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, wordPairHashTable& zonesToFaceZone ); @@ -1394,12 +1482,50 @@ public: surfaceZonesInfo::faceZoneType& fzType ) const; + //- Add pointZone if does not exist. Return index of zone + label addPointZone(const word& name); + + //- Count number of faces per patch edge. Parallel consistent. + labelList countEdgeFaces(const uindirectPrimitivePatch& pp) const; + //- Select coupled faces that are not collocated void selectSeparatedCoupledFaces(boolList&) const; //- Find any intersection of surface. Store in surfaceIndex_. void updateIntersections(const labelList& changedFaces); + //- Calculate nearest intersection for selected mesh faces + void nearestIntersection + ( + const labelList& surfacesToTest, + const labelList& testFaces, + + labelList& surface1, + List<pointIndexHit>& hit1, + labelList& region1, + labelList& surface2, + List<pointIndexHit>& hit2, + labelList& region2 + ) const; + + //- Remove cells. Put exposedFaces into exposedPatchIDs. + autoPtr<mapPolyMesh> doRemoveCells + ( + const labelList& cellsToRemove, + const labelList& exposedFaces, + const labelList& exposedPatchIDs, + removeCells& cellRemover + ); + + //- Find cell point is in. Uses optional perturbation to re-test. + // Returns -1 on processors that do not have the cell. + static label findCell + ( + const polyMesh&, + const vector& perturbVec, + const point& p + ); + //- Find region point is in. Uses optional perturbation to re-test. static label findRegion ( @@ -1418,7 +1544,7 @@ public: const pointField& locationsInMesh, const pointField& locationsOutsideMesh, const bool exitIfLeakPath, - const writer<scalar>& leakPathFormatter, + const refPtr<writer<scalar>>& leakPathFormatter, const label nRegions, labelList& cellRegion, const boolList& blockedFace @@ -1433,16 +1559,7 @@ public: const pointField& locationsInMesh, const pointField& locationsOutsideMesh, const bool exitIfLeakPath, - const writer<scalar>& leakPathFormatter - ); - - //- Remove cells. Put exposedFaces into exposedPatchIDs. - autoPtr<mapPolyMesh> doRemoveCells - ( - const labelList& cellsToRemove, - const labelList& exposedFaces, - const labelList& exposedPatchIDs, - removeCells& cellRemover + const refPtr<writer<scalar>>& leakPathFormatter ); //- Split faces into two diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C index 8638d0386d6..26f891b2289 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBaffles.C @@ -54,6 +54,7 @@ License #include "shellSurfaces.H" #include "zeroGradientFvPatchFields.H" #include "volFields.H" +#include "holeToFace.H" #include "FaceCellWave.H" #include "wallPoints.H" @@ -290,7 +291,8 @@ void Foam::meshRefinement::getBafflePatches const labelList& globalToMasterPatch, const pointField& locationsInMesh, const wordList& zonesInMesh, - + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, const labelList& neiLevel, const pointField& neiCc, @@ -307,6 +309,8 @@ void Foam::meshRefinement::getBafflePatches // 1. Determine intersections with unnamed surfaces and cell zones // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Notice that this also does hole-closure so the unnamed* is not just + // the surface intersections. labelList cellToZone; labelList unnamedRegion1; @@ -321,6 +325,8 @@ void Foam::meshRefinement::getBafflePatches -2, // zone to put unreached cells into locationsInMesh, zonesInMesh, + locationsOutsideMesh, + leakPathFormatter, cellToZone, unnamedRegion1, @@ -2836,6 +2842,8 @@ void Foam::meshRefinement::zonify const label backgroundZoneID, const pointField& locationsInMesh, const wordList& zonesInMesh, + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, labelList& cellToZone, labelList& unnamedRegion1, @@ -2856,6 +2864,17 @@ void Foam::meshRefinement::zonify const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones(); + // Collect inside and outside into single list + const List<pointField> allLocations + ( + refinementParameters::zonePoints + ( + locationsInMesh, + zonesInMesh, + locationsOutsideMesh + ) + ); + // Swap neighbouring cell centres and cell level labelList neiLevel(mesh_.nBoundaryFaces()); pointField neiCc(mesh_.nBoundaryFaces()); @@ -2899,7 +2918,105 @@ void Foam::meshRefinement::zonify unnamedRegion2 ); + // Extend with hole closing faces (only if locationsOutsideMesh) + labelList unnamedFaces; + labelList unnamedClosureFaces; + labelList unnamedToClosure; + autoPtr<mapDistribute> unnamedMapPtr; + if (locationsOutsideMesh.size()) + { + unnamedFaces = ListOps::findIndices + ( + unnamedRegion1, + [](const label x){return x != -1;} + ); + + const globalIndex globalUnnamedFaces(unnamedFaces.size()); + unnamedMapPtr = holeToFace::calcClosure + ( + mesh_, + allLocations, + unnamedFaces, + globalUnnamedFaces, + true, // allow erosion + + unnamedClosureFaces, + unnamedToClosure + ); + + if (debug) + { + Pout<< "meshRefinement::zonify : found wall closure faces:" + << unnamedClosureFaces.size() + << " map:" << unnamedMapPtr.valid() << endl; + } + + + // Add to unnamedRegion1, unnamedRegion2 + if (unnamedMapPtr.valid()) + { + Info<< "Detected and closed leak path from " + << locationsInMesh << " to " << locationsOutsideMesh + << endl; + + // Dump leak path + if (leakPathFormatter.valid()) + { + boolList blockedFace(mesh_.nFaces(), false); + UIndirectList<bool>(blockedFace, unnamedFaces) = true; + const fileName fName + ( + writeLeakPath + ( + mesh_, + locationsInMesh, + locationsOutsideMesh, + leakPathFormatter(), + blockedFace + ) + ); + Info<< "Dumped leak path to " << fName << endl; + } + + labelList packedRegion1 + ( + UIndirectList<label>(unnamedRegion1, unnamedFaces) + ); + unnamedMapPtr->distribute(packedRegion1); + labelList packedRegion2 + ( + UIndirectList<label>(unnamedRegion2, unnamedFaces) + ); + unnamedMapPtr->distribute(packedRegion2); + forAll(unnamedClosureFaces, i) + { + const label sloti = unnamedToClosure[i]; + + if (sloti != -1) + { + const label facei = unnamedClosureFaces[i]; + const label region1 = unnamedRegion1[facei]; + const label slotRegion1 = packedRegion1[sloti]; + const label region2 = unnamedRegion2[facei]; + const label slotRegion2 = packedRegion2[sloti]; + + if (slotRegion1 != region1 || slotRegion2 != region2) + { + unnamedRegion1[facei] = slotRegion1; + unnamedRegion2[facei] = slotRegion2; + } + } + } + } + } + + + // Extend with hole closing faces (only if locationsOutsideMesh) + labelList namedFaces; + labelList namedClosureFaces; + labelList namedToClosure; + autoPtr<mapDistribute> namedMapPtr; if (namedSurfaces.size()) { getIntersections @@ -2910,9 +3027,185 @@ void Foam::meshRefinement::zonify namedSurfaceRegion, posOrientation ); + + if (locationsOutsideMesh.size()) + { + namedFaces = ListOps::findIndices + ( + namedSurfaceRegion, + [](const label x){return x != -1;} + ); + + const globalIndex globalNamedFaces(namedFaces.size()); + + namedMapPtr = holeToFace::calcClosure + ( + mesh_, + allLocations, + namedFaces, + globalNamedFaces, + true, // allow erosion + + namedClosureFaces, + namedToClosure + ); + + if (debug) + { + Pout<< "meshRefinement::zonify : found faceZone closure faces:" + << namedClosureFaces.size() + << " map:" << namedMapPtr.valid() << endl; + } + + // Add to namedSurfaceRegion, posOrientation + if (namedMapPtr.valid()) + { + Info<< "Detected and closed leak path from " + << locationsInMesh << " to " << locationsOutsideMesh + << endl; + + // Dump leak path + if (leakPathFormatter.valid()) + { + boolList blockedFace(mesh_.nFaces(), false); + UIndirectList<bool>(blockedFace, unnamedFaces) = true; + UIndirectList<bool>(blockedFace, namedFaces) = true; + const fileName fName + ( + writeLeakPath + ( + mesh_, + locationsInMesh, + locationsOutsideMesh, + leakPathFormatter(), + blockedFace + ) + ); + Info<< "Dumped leak path to " << fName << endl; + } + + labelList packedSurfaceRegion + ( + UIndirectList<label>(namedSurfaceRegion, namedFaces) + ); + namedMapPtr->distribute(packedSurfaceRegion); + boolList packedOrientation(posOrientation.size()); + forAll(namedFaces, i) + { + const label facei = namedFaces[i]; + packedOrientation[i] = posOrientation[facei]; + } + namedMapPtr->distribute(packedOrientation); + forAll(namedClosureFaces, i) + { + const label sloti = namedToClosure[i]; + if (sloti != -1) + { + const label facei = namedClosureFaces[i]; + const label regioni = namedSurfaceRegion[facei]; + const label slotRegioni = packedSurfaceRegion[sloti]; + const bool orient = posOrientation[facei]; + const bool slotOrient = packedOrientation[sloti]; + + if (slotRegioni != regioni || slotOrient != orient) + { + namedSurfaceRegion[facei] = slotRegioni; + posOrientation[facei] = slotOrient; + } + } + } + } + } + } + + + // 1b. Add any hole closure faces to frozenPoints pointZone + { + bitSet isClosureFace(mesh_.nFaces()); + isClosureFace.set(unnamedClosureFaces); + isClosureFace.set(namedClosureFaces); + const labelList closureFaces(isClosureFace.sortedToc()); + + const uindirectPrimitivePatch pp + ( + UIndirectList<face>(mesh_.faces(), closureFaces), + mesh_.points() + ); + + // Count number of faces per edge + const labelList nEdgeFaces(countEdgeFaces(pp)); + + // Freeze all internal points + bitSet isFrozenPoint(mesh_.nPoints()); + forAll(nEdgeFaces, edgei) + { + if (nEdgeFaces[edgei] != 1) + { + const edge& e = pp.edges()[edgei]; + isFrozenPoint.set(pp.meshPoints()[e[0]]); + isFrozenPoint.set(pp.meshPoints()[e[1]]); + } + } + + // Lookup/add pointZone and include its points + pointZoneMesh& pointZones = + const_cast<pointZoneMesh&>(mesh_.pointZones()); + const label zonei = + const_cast<meshRefinement&>(*this).addPointZone("frozenPoints"); + const bitSet oldSet(mesh_.nPoints(), pointZones[zonei]); + isFrozenPoint.set(oldSet); + + syncTools::syncPointList + ( + mesh_, + isFrozenPoint, + orEqOp<unsigned int>(), + 0u + ); + + // Override addressing + pointZones.clearAddressing(); + pointZones[zonei] = isFrozenPoint.sortedToc(); + + if (debug) + { + const pointZone& pz = pointZones[zonei]; + mkDir(mesh_.time().timePath()); + OBJstream str(mesh_.time().timePath()/pz.name()+".obj"); + Pout<< "Writing " << pz.size() << " frozen points to " + << str.name() << endl; + for (const label pointi : pz) + { + str.write(mesh_.points()[pointi]); + } + } + + if (debug && returnReduce(unnamedClosureFaces.size(), sumOp<label>())) + { + mkDir(mesh_.time().timePath()); + OBJstream str(mesh_.time().timePath()/"unnamedClosureFaces.obj"); + Pout<< "Writing " << unnamedClosureFaces.size() + << " unnamedClosureFaces to " << str.name() << endl; + for (const label facei : unnamedClosureFaces) + { + str.write(mesh_.faces()[facei], mesh_.points(), false); + } + } + if (debug && returnReduce(namedClosureFaces.size(), sumOp<label>())) + { + mkDir(mesh_.time().timePath()); + OBJstream str(mesh_.time().timePath()/"namedClosureFaces.obj"); + Pout<< "Writing " << namedClosureFaces.size() + << " namedClosureFaces to " << str.name() << endl; + for (const label facei : namedClosureFaces) + { + str.write(mesh_.faces()[facei], mesh_.points(), false); + } + } } + // 2. Walk from locationsInMesh. Hard set cellZones. // Note: walk through faceZones! (these might get overridden later) @@ -3369,24 +3662,31 @@ void Foam::meshRefinement::handleSnapProblems << endl; labelList facePatch; + labelList faceZone; if (useTopologicalSnapDetection) { - facePatch = markFacesOnProblemCells + markFacesOnProblemCells ( motionDict, removeEdgeConnectedCells, perpendicularAngle, - globalToMasterPatch + globalToMasterPatch, + + facePatch, + faceZone ); } else { - facePatch = markFacesOnProblemCellsGeometric + markFacesOnProblemCellsGeometric ( snapParams, motionDict, globalToMasterPatch, - globalToSlavePatch + globalToSlavePatch, + + facePatch, + faceZone ); } Info<< "Analyzed problem cells in = " @@ -3416,6 +3716,47 @@ void Foam::meshRefinement::handleSnapProblems ++runTime; } + + // Add faces-to-baffle to faceZone. For now do this outside of topoChanges + { + const faceZoneMesh& fzs = mesh_.faceZones(); + List<DynamicList<label>> zoneToFaces(fzs.size()); + List<DynamicList<bool>> zoneToFlip(fzs.size()); + + // Start off with original contents + forAll(fzs, zonei) + { + zoneToFaces[zonei].append(fzs[zonei]); + zoneToFlip[zonei].append(fzs[zonei].flipMap()); + } + + // Add any to-be-patched face + forAll(facePatch, facei) + { + if (facePatch[facei] != -1) + { + label zonei = faceZone[facei]; + if (zonei != -1) + { + zoneToFaces[zonei].append(facei); + zoneToFlip[zonei].append(false); + } + } + } + + forAll(zoneToFaces, zonei) + { + surfaceZonesInfo::addFaceZone + ( + fzs[zonei].name(), + zoneToFaces[zonei], + zoneToFlip[zonei], + mesh_ + ); + } + } + + // Create baffles with same owner and neighbour for now. createBaffles(facePatch, facePatch); @@ -4168,7 +4509,7 @@ void Foam::meshRefinement::baffleAndSplitMesh const pointField& locationsInMesh, const wordList& zonesInMesh, const pointField& locationsOutsideMesh, - const writer<scalar>& leakPathFormatter + const refPtr<writer<scalar>>& leakPathFormatter ) { // Introduce baffles @@ -4194,6 +4535,8 @@ void Foam::meshRefinement::baffleAndSplitMesh locationsInMesh, zonesInMesh, + locationsOutsideMesh, + refPtr<writer<scalar>>(nullptr), neiLevel, neiCc, @@ -4266,6 +4609,8 @@ void Foam::meshRefinement::baffleAndSplitMesh locationsInMesh, zonesInMesh, + locationsOutsideMesh, + refPtr<writer<scalar>>(nullptr), neiLevel, neiCc, @@ -4347,8 +4692,7 @@ void Foam::meshRefinement::mergeFreeStandingBaffles const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, const pointField& locationsInMesh, - const pointField& locationsOutsideMesh, - const writer<scalar>& leakPathFormatter + const pointField& locationsOutsideMesh ) { // Merge baffles @@ -4416,7 +4760,7 @@ void Foam::meshRefinement::mergeFreeStandingBaffles locationsInMesh, locationsOutsideMesh, true, // Exit if any connection between inside and outside - leakPathFormatter + refPtr<writer<scalar>>(nullptr) //leakPathFormatter ); @@ -4461,6 +4805,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh locationsInMesh, zonesInMesh, + locationsOutsideMesh, + leakPathFormatter, neiLevel, neiCc, @@ -4529,6 +4875,122 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh const labelList& faceOwner = mesh_.faceOwner(); const labelList& faceNeighbour = mesh_.faceNeighbour(); + + // Checks + for (label facei = 0; facei < mesh_.nInternalFaces(); facei++) + { + if (ownPatch[facei] == -1 && neiPatch[facei] != -1) + { + FatalErrorInFunction << "Problem in face:" << facei + << " at:" << mesh_.faceCentres()[facei] + << " ownPatch:" << ownPatch[facei] + << " neiPatch:" << neiPatch[facei] + << exit(FatalError); + } + else + { + // Check if cellRegion indeed limited by patch + const label ownRegion = cellRegion[faceOwner[facei]]; + const label neiRegion = cellRegion[faceNeighbour[facei]]; + if (ownRegion != neiRegion) + { + if (ownPatch[facei] == -1) + { + FatalErrorInFunction << "Problem in face:" << facei + << " at:" << mesh_.faceCentres()[facei] + << " ownPatch:" << ownPatch[facei] + << " ownRegion:" << ownRegion + << " neiPatch:" << neiPatch[facei] + << " neiRegion:" << neiRegion + << exit(FatalError); + } + } + } + } + + // Determine on original data the nearest face. This is used as a fall-back + // to set the patch if the nBufferLayers walking didn't work. + labelList nearestOwnPatch; + if (nBufferLayers) + { + DynamicList<label> startFaces; + forAll(ownPatch, facei) + { + if (ownPatch[facei] != -1) + { + startFaces.append(facei); + } + } + + // Per face the index to the start face. + labelList faceToStart; + autoPtr<mapDistribute> mapPtr; + nearestFace + ( + startFaces, + bitSet(mesh_.nFaces()), // no blocked faces + mapPtr, + faceToStart, + nBufferLayers+4 // bit more than nBufferLayers since + // walking face-cell-face + ); + + // Use map to push ownPatch to all reached faces + labelList startOwnPatch(ownPatch, startFaces); + mapPtr().distribute(startOwnPatch); + + nearestOwnPatch.setSize(mesh_.nFaces()); + nearestOwnPatch = -1; + forAll(faceToStart, facei) + { + const label sloti = faceToStart[facei]; + if (sloti != -1) + { + nearestOwnPatch[facei] = startOwnPatch[sloti]; + } + } + } + + // Leak closure: + // ~~~~~~~~~~~~~ + // We do not want to add buffer layers on the frozen points/faces + // since these are the exact faces needed to close a hole (to an + // locationOutsideMesh). Adding even + // a single layer of cells would mean that in further manipulation there + // is now no path to the locationOutsideMesh so the layer closure does + // not get triggered and we keep the added 1 layer of cells on the + // closure faces. + bitSet isFrozenPoint(mesh_.nPoints()); + bitSet isFrozenFace(mesh_.nFaces()); + + if (nBufferLayers) + { + const labelListList& pointFaces = mesh_.pointFaces(); + const pointZoneMesh& pzs = mesh_.pointZones(); + const label pointZonei = pzs.findZoneID("frozenPoints"); + if (pointZonei != -1) + { + const pointZone& pz = pzs[pointZonei]; + isFrozenPoint.set(pz); + for (const label pointi : pz) + { + isFrozenFace.set(pointFaces[pointi]); + } + } + + const faceZoneMesh& fzs = mesh_.faceZones(); + const label faceZonei = fzs.findZoneID("frozenFaces"); + if (faceZonei != -1) + { + const faceZone& fz = fzs[faceZonei]; + isFrozenFace.set(fz); + for (const label facei : fz) + { + isFrozenPoint.set(mesh_.faces()[facei]); + } + } + } + // Patch for exposed faces for lack of anything sensible. label defaultPatch = 0; if (globalToMasterPatch.size()) @@ -4542,33 +5004,43 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh labelList pointBaffle(mesh_.nPoints(), -1); - forAll(faceNeighbour, faceI) + forAll(faceNeighbour, facei) { - const face& f = mesh_.faces()[faceI]; + if (!isFrozenFace[facei]) + { + const face& f = mesh_.faces()[facei]; - const label ownRegion = cellRegion[faceOwner[faceI]]; - const label neiRegion = cellRegion[faceNeighbour[faceI]]; + const label ownRegion = cellRegion[faceOwner[facei]]; + const label neiRegion = cellRegion[faceNeighbour[facei]]; - if (ownRegion == -1 && neiRegion != -1) - { - // Note max(..) since possibly regionSplit might have split - // off extra unreachable parts of mesh. Note: or can this only - // happen for boundary faces? - forAll(f, fp) + if (ownRegion == -1 && neiRegion != -1) { - pointBaffle[f[fp]] = max(defaultPatch, ownPatch[faceI]); - } - } - else if (ownRegion != -1 && neiRegion == -1) - { - label newPatchI = neiPatch[faceI]; - if (newPatchI == -1) - { - newPatchI = max(defaultPatch, ownPatch[faceI]); + // Note max(..) since possibly regionSplit might have split + // off extra unreachable parts of mesh. Note: or can this + // only happen for boundary faces? + forAll(f, fp) + { + if (!isFrozenPoint[f[fp]]) + { + pointBaffle[f[fp]] = + max(defaultPatch, ownPatch[facei]); + } + } } - forAll(f, fp) + else if (ownRegion != -1 && neiRegion == -1) { - pointBaffle[f[fp]] = newPatchI; + label newPatchi = neiPatch[facei]; + if (newPatchi == -1) + { + newPatchi = max(defaultPatch, ownPatch[facei]); + } + forAll(f, fp) + { + if (!isFrozenPoint[f[fp]]) + { + pointBaffle[f[fp]] = newPatchi; + } + } } } } @@ -4577,21 +5049,29 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh syncTools::swapBoundaryCellList(mesh_, cellRegion, neiCellRegion); for ( - label faceI = mesh_.nInternalFaces(); - faceI < mesh_.nFaces(); - faceI++ + label facei = mesh_.nInternalFaces(); + facei < mesh_.nFaces(); + facei++ ) { - const face& f = mesh_.faces()[faceI]; + if (!isFrozenFace[facei]) + { + const face& f = mesh_.faces()[facei]; - const label ownRegion = cellRegion[faceOwner[faceI]]; - const label neiRegion = neiCellRegion[faceI-mesh_.nInternalFaces()]; + const label ownRegion = cellRegion[faceOwner[facei]]; + const label neiRegion = + neiCellRegion[facei-mesh_.nInternalFaces()]; - if (ownRegion == -1 && neiRegion != -1) - { - forAll(f, fp) + if (ownRegion == -1 && neiRegion != -1) { - pointBaffle[f[fp]] = max(defaultPatch, ownPatch[faceI]); + forAll(f, fp) + { + if (!isFrozenPoint[f[fp]]) + { + pointBaffle[f[fp]] = + max(defaultPatch, ownPatch[facei]); + } + } } } } @@ -4610,19 +5090,19 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh const labelListList& pointFaces = mesh_.pointFaces(); - forAll(pointFaces, pointI) + forAll(pointFaces, pointi) { - if (pointBaffle[pointI] != -1) + if (pointBaffle[pointi] != -1) { - const labelList& pFaces = pointFaces[pointI]; + const labelList& pFaces = pointFaces[pointi]; - forAll(pFaces, pFaceI) + forAll(pFaces, pFacei) { - label faceI = pFaces[pFaceI]; + const label facei = pFaces[pFacei]; - if (ownPatch[faceI] == -1) + if (!isFrozenFace[facei] && ownPatch[facei] == -1) { - ownPatch[faceI] = pointBaffle[pointI]; + ownPatch[facei] = pointBaffle[pointi]; } } } @@ -4634,11 +5114,11 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh labelList newOwnPatch(ownPatch); - forAll(ownPatch, faceI) + forAll(ownPatch, facei) { - if (ownPatch[faceI] != -1) + if (!isFrozenFace[facei] && ownPatch[facei] != -1) { - label own = faceOwner[faceI]; + const label own = faceOwner[facei]; if (cellRegion[own] == -1) { @@ -4647,15 +5127,16 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh const cell& ownFaces = mesh_.cells()[own]; forAll(ownFaces, j) { - if (ownPatch[ownFaces[j]] == -1) + const label ownFacei = ownFaces[j]; + if (!isFrozenFace[ownFacei] && ownPatch[ownFacei] == -1) { - newOwnPatch[ownFaces[j]] = ownPatch[faceI]; + newOwnPatch[ownFacei] = ownPatch[facei]; } } } - if (mesh_.isInternalFace(faceI)) + if (mesh_.isInternalFace(facei)) { - label nei = faceNeighbour[faceI]; + const label nei = faceNeighbour[facei]; if (cellRegion[nei] == -1) { @@ -4664,9 +5145,11 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh const cell& neiFaces = mesh_.cells()[nei]; forAll(neiFaces, j) { - if (ownPatch[neiFaces[j]] == -1) + const label neiFacei = neiFaces[j]; + const bool isFrozen = isFrozenFace[neiFacei]; + if (!isFrozen && ownPatch[neiFacei] == -1) { - newOwnPatch[neiFaces[j]] = ownPatch[faceI]; + newOwnPatch[neiFacei] = ownPatch[facei]; } } } @@ -4680,17 +5163,16 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh } - // Subset // ~~~~~~ // Get cells to remove DynamicList<label> cellsToRemove(mesh_.nCells()); - forAll(cellRegion, cellI) + forAll(cellRegion, celli) { - if (cellRegion[cellI] == -1) + if (cellRegion[celli] == -1) { - cellsToRemove.append(cellI); + cellsToRemove.append(celli); } } cellsToRemove.shrink(); @@ -4706,29 +5188,51 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh removeCells cellRemover(mesh_); // Pick up patches for exposed faces - labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove)); + const labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove)); labelList exposedPatches(exposedFaces.size()); + label nUnpatched = 0; + forAll(exposedFaces, i) { - label faceI = exposedFaces[i]; + label facei = exposedFaces[i]; - if (ownPatch[faceI] != -1) + if (ownPatch[facei] != -1) { - exposedPatches[i] = ownPatch[faceI]; + exposedPatches[i] = ownPatch[facei]; } else { - WarningInFunction - << "For exposed face " << faceI - << " fc:" << mesh_.faceCentres()[faceI] - << " found no patch." << endl - << " Taking patch " << defaultPatch - << " instead." << endl; - exposedPatches[i] = defaultPatch; + const label fallbackPatch = + ( + nearestOwnPatch.size() + ? nearestOwnPatch[facei] + : defaultPatch + ); + if (nUnpatched == 0) + { + WarningInFunction + << "For exposed face " << facei + << " fc:" << mesh_.faceCentres()[facei] + << " found no patch." << endl + << " Taking patch " << fallbackPatch + << " instead. Suppressing future warnings" << endl; + } + nUnpatched++; + + exposedPatches[i] = fallbackPatch; } } + reduce(nUnpatched, sumOp<label>()); + if (nUnpatched > 0) + { + Info<< "Detected " << nUnpatched << " faces out of " + << returnReduce(exposedFaces.size(), sumOp<label>()) + << " for which the default patch " << defaultPatch + << " will be used" << endl; + } + return doRemoveCells ( cellsToRemove, @@ -4746,7 +5250,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeLimitShells const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, const pointField& locationsInMesh, - const wordList& zonesInMesh + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh ) { // Determine patches to put intersections into @@ -4766,6 +5271,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeLimitShells locationsInMesh, zonesInMesh, + locationsOutsideMesh, + refPtr<writer<scalar>>(nullptr), neiLevel, neiCc, @@ -4788,7 +5295,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeLimitShells { if (levelShell[celli] != -1) { - // Mark cell region so it gets deletec + // Mark cell region so it gets deleted cellRegion[celli] = -1; } } @@ -5066,6 +5573,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify const label nErodeCellZones, const pointField& locationsInMesh, const wordList& zonesInMesh, + const pointField& locationsOutsideMesh, + const refPtr<writer<scalar>>& leakPathFormatter, wordPairHashTable& zonesToFaceZone ) { @@ -5145,6 +5654,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify -1, // Set all cells with cellToZone -2 to -1 locationsInMesh, zonesInMesh, + locationsOutsideMesh, + leakPathFormatter, cellToZone, unnamedRegion1, @@ -5383,6 +5894,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify if (debug) { OBJstream str(mesh_.time().path()/"freeStanding.obj"); + Pout<< "meshRefinement::zonify : dumping faceZone faces to " + << str.name() << endl; str.write(patch.localFaces(), patch.localPoints(), false); } diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBlock.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBlock.C index 4f23065d021..f17d933a832 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBlock.C +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementBlock.C @@ -32,13 +32,27 @@ License #include "removeCells.H" #include "unitConversion.H" #include "bitSet.H" +#include "volFields.H" +// Leak path +#include "shortestPathSet.H" +#include "meshSearch.H" +#include "topoDistanceData.H" #include "FaceCellWave.H" +#include "removeCells.H" +#include "regionSplit.H" + #include "volFields.H" #include "wallPoints.H" #include "searchableSurfaces.H" #include "distributedTriSurfaceMesh.H" +#include "holeToFace.H" +#include "refinementParameters.H" +#include "uindirectPrimitivePatch.H" +#include "OBJstream.H" +#include "PatchTools.H" + // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // //Foam::label Foam::meshRefinement::markFakeGapRefinement @@ -1128,4 +1142,1024 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeGapCells } +void Foam::meshRefinement::selectIntersectedFaces +( + const labelList& selectedSurfaces, + boolList& isBlockedFace +) const +{ + // Like meshRefinement::selectSeparatedCoupledFaces. tbd: convert to bitSet + + // Check that no connection between inside and outside points + isBlockedFace.setSize(mesh_.nFaces(), false); + + // Block off separated couples. + selectSeparatedCoupledFaces(isBlockedFace); + + // Block off intersections with selected surfaces + + // Mark per face (for efficiency) + boolList isSelectedSurf(surfaces_.surfaces().size(), false); + UIndirectList<bool>(isSelectedSurf, selectedSurfaces) = true; + + forAll(surfaceIndex_, facei) + { + const label surfi = surfaceIndex_[facei]; + if (surfi != -1 && isSelectedSurf[surfi]) + { + isBlockedFace[facei] = true; + } + } +} + + +//Foam::labelList Foam::meshRefinement::detectLeakCells +//( +// const boolList& isBlockedFace, +// const labelList& leakFaces, +// const labelList& seedCells +//) const +//{ +// int dummyTrackData = 0; +// List<topoDistanceData<label>> allFaceInfo(mesh_.nFaces()); +// List<topoDistanceData<label>> allCellInfo(mesh_.nCells()); +// +// // Block faces +// forAll(isBlockedFace, facei) +// { +// if (isBlockedFace[facei]) +// { +// allFaceInfo[facei] = topoDistanceData<label>(labelMax, 123); +// } +// } +// for (const label facei : leakFaces) +// { +// allFaceInfo[facei] = topoDistanceData<label>(labelMax, 123); +// } +// +// // Walk from inside cell +// DynamicList<topoDistanceData<label>> faceDist; +// DynamicList<label> cFaces1; +// for (const label celli : seedCells) +// { +// if (celli != -1) +// { +// const labelList& cFaces = mesh_.cells()[celli]; +// faceDist.reserve(cFaces.size()); +// cFaces1.reserve(cFaces.size()); +// +// for (const label facei : cFaces) +// { +// if (!allFaceInfo[facei].valid(dummyTrackData)) +// { +// cFaces1.append(facei); +// faceDist.append(topoDistanceData<label>(0, 123)); +// } +// } +// } +// } +// +// // Walk through face-cell wave till all cells are reached +// FaceCellWave<topoDistanceData<label>> wallDistCalc +// ( +// mesh_, +// cFaces1, +// faceDist, +// allFaceInfo, +// allCellInfo, +// mesh_.globalData().nTotalCells()+1 // max iterations +// ); +// +// label nRemove = 0; +// for (const label facei : leakFaces) +// { +// const label own = mesh_.faceOwner()[facei]; +// if (!allCellInfo[own].valid(dummyTrackData)) +// { +// nRemove++; +// } +// if (mesh_.isInternalFace(facei)) +// { +// const label nei = mesh_.faceNeighbour()[facei]; +// if (!allCellInfo[nei].valid(dummyTrackData)) +// { +// nRemove++; +// } +// } +// } +// +// labelList cellsToRemove(nRemove); +// nRemove = 0; +// for (const label facei : leakFaces) +// { +// const label own = mesh_.faceOwner()[facei]; +// if (!allCellInfo[own].valid(dummyTrackData)) +// { +// cellsToRemove[nRemove++] = own; +// } +// if (mesh_.isInternalFace(facei)) +// { +// const label nei = mesh_.faceNeighbour()[facei]; +// if (!allCellInfo[nei].valid(dummyTrackData)) +// { +// cellsToRemove[nRemove++] = nei; +// } +// } +// } +// +// if (debug) +// { +// volScalarField fld +// ( +// IOobject +// ( +// "cellsToKeep", +// mesh_.time().timeName(), +// mesh_, +// IOobject::NO_READ, +// IOobject::NO_WRITE +// ), +// mesh_, +// dimensionedScalar(dimless, Zero) +// ); +// forAll(allCellInfo, celli) +// { +// if (allCellInfo[celli].valid(dummyTrackData)) +// { +// fld[celli] = allCellInfo[celli].distance(); +// } +// } +// forAll(fld.boundaryField(), patchi) +// { +// const polyPatch& pp = mesh_.boundaryMesh()[patchi]; +// SubList<topoDistanceData<label>> p(pp.patchSlice(allFaceInfo)); +// scalarField pfld +// ( +// fld.boundaryField()[patchi].size(), +// Zero +// ); +// forAll(pfld, i) +// { +// if (p[i].valid(dummyTrackData)) +// { +// pfld[i] = 1.0*p[i].distance(); +// } +// } +// fld.boundaryFieldRef()[patchi] == pfld; +// } +// //Note: do not swap cell values so do not do +// //fld.correctBoundaryConditions(); +// Pout<< "Writing distance field for initial cells " +// << seedCells << " to " << fld.objectPath() << endl; +// fld.write(); +// } +// +// return cellsToRemove; +//} +// +// +//Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::removeLeakCells +//( +// const labelList& globalToMasterPatch, +// const labelList& globalToSlavePatch, +// const pointField& locationsInMesh, +// const wordList& zonesInMesh, +// const pointField& locationsOutsideMesh, +// const labelList& selectedSurfaces +//) +//{ +// boolList isBlockedFace; +// selectIntersectedFaces(selectedSurfaces, isBlockedFace); +// +// // Determine cell regions +// const regionSplit cellRegion(mesh_, isBlockedFace); +// +// // Detect locationsInMesh regions +// labelList insideCells(locationsInMesh.size(), -1); +// labelList insideRegions(locationsInMesh.size(), -1); +// forAll(locationsInMesh, i) +// { +// insideCells[i] = findCell +// ( +// mesh_, +// mergeDistance_*vector::one, //perturbVec, +// locationsInMesh[i] +// ); +// if (insideCells[i] != -1) +// { +// insideRegions[i] = cellRegion[insideCells[i]]; +// } +// reduce(insideRegions[i], maxOp<label>()); +// +// if (insideRegions[i] == -1) +// { +// // See if we can perturb a bit +// insideCells[i] = findCell +// ( +// mesh_, +// mergeDistance_*vector::one, //perturbVec, +// locationsInMesh[i]+mergeDistance_*vector::one +// ); +// if (insideCells[i] != -1) +// { +// insideRegions[i] = cellRegion[insideCells[i]]; +// } +// reduce(insideRegions[i], maxOp<label>()); +// +// if (insideRegions[i] == -1) +// { +// FatalErrorInFunction +// << "Cannot find locationInMesh " << locationsInMesh[i] +// << " on any processor" << exit(FatalError); +// } +// } +// } +// +// +// // Check that all the locations outside the +// // mesh do not conflict with those inside +// +// bool haveLeak = false; +// forAll(locationsOutsideMesh, i) +// { +// // Find the region containing the point +// label regioni = findRegion +// ( +// mesh_, +// cellRegion, +// mergeDistance_*vector::one, //perturbVec, +// locationsOutsideMesh[i] +// ); +// +// if (regioni != -1) +// { +// // Check for locationsOutsideMesh overlapping with inside ones +// if (insideRegions.find(regioni) != -1) +// { +// haveLeak = true; +// WarningInFunction +// << "Outside location " << locationsOutsideMesh[i] +// << " in region " << regioni +// << " is connected to one of the inside points " +// << locationsInMesh << endl; +// } +// } +// } +// +// +// autoPtr<mapPolyMesh> mapPtr; +// if (returnReduce(haveLeak, orOp<bool>())) +// { +// // Use shortestPathSet to provide a minimum set of faces needed +// // to close hole. Tbd: maybe directly use wave? +// meshSearch searchEngine(mesh_); +// shortestPathSet leakPath +// ( +// "leakPath", +// mesh_, +// searchEngine, +// coordSet::coordFormatNames[coordSet::coordFormat::DISTANCE], +// true, +// 50, // tbd. Number of iterations. This is the maximum +// // number of faces in the leak hole +// +// //pbm.groupPatchIDs()["wall"], // patch to grow from +// meshedPatches(), // patch to grow from +// +// locationsInMesh, +// locationsOutsideMesh, +// isBlockedFace +// ); +// +// +// // Use leak path to find minimum set of cells to delete +// const labelList cellsToRemove +// ( +// detectLeakCells +// ( +// isBlockedFace, +// leakPath.leakFaces(), +// insideCells +// ) +// ); +// +// // Re-do intersections to find nearest unnamed surface +// const label defaultRegion +// ( +// surfaces().globalRegion +// ( +// selectedSurfaces[0], +// 0 +// ) +// ); +// +// const labelList nearestRegion +// ( +// nearestIntersection +// ( +// selectedSurfaces, +// defaultRegion +// ) +// ); +// +// +// // Remove cells +// removeCells cellRemover(mesh_); +// const labelList exposedFaces +// ( +// cellRemover.getExposedFaces(cellsToRemove) +// ); +// +// labelList exposedPatches(exposedFaces.size()); +// forAll(exposedFaces, i) +// { +// label facei = exposedFaces[i]; +// exposedPatches[i] = globalToMasterPatch[nearestRegion[facei]]; +// } +// +// mapPtr = doRemoveCells +// ( +// cellsToRemove, +// exposedFaces, +// exposedPatches, +// cellRemover +// ); +// +// +// // Put the exposed faces into a special faceZone +// { +// // Add to "frozenFaces" zone +// faceZoneMesh& faceZones = mesh_.faceZones(); +// +// // Get current frozen faces (if any) +// bitSet isFrozenFace(mesh_.nFaces()); +// label zonei = faceZones.findZoneID("frozenFaces"); +// if (zonei != -1) +// { +// const bitSet oldSet(mesh_.nFaces(), faceZones[zonei]); +// isFrozenFace.set(oldSet); +// } +// +// // Add newly exposed faces (if not yet in any faceZone!) +// const labelList exposed +// ( +// renumber +// ( +// mapPtr().reverseFaceMap(), +// exposedFaces +// ) +// ); +// bitSet isZonedFace(mesh_.nFaces(), faceZones.zoneMap().toc()); +// for (const label facei : exposed) +// { +// if (!isZonedFace[facei]) +// { +// isFrozenFace.set(facei); +// } +// } +// +// syncTools::syncFaceList +// ( +// mesh_, +// isFrozenFace, +// orEqOp<unsigned int>(), +// 0u +// ); +// +// // Add faceZone if non existing +// faceZones.clearAddressing(); +// if (zonei == -1) +// { +// zonei = faceZones.size(); +// faceZones.setSize(zonei+1); +// faceZones.set +// ( +// zonei, +// new faceZone +// ( +// "frozenFaces", // name +// labelList(0), // addressing +// boolList(0), // flip +// zonei, // index +// faceZones // faceZoneMesh +// ) +// ); +// } +// +// // Update faceZone with new contents +// labelList frozenFaces(isFrozenFace.toc()); +// boolList frozenFlip(frozenFaces.size(), false); +// +// faceZones[zonei].resetAddressing +// ( +// std::move(frozenFaces), +// std::move(frozenFlip) +// ); +// } +// +// +// //// Put the exposed points into a special pointZone +// //if (false) +// //{ +// // const labelList meshFaceIDs +// // ( +// // renumber +// // ( +// // mapPtr().reverseFaceMap(), +// // exposedFaces +// // ) +// // ); +// // const uindirectPrimitivePatch pp +// // ( +// // UIndirectList<face>(mesh_.faces(), meshFaceIDs), +// // mesh_.points() +// // ); +// // +// // // Count number of faces per edge +// // const labelListList& edgeFaces = pp.edgeFaces(); +// // labelList nEdgeFaces(edgeFaces.size()); +// // forAll(edgeFaces, edgei) +// // { +// // nEdgeFaces[edgei] = edgeFaces[edgei].size(); +// // } +// // +// // // Sync across processor patches +// // if (Pstream::parRun()) +// // { +// // const globalMeshData& globalData = mesh_.globalData(); +// // const mapDistribute& map = globalData.globalEdgeSlavesMap(); +// // const indirectPrimitivePatch& cpp = +// // globalData.coupledPatch(); +// // +// // // Match pp edges to coupled edges +// // labelList patchEdges; +// // labelList coupledEdges; +// // PackedBoolList sameEdgeOrientation; +// // PatchTools::matchEdges +// // ( +// // pp, +// // cpp, +// // patchEdges, +// // coupledEdges, +// // sameEdgeOrientation +// // ); +// // +// // +// // // Convert patch-edge data into cpp-edge data +// // labelList coupledNEdgeFaces(map.constructSize(), Zero); +// // UIndirectList<label>(coupledNEdgeFaces, coupledEdges) = +// // UIndirectList<label>(nEdgeFaces, patchEdges); +// // +// // // Synchronise +// // globalData.syncData +// // ( +// // coupledNEdgeFaces, +// // globalData.globalEdgeSlaves(), +// // globalData.globalEdgeTransformedSlaves(), +// // map, +// // plusEqOp<label>() +// // ); +// // +// // // Convert back from cpp-edge to patch-edge +// // UIndirectList<label>(nEdgeFaces, patchEdges) = +// // UIndirectList<label>(coupledNEdgeFaces, coupledEdges); +// // } +// // +// // // Freeze all internal points +// // bitSet isFrozenPoint(mesh_.nPoints()); +// // forAll(nEdgeFaces, edgei) +// // { +// // if (nEdgeFaces[edgei] != 1) +// // { +// // const edge& e = pp.edges()[edgei]; +// // isFrozenPoint.set(pp.meshPoints()[e[0]]); +// // isFrozenPoint.set(pp.meshPoints()[e[1]]); +// // } +// // } +// // // Add to "frozenPoints" zone +// // pointZoneMesh& pointZones = mesh_.pointZones(); +// // pointZones.clearAddressing(); +// // label zonei = pointZones.findZoneID("frozenPoints"); +// // if (zonei != -1) +// // { +// // const bitSet oldSet(mesh_.nPoints(), pointZones[zonei]); +// // // Add to isFrozenPoint +// // isFrozenPoint.set(oldSet); +// // } +// // +// // syncTools::syncPointList +// // ( +// // mesh_, +// // isFrozenPoint, +// // orEqOp<unsigned int>(), +// // 0u +// // ); +// // +// // if (zonei == -1) +// // { +// // zonei = pointZones.size(); +// // pointZones.setSize(zonei+1); +// // pointZones.set +// // ( +// // zonei, +// // new pointZone +// // ( +// // "frozenPoints", // name +// // isFrozenPoint.sortedToc(), // addressing +// // zonei, // index +// // pointZones // pointZoneMesh +// // ) +// // ); +// // } +// //} +// +// +// if (debug&meshRefinement::MESH) +// { +// const_cast<Time&>(mesh_.time())++; +// +// Pout<< "Writing current mesh to time " +// << timeName() << endl; +// write +// ( +// meshRefinement::debugType(debug), +// meshRefinement::writeType +// ( +// meshRefinement::writeLevel() +// | meshRefinement::WRITEMESH +// ), +// mesh_.time().path()/timeName() +// ); +// Pout<< "Dumped mesh in = " +// << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl; +// } +// } +// return mapPtr; +//} + + +//Foam::autoPtr<Foam::mapDistribute> Foam::meshRefinement::nearestFace +//( +// const globalIndex& globalSeedFaces, +// const labelList& seedFaces, +// const labelList& closureFaces +//) const +//{ +// // Takes a set of faces for which we have information (seedFaces, +// // globalSeedFaces - these are e.g. intersected faces) and walks out +// // across nonSeedFace. Returns for +// // every nonSeedFace the nearest seed face (in global indexing). +// // Used e.g. in hole closing. Assumes that seedFaces, closureFaces +// // are a small subset of the master +// // so are not large - it uses edge addressing on the closureFaces +// +// +// if (seedFaces.size() != globalSeedFaces.localSize()) +// { +// FatalErrorInFunction << "problem : seedFaces:" << seedFaces.size() +// << " globalSeedFaces:" << globalSeedFaces.localSize() +// << exit(FatalError); +// } +// +// //// Mark mesh points that are used by any closureFaces. This is for +// //// initial filtering +// //bitSet isNonSeedPoint(mesh.nPoints()); +// //for (const label facei : closureFaces) +// //{ +// // isNonSeedPoint.set(mesh_.faces()[facei]); +// //} +// //syncTools::syncPointList +// //( +// // mesh_, +// // isNonSeedPoint, +// // orEqOp<unsigned int>(), +// // 0u +// //); +// +// // Make patch +// const uindirectPrimitivePatch pp +// ( +// IndirectList<face>(mesh_.faces(), closureFaces), +// mesh_.points() +// ); +// const edgeList& edges = pp.edges(); +// const labelList& mp = pp.meshPoints(); +// const label nBndEdges = pp.nEdges() - pp.nInternalEdges(); +// +// // For all faces in seedFaces mark the edge with a face. No special +// // handling for multiple faces sharing the edge - first one wins +// EdgeMap<label> edgeMap(pp.nEdges()); +// for (const label facei : seedFaces) +// { +// const label globalFacei = globalSeedFaces.toGlobal(facei); +// const face& f = mesh_.faces()[facei]; +// forAll(f, fp) +// { +// label nextFp = f.fcIndex(fp); +// edgeMap.insert(edge(f[fp], f[nextFp]), globalFacei); +// } +// } +// syncTools::syncEdgeMap(mesh_, edgeMap, maxEqOp<label>()); +// +// +// +// // Seed +// DynamicList<label> initialEdges(2*nBndEdges); +// DynamicList<edgeTopoDistanceData<label, uindirectPrimitivePatch>> +// initialEdgesInfo(2*nBndEdges); +// forAll(edges, edgei) +// { +// const edge& e = edges[edgei]; +// const edge meshE = edge(mp[e[0]], mp[e[1]]); +// +// EdgeMap<label>::const_iterator iter = edgeMap.find(meshE); +// if (iter.found()) +// { +// initialEdges.append(edgei); +// initialEdgesInfo.append +// ( +// edgeTopoDistanceData<label, uindirectPrimitivePatch> +// ( +// 0, // distance +// iter() // globalFacei +// ) +// ); +// } +// } +// +// // Data on all edges and faces +// List<edgeTopoDistanceData<label, uindirectPrimitivePatch>> allEdgeInfo +// ( +// pp.nEdges() +// ); +// List<edgeTopoDistanceData<label, uindirectPrimitivePatch>> allFaceInfo +// ( +// pp.size() +// ); +// +// // Walk +// PatchEdgeFaceWave +// < +// uindirectPrimitivePatch, +// edgeTopoDistanceData<label, uindirectPrimitivePatch> +// > calc +// ( +// mesh_, +// pp, +// initialEdges, +// initialEdgesInfo, +// allEdgeInfo, +// allFaceInfo, +// returnReduce(pp.nEdges(), sumOp<label>()) +// ); +// +// +// // Per non-seed face the seed face +// labelList closureToSeed(pp.size(), -1); +// forAll(allFaceInfo, facei) +// { +// if (allFaceInfo[facei].valid(calc.data())) +// { +// closureToSeed[facei] = allFaceInfo[facei].data(); +// } +// } +// +// List<Map<label>> compactMap; +// return autoPtr<mapDistribute>::New +// ( +// globalSeedFaces, +// closureToSeed, +// compactMap +// ); +//} + + +Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::blockLeakFaces +( + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh, + const labelList& selectedSurfaces +) +{ + // Problem: this is based on cached intersection information. This might + // not include the surface we actually want to use. In which case the + // surface would not be seen as intersected! + boolList isBlockedFace; + selectIntersectedFaces(selectedSurfaces, isBlockedFace); + + // Determine cell regions + const regionSplit cellRegion(mesh_, isBlockedFace); + + // Detect locationsInMesh regions + labelList insideCells(locationsInMesh.size(), -1); + labelList insideRegions(locationsInMesh.size(), -1); + forAll(locationsInMesh, i) + { + insideCells[i] = findCell + ( + mesh_, + mergeDistance_*vector::one, //perturbVec, + locationsInMesh[i] + ); + if (insideCells[i] != -1) + { + insideRegions[i] = cellRegion[insideCells[i]]; + } + reduce(insideRegions[i], maxOp<label>()); + + if (insideRegions[i] == -1) + { + // See if we can perturb a bit + insideCells[i] = findCell + ( + mesh_, + mergeDistance_*vector::one, //perturbVec, + locationsInMesh[i]+mergeDistance_*vector::one + ); + if (insideCells[i] != -1) + { + insideRegions[i] = cellRegion[insideCells[i]]; + } + reduce(insideRegions[i], maxOp<label>()); + + if (insideRegions[i] == -1) + { + FatalErrorInFunction + << "Cannot find locationInMesh " << locationsInMesh[i] + << " on any processor" << exit(FatalError); + } + } + } + + + // Check that all the locations outside the + // mesh do not conflict with those inside + + bool haveLeak = false; + forAll(locationsOutsideMesh, i) + { + // Find the region containing the point + label regioni = findRegion + ( + mesh_, + cellRegion, + mergeDistance_*vector::one, //perturbVec, + locationsOutsideMesh[i] + ); + + if (regioni != -1) + { + // Check for locationsOutsideMesh overlapping with inside ones + if (insideRegions.find(regioni) != -1) + { + haveLeak = true; + WarningInFunction + << "Outside location " << locationsOutsideMesh[i] + << " in region " << regioni + << " is connected to one of the inside points " + << locationsInMesh << endl; + } + } + } + + + autoPtr<mapPolyMesh> mapPtr; + if (returnReduce(haveLeak, orOp<bool>())) + { + // Use holeToFace to provide a minimum set of faces needed + // to close hole. + + const List<pointField> allLocations + ( + refinementParameters::zonePoints + ( + locationsInMesh, + zonesInMesh, + locationsOutsideMesh + ) + ); + + const labelList blockingFaces(findIndices(isBlockedFace, true)); + + labelList closureFaces; + labelList closureToBlocked; + autoPtr<mapDistribute> closureMapPtr; + { + const globalIndex globalBlockingFaces(blockingFaces.size()); + + closureMapPtr = holeToFace::calcClosure + ( + mesh_, + allLocations, + blockingFaces, + globalBlockingFaces, + true, // allow erosion + + closureFaces, + closureToBlocked + ); + + if (debug) + { + Pout<< "meshRefinement::blockLeakFaces :" + << " found closure faces:" << closureFaces.size() + << " map:" << closureMapPtr.valid() << endl; + } + + if (!closureMapPtr.valid()) + { + FatalErrorInFunction + << "have leak but did not find any closure faces" + << exit(FatalError); + } + } + + // Baffle faces + labelList ownPatch(mesh_.nFaces(), -1); + labelList neiPatch(mesh_.nFaces(), -1); + + // Keep (global) boundary faces in their patch + { + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + for (label patchi = 0; patchi < patches.nNonProcessor(); ++patchi) + { + const polyPatch& pp = patches[patchi]; + + forAll(pp, i) + { + ownPatch[pp.start()+i] = patchi; + neiPatch[pp.start()+i] = patchi; + } + } + } + + const faceZoneMesh& fzs = mesh_.faceZones(); + + // Mark zone per face + labelList faceToZone(mesh_.nFaces(), -1); + boolList faceToFlip(mesh_.nFaces(), false); + forAll(fzs, zonei) + { + const labelList& addressing = fzs[zonei]; + const boolList& flipMap = fzs[zonei].flipMap(); + + forAll(addressing, i) + { + faceToZone[addressing[i]] = zonei; + faceToFlip[addressing[i]] = flipMap[i]; + } + } + + + // Fetch patch and zone info from blockingFaces + labelList packedOwnPatch(labelUIndList(ownPatch, blockingFaces)); + closureMapPtr->distribute(packedOwnPatch); + labelList packedNeiPatch(labelUIndList(neiPatch, blockingFaces)); + closureMapPtr->distribute(packedNeiPatch); + labelList packedZone(labelUIndList(faceToZone, blockingFaces)); + closureMapPtr->distribute(packedZone); + boolList packedFlip(UIndirectList<bool>(faceToFlip, blockingFaces)); + closureMapPtr->distribute(packedFlip); + forAll(closureFaces, i) + { + const label facei = closureFaces[i]; + const label sloti = closureToBlocked[i]; + + if (sloti != -1) + { + // TBD. how to orient own/nei patch? + ownPatch[facei] = packedOwnPatch[sloti]; + neiPatch[facei] = packedNeiPatch[sloti]; + faceToZone[facei] = packedZone[sloti]; + faceToFlip[facei] = packedFlip[sloti]; + } + } + + + // Add faces to faceZone. For now do this outside of createBaffles + // below + { + List<DynamicList<label>> zoneToFaces(fzs.size()); + List<DynamicList<bool>> zoneToFlip(fzs.size()); + + // Add any to-be-patched face + forAll(faceToZone, facei) + { + const label zonei = faceToZone[facei]; + if (zonei != -1) + { + zoneToFaces[zonei].append(facei); + zoneToFlip[zonei].append(faceToFlip[facei]); + } + } + + forAll(zoneToFaces, zonei) + { + surfaceZonesInfo::addFaceZone + ( + fzs[zonei].name(), + zoneToFaces[zonei], + zoneToFlip[zonei], + mesh_ + ); + } + } + + // Put the points of closureFaces into a special pointZone + { + const uindirectPrimitivePatch pp + ( + UIndirectList<face>(mesh_.faces(), closureFaces), + mesh_.points() + ); + + // Count number of faces per edge + const labelList nEdgeFaces(countEdgeFaces(pp)); + + // Freeze all internal points + bitSet isFrozenPoint(mesh_.nPoints()); + forAll(nEdgeFaces, edgei) + { + if (nEdgeFaces[edgei] != 1) + { + const edge& e = pp.edges()[edgei]; + isFrozenPoint.set(pp.meshPoints()[e[0]]); + isFrozenPoint.set(pp.meshPoints()[e[1]]); + } + } + + // Lookup/add pointZone and include its points + pointZoneMesh& pointZones = + const_cast<pointZoneMesh&>(mesh_.pointZones()); + const label zonei = addPointZone("frozenPoints"); + const bitSet oldSet(mesh_.nPoints(), pointZones[zonei]); + isFrozenPoint.set(oldSet); + + syncTools::syncPointList + ( + mesh_, + isFrozenPoint, + orEqOp<unsigned int>(), + 0u + ); + + // Override addressing + pointZones.clearAddressing(); + pointZones[zonei] = isFrozenPoint.sortedToc(); + } + + + + // Create baffles for faces + mapPtr = createBaffles(ownPatch, neiPatch); + + //// Put the exposed faces into a special faceZone + //{ + // // Add newly exposed faces (if not yet in any faceZone!) + // const labelList exposed + // ( + // renumber + // ( + // mapPtr().reverseFaceMap(), + // blockingFaces + // ) + // ); + // + // surfaceZonesInfo::addFaceZone + // ( + // "frozenFaces", + // exposed, + // boolList(exposed.size(), false), + // mesh_ + // ); + //} + + + if (debug&meshRefinement::MESH) + { + const_cast<Time&>(mesh_.time())++; + + Pout<< "Writing current mesh to time " + << timeName() << endl; + write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh_.time().path()/timeName() + ); + Pout<< "Dumped mesh in = " + << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl; + } + } + return mapPtr; +} + + // ************************************************************************* // diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementProblemCells.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementProblemCells.C index 58d964a33e6..fa541ba8a6e 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementProblemCells.C +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementProblemCells.C @@ -380,12 +380,15 @@ bool Foam::meshRefinement::isCollapsedCell // are not baffled at this stage) // Used to remove cells by baffling all their faces and have the // splitMeshRegions chuck away non used regions. -Foam::labelList Foam::meshRefinement::markFacesOnProblemCells +void Foam::meshRefinement::markFacesOnProblemCells ( const dictionary& motionDict, const bool removeEdgeConnectedCells, const scalarField& perpendicularAngle, - const labelList& globalToPatch + const labelList& globalToPatch, + + labelList& facePatch, + labelList& faceZone ) const { const labelList& cellLevel = meshCutter_.cellLevel(); @@ -401,7 +404,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells // Fill boundary data. All elements on meshed patches get marked. // Get the labels of added patches. - labelList adaptPatchIDs(meshedPatches()); + const labelList adaptPatchIDs(meshedPatches()); forAll(adaptPatchIDs, i) { @@ -424,13 +427,18 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells } - // Per face the nearest adaptPatch - const labelList nearestAdaptPatch(nearestPatch(adaptPatchIDs)); + // Per mesh face the nearest adaptPatch and its faceZone (if any) + labelList nearestAdaptPatch; + labelList nearestAdaptZone; + nearestPatch(adaptPatchIDs, nearestAdaptPatch, nearestAdaptZone); // Per internal face (boundary faces not used) the patch that the // baffle should get (or -1) - labelList facePatch(mesh_.nFaces(), -1); + facePatch.setSize(mesh_.nFaces()); + facePatch = -1; + faceZone.setSize(mesh_.nFaces()); + faceZone = -1; // Swap neighbouring cell centres and cell level labelList neiLevel(mesh_.nBoundaryFaces()); @@ -474,6 +482,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells if (facePatch[facei] == -1 && mesh_.isInternalFace(facei)) { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; nBaffleFaces++; // Mark face as a 'boundary' @@ -714,6 +723,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells ) { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; nBaffleFaces++; // Mark face as a 'boundary' @@ -795,6 +805,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells ) { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; nBaffleFaces++; // Mark face as a 'boundary' @@ -882,6 +893,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells else { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; nBaffleFaces++; // Do NOT update boundary data since this would grow blocked @@ -937,6 +949,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells else { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; if (isMasterFace.test(facei)) { ++nBaffleFaces; @@ -964,6 +977,12 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells facePatch, maxEqOp<label>() ); + syncTools::syncFaceList + ( + mesh_, + faceZone, + maxEqOp<label>() + ); } Info<< "markFacesOnProblemCells : marked " @@ -978,26 +997,27 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells << " internal faces from getting converted into baffles." << endl; } - - return facePatch; } // Mark faces to be baffled to prevent snapping problems. Does // test to find nearest surface and checks which faces would get squashed. -Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric +void Foam::meshRefinement::markFacesOnProblemCellsGeometric ( const snapParameters& snapParams, const dictionary& motionDict, const labelList& globalToMasterPatch, - const labelList& globalToSlavePatch + const labelList& globalToSlavePatch, + + labelList& facePatch, + labelList& faceZone ) const { pointField oldPoints(mesh_.points()); // Repeat (most of) snappySnapDriver::doSnap { - labelList adaptPatchIDs(meshedPatches()); + const labelList adaptPatchIDs(meshedPatches()); // Construct addressing engine. autoPtr<indirectPrimitivePatch> ppPtr @@ -1106,11 +1126,17 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric // Per face the nearest adaptPatch - const labelList nearestAdaptPatch(nearestPatch(meshedPatches())); + //const labelList nearestAdaptPatch(nearestPatch(meshedPatches())); + labelList nearestAdaptPatch; + labelList nearestAdaptZone; + nearestPatch(meshedPatches(), nearestAdaptPatch, nearestAdaptZone); // Per face (internal or coupled!) the patch that the // baffle should get (or -1). - labelList facePatch(mesh_.nFaces(), -1); + facePatch.setSize(mesh_.nFaces()); + facePatch = -1; + faceZone.setSize(mesh_.nFaces()); + faceZone = -1; // Count of baffled faces label nBaffleFaces = 0; @@ -1218,6 +1244,7 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric if (patchi == -1 || mesh_.boundaryMesh()[patchi].coupled()) { facePatch[facei] = nearestAdaptPatch[facei]; + faceZone[facei] = nearestAdaptZone[facei]; nBaffleFaces++; //Pout<< " " << facei @@ -1244,8 +1271,12 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric facePatch, maxEqOp<label>() ); - - return facePatch; + syncTools::syncFaceList + ( + mesh_, + faceZone, + maxEqOp<label>() + ); } diff --git a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C index 601f7ca6a2e..e8d9e308362 100644 --- a/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C +++ b/src/mesh/snappyHexMesh/meshRefinement/meshRefinementRefine.C @@ -2310,6 +2310,7 @@ Foam::labelList Foam::meshRefinement::refineCandidates // Limit refinement // ~~~~~~~~~~~~~~~~ + if (limitShells_.shells().size()) { label nUnmarked = unmarkInternalRefinement(refineCell, nRefine); if (nUnmarked > 0) diff --git a/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.C b/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.C index c82f64f89a7..c98a71adab7 100644 --- a/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.C +++ b/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.C @@ -207,6 +207,7 @@ Foam::refinementSurfaces::refinementSurfaces PtrList<dictionary> globalPatchInfo(surfI); labelList globalBlockLevel(surfI, labelMax); + labelList globalLeakLevel(surfI, labelMax); // Per surface, per region data List<Map<label>> regionMinLevel(surfI); @@ -218,6 +219,7 @@ Foam::refinementSurfaces::refinementSurfaces List<Map<scalar>> regionAngle(surfI); List<Map<autoPtr<dictionary>>> regionPatchInfo(surfI); List<Map<label>> regionBlockLevel(surfI); + List<Map<label>> regionLeakLevel(surfI); wordHashSet unmatchedKeys(surfacesDict.toc()); @@ -330,6 +332,7 @@ Foam::refinementSurfaces::refinementSurfaces } dict.readIfPresent("perpendicularAngle", globalAngle[surfI]); dict.readIfPresent("blockLevel", globalBlockLevel[surfI]); + dict.readIfPresent("leakLevel", globalLeakLevel[surfI]); if (dict.found("regions")) @@ -455,6 +458,10 @@ Foam::refinementSurfaces::refinementSurfaces { regionBlockLevel[surfI].insert(regionI, l); } + if (regionDict.readIfPresent<label>("leakLevel", l)) + { + regionLeakLevel[surfI].insert(regionI, l); + } } } } @@ -503,6 +510,8 @@ Foam::refinementSurfaces::refinementSurfaces patchInfo_.setSize(nRegions); blockLevel_.setSize(nRegions); blockLevel_ = labelMax; + leakLevel_.setSize(nRegions); + leakLevel_ = labelMax; forAll(globalMinLevel, surfI) @@ -532,6 +541,7 @@ Foam::refinementSurfaces::refinementSurfaces ); } blockLevel_[globalRegionI] = globalBlockLevel[surfI]; + leakLevel_[globalRegionI] = globalLeakLevel[surfI]; } // Overwrite with region specific information @@ -572,6 +582,7 @@ Foam::refinementSurfaces::refinementSurfaces const label globalRegionI = regionOffset_[surfI] + iter.key(); blockLevel_[globalRegionI] = iter.val(); + leakLevel_[globalRegionI] = iter.val(); } } } diff --git a/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.H b/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.H index c96ad7e7115..16d8582c091 100644 --- a/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.H +++ b/src/mesh/snappyHexMesh/refinementSurfaces/refinementSurfaces.H @@ -96,6 +96,10 @@ class refinementSurfaces // needs to apply labelList blockLevel_; + //- From global region number to cell level at which leakage detection + // needs to apply + labelList leakLevel_; + //- From global region number to small-gap level specification List<FixedList<label, 3>> extendedGapLevel_; @@ -228,6 +232,13 @@ public: return blockLevel_; } + //- From global region number to cell level at which leakage + //- detection is applied. labelMax if not set. + const labelList& leakLevel() const + { + return leakLevel_; + } + //- From global region number to specification of gap and its // refinement: 3 labels specifying // - minimum wanted number of cells in the gap diff --git a/src/mesh/snappyHexMesh/refinementSurfaces/surfaceZonesInfo.C b/src/mesh/snappyHexMesh/refinementSurfaces/surfaceZonesInfo.C index d4efaf867ea..8903cfaa2db 100644 --- a/src/mesh/snappyHexMesh/refinementSurfaces/surfaceZonesInfo.C +++ b/src/mesh/snappyHexMesh/refinementSurfaces/surfaceZonesInfo.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2013-2015 OpenFOAM Foundation - Copyright (C) 2015 OpenCFD Ltd. + Copyright (C) 2015-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -522,6 +522,7 @@ Foam::label Foam::surfaceZonesInfo::addFaceZone { zoneI = faceZones.size(); faceZones.setSize(zoneI+1); + faceZones.set ( zoneI, @@ -535,6 +536,75 @@ Foam::label Foam::surfaceZonesInfo::addFaceZone ) ); } +// else +// { +// // Already have faceZone. Add to addressing (if necessary) +// +// faceZone& fz = faceZones[zoneI]; +// +// DebugVar(fz.size()); +// DebugVar(fz.addressing().size()); +// +// if (fz.size() != addressing.size()) +// { +// faceZones.clearAddressing(); +// fz.resetAddressing(addressing, flipMap); +// } +// else +// { +// const labelList& oldAddressing = fz; +// const boolList& oldFlipMap = fz.flipMap(); +// +// bitSet isZoneFace(mesh.nFaces(), oldAddressing); +// bitSet isZoneFlip(mesh.nFaces()); +// forAll(oldAddressing, i) +// { +// const label facei = oldAddressing[i]; +// isZoneFlip[facei] = oldFlipMap[i]; +// } +// +// const bitSet newZoneFace(mesh.nFaces(), addressing); +// bitSet newZoneFlip(mesh.nFaces()); +// forAll(addressing, i) +// { +// if (flipMap[i]) +// { +// newZoneFlip.set(addressing[i]); +// } +// } +// +// bool isChanged = false; +// forAll(isZoneFace, facei) +// { +// if +// ( +// isZoneFace[facei] != newZoneFace[facei] +// || isZoneFlip[facei] != newZoneFlip[facei] +// ) +// { +// isZoneFace[facei] = newZoneFace[facei]; +// isZoneFlip[facei] = newZoneFlip[facei]; +// isChanged = true; +// } +// } +// +// if (isChanged) +// { +// labelList newAddressing(isZoneFace.sortedToc()); +// boolList newFlip(newAddressing.size(), false); +// forAll(newAddressing, i) +// { +// newFlip[i] = isZoneFlip[newAddressing[i]]; +// } +// faceZones.clearAddressing(); +// fz.resetAddressing +// ( +// std::move(newAddressing), +// std::move(newFlip) +// ); +// } +// } +// } return zoneI; } diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C index 62b50bb0c5b..891ca1f0391 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.C @@ -393,7 +393,8 @@ Foam::layerParameters::layerParameters "meshShrinker", medialAxisMeshMover::typeName ) - ) + ), + nOuterIter_(dict.getOrDefault<scalar>("nOuterIter", 1)) { // Detect layer specification mode @@ -693,6 +694,56 @@ Foam::layerParameters::layerParameters // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +Foam::scalar Foam::layerParameters::layerThickness +( + const label nLayers, + const scalar layerThickness, // overall layer thickness + const scalar expansionRatio, + + const label layerStart, // start in nLayers + const label layerSize // size of slice of nLayers +) +{ + if (layerSize == 0 || nLayers == 0) + { + return 0.0; + } + else if (layerSize > nLayers || layerStart >= nLayers) + { + FatalErrorInFunction << "Illegal input for slice of layer:" + << " overall nLayers:" << nLayers + << " slice nLayers:" << layerSize + << " slice start:" << layerStart + << exit(FatalError); + return 0.0; + } + else if (mag(expansionRatio-1) < SMALL) + { + return layerThickness*layerSize/nLayers; + } + else + { + const scalar firstLayerThickness = + finalLayerThicknessRatio(nLayers, expansionRatio) + * layerThickness + / pow(expansionRatio, nLayers-1); + + // Calculate thickness of single layer at layerStart + const scalar startThickness = + firstLayerThickness + *pow(expansionRatio, layerStart); + + // See below for formula + const scalar thickness = + startThickness + *(1.0 - pow(expansionRatio, layerSize)) + /(1.0 - expansionRatio); + + return thickness; + } +} + + Foam::scalar Foam::layerParameters::layerThickness ( const thicknessModelType layerSpec, diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.H index 3c8778a6de9..88e4e1444d7 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.H +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/layerParameters/layerParameters.H @@ -121,26 +121,28 @@ private: scalarField minThickness_; - scalar featureAngle_; + const scalar featureAngle_; - scalar mergePatchFacesAngle_; + const scalar mergePatchFacesAngle_; - scalar concaveAngle_; + const scalar concaveAngle_; - label nGrow_; + const label nGrow_; - scalar maxFaceThicknessRatio_; + const scalar maxFaceThicknessRatio_; - label nBufferCellsNoExtrude_; + const label nBufferCellsNoExtrude_; - label nLayerIter_; + const label nLayerIter_; label nRelaxedIter_; //- Any additional reporting - bool additionalReporting_; + const bool additionalReporting_; - word meshShrinker_; + const word meshShrinker_; + + const label nOuterIter_; // Private Member Functions @@ -277,6 +279,15 @@ public: return nLayerIter_; } + //- Outer loop to add layer by layer. Can be set to >= max layers + // in which case layers get added one at a time. This can help + // layer insertion since the newly added layers get included in + // the shrinking. Default is 1 -> add all layers in one go. + label nOuterIter() const + { + return nOuterIter_; + } + //- Number of iterations after which relaxed motion rules // are to be used. label nRelaxedIter() const @@ -380,6 +391,16 @@ public: const label nLayers, const scalar expansionRatio ); + + //- Determine overall thickness of a slice (usually 1 layer) + static scalar layerThickness + ( + const label nLayers, + const scalar layerThickness, // overall thickness + const scalar expansionRatio, // expansion ratio + const label layerStart, // starting layer (0=firstLayer) + const label layerSize // number of layers in slice + ); }; diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.C index 211b42411e2..16cfd83a766 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.C +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.C @@ -311,4 +311,46 @@ Foam::labelList Foam::refinementParameters::unzonedLocations } +Foam::List<Foam::pointField> Foam::refinementParameters::zonePoints +( + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh +) +{ + // Sort locations according to zone. Add outside as last element + DynamicList<pointField> allLocations(zonesInMesh.size()+1); + DynamicList<word> allZoneNames(allLocations.size()); + + forAll(zonesInMesh, i) + { + const word name + ( + zonesInMesh[i] == word::null + ? "none" + : zonesInMesh[i] + ); + const point& pt = locationsInMesh[i]; + + const label index = allZoneNames.find(name); + if (index == -1) + { + allZoneNames.append(name); + allLocations.append(pointField(1, pt)); + } + else + { + allLocations[index].append(pt); + } + } + + allZoneNames.append("outside"); + allLocations.append(locationsOutsideMesh); + + allLocations.shrink(); + + return std::move(allLocations); +} + + // ************************************************************************* // diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.H index cd7c4082ae2..9bdbba4f2e3 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.H +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/refinementParameters/refinementParameters.H @@ -281,6 +281,15 @@ public: //- Extract indices of unnamed locations ('keepPoints') static labelList unzonedLocations(const wordList& zonesInMesh); + //- Helper: per zone (entry in zonesInMesh) the locations with + // additionally locationsOutsideMesh as last. Used in + // hole filling + static List<pointField> zonePoints + ( + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh + ); }; diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C index 5017e3b22cd..eef47627609 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.C @@ -1005,7 +1005,6 @@ void Foam::snappyLayerDriver::setNumLayers const labelList& patchToNLayers, const labelList& patchIDs, const indirectPrimitivePatch& pp, - pointField& patchDisp, labelList& patchNLayers, List<extrudeMode>& extrudeStatus, label& nAddedCells @@ -3364,169 +3363,117 @@ bool Foam::snappyLayerDriver::writeLayerData } -// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // - -Foam::snappyLayerDriver::snappyLayerDriver -( - meshRefinement& meshRefiner, - const labelList& globalToMasterPatch, - const labelList& globalToSlavePatch, - const bool dryRun -) -: - meshRefiner_(meshRefiner), - globalToMasterPatch_(globalToMasterPatch), - globalToSlavePatch_(globalToSlavePatch), - dryRun_(dryRun) -{} - - -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - -void Foam::snappyLayerDriver::mergePatchFacesUndo -( - const layerParameters& layerParams, - const dictionary& motionDict, - const meshRefinement::FaceMergeType mergeType -) -{ - // Clip to 30 degrees. Not helpful! - //scalar planarAngle = min(30.0, layerParams.featureAngle()); - scalar planarAngle = layerParams.mergePatchFacesAngle(); - scalar minCos = Foam::cos(degToRad(planarAngle)); - - scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle())); - - Info<< nl - << "Merging all faces of a cell" << nl - << "---------------------------" << nl - << " - which are on the same patch" << nl - << " - which make an angle < " << planarAngle - << " degrees" - << " (cos:" << minCos << ')' << nl - << " - as long as the resulting face doesn't become concave" - << " by more than " - << layerParams.concaveAngle() << " degrees" << nl - << " (0=straight, 180=fully concave)" << nl - << endl; - - const fvMesh& mesh = meshRefiner_.mesh(); - - List<labelPair> couples(localPointRegion::findDuplicateFacePairs(mesh)); - - labelList duplicateFace(mesh.nFaces(), -1); - forAll(couples, i) - { - const labelPair& cpl = couples[i]; - duplicateFace[cpl[0]] = cpl[1]; - duplicateFace[cpl[1]] = cpl[0]; - } - - label nChanged = meshRefiner_.mergePatchFacesUndo - ( - minCos, - concaveCos, - meshRefiner_.meshedPatches(), - motionDict, - duplicateFace, - mergeType // How to merge co-planar patch faces - ); - - nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict); -} - - -void Foam::snappyLayerDriver::addLayers +void Foam::snappyLayerDriver::dupFaceZonePoints ( - const layerParameters& layerParams, - const dictionary& motionDict, - const labelList& patchIDs, - const label nAllowableErrors, - decompositionMethod& decomposer, - fvMeshDistribute& distributor + const labelList& patchIDs, // patch indices + const labelList& numLayers, // number of layers per patch + List<labelPair> baffles, // pairs of baffles (input & updated) + labelList& pointToMaster // -1 or index of original point (duplicated + // point) ) { fvMesh& mesh = meshRefiner_.mesh(); + // Check outside of baffles for non-manifoldness - // faceZones of type internal or baffle (for merging points across) - labelList internalOrBaffleFaceZones; + // Points that are candidates for duplication + labelList candidatePoints; { - List<surfaceZonesInfo::faceZoneType> fzTypes(2); - fzTypes[0] = surfaceZonesInfo::INTERNAL; - fzTypes[1] = surfaceZonesInfo::BAFFLE; - internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes); - } - - // faceZones of type internal (for checking mesh quality across and - // merging baffles) - const labelList internalFaceZones - ( - meshRefiner_.getZones + // Do full analysis to see if we need to extrude points + // so have to duplicate them + autoPtr<indirectPrimitivePatch> pp ( - List<surfaceZonesInfo::faceZoneType> + meshRefinement::makePatch ( - 1, - surfaceZonesInfo::INTERNAL + mesh, + patchIDs ) - ) - ); + ); - // Create baffles (pairs of faces that share the same points) - // Baffles stored as owner and neighbour face that have been created. - List<labelPair> baffles; - { - labelList originatingFaceZone; - meshRefiner_.createZoneBaffles + // Displacement for all pp.localPoints. Set to large value to + // avoid truncation in syncPatchDisplacement because of + // minThickness. + vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT)); + labelList patchNLayers(pp().nPoints(), Zero); + label nIdealTotAddedCells = 0; + List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE); + // Get number of layers per point from number of layers per patch + setNumLayers ( - identity(mesh.faceZones().size()), - baffles, - originatingFaceZone + numLayers, // per patch the num layers + patchIDs, // patches that are being moved + *pp, // indirectpatch for all faces moving + + patchNLayers, + extrudeStatus, + nIdealTotAddedCells + ); + // Make sure displacement is equal on both sides of coupled patches. + // Note that we explicitly disable the minThickness truncation + // of the patchDisp here. + syncPatchDisplacement + ( + *pp, + scalarField(patchDisp.size(), Zero), //minThickness, + patchDisp, + patchNLayers, + extrudeStatus ); - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + + // Do duplication only if all patch points decide to extrude. Ignore + // contribution from non-patch points. Note that we need to + // apply this to all mesh points + labelList minPatchState(mesh.nPoints(), labelMax); + forAll(extrudeStatus, patchPointi) { - const_cast<Time&>(mesh.time())++; - Info<< "Writing baffled mesh to time " - << meshRefiner_.timeName() << endl; - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() - ); + label pointi = pp().meshPoints()[patchPointi]; + minPatchState[pointi] = extrudeStatus[patchPointi]; } - } + syncTools::syncPointList + ( + mesh, + minPatchState, + minEqOp<label>(), // combine op + labelMax // null value + ); - // Duplicate points on faceZones of type boundary. Should normally already - // be done by snapping phase - { - autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints(); - if (map) + // So now minPatchState: + // - labelMax on non-patch points + // - NOEXTRUDE if any patch point was not extruded + // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/ + // extrudeRemove. + + label n = 0; + forAll(minPatchState, pointi) { - const labelList& reverseFaceMap = map->reverseFaceMap(); - forAll(baffles, i) + label state = minPatchState[pointi]; + if (state == EXTRUDE || state == EXTRUDEREMOVE) { - label f0 = reverseFaceMap[baffles[i].first()]; - label f1 = reverseFaceMap[baffles[i].second()]; - baffles[i] = labelPair(f0, f1); + n++; + } + } + candidatePoints.setSize(n); + n = 0; + forAll(minPatchState, pointi) + { + label state = minPatchState[pointi]; + if (state == EXTRUDE || state == EXTRUDEREMOVE) + { + candidatePoints[n++] = pointi; } } } + // Not duplicate points on either side of baffles that don't get any + // layers + labelPairList nonDupBaffles; - - // Now we have all patches determine the number of layer per patch - // This will be layerParams.numLayers() except for faceZones where one - // side does get layers and the other not in which case we want to - // suppress movement by explicitly setting numLayers 0 - labelList numLayers(layerParams.numLayers()); { + // faceZones that are not being duplicated + DynamicList<label> nonDupZones(mesh.faceZones().size()); + labelHashSet layerIDs(patchIDs); forAll(mesh.faceZones(), zonei) { @@ -3539,1219 +3486,1809 @@ void Foam::snappyLayerDriver::addLayers spi, fzType ); - if (hasInfo) + if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi)) { - const polyBoundaryMesh& pbm = mesh.boundaryMesh(); - if (layerIDs.found(mpi) && !layerIDs.found(spi)) - { - // Only layers on master side. Fix points on slave side - Info<< "On faceZone " << mesh.faceZones()[zonei].name() - << " adding layers to master patch " << pbm[mpi].name() - << " only. Freezing points on slave patch " - << pbm[spi].name() << endl; - numLayers[spi] = 0; - } - else if (!layerIDs.found(mpi) && layerIDs.found(spi)) - { - // Only layers on slave side. Fix points on master side - Info<< "On faceZone " << mesh.faceZones()[zonei].name() - << " adding layers to slave patch " << pbm[spi].name() - << " only. Freezing points on master patch " - << pbm[mpi].name() << endl; - numLayers[mpi] = 0; - } + nonDupZones.append(zonei); } } + nonDupBaffles = meshRefinement::subsetBaffles + ( + mesh, + nonDupZones, + localPointRegion::findDuplicateFacePairs(mesh) + ); } + const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints); - // Duplicate points on faceZones that layers are added to - labelList pointToMaster; + autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints + ( + regionSide + ); + if (map) { - // Check outside of baffles for non-manifoldness + // Store point duplication + pointToMaster.setSize(mesh.nPoints(), -1); - // Points that are candidates for duplication - labelList candidatePoints; - { - // Do full analysis to see if we need to extrude points - // so have to duplicate them - autoPtr<indirectPrimitivePatch> pp - ( - meshRefinement::makePatch - ( - mesh, - patchIDs - ) - ); - - // Displacement for all pp.localPoints. Set to large value to - // avoid truncation in syncPatchDisplacement because of - // minThickness. - vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT)); - labelList patchNLayers(pp().nPoints(), Zero); - label nIdealTotAddedCells = 0; - List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE); - // Get number of layers per point from number of layers per patch - setNumLayers - ( - numLayers, // per patch the num layers - patchIDs, // patches that are being moved - *pp, // indirectpatch for all faces moving + const labelList& pointMap = map().pointMap(); + const labelList& reversePointMap = map().reversePointMap(); - patchDisp, - patchNLayers, - extrudeStatus, - nIdealTotAddedCells - ); - // Make sure displacement is equal on both sides of coupled patches. - // Note that we explicitly disable the minThickness truncation - // of the patchDisp here. - syncPatchDisplacement - ( - *pp, - scalarField(patchDisp.size(), Zero), //minThickness, - patchDisp, - patchNLayers, - extrudeStatus - ); + forAll(pointMap, pointi) + { + label oldPointi = pointMap[pointi]; + label newMasterPointi = reversePointMap[oldPointi]; + if (newMasterPointi != pointi) + { + // Found slave. Mark both master and slave + pointToMaster[pointi] = newMasterPointi; + pointToMaster[newMasterPointi] = newMasterPointi; + } + } - // Do duplication only if all patch points decide to extrude. Ignore - // contribution from non-patch points. Note that we need to - // apply this to all mesh points - labelList minPatchState(mesh.nPoints(), labelMax); - forAll(extrudeStatus, patchPointi) + // Update baffle numbering + { + const labelList& reverseFaceMap = map().reverseFaceMap(); + forAll(baffles, i) { - label pointi = pp().meshPoints()[patchPointi]; - minPatchState[pointi] = extrudeStatus[patchPointi]; + label f0 = reverseFaceMap[baffles[i].first()]; + label f1 = reverseFaceMap[baffles[i].second()]; + baffles[i] = labelPair(f0, f1); } + } + + + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing point-duplicate mesh to time " + << meshRefiner_.timeName() << endl; - syncTools::syncPointList + meshRefiner_.write ( - mesh, - minPatchState, - minEqOp<label>(), // combine op - labelMax // null value + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() ); - // So now minPatchState: - // - labelMax on non-patch points - // - NOEXTRUDE if any patch point was not extruded - // - EXTRUDE or EXTRUDEREMOVE if all patch points are extruded/ - // extrudeRemove. - - label n = 0; - forAll(minPatchState, pointi) - { - label state = minPatchState[pointi]; - if (state == EXTRUDE || state == EXTRUDEREMOVE) - { - n++; - } - } - candidatePoints.setSize(n); - n = 0; - forAll(minPatchState, pointi) + OBJstream str + ( + mesh.time().path() + / "duplicatePoints_" + + meshRefiner_.timeName() + + ".obj" + ); + Info<< "Writing point-duplicates to " << str.name() << endl; + const pointField& p = mesh.points(); + forAll(pointMap, pointi) { - label state = minPatchState[pointi]; - if (state == EXTRUDE || state == EXTRUDEREMOVE) + label newMasteri = reversePointMap[pointMap[pointi]]; + + if (newMasteri != pointi) { - candidatePoints[n++] = pointi; + str.write(linePointRef(p[pointi], p[newMasteri])); } } } + } +} - // Not duplicate points on either side of baffles that don't get any - // layers - labelPairList nonDupBaffles; - { - // faceZones that are not being duplicated - DynamicList<label> nonDupZones(mesh.faceZones().size()); +void Foam::snappyLayerDriver::mergeFaceZonePoints +( + const labelList& pointToMaster, // -1 or index of duplicated point + labelList& cellNLayers, + scalarField& faceRealThickness, + scalarField& faceWantedThickness +) +{ + // Near opposite of dupFaceZonePoints : merge points and baffles introduced + // for internal faceZones - labelHashSet layerIDs(patchIDs); - forAll(mesh.faceZones(), zonei) + fvMesh& mesh = meshRefiner_.mesh(); + + // Count duplicate points + label nPointPairs = 0; + forAll(pointToMaster, pointi) + { + label otherPointi = pointToMaster[pointi]; + if (otherPointi != -1) + { + nPointPairs++; + } + } + reduce(nPointPairs, sumOp<label>()); + if (nPointPairs > 0) + { + // Merge any duplicated points + Info<< "Merging " << nPointPairs << " duplicated points ..." << endl; + + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + OBJstream str + ( + mesh.time().path() + / "mergePoints_" + + meshRefiner_.timeName() + + ".obj" + ); + Info<< "Points to be merged to " << str.name() << endl; + forAll(pointToMaster, pointi) { - label mpi, spi; - surfaceZonesInfo::faceZoneType fzType; - bool hasInfo = meshRefiner_.getFaceZoneInfo - ( - mesh.faceZones()[zonei].name(), - mpi, - spi, - fzType - ); - if (hasInfo && !layerIDs.found(mpi) && !layerIDs.found(spi)) + label otherPointi = pointToMaster[pointi]; + if (otherPointi != -1) { - nonDupZones.append(zonei); + const point& pt = mesh.points()[pointi]; + const point& otherPt = mesh.points()[otherPointi]; + str.write(linePointRef(pt, otherPt)); } } - nonDupBaffles = meshRefinement::subsetBaffles - ( - mesh, - nonDupZones, - localPointRegion::findDuplicateFacePairs(mesh) - ); } - const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints); + autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster); + if (map) + { + inplaceReorder(map().reverseCellMap(), cellNLayers); + + const labelList& reverseFaceMap = map().reverseFaceMap(); + inplaceReorder(reverseFaceMap, faceWantedThickness); + inplaceReorder(reverseFaceMap, faceRealThickness); + + Info<< "Merged points in = " + << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; + } + } + + if (mesh.faceZones().size() > 0) + { + // Merge any baffles + Info<< "Converting baffles back into zoned faces ..." + << endl; - autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints + autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles ( - regionSide + true, // internal zones + false // baffle zones ); - if (map) { - // Store point duplication - pointToMaster.setSize(mesh.nPoints(), -1); + inplaceReorder(map().reverseCellMap(), cellNLayers); - const labelList& pointMap = map().pointMap(); - const labelList& reversePointMap = map().reversePointMap(); + const labelList& faceMap = map().faceMap(); - forAll(pointMap, pointi) + // Make sure to keep the max since on two patches only one has + // layers. + scalarField newFaceRealThickness(mesh.nFaces(), Zero); + scalarField newFaceWantedThickness(mesh.nFaces(), Zero); + forAll(newFaceRealThickness, facei) { - label oldPointi = pointMap[pointi]; - label newMasterPointi = reversePointMap[oldPointi]; - - if (newMasterPointi != pointi) + label oldFacei = faceMap[facei]; + if (oldFacei >= 0) { - // Found slave. Mark both master and slave - pointToMaster[pointi] = newMasterPointi; - pointToMaster[newMasterPointi] = newMasterPointi; + scalar& realThick = newFaceRealThickness[facei]; + realThick = max(realThick, faceRealThickness[oldFacei]); + scalar& wanted = newFaceWantedThickness[facei]; + wanted = max(wanted, faceWantedThickness[oldFacei]); } } + faceRealThickness.transfer(newFaceRealThickness); + faceWantedThickness.transfer(newFaceWantedThickness); + } - // Update baffle numbering - { - const labelList& reverseFaceMap = map().reverseFaceMap(); - forAll(baffles, i) - { - label f0 = reverseFaceMap[baffles[i].first()]; - label f1 = reverseFaceMap[baffles[i].second()]; - baffles[i] = labelPair(f0, f1); - } - } + Info<< "Converted baffles in = " + << meshRefiner_.mesh().time().cpuTimeIncrement() + << " s\n" << nl << endl; + } +} - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) - { - const_cast<Time&>(mesh.time())++; - Info<< "Writing point-duplicate mesh to time " - << meshRefiner_.timeName() << endl; +Foam::label Foam::snappyLayerDriver::setPointNumLayers +( + const layerParameters& layerParams, - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() - ); + const labelList& numLayers, + const labelList& patchIDs, + const indirectPrimitivePatch& pp, + const labelListList& edgeGlobalFaces, - OBJstream str - ( - mesh.time().path() - / "duplicatePoints_" - + meshRefiner_.timeName() - + ".obj" - ); - Info<< "Writing point-duplicates to " << str.name() << endl; - const pointField& p = mesh.points(); - forAll(pointMap, pointi) - { - label newMasteri = reversePointMap[pointMap[pointi]]; + vectorField& patchDisp, + labelList& patchNLayers, + List<extrudeMode>& extrudeStatus +) const +{ + fvMesh& mesh = meshRefiner_.mesh(); - if (newMasteri != pointi) - { - str.write(linePointRef(p[pointi], p[newMasteri])); - } - } - } - } - } + patchDisp.setSize(pp.nPoints()); + patchDisp = vector(GREAT, GREAT, GREAT); + + // Number of layers for all pp.localPoints. Note: only valid if + // extrudeStatus = EXTRUDE. + patchNLayers.setSize(pp.nPoints()); + patchNLayers = Zero; + // Ideal number of cells added + label nIdealTotAddedCells = 0; - // Add layers to patches - // ~~~~~~~~~~~~~~~~~~~~~ + // Whether to add edge for all pp.localPoints. + extrudeStatus.setSize(pp.nPoints()); + extrudeStatus = EXTRUDE; - // Now we have - // - mesh with optional baffles and duplicated points for faceZones - // where layers are to be added - // - pointToMaster : correspondence for duplicated points - // - baffles : list of pairs of faces + // Get number of layers per point from number of layers per patch + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - autoPtr<indirectPrimitivePatch> pp + setNumLayers ( - meshRefinement::makePatch - ( - mesh, - patchIDs - ) + numLayers, // per patch the num layers + patchIDs, // patches that are being moved + pp, // indirectpatch for all faces moving + + patchNLayers, + extrudeStatus, + nIdealTotAddedCells ); + // Precalculate mesh edge labels for patch edges + labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges())); - // Global face indices engine - const globalIndex globalFaces(mesh.nFaces()); - // Determine extrudePatch.edgeFaces in global numbering (so across - // coupled patches). This is used only to string up edges between coupled - // faces (all edges between same (global)face indices get extruded). - labelListList edgeGlobalFaces + // Disable extrusion on split strings of common points + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + handleNonStringConnected ( - addPatchCellLayer::globalEdgeFaces - ( - mesh, - globalFaces, - *pp - ) + pp, + patchDisp, + patchNLayers, + extrudeStatus ); - // Determine patches for extruded boundary edges. Calculates if any - // additional processor patches need to be constructed. - labelList edgePatchID; - labelList edgeZoneID; - boolList edgeFlip; - labelList inflateFaceID; - determineSidePatches + // Disable extrusion on non-manifold points + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + handleNonManifolds ( - globalFaces, + pp, + meshEdges, edgeGlobalFaces, - *pp, - edgePatchID, - edgeZoneID, - edgeFlip, - inflateFaceID + patchDisp, + patchNLayers, + extrudeStatus ); + // Disable extrusion on feature angles + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Point-wise extrusion data - // ~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Displacement for all pp.localPoints. Set to large value so does - // not trigger the minThickness truncation (see syncPatchDisplacement below) - vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT)); - - // Number of layers for all pp.localPoints. Note: only valid if - // extrudeStatus = EXTRUDE. - labelList patchNLayers(pp().nPoints(), Zero); - - // Ideal number of cells added - label nIdealTotAddedCells = 0; - - // Whether to add edge for all pp.localPoints. - List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE); + handleFeatureAngle + ( + pp, + meshEdges, + layerParams.featureAngle(), + patchDisp, + patchNLayers, + extrudeStatus + ); + // Disable extrusion on warped faces + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // It is hard to calculate some length scale if not in relative + // mode so disable this check. + if (!layerParams.relativeSizes().found(false)) { - // Get number of layers per point from number of layers per patch - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Undistorted edge length + const scalar edge0Len = + meshRefiner_.meshCutter().level0EdgeLength(); + const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel(); - setNumLayers + handleWarpedFaces ( - numLayers, // per patch the num layers - patchIDs, // patches that are being moved - *pp, // indirectpatch for all faces moving + pp, + layerParams.maxFaceThicknessRatio(), + layerParams.relativeSizes(), + edge0Len, + cellLevel, patchDisp, patchNLayers, - extrudeStatus, - nIdealTotAddedCells + extrudeStatus ); + } - // Precalculate mesh edge labels for patch edges - labelList meshEdges(pp().meshEdges(mesh.edges(), mesh.pointEdges())); - + //// Disable extrusion on cells with multiple patch faces + //// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // + //handleMultiplePatchFaces + //( + // pp, + // + // patchDisp, + // patchNLayers, + // extrudeStatus + //); - // Disable extrusion on split strings of common points - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + addProfiling(grow, "snappyHexMesh::layers::grow"); - handleNonStringConnected + // Grow out region of non-extrusion + for (label i = 0; i < layerParams.nGrow(); i++) + { + growNoExtrusion ( - *pp, + pp, patchDisp, patchNLayers, extrudeStatus ); + } + return nIdealTotAddedCells; +} - // Disable extrusion on non-manifold points - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Foam::autoPtr<Foam::externalDisplacementMeshMover> +Foam::snappyLayerDriver::makeMeshMover +( + const layerParameters& layerParams, + const dictionary& motionDict, + const labelList& internalFaceZones, + const scalarIOField& minThickness, + pointVectorField& displacement +) const +{ + // Allocate run-time selectable mesh mover - handleNonManifolds - ( - *pp, - meshEdges, - edgeGlobalFaces, + fvMesh& mesh = meshRefiner_.mesh(); - patchDisp, - patchNLayers, - extrudeStatus - ); - // Disable extrusion on feature angles - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Set up controls for meshMover + dictionary combinedDict(layerParams.dict()); + // Add mesh quality constraints + combinedDict.merge(motionDict); + // Where to get minThickness from + combinedDict.add("minThicknessName", minThickness.name()); - handleFeatureAngle + const List<labelPair> internalBaffles + ( + meshRefinement::subsetBaffles ( - *pp, - meshEdges, - layerParams.featureAngle(), + mesh, + internalFaceZones, + localPointRegion::findDuplicateFacePairs(mesh) + ) + ); - patchDisp, - patchNLayers, - extrudeStatus - ); + // Take over patchDisp as boundary conditions on displacement + // pointVectorField + autoPtr<Foam::externalDisplacementMeshMover> medialAxisMoverPtr + ( + externalDisplacementMeshMover::New + ( + layerParams.meshShrinker(), + combinedDict, + internalBaffles, + displacement + ) + ); - // Disable extrusion on warped faces - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // It is hard to calculate some length scale if not in relative - // mode so disable this check. - if (!layerParams.relativeSizes().found(false)) - { - // Undistorted edge length - const scalar edge0Len = - meshRefiner_.meshCutter().level0EdgeLength(); - const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel(); - handleWarpedFaces - ( - *pp, - layerParams.maxFaceThicknessRatio(), - layerParams.relativeSizes(), - edge0Len, - cellLevel, + if (dryRun_) + { + string errorMsg(FatalError.message()); + string IOerrorMsg(FatalIOError.message()); - patchDisp, - patchNLayers, - extrudeStatus - ); + if (errorMsg.size() || IOerrorMsg.size()) + { + //errorMsg = "[dryRun] " + errorMsg; + //errorMsg.replaceAll("\n", "\n[dryRun] "); + //IOerrorMsg = "[dryRun] " + IOerrorMsg; + //IOerrorMsg.replaceAll("\n", "\n[dryRun] "); + + IOWarningInFunction(combinedDict) + << nl + << "Missing/incorrect required dictionary entries:" + << nl << nl + << IOerrorMsg.c_str() << nl + << errorMsg.c_str() << nl << nl + << "Exiting dry-run" << nl << endl; + + if (Pstream::parRun()) + { + Perr<< "\nFOAM parallel run exiting\n" << endl; + Pstream::exit(0); + } + else + { + Perr<< "\nFOAM exiting\n" << endl; + std::exit(0); + } } + } + return medialAxisMoverPtr; +} - //// Disable extrusion on cells with multiple patch faces - //// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // - //handleMultiplePatchFaces - //( - // *pp, - // - // patchDisp, - // patchNLayers, - // extrudeStatus - //); - - addProfiling(grow, "snappyHexMesh::layers::grow"); - // Grow out region of non-extrusion - for (label i = 0; i < layerParams.nGrow(); i++) - { - growNoExtrusion - ( - *pp, - patchDisp, - patchNLayers, - extrudeStatus - ); - } - } +void Foam::snappyLayerDriver::addLayers +( + const layerParameters& layerParams, + const label nLayerIter, + // Mesh quality provision + const dictionary& motionDict, + const label nRelaxedIter, + const label nAllowableErrors, - // Undistorted edge length - const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength(); - const labelList& cellLevel = meshRefiner_.meshCutter().cellLevel(); + const labelList& patchIDs, + const labelList& internalFaceZones, + const List<labelPair>& baffles, + const labelList& numLayers, + const label nIdealTotAddedCells, + + const globalIndex& globalFaces, + indirectPrimitivePatch& pp, + + const labelListList& edgeGlobalFaces, + const labelList& edgePatchID, + const labelList& edgeZoneID, + const boolList& edgeFlip, + const labelList& inflateFaceID, + + const scalarField& thickness, + const scalarIOField& minThickness, + const scalarField& expansionRatio, + + // Displacement for all pp.localPoints. Set to large value so does + // not trigger the minThickness truncation (see syncPatchDisplacement below) + vectorField& patchDisp, + + // Number of layers for all pp.localPoints. Note: only valid if + // extrudeStatus = EXTRUDE. + labelList& patchNLayers, + + // Whether to add edge for all pp.localPoints. + List<extrudeMode>& extrudeStatus, + + + polyTopoChange& savedMeshMod, + + + // Per cell 0 or number of layers in the cell column it is part of + labelList& cellNLayers, + // Per face actual overall layer thickness + scalarField& faceRealThickness +) +{ + fvMesh& mesh = meshRefiner_.mesh(); - // Determine (wanted) point-wise overall layer thickness and expansion - // ratio - scalarField thickness(pp().nPoints()); - scalarIOField minThickness + // Overall displacement field + pointVectorField displacement ( - IOobject + makeLayerDisplacementField ( - "minThickness", - meshRefiner_.timeName(), - mesh, - IOobject::NO_READ - ), - pp().nPoints() + pointMesh::New(mesh), + numLayers + ) ); - scalarField expansionRatio(pp().nPoints()); - calculateLayerThickness - ( - *pp, - patchIDs, - layerParams, - cellLevel, - patchNLayers, - edge0Len, - thickness, - minThickness, - expansionRatio + // Allocate run-time selectable mesh mover + autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr + ( + makeMeshMover + ( + layerParams, + motionDict, + internalFaceZones, + minThickness, + displacement + ) ); + // Saved old points + const pointField oldPoints(mesh.points()); - // Current set of topology changes. (changing mesh clears out - // polyTopoChange) - polyTopoChange savedMeshMod(mesh.boundaryMesh().size()); - // Per cell 0 or number of layers in the cell column it is part of - labelList cellNLayers; - // Per face actual overall layer thickness - scalarField faceRealThickness; - // Per face wanted overall layer thickness - scalarField faceWantedThickness(mesh.nFaces(), Zero); + for (label iteration = 0; iteration < nLayerIter; iteration++) { - UIndirectList<scalar>(faceWantedThickness, pp->addressing()) = - avgPointData(*pp, thickness); - } + Info<< nl + << "Layer addition iteration " << iteration << nl + << "--------------------------" << endl; - { - // Overall displacement field - pointVectorField displacement + // Unset the extrusion at the pp. + const dictionary& meshQualityDict = ( - makeLayerDisplacementField - ( - pointMesh::New(mesh), - numLayers - ) + iteration < nRelaxedIter + ? motionDict + : motionDict.subDict("relaxed") ); - // Allocate run-time selectable mesh mover - autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr; + if (iteration >= nRelaxedIter) { - // Set up controls for meshMover - dictionary combinedDict(layerParams.dict()); - // Add mesh quality constraints - combinedDict.merge(motionDict); - // Where to get minThickness from - combinedDict.add("minThicknessName", minThickness.name()); - - const List<labelPair> internalBaffles - ( - meshRefinement::subsetBaffles - ( - mesh, - internalFaceZones, - localPointRegion::findDuplicateFacePairs(mesh) - ) - ); + Info<< "Switched to relaxed meshQuality constraints." << endl; + } - // Take over patchDisp as boundary conditions on displacement - // pointVectorField - medialAxisMoverPtr = externalDisplacementMeshMover::New - ( - layerParams.meshShrinker(), - combinedDict, - internalBaffles, - displacement - ); - if (dryRun_) - { - string errorMsg(FatalError.message()); - string IOerrorMsg(FatalIOError.message()); + // Make sure displacement is equal on both sides of coupled patches. + // Note that this also does the patchDisp < minThickness truncation + // so for the first pass make sure the patchDisp is larger than + // that. + syncPatchDisplacement + ( + pp, + minThickness, + patchDisp, + patchNLayers, + extrudeStatus + ); - if (errorMsg.size() || IOerrorMsg.size()) - { - //errorMsg = "[dryRun] " + errorMsg; - //errorMsg.replaceAll("\n", "\n[dryRun] "); - //IOerrorMsg = "[dryRun] " + IOerrorMsg; - //IOerrorMsg.replaceAll("\n", "\n[dryRun] "); - - IOWarningInFunction(combinedDict) - << nl - << "Missing/incorrect required dictionary entries:" - << nl << nl - << IOerrorMsg.c_str() << nl - << errorMsg.c_str() << nl << nl - << "Exiting dry-run" << nl << endl; - - if (Pstream::parRun()) - { - Perr<< "\nFOAM parallel run exiting\n" << endl; - Pstream::exit(0); - } - else - { - Perr<< "\nFOAM exiting\n" << endl; - std::exit(0); - } - } - } - } + // Displacement acc. to pointnormals + getPatchDisplacement + ( + pp, + thickness, + minThickness, + expansionRatio, + patchDisp, + patchNLayers, + extrudeStatus + ); - // Saved old points - const pointField oldPoints(mesh.points()); + // Shrink mesh by displacement value first. + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - for - ( - label iteration = 0; - iteration < layerParams.nLayerIter(); - iteration++ - ) { - Info<< nl - << "Layer addition iteration " << iteration << nl - << "--------------------------" << endl; + const pointField oldPatchPos(pp.localPoints()); + // We have patchDisp which is the outwards pointing + // extrusion distance. Convert into an inwards pointing + // shrink distance + patchDisp = -patchDisp; - // Unset the extrusion at the pp. - const dictionary& meshQualityDict = + // Take over patchDisp into pointDisplacement field and + // adjust both for multi-patch constraints + motionSmootherAlgo::setDisplacement ( - iteration < layerParams.nRelaxedIter() - ? motionDict - : motionDict.subDict("relaxed") + patchIDs, + pp, + patchDisp, + displacement ); - if (iteration >= layerParams.nRelaxedIter()) - { - Info<< "Switched to relaxed meshQuality constraints." << endl; - } + // Move mesh + // ~~~~~~~~~ + // Set up controls for meshMover + dictionary combinedDict(layerParams.dict()); + // Add standard quality constraints + combinedDict.merge(motionDict); + // Add relaxed constraints (overrides standard ones) + combinedDict.merge(meshQualityDict); + // Where to get minThickness from + combinedDict.add("minThicknessName", minThickness.name()); - // Make sure displacement is equal on both sides of coupled patches. - // Note that this also does the patchDisp < minThickness truncation - // so for the first pass make sure the patchDisp is larger than - // that. - syncPatchDisplacement + labelList checkFaces(identity(mesh.nFaces())); + medialAxisMoverPtr().move ( - *pp, - minThickness, - patchDisp, - patchNLayers, - extrudeStatus + combinedDict, + nAllowableErrors, + checkFaces ); - // Displacement acc. to pointnormals - getPatchDisplacement - ( - *pp, - thickness, - minThickness, - expansionRatio, + pp.movePoints(mesh.points()); + + // Update patchDisp (since not all might have been honoured) + patchDisp = oldPatchPos - pp.localPoints(); + } + + // Truncate displacements that are too small (this will do internal + // ones, coupled ones have already been truncated by + // syncPatchDisplacement) + faceSet dummySet(mesh, "wrongPatchFaces", 0); + truncateDisplacement + ( + globalFaces, + edgeGlobalFaces, + pp, + minThickness, + dummySet, + patchDisp, + patchNLayers, + extrudeStatus + ); + + // Dump to .obj file for debugging. + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + dumpDisplacement + ( + mesh.time().path()/"layer_" + meshRefiner_.timeName(), + pp, patchDisp, - patchNLayers, extrudeStatus ); - // Shrink mesh by displacement value first. - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - { - const pointField oldPatchPos(pp().localPoints()); + const_cast<Time&>(mesh.time())++; + Info<< "Writing shrunk mesh to time " + << meshRefiner_.timeName() << endl; - // We have patchDisp which is the outwards pointing - // extrusion distance. Convert into an inwards pointing - // shrink distance - patchDisp = -patchDisp; + // See comment in snappySnapDriver why we should not remove + // meshPhi using mesh.clearOut(). - // Take over patchDisp into pointDisplacement field and - // adjust both for multi-patch constraints - motionSmootherAlgo::setDisplacement + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType ( - patchIDs, - *pp, - patchDisp, - displacement - ); + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } - // Move mesh - // ~~~~~~~~~ + // Mesh topo change engine. Insert current mesh. + polyTopoChange meshMod(mesh); - // Set up controls for meshMover - dictionary combinedDict(layerParams.dict()); - // Add standard quality constraints - combinedDict.merge(motionDict); - // Add relaxed constraints (overrides standard ones) - combinedDict.merge(meshQualityDict); - // Where to get minThickness from - combinedDict.add("minThicknessName", minThickness.name()); + // Grow layer of cells on to patch. Handles zero sized displacement. + addPatchCellLayer addLayer(mesh); - labelList checkFaces(identity(mesh.nFaces())); - medialAxisMoverPtr().move - ( - combinedDict, - nAllowableErrors, - checkFaces - ); + // Determine per point/per face number of layers to extrude. Also + // handles the slow termination of layers when going switching + // layers - pp().movePoints(mesh.points()); + labelList nPatchPointLayers(pp.nPoints(), -1); + labelList nPatchFaceLayers(pp.size(), -1); + setupLayerInfoTruncation + ( + pp, + patchNLayers, + extrudeStatus, + layerParams.nBufferCellsNoExtrude(), + nPatchPointLayers, + nPatchFaceLayers + ); - // Update patchDisp (since not all might have been honoured) - patchDisp = oldPatchPos - pp().localPoints(); - } + // Calculate displacement for final layer for addPatchLayer. + // (layer of cells next to the original mesh) + vectorField finalDisp(patchNLayers.size(), Zero); - // Truncate displacements that are too small (this will do internal - // ones, coupled ones have already been truncated by - // syncPatchDisplacement) - faceSet dummySet(mesh, "wrongPatchFaces", 0); - truncateDisplacement + forAll(nPatchPointLayers, i) + { + scalar ratio = layerParameters::finalLayerThicknessRatio ( - globalFaces, - edgeGlobalFaces, - *pp, - minThickness, - dummySet, - patchDisp, - patchNLayers, - extrudeStatus + nPatchPointLayers[i], + expansionRatio[i] ); + finalDisp[i] = ratio*patchDisp[i]; + } - // Dump to .obj file for debugging. - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) - { - dumpDisplacement - ( - mesh.time().path()/"layer_" + meshRefiner_.timeName(), - pp(), - patchDisp, - extrudeStatus - ); + const scalarField invExpansionRatio(1.0/expansionRatio); - const_cast<Time&>(mesh.time())++; - Info<< "Writing shrunk mesh to time " - << meshRefiner_.timeName() << endl; + // Add topo regardless of whether extrudeStatus is extruderemove. + // Not add layer if patchDisp is zero. + addLayer.setRefinement + ( + globalFaces, + edgeGlobalFaces, - // See comment in snappySnapDriver why we should not remove - // meshPhi using mesh.clearOut(). + invExpansionRatio, + pp, + bitSet(pp.size()), // no flip - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() - ); - } + edgePatchID, // boundary patch for extruded boundary edges + edgeZoneID, // zone for extruded edges + edgeFlip, + inflateFaceID, - // Mesh topo change engine. Insert current mesh. - polyTopoChange meshMod(mesh); + labelList(0), // exposed patchIDs, not used for adding layers + nPatchFaceLayers, // layers per face + nPatchPointLayers, // layers per point + finalDisp, // thickness of layer nearest internal mesh + meshMod + ); - // Grow layer of cells on to patch. Handles zero sized displacement. - addPatchCellLayer addLayer(mesh); + if (debug) + { + const_cast<Time&>(mesh.time())++; + } - // Determine per point/per face number of layers to extrude. Also - // handles the slow termination of layers when going switching - // layers + // Compact storage + meshMod.shrink(); - labelList nPatchPointLayers(pp().nPoints(), -1); - labelList nPatchFaceLayers(pp().size(), -1); - setupLayerInfoTruncation - ( - *pp, - patchNLayers, - extrudeStatus, - layerParams.nBufferCellsNoExtrude(), - nPatchPointLayers, - nPatchFaceLayers - ); + // Store mesh changes for if mesh is correct. + savedMeshMod = meshMod; - // Calculate displacement for final layer for addPatchLayer. - // (layer of cells next to the original mesh) - vectorField finalDisp(patchNLayers.size(), Zero); - forAll(nPatchPointLayers, i) - { - scalar ratio = layerParameters::finalLayerThicknessRatio - ( - nPatchPointLayers[i], - expansionRatio[i] - ); - finalDisp[i] = ratio*patchDisp[i]; - } + // With the stored topo changes we create a new mesh so we can + // undo if necessary. + autoPtr<fvMesh> newMeshPtr; + autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh + ( + newMeshPtr, + IOobject + ( + //mesh.name()+"_layer", + mesh.name(), + static_cast<polyMesh&>(mesh).instance(), + mesh.time(), // register with runTime + IOobject::READ_IF_PRESENT, // read fv* if present + static_cast<polyMesh&>(mesh).writeOpt() + ), // io params from original mesh but new name + mesh, // original mesh + true // parallel sync + ); + fvMesh& newMesh = *newMeshPtr; + mapPolyMesh& map = *mapPtr; - const scalarField invExpansionRatio(1.0/expansionRatio); + // Get timing, but more importantly get memory information + addProfiling(grow, "snappyHexMesh::layers::updateMesh"); - // Add topo regardless of whether extrudeStatus is extruderemove. - // Not add layer if patchDisp is zero. - addLayer.setRefinement - ( - globalFaces, - edgeGlobalFaces, + //?necessary? Update fields + newMesh.updateMesh(map); - invExpansionRatio, - pp(), + newMesh.setInstance(meshRefiner_.timeName()); + + // Update numbering on addLayer: + // - cell/point labels to be newMesh. + // - patchFaces to remain in oldMesh order. + addLayer.updateMesh + ( + map, + identity(pp.size()), + identity(pp.nPoints()) + ); - edgePatchID, // boundary patch for extruded boundary edges - edgeZoneID, // zone for extruded edges - edgeFlip, - inflateFaceID, + // Collect layer faces and cells for outside loop. + getLayerCellsFaces + ( + newMesh, + addLayer, + avgPointData(pp, mag(patchDisp))(), // current thickness + cellNLayers, + faceRealThickness + ); - labelList(0), // exposed patchIDs, not used for adding layers - nPatchFaceLayers, // layers per face - nPatchPointLayers, // layers per point - finalDisp, // thickness of layer nearest internal mesh - meshMod - ); - if (debug) + // Count number of added cells + label nAddedCells = 0; + forAll(cellNLayers, celli) + { + if (cellNLayers[celli] > 0) { - const_cast<Time&>(mesh.time())++; + nAddedCells++; } + } - // Store mesh changes for if mesh is correct. - savedMeshMod = meshMod; + if (debug&meshRefinement::MESH) + { + Info<< "Writing layer mesh to time " << meshRefiner_.timeName() + << endl; + newMesh.write(); + writeLayerSets(newMesh, cellNLayers, faceRealThickness); + + // Reset the instance of the original mesh so next iteration + // it dumps a complete mesh. This is just so that the inbetween + // newMesh does not upset e.g. paraFoam cycling through the + // times. + mesh.setInstance(meshRefiner_.timeName()); + } - // With the stored topo changes we create a new mesh so we can - // undo if necessary. - autoPtr<fvMesh> newMeshPtr; - autoPtr<mapPolyMesh> mapPtr = meshMod.makeMesh + //- Get baffles in newMesh numbering. Note that we cannot detect + // baffles here since the points are duplicated + List<labelPair> internalBaffles; + { + // From old mesh face to corresponding newMesh boundary face + labelList meshToNewMesh(mesh.nFaces(), -1); + for ( - newMeshPtr, - IOobject - ( - //mesh.name()+"_layer", - mesh.name(), - static_cast<polyMesh&>(mesh).instance(), - mesh.time(), // register with runTime - IOobject::READ_IF_PRESENT, // read fv* if present - static_cast<polyMesh&>(mesh).writeOpt() - ), // io params from original mesh but new name - mesh, // original mesh - true // parallel sync - ); - fvMesh& newMesh = *newMeshPtr; - mapPolyMesh& map = *mapPtr; - - // Get timing, but more importantly get memory information - addProfiling(grow, "snappyHexMesh::layers::updateMesh"); - - //?necessary? Update fields - newMesh.updateMesh(map); + label facei = newMesh.nInternalFaces(); + facei < newMesh.nFaces(); + facei++ + ) + { + label newMeshFacei = map.faceMap()[facei]; + if (newMeshFacei != -1) + { + meshToNewMesh[newMeshFacei] = facei; + } + } - newMesh.setInstance(meshRefiner_.timeName()); + List<labelPair> newMeshBaffles(baffles.size()); + label newi = 0; + forAll(baffles, i) + { + const labelPair& p = baffles[i]; + labelPair newMeshBaffle + ( + meshToNewMesh[p[0]], + meshToNewMesh[p[1]] + ); + if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1) + { + newMeshBaffles[newi++] = newMeshBaffle; + } + } + newMeshBaffles.setSize(newi); - // Update numbering on addLayer: - // - cell/point labels to be newMesh. - // - patchFaces to remain in oldMesh order. - addLayer.updateMesh + internalBaffles = meshRefinement::subsetBaffles ( - map, - identity(pp().size()), - identity(pp().nPoints()) + newMesh, + internalFaceZones, + newMeshBaffles ); - // Collect layer faces and cells for outside loop. - getLayerCellsFaces - ( - newMesh, - addLayer, - avgPointData(*pp, mag(patchDisp))(), // current thickness + Info<< "Detected " + << returnReduce(internalBaffles.size(), sumOp<label>()) + << " baffles across faceZones of type internal" << nl + << endl; + } - cellNLayers, - faceRealThickness + label nTotChanged = checkAndUnmark + ( + addLayer, + meshQualityDict, + layerParams.additionalReporting(), + internalBaffles, + pp, + newMesh, + + patchDisp, + patchNLayers, + extrudeStatus + ); + + label nTotExtruded = countExtrusion(pp, extrudeStatus); + label nTotFaces = returnReduce(pp.size(), sumOp<label>()); + label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>()); + + Info<< "Extruding " << nTotExtruded + << " out of " << nTotFaces + << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)." + << " Removed extrusion at " << nTotChanged << " faces." + << endl + << "Added " << nTotAddedCells << " out of " + << nIdealTotAddedCells + << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells + << "%)." << endl; + + if (nTotChanged == 0) + { + break; + } + + // Reset mesh points and start again + mesh.movePoints(oldPoints); + pp.movePoints(mesh.points()); + medialAxisMoverPtr().movePoints(mesh.points()); + + // Grow out region of non-extrusion + for (label i = 0; i < layerParams.nGrow(); i++) + { + growNoExtrusion + ( + pp, + patchDisp, + patchNLayers, + extrudeStatus ); + } + + Info<< endl; + } +} + + +void Foam::snappyLayerDriver::mapFaceZonePoints +( + const mapPolyMesh& map, + labelPairList& baffles, + labelList& pointToMaster +) const +{ + fvMesh& mesh = meshRefiner_.mesh(); + + // Use geometric detection of points-to-be-merged + // - detect any boundary face created from a duplicated face (=baffle) + // - on these mark any point created from a duplicated point + if (returnReduce(pointToMaster.size(), sumOp<label>())) + { + // Estimate number of points-to-be-merged + DynamicList<label> candidates(baffles.size()*4); + + // The problem is that all the internal layer faces also + // have reverseFaceMap pointing to the old baffle face. So instead + // loop over all the boundary faces and see which pair of new boundary + // faces corresponds to the old baffles. + + // Mark whether old face was on baffle + Map<label> oldFaceToBaffle(2*baffles.size()); + forAll(baffles, i) + { + const labelPair& baffle = baffles[i]; + oldFaceToBaffle.insert(baffle[0], i); + oldFaceToBaffle.insert(baffle[1], i); + } - // Count number of added cells - label nAddedCells = 0; - forAll(cellNLayers, celli) + labelPairList newBaffles(baffles.size(), labelPair(-1, -1)); + + for + ( + label facei = mesh.nInternalFaces(); + facei < mesh.nFaces(); + facei++ + ) + { + const label oldFacei = map.faceMap()[facei]; + const auto iter = oldFaceToBaffle.find(oldFacei); + if (oldFacei != -1 && iter.found()) { - if (cellNLayers[celli] > 0) + const label bafflei = iter(); + auto& newBaffle = newBaffles[bafflei]; + if (newBaffle[0] == -1) + { + newBaffle[0] = facei; + } + else if (newBaffle[1] == -1) + { + newBaffle[1] = facei; + } + else + { + FatalErrorInFunction << "face:" << facei + << " at:" << mesh.faceCentres()[facei] + << " already maps to baffle faces:" + << newBaffle[0] + << " at:" << mesh.faceCentres()[newBaffle[0]] + << " and " << newBaffle[1] + << " at:" << mesh.faceCentres()[newBaffle[1]] + << exit(FatalError); + } + + const face& f = mesh.faces()[facei]; + forAll(f, fp) { - nAddedCells++; + label pointi = f[fp]; + label oldPointi = map.pointMap()[pointi]; + + if (pointToMaster[oldPointi] != -1) + { + candidates.append(pointi); + } } } + } - if (debug&meshRefinement::MESH) + // Compact newBaffles + { + label n = 0; + forAll(newBaffles, i) { - Info<< "Writing layer mesh to time " << meshRefiner_.timeName() - << endl; - newMesh.write(); - writeLayerSets(newMesh, cellNLayers, faceRealThickness); - - // Reset the instance of the original mesh so next iteration - // it dumps a complete mesh. This is just so that the inbetween - // newMesh does not upset e.g. paraFoam cycling through the - // times. - mesh.setInstance(meshRefiner_.timeName()); + const labelPair& newBaffle = newBaffles[i]; + if (newBaffle[0] != -1 && newBaffle[1] != -1) + { + newBaffles[n++] = newBaffle; + } } + newBaffles.setSize(n); + baffles.transfer(newBaffles); + } + + + // Do geometric merge. Ideally we'd like to use a topological + // merge but we've thrown away all layer-wise addressing when + // throwing away addPatchCellLayer engine. Also the addressing + // is extremely complicated. There is no problem with merging + // too many points; the problem would be if merging baffles. + // Trust mergeZoneBaffles to do sufficient checks. + labelList oldToNew; + label nNew = mergePoints + ( + pointField(mesh.points(), candidates), + meshRefiner_.mergeDistance(), + false, + oldToNew + ); + + // Extract points to be merged (i.e. multiple points originating + // from a single one) + + labelListList newToOld(invertOneToMany(nNew, oldToNew)); - //- Get baffles in newMesh numbering. Note that we cannot detect - // baffles here since the points are duplicated - List<labelPair> internalBaffles; + // Extract points with more than one old one + pointToMaster.setSize(mesh.nPoints()); + pointToMaster = -1; + + forAll(newToOld, newi) + { + const labelList& oldPoints = newToOld[newi]; + if (oldPoints.size() > 1) { - // From old mesh face to corresponding newMesh boundary face - labelList meshToNewMesh(mesh.nFaces(), -1); - for + labelList meshPoints ( - label facei = newMesh.nInternalFaces(); - facei < newMesh.nFaces(); - facei++ - ) + labelUIndList(candidates, oldPoints) + ); + label masteri = min(meshPoints); + forAll(meshPoints, i) { - label newMeshFacei = map.faceMap()[facei]; - if (newMeshFacei != -1) - { - meshToNewMesh[newMeshFacei] = facei; - } + pointToMaster[meshPoints[i]] = masteri; } + } + } + } +} + + +void Foam::snappyLayerDriver::updatePatch +( + const labelList& patchIDs, + const mapPolyMesh& map, + autoPtr<indirectPrimitivePatch>& pp, + labelList& newToOldPatchPoints +) const +{ + // Update the pp to be consistent with the new mesh + + fvMesh& mesh = meshRefiner_.mesh(); + + autoPtr<indirectPrimitivePatch> newPp + ( + meshRefinement::makePatch + ( + mesh, + patchIDs + ) + ); + + // Map from new back to old patch points + newToOldPatchPoints.setSize(newPp().nPoints()); + newToOldPatchPoints = -1; + { + const Map<label>& baseMap = pp().meshPointMap(); + const labelList& newMeshPoints = newPp().meshPoints(); + + forAll(newMeshPoints, newPointi) + { + const label newMeshPointi = newMeshPoints[newPointi]; + const label oldMeshPointi = + map.pointMap()[newMeshPointi]; + const auto iter = baseMap.find(oldMeshPointi); + if (iter.found()) + { + newToOldPatchPoints[newPointi] = iter(); + } + } + } + + + // Reset pp. Note: make sure to use std::move - otherwise it + // will release the pointer before copying and you get + // memory error. Same if using autoPtr::reset. + pp = std::move(newPp); + + // Make sure pp has adressing cached + (void)pp().meshPoints(); + (void)pp().meshPointMap(); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::snappyLayerDriver::snappyLayerDriver +( + meshRefinement& meshRefiner, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const bool dryRun +) +: + meshRefiner_(meshRefiner), + globalToMasterPatch_(globalToMasterPatch), + globalToSlavePatch_(globalToSlavePatch), + dryRun_(dryRun) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::snappyLayerDriver::mergePatchFacesUndo +( + const layerParameters& layerParams, + const dictionary& motionDict, + const meshRefinement::FaceMergeType mergeType +) +{ + // Clip to 30 degrees. Not helpful! + //scalar planarAngle = min(30.0, layerParams.featureAngle()); + scalar planarAngle = layerParams.mergePatchFacesAngle(); + scalar minCos = Foam::cos(degToRad(planarAngle)); + + scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle())); + + Info<< nl + << "Merging all faces of a cell" << nl + << "---------------------------" << nl + << " - which are on the same patch" << nl + << " - which make an angle < " << planarAngle + << " degrees" + << " (cos:" << minCos << ')' << nl + << " - as long as the resulting face doesn't become concave" + << " by more than " + << layerParams.concaveAngle() << " degrees" << nl + << " (0=straight, 180=fully concave)" << nl + << endl; + + const fvMesh& mesh = meshRefiner_.mesh(); + + List<labelPair> couples(localPointRegion::findDuplicateFacePairs(mesh)); + + labelList duplicateFace(mesh.nFaces(), -1); + forAll(couples, i) + { + const labelPair& cpl = couples[i]; + duplicateFace[cpl[0]] = cpl[1]; + duplicateFace[cpl[1]] = cpl[0]; + } + + label nChanged = meshRefiner_.mergePatchFacesUndo + ( + minCos, + concaveCos, + meshRefiner_.meshedPatches(), + motionDict, + duplicateFace, + mergeType // How to merge co-planar patch faces + ); + + nChanged += meshRefiner_.mergeEdgesUndo(minCos, motionDict); +} + + +void Foam::snappyLayerDriver::addLayers +( + const layerParameters& layerParams, + const dictionary& motionDict, + const labelList& patchIDs, + const label nAllowableErrors, + decompositionMethod& decomposer, + fvMeshDistribute& distributor +) +{ + fvMesh& mesh = meshRefiner_.mesh(); + + // Undistorted edge length + const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength(); + + // faceZones of type internal or baffle (for merging points across) + labelList internalOrBaffleFaceZones; + { + List<surfaceZonesInfo::faceZoneType> fzTypes(2); + fzTypes[0] = surfaceZonesInfo::INTERNAL; + fzTypes[1] = surfaceZonesInfo::BAFFLE; + internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes); + } + + // faceZones of type internal (for checking mesh quality across and + // merging baffles) + const labelList internalFaceZones + ( + meshRefiner_.getZones + ( + List<surfaceZonesInfo::faceZoneType> + ( + 1, + surfaceZonesInfo::INTERNAL + ) + ) + ); + + // Create baffles (pairs of faces that share the same points) + // Baffles stored as owner and neighbour face that have been created. + List<labelPair> baffles; + { + labelList originatingFaceZone; + meshRefiner_.createZoneBaffles + ( + identity(mesh.faceZones().size()), + baffles, + originatingFaceZone + ); + + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing baffled mesh to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } + } + + + // Duplicate points on faceZones of type boundary. Should normally already + // be done by snapping phase + { + autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints(); + if (map) + { + const labelList& reverseFaceMap = map->reverseFaceMap(); + forAll(baffles, i) + { + label f0 = reverseFaceMap[baffles[i].first()]; + label f1 = reverseFaceMap[baffles[i].second()]; + baffles[i] = labelPair(f0, f1); + } + } + } - List<labelPair> newMeshBaffles(baffles.size()); - label newi = 0; - forAll(baffles, i) + + + // Now we have all patches determine the number of layer per patch + // This will be layerParams.numLayers() except for faceZones where one + // side does get layers and the other not in which case we want to + // suppress movement by explicitly setting numLayers 0 + labelList numLayers(layerParams.numLayers()); + { + labelHashSet layerIDs(patchIDs); + forAll(mesh.faceZones(), zonei) + { + label mpi, spi; + surfaceZonesInfo::faceZoneType fzType; + bool hasInfo = meshRefiner_.getFaceZoneInfo + ( + mesh.faceZones()[zonei].name(), + mpi, + spi, + fzType + ); + if (hasInfo) + { + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + if (layerIDs.found(mpi) && !layerIDs.found(spi)) { - const labelPair& p = baffles[i]; - labelPair newMeshBaffle - ( - meshToNewMesh[p[0]], - meshToNewMesh[p[1]] - ); - if (newMeshBaffle[0] != -1 && newMeshBaffle[1] != -1) - { - newMeshBaffles[newi++] = newMeshBaffle; - } + // Only layers on master side. Fix points on slave side + Info<< "On faceZone " << mesh.faceZones()[zonei].name() + << " adding layers to master patch " << pbm[mpi].name() + << " only. Freezing points on slave patch " + << pbm[spi].name() << endl; + numLayers[spi] = 0; + } + else if (!layerIDs.found(mpi) && layerIDs.found(spi)) + { + // Only layers on slave side. Fix points on master side + Info<< "On faceZone " << mesh.faceZones()[zonei].name() + << " adding layers to slave patch " << pbm[spi].name() + << " only. Freezing points on master patch " + << pbm[mpi].name() << endl; + numLayers[mpi] = 0; } - newMeshBaffles.setSize(newi); + } + } + } + + + // Duplicate points on faceZones that layers are added to + labelList pointToMaster; + dupFaceZonePoints + ( + patchIDs, // patch indices + numLayers, // number of layers per patch + baffles, + pointToMaster + ); + + + // Add layers to patches + // ~~~~~~~~~~~~~~~~~~~~~ + + // Now we have + // - mesh with optional baffles and duplicated points for faceZones + // where layers are to be added + // - pointToMaster : correspondence for duplicated points + // - baffles : list of pairs of faces + + + // Calculate 'base' point extrusion + autoPtr<indirectPrimitivePatch> pp + ( + meshRefinement::makePatch + ( + mesh, + patchIDs + ) + ); + // Make sure pp has adressing cached before changing mesh later on + (void)pp().meshPoints(); + (void)pp().meshPointMap(); + + // Global face indices engine + globalIndex globalFaces(mesh.nFaces()); + + // Determine extrudePatch.edgeFaces in global numbering (so across + // coupled patches). This is used only to string up edges between coupled + // faces (all edges between same (global)face indices get extruded). + labelListList edgeGlobalFaces + ( + addPatchCellLayer::globalEdgeFaces + ( + mesh, + globalFaces, + *pp + ) + ); + + + // Point-wise extrusion data + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Displacement for all pp.localPoints. Set to large value so does + // not trigger the minThickness truncation (see syncPatchDisplacement below) + vectorField basePatchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT)); + + // Number of layers for all pp.localPoints. Note: only valid if + // extrudeStatus = EXTRUDE. + labelList basePatchNLayers(pp().nPoints(), Zero); + + // Whether to add edge for all pp.localPoints. + List<extrudeMode> baseExtrudeStatus(pp().nPoints(), EXTRUDE); + + // Ideal number of cells added + const label nIdealTotAddedCells = setPointNumLayers + ( + layerParams, + + numLayers, + patchIDs, + pp(), + edgeGlobalFaces, + + basePatchDisp, + basePatchNLayers, + baseExtrudeStatus + ); + + // Overall thickness of all layers + scalarField baseThickness(pp().nPoints()); + // Truncation thickness - when to truncate layers + scalarIOField baseMinThickness + ( + IOobject + ( + "minThickness", + meshRefiner_.timeName(), + mesh, + IOobject::NO_READ + ), + pp().nPoints() + ); + // Expansion ratio + scalarField baseExpansionRatio(pp().nPoints()); + calculateLayerThickness + ( + pp(), + patchIDs, + layerParams, + meshRefiner_.meshCutter().cellLevel(), + basePatchNLayers, + edge0Len, + + baseThickness, + baseMinThickness, + baseExpansionRatio + ); + + + // Per cell 0 or number of layers in the cell column it is part of + labelList cellNLayers; + // Per face actual overall layer thickness + scalarField faceRealThickness; + // Per face wanted overall layer thickness + scalarField faceWantedThickness(mesh.nFaces(), Zero); + { + UIndirectList<scalar>(faceWantedThickness, pp().addressing()) = + avgPointData(pp(), baseThickness); + } + + + // Per patch point the number of layers to add. Is basePatchNLayers + // for nOuterIter = 1. + labelList deltaNLayers + ( + (basePatchNLayers+layerParams.nOuterIter()-1) + /layerParams.nOuterIter() + ); + + // Per patch point the sum of added layers so far + labelList nAddedLayers(basePatchNLayers.size(), 0); + + + for (label layeri = 0; layeri < layerParams.nOuterIter(); layeri++) + { + // Divide layer addition into outer iterations. E.g. if + // nOutIter = 2, numLayers is 20 for patchA and 1 for patchB + // this will + // - iter0: + // - add 10 layers to patchA and 1 to patchB + // - layers are finalLayerThickness down to the number of layers + // - iter1 : add 10 layer to patchA and 0 to patchB + + + // Exit if nothing to be added + const label nToAdd = gSum(deltaNLayers); + if (debug) + { + Info<< "Outer iteration : " << layeri + << " to add in current iteration : " << nToAdd << endl; + } + if (nToAdd == 0) + { + break; + } + + + // Determine patches for extruded boundary edges. Calculates if any + // additional processor patches need to be constructed. + + labelList edgePatchID; + labelList edgeZoneID; + boolList edgeFlip; + labelList inflateFaceID; + determineSidePatches + ( + globalFaces, + edgeGlobalFaces, + *pp, + + edgePatchID, + edgeZoneID, + edgeFlip, + inflateFaceID + ); - internalBaffles = meshRefinement::subsetBaffles - ( - newMesh, - internalFaceZones, - newMeshBaffles - ); - Info<< "Detected " - << returnReduce(internalBaffles.size(), sumOp<label>()) - << " baffles across faceZones of type internal" << nl - << endl; - } + // Point-wise extrusion data + // ~~~~~~~~~~~~~~~~~~~~~~~~~ - label nTotChanged = checkAndUnmark - ( - addLayer, - meshQualityDict, - layerParams.additionalReporting(), - internalBaffles, - pp(), - newMesh, + // Displacement for all pp.localPoints. Set to large value so does + // not trigger the minThickness truncation (see syncPatchDisplacement + // below) + vectorField patchDisp(basePatchDisp); - patchDisp, - patchNLayers, - extrudeStatus - ); + // Number of layers for all pp.localPoints. Note: only valid if + // extrudeStatus = EXTRUDE. + labelList patchNLayers(deltaNLayers); - label nTotExtruded = countExtrusion(*pp, extrudeStatus); - label nTotFaces = returnReduce(pp().size(), sumOp<label>()); - label nTotAddedCells = returnReduce(nAddedCells, sumOp<label>()); - - Info<< "Extruding " << nTotExtruded - << " out of " << nTotFaces - << " faces (" << 100.0*nTotExtruded/nTotFaces << "%)." - << " Removed extrusion at " << nTotChanged << " faces." - << endl - << "Added " << nTotAddedCells << " out of " - << nIdealTotAddedCells - << " cells (" << 100.0*nTotAddedCells/nIdealTotAddedCells - << "%)." << endl; - - if (nTotChanged == 0) - { - break; - } + // Whether to add edge for all pp.localPoints. + List<extrudeMode> extrudeStatus(baseExtrudeStatus); - // Reset mesh points and start again - mesh.movePoints(oldPoints); - pp().movePoints(mesh.points()); - medialAxisMoverPtr().movePoints(mesh.points()); + // At this point + // - patchDisp is either zero or a large value + // - patchNLayers is the overall number of layers + // - extrudeStatus is EXTRUDE or NOEXTRUDE - // Grow out region of non-extrusion - for (label i = 0; i < layerParams.nGrow(); i++) + // Determine (wanted) point-wise overall layer thickness and expansion + // ratio for this deltaNLayers slice of the overall layers + scalarField sliceThickness(pp().nPoints()); + { + forAll(baseThickness, pointi) { - growNoExtrusion + sliceThickness[pointi] = layerParameters::layerThickness ( - *pp, - patchDisp, - patchNLayers, - extrudeStatus + basePatchNLayers[pointi], // overall number of layers + baseThickness[pointi], // overall thickness + baseExpansionRatio[pointi], // expansion ratio + basePatchNLayers[pointi] + -nAddedLayers[pointi] + -patchNLayers[pointi], // start index + patchNLayers[pointi] // nLayers to add ); } - - Info<< endl; } - } - // At this point we have a (shrunk) mesh and a set of topology changes - // which will make a valid mesh with layer. Apply these changes to the - // current mesh. + // Current set of topology changes. (changing mesh clears out + // polyTopoChange) + polyTopoChange meshMod(mesh.boundaryMesh().size()); - { - // Apply the stored topo changes to the current mesh. - autoPtr<mapPolyMesh> mapPtr = savedMeshMod.changeMesh(mesh, false); - mapPolyMesh& map = *mapPtr; + // Shrink mesh, add layers. Returns with any mesh changes in meshMod + labelList sliceCellNLayers; + scalarField sliceFaceRealThickness; - // Hack to remove meshPhi - mapped incorrectly. TBD. - mesh.clearOut(); + addLayers + ( + layerParams, + layerParams.nLayerIter(), - // Update fields - mesh.updateMesh(map); + // Mesh quality + motionDict, + layerParams.nRelaxedIter(), + nAllowableErrors, - // Move mesh (since morphing does not do this) - if (map.hasMotionPoints()) - { - mesh.movePoints(map.preMotionPoints()); - } - else - { - // Delete mesh volumes. - mesh.clearOut(); - } + patchIDs, + internalFaceZones, + baffles, + numLayers, + nIdealTotAddedCells, - // Reset the instance for if in overwrite mode - mesh.setInstance(meshRefiner_.timeName()); + // Patch information + globalFaces, + pp(), + edgeGlobalFaces, + edgePatchID, + edgeZoneID, + edgeFlip, + inflateFaceID, - meshRefiner_.updateMesh(map, labelList(0)); + // Per patch point the wanted thickness + sliceThickness, + baseMinThickness, + baseExpansionRatio, - // Update numbering of faceWantedThickness - meshRefinement::updateList - ( - map.faceMap(), - scalar(0), - faceWantedThickness - ); + // Per patch point the wanted&obtained layers and thickness + patchDisp, + patchNLayers, + extrudeStatus, - // Print data now that we still have patches for the zones - //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO) - printLayerData - ( - mesh, - patchIDs, - cellNLayers, - faceWantedThickness, - faceRealThickness + // Complete mesh changes + meshMod, + + // Stats on new mesh + sliceCellNLayers, //cellNLayers, + sliceFaceRealThickness //faceRealThickness ); - // Dump for debugging - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + // Exit if nothing added + const label nTotalAdded = gSum(patchNLayers); + if (debug) { - const_cast<Time&>(mesh.time())++; - Info<< "Writing mesh with layers but disconnected to time " - << meshRefiner_.timeName() << endl; - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() - ); + Info<< "Outer iteration : " << layeri + << " added in current iteration : " << nTotalAdded + << " out of : " << gSum(deltaNLayers) << endl; + } + if (nTotalAdded == 0) + { + break; } - // Use geometric detection of points-to-be-merged - // - detect any boundary face created from a duplicated face (=baffle) - // - on these mark any point created from a duplicated point - if (returnReduce(pointToMaster.size(), sumOp<label>())) + // Update wanted layer statistics + forAll(patchNLayers, pointi) { - // Estimate number of points-to-be-merged - DynamicList<label> candidates(baffles.size()*4); + nAddedLayers[pointi] += patchNLayers[pointi]; - // Mark whether old face was on baffle - bitSet oldBaffleFace(map.nOldFaces()); - forAll(baffles, i) + if (patchNLayers[pointi] == 0) { - const labelPair& baffle = baffles[i]; - oldBaffleFace.set(baffle[0]); - oldBaffleFace.set(baffle[1]); + // No layers were added. Make sure that overall extrusion + // gets reset as well + unmarkExtrusion + ( + pointi, + basePatchDisp, + basePatchNLayers, + baseExtrudeStatus + ); + basePatchNLayers[pointi] = nAddedLayers[pointi]; + deltaNLayers[pointi] = 0; } - - // Collect candidate if - // - point on boundary face originating from baffle - // - and point originating from duplicate - for - ( - label facei = mesh.nInternalFaces(); - facei < mesh.nFaces(); - facei++ - ) + else { - label oldFacei = map.faceMap()[facei]; - if (oldFacei != -1 && oldBaffleFace.test(oldFacei)) - { - const face& f = mesh.faces()[facei]; - forAll(f, fp) - { - label pointi = f[fp]; - label oldPointi = map.pointMap()[pointi]; - - if (pointToMaster[oldPointi] != -1) - { - candidates.append(pointi); - } - } - } + // Adjust the number of layers for the next iteration. + // Can never be + // higher than the adjusted overall number of layers. + // Note: is min necessary? + deltaNLayers[pointi] = max + ( + 0, + min + ( + deltaNLayers[pointi], + basePatchNLayers[pointi] - nAddedLayers[pointi] + ) + ); } + } - // Do geometric merge. Ideally we'd like to use a topological - // merge but we've thrown away all layer-wise addressing when - // throwing away addPatchCellLayer engine. Also the addressing - // is extremely complicated. There is no problem with merging - // too many points; the problem would be if merging baffles. - // Trust mergeZoneBaffles to do sufficient checks. - labelList oldToNew; - label nNew = mergePoints - ( - pointField(mesh.points(), candidates), - meshRefiner_.mergeDistance(), - false, - oldToNew - ); + // At this point we have a (shrunk) mesh and a set of topology changes + // which will make a valid mesh with layer. Apply these changes to the + // current mesh. - // Extract points to be merged (i.e. multiple points originating - // from a single one) + { + // Apply the stored topo changes to the current mesh. + autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false); + mapPolyMesh& map = *mapPtr; - labelListList newToOld(invertOneToMany(nNew, oldToNew)); + // Hack to remove meshPhi - mapped incorrectly. TBD. + mesh.clearOut(); - // Extract points with more than one old one - pointToMaster.setSize(mesh.nPoints()); - pointToMaster = -1; + // Update fields + mesh.updateMesh(map); - forAll(newToOld, newi) + // Move mesh (since morphing does not do this) + if (map.hasMotionPoints()) { - const labelList& oldPoints = newToOld[newi]; - if (oldPoints.size() > 1) - { - labelList meshPoints - ( - labelUIndList(candidates, oldPoints) - ); - label masteri = min(meshPoints); - forAll(meshPoints, i) - { - pointToMaster[meshPoints[i]] = masteri; - } - } + mesh.movePoints(map.preMotionPoints()); + } + else + { + // Delete mesh volumes. + mesh.clearOut(); } - } - } + // Reset the instance for if in overwrite mode + mesh.setInstance(meshRefiner_.timeName()); + meshRefiner_.updateMesh(map, labelList(0)); + // Update numbering of accumulated cells + cellNLayers.setSize(map.nOldCells(), 0); + meshRefinement::updateList + ( + map.cellMap(), + 0, + cellNLayers + ); + forAll(cellNLayers, i) + { + cellNLayers[i] += sliceCellNLayers[i]; + } + faceRealThickness.setSize(map.nOldFaces(), scalar(0)); + meshRefinement::updateList + ( + map.faceMap(), + scalar(0), + faceRealThickness + ); + faceRealThickness += sliceFaceRealThickness; - // Count duplicate points - label nPointPairs = 0; - forAll(pointToMaster, pointi) - { - label otherPointi = pointToMaster[pointi]; - if (otherPointi != -1) - { - nPointPairs++; - } - } - reduce(nPointPairs, sumOp<label>()); - if (nPointPairs > 0) - { - // Merge any duplicated points - Info<< "Merging " << nPointPairs << " duplicated points ..." << endl; + meshRefinement::updateList + ( + map.faceMap(), + scalar(0), + faceWantedThickness + ); - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) - { - OBJstream str + // Print data now that we still have patches for the zones + //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO) + printLayerData ( - mesh.time().path() - / "mergePoints_" - + meshRefiner_.timeName() - + ".obj" + mesh, + patchIDs, + cellNLayers, + faceWantedThickness, + faceRealThickness ); - Info<< "Points to be merged to " << str.name() << endl; - forAll(pointToMaster, pointi) + + + // Dump for debugging + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) { - label otherPointi = pointToMaster[pointi]; - if (otherPointi != -1) - { - const point& pt = mesh.points()[pointi]; - const point& otherPt = mesh.points()[otherPointi]; - str.write(linePointRef(pt, otherPt)); - } + const_cast<Time&>(mesh.time())++; + Info<< "Writing mesh with layers but disconnected to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); } - } - autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToMaster); - if (map) - { - inplaceReorder(map().reverseCellMap(), cellNLayers); + // Map baffles, pointToMaster + mapFaceZonePoints(map, baffles, pointToMaster); - const labelList& reverseFaceMap = map().reverseFaceMap(); - inplaceReorder(reverseFaceMap, faceWantedThickness); - inplaceReorder(reverseFaceMap, faceRealThickness); + // Map patch and layer settings + labelList newToOldPatchPoints; + updatePatch(patchIDs, map, pp, newToOldPatchPoints); - Info<< "Merged points in = " - << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; - } - } + // Global face indices engine + globalFaces.reset(mesh.nFaces()); - if (mesh.faceZones().size() > 0) - { - // Merge any baffles - Info<< "Converting baffles back into zoned faces ..." - << endl; + // Patch-edges to global faces using them + edgeGlobalFaces = addPatchCellLayer::globalEdgeFaces + ( + mesh, + globalFaces, + pp() + ); - autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles - ( - true, // internal zones - false // baffle zones - ); - if (map) - { - inplaceReorder(map().reverseCellMap(), cellNLayers); + // Map patch-point based data + meshRefinement::updateList + ( + newToOldPatchPoints, + vector::uniform(-1), + basePatchDisp + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + 0, + basePatchNLayers + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + extrudeMode::NOEXTRUDE, + baseExtrudeStatus + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + scalar(0), + baseThickness + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + scalar(0), + baseMinThickness + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + GREAT, + baseExpansionRatio + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + 0, + deltaNLayers + ); + meshRefinement::updateList + ( + newToOldPatchPoints, + 0, + nAddedLayers + ); + } + } - const labelList& faceMap = map().faceMap(); - // Make sure to keep the max since on two patches only one has - // layers. - scalarField newFaceRealThickness(mesh.nFaces(), Zero); - scalarField newFaceWantedThickness(mesh.nFaces(), Zero); - forAll(newFaceRealThickness, facei) - { - label oldFacei = faceMap[facei]; - if (oldFacei >= 0) - { - scalar& realThick = newFaceRealThickness[facei]; - realThick = max(realThick, faceRealThickness[oldFacei]); - scalar& wanted = newFaceWantedThickness[facei]; - wanted = max(wanted, faceWantedThickness[oldFacei]); - } - } - faceRealThickness.transfer(newFaceRealThickness); - faceWantedThickness.transfer(newFaceWantedThickness); - } + // Merge baffles + mergeFaceZonePoints + ( + pointToMaster, // per new mesh point : -1 or index of duplicated point + cellNLayers, // per new cell : number of layers + faceRealThickness, // per new face : actual thickness + faceWantedThickness // per new face : wanted thickness + ); - Info<< "Converted baffles in = " - << meshRefiner_.mesh().time().cpuTimeIncrement() - << " s\n" << nl << endl; - } // Do final balancing // ~~~~~~~~~~~~~~~~~~ @@ -5032,15 +5569,31 @@ void Foam::snappyLayerDriver::doLayers // Do all topo changes - addLayers - ( - layerParams, - motionDict, - patchIDs, - nInitErrors, - decomposer, - distributor - ); + if (layerParams.nOuterIter() == -1) + { + // For testing. Can be removed once addLayers below works + addLayersSinglePass + ( + layerParams, + motionDict, + patchIDs, + nInitErrors, + decomposer, + distributor + ); + } + else + { + addLayers + ( + layerParams, + motionDict, + patchIDs, + nInitErrors, + decomposer, + distributor + ); + } } } diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.H index 453cd5554b3..e24d83d11cf 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.H +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriver.H @@ -32,6 +32,7 @@ Description SourceFiles snappyLayerDriver.C + snappyLayerDriverOneByOne.C \*---------------------------------------------------------------------------*/ @@ -39,6 +40,7 @@ SourceFiles #define snappyLayerDriver_H #include "meshRefinement.H" +#include "scalarIOField.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -52,6 +54,7 @@ class motionSmoother; class addPatchCellLayer; class faceSet; class layerParameters; +class externalDisplacementMeshMover; /*---------------------------------------------------------------------------*\ Class snappyLayerDriver Declaration @@ -250,12 +253,50 @@ private: const labelList& patchToNLayers, const labelList& patchIDs, const indirectPrimitivePatch& pp, - pointField& patchDisp, + labelList& patchNLayers, List<extrudeMode>& extrudeStatus, label& nIdealAddedCells ) const; + //- Determine number of layers per point; include static checks + //- on invalid extrusion (e.g. non-manifold) + label setPointNumLayers + ( + const layerParameters& layerParams, + + const labelList& numLayers, + const labelList& patchIDs, + const indirectPrimitivePatch& pp, + const labelListList& edgeGlobalFaces, + + vectorField& patchDisp, + labelList& patchNLayers, + List<extrudeMode>& + ) const; + autoPtr<externalDisplacementMeshMover> makeMeshMover + ( + const layerParameters& layerParams, + const dictionary& motionDict, + const labelList& internalFaceZones, + const scalarIOField& minThickness, + pointVectorField& displacement + ) const; + void mapFaceZonePoints + ( + const mapPolyMesh& map, + labelPairList& baffles, + labelList& pointToMaster + ) const; + void updatePatch + ( + const labelList& patchIDs, + const mapPolyMesh& map, + autoPtr<indirectPrimitivePatch>& pp, + labelList& newToOldPatchPoints + ) const; + + //- Helper function to make a pointVectorField with correct // bcs for layer addition: // - numLayers > 0 : fixedValue @@ -278,6 +319,25 @@ private: List<extrudeMode>& extrudeStatus ) const; + //- Duplicate points on faceZones with layers + void dupFaceZonePoints + ( + const labelList& patchIDs, // patch indices + const labelList& numLayers, // number of layers per patch + List<labelPair> baffles, + labelList& pointToMaster + ); + + //- Re-merge points/faces on faceZones. Opposite of + //- dupFaceZonePoints above + void mergeFaceZonePoints + ( + const labelList& pointToMaster, + labelList& cellNLayers, + scalarField& faceRealThickness, + scalarField& faceWantedThickness + ); + //- See what zones and patches edges should be extruded into void determineSidePatches ( @@ -600,6 +660,39 @@ public: const meshRefinement::FaceMergeType mergeType ); + void addLayers + ( + const layerParameters& layerParams, + const label nLayerIter, + + const dictionary& motionDict, + const label nRelaxedIter, + const label nAllowableErrors, + + const labelList& patchIDs, + const labelList& internalFaceZones, + const List<labelPair>& baffles, + const labelList& numLayers, + const label nIdealTotAddedCells, + + const globalIndex& globalFaces, + indirectPrimitivePatch& pp, + const labelListList& edgeGlobalFaces, + const labelList& edgePatchID, + const labelList& edgeZoneID, + const boolList& edgeFlip, + const labelList& inflateFaceID, + const scalarField& thickness, + const scalarIOField& minThickness, + const scalarField& expansionRatio, + vectorField& patchDisp, + labelList& patchNLayers, + List<extrudeMode>& extrudeStatus, + polyTopoChange& savedMeshMod, + labelList& cellNLayers, + scalarField& faceRealThickness + ); + //- Add cell layers void addLayers ( @@ -611,6 +704,17 @@ public: fvMeshDistribute& distributor ); + //- For debugging. Can be removed. + void addLayersSinglePass + ( + const layerParameters& layerParams, + const dictionary& motionDict, + const labelList& patchIDs, + const label nAllowableErrors, + decompositionMethod& decomposer, + fvMeshDistribute& distributor + ); + //- Add layers according to the dictionary settings void doLayers ( diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriverSinglePass.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriverSinglePass.C new file mode 100644 index 00000000000..12eb3eaff5e --- /dev/null +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyLayerDriverSinglePass.C @@ -0,0 +1,500 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Description + Single pass layer addition. Can be removed once multi-pass works ok. + +\*----------------------------------------------------------------------------*/ + +#include "snappyLayerDriver.H" +//#include "motionSmoother.H" +//#include "pointSet.H" +//#include "faceSet.H" +//#include "cellSet.H" +#include "polyTopoChange.H" +#include "mapPolyMesh.H" +#include "addPatchCellLayer.H" +#include "mapDistributePolyMesh.H" +//#include "OBJstream.H" +#include "layerParameters.H" +#include "externalDisplacementMeshMover.H" +//#include "profiling.H" + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::snappyLayerDriver::addLayersSinglePass +( + const layerParameters& layerParams, + const dictionary& motionDict, + const labelList& patchIDs, + const label nAllowableErrors, + decompositionMethod& decomposer, + fvMeshDistribute& distributor +) +{ + fvMesh& mesh = meshRefiner_.mesh(); + + // Undistorted edge length + const scalar edge0Len = meshRefiner_.meshCutter().level0EdgeLength(); + + // faceZones of type internal or baffle (for merging points across) + labelList internalOrBaffleFaceZones; + { + List<surfaceZonesInfo::faceZoneType> fzTypes(2); + fzTypes[0] = surfaceZonesInfo::INTERNAL; + fzTypes[1] = surfaceZonesInfo::BAFFLE; + internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes); + } + + // faceZones of type internal (for checking mesh quality across and + // merging baffles) + const labelList internalFaceZones + ( + meshRefiner_.getZones + ( + List<surfaceZonesInfo::faceZoneType> + ( + 1, + surfaceZonesInfo::INTERNAL + ) + ) + ); + + // Create baffles (pairs of faces that share the same points) + // Baffles stored as owner and neighbour face that have been created. + List<labelPair> baffles; + { + labelList originatingFaceZone; + meshRefiner_.createZoneBaffles + ( + identity(mesh.faceZones().size()), + baffles, + originatingFaceZone + ); + + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing baffled mesh to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } + } + + + // Duplicate points on faceZones of type boundary. Should normally already + // be done by snapping phase + { + autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldBoundaryPoints(); + if (map) + { + const labelList& reverseFaceMap = map->reverseFaceMap(); + forAll(baffles, i) + { + label f0 = reverseFaceMap[baffles[i].first()]; + label f1 = reverseFaceMap[baffles[i].second()]; + baffles[i] = labelPair(f0, f1); + } + } + } + + + + // Now we have all patches determine the number of layer per patch + // This will be layerParams.numLayers() except for faceZones where one + // side does get layers and the other not in which case we want to + // suppress movement by explicitly setting numLayers 0 + labelList numLayers(layerParams.numLayers()); + { + labelHashSet layerIDs(patchIDs); + forAll(mesh.faceZones(), zonei) + { + label mpi, spi; + surfaceZonesInfo::faceZoneType fzType; + bool hasInfo = meshRefiner_.getFaceZoneInfo + ( + mesh.faceZones()[zonei].name(), + mpi, + spi, + fzType + ); + if (hasInfo) + { + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + if (layerIDs.found(mpi) && !layerIDs.found(spi)) + { + // Only layers on master side. Fix points on slave side + Info<< "On faceZone " << mesh.faceZones()[zonei].name() + << " adding layers to master patch " << pbm[mpi].name() + << " only. Freezing points on slave patch " + << pbm[spi].name() << endl; + numLayers[spi] = 0; + } + else if (!layerIDs.found(mpi) && layerIDs.found(spi)) + { + // Only layers on slave side. Fix points on master side + Info<< "On faceZone " << mesh.faceZones()[zonei].name() + << " adding layers to slave patch " << pbm[spi].name() + << " only. Freezing points on master patch " + << pbm[mpi].name() << endl; + numLayers[mpi] = 0; + } + } + } + } + + + // Duplicate points on faceZones that layers are added to + labelList pointToMaster; + dupFaceZonePoints + ( + patchIDs, // patch indices + numLayers, // number of layers per patch + baffles, + pointToMaster + ); + + + // Add layers to patches + // ~~~~~~~~~~~~~~~~~~~~~ + + // Now we have + // - mesh with optional baffles and duplicated points for faceZones + // where layers are to be added + // - pointToMaster : correspondence for duplicated points + // - baffles : list of pairs of faces + + + autoPtr<indirectPrimitivePatch> pp + ( + meshRefinement::makePatch + ( + mesh, + patchIDs + ) + ); + + + // Global face indices engine + const globalIndex globalFaces(mesh.nFaces()); + + // Determine extrudePatch.edgeFaces in global numbering (so across + // coupled patches). This is used only to string up edges between coupled + // faces (all edges between same (global)face indices get extruded). + const labelListList edgeGlobalFaces + ( + addPatchCellLayer::globalEdgeFaces + ( + mesh, + globalFaces, + *pp + ) + ); + + // Determine patches for extruded boundary edges. Calculates if any + // additional processor patches need to be constructed. + + labelList edgePatchID; + labelList edgeZoneID; + boolList edgeFlip; + labelList inflateFaceID; + determineSidePatches + ( + globalFaces, + edgeGlobalFaces, + *pp, + + edgePatchID, + edgeZoneID, + edgeFlip, + inflateFaceID + ); + + + // Point-wise extrusion data + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Displacement for all pp.localPoints. Set to large value so does + // not trigger the minThickness truncation (see syncPatchDisplacement below) + vectorField patchDisp(pp().nPoints(), vector(GREAT, GREAT, GREAT)); + + // Number of layers for all pp.localPoints. Note: only valid if + // extrudeStatus = EXTRUDE. + labelList patchNLayers(pp().nPoints(), Zero); + + // Whether to add edge for all pp.localPoints. + List<extrudeMode> extrudeStatus(pp().nPoints(), EXTRUDE); + + // Ideal number of cells added + const label nIdealTotAddedCells = setPointNumLayers + ( + layerParams, + + numLayers, + patchIDs, + pp(), + edgeGlobalFaces, + + patchDisp, + patchNLayers, + extrudeStatus + ); + + // Determine (wanted) point-wise overall layer thickness and expansion + // ratio + scalarField thickness(pp().nPoints()); + scalarIOField minThickness + ( + IOobject + ( + "minThickness", + meshRefiner_.timeName(), + mesh, + IOobject::NO_READ + ), + pp().nPoints() + ); + scalarField expansionRatio(pp().nPoints()); + calculateLayerThickness + ( + *pp, + patchIDs, + layerParams, + meshRefiner_.meshCutter().cellLevel(), + patchNLayers, + edge0Len, + + thickness, + minThickness, + expansionRatio + ); + + + + // Per cell 0 or number of layers in the cell column it is part of + labelList cellNLayers; + // Per face actual overall layer thickness + scalarField faceRealThickness; + // Per face wanted overall layer thickness + scalarField faceWantedThickness(mesh.nFaces(), Zero); + { + UIndirectList<scalar>(faceWantedThickness, pp->addressing()) = + avgPointData(*pp, thickness); + } + + + // Current set of topology changes. (changing mesh clears out + // polyTopoChange) + polyTopoChange meshMod(mesh.boundaryMesh().size()); + + // Play changes into meshMod + addLayers + ( + layerParams, + layerParams.nLayerIter(), + + // Mesh quality + motionDict, + layerParams.nRelaxedIter(), + nAllowableErrors, + + patchIDs, + internalFaceZones, + baffles, + numLayers, + nIdealTotAddedCells, + + // Patch information + globalFaces, + pp(), + edgeGlobalFaces, + edgePatchID, + edgeZoneID, + edgeFlip, + inflateFaceID, + + // Per patch point the wanted thickness + thickness, + minThickness, + expansionRatio, + + // Per patch point the obtained thickness + patchDisp, + patchNLayers, + extrudeStatus, + + // Complete mesh changes + meshMod, + + // Stats + cellNLayers, + faceRealThickness + ); + + + // At this point we have a (shrunk) mesh and a set of topology changes + // which will make a valid mesh with layer. Apply these changes to the + // current mesh. + + { + // Apply the stored topo changes to the current mesh. + autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh, false); + mapPolyMesh& map = *mapPtr; + + // Hack to remove meshPhi - mapped incorrectly. TBD. + mesh.clearOut(); + + // Update fields + mesh.updateMesh(map); + + // Move mesh (since morphing does not do this) + if (map.hasMotionPoints()) + { + mesh.movePoints(map.preMotionPoints()); + } + else + { + // Delete mesh volumes. + mesh.clearOut(); + } + + // Reset the instance for if in overwrite mode + mesh.setInstance(meshRefiner_.timeName()); + + meshRefiner_.updateMesh(map, labelList(0)); + + // Update numbering of faceWantedThickness + meshRefinement::updateList + ( + map.faceMap(), + scalar(0), + faceWantedThickness + ); + + // Print data now that we still have patches for the zones + //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO) + printLayerData + ( + mesh, + patchIDs, + cellNLayers, + faceWantedThickness, + faceRealThickness + ); + + + // Dump for debugging + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing mesh with layers but disconnected to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } + + + // Map baffles, pointToMaster + mapFaceZonePoints(map, baffles, pointToMaster); + } + + + // Merge baffles + mergeFaceZonePoints + ( + pointToMaster, // -1 or index of duplicated point + cellNLayers, + faceRealThickness, + faceWantedThickness + ); + + + // Do final balancing + // ~~~~~~~~~~~~~~~~~~ + + if (Pstream::parRun()) + { + Info<< nl + << "Doing final balancing" << nl + << "---------------------" << nl + << endl; + + if (debug) + { + const_cast<Time&>(mesh.time())++; + } + + // Balance. No restriction on face zones and baffles. + autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance + ( + false, + false, + scalarField(mesh.nCells(), 1.0), + decomposer, + distributor + ); + + // Re-distribute flag of layer faces and cells + map().distributeCellData(cellNLayers); + map().distributeFaceData(faceWantedThickness); + map().distributeFaceData(faceRealThickness); + } + + + // Write mesh data + // ~~~~~~~~~~~~~~~ + + if (!dryRun_) + { + writeLayerData + ( + mesh, + patchIDs, + cellNLayers, + faceWantedThickness, + faceRealThickness + ); + } +} + + +// ************************************************************************* // diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C index b8ac1ac7f56..95d164eb82d 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C @@ -343,7 +343,8 @@ Foam::label Foam::snappyRefineDriver::smallFeatureRefine Foam::label Foam::snappyRefineDriver::surfaceOnlyRefine ( const refinementParameters& refineParams, - const label maxIter + const label maxIter, + const label leakBlockageIter ) { if (dryRun_) @@ -360,6 +361,7 @@ Foam::label Foam::snappyRefineDriver::surfaceOnlyRefine addProfiling(surface, "snappyHexMesh::refine::surface"); const fvMesh& mesh = meshRefiner_.mesh(); + const refinementSurfaces& surfaces = meshRefiner_.surfaces(); // Determine the maximum refinement level over all surfaces. This // determines the minimum number of surface refinement iterations. @@ -374,6 +376,52 @@ Foam::label Foam::snappyRefineDriver::surfaceOnlyRefine << endl; + // Do optional leak closing (by removing cells) + if (iter >= leakBlockageIter) + { + // Block off intersections with unzoned surfaces with specified + // leakLevel < iter + const labelList unnamedSurfaces + ( + surfaceZonesInfo::getUnnamedSurfaces + ( + surfaces.surfZones() + ) + ); + + DynamicList<label> selectedSurfaces(unnamedSurfaces.size()); + for (const label surfi : unnamedSurfaces) + { + const label regioni = surfaces.globalRegion(surfi, 0); + + // Take shortcut: assume all cells on surface are refined to + // its refinement level at iteration iter. So just use the + // iteration to see if the surface is a candidate. + if (iter > surfaces.leakLevel()[regioni]) + { + selectedSurfaces.append(surfi); + } + } + + if + ( + selectedSurfaces.size() + && refineParams.locationsOutsideMesh().size() + ) + { + meshRefiner_.blockLeakFaces + ( + globalToMasterPatch_, + globalToSlavePatch_, + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh(), + selectedSurfaces + ); + } + } + + // Determine cells to refine // ~~~~~~~~~~~~~~~~~~~~~~~~~ // Only look at surface intersections (minLevel and surface curvature), @@ -1762,15 +1810,19 @@ void Foam::snappyRefineDriver::removeInsideCells } // Remove any cells inside limitShells with level -1 - meshRefiner_.removeLimitShells - ( - nBufferLayers, - 1, - globalToMasterPatch_, - globalToSlavePatch_, - refineParams.locationsInMesh(), - refineParams.zonesInMesh() - ); + if (meshRefiner_.limitShells().shells().size()) + { + meshRefiner_.removeLimitShells + ( + nBufferLayers, + 1, + globalToMasterPatch_, + globalToSlavePatch_, + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh() + ); + } // Fix any additional (e.g. locationsOutsideMesh). Note: probably not // necessary. @@ -2783,8 +2835,7 @@ void Foam::snappyRefineDriver::baffleAndSplitMesh globalToMasterPatch_, globalToSlavePatch_, refineParams.locationsInMesh(), - refineParams.locationsOutsideMesh(), - setFormatter_ + refineParams.locationsOutsideMesh() ); } } @@ -2829,6 +2880,8 @@ void Foam::snappyRefineDriver::zonify refineParams.nErodeCellZone(), refineParams.locationsInMesh(), refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh(), + setFormatter_, zonesToFaceZone ); @@ -2916,8 +2969,7 @@ void Foam::snappyRefineDriver::splitAndMergeBaffles globalToMasterPatch_, globalToSlavePatch_, refineParams.locationsInMesh(), - refineParams.locationsOutsideMesh(), - setFormatter_ + refineParams.locationsOutsideMesh() ); if (debug) @@ -3332,7 +3384,8 @@ void Foam::snappyRefineDriver::doRefine surfaceOnlyRefine ( refineParams, - 20 // maxIter + 20, // maxIter + labelMax // no leak detection yet since all in same cell ); // Refine cells that contain a gap @@ -3348,7 +3401,8 @@ void Foam::snappyRefineDriver::doRefine surfaceOnlyRefine ( refineParams, - 100 // maxIter + 100, // maxIter + 0 // Iteration to start leak detection and closure ); // Pass1 of automatic gap-level refinement: surface-intersected cells @@ -3424,17 +3478,31 @@ void Foam::snappyRefineDriver::doRefine 100 // maxIter ); - //// Re-remove cells inbetween two surfaces. The shell refinement/ - //// directional shell refinement might have caused new small - //// gaps to be resolved. This is currently disabled since it finds - //// gaps just because it very occasionally walks around already removed - //// gaps and still finds 'opposite' surfaces. This probably need additional - //// path-length counting to avoid walking huge distances. - //surfaceProximityBlock - //( - // refineParams, - // 1 //100 // maxIter - //); + // Block gaps (always, ignore surface leakLevel) + if (refineParams.locationsOutsideMesh().size()) + { + // For now: only check leaks on meshed surfaces. The problem is that + // blockLeakFaces always generates baffles any not just faceZones ... + const labelList unnamedSurfaces + ( + surfaceZonesInfo::getUnnamedSurfaces + ( + meshRefiner_.surfaces().surfZones() + ) + ); + if (unnamedSurfaces.size()) + { + meshRefiner_.blockLeakFaces + ( + globalToMasterPatch_, + globalToSlavePatch_, + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh(), + unnamedSurfaces + ); + } + } if ( diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H index 30827dcf24c..dbfb3dda614 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.H @@ -125,7 +125,8 @@ class snappyRefineDriver label surfaceOnlyRefine ( const refinementParameters& refineParams, - const label maxIter + const label maxIter, + const label leakBlockageIter // when to start leak closing ); //- Refine all cells in small gaps diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.C b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.C index dac2dee2405..d80cc3432f2 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.C +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.C @@ -741,6 +741,75 @@ bool Foam::snappySnapDriver::outwardsDisplacement } +void Foam::snappySnapDriver::freezeExposedPoints +( + const meshRefinement& meshRefiner, + const word& fzName, // faceZone name + const word& pzName, // pointZone name + const indirectPrimitivePatch& outside, + vectorField& outsideDisp +) +{ + const fvMesh& mesh = meshRefiner.mesh(); + const pointZoneMesh& pointZones = mesh.pointZones(); + + bitSet isFrozenPoint(mesh.nPoints()); + + // Add frozen points + const label pointZonei = pointZones.findZoneID(pzName); + if (pointZonei != -1) + { + isFrozenPoint.set(pointZones[pointZonei]); + } + + // Add (inside) points of frozen faces + const faceZoneMesh& faceZones = mesh.faceZones(); + const label faceZonei = faceZones.findZoneID(fzName); + if (faceZonei != -1) + { + const uindirectPrimitivePatch pp + ( + UIndirectList<face>(mesh.faces(), faceZones[faceZonei]), + mesh.points() + ); + + // Count number of faces per edge + const labelList nEdgeFaces(meshRefiner.countEdgeFaces(pp)); + + // Freeze all internal points + forAll(nEdgeFaces, edgei) + { + if (nEdgeFaces[edgei] != 1) + { + const edge& e = pp.edges()[edgei]; + isFrozenPoint.set(pp.meshPoints()[e[0]]); + isFrozenPoint.set(pp.meshPoints()[e[1]]); + } + } + } + + syncTools::syncPointList + ( + mesh, + isFrozenPoint, + orEqOp<unsigned int>(), + 0u + ); + + if (returnReduce(isFrozenPoint.count(), sumOp<label>())) + { + for (const label pointi : isFrozenPoint) + { + const auto& iter = outside.meshPointMap().find(pointi); + if (iter.found()) + { + outsideDisp[iter()] = Zero; + } + } + } +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::snappySnapDriver::snappySnapDriver @@ -2799,6 +2868,16 @@ void Foam::snappySnapDriver::doSnap // Check for displacement being outwards. outwardsDisplacement(pp, disp); + // Freeze points on exposed points/faces + freezeExposedPoints + ( + meshRefiner_, + "frozenFaces", // faceZone name + "frozenPoints", // pointZone name + pp, + disp + ); + // Set initial distribution of displacement field (on patches) // from patchDisp and make displacement consistent with b.c. // on displacement pointVectorField. diff --git a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.H b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.H index 7d6a24ed140..5f6ed8fc1be 100644 --- a/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.H +++ b/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappySnapDriver.H @@ -6,6 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2015 OpenFOAM Foundation + Copyright (C) 2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -142,6 +143,16 @@ class snappySnapDriver const vectorField& ); + //- Freeze points on pointZone or (inside of) faceZone + static void freezeExposedPoints + ( + const meshRefinement& meshRefiner, + const word& fzName, // faceZone name + const word& pzName, // pointZone name + const indirectPrimitivePatch& outside, + vectorField& patchDisp + ); + //- Detect warpage void detectWarpedFaces ( diff --git a/src/meshTools/Make/files b/src/meshTools/Make/files index af715350b76..d63788f99a2 100644 --- a/src/meshTools/Make/files +++ b/src/meshTools/Make/files @@ -214,6 +214,7 @@ $(faceSources)/regionToFace/regionToFace.C $(faceSources)/searchableSurfaceToFace/searchableSurfaceToFace.C $(faceSources)/sphereToFace/sphereToFace.C $(faceSources)/zoneToFace/zoneToFace.C +$(faceSources)/holeToFace/holeToFace.C pointSources = topoSet/pointSources $(pointSources)/topoSetPointSource/topoSetPointSource.C diff --git a/src/meshTools/polyTopoChange/polyTopoChange.C b/src/meshTools/polyTopoChange/polyTopoChange.C index 38013b4c3ce..9b0803595b0 100644 --- a/src/meshTools/polyTopoChange/polyTopoChange.C +++ b/src/meshTools/polyTopoChange/polyTopoChange.C @@ -888,18 +888,7 @@ void Foam::polyTopoChange::reorderCompactFaces } -// Compact all and orders points and faces: -// - points into internal followed by external points -// - internalfaces upper-triangular -// - externalfaces after internal ones. -void Foam::polyTopoChange::compact -( - const bool orderCells, - const bool orderPoints, - label& nInternalPoints, - labelList& patchSizes, - labelList& patchStarts -) +void Foam::polyTopoChange::shrink() { points_.shrink(); pointMap_.shrink(); @@ -915,7 +904,23 @@ void Foam::polyTopoChange::compact cellMap_.shrink(); reverseCellMap_.shrink(); cellZone_.shrink(); +} + +// Compact all and orders points and faces: +// - points into internal followed by external points +// - internalfaces upper-triangular +// - externalfaces after internal ones. +void Foam::polyTopoChange::compact +( + const bool orderCells, + const bool orderPoints, + label& nInternalPoints, + labelList& patchSizes, + labelList& patchStarts +) +{ + shrink(); // Compact points label nActivePoints = 0; diff --git a/src/meshTools/polyTopoChange/polyTopoChange.H b/src/meshTools/polyTopoChange/polyTopoChange.H index cea7508ab45..98c74ebdfb4 100644 --- a/src/meshTools/polyTopoChange/polyTopoChange.H +++ b/src/meshTools/polyTopoChange/polyTopoChange.H @@ -511,6 +511,10 @@ public: const label nCells ); + //- Shrink storage (does not remove any elements; just compacts + //- dynamic lists + void shrink(); + //- Move all points. Incompatible with other topology changes. void movePoints(const pointField& newPoints); diff --git a/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.C b/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.C new file mode 100644 index 00000000000..e68ac1b83c3 --- /dev/null +++ b/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.C @@ -0,0 +1,1359 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020-2021 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + +#include "holeToFace.H" +#include "transform.H" +#include "faceSet.H" +#include "cellSet.H" +#include "addToRunTimeSelectionTable.H" +#include "OBJstream.H" +//#include "fvMesh.H" +//#include "volFields.H" +//#include "surfaceFields.H" +#include "topoDistanceData.H" +#include "FaceCellWave.H" +#include "syncTools.H" + +#include "edgeTopoDistanceData.H" +#include "PatchEdgeFaceWave.H" +#include "indirectPrimitivePatch.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(holeToFace, 0); + addToRunTimeSelectionTable(topoSetSource, holeToFace, word); + addToRunTimeSelectionTable(topoSetSource, holeToFace, istream); + addToRunTimeSelectionTable(topoSetFaceSource, holeToFace, word); + addToRunTimeSelectionTable(topoSetFaceSource, holeToFace, istream); + addNamedToRunTimeSelectionTable + ( + topoSetFaceSource, + holeToFace, + word, + hole + ); + addNamedToRunTimeSelectionTable + ( + topoSetFaceSource, + holeToFace, + istream, + hole + ); +} + + +Foam::topoSetSource::addToUsageTable Foam::holeToFace::usage_ +( + holeToFace::typeName, + "\n Usage: holeToFace <faceSet> ((x0 y0 z0) (x1 y1 z1))\n\n" + " Select faces disconnecting the individual regions" + " (each indicated by a locations).\n" +); + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +//Foam::label Foam::holeToFace::globalCount +//( +// const bitSet& isMasterFace, +// const bitSet& set +//) +//{ +// if (set.size() != isMasterFace.size()) +// { +// FatalErrorInFunction << "problem" << exit(FatalError); +// } +// +// label n = 0; +// for (const label facei : set) +// { +// if (isMasterFace(facei)) +// { +// n++; +// } +// } +// return returnReduce(n, sumOp<label>()); +//} + + +//void Foam::holeToFace::checkFaceSync +//( +// const string& setName, +// const bitSet& set +//) const +//{ +// if (set.size() != mesh_.nFaces()) +// { +// FatalErrorInFunction<< "problem" << exit(FatalError); +// } +// bitSet orSet(set); +// syncTools::syncFaceList(mesh_, orSet, orEqOp<unsigned int>()); +// bitSet andSet(set); +// syncTools::syncFaceList(mesh_, andSet, andEqOp<unsigned int>()); +// +// forAll(orSet, facei) +// { +// if (orSet[facei] != andSet[facei] || orSet[facei] != set[facei]) +// { +// FatalErrorInFunction<< "problem for set " << setName +// << "face:" << facei +// << " at:" << mesh_.faceCentres()[facei] +// << " patch:" << mesh_.boundaryMesh().whichPatch(facei) +// << " set:" << set[facei] +// << " orSet:" << orSet[facei] +// << " andSet:" << andSet[facei] +// << exit(FatalError); +// } +// } +//} + + +//void Foam::holeToFace::checkFaceSync +//( +// const string& fldName, +// const labelList& fld +//) const +//{ +// if (fld.size() != mesh_.nFaces()) +// { +// FatalErrorInFunction<< "problem" << exit(FatalError); +// } +// labelList maxFld(fld); +// syncTools::syncFaceList(mesh_, maxFld, maxEqOp<label>()); +// labelList minFld(fld); +// syncTools::syncFaceList(mesh_, minFld, minEqOp<label>()); +// +// forAll(maxFld, facei) +// { +// if (maxFld[facei] != minFld[facei] || maxFld[facei] != fld[facei]) +// { +// FatalErrorInFunction<< "problem for field " << fldName +// << "face:" << facei +// << " at:" << mesh_.faceCentres()[facei] +// << " patch:" << mesh_.boundaryMesh().whichPatch(facei) +// << " fld:" << fld[facei] +// << " maxFld:" << maxFld[facei] +// << " minFld:" << minFld[facei] +// << exit(FatalError); +// } +// } +//} + + +//void Foam::holeToFace::checkFaceSync +//( +// const string& fldName, +// const List<unsigned int>& fld +//) const +//{ +// if (fld.size() != mesh_.nFaces()) +// { +// FatalErrorInFunction<< "problem" << exit(FatalError); +// } +// List<unsigned int> orFld(fld); +// syncTools::syncFaceList(mesh_, orFld, bitOrEqOp<unsigned int>()); +// List<unsigned int> andFld(fld); +// syncTools::syncFaceList(mesh_, andFld, bitAndEqOp<unsigned int>()); +// forAll(orFld, facei) +// { +// if (orFld[facei] != andFld[facei] || orFld[facei] != fld[facei]) +// { +// FatalErrorInFunction<< "problem for field " << fldName +// << "face:" << facei +// << " at:" << mesh_.faceCentres()[facei] +// << " patch:" << mesh_.boundaryMesh().whichPatch(facei) +// << " fld:" << fld[facei] +// << " orFld:" << orFld[facei] +// << " andFld:" << andFld[facei] +// << exit(FatalError); +// } +// } +//} + + +//void Foam::holeToFace::writeCellField +//( +// const word& name, +// const labelList& labelFld +//) const +//{ +// Pout<< "Writing field " << name << endl; +// if (labelFld.size() != mesh_.nCells()) +// { +// FatalErrorInFunction << exit(FatalError); +// } +// +// const fvMesh& fvm = dynamic_cast<const fvMesh&>(mesh_); +// +// volScalarField fld +// ( +// IOobject +// ( +// name, +// fvm.time().timeName(), +// fvm, +// IOobject::NO_READ, +// IOobject::NO_WRITE, +// false +// ), +// fvm, +// dimensionedScalar("zero", dimless, scalar(0)) +// ); +// forAll(labelFld, i) +// { +// fld[i] = labelFld[i]; +// } +// fld.correctBoundaryConditions(); +// fld.write(); +//} + + +//void Foam::holeToFace::writeFaceField +//( +// const word& name, +// const labelList& labelFld +//) const +//{ +// Pout<< "Writing field " << name << endl; +// if (labelFld.size() != mesh_.nFaces()) +// { +// FatalErrorInFunction << exit(FatalError); +// } +// +// const fvMesh& fvm = dynamic_cast<const fvMesh&>(mesh_); +// +// surfaceScalarField fld +// ( +// IOobject +// ( +// name, +// fvm.time().timeName(), +// fvm, +// IOobject::NO_READ, +// IOobject::NO_WRITE, +// false +// ), +// fvm, +// dimensionedScalar("zero", dimless, scalar(0)) +// ); +// for (label i = 0; i < mesh_.nInternalFaces(); i++) +// { +// fld[i] = labelFld[i]; +// } +// surfaceScalarField::Boundary& bfld = fld.boundaryFieldRef(); +// forAll(bfld, patchi) +// { +// fvsPatchScalarField& pfld = bfld[patchi]; +// forAll(pfld, i) +// { +// pfld[i] = labelFld[pfld.patch().start()+i]; +// } +// } +// fld.write(); +// +// // Write as faceSet as well +// const bitSet isMasterFace(syncTools::getInternalOrMasterFaces(mesh_)); +// +// faceSet set(mesh_, name, 100); +// label nMasters = 0; +// forAll(labelFld, facei) +// { +// if (labelFld[facei] >= 0) +// { +// set.insert(facei); +// if (isMasterFace(facei)) +// { +// nMasters++; +// } +// } +// } +// Pout<< "Writing " << returnReduce(nMasters, sumOp<label>()) +// << " >= 0 faces to faceSet " << set.name() << endl; +// set.write(); +//} + + +void Foam::holeToFace::writeFaces +( + const word& name, + const bitSet& faceFld +) const +{ + mkDir(mesh_.time().timePath()); + OBJstream str(mesh_.time().timePath()/name); + Pout<< "Writing " << faceFld.count() << " faces to " << str.name() << endl; + + for (const label facei : faceFld) + { + str.write(mesh_.faces()[facei], mesh_.points(), false); + } +} + + +void Foam::holeToFace::calculateDistance +( + const labelList& seedFaces, + const bitSet& isBlockedCell, + const bitSet& isBlockedFace, + labelList& cellDist, + labelList& faceDist +) const +{ + if (isBlockedCell.size() != mesh_.nCells()) + { + FatalErrorInFunction << "Problem" << exit(FatalError); + } + if (isBlockedFace.size() != mesh_.nFaces()) + { + FatalErrorInFunction << "Problem" << exit(FatalError); + } + + //const bitSet isMasterFace(syncTools::getInternalOrMasterFaces(mesh_)); + + // Field on cells and faces. + List<topoDistanceData<label>> cellData(mesh_.nCells()); + List<topoDistanceData<label>> faceData(mesh_.nFaces()); + + // Start of changes + List<topoDistanceData<label>> seedData + ( + seedFaces.size(), + topoDistanceData<label>(0, 123) + ); + //Pout<< "Seeded " + // << globalCount(isMasterFace, bitSet(mesh_.nFaces(), seedFaces)) + // << " out of " << returnReduce(mesh_.nFaces(), sumOp<label>()) << endl; + + // Make sure we don't walk through inactive cells + //Pout<< "blocking " + // << returnReduce(isBlockedCell.count(), sumOp<label>()) + // << " cells" << endl; + for (const label celli : isBlockedCell) + { + cellData[celli] = topoDistanceData<label>(0, 0); + } + //Pout<< "blocking " + // << globalCount(isMasterFace, isBlockedFace) << " faces" << endl; + for (const label facei : isBlockedFace) + { + faceData[facei] = topoDistanceData<label>(0, 0); + } + + // Propagate information inwards + FaceCellWave<topoDistanceData<label>> deltaCalc + ( + mesh_, + seedFaces, + seedData, + faceData, + cellData, + mesh_.globalData().nTotalCells()+1 + ); + + // And extract + //bool haveWarned = false; + forAll(cellData, celli) + { + if (!isBlockedCell[celli]) + { + if (!cellData[celli].valid(deltaCalc.data())) + { + //if (!haveWarned) + //{ + // WarningInFunction + // << "Did not visit some cells, e.g. cell " << celli + // << " at " << mesh_.cellCentres()[celli] << endl; + // haveWarned = true; + //} + } + else + { + cellDist[celli] = cellData[celli].distance(); + } + } + } + + forAll(faceDist, facei) + { + if (!isBlockedFace[facei]) + { + if (!faceData[facei].valid(deltaCalc.data())) + { + //if (!haveWarned) + //{ + // WarningInFunction + // << "Did not visit some faces, e.g. face " << facei + // << " at " << mesh_.faceCentres()[facei] << endl; + // haveWarned = true; + //} + } + else + { + faceDist[facei] = faceData[facei].distance(); + } + } + } +} + + +Foam::bitSet Foam::holeToFace::frontFaces +( + const bitSet& isSurfaceFace, + const List<unsigned int>& locationFaces, + const bitSet& isHoleCell +) const +{ + const labelList& faceOwner = mesh_.faceOwner(); + const labelList& faceNeighbour = mesh_.faceNeighbour(); + const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); + + bitSet isFrontFace(mesh_.nFaces()); + for (label facei = 0; facei < mesh_.nInternalFaces(); facei++) + { + if (!isSurfaceFace[facei]) + { + const label ownHole = isHoleCell[faceOwner[facei]]; + const label neiHole = isHoleCell[faceNeighbour[facei]]; + + if (ownHole != neiHole) + { + unsigned int masks = locationFaces[facei]; + if (masks == 0u) + { + FatalErrorInFunction << "face:" << facei + << " at:" << mesh_.faceCentres()[facei] + << " not any front" << exit(FatalError); + } + + // Count number of bits set + const label nSet = BitOps::bit_count(masks); + + if (nSet == 1) + { + isFrontFace.set(facei); + } + } + } + } + + // Get neighbouring cell data + bitSet isHoleNeiCell(mesh_.nBoundaryFaces()); + { + for (const polyPatch& pp : pbm) + { + label bFacei = pp.start()-mesh_.nInternalFaces(); + const labelUList& faceCells = pp.faceCells(); + + for (const label celli : faceCells) + { + isHoleNeiCell[bFacei] = isHoleCell[celli]; + ++bFacei; + } + } + syncTools::swapBoundaryFaceList(mesh_, isHoleNeiCell); + } + + + for (label facei = mesh_.nInternalFaces(); facei < mesh_.nFaces(); facei++) + { + if (!isSurfaceFace[facei]) + { + const label ownHole = isHoleCell[faceOwner[facei]]; + const label neiHole = isHoleNeiCell[facei-mesh_.nInternalFaces()]; + + if (ownHole != neiHole) + { + unsigned int masks = locationFaces[facei]; + if (masks == 0u) + { + FatalErrorInFunction << "face:" << facei + << " at:" << mesh_.faceCentres()[facei] + << " not any front" << exit(FatalError); + } + + // Count number of bits set + const label nSet = BitOps::bit_count(masks); + + if (nSet == 1) + { + isFrontFace.set(facei); + } + } + } + } + return isFrontFace; +} + + +Foam::bitSet Foam::holeToFace::findClosure +( + const bitSet& isSurfaceFace, // intersected faces + const bitSet& isCandidateHoleCell, // starting blockage + const labelListList& locationCells // cells per zone +) const +{ + const labelList& faceOwner = mesh_.faceOwner(); + const labelList& faceNeighbour = mesh_.faceNeighbour(); + + if (zonePoints_.size() < 2) + { + FatalErrorInFunction << "single region only : " + << flatOutput(zonePoints_) << exit(FatalError); + } + + if (zonePoints_.size() > 31) + { + FatalErrorInFunction << "only support < 32 locations in mesh." + << " Currently : " << flatOutput(zonePoints_) << exit(FatalError); + } + + + //const bitSet isMasterFace(syncTools::getInternalOrMasterFaces(mesh_)); + + bitSet isHoleCell(isCandidateHoleCell); + for (const labelList& zoneCells : locationCells) + { + for (const label celli : zoneCells) + { + if (celli != -1) + { + isHoleCell.unset(celli); + } + } + } + + bitSet notHoleCell(isHoleCell); + notHoleCell.flip(); + + if (debug) + { + Pout<< "holeToFace::findClosure :" + << " locationCells:" << flatOutput(locationCells) << nl + << "holeToFace::findClosure :" + << " initial blocked faces:" << isSurfaceFace.count() + << " candidate closure cells:" << isHoleCell.count() + << endl; + } + + // Distance to surface for every cell/face inside isHoleCell + labelList surfaceCellDist(mesh_.nCells(), -1); + labelList surfaceNeiCellDist(mesh_.nBoundaryFaces(), -1); + labelList surfaceFaceDist(mesh_.nFaces(), -1); + { + calculateDistance + ( + isSurfaceFace.toc(), // seed faces + notHoleCell, // no need to walk through non-hole cells + bitSet(mesh_.nFaces()), // no blocked faces + surfaceCellDist, + surfaceFaceDist + ); + syncTools::swapBoundaryCellList + ( + mesh_, + surfaceCellDist, + surfaceNeiCellDist + ); + //writeCellField("surfaceCellDistance0", surfaceCellDist); + //writeFaceField("surfaceFaceDistance0", surfaceFaceDist); + //checkFaceSync("surfaceFaceDistance0", surfaceFaceDist); + } + + if (debug) + { + Pout<< "holeToFace::findClosure :" + << " calculated topological distance to initial blocked faces." + << " max distance:" << gMax(surfaceCellDist) + << endl; + } + + + // Find faces reachable from locationCells. If the locationCell is inside + // the blockage it will be only the faces of the cell. If it is outside + // it does a full walk to find the reachable faces on the outside of + // the blockage. + // Note: actual distance itself does not matter - only if they have been + // visited. + List<unsigned int> locationFaces(mesh_.nFaces(), 0u); + forAll(locationCells, zonei) + { + labelList cellDist(mesh_.nCells(), -1); + labelList faceDist(mesh_.nFaces(), -1); + + labelList seedFaces; + + const labelList& zoneCells = locationCells[zonei]; + for (const label celli : zoneCells) + { + if (celli != -1) + { + cellDist[celli] = 0; + const cell& cFaces = mesh_.cells()[celli]; + seedFaces.append(cFaces); + UIndirectList<label>(faceDist, cFaces) = 0; + } + } + + // Extra par sync. Is this needed? + { + bitSet isSeedFace(mesh_.nFaces(), seedFaces); + syncTools::syncFaceList + ( + mesh_, + isSeedFace, + orEqOp<unsigned int>() + ); + seedFaces = isSeedFace.toc(); + } + + + calculateDistance + ( + seedFaces, // seed faces + isHoleCell, // do not walk through blocking cells + isSurfaceFace, // do not walk through surface + cellDist, + faceDist + ); + //writeCellField("cellDistance" + Foam::name(zonei), cellDist); + //writeFaceField("faceDistance" + Foam::name(zonei), faceDist); + //checkFaceSync("faceDistance", faceDist); + + // Add all reached faces + const unsigned int mask = (1u << zonei); + forAll(faceDist, facei) + { + if (faceDist[facei] >= 0) + { + locationFaces[facei] |= mask; + } + } + } + + if (debug) + { + writeFaces("isSurfaceFace.obj", isSurfaceFace); + } + //if (debug) + //{ + // bitSet isMultiBitFace(mesh_.nFaces()); + // forAll(locationFaces, facei) + // { + // const unsigned int bits = locationFaces[facei]; + // const label nSet = BitOps::bit_count(bits); + // if (nSet > 1) + // { + // isMultiBitFace.set(facei); + // } + // } + // writeFaces("isMultiBitFace.obj", isMultiBitFace); + //} + + + //// Check that there are some faces that connect to more than one zone + //- NOT CORRECT!!! At this point the walking has only done the distance + // outside the initial set of blocked faces. We'd have to + // walk through all faces before we can determine. + //bool haveLeak = false; + //forAll(locationFaces, facei) + //{ + // if (!isSurfaceFace[facei]) + // { + // const unsigned int bits = locationFaces[facei]; + // const label nSet = BitOps::bit_count(bits); + // if (nSet > 1) + // { + // haveLeak = true; + // + // if (debug) + // { + // // Collect points + // DynamicList<pointField> connected; + // forAll(zonePoints_, zonei) + // { + // if (bits & (1u << zonei)) + // { + // connected.append(zonePoints_[zonei]); + // } + // } + // Pout<< "holeToFace::findClosure :" + // << " found initial leak at face " + // << mesh_.faceCentres()[facei] + // << " between zones " << flatOutput(connected) + // << endl; + // } + // break; + // } + // } + //} + //reduce(haveLeak, orOp<bool>()); + // + //if (!haveLeak) + //{ + // if (debug) + // { + // Pout<< "holeToFace::findClosure :" + // << " did not find leak between zones " + // << flatOutput(zonePoints_) << endl; + // } + // return bitSet(mesh_.nFaces()); + //} + + + // Front (on outside of hole cell but not connecting multiple locations + bitSet isFrontFace(frontFaces(isSurfaceFace, locationFaces, isHoleCell)); + + + // Start off eroding the cells furthest away from the surface + label surfaceDist = gMax(surfaceCellDist); + + // Work storage + //List<unsigned int> newLocationFaces(mesh_.nFaces()); + //bitSet newHoleCell(mesh_.nCells()); + + while (surfaceDist >= 0) + { + // Erode cells with >= surfaceDist: + // - unmark cell as blockage (isHoleCell) + // - mark faces of cell as visible from inside/outside + + //newLocationFaces = locationFaces; + //newHoleCell = isHoleCell; + + label nChanged = 0; + for (const label facei : isFrontFace) + { + const label own = faceOwner[facei]; + if (isHoleCell[own]) + { + const label ownDist = surfaceCellDist[own]; + if (ownDist >= surfaceDist) + { + //newHoleCell.unset(own); + isHoleCell.unset(own); + nChanged++; + + const cell& cFaces = mesh_.cells()[own]; + // Set corresponding bits on faces + const unsigned int mask = locationFaces[facei]; + for (const label fi : cFaces) + { + //newLocationFaces[fi] |= mask; + locationFaces[fi] |= mask; + } + } + } + else if (mesh_.isInternalFace(facei)) + { + const label nei = faceNeighbour[facei]; + const label neiDist = surfaceCellDist[nei]; + + if (isHoleCell[nei] && neiDist >= surfaceDist) + { + //newHoleCell[nei] = false; + isHoleCell[nei] = false; + nChanged++; + + const cell& cFaces = mesh_.cells()[nei]; + // Set corresponding bits on faces + const unsigned int mask = locationFaces[facei]; + for (const label fi : cFaces) + { + //newLocationFaces[fi] |= mask; + locationFaces[fi] |= mask; + } + } + } + } + + reduce(nChanged, sumOp<label>()); + + + if (debug) + { + Pout<< "holeToFace::findClosure :" + << " surfaceDist:" << surfaceDist + << " front:" << isFrontFace.count() + << " nChangedCells:" << nChanged + << endl; + } + + + if (nChanged == 0) + { + // Nothing eroded at this level. Erode cells nearer to surface. + --surfaceDist; + } + else + { + //locationFaces = newLocationFaces; + //isHoleCell = newHoleCell; + + // Sync locationFaces + syncTools::syncFaceList + ( + mesh_, + locationFaces, + bitOrEqOp<unsigned int>() + ); + + // Calculate new front. Never include faces that are both visible + // from outside and inside + isFrontFace = frontFaces(isSurfaceFace, locationFaces, isHoleCell); + } + } + + + // Debug: dump all the end fronts + //{ + // forAll(locationCells, zonei) + // { + // const unsigned int mask = (1u << zonei); + // + // bitSet isZoneFace(mesh_.nFaces()); + // forAll(locationFaces, facei) + // { + // if (locationFaces[facei] & mask) + // { + // isZoneFace.set(facei); + // } + // } + // writeFaces("isZoneFace"+Foam::name(zonei)+".obj", isZoneFace); + // } + //} + + + + // Find faces that are connected to more than one location + bitSet isCommonFace(mesh_.nFaces()); + forAll(locationFaces, facei) + { + unsigned int masks = locationFaces[facei]; + if (masks != 0u) + { + // Count number of bits set + const label nSet = BitOps::bit_count(masks); + if (nSet >= 2) + { + isCommonFace.set(facei); + } + } + } + + // Remove faces that are on the surface + for (const label facei : isCommonFace) + { + if (surfaceFaceDist[facei] == 0) + { + isCommonFace.unset(facei); + } + } + + if (debug) + { + Pout<< "holeToFace::findClosure :" + << " closure faces:" << isCommonFace.count() << endl; + } + + return isCommonFace; +} + + +Foam::bitSet Foam::holeToFace::erodeSet +( + const bitSet& isSurfaceFace, + const bitSet& isSetFace +) const +{ + // Detect cells with lots of faces in the set. WIP. Not parallel consistent. + + const labelList& faceOwner = mesh_.faceOwner(); + const labelList& faceNeighbour = mesh_.faceNeighbour(); + + bitSet isSetCell(mesh_.nCells()); + for (const label facei : isSetFace) + { + isSetCell.set(faceOwner[facei]); + if (mesh_.isInternalFace(facei)) + { + isSetCell.set(faceNeighbour[facei]); + } + } + + // Count number of faces per cell. Decide if surface would improve by + // moving set + bitSet erodedSet(isSetFace); + for (const label celli : isSetCell) + { + const cell& cFaces = mesh_.cells()[celli]; + + label nBlockedFaces = 0; + label nSurfaceFaces = 0; + for (const label facei : cFaces) + { + if (erodedSet[facei]) + { + nBlockedFaces++; + } + else if (isSurfaceFace[facei]) + { + nSurfaceFaces++; + } + } + + if ((nSurfaceFaces + nBlockedFaces) == cFaces.size()) + { + // Single cell already disconnected by surface intersections + for (const label facei : cFaces) + { + erodedSet.unset(facei); + } + } + } + syncTools::syncFaceList + ( + mesh_, + erodedSet, + andEqOp<unsigned int>() + ); + //checkFaceSync("erodedSet", erodedSet); + + for (const label celli : isSetCell) + { + const cell& cFaces = mesh_.cells()[celli]; + + label nBlockedFaces = 0; + for (const label facei : cFaces) + { + if (erodedSet[facei]) + { + nBlockedFaces++; + } + } + if (nBlockedFaces >= cFaces.size()-2) + { + for (const label facei : cFaces) + { + erodedSet.flip(facei); + } + } + } + syncTools::syncFaceList + ( + mesh_, + erodedSet, + andEqOp<unsigned int>() + ); + + if (debug) + { + Pout<< "holeToFace::erodeSet :" + << " starting set:" << isSetFace.count() + << " eroded set:" << erodedSet.count() << endl; + } + + //checkFaceSync("erodedSet", erodedSet); + return erodedSet; +} + + +void Foam::holeToFace::combine +( + topoSet& set, + const bitSet& isSurfaceFace, + const bitSet& isHoleCell, + const bool add +) const +{ + labelListList locationCells(zonePoints_.size()); + forAll(zonePoints_, zonei) + { + const pointField& zoneLocations = zonePoints_[zonei]; + labelList& zoneCells = locationCells[zonei]; + zoneCells.setSize(zoneLocations.size()); + forAll(zoneLocations, i) + { + const label celli = mesh_.findCell(zoneLocations[i]); + zoneCells[i] = celli; + + // Check that cell has at least one unblocked face so front can + // 'escape'. + if (celli != -1) + { + const cell& cFaces = mesh_.cells()[celli]; + bool hasUnblocked = false; + for (const label facei : cFaces) + { + if (!isSurfaceFace[facei]) + { + hasUnblocked = true; + break; + } + } + + if (!hasUnblocked) + { + FatalErrorInFunction + << "problem : location:" << zoneLocations[i] + << " in zone:" << zonei + << " is found in cell at:" << celli + << mesh_.cellCentres()[celli] + << " which is completely surrounded by blocked faces" + << exit(FatalError); + } + } + } + } + + bitSet isClosingFace + ( + findClosure + ( + isSurfaceFace, // intersected faces + isHoleCell, // starting blockage + locationCells // cells for zonePoints_ + ) + ); + + if (erode_) + { + isClosingFace = erodeSet(isSurfaceFace, isClosingFace); + } + + if (debug) + { + writeFaces("isClosingFace.obj", isClosingFace); + //checkFaceSync("isClosingFace", isCommonFace); + } + + for (const label facei : isClosingFace) + { + addOrDelete(set, facei, add); + } +} + + +Foam::List<Foam::pointField> Foam::holeToFace::expand(const pointField& pts) +{ + List<pointField> allPts(pts.size()); + forAll(pts, i) + { + pointField& onePt = allPts[i]; + onePt.setSize(1, pts[i]); + } + return allPts; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::holeToFace::holeToFace +( + const polyMesh& mesh, + const List<pointField>& zonePoints, + const wordList& blockedFaceNames, + const wordList& blockedCellNames, + const bool erode +) +: + topoSetFaceSource(mesh), + zonePoints_(zonePoints), + blockedFaceNames_(blockedFaceNames), + blockedCellNames_(blockedCellNames), + erode_(erode) +{} + + +Foam::holeToFace::holeToFace +( + const polyMesh& mesh, + const dictionary& dict +) +: + topoSetFaceSource(mesh), + zonePoints_(dict.get<List<pointField>>("points")), + blockedFaceNames_(), + blockedCellNames_(), + erode_(dict.getOrDefault<bool>("erode", false)) +{ + // Look for 'sets' or 'set' + if (!dict.readIfPresent("faceSets", blockedFaceNames_)) + { + blockedFaceNames_.resize(1); + if (!dict.readEntry("faceSet", blockedFaceNames_.first())) + { + blockedFaceNames_.clear(); + } + } + if (!dict.readIfPresent("cellSets", blockedCellNames_)) + { + blockedCellNames_.resize(1); + if (!dict.readEntry("cellSet", blockedCellNames_.first())) + { + blockedCellNames_.clear(); + } + } +} + + +Foam::holeToFace::holeToFace +( + const polyMesh& mesh, + Istream& is +) +: + topoSetFaceSource(mesh), + zonePoints_(expand(pointField(is))), + blockedFaceNames_(one(), word(checkIs(is))), + blockedCellNames_(), + erode_(false) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::holeToFace::applyToSet +( + const topoSetSource::setAction action, + topoSet& set +) const +{ + // Set of additional blocked (internal or coupled) faces + bitSet isBlockedFace(mesh_.nFaces()); + for (const word& setName : blockedFaceNames_) + { + const faceSet loadedSet(mesh_, setName); + isBlockedFace.set(loadedSet.toc()); + } + + // Optional initial blocked cells + bitSet isCandidateCell(mesh_.nCells()); + if (blockedFaceNames_.size()) + { + for (const word& setName : blockedCellNames_) + { + const cellSet loadedSet(mesh_, setName); + isCandidateCell.set(loadedSet.toc()); + } + } + else + { + isCandidateCell = true; + } + + if (action == topoSetSource::ADD || action == topoSetSource::NEW) + { + if (verbose_) + { + Info<< " Adding all faces to disconnect regions " + << flatOutput(zonePoints_) << " ..." << endl; + } + + combine(set, isBlockedFace, isCandidateCell, true); + } + else if (action == topoSetSource::SUBTRACT) + { + if (verbose_) + { + Info<< " Removing all faces to disconnect regions " + << flatOutput(zonePoints_) << " ..." << endl; + } + + combine(set, isBlockedFace, isCandidateCell, false); + } +} + + +Foam::autoPtr<Foam::mapDistribute> Foam::holeToFace::calcClosure +( + const polyMesh& mesh, + const List<pointField>& zonePoints, + const labelList& blockedFaces, + const globalIndex& globalBlockedFaces, + const bool erode, + + labelList& closureFaces, // local faces to close gap + labelList& closureToBlocked +) +{ + if (blockedFaces.size() != globalBlockedFaces.localSize()) + { + FatalErrorInFunction << "problem : blockedFaces:" << blockedFaces.size() + << " globalBlockedFaces:" << globalBlockedFaces.localSize() + << exit(FatalError); + } + + + // Calculate faces needed to close hole (closureFaces) + { + const holeToFace faceSetSource + ( + mesh, + zonePoints, + wordList(0), + wordList(0), + erode //false + ); + faceSet closureFaceSet(mesh, "calcClosure", 256); + + const bitSet isBlockedFace(mesh.nFaces(), blockedFaces); + const bitSet isActiveCell(mesh.nCells(), true); + + faceSetSource.combine + ( + closureFaceSet, + isBlockedFace, + isActiveCell, + true + ); + + closureFaces = closureFaceSet.sortedToc(); + } + + + if (returnReduce(closureFaces.size(), sumOp<label>()) == 0) + { + closureToBlocked.clear(); + return autoPtr<mapDistribute>(nullptr); + } + + + //- Seed edges of closureFaces patch with (global) index of blockedFace + + const indirectPrimitivePatch pp + ( + IndirectList<face>(mesh.faces(), closureFaces), + mesh.points() + ); + const edgeList& edges = pp.edges(); + const labelList& mp = pp.meshPoints(); + const label nBndEdges = pp.nEdges() - pp.nInternalEdges(); + + // For all faces in blockedFaces mark the edge with a face. No special + // handling for multiple faces sharing the edge - first one wins + EdgeMap<label> edgeMap(pp.nEdges()); + forAll(blockedFaces, i) + { + const label globalBlockedi = globalBlockedFaces.toGlobal(i); + const label facei = blockedFaces[i]; + const face& f = mesh.faces()[facei]; + forAll(f, fp) + { + label nextFp = f.fcIndex(fp); + edgeMap.insert(edge(f[fp], f[nextFp]), globalBlockedi); + } + } + syncTools::syncEdgeMap(mesh, edgeMap, maxEqOp<label>()); + + + + // Seed + DynamicList<label> initialEdges(2*nBndEdges); + DynamicList<edgeTopoDistanceData<label, indirectPrimitivePatch>> + initialEdgesInfo(2*nBndEdges); + forAll(edges, edgei) + { + const edge& e = edges[edgei]; + const edge meshE = edge(mp[e[0]], mp[e[1]]); + + auto iter = edgeMap.cfind(meshE); + if (iter.found()) + { + // Found edge on patch connected to blocked face. Seed with the + // (global) index of that blocked face + + initialEdges.append(edgei); + initialEdgesInfo.append + ( + edgeTopoDistanceData<label, indirectPrimitivePatch> + ( + 0, // distance + iter() // globalBlockedi + ) + ); + } + } + + // Data on all edges and faces + List<edgeTopoDistanceData<label, indirectPrimitivePatch>> allEdgeInfo + ( + pp.nEdges() + ); + List<edgeTopoDistanceData<label, indirectPrimitivePatch>> allFaceInfo + ( + pp.size() + ); + + // Walk + PatchEdgeFaceWave + < + indirectPrimitivePatch, + edgeTopoDistanceData<label, indirectPrimitivePatch> + > calc + ( + mesh, + pp, + initialEdges, + initialEdgesInfo, + allEdgeInfo, + allFaceInfo, + returnReduce(pp.nEdges(), sumOp<label>())+1 + ); + + + // Per closure face the seed face + closureToBlocked.setSize(pp.size()); + closureToBlocked = -1; + forAll(allFaceInfo, facei) + { + if (allFaceInfo[facei].valid(calc.data())) + { + closureToBlocked[facei] = allFaceInfo[facei].data(); + } + } + // Above wave only guarantees unique data on coupled edges, not on + // coupled faces (?) so explicitly sync faces + { + labelList syncFld(mesh.nFaces(), -1); + UIndirectList<label>(syncFld, pp.addressing()) = closureToBlocked; + syncTools::syncFaceList(mesh, syncFld, maxEqOp<label>()); + closureToBlocked = UIndirectList<label>(syncFld, pp.addressing()); + } + + List<Map<label>> compactMap; + return autoPtr<mapDistribute>::New + ( + globalBlockedFaces, + closureToBlocked, + compactMap + ); +} + + +// ************************************************************************* // diff --git a/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.H b/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.H new file mode 100644 index 00000000000..98ef41b60fa --- /dev/null +++ b/src/meshTools/topoSet/faceSources/holeToFace/holeToFace.H @@ -0,0 +1,220 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Class + Foam::holeToFace + +Description + A topoSetFaceSource to select a set of faces that closes a hole i.e. + disconnects zones (specified by locations) from one another. + + Algorithm roughly according to + "A 3D-Hole Closing Algorithm", Zouina Aktouf et al + + \heading Dictionary parameters + \table + Property | Description | Required | Default + points | Per zone the list of points | yes | + faceSet | Optional blocked faces | no | <empty> + cellSet | Optional subset of cells to operate in | no | <empty> + erode | Perform some cleanup on set | no | no + \endtable + + Limited to max 31 zones. + +SourceFiles + holeToFace.C + +\*---------------------------------------------------------------------------*/ + +#ifndef holeToFace_H +#define holeToFace_H + +#include "topoSetFaceSource.H" +#include "bitSet.H" +#include "mapDistribute.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class holeToFace Declaration +\*---------------------------------------------------------------------------*/ + +class holeToFace +: + public topoSetFaceSource +{ + // Private data + + //- Add usage string + static addToUsageTable usage_; + + //- Per 'zone' the set of points + List<pointField> zonePoints_; + + //- Names of faceSets specifying blockage + wordList blockedFaceNames_; + + //- Names of cellSets specifying blockage + wordList blockedCellNames_; + + //- Erode set + bool erode_; + + + // Private Member Functions + + // Helper functions + + //- Expand pointField into list of pointFields + static List<pointField> expand(const pointField& pts); + + //- Write .obj file with selected faces + void writeFaces + ( + const word& name, + const bitSet& faceFld + ) const; + + + //- Calculate topological distance from seedFaces. Do not cross + // blocked cells/faces + void calculateDistance + ( + const labelList& seedFaces, + const bitSet& isBlockedCell, + const bitSet& isBlockedFace, + labelList& cellDist, + labelList& faceDist + ) const; + + //- Calculate faces on outside of hole cells. Ignore blocked faces + // (isSurfaceFace) and faces reachable from more than one location + bitSet frontFaces + ( + const bitSet& isSurfaceFace, + const List<unsigned int>& locationFaces, + const bitSet& isHoleCell + ) const; + + //- Overall driver to find minimum set of faces to close the path + // between zones + bitSet findClosure + ( + const bitSet& isSurfaceFace, // intersected faces + const bitSet& isCandidateHoleCell, // starting blockage + const labelListList& locationCells // cells per zone + ) const; + + //- WIP. Remove excess faces. Not parallel consistent. + bitSet erodeSet + ( + const bitSet& isSurfaceFace, + const bitSet& isSetFace + ) const; + + +public: + + //- Runtime type information + TypeName("holeToFace"); + + // Constructors + + //- Construct from components + holeToFace + ( + const polyMesh& mesh, + const List<pointField>& zonePoints, + const wordList& blockedFaceNames, + const wordList& blockedCellNames, + const bool erode + ); + + //- Construct from dictionary + holeToFace(const polyMesh& mesh, const dictionary& dict); + + //- Construct from Istream + holeToFace(const polyMesh& mesh, Istream& is); + + + //- Destructor + virtual ~holeToFace() = default; + + + // Member Functions + + virtual void applyToSet + ( + const topoSetSource::setAction action, + topoSet& + ) const; + + //- Optional direct use to generate a faceSet + void combine + ( + topoSet& set, + const bitSet& isBlockedFace, + const bitSet& isActiveCell, + const bool add + ) const; + + //- Optional direct use to generate the set of faces and the method to + // get data from nearby blocked faces. Gets provided with the + // - set of points per 'zone' + // - set of blocked faces + // - global numbering for these blocked faces + // Returns + // - the set of faces to disconnect zones from one + // another (closureFaces) + // - a nullptr or the maps from closureFaces to nearest blocked face + // (mapDistribute and closureToBlocked). Note: closureToBlocked + // (index into the map) can contain -1 if no nearby valid blocked + // face could be found (from an edge-face-edge walk) + static autoPtr<mapDistribute> calcClosure + ( + const polyMesh& mesh, + const List<pointField>& zonePoints, + const labelList& blockedFaces, + const globalIndex& globalBlockedFaces, + const bool erode, + labelList& closureFaces, + labelList& closureToBlocked + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/Allrun.pre b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/Allrun.pre new file mode 100755 index 00000000000..33c0095d655 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/Allrun.pre @@ -0,0 +1,25 @@ +#!/bin/sh +cd "${0%/*}" || exit # Run from this directory +. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions +#------------------------------------------------------------------------------ + +touch case.foam + +runApplication blockMesh + +# Generate f0Zone faceZone +runApplication topoSet + +runApplication extrudeMesh + +runApplication checkMesh + +#- Extrude some internal faces into owner + +runApplication -s flip topoSet -dict system/topoSetDict.flip + +runApplication -s flip extrudeMesh -dict system/extrudeMeshDict.flip + +runApplication -s flip checkMesh + +#------------------------------------------------------------------------------ diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/README b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/README new file mode 100644 index 00000000000..f7b8c883296 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/README @@ -0,0 +1,9 @@ +Unit testcase demonstrating extruding from internal faces +(using a faceZone instead of a faceSet) + +- pass 1 : extrude on bottom left cell both internal faces and a boundary face. + These faces will all be extruded away from cell 0 (i.e. no flip in + the faceZone) + +- pass 2 : extrude on top right cell both internal and boundary faces. + The internal faces will all have flip in the faceZone) diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/constant/transportProperties b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/constant/transportProperties new file mode 100644 index 00000000000..e0356a427a1 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/constant/transportProperties @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object transportProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +nu 0.01; + + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/blockMeshDict b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/blockMeshDict new file mode 100644 index 00000000000..36f1425873d --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/blockMeshDict @@ -0,0 +1,71 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object blockMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +scale 1; + +vertices +( + (0 0 0) + (2 0 0) + (2 2 0) + (0 2 0) + (0 0 1) + (2 0 1) + (2 2 1) + (0 2 1) +); + +blocks +( + hex (0 1 2 3 4 5 6 7) (2 2 1) simpleGrading (1 1 1) +); + +edges +( +); + +boundary +( + movingWall + { + type wall; + faces + ( + (3 7 6 2) + ); + } + fixedWalls + { + type wall; + faces + ( + (0 4 7 3) + (2 6 5 1) + (1 5 4 0) + ); + } + frontAndBack + { + type wall; + faces + ( + (0 3 2 1) + (4 5 6 7) + ); + } +); + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/controlDict b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/controlDict new file mode 100644 index 00000000000..d8b0b77182e --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/controlDict @@ -0,0 +1,49 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application icoFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 0.5; + +deltaT 0.005; + +writeControl timeStep; + +writeInterval 20; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable true; + + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/decomposeParDict b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/decomposeParDict new file mode 100644 index 00000000000..e2cd8b7b451 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/decomposeParDict @@ -0,0 +1,27 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 9; + +method hierarchical; + +coeffs +{ + n (3 3 1); +} + + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict new file mode 100644 index 00000000000..d928cc57886 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict @@ -0,0 +1,30 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object extrudeMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +constructFrom mesh; +sourceCase "<case>"; + +//sourcePatches (); +sourceFaceZones (f0Zone); +exposedPatchName front; + +extrudeModel linearNormal; +thickness 0.05; + +flipNormals false; +mergeFaces false; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict.flip b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict.flip new file mode 100644 index 00000000000..d928cc57886 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/extrudeMeshDict.flip @@ -0,0 +1,30 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object extrudeMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +constructFrom mesh; +sourceCase "<case>"; + +//sourcePatches (); +sourceFaceZones (f0Zone); +exposedPatchName front; + +extrudeModel linearNormal; +thickness 0.05; + +flipNormals false; +mergeFaces false; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSchemes b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSchemes new file mode 100644 index 00000000000..fe0555ce86c --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSchemes @@ -0,0 +1,51 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default Euler; +} + +gradSchemes +{ + default Gauss linear; + grad(p) Gauss linear; +} + +divSchemes +{ + default none; + div(phi,U) Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear orthogonal; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default orthogonal; +} + + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSolution b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSolution new file mode 100644 index 00000000000..a3b55dd1039 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/fvSolution @@ -0,0 +1,52 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solvers +{ + p + { + solver PCG; + preconditioner DIC; + tolerance 1e-06; + relTol 0.05; + } + + pFinal + { + $p; + relTol 0; + } + + U + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-05; + relTol 0; + } +} + +PISO +{ + nCorrectors 2; + nNonOrthogonalCorrectors 0; + pRefCell 0; + pRefValue 0; +} + + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict new file mode 100644 index 00000000000..568885f770a --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict @@ -0,0 +1,82 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object topoSetDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +actions +( + { + name f0; + type faceSet; + action new; + source boxToFace; + sourceInfo + { + box (-100 -100 -100) (1.01 1.01 1.01); + } + } + { + name f0; + type faceSet; + action delete; + source boundaryToFace; + sourceInfo + { + } + } + + ////- Remove internal faces; do boundary faces only + //{ + // name f0; + // type faceSet; + // action clear; + //} + + //- Add single external face + { + name f0; + type faceSet; + action add; + source boxToFace; + sourceInfo + { + // Minz face + box (-100 -100 -100) (1.01 1.01 0.01); + } + } + + { + name c0; + type cellSet; + action new; + source nearestToCell; + sourceInfo + { + points ((0.5 0.5 0.5)); + } + } + { + name f0Zone; + type faceZoneSet; + action new; + source setsToFaceZone; + sourceInfo + { + faceSet f0; + cellSet c0; + } + } +); + +// ************************************************************************* // diff --git a/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict.flip b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict.flip new file mode 100644 index 00000000000..b0f1cda8fb0 --- /dev/null +++ b/tutorials/mesh/extrudeMesh/faceZoneExtrusion/system/topoSetDict.flip @@ -0,0 +1,50 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object topoSetDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +actions +( + { + name c0; + type cellSet; + action new; + source nearestToCell; + sourceInfo + { + points ((1.5 1.5 0.5)); + } + } + { + name f0; + type faceSet; + action new; + source cellToFace; + set c0; + option all; + } + { + name f0Zone; + type faceZoneSet; + action new; + source setsToFaceZone; + sourceInfo + { + faceSet f0; + cellSet c0; + } + } +); + +// ************************************************************************* // diff --git a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/Allrun b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/Allrun index f9023e1ce6c..9df5c86d892 100755 --- a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/Allrun +++ b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/Allrun @@ -3,6 +3,8 @@ cd "${0%/*}" || exit # Run from this directory . ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions #------------------------------------------------------------------------------ +paraFoam -vtk -touch + restore0Dir runApplication blockMesh diff --git a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/decomposeParDict b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/decomposeParDict index 88ca418e76f..1d2799ae759 100644 --- a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/decomposeParDict +++ b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/decomposeParDict @@ -14,25 +14,9 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -numberOfSubdomains 2; - -//- Use the volScalarField named here as a weight for each cell in the -// decomposition. For example, use a particle population field to decompose -// for a balanced number of particles in a lagrangian simulation. -// weightField dsmcRhoNMean; - -method scotch; - -constraints -{ - //- Keep owner and neighbour of baffles on same processor - // (i.e. keep it detectable as a baffle). - // Baffles are two boundary face sharing the same points. - baffles - { - type preserveBaffles; - } -} +numberOfSubdomains 3; +// For testing only: use random decomposition. +method random; // ************************************************************************* // diff --git a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/fvSolution b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/fvSolution index cd310baa4aa..1d5d21be268 100644 --- a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/fvSolution +++ b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/fvSolution @@ -16,7 +16,7 @@ FoamFile solvers { - "(p|Phi)" + "(p|Phi|cellDisplacement)" { solver PCG; preconditioner DIC; diff --git a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/snappyHexMeshDict b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/snappyHexMeshDict index b8c3ce8cb35..14cf63af838 100644 --- a/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/snappyHexMeshDict +++ b/tutorials/mesh/snappyHexMesh/addLayersToFaceZone/system/snappyHexMeshDict @@ -174,16 +174,15 @@ addLayersControls // size of the refined cell outside layer (true) or absolute sizes (false). relativeSizes false; - // Expansion factor for layer mesh - expansionRatio 1.0; - - firstLayerThickness 0.2e-3; + // Layers defined by overall thickness and expansion ratio + thickness 0.7e-3; + firstLayerThickness 0.1e-3; // Minimum overall thickness of total layers. If for any reason layer // cannot be above minThickness do not add layer. // If relativeSizes this is relative to undistorted size of cell // outside layer.. - minThickness 0.1e-3; + minThickness 0.0001e-3; // Per final patch (so not geometry!) the layer information @@ -191,19 +190,19 @@ addLayersControls { maxY { - nSurfaceLayers 2; + nSurfaceLayers 3; } minY { - nSurfaceLayers 2; + nSurfaceLayers 3; } A { - nSurfaceLayers 2; + nSurfaceLayers 3; } A_slave { - nSurfaceLayers 2; + nSurfaceLayers 3; } } @@ -257,14 +256,29 @@ addLayersControls // Number of smoothing iterations of interior mesh movement direction nSmoothNormals 3; - - // Mesh shrinking - // Optional: at non-patched sides allow mesh to slip if extrusion // direction makes angle larger than slipFeatureAngle. Default is // 0.5*featureAngle. slipFeatureAngle 30; + + //// Motion solver instead of default medial axis + // + // //- Use displacementMotionSolver to shrink mesh + // meshShrinker displacementMotionSolver; + // + // //- Use laplacian for shrinking + // solver displacementLaplacian; + // + // displacementLaplacianCoeffs + // { + // diffusivity quadratic inverseDistance ("m.*"); + // } + + + + // Mesh shrinking + // Maximum number of snapping relaxation iterations. Should stop // before upon reaching a correct mesh. nRelaxIter 5; @@ -276,18 +290,10 @@ addLayersControls // exit if it reaches this number of iterations; possibly with an // illegal mesh. nLayerIter 50; -// -// // Max number of iterations after which relaxed meshQuality controls -// // get used. Up to nRelaxedIter it uses the settings in -// // meshQualityControls, -// // after nRelaxedIter it uses the values in -// // meshQualityControls::relaxed. -// nRelaxedIter 20; -// -// // Additional reporting: if there are just a few faces where there -// // are mesh errors (after adding the layers) print their face centres. -// // This helps in tracking down problematic mesh areas. -// //additionalReporting true; + + // Overall number of outer layer of iterations. Default is 1 -> all + // layers are added in one pass. + nOuterIter 3; } // Generic mesh quality settings. At any undoable phase these determine @@ -313,5 +319,15 @@ meshQualityControls // Note: the write tolerance needs to be higher than this. mergeTolerance 1e-6; +debugFlags +( + //mesh +); + +// Write flags +writeFlags +( + //layerFields // write volScalarField for layer coverage +); // ************************************************************************* // diff --git a/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/controlDict b/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/controlDict new file mode 100644 index 00000000000..43a296491d0 --- /dev/null +++ b/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/controlDict @@ -0,0 +1,54 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v1906 | +| \\ / A nd | Web: www.OpenFOAM.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +//DebugSwitches +//{ +// holeToFace 1; +//} + +application icoFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 100; + +deltaT 1; + +writeControl timeStep; + +writeInterval 1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable true; + + +// ************************************************************************* // diff --git a/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/snappyHexMeshDict b/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/snappyHexMeshDict new file mode 100644 index 00000000000..51d69f15797 --- /dev/null +++ b/tutorials/mesh/snappyHexMesh/sphere_gapClosure/system/snappyHexMeshDict @@ -0,0 +1,311 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v1906 | +| \\ / A nd | Web: www.OpenFOAM.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object snappyHexMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Which of the steps to run +castellatedMesh true; +snap true; +addLayers false; + + +// Geometry. Definition of all surfaces. All surfaces are of class +// searchableSurface. +// Surfaces are used +// - to specify refinement for any mesh cell intersecting it +// - to specify refinement for any mesh cell inside/outside/near +// - to 'snap' the mesh boundary to the surface +geometry +{ + dummy + { + // Dummy surface just to see if indexing is correct + type box; + min (0 0 0); + max (1 1 1); + } + sphere_with_hole.stl + { + type triSurfaceMesh; + name sphere; + } +}; + + + +// Settings for the castellatedMesh generation. +castellatedMeshControls +{ + + // Refinement parameters + // ~~~~~~~~~~~~~~~~~~~~~ + + // If local number of cells is >= maxLocalCells on any processor + // switches from from refinement followed by balancing + // (current method) to (weighted) balancing before refinement. + maxLocalCells 100000; + + // Overall cell limit (approximately). Refinement will stop immediately + // upon reaching this number so a refinement level might not complete. + // Note that this is the number of cells before removing the part which + // is not 'visible' from the keepPoint. The final number of cells might + // actually be a lot less. + maxGlobalCells 2000000; + + // The surface refinement loop might spend lots of iterations refining just a + // few cells. This setting will cause refinement to stop if <= minimumRefine + // are selected for refinement. Note: it will at least do one iteration + // (unless the number of cells to refine is 0) + minRefinementCells 1; + + // Allow a certain level of imbalance during refining + // (since balancing is quite expensive) + // Expressed as fraction of perfect balance (= overall number of cells / + // nProcs). 0=balance always. + maxLoadUnbalance 0.10; + + + // Number of buffer layers between different levels. + // 1 means normal 2:1 refinement restriction, larger means slower + // refinement. + nCellsBetweenLevels 1; + + + + // Explicit feature edge refinement + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Specifies a level for any cell intersected by its edges. + // This is a featureEdgeMesh, read from constant/triSurface for now. + features + ( + ); + + + + // Surface based refinement + // ~~~~~~~~~~~~~~~~~~~~~~~~ + + // Specifies two levels for every surface. The first is the minimum level, + // every cell intersecting a surface gets refined up to the minimum level. + // The second level is the maximum level. Cells that 'see' multiple + // intersections where the intersections make an + // angle > resolveFeatureAngle get refined up to the maximum level. + + refinementSurfaces + { + "sphere.*" + { + // Surface-wise min and max refinement level + level (5 5); + + // Optional level at which to start early checking for leaks + //leakLevel 2; + //faceZone sphere_with_hole; + //cellZone sphere_with_hole; + //cellZoneInside insidePoint; + //insidePoint (1.5 1.5 1.5); + } + } + + // Resolve sharp angles + resolveFeatureAngle 30; + + + // Region-wise refinement + // ~~~~~~~~~~~~~~~~~~~~~~ + + // Specifies refinement level for cells in relation to a surface. One of + // three modes + // - distance. 'levels' specifies per distance to the surface the + // wanted refinement level. The distances need to be specified in + // descending order. + // - inside. 'levels' is only one entry and only the level is used. All + // cells inside the surface get refined up to the level. The surface + // needs to be closed for this to be possible. + // - outside. Same but cells outside. + + refinementRegions + { + } + + + // Mesh selection + // ~~~~~~~~~~~~~~ + + // After refinement patches get added for all refinementSurfaces and + // all cells intersecting the surfaces get put into these patches. The + // section reachable from the locationInMesh is kept. + // NOTE: This point should never be on a face, always inside a cell, even + // after refinement. + locationInMesh (1.5 1.5 1.5); + + locationsOutsideMesh ((3.8 3.8 3.8)); + + // Whether any faceZones (as specified in the refinementSurfaces) + // are only on the boundary of corresponding cellZones or also allow + // free-standing zone faces. Not used if there are no faceZones. + allowFreeStandingZoneFaces false; +} + + +// Settings for the snapping. +snapControls +{ + //- Number of patch smoothing iterations before finding correspondence + // to surface + nSmoothPatch 3; + + //- Relative distance for points to be attracted by surface feature point + // or edge. True distance is this factor times local + // maximum edge length. + tolerance 1.0; + + //- Number of mesh displacement relaxation iterations. + nSolveIter 10; + + //- Maximum number of snapping relaxation iterations. Should stop + // before upon reaching a correct mesh. + nRelaxIter 5; + + // Feature snapping + + //- Number of feature edge snapping iterations. + // Leave out altogether to disable. + nFeatureSnapIter 10; + + //- Detect (geometric only) features by sampling the surface + // (default=false). + implicitFeatureSnap false; + + //- Use castellatedMeshControls::features (default = true) + explicitFeatureSnap false; + + //- Detect points on multiple surfaces (only for explicitFeatureSnap) + multiRegionFeatureSnap false; +} + + + +// Settings for the layer addition. +addLayersControls +{ + // Are the thickness parameters below relative to the undistorted + // size of the refined cell outside layer (true) or absolute sizes (false). + relativeSizes true; + + // Per final patch (so not geometry!) the layer information + layers + { + } + + // Expansion factor for layer mesh + expansionRatio 1.0; + + // Wanted thickness of final added cell layer. If multiple layers + // is the thickness of the layer furthest away from the wall. + // Relative to undistorted size of cell outside layer. + // See relativeSizes parameter. + finalLayerThickness 0.3; + + // Minimum thickness of cell layer. If for any reason layer + // cannot be above minThickness do not add layer. + // Relative to undistorted size of cell outside layer. + minThickness 0.1; + + // If points get not extruded do nGrow layers of connected faces that are + // also not grown. This helps convergence of the layer addition process + // close to features. + // Note: changed(corrected) w.r.t 1.7.x! (didn't do anything in 1.7.x) + nGrow 0; + + // Advanced settings + + // When not to extrude surface. 0 is flat surface, 90 is when two faces + // are perpendicular + featureAngle 60; + + // At non-patched sides allow mesh to slip if extrusion direction makes + // angle larger than slipFeatureAngle. + slipFeatureAngle 30; + + // Maximum number of snapping relaxation iterations. Should stop + // before upon reaching a correct mesh. + nRelaxIter 3; + + // Number of smoothing iterations of surface normals + nSmoothSurfaceNormals 1; + + // Number of smoothing iterations of interior mesh movement direction + nSmoothNormals 3; + + // Smooth layer thickness over surface patches + nSmoothThickness 10; + + // Stop layer growth on highly warped cells + maxFaceThicknessRatio 0.5; + + // Reduce layer growth where ratio thickness to medial + // distance is large + maxThicknessToMedialRatio 0.3; + + // Angle used to pick up medial axis points + // Note: changed(corrected) w.r.t 1.7.x! 90 degrees corresponds to 130 + // in 1.7.x. + minMedianAxisAngle 90; + + + // Create buffer region for new layer terminations + nBufferCellsNoExtrude 0; + + + // Overall max number of layer addition iterations. The mesher will exit + // if it reaches this number of iterations; possibly with an illegal + // mesh. + nLayerIter 50; +} + + + +// Generic mesh quality settings. At any undoable phase these determine +// where to undo. +meshQualityControls +{ + #includeEtc "caseDicts/meshQualityDict" + + // Advanced + + //- Number of error distribution iterations + nSmoothScale 4; + //- Amount to scale back displacement at error points + errorReduction 0.75; +} + + +// Advanced + +// Format to use for lines (e.g. leak path) +setFormat ensight; + +//debugFlags +//( +// mesh +//); + +// Merge tolerance. Is fraction of overall bounding box of initial mesh. +// Note: the write tolerance needs to be higher than this. +mergeTolerance 1e-6; + + +// ************************************************************************* // -- GitLab