Files
openfoam/src/OpenFOAM/meshes/meshShapes/face/face.C

892 lines
20 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "face.H"
#include "triFace.H"
#include "triPointRef.H"
#include "mathematicalConstants.H"
#include "Swap.H"
#include "ConstCirculator.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const char* const Foam::face::typeName = "face";
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::vectorField>
Foam::face::calcEdges(const pointField& points) const
{
tmp<vectorField> tedges(new vectorField(size()));
vectorField& edges = tedges.ref();
forAll(*this, i)
{
label ni = fcIndex(i);
point thisPt = points[operator[](i)];
point nextPt = points[operator[](ni)];
vector vec(nextPt - thisPt);
vec /= Foam::mag(vec) + VSMALL;
edges[i] = vec;
}
return tedges;
}
Foam::scalar Foam::face::edgeCos
(
const vectorField& edges,
const label index
) const
{
label leftEdgeI = left(index);
label rightEdgeI = right(index);
// Note negate on left edge to get correct left-pointing edge.
return -(edges[leftEdgeI] & edges[rightEdgeI]);
}
Foam::label Foam::face::mostConcaveAngle
(
const pointField& points,
const vectorField& edges,
scalar& maxAngle
) const
{
vector n(normal(points));
label index = 0;
maxAngle = -GREAT;
forAll(edges, i)
{
label leftEdgeI = left(i);
label rightEdgeI = right(i);
vector edgeNormal = edges[rightEdgeI] ^ edges[leftEdgeI];
scalar edgeCos = edges[leftEdgeI] & edges[rightEdgeI];
scalar edgeAngle = acos(max(-1.0, min(1.0, edgeCos)));
scalar angle;
if ((edgeNormal & n) > 0)
{
// Concave angle.
angle = constant::mathematical::pi + edgeAngle;
}
else
{
// Convex angle. Note '-' to take into account that rightEdge
// and leftEdge are head-to-tail connected.
angle = constant::mathematical::pi - edgeAngle;
}
if (angle > maxAngle)
{
maxAngle = angle;
index = i;
}
}
return index;
}
Foam::label Foam::face::split
(
const face::splitMode mode,
const pointField& points,
label& triI,
label& quadI,
faceList& triFaces,
faceList& quadFaces
) const
{
label oldIndices = (triI + quadI);
if (size() <= 2)
{
FatalErrorInFunction
<< "Serious problem: asked to split a face with < 3 vertices"
<< abort(FatalError);
}
if (size() == 3)
{
// Triangle. Just copy.
if (mode == COUNTTRIANGLE || mode == COUNTQUAD)
{
triI++;
}
else
{
triFaces[triI++] = *this;
}
}
else if (size() == 4)
{
if (mode == COUNTTRIANGLE)
{
triI += 2;
}
if (mode == COUNTQUAD)
{
quadI++;
}
else if (mode == SPLITTRIANGLE)
{
// Start at point with largest internal angle.
const vectorField edges(calcEdges(points));
scalar minAngle;
label startIndex = mostConcaveAngle(points, edges, minAngle);
label nextIndex = fcIndex(startIndex);
label splitIndex = fcIndex(nextIndex);
// Create triangles
face triFace(3);
triFace[0] = operator[](startIndex);
triFace[1] = operator[](nextIndex);
triFace[2] = operator[](splitIndex);
triFaces[triI++] = triFace;
triFace[0] = operator[](splitIndex);
triFace[1] = operator[](fcIndex(splitIndex));
triFace[2] = operator[](startIndex);
triFaces[triI++] = triFace;
}
else
{
quadFaces[quadI++] = *this;
}
}
else
{
// General case. Like quad: search for largest internal angle.
const vectorField edges(calcEdges(points));
scalar minAngle = 1;
label startIndex = mostConcaveAngle(points, edges, minAngle);
scalar bisectAngle = minAngle/2;
vector rightEdge = edges[right(startIndex)];
//
// Look for opposite point which as close as possible bisects angle
//
// split candidate starts two points away.
label index = fcIndex(fcIndex(startIndex));
label minIndex = index;
scalar minDiff = constant::mathematical::pi;
for (label i = 0; i < size() - 3; i++)
{
vector splitEdge
(
points[operator[](index)]
- points[operator[](startIndex)]
);
splitEdge /= Foam::mag(splitEdge) + VSMALL;
const scalar splitCos = splitEdge & rightEdge;
const scalar splitAngle = acos(max(-1.0, min(1.0, splitCos)));
const scalar angleDiff = fabs(splitAngle - bisectAngle);
if (angleDiff < minDiff)
{
minDiff = angleDiff;
minIndex = index;
}
// Go to next candidate
index = fcIndex(index);
}
// Split into two subshapes.
// face1: startIndex to minIndex
// face2: minIndex to startIndex
// Get sizes of the two subshapes
label diff = 0;
if (minIndex > startIndex)
{
diff = minIndex - startIndex;
}
else
{
// Folded around
diff = minIndex + size() - startIndex;
}
label nPoints1 = diff + 1;
label nPoints2 = size() - diff + 1;
// Collect face1 points
face face1(nPoints1);
index = startIndex;
for (label i = 0; i < nPoints1; i++)
{
face1[i] = operator[](index);
index = fcIndex(index);
}
// Collect face2 points
face face2(nPoints2);
index = minIndex;
for (label i = 0; i < nPoints2; i++)
{
face2[i] = operator[](index);
index = fcIndex(index);
}
// Split faces
face1.split(mode, points, triI, quadI, triFaces, quadFaces);
face2.split(mode, points, triI, quadI, triFaces, quadFaces);
}
return (triI + quadI - oldIndices);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::face::face(const triFace& f)
:
labelList(f)
{}
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
int Foam::face::compare(const face& a, const face& b)
{
// Basic rule: we assume that the sequence of labels in each list
// will be circular in the same order (but not necessarily in the
// same direction or from the same starting point).
// Trivial reject: faces are different size
label sizeA = a.size();
label sizeB = b.size();
if (sizeA != sizeB || sizeA == 0)
{
return 0;
}
else if (sizeA == 1)
{
if (a[0] == b[0])
{
return 1;
}
else
{
return 0;
}
}
ConstCirculator<face> aCirc(a);
ConstCirculator<face> bCirc(b);
// Rotate face b until its element matches the starting element of face a.
do
{
if (aCirc() == bCirc())
{
// Set bCirc fulcrum to its iterator and increment the iterators
bCirc.setFulcrumToIterator();
++aCirc;
++bCirc;
break;
}
} while (bCirc.circulate(CirculatorBase::CLOCKWISE));
// If the circulator has stopped then faces a and b do not share a matching
// point. Doesn't work on matching, single element face.
if (!bCirc.circulate())
{
return 0;
}
// Look forwards around the faces for a match
do
{
if (aCirc() != bCirc())
{
break;
}
}
while
(
aCirc.circulate(CirculatorBase::CLOCKWISE),
bCirc.circulate(CirculatorBase::CLOCKWISE)
);
// If the circulator has stopped then faces a and b matched.
if (!aCirc.circulate())
{
return 1;
}
else
{
// Reset the circulators back to their fulcrum
aCirc.setIteratorToFulcrum();
bCirc.setIteratorToFulcrum();
++aCirc;
--bCirc;
}
// Look backwards around the faces for a match
do
{
if (aCirc() != bCirc())
{
break;
}
}
while
(
aCirc.circulate(CirculatorBase::CLOCKWISE),
bCirc.circulate(CirculatorBase::ANTICLOCKWISE)
);
// If the circulator has stopped then faces a and b matched.
if (!aCirc.circulate())
{
return -1;
}
return 0;
}
bool Foam::face::sameVertices(const face& a, const face& b)
{
label sizeA = a.size();
label sizeB = b.size();
// Trivial reject: faces are different size
if (sizeA != sizeB)
{
return false;
}
// Check faces with a single vertex
else if (sizeA == 1)
{
if (a[0] == b[0])
{
return true;
}
else
{
return false;
}
}
forAll(a, i)
{
// Count occurrences of a[i] in a
label aOcc = 0;
forAll(a, j)
{
if (a[i] == a[j]) aOcc++;
}
// Count occurrences of a[i] in b
label bOcc = 0;
forAll(b, j)
{
if (a[i] == b[j]) bOcc++;
}
// Check if occurrences of a[i] in a and b are the same
if (aOcc != bOcc) return false;
}
return true;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::face::collapse()
{
if (size() > 1)
{
label ci = 0;
for (label i=1; i<size(); i++)
{
if (operator[](i) != operator[](ci))
{
operator[](++ci) = operator[](i);
}
}
if (operator[](ci) != operator[](0))
{
ci++;
}
setSize(ci);
}
return size();
}
void Foam::face::flip()
{
const label n = size();
if (n > 2)
{
for (label i=1; i < (n+1)/2; ++i)
{
Swap(operator[](i), operator[](n-i));
}
}
}
Foam::point Foam::face::centre(const pointField& points) const
{
// Calculate the centre by breaking the face into triangles and
// area-weighted averaging their centres
const label nPoints = size();
// If the face is a triangle, do a direct calculation
if (nPoints == 3)
{
return
(1.0/3.0)
*(
points[operator[](0)]
+ points[operator[](1)]
+ points[operator[](2)]
);
}
point centrePoint = Zero;
for (label pI=0; pI<nPoints; ++pI)
{
centrePoint += points[operator[](pI)];
}
centrePoint /= nPoints;
scalar sumA = 0;
vector sumAc = Zero;
for (label pI=0; pI<nPoints; ++pI)
{
const point& nextPoint = points[operator[]((pI + 1) % nPoints)];
// Calculate 3*triangle centre
const vector ttc
(
points[operator[](pI)]
+ nextPoint
+ centrePoint
);
// Calculate 2*triangle area
const scalar ta = Foam::mag
(
(points[operator[](pI)] - centrePoint)
^ (nextPoint - centrePoint)
);
sumA += ta;
sumAc += ta*ttc;
}
if (sumA > VSMALL)
{
return sumAc/(3.0*sumA);
}
else
{
return centrePoint;
}
}
Foam::vector Foam::face::normal(const pointField& p) const
{
const label nPoints = size();
// Calculate the normal by summing the face triangle normals.
// Changed to deal with small concavity by using a central decomposition
//
// If the face is a triangle, do a direct calculation to avoid round-off
// error-related problems
//
if (nPoints == 3)
{
return triPointRef
(
p[operator[](0)],
p[operator[](1)],
p[operator[](2)]
).normal();
}
label pI;
point centrePoint = Zero;
for (pI = 0; pI < nPoints; ++pI)
{
centrePoint += p[operator[](pI)];
}
centrePoint /= nPoints;
vector n = Zero;
point nextPoint = centrePoint;
for (pI = 0; pI < nPoints; ++pI)
{
if (pI < nPoints - 1)
{
nextPoint = p[operator[](pI + 1)];
}
else
{
nextPoint = p[operator[](0)];
}
// Note: for best accuracy, centre point always comes last
//
n += triPointRef
(
p[operator[](pI)],
nextPoint,
centrePoint
).normal();
}
return n;
}
Foam::face Foam::face::reverseFace() const
{
// Reverse the label list and return
// The starting points of the original and reverse face are identical.
const labelList& f = *this;
labelList newList(size());
newList[0] = f[0];
for (label pointi = 1; pointi < newList.size(); pointi++)
{
newList[pointi] = f[size() - pointi];
}
return face(xferMove(newList));
}
Foam::label Foam::face::which(const label globalIndex) const
{
const labelList& f = *this;
forAll(f, localIdx)
{
if (f[localIdx] == globalIndex)
{
return localIdx;
}
}
return -1;
}
Foam::scalar Foam::face::sweptVol
(
const pointField& oldPoints,
const pointField& newPoints
) const
{
// This Optimization causes a small discrepancy between the swept-volume of
// opposite faces of complex cells with triangular faces opposing polygons.
// It could be used without problem for tetrahedral cells
// if (size() == 3)
// {
// return
// (
// triPointRef
// (
// oldPoints[operator[](0)],
// oldPoints[operator[](1)],
// oldPoints[operator[](2)]
// ).sweptVol
// (
// triPointRef
// (
// newPoints[operator[](0)],
// newPoints[operator[](1)],
// newPoints[operator[](2)]
// )
// )
// );
// }
scalar sv = 0;
// Calculate the swept volume by breaking the face into triangles and
// summing their swept volumes.
// Changed to deal with small concavity by using a central decomposition
point centreOldPoint = centre(oldPoints);
point centreNewPoint = centre(newPoints);
label nPoints = size();
for (label pi=0; pi<nPoints-1; ++pi)
{
// Note: for best accuracy, centre point always comes last
sv += triPointRef
(
centreOldPoint,
oldPoints[operator[](pi)],
oldPoints[operator[](pi + 1)]
).sweptVol
(
triPointRef
(
centreNewPoint,
newPoints[operator[](pi)],
newPoints[operator[](pi + 1)]
)
);
}
sv += triPointRef
(
centreOldPoint,
oldPoints[operator[](nPoints-1)],
oldPoints[operator[](0)]
).sweptVol
(
triPointRef
(
centreNewPoint,
newPoints[operator[](nPoints-1)],
newPoints[operator[](0)]
)
);
return sv;
}
Foam::tensor Foam::face::inertia
(
const pointField& p,
const point& refPt,
scalar density
) const
{
// If the face is a triangle, do a direct calculation
if (size() == 3)
{
return triPointRef
(
p[operator[](0)],
p[operator[](1)],
p[operator[](2)]
).inertia(refPt, density);
}
const point ctr = centre(p);
tensor J = Zero;
forAll(*this, i)
{
J += triPointRef
(
p[operator[](i)],
p[operator[](fcIndex(i))],
ctr
).inertia(refPt, density);
}
return J;
}
Foam::edgeList Foam::face::edges() const
{
const labelList& points = *this;
edgeList e(points.size());
for (label pointi = 0; pointi < points.size() - 1; ++pointi)
{
e[pointi] = edge(points[pointi], points[pointi + 1]);
}
// Add last edge
e.last() = edge(points.last(), points[0]);
return e;
}
int Foam::face::edgeDirection(const edge& e) const
{
forAll(*this, i)
{
if (operator[](i) == e.start())
{
if (operator[](rcIndex(i)) == e.end())
{
// Reverse direction
return -1;
}
else if (operator[](fcIndex(i)) == e.end())
{
// Forward direction
return 1;
}
// No match
return 0;
}
else if (operator[](i) == e.end())
{
if (operator[](rcIndex(i)) == e.start())
{
// Forward direction
return 1;
}
else if (operator[](fcIndex(i)) == e.start())
{
// Reverse direction
return -1;
}
// No match
return 0;
}
}
// Not found
return 0;
}
Foam::label Foam::face::nTriangles(const pointField&) const
{
return nTriangles();
}
Foam::label Foam::face::triangles
(
const pointField& points,
label& triI,
faceList& triFaces
) const
{
label quadI = 0;
faceList quadFaces;
return split(SPLITTRIANGLE, points, triI, quadI, triFaces, quadFaces);
}
Foam::label Foam::face::nTrianglesQuads
(
const pointField& points,
label& triI,
label& quadI
) const
{
faceList triFaces;
faceList quadFaces;
return split(COUNTQUAD, points, triI, quadI, triFaces, quadFaces);
}
Foam::label Foam::face::trianglesQuads
(
const pointField& points,
label& triI,
label& quadI,
faceList& triFaces,
faceList& quadFaces
) const
{
return split(SPLITQUAD, points, triI, quadI, triFaces, quadFaces);
}
Foam::label Foam::longestEdge(const face& f, const pointField& pts)
{
const edgeList& eds = f.edges();
label longestEdgeI = -1;
scalar longestEdgeLength = -SMALL;
forAll(eds, edI)
{
scalar edgeLength = eds[edI].mag(pts);
if (edgeLength > longestEdgeLength)
{
longestEdgeI = edI;
longestEdgeLength = edgeLength;
}
}
return longestEdgeI;
}
// ************************************************************************* //