1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
25 Utility to refine cells near to a surface.
27 \*---------------------------------------------------------------------------*/
30 #include "objectRegistry.H"
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 "directTopoChange.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());
297 labelHashSet::const_iterator iter = cutCells.begin();
298 iter != cutCells.end();
302 label cellI = iter.key();
304 const labelList& cFaces = mesh.cells()[cellI];
308 label faceI = cFaces[i];
310 if (mesh.isInternalFace(faceI))
312 label nbr = mesh.faceOwner()[faceI];
316 nbr = mesh.faceNeighbour()[faceI];
319 if (selectInside && inside.found(nbr))
321 addCutFaces.insert(nbr);
323 else if (selectOutside && outside.found(nbr))
325 addCutFaces.insert(nbr);
331 Info<< " Selected an additional " << addCutFaces.size()
332 << " neighbours of cutCells to refine" << endl;
336 labelHashSet::const_iterator iter = addCutFaces.begin();
337 iter != addCutFaces.end();
341 cutCells.insert(iter.key());
346 // Return true if any cells had to be split to keep a difference between
347 // neighbouring refinement levels < limitDiff.
348 // Gets cells which will be refined (so increase the refinement level) and
350 bool limitRefinementLevel
352 const primitiveMesh& mesh,
353 const label limitDiff,
354 const labelHashSet& excludeCells,
355 const labelList& refLevel,
356 labelHashSet& cutCells
359 // Do simple check on validity of refinement level.
360 forAll(refLevel, cellI)
362 if (!excludeCells.found(cellI))
364 const labelList& cCells = mesh.cellCells()[cellI];
368 label nbr = cCells[i];
370 if (!excludeCells.found(nbr))
372 if (refLevel[cellI] - refLevel[nbr] >= limitDiff)
374 FatalErrorIn("limitRefinementLevel")
375 << "Level difference between neighbouring cells "
376 << cellI << " and " << nbr
377 << " greater than or equal to " << limitDiff << endl
378 << "refLevels:" << refLevel[cellI] << ' '
379 << refLevel[nbr] << abort(FatalError);
387 labelHashSet addCutCells(cutCells.size());
391 labelHashSet::const_iterator iter = cutCells.begin();
392 iter != cutCells.end();
396 // cellI will be refined.
397 label cellI = iter.key();
399 const labelList& cCells = mesh.cellCells()[cellI];
403 label nbr = cCells[i];
405 if (!excludeCells.found(nbr) && !cutCells.found(nbr))
407 if (refLevel[cellI] + 1 - refLevel[nbr] >= limitDiff)
409 addCutCells.insert(nbr);
415 if (addCutCells.size())
417 // Add cells to cutCells.
419 Info<< "Added an additional " << addCutCells.size() << " cells"
420 << " to satisfy 1:" << limitDiff << " refinement level"
424 labelHashSet::const_iterator iter = addCutCells.begin();
425 iter != addCutCells.end();
429 cutCells.insert(iter.key());
435 Info<< "Added no additional cells"
436 << " to satisfy 1:" << limitDiff << " refinement level"
444 // Do all refinement (i.e. refCells) according to refineDict and update
445 // refLevel afterwards for added cells
449 const dictionary& refineDict,
450 const labelHashSet& refCells,
454 label oldCells = mesh.nCells();
456 // Multi-iteration, multi-direction topology modifier.
457 multiDirRefinement multiRef
465 // Update refLevel for split cells
468 refLevel.setSize(mesh.nCells());
470 for (label cellI = oldCells; cellI < mesh.nCells(); cellI++)
475 const labelListList& addedCells = multiRef.addedCells();
477 forAll(addedCells, oldCellI)
479 const labelList& added = addedCells[oldCellI];
483 // Give all cells resulting from split the refinement level
485 label masterLevel = ++refLevel[oldCellI];
489 refLevel[added[i]] = masterLevel;
496 // Subset mesh and update refLevel and cutCells
500 const label writeMesh,
501 const label patchI, // patchID for exposed faces
502 const labelHashSet& cellsToRemove,
504 labelIOList& refLevel
507 removeCells cellRemover(mesh);
509 labelList cellLabels(cellsToRemove.toc());
511 Info<< "Mesh has:" << mesh.nCells() << " cells."
512 << " Removing:" << cellLabels.size() << " cells" << endl;
515 labelList exposedFaces(cellRemover.getExposedFaces(cellLabels));
517 directTopoChange meshMod(mesh);
518 cellRemover.setRefinement
522 labelList(exposedFaces.size(), patchI),
527 Info<< "Morphing ..." << endl;
529 const Time& runTime = mesh.time();
531 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
533 if (morphMap().hasMotionPoints())
535 mesh.movePoints(morphMap().preMotionPoints());
538 // Update topology on cellRemover
539 cellRemover.updateMesh(morphMap());
541 // Update refLevel for removed cells.
542 const labelList& cellMap = morphMap().cellMap();
544 labelList newRefLevel(cellMap.size());
548 newRefLevel[i] = refLevel[cellMap[i]];
551 // Transfer back to refLevel
552 refLevel.transfer(newRefLevel);
556 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
559 IOstream::defaultPrecision(10);
564 // Update cutCells for removed cells.
565 cutCells.updateMesh(morphMap());
569 // Divide the cells according to status compared to surface. Constructs sets:
570 // - cutCells : all cells cut by surface
571 // - inside : all cells inside surface
572 // - outside : ,, outside ,,
573 // and a combined set:
574 // - selected : sum of above according to selectCut/Inside/Outside flags.
577 const polyMesh& mesh,
578 const fileName& surfName,
579 const triSurfaceSearch& querySurf,
580 const pointField& outsidePts,
582 const bool selectCut,
583 const bool selectInside,
584 const bool selectOutside,
586 const label nCutLayers,
594 // Cut faces with surface and classify cells
595 surfaceSets::getSurfaceSets
610 // Combine wanted parts into selected
613 selected.addSet(cutCells);
617 selected.addSet(inside);
621 selected.addSet(outside);
624 Info<< "Determined cell status:" << endl
625 << " inside :" << inside.size() << endl
626 << " outside :" << outside.size() << endl
627 << " cutCells:" << cutCells.size() << endl
628 << " selected:" << selected.size() << endl
631 writeSet(inside, "inside cells");
632 writeSet(outside, "outside cells");
633 writeSet(cutCells, "cut cells");
634 writeSet(selected, "selected cells");
640 int main(int argc, char *argv[])
642 argList::noParallel();
644 # include "setRootCase.H"
645 # include "createTime.H"
646 # include "createPolyMesh.H"
648 // If nessecary add oldInternalFaces patch
649 label newPatchI = addPatch(mesh, "oldInternalFaces");
653 // Read motionProperties dictionary
656 Info<< "Checking for motionProperties\n" << endl;
667 // corrector for mesh motion
668 twoDPointCorrector* correct2DPtr = NULL;
670 if (motionObj.headerOk())
672 Info<< "Reading " << runTime.constant() / "motionProperties"
675 IOdictionary motionProperties(motionObj);
677 Switch twoDMotion(motionProperties.lookup("twoDMotion"));
681 Info<< "Correcting for 2D motion" << endl << endl;
682 correct2DPtr = new twoDPointCorrector(mesh);
686 IOdictionary refineDict
690 "autoRefineMeshDict",
698 fileName surfName(refineDict.lookup("surface"));
700 label nCutLayers(readLabel(refineDict.lookup("nCutLayers")));
701 label cellLimit(readLabel(refineDict.lookup("cellLimit")));
702 bool selectCut(readBool(refineDict.lookup("selectCut")));
703 bool selectInside(readBool(refineDict.lookup("selectInside")));
704 bool selectOutside(readBool(refineDict.lookup("selectOutside")));
705 bool selectHanging(readBool(refineDict.lookup("selectHanging")));
707 scalar minEdgeLen(readScalar(refineDict.lookup("minEdgeLen")));
708 scalar maxEdgeLen(readScalar(refineDict.lookup("maxEdgeLen")));
709 scalar curvature(readScalar(refineDict.lookup("curvature")));
710 scalar curvDist(readScalar(refineDict.lookup("curvatureDistance")));
711 pointField outsidePts(refineDict.lookup("outsidePoints"));
712 label refinementLimit(readLabel(refineDict.lookup("splitLevel")));
713 bool writeMesh(readBool(refineDict.lookup("writeMesh")));
715 Info<< "Cells to be used for meshing (0=false, 1=true):" << nl
716 << " cells cut by surface : " << selectCut << nl
717 << " cells inside of surface : " << selectInside << nl
718 << " cells outside of surface : " << selectOutside << nl
719 << " hanging cells : " << selectHanging << nl
723 if (nCutLayers > 0 && selectInside)
725 WarningIn(args.executable())
726 << "Illogical settings : Both nCutLayers>0 and selectInside true."
728 << "This would mean that inside cells get removed but should be"
729 << " included in final mesh" << endl;
733 triSurface surf(surfName);
735 // Search engine on surface
736 triSurfaceSearch querySurf(surf);
738 // Search engine on mesh. No face decomposition since mesh unwarped.
739 meshSearch queryMesh(mesh, false);
741 // Check all 'outside' points
742 forAll(outsidePts, outsideI)
744 const point& outsidePoint = outsidePts[outsideI];
746 if (queryMesh.findCell(outsidePoint, -1, false) == -1)
748 FatalErrorIn(args.executable())
749 << "outsidePoint " << outsidePoint
750 << " is not inside any cell"
757 // Current refinement level. Read if present.
764 polyMesh::defaultRegion,
766 IOobject::READ_IF_PRESENT,
769 labelList(mesh.nCells(), 0)
772 label maxLevel = max(refLevel);
776 Info<< "Read existing refinement level from file "
777 << refLevel.objectPath() << nl
778 << " min level : " << min(refLevel) << nl
779 << " max level : " << maxLevel << nl
784 Info<< "Created zero refinement level in file "
785 << refLevel.objectPath() << nl
792 // Print edge stats on original mesh. Leave out 2d-normal direction
793 direction normalDir(getNormalDir(correct2DPtr));
794 scalar meshMinEdgeLen = getEdgeStats(mesh, normalDir);
796 while (meshMinEdgeLen > minEdgeLen)
798 // Get inside/outside/cutCells cellSets.
799 cellSet inside(mesh, "inside", mesh.nCells()/10);
800 cellSet outside(mesh, "outside", mesh.nCells()/10);
801 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
802 cellSet selected(mesh, "selected", mesh.nCells()/10);
811 selectCut, // for determination of selected
812 selectInside, // for determination of selected
813 selectOutside, // for determination of selected
815 nCutLayers, // How many layers of cutCells to include
820 selected // not used but determined anyway.
823 Info<< " Selected " << cutCells.name() << " with "
824 << cutCells.size() << " cells" << endl;
826 if ((curvDist > 0) && (meshMinEdgeLen < maxEdgeLen))
828 // Done refining enough close to surface. Now switch to more
829 // intelligent refinement where only cutCells on surface curvature
831 cellSet curveCells(mesh, "curveCells", mesh.nCells()/10);
843 Info<< " Selected " << curveCells.name() << " with "
844 << curveCells.size() << " cells" << endl;
846 // Add neighbours to cutCells. This is if selectCut is not
847 // true and so outside and/or inside cells get exposed there is
848 // also refinement in them.
862 // Subset cutCells to only curveCells
863 cutCells.subset(curveCells);
865 Info<< " Removed cells not on surface curvature. Selected "
866 << cutCells.size() << endl;
872 // Subset mesh to remove inside cells altogether. Updates cutCells,
874 subsetMesh(mesh, writeMesh, newPatchI, inside, cutCells, refLevel);
878 // Added cells from 2:1 refinement level restriction.
893 Info<< " Current cells : " << mesh.nCells() << nl
894 << " Selected for refinement :" << cutCells.size() << nl
897 if (cutCells.empty())
899 Info<< "Stopping refining since 0 cells selected to be refined ..."
904 if ((mesh.nCells() + 8*cutCells.size()) > cellLimit)
906 Info<< "Stopping refining since cell limit reached ..." << nl
907 << "Would refine from " << mesh.nCells()
908 << " to " << mesh.nCells() + 8*cutCells.size() << " cells."
921 Info<< " After refinement:" << mesh.nCells() << nl
926 Info<< " Writing refined mesh to time " << runTime.timeName()
929 IOstream::defaultPrecision(10);
934 // Update mesh edge stats.
935 meshMinEdgeLen = getEdgeStats(mesh, normalDir);
941 // Get inside/outside/cutCells cellSets.
942 cellSet inside(mesh, "inside", mesh.nCells()/10);
943 cellSet outside(mesh, "outside", mesh.nCells()/10);
944 cellSet cutCells(mesh, "cutCells", mesh.nCells()/10);
945 cellSet selected(mesh, "selected", mesh.nCells()/10);
967 // Find any cells which have all their points on the outside of the
968 // selected set and refine them
969 labelHashSet hanging = surfaceSets::getHangingCells(mesh, selected);
971 Info<< "Detected " << hanging.size() << " hanging cells"
972 << " (cells with all points on"
973 << " outside of cellSet 'selected').\nThese would probably result"
974 << " in flattened cells when snapping the mesh to the surface"
977 Info<< "Refining " << hanging.size() << " hanging cells" << nl
980 // Added cells from 2:1 refinement level restriction.
994 doRefinement(mesh, refineDict, hanging, refLevel);
996 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
1000 IOstream::defaultPrecision(10);
1005 else if (!writeMesh)
1007 Info<< "Writing refined mesh to time " << runTime.timeName() << nl
1010 // Write final mesh. (will have been written already if writeMesh=true)
1011 IOstream::defaultPrecision(10);
1017 Info << "End\n" << endl;
1023 // ************************************************************************* //