1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*----------------------------------------------------------------------------*/
27 #include "meshRefinement.H"
29 #include "syncTools.H"
31 #include "refinementSurfaces.H"
34 #include "indirectPrimitivePatch.H"
37 #include "searchableSurfaces.H"
38 #include "polyMeshGeometry.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 Pout<< "Writing " << fSet.size()
206 << " with problematic topology to faceSet "
207 << fSet.objectPath() << endl;
212 // 2. Find nearest surface on candidate faces
213 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
215 List<pointIndexHit> nearestInfo;
216 labelList nearestSurface;
217 labelList nearestRegion;
218 vectorField nearestNormal;
229 // 3. Test angle to surface
230 // ~~~~~~~~~~~~~~~~~~~~~~~~
232 Map<label> candidateCells(candidateFaces.size());
234 faceSet perpFaces(mesh_, "perpendicularFaces", pp.size()/100);
236 forAll(candidateFaces, i)
238 label faceI = candidateFaces[i];
240 vector n = mesh_.faceAreas()[faceI];
243 label region = surfaces_.globalRegion
250 perpendicularAngle[region]
252 * mathematicalConstant::pi;
256 if (mag(n & nearestNormal[i]) < Foam::sin(angle))
258 perpFaces.insert(faceI);
259 candidateCells.insert
261 mesh_.faceOwner()[faceI],
262 globalToPatch[region]
270 Pout<< "Writing " << perpFaces.size()
271 << " faces that are perpendicular to the surface to set "
272 << perpFaces.objectPath() << endl;
275 return candidateCells;
279 // Check if moving face to new points causes a 'collapsed' face.
280 // Uses new point position only for the face, not the neighbouring
282 bool Foam::meshRefinement::isCollapsedFace
284 const pointField& points,
285 const pointField& neiCc,
286 const scalar minFaceArea,
287 const scalar maxNonOrtho,
291 vector s = mesh_.faces()[faceI].normal(points);
292 scalar magS = mag(s);
295 if (magS < minFaceArea)
300 // Check orthogonality
301 const point& ownCc = mesh_.cellCentres()[mesh_.faceOwner()[faceI]];
303 if (mesh_.isInternalFace(faceI))
305 label nei = mesh_.faceNeighbour()[faceI];
306 vector d = ownCc - mesh_.cellCentres()[nei];
308 scalar dDotS = (d & s)/(mag(d)*magS + VSMALL);
310 if (dDotS < maxNonOrtho)
321 label patchI = mesh_.boundaryMesh().whichPatch(faceI);
323 if (mesh_.boundaryMesh()[patchI].coupled())
325 vector d = ownCc - neiCc[faceI-mesh_.nInternalFaces()];
327 scalar dDotS = (d & s)/(mag(d)*magS + VSMALL);
329 if (dDotS < maxNonOrtho)
340 // Collapsing normal boundary face does not cause problems with
348 // Check if moving cell to new points causes it to collapse.
349 bool Foam::meshRefinement::isCollapsedCell
351 const pointField& points,
352 const scalar volFraction,
356 scalar vol = mesh_.cells()[cellI].mag(points, mesh_.faces());
358 if (vol/mesh_.cellVolumes()[cellI] < volFraction)
369 // Returns list with for every internal face -1 or the patch they should
370 // be baffled into. Gets run after createBaffles so all the unzoned surface
371 // intersections have already been turned into baffles. (Note: zoned surfaces
372 // are not baffled at this stage)
373 // Used to remove cells by baffling all their faces and have the
374 // splitMeshRegions chuck away non used regions.
375 Foam::labelList Foam::meshRefinement::markFacesOnProblemCells
377 const dictionary& motionDict,
378 const bool removeEdgeConnectedCells,
379 const scalarField& perpendicularAngle,
380 const labelList& globalToPatch
383 const labelList& cellLevel = meshCutter_.cellLevel();
384 const labelList& pointLevel = meshCutter_.pointLevel();
385 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
387 // Per internal face (boundary faces not used) the patch that the
388 // baffle should get (or -1)
389 labelList facePatch(mesh_.nFaces(), -1);
391 // Mark all points and edges on baffle patches (so not on any inlets,
393 boolList isBoundaryPoint(mesh_.nPoints(), false);
394 boolList isBoundaryEdge(mesh_.nEdges(), false);
395 boolList isBoundaryFace(mesh_.nFaces(), false);
397 // Fill boundary data. All elements on meshed patches get marked.
398 // Get the labels of added patches.
399 labelList adaptPatchIDs(meshedPatches());
401 forAll(adaptPatchIDs, i)
403 label patchI = adaptPatchIDs[i];
405 const polyPatch& pp = patches[patchI];
407 label faceI = pp.start();
423 // Swap neighbouring cell centres and cell level
424 labelList neiLevel(mesh_.nFaces()-mesh_.nInternalFaces());
425 pointField neiCc(mesh_.nFaces()-mesh_.nInternalFaces());
426 calcNeighbourData(neiLevel, neiCc);
429 // Count of faces marked for baffling
430 label nBaffleFaces = 0;
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 Pout<< "Writing " << problemCellSet.size()
488 << " cells that are edge connected to coarser cell to set "
489 << problemCellSet.objectPath() << endl;
490 problemCellSet.write();
494 syncTools::syncPointList
500 false // no separation
503 syncTools::syncEdgeList
509 false // no separation
512 syncTools::syncFaceList
517 false // no separation
521 // See if checking for collapse
522 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
524 // Collapse checking parameters
525 scalar volFraction = -1;
526 if (motionDict.found("minVolCollapseRatio"))
528 volFraction = readScalar(motionDict.lookup("minVolCollapseRatio"));
530 const bool checkCollapse = (volFraction > 0);
532 scalar maxNonOrtho = -1;
535 // Find nearest (non-baffle) surface
536 pointField newPoints;
540 minArea = readScalar(motionDict.lookup("minArea"));
541 maxNonOrtho = readScalar(motionDict.lookup("maxNonOrtho"));
543 Info<< "markFacesOnProblemCells :"
544 << " Deleting all-anchor surface cells only if"
545 << "snapping them violates mesh quality constraints:" << nl
546 << " snapped/original cell volume < " << volFraction << nl
547 << " face area < " << minArea << nl
548 << " non-orthogonality > " << maxNonOrtho << nl
551 // Construct addressing engine.
552 autoPtr<indirectPrimitivePatch> ppPtr
554 meshRefinement::makePatch
560 const indirectPrimitivePatch& pp = ppPtr();
561 const pointField& localPoints = pp.localPoints();
562 const labelList& meshPoints = pp.meshPoints();
564 List<pointIndexHit> hitInfo;
565 labelList hitSurface;
566 surfaces_.findNearest
568 surfaces_.getUnnamedSurfaces(),
570 scalarField(localPoints.size(), sqr(GREAT)), // sqr of attraction
575 // Start of from current points
576 newPoints = mesh_.points();
580 if (hitInfo[i].hit())
582 newPoints[meshPoints[i]] = hitInfo[i].hitPoint();
589 // For each cell count the number of anchor points that are on
591 // 8 : check the number of (baffle) boundary faces. If 3 or more block
592 // off the cell since the cell would get squeezed down to a diamond
593 // (probably; if the 3 or more faces are unrefined (only use the
595 // 7 : store. Used to check later on whether there are points with
596 // 3 or more of these cells. (note that on a flat surface a boundary
597 // point will only have 4 cells connected to it)
600 // Does cell have exactly 7 of its 8 anchor points on the boundary?
601 PackedBoolList hasSevenBoundaryAnchorPoints(mesh_.nCells());
602 // If so what is the remaining non-boundary anchor point?
603 labelHashSet nonBoundaryAnchors(mesh_.nCells()/10000);
605 // On-the-fly addressing storage.
606 DynamicList<label> dynFEdges;
607 DynamicList<label> dynCPoints;
609 forAll(cellLevel, cellI)
611 const labelList& cPoints = mesh_.cellPoints(cellI, dynCPoints);
613 // Get number of anchor points (pointLevel <= cellLevel)
615 label nBoundaryAnchors = 0;
616 label nNonAnchorBoundary = 0;
617 label nonBoundaryAnchor = -1;
621 label pointI = cPoints[i];
623 if (pointLevel[pointI] <= cellLevel[cellI])
626 if (isBoundaryPoint[pointI])
632 // Anchor point which is not on the surface
633 nonBoundaryAnchor = pointI;
636 else if (isBoundaryPoint[pointI])
638 nNonAnchorBoundary++;
642 if (nBoundaryAnchors == 8)
644 const cell& cFaces = mesh_.cells()[cellI];
646 // Count boundary faces.
649 forAll(cFaces, cFaceI)
651 if (isBoundaryFace[cFaces[cFaceI]])
657 // If nBfaces > 1 make all non-boundary non-baffle faces baffles.
658 // We assume that this situation is where there is a single
659 // cell sticking out which would get flattened.
661 // Eugene: delete cell no matter what.
667 && !isCollapsedCell(newPoints, volFraction, cellI)
671 //Pout<< "Preventing collapse of 8 anchor point cell "
672 // << cellI << " at " << mesh_.cellCentres()[cellI]
673 // << " since new volume "
674 // << mesh_.cells()[cellI].mag(newPoints, mesh_.faces())
675 // << " old volume " << mesh_.cellVolumes()[cellI]
680 // Block all faces of cell
683 label faceI = cFaces[cf];
687 facePatch[faceI] == -1
688 && mesh_.isInternalFace(faceI)
691 facePatch[faceI] = getBafflePatch(facePatch, faceI);
694 // Mark face as a 'boundary'
707 else if (nBoundaryAnchors == 7)
709 // Mark the cell. Store the (single!) non-boundary anchor point.
710 hasSevenBoundaryAnchorPoints.set(cellI, 1u);
711 nonBoundaryAnchors.insert(nonBoundaryAnchor);
716 // Loop over all points. If a point is connected to 4 or more cells
717 // with 7 anchor points on the boundary set those cell's non-boundary faces
720 DynamicList<label> dynPCells;
722 forAllConstIter(labelHashSet, nonBoundaryAnchors, iter)
724 label pointI = iter.key();
726 const labelList& pCells = mesh_.pointCells(pointI, dynPCells);
728 // Count number of 'hasSevenBoundaryAnchorPoints' cells.
733 if (hasSevenBoundaryAnchorPoints.get(pCells[i]) == 1u)
741 // Point in danger of being what? Remove all 7-cells.
744 label cellI = pCells[i];
746 if (hasSevenBoundaryAnchorPoints.get(cellI) == 1u)
751 && !isCollapsedCell(newPoints, volFraction, cellI)
755 //Pout<< "Preventing collapse of 7 anchor cell "
757 // << " at " << mesh_.cellCentres()[cellI]
758 // << " since new volume "
759 // << mesh_.cells()[cellI].mag
760 // (newPoints, mesh_.faces())
761 // << " old volume " << mesh_.cellVolumes()[cellI]
766 const cell& cFaces = mesh_.cells()[cellI];
770 label faceI = cFaces[cf];
774 facePatch[faceI] == -1
775 && mesh_.isInternalFace(faceI)
778 facePatch[faceI] = getBafflePatch
785 // Mark face as a 'boundary'
802 // Sync all. (note that pointdata and facedata not used anymore but sync
805 syncTools::syncPointList
811 false // no separation
814 syncTools::syncEdgeList
820 false // no separation
823 syncTools::syncFaceList
828 false // no separation
832 // Find faces with all edges on the boundary and make them baffles
833 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
835 if (facePatch[faceI] == -1)
837 const labelList& fEdges = mesh_.faceEdges(faceI, dynFEdges);
838 label nFaceBoundaryEdges = 0;
842 if (isBoundaryEdge[fEdges[fe]])
844 nFaceBoundaryEdges++;
848 if (nFaceBoundaryEdges == fEdges.size())
864 //Pout<< "Preventing collapse of face " << faceI
865 // << " with all boundary edges "
866 // << " at " << mesh_.faceCentres()[faceI]
871 facePatch[faceI] = getBafflePatch(facePatch, faceI);
874 // Do NOT update boundary data since this would grow blocked
875 // faces across gaps.
881 forAll(patches, patchI)
883 const polyPatch& pp = patches[patchI];
887 label faceI = pp.start();
891 if (facePatch[faceI] == -1)
893 const labelList& fEdges = mesh_.faceEdges(faceI, dynFEdges);
894 label nFaceBoundaryEdges = 0;
898 if (isBoundaryEdge[fEdges[fe]])
900 nFaceBoundaryEdges++;
904 if (nFaceBoundaryEdges == fEdges.size())
920 //Pout<< "Preventing collapse of coupled face "
922 // << " with all boundary edges "
923 // << " at " << mesh_.faceCentres()[faceI]
928 facePatch[faceI] = getBafflePatch(facePatch, faceI);
931 // Do NOT update boundary data since this would grow
932 // blocked faces across gaps.
942 Info<< "markFacesOnProblemCells : marked "
943 << returnReduce(nBaffleFaces, sumOp<label>())
944 << " additional internal faces to be converted into baffles."
949 Info<< "markFacesOnProblemCells : prevented "
950 << returnReduce(nPrevented, sumOp<label>())
951 << " internal faces fom getting converted into baffles."
959 //// Mark faces to be baffled to prevent snapping problems. Does
960 //// test to find nearest surface and checks which faces would get squashed.
961 //Foam::labelList Foam::meshRefinement::markFacesOnProblemCellsGeometric
963 // const dictionary& motionDict
966 // // Construct addressing engine.
967 // autoPtr<indirectPrimitivePatch> ppPtr
969 // meshRefinement::makePatch
975 // const indirectPrimitivePatch& pp = ppPtr();
976 // const pointField& localPoints = pp.localPoints();
977 // const labelList& meshPoints = pp.meshPoints();
979 // // Find nearest (non-baffle) surface
980 // pointField newPoints(mesh_.points());
982 // List<pointIndexHit> hitInfo;
983 // labelList hitSurface;
984 // surfaces_.findNearest
986 // surfaces_.getUnnamedSurfaces(),
988 // scalarField(localPoints.size(), sqr(GREAT)),// sqr of attraction
993 // forAll(hitInfo, i)
995 // if (hitInfo[i].hit())
997 // //label pointI = meshPoints[i];
998 // //Pout<< " " << pointI << " moved from "
999 // // << mesh_.points()[pointI] << " by "
1000 // // << mag(hitInfo[i].hitPoint()-mesh_.points()[pointI])
1002 // newPoints[meshPoints[i]] = hitInfo[i].hitPoint();
1007 // // Per face (internal or coupled!) the patch that the
1008 // // baffle should get (or -1).
1009 // labelList facePatch(mesh_.nFaces(), -1);
1010 // // Count of baffled faces
1011 // label nBaffleFaces = 0;
1014 // pointField oldPoints(mesh_.points());
1015 // mesh_.movePoints(newPoints);
1016 // faceSet wrongFaces(mesh_, "wrongFaces", 100);
1018 // //motionSmoother::checkMesh(false, mesh_, motionDict, wrongFaces);
1020 // // Just check the errors from squashing
1021 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1023 // const labelList allFaces(identity(mesh_.nFaces()));
1024 // label nWrongFaces = 0;
1026 // scalar minArea(readScalar(motionDict.lookup("minArea")));
1027 // if (minArea > -SMALL)
1029 // polyMeshGeometry::checkFaceArea
1034 // mesh_.faceAreas(),
1039 // label nNewWrongFaces = returnReduce
1041 // wrongFaces.size(),
1045 // Info<< " faces with area < "
1046 // << setw(5) << minArea
1048 // << nNewWrongFaces-nWrongFaces << endl;
1050 // nWrongFaces = nNewWrongFaces;
1053 //// scalar minDet(readScalar(motionDict.lookup("minDeterminant")));
1054 // scalar minDet = 0.01;
1057 // polyMeshGeometry::checkCellDeterminant
1062 // mesh_.faceAreas(),
1064 // polyMeshGeometry::affectedCells(mesh_, allFaces),
1068 // label nNewWrongFaces = returnReduce
1070 // wrongFaces.size(),
1074 // Info<< " faces on cells with determinant < "
1075 // << setw(5) << minDet << " : "
1076 // << nNewWrongFaces-nWrongFaces << endl;
1078 // nWrongFaces = nNewWrongFaces;
1083 // forAllConstIter(faceSet, wrongFaces, iter)
1085 // label patchI = mesh_.boundaryMesh().whichPatch(iter.key());
1087 // if (patchI == -1 || mesh_.boundaryMesh()[patchI].coupled())
1089 // facePatch[iter.key()] = getBafflePatch(facePatch, iter.key());
1092 // //Pout<< " " << iter.key()
1093 // // //<< " on patch " << mesh_.boundaryMesh()[patchI].name()
1094 // // << " is destined for patch " << facePatch[iter.key()]
1098 // // Restore points.
1099 // mesh_.movePoints(oldPoints);
1103 // Info<< "markFacesOnProblemCellsGeometric : marked "
1104 // << returnReduce(nBaffleFaces, sumOp<label>())
1105 // << " additional internal and coupled faces"
1106 // << " to be converted into baffles." << endl;
1108 // syncTools::syncFaceList
1112 // maxEqOp<label>(),
1113 // false // no separation
1116 // return facePatch;
1120 // ************************************************************************* //