43 template<
class PatchType>
54 for (
const label patchEdgei : edgeIds)
56 const edge e(
p.meshEdge(patchEdgei));
59 <<
p.points()[
e.first()] <<
' ' 60 <<
p.points()[
e.second()] <<
nl;
63 if (maxOutput > 0 && nOutput >= maxOutput)
65 os <<
" ... suppressing further output" <<
nl;
73 template<
class PatchType>
78 const fileName& outputPath,
82 edgeList dumpEdges(
p.edges(), selectEdges);
105 Foam::faMesh::getBoundaryEdgeConnections()
const 109 const label nNonProcessor =
pbm.nNonProcessor();
129 label patchEdgei_ = -1;
130 label meshFacei_ = -1;
143 <<
"Determining required boundary edge connections, " 144 <<
"resolving locally attached boundary edges." <<
endl;
153 edgeToBoundaryIndex.insert
155 patch().meshEdge(patchEdgei),
162 if (edgeFaces.size() != 1)
168 const label patchFacei = edgeFaces[0];
169 const label meshFacei = faceLabels_[patchFacei];
174 auto& tuple = bndEdgeConnections[bndEdgei].first();
178 tuple.patchEdgei(patchEdgei);
179 tuple.meshFacei(meshFacei);
184 auto& pairing = patchPairings[bndEdgei];
188 pairing.patchEdgei_ = patchEdgei;
189 pairing.meshFacei_ = meshFacei;
196 word
outputName(
"faMesh-construct.nonManifoldEdges");
210 <<
"Boundary edges not singly connected: " 211 <<
returnReduce(selectEdges.size(), sumOp<label>()) <<
'/' 230 label nMissing = patchPairings.size();
232 for (label patchi = 0; patchi < nNonProcessor; ++patchi)
234 if (!nMissing)
break;
236 const polyPatch&
pp =
pbm[patchi];
241 label patchEdgei =
pp.nInternalEdges();
242 patchEdgei <
pp.nEdges();
246 const label bndEdgei =
247 edgeToBoundaryIndex.lookup(
pp.meshEdge(patchEdgei), -1);
253 auto& pairing = patchPairings[bndEdgei];
258 if (pairing.insert(patchi))
261 const labelList& edgeFaces =
pp.edgeFaces()[patchEdgei];
263 if (edgeFaces.size() != 1)
265 pairing.erase(patchi);
270 const label patchFacei = edgeFaces[0];
271 const label meshFacei = patchFacei +
pp.start();
274 pairing.patchEdgei_ = patchEdgei;
275 pairing.meshFacei_ = meshFacei;
278 if (!nMissing)
break;
290 <<
" boundary edges with missing or multiple edge connections" 299 auto& tuple = bndEdgeConnections[bndEdgei].second();
302 const auto& pairing = patchPairings[bndEdgei];
303 const label nbrPatchi = pairing.second();
304 const label nbrPatchEdgei = pairing.patchEdgei_;
305 const label nbrMeshFacei = pairing.meshFacei_;
307 if (nbrMeshFacei >= 0)
313 tuple.patchi(nbrPatchi);
314 tuple.patchEdgei(nbrPatchEdgei);
315 tuple.meshFacei(nbrMeshFacei);
323 tuple.patchEdgei(-1);
345 word
outputName(
"faMesh-construct.invalidEdges");
359 <<
"Boundary edges with missing/invalid neighbours: " 360 <<
returnReduce(selectEdges.size(), sumOp<label>()) <<
'/' 375 return bndEdgeConnections;
384 <<
"Creating global coupling data" <<
endl;
388 const mapDistribute& map =
globalData.globalEdgeSlavesMap();
389 const label nCoupledEdges = cpp.nEdges();
392 List<bool> coupledEdgesUsed(map.constructSize(),
false);
395 for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
397 coupledEdgesUsed[cppEdgei] =
398 edgeToBoundaryIndex.found(cpp.meshEdge(cppEdgei));
402 <<
"Starting sync of boundary edge topology" <<
endl;
416 for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
418 if (coupledEdgesUsed[cppEdgei])
427 <<
" connected boundary edges (total, some duplicates)" <<
endl;
433 List<DynamicList<patchTuple, 2>> gatheredConnections(map.constructSize());
436 EdgeMap<label> edgeToCoupledIndex(2*nCoupledEdges);
443 for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
445 if (coupledEdgesUsed[cppEdgei])
447 const edge meshEdge(cpp.meshEdge(cppEdgei));
449 const label bndEdgei =
450 edgeToBoundaryIndex.lookup(meshEdge, -1);
456 auto& gathered = gatheredConnections[cppEdgei];
457 gathered.setCapacity_nocopy(2);
458 gathered.resize_nocopy(1);
459 auto& tuple = gathered.front();
461 tuple = bndEdgeConnections[bndEdgei].first();
468 edgeToCoupledIndex.insert(meshEdge, cppEdgei);
478 for (label patchi = 0; patchi < nNonProcessor; ++patchi)
480 if (edgeToCoupledIndex.empty())
break;
482 const polyPatch&
pp =
pbm[patchi];
487 label patchEdgei =
pp.nInternalEdges();
488 patchEdgei <
pp.nEdges();
492 const edge meshEdge(
pp.meshEdge(patchEdgei));
494 const label cppEdgei =
495 edgeToCoupledIndex.lookup(meshEdge, -1);
502 const labelList& edgeFaces =
pp.edgeFaces()[patchEdgei];
504 if (edgeFaces.size() != 1)
510 const label patchFacei = edgeFaces[0];
511 const label meshFacei = patchFacei +
pp.start();
513 auto& gathered = gatheredConnections[cppEdgei];
514 gathered.setCapacity_nocopy(2);
515 gathered.resize_nocopy(1);
516 auto& tuple = gathered.front();
519 tuple.patchi(patchi);
520 tuple.patchEdgei(patchEdgei);
521 tuple.meshFacei(meshFacei);
524 edgeToCoupledIndex.erase(meshEdge);
526 if (edgeToCoupledIndex.empty())
break;
535 <<
" coupled boundary edges" 536 <<
" with missing or multiple edge connections" 541 <<
"Starting sync of boundary edge information" <<
endl;
549 ListOps::appendEqOp<patchTuple>()
554 <<
"Collating sync information" <<
endl;
558 danglingEdges.clear();
559 for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
561 auto& gathered = gatheredConnections[cppEdgei];
563 const label bndEdgei =
564 edgeToBoundaryIndex.lookup(cpp.meshEdge(cppEdgei), -1);
569 auto& connection = bndEdgeConnections[bndEdgei];
571 if (gathered.size() == 1)
574 danglingEdges.insert(cppEdgei);
576 else if (gathered.size() == 2)
579 const auto& a = gathered[0];
580 const auto&
b = gathered[1];
582 connection.second() = (connection.first() ==
b) ? a :
b;
584 else if (gathered.size() > 2)
603 label otherIndex = -1;
607 if (gathered[sloti].procNo() == myProci)
624 && otherIndex < gathered.size()
628 const auto& a = gathered[myIndex];
629 const auto&
b = gathered[otherIndex];
631 connection.second() = (connection.first() ==
b) ? a :
b;
645 <<
nl <<
"Multiply connected edges detected" <<
endl;
649 constexpr label maxOutput = 10;
653 for (
const label cppEdgei :
badEdges.sortedToc())
655 const edge
e(cpp.meshEdge(cppEdgei));
657 const auto& gathered = gatheredConnections[cppEdgei];
659 Info<<
"connection: ";
660 gathered.writeList(
Info) <<
nl;
663 << cpp.points()[
e.first()] <<
' ' 664 << cpp.points()[
e.second()] <<
nl;
667 if (maxOutput > 0 && nOutput >= maxOutput)
669 Info<<
" ... suppressing further output" <<
nl;
678 <<
nl <<
"Dangling edges detected" <<
endl;
682 constexpr label maxOutput = 10;
686 for (
const label cppEdgei : danglingEdges.sortedToc())
688 const edge
e(cpp.meshEdge(cppEdgei));
690 const auto& gathered = gatheredConnections[cppEdgei];
692 Info<<
"connection: ";
693 gathered.writeList(
Info) <<
nl;
696 << cpp.points()[
e.first()] <<
' ' 697 << cpp.points()[
e.second()] <<
nl;
700 if (maxOutput > 0 && nOutput >= maxOutput)
702 Info<<
" ... suppressing further output" <<
nl;
707 labelList selectEdges(danglingEdges.sortedToc());
708 word
outputName(
"faMesh-construct.danglingEdges");
727 const auto& connection = bndEdgeConnections[bndEdgei];
729 if (!connection.second().valid())
742 patch().localPoints(),
743 patch().boundaryEdges(),
747 / (
"faMesh-construct.boundaryEdges")
762 const auto& connection = bndEdgeConnections[bndEdgei];
764 neighProc[bndEdgei] = connection.second().procNo();
765 neighPatch[bndEdgei] = connection.second().patchi();
768 writer.write(
"neighProc", neighProc);
769 writer.write(
"neighPatch", neighPatch);
780 / (
"faMesh-construct.faPatch")
796 word
outputName(
"faMesh-construct.invalidEdges");
810 <<
"Boundary edges with missing/invalid neighbours: " 811 <<
returnReduce(selectEdges.size(), sumOp<label>()) <<
'/' 829 <<
"Return sorted list of boundary connections" <<
endl;
831 return bndEdgeConnections;
835 void Foam::faMesh::setBoundaryConnections
837 const List<Pair<patchTuple>>& bndEdgeConnections
840 const label nInternalEdges =
patch().nInternalEdges();
841 const label nBoundaryEdges =
patch().nBoundaryEdges();
843 if (bndEdgeConnections.size() != nBoundaryEdges)
846 <<
"Sizing mismatch. Expected " << nBoundaryEdges
847 <<
" boundary edge connections, but had " 848 << bndEdgeConnections.size() <<
nl 852 bndConnectPtr_ = std::make_unique<List<labelPair>>
857 auto& bndConnect = *bndConnectPtr_;
859 for (
const auto& connection : bndEdgeConnections)
861 const auto& a = connection.first();
862 const auto&
b = connection.second();
864 if (a.is_finiteArea() && a.is_localProc())
866 const label bndEdgei = (a.patchEdgei() - nInternalEdges);
868 bndConnect[bndEdgei].first() =
b.procNo();
869 bndConnect[bndEdgei].second() =
b.meshFacei();
871 else if (
b.is_finiteArea() &&
b.is_localProc())
873 const label bndEdgei = (
b.patchEdgei() - nInternalEdges);
875 bndConnect[bndEdgei].first() = a.procNo();
876 bndConnect[bndEdgei].second() = a.meshFacei();
881 <<
"Unexpected pairing input " << connection
882 <<
" ... programming error" <<
nl 888 for (
const auto& connection : bndConnect)
890 if (connection.first() < 0 || connection.second() < 0)
900 forAll(bndConnect, bndEdgei)
904 bndConnect[bndEdgei].first() < 0
905 || bndConnect[bndEdgei].second() < 0
913 word
outputName(
"faMesh-construct.invalidMatches");
919 thisDb().time().globalPath(),
927 <<
"Did not properly match " 929 <<
" boundary edges" <<
nl;
936 void Foam::faMesh::calcBoundaryConnections()
const 938 setBoundaryConnections(this->getBoundaryEdgeConnections());
946 const auto& connections = this->boundaryConnections();
950 for (
const labelPair& tuple : connections)
952 procsUsed.insert(tuple.first());
958 return procsUsed.sortedToc();
964 const auto& connections = this->boundaryConnections();
968 for (
const labelPair& tuple : connections)
970 ++procCount(tuple.first());
978 for (
const label proci : procCount.
sortedToc())
993 haloMapPtr_ = std::make_unique<faMeshBoundaryHalo>(*this);
1000 bool Foam::faMesh::hasHaloFaceGeometry() const
noexcept 1003 return (haloFaceCentresPtr_ && haloFaceNormalsPtr_);
1007 void Foam::faMesh::calcHaloFaceGeometry()
const 1009 if (haloFaceCentresPtr_ || haloFaceNormalsPtr_)
1012 <<
"Halo centres/normals already calculated" 1017 <<
"Calculating halo face centres/normals" <<
endl;
1022 const faMeshBoundaryHalo& halo = boundaryHaloMap();
1024 const labelList& inputFaceIds = halo.inputMeshFaces();
1026 haloFaceCentresPtr_ = std::make_unique<pointField>();
1027 haloFaceNormalsPtr_ = std::make_unique<vectorField>();
1029 auto& centres = *haloFaceCentresPtr_;
1030 auto& normals = *haloFaceNormalsPtr_;
1032 centres.
resize(inputFaceIds.size());
1033 normals.resize(inputFaceIds.size());
1038 const face&
f = faces[inputFaceIds[i]];
1040 centres[i] =
f.centre(
points);
1041 normals[i] =
f.unitNormal(
points);
1045 halo.distributeSparse(centres);
1046 halo.distributeSparse(normals);
1053 if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1055 calcHaloFaceGeometry();
1058 return *haloFaceCentresPtr_;
1065 if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1067 calcHaloFaceGeometry();
1070 return *haloFaceNormalsPtr_;
1077 if (patchi < 0 || patchi >=
boundary().size())
1080 <<
"Patch " << patchi <<
" is out-of-range 0.." 1091 this->haloFaceCentres(),
1102 if (patchi < 0 || patchi >=
boundary().size())
1105 <<
"Patch " << patchi <<
" is out-of-range 0.." 1116 this->haloFaceNormals(),
const polyBoundaryMesh & pbm
void size(const label n)
Older name for setAddressableSize.
errorManipArg< error, int > exit(error &err, const int errNo=1)
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/"finiteArea-edgesCentres"))
void resize(const label len)
Adjust allocated size of list.
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
List< edge > edgeList
List of edge.
const Time & time() const
Return reference to time.
constexpr char nl
The newline '\n' character (0x0a)
const pointField & haloFaceCentres() const
Face centres of boundary halo neighbours.
Ostream & endl(Ostream &os)
Add newline and flush stream.
static bool & parRun() noexcept
Test if this a parallel run.
bool writeProcIDs()
Write processor ids for each line as CellData or for each point as PointData, depending on isPointDat...
label nInternalEdges() const
Number of internal edges.
const faGlobalMeshData & globalData() const
Return parallel info (demand-driven)
static void printPatchEdges(Ostream &os, const PatchType &p, const labelList &edgeIds, label maxOutput=10)
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field...
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
labelList boundaryProcs() const
Boundary edge neighbour processors (does not include own proc)
labelHashSet badEdges(pp.nEdges()/20)
virtual const objectRegistry & thisDb() const
Reference to the mesh database.
virtual const pointField & points() const
Return raw points.
#define forAll(list, i)
Loop across all elements in list.
static void syncData(List< Type > &elems, const labelListList &slaves, const labelListList &transformedSlaves, const mapDistribute &slavesMap, const globalIndexAndTransform &, const CombineOp &cop, const TransformOp &top)
Helper: synchronise data with transforms.
const uindirectPrimitivePatch & patch() const
Return constant reference to primitive patch.
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
List< face > faceList
List of faces.
label size() const noexcept
The number of elements in table.
void clear()
'Clears' edge by setting both ends to invalid vertex labels.
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of 'true' entries.
List< labelPair > boundaryProcSizes() const
List of proc/size for the boundary edge neighbour processors (does not include own proc) ...
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
word outputName("finiteArea-edges.obj")
vectorField pointField
pointField is a vectorField.
const dimensionedScalar e
Elementary charge.
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
#define DebugInFunction
Report an information message using Foam::Info.
void sort(UList< T > &list)
Sort the list.
const faMeshBoundaryHalo & boundaryHaloMap() const
Mapping/swapping for boundary halo neighbours.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
const vectorField & haloFaceNormals() const
Face unit-normals of boundary halo neighbours.
const labelListList & edgeFaces() const
Return edge-face addressing.
const globalMeshData & globalData() const
Return parallel info (demand-driven)
GenericPatchWriter< uindirectPrimitivePatch > uindirectPatchWriter
Write uindirectPrimitivePatch faces/points (optionally with fields) as a vtp file or a legacy vtk fil...
virtual const faceList & faces() const
Return raw faces.
label nBoundaryEdges() const
Number of boundary edges == (nEdges() - nInternalEdges())
errorManip< error > abort(error &err)
static void vtkWritePatchEdges(const PatchType &p, const labelList &selectEdges, const fileName &outputPath, const word &outputName)
virtual bool beginCellData(label nFields=0)
Begin CellData output section for specified number of fields.
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Pair< label > labelPair
A pair of labels.
OBJstream os(runTime.globalPath()/outputName)
const polyMesh & mesh() const
Return access to polyMesh.
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
label nInternalEdges() const noexcept
Number of internal faces.
#define WarningInFunction
Report a warning using Foam::Warning.
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
void close()
End the file contents and close the file after writing.
const std::string patch
OpenFOAM patch number as a std::string.
void clear()
Clear all entries from the registry.
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual bool writeGeometry()
Write patch topology.
static Ostream & output(Ostream &os, const IntRange< T > &range)
fileName globalPath() const
The complete global path for the object (with instance, local,...)
List< label > labelList
A List of labels.
A class for managing temporary objects.
label nBoundaryEdges() const noexcept
Number of boundary edges (== nEdges - nInternalEdges)
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
Class for obtaining halo face data for the boundary edges. The ordering follows that natural edge ord...
static int debug
Debug switch.
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
#define InfoInFunction
Report an information message using Foam::Info.