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 -------------------------------------------------------------------------------
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 {
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.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 {
194 }
195 
196 
197 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
198 
200 {}
201 
202 
203 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
204 
206 {
207  return table_.empty();
208 }
209 
210 
211 Foam::label Foam::multiWorldConnections::size() const noexcept
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);
239  Pstream::worldComm = 0;
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
432  list.resize(1, UPstream::worldComm);
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 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::UPstream::warnComm
static label warnComm
Debugging: warn for use of any communicator differing from warnComm.
Definition: UPstream.H:296
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:67
runTime
engineTime & runTime
Definition: createEngineTime.H:13
Foam::Time
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:73
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::List::resize
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:139
Foam::worldConnectBitOrEq
Definition: multiWorldConnectionsObject.C:46
Foam::multiWorldConnections::addConnectionById
bool addConnectionById(const label otherWorld)
Define a connection from myWorld to other world by ID.
Definition: multiWorldConnectionsObject.C:305
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:63
Foam::HashTable< T, edge, Hash< edge > >::insert
bool insert(const edge &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::Ostream::beginBlock
virtual Ostream & beginBlock(const keyType &kw)
Write begin block group with the given name.
Definition: Ostream.C:91
Foam::multiWorldConnections::getCommById
label getCommById(const label otherWorld) const
Get communicator for myWorld to other world connection by ID.
Definition: multiWorldConnectionsObject.C:344
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
multiWorldConnectionsObject.H
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::UPstream::allWorlds
static const wordList & allWorlds() noexcept
All worlds.
Definition: UPstream.H:483
Foam::multiWorldConnections::~multiWorldConnections
~multiWorldConnections()
Destructor.
Definition: multiWorldConnectionsObject.C:199
Foam::multiWorldConnections::New
static const multiWorldConnections & New(const Time &runTime)
Access mesh object.
Definition: multiWorldConnectionsObject.C:191
Foam::multiWorldConnections::comms
labelList comms() const
Get communicators used for myWorld to other worlds in sorted order.
Definition: multiWorldConnectionsObject.C:425
Foam::UPstream::allocateCommunicator
static label allocateCommunicator(const label parent, const labelList &subRanks, const bool doPstream=true)
Allocate a new communicator.
Definition: UPstream.C:108
Foam::UPstream::myWorldID
static label myWorldID()
My worldID.
Definition: UPstream.H:495
Foam::constant::physicoChemical::b
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
Foam::multiWorldConnections::size
label size() const noexcept
Number of world-to-world connections defined.
Definition: multiWorldConnectionsObject.C:211
Foam::multiWorldConnections::getCommByName
label getCommByName(const word &otherWorld) const
Get communicator for myWorld to other world connection by NAME.
Definition: multiWorldConnectionsObject.C:385
Foam::sort
void sort(UList< T > &a)
Definition: UList.C:261
Foam::UPstream::worldIDs
static const labelList & worldIDs() noexcept
worldID (index in allWorlds) of all processes
Definition: UPstream.H:489
Foam::UPstream::myWorld
static const word & myWorld()
My world.
Definition: UPstream.H:501
Foam::Ostream::endBlock
virtual Ostream & endBlock()
Write end block group.
Definition: Ostream.C:109
Foam::FatalError
error FatalError
os
OBJstream os(runTime.globalPath()/outputName)
Foam::HashTable< T, edge, Hash< edge > >::lookup
const T & lookup(const edge &key, const T &deflt) const
Return hashed entry if it exists, or return the given default.
Definition: HashTableI.H:224
Foam::multiWorldConnections::createComms
void createComms()
Definition: multiWorldConnectionsObject.C:217
Pstream.H
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::flatOutput
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:216
Foam::Pstream::combineGather
static void combineGather(const List< commsStruct > &comms, T &Value, const CombineOp &cop, const int tag, const label comm)
Definition: combineGatherScatter.C:48
Foam::HashTable< T, edge, Hash< edge > >::sortedToc
List< edge > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:136
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::multiWorldConnections::empty
bool empty() const noexcept
True if no world-to-world connections are defined.
Definition: multiWorldConnectionsObject.C:205
Foam::New
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
Global function forwards to reuseTmpDimensionedField::New.
Definition: DimensionedFieldReuseFunctions.H:105
Foam::Perr
prefixOSstream Perr
OSstream wrapped stderr (std::cerr) with parallel prefix.
Foam::OSstream::indent
virtual void indent()
Add indentation characters.
Definition: OSstream.C:266
Foam::EdgeMap
Map from edge (expressed as its endpoints) to value. For easier forward declaration it is currently i...
Definition: EdgeMap.H:51
Foam::worldConnectBitOrEq::operator()
void operator()(EdgeMap< unsigned > &a, const EdgeMap< unsigned > &b) const
Definition: multiWorldConnectionsObject.C:48
Foam::multiWorldConnections
Centralized handling of multi-world MPI connections.
Definition: multiWorldConnectionsObject.H:55
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::nl
constexpr char nl
Definition: Ostream.H:404
forAllConstIters
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
Foam::UPstream::worldComm
static label worldComm
Default communicator (all processors)
Definition: UPstream.H:293
Foam::UPstream::parRun
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
Foam::List< label >
Foam::multiWorldConnections::multiWorldConnections
multiWorldConnections(const Time &runTime)
Construct.
Definition: multiWorldConnectionsObject.C:182
Foam::printDOT
static void printDOT(Ostream &os, const EdgeMap< unsigned > &connections)
Definition: multiWorldConnectionsObject.C:58
Foam::MeshObject< Time, TopologicalMeshObject, multiWorldConnections >
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::edge::valid
bool valid() const
Return true if the vertices are unique and non-negative.
Definition: edgeI.H:130
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::multiWorldConnections::addConnectionByName
bool addConnectionByName(const word &otherWorld)
Define a connection from myWorld to other world by NAME.
Definition: multiWorldConnectionsObject.C:324