diff --git a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C index d964ac12cc0607feae48d7b56a5c351dac92614b..ef521a257d2238f164acab87bf6e4307d8c8218a 100644 --- a/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C +++ b/applications/utilities/mesh/manipulation/checkMesh/checkGeometry.C @@ -803,7 +803,7 @@ Foam::label Foam::checkGeometry(const polyMesh& mesh, const bool allGeometry) if (allGeometry) { cellSet cells(mesh, "underdeterminedCells", mesh.nCells()/100); - if (mesh.checkCellDeterminant(true, &cells, mesh.geometricD())) + if (mesh.checkCellDeterminant(true, &cells)) { noFailedChecks++; diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files index 1cd7f37fe88d62c3695395e9944b02fd89e9bb56..55208034a17659f7e023b7ad980f38cb131332ec 100644 --- a/src/OpenFOAM/Make/files +++ b/src/OpenFOAM/Make/files @@ -425,6 +425,10 @@ $(polyMesh)/polyMeshInitMesh.C $(polyMesh)/polyMeshClear.C $(polyMesh)/polyMeshUpdate.C +polyMeshCheck = $(polyMesh)/polyMeshCheck +$(polyMeshCheck)/polyMeshCheck.C +$(polyMeshCheck)/polyMeshTools.C + primitiveMesh = meshes/primitiveMesh $(primitiveMesh)/primitiveMesh.C $(primitiveMesh)/primitiveMeshCellCells.C @@ -445,9 +449,9 @@ $(primitiveMesh)/primitiveMeshCalcCellShapes.C primitiveMeshCheck = $(primitiveMesh)/primitiveMeshCheck $(primitiveMeshCheck)/primitiveMeshCheck.C -$(primitiveMeshCheck)/primitiveMeshCheckMotion.C $(primitiveMeshCheck)/primitiveMeshCheckPointNearness.C $(primitiveMeshCheck)/primitiveMeshCheckEdgeLength.C +$(primitiveMeshCheck)/primitiveMeshTools.C primitivePatch = $(primitiveMesh)/primitivePatch $(primitivePatch)/patchZones.C diff --git a/src/OpenFOAM/meshes/polyMesh/polyMesh.C b/src/OpenFOAM/meshes/polyMesh/polyMesh.C index 6388190bf6e5fd10cd9fd38eae5d004d3864bcbd..abe537320f9d0d5b92b2c2589c6047eac269d199 100644 --- a/src/OpenFOAM/meshes/polyMesh/polyMesh.C +++ b/src/OpenFOAM/meshes/polyMesh/polyMesh.C @@ -1129,7 +1129,7 @@ Foam::tmp<Foam::scalarField> Foam::polyMesh::movePoints if (debug) { // Check mesh motion - if (primitiveMesh::checkMeshMotion(points_, true)) + if (checkMeshMotion(points_, true)) { Info<< "tmp<scalarField> polyMesh::movePoints" << "(const pointField&) : " diff --git a/src/OpenFOAM/meshes/polyMesh/polyMesh.H b/src/OpenFOAM/meshes/polyMesh/polyMesh.H index 09c046feb840f098c5a4fb80fd3533b13eb84987..e4c189767192ae87ddddc131ea0ea3e0d1068353 100644 --- a/src/OpenFOAM/meshes/polyMesh/polyMesh.H +++ b/src/OpenFOAM/meshes/polyMesh/polyMesh.H @@ -34,6 +34,7 @@ SourceFiles polyMeshFromShapeMesh.C polyMeshIO.C polyMeshUpdate.C + polyMeshCheck.C \*---------------------------------------------------------------------------*/ @@ -224,7 +225,45 @@ private: cellList& cells ); + // Geometry checks + //- Check non-orthogonality + bool checkFaceOrthogonality + ( + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + const bool detailedReport, + labelHashSet* setPtr + ) const; + + //- Check face skewness + bool checkFaceSkewness + ( + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + const bool detailedReport, + labelHashSet* setPtr + ) const; + + bool checkEdgeAlignment + ( + const pointField& p, + const bool report, + const Vector<label>& directions, + labelHashSet* setPtr + ) const; + + bool checkCellDeterminant + ( + const vectorField& faceAreas, + const bool report, + labelHashSet* setPtr, + const Vector<label>& meshD + ) const; public: @@ -535,6 +574,48 @@ public: void removeFiles() const; + // Geometric checks. Selectively override primitiveMesh functionality. + + //- Check boundary for closedness + virtual bool checkClosedBoundary(const bool report = false) const; + + //- Check non-orthogonality + virtual bool checkFaceOrthogonality + ( + const bool report = false, + labelHashSet* setPtr = NULL + ) const; + + //- Check face skewness + virtual bool checkFaceSkewness + ( + const bool report = false, + labelHashSet* setPtr = NULL + ) const; + + //- Check edge alignment for 1D/2D cases + virtual bool checkEdgeAlignment + ( + const bool report, + const Vector<label>& directions, + labelHashSet* setPtr + ) const; + + virtual bool checkCellDeterminant + ( + const bool report, + labelHashSet* setPtr + ) const; + + //- Check mesh motion for correctness given motion points + virtual bool checkMeshMotion + ( + const pointField& newPoints, + const bool report = false, + const bool detailedReport = false + ) const; + + // Helper functions //- Find the cell, tetFaceI and tetPtI for the given position diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshCheck.C b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshCheck.C new file mode 100644 index 0000000000000000000000000000000000000000..748c7da21a63f0b9d25acb21f34d5df839415ee0 --- /dev/null +++ b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshCheck.C @@ -0,0 +1,679 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2012 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 "polyMesh.H" +#include "polyMeshTools.H" +#include "unitConversion.H" +#include "syncTools.H" + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::polyMesh::checkFaceOrthogonality +( + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + const bool detailedReport, + labelHashSet* setPtr +) const +{ + if (debug) + { + Info<< "bool polyMesh::checkFaceOrthogonality(" + << "const bool, labelHashSet*) const: " + << "checking mesh non-orthogonality" << endl; + } + + const labelList& own = faceOwner(); + const labelList& nei = faceNeighbour(); + + // Calculate orthogonality for all internal and coupled boundary faces + // (1 for uncoupled boundary faces) + tmp<scalarField> tortho = polyMeshTools::faceOrthogonality + ( + *this, + fAreas, + cellCtrs + ); + const scalarField& ortho = tortho(); + + // Severe nonorthogonality threshold + const scalar severeNonorthogonalityThreshold = + ::cos(degToRad(primitiveMesh::nonOrthThreshold_)); + + + scalar minDDotS = GREAT; + scalar sumDDotS = 0.0; + label nSummed = 0; + label severeNonOrth = 0; + label errorNonOrth = 0; + + + // Statistics only for internal and masters of coupled faces + PackedBoolList isMasterFace(syncTools::getInternalOrMasterFaces(*this)); + + forAll(ortho, faceI) + { + if (ortho[faceI] < severeNonorthogonalityThreshold) + { + if (ortho[faceI] > SMALL) + { + if (setPtr) + { + setPtr->insert(faceI); + } + + severeNonOrth++; + } + else + { + // Error : non-ortho too large + if (setPtr) + { + setPtr->insert(faceI); + } + if (detailedReport && errorNonOrth == 0) + { + // Non-orthogonality greater than 90 deg + WarningIn + ( + "polyMesh::checkFaceOrthogonality" + "(const pointField&, const bool) const" + ) << "Severe non-orthogonality for face " + << faceI + << " between cells " << own[faceI] + << " and " << nei[faceI] + << ": Angle = " << radToDeg(::acos(ortho[faceI])) + << " deg." << endl; + } + + errorNonOrth++; + } + } + + if (isMasterFace[faceI]) + { + minDDotS = min(minDDotS, ortho[faceI]); + sumDDotS += ortho[faceI]; + nSummed++; + } + } + + reduce(minDDotS, minOp<scalar>()); + reduce(sumDDotS, sumOp<scalar>()); + reduce(nSummed, sumOp<label>()); + reduce(severeNonOrth, sumOp<label>()); + reduce(errorNonOrth, sumOp<label>()); + + if (debug || report) + { + if (nSummed > 0) + { + if (debug || report) + { + Info<< " Mesh non-orthogonality Max: " + << radToDeg(::acos(minDDotS)) + << " average: " << radToDeg(::acos(sumDDotS/nSummed)) + << 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::polyMesh::checkFaceSkewness +( + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + const bool detailedReport, + labelHashSet* setPtr +) const +{ + if (debug) + { + Info<< "bool polyMesh::checkFaceSkewnesss(" + << "const bool, labelHashSet*) const: " + << "checking face skewness" << endl; + } + + const labelList& own = faceOwner(); + const labelList& nei = faceNeighbour(); + + // Warn if the skew correction vector is more than skewWarning times + // larger than the face area vector + + tmp<scalarField> tskew = polyMeshTools::faceSkewness + ( + *this, + points, + fCtrs, + fAreas, + cellCtrs + ); + const scalarField& skew = tskew(); + + scalar maxSkew = max(skew); + label nWarnSkew = 0; + + // Statistics only for all faces except slave coupled faces + PackedBoolList isMasterFace(syncTools::getMasterFaces(*this)); + + forAll(skew, 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 (skew[faceI] > skewThreshold_) + { + if (setPtr) + { + setPtr->insert(faceI); + } + if (detailedReport && nWarnSkew == 0) + { + // Non-orthogonality greater than 90 deg + if (isInternalFace(faceI)) + { + WarningIn + ( + "polyMesh::checkFaceSkewnesss" + "(const pointField&, const bool) const" + ) << "Severe skewness " << skew[faceI] + << " for face " << faceI + << " between cells " << own[faceI] + << " and " << nei[faceI]; + } + else + { + WarningIn + ( + "polyMesh::checkFaceSkewnesss" + "(const pointField&, const bool) const" + ) << "Severe skewness " << skew[faceI] + << " for boundary face " << faceI + << " on cell " << own[faceI]; + } + } + + if (isMasterFace[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 1D/2Dness of edges. Gets passed the non-empty directions and +// checks all edges in the mesh whether they: +// - have no component in a non-empty direction or +// - are only in a singe non-empty direction. +// Empty direction info is passed in as a vector of labels (synchronised) +// which are 1 if the direction is non-empty, 0 if it is. +bool Foam::polyMesh::checkEdgeAlignment +( + const pointField& p, + const bool report, + const Vector<label>& directions, + labelHashSet* setPtr +) const +{ + if (debug) + { + Info<< "bool polyMesh::checkEdgeAlignment(" + << "const bool, const Vector<label>&, labelHashSet*) const: " + << "checking edge alignment" << endl; + } + + label nDirs = 0; + for (direction cmpt=0; cmpt<vector::nComponents; cmpt++) + { + if (directions[cmpt] == 1) + { + nDirs++; + } + else if (directions[cmpt] != 0) + { + FatalErrorIn + ( + "polyMesh::checkEdgeAlignment" + "(const bool, const Vector<label>&, labelHashSet*)" + ) << "directions should contain 0 or 1 but is now " << directions + << exit(FatalError); + } + } + + if (nDirs == vector::nComponents) + { + return false; + } + + + const faceList& fcs = faces(); + + EdgeMap<label> edgesInError; + + 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) + { + edgesInError.insert(edge(p0, p1), faceI); + } + } + else if (nEmptyDirs > 1) + { + // Always an error + edgesInError.insert(edge(p0, p1), faceI); + } + } + } + } + } + + label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>()); + + if (nErrorEdges > 0) + { + if (debug || report) + { + Info<< " ***Number of edges not aligned with or perpendicular to " + << "non-empty directions: " << nErrorEdges << endl; + } + + if (setPtr) + { + setPtr->resize(2*edgesInError.size()); + forAllConstIter(EdgeMap<label>, edgesInError, iter) + { + setPtr->insert(iter.key()[0]); + setPtr->insert(iter.key()[1]); + } + } + + return true; + } + else + { + if (debug || report) + { + Info<< " All edges aligned with or perpendicular to " + << "non-empty directions." << endl; + } + return false; + } +} + + +bool Foam::polyMesh::checkCellDeterminant +( + const vectorField& faceAreas, + const bool report, + labelHashSet* setPtr, + const Vector<label>& meshD +) const +{ + if (debug) + { + Info<< "bool polyMesh::checkCellDeterminant(const bool" + << ", labelHashSet*) const: " + << "checking for under-determined cells" << endl; + } + + // Determine number of dimensions and (for 2D) missing dimension + label nDims = 0; + label twoD = -1; + for (direction dir = 0; dir < vector::nComponents; dir++) + { + if (meshD[dir] == 1) + { + nDims++; + } + else + { + twoD = dir; + } + } + + + tmp<scalarField> tcellDeterminant = primitiveMeshTools::cellDeterminant + ( + *this, + meshD, + faceAreas, + syncTools::getInternalOrCoupledFaces(*this) + ); + scalarField& cellDeterminant = tcellDeterminant(); + + + label nErrorCells = 0; + scalar minDet = min(cellDeterminant); + scalar sumDet = sum(cellDeterminant); + + forAll (cellDeterminant, cellI) + { + if (cellDeterminant[cellI] < 1e-3) + { + if (setPtr) + { + setPtr->insert(cellI); + } + + nErrorCells++; + } + } + + reduce(nErrorCells, sumOp<label>()); + reduce(minDet, minOp<scalar>()); + reduce(sumDet, sumOp<scalar>()); + label nSummed = returnReduce(cellDeterminant.size(), sumOp<label>()); + + if (debug || report) + { + if (nSummed > 0) + { + Info<< " Cell determinant (wellposedness) : minimum: " << minDet + << " average: " << sumDet/nSummed + << endl; + } + } + + if (nErrorCells > 0) + { + if (debug || report) + { + Info<< " ***Cells with small determinant found, number of cells: " + << nErrorCells << endl; + } + + return true; + } + else + { + if (debug || report) + { + Info<< " Cell determinant check OK." << endl; + } + + return false; + } + + return false; +} + + +bool Foam::polyMesh::checkClosedBoundary(const bool report) const +{ + return primitiveMesh::checkClosedBoundary + ( + faceAreas(), + report, + syncTools::getInternalOrCoupledFaces(*this) + ); +} + + +bool Foam::polyMesh::checkFaceOrthogonality +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkFaceOrthogonality + ( + faceAreas(), + cellCentres(), + report, + false, // detailedReport + setPtr + ); +} + + +bool Foam::polyMesh::checkFaceSkewness +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkFaceSkewness + ( + points(), + faceCentres(), + faceAreas(), + cellCentres(), + report, + false, // detailedReport + setPtr + ); +} + + +bool Foam::polyMesh::checkEdgeAlignment +( + const bool report, + const Vector<label>& directions, + labelHashSet* setPtr +) const +{ + return checkEdgeAlignment + ( + points(), + report, + directions, + setPtr + ); +} + + +bool Foam::polyMesh::checkCellDeterminant +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkCellDeterminant + ( + faceAreas(), + report, + setPtr, + geometricD() + ); +} + + +bool Foam::polyMesh::checkMeshMotion +( + const pointField& newPoints, + const bool report, + const bool detailedReport +) const +{ + if (debug || report) + { + Pout<< "bool polyMesh::checkMeshMotion(" + << "const pointField&, const bool, const bool) const: " + << "checking mesh motion" << endl; + } + + vectorField fCtrs(nFaces()); + vectorField fAreas(nFaces()); + + makeFaceCentresAndAreas(newPoints, fCtrs, fAreas); + + // Check cell volumes and calculate new cell centres + vectorField cellCtrs(nCells()); + scalarField cellVols(nCells()); + + makeCellCentresAndVols(fCtrs, fAreas, cellCtrs, cellVols); + + // Check cell volumes + bool error = checkCellVolumes + ( + cellVols, // vols + report, // report + detailedReport, // detailedReport + NULL // setPtr + ); + + + // Check face areas + bool areaError = checkFaceAreas + ( + faceAreas(), + report, // report + detailedReport, // detailedReport, + NULL // setPtr + ); + error = error || areaError; + + + // Check pyramid volumes + bool pyrVolError = checkFacePyramids + ( + newPoints, + cellCtrs, + report, // report, + detailedReport, // detailedReport, + -SMALL, // minPyrVol + NULL // setPtr + ); + error = error || pyrVolError; + + + // Check face non-orthogonality + bool nonOrthoError = checkFaceOrthogonality + ( + fAreas, + cellCtrs, + report, // report + detailedReport, // detailedReport + NULL // setPtr + ); + error = error || nonOrthoError; + + + if (!error && (debug || report)) + { + Pout<< "Mesh motion check OK." << endl; + } + + return error; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.C b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.C new file mode 100644 index 0000000000000000000000000000000000000000..fc0739e79faf42d048b133d79b2a97f572b99388 --- /dev/null +++ b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.C @@ -0,0 +1,190 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2012 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 "polyMeshTools.H" +#include "syncTools.H" +#include "pyramidPointFaceRef.H" +#include "primitiveMeshTools.H" +#include "polyMeshTools.H" + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::tmp<Foam::scalarField> Foam::polyMeshTools::faceOrthogonality +( + const polyMesh& mesh, + const vectorField& areas, + const vectorField& cc +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + + tmp<scalarField> tortho(new scalarField(mesh.nFaces(), 1.0)); + scalarField& ortho = tortho(); + + // Internal faces + forAll(nei, faceI) + { + ortho[faceI] = primitiveMeshTools::faceOrthogonality + ( + cc[own[faceI]], + cc[nei[faceI]], + areas[faceI] + ); + } + + + // Coupled faces + + pointField neighbourCc; + syncTools::swapBoundaryCellList(mesh, cc, neighbourCc); + + forAll(pbm, patchI) + { + const polyPatch& pp = pbm[patchI]; + if (pp.coupled()) + { + forAll(pp, i) + { + label faceI = pp.start() + i; + label bFaceI = faceI - mesh.nInternalFaces(); + + ortho[faceI] = primitiveMeshTools::faceOrthogonality + ( + cc[own[faceI]], + neighbourCc[bFaceI], + areas[faceI] + ); + } + } + } + + return tortho; +} + + +Foam::tmp<Foam::scalarField> Foam::polyMeshTools::faceSkewness +( + const polyMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + const faceList& fcs = mesh.faces(); + const polyBoundaryMesh& pbm = mesh.boundaryMesh(); + + tmp<scalarField> tskew(new scalarField(mesh.nFaces())); + scalarField& skew = tskew(); + + forAll(nei, faceI) + { + skew[faceI] = primitiveMeshTools::faceSkewness + ( + mesh, + p, + fCtrs, + fAreas, + + faceI, + cellCtrs[own[faceI]], + cellCtrs[nei[faceI]] + ); + } + + + // Boundary faces: consider them to have only skewness error. + // (i.e. treat as if mirror cell on other side) + + pointField neighbourCc; + syncTools::swapBoundaryCellList(mesh, cellCtrs, neighbourCc); + + forAll(pbm, patchI) + { + const polyPatch& pp = pbm[patchI]; + if (pp.coupled()) + { + forAll(pp, i) + { + label faceI = pp.start() + i; + label bFaceI = faceI - mesh.nInternalFaces(); + + skew[faceI] = primitiveMeshTools::faceSkewness + ( + mesh, + p, + fCtrs, + fAreas, + + faceI, + cellCtrs[own[faceI]], + neighbourCc[bFaceI] + ); + } + } + else + { + forAll(pp, i) + { + label faceI = pp.start() + i; + + vector Cpf = fCtrs[faceI] - cellCtrs[own[faceI]]; + + vector normal = fAreas[faceI]; + normal /= mag(normal) + VSMALL; + vector d = normal*(normal & Cpf); + + + // Skewness vector + vector sv = + Cpf + - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + VSMALL))*d; + vector svHat = sv/(mag(sv) + VSMALL); + + // Normalisation distance calculated as the approximate distance + // from the face centre to the edge of the face in the direction + // of the skewness + scalar fd = 0.4*mag(d) + VSMALL; + const face& f = fcs[faceI]; + forAll(f, pi) + { + fd = max(fd, mag(svHat & (p[f[pi]] - fCtrs[faceI]))); + } + + // Normalised skewness + skew[faceI] = mag(sv)/fd; + } + } + } + + return tskew; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.H b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.H new file mode 100644 index 0000000000000000000000000000000000000000..53c758c3be7967f5a914b39e7e0c12a50bbaf86c --- /dev/null +++ b/src/OpenFOAM/meshes/polyMesh/polyMeshCheck/polyMeshTools.H @@ -0,0 +1,109 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2012 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/>. + +Namespace + Foam::polyMeshTools + +Description + Collection of static functions operating on polyMesh (mainly checks) so + that need access to patch information. + +SourceFiles + polyMeshTools.C + +\*---------------------------------------------------------------------------*/ +#ifndef polyMeshTools_H +#define polyMeshTools_H + +#include "polyMesh.H" +#include "primitiveMeshTools.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Namespace polyMeshTools Declaration +\*---------------------------------------------------------------------------*/ + +class polyMeshTools +: + public primitiveMeshTools +{ + +public: + + //- Generate orthogonality field. (1 for fully orthogonal, < 1 for + // non-orthogonal) + static tmp<scalarField> faceOrthogonality + ( + const polyMesh& mesh, + const vectorField& fAreas, + const vectorField& cellCtrs + ); + +// static tmp<scalarField> faceOrthogonality(const polyMesh& mesh) +// { +// return faceOrthogonality +// ( +// mesh, +// mesh.faceAreas(), +// mesh.cellCentres() +// ); +// } + + //- Generate skewness field + static tmp<scalarField> faceSkewness + ( + const polyMesh& mesh, + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs + ); + +// static tmp<scalarField> faceSkewness(const polyMesh& mesh) +// { +// return faceSkewness +// ( +// mesh, +// mesh.points(), +// mesh.faceCentres(), +// mesh.faceAreas(), +// mesh.cellCentres() +// ); +// } + +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H b/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H index 8c647d8454c565f2432ebb877f9c73a8c703c6ae..c4ab139ca3f99f4b8a9366e9ede3bc7444c76853 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMesh.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation + \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -43,9 +43,6 @@ SourceFiles primitiveMeshEdges.C primitiveMeshCellCentresAndVols.C primitiveMeshFaceCentresAndAreas.C - primitiveMeshEdgeVectors.C - primitiveMeshCheck.C - primitiveMeshCheckMotion.C primitiveMeshFindCell.C \*---------------------------------------------------------------------------*/ @@ -69,6 +66,8 @@ SourceFiles namespace Foam { +class PackedBoolList; + /*---------------------------------------------------------------------------*\ Class primitiveMesh Declaration \*---------------------------------------------------------------------------*/ @@ -225,6 +224,28 @@ class primitiveMesh const labelList& ); +protected: + + // Static data members + + //- Static data to control mesh checking + + //- Cell closedness warning threshold + // set as the fraction of un-closed area to closed area + static scalar closedThreshold_; + + //- Aspect ratio warning threshold + static scalar aspectThreshold_; + + //- Non-orthogonality warning threshold in deg + static scalar nonOrthThreshold_; + + //- Skewness warning threshold + static scalar skewThreshold_; + + //- Threshold where faces are considered coplanar + static scalar planarCosAngle_; + // Geometrical calculations @@ -251,7 +272,7 @@ class primitiveMesh void calcEdgeVectors() const; - // Helper functions for mesh checking + // Mesh checking //- Check if all points on face are shared with another face. bool checkDuplicateFaces @@ -270,29 +291,104 @@ class primitiveMesh labelHashSet* ) const; + //- Check boundary for closedness + bool checkClosedBoundary + ( + const vectorField&, + const bool, + const PackedBoolList& + ) const; - // Static data members + //- Check cells for closedness + bool checkClosedCells + ( + const vectorField& faceAreas, + const scalarField& cellVolumes, + const bool report, + labelHashSet* setPtr, + labelHashSet* aspectSetPtr, + const Vector<label>& meshD + ) const; - //- Static data to control mesh checking + //- Check for negative face areas + bool checkFaceAreas + ( + const vectorField& faceAreas, + const bool report, + const bool detailedReport, + labelHashSet* setPtr + ) const; - //- Cell closedness warning threshold - // set as the fraction of un-closed area to closed area - static scalar closedThreshold_; + //- Check for negative cell volumes + bool checkCellVolumes + ( + const scalarField& vols, + const bool report, + const bool detailedReport, + labelHashSet* setPtr + ) const; - //- Aspect ratio warning threshold - static scalar aspectThreshold_; + //- Check for non-orthogonality + bool checkFaceOrthogonality + ( + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + labelHashSet* setPtr + ) const; - //- Non-orthogonality warning threshold in deg - static scalar nonOrthThreshold_; + //- Check face pyramid volume + bool checkFacePyramids + ( + const pointField& points, + const vectorField& ctrs, + const bool report, + const bool detailedReport, + const scalar minPyrVol, + labelHashSet* setPtr + ) const; - //- Skewness warning threshold - static scalar skewThreshold_; + //- Check face skewness + bool checkFaceSkewness + ( + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs, + const bool report, + labelHashSet* setPtr + ) const; - //- Threshold where faces are considered coplanar - static scalar planarCosAngle_; + //- Check face angles + bool checkFaceAngles + ( + const pointField& points, + const vectorField& faceAreas, + const bool report, + const scalar maxDeg, + labelHashSet* setPtr + ) const; + //- Check face warpage + bool checkFaceFlatness + ( + const pointField& points, + const vectorField& faceCentres, + const vectorField& faceAreas, + const bool report, + const scalar warnFlatness, + labelHashSet* setPtr + ) const; + + //- Check for concave cells by the planes of faces + bool checkConcaveCells + ( + const vectorField& fAreas, + const pointField& fCentres, + const bool report, + labelHashSet* setPtr + ) const; -protected: //- Construct null primitiveMesh(); @@ -502,29 +598,36 @@ public: // Topological checks + //- Check face ordering + virtual bool checkUpperTriangular + ( + const bool report = false, + labelHashSet* setPtr = NULL + ) const; + //- Check cell zip-up - bool checkCellsZipUp + virtual bool checkCellsZipUp ( const bool report = false, labelHashSet* setPtr = NULL ) const; //- Check uniqueness of face vertices - bool checkFaceVertices + virtual bool checkFaceVertices ( const bool report = false, labelHashSet* setPtr = NULL ) const; - //- Check face-face connectivity - bool checkFaceFaces + //- Check for unused points + virtual bool checkPoints ( const bool report = false, labelHashSet* setPtr = NULL ) const; - //- Check face ordering - bool checkUpperTriangular + //- Check face-face connectivity + virtual bool checkFaceFaces ( const bool report = false, labelHashSet* setPtr = NULL @@ -534,10 +637,11 @@ public: // Geometric checks //- Check boundary for closedness - bool checkClosedBoundary(const bool report = false) const; + virtual bool checkClosedBoundary(const bool report = false) + const; //- Check cells for closedness - bool checkClosedCells + virtual bool checkClosedCells ( const bool report = false, labelHashSet* setPtr = NULL, @@ -546,28 +650,28 @@ public: ) const; //- Check for negative face areas - bool checkFaceAreas + virtual bool checkFaceAreas ( const bool report = false, labelHashSet* setPtr = NULL ) const; //- Check for negative cell volumes - bool checkCellVolumes + virtual bool checkCellVolumes ( const bool report = false, labelHashSet* setPtr = NULL ) const; //- Check for non-orthogonality - bool checkFaceOrthogonality + virtual bool checkFaceOrthogonality ( const bool report = false, labelHashSet* setPtr = NULL ) const; //- Check face pyramid volume - bool checkFacePyramids + virtual bool checkFacePyramids ( const bool report = false, const scalar minPyrVol = -SMALL, @@ -575,14 +679,14 @@ public: ) const; //- Check face skewness - bool checkFaceSkewness + virtual bool checkFaceSkewness ( const bool report = false, labelHashSet* setPtr = NULL ) const; //- Check face angles - bool checkFaceAngles + virtual bool checkFaceAngles ( const bool report = false, const scalar maxSin = 10, // In degrees @@ -592,31 +696,16 @@ public: //- Check face warpage: decompose face and check ratio between // magnitude of sum of triangle areas and sum of magnitude of // triangle areas. - bool checkFaceFlatness + virtual bool checkFaceFlatness ( const bool report, const scalar warnFlatness, // When to include in set. labelHashSet* setPtr ) const; - //- Check edge alignment for 1D/2D cases - bool checkEdgeAlignment - ( - const bool report, - const Vector<label>& directions, - labelHashSet* setPtr = NULL - ) const; - - //- Check for unused points - bool checkPoints - ( - const bool report = false, - labelHashSet* setPtr = NULL - ) const; - //- Check for point-point-nearness, // e.g. colocated points which may be part of baffles. - bool checkPointNearness + virtual bool checkPointNearness ( const bool report, const scalar reportDistSqr, @@ -624,23 +713,15 @@ public: ) const; //- Check edge length - bool checkEdgeLength + virtual bool checkEdgeLength ( const bool report, const scalar minLenSqr, labelHashSet* setPtr = NULL ) const; - //- Check cell determinant - bool checkCellDeterminant - ( - const bool report = false, - labelHashSet* setPtr = NULL, - const Vector<label>& solutionD = Vector<label>::one - ) const; - //- Check for concave cells by the planes of faces - bool checkConcaveCells + virtual bool checkConcaveCells ( const bool report = false, labelHashSet* setPtr = NULL @@ -649,22 +730,14 @@ public: //- Check mesh topology for correctness. // Returns false for no error. - bool checkTopology(const bool report = false) const; + virtual bool checkTopology(const bool report = false) const; //- Check mesh geometry (& implicitly topology) for correctness. // Returns false for no error. - bool checkGeometry(const bool report = false) const; + virtual bool checkGeometry(const bool report = false) const; //- Check mesh for correctness. Returns false for no error. - bool checkMesh(const bool report = false) const; - - //- Check mesh motion for correctness given motion points - bool checkMeshMotion - ( - const pointField& newPoints, - const bool report = false - ) const; - + virtual bool checkMesh(const bool report = false) const; //- Set the closedness ratio warning threshold static scalar setClosedThreshold(const scalar); diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C index 76fa1aa5d1d38f2c3840c7975fbcc9f03293f206..d805608e4480c11a75c1605a6d5a7b2fe2fb3877 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C @@ -29,6 +29,7 @@ License #include "unitConversion.H" #include "SortableList.H" #include "EdgeMap.H" +#include "primitiveMeshTools.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -41,7 +42,12 @@ Foam::scalar Foam::primitiveMesh::planarCosAngle_ = 1.0e-6; // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // -bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const +bool Foam::primitiveMesh::checkClosedBoundary +( + const vectorField& areas, + const bool report, + const PackedBoolList& internalOrCoupledFaces +) const { if (debug) { @@ -56,12 +62,13 @@ bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const vector sumClosed(vector::zero); scalar sumMagClosedBoundary = 0; - const vectorField& areas = faceAreas(); - for (label faceI = nInternalFaces(); faceI < areas.size(); faceI++) { - sumClosed += areas[faceI]; - sumMagClosedBoundary += mag(areas[faceI]); + if (internalOrCoupledFaces.size() && !internalOrCoupledFaces[faceI]) + { + sumClosed += areas[faceI]; + sumMagClosedBoundary += mag(areas[faceI]); + } } reduce(sumClosed, sumOp<vector>()); @@ -95,6 +102,8 @@ bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const bool Foam::primitiveMesh::checkClosedCells ( + const vectorField& faceAreas, + const scalarField& cellVolumes, const bool report, labelHashSet* setPtr, labelHashSet* aspectSetPtr, @@ -140,66 +149,28 @@ bool Foam::primitiveMesh::checkClosedCells return true; } - // Loop through cell faces and sum up the face area vectors for each cell. - // This should be zero in all vector components - - vectorField sumClosed(nCells(), vector::zero); - vectorField sumMagClosed(nCells(), vector::zero); - const labelList& own = faceOwner(); - const labelList& nei = faceNeighbour(); - const vectorField& areas = faceAreas(); - - forAll(own, faceI) - { - // Add to owner - sumClosed[own[faceI]] += areas[faceI]; - sumMagClosed[own[faceI]] += cmptMag(areas[faceI]); - } - - forAll(nei, faceI) - { - // Subtract from neighbour - sumClosed[nei[faceI]] -= areas[faceI]; - sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]); - } + scalarField openness; + scalarField aspectRatio; + primitiveMeshTools::cellClosedness + ( + *this, + meshD, + faceAreas, + cellVolumes, + openness, + aspectRatio + ); label nOpen = 0; - scalar maxOpennessCell = 0; - + scalar maxOpennessCell = max(openness); label nAspect = 0; - scalar maxAspectRatio = 0; - - const scalarField& vols = cellVolumes(); - - label nDims = 0; - for (direction dir = 0; dir < vector::nComponents; dir++) - { - if (meshD[dir] == 1) - { - nDims++; - } - } - + scalar maxAspectRatio = max(aspectRatio); // Check the sums - forAll(sumClosed, cellI) + forAll(openness, cellI) { - scalar maxOpenness = 0; - - for (direction cmpt=0; cmpt<vector::nComponents; cmpt++) - { - maxOpenness = max - ( - maxOpenness, - mag(sumClosed[cellI][cmpt]) - /(sumMagClosed[cellI][cmpt] + VSMALL) - ); - } - - maxOpennessCell = max(maxOpennessCell, maxOpenness); - - if (maxOpenness > closedThreshold_) + if (openness[cellI] > closedThreshold_) { if (setPtr) { @@ -209,32 +180,7 @@ bool Foam::primitiveMesh::checkClosedCells nOpen++; } - // Calculate the aspect ration as the maximum of Cartesian component - // aspect ratio to the total area hydraulic area aspect ratio - scalar minCmpt = VGREAT; - scalar maxCmpt = -VGREAT; - for (direction dir = 0; dir < vector::nComponents; dir++) - { - if (meshD[dir] == 1) - { - minCmpt = min(minCmpt, sumMagClosed[cellI][dir]); - maxCmpt = max(maxCmpt, sumMagClosed[cellI][dir]); - } - } - - scalar aspectRatio = maxCmpt/(minCmpt + VSMALL); - if (nDims == 3) - { - aspectRatio = max - ( - aspectRatio, - 1.0/6.0*cmptSum(sumMagClosed[cellI])/pow(vols[cellI], 2.0/3.0) - ); - } - - maxAspectRatio = max(maxAspectRatio, aspectRatio); - - if (aspectRatio > aspectThreshold_) + if (aspectRatio[cellI] > aspectThreshold_) { if (aspectSetPtr) { @@ -290,7 +236,9 @@ bool Foam::primitiveMesh::checkClosedCells bool Foam::primitiveMesh::checkFaceAreas ( + const vectorField& faceAreas, const bool report, + const bool detailedReport, labelHashSet* setPtr ) const { @@ -301,7 +249,7 @@ bool Foam::primitiveMesh::checkFaceAreas << "checking face area magnitudes" << endl; } - const scalarField magFaceAreas(mag(faceAreas())); + const scalarField magFaceAreas(mag(faceAreas)); scalar minArea = GREAT; scalar maxArea = -GREAT; @@ -314,6 +262,25 @@ bool Foam::primitiveMesh::checkFaceAreas { 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]); @@ -349,7 +316,9 @@ bool Foam::primitiveMesh::checkFaceAreas bool Foam::primitiveMesh::checkCellVolumes ( + const scalarField& vols, const bool report, + const bool detailedReport, labelHashSet* setPtr ) const { @@ -360,8 +329,6 @@ bool Foam::primitiveMesh::checkCellVolumes << "checking cell volumes" << endl; } - const scalarField& vols = cellVolumes(); - scalar minVolume = GREAT; scalar maxVolume = -GREAT; @@ -375,6 +342,11 @@ bool Foam::primitiveMesh::checkCellVolumes { setPtr->insert(cellI); } + if (detailedReport) + { + Pout<< "Zero or negative cell volume detected for cell " + << cellI << ". Volume = " << vols[cellI] << endl; + } nNegVolCells++; } @@ -416,6 +388,8 @@ bool Foam::primitiveMesh::checkCellVolumes bool Foam::primitiveMesh::checkFaceOrthogonality ( + const vectorField& fAreas, + const vectorField& cellCtrs, const bool report, labelHashSet* setPtr ) const @@ -427,35 +401,33 @@ bool Foam::primitiveMesh::checkFaceOrthogonality << "checking mesh non-orthogonality" << endl; } - // for all internal faces check that the d dot S product is positive - const vectorField& centres = cellCentres(); - const vectorField& areas = faceAreas(); - const labelList& own = faceOwner(); - const labelList& nei = faceNeighbour(); + tmp<scalarField> tortho = primitiveMeshTools::faceOrthogonality + ( + *this, + fAreas, + cellCtrs + ); + const scalarField& ortho = tortho(); // Severe nonorthogonality threshold const scalar severeNonorthogonalityThreshold = ::cos(degToRad(nonOrthThreshold_)); - scalar minDDotS = GREAT; + scalar minDDotS = min(ortho); - scalar sumDDotS = 0; + scalar sumDDotS = sum(ortho); label severeNonOrth = 0; label errorNonOrth = 0; - forAll(nei, faceI) - { - vector d = centres[nei[faceI]] - centres[own[faceI]]; - const vector& s = areas[faceI]; - - scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL); - if (dDotS < severeNonorthogonalityThreshold) + forAll(ortho, faceI) + { + if (ortho[faceI] < severeNonorthogonalityThreshold) { - if (dDotS > SMALL) + if (ortho[faceI] > SMALL) { if (setPtr) { @@ -474,13 +446,6 @@ bool Foam::primitiveMesh::checkFaceOrthogonality errorNonOrth++; } } - - if (dDotS < minDDotS) - { - minDDotS = dDotS; - } - - sumDDotS += dDotS; } reduce(minDDotS, minOp<scalar>()); @@ -490,7 +455,7 @@ bool Foam::primitiveMesh::checkFaceOrthogonality if (debug || report) { - label neiSize = nei.size(); + label neiSize = ortho.size(); reduce(neiSize, sumOp<label>()); if (neiSize > 0) @@ -535,7 +500,10 @@ bool Foam::primitiveMesh::checkFaceOrthogonality bool Foam::primitiveMesh::checkFacePyramids ( + const pointField& points, + const vectorField& ctrs, const bool report, + const bool detailedReport, const scalar minPyrVol, labelHashSet* setPtr ) const @@ -547,46 +515,63 @@ bool Foam::primitiveMesh::checkFacePyramids << "checking face orientation" << endl; } - // check whether face area vector points to the cell with higher label - const vectorField& ctrs = cellCentres(); - const labelList& own = faceOwner(); const labelList& nei = faceNeighbour(); - const faceList& f = faces(); - const pointField& p = points(); + + scalarField ownPyrVol; + scalarField neiPyrVol; + primitiveMeshTools::facePyramidVolume + ( + *this, + points, + ctrs, + ownPyrVol, + neiPyrVol + ); + label nErrorPyrs = 0; - forAll(f, faceI) + forAll(ownPyrVol, faceI) { - // Create the owner pyramid - it will have negative volume - scalar pyrVol = pyramidPointFaceRef(f[faceI], ctrs[own[faceI]]).mag(p); - - if (pyrVol > -minPyrVol) + 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)) { - // Create the neighbour pyramid - it will have positive volume - scalar pyrVol = - pyramidPointFaceRef(f[faceI], ctrs[nei[faceI]]).mag(p); - - if (pyrVol < minPyrVol) + 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++; } } @@ -619,6 +604,10 @@ bool Foam::primitiveMesh::checkFacePyramids bool Foam::primitiveMesh::checkFaceSkewness ( + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs, const bool report, labelHashSet* setPtr ) const @@ -633,93 +622,24 @@ bool Foam::primitiveMesh::checkFaceSkewness // Warn if the skew correction vector is more than skewWarning times // larger than the face area vector - const pointField& p = points(); - const faceList& fcs = faces(); - - const labelList& own = faceOwner(); - const labelList& nei = faceNeighbour(); - const vectorField& cellCtrs = cellCentres(); - const vectorField& faceCtrs = faceCentres(); - const vectorField& fAreas = faceAreas(); - - scalar maxSkew = 0; + tmp<scalarField> tskewness = primitiveMeshTools::faceSkewness + ( + *this, + points, + fCtrs, + fAreas, + cellCtrs + ); + const scalarField& skewness = tskewness(); + + scalar maxSkew = max(skewness); label nWarnSkew = 0; - forAll(nei, faceI) - { - vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]]; - vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]]; - - // Skewness vector - vector sv = - Cpf - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + SMALL))*d; - vector svHat = sv/(mag(sv) + VSMALL); - - // Normalisation distance calculated as the approximate distance - // from the face centre to the edge of the face in the direction of - // the skewness - scalar fd = 0.2*mag(d) + VSMALL; - const face& f = fcs[faceI]; - forAll(f, pi) - { - fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI]))); - } - - // Normalised skewness - scalar skewness = mag(sv)/fd; - - // 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 > skewThreshold_) - { - if (setPtr) - { - setPtr->insert(faceI); - } - - nWarnSkew++; - } - - if (skewness > maxSkew) - { - maxSkew = skewness; - } - } - - - // Boundary faces: consider them to have only skewness error. - // (i.e. treat as if mirror cell on other side) - - for (label faceI = nInternalFaces(); faceI < nFaces(); faceI++) + forAll(skewness, faceI) { - vector Cpf = faceCtrs[faceI] - cellCtrs[own[faceI]]; - - vector normal = fAreas[faceI]; - normal /= mag(normal) + VSMALL; - vector d = normal*(normal & Cpf); - - - // Skewness vector - vector sv = - Cpf - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + VSMALL))*d; - vector svHat = sv/(mag(sv) + VSMALL); - - // Normalisation distance calculated as the approximate distance - // from the face centre to the edge of the face in the direction of - // the skewness - scalar fd = 0.4*mag(d) + VSMALL; - const face& f = fcs[faceI]; - forAll(f, pi) - { - fd = max(fd, mag(svHat & (p[f[pi]] - faceCtrs[faceI]))); - } - - // Normalised skewness - scalar skewness = mag(sv)/fd; - // 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 > skewThreshold_) + if (skewness[faceI] > skewThreshold_) { if (setPtr) { @@ -728,14 +648,8 @@ bool Foam::primitiveMesh::checkFaceSkewness nWarnSkew++; } - - if (skewness > maxSkew) - { - maxSkew = skewness; - } } - reduce(maxSkew, maxOp<scalar>()); reduce(nWarnSkew, sumOp<label>()); @@ -763,86 +677,14 @@ bool Foam::primitiveMesh::checkFaceSkewness } -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 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 @@ -867,71 +709,30 @@ bool Foam::primitiveMesh::checkFaceAngles const scalar maxSin = Foam::sin(degToRad(maxDeg)); - const pointField& p = points(); - const faceList& fcs = faces(); - vectorField faceNormals(faceAreas()); - faceNormals /= mag(faceNormals) + VSMALL; - scalar maxEdgeSin = 0.0; + tmp<scalarField> tfaceAngles = primitiveMeshTools::faceConcavity + ( + maxSin, + *this, + points, + faceAreas + ); + const scalarField& faceAngles = tfaceAngles(); - label nConcave = 0; + scalar maxEdgeSin = max(faceAngles); - label errorFaceI = -1; + label nConcave = 0; - forAll(fcs, faceI) + forAll(faceAngles, faceI) { - const face& f = fcs[faceI]; - - // Get edge from f[0] to f[size-1]; - vector ePrev(p[f.first()] - p[f.last()]); - scalar magEPrev = mag(ePrev); - ePrev /= magEPrev + VSMALL; - - forAll(f, fp0) + if (faceAngles[faceI] > SMALL) { - // Get vertex after fp - label fp1 = f.fcIndex(fp0); - - // Normalized vector between two consecutive points - vector e10(p[f[fp1]] - p[f[fp0]]); - scalar magE10 = mag(e10); - e10 /= magE10 + VSMALL; + nConcave++; - if (magEPrev > SMALL && magE10 > SMALL) + if (setPtr) { - vector edgeNormal = ePrev ^ e10; - scalar magEdgeNormal = mag(edgeNormal); - - if (magEdgeNormal < maxSin) - { - // Edges (almost) aligned -> face is ok. - } - else - { - // Check normal - edgeNormal /= magEdgeNormal; - - if ((edgeNormal & faceNormals[faceI]) < SMALL) - { - if (faceI != errorFaceI) - { - // Count only one error per face. - errorFaceI = faceI; - nConcave++; - } - - if (setPtr) - { - setPtr->insert(faceI); - } - - maxEdgeSin = max(maxEdgeSin, magEdgeNormal); - } - } + setPtr->insert(faceI); } - - ePrev = e10; - magEPrev = magE10; } } @@ -965,11 +766,11 @@ bool Foam::primitiveMesh::checkFaceAngles } -// Check warpage of faces. Is calculated as the difference between areas of -// individual triangles and the overall area of the face (which ifself is -// is the average of the areas of the individual triangles). bool Foam::primitiveMesh::checkFaceFlatness ( + const pointField& points, + const vectorField& faceCentres, + const vectorField& faceAreas, const bool report, const scalar warnFlatness, labelHashSet* setPtr @@ -992,52 +793,34 @@ bool Foam::primitiveMesh::checkFaceFlatness << exit(FatalError); } - - const pointField& p = points(); const faceList& fcs = faces(); - const pointField& fctrs = faceCentres(); - // Areas are calculated as the sum of areas. (see - // primitiveMeshFaceCentresAndAreas.C) - scalarField magAreas(mag(faceAreas())); + tmp<scalarField> tfaceFlatness = primitiveMeshTools::faceFlatness + ( + *this, + points, + faceCentres, + faceAreas + ); + const scalarField& faceFlatness = tfaceFlatness(); - label nWarped = 0; + scalarField magAreas(mag(faceAreas)); scalar minFlatness = GREAT; scalar sumFlatness = 0; label nSummed = 0; + label nWarped = 0; - forAll(fcs, faceI) + forAll(faceFlatness, faceI) { - const face& f = fcs[faceI]; - - if (f.size() > 3 && magAreas[faceI] > VSMALL) + if (fcs[faceI].size() > 3 && magAreas[faceI] > VSMALL) { - const point& fc = fctrs[faceI]; - - // Calculate the sum of magnitude of areas and compare to magnitude - // of sum of areas. - - scalar sumA = 0.0; - - forAll(f, fp) - { - const point& thisPoint = p[f[fp]]; - const point& nextPoint = p[f.nextLabel(fp)]; - - // Triangle around fc. - vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint)); - sumA += mag(n); - } - - scalar flatness = magAreas[faceI] / (sumA+VSMALL); - - sumFlatness += flatness; + sumFlatness += faceFlatness[faceI]; nSummed++; - minFlatness = min(minFlatness, flatness); + minFlatness = min(minFlatness, faceFlatness[faceI]); - if (flatness < warnFlatness) + if (faceFlatness[faceI] < warnFlatness) { nWarped++; @@ -1092,131 +875,103 @@ bool Foam::primitiveMesh::checkFaceFlatness } -// Check 1D/2Dness of edges. Gets passed the non-empty directions and -// checks all edges in the mesh whether they: -// - have no component in a non-empty direction or -// - are only in a singe non-empty direction. -// Empty direction info is passed in as a vector of labels (synchronised) -// which are 1 if the direction is non-empty, 0 if it is. -bool Foam::primitiveMesh::checkEdgeAlignment +bool Foam::primitiveMesh::checkConcaveCells ( + const vectorField& fAreas, + const pointField& fCentres, const bool report, - const Vector<label>& directions, labelHashSet* setPtr ) const { if (debug) { - Info<< "bool primitiveMesh::checkEdgeAlignment(" - << "const bool, const Vector<label>&, labelHashSet*) const: " - << "checking edge alignment" << endl; + Info<< "bool primitiveMesh::checkConcaveCells(const bool" + << ", labelHashSet*) const: " + << "checking for concave cells" << endl; } - label nDirs = 0; - for (direction cmpt=0; cmpt<vector::nComponents; cmpt++) - { - if (directions[cmpt] == 1) - { - nDirs++; - } - else if (directions[cmpt] != 0) - { - FatalErrorIn - ( - "primitiveMesh::checkEdgeAlignment" - "(const bool, const Vector<label>&, labelHashSet*)" - ) << "directions should contain 0 or 1 but is now " << directions - << exit(FatalError); - } - } + const cellList& c = cells(); + const labelList& fOwner = faceOwner(); - if (nDirs == vector::nComponents) - { - return false; - } + label nConcaveCells = 0; + forAll(c, cellI) + { + const cell& cFaces = c[cellI]; + bool concave = false; - const pointField& p = points(); - const faceList& fcs = faces(); + forAll(cFaces, i) + { + if (concave) + { + break; + } - EdgeMap<label> edgesInError; + label fI = cFaces[i]; - forAll(fcs, faceI) - { - const face& f = fcs[faceI]; + const point& fC = fCentres[fI]; - 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); + vector fN = fAreas[fI]; - if (magD > ROOTVSMALL) - { - d /= magD; + fN /= max(mag(fN), VSMALL); - // 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++; - } - } - } + // Flip normal if required so that it is always pointing out of + // the cell + if (fOwner[fI] != cellI) + { + fN *= -1; + } - if (nEmptyDirs == 0) - { - // Purely in ok directions. - } - else if (nEmptyDirs == 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_) { - // Ok if purely in empty directions. - if (nNonEmptyDirs > 0) + // Concave or planar face + + concave = true; + + if (setPtr) { - edgesInError.insert(edge(p0, p1), faceI); + setPtr->insert(cellI); } - } - else if (nEmptyDirs > 1) - { - // Always an error - edgesInError.insert(edge(p0, p1), faceI); + + nConcaveCells++; + + break; } } } } } - label nErrorEdges = returnReduce(edgesInError.size(), sumOp<label>()); + reduce(nConcaveCells, sumOp<label>()); - if (nErrorEdges > 0) + if (nConcaveCells > 0) { if (debug || report) { - Info<< " ***Number of edges not aligned with or perpendicular to " - << "non-empty directions: " << nErrorEdges << endl; - } - - if (setPtr) - { - setPtr->resize(2*edgesInError.size()); - forAllConstIter(EdgeMap<label>, edgesInError, iter) - { - setPtr->insert(iter.key()[0]); - setPtr->insert(iter.key()[1]); - } + Info<< " ***Concave cells (using face planes) found," + << " number of cells: " << nConcaveCells << endl; } return true; @@ -1225,14 +980,18 @@ bool Foam::primitiveMesh::checkEdgeAlignment { if (debug || report) { - Info<< " All edges aligned with or perpendicular to " - << "non-empty directions." << endl; + Info<< " Concave cell check OK." << endl; } + return false; } + + return false; } +// Topological tests + bool Foam::primitiveMesh::checkUpperTriangular ( const bool report, @@ -1574,6 +1333,80 @@ bool Foam::primitiveMesh::checkFaceVertices } +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 ( @@ -1891,168 +1724,153 @@ bool Foam::primitiveMesh::checkFaceFaces } -// Checks cells with 1 or less internal faces. Give numerical problems. -bool Foam::primitiveMesh::checkCellDeterminant -( - const bool report, // report, - labelHashSet* setPtr, // setPtr - const Vector<label>& meshD -) const -{ - if (debug) - { - Info<< "bool primitiveMesh::checkCellDeterminant(const bool" - << ", labelHashSet*) const: " - << "checking for under-determined cells" << endl; - } - - // Determine number of dimensions and (for 2D) missing dimension - label nDims = 0; - label twoD = -1; - for (direction dir = 0; dir < vector::nComponents; dir++) - { - if (meshD[dir] == 1) - { - nDims++; - } - else - { - twoD = dir; - } - } - - - const cellList& c = cells(); - - label nErrorCells = 0; - - scalar minDet = GREAT; - scalar sumDet = 0; - label nSummed = 0; - - if (nDims == 1) - { - minDet = 1; - sumDet = c.size()*minDet; - nSummed = c.size(); - } - else - { - forAll (c, cellI) - { - const labelList& curFaces = c[cellI]; - - // Calculate local normalization factor - scalar avgArea = 0; - - label nInternalFaces = 0; +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - forAll(curFaces, i) - { - if (isInternalFace(curFaces[i])) - { - avgArea += mag(faceAreas()[curFaces[i]]); +bool Foam::primitiveMesh::checkClosedBoundary(const bool report) const +{ + return checkClosedBoundary(faceAreas(), report, PackedBoolList(0)); +} - nInternalFaces++; - } - } - if (nInternalFaces == 0) - { - if (setPtr) - { - setPtr->insert(cellI); - } +bool Foam::primitiveMesh::checkClosedCells +( + const bool report, + labelHashSet* setPtr, + labelHashSet* aspectSetPtr, + const Vector<label>& solutionD +) const +{ + return checkClosedCells + ( + faceAreas(), + cellVolumes(), + report, + setPtr, + aspectSetPtr, + solutionD + ); +} - nErrorCells++; - } - else - { - avgArea /= nInternalFaces; - symmTensor areaTensor(symmTensor::zero); +bool Foam::primitiveMesh::checkFaceAreas +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkFaceAreas + ( + faceAreas(), + report, + false, // detailedReport, + setPtr + ); +} - forAll(curFaces, i) - { - if (isInternalFace(curFaces[i])) - { - areaTensor += sqr(faceAreas()[curFaces[i]]/avgArea); - } - } - if (nDims == 2) - { - // Add the missing eigenvector (such that it does not - // affect the determinant) - if (twoD == 0) - { - areaTensor.xx() = 1; - } - else if (twoD == 1) - { - areaTensor.yy() = 1; - } - else - { - areaTensor.zz() = 1; - } - } +bool Foam::primitiveMesh::checkCellVolumes +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkCellVolumes + ( + cellVolumes(), + report, + false, // detailedReport, + setPtr + ); +} - scalar determinant = mag(det(areaTensor)); - minDet = min(determinant, minDet); - sumDet += determinant; - nSummed++; +bool Foam::primitiveMesh::checkFaceOrthogonality +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkFaceOrthogonality + ( + faceAreas(), + cellCentres(), + report, + setPtr + ); +} - if (determinant < 1e-3) - { - if (setPtr) - { - setPtr->insert(cellI); - } - nErrorCells++; - } - } - } - } +bool Foam::primitiveMesh::checkFacePyramids +( + const bool report, + const scalar minPyrVol, + labelHashSet* setPtr +) const +{ + return checkFacePyramids + ( + points(), + cellCentres(), + report, + false, // detailedReport, + minPyrVol, + setPtr + ); +} - reduce(nErrorCells, sumOp<label>()); - reduce(minDet, minOp<scalar>()); - reduce(sumDet, sumOp<scalar>()); - reduce(nSummed, sumOp<label>()); - if (debug || report) - { - if (nSummed > 0) - { - Info<< " Cell determinant (wellposedness) : minimum: " << minDet - << " average: " << sumDet/nSummed - << endl; - } - } +bool Foam::primitiveMesh::checkFaceSkewness +( + const bool report, + labelHashSet* setPtr +) const +{ + return checkFaceSkewness + ( + points(), + faceCentres(), + faceAreas(), + cellCentres(), + report, + setPtr + ); +} - if (nErrorCells > 0) - { - if (debug || report) - { - Info<< " ***Cells with small determinant found, number of cells: " - << nErrorCells << endl; - } - return true; - } - else - { - if (debug || report) - { - Info<< " Cell determinant check OK." << endl; - } +bool Foam::primitiveMesh::checkFaceAngles +( + const bool report, + const scalar maxDeg, + labelHashSet* setPtr +) const +{ + return checkFaceAngles + ( + points(), + faceAreas(), + report, + maxDeg, + setPtr + ); +} - return false; - } - return false; +bool Foam::primitiveMesh::checkFaceFlatness +( + const bool report, + const scalar warnFlatness, + labelHashSet* setPtr +) const +{ + return checkFaceFlatness + ( + points(), + faceCentres(), + faceAreas(), + report, + warnFlatness, + setPtr + ); } @@ -2062,117 +1880,16 @@ bool Foam::primitiveMesh::checkConcaveCells 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(); - const vectorField& fAreas = faceAreas(); - const pointField& fCentres = faceCentres(); - - 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; + return checkConcaveCells + ( + faceAreas(), + faceCentres(), + report, + setPtr + ); } -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - bool Foam::primitiveMesh::checkTopology(const bool report) const { label noFailedChecks = 0; @@ -2182,7 +1899,6 @@ bool Foam::primitiveMesh::checkTopology(const bool report) const if (checkCellsZipUp(report)) noFailedChecks++; if (checkFaceVertices(report)) noFailedChecks++; if (checkFaceFaces(report)) noFailedChecks++; - //if (checkCellDeterminant(report)) noFailedChecks++; if (noFailedChecks == 0) { diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheckMotion.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheckMotion.C deleted file mode 100644 index 4b0d2da63aa25fb48475dca70dca108eea3d0354..0000000000000000000000000000000000000000 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheckMotion.C +++ /dev/null @@ -1,266 +0,0 @@ -/*---------------------------------------------------------------------------*\ - ========= | - \\ / F ield | OpenFOAM: The Open Source CFD Toolbox - \\ / O peration | - \\ / A nd | Copyright (C) 2011 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/>. - -Description - Given a set of points, find out if the mesh resulting from point motion will - be valid without actually changing the mesh. - -\*---------------------------------------------------------------------------*/ - -#include "primitiveMesh.H" -#include "pyramidPointFaceRef.H" -#include "cell.H" -#include "unitConversion.H" - -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -bool Foam::primitiveMesh::checkMeshMotion -( - const pointField& newPoints, - const bool report -) const -{ - if (debug || report) - { - Pout<< "bool primitiveMesh::checkMeshMotion(" - << "const pointField& newPoints, const bool report) const: " - << "checking mesh motion" << endl; - } - - bool error = false; - - const faceList& f = faces(); - - const labelList& own = faceOwner(); - const labelList& nei = faceNeighbour(); - - vectorField fCtrs(nFaces()); - vectorField fAreas(nFaces()); - - makeFaceCentresAndAreas(newPoints, fCtrs, fAreas); - - // Check cell volumes and calculate new cell centres - vectorField cellCtrs(nCells()); - scalarField cellVols(nCells()); - - makeCellCentresAndVols(fCtrs, fAreas, cellCtrs, cellVols); - - scalar minVolume = GREAT; - label nNegVols = 0; - - forAll(cellVols, cellI) - { - if (cellVols[cellI] < VSMALL) - { - if (debug || report) - { - Pout<< "Zero or negative cell volume detected for cell " - << cellI << ". Volume = " << cellVols[cellI] << endl; - } - - nNegVols++; - } - - minVolume = min(minVolume, cellVols[cellI]); - } - - if (nNegVols > 0) - { - error = true; - - Pout<< "Zero or negative cell volume in mesh motion in " << nNegVols - << " cells. Min volume: " << minVolume << endl; - } - else - { - if (debug || report) - { - Pout<< "Min volume = " << minVolume - << ". Total volume = " << sum(cellVols) - << ". Cell volumes OK." << endl; - } - } - - // Check face areas - - scalar minArea = GREAT; - label nNegAreas = 0; - label nPyrErrors = 0; - label nDotProductErrors = 0; - - forAll(f, faceI) - { - const scalar a = Foam::mag(fAreas[faceI]); - - if (a < VSMALL) - { - if (debug || report) - { - if (isInternalFace(faceI)) - { - Pout<< "Zero or negative face area detected for " - << "internal face "<< faceI << " between cells " - << own[faceI] << " and " << nei[faceI] - << ". Face area magnitude = " << a << endl; - } - else - { - Pout<< "Zero or negative face area detected for " - << "boundary face " << faceI << " next to cell " - << own[faceI] << ". Face area magnitude = " - << a << endl; - } - } - - nNegAreas++; - } - - minArea = min(minArea, a); - - // Create the owner pyramid - it will have negative volume - scalar pyrVol = - pyramidPointFaceRef(f[faceI], cellCtrs[own[faceI]]).mag(newPoints); - - if (pyrVol > SMALL) - { - if (debug || report) - { - Pout<< "Negative pyramid volume: " << -pyrVol - << " for face " << faceI << " " << f[faceI] - << " and owner cell: " << own[faceI] << endl - << "Owner cell vertex labels: " - << cells()[own[faceI]].labels(f) - << endl; - } - - nPyrErrors++; - } - - if (isInternalFace(faceI)) - { - // Create the neighbour pyramid - it will have positive volume - scalar pyrVol = - pyramidPointFaceRef - ( - f[faceI], - cellCtrs[nei[faceI]] - ).mag(newPoints); - - if (pyrVol < -SMALL) - { - if (debug || report) - { - Pout<< "Negative pyramid volume: " << pyrVol - << " for face " << faceI << " " << f[faceI] - << " and neighbour cell: " << nei[faceI] << nl - << "Neighbour cell vertex labels: " - << cells()[nei[faceI]].labels(f) - << endl; - } - - nPyrErrors++; - } - - const vector d = cellCtrs[nei[faceI]] - cellCtrs[own[faceI]]; - const vector& s = fAreas[faceI]; - scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL); - - // Only write full message the first time - if (dDotS < SMALL && nDotProductErrors == 0) - { - // Non-orthogonality greater than 90 deg - WarningIn - ( - "primitiveMesh::checkMeshMotion" - "(const pointField& newPoints, const bool report) const" - ) << "Severe non-orthogonality in mesh motion for face " - << faceI - << " between cells " << own[faceI] << " and " << nei[faceI] - << ": Angle = " << radToDeg(::acos(dDotS)) - << " deg." << endl; - - nDotProductErrors++; - } - } - } - - if (nNegAreas > 0) - { - error = true; - - WarningIn - ( - "primitiveMesh::checkMeshMotion" - "(const pointField& newPoints, const bool report) const" - ) << "Zero or negative face area in mesh motion in " << nNegAreas - << " faces. Min area: " << minArea << endl; - } - else - { - if (debug || report) - { - Pout<< "Min area = " << minArea - << ". Face areas OK." << endl; - } - } - - if (nPyrErrors > 0) - { - Pout<< "Detected " << nPyrErrors - << " negative pyramid volume in mesh motion" << endl; - - error = true; - } - else - { - if (debug || report) - { - Pout<< "Pyramid volumes OK." << endl; - } - } - - if (nDotProductErrors > 0) - { - Pout<< "Detected " << nDotProductErrors - << " in non-orthogonality in mesh motion." << endl; - - error = true; - } - else - { - if (debug || report) - { - Pout<< "Non-orthogonality check OK." << endl; - } - } - - if (!error && (debug || report)) - { - Pout<< "Mesh motion check OK." << endl; - } - - return error; -} - - -// ************************************************************************* // diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C new file mode 100644 index 0000000000000000000000000000000000000000..e94c8edbf77a1709d63377a11c6fb98e5765e067 --- /dev/null +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C @@ -0,0 +1,638 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2012 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 "primitiveMeshTools.H" +#include "syncTools.H" +#include "pyramidPointFaceRef.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::scalar Foam::primitiveMeshTools::faceSkewness +( + const primitiveMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& fAreas, + + const label faceI, + const point& ownCc, + const point& neiCc +) +{ + vector Cpf = fCtrs[faceI] - ownCc; + vector d = neiCc - ownCc; + + // Skewness vector + vector sv = + Cpf + - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + SMALL))*d; + vector svHat = sv/(mag(sv) + VSMALL); + + // Normalisation distance calculated as the approximate distance + // from the face centre to the edge of the face in the direction + // of the skewness + scalar fd = 0.2*mag(d) + VSMALL; + const face& f = mesh.faces()[faceI]; + forAll(f, pi) + { + fd = max(fd, mag(svHat & (p[f[pi]] - fCtrs[faceI]))); + } + + // Normalised skewness + return mag(sv)/fd; +} + + +Foam::scalar Foam::primitiveMeshTools::faceOrthogonality +( + const point& ownCc, + const point& neiCc, + const vector& s +) +{ + vector d = neiCc - ownCc; + + return (d & s)/(mag(d)*mag(s) + VSMALL); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceOrthogonality +( + const primitiveMesh& mesh, + const vectorField& areas, + const vectorField& cc +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + + tmp<scalarField> tortho(new scalarField(mesh.nInternalFaces())); + scalarField& ortho = tortho(); + + // Internal faces + forAll(nei, faceI) + { + ortho[faceI] = faceOrthogonality + ( + cc[own[faceI]], + cc[nei[faceI]], + areas[faceI] + ); + } + + return tortho; +} + + +Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceSkewness +( + const primitiveMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + const faceList& fcs = mesh.faces(); + + tmp<scalarField> tskew(new scalarField(mesh.nFaces())); + scalarField& skew = tskew(); + + forAll(nei, faceI) + { + skew[faceI] = faceSkewness + ( + mesh, + p, + fCtrs, + fAreas, + + faceI, + cellCtrs[own[faceI]], + cellCtrs[nei[faceI]] + ); + } + + + // Boundary faces: consider them to have only skewness error. + // (i.e. treat as if mirror cell on other side) + + for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++) + { + vector Cpf = fCtrs[faceI] - cellCtrs[own[faceI]]; + + vector normal = fAreas[faceI]; + normal /= mag(normal) + VSMALL; + vector d = normal*(normal & Cpf); + + + // Skewness vector + vector sv = + Cpf + - ((fAreas[faceI] & Cpf)/((fAreas[faceI] & d) + VSMALL))*d; + vector svHat = sv/(mag(sv) + VSMALL); + + // Normalisation distance calculated as the approximate distance + // from the face centre to the edge of the face in the direction + // of the skewness + scalar fd = 0.4*mag(d) + VSMALL; + const face& f = fcs[faceI]; + forAll(f, pi) + { + fd = max(fd, mag(svHat & (p[f[pi]] - fCtrs[faceI]))); + } + + // Normalised skewness + skew[faceI] = mag(sv)/fd; + } + + return tskew; +} + +void Foam::primitiveMeshTools::facePyramidVolume +( + const primitiveMesh& mesh, + const pointField& points, + const vectorField& ctrs, + + scalarField& ownPyrVol, + scalarField& neiPyrVol +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + const faceList& f = mesh.faces(); + + ownPyrVol.setSize(mesh.nFaces()); + neiPyrVol.setSize(mesh.nInternalFaces()); + + forAll(f, faceI) + { + // Create the owner pyramid + ownPyrVol[faceI] = -pyramidPointFaceRef + ( + f[faceI], + ctrs[own[faceI]] + ).mag(points); + + if (mesh.isInternalFace(faceI)) + { + // Create the neighbour pyramid - it will have positive volume + neiPyrVol[faceI] = pyramidPointFaceRef + ( + f[faceI], + ctrs[nei[faceI]] + ).mag(points); + } + } +} + + +void Foam::primitiveMeshTools::cellClosedness +( + const primitiveMesh& mesh, + const Vector<label>& meshD, + const vectorField& areas, + const scalarField& vols, + + scalarField& openness, + scalarField& aratio +) +{ + const labelList& own = mesh.faceOwner(); + const labelList& nei = mesh.faceNeighbour(); + + // Loop through cell faces and sum up the face area vectors for each cell. + // This should be zero in all vector components + + vectorField sumClosed(mesh.nCells(), vector::zero); + vectorField sumMagClosed(mesh.nCells(), vector::zero); + + forAll(own, faceI) + { + // Add to owner + sumClosed[own[faceI]] += areas[faceI]; + sumMagClosed[own[faceI]] += cmptMag(areas[faceI]); + } + + forAll(nei, faceI) + { + // Subtract from neighbour + sumClosed[nei[faceI]] -= areas[faceI]; + sumMagClosed[nei[faceI]] += cmptMag(areas[faceI]); + } + + + label nDims = 0; + for (direction dir = 0; dir < vector::nComponents; dir++) + { + if (meshD[dir] == 1) + { + nDims++; + } + } + + + // Check the sums + openness.setSize(mesh.nCells()); + aratio.setSize(mesh.nCells()); + + forAll(sumClosed, cellI) + { + scalar maxOpenness = 0; + + for (direction cmpt=0; cmpt<vector::nComponents; cmpt++) + { + maxOpenness = max + ( + maxOpenness, + mag(sumClosed[cellI][cmpt]) + /(sumMagClosed[cellI][cmpt] + VSMALL) + ); + } + openness[cellI] = maxOpenness; + + // Calculate the aspect ration as the maximum of Cartesian component + // aspect ratio to the total area hydraulic area aspect ratio + scalar minCmpt = VGREAT; + scalar maxCmpt = -VGREAT; + for (direction dir = 0; dir < vector::nComponents; dir++) + { + if (meshD[dir] == 1) + { + minCmpt = min(minCmpt, sumMagClosed[cellI][dir]); + maxCmpt = max(maxCmpt, sumMagClosed[cellI][dir]); + } + } + + scalar aspectRatio = maxCmpt/(minCmpt + VSMALL); + if (nDims == 3) + { + aspectRatio = max + ( + aspectRatio, + 1.0/6.0*cmptSum(sumMagClosed[cellI])/pow(vols[cellI], 2.0/3.0) + ); + } + + aratio[cellI] = aspectRatio; + } +} + + +Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceConcavity +( + const scalar maxSin, + const primitiveMesh& mesh, + const pointField& p, + const vectorField& faceAreas +) +{ + const faceList& fcs = mesh.faces(); + + vectorField faceNormals(faceAreas); + faceNormals /= mag(faceNormals) + VSMALL; + + tmp<scalarField> tfaceAngles(new scalarField(mesh.nFaces())); + scalarField& faceAngles = tfaceAngles(); + + + forAll(fcs, faceI) + { + const face& f = fcs[faceI]; + + // Get edge from f[0] to f[size-1]; + vector ePrev(p[f.first()] - p[f.last()]); + scalar magEPrev = mag(ePrev); + ePrev /= magEPrev + VSMALL; + + scalar maxEdgeSin = 0.0; + + forAll(f, fp0) + { + // Get vertex after fp + label fp1 = f.fcIndex(fp0); + + // Normalized vector between two consecutive points + vector e10(p[f[fp1]] - p[f[fp0]]); + scalar magE10 = mag(e10); + e10 /= magE10 + VSMALL; + + if (magEPrev > SMALL && magE10 > SMALL) + { + vector edgeNormal = ePrev ^ e10; + scalar magEdgeNormal = mag(edgeNormal); + + if (magEdgeNormal < maxSin) + { + // Edges (almost) aligned -> face is ok. + } + else + { + // Check normal + edgeNormal /= magEdgeNormal; + + if ((edgeNormal & faceNormals[faceI]) < SMALL) + { + maxEdgeSin = max(maxEdgeSin, magEdgeNormal); + } + } + } + + ePrev = e10; + magEPrev = magE10; + } + + faceAngles[faceI] = maxEdgeSin; + } + + return tfaceAngles; +} + + +Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceFlatness +( + const primitiveMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& faceAreas +) +{ + const faceList& fcs = mesh.faces(); + + // Areas are calculated as the sum of areas. (see + // primitiveMeshFaceCentresAndAreas.C) + scalarField magAreas(mag(faceAreas)); + + tmp<scalarField> tfaceFlatness(new scalarField(mesh.nFaces(), 1.0)); + scalarField& faceFlatness = tfaceFlatness(); + + + forAll(fcs, faceI) + { + const face& f = fcs[faceI]; + + if (f.size() > 3 && magAreas[faceI] > VSMALL) + { + const point& fc = fCtrs[faceI]; + + // Calculate the sum of magnitude of areas and compare to magnitude + // of sum of areas. + + scalar sumA = 0.0; + + forAll(f, fp) + { + const point& thisPoint = p[f[fp]]; + const point& nextPoint = p[f.nextLabel(fp)]; + + // Triangle around fc. + vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint)); + sumA += mag(n); + } + + faceFlatness[faceI] = magAreas[faceI] / (sumA+VSMALL); + } + } + + return tfaceFlatness; +} + + +//Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::edgeAlignment +//( +// const primitiveMesh& mesh, +// const Vector<label>& meshD, +// const pointField& p +//) +//{ +// label nDirs = 0; +// for (direction cmpt=0; cmpt<vector::nComponents; cmpt++) +// { +// if (meshD[cmpt] == 1) +// { +// nDirs++; +// } +// else if (meshD[cmpt] != 0) +// { +// FatalErrorIn +// ( +// "primitiveMeshTools::edgeAlignment" +// "(const primitiveMesh&, const Vector<label>&" +// ", const pointField&)" +// ) << "directions should contain 0 or 1 but is now " << meshD +// << exit(FatalError); +// } +// } +// +// tmp<scalarField> tedgeAlignment(new scalarField(mesh.nFaces(), 0.0)); +// scalarField& edgeAlignment = tedgeAlignment(); +// +// if (nDirs != vector::nComponents) +// { +// const faceList& fcs = mesh.faces(); +// +// 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 (meshD[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) +// { +// edgeAlignment[faceI] = max +// ( +// edgeAlignment[faceI], +// 1 +// ); +// } +// } +// else if (nEmptyDirs > 1) +// { +// // Always an error +// edgeAlignment[faceI] = max +// ( +// edgeAlignment[faceI], +// 2 +// ); +// } +// } +// } +// } +// } +// } +// +// return tedgeAlignment; +//} + + +// Checks cells with 1 or less internal faces. Give numerical problems. +Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::cellDeterminant +( + const primitiveMesh& mesh, + const Vector<label>& meshD, + const vectorField& faceAreas, + const PackedBoolList& internalOrCoupledFace +) +{ + // Determine number of dimensions and (for 2D) missing dimension + label nDims = 0; + label twoD = -1; + for (direction dir = 0; dir < vector::nComponents; dir++) + { + if (meshD[dir] == 1) + { + nDims++; + } + else + { + twoD = dir; + } + } + + + + tmp<scalarField> tcellDeterminant(new scalarField(mesh.nCells())); + scalarField& cellDeterminant = tcellDeterminant(); + + const cellList& c = mesh.cells(); + + if (nDims == 1) + { + cellDeterminant = 1.0; + } + else + { + forAll (c, cellI) + { + const labelList& curFaces = c[cellI]; + + // Calculate local normalization factor + scalar avgArea = 0; + + label nInternalFaces = 0; + + forAll(curFaces, i) + { + if (internalOrCoupledFace[curFaces[i]]) + { + avgArea += mag(faceAreas[curFaces[i]]); + + nInternalFaces++; + } + } + + if (nInternalFaces == 0) + { + cellDeterminant[cellI] = 0; + } + else + { + avgArea /= nInternalFaces; + + symmTensor areaTensor(symmTensor::zero); + + forAll(curFaces, i) + { + if (internalOrCoupledFace[curFaces[i]]) + { + areaTensor += sqr(faceAreas[curFaces[i]]/avgArea); + } + } + + if (nDims == 2) + { + // Add the missing eigenvector (such that it does not + // affect the determinant) + if (twoD == 0) + { + areaTensor.xx() = 1; + } + else if (twoD == 1) + { + areaTensor.yy() = 1; + } + else + { + areaTensor.zz() = 1; + } + } + + cellDeterminant[cellI] = mag(det(areaTensor)); + } + } + } + + return tcellDeterminant; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.H b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.H new file mode 100644 index 0000000000000000000000000000000000000000..5e3b2c07fd4b03aaacab2f82fca09c83187e74c9 --- /dev/null +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.H @@ -0,0 +1,168 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2012 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/>. + +Namespace + Foam::primitiveMeshTools + +Description + Collection of static functions operating on primitiveMesh (mainly checks). + +SourceFiles + primitiveMeshTools.C + +\*---------------------------------------------------------------------------*/ +#ifndef primitiveMeshTools_H +#define primitiveMeshTools_H + +#include "primitiveMesh.H" +#include "PackedBoolList.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Namespace primitiveMeshTools Declaration +\*---------------------------------------------------------------------------*/ + +class primitiveMeshTools +{ + +protected: + + // Protected Member Functions + + //- Skewness of single face + static scalar faceSkewness + ( + const primitiveMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& fAreas, + + const label faceI, + const point& ownCc, + const point& neiCc + ); + + //- Orthogonality of single face + static scalar faceOrthogonality + ( + const point& ownCc, + const point& neiCc, + const vector& s + ); + +public: + + //- Generate non-orthogonality field (internal faces only) + static tmp<scalarField> faceOrthogonality + ( + const primitiveMesh& mesh, + const vectorField& fAreas, + const vectorField& cellCtrs + ); + + //- Generate face pyramid volume fields + static void facePyramidVolume + ( + const primitiveMesh& mesh, + const pointField& points, + const vectorField& cellCtrs, + scalarField& ownPyrVol, + scalarField& neiPyrVol + ); + + //- Generate skewness field + static tmp<scalarField> faceSkewness + ( + const primitiveMesh& mesh, + const pointField& points, + const vectorField& fCtrs, + const vectorField& fAreas, + const vectorField& cellCtrs + ); + + //- Generate cell openness and cell ascpect ratio field + static void cellClosedness + ( + const primitiveMesh& mesh, + const Vector<label>& meshD, + const vectorField& areas, + const scalarField& vols, + scalarField& openness, + scalarField& aratio + ); + + //- Generate face concavity field. Returns per face the (sin of the) + // most concave angle between two consecutive edges + static tmp<scalarField> faceConcavity + ( + const scalar maxSin, + const primitiveMesh& mesh, + const pointField& p, + const vectorField& faceAreas + ); + + //- Generate face flatness field. Compares the individual triangles' + // normals against the face average normal. Between 0 (fully warped) + // and 1 (fully flat) + static tmp<scalarField> faceFlatness + ( + const primitiveMesh& mesh, + const pointField& p, + const vectorField& fCtrs, + const vectorField& faceAreas + ); + + //- Generate edge alignment field. Is per face the minimum aligned edge + // (does not use edge addressing) + static tmp<scalarField> edgeAlignment + ( + const primitiveMesh& mesh, + const Vector<label>& directions, + const pointField& p + ); + + //- Generate cell determinant field + static tmp<scalarField> cellDeterminant + ( + const primitiveMesh& mesh, + const Vector<label>& directions, + const vectorField& faceAreas, + const PackedBoolList& internalOrCoupledFace + ); + +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //