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 "combineFaces.H"
29 #include "polyTopoChange.H"
30 #include "polyRemoveFace.H"
31 #include "polyAddFace.H"
32 #include "polyModifyFace.H"
33 #include "polyRemovePoint.H"
34 #include "polyAddPoint.H"
35 #include "syncTools.H"
36 #include "meshTools.H"
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 defineTypeNameAndDebug(combineFaces, 0);
47 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
49 // Test face for (almost) convexeness. Allows certain convexity before
51 // For every two consecutive edges calculate the normal. If it differs too
52 // much from the average face normal complain.
53 bool Foam::combineFaces::convexFace
55 const scalar minConcaveCos,
56 const pointField& points,
60 // Get outwards pointing normal of f.
61 vector n = f.normal(points);
64 // Get edge from f[0] to f[size-1];
65 vector ePrev(points[f[0]] - points[f[f.size()-1]]);
66 scalar magEPrev = mag(ePrev);
67 ePrev /= magEPrev + VSMALL;
71 // Get vertex after fp
72 label fp1 = f.fcIndex(fp0);
74 // Normalized vector between two consecutive points
75 vector e10(points[f[fp1]] - points[f[fp0]]);
76 scalar magE10 = mag(e10);
77 e10 /= magE10 + VSMALL;
79 if (magEPrev > SMALL && magE10 > SMALL)
81 vector edgeNormal = ePrev ^ e10;
83 if ((edgeNormal & n) < 0)
85 // Concave. Check angle.
86 if ((ePrev & e10) < minConcaveCos)
97 // Not a single internal angle is concave so face is convex.
102 // Determines if set of faces is valid to collapse into single face.
103 bool Foam::combineFaces::validFace
105 const scalar minConcaveCos,
106 const indirectPrimitivePatch& bigFace
109 // Get outside vertices (in local vertex numbering)
110 const labelListList& edgeLoops = bigFace.edgeLoops();
112 if (edgeLoops.size() > 1)
117 // Check for convexness
118 face f(getOutsideFace(bigFace));
120 return convexFace(minConcaveCos, bigFace.points(), f);
124 void Foam::combineFaces::regioniseFaces
128 const labelList& cEdges,
129 Map<label>& faceRegion
132 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
136 label edgeI = cEdges[i];
139 meshTools::getEdgeFaces(mesh_, cellI, edgeI, f0, f1);
141 label p0 = patches.whichPatch(f0);
142 label p1 = patches.whichPatch(f1);
144 // Face can be merged if
145 // - same non-coupled patch
147 if (p0 != -1 && p0 == p1 && !patches[p0].coupled())
149 vector f0Normal = mesh_.faceAreas()[f0];
150 f0Normal /= mag(f0Normal);
151 vector f1Normal = mesh_.faceAreas()[f1];
152 f1Normal /= mag(f1Normal);
154 if ((f0Normal&f1Normal) > minCos)
156 Map<label>::const_iterator f0Fnd = faceRegion.find(f0);
159 if (f0Fnd != faceRegion.end())
164 Map<label>::const_iterator f1Fnd = faceRegion.find(f1);
167 if (f1Fnd != faceRegion.end())
176 label useRegion = faceRegion.size();
177 faceRegion.insert(f0, useRegion);
178 faceRegion.insert(f1, useRegion);
182 faceRegion.insert(f0, region1);
189 faceRegion.insert(f1, region0);
191 else if (region0 != region1)
193 // Merge the two regions
194 label useRegion = min(region0, region1);
195 label freeRegion = max(region0, region1);
197 forAllIter(Map<label>, faceRegion, iter)
199 if (iter() == freeRegion)
212 bool Foam::combineFaces::faceNeighboursValid
215 const Map<label>& faceRegion
218 if (faceRegion.size() <= 1)
223 const cell& cFaces = mesh_.cells()[cellI];
225 DynamicList<label> storage;
227 // Test for face collapsing to edge since too many neighbours merged.
228 forAll(cFaces, cFaceI)
230 label faceI = cFaces[cFaceI];
232 if (!faceRegion.found(faceI))
234 const labelList& fEdges = mesh_.faceEdges(faceI, storage);
236 // Count number of remaining faces neighbouring faceI. This has
239 // Unregioned neighbouring faces
240 DynamicList<label> neighbourFaces(cFaces.size());
241 // Regioned neighbouring faces
242 labelHashSet neighbourRegions;
246 label edgeI = fEdges[i];
247 label nbrI = meshTools::otherFace(mesh_, cellI, faceI, edgeI);
249 Map<label>::const_iterator iter = faceRegion.find(nbrI);
251 if (iter == faceRegion.end())
253 if (findIndex(neighbourFaces, nbrI) == -1)
255 neighbourFaces.append(nbrI);
260 neighbourRegions.insert(iter());
264 if ((neighbourFaces.size()+neighbourRegions.size()) < 3)
274 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
276 // Construct from mesh
277 Foam::combineFaces::combineFaces
279 const polyMesh& mesh,
286 faceSetsVertices_(0),
287 savedPointLabels_(0),
292 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
294 Foam::labelListList Foam::combineFaces::getMergeSets
296 const scalar featureCos,
297 const scalar minConcaveCos,
298 const labelHashSet& boundaryCells
301 // Lists of faces that can be merged.
302 DynamicList<labelList> allFaceSets(boundaryCells.size() / 10);
303 // Storage for on-the-fly cell-edge addressing.
304 DynamicList<label> storage;
306 // On all cells regionise the faces
307 forAllConstIter(labelHashSet, boundaryCells, iter)
309 label cellI = iter.key();
311 const cell& cFaces = mesh_.cells()[cellI];
313 const labelList& cEdges = mesh_.cellEdges(cellI, storage);
316 Map<label> faceRegion(cFaces.size());
317 regioniseFaces(featureCos, cellI, cEdges, faceRegion);
319 // Now we have in faceRegion for every face the region with planar
320 // face sharing the same region. We now check whether the resulting
322 // - to become a set of edges since too many faces are merged.
323 // - to become convex
325 if (faceNeighboursValid(cellI, faceRegion))
327 // Create region-to-faces addressing
328 Map<labelList> regionToFaces(faceRegion.size());
330 forAllConstIter(Map<label>, faceRegion, iter)
332 label faceI = iter.key();
333 label region = iter();
335 Map<labelList>::iterator regionFnd = regionToFaces.find(region);
337 if (regionFnd != regionToFaces.end())
339 labelList& setFaces = regionFnd();
340 label sz = setFaces.size();
341 setFaces.setSize(sz+1);
342 setFaces[sz] = faceI;
346 regionToFaces.insert(region, labelList(1, faceI));
350 // For every set check if it forms a valid convex face
352 forAllConstIter(Map<labelList>, regionToFaces, iter)
354 // Make face out of setFaces
355 indirectPrimitivePatch bigFace
365 // Only store if -only one outside loop -which forms convex face
366 if (validFace(minConcaveCos, bigFace))
368 allFaceSets.append(iter());
374 return allFaceSets.shrink();
378 Foam::labelListList Foam::combineFaces::getMergeSets
380 const scalar featureCos,
381 const scalar minConcaveCos
384 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
386 // Pick up all cells on boundary
387 labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces());
389 forAll(patches, patchI)
391 const polyPatch& patch = patches[patchI];
393 if (!patch.coupled())
397 boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]);
402 return getMergeSets(featureCos, minConcaveCos, boundaryCells);
406 // Gets outside edgeloop as a face
407 // - in same order as faces
408 // - in mesh vertex labels
409 Foam::face Foam::combineFaces::getOutsideFace
411 const indirectPrimitivePatch& fp
414 if (fp.edgeLoops().size() != 1)
418 "combineFaces::getOutsideFace(const indirectPrimitivePatch&)"
419 ) << "Multiple outside loops:" << fp.edgeLoops()
420 << abort(FatalError);
423 // Get first boundary edge. Since guaranteed one edgeLoop when in here this
424 // edge must be on it.
425 label bEdgeI = fp.nInternalEdges();
427 const edge& e = fp.edges()[bEdgeI];
429 const labelList& eFaces = fp.edgeFaces()[bEdgeI];
431 if (eFaces.size() != 1)
435 "combineFaces::getOutsideFace(const indirectPrimitivePatch&)"
436 ) << "boundary edge:" << bEdgeI
437 << " points:" << fp.meshPoints()[e[0]]
438 << ' ' << fp.meshPoints()[e[1]]
439 << " on indirectPrimitivePatch has " << eFaces.size()
440 << " faces using it" << abort(FatalError);
445 const labelList& outsideLoop = fp.edgeLoops()[0];
448 // Get order of edge e in outside loop
449 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
451 // edgeLoopConsistent : true = edge in same order as outsideloop
452 // false = opposite order
453 bool edgeLoopConsistent = false;
456 label index0 = findIndex(outsideLoop, e[0]);
457 label index1 = findIndex(outsideLoop, e[1]);
459 if (index0 == -1 || index1 == -1)
463 "combineFaces::getOutsideFace"
464 "(const indirectPrimitivePatch&)"
465 ) << "Cannot find boundary edge:" << e
466 << " points:" << fp.meshPoints()[e[0]]
467 << ' ' << fp.meshPoints()[e[1]]
468 << " in edgeLoop:" << outsideLoop << abort(FatalError);
470 else if (index1 == outsideLoop.fcIndex(index0))
472 edgeLoopConsistent = true;
474 else if (index0 == outsideLoop.fcIndex(index1))
476 edgeLoopConsistent = false;
482 "combineFaces::getOutsideFace"
483 "(const indirectPrimitivePatch&)"
484 ) << "Cannot find boundary edge:" << e
485 << " points:" << fp.meshPoints()[e[0]]
486 << ' ' << fp.meshPoints()[e[1]]
487 << " on consecutive points in edgeLoop:"
488 << outsideLoop << abort(FatalError);
493 // Get face in local vertices
494 const face& localF = fp.localFaces()[eFaces[0]];
496 // Get order of edge in localF
497 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
499 // faceEdgeConsistent : true = edge in same order as localF
500 // false = opposite order
501 bool faceEdgeConsistent = false;
504 // Find edge in face.
505 label index = findIndex(fp.faceEdges()[eFaces[0]], bEdgeI);
511 "combineFaces::getOutsideFace"
512 "(const indirectPrimitivePatch&)"
513 ) << "Cannot find boundary edge:" << e
514 << " points:" << fp.meshPoints()[e[0]]
515 << ' ' << fp.meshPoints()[e[1]]
516 << " in face:" << eFaces[0]
517 << " edges:" << fp.faceEdges()[eFaces[0]]
518 << abort(FatalError);
520 else if (localF[index] == e[0] && localF.nextLabel(index) == e[1])
522 faceEdgeConsistent = true;
524 else if (localF[index] == e[1] && localF.nextLabel(index) == e[0])
526 faceEdgeConsistent = false;
532 "combineFaces::getOutsideFace"
533 "(const indirectPrimitivePatch&)"
534 ) << "Cannot find boundary edge:" << e
535 << " points:" << fp.meshPoints()[e[0]]
536 << ' ' << fp.meshPoints()[e[1]]
537 << " in face:" << eFaces[0] << " verts:" << localF
538 << abort(FatalError);
542 // Get face in mesh points.
543 face meshFace(renumber(fp.meshPoints(), outsideLoop));
545 if (faceEdgeConsistent != edgeLoopConsistent)
553 void Foam::combineFaces::setRefinement
555 const labelListList& faceSets,
556 polyTopoChange& meshMod
561 masterFace_.setSize(faceSets.size());
562 faceSetsVertices_.setSize(faceSets.size());
563 forAll(faceSets, setI)
565 const labelList& setFaces = faceSets[setI];
567 masterFace_[setI] = setFaces[0];
568 faceSetsVertices_[setI] = UIndirectList<face>
576 // Running count of number of faces using a point
577 labelList nPointFaces(mesh_.nPoints(), 0);
579 const labelListList& pointFaces = mesh_.pointFaces();
581 forAll(pointFaces, pointI)
583 nPointFaces[pointI] = pointFaces[pointI].size();
586 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
588 forAll(faceSets, setI)
590 const labelList& setFaces = faceSets[setI];
597 label patchI = patches.whichPatch(setFaces[i]);
599 if (patchI == -1 || patches[patchI].coupled())
603 "combineFaces::setRefinement"
604 "(const bool, const labelListList&"
606 ) << "Can only merge non-coupled boundary faces"
607 << " but found internal or coupled face:"
608 << setFaces[i] << " in set " << setI
609 << abort(FatalError);
614 // Make face out of setFaces
615 indirectPrimitivePatch bigFace
625 // Get outside vertices (in local vertex numbering)
626 const labelListList& edgeLoops = bigFace.edgeLoops();
628 if (edgeLoops.size() != 1)
632 "combineFaces::setRefinement"
633 "(const bool, const labelListList&, polyTopoChange&)"
634 ) << "Faces to-be-merged " << setFaces
635 << " do not form a single big face." << nl
636 << abort(FatalError);
640 // Delete all faces except master, whose face gets modified.
642 // Modify master face
643 // ~~~~~~~~~~~~~~~~~~
645 label masterFaceI = setFaces[0];
647 // Get outside face in mesh vertex labels
648 face outsideFace(getOutsideFace(bigFace));
650 label zoneID = mesh_.faceZones().whichZone(masterFaceI);
652 bool zoneFlip = false;
656 const faceZone& fZone = mesh_.faceZones()[zoneID];
658 zoneFlip = fZone.flipMap()[fZone.whichFace(masterFaceI)];
661 label patchI = mesh_.boundaryMesh().whichPatch(masterFaceI);
667 outsideFace, // modified face
668 masterFaceI, // label of face being modified
669 mesh_.faceOwner()[masterFaceI], // owner
672 patchI, // patch for face
673 false, // remove from zone
674 zoneID, // zone for face
675 zoneFlip // face flip in zone
680 // Delete all non-master faces
681 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
683 for (label i = 1; i < setFaces.size(); i++)
685 meshMod.setAction(polyRemoveFace(setFaces[i]));
689 // Mark unused points for deletion
690 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
692 // Remove count of all faces combined
695 const face& f = mesh_.faces()[setFaces[i]];
699 nPointFaces[f[fp]]--;
702 // But keep count on new outside face
703 forAll(outsideFace, fp)
705 nPointFaces[outsideFace[fp]]++;
710 // If one or more processors use the point it needs to be kept.
711 syncTools::syncPointList
717 false // no separation
720 // Remove all unused points. Store position if undoable.
723 forAll(nPointFaces, pointI)
725 if (nPointFaces[pointI] == 0)
727 meshMod.setAction(polyRemovePoint(pointI));
733 // Count removed points
735 forAll(nPointFaces, pointI)
737 if (nPointFaces[pointI] == 0)
743 savedPointLabels_.setSize(n);
744 savedPoints_.setSize(n);
745 Map<label> meshToSaved(2*n);
747 // Remove points and store position
749 forAll(nPointFaces, pointI)
751 if (nPointFaces[pointI] == 0)
753 meshMod.setAction(polyRemovePoint(pointI));
755 savedPointLabels_[n] = pointI;
756 savedPoints_[n] = mesh_.points()[pointI];
758 meshToSaved.insert(pointI, n);
763 // Update stored vertex labels. Negative indices index into local points
764 forAll(faceSetsVertices_, setI)
766 faceList& setFaces = faceSetsVertices_[setI];
770 face& f = setFaces[i];
774 label pointI = f[fp];
776 if (nPointFaces[pointI] == 0)
778 f[fp] = -meshToSaved[pointI]-1;
787 void Foam::combineFaces::updateMesh(const mapPolyMesh& map)
791 // Master face just renumbering of point labels
792 inplaceRenumber(map.reverseFaceMap(), masterFace_);
794 // Stored faces refer to backed-up vertices (not changed)
795 // and normal vertices (need to be renumbered)
796 forAll(faceSetsVertices_, setI)
798 faceList& faces = faceSetsVertices_[setI];
802 // Note: faces[i] can have negative elements.
807 label pointI = f[fp];
811 f[fp] = map.reversePointMap()[pointI];
817 "combineFaces::updateMesh"
818 "(const mapPolyMesh&)"
819 ) << "In set " << setI << " at position " << i
820 << " with master face "
821 << masterFace_[setI] << nl
822 << "the points of the slave face " << faces[i]
823 << " don't exist anymore."
824 << abort(FatalError);
834 // Note that faces on coupled patches are never combined (or at least
835 // getMergeSets never picks them up. Thus the points on them will never get
836 // deleted since still used by the coupled faces. So never a risk of undoing
837 // things (faces/points) on coupled patches.
838 void Foam::combineFaces::setUnrefinement
840 const labelList& masterFaces,
841 polyTopoChange& meshMod,
842 Map<label>& restoredPoints,
843 Map<label>& restoredFaces,
844 Map<label>& restoredCells
851 "combineFaces::setUnrefinement"
852 "(const labelList&, polyTopoChange&"
853 ", Map<label>&, Map<label>&, Map<label>&)"
854 ) << "Can only call setUnrefinement if constructed with"
855 << " unrefinement capability." << exit(FatalError);
860 labelList addedPoints(savedPoints_.size(), -1);
862 // Invert set-to-master-face
863 Map<label> masterToSet(masterFace_.size());
865 forAll(masterFace_, setI)
867 if (masterFace_[setI] >= 0)
869 masterToSet.insert(masterFace_[setI], setI);
873 forAll(masterFaces, i)
875 label masterFaceI = masterFaces[i];
877 Map<label>::const_iterator iter = masterToSet.find(masterFaceI);
879 if (iter == masterToSet.end())
883 "combineFaces::setUnrefinement"
884 "(const labelList&, polyTopoChange&"
885 ", Map<label>&, Map<label>&, Map<label>&)"
886 ) << "Master face " << masterFaceI
887 << " is not the master of one of the merge sets"
888 << " or has already been merged"
889 << abort(FatalError);
895 // Update faces of the merge setI for reintroduced vertices
896 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
898 faceList& faces = faceSetsVertices_[setI];
904 "combineFaces::setUnrefinement"
905 "(const labelList&, polyTopoChange&"
906 ", Map<label>&, Map<label>&, Map<label>&)"
907 ) << "Set " << setI << " with master face " << masterFaceI
908 << " has already been merged." << abort(FatalError);
917 label pointI = f[fp];
921 label localI = -pointI-1;
923 if (addedPoints[localI] == -1)
925 // First occurrence of saved point. Reintroduce point
926 addedPoints[localI] = meshMod.setAction
930 savedPoints_[localI], // point
932 -1, // zone for point
933 true // supports a cell
936 restoredPoints.insert
938 addedPoints[localI], // current point label
939 savedPointLabels_[localI] // point label when it
943 f[fp] = addedPoints[localI];
952 label own = mesh_.faceOwner()[masterFaceI];
953 label zoneID = mesh_.faceZones().whichZone(masterFaceI);
954 bool zoneFlip = false;
957 const faceZone& fZone = mesh_.faceZones()[zoneID];
958 zoneFlip = fZone.flipMap()[fZone.whichFace(masterFaceI)];
960 label patchI = mesh_.boundaryMesh().whichPatch(masterFaceI);
962 if (mesh_.boundaryMesh()[patchI].coupled())
966 "combineFaces::setUnrefinement"
967 "(const labelList&, polyTopoChange&"
968 ", Map<label>&, Map<label>&, Map<label>&)"
969 ) << "Master face " << masterFaceI << " is on coupled patch "
970 << mesh_.boundaryMesh()[patchI].name()
971 << abort(FatalError);
974 //Pout<< "Restoring new master face " << masterFaceI
975 // << " to vertices " << faces[0] << endl;
977 // Modify the master face.
982 faces[0], // original face
983 masterFaceI, // label of face
987 patchI, // patch for face
988 false, // remove from zone
989 zoneID, // zone for face
990 zoneFlip // face flip in zone
994 // Add the previously removed faces
995 for (label i = 1; i < faces.size(); i++)
997 //Pout<< "Restoring removed face with vertices " << faces[i]
1004 faces[i], // vertices
1007 -1, // masterPointID,
1008 -1, // masterEdgeID,
1009 masterFaceI, // masterFaceID,
1010 false, // flipFaceFlux,
1013 zoneFlip // zoneFlip
1018 // Clear out restored set
1019 faceSetsVertices_[setI].clear();
1020 masterFace_[setI] = -1;
1025 // ************************************************************************* //