multiWorldConnectionsObject.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 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
29#include "Pstream.H"
30
31// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32
33namespace Foam
34{
36}
37
38
39// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
40
41namespace Foam
42{
43
44// Combine world-to-world connections.
45// Forward connection = 1, Backward connection = 2, Both = 3
47{
49 {
50 forAllConstIters(b, iter)
51 {
52 a(iter.key()) |= iter.val();
53 }
54 }
55};
56
57
58static void printDOT(Ostream& os, const EdgeMap<unsigned>& connections)
59{
60 os << nl << "// Multiworld communication graph:" << nl;
61 os.beginBlock("graph");
62
63 // Graph Nodes == worlds
64 label worldi = 0;
65 for (const word& worldName : UPstream::allWorlds())
66 {
67 os.indent();
68 os << worldi << " [xlabel=" << worldi
69 << ",label=\"" << worldName << "\"]" << nl;
70
71 ++worldi;
72 }
73 os << nl;
74
75 // Graph Edges == connections
76 for (const edge& connect : connections.sortedToc())
77 {
78 os.indent();
79 os << connect.first() << " -- " << connect.second();
80
81 // Mismatched forward/backward connections?
82 if (connections.lookup(connect, 0u) != 3u)
83 {
84 os << " [style=dashed] // mismatched?";
85 }
86 os << nl;
87 }
88
89 os.endBlock();
90
91 os << "// end graph" << nl;
92}
93
94} // End namespace Foam
95
96
97// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
98
99Foam::edge Foam::multiWorldConnections::worldPair(const label otherWorld)
100{
101 if (otherWorld < 0 || !Pstream::parRun())
102 {
103 Perr<< "ignore: no world or non-parallel" << endl;
104 return edge(-1, -1);
105 }
106 else if (UPstream::allWorlds().size() <= otherWorld)
107 {
108 Perr<< "ignore: invalid world: " << otherWorld << endl;
109 return edge(-1, -1);
110 }
111
112 const label thisWorldID = UPstream::myWorldID();
113
114 // The worlds (sorted)
115 return edge(thisWorldID, otherWorld, true);
116}
117
118
119Foam::edge Foam::multiWorldConnections::worldPair(const word& otherWorld)
120{
121 if (otherWorld.empty() || !Pstream::parRun())
122 {
123 Perr<< "ignore: no world or non-parallel" << endl;
124 return edge(-1, -1);
125 }
126
127 const label thisWorldID = UPstream::myWorldID();
128 const label otherWorldID = UPstream::allWorlds().find(otherWorld);
129
130 if (otherWorldID < 0)
131 {
133 << "Cannot find world " << otherWorld
134 << " in set of worlds " << flatOutput(UPstream::allWorlds())
135 << exit(FatalError);
136 }
137
138 // The worlds (sorted)
139 return edge(thisWorldID, otherWorldID, true);
140}
141
142
143Foam::label Foam::multiWorldConnections::createCommunicator(const edge& worlds)
144{
145 // Fallback: do not create, just use local world
146 label comm = UPstream::worldComm;
147
148 if (!worlds.valid())
149 {
150 return comm;
151 }
152
153 const labelList& worldIDs = UPstream::worldIDs();
154
155 DynamicList<label> subRanks(worldIDs.size());
156 forAll(worldIDs, proci)
157 {
158 if (worlds.found(worldIDs[proci]))
159 {
160 subRanks.append(proci);
161 }
162 }
163
164 // Allocate new communicator with parent 0 (= world)
165 comm = UPstream::allocateCommunicator(0, subRanks, true);
166
167 if (debug & 2)
168 {
169 Pout<< "multiWorld::communicator :"
170 << " between " << UPstream::allWorlds()[worlds.first()]
171 << " and " << UPstream::allWorlds()[worlds.second()]
172 << " sub-ranks: " << subRanks
173 << " comm:" << comm << endl;
174 }
175
176 return comm;
177}
178
179
180// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
181
183:
185{}
186
187
188// * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
189
192{
193 return MeshObjectType::New(runTime);
194}
195
196
197// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
198
200{}
201
202
203// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
204
206{
207 return table_.empty();
208}
209
210
212{
213 return table_.size();
214}
215
216
218{
219 // Need new communicator(s)
220
221 const label thisWorldID = UPstream::myWorldID();
222
223 EdgeMap<unsigned> allConnections;
224 forAllConstIters(table_, iter)
225 {
226 const edge& connect = iter.key();
227
228 allConnections.insert
229 (
230 connect,
231 (connect.first() == thisWorldID ? 1u : 2u)
232 );
233 }
234
235
236 // Use MPI_COMM_WORLD
237 const label oldWorldComm(Pstream::worldComm);
238 const label oldWarnComm(Pstream::warnComm);
241
242 if (Pstream::parRun())
243 {
245 (
246 allConnections,
248 );
249 Pstream::scatter(allConnections);
250 }
251
252 // Check for mismatched connections
253 label brokenConnections = 0;
254
255 forAllConstIters(allConnections, iter)
256 {
257 // Mismatched forward/backward connections?
258 if (iter.val() != 3u)
259 {
260 ++brokenConnections;
261 }
262 }
263
264 if (brokenConnections)
265 {
266 Pstream::warnComm = oldWarnComm;
267 Pstream::worldComm = oldWorldComm;
268
270 << "Has " << brokenConnections
271 << " broken world-world connections";
272
273 printDOT(FatalError, allConnections);
274
276 }
277 else
278 {
279 // NOTE: process in sorted order to ensure proper
280 // synchronization on all worlds and all processors
281
282 for (const edge& connect : allConnections.sortedToc())
283 {
284 // Process known connections without communicators.
285 // - create a communicator and cache its value
286
287 auto iter = table_.find(connect);
288 if (iter.found() && iter.val() == -1)
289 {
290 iter.val() = createCommunicator(connect);
291 }
292 }
293
294 Pstream::warnComm = oldWarnComm;
295 Pstream::worldComm = oldWorldComm;
296 }
297
298 if (debug)
299 {
300 printDOT(Info, allConnections);
301 }
302}
303
304
306{
307 // The worlds (sorted)
308 edge worlds(worldPair(otherWorld));
309
310 if (!worlds.valid())
311 {
312 return false;
313 }
314
315 const bool added = table_.insert(worlds, -1);
316
317 Pout<< (added ? "Add" : "Existing") << " connection from "
318 << UPstream::myWorld() << " to " << otherWorld << nl;
319
320 return added;
321}
322
323
325{
326 // The worlds (sorted)
327 edge worlds(worldPair(otherWorld));
328
329 if (!worlds.valid())
330 {
331 return false;
332 }
333
334 const bool added = table_.insert(worlds, -1);
335
336 Pout<< (added ? "Add" : "Existing") << " connection from "
337 << UPstream::myWorld() << " to " << otherWorld << nl;
338
339 return added;
340}
341
342
344(
345 const label otherWorldID
346) const
347{
348 // Default: use local world
349 label comm = UPstream::worldComm;
350
351 // The communication worlds (sorted)
352 edge worlds(worldPair(otherWorldID));
353
354 if (!worlds.valid())
355 {
356 return comm;
357 }
358
359 const auto iter = table_.cfind(worlds);
360
361 if (!iter.found())
362 {
364 << "No connection registered for worlds " << worlds
365 << exit(FatalError);
366 }
367
368 // Get cached value, or allocate ALL known communicators
369 comm = iter.val();
370
371 if (comm == -1)
372 {
373 // Need new communicator(s)
374 const_cast<multiWorldConnections&>(*this).createComms();
375
376 // Retrieve from table cache
377 comm = table_.lookup(worlds, UPstream::worldComm);
378 }
379
380 return comm;
381}
382
383
385(
386 const word& otherWorld
387) const
388{
389 // Default: use local world
390 label comm = UPstream::worldComm;
391
392 // The communication worlds (sorted)
393 edge worlds(worldPair(otherWorld));
394
395 if (!worlds.valid())
396 {
397 return comm;
398 }
399
400 const auto iter = table_.cfind(worlds);
401
402 if (!iter.found())
403 {
405 << "No connection registered for worlds " << worlds
406 << exit(FatalError);
407 }
408
409 // Get cached value, or allocate ALL known communicators
410 comm = iter.val();
411
412 if (comm == -1)
413 {
414 // Need new communicator(s)
415 const_cast<multiWorldConnections&>(*this).createComms();
416
417 // Retrieve from table cache
418 comm = table_.lookup(worlds, UPstream::worldComm);
419 }
420
421 return comm;
422}
423
424
426{
427 labelList list(table_.size());
428
429 if (list.empty())
430 {
431 // Default: use local world
433 }
434 else
435 {
436 forAllConstIters(table_, iter)
437 {
438 if (iter.val() == -1)
439 {
440 // Need new communicator(s)
441 const_cast<multiWorldConnections&>(*this).createComms();
442 break;
443 }
444 }
445
446 // Retrieve values from table cache
447 label i = 0;
448
449 forAllConstIters(table_, iter)
450 {
451 list[i] = iter.val();
452 ++i;
453 }
454
455 Foam::sort(list); // Consistent order!
456 }
457
458 return list;
459}
460
461
462// ************************************************************************* //
Map from edge (expressed as its endpoints) to value. For easier forward declaration it is currently i...
Definition: EdgeMap.H:54
T & first() noexcept
The first element of the list, position [0].
Definition: FixedListI.H:207
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:137
const T & lookup(const Key &key, const T &deflt) const
Return hashed entry if it exists, or return the given default.
Definition: HashTableI.H:224
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
virtual void indent()
Add indentation characters.
Definition: OSstream.C:266
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:62
virtual Ostream & endBlock()
Write end block group.
Definition: Ostream.C:105
virtual Ostream & beginBlock(const keyType &kw)
Write begin block group with the given name.
Definition: Ostream.C:87
static void combineGather(const List< commsStruct > &comms, T &value, const CombineOp &cop, const int tag, const label comm)
static void scatter(const List< commsStruct > &comms, T &value, const int tag, const label comm)
Broadcast data: Distribute without modification.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:80
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:427
label find(const T &val, label pos=0) const
Find index of the first occurrence of the value.
Definition: UList.C:212
static label warnComm
Debugging: warn for use of any communicator differing from warnComm.
Definition: UPstream.H:296
static const labelList & worldIDs() noexcept
worldID (index in allWorlds) of all processes
Definition: UPstream.H:489
static label worldComm
Default communicator (all processors)
Definition: UPstream.H:293
static label myWorldID()
My worldID.
Definition: UPstream.H:495
static const wordList & allWorlds() noexcept
All worlds.
Definition: UPstream.H:483
static label allocateCommunicator(const label parent, const labelList &subRanks, const bool doPstream=true)
Allocate a new communicator.
Definition: UPstream.C:108
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
static const word & myWorld()
My world.
Definition: UPstream.H:501
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:66
bool valid() const
Return true if the vertices are unique and non-negative.
Definition: edgeI.H:130
Centralized handling of multi-world MPI connections.
label getCommByName(const word &otherWorld) const
Get communicator for myWorld to other world connection by NAME.
bool empty() const noexcept
True if no world-to-world connections are defined.
bool addConnectionByName(const word &otherWorld)
Define a connection from myWorld to other world by NAME.
label getCommById(const label otherWorld) const
Get communicator for myWorld to other world connection by ID.
label size() const noexcept
Number of world-to-world connections defined.
labelList comms() const
Get communicators used for myWorld to other worlds in sorted order.
bool addConnectionById(const label otherWorld)
Define a connection from myWorld to other world by ID.
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
Namespace for OpenFOAM.
List< label > labelList
A List of labels.
Definition: List.H:66
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
messageStream Info
Information stream (stdout output on master, null elsewhere)
static void printDOT(Ostream &os, const EdgeMap< unsigned > &connections)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:215
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:342
const direction noexcept
Definition: Scalar.H:223
error FatalError
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
volScalarField & b
Definition: createFields.H:27
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition: stdFoam.H:278
void operator()(EdgeMap< unsigned > &a, const EdgeMap< unsigned > &b) const