Forward compatibility: flex
[foam-extend-3.2.git] / src / foam / meshes / primitiveMesh / primitiveMeshCheck / primitiveMeshCheck.C
blob2caa1b1faa95755f474ca837fbbd247140994a65
1 /*---------------------------------------------------------------------------*\
2   =========                 |
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 -------------------------------------------------------------------------------
8 License
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"
28 #include "ListOps.H"
29 #include "mathematicalConstants.H"
30 #include "SortableList.H"
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 const Foam::debug::tolerancesSwitch
35 Foam::primitiveMesh::closedThreshold_
37     "primitiveMeshClosedThreshold",
38     1e-6
41 const Foam::debug::tolerancesSwitch
42 Foam::primitiveMesh::aspectThreshold_
44     "primitiveMeshAspectThreshold",
45     1000
48 Foam::debug::tolerancesSwitch
49 Foam::primitiveMesh::nonOrthThreshold_ // deg
51     "primitiveMeshNonOrthThreshold",
52     70
55 const Foam::debug::tolerancesSwitch
56 Foam::primitiveMesh::skewThreshold_
58     "primitiveMeshSkewThreshold",
59     4
62 Foam::debug::tolerancesSwitch
63 Foam::primitiveMesh::faceAngleThreshold_
65     "primitiveMeshFaceAngleThreshold",
66     10
69 const Foam::debug::tolerancesSwitch
70 Foam::primitiveMesh::faceFlatnessThreshold_
72     "primitiveMeshFaceFlatnessThreshold",
73     0.8
77 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
79 bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const
81     if (debug)
82     {
83         Info<< "bool primitiveMesh::checkClosedBoundary("
84             << "const bool) const: "
85             << "checking whether the boundary is closed" << endl;
86     }
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++)
97     {
98         sumClosed += areas[faceI];
99         sumMagClosedBoundary += mag(areas[faceI]);
100     }
102     reduce(sumClosed, sumOp<vector>());
103     reduce(sumMagClosedBoundary, sumOp<scalar>());
105     vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
107     if (cmptMax(cmptMag(openness)) > closedThreshold_())
108     {
109         if (debug || report)
110         {
111             Info<< " ***Boundary openness " << openness
112                 << " Threshold = " << closedThreshold_()
113                 << " possible hole in boundary description."
114                 << endl;
115         }
117         return true;
118     }
119     else
120     {
121         if (debug || report)
122         {
123             Info<< "    Boundary openness " << openness
124                 << " Threshold = " << closedThreshold_()
125                 << " OK."
126                 << endl;
127         }
129         return false;
130     }
134 bool Foam::primitiveMesh::checkClosedCells
136     const bool report,
137     labelHashSet* setPtr,
138     labelHashSet* aspectSetPtr
139 ) const
141     if (debug)
142     {
143         Info<< "bool primitiveMesh::checkClosedCells("
144             << "const bool, labelHashSet*, labelHashSet*) const: "
145             << "checking whether cells are closed" << endl;
146     }
148     // Check that all cells labels are valid
149     const cellList& c = cells();
151     label nErrorClosed = 0;
153     forAll (c, cI)
154     {
155         const cell& curCell = c[cI];
157         if (min(curCell) < 0 || max(curCell) > nFaces())
158         {
159             if (setPtr)
160             {
161                 setPtr->insert(cI);
162             }
164             nErrorClosed++;
165         }
166     }
168     if (nErrorClosed > 0)
169     {
170         if (debug || report)
171         {
172             Info<< " ***Cells with invalid face labels found, number of cells "
173                 << nErrorClosed << endl;
174         }
176         return true;
177     }
179     // Check if a face has got the same cell as owner and neighbour
180     // HJ, 25/May/2011
181     const labelList& own = faceOwner();
182     const labelList& nei = faceNeighbour();
184     label nErrorOwnNei = 0;
186     // Check internal faces only
187     forAll (nei, faceI)
188     {
189         if (own[faceI] == nei[faceI])
190         {
191             if (setPtr)
192             {
193                 setPtr->insert(own[faceI]);
194             }
196             nErrorOwnNei++;
197         }
198     }
200     if (nErrorOwnNei > 0)
201     {
202         if (debug || report)
203         {
204             Info<< " ***Faces declaring same cell as owner and neighbour "
205                 << "found, number of faces "
206                 << nErrorOwnNei << endl;
207         }
209         return true;
210     }
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();
220     forAll (own, faceI)
221     {
222         // Add to owner
223         sumClosed[own[faceI]] += areas[faceI];
224         sumMagClosed[own[faceI]] += cmptMag(areas[faceI]);
225     }
227     forAll (nei, faceI)
228     {
229         // Subtract from neighbour
230         sumClosed[nei[faceI]] -= areas[faceI];
231         sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]);
232     }
234     label nOpen = 0;
235     scalar maxOpennessCell = 0;
237     label nAspect = 0;
238     scalar maxAspectRatio = 0;
240     const scalarField& vols = cellVolumes();
242     // Check the sums
243     forAll (sumClosed, cellI)
244     {
245         scalar maxOpenness = 0;
247         for(direction cmpt=0; cmpt<vector::nComponents; cmpt++)
248         {
249             maxOpenness = max
250             (
251                 maxOpenness,
252                 mag(sumClosed[cellI][cmpt])
253                /(sumMagClosed[cellI][cmpt] + VSMALL)
254             );
255         }
257         maxOpennessCell = max(maxOpennessCell, maxOpenness);
259         if (maxOpenness > closedThreshold_())
260         {
261             if (setPtr)
262             {
263                 setPtr->insert(cellI);
264             }
266             nOpen++;
267         }
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
272         (
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)
277         );
279         maxAspectRatio = max(maxAspectRatio, aspectRatio);
281         if (aspectRatio > aspectThreshold_())
282         {
283             if (aspectSetPtr)
284             {
285                 aspectSetPtr->insert(cellI);
286             }
288             nAspect++;
289         }
290     }
292     reduce(nOpen, sumOp<label>());
293     reduce(maxOpennessCell, maxOp<scalar>());
295     reduce(nAspect, sumOp<label>());
296     reduce(maxAspectRatio, maxOp<scalar>());
299     if (nOpen > 0)
300     {
301         if (debug || report)
302         {
303             Info<< " ***Open cells found, max cell openness: "
304                 << maxOpennessCell << ", number of open cells " << nOpen
305                 << " Threshold = " << closedThreshold_()
306                 << endl;
307         }
309         return true;
310     }
312     if (nAspect > 0)
313     {
314         if (debug || report)
315         {
316             Info<< " ***High aspect ratio cells found, Max aspect ratio: "
317                 << maxAspectRatio
318                 << ", number of cells " << nAspect
319                 << " Threshold = " << aspectThreshold_()
320                 << endl;
321         }
323         return true;
324     }
326     if (debug || report)
327     {
328         Info<< "    Max cell openness = " << maxOpennessCell << " OK." << nl
329             << "    Max aspect ratio = " << maxAspectRatio << " OK."
330             << endl;
331     }
333     return false;
337 bool Foam::primitiveMesh::checkFaceAreas
339     const bool report,
340     labelHashSet* setPtr
341 ) const
343     if (debug)
344     {
345         Info<< "bool primitiveMesh::checkFaceAreas("
346             << "const bool, labelHashSet*) const: "
347             << "checking face area magnitudes" << endl;
348     }
350     const scalarField magFaceAreas = mag(faceAreas());
352     scalar minArea = GREAT;
353     scalar maxArea = -GREAT;
355     forAll (magFaceAreas, faceI)
356     {
357         if (magFaceAreas[faceI] < VSMALL)
358         {
359             if (setPtr)
360             {
361                 setPtr->insert(faceI);
362             }
363         }
365         minArea = min(minArea, magFaceAreas[faceI]);
366         maxArea = max(maxArea, magFaceAreas[faceI]);
367     }
369     reduce(minArea, minOp<scalar>());
370     reduce(maxArea, maxOp<scalar>());
372     if (minArea < VSMALL)
373     {
374         if (debug || report)
375         {
376             Info<< " ***Zero or negative face area detected.  "
377                 "Minimum area: " << minArea << endl;
378         }
380         return true;
381     }
382     else
383     {
384         if (debug || report)
385         {
386             Info<< "    Minumum face area = " << minArea
387                 << ". Maximum face area = " << maxArea
388                 << ".  Face area magnitudes OK." << endl;
389         }
391         return false;
392     }
396 bool Foam::primitiveMesh::checkCellVolumes
398     const bool report,
399     labelHashSet* setPtr
400 ) const
402     if (debug)
403     {
404         Info<< "bool primitiveMesh::checkCellVolumes("
405             << "const bool, labelHashSet*) const: "
406             << "checking cell volumes" << endl;
407     }
409     const scalarField& vols = cellVolumes();
411     scalar minVolume = GREAT;
412     scalar maxVolume = -GREAT;
414     label nNegVolCells = 0;
416     forAll (vols, cellI)
417     {
418         if (vols[cellI] < VSMALL)
419         {
420             if (setPtr)
421             {
422                 setPtr->insert(cellI);
423             }
425             nNegVolCells++;
426         }
428         minVolume = min(minVolume, vols[cellI]);
429         maxVolume = max(maxVolume, vols[cellI]);
430     }
432     reduce(minVolume, minOp<scalar>());
433     reduce(maxVolume, maxOp<scalar>());
434     reduce(nNegVolCells, sumOp<label>());
436     if (minVolume < VSMALL)
437     {
438         if (debug || report)
439         {
440             Info<< " ***Zero or negative cell volume detected.  "
441                 << "Minimum negative volume: " << minVolume
442                 << ", Number of negative volume cells: " << nNegVolCells
443                 << endl;
444         }
446         return true;
447     }
448     else
449     {
450         if (debug || report)
451         {
452             Info<< "    Min volume = " << minVolume
453                 << ". Max volume = " << maxVolume
454                 << ".  Total volume = " << gSum(vols)
455                 << ".  Cell volumes OK." << endl;
456         }
458         return false;
459     }
463 bool Foam::primitiveMesh::checkFaceOrthogonality
465     const bool report,
466     labelHashSet* setPtr
467 ) const
469     if (debug)
470     {
471         Info<< "bool primitiveMesh::checkFaceOrthogonality("
472             << "const bool, labelHashSet*) const: "
473             << "checking mesh non-orthogonality" << endl;
474     }
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;
489     scalar sumDDotS = 0;
491     label severeNonOrth = 0;
493     label errorNonOrth = 0;
495     forAll (nei, faceI)
496     {
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)
503         {
504             if (dDotS > SMALL)
505             {
506                 if (setPtr)
507                 {
508                     setPtr->insert(faceI);
509                 }
511                 severeNonOrth++;
512             }
513             else
514             {
515                 if (setPtr)
516                 {
517                     setPtr->insert(faceI);
518                 }
520                 errorNonOrth++;
521             }
522         }
524         if (dDotS < minDDotS)
525         {
526             minDDotS = dDotS;
527         }
529         sumDDotS += dDotS;
530     }
532     reduce(minDDotS, minOp<scalar>());
533     reduce(sumDDotS, sumOp<scalar>());
534     reduce(severeNonOrth, sumOp<label>());
535     reduce(errorNonOrth, sumOp<label>());
537     if (debug || report)
538     {
539         label neiSize = nei.size();
540         reduce(neiSize, sumOp<label>());
542         if (neiSize > 0)
543         {
544             if (debug || report)
545             {
546                 Info<< "    Mesh non-orthogonality Max: "
547                     << ::acos(minDDotS)/mathematicalConstant::pi*180.0
548                     << " average: " <<
549                     ::acos(sumDDotS/neiSize)/mathematicalConstant::pi*180.0
550                     << " Threshold = " << nonOrthThreshold_()
551                     << endl;
552             }
553         }
555         if (severeNonOrth > 0)
556         {
557             Info<< "   *Number of severely non-orthogonal faces: "
558                 << severeNonOrth << "." << endl;
559         }
560     }
562     if (errorNonOrth > 0)
563     {
564         if (debug || report)
565         {
566             Info<< " ***Number of non-orthogonality errors: "
567                 << errorNonOrth << "." << endl;
568         }
570         return true;
571     }
572     else
573     {
574         if (debug || report)
575         {
576             Info<< "    Non-orthogonality check OK." << endl;
577         }
579         return false;
580     }
584 bool Foam::primitiveMesh::checkFacePyramids
586     const bool report,
587     const scalar minPyrVol,
588     labelHashSet* setPtr
589 ) const
591     if (debug)
592     {
593         Info<< "bool primitiveMesh::checkFacePyramids("
594             << "const bool, const scalar, labelHashSet*) const: "
595             << "checking face orientation" << endl;
596     }
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;
610     forAll (f, faceI)
611     {
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)
616         {
617             if (setPtr)
618             {
619                 setPtr->insert(faceI);
620             }
622             nErrorPyrs++;
623         }
625         if (isInternalFace(faceI))
626         {
627             // Create the neighbour pyramid - it will have positive volume
628             scalar pyrVol =
629                 pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p);
631             if (pyrVol < minPyrVol)
632             {
633                 if (setPtr)
634                 {
635                     setPtr->insert(faceI);
636                 }
638                 nErrorPyrs++;
639             }
640         }
641     }
643     reduce(nErrorPyrs, sumOp<label>());
645     if (nErrorPyrs > 0)
646     {
647         if (debug || report)
648         {
649             Info<< " ***Error in face pyramids: "
650                 << nErrorPyrs << " faces are incorrectly oriented."
651                 << endl;
652         }
654         return true;
655     }
656     else
657     {
658         if (debug || report)
659         {
660             Info<< "    Face pyramids OK." << endl;
661         }
663         return false;
664     }
668 bool Foam::primitiveMesh::checkFaceSkewness
670     const bool report,
671     labelHashSet* setPtr
672 ) const
674     if (debug)
675     {
676         Info<< "bool primitiveMesh::checkFaceSkewnesss("
677             << "const bool, labelHashSet*) const: "
678             << "checking face skewness" << endl;
679     }
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();
693     scalar maxSkew = 0;
694     label nWarnSkew = 0;
696     forAll(nei, faceI)
697     {
698         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
699         vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]];
701         // Skewness vector
702         vector sv =
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
708         // the skewness
709         scalar fd = 0.2*mag(d) + VSMALL;
710         const face& f = fcs[faceI];
711         forAll(f, pi)
712         {
713             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
714         }
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_())
722         {
723             if (setPtr)
724             {
725                 setPtr->insert(faceI);
726             }
728             nWarnSkew++;
729         }
731         if(skewness > maxSkew)
732         {
733             maxSkew = skewness;
734         }
735     }
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++)
742     {
743         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
745         vector normal = fAreas[faceI];
746         normal /= mag(normal) + VSMALL;
747         vector d = normal*(normal & Cpf);
750         // Skewness vector
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
756         // the skewness
757         scalar fd = 0.4*mag(d) + VSMALL;
758         const face& f = fcs[faceI];
759         forAll(f, pi)
760         {
761             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
762         }
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_())
770         {
771             if (setPtr)
772             {
773                 setPtr->insert(faceI);
774             }
776             nWarnSkew++;
777         }
779         if(skewness > maxSkew)
780         {
781             maxSkew = skewness;
782         }
783     }
786     reduce(maxSkew, maxOp<scalar>());
787     reduce(nWarnSkew, sumOp<label>());
789     if (nWarnSkew > 0)
790     {
791         if (debug || report)
792         {
793             Info<< " ***Max skewness = " << maxSkew
794                 << ", " << nWarnSkew << " highly skew faces detected"
795                 << " Threshold = " << skewThreshold_()
796                 << endl;
797         }
799         return true;
800     }
801     else
802     {
803         if (debug || report)
804         {
805             Info<< "    Max skewness = " << maxSkew << " OK." << endl;
806         }
808         return false;
809     }
813 bool Foam::primitiveMesh::checkPoints
815     const bool report,
816     labelHashSet* setPtr
817 ) const
819     if (debug)
820     {
821         Info<< "bool primitiveMesh::checkPoints"
822             << "(const bool, labelHashSet*) const: "
823             << "checking points" << endl;
824     }
826     label nFaceErrors = 0;
827     label nCellErrors = 0;
829     const labelListList& pf = pointFaces();
831     forAll (pf, pointI)
832     {
833         if (pf[pointI].empty())
834         {
835             if (setPtr)
836             {
837                 setPtr->insert(pointI);
838             }
840             nFaceErrors++;
841         }
842     }
845     forAll (pf, pointI)
846     {
847         const labelList& pc = pointCells(pointI);
849         if (pc.empty())
850         {
851             if (setPtr)
852             {
853                 setPtr->insert(pointI);
854             }
856             nCellErrors++;
857         }
858     }
860     reduce(nFaceErrors, sumOp<label>());
861     reduce(nCellErrors, sumOp<label>());
863     if (nFaceErrors > 0 || nCellErrors > 0)
864     {
865         if (debug || report)
866         {
867             Info<< " ***Unused points found in the mesh, "
868                    "number unused by faces: " << nFaceErrors
869                 << " number unused by cells: " << nCellErrors
870                 << endl;
871         }
873         return true;
874     }
875     else
876     {
877         if (debug || report)
878         {
879             Info<< "    Point usage OK." << endl;
880         }
882         return false;
883     }
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
893     const bool report,
894     labelHashSet* setPtr
895 ) const
897     if (debug)
898     {
899         Info<< "bool primitiveMesh::checkFaceAngles"
900             << "(const bool, labelHashSet*) const: "
901             << "checking face angles" << endl;
902     }
904     if (faceAngleThreshold_() < -SMALL || faceAngleThreshold_() > 180 + SMALL)
905     {
906         FatalErrorIn
907         (
908             "primitiveMesh::checkFaceAngles(const bool, labelHashSet*)"
909         )   << "faceAngleThreshold_ should be [0..180] but is now "
910             << faceAngleThreshold_()
911             << exit(FatalError);
912     }
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;
924     label nConcave = 0;
926     label errorFaceI = -1;
928     forAll(fcs, faceI)
929     {
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;
937         forAll(f, fp0)
938         {
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)
948             {
949                 vector edgeNormal = ePrev ^ e10;
950                 scalar magEdgeNormal = mag(edgeNormal);
952                 if (magEdgeNormal < maxSin)
953                 {
954                     // Edges (almost) aligned -> face is ok.
955                 }
956                 else
957                 {
958                     // Check normal
959                     edgeNormal /= magEdgeNormal;
961                     if ((edgeNormal & faceNormals[faceI]) < SMALL)
962                     {
963                         if (faceI != errorFaceI)
964                         {
965                             // Count only one error per face.
966                             errorFaceI = faceI;
967                             nConcave++;
968                         }
970                         if (setPtr)
971                         {
972                             setPtr->insert(faceI);
973                         }
975                         maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
976                     }
977                 }
978             }
980             ePrev = e10;
981             magEPrev = magE10;
982         }
983     }
985     reduce(nConcave, sumOp<label>());
986     reduce(maxEdgeSin, maxOp<scalar>());
988     if (nConcave > 0)
989     {
990         scalar maxConcaveDegr =
991             Foam::asin(Foam::min(1.0, maxEdgeSin))
992            *180.0/mathematicalConstant::pi;
994         if (debug || report)
995         {
996             Info<< "   *There are " << nConcave
997                 << " faces with concave angles between consecutive"
998                 << " edges. Max concave angle = " << maxConcaveDegr
999                 << " degrees." << endl;
1000         }
1002         return true;
1003     }
1004     else
1005     {
1006         if (debug || report)
1007         {
1008             Info<< "    All angles in faces OK." << endl;
1009         }
1011         return false;
1012     }
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
1021     const bool report,
1022     labelHashSet* setPtr
1023 ) const
1025     if (debug)
1026     {
1027         Info<< "bool primitiveMesh::checkFaceFlatness"
1028             << "(const bool, labelHashSet*) const: "
1029             << "checking face flatness" << endl;
1030     }
1032     if (faceFlatnessThreshold_() < 0 || faceFlatnessThreshold_() > 1)
1033     {
1034         FatalErrorIn
1035         (
1036             "primitiveMesh::checkFaceFlatness"
1037             "(const bool, labelHashSet*)"
1038         )   << "faceFlatnessThreshold_ should be [0..1] but is now "
1039             << faceFlatnessThreshold_()
1040             << exit(FatalError);
1041     }
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()));
1052     label nWarped = 0;
1054     scalar minFlatness = GREAT;
1055     scalar sumFlatness = 0;
1056     label nSummed = 0;
1058     forAll(fcs, faceI)
1059     {
1060         const face& f = fcs[faceI];
1062         if (f.size() > 3 && magAreas[faceI] > VSMALL)
1063         {
1064             const point& fc = fctrs[faceI];
1066             // Calculate the sum of magnitude of areas and compare to magnitude
1067             // of sum of areas.
1069             scalar sumA = 0.0;
1071             forAll(f, fp)
1072             {
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));
1078                 sumA += mag(n);
1079             }
1081             scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1083             sumFlatness += flatness;
1084             nSummed++;
1086             minFlatness = min(minFlatness, flatness);
1088             if (flatness < faceFlatnessThreshold_())
1089             {
1090                 nWarped++;
1092                 if (setPtr)
1093                 {
1094                     setPtr->insert(faceI);
1095                 }
1096             }
1097         }
1098     }
1101     reduce(nWarped, sumOp<label>());
1102     reduce(minFlatness, minOp<scalar>());
1104     reduce(nSummed, sumOp<label>());
1105     reduce(sumFlatness, sumOp<scalar>());
1107     if (debug || report)
1108     {
1109         if (nSummed > 0)
1110         {
1111             Info<< "    Face flatness (1 = flat, 0 = butterfly) : average = "
1112                 << sumFlatness / nSummed << "  min = " << minFlatness << endl;
1113         }
1114     }
1117     if (nWarped > 0)
1118     {
1119         if (debug || report)
1120         {
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;
1127         }
1129         return true;
1130     }
1131     else
1132     {
1133         if (debug || report)
1134         {
1135             Info<< "    All face flatness OK." << endl;
1136         }
1138         return false;
1139     }
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
1151     const bool report,
1152     const Vector<label>& directions,
1153     labelHashSet* setPtr
1154 ) const
1156     if (debug)
1157     {
1158         Info<< "bool primitiveMesh::checkEdgeAlignment("
1159             << "const bool, const Vector<label>&, labelHashSet*) const: "
1160             << "checking edge alignment" << endl;
1161     }
1163     label nDirs = 0;
1164     for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1165     {
1166         if (directions[cmpt] == 1)
1167         {
1168             nDirs++;
1169         }
1170         else if (directions[cmpt] != 0)
1171         {
1172             FatalErrorIn
1173             (
1174                 "primitiveMesh::checkEdgeAlignment"
1175                 "(const bool, const Vector<label>&, labelHashSet*)"
1176             )   << "directions should contain 0 or 1 but is now " << directions
1177                 << exit(FatalError);
1178         }
1179     }
1181     if (nDirs == vector::nComponents)
1182     {
1183         return false;
1184     }
1188     const pointField& p = points();
1189     const faceList& fcs = faces();
1191     EdgeMap<label> edgesInError;
1193     forAll(fcs, faceI)
1194     {
1195         const face& f = fcs[faceI];
1197         forAll(f, fp)
1198         {
1199             label p0 = f[fp];
1200             label p1 = f.nextLabel(fp);
1201             if (p0 < p1)
1202             {
1203                 vector d(p[p1]-p[p0]);
1204                 scalar magD = mag(d);
1206                 if (magD > ROOTVSMALL)
1207                 {
1208                     d /= magD;
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++)
1214                     {
1215                         if (mag(d[cmpt]) > 1e-6)
1216                         {
1217                             if (directions[cmpt] == 0)
1218                             {
1219                                 nEmptyDirs++;
1220                             }
1221                             else
1222                             {
1223                                 nNonEmptyDirs++;
1224                             }
1225                         }
1226                     }
1228                     if (nEmptyDirs == 0)
1229                     {
1230                         // Purely in ok directions.
1231                     }
1232                     else if (nEmptyDirs == 1)
1233                     {
1234                         // Ok if purely in empty directions.
1235                         if (nNonEmptyDirs > 0)
1236                         {
1237                             edgesInError.insert(edge(p0, p1), faceI);
1238                         }
1239                     }
1240                     else if (nEmptyDirs > 1)
1241                     {
1242                         // Always an error
1243                         edgesInError.insert(edge(p0, p1), faceI);
1244                     }
1245                 }
1246             }
1247         }
1248     }
1250     label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>());
1252     if (nErrorEdges > 0)
1253     {
1254         if (debug || report)
1255         {
1256             Info<< " ***Number of edges not aligned with or perpendicular to "
1257                 << "non-empty directions: " << nErrorEdges << endl;
1258         }
1260         if (setPtr)
1261         {
1262             setPtr->resize(2*edgesInError.size());
1263             forAllConstIter(EdgeMap<label>, edgesInError, iter)
1264             {
1265                 setPtr->insert(iter.key()[0]);
1266                 setPtr->insert(iter.key()[1]);
1267             }
1268         }
1270         return true;
1271     }
1272     else
1273     {
1274         if (debug || report)
1275         {
1276             Info<< "    All edges aligned with or perpendicular to "
1277                 << "non-empty directions." << endl;
1278         }
1279         return false;
1280     }
1284 bool Foam::primitiveMesh::checkUpperTriangular
1286     const bool report,
1287     labelHashSet* setPtr
1288 ) const
1290     if (debug)
1291     {
1292         Info<< "bool primitiveMesh::checkUpperTriangular("
1293             << "const bool, labelHashSet*) const: "
1294             << "checking face ordering" << endl;
1295     }
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?
1305     bool error = false;
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++)
1312     {
1313         if (own[faceI] >= nei[faceI])
1314         {
1315             error  = true;
1317             if (setPtr)
1318             {
1319                 setPtr->insert(faceI);
1320             }
1321         }
1322     }
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
1328     forAll (c, cellI)
1329     {
1330         const labelList& curFaces = c[cellI];
1332         // Neighbouring cells
1333         SortableList<label> nbr(curFaces.size());
1335         forAll(curFaces, i)
1336         {
1337             label faceI = curFaces[i];
1339             if (faceI >= nInternalFaces())
1340             {
1341                 // Sort last
1342                 nbr[i] = labelMax;
1343             }
1344             else
1345             {
1346                 label nbrCellI = nei[faceI];
1348                 if (nbrCellI == cellI)
1349                 {
1350                     nbrCellI = own[faceI];
1351                 }
1353                 if (cellI < nbrCellI)
1354                 {
1355                     // cellI is master
1356                     nbr[i] = nbrCellI;
1357                 }
1358                 else
1359                 {
1360                     // nbrCell is master. Let it handle this face.
1361                     nbr[i] = labelMax;
1362                 }
1363             }
1364         }
1366         if (!nbr.empty())
1367         {
1368             nbr.sort();
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++)
1381             {
1382                 label thisCell = nbr[i];
1383                 label thisFace = curFaces[nbr.indices()[i]];
1385                 if (thisCell == labelMax)
1386                 {
1387                     break;
1388                 }
1390                 if (thisCell == prevCell)
1391                 {
1392                     hasMultipleFaces = true;
1394                     if (setPtr)
1395                     {
1396                         setPtr->insert(prevFace);
1397                         setPtr->insert(thisFace);
1398                     }
1399                 }
1400                 else if (thisFace < prevFace)
1401                 {
1402                     error = true;
1404                     if (setPtr)
1405                     {
1406                         setPtr->insert(thisFace);
1407                     }
1408                 }
1410                 prevCell = thisCell;
1411                 prevFace = thisFace;
1412             }
1414             if (hasMultipleFaces)
1415             {
1416                 nMultipleCells++;
1417             }
1418         }
1419     }
1421     reduce(error, orOp<bool>());
1422     reduce(nMultipleCells, sumOp<label>());
1424     if ((debug || report) && nMultipleCells > 0)
1425     {
1426         Info<< "  <<Found " << nMultipleCells
1427             << " neighbouring cells with multiple inbetween faces." << endl;
1428     }
1430     if (error)
1431     {
1432         if (debug || report)
1433         {
1434             Info<< " ***Faces not in upper triangular order." << endl;
1435         }
1437         return true;
1438     }
1439     else
1440     {
1441         if (debug || report)
1442         {
1443             Info<< "    Upper triangular ordering OK." << endl;
1444         }
1446         return false;
1447     }
1451 bool Foam::primitiveMesh::checkCellsZipUp
1453     const bool report,
1454     labelHashSet* setPtr
1455 ) const
1457     if (debug)
1458     {
1459         Info<< "bool primitiveMesh::checkCellsZipUp("
1460             << "const bool, labelHashSet*) const: "
1461             << "checking topological cell openness" << endl;
1462     }
1464     label nOpenCells = 0;
1466     const faceList& f = faces();
1467     const cellList& c = cells();
1469     forAll (c, cellI)
1470     {
1471         const labelList& curFaces = c[cellI];
1473         const edgeList cellEdges = c[cellI].edges(f);
1475         labelList edgeUsage(cellEdges.size(), 0);
1477         forAll (curFaces, faceI)
1478         {
1479             edgeList curFaceEdges = f[curFaces[faceI]].edges();
1481             forAll (curFaceEdges, faceEdgeI)
1482             {
1483                 const edge& curEdge = curFaceEdges[faceEdgeI];
1485                 forAll (cellEdges, cellEdgeI)
1486                 {
1487                     if (cellEdges[cellEdgeI] == curEdge)
1488                     {
1489                         edgeUsage[cellEdgeI]++;
1490                         break;
1491                     }
1492                 }
1493             }
1494         }
1496         edgeList singleEdges(cellEdges.size());
1497         label nSingleEdges = 0;
1499         forAll (edgeUsage, edgeI)
1500         {
1501             if (edgeUsage[edgeI] == 1)
1502             {
1503                 singleEdges[nSingleEdges] = cellEdges[edgeI];
1504                 nSingleEdges++;
1505             }
1506             else if (edgeUsage[edgeI] != 2)
1507             {
1508                 if (setPtr)
1509                 {
1510                     setPtr->insert(cellI);
1511                 }
1512             }
1513         }
1515         if (nSingleEdges > 0)
1516         {
1517             if (setPtr)
1518             {
1519                 setPtr->insert(cellI);
1520             }
1522             nOpenCells++;
1523         }
1524     }
1526     reduce(nOpenCells, sumOp<label>());
1528     if (nOpenCells > 0)
1529     {
1530         if (debug || report)
1531         {
1532             Info<< " ***Open cells found, number of cells: " << nOpenCells
1533                 << ". This problem may be fixable using the zipUpMesh utility."
1534                 << endl;
1535         }
1537         return true;
1538     }
1539     else
1540     {
1541         if (debug || report)
1542         {
1543             Info<< "    Topological cell zip-up check OK." << endl;
1544         }
1546         return false;
1547     }
1551 // Vertices of face within point range and unique.
1552 bool Foam::primitiveMesh::checkFaceVertices
1554     const bool report,
1555     labelHashSet* setPtr
1556 ) const
1558     if (debug)
1559     {
1560         Info<< "bool primitiveMesh::checkFaceVertices("
1561             << "const bool, labelHashSet*) const: "
1562             << "checking face vertices" << endl;
1563     }
1565     // Check that all vertex labels are valid
1566     const faceList& f = faces();
1568     label nErrorFaces = 0;
1570     forAll (f, fI)
1571     {
1572         const face& curFace = f[fI];
1574         if (min(curFace) < 0 || max(curFace) > nPoints())
1575         {
1576             if (setPtr)
1577             {
1578                 setPtr->insert(fI);
1579             }
1581             nErrorFaces++;
1582         }
1584         // Uniqueness of vertices
1585         labelHashSet facePoints(2*curFace.size());
1587         forAll(curFace, fp)
1588         {
1589             bool inserted = facePoints.insert(curFace[fp]);
1591             if (!inserted)
1592             {
1593                 if (setPtr)
1594                 {
1595                     setPtr->insert(fI);
1596                 }
1598                 nErrorFaces++;
1599             }
1600         }
1601     }
1603     reduce(nErrorFaces, sumOp<label>());
1605     if (nErrorFaces > 0)
1606     {
1607         if (debug || report)
1608         {
1609             Info<< "    Faces with invalid vertex labels found, "
1610                 << " number of faces: " << nErrorFaces << endl;
1611         }
1613         return true;
1614     }
1615     else
1616     {
1617         if (debug || report)
1618         {
1619             Info<< "    Face vertices OK." << endl;
1620         }
1622         return false;
1623     }
1627 // Check if all points on face are shared between faces.
1628 bool Foam::primitiveMesh::checkDuplicateFaces
1630     const label faceI,
1631     const Map<label>& nCommonPoints,
1632     label& nBaffleFaces,
1633     labelHashSet* setPtr
1634 ) const
1636     bool error = false;
1638     forAllConstIter(Map<label>, nCommonPoints, iter)
1639     {
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())
1647         {
1648             if (nbFace.size() != curFace.size())
1649             {
1650                 error = true;
1651             }
1652             else
1653             {
1654                 nBaffleFaces++;
1655             }
1657             if (setPtr)
1658             {
1659                 setPtr->insert(faceI);
1660                 setPtr->insert(nbFaceI);
1661             }
1662         }
1663     }
1665     return error;
1669 // Check that shared points are in consecutive order.
1670 bool Foam::primitiveMesh::checkCommonOrder
1672     const label faceI,
1673     const Map<label>& nCommonPoints,
1674     labelHashSet* setPtr
1675 ) const
1677     bool error = false;
1679     forAllConstIter(Map<label>, nCommonPoints, iter)
1680     {
1681         label nbFaceI = iter.key();
1682         label nCommon = iter();
1684         const face& curFace = faces()[faceI];
1685         const face& nbFace = faces()[nbFaceI];
1687         if
1688         (
1689             nCommon >= 2
1690          && nCommon != nbFace.size()
1691          && nCommon != curFace.size()
1692         )
1693         {
1694             forAll(curFace, fp)
1695             {
1696                 // Get the index in the neighbouring face shared with curFace
1697                 label nb = findIndex(nbFace, curFace[fp]);
1699                 if (nb != -1)
1700                 {
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
1707                     // other face.
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
1719                     // faces.
1720                     label curInc = labelMax;
1721                     label nbInc = labelMax;
1723                     if (nbFace[nbPlus1] == curFace[fpPlus1])
1724                     {
1725                         curInc = 1;
1726                         nbInc = 1;
1727                     }
1728                     else if (nbFace[nbPlus1] == curFace[fpMin1])
1729                     {
1730                         curInc = -1;
1731                         nbInc = 1;
1732                     }
1733                     else if (nbFace[nbMin1] == curFace[fpMin1])
1734                     {
1735                         curInc = -1;
1736                         nbInc = -1;
1737                     }
1738                     else
1739                     {
1740                         curInc = 1;
1741                         nbInc = -1;
1742                     }
1745                     // Pass1: loop until start of common vertices found.
1746                     label curNb = nb;
1747                     label curFp = fp;
1749                     do
1750                     {
1751                         curFp += curInc;
1753                         if (curFp >= curFace.size())
1754                         {
1755                             curFp = 0;
1756                         }
1757                         else if (curFp < 0)
1758                         {
1759                             curFp = curFace.size()-1;
1760                         }
1762                         curNb += nbInc;
1764                         if (curNb >= nbFace.size())
1765                         {
1766                             curNb = 0;
1767                         }
1768                         else if (curNb < 0)
1769                         {
1770                             curNb = nbFace.size()-1;
1771                         }
1772                     } while (curFace[curFp] == nbFace[curNb]);
1775                     // Pass2: check equality walking from curFp, curNb
1776                     // in opposite order.
1778                     curInc = -curInc;
1779                     nbInc = -nbInc;
1781                     for (label commonI = 0; commonI < nCommon; commonI++)
1782                     {
1783                         curFp += curInc;
1785                         if (curFp >= curFace.size())
1786                         {
1787                             curFp = 0;
1788                         }
1789                         else if (curFp < 0)
1790                         {
1791                             curFp = curFace.size()-1;
1792                         }
1794                         curNb += nbInc;
1796                         if (curNb >= nbFace.size())
1797                         {
1798                             curNb = 0;
1799                         }
1800                         else if (curNb < 0)
1801                         {
1802                             curNb = nbFace.size()-1;
1803                         }
1805                         if (curFace[curFp] != nbFace[curNb])
1806                         {
1807                             if (setPtr)
1808                             {
1809                                 setPtr->insert(faceI);
1810                                 setPtr->insert(nbFaceI);
1811                             }
1813                             error = true;
1815                             break;
1816                         }
1817                     }
1820                     // Done the curFace - nbFace combination.
1821                     break;
1822                 }
1823             }
1824         }
1825     }
1827     return error;
1831 // Checks common vertices between faces. If more than 2 they should be
1832 // consecutive on both faces.
1833 bool Foam::primitiveMesh::checkFaceFaces
1835     const bool report,
1836     labelHashSet* setPtr
1837 ) const
1839     if (debug)
1840     {
1841         Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
1842             << " const: " << "checking face-face connectivity" << endl;
1843     }
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++)
1853     {
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();
1860         forAll(curFace, fp)
1861         {
1862             label pointI = curFace[fp];
1864             const labelList& nbs = pf[pointI];
1866             forAll(nbs, nbI)
1867             {
1868                 label nbFaceI = nbs[nbI];
1870                 if (faceI < nbFaceI)
1871                 {
1872                     // Only check once for each combination of two faces.
1874                     Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
1876                     if (fnd == nCommonPoints.end())
1877                     {
1878                         // First common vertex found.
1879                         nCommonPoints.insert(nbFaceI, 1);
1880                     }
1881                     else
1882                     {
1883                         fnd()++;
1884                     }
1885                 }
1886             }
1887         }
1889         // Perform various checks on common points
1891         // Check all vertices shared (duplicate point)
1892         if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
1893         {
1894             nErrorDuplicate++;
1895         }
1897         // Check common vertices are consecutive on both faces
1898         if (checkCommonOrder(faceI, nCommonPoints, setPtr))
1899         {
1900             nErrorOrder++;
1901         }
1902     }
1904     reduce(nBaffleFaces, sumOp<label>());
1905     reduce(nErrorDuplicate, sumOp<label>());
1906     reduce(nErrorOrder, sumOp<label>());
1908     if (nBaffleFaces)
1909     {
1910         Info<< "    Number of identical duplicate faces (baffle faces): "
1911             << nBaffleFaces << endl;
1912     }
1914     if (nErrorDuplicate > 0 || nErrorOrder > 0)
1915     {
1916         if (nErrorDuplicate > 0)
1917         {
1918             Info<< " ***Number of duplicate (not baffle) faces found: "
1919                 << nErrorDuplicate << endl;
1920         }
1922         if (nErrorOrder > 0)
1923         {
1924             Info<< " ***Number of faces with non-consecutive shared points: "
1925                 << nErrorOrder << endl;
1926         }
1928         return true;
1929     }
1930     else
1931     {
1932         if (debug || report)
1933         {
1934             Info<< "    Face-face connectivity OK." << endl;
1935         }
1937         return false;
1938     }
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
1947 ) const
1949     if (debug)
1950     {
1951         Info<< "bool primitiveMesh::checkCellDeterminant(const bool"
1952             << ", labelHashSet*) const: "
1953             << "checking for under-determined cells" << endl;
1954     }
1956     const cellList& c = cells();
1958     label nErrorCells = 0;
1960     scalar minDet = GREAT;
1961     scalar sumDet = 0;
1962     label nSummed = 0;
1964     forAll (c, cellI)
1965     {
1966         const labelList& curFaces = c[cellI];
1968         // Calculate local normalization factor
1969         scalar avgArea = 0;
1971         label nInternalFaces = 0;
1973         forAll(curFaces, i)
1974         {
1975             if (isInternalFace(curFaces[i]))
1976             {
1977                 avgArea += mag(faceAreas()[curFaces[i]]);
1979                 nInternalFaces++;
1980             }
1981         }
1983         if (nInternalFaces == 0)
1984         {
1985             if (setPtr)
1986             {
1987                 setPtr->insert(cellI);
1988             }
1990             nErrorCells++;
1991         }
1992         else
1993         {
1994             avgArea /= nInternalFaces;
1996             symmTensor areaTensor(symmTensor::zero);
1998             forAll(curFaces, i)
1999             {
2000                 if (isInternalFace(curFaces[i]))
2001                 {
2002                     areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea);
2003                 }
2004             }
2006             scalar determinant = mag(det(areaTensor));
2008             minDet = min(determinant, minDet);
2009             sumDet += determinant;
2010             nSummed++;
2012             if (determinant < 1e-3)
2013             {
2014                 if (setPtr)
2015                 {
2016                     setPtr->insert(cellI);
2017                 }
2019                 nErrorCells++;
2020             }
2021         }
2022     }
2024     reduce(nErrorCells, sumOp<label>());
2025     reduce(minDet, minOp<scalar>());
2026     reduce(sumDet, sumOp<scalar>());
2027     reduce(nSummed, sumOp<label>());
2029     if (debug || report)
2030     {
2031         if (nSummed > 0)
2032         {
2033             Info<< "    Cell determinant (wellposedness) : minimum: " << minDet
2034                 << " average: " << sumDet/nSummed
2035                 << endl;
2036         }
2037     }
2039     if (nErrorCells > 0)
2040     {
2041         if (debug || report)
2042         {
2043             Info<< " ***Cells with small determinant found, number of cells: "
2044                 << nErrorCells << endl;
2045         }
2047         return true;
2048     }
2049     else
2050     {
2051         if (debug || report)
2052         {
2053             Info<< "    Cell determinant check OK." << endl;
2054         }
2056         return false;
2057     }
2059     return false;
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)
2077     {
2078         if (debug || report)
2079         {
2080             Info<< "    Mesh topology OK." << endl;
2081         }
2083         return false;
2084     }
2085     else
2086     {
2087         if (debug || report)
2088         {
2089             Info<< "    Failed " << noFailedChecks
2090                 << " mesh topology checks." << endl;
2091         }
2093         return true;
2094     }
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)
2111     {
2112         if (debug || report)
2113         {
2114             Info<< "    Mesh geometry OK." << endl;
2115         }
2116         return false;
2117     }
2118     else
2119     {
2120         if (debug || report)
2121         {
2122             Info<< "    Failed " << noFailedChecks
2123                 << " mesh geometry checks." << endl;
2124         }
2126         return true;
2127     }
2131 bool Foam::primitiveMesh::checkMesh(const bool report) const
2133     if (debug)
2134     {
2135         Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
2136             << "checking primitiveMesh" << endl;
2137     }
2139     label noFailedChecks = checkTopology(report) + checkGeometry(report);
2141     if (noFailedChecks == 0)
2142     {
2143         if (debug || report)
2144         {
2145             Info<< "Mesh OK." << endl;
2146         }
2148         return false;
2149     }
2150     else
2151     {
2152         if (debug || report)
2153         {
2154             Info<< "    Failed " << noFailedChecks
2155                 << " mesh checks." << endl;
2156         }
2158         return true;
2159     }
2163 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2166 // ************************************************************************* //