diff --git a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C index a7ec45eef47ab4c8c654330a2889d9525c39759b..573f2926e2132893ad5cfa4ccd854f6f22689cfd 100644 --- a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C +++ b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -493,21 +493,30 @@ int main(int argc, char *argv[]) // new patchID to neighbour processor) // - number of new patches (nPatches) - labelList sidePatchID; + labelList edgePatchID; + labelList edgeZoneID; + boolList edgeFlip; + labelList inflateFaceID; label nPatches; Map<label> nbrProcToPatch; Map<label> patchToNbrProc; - addPatchCellLayer::calcSidePatch + addPatchCellLayer::calcExtrudeInfo ( + true, // for internal edges get zone info from any face + mesh, globalFaces, edgeGlobalFaces, extrudePatch, - sidePatchID, + edgePatchID, nPatches, nbrProcToPatch, - patchToNbrProc + patchToNbrProc, + + edgeZoneID, + edgeFlip, + inflateFaceID ); @@ -659,7 +668,12 @@ int main(int argc, char *argv[]) ratio, // expansion ratio extrudePatch, // patch faces to extrude - sidePatchID, // if boundary edge: patch to use + + edgePatchID, // if boundary edge: patch for extruded face + edgeZoneID, // optional zone for extruded face + edgeFlip, + inflateFaceID, // mesh face that zone/patch info is from + exposedPatchID, // if new mesh: patches for exposed faces nFaceLayers, nPointLayers, diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C index c6906a6c920d1a351bd454fcaabc4ca07ca0e3c3..67a9c3a9444a3803f8c43dd270b2b7330219b32a 100644 --- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C +++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMesh.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -1121,6 +1121,38 @@ int main(int argc, char *argv[]) ); + // Refinement parameters + const refinementParameters refineParams(refineDict); + + // Snap parameters + const snapParameters snapParams(snapDict); + + + + // Add all the cellZones and faceZones + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // 1. cellZones relating to surface (faceZones added later) + + const labelList namedSurfaces + ( + surfaceZonesInfo::getNamedSurfaces(surfaces.surfZones()) + ); + + labelList surfaceToCellZone = surfaceZonesInfo::addCellZonesToMesh + ( + surfaces.surfZones(), + namedSurfaces, + mesh + ); + + + // 2. cellZones relating to locations + + refineParams.addCellZonesToMesh(mesh); + + + // Add all the surface regions as patches // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1128,6 +1160,8 @@ int main(int argc, char *argv[]) // (faceZone surfaces) labelList globalToMasterPatch; labelList globalToSlavePatch; + + { Info<< nl << "Adding patches for surface regions" << nl @@ -1148,6 +1182,7 @@ int main(int argc, char *argv[]) const labelList& surfaceGeometry = surfaces.surfaces(); const PtrList<dictionary>& surfacePatchInfo = surfaces.patchInfo(); + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); forAll(surfaceGeometry, surfI) { @@ -1157,7 +1192,9 @@ int main(int argc, char *argv[]) Info<< surfaces.names()[surfI] << ':' << nl << nl; - if (surfaces.surfZones()[surfI].faceZoneName().empty()) + const word& fzName = surfaces.surfZones()[surfI].faceZoneName(); + + if (fzName.empty()) { // 'Normal' surface forAll(regNames, i) @@ -1188,7 +1225,7 @@ int main(int argc, char *argv[]) Info<< setf(ios_base::left) << setw(6) << patchI - << setw(20) << mesh.boundaryMesh()[patchI].type() + << setw(20) << pbm[patchI].type() << setw(30) << regNames[i] << nl; globalToMasterPatch[globalRegionI] = patchI; @@ -1228,7 +1265,7 @@ int main(int argc, char *argv[]) Info<< setf(ios_base::left) << setw(6) << patchI - << setw(20) << mesh.boundaryMesh()[patchI].type() + << setw(20) << pbm[patchI].type() << setw(30) << regNames[i] << nl; globalToMasterPatch[globalRegionI] = patchI; @@ -1260,12 +1297,27 @@ int main(int argc, char *argv[]) Info<< setf(ios_base::left) << setw(6) << patchI - << setw(20) << mesh.boundaryMesh()[patchI].type() + << setw(20) << pbm[patchI].type() << setw(30) << slaveName << nl; globalToSlavePatch[globalRegionI] = patchI; } } + + // For now: have single faceZone per surface. Use first + // region in surface for patch for zoneing + if (regNames.size()) + { + label globalRegionI = surfaces.globalRegion(surfI, 0); + + meshRefiner.addFaceZone + ( + fzName, + pbm[globalToMasterPatch[globalRegionI]].name(), + pbm[globalToSlavePatch[globalRegionI]].name(), + surfaces.surfZones()[surfI].faceType() + ); + } } Info<< nl; @@ -1275,10 +1327,85 @@ int main(int argc, char *argv[]) } + + // Add all information for all the remaining faceZones + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + HashTable<Pair<word> > faceZoneToPatches; + forAll(mesh.faceZones(), zoneI) + { + const word& fzName = mesh.faceZones()[zoneI].name(); + + label mpI, spI; + surfaceZonesInfo::faceZoneType fzType; + bool hasInfo = meshRefiner.getFaceZoneInfo(fzName, mpI, spI, fzType); + + if (!hasInfo) + { + // faceZone does not originate from a surface but presumably + // from a cellZone pair instead + string::size_type i = fzName.find("_to_"); + if (i != string::npos) + { + word cz0 = fzName.substr(0, i); + word cz1 = fzName.substr(i+4, fzName.size()-i+4); + word slaveName(cz1 + "_to_" + cz0); + faceZoneToPatches.insert(fzName, Pair<word>(fzName, slaveName)); + } + else + { + // Add as fzName + fzName_slave + const word slaveName = fzName + "_slave"; + faceZoneToPatches.insert(fzName, Pair<word>(fzName, slaveName)); + } + } + } + + if (faceZoneToPatches.size()) + { + autoRefineDriver::addFaceZones + ( + meshRefiner, + refineParams, + faceZoneToPatches + ); + } + + + + // Re-do intersections on meshed boundaries since they use an extrapolated + // other side + { + const labelList adaptPatchIDs(meshRefiner.meshedPatches()); + + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + + label nFaces = 0; + forAll(adaptPatchIDs, i) + { + nFaces += pbm[adaptPatchIDs[i]].size(); + } + + labelList faceLabels(nFaces); + nFaces = 0; + forAll(adaptPatchIDs, i) + { + const polyPatch& pp = pbm[adaptPatchIDs[i]]; + forAll(pp, i) + { + faceLabels[nFaces++] = pp.start()+i; + } + } + meshRefiner.updateIntersections(faceLabels); + } + + + // Parallel // ~~~~~~~~ - // Decomposition + // Construct decomposition engine. Note: cannot use decompositionModel + // MeshObject since we're clearing out the mesh inside the mesh generation. autoPtr<decompositionMethod> decomposerPtr ( decompositionMethod::New @@ -1312,14 +1439,17 @@ int main(int argc, char *argv[]) const Switch wantSnap(meshDict.lookup("snap")); const Switch wantLayers(meshDict.lookup("addLayers")); - // Refinement parameters - const refinementParameters refineParams(refineDict); - - // Snap parameters - const snapParameters snapParams(snapDict); + const Switch mergePatchFaces + ( + meshDict.lookupOrDefault("mergePatchFaces", true) + ); - // Layer addition parameters - const layerParameters layerParams(layerDict, mesh.boundaryMesh()); + if (!mergePatchFaces) + { + Info<< "Not merging patch-faces of cell to preserve" + << " (split)hex cell shape." + << nl << endl; + } if (wantRefine) @@ -1348,6 +1478,7 @@ int main(int argc, char *argv[]) refineParams, snapParams, refineParams.handleSnapProblems(), + mergePatchFaces, // merge co-planar faces motionDict ); @@ -1387,6 +1518,7 @@ int main(int argc, char *argv[]) ( snapDict, motionDict, + mergePatchFaces, curvature, planarAngle, snapParams @@ -1408,6 +1540,9 @@ int main(int argc, char *argv[]) { cpuTime timer; + // Layer addition parameters + const layerParameters layerParams(layerDict, mesh.boundaryMesh()); + autoLayerDriver layerDriver ( meshRefiner, @@ -1433,6 +1568,7 @@ int main(int argc, char *argv[]) layerDict, motionDict, layerParams, + mergePatchFaces, preBalance, decomposer, distributor diff --git a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict index 577e116b64d067d8f83484b677a850faa782d10a..9b759b5ca4f6628dd9e11117d62697a0b12e0aef 100644 --- a/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict +++ b/applications/utilities/mesh/generation/snappyHexMesh/snappyHexMeshDict @@ -71,6 +71,11 @@ geometry } }; + +// Optional: avoid patch-face merging. Allows mesh to be used for +// refinement/unrefinement +//mergePatchFaces off; // default on + // Settings for the castellatedMesh generation. castellatedMeshControls { @@ -177,7 +182,7 @@ castellatedMeshControls // how to select the cells that are in the cellZone // (inside / outside / specified insidePoint) // The orientation of the faceZone is - // - if on cellZone(s) : point out of (maximum) cellZone + // - if on cellZone(s) : point out of (minimum) cellZone // - if freestanding : oriented according to surface //faceZone sphere; @@ -249,16 +254,70 @@ castellatedMeshControls // 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. + // section reachable from the location(s)InMesh is kept. // NOTE: This point should never be on a face, always inside a cell, even // after refinement. - locationInMesh (5 0.28 0.43); + // + // There are two different ways of specifying the regions to keep: + // 1. a single locationInMesh. All the 'zoned' surfaces are marked as such + // in the refinementSurfaces with the faceZone and cellZone keywords. + // + // or + // + // 2. multiple locationsInMesh, with per location the name of the cellZone. + // This uses walking to determine zones and automatically creates + // faceZones on the outside of cellZones. + + + // Ad 1. Specify a single location and how to treat faces inbetween + // cellZones + locationInMesh (5 0.28 0.43); + + // 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 true; + + + + // 2. Specify multiple locations with optional cellZones for the + // regions. faceZones are automatically constructed from the + // names of the cellZones: <cellZoneA> _to_ <cellZoneB> + // where the cellZoneA is the lowest numbered cellZone (non-cellZone + // cells are in a special region called "none" which is always + // last). + + locationsInMesh + ( + ((-0.09 -0.039 -0.049) bottomAir) // cellZone 0 + ((-0.09 0.009 -0.049) topAir) // cellZone 1 + ((-0.09 0.001 -0.049) leftSolid) // cellZone 2 + ((0.02 0.001 -0.049) rightSolid) // cellZone 3 + ((-0.001 -0.039 0.0015) heater) // cellZone 4 + ); + + // Per synthesised faceZone name the faceType and type of baffles to + // create + faceZoneControls + { + bottomAir_to_heater + { + // Optional specification of patch type (default is wall). No + // constraint types (cyclic, symmetry) etc. are allowed. + patchInfo + { + type patch; + inGroups (patchPatches); + } + faceType baffle; + } + } + - // 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 true; + // Optional locations that should not be reachable from + // location(s)InMesh + locationsOutsideMesh ((100 100 100)); // Optional: do not remove cells likely to give snapping problems // handleSnapProblems false; @@ -266,6 +325,10 @@ castellatedMeshControls // Optional: switch off topological test for cells to-be-squashed // and use geometric test instead //useTopologicalSnapDetection false; + + // Optional: do not refine surface cells with opposite faces of + // differing refinement levels + //interfaceRefine false; } // Settings for the snapping. @@ -275,6 +338,11 @@ snapControls // to surface nSmoothPatch 3; + // Optional: number of smoothing iterations for internal points on + // refinement interfaces. This will reduce non-orthogonality on + // refinement interfaces. + //nSmoothInternal $nSmoothPatch; + // Maximum relative distance for points to be attracted by surface. // True distance is this factor times local maximum edge length. // Note: changed(corrected) w.r.t 17x! (17x used 2* tolerance) @@ -287,6 +355,11 @@ snapControls // before upon reaching a correct mesh. nRelaxIter 5; + // (wip) disable snapping to opposite near surfaces (revert to 22x + // behaviour) + // detectNearSurfacesSnap false; + + // Feature snapping // Number of feature edge snapping iterations. @@ -305,8 +378,28 @@ snapControls multiRegionFeatureSnap false; - // wip: disable snapping to opposite near surfaces (revert to 22x behaviour) - // detectNearSurfacesSnap false; + //- When to run face splitting (never at first iteration, always + // at last iteration). Is interval. Default -1 (disabled) + //nFaceSplitInterval 5; + + + // (wip) Optional for explicit feature snapping: + //- Detect baffle edges. Default is true. + //detectBaffles false; + //- Erase attraction close to feature point. Default is false. + //releasePoints true; + //- Walk along feature edges, adding missing ones. Default is true. + //stringFeatures false; + //- If diagonal attraction also attract other face points. Default is + // false + //avoidDiagonal true; + //- When splitting what concave faces to leave intact. Default is 45 + // degrees. + //concaveAngle 30; + //- When splitting the minimum area ratio of faces. If face split + // causes ratio of area less than this do not split. Default is 0.3 + //minAreaRatio 0.3; + } // Settings for the layer addition. @@ -350,10 +443,10 @@ addLayersControls // cannot be above minThickness do not add layer. // If relativeSizes this is relative to undistorted size of cell // outside layer.. - minThickness 0.25; + minThickness 0.1; - // Per final patch (so not geometry!) the layer information + // Per final patch or faceZone (so not geometry!) the layer information // Note: This behaviour changed after 21x. Any non-mentioned patches // now slide unless: // - nSurfaceLayers is explicitly mentioned to be 0. @@ -397,6 +490,10 @@ addLayersControls // are perpendicular featureAngle 130; + // When to merge patch faces. Default is featureAngle. Useful when + // featureAngle is large. + //mergePatchFacesAngle 45; + // Stop layer growth on highly warped cells maxFaceThicknessRatio 0.5; @@ -433,8 +530,10 @@ addLayersControls // default is 0. //nSmoothDisplacement 90; - // (wip)Optional: do not extrude a point if none of the surrounding points is - // not extruded. Default is false. + // (wip)Optional: do not extrude any point where + // (false) : all surrounding faces are not fully extruded + // (true) : all surrounding points are not extruded + // Default is false. //detectExtrusionIsland true; @@ -449,7 +548,8 @@ addLayersControls // before upon reaching a correct mesh. nRelaxIter 5; - // Create buffer region for new layer terminations + // Create buffer region for new layer terminations, i.e. gradually + // step down number of layers. Set to <0 to terminate layer in one go. nBufferCellsNoExtrude 0; // Overall max number of layer addition iterations. The mesher will diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C index 58d39bc3cf75c31cf9e56a45b30ec9c65d34f712..4d518bcece5a8a2c03865d6f66bc3b52cc97dfe1 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -34,6 +34,8 @@ License #include "polyModifyFace.H" #include "polyAddCell.H" #include "globalIndex.H" +#include "PatchTools.H" +#include "dummyTransform.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -221,13 +223,15 @@ Foam::labelPair Foam::addPatchCellLayer::getEdgeString } -// Adds a side face i.e. extrudes a patch edge. Foam::label Foam::addPatchCellLayer::addSideFace ( const indirectPrimitivePatch& pp, const labelListList& addedCells, // per pp face the new extruded cell const face& newFace, const label newPatchID, + const label zoneI, + const bool edgeFlip, + const label inflateFaceI, const label ownFaceI, // pp face that provides owner const label nbrFaceI, @@ -238,68 +242,14 @@ Foam::label Foam::addPatchCellLayer::addSideFace polyTopoChange& meshMod ) const { - // Face or edge to 'inflate' from - label inflateEdgeI = -1; - label inflateFaceI = -1; - - // Check mesh faces using edge - if (addToMesh_) - { - forAll(meshFaces, i) - { - if (mesh_.isInternalFace(meshFaces[i])) - { - // meshEdge uses internal faces so ok to inflate from it - inflateEdgeI = meshEdgeI; - break; - } - } - } - - // Zone info comes from any side patch face. Otherwise -1 since we - // don't know what to put it in - inherit from the extruded faces? - label zoneI = -1; //mesh_.faceZones().whichZone(meshFaceI); - bool flip = false; - label addedFaceI = -1; + // Is patch edge external edge of indirectPrimitivePatch? if (nbrFaceI == -1) { // External edge so external face. - const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - - // Loop over all faces connected to edge to inflate and - // see if we can find a face that is otherPatchID - - // Get my mesh face and its zone. - label meshFaceI = pp.addressing()[ownFaceI]; - - forAll(meshFaces, k) - { - label faceI = meshFaces[k]; - - if - ( - (faceI != meshFaceI) - && (patches.whichPatch(faceI) == newPatchID) - ) - { - // Found the patch face. Use it to inflate from - inflateEdgeI = -1; - inflateFaceI = faceI; - - zoneI = mesh_.faceZones().whichZone(faceI); - if (zoneI != -1) - { - label index = mesh_.faceZones()[zoneI].whichFace(faceI); - flip = mesh_.faceZones()[zoneI].flipMap()[index]; - } - break; - } - } - // Determine if different number of layer on owner and neighbour side // (relevant only for coupled faces). See section for internal edge // below. @@ -333,16 +283,16 @@ Foam::label Foam::addPatchCellLayer::addSideFace ( polyAddFace ( - newFace, // face - addedCells[ownFaceI][layerOwn], // owner - -1, // neighbour - -1, // master point - inflateEdgeI, // master edge - inflateFaceI, // master face - false, // flux flip - newPatchID, // patch for face - zoneI, // zone for face - flip // face zone flip + newFace, // face + addedCells[ownFaceI][layerOwn], // owner + -1, // neighbour + -1, // master point + -1, // master edge + inflateFaceI, // master face + false, // flux flip + newPatchID, // patch for face + zoneI, // zone for face + edgeFlip // face zone flip ) ); } @@ -395,20 +345,37 @@ Foam::label Foam::addPatchCellLayer::addSideFace layerOwn = layerI; } + + // Check mesh internal faces using edge to initialise + label inflateEdgeI = -1; + if (addToMesh_) + { + forAll(meshFaces, i) + { + if (mesh_.isInternalFace(meshFaces[i])) + { + // meshEdge uses internal faces so ok to inflate from it + inflateEdgeI = meshEdgeI; + break; + } + } + } + + addedFaceI = meshMod.setAction ( polyAddFace ( - newFace, // face - addedCells[ownFaceI][layerOwn], // owner - addedCells[nbrFaceI][layerNbr], // neighbour - -1, // master point - inflateEdgeI, // master edge - -1, // master face - false, // flux flip - -1, // patch for face - zoneI, // zone for face - flip // face zone flip + newFace, // face + addedCells[ownFaceI][layerOwn], // owner + addedCells[nbrFaceI][layerNbr], // neighbour + -1, // master point + inflateEdgeI, // master edge + -1, // master face + false, // flux flip + -1, // patch for face + zoneI, // zone for face + edgeFlip // face zone flip ) ); @@ -467,6 +434,134 @@ void Foam::addPatchCellLayer::setFaceProps } +void Foam::addPatchCellLayer::setFaceProps +( + const polyMesh& mesh, + const indirectPrimitivePatch& pp, + const label ppEdgeI, + const label faceI, + + label& patchI, + label& zoneI, + bool& zoneFlip, + label& inflateFaceI +) +{ + setFaceProps + ( + mesh, + faceI, + + patchI, + zoneI, + zoneFlip + ); + + if (patchI != -1 || zoneI != -1) + { + inflateFaceI = faceI; + } + + if (zoneI != -1) + { + // Correct flip for patch edge ordering + const edge& ppEdge = pp.edges()[ppEdgeI]; + const edge patchEdge + ( + pp.meshPoints()[ppEdge[0]], + pp.meshPoints()[ppEdge[1]] + ); + + const face& f = mesh.faces()[faceI]; + bool found = false; + forAll(f, fp) + { + const edge e(f[fp], f.nextLabel(fp)); + int stat = edge::compare(e, patchEdge); + if (stat == 1) + { + found = true; + break; + } + else if (stat == -1) + { + found = true; + zoneFlip = !zoneFlip; + break; + } + } + + if (!found) + { + FatalErrorIn("addPatchCellLayer::setFaceProps(..)") + << "Problem: cannot find patch edge " << ppEdgeI + << " with mesh vertices " << patchEdge + << " at " << patchEdge.line(mesh.points()) + << " is not in face " << faceI << " with mesh vertices " + << f + << exit(FatalError); + } + } +} + + +void Foam::addPatchCellLayer::findZoneFace +( + const bool useInternalFaces, + const bool useBoundaryFaces, + + const polyMesh& mesh, + const indirectPrimitivePatch& pp, + const label ppEdgeI, + const UIndirectList<label>& excludeFaces, + const labelList& meshFaces, + + label& inflateFaceI, + label& patchI, + label& zoneI, + bool& zoneFlip +) +{ + inflateFaceI = -1; + patchI = -1; + zoneI = -1; + zoneFlip = false; + + forAll(meshFaces, k) + { + label faceI = meshFaces[k]; + + if + ( + (findIndex(excludeFaces, faceI) == -1) + && ( + (mesh.isInternalFace(faceI) && useInternalFaces) + || (!mesh.isInternalFace(faceI) && useBoundaryFaces) + ) + ) + { + setFaceProps + ( + mesh, + pp, + ppEdgeI, + faceI, + + patchI, + zoneI, + zoneFlip, + inflateFaceI + ); + + if (zoneI != -1 || patchI != -1) + { + break; + } + } + } +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // Construct from mesh @@ -564,38 +659,61 @@ Foam::labelListList Foam::addPatchCellLayer::globalEdgeFaces } -void Foam::addPatchCellLayer::calcSidePatch +void Foam::addPatchCellLayer::calcExtrudeInfo ( + const bool zoneFromAnyFace, + const polyMesh& mesh, const globalIndex& globalFaces, const labelListList& globalEdgeFaces, const indirectPrimitivePatch& pp, - labelList& sidePatchID, + labelList& edgePatchID, label& nPatches, Map<label>& nbrProcToPatch, - Map<label>& patchToNbrProc + Map<label>& patchToNbrProc, + labelList& edgeZoneID, + boolList& edgeFlip, + labelList& inflateFaceID ) { const polyBoundaryMesh& patches = mesh.boundaryMesh(); + const globalMeshData& gd = mesh.globalData(); // Precalculate mesh edges for pp.edges. const labelList meshEdges(pp.meshEdges(mesh.edges(), mesh.pointEdges())); - sidePatchID.setSize(pp.nEdges()); - sidePatchID = -1; + edgePatchID.setSize(pp.nEdges()); + edgePatchID = -1; + edgeZoneID.setSize(pp.nEdges()); + edgeZoneID = -1; + edgeFlip.setSize(pp.nEdges()); + edgeFlip = false; + + + // Determine properties for faces extruded from edges + // - edge inbetween two different processors: + // - extrude as patch face on correct processor + // - edge at end of patch (so edgeFaces.size() == 1): + // - use mesh face that is a boundary face + // - edge internal to patch (so edgeFaces.size() == 2): + + // These also get determined but not (yet) exported: // - whether face is created from other face or edge - // - what zone&orientation face should have - labelList inflateEdgeI(pp.nEdges(), -1); - labelList inflateFaceI(pp.nEdges(), -1); - labelList sideZoneID(pp.nEdges(), -1); - boolList sideFlip(pp.nEdges(), false); + inflateFaceID.setSize(pp.nEdges(), -1); nPatches = patches.size(); + + + // Pass1: + // For all edges inbetween two processors: see if matches to existing + // processor patch or create interprocessor-patch if necessary. + // Sets edgePatchID[edgeI] but none of the other quantities + forAll(globalEdgeFaces, edgeI) { const labelList& eGlobalFaces = globalEdgeFaces[edgeI]; @@ -606,8 +724,7 @@ void Foam::addPatchCellLayer::calcSidePatch ) { // Locally but not globally a boundary edge. Hence a coupled - // edge. Find the patch to use if on different - // processors. + // edge. Find the patch to use if on different processors. label f0 = eGlobalFaces[0]; label f1 = eGlobalFaces[1]; @@ -625,18 +742,25 @@ void Foam::addPatchCellLayer::calcSidePatch if (otherProcI != -1) { - sidePatchID[edgeI] = findProcPatch(mesh, otherProcI); - if (sidePatchID[edgeI] == -1) + if (findIndex(gd[Pstream::myProcNo()], otherProcI) != -1) + { + // There is already a processorPolyPatch to otherProcI. + // Use it. Note that we can only index procPatchMap + // if the processor actually is a neighbour processor + // so that is why we first check. + edgePatchID[edgeI] = gd.procPatchMap()[otherProcI]; + } + else { // Cannot find a patch to processor. See if already // marked for addition if (nbrProcToPatch.found(otherProcI)) { - sidePatchID[edgeI] = nbrProcToPatch[otherProcI]; + edgePatchID[edgeI] = nbrProcToPatch[otherProcI]; } else { - sidePatchID[edgeI] = nPatches; + edgePatchID[edgeI] = nPatches; nbrProcToPatch.insert(otherProcI, nPatches); patchToNbrProc.insert(nPatches, otherProcI); nPatches++; @@ -647,9 +771,8 @@ void Foam::addPatchCellLayer::calcSidePatch } - - // Determine face properties for all other boundary edges - // ------------------------------------------------------ + // Pass2: determine face properties for all other edges + // ---------------------------------------------------- const labelListList& edgeFaces = pp.edgeFaces(); @@ -657,13 +780,10 @@ void Foam::addPatchCellLayer::calcSidePatch forAll(edgeFaces, edgeI) { - if (edgeFaces[edgeI].size() == 1 && sidePatchID[edgeI] == -1) + if (edgePatchID[edgeI] == -1) { - // Proper, uncoupled patch edge. + UIndirectList<label> ppFaces(pp.addressing(), edgeFaces[edgeI]); - label myFaceI = pp.addressing()[edgeFaces[edgeI][0]]; - - // Pick up any boundary face on this edge and use its properties label meshEdgeI = meshEdges[edgeI]; const labelList& meshFaces = mesh.edgeFaces ( @@ -671,43 +791,71 @@ void Foam::addPatchCellLayer::calcSidePatch dynMeshEdgeFaces ); - forAll(meshFaces, k) + if (edgeFaces[edgeI].size() == 2) { - label faceI = meshFaces[k]; + // Internal edge. Look at any face (internal or boundary) to + // determine extrusion properties. First one that has zone + // info wins - if (faceI != myFaceI && !mesh.isInternalFace(faceI)) - { - setFaceProps - ( - mesh, - faceI, + label dummyPatchI = -1; + findZoneFace + ( + true, // useInternalFaces, + zoneFromAnyFace, // useBoundaryFaces, - sidePatchID[edgeI], - sideZoneID[edgeI], - sideFlip[edgeI] - ); - inflateFaceI[edgeI] = faceI; - inflateEdgeI[edgeI] = -1; + mesh, + pp, + edgeI, - break; - } + + ppFaces, // excludeFaces, + meshFaces, // meshFaces, + + inflateFaceID[edgeI], + dummyPatchI, // do not use patch info + edgeZoneID[edgeI], + edgeFlip[edgeI] + ); + } + else + { + // Proper, uncoupled patch edge + + findZoneFace + ( + false, // useInternalFaces, + true, // useBoundaryFaces, + + mesh, + pp, + edgeI, + + + ppFaces, // excludeFaces, + meshFaces, // meshFaces, + + inflateFaceID[edgeI], + edgePatchID[edgeI], + edgeZoneID[edgeI], + edgeFlip[edgeI] + ); } } } - // Now hopefully every boundary edge has a side patch. Check + // Now hopefully every boundary edge has a edge patch. Check if (debug) { forAll(edgeFaces, edgeI) { - if (edgeFaces[edgeI].size() == 1 && sidePatchID[edgeI] == -1) + if (edgeFaces[edgeI].size() == 1 && edgePatchID[edgeI] == -1) { const edge& e = pp.edges()[edgeI]; - //FatalErrorIn("addPatchCellLayer::calcSidePatch(..)") - WarningIn("addPatchCellLayer::calcSidePatch(..)") - << "Have no sidePatchID for edge " << edgeI << " points " + //FatalErrorIn("addPatchCellLayer::calcExtrudeInfo(..)") + WarningIn("addPatchCellLayer::calcExtrudeInfo(..)") + << "Have no edgePatchID for edge " << edgeI << " points " << pp.points()[pp.meshPoints()[e[0]]] << pp.points()[pp.meshPoints()[e[1]]] //<< abort(FatalError); @@ -718,15 +866,15 @@ void Foam::addPatchCellLayer::calcSidePatch - // Now we have sidepatch see if we have patchface or edge to inflate - // from. + // Pass3: for any faces set in pass1 see if we can find a processor face + // to inherit from (we only have a patch, not a patch face) forAll(edgeFaces, edgeI) { if ( edgeFaces[edgeI].size() == 1 - && sidePatchID[edgeI] != -1 - && inflateFaceI[edgeI] == -1 + && edgePatchID[edgeI] != -1 + && inflateFaceID[edgeI] == -1 ) { // 1. Do we have a boundary face to inflate from @@ -745,35 +893,116 @@ void Foam::addPatchCellLayer::calcSidePatch { label faceI = meshFaces[k]; - if (faceI != myFaceI) + if (faceI != myFaceI && !mesh.isInternalFace(faceI)) { - if (mesh.isInternalFace(faceI)) + if (patches.whichPatch(faceI) == edgePatchID[edgeI]) { - inflateEdgeI[edgeI] = meshEdgeI; - } - else - { - if (patches.whichPatch(faceI) == sidePatchID[edgeI]) - { - setFaceProps - ( - mesh, - faceI, - - sidePatchID[edgeI], - sideZoneID[edgeI], - sideFlip[edgeI] - ); - inflateFaceI[edgeI] = faceI; - inflateEdgeI[edgeI] = -1; + setFaceProps + ( + mesh, + pp, + edgeI, + faceI, - break; - } + edgePatchID[edgeI], + edgeZoneID[edgeI], + edgeFlip[edgeI], + inflateFaceID[edgeI] + ); + break; } } } } } + + + + // Sync all data: + // - edgePatchID : might be local in case of processor patch. Do not + // sync for now + // - inflateFaceID: local. Do not sync + // - edgeZoneID : global. sync. + // - edgeFlip : global. sync. + + if (Pstream::parRun()) + { + const globalMeshData& gd = mesh.globalData(); + const indirectPrimitivePatch& cpp = gd.coupledPatch(); + + labelList patchEdges; + labelList coupledEdges; + PackedBoolList sameEdgeOrientation; + PatchTools::matchEdges + ( + pp, + cpp, + patchEdges, + coupledEdges, + sameEdgeOrientation + ); + + // Convert data on pp edges to data on coupled patch + labelList cppEdgeZoneID(cpp.nEdges(), -1); + boolList cppEdgeFlip(cpp.nEdges(), false); + forAll(coupledEdges, i) + { + label cppEdgeI = coupledEdges[i]; + label ppEdgeI = patchEdges[i]; + + cppEdgeZoneID[cppEdgeI] = edgeZoneID[ppEdgeI]; + if (sameEdgeOrientation[i]) + { + cppEdgeFlip[cppEdgeI] = edgeFlip[ppEdgeI]; + } + else + { + cppEdgeFlip[cppEdgeI] = !edgeFlip[ppEdgeI]; + } + } + + // Sync + const globalIndexAndTransform& git = gd.globalTransforms(); + const mapDistribute& edgeMap = gd.globalEdgeSlavesMap(); + + globalMeshData::syncData + ( + cppEdgeZoneID, + gd.globalEdgeSlaves(), + gd.globalEdgeTransformedSlaves(), + edgeMap, + git, + maxEqOp<label>(), + dummyTransform() + ); + globalMeshData::syncData + ( + cppEdgeFlip, + gd.globalEdgeSlaves(), + gd.globalEdgeTransformedSlaves(), + edgeMap, + git, + andEqOp<bool>(), + dummyTransform() + ); + + // Convert data on coupled edges to pp edges + forAll(coupledEdges, i) + { + label cppEdgeI = coupledEdges[i]; + label ppEdgeI = patchEdges[i]; + + edgeZoneID[ppEdgeI] = cppEdgeZoneID[cppEdgeI]; + if (sameEdgeOrientation[i]) + { + edgeFlip[ppEdgeI] = cppEdgeFlip[cppEdgeI]; + } + else + { + edgeFlip[ppEdgeI] = !cppEdgeFlip[cppEdgeI]; + } + } + } } @@ -783,7 +1012,12 @@ void Foam::addPatchCellLayer::setRefinement const labelListList& globalEdgeFaces, const scalarField& expansionRatio, const indirectPrimitivePatch& pp, - const labelList& sidePatchID, + + const labelList& edgePatchID, + const labelList& edgeZoneID, + const boolList& edgeFlip, + const labelList& inflateFaceID, + const labelList& exposedPatchID, const labelList& nFaceLayers, const labelList& nPointLayers, @@ -865,7 +1099,10 @@ void Foam::addPatchCellLayer::setRefinement << e.line(pp.localPoints()) << " which is non-manifold (has " << globalEdgeFaces[edgeI].size() - << " faces using it)" + << " local or coupled faces using it)" + << " of which " + << pp.edgeFaces()[edgeI].size() + << " local" << abort(FatalError); } } @@ -1741,13 +1978,25 @@ void Foam::addPatchCellLayer::setRefinement ef ); + // Because we walk in order of patch face and in order + // of face edges so face orientation will be opposite + // that of the patch edge + bool zoneFlip = false; + if (edgeZoneID[startEdgeI] != -1) + { + zoneFlip = !edgeFlip[startEdgeI]; + } + addSideFace ( pp, addedCells, newFace, // vertices of new face - sidePatchID[startEdgeI],// -1 or patch for face + edgePatchID[startEdgeI],// -1 or patch for face + edgeZoneID[startEdgeI], + zoneFlip, + inflateFaceID[startEdgeI], patchFaceI, nbrFaceI, diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H index 158af5e1d8ec77574720b2b300dddc0175f0cfb0..5413ea77fd671a680dce94b6af6232b5c63ba42b 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/addPatchCellLayer.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -220,6 +220,9 @@ class addPatchCellLayer const face& newFace, const label newPatchID, + const label newZoneI, + const bool newFlip, + const label inflateFaceI, const label ownFaceI, const label nbrFaceI, @@ -243,6 +246,39 @@ class addPatchCellLayer bool& ); + //- Extract properties from mesh face in pp edge ordering + static void setFaceProps + ( + const polyMesh& mesh, + const indirectPrimitivePatch& pp, + const label ppEdgeI, + const label faceI, + + label& patchI, + label& zoneI, + bool& zoneFlip, + label& inflateFaceI + ); + + //- Find internal or boundary face to get extrude properties + // from. zoneFlip consistent with ppEdge ordering + static void findZoneFace + ( + const bool useInternalFaces, + const bool useBoundaryFaces, + + const polyMesh& mesh, + const indirectPrimitivePatch& pp, + const label ppEdgeI, + const UIndirectList<label>& excludeFaces, + const labelList& meshFaces, + + label& inflateFaceI, + label& patchI, + label& zoneI, + bool& zoneFlip + ); + //- Disallow default bitwise copy construct addPatchCellLayer(const addPatchCellLayer&); @@ -303,22 +339,37 @@ public: const indirectPrimitivePatch& pp ); - //- Boundary edges get extruded into boundary faces. Determine patch - // for these faces. This might be a to-be-created processor patch - // (patchI >= mesh.boundaryMesh().size()) in which case the - // nbrProcToPatch, patchToNbrProc give the correspondence. nPatches - // is the new number of patches. - static void calcSidePatch + //- Determine extrude information per patch edge: + // - zoneID, zoneFlip : + // picks one of the faces that connects to + // the edge. For boundary edge only looks + // at boundary faces. For internal edge it looks at internal + // faces only (zoneFromAnyFace = false) or at any face + // (zoneFromAnyFace = true). zoneFlip is consistent with + // ordering of pp edge. + // Face selected gets stored in inflateFaceID + // - patchID : + // get patch from any boundary face connected to the + // edge. The patch might be a to-be-created processor patch + // (patchI >= mesh.boundaryMesh().size()) in which case the + // nbrProcToPatch, patchToNbrProc give the correspondence. + // nPatches is the new number of patches. + static void calcExtrudeInfo ( + const bool zoneFromAnyFace, + const polyMesh&, const globalIndex& globalFaces, const labelListList& globalEdgeFaces, const indirectPrimitivePatch& pp, - labelList& sidePatchID, + labelList& edgePatchID, // if extruding a patch edge label& nPatches, Map<label>& nbrProcToPatch, - Map<label>& patchToNbrProc + Map<label>& patchToNbrProc, + labelList& edgeZoneID, + boolList& edgeFlip, + labelList& inflateFaceID ); //- Play commands into polyTopoChange to create layers on top @@ -347,7 +398,12 @@ public: const labelListList& globalEdgeFaces, const scalarField& expansionRatio, const indirectPrimitivePatch& pp, + const labelList& sidePatchID, + const labelList& sideZoneID, + const boolList& sideFlip, + const labelList& inflateFaceID, + const labelList& exposedPatchID, const labelList& nFaceLayers, const labelList& nPointLayers, @@ -374,7 +430,12 @@ public: globalEdgeFaces, scalarField(pp.nPoints(), 1.0), // expansion ration pp, + sidePatchID, + labelList(pp.nEdges(), -1), // zoneID + boolList(pp.nEdges(), false), // zoneFlip + labelList(pp.nEdges(), -1), // inflateFaceID + labelList(0), labelList(pp.size(), nLayers), // nFaceLayers labelList(pp.nPoints(), nLayers), // nPointLayers diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.C b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.C index 263afe869fe46d96e28591fe3c74f6ade9d44bdc..38dd6908e4086c675d91997864f1b7167332be0c 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.C +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -795,7 +795,7 @@ Foam::label Foam::hexRef8::findLevel // Gets cell level such that the face has four points <= level. -Foam::label Foam::hexRef8::getAnchorLevel(const label faceI) const +Foam::label Foam::hexRef8::faceLevel(const label faceI) const { const face& f = mesh_.faces()[faceI]; @@ -3519,7 +3519,7 @@ Foam::labelListList Foam::hexRef8::setRefinement for (label faceI = 0; faceI < mesh_.nFaces(); faceI++) { - faceAnchorLevel[faceI] = getAnchorLevel(faceI); + faceAnchorLevel[faceI] = faceLevel(faceI); } // -1 : no need to split face diff --git a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.H b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.H index 58dfc8559bdf4314f228557ecbaec0c3bf5773ef..883681c789f95b42b2547be20771c4d77bf730de 100644 --- a/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.H +++ b/src/dynamicMesh/polyTopoChange/polyTopoChange/hexRef8.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -411,7 +411,7 @@ public: // Refinement //- Gets level such that the face has four points <= level. - label getAnchorLevel(const label faceI) const; + label faceLevel(const label faceI) const; //- Given valid mesh and current cell level and proposed // cells to refine calculate any clashes (due to 2:1) and return diff --git a/src/mesh/autoMesh/Make/files b/src/mesh/autoMesh/Make/files index 2bc9cd80a17ee6186e46e895250e16ba14cfcf35..081d70afbd52185b77011cfa13f68f8f07fea44e 100644 --- a/src/mesh/autoMesh/Make/files +++ b/src/mesh/autoMesh/Make/files @@ -2,7 +2,6 @@ autoHexMesh = autoHexMesh autoHexMeshDriver = $(autoHexMesh)/autoHexMeshDriver $(autoHexMeshDriver)/autoLayerDriver.C -/* $(autoHexMeshDriver)/autoLayerDriverShrink.C */ $(autoHexMeshDriver)/autoSnapDriver.C $(autoHexMeshDriver)/autoSnapDriverFeature.C $(autoHexMeshDriver)/autoRefineDriver.C @@ -10,7 +9,6 @@ $(autoHexMeshDriver)/autoRefineDriver.C $(autoHexMeshDriver)/layerParameters/layerParameters.C $(autoHexMeshDriver)/refinementParameters/refinementParameters.C $(autoHexMeshDriver)/snapParameters/snapParameters.C -$(autoHexMeshDriver)/pointData/pointData.C $(autoHexMesh)/meshRefinement/meshRefinementBaffles.C $(autoHexMesh)/meshRefinement/meshRefinement.C @@ -30,7 +28,10 @@ meshMover = $(autoHexMesh)/externalDisplacementMeshMover $(meshMover)/displacementMeshMoverMotionSolver.C $(meshMover)/externalDisplacementMeshMover.C $(meshMover)/medialAxisMeshMover.C +$(meshMover)/displacementMotionSolverMeshMover.C +/* $(meshMover)/pointSmoothingMeshMover.C */ $(meshMover)/zeroFixedValue/zeroFixedValuePointPatchFields.C +$(meshMover)/fieldSmoother/fieldSmoother.C LIB = $(FOAM_LIBBIN)/libautoMesh diff --git a/src/mesh/autoMesh/Make/options b/src/mesh/autoMesh/Make/options index d37a26a9dd460742d9203623e4ddf6f69a4c99de..88577d6c825c3afabd897d9744e2fd8a43f8b621 100644 --- a/src/mesh/autoMesh/Make/options +++ b/src/mesh/autoMesh/Make/options @@ -18,4 +18,5 @@ LIB_LIBS = \ -ledgeMesh \ -lsurfMesh \ -ltriSurface \ + -lfvMotionSolvers \ -ldistributed diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C index 4999a16d3ca2e9cccaa80578038ef16ff72c7e0f..a7e30cb36cd0b2064fabd98d9ce01995765858e1 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -55,9 +55,7 @@ Description #include "cyclicSlipPointPatchFields.H" #include "fixedValueFvPatchFields.H" #include "localPointRegion.H" - #include "externalDisplacementMeshMover.H" -#include "medialAxisMeshMover.H" #include "scalarIOField.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -973,34 +971,42 @@ void Foam::autoLayerDriver::determineSidePatches const labelListList& edgeGlobalFaces, const indirectPrimitivePatch& pp, - labelList& sidePatchID + labelList& edgePatchID, + labelList& edgeZoneID, + boolList& edgeFlip, + labelList& inflateFaceID ) { // Sometimes edges-to-be-extruded are on more than 2 processors. // Work out which 2 hold the faces to be extruded and thus which procpatch - // the side-face should be in. As an additional complication this might + // the edge-face should be in. As an additional complication this might // mean that 2 procesors that were only edge-connected now suddenly need // to become face-connected i.e. have a processor patch between them. fvMesh& mesh = meshRefiner_.mesh(); - // Determine sidePatchID. Any additional processor boundary gets added to + // Determine edgePatchID. Any additional processor boundary gets added to // patchToNbrProc,nbrProcToPatch and nPatches gets set to the new number // of patches. label nPatches; Map<label> nbrProcToPatch; Map<label> patchToNbrProc; - addPatchCellLayer::calcSidePatch + addPatchCellLayer::calcExtrudeInfo ( + true, // zoneFromAnyFace + mesh, globalFaces, edgeGlobalFaces, pp, - sidePatchID, + edgePatchID, nPatches, nbrProcToPatch, - patchToNbrProc + patchToNbrProc, + edgeZoneID, + edgeFlip, + inflateFaceID ); label nOldPatches = mesh.boundaryMesh().size(); @@ -1012,7 +1018,7 @@ void Foam::autoLayerDriver::determineSidePatches if (nAdded > 0) { // We might not add patches in same order as in patchToNbrProc - // so prepare to renumber sidePatchID + // so prepare to renumber edgePatchID Map<label> wantedToAddedPatch; for (label patchI = nOldPatches; patchI < nPatches; patchI++) @@ -1046,14 +1052,14 @@ void Foam::autoLayerDriver::determineSidePatches wantedToAddedPatch.insert(patchI, procPatchI); } - // Renumber sidePatchID - forAll(sidePatchID, i) + // Renumber edgePatchID + forAll(edgePatchID, i) { - label patchI = sidePatchID[i]; + label patchI = edgePatchID[i]; Map<label>::const_iterator fnd = wantedToAddedPatch.find(patchI); if (fnd != wantedToAddedPatch.end()) { - sidePatchID[i] = fnd(); + edgePatchID[i] = fnd(); } } @@ -2427,6 +2433,67 @@ Foam::label Foam::autoLayerDriver::countExtrusion } +Foam::List<Foam::labelPair> Foam::autoLayerDriver::getBafflesOnAddedMesh +( + const polyMesh& mesh, + const labelList& newToOldFaces, + const List<labelPair>& baffles +) +{ + // The problem is that the baffle faces are now inside the + // mesh (addPatchCellLayer modifies original boundary faces and + // adds new ones. So 2 pass: + // - find the boundary face for all faces originating from baffle + // - use the boundary face for the new baffles + + Map<label> baffleSet(4*baffles.size()); + forAll(baffles, baffleI) + { + baffleSet.insert(baffles[baffleI][0], baffleI); + baffleSet.insert(baffles[baffleI][1], baffleI); + } + + + List<labelPair> newBaffles(baffles.size(), labelPair(-1, -1)); + for + ( + label faceI = mesh.nInternalFaces(); + faceI < mesh.nFaces(); + faceI++ + ) + { + label oldFaceI = newToOldFaces[faceI]; + + Map<label>::const_iterator faceFnd = baffleSet.find(oldFaceI); + if (faceFnd != baffleSet.end()) + { + label baffleI = faceFnd(); + labelPair& p = newBaffles[baffleI]; + if (p[0] == -1) + { + p[0] = faceI; + } + else if (p[1] == -1) + { + p[1] = faceI; + } + else + { + FatalErrorIn("addLayers(..)") + << "Problem:" << faceI << " at:" + << mesh.faceCentres()[faceI] + << " is on same baffle as " << p[0] + << " at:" << mesh.faceCentres()[p[0]] + << " and " << p[1] + << " at:" << mesh.faceCentres()[p[1]] + << exit(FatalError); + } + } + } + return newBaffles; +} + + // Collect layer faces and layer cells into mesh fields for ease of handling void Foam::autoLayerDriver::getLayerCellsFaces ( @@ -2457,6 +2524,7 @@ void Foam::autoLayerDriver::getLayerCellsFaces if (layer.size()) { + // Leave out original internal face forAll(added, i) { cellNLayers[added[i]] = layer.size()-1; @@ -2577,70 +2645,84 @@ void Foam::autoLayerDriver::printLayerData } -bool Foam::autoLayerDriver::writeLayerData +bool Foam::autoLayerDriver::writeLayerSets ( const fvMesh& mesh, - const labelList& patchIDs, const labelList& cellNLayers, - const scalarField& faceWantedThickness, const scalarField& faceRealThickness ) const { bool allOk = true; - - if (meshRefinement::writeLevel() & meshRefinement::WRITELAYERSETS) { + label nAdded = 0; + forAll(cellNLayers, cellI) { - label nAdded = 0; - forAll(cellNLayers, cellI) + if (cellNLayers[cellI] > 0) { - if (cellNLayers[cellI] > 0) - { - nAdded++; - } + nAdded++; } - cellSet addedCellSet(mesh, "addedCells", nAdded); - forAll(cellNLayers, cellI) + } + cellSet addedCellSet(mesh, "addedCells", nAdded); + forAll(cellNLayers, cellI) + { + if (cellNLayers[cellI] > 0) { - if (cellNLayers[cellI] > 0) - { - addedCellSet.insert(cellI); - } + addedCellSet.insert(cellI); } - addedCellSet.instance() = meshRefiner_.timeName(); - Info<< "Writing " - << returnReduce(addedCellSet.size(), sumOp<label>()) - << " added cells to cellSet " - << addedCellSet.name() << endl; - bool ok = addedCellSet.write(); - allOk = allOk & ok; } + addedCellSet.instance() = meshRefiner_.timeName(); + Info<< "Writing " + << returnReduce(addedCellSet.size(), sumOp<label>()) + << " added cells to cellSet " + << addedCellSet.name() << endl; + bool ok = addedCellSet.write(); + allOk = allOk && ok; + } + { + label nAdded = 0; + for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) { - label nAdded = 0; - for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) + if (faceRealThickness[faceI] > 0) { - if (faceRealThickness[faceI] > 0) - { - nAdded++; - } + nAdded++; } + } - faceSet layerFacesSet(mesh, "layerFaces", nAdded); - for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) + faceSet layerFacesSet(mesh, "layerFaces", nAdded); + for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) + { + if (faceRealThickness[faceI] > 0) { - if (faceRealThickness[faceI] > 0) - { - layerFacesSet.insert(faceI); - } + layerFacesSet.insert(faceI); } - layerFacesSet.instance() = meshRefiner_.timeName(); - Info<< "Writing " - << returnReduce(layerFacesSet.size(), sumOp<label>()) - << " faces inside added layer to faceSet " - << layerFacesSet.name() << endl; - bool ok = layerFacesSet.write(); - allOk = allOk & ok; } + layerFacesSet.instance() = meshRefiner_.timeName(); + Info<< "Writing " + << returnReduce(layerFacesSet.size(), sumOp<label>()) + << " faces inside added layer to faceSet " + << layerFacesSet.name() << endl; + bool ok = layerFacesSet.write(); + allOk = allOk && ok; + } + return allOk; +} + + +bool Foam::autoLayerDriver::writeLayerData +( + const fvMesh& mesh, + const labelList& patchIDs, + const labelList& cellNLayers, + const scalarField& faceWantedThickness, + const scalarField& faceRealThickness +) const +{ + bool allOk = true; + + if (meshRefinement::writeLevel() & meshRefinement::WRITELAYERSETS) + { + bool ok = writeLayerSets(mesh, cellNLayers, faceRealThickness); + allOk = allOk && ok; } if (meshRefinement::writeLevel() & meshRefinement::WRITELAYERFIELDS) @@ -2663,6 +2745,10 @@ bool Foam::autoLayerDriver::writeLayerData dimensionedScalar("zero", dimless, 0), fixedValueFvPatchScalarField::typeName ); + forAll(fld, cellI) + { + fld[cellI] = cellNLayers[cellI]; + } const polyBoundaryMesh& pbm = mesh.boundaryMesh(); forAll(patchIDs, i) { @@ -2679,7 +2765,7 @@ bool Foam::autoLayerDriver::writeLayerData Info<< indent << fld.name() << " : actual number of layers" << endl; bool ok = fld.write(); - allOk = allOk & ok; + allOk = allOk && ok; } { volScalarField fld @@ -2709,7 +2795,7 @@ bool Foam::autoLayerDriver::writeLayerData Info<< indent << fld.name() << " : overall layer thickness" << endl; bool ok = fld.write(); - allOk = allOk & ok; + allOk = allOk && ok; } { volScalarField fld @@ -2757,23 +2843,11 @@ bool Foam::autoLayerDriver::writeLayerData << " : overall layer thickness (fraction" << " of desired thickness)" << endl; bool ok = fld.write(); - allOk = allOk & ok; + allOk = allOk && ok; } Info<< decrIndent<< endl; } - //if (meshRefinement::outputLevel() & meshRefinement::OUTPUTLAYERINFO) - { - printLayerData - ( - mesh, - patchIDs, - cellNLayers, - faceWantedThickness, - faceRealThickness - ); - } - return allOk; } @@ -2803,7 +2877,7 @@ void Foam::autoLayerDriver::mergePatchFacesUndo { // Clip to 30 degrees. Not helpful! //scalar planarAngle = min(30.0, layerParams.featureAngle()); - scalar planarAngle = layerParams.featureAngle(); + scalar planarAngle = layerParams.mergePatchFacesAngle(); scalar minCos = Foam::cos(degToRad(planarAngle)); scalar concaveCos = Foam::cos(degToRad(layerParams.concaveAngle())); @@ -2814,8 +2888,7 @@ void Foam::autoLayerDriver::mergePatchFacesUndo << " - which are on the same patch" << nl << " - which make an angle < " << planarAngle << " degrees" - << nl - << " (cos:" << minCos << ')' << nl + << " (cos:" << minCos << ')' << nl << " - as long as the resulting face doesn't become concave" << " by more than " << layerParams.concaveAngle() << " degrees" << nl @@ -2859,34 +2932,322 @@ void Foam::autoLayerDriver::addLayers { fvMesh& mesh = meshRefiner_.mesh(); + + // 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; - meshRefiner_.createZoneBaffles - ( - globalToMasterPatch_, - globalToSlavePatch_, - 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.valid()) + { + 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); + } + } + } + + - if (debug&meshRefinement::MESH) + // 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()); { - const_cast<Time&>(mesh.time())++; - Info<< "Writing baffled mesh to time " - << meshRefiner_.timeName() << endl; - meshRefiner_.write + 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 pointToDuplicate; + + { + // Check outside of baffles for non-manifoldness + PackedBoolList duplicatePoint(mesh.nPoints()); + { + // 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. + vectorField patchDisp(pp().nPoints(), vector::one); + labelList patchNLayers(pp().nPoints(), 0); + 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 + + patchDisp, + patchNLayers, + extrudeStatus, + nIdealTotAddedCells + ); + // Make sure displacement is equal on both sides of coupled patches. + syncPatchDisplacement + ( + pp, + scalarField(patchDisp.size(), 0.0), //minThickness, + patchDisp, + patchNLayers, + extrudeStatus + ); + + forAll(extrudeStatus, patchPointI) + { + if (extrudeStatus[patchPointI] != NOEXTRUDE) + { + duplicatePoint[pp().meshPoints()[patchPointI]] = 1; + } + } + } + + + // Duplicate points only if all points agree + syncTools::syncPointList ( - meshRefinement::debugType(debug), - meshRefinement::writeType + mesh, + duplicatePoint, + andEqOp<unsigned int>(), // combine op + 0u // null value + ); + label n = duplicatePoint.count(); + labelList candidatePoints(n); + n = 0; + forAll(duplicatePoint, pointI) + { + if (duplicatePoint[pointI]) + { + candidatePoints[n++] = pointI; + } + } + + // 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()); + + 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 && !layerIDs.found(mpI) && !layerIDs.found(spI)) + { + nonDupZones.append(zoneI); + } + } + nonDupBaffles = meshRefinement::subsetBaffles ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() + mesh, + nonDupZones, + localPointRegion::findDuplicateFacePairs(mesh) + ); + } + + + const localPointRegion regionSide(mesh, nonDupBaffles, candidatePoints); + + autoPtr<mapPolyMesh> map = meshRefiner_.dupNonManifoldPoints + ( + regionSide ); + + if (map.valid()) + { + // Store point duplication + pointToDuplicate.setSize(mesh.nPoints(), -1); + + const labelList& pointMap = map().pointMap(); + const labelList& reversePointMap = map().reversePointMap(); + + forAll(pointMap, pointI) + { + label oldPointI = pointMap[pointI]; + label newMasterPointI = reversePointMap[oldPointI]; + + if (newMasterPointI != pointI) + { + // Found slave. Mark both master and slave + pointToDuplicate[pointI] = newMasterPointI; + pointToDuplicate[newMasterPointI] = newMasterPointI; + } + } + + // 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); + } + } + + + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing point-duplicate mesh to time " + << meshRefiner_.timeName() << endl; + + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + + 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]]; + + if (newMasterI != pointI) + { + str.write(linePointRef(p[pointI], p[newMasterI])); + } + } + } + } } + // Add layers to patches + // ~~~~~~~~~~~~~~~~~~~~~ + + // Now we have + // - mesh with optional baffles and duplicated points for faceZones + // where layers are to be added + // - pointToDuplicate : correspondence for duplicated points + // - baffles : list of pairs of faces + + autoPtr<indirectPrimitivePatch> pp ( meshRefinement::makePatch @@ -2916,14 +3277,20 @@ void Foam::autoLayerDriver::addLayers // Determine patches for extruded boundary edges. Calculates if any // additional processor patches need to be constructed. - labelList sidePatchID; + labelList edgePatchID; + labelList edgeZoneID; + boolList edgeFlip; + labelList inflateFaceID; determineSidePatches ( globalFaces, edgeGlobalFaces, pp, - sidePatchID + edgePatchID, + edgeZoneID, + edgeFlip, + inflateFaceID ); @@ -2945,12 +3312,12 @@ void Foam::autoLayerDriver::addLayers { - // Get number of layer per point from number of layers per patch - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Get number of layers per point from number of layers per patch + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ setNumLayers ( - layerParams.numLayers(), // per patch the num layers + numLayers, // per patch the num layers patchIDs, // patches that are being moved pp, // indirectpatch for all faces moving @@ -3077,41 +3444,6 @@ void Foam::autoLayerDriver::addLayers - // Overall displacement field - pointVectorField displacement - ( - makeLayerDisplacementField - ( - pointMesh::New(mesh), - layerParams.numLayers() - ) - ); - - // Allocate run-time selectable mesh mover - autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr; - { - // 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()); - - // Take over patchDisp as boundary conditions on displacement - // pointVectorField - medialAxisMoverPtr = externalDisplacementMeshMover::New - ( - layerParams.meshShrinker(), - combinedDict, - baffles, - displacement - ); - } - - - // Saved old points - pointField oldPoints(mesh.points()); - // Current set of topology changes. (changing mesh clears out // polyTopoChange) polyTopoChange savedMeshMod(mesh.boundaryMesh().size()); @@ -3127,422 +3459,752 @@ void Foam::autoLayerDriver::addLayers } - for (label iteration = 0; iteration < layerParams.nLayerIter(); iteration++) { - Info<< nl - << "Layer addition iteration " << iteration << nl - << "--------------------------" << endl; - - - // Unset the extrusion at the pp. - const dictionary& meshQualityDict = + // Overall displacement field + pointVectorField displacement ( - iteration < layerParams.nRelaxedIter() - ? motionDict - : motionDict.subDict("relaxed") + makeLayerDisplacementField + ( + pointMesh::New(mesh), + numLayers + ) ); - if (iteration >= layerParams.nRelaxedIter()) + // Allocate run-time selectable mesh mover + autoPtr<externalDisplacementMeshMover> medialAxisMoverPtr; { - Info<< "Switched to relaxed meshQuality constraints." << endl; - } - - + // 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()); - // Make sure displacement is equal on both sides of coupled patches. - syncPatchDisplacement - ( - pp, - minThickness, - patchDisp, - patchNLayers, - extrudeStatus - ); + const List<labelPair> internalBaffles + ( + meshRefinement::subsetBaffles + ( + mesh, + internalFaceZones, + localPointRegion::findDuplicateFacePairs(mesh) + ) + ); - // Displacement acc. to pointnormals - getPatchDisplacement - ( - pp, - thickness, - minThickness, - patchDisp, - patchNLayers, - extrudeStatus - ); + // Take over patchDisp as boundary conditions on displacement + // pointVectorField + medialAxisMoverPtr = externalDisplacementMeshMover::New + ( + layerParams.meshShrinker(), + combinedDict, + internalBaffles, + displacement + ); + } - // Shrink mesh by displacement value first. - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Saved old points + const pointField oldPoints(mesh.points()); + + for + ( + label iteration = 0; + iteration < layerParams.nLayerIter(); + iteration++ + ) { - pointField oldPatchPos(pp().localPoints()); + Info<< nl + << "Layer addition iteration " << iteration << nl + << "--------------------------" << endl; + - // Take over patchDisp into pointDisplacement field and - // adjust both for multi-patch constraints - motionSmootherAlgo::setDisplacement + // Unset the extrusion at the pp. + const dictionary& meshQualityDict = ( - patchIDs, - pp, - patchDisp, - displacement + iteration < layerParams.nRelaxedIter() + ? motionDict + : motionDict.subDict("relaxed") ); + 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()); - labelList checkFaces(identity(mesh.nFaces())); - medialAxisMoverPtr().move + // Make sure displacement is equal on both sides of coupled patches. + syncPatchDisplacement ( - combinedDict, - nAllowableErrors, - checkFaces + pp, + minThickness, + patchDisp, + patchNLayers, + extrudeStatus ); - pp().movePoints(mesh.points()); + // Displacement acc. to pointnormals + getPatchDisplacement + ( + pp, + thickness, + minThickness, + patchDisp, + patchNLayers, + extrudeStatus + ); - // Update patchDisp (since not all might have been honoured) - patchDisp = oldPatchPos - pp().localPoints(); - } + // Shrink mesh by displacement value first. + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // 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 - ); + { + const pointField oldPatchPos(pp().localPoints()); + // We have patchDisp which is the outwards pointing + // extrusion distance. Convert into an inwards pointing + // shrink distance + patchDisp = -patchDisp; - // Dump to .obj file for debugging. - if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) - { - dumpDisplacement + // Take over patchDisp into pointDisplacement field and + // adjust both for multi-patch constraints + motionSmootherAlgo::setDisplacement + ( + patchIDs, + pp, + patchDisp, + displacement + ); + + + // 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()); + + labelList checkFaces(identity(mesh.nFaces())); + medialAxisMoverPtr().move + ( + combinedDict, + nAllowableErrors, + checkFaces + ); + + 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 ( - mesh.time().path()/"layer_" + meshRefiner_.timeName(), - pp(), + globalFaces, + edgeGlobalFaces, + pp, + minThickness, + dummySet, patchDisp, + patchNLayers, extrudeStatus ); - const_cast<Time&>(mesh.time())++; - Info<< "Writing shrunk mesh to time " - << meshRefiner_.timeName() << endl; - // See comment in autoSnapDriver why we should not remove meshPhi - // using mesh.clearOut(). + // Dump to .obj file for debugging. + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) + { + dumpDisplacement + ( + mesh.time().path()/"layer_" + meshRefiner_.timeName(), + pp(), + patchDisp, + extrudeStatus + ); + + const_cast<Time&>(mesh.time())++; + Info<< "Writing shrunk mesh to time " + << meshRefiner_.timeName() << endl; - meshRefiner_.write + // See comment in autoSnapDriver why we should not remove + // meshPhi using mesh.clearOut(). + + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } + + + // Mesh topo change engine. Insert current mesh. + polyTopoChange meshMod(mesh); + + // Grow layer of cells on to patch. Handles zero sized displacement. + addPatchCellLayer addLayer(mesh); + + // Determine per point/per face number of layers to extrude. Also + // handles the slow termination of layers when going switching + // layers + + labelList nPatchPointLayers(pp().nPoints(), -1); + labelList nPatchFaceLayers(pp().size(), -1); + setupLayerInfoTruncation ( - meshRefinement::debugType(debug), - meshRefinement::writeType + pp, + patchNLayers, + extrudeStatus, + layerParams.nBufferCellsNoExtrude(), + nPatchPointLayers, + nPatchFaceLayers + ); + + // Calculate displacement for final layer for addPatchLayer. + // (layer of cells next to the original mesh) + vectorField finalDisp(patchNLayers.size(), vector::zero); + + forAll(nPatchPointLayers, i) + { + scalar ratio = layerParams.finalLayerThicknessRatio ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/meshRefiner_.timeName() + nPatchPointLayers[i], + expansionRatio[i] + ); + finalDisp[i] = ratio*patchDisp[i]; + } + + + const scalarField invExpansionRatio(1.0/expansionRatio); + + // Add topo regardless of whether extrudeStatus is extruderemove. + // Not add layer if patchDisp is zero. + addLayer.setRefinement + ( + globalFaces, + edgeGlobalFaces, + + invExpansionRatio, + pp(), + + edgePatchID, // boundary patch for extruded boundary edges + edgeZoneID, // zone for extruded edges + edgeFlip, + inflateFaceID, + + + 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) + { + const_cast<Time&>(mesh.time())++; + } - // Mesh topo change engine - polyTopoChange meshMod(mesh); + // Store mesh changes for if mesh is correct. + savedMeshMod = meshMod; - // Grow layer of cells on to patch. Handles zero sized displacement. - addPatchCellLayer addLayer(mesh); - // Determine per point/per face number of layers to extrude. Also - // handles the slow termination of layers when going switching layers + // With the stored topo changes we create a new mesh so we can + // undo if neccesary. - labelList nPatchPointLayers(pp().nPoints(), -1); - labelList nPatchFaceLayers(pp().size(), -1); - setupLayerInfoTruncation - ( - pp, - patchNLayers, - extrudeStatus, - layerParams.nBufferCellsNoExtrude(), - nPatchPointLayers, - nPatchFaceLayers - ); + autoPtr<fvMesh> newMeshPtr; + autoPtr<mapPolyMesh> map = meshMod.makeMesh + ( + newMeshPtr, + IOobject + ( + //mesh.name()+"_layer", + mesh.name(), + static_cast<polyMesh&>(mesh).instance(), + mesh.time(), // register with runTime + IOobject::NO_READ, + static_cast<polyMesh&>(mesh).writeOpt() + ), // io params from original mesh but new name + mesh, // original mesh + true // parallel sync + ); + fvMesh& newMesh = newMeshPtr(); - // Calculate displacement for final layer for addPatchLayer. - // (layer of cells next to the original mesh) - vectorField finalDisp(patchNLayers.size(), vector::zero); + //?neccesary? Update fields + newMesh.updateMesh(map); - forAll(nPatchPointLayers, i) - { - scalar ratio = layerParams.finalLayerThicknessRatio + newMesh.setInstance(meshRefiner_.timeName()); + + // Update numbering on addLayer: + // - cell/point labels to be newMesh. + // - patchFaces to remain in oldMesh order. + addLayer.updateMesh ( - nPatchPointLayers[i], - expansionRatio[i] + map, + identity(pp().size()), + identity(pp().nPoints()) ); - finalDisp[i] = ratio*patchDisp[i]; - } + // Collect layer faces and cells for outside loop. + getLayerCellsFaces + ( + newMesh, + addLayer, + avgPointData(pp, mag(patchDisp))(), // current thickness - const scalarField invExpansionRatio(1.0/expansionRatio); + cellNLayers, + faceRealThickness + ); - // Add topo regardless of whether extrudeStatus is extruderemove. - // Not add layer if patchDisp is zero. - addLayer.setRefinement - ( - globalFaces, - edgeGlobalFaces, - invExpansionRatio, - pp(), - sidePatchID, // boundary patch for extruded boundary edges - 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 - ); + // Count number of added cells + label nAddedCells = 0; + forAll(cellNLayers, cellI) + { + if (cellNLayers[cellI] > 0) + { + nAddedCells++; + } + } - if (debug) - { - const_cast<Time&>(mesh.time())++; - } - // 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 neccesary. + //- 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 + ( + label faceI = newMesh.nInternalFaces(); + faceI < newMesh.nFaces(); + faceI++ + ) + { + label newMeshFaceI = map().faceMap()[faceI]; + if (newMeshFaceI != -1) + { + meshToNewMesh[newMeshFaceI] = faceI; + } + } - autoPtr<fvMesh> newMeshPtr; - autoPtr<mapPolyMesh> map = meshMod.makeMesh - ( - newMeshPtr, - IOobject + 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); + + internalBaffles = meshRefinement::subsetBaffles + ( + newMesh, + internalFaceZones, + newMeshBaffles + ); + + Info<< "Detected " + << returnReduce(internalBaffles.size(), sumOp<label>()) + << " baffles across faceZones of type internal" << nl + << endl; + } + + label nTotChanged = checkAndUnmark ( - //mesh.name()+"_layer", - mesh.name(), - static_cast<polyMesh&>(mesh).instance(), - mesh.time(), // register with runTime - IOobject::NO_READ, - static_cast<polyMesh&>(mesh).writeOpt() - ), // io params from original mesh but new name - mesh, // original mesh - true // parallel sync - ); - fvMesh& newMesh = newMeshPtr(); + addLayer, + meshQualityDict, + layerParams.additionalReporting(), + internalBaffles, + pp(), + newMesh, - //?neccesary? Update fields - newMesh.updateMesh(map); + patchDisp, + patchNLayers, + extrudeStatus + ); - newMesh.setInstance(meshRefiner_.timeName()); + 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; + } - // 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()) - ); + // Reset mesh points and start again + mesh.movePoints(oldPoints); + pp().movePoints(mesh.points()); + medialAxisMoverPtr().movePoints(mesh.points()); - // Update numbering of baffles - List<labelPair> newMeshBaffles(baffles.size()); - forAll(baffles, i) + // Grow out region of non-extrusion + for (label i = 0; i < layerParams.nGrow(); i++) + { + growNoExtrusion + ( + pp, + patchDisp, + patchNLayers, + extrudeStatus + ); + } + + 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. + + { + // Apply the stored topo changes to the current mesh. + autoPtr<mapPolyMesh> map = savedMeshMod.changeMesh(mesh, false); + + // 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 { - const labelPair& p = baffles[i]; - newMeshBaffles[i][0] = map().reverseFaceMap()[p[0]]; - newMeshBaffles[i][1] = map().reverseFaceMap()[p[1]]; + // Delete mesh volumes. + mesh.clearOut(); } - // Collect layer faces and cells for outside loop. - getLayerCellsFaces + // Reset the instance for if in overwrite mode + mesh.setInstance(meshRefiner_.timeName()); + + meshRefiner_.updateMesh(map, labelList(0)); + + // Update numbering of faceWantedThickness + meshRefinement::updateList ( - newMesh, - addLayer, - avgPointData(pp, mag(patchDisp))(), // current thickness + 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 ); - // Count number of added cells - label nAddedCells = 0; - forAll(cellNLayers, cellI) + // Dump for debugging + if (debug&meshRefinement::MESH || debug&meshRefinement::LAYERINFO) { - if (cellNLayers[cellI] > 0) - { - nAddedCells++; - } + 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() + ); } - if (debug&meshRefinement::MESH) - { - Info<< "Writing layer mesh to time " << meshRefiner_.timeName() - << endl; - newMesh.write(); - cellSet addedCellSet(newMesh, "addedCells", nAddedCells); - forAll(cellNLayers, cellI) + // Update numbering of baffles + { + // From old mesh face to corresponding newMesh boundary face. + // (we cannot just use faceMap or reverseFaceMap here since + // multiple faces originate from the old face) + labelList oldMeshToNewMesh(map().nOldFaces(), -1); + for + ( + label faceI = mesh.nInternalFaces(); + faceI < mesh.nFaces(); + faceI++ + ) { - if (cellNLayers[cellI] > 0) + label oldFaceI = map().faceMap()[faceI]; + + if (oldFaceI != -1) { - addedCellSet.insert(cellI); + oldMeshToNewMesh[oldFaceI] = faceI; } } - addedCellSet.instance() = meshRefiner_.timeName(); - Info<< "Writing " - << returnReduce(addedCellSet.size(), sumOp<label>()) - << " added cells to cellSet " << addedCellSet.name() << endl; - addedCellSet.write(); - faceSet layerFacesSet(newMesh, "layerFaces", newMesh.nFaces()/100); - for (label faceI = 0; faceI < newMesh.nInternalFaces(); faceI++) + label newI = 0; + forAll(baffles, i) { - if (faceRealThickness[faceI] > 0) + const labelPair& p = baffles[i]; + + labelPair newB(oldMeshToNewMesh[p[0]], oldMeshToNewMesh[p[1]]); + if (newB[0] != -1 && newB[1] != -1) { - layerFacesSet.insert(faceI); + baffles[newI++] = newB; } } - layerFacesSet.instance() = meshRefiner_.timeName(); - Info<< "Writing " - << returnReduce(layerFacesSet.size(), sumOp<label>()) - << " faces inside added layer to faceSet " - << layerFacesSet.name() << endl; - layerFacesSet.write(); + baffles.setSize(newI); } - label nTotChanged = checkAndUnmark - ( - addLayer, - meshQualityDict, - layerParams.additionalReporting(), - newMeshBaffles, - 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) + // Update numbering of pointToDuplicate + if (returnReduce(pointToDuplicate.size(), sumOp<label>())) { - break; - } + // The problem is that pointToDuplicate is valid for the old + // boundary points which are now internal. We need to find the + // corresponding new boundary point. - // Reset mesh points and start again - mesh.movePoints(oldPoints); - pp().movePoints(mesh.points()); - // Grow out region of non-extrusion - for (label i = 0; i < layerParams.nGrow(); i++) - { - growNoExtrusion + List<labelPair> mergePointBaffles ( - pp, - patchDisp, - patchNLayers, - extrudeStatus + meshRefinement::subsetBaffles + ( + mesh, + internalOrBaffleFaceZones, + baffles + ) ); - } + Info<< "Detected " + << returnReduce(mergePointBaffles.size(), sumOp<label>()) + << " baffles to merge points across" << nl << endl; - Info<< endl; - } + label nPointPairs = 0; + forAll(pointToDuplicate, oldPointI) + { + label otherOldPointI = pointToDuplicate[oldPointI]; + if (otherOldPointI != -1) + { + nPointPairs++; + } + } - // 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. + const labelList& pointMap = map().pointMap(); + + // 1. Construct map from old (possibly) internal point to + // new boundary point + Map<label> oldPointToBoundaryPoint(2*nPointPairs); + + forAll(mergePointBaffles, i) + { + const labelPair& baffle = mergePointBaffles[i]; + forAll(baffle, j) + { + const face& f = mesh.faces()[baffle[j]]; + forAll(f, fp) + { + label pointI = f[fp]; + label oldPointI = pointMap[pointI]; + if (pointToDuplicate[oldPointI] != -1) + { + oldPointToBoundaryPoint.insert(oldPointI, pointI); + } + } + } + } - // Apply the stored topo changes to the current mesh. - autoPtr<mapPolyMesh> map = savedMeshMod.changeMesh(mesh, false); - // Hack to remove meshPhi - mapped incorrectly. TBD. - mesh.clearOut(); + // 2. Pick up old internal point - // Update fields - mesh.updateMesh(map); + labelList oldPointToDuplicate(pointToDuplicate.xfer()); + pointToDuplicate.setSize(mesh.nPoints(), -1); - // Move mesh (since morphing does not do this) - if (map().hasMotionPoints()) - { - mesh.movePoints(map().preMotionPoints()); + forAll(mergePointBaffles, i) + { + const labelPair& baffle = mergePointBaffles[i]; + forAll(baffle, j) + { + const face& f = mesh.faces()[baffle[j]]; + forAll(f, fp) + { + label pointI = f[fp]; + label oldPointI = pointMap[pointI]; + label oldDupI = oldPointToDuplicate[oldPointI]; + if (oldDupI != -1) + { + label newPointI = oldPointToBoundaryPoint[oldDupI]; + pointToDuplicate[pointI] = newPointI; + } + } + } + } + + + // Check + forAll(pointToDuplicate, pointI) + { + label dupI = pointToDuplicate[pointI]; + if (dupI != -1) + { + const point& pt = mesh.points()[pointI]; + const point& dupPt = mesh.points()[dupI]; + if (mag(pt-dupPt) > meshRefiner_.mergeDistance()) + { + WarningIn("autoLayerDriver::addLayers(..)") + << "Trying to merge points " + << pointI << " at:" << pt + << "and " << dupI << " at:" << dupPt + << " distance " << mag(pt-dupPt) + << endl; + } + } + } + } } - else + + // Count duplicate points + label nPointPairs = 0; + forAll(pointToDuplicate, pointI) { - // Delete mesh volumes. - mesh.clearOut(); + label otherPointI = pointToDuplicate[pointI]; + if (otherPointI != -1) + { + nPointPairs++; + } } + reduce(nPointPairs, sumOp<label>()); + if (nPointPairs > 0) + { + // Merge any duplicated points + Info<< "Merging " << nPointPairs << " duplicated points ..." << endl; - // Reset the instance for if in overwrite mode - mesh.setInstance(meshRefiner_.timeName()); + 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(pointToDuplicate, pointI) + { + label otherPointI = pointToDuplicate[pointI]; + if (otherPointI != -1) + { + const point& pt = mesh.points()[pointI]; + const point& otherPt = mesh.points()[otherPointI]; + str.write(linePointRef(pt, otherPt)); + } + } + } - meshRefiner_.updateMesh(map, labelList(0)); - // Update numbering of faceWantedThickness - meshRefinement::updateList(map().faceMap(), scalar(0), faceWantedThickness); + autoPtr<mapPolyMesh> map = meshRefiner_.mergePoints(pointToDuplicate); + if (map.valid()) + { + inplaceReorder(map().reverseCellMap(), cellNLayers); - // Update numbering on baffles - forAll(baffles, i) - { - labelPair& p = baffles[i]; - p[0] = map().reverseFaceMap()[p[0]]; - p[1] = map().reverseFaceMap()[p[1]]; - } + const labelList& reverseFaceMap = map().reverseFaceMap(); + inplaceReorder(reverseFaceMap, faceWantedThickness); + inplaceReorder(reverseFaceMap, faceRealThickness); + Info<< "Merged points in = " + << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; + } + } - label nBaffles = returnReduce(baffles.size(), sumOp<label>()); - if (nBaffles > 0) + if (mesh.faceZones().size() > 0) { // Merge any baffles - Info<< "Converting " << nBaffles - << " baffles back into zoned faces ..." + Info<< "Converting baffles back into zoned faces ..." << endl; - autoPtr<mapPolyMesh> map = meshRefiner_.mergeBaffles(baffles); + autoPtr<mapPolyMesh> map = meshRefiner_.mergeZoneBaffles + ( + true, // internal zones + false // baffle zones + ); + if (map.valid()) + { + inplaceReorder(map().reverseCellMap(), cellNLayers); + + const labelList& faceMap = map().faceMap(); - inplaceReorder(map().reverseCellMap(), cellNLayers); - inplaceReorder(map().reverseFaceMap(), faceWantedThickness); - inplaceReorder(map().reverseFaceMap(), faceRealThickness); + // Make sure to keep the max since on two patches only one has + // layers. + scalarField newFaceRealThickness(mesh.nFaces(), 0.0); + scalarField newFaceWantedThickness(mesh.nFaces(), 0.0); + 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); + } Info<< "Converted baffles in = " << meshRefiner_.mesh().time().cpuTimeIncrement() << " s\n" << nl << endl; } - // Do final balancing // ~~~~~~~~~~~~~~~~~~ @@ -3594,6 +4256,7 @@ void Foam::autoLayerDriver::doLayers const dictionary& shrinkDict, const dictionary& motionDict, const layerParameters& layerParams, + const bool mergePatchFaces, const bool preBalance, decompositionMethod& decomposer, fvMeshDistribute& distributor @@ -3606,10 +4269,15 @@ void Foam::autoLayerDriver::doLayers << "----------------------------------" << nl << endl; + Info<< "Using mesh parameters " << motionDict << nl << endl; // Merge coplanar boundary faces - mergePatchFacesUndo(layerParams, motionDict); + if (mergePatchFaces) + { + mergePatchFacesUndo(layerParams, motionDict); + } + // Per patch the number of layers (-1 or 0 if no layer) const labelList& numLayers = layerParams.numLayers(); @@ -3636,6 +4304,26 @@ void Foam::autoLayerDriver::doLayers } } } + + // Add contributions from faceZones that get layers + const faceZoneMesh& fZones = mesh.faceZones(); + forAll(fZones, zoneI) + { + label mpI, spI; + surfaceZonesInfo::faceZoneType fzType; + meshRefiner_.getFaceZoneInfo(fZones[zoneI].name(), mpI, spI, fzType); + + if (numLayers[mpI] > 0) + { + nFacesWithLayers += fZones[zoneI].size(); + } + if (numLayers[spI] > 0) + { + nFacesWithLayers += fZones[zoneI].size(); + } + } + + patchIDs.shrink(); if (returnReduce(nFacesWithLayers, sumOp<label>()) == 0) @@ -3683,12 +4371,45 @@ void Foam::autoLayerDriver::doLayers } } + // Add contributions from faceZones that get layers + const faceZoneMesh& fZones = mesh.faceZones(); + forAll(fZones, zoneI) + { + const faceZone& fZone = fZones[zoneI]; + const word& fzName = fZone.name(); + + label mpI, spI; + surfaceZonesInfo::faceZoneType fzType; + meshRefiner_.getFaceZoneInfo(fzName, mpI, spI, fzType); + + if (numLayers[mpI] > 0) + { + // Get the owner side for unflipped faces, neighbour side + // for flipped ones + const labelList& cellIDs = fZone.slaveCells(); + forAll(cellIDs, i) + { + cellWeights[cellIDs[i]] += numLayers[mpI]; + } + } + if (numLayers[spI] > 0) + { + const labelList& cellIDs = fZone.masterCells(); + forAll(cellIDs, i) + { + cellWeights[cellIDs[i]] += numLayers[mpI]; + } + } + } + + + // Balance mesh (and meshRefinement). Restrict faceZones to // be on internal faces only since they will be converted into // baffles. autoPtr<mapDistributePolyMesh> map = meshRefiner_.balance ( - true, //false, // keepZoneFaces + true, // keepZoneFaces false, cellWeights, decomposer, diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H index 7aa5050a2291dc9916279c86432c7c63549d2841..55db5ed9d04a9f747e4db4a0c6c0ba0d1764ebc5 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoLayerDriver.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -47,7 +47,6 @@ class removePoints; class pointSet; class motionSmoother; class addPatchCellLayer; -class pointData; class faceSet; class layerParameters; @@ -108,7 +107,6 @@ private: const labelList globalToSlavePatch_; - // Private Member Functions // Layers @@ -231,14 +229,17 @@ private: List<extrudeMode>& extrudeStatus ) const; - //- See what patches boundaryedges should be extruded into + //- See what zones and patches edges should be extruded into void determineSidePatches ( const globalIndex& globalFaces, const labelListList& edgeGlobalFaces, const indirectPrimitivePatch& pp, - labelList& sidePatchID + labelList& edgePatchID, + labelList& edgeZoneID, + boolList& edgeFlip, + labelList& inflateFaceID ); //- Calculate pointwise wanted and minimum thickness. @@ -370,6 +371,14 @@ private: const List<extrudeMode>& extrudeStatus ); + //- After adding to mesh get the new baffles + static List<labelPair> getBafflesOnAddedMesh + ( + const polyMesh& mesh, + const labelList& newToOldFaces, + const List<labelPair>& baffles + ); + //- Collect layer faces and layer cells into bools // for ease of handling static void getLayerCellsFaces @@ -393,6 +402,15 @@ private: ) const; //- Write cellSet,faceSet for layers + bool writeLayerSets + ( + const fvMesh& mesh, + const labelList& cellNLayers, + const scalarField& faceRealThickness + ) const; + + //- Write volFields,cellSet,faceSet for layers depending + // on write level bool writeLayerData ( const fvMesh& mesh, @@ -464,13 +482,6 @@ private: pointVectorField& normals ) const; - bool isMaxEdge - ( - const List<pointData>&, - const label edgeI, - const scalar minCos - ) const; - //- Stop layer growth where mesh wraps around edge with a // large feature angle void handleFeatureAngleLayerTerminations @@ -595,6 +606,7 @@ public: const dictionary& shrinkDict, const dictionary& motionDict, const layerParameters& layerParams, + const bool mergePatchFaces, // merging patch faces const bool preBalance, // balance before adding? decompositionMethod& decomposer, fvMeshDistribute& distributor diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C index 8c63239f397c6ece0c5baedcb2c9bf433c303337..be908b76bbc9588fb48a586579613ac1539a62bd 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -37,6 +37,8 @@ License #include "unitConversion.H" #include "snapParameters.H" #include "localPointRegion.H" +#include "IOmanip.H" +#include "labelVector.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -94,7 +96,7 @@ Foam::label Foam::autoRefineDriver::featureEdgeRefine ( meshRefiner_.refineCandidates ( - refineParams.keepPoints(), + refineParams.locationsInMesh(), refineParams.curvature(), refineParams.planarAngle(), @@ -207,7 +209,7 @@ Foam::label Foam::autoRefineDriver::surfaceOnlyRefine ( meshRefiner_.refineCandidates ( - refineParams.keepPoints(), + refineParams.locationsInMesh(), refineParams.curvature(), refineParams.planarAngle(), @@ -341,7 +343,7 @@ Foam::label Foam::autoRefineDriver::gapOnlyRefine ( meshRefiner_.refineCandidates ( - refineParams.keepPoints(), + refineParams.locationsInMesh(), refineParams.curvature(), refineParams.planarAngle(), @@ -669,6 +671,348 @@ Foam::label Foam::autoRefineDriver::danglingCellRefine } +// Detect cells with opposing intersected faces of differing refinement +// level and refine them. +Foam::label Foam::autoRefineDriver::refinementInterfaceRefine +( + const refinementParameters& refineParams, + const label maxIter +) +{ + const fvMesh& mesh = meshRefiner_.mesh(); + + label iter = 0; + + if (refineParams.interfaceRefine()) + { + for (;iter < maxIter; iter++) + { + Info<< nl + << "Refinement transition refinement iteration " << iter << nl + << "--------------------------------------------" << nl + << endl; + + const labelList& surfaceIndex = meshRefiner_.surfaceIndex(); + const hexRef8& cutter = meshRefiner_.meshCutter(); + const vectorField& fA = mesh.faceAreas(); + const labelList& faceOwner = mesh.faceOwner(); + + + // Determine cells to refine + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + + const cellList& cells = mesh.cells(); + + labelList candidateCells; + { + // Pass1: pick up cells with differing face level + + cellSet transitionCells + ( + mesh, + "transitionCells", + cells.size()/100 + ); + + forAll(cells, cellI) + { + const cell& cFaces = cells[cellI]; + label cLevel = cutter.cellLevel()[cellI]; + + forAll(cFaces, cFaceI) + { + label faceI = cFaces[cFaceI]; + + if (surfaceIndex[faceI] != -1) + { + label fLevel = cutter.faceLevel(faceI); + if (fLevel != cLevel) + { + transitionCells.insert(cellI); + } + } + } + } + + + cellSet candidateCellSet + ( + mesh, + "candidateCells", + cells.size()/1000 + ); + + // Pass2: check for oppositeness + + //forAllConstIter(cellSet, transitionCells, iter) + //{ + // label cellI = iter.key(); + // const cell& cFaces = cells[cellI]; + // const point& cc = cellCentres[cellI]; + // const scalar rCVol = pow(cellVolumes[cellI], -5.0/3.0); + // + // // Determine principal axes of cell + // symmTensor R(symmTensor::zero); + // + // forAll(cFaces, i) + // { + // label faceI = cFaces[i]; + // + // const point& fc = faceCentres[faceI]; + // + // // Calculate face-pyramid volume + // scalar pyrVol = 1.0/3.0 * fA[faceI] & (fc-cc); + // + // if (faceOwner[faceI] != cellI) + // { + // pyrVol = -pyrVol; + // } + // + // // Calculate face-pyramid centre + // vector pc = (3.0/4.0)*fc + (1.0/4.0)*cc; + // + // R += pyrVol*sqr(pc-cc)*rCVol; + // } + // + // //- MEJ: Problem: truncation errors cause complex evs + // vector lambdas(eigenValues(R)); + // const tensor axes(eigenVectors(R, lambdas)); + // + // + // // Check if this cell has + // // - opposing sides intersected + // // - which are of different refinement level + // // - plus the inbetween face + // + // labelVector plusFaceLevel(labelVector(-1, -1, -1)); + // labelVector minFaceLevel(labelVector(-1, -1, -1)); + // + // forAll(cFaces, cFaceI) + // { + // label faceI = cFaces[cFaceI]; + // + // if (surfaceIndex[faceI] != -1) + // { + // label fLevel = cutter.faceLevel(faceI); + // + // // Get outwards pointing normal + // vector n = fA[faceI]/mag(fA[faceI]); + // if (faceOwner[faceI] != cellI) + // { + // n = -n; + // } + // + // // What is major direction and sign + // direction cmpt = vector::X; + // scalar maxComp = (n&axes.x()); + // + // scalar yComp = (n&axes.y()); + // scalar zComp = (n&axes.z()); + // + // if (mag(yComp) > mag(maxComp)) + // { + // maxComp = yComp; + // cmpt = vector::Y; + // } + // + // if (mag(zComp) > mag(maxComp)) + // { + // maxComp = zComp; + // cmpt = vector::Z; + // } + // + // if (maxComp > 0) + // { + // plusFaceLevel[cmpt] = max + // ( + // plusFaceLevel[cmpt], + // fLevel + // ); + // } + // else + // { + // minFaceLevel[cmpt] = max + // ( + // minFaceLevel[cmpt], + // fLevel + // ); + // } + // } + // } + // + // // Check if we picked up any opposite differing level + // for (direction dir = 0; dir < vector::nComponents; dir++) + // { + // if + // ( + // plusFaceLevel[dir] != -1 + // && minFaceLevel[dir] != -1 + // && plusFaceLevel[dir] != minFaceLevel[dir] + // ) + // { + // candidateCellSet.insert(cellI); + // } + // } + //} + + const scalar oppositeCos = Foam::cos(Foam::degToRad(135)); + + forAllConstIter(cellSet, transitionCells, iter) + { + label cellI = iter.key(); + const cell& cFaces = cells[cellI]; + label cLevel = cutter.cellLevel()[cellI]; + + // Detect opposite intersection + bool foundOpposite = false; + + forAll(cFaces, cFaceI) + { + label faceI = cFaces[cFaceI]; + + if + ( + surfaceIndex[faceI] != -1 + && cutter.faceLevel(faceI) > cLevel + ) + { + // Get outwards pointing normal + vector n = fA[faceI]/mag(fA[faceI]); + if (faceOwner[faceI] != cellI) + { + n = -n; + } + + // Check for any opposite intersection + forAll(cFaces, cFaceI2) + { + label face2I = cFaces[cFaceI2]; + + if + ( + face2I != faceI + && surfaceIndex[face2I] != -1 + ) + { + // Get outwards pointing normal + vector n2 = fA[face2I]/mag(fA[face2I]); + if (faceOwner[face2I] != cellI) + { + n2 = -n2; + } + + + if ((n&n2) < oppositeCos) + { + foundOpposite = true; + break; + } + } + } + + if (foundOpposite) + { + break; + } + } + } + + + if (foundOpposite) + { + candidateCellSet.insert(cellI); + } + } + + if (debug&meshRefinement::MESH) + { + Pout<< "Dumping " << candidateCellSet.size() + << " cells to cellSet candidateCellSet." << endl; + candidateCellSet.instance() = meshRefiner_.timeName(); + candidateCellSet.write(); + } + candidateCells = candidateCellSet.toc(); + } + + + + labelList cellsToRefine + ( + meshRefiner_.meshCutter().consistentRefinement + ( + candidateCells, + true + ) + ); + Info<< "Determined cells to refine in = " + << mesh.time().cpuTimeIncrement() << " s" << endl; + + + label nCellsToRefine = cellsToRefine.size(); + reduce(nCellsToRefine, sumOp<label>()); + + Info<< "Selected for refinement : " << nCellsToRefine + << " cells (out of " << mesh.globalData().nTotalCells() + << ')' << endl; + + // Stop when no cells to refine. After a few iterations check if too + // few cells + if + ( + nCellsToRefine == 0 + || ( + iter >= 1 + && nCellsToRefine <= refineParams.minRefineCells() + ) + ) + { + Info<< "Stopping refining since too few cells selected." + << nl << endl; + break; + } + + + if (debug) + { + const_cast<Time&>(mesh.time())++; + } + + + if + ( + returnReduce + ( + (mesh.nCells() >= refineParams.maxLocalCells()), + orOp<bool>() + ) + ) + { + meshRefiner_.balanceAndRefine + ( + "interface cell refinement iteration " + name(iter), + decomposer_, + distributor_, + cellsToRefine, + refineParams.maxLoadUnbalance() + ); + } + else + { + meshRefiner_.refineAndBalance + ( + "interface cell refinement iteration " + name(iter), + decomposer_, + distributor_, + cellsToRefine, + refineParams.maxLoadUnbalance() + ); + } + } + } + return iter; +} + + void Foam::autoRefineDriver::removeInsideCells ( const refinementParameters& refineParams, @@ -692,13 +1036,15 @@ void Foam::autoRefineDriver::removeInsideCells nBufferLayers, // nBufferLayers globalToMasterPatch_, globalToSlavePatch_, - refineParams.keepPoints()[0] + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh() ); if (debug&meshRefinement::MESH) { Pout<< "Writing subsetted mesh to time " - << meshRefiner_.timeName() << '.' << endl; + << meshRefiner_.timeName() << endl; meshRefiner_.write ( meshRefinement::debugType(debug), @@ -753,7 +1099,7 @@ Foam::label Foam::autoRefineDriver::shellRefine ( meshRefiner_.refineCandidates ( - refineParams.keepPoints(), + refineParams.locationsInMesh(), refineParams.curvature(), refineParams.planarAngle(), @@ -913,22 +1259,40 @@ void Foam::autoRefineDriver::baffleAndSplitMesh false, // perpendicular edge connected cells scalarField(0), // per region perpendicular angle - // Free standing baffles - !handleSnapProblems, // merge free standing baffles? - refineParams.planarAngle(), - motionDict, const_cast<Time&>(mesh.time()), globalToMasterPatch_, globalToSlavePatch_, - refineParams.keepPoints()[0] + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh() ); + + + if (!handleSnapProblems) // merge free standing baffles? + { + meshRefiner_.mergeFreeStandingBaffles + ( + snapParams, + refineParams.useTopologicalSnapDetection(), + false, // perpendicular edge connected cells + scalarField(0), // per region perpendicular angle + refineParams.planarAngle(), + motionDict, + const_cast<Time&>(mesh.time()), + globalToMasterPatch_, + globalToSlavePatch_, + refineParams.locationsInMesh(), + refineParams.locationsOutsideMesh() + ); + } } void Foam::autoRefineDriver::zonify ( - const refinementParameters& refineParams + const refinementParameters& refineParams, + wordPairHashTable& zonesToFaceZone ) { // Mesh is at its finest. Do zoning @@ -940,7 +1304,11 @@ void Foam::autoRefineDriver::zonify const labelList namedSurfaces = surfaceZonesInfo::getNamedSurfaces(meshRefiner_.surfaces().surfZones()); - if (namedSurfaces.size()) + if + ( + namedSurfaces.size() + || refineParams.zonesInMesh().size() + ) { Info<< nl << "Introducing zones for interfaces" << nl @@ -956,14 +1324,16 @@ void Foam::autoRefineDriver::zonify meshRefiner_.zonify ( - refineParams.keepPoints()[0], - refineParams.allowFreeStandingZoneFaces() + refineParams.allowFreeStandingZoneFaces(), + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + zonesToFaceZone ); if (debug&meshRefinement::MESH) { Pout<< "Writing zoned mesh to time " - << meshRefiner_.timeName() << '.' << endl; + << meshRefiner_.timeName() << endl; meshRefiner_.write ( meshRefinement::debugType(debug), @@ -1015,15 +1385,29 @@ void Foam::autoRefineDriver::splitAndMergeBaffles handleSnapProblems, // remove perp edge connected cells perpAngle, // perp angle - // Free standing baffles - true, // merge free standing baffles? - refineParams.planarAngle(), // planar angle + motionDict, + const_cast<Time&>(mesh.time()), + globalToMasterPatch_, + globalToSlavePatch_, + refineParams.locationsInMesh(), + refineParams.zonesInMesh(), + refineParams.locationsOutsideMesh() + ); + // Merge free-standing baffles always + meshRefiner_.mergeFreeStandingBaffles + ( + snapParams, + refineParams.useTopologicalSnapDetection(), + handleSnapProblems, + perpAngle, + refineParams.planarAngle(), motionDict, const_cast<Time&>(mesh.time()), globalToMasterPatch_, globalToSlavePatch_, - refineParams.keepPoints()[0] + refineParams.locationsInMesh(), + refineParams.locationsOutsideMesh() ); if (debug) @@ -1048,7 +1432,7 @@ void Foam::autoRefineDriver::splitAndMergeBaffles // Actually merge baffles. Note: not exactly parallellized. Should // convert baffle faces into processor faces if they resulted // from them. - meshRefiner_.mergeBaffles(couples); + meshRefiner_.mergeBaffles(couples, Map<label>(0)); if (debug) { @@ -1061,7 +1445,8 @@ void Foam::autoRefineDriver::splitAndMergeBaffles ( globalToMasterPatch_, globalToSlavePatch_, - refineParams.keepPoints()[0] + refineParams.locationsInMesh(), + refineParams.locationsOutsideMesh() ); if (debug) @@ -1077,7 +1462,7 @@ void Foam::autoRefineDriver::splitAndMergeBaffles if (debug&meshRefinement::MESH) { Pout<< "Writing handleProblemCells mesh to time " - << meshRefiner_.timeName() << '.' << endl; + << meshRefiner_.timeName() << endl; meshRefiner_.write ( meshRefinement::debugType(debug), @@ -1092,8 +1477,83 @@ void Foam::autoRefineDriver::splitAndMergeBaffles } +void Foam::autoRefineDriver::addFaceZones +( + meshRefinement& meshRefiner, + const refinementParameters& refineParams, + const HashTable<Pair<word> >& faceZoneToPatches +) +{ + if (faceZoneToPatches.size()) + { + Info<< nl + << "Adding patches for face zones" << nl + << "-----------------------------" << nl + << endl; + + Info<< setf(ios_base::left) + << setw(6) << "Patch" + << setw(20) << "Type" + << setw(30) << "Name" + << setw(30) << "FaceZone" + << setw(10) << "FaceType" + << nl + << setw(6) << "-----" + << setw(20) << "----" + << setw(30) << "----" + << setw(30) << "--------" + << setw(10) << "--------" + << endl; + + const polyMesh& mesh = meshRefiner.mesh(); + + // Add patches for added inter-region faceZones + forAllConstIter(HashTable<Pair<word> >, faceZoneToPatches, iter) + { + const word& fzName = iter.key(); + const Pair<word>& patchNames = iter(); + + // Get any user-defined faceZone data + surfaceZonesInfo::faceZoneType fzType; + dictionary patchInfo = refineParams.getZoneInfo(fzName, fzType); + + const word& masterName = fzName; + //const word slaveName = fzName + "_slave"; + //const word slaveName = czNames.second()+"_to_"+czNames.first(); + const word& slaveName = patchNames.second(); + + label mpI = meshRefiner.addMeshedPatch(masterName, patchInfo); + + Info<< setf(ios_base::left) + << setw(6) << mpI + << setw(20) << mesh.boundaryMesh()[mpI].type() + << setw(30) << masterName + << setw(30) << fzName + << setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType] + << nl; + + + label slI = meshRefiner.addMeshedPatch(slaveName, patchInfo); + + Info<< setf(ios_base::left) + << setw(6) << slI + << setw(20) << mesh.boundaryMesh()[slI].type() + << setw(30) << slaveName + << setw(30) << fzName + << setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType] + << nl; + + meshRefiner.addFaceZone(fzName, masterName, slaveName, fzType); + } + + Info<< endl; + } +} + + void Foam::autoRefineDriver::mergePatchFaces ( + const bool geometricMerge, const refinementParameters& refineParams, const dictionary& motionDict ) @@ -1105,14 +1565,28 @@ void Foam::autoRefineDriver::mergePatchFaces const fvMesh& mesh = meshRefiner_.mesh(); - meshRefiner_.mergePatchFacesUndo - ( - Foam::cos(degToRad(45.0)), - Foam::cos(degToRad(45.0)), - meshRefiner_.meshedPatches(), - motionDict, - labelList(mesh.nFaces(), -1) - ); + if (geometricMerge) + { + meshRefiner_.mergePatchFacesUndo + ( + Foam::cos(degToRad(45.0)), + Foam::cos(degToRad(45.0)), + meshRefiner_.meshedPatches(), + motionDict, + labelList(mesh.nFaces(), -1) + ); + } + else + { + // Still merge refined boundary faces if all four are on same patch + meshRefiner_.mergePatchFaces + ( + Foam::cos(degToRad(45.0)), + Foam::cos(degToRad(45.0)), + 4, // only merge faces split into 4 + meshRefiner_.meshedPatches() + ); + } if (debug) { @@ -1134,6 +1608,7 @@ void Foam::autoRefineDriver::doRefine const refinementParameters& refineParams, const snapParameters& snapParams, const bool prepareForSnapping, + const bool doMergePatchFaces, const dictionary& motionDict ) { @@ -1145,7 +1620,7 @@ void Foam::autoRefineDriver::doRefine const fvMesh& mesh = meshRefiner_.mesh(); // Check that all the keep points are inside the mesh. - refineParams.findCells(mesh); + refineParams.findCells(true, mesh, refineParams.locationsInMesh()); // Refine around feature edges featureEdgeRefine @@ -1196,6 +1671,13 @@ void Foam::autoRefineDriver::doRefine 100 // maxIter ); + // Refine any cells with differing refinement level on either side + refinementInterfaceRefine + ( + refineParams, + 10 // maxIter + ); + // Introduce baffles at surface intersections. Remove sections unreachable // from keepPoint. baffleAndSplitMesh @@ -1206,8 +1688,31 @@ void Foam::autoRefineDriver::doRefine motionDict ); - // Mesh is at its finest. Do optional zoning. - zonify(refineParams); + // Mesh is at its finest. Do optional zoning (cellZones and faceZones) + wordPairHashTable zonesToFaceZone; + zonify(refineParams, zonesToFaceZone); + + // Create pairs of patches for faceZones + { + HashTable<Pair<word> > faceZoneToPatches(zonesToFaceZone.size()); + + // Note: zonesToFaceZone contains the same data on different + // processors but in different order. We could sort the + // contents but instead just loop in sortedToc order. + List<Pair<word> > czs(zonesToFaceZone.sortedToc()); + + forAll(czs, i) + { + const Pair<word>& czNames = czs[i]; + const word& fzName = zonesToFaceZone[czNames]; + + const word& masterName = fzName; + const word slaveName = czNames.second() + "_to_" + czNames.first(); + Pair<word> patches(masterName, slaveName); + faceZoneToPatches.insert(fzName, patches); + } + addFaceZones(meshRefiner_, refineParams, faceZoneToPatches); + } // Pull baffles apart splitAndMergeBaffles @@ -1221,7 +1726,7 @@ void Foam::autoRefineDriver::doRefine // Do something about cells with refined faces on the boundary if (prepareForSnapping) { - mergePatchFaces(refineParams, motionDict); + mergePatchFaces(doMergePatchFaces, refineParams, motionDict); } @@ -1232,28 +1737,17 @@ void Foam::autoRefineDriver::doRefine << "---------------------" << nl << endl; - //if (debug) - //{ - // const_cast<Time&>(mesh.time())++; - //} - // Do final balancing. Keep zoned faces on one processor since the // snap phase will convert them to baffles and this only works for // internal faces. meshRefiner_.balance ( - true, - false, - scalarField(mesh.nCells(), 1), // dummy weights + true, // keepZoneFaces + false, // keepBaffles + scalarField(mesh.nCells(), 1), // cellWeights decomposer_, distributor_ ); - - - if (debug) - { - meshRefiner_.checkZoneFaces(); - } } } diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H index a72f3c0796d291f8d16409cefdfd09bec479f9c0..11ed1d9454d0b5c7d6db927e0fa25c0d9fdb1ef4 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoRefineDriver.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -34,7 +34,8 @@ SourceFiles #ifndef autoRefineDriver_H #define autoRefineDriver_H -#include "treeBoundBox.H" +#include "wordPairHashTable.H" +#include "labelList.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -105,6 +106,13 @@ class autoRefineDriver const label maxIter ); + //- Refine cells with opposite faces with differing refinement level + label refinementInterfaceRefine + ( + const refinementParameters& refineParams, + const label maxIter + ); + //- Remove all cells within intersected region void removeInsideCells ( @@ -129,7 +137,11 @@ class autoRefineDriver ); //- Add zones - void zonify(const refinementParameters& refineParams); + void zonify + ( + const refinementParameters& refineParams, + wordPairHashTable& zonesToFaceZone + ); void splitAndMergeBaffles ( @@ -142,11 +154,11 @@ class autoRefineDriver //- Merge refined boundary faces (from exposing coarser cell) void mergePatchFaces ( + const bool geometricMerge, const refinementParameters& refineParams, const dictionary& motionDict ); - //- Disallow default bitwise copy construct autoRefineDriver(const autoRefineDriver&); @@ -182,8 +194,18 @@ public: const refinementParameters& refineParams, const snapParameters& snapParams, const bool prepareForSnapping, + const bool mergePatchFaces, const dictionary& motionDict ); + + //- Helper: add faceZones and patches + static void addFaceZones + ( + meshRefinement& meshRefiner, + const refinementParameters& refineParams, + const HashTable<Pair<word> >& faceZoneToPatches + ); + }; diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C index 41e6ca0dccc5807d7d3234bea387e84be0ba60d9..772c9530b5395e9c84656cc4733ab4f13a86ca3b 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -40,9 +40,11 @@ Description #include "mergePoints.H" #include "snapParameters.H" #include "refinementSurfaces.H" +#include "searchableSurfaces.H" #include "unitConversion.H" #include "localPointRegion.H" #include "PatchTools.H" +#include "refinementFeatures.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -118,8 +120,187 @@ Foam::label Foam::autoSnapDriver::getCollocatedPoints } +Foam::tmp<Foam::pointField> Foam::autoSnapDriver::smoothInternalDisplacement +( + const meshRefinement& meshRefiner, + const motionSmoother& meshMover +) +{ + const indirectPrimitivePatch& pp = meshMover.patch(); + const polyMesh& mesh = meshMover.mesh(); + + // Get neighbour refinement + const hexRef8& cutter = meshRefiner.meshCutter(); + const labelList& cellLevel = cutter.cellLevel(); + + + // Get the faces on the boundary + PackedBoolList isFront(mesh.nFaces()); + forAll(pp.addressing(), i) + { + isFront[pp.addressing()[i]] = true; + } + + // Walk out from the surface a bit. Poor man's FaceCellWave. + // Commented out for now - not sure if needed and if so how much + //for (label iter = 0; iter < 2; iter++) + //{ + // PackedBoolList newIsFront(mesh.nFaces()); + // + // forAll(isFront, faceI) + // { + // if (isFront[faceI]) + // { + // label own = mesh.faceOwner()[faceI]; + // const cell& ownFaces = mesh.cells()[own]; + // forAll(ownFaces, i) + // { + // newIsFront[ownFaces[i]] = true; + // } + // + // if (mesh.isInternalFace(faceI)) + // { + // label nei = mesh.faceNeighbour()[faceI]; + // const cell& neiFaces = mesh.cells()[nei]; + // forAll(neiFaces, i) + // { + // newIsFront[neiFaces[i]] = true; + // } + // } + // } + // } + // + // syncTools::syncFaceList + // ( + // mesh, + // newIsFront, + // orEqOp<unsigned int>() + // ); + // + // isFront = newIsFront; + //} + + // Mark all points on faces + // - not on the boundary + // - inbetween differing refinement levels + PackedBoolList isMovingPoint(mesh.nPoints()); + + label nInterface = 0; + + for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++) + { + label ownLevel = cellLevel[mesh.faceOwner()[faceI]]; + label neiLevel = cellLevel[mesh.faceNeighbour()[faceI]]; + + if (!isFront[faceI] && ownLevel != neiLevel) + { + const face& f = mesh.faces()[faceI]; + forAll(f, fp) + { + isMovingPoint[f[fp]] = true; + } + + nInterface++; + } + } + + labelList neiCellLevel; + syncTools::swapBoundaryCellList(mesh, cellLevel, neiCellLevel); + + for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++) + { + label ownLevel = cellLevel[mesh.faceOwner()[faceI]]; + label neiLevel = neiCellLevel[faceI-mesh.nInternalFaces()]; + + if (!isFront[faceI] && ownLevel != neiLevel) + { + const face& f = mesh.faces()[faceI]; + forAll(f, fp) + { + isMovingPoint[f[fp]] = true; + } + + nInterface++; + } + } + + if (debug) + { + reduce(nInterface, sumOp<label>()); + Info<< "Found " << nInterface << " faces out of " + << mesh.globalData().nTotalFaces() + << " inbetween refinement regions." << endl; + } + + // Make sure that points that are coupled to a moving point are marked + // as well + syncTools::syncPointList(mesh, isMovingPoint, maxEqOp<unsigned int>(), 0); + + // Unmark any point on the boundary. If we're doing zero iterations of + // face-cell wave we might have coupled points not being unmarked. + forAll(pp.meshPoints(), pointI) + { + isMovingPoint[pp.meshPoints()[pointI]] = false; + } + + // Make sure that points that are coupled to meshPoints but not on a patch + // are unmarked as well + syncTools::syncPointList(mesh, isMovingPoint, minEqOp<unsigned int>(), 1); + + + // Calculate average of connected cells + labelList nCells(mesh.nPoints(), 0); + pointField sumLocation(mesh.nPoints(), vector::zero); + + forAll(isMovingPoint, pointI) + { + if (isMovingPoint[pointI]) + { + const labelList& pCells = mesh.pointCells(pointI); + + forAll(pCells, i) + { + sumLocation[pointI] += mesh.cellCentres()[pCells[i]]; + nCells[pointI]++; + } + } + } + + // Sum + syncTools::syncPointList(mesh, nCells, plusEqOp<label>(), label(0)); + syncTools::syncPointList + ( + mesh, + sumLocation, + plusEqOp<point>(), + vector::zero + ); + + tmp<pointField> tdisplacement(new pointField(mesh.nPoints(), vector::zero)); + pointField& displacement = tdisplacement(); + + label nAdapted = 0; + + forAll(displacement, pointI) + { + if (nCells[pointI] > 0) + { + displacement[pointI] = + sumLocation[pointI]/nCells[pointI]-mesh.points()[pointI]; + nAdapted++; + } + } + + reduce(nAdapted, sumOp<label>()); + Info<< "Smoothing " << nAdapted << " points inbetween refinement regions." + << endl; + + return tdisplacement; +} + + // Calculate displacement as average of patch points. -Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement +Foam::tmp<Foam::pointField> Foam::autoSnapDriver::smoothPatchDisplacement ( const motionSmoother& meshMover, const List<labelPair>& baffles @@ -327,20 +508,9 @@ Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ labelList anyCell(mesh.nPoints(), -1); - forAll(mesh.faceNeighbour(), faceI) - { - label own = mesh.faceOwner()[faceI]; - const face& f = mesh.faces()[faceI]; - - forAll(f, fp) - { - anyCell[f[fp]] = own; - } - } - for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++) + forAll(mesh.faceOwner(), faceI) { label own = mesh.faceOwner()[faceI]; - const face& f = mesh.faces()[faceI]; forAll(f, fp) @@ -351,7 +521,8 @@ Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement // Displacement to calculate. - pointField patchDisp(meshPoints.size(), vector::zero); + tmp<pointField> tpatchDisp(new pointField(meshPoints.size(), vector::zero)); + pointField& patchDisp = tpatchDisp(); forAll(pointFaces, i) { @@ -414,7 +585,7 @@ Foam::pointField Foam::autoSnapDriver::smoothPatchDisplacement patchDisp[i] = newPos - currentPos; } - return patchDisp; + return tpatchDisp; } //XXXXXXX //Foam::tmp<Foam::pointField> Foam::autoSnapDriver::avg @@ -521,42 +692,6 @@ Foam::tmp<Foam::scalarField> Foam::autoSnapDriver::edgePatchDist edgeDist[edgeI] = Foam::sqrt(allEdgeInfo[edgeI].distSqr()); } - - //{ - // // For debugging: dump to file - // pointScalarField pointDist - // ( - // IOobject - // ( - // "pointDist", - // meshRefiner_.timeName(), - // mesh.DB(), - // IOobject::NO_READ, - // IOobject::AUTO_WRITE - // ), - // pMesh, - // dimensionedScalar("pointDist", dimless, 0.0) - // ); - // - // forAll(allEdgeInfo, edgeI) - // { - // scalar d = Foam::sqrt(allEdgeInfo[edgeI].distSqr()); - // - // const edge& e = mesh.edges()[edgeI]; - // - // pointDist[e[0]] += d; - // pointDist[e[1]] += d; - // } - // forAll(pointDist, pointI) - // { - // pointDist[pointI] /= mesh.pointEdges()[pointI].size(); - // } - // Info<< "Writing patch distance to " << pointDist.name() - // << " at time " << meshRefiner_.timeName() << endl; - // - // pointDist.write(); - //} - return tedgeDist; } @@ -648,35 +783,6 @@ Foam::autoSnapDriver::autoSnapDriver // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // -Foam::autoPtr<Foam::mapPolyMesh> Foam::autoSnapDriver::mergeZoneBaffles -( - const List<labelPair>& baffles -) -{ - labelList zonedSurfaces = - surfaceZonesInfo::getNamedSurfaces(meshRefiner_.surfaces().surfZones()); - - autoPtr<mapPolyMesh> map; - - // No need to sync; all processors will have all same zonedSurfaces. - label nBaffles = returnReduce(baffles.size(), sumOp<label>()); - if (zonedSurfaces.size() && nBaffles > 0) - { - // Merge any baffles - Info<< "Converting " << nBaffles << " baffles back into zoned faces ..." - << endl; - - map = meshRefiner_.mergeBaffles(baffles); - - Info<< "Converted baffles in = " - << meshRefiner_.mesh().time().cpuTimeIncrement() - << " s\n" << nl << endl; - } - - return map; -} - - Foam::scalarField Foam::autoSnapDriver::calcSnapDistance ( const fvMesh& mesh, @@ -730,7 +836,15 @@ void Foam::autoSnapDriver::preSmoothPatch labelList checkFaces; - Info<< "Smoothing patch points ..." << endl; + if (snapParams.nSmoothInternal() > 0) + { + Info<< "Smoothing patch and internal points ..." << endl; + } + else + { + Info<< "Smoothing patch points ..." << endl; + } + for ( label smoothIter = 0; @@ -745,15 +859,26 @@ void Foam::autoSnapDriver::preSmoothPatch checkFaces[faceI] = faceI; } + // If enabled smooth the internal points + if (snapParams.nSmoothInternal() > smoothIter) + { + // Override values on internal points on refinement interfaces + meshMover.pointDisplacement().internalField() = + smoothInternalDisplacement(meshRefiner, meshMover); + } + + // Smooth the patch points pointField patchDisp(smoothPatchDisplacement(meshMover, baffles)); //pointField patchDisp //( // smoothLambdaMuPatchDisplacement(meshMover, baffles) //); - // The current mesh is the starting mesh to smooth from. + // Take over patch displacement as boundary condition on + // pointDisplacement meshMover.setDisplacement(patchDisp); + // Start off from current mesh.points() meshMover.correct(); scalar oldErrorReduction = -1; @@ -1566,9 +1691,92 @@ void Foam::autoSnapDriver::detectNearSurfaces } +void Foam::autoSnapDriver::calcNearestSurface +( + const refinementSurfaces& surfaces, + + const labelList& surfacesToTest, + const labelListList& regionsToTest, + + const pointField& localPoints, + const labelList& zonePointIndices, + + scalarField& minSnapDist, + labelList& snapSurf, + vectorField& patchDisp, + + // Optional: nearest point, normal + pointField& nearestPoint, + vectorField& nearestNormal +) +{ + // Find nearest for points both on faceZone and pp. + List<pointIndexHit> hitInfo; + labelList hitSurface; + + if (nearestNormal.size() == localPoints.size()) + { + labelList hitRegion; + vectorField hitNormal; + surfaces.findNearestRegion + ( + surfacesToTest, + regionsToTest, + + pointField(localPoints, zonePointIndices), + sqr(scalarField(minSnapDist, zonePointIndices)), + + hitSurface, + hitInfo, + hitRegion, + hitNormal + ); + + forAll(hitInfo, i) + { + if (hitInfo[i].hit()) + { + label pointI = zonePointIndices[i]; + nearestPoint[pointI] = hitInfo[i].hitPoint(); + nearestNormal[pointI] = hitNormal[i]; + } + } + } + else + { + surfaces.findNearest + ( + surfacesToTest, + regionsToTest, + + pointField(localPoints, zonePointIndices), + sqr(scalarField(minSnapDist, zonePointIndices)), + + hitSurface, + hitInfo + ); + } + + forAll(hitInfo, i) + { + if (hitInfo[i].hit()) + { + label pointI = zonePointIndices[i]; + + patchDisp[pointI] = hitInfo[i].hitPoint() - localPoints[pointI]; + minSnapDist[pointI] = mag(patchDisp[pointI]); + snapSurf[pointI] = hitSurface[i]; + } + } +} + + Foam::vectorField Foam::autoSnapDriver::calcNearestSurface ( + const bool strictRegionSnap, const meshRefinement& meshRefiner, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, const scalarField& snapDist, const indirectPrimitivePatch& pp, pointField& nearestPoint, @@ -1577,6 +1785,23 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurface { Info<< "Calculating patchDisplacement as distance to nearest surface" << " point ..." << endl; + if (strictRegionSnap) + { + Info<< " non-zone points : attract to local region on surface only" + << nl + << " zone points : attract to local region on surface only" + << nl + << endl; + } + else + { + Info<< " non-zone points :" + << " attract to nearest of all non-zone surfaces" + << nl + << " zone points : attract to zone surface only" << nl + << endl; + } + const pointField& localPoints = pp.localPoints(); const refinementSurfaces& surfaces = meshRefiner.surfaces(); @@ -1587,26 +1812,109 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurface if (returnReduce(localPoints.size(), sumOp<label>()) > 0) { - // Current surface snapped to + // Current surface snapped to. Used to check whether points have been + // snapped at all labelList snapSurf(localPoints.size(), -1); - // Divide surfaces into zoned and unzoned - const labelList zonedSurfaces = - surfaceZonesInfo::getNamedSurfaces - ( - meshRefiner.surfaces().surfZones() - ); - const labelList unzonedSurfaces = - surfaceZonesInfo::getUnnamedSurfaces - ( - meshRefiner.surfaces().surfZones() - ); + // Current best snap distance (since point might be on multiple + // regions) + scalarField minSnapDist(snapDist); + + + if (strictRegionSnap) + { + // Attract patch points to same region only + + forAll(surfaces.surfaces(), surfI) + { + label geomI = surfaces.surfaces()[surfI]; + label nRegions = surfaces.geometry()[geomI].regions().size(); + + const labelList surfacesToTest(1, surfI); + + for (label regionI = 0; regionI < nRegions; regionI++) + { + label globalI = surfaces.globalRegion(surfI, regionI); + label masterPatchI = globalToMasterPatch[globalI]; + + // Get indices of points both on patch and on pp + labelList zonePointIndices + ( + getFacePoints + ( + pp, + mesh.boundaryMesh()[masterPatchI] + ) + ); + + calcNearestSurface + ( + surfaces, + + surfacesToTest, + labelListList(1, labelList(1, regionI)), //regionsToTest + + localPoints, + zonePointIndices, + + minSnapDist, + snapSurf, + patchDisp, + + // Optional: nearest point, normal + nearestPoint, + nearestNormal + ); + + if (globalToSlavePatch[globalI] != masterPatchI) + { + label slavePatchI = globalToSlavePatch[globalI]; + + // Get indices of points both on patch and on pp + labelList zonePointIndices + ( + getFacePoints + ( + pp, + mesh.boundaryMesh()[slavePatchI] + ) + ); + + calcNearestSurface + ( + surfaces, + surfacesToTest, + labelListList(1, labelList(1, regionI)), - // 1. All points to non-interface surfaces - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + localPoints, + zonePointIndices, + minSnapDist, + snapSurf, + patchDisp, + + // Optional: nearest point, normal + nearestPoint, + nearestNormal + ); + } + } + } + } + else { + // Divide surfaces into zoned and unzoned + const labelList unzonedSurfaces = + surfaceZonesInfo::getUnnamedSurfaces + ( + meshRefiner.surfaces().surfZones() + ); + + + // 1. All points to non-interface surfaces + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + List<pointIndexHit> hitInfo; labelList hitSurface; @@ -1657,100 +1965,67 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurface snapSurf[pointI] = hitSurface[pointI]; } } - } + const labelList zonedSurfaces = + surfaceZonesInfo::getNamedSurfaces + ( + meshRefiner.surfaces().surfZones() + ); - // 2. All points on zones to their respective surface - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Surfaces with zone information - const PtrList<surfaceZonesInfo>& surfZones = surfaces.surfZones(); + // 2. All points on zones to their respective surface + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Current best snap distance - scalarField minSnapDist(snapDist); + // Surfaces with zone information + const PtrList<surfaceZonesInfo>& surfZones = surfaces.surfZones(); - forAll(zonedSurfaces, i) - { - label zoneSurfI = zonedSurfaces[i]; + forAll(zonedSurfaces, i) + { + label surfI = zonedSurfaces[i]; - const word& faceZoneName = surfZones[zoneSurfI].faceZoneName(); + const word& faceZoneName = surfZones[surfI].faceZoneName(); - const labelList surfacesToTest(1, zoneSurfI); + const labelList surfacesToTest(1, surfI); - // Get indices of points both on faceZone and on pp. - labelList zonePointIndices - ( - getZoneSurfacePoints - ( - mesh, - pp, - faceZoneName - ) - ); + label geomI = surfaces.surfaces()[surfI]; + label nRegions = surfaces.geometry()[geomI].regions().size(); - // Find nearest for points both on faceZone and pp. - List<pointIndexHit> hitInfo; - labelList hitSurface; - if (nearestNormal.size() == localPoints.size()) - { - labelList hitRegion; - vectorField hitNormal; - surfaces.findNearestRegion + // Get indices of points both on faceZone and on pp. + labelList zonePointIndices ( - surfacesToTest, - pointField(localPoints, zonePointIndices), - sqr(scalarField(minSnapDist, zonePointIndices)), - hitSurface, - hitInfo, - hitRegion, - hitNormal + getZoneSurfacePoints + ( + mesh, + pp, + faceZoneName + ) ); - forAll(hitInfo, i) - { - if (hitInfo[i].hit()) - { - label pointI = zonePointIndices[i]; - nearestPoint[pointI] = hitInfo[i].hitPoint(); - nearestNormal[pointI] = hitNormal[i]; - } - } - } - else - { - surfaces.findNearest + + calcNearestSurface ( - surfacesToTest, - pointField(localPoints, zonePointIndices), - sqr(scalarField(minSnapDist, zonePointIndices)), - hitSurface, - hitInfo - ); - } + surfaces, - forAll(hitInfo, i) - { - label pointI = zonePointIndices[i]; + surfacesToTest, + labelListList(1, identity(nRegions)), - if (hitInfo[i].hit()) - { - patchDisp[pointI] = - hitInfo[i].hitPoint() - - localPoints[pointI]; + localPoints, + zonePointIndices, - minSnapDist[pointI] = min - ( - minSnapDist[pointI], - mag(patchDisp[pointI]) - ); + minSnapDist, + snapSurf, + patchDisp, - snapSurf[pointI] = zoneSurfI; - } + // Optional: nearest point, normal + nearestPoint, + nearestNormal + ); } } + // Check if all points are being snapped forAll(snapSurf, pointI) { @@ -1788,7 +2063,8 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurface << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; - // Limit amount of movement. + // Limit amount of movement. Can not happen for triSurfaceMesh but + // can happen for some analytical shapes? forAll(patchDisp, patchPointI) { scalar magDisp = mag(patchDisp[patchPointI]); @@ -1818,254 +2094,6 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurface } -////XXXXXXXXX -//// Get (pp-local) indices of points that are on both patches -//Foam::labelList Foam::autoSnapDriver::getPatchSurfacePoints -//( -// const fvMesh& mesh, -// const indirectPrimitivePatch& allPp, -// const polyPatch& pp -//) -//{ -// // Could use PrimitivePatch & localFaces to extract points but might just -// // as well do it ourselves. -// -// boolList pointOnZone(allPp.nPoints(), false); -// -// forAll(pp, i) -// { -// const face& f = pp[i]; -// -// forAll(f, fp) -// { -// label meshPointI = f[fp]; -// -// Map<label>::const_iterator iter = -// allPp.meshPointMap().find(meshPointI); -// -// if (iter != allPp.meshPointMap().end()) -// { -// label pointI = iter(); -// pointOnZone[pointI] = true; -// } -// } -// } -// -// return findIndices(pointOnZone, true); -//} -//Foam::vectorField Foam::autoSnapDriver::calcNearestLocalSurface -//( -// const meshRefinement& meshRefiner, -// const scalarField& snapDist, -// const indirectPrimitivePatch& pp -//) -//{ -// Info<< "Calculating patchDisplacement as distance to nearest" -// << " local surface point ..." << endl; -// -// const pointField& localPoints = pp.localPoints(); -// const refinementSurfaces& surfaces = meshRefiner.surfaces(); -// const fvMesh& mesh = meshRefiner.mesh(); -// const polyBoundaryMesh& pbm = mesh.boundaryMesh(); -// -// -//// // Assume that all patch-internal points get attracted to their surface -//// // only. So we want to know if point is on multiple regions -//// -//// labelList minPatch(mesh.nPoints(), labelMax); -//// labelList maxPatch(mesh.nPoints(), labelMin); -//// -//// forAll(meshMover.adaptPatchIDs(), i) -//// { -//// label patchI = meshMover.adaptPatchIDs()[i]; -//// const labelList& meshPoints = pbm[patchI].meshPoints(); -//// -//// forAll(meshPoints, meshPointI) -//// { -//// label meshPointI = meshPoints[meshPointI]; -//// minPatch[meshPointI] = min(minPatch[meshPointI], patchI); -//// maxPatch[meshPointI] = max(maxPatch[meshPointI], patchI); -//// } -//// } -//// -//// syncTools::syncPointList -//// ( -//// mesh, -//// minPatch, -//// minEqOp<label>(), // combine op -//// labelMax // null value -//// ); -//// syncTools::syncPointList -//// ( -//// mesh, -//// maxPatch, -//// maxEqOp<label>(), // combine op -//// labelMin // null value -//// ); -// -// // Now all points with minPatch != maxPatch will be on the outside of -// // the patch. -// -// // Displacement per patch point -// vectorField patchDisp(localPoints.size(), vector::zero); -// // Current best snap distance -// scalarField minSnapDist(snapDist); -// // Current surface snapped to -// labelList snapSurf(localPoints.size(), -1); -// -// const labelList& surfaceGeometry = surfaces.surfaces(); -// forAll(surfaceGeometry, surfI) -// { -// label geomI = surfaceGeometry[surfI]; -// const wordList& regNames = allGeometry.regionNames()[geomI]; -// forAll(regNames, regionI) -// { -// label globalRegionI = surfaces.globalRegion(surfI, regionI); -// // Collect master patch points -// label masterPatchI = globalToMasterPatch_[globalRegionI]; -// label slavePatchI = globalToSlavePatch_[globalRegionI]; -// -// labelList patchPointIndices -// ( -// getPatchSurfacePoints -// ( -// mesh, -// pp, -// pbm[masterPatchI] -// ) -// ); -// -// // Find nearest for points both on faceZone and pp. -// List<pointIndexHit> hitInfo; -// surfaces.findNearest -// ( -// surfI, -// regionI, -// pointField(localPoints, patchPointIndices), -// sqr(scalarField(minSnapDist, patchPointIndices)), -// hitInfo -// ); -// -// forAll(hitInfo, i) -// { -// label pointI = patchPointIndices[i]; -// -// if (hitInfo[i].hit()) -// { -// const point& pt = hitInfo[i].hitPoint(); -// patchDisp[pointI] = pt-localPoints[pointI]; -// minSnapDist[pointI] = min -// ( -// minSnapDist[pointI], -// mag(patchDisp[pointI]) -// ); -// snapSurf[pointI] = surfI; -// } -// } -// -// // Slave patch -// if (slavePatchI != masterPatchI) -// { -// labelList patchPointIndices -// ( -// getPatchSurfacePoints -// ( -// mesh, -// pp, -// pbm[slavePatchI] -// ) -// ); -// -// // Find nearest for points both on faceZone and pp. -// List<pointIndexHit> hitInfo; -// surfaces.findNearest -// ( -// surfI, -// regionI, -// pointField(localPoints, patchPointIndices), -// sqr(scalarField(minSnapDist, patchPointIndices)), -// hitInfo -// ); -// -// forAll(hitInfo, i) -// { -// label pointI = patchPointIndices[i]; -// -// if (hitInfo[i].hit()) -// { -// const point& pt = hitInfo[i].hitPoint(); -// patchDisp[pointI] = pt-localPoints[pointI]; -// minSnapDist[pointI] = min -// ( -// minSnapDist[pointI], -// mag(patchDisp[pointI]) -// ); -// snapSurf[pointI] = surfI; -// } -// } -// } -// } -// } -// -// -// // Check if all points are being snapped -// forAll(snapSurf, pointI) -// { -// if (snapSurf[pointI] == -1) -// { -// WarningIn("autoSnapDriver::calcNearestLocalSurface(..)") -// << "For point:" << pointI -// << " coordinate:" << localPoints[pointI] -// << " did not find any surface within:" -// << minSnapDist[pointI] -// << " metre." << endl; -// } -// } -// -// { -// scalarField magDisp(mag(patchDisp)); -// -// Info<< "Wanted displacement : average:" -// << gSum(magDisp)/returnReduce(patchDisp.size(), sumOp<label>()) -// << " min:" << gMin(magDisp) -// << " max:" << gMax(magDisp) << endl; -// } -// -// -// Info<< "Calculated surface displacement in = " -// << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; -// -// -// // Limit amount of movement. -// forAll(patchDisp, patchPointI) -// { -// scalar magDisp = mag(patchDisp[patchPointI]); -// -// if (magDisp > snapDist[patchPointI]) -// { -// patchDisp[patchPointI] *= snapDist[patchPointI] / magDisp; -// -// Pout<< "Limiting displacement for " << patchPointI -// << " from " << magDisp << " to " << snapDist[patchPointI] -// << endl; -// } -// } -// -// // Points on zones in one domain but only present as point on other -// // will not do condition 2 on all. Sync explicitly. -// syncTools::syncPointList -// ( -// mesh, -// pp.meshPoints(), -// patchDisp, -// minMagSqrEqOp<point>(), // combine op -// vector(GREAT, GREAT, GREAT)// null value (note: cannot use VGREAT) -// ); -// -// return patchDisp; -//} -////XXXXXXXXX - void Foam::autoSnapDriver::smoothDisplacement ( const snapParameters& snapParams, @@ -2465,10 +2493,45 @@ void Foam::autoSnapDriver::detectWarpedFaces } +Foam::labelList Foam::autoSnapDriver::getInternalOrBaffleDuplicateFace() const +{ + const fvMesh& mesh = meshRefiner_.mesh(); + + labelList internalOrBaffleFaceZones; + { + List<surfaceZonesInfo::faceZoneType> fzTypes(2); + fzTypes[0] = surfaceZonesInfo::INTERNAL; + fzTypes[1] = surfaceZonesInfo::BAFFLE; + internalOrBaffleFaceZones = meshRefiner_.getZones(fzTypes); + } + + List<labelPair> baffles + ( + meshRefiner_.subsetBaffles + ( + mesh, + internalOrBaffleFaceZones, + localPointRegion::findDuplicateFacePairs(mesh) + ) + ); + + labelList faceToDuplicate(mesh.nFaces(), -1); + forAll(baffles, i) + { + const labelPair& p = baffles[i]; + faceToDuplicate[p[0]] = p[1]; + faceToDuplicate[p[1]] = p[0]; + } + + return faceToDuplicate; +} + + void Foam::autoSnapDriver::doSnap ( const dictionary& snapDict, const dictionary& motionDict, + const bool mergePatchFaces, const scalar featureCos, const scalar planarAngle, const snapParameters& snapParams @@ -2481,10 +2544,6 @@ void Foam::autoSnapDriver::doSnap << "--------------" << nl << endl; - // Get the labels of added patches. - labelList adaptPatchIDs(meshRefiner_.meshedPatches()); - - // faceZone handling // ~~~~~~~~~~~~~~~~~ // @@ -2499,233 +2558,55 @@ void Foam::autoSnapDriver::doSnap // // internal // -------- - // - baffles: contains all faces on faceZone so - // - mesh checks check across baffles - // - they get back merged into internal faces + // - baffles: need to be checked across // - duplicateFace: from face to duplicate face. Contains // all faces on faceZone to prevents merging patch faces. // // baffle // ------ - // - baffles: contains no faces on faceZone since need not be merged/checked - // across + // - baffles: no need to be checked across // - duplicateFace: contains all faces on faceZone to prevent // merging patch faces. // // boundary // -------- - // - baffles: contains no faces on faceZone since need not be merged/checked - // across + // - baffles: no need to be checked across. Also points get duplicated + // so will no longer be baffles // - duplicateFace: contains no faces on faceZone since both sides can // merge faces independently. - // Create baffles (pairs of faces that share the same points) - // Baffles stored as owner and neighbour face that have been created. - List<labelPair> baffles; - meshRefiner_.createZoneBaffles + + // faceZones of type internal + const labelList internalFaceZones ( - globalToMasterPatch_, - globalToSlavePatch_, - baffles + meshRefiner_.getZones + ( + List<surfaceZonesInfo::faceZoneType> + ( + 1, + surfaceZonesInfo::INTERNAL + ) + ) ); - // Maintain map from face to baffle face (-1 for non-baffle faces). Used - // later on to prevent patchface merging if faceType=baffle - labelList duplicateFace(mesh.nFaces(), -1); - forAll(baffles, i) - { - const labelPair& baffle = baffles[i]; - duplicateFace[baffle.first()] = baffle.second(); - duplicateFace[baffle.second()] = baffle.first(); - } - - // Selectively 'forget' about the baffles, i.e. not check across them - // or merge across them. + // Create baffles (pairs of faces that share the same points) + // Baffles stored as owner and neighbour face that have been created. { - const faceZoneMesh& fZones = mesh.faceZones(); - const refinementSurfaces& surfaces = meshRefiner_.surfaces(); - const PtrList<surfaceZonesInfo>& surfZones = surfaces.surfZones(); - - // Determine which - // - faces to remove from list of baffles (so not merge) - // - points to duplicate - - // Per face if is on faceType 'baffle' or 'boundary' - labelList filterFace(mesh.nFaces(), -1); - label nFilterFaces = 0; - // Per point whether it need to be duplicated - PackedBoolList duplicatePoint(mesh.nPoints()); - label nDuplicatePoints = 0; - forAll(surfZones, surfI) - { - const word& faceZoneName = surfZones[surfI].faceZoneName(); - - if (faceZoneName.size()) - { - const surfaceZonesInfo::faceZoneType& faceType = - surfZones[surfI].faceType(); - - if - ( - faceType == surfaceZonesInfo::BAFFLE - || faceType == surfaceZonesInfo::BOUNDARY - ) - { - // Filter out all faces for this zone. - label zoneI = fZones.findZoneID(faceZoneName); - const faceZone& fZone = fZones[zoneI]; - forAll(fZone, i) - { - label faceI = fZone[i]; - filterFace[faceI] = zoneI; - nFilterFaces++; - } - - if (faceType == surfaceZonesInfo::BOUNDARY) - { - forAll(fZone, i) - { - label faceI = fZone[i]; - - // Allow combining patch faces across this face - duplicateFace[faceI] = -1; - - const face& f = mesh.faces()[faceI]; - forAll(f, fp) - { - if (!duplicatePoint[f[fp]]) - { - duplicatePoint[f[fp]] = 1; - nDuplicatePoints++; - } - } - } - } - - Info<< "Surface : " << surfaces.names()[surfI] << nl - << " faces to become baffle : " - << returnReduce(nFilterFaces, sumOp<label>()) << nl - << " points to duplicate : " - << returnReduce(nDuplicatePoints, sumOp<label>()) - << endl; - } - } - } - - // Duplicate points only if all points agree - syncTools::syncPointList + List<labelPair> baffles; + labelList originatingFaceZone; + meshRefiner_.createZoneBaffles ( - mesh, - duplicatePoint, - andEqOp<unsigned int>(), // combine op - 0u // null value + identity(mesh.faceZones().size()), + baffles, + originatingFaceZone ); - // Mark as duplicate (avoids combining patch faces) if one or both - syncTools::syncFaceList(mesh, duplicateFace, maxEqOp<label>()); - // Mark as resulting from baffle/boundary face zone only if both agree - syncTools::syncFaceList(mesh, filterFace, minEqOp<label>()); - - // Duplicate points - if (returnReduce(nDuplicatePoints, sumOp<label>()) > 0) - { - // Collect all points (recount since syncPointList might have - // increased set) - nDuplicatePoints = 0; - forAll(duplicatePoint, pointI) - { - if (duplicatePoint[pointI]) - { - nDuplicatePoints++; - } - } - labelList candidatePoints(nDuplicatePoints); - nDuplicatePoints = 0; - forAll(duplicatePoint, pointI) - { - if (duplicatePoint[pointI]) - { - candidatePoints[nDuplicatePoints++] = pointI; - } - } - - - localPointRegion regionSide(mesh, candidatePoints); - autoPtr<mapPolyMesh> mapPtr = meshRefiner_.dupNonManifoldPoints - ( - regionSide - ); - meshRefinement::updateList - ( - mapPtr().faceMap(), - label(-1), - filterFace - ); - meshRefinement::updateList - ( - mapPtr().faceMap(), - label(-1), - duplicateFace - ); - - // Update baffles and baffle-to-baffle addressing - - const labelList& reverseFaceMap = mapPtr().reverseFaceMap(); - - forAll(baffles, i) - { - labelPair& baffle = baffles[i]; - baffle.first() = reverseFaceMap[baffle.first()]; - baffle.second() = reverseFaceMap[baffle.second()]; - } - - if (debug&meshRefinement::MESH) - { - const_cast<Time&>(mesh.time())++; - Pout<< "Writing duplicatedPoints mesh to time " - << meshRefiner_.timeName() - << endl; - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - mesh.time().path()/"duplicatedPoints" - ); - } - } - - - // Forget about baffles in a BAFFLE/BOUNDARY type zone - DynamicList<labelPair> newBaffles(baffles.size()); - forAll(baffles, i) - { - const labelPair& baffle = baffles[i]; - if - ( - filterFace[baffle.first()] == -1 - && filterFace[baffles[i].second()] == -1 - ) - { - newBaffles.append(baffle); - } - } - - if (newBaffles.size() < baffles.size()) - { - //Info<< "Splitting baffles into" << nl - // << " internal : " << newBaffles.size() << nl - // << " baffle : " << baffles.size()-newBaffles.size() - // << nl << endl; - baffles.transfer(newBaffles); - } - Info<< endl; } + // Duplicate points on faceZones of type boundary + meshRefiner_.dupNonManifoldBoundaryPoints(); + bool doFeatures = false; label nFeatIter = 1; @@ -2741,6 +2622,12 @@ void Foam::autoSnapDriver::doSnap bool meshOk = false; + + // Get the labels of added patches. + labelList adaptPatchIDs(meshRefiner_.meshedPatches()); + + + { autoPtr<indirectPrimitivePatch> ppPtr ( @@ -2753,7 +2640,7 @@ void Foam::autoSnapDriver::doSnap // Distance to attract to nearest feature on surface - const scalarField snapDist(calcSnapDistance(mesh, snapParams, ppPtr())); + scalarField snapDist(calcSnapDistance(mesh, snapParams, ppPtr())); // Construct iterative mesh mover. @@ -2795,13 +2682,27 @@ void Foam::autoSnapDriver::doSnap Info<< "Checked initial mesh in = " << mesh.time().cpuTimeIncrement() << " s\n" << nl << endl; + // Extract baffles across internal faceZones (for checking mesh quality + // across + labelPairList internalBaffles + ( + meshRefiner_.subsetBaffles + ( + mesh, + internalFaceZones, + localPointRegion::findDuplicateFacePairs(mesh) + ) + ); + + + // Pre-smooth patch vertices (so before determining nearest) preSmoothPatch ( meshRefiner_, snapParams, nInitErrors, - baffles, + internalBaffles, meshMoverPtr() ); @@ -2814,235 +2715,35 @@ void Foam::autoSnapDriver::doSnap List<pointConstraint> patchConstraints; - for (label iter = 0; iter < nFeatIter; iter++) - { - //if (doFeatures && (iter == 0 || iter == nFeatIter/2)) - //{ - // Info<< "Splitting diagonal attractions" << endl; - // - // indirectPrimitivePatch& pp = ppPtr(); - // motionSmoother& meshMover = meshMoverPtr(); - // - // // Calculate displacement at every patch point. Insert into - // // meshMover. - // // Calculate displacement at every patch point - // pointField nearestPoint; - // vectorField nearestNormal; - // - // if (snapParams.detectNearSurfacesSnap()) - // { - // nearestPoint.setSize(pp.nPoints(), vector::max); - // nearestNormal.setSize(pp.nPoints(), vector::zero); - // } - // - // vectorField disp = calcNearestSurface - // ( - // meshRefiner_, - // snapDist, - // pp, - // nearestPoint, - // nearestNormal - // ); - // - // - // // Override displacement at thin gaps - // if (snapParams.detectNearSurfacesSnap()) - // { - // detectNearSurfaces - // ( - // Foam::cos(degToRad(planarAngle)),// planar gaps - // pp, - // nearestPoint, // surfacepoint from nearest test - // nearestNormal, // surfacenormal from nearest test - // - // disp - // ); - // } - // - // // Override displacement with feature edge attempt - // const label iter = 0; - // calcNearestSurfaceFeature - // ( - // snapParams, - // false, // avoidSnapProblems - // iter, - // featureCos, - // scalar(iter+1)/nFeatIter, - // snapDist, - // disp, - // meshMover, - // patchAttraction, - // patchConstraints - // ); - // - // - // const labelList& bFaces = ppPtr().addressing(); - // DynamicList<label> splitFaces(bFaces.size()); - // DynamicList<labelPair> splits(bFaces.size()); - // - // forAll(bFaces, faceI) - // { - // const labelPair split - // ( - // findDiagonalAttraction - // ( - // ppPtr(), - // patchAttraction, - // patchConstraints, - // faceI - // ) - // ); - // - // if (split != labelPair(-1, -1)) - // { - // splitFaces.append(bFaces[faceI]); - // splits.append(split); - // } - // } - // - // Info<< "Splitting " - // << returnReduce(splitFaces.size(), sumOp<label>()) - // << " faces along diagonal attractions" << endl; - // - // autoPtr<mapPolyMesh> mapPtr = meshRefiner_.splitFaces - // ( - // splitFaces, - // splits - // ); - // - // const labelList& faceMap = mapPtr().faceMap(); - // meshRefinement::updateList(faceMap, -1, duplicateFace); - // const labelList& reverseFaceMap = mapPtr().reverseFaceMap(); - // forAll(baffles, i) - // { - // labelPair& baffle = baffles[i]; - // baffle.first() = reverseFaceMap[baffle.first()]; - // baffle.second() = reverseFaceMap[baffle.second()]; - // } - // - // meshMoverPtr.clear(); - // ppPtr.clear(); - // - // ppPtr = meshRefinement::makePatch(mesh, adaptPatchIDs); - // meshMoverPtr.reset - // ( - // new motionSmoother - // ( - // mesh, - // ppPtr(), - // adaptPatchIDs, - // meshRefinement::makeDisplacementField - // ( - // pointMesh::New(mesh), - // adaptPatchIDs - // ), - // motionDict - // ) - // ); - // - // if (debug&meshRefinement::MESH) - // { - // const_cast<Time&>(mesh.time())++; - // Info<< "Writing split diagonal mesh to time " - // << meshRefiner_.timeName() << endl; - // meshRefiner_.write - // ( - // meshRefinement::debugType(debug), - // meshRefinement::writeType - // ( - // meshRefinement::writeLevel() - // | meshRefinement::WRITEMESH - // ), - // mesh.time().path()/meshRefiner_.timeName() - // ); - // } - //} - //else - //if - //( - // doFeatures - // && (iter == 1 || iter == nFeatIter/2+1 || iter == nFeatIter-1) - //) - //{ - // Info<< "Splitting warped faces" << endl; - // - // const labelList& bFaces = ppPtr().addressing(); - // DynamicList<label> splitFaces(bFaces.size()); - // DynamicList<labelPair> splits(bFaces.size()); - // - // detectWarpedFaces - // ( - // featureCos, - // ppPtr(), - // - // splitFaces, - // splits - // ); - // - // Info<< "Splitting " - // << returnReduce(splitFaces.size(), sumOp<label>()) - // << " faces along diagonal to avoid warpage" << endl; - // - // autoPtr<mapPolyMesh> mapPtr = meshRefiner_.splitFaces - // ( - // splitFaces, - // splits - // ); - // - // const labelList& faceMap = mapPtr().faceMap(); - // meshRefinement::updateList(faceMap, -1, duplicateFace); - // const labelList& reverseFaceMap = mapPtr().reverseFaceMap(); - // forAll(baffles, i) - // { - // labelPair& baffle = baffles[i]; - // baffle.first() = reverseFaceMap[baffle.first()]; - // baffle.second() = reverseFaceMap[baffle.second()]; - // } - // - // meshMoverPtr.clear(); - // ppPtr.clear(); - // - // ppPtr = meshRefinement::makePatch(mesh, adaptPatchIDs); - // meshMoverPtr.reset - // ( - // new motionSmoother - // ( - // mesh, - // ppPtr(), - // adaptPatchIDs, - // meshRefinement::makeDisplacementField - // ( - // pointMesh::New(mesh), - // adaptPatchIDs - // ), - // motionDict - // ) - // ); - // - // if (debug&meshRefinement::MESH) - // { - // const_cast<Time&>(mesh.time())++; - // Info<< "Writing split warped mesh to time " - // << meshRefiner_.timeName() << endl; - // meshRefiner_.write - // ( - // meshRefinement::debugType(debug), - // meshRefinement::writeType - // ( - // meshRefinement::writeLevel() - // | meshRefinement::WRITEMESH - // ), - // mesh.time().path()/meshRefiner_.timeName() - // ); - // } - //} - + //- Any faces to split + DynamicList<label> splitFaces; + //- Indices in face to split across + DynamicList<labelPair> splits; + for (label iter = 0; iter < nFeatIter; iter++) + { Info<< nl << "Morph iteration " << iter << nl << "-----------------" << endl; + // Splitting iteration? + bool doSplit = false; + if + ( + doFeatures + && snapParams.nFaceSplitInterval() > 0 + && ( + (iter == nFeatIter-1) + || (iter > 0 && (iter%snapParams.nFaceSplitInterval()) == 0) + ) + ) + { + doSplit = true; + } + + + indirectPrimitivePatch& pp = ppPtr(); motionSmoother& meshMover = meshMoverPtr(); @@ -3061,9 +2762,13 @@ void Foam::autoSnapDriver::doSnap vectorField disp = calcNearestSurface ( + snapParams.strictRegionSnap(), // attract points to region only meshRefiner_, + globalToMasterPatch_, // for if strictRegionSnap + globalToSlavePatch_, // for if strictRegionSnap snapDist, pp, + nearestPoint, nearestNormal ); @@ -3086,18 +2791,26 @@ void Foam::autoSnapDriver::doSnap // Override displacement with feature edge attempt if (doFeatures) { + splitFaces.clear(); + splits.clear(); disp = calcNearestSurfaceFeature ( snapParams, - true, // avoidSnapProblems + !doSplit, // alignMeshEdges iter, featureCos, scalar(iter+1)/nFeatIter, + snapDist, disp, + nearestNormal, meshMover, + patchAttraction, - patchConstraints + patchConstraints, + + splitFaces, + splits ); } @@ -3129,7 +2842,7 @@ void Foam::autoSnapDriver::doSnap ( snapParams, nInitErrors, - baffles, + internalBaffles, meshMover ); @@ -3169,71 +2882,190 @@ void Foam::autoSnapDriver::doSnap // Use current mesh as base mesh meshMover.correct(); + + + + // See if any faces need splitting + label nTotalSplit = returnReduce(splitFaces.size(), sumOp<label>()); + if (nTotalSplit && doSplit) + { + // Filter out baffle faces from faceZones of type + // internal/baffle + + labelList duplicateFace(getInternalOrBaffleDuplicateFace()); + + { + labelList oldSplitFaces(splitFaces.xfer()); + List<labelPair> oldSplits(splits.xfer()); + forAll(oldSplitFaces, i) + { + if (duplicateFace[oldSplitFaces[i]] == -1) + { + splitFaces.append(oldSplitFaces[i]); + splits.append(oldSplits[i]); + } + } + nTotalSplit = returnReduce + ( + splitFaces.size(), + sumOp<label>() + ); + } + + // Update mesh + meshRefiner_.splitFacesUndo + ( + splitFaces, + splits, + motionDict, + + duplicateFace, + internalBaffles + ); + + // Redo meshMover + meshMoverPtr.clear(); + ppPtr.clear(); + + // Update mesh mover + ppPtr = meshRefinement::makePatch(mesh, adaptPatchIDs); + meshMoverPtr.reset + ( + new motionSmoother + ( + mesh, + ppPtr(), + adaptPatchIDs, + meshRefinement::makeDisplacementField + ( + pointMesh::New(mesh), + adaptPatchIDs + ), + motionDict + ) + ); + + // Update snapping distance + snapDist = calcSnapDistance(mesh, snapParams, ppPtr()); + + + if (debug&meshRefinement::MESH) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing split-faces mesh to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + mesh.time().path()/meshRefiner_.timeName() + ); + } + } + + + if (debug&meshRefinement::MESH) + { + forAll(internalBaffles, i) + { + const labelPair& p = internalBaffles[i]; + const point& fc0 = mesh.faceCentres()[p[0]]; + const point& fc1 = mesh.faceCentres()[p[1]]; + + if (mag(fc0-fc1) > meshRefiner_.mergeDistance()) + { + FatalErrorIn("autoSnapDriver::doSnap(..)") + << "Separated baffles : f0:" << p[0] + << " centre:" << fc0 + << " f1:" << p[1] << " centre:" << fc1 + << " distance:" << mag(fc0-fc1) + << exit(FatalError); + } + } + } } } // Merge any introduced baffles (from faceZones of faceType 'internal') { - autoPtr<mapPolyMesh> mapPtr = mergeZoneBaffles(baffles); + autoPtr<mapPolyMesh> mapPtr = meshRefiner_.mergeZoneBaffles + ( + true, // internal zones + false // baffle zones + ); if (mapPtr.valid()) { - forAll(duplicateFace, faceI) + if (debug & meshRefinement::MESH) { - if (duplicateFace[faceI] != -1) - { - duplicateFace[faceI] = mapPtr().reverseFaceMap()[faceI]; - } + const_cast<Time&>(mesh.time())++; + Info<< "Writing baffle-merged mesh to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + meshRefiner_.timeName() + ); } } } // Repatch faces according to nearest. Do not repatch baffle faces. { - autoPtr<mapPolyMesh> mapPtr = repatchToSurface - ( - snapParams, - adaptPatchIDs, - duplicateFace - ); - meshRefinement::updateList + labelList duplicateFace(getInternalOrBaffleDuplicateFace()); + + repatchToSurface(snapParams, adaptPatchIDs, duplicateFace); + } + + if (mergePatchFaces) + { + labelList duplicateFace(getInternalOrBaffleDuplicateFace()); + + // Repatching might have caused faces to be on same patch and hence + // mergeable so try again to merge coplanar faces. Do not merge baffle + // faces to ensure they both stay the same. + label nChanged = meshRefiner_.mergePatchFacesUndo ( - mapPtr().faceMap(), - label(-1), - duplicateFace + featureCos, // minCos + featureCos, // concaveCos + meshRefiner_.meshedPatches(), + motionDict, + duplicateFace // faces not to merge ); - } - // Repatching might have caused faces to be on same patch and hence - // mergeable so try again to merge coplanar faces. Do not merge baffle - // faces to ensure they both stay the same. - label nChanged = meshRefiner_.mergePatchFacesUndo - ( - featureCos, // minCos - featureCos, // concaveCos - meshRefiner_.meshedPatches(), - motionDict, - duplicateFace // faces not to merge - ); + nChanged += meshRefiner_.mergeEdgesUndo(featureCos, motionDict); - nChanged += meshRefiner_.mergeEdgesUndo(featureCos, motionDict); + if (nChanged > 0 && debug & meshRefinement::MESH) + { + const_cast<Time&>(mesh.time())++; + Info<< "Writing patchFace merged mesh to time " + << meshRefiner_.timeName() << endl; + meshRefiner_.write + ( + meshRefinement::debugType(debug), + meshRefinement::writeType + ( + meshRefinement::writeLevel() + | meshRefinement::WRITEMESH + ), + meshRefiner_.timeName() + ); + } + } - if (nChanged > 0 && debug & meshRefinement::MESH) + if (debug & meshRefinement::MESH) { const_cast<Time&>(mesh.time())++; - Info<< "Writing patchFace merged mesh to time " - << meshRefiner_.timeName() << endl; - meshRefiner_.write - ( - meshRefinement::debugType(debug), - meshRefinement::writeType - ( - meshRefinement::writeLevel() - | meshRefinement::WRITEMESH - ), - meshRefiner_.timeName() - ); } } diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H index b72d147a844f74e7426d318d4fbaf0460a1beacb..630d24d24e3ad3f9552f0ee158a47f6ac1d397a0 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriver.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -37,6 +37,7 @@ SourceFiles #define autoSnapDriver_H #include "meshRefinement.H" +#include "DynamicField.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -45,6 +46,7 @@ namespace Foam // Forward declaration of classes class motionSmoother; +class refinementParameters; class snapParameters; class pointConstraint; @@ -79,26 +81,34 @@ class autoSnapDriver PackedBoolList& ); + //- Calculate displacement (over all mesh points) to move points + // to average of connected cell centres + static tmp<pointField> smoothInternalDisplacement + ( + const meshRefinement& meshRefiner, + const motionSmoother& + ); + //- Calculate displacement per patch point to smooth out patch. // Quite complicated in determining which points to move where. - static pointField smoothPatchDisplacement + static tmp<pointField> smoothPatchDisplacement ( const motionSmoother&, const List<labelPair>& ); - //static tmp<pointField> avg - //( - // const indirectPrimitivePatch&, - // const pointField& - //); + static tmp<pointField> avg + ( + const indirectPrimitivePatch&, + const pointField& + ); //- Calculate displacement per patch point. Wip. - //static tmp<pointField> smoothLambdaMuPatchDisplacement - //( - // const motionSmoother& meshMover, - // const List<labelPair>& baffles - //); + static pointField smoothLambdaMuPatchDisplacement + ( + const motionSmoother& meshMover, + const List<labelPair>& baffles + ); //- Check that face zones are synced @@ -136,6 +146,48 @@ class autoSnapDriver DynamicList<labelPair>& splits ) const; + //- Get per face -1 or label of opposite face if on internal/baffle + // faceZone + labelList getInternalOrBaffleDuplicateFace() const; + + //- Get points both on patch and facezone. + static labelList getZoneSurfacePoints + ( + const fvMesh& mesh, + const indirectPrimitivePatch&, + const word& zoneName + ); + + //- Get points both on patch and facezone. + template<class FaceList> + static labelList getFacePoints + ( + const indirectPrimitivePatch& pp, + const FaceList& faces + ); + + //- Per patch point calculate point on nearest surface. + // Return displacement of patch points. + static void calcNearestSurface + ( + const refinementSurfaces& surfaces, + + const labelList& surfacesToTest, + const labelListList& regionsToTest, + + const pointField& localPoints, + const labelList& zonePointIndices, + + scalarField& minSnapDist, + labelList& snapSurf, + vectorField& patchDisp, + + // Optional: nearest point, normal + pointField& nearestPoint, + vectorField& nearestNormal + ); + + // Feature line snapping //- Is point on two feature edges that make a largish angle? @@ -177,8 +229,8 @@ class autoSnapDriver const scalarField& faceSnapDist, vectorField& faceDisp, vectorField& faceSurfaceNormal, - labelList& faceSurfaceRegion, - vectorField& faceRotation + labelList& faceSurfaceRegion + //vectorField& faceRotation ) const; void calcNearestFacePointProperties ( @@ -253,6 +305,86 @@ class autoSnapDriver const label faceI ) const; + scalar pyrVol + ( + const indirectPrimitivePatch& pp, + const vectorField& featureAttraction, + const face& localF, + const point& cc + ) const; + void facePoints + ( + const indirectPrimitivePatch& pp, + const vectorField& featureAttraction, + const vectorField& nearestAttraction, + const face& f, + DynamicField<point>& points + ) const; + scalar pyrVol + ( + const indirectPrimitivePatch& pp, + const vectorField& featureAttraction, + const vectorField& nearestAttraction, + const face& localF, + const point& cc + ) const; + Tuple2<point, vector> centreAndNormal + ( + const indirectPrimitivePatch& pp, + const vectorField& featureAttraction, + const vectorField& nearestAttraction, + const face& localF + ) const; + bool isSplitAlignedWithFeature + ( + const scalar featureCos, + const point& newPt0, + const pointConstraint& pc0, + const point& newPt1, + const pointConstraint& pc1 + ) const; + bool isConcave + ( + const point& c0, + const vector& area0, + const point& c1, + const vector& area1, + const scalar concaveCos + ) const; + labelPair findDiagonalAttraction + ( + const scalar featureCos, + const scalar concaveCos, + const scalar minAreaFraction, + const indirectPrimitivePatch& pp, + const vectorField& patchAttraction, + const List<pointConstraint>& patchConstraints, + const vectorField& nearestAttraction, + const vectorField& nearestNormal, + const label faceI, + + DynamicField<point>& points0, + DynamicField<point>& points1 + ) const; + + //- Do all logic on whether to add face cut to diagonal + // attraction + void splitDiagonals + ( + const scalar featureCos, + const scalar concaveCos, + const scalar minAreaFraction, + + const indirectPrimitivePatch& pp, + const vectorField& nearestAttraction, + const vectorField& nearestNormal, + + vectorField& patchAttraction, + List<pointConstraint>& patchConstraints, + DynamicList<label>& splitFaces, + DynamicList<labelPair>& splits + ) const; + //- Avoid attraction across face diagonal since would // cause face squeeze void avoidDiagonalAttraction @@ -264,6 +396,14 @@ class autoSnapDriver List<pointConstraint>& patchConstraints ) const; + //- Write some stats about constraints + void writeStats + ( + const indirectPrimitivePatch& pp, + const PackedBoolList& isMasterPoint, + const List<pointConstraint>& patchConstraints + ) const; + //- Return hit if on multiple points pointIndexHit findMultiPatchPoint ( @@ -320,7 +460,6 @@ class autoSnapDriver void featureAttractionUsingReconstruction ( const label iter, - const bool avoidSnapProblems, const scalar featureCos, const indirectPrimitivePatch& pp, const scalarField& snapDist, @@ -366,6 +505,7 @@ class autoSnapDriver void determineBaffleFeatures ( const label iter, + const bool baffleFeaturePoints, const scalar featureCos, const indirectPrimitivePatch& pp, @@ -450,12 +590,20 @@ class autoSnapDriver void featureAttractionUsingFeatureEdges ( const label iter, - const bool avoidSnapProblems, - const scalar featureCos, const bool multiRegionFeatureSnap, + + const bool detectBaffles, + const bool baffleFeaturePoints, + const bool releasePoints, + const bool stringFeatures, + const bool avoidDiagonal, + + const scalar featureCos, + const indirectPrimitivePatch& pp, const scalarField& snapDist, const vectorField& nearestDisp, + const vectorField& nearestNormal, const List<List<point> >& pointFaceSurfNormals, const List<List<point> >& pointFaceDisp, @@ -465,12 +613,14 @@ class autoSnapDriver vectorField& patchAttraction, List<pointConstraint>& patchConstraints ) const; + void preventFaceSqueeze ( const label iter, const scalar featureCos, const indirectPrimitivePatch& pp, const scalarField& snapDist, + const vectorField& nearestAttraction, vectorField& patchAttraction, List<pointConstraint>& patchConstraints @@ -483,15 +633,19 @@ class autoSnapDriver vectorField calcNearestSurfaceFeature ( const snapParameters& snapParams, - const bool avoidSnapProblems, + const bool alignMeshEdges, const label iter, const scalar featureCos, const scalar featureAttract, const scalarField& snapDist, const vectorField& nearestDisp, + const vectorField& nearestNormal, motionSmoother& meshMover, vectorField& patchAttraction, - List<pointConstraint>& patchConstraints + List<pointConstraint>& patchConstraints, + + DynamicList<label>& splitFaces, + DynamicList<labelPair>& splits ) const; @@ -545,14 +699,6 @@ public: motionSmoother& ); - //- Get points both on patch and facezone. - static labelList getZoneSurfacePoints - ( - const fvMesh& mesh, - const indirectPrimitivePatch&, - const word& zoneName - ); - //- Helper: calculate average cell centre per point static tmp<pointField> avgCellCentres ( @@ -575,7 +721,10 @@ public: // displacement of patch points. static vectorField calcNearestSurface ( + const bool strictRegionSnap, const meshRefinement& meshRefiner, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, const scalarField& snapDist, const indirectPrimitivePatch&, pointField& nearestPoint, @@ -622,6 +771,7 @@ public: ( const dictionary& snapDict, const dictionary& motionDict, + const bool mergePatchFaces, const scalar featureCos, const scalar planarAngle, const snapParameters& snapParams @@ -636,6 +786,12 @@ public: // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +#ifdef NoRepository +# include "autoSnapDriverTemplates.C" +#endif + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + #endif // ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverFeature.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverFeature.C index 6fc6363bb95cbd96f088d59eca39abb27b0c2821..ac236f72b5bd74a522f44bdef820abfb816eb607 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverFeature.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverFeature.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -38,6 +38,8 @@ License #include "indexedOctree.H" #include "snapParameters.H" #include "PatchTools.H" +#include "pyramidPointFaceRef.H" +#include "localPointRegion.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -222,8 +224,8 @@ void Foam::autoSnapDriver::calcNearestFace const scalarField& faceSnapDist, vectorField& faceDisp, vectorField& faceSurfaceNormal, - labelList& faceSurfaceGlobalRegion, - vectorField& faceRotation + labelList& faceSurfaceGlobalRegion + //vectorField& faceRotation ) const { const fvMesh& mesh = meshRefiner_.mesh(); @@ -407,38 +409,38 @@ void Foam::autoSnapDriver::calcNearestFace } - // Determine rotation - // ~~~~~~~~~~~~~~~~~~ - - // Determine rotation axis - faceRotation.setSize(pp.size()); - faceRotation = vector::zero; - - forAll(faceRotation, faceI) - { - // Note: extend to >180 degrees checking - faceRotation[faceI] = - pp.faceNormals()[faceI] - ^ faceSurfaceNormal[faceI]; - } - - if (debug&meshRefinement::ATTRACTION) - { - dumpMove - ( - mesh.time().path() - / "faceDisp_" + name(iter) + ".obj", - pp.faceCentres(), - pp.faceCentres() + faceDisp - ); - dumpMove - ( - mesh.time().path() - / "faceRotation_" + name(iter) + ".obj", - pp.faceCentres(), - pp.faceCentres() + faceRotation - ); - } + //// Determine rotation + //// ~~~~~~~~~~~~~~~~~~ + // + //// Determine rotation axis + //faceRotation.setSize(pp.size()); + //faceRotation = vector::zero; + // + //forAll(faceRotation, faceI) + //{ + // // Note: extend to >180 degrees checking + // faceRotation[faceI] = + // pp.faceNormals()[faceI] + // ^ faceSurfaceNormal[faceI]; + //} + // + //if (debug&meshRefinement::ATTRACTION) + //{ + // dumpMove + // ( + // mesh.time().path() + // / "faceDisp_" + name(iter) + ".obj", + // pp.faceCentres(), + // pp.faceCentres() + faceDisp + // ); + // dumpMove + // ( + // mesh.time().path() + // / "faceRotation_" + name(iter) + ".obj", + // pp.faceCentres(), + // pp.faceCentres() + faceRotation + // ); + //} } @@ -553,34 +555,77 @@ void Foam::autoSnapDriver::calcNearestFacePointProperties patchID[meshFaceI-mesh.nInternalFaces()] = -1; } - // See if pp point uses any non-meshed boundary faces - const labelList& boundaryPoints = pp.boundaryPoints(); - forAll(boundaryPoints, i) + + // See if edge of pp uses any non-meshed boundary faces. If so add the + // boundary face as additional constraint. Note that we account for + // both 'real' boundary edges and boundary edge of baffles + + const labelList bafflePair + ( + localPointRegion::findDuplicateFaces(mesh, pp.addressing()) + ); + + + // Mark all points on 'boundary' edges + PackedBoolList isBoundaryPoint(pp.nPoints()); + + const labelListList& edgeFaces = pp.edgeFaces(); + const edgeList& edges = pp.edges(); + + forAll(edgeFaces, edgeI) { - label pointI = boundaryPoints[i]; - label meshPointI = pp.meshPoints()[pointI]; - const point& pt = mesh.points()[meshPointI]; - const labelList& pFaces = mesh.pointFaces()[meshPointI]; + const edge& e = edges[edgeI]; + const labelList& eFaces = edgeFaces[edgeI]; + + if (eFaces.size() == 1) + { + // 'real' boundary edge + isBoundaryPoint[e[0]] = true; + isBoundaryPoint[e[1]] = true; + } + else if (eFaces.size() == 2 && bafflePair[eFaces[0]] == eFaces[1]) + { + // 'baffle' boundary edge + isBoundaryPoint[e[0]] = true; + isBoundaryPoint[e[1]] = true; + } + } + - List<point>& pNormals = pointFaceSurfNormals[pointI]; - List<point>& pDisp = pointFaceDisp[pointI]; - List<point>& pFc = pointFaceCentres[pointI]; - labelList& pFid = pointFacePatchID[pointI]; + // Construct labelList equivalent of meshPointMap + labelList meshToPatchPoint(mesh.nPoints(), -1); + forAll(pp.meshPoints(), pointI) + { + meshToPatchPoint[pp.meshPoints()[pointI]] = pointI; + } - forAll(pFaces, i) + forAll(patchID, bFaceI) + { + label patchI = patchID[bFaceI]; + + if (patchI != -1) { - label meshFaceI = pFaces[i]; - if (!mesh.isInternalFace(meshFaceI)) + label faceI = mesh.nInternalFaces()+bFaceI; + const face& f = mesh.faces()[faceI]; + + forAll(f, fp) { - label patchI = patchID[meshFaceI-mesh.nInternalFaces()]; + label pointI = meshToPatchPoint[f[fp]]; - if (patchI != -1) + if (pointI != -1 && isBoundaryPoint[pointI]) { - vector fn = mesh.faceAreas()[meshFaceI]; + List<point>& pNormals = pointFaceSurfNormals[pointI]; + List<point>& pDisp = pointFaceDisp[pointI]; + List<point>& pFc = pointFaceCentres[pointI]; + labelList& pFid = pointFacePatchID[pointI]; + + const point& pt = mesh.points()[f[fp]]; + vector fn = mesh.faceAreas()[faceI]; + pNormals.append(fn/mag(fn)); - pDisp.append(mesh.faceCentres()[meshFaceI]-pt); - pFc.append(mesh.faceCentres()[meshFaceI]); + pDisp.append(mesh.faceCentres()[faceI]-pt); + pFc.append(mesh.faceCentres()[faceI]); pFid.append(patchI); } } @@ -823,6 +868,54 @@ Foam::pointIndexHit Foam::autoSnapDriver::findMultiPatchPoint } +void Foam::autoSnapDriver::writeStats +( + const indirectPrimitivePatch& pp, + const PackedBoolList& isPatchMasterPoint, + const List<pointConstraint>& patchConstraints +) const +{ + label nMasterPoints = 0; + label nPlanar = 0; + label nEdge = 0; + label nPoint = 0; + + forAll(patchConstraints, pointI) + { + if (isPatchMasterPoint[pointI]) + { + nMasterPoints++; + + if (patchConstraints[pointI].first() == 1) + { + nPlanar++; + } + else if (patchConstraints[pointI].first() == 2) + { + nEdge++; + } + else if (patchConstraints[pointI].first() == 3) + { + nPoint++; + } + } + } + + reduce(nMasterPoints, sumOp<label>()); + reduce(nPlanar, sumOp<label>()); + reduce(nEdge, sumOp<label>()); + reduce(nPoint, sumOp<label>()); + Info<< "total master points :" << nMasterPoints + << " of which attracted to :" << nl + << " feature point : " << nPoint << nl + << " feature edge : " << nEdge << nl + << " nearest surface : " << nPlanar << nl + << " rest : " << nMasterPoints-nPoint-nEdge-nPlanar + << nl + << endl; +} + + void Foam::autoSnapDriver::featureAttractionUsingReconstruction ( const label iter, @@ -1019,7 +1112,6 @@ void Foam::autoSnapDriver::featureAttractionUsingReconstruction void Foam::autoSnapDriver::featureAttractionUsingReconstruction ( const label iter, - const bool avoidSnapProblems, const scalar featureCos, const indirectPrimitivePatch& pp, @@ -1443,7 +1535,6 @@ void Foam::autoSnapDriver::releasePointsNextToMultiPatch } -// If only two attractions and across face return the face indices Foam::labelPair Foam::autoSnapDriver::findDiagonalAttraction ( const indirectPrimitivePatch& pp, @@ -1460,50 +1551,424 @@ Foam::labelPair Foam::autoSnapDriver::findDiagonalAttraction if (f.size() >= 4) { - forAll(f, fp) + for (label startFp = 0; startFp < f.size()-2; startFp++) { - label pointI = f[fp]; - if (patchConstraints[pointI].first() >= 2) + label minFp = f.rcIndex(startFp); + + for + ( + label endFp = f.fcIndex(f.fcIndex(startFp)); + endFp < f.size() && endFp != minFp; + endFp++ + ) { - // Attract to feature edge or point - if (attractIndices[0] == -1) + if + ( + patchConstraints[f[startFp]].first() >= 2 + && patchConstraints[f[endFp]].first() >= 2 + ) { - // First attraction. Store - attractIndices[0] = fp; + attractIndices = labelPair(startFp, endFp); + break; } - else if (attractIndices[1] == -1) + } + } + } + return attractIndices; +} + + +bool Foam::autoSnapDriver::isSplitAlignedWithFeature +( + const scalar featureCos, + const point& p0, + const pointConstraint& pc0, + const point& p1, + const pointConstraint& pc1 +) const +{ + vector d(p1-p0); + scalar magD = mag(d); + if (magD < VSMALL) + { + // Two diagonal points already colocated? + return false; + } + else + { + d /= magD; + + // Is diagonal d aligned with at least one of the feature + // edges? + + if (pc0.first() == 2 && mag(d & pc0.second()) > featureCos) + { + return true; + } + else if (pc1.first() == 2 && mag(d & pc1.second()) > featureCos) + { + return true; + } + else + { + return false; + } + } +} + + +// Is situation very concave +bool Foam::autoSnapDriver::isConcave +( + const point& c0, + const vector& area0, + const point& c1, + const vector& area1, + const scalar concaveCos +) const +{ + vector n0 = area0; + scalar magN0 = mag(n0); + if (magN0 < VSMALL) + { + // Zero area face. What to return? For now disable splitting. + return true; + } + n0 /= magN0; + + // Distance from c1 to plane of face0 + scalar d = (c1-c0)&n0; + + if (d <= 0) + { + // Convex (face1 centre on 'inside' of face0) + return false; + } + else + { + // Is a bit or very concave? + vector n1 = area1; + scalar magN1 = mag(n1); + if (magN1 < VSMALL) + { + // Zero area face. See above + return true; + } + n1 /= magN1; + + if ((n0&n1) < concaveCos) + { + return true; + } + else + { + return false; + } + } +} + + +Foam::labelPair Foam::autoSnapDriver::findDiagonalAttraction +( + const scalar featureCos, + const scalar concaveCos, + const scalar minAreaRatio, + const indirectPrimitivePatch& pp, + const vectorField& patchAttr, + const List<pointConstraint>& patchConstraints, + const vectorField& nearestAttr, + const vectorField& nearestNormal, + const label faceI, + + DynamicField<point>& points0, + DynamicField<point>& points1 +) const +{ + const face& localF = pp.localFaces()[faceI]; + + labelPair attractIndices(-1, -1); + + if (localF.size() >= 4) + { + const pointField& localPts = pp.localPoints(); + + //// Estimate cell centre taking patchAttraction into account + //// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + //// (is this necessary?) + //const polyMesh& mesh = meshRefiner_.mesh(); + //label meshFaceI = pp.addressing()[faceI]; + //const face& meshF = mesh.faces()[meshFaceI]; + //label cellI = mesh.faceOwner()[meshFaceI]; + //const labelList& cPoints = mesh.cellPoints(cellI); + // + //point cc(mesh.points()[meshF[0]]); + //for (label i = 1; i < meshF.size(); i++) + //{ + // cc += mesh.points()[meshF[i]]+patchAttr[localF[i]]; + //} + //forAll(cPoints, i) + //{ + // label pointI = cPoints[i]; + // if (findIndex(meshF, pointI) == -1) + // { + // cc += mesh.points()[pointI]; + // } + //} + //cc /= cPoints.size(); + ////const point& cc = mesh.cellCentres()[cellI]; + // + //const scalar vol = pyrVol(pp, patchAttr, localF, cc); + //const scalar area = localF.mag(localPts); + + + + // Try all diagonal cuts + // ~~~~~~~~~~~~~~~~~~~~~ + + face f0(3); + face f1(3); + + for (label startFp = 0; startFp < localF.size()-2; startFp++) + { + label minFp = localF.rcIndex(startFp); + + for + ( + label endFp = localF.fcIndex(localF.fcIndex(startFp)); + endFp < localF.size() && endFp != minFp; + endFp++ + ) + { + label startPtI = localF[startFp]; + label endPtI = localF[endFp]; + + const pointConstraint& startPc = patchConstraints[startPtI]; + const pointConstraint& endPc = patchConstraints[endPtI]; + + if (startPc.first() >= 2 && endPc.first() >= 2) { - // Second attraction. Check if not consecutive to first - // attraction - label fp0 = attractIndices[0]; - if (f.fcIndex(fp0) == fp || f.fcIndex(fp) == fp0) + if (startPc.first() == 2 || endPc.first() == 2) { - // Consecutive. Skip. - attractIndices = labelPair(-1, -1); - break; + // Check if + // - sameish feature edge normal + // - diagonal aligned with feature edge normal + point start = localPts[startPtI]+patchAttr[startPtI]; + point end = localPts[endPtI]+patchAttr[endPtI]; + + if + ( + !isSplitAlignedWithFeature + ( + featureCos, + start, + startPc, + end, + endPc + ) + ) + { + // Attract to different features. No need to + // introduce split + continue; + } + } + + + + // Form two faces + // ~~~~~~~~~~~~~~ + // Predict position of faces. End points of the faces + // attract to the feature + // and all the other points just attract to the nearest + + // face0 + + f0.setSize(endFp-startFp+1); + label i0 = 0; + for (label fp = startFp; fp <= endFp; fp++) + { + f0[i0++] = localF[fp]; + } + + // Get compact face and points + const face compact0(identity(f0.size())); + points0.clear(); + points0.append(localPts[f0[0]] + patchAttr[f0[0]]); + for (label fp=1; fp < f0.size()-1; fp++) + { + label pI = f0[fp]; + points0.append(localPts[pI] + nearestAttr[pI]); + } + points0.append + ( + localPts[f0.last()] + patchAttr[f0.last()] + ); + + + // face1 + + f1.setSize(localF.size()+2-f0.size()); + label i1 = 0; + for + ( + label fp = endFp; + fp != startFp; + fp = localF.fcIndex(fp) + ) + { + f1[i1++] = localF[fp]; + } + f1[i1++] = localF[startFp]; + + + // Get compact face and points + const face compact1(identity(f1.size())); + points1.clear(); + points1.append(localPts[f1[0]] + patchAttr[f1[0]]); + for (label fp=1; fp < f1.size()-1; fp++) + { + label pI = f1[fp]; + points1.append(localPts[pI] + nearestAttr[pI]); + } + points1.append + ( + localPts[f1.last()] + patchAttr[f1.last()] + ); + + + + // Avoid splitting concave faces + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + if + ( + isConcave + ( + compact0.centre(points0), + compact0.normal(points0), + compact1.centre(points1), + compact1.normal(points1), + concaveCos + ) + ) + { + //// Dump to obj + //Pout<< "# f0:" << f0 << endl; + //forAll(p, i) + //{ + // meshTools::writeOBJ(Pout, points0[i]); + //} + //Pout<< "# f1:" << f1 << endl; + //forAll(p, i) + //{ + // meshTools::writeOBJ(Pout, points1[i]); + //} } else { - attractIndices[1] = fp; + // Existing areas + const scalar area0 = f0.mag(localPts); + const scalar area1 = f1.mag(localPts); + + if + ( + area0/area1 >= minAreaRatio + && area1/area0 >= minAreaRatio + ) + { + attractIndices = labelPair(startFp, endFp); + } } } - else - { - // More than two attractions. Skip. - attractIndices = labelPair(-1, -1); - break; - } } } + } + return attractIndices; +} + + +void Foam::autoSnapDriver::splitDiagonals +( + const scalar featureCos, + const scalar concaveCos, + const scalar minAreaRatio, + + const indirectPrimitivePatch& pp, + const vectorField& nearestAttraction, + const vectorField& nearestNormal, + + vectorField& patchAttraction, + List<pointConstraint>& patchConstraints, + DynamicList<label>& splitFaces, + DynamicList<labelPair>& splits +) const +{ + const labelList& bFaces = pp.addressing(); + + splitFaces.clear(); + splitFaces.setCapacity(bFaces.size()); + splits.clear(); + splits.setCapacity(bFaces.size()); + + + // Work arrays for storing points of face + DynamicField<point> facePoints0; + DynamicField<point> facePoints1; + + forAll(bFaces, faceI) + { + const labelPair split + ( + findDiagonalAttraction + ( + featureCos, + concaveCos, + minAreaRatio, + + pp, + patchAttraction, + patchConstraints, + nearestAttraction, + nearestNormal, + faceI, + + facePoints0, + facePoints1 + ) + ); - if (attractIndices[1] == -1) + if (split != labelPair(-1, -1)) { - // Found only one attraction. Skip. - attractIndices = labelPair(-1, -1); + splitFaces.append(bFaces[faceI]); + splits.append(split); + + const face& f = pp.localFaces()[faceI]; + + // Knock out other attractions on face + forAll(f, fp) + { + // Knock out any other constraints + if + ( + fp != split[0] + && fp != split[1] + && patchConstraints[f[fp]].first() >= 2 + ) + { + patchConstraints[f[fp]] = pointConstraint + ( + Tuple2<label, vector> + ( + 1, + nearestNormal[f[fp]] + ) + ); + patchAttraction[f[fp]] = nearestAttraction[f[fp]]; + } + } } } - return attractIndices; } @@ -1556,7 +2021,8 @@ void Foam::autoSnapDriver::avoidDiagonalAttraction if (cosAngle > featureCos) { - // Add the nearest of the other two points as + // The two feature edges are roughly in the same direction. + // Add the nearest of the other points in the face as // attractor label minFp = -1; scalar minDistSqr = GREAT; @@ -1579,8 +2045,7 @@ void Foam::autoSnapDriver::avoidDiagonalAttraction label minPointI = f[minFp]; patchAttraction[minPointI] = mid-pp.localPoints()[minPointI]; - patchConstraints[minPointI] = - patchConstraints[f[diag[0]]]; + patchConstraints[minPointI] = patchConstraints[f[diag[0]]]; } } else @@ -1665,8 +2130,7 @@ Foam::autoSnapDriver::findNearFeatureEdge edgeConstraints[featI][nearInfo.index()].append(c); // Store for later use - patchAttraction[pointI] = - nearInfo.hitPoint()-pp.localPoints()[pointI]; + patchAttraction[pointI] = nearInfo.hitPoint()-pp.localPoints()[pointI]; patchConstraints[pointI] = c; } return Tuple2<label, pointIndexHit>(featI, nearInfo); @@ -1696,6 +2160,8 @@ Foam::autoSnapDriver::findNearFeaturePoint { const refinementFeatures& features = meshRefiner_.features(); + // Search for for featurePoints only! This ignores any non-feature points. + labelList nearFeat; List<pointIndexHit> nearInfo; features.findNearestPoint @@ -1737,8 +2203,7 @@ Foam::autoSnapDriver::findNearFeaturePoint // Store for later use patchAttraction[pointI] = featPt-pt; - patchConstraints[pointI] = - pointConstraints[featI][featPointI]; + patchConstraints[pointI] = pointConstraints[featI][featPointI]; // Reset oldPointI to nearest on feature edge patchAttraction[oldPointI] = vector::zero; @@ -1808,6 +2273,8 @@ void Foam::autoSnapDriver::determineFeatures autoPtr<OBJstream> featureEdgeStr; autoPtr<OBJstream> missedEdgeStr; autoPtr<OBJstream> featurePointStr; + autoPtr<OBJstream> missedMP0Str; + autoPtr<OBJstream> missedMP1Str; if (debug&meshRefinement::ATTRACTION) { @@ -1843,6 +2310,28 @@ void Foam::autoSnapDriver::determineFeatures ); Info<< "Dumping feature-point sampling to " << featurePointStr().name() << endl; + + missedMP0Str.reset + ( + new OBJstream + ( + meshRefiner_.mesh().time().path() + / "missedFeatureEdgeFromMPEdge_" + name(iter) + ".obj" + ) + ); + Info<< "Dumping region-edges that are too far away to " + << missedMP0Str().name() << endl; + + missedMP1Str.reset + ( + new OBJstream + ( + meshRefiner_.mesh().time().path() + / "missedFeatureEdgeFromMPPoint_" + name(iter) + ".obj" + ) + ); + Info<< "Dumping region-points that are too far away to " + << missedMP1Str().name() << endl; } @@ -1854,6 +2343,15 @@ void Foam::autoSnapDriver::determineFeatures { const point& pt = pp.localPoints()[pointI]; + + // Determine the geometric planes the point is (approximately) on. + // This is returned as a + // - attraction vector + // - and a constraint + // (1: attract to surface, constraint is normal of plane + // 2: attract to feature line, constraint is feature line direction + // 3: attract to feature point, constraint is zero) + vector attraction = vector::zero; pointConstraint constraint; @@ -1881,6 +2379,35 @@ void Foam::autoSnapDriver::determineFeatures constraint ); + // Now combine the reconstruction with the current state of the + // point. The logic is quite complicated: + // - the new constraint (from reconstruction) will only win if + // - the constraint is higher (feature-point wins from feature-edge + // etc.) + // - or the constraint is the same but the attraction distance is less + // + // - then this will be combined with explicit searching on the + // features and optionally the analysis of the patches using the + // point. This analysis can do three thing: + // - the point is not on multiple patches + // - the point is on multiple patches but these are also + // different planes (so the region feature is also a geometric + // feature) + // - the point is on multiple patches some of which are on + // the same plane. This is the problem one - do we assume it is + // an additional constraint (feat edge upgraded to region point, + // see below)? + // + // Reconstruction MultiRegionFeatureSnap Attraction + // ------- ---------------------- ----------- + // surface false surface + // surface true region edge + // feat edge false feat edge + // feat edge true and no planar regions feat edge + // feat edge true and yes planar regions region point + // feat point false feat point + // feat point true region point + if ( @@ -1920,7 +2447,7 @@ void Foam::autoSnapDriver::determineFeatures Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge ( - true, // isRegionPoint + true, // isRegionEdge pp, snapDist, pointI, @@ -1951,7 +2478,7 @@ void Foam::autoSnapDriver::determineFeatures { missedEdgeStr().write ( - linePointRef(pt, info.missPoint()) + linePointRef(pt, multiPatchPt.hitPoint()) ); } } @@ -1985,10 +2512,10 @@ void Foam::autoSnapDriver::determineFeatures { if (multiPatchPt.index() == 0) { - // Geometric feature edge is also region edge + // Region edge is also a geometric feature edge nearInfo = findNearFeatureEdge ( - true, // isRegionPoint + true, // isRegionEdge pp, snapDist, pointI, @@ -2001,11 +2528,28 @@ void Foam::autoSnapDriver::determineFeatures patchConstraints ); hasSnapped = true; + + // Debug: dump missed feature point + if + ( + missedMP0Str.valid() + && !nearInfo.second().hit() + ) + { + missedMP0Str().write + ( + linePointRef(pt, estimatedPt) + ); + } } else { // One of planes of feature contains multiple - // regions + // regions. We assume (contentious!) that the + // separation between + // the regions is not aligned with the geometric + // feature so is an additional constraint on the + // point -> is region-feature-point. nearInfo = findNearFeaturePoint ( true, // isRegionPoint @@ -2025,6 +2569,47 @@ void Foam::autoSnapDriver::determineFeatures patchConstraints ); hasSnapped = true; + + // More contentious: if we don't find + // a near feature point we will never find the + // attraction to a feature edge either since + // the edgeAttractors/edgeConstraints do not get + // filled and we're using reverse attraction + // Note that we're in multiRegionFeatureSnap which + // where findMultiPatchPoint can decide the + // wrong thing. So: if failed finding a near + // feature point try for a feature edge + if (!nearInfo.second().hit()) + { + nearInfo = findNearFeatureEdge + ( + true, // isRegionEdge + pp, + snapDist, + pointI, + estimatedPt, + + // Feature-edge to pp point + edgeAttractors, + edgeConstraints, + // pp point to nearest feature + patchAttraction, + patchConstraints + ); + } + + // Debug: dump missed feature point + if + ( + missedMP1Str.valid() + && !nearInfo.second().hit() + ) + { + missedMP1Str().write + ( + linePointRef(pt, estimatedPt) + ); + } } } } @@ -2085,7 +2670,7 @@ void Foam::autoSnapDriver::determineFeatures { missedEdgeStr().write ( - linePointRef(pt, info.missPoint()) + linePointRef(pt, estimatedPt) ); } } @@ -2204,6 +2789,7 @@ void Foam::autoSnapDriver::determineFeatures void Foam::autoSnapDriver::determineBaffleFeatures ( const label iter, + const bool baffleFeaturePoints, const scalar featureCos, const indirectPrimitivePatch& pp, @@ -2258,8 +2844,8 @@ void Foam::autoSnapDriver::determineBaffleFeatures // Detect baffle edges. Assume initial mesh will have 0,90 or 180 // (baffle) degree angles so smoothing should make 0,90 - // to be less than 90. - const scalar baffleFeatureCos = Foam::cos(degToRad(91)); + // to be less than 90. Choose reasonable value + const scalar baffleFeatureCos = Foam::cos(degToRad(110)); autoPtr<OBJstream> baffleEdgeStr; @@ -2308,40 +2894,58 @@ void Foam::autoSnapDriver::determineBaffleFeatures } } - Info<< "Detected " - << returnReduce(nBaffleEdges, sumOp<label>()) + reduce(nBaffleEdges, sumOp<label>()); + + Info<< "Detected " << nBaffleEdges << " baffle edges out of " << returnReduce(pp.nEdges(), sumOp<label>()) << " edges." << endl; - forAll(pp.pointEdges(), pointI) + //- Baffle edges will be too ragged to sensibly determine feature points + //forAll(pp.pointEdges(), pointI) + //{ + // if + // ( + // isFeaturePoint + // ( + // featureCos, + // pp, + // isBaffleEdge, + // pointI + // ) + // ) + // { + // //Pout<< "Detected feature point:" << pp.localPoints()[pointI] + // // << endl; + // //-TEMPORARILY DISABLED: + // //pointStatus[pointI] = 1; + // } + //} + + + label nBafflePoints = 0; + forAll(pointStatus, pointI) { - if - ( - isFeaturePoint - ( - featureCos, - pp, - isBaffleEdge, - pointI - ) - ) + if (pointStatus[pointI] != -1) { - //Pout<< "Detected feature point:" << pp.localPoints()[pointI] - // << endl; - //-TEMPORARILY DISABLED: - //pointStatus[pointI] = 1; + nBafflePoints++; } } + reduce(nBafflePoints, sumOp<label>()); + label nPointAttract = 0; + label nEdgeAttract = 0; + forAll(pointStatus, pointI) { const point& pt = pp.localPoints()[pointI]; if (pointStatus[pointI] == 0) // baffle edge { + // 1: attract to near feature edge first + Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge ( false, // isRegionPoint? @@ -2356,10 +2960,43 @@ void Foam::autoSnapDriver::determineBaffleFeatures patchConstraints ); - if (!nearInfo.second().hit()) + + //- MEJ: + // 2: optionally override with nearest feature point. + // On baffles we don't have enough normals to construct a feature + // point so assume all feature edges are close to feature points + if (nearInfo.second().hit()) { - //Pout<< "*** Failed to find close edge to point " << pt - // << endl; + nEdgeAttract++; + + if (baffleFeaturePoints) + { + nearInfo = findNearFeaturePoint + ( + false, // isRegionPoint, + + pp, + snapDist, + pointI, + pt, // estimatedPt, + + // Feature-point to pp point + pointAttractor, + pointConstraints, + // Feature-edge to pp point + edgeAttractors, + edgeConstraints, + // pp point to nearest feature + patchAttraction, + patchConstraints + ); + + if (nearInfo.first() != -1) + { + nEdgeAttract--; + nPointAttract++; + } + } } } else if (pointStatus[pointI] == 1) // baffle point @@ -2378,6 +3015,8 @@ void Foam::autoSnapDriver::determineBaffleFeatures if (featI != -1) { + nPointAttract++; + label featPointI = nearInfo[0].index(); const point& featPt = nearInfo[0].hitPoint(); scalar distSqr = magSqr(featPt-pt); @@ -2440,7 +3079,7 @@ void Foam::autoSnapDriver::determineBaffleFeatures // << " for baffle-feature-point " << pt // << endl; - findNearFeatureEdge + Tuple2<label, pointIndexHit> nearInfo = findNearFeatureEdge ( false, // isRegionPoint pp, @@ -2453,9 +3092,25 @@ void Foam::autoSnapDriver::determineBaffleFeatures patchAttraction, patchConstraints ); + + if (nearInfo.first() != -1) + { + nEdgeAttract++; + } } } } + + reduce(nPointAttract, sumOp<label>()); + reduce(nEdgeAttract, sumOp<label>()); + + Info<< "Baffle points : " << nBafflePoints + << " of which attracted to :" << nl + << " feature point : " << nPointAttract << nl + << " feature edge : " << nEdgeAttract << nl + << " rest : " << nBafflePoints-nPointAttract-nEdgeAttract + << nl + << endl; } @@ -2518,7 +3173,8 @@ void Foam::autoSnapDriver::reverseAttractMeshPoints } Info<< "Initially selected " << returnReduce(nFeats, sumOp<label>()) - << " points out of " << returnReduce(pp.nPoints(), sumOp<label>()) + << " mesh points out of " + << returnReduce(pp.nPoints(), sumOp<label>()) << " for reverse attraction." << endl; // Make sure is synchronised (note: check if constraint is already @@ -2578,7 +3234,8 @@ void Foam::autoSnapDriver::reverseAttractMeshPoints Info<< "Selected " << returnReduce(attractPoints.size(), sumOp<label>()) - << " points out of " << returnReduce(pp.nPoints(), sumOp<label>()) + << " mesh points out of " + << returnReduce(pp.nPoints(), sumOp<label>()) << " for reverse attraction." << endl; } @@ -2724,13 +3381,21 @@ void Foam::autoSnapDriver::reverseAttractMeshPoints void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges ( const label iter, - const bool avoidSnapProblems, - const scalar featureCos, const bool multiRegionFeatureSnap, + const bool detectBaffles, + const bool baffleFeaturePoints, + + const bool releasePoints, + const bool stringFeatures, + const bool avoidDiagonal, + + const scalar featureCos, + const indirectPrimitivePatch& pp, const scalarField& snapDist, const vectorField& nearestDisp, + const vectorField& nearestNormal, const List<List<point> >& pointFaceSurfNormals, const List<List<point> >& pointFaceDisp, @@ -2742,6 +3407,17 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges ) const { const refinementFeatures& features = meshRefiner_.features(); + const fvMesh& mesh = meshRefiner_.mesh(); + + const PackedBoolList isPatchMasterPoint + ( + meshRefinement::getMasterPoints + ( + mesh, + pp.meshPoints() + ) + ); + // Collect ordered attractions on feature edges // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2802,7 +3478,12 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges rawPatchConstraints ); - + // Print a bit about the attraction from patch point to feature + if (debug) + { + Info<< "Raw geometric feature analysis : "; + writeStats(pp, isPatchMasterPoint, rawPatchConstraints); + } // Baffle handling // ~~~~~~~~~~~~~~~ @@ -2815,25 +3496,35 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges // detected anything. So explicitly pick up feature edges on the pp // (after duplicating points & smoothing so will already have been // expanded) and match these to the features. - determineBaffleFeatures - ( - iter, - featureCos, + if (detectBaffles) + { + determineBaffleFeatures + ( + iter, + baffleFeaturePoints, + featureCos, - pp, - snapDist, + pp, + snapDist, - // Feature-point to pp point - pointAttractor, - pointConstraints, - // Feature-edge to pp point - edgeAttractors, - edgeConstraints, - // pp point to nearest feature - rawPatchAttraction, - rawPatchConstraints - ); + // Feature-point to pp point + pointAttractor, + pointConstraints, + // Feature-edge to pp point + edgeAttractors, + edgeConstraints, + // pp point to nearest feature + rawPatchAttraction, + rawPatchConstraints + ); + } + // Print a bit about the attraction from patch point to feature + if (debug) + { + Info<< "After baffle feature analysis : "; + writeStats(pp, isPatchMasterPoint, rawPatchConstraints); + } // Reverse lookup: Find nearest mesh point to feature edge @@ -2863,6 +3554,12 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges patchConstraints ); + // Print a bit about the attraction from patch point to feature + if (debug) + { + Info<< "Reverse attract feature analysis : "; + writeStats(pp, isPatchMasterPoint, patchConstraints); + } // Dump if (debug&meshRefinement::ATTRACTION) @@ -2900,38 +3597,38 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges } - if (avoidSnapProblems) + //MEJ: any faces that have multi-patch points only keep the + // multi-patch + // points. The other points on the face will be dragged along + // (hopefully) + if (releasePoints) { - //MEJ: any faces that have multi-patch points only keep the multi-patch - // points. The other points on the face will be dragged along - // (hopefully) - if (multiRegionFeatureSnap) - { - releasePointsNextToMultiPatch - ( - iter, - featureCos, - - pp, - snapDist, + releasePointsNextToMultiPatch + ( + iter, + featureCos, - pointFaceCentres, - pointFacePatchID, + pp, + snapDist, - rawPatchAttraction, - rawPatchConstraints, + pointFaceCentres, + pointFacePatchID, - patchAttraction, - patchConstraints - ); - } + rawPatchAttraction, + rawPatchConstraints, + patchAttraction, + patchConstraints + ); + } - // Snap edges to feature edges - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Walk existing edges and snap remaining ones (that are marked as - // feature edges in rawPatchConstraints) + // Snap edges to feature edges + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Walk existing edges and snap remaining ones (that are marked as + // feature edges in rawPatchConstraints) + if (stringFeatures) + { stringFeatureEdges ( iter, @@ -2946,12 +3643,14 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges patchAttraction, patchConstraints ); + } - // Avoid diagonal attraction - // ~~~~~~~~~~~~~~~~~~~~~~~~~ - // Attract one of the non-diagonal points. - + // Avoid diagonal attraction + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // Attract one of the non-diagonal points. + if (avoidDiagonal) + { avoidDiagonalAttraction ( iter, @@ -2962,6 +3661,7 @@ void Foam::autoSnapDriver::featureAttractionUsingFeatureEdges ); } + if (debug&meshRefinement::ATTRACTION) { dumpMove @@ -2983,11 +3683,27 @@ void Foam::autoSnapDriver::preventFaceSqueeze const indirectPrimitivePatch& pp, const scalarField& snapDist, + const vectorField& nearestAttraction, vectorField& patchAttraction, List<pointConstraint>& patchConstraints ) const { + autoPtr<OBJstream> strPtr; + if (debug&meshRefinement::ATTRACTION) + { + strPtr.reset + ( + new OBJstream + ( + meshRefiner_.mesh().time().path() + / "faceSqueeze_" + name(iter) + ".obj" + ) + ); + Info<< "Dumping faceSqueeze corrections to " + << strPtr().name() << endl; + } + pointField points; face singleF; forAll(pp.localFaces(), faceI) @@ -3022,16 +3738,19 @@ void Foam::autoSnapDriver::preventFaceSqueeze if (nConstraints == f.size()) { - scalar oldArea = f.mag(pp.localPoints()); - scalar newArea = singleF.mag(points); - if (newArea < 0.1*oldArea) + if (f.size() == 3) { - // For now remove the point with largest distance + // Triangle: knock out attraction altogether + + // For now keep the points on the longest edge label maxFp = -1; scalar maxS = -1; forAll(f, fp) { - scalar s = magSqr(patchAttraction[f[fp]]); + const point& pt = pp.localPoints()[f[fp]]; + const point& nextPt = pp.localPoints()[f.nextLabel(fp)]; + + scalar s = magSqr(pt-nextPt); if (s > maxS) { maxS = s; @@ -3040,9 +3759,52 @@ void Foam::autoSnapDriver::preventFaceSqueeze } if (maxFp != -1) { - label pointI = f[maxFp]; - // Lower attraction on pointI - patchAttraction[pointI] *= 0.5; + label pointI = f.prevLabel(maxFp); + + // Reset attraction on pointI to nearest + + const point& pt = pp.localPoints()[pointI]; + + //Pout<< "** on triangle " << pp.faceCentres()[faceI] + // << " knocking out attraction to " << pointI + // << " at:" << pt + // << endl; + + patchAttraction[pointI] = nearestAttraction[pointI]; + + if (strPtr.valid()) + { + strPtr().write + ( + linePointRef(pt, pt+patchAttraction[pointI]) + ); + } + } + } + else + { + scalar oldArea = f.mag(pp.localPoints()); + scalar newArea = singleF.mag(points); + if (newArea < 0.1*oldArea) + { + // For now remove the point with largest distance + label maxFp = -1; + scalar maxS = -1; + forAll(f, fp) + { + scalar s = magSqr(patchAttraction[f[fp]]); + if (s > maxS) + { + maxS = s; + maxFp = fp; + } + } + if (maxFp != -1) + { + label pointI = f[maxFp]; + // Lower attraction on pointI + patchAttraction[pointI] *= 0.5; + } } } } @@ -3053,15 +3815,20 @@ void Foam::autoSnapDriver::preventFaceSqueeze Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature ( const snapParameters& snapParams, - const bool avoidSnapProblems, + const bool alignMeshEdges, const label iter, const scalar featureCos, const scalar featureAttract, const scalarField& snapDist, const vectorField& nearestDisp, + const vectorField& nearestNormal, motionSmoother& meshMover, vectorField& patchAttraction, - List<pointConstraint>& patchConstraints + List<pointConstraint>& patchConstraints, + + DynamicList<label>& splitFaces, + DynamicList<labelPair>& splits + ) const { const Switch implicitFeatureAttraction = snapParams.implicitFeatureSnap(); @@ -3080,6 +3847,15 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature const fvMesh& mesh = meshRefiner_.mesh(); + //const PackedBoolList isMasterPoint(syncTools::getMasterPoints(mesh)); + const PackedBoolList isPatchMasterPoint + ( + meshRefinement::getMasterPoints + ( + mesh, + pp.meshPoints() + ) + ); // Per point, per surrounding face: // - faceSurfaceNormal @@ -3112,7 +3888,7 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature // normal of surface at point on surface vectorField faceSurfaceNormal(pp.size(), vector::zero); labelList faceSurfaceGlobalRegion(pp.size(), -1); - vectorField faceRotation(pp.size(), vector::zero); + //vectorField faceRotation(pp.size(), vector::zero); calcNearestFace ( @@ -3121,8 +3897,8 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature faceSnapDist, faceDisp, faceSurfaceNormal, - faceSurfaceGlobalRegion, - faceRotation + faceSurfaceGlobalRegion + //faceRotation ); @@ -3173,7 +3949,6 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature featureAttractionUsingReconstruction ( iter, - avoidSnapProblems, featureCos, pp, @@ -3192,6 +3967,18 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature if (explicitFeatureAttraction) { + // Only do fancy stuff if alignMeshEdges + bool releasePoints = false; + bool stringFeatures = false; + bool avoidDiagonal = false; + if (alignMeshEdges) + { + releasePoints = snapParams.releasePoints(); + stringFeatures = snapParams.stringFeatures(); + avoidDiagonal = snapParams.avoidDiagonal(); + } + + // Sample faces around each point and see if nearest surface normal // differs. For those find the nearest real feature edge/point and // store the correspondence. Then loop over feature edge/point @@ -3201,13 +3988,22 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature featureAttractionUsingFeatureEdges ( iter, - avoidSnapProblems, - featureCos, multiRegionFeatureSnap, + snapParams.detectBaffles(), + snapParams.baffleFeaturePoints(), // all points on baffle edges + // are attracted to feature pts + + releasePoints, + stringFeatures, + avoidDiagonal, + + featureCos, + pp, snapDist, nearestDisp, + nearestNormal, pointFaceSurfNormals, pointFaceDisp, @@ -3219,6 +4015,46 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature ); } + if (!alignMeshEdges) + { + const scalar concaveCos = Foam::cos + ( + degToRad(snapParams.concaveAngle()) + ); + const scalar minAreaRatio = snapParams.minAreaRatio(); + + Info<< "Experimental: introducing face splits to avoid rotating" + << " mesh edges. Splitting faces when" << nl + << indent << "- angle not concave by more than " + << snapParams.concaveAngle() << " degrees" << nl + << indent << "- resulting triangles of similar area " + << " (ratio within " << minAreaRatio << ")" << nl + << endl; + + splitDiagonals + ( + featureCos, + concaveCos, + minAreaRatio, + pp, + + nearestDisp, + nearestNormal, + + patchAttraction, + patchConstraints, + splitFaces, + splits + ); + + if (debug) + { + Info<< "Diagonal attraction feature correction : "; + writeStats(pp, isPatchMasterPoint, patchConstraints); + } + } + + preventFaceSqueeze ( iter, @@ -3226,20 +4062,12 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature pp, snapDist, + nearestDisp, patchAttraction, patchConstraints ); - //const PackedBoolList isMasterPoint(syncTools::getMasterPoints(mesh)); - const PackedBoolList isPatchMasterPoint - ( - meshRefinement::getMasterPoints - ( - mesh, - pp.meshPoints() - ) - ); { vector avgPatchDisp = meshRefinement::gAverage ( @@ -3253,9 +4081,9 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature ); Info<< "Attraction:" << endl - << " linear : max:" << gMaxMagSqr(patchDisp) + << " linear : max:" << gMaxMagSqr(patchDisp) << " avg:" << avgPatchDisp << endl - << " feature : max:" << gMaxMagSqr(patchAttraction) + << " feature : max:" << gMaxMagSqr(patchAttraction) << " avg:" << avgPatchAttr << endl; } @@ -3284,45 +4112,8 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature // Count { - label nMasterPoints = 0; - label nPlanar = 0; - label nEdge = 0; - label nPoint = 0; - - forAll(patchConstraints, pointI) - { - if (isPatchMasterPoint[pointI]) - { - nMasterPoints++; - - if (patchConstraints[pointI].first() == 1) - { - nPlanar++; - } - else if (patchConstraints[pointI].first() == 2) - { - nEdge++; - } - else if (patchConstraints[pointI].first() == 3) - { - nPoint++; - } - } - } - - reduce(nMasterPoints, sumOp<label>()); - reduce(nPlanar, sumOp<label>()); - reduce(nEdge, sumOp<label>()); - reduce(nPoint, sumOp<label>()); - Info<< "Feature analysis : total master points:" - << nMasterPoints - << " attraction to :" << nl - << " feature point : " << nPoint << nl - << " feature edge : " << nEdge << nl - << " nearest surface : " << nPlanar << nl - << " rest : " << nMasterPoints-nPoint-nEdge-nPlanar - << nl - << endl; + Info<< "Feature analysis : "; + writeStats(pp, isPatchMasterPoint, patchConstraints); } @@ -3362,7 +4153,6 @@ Foam::vectorField Foam::autoSnapDriver::calcNearestSurfaceFeature ) ); - // 1. Smoothed all displacement vectorField smoothedPatchDisp = patchDisp; smoothAndConstrain diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverTemplates.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverTemplates.C new file mode 100644 index 0000000000000000000000000000000000000000..f047752b07985e93f6054c9a4f87c3be1c546201 --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/autoSnapDriverTemplates.C @@ -0,0 +1,65 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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 "autoSnapDriver.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +template<class FaceList> +Foam::labelList Foam::autoSnapDriver::getFacePoints +( + const indirectPrimitivePatch& pp, + const FaceList& faces +) +{ + // Could use PrimitivePatch & localFaces to extract points but might just + // as well do it ourselves. + + boolList pointOnZone(pp.nPoints(), false); + + forAll(faces, i) + { + const face& f = faces[i]; + + forAll(f, fp) + { + label meshPointI = f[fp]; + + Map<label>::const_iterator iter = + pp.meshPointMap().find(meshPointI); + + if (iter != pp.meshPointMap().end()) + { + label pointI = iter(); + pointOnZone[pointI] = true; + } + } + } + + return findIndices(pointOnZone, true); +} + + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C index be4272e37a575ab78c3562150e32e824ab7f41ba..a4148b950b5494eb6fa6e48bb12dd12d92d9fc50 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -124,6 +124,14 @@ Foam::layerParameters::layerParameters readScalar(dict.lookup("minThickness")) ), featureAngle_(readScalar(dict.lookup("featureAngle"))), + mergePatchFacesAngle_ + ( + dict.lookupOrDefault<scalar> + ( + "mergePatchFacesAngle", + featureAngle_ + ) + ), concaveAngle_ ( dict.lookupOrDefault("concaveAngle", defaultConcaveAngle) diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H index 21d52fdd6924a75e88f251107a990630d4b052e1..505fb8b07bd1cd7a0f29e186fb6557ec4546b182 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/layerParameters/layerParameters.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -114,6 +114,8 @@ private: scalar featureAngle_; + scalar mergePatchFacesAngle_; + scalar concaveAngle_; label nGrow_; @@ -247,6 +249,11 @@ public: return featureAngle_; } + scalar mergePatchFacesAngle() const + { + return mergePatchFacesAngle_; + } + scalar concaveAngle() const { return concaveAngle_; diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C index 0ed25cf81c7300a9e6fe333ef44018786d6985a5..704dcf16adbc3f6c4c4907871055ca31e39b996a 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,6 +27,8 @@ License #include "unitConversion.H" #include "polyMesh.H" #include "globalIndex.H" +#include "Tuple2.H" +#include "wallPolyPatch.H" // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // @@ -44,7 +46,15 @@ Foam::refinementParameters::refinementParameters(const dictionary& dict) ) ), nBufferLayers_(readLabel(dict.lookup("nCellsBetweenLevels"))), - keepPoints_(pointField(1, dict.lookup("locationInMesh"))), + locationsOutsideMesh_ + ( + dict.lookupOrDefault + ( + "locationsOutsideMesh", + pointField(0) + ) + ), + faceZoneControls_(dict.subOrEmptyDict("faceZoneControls")), allowFreeStandingZoneFaces_(dict.lookup("allowFreeStandingZoneFaces")), useTopologicalSnapDetection_ ( @@ -54,8 +64,35 @@ Foam::refinementParameters::refinementParameters(const dictionary& dict) handleSnapProblems_ ( dict.lookupOrDefault<Switch>("handleSnapProblems", true) + ), + interfaceRefine_ + ( + dict.lookupOrDefault<Switch>("interfaceRefine", true) ) { + point locationInMesh; + if (dict.readIfPresent("locationInMesh", locationInMesh)) + { + locationsInMesh_.append(locationInMesh); + zonesInMesh_.append("noneIfNotSet");// special name for no cellZone + } + + List<Tuple2<point, word> > pointsToZone; + if (dict.readIfPresent("locationsInMesh", pointsToZone)) + { + label nZones = locationsInMesh_.size(); + locationsInMesh_.setSize(nZones+pointsToZone.size()); + zonesInMesh_.setSize(locationsInMesh_.size()); + + forAll(pointsToZone, i) + { + locationsInMesh_[nZones] = pointsToZone[i].first(); + zonesInMesh_[nZones] = pointsToZone[i].second(); + nZones++; + } + } + + scalar featAngle(readScalar(dict.lookup("resolveFeatureAngle"))); if (featAngle < 0 || featAngle > 180) @@ -71,8 +108,68 @@ Foam::refinementParameters::refinementParameters(const dictionary& dict) // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // -Foam::labelList Foam::refinementParameters::findCells(const polyMesh& mesh) -const +Foam::dictionary Foam::refinementParameters::getZoneInfo +( + const word& fzName, + surfaceZonesInfo::faceZoneType& faceType +) const +{ + dictionary patchInfo; + patchInfo.add("type", wallPolyPatch::typeName); + faceType = surfaceZonesInfo::INTERNAL; + + if (faceZoneControls_.found(fzName)) + { + const dictionary& fzDict = faceZoneControls_.subDict(fzName); + + if (fzDict.found("patchInfo")) + { + patchInfo = fzDict.subDict("patchInfo"); + } + + word faceTypeName; + if (fzDict.readIfPresent("faceType", faceTypeName)) + { + faceType = surfaceZonesInfo::faceZoneTypeNames[faceTypeName]; + } + } + return patchInfo; +} + + +Foam::labelList Foam::refinementParameters::addCellZonesToMesh +( + polyMesh& mesh +) const +{ + labelList zoneIDs(zonesInMesh_.size(), -1); + forAll(zonesInMesh_, i) + { + if + ( + zonesInMesh_[i] != word::null + && zonesInMesh_[i] != "none" + && zonesInMesh_[i] != "noneIfNotSet" + ) + { + zoneIDs[i] = surfaceZonesInfo::addCellZone + ( + zonesInMesh_[i], // name + labelList(0), // addressing + mesh + ); + } + } + return zoneIDs; +} + + +Foam::labelList Foam::refinementParameters::findCells +( + const bool checkInsideMesh, + const polyMesh& mesh, + const pointField& locations +) { // Force calculation of tet-diag decomposition (for use in findCell) (void)mesh.tetBasePtIs(); @@ -81,13 +178,13 @@ const globalIndex globalCells(mesh.nCells()); // Cell label per point - labelList cellLabels(keepPoints_.size()); + labelList cellLabels(locations.size()); - forAll(keepPoints_, i) + forAll(locations, i) { - const point& keepPoint = keepPoints_[i]; + const point& location = locations[i]; - label localCellI = mesh.findCell(keepPoint); + label localCellI = mesh.findCell(location); label globalCellI = -1; @@ -98,12 +195,13 @@ const reduce(globalCellI, maxOp<label>()); - if (globalCellI == -1) + if (checkInsideMesh && globalCellI == -1) { FatalErrorIn ( - "refinementParameters::findCells(const polyMesh&) const" - ) << "Point " << keepPoint + "refinementParameters::findCells" + "(const polyMesh&, const pointField&) const" + ) << "Point " << location << " is not inside the mesh or on a face or edge." << nl << "Bounding box of the mesh:" << mesh.bounds() << exit(FatalError); @@ -113,10 +211,9 @@ const label procI = globalCells.whichProcID(globalCellI); label procCellI = globalCells.toLocal(procI, globalCellI); - Info<< "Found point " << keepPoint << " in cell " << procCellI + Info<< "Found point " << location << " in cell " << procCellI << " on processor " << procI << endl; - if (globalCells.isLocal(globalCellI)) { cellLabels[i] = localCellI; @@ -130,4 +227,48 @@ const } +Foam::labelList Foam::refinementParameters::zonedLocations +( + const wordList& zonesInMesh +) +{ + DynamicList<label> indices(zonesInMesh.size()); + + forAll(zonesInMesh, i) + { + if + ( + zonesInMesh[i] == word::null + || zonesInMesh[i] != "noneIfNotSet" + ) + { + indices.append(i); + } + } + return indices; +} + + +Foam::labelList Foam::refinementParameters::unzonedLocations +( + const wordList& zonesInMesh +) +{ + DynamicList<label> indices(0); + + forAll(zonesInMesh, i) + { + if + ( + zonesInMesh[i] != word::null + && zonesInMesh[i] == "noneIfNotSet" + ) + { + indices.append(i); + } + } + return indices; +} + + // ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H index 06b90b07580b41986f45d86db2c77352fc7af2a5..9504efa56726941156392b218c11636391f4c125 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/refinementParameters.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -38,6 +38,8 @@ SourceFiles #include "dictionary.H" #include "pointField.H" #include "Switch.H" +#include "wordPairHashTable.H" +#include "surfaceZonesInfo.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -73,8 +75,21 @@ class refinementParameters //- Number of layers between different refinement levels const label nBufferLayers_; - //- Areas to keep - const pointField keepPoints_; + + // Selection of areas + + //- Areas not to keep + const pointField locationsOutsideMesh_; + + //- Areas to keep + pointField locationsInMesh_; + + //- Region for location + wordList zonesInMesh_; + + //- Information on how to handle faces on faceZones + dictionary faceZoneControls_; + //- FaceZone faces allowed which have owner and neighbour in same // cellZone? @@ -89,6 +104,8 @@ class refinementParameters Switch handleSnapProblems_; + Switch interfaceRefine_; + // Private Member Functions //- Disallow default bitwise copy construct @@ -147,9 +164,21 @@ public: } //- Areas to keep - const pointField& keepPoints() const + const pointField& locationsInMesh() const { - return keepPoints_; + return locationsInMesh_; + } + + //- Per area the zone name + const wordList& zonesInMesh() const + { + return zonesInMesh_; + } + + //- Optional points which are checked to be outside the mesh + const pointField& locationsOutsideMesh() const + { + return locationsOutsideMesh_; } //- Are zone faces allowed only inbetween different cell zones @@ -177,11 +206,39 @@ public: return handleSnapProblems_; } + //- Refine cell with opposite faces with different refinement level + bool interfaceRefine() const + { + return interfaceRefine_; + } + // Other - //- Checks that cells are in mesh. Returns cells they are in. - labelList findCells(const polyMesh&) const; + //- Get patchInfo and faceType for faceZone + dictionary getZoneInfo + ( + const word& fzName, + surfaceZonesInfo::faceZoneType& faceType + ) const; + + //- Add cellZones to mesh. Return indices of cellZones (or -1) + labelList addCellZonesToMesh(polyMesh&) const; + + //- Checks that cells are in mesh. Returns cells (or -1) they + // are in. + static labelList findCells + ( + const bool checkInsideMesh, + const polyMesh&, + const pointField& locations + ); + + //- Extract indices of named locations (so excludes 'keepPoints') + static labelList zonedLocations(const wordList& zonesInMesh); + + //- Extract indices of unnamed locations ('keepPoints') + static labelList unzonedLocations(const wordList& zonesInMesh); }; diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/wordPairHashTable.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/wordPairHashTable.H new file mode 100644 index 0000000000000000000000000000000000000000..f095f1f4e75b1d1cadccc1af024aa98be2f153d3 --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/refinementParameters/wordPairHashTable.H @@ -0,0 +1,56 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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/>. + +Typedef + Foam::wordPairHashTable + +Description + HashTable of Pair<word> + +Typedef + Foam::wordPairHashTable + +Description + HashTable of Pair<word> + +\*---------------------------------------------------------------------------*/ + +#ifndef wordPairHashTable_H +#define wordPairHashTable_H + +#include "Pair.H" +#include "HashTable.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + typedef HashTable<word, Pair<word>, FixedList<word, 2>::Hash<> > + wordPairHashTable; +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C index 364a6aae490a4f36dc44b0b74e2ccbe53640fe54..3f74c035aaf221d9df19be9eb5b1d4779cd4c977 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -31,6 +31,7 @@ License Foam::snapParameters::snapParameters(const dictionary& dict) : nSmoothPatch_(readLabel(dict.lookup("nSmoothPatch"))), + nSmoothInternal_(dict.lookupOrDefault("nSmoothInternal", 0)), snapTol_(readScalar(dict.lookup("tolerance"))), nSmoothDispl_(readLabel(dict.lookup("nSolveIter"))), nSnap_(readLabel(dict.lookup("nRelaxIter"))), @@ -44,7 +45,22 @@ Foam::snapParameters::snapParameters(const dictionary& dict) detectNearSurfacesSnap_ ( dict.lookupOrDefault("detectNearSurfacesSnap", true) - ) + ), + strictRegionSnap_ + ( + dict.lookupOrDefault("strictRegionSnap", false) + ), + detectBaffles_(dict.lookupOrDefault("detectBaffles", true)), + baffleFeaturePoints_(dict.lookupOrDefault("baffleFeaturePoints", false)), + releasePoints_(dict.lookupOrDefault("releasePoints", false)), + stringFeatures_(dict.lookupOrDefault("stringFeatures", true)), + avoidDiagonal_(dict.lookupOrDefault("avoidDiagonal", false)), + nFaceSplitInterval_ + ( + dict.lookupOrDefault("nFaceSplitInterval", labelMin) + ), + concaveAngle_(dict.lookupOrDefault("concaveAngle", 45)), + minAreaRatio_(dict.lookupOrDefault("minAreaRatio", 0.3)) {} diff --git a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H index 5dc4ca34e80a28d9cb494a02ad20b6d7b6d8e131..26982ea79dd7a40a34be89ebf6b99e4a9195047d 100644 --- a/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H +++ b/src/mesh/autoMesh/autoHexMesh/autoHexMeshDriver/snapParameters/snapParameters.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -44,10 +44,8 @@ SourceFiles namespace Foam { -// Class forward declarations - /*---------------------------------------------------------------------------*\ - Class snapParameters Declaration + Class snapParameters Declaration \*---------------------------------------------------------------------------*/ class snapParameters @@ -56,6 +54,8 @@ class snapParameters const label nSmoothPatch_; + const label nSmoothInternal_; + const scalar snapTol_; const label nSmoothDispl_; @@ -72,6 +72,28 @@ class snapParameters const Switch detectNearSurfacesSnap_; + const Switch strictRegionSnap_; + + const Switch detectBaffles_; + + const Switch baffleFeaturePoints_; + + const Switch releasePoints_; + + const Switch stringFeatures_; + + const Switch avoidDiagonal_; + + + //- How often needs face splitting be run + label nFaceSplitInterval_; + + //- When is angle too concave too split + scalar concaveAngle_; + + //- When is face-split not sufficiently diagonal + scalar minAreaRatio_; + // Private Member Functions @@ -101,6 +123,13 @@ public: return nSmoothPatch_; } + //- Number of internal point smoothing iterations (combined with + // nSmoothPatch + label nSmoothInternal() const + { + return nSmoothInternal_; + } + //- Relative distance for points to be attracted by surface // feature point // or edge. True distance is this factor times local @@ -123,30 +152,90 @@ public: return nSnap_; } - label nFeatureSnap() const - { - return nFeatureSnap_; - } - Switch explicitFeatureSnap() const - { - return explicitFeatureSnap_; - } + // Surface snapping specific + + //- Override attraction to nearest with intersection location + // at near surfaces + Switch detectNearSurfacesSnap() const + { + return detectNearSurfacesSnap_; + } + + //- Attract point to corresponding surface region only + Switch strictRegionSnap() const + { + return strictRegionSnap_; + } + + + // Feature edge snapping specific + + label nFeatureSnap() const + { + return nFeatureSnap_; + } + + Switch explicitFeatureSnap() const + { + return explicitFeatureSnap_; + } + + Switch implicitFeatureSnap() const + { + return implicitFeatureSnap_; + } + + Switch multiRegionFeatureSnap() const + { + return multiRegionFeatureSnap_; + } + + Switch detectBaffles() const + { + return detectBaffles_; + } + + Switch baffleFeaturePoints() const + { + return baffleFeaturePoints_; + } + + Switch releasePoints() const + { + return releasePoints_; + } + + Switch stringFeatures() const + { + return stringFeatures_; + } + + Switch avoidDiagonal() const + { + return avoidDiagonal_; + } + + + // Face splitting + + label nFaceSplitInterval() const + { + return nFaceSplitInterval_; + } + + scalar concaveAngle() const + { + return concaveAngle_; + } + + scalar minAreaRatio() const + { + return minAreaRatio_; + } - Switch implicitFeatureSnap() const - { - return implicitFeatureSnap_; - } - Switch multiRegionFeatureSnap() const - { - return multiRegionFeatureSnap_; - } - Switch detectNearSurfacesSnap() const - { - return detectNearSurfacesSnap_; - } }; diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.C index 84d212e4251ce0b684056c4068b6a360210f7237..d44bc26597af40270d792641c7e0edc5b8473d18 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.C +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2013 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -42,9 +42,6 @@ namespace Foam } -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::displacementMeshMoverMotionSolver::displacementMeshMoverMotionSolver diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.H b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.H index 273f5fc301b0c19ecd821f9d9820e22ad20d23c9..26a5be3aa8628942e500e0b631579333a37178e7 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.H +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMeshMoverMotionSolver.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2013 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,8 +25,8 @@ Class Foam::displacementMeshMoverMotionSolver Description - Mesh motion solver for an fvMesh. Based on solving the cell-centre - Laplacian for the motion displacement. + Mesh motion solver for an fvMesh. Uses externalDisplacementMeshMover + to do the mesh motion. SourceFiles displacementMeshMoverMotionSolver.C diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.C new file mode 100644 index 0000000000000000000000000000000000000000..0891b067bc8472964d7b177754c28ae7ff91fbf5 --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.C @@ -0,0 +1,316 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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 "displacementMotionSolverMeshMover.H" +#include "addToRunTimeSelectionTable.H" +#include "pointConstraints.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(displacementMotionSolverMeshMover, 1); + + addToRunTimeSelectionTable + ( + externalDisplacementMeshMover, + displacementMotionSolverMeshMover, + dictionary + ); +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +bool Foam::displacementMotionSolverMeshMover::moveMesh +( + const dictionary& moveDict, + const label nAllowableErrors, + labelList& checkFaces +) +{ + const label nRelaxIter = readLabel(moveDict.lookup("nRelaxIter")); + + meshMover_.setDisplacementPatchFields(); + + Info<< typeName << " : Moving mesh ..." << endl; + + scalar oldErrorReduction = -1; + + bool meshOk = false; + + for (label iter = 0; iter < 2*nRelaxIter; ++ iter) + { + Info<< typeName << " : Iteration " << iter << endl; + + if (iter == nRelaxIter) + { + Info<< typeName + << " : Displacement scaling for error reduction set to 0." + << endl; + oldErrorReduction = meshMover_.setErrorReduction(0.0); + } + + if + ( + meshMover_.scaleMesh + ( + checkFaces, + baffles_, + meshMover_.paramDict(), + moveDict, + true, + nAllowableErrors + ) + ) + { + Info<< typeName << " : Successfully moved mesh" << endl; + meshOk = true; + break; + } + } + + if (oldErrorReduction >= 0) + { + meshMover_.setErrorReduction(oldErrorReduction); + } + + Info<< typeName << " : Finished moving mesh ..." << endl; + + return meshOk; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::displacementMotionSolverMeshMover::displacementMotionSolverMeshMover +( + const dictionary& dict, + const List<labelPair>& baffles, + pointVectorField& pointDisplacement +) +: + externalDisplacementMeshMover(dict, baffles, pointDisplacement), + + solverPtr_ + ( + displacementMotionSolver::New + ( + dict.lookup("solver"), + pointDisplacement.mesh()(), + IOdictionary + ( + IOobject + ( + "motionSolverDict", + pointDisplacement.mesh().time().constant(), + pointDisplacement.db(), + IOobject::NO_READ, + IOobject::NO_WRITE, + false + ), + dict + ), + pointDisplacement, + pointIOField + ( + IOobject + ( + "points0", + pointDisplacement.mesh().time().constant(), + pointDisplacement.db(), + IOobject::NO_READ, + IOobject::NO_WRITE, + false + ), + pointDisplacement.mesh()().points() + ) + ) + ), + + adaptPatchIDs_(getFixedValueBCs(pointDisplacement)), + adaptPatchPtr_(getPatch(mesh(), adaptPatchIDs_)), + + scale_ + ( + IOobject + ( + "scale", + pointDisplacement.time().timeName(), + pointDisplacement.db(), + IOobject::NO_READ, + IOobject::AUTO_WRITE + ), + pMesh(), + dimensionedScalar("scale", dimless, 1.0) + ), + + oldPoints_(mesh().points()), + + meshMover_ + ( + const_cast<polyMesh&>(mesh()), + const_cast<pointMesh&>(pMesh()), + adaptPatchPtr_(), + pointDisplacement, + scale_, + oldPoints_, + adaptPatchIDs_, + dict + ), + + fieldSmoother_(mesh()) +{} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::displacementMotionSolverMeshMover::~displacementMotionSolverMeshMover() +{} + + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +bool Foam::displacementMotionSolverMeshMover::move +( + const dictionary& moveDict, + const label nAllowableErrors, + labelList& checkFaces +) +{ + // Correct and smooth the patch displacements so points next to + // points where the extrusion was disabled also use less extrusion. + // Note that this has to update the pointDisplacement boundary conditions + // as well, not just the internal field. + { + const label nSmoothPatchThickness = readLabel + ( + moveDict.lookup("nSmoothThickness") + ); + + const word minThicknessName = word(moveDict.lookup("minThicknessName")); + + scalarField zeroMinThickness; + + if (minThicknessName == "none") + { + zeroMinThickness = scalarField(adaptPatchPtr_().nPoints(), 0.0); + } + + const scalarField& minThickness = + ( + (minThicknessName == "none") + ? zeroMinThickness + : mesh().lookupObject<scalarField>(minThicknessName) + ); + + const PackedBoolList isPatchMasterPoint + ( + meshRefinement::getMasterPoints + ( + mesh(), + adaptPatchPtr_().meshPoints() + ) + ); + + const PackedBoolList isPatchMasterEdge + ( + meshRefinement::getMasterEdges + ( + mesh(), + adaptPatchPtr_().meshEdges + ( + mesh().edges(), + mesh().pointEdges() + ) + ) + ); + + // Smooth patch displacement + + vectorField displacement + ( + pointDisplacement().internalField(), + adaptPatchPtr_().meshPoints() + ); + + fieldSmoother_.minSmoothField + ( + nSmoothPatchThickness, + isPatchMasterPoint, + isPatchMasterEdge, + adaptPatchPtr_(), + minThickness, + displacement + ); + + + scalar resid = 0; + + forAll(displacement, patchPointI) + { + const label pointI(adaptPatchPtr_().meshPoints()[patchPointI]); + + resid += mag(pointDisplacement()[pointI]-displacement[patchPointI]); + + pointDisplacement()[pointI] = displacement[patchPointI]; + } + + // Take over smoothed displacements on bcs + meshMover_.setDisplacementPatchFields(); + } + + // Use motionSolver to calculate internal displacement + { + solverPtr_->pointDisplacement() == pointDisplacement(); + // Force solving and constraining - just so its pointDisplacement gets + // the correct value + (void)solverPtr_->newPoints(); + pointDisplacement() == solverPtr_->pointDisplacement(); + } + + return moveMesh(moveDict, nAllowableErrors, checkFaces); +} + + +void Foam::displacementMotionSolverMeshMover::movePoints +( + const pointField& p +) +{ + externalDisplacementMeshMover::movePoints(p); + + // Update motion solver for new geometry + solverPtr_->movePoints(p); + + // Update motionSmoother for new geometry (moves adaptPatchPtr_) + meshMover_.movePoints(); + + // Assume current mesh location is correct (reset oldPoints, scale) + meshMover_.correct(); +} + + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.H b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.H new file mode 100644 index 0000000000000000000000000000000000000000..73acb7a8261266cf6f3ebbe3eb5114d1a79e2d37 --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/displacementMotionSolverMeshMover.H @@ -0,0 +1,158 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify i + 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::displacementMotionSolverMeshMover + +Description + Quality-based under-relaxation wrapped around generic + displacementMotionSolver. + + Example of use in layer settings in snappyHexMeshDict: + \verbatim + meshShrinker displacementMotionSolver; + solver displacementLaplacian; + displacementLaplacianCoeffs + { + diffusivity quadratic inverseDistance 1(wall); + } + \endverbatim + +SourceFiles + displacementMotionSolverMeshMover.C + +\*---------------------------------------------------------------------------*/ + +#ifndef displacementMotionSolverMeshMover_H +#define displacementMotionSolverMeshMover_H + +#include "externalDisplacementMeshMover.H" +#include "displacementMotionSolver.H" +#include "motionSmootherAlgo.H" +#include "fieldSmoother.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class displacementMotionSolverMeshMover Declaration +\*---------------------------------------------------------------------------*/ + +class displacementMotionSolverMeshMover +: + public externalDisplacementMeshMover +{ + // Private Data + + //- Mesh motion solver + autoPtr<displacementMotionSolver> solverPtr_; + + //- IDs of fixedValue patches that we can modify + const labelList adaptPatchIDs_; + + //- Combined indirect fixedValue patches that we can modify + autoPtr<indirectPrimitivePatch> adaptPatchPtr_; + + //- Scale factor for displacemen + pointScalarField scale_; + + //- Old point field + pointField oldPoints_; + + //- Mesh mover algorithm + motionSmootherAlgo meshMover_; + + //- Field smoothing + fieldSmoother fieldSmoother_; + + + // Private Member Functions + + //- Apply the mesh mover algorithm + bool moveMesh + ( + const dictionary& moveDict, + const label nAllowableErrors, + labelList& checkFaces + ); + + +public: + + //- Runtime type information + TypeName("displacementMotionSolver"); + + + // Constructors + + //- Construct from a polyMesh and an IOdictionary + displacementMotionSolverMeshMover + ( + const dictionary& dict, + const List<labelPair>& baffles, + pointVectorField& pointDisplacemen + ); + + + //- Destructor + virtual ~displacementMotionSolverMeshMover(); + + + // Member Functions + + //- Move mesh using current pointDisplacement boundary values. + // Return true if succesful (errors on checkFaces less than + // allowable). Updates pointDisplacement. + virtual bool move + ( + const dictionary&, + const label nAllowableErrors, + labelList& checkFaces + ); + + //- Update local data for geometry changes + virtual void movePoints(const pointField&); + + //- Update local data for topology changes + virtual void updateMesh(const mapPolyMesh&) + { + notImplemented + ( + "displacementMotionSolverMeshMover::updateMesh" + "(const mapPolyMesh&)" + ); + } +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.C index 2067b24caceb2e244920c11df214b587a58a27bd..4d5531c330c09816320f63f1afad81a6f273c189 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.C +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2013-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -25,6 +25,7 @@ License #include "externalDisplacementMeshMover.H" #include "mapPolyMesh.H" +#include "zeroFixedValuePointPatchFields.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -35,6 +36,84 @@ namespace Foam } +// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // + +Foam::labelList Foam::externalDisplacementMeshMover::getFixedValueBCs +( + const pointVectorField& field +) +{ + DynamicList<label> adaptPatchIDs; + + forAll(field.boundaryField(), patchI) + { + const pointPatchField<vector>& patchField = + field.boundaryField()[patchI]; + + if (isA<valuePointPatchField<vector> >(patchField)) + { + if (isA<zeroFixedValuePointPatchField<vector> >(patchField)) + { + // Special condition of fixed boundary condition. Does not + // get adapted + } + else + { + adaptPatchIDs.append(patchI); + } + } + } + + return adaptPatchIDs; +} + + +Foam::autoPtr<Foam::indirectPrimitivePatch> +Foam::externalDisplacementMeshMover::getPatch +( + const polyMesh& mesh, + const labelList& patchIDs +) +{ + const polyBoundaryMesh& patches = mesh.boundaryMesh(); + + // Count faces. + label nFaces = 0; + + forAll(patchIDs, i) + { + const polyPatch& pp = patches[patchIDs[i]]; + + nFaces += pp.size(); + } + + // Collect faces. + labelList addressing(nFaces); + nFaces = 0; + + forAll(patchIDs, i) + { + const polyPatch& pp = patches[patchIDs[i]]; + + label meshFaceI = pp.start(); + + forAll(pp, i) + { + addressing[nFaces++] = meshFaceI++; + } + } + + return autoPtr<indirectPrimitivePatch> + ( + new indirectPrimitivePatch + ( + IndirectList<face>(mesh.faces(), addressing), + mesh.points() + ) + ); +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::externalDisplacementMeshMover::externalDisplacementMeshMover diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.H b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.H index 9d98b435b8d0d0d12268c9b422bf99be6b551f16..d37a2bb9f2ee18abd660bce92b1fe42162ee0df3 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.H +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/externalDisplacementMeshMover.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2013 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -67,6 +67,19 @@ protected: pointVectorField& pointDisplacement_; + // Protected Member functions + + //- Extract fixed-value patchfields + static labelList getFixedValueBCs(const pointVectorField&); + + //- Construct patch on selected patches + static autoPtr<indirectPrimitivePatch> getPatch + ( + const polyMesh&, + const labelList& + ); + + private: // Private Member Functions @@ -80,6 +93,7 @@ private: //- Disallow default bitwise assignment void operator=(const externalDisplacementMeshMover&); + public: //- Runtime type information diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.C new file mode 100644 index 0000000000000000000000000000000000000000..d80a7865ac10cb1ebbdcd9c88cf7e4eeee4d4fab --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.C @@ -0,0 +1,299 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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 "fieldSmoother.H" +#include "pointFields.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeNameAndDebug(fieldSmoother, 0); +} + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::fieldSmoother::fieldSmoother(const polyMesh& mesh) +: + mesh_(mesh) +{} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::fieldSmoother::~fieldSmoother() +{} + + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +void Foam::fieldSmoother::smoothNormals +( + const label nIter, + const PackedBoolList& isMeshMasterPoint, + const PackedBoolList& isMeshMasterEdge, + const labelList& fixedPoints, + pointVectorField& normals +) const +{ + // Get smoothly varying internal normals field. + Info<< typeName + << " : Smoothing normals in interior ..." << endl; + + const edgeList& edges = mesh_.edges(); + + // Points that do not change. + PackedBoolList isFixedPoint(mesh_.nPoints()); + + // Internal points that are fixed + forAll(fixedPoints, i) + { + label meshPointI = fixedPoints[i]; + isFixedPoint.set(meshPointI, 1); + } + + // Make sure that points that are coupled to meshPoints but not on a patch + // are fixed as well + syncTools::syncPointList(mesh_, isFixedPoint, maxEqOp<unsigned int>(), 0); + + + // Correspondence between local edges/points and mesh edges/points + const labelList meshPoints(identity(mesh_.nPoints())); + + // Calculate inverse sum of weights + + scalarField edgeWeights(mesh_.nEdges()); + scalarField invSumWeight(meshPoints.size()); + meshRefinement::calculateEdgeWeights + ( + mesh_, + isMeshMasterEdge, + meshPoints, + edges, + edgeWeights, + invSumWeight + ); + + vectorField average; + for (label iter = 0; iter < nIter; iter++) + { + meshRefinement::weightedSum + ( + mesh_, + isMeshMasterEdge, + meshPoints, + edges, + edgeWeights, + normals, + average + ); + average *= invSumWeight; + + // Do residual calculation every so often. + if ((iter % 10) == 0) + { + scalar resid = meshRefinement::gAverage + ( + isMeshMasterPoint, + mag(normals-average)() + ); + Info<< " Iteration " << iter << " residual " << resid << endl; + } + + // Transfer to normals vector field + forAll(average, pointI) + { + if (isFixedPoint.get(pointI) == 0) + { + //full smoothing neighbours + point value + average[pointI] = 0.5*(normals[pointI]+average[pointI]); + normals[pointI] = average[pointI]; + normals[pointI] /= mag(normals[pointI]) + VSMALL; + } + } + } +} + + +void Foam::fieldSmoother::smoothPatchNormals +( + const label nIter, + const PackedBoolList& isPatchMasterPoint, + const PackedBoolList& isPatchMasterEdge, + const indirectPrimitivePatch& adaptPatch, + pointField& normals +) const +{ + const edgeList& edges = adaptPatch.edges(); + const labelList& meshPoints = adaptPatch.meshPoints(); + + // Get smoothly varying internal normals field. + Info<< typeName << " : Smoothing normals ..." << endl; + + scalarField edgeWeights(edges.size()); + scalarField invSumWeight(meshPoints.size()); + meshRefinement::calculateEdgeWeights + ( + mesh_, + isPatchMasterEdge, + meshPoints, + edges, + edgeWeights, + invSumWeight + ); + + + vectorField average; + for (label iter = 0; iter < nIter; iter++) + { + meshRefinement::weightedSum + ( + mesh_, + isPatchMasterEdge, + meshPoints, + edges, + edgeWeights, + normals, + average + ); + average *= invSumWeight; + + // Do residual calculation every so often. + if ((iter % 10) == 0) + { + scalar resid = meshRefinement::gAverage + ( + isPatchMasterPoint, + mag(normals-average)() + ); + Info<< " Iteration " << iter << " residual " << resid << endl; + } + + // Transfer to normals vector field + forAll(average, pointI) + { + // full smoothing neighbours + point value + average[pointI] = 0.5*(normals[pointI]+average[pointI]); + normals[pointI] = average[pointI]; + normals[pointI] /= mag(normals[pointI]) + VSMALL; + } + } +} + + +void Foam::fieldSmoother::smoothLambdaMuDisplacement +( + const label nIter, + const PackedBoolList& isMeshMasterPoint, + const PackedBoolList& isMeshMasterEdge, + const PackedBoolList& isToBeSmoothed, + vectorField& displacement +) const +{ + const edgeList& edges = mesh_.edges(); + + // Correspondence between local edges/points and mesh edges/points + const labelList meshPoints(identity(mesh_.nPoints())); + + // Calculate inverse sum of weights + scalarField edgeWeights(mesh_.nEdges()); + scalarField invSumWeight(meshPoints.size()); + meshRefinement::calculateEdgeWeights + ( + mesh_, + isMeshMasterEdge, + meshPoints, + edges, + edgeWeights, + invSumWeight + ); + + // Get smoothly varying patch field. + Info<< typeName << " : Smoothing displacement ..." << endl; + + const scalar lambda = 0.33; + const scalar mu = -0.34; + + vectorField average; + + for (label iter = 0; iter < nIter; iter++) + { + meshRefinement::weightedSum + ( + mesh_, + isMeshMasterEdge, + meshPoints, + edges, + edgeWeights, + displacement, + average + ); + average *= invSumWeight; + + forAll(displacement, i) + { + if (isToBeSmoothed[i]) + { + displacement[i] = (1-lambda)*displacement[i]+lambda*average[i]; + } + } + + meshRefinement::weightedSum + ( + mesh_, + isMeshMasterEdge, + meshPoints, + edges, + edgeWeights, + displacement, + average + ); + average *= invSumWeight; + + + forAll(displacement, i) + { + if (isToBeSmoothed[i]) + { + displacement[i] = (1-mu)*displacement[i]+mu*average[i]; + } + } + + + // Do residual calculation every so often. + if ((iter % 10) == 0) + { + scalar resid = meshRefinement::gAverage + ( + isMeshMasterPoint, + mag(displacement-average)() + ); + Info<< " Iteration " << iter << " residual " << resid << endl; + } + } +} + + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.H b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.H new file mode 100644 index 0000000000000000000000000000000000000000..bb379f76a3c889367f1bcf0efe5c9ab37831706f --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmoother.H @@ -0,0 +1,141 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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::fieldSmoother + +Description + Utility functions for mesh motion solvers + +SourceFiles + fieldSmoother.C + +\*---------------------------------------------------------------------------*/ + +#ifndef fieldSmoother_H +#define fieldSmoother_H + +#include "meshRefinement.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class fieldSmoother Declaration +\*---------------------------------------------------------------------------*/ + +class fieldSmoother +{ + // Private data + + //- Reference to the poly mesh + const polyMesh& mesh_; + + + // Private Member Functions + + //- Disallow default bitwise copy construct + fieldSmoother(const fieldSmoother&); + + //- Disallow default bitwise assignment + void operator=(const fieldSmoother&); + + +public: + + // Run-time type information + TypeName("fieldSmoother"); + + + // Constructors + + //- Construct from a polyMesh + fieldSmoother(const polyMesh&); + + + //- Destructor + virtual ~fieldSmoother(); + + + // Member Functions + + //- Smooth interior normals + void smoothNormals + ( + const label nIter, + const PackedBoolList& isMeshMasterPoint, + const PackedBoolList& isMeshMasterEdge, + const labelList& fixedPoints, + pointVectorField& normals + ) const; + + //- Smooth patch normals + void smoothPatchNormals + ( + const label nIter, + const PackedBoolList& isPatchMasterPoint, + const PackedBoolList& isPatchMasterEdge, + const indirectPrimitivePatch& adaptPatch, + pointField& normals + ) const; + + //- Smooth a scalar field towards, but not beyond, a minimum value + template <class Type> + void minSmoothField + ( + const label nIter, + const PackedBoolList& isPatchMasterPoint, + const PackedBoolList& isPatchMasterEdge, + const indirectPrimitivePatch& adaptPatch, + const scalarField& fieldMin, + Field<Type>& field + ) const; + + //- Smooth and then un-smooth a displacement + void smoothLambdaMuDisplacement + ( + const label nIter, + const PackedBoolList& isMeshMasterPoint, + const PackedBoolList& isMeshMasterEdge, + const PackedBoolList& isSmoothable, + vectorField& displacement + ) const; +}; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#ifdef NoRepository +# include "fieldSmootherTemplates.C" +#endif + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmootherTemplates.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmootherTemplates.C new file mode 100644 index 0000000000000000000000000000000000000000..be2f2cfd0b4f8883ef58f82783c6e6c948ebbbc3 --- /dev/null +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/fieldSmoother/fieldSmootherTemplates.C @@ -0,0 +1,103 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2015 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 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/>. + +\*---------------------------------------------------------------------------*/ + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +template <class Type> +void Foam::fieldSmoother::minSmoothField +( + const label nIter, + const PackedBoolList& isPatchMasterPoint, + const PackedBoolList& isPatchMasterEdge, + const indirectPrimitivePatch& adaptPatch, + const scalarField& fieldMinMag, + Field<Type>& field +) const +{ + const edgeList& edges = adaptPatch.edges(); + const labelList& meshPoints = adaptPatch.meshPoints(); + + scalarField edgeWeights(edges.size()); + scalarField invSumWeight(meshPoints.size()); + meshRefinement::calculateEdgeWeights + ( + mesh_, + isPatchMasterEdge, + meshPoints, + edges, + edgeWeights, + invSumWeight + ); + + // Get smoothly varying patch field. + Info<< typeName << " : Smoothing field ..." << endl; + + for (label iter = 0; iter < nIter; iter++) + { + Field<Type> average(adaptPatch.nPoints()); + meshRefinement::weightedSum + ( + mesh_, + isPatchMasterEdge, + meshPoints, + edges, + edgeWeights, + field, + average + ); + average *= invSumWeight; + + // Transfer to field + forAll(field, pointI) + { + //full smoothing neighbours + point value + average[pointI] = 0.5*(field[pointI]+average[pointI]); + + // perform monotonic smoothing + if + ( + mag(average[pointI]) < mag(field[pointI]) + && mag(average[pointI]) >= mag(fieldMinMag[pointI]) + ) + { + field[pointI] = average[pointI]; + } + } + + // Do residual calculation every so often. + if ((iter % 10) == 0) + { + scalar resid = meshRefinement::gAverage + ( + isPatchMasterPoint, + mag(field-average)() + ); + Info<< " Iteration " << iter << " residual " << resid << endl; + } + } +} + + +// ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.C b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.C index df17b0ece4a2d35c717f06776fa56929e296aed6..fd993ae20bc07313dd12cadeb2c0eb691f9ff7db 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.C +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -32,7 +32,7 @@ License #include "unitConversion.H" #include "PatchTools.H" #include "OBJstream.H" -#include "pointData.H" +#include "PointData.H" #include "zeroFixedValuePointPatchFields.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -52,241 +52,11 @@ namespace Foam // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // -Foam::labelList Foam::medialAxisMeshMover::getFixedValueBCs -( - const pointVectorField& fld -) -{ - DynamicList<label> adaptPatchIDs; - forAll(fld.boundaryField(), patchI) - { - const pointPatchField<vector>& patchFld = - fld.boundaryField()[patchI]; - - if (isA<valuePointPatchField<vector> >(patchFld)) - { - if (isA<zeroFixedValuePointPatchField<vector> >(patchFld)) - { - // Special condition of fixed boundary condition. Does not - // get adapted - } - else - { - adaptPatchIDs.append(patchI); - } - } - } - return adaptPatchIDs; -} - - -Foam::autoPtr<Foam::indirectPrimitivePatch> -Foam::medialAxisMeshMover::getPatch -( - const polyMesh& mesh, - const labelList& patchIDs -) -{ - const polyBoundaryMesh& patches = mesh.boundaryMesh(); - - // Count faces. - label nFaces = 0; - - forAll(patchIDs, i) - { - const polyPatch& pp = patches[patchIDs[i]]; - - nFaces += pp.size(); - } - - // Collect faces. - labelList addressing(nFaces); - nFaces = 0; - - forAll(patchIDs, i) - { - const polyPatch& pp = patches[patchIDs[i]]; - - label meshFaceI = pp.start(); - - forAll(pp, i) - { - addressing[nFaces++] = meshFaceI++; - } - } - - return autoPtr<indirectPrimitivePatch> - ( - new indirectPrimitivePatch - ( - IndirectList<face>(mesh.faces(), addressing), - mesh.points() - ) - ); -} - - -void Foam::medialAxisMeshMover::smoothPatchNormals -( - const label nSmoothDisp, - const PackedBoolList& isPatchMasterPoint, - const PackedBoolList& isPatchMasterEdge, - pointField& normals -) const -{ - const indirectPrimitivePatch& pp = adaptPatchPtr_(); - const edgeList& edges = pp.edges(); - const labelList& meshPoints = pp.meshPoints(); - - // Get smoothly varying internal normals field. - Info<< typeName << " : Smoothing normals ..." << endl; - - scalarField edgeWeights(edges.size()); - scalarField invSumWeight(meshPoints.size()); - meshRefinement::calculateEdgeWeights - ( - mesh(), - isPatchMasterEdge, - meshPoints, - edges, - edgeWeights, - invSumWeight - ); - - - vectorField average; - for (label iter = 0; iter < nSmoothDisp; iter++) - { - meshRefinement::weightedSum - ( - mesh(), - isPatchMasterEdge, - meshPoints, - edges, - edgeWeights, - normals, - average - ); - average *= invSumWeight; - - // Do residual calculation every so often. - if ((iter % 10) == 0) - { - scalar resid = meshRefinement::gAverage - ( - isPatchMasterPoint, - mag(normals-average)() - ); - Info<< " Iteration " << iter << " residual " << resid << endl; - } - - // Transfer to normals vector field - forAll(average, pointI) - { - // full smoothing neighbours + point value - average[pointI] = 0.5*(normals[pointI]+average[pointI]); - normals[pointI] = average[pointI]; - normals[pointI] /= mag(normals[pointI]) + VSMALL; - } - } -} - - -// Smooth normals in interior. -void Foam::medialAxisMeshMover::smoothNormals -( - const label nSmoothDisp, - const PackedBoolList& isMeshMasterPoint, - const PackedBoolList& isMeshMasterEdge, - const labelList& fixedPoints, - pointVectorField& normals -) const -{ - // Get smoothly varying internal normals field. - Info<< typeName - << " : Smoothing normals in interior ..." << endl; - - const edgeList& edges = mesh().edges(); - - // Points that do not change. - PackedBoolList isFixedPoint(mesh().nPoints()); - - // Internal points that are fixed - forAll(fixedPoints, i) - { - label meshPointI = fixedPoints[i]; - isFixedPoint.set(meshPointI, 1); - } - - // Make sure that points that are coupled to meshPoints but not on a patch - // are fixed as well - syncTools::syncPointList(mesh(), isFixedPoint, maxEqOp<unsigned int>(), 0); - - - // Correspondence between local edges/points and mesh edges/points - const labelList meshPoints(identity(mesh().nPoints())); - - // Calculate inverse sum of weights - - scalarField edgeWeights(mesh().nEdges()); - scalarField invSumWeight(meshPoints.size()); - meshRefinement::calculateEdgeWeights - ( - mesh(), - isMeshMasterEdge, - meshPoints, - edges, - edgeWeights, - invSumWeight - ); - - vectorField average; - for (label iter = 0; iter < nSmoothDisp; iter++) - { - meshRefinement::weightedSum - ( - mesh(), - isMeshMasterEdge, - meshPoints, - edges, - edgeWeights, - normals, - average - ); - average *= invSumWeight; - - // Do residual calculation every so often. - if ((iter % 10) == 0) - { - scalar resid = meshRefinement::gAverage - ( - isMeshMasterPoint, - mag(normals-average)() - ); - Info<< " Iteration " << iter << " residual " << resid << endl; - } - - - // Transfer to normals vector field - forAll(average, pointI) - { - if (isFixedPoint.get(pointI) == 0) - { - //full smoothing neighbours + point value - average[pointI] = 0.5*(normals[pointI]+average[pointI]); - normals[pointI] = average[pointI]; - normals[pointI] /= mag(normals[pointI]) + VSMALL; - } - } - } -} - - // Tries and find a medial axis point. Done by comparing vectors to nearest // wall point for both vertices of edge. bool Foam::medialAxisMeshMover::isMaxEdge ( - const List<pointData>& pointWallDist, + const List<PointData<vector> >& pointWallDist, const label edgeI, const scalar minCos ) const @@ -331,7 +101,7 @@ bool Foam::medialAxisMeshMover::isMaxEdge //- Detect based on extrusion vector differing for both endpoints // the idea is that e.g. a sawtooth wall can still be extruded // successfully as long as it is done all to the same direction. - if ((pointWallDist[e[0]].v() & pointWallDist[e[1]].v()) < minCos) + if ((pointWallDist[e[0]].data() & pointWallDist[e[1]].data()) < minCos) { return true; } @@ -439,11 +209,12 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) pointField pointNormals(PatchTools::pointNormals(mesh(), pp)); // Smooth patch normal vectors - smoothPatchNormals + fieldSmoother_.smoothPatchNormals ( nSmoothSurfaceNormals, isPatchMasterPoint, isPatchMasterEdge, + pp, pointNormals ); @@ -452,7 +223,7 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Distance to wall - List<pointData> pointWallDist(mesh().nPoints()); + List<PointData<vector> > pointWallDist(mesh().nPoints()); // Dummy additional info for PointEdgeWave int dummyTrackData = 0; @@ -461,23 +232,22 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) // 1. Calculate distance to points where displacement is specified. { // Seed data. - List<pointData> wallInfo(meshPoints.size()); + List<PointData<vector> > wallInfo(meshPoints.size()); forAll(meshPoints, patchPointI) { label pointI = meshPoints[patchPointI]; - wallInfo[patchPointI] = pointData + wallInfo[patchPointI] = PointData<vector> ( points[pointI], 0.0, - pointI, // passive scalar pointNormals[patchPointI] // surface normals ); } // Do all calculations - List<pointData> edgeWallDist(mesh().nEdges()); - PointEdgeWave<pointData> wallDistCalc + List<PointData<vector> > edgeWallDist(mesh().nEdges()); + PointEdgeWave<PointData<vector> > wallDistCalc ( mesh(), meshPoints, @@ -525,11 +295,11 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) // 2. Find points with max distance and transport information back to // wall. { - List<pointData> pointMedialDist(mesh().nPoints()); - List<pointData> edgeMedialDist(mesh().nEdges()); + List<pointEdgePoint> pointMedialDist(mesh().nPoints()); + List<pointEdgePoint> edgeMedialDist(mesh().nEdges()); // Seed point data. - DynamicList<pointData> maxInfo(meshPoints.size()); + DynamicList<pointEdgePoint> maxInfo(meshPoints.size()); DynamicList<label> maxPoints(meshPoints.size()); // 1. Medial axis points @@ -556,12 +326,10 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) maxPoints.append(pointI); maxInfo.append ( - pointData + pointEdgePoint ( points[pointI], - 0.0, - pointI, // passive data - vector::zero // passive data + 0.0 ) ); pointMedialDist[pointI] = maxInfo.last(); @@ -612,12 +380,10 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) maxPoints.append(pointI); maxInfo.append ( - pointData + pointEdgePoint ( medialAxisPt, //points[pointI], - magSqr(points[pointI]-medialAxisPt),//0.0, - pointI, // passive data - vector::zero // passive data + magSqr(points[pointI]-medialAxisPt)//0.0 ) ); pointMedialDist[pointI] = maxInfo.last(); @@ -669,12 +435,10 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) maxPoints.append(pointI); maxInfo.append ( - pointData + pointEdgePoint ( points[pointI], - 0.0, - pointI, // passive data - vector::zero // passive data + 0.0 ) ); pointMedialDist[pointI] = maxInfo.last(); @@ -715,7 +479,7 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) // Check if angle not too large. scalar cosAngle = ( - -pointWallDist[pointI].v() + -pointWallDist[pointI].data() & pointNormals[i] ); if (cosAngle > slipFeatureAngleCos) @@ -726,12 +490,10 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) maxPoints.append(pointI); maxInfo.append ( - pointData + pointEdgePoint ( points[pointI], - 0.0, - pointI, // passive data - vector::zero // passive data + 0.0 ) ); pointMedialDist[pointI] = maxInfo.last(); @@ -751,7 +513,7 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) maxPoints.shrink(); // Do all calculations - PointEdgeWave<pointData> medialDistCalc + PointEdgeWave<pointEdgePoint> medialDistCalc ( mesh(), maxPoints, @@ -794,12 +556,12 @@ void Foam::medialAxisMeshMover::update(const dictionary& coeffDict) } else { - dispVec_[i] = pointWallDist[i].v(); + dispVec_[i] = pointWallDist[i].data(); } } // Smooth normal vectors. Do not change normals on pp.meshPoints - smoothNormals + fieldSmoother_.smoothNormals ( nSmoothNormals, isMeshMasterPoint, @@ -1003,79 +765,6 @@ void Foam::medialAxisMeshMover::syncPatchDisplacement } -void Foam::medialAxisMeshMover::minSmoothField -( - const label nSmoothDisp, - const PackedBoolList& isPatchMasterPoint, - const PackedBoolList& isPatchMasterEdge, - const scalarField& fieldMin, - scalarField& field -) const -{ - const indirectPrimitivePatch& pp = adaptPatchPtr_(); - const edgeList& edges = pp.edges(); - const labelList& meshPoints = pp.meshPoints(); - - scalarField edgeWeights(edges.size()); - scalarField invSumWeight(meshPoints.size()); - meshRefinement::calculateEdgeWeights - ( - mesh(), - isPatchMasterEdge, - meshPoints, - edges, - edgeWeights, - invSumWeight - ); - - // Get smoothly varying patch field. - Info<< typeName << " : Smoothing field ..." << endl; - - for (label iter = 0; iter < nSmoothDisp; iter++) - { - scalarField average(pp.nPoints()); - meshRefinement::weightedSum - ( - mesh(), - isPatchMasterEdge, - meshPoints, - edges, - edgeWeights, - field, - average - ); - average *= invSumWeight; - - // Transfer to field - forAll(field, pointI) - { - //full smoothing neighbours + point value - average[pointI] = 0.5*(field[pointI]+average[pointI]); - - // perform monotonic smoothing - if - ( - average[pointI] < field[pointI] - && average[pointI] >= fieldMin[pointI] - ) - { - field[pointI] = average[pointI]; - } - } - - // Do residual calculation every so often. - if ((iter % 10) == 0) - { - scalar resid = meshRefinement::gAverage - ( - isPatchMasterPoint, - mag(field-average)() - ); - Info<< " Iteration " << iter << " residual " << resid << endl; - } - } -} - // Stop layer growth where mesh wraps around edge with a // large feature angle void Foam::medialAxisMeshMover:: @@ -1204,7 +893,8 @@ handleFeatureAngleLayerTerminations //Info<< "Added " // << returnReduce(nPointCounter-nOldPointCounter, sumOp<label>()) - // << " point not to extrude." << endl; + // << " point not to extrude due to minCos " + // << minCos << endl; } @@ -1226,47 +916,43 @@ void Foam::medialAxisMeshMover::findIsolatedRegions const labelListList& pointFaces = pp.pointFaces(); const labelList& meshPoints = pp.meshPoints(); - Info<< typeName << " : Removing isolated regions ..." << endl; - - // Keep count of number of points unextruded - label nPointCounter = 0; - - autoPtr<OBJstream> str; - if (debug) + Info<< typeName << " : Removing isolated regions ..." << nl + << indent << "- if partially extruded faces make angle < " + << Foam::radToDeg(Foam::acos(minCosLayerTermination)) << nl; + if (detectExtrusionIsland) { - str.reset - ( - new OBJstream - ( - mesh().time().path() - / "islandExcludePoints_" - + mesh().time().timeName() - + ".obj" - ) - ); - Info<< typeName - << " : Writing points surrounded by non-extruded points to " - << str().name() << endl; + Info<< indent << "- if exclusively surrounded by non-extruded points" + << nl; } + else + { + Info<< indent << "- if exclusively surrounded by non-extruded faces" + << nl; + } + + // Keep count of number of points unextruded + label nPointCounter = 0; while (true) { // Stop layer growth where mesh wraps around edge with a // large feature angle - handleFeatureAngleLayerTerminations - ( - minCosLayerTermination, - isPatchMasterPoint, - meshEdges, - - extrudeStatus, - patchDisp, - nPointCounter - ); + if (minCosLayerTermination > -1) + { + handleFeatureAngleLayerTerminations + ( + minCosLayerTermination, + isPatchMasterPoint, + meshEdges, - syncPatchDisplacement(minThickness, patchDisp, extrudeStatus); + extrudeStatus, + patchDisp, + nPointCounter + ); + syncPatchDisplacement(minThickness, patchDisp, extrudeStatus); + } // Detect either: @@ -1389,11 +1075,6 @@ void Foam::medialAxisMeshMover::findIsolatedRegions { nPointCounter++; nChanged++; - - if (str.valid()) - { - str().write(pp.points()[meshPoints[patchPointI]]); - } } } } @@ -1483,11 +1164,6 @@ void Foam::medialAxisMeshMover::findIsolatedRegions ) { nPointCounter++; - - if (str.valid()) - { - str().write(pp.points()[meshPoints[f[fp]]]); - } } } } @@ -1501,98 +1177,6 @@ void Foam::medialAxisMeshMover::findIsolatedRegions } -void Foam::medialAxisMeshMover::smoothLambdaMuDisplacement -( - const label nSmoothDisp, - const PackedBoolList& isMeshMasterPoint, - const PackedBoolList& isMeshMasterEdge, - vectorField& displacement -) const -{ - const edgeList& edges = mesh().edges(); - - // Correspondence between local edges/points and mesh edges/points - const labelList meshPoints(identity(mesh().nPoints())); - - // Calculate inverse sum of weights - scalarField edgeWeights(mesh().nEdges()); - scalarField invSumWeight(meshPoints.size()); - meshRefinement::calculateEdgeWeights - ( - mesh(), - isMeshMasterEdge, - meshPoints, - edges, - edgeWeights, - invSumWeight - ); - - // Get smoothly varying patch field. - Info<< typeName << " : Smoothing displacement ..." << endl; - - const scalar lambda = 0.33; - const scalar mu = -0.34; - - vectorField average; - - for (label iter = 0; iter < nSmoothDisp; iter++) - { - meshRefinement::weightedSum - ( - mesh(), - isMeshMasterEdge, - meshPoints, - edges, - edgeWeights, - displacement, - average - ); - average *= invSumWeight; - - forAll(displacement, i) - { - if (medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL) - { - displacement[i] = (1-lambda)*displacement[i]+lambda*average[i]; - } - } - - meshRefinement::weightedSum - ( - mesh(), - isMeshMasterEdge, - meshPoints, - edges, - edgeWeights, - displacement, - average - ); - average *= invSumWeight; - - - forAll(displacement, i) - { - if (medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL) - { - displacement[i] = (1-mu)*displacement[i]+mu*average[i]; - } - } - - - // Do residual calculation every so often. - if ((iter % 10) == 0) - { - scalar resid = meshRefinement::gAverage - ( - isMeshMasterPoint, - mag(displacement-average)() - ); - Info<< " Iteration " << iter << " residual " << resid << endl; - } - } -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::medialAxisMeshMover::medialAxisMeshMover @@ -1630,6 +1214,7 @@ Foam::medialAxisMeshMover::medialAxisMeshMover adaptPatchIDs_, dict ), + fieldSmoother_(mesh()), dispVec_ ( IOobject @@ -1733,10 +1318,12 @@ void Foam::medialAxisMeshMover::calculateDisplacement const scalar featureAngle = readScalar(coeffDict.lookup("featureAngle")); //- Stop layer growth where mesh wraps around sharp edge - const scalar minCosLayerTermination = Foam::cos + scalar layerTerminationAngle = coeffDict.lookupOrDefault<scalar> ( - degToRad(0.5*featureAngle) + "layerTerminationAngle", + 0.5*featureAngle ); + scalar minCosLayerTermination = Foam::cos(degToRad(layerTerminationAngle)); //- Smoothing wanted patch thickness const label nSmoothPatchThickness = readLabel @@ -1791,9 +1378,7 @@ void Foam::medialAxisMeshMover::calculateDisplacement ); - scalarField thickness(patchDisp.size()); - - thickness = mag(patchDisp); + scalarField thickness(mag(patchDisp)); forAll(thickness, patchPointI) { @@ -1860,7 +1445,7 @@ void Foam::medialAxisMeshMover::calculateDisplacement vector n = patchDisp[patchPointI] / (mag(patchDisp[patchPointI]) + VSMALL); - vector mVec = mesh().points()[pointI]-medialVec_[pointI]; + vector mVec = medialVec_[pointI]-mesh().points()[pointI]; mVec /= mag(mVec)+VSMALL; thicknessRatio *= (n&mVec); @@ -1947,14 +1532,17 @@ void Foam::medialAxisMeshMover::calculateDisplacement } - // smooth layer thickness on moving patch - minSmoothField + // Smooth layer thickness on moving patch. Since some locations will have + // disabled the extrusion this might cause big jumps in wanted displacement + // for neighbouring patch points. So smooth the wanted displacement + // before actually trying to move the mesh. + fieldSmoother_.minSmoothField ( nSmoothPatchThickness, isPatchMasterPoint, isPatchMasterEdge, + pp, minThickness, - thickness ); @@ -1962,34 +1550,33 @@ void Foam::medialAxisMeshMover::calculateDisplacement // Dummy additional info for PointEdgeWave int dummyTrackData = 0; - List<pointData> pointWallDist(mesh().nPoints()); + List<PointData<scalar> > pointWallDist(mesh().nPoints()); const pointField& points = mesh().points(); // 1. Calculate distance to points where displacement is specified. // This wave is used to transport layer thickness { // Distance to wall and medial axis on edges. - List<pointData> edgeWallDist(mesh().nEdges()); + List<PointData<scalar> > edgeWallDist(mesh().nEdges()); labelList wallPoints(meshPoints.size()); // Seed data. - List<pointData> wallInfo(meshPoints.size()); + List<PointData<scalar> > wallInfo(meshPoints.size()); forAll(meshPoints, patchPointI) { label pointI = meshPoints[patchPointI]; wallPoints[patchPointI] = pointI; - wallInfo[patchPointI] = pointData + wallInfo[patchPointI] = PointData<scalar> ( points[pointI], 0.0, - thickness[patchPointI], // transport layer thickness - vector::zero // passive vector + thickness[patchPointI] // transport layer thickness ); } // Do all calculations - PointEdgeWave<pointData> wallDistCalc + PointEdgeWave<PointData<scalar> > wallDistCalc ( mesh(), wallPoints, @@ -2016,12 +1603,12 @@ void Foam::medialAxisMeshMover::calculateDisplacement { // 1) displacement on nearest wall point, scaled by medialRatio // (wall distance / medial distance) - // 2) pointWallDist[pointI].s() is layer thickness transported + // 2) pointWallDist[pointI].data() is layer thickness transported // from closest wall point. // 3) shrink in opposite direction of addedPoints displacement[pointI] = -medialRatio_[pointI] - *pointWallDist[pointI].s() + *pointWallDist[pointI].data() *dispVec_[pointI]; } } @@ -2030,11 +1617,20 @@ void Foam::medialAxisMeshMover::calculateDisplacement // Smear displacement away from fixed values (medialRatio=0 or 1) if (nSmoothDisplacement > 0) { - smoothLambdaMuDisplacement + PackedBoolList isToBeSmoothed(displacement.size(), false); + + forAll(displacement, i) + { + isToBeSmoothed[i] = + medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL; + } + + fieldSmoother_.smoothLambdaMuDisplacement ( nSmoothDisplacement, isMeshMasterPoint, isMeshMasterEdge, + isToBeSmoothed, displacement ); } @@ -2052,8 +1648,6 @@ bool Foam::medialAxisMeshMover::shrinkMesh const label nSnap = readLabel(meshQualityDict.lookup("nRelaxIter")); - - // Make sure displacement boundary conditions is uptodate with // internal field meshMover_.setDisplacementPatchFields(); @@ -2119,14 +1713,6 @@ bool Foam::medialAxisMeshMover::move const word minThicknessName = word(moveDict.lookup("minThicknessName")); - // The points have moved so before calculation update - // the mesh and motionSolver accordingly - movePoints(mesh().points()); - // - //// Update any point motion bcs (e.g. timevarying) - //pointDisplacement_.boundaryField().updateCoeffs(); - - // Extract out patch-wise displacement const indirectPrimitivePatch& pp = adaptPatchPtr_(); @@ -2180,13 +1766,10 @@ void Foam::medialAxisMeshMover::movePoints(const pointField& p) { externalDisplacementMeshMover::movePoints(p); - // Update local data for new geometry - adaptPatchPtr_().movePoints(p); - - // Update motionSmoother for new geometry + // Update motionSmoother for new geometry (moves adaptPatchPtr_) meshMover_.movePoints(); - // Assume corrent mesh location is correct + // Assume corrent mesh location is correct (reset oldPoints, scale) meshMover_.correct(); } diff --git a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.H b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.H index a060a04fe6fb2ea685ee0f812a341b993d24eec4..b235182f6897564d0faeee26fd8bdf461a5c2b3e 100644 --- a/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.H +++ b/src/mesh/autoMesh/autoHexMesh/externalDisplacementMeshMover/medialAxisMeshMover.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2014-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -28,10 +28,13 @@ Description Mesh motion solver that uses a medial axis algorithm to work out a fraction between the (nearest point on a) moving surface and the (nearest point on a) fixed surface. + This fraction is then used to scale the motion. Use - fixedValue on all moving patches - zeroFixedValue on stationary patches - slip on all slipping patches + Note that the fixedValue boundary conditions might be changed by this + solver to enforce consistency and a valid resulting mesh. SourceFiles medialAxisMeshMover.C @@ -44,13 +47,15 @@ SourceFiles #include "externalDisplacementMeshMover.H" #include "motionSmootherAlgo.H" #include "autoLayerDriver.H" +#include "fieldSmoother.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { -class pointData; +template <class DataType> +class PointData; /*---------------------------------------------------------------------------*\ Class medialAxisMeshMover Declaration @@ -75,6 +80,9 @@ class medialAxisMeshMover //- Mesh mover algorithm motionSmootherAlgo meshMover_; + //- Field smoothing + fieldSmoother fieldSmoother_; + // Pre-calculated medial axis information @@ -95,45 +103,16 @@ class medialAxisMeshMover // Private Member Functions - //- Extract fixed-value patchfields - static labelList getFixedValueBCs(const pointVectorField&); - //- Extract bc types. Replace fixedValue derivatives with fixedValue wordList getPatchFieldTypes(const pointVectorField& fld); - //- Construct patch on selected patches - static autoPtr<indirectPrimitivePatch> getPatch - ( - const polyMesh&, - const labelList& - ); - // Calculation of medial axis information - //- Smooth normals on patch - void smoothPatchNormals - ( - const label nSmoothDisp, - const PackedBoolList& isMasterPoint, - const PackedBoolList& isMasterEdge, - pointField& normals - ) const; - - //- Smooth normals on interior - void smoothNormals - ( - const label nSmoothDisp, - const PackedBoolList& isMasterPoint, - const PackedBoolList& isMasterEdge, - const labelList& fixedPoints, - pointVectorField& normals - ) const; - //- Is mesh edge on a cusp of displacement bool isMaxEdge ( - const List<pointData>& pointWallDist, + const List<PointData<vector> >& pointWallDist, const label edgeI, const scalar minCos ) const; @@ -161,23 +140,6 @@ class medialAxisMeshMover List<autoLayerDriver::extrudeMode>& extrudeStatus ) const; - void smoothLambdaMuDisplacement - ( - const label nSmoothDisp, - const PackedBoolList& isMasterPoint, - const PackedBoolList& isMasterEdge, - vectorField& displacement - ) const; - - void minSmoothField - ( - const label nSmoothDisp, - const PackedBoolList& isMasterPoint, - const PackedBoolList& isMasterEdge, - const scalarField& fieldMin, - scalarField& field - ) const; - //- Stop layer growth at feature edges void handleFeatureAngleLayerTerminations ( diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C index 5d96aafca974d709eb1310271ea6c757865b9152..7b77e35875e5d22bbdf48523e3822b951c969283 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -29,7 +29,6 @@ License #include "surfaceMesh.H" #include "syncTools.H" #include "Time.H" -#include "refinementHistory.H" #include "refinementSurfaces.H" #include "refinementFeatures.H" #include "decompositionMethod.H" @@ -56,6 +55,8 @@ License #include "treeBoundBox.H" #include "zeroGradientFvPatchFields.H" #include "fvMeshTools.H" +#include "motionSmoother.H" +#include "faceSet.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -176,7 +177,13 @@ void Foam::meshRefinement::calcNeighbourData label own = faceCells[i]; label ownLevel = cellLevel[own]; - label faceLevel = meshCutter_.getAnchorLevel(pp.start()+i); + label faceLevel = meshCutter_.faceLevel(pp.start()+i); + if (faceLevel < 0) + { + // Due to e.g. face merging no longer a consistent + // refinementlevel of face. Assume same as cell + faceLevel = ownLevel; + } // Normal distance from face centre to cell centre scalar d = ((faceCentres[i] - cellCentres[own]) & fn); @@ -284,6 +291,7 @@ void Foam::meshRefinement::updateIntersections(const labelList& changedFaces) labelList surfaceLevel; surfaces_.findHigherIntersection ( + shells_, start, end, labelList(start.size(), -1), // accept any intersection @@ -495,6 +503,7 @@ void Foam::meshRefinement::checkData() labelList surfaceLevel; surfaces_.findHigherIntersection ( + shells_, start, end, labelList(start.size(), -1), // accept any intersection @@ -545,7 +554,13 @@ void Foam::meshRefinement::checkData() << " current:" << surfaceHit[faceI] << " ownCc:" << mesh_.cellCentres()[mesh_.faceOwner()[faceI]] + << " neiCc:" + << neiCc[faceI-mesh_.nInternalFaces()] << " end:" << end[faceI] + << " ownLevel:" + << meshCutter_.cellLevel()[mesh_.faceOwner()[faceI]] + << " faceLevel:" + << meshCutter_.faceLevel(faceI) << endl; } } @@ -678,20 +693,19 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::doRemoveCells // Split faces -Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitFaces +void Foam::meshRefinement::doSplitFaces ( const labelList& splitFaces, - const labelPairList& splits -) + const labelPairList& splits, + //const List<Pair<point> >& splitPoints, + polyTopoChange& meshMod +) const { - polyTopoChange meshMod(mesh_); - forAll(splitFaces, i) { label faceI = splitFaces[i]; const face& f = mesh_.faces()[faceI]; - // Split as start and end index in face const labelPair& split = splits[i]; @@ -744,9 +758,9 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitFaces } -Pout<< "face:" << faceI << " verts:" << f - << " split into f0:" << f0 - << " f1:" << f1 << endl; + //Pout<< "face:" << faceI << " verts:" << f + // << " split into f0:" << f0 + // << " f1:" << f1 << endl; // Change/add faces meshMod.modifyFace @@ -773,396 +787,394 @@ Pout<< "face:" << faceI << " verts:" << f zoneI, // zone for face zoneFlip // face flip in zone ); + + + //// Move points + //meshMod.modifyPoint + //( + // f[split[0]], + // splitPoints[i][0], + // -1, + // true + //); + //meshMod.modifyPoint + //( + // f[split[1]], + // splitPoints[i][1], + // -1, + // true + //); } +} +Foam::label Foam::meshRefinement::splitFacesUndo +( + const labelList& splitFaces, + const labelPairList& splits, + const dictionary& motionDict, - // Change the mesh (no inflation) - autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + labelList& duplicateFace, + List<labelPair>& baffles +) +{ + label nSplit = returnReduce(splitFaces.size(), sumOp<label>()); + Info<< nl + << "Splitting " << nSplit + << " faces across diagonals" << "." << nl << endl; + + // To undo: store original faces + faceList originalFaces(UIndirectList<face>(mesh_.faces(), splitFaces)); + labelPairList facePairs(splitFaces.size(), labelPair(-1, -1)); - // Update fields - mesh_.updateMesh(map); - // Move mesh (since morphing might not do this) - if (map().hasMotionPoints()) - { - mesh_.movePoints(map().preMotionPoints()); - } - else { - // Delete mesh volumes. No other way to do this? - mesh_.clearOut(); + polyTopoChange meshMod(mesh_); + meshMod.setCapacity + ( + meshMod.points().size(), + meshMod.faces().size()+splitFaces.size(), + mesh_.nCells() + ); + + // Insert the mesh changes + doSplitFaces(splitFaces, splits, meshMod); + + // Change the mesh (no inflation) + autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + + // Update fields + mesh_.updateMesh(map); + + // Move mesh (since morphing might not do this) + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); + setInstance(mesh_.facesInstance()); + + + // Update local mesh data + // ~~~~~~~~~~~~~~~~~~~~~~ + + forAll(originalFaces, i) + { + inplaceRenumber(map().reversePointMap(), originalFaces[i]); + } + + { + Map<label> splitFaceToIndex(2*splitFaces.size()); + forAll(splitFaces, i) + { + splitFaceToIndex.insert(splitFaces[i], i); + } + + forAll(map().faceMap(), faceI) + { + label oldFaceI = map().faceMap()[faceI]; + Map<label>::iterator oldFaceFnd = splitFaceToIndex.find + ( + oldFaceI + ); + if (oldFaceFnd != splitFaceToIndex.end()) + { + labelPair& twoFaces = facePairs[oldFaceFnd()]; + if (twoFaces[0] == -1) + { + twoFaces[0] = faceI; + } + else if (twoFaces[1] == -1) + { + twoFaces[1] = faceI; + } + else + { + FatalErrorIn("meshRefinement::splitFacesUndo()") + << "problem: twoFaces:" << twoFaces + << exit(FatalError); + } + } + } + } + + + // Update baffle data + // ~~~~~~~~~~~~~~~~~~ + + if (duplicateFace.size()) + { + meshRefinement::updateList + ( + map().faceMap(), + label(-1), + duplicateFace + ); + } + + const labelList& oldToNewFaces = map().reverseFaceMap(); + forAll(baffles, i) + { + labelPair& baffle = baffles[i]; + baffle.first() = oldToNewFaces[baffle.first()]; + baffle.second() = oldToNewFaces[baffle.second()]; + + if (baffle.first() == -1 || baffle.second() == -1) + { + FatalErrorIn("meshRefinement::splitFacesUndo()") + << "Removed baffle : faces:" << baffle + << exit(FatalError); + } + } + + + // Update insersections + // ~~~~~~~~~~~~~~~~~~~~ + + { + DynamicList<label> changedFaces(facePairs.size()); + forAll(facePairs, i) + { + changedFaces.append(facePairs[i].first()); + changedFaces.append(facePairs[i].second()); + } + + // Update intersections on changed faces + updateMesh(map, growFaceCellFace(changedFaces)); + } } - // Reset the instance for if in overwrite mode - mesh_.setInstance(timeName()); - setInstance(mesh_.facesInstance()); - // Update local mesh data - const labelList& oldToNew = map().reverseFaceMap(); - labelList newSplitFaces(renumber(oldToNew, splitFaces)); - // Add added faces (every splitFaces becomes two faces) - label sz = newSplitFaces.size(); - newSplitFaces.setSize(2*sz); - forAll(map().faceMap(), faceI) + + // Undo loop + // ~~~~~~~~~ + // Maintains two bits of information: + // facePairs : two faces originating from the same face + // originalFaces : original face in current vertices + + + for (label iteration = 0; iteration < 100; iteration++) { - label oldFaceI = map().faceMap()[faceI]; - if (oldToNew[oldFaceI] != faceI) + Info<< nl + << "Undo iteration " << iteration << nl + << "----------------" << endl; + + + // Check mesh for errors + // ~~~~~~~~~~~~~~~~~~~~~ + + faceSet errorFaces + ( + mesh_, + "errorFaces", + mesh_.nFaces()-mesh_.nInternalFaces() + ); + bool hasErrors = motionSmoother::checkMesh + ( + false, // report + mesh_, + motionDict, + errorFaces + ); + if (!hasErrors) { - // Added face - newSplitFaces[sz++] = faceI; + break; } - } - updateMesh(map, newSplitFaces); + // Extend faces + { + const labelList grownFaces(growFaceCellFace(errorFaces)); + errorFaces.clear(); + errorFaces.insert(grownFaces); + } - return map; -} + // Merge face pairs + // ~~~~~~~~~~~~~~~~ + // (if one of the faces is in the errorFaces set) -//// Determine for multi-processor regions the lowest numbered cell on the -//// lowest numbered processor. -//void Foam::meshRefinement::getCoupledRegionMaster -//( -// const globalIndex& globalCells, -// const boolList& blockedFace, -// const regionSplit& globalRegion, -// Map<label>& regionToMaster -//) const -//{ -// const polyBoundaryMesh& patches = mesh_.boundaryMesh(); -// -// forAll(patches, patchI) -// { -// const polyPatch& pp = patches[patchI]; -// -// if (isA<processorPolyPatch>(pp)) -// { -// forAll(pp, i) -// { -// label faceI = pp.start()+i; -// -// if (!blockedFace[faceI]) -// { -// // Only if there is a connection to the neighbour -// // will there be a multi-domain region; if not through -// // this face then through another. -// -// label cellI = mesh_.faceOwner()[faceI]; -// label globalCellI = globalCells.toGlobal(cellI); -// -// Map<label>::iterator iter = -// regionToMaster.find(globalRegion[cellI]); -// -// if (iter != regionToMaster.end()) -// { -// label master = iter(); -// iter() = min(master, globalCellI); -// } -// else -// { -// regionToMaster.insert -// ( -// globalRegion[cellI], -// globalCellI -// ); -// } -// } -// } -// } -// } -// -// // Do reduction -// Pstream::mapCombineGather(regionToMaster, minEqOp<label>()); -// Pstream::mapCombineScatter(regionToMaster); -//} -// -// -//void Foam::meshRefinement::calcLocalRegions -//( -// const globalIndex& globalCells, -// const labelList& globalRegion, -// const Map<label>& coupledRegionToMaster, -// const scalarField& cellWeights, -// -// Map<label>& globalToLocalRegion, -// pointField& localPoints, -// scalarField& localWeights -//) const -//{ -// globalToLocalRegion.resize(globalRegion.size()); -// DynamicList<point> localCc(globalRegion.size()/2); -// DynamicList<scalar> localWts(globalRegion.size()/2); -// -// forAll(globalRegion, cellI) -// { -// Map<label>::const_iterator fndMaster = -// coupledRegionToMaster.find(globalRegion[cellI]); -// -// if (fndMaster != coupledRegionToMaster.end()) -// { -// // Multi-processor region. -// if (globalCells.toGlobal(cellI) == fndMaster()) -// { -// // I am master. Allocate region for me. -// globalToLocalRegion.insert -// ( -// globalRegion[cellI], -// localCc.size() -// ); -// localCc.append(mesh_.cellCentres()[cellI]); -// localWts.append(cellWeights[cellI]); -// } -// } -// else -// { -// // Single processor region. -// if -// ( -// globalToLocalRegion.insert -// ( -// globalRegion[cellI], -// localCc.size() -// ) -// ) -// { -// localCc.append(mesh_.cellCentres()[cellI]); -// localWts.append(cellWeights[cellI]); -// } -// } -// } -// -// localPoints.transfer(localCc); -// localWeights.transfer(localWts); -// -// if (localPoints.size() != globalToLocalRegion.size()) -// { -// FatalErrorIn("calcLocalRegions(..)") -// << "localPoints:" << localPoints.size() -// << " globalToLocalRegion:" << globalToLocalRegion.size() -// << exit(FatalError); -// } -//} -// -// -//Foam::label Foam::meshRefinement::getShiftedRegion -//( -// const globalIndex& indexer, -// const Map<label>& globalToLocalRegion, -// const Map<label>& coupledRegionToShifted, -// const label globalRegion -//) -//{ -// Map<label>::const_iterator iter = -// globalToLocalRegion.find(globalRegion); -// -// if (iter != globalToLocalRegion.end()) -// { -// // Region is 'owned' locally. Convert local region index into global. -// return indexer.toGlobal(iter()); -// } -// else -// { -// return coupledRegionToShifted[globalRegion]; -// } -//} -// -// -//// Add if not yet present -//void Foam::meshRefinement::addUnique(const label elem, labelList& lst) -//{ -// if (findIndex(lst, elem) == -1) -// { -// label sz = lst.size(); -// lst.setSize(sz+1); -// lst[sz] = elem; -// } -//} -// -// -//void Foam::meshRefinement::calcRegionRegions -//( -// const labelList& globalRegion, -// const Map<label>& globalToLocalRegion, -// const Map<label>& coupledRegionToMaster, -// labelListList& regionRegions -//) const -//{ -// // Global region indexing since we now know the shifted regions. -// globalIndex shiftIndexer(globalToLocalRegion.size()); -// -// // Redo the coupledRegionToMaster to be in shifted region indexing. -// Map<label> coupledRegionToShifted(coupledRegionToMaster.size()); -// forAllConstIter(Map<label>, coupledRegionToMaster, iter) -// { -// label region = iter.key(); -// -// Map<label>::const_iterator fndRegion = globalToLocalRegion.find -// (region); -// -// if (fndRegion != globalToLocalRegion.end()) -// { -// // A local cell is master of this region. Get its shifted region. -// coupledRegionToShifted.insert -// ( -// region, -// shiftIndexer.toGlobal(fndRegion()) -// ); -// } -// Pstream::mapCombineGather(coupledRegionToShifted, minEqOp<label>()); -// Pstream::mapCombineScatter(coupledRegionToShifted); -// } -// -// -// // Determine region-region connectivity. -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// // This is for all my regions (so my local ones or the ones I am master -// // of) the neighbouring region indices. -// -// -// // Transfer lists. -// PtrList<HashSet<edge, Hash<edge> > > regionConnectivity -// (Pstream::nProcs()); -// forAll(regionConnectivity, procI) -// { -// if (procI != Pstream::myProcNo()) -// { -// regionConnectivity.set -// ( -// procI, -// new HashSet<edge, Hash<edge> > -// ( -// coupledRegionToShifted.size() -// / Pstream::nProcs() -// ) -// ); -// } -// } -// -// -// // Connectivity. For all my local regions the connected regions. -// regionRegions.setSize(globalToLocalRegion.size()); -// -// // Add all local connectivity to regionRegions; add all non-local -// // to the transferlists. -// for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) -// { -// label ownRegion = globalRegion[mesh_.faceOwner()[faceI]]; -// label neiRegion = globalRegion[mesh_.faceNeighbour()[faceI]]; -// -// if (ownRegion != neiRegion) -// { -// label shiftOwnRegion = getShiftedRegion -// ( -// shiftIndexer, -// globalToLocalRegion, -// coupledRegionToShifted, -// ownRegion -// ); -// label shiftNeiRegion = getShiftedRegion -// ( -// shiftIndexer, -// globalToLocalRegion, -// coupledRegionToShifted, -// neiRegion -// ); -// -// -// // Connection between two regions. Send to owner of region. -// // - is ownRegion 'owned' by me -// // - is neiRegion 'owned' by me -// -// if (shiftIndexer.isLocal(shiftOwnRegion)) -// { -// label localI = shiftIndexer.toLocal(shiftOwnRegion); -// addUnique(shiftNeiRegion, regionRegions[localI]); -// } -// else -// { -// label masterProc = shiftIndexer.whichProcID(shiftOwnRegion); -// regionConnectivity[masterProc].insert -// ( -// edge(shiftOwnRegion, shiftNeiRegion) -// ); -// } -// -// if (shiftIndexer.isLocal(shiftNeiRegion)) -// { -// label localI = shiftIndexer.toLocal(shiftNeiRegion); -// addUnique(shiftOwnRegion, regionRegions[localI]); -// } -// else -// { -// label masterProc = shiftIndexer.whichProcID(shiftNeiRegion); -// regionConnectivity[masterProc].insert -// ( -// edge(shiftOwnRegion, shiftNeiRegion) -// ); -// } -// } -// } -// -// -// // Send -// forAll(regionConnectivity, procI) -// { -// if (procI != Pstream::myProcNo()) -// { -// OPstream str(Pstream::blocking, procI); -// str << regionConnectivity[procI]; -// } -// } -// // Receive -// forAll(regionConnectivity, procI) -// { -// if (procI != Pstream::myProcNo()) -// { -// IPstream str(Pstream::blocking, procI); -// str >> regionConnectivity[procI]; -// } -// } -// -// // Add to addressing. -// forAll(regionConnectivity, procI) -// { -// if (procI != Pstream::myProcNo()) -// { -// for -// ( -// HashSet<edge, Hash<edge> >::const_iterator iter = -// regionConnectivity[procI].begin(); -// iter != regionConnectivity[procI].end(); -// ++iter -// ) -// { -// const edge& e = iter.key(); -// -// bool someLocal = false; -// if (shiftIndexer.isLocal(e[0])) -// { -// label localI = shiftIndexer.toLocal(e[0]); -// addUnique(e[1], regionRegions[localI]); -// someLocal = true; -// } -// if (shiftIndexer.isLocal(e[1])) -// { -// label localI = shiftIndexer.toLocal(e[1]); -// addUnique(e[0], regionRegions[localI]); -// someLocal = true; -// } -// -// if (!someLocal) -// { -// FatalErrorIn("calcRegionRegions(..)") -// << "Received from processor " << procI -// << " connection " << e -// << " where none of the elements is local to me." -// << abort(FatalError); -// } -// } -// } -// } -//} + polyTopoChange meshMod(mesh_); + + // Indices (in facePairs) of merged faces + labelHashSet mergedIndices(facePairs.size()); + forAll(facePairs, index) + { + const labelPair& twoFaces = facePairs[index]; + + if + ( + errorFaces.found(twoFaces.first()) + || errorFaces.found(twoFaces.second()) + ) + { + const face& originalFace = originalFaces[index]; + + + // Determine face properties + label own = mesh_.faceOwner()[twoFaces[0]]; + label nei = -1; + label patchI = -1; + if (twoFaces[0] >= mesh_.nInternalFaces()) + { + patchI = mesh_.boundaryMesh().whichPatch(twoFaces[0]); + } + else + { + nei = mesh_.faceNeighbour()[twoFaces[0]]; + } + + label zoneI = mesh_.faceZones().whichZone(twoFaces[0]); + bool zoneFlip = false; + if (zoneI != -1) + { + const faceZone& fz = mesh_.faceZones()[zoneI]; + zoneFlip = fz.flipMap()[fz.whichFace(twoFaces[0])]; + } + + // Modify first face + meshMod.modifyFace + ( + originalFace, // modified face + twoFaces[0], // label of face + own, // owner + nei, // neighbour + false, // face flip + patchI, // patch for face + zoneI, // zone for face + zoneFlip // face flip in zone + ); + // Merge second face into first + meshMod.removeFace(twoFaces[1], twoFaces[0]); + + mergedIndices.insert(index); + } + } + + label n = returnReduce(mergedIndices.size(), sumOp<label>()); + + Info<< "Detected " << n + << " split faces that will be restored to their original faces." + << nl << endl; + + if (n == 0) + { + // Nothing to be restored + break; + } + + nSplit -= n; + + + // Change the mesh (no inflation) + autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + + // Update fields + mesh_.updateMesh(map); + + // Move mesh (since morphing might not do this) + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); + setInstance(mesh_.facesInstance()); + + + // Update local mesh data + // ~~~~~~~~~~~~~~~~~~~~~~ + + { + const labelList& oldToNewFaces = map().reverseFaceMap(); + const labelList& oldToNewPoints = map().reversePointMap(); + + // Compact out merged faces + DynamicList<label> changedFaces(mergedIndices.size()); + + label newIndex = 0; + forAll(facePairs, index) + { + const labelPair& oldSplit = facePairs[index]; + label new0 = oldToNewFaces[oldSplit[0]]; + label new1 = oldToNewFaces[oldSplit[1]]; + + if (!mergedIndices.found(index)) + { + // Faces still split + if (new0 < 0 || new1 < 0) + { + FatalErrorIn("meshRefinement::splitFacesUndo()") + << "Problem: oldFaces:" << oldSplit + << " newFaces:" << labelPair(new0, new1) + << exit(FatalError); + } + + facePairs[newIndex] = labelPair(new0, new1); + originalFaces[newIndex] = renumber + ( + oldToNewPoints, + originalFaces[index] + ); + newIndex++; + } + else + { + // Merged face. Only new0 kept. + if (new0 < 0 || new1 == -1) + { + FatalErrorIn("meshRefinement::splitFacesUndo()") + << "Problem: oldFaces:" << oldSplit + << " newFace:" << labelPair(new0, new1) + << exit(FatalError); + } + changedFaces.append(new0); + } + } + + facePairs.setSize(newIndex); + originalFaces.setSize(newIndex); + + + // Update intersections + updateMesh(map, growFaceCellFace(changedFaces)); + } + + // Update baffle data + // ~~~~~~~~~~~~~~~~~~ + { + if (duplicateFace.size()) + { + meshRefinement::updateList + ( + map().faceMap(), + label(-1), + duplicateFace + ); + } + + const labelList& reverseFaceMap = map().reverseFaceMap(); + forAll(baffles, i) + { + labelPair& baffle = baffles[i]; + baffle.first() = reverseFaceMap[baffle.first()]; + baffle.second() = reverseFaceMap[baffle.second()]; + + if (baffle.first() == -1 || baffle.second() == -1) + { + FatalErrorIn("meshRefinement::splitFacesUndo()") + << "Removed baffle : faces:" << baffle + << exit(FatalError); + } + } + } + + } + + return nSplit; +} // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // @@ -1231,150 +1243,6 @@ Foam::label Foam::meshRefinement::countHits() const } -//// Determine distribution to move connected regions onto one processor. -//Foam::labelList Foam::meshRefinement::decomposeCombineRegions -//( -// const scalarField& cellWeights, -// const boolList& blockedFace, -// const List<labelPair>& explicitConnections, -// decompositionMethod& decomposer -//) const -//{ -// // Determine global regions, separated by blockedFaces -// regionSplit globalRegion(mesh_, blockedFace, explicitConnections); -// -// // Now globalRegion has global region per cell. Problem is that -// // the region might span multiple domains so we want to get -// // a "region master" per domain. Note that multi-processor -// // regions can only occur on cells on coupled patches. -// // Note: since the number of regions does not change by this the -// // process can be seen as just a shift of a region onto a single -// // processor. -// -// -// // Global cell numbering engine -// globalIndex globalCells(mesh_.nCells()); -// -// -// // Determine per coupled region the master cell (lowest numbered cell -// // on lowest numbered processor) -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// // (does not determine master for non-coupled=fully-local regions) -// -// Map<label> coupledRegionToMaster(mesh_.nFaces()-mesh_.nInternalFaces()); -// getCoupledRegionMaster -// ( -// globalCells, -// blockedFace, -// globalRegion, -// coupledRegionToMaster -// ); -// -// // Determine my regions -// // ~~~~~~~~~~~~~~~~~~~~ -// // These are the fully local ones or the coupled ones of which I am master -// -// Map<label> globalToLocalRegion; -// pointField localPoints; -// scalarField localWeights; -// calcLocalRegions -// ( -// globalCells, -// globalRegion, -// coupledRegionToMaster, -// cellWeights, -// -// globalToLocalRegion, -// localPoints, -// localWeights -// ); -// -// -// -// // Find distribution for regions -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// labelList regionDistribution; -// -// if (isA<geomDecomp>(decomposer)) -// { -// regionDistribution = decomposer.decompose(localPoints, localWeights); -// } -// else -// { -// labelListList regionRegions; -// calcRegionRegions -// ( -// globalRegion, -// globalToLocalRegion, -// coupledRegionToMaster, -// -// regionRegions -// ); -// -// regionDistribution = decomposer.decompose -// ( -// regionRegions, -// localPoints, -// localWeights -// ); -// } -// -// -// -// // Convert region-based decomposition back to cell-based one -// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// -// // Transfer destination processor back to all. Use global reduce for now. -// Map<label> regionToDist(coupledRegionToMaster.size()); -// forAllConstIter(Map<label>, coupledRegionToMaster, iter) -// { -// label region = iter.key(); -// -// Map<label>::const_iterator regionFnd = globalToLocalRegion.find -// (region); -// -// if (regionFnd != globalToLocalRegion.end()) -// { -// // Master cell is local. Store my distribution. -// regionToDist.insert(iter.key(), regionDistribution[regionFnd()]); -// } -// else -// { -// // Master cell is not on this processor. Make sure it is -// // overridden. -// regionToDist.insert(iter.key(), labelMax); -// } -// } -// Pstream::mapCombineGather(regionToDist, minEqOp<label>()); -// Pstream::mapCombineScatter(regionToDist); -// -// -// // Determine destination for all cells -// labelList distribution(mesh_.nCells()); -// -// forAll(globalRegion, cellI) -// { -// Map<label>::const_iterator fndRegion = -// regionToDist.find(globalRegion[cellI]); -// -// if (fndRegion != regionToDist.end()) -// { -// distribution[cellI] = fndRegion(); -// } -// else -// { -// // region is local to the processor. -// label localRegionI = globalToLocalRegion[globalRegion[cellI]]; -// -// distribution[cellI] = regionDistribution[localRegionI]; -// } -// } -// -// return distribution; -//} - - Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::meshRefinement::balance ( const bool keepZoneFaces, @@ -1432,37 +1300,29 @@ Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::meshRefinement::balance // keep owner&neighbour of such a surface zone on the same // processor. - const PtrList<surfaceZonesInfo>& surfZones = - surfaces().surfZones(); const faceZoneMesh& fZones = mesh_.faceZones(); const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); // Get faces whose owner and neighbour should stay together, // i.e. they are not 'blocked'. - forAll(surfZones, surfI) + forAll(fZones, zoneI) { - const word& fzName = surfZones[surfI].faceZoneName(); + const faceZone& fZone = fZones[zoneI]; - if (fzName.size()) + forAll(fZone, i) { - // Get zone - const faceZone& fZone = fZones[fzName]; - - forAll(fZone, i) + label faceI = fZone[i]; + if (blockedFace[faceI]) { - label faceI = fZone[i]; - if (blockedFace[faceI]) + if + ( + mesh_.isInternalFace(faceI) + || pbm[pbm.whichPatch(faceI)].coupled() + ) { - if - ( - mesh_.isInternalFace(faceI) - || pbm[pbm.whichPatch(faceI)].coupled() - ) - { - blockedFace[faceI] = false; - nUnblocked++; - } + blockedFace[faceI] = false; + nUnblocked++; } } } @@ -1559,52 +1419,7 @@ Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::meshRefinement::balance Info<< "Found " << nCouples << " baffles to keep together." << endl; } - - //if (nUnblocked > 0 || nCouples > 0) - //{ - // Info<< "Applying special decomposition to keep baffles" - // << " and zoned faces together." << endl; - // - // distribution = decomposeCombineRegions - // ( - // cellWeights, - // blockedFace, - // couples, - // decomposer - // ); - // - // labelList nProcCells(distributor.countCells(distribution)); - // Pstream::listCombineGather(nProcCells, plusEqOp<label>()); - // Pstream::listCombineScatter(nProcCells); - // - // Info<< "Calculated decomposition:" << endl; - // forAll(nProcCells, procI) - // { - // Info<< " " << procI << '\t' << nProcCells[procI] - // << endl; - // } - // Info<< endl; - //} - //else - //{ - // // Normal decomposition - // distribution = decomposer.decompose - // ( - // mesh_, - // mesh_.cellCentres(), - // cellWeights - // ); - //} - } - //else - //{ - // // Normal decomposition - // distribution = decomposer.decompose - // ( - // mesh_, - // cellWeights - // ); - //} + } // Make sure blockedFace not set on couples @@ -1639,7 +1454,36 @@ Foam::autoPtr<Foam::mapDistributePolyMesh> Foam::meshRefinement::balance Pout<< " " << procI << '\t' << nProcCells[procI] << endl; } Pout<< endl; + + + if (keepZoneFaces) + { + const faceZoneMesh& fZones = mesh_.faceZones(); + const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); + + // Check that faceZone faces are indeed internal + forAll(fZones, zoneI) + { + const faceZone& fZone = fZones[zoneI]; + + forAll(fZone, i) + { + label faceI = fZone[i]; + label patchI = pbm.whichPatch(faceI); + + if (patchI >= 0 && pbm[patchI].coupled()) + { + WarningIn("meshRefinement::balance(..)") + << "Face at " << mesh_.faceCentres()[faceI] + << " on zone " << fZone.name() + << " is on coupled patch " << pbm[patchI].name() + << endl; + } + } + } + } } + // Do actual sending/receiving of mesh map = distributor.distribute(distribution); @@ -2226,6 +2070,9 @@ Foam::label Foam::meshRefinement::addMeshedPatch // Store meshedPatches_.append(name); + // Clear patch based addressing + faceToCoupledPatch_.clear(); + return patchI; } } @@ -2257,6 +2104,59 @@ Foam::labelList Foam::meshRefinement::meshedPatches() const } +Foam::label Foam::meshRefinement::addFaceZone +( + const word& fzName, + const word& masterPatch, + const word& slavePatch, + const surfaceZonesInfo::faceZoneType& fzType +) +{ + label zoneI = surfaceZonesInfo::addFaceZone + ( + fzName, //name + labelList(0), //addressing + boolList(0), //flipmap + mesh_ + ); + + faceZoneToMasterPatch_.insert(fzName, masterPatch); + faceZoneToSlavePatch_.insert(fzName, slavePatch); + faceZoneToType_.insert(fzName, fzType); + + return zoneI; +} + + +bool Foam::meshRefinement::getFaceZoneInfo +( + const word& fzName, + label& masterPatchID, + label& slavePatchID, + surfaceZonesInfo::faceZoneType& fzType +) const +{ + const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); + + if (!faceZoneToMasterPatch_.found(fzName)) + { + return false; + } + else + { + const word& masterName = faceZoneToMasterPatch_[fzName]; + masterPatchID = pbm.findPatchID(masterName); + + const word& slaveName = faceZoneToSlavePatch_[fzName]; + slavePatchID = pbm.findPatchID(slaveName); + + fzType = faceZoneToType_[fzName]; + + return true; + } +} + + void Foam::meshRefinement::selectSeparatedCoupledFaces(boolList& selected) const { const polyBoundaryMesh& patches = mesh_.boundaryMesh(); @@ -2292,7 +2192,22 @@ Foam::label Foam::meshRefinement::findRegion ) { label regionI = -1; + + // Force calculation of base points (needs to be synchronised) + (void)mesh.tetBasePtIs(); + label cellI = mesh.findCell(p); + //if (cellI != -1) + //{ + // Pout<< "findRegion : Found point:" << p << " in cell " << cellI + // << " at:" << mesh.cellCentres()[cellI] << endl; + //} + //else + //{ + // Pout<< "findRegion : Found point:" << p << " in cell " << cellI + // << endl; + //} + if (cellI != -1) { regionI = cellToRegion[cellI]; @@ -2311,13 +2226,152 @@ Foam::label Foam::meshRefinement::findRegion } return regionI; } +//XXXXXXXX +//Foam::labelList Foam::meshRefinement::findRegion +//( +// const polyMesh& mesh, +// const labelList& cellToRegion, +// const vector& perturbVec, +// const pointField& pts +//) +//{ +// labelList regions(pts.size(), -1); +// +// forAll(pts, i) +// { +// label cellI = mesh.findCell(pts[i]); +// if (cellI != -1) +// { +// regions[i] = cellToRegion[cellI]; +// } +// reduce(regions[i], maxOp<label>()); +// +// if (regions[i] == -1) +// { +// // See if we can perturb a bit +// cellI = mesh.findCell(pts[i]+perturbVec); +// if (cellI != -1) +// { +// regions[i] = cellToRegion[cellI]; +// } +// reduce(regions[i], maxOp<label>()); +// } +// } +// +// forAll(regions, i) +// { +// if (regions[i] == -1) +// { +// FatalErrorIn +// ( +// "meshRefinement::findRegion" +// "(const polyMesh&, const labelList&, const vector&" +// ", const pointField&)" +// ) << "Point " << pts[i] +// << " is not inside the mesh." << nl +// << "Bounding box of the mesh:" << mesh.bounds() +// //<< "All points " << pts +// //<< " with corresponding regions " << regions +// << exit(FatalError); +// } +// } +// +// return regions; +//} +//XXXXXXXX + + +// 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 +void Foam::meshRefinement::findRegions +( + const polyMesh& mesh, + const vector& perturbVec, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh, + const label nRegions, + labelList& cellRegion +) +{ + PackedBoolList insideCell(mesh.nCells()); + + + // Mark all cells reachable from locationsInMesh + labelList insideRegions(locationsInMesh.size()); + forAll(insideRegions, i) + { + // Find the region containing the point + label regionI = findRegion + ( + mesh, + cellRegion, + perturbVec, + locationsInMesh[i] + ); + + insideRegions[i] = regionI; + + // Mark all cells in the region as being inside + forAll(cellRegion, cellI) + { + if (cellRegion[cellI] == regionI) + { + insideCell[cellI] = true; + } + } + } + + + + // Check that all the locations outside the + // mesh do not conflict with those inside + forAll(locationsOutsideMesh, i) + { + // Find the region containing the point + label regionI = findRegion + ( + mesh, + cellRegion, + perturbVec, + locationsOutsideMesh[i] + ); + + if (regionI != -1) + { + // Do a quick check for locationsOutsideMesh overlapping with + // inside ones. + label index = findIndex(insideRegions, regionI); + if (index != -1) + { + FatalErrorIn("meshRefinement::findRegions(..)") + << "Location in mesh " << locationsInMesh[index] + << " is inside same mesh region " << regionI + << " as location outside mesh " + << locationsOutsideMesh[i] + << exit(FatalError); + } + } + } + + + // Now update cellRegion to -1 for unreachable cells + forAll(insideCell, cellI) + { + if (!insideCell[cellI]) + { + cellRegion[cellI] = -1; + } + } +} Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMeshRegions ( const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh ) { // Force calculation of face decomposition (used in findCell) @@ -2331,25 +2385,16 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMeshRegions regionSplit cellRegion(mesh_, blockedFace); - label regionI = findRegion + findRegions ( mesh_, - cellRegion, - mergeDistance_*vector(1,1,1), // note:1,1,1 should really be normalised - keepPoint + mergeDistance_*vector(1,1,1), // perturbVec + locationsInMesh, + locationsOutsideMesh, + cellRegion.nRegions(), + cellRegion ); - if (regionI == -1) - { - FatalErrorIn - ( - "meshRefinement::splitMeshRegions(const point&)" - ) << "Point " << keepPoint - << " is not inside the mesh." << nl - << "Bounding box of the mesh:" << mesh_.bounds() - << exit(FatalError); - } - // Subset // ~~~~~~ @@ -2357,62 +2402,74 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMeshRegions DynamicList<label> cellsToRemove(mesh_.nCells()); forAll(cellRegion, cellI) { - if (cellRegion[cellI] != regionI) + if (cellRegion[cellI] == -1) { cellsToRemove.append(cellI); } } cellsToRemove.shrink(); - label nCellsToKeep = mesh_.nCells() - cellsToRemove.size(); - reduce(nCellsToKeep, sumOp<label>()); + label nTotCellsToRemove = returnReduce + ( + cellsToRemove.size(), + sumOp<label>() + ); + - Info<< "Keeping all cells in region " << regionI - << " containing point " << keepPoint << endl - << "Selected for keeping : " - << nCellsToKeep - << " cells." << endl; + autoPtr<mapPolyMesh> mapPtr; + if (nTotCellsToRemove > 0) + { + label nCellsToKeep = + mesh_.globalData().nTotalCells() + - nTotCellsToRemove; + Info<< "Keeping all cells containing points " << locationsInMesh << endl + << "Selected for keeping : " + << nCellsToKeep + << " cells." << endl; - // Remove cells - removeCells cellRemover(mesh_); - labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove)); - labelList exposedPatch; + // Remove cells + removeCells cellRemover(mesh_); - label nExposedFaces = returnReduce(exposedFaces.size(), sumOp<label>()); - if (nExposedFaces) - { - //FatalErrorIn - //( - // "meshRefinement::splitMeshRegions(const point&)" - //) << "Removing non-reachable cells should only expose" - // << " boundary faces" << nl - // << "ExposedFaces:" << exposedFaces << abort(FatalError); + labelList exposedFaces(cellRemover.getExposedFaces(cellsToRemove)); + labelList exposedPatch; - // Patch for exposed faces for lack of anything sensible. - label defaultPatch = 0; - if (globalToMasterPatch.size()) + label nExposedFaces = returnReduce(exposedFaces.size(), sumOp<label>()); + if (nExposedFaces) { - defaultPatch = globalToMasterPatch[0]; + //FatalErrorIn + //( + // "meshRefinement::splitMeshRegions(const point&)" + //) << "Removing non-reachable cells should only expose" + // << " boundary faces" << nl + // << "ExposedFaces:" << exposedFaces << abort(FatalError); + + // Patch for exposed faces for lack of anything sensible. + label defaultPatch = 0; + if (globalToMasterPatch.size()) + { + defaultPatch = globalToMasterPatch[0]; + } + + WarningIn + ( + "meshRefinement::splitMeshRegions(const point&)" + ) << "Removing non-reachable cells exposes " + << nExposedFaces << " internal or coupled faces." << endl + << " These get put into patch " << defaultPatch << endl; + exposedPatch.setSize(exposedFaces.size(), defaultPatch); } - WarningIn + mapPtr = doRemoveCells ( - "meshRefinement::splitMeshRegions(const point&)" - ) << "Removing non-reachable cells exposes " - << nExposedFaces << " internal or coupled faces." << endl - << " These get put into patch " << defaultPatch << endl; - exposedPatch.setSize(exposedFaces.size(), defaultPatch); + cellsToRemove, + exposedFaces, + exposedPatch, + cellRemover + ); } - - return doRemoveCells - ( - cellsToRemove, - exposedFaces, - exposedPatch, - cellRemover - ); + return mapPtr; } @@ -2428,6 +2485,10 @@ void Foam::meshRefinement::distribute(const mapDistributePolyMesh& map) // surfaceIndex is face data. map.distributeFaceData(surfaceIndex_); + // faceToPatch (baffles that were on coupled faces) is not maintained + // (since baffling also disconnects points) + faceToCoupledPatch_.clear(); + // maintainedFaces are indices of faces. forAll(userFaceData_, i) { @@ -2526,6 +2587,22 @@ void Foam::meshRefinement::updateMesh // Update surfaceIndex updateList(map.faceMap(), label(-1), surfaceIndex_); + // Update faceToCoupledPatch_ + { + Map<label> newFaceToPatch(faceToCoupledPatch_.size()); + forAllConstIter(Map<label>, faceToCoupledPatch_, iter) + { + label newFaceI = map.reverseFaceMap()[iter.key()]; + + if (newFaceI >= 0) + { + newFaceToPatch.insert(newFaceI, iter()); + } + } + faceToCoupledPatch_.transfer(newFaceToPatch); + } + + // Update cached intersection information updateIntersections(changedFaces); diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H index 7281a3d520d41077564ffb7a7ae2c7a8a8f40e28..31ef9f2e1a286d2bbc2cc9fa7c35a15bef20ebef 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinement.H @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -51,6 +51,8 @@ SourceFiles #include "pointFieldsFwd.H" #include "Tuple2.H" #include "pointIndexHit.H" +#include "wordPairHashTable.H" +#include "surfaceZonesInfo.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -66,14 +68,11 @@ class refinementFeatures; class shellSurfaces; class removeCells; class fvMeshDistribute; -class searchableSurface; -class regionSplit; -class globalIndex; class removePoints; class localPointRegion; - class snapParameters; + /*---------------------------------------------------------------------------*\ Class meshRefinement Declaration \*---------------------------------------------------------------------------*/ @@ -183,6 +182,14 @@ private: //- Per cc-cc vector the index of the surface hit labelIOList surfaceIndex_; + + // For baffle merging + + //- Original patch for baffle faces that used to be on + // coupled patches + Map<label> faceToCoupledPatch_; + + //- User supplied face based data. List<Tuple2<mapType, labelList> > userFaceData_; @@ -190,6 +197,15 @@ private: // order changes. wordList meshedPatches_; + //- FaceZone to master patch name + HashTable<word, word> faceZoneToMasterPatch_; + + //- FaceZone to slave patch name + HashTable<word, word> faceZoneToSlavePatch_; + + //- FaceZone to method to handle faces + HashTable<surfaceZonesInfo::faceZoneType, word> faceZoneToType_; + // Private Member Functions @@ -216,9 +232,6 @@ private: pointField& neiCc ) const; - //- Find any intersection of surface. Store in surfaceIndex_. - void updateIntersections(const labelList& changedFaces); - //- Remove cells. Put exposedFaces into exposedPatchIDs. autoPtr<mapPolyMesh> doRemoveCells ( @@ -369,10 +382,25 @@ private: const labelList& globalToSlavePatch ) const; + //- Calculate intersections. Return per face -1 or the global + // surface region + void getIntersections + ( + const labelList& surfacesToTest, + const pointField& neiCc, + const labelList& testFaces, + + labelList& globalRegion1, + labelList& globalRegion2 + ) const; + //- Determine patches for baffles void getBafflePatches ( const labelList& globalToMasterPatch, + const pointField& locationsInMesh, + const wordList& regionsInMesh, + const labelList& neiLevel, const pointField& neiCc, labelList& ownPatch, @@ -454,7 +482,9 @@ private: labelList markFacesOnProblemCellsGeometric ( const snapParameters& snapParams, - const dictionary& motionDict + const dictionary& motionDict, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch ) const; @@ -494,6 +524,16 @@ private: labelList& cellToZone ) const; + //- Finds zone per cell for cells inside region for which name + // is specified. + void findCellZoneInsideWalk + ( + const pointField& locationsInMesh, + const wordList& regionsInMesh, + const labelList& blockedFace, // per face -1 or some index >= 0 + labelList& cellToZone + ) const; + //- Determines cell zone from cell region information. bool calcRegionToZone ( @@ -508,7 +548,7 @@ private: // marked in namedSurfaceIndex regarded as blocked. void findCellZoneTopo ( - const point& keepPoint, + const pointField& locationsInMesh, const labelList& namedSurfaceIndex, const labelList& surfaceToCellZone, labelList& cellToZone @@ -520,6 +560,26 @@ private: labelList& namedSurfaceIndex ) const; + //- Put cells into cellZone, faces into faceZone + void zonify + ( + const PackedBoolList& isMasterFace, + const labelList& cellToZone, + const labelList& neiCellZone, + const labelList& faceToZone, + const boolList& meshFlipMap, + polyTopoChange& meshMod + ) const; + + //- Allocate faceZoneName + void allocateInterRegionFaceZone + ( + const label ownZone, + const label neiZone, + wordPairHashTable& zonesToFaceZone, + HashTable<word, labelPair, labelPair::Hash<> >& + ) const; + //- Remove any loose standing cells void handleSnapProblems ( @@ -667,6 +727,13 @@ public: return surfaceIndex_; } + //- For faces originating from processor faces store the original + // patch + const Map<label>& faceToCoupledPatch() const + { + return faceToCoupledPatch_; + } + //- Additional face data that is maintained across // topo changes. Every entry is a list over all faces. // Bit of a hack. Additional flag to say whether to maintain master @@ -829,16 +896,29 @@ public: const bool useTopologicalSnapDetection, const bool removeEdgeConnectedCells, const scalarField& perpendicularAngle, + const dictionary& motionDict, + Time& runTime, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const pointField& locationsInMesh, + const wordList& regionsInMesh, + const pointField& locationsOutsideMesh + ); - // How to handle free-standing baffles - const bool mergeFreeStanding, - const scalar freeStandingAngle, - + //- Merge free-standing baffles + void mergeFreeStandingBaffles + ( + const snapParameters& snapParams, + const bool useTopologicalSnapDetection, + const bool removeEdgeConnectedCells, + const scalarField& perpendicularAngle, + const scalar planarAngle, const dictionary& motionDict, Time& runTime, const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh ); //- Split off (with optional buffer layers) unreachable areas @@ -848,7 +928,10 @@ public: const label nBufferLayers, const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + + const pointField& locationsInMesh, + const wordList& regionsInMesh, + const pointField& locationsOutsideMesh ); //- Find boundary points that connect to more than one cell @@ -859,6 +942,16 @@ public: // region and split them. autoPtr<mapPolyMesh> dupNonManifoldPoints(); + //- Find boundary points that are on faceZones of type boundary + // and duplicate them + autoPtr<mapPolyMesh> dupNonManifoldBoundaryPoints(); + + //- Merge duplicate points + autoPtr<mapPolyMesh> mergePoints + ( + const labelList& pointToDuplicate + ); + //- Create baffle for every internal face where ownPatch != -1. // External faces get repatched according to ownPatch (neiPatch // should be -1 for these) @@ -868,28 +961,55 @@ public: const labelList& neiPatch ); - //- Debug helper: check faceZones are not on processor patches - void checkZoneFaces() const; + //- Get zones of given type + labelList getZones + ( + const List<surfaceZonesInfo::faceZoneType>& fzTypes + ) const; - //- Create baffles for faces straddling zoned surfaces. Return - // baffles. + //- Subset baffles according to zones + static List<labelPair> subsetBaffles + ( + const polyMesh& mesh, + const labelList& zoneIDs, + const List<labelPair>& baffles + ); + + //- Create baffles for faces on faceZones. Return created baffles + // (= pairs of faces) and corresponding faceZone autoPtr<mapPolyMesh> createZoneBaffles ( - const labelList& globalToMasterPatch, - const labelList& globalToSlavePatch, - List<labelPair>& + const labelList& zoneIDs, + List<labelPair>& baffles, + labelList& originatingFaceZone ); - //- Merge baffles. Gets pairs of faces. - autoPtr<mapPolyMesh> mergeBaffles(const List<labelPair>&); + //- Merge baffles. Gets pairs of faces and boundary faces to move + // onto (coupled) patches + autoPtr<mapPolyMesh> mergeBaffles + ( + const List<labelPair>&, + const Map<label>& faceToPatch + ); + + //- Merge all baffles on faceZones + autoPtr<mapPolyMesh> mergeZoneBaffles + ( + const bool doInternalZones, + const bool doBaffleZones + ); //- Put faces/cells into zones according to surface specification. - // Returns null if no zone surfaces present. Region containing - // the keepPoint will not be put into a cellZone. + // Returns null if no zone surfaces present. Regions containing + // locationsInMesh/regionsInMesh will be put in corresponding + // cellZone. keepPoints is for backwards compatibility and sets + // all yet unassigned cells to be non-zoned (zone = -1) autoPtr<mapPolyMesh> zonify ( - const point& keepPoint, - const bool allowFreeStandingZoneFaces + const bool allowFreeStandingZoneFaces, + const pointField& locationsInMesh, + const wordList& regionsInMesh, + wordPairHashTable& zonesToFaceZone ); @@ -914,9 +1034,32 @@ public: //- Get patchIDs for patches added in addMeshedPatch. labelList meshedPatches() const; + //- Add/lookup faceZone and update information. Return index of + // faceZone + label addFaceZone + ( + const word& fzName, + const word& masterPatch, + const word& slavePatch, + const surfaceZonesInfo::faceZoneType& fzType + ); + + //- Lookup faceZone information. Return false if no information + // for faceZone + bool getFaceZoneInfo + ( + const word& fzName, + label& masterPatchID, + label& slavePatchID, + surfaceZonesInfo::faceZoneType& fzType + ) 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); + //- Find region point is in. Uses optional perturbation to re-test. static label findRegion ( @@ -926,19 +1069,44 @@ public: const point& p ); - //- Split mesh. Keep part containing point. + static void findRegions + ( + const polyMesh&, + const vector& perturbVec, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh, + const label nRegions, + labelList& cellRegion + ); + + //- Split mesh. Keep part containing point. Return empty map if + // no cells removed. autoPtr<mapPolyMesh> splitMeshRegions ( const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh ); //- Split faces into two - autoPtr<mapPolyMesh> splitFaces + void doSplitFaces ( const labelList& splitFaces, - const labelPairList& splits + const labelPairList& splits, + polyTopoChange& meshMod + ) const; + + //- Split faces along diagonal. Maintain mesh quality. Return + // total number of faces split. + label splitFacesUndo + ( + const labelList& splitFaces, + const labelPairList& splits, + const dictionary& motionDict, + + labelList& duplicateFace, + List<labelPair>& baffles ); //- Update local numbering for mesh redistribution @@ -986,6 +1154,16 @@ public: // Merging coplanar faces and edges + //- Merge coplanar faces if sets are of size mergeSize + // (usually 4) + label mergePatchFaces + ( + const scalar minCos, + const scalar concaveCos, + const label mergeSize, + const labelList& patchIDs + ); + //- Merge coplanar faces. preserveFaces is != -1 for faces // to be preserved label mergePatchFacesUndo diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C index 8cf215729d9323fc7176a4d399b75f9fac2c18b8..e63c6a353ed37bb7e9dc0658a3f9769ce3f4bcc3 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementBaffles.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -40,7 +40,6 @@ License #include "polyAddPoint.H" #include "localPointRegion.H" #include "duplicatePoints.H" -#include "OFstream.H" #include "regionSplit.H" #include "removeCells.H" #include "unitConversion.H" @@ -48,6 +47,9 @@ License #include "patchFaceOrientation.H" #include "PatchEdgeFaceWave.H" #include "patchEdgeFaceRegion.H" +#include "polyMeshAdder.H" +#include "IOmanip.H" +#include "refinementParameters.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -170,52 +172,40 @@ Foam::label Foam::meshRefinement::createBaffle //} -// Determine patches for baffles on all intersected unnamed faces -void Foam::meshRefinement::getBafflePatches +void Foam::meshRefinement::getIntersections ( - const labelList& globalToMasterPatch, - const labelList& neiLevel, + const labelList& surfacesToTest, const pointField& neiCc, + const labelList& testFaces, - labelList& ownPatch, - labelList& neiPatch + labelList& globalRegion1, + labelList& globalRegion2 ) const { - autoPtr<OFstream> str; - label vertI = 0; + autoPtr<OBJstream> str; if (debug&OBJINTERSECTIONS) { mkDir(mesh_.time().path()/timeName()); str.reset ( - new OFstream + new OBJstream ( mesh_.time().path()/timeName()/"intersections.obj" ) ); - Pout<< "getBafflePatches : Writing surface intersections to file " + Pout<< "getIntersections : Writing surface intersections to file " << str().name() << nl << endl; } const pointField& cellCentres = mesh_.cellCentres(); - // Surfaces that need to be baffled - const labelList surfacesToBaffle - ( - surfaceZonesInfo::getUnnamedSurfaces(surfaces_.surfZones()) - ); - - ownPatch.setSize(mesh_.nFaces()); - ownPatch = -1; - neiPatch.setSize(mesh_.nFaces()); - neiPatch = -1; - - // Collect candidate faces - // ~~~~~~~~~~~~~~~~~~~~~~~ + globalRegion1.setSize(mesh_.nFaces()); + globalRegion1 = -1; + globalRegion2.setSize(mesh_.nFaces()); + globalRegion2 = -1; - labelList testFaces(intersectedFaces()); // Collect segments // ~~~~~~~~~~~~~~~~ @@ -259,7 +249,7 @@ void Foam::meshRefinement::getBafflePatches labelList region2; surfaces_.findNearestIntersection ( - surfacesToBaffle, + surfacesToTest, start, end, @@ -272,6 +262,7 @@ void Foam::meshRefinement::getBafflePatches region2 ); + forAll(testFaces, i) { label faceI = testFaces[i]; @@ -280,36 +271,172 @@ void Foam::meshRefinement::getBafflePatches { if (str.valid()) { - meshTools::writeOBJ(str(), start[i]); - vertI++; - meshTools::writeOBJ(str(), hit1[i].rawPoint()); - vertI++; - meshTools::writeOBJ(str(), hit2[i].rawPoint()); - vertI++; - meshTools::writeOBJ(str(), end[i]); - vertI++; - str()<< "l " << vertI-3 << ' ' << vertI-2 << nl; - str()<< "l " << vertI-2 << ' ' << vertI-1 << nl; - str()<< "l " << vertI-1 << ' ' << vertI << nl; + str().write(linePointRef(start[i], hit1[i].rawPoint())); + str().write + ( + linePointRef(hit1[i].rawPoint(), hit2[i].rawPoint()) + ); + str().write(linePointRef(hit2[i].rawPoint(), end[i])); } // Pick up the patches - ownPatch[faceI] = globalToMasterPatch - [ - surfaces_.globalRegion(surface1[i], region1[i]) - ]; - neiPatch[faceI] = globalToMasterPatch - [ - surfaces_.globalRegion(surface2[i], region2[i]) - ]; - - if (ownPatch[faceI] == -1 || neiPatch[faceI] == -1) - { - FatalErrorIn("getBafflePatches(..)") + globalRegion1[faceI] = + surfaces_.globalRegion(surface1[i], region1[i]); + globalRegion2[faceI] = + surfaces_.globalRegion(surface2[i], region2[i]); + + if (globalRegion1[faceI] == -1 || globalRegion2[faceI] == -1) + { + FatalErrorIn("getIntersections(..)") << "problem." << abort(FatalError); } } } +} + + +// Determine patches for baffles on all intersected unnamed faces +void Foam::meshRefinement::getBafflePatches +( + const labelList& globalToMasterPatch, + const pointField& locationsInMesh, + const wordList& zonesInMesh, + + const labelList& neiLevel, + const pointField& neiCc, + + labelList& ownPatch, + labelList& neiPatch +) const +{ + // Check all unnamed surfaces + + { + const labelList testFaces(intersectedFaces()); + + labelList globalRegion1; + labelList globalRegion2; + getIntersections + ( + surfaceZonesInfo::getUnnamedSurfaces(surfaces_.surfZones()), + neiCc, + testFaces, + globalRegion1, + globalRegion2 + ); + + ownPatch.setSize(mesh_.nFaces()); + ownPatch = -1; + neiPatch.setSize(mesh_.nFaces()); + neiPatch = -1; + forAll(testFaces, i) + { + label faceI = testFaces[i]; + if (globalRegion1[faceI] != -1) + { + ownPatch[faceI] = globalToMasterPatch[globalRegion1[faceI]]; + } + if (globalRegion2[faceI] != -1) + { + neiPatch[faceI] = globalToMasterPatch[globalRegion2[faceI]]; + } + } + } + + + if (locationsInMesh.size() > 1) + { + // Now we need to filter out any possible intersections between + // multiple regions. Only the faces on the outside of all + // regions are candidates for baffling. + // For this we need to go to per-cell information + + labelList cellToZone(mesh_.nCells(), -2); + + + // Closed surfaces with cellZone specified. + const labelList closedNamedSurfaces + ( + surfaceZonesInfo::getClosedNamedSurfaces + ( + surfaces_.surfZones(), + surfaces_.geometry(), + surfaces_.surfaces() + ) + ); + + if (closedNamedSurfaces.size()) + { + Info<< "Found " << closedNamedSurfaces.size() + << " closed, named surfaces. Assigning cells in/outside" + << " these surfaces to the corresponding cellZone." + << nl << endl; + + findCellZoneGeometric + ( + neiCc, + closedNamedSurfaces, // indices of closed surfaces + ownPatch, // per face patch + labelList(mesh_.boundaryMesh().size(), 0),// cellZone per patch + + cellToZone + ); + } + + // Locations in mesh - walk + findCellZoneInsideWalk + ( + locationsInMesh, + zonesInMesh, + ownPatch, // per face -1 or some index >= 0 + cellToZone + ); + + + // Now we will still have some -2 in cellToZone on parts that + // are unreachable from the locationsInMesh or named surfaces. We only + // want to keep the interfaces to these cellZones. + + + labelList neiCellZone; + syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); + + // Only keep baffles where one of the sides has an unset cellToZone (-2) + for (label faceI=0; faceI < mesh_.nInternalFaces(); faceI++) + { + if (ownPatch[faceI] != -1 || neiPatch[faceI] != -1) + { + label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; + label neiZone = cellToZone[mesh_.faceNeighbour()[faceI]]; + + if (ownZone != -2 && neiZone != -2) + { + ownPatch[faceI] = -1; + neiPatch[faceI] = -1; + } + } + } + for + ( + label faceI = mesh_.nInternalFaces(); + faceI < mesh_.nFaces(); + faceI++ + ) + { + if (ownPatch[faceI] != -1 || neiPatch[faceI] != -1) + { + label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; + label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; + + if (ownZone != -2 && neiZone != -2) + { + ownPatch[faceI] = -1; + neiPatch[faceI] = -1; + } + } + } + } + // No need to parallel sync since intersection data (surfaceIndex_ etc.) // already guaranteed to be synced... @@ -449,7 +576,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::createBaffles label nBaffles = 0; - forAll(ownPatch, faceI) + for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) { if (ownPatch[faceI] != -1) { @@ -465,184 +592,318 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::createBaffles nBaffles++; } } + const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); - // Change the mesh (no inflation, parallel sync) - autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + forAll(pbm, patchI) + { + const polyPatch& pp = pbm[patchI]; - // Update fields - mesh_.updateMesh(map); + label coupledPatchI = -1; + if + ( + pp.coupled() + && !refCast<const coupledPolyPatch>(pp).separated() + ) + { + coupledPatchI = patchI; + } - // Move mesh if in inflation mode - if (map().hasMotionPoints()) - { - mesh_.movePoints(map().preMotionPoints()); + forAll(pp, i) + { + label faceI = pp.start()+i; + + if (ownPatch[faceI] != -1) + { + createBaffle + ( + faceI, + ownPatch[faceI], // owner side patch + neiPatch[faceI], // neighbour side patch + meshMod + ); + + if (coupledPatchI != -1) + { + faceToCoupledPatch_.insert(faceI, coupledPatchI); + } + + nBaffles++; + } + } } - else + + + autoPtr<mapPolyMesh> map; + if (returnReduce(nBaffles, sumOp<label>())) { - // Delete mesh volumes. - mesh_.clearOut(); - } + // Change the mesh (no inflation, parallel sync) + map = meshMod.changeMesh(mesh_, false, true); + // Update fields + mesh_.updateMesh(map); - // Reset the instance for if in overwrite mode - mesh_.setInstance(timeName()); + // Move mesh if in inflation mode + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + else + { + // Delete mesh volumes. + mesh_.clearOut(); + } - //- Redo the intersections on the newly create baffle faces. Note that - // this changes also the cell centre positions. - faceSet baffledFacesSet(mesh_, "baffledFacesSet", 2*nBaffles); - const labelList& reverseFaceMap = map().reverseFaceMap(); - const labelList& faceMap = map().faceMap(); + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); - // Pick up owner side of baffle - forAll(ownPatch, oldFaceI) - { - label faceI = reverseFaceMap[oldFaceI]; + //- Redo the intersections on the newly create baffle faces. Note that + // this changes also the cell centre positions. + faceSet baffledFacesSet(mesh_, "baffledFacesSet", 2*nBaffles); - if (ownPatch[oldFaceI] != -1 && faceI >= 0) + const labelList& reverseFaceMap = map().reverseFaceMap(); + const labelList& faceMap = map().faceMap(); + + // Pick up owner side of baffle + forAll(ownPatch, oldFaceI) { - const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]]; + label faceI = reverseFaceMap[oldFaceI]; - forAll(ownFaces, i) + if (ownPatch[oldFaceI] != -1 && faceI >= 0) { - baffledFacesSet.insert(ownFaces[i]); + const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]]; + + forAll(ownFaces, i) + { + baffledFacesSet.insert(ownFaces[i]); + } } } - } - // Pick up neighbour side of baffle (added faces) - forAll(faceMap, faceI) - { - label oldFaceI = faceMap[faceI]; - - if (oldFaceI >= 0 && reverseFaceMap[oldFaceI] != faceI) + // Pick up neighbour side of baffle (added faces) + forAll(faceMap, faceI) { - const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]]; + label oldFaceI = faceMap[faceI]; - forAll(ownFaces, i) + if (oldFaceI >= 0 && reverseFaceMap[oldFaceI] != faceI) { - baffledFacesSet.insert(ownFaces[i]); + const cell& ownFaces = mesh_.cells()[mesh_.faceOwner()[faceI]]; + + forAll(ownFaces, i) + { + baffledFacesSet.insert(ownFaces[i]); + } } } - } - baffledFacesSet.sync(mesh_); + baffledFacesSet.sync(mesh_); - updateMesh(map, baffledFacesSet.toc()); + updateMesh(map, baffledFacesSet.toc()); + } return map; } -void Foam::meshRefinement::checkZoneFaces() const +Foam::labelList Foam::meshRefinement::getZones +( + const List<surfaceZonesInfo::faceZoneType>& fzTypes +) const { - const faceZoneMesh& fZones = mesh_.faceZones(); + const faceZoneMesh& faceZones = mesh_.faceZones(); - const polyBoundaryMesh& pbm = mesh_.boundaryMesh(); + DynamicList<label> zoneIDs(faceZones.size()); - forAll(pbm, patchI) + forAll(faceZones, zoneI) { - const polyPatch& pp = pbm[patchI]; + const faceZone& fZone = faceZones[zoneI]; + + label mpI, spI; + surfaceZonesInfo::faceZoneType fzType; + bool hasInfo = getFaceZoneInfo(fZone.name(), mpI, spI, fzType); - if (isA<processorPolyPatch>(pp)) + if (hasInfo && findIndex(fzTypes, fzType) != -1) { - forAll(pp, i) - { - label faceI = pp.start()+i; - label zoneI = fZones.whichZone(faceI); + zoneIDs.append(zoneI); + } + } + return zoneIDs; +} - if (zoneI != -1) - { - FatalErrorIn("meshRefinement::checkZoneFaces") - << "face:" << faceI << " on patch " << pp.name() - << " is in zone " << fZones[zoneI].name() - << exit(FatalError); - } - } + +// Subset those baffles where both faces are on the same zone +Foam::List<Foam::labelPair> Foam::meshRefinement::subsetBaffles +( + const polyMesh& mesh, + const labelList& zoneIDs, + const List<labelPair>& baffles +) +{ + const faceZoneMesh& faceZones = mesh.faceZones(); + + // Mark zone per face + labelList faceToZone(mesh.nFaces(), -1); + + forAll(zoneIDs, i) + { + label zoneID = zoneIDs[i]; + UIndirectList<label>(faceToZone, faceZones[zoneID]) = zoneID; + } + + + // Subset baffles + DynamicList<labelPair> newBaffles(baffles.size()); + forAll(baffles, i) + { + const labelPair& p = baffles[i]; + if (faceToZone[p[0]] != -1 && (faceToZone[p[0]] == faceToZone[p[1]])) + { + newBaffles.append(p); } } + + return newBaffles; } Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::createZoneBaffles ( - const labelList& globalToMasterPatch, - const labelList& globalToSlavePatch, - List<labelPair>& baffles + const labelList& zoneIDs, + List<labelPair>& baffles, + labelList& originatingFaceZone ) { - const labelList zonedSurfaces - ( - surfaceZonesInfo::getNamedSurfaces(surfaces_.surfZones()) - ); - autoPtr<mapPolyMesh> map; - // No need to sync; all processors will have all same zonedSurfaces. - if (zonedSurfaces.size()) + if (zoneIDs.size() > 0) { + const faceZoneMesh& faceZones = mesh_.faceZones(); + // Split internal faces on interface surfaces Info<< "Converting zoned faces into baffles ..." << endl; - // Get faces (internal only) to be baffled. Map from face to patch - // label. - Map<labelPair> faceToPatch - ( - getZoneBafflePatches - ( - false, - globalToMasterPatch, - globalToSlavePatch - ) - ); + // Per (internal) face the patch it should go into + labelList ownPatch(mesh_.nFaces(), -1); + labelList neiPatch(mesh_.nFaces(), -1); + labelList faceZoneID(mesh_.nFaces(), -1); + + labelList nBaffles(zoneIDs.size(), 0); + + forAll(zoneIDs, j) + { + label zoneI = zoneIDs[j]; + + const faceZone& fz = faceZones[zoneI]; + + const word& masterName = faceZoneToMasterPatch_[fz.name()]; + label masterPatchI = mesh_.boundaryMesh().findPatchID(masterName); + const word& slaveName = faceZoneToSlavePatch_[fz.name()]; + label slavePatchI = mesh_.boundaryMesh().findPatchID(slaveName); + + if (masterPatchI == -1 || slavePatchI == -1) + { + FatalErrorIn("meshRefinement::createZoneBaffles(..)") + << "Problem: masterPatchI:" << masterPatchI + << " slavePatchI:" << slavePatchI << exit(FatalError); + } - label nZoneFaces = returnReduce(faceToPatch.size(), sumOp<label>()); - if (nZoneFaces > 0) + forAll(fz, i) + { + label faceI = fz[i]; + if (mesh_.isInternalFace(faceI)) + { + if (fz.flipMap()[i]) + { + ownPatch[faceI] = slavePatchI; + neiPatch[faceI] = masterPatchI; + } + else + { + ownPatch[faceI] = masterPatchI; + neiPatch[faceI] = slavePatchI; + } + faceZoneID[faceI] = zoneI; + + nBaffles[j]++; + } + } + } + + label nLocalBaffles = sum(nBaffles); + + + label nTotalBaffles = returnReduce(nLocalBaffles, sumOp<label>()); + + if (nTotalBaffles > 0) { - // Convert into labelLists - labelList ownPatch(mesh_.nFaces(), -1); - labelList neiPatch(mesh_.nFaces(), -1); - forAllConstIter(Map<labelPair>, faceToPatch, iter) + Pstream::listCombineGather(nBaffles, plusEqOp<label>()); + Pstream::listCombineScatter(nBaffles); + + Info<< nl + << setf(ios_base::left) + << setw(30) << "FaceZone" + << setw(10) << "FaceType" + << setw(10) << "nBaffles" + << nl + << setw(30) << "--------" + << setw(10) << "--------" + << setw(10) << "--------" + << endl; + + forAll(zoneIDs, j) { - ownPatch[iter.key()] = iter().first(); - neiPatch[iter.key()] = iter().second(); + label zoneI = zoneIDs[j]; + const faceZone& fz = faceZones[zoneI]; + + label mpI, spI; + surfaceZonesInfo::faceZoneType fzType; + bool hasInfo = getFaceZoneInfo(fz.name(), mpI, spI, fzType); + + if (hasInfo) + { + Info<< setf(ios_base::left) + << setw(30) << fz.name() + << setw(10) + << surfaceZonesInfo::faceZoneTypeNames[fzType] + << setw(10) << nBaffles[j] + << nl; + } } + Info<< endl; - // Create baffles. both sides same patch. + // Create baffles. map = createBaffles(ownPatch, neiPatch); // Get pairs of faces created. // Just loop over faceMap and store baffle if we encounter a slave // face. - baffles.setSize(faceToPatch.size()); + baffles.setSize(nLocalBaffles); + originatingFaceZone.setSize(nLocalBaffles); label baffleI = 0; const labelList& faceMap = map().faceMap(); const labelList& reverseFaceMap = map().reverseFaceMap(); - forAll(faceMap, faceI) + for + ( + label faceI = mesh_.nInternalFaces(); + faceI < mesh_.nFaces(); + faceI++ + ) { label oldFaceI = faceMap[faceI]; - - // Does face originate from face-to-patch - Map<labelPair>::const_iterator iter = faceToPatch.find - ( - oldFaceI - ); - - if (iter != faceToPatch.end()) + label masterFaceI = reverseFaceMap[oldFaceI]; + if (masterFaceI != faceI && ownPatch[oldFaceI] != -1) { - label masterFaceI = reverseFaceMap[oldFaceI]; - if (faceI != masterFaceI) - { - baffles[baffleI++] = labelPair(masterFaceI, faceI); - } + baffles[baffleI] = labelPair(masterFaceI, faceI); + originatingFaceZone[baffleI] = faceZoneID[oldFaceI]; + baffleI++; } } - if (baffleI != faceToPatch.size()) + if (baffleI != baffles.size()) { FatalErrorIn("meshRefinement::createZoneBaffles(..)") - << "Had " << faceToPatch.size() << " patches to create " + << "Had " << baffles.size() << " baffles to create " << " but encountered " << baffleI << " slave faces originating from patcheable faces." << abort(FatalError); @@ -661,9 +922,15 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::createZoneBaffles ); } } - Info<< "Created " << nZoneFaces << " baffles in = " + Info<< "Created " << nTotalBaffles << " baffles in = " << mesh_.time().cpuTimeIncrement() << " s\n" << nl << endl; } + else + { + baffles.clear(); + originatingFaceZone.clear(); + } + return map; } @@ -878,14 +1145,19 @@ Foam::List<Foam::labelPair> Foam::meshRefinement::freeStandingBaffles { const labelPair& couple = filteredCouples[i]; + // Note: for a baffle-surface we do not want to merge the baffle. + // We could either check for hitting the same triangle (but you + // might hit same point on neighbouring triangles due to tolerance + // issues) or better just to compare the hit point. + // This might still go wrong for a ray in the plane of the triangle + // which might hit two different points on the same triangle due + // to tolerances... + if ( hit1[i].hit() && hit2[i].hit() - && ( - surface1[i] != surface2[i] - || hit1[i].index() != hit2[i].index() - ) + && mag(hit1[i].hitPoint()-hit2[i].hitPoint()) > mergeDistance_ ) { // Two different hits. Check angle. @@ -930,132 +1202,219 @@ Foam::List<Foam::labelPair> Foam::meshRefinement::freeStandingBaffles // Merge baffles. Gets pairs of faces. Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::mergeBaffles ( - const List<labelPair>& couples + const List<labelPair>& couples, + const Map<label>& faceToPatch ) { - // Mesh change engine - polyTopoChange meshMod(mesh_); - - const faceList& faces = mesh_.faces(); - const labelList& faceOwner = mesh_.faceOwner(); - const faceZoneMesh& faceZones = mesh_.faceZones(); + autoPtr<mapPolyMesh> map; - forAll(couples, i) + if (returnReduce(couples.size()+faceToPatch.size(), sumOp<label>())) { - label face0 = couples[i].first(); - label face1 = couples[i].second(); - - // face1 < 0 signals a coupled face that has been converted to baffle. + // Mesh change engine + polyTopoChange meshMod(mesh_); - label own0 = faceOwner[face0]; - label own1 = faceOwner[face1]; + const faceList& faces = mesh_.faces(); + const labelList& faceOwner = mesh_.faceOwner(); + const faceZoneMesh& faceZones = mesh_.faceZones(); - if (face1 < 0 || own0 < own1) + forAll(couples, i) { - // Use face0 as the new internal face. - label zoneID = faceZones.whichZone(face0); - bool zoneFlip = false; + label face0 = couples[i].first(); + label face1 = couples[i].second(); - if (zoneID >= 0) + // face1 < 0 signals a coupled face that has been converted to + // baffle + + label own0 = faceOwner[face0]; + label own1 = faceOwner[face1]; + + if (face1 < 0 || own0 < own1) { - const faceZone& fZone = faceZones[zoneID]; - zoneFlip = fZone.flipMap()[fZone.whichFace(face0)]; + // Use face0 as the new internal face. + label zoneID = faceZones.whichZone(face0); + bool zoneFlip = false; + + if (zoneID >= 0) + { + const faceZone& fZone = faceZones[zoneID]; + zoneFlip = fZone.flipMap()[fZone.whichFace(face0)]; + } + + label nei = (face1 < 0 ? -1 : own1); + + meshMod.setAction(polyRemoveFace(face1)); + meshMod.setAction + ( + polyModifyFace + ( + faces[face0], // modified face + face0, // label of face being modified + own0, // owner + nei, // neighbour + false, // face flip + -1, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); } + else + { + // Use face1 as the new internal face. + label zoneID = faceZones.whichZone(face1); + bool zoneFlip = false; - label nei = (face1 < 0 ? -1 : own1); + if (zoneID >= 0) + { + const faceZone& fZone = faceZones[zoneID]; + zoneFlip = fZone.flipMap()[fZone.whichFace(face1)]; + } - meshMod.setAction(polyRemoveFace(face1)); - meshMod.setAction - ( - polyModifyFace + meshMod.setAction(polyRemoveFace(face0)); + meshMod.setAction ( - faces[face0], // modified face - face0, // label of face being modified - own0, // owner - nei, // neighbour - false, // face flip - -1, // patch for face - false, // remove from zone - zoneID, // zone for face - zoneFlip // face flip in zone - ) - ); + polyModifyFace + ( + faces[face1], // modified face + face1, // label of face being modified + own1, // owner + own0, // neighbour + false, // face flip + -1, // patch for face + false, // remove from zone + zoneID, // zone for face + zoneFlip // face flip in zone + ) + ); + } } - else + + forAllConstIter(Map<label>, faceToPatch, iter) { - // Use face1 as the new internal face. - label zoneID = faceZones.whichZone(face1); + label faceI = iter.key(); + label patchI = iter(); + + if (!mesh_.isInternalFace(faceI)) + { + FatalErrorIn("meshRefinement::mergeBaffles(..)") + << "problem: face:" << faceI + << " at:" << mesh_.faceCentres()[faceI] + << "(wanted patch:" << patchI + << ") is an internal face" << exit(FatalError); + } + + label zoneID = faceZones.whichZone(faceI); bool zoneFlip = false; if (zoneID >= 0) { const faceZone& fZone = faceZones[zoneID]; - zoneFlip = fZone.flipMap()[fZone.whichFace(face1)]; + zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)]; } - meshMod.setAction(polyRemoveFace(face0)); meshMod.setAction ( polyModifyFace ( - faces[face1], // modified face - face1, // label of face being modified - own1, // owner - own0, // neighbour + faces[faceI], // modified face + faceI, // label of face being modified + faceOwner[faceI], // owner + -1, // neighbour false, // face flip - -1, // patch for face + patchI, // patch for face false, // remove from zone zoneID, // zone for face zoneFlip // face flip in zone ) ); } - } - // Change the mesh (no inflation) - autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); - // Update fields - mesh_.updateMesh(map); + // Change the mesh (no inflation) + map = meshMod.changeMesh(mesh_, false, true); - // Move mesh (since morphing does not do this) - if (map().hasMotionPoints()) - { - mesh_.movePoints(map().preMotionPoints()); - } - else - { - // Delete mesh volumes. - 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(timeName()); + + // Update intersections. Recalculate intersections on merged faces since + // this seems to give problems? Note: should not be necessary since + // baffles preserve intersections from when they were created. + labelList newExposedFaces(2*couples.size()); + label newI = 0; + + forAll(couples, i) + { + label newFace0 = map().reverseFaceMap()[couples[i].first()]; + if (newFace0 != -1) + { + newExposedFaces[newI++] = newFace0; + } + + label newFace1 = map().reverseFaceMap()[couples[i].second()]; + if (newFace1 != -1) + { + newExposedFaces[newI++] = newFace1; + } + } + newExposedFaces.setSize(newI); + updateMesh(map, newExposedFaces); } - // Reset the instance for if in overwrite mode - mesh_.setInstance(timeName()); + return map; +} - // Update intersections. Recalculate intersections on merged faces since - // this seems to give problems? Note: should not be necessary since - // baffles preserve intersections from when they were created. - labelList newExposedFaces(2*couples.size()); - label newI = 0; - forAll(couples, i) +Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::mergeZoneBaffles +( + const bool doInternalZones, + const bool doBaffleZones +) +{ + labelList zoneIDs; { - label newFace0 = map().reverseFaceMap()[couples[i].first()]; - if (newFace0 != -1) + DynamicList<surfaceZonesInfo::faceZoneType> fzTypes; + if (doInternalZones) { - newExposedFaces[newI++] = newFace0; + fzTypes.append(surfaceZonesInfo::INTERNAL); } - - label newFace1 = map().reverseFaceMap()[couples[i].second()]; - if (newFace1 != -1) + if (doBaffleZones) { - newExposedFaces[newI++] = newFace1; + fzTypes.append(surfaceZonesInfo::BAFFLE); } + zoneIDs = getZones(fzTypes); } - newExposedFaces.setSize(newI); - updateMesh(map, newExposedFaces); - return map; + List<labelPair> zoneBaffles + ( + subsetBaffles + ( + mesh_, + zoneIDs, + localPointRegion::findDuplicateFacePairs(mesh_) + ) + ); + + autoPtr<mapPolyMesh> mapPtr; + if (returnReduce(zoneBaffles.size(), sumOp<label>())) + { + mapPtr = mergeBaffles(zoneBaffles, Map<label>(0)); + } + return mapPtr; } @@ -1216,33 +1575,29 @@ void Foam::meshRefinement::findCellZoneGeometric if (namedSurfaceIndex[faceI] == -1 && (ownZone != neiZone)) { - // Give face the zone of max cell zone - namedSurfaceIndex[faceI] = findIndex - ( - surfaceToCellZone, - max(ownZone, neiZone) - ); - } - } - - labelList neiCellZone(mesh_.nFaces()-mesh_.nInternalFaces()); - const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - - forAll(patches, patchI) - { - const polyPatch& pp = patches[patchI]; - - if (pp.coupled()) - { - forAll(pp, i) + // Give face the zone of min cell zone + label minZone; + if (ownZone == -1) { - label faceI = pp.start()+i; - label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; - neiCellZone[faceI-mesh_.nInternalFaces()] = ownZone; + minZone = neiZone; + } + else if (neiZone == -1) + { + minZone = ownZone; + } + else + { + minZone = min(ownZone, neiZone); } + + namedSurfaceIndex[faceI] = findIndex(surfaceToCellZone, minZone); } } - syncTools::swapBoundaryFaceList(mesh_, neiCellZone); + + labelList neiCellZone; + syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); + + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); forAll(patches, patchI) { @@ -1258,11 +1613,25 @@ void Foam::meshRefinement::findCellZoneGeometric if (namedSurfaceIndex[faceI] == -1 && (ownZone != neiZone)) { - // Give face the max cell zone + // Give face the min cell zone + label minZone; + if (ownZone == -1) + { + minZone = neiZone; + } + else if (neiZone == -1) + { + minZone = ownZone; + } + else + { + minZone = min(ownZone, neiZone); + } + namedSurfaceIndex[faceI] = findIndex ( surfaceToCellZone, - max(ownZone, neiZone) + minZone ); } } @@ -1317,10 +1686,6 @@ void Foam::meshRefinement::findCellZoneInsideWalk const point& insidePoint = surfZones[surfI].zoneInsidePoint(); - Info<< "For surface " << surfaces_.names()[surfI] - << " finding inside point " << insidePoint - << endl; - // Find the region containing the insidePoint label keepRegionI = findRegion ( @@ -1379,6 +1744,173 @@ void Foam::meshRefinement::findCellZoneInsideWalk } +void Foam::meshRefinement::findCellZoneInsideWalk +( + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const labelList& faceToZone, // per face -1 or some index >= 0 + + labelList& cellToZone +) const +{ + // Analyse regions. Reuse regionsplit + boolList blockedFace(mesh_.nFaces()); + //selectSeparatedCoupledFaces(blockedFace); + + forAll(blockedFace, faceI) + { + if (faceToZone[faceI] == -1) + { + blockedFace[faceI] = false; + } + else + { + blockedFace[faceI] = true; + } + } + // No need to sync since blockedFace already is synced + + // Set region per cell based on walking + regionSplit cellRegion(mesh_, blockedFace); + blockedFace.clear(); + + // Mark off which regions correspond to a zone + // (note zone is -1 for the non-zoned bit so initialise to -2) + labelList regionToZone(cellRegion.nRegions(), -2); + + + // Force calculation of face decomposition (used in findCell) + (void)mesh_.tetBasePtIs(); + + // For all locationsInMesh find the cell + forAll(locationsInMesh, i) + { + // Get location and index of zone ("none" for cellZone -1) + const point& insidePoint = locationsInMesh[i]; + label zoneID = mesh_.cellZones().findZoneID(zonesInMesh[i]); + + // Find the region containing the insidePoint + label keepRegionI = findRegion + ( + mesh_, + cellRegion, + mergeDistance_*vector(1,1,1), + insidePoint + ); + + Info<< "For cellZone " << zonesInMesh[i] + << " found point " << insidePoint + << " in global region " << keepRegionI + << " out of " << cellRegion.nRegions() << " regions." << endl; + + if (keepRegionI == -1) + { + FatalErrorIn + ( + "meshRefinement::findCellZoneInsideWalk" + "(const labelList&, const labelList&" + ", const labelList&, const labelList&)" + ) << "Point " << insidePoint + << " is not inside the mesh." << nl + << "Bounding box of the mesh:" << mesh_.bounds() + << exit(FatalError); + } + + + // Mark correspondence to zone + regionToZone[keepRegionI] = zoneID; + + + // Set all cells with this region to the zoneID + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + label nWarnings = 0; + + forAll(cellRegion, cellI) + { + if (cellRegion[cellI] == keepRegionI) + { + if (cellToZone[cellI] == -2) + { + // First visit of cell + cellToZone[cellI] = zoneID; + } + else if (cellToZone[cellI] != zoneID) + { + if (nWarnings < 10) + { + WarningIn + ( + "meshRefinement::findCellZoneInsideWalk" + "(const labelList&, const labelList&" + ", const labelList&, const labelList&)" + ) << "Cell " << cellI + << " at " << mesh_.cellCentres()[cellI] + << " is inside cellZone " << zonesInMesh[i] + << " from locationInMesh " << insidePoint + << " but already marked as being in zone " + << mesh_.cellZones()[cellToZone[cellI]].name() + << endl + << "This can happen if your surfaces are not" + << " (sufficiently) closed." + << endl; + nWarnings++; + } + } + } + } + } + + + // Check if any unassigned regions + label nUnzoned = 0; + forAll(regionToZone, regionI) + { + if (regionToZone[regionI] == -2) + { + // region that has not been assigned a cellZone + nUnzoned++; + } + } + + if (nUnzoned > 0) + { + Info<< "Detected " << nUnzoned << " regions in the mesh that do" + << " not have a locationInMesh." << nl + << "Per unzoned region displaying a single cell centre:" << endl; + + // Determine single location per unzoned region + pointField regionToLocation + ( + regionToZone.size(), + point(GREAT, GREAT, GREAT) + ); + forAll(cellRegion, cellI) + { + label regionI = cellRegion[cellI]; + if (regionToZone[regionI] == -2) + { + const point& cc = mesh_.cellCentres()[cellI]; + minMagSqrEqOp<point>()(regionToLocation[regionI], cc); + } + } + + Pstream::listCombineGather(regionToLocation, minMagSqrEqOp<point>()); + Pstream::listCombineScatter(regionToLocation); + + forAll(regionToZone, regionI) + { + if (regionToZone[regionI] == -2) + { + Info<< '\t' << regionI + << '\t' << regionToLocation[regionI] << endl; + } + } + Info<< endl; + } +} + + bool Foam::meshRefinement::calcRegionToZone ( const label surfZoneI, @@ -1438,12 +1970,12 @@ bool Foam::meshRefinement::calcRegionToZone // Finds region per cell. Assumes: -// - region containing keepPoint does not go into a cellZone +// - locationsInMesh go into specified cellZone or non-zone // - all other regions can be found by crossing faces marked in // namedSurfaceIndex. void Foam::meshRefinement::findCellZoneTopo ( - const point& keepPoint, + const pointField& locationsInMesh, const labelList& namedSurfaceIndex, const labelList& surfaceToCellZone, labelList& cellToZone @@ -1488,34 +2020,39 @@ void Foam::meshRefinement::findCellZoneTopo // Find the region containing the keepPoint - label keepRegionI = findRegion - ( - mesh_, - cellRegion, - mergeDistance_*vector(1,1,1), - keepPoint - ); - - Info<< "Found point " << keepPoint - << " in global region " << keepRegionI - << " out of " << cellRegion.nRegions() << " regions." << endl; - - if (keepRegionI == -1) + forAll(locationsInMesh, i) { - FatalErrorIn + const point& keepPoint = locationsInMesh[i]; + label keepRegionI = findRegion ( - "meshRefinement::findCellZoneTopo" - "(const point&, const labelList&, const labelList&, labelList&)" - ) << "Point " << keepPoint - << " is not inside the mesh." << nl - << "Bounding box of the mesh:" << mesh_.bounds() - << exit(FatalError); - } + mesh_, + cellRegion, + mergeDistance_*vector(1,1,1), + keepPoint + ); - // Mark default region with zone -1. - if (regionToCellZone[keepRegionI] == -2) - { - regionToCellZone[keepRegionI] = -1; + Info<< "Found point " << keepPoint + << " in global region " << keepRegionI + << " out of " << cellRegion.nRegions() << " regions." << endl; + + if (keepRegionI == -1) + { + FatalErrorIn + ( + "meshRefinement::findCellZoneTopo" + "(const point&, const labelList&" + ", const labelList&, labelList&)" + ) << "Point " << keepPoint + << " is not inside the mesh." << nl + << "Bounding box of the mesh:" << mesh_.bounds() + << exit(FatalError); + } + + // Mark default region with zone -1. + if (regionToCellZone[keepRegionI] == -2) + { + regionToCellZone[keepRegionI] = -1; + } } @@ -1564,25 +2101,11 @@ void Foam::meshRefinement::findCellZoneTopo const polyBoundaryMesh& patches = mesh_.boundaryMesh(); // Get coupled neighbour cellRegion - labelList neiCellRegion(mesh_.nFaces()-mesh_.nInternalFaces()); - forAll(patches, patchI) - { - const polyPatch& pp = patches[patchI]; - - if (pp.coupled()) - { - forAll(pp, i) - { - label faceI = pp.start()+i; - neiCellRegion[faceI-mesh_.nInternalFaces()] = - cellRegion[mesh_.faceOwner()[faceI]]; - } - } - } - syncTools::swapBoundaryFaceList(mesh_, neiCellRegion); - - // Calculate region to zone from cellRegions on either side of coupled - // face. + labelList neiCellRegion; + syncTools::swapBoundaryCellList(mesh_, cellRegion, neiCellRegion); + + // Calculate region to zone from cellRegions on either side of coupled + // face. forAll(patches, patchI) { const polyPatch& pp = patches[patchI]; @@ -1657,7 +2180,7 @@ void Foam::meshRefinement::findCellZoneTopo void Foam::meshRefinement::makeConsistentFaceIndex ( const labelList& cellToZone, - labelList& namedSurfaceIndex + labelList& faceToZone ) const { const labelList& faceOwner = mesh_.faceOwner(); @@ -1668,11 +2191,11 @@ void Foam::meshRefinement::makeConsistentFaceIndex label ownZone = cellToZone[faceOwner[faceI]]; label neiZone = cellToZone[faceNeighbour[faceI]]; - if (ownZone == neiZone && namedSurfaceIndex[faceI] != -1) + if (ownZone == neiZone && faceToZone[faceI] != -1) { - namedSurfaceIndex[faceI] = -1; + faceToZone[faceI] = -1; } - else if (ownZone != neiZone && namedSurfaceIndex[faceI] == -1) + else if (ownZone != neiZone && faceToZone[faceI] == -1) { FatalErrorIn("meshRefinement::zonify()") << "Different cell zones on either side of face " << faceI @@ -1685,22 +2208,8 @@ void Foam::meshRefinement::makeConsistentFaceIndex const polyBoundaryMesh& patches = mesh_.boundaryMesh(); // Get coupled neighbour cellZone - labelList neiCellZone(mesh_.nFaces()-mesh_.nInternalFaces()); - forAll(patches, patchI) - { - const polyPatch& pp = patches[patchI]; - - if (pp.coupled()) - { - forAll(pp, i) - { - label faceI = pp.start()+i; - neiCellZone[faceI-mesh_.nInternalFaces()] = - cellToZone[mesh_.faceOwner()[faceI]]; - } - } - } - syncTools::swapBoundaryFaceList(mesh_, neiCellZone); + labelList neiCellZone; + syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); // Use coupled cellZone to do check forAll(patches, patchI) @@ -1716,11 +2225,11 @@ void Foam::meshRefinement::makeConsistentFaceIndex label ownZone = cellToZone[faceOwner[faceI]]; label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; - if (ownZone == neiZone && namedSurfaceIndex[faceI] != -1) + if (ownZone == neiZone && faceToZone[faceI] != -1) { - namedSurfaceIndex[faceI] = -1; + faceToZone[faceI] = -1; } - else if (ownZone != neiZone && namedSurfaceIndex[faceI] == -1) + else if (ownZone != neiZone && faceToZone[faceI] == -1) { FatalErrorIn("meshRefinement::zonify()") << "Different cell zones on either side of face " @@ -1736,7 +2245,7 @@ void Foam::meshRefinement::makeConsistentFaceIndex forAll(pp, i) { label faceI = pp.start()+i; - namedSurfaceIndex[faceI] = -1; + faceToZone[faceI] = -1; } } } @@ -1773,7 +2282,13 @@ void Foam::meshRefinement::handleSnapProblems } else { - facePatch = markFacesOnProblemCellsGeometric(snapParams, motionDict); + facePatch = markFacesOnProblemCellsGeometric + ( + snapParams, + motionDict, + globalToMasterPatch, + globalToSlavePatch + ); } Info<< "Analyzed problem cells in = " << runTime.cpuTimeIncrement() << " s\n" << nl << endl; @@ -1833,7 +2348,7 @@ void Foam::meshRefinement::handleSnapProblems Foam::labelList Foam::meshRefinement::freeStandingBaffleFaces ( - const labelList& namedSurfaceIndex, + const labelList& faceToZone, const labelList& cellToZone, const labelList& neiCellZone ) const @@ -1843,17 +2358,30 @@ Foam::labelList Foam::meshRefinement::freeStandingBaffleFaces const labelList& faceNeighbour = mesh_.faceNeighbour(); - DynamicList<label> faceLabels(mesh_.nFaces()/20); + // We want to pick up the faces to orient. These faces come in + // two variants: + // - faces originating from stand-alone faceZones + // (these will most likely have no cellZone on either side so + // ownZone and neiZone both -1) + // - sticky-up faces originating from a 'bulge' in a outside of + // a cellZone. These will have the same cellZone on either side. + // How to orient these is not really clearly defined so do them + // same as stand-alone faceZone faces for now. (Normally these will + // already have been removed by the 'allowFreeStandingZoneFaces=false' + // default setting) + + // Note that argument neiCellZone will have -1 on uncoupled boundaries. + + DynamicList<label> faceLabels(mesh_.nFaces()/100); for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) { - label surfI = namedSurfaceIndex[faceI]; - if (surfI != -1) + if (faceToZone[faceI] != -1) { // Free standing baffle? label ownZone = cellToZone[faceOwner[faceI]]; label neiZone = cellToZone[faceNeighbour[faceI]]; - if (max(ownZone, neiZone) == -1) + if (ownZone == neiZone) { faceLabels.append(faceI); } @@ -1866,13 +2394,12 @@ Foam::labelList Foam::meshRefinement::freeStandingBaffleFaces forAll(pp, i) { label faceI = pp.start()+i; - label surfI = namedSurfaceIndex[faceI]; - if (surfI != -1) + if (faceToZone[faceI] != -1) { // Free standing baffle? label ownZone = cellToZone[faceOwner[faceI]]; label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; - if (max(ownZone, neiZone) == -1) + if (ownZone == neiZone) { faceLabels.append(faceI); } @@ -2324,6 +2851,211 @@ void Foam::meshRefinement::consistentOrientation } +void Foam::meshRefinement::zonify +( + // Get per face whether is it master (of a coupled set of faces) + const PackedBoolList& isMasterFace, + const labelList& cellToZone, + const labelList& neiCellZone, + const labelList& faceToZone, + const boolList& meshFlipMap, + polyTopoChange& meshMod +) const +{ + const labelList& faceOwner = mesh_.faceOwner(); + const labelList& faceNeighbour = mesh_.faceNeighbour(); + + for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) + { + label faceZoneI = faceToZone[faceI]; + + if (faceZoneI != -1) + { + // Orient face zone to have slave cells in min cell zone. + // Note: logic to use flipMap should be consistent with logic + // to pick up the freeStandingBaffleFaces! + + label ownZone = cellToZone[faceOwner[faceI]]; + label neiZone = cellToZone[faceNeighbour[faceI]]; + + bool flip; + + if (ownZone == neiZone) + { + // free-standing face. Use geometrically derived orientation + flip = meshFlipMap[faceI]; + } + else + { + flip = + ( + ownZone == -1 + || (neiZone != -1 && ownZone > neiZone) + ); + } + + meshMod.setAction + ( + polyModifyFace + ( + mesh_.faces()[faceI], // modified face + faceI, // label of face + faceOwner[faceI], // owner + faceNeighbour[faceI], // neighbour + false, // face flip + -1, // patch for face + false, // remove from zone + faceZoneI, // zone for face + flip // face flip in zone + ) + ); + } + } + + + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + + // Set owner as no-flip + forAll(patches, patchI) + { + const polyPatch& pp = patches[patchI]; + + label faceI = pp.start(); + + forAll(pp, i) + { + label faceZoneI = faceToZone[faceI]; + + if (faceZoneI != -1) + { + label ownZone = cellToZone[faceOwner[faceI]]; + label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; + + bool flip; + + if (ownZone == neiZone) + { + // free-standing face. Use geometrically derived orientation + flip = meshFlipMap[faceI]; + } + else + { + flip = + ( + ownZone == -1 + || (neiZone != -1 && ownZone > neiZone) + ); + } + + meshMod.setAction + ( + polyModifyFace + ( + mesh_.faces()[faceI], // modified face + faceI, // label of face + faceOwner[faceI], // owner + -1, // neighbour + false, // face flip + patchI, // patch for face + false, // remove from zone + faceZoneI, // zone for face + flip // face flip in zone + ) + ); + } + faceI++; + } + } + + + // Put the cells into the correct zone + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + forAll(cellToZone, cellI) + { + label zoneI = cellToZone[cellI]; + + if (zoneI >= 0) + { + meshMod.setAction + ( + polyModifyCell + ( + cellI, + false, // removeFromZone + zoneI + ) + ); + } + } +} + + +void Foam::meshRefinement::allocateInterRegionFaceZone +( + const label ownZone, + const label neiZone, + wordPairHashTable& zonesToFaceZone, + HashTable<word, labelPair, labelPair::Hash<> >& zoneIDsToFaceZone +) const +{ + const cellZoneMesh& cellZones = mesh_.cellZones(); + + if (ownZone != neiZone) + { + // Make sure lowest number cellZone is master. Non-cellZone + // areas are slave + bool swap = + ( + ownZone == -1 + || (neiZone != -1 && ownZone > neiZone) + ); + + // Quick check whether we already have pair of zones + labelPair key(ownZone, neiZone); + if (swap) + { + Swap(key.first(), key.second()); + } + + HashTable<word, labelPair, labelPair::Hash<> >:: + const_iterator zoneFnd = zoneIDsToFaceZone.find + ( + key + ); + + if (zoneFnd == zoneIDsToFaceZone.end()) + { + // Not found. Allocate. + const word ownZoneName = + ( + ownZone != -1 + ? cellZones[ownZone].name() + : "none" + ); + const word neiZoneName = + ( + neiZone != -1 + ? cellZones[neiZone].name() + : "none" + ); + + // Get lowest zone first + Pair<word> wordKey(ownZoneName, neiZoneName); + if (swap) + { + Swap(wordKey.first(), wordKey.second()); + } + + word fzName = wordKey.first() + "_to_" + wordKey.second(); + + zoneIDsToFaceZone.insert(key, fzName); + zonesToFaceZone.insert(wordKey, fzName); + } + } +} + + // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // Split off unreachable areas of mesh. @@ -2334,13 +3066,14 @@ void Foam::meshRefinement::baffleAndSplitMesh const bool useTopologicalSnapDetection, const bool removeEdgeConnectedCells, const scalarField& perpendicularAngle, - const bool mergeFreeStanding, - const scalar planarAngle, const dictionary& motionDict, Time& runTime, const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh ) { // Introduce baffles @@ -2362,6 +3095,10 @@ void Foam::meshRefinement::baffleAndSplitMesh getBafflePatches ( globalToMasterPatch, + + locationsInMesh, + zonesInMesh, + neiLevel, neiCc, @@ -2415,6 +3152,38 @@ void Foam::meshRefinement::baffleAndSplitMesh globalToMasterPatch, globalToSlavePatch ); + + // Removing additional cells might have created disconnected bits + // so re-do the surface intersections + { + // Swap neighbouring cell centres and cell level + neiLevel.setSize(mesh_.nFaces()-mesh_.nInternalFaces()); + neiCc.setSize(mesh_.nFaces()-mesh_.nInternalFaces()); + calcNeighbourData(neiLevel, neiCc); + + labelList ownPatch, neiPatch; + getBafflePatches + ( + globalToMasterPatch, + + locationsInMesh, + zonesInMesh, + + neiLevel, + neiCc, + + ownPatch, + neiPatch + ); + + createBaffles(ownPatch, neiPatch); + } + + if (debug) + { + // Debug:test all is still synced across proc patches + checkData(); + } } @@ -2431,7 +3200,13 @@ void Foam::meshRefinement::baffleAndSplitMesh runTime++; } - splitMeshRegions(globalToMasterPatch, globalToSlavePatch, keepPoint); + splitMeshRegions + ( + globalToMasterPatch, + globalToSlavePatch, + locationsInMesh, + locationsOutsideMesh + ); if (debug) { @@ -2456,74 +3231,111 @@ void Foam::meshRefinement::baffleAndSplitMesh Pout<< "Dumped debug data in = " << runTime.cpuTimeIncrement() << " s\n" << nl << endl; } +} +void Foam::meshRefinement::mergeFreeStandingBaffles +( + const snapParameters& snapParams, + const bool useTopologicalSnapDetection, + const bool removeEdgeConnectedCells, + const scalarField& perpendicularAngle, + const scalar planarAngle, + const dictionary& motionDict, + Time& runTime, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch, + const pointField& locationsInMesh, + const pointField& locationsOutsideMesh +) +{ // Merge baffles // ~~~~~~~~~~~~~ - if (mergeFreeStanding) + Info<< nl + << "Merge free-standing baffles" << nl + << "---------------------------" << nl + << endl; + + + // List of pairs of freestanding baffle faces. + List<labelPair> couples + ( + freeStandingBaffles // filter out freestanding baffles + ( + localPointRegion::findDuplicateFacePairs(mesh_), + planarAngle + ) + ); + + label nCouples = couples.size(); + reduce(nCouples, sumOp<label>()); + + Info<< "Detected free-standing baffles : " << nCouples << endl; + + if (nCouples > 0) { + // Actually merge baffles. Note: not exactly parallellized. Should + // convert baffle faces into processor faces if they resulted + // from them. + mergeBaffles(couples, Map<label>(0)); + + // Detect any problem cells resulting from merging of baffles + // and delete them + handleSnapProblems + ( + snapParams, + useTopologicalSnapDetection, + removeEdgeConnectedCells, + perpendicularAngle, + motionDict, + runTime, + globalToMasterPatch, + globalToSlavePatch + ); + + // Very occasionally removing a problem cell might create a disconnected + // region so re-check + Info<< nl - << "Merge free-standing baffles" << nl - << "---------------------------" << nl + << "Remove unreachable sections of mesh" << nl + << "-----------------------------------" << nl << endl; + if (debug) + { + runTime++; + } - // List of pairs of freestanding baffle faces. - List<labelPair> couples + splitMeshRegions ( - freeStandingBaffles // filter out freestanding baffles - ( - localPointRegion::findDuplicateFacePairs(mesh_), - planarAngle - ) + globalToMasterPatch, + globalToSlavePatch, + locationsInMesh, + locationsOutsideMesh ); - label nCouples = couples.size(); - reduce(nCouples, sumOp<label>()); - Info<< "Detected free-standing baffles : " << nCouples << endl; - - if (nCouples > 0) + if (debug) { - // Actually merge baffles. Note: not exactly parallellized. Should - // convert baffle faces into processor faces if they resulted - // from them. - mergeBaffles(couples); - - // Detect any problem cells resulting from merging of baffles - // and delete them - handleSnapProblems - ( - snapParams, - useTopologicalSnapDetection, - removeEdgeConnectedCells, - perpendicularAngle, - motionDict, - runTime, - globalToMasterPatch, - globalToSlavePatch - ); - - if (debug) - { - // Debug:test all is still synced across proc patches - checkData(); - } + // Debug:test all is still synced across proc patches + checkData(); } - Info<< "Merged free-standing baffles in = " - << runTime.cpuTimeIncrement() << " s\n" << nl << endl; } + Info<< "Merged free-standing baffles in = " + << runTime.cpuTimeIncrement() << " s\n" << nl << endl; } -// Split off (with optional buffer layers) unreachable areas of mesh. Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh ( const label nBufferLayers, const labelList& globalToMasterPatch, const labelList& globalToSlavePatch, - const point& keepPoint + + const pointField& locationsInMesh, + const wordList& zonesInMesh, + const pointField& locationsOutsideMesh ) { // Determine patches to put intersections into @@ -2534,10 +3346,15 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces()); calcNeighbourData(neiLevel, neiCc); + // Find intersections with all unnamed(!) surfaces labelList ownPatch, neiPatch; getBafflePatches ( globalToMasterPatch, + + locationsInMesh, + zonesInMesh, + neiLevel, neiCc, @@ -2557,39 +3374,25 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh } syncTools::syncFaceList(mesh_, blockedFace, orEqOp<bool>()); - // Set region per cell based on walking + regionSplit cellRegion(mesh_, blockedFace); blockedFace.clear(); - // Find the region containing the keepPoint - const label keepRegionI = findRegion + // Set unreachable cells to -1 + findRegions ( mesh_, - cellRegion, - mergeDistance_*vector(1,1,1), - keepPoint + mergeDistance_*vector(1,1,1), // perturbVec + locationsInMesh, + locationsOutsideMesh, + cellRegion.nRegions(), + cellRegion ); - Info<< "Found point " << keepPoint - << " in global region " << keepRegionI - << " out of " << cellRegion.nRegions() << " regions." << endl; - if (keepRegionI == -1) - { - FatalErrorIn - ( - "meshRefinement::splitMesh" - "(const label, const labelList&, const point&)" - ) << "Point " << keepPoint - << " is not inside the mesh." << nl - << "Bounding box of the mesh:" << mesh_.bounds() - << exit(FatalError); - } - - - // Walk out nBufferlayers from region split + // Walk out nBufferlayers from region boundary + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // (modifies cellRegion, ownPatch) - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Takes over face patch onto points and then back to faces and cells // (so cell-face-point walk) @@ -2616,7 +3419,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh label ownRegion = cellRegion[faceOwner[faceI]]; label neiRegion = cellRegion[faceNeighbour[faceI]]; - if (ownRegion == keepRegionI && neiRegion != keepRegionI) + if (ownRegion == -1 && neiRegion != -1) { // Note max(..) since possibly regionSplit might have split // off extra unreachable parts of mesh. Note: or can this only @@ -2626,7 +3429,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh pointBaffle[f[fp]] = max(defaultPatch, ownPatch[faceI]); } } - else if (ownRegion != keepRegionI && neiRegion == keepRegionI) + else if (ownRegion != -1 && neiRegion == -1) { label newPatchI = neiPatch[faceI]; if (newPatchI == -1) @@ -2650,7 +3453,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh label ownRegion = cellRegion[faceOwner[faceI]]; - if (ownRegion == keepRegionI) + if (ownRegion == -1) { forAll(f, fp) { @@ -2703,9 +3506,9 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh { label own = faceOwner[faceI]; - if (cellRegion[own] != keepRegionI) + if (cellRegion[own] == -1) { - cellRegion[own] = keepRegionI; + cellRegion[own] = labelMax; const cell& ownFaces = mesh_.cells()[own]; forAll(ownFaces, j) @@ -2720,9 +3523,9 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh { label nei = faceNeighbour[faceI]; - if (cellRegion[nei] != keepRegionI) + if (cellRegion[nei] == -1) { - cellRegion[nei] = keepRegionI; + cellRegion[nei] = labelMax; const cell& neiFaces = mesh_.cells()[nei]; forAll(neiFaces, j) @@ -2751,7 +3554,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh DynamicList<label> cellsToRemove(mesh_.nCells()); forAll(cellRegion, cellI) { - if (cellRegion[cellI] != keepRegionI) + if (cellRegion[cellI] == -1) { cellsToRemove.append(cellI); } @@ -2761,10 +3564,8 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::splitMesh label nCellsToKeep = mesh_.nCells() - cellsToRemove.size(); reduce(nCellsToKeep, sumOp<label>()); - Info<< "Keeping all cells in region " << keepRegionI - << " containing point " << keepPoint << endl - << "Selected for keeping : " << nCellsToKeep - << " cells." << endl; + Info<< "Keeping all cells containing points " << locationsInMesh << endl + << "Selected for keeping : " << nCellsToKeep << " cells." << endl; // Remove cells @@ -2825,35 +3626,41 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::dupNonManifoldPoints << mesh_.globalData().nTotalPoints() << ')' << endl; - // Topo change engine - duplicatePoints pointDuplicator(mesh_); - // Insert changes into meshMod - pointDuplicator.setRefinement(regionSide, meshMod); + autoPtr<mapPolyMesh> map; - // Change the mesh (no inflation, parallel sync) - autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + if (nNonManifPoints) + { + // Topo change engine + duplicatePoints pointDuplicator(mesh_); - // Update fields - mesh_.updateMesh(map); + // Insert changes into meshMod + pointDuplicator.setRefinement(regionSide, meshMod); - // Move mesh if in inflation mode - if (map().hasMotionPoints()) - { - mesh_.movePoints(map().preMotionPoints()); - } - else - { - // Delete mesh volumes. - mesh_.clearOut(); - } + // Change the mesh (no inflation, parallel sync) + map = meshMod.changeMesh(mesh_, false, true); - // Reset the instance for if in overwrite mode - mesh_.setInstance(timeName()); + // Update fields + mesh_.updateMesh(map); + + // Move mesh if in inflation mode + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + else + { + // Delete mesh volumes. + mesh_.clearOut(); + } + + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); - // Update intersections. Is mapping only (no faces created, positions stay - // same) so no need to recalculate intersections. - updateMesh(map, labelList(0)); + // Update intersections. Is mapping only (no faces created, positions + // stay same) so no need to recalculate intersections. + updateMesh(map, labelList(0)); + } return map; } @@ -2870,45 +3677,181 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::dupNonManifoldPoints() } -// Zoning -Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify +Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::mergePoints ( - const point& keepPoint, - const bool allowFreeStandingZoneFaces + const labelList& pointToDuplicate ) { - const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones(); + label nPointPairs = 0; + forAll(pointToDuplicate, pointI) + { + label otherPointI = pointToDuplicate[pointI]; + if (otherPointI != -1) + { + nPointPairs++; + } + } - labelList namedSurfaces(surfaceZonesInfo::getNamedSurfaces(surfZones)); + autoPtr<mapPolyMesh> map; - forAll(namedSurfaces, i) + if (returnReduce(nPointPairs, sumOp<label>())) { - label surfI = namedSurfaces[i]; + Map<label> pointToMaster(2*nPointPairs); + forAll(pointToDuplicate, pointI) + { + label otherPointI = pointToDuplicate[pointI]; + if (otherPointI != -1) + { + // Slave point + pointToMaster.insert(pointI, otherPointI); + } + } + + // Topochange container + polyTopoChange meshMod(mesh_); + + // Insert changes + polyMeshAdder::mergePoints(mesh_, pointToMaster, meshMod); - Info<< "Surface : " << surfaces_.names()[surfI] << nl - << " faceZone : " << surfZones[surfI].faceZoneName() << nl - << " cellZone : " << surfZones[surfI].cellZoneName() << endl; + // Change the mesh (no inflation, parallel sync) + map = meshMod.changeMesh(mesh_, false, true); + + // Update fields + mesh_.updateMesh(map); + + // Move mesh if in inflation mode + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + else + { + // Delete mesh volumes. + mesh_.clearOut(); + } + + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); + + // Update intersections. Is mapping only (no faces created, positions + // stay same) so no need to recalculate intersections. + updateMesh(map, labelList(0)); } + return map; +} - // Add zones to mesh - labelList surfaceToFaceZone = - surfaceZonesInfo::addFaceZonesToMesh - ( - surfZones, - namedSurfaces, - mesh_ - ); - labelList surfaceToCellZone = - surfaceZonesInfo::addCellZonesToMesh +// Duplicate points on 'boundary' zones. Do not duplicate points on +// 'internal' or 'baffle' zone. Whether points are on normal patches does +// not matter +Foam::autoPtr<Foam::mapPolyMesh> +Foam::meshRefinement::dupNonManifoldBoundaryPoints() +{ + const labelList boundaryFaceZones + ( + getZones ( - surfZones, - namedSurfaces, - mesh_ - ); + List<surfaceZonesInfo::faceZoneType> + ( + 1, + surfaceZonesInfo::BOUNDARY + ) + ) + ); + labelList internalOrBaffleFaceZones; + { + List<surfaceZonesInfo::faceZoneType> fzTypes(2); + fzTypes[0] = surfaceZonesInfo::INTERNAL; + fzTypes[1] = surfaceZonesInfo::BAFFLE; + internalOrBaffleFaceZones = getZones(fzTypes); + } + + + // 0 : point used by normal, unzoned boundary faces + // 1 : point used by 'boundary' zone + // 2 : point used by internal/baffle zone + PackedList<2> pointStatus(mesh_.nPoints(), 0u); + forAll(boundaryFaceZones, j) + { + const faceZone& fZone = mesh_.faceZones()[boundaryFaceZones[j]]; + forAll(fZone, i) + { + const face& f = mesh_.faces()[fZone[i]]; + forAll(f, fp) + { + pointStatus[f[fp]] = max(pointStatus[f[fp]], 1u); + } + } + } + forAll(internalOrBaffleFaceZones, j) + { + const faceZone& fZone = mesh_.faceZones()[internalOrBaffleFaceZones[j]]; + forAll(fZone, i) + { + const face& f = mesh_.faces()[fZone[i]]; + forAll(f, fp) + { + pointStatus[f[fp]] = max(pointStatus[f[fp]], 2u); + } + } + } + + syncTools::syncPointList + ( + mesh_, + pointStatus, + maxEqOp<unsigned int>(), // combine op + 0u // null value + ); + + // Pick up points on boundary zones that are not on internal/baffle zones + label n = 0; + forAll(pointStatus, pointI) + { + if (pointStatus[pointI] == 1u) + { + n++; + } + } + + label globalNPoints = returnReduce(n, sumOp<label>()); + Info<< "Duplicating " << globalNPoints << " points on" + << " faceZones of type " + << surfaceZonesInfo::faceZoneTypeNames[surfaceZonesInfo::BOUNDARY] + << endl; + + autoPtr<mapPolyMesh> map; + + if (globalNPoints) + { + labelList candidatePoints(n); + n = 0; + forAll(pointStatus, pointI) + { + if (pointStatus[pointI] == 1u) + { + candidatePoints[n++] = pointI; + } + } + localPointRegion regionSide(mesh_, candidatePoints); + map = dupNonManifoldPoints(regionSide); + } + return map; +} + + +// Zoning +Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify +( + const bool allowFreeStandingZoneFaces, + const pointField& locationsInMesh, + const wordList& zonesInMesh, + wordPairHashTable& zonesToFaceZone +) +{ const pointField& cellCentres = mesh_.cellCentres(); const labelList& faceOwner = mesh_.faceOwner(); const labelList& faceNeighbour = mesh_.faceNeighbour(); @@ -2921,279 +3864,570 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify calcNeighbourData(neiLevel, neiCc); - // Mark faces intersecting zoned surfaces - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Put the cells into the correct zone + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Zone per cell: + // -2 : unset + // -1 : not in any zone (zone 'none') + // >=0: zoneID + labelList cellToZone(mesh_.nCells(), -2); - // Like surfaceIndex_ but only for named surfaces. - labelList namedSurfaceIndex(mesh_.nFaces(), -1); - PackedBoolList posOrientation(mesh_.nFaces()); + // Add from locationsInMesh + // ~~~~~~~~~~~~~~~~~~~~~~~~ + + // Filter out keepPoints + labelList zonedIndices(refinementParameters::zonedLocations(zonesInMesh)); + + if (zonedIndices.size()) { - // Statistics: number of faces per faceZone - labelList nSurfFaces(surfZones.size(), 0); + // Explicitly provided locations and their cellZone. Determine all + // blocked faces and do walking. Updates cellToZone. - // Note: for all internal faces? internal + coupled? - // Since zonify is run after baffling the surfaceIndex_ on baffles is - // not synchronised across both baffle faces. Fortunately we don't - // do zonify baffle faces anyway (they are normal boundary faces). + Info<< "Setting cellZones according to locationsInMesh:" << endl; + //forAll(locationsInMesh, regionI) + forAll(zonedIndices, j) + { + label regionI = zonedIndices[j]; + Info<< "Location : " << locationsInMesh[regionI] << nl + << " cellZone : " << zonesInMesh[regionI] << endl; + } + Info<< endl; - // Collect candidate faces - // ~~~~~~~~~~~~~~~~~~~~~~~ + // Test all (unnamed & named) surfaces + labelList globalRegion1; + labelList globalRegion2; + getIntersections + ( + identity(surfaces_.surfaces().size()), // surfacesToTest, + neiCc, + intersectedFaces(), // testFaces + globalRegion1, + globalRegion2 + ); - labelList testFaces(intersectedFaces()); + // Assign cellZone according to seed points + findCellZoneInsideWalk + ( + pointField(locationsInMesh, zonedIndices), // locations + UIndirectList<word>(zonesInMesh, zonedIndices)(),// name of region + globalRegion1, // per face -1 (unblocked) or >= 0 (blocked) + cellToZone + ); + } - // Collect segments - // ~~~~~~~~~~~~~~~~ - pointField start(testFaces.size()); - pointField end(testFaces.size()); - forAll(testFaces, i) - { - label faceI = testFaces[i]; + // Mark faces intersecting zoned surfaces + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - if (mesh_.isInternalFace(faceI)) - { - start[i] = cellCentres[faceOwner[faceI]]; - end[i] = cellCentres[faceNeighbour[faceI]]; - } - else - { - start[i] = cellCentres[faceOwner[faceI]]; - end[i] = neiCc[faceI-mesh_.nInternalFaces()]; - } - } + const PtrList<surfaceZonesInfo>& surfZones = surfaces_.surfZones(); - // Extend segments a bit + labelList namedSurfaces(surfaceZonesInfo::getNamedSurfaces(surfZones)); + + + //- Face face orientation of zone (only from those intersecting named + // surfaces) + PackedBoolList posOrientation(mesh_.nFaces()); + //- Per face index of faceZone or -1 + labelList faceToZone(mesh_.nFaces(), -1); + + if (namedSurfaces.size()) + { + Info<< "Setting cellZones according to named surfaces:" << endl; + forAll(namedSurfaces, i) { - const vectorField smallVec(Foam::sqrt(SMALL)*(end-start)); - start -= smallVec; - end += smallVec; + label surfI = namedSurfaces[i]; + + Info<< "Surface : " << surfaces_.names()[surfI] << nl + << " faceZone : " << surfZones[surfI].faceZoneName() << nl + << " cellZone : " << surfZones[surfI].cellZoneName() << endl; } + Info<< endl; - // Do test for intersections - // ~~~~~~~~~~~~~~~~~~~~~~~~~ - // Note that we intersect all intersected faces again. Could reuse - // the information already in surfaceIndex_. + // Add zones to mesh + labelList surfaceToFaceZone = + surfaceZonesInfo::addFaceZonesToMesh + ( + surfZones, + namedSurfaces, + mesh_ + ); - labelList surface1; - List<pointIndexHit> hit1; - vectorField normal1; - labelList surface2; - List<pointIndexHit> hit2; - vectorField normal2; - { - labelList region1; - labelList region2; - surfaces_.findNearestIntersection + labelList surfaceToCellZone = + surfaceZonesInfo::addCellZonesToMesh ( + surfZones, namedSurfaces, - start, - end, - - surface1, - hit1, - region1, - normal1, - - surface2, - hit2, - region2, - normal2 + mesh_ ); - } - forAll(testFaces, i) + + + + // Like surfaceIndex_ but only for named surfaces. + labelList namedSurfaceIndex(mesh_.nFaces(), -1); + { - label faceI = testFaces[i]; - const vector& area = mesh_.faceAreas()[faceI]; + // Statistics: number of faces per faceZone + labelList nSurfFaces(surfZones.size(), 0); - if (surface1[i] != -1) + // Note: for all internal faces? internal + coupled? + // Since zonify is run after baffling the surfaceIndex_ on baffles + // is + // not synchronised across both baffle faces. Fortunately we don't + // do zonify baffle faces anyway (they are normal boundary faces). + + // Collect candidate faces + // ~~~~~~~~~~~~~~~~~~~~~~~ + + labelList testFaces(intersectedFaces()); + + // Collect segments + // ~~~~~~~~~~~~~~~~ + + pointField start(testFaces.size()); + pointField end(testFaces.size()); + + forAll(testFaces, i) { - // If both hit should probably choose 'nearest' - if - ( - surface2[i] != -1 - && ( - magSqr(hit2[i].hitPoint()) - < magSqr(hit1[i].hitPoint()) - ) - ) + label faceI = testFaces[i]; + + if (mesh_.isInternalFace(faceI)) { - namedSurfaceIndex[faceI] = surface2[i]; - posOrientation[faceI] = ((area&normal2[i]) > 0); - nSurfFaces[surface2[i]]++; + start[i] = cellCentres[faceOwner[faceI]]; + end[i] = cellCentres[faceNeighbour[faceI]]; } else { - namedSurfaceIndex[faceI] = surface1[i]; - posOrientation[faceI] = ((area&normal1[i]) > 0); - nSurfFaces[surface1[i]]++; + start[i] = cellCentres[faceOwner[faceI]]; + end[i] = neiCc[faceI-mesh_.nInternalFaces()]; } } - else if (surface2[i] != -1) + + // Extend segments a bit { - namedSurfaceIndex[faceI] = surface2[i]; - posOrientation[faceI] = ((area&normal2[i]) > 0); - nSurfFaces[surface2[i]]++; + const vectorField smallVec(Foam::sqrt(SMALL)*(end-start)); + start -= smallVec; + end += smallVec; } - } - // surfaceIndex might have different surfaces on both sides if - // there happen to be a (obviously thin) surface with different - // regions between the cell centres. If one is on a named surface - // and the other is not this might give problems so sync. - syncTools::syncFaceList - ( - mesh_, - namedSurfaceIndex, - maxEqOp<label>() - ); + // Do test for intersections + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // Note that we intersect all intersected faces again. Could reuse + // the information already in surfaceIndex_. - // Print a bit - if (debug) - { - forAll(nSurfFaces, surfI) + labelList surface1; + List<pointIndexHit> hit1; + vectorField normal1; + labelList surface2; + List<pointIndexHit> hit2; + vectorField normal2; { - Pout<< "Surface:" - << surfaces_.names()[surfI] - << " nZoneFaces:" << nSurfFaces[surfI] << nl; + labelList region1; + labelList region2; + surfaces_.findNearestIntersection + ( + namedSurfaces, + start, + end, + + surface1, + hit1, + region1, + normal1, + + surface2, + hit2, + region2, + normal2 + ); } - Pout<< endl; - } - } + forAll(testFaces, i) + { + label faceI = testFaces[i]; + const vector& area = mesh_.faceAreas()[faceI]; - // Put the cells into the correct zone - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if (surface1[i] != -1) + { + // If both hit should probably choose 'nearest' + if + ( + surface2[i] != -1 + && ( + magSqr(hit2[i].hitPoint()) + < magSqr(hit1[i].hitPoint()) + ) + ) + { + namedSurfaceIndex[faceI] = surface2[i]; + posOrientation[faceI] = ((area&normal2[i]) > 0); + nSurfFaces[surface2[i]]++; + } + else + { + namedSurfaceIndex[faceI] = surface1[i]; + posOrientation[faceI] = ((area&normal1[i]) > 0); + nSurfFaces[surface1[i]]++; + } + } + else if (surface2[i] != -1) + { + namedSurfaceIndex[faceI] = surface2[i]; + posOrientation[faceI] = ((area&normal2[i]) > 0); + nSurfFaces[surface2[i]]++; + } + } - // Zone per cell: - // -2 : unset - // -1 : not in any zone - // >=0: zoneID - labelList cellToZone(mesh_.nCells(), -2); + // surfaceIndex might have different surfaces on both sides if + // there happen to be a (obviously thin) surface with different + // regions between the cell centres. If one is on a named surface + // and the other is not this might give problems so sync. + syncTools::syncFaceList + ( + mesh_, + namedSurfaceIndex, + maxEqOp<label>() + ); - // Set using geometric test - // ~~~~~~~~~~~~~~~~~~~~~~~~ + // Print a bit + if (debug) + { + forAll(nSurfFaces, surfI) + { + Pout<< "Surface:" + << surfaces_.names()[surfI] + << " nZoneFaces:" << nSurfFaces[surfI] << nl; + } + Pout<< endl; + } + } - // Closed surfaces with cellZone specified. - labelList closedNamedSurfaces - ( - surfaceZonesInfo::getClosedNamedSurfaces - ( - surfZones, - surfaces_.geometry(), - surfaces_.surfaces() - ) - ); - if (closedNamedSurfaces.size()) - { - Info<< "Found " << closedNamedSurfaces.size() - << " closed, named surfaces. Assigning cells in/outside" - << " these surfaces to the corresponding cellZone." - << nl << endl; + // Now we have for all faces the intersection with a named surfaces (or + // -1). Use this information to set zone of cells - findCellZoneGeometric - ( - neiCc, - closedNamedSurfaces, // indices of closed surfaces - namedSurfaceIndex, // per face index of named surface - surfaceToCellZone, // cell zone index per surface + // Set using geometric test + // ~~~~~~~~~~~~~~~~~~~~~~~~ - cellToZone + // Closed surfaces with cellZone specified. + labelList closedNamedSurfaces + ( + surfaceZonesInfo::getClosedNamedSurfaces + ( + surfZones, + surfaces_.geometry(), + surfaces_.surfaces() + ) ); - } + if (closedNamedSurfaces.size()) + { + Info<< "Found " << closedNamedSurfaces.size() + << " closed, named surfaces. Assigning cells in/outside" + << " these surfaces to the corresponding cellZone." + << nl << endl; + + findCellZoneGeometric + ( + neiCc, + closedNamedSurfaces, // indices of closed surfaces + namedSurfaceIndex, // per face index of named surface + surfaceToCellZone, // cell zone index per surface - // Set using provided locations - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + cellToZone + ); + } - labelList locationSurfaces - ( - surfaceZonesInfo::getInsidePointNamedSurfaces(surfZones) - ); - if (locationSurfaces.size()) - { - Info<< "Found " << locationSurfaces.size() - << " named surfaces with a provided inside point." - << " Assigning cells inside these surfaces" - << " to the corresponding cellZone." - << nl << endl; + // Set using provided locations + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - findCellZoneInsideWalk + labelList locationSurfaces ( - locationSurfaces, // indices of closed surfaces - namedSurfaceIndex, // per face index of named surface - surfaceToCellZone, // cell zone index per surface - - cellToZone + surfaceZonesInfo::getInsidePointNamedSurfaces(surfZones) ); - } + if (locationSurfaces.size()) + { + Info<< "Found " << locationSurfaces.size() + << " named surfaces with a provided inside point." + << " Assigning cells inside these surfaces" + << " to the corresponding cellZone." + << nl << endl; - // Set using walking - // ~~~~~~~~~~~~~~~~~ + findCellZoneInsideWalk + ( + locationSurfaces, // indices of closed surfaces + namedSurfaceIndex, // per face index of named surface + surfaceToCellZone, // cell zone index per surface - { - Info<< "Walking from location-in-mesh " << keepPoint + cellToZone + ); + } + + + // Set using walking + // ~~~~~~~~~~~~~~~~~ + + Info<< "Walking from " << locationsInMesh.size() + << " locations-in-mesh " << " to assign cellZones " << "- crossing a faceZone face changes cellZone" << nl << endl; - // Topological walk + labelList zoneIDs(refinementParameters::unzonedLocations(zonesInMesh)); + findCellZoneTopo ( - keepPoint, + pointField(locationsInMesh, zoneIDs), namedSurfaceIndex, surfaceToCellZone, cellToZone ); + + + // Make sure namedSurfaceIndex is unset inbetween same cell zones. + if (!allowFreeStandingZoneFaces) + { + Info<< "Only keeping zone faces inbetween different cellZones." + << nl << endl; + + makeConsistentFaceIndex(cellToZone, namedSurfaceIndex); + } + + + // Convert namedSurfaceIndex (index of named surfaces) to + // actual faceZone index + + forAll(namedSurfaceIndex, faceI) + { + label surfI = namedSurfaceIndex[faceI]; + if (surfI != -1) + { + faceToZone[faceI] = surfaceToFaceZone[surfI]; + } + } } - // Make sure namedSurfaceIndex is unset inbetween same cell cell zones. - if (!allowFreeStandingZoneFaces) + + //if (debug&MESH) + //{ + // const_cast<Time&>(mesh_.time())++; + // Pout<< "Writing mesh to time " << timeName() << endl; + // write + // ( + // debugType(debug), + // writeType(writeLevel() | WRITEMESH), + // mesh_.time().path()/"cellToZone" + // ); + // volScalarField volCellToZone + // ( + // IOobject + // ( + // "cellToZone", + // mesh_.time().timeName(), + // mesh_, + // IOobject::NO_READ, + // IOobject::AUTO_WRITE, + // false + // ), + // mesh_, + // dimensionedScalar("zero", dimless, 0), + // zeroGradientFvPatchScalarField::typeName + // ); + // + // forAll(cellToZone, cellI) + // { + // volCellToZone[cellI] = cellToZone[cellI]; + // } + // volCellToZone.write(); + //} + + + + // Allocate and assign faceZones from cellZones + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + { - Info<< "Only keeping zone faces inbetween different cellZones." - << nl << endl; + // 1. Detect inter-region face and allocate names + + HashTable<word, labelPair, labelPair::Hash<> > zoneIDsToFaceZone; - makeConsistentFaceIndex(cellToZone, namedSurfaceIndex); + for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) + { + if (faceToZone[faceI] == -1) + { + // Face not yet in a faceZone. (it might already have been + // done so by a 'named' surface). Check if inbetween different + // cellZones + allocateInterRegionFaceZone + ( + cellToZone[mesh_.faceOwner()[faceI]], + cellToZone[mesh_.faceNeighbour()[faceI]], + zonesToFaceZone, + zoneIDsToFaceZone + ); + } + } + + labelList neiCellZone; + syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); + + forAll(neiCellZone, bFaceI) + { + label faceI = bFaceI + mesh_.nInternalFaces(); + if (faceToZone[faceI] == -1) + { + allocateInterRegionFaceZone + ( + cellToZone[mesh_.faceOwner()[faceI]], + neiCellZone[bFaceI], + zonesToFaceZone, + zoneIDsToFaceZone + ); + } + } + + + // 2. Combine faceZoneNames allocated on different processors + + Pstream::mapCombineGather(zonesToFaceZone, eqOp<word>()); + Pstream::mapCombineScatter(zonesToFaceZone); + + + // 3. Allocate faceZones from (now synchronised) faceZoneNames + // Note: the faceZoneNames contain the same data but in different + // order. We could sort the contents but instead just loop + // in sortedToc order. + + Info<< "Setting faceZones according to neighbouring cellZones:" + << endl; + + // From cellZone indices to faceZone index + HashTable<label, labelPair, labelPair::Hash<> > fZoneLookup + ( + zonesToFaceZone.size() + ); + + const cellZoneMesh& cellZones = mesh_.cellZones(); + + { + List<Pair<word> > czs(zonesToFaceZone.sortedToc()); + + forAll(czs, i) + { + const Pair<word>& cz = czs[i]; + const word& fzName = zonesToFaceZone[cz]; + + Info<< indent<< "cellZones : " + << cz[0] << ' ' << cz[1] << nl + << " faceZone : " << fzName << endl; + + label faceZoneI = surfaceZonesInfo::addFaceZone + ( + fzName, // name + labelList(0), // addressing + boolList(0), // flipMap + mesh_ + ); + + label cz0 = cellZones.findZoneID(cz[0]); + label cz1 = cellZones.findZoneID(cz[1]); + + fZoneLookup.insert(labelPair(cz0, cz1), faceZoneI); + } + } + + + // 4. Set faceToZone with new faceZones + + + for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) + { + if (faceToZone[faceI] == -1) + { + // Face not yet in a faceZone. (it might already have been + // done so by a 'named' surface). Check if inbetween different + // cellZones + + label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; + label neiZone = cellToZone[mesh_.faceNeighbour()[faceI]]; + if (ownZone != neiZone) + { + bool swap = + ( + ownZone == -1 + || (neiZone != -1 && ownZone > neiZone) + ); + labelPair key(ownZone, neiZone); + if (swap) + { + Swap(key.first(), key.second()); + } + faceToZone[faceI] = fZoneLookup[key]; + } + } + } + forAll(neiCellZone, bFaceI) + { + label faceI = bFaceI + mesh_.nInternalFaces(); + if (faceToZone[faceI] == -1) + { + label ownZone = cellToZone[mesh_.faceOwner()[faceI]]; + label neiZone = neiCellZone[bFaceI]; + if (ownZone != neiZone) + { + bool swap = + ( + ownZone == -1 + || (neiZone != -1 && ownZone > neiZone) + ); + labelPair key(ownZone, neiZone); + if (swap) + { + Swap(key.first(), key.second()); + } + faceToZone[faceI] = fZoneLookup[key]; + } + } + } + Info<< endl; } - // Topochange container - polyTopoChange meshMod(mesh_); // Get coupled neighbour cellZone. Set to -1 on non-coupled patches. - labelList neiCellZone(mesh_.nFaces()-mesh_.nInternalFaces(), -1); + labelList neiCellZone; + syncTools::swapBoundaryCellList(mesh_, cellToZone, neiCellZone); forAll(patches, patchI) { const polyPatch& pp = patches[patchI]; - if (pp.coupled()) + if (!pp.coupled()) { + label bFaceI = pp.start()-mesh_.nInternalFaces(); forAll(pp, i) { - label faceI = pp.start()+i; - neiCellZone[faceI-mesh_.nInternalFaces()] = - cellToZone[mesh_.faceOwner()[faceI]]; + neiCellZone[bFaceI++] = -1; } } } - syncTools::swapBoundaryFaceList(mesh_, neiCellZone); + + // Get per face whether is it master (of a coupled set of faces) const PackedBoolList isMasterFace(syncTools::getMasterFaces(mesh_)); - // faceZones // ~~~~~~~~~ // Faces on faceZones come in two variants: @@ -3214,7 +4448,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify mesh_.faces(), freeStandingBaffleFaces ( - namedSurfaceIndex, + faceToZone, cellToZone, neiCellZone ) @@ -3228,18 +4462,25 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify Info<< "Detected " << nFreeStanding << " free-standing zone faces" << endl; + if (debug) + { + OBJstream str(mesh_.time().path()/"freeStanding.obj"); + str.write(patch.localFaces(), patch.localPoints(), false); + } + + // Detect non-manifold edges labelList nMasterFacesPerEdge; calcPatchNumMasterFaces(isMasterFace, patch, nMasterFacesPerEdge); // Mark zones. Even a single original surface might create multiple // disconnected/non-manifold-connected zones - labelList faceToZone; + labelList faceToConnectedZone; const label nZones = markPatchZones ( patch, nMasterFacesPerEdge, - faceToZone + faceToConnectedZone ); Map<label> nPosOrientation(2*nZones); @@ -3256,7 +4497,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify isMasterFace, patch, nMasterFacesPerEdge, - faceToZone, + faceToConnectedZone, nPosOrientation, meshFlipMap @@ -3280,7 +4521,7 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify n = -1; } - nPosOrientation.find(faceToZone[faceI])() += n; + nPosOrientation.find(faceToConnectedZone[faceI])() += n; } } Pstream::mapCombineGather(nPosOrientation, plusEqOp<label>()); @@ -3300,14 +4541,14 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify Info<< endl; - // Reapply with new counts (in nPosOrientation). This will cause + // Re-apply with new counts (in nPosOrientation). This will cause // zones with a negative count to be flipped. consistentOrientation ( isMasterFace, patch, nMasterFacesPerEdge, - faceToZone, + faceToConnectedZone, nPosOrientation, meshFlipMap @@ -3316,137 +4557,21 @@ Foam::autoPtr<Foam::mapPolyMesh> Foam::meshRefinement::zonify } - // Put the faces into the correct zone - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++) - { - label surfI = namedSurfaceIndex[faceI]; - - if (surfI != -1) - { - // Orient face zone to have slave cells in max cell zone. - label ownZone = cellToZone[faceOwner[faceI]]; - label neiZone = cellToZone[faceNeighbour[faceI]]; - - bool flip; - - label maxZone = max(ownZone, neiZone); - - if (maxZone == -1) - { - // free-standing face. Use geometrically derived orientation - flip = meshFlipMap[faceI]; - } - else if (ownZone == maxZone) - { - flip = false; - } - else - { - flip = true; - } - - meshMod.setAction - ( - polyModifyFace - ( - mesh_.faces()[faceI], // modified face - faceI, // label of face - faceOwner[faceI], // owner - faceNeighbour[faceI], // neighbour - false, // face flip - -1, // patch for face - false, // remove from zone - surfaceToFaceZone[surfI], // zone for face - flip // face flip in zone - ) - ); - } - } - - - // Set owner as no-flip - forAll(patches, patchI) - { - const polyPatch& pp = patches[patchI]; - - label faceI = pp.start(); - forAll(pp, i) - { - label surfI = namedSurfaceIndex[faceI]; - if (surfI != -1) - { - label ownZone = cellToZone[faceOwner[faceI]]; - label neiZone = neiCellZone[faceI-mesh_.nInternalFaces()]; - - bool flip; - - label maxZone = max(ownZone, neiZone); - - if (maxZone == -1) - { - // free-standing face. Use geometrically derived orientation - flip = meshFlipMap[faceI]; - } - else if (ownZone == neiZone) - { - // Free-standing zone face or coupled boundary. Keep master - // face unflipped. - flip = !isMasterFace[faceI]; - } - else if (neiZone == maxZone) - { - flip = true; - } - else - { - flip = false; - } - - meshMod.setAction - ( - polyModifyFace - ( - mesh_.faces()[faceI], // modified face - faceI, // label of face - faceOwner[faceI], // owner - -1, // neighbour - false, // face flip - patchI, // patch for face - false, // remove from zone - surfaceToFaceZone[surfI], // zone for face - flip // face flip in zone - ) - ); - } - faceI++; - } - } - - - // Put the cells into the correct zone - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - forAll(cellToZone, cellI) - { - label zoneI = cellToZone[cellI]; + // Topochange container + polyTopoChange meshMod(mesh_); - if (zoneI >= 0) - { - meshMod.setAction - ( - polyModifyCell - ( - cellI, - false, // removeFromZone - zoneI - ) - ); - } - } + // Insert changes to put cells and faces into zone + zonify + ( + isMasterFace, + cellToZone, + neiCellZone, + faceToZone, + meshFlipMap, + meshMod + ); // Change the mesh (no inflation, parallel sync) autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C index 4fc1b400771c030c59ed7b3a83dd230e2e601f94..5e4864d23dc3aea5f7dbbf451dd29a0e9b6c3311 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementMerge.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -34,110 +34,115 @@ License // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // -//// Merge faces that are in-line. -//Foam::label Foam::meshRefinement::mergePatchFaces -//( -// const scalar minCos, -// const scalar concaveCos, -// const labelList& patchIDs -//) -//{ -// // Patch face merging engine -// combineFaces faceCombiner(mesh_); -// -// const polyBoundaryMesh& patches = mesh_.boundaryMesh(); -// -// // Pick up all candidate cells on boundary -// labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces()); -// -// forAll(patchIDs, i) -// { -// label patchI = patchIDs[i]; -// -// const polyPatch& patch = patches[patchI]; -// -// if (!patch.coupled()) -// { -// forAll(patch, i) -// { -// boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]); -// } -// } -// } -// -// // Get all sets of faces that can be merged -// labelListList mergeSets -// ( -// faceCombiner.getMergeSets -// ( -// minCos, -// concaveCos, -// boundaryCells -// ) -// ); -// -// label nFaceSets = returnReduce(mergeSets.size(), sumOp<label>()); -// -// Info<< "mergePatchFaces : Merging " << nFaceSets -// << " sets of faces." << endl; -// -// if (nFaceSets > 0) -// { -// // Topology changes container -// polyTopoChange meshMod(mesh_); -// -// // Merge all faces of a set into the first face of the set. Remove -// // unused points. -// faceCombiner.setRefinement(mergeSets, meshMod); -// -// // Change the mesh (no inflation) -// autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); -// -// // Update fields -// mesh_.updateMesh(map); -// -// // Move mesh (since morphing does not do this) -// if (map().hasMotionPoints()) -// { -// mesh_.movePoints(map().preMotionPoints()); -// } -// else -// { -// // Delete mesh volumes. No other way to do this? -// mesh_.clearOut(); -// } -// -// -// // Reset the instance for if in overwrite mode -// mesh_.setInstance(timeName()); -// -// faceCombiner.updateMesh(map); -// -// // Get the kept faces that need to be recalculated. -// // Merging two boundary faces might shift the cell centre -// // (unless the faces are absolutely planar) -// labelHashSet retestFaces(6*mergeSets.size()); -// -// forAll(mergeSets, setI) -// { -// label oldMasterI = mergeSets[setI][0]; -// -// label faceI = map().reverseFaceMap()[oldMasterI]; -// -// // faceI is always uncoupled boundary face -// const cell& cFaces = mesh_.cells()[mesh_.faceOwner()[faceI]]; -// -// forAll(cFaces, i) -// { -// retestFaces.insert(cFaces[i]); -// } -// } -// updateMesh(map, retestFaces.toc()); -// } -// -// -// return nFaceSets; -//} +// Merge faces that are in-line. +Foam::label Foam::meshRefinement::mergePatchFaces +( + const scalar minCos, + const scalar concaveCos, + const label mergeSize, + const labelList& patchIDs +) +{ + // Patch face merging engine + combineFaces faceCombiner(mesh_, false); + + const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + + // Pick up all candidate cells on boundary + labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces()); + + forAll(patchIDs, i) + { + label patchI = patchIDs[i]; + + const polyPatch& patch = patches[patchI]; + + if (!patch.coupled()) + { + forAll(patch, i) + { + boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]); + } + } + } + + // Get all sets of faces that can be merged + labelListList mergeSets + ( + faceCombiner.getMergeSets + ( + minCos, + concaveCos, + boundaryCells + ) + ); + + if (mergeSize != -1) + { + // Keep only those that are mergeSize faces + label compactI = 0; + forAll(mergeSets, setI) + { + if (mergeSets[setI].size() == mergeSize) + { + mergeSets[compactI++] = mergeSets[setI]; + } + } + mergeSets.setSize(compactI); + } + + + label nFaceSets = returnReduce(mergeSets.size(), sumOp<label>()); + + Info<< "Merging " << nFaceSets << " sets of faces." << nl << endl; + + if (nFaceSets > 0) + { + // Topology changes container + polyTopoChange meshMod(mesh_); + + // Merge all faces of a set into the first face of the set. Remove + // unused points. + faceCombiner.setRefinement(mergeSets, meshMod); + + // Change the mesh (no inflation) + autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh_, false, true); + + // Update fields + mesh_.updateMesh(map); + + // Move mesh (since morphing does not do this) + if (map().hasMotionPoints()) + { + mesh_.movePoints(map().preMotionPoints()); + } + else + { + // Delete mesh volumes. No other way to do this? + mesh_.clearOut(); + } + + // Reset the instance for if in overwrite mode + mesh_.setInstance(timeName()); + + faceCombiner.updateMesh(map); + + // Get the kept faces that need to be recalculated. + // Merging two boundary faces might shift the cell centre + // (unless the faces are absolutely planar) + labelHashSet retestFaces(2*mergeSets.size()); + + forAll(mergeSets, setI) + { + label oldMasterI = mergeSets[setI][0]; + retestFaces.insert(map().reverseFaceMap()[oldMasterI]); + } + updateMesh(map, growFaceCellFace(retestFaces)); + } + + return nFaceSets; +} + // // //// Remove points not used by any face or points used by only two faces where diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementProblemCells.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementProblemCells.C index 871c7f8a6c6eaa4d0993f955d546c85d9ab72a60..a32ee117ce37834d376178f3d4246d83b25e498d 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementProblemCells.C +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementProblemCells.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -1085,7 +1085,9 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCells Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric ( const snapParameters& snapParams, - const dictionary& motionDict + const dictionary& motionDict, + const labelList& globalToMasterPatch, + const labelList& globalToSlavePatch ) const { pointField oldPoints(mesh_.points()); @@ -1162,7 +1164,10 @@ Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric ( autoSnapDriver::calcNearestSurface ( + snapParams.strictRegionSnap(), *this, + globalToMasterPatch, + globalToSlavePatch, snapDist, // attraction pp, nearestPoint, diff --git a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C index 3ccaa72835058e87bf1f48196b84e842f542d500..05f89fb5dd51396b8c56b822d1dffd87e83a3acc 100644 --- a/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C +++ b/src/mesh/autoMesh/autoHexMesh/meshRefinement/meshRefinementRefine.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -980,6 +980,7 @@ Foam::label Foam::meshRefinement::markSurfaceRefinement labelList surfaceMinLevel; surfaces_.findHigherIntersection ( + shells_, start, end, minLevel, diff --git a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C index ea43fb6f88824cb874ec63d227bcd6498392fbd3..7d47a612c9dc35a655594fdfe6a449b7fb490264 100644 --- a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C +++ b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -33,6 +33,91 @@ License #include "UPtrList.H" #include "volumeType.H" +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::labelList Foam::refinementSurfaces::findHigherLevel +( + const searchableSurface& geom, + const shellSurfaces& shells, + const List<pointIndexHit>& intersectionInfo, + const labelList& surfaceLevel // current level +) const +{ + // See if a cached level field available + labelList minLevelField; + geom.getField(intersectionInfo, minLevelField); + + + // Detect any uncached values and do proper search + labelList localLevel(surfaceLevel); + { + // Check hits: + // 1. cached value == -1 : store for re-testing + // 2. cached value != -1 : use + // 3. uncached : use region 0 value + + DynamicList<label> retestSet; + label nHits = 0; + + forAll(intersectionInfo, i) + { + if (intersectionInfo[i].hit()) + { + nHits++; + + // Check if minLevelField for this surface. + if (minLevelField.size()) + { + if (minLevelField[i] == -1) + { + retestSet.append(i); + } + else + { + localLevel[i] = max(localLevel[i], minLevelField[i]); + } + } + else + { + retestSet.append(i); + } + } + } + + label nRetest = returnReduce(retestSet.size(), sumOp<label>()); + if (nRetest > 0) + { + reduce(nHits, sumOp<label>()); + + //Info<< "Retesting " << nRetest + // << " out of " << nHits + // << " intersections on uncached elements on geometry " + // << geom.name() << endl; + + pointField samples(retestSet.size()); + forAll(retestSet, i) + { + samples[i] = intersectionInfo[retestSet[i]].hitPoint(); + } + labelList shellLevel; + shells.findHigherLevel + ( + samples, + UIndirectList<label>(surfaceLevel, retestSet)(), + shellLevel + ); + forAll(retestSet, i) + { + label sampleI = retestSet[i]; + localLevel[sampleI] = max(localLevel[sampleI], shellLevel[i]); + } + } + } + + return localLevel; +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::refinementSurfaces::refinementSurfaces @@ -408,19 +493,19 @@ void Foam::refinementSurfaces::setMinLevelFields { const searchableSurface& geom = allGeometry_[surfaces_[surfI]]; - // Precalculation only makes sense if there are different regions - // (so different refinement levels possible) and there are some + // Cache the refinement level (max of surface level and shell level) + // on a per-element basis. Only makes sense if there are lots of // elements. Possibly should have 'enough' elements to have fine // enough resolution but for now just make sure we don't catch e.g. // searchableBox (size=6) - if (geom.regions().size() > 1 && geom.globalSize() > 10) + if (geom.globalSize() > 10) { // Representative local coordinates and bounding sphere pointField ctrs; scalarField radiusSqr; geom.boundingSpheres(ctrs, radiusSqr); - labelList minLevelField(ctrs.size(), -1); + labelList minLevelField(ctrs.size(), 0); { // Get the element index in a roundabout way. Problem is e.g. // distributed surface where local indices differ from global @@ -447,9 +532,78 @@ void Foam::refinementSurfaces::setMinLevelFields labelList shellLevel; shells.findHigherLevel(ctrs, minLevelField, shellLevel); + + // In case of triangulated surfaces only cache value if triangle + // centre and vertices are in same shell + if (isA<triSurface>(geom)) + { + label nUncached = 0; + + // Check if points differing from ctr level + + const triSurface& ts = refCast<const triSurface>(geom); + const pointField& points = ts.points(); + + // Determine minimum expected level to avoid having to + // test lots of points + labelList minPointLevel(points.size(), labelMax); + forAll(shellLevel, triI) + { + const labelledTri& t = ts[triI]; + label level = shellLevel[triI]; + forAll(t, tI) + { + minPointLevel[t[tI]] = min(minPointLevel[t[tI]], level); + } + } + + + // See if inside any shells with higher refinement level + labelList pointLevel; + shells.findHigherLevel(points, minPointLevel, pointLevel); + + + // See if triangle centre values differ from triangle points + forAll(shellLevel, triI) + { + const labelledTri& t = ts[triI]; + label fLevel = shellLevel[triI]; + if + ( + (pointLevel[t[0]] != fLevel) + || (pointLevel[t[1]] != fLevel) + || (pointLevel[t[2]] != fLevel) + ) + { + //Pout<< "Detected triangle " << t.tri(ts.points()) + // << " partially inside/partially outside" << endl; + + // Mark as uncached + shellLevel[triI] = -1; + nUncached++; + } + } + + Info<< "For geometry " << geom.name() + << " detected " << returnReduce(nUncached, sumOp<label>()) + << " uncached triangles out of " << geom.globalSize() + << endl; + } + + + // Combine overall level field with current shell level. Make sure + // to preserve -1 (from triSurfaceMeshes with triangles partly + // inside/outside forAll(minLevelField, i) { - minLevelField[i] = max(minLevelField[i], shellLevel[i]); + if (min(minLevelField[i], shellLevel[i]) < 0) + { + minLevelField[i] = -1; + } + else + { + minLevelField[i] = max(minLevelField[i], shellLevel[i]); + } } // Store minLevelField on surface @@ -463,6 +617,8 @@ void Foam::refinementSurfaces::setMinLevelFields // number. void Foam::refinementSurfaces::findHigherIntersection ( + const shellSurfaces& shells, + const pointField& start, const pointField& end, const labelList& currentLevel, // current cell refinement level @@ -494,41 +650,48 @@ void Foam::refinementSurfaces::findHigherIntersection List<pointIndexHit> intersectionInfo(start.size()); geom.findLineAny(start, end, intersectionInfo); - // See if a cached level field available - labelList minLevelField; - geom.getField(intersectionInfo, minLevelField); - bool haveLevelField = - ( - returnReduce(minLevelField.size(), sumOp<label>()) - > 0 - ); - if (!haveLevelField && geom.regions().size() == 1) + // Surface-based refinement level + labelList surfaceOnlyLevel(start.size(), -1); { - minLevelField = labelList - ( - intersectionInfo.size(), - minLevel(surfI, 0) - ); - haveLevelField = true; - } + // Get per intersection the region + labelList region; + geom.getRegion(intersectionInfo, region); - if (haveLevelField) - { forAll(intersectionInfo, i) { - if - ( - intersectionInfo[i].hit() - && minLevelField[i] > currentLevel[i] - ) + if (intersectionInfo[i].hit()) { - surfaces[i] = surfI; // index of surface - surfaceLevel[i] = minLevelField[i]; + surfaceOnlyLevel[i] = minLevel(surfI, region[i]); } } - return; } + + + // Get shell refinement level if higher + const labelList localLevel + ( + findHigherLevel + ( + geom, + shells, + intersectionInfo, + surfaceOnlyLevel // starting level + ) + ); + + + // Combine localLevel with current level + forAll(localLevel, i) + { + if (localLevel[i] > currentLevel[i]) + { + surfaces[i] = surfI; // index of surface + surfaceLevel[i] = localLevel[i]; + } + } + + return; } @@ -546,40 +709,48 @@ void Foam::refinementSurfaces::findHigherIntersection // Do intersection test geom.findLineAny(p0, p1, intersectionInfo); - // See if a cached level field available - labelList minLevelField; - geom.getField(intersectionInfo, minLevelField); - // Copy all hits into arguments, In-place compact misses. - label missI = 0; - forAll(intersectionInfo, i) + // Surface-based refinement level + labelList surfaceOnlyLevel(intersectionInfo.size(), -1); { - // Get the minLevel for the point - label minLocalLevel = -1; + // Get per intersection the region + labelList region; + geom.getRegion(intersectionInfo, region); - if (intersectionInfo[i].hit()) + forAll(intersectionInfo, i) { - // Check if minLevelField for this surface. - if (minLevelField.size()) - { - minLocalLevel = minLevelField[i]; - } - else + if (intersectionInfo[i].hit()) { - // Use the min level for the surface instead. Assume - // single region 0. - minLocalLevel = minLevel(surfI, 0); + surfaceOnlyLevel[i] = minLevel(surfI, region[i]); } } + } + + + // Get shell refinement level if higher + const labelList localLevel + ( + findHigherLevel + ( + geom, + shells, + intersectionInfo, + surfaceOnlyLevel + ) + ); + // Combine localLevel with current level + label missI = 0; + forAll(localLevel, i) + { label pointI = intersectionToPoint[i]; - if (minLocalLevel > currentLevel[pointI]) + if (localLevel[i] > currentLevel[pointI]) { // Mark point for refinement surfaces[pointI] = surfI; - surfaceLevel[pointI] = minLocalLevel; + surfaceLevel[pointI] = localLevel[i]; } else { @@ -590,6 +761,7 @@ void Foam::refinementSurfaces::findHigherIntersection } } + // All done? Note that this decision should be synchronised if (returnReduce(missI, sumOp<label>()) == 0) { @@ -1061,7 +1233,7 @@ void Foam::refinementSurfaces::findNearest ( const labelList& surfacesToTest, const pointField& samples, - const scalarField& nearestDistSqr, + const scalarField& nearestDistSqr, labelList& hitSurface, List<pointIndexHit>& hitInfo ) const @@ -1337,4 +1509,121 @@ void Foam::refinementSurfaces::findInside } +void Foam::refinementSurfaces::findNearest +( + const labelList& surfacesToTest, + const labelListList& regions, + + const pointField& samples, + const scalarField& nearestDistSqr, + + labelList& hitSurface, + List<pointIndexHit>& hitInfo +) const +{ + labelList geometries(UIndirectList<label>(surfaces_, surfacesToTest)); + + // Do the tests. Note that findNearest returns index in geometries. + searchableSurfacesQueries::findNearest + ( + allGeometry_, + geometries, + regions, + samples, + nearestDistSqr, + hitSurface, + hitInfo + ); + + // Rework the hitSurface to be surface (i.e. index into surfaces_) + forAll(hitSurface, pointI) + { + if (hitSurface[pointI] != -1) + { + hitSurface[pointI] = surfacesToTest[hitSurface[pointI]]; + } + } +} + + +void Foam::refinementSurfaces::findNearestRegion +( + const labelList& surfacesToTest, + const labelListList& regions, + + const pointField& samples, + const scalarField& nearestDistSqr, + + labelList& hitSurface, + List<pointIndexHit>& hitInfo, + labelList& hitRegion, + vectorField& hitNormal +) const +{ + labelList geometries(UIndirectList<label>(surfaces_, surfacesToTest)); + + // Do the tests. Note that findNearest returns index in geometries. + searchableSurfacesQueries::findNearest + ( + allGeometry_, + geometries, + regions, + samples, + nearestDistSqr, + hitSurface, + hitInfo + ); + + // Rework the hitSurface to be surface (i.e. index into surfaces_) + forAll(hitSurface, pointI) + { + if (hitSurface[pointI] != -1) + { + hitSurface[pointI] = surfacesToTest[hitSurface[pointI]]; + } + } + + // Collect the region + hitRegion.setSize(hitSurface.size()); + hitRegion = -1; + hitNormal.setSize(hitSurface.size()); + hitNormal = vector::zero; + + forAll(surfacesToTest, i) + { + label surfI = surfacesToTest[i]; + + // Collect hits for surfI + const labelList localIndices(findIndices(hitSurface, surfI)); + + List<pointIndexHit> localHits + ( + UIndirectList<pointIndexHit> + ( + hitInfo, + localIndices + ) + ); + + // Region + labelList localRegion; + allGeometry_[surfaces_[surfI]].getRegion(localHits, localRegion); + + forAll(localIndices, i) + { + hitRegion[localIndices[i]] = localRegion[i]; + } + + // Normal + vectorField localNormal; + allGeometry_[surfaces_[surfI]].getNormal(localHits, localNormal); + + forAll(localIndices, i) + { + hitNormal[localIndices[i]] = localNormal[i]; + } + } +} + + // ************************************************************************* // diff --git a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H index 7f777daf334a51f4755e6cc8b94d4f8a38623b42..fadb9aaa2694e6b1d1b3dfa8c02e138a1c642307 100644 --- a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H +++ b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/refinementSurfaces.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -42,6 +42,7 @@ SourceFiles #include "vectorList.H" #include "pointIndexHit.H" #include "surfaceZonesInfo.H" +#include "pointList.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -52,8 +53,6 @@ class searchableSurfaces; class shellSurfaces; class triSurfaceMesh; -typedef List<point> pointList; - /*---------------------------------------------------------------------------*\ Class refinementSurfaces Declaration \*---------------------------------------------------------------------------*/ @@ -95,6 +94,16 @@ class refinementSurfaces // Private Member Functions + //- Given intersection results with geom detect local shell refinement + // level (possibly cached on triangles of geom) + labelList findHigherLevel + ( + const searchableSurface& geom, + const shellSurfaces& shells, + const List<pointIndexHit>& intersectionInfo, + const labelList& surfaceLevel + ) const; + //- Disallow default bitwise copy construct refinementSurfaces(const refinementSurfaces&); @@ -234,6 +243,8 @@ public: // Return surface number and level. void findHigherIntersection ( + const shellSurfaces& shells, + const pointField& start, const pointField& end, const labelList& currentLevel, // current cell refinement level @@ -354,6 +365,37 @@ public: const pointField& pt, labelList& insideSurfaces ) const; + + // Region wise searching + + //- Find nearest point on selected regions of surfaces. + void findNearest + ( + const labelList& surfacesToTest, + const labelListList& regions, + + const pointField& samples, + const scalarField& nearestDistSqr, + + labelList& hitSurface, + List<pointIndexHit>& hitInfo + ) const; + + //- Find nearest point on selected regions of surfaces. + void findNearestRegion + ( + const labelList& surfacesToTest, + const labelListList& regions, + + const pointField& samples, + const scalarField& nearestDistSqr, + + labelList& hitSurface, + List<pointIndexHit>& hitInfo, + labelList& hitRegion, + vectorField& hitNormal + ) const; + }; diff --git a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.C b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.C index 69c3f7777b95ea4b1a02a1d3d817673d55a7779b..771f826d8b5afeae064ccc25892b9eae8762c6b8 100644 --- a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.C +++ b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.C @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2014 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -341,6 +341,37 @@ Foam::labelList Foam::surfaceZonesInfo::getInsidePointNamedSurfaces } +Foam::label Foam::surfaceZonesInfo::addCellZone +( + const word& name, + const labelList& addressing, + polyMesh& mesh +) +{ + cellZoneMesh& cellZones = mesh.cellZones(); + + label zoneI = cellZones.findZoneID(name); + + if (zoneI == -1) + { + zoneI = cellZones.size(); + cellZones.setSize(zoneI+1); + cellZones.set + ( + zoneI, + new cellZone + ( + name, // name + addressing, // addressing + zoneI, // index + cellZones // cellZoneMesh + ) + ); + } + return zoneI; +} + + Foam::labelList Foam::surfaceZonesInfo::addCellZonesToMesh ( const PtrList<surfaceZonesInfo>& surfList, @@ -350,8 +381,6 @@ Foam::labelList Foam::surfaceZonesInfo::addCellZonesToMesh { labelList surfaceToCellZone(surfList.size(), -1); - cellZoneMesh& cellZones = mesh.cellZones(); - forAll(namedSurfaces, i) { label surfI = namedSurfaces[i]; @@ -360,24 +389,12 @@ Foam::labelList Foam::surfaceZonesInfo::addCellZonesToMesh if (cellZoneName != word::null) { - label zoneI = cellZones.findZoneID(cellZoneName); - - if (zoneI == -1) - { - zoneI = cellZones.size(); - cellZones.setSize(zoneI+1); - cellZones.set - ( - zoneI, - new cellZone - ( - cellZoneName, //name - labelList(0), //addressing - zoneI, //index - cellZones //cellZoneMesh - ) - ); - } + label zoneI = addCellZone + ( + cellZoneName, + labelList(0), // addressing + mesh + ); surfaceToCellZone[surfI] = zoneI; } @@ -385,7 +402,7 @@ Foam::labelList Foam::surfaceZonesInfo::addCellZonesToMesh // Check they are synced List<wordList> allCellZones(Pstream::nProcs()); - allCellZones[Pstream::myProcNo()] = cellZones.names(); + allCellZones[Pstream::myProcNo()] = mesh.cellZones().names(); Pstream::gatherList(allCellZones); Pstream::scatterList(allCellZones); @@ -409,6 +426,40 @@ Foam::labelList Foam::surfaceZonesInfo::addCellZonesToMesh } + +Foam::label Foam::surfaceZonesInfo::addFaceZone +( + const word& name, + const labelList& addressing, + const boolList& flipMap, + polyMesh& mesh +) +{ + faceZoneMesh& faceZones = mesh.faceZones(); + + label zoneI = faceZones.findZoneID(name); + + if (zoneI == -1) + { + zoneI = faceZones.size(); + faceZones.setSize(zoneI+1); + faceZones.set + ( + zoneI, + new faceZone + ( + name, // name + addressing, // addressing + flipMap, // flipMap + zoneI, // index + faceZones // faceZoneMesh + ) + ); + } + return zoneI; +} + + Foam::labelList Foam::surfaceZonesInfo::addFaceZonesToMesh ( const PtrList<surfaceZonesInfo>& surfList, @@ -426,25 +477,13 @@ Foam::labelList Foam::surfaceZonesInfo::addFaceZonesToMesh const word& faceZoneName = surfList[surfI].faceZoneName(); - label zoneI = faceZones.findZoneID(faceZoneName); - - if (zoneI == -1) - { - zoneI = faceZones.size(); - faceZones.setSize(zoneI+1); - faceZones.set - ( - zoneI, - new faceZone - ( - faceZoneName, //name - labelList(0), //addressing - boolList(0), //flipmap - zoneI, //index - faceZones //faceZoneMesh - ) - ); - } + label zoneI = addFaceZone + ( + faceZoneName, //name + labelList(0), //addressing + boolList(0), //flipmap + mesh + ); surfaceToFaceZone[surfI] = zoneI; } diff --git a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.H b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.H index eb59aad34fab72539af60b17a02d55a2c05051e2..3b5a73c3f5c3c5778b2fa17565eaeca5f34f2444 100644 --- a/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.H +++ b/src/mesh/autoMesh/autoHexMesh/refinementSurfaces/surfaceZonesInfo.H @@ -2,8 +2,8 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2013 OpenFOAM Foundation - \\/ M anipulation | + \\ / A nd | Copyright (C) 2014 OpenFOAM Foundation + \\/ M anipulation | Copyright (C) 2015 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -39,6 +39,7 @@ SourceFiles #include "word.H" #include "PtrList.H" #include "labelList.H" +#include "boolList.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -222,6 +223,13 @@ public: const PtrList<surfaceZonesInfo>& surfList ); + static label addCellZone + ( + const word& name, + const labelList& addressing, + polyMesh& mesh + ); + static labelList addCellZonesToMesh ( const PtrList<surfaceZonesInfo>& surfList, @@ -229,6 +237,14 @@ public: polyMesh& mesh ); + static label addFaceZone + ( + const word& name, + const labelList& addressing, + const boolList& flipMap, + polyMesh& mesh + ); + static labelList addFaceZonesToMesh ( const PtrList<surfaceZonesInfo>& surfList, diff --git a/tutorials/mesh/snappyHexMesh/Allrun b/tutorials/mesh/snappyHexMesh/Allrun index a035a98d0a073da131e6af6c40fa88bd41cbcc91..d37c93a33db0fd865f4840fc415a4cd87b45e334 100755 --- a/tutorials/mesh/snappyHexMesh/Allrun +++ b/tutorials/mesh/snappyHexMesh/Allrun @@ -7,6 +7,11 @@ cd ${0%/*} || exit 1 # Run from this directory ./Allrun ) +( + cd addLayersToFaceZone || exit + ./Allrun +) + exit 0 # These cases are links to solver test cases and are run when the Allrun