Add the OpenFOAM source tree
This commit is contained in:
@ -0,0 +1,7 @@
|
||||
printMeshStats.C
|
||||
checkTopology.C
|
||||
checkGeometry.C
|
||||
checkMeshQuality.C
|
||||
checkMesh.C
|
||||
|
||||
EXE = $(FOAM_APPBIN)/checkMesh
|
||||
@ -0,0 +1,8 @@
|
||||
EXE_INC = \
|
||||
-I$(LIB_SRC)/meshTools/lnInclude \
|
||||
-I$(LIB_SRC)/finiteVolume/lnInclude \
|
||||
-I$(LIB_SRC)/dynamicMesh/lnInclude
|
||||
|
||||
EXE_LIBS = \
|
||||
-lmeshTools \
|
||||
-ldynamicMesh
|
||||
@ -0,0 +1,864 @@
|
||||
#include "checkGeometry.H"
|
||||
#include "polyMesh.H"
|
||||
#include "cellSet.H"
|
||||
#include "faceSet.H"
|
||||
#include "pointSet.H"
|
||||
#include "EdgeMap.H"
|
||||
#include "wedgePolyPatch.H"
|
||||
#include "unitConversion.H"
|
||||
#include "polyMeshTetDecomposition.H"
|
||||
|
||||
|
||||
// Find wedge with opposite orientation. Note: does not actually check that
|
||||
// it is opposite, only that it has opposite normal and same axis
|
||||
Foam::label Foam::findOppositeWedge
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const wedgePolyPatch& wpp
|
||||
)
|
||||
{
|
||||
const polyBoundaryMesh& patches = mesh.boundaryMesh();
|
||||
|
||||
scalar wppCosAngle = wpp.cosAngle();
|
||||
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
if
|
||||
(
|
||||
patchI != wpp.index()
|
||||
&& patches[patchI].size()
|
||||
&& isA<wedgePolyPatch>(patches[patchI])
|
||||
)
|
||||
{
|
||||
const wedgePolyPatch& pp =
|
||||
refCast<const wedgePolyPatch>(patches[patchI]);
|
||||
|
||||
// Calculate (cos of) angle to wpp (not pp!) centre normal
|
||||
scalar ppCosAngle = wpp.centreNormal() & pp.n();
|
||||
|
||||
if
|
||||
(
|
||||
pp.size() == wpp.size()
|
||||
&& mag(pp.axis() & wpp.axis()) >= (1-1e-3)
|
||||
&& mag(ppCosAngle - wppCosAngle) >= 1e-3
|
||||
)
|
||||
{
|
||||
return patchI;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::checkWedges
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const bool report,
|
||||
const Vector<label>& directions,
|
||||
labelHashSet* setPtr
|
||||
)
|
||||
{
|
||||
// To mark edges without calculating edge addressing
|
||||
EdgeMap<label> edgesInError;
|
||||
|
||||
const pointField& p = mesh.points();
|
||||
const faceList& fcs = mesh.faces();
|
||||
|
||||
|
||||
const polyBoundaryMesh& patches = mesh.boundaryMesh();
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
if (patches[patchI].size() && isA<wedgePolyPatch>(patches[patchI]))
|
||||
{
|
||||
const wedgePolyPatch& pp =
|
||||
refCast<const wedgePolyPatch>(patches[patchI]);
|
||||
|
||||
scalar wedgeAngle = acos(pp.cosAngle());
|
||||
|
||||
if (report)
|
||||
{
|
||||
Info<< " Wedge " << pp.name() << " with angle "
|
||||
<< radToDeg(wedgeAngle) << " degrees"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// Find opposite
|
||||
label oppositePatchI = findOppositeWedge(mesh, pp);
|
||||
|
||||
if (oppositePatchI == -1)
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " ***Cannot find opposite wedge for wedge "
|
||||
<< pp.name() << endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const wedgePolyPatch& opp =
|
||||
refCast<const wedgePolyPatch>(patches[oppositePatchI]);
|
||||
|
||||
|
||||
if (mag(opp.axis() & pp.axis()) < (1-1e-3))
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " ***Wedges do not have the same axis."
|
||||
<< " Encountered " << pp.axis()
|
||||
<< " on patch " << pp.name()
|
||||
<< " which differs from " << opp.axis()
|
||||
<< " on opposite wedge patch" << opp.axis()
|
||||
<< endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Mark edges on wedgePatches
|
||||
forAll(pp, i)
|
||||
{
|
||||
const face& f = pp[i];
|
||||
forAll(f, fp)
|
||||
{
|
||||
label p0 = f[fp];
|
||||
label p1 = f.nextLabel(fp);
|
||||
edgesInError.insert(edge(p0, p1), -1); // non-error value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check that wedge patch is flat
|
||||
const point& p0 = p[pp.meshPoints()[0]];
|
||||
forAll(pp.meshPoints(), i)
|
||||
{
|
||||
const point& pt = p[pp.meshPoints()[i]];
|
||||
scalar d = mag((pt - p0) & pp.n());
|
||||
|
||||
if (d > sqrt(SMALL))
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " ***Wedge patch " << pp.name() << " not planar."
|
||||
<< " Point " << pt << " is not in patch plane by "
|
||||
<< d << " metre."
|
||||
<< endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Check all non-wedge faces
|
||||
label nEdgesInError = 0;
|
||||
|
||||
forAll(fcs, faceI)
|
||||
{
|
||||
const face& f = fcs[faceI];
|
||||
|
||||
forAll(f, fp)
|
||||
{
|
||||
label p0 = f[fp];
|
||||
label p1 = f.nextLabel(fp);
|
||||
if (p0 < p1)
|
||||
{
|
||||
vector d(p[p1]-p[p0]);
|
||||
scalar magD = mag(d);
|
||||
|
||||
if (magD > ROOTVSMALL)
|
||||
{
|
||||
d /= magD;
|
||||
|
||||
// Check how many empty directions are used by the edge.
|
||||
label nEmptyDirs = 0;
|
||||
label nNonEmptyDirs = 0;
|
||||
for (direction cmpt=0; cmpt<vector::nComponents; cmpt++)
|
||||
{
|
||||
if (mag(d[cmpt]) > 1e-6)
|
||||
{
|
||||
if (directions[cmpt] == 0)
|
||||
{
|
||||
nEmptyDirs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
nNonEmptyDirs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nEmptyDirs == 0)
|
||||
{
|
||||
// Purely in ok directions.
|
||||
}
|
||||
else if (nEmptyDirs == 1)
|
||||
{
|
||||
// Ok if purely in empty directions.
|
||||
if (nNonEmptyDirs > 0)
|
||||
{
|
||||
if (edgesInError.insert(edge(p0, p1), faceI))
|
||||
{
|
||||
nEdgesInError++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (nEmptyDirs > 1)
|
||||
{
|
||||
// Always an error
|
||||
if (edgesInError.insert(edge(p0, p1), faceI))
|
||||
{
|
||||
nEdgesInError++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label nErrorEdges = returnReduce(nEdgesInError, sumOp<label>());
|
||||
|
||||
if (nErrorEdges > 0)
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " ***Number of edges not aligned with or perpendicular to "
|
||||
<< "non-empty directions: " << nErrorEdges << endl;
|
||||
}
|
||||
|
||||
if (setPtr)
|
||||
{
|
||||
setPtr->resize(2*nEdgesInError);
|
||||
forAllConstIter(EdgeMap<label>, edgesInError, iter)
|
||||
{
|
||||
if (iter() >= 0)
|
||||
{
|
||||
setPtr->insert(iter.key()[0]);
|
||||
setPtr->insert(iter.key()[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " All edges aligned with or perpendicular to "
|
||||
<< "non-empty directions." << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
//- Default transformation behaviour for position
|
||||
class transformPositionList
|
||||
{
|
||||
public:
|
||||
|
||||
//- Transform patch-based field
|
||||
void operator()
|
||||
(
|
||||
const coupledPolyPatch& cpp,
|
||||
List<pointField>& pts
|
||||
) const
|
||||
{
|
||||
// Each element of pts is all the points in the face. Convert into
|
||||
// lists of size cpp to transform.
|
||||
|
||||
List<pointField> newPts(pts.size());
|
||||
forAll(pts, faceI)
|
||||
{
|
||||
newPts[faceI].setSize(pts[faceI].size());
|
||||
}
|
||||
|
||||
label index = 0;
|
||||
while (true)
|
||||
{
|
||||
label n = 0;
|
||||
|
||||
// Extract for every face the i'th position
|
||||
pointField ptsAtIndex(pts.size(), vector::zero);
|
||||
forAll(cpp, faceI)
|
||||
{
|
||||
const pointField& facePts = pts[faceI];
|
||||
if (facePts.size() > index)
|
||||
{
|
||||
ptsAtIndex[faceI] = facePts[index];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Now ptsAtIndex will have for every face either zero or
|
||||
// the position of the i'th vertex. Transform.
|
||||
cpp.transformPosition(ptsAtIndex);
|
||||
|
||||
// Extract back from ptsAtIndex into newPts
|
||||
forAll(cpp, faceI)
|
||||
{
|
||||
pointField& facePts = newPts[faceI];
|
||||
if (facePts.size() > index)
|
||||
{
|
||||
facePts[index] = ptsAtIndex[faceI];
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
pts.transfer(newPts);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
bool Foam::checkCoupledPoints
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const bool report,
|
||||
labelHashSet* setPtr
|
||||
)
|
||||
{
|
||||
const pointField& p = mesh.points();
|
||||
const faceList& fcs = mesh.faces();
|
||||
const polyBoundaryMesh& patches = mesh.boundaryMesh();
|
||||
|
||||
// Zero'th point on coupled faces
|
||||
//pointField nbrZeroPoint(fcs.size()-mesh.nInternalFaces(), vector::max);
|
||||
List<pointField> nbrPoints(fcs.size() - mesh.nInternalFaces());
|
||||
|
||||
// Exchange zero point
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
if (patches[patchI].coupled())
|
||||
{
|
||||
const coupledPolyPatch& cpp = refCast<const coupledPolyPatch>
|
||||
(
|
||||
patches[patchI]
|
||||
);
|
||||
|
||||
forAll(cpp, i)
|
||||
{
|
||||
label bFaceI = cpp.start() + i - mesh.nInternalFaces();
|
||||
const face& f = cpp[i];
|
||||
nbrPoints[bFaceI].setSize(f.size());
|
||||
forAll(f, fp)
|
||||
{
|
||||
const point& p0 = p[f[fp]];
|
||||
nbrPoints[bFaceI][fp] = p0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syncTools::syncBoundaryFaceList
|
||||
(
|
||||
mesh,
|
||||
nbrPoints,
|
||||
eqOp<pointField>(),
|
||||
transformPositionList()
|
||||
);
|
||||
|
||||
// Compare to local ones. Use same tolerance as for matching
|
||||
label nErrorFaces = 0;
|
||||
scalar avgMismatch = 0;
|
||||
label nCoupledPoints = 0;
|
||||
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
if (patches[patchI].coupled())
|
||||
{
|
||||
const coupledPolyPatch& cpp =
|
||||
refCast<const coupledPolyPatch>(patches[patchI]);
|
||||
|
||||
if (cpp.owner())
|
||||
{
|
||||
scalarField smallDist
|
||||
(
|
||||
cpp.calcFaceTol
|
||||
(
|
||||
//cpp.matchTolerance(),
|
||||
cpp,
|
||||
cpp.points(),
|
||||
cpp.faceCentres()
|
||||
)
|
||||
);
|
||||
|
||||
forAll(cpp, i)
|
||||
{
|
||||
label bFaceI = cpp.start() + i - mesh.nInternalFaces();
|
||||
const face& f = cpp[i];
|
||||
|
||||
if (f.size() != nbrPoints[bFaceI].size())
|
||||
{
|
||||
FatalErrorIn
|
||||
(
|
||||
"Foam::checkCoupledPoints\n"
|
||||
"(\n"
|
||||
" const polyMesh&, const bool, labelHashSet*\n"
|
||||
")\n"
|
||||
) << "Local face size : " << f.size()
|
||||
<< " does not equal neighbour face size : "
|
||||
<< nbrPoints[bFaceI].size()
|
||||
<< abort(FatalError);
|
||||
}
|
||||
|
||||
label fp = 0;
|
||||
forAll(f, j)
|
||||
{
|
||||
const point& p0 = p[f[fp]];
|
||||
scalar d = mag(p0 - nbrPoints[bFaceI][j]);
|
||||
|
||||
if (d > smallDist[i])
|
||||
{
|
||||
if (setPtr)
|
||||
{
|
||||
setPtr->insert(cpp.start()+i);
|
||||
}
|
||||
nErrorFaces++;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
avgMismatch += d;
|
||||
nCoupledPoints++;
|
||||
|
||||
fp = f.rcIndex(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reduce(nErrorFaces, sumOp<label>());
|
||||
reduce(avgMismatch, maxOp<scalar>());
|
||||
reduce(nCoupledPoints, sumOp<label>());
|
||||
|
||||
if (nCoupledPoints > 0)
|
||||
{
|
||||
avgMismatch /= nCoupledPoints;
|
||||
}
|
||||
|
||||
if (nErrorFaces > 0)
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " **Error in coupled point location: "
|
||||
<< nErrorFaces
|
||||
<< " faces have their 0th or consecutive vertex not opposite"
|
||||
<< " their coupled equivalent. Average mismatch "
|
||||
<< avgMismatch << "."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
Info<< " Coupled point location match (average "
|
||||
<< avgMismatch << ") OK." << endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry)
|
||||
{
|
||||
label noFailedChecks = 0;
|
||||
|
||||
Info<< "\nChecking geometry..." << endl;
|
||||
|
||||
// Get a small relative length from the bounding box
|
||||
const boundBox& globalBb = mesh.bounds();
|
||||
|
||||
Info<< " Overall domain bounding box "
|
||||
<< globalBb.min() << " " << globalBb.max() << endl;
|
||||
|
||||
|
||||
// Min length
|
||||
scalar minDistSqr = magSqr(1e-6 * globalBb.span());
|
||||
|
||||
// Non-empty directions
|
||||
const Vector<label> validDirs = (mesh.geometricD() + Vector<label>::one)/2;
|
||||
Info<< " Mesh (non-empty, non-wedge) directions " << validDirs << endl;
|
||||
|
||||
const Vector<label> solDirs = (mesh.solutionD() + Vector<label>::one)/2;
|
||||
Info<< " Mesh (non-empty) directions " << solDirs << endl;
|
||||
|
||||
if (mesh.nGeometricD() < 3)
|
||||
{
|
||||
pointSet nonAlignedPoints(mesh, "nonAlignedEdges", mesh.nPoints()/100);
|
||||
|
||||
if
|
||||
(
|
||||
(
|
||||
validDirs != solDirs
|
||||
&& checkWedges(mesh, true, validDirs, &nonAlignedPoints)
|
||||
)
|
||||
|| (
|
||||
validDirs == solDirs
|
||||
&& mesh.checkEdgeAlignment(true, validDirs, &nonAlignedPoints)
|
||||
)
|
||||
)
|
||||
{
|
||||
noFailedChecks++;
|
||||
label nNonAligned = returnReduce
|
||||
(
|
||||
nonAlignedPoints.size(),
|
||||
sumOp<label>()
|
||||
);
|
||||
|
||||
if (nNonAligned > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nNonAligned
|
||||
<< " points on non-aligned edges to set "
|
||||
<< nonAlignedPoints.name() << endl;
|
||||
nonAlignedPoints.instance() = mesh.pointsInstance();
|
||||
nonAlignedPoints.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.checkClosedBoundary(true)) noFailedChecks++;
|
||||
|
||||
{
|
||||
cellSet cells(mesh, "nonClosedCells", mesh.nCells()/100+1);
|
||||
cellSet aspectCells(mesh, "highAspectRatioCells", mesh.nCells()/100+1);
|
||||
if
|
||||
(
|
||||
mesh.checkClosedCells
|
||||
(
|
||||
true,
|
||||
&cells,
|
||||
&aspectCells,
|
||||
mesh.geometricD()
|
||||
)
|
||||
)
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nNonClosed = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
if (nNonClosed > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nNonClosed
|
||||
<< " non closed cells to set " << cells.name() << endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
}
|
||||
|
||||
label nHighAspect = returnReduce(aspectCells.size(), sumOp<label>());
|
||||
|
||||
if (nHighAspect > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nHighAspect
|
||||
<< " cells with high aspect ratio to set "
|
||||
<< aspectCells.name() << endl;
|
||||
aspectCells.instance() = mesh.pointsInstance();
|
||||
aspectCells.write();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "zeroAreaFaces", mesh.nFaces()/100+1);
|
||||
if (mesh.checkFaceAreas(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " zero area faces to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cellSet cells(mesh, "zeroVolumeCells", mesh.nCells()/100+1);
|
||||
if (mesh.checkCellVolumes(true, &cells))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
if (nCells > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " zero volume cells to set " << cells.name() << endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "nonOrthoFaces", mesh.nFaces()/100+1);
|
||||
if (mesh.checkFaceOrthogonality(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " non-orthogonal faces to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "wrongOrientedFaces", mesh.nFaces()/100 + 1);
|
||||
if (mesh.checkFacePyramids(true, -SMALL, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with incorrect orientation to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "skewFaces", mesh.nFaces()/100+1);
|
||||
if (mesh.checkFaceSkewness(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " skew faces to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "coupledFaces", mesh.nFaces()/100 + 1);
|
||||
if (checkCoupledPoints(mesh, true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with incorrectly matched 0th (or consecutive)"
|
||||
<< " vertex to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
faceSet faces(mesh, "lowQualityTetFaces", mesh.nFaces()/100+1);
|
||||
if
|
||||
(
|
||||
polyMeshTetDecomposition::checkFaceTets
|
||||
(
|
||||
mesh,
|
||||
polyMeshTetDecomposition::minTetQuality,
|
||||
true,
|
||||
&faces
|
||||
)
|
||||
)
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with low quality or negative volume "
|
||||
<< "decomposition tets to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
// Note use of nPoints since don't want edge construction.
|
||||
pointSet points(mesh, "shortEdges", mesh.nPoints()/1000 + 1);
|
||||
if (mesh.checkEdgeLength(true, minDistSqr, &points))
|
||||
{
|
||||
//noFailedChecks++;
|
||||
|
||||
label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
if (nPoints > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nPoints
|
||||
<< " points on short edges to set " << points.name()
|
||||
<< endl;
|
||||
points.instance() = mesh.pointsInstance();
|
||||
points.write();
|
||||
}
|
||||
}
|
||||
|
||||
label nEdgeClose = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
if (mesh.checkPointNearness(false, minDistSqr, &points))
|
||||
{
|
||||
//noFailedChecks++;
|
||||
|
||||
label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
if (nPoints > nEdgeClose)
|
||||
{
|
||||
pointSet nearPoints(mesh, "nearPoints", points);
|
||||
Info<< " <<Writing " << nPoints
|
||||
<< " near (closer than " << Foam::sqrt(minDistSqr)
|
||||
<< " apart) points to set " << nearPoints.name() << endl;
|
||||
nearPoints.instance() = mesh.pointsInstance();
|
||||
nearPoints.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
faceSet faces(mesh, "concaveFaces", mesh.nFaces()/100 + 1);
|
||||
if (mesh.checkFaceAngles(true, 10, &faces))
|
||||
{
|
||||
//noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with concave angles to set " << faces.name()
|
||||
<< endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
faceSet faces(mesh, "warpedFaces", mesh.nFaces()/100 + 1);
|
||||
if (mesh.checkFaceFlatness(true, 0.8, &faces))
|
||||
{
|
||||
//noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " warped faces to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
cellSet cells(mesh, "underdeterminedCells", mesh.nCells()/100);
|
||||
if (mesh.checkCellDeterminant(true, &cells))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " under-determined cells to set " << cells.name() << endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
cellSet cells(mesh, "concaveCells", mesh.nCells()/100);
|
||||
if (mesh.checkConcaveCells(true, &cells))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " concave cells to set " << cells.name() << endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
faceSet faces(mesh, "lowWeightFaces", mesh.nFaces()/100);
|
||||
if (mesh.checkFaceWeight(true, 0.05, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with low interpolation weights to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
faceSet faces(mesh, "lowVolRatioFaces", mesh.nFaces()/100);
|
||||
if (mesh.checkVolRatio(true, 0.01, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with low volume ratio cells to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
return noFailedChecks;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
#include "label.H"
|
||||
#include "HashSet.H"
|
||||
#include "labelVector.H"
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
class polyMesh;
|
||||
class wedgePolyPatch;
|
||||
|
||||
label findOppositeWedge(const polyMesh&, const wedgePolyPatch&);
|
||||
|
||||
//- Check wedge orientation
|
||||
bool checkWedges
|
||||
(
|
||||
const polyMesh&,
|
||||
const bool report,
|
||||
const Vector<label>&,
|
||||
labelHashSet*
|
||||
);
|
||||
|
||||
//- Check 0th vertex on coupled faces
|
||||
bool checkCoupledPoints(const polyMesh&, const bool report, labelHashSet*);
|
||||
|
||||
label checkGeometry(const polyMesh& mesh, const bool allGeometry);
|
||||
}
|
||||
218
applications/utilities/mesh/manipulation/checkMesh/checkMesh.C
Normal file
218
applications/utilities/mesh/manipulation/checkMesh/checkMesh.C
Normal file
@ -0,0 +1,218 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Application
|
||||
checkMesh
|
||||
|
||||
Description
|
||||
Checks validity of a mesh.
|
||||
|
||||
Usage
|
||||
- checkMesh [OPTION]
|
||||
|
||||
\param -allGeometry \n
|
||||
Checks all (including non finite-volume specific) geometry
|
||||
|
||||
\param -allTopology \n
|
||||
Checks all (including non finite-volume specific) addressing
|
||||
|
||||
\param -meshQuality \n
|
||||
Checks against user defined (in \a system/meshQualityDict) quality settings
|
||||
|
||||
\param -region \<name\> \n
|
||||
Specify an alternative mesh region.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "argList.H"
|
||||
#include "timeSelector.H"
|
||||
#include "Time.H"
|
||||
|
||||
#include "polyMesh.H"
|
||||
#include "globalMeshData.H"
|
||||
|
||||
#include "printMeshStats.H"
|
||||
#include "checkTopology.H"
|
||||
#include "checkGeometry.H"
|
||||
#include "checkMeshQuality.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
timeSelector::addOptions();
|
||||
# include "addRegionOption.H"
|
||||
argList::addBoolOption
|
||||
(
|
||||
"noTopology",
|
||||
"skip checking the mesh topology"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"allGeometry",
|
||||
"include bounding box checks"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"allTopology",
|
||||
"include extra topology checks"
|
||||
);
|
||||
argList::addBoolOption
|
||||
(
|
||||
"meshQuality",
|
||||
"read user-defined mesh quality criterions from system/meshQualityDict"
|
||||
);
|
||||
|
||||
# include "setRootCase.H"
|
||||
# include "createTime.H"
|
||||
instantList timeDirs = timeSelector::select0(runTime, args);
|
||||
# include "createNamedPolyMesh.H"
|
||||
|
||||
const bool noTopology = args.optionFound("noTopology");
|
||||
const bool allGeometry = args.optionFound("allGeometry");
|
||||
const bool allTopology = args.optionFound("allTopology");
|
||||
const bool meshQuality = args.optionFound("meshQuality");
|
||||
|
||||
if (noTopology)
|
||||
{
|
||||
Info<< "Disabling all topology checks." << nl << endl;
|
||||
}
|
||||
if (allTopology)
|
||||
{
|
||||
Info<< "Enabling all (cell, face, edge, point) topology checks."
|
||||
<< nl << endl;
|
||||
}
|
||||
if (allGeometry)
|
||||
{
|
||||
Info<< "Enabling all geometry checks." << nl << endl;
|
||||
}
|
||||
if (meshQuality)
|
||||
{
|
||||
Info<< "Enabling user-defined geometry checks." << nl << endl;
|
||||
}
|
||||
|
||||
|
||||
autoPtr<IOdictionary> qualDict;
|
||||
if (meshQuality)
|
||||
{
|
||||
qualDict.reset
|
||||
(
|
||||
new IOdictionary
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"meshQualityDict",
|
||||
mesh.time().system(),
|
||||
mesh,
|
||||
IOobject::MUST_READ,
|
||||
IOobject::NO_WRITE
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
forAll(timeDirs, timeI)
|
||||
{
|
||||
runTime.setTime(timeDirs[timeI], timeI);
|
||||
|
||||
polyMesh::readUpdateState state = mesh.readUpdate();
|
||||
|
||||
if
|
||||
(
|
||||
!timeI
|
||||
|| state == polyMesh::TOPO_CHANGE
|
||||
|| state == polyMesh::TOPO_PATCH_CHANGE
|
||||
)
|
||||
{
|
||||
Info<< "Time = " << runTime.timeName() << nl << endl;
|
||||
|
||||
// Clear mesh before checking
|
||||
mesh.clearOut();
|
||||
|
||||
// Reconstruct globalMeshData
|
||||
mesh.globalData();
|
||||
|
||||
printMeshStats(mesh, allTopology);
|
||||
|
||||
label nFailedChecks = 0;
|
||||
|
||||
if (!noTopology)
|
||||
{
|
||||
nFailedChecks += checkTopology(mesh, allTopology, allGeometry);
|
||||
}
|
||||
|
||||
nFailedChecks += checkGeometry(mesh, allGeometry);
|
||||
|
||||
if (meshQuality)
|
||||
{
|
||||
nFailedChecks += checkMeshQuality(mesh, qualDict());
|
||||
}
|
||||
|
||||
|
||||
// Note: no reduction in nFailedChecks necessary since is
|
||||
// counter of checks, not counter of failed cells,faces etc.
|
||||
|
||||
if (nFailedChecks == 0)
|
||||
{
|
||||
Info<< "\nMesh OK.\n" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "\nFailed " << nFailedChecks << " mesh checks.\n"
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
else if (state == polyMesh::POINTS_MOVED)
|
||||
{
|
||||
Info<< "Time = " << runTime.timeName() << nl << endl;
|
||||
|
||||
label nFailedChecks = checkGeometry(mesh, allGeometry);
|
||||
|
||||
if (meshQuality)
|
||||
{
|
||||
nFailedChecks += checkMeshQuality(mesh, qualDict());
|
||||
}
|
||||
|
||||
|
||||
if (nFailedChecks)
|
||||
{
|
||||
Info<< "\nFailed " << nFailedChecks << " mesh checks.\n"
|
||||
<< endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "\nMesh OK.\n" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Info<< "End\n" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,34 @@
|
||||
#include "checkMeshQuality.H"
|
||||
#include "polyMesh.H"
|
||||
#include "cellSet.H"
|
||||
#include "faceSet.H"
|
||||
#include "motionSmoother.H"
|
||||
|
||||
|
||||
Foam::label Foam::checkMeshQuality
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
label noFailedChecks = 0;
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "meshQualityFaces", mesh.nFaces()/100+1);
|
||||
motionSmoother::checkMesh(false, mesh, dict, faces);
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces in error to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
return noFailedChecks;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
#include "polyMesh.H"
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
label checkMeshQuality(const polyMesh& mesh, const dictionary&);
|
||||
}
|
||||
@ -0,0 +1,500 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
|
||||
OpenFOAM is free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "checkTopology.H"
|
||||
#include "polyMesh.H"
|
||||
#include "Time.H"
|
||||
#include "regionSplit.H"
|
||||
#include "cellSet.H"
|
||||
#include "faceSet.H"
|
||||
#include "pointSet.H"
|
||||
#include "IOmanip.H"
|
||||
#include "emptyPolyPatch.H"
|
||||
#include "processorPolyPatch.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::label Foam::checkTopology
|
||||
(
|
||||
const polyMesh& mesh,
|
||||
const bool allTopology,
|
||||
const bool allGeometry
|
||||
)
|
||||
{
|
||||
label noFailedChecks = 0;
|
||||
|
||||
Info<< "Checking topology..." << endl;
|
||||
|
||||
// Check if the boundary definition is unique
|
||||
mesh.boundaryMesh().checkDefinition(true);
|
||||
|
||||
// Check that empty patches cover all sides of the mesh
|
||||
{
|
||||
label nEmpty = 0;
|
||||
forAll(mesh.boundaryMesh(), patchI)
|
||||
{
|
||||
if (isA<emptyPolyPatch>(mesh.boundaryMesh()[patchI]))
|
||||
{
|
||||
nEmpty += mesh.boundaryMesh()[patchI].size();
|
||||
}
|
||||
}
|
||||
reduce(nEmpty, sumOp<label>());
|
||||
label nTotCells = returnReduce(mesh.cells().size(), sumOp<label>());
|
||||
|
||||
// These are actually warnings, not errors.
|
||||
if (nTotCells && (nEmpty % nTotCells))
|
||||
{
|
||||
Info<< " ***Total number of faces on empty patches"
|
||||
<< " is not divisible by the number of cells in the mesh."
|
||||
<< " Hence this mesh is not 1D or 2D."
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the boundary processor patches are correct
|
||||
mesh.boundaryMesh().checkParallelSync(true);
|
||||
|
||||
// Check names of zones are equal
|
||||
mesh.cellZones().checkDefinition(true);
|
||||
if (mesh.cellZones().checkParallelSync(true))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
mesh.faceZones().checkDefinition(true);
|
||||
if (mesh.faceZones().checkParallelSync(true))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
mesh.pointZones().checkDefinition(true);
|
||||
if (mesh.pointZones().checkParallelSync(true))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
cellSet cells(mesh, "illegalCells", mesh.nCells()/100);
|
||||
|
||||
forAll(mesh.cells(), cellI)
|
||||
{
|
||||
const cell& cFaces = mesh.cells()[cellI];
|
||||
|
||||
if (cFaces.size() <= 3)
|
||||
{
|
||||
cells.insert(cellI);
|
||||
}
|
||||
forAll(cFaces, i)
|
||||
{
|
||||
if (cFaces[i] < 0 || cFaces[i] >= mesh.nFaces())
|
||||
{
|
||||
cells.insert(cellI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
if (nCells > 0)
|
||||
{
|
||||
Info<< " Illegal cells (less than 4 faces or out of range faces)"
|
||||
<< " found, number of cells: " << nCells << endl;
|
||||
noFailedChecks++;
|
||||
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " illegal cells to set " << cells.name() << endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< " Cell to face addressing OK." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
pointSet points(mesh, "unusedPoints", mesh.nPoints()/100);
|
||||
if (mesh.checkPoints(true, &points))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nPoints = returnReduce(points.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nPoints
|
||||
<< " unused points to set " << points.name() << endl;
|
||||
points.instance() = mesh.pointsInstance();
|
||||
points.write();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "upperTriangularFace", mesh.nFaces()/100);
|
||||
if (mesh.checkUpperTriangular(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " unordered faces to set " << faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
faceSet faces(mesh, "outOfRangeFaces", mesh.nFaces()/100);
|
||||
if (mesh.checkFaceVertices(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with out-of-range or duplicate vertices to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allTopology)
|
||||
{
|
||||
cellSet cells(mesh, "zipUpCells", mesh.nCells()/100);
|
||||
if (mesh.checkCellsZipUp(true, &cells))
|
||||
{
|
||||
noFailedChecks++;
|
||||
|
||||
label nCells = returnReduce(cells.size(), sumOp<label>());
|
||||
|
||||
Info<< " <<Writing " << nCells
|
||||
<< " cells with over used edges to set " << cells.name()
|
||||
<< endl;
|
||||
cells.instance() = mesh.pointsInstance();
|
||||
cells.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allTopology)
|
||||
{
|
||||
faceSet faces(mesh, "edgeFaces", mesh.nFaces()/100);
|
||||
if (mesh.checkFaceFaces(true, &faces))
|
||||
{
|
||||
noFailedChecks++;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(faces.size(), sumOp<label>());
|
||||
if (nFaces > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nFaces
|
||||
<< " faces with non-standard edge connectivity to set "
|
||||
<< faces.name() << endl;
|
||||
faces.instance() = mesh.pointsInstance();
|
||||
faces.write();
|
||||
}
|
||||
}
|
||||
|
||||
if (allTopology)
|
||||
{
|
||||
labelList nInternalFaces(mesh.nCells(), 0);
|
||||
|
||||
for (label faceI = 0; faceI < mesh.nInternalFaces(); faceI++)
|
||||
{
|
||||
nInternalFaces[mesh.faceOwner()[faceI]]++;
|
||||
nInternalFaces[mesh.faceNeighbour()[faceI]]++;
|
||||
}
|
||||
const polyBoundaryMesh& patches = mesh.boundaryMesh();
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
if (patches[patchI].coupled())
|
||||
{
|
||||
const labelUList& owners = patches[patchI].faceCells();
|
||||
|
||||
forAll(owners, i)
|
||||
{
|
||||
nInternalFaces[owners[i]]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellSet oneCells(mesh, "oneInternalFaceCells", mesh.nCells()/100);
|
||||
cellSet twoCells(mesh, "twoInternalFacesCells", mesh.nCells()/100);
|
||||
|
||||
forAll(nInternalFaces, cellI)
|
||||
{
|
||||
if (nInternalFaces[cellI] <= 1)
|
||||
{
|
||||
oneCells.insert(cellI);
|
||||
}
|
||||
else if (nInternalFaces[cellI] == 2)
|
||||
{
|
||||
twoCells.insert(cellI);
|
||||
}
|
||||
}
|
||||
|
||||
label nOneCells = returnReduce(oneCells.size(), sumOp<label>());
|
||||
|
||||
if (nOneCells > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nOneCells
|
||||
<< " cells with zero or one non-boundary face to set "
|
||||
<< oneCells.name()
|
||||
<< endl;
|
||||
oneCells.instance() = mesh.pointsInstance();
|
||||
oneCells.write();
|
||||
}
|
||||
|
||||
label nTwoCells = returnReduce(twoCells.size(), sumOp<label>());
|
||||
|
||||
if (nTwoCells > 0)
|
||||
{
|
||||
Info<< " <<Writing " << nTwoCells
|
||||
<< " cells with two non-boundary faces to set "
|
||||
<< twoCells.name()
|
||||
<< endl;
|
||||
twoCells.instance() = mesh.pointsInstance();
|
||||
twoCells.write();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
regionSplit rs(mesh);
|
||||
|
||||
if (rs.nRegions() <= 1)
|
||||
{
|
||||
Info<< " Number of regions: " << rs.nRegions() << " (OK)."
|
||||
<< endl;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< " *Number of regions: " << rs.nRegions() << endl;
|
||||
|
||||
Info<< " The mesh has multiple regions which are not connected "
|
||||
"by any face." << endl
|
||||
<< " <<Writing region information to "
|
||||
<< mesh.time().timeName()/"cellToRegion"
|
||||
<< endl;
|
||||
|
||||
labelIOList ctr
|
||||
(
|
||||
IOobject
|
||||
(
|
||||
"cellToRegion",
|
||||
mesh.time().timeName(),
|
||||
mesh,
|
||||
IOobject::NO_READ,
|
||||
IOobject::NO_WRITE
|
||||
),
|
||||
rs
|
||||
);
|
||||
ctr.write();
|
||||
|
||||
|
||||
// write cellSet for each region
|
||||
PtrList<cellSet> cellRegions(rs.nRegions());
|
||||
for (label i = 0; i < rs.nRegions(); i++)
|
||||
{
|
||||
cellRegions.set
|
||||
(
|
||||
i,
|
||||
new cellSet
|
||||
(
|
||||
mesh,
|
||||
"region" + Foam::name(i),
|
||||
mesh.nCells()/100
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
forAll(rs, i)
|
||||
{
|
||||
cellRegions[rs[i]].insert(i);
|
||||
}
|
||||
|
||||
for (label i = 0; i < rs.nRegions(); i++)
|
||||
{
|
||||
Info<< " <<Writing region " << i << " with "
|
||||
<< returnReduce(cellRegions[i].size(), sumOp<scalar>())
|
||||
<< " cells to cellSet " << cellRegions[i].name() << endl;
|
||||
|
||||
cellRegions[i].write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
if (!Pstream::parRun())
|
||||
{
|
||||
Info<< "\nChecking patch topology for multiply connected"
|
||||
<< " surfaces..." << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< "\nChecking basic patch addressing..." << endl;
|
||||
}
|
||||
|
||||
|
||||
const polyBoundaryMesh& patches = mesh.boundaryMesh();
|
||||
|
||||
// Non-manifold points
|
||||
pointSet points
|
||||
(
|
||||
mesh,
|
||||
"nonManifoldPoints",
|
||||
mesh.nPoints()/1000
|
||||
);
|
||||
|
||||
Pout.setf(ios_base::left);
|
||||
|
||||
Info<< " "
|
||||
<< setw(20) << "Patch"
|
||||
<< setw(9) << "Faces"
|
||||
<< setw(9) << "Points";
|
||||
if (!Pstream::parRun())
|
||||
{
|
||||
Info<< setw(34) << "Surface topology";
|
||||
}
|
||||
if (allGeometry)
|
||||
{
|
||||
Info<< " Bounding box";
|
||||
}
|
||||
Info<< endl;
|
||||
|
||||
forAll(patches, patchI)
|
||||
{
|
||||
const polyPatch& pp = patches[patchI];
|
||||
|
||||
if (!isA<processorPolyPatch>(pp))
|
||||
{
|
||||
Info<< " "
|
||||
<< setw(20) << pp.name()
|
||||
<< setw(9) << returnReduce(pp.size(), sumOp<label>())
|
||||
<< setw(9) << returnReduce(pp.nPoints(), sumOp<label>());
|
||||
|
||||
if (!Pstream::parRun())
|
||||
{
|
||||
primitivePatch::surfaceTopo pTyp = pp.surfaceType();
|
||||
|
||||
if (pp.empty())
|
||||
{
|
||||
Info<< setw(34) << "ok (empty)";
|
||||
}
|
||||
else if (pTyp == primitivePatch::MANIFOLD)
|
||||
{
|
||||
if (pp.checkPointManifold(true, &points))
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "multiply connected (shared point)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34) << "ok (closed singly connected)";
|
||||
}
|
||||
|
||||
// Add points on non-manifold edges to make set complete
|
||||
pp.checkTopology(false, &points);
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.checkTopology(false, &points);
|
||||
|
||||
if (pTyp == primitivePatch::OPEN)
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "ok (non-closed singly connected)";
|
||||
}
|
||||
else
|
||||
{
|
||||
Info<< setw(34)
|
||||
<< "multiply connected (shared edge)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allGeometry)
|
||||
{
|
||||
const pointField& pts = pp.points();
|
||||
const labelList& mp = pp.meshPoints();
|
||||
|
||||
if (returnReduce(mp.size(), sumOp<label>()) > 0)
|
||||
{
|
||||
boundBox bb(point::max, point::min);
|
||||
forAll (mp, i)
|
||||
{
|
||||
bb.min() = min(bb.min(), pts[mp[i]]);
|
||||
bb.max() = max(bb.max(), pts[mp[i]]);
|
||||
}
|
||||
reduce(bb.min(), minOp<vector>());
|
||||
reduce(bb.max(), maxOp<vector>());
|
||||
Info<< ' ' << bb;
|
||||
}
|
||||
}
|
||||
Info<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (points.size())
|
||||
{
|
||||
Info<< " <<Writing " << returnReduce(points.size(), sumOp<label>())
|
||||
<< " conflicting points to set "
|
||||
<< points.name() << endl;
|
||||
|
||||
points.instance() = mesh.pointsInstance();
|
||||
points.write();
|
||||
}
|
||||
|
||||
//Info.setf(ios_base::right);
|
||||
}
|
||||
|
||||
// Force creation of all addressing if requested.
|
||||
// Errors will be reported as required
|
||||
if (allTopology)
|
||||
{
|
||||
mesh.cells();
|
||||
mesh.faces();
|
||||
mesh.edges();
|
||||
mesh.points();
|
||||
mesh.faceOwner();
|
||||
mesh.faceNeighbour();
|
||||
mesh.cellCells();
|
||||
mesh.edgeCells();
|
||||
mesh.pointCells();
|
||||
mesh.edgeFaces();
|
||||
mesh.pointFaces();
|
||||
mesh.cellEdges();
|
||||
mesh.faceEdges();
|
||||
mesh.pointEdges();
|
||||
}
|
||||
|
||||
return noFailedChecks;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,9 @@
|
||||
#include "label.H"
|
||||
#include "wordList.H"
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
class polyMesh;
|
||||
|
||||
label checkTopology(const polyMesh&, const bool, const bool);
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
#include "printMeshStats.H"
|
||||
#include "polyMesh.H"
|
||||
#include "globalMeshData.H"
|
||||
|
||||
#include "hexMatcher.H"
|
||||
#include "wedgeMatcher.H"
|
||||
#include "prismMatcher.H"
|
||||
#include "pyrMatcher.H"
|
||||
#include "tetWedgeMatcher.H"
|
||||
#include "tetMatcher.H"
|
||||
#include "IOmanip.H"
|
||||
|
||||
|
||||
void Foam::printMeshStats(const polyMesh& mesh, const bool allTopology)
|
||||
{
|
||||
Info<< "Mesh stats" << nl
|
||||
<< " points: "
|
||||
<< returnReduce(mesh.points().size(), sumOp<label>()) << nl;
|
||||
|
||||
label nInternalPoints = returnReduce
|
||||
(
|
||||
mesh.nInternalPoints(),
|
||||
sumOp<label>()
|
||||
);
|
||||
|
||||
if (nInternalPoints != -Pstream::nProcs())
|
||||
{
|
||||
Info<< " internal points: " << nInternalPoints << nl;
|
||||
|
||||
if (returnReduce(mesh.nInternalPoints(), minOp<label>()) == -1)
|
||||
{
|
||||
WarningIn("Foam::printMeshStats(const polyMesh&, const bool)")
|
||||
<< "Some processors have their points sorted into internal"
|
||||
<< " and external and some do not." << endl
|
||||
<< "This can cause problems later on." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (allTopology && nInternalPoints != -Pstream::nProcs())
|
||||
{
|
||||
label nEdges = returnReduce(mesh.nEdges(), sumOp<label>());
|
||||
label nInternalEdges = returnReduce
|
||||
(
|
||||
mesh.nInternalEdges(),
|
||||
sumOp<label>()
|
||||
);
|
||||
label nInternal1Edges = returnReduce
|
||||
(
|
||||
mesh.nInternal1Edges(),
|
||||
sumOp<label>()
|
||||
);
|
||||
label nInternal0Edges = returnReduce
|
||||
(
|
||||
mesh.nInternal0Edges(),
|
||||
sumOp<label>()
|
||||
);
|
||||
|
||||
Info<< " edges: " << nEdges << nl
|
||||
<< " internal edges: " << nInternalEdges << nl
|
||||
<< " internal edges using one boundary point: "
|
||||
<< nInternal1Edges-nInternal0Edges << nl
|
||||
<< " internal edges using two boundary points: "
|
||||
<< nInternalEdges-nInternal1Edges << nl;
|
||||
}
|
||||
|
||||
label nFaces = returnReduce(mesh.faces().size(), sumOp<label>());
|
||||
label nIntFaces = returnReduce(mesh.faceNeighbour().size(), sumOp<label>());
|
||||
label nCells = returnReduce(mesh.cells().size(), sumOp<label>());
|
||||
|
||||
Info<< " faces: " << nFaces << nl
|
||||
<< " internal faces: " << nIntFaces << nl
|
||||
<< " cells: " << nCells << nl
|
||||
<< " faces per cell: "
|
||||
<< scalar(nFaces + nIntFaces)/max(1, nCells) << nl
|
||||
<< " boundary patches: " << mesh.boundaryMesh().size() << nl
|
||||
<< " point zones: " << mesh.pointZones().size() << nl
|
||||
<< " face zones: " << mesh.faceZones().size() << nl
|
||||
<< " cell zones: " << mesh.cellZones().size() << nl
|
||||
<< endl;
|
||||
|
||||
// Construct shape recognizers
|
||||
hexMatcher hex;
|
||||
prismMatcher prism;
|
||||
wedgeMatcher wedge;
|
||||
pyrMatcher pyr;
|
||||
tetWedgeMatcher tetWedge;
|
||||
tetMatcher tet;
|
||||
|
||||
// Counters for different cell types
|
||||
label nHex = 0;
|
||||
label nWedge = 0;
|
||||
label nPrism = 0;
|
||||
label nPyr = 0;
|
||||
label nTet = 0;
|
||||
label nTetWedge = 0;
|
||||
label nUnknown = 0;
|
||||
|
||||
Map<label> polyhedralFaces;
|
||||
|
||||
for (label cellI = 0; cellI < mesh.nCells(); cellI++)
|
||||
{
|
||||
if (hex.isA(mesh, cellI))
|
||||
{
|
||||
nHex++;
|
||||
}
|
||||
else if (tet.isA(mesh, cellI))
|
||||
{
|
||||
nTet++;
|
||||
}
|
||||
else if (pyr.isA(mesh, cellI))
|
||||
{
|
||||
nPyr++;
|
||||
}
|
||||
else if (prism.isA(mesh, cellI))
|
||||
{
|
||||
nPrism++;
|
||||
}
|
||||
else if (wedge.isA(mesh, cellI))
|
||||
{
|
||||
nWedge++;
|
||||
}
|
||||
else if (tetWedge.isA(mesh, cellI))
|
||||
{
|
||||
nTetWedge++;
|
||||
}
|
||||
else
|
||||
{
|
||||
nUnknown++;
|
||||
polyhedralFaces(mesh.cells()[cellI].size())++;
|
||||
}
|
||||
}
|
||||
|
||||
reduce(nHex,sumOp<label>());
|
||||
reduce(nPrism,sumOp<label>());
|
||||
reduce(nWedge,sumOp<label>());
|
||||
reduce(nPyr,sumOp<label>());
|
||||
reduce(nTetWedge,sumOp<label>());
|
||||
reduce(nTet,sumOp<label>());
|
||||
reduce(nUnknown,sumOp<label>());
|
||||
|
||||
Info<< "Overall number of cells of each type:" << nl
|
||||
<< " hexahedra: " << nHex << nl
|
||||
<< " prisms: " << nPrism << nl
|
||||
<< " wedges: " << nWedge << nl
|
||||
<< " pyramids: " << nPyr << nl
|
||||
<< " tet wedges: " << nTetWedge << nl
|
||||
<< " tetrahedra: " << nTet << nl
|
||||
<< " polyhedra: " << nUnknown
|
||||
<< endl;
|
||||
|
||||
if (nUnknown > 0)
|
||||
{
|
||||
Pstream::mapCombineGather(polyhedralFaces, plusEqOp<label>());
|
||||
|
||||
Info<< " Breakdown of polyhedra by number of faces:" << nl
|
||||
<< " faces" << " number of cells" << endl;
|
||||
|
||||
const labelList sortedKeys = polyhedralFaces.sortedToc();
|
||||
|
||||
forAll(sortedKeys, keyI)
|
||||
{
|
||||
const label nFaces = sortedKeys[keyI];
|
||||
|
||||
Info<< setf(std::ios::right) << setw(13)
|
||||
<< nFaces << " " << polyhedralFaces[nFaces] << nl;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< endl;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
namespace Foam
|
||||
{
|
||||
class polyMesh;
|
||||
|
||||
void printMeshStats(const polyMesh& mesh, const bool allTopology);
|
||||
}
|
||||
Reference in New Issue
Block a user