faMeshTopology.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2021-2023 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "faMesh.H"
29 #include "faMeshBoundaryHalo.H"
30 #include "globalMeshData.H"
31 #include "indirectPrimitivePatch.H"
32 #include "edgeHashes.H"
33 #include "syncTools.H"
34 #include "foamVtkLineWriter.H"
35 #include "foamVtkIndPatchWriter.H"
36 
37 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
38 
39 namespace Foam
40 {
41 
42 // Print out edges as point pairs
43 template<class PatchType>
44 static void printPatchEdges
45 (
46  Ostream& os,
47  const PatchType& p,
48  const labelList& edgeIds,
49  label maxOutput = 10
50 )
51 {
52  label nOutput = 0;
53 
54  for (const label patchEdgei : edgeIds)
55  {
56  const edge e(p.meshEdge(patchEdgei));
57 
58  os << " "
59  << p.points()[e.first()] << ' '
60  << p.points()[e.second()] << nl;
61 
62  ++nOutput;
63  if (maxOutput > 0 && nOutput >= maxOutput)
64  {
65  os << " ... suppressing further output" << nl;
66  break;
67  }
68  }
69 }
70 
71 
72 // Write edges in VTK format
73 template<class PatchType>
74 static void vtkWritePatchEdges
75 (
76  const PatchType& p,
77  const labelList& selectEdges,
78  const fileName& outputPath,
79  const word& outputName
80 )
81 {
82  edgeList dumpEdges(p.edges(), selectEdges);
83 
84  vtk::lineWriter writer
85  (
86  p.localPoints(),
87  dumpEdges,
88  outputPath/outputName
89  );
90 
92 
93  // CellData
96  writer.close();
97 }
98 
99 } // End namespace Foam
100 
101 
102 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
103 
105 Foam::faMesh::getBoundaryEdgeConnections() const
106 {
107  const polyBoundaryMesh& pbm = mesh().boundaryMesh();
108 
109  const label nNonProcessor = pbm.nNonProcessor();
110 
111  const label nInternalEdges = patch().nInternalEdges();
112  const label nBoundaryEdges = patch().nBoundaryEdges();
113 
114  // The output result:
115  List<Pair<patchTuple>> bndEdgeConnections(nBoundaryEdges);
116 
117  // Map edges (mesh numbering) back to a boundary index
118  EdgeMap<label> edgeToBoundaryIndex(2*nBoundaryEdges);
119 
121  labelHashSet danglingEdges(2*nBoundaryEdges);
122 
123  {
124  // Local collection structure for accounting of patch pairs.
125  // Based on 'edge' which has some hash-like insertion properties
126  // that are useful.
127  struct patchPairingType : public Foam::edge
128  {
129  label patchEdgei_ = -1;
130  label meshFacei_ = -1;
131 
132  void clear()
133  {
134  Foam::edge::clear(); // ie, (-1, -1)
135  patchEdgei_ = -1;
136  meshFacei_ = -1;
137  }
138  };
139 
140  List<patchPairingType> patchPairings(nBoundaryEdges);
141 
143  << "Determining required boundary edge connections, "
144  << "resolving locally attached boundary edges." << endl;
145 
146  // Pass 1:
147  // - setup lookup (edge -> bnd index)
148  // - add owner patch for each boundary edge
149  for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
150  {
151  const label patchEdgei = (bndEdgei + nInternalEdges);
152 
153  edgeToBoundaryIndex.insert
154  (
155  patch().meshEdge(patchEdgei),
156  bndEdgei
157  );
158 
159  // The attached patch face. Should only be one!
160  const labelList& edgeFaces = patch().edgeFaces()[patchEdgei];
161 
162  if (edgeFaces.size() != 1)
163  {
164  badEdges.insert(patchEdgei);
165  continue;
166  }
167 
168  const label patchFacei = edgeFaces[0];
169  const label meshFacei = faceLabels_[patchFacei];
170  const label patchId = pbm.patchID(meshFacei);
171 
172  // Primary bookkeeping
173  {
174  auto& tuple = bndEdgeConnections[bndEdgei].first();
175 
176  tuple.procNo(UPstream::myProcNo());
177  tuple.faPatchi(patchId); // Tag as finiteArea patch
178  tuple.patchEdgei(patchEdgei);
179  tuple.meshFacei(meshFacei);
180  }
181 
182  // Temporary local bookkeeping
183  {
184  auto& pairing = patchPairings[bndEdgei];
185 
186  pairing.clear(); // invalidate
187  pairing.insert(patchId); // 'hash' into first location
188  pairing.patchEdgei_ = patchEdgei;
189  pairing.meshFacei_ = meshFacei;
190  }
191  }
192 
193  if (returnReduceOr(badEdges.size()))
194  {
195  labelList selectEdges(badEdges.sortedToc());
196  word outputName("faMesh-construct.nonManifoldEdges");
197 
199  (
200  patch(),
201  selectEdges,
202  thisDb().time().globalPath(),
203  outputName
204  );
205 
207  << "(debug) wrote " << outputName << nl;
208 
210  << "Boundary edges not singly connected: "
211  << returnReduce(selectEdges.size(), sumOp<label>()) << '/'
212  << nBoundaryEdges << nl;
213 
215  (
216  FatalError,
217  patch(),
218  selectEdges
219  );
220 
222  }
223  badEdges.clear();
224 
225  // Pass 2:
226  // Add in first connecting neighbour patch for the boundary edges.
227  // Need to examine all possibly connecting (non-processor) neighbours,
228  // but only need to check their boundary edges.
229 
230  label nMissing = patchPairings.size();
231 
232  for (label patchi = 0; patchi < nNonProcessor; ++patchi)
233  {
234  if (!nMissing) break; // Early exit
235 
236  const polyPatch& pp = pbm[patchi];
237 
238  // Check boundary edges
239  for
240  (
241  label patchEdgei = pp.nInternalEdges();
242  patchEdgei < pp.nEdges();
243  ++patchEdgei
244  )
245  {
246  const label bndEdgei =
247  edgeToBoundaryIndex.lookup(pp.meshEdge(patchEdgei), -1);
248 
249  if (bndEdgei != -1)
250  {
251  // Has a matching owner boundary edge
252 
253  auto& pairing = patchPairings[bndEdgei];
254 
255  // Add neighbour (patchId, patchEdgei, meshFacei)
256  // 'hash' into the second location
257  // which does not insert the same value twice
258  if (pairing.insert(patchi))
259  {
260  // The attached patch face. Should only be one!
261  const labelList& edgeFaces = pp.edgeFaces()[patchEdgei];
262 
263  if (edgeFaces.size() != 1)
264  {
265  pairing.erase(patchi);
266  badEdges.insert(badEdges.size());
267  continue;
268  }
269 
270  const label patchFacei = edgeFaces[0];
271  const label meshFacei = patchFacei + pp.start();
272 
273  // The neighbour information
274  pairing.patchEdgei_ = patchEdgei;
275  pairing.meshFacei_ = meshFacei;
276 
277  --nMissing;
278  if (!nMissing) break; // Early exit
279  }
280  }
281  }
282  }
283 
284  if (returnReduceOr(badEdges.size()))
285  {
287  << "Had "
288  << returnReduce(badEdges.size(), sumOp<label>()) << '/'
289  << nBoundaryEdges
290  << " boundary edges with missing or multiple edge connections"
291  << abort(FatalError);
292  }
293 
294  // Combine local bookkeeping into final list
295  badEdges.clear();
296  for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
297  {
298  // Primary bookkeeping
299  auto& tuple = bndEdgeConnections[bndEdgei].second();
300 
301  // Local bookkeeping
302  const auto& pairing = patchPairings[bndEdgei];
303  const label nbrPatchi = pairing.second();
304  const label nbrPatchEdgei = pairing.patchEdgei_;
305  const label nbrMeshFacei = pairing.meshFacei_;
306 
307  if (nbrMeshFacei >= 0) // Additional safety
308  {
309  if (nbrPatchi >= 0)
310  {
311  // Local connection
312  tuple.procNo(UPstream::myProcNo());
313  tuple.patchi(nbrPatchi);
314  tuple.patchEdgei(nbrPatchEdgei);
315  tuple.meshFacei(nbrMeshFacei);
316  }
317  else
318  {
319  // No local connection.
320  // Is likely to be a processor connection
321  tuple.procNo(UPstream::myProcNo());
322  tuple.patchi(-1);
323  tuple.patchEdgei(-1);
324  tuple.meshFacei(-1);
325  }
326  }
327  else if (!UPstream::parRun())
328  {
329  badEdges.insert(nInternalEdges + bndEdgei);
330  }
331  }
332  }
333 
334 
335  // ~~~~~~
336  // Serial - can return already
337  // ~~~~~~
338  if (!UPstream::parRun())
339  {
340  // Verbose report of missing edges - in serial
341 
342  if (returnReduceOr(badEdges.size()))
343  {
344  labelList selectEdges(badEdges.sortedToc());
345  word outputName("faMesh-construct.invalidEdges");
346 
348  (
349  patch(),
350  selectEdges,
351  thisDb().time().globalPath(),
352  outputName
353  );
354 
356  << "(debug) wrote " << outputName << nl;
357 
359  << "Boundary edges with missing/invalid neighbours: "
360  << returnReduce(selectEdges.size(), sumOp<label>()) << '/'
361  << nBoundaryEdges << nl;
362 
364  (
365  FatalError,
366  patch(),
367  selectEdges
368  );
369 
371  }
372 
373  // Globally consistent ordering
374  patchTuple::sort(bndEdgeConnections);
375  return bndEdgeConnections;
376  }
377 
378 
379  // ~~~~~~~~
380  // Parallel
381  // ~~~~~~~~
382 
384  << "Creating global coupling data" << endl;
385 
386  const globalMeshData& globalData = mesh().globalData();
387  const indirectPrimitivePatch& cpp = globalData.coupledPatch();
388  const mapDistribute& map = globalData.globalEdgeSlavesMap();
389  const label nCoupledEdges = cpp.nEdges();
390 
391  // Construct coupled edge usage with all data
392  List<bool> coupledEdgesUsed(map.constructSize(), false);
393 
394  // Markup finiteArea boundary edges that are coupled across processors
395  for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
396  {
397  coupledEdgesUsed[cppEdgei] =
398  edgeToBoundaryIndex.found(cpp.meshEdge(cppEdgei));
399  }
400 
402  << "Starting sync of boundary edge topology" << endl;
403 
405  (
406  coupledEdgesUsed,
407  globalData.globalEdgeSlaves(),
408  globalData.globalEdgeTransformedSlaves(), // probably not used
409  map,
410  orEqOp<bool>()
411  );
412 
413  if (debug)
414  {
415  label nAttached = 0;
416  for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
417  {
418  if (coupledEdgesUsed[cppEdgei])
419  {
420  ++nAttached;
421  }
422  }
423 
425  << "Approx "
426  << returnReduce(nAttached, sumOp<label>())
427  << " connected boundary edges (total, some duplicates)" << endl;
428  }
429 
430  // Combine information
431 
432  // Normally 0 or 2 connections
433  List<DynamicList<patchTuple, 2>> gatheredConnections(map.constructSize());
434 
435  // Map edges (mesh numbering) back to a coupled index in use
436  EdgeMap<label> edgeToCoupledIndex(2*nCoupledEdges);
437 
438  // Pass 1
439  // Look for attached boundary edges
440  // - boundary edges from this side go into gathered connections
441  // - boundary edges connected from the other side are noted for later
442 
443  for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
444  {
445  if (coupledEdgesUsed[cppEdgei])
446  {
447  const edge meshEdge(cpp.meshEdge(cppEdgei));
448 
449  const label bndEdgei =
450  edgeToBoundaryIndex.lookup(meshEdge, -1);
451 
452  if (bndEdgei != -1)
453  {
454  // A boundary finiteEdge edge (known from this side)
455 
456  auto& gathered = gatheredConnections[cppEdgei];
457  gathered.setCapacity_nocopy(2);
458  gathered.resize_nocopy(1);
459  auto& tuple = gathered.front();
460 
461  tuple = bndEdgeConnections[bndEdgei].first();
462  }
463  else
464  {
465  // Boundary edge connected from the other side
466  // - mark for it to be added later
467 
468  edgeToCoupledIndex.insert(meshEdge, cppEdgei);
469  }
470  }
471  }
472 
473  // Pass 2
474  // - add previously noted boundary edges (connected from other side)
475  // into gathered connections
476 
477  badEdges.clear();
478  for (label patchi = 0; patchi < nNonProcessor; ++patchi)
479  {
480  if (edgeToCoupledIndex.empty()) break; // Early exit
481 
482  const polyPatch& pp = pbm[patchi];
483 
484  // Check boundary edges
485  for
486  (
487  label patchEdgei = pp.nInternalEdges();
488  patchEdgei < pp.nEdges();
489  ++patchEdgei
490  )
491  {
492  const edge meshEdge(pp.meshEdge(patchEdgei));
493 
494  const label cppEdgei =
495  edgeToCoupledIndex.lookup(meshEdge, -1);
496 
497  if (cppEdgei != -1)
498  {
499  // A known connection
500 
501  // The attached patch face. Should only be one!
502  const labelList& edgeFaces = pp.edgeFaces()[patchEdgei];
503 
504  if (edgeFaces.size() != 1)
505  {
506  badEdges.insert(cppEdgei);
507  continue;
508  }
509 
510  const label patchFacei = edgeFaces[0];
511  const label meshFacei = patchFacei + pp.start();
512 
513  auto& gathered = gatheredConnections[cppEdgei];
514  gathered.setCapacity_nocopy(2);
515  gathered.resize_nocopy(1);
516  auto& tuple = gathered.front();
517 
518  tuple.procNo(UPstream::myProcNo());
519  tuple.patchi(patchi);
520  tuple.patchEdgei(patchEdgei);
521  tuple.meshFacei(meshFacei);
522 
523  // Do not consider again
524  edgeToCoupledIndex.erase(meshEdge);
525 
526  if (edgeToCoupledIndex.empty()) break; // Early exit
527  }
528  }
529  }
530 
531  if (returnReduceOr(badEdges.size()))
532  {
534  << "Had " << returnReduce(badEdges.size(), sumOp<label>())
535  << " coupled boundary edges"
536  << " with missing or multiple edge connections"
537  << abort(FatalError);
538  }
539 
541  << "Starting sync of boundary edge information" << endl;
542 
544  (
545  gatheredConnections,
546  globalData.globalEdgeSlaves(),
547  globalData.globalEdgeTransformedSlaves(), // probably not used
548  map,
549  ListOps::appendEqOp<patchTuple>()
550  );
551 
552 
554  << "Collating sync information" << endl;
555 
556  // Pick out gathered connections and add into primary bookkeeping
557  badEdges.clear();
558  danglingEdges.clear();
559  for (label cppEdgei = 0; cppEdgei < nCoupledEdges; ++cppEdgei)
560  {
561  auto& gathered = gatheredConnections[cppEdgei];
562 
563  const label bndEdgei =
564  edgeToBoundaryIndex.lookup(cpp.meshEdge(cppEdgei), -1);
565 
566  if (bndEdgei != -1)
567  {
568  // A boundary finiteEdge edge (known from this side)
569  auto& connection = bndEdgeConnections[bndEdgei];
570 
571  if (gathered.size() == 1)
572  {
573  // Dangling edge!!
574  danglingEdges.insert(cppEdgei);
575  }
576  else if (gathered.size() == 2)
577  {
578  // Copy second side of connection
579  const auto& a = gathered[0];
580  const auto& b = gathered[1];
581 
582  connection.second() = (connection.first() == b) ? a : b;
583  }
584  else if (gathered.size() > 2)
585  {
586  // Multiply connected!!
587  // ++nUnresolved;
588 
589  // Extra safety (but should already be consistently ordered)
590  Foam::sort(gathered);
591 
592  // These connections can arise at the centre of a
593  // "star" connection, or because the patch faces are
594  // actually baffles.
595 
596  // We don't necessary have enough information to know how
597  // things should be connected, so connect pair-wise
598  // as the first remedial solution
599 
600  const label myProci = UPstream::myProcNo();
601 
602  label myIndex = -1;
603  label otherIndex = -1;
604 
605  forAll(gathered, sloti)
606  {
607  if (gathered[sloti].procNo() == myProci)
608  {
609  myIndex = sloti;
610  otherIndex =
611  (
612  (sloti % 2)
613  ? (sloti - 1) // ie, connect (1 -> 0)
614  : (sloti + 1) // ie, connect (0 -> 1)
615  );
616  break;
617  }
618  }
619 
620  if
621  (
622  myIndex >= 0
623  && otherIndex >= 0
624  && otherIndex < gathered.size()
625  )
626  {
627  // Copy second side of connection
628  const auto& a = gathered[myIndex];
629  const auto& b = gathered[otherIndex];
630 
631  connection.second() = (connection.first() == b) ? a : b;
632  }
633 
634  // Mark as 'bad' even if somehow resolved. If we fail
635  // to make any connection, these will still be
636  // flagged later.
637  badEdges.insert(cppEdgei);
638  }
639  }
640  }
641 
642  if (returnReduceOr(badEdges.size()))
643  {
645  << nl << "Multiply connected edges detected" << endl;
646 
647  // Print out edges as point pairs
648  // These are globally synchronised - so only output on master
649  constexpr label maxOutput = 10;
650 
651  label nOutput = 0;
652 
653  for (const label cppEdgei : badEdges.sortedToc())
654  {
655  const edge e(cpp.meshEdge(cppEdgei));
656 
657  const auto& gathered = gatheredConnections[cppEdgei];
658 
659  Info<< "connection: ";
660  gathered.writeList(Info) << nl;
661 
662  Info<<" edge : "
663  << cpp.points()[e.first()] << ' '
664  << cpp.points()[e.second()] << nl;
665 
666  ++nOutput;
667  if (maxOutput > 0 && nOutput >= maxOutput)
668  {
669  Info<< " ... suppressing further output" << nl;
670  break;
671  }
672  }
673  }
674 
675  if (returnReduceOr(danglingEdges.size()))
676  {
678  << nl << "Dangling edges detected" << endl;
679 
680  // Print out edges as point pairs
681  // These are globally synchronised - so only output on master
682  constexpr label maxOutput = 10;
683 
684  label nOutput = 0;
685 
686  for (const label cppEdgei : danglingEdges.sortedToc())
687  {
688  const edge e(cpp.meshEdge(cppEdgei));
689 
690  const auto& gathered = gatheredConnections[cppEdgei];
691 
692  Info<< "connection: ";
693  gathered.writeList(Info) << nl;
694 
695  Info<<" edge : "
696  << cpp.points()[e.first()] << ' '
697  << cpp.points()[e.second()] << nl;
698 
699  ++nOutput;
700  if (maxOutput > 0 && nOutput >= maxOutput)
701  {
702  Info<< " ... suppressing further output" << nl;
703  break;
704  }
705  }
706 
707  labelList selectEdges(danglingEdges.sortedToc());
708  word outputName("faMesh-construct.danglingEdges");
709 
711  (
712  cpp,
713  selectEdges,
714  thisDb().time().globalPath(),
715  outputName
716  );
717 
719  << "(debug) wrote " << outputName << nl;
720  }
721 
722 
723  // Check missing/invalid
724  badEdges.clear();
725  for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
726  {
727  const auto& connection = bndEdgeConnections[bndEdgei];
728 
729  if (!connection.second().valid())
730  {
731  badEdges.insert(nInternalEdges + bndEdgei);
732  }
733  }
734 
735 
736  if (debug & 8)
737  {
738  // Boundary edges
739  {
740  vtk::lineWriter writer
741  (
742  patch().localPoints(),
743  patch().boundaryEdges(),
744  fileName
745  (
746  thisDb().time().globalPath()
747  / ("faMesh-construct.boundaryEdges")
748  )
749  );
750 
751  writer.writeGeometry();
752 
753  // CellData
754  writer.beginCellData();
755  writer.writeProcIDs();
756 
757  // For each boundary edge - the associate neighbour patch
758  labelList neighProc(nBoundaryEdges);
759  labelList neighPatch(nBoundaryEdges);
760  for (label bndEdgei = 0; bndEdgei < nBoundaryEdges; ++bndEdgei)
761  {
762  const auto& connection = bndEdgeConnections[bndEdgei];
763 
764  neighProc[bndEdgei] = connection.second().procNo();
765  neighPatch[bndEdgei] = connection.second().patchi();
766  }
767 
768  writer.write("neighProc", neighProc);
769  writer.write("neighPatch", neighPatch);
770  }
771 
772  // finiteArea
773  {
775  (
776  patch(),
777  fileName
778  (
779  thisDb().time().globalPath()
780  / ("faMesh-construct.faPatch")
781  )
782  );
783 
784  writer.writeGeometry();
785 
786  // CellData
787  writer.beginCellData();
788  writer.writeProcIDs();
789  }
790  }
791 
792  // Verbose report of missing edges
793  if (returnReduceOr(badEdges.size()))
794  {
795  labelList selectEdges(badEdges.sortedToc());
796  word outputName("faMesh-construct.invalidEdges");
797 
799  (
800  patch(),
801  selectEdges,
802  thisDb().time().globalPath(),
803  outputName
804  );
805 
807  << "(debug) wrote " << outputName << nl;
808 
810  << "Boundary edges with missing/invalid neighbours: "
811  << returnReduce(selectEdges.size(), sumOp<label>()) << '/'
812  << nBoundaryEdges << nl;
813 
815  (
816  FatalError,
817  patch(),
818  selectEdges
819  );
820 
821  // Delay until later... FatalError << abort(FatalError);
822  }
823 
824 
825  // Globally consistent ordering
826  patchTuple::sort(bndEdgeConnections);
827 
829  << "Return sorted list of boundary connections" << endl;
830 
831  return bndEdgeConnections;
832 }
833 
834 
835 void Foam::faMesh::setBoundaryConnections
836 (
837  const List<Pair<patchTuple>>& bndEdgeConnections
838 ) const
839 {
840  const label nInternalEdges = patch().nInternalEdges();
841  const label nBoundaryEdges = patch().nBoundaryEdges();
842 
843  if (bndEdgeConnections.size() != nBoundaryEdges)
844  {
846  << "Sizing mismatch. Expected " << nBoundaryEdges
847  << " boundary edge connections, but had "
848  << bndEdgeConnections.size() << nl
849  << abort(FatalError);
850  }
851 
852  bndConnectPtr_.reset
853  (
854  new List<labelPair>(nBoundaryEdges, labelPair(-1,-1))
855  );
856  auto& bndConnect = *bndConnectPtr_;
857 
858  for (const auto& connection : bndEdgeConnections)
859  {
860  const auto& a = connection.first();
861  const auto& b = connection.second();
862 
863  if (a.is_finiteArea() && a.is_localProc())
864  {
865  const label bndEdgei = (a.patchEdgei() - nInternalEdges);
866 
867  bndConnect[bndEdgei].first() = b.procNo();
868  bndConnect[bndEdgei].second() = b.meshFacei();
869  }
870  else if (b.is_finiteArea() && b.is_localProc())
871  {
872  const label bndEdgei = (b.patchEdgei() - nInternalEdges);
873 
874  bndConnect[bndEdgei].first() = a.procNo();
875  bndConnect[bndEdgei].second() = a.meshFacei();
876  }
877  else
878  {
880  << "Unexpected pairing input " << connection
881  << " ... programming error" << nl
882  << abort(FatalError);
883  }
884  }
885 
886  label nInvalid(0);
887  for (const auto& connection : bndConnect)
888  {
889  if (connection.first() < 0 || connection.second() < 0)
890  {
891  ++nInvalid;
892  }
893  }
894 
895  if (returnReduceOr(nInvalid))
896  {
897  labelHashSet badEdges(2*nInvalid);
898 
899  forAll(bndConnect, bndEdgei)
900  {
901  if
902  (
903  bndConnect[bndEdgei].first() < 0
904  || bndConnect[bndEdgei].second() < 0
905  )
906  {
907  badEdges.insert(nInternalEdges + bndEdgei);
908  }
909  }
910 
911  labelList selectEdges(badEdges.sortedToc());
912  word outputName("faMesh-construct.invalidMatches");
913 
915  (
916  patch(),
917  selectEdges,
918  thisDb().time().globalPath(),
919  outputName
920  );
921 
923  << "(debug) wrote " << outputName << nl;
924 
926  << "Did not properly match "
927  << returnReduce(nInvalid, sumOp<label>())
928  << " boundary edges" << nl;
929 
930  // Delay until later... FatalError << abort(FatalError);
931  }
932 }
933 
934 
935 void Foam::faMesh::calcBoundaryConnections() const
936 {
937  setBoundaryConnections(this->getBoundaryEdgeConnections());
938 }
939 
940 
941 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
942 
944 {
945  const auto& connections = this->boundaryConnections();
946 
947  labelHashSet procsUsed(2*UPstream::nProcs());
948 
949  for (const labelPair& tuple : connections)
950  {
951  procsUsed.insert(tuple.first());
952  }
953 
954  procsUsed.erase(-1); // placeholder value
955  procsUsed.erase(UPstream::myProcNo());
956 
957  return procsUsed.sortedToc();
958 }
959 
960 
962 {
963  const auto& connections = this->boundaryConnections();
964 
965  Map<label> procCount(2*UPstream::nProcs());
966 
967  for (const labelPair& tuple : connections)
968  {
969  ++procCount(tuple.first());
970  }
971  procCount.erase(-1); // placeholder value
972  procCount.erase(UPstream::myProcNo());
973 
974  // Flatten as list
975  List<labelPair> output(procCount.size());
976  label count = 0;
977  for (const label proci : procCount.sortedToc())
978  {
979  output[count].first() = proci;
980  output[count].second() = procCount[proci]; // size
981  ++count;
982  }
983 
984  return output;
985 }
986 
987 
989 {
990  if (!haloMapPtr_)
991  {
992  haloMapPtr_.reset(new faMeshBoundaryHalo(*this));
993  }
994 
995  return *haloMapPtr_;
996 }
997 
998 
999 bool Foam::faMesh::hasHaloFaceGeometry() const noexcept
1000 {
1001  // Always create/destroy in tandem
1002  return (haloFaceCentresPtr_ && haloFaceNormalsPtr_);
1003 }
1004 
1005 
1006 void Foam::faMesh::calcHaloFaceGeometry() const
1007 {
1008  if (haloFaceCentresPtr_ || haloFaceNormalsPtr_)
1009  {
1011  << "Halo centres/normals already calculated"
1012  << exit(FatalError);
1013  }
1014 
1016  << "Calculating halo face centres/normals" << endl;
1017 
1018  const faceList& faces = mesh().faces();
1019  const pointField& points = mesh().points();
1020 
1021  const faMeshBoundaryHalo& halo = boundaryHaloMap();
1022 
1023  const labelList& inputFaceIds = halo.inputMeshFaces();
1024 
1025  haloFaceCentresPtr_.reset(new pointField);
1026  haloFaceNormalsPtr_.reset(new vectorField);
1027 
1028  auto& centres = *haloFaceCentresPtr_;
1029  auto& normals = *haloFaceNormalsPtr_;
1030 
1031  centres.resize(inputFaceIds.size());
1032  normals.resize(inputFaceIds.size());
1033 
1034  // My values
1035  forAll(inputFaceIds, i)
1036  {
1037  const face& f = faces[inputFaceIds[i]];
1038 
1039  centres[i] = f.centre(points);
1040  normals[i] = f.unitNormal(points);
1041  }
1043  // Swap information and resize
1044  halo.distributeSparse(centres);
1045  halo.distributeSparse(normals);
1046 }
1047 
1048 
1050 {
1051  // Always create/destroy in tandem
1052  if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1053  {
1054  calcHaloFaceGeometry();
1055  }
1056 
1057  return *haloFaceCentresPtr_;
1058 }
1059 
1060 
1062 {
1063  // Always create/destroy in tandem
1064  if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1065  {
1066  calcHaloFaceGeometry();
1067  }
1068 
1069  return *haloFaceNormalsPtr_;
1070 }
1071 
1072 
1074 Foam::faMesh::haloFaceCentres(const label patchi) const
1075 {
1076  if (patchi < 0 || patchi >= boundary().size())
1077  {
1079  << "Patch " << patchi << " is out-of-range 0.."
1080  << (boundary().size()-1) << nl
1081  << exit(FatalError);
1082  }
1083 
1084  return tmp<pointField>::New
1085  (
1086  List<point>
1087  (
1088  boundarySubset
1089  (
1090  this->haloFaceCentres(),
1091  boundary()[patchi].edgeLabels()
1092  )
1093  )
1094  );
1095 }
1096 
1097 
1099 Foam::faMesh::haloFaceNormals(const label patchi) const
1100 {
1101  if (patchi < 0 || patchi >= boundary().size())
1102  {
1104  << "Patch " << patchi << " is out-of-range 0.."
1105  << (boundary().size()-1) << nl
1106  << exit(FatalError);
1107  }
1108 
1109  return tmp<vectorField>::New
1110  (
1111  List<vector>
1112  (
1113  boundarySubset
1114  (
1115  this->haloFaceNormals(),
1116  boundary()[patchi].edgeLabels()
1117  )
1118  )
1119  );
1120 }
1121 
1122 
1123 // ************************************************************************* //
faceListList boundary
label patchId(-1)
const polyBoundaryMesh & pbm
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/"finiteArea-edgesCentres"))
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:160
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
const Time & time() const
Return reference to time.
Definition: faMesh.C:791
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
const pointField & haloFaceCentres() const
Face centres of boundary halo neighbours.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1049
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)
Definition: faMesh.C:1033
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...
Definition: UPstream.H:1074
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.
Definition: HashSet.H:232
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)
virtual const objectRegistry & thisDb() const
Return reference to the mesh database.
Definition: faMesh.C:815
labelHashSet badEdges(pp.nEdges()/20)
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1078
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
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.
Definition: faMeshI.H:109
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
label size() const noexcept
The number of elements in table.
Definition: HashTable.H:342
void clear()
&#39;Clears&#39; edge by setting both ends to invalid vertex labels.
Definition: edgeI.H:201
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
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. ...
Definition: UPstream.H:1065
word outputName("finiteArea-edges.obj")
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
dynamicFvMesh & mesh
const pointField & points
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition: edge.H:59
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:608
#define DebugInFunction
Report an information message using Foam::Info.
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
const faMeshBoundaryHalo & boundaryHaloMap() const
Mapping/swapping for boundary halo neighbours.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
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)
Definition: polyMesh.C:1311
GenericPatchWriter< uindirectPrimitivePatch > uindirectPatchWriter
Write uindirectPrimitivePatch faces/points (optionally with fields) as a vtp file or a legacy vtk fil...
patchWriters clear()
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1103
label nBoundaryEdges() const
Number of boundary edges == (nEdges() - nInternalEdges())
errorManip< error > abort(error &err)
Definition: errorManip.H:139
static void vtkWritePatchEdges(const PatchType &p, const labelList &selectEdges, const fileName &outputPath, const word &outputName)
Definition: faMeshPatches.C:38
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...
Definition: Ostream.H:56
const direction noexcept
Definition: Scalar.H:258
void reset(const faMesh &mesh)
Redefine map and connectivity for a mesh.
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
OBJstream os(runTime.globalPath()/outputName)
const polyMesh & mesh() const
Return access to polyMesh.
Definition: faMeshI.H:24
labelList f(nPoints)
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition: HashTable.C:472
label nInternalEdges() const noexcept
Number of internal faces.
Definition: faMeshI.H:49
#define WarningInFunction
Report a warning using Foam::Warning.
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:139
void close()
End the file contents and close the file after writing.
const std::string patch
OpenFOAM patch number as a std::string.
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)
Definition: IntRanges.C:44
Field< vector > vectorField
Specialisation of Field<T> for vector.
fileName globalPath() const
The complete global path for the object (with instance, local,...)
Definition: IOobject.C:491
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
label nBoundaryEdges() const noexcept
Number of boundary edges (== nEdges - nInternalEdges)
Definition: faMeshI.H:55
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())
Namespace for OpenFOAM.
#define InfoInFunction
Report an information message using Foam::Info.