MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
communication.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#ifndef MFEM_COMMUNICATION
13#define MFEM_COMMUNICATION
14
15#include "../config/config.hpp"
16
17#ifdef MFEM_USE_MPI
18
19#include "array.hpp"
20#include "table.hpp"
21#include "sets.hpp"
22#include "globals.hpp"
23#include <mpi.h>
24#include <cstdint>
25#include <type_traits>
26
27// can't directly use MPI_CXX_BOOL because Microsoft's MPI implementation
28// doesn't include MPI_CXX_BOOL. Fallback to MPI_C_BOOL if unavailable.
29#ifdef MPI_CXX_BOOL
30#define MFEM_MPI_CXX_BOOL MPI_CXX_BOOL
31#else
32#define MFEM_MPI_CXX_BOOL MPI_C_BOOL
33#endif
34
35namespace mfem
36{
37
38/** @brief A simple singleton class that calls MPI_Init() at construction and
39 MPI_Finalize() at destruction. It also provides easy access to
40 MPI_COMM_WORLD's rank and size. */
41class Mpi
42{
43public:
44 /// Singleton creation with Mpi::Init(argc, argv).
45 static void Init(int &argc, char **&argv,
46 int required = default_thread_required,
47 int *provided = nullptr)
48 { Init(&argc, &argv, required, provided); }
49 /// Singleton creation with Mpi::Init().
50 static void Init(int *argc = nullptr, char ***argv = nullptr,
51 int required = default_thread_required,
52 int *provided = nullptr)
53 {
54 MFEM_VERIFY(!IsInitialized(), "MPI already initialized!");
55 if (required == MPI_THREAD_SINGLE)
56 {
57 int mpi_err = MPI_Init(argc, argv);
58 MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
59 if (provided) { *provided = MPI_THREAD_SINGLE; }
60 }
61 else
62 {
63 int mpi_provided;
64 int mpi_err = MPI_Init_thread(argc, argv, required, &mpi_provided);
65 MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
66 if (provided) { *provided = mpi_provided; }
67 }
68 // The Mpi singleton object below needs to be created after MPI_Init() for
69 // some MPI implementations.
70 Singleton();
71 }
72 /// Finalize MPI (if it has been initialized and not yet already finalized).
73 static void Finalize()
74 {
75 if (IsInitialized() && !IsFinalized()) { MPI_Finalize(); }
76 }
77 /// Return true if MPI has been initialized.
78 static bool IsInitialized()
79 {
80 int mpi_is_initialized;
81 int mpi_err = MPI_Initialized(&mpi_is_initialized);
82 return (mpi_err == MPI_SUCCESS) && mpi_is_initialized;
83 }
84 /// Return true if MPI has been finalized.
85 static bool IsFinalized()
86 {
87 int mpi_is_finalized;
88 int mpi_err = MPI_Finalized(&mpi_is_finalized);
89 return (mpi_err == MPI_SUCCESS) && mpi_is_finalized;
90 }
91 /// Return the MPI rank in MPI_COMM_WORLD.
92 static int WorldRank()
93 {
94 int world_rank;
95 MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
96 return world_rank;
97 }
98 /// Return the size of MPI_COMM_WORLD.
99 static int WorldSize()
100 {
101 int world_size;
102 MPI_Comm_size(MPI_COMM_WORLD, &world_size);
103 return world_size;
104 }
105 /// Return true if the rank in MPI_COMM_WORLD is zero.
106 static bool Root() { return WorldRank() == 0; }
107 /// Default level of thread support for MPI_Init_thread.
108 static MFEM_EXPORT int default_thread_required;
109private:
110 /// Initialize the Mpi singleton.
111 static Mpi &Singleton()
112 {
113 static Mpi mpi;
114 return mpi;
115 }
116 /// Finalize MPI.
117 ~Mpi() { Finalize(); }
118 /// Prevent direct construction of objects of this class.
119 Mpi() {}
120};
121
122/** @brief A simple convenience class based on the Mpi singleton class above.
123 Preserved for backward compatibility. New code should use Mpi::Init() and
124 other Mpi methods instead. */
126{
127public:
129 MPI_Session(int &argc, char **&argv) { Mpi::Init(argc, argv); }
130 /// Return MPI_COMM_WORLD's rank.
131 int WorldRank() const { return Mpi::WorldRank(); }
132 /// Return MPI_COMM_WORLD's size.
133 int WorldSize() const { return Mpi::WorldSize(); }
134 /// Return true if WorldRank() == 0.
135 bool Root() const { return Mpi::Root(); }
136};
137
138
139/** The shared entities (e.g. vertices, faces and edges) are split into groups,
140 each group determined by the set of participating processors. They are
141 numbered locally in lproc. Assumptions:
142 - group 0 is the 'local' group
143 - groupmaster_lproc[0] = 0
144 - lproc_proc[0] = MyRank */
146{
147private:
148 MPI_Comm MyComm;
149
150 /// Neighbor ids (lproc) in each group.
151 Table group_lproc;
152 /// Master neighbor id for each group.
153 Array<int> groupmaster_lproc;
154 /// MPI rank of each neighbor.
155 Array<int> lproc_proc;
156 /// Group --> Group number in the master.
157 Array<int> group_mgroup;
158
159 void ProcToLProc();
160
161public:
162 /// Constructor with the MPI communicator = 0.
163 GroupTopology() : MyComm(0) {}
164
165 /// Constructor given the MPI communicator 'comm'.
166 GroupTopology(MPI_Comm comm) { MyComm = comm; }
167
168 /// Copy constructor
169 GroupTopology(const GroupTopology &gt);
170
171 /// Set the MPI communicator to 'comm'.
172 void SetComm(MPI_Comm comm) { MyComm = comm; }
173
174 /// Return the MPI communicator.
175 MPI_Comm GetComm() const { return MyComm; }
176
177 /// Return the MPI rank within this object's communicator.
178 int MyRank() const { int r; MPI_Comm_rank(MyComm, &r); return r; }
179
180 /// Return the number of MPI ranks within this object's communicator.
181 int NRanks() const { int s; MPI_Comm_size(MyComm, &s); return s; }
182
183 /// Set up the group topology given the list of sets of shared entities.
184 void Create(ListOfIntegerSets &groups, int mpitag);
185
186 /// Return the number of groups.
187 int NGroups() const { return group_lproc.Size(); }
188
189 /// Return the number of neighbors including the local processor.
190 int GetNumNeighbors() const { return lproc_proc.Size(); }
191
192 /// Return the MPI rank of neighbor 'i'.
193 int GetNeighborRank(int i) const { return lproc_proc[i]; }
194
195 /// Return true if I am master for group 'g'.
196 bool IAmMaster(int g) const { return (groupmaster_lproc[g] == 0); }
197
198 /** @brief Return the neighbor index of the group master for a given group.
199 Neighbor 0 is the local processor. */
200 int GetGroupMaster(int g) const { return groupmaster_lproc[g]; }
201
202 /// Return the rank of the group master for group 'g'.
203 int GetGroupMasterRank(int g) const
204 { return lproc_proc[groupmaster_lproc[g]]; }
205
206 /// Return the group number in the master for group 'g'.
207 int GetGroupMasterGroup(int g) const { return group_mgroup[g]; }
208
209 /// Get the number of processors in a group
210 int GetGroupSize(int g) const { return group_lproc.RowSize(g); }
211
212 /** @brief Return a pointer to a list of neighbors for a given group.
213 Neighbor 0 is the local processor */
214 const int *GetGroup(int g) const { return group_lproc.GetRow(g); }
215
216 /// Save the data in a stream.
217 void Save(std::ostream &out) const;
218
219 /// Load the data from a stream.
220 void Load(std::istream &in);
221
222 /// Copy the internal data to the external 'copy'.
223 void Copy(GroupTopology & copy) const;
224
225 /// Swap the internal data with another @a GroupTopology object.
226 void Swap(GroupTopology &other);
227
228 virtual ~GroupTopology() {}
229};
230
231
232// Forward declaration
233class DeviceGroupCommunicator;
234
235
236/** @brief Communicator performing operations within groups defined by a
237 GroupTopology with arbitrary-size data associated with each group. */
239{
241
242public:
243 /// Communication mode.
244 enum Mode
245 {
246 byGroup, ///< Communications are performed one group at a time.
247 byNeighbor /**< Communications are performed one neighbor at a time,
248 aggregating over groups. */
249 };
250
251protected:
255 Table group_ltdof; // only for groups for which this processor is master.
258 MPI_Request *requests;
259 // MPI_Status *statuses;
260 // comm_lock: 0 - no lock, 1 - locked for Bcast, 2 - locked for Reduce
261 mutable int comm_lock;
262 mutable int num_requests;
263 mutable void (*reduce_op)(); // used when 'comm_lock' is 2
265 int *buf_offsets; // size = max(number of groups, number of neighbors)
271
272public:
273 /// Construct a GroupCommunicator object.
274 /** The object must be initialized before it can be used to perform any
275 operations. To initialize the object, either
276 - call Create() or
277 - initialize the Table reference returned by GroupLDofTable() and then
278 call Finalize().
279 */
281
282 /** @brief Initialize the communicator from a local-dof to group map.
283 Finalize() is called internally. */
284 void Create(const Array<int> &ldof_group);
285
286 /** @brief Fill-in the returned Table reference to initialize the
287 GroupCommunicator then call Finalize(). */
289
290 /// Read-only access to group-ldof Table.
291 const Table &GroupLDofTable() const { return group_ldof; }
292
293 /// Allocate internal buffers after the GroupLDofTable is defined
294 void Finalize();
295
296 /// Initialize the internal group_ltdof Table.
297 /** This method must be called before performing operations that use local
298 data layout 2, see CopyGroupToBuffer() for layout descriptions. */
299 void SetLTDofTable(const Array<int> &ldof_ltdof);
300
301 /// Get a const reference to the associated GroupTopology object
302 const GroupTopology &GetGroupTopology() const { return gtopo; }
303
304 /// Dofs to be sent (during Bcast) to communication neighbors
305 void GetNeighborLTDofTable(Table &nbr_ltdof) const;
306
307 /// Dofs to be received (during Bcast) from communication neighbors
308 void GetNeighborLDofTable(Table &nbr_ldof) const;
309
310 /** @brief Return the device communicator, 'device_gc', constructing it if
311 it was not already constructed. */
313
314 /** @brief Data structure on which we define reduce operations.
315 The data is associated with (and the operation is performed on) one
316 group at a time. */
317 template <class T> struct OpData
318 {
319 int nldofs, nb;
320 const int *ldofs;
321 T *ldata, *buf;
322 };
323
324 /** @brief Copy the entries corresponding to the group @a group from the
325 local array @a ldata to the buffer @a buf. */
326 /** The @a layout of the local array can be:
327 - 0 - @a ldata is an array on all ldofs: copied indices:
328 `{ J[j] : I[group] <= j < I[group+1] }` where `I,J=group_ldof.{I,J}`
329 - 1 - @a ldata is an array on the shared ldofs: copied indices:
330 `{ j : I[group] <= j < I[group+1] }` where `I,J=group_ldof.{I,J}`
331 - 2 - @a ldata is an array on the true ldofs, ltdofs: copied indices:
332 `{ J[j] : I[group] <= j < I[group+1] }` where `I,J=group_ltdof.{I,J}`.
333 @returns The pointer @a buf plus the number of elements in the group. */
334 template <class T>
335 T *CopyGroupToBuffer(const T *ldata, T *buf, int group, int layout) const;
336
337 /** @brief Copy the entries corresponding to the group @a group from the
338 buffer @a buf to the local array @a ldata. */
339 /** For a description of @a layout, see CopyGroupToBuffer().
340 @returns The pointer @a buf plus the number of elements in the group. */
341 template <class T>
342 const T *CopyGroupFromBuffer(const T *buf, T *ldata, int group,
343 int layout) const;
344
345 /** @brief Perform the reduction operation @a Op to the entries of group
346 @a group using the values from the buffer @a buf and the values from the
347 local array @a ldata, saving the result in the latter. */
348 /** For a description of @a layout, see CopyGroupToBuffer().
349 @returns The pointer @a buf plus the number of elements in the group. */
350 template <class T>
351 const T *ReduceGroupFromBuffer(const T *buf, T *ldata, int group,
352 int layout, void (*Op)(OpData<T>)) const;
353
354 /** @brief Begin a broadcast within each group where the master is the root,
355 host version.
356
357 @param[in,out] ldata Input L-vector data; in some cases it is used as a
358 receive buffer, so its type is not const. It must
359 be a host pointer.
360 @param[in] layout For a description, see CopyGroupToBuffer().
361
362 This method performs the operation on host. */
363 template <class T> void BcastBegin(T *ldata, int layout) const;
364
365 /** @brief Begin a broadcast within each group where the master is the root,
366 device version.
367
368 @param[in,out] ldata Input L-vector data; in some cases it is used as a
369 receive buffer, so its type is not const.
370 @param[in] layout For a description, see CopyGroupToBuffer().
371
372 This method performs the operation on device if the device flag of
373 @a ldata is set. However, not all communication modes and layouts are
374 supported on device yet. In such cases, the operation is performed on
375 host. */
376 template <class T> void BcastBegin(Array<T> &ldata, int layout) const;
377
378 /** @brief Finalize a broadcast started with the host version of
379 BcastBegin().
380
381 @param[out] ldata Output L-vector data. It must be a host pointer.
382 @param[in] layout Output data layout; one of:
383 - 0: @a ldata is an array on all ldofs; the input
384 layout should be either 0 or 2,
385 - 1: @a ldata is the same array as given to
386 BcastBegin(); the input layout should be 1.
387 .
388 For a description of the layouts, see
389 CopyGroupToBuffer().
390
391 This method performs the operation on host. */
392 template <class T> void BcastEnd(T *ldata, int layout) const;
393
394 /** @brief Finalize a broadcast started with the device version of
395 BcastBegin().
396
397 @param[out] ldata Output L-vector data.
398 @param[in] layout Output data layout; one of:
399 - 0: @a ldata is an array on all ldofs; the input
400 layout should be either 0 or 2,
401 - 1: @a ldata is the same array as given to
402 BcastBegin(); the input layout should be 1.
403 .
404 For a description of the layouts, see
405 CopyGroupToBuffer().
406
407 This method performs the operation on device if the device flag of
408 @a ldata is set. However, not all communication modes and layouts are
409 supported on device yet. In such cases, the operation is performed on
410 host.
411
412 It is expected that the device flag of @a ldata is the same as the device
413 flag of the data array provided to BcastBegin(). */
414 template <class T> void BcastEnd(Array<T> &ldata, int layout) const;
415
416 /** @brief Broadcast within each group where the master is the root.
417
418 The data @a layout can be either 0 or 1.
419
420 For a description of @a layout, see CopyGroupToBuffer().
421
422 This method performs the operation on host and expects @a ldata to be a
423 host pointer. */
424 template <class T> void Bcast(T *ldata, int layout) const
425 {
426 BcastBegin(ldata, layout);
427 BcastEnd(ldata, layout);
428 }
429
430 /// Broadcast within each group where the master is the root, host version.
431 /** The implicit data layout is 0, i.e. the @a ldata array is an L-dof array.
432
433 This method performs the operation on host and expects @a ldata to be a
434 host pointer. */
435 template <class T> void Bcast(T *ldata) const { Bcast(ldata, 0); }
436
437 /// Broadcast within each group where the master is the root, device version.
438 /** The implicit data layout is 0, i.e. the @a ldata array is an L-dof array.
439
440 This method performs the operation on device. However, not all
441 communication modes are supported on device yet. In such cases, the
442 operation is performed on host. */
443 template <class T> void Bcast(Array<T> &ldata) const
444 {
445 BcastBegin(ldata, 0);
446 BcastEnd(ldata, 0);
447 }
448
449 /** @brief Begin reduction operation within each group where the master is
450 the root, host version.
451
452 The input data layout is an array on all ldofs, i.e. layout 0, see
453 CopyGroupToBuffer().
454
455 The reduce operation will be specified when calling ReduceEnd(). This
456 method is instantiated for int, double, and float.
457
458 This method performs the operation on host and expects @a ldata to be a
459 host pointer. */
460 template <class T> void ReduceBegin(const T *ldata) const;
461
462 /** @brief Begin reduction operation within each group where the master is
463 the root, device version.
464
465 The input data layout is an array on all ldofs, i.e. layout 0, see
466 CopyGroupToBuffer().
467
468 Generally, the reduce operation will be specified when calling
469 ReduceEnd(), however, if the reduction operation is not supported on
470 device, it must be given to this call as the optional second argument
471 @a Op. This method is instantiated for int, double, and float.
472
473 This method performs the operation on device if the device flag of
474 @a ldata is set. However, not all communication modes are supported on
475 device yet. In such cases, the operation is performed on host. */
476 template <class T> void ReduceBegin(const Array<T> &ldata,
477 void (*Op)(OpData<T>) = nullptr) const;
478
479 /** @brief Finalize reduction operation started with the host version of
480 ReduceBegin().
481
482 The output data @a layout can be either 0 or 2, see CopyGroupToBuffer().
483
484 The reduce operation is given by the third argument (see below for list
485 of the supported operations.) This method is instantiated for int,
486 double, and float.
487
488 This method performs the operation on host and expects @a ldata to be a
489 host pointer.
490
491 @note If the output data layout is 2, then the data from the @a ldata
492 array passed to this call is used in the reduction operation, instead of
493 the data from the @a ldata array passed to ReduceBegin(). Therefore, the
494 data for master-groups has to be identical in both arrays.
495 */
496 template <class T> void ReduceEnd(T *ldata, int layout,
497 void (*Op)(OpData<T>)) const;
498
499 /** @brief Finalize reduction operation started with the device version of
500 ReduceBegin().
501
502 The output data @a layout can be either 0 or 2, see CopyGroupToBuffer().
503
504 The reduce operation is given by the third argument (see below for list
505 of the supported operations.) This method is instantiated for int,
506 double, and float.
507
508 This method performs the operation on device if the device flag of
509 @a ldata is set. However, not all communication modes and layouts are
510 supported on device yet. In such cases, the operation is performed on
511 host.
512
513 It is expected that the device flag of @a ldata is the same as the device
514 flag of the data array provided to ReduceBegin().
515
516 @note If the output data layout is 2, then the data from the @a ldata
517 array passed to this call is used in the reduction operation, instead of
518 the data from the @a ldata array passed to ReduceBegin(). Therefore, the
519 data for master-groups has to be identical in both arrays.
520 */
521 template <class T> void ReduceEnd(Array<T> &ldata, int layout,
522 void (*Op)(OpData<T>)) const;
523
524 /** @brief Reduce within each group where the master is the root, host
525 version.
526
527 The implicit data layout is 0, i.e. the @a ldata array is an L-dof array.
528
529 The reduce operation is given by the second argument (see below for list
530 of the supported operations.)
531
532 This method performs the operation on host and expects @a ldata to be a
533 host pointer. */
534 template <class T> void Reduce(T *ldata, void (*Op)(OpData<T>)) const
535 {
536 ReduceBegin(ldata);
537 ReduceEnd(ldata, 0, Op);
538 }
539
540 /** @brief Reduce within each group where the master is the root, device
541 version.
542
543 The implicit data layout is 0, i.e. the @a ldata array is an L-dof array.
544
545 The reduce operation is given by the second argument (see below for list
546 of the supported operations.)
547
548 This method performs the operation on device. However, not all
549 communication modes are supported on device yet. In such cases, the
550 operation is performed on host. */
551 template <class T> void Reduce(Array<T> &ldata, void (*Op)(OpData<T>)) const
552 {
553 ReduceBegin(ldata, Op);
554 ReduceEnd(ldata, 0, Op);
555 }
556
557 /// Reduce operation Sum, instantiated for int, double and float
558 template <class T> static void Sum(OpData<T>);
559 /// Reduce operation Min, instantiated for int, double and float
560 template <class T> static void Min(OpData<T>);
561 /// Reduce operation Max, instantiated for int, double and float
562 template <class T> static void Max(OpData<T>);
563 /// Reduce operation bitwise OR, instantiated for int only
564 template <class T> static void BitOR(OpData<T>);
565 /// Reduce operation selecting the signed value with the largest absolute
566 /// value, instantiated for int, double and float. The result keeps its sign;
567 /// it is not the non-negative absolute value. Equal-magnitude ties are
568 /// broken deterministically toward the more positive value, so opposite-sign
569 /// ties resolve to the positive one regardless of accumulation order.
570 template <class T> static void MaxAbs(OpData<T>);
571
572 /** @brief Finalize reduction operation started with ReduceBegin(), but only
573 apply the reduction to DOFs marked in the marker array.
574
575 @note The reduction is carried out in the signed type @a T, so the result
576 is signed even for bitwise operations.
577 */
578 template <class T>
579 void ReduceMarked(T *ldata, const Array<int> &marker, int layout,
580 void (*Op)(OpData<T>)) const;
581
582 /** @brief Reduce within each group where the master is the root, but only
583 for marked DOFs. */
584 template <class T>
585 void Reduce(T *ldata, const Array<int> &marker, void (*Op)(OpData<T>)) const
586 {
587 ReduceBegin(ldata);
588 ReduceMarked(ldata, marker, 0, Op);
589 }
590
591 /// Print information about the GroupCommunicator from all MPI ranks.
592 void PrintInfo(std::ostream &out = mfem::out) const;
593
594 /** @brief Destroy a GroupCommunicator object, deallocating internal data
595 structures and buffers. */
597};
598
599
600/** @brief Auxiliary class used by class GroupCommunicator implementing its
601 device (GPU) code paths for data passed as Array<T>. The operations are
602 performed on the configured mfem::Device and can use GPU-aware MPI, if
603 enabled for the device. */
605{
606public:
607 friend class GroupCommunicator;
608
609 /// Reduction operation applied at the receiving dofs.
610 enum class Op { Sum, Min, Max };
611
612 /// Construct a device communicator based on the GroupCommunicator @a gc_.
613 explicit DeviceGroupCommunicator(const GroupCommunicator &gc_);
614
615 /// Begin a group broadcast of the true-dof data @a x_tdof.
616 template <typename T>
617 void BcastBeginTDofs(Array<T> &x_tdof) const;
618
619 /// Begin a group broadcast of the local-dof data @a x_ldof.
620 template <typename T>
621 void BcastBeginLDofs(Array<T> &x_ldof) const;
622
623 /// Finalize a group broadcast into the local-dof data @a x_ldof.
624 template <typename T>
625 void BcastEndLDofs(Array<T> &x_ldof) const;
626
627 /// Begin a group reduction of the local-dof data @a x_ldof.
628 template <typename T>
629 void ReduceBeginLDofs(const Array<T> &x_ldof) const;
630
631 /** @brief Finalize a group reduction into the true-dof data @a x_tdof,
632 applying the reduction operation @a op. */
633 template <typename T>
634 void ReduceEndTDofs(Array<T> &x_tdof, Op op) const;
635
636 /** @brief Finalize a group reduction into the local-dof data @a x_ldof,
637 applying the reduction operation @a op. */
638 template <typename T>
639 void ReduceEndLDofs(Array<T> &x_ldof, Op op) const;
640
641 /** @brief Kernel: copy ltdofs from @a x_tdof to ldofs in @a x_ldof,
642 i.e. x_ldof[ltdof_ldof[i]] = x_tdof[i]. */
643 template <typename T>
644 void CopyTDofsToLDofs(const Array<T> &x_tdof, Array<T> &x_ldof) const;
645
646 /// Prolongate the true-dof data @a x_tdof to the local-dof data @a x_ldof.
647 template <typename T>
648 void Prolongate(const Array<T> &x_tdof, Array<T> &x_ldof) const;
649
650 /** @brief Transpose of Prolongate(): reduce the local-dof data @a x_ldof
651 into the true-dof data @a x_tdof, applying the operation @a op. */
652 template <typename T>
653 void ProlongateTranspose(const Array<T> &x_ldof,
654 Array<T> &x_tdof, Op op = Op::Sum) const;
655
656 /** @brief Kernel: copy owned ldofs from @a x_ldof to ltdofs in @a x_tdof,
657 i.e. x_tdof[i] = x_ldof[ltdof_ldof[i]]. */
658 template <typename T>
659 void Restrict(const Array<T> &x_ldof, Array<T> &x_tdof) const;
660
661 /** @brief Transpose of Restrict(): copy the true-dof data @a x_tdof into
662 the owned local dofs of @a x_ldof and set the remaining (external)
663 local dofs to zero. */
664 template <typename T>
665 void RestrictTranspose(const Array<T> &x_tdof, Array<T> &x_ldof) const;
666
667protected:
668 template <typename T>
669 void Exchange(const Array<T> &send_buf, const Array<int> &send_offsets,
670 Array<T> &recv_buf, const Array<int> &recv_offsets,
671 int tag) const;
672
673 template <typename T>
675 Array<T> &ext_buf) const;
676
677 template <typename T>
679 Array<T> &shr_buf) const;
680
681 // Kernel: copy ext. dofs from 'x_ldof' to 'ext_buf_t' - prepare for send.
682 // ext_buf_t[i] = x_ldof[ext_ldof[i]]
683 template <typename T>
684 void ReduceBeginCopy(const Array<T> &x_ldof, Array<T> &ext_buf_t) const;
685
686 // Kernel: assemble dofs from 'shr_buf_t' into to 'x_tdof' - after recv.
687 // x_tdof[shr_ltdof[i]] Op= shr_buf_t[i]
688 template <typename T>
689 void ReduceEndAssembleTDofs(const Array<T> &shr_buf_t,
690 Array<T> &x_tdof, Op op) const;
691
692 // Kernel: copy ltdofs from 'x_tdof' to 'shr_buf_t' - prepare for send.
693 // shr_buf_t[i] = x_tdof[shr_ltdof[i]]
694 template <typename T>
695 void BcastBeginCopyTDofs(const Array<T> &x_tdof, Array<T> &shr_buf_t) const;
696
697 // Kernel: copy ldofs from 'x_ldof' to 'shr_buf_t' - prepare for send.
698 // shr_buf_t[i] = x_ldof[shr_ldof[i]]
699 template <typename T>
700 void BcastBeginCopyLDofs(const Array<T> &x_ldof, Array<T> &shr_buf_t) const;
701
702 // Kernel: copy ext. dofs from 'ext_buf_t' to 'x_ldof' - after recv.
703 // x_ldof[ext_ldof[i]] = ext_buf_t[i]
704 template <typename T>
705 void BcastEndCopy(const Array<T> &ext_buf_t, Array<T> &x_ldof) const;
706
707 void WaitAll() const;
708
714 using buffer_max_type = int64_t;
717 mutable int num_requests;
718
719 template <class T> struct TypedBufferView
720 {
724 {
726 view.SetSize(storage.Size());
727 }
728
730 {
731 storage.GetMemory().CopyConvertPtr(view.GetMemory());
732 view.LoseData();
733 }
734 };
735};
736
737
738/// General MPI message tags used by MFEM
740{
742 291, /// ParFiniteElementSpace ParallelDerefinementMatrix and
743 /// ParDerefineMatrixOp
744};
745
747{
748 NEIGHBOR_ELEMENT_RANK_VM, ///< NeighborElementRankMessage
749 NEIGHBOR_ORDER_VM, ///< NeighborOrderMessage
750 NEIGHBOR_DEREFINEMENT_VM, ///< NeighborDerefinementMessage
751 NEIGHBOR_REFINEMENT_VM, ///< NeighborRefinementMessage
752 NEIGHBOR_PREFINEMENT_VM, ///< NeighborPRefinementMessage
753 NEIGHBOR_ROW_VM, ///< NeighborRowMessage
754 REBALANCE_VM, ///< RebalanceMessage
755 REBALANCE_DOF_VM, ///< RebalanceDofMessage
756};
757
758/// \brief Variable-length MPI message containing unspecific binary data.
759template<int Tag>
761{
762 std::string data;
763 MPI_Request send_request;
764
765 /** @brief Non-blocking send to processor 'rank'.
766 Returns immediately. Completion (as tested by MPI_Wait/Test) does not
767 mean the message was received -- it may be on its way or just buffered
768 locally. */
769 void Isend(int rank, MPI_Comm comm)
770 {
771 Encode(rank);
772 MPI_Isend((void*) data.data(), static_cast<int>(data.length()), MPI_BYTE, rank,
773 Tag, comm, &send_request);
774 }
775
776 /** @brief Non-blocking synchronous send to processor 'rank'.
777 Returns immediately. Completion (MPI_Wait/Test) means that the message
778 was received. */
779 void Issend(int rank, MPI_Comm comm)
780 {
781 Encode(rank);
782 MPI_Issend((void*) data.data(), static_cast<int>(data.length()), MPI_BYTE, rank,
783 Tag, comm, &send_request);
784 }
785
786 /// Helper to send all messages in a rank-to-message map container.
787 template<typename MapT>
788 static void IsendAll(MapT& rank_msg, MPI_Comm comm)
789 {
790 for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
791 {
792 it->second.Isend(it->first, comm);
793 }
794 }
795
796 /// Helper to wait for all messages in a map container to be sent.
797 template<typename MapT>
798 static void WaitAllSent(MapT& rank_msg)
799 {
800 for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
801 {
802 MPI_Wait(&it->second.send_request, MPI_STATUS_IGNORE);
803 it->second.Clear();
804 }
805 }
806
807 /** @brief Return true if all messages in the map container were sent,
808 otherwise return false, without waiting. */
809 template<typename MapT>
810 static bool TestAllSent(MapT& rank_msg)
811 {
812 for (auto it = rank_msg.begin(); it != rank_msg.end(); ++it)
813 {
814 VarMessage &msg = it->second;
815 if (msg.send_request != MPI_REQUEST_NULL)
816 {
817 int sent;
818 MPI_Test(&msg.send_request, &sent, MPI_STATUS_IGNORE);
819 if (!sent) { return false; }
820 msg.Clear();
821 }
822 }
823 return true;
824 }
825
826 /** @brief Blocking probe for incoming message of this type from any rank.
827 Returns the rank and message size. */
828 static void Probe(int &rank, int &size, MPI_Comm comm)
829 {
830 MPI_Status status;
831 MPI_Probe(MPI_ANY_SOURCE, Tag, comm, &status);
832 rank = status.MPI_SOURCE;
833 MPI_Get_count(&status, MPI_BYTE, &size);
834 }
835
836 /** @brief Non-blocking probe for incoming message of this type from any
837 rank. If there is an incoming message, returns true and sets 'rank' and
838 'size'. Otherwise returns false. */
839 static bool IProbe(int &rank, int &size, MPI_Comm comm)
840 {
841 int flag;
842 MPI_Status status;
843 MPI_Iprobe(MPI_ANY_SOURCE, Tag, comm, &flag, &status);
844 if (!flag) { return false; }
845
846 rank = status.MPI_SOURCE;
847 MPI_Get_count(&status, MPI_BYTE, &size);
848 return true;
849 }
850
851 /// Post-probe receive from processor 'rank' of message size 'size'.
852 void Recv(int rank, int size, MPI_Comm comm)
853 {
854 MFEM_ASSERT(size >= 0, "");
855 data.resize(size);
856 MPI_Status status;
857 MPI_Recv((void*) data.data(), size, MPI_BYTE, rank, Tag, comm, &status);
858#ifdef MFEM_DEBUG
859 int count;
860 MPI_Get_count(&status, MPI_BYTE, &count);
861 MFEM_VERIFY(count == size, "");
862#endif
863 Decode(rank);
864 }
865
866 /// Like Recv(), but throw away the message.
867 void RecvDrop(int rank, int size, MPI_Comm comm)
868 {
869 data.resize(size);
870 MPI_Status status;
871 MPI_Recv((void*) data.data(), size, MPI_BYTE, rank, Tag, comm, &status);
872 data.resize(0); // don't decode
873 }
874
875 /// Helper to receive all messages in a rank-to-message map container.
876 template<typename MapT>
877 static void RecvAll(MapT& rank_msg, MPI_Comm comm)
878 {
879 int recv_left = static_cast<int>(rank_msg.size());
880 while (recv_left > 0)
881 {
882 int rank, size;
883 Probe(rank, size, comm);
884 MFEM_ASSERT(rank_msg.find(rank) != rank_msg.end(), "Unexpected message"
885 " (tag " << Tag << ") from rank " << rank);
886 // NOTE: no guard against receiving two messages from the same rank
887 rank_msg[rank].Recv(rank, size, comm);
888 --recv_left;
889 }
890 }
891
892 VarMessage() : send_request(MPI_REQUEST_NULL) {}
893
894 /// Clear the message and associated request.
895 void Clear() { data.clear(); send_request = MPI_REQUEST_NULL; }
896
897 virtual ~VarMessage()
898 {
899 MFEM_ASSERT(send_request == MPI_REQUEST_NULL,
900 "WaitAllSent was not called after Isend");
901 }
902
903 VarMessage(const VarMessage &other)
904 : data(other.data), send_request(other.send_request)
905 {
906 MFEM_ASSERT(send_request == MPI_REQUEST_NULL,
907 "Cannot copy message with a pending send.");
908 }
909
910protected:
911 virtual void Encode(int rank) = 0;
912 virtual void Decode(int rank) = 0;
913};
914
915
916/// Helper struct to convert a C++ type to an MPI type
917template <typename Type> struct MPITypeMap;
918
919// Specializations of MPITypeMap; mpi_type initialized in communication.cpp:
920template<> struct MPITypeMap<bool>
921{
922 static MFEM_EXPORT const MPI_Datatype mpi_type;
923};
924template<> struct MPITypeMap<char>
925{
926 static MFEM_EXPORT const MPI_Datatype mpi_type;
927};
928template<> struct MPITypeMap<unsigned char>
929{
930 static MFEM_EXPORT const MPI_Datatype mpi_type;
931};
932template<> struct MPITypeMap<short>
933{
934 static MFEM_EXPORT const MPI_Datatype mpi_type;
935};
936template<> struct MPITypeMap<unsigned short>
937{
938 static MFEM_EXPORT const MPI_Datatype mpi_type;
939};
940template<> struct MPITypeMap<int>
941{
942 static MFEM_EXPORT const MPI_Datatype mpi_type;
943};
944template<> struct MPITypeMap<unsigned int>
945{
946 static MFEM_EXPORT const MPI_Datatype mpi_type;
947};
948template<> struct MPITypeMap<long>
949{
950 static MFEM_EXPORT const MPI_Datatype mpi_type;
951};
952template<> struct MPITypeMap<unsigned long>
953{
954 static MFEM_EXPORT const MPI_Datatype mpi_type;
955};
956template<> struct MPITypeMap<long long>
957{
958 static MFEM_EXPORT const MPI_Datatype mpi_type;
959};
960template<> struct MPITypeMap<unsigned long long>
961{
962 static MFEM_EXPORT const MPI_Datatype mpi_type;
963};
964template<> struct MPITypeMap<double>
965{
966 static MFEM_EXPORT const MPI_Datatype mpi_type;
967};
968template<> struct MPITypeMap<float>
969{
970 static MFEM_EXPORT const MPI_Datatype mpi_type;
971};
972
973/** Reorder MPI ranks to follow the Z-curve within the physical machine topology
974 (provided that functions to query physical node coordinates are available).
975 Returns a new communicator with reordered ranks. */
976MPI_Comm ReorderRanksZCurve(MPI_Comm comm);
977
978
979} // namespace mfem
980
981#endif
982
983#endif
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
Definition array.hpp:164
int Size() const
Return the logical size of the array.
Definition array.hpp:192
Auxiliary class used by class GroupCommunicator implementing its device (GPU) code paths for data pas...
Array< buffer_max_type > ext_buf
void RestrictTranspose(const Array< T > &x_tdof, Array< T > &x_ldof) const
Transpose of Restrict(): copy the true-dof data x_tdof into the owned local dofs of x_ldof and set th...
void Restrict(const Array< T > &x_ldof, Array< T > &x_tdof) const
Kernel: copy owned ldofs from x_ldof to ltdofs in x_tdof, i.e. x_tdof[i] = x_ldof[ltdof_ldof[i]].
void BcastEndCopy(const Array< T > &ext_buf_t, Array< T > &x_ldof) const
void CopyTDofsToLDofs(const Array< T > &x_tdof, Array< T > &x_ldof) const
Kernel: copy ltdofs from x_tdof to ldofs in x_ldof, i.e. x_ldof[ltdof_ldof[i]] = x_tdof[i].
void ReduceEndLDofs(Array< T > &x_ldof, Op op) const
Finalize a group reduction into the local-dof data x_ldof, applying the reduction operation op.
void BcastBeginTDofs(Array< T > &x_tdof) const
Begin a group broadcast of the true-dof data x_tdof.
const GroupCommunicator & gc
void ProlongateTranspose(const Array< T > &x_ldof, Array< T > &x_tdof, Op op=Op::Sum) const
Transpose of Prolongate(): reduce the local-dof data x_ldof into the true-dof data x_tdof,...
void ReduceEndAssembleTDofs(const Array< T > &shr_buf_t, Array< T > &x_tdof, Op op) const
Array< buffer_max_type > shr_buf
void BcastBeginCopyTDofs(const Array< T > &x_tdof, Array< T > &shr_buf_t) const
void ReduceEndTDofs(Array< T > &x_tdof, Op op) const
Finalize a group reduction into the true-dof data x_tdof, applying the reduction operation op.
void ReduceBeginLDofs(const Array< T > &x_ldof) const
Begin a group reduction of the local-dof data x_ldof.
void ExchangeSharedToExternal(const Array< T > &shr_buf, Array< T > &ext_buf) const
Op
Reduction operation applied at the receiving dofs.
void BcastBeginLDofs(Array< T > &x_ldof) const
Begin a group broadcast of the local-dof data x_ldof.
void ReduceBeginCopy(const Array< T > &x_ldof, Array< T > &ext_buf_t) const
DeviceGroupCommunicator(const GroupCommunicator &gc_)
Construct a device communicator based on the GroupCommunicator gc_.
void BcastEndLDofs(Array< T > &x_ldof) const
Finalize a group broadcast into the local-dof data x_ldof.
void BcastBeginCopyLDofs(const Array< T > &x_ldof, Array< T > &shr_buf_t) const
Array< MPI_Request > requests
void ExchangeExternalToShared(const Array< T > &ext_buf, Array< T > &shr_buf) const
void Prolongate(const Array< T > &x_tdof, Array< T > &x_ldof) const
Prolongate the true-dof data x_tdof to the local-dof data x_ldof.
void Exchange(const Array< T > &send_buf, const Array< int > &send_offsets, Array< T > &recv_buf, const Array< int > &recv_offsets, int tag) const
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
const Table & GroupLDofTable() const
Read-only access to group-ldof Table.
GroupCommunicator(const GroupTopology &gt, Mode m=byNeighbor)
Construct a GroupCommunicator object.
DeviceGroupCommunicator * device_gc
void GetNeighborLTDofTable(Table &nbr_ltdof) const
Dofs to be sent (during Bcast) to communication neighbors.
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize().
Mode
Communication mode.
@ byGroup
Communications are performed one group at a time.
void GetNeighborLDofTable(Table &nbr_ldof) const
Dofs to be received (during Bcast) from communication neighbors.
const T * ReduceGroupFromBuffer(const T *buf, T *ldata, int group, int layout, void(*Op)(OpData< T >)) const
Perform the reduction operation Op to the entries of group group using the values from the buffer buf...
void ReduceMarked(T *ldata, const Array< int > &marker, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with ReduceBegin(), but only apply the reduction to DOFs marked ...
static void MaxAbs(OpData< T >)
void Reduce(Array< T > &ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root, device version.
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with the host version of ReduceBegin().
const GroupTopology & GetGroupTopology() const
Get a const reference to the associated GroupTopology object.
const GroupTopology & gtopo
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root, host version.
T * CopyGroupToBuffer(const T *ldata, T *buf, int group, int layout) const
Copy the entries corresponding to the group group from the local array ldata to the buffer buf.
const T * CopyGroupFromBuffer(const T *buf, T *ldata, int group, int layout) const
Copy the entries corresponding to the group group from the buffer buf to the local array ldata.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with the host version of BcastBegin().
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root, host version.
void Create(const Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
~GroupCommunicator()
Destroy a GroupCommunicator object, deallocating internal data structures and buffers.
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int, double and float.
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
static void Max(OpData< T >)
Reduce operation Max, instantiated for int, double and float.
static void Min(OpData< T >)
Reduce operation Min, instantiated for int, double and float.
void Bcast(Array< T > &ldata) const
Broadcast within each group where the master is the root, device version.
void Bcast(T *ldata) const
Broadcast within each group where the master is the root, host version.
void BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root, host version.
void Reduce(T *ldata, const Array< int > &marker, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root, but only for marked DOFs.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
void PrintInfo(std::ostream &out=mfem::out) const
Print information about the GroupCommunicator from all MPI ranks.
const DeviceGroupCommunicator & GetDeviceComm() const
Return the device communicator, 'device_gc', constructing it if it was not already constructed.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
void SetComm(MPI_Comm comm)
Set the MPI communicator to 'comm'.
int NRanks() const
Return the number of MPI ranks within this object's communicator.
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
void Swap(GroupTopology &other)
Swap the internal data with another GroupTopology object.
void Save(std::ostream &out) const
Save the data in a stream.
const int * GetGroup(int g) const
Return a pointer to a list of neighbors for a given group. Neighbor 0 is the local processor.
int MyRank() const
Return the MPI rank within this object's communicator.
int GetGroupSize(int g) const
Get the number of processors in a group.
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor.
void Load(std::istream &in)
Load the data from a stream.
GroupTopology()
Constructor with the MPI communicator = 0.
GroupTopology(MPI_Comm comm)
Constructor given the MPI communicator 'comm'.
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
void Create(ListOfIntegerSets &groups, int mpitag)
Set up the group topology given the list of sets of shared entities.
MPI_Comm GetComm() const
Return the MPI communicator.
void Copy(GroupTopology &copy) const
Copy the internal data to the external 'copy'.
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
int NGroups() const
Return the number of groups.
int GetGroupMasterGroup(int g) const
Return the group number in the master for group 'g'.
List of integer sets.
Definition sets.hpp:51
A simple convenience class based on the Mpi singleton class above. Preserved for backward compatibili...
bool Root() const
Return true if WorldRank() == 0.
int WorldSize() const
Return MPI_COMM_WORLD's size.
MPI_Session(int &argc, char **&argv)
int WorldRank() const
Return MPI_COMM_WORLD's rank.
void CopyConvertPtr(const Memory< U > &base)
A simple singleton class that calls MPI_Init() at construction and MPI_Finalize() at destruction....
static MFEM_EXPORT int default_thread_required
Default level of thread support for MPI_Init_thread.
static bool IsFinalized()
Return true if MPI has been finalized.
static bool Root()
Return true if the rank in MPI_COMM_WORLD is zero.
static void Finalize()
Finalize MPI (if it has been initialized and not yet already finalized).
static void Init(int *argc=nullptr, char ***argv=nullptr, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init().
static bool IsInitialized()
Return true if MPI has been initialized.
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
static int WorldSize()
Return the size of MPI_COMM_WORLD.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
int RowSize(int i) const
Definition table.hpp:122
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:103
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
MPI_Comm ReorderRanksZCurve(MPI_Comm comm)
@ REBALANCE_DOF_VM
RebalanceDofMessage.
@ NEIGHBOR_REFINEMENT_VM
NeighborRefinementMessage.
@ NEIGHBOR_ROW_VM
NeighborRowMessage.
@ NEIGHBOR_PREFINEMENT_VM
NeighborPRefinementMessage.
@ NEIGHBOR_ELEMENT_RANK_VM
NeighborElementRankMessage.
@ NEIGHBOR_ORDER_VM
NeighborOrderMessage.
@ REBALANCE_VM
RebalanceMessage.
@ NEIGHBOR_DEREFINEMENT_VM
NeighborDerefinementMessage.
MessageTag
General MPI message tags used by MFEM.
@ DEREFINEMENT_MATRIX_CONSTRUCTION_DATA
TypedBufferView(Array< buffer_max_type > &storage_)
Data structure on which we define reduce operations. The data is associated with (and the operation i...
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
static MFEM_EXPORT const MPI_Datatype mpi_type
Helper struct to convert a C++ type to an MPI type.
Variable-length MPI message containing unspecific binary data.
void Issend(int rank, MPI_Comm comm)
Non-blocking synchronous send to processor 'rank'. Returns immediately. Completion (MPI_Wait/Test) me...
static void WaitAllSent(MapT &rank_msg)
Helper to wait for all messages in a map container to be sent.
virtual void Encode(int rank)=0
static bool TestAllSent(MapT &rank_msg)
Return true if all messages in the map container were sent, otherwise return false,...
void Isend(int rank, MPI_Comm comm)
Non-blocking send to processor 'rank'. Returns immediately. Completion (as tested by MPI_Wait/Test) d...
MPI_Request send_request
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
Helper to send all messages in a rank-to-message map container.
virtual void Decode(int rank)=0
static void RecvAll(MapT &rank_msg, MPI_Comm comm)
Helper to receive all messages in a rank-to-message map container.
VarMessage(const VarMessage &other)
void Clear()
Clear the message and associated request.
void RecvDrop(int rank, int size, MPI_Comm comm)
Like Recv(), but throw away the message.
static bool IProbe(int &rank, int &size, MPI_Comm comm)
Non-blocking probe for incoming message of this type from any rank. If there is an incoming message,...
void Recv(int rank, int size, MPI_Comm comm)
Post-probe receive from processor 'rank' of message size 'size'.
static void Probe(int &rank, int &size, MPI_Comm comm)
Blocking probe for incoming message of this type from any rank. Returns the rank and message size.