Loading...
Searching...
No Matches
UPstreamCommsStruct.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) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2021-2025 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "UPstream.H"
30
31#include <algorithm>
32#include <numeric>
34// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
35
36namespace Foam
37{
38
39// This outputs as depth-first, but graphviz sorts that for us
40static void printGraph_impl
41(
42 Ostream& os,
43 const UPstream::commsStructList& comms,
44 const int proci,
45 int depth,
46 const int maxDepth = 1024
47)
48{
49 if (proci >= comms.size())
50 {
51 // Corner case when only a single rank involved
52 // (eg, for node-local communicator)
53 return;
54 }
55
56 const auto& below = comms[proci].below();
57
58 if (proci == 0)
59 {
60 os << nl << "// communication graph:" << nl;
61 os.beginBlock("graph");
62
63 // Prefer left-to-right layout for large graphs
64 os << indent << "rankdir=LR" << nl;
65 }
66
67
68 // Output the immediate neighbours below
69
70 if (below.empty())
71 {
72 if (proci == 0)
73 {
74 // A graph with a single-node (eg, self-comm)
75 os << indent << proci << nl;
76 }
77 }
78 else
79 {
80 os << indent << proci << " -- " << token::BEGIN_BLOCK;
81
82 // Accumulate into ranges whenever possible
84
85 // Print accumulated range and reset
86 auto emit_range = [&]()
87 {
88 if (!range.empty())
89 {
90 os << ' ';
91 if (range.min() < range.max())
92 {
93 os << '"' << range.min() << ".." << range.max() << '"';
94 }
95 else
96 {
97 os << range.min();
98 }
99 range.reset();
100 }
101 };
102
103 for (const auto nbrProci : below)
104 {
105 const bool terminal = comms[nbrProci].below().empty();
106
107 if
108 (
109 terminal
110 && (!range.empty() && (range.max()+1 == nbrProci))
111 )
112 {
113 // Accumulate
114 ++range;
115 continue;
116 }
117
118 // Emit accumulated range
119 emit_range();
120
121 if (terminal)
122 {
123 range.reset(nbrProci, 1);
124 }
125 else
126 {
127 os << token::SPACE << nbrProci;
128 }
129 }
130
131 // Emit accumulated range
132 emit_range();
133
135 }
136
137
138 // Recurse into below neighbours, but limit the maximum depth
139 ++depth;
140 if (depth >= maxDepth && (proci != 0))
141 {
142 return;
143 }
144
145 for (const auto nbrProci : below)
146 {
147 // if (proci == nbrProci) continue; // Extreme safety!
148 printGraph_impl(os, comms, nbrProci, depth, maxDepth);
149 }
150
151 if (proci == 0)
152 {
153 os.endBlock();
154 os << "// end graph" << nl;
155 }
156}
157
158} // End namespace Foam
159
160
161// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
162
163// Create a tree-like schedule. For 8 procs:
164// (level 0)
165// 0 receives from 1
166// 2 receives from 3
167// 4 receives from 5
168// 6 receives from 7
169// (level 1)
170// 0 receives from 2
171// 4 receives from 6
172// (level 2)
173// 0 receives from 4
174//
175// The sends/receives for all levels are collected per processor
176// (one send per processor; multiple receives possible) creating
177// a table:
178//
179// So per processor:
180// proc receives from sends to
181// ---- ------------- --------
182// 0 1,2,4 -
183// 1 - 0
184// 2 3 0
185// 3 - 2
186// 4 5 0
187// 5 - 4
188// 6 7 4
189// 7 - 6
190
191namespace Foam
192{
193
194static int simpleTree
195(
196 const int myProci,
197 const int numProcs,
198
199 DynamicList<int>& below,
200 DynamicList<int>& allBelow
201)
202{
203 int above(-1);
204
205 for (int mod = 2, step = 1; step < numProcs; step = mod)
206 {
207 mod = step * 2;
208
209 if (myProci % mod)
210 {
211 // The rank above
212 above = myProci - (myProci % mod);
213 break;
214 }
215 else
216 {
217 for
218 (
219 int i = myProci + step;
220 i < numProcs && i < myProci + mod;
221 i += step
222 )
223 {
224 below.push_back(i);
225 }
226 for
227 (
228 int i = myProci + step;
229 i < numProcs && i < myProci + mod;
230 ++i
231 )
232 {
233 allBelow.push_back(i);
234 }
235 }
236 }
237
238 return above;
240
241} // End namespace Foam
242
243
244// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
245
247(
248 const int above,
249 List<int>&& below,
250 List<int>&& allBelow,
251 List<int>&& allNotBelow
252)
253:
254 above_(above),
255 below_(std::move(below)),
256 allBelow_(std::move(allBelow)),
257 allNotBelow_(std::move(allNotBelow))
258{}
259
260
262(
263 const int numProcs,
264 const int myProcID,
265 const int above,
266 const UList<int>& below,
267 const UList<int>& allBelow
268)
269:
270 above_(above),
271 below_(below),
272 allBelow_(allBelow),
273 allNotBelow_(numProcs - allBelow.size() - 1)
274{
275 List<bool> isNotBelow(numProcs, true);
276
277 // Exclude self
278 isNotBelow[myProcID] = false;
279
280 // Exclude allBelow
281 for (const auto proci : allBelow)
282 {
283 isNotBelow[proci] = false;
284 }
285
286 // Compacting to obtain allNotBelow_
287 int nNotBelow = 0;
288 for (int proci = 0; proci < numProcs; ++proci)
289 {
290 if (isNotBelow[proci])
291 {
292 allNotBelow_[nNotBelow++] = proci;
293 }
294 }
295
296 if (nNotBelow != allNotBelow_.size())
297 {
299 << "Problem: " << nNotBelow << " != " << allNotBelow_.size() << nl
301 }
302}
303
304
305// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
306
308(
309 Ostream& os,
310 const int proci
311) const
312{
313 // Print graph - starting at depth 0
314 // Avoid corner case when only a single rank involved
315 // (eg, for node-local communicator)
316
317 if (proci < size())
318 {
319 printGraph_impl(os, *this, proci, 0);
320 }
321}
322
323
324// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
327{
328 return (1 + int(allBelow_.size() + allNotBelow_.size()));
329}
330
331
333{
334 above_ = -1;
335 below_.clear();
336 allBelow_.clear();
337 allNotBelow_.clear();
338}
339
340
342(
343 const int myProci,
344 const int numProcs
345)
346{
347 reset();
348
349 // Linear (flat) communication pattern
350 int above(-1);
351 List<int> below;
352
353 if (myProci == 0)
354 {
355 below.resize(numProcs-1);
356 std::iota(below.begin(), below.end(), 1);
357 }
358 else
359 {
360 above = 0;
361 }
362
363 *this = UPstream::commsStruct(numProcs, myProci, above, below, below);
364}
365
366
368(
369 const int myProci,
370 const int numProcs,
371 const int communicator
372)
373{
374 // Trivially small domains
375 if (numProcs <= 2)
376 {
377 reset_linear(myProci, numProcs);
378 return;
379 }
380
381
382 reset();
383
384 int above(-1);
385 DynamicList<int> below;
386 DynamicList<int> allBelow;
387
388 if (UPstream::usingNodeComms(communicator))
389 {
390 // Additional treatment...
391 }
392
393
394 // Simple tree communication pattern
395 above = simpleTree
396 (
397 myProci,
398 numProcs,
399 below,
400 allBelow
401 );
402
403 *this = UPstream::commsStruct(numProcs, myProci, above, below, allBelow);
404}
405
406
407// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
408
411{
412 static std::unique_ptr<commsStructList> singleton;
413
414 if (!singleton)
415 {
416 singleton = std::make_unique<commsStructList>();
417 }
419 return *singleton;
420}
421
422
423// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
424
426{
427 if (flat_ != on)
428 {
429 // Current size
430 const auto len = tree_.size();
431
432 flat_ = on;
433 tree_.clear();
434 if (len > 0)
436 tree_.resize(len);
437 }
438 }
439}
440
441
443{
444 comm_ = communicator;
445 tree_.clear();
446}
447
448
451 comm_ = communicator;
452 tree_.clear();
453 flat_ = flat;
454}
455
456
458{
459 comm_ = communicator;
460 tree_.clear();
461 if (comm_ >= 0)
462 {
463 tree_.resize(UPstream::nProcs(comm_));
464 }
465}
466
467
468void Foam::UPstream::commsStructList::init(int communicator, bool flat)
471 flat_ = flat;
472}
473
474
477{
478 const auto numProcs = UPstream::nProcs(comm_);
479
480 // Only if reset(comm) instead of init(comm) was used
481 if (tree_.size() < numProcs)
482 {
483 const_cast<List<commsStruct>&>(tree_).resize(numProcs);
484 }
485
486 const UPstream::commsStruct& entry = tree_[proci];
487
488 if (entry.nProcs() != numProcs)
489 {
490 // Create/update
491
492 if (flat_)
493 {
494 const_cast<UPstream::commsStruct&>(entry)
495 .reset_linear(proci, numProcs);
496 }
497 else
498 {
499 const_cast<UPstream::commsStruct&>(entry)
500 .reset(proci, numProcs, comm_);
501 }
502 }
504 return entry;
505}
506
507
508// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
509
511{
512 return
514 (above() == comm.above())
515 && (below() == comm.below())
516 );
517}
518
519
522 return !operator==(comm);
523}
524
525
526// * * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * //
527
528Foam::Ostream& Foam::operator<<(Ostream& os, const UPstream::commsStruct& comm)
529{
530 os << comm.above() << nl;
531 os << " "; comm.below().writeList(os) << nl;
532 os << " "; comm.allBelow().writeList(os) << nl;
533 os << " "; comm.allNotBelow().writeList(os);
534
535 os.check(FUNCTION_NAME);
536 return os;
537}
538
539
540// ************************************************************************* //
scalar range
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void push_back(const T &val)
Copy append an element to the end of this list.
An interval of (signed) integers defined by a start and a size.
Definition IntRange.H:69
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
void resize(const label len)
Adjust allocated size of list.
Definition ListI.H:153
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition UList.H:89
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition UListI.H:410
iterator end() noexcept
Return an iterator to end traversing the UList.
Definition UListI.H:454
Ostream & writeList(Ostream &os, const label shortLen=0) const
Write List, with line-breaks in ASCII when length exceeds shortLen.
Definition UListIO.C:90
Collection of communication structures.
Definition UPstream.H:363
void init(int communicator)
Reset communicator index, fill tree with empty entries.
const UPstream::commsStruct & get(int proci) const
Get existing or create (demand-driven) entry.
bool linear() const noexcept
Linear (flat) communication instead of tree communication.
Definition UPstream.H:427
bool empty() const noexcept
True if the list is empty.
Definition UPstream.H:443
void reset(int communicator)
Reset communicator index, clear tree entries.
static const commsStructList & null()
An empty structure. Used for placeholders etc.
label size() const noexcept
The number of entries.
Definition UPstream.H:448
void printGraph(Ostream &os, int proci=0) const
Print un-directed graph in graphviz dot format.
Structure for communicating between processors.
Definition UPstream.H:229
bool operator!=(const commsStruct &) const
int nProcs() const noexcept
The number of processors addressed by the structure.
void reset_linear(const int myProci, const int numProcs)
Reset to linear (flat) communication.
const List< int > & below() const noexcept
The procIDs of the processors directly below.
Definition UPstream.H:300
bool operator==(const commsStruct &) const
commsStruct() noexcept
Default construct with above == -1.
Definition UPstream.H:262
const List< int > & allNotBelow() const noexcept
The procIDs of all processors not below myProcNo. The inverse set of allBelow without myProcNo.
Definition UPstream.H:312
void reset()
Reset to default constructed state.
int above() const noexcept
The procID of the processor directly above.
Definition UPstream.H:295
const List< int > & allBelow() const noexcept
The procIDs of all processors below (so not just directly below).
Definition UPstream.H:306
Wrapper for internally indexed communicator label. Always invokes UPstream::allocateCommunicatorCompo...
Definition UPstream.H:2546
static bool init(int &argc, char **&argv, const bool needsThread)
Initialisation function called from main.
Definition UPstream.C:40
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 usingNodeComms(const int communicator)
True if node topology-aware routines have been enabled, it is running in parallel,...
Definition UPstream.C:751
A keyword and a list of tokens is an 'entry'.
Definition entry.H:66
@ BEGIN_BLOCK
Begin block [isseparator].
Definition token.H:178
@ END_BLOCK
End block [isseparator].
Definition token.H:179
@ SPACE
Space [isspace].
Definition token.H:144
limits reset(1/(limits.max()+VSMALL), 1/(limits.min()+VSMALL))
patchWriters resize(patchIds.size())
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
OBJstream os(runTime.globalPath()/outputName)
#define FUNCTION_NAME
Namespace for OpenFOAM.
static int simpleTree(const int myProci, const int numProcs, DynamicList< int > &below, DynamicList< int > &allBelow)
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces).
Ostream & indent(Ostream &os)
Indent stream.
Definition Ostream.H:481
tmp< faMatrix< Type > > operator==(const faMatrix< Type > &, const faMatrix< Type > &)
errorManip< error > abort(error &err)
Definition errorManip.H:139
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...
static void printGraph_impl(Ostream &os, const UPstream::commsStructList &comms, const int proci, int depth, const int maxDepth=1024)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50