initial commit for version 1.5.x patch release
[OpenFOAM-1.5.x.git] / src / OpenFOAM / meshes / primitiveMesh / primitiveMeshCheck / primitiveMeshCheck.C
blobfd56846ebb0a448172763cf6c401c9ffeb08f4f9
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2008 OpenCFD Ltd.
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of OpenFOAM.
11     OpenFOAM is free software; you can redistribute it and/or modify it
12     under the terms of the GNU General Public License as published by the
13     Free Software Foundation; either version 2 of the License, or (at your
14     option) any later version.
16     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19     for more details.
21     You should have received a copy of the GNU General Public License
22     along with OpenFOAM; if not, write to the Free Software Foundation,
23     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "primitiveMesh.H"
28 #include "pyramidPointFaceRef.H"
29 #include "ListOps.H"
30 #include "mathematicalConstants.H"
31 #include "SortableList.H"
33 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
35 namespace Foam
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 scalar primitiveMesh::closedThreshold_  = 1.0e-6;
41 scalar primitiveMesh::aspectThreshold_  = 1000;
42 scalar primitiveMesh::nonOrthThreshold_ = 70;    // deg
43 scalar primitiveMesh::skewThreshold_    = 4;
46 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
48 bool primitiveMesh::checkClosedBoundary(const bool report) const
50     if (debug)
51     {
52         Info<< "bool primitiveMesh::checkClosedBoundary("
53             << "const bool) const: "
54             << "checking whether the boundary is closed" << endl;
55     }
57     // Loop through all boundary faces and sum up the face area vectors.
58     // For a closed boundary, this should be zero in all vector components
60     vector sumClosed(vector::zero);
61     scalar sumMagClosedBoundary = 0;
63     const vectorField& areas = faceAreas();
65     for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++)
66     {
67         sumClosed += areas[faceI];
68         sumMagClosedBoundary += mag(areas[faceI]);
69     }
71     reduce(sumClosed, sumOp<vector>());
72     reduce(sumMagClosedBoundary, sumOp<scalar>());
74     vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
76     if (cmptMax(cmptMag(openness)) > closedThreshold_)
77     {
78         if (debug || report)
79         {
80             Info<< " ***Boundary openness " << openness
81                 << " possible hole in boundary description."
82                 << endl;
83         }
85         return true;
86     }
87     else
88     {
89         if (debug || report)
90         {
91             Info<< "    Boundary openness " << openness << " OK."
92                 << endl;
93         }
95         return false;
96     }
100 bool primitiveMesh::checkClosedCells
102     const bool report,
103     labelHashSet* setPtr,
104     labelHashSet* aspectSetPtr
105 ) const
107     if (debug)
108     {
109         Info<< "bool primitiveMesh::checkClosedCells("
110             << "const bool, labelHashSet*, labelHashSet*) const: "
111             << "checking whether cells are closed" << endl;
112     }
114     // Check that all cells labels are valid
115     const cellList& c = cells();
117     label nErrorClosed = 0;
119     forAll (c, cI)
120     {
121         const cell& curCell = c[cI];
123         if (min(curCell) < 0 || max(curCell) > nFaces())
124         {
125             if (setPtr)
126             {
127                 setPtr->insert(cI);
128             }
130             nErrorClosed++;
131         }
132     }
134     if (nErrorClosed > 0)
135     {
136         if (debug || report)
137         {
138             Info<< " ***Cells with invalid face labels found, number of cells "
139                 << nErrorClosed << endl;
140         }
142         return true;
143     }
145     // Loop through cell faces and sum up the face area vectors for each cell.
146     // This should be zero in all vector components
148     vectorField sumClosed(nCells(), vector::zero);
149     vectorField sumMagClosed(nCells(), vector::zero);
151     const labelList& own = faceOwner();
152     const labelList& nei = faceNeighbour();
153     const vectorField& areas = faceAreas();
155     forAll (own, faceI)
156     {
157         // Add to owner
158         sumClosed[own[faceI]] += areas[faceI];
159         sumMagClosed[own[faceI]] += cmptMag(areas[faceI]);
160     }
162     forAll (nei, faceI)
163     {
164         // Subtract from neighbour
165         sumClosed[nei[faceI]] -= areas[faceI];
166         sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]);
167     }
169     label nOpen = 0;
170     scalar maxOpennessCell = 0;
172     label nAspect = 0;
173     scalar maxAspectRatio = 0;
175     const scalarField& vols = cellVolumes();
177     // Check the sums
178     forAll (sumClosed, cellI)
179     {
180         scalar maxOpenness = 0;
182         for(direction cmpt=0; cmpt<vector::nComponents; cmpt++)
183         {
184             maxOpenness = max
185             (
186                 maxOpenness,
187                 mag(sumClosed[cellI][cmpt])
188                /(sumMagClosed[cellI][cmpt] + VSMALL)
189             );
190         }
192         maxOpennessCell = max(maxOpennessCell, maxOpenness);
194         if (maxOpenness > closedThreshold_)
195         {
196             if (setPtr)
197             {
198                 setPtr->insert(cellI);
199             }
201             nOpen++;
202         }
204         // Calculate the aspect ration as the maximum of Cartesian component
205         // aspect ratio to the total area hydraulic area aspect ratio
206         scalar aspectRatio = max
207         (
208             cmptMax(sumMagClosed[cellI])
209            /(cmptMin(sumMagClosed[cellI]) + VSMALL),
210             1.0/6.0*cmptSum(sumMagClosed[cellI])/pow(vols[cellI], 2.0/3.0)
211         );
213         maxAspectRatio = max(maxAspectRatio, aspectRatio);
215         if (aspectRatio > aspectThreshold_)
216         {
217             if (aspectSetPtr)
218             {
219                 aspectSetPtr->insert(cellI);
220             }
222             nAspect++;
223         }
224     }
226     reduce(nOpen, sumOp<label>());
227     reduce(maxOpennessCell, maxOp<scalar>());
229     reduce(nAspect, sumOp<label>());
230     reduce(maxAspectRatio, maxOp<scalar>());
233     if (nOpen > 0)
234     {
235         if (debug || report)
236         {
237             Info<< " ***Open cells found, max cell openness: "
238                 << maxOpennessCell << ", number of open cells " << nOpen
239                 << endl;
240         }
242         return true;
243     }
245     if (nAspect > 0)
246     {
247         if (debug || report)
248         {
249             Info<< " ***High aspect ratio cells found, Max aspect ratio: "
250                 << maxAspectRatio
251                 << ", number of cells " << nAspect
252                 << endl;
253         }
255         return true;
256     }
258     if (debug || report)
259     {
260         Info<< "    Max cell openness = " << maxOpennessCell << " OK." << nl
261             << "    Max aspect ratio = " << maxAspectRatio << " OK."
262             << endl;
263     }
265     return false;
269 bool primitiveMesh::checkFaceAreas
271     const bool report,
272     labelHashSet* setPtr
273 ) const
275     if (debug)
276     {
277         Info<< "bool primitiveMesh::checkFaceAreas("
278             << "const bool, labelHashSet*) const: "
279             << "checking face area magnitudes" << endl;
280     }
282     const scalarField magFaceAreas = mag(faceAreas());
284     scalar minArea = GREAT;
285     scalar maxArea = -GREAT;
287     forAll (magFaceAreas, faceI)
288     {
289         if (magFaceAreas[faceI] < VSMALL)
290         {
291             if (setPtr)
292             {
293                 setPtr->insert(faceI);
294             }
295         }
297         minArea = min(minArea, magFaceAreas[faceI]);
298         maxArea = max(maxArea, magFaceAreas[faceI]);
299     }
301     reduce(minArea, minOp<scalar>());
302     reduce(maxArea, maxOp<scalar>());
304     if (minArea < VSMALL)
305     {
306         if (debug || report)
307         {
308             Info<< " ***Zero or negative face area detected.  "
309                 "Minimum area: " << minArea << endl;
310         }
312         return true;
313     }
314     else
315     {
316         if (debug || report)
317         {
318             Info<< "    Minumum face area = " << minArea
319                 << ". Maximum face area = " << maxArea
320                 << ".  Face area magnitudes OK." << endl;
321         }
323         return false;
324     }
328 bool primitiveMesh::checkCellVolumes
330     const bool report,
331     labelHashSet* setPtr
332 ) const
334     if (debug)
335     {
336         Info<< "bool primitiveMesh::checkCellVolumes("
337             << "const bool, labelHashSet*) const: "
338             << "checking cell volumes" << endl;
339     }
341     const scalarField& vols = cellVolumes();
343     scalar minVolume = GREAT;
344     scalar maxVolume = -GREAT;
346     label nNegVolCells = 0;
348     forAll (vols, cellI)
349     {
350         if (vols[cellI] < VSMALL)
351         {
352             if (setPtr)
353             {
354                 setPtr->insert(cellI);
355             }
357             nNegVolCells++;
358         }
360         minVolume = min(minVolume, vols[cellI]);
361         maxVolume = max(maxVolume, vols[cellI]);
362     }
364     reduce(minVolume, minOp<scalar>());
365     reduce(maxVolume, maxOp<scalar>());
366     reduce(nNegVolCells, sumOp<label>());
368     if (minVolume < VSMALL)
369     {
370         if (debug || report)
371         {
372             Info<< " ***Zero or negative cell volume detected.  "
373                 << "Minimum negative volume: " << minVolume
374                 << ", Number of negative volume cells: " << nNegVolCells
375                 << endl;
376         }
378         return true;
379     }
380     else
381     {
382         if (debug || report)
383         {
384             Info<< "    Min volume = " << minVolume
385                 << ". Max volume = " << maxVolume
386                 << ".  Total volume = " << gSum(vols)
387                 << ".  Cell volumes OK." << endl;
388         }
390         return false;
391     }
395 bool primitiveMesh::checkFaceOrthogonality
397     const bool report,
398     labelHashSet* setPtr
399 ) const
401     if (debug)
402     {
403         Info<< "bool primitiveMesh::checkFaceOrthogonality("
404             << "const bool, labelHashSet*) const: "
405             << "checking mesh non-orthogonality" << endl;
406     }
408     // for all internal faces check theat the d dot S product is positive
409     const vectorField& centres = cellCentres();
410     const vectorField& areas = faceAreas();
412     const labelList& own = faceOwner();
413     const labelList& nei = faceNeighbour();
415     // Severe nonorthogonality threshold
416     const scalar severeNonorthogonalityThreshold =
417         ::cos(nonOrthThreshold_/180.0*mathematicalConstant::pi);
419     scalar minDDotS = GREAT;
421     scalar sumDDotS = 0;
423     label severeNonOrth = 0;
425     label errorNonOrth = 0;
427     forAll (nei, faceI)
428     {
429         vector d = centres[nei[faceI]] - centres[own[faceI]];
430         const vector& s = areas[faceI];
432         scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL);
434         if (dDotS < severeNonorthogonalityThreshold)
435         {
436             if (dDotS > SMALL)
437             {
438                 if (setPtr)
439                 {
440                     setPtr->insert(faceI);
441                 }
443                 severeNonOrth++;
444             }
445             else
446             {
447                 if (setPtr)
448                 {
449                     setPtr->insert(faceI);
450                 }
452                 errorNonOrth++;
453             }
454         }
456         if (dDotS < minDDotS)
457         {
458             minDDotS = dDotS;
459         }
461         sumDDotS += dDotS;
462     }
464     reduce(minDDotS, minOp<scalar>());
465     reduce(sumDDotS, sumOp<scalar>());
466     reduce(severeNonOrth, sumOp<label>());
467     reduce(errorNonOrth, sumOp<label>());
469     if (debug || report)
470     {
471         label neiSize = nei.size();
472         reduce(neiSize, sumOp<label>());
474         if (neiSize > 0)
475         {
476             if (debug || report)
477             {
478                 Info<< "    Mesh non-orthogonality Max: "
479                     << ::acos(minDDotS)/mathematicalConstant::pi*180.0
480                     << " average: " <<
481                     ::acos(sumDDotS/neiSize)/mathematicalConstant::pi*180.0
482                     << endl;
483             }
484         }
486         if (severeNonOrth > 0)
487         {
488             Info<< "   *Number of severely non-orthogonal faces: "
489                 << severeNonOrth << "." << endl;
490         }
491     }
493     if (errorNonOrth > 0)
494     {
495         if (debug || report)
496         {
497             Info<< " ***Number of non-orthogonality errors: "
498                 << errorNonOrth << "." << endl;
499         }
501         return true;
502     }
503     else
504     {
505         if (debug || report)
506         {
507             Info<< "    Non-orthogonality check OK." << endl;
508         }
510         return false;
511     }
515 bool primitiveMesh::checkFacePyramids
517     const bool report,
518     const scalar minPyrVol,
519     labelHashSet* setPtr
520 ) const
522     if (debug)
523     {
524         Info<< "bool primitiveMesh::checkFacePyramids("
525             << "const bool, const scalar, labelHashSet*) const: "
526             << "checking face orientation" << endl;
527     }
529     // check whether face area vector points to the cell with higher label
530     const vectorField& ctrs = cellCentres();
532     const labelList& own = faceOwner();
533     const labelList& nei = faceNeighbour();
535     const faceList& f = faces();
537     const pointField& p = points();
539     label nErrorPyrs = 0;
541     forAll (f, faceI)
542     {
543         // Create the owner pyramid - it will have negative volume
544         scalar pyrVol = pyramidPointFaceRef(f[faceI], ctrs[own[faceI]]).mag(p);
546         if (pyrVol > -minPyrVol)
547         {
548             if (setPtr)
549             {
550                 setPtr->insert(faceI);
551             }
553             nErrorPyrs++;
554         }
556         if (isInternalFace(faceI))
557         {
558             // Create the neighbour pyramid - it will have positive volume
559             scalar pyrVol =
560                 pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p);
562             if (pyrVol < minPyrVol)
563             {
564                 if (setPtr)
565                 {
566                     setPtr->insert(faceI);
567                 }
569                 nErrorPyrs++;
570             }
571         }
572     }
574     reduce(nErrorPyrs, sumOp<label>());
576     if (nErrorPyrs > 0)
577     {
578         if (debug || report)
579         {
580             Info<< " ***Error in face pyramids: "
581                 << nErrorPyrs << " faces are incorrectly oriented."
582                 << endl;
583         }
585         return true;
586     }
587     else
588     {
589         if (debug || report)
590         {
591             Info<< "    Face pyramids OK." << endl;
592         }
594         return false;
595     }
599 bool primitiveMesh::checkFaceSkewness
601     const bool report,
602     labelHashSet* setPtr
603 ) const
605     if (debug)
606     {
607         Info<< "bool primitiveMesh::checkFaceSkewnesss("
608             << "const bool, labelHashSet*) const: "
609             << "checking face skewness" << endl;
610     }
612     // Warn if the skew correction vector is more than skewWarning times
613     // larger than the face area vector
615     const pointField& p = points();
616     const faceList& fcs = faces();
618     const labelList& own = faceOwner();
619     const labelList& nei = faceNeighbour();
620     const vectorField& cellCtrs = cellCentres();
621     const vectorField& faceCtrs = faceCentres();
622     const vectorField& fAreas = faceAreas();
624     scalar maxSkew = 0;
625     label nWarnSkew = 0;
627     forAll(nei, faceI)
628     {
629         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
630         vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]];
632         // Skewness vector
633         vector sv = Cpf - ((fAreas[faceI] & Cpf)/(fAreas[faceI] & d))*d;
634         vector svHat = sv/(mag(sv) + VSMALL);
636         // Normalisation distance calculated as the approximate distance
637         // from the face centre to the edge of the face in the direction of
638         // the skewness
639         scalar fd = 0.2*mag(d) + VSMALL;
640         const face& f = fcs[faceI];
641         forAll(f, pi)
642         {
643             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
644         }
646         // Normalised skewness
647         scalar skewness = mag(sv)/fd;
649         // Check if the skewness vector is greater than the PN vector.
650         // This does not cause trouble but is a good indication of a poor mesh.
651         if (skewness > skewThreshold_)
652         {
653             if (setPtr)
654             {
655                 setPtr->insert(faceI);
656             }
658             nWarnSkew++;
659         }
661         if(skewness > maxSkew)
662         {
663             maxSkew = skewness;
664         }
665     }
668     // Boundary faces: consider them to have only skewness error.
669     // (i.e. treat as if mirror cell on other side)
671     for (label faceI = nInternalFaces(); faceI < nFaces(); faceI++)
672     {
673         vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
675         vector normal = fAreas[faceI];
676         normal /= mag(normal) + VSMALL;
677         vector d = normal*(normal & Cpf);
680         // Skewness vector
681         vector sv = Cpf - ((fAreas[faceI]&Cpf)/((fAreas[faceI]&d)+VSMALL))*d;
682         vector svHat = sv/(mag(sv) + VSMALL);
684         // Normalisation distance calculated as the approximate distance
685         // from the face centre to the edge of the face in the direction of
686         // the skewness
687         scalar fd = 0.4*mag(d) + VSMALL;
688         const face& f = fcs[faceI];
689         forAll(f, pi)
690         {
691             fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
692         }
694         // Normalised skewness
695         scalar skewness = mag(sv)/fd;
697         // Check if the skewness vector is greater than the PN vector.
698         // This does not cause trouble but is a good indication of a poor mesh.
699         if (skewness > skewThreshold_)
700         {
701             if (setPtr)
702             {
703                 setPtr->insert(faceI);
704             }
706             nWarnSkew++;
707         }
709         if(skewness > maxSkew)
710         {
711             maxSkew = skewness;
712         }
713     }
716     reduce(maxSkew, maxOp<scalar>());
717     reduce(nWarnSkew, sumOp<label>());
719     if (nWarnSkew > 0)
720     {
721         if (debug || report)
722         {
723             Info<< " ***Max skewness = " << maxSkew
724                 << ", " << nWarnSkew << " highly skew faces detected"
725                    " which may impair the quality of the results"
726                 << endl;
727         }
729         return true;
730     }
731     else
732     {
733         if (debug || report)
734         {
735             Info<< "    Max skewness = " << maxSkew << " OK." << endl;
736         }
738         return false;
739     }
743 bool primitiveMesh::checkPoints
745     const bool report,
746     labelHashSet* setPtr
747 ) const
749     if (debug)
750     {
751         Info<< "bool primitiveMesh::checkPoints"
752             << "(const bool, labelHashSet*) const: "
753             << "checking points" << endl;
754     }
756     label nFaceErrors = 0;
757     label nCellErrors = 0;
759     const labelListList& pf = pointFaces();
761     forAll (pf, pointI)
762     {
763         if (pf[pointI].size() == 0)
764         {
765             if (setPtr)
766             {
767                 setPtr->insert(pointI);
768             }
770             nFaceErrors++;
771         }
772     }
774     const labelListList& pc = pointCells();
776     forAll (pc, pointI)
777     {
778         if (pc[pointI].size() == 0)
779         {
780             if (setPtr)
781             {
782                 setPtr->insert(pointI);
783             }
785             nCellErrors++;
786         }
787     }
789     reduce(nFaceErrors, sumOp<label>());
790     reduce(nCellErrors, sumOp<label>());
792     if (nFaceErrors > 0 || nCellErrors > 0)
793     {
794         if (debug || report)
795         {
796             Info<< " ***Unsed points found in the mesh, "
797                    "number unused by faces: " << nFaceErrors
798                 << " number unused by cells: " << nCellErrors
799                 << endl;
800         }
802         return true;
803     }
804     else
805     {
806         if (debug || report)
807         {
808             Info<< "    Point usage OK." << endl;
809         }
811         return false;
812     }
816 // Check convexity of angles in a face. Allow a slight non-convexity.
817 // E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
818 // (if truly concave and points not visible from face centre the face-pyramid
819 //  check in checkMesh will fail)
820 bool primitiveMesh::checkFaceAngles
822     const bool report,
823     const scalar maxDeg,
824     labelHashSet* setPtr
825 ) const
827     if (debug)
828     {
829         Info<< "bool primitiveMesh::checkFaceAngles"
830             << "(const bool, const scalar, labelHashSet*) const: "
831             << "checking face angles" << endl;
832     }
834     if (maxDeg < -SMALL || maxDeg > 180+SMALL)
835     {
836         FatalErrorIn
837         (
838             "primitiveMesh::checkFaceAngles"
839             "(const bool, const scalar, labelHashSet*)"
840         )   << "maxDeg should be [0..180] but is now " << maxDeg
841             << exit(FatalError);
842     }
844     const scalar maxSin = Foam::sin(maxDeg/180.0*mathematicalConstant::pi);
846     const pointField& p = points();
847     const faceList& fcs = faces();
848     vectorField faceNormals(faceAreas());
849     faceNormals /= mag(faceNormals) + VSMALL;
851     scalar maxEdgeSin = 0.0;
853     label nConcave = 0;
855     label errorFaceI = -1;
857     forAll(fcs, faceI)
858     {
859         const face& f = fcs[faceI];
861         // Get edge from f[0] to f[size-1];
862         vector ePrev(p[f[0]] - p[f[f.size()-1]]);
863         scalar magEPrev = mag(ePrev);
864         ePrev /= magEPrev + VSMALL;
866         forAll(f, fp0)
867         {
868             // Get vertex after fp
869             label fp1 = (fp0 + 1) % f.size();
871             // Normalized vector between two consecutive points
872             vector e10(p[f[fp1]] - p[f[fp0]]);
873             scalar magE10 = mag(e10);
874             e10 /= magE10 + VSMALL;
876             if (magEPrev > SMALL && magE10 > SMALL)
877             {
878                 vector edgeNormal = ePrev ^ e10;
879                 scalar magEdgeNormal = mag(edgeNormal);
881                 if (magEdgeNormal < maxSin)
882                 {
883                     // Edges (almost) aligned -> face is ok.
884                 }
885                 else
886                 {
887                     // Check normal
888                     edgeNormal /= magEdgeNormal;
890                     if ((edgeNormal & faceNormals[faceI]) < SMALL)
891                     {
892                         if (faceI != errorFaceI)
893                         {
894                             // Count only one error per face.
895                             errorFaceI = faceI;
896                             nConcave++;
897                         }
899                         if (setPtr)
900                         {
901                             setPtr->insert(faceI);
902                         }
904                         maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
905                     }
906                 }
907             }
909             ePrev = e10;
910             magEPrev = magE10;
911         }
912     }
914     reduce(nConcave, sumOp<label>());
915     reduce(maxEdgeSin, maxOp<scalar>());
917     if (nConcave > 0)
918     {
919         scalar maxConcaveDegr =
920             Foam::asin(Foam::min(1.0, maxEdgeSin))
921            *180.0/mathematicalConstant::pi;
923         if (debug || report)
924         {
925             Info<< "   *There are " << nConcave
926                 << " faces with concave angles between consecutive"
927                 << " edges. Max concave angle = " << maxConcaveDegr
928                 << " degrees." << endl;
929         }
931         return true;
932     }
933     else
934     {
935         if (debug || report)
936         {
937             Info<< "    All angles in faces OK." << endl;
938         }
940         return false;
941     }
945 // Check warpage of faces. Is calculated as the difference between areas of
946 // individual triangles and the overall area of the face (which ifself is
947 // is the average of the areas of the individual triangles).
948 bool primitiveMesh::checkFaceFlatness
950     const bool report,
951     const scalar warnFlatness,
952     labelHashSet* setPtr
953 ) const
955     if (debug)
956     {
957         Info<< "bool primitiveMesh::checkFaceFlatness"
958             << "(const bool, const scalar, labelHashSet*) const: "
959             << "checking face flatness" << endl;
960     }
962     if (warnFlatness < 0 || warnFlatness > 1)
963     {
964         FatalErrorIn
965         (
966             "primitiveMesh::checkFaceFlatness"
967             "(const bool, const scalar, labelHashSet*)"
968         )   << "warnFlatness should be [0..1] but is now " << warnFlatness
969             << exit(FatalError);
970     }
973     const pointField& p = points();
974     const faceList& fcs = faces();
975     const pointField& fctrs = faceCentres();
977     // Areas are calculated as the sum of areas. (see
978     // primitiveMeshFaceCentresAndAreas.C)
979     scalarField magAreas(mag(faceAreas()));
981     label nWarped = 0;
983     scalar minFlatness = GREAT;
984     scalar sumFlatness = 0;
985     label nSummed = 0;
987     forAll(fcs, faceI)
988     {
989         const face& f = fcs[faceI];
991         if (f.size() > 3 && magAreas[faceI] > VSMALL)
992         {
993             const point& fc = fctrs[faceI];
995             // Calculate the sum of magnitude of areas and compare to magnitude
996             // of sum of areas.
998             scalar sumA = 0.0;
1000             forAll(f, fp)
1001             {
1002                 const point& thisPoint = p[f[fp]];
1003                 const point& nextPoint = p[f.nextLabel(fp)];
1005                 // Triangle around fc.
1006                 vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint));
1007                 sumA += mag(n);
1008             }
1010             scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1012             sumFlatness += flatness;
1013             nSummed++;
1015             minFlatness = min(minFlatness, flatness);
1017             if (flatness < warnFlatness)
1018             {
1019                 nWarped++;
1021                 if (setPtr)
1022                 {
1023                     setPtr->insert(faceI);
1024                 }
1025             }
1026         }
1027     }
1030     reduce(nWarped, sumOp<label>());
1031     reduce(minFlatness, minOp<scalar>());
1033     reduce(nSummed, sumOp<label>());
1034     reduce(sumFlatness, sumOp<scalar>());
1036     if (debug || report)
1037     {
1038         if (nSummed > 0)
1039         {
1040             Info<< "    Face flatness (1 = flat, 0 = butterfly) : average = "
1041                 << sumFlatness / nSummed << "  min = " << minFlatness << endl;
1042         }
1043     }
1046     if (nWarped> 0)
1047     {
1048         if (debug || report)
1049         {
1050             Info<< "   *There are " << nWarped
1051                 << " faces with ratio between projected and actual area < "
1052                 << warnFlatness << endl;
1054             Info<< "    Minimum ratio (minimum flatness, maximum warpage) = "
1055                 << minFlatness << endl;
1056         }
1058         return true;
1059     }
1060     else
1061     {
1062         if (debug || report)
1063         {
1064             Info<< "    All face flatness OK." << endl;
1065         }
1067         return false;
1068     }
1072 // Check 1D/2Dness of edges. Gets passed the non-empty directions and
1073 // checks all edges in the mesh whether they:
1074 // - have no component in a non-empty direction or
1075 // - are only in a singe non-empty direction.
1076 // Empty direction info is passed in as a vector of labels (synchronised)
1077 // which are 1 if the direction is non-empty, 0 if it is.
1078 bool primitiveMesh::checkEdgeAlignment
1080     const bool report,
1081     const Vector<label>& directions,
1082     labelHashSet* setPtr
1083 ) const
1085     if (debug)
1086     {
1087         Info<< "bool primitiveMesh::checkEdgeAlignment("
1088             << "const bool, const Vector<label>&, labelHashSet*) const: "
1089             << "checking edge alignment" << endl;
1090     }
1092     label nDirs = 0;
1093     for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1094     {
1095         if (directions[cmpt] == 1)
1096         {
1097             nDirs++;
1098         }
1099         else if (directions[cmpt] != 0)
1100         {
1101             FatalErrorIn
1102             (
1103                 "primitiveMesh::checkEdgeAlignment"
1104                 "(const bool, const Vector<label>&, labelHashSet*)"
1105             )   << "directions should contain 0 or 1 but is now " << directions
1106                 << exit(FatalError);
1107         }
1108     }
1110     if (nDirs == vector::nComponents)
1111     {
1112         return false;
1113     }
1117     const pointField& p = points();
1118     const faceList& fcs = faces();
1120     EdgeMap<label> edgesInError;
1122     forAll(fcs, faceI)
1123     {
1124         const face& f = fcs[faceI];
1126         forAll(f, fp)
1127         {
1128             label p0 = f[fp];
1129             label p1 = f.nextLabel(fp);
1130             if (p0 < p1)
1131             {
1132                 vector d(p[p1]-p[p0]);
1133                 scalar magD = mag(d);
1135                 if (magD > ROOTVSMALL)
1136                 {
1137                     d /= magD;
1139                     // Check how many empty directions are used by the edge.
1140                     label nEmptyDirs = 0;
1141                     label nNonEmptyDirs = 0;
1142                     for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1143                     {
1144                         if (mag(d[cmpt]) > 1e-6)
1145                         {
1146                             if (directions[cmpt] == 0)
1147                             {
1148                                 nEmptyDirs++;
1149                             }
1150                             else
1151                             {
1152                                 nNonEmptyDirs++;
1153                             }
1154                         }
1155                     }
1157                     if (nEmptyDirs == 0)
1158                     {
1159                         // Purely in ok directions.
1160                     }
1161                     else if (nEmptyDirs == 1)
1162                     {
1163                         // Ok if purely in empty directions.
1164                         if (nNonEmptyDirs > 0)
1165                         {
1166                             edgesInError.insert(edge(p0, p1), faceI);
1167                         }
1168                     }
1169                     else if (nEmptyDirs > 1)
1170                     {
1171                         // Always an error
1172                         edgesInError.insert(edge(p0, p1), faceI);
1173                     }
1174                 }
1175             }
1176         }
1177     }
1179     label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>());
1181     if (nErrorEdges > 0)
1182     {
1183         if (debug || report)
1184         {
1185             Info<< " ***Number of edges not aligned with or perpendicular to "
1186                 << "non-empty directions: " << nErrorEdges << endl;
1187         }
1189         if (setPtr)
1190         {
1191             setPtr->resize(2*edgesInError.size());
1192             forAllConstIter(EdgeMap<label>, edgesInError, iter)
1193             {
1194                 setPtr->insert(iter.key()[0]);
1195                 setPtr->insert(iter.key()[1]);
1196             }
1197         }
1199         return true;
1200     }
1201     else
1202     {
1203         if (debug || report)
1204         {
1205             Info<< "    All edges aligned with or perpendicular to "
1206                 << "non-empty directions." << endl;
1207         }
1208         return false;
1209     }
1213 bool primitiveMesh::checkUpperTriangular
1215     const bool report,
1216     labelHashSet* setPtr
1217 ) const
1219     if (debug)
1220     {
1221         Info<< "bool primitiveMesh::checkUpperTriangular("
1222             << "const bool, labelHashSet*) const: "
1223             << "checking face ordering" << endl;
1224     }
1226     // Check whether internal faces are ordered in the upper triangular order
1227     const labelList& own = faceOwner();
1228     const labelList& nei = faceNeighbour();
1230     const cellList& c = cells();
1232     label internal = nInternalFaces();
1234     // Has error occurred?
1235     bool error = false;
1236     // Have multiple faces been detected?
1237     label nMultipleCells = false;
1239     // Loop through faceCells once more and make sure that for internal cell
1240     // the first label is smaller
1241     for (label faceI = 0; faceI < internal; faceI++)
1242     {
1243         if (own[faceI] >= nei[faceI])
1244         {
1245             error  = true;
1247             if (setPtr)
1248             {
1249                 setPtr->insert(faceI);
1250             }
1251         }
1252     }
1254     // Loop through all cells. For each cell, find the face that is internal
1255     // and add it to the check list (upper triangular order).
1256     // Once the list is completed, check it against the faceCell list
1258     forAll (c, cellI)
1259     {
1260         const labelList& curFaces = c[cellI];
1262         // Neighbouring cells
1263         SortableList<label> nbr(curFaces.size());
1265         forAll(curFaces, i)
1266         {
1267             label faceI = curFaces[i];
1269             if (faceI >= nInternalFaces())
1270             {
1271                 // Sort last
1272                 nbr[i] = labelMax;
1273             }
1274             else
1275             {
1276                 label nbrCellI = nei[faceI];
1278                 if (nbrCellI == cellI)
1279                 {
1280                     nbrCellI = own[faceI];
1281                 }
1283                 if (cellI < nbrCellI)
1284                 {
1285                     // cellI is master
1286                     nbr[i] = nbrCellI;
1287                 }
1288                 else
1289                 {
1290                     // nbrCell is master. Let it handle this face.
1291                     nbr[i] = labelMax;
1292                 }
1293             }
1294         }
1296         nbr.sort();
1298         // Now nbr holds the cellCells in incremental order. Check:
1299         // - neighbouring cells appear only once. Since nbr is sorted this
1300         //   is simple check on consecutive elements
1301         // - faces indexed in same order as nbr are incrementing as well.
1303         label prevCell = nbr[0];
1304         label prevFace = curFaces[nbr.indices()[0]];
1306         bool hasMultipleFaces = false;
1308         for (label i = 1; i < nbr.size(); i++)
1309         {
1310             label thisCell = nbr[i];
1311             label thisFace = curFaces[nbr.indices()[i]];
1313             if (thisCell == labelMax)
1314             {
1315                 break;
1316             }
1318             if (thisCell == prevCell)
1319             {
1320                 hasMultipleFaces = true;
1322                 if (setPtr)
1323                 {
1324                     setPtr->insert(prevFace);
1325                     setPtr->insert(thisFace);
1326                 }
1327             }
1328             else if (thisFace < prevFace)
1329             {
1330                 error = true;
1332                 if (setPtr)
1333                 {
1334                     setPtr->insert(thisFace);
1335                 }
1336             }
1338             prevCell = thisCell;
1339             prevFace = thisFace;
1340         }
1342         if (hasMultipleFaces)
1343         {
1344             nMultipleCells++;
1345         }
1346     }
1348     reduce(error, orOp<bool>());
1349     reduce(nMultipleCells, sumOp<label>());
1351     if ((debug || report) && nMultipleCells > 0)
1352     {
1353         Info<< "  <<Found " << nMultipleCells
1354             << " neighbouring cells with multiple inbetween faces." << endl;
1355     }
1357     if (error)
1358     {
1359         if (debug || report)
1360         {
1361             Info<< " ***Faces not in upper triangular order." << endl;
1362         }
1364         return true;
1365     }
1366     else
1367     {
1368         if (debug || report)
1369         {
1370             Info<< "    Upper triangular ordering OK." << endl;
1371         }
1373         return false;
1374     }
1378 bool primitiveMesh::checkCellsZipUp
1380     const bool report,
1381     labelHashSet* setPtr
1382 ) const
1384     if (debug)
1385     {
1386         Info<< "bool primitiveMesh::checkCellsZipUp("
1387             << "const bool, labelHashSet*) const: "
1388             << "checking topological cell openness" << endl;
1389     }
1391     label nOpenCells = 0;
1393     const faceList& f = faces();
1394     const cellList& c = cells();
1396     forAll (c, cellI)
1397     {
1398         const labelList& curFaces = c[cellI];
1400         const edgeList cellEdges = c[cellI].edges(f);
1402         labelList edgeUsage(cellEdges.size(), 0);
1404         forAll (curFaces, faceI)
1405         {
1406             edgeList curFaceEdges = f[curFaces[faceI]].edges();
1408             forAll (curFaceEdges, faceEdgeI)
1409             {
1410                 const edge& curEdge = curFaceEdges[faceEdgeI];
1412                 forAll (cellEdges, cellEdgeI)
1413                 {
1414                     if (cellEdges[cellEdgeI] == curEdge)
1415                     {
1416                         edgeUsage[cellEdgeI]++;
1417                         break;
1418                     }
1419                 }
1420             }
1421         }
1423         edgeList singleEdges(cellEdges.size());
1424         label nSingleEdges = 0;
1426         forAll (edgeUsage, edgeI)
1427         {
1428             if (edgeUsage[edgeI] == 1)
1429             {
1430                 singleEdges[nSingleEdges] = cellEdges[edgeI];
1431                 nSingleEdges++;
1432             }
1433             else if (edgeUsage[edgeI] != 2)
1434             {
1435                 if (setPtr)
1436                 {
1437                     setPtr->insert(cellI);
1438                 }
1439             }
1440         }
1442         if (nSingleEdges > 0)
1443         {
1444             if (setPtr)
1445             {
1446                 setPtr->insert(cellI);
1447             }
1449             nOpenCells++;
1450         }
1451     }
1453     reduce(nOpenCells, sumOp<label>());
1455     if (nOpenCells > 0)
1456     {
1457         if (debug || report)
1458         {
1459             Info<< " ***Open cells found, number of cells: " << nOpenCells
1460                 << ". This problem may be fixable using the zipUpMesh utility."
1461                 << endl;
1462         }
1464         return true;
1465     }
1466     else
1467     {
1468         if (debug || report)
1469         {
1470             Info<< "    Topological cell zip-up check OK." << endl;
1471         }
1473         return false;
1474     }
1478 // Vertices of face within point range and unique.
1479 bool primitiveMesh::checkFaceVertices
1481     const bool report,
1482     labelHashSet* setPtr
1483 ) const
1485     if (debug)
1486     {
1487         Info<< "bool primitiveMesh::checkFaceVertices("
1488             << "const bool, labelHashSet*) const: "
1489             << "checking face vertices" << endl;
1490     }
1492     // Check that all vertex labels are valid
1493     const faceList& f = faces();
1495     label nErrorFaces = 0;
1497     forAll (f, fI)
1498     {
1499         const face& curFace = f[fI];
1501         if (min(curFace) < 0 || max(curFace) > nPoints())
1502         {
1503             if (setPtr)
1504             {
1505                 setPtr->insert(fI);
1506             }
1508             nErrorFaces++;
1509         }
1511         // Uniqueness of vertices
1512         labelHashSet facePoints(2*curFace.size());
1514         forAll(curFace, fp)
1515         {
1516             bool inserted = facePoints.insert(curFace[fp]);
1518             if (!inserted)
1519             {
1520                 if (setPtr)
1521                 {
1522                     setPtr->insert(fI);
1523                 }
1525                 nErrorFaces++;
1526             }
1527         }
1528     }
1530     reduce(nErrorFaces, sumOp<label>());
1532     if (nErrorFaces > 0)
1533     {
1534         if (debug || report)
1535         {
1536             Info<< "    Faces with invalid vertex labels found, "
1537                 << " number of faces: " << nErrorFaces << endl;
1538         }
1540         return true;
1541     }
1542     else
1543     {
1544         if (debug || report)
1545         {
1546             Info<< "    Face vertices OK." << endl;
1547         }
1549         return false;
1550     }
1554 // Check if all points on face are shared between faces.
1555 bool primitiveMesh::checkDuplicateFaces
1557     const label faceI,
1558     const Map<label>& nCommonPoints,
1559     label& nBaffleFaces,
1560     labelHashSet* setPtr
1561 ) const
1563     bool error = false;
1565     forAllConstIter(Map<label>, nCommonPoints, iter)
1566     {
1567         label nbFaceI = iter.key();
1568         label nCommon = iter();
1570         const face& curFace = faces()[faceI];
1571         const face& nbFace = faces()[nbFaceI];
1573         if (nCommon == nbFace.size() || nCommon == curFace.size())
1574         {
1575             if (nbFace.size() != curFace.size())
1576             {
1577                 error = true;
1578             }
1579             else
1580             {
1581                 nBaffleFaces++;
1582             }
1584             if (setPtr)
1585             {
1586                 setPtr->insert(faceI);
1587                 setPtr->insert(nbFaceI);
1588             }
1589         }
1590     }
1592     return error;
1596 // Check that shared points are in consecutive order.
1597 bool primitiveMesh::checkCommonOrder
1599     const label faceI,
1600     const Map<label>& nCommonPoints,
1601     labelHashSet* setPtr
1602 ) const
1604     bool error = false;
1606     forAllConstIter(Map<label>, nCommonPoints, iter)
1607     {
1608         label nbFaceI = iter.key();
1609         label nCommon = iter();
1611         const face& curFace = faces()[faceI];
1612         const face& nbFace = faces()[nbFaceI];
1614         if
1615         (
1616             nCommon >= 2
1617          && nCommon != nbFace.size()
1618          && nCommon != curFace.size()
1619         )
1620         {
1621             forAll(curFace, fp)
1622             {
1623                 // Get the index in the neighbouring face shared with curFace
1624                 label nb = findIndex(nbFace, curFace[fp]);
1626                 if (nb != -1)
1627                 {
1629                     // Check the whole face from nb onwards for shared vertices
1630                     // with neighbouring face. Rule is that any shared vertices
1631                     // should be consecutive on both faces i.e. if they are
1632                     // vertices fp,fp+1,fp+2 on one face they should be
1633                     // vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
1634                     // other face.
1637                     // Vertices before and after on curFace
1638                     label fpPlus1 = (fp+1) % curFace.size();
1639                     label fpMin1 = (fp == 0 ? curFace.size()-1 : fp-1);
1641                     // Vertices before and after on nbFace
1642                     label nbPlus1 = (nb+1) % nbFace.size();
1643                     label nbMin1 = (nb == 0 ? nbFace.size()-1 : nb-1);
1645                     // Find order of walking by comparing next points on both
1646                     // faces.
1647                     label curInc = labelMax;
1648                     label nbInc = labelMax;
1650                     if (nbFace[nbPlus1] == curFace[fpPlus1])
1651                     {
1652                         curInc = 1;
1653                         nbInc = 1;
1654                     }
1655                     else if (nbFace[nbPlus1] == curFace[fpMin1])
1656                     {
1657                         curInc = -1;
1658                         nbInc = 1;
1659                     }
1660                     else if (nbFace[nbMin1] == curFace[fpMin1])
1661                     {
1662                         curInc = -1;
1663                         nbInc = -1;
1664                     }
1665                     else
1666                     {
1667                         curInc = 1;
1668                         nbInc = -1;
1669                     }
1672                     // Pass1: loop until start of common vertices found.
1673                     label curNb = nb;
1674                     label curFp = fp;
1676                     do
1677                     {
1678                         curFp += curInc;
1680                         if (curFp >= curFace.size())
1681                         {
1682                             curFp = 0;
1683                         }
1684                         else if (curFp < 0)
1685                         {
1686                             curFp = curFace.size()-1;
1687                         }
1689                         curNb += nbInc;
1691                         if (curNb >= nbFace.size())
1692                         {
1693                             curNb = 0;
1694                         }
1695                         else if (curNb < 0)
1696                         {
1697                             curNb = nbFace.size()-1;
1698                         }
1699                     } while (curFace[curFp] == nbFace[curNb]);
1702                     // Pass2: check equality walking from curFp, curNb
1703                     // in opposite order.
1705                     curInc = -curInc;
1706                     nbInc = -nbInc;
1708                     for (label commonI = 0; commonI < nCommon; commonI++)
1709                     {
1710                         curFp += curInc;
1712                         if (curFp >= curFace.size())
1713                         {
1714                             curFp = 0;
1715                         }
1716                         else if (curFp < 0)
1717                         {
1718                             curFp = curFace.size()-1;
1719                         }
1721                         curNb += nbInc;
1723                         if (curNb >= nbFace.size())
1724                         {
1725                             curNb = 0;
1726                         }
1727                         else if (curNb < 0)
1728                         {
1729                             curNb = nbFace.size()-1;
1730                         }
1732                         if (curFace[curFp] != nbFace[curNb])
1733                         {
1734                             if (setPtr)
1735                             {
1736                                 setPtr->insert(faceI);
1737                                 setPtr->insert(nbFaceI);
1738                             }
1740                             error = true;
1742                             break;
1743                         }
1744                     }
1747                     // Done the curFace - nbFace combination.
1748                     break;
1749                 }
1750             }
1751         }
1752     }
1754     return error;
1758 // Checks common vertices between faces. If more than 2 they should be
1759 // consecutive on both faces.
1760 bool primitiveMesh::checkFaceFaces
1762     const bool report,
1763     labelHashSet* setPtr
1764 ) const
1766     if (debug)
1767     {
1768         Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
1769             << " const: " << "checking face-face connectivity" << endl;
1770     }
1772     const labelListList& pf = pointFaces();
1774     label nBaffleFaces = 0;
1775     label nErrorDuplicate = 0;
1776     label nErrorOrder = 0;
1777     Map<label> nCommonPoints(100);
1779     for (label faceI = 0; faceI < nFaces(); faceI++)
1780     {
1781         const face& curFace = faces()[faceI];
1783         // Calculate number of common points between current faceI and
1784         // neighbouring face. Store on map.
1785         nCommonPoints.clear();
1787         forAll(curFace, fp)
1788         {
1789             label pointI = curFace[fp];
1791             const labelList& nbs = pf[pointI];
1793             forAll(nbs, nbI)
1794             {
1795                 label nbFaceI = nbs[nbI];
1797                 if (faceI < nbFaceI)
1798                 {
1799                     // Only check once for each combination of two faces.
1801                     Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
1803                     if (fnd == nCommonPoints.end())
1804                     {
1805                         // First common vertex found.
1806                         nCommonPoints.insert(nbFaceI, 1);
1807                     }
1808                     else
1809                     {
1810                         fnd()++;
1811                     }
1812                 }
1813             }
1814         }
1816         // Perform various checks on common points
1818         // Check all vertices shared (duplicate point)
1819         if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
1820         {
1821             nErrorDuplicate++;
1822         }
1824         // Check common vertices are consecutive on both faces
1825         if (checkCommonOrder(faceI, nCommonPoints, setPtr))
1826         {
1827             nErrorOrder++;
1828         }
1829     }
1831     reduce(nBaffleFaces, sumOp<label>());
1832     reduce(nErrorDuplicate, sumOp<label>());
1833     reduce(nErrorOrder, sumOp<label>());
1835     if (nBaffleFaces)
1836     {
1837         Info<< "    Number of identical duplicate faces (baffle faces): "
1838             << nBaffleFaces << endl;
1839     }
1841     if (nErrorDuplicate > 0 || nErrorOrder > 0)
1842     {
1843         if (nErrorDuplicate > 0)
1844         {
1845             Info<< " ***Number of duplicate (not baffle) faces found: "
1846                 << nErrorDuplicate << endl;
1847         }
1849         if (nErrorOrder > 0)
1850         {
1851             Info<< " ***Number of faces with non-consecutive shared points: "
1852                 << nErrorOrder << endl;
1853         }
1855         return true;
1856     }
1857     else
1858     {
1859         if (debug || report)
1860         {
1861             Info<< "    Face-face connectivity OK." << endl;
1862         }
1864         return false;
1865     }
1869 // Checks cells with 1 or less internal faces. Give numerical problems.
1870 bool primitiveMesh::checkCellDeterminant
1872     const bool report,    // report,
1873     labelHashSet* setPtr  // setPtr
1874 ) const
1876     if (debug)
1877     {
1878         Info<< "bool primitiveMesh::checkCellDeterminant(const bool"
1879             << ", labelHashSet*) const: "
1880             << "checking for under-determined cells" << endl;
1881     }
1883     const cellList& c = cells();
1885     label nErrorCells = 0;
1887     scalar minDet = GREAT;
1888     scalar sumDet = 0;
1889     label nSummed = 0;
1891     forAll (c, cellI)
1892     {
1893         const labelList& curFaces = c[cellI];
1895         // Calculate local normalization factor
1896         scalar avgArea = 0;
1898         label nInternalFaces = 0;
1900         forAll(curFaces, i)
1901         {
1902             if (isInternalFace(curFaces[i]))
1903             {
1904                 avgArea += mag(faceAreas()[curFaces[i]]);
1906                 nInternalFaces++;
1907             }
1908         }
1910         if (nInternalFaces == 0)
1911         {
1912             if (setPtr)
1913             {
1914                 setPtr->insert(cellI);
1915             }
1917             nErrorCells++;
1918         }
1919         else
1920         {
1921             avgArea /= nInternalFaces;
1923             symmTensor areaTensor(symmTensor::zero);
1925             forAll(curFaces, i)
1926             {
1927                 if (isInternalFace(curFaces[i]))
1928                 {
1929                     areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea);
1930                 }
1931             }
1933             scalar determinant = mag(det(areaTensor));
1935             minDet = min(determinant, minDet);
1936             sumDet += determinant;
1937             nSummed++;
1939             if (determinant < 1e-3)
1940             {
1941                 if (setPtr)
1942                 {
1943                     setPtr->insert(cellI);
1944                 }
1946                 nErrorCells++;
1947             }
1948         }
1949     }
1951     reduce(nErrorCells, sumOp<label>());
1952     reduce(minDet, minOp<scalar>());
1953     reduce(sumDet, sumOp<scalar>());
1954     reduce(nSummed, sumOp<label>());
1956     if (debug || report)
1957     {
1958         if (nSummed > 0)
1959         {
1960             Info<< "    Cell determinant (wellposedness) : minimum: " << minDet
1961                 << " average: " << sumDet/nSummed
1962                 << endl;
1963         }
1964     }
1966     if (nErrorCells > 0)
1967     {
1968         if (debug || report)
1969         {
1970             Info<< " ***Cells with small determinant found, number of cells: "
1971                 << nErrorCells << endl;
1972         }
1974         return true;
1975     }
1976     else
1977     {
1978         if (debug || report)
1979         {
1980             Info<< "    Cell determinant check OK." << endl;
1981         }
1983         return false;
1984     }
1986     return false;
1990 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
1992 bool primitiveMesh::checkTopology(const bool report) const
1994     label noFailedChecks = 0;
1996     if (checkPoints(report)) noFailedChecks++;
1997     if (checkUpperTriangular(report)) noFailedChecks++;
1998     if (checkCellsZipUp(report)) noFailedChecks++;
1999     if (checkFaceVertices(report)) noFailedChecks++;
2000     if (checkFaceFaces(report)) noFailedChecks++;
2001     //if (checkCellDeterminant(report)) noFailedChecks++;
2003     if (noFailedChecks == 0)
2004     {
2005         if (debug || report)
2006         {
2007             Info<< "    Mesh topology OK." << endl;
2008         }
2010         return false;
2011     }
2012     else
2013     {
2014         if (debug || report)
2015         {
2016             Info<< "    Failed " << noFailedChecks
2017                 << " mesh topology checks." << endl;
2018         }
2020         return true;
2021     }
2025 bool primitiveMesh::checkGeometry(const bool report) const
2027     label noFailedChecks = 0;
2029     if (checkClosedBoundary(report)) noFailedChecks++;
2030     if (checkClosedCells(report)) noFailedChecks++;
2031     if (checkFaceAreas(report)) noFailedChecks++;
2032     if (checkCellVolumes(report)) noFailedChecks++;
2033     if (checkFaceOrthogonality(report)) noFailedChecks++;
2034     if (checkFacePyramids(report)) noFailedChecks++;
2035     if (checkFaceSkewness(report)) noFailedChecks++;
2037     if (noFailedChecks == 0)
2038     {
2039         if (debug || report)
2040         {
2041             Info<< "    Mesh geometry OK." << endl;
2042         }
2043         return false;
2044     }
2045     else
2046     {
2047         if (debug || report)
2048         {
2049             Info<< "    Failed " << noFailedChecks
2050                 << " mesh geometry checks." << endl;
2051         }
2053         return true;
2054     }
2058 bool primitiveMesh::checkMesh(const bool report) const
2060     if (debug)
2061     {
2062         Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
2063             << "checking primitiveMesh" << endl;
2064     }
2066     label noFailedChecks = checkTopology(report) + checkGeometry(report);
2068     if (noFailedChecks == 0)
2069     {
2070         if (debug || report)
2071         {
2072             Info<< "Mesh OK." << endl;
2073         }
2075         return false;
2076     }
2077     else
2078     {
2079         if (debug || report)
2080         {
2081             Info<< "    Failed " << noFailedChecks
2082                 << " mesh checks." << endl;
2083         }
2085         return true;
2086     }
2090 scalar primitiveMesh::setClosedThreshold(const scalar ct)
2092     scalar oldClosedThreshold = closedThreshold_;
2093     closedThreshold_ = ct;
2095     return oldClosedThreshold;
2099 scalar primitiveMesh::setAspectThreshold(const scalar at)
2101     scalar oldAspectThreshold = aspectThreshold_;
2102     aspectThreshold_ = at;
2104     return oldAspectThreshold;
2108 scalar primitiveMesh::setNonOrthThreshold(const scalar nt)
2110     scalar oldNonOrthThreshold = nonOrthThreshold_;
2111     nonOrthThreshold_ = nt;
2113     return oldNonOrthThreshold;
2117 scalar primitiveMesh::setSkewThreshold(const scalar st)
2119     scalar oldSkewThreshold = skewThreshold_;
2120     skewThreshold_ = st;
2122     return oldSkewThreshold;
2126 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2128 } // End namespace Foam
2130 // ************************************************************************* //