Loading...
Searching...
No Matches
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-2025 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{
48 void operator()(EdgeMap<unsigned>& a, const EdgeMap<unsigned>& b) const
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::sorted(thisWorldID, otherWorld);
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::sorted(thisWorldID, otherWorldID);
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.good())
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.push_back(proci);
161 }
162 }
163
164 // Allocate new communicator with global world
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 }
176 return comm;
177}
178
179
180// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
181
183:
184 MeshObject_type(runTime)
185{}
186
187
188// * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
189
194}
195
196
197// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
200{
201 return table_.empty();
202}
203
206{
207 return table_.size();
208}
209
210
212{
213 // Need new communicator(s)
214
215 const label thisWorldID = UPstream::myWorldID();
216
217 EdgeMap<unsigned> allConnections;
218 forAllConstIters(table_, iter)
219 {
220 const edge& connect = iter.key();
221
222 allConnections.insert
223 (
224 connect,
225 (connect.first() == thisWorldID ? 1u : 2u)
226 );
227 }
228
229
230 // Use MPI_COMM_WORLD
231 const label oldWarnComm = UPstream::commWarn(UPstream::commGlobal());
232 const label oldWorldComm = UPstream::commWorld(UPstream::commGlobal());
233
234 Pstream::combineReduce(allConnections, worldConnectBitOrEq());
235
236 // Check for mismatched connections
237 label brokenConnections = 0;
238
239 forAllConstIters(allConnections, iter)
240 {
241 // Mismatched forward/backward connections?
242 if (iter.val() != 3u)
243 {
244 ++brokenConnections;
245 }
246 }
247
248 if (brokenConnections)
249 {
250 // Restore communicator settings
251 UPstream::commWarn(oldWarnComm);
252 UPstream::commWorld(oldWorldComm);
253
255 << "Has " << brokenConnections
256 << " broken world-world connections";
257
258 printDOT(FatalError, allConnections);
259
261 }
262 else
263 {
264 // NOTE: process in sorted order to ensure proper
265 // synchronization on all worlds and all processors
266
267 for (const edge& connect : allConnections.sortedToc())
268 {
269 // Process known connections without communicators.
270 // - create a communicator and cache its value
271
272 auto iter = table_.find(connect);
273 if (iter.good() && iter.val() == -1)
274 {
275 iter.val() = createCommunicator(connect);
276 }
277 }
278
279 // Restore communicator settings
280 UPstream::commWarn(oldWarnComm);
281 UPstream::commWorld(oldWorldComm);
282 }
283
284 if (debug)
285 {
286 printDOT(Info, allConnections);
287 }
288}
289
290
291bool Foam::multiWorldConnections::addConnectionById(const label otherWorld)
292{
293 // The worlds (sorted)
294 edge worlds(worldPair(otherWorld));
295
296 if (!worlds.good())
297 {
298 return false;
299 }
300
301 const bool added = table_.insert(worlds, -1);
302
303 Pout<< (added ? "Add" : "Existing") << " connection from "
304 << UPstream::myWorld() << " to " << otherWorld << nl;
305
306 return added;
307}
308
309
311{
312 // The worlds (sorted)
313 edge worlds(worldPair(otherWorld));
314
315 if (!worlds.good())
316 {
317 return false;
318 }
319
320 const bool added = table_.insert(worlds, -1);
321
322 Pout<< (added ? "Add" : "Existing") << " connection from "
323 << UPstream::myWorld() << " to " << otherWorld << nl;
324
325 return added;
326}
327
328
330(
331 const label otherWorldID
332) const
333{
334 // Default: use local world
335 label comm = UPstream::worldComm;
336
337 // The communication worlds (sorted)
338 edge worlds(worldPair(otherWorldID));
339
340 if (!worlds.good())
341 {
342 return comm;
343 }
344
345 const auto iter = table_.cfind(worlds);
346
347 if (!iter.good())
348 {
350 << "No connection registered for worlds " << worlds
351 << exit(FatalError);
352 }
353
354 // Get cached value, or allocate ALL known communicators
355 comm = iter.val();
356
357 if (comm == -1)
358 {
359 // Need new communicator(s)
360 const_cast<multiWorldConnections&>(*this).createComms();
361
362 // Retrieve from table cache
363 comm = table_.lookup(worlds, UPstream::worldComm);
364 }
365
366 return comm;
367}
368
369
371(
372 const word& otherWorld
373) const
374{
375 // Default: use local world
376 label comm = UPstream::worldComm;
377
378 // The communication worlds (sorted)
379 edge worlds(worldPair(otherWorld));
380
381 if (!worlds.good())
382 {
383 return comm;
384 }
385
386 const auto iter = table_.cfind(worlds);
387
388 if (!iter.good())
389 {
391 << "No connection registered for worlds " << worlds
392 << exit(FatalError);
393 }
394
395 // Get cached value, or allocate ALL known communicators
396 comm = iter.val();
397
398 if (comm == -1)
399 {
400 // Need new communicator(s)
401 const_cast<multiWorldConnections&>(*this).createComms();
402
403 // Retrieve from table cache
404 comm = table_.lookup(worlds, UPstream::worldComm);
405 }
406
407 return comm;
408}
409
410
412{
413 labelList list(table_.size());
414
415 if (list.empty())
416 {
417 // Default: use local world
418 list.resize(1, UPstream::worldComm);
419 }
420 else
421 {
422 forAllConstIters(table_, iter)
423 {
424 if (iter.val() == -1)
425 {
426 // Need new communicator(s)
427 const_cast<multiWorldConnections&>(*this).createComms();
428 break;
429 }
430 }
431
432 // Retrieve values from table cache
433 label i = 0;
434
435 forAllConstIters(table_, iter)
436 {
437 list[i] = iter.val();
438 ++i;
439 }
440
441 Foam::sort(list); // Consistent order!
442 }
443
444 return list;
445}
446
447
448// ************************************************************************* //
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
Map from edge (expressed as its endpoints) to value. Hashing (and ==) on an edge is symmetric.
Definition edgeHashes.H:59
Foam::List< Key > sortedToc(const Compare &comp) const
Definition HashTable.C:168
const T & lookup(const Key &key, const T &deflt) const
Return hashed entry if it exists, or return the given default.
Definition HashTableI.H:222
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition HashTableI.H:152
void resize(const label len)
Adjust allocated size of list.
Definition ListI.H:153
static FOAM_NO_DANGLING_REFERENCE const multiWorldConnections & New(const Time &mesh, Args &&... args)
virtual void indent() override
Add indentation characters.
Definition OSstream.C:294
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
virtual Ostream & endBlock()
Write end block group.
Definition Ostream.C:108
virtual Ostream & beginBlock(const keyType &kw)
Write begin block group with the given name.
Definition Ostream.C:90
const T & first() const noexcept
Access the first element.
Definition Pair.H:137
static void combineReduce(T &value, CombineOp cop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce) applying cop to inplace combine value from different processors.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition Time.H:75
bool empty() const noexcept
True if List is empty (ie, size() is zero).
Definition UList.H:701
label find(const T &val) const
Find index of the first occurrence of the value.
Definition UList.C:160
static label commWorld() noexcept
Communicator for all ranks (respecting any local worlds).
Definition UPstream.H:1101
static bool parRun(const bool on) noexcept
Set as parallel run on/off.
Definition UPstream.H:1669
static const labelList & worldIDs() noexcept
The indices into allWorlds for all processes.
Definition UPstream.H:1830
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition UPstream.H:1069
static label myWorldID()
My worldID.
Definition UPstream.H:1838
static label commWarn(const label communicator) noexcept
Alter communicator debugging setting. Warns for use of any communicator differing from specified....
Definition UPstream.H:1122
static const wordList & allWorlds() noexcept
All worlds.
Definition UPstream.H:1822
static constexpr int commGlobal() noexcept
Communicator for all ranks, irrespective of any local worlds.
Definition UPstream.H:1081
static label newCommunicator(const label parent, const labelRange &subRanks, const bool withComponents=true)
Create new communicator with sub-ranks on the parent communicator.
Definition UPstream.C:272
static bool & parRun() noexcept
Test if this a parallel run.
Definition UPstream.H:1681
static const word & myWorld()
My world.
Definition UPstream.H:1846
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
bool good() const noexcept
True if the vertices are unique and non-negative.
Definition edgeI.H:116
static edge sorted(label from, label to)
Create (in ascending order) from two vertex labels.
Definition edgeI.H:32
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.
void createComms()
Create all communicators. Low-level, not normally called directly.
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.
multiWorldConnections(const Time &runTime)
Construct.
static const multiWorldConnections & New(const Time &runTime)
Access mesh object.
A class for handling words, derived from Foam::string.
Definition word.H:66
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
OBJstream os(runTime.globalPath()/outputName)
Namespace for handling debugging switches.
Definition debug.C:45
Namespace for OpenFOAM.
List< label > labelList
A List of labels.
Definition List.H:62
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:519
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition FlatOutput.H:217
void sort(UList< T > &list)
Sort the list.
Definition UList.C:283
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...
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
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
volScalarField & b
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
Definition stdFoam.H:235
void operator()(EdgeMap< unsigned > &a, const EdgeMap< unsigned > &b) const