mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-12-28 03:37:59 +00:00
2027 lines
46 KiB
C
2027 lines
46 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / 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 "primitiveMesh.H"
|
|
#include "pyramidPointFaceRef.H"
|
|
#include "ListOps.H"
|
|
#include "unitConversion.H"
|
|
#include "SortableList.H"
|
|
#include "EdgeMap.H"
|
|
#include "primitiveMeshTools.H"
|
|
|
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
|
|
|
Foam::scalar Foam::primitiveMesh::closedThreshold_ = 1.0e-6;
|
|
Foam::scalar Foam::primitiveMesh::aspectThreshold_ = 1000;
|
|
Foam::scalar Foam::primitiveMesh::nonOrthThreshold_ = 70; // deg
|
|
Foam::scalar Foam::primitiveMesh::skewThreshold_ = 4;
|
|
Foam::scalar Foam::primitiveMesh::planarCosAngle_ = 1.0e-6;
|
|
|
|
|
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
|
|
|
bool Foam::primitiveMesh::checkClosedBoundary
|
|
(
|
|
const vectorField& areas,
|
|
const bool report,
|
|
const PackedBoolList& internalOrCoupledFaces
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkClosedBoundary("
|
|
<< "const bool) const: "
|
|
<< "checking whether the boundary is closed" << endl;
|
|
}
|
|
|
|
// Loop through all boundary faces and sum up the face area vectors.
|
|
// For a closed boundary, this should be zero in all vector components
|
|
|
|
vector sumClosed(vector::zero);
|
|
scalar sumMagClosedBoundary = 0;
|
|
|
|
for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++)
|
|
{
|
|
if (!internalOrCoupledFaces.size() || !internalOrCoupledFaces[faceI])
|
|
{
|
|
sumClosed += areas[faceI];
|
|
sumMagClosedBoundary += mag(areas[faceI]);
|
|
}
|
|
}
|
|
|
|
reduce(sumClosed, sumOp<vector>());
|
|
reduce(sumMagClosedBoundary, sumOp<scalar>());
|
|
|
|
vector openness = sumClosed/(sumMagClosedBoundary + VSMALL);
|
|
|
|
if (cmptMax(cmptMag(openness)) > closedThreshold_)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Boundary openness " << openness
|
|
<< " possible hole in boundary description."
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Boundary openness " << openness << " OK."
|
|
<< endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkClosedCells
|
|
(
|
|
const vectorField& faceAreas,
|
|
const scalarField& cellVolumes,
|
|
const bool report,
|
|
labelHashSet* setPtr,
|
|
labelHashSet* aspectSetPtr,
|
|
const Vector<label>& meshD
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkClosedCells("
|
|
<< "const bool, labelHashSet*, labelHashSet*"
|
|
<< ", const Vector<label>&) const: "
|
|
<< "checking whether cells are closed" << endl;
|
|
}
|
|
|
|
// Check that all cells labels are valid
|
|
const cellList& c = cells();
|
|
|
|
label nErrorClosed = 0;
|
|
|
|
forAll(c, cI)
|
|
{
|
|
const cell& curCell = c[cI];
|
|
|
|
if (min(curCell) < 0 || max(curCell) > nFaces())
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cI);
|
|
}
|
|
|
|
nErrorClosed++;
|
|
}
|
|
}
|
|
|
|
if (nErrorClosed > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Cells with invalid face labels found, number of cells "
|
|
<< nErrorClosed << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
scalarField openness;
|
|
scalarField aspectRatio;
|
|
primitiveMeshTools::cellClosedness
|
|
(
|
|
*this,
|
|
meshD,
|
|
faceAreas,
|
|
cellVolumes,
|
|
openness,
|
|
aspectRatio
|
|
);
|
|
|
|
label nOpen = 0;
|
|
scalar maxOpennessCell = max(openness);
|
|
label nAspect = 0;
|
|
scalar maxAspectRatio = max(aspectRatio);
|
|
|
|
// Check the sums
|
|
forAll(openness, cellI)
|
|
{
|
|
if (openness[cellI] > closedThreshold_)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cellI);
|
|
}
|
|
|
|
nOpen++;
|
|
}
|
|
|
|
if (aspectRatio[cellI] > aspectThreshold_)
|
|
{
|
|
if (aspectSetPtr)
|
|
{
|
|
aspectSetPtr->insert(cellI);
|
|
}
|
|
|
|
nAspect++;
|
|
}
|
|
}
|
|
|
|
reduce(nOpen, sumOp<label>());
|
|
reduce(maxOpennessCell, maxOp<scalar>());
|
|
|
|
reduce(nAspect, sumOp<label>());
|
|
reduce(maxAspectRatio, maxOp<scalar>());
|
|
|
|
|
|
if (nOpen > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Open cells found, max cell openness: "
|
|
<< maxOpennessCell << ", number of open cells " << nOpen
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (nAspect > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***High aspect ratio cells found, Max aspect ratio: "
|
|
<< maxAspectRatio
|
|
<< ", number of cells " << nAspect
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (debug || report)
|
|
{
|
|
Info<< " Max cell openness = " << maxOpennessCell << " OK." << nl
|
|
<< " Max aspect ratio = " << maxAspectRatio << " OK."
|
|
<< endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceAreas
|
|
(
|
|
const vectorField& faceAreas,
|
|
const bool report,
|
|
const bool detailedReport,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceAreas("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking face area magnitudes" << endl;
|
|
}
|
|
|
|
const scalarField magFaceAreas(mag(faceAreas));
|
|
|
|
scalar minArea = GREAT;
|
|
scalar maxArea = -GREAT;
|
|
|
|
forAll(magFaceAreas, faceI)
|
|
{
|
|
if (magFaceAreas[faceI] < VSMALL)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
if (detailedReport)
|
|
{
|
|
if (isInternalFace(faceI))
|
|
{
|
|
Pout<< "Zero or negative face area detected for "
|
|
<< "internal face "<< faceI << " between cells "
|
|
<< faceOwner()[faceI] << " and "
|
|
<< faceNeighbour()[faceI]
|
|
<< ". Face area magnitude = " << magFaceAreas[faceI]
|
|
<< endl;
|
|
}
|
|
else
|
|
{
|
|
Pout<< "Zero or negative face area detected for "
|
|
<< "boundary face " << faceI << " next to cell "
|
|
<< faceOwner()[faceI] << ". Face area magnitude = "
|
|
<< magFaceAreas[faceI] << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
minArea = min(minArea, magFaceAreas[faceI]);
|
|
maxArea = max(maxArea, magFaceAreas[faceI]);
|
|
}
|
|
|
|
reduce(minArea, minOp<scalar>());
|
|
reduce(maxArea, maxOp<scalar>());
|
|
|
|
if (minArea < VSMALL)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Zero or negative face area detected. "
|
|
"Minimum area: " << minArea << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Minimum face area = " << minArea
|
|
<< ". Maximum face area = " << maxArea
|
|
<< ". Face area magnitudes OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkCellVolumes
|
|
(
|
|
const scalarField& vols,
|
|
const bool report,
|
|
const bool detailedReport,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkCellVolumes("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking cell volumes" << endl;
|
|
}
|
|
|
|
scalar minVolume = GREAT;
|
|
scalar maxVolume = -GREAT;
|
|
|
|
label nNegVolCells = 0;
|
|
|
|
forAll(vols, cellI)
|
|
{
|
|
if (vols[cellI] < VSMALL)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cellI);
|
|
}
|
|
if (detailedReport)
|
|
{
|
|
Pout<< "Zero or negative cell volume detected for cell "
|
|
<< cellI << ". Volume = " << vols[cellI] << endl;
|
|
}
|
|
|
|
nNegVolCells++;
|
|
}
|
|
|
|
minVolume = min(minVolume, vols[cellI]);
|
|
maxVolume = max(maxVolume, vols[cellI]);
|
|
}
|
|
|
|
reduce(minVolume, minOp<scalar>());
|
|
reduce(maxVolume, maxOp<scalar>());
|
|
reduce(nNegVolCells, sumOp<label>());
|
|
|
|
if (minVolume < VSMALL)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Zero or negative cell volume detected. "
|
|
<< "Minimum negative volume: " << minVolume
|
|
<< ", Number of negative volume cells: " << nNegVolCells
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Min volume = " << minVolume
|
|
<< ". Max volume = " << maxVolume
|
|
<< ". Total volume = " << gSum(vols)
|
|
<< ". Cell volumes OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceOrthogonality
|
|
(
|
|
const vectorField& fAreas,
|
|
const vectorField& cellCtrs,
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceOrthogonality("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking mesh non-orthogonality" << endl;
|
|
}
|
|
|
|
|
|
tmp<scalarField> tortho = primitiveMeshTools::faceOrthogonality
|
|
(
|
|
*this,
|
|
fAreas,
|
|
cellCtrs
|
|
);
|
|
const scalarField& ortho = tortho();
|
|
|
|
// Severe nonorthogonality threshold
|
|
const scalar severeNonorthogonalityThreshold =
|
|
::cos(degToRad(nonOrthThreshold_));
|
|
|
|
scalar minDDotS = min(ortho);
|
|
|
|
scalar sumDDotS = sum(ortho);
|
|
|
|
label severeNonOrth = 0;
|
|
|
|
label errorNonOrth = 0;
|
|
|
|
|
|
forAll(ortho, faceI)
|
|
{
|
|
if (ortho[faceI] < severeNonorthogonalityThreshold)
|
|
{
|
|
if (ortho[faceI] > SMALL)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
|
|
severeNonOrth++;
|
|
}
|
|
else
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
|
|
errorNonOrth++;
|
|
}
|
|
}
|
|
}
|
|
|
|
reduce(minDDotS, minOp<scalar>());
|
|
reduce(sumDDotS, sumOp<scalar>());
|
|
reduce(severeNonOrth, sumOp<label>());
|
|
reduce(errorNonOrth, sumOp<label>());
|
|
|
|
if (debug || report)
|
|
{
|
|
label neiSize = ortho.size();
|
|
reduce(neiSize, sumOp<label>());
|
|
|
|
if (neiSize > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Mesh non-orthogonality Max: "
|
|
<< radToDeg(::acos(minDDotS))
|
|
<< " average: " << radToDeg(::acos(sumDDotS/neiSize))
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
if (severeNonOrth > 0)
|
|
{
|
|
Info<< " *Number of severely non-orthogonal faces: "
|
|
<< severeNonOrth << "." << endl;
|
|
}
|
|
}
|
|
|
|
if (errorNonOrth > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Number of non-orthogonality errors: "
|
|
<< errorNonOrth << "." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Non-orthogonality check OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFacePyramids
|
|
(
|
|
const pointField& points,
|
|
const vectorField& ctrs,
|
|
const bool report,
|
|
const bool detailedReport,
|
|
const scalar minPyrVol,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFacePyramids("
|
|
<< "const bool, const scalar, labelHashSet*) const: "
|
|
<< "checking face orientation" << endl;
|
|
}
|
|
|
|
const labelList& own = faceOwner();
|
|
const labelList& nei = faceNeighbour();
|
|
const faceList& f = faces();
|
|
|
|
|
|
scalarField ownPyrVol;
|
|
scalarField neiPyrVol;
|
|
primitiveMeshTools::facePyramidVolume
|
|
(
|
|
*this,
|
|
points,
|
|
ctrs,
|
|
ownPyrVol,
|
|
neiPyrVol
|
|
);
|
|
|
|
|
|
label nErrorPyrs = 0;
|
|
|
|
forAll(ownPyrVol, faceI)
|
|
{
|
|
if (ownPyrVol[faceI] < minPyrVol)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
if (detailedReport)
|
|
{
|
|
Pout<< "Negative pyramid volume: " << ownPyrVol[faceI]
|
|
<< " for face " << faceI << " " << f[faceI]
|
|
<< " and owner cell: " << own[faceI] << endl
|
|
<< "Owner cell vertex labels: "
|
|
<< cells()[own[faceI]].labels(faces())
|
|
<< endl;
|
|
}
|
|
|
|
nErrorPyrs++;
|
|
}
|
|
|
|
if (isInternalFace(faceI))
|
|
{
|
|
if (neiPyrVol[faceI] < minPyrVol)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
if (detailedReport)
|
|
{
|
|
Pout<< "Negative pyramid volume: " << neiPyrVol[faceI]
|
|
<< " for face " << faceI << " " << f[faceI]
|
|
<< " and neighbour cell: " << nei[faceI] << nl
|
|
<< "Neighbour cell vertex labels: "
|
|
<< cells()[nei[faceI]].labels(faces())
|
|
<< endl;
|
|
}
|
|
nErrorPyrs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
reduce(nErrorPyrs, sumOp<label>());
|
|
|
|
if (nErrorPyrs > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Error in face pyramids: "
|
|
<< nErrorPyrs << " faces are incorrectly oriented."
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Face pyramids OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceSkewness
|
|
(
|
|
const pointField& points,
|
|
const vectorField& fCtrs,
|
|
const vectorField& fAreas,
|
|
const vectorField& cellCtrs,
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceSkewnesss("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking face skewness" << endl;
|
|
}
|
|
|
|
// Warn if the skew correction vector is more than skewWarning times
|
|
// larger than the face area vector
|
|
|
|
tmp<scalarField> tskewness = primitiveMeshTools::faceSkewness
|
|
(
|
|
*this,
|
|
points,
|
|
fCtrs,
|
|
fAreas,
|
|
cellCtrs
|
|
);
|
|
const scalarField& skewness = tskewness();
|
|
|
|
scalar maxSkew = max(skewness);
|
|
label nWarnSkew = 0;
|
|
|
|
forAll(skewness, faceI)
|
|
{
|
|
// Check if the skewness vector is greater than the PN vector.
|
|
// This does not cause trouble but is a good indication of a poor mesh.
|
|
if (skewness[faceI] > skewThreshold_)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
|
|
nWarnSkew++;
|
|
}
|
|
}
|
|
|
|
reduce(maxSkew, maxOp<scalar>());
|
|
reduce(nWarnSkew, sumOp<label>());
|
|
|
|
if (nWarnSkew > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Max skewness = " << maxSkew
|
|
<< ", " << nWarnSkew << " highly skew faces detected"
|
|
" which may impair the quality of the results"
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Max skewness = " << maxSkew << " OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// Check convexity of angles in a face. Allow a slight non-convexity.
|
|
// E.g. maxDeg = 10 allows for angles < 190 (or 10 degrees concavity)
|
|
// (if truly concave and points not visible from face centre the face-pyramid
|
|
// check in checkMesh will fail)
|
|
bool Foam::primitiveMesh::checkFaceAngles
|
|
(
|
|
const pointField& points,
|
|
const vectorField& faceAreas,
|
|
const bool report,
|
|
const scalar maxDeg,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceAngles"
|
|
<< "(const bool, const scalar, labelHashSet*) const: "
|
|
<< "checking face angles" << endl;
|
|
}
|
|
|
|
if (maxDeg < -SMALL || maxDeg > 180+SMALL)
|
|
{
|
|
FatalErrorIn
|
|
(
|
|
"primitiveMesh::checkFaceAngles"
|
|
"(const bool, const scalar, labelHashSet*)"
|
|
) << "maxDeg should be [0..180] but is now " << maxDeg
|
|
<< exit(FatalError);
|
|
}
|
|
|
|
const scalar maxSin = Foam::sin(degToRad(maxDeg));
|
|
|
|
|
|
tmp<scalarField> tfaceAngles = primitiveMeshTools::faceConcavity
|
|
(
|
|
maxSin,
|
|
*this,
|
|
points,
|
|
faceAreas
|
|
);
|
|
const scalarField& faceAngles = tfaceAngles();
|
|
|
|
scalar maxEdgeSin = max(faceAngles);
|
|
|
|
label nConcave = 0;
|
|
|
|
forAll(faceAngles, faceI)
|
|
{
|
|
if (faceAngles[faceI] > SMALL)
|
|
{
|
|
nConcave++;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
}
|
|
}
|
|
|
|
reduce(nConcave, sumOp<label>());
|
|
reduce(maxEdgeSin, maxOp<scalar>());
|
|
|
|
if (nConcave > 0)
|
|
{
|
|
scalar maxConcaveDegr =
|
|
radToDeg(Foam::asin(Foam::min(1.0, maxEdgeSin)));
|
|
|
|
if (debug || report)
|
|
{
|
|
Info<< " *There are " << nConcave
|
|
<< " faces with concave angles between consecutive"
|
|
<< " edges. Max concave angle = " << maxConcaveDegr
|
|
<< " degrees." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " All angles in faces OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceFlatness
|
|
(
|
|
const pointField& points,
|
|
const vectorField& faceCentres,
|
|
const vectorField& faceAreas,
|
|
const bool report,
|
|
const scalar warnFlatness,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceFlatness"
|
|
<< "(const bool, const scalar, labelHashSet*) const: "
|
|
<< "checking face flatness" << endl;
|
|
}
|
|
|
|
if (warnFlatness < 0 || warnFlatness > 1)
|
|
{
|
|
FatalErrorIn
|
|
(
|
|
"primitiveMesh::checkFaceFlatness"
|
|
"(const bool, const scalar, labelHashSet*)"
|
|
) << "warnFlatness should be [0..1] but is now " << warnFlatness
|
|
<< exit(FatalError);
|
|
}
|
|
|
|
const faceList& fcs = faces();
|
|
|
|
tmp<scalarField> tfaceFlatness = primitiveMeshTools::faceFlatness
|
|
(
|
|
*this,
|
|
points,
|
|
faceCentres,
|
|
faceAreas
|
|
);
|
|
const scalarField& faceFlatness = tfaceFlatness();
|
|
|
|
scalarField magAreas(mag(faceAreas));
|
|
|
|
scalar minFlatness = GREAT;
|
|
scalar sumFlatness = 0;
|
|
label nSummed = 0;
|
|
label nWarped = 0;
|
|
|
|
forAll(faceFlatness, faceI)
|
|
{
|
|
if (fcs[faceI].size() > 3 && magAreas[faceI] > VSMALL)
|
|
{
|
|
sumFlatness += faceFlatness[faceI];
|
|
nSummed++;
|
|
|
|
minFlatness = min(minFlatness, faceFlatness[faceI]);
|
|
|
|
if (faceFlatness[faceI] < warnFlatness)
|
|
{
|
|
nWarped++;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
reduce(nWarped, sumOp<label>());
|
|
reduce(minFlatness, minOp<scalar>());
|
|
|
|
reduce(nSummed, sumOp<label>());
|
|
reduce(sumFlatness, sumOp<scalar>());
|
|
|
|
if (debug || report)
|
|
{
|
|
if (nSummed > 0)
|
|
{
|
|
Info<< " Face flatness (1 = flat, 0 = butterfly) : average = "
|
|
<< sumFlatness / nSummed << " min = " << minFlatness << endl;
|
|
}
|
|
}
|
|
|
|
|
|
if (nWarped> 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " *There are " << nWarped
|
|
<< " faces with ratio between projected and actual area < "
|
|
<< warnFlatness << endl;
|
|
|
|
Info<< " Minimum ratio (minimum flatness, maximum warpage) = "
|
|
<< minFlatness << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " All face flatness OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkConcaveCells
|
|
(
|
|
const vectorField& fAreas,
|
|
const pointField& fCentres,
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkConcaveCells(const bool"
|
|
<< ", labelHashSet*) const: "
|
|
<< "checking for concave cells" << endl;
|
|
}
|
|
|
|
const cellList& c = cells();
|
|
const labelList& fOwner = faceOwner();
|
|
|
|
label nConcaveCells = 0;
|
|
|
|
forAll(c, cellI)
|
|
{
|
|
const cell& cFaces = c[cellI];
|
|
|
|
bool concave = false;
|
|
|
|
forAll(cFaces, i)
|
|
{
|
|
if (concave)
|
|
{
|
|
break;
|
|
}
|
|
|
|
label fI = cFaces[i];
|
|
|
|
const point& fC = fCentres[fI];
|
|
|
|
vector fN = fAreas[fI];
|
|
|
|
fN /= max(mag(fN), VSMALL);
|
|
|
|
// Flip normal if required so that it is always pointing out of
|
|
// the cell
|
|
if (fOwner[fI] != cellI)
|
|
{
|
|
fN *= -1;
|
|
}
|
|
|
|
// Is the centre of any other face of the cell on the
|
|
// wrong side of the plane of this face?
|
|
|
|
forAll(cFaces, j)
|
|
{
|
|
if (j != i)
|
|
{
|
|
label fJ = cFaces[j];
|
|
|
|
const point& pt = fCentres[fJ];
|
|
|
|
// If the cell is concave, the point will be on the
|
|
// positive normal side of the plane of f, defined by
|
|
// its centre and normal, and the angle between (pt -
|
|
// fC) and fN will be less than 90 degrees, so the dot
|
|
// product will be positive.
|
|
|
|
vector pC = (pt - fC);
|
|
|
|
pC /= max(mag(pC), VSMALL);
|
|
|
|
if ((pC & fN) > -planarCosAngle_)
|
|
{
|
|
// Concave or planar face
|
|
|
|
concave = true;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cellI);
|
|
}
|
|
|
|
nConcaveCells++;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
reduce(nConcaveCells, sumOp<label>());
|
|
|
|
if (nConcaveCells > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Concave cells (using face planes) found,"
|
|
<< " number of cells: " << nConcaveCells << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Concave cell check OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// Topological tests
|
|
|
|
bool Foam::primitiveMesh::checkUpperTriangular
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkUpperTriangular("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking face ordering" << endl;
|
|
}
|
|
|
|
// Check whether internal faces are ordered in the upper triangular order
|
|
const labelList& own = faceOwner();
|
|
const labelList& nei = faceNeighbour();
|
|
|
|
const cellList& c = cells();
|
|
|
|
label internal = nInternalFaces();
|
|
|
|
// Has error occurred?
|
|
bool error = false;
|
|
// Have multiple faces been detected?
|
|
label nMultipleCells = false;
|
|
|
|
// Loop through faceCells once more and make sure that for internal cell
|
|
// the first label is smaller
|
|
for (label faceI = 0; faceI < internal; faceI++)
|
|
{
|
|
if (own[faceI] >= nei[faceI])
|
|
{
|
|
error = true;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Loop through all cells. For each cell, find the face that is internal
|
|
// and add it to the check list (upper triangular order).
|
|
// Once the list is completed, check it against the faceCell list
|
|
|
|
forAll(c, cellI)
|
|
{
|
|
const labelList& curFaces = c[cellI];
|
|
|
|
// Neighbouring cells
|
|
SortableList<label> nbr(curFaces.size());
|
|
|
|
forAll(curFaces, i)
|
|
{
|
|
label faceI = curFaces[i];
|
|
|
|
if (faceI >= nInternalFaces())
|
|
{
|
|
// Sort last
|
|
nbr[i] = labelMax;
|
|
}
|
|
else
|
|
{
|
|
label nbrCellI = nei[faceI];
|
|
|
|
if (nbrCellI == cellI)
|
|
{
|
|
nbrCellI = own[faceI];
|
|
}
|
|
|
|
if (cellI < nbrCellI)
|
|
{
|
|
// cellI is master
|
|
nbr[i] = nbrCellI;
|
|
}
|
|
else
|
|
{
|
|
// nbrCell is master. Let it handle this face.
|
|
nbr[i] = labelMax;
|
|
}
|
|
}
|
|
}
|
|
|
|
nbr.sort();
|
|
|
|
// Now nbr holds the cellCells in incremental order. Check:
|
|
// - neighbouring cells appear only once. Since nbr is sorted this
|
|
// is simple check on consecutive elements
|
|
// - faces indexed in same order as nbr are incrementing as well.
|
|
|
|
label prevCell = nbr[0];
|
|
label prevFace = curFaces[nbr.indices()[0]];
|
|
|
|
bool hasMultipleFaces = false;
|
|
|
|
for (label i = 1; i < nbr.size(); i++)
|
|
{
|
|
label thisCell = nbr[i];
|
|
label thisFace = curFaces[nbr.indices()[i]];
|
|
|
|
if (thisCell == labelMax)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (thisCell == prevCell)
|
|
{
|
|
hasMultipleFaces = true;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(prevFace);
|
|
setPtr->insert(thisFace);
|
|
}
|
|
}
|
|
else if (thisFace < prevFace)
|
|
{
|
|
error = true;
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(thisFace);
|
|
}
|
|
}
|
|
|
|
prevCell = thisCell;
|
|
prevFace = thisFace;
|
|
}
|
|
|
|
if (hasMultipleFaces)
|
|
{
|
|
nMultipleCells++;
|
|
}
|
|
}
|
|
|
|
reduce(error, orOp<bool>());
|
|
reduce(nMultipleCells, sumOp<label>());
|
|
|
|
if ((debug || report) && nMultipleCells > 0)
|
|
{
|
|
Info<< " <<Found " << nMultipleCells
|
|
<< " neighbouring cells with multiple inbetween faces." << endl;
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Faces not in upper triangular order." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Upper triangular ordering OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkCellsZipUp
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkCellsZipUp("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking topological cell openness" << endl;
|
|
}
|
|
|
|
label nOpenCells = 0;
|
|
|
|
const faceList& f = faces();
|
|
const cellList& c = cells();
|
|
|
|
forAll(c, cellI)
|
|
{
|
|
const labelList& curFaces = c[cellI];
|
|
|
|
const edgeList cellEdges = c[cellI].edges(f);
|
|
|
|
labelList edgeUsage(cellEdges.size(), 0);
|
|
|
|
forAll(curFaces, faceI)
|
|
{
|
|
edgeList curFaceEdges = f[curFaces[faceI]].edges();
|
|
|
|
forAll(curFaceEdges, faceEdgeI)
|
|
{
|
|
const edge& curEdge = curFaceEdges[faceEdgeI];
|
|
|
|
forAll(cellEdges, cellEdgeI)
|
|
{
|
|
if (cellEdges[cellEdgeI] == curEdge)
|
|
{
|
|
edgeUsage[cellEdgeI]++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
edgeList singleEdges(cellEdges.size());
|
|
label nSingleEdges = 0;
|
|
|
|
forAll(edgeUsage, edgeI)
|
|
{
|
|
if (edgeUsage[edgeI] == 1)
|
|
{
|
|
singleEdges[nSingleEdges] = cellEdges[edgeI];
|
|
nSingleEdges++;
|
|
}
|
|
else if (edgeUsage[edgeI] != 2)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cellI);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nSingleEdges > 0)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(cellI);
|
|
}
|
|
|
|
nOpenCells++;
|
|
}
|
|
}
|
|
|
|
reduce(nOpenCells, sumOp<label>());
|
|
|
|
if (nOpenCells > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Open cells found, number of cells: " << nOpenCells
|
|
<< ". This problem may be fixable using the zipUpMesh utility."
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Topological cell zip-up check OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// Vertices of face within point range and unique.
|
|
bool Foam::primitiveMesh::checkFaceVertices
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceVertices("
|
|
<< "const bool, labelHashSet*) const: "
|
|
<< "checking face vertices" << endl;
|
|
}
|
|
|
|
// Check that all vertex labels are valid
|
|
const faceList& f = faces();
|
|
|
|
label nErrorFaces = 0;
|
|
|
|
forAll(f, fI)
|
|
{
|
|
const face& curFace = f[fI];
|
|
|
|
if (min(curFace) < 0 || max(curFace) > nPoints())
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(fI);
|
|
}
|
|
|
|
nErrorFaces++;
|
|
}
|
|
|
|
// Uniqueness of vertices
|
|
labelHashSet facePoints(2*curFace.size());
|
|
|
|
forAll(curFace, fp)
|
|
{
|
|
bool inserted = facePoints.insert(curFace[fp]);
|
|
|
|
if (!inserted)
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(fI);
|
|
}
|
|
|
|
nErrorFaces++;
|
|
}
|
|
}
|
|
}
|
|
|
|
reduce(nErrorFaces, sumOp<label>());
|
|
|
|
if (nErrorFaces > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Faces with invalid vertex labels found, "
|
|
<< " number of faces: " << nErrorFaces << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Face vertices OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkPoints
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkPoints"
|
|
<< "(const bool, labelHashSet*) const: "
|
|
<< "checking points" << endl;
|
|
}
|
|
|
|
label nFaceErrors = 0;
|
|
label nCellErrors = 0;
|
|
|
|
const labelListList& pf = pointFaces();
|
|
|
|
forAll(pf, pointI)
|
|
{
|
|
if (pf[pointI].empty())
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(pointI);
|
|
}
|
|
|
|
nFaceErrors++;
|
|
}
|
|
}
|
|
|
|
|
|
forAll(pf, pointI)
|
|
{
|
|
const labelList& pc = pointCells(pointI);
|
|
|
|
if (pc.empty())
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(pointI);
|
|
}
|
|
|
|
nCellErrors++;
|
|
}
|
|
}
|
|
|
|
reduce(nFaceErrors, sumOp<label>());
|
|
reduce(nCellErrors, sumOp<label>());
|
|
|
|
if (nFaceErrors > 0 || nCellErrors > 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " ***Unused points found in the mesh, "
|
|
"number unused by faces: " << nFaceErrors
|
|
<< " number unused by cells: " << nCellErrors
|
|
<< endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Point usage OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// Check if all points on face are shared between faces.
|
|
bool Foam::primitiveMesh::checkDuplicateFaces
|
|
(
|
|
const label faceI,
|
|
const Map<label>& nCommonPoints,
|
|
label& nBaffleFaces,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
bool error = false;
|
|
|
|
forAllConstIter(Map<label>, nCommonPoints, iter)
|
|
{
|
|
label nbFaceI = iter.key();
|
|
label nCommon = iter();
|
|
|
|
const face& curFace = faces()[faceI];
|
|
const face& nbFace = faces()[nbFaceI];
|
|
|
|
if (nCommon == nbFace.size() || nCommon == curFace.size())
|
|
{
|
|
if (nbFace.size() != curFace.size())
|
|
{
|
|
error = true;
|
|
}
|
|
else
|
|
{
|
|
nBaffleFaces++;
|
|
}
|
|
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
setPtr->insert(nbFaceI);
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
// Check that shared points are in consecutive order.
|
|
bool Foam::primitiveMesh::checkCommonOrder
|
|
(
|
|
const label faceI,
|
|
const Map<label>& nCommonPoints,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
bool error = false;
|
|
|
|
forAllConstIter(Map<label>, nCommonPoints, iter)
|
|
{
|
|
label nbFaceI = iter.key();
|
|
label nCommon = iter();
|
|
|
|
const face& curFace = faces()[faceI];
|
|
const face& nbFace = faces()[nbFaceI];
|
|
|
|
if
|
|
(
|
|
nCommon >= 2
|
|
&& nCommon != nbFace.size()
|
|
&& nCommon != curFace.size()
|
|
)
|
|
{
|
|
forAll(curFace, fp)
|
|
{
|
|
// Get the index in the neighbouring face shared with curFace
|
|
label nb = findIndex(nbFace, curFace[fp]);
|
|
|
|
if (nb != -1)
|
|
{
|
|
|
|
// Check the whole face from nb onwards for shared vertices
|
|
// with neighbouring face. Rule is that any shared vertices
|
|
// should be consecutive on both faces i.e. if they are
|
|
// vertices fp,fp+1,fp+2 on one face they should be
|
|
// vertices nb, nb+1, nb+2 (or nb+2, nb+1, nb) on the
|
|
// other face.
|
|
|
|
|
|
// Vertices before and after on curFace
|
|
label fpPlus1 = curFace.fcIndex(fp);
|
|
label fpMin1 = curFace.rcIndex(fp);
|
|
|
|
// Vertices before and after on nbFace
|
|
label nbPlus1 = nbFace.fcIndex(nb);
|
|
label nbMin1 = nbFace.rcIndex(nb);
|
|
|
|
// Find order of walking by comparing next points on both
|
|
// faces.
|
|
label curInc = labelMax;
|
|
label nbInc = labelMax;
|
|
|
|
if (nbFace[nbPlus1] == curFace[fpPlus1])
|
|
{
|
|
curInc = 1;
|
|
nbInc = 1;
|
|
}
|
|
else if (nbFace[nbPlus1] == curFace[fpMin1])
|
|
{
|
|
curInc = -1;
|
|
nbInc = 1;
|
|
}
|
|
else if (nbFace[nbMin1] == curFace[fpMin1])
|
|
{
|
|
curInc = -1;
|
|
nbInc = -1;
|
|
}
|
|
else
|
|
{
|
|
curInc = 1;
|
|
nbInc = -1;
|
|
}
|
|
|
|
|
|
// Pass1: loop until start of common vertices found.
|
|
label curNb = nb;
|
|
label curFp = fp;
|
|
|
|
do
|
|
{
|
|
curFp += curInc;
|
|
|
|
if (curFp >= curFace.size())
|
|
{
|
|
curFp = 0;
|
|
}
|
|
else if (curFp < 0)
|
|
{
|
|
curFp = curFace.size()-1;
|
|
}
|
|
|
|
curNb += nbInc;
|
|
|
|
if (curNb >= nbFace.size())
|
|
{
|
|
curNb = 0;
|
|
}
|
|
else if (curNb < 0)
|
|
{
|
|
curNb = nbFace.size()-1;
|
|
}
|
|
} while (curFace[curFp] == nbFace[curNb]);
|
|
|
|
|
|
// Pass2: check equality walking from curFp, curNb
|
|
// in opposite order.
|
|
|
|
curInc = -curInc;
|
|
nbInc = -nbInc;
|
|
|
|
for (label commonI = 0; commonI < nCommon; commonI++)
|
|
{
|
|
curFp += curInc;
|
|
|
|
if (curFp >= curFace.size())
|
|
{
|
|
curFp = 0;
|
|
}
|
|
else if (curFp < 0)
|
|
{
|
|
curFp = curFace.size()-1;
|
|
}
|
|
|
|
curNb += nbInc;
|
|
|
|
if (curNb >= nbFace.size())
|
|
{
|
|
curNb = 0;
|
|
}
|
|
else if (curNb < 0)
|
|
{
|
|
curNb = nbFace.size()-1;
|
|
}
|
|
|
|
if (curFace[curFp] != nbFace[curNb])
|
|
{
|
|
if (setPtr)
|
|
{
|
|
setPtr->insert(faceI);
|
|
setPtr->insert(nbFaceI);
|
|
}
|
|
|
|
error = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Done the curFace - nbFace combination.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
// Checks common vertices between faces. If more than 2 they should be
|
|
// consecutive on both faces.
|
|
bool Foam::primitiveMesh::checkFaceFaces
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkFaceFaces(const bool, labelHashSet*)"
|
|
<< " const: " << "checking face-face connectivity" << endl;
|
|
}
|
|
|
|
const labelListList& pf = pointFaces();
|
|
|
|
label nBaffleFaces = 0;
|
|
label nErrorDuplicate = 0;
|
|
label nErrorOrder = 0;
|
|
Map<label> nCommonPoints(100);
|
|
|
|
for (label faceI = 0; faceI < nFaces(); faceI++)
|
|
{
|
|
const face& curFace = faces()[faceI];
|
|
|
|
// Calculate number of common points between current faceI and
|
|
// neighbouring face. Store on map.
|
|
nCommonPoints.clear();
|
|
|
|
forAll(curFace, fp)
|
|
{
|
|
label pointI = curFace[fp];
|
|
|
|
const labelList& nbs = pf[pointI];
|
|
|
|
forAll(nbs, nbI)
|
|
{
|
|
label nbFaceI = nbs[nbI];
|
|
|
|
if (faceI < nbFaceI)
|
|
{
|
|
// Only check once for each combination of two faces.
|
|
|
|
Map<label>::iterator fnd = nCommonPoints.find(nbFaceI);
|
|
|
|
if (fnd == nCommonPoints.end())
|
|
{
|
|
// First common vertex found.
|
|
nCommonPoints.insert(nbFaceI, 1);
|
|
}
|
|
else
|
|
{
|
|
fnd()++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Perform various checks on common points
|
|
|
|
// Check all vertices shared (duplicate point)
|
|
if (checkDuplicateFaces(faceI, nCommonPoints, nBaffleFaces, setPtr))
|
|
{
|
|
nErrorDuplicate++;
|
|
}
|
|
|
|
// Check common vertices are consecutive on both faces
|
|
if (checkCommonOrder(faceI, nCommonPoints, setPtr))
|
|
{
|
|
nErrorOrder++;
|
|
}
|
|
}
|
|
|
|
reduce(nBaffleFaces, sumOp<label>());
|
|
reduce(nErrorDuplicate, sumOp<label>());
|
|
reduce(nErrorOrder, sumOp<label>());
|
|
|
|
if (nBaffleFaces)
|
|
{
|
|
Info<< " Number of identical duplicate faces (baffle faces): "
|
|
<< nBaffleFaces << endl;
|
|
}
|
|
|
|
if (nErrorDuplicate > 0 || nErrorOrder > 0)
|
|
{
|
|
// These are actually warnings, not errors.
|
|
if (nErrorDuplicate > 0)
|
|
{
|
|
Info<< " <<Number of duplicate (not baffle) faces found: "
|
|
<< nErrorDuplicate
|
|
<< ". This might indicate a problem." << endl;
|
|
}
|
|
|
|
if (nErrorOrder > 0)
|
|
{
|
|
Info<< " <<Number of faces with non-consecutive shared points: "
|
|
<< nErrorOrder << ". This might indicate a problem." << endl;
|
|
}
|
|
|
|
return false; //return true;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Face-face connectivity OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
|
|
|
bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const
|
|
{
|
|
return checkClosedBoundary(faceAreas(), report, PackedBoolList(0));
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkClosedCells
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr,
|
|
labelHashSet* aspectSetPtr,
|
|
const Vector<label>& solutionD
|
|
) const
|
|
{
|
|
return checkClosedCells
|
|
(
|
|
faceAreas(),
|
|
cellVolumes(),
|
|
report,
|
|
setPtr,
|
|
aspectSetPtr,
|
|
solutionD
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceAreas
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFaceAreas
|
|
(
|
|
faceAreas(),
|
|
report,
|
|
false, // detailedReport,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkCellVolumes
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkCellVolumes
|
|
(
|
|
cellVolumes(),
|
|
report,
|
|
false, // detailedReport,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceOrthogonality
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFaceOrthogonality
|
|
(
|
|
faceAreas(),
|
|
cellCentres(),
|
|
report,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFacePyramids
|
|
(
|
|
const bool report,
|
|
const scalar minPyrVol,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFacePyramids
|
|
(
|
|
points(),
|
|
cellCentres(),
|
|
report,
|
|
false, // detailedReport,
|
|
minPyrVol,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceSkewness
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFaceSkewness
|
|
(
|
|
points(),
|
|
faceCentres(),
|
|
faceAreas(),
|
|
cellCentres(),
|
|
report,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceAngles
|
|
(
|
|
const bool report,
|
|
const scalar maxDeg,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFaceAngles
|
|
(
|
|
points(),
|
|
faceAreas(),
|
|
report,
|
|
maxDeg,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkFaceFlatness
|
|
(
|
|
const bool report,
|
|
const scalar warnFlatness,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkFaceFlatness
|
|
(
|
|
points(),
|
|
faceCentres(),
|
|
faceAreas(),
|
|
report,
|
|
warnFlatness,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkConcaveCells
|
|
(
|
|
const bool report,
|
|
labelHashSet* setPtr
|
|
) const
|
|
{
|
|
return checkConcaveCells
|
|
(
|
|
faceAreas(),
|
|
faceCentres(),
|
|
report,
|
|
setPtr
|
|
);
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkTopology(const bool report) const
|
|
{
|
|
label noFailedChecks = 0;
|
|
|
|
if (checkPoints(report)) noFailedChecks++;
|
|
if (checkUpperTriangular(report)) noFailedChecks++;
|
|
if (checkCellsZipUp(report)) noFailedChecks++;
|
|
if (checkFaceVertices(report)) noFailedChecks++;
|
|
if (checkFaceFaces(report)) noFailedChecks++;
|
|
|
|
if (noFailedChecks == 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Mesh topology OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Failed " << noFailedChecks
|
|
<< " mesh topology checks." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkGeometry(const bool report) const
|
|
{
|
|
label noFailedChecks = 0;
|
|
|
|
if (checkClosedBoundary(report)) noFailedChecks++;
|
|
if (checkClosedCells(report)) noFailedChecks++;
|
|
if (checkFaceAreas(report)) noFailedChecks++;
|
|
if (checkCellVolumes(report)) noFailedChecks++;
|
|
if (checkFaceOrthogonality(report)) noFailedChecks++;
|
|
if (checkFacePyramids(report)) noFailedChecks++;
|
|
if (checkFaceSkewness(report)) noFailedChecks++;
|
|
|
|
if (noFailedChecks == 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Mesh geometry OK." << endl;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Failed " << noFailedChecks
|
|
<< " mesh geometry checks." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
bool Foam::primitiveMesh::checkMesh(const bool report) const
|
|
{
|
|
if (debug)
|
|
{
|
|
Info<< "bool primitiveMesh::checkMesh(const bool report) const: "
|
|
<< "checking primitiveMesh" << endl;
|
|
}
|
|
|
|
label noFailedChecks = checkTopology(report) + checkGeometry(report);
|
|
|
|
if (noFailedChecks == 0)
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< "Mesh OK." << endl;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (debug || report)
|
|
{
|
|
Info<< " Failed " << noFailedChecks
|
|
<< " mesh checks." << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
Foam::scalar Foam::primitiveMesh::setClosedThreshold(const scalar val)
|
|
{
|
|
scalar prev = closedThreshold_;
|
|
closedThreshold_ = val;
|
|
|
|
return prev;
|
|
}
|
|
|
|
|
|
Foam::scalar Foam::primitiveMesh::setAspectThreshold(const scalar val)
|
|
{
|
|
scalar prev = aspectThreshold_;
|
|
aspectThreshold_ = val;
|
|
|
|
return prev;
|
|
}
|
|
|
|
|
|
Foam::scalar Foam::primitiveMesh::setNonOrthThreshold(const scalar val)
|
|
{
|
|
scalar prev = nonOrthThreshold_;
|
|
nonOrthThreshold_ = val;
|
|
|
|
return prev;
|
|
}
|
|
|
|
|
|
Foam::scalar Foam::primitiveMesh::setSkewThreshold(const scalar val)
|
|
{
|
|
scalar prev = skewThreshold_;
|
|
skewThreshold_ = val;
|
|
|
|
return prev;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|