1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "primitiveMesh.H"
27 #include "pyramidPointFaceRef.H"
29 #include "mathematicalConstants.H"
30 #include "SortableList.H"
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 const Foam::debug::tolerancesSwitch
35 Foam::primitiveMesh::closedThreshold_
37 "primitiveMeshClosedThreshold",
41 const Foam::debug::tolerancesSwitch
42 Foam::primitiveMesh::aspectThreshold_
44 "primitiveMeshAspectThreshold",
48 Foam::debug::tolerancesSwitch
49 Foam::primitiveMesh::nonOrthThreshold_ // deg
51 "primitiveMeshNonOrthThreshold",
55 const Foam::debug::tolerancesSwitch
56 Foam::primitiveMesh::skewThreshold_
58 "primitiveMeshSkewThreshold",
62 Foam::debug::tolerancesSwitch
63 Foam::primitiveMesh::faceAngleThreshold_
65 "primitiveMeshFaceAngleThreshold",
69 const Foam::debug::tolerancesSwitch
70 Foam::primitiveMesh::faceFlatnessThreshold_
72 "primitiveMeshFaceFlatnessThreshold",
77 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
79 bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const
83 Info<< "bool primitiveMesh::checkClosedBoundary("
84 << "const bool) const: "
85 << "checking whether the boundary is closed" << endl;
88 // Loop through all boundary faces and sum up the face area vectors.
89 // For a closed boundary, this should be zero in all vector components
91 vector sumClosed(vector::zero);
92 scalar sumMagClosedBoundary = 0;
94 const vectorField& areas = faceAreas();
96 for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++)
98 sumClosed += areas[faceI];
99 sumMagClosedBoundary += mag(areas[faceI]);
102 reduce(sumClosed, sumOp<vector>());
103 reduce(sumMagClosedBoundary, sumOp<scalar>());
105 vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
107 if (cmptMax(cmptMag(openness)) > closedThreshold_())
111 Info<< " ***Boundary openness " << openness
112 << " Threshold = " << closedThreshold_()
113 << " possible hole in boundary description."
123 Info<< " Boundary openness " << openness
124 << " Threshold = " << closedThreshold_()
134 bool Foam::primitiveMesh::checkClosedCells
137 labelHashSet* setPtr,
138 labelHashSet* aspectSetPtr
143 Info<< "bool primitiveMesh::checkClosedCells("
144 << "const bool, labelHashSet*, labelHashSet*) const: "
145 << "checking whether cells are closed" << endl;
148 // Check that all cells labels are valid
149 const cellList& c = cells();
151 label nErrorClosed = 0;
155 const cell& curCell = c[cI];
157 if (min(curCell) < 0 || max(curCell) > nFaces())
168 if (nErrorClosed > 0)
172 Info<< " ***Cells with invalid face labels found, number of cells "
173 << nErrorClosed << endl;
179 // Check if a face has got the same cell as owner and neighbour
181 const labelList& own = faceOwner();
182 const labelList& nei = faceNeighbour();
184 label nErrorOwnNei = 0;
186 // Check internal faces only
189 if (own[faceI] == nei[faceI])
193 setPtr->insert(own[faceI]);
200 if (nErrorOwnNei > 0)
204 Info<< " ***Faces declaring same cell as owner and neighbour "
205 << "found, number of faces "
206 << nErrorOwnNei << endl;
212 // Loop through cell faces and sum up the face area vectors for each cell.
213 // This should be zero in all vector components
215 vectorField sumClosed(nCells(), vector::zero);
216 vectorField sumMagClosed(nCells(), vector::zero);
218 const vectorField& areas = faceAreas();
223 sumClosed[own[faceI]] += areas[faceI];
224 sumMagClosed[own[faceI]] += cmptMag(areas[faceI]);
229 // Subtract from neighbour
230 sumClosed[nei[faceI]] -= areas[faceI];
231 sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]);
235 scalar maxOpennessCell = 0;
238 scalar maxAspectRatio = 0;
240 const scalarField& vols = cellVolumes();
243 forAll (sumClosed, cellI)
245 scalar maxOpenness = 0;
247 for(direction cmpt=0; cmpt<vector::nComponents; cmpt++)
252 mag(sumClosed[cellI][cmpt])
253 /(sumMagClosed[cellI][cmpt] + VSMALL)
257 maxOpennessCell = max(maxOpennessCell, maxOpenness);
259 if (maxOpenness > closedThreshold_())
263 setPtr->insert(cellI);
269 // Calculate the aspect ration as the maximum of Cartesian component
270 // aspect ratio to the total area hydraulic area aspect ratio
271 scalar aspectRatio = max
273 cmptMax(sumMagClosed[cellI])
274 /(cmptMin(sumMagClosed[cellI]) + VSMALL),
275 1.0/6.0*cmptSum(sumMagClosed[cellI])/
276 Foam::pow(Foam::max(vols[cellI], SMALL), 2.0/3.0)
279 maxAspectRatio = max(maxAspectRatio, aspectRatio);
281 if (aspectRatio > aspectThreshold_())
285 aspectSetPtr->insert(cellI);
292 reduce(nOpen, sumOp<label>());
293 reduce(maxOpennessCell, maxOp<scalar>());
295 reduce(nAspect, sumOp<label>());
296 reduce(maxAspectRatio, maxOp<scalar>());
303 Info<< " ***Open cells found, max cell openness: "
304 << maxOpennessCell << ", number of open cells " << nOpen
305 << " Threshold = " << closedThreshold_()
316 Info<< " ***High aspect ratio cells found, Max aspect ratio: "
318 << ", number of cells " << nAspect
319 << " Threshold = " << aspectThreshold_()
328 Info<< " Max cell openness = " << maxOpennessCell << " OK." << nl
329 << " Max aspect ratio = " << maxAspectRatio << " OK."
337 bool Foam::primitiveMesh::checkFaceAreas
345 Info<< "bool primitiveMesh::checkFaceAreas("
346 << "const bool, labelHashSet*) const: "
347 << "checking face area magnitudes" << endl;
350 const scalarField magFaceAreas = mag(faceAreas());
352 scalar minArea = GREAT;
353 scalar maxArea = -GREAT;
355 forAll (magFaceAreas, faceI)
357 if (magFaceAreas[faceI] < VSMALL)
361 setPtr->insert(faceI);
365 minArea = min(minArea, magFaceAreas[faceI]);
366 maxArea = max(maxArea, magFaceAreas[faceI]);
369 reduce(minArea, minOp<scalar>());
370 reduce(maxArea, maxOp<scalar>());
372 if (minArea < VSMALL)
376 Info<< " ***Zero or negative face area detected. "
377 "Minimum area: " << minArea << endl;
386 Info<< " Minumum face area = " << minArea
387 << ". Maximum face area = " << maxArea
388 << ". Face area magnitudes OK." << endl;
396 bool Foam::primitiveMesh::checkCellVolumes
404 Info<< "bool primitiveMesh::checkCellVolumes("
405 << "const bool, labelHashSet*) const: "
406 << "checking cell volumes" << endl;
409 const scalarField& vols = cellVolumes();
411 scalar minVolume = GREAT;
412 scalar maxVolume = -GREAT;
414 label nNegVolCells = 0;
418 if (vols[cellI] < VSMALL)
422 setPtr->insert(cellI);
428 minVolume = min(minVolume, vols[cellI]);
429 maxVolume = max(maxVolume, vols[cellI]);
432 reduce(minVolume, minOp<scalar>());
433 reduce(maxVolume, maxOp<scalar>());
434 reduce(nNegVolCells, sumOp<label>());
436 if (minVolume < VSMALL)
440 Info<< " ***Zero or negative cell volume detected. "
441 << "Minimum negative volume: " << minVolume
442 << ", Number of negative volume cells: " << nNegVolCells
452 Info<< " Min volume = " << minVolume
453 << ". Max volume = " << maxVolume
454 << ". Total volume = " << gSum(vols)
455 << ". Cell volumes OK." << endl;
463 bool Foam::primitiveMesh::checkFaceOrthogonality
471 Info<< "bool primitiveMesh::checkFaceOrthogonality("
472 << "const bool, labelHashSet*) const: "
473 << "checking mesh non-orthogonality" << endl;
476 // for all internal faces check theat the d dot S product is positive
477 const vectorField& centres = cellCentres();
478 const vectorField& areas = faceAreas();
480 const labelList& own = faceOwner();
481 const labelList& nei = faceNeighbour();
483 // Severe nonorthogonality threshold
484 const scalar severeNonorthogonalityThreshold =
485 ::cos(nonOrthThreshold_()/180.0*mathematicalConstant::pi);
487 scalar minDDotS = GREAT;
491 label severeNonOrth = 0;
493 label errorNonOrth = 0;
497 vector d = centres[nei[faceI]] - centres[own[faceI]];
498 const vector& s = areas[faceI];
500 scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL);
502 if (dDotS < severeNonorthogonalityThreshold)
508 setPtr->insert(faceI);
517 setPtr->insert(faceI);
524 if (dDotS < minDDotS)
532 reduce(minDDotS, minOp<scalar>());
533 reduce(sumDDotS, sumOp<scalar>());
534 reduce(severeNonOrth, sumOp<label>());
535 reduce(errorNonOrth, sumOp<label>());
539 label neiSize = nei.size();
540 reduce(neiSize, sumOp<label>());
546 Info<< " Mesh non-orthogonality Max: "
547 << ::acos(minDDotS)/mathematicalConstant::pi*180.0
549 ::acos(sumDDotS/neiSize)/mathematicalConstant::pi*180.0
550 << " Threshold = " << nonOrthThreshold_()
555 if (severeNonOrth > 0)
557 Info<< " *Number of severely non-orthogonal faces: "
558 << severeNonOrth << "." << endl;
562 if (errorNonOrth > 0)
566 Info<< " ***Number of non-orthogonality errors: "
567 << errorNonOrth << "." << endl;
576 Info<< " Non-orthogonality check OK." << endl;
584 bool Foam::primitiveMesh::checkFacePyramids
587 const scalar minPyrVol,
593 Info<< "bool primitiveMesh::checkFacePyramids("
594 << "const bool, const scalar, labelHashSet*) const: "
595 << "checking face orientation" << endl;
598 // check whether face area vector points to the cell with higher label
599 const vectorField& ctrs = cellCentres();
601 const labelList& own = faceOwner();
602 const labelList& nei = faceNeighbour();
604 const faceList& f = faces();
606 const pointField& p = points();
608 label nErrorPyrs = 0;
612 // Create the owner pyramid - it will have negative volume
613 scalar pyrVol = pyramidPointFaceRef(f[faceI], ctrs[own[faceI]]).mag(p);
615 if (pyrVol > -minPyrVol)
619 setPtr->insert(faceI);
625 if (isInternalFace(faceI))
627 // Create the neighbour pyramid - it will have positive volume
629 pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p);
631 if (pyrVol < minPyrVol)
635 setPtr->insert(faceI);
643 reduce(nErrorPyrs, sumOp<label>());
649 Info<< " ***Error in face pyramids: "
650 << nErrorPyrs << " faces are incorrectly oriented."
660 Info<< " Face pyramids OK." << endl;
668 bool Foam::primitiveMesh::checkFaceSkewness
676 Info<< "bool primitiveMesh::checkFaceSkewnesss("
677 << "const bool, labelHashSet*) const: "
678 << "checking face skewness" << endl;
681 // Warn if the skew correction vector is more than skewWarning times
682 // larger than the face area vector
684 const pointField& p = points();
685 const faceList& fcs = faces();
687 const labelList& own = faceOwner();
688 const labelList& nei = faceNeighbour();
689 const vectorField& cellCtrs = cellCentres();
690 const vectorField& faceCtrs = faceCentres();
691 const vectorField& fAreas = faceAreas();
698 vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
699 vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]];
703 Cpf - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + SMALL))*d;
704 vector svHat = sv/(mag(sv) + VSMALL);
706 // Normalisation distance calculated as the approximate distance
707 // from the face centre to the edge of the face in the direction of
709 scalar fd = 0.2*mag(d) + VSMALL;
710 const face& f = fcs[faceI];
713 fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
716 // Normalised skewness
717 scalar skewness = mag(sv)/fd;
719 // Check if the skewness vector is greater than the PN vector.
720 // This does not cause trouble but is a good indication of a poor mesh.
721 if (skewness > skewThreshold_())
725 setPtr->insert(faceI);
731 if(skewness > maxSkew)
738 // Boundary faces: consider them to have only skewness error.
739 // (i.e. treat as if mirror cell on other side)
741 for (label faceI = nInternalFaces(); faceI < nFaces(); faceI++)
743 vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
745 vector normal = fAreas[faceI];
746 normal /= mag(normal) + VSMALL;
747 vector d = normal*(normal & Cpf);
751 vector sv = Cpf - ((fAreas[faceI]&Cpf)/((fAreas[faceI]&d)+VSMALL))*d;
752 vector svHat = sv/(mag(sv) + VSMALL);
754 // Normalisation distance calculated as the approximate distance
755 // from the face centre to the edge of the face in the direction of
757 scalar fd = 0.4*mag(d) + VSMALL;
758 const face& f = fcs[faceI];
761 fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
764 // Normalised skewness
765 scalar skewness = mag(sv)/fd;
767 // Check if the skewness vector is greater than the PN vector.
768 // This does not cause trouble but is a good indication of a poor mesh.
769 if (skewness > skewThreshold_())
773 setPtr->insert(faceI);
779 if(skewness > maxSkew)
786 reduce(maxSkew, maxOp<scalar>());
787 reduce(nWarnSkew, sumOp<label>());
793 Info<< " ***Max skewness = " << maxSkew
794 << ", " << nWarnSkew << " highly skew faces detected"
795 << " Threshold = " << skewThreshold_()
805 Info<< " Max skewness = " << maxSkew << " OK." << endl;
813 bool Foam::primitiveMesh::checkPoints
821 Info<< "bool primitiveMesh::checkPoints"
822 << "(const bool, labelHashSet*) const: "
823 << "checking points" << endl;
826 label nFaceErrors = 0;
827 label nCellErrors = 0;
829 const labelListList& pf = pointFaces();
833 if (pf[pointI].empty())
837 setPtr->insert(pointI);
847 const labelList& pc = pointCells(pointI);
853 setPtr->insert(pointI);
860 reduce(nFaceErrors, sumOp<label>());
861 reduce(nCellErrors, sumOp<label>());
863 if (nFaceErrors > 0 || nCellErrors > 0)
867 Info<< " ***Unused points found in the mesh, "
868 "number unused by faces: " << nFaceErrors
869 << " number unused by cells: " << nCellErrors
879 Info<< " Point usage OK." << endl;
887 // Check convexity of angles in a face. Allow a slight non-convexity.
888 // E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
889 // (if truly concave and points not visible from face centre the face-pyramid
890 // check in checkMesh will fail)
891 bool Foam::primitiveMesh::checkFaceAngles
899 Info<< "bool primitiveMesh::checkFaceAngles"
900 << "(const bool, labelHashSet*) const: "
901 << "checking face angles" << endl;
904 if (faceAngleThreshold_() < -SMALL || faceAngleThreshold_() > 180 + SMALL)
908 "primitiveMesh::checkFaceAngles(const bool, labelHashSet*)"
909 ) << "faceAngleThreshold_ should be [0..180] but is now "
910 << faceAngleThreshold_()
914 const scalar maxSin =
915 Foam::sin(faceAngleThreshold_()/180.0*mathematicalConstant::pi);
917 const pointField& p = points();
918 const faceList& fcs = faces();
919 vectorField faceNormals(faceAreas());
920 faceNormals /= mag(faceNormals) + VSMALL;
922 scalar maxEdgeSin = 0.0;
926 label errorFaceI = -1;
930 const face& f = fcs[faceI];
932 // Get edge from f[0] to f[size-1];
933 vector ePrev(p[f[0]] - p[f[f.size()-1]]);
934 scalar magEPrev = mag(ePrev);
935 ePrev /= magEPrev + VSMALL;
939 // Get vertex after fp
940 label fp1 = f.fcIndex(fp0);
942 // Normalized vector between two consecutive points
943 vector e10(p[f[fp1]] - p[f[fp0]]);
944 scalar magE10 = mag(e10);
945 e10 /= magE10 + VSMALL;
947 if (magEPrev > SMALL && magE10 > SMALL)
949 vector edgeNormal = ePrev ^ e10;
950 scalar magEdgeNormal = mag(edgeNormal);
952 if (magEdgeNormal < maxSin)
954 // Edges (almost) aligned -> face is ok.
959 edgeNormal /= magEdgeNormal;
961 if ((edgeNormal & faceNormals[faceI]) < SMALL)
963 if (faceI != errorFaceI)
965 // Count only one error per face.
972 setPtr->insert(faceI);
975 maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
985 reduce(nConcave, sumOp<label>());
986 reduce(maxEdgeSin, maxOp<scalar>());
990 scalar maxConcaveDegr =
991 Foam::asin(Foam::min(1.0, maxEdgeSin))
992 *180.0/mathematicalConstant::pi;
996 Info<< " *There are " << nConcave
997 << " faces with concave angles between consecutive"
998 << " edges. Max concave angle = " << maxConcaveDegr
999 << " degrees." << endl;
1006 if (debug || report)
1008 Info<< " All angles in faces OK." << endl;
1016 // Check warpage of faces. Is calculated as the difference between areas of
1017 // individual triangles and the overall area of the face (which ifself is
1018 // is the average of the areas of the individual triangles).
1019 bool Foam::primitiveMesh::checkFaceFlatness
1022 labelHashSet* setPtr
1027 Info<< "bool primitiveMesh::checkFaceFlatness"
1028 << "(const bool, labelHashSet*) const: "
1029 << "checking face flatness" << endl;
1032 if (faceFlatnessThreshold_() < 0 || faceFlatnessThreshold_() > 1)
1036 "primitiveMesh::checkFaceFlatness"
1037 "(const bool, labelHashSet*)"
1038 ) << "faceFlatnessThreshold_ should be [0..1] but is now "
1039 << faceFlatnessThreshold_()
1040 << exit(FatalError);
1044 const pointField& p = points();
1045 const faceList& fcs = faces();
1046 const pointField& fctrs = faceCentres();
1048 // Areas are calculated as the sum of areas. (see
1049 // primitiveMeshFaceCentresAndAreas.C)
1050 scalarField magAreas(mag(faceAreas()));
1054 scalar minFlatness = GREAT;
1055 scalar sumFlatness = 0;
1060 const face& f = fcs[faceI];
1062 if (f.size() > 3 && magAreas[faceI] > VSMALL)
1064 const point& fc = fctrs[faceI];
1066 // Calculate the sum of magnitude of areas and compare to magnitude
1073 const point& thisPoint = p[f[fp]];
1074 const point& nextPoint = p[f.nextLabel(fp)];
1076 // Triangle around fc.
1077 vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint));
1081 scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1083 sumFlatness += flatness;
1086 minFlatness = min(minFlatness, flatness);
1088 if (flatness < faceFlatnessThreshold_())
1094 setPtr->insert(faceI);
1101 reduce(nWarped, sumOp<label>());
1102 reduce(minFlatness, minOp<scalar>());
1104 reduce(nSummed, sumOp<label>());
1105 reduce(sumFlatness, sumOp<scalar>());
1107 if (debug || report)
1111 Info<< " Face flatness (1 = flat, 0 = butterfly) : average = "
1112 << sumFlatness / nSummed << " min = " << minFlatness << endl;
1119 if (debug || report)
1121 Info<< " *There are " << nWarped
1122 << " faces with ratio between projected and actual area < "
1123 << faceFlatnessThreshold_() << endl;
1125 Info<< " Minimum ratio (minimum flatness, maximum warpage) = "
1126 << minFlatness << endl;
1133 if (debug || report)
1135 Info<< " All face flatness OK." << endl;
1143 // Check 1D/2Dness of edges. Gets passed the non-empty directions and
1144 // checks all edges in the mesh whether they:
1145 // - have no component in a non-empty direction or
1146 // - are only in a singe non-empty direction.
1147 // Empty direction info is passed in as a vector of labels (synchronised)
1148 // which are 1 if the direction is non-empty, 0 if it is.
1149 bool Foam::primitiveMesh::checkEdgeAlignment
1152 const Vector<label>& directions,
1153 labelHashSet* setPtr
1158 Info<< "bool primitiveMesh::checkEdgeAlignment("
1159 << "const bool, const Vector<label>&, labelHashSet*) const: "
1160 << "checking edge alignment" << endl;
1164 for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1166 if (directions[cmpt] == 1)
1170 else if (directions[cmpt] != 0)
1174 "primitiveMesh::checkEdgeAlignment"
1175 "(const bool, const Vector<label>&, labelHashSet*)"
1176 ) << "directions should contain 0 or 1 but is now " << directions
1177 << exit(FatalError);
1181 if (nDirs == vector::nComponents)
1188 const pointField& p = points();
1189 const faceList& fcs = faces();
1191 EdgeMap<label> edgesInError;
1195 const face& f = fcs[faceI];
1200 label p1 = f.nextLabel(fp);
1203 vector d(p[p1]-p[p0]);
1204 scalar magD = mag(d);
1206 if (magD > ROOTVSMALL)
1210 // Check how many empty directions are used by the edge.
1211 label nEmptyDirs = 0;
1212 label nNonEmptyDirs = 0;
1213 for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1215 if (mag(d[cmpt]) > 1e-6)
1217 if (directions[cmpt] == 0)
1228 if (nEmptyDirs == 0)
1230 // Purely in ok directions.
1232 else if (nEmptyDirs == 1)
1234 // Ok if purely in empty directions.
1235 if (nNonEmptyDirs > 0)
1237 edgesInError.insert(edge(p0, p1), faceI);
1240 else if (nEmptyDirs > 1)
1243 edgesInError.insert(edge(p0, p1), faceI);
1250 label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>());
1252 if (nErrorEdges > 0)
1254 if (debug || report)
1256 Info<< " ***Number of edges not aligned with or perpendicular to "
1257 << "non-empty directions: " << nErrorEdges << endl;
1262 setPtr->resize(2*edgesInError.size());
1263 forAllConstIter(EdgeMap<label>, edgesInError, iter)
1265 setPtr->insert(iter.key()[0]);
1266 setPtr->insert(iter.key()[1]);
1274 if (debug || report)
1276 Info<< " All edges aligned with or perpendicular to "
1277 << "non-empty directions." << endl;
1284 bool Foam::primitiveMesh::checkUpperTriangular
1287 labelHashSet* setPtr
1292 Info<< "bool primitiveMesh::checkUpperTriangular("
1293 << "const bool, labelHashSet*) const: "
1294 << "checking face ordering" << endl;
1297 // Check whether internal faces are ordered in the upper triangular order
1298 const labelList& own = faceOwner();
1299 const labelList& nei = faceNeighbour();
1301 const cellList& c = cells();
1302 label internal = nInternalFaces();
1304 // Has error occurred?
1306 // Have multiple faces been detected?
1307 label nMultipleCells = false;
1309 // Loop through faceCells once more and make sure that for internal cell
1310 // the first label is smaller
1311 for (label faceI = 0; faceI < internal; faceI++)
1313 if (own[faceI] >= nei[faceI])
1319 setPtr->insert(faceI);
1324 // Loop through all cells. For each cell, find the face that is internal
1325 // and add it to the check list (upper triangular order).
1326 // Once the list is completed, check it against the faceCell list
1330 const labelList& curFaces = c[cellI];
1332 // Neighbouring cells
1333 SortableList<label> nbr(curFaces.size());
1337 label faceI = curFaces[i];
1339 if (faceI >= nInternalFaces())
1346 label nbrCellI = nei[faceI];
1348 if (nbrCellI == cellI)
1350 nbrCellI = own[faceI];
1353 if (cellI < nbrCellI)
1360 // nbrCell is master. Let it handle this face.
1370 // Now nbr holds the cellCells in incremental order. Check:
1371 // - neighbouring cells appear only once. Since nbr is sorted this
1372 // is simple check on consecutive elements
1373 // - faces indexed in same order as nbr are incrementing as well.
1375 label prevCell = nbr[0];
1376 label prevFace = curFaces[nbr.indices()[0]];
1378 bool hasMultipleFaces = false;
1380 for (label i = 1; i < nbr.size(); i++)
1382 label thisCell = nbr[i];
1383 label thisFace = curFaces[nbr.indices()[i]];
1385 if (thisCell == labelMax)
1390 if (thisCell == prevCell)
1392 hasMultipleFaces = true;
1396 setPtr->insert(prevFace);
1397 setPtr->insert(thisFace);
1400 else if (thisFace < prevFace)
1406 setPtr->insert(thisFace);
1410 prevCell = thisCell;
1411 prevFace = thisFace;
1414 if (hasMultipleFaces)
1421 reduce(error, orOp<bool>());
1422 reduce(nMultipleCells, sumOp<label>());
1424 if ((debug || report) && nMultipleCells > 0)
1426 Info<< " <<Found " << nMultipleCells
1427 << " neighbouring cells with multiple inbetween faces." << endl;
1432 if (debug || report)
1434 Info<< " ***Faces not in upper triangular order." << endl;
1441 if (debug || report)
1443 Info<< " Upper triangular ordering OK." << endl;
1451 bool Foam::primitiveMesh::checkCellsZipUp
1454 labelHashSet* setPtr
1459 Info<< "bool primitiveMesh::checkCellsZipUp("
1460 << "const bool, labelHashSet*) const: "
1461 << "checking topological cell openness" << endl;
1464 label nOpenCells = 0;
1466 const faceList& f = faces();
1467 const cellList& c = cells();
1471 const labelList& curFaces = c[cellI];
1473 const edgeList cellEdges = c[cellI].edges(f);
1475 labelList edgeUsage(cellEdges.size(), 0);
1477 forAll (curFaces, faceI)
1479 edgeList curFaceEdges = f[curFaces[faceI]].edges();
1481 forAll (curFaceEdges, faceEdgeI)
1483 const edge& curEdge = curFaceEdges[faceEdgeI];
1485 forAll (cellEdges, cellEdgeI)
1487 if (cellEdges[cellEdgeI] == curEdge)
1489 edgeUsage[cellEdgeI]++;
1496 edgeList singleEdges(cellEdges.size());
1497 label nSingleEdges = 0;
1499 forAll (edgeUsage, edgeI)
1501 if (edgeUsage[edgeI] == 1)
1503 singleEdges[nSingleEdges] = cellEdges[edgeI];
1506 else if (edgeUsage[edgeI] != 2)
1510 setPtr->insert(cellI);
1515 if (nSingleEdges > 0)
1519 setPtr->insert(cellI);
1526 reduce(nOpenCells, sumOp<label>());
1530 if (debug || report)
1532 Info<< " ***Open cells found, number of cells: " << nOpenCells
1533 << ". This problem may be fixable using the zipUpMesh utility."
1541 if (debug || report)
1543 Info<< " Topological cell zip-up check OK." << endl;
1551 // Vertices of face within point range and unique.
1552 bool Foam::primitiveMesh::checkFaceVertices
1555 labelHashSet* setPtr
1560 Info<< "bool primitiveMesh::checkFaceVertices("
1561 << "const bool, labelHashSet*) const: "
1562 << "checking face vertices" << endl;
1565 // Check that all vertex labels are valid
1566 const faceList& f = faces();
1568 label nErrorFaces = 0;
1572 const face& curFace = f[fI];
1574 if (min(curFace) < 0 || max(curFace) > nPoints())
1584 // Uniqueness of vertices
1585 labelHashSet facePoints(2*curFace.size());
1589 bool inserted = facePoints.insert(curFace[fp]);
1603 reduce(nErrorFaces, sumOp<label>());
1605 if (nErrorFaces > 0)
1607 if (debug || report)
1609 Info<< " Faces with invalid vertex labels found, "
1610 << " number of faces: " << nErrorFaces << endl;
1617 if (debug || report)
1619 Info<< " Face vertices OK." << endl;
1627 // Check if all points on face are shared between faces.
1628 bool Foam::primitiveMesh::checkDuplicateFaces
1631 const Map<label>& nCommonPoints,
1632 label& nBaffleFaces,
1633 labelHashSet* setPtr
1638 forAllConstIter(Map<label>, nCommonPoints, iter)
1640 label nbFaceI = iter.key();
1641 label nCommon = iter();
1643 const face& curFace = faces()[faceI];
1644 const face& nbFace = faces()[nbFaceI];
1646 if (nCommon == nbFace.size() || nCommon == curFace.size())
1648 if (nbFace.size() != curFace.size())
1659 setPtr->insert(faceI);
1660 setPtr->insert(nbFaceI);
1669 // Check that shared points are in consecutive order.
1670 bool Foam::primitiveMesh::checkCommonOrder
1673 const Map<label>& nCommonPoints,
1674 labelHashSet* setPtr
1679 forAllConstIter(Map<label>, nCommonPoints, iter)
1681 label nbFaceI = iter.key();
1682 label nCommon = iter();
1684 const face& curFace = faces()[faceI];
1685 const face& nbFace = faces()[nbFaceI];
1690 && nCommon != nbFace.size()
1691 && nCommon != curFace.size()
1696 // Get the index in the neighbouring face shared with curFace
1697 label nb = findIndex(nbFace, curFace[fp]);
1702 // Check the whole face from nb onwards for shared vertices
1703 // with neighbouring face. Rule is that any shared vertices
1704 // should be consecutive on both faces i.e. if they are
1705 // vertices fp,fp+1,fp+2 on one face they should be
1706 // vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
1710 // Vertices before and after on curFace
1711 label fpPlus1 = curFace.fcIndex(fp);
1712 label fpMin1 = curFace.rcIndex(fp);
1714 // Vertices before and after on nbFace
1715 label nbPlus1 = nbFace.fcIndex(nb);
1716 label nbMin1 = nbFace.rcIndex(nb);
1718 // Find order of walking by comparing next points on both
1720 label curInc = labelMax;
1721 label nbInc = labelMax;
1723 if (nbFace[nbPlus1] == curFace[fpPlus1])
1728 else if (nbFace[nbPlus1] == curFace[fpMin1])
1733 else if (nbFace[nbMin1] == curFace[fpMin1])
1745 // Pass1: loop until start of common vertices found.
1753 if (curFp >= curFace.size())
1759 curFp = curFace.size()-1;
1764 if (curNb >= nbFace.size())
1770 curNb = nbFace.size()-1;
1772 } while (curFace[curFp] == nbFace[curNb]);
1775 // Pass2: check equality walking from curFp, curNb
1776 // in opposite order.
1781 for (label commonI = 0; commonI < nCommon; commonI++)
1785 if (curFp >= curFace.size())
1791 curFp = curFace.size()-1;
1796 if (curNb >= nbFace.size())
1802 curNb = nbFace.size()-1;
1805 if (curFace[curFp] != nbFace[curNb])
1809 setPtr->insert(faceI);
1810 setPtr->insert(nbFaceI);
1820 // Done the curFace - nbFace combination.
1831 // Checks common vertices between faces. If more than 2 they should be
1832 // consecutive on both faces.
1833 bool Foam::primitiveMesh::checkFaceFaces
1836 labelHashSet* setPtr
1841 Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
1842 << " const: " << "checking face-face connectivity" << endl;
1845 const labelListList& pf = pointFaces();
1847 label nBaffleFaces = 0;
1848 label nErrorDuplicate = 0;
1849 label nErrorOrder = 0;
1850 Map<label> nCommonPoints(100);
1852 for (label faceI = 0; faceI < nFaces(); faceI++)
1854 const face& curFace = faces()[faceI];
1856 // Calculate number of common points between current faceI and
1857 // neighbouring face. Store on map.
1858 nCommonPoints.clear();
1862 label pointI = curFace[fp];
1864 const labelList& nbs = pf[pointI];
1868 label nbFaceI = nbs[nbI];
1870 if (faceI < nbFaceI)
1872 // Only check once for each combination of two faces.
1874 Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
1876 if (fnd == nCommonPoints.end())
1878 // First common vertex found.
1879 nCommonPoints.insert(nbFaceI, 1);
1889 // Perform various checks on common points
1891 // Check all vertices shared (duplicate point)
1892 if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
1897 // Check common vertices are consecutive on both faces
1898 if (checkCommonOrder(faceI, nCommonPoints, setPtr))
1904 reduce(nBaffleFaces, sumOp<label>());
1905 reduce(nErrorDuplicate, sumOp<label>());
1906 reduce(nErrorOrder, sumOp<label>());
1910 Info<< " Number of identical duplicate faces (baffle faces): "
1911 << nBaffleFaces << endl;
1914 if (nErrorDuplicate > 0 || nErrorOrder > 0)
1916 if (nErrorDuplicate > 0)
1918 Info<< " ***Number of duplicate (not baffle) faces found: "
1919 << nErrorDuplicate << endl;
1922 if (nErrorOrder > 0)
1924 Info<< " ***Number of faces with non-consecutive shared points: "
1925 << nErrorOrder << endl;
1932 if (debug || report)
1934 Info<< " Face-face connectivity OK." << endl;
1942 // Checks cells with 1 or less internal faces. Give numerical problems.
1943 bool Foam::primitiveMesh::checkCellDeterminant
1945 const bool report, // report,
1946 labelHashSet* setPtr // setPtr
1951 Info<< "bool primitiveMesh::checkCellDeterminant(const bool"
1952 << ", labelHashSet*) const: "
1953 << "checking for under-determined cells" << endl;
1956 const cellList& c = cells();
1958 label nErrorCells = 0;
1960 scalar minDet = GREAT;
1966 const labelList& curFaces = c[cellI];
1968 // Calculate local normalization factor
1971 label nInternalFaces = 0;
1975 if (isInternalFace(curFaces[i]))
1977 avgArea += mag(faceAreas()[curFaces[i]]);
1983 if (nInternalFaces == 0)
1987 setPtr->insert(cellI);
1994 avgArea /= nInternalFaces;
1996 symmTensor areaTensor(symmTensor::zero);
2000 if (isInternalFace(curFaces[i]))
2002 areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea);
2006 scalar determinant = mag(det(areaTensor));
2008 minDet = min(determinant, minDet);
2009 sumDet += determinant;
2012 if (determinant < 1e-3)
2016 setPtr->insert(cellI);
2024 reduce(nErrorCells, sumOp<label>());
2025 reduce(minDet, minOp<scalar>());
2026 reduce(sumDet, sumOp<scalar>());
2027 reduce(nSummed, sumOp<label>());
2029 if (debug || report)
2033 Info<< " Cell determinant (wellposedness) : minimum: " << minDet
2034 << " average: " << sumDet/nSummed
2039 if (nErrorCells > 0)
2041 if (debug || report)
2043 Info<< " ***Cells with small determinant found, number of cells: "
2044 << nErrorCells << endl;
2051 if (debug || report)
2053 Info<< " Cell determinant check OK." << endl;
2063 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2065 bool Foam::primitiveMesh::checkTopology(const bool report) const
2067 label noFailedChecks = 0;
2069 if (checkPoints(report)) noFailedChecks++;
2070 if (checkUpperTriangular(report)) noFailedChecks++;
2071 if (checkCellsZipUp(report)) noFailedChecks++;
2072 if (checkFaceVertices(report)) noFailedChecks++;
2073 if (checkFaceFaces(report)) noFailedChecks++;
2074 //if (checkCellDeterminant(report)) noFailedChecks++;
2076 if (noFailedChecks == 0)
2078 if (debug || report)
2080 Info<< " Mesh topology OK." << endl;
2087 if (debug || report)
2089 Info<< " Failed " << noFailedChecks
2090 << " mesh topology checks." << endl;
2098 bool Foam::primitiveMesh::checkGeometry(const bool report) const
2100 label noFailedChecks = 0;
2102 if (checkClosedBoundary(report)) noFailedChecks++;
2103 if (checkClosedCells(report)) noFailedChecks++;
2104 if (checkFaceAreas(report)) noFailedChecks++;
2105 if (checkCellVolumes(report)) noFailedChecks++;
2106 if (checkFaceOrthogonality(report)) noFailedChecks++;
2107 if (checkFacePyramids(report)) noFailedChecks++;
2108 if (checkFaceSkewness(report)) noFailedChecks++;
2110 if (noFailedChecks == 0)
2112 if (debug || report)
2114 Info<< " Mesh geometry OK." << endl;
2120 if (debug || report)
2122 Info<< " Failed " << noFailedChecks
2123 << " mesh geometry checks." << endl;
2131 bool Foam::primitiveMesh::checkMesh(const bool report) const
2135 Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
2136 << "checking primitiveMesh" << endl;
2139 label noFailedChecks = checkTopology(report) + checkGeometry(report);
2141 if (noFailedChecks == 0)
2143 if (debug || report)
2145 Info<< "Mesh OK." << endl;
2152 if (debug || report)
2154 Info<< " Failed " << noFailedChecks
2155 << " mesh checks." << endl;
2163 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2166 // ************************************************************************* //