checkGeometry.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "meshCheck.H"
27 #include "PatchTools.H"
28 #include "polyMeshCheck.H"
29 #include "cellSet.H"
30 #include "faceSet.H"
31 #include "pointSet.H"
32 #include "EdgeMap.H"
33 #include "wedgePolyPatch.H"
35 
36 #include "vtkSurfaceWriter.H"
37 #include "setWriter.H"
38 #include "writeFile.H"
40 
41 #include "mergeAndWrite.H"
42 #include "Time.H"
43 
44 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
45 
47 (
48  const polyMesh& mesh,
49  const wedgePolyPatch& wpp
50 )
51 {
52  const polyBoundaryMesh& patches = mesh.boundaryMesh();
53 
54  scalar wppCosAngle = wpp.cosAngle();
55 
57  {
58  if
59  (
60  patchi != wpp.index()
61  && patches[patchi].size()
62  && isA<wedgePolyPatch>(patches[patchi])
63  )
64  {
65  const wedgePolyPatch& pp =
66  refCast<const wedgePolyPatch>(patches[patchi]);
67 
68  // Calculate (cos of) angle to wpp (not pp!) centre normal
69  scalar ppCosAngle = wpp.centreNormal() & pp.n();
70 
71  if
72  (
73  pp.size() == wpp.size()
74  && mag(pp.axis() & wpp.axis()) >= (1-1e-3)
75  && mag(ppCosAngle - wppCosAngle) >= 1e-3
76  )
77  {
78  return patchi;
79  }
80  }
81  }
82  return -1;
83 }
84 
85 
87 (
88  const polyMesh& mesh,
89  const bool report,
91  labelHashSet* setPtr
92 )
93 {
94  // To mark edges without calculating edge addressing
95  EdgeMap<label> edgesInError;
96 
97  const pointField& p = mesh.points();
98  const faceList& fcs = mesh.faces();
99 
100 
101  const polyBoundaryMesh& patches = mesh.boundaryMesh();
103  {
104  if (patches[patchi].size() && isA<wedgePolyPatch>(patches[patchi]))
105  {
106  const wedgePolyPatch& pp =
107  refCast<const wedgePolyPatch>(patches[patchi]);
108 
109  scalar wedgeAngle = acos(pp.cosAngle());
110 
111  if (report)
112  {
113  Info<< " Wedge " << pp.name() << " with angle "
114  << radToDeg(wedgeAngle) << " degrees"
115  << endl;
116  }
117 
118  // Find opposite
119  label oppositePatchi = findOppositeWedge(mesh, pp);
120 
121  if (oppositePatchi == -1)
122  {
123  if (report)
124  {
125  Info<< " ***Cannot find opposite wedge for wedge "
126  << pp.name() << endl;
127  }
128  return true;
129  }
130 
131  const wedgePolyPatch& opp =
132  refCast<const wedgePolyPatch>(patches[oppositePatchi]);
133 
134 
135  if (mag(opp.axis() & pp.axis()) < (1-1e-3))
136  {
137  if (report)
138  {
139  Info<< " ***Wedges do not have the same axis."
140  << " Encountered " << pp.axis()
141  << " on patch " << pp.name()
142  << " which differs from " << opp.axis()
143  << " on opposite wedge patch" << opp.axis()
144  << endl;
145  }
146  return true;
147  }
148 
149 
150 
151  // Mark edges on wedgePatches
152  forAll(pp, i)
153  {
154  const face& f = pp[i];
155  forAll(f, fp)
156  {
157  label p0 = f[fp];
158  label p1 = f.nextLabel(fp);
159  edgesInError.insert(edge(p0, p1), -1); // non-error value
160  }
161  }
162 
163 
164  // Check that wedge patch is flat
165  const point& p0 = p[pp.meshPoints()[0]];
166  forAll(pp.meshPoints(), i)
167  {
168  const point& pt = p[pp.meshPoints()[i]];
169  scalar d = mag((pt - p0) & pp.n());
170 
171  if (d > rootSmall)
172  {
173  if (report)
174  {
175  Info<< " ***Wedge patch " << pp.name() << " not planar."
176  << " Point " << pt << " is not in patch plane by "
177  << d << " metre."
178  << endl;
179  }
180  return true;
181  }
182  }
183  }
184  }
185 
186 
187 
188  // Check all non-wedge faces
189  label nEdgesInError = 0;
190 
191  forAll(fcs, facei)
192  {
193  const face& f = fcs[facei];
194 
195  forAll(f, fp)
196  {
197  label p0 = f[fp];
198  label p1 = f.nextLabel(fp);
199  if (p0 < p1)
200  {
201  vector d(p[p1]-p[p0]);
202  scalar magD = mag(d);
203 
204  if (magD > rootVSmall)
205  {
206  d /= magD;
207 
208  // Check how many empty directions are used by the edge.
209  label nEmptyDirs = 0;
210  label nNonEmptyDirs = 0;
211  for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
212  {
213  if (mag(d[cmpt]) > 1e-6)
214  {
215  if (directions[cmpt] == 0)
216  {
217  nEmptyDirs++;
218  }
219  else
220  {
221  nNonEmptyDirs++;
222  }
223  }
224  }
225 
226  if (nEmptyDirs == 0)
227  {
228  // Purely in ok directions.
229  }
230  else if (nEmptyDirs == 1)
231  {
232  // Ok if purely in empty directions.
233  if (nNonEmptyDirs > 0)
234  {
235  if (edgesInError.insert(edge(p0, p1), facei))
236  {
237  nEdgesInError++;
238  }
239  }
240  }
241  else if (nEmptyDirs > 1)
242  {
243  // Always an error
244  if (edgesInError.insert(edge(p0, p1), facei))
245  {
246  nEdgesInError++;
247  }
248  }
249  }
250  }
251  }
252  }
253 
254  label nErrorEdges = returnReduce(nEdgesInError, sumOp<label>());
255 
256  if (nErrorEdges > 0)
257  {
258  if (report)
259  {
260  Info<< " ***Number of edges not aligned with or perpendicular to "
261  << "non-empty directions: " << nErrorEdges << endl;
262  }
263 
264  if (setPtr)
265  {
266  setPtr->resize(2*nEdgesInError);
267  forAllConstIter(EdgeMap<label>, edgesInError, iter)
268  {
269  if (iter() >= 0)
270  {
271  setPtr->insert(iter.key()[0]);
272  setPtr->insert(iter.key()[1]);
273  }
274  }
275  }
276 
277  return true;
278  }
279  else
280  {
281  if (report)
282  {
283  Info<< " All edges aligned with or perpendicular to "
284  << "non-empty directions." << endl;
285  }
286  return false;
287  }
288 }
289 
290 
291 namespace Foam
292 {
293  //- Default transformation behaviour for position
295  {
296  public:
297 
298  //- Transform patch-based field
299  void operator()
300  (
301  const coupledPolyPatch& cpp,
302  List<pointField>& pts
303  ) const
304  {
305  // Each element of pts is all the points in the face. Convert into
306  // lists of size cpp to transform.
307 
308  List<pointField> newPts(pts.size());
309  forAll(pts, facei)
310  {
311  newPts[facei].setSize(pts[facei].size());
312  }
313 
314  label index = 0;
315  while (true)
316  {
317  label n = 0;
318 
319  // Extract for every face the i'th position
320  pointField ptsAtIndex(pts.size(), Zero);
321  forAll(cpp, facei)
322  {
323  const pointField& facePts = pts[facei];
324  if (facePts.size() > index)
325  {
326  ptsAtIndex[facei] = facePts[index];
327  n++;
328  }
329  }
330 
331  if (n == 0)
332  {
333  break;
334  }
335 
336  // Now ptsAtIndex will have for every face either zero or
337  // the position of the i'th vertex. Transform.
338  cpp.transform().transformPosition(ptsAtIndex, ptsAtIndex);
339 
340  // Extract back from ptsAtIndex into newPts
341  forAll(cpp, facei)
342  {
343  pointField& facePts = newPts[facei];
344  if (facePts.size() > index)
345  {
346  facePts[index] = ptsAtIndex[facei];
347  }
348  }
349 
350  index++;
351  }
352 
353  pts.transfer(newPts);
354  }
355  };
356 }
357 
358 
360 (
361  const polyMesh& mesh,
362  const bool report,
363  labelHashSet* setPtr
364 )
365 {
366  const pointField& p = mesh.points();
367  const faceList& fcs = mesh.faces();
368  const polyBoundaryMesh& patches = mesh.boundaryMesh();
369 
370  // Zero'th point on coupled faces
371  // pointField nbrZeroPoint(fcs.size()-mesh.nInternalFaces(), vector::max);
372  List<pointField> nbrPoints(fcs.size() - mesh.nInternalFaces());
373 
374  // Exchange zero point
376  {
377  if (patches[patchi].coupled())
378  {
379  const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>
380  (
381  patches[patchi]
382  );
383 
384  forAll(cpp, i)
385  {
386  label bFacei = cpp.start() + i - mesh.nInternalFaces();
387  const face& f = cpp[i];
388  nbrPoints[bFacei].setSize(f.size());
389  forAll(f, fp)
390  {
391  const point& p0 = p[f[fp]];
392  nbrPoints[bFacei][fp] = p0;
393  }
394  }
395  }
396  }
397  syncTools::syncBoundaryFaceList
398  (
399  mesh,
400  nbrPoints,
403  );
404 
405  // Compare to local ones. Use same tolerance as for matching
406  label nErrorFaces = 0;
407  scalar avgMismatch = 0;
408  label nCoupledPoints = 0;
409 
411  {
412  if (patches[patchi].coupled())
413  {
414  const coupledPolyPatch& cpp =
415  refCast<const coupledPolyPatch>(patches[patchi]);
416 
417  if (cpp.owner())
418  {
419  scalarField smallDist
420  (
421  cpp.calcFaceTol
422  (
423  // cpp.matchTolerance(),
424  cpp,
425  cpp.points(),
426  cpp.faceCentres()
427  )
428  );
429 
430  forAll(cpp, i)
431  {
432  label bFacei = cpp.start() + i - mesh.nInternalFaces();
433  const face& f = cpp[i];
434 
435  if (f.size() != nbrPoints[bFacei].size())
436  {
438  << "Local face size : " << f.size()
439  << " does not equal neighbour face size : "
440  << nbrPoints[bFacei].size()
441  << abort(FatalError);
442  }
443 
444  label fp = 0;
445  forAll(f, j)
446  {
447  const point& p0 = p[f[fp]];
448  scalar d = mag(p0 - nbrPoints[bFacei][j]);
449 
450  if (d > smallDist[i])
451  {
452  if (setPtr)
453  {
454  setPtr->insert(cpp.start()+i);
455  }
456  nErrorFaces++;
457 
458  break;
459  }
460 
461  avgMismatch += d;
462  nCoupledPoints++;
463 
464  fp = f.rcIndex(fp);
465  }
466  }
467  }
468  }
469  }
470 
471  reduce(nErrorFaces, sumOp<label>());
472  reduce(avgMismatch, maxOp<scalar>());
473  reduce(nCoupledPoints, sumOp<label>());
474 
475  if (nCoupledPoints > 0)
476  {
477  avgMismatch /= nCoupledPoints;
478  }
479 
480  if (nErrorFaces > 0)
481  {
482  if (report)
483  {
484  Info<< " **Error in coupled point location: "
485  << nErrorFaces
486  << " faces have their 0th or consecutive vertex not opposite"
487  << " their coupled equivalent. Average mismatch "
488  << avgMismatch << "."
489  << endl;
490  }
491 
492  return true;
493  }
494  else
495  {
496  if (report)
497  {
498  Info<< " Coupled point location match (average "
499  << avgMismatch << ") OK." << endl;
500  }
501 
502  return false;
503  }
504 }
505 
506 
508 (
509  const polyMesh& mesh,
510  const bool allGeometry,
511  const scalar nonOrthThreshold,
512  const scalar skewThreshold,
513  const autoPtr<surfaceWriter>& surfWriter,
515 )
516 {
517  const scalar closedThreshold = 1.0e-6;
518  const scalar aspectThreshold = 1000;
519  const scalar planarCosAngle = 1.0e-6;
520 
521  label noFailedChecks = 0;
522 
523  Info<< "\nChecking geometry..." << endl;
524 
525  // Get a small relative length from the bounding box
526  const boundBox& globalBb = mesh.bounds();
527 
528  Info<< " Overall domain bounding box "
529  << globalBb.min() << " " << globalBb.max() << endl;
530 
531  if (allGeometry)
532  {
533  Info<< " Patch bounding boxes" << endl;
534 
535  const polyBoundaryMesh& patches = mesh.boundaryMesh();
536 
538  {
539  const polyPatch& pp = patches[patchi];
540 
541  if (!isA<processorPolyPatch>(pp))
542  {
543  Info<< " " << setw(20) << pp.name();
544 
545  const pointField& pts = pp.points();
546  const labelList& mp = pp.meshPoints();
547 
548  if (returnReduce(mp.size(), sumOp<label>()) > 0)
549  {
551  forAll(mp, i)
552  {
553  bb.min() = min(bb.min(), pts[mp[i]]);
554  bb.max() = max(bb.max(), pts[mp[i]]);
555  }
556  reduce(bb.min(), minOp<vector>());
557  reduce(bb.max(), maxOp<vector>());
558  Info<< ' ' << bb;
559  }
560  }
561  Info<< endl;
562  }
563  }
564 
565  // Min length
566  scalar minDistSqr = magSqr(1e-6 * globalBb.span());
567 
568  // Geometric directions
569  const Vector<label> validDirs = (mesh.geometricD() + Vector<label>::one)/2;
570  Info<< " Mesh has " << mesh.nGeometricD()
571  << " geometric (non-empty/wedge) directions " << validDirs << endl;
572 
573  // Solution directions
574  const Vector<label> solDirs = (mesh.solutionD() + Vector<label>::one)/2;
575  Info<< " Mesh has " << mesh.nSolutionD()
576  << " solution (non-empty) directions " << solDirs << endl;
577 
578  if (mesh.nGeometricD() < 3)
579  {
580  pointSet nonAlignedPoints(mesh, "nonAlignedEdges", mesh.nPoints()/100);
581 
582  if
583  (
584  (
585  validDirs != solDirs
586  && checkWedges(mesh, true, validDirs, &nonAlignedPoints)
587  )
588  || (
589  validDirs == solDirs
591  (
592  mesh,
593  true,
594  validDirs,
595  &nonAlignedPoints
596  )
597  )
598  )
599  {
600  noFailedChecks++;
601  label nNonAligned = returnReduce
602  (
603  nonAlignedPoints.size(),
604  sumOp<label>()
605  );
606 
607  if (nNonAligned > 0)
608  {
609  if (setWriter.valid())
610  {
611  Info<< " <<Writing " << nNonAligned
612  << " points on non-aligned edges to set "
613  << nonAlignedPoints.name() << endl;
614  nonAlignedPoints.instance() = mesh.pointsInstance();
615  nonAlignedPoints.write();
616  meshCheck::mergeAndWrite(setWriter, nonAlignedPoints);
617  }
618  }
619  }
620  }
621 
622  if (meshCheck::checkClosedBoundary(mesh, true)) noFailedChecks++;
623 
624  {
625  cellSet cells(mesh, "nonClosedCells", mesh.nCells()/100+1);
626  cellSet aspectCells(mesh, "highAspectRatioCells", mesh.nCells()/100+1);
627  if
628  (
630  (
631  mesh,
632  closedThreshold,
633  aspectThreshold,
634  true,
635  &cells,
636  &aspectCells,
637  mesh.geometricD()
638  )
639  )
640  {
641  noFailedChecks++;
642 
643  label nNonClosed = returnReduce(cells.size(), sumOp<label>());
644 
645  if (nNonClosed > 0)
646  {
647  if (setWriter.valid())
648  {
649  Info<< " <<Writing " << nNonClosed
650  << " non closed cells to set " << cells.name() << endl;
651  cells.instance() = mesh.pointsInstance();
652  cells.write();
653  if (surfWriter.valid())
654  {
655  meshCheck::mergeAndWrite(surfWriter(), cells);
656  }
657  }
658  }
659  }
660 
661  label nHighAspect = returnReduce(aspectCells.size(), sumOp<label>());
662 
663  if (nHighAspect > 0)
664  {
665  if (setWriter.valid())
666  {
667  Info<< " <<Writing " << nHighAspect
668  << " cells with high aspect ratio to set "
669  << aspectCells.name() << endl;
670  aspectCells.instance() = mesh.pointsInstance();
671  aspectCells.write();
672  if (surfWriter.valid())
673  {
674  meshCheck::mergeAndWrite(surfWriter(), aspectCells);
675  }
676  }
677  }
678  }
679 
680  {
681  faceSet faces(mesh, "zeroAreaFaces", mesh.nFaces()/100+1);
682  if (meshCheck::checkFaceAreas(mesh, true, &faces))
683  {
684  noFailedChecks++;
685 
686  label nFaces = returnReduce(faces.size(), sumOp<label>());
687 
688  if (nFaces > 0)
689  {
690  if (setWriter.valid())
691  {
692  Info<< " <<Writing " << nFaces
693  << " zero area faces to set " << faces.name() << endl;
694  faces.instance() = mesh.pointsInstance();
695  faces.write();
696  if (surfWriter.valid())
697  {
698  meshCheck::mergeAndWrite(surfWriter(), faces);
699  }
700  }
701  }
702  }
703  }
704 
705  {
706  cellSet cells(mesh, "zeroVolumeCells", mesh.nCells()/100+1);
707  if (meshCheck::checkCellVolumes(mesh, true, &cells))
708  {
709  noFailedChecks++;
710 
711  label nCells = returnReduce(cells.size(), sumOp<label>());
712 
713  if (nCells > 0)
714  {
715  if (setWriter.valid())
716  {
717  Info<< " <<Writing " << nCells
718  << " zero volume cells to set " << cells.name() << endl;
719  cells.instance() = mesh.pointsInstance();
720  cells.write();
721  if (surfWriter.valid())
722  {
723  meshCheck::mergeAndWrite(surfWriter(), cells);
724  }
725  }
726  }
727  }
728  }
729 
730  {
731  faceSet faces(mesh, "nonOrthoFaces", mesh.nFaces()/100+1);
732  if
733  (
735  (
736  mesh,
737  nonOrthThreshold,
738  true,
739  &faces
740  )
741  )
742  {
743  noFailedChecks++;
744  }
745 
746  label nFaces = returnReduce(faces.size(), sumOp<label>());
747 
748  if (nFaces > 0)
749  {
750  if (setWriter.valid())
751  {
752  Info<< " <<Writing " << nFaces
753  << " non-orthogonal faces to set " << faces.name() << endl;
754  faces.instance() = mesh.pointsInstance();
755  faces.write();
756  if (surfWriter.valid())
757  {
758  meshCheck::mergeAndWrite(surfWriter(), faces);
759  }
760  }
761  }
762  }
763 
764  {
765  faceSet faces(mesh, "wrongOrientedFaces", mesh.nFaces()/100 + 1);
766  if (meshCheck::checkFacePyramids(mesh, true, -small, &faces))
767  {
768  noFailedChecks++;
769 
770  label nFaces = returnReduce(faces.size(), sumOp<label>());
771 
772  if (nFaces > 0)
773  {
774  if (setWriter.valid())
775  {
776  Info<< " <<Writing " << nFaces
777  << " faces with incorrect orientation to set "
778  << faces.name() << endl;
779  faces.instance() = mesh.pointsInstance();
780  faces.write();
781  if (surfWriter.valid())
782  {
783  meshCheck::mergeAndWrite(surfWriter(), faces);
784  }
785  }
786  }
787  }
788  }
789 
790  {
791  faceSet faces(mesh, "skewFaces", mesh.nFaces()/100+1);
792  if
793  (
795  (
796  mesh,
797  skewThreshold,
798  true,
799  &faces
800  )
801  )
802  {
803  noFailedChecks++;
804 
805  label nFaces = returnReduce(faces.size(), sumOp<label>());
806 
807  if (nFaces > 0)
808  {
809  if (setWriter.valid())
810  {
811  Info<< " <<Writing " << nFaces
812  << " skew faces to set " << faces.name() << endl;
813  faces.instance() = mesh.pointsInstance();
814  faces.write();
815  if (surfWriter.valid())
816  {
817  meshCheck::mergeAndWrite(surfWriter(), faces);
818  }
819  }
820  }
821  }
822  }
823 
824  {
825  faceSet faces(mesh, "coupledFaces", mesh.nFaces()/100 + 1);
826  if (checkCoupledPoints(mesh, true, &faces))
827  {
828  noFailedChecks++;
829 
830  label nFaces = returnReduce(faces.size(), sumOp<label>());
831 
832  if (nFaces > 0)
833  {
834  if (setWriter.valid())
835  {
836  Info<< " <<Writing " << nFaces
837  << " faces with incorrectly matched 0th "
838  "(or consecutive) vertex to set "
839  << faces.name() << endl;
840  faces.instance() = mesh.pointsInstance();
841  faces.write();
842  if (surfWriter.valid())
843  {
844  meshCheck::mergeAndWrite(surfWriter(), faces);
845  }
846  }
847  }
848  }
849  }
850 
851  if (allGeometry)
852  {
853  faceSet faces(mesh, "lowQualityTetFaces", mesh.nFaces()/100+1);
854  if
855  (
857  (
858  mesh,
859  polyMeshTetDecomposition::minTetQuality,
860  true,
861  &faces
862  )
863  )
864  {
865  noFailedChecks++;
866 
867  label nFaces = returnReduce(faces.size(), sumOp<label>());
868 
869  if (nFaces > 0)
870  {
871  if (setWriter.valid())
872  {
873  Info<< " <<Writing " << nFaces
874  << " faces with low quality or negative volume "
875  << "decomposition tets to set " << faces.name() << endl;
876  faces.instance() = mesh.pointsInstance();
877  faces.write();
878  if (surfWriter.valid())
879  {
880  meshCheck::mergeAndWrite(surfWriter(), faces);
881  }
882  }
883  }
884  }
885  }
886 
887  if (allGeometry)
888  {
889  // Note use of nPoints since don't want edge construction.
890  pointSet points(mesh, "shortEdges", mesh.nPoints()/1000 + 1);
891  if (meshCheck::checkEdgeLength(mesh, true, minDistSqr, &points))
892  {
893  // noFailedChecks++;
894 
896 
897  if (nPoints > 0)
898  {
899  if (setWriter.valid())
900  {
901  Info<< " <<Writing " << nPoints
902  << " points on short edges to set " << points.name()
903  << endl;
904  points.instance() = mesh.pointsInstance();
905  points.write();
907  }
908  }
909  }
910 
911  label nEdgeClose = returnReduce(points.size(), sumOp<label>());
912 
913  if (meshCheck::checkPointNearness(mesh, false, minDistSqr, &points))
914  {
915  // noFailedChecks++;
916 
918 
919  if (nPoints > nEdgeClose)
920  {
921  if (setWriter.valid())
922  {
923  pointSet nearPoints(mesh, "nearPoints", points);
924  Info<< " <<Writing " << nPoints
925  << " near (closer than " << Foam::sqrt(minDistSqr)
926  << " apart) points to set " << nearPoints.name()
927  << endl;
928  nearPoints.instance() = mesh.pointsInstance();
929  nearPoints.write();
930  meshCheck::mergeAndWrite(setWriter, nearPoints);
931  }
932  }
933  }
934  }
935 
936  if (allGeometry)
937  {
938  faceSet faces(mesh, "concaveFaces", mesh.nFaces()/100 + 1);
939  if (meshCheck::checkFaceAngles(mesh, true, degToRad(10), &faces))
940  {
941  // noFailedChecks++;
942 
943  label nFaces = returnReduce(faces.size(), sumOp<label>());
944 
945  if (nFaces > 0)
946  {
947  if (setWriter.valid())
948  {
949  Info<< " <<Writing " << nFaces
950  << " faces with concave angles to set " << faces.name()
951  << endl;
952  faces.instance() = mesh.pointsInstance();
953  faces.write();
954  if (surfWriter.valid())
955  {
956  meshCheck::mergeAndWrite(surfWriter(), faces);
957  }
958  }
959  }
960  }
961  }
962 
963  if (allGeometry)
964  {
965  faceSet faces(mesh, "warpedFaces", mesh.nFaces()/100 + 1);
966  if (meshCheck::checkFaceFlatness(mesh, true, 0.8, &faces))
967  {
968  // noFailedChecks++;
969 
970  label nFaces = returnReduce(faces.size(), sumOp<label>());
971 
972  if (nFaces > 0)
973  {
974  if (setWriter.valid())
975  {
976  Info<< " <<Writing " << nFaces
977  << " warped faces to set " << faces.name() << endl;
978  faces.instance() = mesh.pointsInstance();
979  faces.write();
980  if (surfWriter.valid())
981  {
982  meshCheck::mergeAndWrite(surfWriter(), faces);
983  }
984  }
985  }
986  }
987  }
988 
989  if (allGeometry)
990  {
991  cellSet cells(mesh, "underdeterminedCells", mesh.nCells()/100);
992  if (meshCheck::checkCellDeterminant(mesh, true, &cells))
993  {
994  noFailedChecks++;
995 
996  if (setWriter.valid())
997  {
998  label nCells = returnReduce(cells.size(), sumOp<label>());
999 
1000  Info<< " <<Writing " << nCells
1001  << " under-determined cells to set " << cells.name()
1002  << endl;
1003  cells.instance() = mesh.pointsInstance();
1004  cells.write();
1005  if (surfWriter.valid())
1006  {
1007  meshCheck::mergeAndWrite(surfWriter(), cells);
1008  }
1009  }
1010  }
1011  }
1012 
1013  if (allGeometry)
1014  {
1015  cellSet cells(mesh, "concaveCells", mesh.nCells()/100);
1016  if (meshCheck::checkConcaveCells(mesh, planarCosAngle, true, &cells))
1017  {
1018  noFailedChecks++;
1019 
1020  if (setWriter.valid())
1021  {
1022  label nCells = returnReduce(cells.size(), sumOp<label>());
1023 
1024  Info<< " <<Writing " << nCells
1025  << " concave cells to set " << cells.name() << endl;
1026  cells.instance() = mesh.pointsInstance();
1027  cells.write();
1028  if (surfWriter.valid())
1029  {
1030  meshCheck::mergeAndWrite(surfWriter(), cells);
1031  }
1032  }
1033  }
1034  }
1035 
1036  if (allGeometry)
1037  {
1038  faceSet faces(mesh, "lowWeightFaces", mesh.nFaces()/100);
1039  if (meshCheck::checkFaceWeight(mesh, true, 0.05, &faces))
1040  {
1041  noFailedChecks++;
1042 
1043  if (setWriter.valid())
1044  {
1045  label nFaces = returnReduce(faces.size(), sumOp<label>());
1046 
1047  Info<< " <<Writing " << nFaces
1048  << " faces with low interpolation weights to set "
1049  << faces.name() << endl;
1050  faces.instance() = mesh.pointsInstance();
1051  faces.write();
1052  if (surfWriter.valid())
1053  {
1054  meshCheck::mergeAndWrite(surfWriter(), faces);
1055  }
1056  }
1057  }
1058  }
1059 
1060  if (allGeometry)
1061  {
1062  faceSet faces(mesh, "lowVolRatioFaces", mesh.nFaces()/100);
1063  if (meshCheck::checkVolRatio(mesh, true, 0.01, &faces))
1064  {
1065  noFailedChecks++;
1066 
1067  if (setWriter.valid())
1068  {
1069  label nFaces = returnReduce(faces.size(), sumOp<label>());
1070 
1071  Info<< " <<Writing " << nFaces
1072  << " faces with low volume ratio cells to set "
1073  << faces.name() << endl;
1074  faces.instance() = mesh.pointsInstance();
1075  faces.write();
1076  if (surfWriter.valid())
1077  {
1078  meshCheck::mergeAndWrite(surfWriter(), faces);
1079  }
1080  }
1081  }
1082  }
1083 
1084  if (allGeometry)
1085  {
1086  const fileName outputPath =
1087  mesh.time().globalPath()
1088  /functionObjects::writeFile::outputPrefix
1089  /(mesh.name() != polyMesh::defaultRegion ? mesh.name() : word())
1090  /"checkMesh"
1091  /mesh.time().name();
1092 
1093  const polyBoundaryMesh& patches = mesh.boundaryMesh();
1094 
1095  // Compute coverage for all orig patches
1096  PtrList<scalarField> patchCoverage(patches.size());
1097  forAll(patches, nccPatchi)
1098  {
1099  if (isA<nonConformalCyclicPolyPatch>(patches[nccPatchi]))
1100  {
1101  const nonConformalCyclicPolyPatch& nccPp =
1102  refCast<const nonConformalCyclicPolyPatch>
1103  (patches[nccPatchi]);
1104 
1105  if (nccPp.owner())
1106  {
1107  const polyPatch& origPp = nccPp.origPatch();
1108  const polyPatch& nbrOrigPp = nccPp.nbrPatch().origPatch();
1109 
1111  nccPp.intersection();
1112 
1113  if (!patchCoverage.set(origPp.index()))
1114  {
1115  patchCoverage.set
1116  (
1117  origPp.index(),
1118  scalarField(origPp.size(), 0)
1119  );
1120  }
1121 
1122  patchCoverage[origPp.index()] +=
1123  intersection.srcCoverage();
1124 
1125  if (!patchCoverage.set(nbrOrigPp.index()))
1126  {
1127  patchCoverage.set
1128  (
1129  nbrOrigPp.index(),
1130  scalarField(nbrOrigPp.size(), 0)
1131  );
1132  }
1133 
1134  patchCoverage[nbrOrigPp.index()] +=
1135  intersection.tgtCoverage();
1136  }
1137  }
1138  }
1139 
1140  // Write out to surface files
1142  {
1143  if (patchCoverage.set(patchi))
1144  {
1145  const polyPatch& patch = patches[patchi];
1146 
1147  // Collect the patch geometry
1148  labelList pointToGlobal;
1149  labelList uniqueMeshPointLabels;
1151  autoPtr<globalIndex> globalFaces;
1152  faceList mergedFaces;
1153  pointField mergedPoints;
1155  (
1156  mesh,
1157  patch.localFaces(),
1158  patch.meshPoints(),
1159  patch.meshPointMap(),
1160  pointToGlobal,
1161  uniqueMeshPointLabels,
1162  globalPoints,
1163  globalFaces,
1164  mergedFaces,
1165  mergedPoints
1166  );
1167 
1168  // Collect the patch coverage
1169  scalarField mergedCoverage;
1170  globalFaces().gather
1171  (
1172  UPstream::worldComm,
1173  labelList(UPstream::procID(UPstream::worldComm)),
1174  patchCoverage[patchi],
1175  mergedCoverage
1176  );
1177 
1178  // Write the surface
1179  if (Pstream::master())
1180  {
1182  (
1183  mesh.time().writeFormat(),
1184  mesh.time().writeCompression()
1185  ).write
1186  (
1187  outputPath,
1188  patch.name() + "_coverage",
1189  mergedPoints,
1190  mergedFaces,
1191  false,
1192  "coverage",
1193  mergedCoverage
1194  );
1195  }
1196  }
1197  }
1198  }
1199 
1200  return noFailedChecks;
1201 }
1202 
1203 
1204 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
bool insert(const Key &key)
Insert a new entry.
Definition: HashSet.H:109
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
void resize(const label newSize)
Resize the hash table for efficiency.
Definition: HashTable.C:506
fileName & instance() const
Return the instance directory, constant, system, <time> etc.
Definition: IOobject.C:355
const word & name() const
Return name.
Definition: IOobject.H:310
void transfer(List< T > &)
Transfer the contents of the argument List into this list.
Definition: List.C:342
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
void setSize(const label)
Reset size of List.
Definition: List.C:281
static void gatherAndMerge(const scalar mergeDist, const PrimitivePatch< FaceList, PointField > &p, Field< typename PrimitivePatch< FaceList, PointField >::PointType > &mergedPoints, List< typename PrimitivePatch< FaceList, PointField >::FaceType > &mergedFaces, labelList &pointMergeMap)
Gather points and faces onto master and merge into single patch.
const Map< label > & meshPointMap() const
Mesh point map. Given the global point index find its.
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
const Field< PointType > & points() const
Return reference to global points.
const List< FaceType > & localFaces() const
Return patch faces addressing into local point list.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:62
fileName globalPath() const
Return the global path.
Definition: TimePaths.H:132
IOstream::streamFormat writeFormat() const
Default write format.
Definition: Time.H:287
IOstream::compressionType writeCompression() const
Default write compression.
Definition: Time.H:299
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: autoPtr.H:51
bool valid() const
Return true if the autoPtr valid (ie, the pointer is set)
Definition: autoPtrI.H:83
A bounding box defined in terms of the points at its extremities.
Definition: boundBox.H:59
const point & min() const
Minimum point defining the bounding box.
Definition: boundBoxI.H:60
const point & max() const
Maximum point defining the bounding box.
Definition: boundBoxI.H:66
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:90
A collection of cell labels.
Definition: cellSet.H:51
The coupledPolyPatch is an abstract base class for patches that couple regions of the computational d...
virtual bool owner() const =0
Does this side own the patch ?
static scalarField calcFaceTol(const UList< face > &faces, const pointField &points, const pointField &faceCentres)
Calculate typical tolerance per face. Is currently max distance.
const word & name() const
Return const reference to name.
Set of directions for each cell in the mesh. Either uniform and size=1 or one set of directions per c...
Definition: directions.H:67
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
Definition: ops.H:70
A list of face labels.
Definition: faceSet.H:51
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:76
A class for handling file names.
Definition: fileName.H:82
Calculates points shared by more than two processor patches or cyclic patches.
Definition: globalPoints.H:101
Foam::intersection.
Definition: intersection.H:50
Non-conformal cyclic poly patch. As nonConformalCoupledPolyPatch, but the neighbouring patch is local...
virtual bool owner() const
Inherit the cyclic owner method.
const patchToPatches::intersection & intersection() const
Access the intersection engine.
const nonConformalCyclicPolyPatch & nbrPatch() const
Neighbour patch.
const polyPatch & origPatch() const
Original patch.
const Time & time() const
Return time.
label index() const
Return the index of this patch in the boundaryMesh.
const word & name() const
Return name.
Class to generate patchToPatch coupling geometry. A full geometric intersection is done between a fac...
A set of point labels.
Definition: pointSet.H:51
Foam::polyBoundaryMesh.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
label nGeometricD() const
Return the number of valid geometric dimensions in the mesh.
Definition: polyMesh.C:1000
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1326
label nSolutionD() const
Return the number of valid solved-for dimensions in the mesh.
Definition: polyMesh.C:1017
const fileName & pointsInstance() const
Return the current instance directory for points.
Definition: polyMesh.C:965
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:404
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1313
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:410
const Vector< label > & solutionD() const
Return the vector of solved-for directions in mesh.
Definition: polyMesh.C:1006
const Vector< label > & geometricD() const
Return the vector of geometric directions in mesh.
Definition: polyMesh.C:989
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:70
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:280
const vectorField::subField faceCentres() const
Return face centres.
Definition: polyPatch.C:276
label nCells() const
label nPoints() const
label nInternalFaces() const
label nFaces() const
virtual bool write(const bool write=true) const
Write using setting from DB.
Base class for writing coordinate sets with data.
Definition: setWriter.H:64
Default transformation behaviour for position.
A surfaceWriter for VTK legacy format with support for writing ASCII or binary.
virtual void write(const fileName &outputDir, const fileName &surfaceName, const pointField &points, const faceList &faces, const wordList &fieldNames, const bool writePointValues #define FieldTypeValuesConstArg(Type, nullArg)) const
Write fields for a single surface to file.
Wedge front and back plane patch.
const vector & centreNormal() const
Return plane normal between the wedge boundaries.
scalar cosAngle() const
Return the cosine of the wedge angle.
const vector & n() const
Return the normal to the patch.
const vector & axis() const
Return axis of the wedge.
A class for handling words, derived from string.
Definition: word.H:62
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
label patchi
const pointField & points
label nPoints
const cellShapeList & cells
const fvPatchList & patches
Tools for checking the mesh.
Functions for checking mesh topology and geometry.
const dimensionedScalar mp
Proton mass.
const dimensionedScalar e
Elementary charge.
bool checkFaceWeight(const polyMesh &mesh, const bool report, const scalar minWeight=0.05, labelHashSet *setPtr=nullptr)
Check for face weights.
bool checkFacePyramids(const bool report, const scalar minPyrVol, const polyMesh &, const vectorField &cellCentres, const pointField &p, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *)
Check face pyramid volumes.
bool checkEdgeAlignment(const polyMesh &mesh, const bool report, const Vector< label > &directions, labelHashSet *setPtr)
Check edge alignment for 1D/2D cases.
bool checkEdgeLength(const primitiveMesh &mesh, const bool report, const scalar minLenSqr, labelHashSet *setPtr=nullptr)
Check edge length.
bool checkCoupledPoints(const polyMesh &, const bool report, labelHashSet *)
Check 0th vertex on coupled faces.
bool checkConcaveCells(const primitiveMesh &mesh, const scalar planarCosAngle, const bool report=false, labelHashSet *setPtr=nullptr)
Check for concave cells by the planes of faces.
bool checkWedges(const polyMesh &, const bool report, const Vector< label > &, labelHashSet *)
Check wedge orientation.
Definition: checkGeometry.C:87
bool checkPointNearness(const primitiveMesh &mesh, const bool report, const scalar reportDistSqr, labelHashSet *setPtr=nullptr)
Check for point-point-nearness,.
bool checkCellDeterminant(const polyMesh &mesh, const bool report, labelHashSet *setPtr)
bool checkFaceAngles(const bool report, const scalar maxConcave, const polyMesh &mesh, const vectorField &faceAreas, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
Check convexity of angles in a face. See primitiveMesh for explanation.
bool checkVolRatio(const polyMesh &mesh, const bool report, const scalar minRatio=0.01, labelHashSet *setPtr=nullptr)
Check for neighbouring cell volumes.
label findOppositeWedge(const polyMesh &, const wedgePolyPatch &)
Find wedge with opposite orientation. Note: does not actually check.
Definition: checkGeometry.C:47
bool checkClosedCells(const primitiveMesh &mesh, const scalar closedThreshold, const scalar aspectThreshold, const bool report=false, labelHashSet *setPtr=nullptr, labelHashSet *highAspectSetPtr=nullptr, const Vector< label > &solutionD=Vector< label >::one)
Check cells for closedness.
void mergeAndWrite(const polyMesh &mesh, const surfaceWriter &setWriter, const word &name, const indirectPrimitivePatch setPatch, const fileName &outputDir)
Generate merged surface on master and write. Needs input patch.
bool checkClosedBoundary(const primitiveMesh &mesh, const scalar closedThreshold, const bool report=false)
Check boundary for closedness.
bool checkFaceTets(const bool report, const scalar minPyrVol, const polyMesh &, const vectorField &cellCentres, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, const List< labelPair > &baffles, labelHashSet *)
Check face tetrahedra volumes.
bool checkFaceFlatness(const bool report, const scalar minFlatness, const polyMesh &, const vectorField &faceAreas, const vectorField &faceCentres, const pointField &p, const labelList &checkFaces, labelHashSet *setPtr)
Check for face areas v.s. sum of face-triangle (from face-centre.
bool checkFaceOrthogonality(const polyMesh &mesh, const scalar nonOrthThreshold, const bool report=false, labelHashSet *setPtr=nullptr)
Check face orthogonality.
bool checkCellVolumes(const primitiveMesh &mesh, const bool report=false, labelHashSet *setPtr=nullptr)
Check for negative cell volumes.
label checkGeometry(const polyMesh &mesh, const bool allGeometry, const scalar nonOrthThreshold, const scalar skewThreshold, const autoPtr< surfaceWriter > &, const autoPtr< setWriter > &)
Check the geometry.
bool checkFaceAreas(const primitiveMesh &mesh, const bool report=false, labelHashSet *setPtr=nullptr)
Check for negative face areas.
bool checkFaceSkewness(const polyMesh &mesh, const scalar skewThreshold, const bool report=false, labelHashSet *setPtr=nullptr)
Check face skewness.
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
scalar radToDeg(const scalar rad)
Convert radians to degrees.
scalar degToRad(const scalar deg)
Convert degrees to radians.
List< label > labelList
A List of labels.
Definition: labelList.H:56
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
errorManip< error > abort(error &err)
Definition: errorManip.H:131
messageStream Info
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
dimensionedScalar sqrt(const dimensionedScalar &ds)
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
error FatalError
dimensioned< scalar > magSqr(const dimensioned< Type > &)
uint8_t direction
Definition: direction.H:45
dimensionedScalar acos(const dimensionedScalar &ds)
labelList f(nPoints)
volScalarField & p