1 #include "checkGeometry.H"
7 #include "wedgePolyPatch.H"
8 #include "unitConversion.H"
9 #include "polyMeshTetDecomposition.H"
12 // Find wedge with opposite orientation. Note: does not actually check that
13 // it is opposite, only that it has opposite normal and same axis
14 Foam::label Foam::findOppositeWedge
17 const wedgePolyPatch& wpp
20 const polyBoundaryMesh& patches = mesh.boundaryMesh();
22 scalar wppCosAngle = wpp.centreNormal()&wpp.patchNormal();
24 forAll(patches, patchI)
29 && patches[patchI].size()
30 && isA<wedgePolyPatch>(patches[patchI])
33 const wedgePolyPatch& pp = refCast<const wedgePolyPatch>
38 // Calculate (cos of) angle to wpp (not pp!) centre normal
39 scalar ppCosAngle = wpp.centreNormal()&pp.patchNormal();
43 pp.size() == wpp.size()
44 && mag(pp.axis() & wpp.axis()) >= (1-1E-3)
45 && mag(ppCosAngle - wppCosAngle) >= 1E-3
56 bool Foam::checkWedges
60 const Vector<label>& directions,
64 // To mark edges without calculating edge addressing
65 EdgeMap<label> edgesInError;
67 const pointField& p = mesh.points();
68 const faceList& fcs = mesh.faces();
71 const polyBoundaryMesh& patches = mesh.boundaryMesh();
72 forAll(patches, patchI)
74 if (patches[patchI].size() && isA<wedgePolyPatch>(patches[patchI]))
76 const wedgePolyPatch& pp = refCast<const wedgePolyPatch>
81 scalar wedgeAngle = acos(pp.centreNormal()&pp.patchNormal());
85 Info<< " Wedge " << pp.name() << " with angle "
86 << radToDeg(wedgeAngle) << " degrees"
91 label oppositePatchI = findOppositeWedge(mesh, pp);
93 if (oppositePatchI == -1)
97 Info<< " ***Cannot find opposite wedge for wedge "
103 const wedgePolyPatch& opp = refCast<const wedgePolyPatch>
105 patches[oppositePatchI]
109 if (mag(opp.axis() & pp.axis()) < (1-1E-3))
113 Info<< " ***Wedges do not have the same axis."
114 << " Encountered " << pp.axis()
115 << " on patch " << pp.name()
116 << " which differs from " << opp.axis()
117 << " on opposite wedge patch" << opp.axis()
125 // Mark edges on wedgePatches
128 const face& f = pp[i];
132 label p1 = f.nextLabel(fp);
133 edgesInError.insert(edge(p0, p1), -1); // non-error value
138 // Check that wedge patch is flat
139 const point& p0 = p[pp.meshPoints()[0]];
140 forAll(pp.meshPoints(), i)
142 const point& pt = p[pp.meshPoints()[i]];
143 scalar d = mag((pt-p0) & pp.patchNormal());
149 Info<< " ***Wedge patch " << pp.name() << " not planar."
150 << " Point " << pt << " is not in patch plane by "
162 // Check all non-wedge faces
163 label nEdgesInError = 0;
167 const face& f = fcs[faceI];
172 label p1 = f.nextLabel(fp);
175 vector d(p[p1]-p[p0]);
176 scalar magD = mag(d);
178 if (magD > ROOTVSMALL)
182 // Check how many empty directions are used by the edge.
183 label nEmptyDirs = 0;
184 label nNonEmptyDirs = 0;
185 for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
187 if (mag(d[cmpt]) > 1e-6)
189 if (directions[cmpt] == 0)
202 // Purely in ok directions.
204 else if (nEmptyDirs == 1)
206 // Ok if purely in empty directions.
207 if (nNonEmptyDirs > 0)
209 if (edgesInError.insert(edge(p0, p1), faceI))
215 else if (nEmptyDirs > 1)
218 if (edgesInError.insert(edge(p0, p1), faceI))
228 label nErrorEdges = returnReduce(nEdgesInError, sumOp<label>());
234 Info<< " ***Number of edges not aligned with or perpendicular to "
235 << "non-empty directions: " << nErrorEdges << endl;
240 setPtr->resize(2*nEdgesInError);
241 forAllConstIter(EdgeMap<label>, edgesInError, iter)
245 setPtr->insert(iter.key()[0]);
246 setPtr->insert(iter.key()[1]);
257 Info<< " All edges aligned with or perpendicular to "
258 << "non-empty directions." << endl;
265 bool Foam::checkCoupledPoints
267 const polyMesh& mesh,
272 const pointField& p = mesh.points();
273 const faceList& fcs = mesh.faces();
274 const polyBoundaryMesh& patches = mesh.boundaryMesh();
276 // Zero'th point on coupled faces
277 pointField nbrZeroPoint(fcs.size()-mesh.nInternalFaces(), vector::max);
279 // Exchange zero point
280 forAll(patches, patchI)
282 if (patches[patchI].coupled())
284 const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>
291 label bFaceI = cpp.start()+i-mesh.nInternalFaces();
292 const point& p0 = p[cpp[i][0]];
293 nbrZeroPoint[bFaceI] = p0;
297 syncTools::swapBoundaryFacePositions(mesh, nbrZeroPoint);
299 // Compare to local ones. Use same tolerance as for matching
300 label nErrorFaces = 0;
301 scalar avgMismatch = 0;
302 label nCoupledFaces = 0;
304 forAll(patches, patchI)
306 if (patches[patchI].coupled())
308 const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>
315 scalarField smallDist
319 cpp.matchTolerance(),
328 label bFaceI = cpp.start()+i-mesh.nInternalFaces();
329 const point& p0 = p[cpp[i][0]];
331 scalar d = mag(p0 - nbrZeroPoint[bFaceI]);
333 if (d > smallDist[i])
337 setPtr->insert(cpp.start()+i);
348 reduce(nErrorFaces, sumOp<label>());
349 reduce(avgMismatch, maxOp<scalar>());
350 reduce(nCoupledFaces, sumOp<label>());
352 if (nCoupledFaces > 0)
354 avgMismatch /= nCoupledFaces;
361 Info<< " **Error in coupled point location: "
363 << " faces have their 0th vertex not opposite"
364 << " their coupled equivalent. Average mismatch "
365 << avgMismatch << "."
375 Info<< " Coupled point location match (average "
376 << avgMismatch << ") OK." << endl;
384 Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
386 label noFailedChecks = 0;
388 Info<< "\nChecking geometry..." << endl;
390 // Get a small relative length from the bounding box
391 const boundBox& globalBb = mesh.bounds();
393 Info<< " Overall domain bounding box "
394 << globalBb.min() << " " << globalBb.max() << endl;
398 scalar minDistSqr = magSqr(1e-6 * globalBb.span());
400 // Non-empty directions
401 const Vector<label> validDirs = (mesh.geometricD() + Vector<label>::one)/2;
402 Info<< " Mesh (non-empty, non-wedge) directions " << validDirs << endl;
404 const Vector<label> solDirs = (mesh.solutionD() + Vector<label>::one)/2;
405 Info<< " Mesh (non-empty) directions " << solDirs << endl;
407 if (mesh.nGeometricD() < 3)
409 pointSet nonAlignedPoints(mesh, "nonAlignedEdges", mesh.nPoints()/100);
415 && checkWedges(mesh, true, validDirs, &nonAlignedPoints)
419 && mesh.checkEdgeAlignment(true, validDirs, &nonAlignedPoints)
424 label nNonAligned = returnReduce
426 nonAlignedPoints.size(),
432 Info<< " <<Writing " << nNonAligned
433 << " points on non-aligned edges to set "
434 << nonAlignedPoints.name() << endl;
435 nonAlignedPoints.instance() = mesh.pointsInstance();
436 nonAlignedPoints.write();
441 if (mesh.checkClosedBoundary(true)) noFailedChecks++;
444 cellSet cells(mesh, "nonClosedCells", mesh.nCells()/100+1);
445 cellSet aspectCells(mesh, "highAspectRatioCells", mesh.nCells()/100+1);
448 mesh.checkClosedCells
459 label nNonClosed = returnReduce(cells.size(), sumOp<label>());
463 Info<< " <<Writing " << nNonClosed
464 << " non closed cells to set " << cells.name() << endl;
465 cells.instance() = mesh.pointsInstance();
470 label nHighAspect = returnReduce(aspectCells.size(), sumOp<label>());
474 Info<< " <<Writing " << nHighAspect
475 << " cells with high aspect ratio to set "
476 << aspectCells.name() << endl;
477 aspectCells.instance() = mesh.pointsInstance();
483 faceSet faces(mesh, "zeroAreaFaces", mesh.nFaces()/100+1);
484 if (mesh.checkFaceAreas(true, &faces))
488 label nFaces = returnReduce(faces.size(), sumOp<label>());
492 Info<< " <<Writing " << nFaces
493 << " zero area faces to set " << faces.name() << endl;
494 faces.instance() = mesh.pointsInstance();
501 cellSet cells(mesh, "zeroVolumeCells", mesh.nCells()/100+1);
502 if (mesh.checkCellVolumes(true, &cells))
506 label nCells = returnReduce(cells.size(), sumOp<label>());
510 Info<< " <<Writing " << nCells
511 << " zero volume cells to set " << cells.name() << endl;
512 cells.instance() = mesh.pointsInstance();
519 faceSet faces(mesh, "nonOrthoFaces", mesh.nFaces()/100+1);
520 if (mesh.checkFaceOrthogonality(true, &faces))
525 label nFaces = returnReduce(faces.size(), sumOp<label>());
529 Info<< " <<Writing " << nFaces
530 << " non-orthogonal faces to set " << faces.name() << endl;
531 faces.instance() = mesh.pointsInstance();
537 faceSet faces(mesh, "wrongOrientedFaces", mesh.nFaces()/100 + 1);
538 if (mesh.checkFacePyramids(true, -SMALL, &faces))
542 label nFaces = returnReduce(faces.size(), sumOp<label>());
546 Info<< " <<Writing " << nFaces
547 << " faces with incorrect orientation to set "
548 << faces.name() << endl;
549 faces.instance() = mesh.pointsInstance();
556 faceSet faces(mesh, "skewFaces", mesh.nFaces()/100+1);
557 if (mesh.checkFaceSkewness(true, &faces))
561 label nFaces = returnReduce(faces.size(), sumOp<label>());
565 Info<< " <<Writing " << nFaces
566 << " skew faces to set " << faces.name() << endl;
567 faces.instance() = mesh.pointsInstance();
574 faceSet faces(mesh, "coupledFaces", mesh.nFaces()/100 + 1);
575 if (checkCoupledPoints(mesh, true, &faces))
579 label nFaces = returnReduce(faces.size(), sumOp<label>());
583 Info<< " <<Writing " << nFaces
584 << " faces with incorrectly matched 0th vertex to set "
585 << faces.name() << endl;
586 faces.instance() = mesh.pointsInstance();
594 faceSet faces(mesh, "lowQualityTetFaces", mesh.nFaces()/100+1);
597 polyMeshTetDecomposition::checkFaceTets
600 polyMeshTetDecomposition::minTetQuality,
608 label nFaces = returnReduce(faces.size(), sumOp<label>());
612 Info<< " <<Writing " << nFaces
613 << " faces with low quality or negative volume "
614 << "decomposition tets to set " << faces.name() << endl;
615 faces.instance() = mesh.pointsInstance();
623 // Note use of nPoints since don't want edge construction.
624 pointSet points(mesh, "shortEdges", mesh.nPoints()/1000 + 1);
625 if (mesh.checkEdgeLength(true, minDistSqr, &points))
629 label nPoints = returnReduce(points.size(), sumOp<label>());
633 Info<< " <<Writing " << nPoints
634 << " points on short edges to set " << points.name()
636 points.instance() = mesh.pointsInstance();
641 label nEdgeClose = returnReduce(points.size(), sumOp<label>());
643 if (mesh.checkPointNearness(false, minDistSqr, &points))
647 label nPoints = returnReduce(points.size(), sumOp<label>());
649 if (nPoints > nEdgeClose)
651 pointSet nearPoints(mesh, "nearPoints", points);
652 Info<< " <<Writing " << nPoints
653 << " near (closer than " << Foam::sqrt(minDistSqr)
654 << " apart) points to set " << nearPoints.name() << endl;
655 nearPoints.instance() = mesh.pointsInstance();
663 faceSet faces(mesh, "concaveFaces", mesh.nFaces()/100 + 1);
664 if (mesh.checkFaceAngles(true, 10, &faces))
668 label nFaces = returnReduce(faces.size(), sumOp<label>());
672 Info<< " <<Writing " << nFaces
673 << " faces with concave angles to set " << faces.name()
675 faces.instance() = mesh.pointsInstance();
683 faceSet faces(mesh, "warpedFaces", mesh.nFaces()/100 + 1);
684 if (mesh.checkFaceFlatness(true, 0.8, &faces))
688 label nFaces = returnReduce(faces.size(), sumOp<label>());
692 Info<< " <<Writing " << nFaces
693 << " warped faces to set " << faces.name() << endl;
694 faces.instance() = mesh.pointsInstance();
702 cellSet cells(mesh, "underdeterminedCells", mesh.nCells()/100);
703 if (mesh.checkCellDeterminant(true, &cells, mesh.geometricD()))
707 label nCells = returnReduce(cells.size(), sumOp<label>());
709 Info<< " <<Writing " << nCells
710 << " under-determined cells to set " << cells.name() << endl;
711 cells.instance() = mesh.pointsInstance();
718 cellSet cells(mesh, "concaveCells", mesh.nCells()/100);
719 if (mesh.checkConcaveCells(true, &cells))
723 label nCells = returnReduce(cells.size(), sumOp<label>());
725 Info<< " <<Writing " << nCells
726 << " concave cells to set " << cells.name() << endl;
727 cells.instance() = mesh.pointsInstance();
732 return noFailedChecks;