/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2015-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- 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 . \*---------------------------------------------------------------------------*/ #include "polyMeshGeometry.H" #include "polyMeshTetDecomposition.H" #include "pyramid.H" #include "tetrahedron.H" #include "syncTools.H" #include "unitConversion.H" #include "primitiveMeshTools.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(polyMeshGeometry, 0); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::polyMeshGeometry::updateFaceCentresAndAreas ( const pointField& p, const labelList& changedFaces ) { const faceList& fcs = mesh_.faces(); for (const label facei : changedFaces) { const face& f = fcs[facei]; const label nPoints = f.size(); // If the face is a triangle, do a direct calculation for efficiency // and to avoid round-off error-related problems if (nPoints == 3) { faceCentres_[facei] = triPointRef::centre(p[f[0]], p[f[1]], p[f[2]]); faceAreas_[facei] = triPointRef::areaNormal(p[f[0]], p[f[1]], p[f[2]]); } else { solveVector sumN = Zero; solveScalar sumA = Zero; solveVector sumAc = Zero; solveVector fCentre = p[f[0]]; for (label pi = 1; pi < nPoints; ++pi) { fCentre += solveVector(p[f[pi]]); } fCentre /= nPoints; for (label pi = 0; pi < nPoints; ++pi) { const solveVector thisPoint(p[f.thisLabel(pi)]); const solveVector nextPoint(p[f.nextLabel(pi)]); solveVector c = thisPoint + nextPoint + fCentre; solveVector n = (nextPoint - thisPoint)^(fCentre - thisPoint); solveScalar a = mag(n); sumN += n; sumA += a; sumAc += a*c; } if (sumA < ROOTVSMALL) { faceCentres_[facei] = fCentre; faceAreas_[facei] = Zero; } else { faceCentres_[facei] = (1.0/3.0)*sumAc/sumA; faceAreas_[facei] = 0.5*sumN; } } } } void Foam::polyMeshGeometry::updateCellCentresAndVols ( const labelList& changedCells, const labelList& changedFaces ) { const labelList& own = mesh().faceOwner(); const cellList& cells = mesh().cells(); // Clear the fields for accumulation UIndirectList(cellCentres_, changedCells) = Zero; UIndirectList(cellVolumes_, changedCells) = Zero; // Re-calculate the changed cell centres and volumes for (const label celli : changedCells) { const labelList& cFaces = cells[celli]; // Estimate the cell centre and bounding box using the face centres vector cEst(Zero); boundBox bb; for (const label facei : cFaces) { const point& fc = faceCentres_[facei]; cEst += fc; bb.add(fc); } cEst /= cFaces.size(); // Sum up the face-pyramid contributions for (const label facei : cFaces) { // Calculate 3* the face-pyramid volume scalar pyr3Vol = faceAreas_[facei] & (faceCentres_[facei] - cEst); if (own[facei] != celli) { pyr3Vol = -pyr3Vol; } // Accumulate face-pyramid volume cellVolumes_[celli] += pyr3Vol; // Calculate the face-pyramid centre const vector pCtr = (3.0/4.0)*faceCentres_[facei] + (1.0/4.0)*cEst; // Accumulate volume-weighted face-pyramid centre cellCentres_[celli] += pyr3Vol*pCtr; } // Average the accumulated quantities if (mag(cellVolumes_[celli]) > VSMALL) { point cc = cellCentres_[celli] / cellVolumes_[celli]; // Do additional check for collapsed cells since some volumes // (e.g. 1e-33) do not trigger above but do return completely // wrong cell centre if (bb.contains(cc)) { cellCentres_[celli] = cc; } else { cellCentres_[celli] = cEst; } } else { cellCentres_[celli] = cEst; } cellVolumes_[celli] *= (1.0/3.0); } } Foam::labelList Foam::polyMeshGeometry::affectedCells ( const polyMesh& mesh, const labelList& changedFaces ) { const labelList& own = mesh.faceOwner(); const labelList& nei = mesh.faceNeighbour(); labelHashSet affectedCells(2*changedFaces.size()); for (const label facei : changedFaces) { affectedCells.insert(own[facei]); if (mesh.isInternalFace(facei)) { affectedCells.insert(nei[facei]); } } return affectedCells.toc(); } Foam::scalar Foam::polyMeshGeometry::checkNonOrtho ( const polyMesh& mesh, const bool report, const scalar severeNonorthogonalityThreshold, const label facei, const vector& s, // face area vector const vector& d, // cc-cc vector label& severeNonOrth, label& errorNonOrth, labelHashSet* setPtr ) { scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL); if (dDotS < severeNonorthogonalityThreshold) { label nei = -1; if (mesh.isInternalFace(facei)) { nei = mesh.faceNeighbour()[facei]; } if (dDotS > SMALL) { if (report) { // Severe non-orthogonality but mesh still OK Pout<< "Severe non-orthogonality for face " << facei << " between cells " << mesh.faceOwner()[facei] << " and " << nei << ": Angle = " << radToDeg(::acos(dDotS)) << " deg." << endl; } ++severeNonOrth; } else { // Non-orthogonality greater than 90 deg if (report) { WarningInFunction << "Severe non-orthogonality detected for face " << facei << " between cells " << mesh.faceOwner()[facei] << " and " << nei << ": Angle = " << radToDeg(::acos(dDotS)) << " deg." << endl; } ++errorNonOrth; } if (setPtr) { setPtr->insert(facei); } } return dDotS; } // Create the neighbour pyramid - it will have positive volume bool Foam::polyMeshGeometry::checkFaceTet ( const polyMesh& mesh, const bool report, const scalar minTetQuality, const pointField& p, const label facei, const point& fc, // face centre const point& cc, // cell centre labelHashSet* setPtr ) { const face& f = mesh.faces()[facei]; forAll(f, fp) { scalar tetQual = tetPointRef ( p[f[fp]], p[f.nextLabel(fp)], fc, cc ).quality(); if (tetQual < minTetQuality) { if (report) { Pout<< "bool polyMeshGeometry::checkFaceTets(" << "const bool, const scalar, const pointField&" << ", const pointField&" << ", const labelList&, labelHashSet*) : " << "face " << facei << " has a triangle that points the wrong way." << nl << "Tet quality: " << tetQual << " Face " << facei << endl; } if (setPtr) { setPtr->insert(facei); } return true; } } return false; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::polyMeshGeometry::polyMeshGeometry(const polyMesh& mesh) : mesh_(mesh) { correct(); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::polyMeshGeometry::correct() { faceAreas_ = mesh_.faceAreas(); faceCentres_ = mesh_.faceCentres(); cellCentres_ = mesh_.cellCentres(); cellVolumes_ = mesh_.cellVolumes(); } void Foam::polyMeshGeometry::correct ( const pointField& p, const labelList& changedFaces ) { // Update face quantities updateFaceCentresAndAreas(p, changedFaces); // Update cell quantities from face quantities updateCellCentresAndVols(affectedCells(mesh_, changedFaces), changedFaces); } bool Foam::polyMeshGeometry::checkFaceDotProduct ( const bool report, const scalar orthWarn, const polyMesh& mesh, const vectorField& cellCentres, const vectorField& faceAreas, const labelList& checkFaces, const List& baffles, labelHashSet* setPtr ) { // for all internal and coupled faces check that the d dot S product // is positive const labelList& own = mesh.faceOwner(); const labelList& nei = mesh.faceNeighbour(); const polyBoundaryMesh& patches = mesh.boundaryMesh(); // Severe nonorthogonality threshold const scalar severeNonorthogonalityThreshold = ::cos(degToRad(orthWarn)); // Calculate coupled cell centre pointField neiCc(mesh.nBoundaryFaces()); for (label facei = mesh.nInternalFaces(); facei < mesh.nFaces(); ++facei) { neiCc[facei-mesh.nInternalFaces()] = cellCentres[own[facei]]; } syncTools::swapBoundaryFacePositions(mesh, neiCc); scalar minDDotS = GREAT; scalar sumDDotS = 0; label nDDotS = 0; label severeNonOrth = 0; label errorNonOrth = 0; for (const label facei : checkFaces) { const point& ownCc = cellCentres[own[facei]]; if (mesh.isInternalFace(facei)) { scalar dDotS = checkNonOrtho ( mesh, report, severeNonorthogonalityThreshold, facei, faceAreas[facei], cellCentres[nei[facei]] - ownCc, severeNonOrth, errorNonOrth, setPtr ); if (dDotS < minDDotS) { minDDotS = dDotS; } sumDDotS += dDotS; ++nDDotS; } else { const label patchi = patches.whichPatch(facei); if (patches[patchi].coupled()) { scalar dDotS = checkNonOrtho ( mesh, report, severeNonorthogonalityThreshold, facei, faceAreas[facei], neiCc[facei-mesh.nInternalFaces()] - ownCc, severeNonOrth, errorNonOrth, setPtr ); if (dDotS < minDDotS) { minDDotS = dDotS; } sumDDotS += dDotS; ++nDDotS; } } } for (const labelPair& baffle : baffles) { const label face0 = baffle.first(); const label face1 = baffle.second(); const point& ownCc = cellCentres[own[face0]]; scalar dDotS = checkNonOrtho ( mesh, report, severeNonorthogonalityThreshold, face0, faceAreas[face0], cellCentres[own[face1]] - ownCc, severeNonOrth, errorNonOrth, setPtr ); if (dDotS < minDDotS) { minDDotS = dDotS; } sumDDotS += dDotS; ++nDDotS; } reduce(minDDotS, minOp()); reduce(sumDDotS, sumOp()); reduce(nDDotS, sumOp