/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2019 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 "primitiveMeshGeometry.H" #include "pyramidPointFaceRef.H" #include "unitConversion.H" #include "triPointRef.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(primitiveMeshGeometry, 0); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::primitiveMeshGeometry::updateFaceCentresAndAreas ( const pointField& p, const labelList& changedFaces ) { const faceList& fs = mesh_.faces(); for (label facei : changedFaces) { const labelList& f = fs[facei]; 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] = (1.0/3.0)*(p[f[0]] + p[f[1]] + p[f[2]]); faceAreas_[facei] = 0.5*((p[f[1]] - p[f[0]])^(p[f[2]] - p[f[0]])); } else { vector sumN = Zero; scalar sumA = 0.0; vector sumAc = Zero; point fCentre = p[f[0]]; for (label pi = 1; pi < nPoints; ++pi) { fCentre += p[f[pi]]; } fCentre /= nPoints; for (label pi = 0; pi < nPoints; ++pi) { const point& nextPoint = p[f[(pi + 1) % nPoints]]; vector c(p[f[pi]] + nextPoint + fCentre); vector n((nextPoint - p[f[pi]])^(fCentre - p[f[pi]])); scalar a = mag(n); sumN += n; sumA += a; sumAc += a*c; } faceCentres_[facei] = (1.0/3.0)*sumAc/(sumA + VSMALL); faceAreas_[facei] = 0.5*sumN; } } } void Foam::primitiveMeshGeometry::updateCellCentresAndVols ( const labelList& changedCells, const labelList& changedFaces ) { // Clear the fields for accumulation UIndirectList(cellCentres_, changedCells) = Zero; UIndirectList(cellVolumes_, changedCells) = 0.0; const labelList& own = mesh_.faceOwner(); const labelList& nei = mesh_.faceNeighbour(); // first estimate the approximate cell centre as the average of face centres vectorField cEst(mesh_.nCells()); UIndirectList(cEst, changedCells) = Zero; scalarField nCellFaces(mesh_.nCells()); UIndirectList(nCellFaces, changedCells) = 0.0; for (label facei : changedFaces) { cEst[own[facei]] += faceCentres_[facei]; nCellFaces[own[facei]] += 1; if (mesh_.isInternalFace(facei)) { cEst[nei[facei]] += faceCentres_[facei]; nCellFaces[nei[facei]] += 1; } } for (label celli : changedCells) { cEst[celli] /= nCellFaces[celli]; } for (label facei : changedFaces) { // Calculate 3*face-pyramid volume scalar pyr3Vol = max ( faceAreas_[facei] & (faceCentres_[facei] - cEst[own[facei]]), VSMALL ); // Calculate face-pyramid centre vector pc = (3.0/4.0)*faceCentres_[facei] + (1.0/4.0)*cEst[own[facei]]; // Accumulate volume-weighted face-pyramid centre cellCentres_[own[facei]] += pyr3Vol*pc; // Accumulate face-pyramid volume cellVolumes_[own[facei]] += pyr3Vol; if (mesh_.isInternalFace(facei)) { // Calculate 3*face-pyramid volume scalar pyr3Vol = max ( faceAreas_[facei] & (cEst[nei[facei]] - faceCentres_[facei]), VSMALL ); // Calculate face-pyramid centre vector pc = (3.0/4.0)*faceCentres_[facei] + (1.0/4.0)*cEst[nei[facei]]; // Accumulate volume-weighted face-pyramid centre cellCentres_[nei[facei]] += pyr3Vol*pc; // Accumulate face-pyramid volume cellVolumes_[nei[facei]] += pyr3Vol; } } forAll(changedCells, i) { label celli = changedCells[i]; cellCentres_[celli] /= cellVolumes_[celli]; cellVolumes_[celli] *= (1.0/3.0); } } Foam::labelList Foam::primitiveMeshGeometry::affectedCells ( const labelList& changedFaces ) const { const labelList& own = mesh_.faceOwner(); const labelList& nei = mesh_.faceNeighbour(); labelHashSet affectedCells(2*changedFaces.size()); for (label facei : changedFaces) { affectedCells.insert(own[facei]); if (mesh_.isInternalFace(facei)) { affectedCells.insert(nei[facei]); } } return affectedCells.toc(); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::primitiveMeshGeometry::primitiveMeshGeometry ( const primitiveMesh& mesh ) : mesh_(mesh) { correct(); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::primitiveMeshGeometry::correct() { faceAreas_ = mesh_.faceAreas(); faceCentres_ = mesh_.faceCentres(); cellCentres_ = mesh_.cellCentres(); cellVolumes_ = mesh_.cellVolumes(); } void Foam::primitiveMeshGeometry::correct ( const pointField& p, const labelList& changedFaces ) { // Update face quantities updateFaceCentresAndAreas(p, changedFaces); // Update cell quantities from face quantities updateCellCentresAndVols(affectedCells(changedFaces), changedFaces); } bool Foam::primitiveMeshGeometry::checkFaceDotProduct ( const bool report, const scalar orthWarn, const primitiveMesh& mesh, const vectorField& cellCentres, const vectorField& faceAreas, const labelList& checkFaces, labelHashSet* setPtr ) { // for all internal faces check that the d dot S product is positive const labelList& own = mesh.faceOwner(); const labelList& nei = mesh.faceNeighbour(); // Severe nonorthogonality threshold const scalar severeNonorthogonalityThreshold = ::cos(degToRad(orthWarn)); scalar minDDotS = GREAT; scalar sumDDotS = 0; label severeNonOrth = 0; label errorNonOrth = 0; forAll(checkFaces, i) { label facei = checkFaces[i]; if (mesh.isInternalFace(facei)) { vector d = cellCentres[nei[facei]] - cellCentres[own[facei]]; const vector& s = faceAreas[facei]; scalar dDotS = (d & s)/(mag(d)*mag(s) + VSMALL); if (dDotS < severeNonorthogonalityThreshold) { if (dDotS > SMALL) { if (report) { // Severe non-orthogonality but mesh still OK Pout<< "Severe non-orthogonality for face " << facei << " between cells " << own[facei] << " and " << nei[facei] << ": Angle = " << radToDeg(::acos(dDotS)) << " deg." << endl; } if (setPtr) { setPtr->insert(facei); } ++severeNonOrth; } else { // Non-orthogonality greater than 90 deg if (report) { WarningInFunction << "Severe non-orthogonality detected for face " << facei << " between cells " << own[facei] << " and " << nei[facei] << ": Angle = " << radToDeg(::acos(dDotS)) << " deg." << endl; } ++errorNonOrth; if (setPtr) { setPtr->insert(facei); } } } if (dDotS < minDDotS) { minDDotS = dDotS; } sumDDotS += dDotS; } } reduce(minDDotS, minOp()); reduce(sumDDotS, sumOp()); reduce(severeNonOrth, sumOp