1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
24 \*----------------------------------------------------------------------------*/
26 #include "meshRefinement.H"
28 #include "syncTools.H"
30 #include "refinementSurfaces.H"
33 #include "indirectPrimitivePatch.H"
36 #include "searchableSurfaces.H"
37 #include "polyMeshGeometry.H"
39 #include "unitConversion.H"
41 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
43 void Foam::meshRefinement::markBoundaryFace
46 boolList& isBoundaryFace,
47 boolList& isBoundaryEdge,
48 boolList& isBoundaryPoint
51 isBoundaryFace[faceI] = true;
53 const labelList& fEdges = mesh_.faceEdges(faceI);
57 isBoundaryEdge[fEdges[fp]] = true;
60 const face& f = mesh_.faces()[faceI];
64 isBoundaryPoint[f[fp]] = true;
69 void Foam::meshRefinement::findNearest
71 const labelList& meshFaces,
72 List<pointIndexHit>& nearestInfo,
73 labelList& nearestSurface,
74 labelList& nearestRegion,
75 vectorField& nearestNormal
78 pointField fc(meshFaces.size());
81 fc[i] = mesh_.faceCentres()[meshFaces[i]];
84 const labelList allSurfaces(identity(surfaces_.surfaces().size()));
90 scalarField(fc.size(), sqr(GREAT)), // sqr of attraction
95 // Do normal testing per surface.
96 nearestNormal.setSize(nearestInfo.size());
97 nearestRegion.setSize(nearestInfo.size());
99 forAll(allSurfaces, surfI)
101 DynamicList<pointIndexHit> localHits;
103 forAll(nearestSurface, i)
105 if (nearestSurface[i] == surfI)
107 localHits.append(nearestInfo[i]);
111 label geomI = surfaces_.surfaces()[surfI];
113 pointField localNormals;
114 surfaces_.geometry()[geomI].getNormal(localHits, localNormals);
116 labelList localRegion;
117 surfaces_.geometry()[geomI].getRegion(localHits, localRegion);
120 forAll(nearestSurface, i)
122 if (nearestSurface[i] == surfI)
124 nearestNormal[i] = localNormals[localI];
125 nearestRegion[i] = localRegion[localI];
133 Foam::Map<Foam::label> Foam::meshRefinement::findEdgeConnectedProblemCells
135 const scalarField& perpendicularAngle,
136 const labelList& globalToPatch
139 // Construct addressing engine from all patches added for meshing.
140 autoPtr<indirectPrimitivePatch> ppPtr
142 meshRefinement::makePatch
148 const indirectPrimitivePatch& pp = ppPtr();
151 // 1. Collect faces to test
152 // ~~~~~~~~~~~~~~~~~~~~~~~~
154 DynamicList<label> candidateFaces(pp.size()/20);
156 const labelListList& edgeFaces = pp.edgeFaces();
158 const labelList& cellLevel = meshCutter_.cellLevel();
160 forAll(edgeFaces, edgeI)
162 const labelList& eFaces = edgeFaces[edgeI];
164 if (eFaces.size() == 2)
166 label face0 = pp.addressing()[eFaces[0]];
167 label face1 = pp.addressing()[eFaces[1]];
169 label cell0 = mesh_.faceOwner()[face0];
170 label cell1 = mesh_.faceOwner()[face1];
172 if (cellLevel[cell0] > cellLevel[cell1])
175 const vector& n0 = pp.faceNormals()[eFaces[0]];
176 const vector& n1 = pp.faceNormals()[eFaces[1]];
178 if (mag(n0 & n1) < 0.1)
180 candidateFaces.append(face0);
183 else if (cellLevel[cell1] > cellLevel[cell0])
186 const vector& n0 = pp.faceNormals()[eFaces[0]];
187 const vector& n1 = pp.faceNormals()[eFaces[1]];
189 if (mag(n0 & n1) < 0.1)
191 candidateFaces.append(face1);
196 candidateFaces.shrink();
198 Info<< "Testing " << returnReduce(candidateFaces.size(), sumOp<label>())
199 << " faces on edge-connected cells of differing level."
204 faceSet fSet(mesh_, "edgeConnectedFaces", candidateFaces);
205 fSet.instance() = timeName();
206 Pout<< "Writing " << fSet.size()
207 << " with problematic topology to faceSet "
208 << fSet.objectPath() << endl;
213 // 2. Find nearest surface on candidate faces
214 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216 List<pointIndexHit> nearestInfo;
217 labelList nearestSurface;
218 labelList nearestRegion;
219 vectorField nearestNormal;
230 // 3. Test angle to surface
231 // ~~~~~~~~~~~~~~~~~~~~~~~~
233 Map<label> candidateCells(candidateFaces.size());
235 faceSet perpFaces(mesh_, "perpendicularFaces", pp.size()/100);
237 forAll(candidateFaces, i)
239 label faceI = candidateFaces[i];
241 vector n = mesh_.faceAreas()[faceI];
244 label region = surfaces_.globalRegion
250 scalar angle = degToRad(perpendicularAngle[region]);
254 if (mag(n & nearestNormal[i]) < Foam::sin(angle))
256 perpFaces.insert(faceI);
257 candidateCells.insert
259 mesh_.faceOwner()[faceI],
260 globalToPatch[region]
268 perpFaces.instance() = timeName();
269 Pout<< "Writing " << perpFaces.size()
270 << " faces that are perpendicular to the surface to set "
271 << perpFaces.objectPath() << endl;
274 return candidateCells;
278 // Check if moving face to new points causes a 'collapsed' face.
279 // Uses new point position only for the face, not the neighbouring
281 bool Foam::meshRefinement::isCollapsedFace
283 const pointField& points,
284 const pointField& neiCc,
285 const scalar minFaceArea,
286 const scalar maxNonOrtho,
290 vector s = mesh_.faces()[faceI].normal(points);
291 scalar magS = mag(s);
294 if (magS < minFaceArea)
299 // Check orthogonality
300 const point& ownCc = mesh_.cellCentres()[mesh_.faceOwner()[faceI]];
302 if (mesh_.isInternalFace(faceI))
304 label nei = mesh_.faceNeighbour()[faceI];
305 vector d = ownCc - mesh_.cellCentres()[nei];
307 scalar dDotS = (d & s)/(mag(d)*magS + VSMALL);
309 if (dDotS < maxNonOrtho)
320 label patchI = mesh_.boundaryMesh().whichPatch(faceI);
322 if (mesh_.boundaryMesh()[patchI].coupled())
324 vector d = ownCc - neiCc[faceI-mesh_.nInternalFaces()];
326 scalar dDotS = (d & s)/(mag(d)*magS + VSMALL);
328 if (dDotS < maxNonOrtho)
339 // Collapsing normal boundary face does not cause problems with
347 // Check if moving cell to new points causes it to collapse.
348 bool Foam::meshRefinement::isCollapsedCell
350 const pointField& points,
351 const scalar volFraction,
355 scalar vol = mesh_.cells()[cellI].mag(points, mesh_.faces());
357 if (vol/mesh_.cellVolumes()[cellI] < volFraction)
368 // Returns list with for every internal face -1 or the patch they should
369 // be baffled into. Gets run after createBaffles so all the unzoned surface
370 // intersections have already been turned into baffles. (Note: zoned surfaces
371 // are not baffled at this stage)
372 // Used to remove cells by baffling all their faces and have the
373 // splitMeshRegions chuck away non used regions.
374 Foam::labelList Foam::meshRefinement::markFacesOnProblemCells
376 const dictionary& motionDict,
377 const bool removeEdgeConnectedCells,
378 const scalarField& perpendicularAngle,
379 const labelList& globalToPatch
382 const labelList& cellLevel = meshCutter_.cellLevel();
383 const labelList& pointLevel = meshCutter_.pointLevel();
384 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
386 // Per internal face (boundary faces not used) the patch that the
387 // baffle should get (or -1)
388 labelList facePatch(mesh_.nFaces(), -1);
390 // Mark all points and edges on baffle patches (so not on any inlets,
392 boolList isBoundaryPoint(mesh_.nPoints(), false);
393 boolList isBoundaryEdge(mesh_.nEdges(), false);
394 boolList isBoundaryFace(mesh_.nFaces(), false);
396 // Fill boundary data. All elements on meshed patches get marked.
397 // Get the labels of added patches.
398 labelList adaptPatchIDs(meshedPatches());
400 forAll(adaptPatchIDs, i)
402 label patchI = adaptPatchIDs[i];
404 const polyPatch& pp = patches[patchI];
406 label faceI = pp.start();
422 // Swap neighbouring cell centres and cell level
423 labelList neiLevel(mesh_.nFaces()-mesh_.nInternalFaces());
424 pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
425 calcNeighbourData(neiLevel, neiCc);
428 // Count of faces marked for baffling
429 label nBaffleFaces = 0;
430 PackedBoolList isMasterFace(syncTools::getMasterFaces(mesh_));
432 // Count of faces not baffled since would not cause a collapse
433 label nPrevented = 0;
435 if (removeEdgeConnectedCells && max(perpendicularAngle) >= 0)
437 Info<< "markFacesOnProblemCells :"
438 << " Checking for edge-connected cells of highly differing sizes."
441 // Pick up the cells that need to be removed and (a guess for)
442 // the patch they should be patched with.
443 Map<label> problemCells
445 findEdgeConnectedProblemCells
452 // Baffle all faces of cells that need to be removed
453 forAllConstIter(Map<label>, problemCells, iter)
455 const cell& cFaces = mesh_.cells()[iter.key()];
459 label faceI = cFaces[i];
461 if (facePatch[faceI] == -1 && mesh_.isInternalFace(faceI))
463 facePatch[faceI] = getBafflePatch(facePatch, faceI);
466 // Mark face as a 'boundary'
477 Info<< "markFacesOnProblemCells : Marked "
478 << returnReduce(nBaffleFaces, sumOp<label>())
479 << " additional internal faces to be converted into baffles"
481 << returnReduce(problemCells.size(), sumOp<label>())
482 << " cells edge-connected to lower level cells." << endl;
486 cellSet problemCellSet(mesh_, "problemCells", problemCells.toc());
487 problemCellSet.instance() = timeName();
488 Pout<< "Writing " << problemCellSet.size()
489 << " cells that are edge connected to coarser cell to set "
490 << problemCellSet.objectPath() << endl;
491 problemCellSet.write();
495 syncTools::syncPointList
503 syncTools::syncEdgeList
511 syncTools::syncFaceList
519 // See if checking for collapse
520 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
522 // Collapse checking parameters
523 const scalar volFraction =
524 motionDict.lookupOrDefault<scalar>("minVolCollapseRatio", -1);
526 const bool checkCollapse = (volFraction > 0);
528 scalar maxNonOrtho = -1;
531 // Find nearest (non-baffle) surface
532 pointField newPoints;
536 minArea = readScalar(motionDict.lookup("minArea"));
537 maxNonOrtho = readScalar(motionDict.lookup("maxNonOrtho"));
539 Info<< "markFacesOnProblemCells :"
540 << " Deleting all-anchor surface cells only if"
541 << "snapping them violates mesh quality constraints:" << nl
542 << " snapped/original cell volume < " << volFraction << nl
543 << " face area < " << minArea << nl
544 << " non-orthogonality > " << maxNonOrtho << nl
547 // Construct addressing engine.
548 autoPtr<indirectPrimitivePatch> ppPtr
550 meshRefinement::makePatch
556 const indirectPrimitivePatch& pp = ppPtr();
557 const pointField& localPoints = pp.localPoints();
558 const labelList& meshPoints = pp.meshPoints();
560 List<pointIndexHit> hitInfo;
561 labelList hitSurface;
562 surfaces_.findNearest
564 surfaces_.getUnnamedSurfaces(),
566 scalarField(localPoints.size(), sqr(GREAT)), // sqr of attraction
571 // Start of from current points
572 newPoints = mesh_.points();
576 if (hitInfo[i].hit())
578 newPoints[meshPoints[i]] = hitInfo[i].hitPoint();
585 // For each cell count the number of anchor points that are on
587 // 8 : check the number of (baffle) boundary faces. If 3 or more block
588 // off the cell since the cell would get squeezed down to a diamond
589 // (probably; if the 3 or more faces are unrefined (only use the
591 // 7 : store. Used to check later on whether there are points with
592 // 3 or more of these cells. (note that on a flat surface a boundary
593 // point will only have 4 cells connected to it)
596 // Does cell have exactly 7 of its 8 anchor points on the boundary?
597 PackedBoolList hasSevenBoundaryAnchorPoints(mesh_.nCells());
598 // If so what is the remaining non-boundary anchor point?
599 labelHashSet nonBoundaryAnchors(mesh_.nCells()/10000);
601 // On-the-fly addressing storage.
602 DynamicList<label> dynFEdges;
603 DynamicList<label> dynCPoints;
605 forAll(cellLevel, cellI)
607 const labelList& cPoints = mesh_.cellPoints(cellI, dynCPoints);
609 // Get number of anchor points (pointLevel <= cellLevel)
611 label nBoundaryAnchors = 0;
612 label nNonAnchorBoundary = 0;
613 label nonBoundaryAnchor = -1;
617 label pointI = cPoints[i];
619 if (pointLevel[pointI] <= cellLevel[cellI])
622 if (isBoundaryPoint[pointI])
628 // Anchor point which is not on the surface
629 nonBoundaryAnchor = pointI;
632 else if (isBoundaryPoint[pointI])
634 nNonAnchorBoundary++;
638 if (nBoundaryAnchors == 8)
640 const cell& cFaces = mesh_.cells()[cellI];
642 // Count boundary faces.
645 forAll(cFaces, cFaceI)
647 if (isBoundaryFace[cFaces[cFaceI]])
653 // If nBfaces > 1 make all non-boundary non-baffle faces baffles.
654 // We assume that this situation is where there is a single
655 // cell sticking out which would get flattened.
657 // Eugene: delete cell no matter what.
663 && !isCollapsedCell(newPoints, volFraction, cellI)
667 //Pout<< "Preventing collapse of 8 anchor point cell "
668 // << cellI << " at " << mesh_.cellCentres()[cellI]
669 // << " since new volume "
670 // << mesh_.cells()[cellI].mag(newPoints, mesh_.faces())
671 // << " old volume " << mesh_.cellVolumes()[cellI]
676 // Block all faces of cell
679 label faceI = cFaces[cf];
683 facePatch[faceI] == -1
684 && mesh_.isInternalFace(faceI)
687 facePatch[faceI] = getBafflePatch(facePatch, faceI);
690 // Mark face as a 'boundary'
703 else if (nBoundaryAnchors == 7)
705 // Mark the cell. Store the (single!) non-boundary anchor point.
706 hasSevenBoundaryAnchorPoints.set(cellI, 1u);
707 nonBoundaryAnchors.insert(nonBoundaryAnchor);
712 // Loop over all points. If a point is connected to 4 or more cells
713 // with 7 anchor points on the boundary set those cell's non-boundary faces
716 DynamicList<label> dynPCells;
718 forAllConstIter(labelHashSet, nonBoundaryAnchors, iter)
720 label pointI = iter.key();
722 const labelList& pCells = mesh_.pointCells(pointI, dynPCells);
724 // Count number of 'hasSevenBoundaryAnchorPoints' cells.
729 if (hasSevenBoundaryAnchorPoints.get(pCells[i]) == 1u)
737 // Point in danger of being what? Remove all 7-cells.
740 label cellI = pCells[i];
742 if (hasSevenBoundaryAnchorPoints.get(cellI) == 1u)
747 && !isCollapsedCell(newPoints, volFraction, cellI)
751 //Pout<< "Preventing collapse of 7 anchor cell "
753 // << " at " << mesh_.cellCentres()[cellI]
754 // << " since new volume "
755 // << mesh_.cells()[cellI].mag
756 // (newPoints, mesh_.faces())
757 // << " old volume " << mesh_.cellVolumes()[cellI]
762 const cell& cFaces = mesh_.cells()[cellI];
766 label faceI = cFaces[cf];
770 facePatch[faceI] == -1
771 && mesh_.isInternalFace(faceI)
774 facePatch[faceI] = getBafflePatch
781 // Mark face as a 'boundary'
798 // Sync all. (note that pointdata and facedata not used anymore but sync
801 syncTools::syncPointList
809 syncTools::syncEdgeList
817 syncTools::syncFaceList
825 // Find faces with all edges on the boundary and make them baffles
826 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
828 if (facePatch[faceI] == -1)
830 const labelList& fEdges = mesh_.faceEdges(faceI, dynFEdges);
831 label nFaceBoundaryEdges = 0;
835 if (isBoundaryEdge[fEdges[fe]])
837 nFaceBoundaryEdges++;
841 if (nFaceBoundaryEdges == fEdges.size())
857 //Pout<< "Preventing collapse of face " << faceI
858 // << " with all boundary edges "
859 // << " at " << mesh_.faceCentres()[faceI]
864 facePatch[faceI] = getBafflePatch(facePatch, faceI);
867 // Do NOT update boundary data since this would grow blocked
868 // faces across gaps.
874 forAll(patches, patchI)
876 const polyPatch& pp = patches[patchI];
880 label faceI = pp.start();
884 if (facePatch[faceI] == -1)
886 const labelList& fEdges = mesh_.faceEdges(faceI, dynFEdges);
887 label nFaceBoundaryEdges = 0;
891 if (isBoundaryEdge[fEdges[fe]])
893 nFaceBoundaryEdges++;
897 if (nFaceBoundaryEdges == fEdges.size())
913 //Pout<< "Preventing collapse of coupled face "
915 // << " with all boundary edges "
916 // << " at " << mesh_.faceCentres()[faceI]
921 facePatch[faceI] = getBafflePatch(facePatch, faceI);
922 if (isMasterFace[faceI])
927 // Do NOT update boundary data since this would grow
928 // blocked faces across gaps.
938 Info<< "markFacesOnProblemCells : marked "
939 << returnReduce(nBaffleFaces, sumOp<label>())
940 << " additional internal faces to be converted into baffles."
945 Info<< "markFacesOnProblemCells : prevented "
946 << returnReduce(nPrevented, sumOp<label>())
947 << " internal faces fom getting converted into baffles."
955 //// Mark faces to be baffled to prevent snapping problems. Does
956 //// test to find nearest surface and checks which faces would get squashed.
957 //Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric
959 // const dictionary& motionDict
962 // // Construct addressing engine.
963 // autoPtr<indirectPrimitivePatch> ppPtr
965 // meshRefinement::makePatch
971 // const indirectPrimitivePatch& pp = ppPtr();
972 // const pointField& localPoints = pp.localPoints();
973 // const labelList& meshPoints = pp.meshPoints();
975 // // Find nearest (non-baffle) surface
976 // pointField newPoints(mesh_.points());
978 // List<pointIndexHit> hitInfo;
979 // labelList hitSurface;
980 // surfaces_.findNearest
982 // surfaces_.getUnnamedSurfaces(),
984 // scalarField(localPoints.size(), sqr(GREAT)),// sqr of attraction
989 // forAll(hitInfo, i)
991 // if (hitInfo[i].hit())
993 // //label pointI = meshPoints[i];
994 // //Pout<< " " << pointI << " moved from "
995 // // << mesh_.points()[pointI] << " by "
996 // // << mag(hitInfo[i].hitPoint()-mesh_.points()[pointI])
998 // newPoints[meshPoints[i]] = hitInfo[i].hitPoint();
1003 // // Per face (internal or coupled!) the patch that the
1004 // // baffle should get (or -1).
1005 // labelList facePatch(mesh_.nFaces(), -1);
1006 // // Count of baffled faces
1007 // label nBaffleFaces = 0;
1010 // pointField oldPoints(mesh_.points());
1011 // mesh_.movePoints(newPoints);
1012 // faceSet wrongFaces(mesh_, "wrongFaces", 100);
1014 // //motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
1016 // // Just check the errors from squashing
1017 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1019 // const labelList allFaces(identity(mesh_.nFaces()));
1020 // label nWrongFaces = 0;
1022 // scalar minArea(readScalar(motionDict.lookup("minArea")));
1023 // if (minArea > -SMALL)
1025 // polyMeshGeometry::checkFaceArea
1030 // mesh_.faceAreas(),
1035 // label nNewWrongFaces = returnReduce
1037 // wrongFaces.size(),
1041 // Info<< " faces with area < "
1042 // << setw(5) << minArea
1044 // << nNewWrongFaces-nWrongFaces << endl;
1046 // nWrongFaces = nNewWrongFaces;
1049 //// scalar minDet(readScalar(motionDict.lookup("minDeterminant")));
1050 // scalar minDet = 0.01;
1053 // polyMeshGeometry::checkCellDeterminant
1058 // mesh_.faceAreas(),
1060 // polyMeshGeometry::affectedCells(mesh_, allFaces),
1064 // label nNewWrongFaces = returnReduce
1066 // wrongFaces.size(),
1070 // Info<< " faces on cells with determinant < "
1071 // << setw(5) << minDet << " : "
1072 // << nNewWrongFaces-nWrongFaces << endl;
1074 // nWrongFaces = nNewWrongFaces;
1079 // forAllConstIter(faceSet, wrongFaces, iter)
1081 // label patchI = mesh_.boundaryMesh().whichPatch(iter.key());
1083 // if (patchI == -1 || mesh_.boundaryMesh()[patchI].coupled())
1085 // facePatch[iter.key()] = getBafflePatch(facePatch, iter.key());
1088 // //Pout<< " " << iter.key()
1089 // // //<< " on patch " << mesh_.boundaryMesh()[patchI].name()
1090 // // << " is destined for patch " << facePatch[iter.key()]
1094 // // Restore points.
1095 // mesh_.movePoints(oldPoints);
1099 // Info<< "markFacesOnProblemCellsGeometric : marked "
1100 // << returnReduce(nBaffleFaces, sumOp<label>())
1101 // << " additional internal and coupled faces"
1102 // << " to be converted into baffles." << endl;
1104 // syncTools::syncFaceList
1111 // return facePatch;
1115 // ************************************************************************* //