43template<
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;
73template<
class PatchType>
82 edgeList dumpEdges(
p.edges(), selectEdges);
105Foam::faMesh::getBoundaryEdgeConnections()
const
127 struct patchPairingType :
public Foam::edge
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>()) <<
'/'
374 patchTuple::sort(bndEdgeConnections);
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>()) <<
'/'
826 patchTuple::sort(bndEdgeConnections);
829 <<
"Return sorted list of boundary connections" <<
endl;
831 return bndEdgeConnections;
835void Foam::faMesh::setBoundaryConnections
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
919 thisDb().time().globalPath(),
927 <<
"Did not properly match "
929 <<
" boundary edges" <<
nl;
936void 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());
976 List<labelPair> output(procCount.
size());
978 for (
const label proci : procCount.
sortedToc())
980 output[count].first() = proci;
981 output[count].second() = procCount[proci];
993 haloMapPtr_ = std::make_unique<faMeshBoundaryHalo>(*
this);
1000bool Foam::faMesh::hasHaloFaceGeometry() const
noexcept
1003 return (haloFaceCentresPtr_ && haloFaceNormalsPtr_);
1007void Foam::faMesh::calcHaloFaceGeometry()
const
1009 if (haloFaceCentresPtr_ || haloFaceNormalsPtr_)
1012 <<
"Halo centres/normals already calculated"
1017 <<
"Calculating halo face centres/normals" <<
endl;
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(),
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/(vtkBaseFileName+"-edgesCentres")))
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
const polyBoundaryMesh & pbm
labelHashSet badEdges(pp.nEdges()/20)
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
label size() const noexcept
The number of elements in table.
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
fileName globalPath() const
The complete global path for the object (with instance, local,...).
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
A HashTable to objects of type <T> with a label key.
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
An ordered pair of two objects of type <T> with first() and second() elements.
label nBoundaryEdges() const
Number of boundary edges == (nEdges() - nInternalEdges()).
label nInternalEdges() const
Number of internal edges.
const labelListList & edgeFaces() const
Return edge-face addressing.
T & first()
Access first element of the list, position [0].
void size(const label n)
Older name for setAddressableSize.
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run.
static bool & parRun() noexcept
Test if this a parallel run.
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
void clear()
'Clears' edge by setting both ends to invalid vertex labels.
Class for obtaining halo face data for the boundary edges. The ordering follows that natural edge ord...
virtual const objectRegistry & thisDb() const
Reference to the mesh database.
const vectorField & haloFaceNormals() const
Face unit-normals of boundary halo neighbours.
const Time & time() const
Return reference to time.
const pointField & haloFaceCentres() const
Face centres of boundary halo neighbours.
label nBoundaryEdges() const noexcept
Number of boundary edges (== nEdges - nInternalEdges).
const polyMesh & mesh() const
Return access to polyMesh.
const uindirectPrimitivePatch & patch() const
Return constant reference to primitive patch.
label nInternalEdges() const noexcept
Number of internal faces.
const faGlobalMeshData & globalData() const
Return parallel info (demand-driven).
const List< labelPair > & boundaryConnections() const
List of proc/face for the boundary edge neighbours using primitive patch edge numbering.
labelList boundaryProcs() const
Boundary edge neighbour processors (does not include own proc).
List< labelPair > boundaryProcSizes() const
List of proc/size for the boundary edge neighbour processors (does not include own proc).
const faMeshBoundaryHalo & boundaryHaloMap() const
Mapping/swapping for boundary halo neighbours.
A face is a list of labels corresponding to mesh vertices.
A class for handling file names.
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.
label count(const char *clsName) const
The number of objects of the given class name.
label nNonProcessor() const
The number of patches before the first processor patch.
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
virtual const faceList & faces() const
Return raw faces.
const globalMeshData & globalData() const
Return parallel info (demand-driven).
virtual const pointField & points() const
Return raw points.
static int debug
Debug switch.
A class for managing temporary objects.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
void close()
End the file contents and close the file after writing.
Write edge/points (optionally with fields) as a vtp file or a legacy vtk file.
bool writeProcIDs()
Write processor ids for each line as CellData or for each point as PointData, depending on isPointDat...
virtual bool writeGeometry()
Write patch topology.
virtual bool beginCellData(label nFields=0)
Begin CellData output section for specified number of fields.
A class for handling words, derived from Foam::string.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
word outputName("finiteArea-edges.obj")
OBJstream os(runTime.globalPath()/outputName)
#define WarningInFunction
Report a warning using Foam::Warning.
#define DebugInFunction
Report an information message using Foam::Info.
#define InfoInFunction
Report an information message using Foam::Info.
const std::string patch
OpenFOAM patch number as a std::string.
GenericPatchWriter< uindirectPrimitivePatch > uindirectPatchWriter
Write uindirectPrimitivePatch faces/points (optionally with fields) as a vtp file or a legacy vtk fil...
List< edge > edgeList
List of edge.
Pair< label > labelPair
A pair of labels.
bool returnReduceOr(const bool value, const int communicator=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
List< label > labelList
A List of labels.
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
static void vtkWritePatchEdges(const PatchType &p, const labelList &selectEdges, const fileName &outputPath, const word &outputName)
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< face > faceList
List of faces.
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
Ostream & endl(Ostream &os)
Add newline and flush stream.
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field.
static void printPatchEdges(Ostream &os, const PatchType &p, const labelList &edgeIds, label maxOutput=10)
void sort(UList< T > &list)
Sort the list.
errorManip< error > abort(error &err)
Field< vector > vectorField
Specialisation of Field<T> for vector.
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
vectorField pointField
pointField is a vectorField.
errorManipArg< error, int > exit(error &err, const int errNo=1)
constexpr char nl
The newline '\n' character (0x0a).
#define forAll(list, i)
Loop across all elements in list.