BUG: parallel construct finiteArea fails with arbitrary connections (#2152)

- the case of 'fan-like' processor was previously assumed to be
  rare (see merge-request !490 and issue #2084).

  However, Vaggelis Papoutsis noticed that even fairly normal geometries
  can trigger problems.

- replaced the old patch/patch matching style with a more general
  edge-based synchronisation and matching that appears to handle
  the corner cases inherently. The internal communication overhead
  is essentially unchanged, and the logic is simpler.

ENH: additional framework for managing patch connectivity
This commit is contained in:
Mark Olesen
2021-10-01 18:26:49 +02:00
committed by Andrew Heather
parent 9c1f94d4fd
commit 8e45108905
10 changed files with 1369 additions and 753 deletions

View File

@ -2,6 +2,7 @@ faMesh/faGlobalMeshData/faGlobalMeshData.C
faMesh/faMesh.C
faMesh/faMeshDemandDrivenData.C
faMesh/faMeshPatches.C
faMesh/faMeshTopology.C
faMesh/faMeshUpdate.C
faMesh/faBoundaryMesh/faBoundaryMesh.C

View File

@ -111,15 +111,15 @@ static labelList selectPatchFaces
void Foam::faMesh::initPatch() const
{
if (patchPtr_)
{
delete patchPtr_;
}
patchPtr_ = new uindirectPrimitivePatch
patchPtr_.reset
(
UIndirectList<face>(mesh().faces(), faceLabels_),
mesh().points()
new uindirectPrimitivePatch
(
UIndirectList<face>(mesh().faces(), faceLabels_),
mesh().points()
)
);
bndConnectPtr_.reset(nullptr);
}
@ -172,8 +172,9 @@ void Foam::faMesh::clearGeomNotAreas() const
{
DebugInFunction << "Clearing geometry" << endl;
patchPtr_.reset(nullptr);
bndConnectPtr_.reset(nullptr);
deleteDemandDrivenData(SPtr_);
deleteDemandDrivenData(patchPtr_);
deleteDemandDrivenData(patchStartsPtr_);
deleteDemandDrivenData(LePtr_);
deleteDemandDrivenData(magLePtr_);
@ -256,6 +257,7 @@ Foam::faMesh::faMesh(const polyMesh& pMesh)
),
comm_(Pstream::worldComm),
patchPtr_(nullptr),
bndConnectPtr_(nullptr),
lduPtr_(nullptr),
curTimeIndex_(time().timeIndex()),
SPtr_(nullptr),
@ -349,6 +351,7 @@ Foam::faMesh::faMesh
),
comm_(Pstream::worldComm),
patchPtr_(nullptr),
bndConnectPtr_(nullptr),
lduPtr_(nullptr),
curTimeIndex_(time().timeIndex()),
SPtr_(nullptr),

View File

@ -33,6 +33,9 @@ Description
SourceFiles
faMesh.C
faMeshDemandDrivenData.C
faMeshPatches.C
faMeshTopology.C
faMeshUpdate.C
Author
Zeljko Tukovic, FMENA
@ -71,7 +74,6 @@ namespace Foam
class faMeshLduAddressing;
class faMeshMapper;
class faPatchData;
template<class T> class LabelledItem;
/*---------------------------------------------------------------------------*\
Class faMesh Declaration
@ -86,6 +88,107 @@ class faMesh
public faSolution,
public data
{
// Private (internal) classes/structures
//- A (proc, patchi, patchEdgei) tuple used internally for managing
//- patch/patch bookkeeping during construction.
// Finite-area patches are stored with negated indices, which makes
// them readily identifiable and always sort before normal patches.
// Note
struct patchTuple
:
public FixedList<label, 4>
{
// Constructors
// Inherit constructors
using FixedList<label, 4>::FixedList;
//- Default construct as 'invalid'
patchTuple()
{
clear();
}
// Static Member Functions
// Globally consistent ordering
//
// 1. sort left/right as lower/higher processor connection
// 2. sort by proc/patch/patch index
static void sort(UList<Pair<patchTuple>>& list)
{
for (auto& tuples : list)
{
tuples.sort();
}
Foam::stableSort(list);
}
// Member Functions
//- Reset to 'invalid'
void clear()
{
procNo(-1);
patchi(labelMax);
patchEdgei(-1);
meshFacei(-1);
}
//- Valid if proc and edge are non-negative
bool valid() const noexcept
{
return (procNo() >= 0 && patchEdgei() >= 0);
}
// Processor is the first sort index
label procNo() const { return (*this)[0]; }
void procNo(label val) { (*this)[0] = val; }
// PatchId (-ve for finiteArea patches) is the second sort index
label patchi() const { return (*this)[1]; }
void patchi(label val) { (*this)[1] = val; }
// The patch edge index (on the finiteArea patch)
// is the third sort index
label patchEdgei() const { return (*this)[2]; }
void patchEdgei(label val) { (*this)[2] = val; }
// The processor-local mesh face is the fourth sort index
label meshFacei() const { return (*this)[3]; }
void meshFacei(label val) { (*this)[3] = val; }
//- Return the real patchId
label realPatchi() const
{
const label id = patchi();
return (id < 0 ? -(id + 1) : id);
}
//- Set patchId as finiteArea
void faPatchi(label val)
{
patchi(-(val + 1));
}
//- Considered to be finiteArea if patchi < 0
bool is_finiteArea() const noexcept
{
return (patchi() < 0);
}
//- Considered to be processor local
bool is_localProc() const noexcept
{
return (procNo() == Pstream::myProcNo());
}
};
// Private Data
//- Face labels
@ -131,7 +234,10 @@ class faMesh
// Demand-driven data
//- Primitive patch
mutable uindirectPrimitivePatch* patchPtr_;
mutable std::unique_ptr<uindirectPrimitivePatch> patchPtr_;
//- List of proc/mesh-face for boundary edge neighbours
mutable std::unique_ptr<List<labelPair>> bndConnectPtr_;
//- Ldu addressing data
mutable faMeshLduAddressing* lduPtr_;
@ -211,6 +317,20 @@ class faMesh
//- Set primitive mesh data
void setPrimitiveMeshData();
//- Get list of (proc/patchi/patchEdgei/meshFacei) tuple pairs in an
//- globally consistent ordering
List<Pair<patchTuple>> getBoundaryEdgeConnections() const;
//- Determine the boundary edge neighbour connections
void calcBoundaryConnections() const;
//- Define boundary edge neighbours (proc/face) based on
//- gathered topology information
void setBoundaryConnections
(
const List<Pair<patchTuple>>& bndEdgeConnections
) const;
// Private member functions to calculate demand driven data
@ -268,9 +388,6 @@ class faMesh
// Helpers
//- Get the polyPatch pairs for the boundary edges (natural order)
List<LabelledItem<edge>> getBoundaryEdgePatchPairs() const;
//- Create a single patch
PtrList<faPatch> createOnePatch
(
@ -286,14 +403,6 @@ class faMesh
const dictionary* defaultPatchDefinition = nullptr
) const;
//- Reorder processor edges using order of the
//- neighbour processorPolyPatch
void reorderProcEdges
(
faPatchData& patchDef,
const List<LabelledItem<edge>>& bndEdgePatchPairs
) const;
public:
@ -451,7 +560,7 @@ public:
//- Return constant reference to boundary mesh
inline const faBoundaryMesh& boundary() const noexcept;
//- Return faMesh face labels
//- Return the underlying polyMesh face labels
inline const labelList& faceLabels() const noexcept;
@ -486,6 +595,18 @@ public:
return (edgeIndex < nInternalEdges_);
}
//- List of proc/face for the boundary edge neighbours
//- using primitive patch edge numbering.
inline const List<labelPair>& boundaryConnections() const;
//- Boundary edge neighbour processors
//- (does not include own proc)
labelList boundaryProcs() const;
//- List of proc/size for the boundary edge neighbour processors
//- (does not include own proc)
List<labelPair> boundaryProcSizes() const;
// Mesh motion and morphing
@ -590,6 +711,7 @@ public:
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "faMeshI.H"

View File

@ -139,4 +139,15 @@ inline Foam::uindirectPrimitivePatch& Foam::faMesh::patch()
}
inline const Foam::List<Foam::labelPair>&
Foam::faMesh::boundaryConnections() const
{
if (!bndConnectPtr_)
{
calcBoundaryConnections();
}
return *bndConnectPtr_;
}
// ************************************************************************* //

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,803 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "faMesh.H"
#include "globalMeshData.H"
#include "indirectPrimitivePatch.H"
#include "edgeHashes.H"
#include "foamVtkLineWriter.H"
#include "foamVtkIndPatchWriter.H"
#include "foamVtkUIndPatchWriter.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Print out edges as point pairs
template<class PatchType>
static void printPatchEdges
(
Ostream& os,
const PatchType& p,
const labelList& edgeIds,
label maxOutput = 10
)
{
label nOutput = 0;
for (const label patchEdgei : edgeIds)
{
const edge e(p.meshEdge(patchEdgei));
os << " "
<< p.points()[e.first()] << ' '
<< p.points()[e.second()] << nl;
++nOutput;
if (maxOutput > 0 && nOutput >= maxOutput)
{
os << " ... suppressing further output" << nl;
break;
}
}
}
} // End namespace Foam
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::List<Foam::Pair<Foam::faMesh::patchTuple>>
Foam::faMesh::getBoundaryEdgeConnections() const
{
const polyBoundaryMesh& pbm = mesh().boundaryMesh();
const label nNonProcessor = pbm.nNonProcessor();
const label nInternalEdges = patch().nInternalEdges();
const label nBoundaryEdges = patch().nBoundaryEdges();
// The output result:
List<Pair<patchTuple>> bndEdgeConnections(nBoundaryEdges);
// Map edges (mesh numbering) back to a boundary index
EdgeMap<label> edgeToBoundaryIndex(2*nBoundaryEdges);
label nBadEdges(0);
labelHashSet badEdges(2*nBoundaryEdges);
{
// Local collection structure for accounting of patch pairs.
// Based on 'edge' which has some hash-like insertion properties
// that are useful.
struct patchPairingType : public Foam::edge
{
label patchEdgei_ = -1;
label meshFacei_ = -1;
void clear()
{
Foam::edge::clear();
patchEdgei_ = -1;
meshFacei_ = -1;
}
};
List<patchPairingType> patchPairings(nBoundaryEdges);
DebugInFunction
<< "Determining required boundary edge connections, "
<< "resolving locally attached boundary edges." << endl;
// Pass 1:
// - setup lookup (edge -> bnd index)
// - add owner patch for each boundary edge
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
{
const label patchEdgei = (bndEdgei + nInternalEdges);
edgeToBoundaryIndex.insert
(
patch().meshEdge(patchEdgei),
bndEdgei
);
// The attached patch face. Should only be one!
const labelList& edgeFaces = patch().edgeFaces()[patchEdgei];
if (edgeFaces.size() != 1)
{
badEdges.insert(patchEdgei);
continue;
}
const label patchFacei = edgeFaces[0];
const label meshFacei = faceLabels_[patchFacei];
const label bndFacei = (meshFacei - mesh().nInternalFaces());
/// const label patchId = pbm.whichPatch(meshFacei);
const label patchId = pbm.patchID()[bndFacei];
// Primary bookkeeping
{
auto& tuple = bndEdgeConnections[bndEdgei].first();
tuple.procNo(Pstream::myProcNo());
tuple.faPatchi(patchId); // Tag as finiteArea patch
tuple.patchEdgei(patchEdgei);
tuple.meshFacei(meshFacei);
}
// Temporary local bookkeeping
{
auto& pairing = patchPairings[bndEdgei];
pairing.clear(); // invalidate
pairing.insert(patchId); // 'hash' into first location
pairing.patchEdgei_ = patchEdgei;
pairing.meshFacei_ = meshFacei;
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) > 0)
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
vtk::lineWriter writer
(
patch().localPoints(),
dumpEdges,
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.nonManifoldEdges")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
FatalErrorInFunction
<< "Boundary edges not singly connected: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
printPatchEdges
(
FatalError,
patch(),
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
badEdges.clear();
// Pass 2:
// Add in first connecting neighbour patch for the boundary edges.
// Need to examine all possibly connecting (non-processor) neighbours,
// but only need to check their boundary edges.
label nMissing = patchPairings.size();
for (label patchi = 0; patchi < nNonProcessor; ++patchi)
{
if (!nMissing) break; // Early exit
const polyPatch& pp = pbm[patchi];
// Check boundary edges
for
(
label patchEdgei = pp.nInternalEdges();
patchEdgei < pp.nEdges();
++patchEdgei
)
{
const label bndEdgei =
edgeToBoundaryIndex.lookup(pp.meshEdge(patchEdgei), -1);
if (bndEdgei != -1)
{
// Has a matching owner boundary edge
auto& pairing = patchPairings[bndEdgei];
// Add neighbour (patchId, patchEdgei, meshFacei)
// 'hash' into the second location
// which does not insert the same value twice
if (pairing.insert(patchi))
{
// The attached patch face. Should only be one!
const labelList& edgeFaces = pp.edgeFaces()[patchEdgei];
if (edgeFaces.size() != 1)
{
pairing.erase(patchi);
badEdges.insert(badEdges.size());
continue;
}
const label patchFacei = edgeFaces[0];
const label meshFacei = patchFacei + pp.start();
// The neighbour information
pairing.patchEdgei_ = patchEdgei;
pairing.meshFacei_ = meshFacei;
--nMissing;
if (!nMissing) break; // Early exit
}
}
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) > 0)
{
FatalErrorInFunction
<< "Had " << nBadEdges
<< " boundary edges with missing or multiple edge connections"
<< abort(FatalError);
}
// Combine local bookkeeping into final list
badEdges.clear();
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
{
const auto& pairing = patchPairings[bndEdgei];
const label nbrPatchi = pairing.second();
const label nbrPatchEdgei = pairing.patchEdgei_;
const label nbrMeshFacei = pairing.meshFacei_;
if (nbrMeshFacei >= 0)
{
// Add into primary bookkeeping
auto& tuple = bndEdgeConnections[bndEdgei].second();
tuple.procNo(Pstream::myProcNo());
tuple.patchi(nbrPatchi);
tuple.patchEdgei(nbrPatchEdgei);
tuple.meshFacei(nbrMeshFacei);
}
else if (!Pstream::parRun())
{
badEdges.insert(nInternalEdges + bndEdgei);
}
}
}
// ~~~~~~
// Serial - can return already
// ~~~~~~
if (!Pstream::parRun())
{
// Verbose report of missing edges - in serial
nBadEdges = badEdges.size();
if (nBadEdges)
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
vtk::lineWriter writer
(
patch().localPoints(),
dumpEdges,
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.invalidEdges")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
FatalErrorInFunction
<< "Boundary edges with missing/invalid neighbours: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
printPatchEdges
(
FatalError,
patch(),
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
// Globally consistent ordering
patchTuple::sort(bndEdgeConnections);
return bndEdgeConnections;
}
// ~~~~~~~~
// Parallel
// ~~~~~~~~
DebugInFunction
<< "Creating global coupling data" << endl;
const globalMeshData& globalData = mesh().globalData();
const indirectPrimitivePatch& cpp = globalData.coupledPatch();
const mapDistribute& map = globalData.globalEdgeSlavesMap();
const label nCoupledEdges = cpp.nEdges();
// Construct coupled edge usage with all data
List<bool> coupledEdgesUsed(map.constructSize(), false);
// Markup finiteArea boundary edges that are coupled across processors
for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
{
coupledEdgesUsed[cppEdgei] =
edgeToBoundaryIndex.found(cpp.meshEdge(cppEdgei));
}
DebugInFunction
<< "Starting sync of boundary edge topology" << endl;
globalMeshData::syncData
(
coupledEdgesUsed,
globalData.globalEdgeSlaves(),
globalData.globalEdgeTransformedSlaves(), // probably not used
map,
orEqOp<bool>()
);
if (debug)
{
label nAttached = 0;
for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
{
if (coupledEdgesUsed[cppEdgei])
{
++nAttached;
}
}
InfoInFunction
<< "Approx "
<< returnReduce(nAttached, sumOp<label>())
<< " connected boundary edges (total, some duplicates)" << endl;
}
// Combine information
// Normally 0 or 2 connections
List<DynamicList<patchTuple, 2>> gatheredConnections(map.constructSize());
// Map edges (mesh numbering) back to a coupled index in use
EdgeMap<label> edgeToCoupledIndex(2*nCoupledEdges);
// Pass 1
// Look for attached boundary edges
// - boundary edges from this side go into gathered connections
// - boundary edges connected from the other side are noted for later
for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
{
if (coupledEdgesUsed[cppEdgei])
{
const edge meshEdge(cpp.meshEdge(cppEdgei));
const label bndEdgei =
edgeToBoundaryIndex.lookup(meshEdge, -1);
if (bndEdgei != -1)
{
// A boundary finiteEdge edge (known from this side)
auto& gathered = gatheredConnections[cppEdgei];
gathered.setCapacity(2);
gathered.resize(1);
auto& tuple = gathered.last();
tuple = bndEdgeConnections[bndEdgei].first();
}
else
{
// Boundary edge connected from the other side
// - mark for it to be added later
edgeToCoupledIndex.insert(meshEdge, cppEdgei);
}
}
}
// Pass 2
// - add previously noted boundary edges (connected from other side)
// into gathered connections
badEdges.clear();
for (label patchi = 0; patchi < nNonProcessor; ++patchi)
{
if (edgeToCoupledIndex.empty()) break; // Early exit
const polyPatch& pp = pbm[patchi];
// Check boundary edges
for
(
label patchEdgei = pp.nInternalEdges();
patchEdgei < pp.nEdges();
++patchEdgei
)
{
const edge meshEdge(pp.meshEdge(patchEdgei));
const label cppEdgei =
edgeToCoupledIndex.lookup(meshEdge, -1);
if (cppEdgei != -1)
{
// A known connection
// The attached patch face. Should only be one!
const labelList& edgeFaces = pp.edgeFaces()[patchEdgei];
if (edgeFaces.size() != 1)
{
badEdges.insert(cppEdgei);
continue;
}
const label patchFacei = edgeFaces[0];
const label meshFacei = patchFacei + pp.start();
auto& gathered = gatheredConnections[cppEdgei];
gathered.setCapacity(2);
gathered.resize(1);
auto& tuple = gathered.last();
tuple.procNo(Pstream::myProcNo());
tuple.patchi(patchi);
tuple.patchEdgei(patchEdgei);
tuple.meshFacei(meshFacei);
// Do not consider again
edgeToCoupledIndex.erase(meshEdge);
if (edgeToCoupledIndex.empty()) break; // Early exit
}
}
}
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) > 0)
{
FatalErrorInFunction
<< "Had " << nBadEdges << " coupled boundary edges"
<< " with missing or multiple edge connections"
<< abort(FatalError);
}
DebugInFunction
<< "Starting sync of boundary edge information" << endl;
globalMeshData::syncData
(
gatheredConnections,
globalData.globalEdgeSlaves(),
globalData.globalEdgeTransformedSlaves(), // probably not used
map,
ListOps::appendEqOp<patchTuple>()
);
DebugInFunction
<< "Collating sync information" << endl;
// Pick out gathered connections and add into primary bookkeeping
for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
{
const auto& gathered = gatheredConnections[cppEdgei];
const label bndEdgei =
edgeToBoundaryIndex.lookup(cpp.meshEdge(cppEdgei), -1);
if
(
// A boundary finiteEdge edge (known from this side)
bndEdgei != -1
// Gathered a one-to-one connection
&& gathered.size() == 2
)
{
const auto& a = gathered[0];
const auto& b = gathered[1];
// Copy second side of connection
auto& connection = bndEdgeConnections[bndEdgei];
connection.second() = (connection.first() == b) ? a : b;
}
}
// Check missing/invalid
badEdges.clear();
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
{
const auto& connection = bndEdgeConnections[bndEdgei];
if (!connection.second().valid())
{
badEdges.insert(nInternalEdges + bndEdgei);
}
}
if (debug & 8)
{
// Boundary edges
{
vtk::lineWriter writer
(
patch().localPoints(),
patch().boundaryEdges(),
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.boundaryEdges")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
// For each boundary edge - the associate neighbour patch
labelList neighProc(nBoundaryEdges);
labelList neighPatch(nBoundaryEdges);
for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
{
const auto& connection = bndEdgeConnections[bndEdgei];
neighProc[bndEdgei] = connection.second().procNo();
neighPatch[bndEdgei] = connection.second().patchi();
}
writer.write("neighProc", neighProc);
writer.write("neighPatch", neighPatch);
}
// finiteArea
{
vtk::uindirectPatchWriter writer
(
patch(),
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.faPatch")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
}
}
// Verbose report of missing edges
if ((nBadEdges = returnReduce(badEdges.size(), sumOp<label>())) > 0)
{
edgeList dumpEdges(patch().edges(), badEdges.sortedToc());
vtk::lineWriter writer
(
patch().localPoints(),
dumpEdges,
fileName
(
mesh().time().globalPath()
/ ("faMesh-construct.invalidEdges")
)
);
writer.writeGeometry();
// CellData
writer.beginCellData();
writer.writeProcIDs();
FatalErrorInFunction
<< "Boundary edges with missing/invalid neighbours: "
<< nBadEdges << '/' << nBoundaryEdges << nl;
printPatchEdges
(
FatalError,
patch(),
badEdges.sortedToc()
);
InfoInFunction
<< "(debug) wrote " << writer.output().name() << nl;
FatalError << abort(FatalError);
}
// Globally consistent ordering
patchTuple::sort(bndEdgeConnections);
DebugInFunction
<< "Return sorted list of boundary connections" << endl;
return bndEdgeConnections;
}
void Foam::faMesh::setBoundaryConnections
(
const List<Pair<patchTuple>>& bndEdgeConnections
) const
{
const label nInternalEdges = patch().nInternalEdges();
const label nBoundaryEdges = patch().nBoundaryEdges();
if (bndEdgeConnections.size() != nBoundaryEdges)
{
FatalErrorInFunction
<< "Sizing mismatch. Expected " << nBoundaryEdges
<< " boundary edge connections, but had "
<< bndEdgeConnections.size() << nl
<< abort(FatalError);
}
bndConnectPtr_.reset
(
new List<labelPair>(nBoundaryEdges, labelPair(-1,-1))
);
auto& bndConnect = *bndConnectPtr_;
for (const auto& connection : bndEdgeConnections)
{
const auto& a = connection.first();
const auto& b = connection.second();
if (a.is_finiteArea() && a.is_localProc())
{
const label bndEdgei = (a.patchEdgei() - nInternalEdges);
bndConnect[bndEdgei].first() = b.procNo();
bndConnect[bndEdgei].second() = b.meshFacei();
}
else if (b.is_finiteArea() && b.is_localProc())
{
const label bndEdgei = (b.patchEdgei() - nInternalEdges);
bndConnect[bndEdgei].first() = a.procNo();
bndConnect[bndEdgei].second() = a.meshFacei();
}
else
{
FatalErrorInFunction
<< "Unexpected pairing input " << connection
<< " ... programming error" << nl
<< abort(FatalError);
}
}
label nInvalid = 0;
for (const auto& connection : bndConnect)
{
if (connection.first() < 0 || connection.second() < 0)
{
++nInvalid;
}
}
if (Pstream::parRun())
{
reduce(nInvalid, sumOp<label>());
}
if (nInvalid)
{
FatalErrorInFunction
<< "Did not properly match " << nInvalid
<< " boundary edges" << nl
<< abort(FatalError);
}
}
void Foam::faMesh::calcBoundaryConnections() const
{
setBoundaryConnections(this->getBoundaryEdgeConnections());
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::labelList Foam::faMesh::boundaryProcs() const
{
const auto& connections = this->boundaryConnections();
labelHashSet procsUsed(2*Pstream::nProcs());
for (const labelPair& tuple : connections)
{
procsUsed.insert(tuple.first());
}
procsUsed.erase(-1); // placeholder value
procsUsed.erase(Pstream::myProcNo());
return procsUsed.sortedToc();
}
Foam::List<Foam::labelPair> Foam::faMesh::boundaryProcSizes() const
{
const auto& connections = this->boundaryConnections();
Map<label> procCount(2*Pstream::nProcs());
for (const labelPair& tuple : connections)
{
++procCount(tuple.first());
}
procCount.erase(-1); // placeholder value
procCount.erase(Pstream::myProcNo());
// Flatten as list
List<labelPair> output(procCount.size());
label count = 0;
for (const label proci : procCount.sortedToc())
{
output[count].first() = proci;
output[count].second() = procCount[proci]; // size
++count;
}
return output;
}
// ************************************************************************* //

View File

@ -116,12 +116,6 @@ Foam::faPatch::~faPatch()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::faPatch::ngbPolyPatchIndex() const noexcept
{
return nbrPolyPatchId_;
}
const Foam::faBoundaryMesh& Foam::faPatch::boundaryMesh() const noexcept
{
return boundaryMesh_;
@ -134,6 +128,76 @@ Foam::label Foam::faPatch::start() const
}
Foam::List<Foam::labelPair> Foam::faPatch::boundaryConnections() const
{
const auto& connections = boundaryMesh().mesh().boundaryConnections();
const label nInternalEdges = boundaryMesh().mesh().nInternalEdges();
List<labelPair> output(this->nEdges());
// Like an IndirectList but removing the nInternalEdges offset
label count = 0;
for (const label patchEdgei : this->edgeLabels())
{
const label bndEdgei = (patchEdgei - nInternalEdges);
output[count] = connections[bndEdgei];
++count;
}
return output;
}
Foam::labelList Foam::faPatch::boundaryProcs() const
{
const auto& connections = boundaryMesh().mesh().boundaryConnections();
const label nInternalEdges = boundaryMesh().mesh().nInternalEdges();
labelHashSet procsUsed(2*Pstream::nProcs());
for (const label patchEdgei : this->edgeLabels())
{
const label bndEdgei = (patchEdgei - nInternalEdges);
const label proci = connections[bndEdgei].first();
procsUsed.insert(proci);
}
procsUsed.erase(-1); // placeholder value
procsUsed.erase(Pstream::myProcNo());
return procsUsed.sortedToc();
}
Foam::List<Foam::labelPair> Foam::faPatch::boundaryProcSizes() const
{
const auto& connections = boundaryMesh().mesh().boundaryConnections();
const label nInternalEdges = boundaryMesh().mesh().nInternalEdges();
Map<label> procCount(2*Pstream::nProcs());
for (const label patchEdgei : this->edgeLabels())
{
const label bndEdgei = (patchEdgei - nInternalEdges);
const label proci = connections[bndEdgei].first();
++procCount(proci);
}
procCount.erase(-1); // placeholder value
procCount.erase(Pstream::myProcNo());
// Flatten as list
List<labelPair> output(procCount.size());
label count = 0;
for (const label proci : procCount.sortedToc())
{
output[count].first() = proci;
output[count].second() = procCount[proci]; // size
++count;
}
return output;
}
const Foam::labelList& Foam::faPatch::pointLabels() const
{
if (!pointLabelsPtr_)

View File

@ -231,6 +231,7 @@ public:
return static_cast<const labelList&>(*this);
}
//- Define new list of edges
void edgeLabels(const UList<label>& newEdgeLabels);
//- Number of patch points
@ -245,8 +246,11 @@ public:
return labelList::size();
}
//- Return neighbour polyPatch index
label ngbPolyPatchIndex() const noexcept;
//- The neighbour polyPatch index
label ngbPolyPatchIndex() const noexcept
{
return nbrPolyPatchId_;
}
//- Return boundaryMesh reference
const faBoundaryMesh& boundaryMesh() const noexcept;
@ -284,6 +288,21 @@ public:
virtual void write(Ostream&) const;
// Topological information
//- List of proc/face for the boundary edge neighbours
//- in locally reordered edge numbering.
List<labelPair> boundaryConnections() const;
//- Boundary edge neighbour processors
//- (does not include own proc)
labelList boundaryProcs() const;
//- List of proc/size for the boundary edge neighbour processors
//- (does not include own proc)
List<labelPair> boundaryProcSizes() const;
// Access functions for geometrical data
//- Return patch point labels

View File

@ -28,6 +28,7 @@ License
#include "faPatchData.H"
#include "dictionary.H"
#include "processorFaPatch.H"
#include "processorPolyPatch.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -108,6 +109,24 @@ void Foam::faPatchData::assign(const faPatch& fap)
}
bool Foam::faPatchData::assign_coupled(int ownProci, int neiProci)
{
clear();
if (ownProci == neiProci)
{
return false;
}
name_ = processorPolyPatch::newName(ownProci, neiProci);
type_ = processorFaPatch::typeName;
ownerProcId_ = ownProci;
neighProcId_ = neiProci;
return true;
}
int Foam::faPatchData::matchPatchPair
(
const labelPair& patchPair

View File

@ -98,6 +98,9 @@ public:
//- Clear and populate with values from finiteArea patch
void assign(const faPatch& fap);
//- Set values consistent with a processor coupling
bool assign_coupled(int ownProci, int neiProci);
//- True if owner/neighbour processor ids are non-equal
bool coupled() const noexcept
{