Loading...
Searching...
No Matches
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-------------------------------------------------------------------------------
10License
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"
32#include "edgeHashes.H"
33#include "syncTools.H"
34#include "foamVtkLineWriter.H"
36
37// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
38
39namespace Foam
40{
41
42// Print out edges as point pairs
43template<class PatchType>
44static 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
73template<class PatchType>
74static 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
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
105Foam::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(),
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 (
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>()) << '/'
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(),
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 (
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(),
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 (
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 (
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(),
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 (
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
835void 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_ = std::make_unique<List<labelPair>>
853 (
854 nBoundaryEdges,
855 labelPair(-1,-1)
856 );
857 auto& bndConnect = *bndConnectPtr_;
858
859 for (const auto& connection : bndEdgeConnections)
860 {
861 const auto& a = connection.first();
862 const auto& b = connection.second();
863
864 if (a.is_finiteArea() && a.is_localProc())
865 {
866 const label bndEdgei = (a.patchEdgei() - nInternalEdges);
867
868 bndConnect[bndEdgei].first() = b.procNo();
869 bndConnect[bndEdgei].second() = b.meshFacei();
870 }
871 else if (b.is_finiteArea() && b.is_localProc())
872 {
873 const label bndEdgei = (b.patchEdgei() - nInternalEdges);
874
875 bndConnect[bndEdgei].first() = a.procNo();
876 bndConnect[bndEdgei].second() = a.meshFacei();
877 }
878 else
879 {
881 << "Unexpected pairing input " << connection
882 << " ... programming error" << nl
883 << abort(FatalError);
884 }
885 }
886
887 label nInvalid(0);
888 for (const auto& connection : bndConnect)
889 {
890 if (connection.first() < 0 || connection.second() < 0)
891 {
892 ++nInvalid;
893 }
894 }
895
896 if (returnReduceOr(nInvalid))
897 {
898 labelHashSet badEdges(2*nInvalid);
899
900 forAll(bndConnect, bndEdgei)
901 {
902 if
903 (
904 bndConnect[bndEdgei].first() < 0
905 || bndConnect[bndEdgei].second() < 0
906 )
907 {
908 badEdges.insert(nInternalEdges + bndEdgei);
909 }
910 }
911
912 labelList selectEdges(badEdges.sortedToc());
913 word outputName("faMesh-construct.invalidMatches");
914
916 (
917 patch(),
918 selectEdges,
919 thisDb().time().globalPath(),
921 );
922
924 << "(debug) wrote " << outputName << nl;
925
927 << "Did not properly match "
928 << returnReduce(nInvalid, sumOp<label>())
929 << " boundary edges" << nl;
930
931 // Delay until later... FatalError << abort(FatalError);
932 }
933}
934
935
936void Foam::faMesh::calcBoundaryConnections() const
938 setBoundaryConnections(this->getBoundaryEdgeConnections());
939}
940
941
942// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
943
945{
946 const auto& connections = this->boundaryConnections();
947
948 labelHashSet procsUsed(2*UPstream::nProcs());
949
950 for (const labelPair& tuple : connections)
951 {
952 procsUsed.insert(tuple.first());
953 }
954
955 procsUsed.erase(-1); // placeholder value
956 procsUsed.erase(UPstream::myProcNo());
957
958 return procsUsed.sortedToc();
959}
960
961
963{
964 const auto& connections = this->boundaryConnections();
965
966 Map<label> procCount(2*UPstream::nProcs());
967
968 for (const labelPair& tuple : connections)
969 {
970 ++procCount(tuple.first());
971 }
972 procCount.erase(-1); // placeholder value
973 procCount.erase(UPstream::myProcNo());
974
975 // Flatten as list
976 List<labelPair> output(procCount.size());
977 label count = 0;
978 for (const label proci : procCount.sortedToc())
979 {
980 output[count].first() = proci;
981 output[count].second() = procCount[proci]; // size
983 }
984
985 return output;
986}
987
988
990{
991 if (!haloMapPtr_)
992 {
993 haloMapPtr_ = std::make_unique<faMeshBoundaryHalo>(*this);
994 }
995
996 return *haloMapPtr_;
997}
998
999
1000bool Foam::faMesh::hasHaloFaceGeometry() const noexcept
1001{
1002 // Always create/destroy in tandem
1003 return (haloFaceCentresPtr_ && haloFaceNormalsPtr_);
1004}
1005
1006
1007void Foam::faMesh::calcHaloFaceGeometry() const
1008{
1009 if (haloFaceCentresPtr_ || haloFaceNormalsPtr_)
1010 {
1012 << "Halo centres/normals already calculated"
1013 << exit(FatalError);
1014 }
1015
1017 << "Calculating halo face centres/normals" << endl;
1018
1019 const faceList& faces = mesh().faces();
1020 const pointField& points = mesh().points();
1021
1022 const faMeshBoundaryHalo& halo = boundaryHaloMap();
1023
1024 const labelList& inputFaceIds = halo.inputMeshFaces();
1025
1026 haloFaceCentresPtr_ = std::make_unique<pointField>();
1027 haloFaceNormalsPtr_ = std::make_unique<vectorField>();
1028
1029 auto& centres = *haloFaceCentresPtr_;
1030 auto& normals = *haloFaceNormalsPtr_;
1031
1032 centres.resize(inputFaceIds.size());
1033 normals.resize(inputFaceIds.size());
1034
1035 // My values
1036 forAll(inputFaceIds, i)
1037 {
1038 const face& f = faces[inputFaceIds[i]];
1039
1040 centres[i] = f.centre(points);
1041 normals[i] = f.unitNormal(points);
1042 }
1044 // Swap information and resize
1045 halo.distributeSparse(centres);
1046 halo.distributeSparse(normals);
1047}
1048
1049
1051{
1052 // Always create/destroy in tandem
1053 if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1054 {
1055 calcHaloFaceGeometry();
1056 }
1057
1058 return *haloFaceCentresPtr_;
1059}
1060
1061
1063{
1064 // Always create/destroy in tandem
1065 if (!haloFaceCentresPtr_ || !haloFaceNormalsPtr_)
1066 {
1067 calcHaloFaceGeometry();
1069
1070 return *haloFaceNormalsPtr_;
1071}
1072
1073
1075Foam::faMesh::haloFaceCentres(const label patchi) const
1076{
1077 if (patchi < 0 || patchi >= boundary().size())
1078 {
1080 << "Patch " << patchi << " is out-of-range 0.."
1081 << (boundary().size()-1) << nl
1082 << exit(FatalError);
1083 }
1084
1086 (
1088 (
1089 boundarySubset
1090 (
1091 this->haloFaceCentres(),
1092 boundary()[patchi].edgeLabels()
1094 )
1095 );
1096}
1097
1098
1100Foam::faMesh::haloFaceNormals(const label patchi) const
1101{
1102 if (patchi < 0 || patchi >= boundary().size())
1103 {
1105 << "Patch " << patchi << " is out-of-range 0.."
1106 << (boundary().size()-1) << nl
1107 << exit(FatalError);
1108 }
1109
1111 (
1112 List<vector>
1113 (
1114 boundarySubset
1115 (
1116 this->haloFaceNormals(),
1117 boundary()[patchi].edgeLabels()
1118 )
1119 )
1120 );
1121}
1122
1123
1124// ************************************************************************* //
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.
Definition HashSet.H:229
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition HashTable.C:156
label size() const noexcept
The number of elements in table.
Definition HashTable.H:358
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition HashTable.C:489
fileName globalPath() const
The complete global path for the object (with instance, local,...).
Definition IOobject.C:512
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
A HashTable to objects of type <T> with a label key.
Definition Map.H:54
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
An ordered pair of two objects of type <T> with first() and second() elements.
Definition Pair.H:66
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].
Definition UList.H:957
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition UPstream.H:1706
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:1697
static bool & parRun() noexcept
Test if this a parallel run.
Definition UPstream.H:1681
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:62
void clear()
'Clears' edge by setting both ends to invalid vertex labels.
Definition edgeI.H:217
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.
Definition faMesh.H:1209
const vectorField & haloFaceNormals() const
Face unit-normals of boundary halo neighbours.
const Time & time() const
Return reference to time.
Definition faMesh.C:1028
const pointField & haloFaceCentres() const
Face centres of boundary halo neighbours.
label nBoundaryEdges() const noexcept
Number of boundary edges (== nEdges - nInternalEdges).
Definition faMeshI.H:48
const polyMesh & mesh() const
Return access to polyMesh.
Definition faMesh.C:1016
const uindirectPrimitivePatch & patch() const
Return constant reference to primitive patch.
Definition faMeshI.H:102
label nInternalEdges() const noexcept
Number of internal faces.
Definition faMeshI.H:42
const faGlobalMeshData & globalData() const
Return parallel info (demand-driven).
Definition faMesh.C:1258
const List< labelPair > & boundaryConnections() const
List of proc/face for the boundary edge neighbours using primitive patch edge numbering.
Definition faMeshI.H:155
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.
Definition face.H:71
A class for handling file names.
Definition fileName.H:75
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.
Definition polyMesh.H:609
virtual const faceList & faces() const
Return raw faces.
Definition polyMesh.C:1088
const globalMeshData & globalData() const
Return parallel info (demand-driven).
Definition polyMesh.C:1296
virtual const pointField & points() const
Return raw points.
Definition polyMesh.C:1063
static int debug
Debug switch.
A class for managing temporary objects.
Definition tmp.H:75
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition tmp.H:215
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.
Definition word.H:66
volScalarField & p
faceListList boundary
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
word outputName("finiteArea-edges.obj")
OBJstream os(runTime.globalPath()/outputName)
const pointField & points
label patchId(-1)
surface1 clear()
#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...
Namespace for OpenFOAM.
List< edge > edgeList
List of edge.
Definition edgeList.H:32
Pair< label > labelPair
A pair of labels.
Definition Pair.H:54
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.
Definition List.H:62
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition HashSet.H:85
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.
Definition faceListFwd.H:41
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.
Definition Ostream.H:519
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.
Definition UList.C:283
errorManip< error > abort(error &err)
Definition errorManip.H:139
Field< vector > vectorField
Specialisation of Field<T> for vector.
const direction noexcept
Definition scalarImpl.H:265
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)
Definition errorManip.H:125
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)
volScalarField & b
volScalarField & e
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299