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-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 
29 #include "Pstream.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
33 namespace Foam
34 {
35  defineTypeNameAndDebug(multiWorldConnections, 0);
36 }
37 
38 
39 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
40 
41 namespace 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 
58 static 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 
99 Foam::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 
119 Foam::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 
143 Foam::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 :
185 {}
186 
187 
188 // * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
189 
192 {
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 
291 bool 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 // ************************************************************************* //
static const word & myWorld()
My world.
Definition: UPstream.H:1177
label getCommById(const label otherWorld) const
Get communicator for myWorld to other world connection by ID.
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
const T & first() const noexcept
Access the first element.
Definition: Pair.H:137
void operator()(EdgeMap< unsigned > &a, const EdgeMap< unsigned > &b) const
static const labelList & worldIDs() noexcept
The indices into allWorlds for all processes.
Definition: UPstream.H:1161
static void printDOT(Ostream &os, const EdgeMap< unsigned > &connections)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:153
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:608
Centralized handling of multi-world MPI connections.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
static const multiWorldConnections & New(const Time &runTime)
Access mesh object.
engineTime & runTime
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:1061
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
labelList comms() const
Get communicators used for myWorld to other worlds in sorted order.
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:421
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
bool insert(const edge &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
bool empty() const noexcept
True if no world-to-world connections are defined.
static label commWarn(const label communicator) noexcept
Alter communicator debugging setting. Warns for use of any communicator differing from specified...
Definition: UPstream.H:461
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
A class for handling words, derived from Foam::string.
Definition: word.H:63
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
static void combineReduce(T &value, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce) applying cop to inplace combine value from different processors...
virtual Ostream & endBlock()
Write end block group.
Definition: Ostream.C:108
multiWorldConnections(const Time &runTime)
Construct.
label find(const T &val) const
Find index of the first occurrence of the value.
Definition: UList.C:173
const direction noexcept
Definition: Scalar.H:258
bool good() const noexcept
True if the vertices are unique and non-negative.
Definition: edgeI.H:100
label getCommByName(const word &otherWorld) const
Get communicator for myWorld to other world connection by NAME.
int debug
Static debugging option.
Map from edge (expressed as its endpoints) to value. Hashing (and ==) on an edge is symmetric...
Definition: edgeHashes.H:56
OBJstream os(runTime.globalPath()/outputName)
defineTypeNameAndDebug(combustionModel, 0)
static label commWorld() noexcept
Communicator for all ranks (respecting any local worlds)
Definition: UPstream.H:441
label size() const noexcept
Number of world-to-world connections defined.
bool addConnectionByName(const word &otherWorld)
Define a connection from myWorld to other world by NAME.
virtual void indent() override
Add indentation characters.
Definition: OSstream.C:285
List< edge > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:163
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual Ostream & beginBlock(const keyType &kw)
Write begin block group with the given name.
Definition: Ostream.C:90
void createComms()
Create all communicators. Low-level, not normally called directly.
List< label > labelList
A List of labels.
Definition: List.H:62
static const wordList & allWorlds() noexcept
All worlds.
Definition: UPstream.H:1153
bool addConnectionById(const label otherWorld)
Define a connection from myWorld to other world by ID.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
static label allocateCommunicator(const label parent, const labelRange &subRanks, const bool withComponents=true)
Allocate new communicator with contiguous sub-ranks on the parent communicator.
Definition: UPstream.C:260
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
static label myWorldID()
My worldID.
Definition: UPstream.H:1169
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:225
static constexpr label commGlobal() noexcept
Communicator for all ranks, irrespective of any local worlds.
Definition: UPstream.H:431