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 "isoSurface.H"
28 #include "mergePoints.H"
29 #include "addToRunTimeSelectionTable.H"
30 #include "slicedVolFields.H"
31 #include "volFields.H"
32 #include "surfaceFields.H"
34 #include "meshTools.H"
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
38 defineTypeNameAndDebug(Foam::isoSurface, 0);
41 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
43 bool Foam::isoSurface::noTransform(const tensor& tt) const
46 (mag(tt.xx()-1) < mergeDistance_)
47 && (mag(tt.yy()-1) < mergeDistance_)
48 && (mag(tt.zz()-1) < mergeDistance_)
49 && (mag(tt.xy()) < mergeDistance_)
50 && (mag(tt.xz()) < mergeDistance_)
51 && (mag(tt.yx()) < mergeDistance_)
52 && (mag(tt.yz()) < mergeDistance_)
53 && (mag(tt.zx()) < mergeDistance_)
54 && (mag(tt.zy()) < mergeDistance_);
58 // Calculates per face whether couple is collocated.
59 bool Foam::isoSurface::collocatedPatch(const polyPatch& pp)
61 const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>(pp);
62 return cpp.parallel() && !cpp.separated();
66 // Calculates per face whether couple is collocated.
67 Foam::PackedBoolList Foam::isoSurface::collocatedFaces
69 const coupledPolyPatch& pp
72 // Initialise to false
73 PackedBoolList collocated(pp.size());
75 if (isA<processorPolyPatch>(pp) && collocatedPatch(pp))
82 else if (isA<cyclicPolyPatch>(pp) && collocatedPatch(pp))
93 "isoSurface::collocatedFaces(const coupledPolyPatch&) const"
94 ) << "Unhandled coupledPolyPatch type " << pp.type()
101 void Foam::isoSurface::syncUnseparatedPoints
103 pointField& pointValues,
104 const point& nullValue
107 // Until syncPointList handles separated coupled patches with multiple
108 // transforms do our own synchronisation of non-separated patches only
109 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
111 if (Pstream::parRun())
114 forAll(patches, patchI)
118 isA<processorPolyPatch>(patches[patchI])
119 && patches[patchI].nPoints() > 0
120 && collocatedPatch(patches[patchI])
123 const processorPolyPatch& pp =
124 refCast<const processorPolyPatch>(patches[patchI]);
126 const labelList& meshPts = pp.meshPoints();
127 const labelList& nbrPts = pp.neighbPoints();
129 pointField patchInfo(meshPts.size());
131 forAll(nbrPts, pointI)
133 label nbrPointI = nbrPts[pointI];
134 patchInfo[nbrPointI] = pointValues[meshPts[pointI]];
137 OPstream toNbr(Pstream::blocking, pp.neighbProcNo());
142 // Receive and combine.
144 forAll(patches, patchI)
148 isA<processorPolyPatch>(patches[patchI])
149 && patches[patchI].nPoints() > 0
150 && collocatedPatch(patches[patchI])
153 const processorPolyPatch& pp =
154 refCast<const processorPolyPatch>(patches[patchI]);
156 pointField nbrPatchInfo(pp.nPoints());
158 // We do not know the number of points on the other side
159 // so cannot use Pstream::read.
160 IPstream fromNbr(Pstream::blocking, pp.neighbProcNo());
161 fromNbr >> nbrPatchInfo;
164 const labelList& meshPts = pp.meshPoints();
166 forAll(meshPts, pointI)
168 label meshPointI = meshPts[pointI];
171 pointValues[meshPointI],
180 forAll(patches, patchI)
182 if (isA<cyclicPolyPatch>(patches[patchI]))
184 const cyclicPolyPatch& cycPatch =
185 refCast<const cyclicPolyPatch>(patches[patchI]);
187 if (cycPatch.owner() && collocatedPatch(cycPatch))
191 const edgeList& coupledPoints = cycPatch.coupledPoints();
192 const labelList& meshPts = cycPatch.meshPoints();
193 const cyclicPolyPatch& nbrPatch = cycPatch.neighbPatch();
194 const labelList& nbrMeshPoints = nbrPatch.meshPoints();
196 pointField half0Values(coupledPoints.size());
197 pointField half1Values(coupledPoints.size());
199 forAll(coupledPoints, i)
201 const edge& e = coupledPoints[i];
202 half0Values[i] = pointValues[meshPts[e[0]]];
203 half1Values[i] = pointValues[nbrMeshPoints[e[1]]];
206 forAll(coupledPoints, i)
208 const edge& e = coupledPoints[i];
209 label p0 = meshPts[e[0]];
210 label p1 = nbrMeshPoints[e[1]];
212 minEqOp<point>()(pointValues[p0], half1Values[i]);
213 minEqOp<point>()(pointValues[p1], half0Values[i]);
219 // Synchronize multiple shared points.
220 const globalMeshData& pd = mesh_.globalData();
222 if (pd.nGlobalPoints() > 0)
224 // Values on shared points.
225 pointField sharedPts(pd.nGlobalPoints(), nullValue);
227 forAll(pd.sharedPointLabels(), i)
229 label meshPointI = pd.sharedPointLabels()[i];
230 // Fill my entries in the shared points
231 sharedPts[pd.sharedPointAddr()[i]] = pointValues[meshPointI];
234 // Combine on master.
235 Pstream::listCombineGather(sharedPts, minEqOp<point>());
236 Pstream::listCombineScatter(sharedPts);
238 // Now we will all have the same information. Merge it back with
239 // my local information.
240 forAll(pd.sharedPointLabels(), i)
242 label meshPointI = pd.sharedPointLabels()[i];
243 pointValues[meshPointI] = sharedPts[pd.sharedPointAddr()[i]];
249 Foam::scalar Foam::isoSurface::isoFraction
268 bool Foam::isoSurface::isEdgeOfFaceCut
270 const scalarField& pVals,
278 bool fpLower = (pVals[f[fp]] < iso_);
281 (fpLower != ownLower)
282 || (fpLower != neiLower)
283 || (fpLower != (pVals[f[f.fcIndex(fp)]] < iso_))
293 // Get neighbour value and position.
294 void Foam::isoSurface::getNeighbour
296 const labelList& boundaryRegion,
297 const volVectorField& meshC,
298 const volScalarField& cVals,
305 const labelList& own = mesh_.faceOwner();
306 const labelList& nei = mesh_.faceNeighbour();
308 if (mesh_.isInternalFace(faceI))
310 label nbr = (own[faceI] == cellI ? nei[faceI] : own[faceI]);
311 nbrValue = cVals[nbr];
312 nbrPoint = meshC[nbr];
316 label bFaceI = faceI-mesh_.nInternalFaces();
317 label patchI = boundaryRegion[bFaceI];
318 const polyPatch& pp = mesh_.boundaryMesh()[patchI];
319 label patchFaceI = faceI-pp.start();
321 nbrValue = cVals.boundaryField()[patchI][patchFaceI];
322 nbrPoint = meshC.boundaryField()[patchI][patchFaceI];
327 // Determine for every face/cell whether it (possibly) generates triangles.
328 void Foam::isoSurface::calcCutTypes
330 const labelList& boundaryRegion,
331 const volVectorField& meshC,
332 const volScalarField& cVals,
333 const scalarField& pVals
336 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
337 const labelList& own = mesh_.faceOwner();
338 const labelList& nei = mesh_.faceNeighbour();
340 faceCutType_.setSize(mesh_.nFaces());
341 faceCutType_ = NOTCUT;
343 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
346 bool ownLower = (cVals[own[faceI]] < iso_);
361 bool neiLower = (nbrValue < iso_);
363 if (ownLower != neiLower)
365 faceCutType_[faceI] = CUT;
369 // See if any mesh edge is cut by looping over all the edges of the
371 const face f = mesh_.faces()[faceI];
373 if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower))
375 faceCutType_[faceI] = CUT;
380 forAll(patches, patchI)
382 const polyPatch& pp = patches[patchI];
384 label faceI = pp.start();
388 bool ownLower = (cVals[own[faceI]] < iso_);
403 bool neiLower = (nbrValue < iso_);
405 if (ownLower != neiLower)
407 faceCutType_[faceI] = CUT;
412 const face f = mesh_.faces()[faceI];
414 if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower))
416 faceCutType_[faceI] = CUT;
427 cellCutType_.setSize(mesh_.nCells());
428 cellCutType_ = NOTCUT;
430 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
432 if (faceCutType_[faceI] != NOTCUT)
434 if (cellCutType_[own[faceI]] == NOTCUT)
436 cellCutType_[own[faceI]] = CUT;
439 if (cellCutType_[nei[faceI]] == NOTCUT)
441 cellCutType_[nei[faceI]] = CUT;
446 for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
448 if (faceCutType_[faceI] != NOTCUT)
450 if (cellCutType_[own[faceI]] == NOTCUT)
452 cellCutType_[own[faceI]] = CUT;
460 Pout<< "isoSurface : detected " << nCutCells_
461 << " candidate cut cells (out of " << mesh_.nCells()
467 // Return the two common points between two triangles
468 Foam::labelPair Foam::isoSurface::findCommonPoints
470 const labelledTri& tri0,
471 const labelledTri& tri1
474 labelPair common(-1, -1);
477 label fp1 = findIndex(tri1, tri0[fp0]);
482 fp1 = findIndex(tri1, tri0[fp0]);
487 // So tri0[fp0] is tri1[fp1]
489 // Find next common point
490 label fp0p1 = tri0.fcIndex(fp0);
491 label fp1p1 = tri1.fcIndex(fp1);
492 label fp1m1 = tri1.rcIndex(fp1);
494 if (tri0[fp0p1] == tri1[fp1p1] || tri0[fp0p1] == tri1[fp1m1])
496 common[0] = tri0[fp0];
497 common[1] = tri0[fp0p1];
504 // Caculate centre of surface.
505 Foam::point Foam::isoSurface::calcCentre(const triSurface& s)
507 vector sum = vector::zero;
511 sum += s[i].centre(s.points());
517 // Replace surface (localPoints, localTris) with single point. Returns
518 // point. Destructs arguments.
519 Foam::pointIndexHit Foam::isoSurface::collapseSurface
521 pointField& localPoints,
522 DynamicList<labelledTri, 64>& localTris
525 pointIndexHit info(false, vector::zero, localTris.size());
527 if (localTris.size() == 1)
529 const labelledTri& tri = localTris[0];
530 info.setPoint(tri.centre(localPoints));
533 else if (localTris.size() == 2)
535 // Check if the two triangles share an edge.
536 const labelledTri& tri0 = localTris[0];
537 const labelledTri& tri1 = localTris[0];
539 labelPair shared = findCommonPoints(tri0, tri1);
547 tri0.centre(localPoints)
548 + tri1.centre(localPoints)
554 else if (localTris.size())
556 // Check if single region. Rare situation.
560 geometricSurfacePatchList(0),
564 localTris.clearStorage();
567 label nZones = surf.markZones
569 boolList(surf.nEdges(), false),
575 info.setPoint(calcCentre(surf));
584 // Determine per cell centre whether all the intersections get collapsed
586 void Foam::isoSurface::calcSnappedCc
588 const labelList& boundaryRegion,
589 const volVectorField& meshC,
590 const volScalarField& cVals,
591 const scalarField& pVals,
593 DynamicList<point>& snappedPoints,
597 const pointField& pts = mesh_.points();
598 const pointField& cc = mesh_.cellCentres();
600 snappedCc.setSize(mesh_.nCells());
604 DynamicList<point, 64> localTriPoints(64);
606 forAll(mesh_.cells(), cellI)
608 if (cellCutType_[cellI] == CUT)
610 scalar cVal = cVals[cellI];
612 const cell& cFaces = mesh_.cells()[cellI];
614 localTriPoints.clear();
616 point otherPointSum = vector::zero;
618 // Create points for all intersections close to cell centre
619 // (i.e. from pyramid edges)
621 forAll(cFaces, cFaceI)
623 label faceI = cFaces[cFaceI];
638 // Calculate intersection points of edges to cell centre
639 FixedList<scalar, 3> s;
640 FixedList<point, 3> pt;
642 // From cc to neighbour cc.
643 s[2] = isoFraction(cVal, nbrValue);
644 pt[2] = (1.0-s[2])*cc[cellI] + s[2]*nbrPoint;
646 const face& f = mesh_.faces()[cFaces[cFaceI]];
652 s[0] = isoFraction(cVal, pVals[p0]);
653 pt[0] = (1.0-s[0])*cc[cellI] + s[0]*pts[p0];
656 label p1 = f[f.fcIndex(fp)];
657 s[1] = isoFraction(cVal, pVals[p1]);
658 pt[1] = (1.0-s[1])*cc[cellI] + s[1]*pts[p1];
662 (s[0] >= 0.0 && s[0] <= 0.5)
663 && (s[1] >= 0.0 && s[1] <= 0.5)
664 && (s[2] >= 0.0 && s[2] <= 0.5)
667 localTriPoints.append(pt[0]);
668 localTriPoints.append(pt[1]);
669 localTriPoints.append(pt[2]);
673 // Get average of all other points
676 if (s[i] >= 0.0 && s[i] <= 0.5)
678 otherPointSum += pt[i];
686 if (localTriPoints.size() == 0)
688 // No complete triangles. Get average of all intersection
692 snappedCc[cellI] = snappedPoints.size();
693 snappedPoints.append(otherPointSum/nOther);
695 //Pout<< " point:" << pointI
696 // << " replacing coord:" << mesh_.points()[pointI]
697 // << " by average:" << collapsedPoint[pointI] << endl;
700 else if (localTriPoints.size() == 3)
702 // Single triangle. No need for any analysis. Average points.
704 points.transfer(localTriPoints);
705 snappedCc[cellI] = snappedPoints.size();
706 snappedPoints.append(sum(points)/points.size());
708 //Pout<< " point:" << pointI
709 // << " replacing coord:" << mesh_.points()[pointI]
710 // << " by average:" << collapsedPoint[pointI] << endl;
714 // Convert points into triSurface.
716 // Merge points and compact out non-valid triangles
717 labelList triMap; // merged to unmerged triangle
718 labelList triPointReverseMap; // unmerged to merged point
723 false, // do not check for duplicate tris
731 label nZones = surf.markZones
733 boolList(surf.nEdges(), false),
739 snappedCc[cellI] = snappedPoints.size();
740 snappedPoints.append(calcCentre(surf));
741 //Pout<< " point:" << pointI << " nZones:" << nZones
742 // << " replacing coord:" << mesh_.points()[pointI]
743 // << " by average:" << collapsedPoint[pointI] << endl;
751 // Determine per meshpoint whether all the intersections get collapsed
753 void Foam::isoSurface::calcSnappedPoint
755 const PackedBoolList& isBoundaryPoint,
756 const labelList& boundaryRegion,
757 const volVectorField& meshC,
758 const volScalarField& cVals,
759 const scalarField& pVals,
761 DynamicList<point>& snappedPoints,
762 labelList& snappedPoint
765 const pointField& pts = mesh_.points();
766 const pointField& cc = mesh_.cellCentres();
768 pointField collapsedPoint(mesh_.nPoints(), point::max);
772 DynamicList<point, 64> localTriPoints(100);
774 forAll(mesh_.pointFaces(), pointI)
776 if (isBoundaryPoint.get(pointI) == 1)
781 const labelList& pFaces = mesh_.pointFaces()[pointI];
787 label faceI = pFaces[i];
789 if (faceCutType_[faceI] == CUT)
802 localTriPoints.clear();
804 point otherPointSum = vector::zero;
806 forAll(pFaces, pFaceI)
808 // Create points for all intersections close to point
809 // (i.e. from pyramid edges)
811 label faceI = pFaces[pFaceI];
812 const face& f = mesh_.faces()[faceI];
813 label own = mesh_.faceOwner()[faceI];
815 // Get neighbour value
829 // Calculate intersection points of edges emanating from point
830 FixedList<scalar, 4> s;
831 FixedList<point, 4> pt;
833 label fp = findIndex(f, pointI);
834 s[0] = isoFraction(pVals[pointI], cVals[own]);
835 pt[0] = (1.0-s[0])*pts[pointI] + s[0]*cc[own];
837 s[1] = isoFraction(pVals[pointI], nbrValue);
838 pt[1] = (1.0-s[1])*pts[pointI] + s[1]*nbrPoint;
840 label nextPointI = f[f.fcIndex(fp)];
841 s[2] = isoFraction(pVals[pointI], pVals[nextPointI]);
842 pt[2] = (1.0-s[2])*pts[pointI] + s[2]*pts[nextPointI];
844 label prevPointI = f[f.rcIndex(fp)];
845 s[3] = isoFraction(pVals[pointI], pVals[prevPointI]);
846 pt[3] = (1.0-s[3])*pts[pointI] + s[3]*pts[prevPointI];
850 (s[0] >= 0.0 && s[0] <= 0.5)
851 && (s[1] >= 0.0 && s[1] <= 0.5)
852 && (s[2] >= 0.0 && s[2] <= 0.5)
855 localTriPoints.append(pt[0]);
856 localTriPoints.append(pt[1]);
857 localTriPoints.append(pt[2]);
861 (s[0] >= 0.0 && s[0] <= 0.5)
862 && (s[1] >= 0.0 && s[1] <= 0.5)
863 && (s[3] >= 0.0 && s[3] <= 0.5)
866 localTriPoints.append(pt[3]);
867 localTriPoints.append(pt[0]);
868 localTriPoints.append(pt[1]);
871 // Get average of points as fallback
874 if (s[i] >= 0.0 && s[i] <= 0.5)
876 otherPointSum += pt[i];
882 if (localTriPoints.size() == 0)
884 // No complete triangles. Get average of all intersection
888 collapsedPoint[pointI] = otherPointSum/nOther;
891 else if (localTriPoints.size() == 3)
893 // Single triangle. No need for any analysis. Average points.
895 points.transfer(localTriPoints);
896 collapsedPoint[pointI] = sum(points)/points.size();
900 // Convert points into triSurface.
902 // Merge points and compact out non-valid triangles
903 labelList triMap; // merged to unmerged triangle
904 labelList triPointReverseMap; // unmerged to merged point
909 false, // do not check for duplicate tris
917 label nZones = surf.markZones
919 boolList(surf.nEdges(), false),
925 collapsedPoint[pointI] = calcCentre(surf);
931 // Synchronise snap location
932 syncUnseparatedPoints(collapsedPoint, point::max);
935 snappedPoint.setSize(mesh_.nPoints());
938 forAll(collapsedPoint, pointI)
940 if (collapsedPoint[pointI] != point::max)
942 snappedPoint[pointI] = snappedPoints.size();
943 snappedPoints.append(collapsedPoint[pointI]);
949 Foam::triSurface Foam::isoSurface::stitchTriPoints
951 const bool checkDuplicates,
952 const List<point>& triPoints,
953 labelList& triPointReverseMap, // unmerged to merged point
954 labelList& triMap // merged to unmerged triangle
957 label nTris = triPoints.size()/3;
959 if ((triPoints.size() % 3) != 0)
961 FatalErrorIn("isoSurface::stitchTriPoints(..)")
962 << "Problem: number of points " << triPoints.size()
963 << " not a multiple of 3." << abort(FatalError);
966 pointField newPoints;
976 // Check that enough merged.
979 pointField newNewPoints;
981 bool hasMerged = mergePoints
992 FatalErrorIn("isoSurface::stitchTriPoints(..)")
993 << "Merged points contain duplicates"
994 << " when merging with distance " << mergeDistance_ << endl
995 << "merged:" << newPoints.size() << " re-merged:"
996 << newNewPoints.size()
997 << abort(FatalError);
1002 List<labelledTri> tris;
1004 DynamicList<labelledTri> dynTris(nTris);
1005 label rawPointI = 0;
1006 DynamicList<label> newToOldTri(nTris);
1008 for (label oldTriI = 0; oldTriI < nTris; oldTriI++)
1012 triPointReverseMap[rawPointI],
1013 triPointReverseMap[rawPointI+1],
1014 triPointReverseMap[rawPointI+2],
1019 if ((tri[0] != tri[1]) && (tri[0] != tri[2]) && (tri[1] != tri[2]))
1021 newToOldTri.append(oldTriI);
1022 dynTris.append(tri);
1026 triMap.transfer(newToOldTri);
1027 tris.transfer(dynTris);
1032 // Determine 'flat hole' situation (see RMT paper).
1033 // Two unconnected triangles get connected because (some of) the edges
1034 // separating them get collapsed. Below only checks for duplicate triangles,
1035 // not non-manifold edge connectivity.
1036 if (checkDuplicates)
1038 labelListList pointFaces;
1039 invertManyToMany(newPoints.size(), tris, pointFaces);
1041 // Filter out duplicates.
1042 DynamicList<label> newToOldTri(tris.size());
1046 const labelledTri& tri = tris[triI];
1047 const labelList& pFaces = pointFaces[tri[0]];
1049 // Find the maximum of any duplicates. Maximum since the tris
1051 // get overwritten so we cannot use them in a comparison.
1055 label nbrTriI = pFaces[i];
1057 if (nbrTriI > triI && (tris[nbrTriI] == tri))
1059 //Pout<< "Duplicate : " << triI << " verts:" << tri
1060 // << " to " << nbrTriI << " verts:" << tris[nbrTriI]
1069 // There is no (higher numbered) duplicate triangle
1070 label newTriI = newToOldTri.size();
1071 newToOldTri.append(triI);
1072 tris[newTriI] = tris[triI];
1076 triMap.transfer(newToOldTri);
1077 tris.setSize(triMap.size());
1081 Pout<< "isoSurface : merged from " << nTris
1082 << " down to " << tris.size() << " unique triangles." << endl;
1088 triSurface surf(tris, geometricSurfacePatchList(0), newPoints);
1092 const labelledTri& f = surf[faceI];
1093 const labelList& fFaces = surf.faceFaces()[faceI];
1097 label nbrFaceI = fFaces[i];
1099 if (nbrFaceI <= faceI)
1101 // lower numbered faces already checked
1105 const labelledTri& nbrF = surf[nbrFaceI];
1109 FatalErrorIn("validTri(const triSurface&, const label)")
1111 << " triangle " << faceI << " vertices " << f
1112 << " fc:" << f.centre(surf.points())
1113 << " has the same vertices as triangle " << nbrFaceI
1114 << " vertices " << nbrF
1115 << " fc:" << nbrF.centre(surf.points())
1116 << abort(FatalError);
1123 return triSurface(tris, geometricSurfacePatchList(0), newPoints, true);
1127 // Does face use valid vertices?
1128 bool Foam::isoSurface::validTri(const triSurface& surf, const label faceI)
1130 // Simple check on indices ok.
1132 const labelledTri& f = surf[faceI];
1136 (f[0] < 0) || (f[0] >= surf.points().size())
1137 || (f[1] < 0) || (f[1] >= surf.points().size())
1138 || (f[2] < 0) || (f[2] >= surf.points().size())
1141 WarningIn("validTri(const triSurface&, const label)")
1142 << "triangle " << faceI << " vertices " << f
1143 << " uses point indices outside point range 0.."
1144 << surf.points().size()-1 << endl;
1149 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
1151 WarningIn("validTri(const triSurface&, const label)")
1152 << "triangle " << faceI
1153 << " uses non-unique vertices " << f
1158 // duplicate triangle check
1160 const labelList& fFaces = surf.faceFaces()[faceI];
1162 // Check if faceNeighbours use same points as this face.
1163 // Note: discards normal information - sides of baffle are merged.
1166 label nbrFaceI = fFaces[i];
1168 if (nbrFaceI <= faceI)
1170 // lower numbered faces already checked
1174 const labelledTri& nbrF = surf[nbrFaceI];
1178 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
1179 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
1180 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
1183 WarningIn("validTri(const triSurface&, const label)")
1184 << "triangle " << faceI << " vertices " << f
1185 << " fc:" << f.centre(surf.points())
1186 << " has the same vertices as triangle " << nbrFaceI
1187 << " vertices " << nbrF
1188 << " fc:" << nbrF.centre(surf.points())
1198 void Foam::isoSurface::calcAddressing
1200 const triSurface& surf,
1201 List<FixedList<label, 3> >& faceEdges,
1202 labelList& edgeFace0,
1203 labelList& edgeFace1,
1204 Map<labelList>& edgeFacesRest
1207 const pointField& points = surf.points();
1209 pointField edgeCentres(3*surf.size());
1213 const labelledTri& tri = surf[triI];
1214 edgeCentres[edgeI++] = 0.5*(points[tri[0]]+points[tri[1]]);
1215 edgeCentres[edgeI++] = 0.5*(points[tri[1]]+points[tri[2]]);
1216 edgeCentres[edgeI++] = 0.5*(points[tri[2]]+points[tri[0]]);
1219 pointField mergedCentres;
1220 labelList oldToMerged;
1221 bool hasMerged = mergePoints
1232 Pout<< "isoSurface : detected "
1233 << mergedCentres.size()
1234 << " geometric edges on " << surf.size() << " triangles." << endl;
1243 // Determine faceEdges
1244 faceEdges.setSize(surf.size());
1248 faceEdges[triI][0] = oldToMerged[edgeI++];
1249 faceEdges[triI][1] = oldToMerged[edgeI++];
1250 faceEdges[triI][2] = oldToMerged[edgeI++];
1254 // Determine edgeFaces
1255 edgeFace0.setSize(mergedCentres.size());
1257 edgeFace1.setSize(mergedCentres.size());
1259 edgeFacesRest.clear();
1261 // Overflow edge faces for geometric shared edges that turned
1262 // out to be different anyway.
1263 EdgeMap<labelList> extraEdgeFaces(mergedCentres.size()/100);
1265 forAll(oldToMerged, oldEdgeI)
1267 label triI = oldEdgeI / 3;
1268 label edgeI = oldToMerged[oldEdgeI];
1270 if (edgeFace0[edgeI] == -1)
1272 // First triangle for edge
1273 edgeFace0[edgeI] = triI;
1277 //- Check that the two triangles actually topologically
1279 const labelledTri& prevTri = surf[edgeFace0[edgeI]];
1280 const labelledTri& tri = surf[triI];
1282 label fp = oldEdgeI % 3;
1284 edge e(tri[fp], tri[tri.fcIndex(fp)]);
1286 label prevTriIndex = -1;
1290 if (edge(prevTri[i], prevTri[prevTri.fcIndex(i)]) == e)
1297 if (prevTriIndex == -1)
1299 // Different edge. Store for later.
1300 EdgeMap<labelList>::iterator iter = extraEdgeFaces.find(e);
1302 if (iter != extraEdgeFaces.end())
1304 labelList& eFaces = iter();
1305 label sz = eFaces.size();
1306 eFaces.setSize(sz+1);
1311 extraEdgeFaces.insert(e, labelList(1, triI));
1314 else if (edgeFace1[edgeI] == -1)
1316 edgeFace1[edgeI] = triI;
1320 //WarningIn("orientSurface(triSurface&)")
1321 // << "Edge " << edgeI << " with centre "
1322 // << mergedCentres[edgeI]
1323 // << " used by more than two triangles: "
1324 // << edgeFace0[edgeI] << ", "
1325 // << edgeFace1[edgeI] << " and " << triI << endl;
1326 Map<labelList>::iterator iter = edgeFacesRest.find(edgeI);
1328 if (iter != edgeFacesRest.end())
1330 labelList& eFaces = iter();
1331 label sz = eFaces.size();
1332 eFaces.setSize(sz+1);
1337 edgeFacesRest.insert(edgeI, labelList(1, triI));
1343 // Add extraEdgeFaces
1344 edgeI = edgeFace0.size();
1346 edgeFace0.setSize(edgeI + extraEdgeFaces.size());
1347 edgeFace1.setSize(edgeI + extraEdgeFaces.size(), -1);
1349 forAllConstIter(EdgeMap<labelList>, extraEdgeFaces, iter)
1351 const labelList& eFaces = iter();
1353 // The current edge will become edgeI. Replace all occurrences in
1357 label triI = eFaces[i];
1358 const labelledTri& tri = surf[triI];
1360 FixedList<label, 3>& fEdges = faceEdges[triI];
1363 edge e(tri[fp], tri[tri.fcIndex(fp)]);
1365 if (e == iter.key())
1374 // Add face to edgeFaces
1376 edgeFace0[edgeI] = eFaces[0];
1378 if (eFaces.size() >= 2)
1380 edgeFace1[edgeI] = eFaces[1];
1382 if (eFaces.size() > 2)
1384 edgeFacesRest.insert
1387 SubList<label>(eFaces, eFaces.size()-2, 2)
1397 void Foam::isoSurface::walkOrientation
1399 const triSurface& surf,
1400 const List<FixedList<label, 3> >& faceEdges,
1401 const labelList& edgeFace0,
1402 const labelList& edgeFace1,
1403 const label seedTriI,
1404 labelList& flipState
1407 // Do walk for consistent orientation.
1408 DynamicList<label> changedFaces(surf.size());
1410 changedFaces.append(seedTriI);
1412 while (changedFaces.size())
1414 DynamicList<label> newChangedFaces(changedFaces.size());
1416 forAll(changedFaces, i)
1418 label triI = changedFaces[i];
1419 const labelledTri& tri = surf[triI];
1420 const FixedList<label, 3>& fEdges = faceEdges[triI];
1424 label edgeI = fEdges[fp];
1428 label p1 = tri[tri.fcIndex(fp)];
1432 edgeFace0[edgeI] != triI
1437 if (nbrI != -1 && flipState[nbrI] == -1)
1439 const labelledTri& nbrTri = surf[nbrI];
1442 label nbrFp = findIndex(nbrTri, p0);
1446 FatalErrorIn("isoSurface::walkOrientation(..)")
1451 << " fEdges:" << fEdges
1452 << " edgeI:" << edgeI
1453 << " edgeFace0:" << edgeFace0[edgeI]
1454 << " edgeFace1:" << edgeFace1[edgeI]
1456 << " nbrTri:" << nbrTri
1457 << abort(FatalError);
1461 label nbrP1 = nbrTri[nbrTri.rcIndex(nbrFp)];
1463 bool sameOrientation = (p1 == nbrP1);
1465 if (flipState[triI] == 0)
1467 flipState[nbrI] = (sameOrientation ? 0 : 1);
1471 flipState[nbrI] = (sameOrientation ? 1 : 0);
1473 newChangedFaces.append(nbrI);
1478 changedFaces.transfer(newChangedFaces);
1483 void Foam::isoSurface::orientSurface
1486 const List<FixedList<label, 3> >& faceEdges,
1487 const labelList& edgeFace0,
1488 const labelList& edgeFace1,
1489 const Map<labelList>& edgeFacesRest
1495 labelList flipState(surf.size(), -1);
1501 // Find first unvisited triangle
1505 seedTriI < surf.size() && flipState[seedTriI] != -1;
1510 if (seedTriI == surf.size())
1515 // Note: Determine orientation of seedTriI?
1516 // for now assume it is ok
1517 flipState[seedTriI] = 0;
1530 // Do actual flipping
1534 if (flipState[triI] == 1)
1536 labelledTri tri(surf[triI]);
1538 surf[triI][0] = tri[0];
1539 surf[triI][1] = tri[2];
1540 surf[triI][2] = tri[1];
1542 else if (flipState[triI] == -1)
1546 "isoSurface::orientSurface(triSurface&, const label)"
1547 ) << "problem" << abort(FatalError);
1553 // Checks if triangle is connected through edgeI only.
1554 bool Foam::isoSurface::danglingTriangle
1556 const FixedList<label, 3>& fEdges,
1557 const labelList& edgeFace1
1563 if (edgeFace1[fEdges[i]] == -1)
1569 if (nOpen == 1 || nOpen == 2 || nOpen == 3)
1580 // Mark triangles to keep. Returns number of dangling triangles.
1581 Foam::label Foam::isoSurface::markDanglingTriangles
1583 const List<FixedList<label, 3> >& faceEdges,
1584 const labelList& edgeFace0,
1585 const labelList& edgeFace1,
1586 const Map<labelList>& edgeFacesRest,
1587 boolList& keepTriangles
1590 keepTriangles.setSize(faceEdges.size());
1591 keepTriangles = true;
1593 label nDangling = 0;
1595 // Remove any dangling triangles
1596 forAllConstIter(Map<labelList>, edgeFacesRest, iter)
1598 // These are all the non-manifold edges. Filter out all triangles
1599 // with only one connected edge (= this edge)
1601 label edgeI = iter.key();
1602 const labelList& otherEdgeFaces = iter();
1604 // Remove all dangling triangles
1605 if (danglingTriangle(faceEdges[edgeFace0[edgeI]], edgeFace1))
1607 keepTriangles[edgeFace0[edgeI]] = false;
1610 if (danglingTriangle(faceEdges[edgeFace1[edgeI]], edgeFace1))
1612 keepTriangles[edgeFace1[edgeI]] = false;
1615 forAll(otherEdgeFaces, i)
1617 label triI = otherEdgeFaces[i];
1618 if (danglingTriangle(faceEdges[triI], edgeFace1))
1620 keepTriangles[triI] = false;
1629 Foam::triSurface Foam::isoSurface::subsetMesh
1631 const triSurface& s,
1632 const labelList& newToOldFaces,
1633 labelList& oldToNewPoints,
1634 labelList& newToOldPoints
1637 const boolList include
1639 createWithValues<boolList>
1648 newToOldPoints.setSize(s.points().size());
1649 oldToNewPoints.setSize(s.points().size());
1650 oldToNewPoints = -1;
1654 forAll(include, oldFacei)
1656 if (include[oldFacei])
1658 // Renumber labels for triangle
1659 const labelledTri& tri = s[oldFacei];
1663 label oldPointI = tri[fp];
1665 if (oldToNewPoints[oldPointI] == -1)
1667 oldToNewPoints[oldPointI] = pointI;
1668 newToOldPoints[pointI++] = oldPointI;
1673 newToOldPoints.setSize(pointI);
1677 pointField newPoints(newToOldPoints.size());
1678 forAll(newToOldPoints, i)
1680 newPoints[i] = s.points()[newToOldPoints[i]];
1683 List<labelledTri> newTriangles(newToOldFaces.size());
1685 forAll(newToOldFaces, i)
1687 // Get old vertex labels
1688 const labelledTri& tri = s[newToOldFaces[i]];
1690 newTriangles[i][0] = oldToNewPoints[tri[0]];
1691 newTriangles[i][1] = oldToNewPoints[tri[1]];
1692 newTriangles[i][2] = oldToNewPoints[tri[2]];
1693 newTriangles[i].region() = tri.region();
1697 return triSurface(newTriangles, s.patches(), newPoints, true);
1701 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1703 Foam::isoSurface::isoSurface
1705 const volScalarField& cVals,
1706 const scalarField& pVals,
1708 const bool regularise,
1709 const scalar mergeTol
1712 mesh_(cVals.mesh()),
1715 regularise_(regularise),
1716 mergeDistance_(mergeTol*mesh_.bounds().mag())
1720 Pout<< "isoSurface:" << nl
1721 << " isoField : " << cVals.name() << nl
1722 << " cell min/max : "
1723 << min(cVals.internalField()) << " / "
1724 << max(cVals.internalField()) << nl
1725 << " point min/max : "
1726 << min(pVals_) << " / "
1727 << max(pVals_) << nl
1728 << " isoValue : " << iso << nl
1729 << " regularise : " << regularise_ << nl
1730 << " mergeTol : " << mergeTol << nl
1734 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
1737 // Rewrite input field
1738 // ~~~~~~~~~~~~~~~~~~~
1739 // Rewrite input volScalarField to have interpolated values
1740 // on separated patches.
1742 cValsPtr_.reset(adaptPatchFields(cVals).ptr());
1745 // Construct cell centres field consistent with cVals
1746 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1747 // Generate field to interpolate. This is identical to the mesh.C()
1748 // except on separated coupled patches and on empty patches.
1750 slicedVolVectorField meshC
1755 mesh_.pointsInstance(),
1764 mesh_.cellCentres(),
1767 forAll(patches, patchI)
1769 const polyPatch& pp = patches[patchI];
1771 // Adapt separated coupled (proc and cyclic) patches
1772 if (isA<coupledPolyPatch>(pp))
1774 fvPatchVectorField& pfld = const_cast<fvPatchVectorField&>
1776 meshC.boundaryField()[patchI]
1779 PackedBoolList isCollocated
1781 collocatedFaces(refCast<const coupledPolyPatch>(pp))
1784 forAll(isCollocated, i)
1786 if (!isCollocated[i])
1788 pfld[i] = mesh_.faceCentres()[pp.start()+i];
1792 else if (isA<emptyPolyPatch>(pp))
1794 typedef slicedVolVectorField::GeometricBoundaryField bType;
1796 bType& bfld = const_cast<bType&>(meshC.boundaryField());
1798 // Clear old value. Cannot resize it since is a slice.
1799 bfld.set(patchI, NULL);
1801 // Set new value we can change
1805 new calculatedFvPatchField<vector>
1807 mesh_.boundary()[patchI],
1812 // Change to face centres
1813 bfld[patchI] = pp.patchSlice(mesh_.faceCentres());
1819 // Pre-calculate patch-per-face to avoid whichPatch call.
1820 labelList boundaryRegion(mesh_.nFaces()-mesh_.nInternalFaces());
1822 forAll(patches, patchI)
1824 const polyPatch& pp = patches[patchI];
1826 label faceI = pp.start();
1830 boundaryRegion[faceI-mesh_.nInternalFaces()] = patchI;
1837 // Determine if any cut through face/cell
1838 calcCutTypes(boundaryRegion, meshC, cValsPtr_(), pVals_);
1841 DynamicList<point> snappedPoints(nCutCells_);
1843 // Per cc -1 or a point inside snappedPoints.
1844 labelList snappedCc;
1860 snappedCc.setSize(mesh_.nCells());
1868 Pout<< "isoSurface : shifted " << snappedPoints.size()
1869 << " cell centres to intersection." << endl;
1872 label nCellSnaps = snappedPoints.size();
1875 // Per point -1 or a point inside snappedPoints.
1876 labelList snappedPoint;
1879 // Determine if point is on boundary.
1880 PackedBoolList isBoundaryPoint(mesh_.nPoints());
1882 forAll(patches, patchI)
1884 // Mark all boundary points that are not physically coupled
1885 // (so anything but collocated coupled patches)
1887 if (patches[patchI].coupled())
1889 const coupledPolyPatch& cpp =
1890 refCast<const coupledPolyPatch>
1895 PackedBoolList isCollocated(collocatedFaces(cpp));
1897 forAll(isCollocated, i)
1899 if (!isCollocated[i])
1901 const face& f = mesh_.faces()[cpp.start()+i];
1905 isBoundaryPoint.set(f[fp], 1);
1912 const polyPatch& pp = patches[patchI];
1916 const face& f = mesh_.faces()[pp.start()+i];
1920 isBoundaryPoint.set(f[fp], 1);
1940 snappedPoint.setSize(mesh_.nPoints());
1946 Pout<< "isoSurface : shifted " << snappedPoints.size()-nCellSnaps
1947 << " vertices to intersection." << endl;
1952 DynamicList<point> triPoints(nCutCells_);
1953 DynamicList<label> triMeshCells(nCutCells_);
1973 Pout<< "isoSurface : generated " << triMeshCells.size()
1974 << " unmerged triangles from " << triPoints.size()
1975 << " unmerged points." << endl;
1979 // Merge points and compact out non-valid triangles
1980 labelList triMap; // merged to unmerged triangle
1981 triSurface::operator=
1985 true, // check for duplicate tris
1987 triPointMergeMap_, // unmerged to merged point
1994 Pout<< "isoSurface : generated " << triMap.size()
1995 << " merged triangles." << endl;
1998 meshCells_.setSize(triMap.size());
2001 meshCells_[i] = triMeshCells[triMap[i]];
2006 Pout<< "isoSurface : checking " << size()
2007 << " triangles for validity." << endl;
2011 // Copied from surfaceCheck
2012 validTri(*this, triI);
2019 List<FixedList<label, 3> > faceEdges;
2020 labelList edgeFace0, edgeFace1;
2021 Map<labelList> edgeFacesRest;
2026 // Calculate addressing
2036 // See if any dangling triangles
2037 boolList keepTriangles;
2038 label nDangling = markDanglingTriangles
2049 Pout<< "isoSurface : detected " << nDangling
2050 << " dangling triangles." << endl;
2058 // Create face map (new to old)
2059 labelList subsetTriMap(findIndices(keepTriangles, true));
2061 labelList subsetPointMap;
2062 labelList reversePointMap;
2063 triSurface::operator=
2073 meshCells_ = labelField(meshCells_, subsetTriMap);
2074 inplaceRenumber(reversePointMap, triPointMergeMap_);
2077 orientSurface(*this, faceEdges, edgeFace0, edgeFace1, edgeFacesRest);
2083 fileName stlFile = mesh_.time().path() + ".stl";
2084 Pout<< "Dumping surface to " << stlFile << endl;
2085 triSurface::write(stlFile);
2090 // ************************************************************************* //