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/>.
25 Utility to refine cells near to a surface.
27 \*---------------------------------------------------------------------------*/
32 #include "twoDPointCorrector.H"
34 #include "multiDirRefinement.H"
36 #include "triSurface.H"
37 #include "triSurfaceSearch.H"
41 #include "surfaceToCell.H"
42 #include "surfaceToPoint.H"
43 #include "cellToPoint.H"
44 #include "pointToCell.H"
45 #include "cellToCell.H"
46 #include "surfaceSets.H"
47 #include "polyTopoChange.H"
48 #include "polyTopoChanger.H"
49 #include "mapPolyMesh.H"
50 #include "labelIOList.H"
51 #include "emptyPolyPatch.H"
52 #include "removeCells.H"
53 #include "meshSearch.H"
57 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
60 // Max cos angle for edges to be considered aligned with axis.
61 static const scalar edgeTol = 1E-3;
64 void writeSet(const cellSet& cells, const string& msg)
66 Info<< "Writing " << msg << " (" << cells.size() << ") to cellSet "
67 << cells.instance()/cells.local()/cells.name()
73 direction getNormalDir(const twoDPointCorrector* correct2DPtr)
79 const vector& normal = correct2DPtr->planeNormal();
81 if (mag(normal & vector(1, 0, 0)) > 1-edgeTol)
85 else if (mag(normal & vector(0, 1, 0)) > 1-edgeTol)
89 else if (mag(normal & vector(0, 0, 1)) > 1-edgeTol)
99 // Calculate some edge statistics on mesh. Return min. edge length over all
100 // directions but exclude component (0=x, 1=y, 2=z, other=none)
101 scalar getEdgeStats(const primitiveMesh& mesh, const direction excludeCmpt)
108 scalar maxX = -GREAT;
112 scalar maxY = -GREAT;
116 scalar maxZ = -GREAT;
119 scalar minOther = GREAT;
120 scalar maxOther = -GREAT;
122 const edgeList& edges = mesh.edges();
126 const edge& e = edges[edgeI];
128 vector eVec(e.vec(mesh.points()));
130 scalar eMag = mag(eVec);
134 if (mag(eVec & x) > 1-edgeTol)
136 minX = min(minX, eMag);
137 maxX = max(maxX, eMag);
140 else if (mag(eVec & y) > 1-edgeTol)
142 minY = min(minY, eMag);
143 maxY = max(maxY, eMag);
146 else if (mag(eVec & z) > 1-edgeTol)
148 minZ = min(minZ, eMag);
149 maxZ = max(maxZ, eMag);
154 minOther = min(minOther, eMag);
155 maxOther = max(maxOther, eMag);
159 Info<< "Mesh bounding box:" << boundBox(mesh.points()) << nl << nl
160 << "Mesh edge statistics:" << nl
161 << " x aligned : number:" << nX << "\tminLen:" << minX
162 << "\tmaxLen:" << maxX << nl
163 << " y aligned : number:" << nY << "\tminLen:" << minY
164 << "\tmaxLen:" << maxY << nl
165 << " z aligned : number:" << nZ << "\tminLen:" << minZ
166 << "\tmaxLen:" << maxZ << nl
167 << " other : number:" << mesh.nEdges() - nX - nY - nZ
168 << "\tminLen:" << minOther
169 << "\tmaxLen:" << maxOther << nl << endl;
171 if (excludeCmpt == 0)
173 return min(minY, min(minZ, minOther));
175 else if (excludeCmpt == 1)
177 return min(minX, min(minZ, minOther));
179 else if (excludeCmpt == 2)
181 return min(minX, min(minY, minOther));
185 return min(minX, min(minY, min(minZ, minOther)));
190 // Adds empty patch if not yet there. Returns patchID.
191 label addPatch(polyMesh& mesh, const word& patchName)
193 label patchI = mesh.boundaryMesh().findPatchID(patchName);
197 const polyBoundaryMesh& patches = mesh.boundaryMesh();
199 List<polyPatch*> newPatches(patches.size() + 1);
201 // Add empty patch as 0th entry (Note: only since subsetMesh wants this)
207 Foam::word(patchName),
209 mesh.nInternalFaces(),
216 const polyPatch& pp = patches[i];
228 mesh.removeBoundary();
229 mesh.addPatches(newPatches);
231 Info<< "Created patch oldInternalFaces at " << patchI << endl;
235 Info<< "Reusing patch oldInternalFaces at " << patchI << endl;
243 // Take surface and select cells based on surface curvature.
244 void selectCurvatureCells
246 const polyMesh& mesh,
247 const fileName& surfName,
248 const triSurfaceSearch& querySurf,
249 const scalar nearDist,
250 const scalar curvature,
254 // Use surfaceToCell to do actual calculation.
256 // Since we're adding make sure set is on disk.
259 // Take centre of cell 0 as outside point since info not used.
261 surfaceToCell cutSource
267 pointField(1, mesh.cellCentres()[0]),
275 cutSource.applyToSet(topoSetSource::ADD, cells);
279 // cutCells contains currently selected cells to be refined. Add neighbours
280 // on the inside or outside to them.
281 void addCutNeighbours
283 const polyMesh& mesh,
284 const bool selectInside,
285 const bool selectOutside,
286 const labelHashSet& inside,
287 const labelHashSet& outside,
288 labelHashSet& cutCells
291 // Pick up face neighbours of cutCells
293 labelHashSet addCutFaces(cutCells.size());
295 forAllConstIter(labelHashSet, cutCells, iter)
297 const label cellI = iter.key();
298 const labelList& cFaces = mesh.cells()[cellI];
302 const label faceI = cFaces[i];
304 if (mesh.isInternalFace(faceI))
306 label nbr = mesh.faceOwner()[faceI];
310 nbr = mesh.faceNeighbour()[faceI];
313 if (selectInside && inside.found(nbr))
315 addCutFaces.insert(nbr);
317 else if (selectOutside && outside.found(nbr))
319 addCutFaces.insert(nbr);
325 Info<< " Selected an additional " << addCutFaces.size()
326 << " neighbours of cutCells to refine" << endl;
328 forAllConstIter(labelHashSet, addCutFaces, iter)
330 cutCells.insert(iter.key());
335 // Return true if any cells had to be split to keep a difference between
336 // neighbouring refinement levels < limitDiff.
337 // Gets cells which will be refined (so increase the refinement level) and
339 bool limitRefinementLevel
341 const primitiveMesh& mesh,
342 const label limitDiff,
343 const labelHashSet& excludeCells,
344 const labelList& refLevel,
345 labelHashSet& cutCells
348 // Do simple check on validity of refinement level.
349 forAll(refLevel, cellI)
351 if (!excludeCells.found(cellI))
353 const labelList& cCells = mesh.cellCells()[cellI];
357 label nbr = cCells[i];
359 if (!excludeCells.found(nbr))
361 if (refLevel[cellI] - refLevel[nbr] >= limitDiff)
363 FatalErrorIn("limitRefinementLevel")
364 << "Level difference between neighbouring cells "
365 << cellI << " and " << nbr
366 << " greater than or equal to " << limitDiff << endl
367 << "refLevels:" << refLevel[cellI] << ' '
368 << refLevel[nbr] << abort(FatalError);
376 labelHashSet addCutCells(cutCells.size());
378 forAllConstIter(labelHashSet, cutCells, iter)
380 // cellI will be refined.
381 const label cellI = iter.key();
382 const labelList& cCells = mesh.cellCells()[cellI];
386 const label nbr = cCells[i];
388 if (!excludeCells.found(nbr) && !cutCells.found(nbr))
390 if (refLevel[cellI] + 1 - refLevel[nbr] >= limitDiff)
392 addCutCells.insert(nbr);
398 if (addCutCells.size())
400 // Add cells to cutCells.
402 Info<< "Added an additional " << addCutCells.size() << " cells"
403 << " to satisfy 1:" << limitDiff << " refinement level"
406 forAllConstIter(labelHashSet, addCutCells, iter)
408 cutCells.insert(iter.key());
414 Info<< "Added no additional cells"
415 << " to satisfy 1:" << limitDiff << " refinement level"
423 // Do all refinement (i.e. refCells) according to refineDict and update
424 // refLevel afterwards for added cells
428 const dictionary& refineDict,
429 const labelHashSet& refCells,
433 label oldCells = mesh.nCells();
435 // Multi-iteration, multi-direction topology modifier.
436 multiDirRefinement multiRef
444 // Update refLevel for split cells
447 refLevel.setSize(mesh.nCells());
449 for (label cellI = oldCells; cellI < mesh.nCells(); cellI++)
454 const labelListList& addedCells = multiRef.addedCells();
456 forAll(addedCells, oldCellI)
458 const labelList& added = addedCells[oldCellI];
462 // Give all cells resulting from split the refinement level
464 label masterLevel = ++refLevel[oldCellI];
468 refLevel[added[i]] = masterLevel;
475 // Subset mesh and update refLevel and cutCells
479 const label writeMesh,
480 const label patchI, // patchID for exposed faces
481 const labelHashSet& cellsToRemove,
483 labelIOList& refLevel
486 removeCells cellRemover(mesh);
488 labelList cellLabels(cellsToRemove.toc());
490 Info<< "Mesh has:" << mesh.nCells() << " cells."
491 << " Removing:" << cellLabels.size() << " cells" << endl;
494 labelList exposedFaces(cellRemover.getExposedFaces(cellLabels));
496 polyTopoChange meshMod(mesh);
497 cellRemover.setRefinement
501 labelList(exposedFaces.size(), patchI),
506 Info<< "Morphing ..." << endl;
508 const Time& runTime = mesh.time();
510 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
512 if (morphMap().hasMotionPoints())
514 mesh.movePoints(morphMap().preMotionPoints());
517 // Update topology on cellRemover
518 cellRemover.updateMesh(morphMap());
520 // Update refLevel for removed cells.
521 const labelList& cellMap = morphMap().cellMap();
523 labelList newRefLevel(cellMap.size());
527 newRefLevel[i] = refLevel[cellMap[i]];
530 // Transfer back to refLevel
531 refLevel.transfer(newRefLevel);
535 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
538 IOstream::defaultPrecision(10);
543 // Update cutCells for removed cells.
544 cutCells.updateMesh(morphMap());
548 // Divide the cells according to status compared to surface. Constructs sets:
549 // - cutCells : all cells cut by surface
550 // - inside : all cells inside surface
551 // - outside : ,, outside ,,
552 // and a combined set:
553 // - selected : sum of above according to selectCut/Inside/Outside flags.
556 const polyMesh& mesh,
557 const fileName& surfName,
558 const triSurfaceSearch& querySurf,
559 const pointField& outsidePts,
561 const bool selectCut,
562 const bool selectInside,
563 const bool selectOutside,
565 const label nCutLayers,
573 // Cut faces with surface and classify cells
574 surfaceSets::getSurfaceSets
589 // Combine wanted parts into selected
592 selected.addSet(cutCells);
596 selected.addSet(inside);
600 selected.addSet(outside);
603 Info<< "Determined cell status:" << endl
604 << " inside :" << inside.size() << endl
605 << " outside :" << outside.size() << endl
606 << " cutCells:" << cutCells.size() << endl
607 << " selected:" << selected.size() << endl
610 writeSet(inside, "inside cells");
611 writeSet(outside, "outside cells");
612 writeSet(cutCells, "cut cells");
613 writeSet(selected, "selected cells");
619 int main(int argc, char *argv[])
621 argList::noParallel();
623 # include "setRootCase.H"
624 # include "createTime.H"
625 # include "createPolyMesh.H"
627 // If nessecary add oldInternalFaces patch
628 label newPatchI = addPatch(mesh, "oldInternalFaces");
632 // Read motionProperties dictionary
635 Info<< "Checking for motionProperties\n" << endl;
642 IOobject::MUST_READ_IF_MODIFIED,
646 // corrector for mesh motion
647 twoDPointCorrector* correct2DPtr = NULL;
649 if (motionObj.headerOk())
651 Info<< "Reading " << runTime.constant() / "motionProperties"
654 IOdictionary motionProperties(motionObj);
656 Switch twoDMotion(motionProperties.lookup("twoDMotion"));
660 Info<< "Correcting for 2D motion" << endl << endl;
661 correct2DPtr = new twoDPointCorrector(mesh);
665 IOdictionary refineDict
669 "autoRefineMeshDict",
672 IOobject::MUST_READ_IF_MODIFIED,
677 fileName surfName(refineDict.lookup("surface"));
679 label nCutLayers(readLabel(refineDict.lookup("nCutLayers")));
680 label cellLimit(readLabel(refineDict.lookup("cellLimit")));
681 bool selectCut(readBool(refineDict.lookup("selectCut")));
682 bool selectInside(readBool(refineDict.lookup("selectInside")));
683 bool selectOutside(readBool(refineDict.lookup("selectOutside")));
684 bool selectHanging(readBool(refineDict.lookup("selectHanging")));
686 scalar minEdgeLen(readScalar(refineDict.lookup("minEdgeLen")));
687 scalar maxEdgeLen(readScalar(refineDict.lookup("maxEdgeLen")));
688 scalar curvature(readScalar(refineDict.lookup("curvature")));
689 scalar curvDist(readScalar(refineDict.lookup("curvatureDistance")));
690 pointField outsidePts(refineDict.lookup("outsidePoints"));
691 label refinementLimit(readLabel(refineDict.lookup("splitLevel")));
692 bool writeMesh(readBool(refineDict.lookup("writeMesh")));
694 Info<< "Cells to be used for meshing (0=false, 1=true):" << nl
695 << " cells cut by surface : " << selectCut << nl
696 << " cells inside of surface : " << selectInside << nl
697 << " cells outside of surface : " << selectOutside << nl
698 << " hanging cells : " << selectHanging << nl
702 if (nCutLayers > 0 && selectInside)
704 WarningIn(args.executable())
705 << "Illogical settings : Both nCutLayers>0 and selectInside true."
707 << "This would mean that inside cells get removed but should be"
708 << " included in final mesh" << endl;
712 triSurface surf(surfName);
714 // Search engine on surface
715 triSurfaceSearch querySurf(surf);
717 // Search engine on mesh. No face decomposition since mesh unwarped.
718 meshSearch queryMesh(mesh, false);
720 // Check all 'outside' points
721 forAll(outsidePts, outsideI)
723 const point& outsidePoint = outsidePts[outsideI];
725 if (queryMesh.findCell(outsidePoint, -1, false) == -1)
727 FatalErrorIn(args.executable())
728 << "outsidePoint " << outsidePoint
729 << " is not inside any cell"
736 // Current refinement level. Read if present.
743 polyMesh::defaultRegion,
745 IOobject::READ_IF_PRESENT,
748 labelList(mesh.nCells(), 0)
751 label maxLevel = max(refLevel);
755 Info<< "Read existing refinement level from file "
756 << refLevel.objectPath() << nl
757 << " min level : " << min(refLevel) << nl
758 << " max level : " << maxLevel << nl
763 Info<< "Created zero refinement level in file "
764 << refLevel.objectPath() << nl
771 // Print edge stats on original mesh. Leave out 2d-normal direction
772 direction normalDir(getNormalDir(correct2DPtr));
773 scalar meshMinEdgeLen = getEdgeStats(mesh, normalDir);
775 while (meshMinEdgeLen > minEdgeLen)
777 // Get inside/outside/cutCells cellSets.
778 cellSet inside(mesh, "inside", mesh.nCells()/10);
779 cellSet outside(mesh, "outside", mesh.nCells()/10);
780 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
781 cellSet selected(mesh, "selected", mesh.nCells()/10);
790 selectCut, // for determination of selected
791 selectInside, // for determination of selected
792 selectOutside, // for determination of selected
794 nCutLayers, // How many layers of cutCells to include
799 selected // not used but determined anyway.
802 Info<< " Selected " << cutCells.name() << " with "
803 << cutCells.size() << " cells" << endl;
805 if ((curvDist > 0) && (meshMinEdgeLen < maxEdgeLen))
807 // Done refining enough close to surface. Now switch to more
808 // intelligent refinement where only cutCells on surface curvature
810 cellSet curveCells(mesh, "curveCells", mesh.nCells()/10);
822 Info<< " Selected " << curveCells.name() << " with "
823 << curveCells.size() << " cells" << endl;
825 // Add neighbours to cutCells. This is if selectCut is not
826 // true and so outside and/or inside cells get exposed there is
827 // also refinement in them.
841 // Subset cutCells to only curveCells
842 cutCells.subset(curveCells);
844 Info<< " Removed cells not on surface curvature. Selected "
845 << cutCells.size() << endl;
851 // Subset mesh to remove inside cells altogether. Updates cutCells,
853 subsetMesh(mesh, writeMesh, newPatchI, inside, cutCells, refLevel);
857 // Added cells from 2:1 refinement level restriction.
872 Info<< " Current cells : " << mesh.nCells() << nl
873 << " Selected for refinement :" << cutCells.size() << nl
876 if (cutCells.empty())
878 Info<< "Stopping refining since 0 cells selected to be refined ..."
883 if ((mesh.nCells() + 8*cutCells.size()) > cellLimit)
885 Info<< "Stopping refining since cell limit reached ..." << nl
886 << "Would refine from " << mesh.nCells()
887 << " to " << mesh.nCells() + 8*cutCells.size() << " cells."
900 Info<< " After refinement:" << mesh.nCells() << nl
905 Info<< " Writing refined mesh to time " << runTime.timeName()
908 IOstream::defaultPrecision(10);
913 // Update mesh edge stats.
914 meshMinEdgeLen = getEdgeStats(mesh, normalDir);
920 // Get inside/outside/cutCells cellSets.
921 cellSet inside(mesh, "inside", mesh.nCells()/10);
922 cellSet outside(mesh, "outside", mesh.nCells()/10);
923 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
924 cellSet selected(mesh, "selected", mesh.nCells()/10);
946 // Find any cells which have all their points on the outside of the
947 // selected set and refine them
948 labelHashSet hanging = surfaceSets::getHangingCells(mesh, selected);
950 Info<< "Detected " << hanging.size() << " hanging cells"
951 << " (cells with all points on"
952 << " outside of cellSet 'selected').\nThese would probably result"
953 << " in flattened cells when snapping the mesh to the surface"
956 Info<< "Refining " << hanging.size() << " hanging cells" << nl
959 // Added cells from 2:1 refinement level restriction.
973 doRefinement(mesh, refineDict, hanging, refLevel);
975 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
979 IOstream::defaultPrecision(10);
986 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
989 // Write final mesh. (will have been written already if writeMesh=true)
990 IOstream::defaultPrecision(10);
996 Info<< "End\n" << endl;
1002 // ************************************************************************* //