1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | cfMesh: A library for mesh generation
5 \\ / A nd | Copyright held by the origina author
7 -------------------------------------------------------------------------------
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
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"
35 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
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)
56 sumClosed += areas[faceI];
57 sumMagClosedBoundary += mag(areas[faceI]);
60 // Check the openness in the maximum direction (this is APPROXIMATE!)
63 sumClosed.component(vector::X),
66 sumClosed.component(vector::Y),
67 sumClosed.component(vector::Z)
71 reduce(sumClosed, sumOp<vector>());
72 reduce(maxOpen, maxOp<scalar>());
74 if( maxOpen > SMALL*max(1.0, sumMagClosedBoundary) )
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;
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;
112 bool checkClosedCells
114 const polyMeshGen& mesh,
116 const scalar aspectWarn,
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;
127 # pragma omp parallel for schedule(guided) reduction(+ : nErrorClosed)
131 const cell& curCell = cells[cI];
133 if( min(curCell) < 0 || max(curCell) > nFaces )
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;
145 # pragma omp critical
154 if( nErrorClosed > 0 )
158 "bool checkClosedCells("
159 "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
160 ) << nErrorClosed << " cells with invalid face labels found"
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();
179 sumClosed[own[faceI]] += areas[faceI];
180 sumMagClosed[own[faceI]] += mag(areas[faceI]);
182 if( nei[faceI] == -1 )
185 // Subtract from neighbour
186 sumClosed[nei[faceI]] -= areas[faceI];
187 sumMagClosed[nei[faceI]] += mag(areas[faceI]);
191 scalar maxOpenCell = 0;
194 scalar maxAspectRatio = 0;
196 const scalarField& vols = mesh.addressingData().cellVolumes();
199 forAll(sumClosed, cellI)
203 sumClosed[cellI].component(vector::X),
206 sumClosed[cellI].component(vector::Y),
207 sumClosed[cellI].component(vector::Z)
211 maxOpenCell = max(maxOpenCell, maxOpen);
213 if( maxOpen > SMALL*max(1.0, sumMagClosed[cellI]) )
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;
229 setPtr->insert(cellI);
236 1.0/6.0*sumMagClosed[cellI]/pow(vols[cellI], 2.0/3.0);
238 maxAspectRatio = max(maxAspectRatio, aspectRatio);
240 if( aspectRatio > aspectWarn )
244 Pout<< "High aspect ratio for cell " << cellI
245 << ": " << aspectRatio << endl;
252 reduce(nOpen, sumOp<label>());
253 reduce(maxOpenCell, maxOp<scalar>());
255 reduce(nAspect, sumOp<label>());
256 reduce(maxAspectRatio, maxOp<scalar>());
262 "bool checkClosedCells("
263 "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
264 ) << nOpen << " open cells found. Max cell openness: "
265 << maxOpenCell << endl;
274 "bool checkClosedCells("
275 "const polyMeshGen&, const bool, const scalar, labelHashSet*)"
276 ) << nAspect << " high aspect ratio cells found. "
277 << "Max aspect ratio: " << maxAspectRatio
285 Info<< "Max cell openness = " << maxOpenCell
286 << " Max aspect ratio = " << maxAspectRatio
287 << ". All cells OK.\n" << endl;
293 bool checkCellVolumes
295 const polyMeshGen& mesh,
300 const scalarField& vols = mesh.addressingData().cellVolumes();
302 scalar minVolume = GREAT;
303 scalar maxVolume = -GREAT;
305 label nNegVolCells = 0;
309 if( vols[cellI] < VSMALL )
314 "bool checkCellVolumes("
315 "const polyMeshGen&, const bool, labelHashSet*)"
316 ) << "Zero or negative cell volume detected for cell "
317 << cellI << ". Volume = " << vols[cellI] << endl;
320 setPtr->insert(cellI);
325 minVolume = min(minVolume, vols[cellI]);
326 maxVolume = max(maxVolume, vols[cellI]);
329 reduce(minVolume, minOp<scalar>());
330 reduce(maxVolume, maxOp<scalar>());
331 reduce(nNegVolCells, sumOp<label>());
333 if( minVolume < VSMALL )
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"
351 Info<< "Min volume = " << minVolume
352 << ". Max volume = " << maxVolume
353 << ". Total volume = " << sum(vols)
354 << ". Cell volumes OK.\n" << endl;
363 const polyMeshGen& mesh,
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;
379 # pragma omp parallel if( own.size() > 100 )
382 scalar localMaxArea(-VGREAT), localMinArea(VGREAT);
385 # pragma omp for schedule(guided)
387 forAll(faceAreas, faceI)
389 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
392 const scalar magFaceArea = mag(faceAreas[faceI]);
394 if( magFaceArea < minFaceArea )
398 if( nei[faceI] != -1 )
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;
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;
418 # pragma omp critical
420 setPtr->insert(faceI);
424 localMinArea = Foam::min(localMinArea, magFaceArea);
425 localMaxArea = Foam::max(localMaxArea, magFaceArea);
429 # pragma omp critical
432 minArea = Foam::min(minArea, localMinArea);
433 maxArea = Foam::max(maxArea, localMaxArea);
437 reduce(minArea, minOp<scalar>());
438 reduce(maxArea, maxOp<scalar>());
440 if( minArea < VSMALL )
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"
457 Info<< "Minumum face area = " << minArea
458 << ". Maximum face area = " << maxArea
459 << ". Face area magnitudes OK.\n" << endl;
466 bool checkCellPartTetrahedra
468 const polyMeshGen& mesh,
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;
486 # pragma omp parallel for if( owner.size() > 100 ) \
487 schedule(guided) reduction(+ : nNegVolCells)
491 if( changedFacePtr && !(*changedFacePtr)[faceI] )
494 const face& f = faces[faceI];
500 const tetrahedron<point, point> tetOwn
503 points[f.nextLabel(eI)],
505 cCentres[owner[faceI]]
508 if( tetOwn.mag() < minPartTet )
513 # pragma omp critical
515 Pout<< "Zero or negative cell volume detected for cell "
516 << owner[faceI] << "." << endl;
522 if( neighbour[faceI] < 0 )
525 const tetrahedron<point, point> tetNei
529 points[f.nextLabel(eI)],
530 cCentres[neighbour[faceI]]
533 if( tetNei.mag() < minPartTet )
538 # pragma omp critical
540 Pout<< "Zero or negative cell volume detected for cell "
541 << neighbour[faceI] << "." << endl;
553 # pragma omp critical
555 setPtr->insert(faceI);
564 //- ensure that faces are selected at both sides
565 const PtrList<processorBoundaryPatch>& procBnd = mesh.procBoundaries();
566 forAll(procBnd, patchI)
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)
574 if( setPtr->found(faceI+start) )
575 sendData.append(faceI);
581 procBnd[patchI].neiProcNo(),
585 toOtherProc << sendData;
588 forAll(procBnd, patchI)
590 labelList receivedData;
592 IPstream fromOtherProc
595 procBnd[patchI].neiProcNo()
598 fromOtherProc >> receivedData;
600 const label start = procBnd[patchI].patchStart();
601 forAll(receivedData, i)
602 setPtr->insert(start+receivedData[i]);
606 reduce(nNegVolCells, sumOp<label>());
608 if( nNegVolCells != 0 )
612 "bool checkCellPartTetrahedra("
613 "const polyMeshGen&, const bool, const scalar,"
614 " labelHashSet*, const boolList*)"
615 ) << nNegVolCells << " zero or negative part tetrahedra detected."
623 Info<< "Part cell tetrahedra OK.\n" << endl;
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;
648 # pragma omp parallel if( nInternalFaces > 1000 )
652 # pragma omp for schedule(dynamic, 100)
654 for(label faceI=0;faceI<nInternalFaces;++faceI)
656 if( changedFacePtr && !(*changedFacePtr)[faceI] )
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);
666 if( Pstream::parRun() )
668 const PtrList<processorBoundaryPatch>& procBoundaries =
669 mesh.procBoundaries();
671 forAll(procBoundaries, patchI)
673 const label start = procBoundaries[patchI].patchStart();
675 vectorField cCentres(procBoundaries[patchI].patchSize());
676 forAll(cCentres, faceI)
677 cCentres[faceI] = centres[own[start+faceI]];
682 procBoundaries[patchI].neiProcNo(),
686 toOtherProc << cCentres;
689 forAll(procBoundaries, patchI)
691 vectorField otherCentres;
692 IPstream fromOtherProc
695 procBoundaries[patchI].neiProcNo()
698 fromOtherProc >> otherCentres;
700 //- calculate skewness at processor faces
701 const label start = procBoundaries[patchI].patchStart();
703 # pragma omp parallel
707 # pragma omp for schedule(dynamic, 100)
709 forAll(otherCentres, fI)
711 const label faceI = start + fI;
713 if( changedFacePtr && !(*changedFacePtr)[faceI] )
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);
728 bool checkFaceDotProduct
730 const polyMeshGen& mesh,
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;
757 # pragma omp parallel if( nInternalFaces > 1000 ) \
758 reduction(+ : severeNonOrth, errorNonOrth, sumDDotS)
761 scalar localMinDDotS(VGREAT);
763 # pragma omp for schedule(guided)
765 for(label faceI=0;faceI<nInternalFaces;++faceI)
767 if( changedFacePtr && !(*changedFacePtr)[faceI] )
770 const scalar dDotS = faceDotProduct[faceI];
772 if( dDotS < severeNonorthogonalityThreshold )
778 // Severe non-orthogonality but mesh still OK
780 # pragma omp critical
782 Pout<< "Severe non-orthogonality for face " << faceI
783 << " between cells " << own[faceI]
784 << " and " << nei[faceI]
786 << ::acos(dDotS)/M_PI*180.0
793 # pragma omp critical
795 setPtr->insert(faceI);
807 # pragma omp critical
809 setPtr->insert(faceI);
814 localMinDDotS = Foam::min(dDotS, localMinDDotS);
819 # pragma omp critical
821 minDDotS = Foam::min(minDDotS, localMinDDotS);
824 counter += nInternalFaces;
826 if( Pstream::parRun() )
828 const PtrList<processorBoundaryPatch>& procBoundaries =
829 mesh.procBoundaries();
831 const label start = procBoundaries[0].patchStart();
834 # pragma omp parallel \
835 reduction(+ : severeNonOrth, errorNonOrth, sumDDotS, counter)
838 scalar localMinDDotS(VGREAT);
841 # pragma omp for schedule(dynamic, 100)
843 for(label faceI=start;faceI<own.size();++faceI)
845 if( changedFacePtr && !(*changedFacePtr)[start+faceI] )
848 const scalar dDotS = faceDotProduct[faceI];
850 if( dDotS < severeNonorthogonalityThreshold )
856 // Severe non-orthogonality but mesh still OK
858 # pragma omp critical
866 Pout<< "Severe non-orthogonality for face "
877 # pragma omp critical
879 setPtr->insert(start+faceI);
891 # pragma omp critical
893 setPtr->insert(start+faceI);
898 localMinDDotS = Foam::min(dDotS, localMinDDotS);
899 sumDDotS += 0.5 * dDotS;
905 # pragma omp critical
907 minDDotS = Foam::min(minDDotS, localMinDDotS);
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
921 if( minDDotS < severeNonorthogonalityThreshold )
923 Info<< "Number of non-orthogonality errors: " << errorNonOrth
924 << ". Number of severely non-orthogonal faces: "
925 << severeNonOrth << "." << endl;
933 Info<< "Mesh non-orthogonality Max: "
934 << ::acos(minDDotS)/M_PI*180.0
936 ::acos(sumDDotS/counter)/M_PI*180.0
941 if( errorNonOrth > 0 )
945 "checkFaceDotProduct("
946 "const polyMeshGen&, const bool, const scalar,"
947 " labelHashSet*, const boolList*)"
948 ) << "Error in non-orthogonality detected" << endl;
955 Info<< "Non-orthogonality check OK.\n" << endl;
961 bool checkFacePyramids
963 const polyMeshGen& mesh,
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;
982 # pragma omp parallel for schedule(guided) reduction(+ : nErrorPyrs)
986 if( changedFacePtr && !(*changedFacePtr)[faceI] )
989 // Create the owner pyramid - it will have negative volume
990 const scalar pyrVol = pyramidPointFaceRef
998 if( pyrVol > -minPyrVol )
1003 # pragma omp critical
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)
1020 if( neighbour[faceI] != -1 )
1022 // Create the neighbour pyramid - it will have positive volume
1023 const scalar pyrVol =
1027 ctrs[neighbour[faceI]]
1030 if( pyrVol < minPyrVol )
1035 # pragma omp critical
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)
1058 # pragma omp critical
1060 setPtr->insert(faceI);
1067 reduce(nErrorPyrs, sumOp<label>());
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)
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)
1084 if( setPtr->found(start+faceI) )
1085 markedFaces.append(faceI);
1088 OPstream toOtherProc
1091 procBoundaries[patchI].neiProcNo(),
1092 markedFaces.byteSize()
1095 toOtherProc << markedFaces;
1098 forAll(procBoundaries, patchI)
1100 labelList receivedData;
1101 IPstream fromOtheProc
1104 procBoundaries[patchI].neiProcNo()
1106 fromOtheProc >> receivedData;
1108 const label start = procBoundaries[patchI].patchStart();
1109 forAll(receivedData, i)
1110 setPtr->insert(start+receivedData[i]);
1114 if( nErrorPyrs > 0 )
1116 if( Pstream::master() )
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!"
1131 Info<< "Face pyramids OK.\n" << endl;
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());
1156 //- check internal faces
1158 # pragma omp parallel for schedule(dynamic, 100)
1160 for(label faceI=0;faceI<nInternalFaces;++faceI)
1162 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
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);
1177 if( Pstream::parRun() )
1179 //- check parallel boundaries
1180 const PtrList<processorBoundaryPatch>& procBoundaries =
1181 mesh.procBoundaries();
1183 forAll(procBoundaries, patchI)
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
1194 procBoundaries[patchI].neiProcNo(),
1198 toOtherProc << cCentres;
1201 forAll(procBoundaries, patchI)
1203 const label start = procBoundaries[patchI].patchStart();
1205 vectorField otherCentres;
1206 IPstream fromOtheProc
1209 procBoundaries[patchI].neiProcNo()
1212 fromOtheProc >> otherCentres;
1214 //- calculate skewness at processor faces
1216 # pragma omp parallel
1220 # pragma omp for schedule(dynamic, 100)
1222 forAll(otherCentres, fI)
1224 const label faceI = start + fI;
1228 !changedFacePtr->operator[](faceI)
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);
1250 const faceListPMG& faces = mesh.faces();
1251 const pointFieldPMG& points = mesh.points();
1252 const PtrList<boundaryPatch>& boundaries = mesh.boundaries();
1253 forAll(boundaries, patchI)
1255 label faceI = boundaries[patchI].patchStart();
1256 const label end = faceI + boundaries[patchI].patchSize();
1258 for(;faceI<end;++faceI)
1260 const vector d = fCentres[faceI] - centres[own[faceI]];
1262 vector n = faces[faceI].normal(points);
1263 const scalar magn = mag(n);
1273 const vector dn = (n & d) * n;
1275 faceSkewness[faceI] = mag(d - dn) / (mag(d) + VSMALL);
1280 bool checkFaceSkewness
1282 const polyMeshGen& mesh,
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;
1301 # pragma omp parallel \
1302 reduction(+ : sumSkew, nWarnSkew)
1305 scalar localMaxSkew(0.0);
1308 # pragma omp for schedule(dynamic, 100)
1310 forAll(faceSkewness, faceI)
1312 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1315 const scalar skewness = faceSkewness[faceI];
1317 // Check if the skewness vector is greater than the PN vector.
1318 if( skewness > warnSkew )
1323 # pragma omp critical
1325 Pout<< " Severe skewness for face " << faceI
1326 << " skewness = " << skewness << endl;
1332 # pragma omp critical
1334 setPtr->insert(faceI);
1340 localMaxSkew = Foam::max(localMaxSkew, skewness);
1341 sumSkew += skewness;
1345 # pragma omp critical
1347 maxSkew = Foam::max(maxSkew, localMaxSkew);
1350 reduce(maxSkew, maxOp<scalar>());
1351 reduce(sumSkew, sumOp<scalar>());
1352 reduce(nWarnSkew, sumOp<label>());
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."
1372 Info<< "Max skewness = " << maxSkew
1373 << " Average skewness = " << sumSkew/faceSkewness.size()
1374 << ". Face skewness OK.\n" << endl;
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;
1399 # pragma omp parallel for schedule(dynamic, 100)
1401 for(label faceI=0;faceI<nInternalFaces;++faceI)
1403 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1408 Foam::mag(centres[owner[faceI]] - fCentres[faceI])
1412 Foam::mag(centres[neighbour[faceI]] - fCentres[faceI])
1415 faceUniformity[faceI] = Foam::min(dOwn, dNei) / (dOwn + dNei);
1418 if( Pstream::parRun() )
1420 const PtrList<processorBoundaryPatch>& procBoundaries =
1421 mesh.procBoundaries();
1423 forAll(procBoundaries, patchI)
1425 scalarField dst(procBoundaries[patchI].patchSize());
1426 const label start = procBoundaries[patchI].patchStart();
1430 const label fI = start + faceI;
1431 dst[faceI] = Foam::mag(centres[owner[fI]] - fCentres[fI]);
1434 OPstream toOtherProc
1437 procBoundaries[patchI].neiProcNo(),
1444 forAll(procBoundaries, patchI)
1446 const label start = procBoundaries[patchI].patchStart();
1448 scalarField otherDst;
1449 IPstream fromOtheProc
1452 procBoundaries[patchI].neiProcNo()
1455 fromOtheProc >> otherDst;
1457 forAll(otherDst, faceI)
1459 const label fI = start + faceI;
1461 Foam::mag(centres[owner[fI]] - fCentres[fI]);
1462 const scalar dNei = otherDst[faceI];
1463 faceUniformity[fI] = Foam::min(dOwn, dNei) / (dOwn + dNei);
1469 bool checkFaceUniformity
1471 const polyMeshGen& mesh,
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;
1492 # pragma omp parallel \
1493 reduction(+ : sumUniformity, severeNonUniform)
1496 scalar localMinUniformity(VGREAT);
1497 scalar localMaxUniformity(0.0);
1500 # pragma omp for schedule(guided)
1502 for(label faceI=0;faceI<nInternalFaces;++faceI)
1504 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
1507 const scalar uniformity = faceUniformity[faceI];
1509 if( uniformity < warnUniform )
1514 # pragma omp critical
1516 setPtr->insert(faceI);
1522 localMaxUniformity = Foam::max(localMaxUniformity, uniformity);
1523 localMinUniformity = Foam::min(localMinUniformity, uniformity);
1524 sumUniformity += uniformity;
1528 # pragma omp critical
1531 maxUniformity = Foam::max(maxUniformity, localMaxUniformity);
1532 minUniformity = Foam::min(minUniformity, localMinUniformity);
1536 label counter = nInternalFaces;
1538 if( Pstream::parRun() )
1540 const PtrList<processorBoundaryPatch>& procBoundaries =
1541 mesh.procBoundaries();
1543 forAll(procBoundaries, patchI)
1545 const label start = procBoundaries[patchI].patchStart();
1546 const label size = procBoundaries[patchI].patchSize();
1548 for(label fI=0;fI<size;++fI)
1550 const label faceI = start + fI;
1552 const scalar uniformity = faceUniformity[faceI];
1554 if( uniformity < warnUniform )
1557 setPtr->insert(faceI);
1562 maxUniformity = Foam::max(maxUniformity, uniformity);
1563 minUniformity = Foam::min(minUniformity, uniformity);
1564 sumUniformity += 0.5 * uniformity;
1567 if( procBoundaries[patchI].owner() )
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
1581 if( minUniformity < warnUniform )
1582 Info<< "Number of severely non-uniform faces: "
1583 << severeNonUniform << "." << endl;
1589 Info<< "Mesh non-uniformity Max: "
1591 << " Min: " << minUniformity
1592 << " average: " << sumUniformity/counter
1599 void checkVolumeUniformity
1601 const polyMeshGen& mesh,
1602 scalarField& faceUniformity,
1603 const boolList* changedFacePtr
1606 bool checkVolumeUniformity
1610 const scalar warnUniform,
1611 labelHashSet* setPtr,
1612 const boolList* changedFacePtr
1619 bool checkFaceAngles
1621 const polyMeshGen& mesh,
1623 const scalar maxDeg,
1624 labelHashSet* setPtr,
1625 const boolList* changedFacePtr
1628 if( maxDeg < -SMALL || maxDeg > 180+SMALL )
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);
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;
1651 # pragma omp parallel reduction(+ : nConcave)
1654 scalar localMaxEdgeSin(0.0);
1655 label errorFaceI(-1);
1658 # pragma omp for schedule(guided)
1660 forAll(faces, faceI)
1662 if( changedFacePtr && !changedFacePtr->operator[](faceI) )
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;
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 )
1684 vector edgeNormal = ePrev ^ e10;
1685 scalar magEdgeNormal = mag(edgeNormal);
1687 if( magEdgeNormal < maxSin )
1689 // Edges (almost) aligned -> face is ok.
1694 edgeNormal /= magEdgeNormal;
1696 if( (edgeNormal & faceNormals[faceI]) < SMALL )
1699 # pragma omp critical
1702 if( faceI != errorFaceI )
1704 // Count only one error per face.
1710 setPtr->insert(faceI);
1713 Foam::max(localMaxEdgeSin, magEdgeNormal);
1725 # pragma omp critical
1727 maxEdgeSin = Foam::max(maxEdgeSin, localMaxEdgeSin);
1730 reduce(nConcave, sumOp<label>());
1731 reduce(maxEdgeSin, maxOp<scalar>());
1735 if( maxEdgeSin > SMALL )
1737 scalar maxConcaveDegr =
1738 Foam::asin(Foam::min(1.0, maxEdgeSin))
1741 Warning<< "There are " << nConcave
1742 << " faces with concave angles between consecutive"
1743 << " edges. Max concave angle = "
1745 << " degrees.\n" << endl;
1749 Info<< "All angles in faces are convex or less than " << maxDeg
1750 << " degrees concave.\n" << endl;
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"
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,
1780 const scalar warnFlatness,
1781 labelHashSet* setPtr,
1782 const boolList* changedFacePtr
1785 if( warnFlatness < 0 || warnFlatness > 1 )
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);
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()));
1806 scalar minFlatness = GREAT;
1807 scalar sumFlatness = 0;
1811 # pragma omp parallel if( faces.size() > 1000 ) \
1812 reduction(+ : nSummed, nWarped) reduction(+ : sumFlatness)
1815 scalar minFlatnessProc = VGREAT;
1818 # pragma omp for schedule(guided)
1820 forAll(faces, faceI)
1822 if( changedFacePtr && !(*changedFacePtr)[faceI] )
1825 const face& f = faces[faceI];
1827 if( f.size() > 3 && magAreas[faceI] > VSMALL )
1829 const point& fc = fctrs[faceI];
1831 //- Calculate the sum of magnitude of areas and compare
1832 //- the magnitude of sum of areas.
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));
1846 scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1848 sumFlatness += flatness;
1851 minFlatnessProc = Foam::min(minFlatnessProc, flatness);
1853 if( flatness < warnFlatness )
1860 # pragma omp critical
1862 setPtr->insert(faceI);
1869 # pragma omp critical
1872 minFlatness = Foam::min(minFlatness, minFlatnessProc);
1876 if( Pstream::parRun() && setPtr )
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)
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);
1893 //- exchange list sizes
1894 forAll(procBoundaries, patchI)
1896 OPstream toOtherProc
1899 procBoundaries[patchI].neiProcNo(),
1903 toOtherProc << markedFaces[patchI].size();
1906 labelList nMarkedOnOtherProcs(procBoundaries.size());
1907 forAll(procBoundaries, patchI)
1909 IPstream fromOtheProc
1912 procBoundaries[patchI].neiProcNo(),
1916 fromOtheProc >> nMarkedOnOtherProcs[patchI];
1920 forAll(procBoundaries, patchI)
1922 if( markedFaces[patchI].size() == 0 )
1925 OPstream toOtherProc
1928 procBoundaries[patchI].neiProcNo(),
1929 markedFaces[patchI].byteSize()
1932 toOtherProc << markedFaces[patchI];
1935 forAll(procBoundaries, patchI)
1937 if( nMarkedOnOtherProcs[patchI] == 0 )
1940 labelList receivedData;
1941 IPstream fromOtheProc
1944 procBoundaries[patchI].neiProcNo(),
1945 nMarkedOnOtherProcs[patchI]*sizeof(label)
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]);
1956 reduce(nWarped, sumOp<label>());
1957 reduce(minFlatness, minOp<scalar>());
1959 reduce(nSummed, sumOp<label>());
1960 reduce(sumFlatness, sumOp<scalar>());
1966 Info<< "Face flatness (1 = flat, 0 = butterfly) : average = "
1967 << sumFlatness / nSummed << " min = " << minFlatness << endl;
1972 Info<< "There are " << nWarped
1973 << " faces with ratio between projected and actual area < "
1975 << ".\nMinimum ratio (minimum flatness, maximum warpage) = "
1976 << minFlatness << nl << endl;
1980 Info<< "All faces are flat in that the ratio between projected"
1981 << " and actual area is > " << warnFlatness << nl << endl;
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"
2004 label findBadFacesRelaxed
2006 const polyMeshGen& mesh,
2007 labelHashSet& badFaces,
2009 const boolList* activeFacePtr
2014 polyMeshGenChecks::checkFacePyramids
2023 polyMeshGenChecks::checkFaceAreas
2032 const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2039 const polyMeshGen& mesh,
2040 labelHashSet& badFaces,
2042 const boolList* activeFacePtr
2047 polyMeshGenChecks::checkFacePyramids
2056 polyMeshGenChecks::checkFaceFlatness
2065 polyMeshGenChecks::checkCellPartTetrahedra
2074 polyMeshGenChecks::checkFaceAreas
2083 const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2088 label findLowQualityFaces
2090 const polyMeshGen& mesh,
2091 labelHashSet& badFaces,
2093 const boolList* activeFacePtr
2098 polyMeshGenChecks::checkFaceDotProduct
2107 polyMeshGenChecks::checkFaceSkewness
2116 const label nBadFaces = returnReduce(badFaces.size(), sumOp<label>());
2121 label findWorstQualityFaces
2123 const polyMeshGen& mesh,
2124 labelHashSet& badFaces,
2126 const boolList* activeFacePtr,
2127 const scalar relativeThreshold
2132 scalarField checkValues;
2133 polyMeshGenChecks::checkFaceDotProduct
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)
2152 activeFacePtr && activeFacePtr->operator[](faceI) &&
2153 checkValues[faceI] < warnNonOrtho
2155 badFaces.insert(faceI);
2158 polyMeshGenChecks::checkFaceSkewness
2165 const scalar maxSkew = returnReduce(max(checkValues), maxOp<scalar>());
2166 const scalar warnSkew = (1.0 - relativeThreshold) * maxSkew;
2167 forAll(checkValues, faceI)
2170 activeFacePtr && activeFacePtr->operator[](faceI) &&
2171 checkValues[faceI] > warnSkew
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;
2187 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2189 } // End namespace polyMeshGenChecks
2191 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2193 } // End namespace Foam
2195 // ************************************************************************* //