openfoam/applications/test/syncTools/Test-syncTools.C
Mark Olesen d597b3f959 STYLE: check iterator validity with good() instead of found()
- aligns better with other container checks
2023-02-10 17:12:48 +01:00

978 lines
24 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / 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) 2021 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 <http://www.gnu.org/licenses/>.
Application
syncToolsTest
Description
Test some functionality in syncTools.
\*---------------------------------------------------------------------------*/
#include "syncTools.H"
#include "argList.H"
#include "polyMesh.H"
#include "Time.H"
#include "Random.H"
#include "PackedList.H"
#include "flipOp.H"
#include "pointList.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void testPackedList(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing PackedList synchronisation." << endl;
{
PackedList<3> bits(mesh.nEdges());
forAll(bits, i)
{
bits.set(i, rndGen.position<label>(0,3));
}
labelList edgeValues(mesh.nEdges());
forAll(bits, i)
{
edgeValues[i] = bits.get(i);
}
PackedList<3> maxBits(bits);
labelList maxEdgeValues(edgeValues);
syncTools::syncEdgeList(mesh, bits, minEqOp<unsigned int>(), 0);
syncTools::syncEdgeList(mesh, edgeValues, minEqOp<label>(), 0);
syncTools::syncEdgeList(mesh, maxBits, maxEqOp<unsigned int>(), 0);
syncTools::syncEdgeList
(
mesh,
maxEdgeValues,
maxEqOp<label>(),
0
);
forAll(bits, i)
{
if
(
edgeValues[i] != label(bits.get(i))
|| maxEdgeValues[i] != label(maxBits.get(i))
)
{
FatalErrorInFunction
<< "edge:" << i
<< " minlabel:" << edgeValues[i]
<< " minbits:" << bits.get(i)
<< " maxLabel:" << maxEdgeValues[i]
<< " maxBits:" << maxBits.get(i)
<< exit(FatalError);
}
}
}
{
PackedList<3> bits(mesh.nPoints());
forAll(bits, i)
{
bits.set(i, rndGen.position<label>(0,3));
}
labelList pointValues(mesh.nPoints());
forAll(bits, i)
{
pointValues[i] = bits.get(i);
}
PackedList<3> maxBits(bits);
labelList maxPointValues(pointValues);
syncTools::syncPointList(mesh, bits, minEqOp<unsigned int>(), 0);
syncTools::syncPointList(mesh, pointValues, minEqOp<label>(), 0);
syncTools::syncPointList(mesh, maxBits, maxEqOp<unsigned int>(), 0);
syncTools::syncPointList
(
mesh,
maxPointValues,
maxEqOp<label>(),
0
);
forAll(bits, i)
{
if
(
pointValues[i] != label(bits.get(i))
|| maxPointValues[i] != label(maxBits.get(i))
)
{
FatalErrorInFunction
<< "point:" << i
<< " at:" << mesh.points()[i]
<< " minlabel:" << pointValues[i]
<< " minbits:" << bits.get(i)
<< " maxLabel:" << maxPointValues[i]
<< " maxBits:" << maxBits.get(i)
<< exit(FatalError);
}
}
}
{
PackedList<3> bits(mesh.nFaces());
forAll(bits, facei)
{
bits.set(facei, rndGen.position<label>(0,3));
}
labelList faceValues(mesh.nFaces());
forAll(bits, facei)
{
faceValues[facei] = bits.get(facei);
}
PackedList<3> maxBits(bits);
labelList maxFaceValues(faceValues);
syncTools::syncFaceList(mesh, bits, minEqOp<unsigned int>());
syncTools::syncFaceList(mesh, faceValues, minEqOp<label>());
syncTools::syncFaceList(mesh, maxBits, maxEqOp<unsigned int>());
syncTools::syncFaceList(mesh, maxFaceValues, maxEqOp<label>());
forAll(bits, facei)
{
if
(
faceValues[facei] != label(bits.get(facei))
|| maxFaceValues[facei] != label(maxBits.get(facei))
)
{
FatalErrorInFunction
<< "face:" << facei
<< " minlabel:" << faceValues[facei]
<< " minbits:" << bits.get(facei)
<< " maxLabel:" << maxFaceValues[facei]
<< " maxBits:" << maxBits.get(facei)
<< exit(FatalError);
}
}
}
}
void testSparseData(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing Map synchronisation." << endl;
WarningInFunction
<< "Position test of sparse data only correct for cases without cyclics"
<< " with shared points." << endl;
primitivePatch allBoundary
(
SubList<face>
(
mesh.faces(),
mesh.nBoundaryFaces(),
mesh.nInternalFaces()
),
mesh.points()
);
const pointField& localPoints = allBoundary.localPoints();
// Point data
// ~~~~~~~~~~
{
// Create some data. Use slightly perturbed positions.
Map<point> sparseData;
pointField fullData(mesh.nPoints(), point(GREAT, GREAT, GREAT));
forAll(localPoints, i)
{
const point pt = localPoints[i] + 1e-4*rndGen.sample01<vector>();
label meshPointi = allBoundary.meshPoints()[i];
sparseData.insert(meshPointi, pt);
fullData[meshPointi] = pt;
}
syncTools::syncPointMap
(
mesh,
sparseData,
minMagSqrEqOp<point>()
// true // apply separation
);
syncTools::syncPointList
(
mesh,
fullData,
minMagSqrEqOp<point>(),
point(GREAT, GREAT, GREAT)
// true // apply separation
);
// Compare.
// 1. Is all fullData also present in sparseData and same value
forAll(fullData, meshPointi)
{
const point& fullPt = fullData[meshPointi];
if (fullPt != point(GREAT, GREAT, GREAT))
{
const point& sparsePt = sparseData[meshPointi];
if (fullPt != sparsePt)
{
FatalErrorInFunction
<< "point:" << meshPointi
<< " full:" << fullPt
<< " sparse:" << sparsePt
<< exit(FatalError);
}
}
}
// 2. Does sparseData contain more?
forAllConstIters(sparseData, iter)
{
const label meshPointi = iter.key();
const point& sparsePt = iter.val();
const point& fullPt = fullData[meshPointi];
if (fullPt != sparsePt)
{
FatalErrorInFunction
<< "point:" << meshPointi
<< " full:" << fullPt
<< " sparse:" << sparsePt
<< exit(FatalError);
}
}
}
// Edge data
// ~~~~~~~~~
{
// Create some data. Use slightly perturbed positions.
EdgeMap<point> sparseData;
pointField fullData(mesh.nEdges(), point(GREAT, GREAT, GREAT));
const edgeList& edges = allBoundary.edges();
const labelList meshEdges = allBoundary.meshEdges
(
mesh.edges(),
mesh.pointEdges()
);
forAll(edges, i)
{
const edge& e = edges[i];
const point pt =
e.centre(localPoints) + 1e-4*rndGen.sample01<vector>();
label meshEdgeI = meshEdges[i];
sparseData.insert(mesh.edges()[meshEdgeI], pt);
fullData[meshEdgeI] = pt;
}
syncTools::syncEdgeMap
(
mesh,
sparseData,
minMagSqrEqOp<point>()
);
syncTools::syncEdgeList
(
mesh,
fullData,
minMagSqrEqOp<point>(),
point(GREAT, GREAT, GREAT)
);
// Compare.
// 1. Is all fullData also present in sparseData and same value
forAll(fullData, meshEdgeI)
{
const point& fullPt = fullData[meshEdgeI];
if (fullPt != point(GREAT, GREAT, GREAT))
{
const point& sparsePt = sparseData[mesh.edges()[meshEdgeI]];
if (fullPt != sparsePt)
{
FatalErrorInFunction
<< "edge:" << meshEdgeI
<< " points:" << mesh.edges()[meshEdgeI]
<< " full:" << fullPt
<< " sparse:" << sparsePt
<< exit(FatalError);
}
}
}
// 2. Does sparseData contain more?
forAll(fullData, meshEdgeI)
{
const edge& e = mesh.edges()[meshEdgeI];
const auto iter = sparseData.cfind(e);
if (iter.good())
{
const point& sparsePt = iter.val();
const point& fullPt = fullData[meshEdgeI];
if (fullPt != sparsePt)
{
FatalErrorInFunction
<< "Extra edge:" << meshEdgeI
<< " points:" << mesh.edges()[meshEdgeI]
<< " full:" << fullPt
<< " sparse:" << sparsePt
<< exit(FatalError);
}
}
}
}
}
void testPointSync(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing point-wise data synchronisation." << endl;
// Test position.
{
pointField syncedPoints(mesh.points());
syncTools::syncPointPositions
(
mesh,
syncedPoints,
minMagSqrEqOp<point>(),
point(GREAT, GREAT, GREAT)
);
forAll(syncedPoints, pointi)
{
if (mag(syncedPoints[pointi] - mesh.points()[pointi]) > SMALL)
{
FatalErrorInFunction
<< "Point " << pointi
<< " original location " << mesh.points()[pointi]
<< " synced location " << syncedPoints[pointi]
<< exit(FatalError);
}
}
}
// Test masterPoints
{
labelList nMasters(mesh.nPoints(), Zero);
const bitSet isMasterPoint(syncTools::getMasterPoints(mesh));
forAll(isMasterPoint, pointi)
{
if (isMasterPoint.test(pointi))
{
nMasters[pointi] = 1;
}
}
syncTools::syncPointList
(
mesh,
nMasters,
plusEqOp<label>(),
0
);
forAll(nMasters, pointi)
{
if (nMasters[pointi] != 1)
{
WarningInFunction
<< "Point " << pointi
<< " original location " << mesh.points()[pointi]
<< " has " << nMasters[pointi]
<< " masters."
<< endl;
}
}
}
}
void testEdgeSync(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing edge-wise data synchronisation." << endl;
const edgeList& edges = mesh.edges();
// Test position.
{
pointField syncedMids(edges.size());
forAll(syncedMids, edgeI)
{
syncedMids[edgeI] = edges[edgeI].centre(mesh.points());
}
syncTools::syncEdgePositions
(
mesh,
syncedMids,
minMagSqrEqOp<point>(),
point(GREAT, GREAT, GREAT)
);
forAll(syncedMids, edgeI)
{
point eMid = edges[edgeI].centre(mesh.points());
if (mag(syncedMids[edgeI] - eMid) > SMALL)
{
FatalErrorInFunction
<< "Edge " << edgeI
<< " original midpoint " << eMid
<< " synced location " << syncedMids[edgeI]
<< exit(FatalError);
}
}
}
// Test masterEdges
{
labelList nMasters(edges.size(), Zero);
const bitSet isMasterEdge(syncTools::getMasterEdges(mesh));
forAll(isMasterEdge, edgeI)
{
if (isMasterEdge.test(edgeI))
{
nMasters[edgeI] = 1;
}
}
syncTools::syncEdgeList
(
mesh,
nMasters,
plusEqOp<label>(),
0
);
forAll(nMasters, edgeI)
{
if (nMasters[edgeI] != 1)
{
const edge& e = edges[edgeI];
WarningInFunction
<< "Edge " << edgeI
<< " at:" << mesh.points()[e[0]] << mesh.points()[e[1]]
<< " has " << nMasters[edgeI]
<< " masters."
<< endl;
}
}
}
}
typedef Pair<point> PointPair;
class edgePointCombineOp
{
public:
void operator()(PointPair& x, const PointPair& y) const
{
if
(
(y[0] < x[0])
|| (y[0] == x[0] && y[1] < x[1])
)
{
x = y;
}
}
};
class edgePointTransformOp
{
public:
void operator()
(
const vectorTensorTransform& vt,
const bool forward,
List<PointPair>& fld
) const
{
pointField points0(fld.size());
pointField points1(fld.size());
forAll(fld, i)
{
points0[i] = fld[i].first();
points1[i] = fld[i].second();
}
pointField points0New;
pointField points1New;
if (forward)
{
points0New = vt.transformPosition(points0);
points1New = vt.transformPosition(points1);
}
else
{
points0New = vt.invTransformPosition(points0);
points1New = vt.invTransformPosition(points1);
}
forAll(fld, i)
{
fld[i] = PointPair(points0New[i], points1New[i]);
}
}
};
class edgePointFlipOp
{
public:
PointPair operator()(const PointPair& val) const
{
PointPair newVal(val);
newVal.flip();
return newVal;
}
};
void testEdgeFlip2(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing edge-wise (oriented) data synchronisation." << endl;
const edgeList& edges = mesh.edges();
const pointField& points = mesh.points();
// Test position.
List<PointPair> synEdgeEnds(edges.size());
{
forAll(synEdgeEnds, edgeI)
{
const edge& e = edges[edgeI];
synEdgeEnds[edgeI] = PointPair
(
points[e[0]],
points[e[1]]
);
}
}
// 1. Ignore flipping
{
List<PointPair> fld(synEdgeEnds);
syncTools::syncEdgeList
(
mesh,
fld,
edgePointCombineOp(),
PointPair(point::max, point::max),
edgePointTransformOp(),
identityOp()
);
forAll(fld, edgeI)
{
const edge& e = edges[edgeI];
const PointPair edgeEnd
(
points[e[0]],
points[e[1]]
);
const PointPair& sync = fld[edgeI];
if
(
(mag(edgeEnd[0] - sync[0]) > SMALL)
|| (mag(edgeEnd[1] - sync[1]) > SMALL)
)
{
WarningInFunction
<< "Edge " << edgeI
<< " original endpoints " << edgeEnd
<< " synced endpoints " << sync
<< endl;
}
}
}
// 2. Use flipping operator. Should produce no warnings
{
syncTools::syncEdgeList
(
mesh,
synEdgeEnds,
edgePointCombineOp(),
PointPair(point::max, point::max),
edgePointTransformOp(),
edgePointFlipOp()
);
forAll(synEdgeEnds, edgeI)
{
const edge& e = edges[edgeI];
const PointPair edgeEnd
(
points[e[0]],
points[e[1]]
);
const PointPair& sync = synEdgeEnds[edgeI];
if
(
(mag(edgeEnd[0] - sync[0]) > SMALL)
|| (mag(edgeEnd[1] - sync[1]) > SMALL)
)
{
FatalErrorInFunction
<< "Edge " << edgeI
<< " original endpoints " << edgeEnd
<< " synced endpoints " << sync
<< exit(FatalError);
}
}
}
}
void testEdgeFlip(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing edge-wise (oriented) data synchronisation."
<< endl;
const edgeList& edges = mesh.edges();
const pointField& points = mesh.points();
// Test vector.
vectorField synEdgeVecs(edges.size());
{
forAll(synEdgeVecs, edgeI)
{
synEdgeVecs[edgeI] = edges[edgeI].unitVec(points);
}
}
// Without flipping (should produce warnings)
{
vectorField fld(synEdgeVecs);
// Ignore flipping
syncTools::syncEdgeList
(
mesh,
fld,
minEqOp<vector>(),
point::max
);
forAll(fld, edgeI)
{
const edge& e = edges[edgeI];
const vector eVec(e.unitVec(points));
if ((eVec & fld[edgeI]) < (1-SMALL))
{
WarningInFunction
<< "Edge " << edgeI
<< " at " << e.line(points)
<< " original vector " << eVec
<< " synced vector " << fld[edgeI]
<< " diff:" << (eVec & fld[edgeI])
<< endl;
}
}
}
// With consistent flipping. Should never produce difference
{
syncTools::syncEdgeList
(
mesh,
synEdgeVecs,
minMagSqrEqOp<vector>(),
point::max,
mapDistribute::transform(),
flipOp()
);
forAll(synEdgeVecs, edgeI)
{
const edge& e = edges[edgeI];
const vector eVec(e.unitVec(points));
if ((eVec & synEdgeVecs[edgeI]) < (1-SMALL))
{
FatalErrorInFunction
<< "Edge " << edgeI
<< " at " << e.line(points)
<< " original vector " << eVec
<< " synced vector " << synEdgeVecs[edgeI]
<< " diff:" << (eVec & synEdgeVecs[edgeI])
<< exit(FatalError);
}
}
}
}
class pointListOps
{
public:
void operator()(pointList& lhs, const pointList& rhs) const
{
forAll(lhs, i)
{
point& l = lhs[i];
const point& r = rhs[i];
maxMagSqrEqOp<vector>()(l, r);
}
}
void operator()
(
const vectorTensorTransform& vt,
const bool forward,
List<pointList>& fld
) const
{
if (forward)
{
for (auto& elems : fld)
{
for (auto& elem : elems)
{
elem = vt.transformPosition(elem);
}
}
}
else
{
for (auto& elems : fld)
{
for (auto& elem : elems)
{
elem = vt.invTransformPosition(elem);
}
}
}
}
//- Transform patch-based field
void operator()(const coupledPolyPatch& cpp, List<pointList>& fld) const
{
forAll(fld, facei)
{
pointList& pts = fld[facei];
for (auto& pt : pts)
{
cpp.transformPosition(pt, facei);
}
}
}
};
void testFaceSync(const polyMesh& mesh, Random& rndGen)
{
Info<< nl << "Testing face-wise data synchronisation." << endl;
// Test position.
{
pointField syncedFc(mesh.faceCentres());
syncTools::syncFacePositions
(
mesh,
syncedFc,
maxMagSqrEqOp<point>()
);
forAll(syncedFc, facei)
{
if (mag(syncedFc[facei] - mesh.faceCentres()[facei]) > SMALL)
{
FatalErrorInFunction
<< "Face " << facei
<< " original centre " << mesh.faceCentres()[facei]
<< " synced centre " << syncedFc[facei]
<< exit(FatalError);
}
}
}
// Test non-contiguous data (uses streaming)
{
List<pointList> syncedFc(mesh.nFaces());
const pointField& fcs = mesh.faceCentres();
forAll(fcs, facei)
{
const point& fc = fcs[facei];
syncedFc[facei].setSize(2, fc);
}
SubList<pointList> bndValues
(
syncedFc,
mesh.nBoundaryFaces(),
mesh.nInternalFaces()
);
syncTools::syncBoundaryFaceList
(
mesh,
bndValues,
pointListOps(), //does maxMagSqrEqOp<pointList>()
pointListOps() //transforms all
);
forAll(syncedFc, facei)
{
const point& fc = fcs[facei];
const pointList& fld = syncedFc[facei];
forAll(fld, i)
{
if (mag(fld[i] - fc) > SMALL)
{
FatalErrorInFunction
<< "Face " << facei
<< " original centre " << fc
<< " synced centre " << fld[i]
<< exit(FatalError);
}
}
}
}
// Test masterFaces
{
labelList nMasters(mesh.nFaces(), Zero);
const bitSet isMasterFace(syncTools::getMasterFaces(mesh));
forAll(isMasterFace, facei)
{
if (isMasterFace.test(facei))
{
nMasters[facei] = 1;
}
}
syncTools::syncFaceList
(
mesh,
nMasters,
plusEqOp<label>()
);
forAll(nMasters, facei)
{
if (nMasters[facei] != 1)
{
FatalErrorInFunction
<< "Face " << facei
<< " centre " << mesh.faceCentres()[facei]
<< " has " << nMasters[facei]
<< " masters."
<< exit(FatalError);
}
}
}
}
// Main program:
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createPolyMesh.H"
Random rndGen(5341*(Pstream::myProcNo()+1));
// Face sync
testFaceSync(mesh, rndGen);
// Edge sync
testEdgeSync(mesh, rndGen);
// Edge sync and flip
testEdgeFlip(mesh, rndGen);
// Edge sync and flip of more complex structure
testEdgeFlip2(mesh, rndGen);
// Point sync
testPointSync(mesh, rndGen);
// PackedList synchronisation
testPackedList(mesh, rndGen);
// Sparse synchronisation
testSparseData(mesh, rndGen);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //