20 #ifndef GEOS_COMMON_MPIWRAPPER_HPP_
21 #define GEOS_COMMON_MPIWRAPPER_HPP_
26 #if defined(GEOS_USE_MPI)
28 #define MPI_PARAM( x ) x
30 #define MPI_PARAM( x )
33 #define MPI_COMM_NULL ((MPI_Comm)0x04000000)
34 #define MPI_COMM_WORLD ((MPI_Comm)0x44000000)
35 #define MPI_COMM_SELF ((MPI_Comm)0x40000000)
38 typedef int MPI_Datatype;
39 #define MPI_CHAR ((MPI_Datatype)0x4c000101)
40 #define MPI_SIGNED_CHAR ((MPI_Datatype)0x4c000118)
41 #define MPI_UNSIGNED_CHAR ((MPI_Datatype)0x4c000102)
42 #define MPI_BYTE ((MPI_Datatype)0x4c00010d)
43 #define MPI_WCHAR ((MPI_Datatype)0x4c00040e)
44 #define MPI_SHORT ((MPI_Datatype)0x4c000203)
45 #define MPI_UNSIGNED_SHORT ((MPI_Datatype)0x4c000204)
46 #define MPI_INT ((MPI_Datatype)0x4c000405)
47 #define MPI_UNSIGNED ((MPI_Datatype)0x4c000406)
48 #define MPI_LONG ((MPI_Datatype)0x4c000807)
49 #define MPI_UNSIGNED_LONG ((MPI_Datatype)0x4c000808)
50 #define MPI_FLOAT ((MPI_Datatype)0x4c00040a)
51 #define MPI_DOUBLE ((MPI_Datatype)0x4c00080b)
52 #define MPI_LONG_DOUBLE ((MPI_Datatype)0x4c00100c)
53 #define MPI_LONG_LONG_INT ((MPI_Datatype)0x4c000809)
54 #define MPI_UNSIGNED_LONG_LONG ((MPI_Datatype)0x4c000819)
55 #define MPI_LONG_LONG MPI_LONG_LONG_INT
59 #define MPI_MAX (MPI_Op)(0x58000001)
60 #define MPI_MIN (MPI_Op)(0x58000002)
61 #define MPI_SUM (MPI_Op)(0x58000003)
62 #define MPI_PROD (MPI_Op)(0x58000004)
63 #define MPI_LAND (MPI_Op)(0x58000005)
64 #define MPI_BAND (MPI_Op)(0x58000006)
65 #define MPI_LOR (MPI_Op)(0x58000007)
66 #define MPI_BOR (MPI_Op)(0x58000008)
67 #define MPI_LXOR (MPI_Op)(0x58000009)
68 #define MPI_BXOR (MPI_Op)(0x5800000a)
69 #define MPI_MINLOC (MPI_Op)(0x5800000b)
70 #define MPI_MAXLOC (MPI_Op)(0x5800000c)
71 #define MPI_REPLACE (MPI_Op)(0x5800000d)
72 #define MPI_NO_OP (MPI_Op)(0x5800000e)
75 #define MPI_UNDEFINED (-32766)
76 #define MPI_STATUS_IGNORE (MPI_Status *)1
77 #define MPI_STATUSES_IGNORE (MPI_Status *)1
78 #define MPI_REQUEST_NULL ((MPI_Request)0x2c000000)
79 typedef int MPI_Request;
82 #define MPI_INFO_NULL (MPI_Info)(0x60000000)
92 #define MPI_CHECK_ERROR( error ) ((void) error)
94 #define MPI_CHECK_ERROR( error ) GEOS_ERROR_IF_NE( error, MPI_SUCCESS );
149 static void barrier( MPI_Comm
const & MPI_PARAM( comm )=
MPI_COMM_GEOS );
151 static int cartCoords( MPI_Comm comm,
int rank,
int maxdims,
int coords[] );
153 static int cartCreate( MPI_Comm comm_old,
int ndims,
const int dims[],
const int periods[],
154 int reorder, MPI_Comm * comm_cart );
156 static int cartRank( MPI_Comm comm,
const int coords[] );
158 static void commFree( MPI_Comm & comm );
160 static int commRank( MPI_Comm
const & MPI_PARAM( comm )=
MPI_COMM_GEOS );
162 static int commSize( MPI_Comm
const & MPI_PARAM( comm )=
MPI_COMM_GEOS );
164 static bool commCompare( MPI_Comm
const & comm1, MPI_Comm
const & comm2 );
166 static bool initialized();
168 static int init(
int * argc,
char * * * argv );
170 static void finalize();
172 static MPI_Comm commDup( MPI_Comm
const comm );
174 static MPI_Comm commSplit( MPI_Comm
const comm,
int color,
int key );
176 static int test( MPI_Request * request,
int * flag,
MPI_Status * status );
178 static int testAny(
int count, MPI_Request array_of_requests[],
int * idx,
int * flags,
MPI_Status array_of_statuses[] );
180 static int testSome(
int count, MPI_Request array_of_requests[],
int * outcount,
int array_of_indices[],
MPI_Status array_of_statuses[] );
182 static int testAll(
int count, MPI_Request array_of_requests[],
int * flags,
MPI_Status array_of_statuses[] );
204 static int checkAny(
int count, MPI_Request array_of_requests[],
int * idx,
int * flag,
MPI_Status array_of_statuses[] );
215 static int checkAll(
int count, MPI_Request array_of_requests[],
int * flag,
MPI_Status array_of_statuses[] );
217 static int wait( MPI_Request * request,
MPI_Status * status );
219 static int waitAny(
int count, MPI_Request array_of_requests[],
int * indx,
MPI_Status array_of_statuses[] );
221 static int waitSome(
int count, MPI_Request array_of_requests[],
int * outcount,
int array_of_indices[],
MPI_Status array_of_statuses[] );
223 static int waitAll(
int count, MPI_Request array_of_requests[],
MPI_Status array_of_statuses[] );
225 static double wtime(
void );
238 MPI_Request array_of_requests[],
240 std::function< MPI_Request (
int ) > func );
252 MPI_Request array_of_requests[],
254 std::function< MPI_Request (
int ) > func );
269 std::vector< std::tuple< MPI_Request *,
MPI_Status *, std::function< MPI_Request (
int ) > > >
const & phases );
285 std::vector< std::tuple< MPI_Request *,
MPI_Status *, std::function< MPI_Request (
int ) > > >
const & phases );
288 #if !defined(GEOS_USE_MPI)
289 static std::map< int, std::pair< int, void * > > & getTagToPointersMap()
291 static std::map< int, std::pair< int, void * > > tagToPointers;
292 return tagToPointers;
313 template<
typename T_SEND,
typename T_RECV >
332 template<
typename T_SEND,
typename T_RECV >
346 template<
typename T >
349 template<
typename T >
363 template<
typename T >
374 template<
typename T >
385 template<
typename T >
398 template<
typename T >
399 static int reduce( T
const * sendbuf, T * recvbuf,
int count, MPI_Op op,
int root, MPI_Comm comm =
MPI_COMM_GEOS );
409 template<
typename T >
420 template<
typename T >
424 template<
typename T >
425 static int scan( T
const * sendbuf, T * recvbuf,
int count, MPI_Op op, MPI_Comm comm );
427 template<
typename T >
428 static int exscan( T
const * sendbuf, T * recvbuf,
int count, MPI_Op op, MPI_Comm comm );
438 template<
typename T >
439 static int bcast( T * buffer,
int count,
int root, MPI_Comm comm );
448 template<
typename T >
463 template<
typename TS,
typename TR >
464 static int gather( TS
const *
const sendbuf,
485 template<
typename TS,
typename TR >
489 const int * recvcounts,
501 template<
typename T >
503 int MPI_PARAM( source ),
505 MPI_Comm MPI_PARAM( comm ),
508 template<
typename T >
510 int MPI_PARAM( dest ),
512 MPI_Comm MPI_PARAM( comm ),
513 MPI_Request * MPI_PARAM( request ) );
525 template<
typename T >
531 MPI_Request * request );
543 template<
typename T >
544 static int iSend( T
const *
const buf,
549 MPI_Request * request );
558 template<
typename U,
typename T >
566 template<
typename T >
575 template<
typename T >
583 template<
typename T >
592 template<
typename T >
600 template<
typename T >
609 template<
typename T >
626 template<
typename T,
typename ENABLE =
void >
627 struct MpiTypeImpl {};
629 #define ADD_MPI_TYPE_MAP( T, MPI_T ) \
630 template<> struct MpiTypeImpl< T > { static MPI_Datatype get() { return MPI_T; } }
632 ADD_MPI_TYPE_MAP(
float, MPI_FLOAT );
633 ADD_MPI_TYPE_MAP(
double, MPI_DOUBLE );
635 ADD_MPI_TYPE_MAP(
char, MPI_CHAR );
636 ADD_MPI_TYPE_MAP(
signed char, MPI_SIGNED_CHAR );
637 ADD_MPI_TYPE_MAP(
unsigned char, MPI_UNSIGNED_CHAR );
639 ADD_MPI_TYPE_MAP(
int, MPI_INT );
640 ADD_MPI_TYPE_MAP(
long int, MPI_LONG );
641 ADD_MPI_TYPE_MAP(
long long int, MPI_LONG_LONG );
643 ADD_MPI_TYPE_MAP(
unsigned int, MPI_UNSIGNED );
644 ADD_MPI_TYPE_MAP(
unsigned long int, MPI_UNSIGNED_LONG );
645 ADD_MPI_TYPE_MAP(
unsigned long long int, MPI_UNSIGNED_LONG_LONG );
647 #undef ADD_MPI_TYPE_MAP
649 template<
typename T >
650 struct MpiTypeImpl< T, std::enable_if_t< std::is_enum< T >::value > >
652 static MPI_Datatype get() {
return MpiTypeImpl< std::underlying_type_t< T > >::get(); }
655 template<
typename T >
656 MPI_Datatype getMpiType()
658 return MpiTypeImpl< T >::get();
684 GEOS_ERROR(
"Unsupported reduction operation" );
689 template<
typename T_SEND,
typename T_RECV >
692 T_RECV *
const recvbuf,
694 MPI_Comm MPI_PARAM( comm ) )
697 return MPI_Allgather( sendbuf, sendcount, internal::getMpiType< T_SEND >(),
698 recvbuf, recvcount, internal::getMpiType< T_RECV >(),
701 static_assert( std::is_same< T_SEND, T_RECV >::value,
702 "MpiWrapper::allgather() for serial run requires send and receive buffers are of the same type" );
704 std::copy( sendbuf, sendbuf + sendcount, recvbuf )
709 template<
typename T_SEND,
typename T_RECV >
712 T_RECV *
const recvbuf,
715 MPI_Comm MPI_PARAM( comm ) )
718 return MPI_Allgatherv( sendbuf, sendcount, internal::getMpiType< T_SEND >(),
719 recvbuf, recvcounts, displacements, internal::getMpiType< T_RECV >(),
722 static_assert( std::is_same< T_SEND, T_RECV >::value,
723 "MpiWrapper::allgatherv() for serial run requires send and receive buffers are of the same type" );
725 std::copy( sendbuf, sendbuf + sendcount, recvbuf )
731 template<
typename T >
735 int const mpiSize = commSize( comm );
736 allValues.resize( mpiSize );
738 MPI_Datatype
const MPI_TYPE = internal::getMpiType< T >();
740 MPI_Allgather( &myValue, 1, MPI_TYPE, allValues.data(), 1, MPI_TYPE, comm );
743 allValues.resize( 1 );
744 allValues[0] = myValue;
748 template<
typename T >
750 array1d< T > & allValues,
751 MPI_Comm MPI_PARAM( comm ) )
753 int const sendSize = LvArray::integerConversion< int >( sendValues.size() );
755 int const mpiSize = commSize( comm );
756 allValues.resize( mpiSize * sendSize );
757 return MPI_Allgather( sendValues.data(),
759 internal::getMpiType< T >(),
762 internal::getMpiType< T >(),
766 allValues.resize( sendSize );
769 allValues[a] = sendValues[a];
775 template<
typename T >
779 MPI_Op
const MPI_PARAM( op ),
780 MPI_Comm
const MPI_PARAM( comm ) )
783 MPI_Datatype
const mpiType = internal::getMpiType< T >();
784 return MPI_Allreduce( sendbuf == recvbuf ? MPI_IN_PLACE : sendbuf, recvbuf, count, mpiType, op, comm );
786 if( sendbuf != recvbuf )
788 memcpy( recvbuf, sendbuf, count *
sizeof( T ) );
794 template<
typename T >
798 MPI_Op
const MPI_PARAM( op ),
800 MPI_Comm
const MPI_PARAM( comm ) )
803 MPI_Datatype
const mpiType = internal::getMpiType< T >();
804 return MPI_Reduce( sendbuf == recvbuf ? MPI_IN_PLACE : sendbuf, recvbuf, count, mpiType, op, root, comm );
806 if( sendbuf != recvbuf )
808 memcpy( recvbuf, sendbuf, count *
sizeof( T ) );
814 template<
typename T >
815 int MpiWrapper::scan( T
const *
const sendbuf,
818 MPI_Op MPI_PARAM( op ),
819 MPI_Comm MPI_PARAM( comm ) )
822 return MPI_Scan( sendbuf, recvbuf, count, internal::getMpiType< T >(), op, comm );
824 memcpy( recvbuf, sendbuf, count*
sizeof(T) );
829 template<
typename T >
830 int MpiWrapper::exscan( T
const *
const MPI_PARAM( sendbuf ),
833 MPI_Op MPI_PARAM( op ),
834 MPI_Comm MPI_PARAM( comm ) )
837 return MPI_Exscan( sendbuf, recvbuf, count, internal::getMpiType< T >(), op, comm );
839 memset( recvbuf, 0, count*
sizeof(T) );
844 template<
typename T >
846 int MPI_PARAM( count ),
847 int MPI_PARAM( root ),
848 MPI_Comm MPI_PARAM( comm ) )
851 return MPI_Bcast( buffer, count, internal::getMpiType< T >(), root, comm );
858 template<
typename T >
859 void MpiWrapper::broadcast( T & MPI_PARAM( value ),
int MPI_PARAM( srcRank ), MPI_Comm MPI_PARAM( comm ) )
862 MPI_Bcast( &value, 1, internal::getMpiType< T >(), srcRank, comm );
868 void MpiWrapper::broadcast< string >(
string & MPI_PARAM( value ),
869 int MPI_PARAM( srcRank ),
870 MPI_Comm MPI_PARAM( comm ) )
873 int size = LvArray::integerConversion< int >( value.size() );
875 value.resize( size );
876 MPI_Bcast(
const_cast< char *
>( value.data() ), size, internal::getMpiType< char >(), srcRank, comm );
880 template<
typename TS,
typename TR >
885 int MPI_PARAM( root ),
886 MPI_Comm MPI_PARAM( comm ) )
889 return MPI_Gather( sendbuf, sendcount, internal::getMpiType< TS >(),
890 recvbuf, recvcount, internal::getMpiType< TR >(),
893 static_assert( std::is_same< TS, TR >::value,
894 "MpiWrapper::gather() for serial run requires send and receive buffers are of the same type" );
895 std::size_t const sendBufferSize = sendcount *
sizeof(TS);
896 std::size_t const recvBufferSize = recvcount *
sizeof(TR);
897 GEOS_ERROR_IF_NE_MSG( sendBufferSize, recvBufferSize,
"size of send buffer and receive buffer are not equal" );
898 memcpy( recvbuf, sendbuf, sendBufferSize );
903 template<
typename TS,
typename TR >
907 const int * recvcounts,
908 const int * MPI_PARAM( displs ),
909 int MPI_PARAM( root ),
910 MPI_Comm MPI_PARAM( comm ) )
913 return MPI_Gatherv( sendbuf, sendcount, internal::getMpiType< TS >(),
914 recvbuf, recvcounts, displs, internal::getMpiType< TR >(),
917 static_assert( std::is_same< TS, TR >::value,
918 "MpiWrapper::gather() for serial run requires send and receive buffers are of the same type" );
919 std::size_t const sendBufferSize = sendcount *
sizeof(TS);
920 std::size_t const recvBufferSize = recvcounts[0] *
sizeof(TR);
921 GEOS_ERROR_IF_NE_MSG( sendBufferSize, recvBufferSize,
"size of send buffer and receive buffer are not equal" );
922 memcpy( recvbuf, sendbuf, sendBufferSize );
927 template<
typename T >
930 int MPI_PARAM( source ),
932 MPI_Comm MPI_PARAM( comm ),
933 MPI_Request * MPI_PARAM( request ) )
937 "Attempting to use an MPI_Request that is still in use." );
938 return MPI_Irecv( buf, count, internal::getMpiType< T >(), source, tag, comm, request );
940 std::map< int, std::pair< int, void * > > & pointerMap = getTagToPointersMap();
941 std::map< int, std::pair< int, void * > >::iterator iPointer = pointerMap.find( tag );
943 if( iPointer==pointerMap.end() )
945 pointerMap.insert( {tag, {1, buf} } );
950 "Tag does is assigned, but pointer was not set by iSend." );
951 memcpy( buf, iPointer->second.second, count*
sizeof(T) );
952 pointerMap.erase( iPointer );
958 template<
typename T >
959 int MpiWrapper::recv( array1d< T > & buf,
960 int MPI_PARAM( source ),
962 MPI_Comm MPI_PARAM( comm ),
968 MPI_Probe( source, tag, comm, &status );
969 MPI_Get_count( &status, MPI_CHAR, &count );
972 buf.resize( count /
sizeof( T ) );
974 return MPI_Recv(
reinterpret_cast< char *
>( buf.data() ),
987 template<
typename T >
988 int MpiWrapper::iSend( arrayView1d< T >
const & buf,
989 int MPI_PARAM( dest ),
991 MPI_Comm MPI_PARAM( comm ),
992 MPI_Request * MPI_PARAM( request ) )
996 "Attempting to use an MPI_Request that is still in use." );
997 return MPI_Isend(
reinterpret_cast< void const *
>( buf.data() ),
998 buf.size() *
sizeof( T ),
1010 template<
typename T >
1011 int MpiWrapper::iSend( T
const *
const buf,
1013 int MPI_PARAM( dest ),
1015 MPI_Comm MPI_PARAM( comm ),
1016 MPI_Request * MPI_PARAM( request ) )
1020 "Attempting to use an MPI_Request that is still in use." );
1021 return MPI_Isend( buf, count, internal::getMpiType< T >(), dest, tag, comm, request );
1023 std::map< int, std::pair< int, void * > > & pointerMap = getTagToPointersMap();
1024 std::map< int, std::pair< int, void * > >::iterator iPointer = pointerMap.find( tag );
1026 if( iPointer==pointerMap.end() )
1028 pointerMap.insert( {tag, {0,
const_cast< T *
>(buf)}
1034 "Tag does is assigned, but pointer was not set by iRecv." );
1035 memcpy( iPointer->second.second, buf, count*
sizeof(T) );
1036 pointerMap.erase( iPointer );
1042 template<
typename U,
typename T >
1048 U
const convertedValue = value;
1049 int const error = MPI_Exscan( &convertedValue, &localResult, 1, internal::getMpiType< U >(), MPI_SUM, comm );
1050 MPI_CHECK_ERROR( error );
1052 if( commRank() == 0 )
1061 template<
typename T >
1069 template<
typename T >
1076 template<
typename T >
1082 template<
typename T >
1088 template<
typename T >
1094 template<
typename T >
1100 template<
typename T >
1106 template<
typename T >
1113 template<
typename T >
1121 template<
typename T >
1129 template<
typename T >
1133 static_assert( std::is_trivially_copyable< T >::value,
"maxValLoc requires a trivially copyable type" );
1136 static_assert( (
sizeof(T::value)+
sizeof(T::location)) ==
sizeof(T) );
1139 static_assert( std::is_scalar_v< decltype(T::value) > || std::is_scalar_v< decltype(T::location) >,
"members of struct should be scalar" );
1140 static_assert( !std::is_pointer_v< decltype(T::value) > && !std::is_pointer_v< decltype(T::location) >,
"members of struct should not be pointers" );
1143 int const numProcs = commSize( comm );
1144 std::vector< T > recvValLoc( numProcs );
1146 MPI_Allgather( &localValueLocation,
sizeof(T), MPI_BYTE, recvValLoc.data(),
sizeof(T), MPI_BYTE, comm );
1148 T
maxValLoc= *std::max_element( recvValLoc.begin(),
1150 [](
auto & lhs,
auto & rhs ) ->
bool {return lhs.value < rhs.value; } );
#define GEOS_ERROR(msg)
Raise a hard error and terminate the program.
#define GEOS_ERROR_IF(EXP, msg)
Conditionally raise a hard error and terminate the program.
#define GEOS_ERROR_IF_NE_MSG(lhs, rhs, msg)
Raise a hard error if two values are not equal.
#define GEOS_ASSERT_EQ(lhs, rhs)
Assert that two values compare equal in debug builds.
Lightweight non-owning wrapper over a contiguous range of elements.
constexpr T * data() const noexcept
constexpr size_type size() const noexcept
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
int MPI_COMM_GEOS
Global MPI communicator used by GEOSX.
GEOS_LOCALINDEX_TYPE localIndex
Local index type (for indexing objects within an MPI partition).
std::size_t size_t
Unsigned size type.
Array< T, 1 > array1d
Alias for 1D array.
static int allgatherv(T_SEND const *sendbuf, int sendcount, T_RECV *recvbuf, int *recvcounts, int *displacements, MPI_Comm comm)
Strongly typed wrapper around MPI_Allgatherv.
static int bcast(T *buffer, int count, int root, MPI_Comm comm)
Strongly typed wrapper around MPI_Bcast.
static MPI_Op getMpiOp(Reduction const op)
Returns an MPI_Op associated with our strongly typed Reduction enum.
static int activeWaitSomeCompletePhase(const int participants, std::vector< std::tuple< MPI_Request *, MPI_Status *, std::function< MPI_Request(int) > > > const &phases)
static int checkAll(int count, MPI_Request array_of_requests[], int *flag, MPI_Status array_of_statuses[])
static int activeWaitOrderedCompletePhase(const int participants, std::vector< std::tuple< MPI_Request *, MPI_Status *, std::function< MPI_Request(int) > > > const &phases)
static int gather(TS const *const sendbuf, int sendcount, TR *const recvbuf, int recvcount, int root, MPI_Comm comm)
Strongly typed wrapper around MPI_Gather().
static int gatherv(TS const *const sendbuf, int sendcount, TR *const recvbuf, const int *recvcounts, const int *displs, int root, MPI_Comm comm)
Strongly typed wrapper around MPI_Gatherv.
static int check(MPI_Request *request, int *flag, MPI_Status *status)
static int activeWaitAny(const int count, MPI_Request array_of_requests[], MPI_Status array_of_statuses[], std::function< MPI_Request(int) > func)
static int iSend(T const *const buf, int count, int dest, int tag, MPI_Comm comm, MPI_Request *request)
Strongly typed wrapper around MPI_Isend()
static void allGather(T const myValue, array1d< T > &allValues, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for MPI_Allgather.
static int allgather(T_SEND const *sendbuf, int sendcount, T_RECV *recvbuf, int recvcount, MPI_Comm comm)
Strongly typed wrapper around MPI_Allgather.
static T max(T const &value, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for a MPI_Allreduce using a MPI_MAX operation.
static int activeWaitSome(const int count, MPI_Request array_of_requests[], MPI_Status array_of_statuses[], std::function< MPI_Request(int) > func)
static int allReduce(T const *sendbuf, T *recvbuf, int count, MPI_Op op, MPI_Comm comm=MPI_COMM_GEOS)
Strongly typed wrapper around MPI_Allreduce.
static U prefixSum(T const value, MPI_Comm comm=MPI_COMM_GEOS)
Compute exclusive prefix sum and full sum.
static T maxValLoc(T localValueLocation, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for MPI_Gather using a MPI_MAX operation on struct of value and location.
static int checkAny(int count, MPI_Request array_of_requests[], int *idx, int *flag, MPI_Status array_of_statuses[])
static int iRecv(T *const buf, int count, int source, int tag, MPI_Comm comm, MPI_Request *request)
Strongly typed wrapper around MPI_Irecv()
static T sum(T const &value, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for a MPI_Allreduce using a MPI_SUM operation.
static T min(T const &value, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for a MPI_Allreduce using a MPI_MIN operation.
static int nodeCommSize()
Compute the number of ranks allocated on the same node.
static void broadcast(T &value, int srcRank=0, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for MPI_Broadcast.
static int reduce(T const *sendbuf, T *recvbuf, int count, MPI_Op op, int root, MPI_Comm comm=MPI_COMM_GEOS)
Strongly typed wrapper around MPI_Reduce.