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 "cellClassification.H"
27 #include "triSurfaceSearch.H"
28 #include "indexedOctree.H"
29 #include "treeDataFace.H"
30 #include "meshSearch.H"
35 #include "meshTools.H"
37 #include "globalMeshData.H"
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 defineTypeNameAndDebug(Foam::cellClassification, 0);
44 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
46 Foam::label Foam::cellClassification::count
48 const labelList& elems,
56 if (elems[elemI] == elem)
66 // Mark all faces that are cut by the surface. Two pass:
67 // Pass1: mark all mesh edges that intersect surface (quick since triangle
69 // Pass2: Check for all point neighbours of these faces whether any of their
71 Foam::boolList Foam::cellClassification::markFaces
73 const triSurfaceSearch& search
78 boolList cutFace(mesh_.nFaces(), false);
82 // Intersect mesh edges with surface (is fast) and mark all faces that
85 forAll(mesh_.edges(), edgeI)
87 if (debug && (edgeI % 10000 == 0))
89 Pout<< "Intersecting mesh edge " << edgeI << " with surface"
93 const edge& e = mesh_.edges()[edgeI];
95 const point& p0 = mesh_.points()[e.start()];
96 const point& p1 = mesh_.points()[e.end()];
98 pointIndexHit pHit(search.tree().findLineAny(p0, p1));
102 const labelList& myFaces = mesh_.edgeFaces()[edgeI];
104 forAll(myFaces, myFaceI)
106 label faceI = myFaces[myFaceI];
110 cutFace[faceI] = true;
120 Pout<< "Intersected edges of mesh with surface in = "
121 << timer.cpuTimeIncrement() << " s\n" << endl << endl;
125 // Construct octree on faces that have not yet been marked as cut
128 labelList allFaces(mesh_.nFaces() - nCutFaces);
132 forAll(cutFace, faceI)
136 allFaces[allFaceI++] = faceI;
142 Pout<< "Testing " << allFaceI << " faces for piercing by surface"
146 treeBoundBox allBb(mesh_.points());
148 // Extend domain slightly (also makes it 3D if was 2D)
149 scalar tol = 1E-6 * allBb.avgDim();
151 point& bbMin = allBb.min();
156 point& bbMax = allBb.max();
161 indexedOctree<treeDataFace> faceTree
163 treeDataFace(false, mesh_, allFaces),
164 allBb, // overall search domain
170 const triSurface& surf = search.surface();
171 const edgeList& edges = surf.edges();
172 const pointField& localPoints = surf.localPoints();
178 if (debug && (edgeI % 10000 == 0))
180 Pout<< "Intersecting surface edge " << edgeI
181 << " with mesh faces" << endl;
183 const edge& e = edges[edgeI];
185 const point& start = localPoints[e.start()];
186 const point& end = localPoints[e.end()];
188 vector edgeNormal(end - start);
189 const scalar edgeMag = mag(edgeNormal);
190 const vector smallVec = 1E-9*edgeNormal;
192 edgeNormal /= edgeMag+VSMALL;
194 // Current start of pierce test
199 pointIndexHit pHit(faceTree.findLine(pt, end));
207 label faceI = faceTree.shapes().faceLabels()[pHit.index()];
211 cutFace[faceI] = true;
216 // Restart from previous endpoint
217 pt = pHit.hitPoint() + smallVec;
219 if (((pt-start) & edgeNormal) >= edgeMag)
229 Pout<< "Detected an additional " << nAddFaces << " faces cut"
232 Pout<< "Intersected edges of surface with mesh faces in = "
233 << timer.cpuTimeIncrement() << " s\n" << endl << endl;
240 // Determine faces cut by surface and use to divide cells into types. See
241 // cellInfo. All cells reachable from outsidePts are considered to be of type
243 void Foam::cellClassification::markCells
245 const meshSearch& queryMesh,
246 const boolList& piercedFace,
247 const pointField& outsidePts
250 // Use meshwave to partition mesh, starting from outsidePt
253 // Construct null; sets type to NOTSET
254 List<cellInfo> cellInfoList(mesh_.nCells());
256 // Mark cut cells first
257 forAll(piercedFace, faceI)
259 if (piercedFace[faceI])
261 cellInfoList[mesh_.faceOwner()[faceI]] =
262 cellInfo(cellClassification::CUT);
264 if (mesh_.isInternalFace(faceI))
266 cellInfoList[mesh_.faceNeighbour()[faceI]] =
267 cellInfo(cellClassification::CUT);
273 // Mark cells containing outside points as being outside
276 // Coarse guess number of faces
277 labelHashSet outsideFacesMap(outsidePts.size() * 6 * 2);
279 forAll(outsidePts, outsidePtI)
281 // Use linear search for points.
282 label cellI = queryMesh.findCell(outsidePts[outsidePtI], -1, false);
284 if (returnReduce(cellI, maxOp<label>()) == -1)
288 "List<cellClassification::cType> markCells"
289 "(const meshSearch&, const boolList&, const pointField&)"
290 ) << "outsidePoint " << outsidePts[outsidePtI]
291 << " is not inside any cell"
292 << nl << "It might be on a face or outside the geometry"
298 cellInfoList[cellI] = cellInfo(cellClassification::OUTSIDE);
300 // Mark faces of cellI
301 const labelList& myFaces = mesh_.cells()[cellI];
302 forAll(myFaces, myFaceI)
304 outsideFacesMap.insert(myFaces[myFaceI]);
311 // Mark faces to start wave from
314 labelList changedFaces(outsideFacesMap.toc());
316 List<cellInfo> changedFacesInfo
319 cellInfo(cellClassification::OUTSIDE)
322 MeshWave<cellInfo> cellInfoCalc
325 changedFaces, // Labels of changed faces
326 changedFacesInfo, // Information on changed faces
327 cellInfoList, // Information on all cells
328 mesh_.globalData().nTotalCells()+1 // max iterations
331 // Get information out of cellInfoList
332 const List<cellInfo>& allInfo = cellInfoCalc.allCellInfo();
334 forAll(allInfo, cellI)
336 label t = allInfo[cellI].type();
338 if (t == cellClassification::NOTSET)
340 t = cellClassification::INSIDE;
342 operator[](cellI) = t;
347 void Foam::cellClassification::classifyPoints
349 const label meshType,
350 const labelList& cellType,
351 List<pointStatus>& pointSide
354 pointSide.setSize(mesh_.nPoints());
356 forAll(mesh_.pointCells(), pointI)
358 const labelList& pCells = mesh_.pointCells()[pointI];
360 pointSide[pointI] = UNSET;
364 label type = cellType[pCells[i]];
366 if (type == meshType)
368 if (pointSide[pointI] == UNSET)
370 pointSide[pointI] = MESH;
372 else if (pointSide[pointI] == NONMESH)
374 pointSide[pointI] = MIXED;
381 if (pointSide[pointI] == UNSET)
383 pointSide[pointI] = NONMESH;
385 else if (pointSide[pointI] == MESH)
387 pointSide[pointI] = MIXED;
397 bool Foam::cellClassification::usesMixedPointsOnly
399 const List<pointStatus>& pointSide,
403 const faceList& faces = mesh_.faces();
405 const cell& cFaces = mesh_.cells()[cellI];
407 forAll(cFaces, cFaceI)
409 const face& f = faces[cFaces[cFaceI]];
413 if (pointSide[f[fp]] != MIXED)
420 // All points are mixed.
425 void Foam::cellClassification::getMeshOutside
427 const label meshType,
428 faceList& outsideFaces,
429 labelList& outsideOwner
432 const labelList& own = mesh_.faceOwner();
433 const labelList& nbr = mesh_.faceNeighbour();
434 const faceList& faces = mesh_.faces();
436 outsideFaces.setSize(mesh_.nFaces());
437 outsideOwner.setSize(mesh_.nFaces());
440 // Get faces on interface between meshType and non-meshType
442 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
444 label ownType = operator[](own[faceI]);
445 label nbrType = operator[](nbr[faceI]);
447 if (ownType == meshType && nbrType != meshType)
449 outsideFaces[outsideI] = faces[faceI];
450 outsideOwner[outsideI] = own[faceI]; // meshType cell
453 else if (ownType != meshType && nbrType == meshType)
455 outsideFaces[outsideI] = faces[faceI];
456 outsideOwner[outsideI] = nbr[faceI]; // meshType cell
461 // Get faces on outside of real mesh with cells of meshType.
463 for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
465 if (operator[](own[faceI]) == meshType)
467 outsideFaces[outsideI] = faces[faceI];
468 outsideOwner[outsideI] = own[faceI]; // meshType cell
472 outsideFaces.setSize(outsideI);
473 outsideOwner.setSize(outsideI);
477 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
479 // Construct from mesh and surface and point(s) on outside
480 Foam::cellClassification::cellClassification
482 const polyMesh& mesh,
483 const meshSearch& meshQuery,
484 const triSurfaceSearch& surfQuery,
485 const pointField& outsidePoints
488 labelList(mesh.nCells(), cellClassification::NOTSET),
494 markFaces(surfQuery),
500 // Construct from mesh and meshType.
501 Foam::cellClassification::cellClassification
503 const polyMesh& mesh,
504 const labelList& cellType
510 if (mesh_.nCells() != size())
514 "cellClassification::cellClassification"
515 "(const polyMesh&, const labelList&)"
516 ) << "Number of elements of cellType argument is not equal to the"
517 << " number of cells" << abort(FatalError);
523 Foam::cellClassification::cellClassification(const cellClassification& cType)
530 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
533 // Makes cutCells further than nLayers away from meshType to
534 // fillType. Returns number of cells changed.
535 Foam::label Foam::cellClassification::trimCutCells
538 const label meshType,
542 // Temporary cell type for growing.
543 labelList newCellType(*this);
545 // // Split types into outside and rest
546 // forAll(*this, cellI)
548 // label type = operator[](cellI);
550 // if (type == meshType)
552 // newCellType[cellI] = type;
556 // newCellType[cellI] = fillType;
563 // Do point-cell-point walk on newCellType out from meshType cells
564 for (label iter = 0; iter < nLayers; iter++)
566 // Get status of points: visible from meshType (pointSide == MESH)
567 // or fillType/cutCells cells (pointSide == NONMESH) or
568 // both (pointSide == MIXED)
569 List<pointStatus> pointSide(mesh_.nPoints());
570 classifyPoints(meshType, newCellType, pointSide);
572 // Grow layer of outside cells
573 forAll(pointSide, pointI)
575 if (pointSide[pointI] == MIXED)
578 const labelList& pCells = mesh_.pointCells()[pointI];
582 label type = newCellType[pCells[i]];
584 if (type == cellClassification::CUT)
586 // Found cut cell sharing point with
587 // mesh type cell. Grow.
588 newCellType[pCells[i]] = meshType;
595 // Merge newCellType into *this:
596 // - leave meshType cells intact
597 // - leave nonMesh cells intact
598 // - make cutcells fillType if they were not marked in newCellType
602 forAll(newCellType, cellI)
604 if (operator[](cellI) == cellClassification::CUT)
606 if (newCellType[cellI] != meshType)
608 // Cell was cutCell but further than nLayers away from
609 // meshType. Convert to fillType.
610 operator[](cellI) = fillType;
620 // Grow surface by pointNeighbours of type meshType
621 Foam::label Foam::cellClassification::growSurface
623 const label meshType,
627 boolList hasMeshType(mesh_.nPoints(), false);
629 // Mark points used by meshType cells
631 forAll(mesh_.pointCells(), pointI)
633 const labelList& myCells = mesh_.pointCells()[pointI];
635 // Check if one of cells has meshType
636 forAll(myCells, myCellI)
638 label type = operator[](myCells[myCellI]);
640 if (type == meshType)
642 hasMeshType[pointI] = true;
649 // Change neighbours of marked points
653 forAll(hasMeshType, pointI)
655 if (hasMeshType[pointI])
657 const labelList& myCells = mesh_.pointCells()[pointI];
659 forAll(myCells, myCellI)
661 if (operator[](myCells[myCellI]) != meshType)
663 operator[](myCells[myCellI]) = fillType;
674 // Check all points used by cells of meshType for use of at least one point
675 // which is not on the outside. If all points are on the outside of the mesh
676 // this would probably result in a flattened cell when projecting the cell
678 Foam::label Foam::cellClassification::fillHangingCells
680 const label meshType,
681 const label fillType,
685 label nTotChanged = 0;
687 for (label iter = 0; iter < maxIter; iter++)
691 // Get status of points: visible from meshType or non-meshType cells
692 List<pointStatus> pointSide(mesh_.nPoints());
693 classifyPoints(meshType, *this, pointSide);
695 // Check all cells using mixed point type for whether they use mixed
696 // points only. Note: could probably speed this up by counting number
697 // of mixed verts per face and mixed faces per cell or something?
698 forAll(pointSide, pointI)
700 if (pointSide[pointI] == MIXED)
702 const labelList& pCells = mesh_.pointCells()[pointI];
706 label cellI = pCells[i];
708 if (operator[](cellI) == meshType)
710 if (usesMixedPointsOnly(pointSide, cellI))
712 operator[](cellI) = fillType;
720 nTotChanged += nChanged;
722 Pout<< "removeHangingCells : changed " << nChanged
723 << " hanging cells" << endl;
735 Foam::label Foam::cellClassification::fillRegionEdges
737 const label meshType,
738 const label fillType,
742 label nTotChanged = 0;
744 for (label iter = 0; iter < maxIter; iter++)
746 // Get interface between meshType cells and non-meshType cells as a list
747 // of faces and for each face the cell which is the meshType.
748 faceList outsideFaces;
749 labelList outsideOwner;
751 getMeshOutside(meshType, outsideFaces, outsideOwner);
753 // Build primitivePatch out of it and check it for problems.
754 primitiveFacePatch fp(outsideFaces, mesh_.points());
756 const labelListList& edgeFaces = fp.edgeFaces();
760 // Check all edgeFaces for non-manifoldness
762 forAll(edgeFaces, edgeI)
764 const labelList& eFaces = edgeFaces[edgeI];
766 if (eFaces.size() > 2)
768 // patch connected through pinched edge. Remove first face using
769 // edge (and not yet changed)
773 label patchFaceI = eFaces[i];
775 label ownerCell = outsideOwner[patchFaceI];
777 if (operator[](ownerCell) == meshType)
779 operator[](ownerCell) = fillType;
789 nTotChanged += nChanged;
791 Pout<< "fillRegionEdges : changed " << nChanged
792 << " cells using multiply connected edges" << endl;
804 Foam::label Foam::cellClassification::fillRegionPoints
806 const label meshType,
807 const label fillType,
811 label nTotChanged = 0;
813 for (label iter = 0; iter < maxIter; iter++)
815 // Get interface between meshType cells and non-meshType cells as a list
816 // of faces and for each face the cell which is the meshType.
817 faceList outsideFaces;
818 labelList outsideOwner;
820 getMeshOutside(meshType, outsideFaces, outsideOwner);
822 // Build primitivePatch out of it and check it for problems.
823 primitiveFacePatch fp(outsideFaces, mesh_.points());
825 labelHashSet nonManifoldPoints;
827 // Check for non-manifold points.
828 fp.checkPointManifold(false, &nonManifoldPoints);
830 const Map<label>& meshPointMap = fp.meshPointMap();
834 forAllConstIter(labelHashSet, nonManifoldPoints, iter)
836 // Find a face on fp using point and remove it.
837 const label patchPointI = meshPointMap[iter.key()];
839 const labelList& pFaces = fp.pointFaces()[patchPointI];
841 // Remove any face using conflicting point. Does first face which
842 // has not yet been done. Could be more intelligent and decide which
843 // one would be best to remove.
846 const label patchFaceI = pFaces[i];
847 const label ownerCell = outsideOwner[patchFaceI];
849 if (operator[](ownerCell) == meshType)
851 operator[](ownerCell) = fillType;
859 nTotChanged += nChanged;
861 Pout<< "fillRegionPoints : changed " << nChanged
862 << " cells using multiply connected points" << endl;
874 void Foam::cellClassification::writeStats(Ostream& os) const
876 os << "Cells:" << size() << endl
877 << " notset : " << count(*this, NOTSET) << endl
878 << " cut : " << count(*this, CUT) << endl
879 << " inside : " << count(*this, INSIDE) << endl
880 << " outside : " << count(*this, OUTSIDE) << endl;
884 // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
886 void Foam::cellClassification::operator=(const Foam::cellClassification& rhs)
888 labelList::operator=(rhs);
892 // ************************************************************************* //