primitiveMeshCheck.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | foam-extend: Open Source CFD
4  \\ / O peration | Version: 3.2
5  \\ / A nd | Web: http://www.foam-extend.org
6  \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
8 License
9  This file is part of foam-extend.
10 
11  foam-extend is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by the
13  Free Software Foundation, either version 3 of the License, or (at your
14  option) any later version.
15 
16  foam-extend is distributed in the hope that it will be useful, but
17  WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "primitiveMesh.H"
27 #include "pyramidPointFaceRef.H"
28 #include "ListOps.H"
29 #include "mathematicalConstants.H"
30 #include "SortableList.H"
31 
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 const Foam::debug::tolerancesSwitch
36 (
37  "primitiveMeshClosedThreshold",
38  1e-6
39 );
40 
41 const Foam::debug::tolerancesSwitch
43 (
44  "primitiveMeshAspectThreshold",
45  1000
46 );
47 
48 Foam::debug::tolerancesSwitch
50 (
51  "primitiveMeshNonOrthThreshold",
52  70
53 );
54 
55 const Foam::debug::tolerancesSwitch
57 (
58  "primitiveMeshSkewThreshold",
59  4
60 );
61 
62 Foam::debug::tolerancesSwitch
64 (
65  "primitiveMeshFaceAngleThreshold",
66  10
67 );
68 
69 const Foam::debug::tolerancesSwitch
71 (
72  "primitiveMeshFaceFlatnessThreshold",
73  0.8
74 );
75 
76 
77 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
78 
79 bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const
80 {
81  if (debug)
82  {
83  Info<< "bool primitiveMesh::checkClosedBoundary("
84  << "const bool) const: "
85  << "checking whether the boundary is closed" << endl;
86  }
87 
88  // Loop through all boundary faces and sum up the face area vectors.
89  // For a closed boundary, this should be zero in all vector components
90 
91  vector sumClosed(vector::zero);
92  scalar sumMagClosedBoundary = 0;
93 
94  const vectorField& areas = faceAreas();
95 
96  for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++)
97  {
98  sumClosed += areas[faceI];
99  sumMagClosedBoundary += mag(areas[faceI]);
100  }
101 
102  reduce(sumClosed, sumOp<vector>());
103  reduce(sumMagClosedBoundary, sumOp<scalar>());
104 
105  vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
106 
107  if (cmptMax(cmptMag(openness)) > closedThreshold_())
108  {
109  if (debug || report)
110  {
111  Info<< " ***Boundary openness " << openness
112  << " Threshold = " << closedThreshold_()
113  << " possible hole in boundary description."
114  << endl;
115  }
116 
117  return true;
118  }
119  else
120  {
121  if (debug || report)
122  {
123  Info<< " Boundary openness " << openness
124  << " Threshold = " << closedThreshold_()
125  << " OK."
126  << endl;
127  }
128 
129  return false;
130  }
131 }
132 
133 
135 (
136  const bool report,
137  labelHashSet* setPtr,
138  labelHashSet* aspectSetPtr
139 ) const
140 {
141  if (debug)
142  {
143  Info<< "bool primitiveMesh::checkClosedCells("
144  << "const bool, labelHashSet*, labelHashSet*) const: "
145  << "checking whether cells are closed" << endl;
146  }
147 
148  // Check that all cells labels are valid
149  const cellList& c = cells();
150 
151  label nErrorClosed = 0;
152 
153  forAll (c, cI)
154  {
155  const cell& curCell = c[cI];
156 
157  if (min(curCell) < 0 || max(curCell) > nFaces())
158  {
159  if (setPtr)
160  {
161  setPtr->insert(cI);
162  }
163 
164  nErrorClosed++;
165  }
166  }
167 
168  if (nErrorClosed > 0)
169  {
170  if (debug || report)
171  {
172  Info<< " ***Cells with invalid face labels found, number of cells "
173  << nErrorClosed << endl;
174  }
175 
176  return true;
177  }
178 
179  // Check if a face has got the same cell as owner and neighbour
180  // HJ, 25/May/2011
181  const labelList& own = faceOwner();
182  const labelList& nei = faceNeighbour();
183 
184  label nErrorOwnNei = 0;
185 
186  // Check internal faces only
187  forAll (nei, faceI)
188  {
189  if (own[faceI] == nei[faceI])
190  {
191  if (setPtr)
192  {
193  setPtr->insert(own[faceI]);
194  }
195 
196  nErrorOwnNei++;
197  }
198  }
199 
200  if (nErrorOwnNei > 0)
201  {
202  if (debug || report)
203  {
204  Info<< " ***Faces declaring same cell as owner and neighbour "
205  << "found, number of faces "
206  << nErrorOwnNei << endl;
207  }
208 
209  return true;
210  }
211 
212  // Loop through cell faces and sum up the face area vectors for each cell.
213  // This should be zero in all vector components
214 
215  vectorField sumClosed(nCells(), vector::zero);
216  vectorField sumMagClosed(nCells(), vector::zero);
217 
218  const vectorField& areas = faceAreas();
219 
220  forAll (own, faceI)
221  {
222  // Add to owner
223  sumClosed[own[faceI]] += areas[faceI];
224  sumMagClosed[own[faceI]] += cmptMag(areas[faceI]);
225  }
226 
227  forAll (nei, faceI)
228  {
229  // Subtract from neighbour
230  sumClosed[nei[faceI]] -= areas[faceI];
231  sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]);
232  }
233 
234  label nOpen = 0;
235  scalar maxOpennessCell = 0;
236 
237  label nAspect = 0;
238  scalar maxAspectRatio = 0;
239 
240  const scalarField& vols = cellVolumes();
241 
242  // Check the sums
243  forAll (sumClosed, cellI)
244  {
245  scalar maxOpenness = 0;
246 
247  for(direction cmpt=0; cmpt<vector::nComponents; cmpt++)
248  {
249  maxOpenness = max
250  (
251  maxOpenness,
252  mag(sumClosed[cellI][cmpt])
253  /(sumMagClosed[cellI][cmpt] + VSMALL)
254  );
255  }
256 
257  maxOpennessCell = max(maxOpennessCell, maxOpenness);
258 
259  if (maxOpenness > closedThreshold_())
260  {
261  if (setPtr)
262  {
263  setPtr->insert(cellI);
264  }
265 
266  nOpen++;
267  }
268 
269  // Calculate the aspect ration as the maximum of Cartesian component
270  // aspect ratio to the total area hydraulic area aspect ratio
271  scalar aspectRatio = max
272  (
273  cmptMax(sumMagClosed[cellI])
274  /(cmptMin(sumMagClosed[cellI]) + VSMALL),
275  1.0/6.0*cmptSum(sumMagClosed[cellI])/
276  Foam::pow(Foam::max(vols[cellI], SMALL), 2.0/3.0)
277  );
278 
279  maxAspectRatio = max(maxAspectRatio, aspectRatio);
280 
281  if (aspectRatio > aspectThreshold_())
282  {
283  if (aspectSetPtr)
284  {
285  aspectSetPtr->insert(cellI);
286  }
287 
288  nAspect++;
289  }
290  }
291 
292  reduce(nOpen, sumOp<label>());
293  reduce(maxOpennessCell, maxOp<scalar>());
294 
295  reduce(nAspect, sumOp<label>());
296  reduce(maxAspectRatio, maxOp<scalar>());
297 
298 
299  if (nOpen > 0)
300  {
301  if (debug || report)
302  {
303  Info<< " ***Open cells found, max cell openness: "
304  << maxOpennessCell << ", number of open cells " << nOpen
305  << " Threshold = " << closedThreshold_()
306  << endl;
307  }
308 
309  return true;
310  }
311 
312  if (nAspect > 0)
313  {
314  if (debug || report)
315  {
316  Info<< " ***High aspect ratio cells found, Max aspect ratio: "
317  << maxAspectRatio
318  << ", number of cells " << nAspect
319  << " Threshold = " << aspectThreshold_()
320  << endl;
321  }
322 
323  return true;
324  }
325 
326  if (debug || report)
327  {
328  Info<< " Max cell openness = " << maxOpennessCell << " OK." << nl
329  << " Max aspect ratio = " << maxAspectRatio << " OK."
330  << endl;
331  }
332 
333  return false;
334 }
335 
336 
338 (
339  const bool report,
340  labelHashSet* setPtr
341 ) const
342 {
343  if (debug)
344  {
345  Info<< "bool primitiveMesh::checkFaceAreas("
346  << "const bool, labelHashSet*) const: "
347  << "checking face area magnitudes" << endl;
348  }
349 
350  const scalarField magFaceAreas = mag(faceAreas());
351 
352  scalar minArea = GREAT;
353  scalar maxArea = -GREAT;
354 
355  forAll (magFaceAreas, faceI)
356  {
357  if (magFaceAreas[faceI] < VSMALL)
358  {
359  if (setPtr)
360  {
361  setPtr->insert(faceI);
362  }
363  }
364 
365  minArea = min(minArea, magFaceAreas[faceI]);
366  maxArea = max(maxArea, magFaceAreas[faceI]);
367  }
368 
369  reduce(minArea, minOp<scalar>());
370  reduce(maxArea, maxOp<scalar>());
371 
372  if (minArea < VSMALL)
373  {
374  if (debug || report)
375  {
376  Info<< " ***Zero or negative face area detected. "
377  "Minimum area: " << minArea << endl;
378  }
379 
380  return true;
381  }
382  else
383  {
384  if (debug || report)
385  {
386  Info<< " Minumum face area = " << minArea
387  << ". Maximum face area = " << maxArea
388  << ". Face area magnitudes OK." << endl;
389  }
390 
391  return false;
392  }
393 }
394 
395 
397 (
398  const bool report,
399  labelHashSet* setPtr
400 ) const
401 {
402  if (debug)
403  {
404  Info<< "bool primitiveMesh::checkCellVolumes("
405  << "const bool, labelHashSet*) const: "
406  << "checking cell volumes" << endl;
407  }
408 
409  const scalarField& vols = cellVolumes();
410 
411  scalar minVolume = GREAT;
412  scalar maxVolume = -GREAT;
413 
414  label nNegVolCells = 0;
415 
416  forAll (vols, cellI)
417  {
418  if (vols[cellI] < VSMALL)
419  {
420  if (setPtr)
421  {
422  setPtr->insert(cellI);
423  }
424 
425  nNegVolCells++;
426  }
427 
428  minVolume = min(minVolume, vols[cellI]);
429  maxVolume = max(maxVolume, vols[cellI]);
430  }
431 
432  reduce(minVolume, minOp<scalar>());
433  reduce(maxVolume, maxOp<scalar>());
434  reduce(nNegVolCells, sumOp<label>());
435 
436  if (minVolume < VSMALL)
437  {
438  if (debug || report)
439  {
440  Info<< " ***Zero or negative cell volume detected. "
441  << "Minimum negative volume: " << minVolume
442  << ", Number of negative volume cells: " << nNegVolCells
443  << endl;
444  }
445 
446  return true;
447  }
448  else
449  {
450  if (debug || report)
451  {
452  Info<< " Min volume = " << minVolume
453  << ". Max volume = " << maxVolume
454  << ". Total volume = " << gSum(vols)
455  << ". Cell volumes OK." << endl;
456  }
457 
458  return false;
459  }
460 }
461 
462 
464 (
465  const bool report,
466  labelHashSet* setPtr
467 ) const
468 {
469  if (debug)
470  {
471  Info<< "bool primitiveMesh::checkFaceOrthogonality("
472  << "const bool, labelHashSet*) const: "
473  << "checking mesh non-orthogonality" << endl;
474  }
475 
476  // for all internal faces check theat the d dot S product is positive
477  const vectorField& centres = cellCentres();
478  const vectorField& areas = faceAreas();
479 
480  const labelList& own = faceOwner();
481  const labelList& nei = faceNeighbour();
482 
483  // Severe nonorthogonality threshold
484  const scalar severeNonorthogonalityThreshold =
485  ::cos(nonOrthThreshold_()/180.0*mathematicalConstant::pi);
486 
487  scalar minDDotS = GREAT;
488 
489  scalar sumDDotS = 0;
490 
491  label severeNonOrth = 0;
492 
493  label errorNonOrth = 0;
494 
495  forAll (nei, faceI)
496  {
497  vector d = centres[nei[faceI]] - centres[own[faceI]];
498  const vector& s = areas[faceI];
499 
500  scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL);
501 
502  if (dDotS < severeNonorthogonalityThreshold)
503  {
504  if (dDotS > SMALL)
505  {
506  if (setPtr)
507  {
508  setPtr->insert(faceI);
509  }
510 
511  severeNonOrth++;
512  }
513  else
514  {
515  if (setPtr)
516  {
517  setPtr->insert(faceI);
518  }
519 
520  errorNonOrth++;
521  }
522  }
523 
524  if (dDotS < minDDotS)
525  {
526  minDDotS = dDotS;
527  }
528 
529  sumDDotS += dDotS;
530  }
531 
532  reduce(minDDotS, minOp<scalar>());
533  reduce(sumDDotS, sumOp<scalar>());
534  reduce(severeNonOrth, sumOp<label>());
535  reduce(errorNonOrth, sumOp<label>());
536 
537  if (debug || report)
538  {
539  label neiSize = nei.size();
540  reduce(neiSize, sumOp<label>());
541 
542  if (neiSize > 0)
543  {
544  if (debug || report)
545  {
546  Info<< " Mesh non-orthogonality Max: "
547  << ::acos(minDDotS)/mathematicalConstant::pi*180.0
548  << " average: " <<
549  ::acos(sumDDotS/neiSize)/mathematicalConstant::pi*180.0
550  << " Threshold = " << nonOrthThreshold_()
551  << endl;
552  }
553  }
554 
555  if (severeNonOrth > 0)
556  {
557  Info<< " *Number of severely non-orthogonal faces: "
558  << severeNonOrth << "." << endl;
559  }
560  }
561 
562  if (errorNonOrth > 0)
563  {
564  if (debug || report)
565  {
566  Info<< " ***Number of non-orthogonality errors: "
567  << errorNonOrth << "." << endl;
568  }
569 
570  return true;
571  }
572  else
573  {
574  if (debug || report)
575  {
576  Info<< " Non-orthogonality check OK." << endl;
577  }
578 
579  return false;
580  }
581 }
582 
583 
585 (
586  const bool report,
587  const scalar minPyrVol,
588  labelHashSet* setPtr
589 ) const
590 {
591  if (debug)
592  {
593  Info<< "bool primitiveMesh::checkFacePyramids("
594  << "const bool, const scalar, labelHashSet*) const: "
595  << "checking face orientation" << endl;
596  }
597 
598  // check whether face area vector points to the cell with higher label
599  const vectorField& ctrs = cellCentres();
600 
601  const labelList& own = faceOwner();
602  const labelList& nei = faceNeighbour();
603 
604  const faceList& f = faces();
605 
606  const pointField& p = points();
607 
608  label nErrorPyrs = 0;
609 
610  forAll (f, faceI)
611  {
612  // Create the owner pyramid - it will have negative volume
613  scalar pyrVol = pyramidPointFaceRef(f[faceI], ctrs[own[faceI]]).mag(p);
614 
615  if (pyrVol > -minPyrVol)
616  {
617  if (setPtr)
618  {
619  setPtr->insert(faceI);
620  }
621 
622  nErrorPyrs++;
623  }
624 
625  if (isInternalFace(faceI))
626  {
627  // Create the neighbour pyramid - it will have positive volume
628  scalar pyrVol =
629  pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p);
630 
631  if (pyrVol < minPyrVol)
632  {
633  if (setPtr)
634  {
635  setPtr->insert(faceI);
636  }
637 
638  nErrorPyrs++;
639  }
640  }
641  }
642 
643  reduce(nErrorPyrs, sumOp<label>());
644 
645  if (nErrorPyrs > 0)
646  {
647  if (debug || report)
648  {
649  Info<< " ***Error in face pyramids: "
650  << nErrorPyrs << " faces are incorrectly oriented."
651  << endl;
652  }
653 
654  return true;
655  }
656  else
657  {
658  if (debug || report)
659  {
660  Info<< " Face pyramids OK." << endl;
661  }
662 
663  return false;
664  }
665 }
666 
667 
669 (
670  const bool report,
671  labelHashSet* setPtr
672 ) const
673 {
674  if (debug)
675  {
676  Info<< "bool primitiveMesh::checkFaceSkewnesss("
677  << "const bool, labelHashSet*) const: "
678  << "checking face skewness" << endl;
679  }
680 
681  // Warn if the skew correction vector is more than skewWarning times
682  // larger than the face area vector
683 
684  const pointField& p = points();
685  const faceList& fcs = faces();
686 
687  const labelList& own = faceOwner();
688  const labelList& nei = faceNeighbour();
689  const vectorField& cellCtrs = cellCentres();
690  const vectorField& faceCtrs = faceCentres();
691  const vectorField& fAreas = faceAreas();
692 
693  scalar maxSkew = 0;
694  label nWarnSkew = 0;
695 
696  forAll(nei, faceI)
697  {
698  vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
699  vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]];
700 
701  // Skewness vector
702  vector sv =
703  Cpf - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + SMALL))*d;
704  vector svHat = sv/(mag(sv) + VSMALL);
705 
706  // Normalisation distance calculated as the approximate distance
707  // from the face centre to the edge of the face in the direction of
708  // the skewness
709  scalar fd = 0.2*mag(d) + VSMALL;
710  const face& f = fcs[faceI];
711  forAll(f, pi)
712  {
713  fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
714  }
715 
716  // Normalised skewness
717  scalar skewness = mag(sv)/fd;
718 
719  // Check if the skewness vector is greater than the PN vector.
720  // This does not cause trouble but is a good indication of a poor mesh.
721  if (skewness > skewThreshold_())
722  {
723  if (setPtr)
724  {
725  setPtr->insert(faceI);
726  }
727 
728  nWarnSkew++;
729  }
730 
731  if(skewness > maxSkew)
732  {
733  maxSkew = skewness;
734  }
735  }
736 
737 
738  // Boundary faces: consider them to have only skewness error.
739  // (i.e. treat as if mirror cell on other side)
740 
741  for (label faceI = nInternalFaces(); faceI < nFaces(); faceI++)
742  {
743  vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]];
744 
745  vector normal = fAreas[faceI];
746  normal /= mag(normal) + VSMALL;
747  vector d = normal*(normal & Cpf);
748 
749 
750  // Skewness vector
751  vector sv = Cpf - ((fAreas[faceI]&Cpf)/((fAreas[faceI]&d)+VSMALL))*d;
752  vector svHat = sv/(mag(sv) + VSMALL);
753 
754  // Normalisation distance calculated as the approximate distance
755  // from the face centre to the edge of the face in the direction of
756  // the skewness
757  scalar fd = 0.4*mag(d) + VSMALL;
758  const face& f = fcs[faceI];
759  forAll(f, pi)
760  {
761  fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI])));
762  }
763 
764  // Normalised skewness
765  scalar skewness = mag(sv)/fd;
766 
767  // Check if the skewness vector is greater than the PN vector.
768  // This does not cause trouble but is a good indication of a poor mesh.
769  if (skewness > skewThreshold_())
770  {
771  if (setPtr)
772  {
773  setPtr->insert(faceI);
774  }
775 
776  nWarnSkew++;
777  }
778 
779  if(skewness > maxSkew)
780  {
781  maxSkew = skewness;
782  }
783  }
784 
785 
786  reduce(maxSkew, maxOp<scalar>());
787  reduce(nWarnSkew, sumOp<label>());
788 
789  if (nWarnSkew > 0)
790  {
791  if (debug || report)
792  {
793  Info<< " ***Max skewness = " << maxSkew
794  << ", " << nWarnSkew << " highly skew faces detected"
795  << " Threshold = " << skewThreshold_()
796  << endl;
797  }
798 
799  return true;
800  }
801  else
802  {
803  if (debug || report)
804  {
805  Info<< " Max skewness = " << maxSkew << " OK." << endl;
806  }
807 
808  return false;
809  }
810 }
811 
812 
814 (
815  const bool report,
816  labelHashSet* setPtr
817 ) const
818 {
819  if (debug)
820  {
821  Info<< "bool primitiveMesh::checkPoints"
822  << "(const bool, labelHashSet*) const: "
823  << "checking points" << endl;
824  }
825 
826  label nFaceErrors = 0;
827  label nCellErrors = 0;
828 
829  const labelListList& pf = pointFaces();
830 
831  forAll (pf, pointI)
832  {
833  if (pf[pointI].empty())
834  {
835  if (setPtr)
836  {
837  setPtr->insert(pointI);
838  }
839 
840  nFaceErrors++;
841  }
842  }
843 
844 
845  forAll (pf, pointI)
846  {
847  const labelList& pc = pointCells(pointI);
848 
849  if (pc.empty())
850  {
851  if (setPtr)
852  {
853  setPtr->insert(pointI);
854  }
855 
856  nCellErrors++;
857  }
858  }
859 
860  reduce(nFaceErrors, sumOp<label>());
861  reduce(nCellErrors, sumOp<label>());
862 
863  if (nFaceErrors > 0 || nCellErrors > 0)
864  {
865  if (debug || report)
866  {
867  Info<< " ***Unused points found in the mesh, "
868  "number unused by faces: " << nFaceErrors
869  << " number unused by cells: " << nCellErrors
870  << endl;
871  }
872 
873  return true;
874  }
875  else
876  {
877  if (debug || report)
878  {
879  Info<< " Point usage OK." << endl;
880  }
881 
882  return false;
883  }
884 }
885 
886 
887 // Check convexity of angles in a face. Allow a slight non-convexity.
888 // E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
889 // (if truly concave and points not visible from face centre the face-pyramid
890 // check in checkMesh will fail)
892 (
893  const bool report,
894  labelHashSet* setPtr
895 ) const
896 {
897  if (debug)
898  {
899  Info<< "bool primitiveMesh::checkFaceAngles"
900  << "(const bool, labelHashSet*) const: "
901  << "checking face angles" << endl;
902  }
903 
904  if (faceAngleThreshold_() < -SMALL || faceAngleThreshold_() > 180 + SMALL)
905  {
907  (
908  "primitiveMesh::checkFaceAngles(const bool, labelHashSet*)"
909  ) << "faceAngleThreshold_ should be [0..180] but is now "
910  << faceAngleThreshold_()
911  << exit(FatalError);
912  }
913 
914  const scalar maxSin =
915  Foam::sin(faceAngleThreshold_()/180.0*mathematicalConstant::pi);
916 
917  const pointField& p = points();
918  const faceList& fcs = faces();
919  vectorField faceNormals(faceAreas());
920  faceNormals /= mag(faceNormals) + VSMALL;
921 
922  scalar maxEdgeSin = 0.0;
923 
924  label nConcave = 0;
925 
926  label errorFaceI = -1;
927 
928  forAll(fcs, faceI)
929  {
930  const face& f = fcs[faceI];
931 
932  // Get edge from f[0] to f[size-1];
933  vector ePrev(p[f[0]] - p[f[f.size()-1]]);
934  scalar magEPrev = mag(ePrev);
935  ePrev /= magEPrev + VSMALL;
936 
937  forAll(f, fp0)
938  {
939  // Get vertex after fp
940  label fp1 = f.fcIndex(fp0);
941 
942  // Normalized vector between two consecutive points
943  vector e10(p[f[fp1]] - p[f[fp0]]);
944  scalar magE10 = mag(e10);
945  e10 /= magE10 + VSMALL;
946 
947  if (magEPrev > SMALL && magE10 > SMALL)
948  {
949  vector edgeNormal = ePrev ^ e10;
950  scalar magEdgeNormal = mag(edgeNormal);
951 
952  if (magEdgeNormal < maxSin)
953  {
954  // Edges (almost) aligned -> face is ok.
955  }
956  else
957  {
958  // Check normal
959  edgeNormal /= magEdgeNormal;
960 
961  if ((edgeNormal & faceNormals[faceI]) < SMALL)
962  {
963  if (faceI != errorFaceI)
964  {
965  // Count only one error per face.
966  errorFaceI = faceI;
967  nConcave++;
968  }
969 
970  if (setPtr)
971  {
972  setPtr->insert(faceI);
973  }
974 
975  maxEdgeSin = max(maxEdgeSin, magEdgeNormal);
976  }
977  }
978  }
979 
980  ePrev = e10;
981  magEPrev = magE10;
982  }
983  }
984 
985  reduce(nConcave, sumOp<label>());
986  reduce(maxEdgeSin, maxOp<scalar>());
987 
988  if (nConcave > 0)
989  {
990  scalar maxConcaveDegr =
991  Foam::asin(Foam::min(1.0, maxEdgeSin))
993 
994  if (debug || report)
995  {
996  Info<< " *There are " << nConcave
997  << " faces with concave angles between consecutive"
998  << " edges. Max concave angle = " << maxConcaveDegr
999  << " degrees." << endl;
1000  }
1001 
1002  return true;
1003  }
1004  else
1005  {
1006  if (debug || report)
1007  {
1008  Info<< " All angles in faces OK." << endl;
1009  }
1010 
1011  return false;
1012  }
1013 }
1014 
1015 
1016 // Check warpage of faces. Is calculated as the difference between areas of
1017 // individual triangles and the overall area of the face (which ifself is
1018 // is the average of the areas of the individual triangles).
1021  const bool report,
1022  labelHashSet* setPtr
1023 ) const
1024 {
1025  if (debug)
1026  {
1027  Info<< "bool primitiveMesh::checkFaceFlatness"
1028  << "(const bool, labelHashSet*) const: "
1029  << "checking face flatness" << endl;
1030  }
1031 
1032  if (faceFlatnessThreshold_() < 0 || faceFlatnessThreshold_() > 1)
1033  {
1034  FatalErrorIn
1035  (
1036  "primitiveMesh::checkFaceFlatness"
1037  "(const bool, labelHashSet*)"
1038  ) << "faceFlatnessThreshold_ should be [0..1] but is now "
1039  << faceFlatnessThreshold_()
1040  << exit(FatalError);
1041  }
1042 
1043 
1044  const pointField& p = points();
1045  const faceList& fcs = faces();
1046  const pointField& fctrs = faceCentres();
1047 
1048  // Areas are calculated as the sum of areas. (see
1049  // primitiveMeshFaceCentresAndAreas.C)
1050  scalarField magAreas(mag(faceAreas()));
1051 
1052  label nWarped = 0;
1053 
1054  scalar minFlatness = GREAT;
1055  scalar sumFlatness = 0;
1056  label nSummed = 0;
1057 
1058  forAll(fcs, faceI)
1059  {
1060  const face& f = fcs[faceI];
1061 
1062  if (f.size() > 3 && magAreas[faceI] > VSMALL)
1063  {
1064  const point& fc = fctrs[faceI];
1065 
1066  // Calculate the sum of magnitude of areas and compare to magnitude
1067  // of sum of areas.
1068 
1069  scalar sumA = 0.0;
1070 
1071  forAll(f, fp)
1072  {
1073  const point& thisPoint = p[f[fp]];
1074  const point& nextPoint = p[f.nextLabel(fp)];
1075 
1076  // Triangle around fc.
1077  vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint));
1078  sumA += mag(n);
1079  }
1080 
1081  scalar flatness = magAreas[faceI] / (sumA+VSMALL);
1082 
1083  sumFlatness += flatness;
1084  nSummed++;
1085 
1086  minFlatness = min(minFlatness, flatness);
1087 
1088  if (flatness < faceFlatnessThreshold_())
1089  {
1090  nWarped++;
1091 
1092  if (setPtr)
1093  {
1094  setPtr->insert(faceI);
1095  }
1096  }
1097  }
1098  }
1099 
1100 
1101  reduce(nWarped, sumOp<label>());
1102  reduce(minFlatness, minOp<scalar>());
1103 
1104  reduce(nSummed, sumOp<label>());
1105  reduce(sumFlatness, sumOp<scalar>());
1106 
1107  if (debug || report)
1108  {
1109  if (nSummed > 0)
1110  {
1111  Info<< " Face flatness (1 = flat, 0 = butterfly) : average = "
1112  << sumFlatness / nSummed << " min = " << minFlatness << endl;
1113  }
1114  }
1115 
1116 
1117  if (nWarped > 0)
1118  {
1119  if (debug || report)
1120  {
1121  Info<< " *There are " << nWarped
1122  << " faces with ratio between projected and actual area < "
1123  << faceFlatnessThreshold_() << endl;
1124 
1125  Info<< " Minimum ratio (minimum flatness, maximum warpage) = "
1126  << minFlatness << endl;
1127  }
1128 
1129  return true;
1130  }
1131  else
1132  {
1133  if (debug || report)
1134  {
1135  Info<< " All face flatness OK." << endl;
1136  }
1137 
1138  return false;
1139  }
1140 }
1141 
1142 
1143 // Check 1D/2Dness of edges. Gets passed the non-empty directions and
1144 // checks all edges in the mesh whether they:
1145 // - have no component in a non-empty direction or
1146 // - are only in a singe non-empty direction.
1147 // Empty direction info is passed in as a vector of labels (synchronised)
1148 // which are 1 if the direction is non-empty, 0 if it is.
1151  const bool report,
1152  const Vector<label>& directions,
1153  labelHashSet* setPtr
1154 ) const
1155 {
1156  if (debug)
1157  {
1158  Info<< "bool primitiveMesh::checkEdgeAlignment("
1159  << "const bool, const Vector<label>&, labelHashSet*) const: "
1160  << "checking edge alignment" << endl;
1161  }
1162 
1163  label nDirs = 0;
1164  for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1165  {
1166  if (directions[cmpt] == 1)
1167  {
1168  nDirs++;
1169  }
1170  else if (directions[cmpt] != 0)
1171  {
1172  FatalErrorIn
1173  (
1174  "primitiveMesh::checkEdgeAlignment"
1175  "(const bool, const Vector<label>&, labelHashSet*)"
1176  ) << "directions should contain 0 or 1 but is now " << directions
1177  << exit(FatalError);
1178  }
1179  }
1180 
1181  if (nDirs == vector::nComponents)
1182  {
1183  return false;
1184  }
1185 
1186 
1187 
1188  const pointField& p = points();
1189  const faceList& fcs = faces();
1190 
1191  EdgeMap<label> edgesInError;
1192 
1193  forAll(fcs, faceI)
1194  {
1195  const face& f = fcs[faceI];
1196 
1197  forAll(f, fp)
1198  {
1199  label p0 = f[fp];
1200  label p1 = f.nextLabel(fp);
1201  if (p0 < p1)
1202  {
1203  vector d(p[p1]-p[p0]);
1204  scalar magD = mag(d);
1205 
1206  if (magD > ROOTVSMALL)
1207  {
1208  d /= magD;
1209 
1210  // Check how many empty directions are used by the edge.
1211  label nEmptyDirs = 0;
1212  label nNonEmptyDirs = 0;
1213  for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
1214  {
1215  if (mag(d[cmpt]) > 1e-6)
1216  {
1217  if (directions[cmpt] == 0)
1218  {
1219  nEmptyDirs++;
1220  }
1221  else
1222  {
1223  nNonEmptyDirs++;
1224  }
1225  }
1226  }
1227 
1228  if (nEmptyDirs == 0)
1229  {
1230  // Purely in ok directions.
1231  }
1232  else if (nEmptyDirs == 1)
1233  {
1234  // Ok if purely in empty directions.
1235  if (nNonEmptyDirs > 0)
1236  {
1237  edgesInError.insert(edge(p0, p1), faceI);
1238  }
1239  }
1240  else if (nEmptyDirs > 1)
1241  {
1242  // Always an error
1243  edgesInError.insert(edge(p0, p1), faceI);
1244  }
1245  }
1246  }
1247  }
1248  }
1249 
1250  label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>());
1251 
1252  if (nErrorEdges > 0)
1253  {
1254  if (debug || report)
1255  {
1256  Info<< " ***Number of edges not aligned with or perpendicular to "
1257  << "non-empty directions: " << nErrorEdges << endl;
1258  }
1259 
1260  if (setPtr)
1261  {
1262  setPtr->resize(2*edgesInError.size());
1263  forAllConstIter(EdgeMap<label>, edgesInError, iter)
1264  {
1265  setPtr->insert(iter.key()[0]);
1266  setPtr->insert(iter.key()[1]);
1267  }
1268  }
1269 
1270  return true;
1271  }
1272  else
1273  {
1274  if (debug || report)
1275  {
1276  Info<< " All edges aligned with or perpendicular to "
1277  << "non-empty directions." << endl;
1278  }
1279  return false;
1280  }
1281 }
1282 
1283 
1286  const bool report,
1287  labelHashSet* setPtr
1288 ) const
1289 {
1290  if (debug)
1291  {
1292  Info<< "bool primitiveMesh::checkUpperTriangular("
1293  << "const bool, labelHashSet*) const: "
1294  << "checking face ordering" << endl;
1295  }
1296 
1297  // Check whether internal faces are ordered in the upper triangular order
1298  const labelList& own = faceOwner();
1299  const labelList& nei = faceNeighbour();
1300 
1301  const cellList& c = cells();
1302  label internal = nInternalFaces();
1303 
1304  // Has error occurred?
1305  bool error = false;
1306  // Have multiple faces been detected?
1307  label nMultipleCells = false;
1308 
1309  // Loop through faceCells once more and make sure that for internal cell
1310  // the first label is smaller
1311  for (label faceI = 0; faceI < internal; faceI++)
1312  {
1313  if (own[faceI] >= nei[faceI])
1314  {
1315  error = true;
1316 
1317  if (setPtr)
1318  {
1319  setPtr->insert(faceI);
1320  }
1321  }
1322  }
1323 
1324  // Loop through all cells. For each cell, find the face that is internal
1325  // and add it to the check list (upper triangular order).
1326  // Once the list is completed, check it against the faceCell list
1327 
1328  forAll (c, cellI)
1329  {
1330  const labelList& curFaces = c[cellI];
1331 
1332  // Neighbouring cells
1333  SortableList<label> nbr(curFaces.size());
1334 
1335  forAll(curFaces, i)
1336  {
1337  label faceI = curFaces[i];
1338 
1339  if (faceI >= nInternalFaces())
1340  {
1341  // Sort last
1342  nbr[i] = labelMax;
1343  }
1344  else
1345  {
1346  label nbrCellI = nei[faceI];
1347 
1348  if (nbrCellI == cellI)
1349  {
1350  nbrCellI = own[faceI];
1351  }
1352 
1353  if (cellI < nbrCellI)
1354  {
1355  // cellI is master
1356  nbr[i] = nbrCellI;
1357  }
1358  else
1359  {
1360  // nbrCell is master. Let it handle this face.
1361  nbr[i] = labelMax;
1362  }
1363  }
1364  }
1365 
1366  if (!nbr.empty())
1367  {
1368  nbr.sort();
1369 
1370  // Now nbr holds the cellCells in incremental order. Check:
1371  // - neighbouring cells appear only once. Since nbr is sorted this
1372  // is simple check on consecutive elements
1373  // - faces indexed in same order as nbr are incrementing as well.
1374 
1375  label prevCell = nbr[0];
1376  label prevFace = curFaces[nbr.indices()[0]];
1377 
1378  bool hasMultipleFaces = false;
1379 
1380  for (label i = 1; i < nbr.size(); i++)
1381  {
1382  label thisCell = nbr[i];
1383  label thisFace = curFaces[nbr.indices()[i]];
1384 
1385  if (thisCell == labelMax)
1386  {
1387  break;
1388  }
1389 
1390  if (thisCell == prevCell)
1391  {
1392  hasMultipleFaces = true;
1393 
1394  if (setPtr)
1395  {
1396  setPtr->insert(prevFace);
1397  setPtr->insert(thisFace);
1398  }
1399  }
1400  else if (thisFace < prevFace)
1401  {
1402  error = true;
1403 
1404  if (setPtr)
1405  {
1406  setPtr->insert(thisFace);
1407  }
1408  }
1409 
1410  prevCell = thisCell;
1411  prevFace = thisFace;
1412  }
1413 
1414  if (hasMultipleFaces)
1415  {
1416  nMultipleCells++;
1417  }
1418  }
1419  }
1420 
1421  reduce(error, orOp<bool>());
1422  reduce(nMultipleCells, sumOp<label>());
1423 
1424  if ((debug || report) && nMultipleCells > 0)
1425  {
1426  Info<< " <<Found " << nMultipleCells
1427  << " neighbouring cells with multiple inbetween faces." << endl;
1428  }
1429 
1430  if (error)
1431  {
1432  if (debug || report)
1433  {
1434  Info<< " ***Faces not in upper triangular order." << endl;
1435  }
1436 
1437  return true;
1438  }
1439  else
1440  {
1441  if (debug || report)
1442  {
1443  Info<< " Upper triangular ordering OK." << endl;
1444  }
1445 
1446  return false;
1447  }
1448 }
1449 
1450 
1453  const bool report,
1454  labelHashSet* setPtr
1455 ) const
1456 {
1457  if (debug)
1458  {
1459  Info<< "bool primitiveMesh::checkCellsZipUp("
1460  << "const bool, labelHashSet*) const: "
1461  << "checking topological cell openness" << endl;
1462  }
1463 
1464  label nOpenCells = 0;
1465 
1466  const faceList& f = faces();
1467  const cellList& c = cells();
1468 
1469  forAll (c, cellI)
1470  {
1471  const labelList& curFaces = c[cellI];
1472 
1473  const edgeList cellEdges = c[cellI].edges(f);
1474 
1475  labelList edgeUsage(cellEdges.size(), 0);
1476 
1477  forAll (curFaces, faceI)
1478  {
1479  edgeList curFaceEdges = f[curFaces[faceI]].edges();
1480 
1481  forAll (curFaceEdges, faceEdgeI)
1482  {
1483  const edge& curEdge = curFaceEdges[faceEdgeI];
1484 
1485  forAll (cellEdges, cellEdgeI)
1486  {
1487  if (cellEdges[cellEdgeI] == curEdge)
1488  {
1489  edgeUsage[cellEdgeI]++;
1490  break;
1491  }
1492  }
1493  }
1494  }
1495 
1496  edgeList singleEdges(cellEdges.size());
1497  label nSingleEdges = 0;
1498 
1499  forAll (edgeUsage, edgeI)
1500  {
1501  if (edgeUsage[edgeI] == 1)
1502  {
1503  singleEdges[nSingleEdges] = cellEdges[edgeI];
1504  nSingleEdges++;
1505  }
1506  else if (edgeUsage[edgeI] != 2)
1507  {
1508  if (setPtr)
1509  {
1510  setPtr->insert(cellI);
1511  }
1512  }
1513  }
1514 
1515  if (nSingleEdges > 0)
1516  {
1517  if (setPtr)
1518  {
1519  setPtr->insert(cellI);
1520  }
1521 
1522  nOpenCells++;
1523  }
1524  }
1525 
1526  reduce(nOpenCells, sumOp<label>());
1527 
1528  if (nOpenCells > 0)
1529  {
1530  if (debug || report)
1531  {
1532  Info<< " ***Open cells found, number of cells: " << nOpenCells
1533  << ". This problem may be fixable using the zipUpMesh utility."
1534  << endl;
1535  }
1536 
1537  return true;
1538  }
1539  else
1540  {
1541  if (debug || report)
1542  {
1543  Info<< " Topological cell zip-up check OK." << endl;
1544  }
1545 
1546  return false;
1547  }
1548 }
1549 
1550 
1551 // Vertices of face within point range and unique.
1554  const bool report,
1555  labelHashSet* setPtr
1556 ) const
1557 {
1558  if (debug)
1559  {
1560  Info<< "bool primitiveMesh::checkFaceVertices("
1561  << "const bool, labelHashSet*) const: "
1562  << "checking face vertices" << endl;
1563  }
1564 
1565  // Check that all vertex labels are valid
1566  const faceList& f = faces();
1567 
1568  label nErrorFaces = 0;
1569 
1570  forAll (f, fI)
1571  {
1572  const face& curFace = f[fI];
1573 
1574  if (min(curFace) < 0 || max(curFace) > nPoints())
1575  {
1576  if (setPtr)
1577  {
1578  setPtr->insert(fI);
1579  }
1580 
1581  nErrorFaces++;
1582  }
1583 
1584  // Uniqueness of vertices
1585  labelHashSet facePoints(2*curFace.size());
1586 
1587  forAll(curFace, fp)
1588  {
1589  bool inserted = facePoints.insert(curFace[fp]);
1590 
1591  if (!inserted)
1592  {
1593  if (setPtr)
1594  {
1595  setPtr->insert(fI);
1596  }
1597 
1598  nErrorFaces++;
1599  }
1600  }
1601  }
1602 
1603  reduce(nErrorFaces, sumOp<label>());
1604 
1605  if (nErrorFaces > 0)
1606  {
1607  if (debug || report)
1608  {
1609  Info<< " Faces with invalid vertex labels found, "
1610  << " number of faces: " << nErrorFaces << endl;
1611  }
1612 
1613  return true;
1614  }
1615  else
1616  {
1617  if (debug || report)
1618  {
1619  Info<< " Face vertices OK." << endl;
1620  }
1621 
1622  return false;
1623  }
1624 }
1625 
1626 
1627 // Check if all points on face are shared between faces.
1630  const label faceI,
1631  const Map<label>& nCommonPoints,
1632  label& nBaffleFaces,
1633  labelHashSet* setPtr
1634 ) const
1635 {
1636  bool error = false;
1637 
1638  forAllConstIter(Map<label>, nCommonPoints, iter)
1639  {
1640  label nbFaceI = iter.key();
1641  label nCommon = iter();
1642 
1643  const face& curFace = faces()[faceI];
1644  const face& nbFace = faces()[nbFaceI];
1645 
1646  if (nCommon == nbFace.size() || nCommon == curFace.size())
1647  {
1648  if (nbFace.size() != curFace.size())
1649  {
1650  error = true;
1651  }
1652  else
1653  {
1654  nBaffleFaces++;
1655  }
1656 
1657  if (setPtr)
1658  {
1659  setPtr->insert(faceI);
1660  setPtr->insert(nbFaceI);
1661  }
1662  }
1663  }
1664 
1665  return error;
1666 }
1667 
1668 
1669 // Check that shared points are in consecutive order.
1672  const label faceI,
1673  const Map<label>& nCommonPoints,
1674  labelHashSet* setPtr
1675 ) const
1676 {
1677  bool error = false;
1678 
1679  forAllConstIter(Map<label>, nCommonPoints, iter)
1680  {
1681  label nbFaceI = iter.key();
1682  label nCommon = iter();
1683 
1684  const face& curFace = faces()[faceI];
1685  const face& nbFace = faces()[nbFaceI];
1686 
1687  if
1688  (
1689  nCommon >= 2
1690  && nCommon != nbFace.size()
1691  && nCommon != curFace.size()
1692  )
1693  {
1694  forAll(curFace, fp)
1695  {
1696  // Get the index in the neighbouring face shared with curFace
1697  label nb = findIndex(nbFace, curFace[fp]);
1698 
1699  if (nb != -1)
1700  {
1701 
1702  // Check the whole face from nb onwards for shared vertices
1703  // with neighbouring face. Rule is that any shared vertices
1704  // should be consecutive on both faces i.e. if they are
1705  // vertices fp,fp+1,fp+2 on one face they should be
1706  // vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
1707  // other face.
1708 
1709 
1710  // Vertices before and after on curFace
1711  label fpPlus1 = curFace.fcIndex(fp);
1712  label fpMin1 = curFace.rcIndex(fp);
1713 
1714  // Vertices before and after on nbFace
1715  label nbPlus1 = nbFace.fcIndex(nb);
1716  label nbMin1 = nbFace.rcIndex(nb);
1717 
1718  // Find order of walking by comparing next points on both
1719  // faces.
1720  label curInc = labelMax;
1721  label nbInc = labelMax;
1722 
1723  if (nbFace[nbPlus1] == curFace[fpPlus1])
1724  {
1725  curInc = 1;
1726  nbInc = 1;
1727  }
1728  else if (nbFace[nbPlus1] == curFace[fpMin1])
1729  {
1730  curInc = -1;
1731  nbInc = 1;
1732  }
1733  else if (nbFace[nbMin1] == curFace[fpMin1])
1734  {
1735  curInc = -1;
1736  nbInc = -1;
1737  }
1738  else
1739  {
1740  curInc = 1;
1741  nbInc = -1;
1742  }
1743 
1744 
1745  // Pass1: loop until start of common vertices found.
1746  label curNb = nb;
1747  label curFp = fp;
1748 
1749  do
1750  {
1751  curFp += curInc;
1752 
1753  if (curFp >= curFace.size())
1754  {
1755  curFp = 0;
1756  }
1757  else if (curFp < 0)
1758  {
1759  curFp = curFace.size()-1;
1760  }
1761 
1762  curNb += nbInc;
1763 
1764  if (curNb >= nbFace.size())
1765  {
1766  curNb = 0;
1767  }
1768  else if (curNb < 0)
1769  {
1770  curNb = nbFace.size()-1;
1771  }
1772  } while (curFace[curFp] == nbFace[curNb]);
1773 
1774 
1775  // Pass2: check equality walking from curFp, curNb
1776  // in opposite order.
1777 
1778  curInc = -curInc;
1779  nbInc = -nbInc;
1780 
1781  for (label commonI = 0; commonI < nCommon; commonI++)
1782  {
1783  curFp += curInc;
1784 
1785  if (curFp >= curFace.size())
1786  {
1787  curFp = 0;
1788  }
1789  else if (curFp < 0)
1790  {
1791  curFp = curFace.size()-1;
1792  }
1793 
1794  curNb += nbInc;
1795 
1796  if (curNb >= nbFace.size())
1797  {
1798  curNb = 0;
1799  }
1800  else if (curNb < 0)
1801  {
1802  curNb = nbFace.size()-1;
1803  }
1804 
1805  if (curFace[curFp] != nbFace[curNb])
1806  {
1807  if (setPtr)
1808  {
1809  setPtr->insert(faceI);
1810  setPtr->insert(nbFaceI);
1811  }
1812 
1813  error = true;
1814 
1815  break;
1816  }
1817  }
1818 
1819 
1820  // Done the curFace - nbFace combination.
1821  break;
1822  }
1823  }
1824  }
1825  }
1826 
1827  return error;
1828 }
1829 
1830 
1831 // Checks common vertices between faces. If more than 2 they should be
1832 // consecutive on both faces.
1835  const bool report,
1836  labelHashSet* setPtr
1837 ) const
1838 {
1839  if (debug)
1840  {
1841  Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
1842  << " const: " << "checking face-face connectivity" << endl;
1843  }
1844 
1845  const labelListList& pf = pointFaces();
1846 
1847  label nBaffleFaces = 0;
1848  label nErrorDuplicate = 0;
1849  label nErrorOrder = 0;
1850  Map<label> nCommonPoints(100);
1851 
1852  for (label faceI = 0; faceI < nFaces(); faceI++)
1853  {
1854  const face& curFace = faces()[faceI];
1855 
1856  // Calculate number of common points between current faceI and
1857  // neighbouring face. Store on map.
1858  nCommonPoints.clear();
1859 
1860  forAll(curFace, fp)
1861  {
1862  label pointI = curFace[fp];
1863 
1864  const labelList& nbs = pf[pointI];
1865 
1866  forAll(nbs, nbI)
1867  {
1868  label nbFaceI = nbs[nbI];
1869 
1870  if (faceI < nbFaceI)
1871  {
1872  // Only check once for each combination of two faces.
1873 
1874  Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
1875 
1876  if (fnd == nCommonPoints.end())
1877  {
1878  // First common vertex found.
1879  nCommonPoints.insert(nbFaceI, 1);
1880  }
1881  else
1882  {
1883  fnd()++;
1884  }
1885  }
1886  }
1887  }
1888 
1889  // Perform various checks on common points
1890 
1891  // Check all vertices shared (duplicate point)
1892  if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
1893  {
1894  nErrorDuplicate++;
1895  }
1896 
1897  // Check common vertices are consecutive on both faces
1898  if (checkCommonOrder(faceI, nCommonPoints, setPtr))
1899  {
1900  nErrorOrder++;
1901  }
1902  }
1903 
1904  reduce(nBaffleFaces, sumOp<label>());
1905  reduce(nErrorDuplicate, sumOp<label>());
1906  reduce(nErrorOrder, sumOp<label>());
1907 
1908  if (nBaffleFaces)
1909  {
1910  Info<< " Number of identical duplicate faces (baffle faces): "
1911  << nBaffleFaces << endl;
1912  }
1913 
1914  if (nErrorDuplicate > 0 || nErrorOrder > 0)
1915  {
1916  if (nErrorDuplicate > 0)
1917  {
1918  Info<< " ***Number of duplicate (not baffle) faces found: "
1919  << nErrorDuplicate << endl;
1920  }
1921 
1922  if (nErrorOrder > 0)
1923  {
1924  Info<< " ***Number of faces with non-consecutive shared points: "
1925  << nErrorOrder << endl;
1926  }
1927 
1928  return true;
1929  }
1930  else
1931  {
1932  if (debug || report)
1933  {
1934  Info<< " Face-face connectivity OK." << endl;
1935  }
1936 
1937  return false;
1938  }
1939 }
1940 
1941 
1942 // Checks cells with 1 or less internal faces. Give numerical problems.
1945  const bool report, // report,
1946  labelHashSet* setPtr // setPtr
1947 ) const
1948 {
1949  if (debug)
1950  {
1951  Info<< "bool primitiveMesh::checkCellDeterminant(const bool"
1952  << ", labelHashSet*) const: "
1953  << "checking for under-determined cells" << endl;
1954  }
1955 
1956  const cellList& c = cells();
1957 
1958  label nErrorCells = 0;
1959 
1960  scalar minDet = GREAT;
1961  scalar sumDet = 0;
1962  label nSummed = 0;
1963 
1964  forAll (c, cellI)
1965  {
1966  const labelList& curFaces = c[cellI];
1967 
1968  // Calculate local normalization factor
1969  scalar avgArea = 0;
1970 
1971  label nInternalFaces = 0;
1972 
1973  forAll(curFaces, i)
1974  {
1975  if (isInternalFace(curFaces[i]))
1976  {
1977  avgArea += mag(faceAreas()[curFaces[i]]);
1978 
1979  nInternalFaces++;
1980  }
1981  }
1982 
1983  if (nInternalFaces == 0)
1984  {
1985  if (setPtr)
1986  {
1987  setPtr->insert(cellI);
1988  }
1989 
1990  nErrorCells++;
1991  }
1992  else
1993  {
1994  avgArea /= nInternalFaces;
1995 
1996  symmTensor areaTensor(symmTensor::zero);
1997 
1998  forAll(curFaces, i)
1999  {
2000  if (isInternalFace(curFaces[i]))
2001  {
2002  areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea);
2003  }
2004  }
2005 
2006  scalar determinant = mag(det(areaTensor));
2007 
2008  minDet = min(determinant, minDet);
2009  sumDet += determinant;
2010  nSummed++;
2011 
2012  if (determinant < 1e-3)
2013  {
2014  if (setPtr)
2015  {
2016  setPtr->insert(cellI);
2017  }
2018 
2019  nErrorCells++;
2020  }
2021  }
2022  }
2023 
2024  reduce(nErrorCells, sumOp<label>());
2025  reduce(minDet, minOp<scalar>());
2026  reduce(sumDet, sumOp<scalar>());
2027  reduce(nSummed, sumOp<label>());
2028 
2029  if (debug || report)
2030  {
2031  if (nSummed > 0)
2032  {
2033  Info<< " Cell determinant (wellposedness) : minimum: " << minDet
2034  << " average: " << sumDet/nSummed
2035  << endl;
2036  }
2037  }
2038 
2039  if (nErrorCells > 0)
2040  {
2041  if (debug || report)
2042  {
2043  Info<< " ***Cells with small determinant found, number of cells: "
2044  << nErrorCells << endl;
2045  }
2046 
2047  return true;
2048  }
2049  else
2050  {
2051  if (debug || report)
2052  {
2053  Info<< " Cell determinant check OK." << endl;
2054  }
2055 
2056  return false;
2057  }
2058 
2059  return false;
2060 }
2061 
2062 
2063 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2064 
2065 bool Foam::primitiveMesh::checkTopology(const bool report) const
2066 {
2067  label noFailedChecks = 0;
2068 
2069  if (checkPoints(report)) noFailedChecks++;
2070  if (checkUpperTriangular(report)) noFailedChecks++;
2071  if (checkCellsZipUp(report)) noFailedChecks++;
2072  if (checkFaceVertices(report)) noFailedChecks++;
2073  if (checkFaceFaces(report)) noFailedChecks++;
2074  //if (checkCellDeterminant(report)) noFailedChecks++;
2075 
2076  if (noFailedChecks == 0)
2077  {
2078  if (debug || report)
2079  {
2080  Info<< " Mesh topology OK." << endl;
2081  }
2082 
2083  return false;
2084  }
2085  else
2086  {
2087  if (debug || report)
2088  {
2089  Info<< " Failed " << noFailedChecks
2090  << " mesh topology checks." << endl;
2091  }
2092 
2093  return true;
2094  }
2095 }
2096 
2097 
2098 bool Foam::primitiveMesh::checkGeometry(const bool report) const
2099 {
2100  label noFailedChecks = 0;
2101 
2102  if (checkClosedBoundary(report)) noFailedChecks++;
2103  if (checkClosedCells(report)) noFailedChecks++;
2104  if (checkFaceAreas(report)) noFailedChecks++;
2105  if (checkCellVolumes(report)) noFailedChecks++;
2106  if (checkFaceOrthogonality(report)) noFailedChecks++;
2107  if (checkFacePyramids(report)) noFailedChecks++;
2108  if (checkFaceSkewness(report)) noFailedChecks++;
2109 
2110  if (noFailedChecks == 0)
2111  {
2112  if (debug || report)
2113  {
2114  Info<< " Mesh geometry OK." << endl;
2115  }
2116  return false;
2117  }
2118  else
2119  {
2120  if (debug || report)
2121  {
2122  Info<< " Failed " << noFailedChecks
2123  << " mesh geometry checks." << endl;
2124  }
2125 
2126  return true;
2127  }
2128 }
2129 
2130 
2131 bool Foam::primitiveMesh::checkMesh(const bool report) const
2132 {
2133  if (debug)
2134  {
2135  Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
2136  << "checking primitiveMesh" << endl;
2137  }
2138 
2139  label noFailedChecks = checkTopology(report) + checkGeometry(report);
2140 
2141  if (noFailedChecks == 0)
2142  {
2143  if (debug || report)
2144  {
2145  Info<< "Mesh OK." << endl;
2146  }
2147 
2148  return false;
2149  }
2150  else
2151  {
2152  if (debug || report)
2153  {
2154  Info<< " Failed " << noFailedChecks
2155  << " mesh checks." << endl;
2156  }
2157 
2158  return true;
2159  }
2160 }
2161 
2162 
2163 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
2164 
2165 
2166 // ************************************************************************* //
Foam::maxOp
Definition: ops.H:172
Foam::polyMeshGenChecks::checkClosedBoundary
bool checkClosedBoundary(const polyMeshGen &, const bool report=false)
Check boundary closedness.
Definition: polyMeshGenChecksGeometry.C:45
mathematicalConstants.H
Foam::SymmTensor< scalar >
Foam::Vector< scalar >::zero
static const Vector zero
Definition: Vector.H:80
p
p
Definition: pEqn.H:62
Foam::HashTable::resize
void resize(const label newSize)
Resize the hash table for efficiency.
Definition: HashTable.C:436
Foam::polyMeshGenChecks::checkFaceSkewness
void checkFaceSkewness(const polyMeshGen &, scalarField &, const boolList *changedFacePtr=NULL)
Check face skewness.
Definition: polyMeshGenChecksGeometry.C:1138
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:86
Foam::SortableList::sort
void sort()
(stable) sort the list (if changed after construction time)
Definition: SortableList.C:95
Foam::primitiveMesh::checkMesh
bool checkMesh(const bool report=false) const
Check mesh for correctness. Returns false for no error.
Definition: primitiveMeshCheck.C:2131
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:406
Foam::findIndex
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurence of given element and return index,.
Foam::sin
dimensionedScalar sin(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:255
Foam::primitiveMesh::checkFaceVertices
bool checkFaceVertices(const bool report=false, labelHashSet *setPtr=NULL) const
Check uniqueness of face vertices.
Definition: primitiveMeshCheck.C:1553
Foam::labelMax
static const label labelMax
Definition: label.H:62
Foam::minOp
Definition: ops.H:173
Foam::primitiveMesh::checkDuplicateFaces
bool checkDuplicateFaces(const label, const Map< label > &, label &nBaffleFaces, labelHashSet *) const
Check if all points on face are shared with another face.
Definition: primitiveMeshCheck.C:1629
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:58
Foam::polyMeshGenChecks::checkUpperTriangular
bool checkUpperTriangular(const polyMeshGen &, const bool report=false, labelHashSet *setPtr=NULL)
Check face ordering.
Definition: polyMeshGenChecksTopology.C:118
Foam::HashTable::insert
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
Foam::VectorSpace< Vector< scalar >, scalar, 3 >::nComponents
@ nComponents
Number of components in this vector space.
Definition: VectorSpace.H:88
Foam::checkGeometry
label checkGeometry(const polyMesh &mesh, const bool allGeometry, const autoPtr< surfaceWriter > &)
Definition: checkGeometry.C:478
Foam::Map< label >
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
Foam::gSum
Type gSum(const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:564
Foam::polyMeshGenChecks::checkFaceAreas
bool checkFaceAreas(const polyMeshGen &, const bool report=false, const scalar minFaceArea=VSMALL, labelHashSet *setPtr=NULL, const boolList *changedFacePtr=NULL)
Check for negative face areas.
Definition: polyMeshGenChecksGeometry.C:362
Foam::mag
dimensioned< scalar > mag(const dimensioned< Type > &)
Foam::directions
Set of directions for each cell in the mesh. Either uniform and size=1 or one set of directions per c...
Definition: directions.H:63
Foam::HashSet< label, Hash< label > >
Foam::primitiveMesh::checkFaceAreas
bool checkFaceAreas(const bool report=false, labelHashSet *setPtr=NULL) const
Check for negative face areas.
Definition: primitiveMeshCheck.C:338
Foam::polyMeshGenChecks::checkFaceVertices
bool checkFaceVertices(const polyMeshGen &, const bool report=false, labelHashSet *setPtr=NULL)
Check uniqueness of face vertices.
Definition: polyMeshGenChecksTopology.C:373
Foam::primitiveMesh::checkTopology
bool checkTopology(const bool report=false) const
Check mesh topology for correctness.
Definition: primitiveMeshCheck.C:2065
forAllConstIter
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:39
Foam::cmptMin
void cmptMin(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:269
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
Foam::cmptMag
void cmptMag(FieldField< Field, Type > &cf, const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:359
Foam::cmptMax
void cmptMax(FieldField< Field, typename FieldField< Field, Type >::cmptType > &cf, const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:224
n
label n
Definition: TABSMDCalcMethod2.H:31
SortableList.H
Foam::primitiveMesh::checkGeometry
bool checkGeometry(const bool report=false) const
Check mesh geometry (& implicitly topology) for correctness.
Definition: primitiveMeshCheck.C:2098
Foam::reduce
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
Definition: PstreamReduceOps.H:43
Foam::primitiveMesh::checkFaceFlatness
bool checkFaceFlatness(const bool report, labelHashSet *setPtr) const
Check face warpage: decompose face and check ratio between.
Definition: primitiveMeshCheck.C:1020
Foam::label
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Foam::Field
Pre-declare SubField and related Field type.
Definition: Field.H:57
Foam::orOp
Definition: ops.H:178
Foam::nl
static const char nl
Definition: Ostream.H:260
Foam::Info
messageStream Info
Foam::polyMeshGenChecks::checkCellsZipUp
bool checkCellsZipUp(const polyMeshGen &, const bool report=false, labelHashSet *setPtr=NULL)
Check cell zip-up.
Definition: polyMeshGenChecksTopology.C:254
Foam::primitiveMesh::checkFaceOrthogonality
bool checkFaceOrthogonality(const bool report=false, labelHashSet *setPtr=NULL) const
Check for non-orthogonality.
Definition: primitiveMeshCheck.C:464
Foam::polyMeshGenChecks::checkPoints
bool checkPoints(const polyMeshGen &, const bool report=false, labelHashSet *setPtr=NULL)
Check for unused points.
Definition: polyMeshGenChecksTopology.C:46
Foam::pow
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
Definition: dimensionedScalar.C:73
Foam::FatalError
error FatalError
Foam::HashTable::size
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
Foam::primitiveMesh::nInternalFaces
label nInternalFaces() const
Definition: primitiveMeshI.H:52
Foam::SortableList
A list that is sorted upon construction or when explicitly requested with the sort() method.
Definition: List.H:65
Foam::primitiveMesh::aspectThreshold_
static const debug::tolerancesSwitch aspectThreshold_
Aspect ratio warning threshold.
Definition: primitiveMesh.H:327
Foam::primitiveMesh::checkClosedBoundary
bool checkClosedBoundary(const bool report=false) const
Check boundary for closedness.
Definition: primitiveMeshCheck.C:79
Foam::primitiveMesh::checkFaceSkewness
bool checkFaceSkewness(const bool report=false, labelHashSet *setPtr=NULL) const
Check face skewness.
Definition: primitiveMeshCheck.C:669
Foam::SymmTensor< scalar >::zero
static const SymmTensor zero
Definition: SymmTensor.H:77
Foam::e
const double e
Elementary charge.
Definition: doubleFloat.H:94
s
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Foam::primitiveMesh::checkEdgeAlignment
bool checkEdgeAlignment(const bool report, const Vector< label > &directions, labelHashSet *setPtr=NULL) const
Check edge alignment for 1D/2D cases.
Definition: primitiveMeshCheck.C:1150
Foam::primitiveMesh::checkUpperTriangular
bool checkUpperTriangular(const bool report=false, labelHashSet *setPtr=NULL) const
Check face ordering.
Definition: primitiveMeshCheck.C:1285
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
Foam::polyMeshGenChecks::checkFacePyramids
bool checkFacePyramids(const polyMeshGen &, const bool report=false, const scalar minPyrVol=-SMALL, labelHashSet *setPtr=NULL, const boolList *changedFacePtr=NULL)
Check face pyramid volume.
Definition: polyMeshGenChecksGeometry.C:962
Foam::max
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
Foam::primitiveMesh::closedThreshold_
static const debug::tolerancesSwitch closedThreshold_
Static data to control mesh checking.
Definition: primitiveMesh.H:324
Foam::EdgeMap< label >
Foam::primitiveMesh::checkClosedCells
bool checkClosedCells(const bool report=false, labelHashSet *setPtr=NULL, labelHashSet *highAspectSetPtr=NULL) const
Check cells for closedness.
Definition: primitiveMeshCheck.C:135
Foam::primitiveMesh::nonOrthThreshold_
static Foam::debug::tolerancesSwitch nonOrthThreshold_
Non-orthogonality warning threshold in deg.
Definition: primitiveMesh.H:330
Foam::primitiveMesh::checkCellDeterminant
bool checkCellDeterminant(const bool report=false, labelHashSet *setPtr=NULL) const
Check cell determinant.
Definition: primitiveMeshCheck.C:1944
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:49
Foam::sumOp
Definition: ops.H:162
Foam::primitiveMesh::checkFaceAngles
bool checkFaceAngles(const bool report=false, labelHashSet *setPtr=NULL) const
Check face angles.
Definition: primitiveMeshCheck.C:892
f
labelList f(nPoints)
Foam::polyMeshGenChecks::checkCellVolumes
bool checkCellVolumes(const polyMeshGen &, const bool report=false, labelHashSet *setPtr=NULL)
Check for negative cell volumes.
Definition: polyMeshGenChecksGeometry.C:294
Foam::primitiveMesh::skewThreshold_
static const debug::tolerancesSwitch skewThreshold_
Skewness warning threshold.
Definition: primitiveMesh.H:333
Foam::Vector< scalar >
Foam::primitiveMesh::checkCellVolumes
bool checkCellVolumes(const bool report=false, labelHashSet *setPtr=NULL) const
Check for negative cell volumes.
Definition: primitiveMeshCheck.C:397
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:59
Foam::primitiveMesh::checkPoints
bool checkPoints(const bool report=false, labelHashSet *setPtr=NULL) const
Check for unused points.
Definition: primitiveMeshCheck.C:814
Foam::acos
dimensionedScalar acos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:259
Foam::primitiveMesh::checkFaceFaces
bool checkFaceFaces(const bool report=false, labelHashSet *setPtr=NULL) const
Check face-face connectivity.
Definition: primitiveMeshCheck.C:1834
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: doubleFloat.H:94
Foam::HashSet::insert
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:116
Foam::direction
unsigned char direction
Definition: direction.H:43
Foam::SortableList::indices
const labelList & indices() const
Return the list of sorted indices. Updated every sort.
Definition: SortableList.H:90
Foam::constant::mathematical::pi
const scalar pi(M_PI)
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
FatalErrorIn
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:313
Foam::face
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
cells
const cellShapeList & cells
Definition: gmvOutputHeader.H:3
ListOps.H
Various functions to operate on Lists.
Foam::det
dimensionedScalar det(const dimensionedSphericalTensor &dt)
Definition: dimensionedSphericalTensor.C:60
Foam::List::size
void size(const label)
Override size to be inconsistent with allocated storage.
Foam::primitiveMesh::checkCellsZipUp
bool checkCellsZipUp(const bool report=false, labelHashSet *setPtr=NULL) const
Check cell zip-up.
Definition: primitiveMeshCheck.C:1452
Foam::primitiveMesh::faceFlatnessThreshold_
static const debug::tolerancesSwitch faceFlatnessThreshold_
Face flatness threshold.
Definition: primitiveMesh.H:339
Foam::primitiveMesh::faceAngleThreshold_
static Foam::debug::tolerancesSwitch faceAngleThreshold_
Face angle threshold.
Definition: primitiveMesh.H:336
Foam::primitiveMesh::checkFacePyramids
bool checkFacePyramids(const bool report=false, const scalar minPyrVol=-SMALL, labelHashSet *setPtr=NULL) const
Check face pyramid volume.
Definition: primitiveMeshCheck.C:585
Foam::cmptSum
Cmpt cmptSum(const VectorSpace< Form, Cmpt, nCmpt > &vs)
Definition: VectorSpaceI.H:405
Foam::min
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
Foam::pyramidPointFaceRef
pyramid< point, const point &, const face & > pyramidPointFaceRef
Definition: pyramidPointFaceRef.H:45
pyramidPointFaceRef.H
Foam::cell
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:56
Foam::error
Class to handle errors and exceptions in a simple, consistent stream-based manner.
Definition: error.H:66
Foam::polyMeshGenChecks::checkClosedCells
bool checkClosedCells(const polyMeshGen &, const bool report=false, const scalar aspectWarn=1000, labelHashSet *setPtr=NULL)
Check cells for closedness.
Definition: polyMeshGenChecksGeometry.C:113
Foam::asin
dimensionedScalar asin(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:258
normal
A normal distribution model.
Foam::checkTopology
label checkTopology(const polyMesh &, const bool, const bool, const autoPtr< surfaceWriter > &)
Definition: checkTopology.C:42
Foam::primitiveMesh::checkCommonOrder
bool checkCommonOrder(const label, const Map< label > &, labelHashSet *) const
Check that shared points are in consecutive order.
Definition: primitiveMeshCheck.C:1671
Foam::cos
dimensionedScalar cos(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:256
Foam::primitiveMesh::faceAreas
const vectorField & faceAreas() const
Definition: primitiveMeshFaceCentresAndAreas.C:141