checkTopology.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2017-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "checkTopology.H"
30 #include "polyMesh.H"
31 #include "Time.H"
32 #include "regionSplit.H"
33 #include "cellSet.H"
34 #include "faceSet.H"
35 #include "pointSet.H"
36 #include "IOmanip.H"
37 #include "emptyPolyPatch.H"
38 #include "processorPolyPatch.H"
39 #include "foamVtkLineWriter.H"
40 #include "vtkCoordSetWriter.H"
41 #include "vtkSurfaceWriter.H"
42 #include "checkTools.H"
43 #include "treeBoundBox.H"
44 #include "syncTools.H"
45 
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
47 
48 // NEWER CODE
49 // ~~~~~~~~~~
50 // Return true on error
51 template<class PatchType>
53 (
54  const bool allGeometry,
55  const std::string& name,
56  const polyMesh& mesh,
57  const PatchType& pp,
58  const labelUList& meshEdges,
61 )
62 {
63  if (badEdgesPtr)
64  {
65  badEdgesPtr->clear();
66  }
67 
68  typedef typename PatchType::surfaceTopo TopoType;
69 
70  bool foundError = false;
71 
72  const label globalSize = returnReduce(pp.size(), sumOp<label>());
73 
74  Info<< " "
75  << setw(20) << name.c_str()
76  << setw(9) << globalSize
77  << setw(9) << returnReduce(pp.nPoints(), sumOp<label>());
78 
79  if (globalSize == 0)
80  {
81  Info<< setw(34) << "ok (empty)";
82  }
83  else if (UPstream::parRun())
84  {
85  // Parallel - use mesh edges
86  // - no check for point-pinch
87  // - no check for consistent orientation (if that is posible to
88  // check?)
89 
90  // Count number of edge/face connections (globally)
91  labelList nEdgeConnections(mesh.nEdges(), Zero);
92 
93  const labelListList& edgeFaces = pp.edgeFaces();
94 
95  forAll(edgeFaces, edgei)
96  {
97  nEdgeConnections[meshEdges[edgei]] = edgeFaces[edgei].size();
98  }
99 
100  // Synchronise across coupled edges
101  syncTools::syncEdgeList
102  (
103  mesh,
104  nEdgeConnections,
105  plusEqOp<label>(),
106  label(0) // null value
107  );
108 
109  label labelTyp = TopoType::MANIFOLD;
110  forAll(meshEdges, edgei)
111  {
112  const label meshEdgei = meshEdges[edgei];
113  const label numNbrs = nEdgeConnections[meshEdgei];
114  if (numNbrs == 1)
115  {
116  //if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
117  labelTyp = max(labelTyp, TopoType::OPEN);
118  }
119  else if (numNbrs == 0 || numNbrs > 2)
120  {
121  if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
122  if (badEdgesPtr) badEdgesPtr->insert(edgei);
123  labelTyp = max(labelTyp, TopoType::ILLEGAL);
124  }
125  }
126  reduce(labelTyp, maxOp<label>());
127 
128  if (labelTyp == TopoType::MANIFOLD)
129  {
130  Info<< setw(34) << "ok (closed singly connected)";
131  }
132  else if (labelTyp == TopoType::OPEN)
133  {
134  Info<< setw(34) << "ok (non-closed singly connected)";
135  }
136  else
137  {
138  Info<< setw(34) << "multiply connected (shared edge)";
139  }
140 
141  foundError = (labelTyp == TopoType::ILLEGAL);
142  }
143  else
144  {
145  const TopoType pTyp = pp.surfaceType(badEdgesPtr);
146 
147  if (pTyp == TopoType::MANIFOLD)
148  {
149  if (pp.checkPointManifold(true, pointSetPtr))
150  {
151  Info<< setw(34) << "multiply connected (shared point)";
152  }
153  else
154  {
155  Info<< setw(34) << "ok (closed singly connected)";
156  }
157 
158  if (pointSetPtr)
159  {
160  // Add points on non-manifold edges to make set complete
161  pp.checkTopology(false, pointSetPtr);
162  }
163  }
164  else
165  {
166  if (pointSetPtr)
167  {
168  pp.checkTopology(false, pointSetPtr);
169  }
170 
171  if (pTyp == TopoType::OPEN)
172  {
173  Info<< setw(34) << "ok (non-closed singly connected)";
174  }
175  else
176  {
177  Info<< setw(34) << "multiply connected (shared edge)";
178  }
179  }
180 
181  foundError = (pTyp == TopoType::ILLEGAL);
182  }
183 
184  if (allGeometry)
185  {
186  boundBox bb(pp.box());
187  bb.reduce();
188 
189  if (bb.good())
190  {
191  Info<< ' ' << bb;
192  }
193  }
194 
195  return foundError;
196 }
197 
198 
199 // OLDER CODE
200 // ~~~~~~~~~~
201 // Return true on error
202 template<class PatchType>
203 bool Foam::checkPatch
204 (
205  const bool allGeometry,
206  const std::string& name,
207  const polyMesh& mesh,
208  const PatchType& pp,
209  const labelUList& meshFaces,
210  const labelUList& meshEdges,
213 )
214 {
215  if (badEdgesPtr)
216  {
217  badEdgesPtr->clear();
218  }
219 
220  typedef typename PatchType::surfaceTopo TopoType;
221 
222  bool foundError = false;
223 
224  const label globalSize = returnReduce(pp.size(), sumOp<label>());
225 
226  Info<< " "
227  << setw(20) << name.c_str()
228  << setw(9) << globalSize
229  << setw(9) << returnReduce(pp.nPoints(), sumOp<label>());
230 
231  if (globalSize == 0)
232  {
233  Info<< setw(34) << "ok (empty)";
234  }
235  else if (UPstream::parRun())
236  {
237  // Parallel - use mesh edges
238  // - no check for point-pinch
239  // - no check for consistent orientation (if that is posible to
240  // check?)
241 
242  // OLDER CODE
243  // ~~~~~~~~~~
244  // Synchronise connected faces using global face numbering
245  //
246  // (see addPatchCellLayer::globalEdgeFaces)
247  // From mesh edge to global face labels. Non-empty sublists only for
248  // pp edges.
249  labelListList globalEdgeFaces(mesh.nEdges());
250 
251  const labelListList& edgeFaces = pp.edgeFaces();
252 
253  // Global numbering
254  const globalIndex globalFaces(mesh.nFaces());
255 
256  forAll(edgeFaces, edgei)
257  {
258  const label meshEdgei = meshEdges[edgei];
259  const labelList& eFaces = edgeFaces[edgei];
260 
261  // Store face and processor as unique tag.
262  labelList& globalEFaces = globalEdgeFaces[meshEdgei];
263  globalEFaces.resize(eFaces.size());
264  forAll(eFaces, i)
265  {
266  globalEFaces[i] = globalFaces.toGlobal(meshFaces[eFaces[i]]);
267  }
268  //Pout<< "At edge:" << meshEdgei
269  // << " ctr:" << mesh.edges()[meshEdgei].centre(mesh.points())
270  // << " have eFaces:" << globalEdgeFaces[meshEdgei]
271  // << endl;
272  }
273 
274  // Synchronise across coupled edges
275  syncTools::syncEdgeList
276  (
277  mesh,
278  globalEdgeFaces,
279  ListOps::uniqueEqOp<label>(),
280  labelList() // null value
281  );
282 
283  label labelTyp = TopoType::MANIFOLD;
284  forAll(meshEdges, edgei)
285  {
286  const label meshEdgei = meshEdges[edgei];
287  const labelList& globalEFaces = globalEdgeFaces[meshEdgei];
288  const label numNbrs = globalEFaces.size();
289  if (numNbrs == 1)
290  {
291  //if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
292  labelTyp = max(labelTyp, TopoType::OPEN);
293  }
294  else if (numNbrs == 0 || numNbrs > 2)
295  {
296  if (pointSetPtr) pointSetPtr->insert(mesh.edges()[meshEdgei]);
297  if (badEdgesPtr) badEdgesPtr->insert(edgei);
298  labelTyp = max(labelTyp, TopoType::ILLEGAL);
299  }
300  }
301  reduce(labelTyp, maxOp<label>());
302 
303  if (labelTyp == TopoType::MANIFOLD)
304  {
305  Info<< setw(34) << "ok (closed singly connected)";
306  }
307  else if (labelTyp == TopoType::OPEN)
308  {
309  Info<< setw(34) << "ok (non-closed singly connected)";
310  }
311  else
312  {
313  Info<< setw(34) << "multiply connected (shared edge)";
314  }
315 
316  foundError = (labelTyp == TopoType::ILLEGAL);
317  }
318  else
319  {
320  const TopoType pTyp = pp.surfaceType(badEdgesPtr);
321 
322  if (pTyp == TopoType::MANIFOLD)
323  {
324  if (pp.checkPointManifold(true, pointSetPtr))
325  {
326  Info<< setw(34) << "multiply connected (shared point)";
327  }
328  else
329  {
330  Info<< setw(34) << "ok (closed singly connected)";
331  }
332 
333  if (pointSetPtr)
334  {
335  // Add points on non-manifold edges to make set complete
336  pp.checkTopology(false, pointSetPtr);
337  }
338  }
339  else
340  {
341  if (pointSetPtr)
342  {
343  pp.checkTopology(false, pointSetPtr);
344  }
345 
346  if (pTyp == TopoType::OPEN)
347  {
348  Info<< setw(34) << "ok (non-closed singly connected)";
349  }
350  else
351  {
352  Info<< setw(34) << "multiply connected (shared edge)";
353  }
354  }
355 
356  foundError = (pTyp == TopoType::ILLEGAL);
357  }
358 
359  if (allGeometry)
360  {
361  boundBox bb(pp.box());
362  bb.reduce();
363 
364  if (bb.good())
365  {
366  Info<< ' ' << bb;
367  }
368  }
369 
370  return foundError;
371 }
372 
373 
374 template<class Zone>
375 Foam::label Foam::checkZones
376 (
377  const polyMesh& mesh,
378  const ZoneMesh<Zone, polyMesh>& zones,
379  topoSet& set
380 )
381 {
382  labelList zoneID(set.maxSize(mesh), -1);
383  for (const auto& zone : zones)
384  {
385  for (const label elem : zone)
386  {
387  if
388  (
389  zoneID[elem] != -1
390  && zoneID[elem] != zone.index()
391  )
392  {
393  set.insert(elem);
394  }
395  zoneID[elem] = zone.index();
396  }
397  }
398 
399  return returnReduce(set.size(), sumOp<label>());
400 }
401 
402 
403 Foam::label Foam::checkTopology
404 (
405  const polyMesh& mesh,
406  const bool allTopology,
407  const bool allGeometry,
408  autoPtr<surfaceWriter>& surfWriter,
409  autoPtr<coordSetWriter>& setWriter,
410  const bool writeBadEdges
411 )
412 {
413  label noFailedChecks = 0;
414 
415  Info<< "Checking topology..." << endl;
416 
417  // Check if the boundary definition is unique
418  mesh.boundaryMesh().checkDefinition(true);
419 
420  // Check that empty patches cover all sides of the mesh
421  {
422  label nEmpty = 0;
423  forAll(mesh.boundaryMesh(), patchi)
424  {
425  if (isA<emptyPolyPatch>(mesh.boundaryMesh()[patchi]))
426  {
427  nEmpty += mesh.boundaryMesh()[patchi].size();
428  }
429  }
430  reduce(nEmpty, sumOp<label>());
431  const label nCells = returnReduce(mesh.cells().size(), sumOp<label>());
432 
433  // These are actually warnings, not errors.
434  if (nCells && (nEmpty % nCells))
435  {
436  Info<< " ***Total number of faces on empty patches"
437  << " is not divisible by the number of cells in the mesh."
438  << " Hence this mesh is not 1D or 2D."
439  << endl;
440  }
441  }
442 
443  // Check if the boundary processor patches are correct
444  mesh.boundaryMesh().checkParallelSync(true);
445 
446  // Check names of zones are equal
447  mesh.cellZones().checkDefinition(true);
448  if (mesh.cellZones().checkParallelSync(true))
449  {
450  noFailedChecks++;
451  }
452  mesh.faceZones().checkDefinition(true);
453  if (mesh.faceZones().checkParallelSync(true))
454  {
455  noFailedChecks++;
456  }
457  mesh.pointZones().checkDefinition(true);
458  if (mesh.pointZones().checkParallelSync(true))
459  {
460  noFailedChecks++;
461  }
462 
463 
464  {
465  cellSet cells(mesh, "illegalCells", mesh.nCells()/100);
466 
467  forAll(mesh.cells(), celli)
468  {
469  const cell& cFaces = mesh.cells()[celli];
470 
471  if (cFaces.size() <= 3)
472  {
473  cells.insert(celli);
474  }
475  for (const label facei : cFaces)
476  {
477  if (facei < 0 || facei >= mesh.nFaces())
478  {
479  cells.insert(celli);
480  break;
481  }
482  }
483  }
484  const label nCells = returnReduce(cells.size(), sumOp<label>());
485 
486  if (nCells > 0)
487  {
488  Info<< " Illegal cells (less than 4 faces or out of range faces)"
489  << " found, number of cells: " << nCells << endl;
490  noFailedChecks++;
491 
492  Info<< " <<Writing " << nCells
493  << " illegal cells to set " << cells.name() << endl;
494  cells.instance() = mesh.pointsInstance();
495  cells.write();
496  if (surfWriter && surfWriter->enabled())
497  {
498  mergeAndWrite(*surfWriter, cells);
499  }
500  }
501  else
502  {
503  Info<< " Cell to face addressing OK." << endl;
504  }
505  }
506 
507 
508  {
509  pointSet points(mesh, "unusedPoints", mesh.nPoints()/100);
510  if (mesh.checkPoints(true, &points))
511  {
512  noFailedChecks++;
513 
514  const label nPoints = returnReduce(points.size(), sumOp<label>());
515 
516  Info<< " <<Writing " << nPoints
517  << " unused points to set " << points.name() << endl;
518  points.instance() = mesh.pointsInstance();
519  points.write();
520  if (setWriter && setWriter->enabled())
521  {
522  mergeAndWrite(*setWriter, points);
523  }
524  }
525  }
526 
527  {
528  faceSet faces(mesh, "upperTriangularFace", mesh.nFaces()/100);
529  if (mesh.checkUpperTriangular(true, &faces))
530  {
531  noFailedChecks++;
532  }
533 
534  const label nFaces = returnReduce(faces.size(), sumOp<label>());
535 
536  if (nFaces > 0)
537  {
538  Info<< " <<Writing " << nFaces
539  << " unordered faces to set " << faces.name() << endl;
540  faces.instance() = mesh.pointsInstance();
541  faces.write();
542  if (surfWriter && surfWriter->enabled())
543  {
544  mergeAndWrite(*surfWriter, faces);
545  }
546  }
547  }
548 
549  {
550  faceSet faces(mesh, "outOfRangeFaces", mesh.nFaces()/100);
551  if (mesh.checkFaceVertices(true, &faces))
552  {
553  noFailedChecks++;
554 
555  const label nFaces = returnReduce(faces.size(), sumOp<label>());
556 
557  Info<< " <<Writing " << nFaces
558  << " faces with out-of-range or duplicate vertices to set "
559  << faces.name() << endl;
560  faces.instance() = mesh.pointsInstance();
561  faces.write();
562  if (surfWriter && surfWriter->enabled())
563  {
564  mergeAndWrite(*surfWriter, faces);
565  }
566  }
567  }
568 
569  if (allTopology)
570  {
571  cellSet cells(mesh, "zipUpCells", mesh.nCells()/100);
572  if (mesh.checkCellsZipUp(true, &cells))
573  {
574  noFailedChecks++;
575 
576  const label nCells = returnReduce(cells.size(), sumOp<label>());
577 
578  Info<< " <<Writing " << nCells
579  << " cells with over used edges to set " << cells.name()
580  << endl;
581  cells.instance() = mesh.pointsInstance();
582  cells.write();
583  if (surfWriter && surfWriter->enabled())
584  {
585  mergeAndWrite(*surfWriter, cells);
586  }
587  }
588  }
589 
590  if (allTopology)
591  {
592  faceSet faces(mesh, "edgeFaces", mesh.nFaces()/100);
593  if (mesh.checkFaceFaces(true, &faces))
594  {
595  noFailedChecks++;
596  }
597 
598  const label nFaces = returnReduce(faces.size(), sumOp<label>());
599  if (nFaces > 0)
600  {
601  Info<< " <<Writing " << nFaces
602  << " faces with non-standard edge connectivity to set "
603  << faces.name() << endl;
604  faces.instance() = mesh.pointsInstance();
605  faces.write();
606  if (surfWriter && surfWriter->enabled())
607  {
608  mergeAndWrite(*surfWriter, faces);
609  }
610  }
611  }
612 
613  if (allTopology)
614  {
615  labelList nInternalFaces(mesh.nCells(), Zero);
616 
617  for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
618  {
619  nInternalFaces[mesh.faceOwner()[facei]]++;
620  nInternalFaces[mesh.faceNeighbour()[facei]]++;
621  }
622  const polyBoundaryMesh& patches = mesh.boundaryMesh();
623  forAll(patches, patchi)
624  {
625  if (patches[patchi].coupled())
626  {
627  const labelUList& owners = patches[patchi].faceCells();
628 
629  for (const label facei : owners)
630  {
631  nInternalFaces[facei]++;
632  }
633  }
634  }
635 
636  cellSet oneCells(mesh, "oneInternalFaceCells", mesh.nCells()/100);
637  cellSet twoCells(mesh, "twoInternalFacesCells", mesh.nCells()/100);
638 
639  forAll(nInternalFaces, celli)
640  {
641  if (nInternalFaces[celli] <= 1)
642  {
643  oneCells.insert(celli);
644  }
645  else if (nInternalFaces[celli] == 2)
646  {
647  twoCells.insert(celli);
648  }
649  }
650 
651  const label nOneCells = returnReduce(oneCells.size(), sumOp<label>());
652 
653  if (nOneCells > 0)
654  {
655  Info<< " <<Writing " << nOneCells
656  << " cells with zero or one non-boundary face to set "
657  << oneCells.name()
658  << endl;
659  oneCells.instance() = mesh.pointsInstance();
660  oneCells.write();
661  if (surfWriter && surfWriter->enabled())
662  {
663  mergeAndWrite(*surfWriter, oneCells);
664  }
665  }
666 
667  const label nTwoCells = returnReduce(twoCells.size(), sumOp<label>());
668 
669  if (nTwoCells > 0)
670  {
671  Info<< " <<Writing " << nTwoCells
672  << " cells with two non-boundary faces to set "
673  << twoCells.name()
674  << endl;
675  twoCells.instance() = mesh.pointsInstance();
676  twoCells.write();
677  if (surfWriter && surfWriter->enabled())
678  {
679  mergeAndWrite(*surfWriter, twoCells);
680  }
681  }
682  }
683 
684  {
685  regionSplit rs(mesh);
686 
687  if (rs.nRegions() <= 1)
688  {
689  Info<< " Number of regions: " << rs.nRegions() << " (OK)."
690  << endl;
691 
692  }
693  else
694  {
695  Info<< " *Number of regions: " << rs.nRegions() << endl;
696 
697  Info<< " The mesh has multiple regions which are not connected "
698  "by any face." << endl
699  << " <<Writing region information to "
700  << mesh.time().timeName()/"cellToRegion"
701  << endl;
702 
703  IOListRef<label>
704  (
705  IOobject
706  (
707  "cellToRegion",
708  mesh.time().timeName(),
709  mesh,
710  IOobject::NO_READ,
711  IOobject::NO_WRITE,
712  IOobject::NO_REGISTER
713  ),
714  rs
715  ).write();
716 
717 
718  // Points in multiple regions
719  pointSet points
720  (
721  mesh,
722  "multiRegionPoints",
723  mesh.nPoints()/1000
724  );
725 
726  // Is region disconnected
727  boolList regionDisconnected(rs.nRegions(), true);
728  if (allTopology)
729  {
730  // -1 : not assigned
731  // -2 : multiple regions
732  // >= 0 : single region
733  labelList pointToRegion(mesh.nPoints(), -1);
734 
735  for
736  (
737  label facei = mesh.nInternalFaces();
738  facei < mesh.nFaces();
739  ++facei
740  )
741  {
742  const label regioni = rs[mesh.faceOwner()[facei]];
743  const face& f = mesh.faces()[facei];
744  for (const label verti : f)
745  {
746  label& pRegion = pointToRegion[verti];
747  if (pRegion == -1)
748  {
749  pRegion = regioni;
750  }
751  else if (pRegion == -2)
752  {
753  // Already marked
754  regionDisconnected[regioni] = false;
755  }
756  else if (pRegion != regioni)
757  {
758  // Multiple regions
759  regionDisconnected[regioni] = false;
760  regionDisconnected[pRegion] = false;
761  pRegion = -2;
762  points.insert(verti);
763  }
764  }
765  }
766 
767  Pstream::listCombineReduce(regionDisconnected, andEqOp<bool>());
768  }
769 
770 
771 
772  // write cellSet for each region
773  PtrList<cellSet> cellRegions(rs.nRegions());
774  for (label i = 0; i < rs.nRegions(); i++)
775  {
776  cellRegions.set
777  (
778  i,
779  new cellSet
780  (
781  mesh,
782  "region" + Foam::name(i),
783  mesh.nCells()/100
784  )
785  );
786  }
787 
788  forAll(rs, i)
789  {
790  cellRegions[rs[i]].insert(i);
791  }
792 
793  for (label i = 0; i < rs.nRegions(); i++)
794  {
795  Info<< " <<Writing region " << i;
796  if (allTopology)
797  {
798  if (regionDisconnected[i])
799  {
800  Info<< " (fully disconnected)";
801  }
802  else
803  {
804  Info<< " (point connected)";
805  }
806  }
807  Info<< " with "
808  << returnReduce(cellRegions[i].size(), sumOp<scalar>())
809  << " cells to cellSet " << cellRegions[i].name() << endl;
810 
811  cellRegions[i].write();
812  }
813 
814  const label nPoints = returnReduce(points.size(), sumOp<label>());
815  if (nPoints)
816  {
817  Info<< " <<Writing " << nPoints
818  << " points that are in multiple regions to set "
819  << points.name() << endl;
820  points.write();
821  if (setWriter && setWriter->enabled())
822  {
823  mergeAndWrite(*setWriter, points);
824  }
825  }
826  }
827  }
828 
829  // Non-manifold points
830  pointSet nonManifoldPoints
831  (
832  mesh,
833  "nonManifoldPoints",
834  mesh.nPoints()/1000
835  );
836 
837  {
838  Info<< "\nChecking patch topology for multiply connected"
839  << " surfaces..." << endl;
840 
841  const polyBoundaryMesh& patches = mesh.boundaryMesh();
842 
843  Pout.setf(ios_base::left);
844 
845  Info<< " "
846  << setw(20) << "Patch"
847  << setw(9) << "Faces"
848  << setw(9) << "Points"
849  << " Surface topology";
850  if (allGeometry)
851  {
852  Info<< " Bounding box";
853  }
854  Info<< endl;
855 
856  for (const polyPatch& pp : patches)
857  {
858  if (!isA<processorPolyPatch>(pp))
859  {
860  checkPatch
861  (
862  allGeometry,
863  pp.name(),
864  mesh,
865  pp,
866  // identity(pp.size(), pp.start()),
867  pp.meshEdges(),
868  &nonManifoldPoints
869  );
870  Info<< endl;
871  }
872  }
873 
874  // All non-processor boundary patches
875  {
877  (
878  identity
879  (
880  (
881  patches.range(patches.nNonProcessor()-1).end_value()
882  - patches.start()
883  ),
884  patches.start()
885  )
886  );
887 
889  (
890  UIndirectList<face>(mesh.faces(), faceLabels),
891  mesh.points()
892  );
893 
894  // Non-manifold
895  labelHashSet badEdges(pp.nEdges()/20);
896 
897  bool hadBadEdges = checkPatch
898  (
899  allGeometry,
900  "\".*\"",
901  mesh,
902  pp,
903  // faceLabels,
904  pp.meshEdges(mesh.edges(), mesh.pointEdges()),
905  nullptr, // No point set
906  &badEdges
907  );
908  Info<< nl << endl;
909 
910  if (writeBadEdges && hadBadEdges)
911  {
912  edgeList dumpEdges(pp.edges(), badEdges.sortedToc());
913 
914  vtk::lineWriter writer
915  (
916  pp.localPoints(),
917  dumpEdges,
918  fileName
919  (
920  mesh.time().globalPath()
921  / ("checkMesh-illegal-edges")
922  )
923  );
924 
925  writer.writeGeometry();
926 
927  // CellData
928  writer.beginCellData();
929  writer.writeProcIDs();
930 
931  Info<< "Wrote "
932  << returnReduce(dumpEdges.size(), sumOp<label>())
933  << " bad edges: " << writer.output().name() << nl;
934  writer.close();
935  }
936  else if (hadBadEdges)
937  {
938  Info<< "Detected "
939  << returnReduce(badEdges.size(), sumOp<label>())
940  << " bad edges (possibly relevant for finite-area)" << nl;
941  }
942  }
943 
944  //Info.setf(ios_base::right);
945  }
946 
947  {
948  Info<< "\nChecking faceZone topology for multiply connected"
949  << " surfaces..." << endl;
950 
951  Pout.setf(ios_base::left);
952 
953  const faceZoneMesh& faceZones = mesh.faceZones();
954 
955  if (faceZones.size())
956  {
957  Info<< " "
958  << setw(20) << "FaceZone"
959  << setw(9) << "Faces"
960  << setw(9) << "Points"
961  << setw(34) << "Surface topology";
962  if (allGeometry)
963  {
964  Info<< " Bounding box";
965  }
966  Info<< endl;
967 
968  for (const faceZone& fz : faceZones)
969  {
970  checkPatch
971  (
972  allGeometry,
973  fz.name(),
974  mesh,
975  fz(), // patch
976  // fz, // mesh face labels
977  fz.meshEdges(), // mesh edge labels
978  &nonManifoldPoints
979  );
980  Info<< endl;
981  }
982 
983  // Check for duplicates
984  if (allTopology)
985  {
986  faceSet mzFaces(mesh, "multiZoneFaces", mesh.nFaces()/100);
987  const label nMulti = checkZones(mesh, faceZones, mzFaces);
988  if (nMulti)
989  {
990  Info<< " <<Writing " << nMulti
991  << " faces that are in multiple zones"
992  << " to set " << mzFaces.name() << endl;
993  mzFaces.instance() = mesh.pointsInstance();
994  mzFaces.write();
995  if (surfWriter && surfWriter->enabled())
996  {
997  mergeAndWrite(*surfWriter, mzFaces);
998  }
999  }
1000  }
1001  }
1002  else
1003  {
1004  Info<< " No faceZones found."<<endl;
1005  }
1006  }
1007 
1008 
1009  const label nPoints =
1010  returnReduce(nonManifoldPoints.size(), sumOp<label>());
1011 
1012  if (nPoints)
1013  {
1014  Info<< " <<Writing " << nPoints
1015  << " conflicting points to set " << nonManifoldPoints.name()
1016  << endl;
1017  nonManifoldPoints.instance() = mesh.pointsInstance();
1018  nonManifoldPoints.write();
1019  if (setWriter && setWriter->enabled())
1020  {
1021  mergeAndWrite(*setWriter, nonManifoldPoints);
1022  }
1023  }
1024 
1025  {
1026  Info<< "\nChecking basic cellZone addressing..." << endl;
1027 
1028  Pout.setf(ios_base::left);
1029 
1030  const cellZoneMesh& cellZones = mesh.cellZones();
1031 
1032  if (cellZones.size())
1033  {
1034  Info<< " "
1035  << setw(20) << "CellZone"
1036  << setw(13) << "Cells"
1037  << setw(13) << "Points"
1038  << setw(13) << "Volume"
1039  << "BoundingBox" << endl;
1040 
1041  const cellList& cells = mesh.cells();
1042  const faceList& faces = mesh.faces();
1043  const scalarField& cellVolumes = mesh.cellVolumes();
1044 
1045  bitSet isZonePoint(mesh.nPoints());
1046 
1047  for (const cellZone& cZone : cellZones)
1048  {
1049  boundBox bb;
1050  isZonePoint.reset(); // clears all bits (reset count)
1051  scalar v = 0.0;
1052 
1053  for (const label celli : cZone)
1054  {
1055  v += cellVolumes[celli];
1056  for (const label facei : cells[celli])
1057  {
1058  const face& f = faces[facei];
1059  for (const label verti : f)
1060  {
1061  if (isZonePoint.set(verti))
1062  {
1063  bb.add(mesh.points()[verti]);
1064  }
1065  }
1066  }
1067  }
1068 
1069  bb.reduce(); // Global min/max
1070 
1071  Info<< " "
1072  << setw(19) << cZone.name()
1073  << ' ' << setw(12)
1074  << returnReduce(cZone.size(), sumOp<label>())
1075  << ' ' << setw(12)
1076  << returnReduce(isZonePoint.count(), sumOp<label>())
1077  << ' ' << setw(12)
1078  << returnReduce(v, sumOp<scalar>())
1079  << ' ' << bb << endl;
1080  }
1081 
1082 
1083  // Check for duplicates
1084  if (allTopology)
1085  {
1086  cellSet mzCells(mesh, "multiZoneCells", mesh.nCells()/100);
1087  const label nMulti = checkZones(mesh, cellZones, mzCells);
1088  if (nMulti)
1089  {
1090  Info<< " <<Writing " << nMulti
1091  << " cells that are in multiple zones"
1092  << " to set " << mzCells.name() << endl;
1093  mzCells.instance() = mesh.pointsInstance();
1094  mzCells.write();
1095  if (surfWriter && surfWriter->enabled())
1096  {
1097  mergeAndWrite(*surfWriter, mzCells);
1098  }
1099  }
1100  }
1101  }
1102  else
1103  {
1104  Info<< " No cellZones found."<<endl;
1105  }
1106  }
1107 
1108 
1109  {
1110  Info<< "\nChecking basic pointZone addressing..." << endl;
1111 
1112  Pout.setf(ios_base::left);
1113 
1114  const pointZoneMesh& pointZones = mesh.pointZones();
1115 
1116  if (pointZones.size())
1117  {
1118  Info<< " "
1119  << setw(20) << "PointZone"
1120  << setw(8) << "Points"
1121  << "BoundingBox" << nl;
1122 
1123  for (const auto& zone : pointZones)
1124  {
1125  boundBox bb
1126  (
1127  mesh.points(),
1128  static_cast<const labelUList&>(zone),
1129  true // Reduce (global min/max)
1130  );
1131 
1132  Info<< " "
1133  << setw(20) << zone.name()
1134  << setw(8)
1135  << returnReduce(zone.size(), sumOp<label>())
1136  << bb << endl;
1137  }
1138 
1139 
1140  // Check for duplicates
1141  if (allTopology)
1142  {
1143  pointSet mzPoints(mesh, "multiZonePoints", mesh.nPoints()/100);
1144  const label nMulti = checkZones(mesh, pointZones, mzPoints);
1145  if (nMulti)
1146  {
1147  Info<< " <<Writing " << nMulti
1148  << " points that are in multiple zones"
1149  << " to set " << mzPoints.name() << endl;
1150  mzPoints.instance() = mesh.pointsInstance();
1151  mzPoints.write();
1152  if (setWriter && setWriter->enabled())
1153  {
1154  mergeAndWrite(*setWriter, mzPoints);
1155  }
1156  }
1157  }
1158  }
1159  else
1160  {
1161  Info<< " No pointZones found."<<endl;
1162  }
1163  }
1164 
1165 
1166  // Force creation of all addressing if requested.
1167  // Errors will be reported as required
1168  if (allTopology)
1169  {
1170  mesh.cells();
1171  mesh.faces();
1172  mesh.edges();
1173  mesh.points();
1174  mesh.faceOwner();
1175  mesh.faceNeighbour();
1176  mesh.cellCells();
1177  mesh.edgeCells();
1178  mesh.pointCells();
1179  mesh.edgeFaces();
1180  mesh.pointFaces();
1181  mesh.cellEdges();
1182  mesh.faceEdges();
1183  mesh.pointEdges();
1184  }
1185 
1186  return noFailedChecks;
1187 }
1188 
1189 
1190 // ************************************************************************* //
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
ZoneMesh< faceZone, polyMesh > faceZoneMesh
A ZoneMesh with the type faceZone.
List< cell > cellList
List of cell.
Definition: cellListFwd.H:39
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/"finiteArea-edgesCentres"))
ios_base::fmtflags setf(const ios_base::fmtflags f)
Set flags of stream.
Definition: IOstream.H:472
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:132
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
label checkTopology(const polyMesh &mesh, const bool allTopology, const bool allGeometry, autoPtr< surfaceWriter > &surfWriter, autoPtr< coordSetWriter > &setWriter, const bool writeBadEdges=false)
bool coupled(solutionDict.getOrDefault("coupledEnergyField", false))
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
void mergeAndWrite(const polyMesh &mesh, surfaceWriter &writer, const word &name, const indirectPrimitivePatch &setPatch, const fileName &outputDir)
Generate merged surface on master and write. Needs input patch.
label checkZones(const polyMesh &mesh, const ZoneMesh< Zone, polyMesh > &zones, topoSet &set)
labelList faceLabels(nFaceLabels)
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
UList< label > labelUList
A UList of labels.
Definition: UList.H:78
labelHashSet badEdges(pp.nEdges()/20)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
const cellShapeList & cells
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:31
label nPoints
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
labelHashSet * pointSetPtr
labelHashSet * badEdgesPtr
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
bool foundError
ZoneMesh< pointZone, polyMesh > pointZoneMesh
A ZoneMesh with the type pointZone.
Istream and Ostream manipulators taking arguments.
labelList f(nPoints)
PrimitivePatch< UIndirectList< face >, const pointField & > uindirectPrimitivePatch
A PrimitivePatch with UIndirectList for the faces, const reference for the point field.
const polyBoundaryMesh & patches
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
messageStream Info
Information stream (stdout output on master, null elsewhere)
Omanip< int > setw(const int i)
Definition: IOmanip.H:199
List< label > labelList
A List of labels.
Definition: List.H:62
List< bool > boolList
A List of bools.
Definition: List.H:60
bool checkPatch(const bool allGeometry, const std::string &name, const polyMesh &mesh, const PatchType &pp, const labelUList &meshEdges, labelHashSet *pointSetPtr=nullptr, labelHashSet *badEdgesPtr=nullptr)
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
ZoneMesh< cellZone, polyMesh > cellZoneMesh
A ZoneMesh with the type cellZone.
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:133