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