/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2015 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 "pyramidPointFaceRef.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& fs = mesh_.faces();
forAll(changedFaces, i)
{
label facei = changedFaces[i];
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::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) = 0.0;
// Re-calculate the changed cell centres and volumes
forAll(changedCells, changedCellI)
{
const label cellI(changedCells[changedCellI]);
const labelList& cFaces(cells[cellI]);
// Estimate the cell centre and bounding box using the face centres
vector cEst(Zero);
boundBox bb(boundBox::invertedBox);
forAll(cFaces, cFaceI)
{
const point& fc = faceCentres_[cFaces[cFaceI]];
cEst += fc;
bb.max() = max(bb.max(), fc);
bb.min() = min(bb.min(), fc);
}
cEst /= cFaces.size();
// Sum up the face-pyramid contributions
forAll(cFaces, cFaceI)
{
const label faceI(cFaces[cFaceI]);
// 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());
forAll(changedFaces, i)
{
label faceI = changedFaces[i];
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."
<< endl
<< "Tet quality: " << tetQual
<< " Face " << faceI
<< endl;
}
if (setPtr)
{
setPtr->insert(faceI);
}
return true;
}
}
return false;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// Construct from components
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 theat 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.nFaces() - mesh.nInternalFaces());
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;
forAll(checkFaces, i)
{
label faceI = checkFaces[i];
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
{
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++;
}
}
}
forAll(baffles, i)
{
label face0 = baffles[i].first();
label face1 = baffles[i].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