Merge branch 'master' of ssh://git.code.sf.net/p/foam-extend/foam-extend-3.2
[foam-extend-3.2.git] / src / mesh / cfMesh / utilities / meshes / polyMeshGenChecks / polyMeshGenChecksGeometry.C
blob173344797d3962ec842a3c7a77cef5347bb90478
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | cfMesh: A library for mesh generation
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright held by the origina author
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of cfMesh.
11     cfMesh 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     cfMesh 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 cfMesh.  If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "polyMeshGenChecks.H"
27 #include "polyMeshGenAddressing.H"
28 #include "pyramidPointFaceRef.H"
29 #include "tetrahedron.H"
31 # ifdef USE_OMP
32 #include <omp.h>
33 # endif
35 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
37 namespace Foam
40 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
42 namespace polyMeshGenChecks
45 bool checkClosedBoundary(const polyMeshGen& mesh, const bool report)
47     // Loop through all boundary faces and sum up the face area vectors.
48     // For a closed boundary, this should be zero in all vector components
49     vector sumClosed(vector::zero);
50     scalar sumMagClosedBoundary = 0;
52     const vectorField& areas = mesh.addressingData().faceAreas();
54     for(label faceI = mesh.nInternalFaces();faceI<areas.size();++faceI)
55     {
56         sumClosed += areas[faceI];
57         sumMagClosedBoundary += mag(areas[faceI]);
58     }
60     // Check the openness in the maximum direction (this is APPROXIMATE!)
61     scalar maxOpen = max
62     (
63         sumClosed.component(vector::X),
64         max
65         (
66             sumClosed.component(vector::Y),
67             sumClosed.component(vector::Z)
68         )
69     );
71     reduce(sumClosed, sumOp<vector>());
72     reduce(maxOpen, maxOp<scalar>());
74     if( maxOpen > SMALL*max(1.0, sumMagClosedBoundary) )
75     {
76         SeriousErrorIn
77         (
78             "bool checkClosedBoundary(const polyMeshGen&, const bool report)"
79         )   << "Possible hole in boundary description" << endl;
81         Info<< "Boundary openness in x-direction = "
82             << sumClosed.component(vector::X) << endl;
84         Info<< "Boundary openness in y-direction = "
85             << sumClosed.component(vector::Y) << endl;
87         Info<< "Boundary openness in z-direction = "
88             << sumClosed.component(vector::Z) << endl;
90         return true;
91     }
92     else
93     {
94         if( report )
95         {
96             Info<< "Boundary openness in x-direction = "
97                 << sumClosed.component(vector::X) << endl;
99             Info<< "Boundary openness in y-direction = "
100                 << sumClosed.component(vector::Y) << endl;
102             Info<< "Boundary openness in z-direction = "
103                 << sumClosed.component(vector::Z) << endl;
105             Info<< "Boundary closed (OK)." << endl;
106         }
108         return false;
109     }
112 bool checkClosedCells
114     const polyMeshGen& mesh,
115     const bool report,
116     const scalar aspectWarn,
117     labelHashSet* setPtr
120     // Check that all cells labels are valid
121     const cellListPMG& cells = mesh.cells();
122     const label nFaces = mesh.faces().size();
124     label nErrorClosed = 0;
126     # ifdef USE_OMP
127     # pragma omp parallel for schedule(guided) reduction(+ : nErrorClosed)
128     # endif
129     forAll(cells, cI)
130     {
131         const cell& curCell = cells[cI];
133         if( min(curCell) < 0 || max(curCell) > nFaces )
134         {
135             WarningIn
136             (
137                 "bool checkClosedCells("
138                 "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
139             )   << "Cell " << cI << " contains face labels out of range: "
140                 << curCell << " Max face index = " << nFaces << endl;
142             if( setPtr )
143             {
144                 # ifdef USE_OMP
145                 # pragma omp critical
146                 # endif
147                 setPtr->insert(cI);
148             }
150             ++nErrorClosed;
151         }
152     }
154     if( nErrorClosed > 0 )
155     {
156         SeriousErrorIn
157         (
158             "bool checkClosedCells("
159             "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
160         )  << nErrorClosed << " cells with invalid face labels found"
161             << endl;
163         return true;
164     }
166     // Loop through cell faces and sum up the face area vectors for each cell.
167     // This should be zero in all vector components
168     vectorField sumClosed(cells.size(), vector::zero);
170     scalarField sumMagClosed(cells.size(), 0.0);
172     const labelList& own = mesh.owner();
173     const labelList& nei = mesh.neighbour();
174     const vectorField& areas = mesh.addressingData().faceAreas();
176     forAll(own, faceI)
177     {
178         // Add to owner
179         sumClosed[own[faceI]] += areas[faceI];
180         sumMagClosed[own[faceI]] += mag(areas[faceI]);
182         if( nei[faceI] == -1 )
183             continue;
185         // Subtract from neighbour
186         sumClosed[nei[faceI]] -= areas[faceI];
187         sumMagClosed[nei[faceI]] += mag(areas[faceI]);
188     }
190     label nOpen = 0;
191     scalar maxOpenCell = 0;
193     label nAspect = 0;
194     scalar maxAspectRatio = 0;
196     const scalarField& vols = mesh.addressingData().cellVolumes();
198     // Check the sums
199     forAll(sumClosed, cellI)
200     {
201         scalar maxOpen = max
202         (
203             sumClosed[cellI].component(vector::X),
204             max
205             (
206                 sumClosed[cellI].component(vector::Y),
207                 sumClosed[cellI].component(vector::Z)
208             )
209         );
211         maxOpenCell = max(maxOpenCell, maxOpen);
213         if( maxOpen > SMALL*max(1.0, sumMagClosed[cellI]) )
214         {
215             if( report )
216             {
217                 Pout<< "Cell " << cellI << " is not closed. "
218                     << "Face area vectors sum up to " << mag(sumClosed[cellI])
219                     << " directionwise " << sumClosed[cellI] << " or "
220                     << mag(sumClosed[cellI])
221                        /(mag(sumMagClosed[cellI]) + VSMALL)
222                     << " of the area of all faces of the cell. " << endl
223                     << "    Area magnitudes sum up to "
224                     << sumMagClosed[cellI] << endl;
225             }
227             if( setPtr )
228             {
229                 setPtr->insert(cellI);
230             }
232             ++nOpen;
233         }
235         scalar aspectRatio =
236             1.0/6.0*sumMagClosed[cellI]/pow(vols[cellI], 2.0/3.0);
238         maxAspectRatio = max(maxAspectRatio, aspectRatio);
240         if( aspectRatio > aspectWarn )
241         {
242             if( report )
243             {
244                 Pout<< "High aspect ratio for cell " << cellI
245                     << ": " << aspectRatio << endl;
246             }
248             ++nAspect;
249         }
250     }
252     reduce(nOpen, sumOp<label>());
253     reduce(maxOpenCell, maxOp<scalar>());
255     reduce(nAspect, sumOp<label>());
256     reduce(maxAspectRatio, maxOp<scalar>());
258     if( nOpen > 0 )
259     {
260         SeriousErrorIn
261         (
262             "bool checkClosedCells("
263             "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
264         )   << nOpen << " open cells found. Max cell openness: "
265             << maxOpenCell << endl;
267         return true;
268     }
270     if( nAspect > 0 )
271     {
272         SeriousErrorIn
273         (
274             "bool checkClosedCells("
275             "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
276         )   << nAspect << " high aspect ratio cells found.  "
277             << "Max aspect ratio: " << maxAspectRatio
278             << endl;
280         return true;
281     }
283     if( report )
284     {
285         Info<< "Max cell openness = " << maxOpenCell
286             << "  Max aspect ratio = " << maxAspectRatio
287             << ".  All cells OK.\n" << endl;
288     }
290     return false;
293 bool checkCellVolumes
295     const polyMeshGen& mesh,
296     const bool report,
297     labelHashSet* setPtr
300     const scalarField& vols = mesh.addressingData().cellVolumes();
302     scalar minVolume = GREAT;
303     scalar maxVolume = -GREAT;
305     label nNegVolCells = 0;
307     forAll(vols, cellI)
308     {
309         if( vols[cellI] < VSMALL )
310         {
311             if( report )
312                 SeriousErrorIn
313                 (
314                     "bool checkCellVolumes("
315                     "const polyMeshGen&, const bool, labelHashSet*)"
316                 )   << "Zero or negative cell volume detected for cell "
317                     << cellI << ".  Volume = " << vols[cellI] << endl;
319             if( setPtr )
320                 setPtr->insert(cellI);
322             ++nNegVolCells;
323         }
325         minVolume = min(minVolume, vols[cellI]);
326         maxVolume = max(maxVolume, vols[cellI]);
327     }
329     reduce(minVolume, minOp<scalar>());
330     reduce(maxVolume, maxOp<scalar>());
331     reduce(nNegVolCells, sumOp<label>());
333     if( minVolume < VSMALL )
334     {
335         SeriousErrorIn
336         (
337             "bool checkCellVolumes("
338             "const polyMeshGen&, const bool, labelHashSet*)"
339         )   << "Zero or negative cell volume detected.  "
340             << "Minimum negative volume: "
341             << minVolume << ".\nNumber of negative volume cells: "
342             << nNegVolCells << ".  This mesh is invalid"
343             << endl;
345         return true;
346     }
347     else
348     {
349         if( report )
350         {
351             Info<< "Min volume = " << minVolume
352                 << ". Max volume = " << maxVolume
353                 << ".  Total volume = " << sum(vols)
354                 << ".  Cell volumes OK.\n" << endl;
355         }
357         return false;
358     }
361 bool checkFaceAreas
363     const polyMeshGen& mesh,
364     const bool report,
365     const scalar minFaceArea,
366     labelHashSet* setPtr,
367     const boolList* changedFacePtr
370     const vectorField& faceAreas = mesh.addressingData().faceAreas();
372     const labelList& own = mesh.owner();
373     const labelList& nei = mesh.neighbour();
375     scalar minArea = VGREAT;
376     scalar maxArea = -VGREAT;
378     # ifdef USE_OMP
379     # pragma omp parallel if( own.size() > 100 )
380     # endif
381     {
382         scalar localMaxArea(-VGREAT), localMinArea(VGREAT);
384         # ifdef USE_OMP
385         # pragma omp for schedule(guided)
386         # endif
387         forAll(faceAreas, faceI)
388         {
389             if( changedFacePtr && !changedFacePtr->operator[](faceI) )
390                 continue;
392             const scalar magFaceArea = mag(faceAreas[faceI]);
394             if( magFaceArea < minFaceArea )
395             {
396                 if( report )
397                 {
398                     if( nei[faceI] != -1 )
399                     {
400                         Pout<< "Zero or negative face area detected for "
401                             << "internal face " << faceI << " between cells "
402                             << own[faceI] << " and " << nei[faceI]
403                             << ".  Face area magnitude = "
404                             << magFaceArea << endl;
405                     }
406                     else
407                     {
408                         Pout<< "Zero or negative face area detected for "
409                             << "boundary face " << faceI << " next to cell "
410                             << own[faceI] << ".  Face area magnitude = "
411                             << magFaceArea << endl;
412                     }
413                 }
415                 if( setPtr )
416                 {
417                     # ifdef USE_OMP
418                     # pragma omp critical
419                     # endif
420                     setPtr->insert(faceI);
421                 }
422             }
424             localMinArea = Foam::min(localMinArea, magFaceArea);
425             localMaxArea = Foam::max(localMaxArea, magFaceArea);
426         }
428         # ifdef USE_OMP
429         # pragma omp critical
430         # endif
431         {
432             minArea = Foam::min(minArea, localMinArea);
433             maxArea = Foam::max(maxArea, localMaxArea);
434         }
435     }
437     reduce(minArea, minOp<scalar>());
438     reduce(maxArea, maxOp<scalar>());
440     if( minArea < VSMALL )
441     {
442         SeriousErrorIn
443         (
444             "bool checkFaceAreas("
445             "const polyMeshGen&, const bool, const scalar,"
446             " labelHashSet*, const boolList*)"
447         )   << "Zero or negative face area detected.  Minimum negative area: "
448             << minArea << ". This mesh is invalid"
449             << endl;
451         return true;
452     }
453     else
454     {
455         if( report )
456         {
457             Info<< "Minumum face area = " << minArea
458                 << ". Maximum face area = " << maxArea
459                 << ".  Face area magnitudes OK.\n" << endl;
460         }
462         return false;
463     }
466 bool checkCellPartTetrahedra
468     const polyMeshGen& mesh,
469     const bool report,
470     const scalar minPartTet,
471     labelHashSet* setPtr,
472     const boolList* changedFacePtr
475     const pointFieldPMG& points = mesh.points();
476     const faceListPMG& faces = mesh.faces();
477     const labelList& owner = mesh.owner();
478     const labelList& neighbour = mesh.neighbour();
480     const vectorField& fCentres = mesh.addressingData().faceCentres();
481     const vectorField& cCentres = mesh.addressingData().cellCentres();
483     label nNegVolCells = 0;
485     # ifdef USE_OMP
486     # pragma omp parallel for if( owner.size() > 100 ) \
487     schedule(guided) reduction(+ : nNegVolCells)
488     # endif
489     forAll(owner, faceI)
490     {
491         if( changedFacePtr && !(*changedFacePtr)[faceI] )
492             continue;
494         const face& f = faces[faceI];
496         bool badFace(false);
498         forAll(f, eI)
499         {
500             const tetrahedron<point, point> tetOwn
501             (
502                 fCentres[faceI],
503                 points[f.nextLabel(eI)],
504                 points[f[eI]],
505                 cCentres[owner[faceI]]
506             );
508             if( tetOwn.mag() < minPartTet )
509             {
510                 if( report )
511                 {
512                     # ifdef USE_OMP
513                     # pragma omp critical
514                     # endif
515                     Pout<< "Zero or negative cell volume detected for cell "
516                         << owner[faceI] << "." << endl;
517                 }
519                 badFace = true;
520             }
522             if( neighbour[faceI] < 0 )
523                 continue;
525             const tetrahedron<point, point> tetNei
526             (
527                 fCentres[faceI],
528                 points[f[eI]],
529                 points[f.nextLabel(eI)],
530                 cCentres[neighbour[faceI]]
531             );
533             if( tetNei.mag() < minPartTet )
534             {
535                 if( report )
536                 {
537                     # ifdef USE_OMP
538                     # pragma omp critical
539                     # endif
540                     Pout<< "Zero or negative cell volume detected for cell "
541                         << neighbour[faceI] << "." << endl;
542                 }
544                 badFace = true;
545             }
546         }
548         if( badFace )
549         {
550             if( setPtr )
551             {
552                 # ifdef USE_OMP
553                 # pragma omp critical
554                 # endif
555                 setPtr->insert(faceI);
556             }
558             ++nNegVolCells;
559         }
560     }
562     if( setPtr )
563     {
564         //- ensure that faces are selected at both sides
565         const PtrList<processorBoundaryPatch>& procBnd = mesh.procBoundaries();
566         forAll(procBnd, patchI)
567         {
568             const label start = procBnd[patchI].patchStart();
569             const label size = procBnd[patchI].patchSize();
571             labelLongList sendData;
572             for(label faceI=0;faceI<size;++faceI)
573             {
574                 if( setPtr->found(faceI+start) )
575                     sendData.append(faceI);
576             }
578             OPstream toOtherProc
579             (
580                 Pstream::blocking,
581                 procBnd[patchI].neiProcNo(),
582                 sendData.byteSize()
583             );
585             toOtherProc << sendData;
586         }
588         forAll(procBnd, patchI)
589         {
590             labelList receivedData;
592             IPstream fromOtherProc
593             (
594                 Pstream::blocking,
595                 procBnd[patchI].neiProcNo()
596             );
598             fromOtherProc >> receivedData;
600             const label start = procBnd[patchI].patchStart();
601             forAll(receivedData, i)
602                 setPtr->insert(start+receivedData[i]);
603         }
604     }
606     reduce(nNegVolCells, sumOp<label>());
608     if( nNegVolCells != 0 )
609     {
610         WarningIn
611         (
612             "bool checkCellPartTetrahedra("
613             "const polyMeshGen&, const bool, const scalar,"
614             " labelHashSet*, const boolList*)"
615         )   << nNegVolCells << " zero or negative part tetrahedra detected."
616             << endl;
618         return true;
619     }
620     else
621     {
622         if( report )
623             Info<< "Part cell tetrahedra OK.\n" << endl;
625         return false;
626     }
629 void checkFaceDotProduct
631     const polyMeshGen& mesh,
632     scalarField& faceDotProduct,
633     const boolList* changedFacePtr
636     //- for all internal faces check theat the d dot S product is positive
637     const vectorField& centres = mesh.addressingData().cellCentres();
638     const vectorField& areas = mesh.addressingData().faceAreas();
640     const labelList& own = mesh.owner();
641     const labelList& nei = mesh.neighbour();
642     const label nInternalFaces = mesh.nInternalFaces();
644     faceDotProduct.setSize(own.size());
645     faceDotProduct = 1.0;
647     # ifdef USE_OMP
648     # pragma omp parallel if( nInternalFaces > 1000 )
649     # endif
650     {
651         # ifdef USE_OMP
652         # pragma omp for schedule(dynamic, 100)
653         # endif
654         for(label faceI=0;faceI<nInternalFaces;++faceI)
655         {
656             if( changedFacePtr && !(*changedFacePtr)[faceI] )
657                 continue;
659             const vector d = centres[nei[faceI]] - centres[own[faceI]];
660             const vector& s = areas[faceI];
662             faceDotProduct[faceI] = (d & s)/(mag(d)*mag(s) + VSMALL);
663         }
664     }
666     if( Pstream::parRun() )
667     {
668         const PtrList<processorBoundaryPatch>& procBoundaries =
669             mesh.procBoundaries();
671         forAll(procBoundaries, patchI)
672         {
673             const label start = procBoundaries[patchI].patchStart();
675             vectorField cCentres(procBoundaries[patchI].patchSize());
676             forAll(cCentres, faceI)
677                 cCentres[faceI] = centres[own[start+faceI]];
679             OPstream toOtherProc
680             (
681                 Pstream::blocking,
682                 procBoundaries[patchI].neiProcNo(),
683                 cCentres.byteSize()
684             );
686             toOtherProc << cCentres;
687         }
689         forAll(procBoundaries, patchI)
690         {
691             vectorField otherCentres;
692             IPstream fromOtherProc
693             (
694                 Pstream::blocking,
695                 procBoundaries[patchI].neiProcNo()
696             );
698             fromOtherProc >> otherCentres;
700             //- calculate skewness at processor faces
701             const label start = procBoundaries[patchI].patchStart();
702             # ifdef USE_OMP
703             # pragma omp parallel
704             # endif
705             {
706                 # ifdef USE_OMP
707                 # pragma omp for schedule(dynamic, 100)
708                 # endif
709                 forAll(otherCentres, fI)
710                 {
711                     const label faceI = start + fI;
713                     if( changedFacePtr && !(*changedFacePtr)[faceI] )
714                         continue;
716                     const point& cOwn = centres[own[faceI]];
717                     const point& cNei = otherCentres[fI];
718                     const vector d = cNei - cOwn;
719                     const vector& s = areas[faceI];
721                     faceDotProduct[faceI] = (d & s)/(mag(d)*mag(s) + VSMALL);
722                 }
723             }
724         }
725     }
728 bool checkFaceDotProduct
730     const polyMeshGen& mesh,
731     const bool report,
732     const scalar nonOrthWarn,
733     labelHashSet* setPtr,
734     const boolList* changedFacePtr
737     //- for all internal faces check theat the d dot S product is positive
738     scalarField faceDotProduct;
739     checkFaceDotProduct(mesh, faceDotProduct, changedFacePtr);
741     const labelList& own = mesh.owner();
742     const labelList& nei = mesh.neighbour();
743     const label nInternalFaces = mesh.nInternalFaces();
745     // Severe nonorthogonality threshold
746     const scalar severeNonorthogonalityThreshold =
747         ::cos(nonOrthWarn/180.0*M_PI);
749     scalar minDDotS = VGREAT;
750     scalar sumDDotS = 0.0;
752     label severeNonOrth = 0;
753     label errorNonOrth = 0;
754     label counter = 0;
756     # ifdef USE_OMP
757     # pragma omp parallel if( nInternalFaces > 1000 ) \
758     reduction(+ : severeNonOrth, errorNonOrth, sumDDotS)
759     # endif
760     {
761         scalar localMinDDotS(VGREAT);
762         # ifdef USE_OMP
763         # pragma omp for schedule(guided)
764         # endif
765         for(label faceI=0;faceI<nInternalFaces;++faceI)
766         {
767             if( changedFacePtr && !(*changedFacePtr)[faceI] )
768                 continue;
770             const scalar dDotS = faceDotProduct[faceI];
772             if( dDotS < severeNonorthogonalityThreshold )
773             {
774                 if( dDotS > SMALL )
775                 {
776                     if( report )
777                     {
778                         // Severe non-orthogonality but mesh still OK
779                         # ifdef USE_OMP
780                         # pragma omp critical
781                         # endif
782                         Pout<< "Severe non-orthogonality for face " << faceI
783                             << " between cells " << own[faceI]
784                             << " and " << nei[faceI]
785                             << ": Angle = "
786                             << ::acos(dDotS)/M_PI*180.0
787                             << " deg." << endl;
788                     }
790                     if( setPtr )
791                     {
792                         # ifdef USE_OMP
793                         # pragma omp critical
794                         # endif
795                         setPtr->insert(faceI);
796                     }
798                     ++severeNonOrth;
799                 }
800                 else
801                 {
802                     ++errorNonOrth;
804                     if( setPtr )
805                     {
806                         # ifdef USE_OMP
807                         # pragma omp critical
808                         # endif
809                         setPtr->insert(faceI);
810                     }
811                 }
812             }
814             localMinDDotS = Foam::min(dDotS, localMinDDotS);
815             sumDDotS += dDotS;
816         }
818         # ifdef USE_OMP
819         # pragma omp critical
820         # endif
821         minDDotS = Foam::min(minDDotS, localMinDDotS);
822     }
824     counter += nInternalFaces;
826     if( Pstream::parRun() )
827     {
828         const PtrList<processorBoundaryPatch>& procBoundaries =
829             mesh.procBoundaries();
831         const label start = procBoundaries[0].patchStart();
833         # ifdef USE_OMP
834         # pragma omp parallel \
835         reduction(+ : severeNonOrth, errorNonOrth, sumDDotS, counter)
836         # endif
837         {
838             scalar localMinDDotS(VGREAT);
840             # ifdef USE_OMP
841             # pragma omp for schedule(dynamic, 100)
842             # endif
843             for(label faceI=start;faceI<own.size();++faceI)
844             {
845                 if( changedFacePtr && !(*changedFacePtr)[start+faceI] )
846                             continue;
848                 const scalar dDotS = faceDotProduct[faceI];
850                 if( dDotS < severeNonorthogonalityThreshold )
851                 {
852                     if( dDotS > SMALL )
853                     {
854                         if( report )
855                         {
856                             // Severe non-orthogonality but mesh still OK
857                             # ifdef USE_OMP
858                             # pragma omp critical
859                             # endif
860                             {
861                                 const scalar angle
862                                 (
863                                     Foam::acos(dDotS) /
864                                     M_PI * 180.0
865                                 );
866                                 Pout<< "Severe non-orthogonality for face "
867                                     << start+faceI
868                                     << ": Angle = "
869                                     << angle
870                                     << " deg." << endl;
871                             }
872                         }
874                         if( setPtr )
875                         {
876                             # ifdef USE_OMP
877                             # pragma omp critical
878                             # endif
879                             setPtr->insert(start+faceI);
880                         }
882                         ++severeNonOrth;
883                     }
884                     else
885                     {
886                         ++errorNonOrth;
888                         if( setPtr )
889                         {
890                             # ifdef USE_OMP
891                             # pragma omp critical
892                             # endif
893                             setPtr->insert(start+faceI);
894                         }
895                     }
896                 }
898                 localMinDDotS = Foam::min(dDotS, localMinDDotS);
899                 sumDDotS += 0.5 * dDotS;
901                 ++counter;
902             }
904             # ifdef USE_OMP
905             # pragma omp critical
906             # endif
907             minDDotS = Foam::min(minDDotS, localMinDDotS);
908         }
909     }
911     reduce(minDDotS, minOp<scalar>());
912     reduce(sumDDotS, sumOp<scalar>());
913     reduce(severeNonOrth, sumOp<label>());
914     reduce(errorNonOrth, sumOp<label>());
916     reduce(counter, sumOp<label>());
918     // Only report if there are some internal faces
919     if( counter > 0 )
920     {
921         if( minDDotS < severeNonorthogonalityThreshold )
922         {
923             Info<< "Number of non-orthogonality errors: " << errorNonOrth
924                 << ". Number of severely non-orthogonal faces: "
925                 << severeNonOrth  << "." << endl;
926         }
927     }
929     if( report )
930     {
931         if( counter > 0 )
932         {
933             Info<< "Mesh non-orthogonality Max: "
934                 << ::acos(minDDotS)/M_PI*180.0
935                 << " average: " <<
936                    ::acos(sumDDotS/counter)/M_PI*180.0
937                 << endl;
938         }
939     }
941     if( errorNonOrth > 0 )
942     {
943         WarningIn
944         (
945             "checkFaceDotProduct("
946             "const polyMeshGen&, const bool, const scalar,"
947             " labelHashSet*, const boolList*)"
948         )   << "Error in non-orthogonality detected" << endl;
950         return true;
951     }
952     else
953     {
954         if( report )
955             Info<< "Non-orthogonality check OK.\n" << endl;
957         return false;
958     }
961 bool checkFacePyramids
963     const polyMeshGen& mesh,
964     const bool report,
965     const scalar minPyrVol,
966     labelHashSet* setPtr,
967     const boolList* changedFacePtr
970     // check whether face area vector points to the cell with higher label
971     const vectorField& ctrs = mesh.addressingData().cellCentres();
973     const labelList& owner = mesh.owner();
974     const labelList& neighbour = mesh.neighbour();
976     const faceListPMG& faces = mesh.faces();
977     const pointFieldPMG& points = mesh.points();
979     label nErrorPyrs = 0;
981     # ifdef USE_OMP
982     # pragma omp parallel for schedule(guided) reduction(+ : nErrorPyrs)
983     # endif
984     forAll(faces, faceI)
985     {
986         if( changedFacePtr && !(*changedFacePtr)[faceI] )
987             continue;
989         // Create the owner pyramid - it will have negative volume
990         const scalar pyrVol = pyramidPointFaceRef
991         (
992             faces[faceI],
993             ctrs[owner[faceI]]
994         ).mag(points);
996         bool badFace(false);
998         if( pyrVol > -minPyrVol )
999         {
1000             if( report )
1001             {
1002                 # ifdef USE_OMP
1003                 # pragma omp critical
1004                 # endif
1005                 Pout<< "bool checkFacePyramids("
1006                     << "const bool, const scalar, labelHashSet*) : "
1007                     << "face " << faceI << " points the wrong way. " << endl
1008                     << "Pyramid volume: " << -pyrVol
1009                     << " Face " << faces[faceI] << " area: "
1010                     << faces[faceI].mag(points)
1011                     << " Owner cell: " << owner[faceI] << endl
1012                     << "Owner cell vertex labels: "
1013                     << mesh.cells()[owner[faceI]].labels(faces)
1014                     << endl;
1015             }
1017             badFace = true;
1018         }
1020         if( neighbour[faceI] != -1 )
1021         {
1022             // Create the neighbour pyramid - it will have positive volume
1023             const scalar pyrVol =
1024                 pyramidPointFaceRef
1025                 (
1026                     faces[faceI],
1027                     ctrs[neighbour[faceI]]
1028                 ).mag(points);
1030             if( pyrVol < minPyrVol )
1031             {
1032                 if( report )
1033                 {
1034                     # ifdef USE_OMP
1035                     # pragma omp critical
1036                     # endif
1037                     Pout<< "bool checkFacePyramids("
1038                         << "const bool, const scalar, labelHashSet*) : "
1039                         << "face " << faceI << " points the wrong way. " << endl
1040                         << "Pyramid volume: " << -pyrVol
1041                         << " Face " << faces[faceI] << " area: "
1042                         << faces[faceI].mag(points)
1043                         << " Neighbour cell: " << neighbour[faceI] << endl
1044                         << "Neighbour cell vertex labels: "
1045                         << mesh.cells()[neighbour[faceI]].labels(faces)
1046                         << endl;
1047                 }
1049                 badFace = true;
1050             }
1051         }
1053         if( badFace )
1054         {
1055             if( setPtr )
1056             {
1057                 # ifdef USE_OMP
1058                 # pragma omp critical
1059                 # endif
1060                 setPtr->insert(faceI);
1061             }
1063             ++nErrorPyrs;
1064         }
1065     }
1067     reduce(nErrorPyrs, sumOp<label>());
1069     if( setPtr )
1070     {
1071         //- make sure that processor faces are marked on both sides
1072         const PtrList<processorBoundaryPatch>& procBoundaries =
1073             mesh.procBoundaries();
1075         //- send and receive data where needed
1076         forAll(procBoundaries, patchI)
1077         {
1078             const label start = procBoundaries[patchI].patchStart();
1079             const label size = procBoundaries[patchI].patchSize();
1081             labelLongList markedFaces;
1082             for(label faceI=0;faceI<size;++faceI)
1083             {
1084                 if( setPtr->found(start+faceI) )
1085                     markedFaces.append(faceI);
1086             }
1088             OPstream toOtherProc
1089             (
1090                 Pstream::blocking,
1091                 procBoundaries[patchI].neiProcNo(),
1092                 markedFaces.byteSize()
1093             );
1095             toOtherProc << markedFaces;
1096         }
1098         forAll(procBoundaries, patchI)
1099         {
1100             labelList receivedData;
1101             IPstream fromOtheProc
1102             (
1103                 Pstream::blocking,
1104                 procBoundaries[patchI].neiProcNo()
1105             );
1106             fromOtheProc >> receivedData;
1108             const label start = procBoundaries[patchI].patchStart();
1109             forAll(receivedData, i)
1110                 setPtr->insert(start+receivedData[i]);
1111         }
1112     }
1114     if( nErrorPyrs > 0 )
1115     {
1116         if( Pstream::master() )
1117             WarningIn
1118             (
1119                 "bool checkFacePyramids("
1120                 "const polyMeshGen&, const bool, const scalar,"
1121                 " labelHashSet*, const boolList*)"
1122             )   << "Error in face pyramids: " << nErrorPyrs
1123                 << " faces pointing the wrong way!"
1124                 << endl;
1126         return true;
1127     }
1128     else
1129     {
1130         if( report )
1131             Info<< "Face pyramids OK.\n" << endl;
1133         return false;
1134     }
1137 void checkFaceSkewness
1139     const polyMeshGen& mesh,
1140     scalarField& faceSkewness,
1141     const boolList* changedFacePtr
1144     //- Warn if the skew correction vector is more than skewWarning times
1145     //- larger than the face area vector
1146     const labelList& own = mesh.owner();
1147     const labelList& nei = mesh.neighbour();
1148     const label nInternalFaces = mesh.nInternalFaces();
1150     const vectorField& centres = mesh.addressingData().cellCentres();
1151     const vectorField& fCentres = mesh.addressingData().faceCentres();
1153     faceSkewness.setSize(own.size());
1154     faceSkewness = 0.0;
1156     //- check internal faces
1157     # ifdef USE_OMP
1158     # pragma omp parallel for schedule(dynamic, 100)
1159     # endif
1160     for(label faceI=0;faceI<nInternalFaces;++faceI)
1161     {
1162         if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1163             continue;
1165         const scalar dOwn = mag(fCentres[faceI] - centres[own[faceI]]);
1166         const scalar dNei = mag(fCentres[faceI] - centres[nei[faceI]]);
1168         const point faceIntersection =
1169             centres[own[faceI]]*dNei/(dOwn+dNei)
1170           + centres[nei[faceI]]*dOwn/(dOwn+dNei);
1172         faceSkewness[faceI] =
1173             mag(fCentres[faceI] - faceIntersection)
1174             /(mag(centres[nei[faceI]] - centres[own[faceI]]) + VSMALL);
1175     }
1177     if( Pstream::parRun() )
1178     {
1179         //- check parallel boundaries
1180         const PtrList<processorBoundaryPatch>& procBoundaries =
1181             mesh.procBoundaries();
1183         forAll(procBoundaries, patchI)
1184         {
1185             const label start = procBoundaries[patchI].patchStart();
1187             vectorField cCentres(procBoundaries[patchI].patchSize());
1188             forAll(cCentres, faceI)
1189                 cCentres[faceI] = centres[own[start+faceI]];
1191             OPstream toOtherProc
1192             (
1193                 Pstream::blocking,
1194                 procBoundaries[patchI].neiProcNo(),
1195                 cCentres.byteSize()
1196             );
1198             toOtherProc << cCentres;
1199         }
1201         forAll(procBoundaries, patchI)
1202         {
1203             const label start = procBoundaries[patchI].patchStart();
1205             vectorField otherCentres;
1206             IPstream fromOtheProc
1207             (
1208                 Pstream::blocking,
1209                 procBoundaries[patchI].neiProcNo()
1210             );
1212             fromOtheProc >> otherCentres;
1214             //- calculate skewness at processor faces
1215             # ifdef USE_OMP
1216             # pragma omp parallel
1217             # endif
1218             {
1219                 # ifdef USE_OMP
1220                 # pragma omp for schedule(dynamic, 100)
1221                 # endif
1222                 forAll(otherCentres, fI)
1223                 {
1224                     const label faceI = start + fI;
1226                     if(
1227                         changedFacePtr &&
1228                         !changedFacePtr->operator[](faceI)
1229                     )
1230                         continue;
1232                     const point& cOwn = centres[own[faceI]];
1233                     const point& cNei = otherCentres[fI];
1234                     const scalar dOwn = mag(fCentres[faceI] - cOwn);
1235                     const scalar dNei = mag(fCentres[faceI] - cNei);
1237                     const point faceIntersection =
1238                         cOwn*dNei/(dOwn+dNei)
1239                       + cNei*dOwn/(dOwn+dNei);
1241                     faceSkewness[faceI] =
1242                         mag(fCentres[faceI] - faceIntersection)
1243                         /(mag(cOwn - cNei) + VSMALL);
1244                 }
1245             }
1246         }
1247     }
1249     //- boundary faces
1250     const faceListPMG& faces = mesh.faces();
1251     const pointFieldPMG& points = mesh.points();
1252     const PtrList<boundaryPatch>& boundaries = mesh.boundaries();
1253     forAll(boundaries, patchI)
1254     {
1255         label faceI = boundaries[patchI].patchStart();
1256         const label end = faceI + boundaries[patchI].patchSize();
1258         for(;faceI<end;++faceI)
1259         {
1260             const vector d = fCentres[faceI] - centres[own[faceI]];
1262             vector n = faces[faceI].normal(points);
1263             const scalar magn = mag(n);
1264             if( magn > VSMALL )
1265             {
1266                 n /= magn;
1267             }
1268             else
1269             {
1270                 continue;
1271             }
1273             const vector dn = (n & d) * n;
1275             faceSkewness[faceI] = mag(d - dn) / (mag(d) + VSMALL);
1276         }
1277     }
1280 bool checkFaceSkewness
1282     const polyMeshGen& mesh,
1283     const bool report,
1284     const scalar warnSkew,
1285     labelHashSet* setPtr,
1286     const boolList* changedFacePtr
1289     scalarField faceSkewness;
1290     checkFaceSkewness(mesh, faceSkewness, changedFacePtr);
1292     //- Warn if the skew correction vector is more than skewWarning times
1293     //- larger than the face area vector
1294     scalar maxSkew = 0.0;
1295     scalar sumSkew = 0.0;
1297     label nWarnSkew = 0;
1299     //- check faces
1300     # ifdef USE_OMP
1301     # pragma omp parallel \
1302     reduction(+ : sumSkew, nWarnSkew)
1303     # endif
1304     {
1305         scalar localMaxSkew(0.0);
1307         # ifdef USE_OMP
1308         # pragma omp for schedule(dynamic, 100)
1309         # endif
1310         forAll(faceSkewness, faceI)
1311         {
1312             if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1313                 continue;
1315             const scalar skewness = faceSkewness[faceI];
1317             // Check if the skewness vector is greater than the PN vector.
1318             if( skewness > warnSkew )
1319             {
1320                 if( report )
1321                 {
1322                     # ifdef USE_OMP
1323                     # pragma omp critical
1324                     # endif
1325                     Pout<< " Severe skewness for face " << faceI
1326                         << " skewness = " << skewness << endl;
1327                 }
1329                 if( setPtr )
1330                 {
1331                     # ifdef USE_OMP
1332                     # pragma omp critical
1333                     # endif
1334                     setPtr->insert(faceI);
1335                 }
1337                 ++nWarnSkew;
1338             }
1340             localMaxSkew = Foam::max(localMaxSkew, skewness);
1341             sumSkew += skewness;
1342         }
1344         # ifdef USE_OMP
1345         # pragma omp critical
1346         # endif
1347         maxSkew = Foam::max(maxSkew, localMaxSkew);
1348     }
1350     reduce(maxSkew, maxOp<scalar>());
1351     reduce(sumSkew, sumOp<scalar>());
1352     reduce(nWarnSkew, sumOp<label>());
1354     if( nWarnSkew > 0 )
1355     {
1356         WarningIn
1357         (
1358             "checkFaceSkewness("
1359             "const polyMeshGen&, const bool, const scalar,"
1360             "labelHashSet*, const boolList*)"
1361         )   << "Large face skewness detected.  Max skewness = " << maxSkew
1362             << " Average skewness = " << sumSkew/faceSkewness.size()
1363             << ".\nThis may impair the quality of the result." << nl
1364             << nWarnSkew << " highly skew faces detected."
1365             << endl;
1367         return true;
1368     }
1369     else
1370     {
1371         if( report )
1372             Info<< "Max skewness = " << maxSkew
1373                 << " Average skewness = " << sumSkew/faceSkewness.size()
1374                 << ".  Face skewness OK.\n" << endl;
1376         return false;
1377     }
1380 void checkFaceUniformity
1382     const polyMeshGen& mesh,
1383     scalarField& faceUniformity,
1384     const boolList* changedFacePtr
1387     //- for all internal faces check the uniformity of the mesh at faces
1388     const vectorField& centres = mesh.addressingData().cellCentres();
1389     const vectorField& fCentres = mesh.addressingData().faceCentres();
1391     const labelList& owner = mesh.owner();
1392     const labelList& neighbour = mesh.neighbour();
1393     const label nInternalFaces = mesh.nInternalFaces();
1395     faceUniformity.setSize(owner.size());
1396     faceUniformity = 0.5;
1398     # ifdef USE_OMP
1399     # pragma omp parallel for schedule(dynamic, 100)
1400     # endif
1401     for(label faceI=0;faceI<nInternalFaces;++faceI)
1402     {
1403         if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1404             continue;
1406         const scalar dOwn
1407         (
1408             Foam::mag(centres[owner[faceI]] - fCentres[faceI])
1409         );
1410         const scalar dNei
1411         (
1412             Foam::mag(centres[neighbour[faceI]] - fCentres[faceI])
1413         );
1415         faceUniformity[faceI] = Foam::min(dOwn, dNei) / (dOwn + dNei);
1416     }
1418     if( Pstream::parRun() )
1419     {
1420         const PtrList<processorBoundaryPatch>& procBoundaries =
1421             mesh.procBoundaries();
1423         forAll(procBoundaries, patchI)
1424         {
1425             scalarField dst(procBoundaries[patchI].patchSize());
1426             const label start = procBoundaries[patchI].patchStart();
1428             forAll(dst, faceI)
1429             {
1430                 const label fI = start + faceI;
1431                 dst[faceI] = Foam::mag(centres[owner[fI]] - fCentres[fI]);
1432             }
1434             OPstream toOtherProc
1435             (
1436                 Pstream::blocking,
1437                 procBoundaries[patchI].neiProcNo(),
1438                 dst.byteSize()
1439             );
1441             toOtherProc << dst;
1442         }
1444         forAll(procBoundaries, patchI)
1445         {
1446             const label start = procBoundaries[patchI].patchStart();
1448             scalarField otherDst;
1449             IPstream fromOtheProc
1450             (
1451                 Pstream::blocking,
1452                 procBoundaries[patchI].neiProcNo()
1453             );
1455             fromOtheProc >> otherDst;
1457             forAll(otherDst, faceI)
1458             {
1459                 const label fI = start + faceI;
1460                 const scalar dOwn =
1461                     Foam::mag(centres[owner[fI]] - fCentres[fI]);
1462                 const scalar dNei = otherDst[faceI];
1463                 faceUniformity[fI] = Foam::min(dOwn, dNei) / (dOwn + dNei);
1464             }
1465         }
1466     }
1469 bool checkFaceUniformity
1471     const polyMeshGen& mesh,
1472     const bool report,
1473     const scalar warnUniform,
1474     labelHashSet* setPtr,
1475     const boolList* changedFacePtr
1478     scalarField faceUniformity;
1479     checkFaceUniformity(mesh, faceUniformity, changedFacePtr);
1481     //- for all internal faces check the uniformity of the mesh at faces
1482     const label nInternalFaces = mesh.nInternalFaces();
1484     scalar maxUniformity = 0.0;
1485     scalar minUniformity = VGREAT;
1487     scalar sumUniformity = 0.0;
1489     label severeNonUniform = 0;
1491     # ifdef USE_OMP
1492     # pragma omp parallel \
1493     reduction(+ : sumUniformity, severeNonUniform)
1494     # endif
1495     {
1496         scalar localMinUniformity(VGREAT);
1497         scalar localMaxUniformity(0.0);
1499         # ifdef USE_OMP
1500         # pragma omp for schedule(guided)
1501         # endif
1502         for(label faceI=0;faceI<nInternalFaces;++faceI)
1503         {
1504             if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1505                 continue;
1507             const scalar uniformity = faceUniformity[faceI];
1509             if( uniformity < warnUniform )
1510             {
1511                 if( setPtr )
1512                 {
1513                     # ifdef USE_OMP
1514                     # pragma omp critical
1515                     # endif
1516                     setPtr->insert(faceI);
1517                 }
1519                 ++severeNonUniform;
1520             }
1522             localMaxUniformity = Foam::max(localMaxUniformity, uniformity);
1523             localMinUniformity = Foam::min(localMinUniformity, uniformity);
1524             sumUniformity += uniformity;
1525         }
1527         # ifdef USE_OMP
1528         # pragma omp critical
1529         # endif
1530         {
1531             maxUniformity = Foam::max(maxUniformity, localMaxUniformity);
1532             minUniformity = Foam::min(minUniformity, localMinUniformity);
1533         }
1534     }
1536     label counter = nInternalFaces;
1538     if( Pstream::parRun() )
1539     {
1540         const PtrList<processorBoundaryPatch>& procBoundaries =
1541             mesh.procBoundaries();
1543         forAll(procBoundaries, patchI)
1544         {
1545             const label start = procBoundaries[patchI].patchStart();
1546             const label size = procBoundaries[patchI].patchSize();
1548             for(label fI=0;fI<size;++fI)
1549             {
1550                 const label faceI = start + fI;
1552                 const scalar uniformity = faceUniformity[faceI];
1554                 if( uniformity < warnUniform )
1555                 {
1556                     if( setPtr)
1557                         setPtr->insert(faceI);
1559                     ++severeNonUniform;
1560                 }
1562                 maxUniformity = Foam::max(maxUniformity, uniformity);
1563                 minUniformity = Foam::min(minUniformity, uniformity);
1564                 sumUniformity += 0.5 * uniformity;
1565             }
1567             if( procBoundaries[patchI].owner() )
1568                 counter += size;
1569         }
1570     }
1572     reduce(maxUniformity, maxOp<scalar>());
1573     reduce(minUniformity, minOp<scalar>());
1574     reduce(sumUniformity, sumOp<scalar>());
1575     reduce(severeNonUniform, sumOp<label>());
1576     reduce(counter, sumOp<label>());
1578     // Only report if there are some internal faces
1579     if( counter > 0 )
1580     {
1581         if( minUniformity < warnUniform )
1582             Info<< "Number of severely non-uniform faces: "
1583                 << severeNonUniform << "." << endl;
1584     }
1586     if( report )
1587     {
1588         if( counter > 0 )
1589             Info<< "Mesh non-uniformity Max: "
1590                 << maxUniformity
1591                 << " Min: " << minUniformity
1592                 << " average: " << sumUniformity/counter
1593                 << endl;
1594     }
1596     return false;
1599 void checkVolumeUniformity
1601     const polyMeshGen& mesh,
1602     scalarField& faceUniformity,
1603     const boolList* changedFacePtr
1606 bool checkVolumeUniformity
1608     const polyMeshGen&,
1609     const bool report,
1610     const scalar warnUniform,
1611     labelHashSet* setPtr,
1612     const boolList* changedFacePtr
1616     return false;
1619 bool checkFaceAngles
1621     const polyMeshGen& mesh,
1622     const bool report,
1623     const scalar maxDeg,
1624     labelHashSet* setPtr,
1625     const boolList* changedFacePtr
1628     if( maxDeg < -SMALL || maxDeg > 180+SMALL )
1629     {
1630         FatalErrorIn
1631         (
1632             "bool checkFaceAngles("
1633             "const polyMeshGen&, const bool, const scalar,"
1634             " labelHashSet*, const boolList*)"
1635         )   << "maxDeg should be [0..180] but is now " << maxDeg
1636             << abort(FatalError);
1637     }
1639     const scalar maxSin = Foam::sin(maxDeg/180.0*M_PI);
1641     const pointFieldPMG& points = mesh.points();
1642     const faceListPMG& faces = mesh.faces();
1643     vectorField faceNormals(mesh.addressingData().faceAreas());
1644     faceNormals /= mag(faceNormals) + VSMALL;
1646     scalar maxEdgeSin = 0.0;
1648     label nConcave = 0;
1650     # ifdef USE_OMP
1651     # pragma omp parallel reduction(+ : nConcave)
1652     # endif
1653     {
1654         scalar localMaxEdgeSin(0.0);
1655         label errorFaceI(-1);
1657         # ifdef USE_OMP
1658         # pragma omp for schedule(guided)
1659         # endif
1660         forAll(faces, faceI)
1661         {
1662             if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1663                 continue;
1665             const face& f = faces[faceI];
1667             // Get edge from f[0] to f[size-1];
1668             vector ePrev(points[f[0]] - points[f[f.size()-1]]);
1669             scalar magEPrev = mag(ePrev);
1670             ePrev /= magEPrev + VSMALL;
1672             forAll(f, fp0)
1673             {
1674                 // Get vertex after fp
1675                 label fp1 = f.fcIndex(fp0);
1677                 // Normalized vector between two consecutive points
1678                 vector e10(points[f[fp1]] - points[f[fp0]]);
1679                 scalar magE10 = mag(e10);
1680                 e10 /= magE10 + VSMALL;
1682                 if( magEPrev > SMALL && magE10 > SMALL )
1683                 {
1684                     vector edgeNormal = ePrev ^ e10;
1685                     scalar magEdgeNormal = mag(edgeNormal);
1687                     if( magEdgeNormal < maxSin )
1688                     {
1689                         // Edges (almost) aligned -> face is ok.
1690                     }
1691                     else
1692                     {
1693                         // Check normal
1694                         edgeNormal /= magEdgeNormal;
1696                         if( (edgeNormal & faceNormals[faceI]) < SMALL )
1697                         {
1698                             # ifdef USE_OMP
1699                             # pragma omp critical
1700                             # endif
1701                             {
1702                                 if( faceI != errorFaceI )
1703                                 {
1704                                     // Count only one error per face.
1705                                     errorFaceI = faceI;
1706                                     ++nConcave;
1707                                 }
1709                                 if( setPtr )
1710                                     setPtr->insert(faceI);
1712                                 localMaxEdgeSin =
1713                                     Foam::max(localMaxEdgeSin, magEdgeNormal);
1714                             }
1715                         }
1716                     }
1717                 }
1719                 ePrev = e10;
1720                 magEPrev = magE10;
1721             }
1722         }
1724         # ifdef USE_OMP
1725         # pragma omp critical
1726         # endif
1727         maxEdgeSin = Foam::max(maxEdgeSin, localMaxEdgeSin);
1728     }
1730     reduce(nConcave, sumOp<label>());
1731     reduce(maxEdgeSin, maxOp<scalar>());
1733     if( report )
1734     {
1735         if( maxEdgeSin > SMALL )
1736         {
1737             scalar maxConcaveDegr =
1738                 Foam::asin(Foam::min(1.0, maxEdgeSin))
1739              * 180.0/M_PI;
1741             Warning<< "There are " << nConcave
1742                 << " faces with concave angles between consecutive"
1743                 << " edges. Max concave angle = "
1744                 << maxConcaveDegr
1745                 << " degrees.\n" << endl;
1746         }
1747         else
1748         {
1749             Info<< "All angles in faces are convex or less than "  << maxDeg
1750                 << " degrees concave.\n" << endl;
1751         }
1752     }
1754     if( nConcave > 0 )
1755     {
1756         WarningIn
1757         (
1758             "bool checkFaceAngles("
1759             "const polyMeshGen&, const bool, const scalar,"
1760             " labelHashSet*, const boolList*)"
1761         )   << nConcave  << " face points with severe concave angle (> "
1762             << maxDeg << " deg) found.\n"
1763             << endl;
1765         return true;
1766     }
1767     else
1768     {
1769         return false;
1770     }
1773 // Check warpage of faces. Is calculated as the difference between areas of
1774 // individual triangles and the overall area of the face (which ifself is
1775 // is the average of the areas of the individual triangles).
1776 bool checkFaceFlatness
1778     const polyMeshGen& mesh,
1779     const bool report,
1780     const scalar warnFlatness,
1781     labelHashSet* setPtr,
1782     const boolList* changedFacePtr
1785     if( warnFlatness < 0 || warnFlatness > 1 )
1786     {
1787         FatalErrorIn
1788         (
1789             "bool checkFaceFlatness("
1790             "const polyMeshGen&, const bool, const scalar,"
1791             " labelHashSet*, const boolList*)"
1792         )   << "warnFlatness should be [0..1] but is now " << warnFlatness
1793             << abort(FatalError);
1794     }
1796     const pointFieldPMG& points = mesh.points();
1797     const faceListPMG& faces = mesh.faces();
1798     const vectorField& fctrs = mesh.addressingData().faceCentres();
1800     // Areas are calculated as the sum of areas. (see
1801     // polyMeshGenAddressingFaceCentresAndAreas.C)
1802     scalarField magAreas(mag(mesh.addressingData().faceAreas()));
1804     label nWarped = 0;
1806     scalar minFlatness = GREAT;
1807     scalar sumFlatness = 0;
1808     label nSummed = 0;
1810     # ifdef USE_OMP
1811     # pragma omp parallel if( faces.size() > 1000 ) \
1812     reduction(+ : nSummed, nWarped) reduction(+ : sumFlatness)
1813     # endif
1814     {
1815         scalar minFlatnessProc = VGREAT;
1817         # ifdef USE_OMP
1818         # pragma omp for schedule(guided)
1819         # endif
1820         forAll(faces, faceI)
1821         {
1822             if( changedFacePtr && !(*changedFacePtr)[faceI] )
1823                 continue;
1825             const face& f = faces[faceI];
1827             if( f.size() > 3 && magAreas[faceI] > VSMALL )
1828             {
1829                 const point& fc = fctrs[faceI];
1831                 //- Calculate the sum of magnitude of areas and compare
1832                 //- the magnitude of sum of areas.
1834                 scalar sumA = 0.0;
1836                 forAll(f, fp)
1837                 {
1838                     const point& thisPoint = points[f[fp]];
1839                     const point& nextPoint = points[f.nextLabel(fp)];
1841                     // Triangle around fc.
1842                     vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint));
1843                     sumA += mag(n);
1844                 }
1846                 scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1848                 sumFlatness += flatness;
1849                 ++nSummed;
1851                 minFlatnessProc = Foam::min(minFlatnessProc, flatness);
1853                 if( flatness < warnFlatness )
1854                 {
1855                     ++nWarped;
1857                     if( setPtr )
1858                     {
1859                         # ifdef USE_OMP
1860                         # pragma omp critical
1861                         # endif
1862                         setPtr->insert(faceI);
1863                     }
1864                 }
1865             }
1866         }
1868         # ifdef USE_OMP
1869         # pragma omp critical
1870         # endif
1871         {
1872             minFlatness = Foam::min(minFlatness, minFlatnessProc);
1873         }
1874     }
1876     if( Pstream::parRun() && setPtr )
1877     {
1878         //- make sure that processor faces are marked on both sides
1879         const PtrList<processorBoundaryPatch>& procBoundaries =
1880             mesh.procBoundaries();
1882         List<DynList<label> > markedFaces(procBoundaries.size());
1883         forAll(procBoundaries, patchI)
1884         {
1885             const label start = procBoundaries[patchI].patchStart();
1886             const label size = procBoundaries[patchI].patchSize();
1888             for(label i=0;i<size;++i)
1889                 if( setPtr->found(start+i) )
1890                     markedFaces[patchI].append(i);
1891         }
1893         //- exchange list sizes
1894         forAll(procBoundaries, patchI)
1895         {
1896             OPstream toOtherProc
1897             (
1898                 Pstream::blocking,
1899                 procBoundaries[patchI].neiProcNo(),
1900                 sizeof(label)
1901             );
1903             toOtherProc << markedFaces[patchI].size();
1904         }
1906         labelList nMarkedOnOtherProcs(procBoundaries.size());
1907         forAll(procBoundaries, patchI)
1908         {
1909             IPstream fromOtheProc
1910             (
1911                 Pstream::blocking,
1912                 procBoundaries[patchI].neiProcNo(),
1913                 sizeof(label)
1914             );
1916             fromOtheProc >> nMarkedOnOtherProcs[patchI];
1917         }
1919         //- exchange data
1920         forAll(procBoundaries, patchI)
1921         {
1922             if( markedFaces[patchI].size() == 0 )
1923                 continue;
1925             OPstream toOtherProc
1926             (
1927                 Pstream::blocking,
1928                 procBoundaries[patchI].neiProcNo(),
1929                 markedFaces[patchI].byteSize()
1930             );
1932             toOtherProc << markedFaces[patchI];
1933         }
1935         forAll(procBoundaries, patchI)
1936         {
1937             if( nMarkedOnOtherProcs[patchI] == 0 )
1938                 continue;
1940             labelList receivedData;
1941             IPstream fromOtheProc
1942             (
1943                 Pstream::blocking,
1944                 procBoundaries[patchI].neiProcNo(),
1945                 nMarkedOnOtherProcs[patchI]*sizeof(label)
1946             );
1947             fromOtheProc >> receivedData;
1949             const label start = procBoundaries[patchI].patchStart();
1950             forAll(receivedData, i)
1951                 if( !setPtr->found(start+receivedData[i]) )
1952                     setPtr->insert(start+receivedData[i]);
1953         }
1954     }
1956     reduce(nWarped, sumOp<label>());
1957     reduce(minFlatness, minOp<scalar>());
1959     reduce(nSummed, sumOp<label>());
1960     reduce(sumFlatness, sumOp<scalar>());
1962     if( report )
1963     {
1964         if( nSummed > 0 )
1965         {
1966             Info<< "Face flatness (1 = flat, 0 = butterfly) : average = "
1967                 << sumFlatness / nSummed << "  min = " << minFlatness << endl;
1968         }
1970         if( nWarped> 0 )
1971         {
1972             Info<< "There are " << nWarped
1973                 << " faces with ratio between projected and actual area < "
1974                 << warnFlatness
1975                 << ".\nMinimum ratio (minimum flatness, maximum warpage) = "
1976                 << minFlatness << nl << endl;
1977         }
1978         else
1979         {
1980             Info<< "All faces are flat in that the ratio between projected"
1981                 << " and actual area is > " << warnFlatness << nl << endl;
1982         }
1983     }
1985     if( nWarped > 0 )
1986     {
1987         WarningIn
1988         (
1989             "bool checkFaceFlatness("
1990             "const polyMeshGen&, const bool, const scalar,"
1991             " labelHashSet*, const boolList*)"
1992         )   << nWarped  << " faces with severe warpage (flatness < "
1993             << warnFlatness << ") found.\n"
1994             << endl;
1996         return true;
1997     }
1998     else
1999     {
2000         return false;
2001     }
2004 label findBadFacesRelaxed
2006     const polyMeshGen& mesh,
2007     labelHashSet& badFaces,
2008     const bool report,
2009     const boolList* activeFacePtr
2012     badFaces.clear();
2014     polyMeshGenChecks::checkFacePyramids
2015     (
2016         mesh,
2017         report,
2018         VSMALL,
2019         &badFaces,
2020         activeFacePtr
2021     );
2023     polyMeshGenChecks::checkFaceAreas
2024     (
2025         mesh,
2026         report,
2027         VSMALL,
2028         &badFaces,
2029         activeFacePtr
2030     );
2032     const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2034     return nBadFaces;
2037 label findBadFaces
2039     const polyMeshGen& mesh,
2040     labelHashSet& badFaces,
2041     const bool report,
2042     const boolList* activeFacePtr
2045     badFaces.clear();
2047     polyMeshGenChecks::checkFacePyramids
2048     (
2049         mesh,
2050         report,
2051         VSMALL,
2052         &badFaces,
2053         activeFacePtr
2054     );
2056     polyMeshGenChecks::checkFaceFlatness
2057     (
2058         mesh,
2059         report,
2060         0.8,
2061         &badFaces,
2062         activeFacePtr
2063     );
2065     polyMeshGenChecks::checkCellPartTetrahedra
2066     (
2067         mesh,
2068         report,
2069         VSMALL,
2070         &badFaces,
2071         activeFacePtr
2072     );
2074     polyMeshGenChecks::checkFaceAreas
2075     (
2076         mesh,
2077         report,
2078         VSMALL,
2079         &badFaces,
2080         activeFacePtr
2081     );
2083     const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2085     return nBadFaces;
2088 label findLowQualityFaces
2090     const polyMeshGen& mesh,
2091     labelHashSet& badFaces,
2092     const bool report,
2093     const boolList* activeFacePtr
2096     badFaces.clear();
2098     polyMeshGenChecks::checkFaceDotProduct
2099     (
2100         mesh,
2101         report,
2102         65.0,
2103         &badFaces,
2104         activeFacePtr
2105     );
2107     polyMeshGenChecks::checkFaceSkewness
2108     (
2109         mesh,
2110         report,
2111         2.0,
2112         &badFaces,
2113         activeFacePtr
2114     );
2116     const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2118     return nBadFaces;
2121 label findWorstQualityFaces
2123     const polyMeshGen& mesh,
2124     labelHashSet& badFaces,
2125     const bool report,
2126     const boolList* activeFacePtr,
2127     const scalar relativeThreshold
2130     badFaces.clear();
2132     scalarField checkValues;
2133     polyMeshGenChecks::checkFaceDotProduct
2134     (
2135         mesh,
2136         checkValues,
2137         activeFacePtr
2138     );
2140     scalar minNonOrtho = returnReduce(min(checkValues), minOp<scalar>());
2141     const scalar warnNonOrtho =
2142         minNonOrtho + relativeThreshold * (1.0 - minNonOrtho);
2144     Info << "Worst non-orthogonality " << Foam::acos(minNonOrtho) * 180.0 / M_PI
2145          << " selecting faces with non-orthogonality greater than "
2146          << (Foam::acos(warnNonOrtho) * 180.0 / M_PI) << endl;
2148     forAll(checkValues, faceI)
2149     {
2150         if
2151         (
2152             activeFacePtr && activeFacePtr->operator[](faceI) &&
2153             checkValues[faceI] < warnNonOrtho
2154         )
2155             badFaces.insert(faceI);
2156     }
2158     polyMeshGenChecks::checkFaceSkewness
2159     (
2160         mesh,
2161         checkValues,
2162         activeFacePtr
2163     );
2165     const scalar maxSkew = returnReduce(max(checkValues), maxOp<scalar>());
2166     const scalar warnSkew = (1.0 - relativeThreshold) * maxSkew;
2167     forAll(checkValues, faceI)
2168         if
2169         (
2170             activeFacePtr && activeFacePtr->operator[](faceI) &&
2171             checkValues[faceI] > warnSkew
2172         )
2173             badFaces.insert(faceI);
2175     Info << "Maximum skewness in the mesh is " << maxSkew
2176          << " selecting faces with skewness greater than " << warnSkew << endl;
2178     const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2180     Info << "Selected " << nBadFaces
2181          << " out of " << returnReduce(checkValues.size(), sumOp<label>())
2182          << " faces" << endl;
2184     return nBadFaces;
2187 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2189 } // End namespace polyMeshGenChecks
2191 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2193 } // End namespace Foam
2195 // ************************************************************************* //