/*---------------------------------------------------------------------------*\
========= |
\\ / 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