1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 "isoSurface.H"
29 #include "mergePoints.H"
30 #include "syncTools.H"
31 #include "addToRunTimeSelectionTable.H"
32 #include "slicedVolFields.H"
33 #include "volFields.H"
34 #include "surfaceFields.H"
36 #include "meshTools.H"
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
42 defineTypeNameAndDebug(isoSurface, 0);
45 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
47 bool Foam::isoSurface::noTransform(const tensor& tt) const
50 (mag(tt.xx()-1) < mergeDistance_)
51 && (mag(tt.yy()-1) < mergeDistance_)
52 && (mag(tt.zz()-1) < mergeDistance_)
53 && (mag(tt.xy()) < mergeDistance_)
54 && (mag(tt.xz()) < mergeDistance_)
55 && (mag(tt.yx()) < mergeDistance_)
56 && (mag(tt.yz()) < mergeDistance_)
57 && (mag(tt.zx()) < mergeDistance_)
58 && (mag(tt.zy()) < mergeDistance_);
62 bool Foam::isoSurface::collocatedPatch(const polyPatch& pp)
64 const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>(pp);
66 return cpp.parallel() && !cpp.separated();
70 // Calculates per face whether couple is collocated.
71 Foam::PackedBoolList Foam::isoSurface::collocatedFaces
73 const coupledPolyPatch& pp
76 // Initialise to false
77 PackedBoolList collocated(pp.size());
79 const vectorField& separation = pp.separation();
80 const tensorField& forwardT = pp.forwardT();
82 if (forwardT.size() == 0)
85 if (separation.size() == 0)
89 else if (separation.size() == 1)
91 // Fully separate. Do not synchronise.
95 // Per face separation.
98 if (mag(separation[faceI]) < mergeDistance_)
100 collocated[faceI] = 1u;
105 else if (forwardT.size() == 1)
107 // Fully transformed.
111 // Per face transformation.
114 if (noTransform(forwardT[faceI]))
116 collocated[faceI] = 1u;
124 // Insert the data for local point patchPointI into patch local values
125 // and/or into the shared values field.
126 void Foam::isoSurface::insertPointData
128 const processorPolyPatch& pp,
129 const Map<label>& meshToShared,
130 const pointField& pointValues,
131 const label patchPointI,
132 pointField& patchValues,
133 pointField& sharedValues
136 label meshPointI = pp.meshPoints()[patchPointI];
138 // Store in local field
139 label nbrPointI = pp.neighbPoints()[patchPointI];
140 if (nbrPointI >= 0 && nbrPointI < patchValues.size())
142 minEqOp<point>()(patchValues[nbrPointI], pointValues[meshPointI]);
145 // Store in shared field
146 Map<label>::const_iterator iter = meshToShared.find(meshPointI);
147 if (iter != meshToShared.end())
149 minEqOp<point>()(sharedValues[iter()], pointValues[meshPointI]);
154 void Foam::isoSurface::syncUnseparatedPoints
156 pointField& pointValues,
157 const point& nullValue
160 // Until syncPointList handles separated coupled patches with multiple
161 // transforms do our own synchronisation of non-separated patches only
162 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
163 const globalMeshData& pd = mesh_.globalData();
164 const labelList& sharedPtAddr = pd.sharedPointAddr();
165 const labelList& sharedPtLabels = pd.sharedPointLabels();
167 // Create map from meshPoint to globalShared index.
168 Map<label> meshToShared(2*sharedPtLabels.size());
169 forAll(sharedPtLabels, i)
171 meshToShared.insert(sharedPtLabels[i], sharedPtAddr[i]);
174 // Values on shared points.
175 pointField sharedInfo(pd.nGlobalPoints(), nullValue);
178 if (Pstream::parRun())
181 forAll(patches, patchI)
185 isA<processorPolyPatch>(patches[patchI])
186 && patches[patchI].nPoints() > 0
189 const processorPolyPatch& pp =
190 refCast<const processorPolyPatch>(patches[patchI]);
191 const labelList& meshPts = pp.meshPoints();
193 pointField patchInfo(meshPts.size(), nullValue);
195 PackedBoolList isCollocated(collocatedFaces(pp));
197 forAll(isCollocated, faceI)
199 if (isCollocated[faceI])
201 const face& f = pp.localFaces()[faceI];
205 label pointI = f[fp];
220 OPstream toNbr(Pstream::blocking, pp.neighbProcNo());
225 // Receive and combine.
227 forAll(patches, patchI)
231 isA<processorPolyPatch>(patches[patchI])
232 && patches[patchI].nPoints() > 0
235 const processorPolyPatch& pp =
236 refCast<const processorPolyPatch>(patches[patchI]);
238 pointField nbrPatchInfo(pp.nPoints());
240 // We do not know the number of points on the other side
241 // so cannot use Pstream::read.
242 IPstream fromNbr(Pstream::blocking, pp.neighbProcNo());
243 fromNbr >> nbrPatchInfo;
246 // Null any value which is not on neighbouring processor
247 nbrPatchInfo.setSize(pp.nPoints(), nullValue);
249 const labelList& meshPts = pp.meshPoints();
251 forAll(meshPts, pointI)
253 label meshPointI = meshPts[pointI];
256 pointValues[meshPointI],
265 // Don't do cyclics for now. Are almost always separated anyway.
270 // Combine on master.
271 Pstream::listCombineGather(sharedInfo, minEqOp<point>());
272 Pstream::listCombineScatter(sharedInfo);
274 // Now we will all have the same information. Merge it back with
275 // my local information. (Note assignment and not combine operator)
276 forAll(sharedPtLabels, i)
278 label meshPointI = sharedPtLabels[i];
279 pointValues[meshPointI] = sharedInfo[sharedPtAddr[i]];
284 Foam::scalar Foam::isoSurface::isoFraction
303 bool Foam::isoSurface::isEdgeOfFaceCut
305 const scalarField& pVals,
313 bool fpLower = (pVals[f[fp]] < iso_);
316 (fpLower != ownLower)
317 || (fpLower != neiLower)
318 || (fpLower != (pVals[f[f.fcIndex(fp)]] < iso_))
328 // Get neighbour value and position.
329 void Foam::isoSurface::getNeighbour
331 const labelList& boundaryRegion,
332 const volVectorField& meshC,
333 const volScalarField& cVals,
340 const labelList& own = mesh_.faceOwner();
341 const labelList& nei = mesh_.faceNeighbour();
343 if (mesh_.isInternalFace(faceI))
345 label nbr = (own[faceI] == cellI ? nei[faceI] : own[faceI]);
346 nbrValue = cVals[nbr];
347 nbrPoint = meshC[nbr];
351 label bFaceI = faceI-mesh_.nInternalFaces();
352 label patchI = boundaryRegion[bFaceI];
353 const polyPatch& pp = mesh_.boundaryMesh()[patchI];
354 label patchFaceI = faceI-pp.start();
356 nbrValue = cVals.boundaryField()[patchI][patchFaceI];
357 nbrPoint = meshC.boundaryField()[patchI][patchFaceI];
362 // Determine for every face/cell whether it (possibly) generates triangles.
363 void Foam::isoSurface::calcCutTypes
365 const labelList& boundaryRegion,
366 const volVectorField& meshC,
367 const volScalarField& cVals,
368 const scalarField& pVals
371 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
372 const labelList& own = mesh_.faceOwner();
373 const labelList& nei = mesh_.faceNeighbour();
375 faceCutType_.setSize(mesh_.nFaces());
376 faceCutType_ = NOTCUT;
378 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
381 bool ownLower = (cVals[own[faceI]] < iso_);
396 bool neiLower = (nbrValue < iso_);
398 if (ownLower != neiLower)
400 faceCutType_[faceI] = CUT;
404 // See if any mesh edge is cut by looping over all the edges of the
406 const face f = mesh_.faces()[faceI];
408 if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower))
410 faceCutType_[faceI] = CUT;
415 forAll(patches, patchI)
417 const polyPatch& pp = patches[patchI];
419 label faceI = pp.start();
423 bool ownLower = (cVals[own[faceI]] < iso_);
438 bool neiLower = (nbrValue < iso_);
440 if (ownLower != neiLower)
442 faceCutType_[faceI] = CUT;
447 const face f = mesh_.faces()[faceI];
449 if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower))
451 faceCutType_[faceI] = CUT;
462 cellCutType_.setSize(mesh_.nCells());
463 cellCutType_ = NOTCUT;
465 for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
467 if (faceCutType_[faceI] != NOTCUT)
469 if (cellCutType_[own[faceI]] == NOTCUT)
471 cellCutType_[own[faceI]] = CUT;
474 if (cellCutType_[nei[faceI]] == NOTCUT)
476 cellCutType_[nei[faceI]] = CUT;
481 for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
483 if (faceCutType_[faceI] != NOTCUT)
485 if (cellCutType_[own[faceI]] == NOTCUT)
487 cellCutType_[own[faceI]] = CUT;
495 Pout<< "isoSurface : detected " << nCutCells_
496 << " candidate cut cells (out of " << mesh_.nCells()
502 // Return the two common points between two triangles
503 Foam::labelPair Foam::isoSurface::findCommonPoints
505 const labelledTri& tri0,
506 const labelledTri& tri1
509 labelPair common(-1, -1);
512 label fp1 = findIndex(tri1, tri0[fp0]);
517 fp1 = findIndex(tri1, tri0[fp0]);
522 // So tri0[fp0] is tri1[fp1]
524 // Find next common point
525 label fp0p1 = tri0.fcIndex(fp0);
526 label fp1p1 = tri1.fcIndex(fp1);
527 label fp1m1 = tri1.rcIndex(fp1);
529 if (tri0[fp0p1] == tri1[fp1p1] || tri0[fp0p1] == tri1[fp1m1])
531 common[0] = tri0[fp0];
532 common[1] = tri0[fp0p1];
539 // Caculate centre of surface.
540 Foam::point Foam::isoSurface::calcCentre(const triSurface& s)
542 vector sum = vector::zero;
546 sum += s[i].centre(s.points());
552 // Replace surface (localPoints, localTris) with single point. Returns
553 // point. Destructs arguments.
554 Foam::pointIndexHit Foam::isoSurface::collapseSurface
556 pointField& localPoints,
557 DynamicList<labelledTri, 64>& localTris
560 pointIndexHit info(false, vector::zero, localTris.size());
562 if (localTris.size() == 1)
564 const labelledTri& tri = localTris[0];
565 info.setPoint(tri.centre(localPoints));
568 else if (localTris.size() == 2)
570 // Check if the two triangles share an edge.
571 const labelledTri& tri0 = localTris[0];
572 const labelledTri& tri1 = localTris[0];
574 labelPair shared = findCommonPoints(tri0, tri1);
582 tri0.centre(localPoints)
583 + tri1.centre(localPoints)
589 else if (localTris.size())
591 // Check if single region. Rare situation.
595 geometricSurfacePatchList(0),
599 localTris.clearStorage();
602 label nZones = surf.markZones
604 boolList(surf.nEdges(), false),
610 info.setPoint(calcCentre(surf));
619 // Determine per cell centre whether all the intersections get collapsed
621 void Foam::isoSurface::calcSnappedCc
623 const labelList& boundaryRegion,
624 const volVectorField& meshC,
625 const volScalarField& cVals,
626 const scalarField& pVals,
628 DynamicList<point>& snappedPoints,
632 const pointField& pts = mesh_.points();
633 const pointField& cc = mesh_.cellCentres();
635 snappedCc.setSize(mesh_.nCells());
639 DynamicList<point, 64> localTriPoints(64);
641 forAll(mesh_.cells(), cellI)
643 if (cellCutType_[cellI] == CUT)
645 scalar cVal = cVals[cellI];
647 const cell& cFaces = mesh_.cells()[cellI];
649 localTriPoints.clear();
651 point otherPointSum = vector::zero;
653 // Create points for all intersections close to cell centre
654 // (i.e. from pyramid edges)
656 forAll(cFaces, cFaceI)
658 label faceI = cFaces[cFaceI];
673 // Calculate intersection points of edges to cell centre
674 FixedList<scalar, 3> s;
675 FixedList<point, 3> pt;
677 // From cc to neighbour cc.
678 s[2] = isoFraction(cVal, nbrValue);
679 pt[2] = (1.0-s[2])*cc[cellI] + s[2]*nbrPoint;
681 const face& f = mesh_.faces()[cFaces[cFaceI]];
687 s[0] = isoFraction(cVal, pVals[p0]);
688 pt[0] = (1.0-s[0])*cc[cellI] + s[0]*pts[p0];
691 label p1 = f[f.fcIndex(fp)];
692 s[1] = isoFraction(cVal, pVals[p1]);
693 pt[1] = (1.0-s[1])*cc[cellI] + s[1]*pts[p1];
697 (s[0] >= 0.0 && s[0] <= 0.5)
698 && (s[1] >= 0.0 && s[1] <= 0.5)
699 && (s[2] >= 0.0 && s[2] <= 0.5)
702 localTriPoints.append(pt[0]);
703 localTriPoints.append(pt[1]);
704 localTriPoints.append(pt[2]);
708 // Get average of all other points
711 if (s[i] >= 0.0 && s[i] <= 0.5)
713 otherPointSum += pt[i];
721 if (localTriPoints.size() == 0)
723 // No complete triangles. Get average of all intersection
727 snappedCc[cellI] = snappedPoints.size();
728 snappedPoints.append(otherPointSum/nOther);
730 //Pout<< " point:" << pointI
731 // << " replacing coord:" << mesh_.points()[pointI]
732 // << " by average:" << collapsedPoint[pointI] << endl;
735 else if (localTriPoints.size() == 3)
737 // Single triangle. No need for any analysis. Average points.
739 points.transfer(localTriPoints);
740 snappedCc[cellI] = snappedPoints.size();
741 snappedPoints.append(sum(points)/points.size());
743 //Pout<< " point:" << pointI
744 // << " replacing coord:" << mesh_.points()[pointI]
745 // << " by average:" << collapsedPoint[pointI] << endl;
749 // Convert points into triSurface.
751 // Merge points and compact out non-valid triangles
752 labelList triMap; // merged to unmerged triangle
753 labelList triPointReverseMap; // unmerged to merged point
758 false, // do not check for duplicate tris
766 label nZones = surf.markZones
768 boolList(surf.nEdges(), false),
774 snappedCc[cellI] = snappedPoints.size();
775 snappedPoints.append(calcCentre(surf));
776 //Pout<< " point:" << pointI << " nZones:" << nZones
777 // << " replacing coord:" << mesh_.points()[pointI]
778 // << " by average:" << collapsedPoint[pointI] << endl;
786 // Determine per meshpoint whether all the intersections get collapsed
788 void Foam::isoSurface::calcSnappedPoint
790 const PackedBoolList& isBoundaryPoint,
791 const labelList& boundaryRegion,
792 const volVectorField& meshC,
793 const volScalarField& cVals,
794 const scalarField& pVals,
796 DynamicList<point>& snappedPoints,
797 labelList& snappedPoint
800 const pointField& pts = mesh_.points();
801 const pointField& cc = mesh_.cellCentres();
804 const point greatPoint(VGREAT, VGREAT, VGREAT);
805 pointField collapsedPoint(mesh_.nPoints(), greatPoint);
809 DynamicList<point, 64> localTriPoints(100);
811 forAll(mesh_.pointFaces(), pointI)
813 if (isBoundaryPoint.get(pointI) == 1)
818 const labelList& pFaces = mesh_.pointFaces()[pointI];
824 label faceI = pFaces[i];
826 if (faceCutType_[faceI] == CUT)
839 localTriPoints.clear();
841 point otherPointSum = vector::zero;
843 forAll(pFaces, pFaceI)
845 // Create points for all intersections close to point
846 // (i.e. from pyramid edges)
848 label faceI = pFaces[pFaceI];
849 const face& f = mesh_.faces()[faceI];
850 label own = mesh_.faceOwner()[faceI];
852 // Get neighbour value
866 // Calculate intersection points of edges emanating from point
867 FixedList<scalar, 4> s;
868 FixedList<point, 4> pt;
870 label fp = findIndex(f, pointI);
871 s[0] = isoFraction(pVals[pointI], cVals[own]);
872 pt[0] = (1.0-s[0])*pts[pointI] + s[0]*cc[own];
874 s[1] = isoFraction(pVals[pointI], nbrValue);
875 pt[1] = (1.0-s[1])*pts[pointI] + s[1]*nbrPoint;
877 label nextPointI = f[f.fcIndex(fp)];
878 s[2] = isoFraction(pVals[pointI], pVals[nextPointI]);
879 pt[2] = (1.0-s[2])*pts[pointI] + s[2]*pts[nextPointI];
881 label prevPointI = f[f.rcIndex(fp)];
882 s[3] = isoFraction(pVals[pointI], pVals[prevPointI]);
883 pt[3] = (1.0-s[3])*pts[pointI] + s[3]*pts[prevPointI];
887 (s[0] >= 0.0 && s[0] <= 0.5)
888 && (s[1] >= 0.0 && s[1] <= 0.5)
889 && (s[2] >= 0.0 && s[2] <= 0.5)
892 localTriPoints.append(pt[0]);
893 localTriPoints.append(pt[1]);
894 localTriPoints.append(pt[2]);
898 (s[0] >= 0.0 && s[0] <= 0.5)
899 && (s[1] >= 0.0 && s[1] <= 0.5)
900 && (s[3] >= 0.0 && s[3] <= 0.5)
903 localTriPoints.append(pt[3]);
904 localTriPoints.append(pt[0]);
905 localTriPoints.append(pt[1]);
908 // Get average of points as fallback
911 if (s[i] >= 0.0 && s[i] <= 0.5)
913 otherPointSum += pt[i];
919 if (localTriPoints.size() == 0)
921 // No complete triangles. Get average of all intersection
925 collapsedPoint[pointI] = otherPointSum/nOther;
928 else if (localTriPoints.size() == 3)
930 // Single triangle. No need for any analysis. Average points.
932 points.transfer(localTriPoints);
933 collapsedPoint[pointI] = sum(points)/points.size();
937 // Convert points into triSurface.
939 // Merge points and compact out non-valid triangles
940 labelList triMap; // merged to unmerged triangle
941 labelList triPointReverseMap; // unmerged to merged point
946 false, // do not check for duplicate tris
954 label nZones = surf.markZones
956 boolList(surf.nEdges(), false),
962 collapsedPoint[pointI] = calcCentre(surf);
968 // Synchronise snap location
969 syncUnseparatedPoints(collapsedPoint, greatPoint);
972 snappedPoint.setSize(mesh_.nPoints());
975 forAll(collapsedPoint, pointI)
977 if (collapsedPoint[pointI] != greatPoint)
979 snappedPoint[pointI] = snappedPoints.size();
980 snappedPoints.append(collapsedPoint[pointI]);
986 Foam::triSurface Foam::isoSurface::stitchTriPoints
988 const bool checkDuplicates,
989 const List<point>& triPoints,
990 labelList& triPointReverseMap, // unmerged to merged point
991 labelList& triMap // merged to unmerged triangle
994 label nTris = triPoints.size()/3;
996 if ((triPoints.size() % 3) != 0)
998 FatalErrorIn("isoSurface::stitchTriPoints(..)")
999 << "Problem: number of points " << triPoints.size()
1000 << " not a multiple of 3." << abort(FatalError);
1003 pointField newPoints;
1013 // Check that enough merged.
1016 pointField newNewPoints;
1018 bool hasMerged = mergePoints
1029 FatalErrorIn("isoSurface::stitchTriPoints(..)")
1030 << "Merged points contain duplicates"
1031 << " when merging with distance " << mergeDistance_ << endl
1032 << "merged:" << newPoints.size() << " re-merged:"
1033 << newNewPoints.size()
1034 << abort(FatalError);
1039 List<labelledTri> tris;
1041 DynamicList<labelledTri> dynTris(nTris);
1042 label rawPointI = 0;
1043 DynamicList<label> newToOldTri(nTris);
1045 for (label oldTriI = 0; oldTriI < nTris; oldTriI++)
1049 triPointReverseMap[rawPointI],
1050 triPointReverseMap[rawPointI+1],
1051 triPointReverseMap[rawPointI+2],
1056 if ((tri[0] != tri[1]) && (tri[0] != tri[2]) && (tri[1] != tri[2]))
1058 newToOldTri.append(oldTriI);
1059 dynTris.append(tri);
1063 triMap.transfer(newToOldTri);
1064 tris.transfer(dynTris);
1069 // Determine 'flat hole' situation (see RMT paper).
1070 // Two unconnected triangles get connected because (some of) the edges
1071 // separating them get collapsed. Below only checks for duplicate triangles,
1072 // not non-manifold edge connectivity.
1073 if (checkDuplicates)
1075 labelListList pointFaces;
1076 invertManyToMany(newPoints.size(), tris, pointFaces);
1078 // Filter out duplicates.
1079 DynamicList<label> newToOldTri(tris.size());
1083 const labelledTri& tri = tris[triI];
1084 const labelList& pFaces = pointFaces[tri[0]];
1086 // Find the maximum of any duplicates. Maximum since the tris
1088 // get overwritten so we cannot use them in a comparison.
1092 label nbrTriI = pFaces[i];
1094 if (nbrTriI > triI && (tris[nbrTriI] == tri))
1096 //Pout<< "Duplicate : " << triI << " verts:" << tri
1097 // << " to " << nbrTriI << " verts:" << tris[nbrTriI]
1106 // There is no (higher numbered) duplicate triangle
1107 label newTriI = newToOldTri.size();
1108 newToOldTri.append(triI);
1109 tris[newTriI] = tris[triI];
1113 triMap.transfer(newToOldTri);
1114 tris.setSize(triMap.size());
1118 Pout<< "isoSurface : merged from " << nTris
1119 << " down to " << tris.size() << " unique triangles." << endl;
1125 triSurface surf(tris, geometricSurfacePatchList(0), newPoints);
1129 const labelledTri& f = surf[faceI];
1130 const labelList& fFaces = surf.faceFaces()[faceI];
1134 label nbrFaceI = fFaces[i];
1136 if (nbrFaceI <= faceI)
1138 // lower numbered faces already checked
1142 const labelledTri& nbrF = surf[nbrFaceI];
1146 FatalErrorIn("validTri(const triSurface&, const label)")
1148 << " triangle " << faceI << " vertices " << f
1149 << " fc:" << f.centre(surf.points())
1150 << " has the same vertices as triangle " << nbrFaceI
1151 << " vertices " << nbrF
1152 << " fc:" << nbrF.centre(surf.points())
1153 << abort(FatalError);
1160 return triSurface(tris, geometricSurfacePatchList(0), newPoints, true);
1164 // Does face use valid vertices?
1165 bool Foam::isoSurface::validTri(const triSurface& surf, const label faceI)
1167 // Simple check on indices ok.
1169 const labelledTri& f = surf[faceI];
1173 (f[0] < 0) || (f[0] >= surf.points().size())
1174 || (f[1] < 0) || (f[1] >= surf.points().size())
1175 || (f[2] < 0) || (f[2] >= surf.points().size())
1178 WarningIn("validTri(const triSurface&, const label)")
1179 << "triangle " << faceI << " vertices " << f
1180 << " uses point indices outside point range 0.."
1181 << surf.points().size()-1 << endl;
1186 if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
1188 WarningIn("validTri(const triSurface&, const label)")
1189 << "triangle " << faceI
1190 << " uses non-unique vertices " << f
1195 // duplicate triangle check
1197 const labelList& fFaces = surf.faceFaces()[faceI];
1199 // Check if faceNeighbours use same points as this face.
1200 // Note: discards normal information - sides of baffle are merged.
1203 label nbrFaceI = fFaces[i];
1205 if (nbrFaceI <= faceI)
1207 // lower numbered faces already checked
1211 const labelledTri& nbrF = surf[nbrFaceI];
1215 ((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
1216 && ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
1217 && ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
1220 WarningIn("validTri(const triSurface&, const label)")
1221 << "triangle " << faceI << " vertices " << f
1222 << " fc:" << f.centre(surf.points())
1223 << " has the same vertices as triangle " << nbrFaceI
1224 << " vertices " << nbrF
1225 << " fc:" << nbrF.centre(surf.points())
1235 void Foam::isoSurface::calcAddressing
1237 const triSurface& surf,
1238 List<FixedList<label, 3> >& faceEdges,
1239 labelList& edgeFace0,
1240 labelList& edgeFace1,
1241 Map<labelList>& edgeFacesRest
1244 const pointField& points = surf.points();
1246 pointField edgeCentres(3*surf.size());
1250 const labelledTri& tri = surf[triI];
1251 edgeCentres[edgeI++] = 0.5*(points[tri[0]]+points[tri[1]]);
1252 edgeCentres[edgeI++] = 0.5*(points[tri[1]]+points[tri[2]]);
1253 edgeCentres[edgeI++] = 0.5*(points[tri[2]]+points[tri[0]]);
1256 pointField mergedCentres;
1257 labelList oldToMerged;
1258 bool hasMerged = mergePoints
1269 Pout<< "isoSurface : detected "
1270 << mergedCentres.size()
1271 << " geometric edges on " << surf.size() << " triangles." << endl;
1276 if (surf.size() == 1)
1278 faceEdges.setSize(1);
1279 faceEdges[0][0] = 0;
1280 faceEdges[0][1] = 1;
1281 faceEdges[0][2] = 2;
1282 edgeFace0.setSize(1);
1284 edgeFace1.setSize(1);
1286 edgeFacesRest.clear();
1292 // Determine faceEdges
1293 faceEdges.setSize(surf.size());
1297 faceEdges[triI][0] = oldToMerged[edgeI++];
1298 faceEdges[triI][1] = oldToMerged[edgeI++];
1299 faceEdges[triI][2] = oldToMerged[edgeI++];
1303 // Determine edgeFaces
1304 edgeFace0.setSize(mergedCentres.size());
1306 edgeFace1.setSize(mergedCentres.size());
1308 edgeFacesRest.clear();
1310 // Overflow edge faces for geometric shared edges that turned
1311 // out to be different anyway.
1312 EdgeMap<labelList> extraEdgeFaces(mergedCentres.size()/100);
1314 forAll(oldToMerged, oldEdgeI)
1316 label triI = oldEdgeI / 3;
1317 label edgeI = oldToMerged[oldEdgeI];
1319 if (edgeFace0[edgeI] == -1)
1321 // First triangle for edge
1322 edgeFace0[edgeI] = triI;
1326 //- Check that the two triangles actually topologically
1328 const labelledTri& prevTri = surf[edgeFace0[edgeI]];
1329 const labelledTri& tri = surf[triI];
1331 label fp = oldEdgeI % 3;
1333 edge e(tri[fp], tri[tri.fcIndex(fp)]);
1335 label prevTriIndex = -1;
1339 if (edge(prevTri[i], prevTri[prevTri.fcIndex(i)]) == e)
1346 if (prevTriIndex == -1)
1348 // Different edge. Store for later.
1349 EdgeMap<labelList>::iterator iter = extraEdgeFaces.find(e);
1351 if (iter != extraEdgeFaces.end())
1353 labelList& eFaces = iter();
1354 label sz = eFaces.size();
1355 eFaces.setSize(sz+1);
1360 extraEdgeFaces.insert(e, labelList(1, triI));
1363 else if (edgeFace1[edgeI] == -1)
1365 edgeFace1[edgeI] = triI;
1369 //WarningIn("orientSurface(triSurface&)")
1370 // << "Edge " << edgeI << " with centre "
1371 // << mergedCentres[edgeI]
1372 // << " used by more than two triangles: "
1373 // << edgeFace0[edgeI] << ", "
1374 // << edgeFace1[edgeI] << " and " << triI << endl;
1375 Map<labelList>::iterator iter = edgeFacesRest.find(edgeI);
1377 if (iter != edgeFacesRest.end())
1379 labelList& eFaces = iter();
1380 label sz = eFaces.size();
1381 eFaces.setSize(sz+1);
1386 edgeFacesRest.insert(edgeI, labelList(1, triI));
1392 // Add extraEdgeFaces
1393 edgeI = edgeFace0.size();
1395 edgeFace0.setSize(edgeI + extraEdgeFaces.size());
1396 edgeFace1.setSize(edgeI + extraEdgeFaces.size(), -1);
1398 forAllConstIter(EdgeMap<labelList>, extraEdgeFaces, iter)
1400 const labelList& eFaces = iter();
1402 // The current edge will become edgeI. Replace all occurrences in
1406 label triI = eFaces[i];
1407 const labelledTri& tri = surf[triI];
1409 FixedList<label, 3>& fEdges = faceEdges[triI];
1412 edge e(tri[fp], tri[tri.fcIndex(fp)]);
1414 if (e == iter.key())
1423 // Add face to edgeFaces
1425 edgeFace0[edgeI] = eFaces[0];
1427 if (eFaces.size() >= 2)
1429 edgeFace1[edgeI] = eFaces[1];
1431 if (eFaces.size() > 2)
1433 edgeFacesRest.insert
1436 SubList<label>(eFaces, eFaces.size()-2, 2)
1446 void Foam::isoSurface::walkOrientation
1448 const triSurface& surf,
1449 const List<FixedList<label, 3> >& faceEdges,
1450 const labelList& edgeFace0,
1451 const labelList& edgeFace1,
1452 const label seedTriI,
1453 labelList& flipState
1456 // Do walk for consistent orientation.
1457 DynamicList<label> changedFaces(surf.size());
1459 changedFaces.append(seedTriI);
1461 while (changedFaces.size())
1463 DynamicList<label> newChangedFaces(changedFaces.size());
1465 forAll(changedFaces, i)
1467 label triI = changedFaces[i];
1468 const labelledTri& tri = surf[triI];
1469 const FixedList<label, 3>& fEdges = faceEdges[triI];
1473 label edgeI = fEdges[fp];
1477 label p1 = tri[tri.fcIndex(fp)];
1481 edgeFace0[edgeI] != triI
1486 if (nbrI != -1 && flipState[nbrI] == -1)
1488 const labelledTri& nbrTri = surf[nbrI];
1491 label nbrFp = findIndex(nbrTri, p0);
1495 FatalErrorIn("isoSurface::walkOrientation(..)")
1500 << " fEdges:" << fEdges
1501 << " edgeI:" << edgeI
1502 << " edgeFace0:" << edgeFace0[edgeI]
1503 << " edgeFace1:" << edgeFace1[edgeI]
1505 << " nbrTri:" << nbrTri
1506 << abort(FatalError);
1510 label nbrP1 = nbrTri[nbrTri.rcIndex(nbrFp)];
1512 bool sameOrientation = (p1 == nbrP1);
1514 if (flipState[triI] == 0)
1516 flipState[nbrI] = (sameOrientation ? 0 : 1);
1520 flipState[nbrI] = (sameOrientation ? 1 : 0);
1522 newChangedFaces.append(nbrI);
1527 changedFaces.transfer(newChangedFaces);
1532 void Foam::isoSurface::orientSurface
1535 const List<FixedList<label, 3> >& faceEdges,
1536 const labelList& edgeFace0,
1537 const labelList& edgeFace1,
1538 const Map<labelList>& edgeFacesRest
1544 labelList flipState(surf.size(), -1);
1550 // Find first unvisited triangle
1554 seedTriI < surf.size() && flipState[seedTriI] != -1;
1559 if (seedTriI == surf.size())
1564 // Note: Determine orientation of seedTriI?
1565 // for now assume it is ok
1566 flipState[seedTriI] = 0;
1579 // Do actual flipping
1583 if (flipState[triI] == 1)
1585 labelledTri tri(surf[triI]);
1587 surf[triI][0] = tri[0];
1588 surf[triI][1] = tri[2];
1589 surf[triI][2] = tri[1];
1591 else if (flipState[triI] == -1)
1595 "isoSurface::orientSurface(triSurface&, const label)"
1596 ) << "problem" << abort(FatalError);
1602 // Checks if triangle is connected through edgeI only.
1603 bool Foam::isoSurface::danglingTriangle
1605 const FixedList<label, 3>& fEdges,
1606 const labelList& edgeFace1
1612 if (edgeFace1[fEdges[i]] == -1)
1618 if (nOpen == 1 || nOpen == 2 || nOpen == 3)
1629 // Mark triangles to keep. Returns number of dangling triangles.
1630 Foam::label Foam::isoSurface::markDanglingTriangles
1632 const List<FixedList<label, 3> >& faceEdges,
1633 const labelList& edgeFace0,
1634 const labelList& edgeFace1,
1635 const Map<labelList>& edgeFacesRest,
1636 boolList& keepTriangles
1639 keepTriangles.setSize(faceEdges.size());
1640 keepTriangles = true;
1642 label nDangling = 0;
1644 // Remove any dangling triangles
1645 forAllConstIter(Map<labelList>, edgeFacesRest, iter)
1647 // These are all the non-manifold edges. Filter out all triangles
1648 // with only one connected edge (= this edge)
1650 label edgeI = iter.key();
1651 const labelList& otherEdgeFaces = iter();
1653 // Remove all dangling triangles
1654 if (danglingTriangle(faceEdges[edgeFace0[edgeI]], edgeFace1))
1656 keepTriangles[edgeFace0[edgeI]] = false;
1659 if (danglingTriangle(faceEdges[edgeFace1[edgeI]], edgeFace1))
1661 keepTriangles[edgeFace1[edgeI]] = false;
1664 forAll(otherEdgeFaces, i)
1666 label triI = otherEdgeFaces[i];
1667 if (danglingTriangle(faceEdges[triI], edgeFace1))
1669 keepTriangles[triI] = false;
1678 Foam::triSurface Foam::isoSurface::subsetMesh
1680 const triSurface& s,
1681 const labelList& newToOldFaces,
1682 labelList& oldToNewPoints,
1683 labelList& newToOldPoints
1686 const boolList include
1688 createWithValues<boolList>
1697 newToOldPoints.setSize(s.points().size());
1698 oldToNewPoints.setSize(s.points().size());
1699 oldToNewPoints = -1;
1703 forAll(include, oldFacei)
1705 if (include[oldFacei])
1707 // Renumber labels for triangle
1708 const labelledTri& tri = s[oldFacei];
1712 label oldPointI = tri[fp];
1714 if (oldToNewPoints[oldPointI] == -1)
1716 oldToNewPoints[oldPointI] = pointI;
1717 newToOldPoints[pointI++] = oldPointI;
1722 newToOldPoints.setSize(pointI);
1726 pointField newPoints(newToOldPoints.size());
1727 forAll(newToOldPoints, i)
1729 newPoints[i] = s.points()[newToOldPoints[i]];
1732 List<labelledTri> newTriangles(newToOldFaces.size());
1734 forAll(newToOldFaces, i)
1736 // Get old vertex labels
1737 const labelledTri& tri = s[newToOldFaces[i]];
1739 newTriangles[i][0] = oldToNewPoints[tri[0]];
1740 newTriangles[i][1] = oldToNewPoints[tri[1]];
1741 newTriangles[i][2] = oldToNewPoints[tri[2]];
1742 newTriangles[i].region() = tri.region();
1746 return triSurface(newTriangles, s.patches(), newPoints, true);
1750 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1752 Foam::isoSurface::isoSurface
1754 const volScalarField& cVals,
1755 const scalarField& pVals,
1757 const bool regularise,
1758 const scalar mergeTol
1761 mesh_(cVals.mesh()),
1764 regularise_(regularise),
1765 mergeDistance_(mergeTol*mesh_.bounds().mag())
1769 Pout<< "isoSurface:" << nl
1770 << " isoField : " << cVals.name() << nl
1771 << " cell min/max : "
1772 << min(cVals.internalField()) << " / "
1773 << max(cVals.internalField()) << nl
1774 << " point min/max : "
1775 << min(pVals_) << " / "
1776 << max(pVals_) << nl
1777 << " isoValue : " << iso << nl
1778 << " regularise : " << regularise_ << nl
1779 << " mergeTol : " << mergeTol << nl
1783 const polyBoundaryMesh& patches = mesh_.boundaryMesh();
1786 // Rewrite input field
1787 // ~~~~~~~~~~~~~~~~~~~
1788 // Rewrite input volScalarField to have interpolated values
1789 // on separated patches.
1791 cValsPtr_.reset(adaptPatchFields(cVals).ptr());
1794 // Construct cell centres field consistent with cVals
1795 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1796 // Generate field to interpolate. This is identical to the mesh.C()
1797 // except on separated coupled patches and on empty patches.
1799 slicedVolVectorField meshC
1804 mesh_.pointsInstance(),
1813 mesh_.cellCentres(),
1816 forAll(patches, patchI)
1818 const polyPatch& pp = patches[patchI];
1820 // Adapt separated coupled (proc and cyclic) patches
1821 if (isA<coupledPolyPatch>(pp) && !collocatedPatch(pp))
1823 fvPatchVectorField& pfld = const_cast<fvPatchVectorField&>
1825 meshC.boundaryField()[patchI]
1828 PackedBoolList isCollocated
1830 collocatedFaces(refCast<const coupledPolyPatch>(pp))
1833 forAll(isCollocated, i)
1835 if (!isCollocated[i])
1837 pfld[i] = mesh_.faceCentres()[pp.start()+i];
1841 else if (isA<emptyPolyPatch>(pp))
1843 typedef slicedVolVectorField::GeometricBoundaryField bType;
1845 bType& bfld = const_cast<bType&>(meshC.boundaryField());
1847 // Clear old value. Cannot resize it since is a slice.
1848 bfld.set(patchI, NULL);
1850 // Set new value we can change
1854 new calculatedFvPatchField<vector>
1856 mesh_.boundary()[patchI],
1861 // Change to face centres
1862 bfld[patchI] = pp.patchSlice(mesh_.faceCentres());
1868 // Pre-calculate patch-per-face to avoid whichPatch call.
1869 labelList boundaryRegion(mesh_.nFaces()-mesh_.nInternalFaces());
1871 forAll(patches, patchI)
1873 const polyPatch& pp = patches[patchI];
1875 label faceI = pp.start();
1879 boundaryRegion[faceI-mesh_.nInternalFaces()] = patchI;
1886 // Determine if any cut through face/cell
1887 calcCutTypes(boundaryRegion, meshC, cValsPtr_(), pVals_);
1890 DynamicList<point> snappedPoints(nCutCells_);
1892 // Per cc -1 or a point inside snappedPoints.
1893 labelList snappedCc;
1909 snappedCc.setSize(mesh_.nCells());
1917 Pout<< "isoSurface : shifted " << snappedPoints.size()
1918 << " cell centres to intersection." << endl;
1921 label nCellSnaps = snappedPoints.size();
1924 // Per point -1 or a point inside snappedPoints.
1925 labelList snappedPoint;
1928 // Determine if point is on boundary.
1929 PackedBoolList isBoundaryPoint(mesh_.nPoints());
1931 forAll(patches, patchI)
1933 // Mark all boundary points that are not physically coupled
1934 // (so anything but collocated coupled patches)
1936 if (patches[patchI].coupled())
1938 if (!collocatedPatch(patches[patchI]))
1940 const coupledPolyPatch& cpp =
1941 refCast<const coupledPolyPatch>
1946 PackedBoolList isCollocated(collocatedFaces(cpp));
1948 forAll(isCollocated, i)
1950 if (!isCollocated[i])
1952 const face& f = mesh_.faces()[cpp.start()+i];
1956 isBoundaryPoint.set(f[fp], 1);
1964 const polyPatch& pp = patches[patchI];
1968 const face& f = mesh_.faces()[pp.start()+i];
1972 isBoundaryPoint.set(f[fp], 1);
1992 snappedPoint.setSize(mesh_.nPoints());
1998 Pout<< "isoSurface : shifted " << snappedPoints.size()-nCellSnaps
1999 << " vertices to intersection." << endl;
2004 DynamicList<point> triPoints(nCutCells_);
2005 DynamicList<label> triMeshCells(nCutCells_);
2025 Pout<< "isoSurface : generated " << triMeshCells.size()
2026 << " unmerged triangles from " << triPoints.size()
2027 << " unmerged points." << endl;
2031 // Merge points and compact out non-valid triangles
2032 labelList triMap; // merged to unmerged triangle
2033 triSurface::operator=
2037 true, // check for duplicate tris
2039 triPointMergeMap_, // unmerged to merged point
2046 Pout<< "isoSurface : generated " << triMap.size()
2047 << " merged triangles." << endl;
2050 meshCells_.setSize(triMap.size());
2053 meshCells_[i] = triMeshCells[triMap[i]];
2058 Pout<< "isoSurface : checking " << size()
2059 << " triangles for validity." << endl;
2063 // Copied from surfaceCheck
2064 validTri(*this, triI);
2071 List<FixedList<label, 3> > faceEdges;
2072 labelList edgeFace0, edgeFace1;
2073 Map<labelList> edgeFacesRest;
2078 // Calculate addressing
2079 calcAddressing(*this, faceEdges, edgeFace0, edgeFace1, edgeFacesRest);
2081 // See if any dangling triangles
2082 boolList keepTriangles;
2083 label nDangling = markDanglingTriangles
2094 Pout<< "isoSurface : detected " << nDangling
2095 << " dangling triangles." << endl;
2103 // Create face map (new to old)
2104 labelList subsetTriMap(findIndices(keepTriangles, true));
2106 labelList subsetPointMap;
2107 labelList reversePointMap;
2108 triSurface::operator=
2118 meshCells_ = labelField(meshCells_, subsetTriMap);
2119 inplaceRenumber(reversePointMap, triPointMergeMap_);
2122 orientSurface(*this, faceEdges, edgeFace0, edgeFace1, edgeFacesRest);
2128 fileName stlFile = mesh_.time().path() + ".stl";
2129 Pout<< "Dumping surface to " << stlFile << endl;
2130 triSurface::write(stlFile);
2135 // ************************************************************************* //