mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
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:
committed by
Andrew Heather
parent
9c1f94d4fd
commit
8e45108905
@ -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
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
803
src/finiteArea/faMesh/faMeshTopology.C
Normal file
803
src/finiteArea/faMesh/faMeshTopology.C
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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_)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user