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
26 Utility to refine cells near to a surface.
28 \*---------------------------------------------------------------------------*/
33 #include "twoDPointCorrector.H"
35 #include "multiDirRefinement.H"
37 #include "triSurface.H"
38 #include "triSurfaceSearch.H"
42 #include "surfaceToCell.H"
43 #include "surfaceToPoint.H"
44 #include "cellToPoint.H"
45 #include "pointToCell.H"
46 #include "cellToCell.H"
47 #include "surfaceSets.H"
48 #include "polyTopoChange.H"
49 #include "polyTopoChanger.H"
50 #include "mapPolyMesh.H"
51 #include "labelIOList.H"
52 #include "emptyPolyPatch.H"
53 #include "removeCells.H"
54 #include "meshSearch.H"
58 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
61 // Max cos angle for edges to be considered aligned with axis.
62 static const scalar edgeTol = 1E-3;
65 void writeSet(const cellSet& cells, const string& msg)
67 Info<< "Writing " << msg << " (" << cells.size() << ") to cellSet "
68 << cells.instance()/cells.local()/cells.name()
74 direction getNormalDir(const twoDPointCorrector* correct2DPtr)
80 const vector& normal = correct2DPtr->planeNormal();
82 if (mag(normal & vector(1, 0, 0)) > 1-edgeTol)
86 else if (mag(normal & vector(0, 1, 0)) > 1-edgeTol)
90 else if (mag(normal & vector(0, 0, 1)) > 1-edgeTol)
100 // Calculate some edge statistics on mesh. Return min. edge length over all
101 // directions but exclude component (0=x, 1=y, 2=z, other=none)
102 scalar getEdgeStats(const primitiveMesh& mesh, const direction excludeCmpt)
109 scalar maxX = -GREAT;
113 scalar maxY = -GREAT;
117 scalar maxZ = -GREAT;
120 scalar minOther = GREAT;
121 scalar maxOther = -GREAT;
123 const edgeList& edges = mesh.edges();
127 const edge& e = edges[edgeI];
129 vector eVec(e.vec(mesh.points()));
131 scalar eMag = mag(eVec);
135 if (mag(eVec & x) > 1-edgeTol)
137 minX = min(minX, eMag);
138 maxX = max(maxX, eMag);
141 else if (mag(eVec & y) > 1-edgeTol)
143 minY = min(minY, eMag);
144 maxY = max(maxY, eMag);
147 else if (mag(eVec & z) > 1-edgeTol)
149 minZ = min(minZ, eMag);
150 maxZ = max(maxZ, eMag);
155 minOther = min(minOther, eMag);
156 maxOther = max(maxOther, eMag);
160 Info<< "Mesh bounding box:" << boundBox(mesh.points()) << nl << nl
161 << "Mesh edge statistics:" << nl
162 << " x aligned : number:" << nX << "\tminLen:" << minX
163 << "\tmaxLen:" << maxX << nl
164 << " y aligned : number:" << nY << "\tminLen:" << minY
165 << "\tmaxLen:" << maxY << nl
166 << " z aligned : number:" << nZ << "\tminLen:" << minZ
167 << "\tmaxLen:" << maxZ << nl
168 << " other : number:" << mesh.nEdges() - nX - nY - nZ
169 << "\tminLen:" << minOther
170 << "\tmaxLen:" << maxOther << nl << endl;
172 if (excludeCmpt == 0)
174 return min(minY, min(minZ, minOther));
176 else if (excludeCmpt == 1)
178 return min(minX, min(minZ, minOther));
180 else if (excludeCmpt == 2)
182 return min(minX, min(minY, minOther));
186 return min(minX, min(minY, min(minZ, minOther)));
191 // Adds empty patch if not yet there. Returns patchID.
192 label addPatch(polyMesh& mesh, const word& patchName)
194 label patchI = mesh.boundaryMesh().findPatchID(patchName);
198 const polyBoundaryMesh& patches = mesh.boundaryMesh();
200 List<polyPatch*> newPatches(patches.size() + 1);
202 // Add empty patch as 0th entry (Note: only since subsetMesh wants this)
208 Foam::word(patchName),
210 mesh.nInternalFaces(),
217 const polyPatch& pp = patches[i];
229 mesh.removeBoundary();
230 mesh.addPatches(newPatches);
232 Info<< "Created patch oldInternalFaces at " << patchI << endl;
236 Info<< "Reusing patch oldInternalFaces at " << patchI << endl;
244 // Take surface and select cells based on surface curvature.
245 void selectCurvatureCells
247 const polyMesh& mesh,
248 const fileName& surfName,
249 const triSurfaceSearch& querySurf,
250 const scalar nearDist,
251 const scalar curvature,
255 // Use surfaceToCell to do actual calculation.
257 // Since we're adding make sure set is on disk.
260 // Take centre of cell 0 as outside point since info not used.
262 surfaceToCell cutSource
268 pointField(1, mesh.cellCentres()[0]),
276 cutSource.applyToSet(topoSetSource::ADD, cells);
280 // cutCells contains currently selected cells to be refined. Add neighbours
281 // on the inside or outside to them.
282 void addCutNeighbours
284 const polyMesh& mesh,
285 const bool selectInside,
286 const bool selectOutside,
287 const labelHashSet& inside,
288 const labelHashSet& outside,
289 labelHashSet& cutCells
292 // Pick up face neighbours of cutCells
294 labelHashSet addCutFaces(cutCells.size());
298 labelHashSet::const_iterator iter = cutCells.begin();
299 iter != cutCells.end();
303 label cellI = iter.key();
305 const labelList& cFaces = mesh.cells()[cellI];
309 label faceI = cFaces[i];
311 if (mesh.isInternalFace(faceI))
313 label nbr = mesh.faceOwner()[faceI];
317 nbr = mesh.faceNeighbour()[faceI];
320 if (selectInside && inside.found(nbr))
322 addCutFaces.insert(nbr);
324 else if (selectOutside && outside.found(nbr))
326 addCutFaces.insert(nbr);
332 Info<< " Selected an additional " << addCutFaces.size()
333 << " neighbours of cutCells to refine" << endl;
337 labelHashSet::const_iterator iter = addCutFaces.begin();
338 iter != addCutFaces.end();
342 cutCells.insert(iter.key());
347 // Return true if any cells had to be split to keep a difference between
348 // neighbouring refinement levels < limitDiff.
349 // Gets cells which will be refined (so increase the refinement level) and
351 bool limitRefinementLevel
353 const primitiveMesh& mesh,
354 const label limitDiff,
355 const labelHashSet& excludeCells,
356 const labelList& refLevel,
357 labelHashSet& cutCells
360 // Do simple check on validity of refinement level.
361 forAll(refLevel, cellI)
363 if (!excludeCells.found(cellI))
365 const labelList& cCells = mesh.cellCells()[cellI];
369 label nbr = cCells[i];
371 if (!excludeCells.found(nbr))
373 if (refLevel[cellI] - refLevel[nbr] >= limitDiff)
375 FatalErrorIn("limitRefinementLevel")
376 << "Level difference between neighbouring cells "
377 << cellI << " and " << nbr
378 << " greater than or equal to " << limitDiff << endl
379 << "refLevels:" << refLevel[cellI] << ' '
380 << refLevel[nbr] << abort(FatalError);
388 labelHashSet addCutCells(cutCells.size());
392 labelHashSet::const_iterator iter = cutCells.begin();
393 iter != cutCells.end();
397 // cellI will be refined.
398 label cellI = iter.key();
400 const labelList& cCells = mesh.cellCells()[cellI];
404 label nbr = cCells[i];
406 if (!excludeCells.found(nbr) && !cutCells.found(nbr))
408 if (refLevel[cellI] + 1 - refLevel[nbr] >= limitDiff)
410 addCutCells.insert(nbr);
416 if (addCutCells.size())
418 // Add cells to cutCells.
420 Info<< "Added an additional " << addCutCells.size() << " cells"
421 << " to satisfy 1:" << limitDiff << " refinement level"
425 labelHashSet::const_iterator iter = addCutCells.begin();
426 iter != addCutCells.end();
430 cutCells.insert(iter.key());
436 Info<< "Added no additional cells"
437 << " to satisfy 1:" << limitDiff << " refinement level"
445 // Do all refinement (i.e. refCells) according to refineDict and update
446 // refLevel afterwards for added cells
450 const dictionary& refineDict,
451 const labelHashSet& refCells,
455 label oldCells = mesh.nCells();
457 // Multi-iteration, multi-direction topology modifier.
458 multiDirRefinement multiRef
466 // Update refLevel for split cells
469 refLevel.setSize(mesh.nCells());
471 for (label cellI = oldCells; cellI < mesh.nCells(); cellI++)
476 const labelListList& addedCells = multiRef.addedCells();
478 forAll(addedCells, oldCellI)
480 const labelList& added = addedCells[oldCellI];
484 // Give all cells resulting from split the refinement level
486 label masterLevel = ++refLevel[oldCellI];
490 refLevel[added[i]] = masterLevel;
497 // Subset mesh and update refLevel and cutCells
501 const label writeMesh,
502 const label patchI, // patchID for exposed faces
503 const labelHashSet& cellsToRemove,
505 labelIOList& refLevel
508 removeCells cellRemover(mesh);
510 labelList cellLabels(cellsToRemove.toc());
512 Info<< "Mesh has:" << mesh.nCells() << " cells."
513 << " Removing:" << cellLabels.size() << " cells" << endl;
516 labelList exposedFaces(cellRemover.getExposedFaces(cellLabels));
518 polyTopoChange meshMod(mesh);
519 cellRemover.setRefinement
523 labelList(exposedFaces.size(), patchI),
528 Info<< "Morphing ..." << endl;
530 const Time& runTime = mesh.time();
532 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
534 if (morphMap().hasMotionPoints())
536 mesh.movePoints(morphMap().preMotionPoints());
539 // Update topology on cellRemover
540 cellRemover.updateMesh(morphMap());
542 // Update refLevel for removed cells.
543 const labelList& cellMap = morphMap().cellMap();
545 labelList newRefLevel(cellMap.size());
549 newRefLevel[i] = refLevel[cellMap[i]];
552 // Transfer back to refLevel
553 refLevel.transfer(newRefLevel);
557 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
560 IOstream::defaultPrecision(10);
565 // Update cutCells for removed cells.
566 cutCells.updateMesh(morphMap());
570 // Divide the cells according to status compared to surface. Constructs sets:
571 // - cutCells : all cells cut by surface
572 // - inside : all cells inside surface
573 // - outside : ,, outside ,,
574 // and a combined set:
575 // - selected : sum of above according to selectCut/Inside/Outside flags.
578 const polyMesh& mesh,
579 const fileName& surfName,
580 const triSurfaceSearch& querySurf,
581 const pointField& outsidePts,
583 const bool selectCut,
584 const bool selectInside,
585 const bool selectOutside,
587 const label nCutLayers,
595 // Cut faces with surface and classify cells
596 surfaceSets::getSurfaceSets
611 // Combine wanted parts into selected
614 selected.addSet(cutCells);
618 selected.addSet(inside);
622 selected.addSet(outside);
625 Info<< "Determined cell status:" << endl
626 << " inside :" << inside.size() << endl
627 << " outside :" << outside.size() << endl
628 << " cutCells:" << cutCells.size() << endl
629 << " selected:" << selected.size() << endl
632 writeSet(inside, "inside cells");
633 writeSet(outside, "outside cells");
634 writeSet(cutCells, "cut cells");
635 writeSet(selected, "selected cells");
641 int main(int argc, char *argv[])
643 argList::noParallel();
645 # include "setRootCase.H"
646 # include "createTime.H"
647 # include "createPolyMesh.H"
649 // If nessecary add oldInternalFaces patch
650 label newPatchI = addPatch(mesh, "oldInternalFaces");
654 // Read motionProperties dictionary
657 Info<< "Checking for motionProperties\n" << endl;
668 // corrector for mesh motion
669 twoDPointCorrector* correct2DPtr = NULL;
671 if (motionObj.headerOk())
673 Info<< "Reading " << runTime.constant() / "motionProperties"
676 IOdictionary motionProperties(motionObj);
678 Switch twoDMotion(motionProperties.lookup("twoDMotion"));
682 Info<< "Correcting for 2D motion" << endl << endl;
683 correct2DPtr = new twoDPointCorrector(mesh);
687 IOdictionary refineDict
691 "autoRefineMeshDict",
699 fileName surfName(refineDict.lookup("surface"));
701 label nCutLayers(readLabel(refineDict.lookup("nCutLayers")));
702 label cellLimit(readLabel(refineDict.lookup("cellLimit")));
703 bool selectCut(readBool(refineDict.lookup("selectCut")));
704 bool selectInside(readBool(refineDict.lookup("selectInside")));
705 bool selectOutside(readBool(refineDict.lookup("selectOutside")));
706 bool selectHanging(readBool(refineDict.lookup("selectHanging")));
708 scalar minEdgeLen(readScalar(refineDict.lookup("minEdgeLen")));
709 scalar maxEdgeLen(readScalar(refineDict.lookup("maxEdgeLen")));
710 scalar curvature(readScalar(refineDict.lookup("curvature")));
711 scalar curvDist(readScalar(refineDict.lookup("curvatureDistance")));
712 pointField outsidePts(refineDict.lookup("outsidePoints"));
713 label refinementLimit(readLabel(refineDict.lookup("splitLevel")));
714 bool writeMesh(readBool(refineDict.lookup("writeMesh")));
716 Info<< "Cells to be used for meshing (0=false, 1=true):" << nl
717 << " cells cut by surface : " << selectCut << nl
718 << " cells inside of surface : " << selectInside << nl
719 << " cells outside of surface : " << selectOutside << nl
720 << " hanging cells : " << selectHanging << nl
724 if (nCutLayers > 0 && selectInside)
726 WarningIn(args.executable())
727 << "Illogical settings : Both nCutLayers>0 and selectInside true."
729 << "This would mean that inside cells get removed but should be"
730 << " included in final mesh" << endl;
734 triSurface surf(surfName);
736 // Search engine on surface
737 triSurfaceSearch querySurf(surf);
739 // Search engine on mesh. No face decomposition since mesh unwarped.
740 meshSearch queryMesh(mesh, false);
742 // Check all 'outside' points
743 forAll(outsidePts, outsideI)
745 const point& outsidePoint = outsidePts[outsideI];
747 if (queryMesh.findCell(outsidePoint, -1, false) == -1)
749 FatalErrorIn(args.executable())
750 << "outsidePoint " << outsidePoint
751 << " is not inside any cell"
758 // Current refinement level. Read if present.
765 polyMesh::defaultRegion,
767 IOobject::READ_IF_PRESENT,
770 labelList(mesh.nCells(), 0)
773 label maxLevel = max(refLevel);
777 Info<< "Read existing refinement level from file "
778 << refLevel.objectPath() << nl
779 << " min level : " << min(refLevel) << nl
780 << " max level : " << maxLevel << nl
785 Info<< "Created zero refinement level in file "
786 << refLevel.objectPath() << nl
793 // Print edge stats on original mesh. Leave out 2d-normal direction
794 direction normalDir(getNormalDir(correct2DPtr));
795 scalar meshMinEdgeLen = getEdgeStats(mesh, normalDir);
797 while (meshMinEdgeLen > minEdgeLen)
799 // Get inside/outside/cutCells cellSets.
800 cellSet inside(mesh, "inside", mesh.nCells()/10);
801 cellSet outside(mesh, "outside", mesh.nCells()/10);
802 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
803 cellSet selected(mesh, "selected", mesh.nCells()/10);
812 selectCut, // for determination of selected
813 selectInside, // for determination of selected
814 selectOutside, // for determination of selected
816 nCutLayers, // How many layers of cutCells to include
821 selected // not used but determined anyway.
824 Info<< " Selected " << cutCells.name() << " with "
825 << cutCells.size() << " cells" << endl;
827 if ((curvDist > 0) && (meshMinEdgeLen < maxEdgeLen))
829 // Done refining enough close to surface. Now switch to more
830 // intelligent refinement where only cutCells on surface curvature
832 cellSet curveCells(mesh, "curveCells", mesh.nCells()/10);
844 Info<< " Selected " << curveCells.name() << " with "
845 << curveCells.size() << " cells" << endl;
847 // Add neighbours to cutCells. This is if selectCut is not
848 // true and so outside and/or inside cells get exposed there is
849 // also refinement in them.
863 // Subset cutCells to only curveCells
864 cutCells.subset(curveCells);
866 Info<< " Removed cells not on surface curvature. Selected "
867 << cutCells.size() << endl;
873 // Subset mesh to remove inside cells altogether. Updates cutCells,
875 subsetMesh(mesh, writeMesh, newPatchI, inside, cutCells, refLevel);
879 // Added cells from 2:1 refinement level restriction.
894 Info<< " Current cells : " << mesh.nCells() << nl
895 << " Selected for refinement :" << cutCells.size() << nl
898 if (cutCells.empty())
900 Info<< "Stopping refining since 0 cells selected to be refined ..."
905 if ((mesh.nCells() + 8*cutCells.size()) > cellLimit)
907 Info<< "Stopping refining since cell limit reached ..." << nl
908 << "Would refine from " << mesh.nCells()
909 << " to " << mesh.nCells() + 8*cutCells.size() << " cells."
922 Info<< " After refinement:" << mesh.nCells() << nl
927 Info<< " Writing refined mesh to time " << runTime.timeName()
930 IOstream::defaultPrecision(10);
935 // Update mesh edge stats.
936 meshMinEdgeLen = getEdgeStats(mesh, normalDir);
942 // Get inside/outside/cutCells cellSets.
943 cellSet inside(mesh, "inside", mesh.nCells()/10);
944 cellSet outside(mesh, "outside", mesh.nCells()/10);
945 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
946 cellSet selected(mesh, "selected", mesh.nCells()/10);
968 // Find any cells which have all their points on the outside of the
969 // selected set and refine them
970 labelHashSet hanging = surfaceSets::getHangingCells(mesh, selected);
972 Info<< "Detected " << hanging.size() << " hanging cells"
973 << " (cells with all points on"
974 << " outside of cellSet 'selected').\nThese would probably result"
975 << " in flattened cells when snapping the mesh to the surface"
978 Info<< "Refining " << hanging.size() << " hanging cells" << nl
981 // Added cells from 2:1 refinement level restriction.
995 doRefinement(mesh, refineDict, hanging, refLevel);
997 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
1001 IOstream::defaultPrecision(10);
1006 else if (!writeMesh)
1008 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
1011 // Write final mesh. (will have been written already if writeMesh=true)
1012 IOstream::defaultPrecision(10);
1018 Info << "End\n" << endl;
1024 // ************************************************************************* //