BELFEM 0.9.0
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
commtools.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef COMMTOOLS_HPP
13#define COMMTOOLS_HPP
14
15#include <limits>
16#include <type_traits>
17#include <string>
18
19#include "typedefs.hpp"
20#include "assert.hpp"
21#include "cl_Vector.hpp"
22#include "cl_Matrix.hpp"
23#include "cl_Communicator.hpp"
24#include "commtypes.hpp"
25
27
28namespace belfem
29{
30
31 constexpr int gMaxCommChunkLength = 64 * 1024;
32
33//==============================================================================
34// HELPERS TO AVOID AMBIGUITIES
35//==============================================================================
36
37 template<typename T>
38 struct is_scalar : std::true_type {};
39
40 template<typename T>
41 struct is_scalar<Vector<T>> : std::false_type {};
42
43 template<typename T>
44 struct is_scalar<Matrix<T>> : std::false_type {};
45
46//==============================================================================
47// UTILITIES
48//==============================================================================
49
54 proc_t
55 comm_size();
56
57//------------------------------------------------------------------------------
58
63 proc_t
64 comm_rank();
65
66
67//------------------------------------------------------------------------------
68
73 void
74 comm_check( const int aErrorCode );
75
76//------------------------------------------------------------------------------
77
81 void
83
84//------------------------------------------------------------------------------
85
113 void
114 comm_drain_check( const char * aLabel );
115
116//------------------------------------------------------------------------------
117
124 int
125 comm_tag( const proc_t aSource, const proc_t aTarget );
126
127//------------------------------------------------------------------------------
128
135 comm_split( const index_t aLength );
136
137//------------------------------------------------------------------------------
138
145 index_t
146 comm_splitcount( const Vector< index_t > & aLengths, const proc_t aRoot );
147
148 index_t
149 comm_splitcount( index_t aLength );
150
151//------------------------------------------------------------------------------
152
159 template< typename T >
160 void
161 broadcast( T & aMessage, const proc_t aRoot=0,
162 typename std::enable_if<is_scalar<T>::value>::type* = nullptr )
163 {
164#ifdef BELFEM_MPI
165 if( gComm.size() > 1 )
166 {
167 // Ensure T is a scalar type.
168 // Note: std::complex<T> is not supported here; use
169 // send/receive for complex scalar communication.
170 BELFEM_ASSERT( std::is_arithmetic<T>::value,
171 "Can only broadcast arithmetic types." );
172
173 // call the bcast command
174 comm_t tCommType = comm_type<T>();
175 comm_check( MPI_Bcast(
176 &aMessage,
177 1,
178 tCommType,
179 aRoot,
180 gComm.world() ) );
181 }
182#endif
183 }
184
185 // for raw array with known length
186 template< typename T >
187 void
188 broadcast( T * aMessage, const proc_t aRoot, const proc_t aLength )
189 {
190#ifdef BELFEM_MPI
191 if( gComm.size() > 1 )
192 {
193 // Ensure T is a scalar type.
194 // Note: std::complex<T> is not supported here; use
195 // send/receive for complex array communication.
196 BELFEM_ASSERT( std::is_arithmetic<T>::value,
197 "Can only broadcast arithmetic types." );
198
199 // call the bcast command
200 comm_t tCommType = comm_type<T>();
201
202 comm_check( MPI_Bcast(
203 aMessage,
204 aLength,
205 tCommType,
206 aRoot,
207 gComm.world() ) );
208 }
209#endif
210 }
211
212//------------------------------------------------------------------------------
213
231 template < typename T >
232 void
233 allreduce( const T * aSend, T * aRecv, const int aCount )
234 {
235 // MPI_MAX is meaningless for the complex types comm_type<> also
236 // serves - refuse them at compile time
237 static_assert( std::is_arithmetic< T >::value,
238 "allreduce: MAX-reduction is defined for arithmetic types only" );
239
240 BELFEM_ASSERT( aCount >= 0, "allreduce: negative count %d", aCount );
241
242#ifdef BELFEM_MPI
243 comm_check( MPI_Allreduce(
244 aSend,
245 aRecv,
246 aCount,
248 MPI_MAX,
249 gComm.world() ) );
250#else
251 // serial semantic of a one-rank allreduce: the identity copy
252 if ( aSend != aRecv )
253 {
254 for ( int i = 0; i < aCount; ++i )
255 {
256 aRecv[ i ] = aSend[ i ];
257 }
258 }
259#endif
260 }
261
262//------------------------------------------------------------------------------
263
273 template < typename T >
274 void
275 allreduce_min( const T * aSend, T * aRecv, const int aCount )
276 {
277 static_assert( std::is_arithmetic< T >::value,
278 "allreduce_min: MIN-reduction is defined for arithmetic types only" );
279
280 BELFEM_ASSERT( aCount >= 0, "allreduce_min: negative count %d", aCount );
281
282#ifdef BELFEM_MPI
283 comm_check( MPI_Allreduce(
284 aSend,
285 aRecv,
286 aCount,
288 MPI_MIN,
289 gComm.world() ) );
290#else
291 if ( aSend != aRecv )
292 {
293 for ( int i = 0; i < aCount; ++i )
294 {
295 aRecv[ i ] = aSend[ i ];
296 }
297 }
298#endif
299 }
300
301
302//==============================================================================
303// SCALARS
304//==============================================================================
305
312 template< typename T >
313 void
314 send( const T aData, const proc_t aTarget=0, typename std::enable_if<is_scalar<T>::value>::type* = nullptr )
315 {
316#ifdef BELFEM_MPI
317 // get my id
318 proc_t tMyRank = gComm.rank();
319
320 if( aTarget < comm_size() && tMyRank != aTarget )
321 {
322 // status/request handlers
323 MPI_Status tStatus;
324 MPI_Request tRequest;
325
326 comm_check( MPI_Isend( &aData,
327 1,
329 aTarget,
330 comm_tag( aTarget, tMyRank ),
331 gComm.world(),
332 & tRequest ) );
333
334
335 // wait until receive is complete
336 comm_check( MPI_Wait( &tRequest, &tStatus ) );
337 }
338#endif
339 }
340
341//------------------------------------------------------------------------------
342
349 template< typename T >
350 void
351 receive( T & aData, const proc_t aSource=0, typename std::enable_if<is_scalar<T>::value>::type* = nullptr )
352 {
353#ifdef BELFEM_MPI
354 // get my id
355 proc_t tMyRank = gComm.rank();
356
357 // get the number of procs
358 proc_t tCommSize = gComm.size();
359
360
361 if( aSource < tCommSize && tMyRank != aSource )
362 {
363 // status/request handlers
364 MPI_Status tStatus;
365 MPI_Request tRequest;
366
367 // call MPI command
368 comm_check( MPI_Irecv( &aData,
369 1,
371 aSource,
372 comm_tag( aSource, tMyRank ),
373 gComm.world(),
374 & tRequest ) );
375
376 // wait until receive is complete
377 comm_check( MPI_Wait( &tRequest, &tStatus ) );
378 }
379#endif
380 }
381
382//==============================================================================
383// RAW ARRAYS
384//==============================================================================
385
399 template< typename T >
400 void
401 send( T * aData, const index_t aLength, const proc_t aTarget )
402 {
403#ifdef BELFEM_MPI
404 // get the total number of procs
405 proc_t tCommSize = gComm.size();
406
407 // get my rank
408 proc_t tMyRank = gComm.rank();
409
410 if ( aTarget < tCommSize && aTarget != tMyRank )
411 {
412 // status/request handlers
413 MPI_Status tSizeStatus;
414 MPI_Request tSizeRequest;
415
416 // compute the commtag
417 int tCommTag = comm_tag( tMyRank, aTarget );
418
419 // send the length
420 comm_check( MPI_Isend( &aLength,
421 1,
423 aTarget,
424 tCommTag++,
425 gComm.world(),
426 & tSizeRequest ) );
427
428 // wait until receive is complete
429 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
430
431 // nothing to do if the vector is empty
432 if ( aLength == 0 ) return ;
433
434 // compute the chunks for this message
435 Cell< int > tChunkSizes = comm_split( aLength );
436
437 // Allocate memory for status/request vector
438 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
439 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
440
441 // initialitze the counter
442 index_t tCount = 0 ;
443
444 // offset in data container
445 index_t tOffset = 0 ;
446
447 // get the communication type
448 comm_t tCommType = comm_type< T >();
449
450 for ( index_t c : tChunkSizes )
451 {
452 // send data
453 comm_check( MPI_Isend(
454 &aData[ tOffset ],
455 c,
456 tCommType,
457 aTarget,
458 tCommTag,
459 gComm.world(),
460 & tRequest[ tCount++] ) );
461
462 // increment offset
463 tOffset+= c;
464 }
465
466 // wait until send is complete
467 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
468
469 free( tStatus );
470 free( tRequest );
471 }
472#endif
473 }
474
489 template< typename T >
490 void
491 receive( T * aData, index_t & aLength, const proc_t aSource )
492 {
493#ifdef BELFEM_MPI
494
495 // get the total number of procs
496 proc_t tCommSize = gComm.size();
497
498 // get my rank
499 proc_t tMyRank = gComm.rank();
500
501 if ( aSource < tCommSize && aSource != tMyRank )
502 {
503 // status/request handlers
504 MPI_Status tSizeStatus;
505 MPI_Request tSizeRequest;
506
507 // compute the commtag
508 int tCommTag = comm_tag( aSource, tMyRank );
509
510 // receive the length
511 index_t tSize = 0;
512
513 comm_check( MPI_Irecv( &tSize,
514 1,
516 aSource,
517 tCommTag++,
518 gComm.world(),
519 & tSizeRequest ) );
520
521 // wait until receive is complete
522 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
523
524 // make sure that length is properly defined
525 BELFEM_ERROR( tSize <= aLength, "Datastream too large ( length %u but expect <=%u ).",
526 ( unsigned int ) tSize, ( unsigned int ) aLength );
527
528 // return new value
529 aLength = tSize;
530
531 // nothing to do if the vector is empty
532 if ( aLength == 0 ) return ;
533
534 // compute the chunks for this message
535 Cell< int > tChunkSizes = comm_split( tSize );
536
537 // Allocate memory for status/request vector
538 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
539 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
540
541 // initialitze the counter
542 index_t tCount = 0 ;
543
544 // offset in data container
545 index_t tOffset = 0 ;
546
547 // get the communication type
548 comm_t tCommType = comm_type< T >();
549
550 for ( index_t c : tChunkSizes )
551 {
552 // send data
553 comm_check( MPI_Irecv(
554 &aData[ tOffset ],
555 c,
556 tCommType,
557 aSource,
558 tCommTag,
559 gComm.world(),
560 & tRequest[ tCount++] ) );
561
562 // increment offset
563 tOffset+= c;
564 }
565
566 // wait until send is complete
567 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
568
569 free( tStatus );
570 free( tRequest );
571 }
572#endif
573 }
574
575//==============================================================================
576// CELLS
577//==============================================================================
578
585 template< typename T >
586 void
587 send( Cell< T > & aData, const proc_t aTarget=0 )
588 {
589#ifdef BELFEM_MPI
590 // get my id
591 proc_t tMyRank = gComm.rank();
592
593 // nothing to do here
594 if ( tMyRank == aTarget ) return ;
595
596 // get the length of the cell
597 index_t tSize = aData.size();
598
599 // status/request handlers
600 MPI_Status tSizeStatus;
601 MPI_Request tSizeRequest;
602
603 // compute the commtag
604 int tCommTag = comm_tag( tMyRank, aTarget );
605
606 // send the length
607 comm_check( MPI_Isend( &tSize,
608 1,
610 aTarget,
611 tCommTag++,
612 gComm.world(),
613 & tSizeRequest ) );
614
615 // wait until receive is complete
616 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
617
618 // nothing to do if the cell is empty
619 if ( tSize == 0 ) return ;
620
621 // get the communication type
622 comm_t tCommType = comm_type< T >();
623
624 // compute the chunks for this message
625 Cell< int > tChunkSizes = comm_split( aData.size() );
626
627 // offset in data container
628 index_t tOffset = 0 ;
629
630 // Allocate memory for status/request cell
631 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
632 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
633
634 // initialitze the counter
635 index_t tCount = 0 ;
636
637 // get the raw pointer of the cell
638 const T * tData = aData.data();
639
640 for ( index_t c : tChunkSizes )
641 {
642 // send data
643 comm_check( MPI_Isend( &tData[ tOffset ],
644 c,
645 tCommType,
646 aTarget,
647 tCommTag,
648 gComm.world(),
649 &tRequest[ tCount++ ] ) );
650
651 tOffset+= c;
652 }
653
654 // wait until send is complete
655 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
656
657 free( tStatus );
658 free( tRequest );
659#endif
660 }
661
662//------------------------------------------------------------------------------
663
670 template< typename T >
671 void
672 receive( Cell< T > & aData, const proc_t aSource=0 )
673 {
674#ifdef BELFEM_MPI
675
676 // get my id
677 proc_t tMyRank = gComm.rank();
678
679 // nothing to do here
680 if ( tMyRank == aSource ) return ;
681
682 // obtain the length of the cell
683 index_t tSize = 0 ;
684 receive( tSize, aSource );
685
686 // allocate memory for the cell
687 aData.set_size( tSize, 0 );
688
689 // nothing more to do if the cell is empty
690 if ( tSize == 0 ) return ;
691
692 // get the communication type
693 comm_t tCommType = comm_type< T >();
694
695 // compute the chunks for this message
696 Cell< int > tChunkSizes = comm_split( aData.size() );
697
698 // offset in data container
699 index_t tOffset = 0 ;
700
701 // Inside the length distrubution, we use comm_tag too.
702 // For safety, the tag for the messages is incremented.
703 int tCommTag = comm_tag( aSource, tMyRank ) + 1 ;
704
705 // Allocate memory for status/request cell
706 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
707 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
708
709 // initialitze the counter
710 index_t tCount = 0 ;
711
712 // get the raw pointer of the cell
713 T * tData = aData.data();
714
715 for ( index_t c : tChunkSizes )
716 {
717 // send data
718 comm_check( MPI_Irecv(
719 &tData[ tOffset ],
720 c,
721 tCommType,
722 aSource,
723 tCommTag,
724 gComm.world(),
725 &tRequest[ tCount++ ] ) );
726
727 tOffset+= c;
728 }
729
730 // wait until send is complete
731 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
732
733 free( tStatus );
734 free( tRequest );
735#endif
736 }
737
738//------------------------------------------------------------------------------
739
746 template< typename T >
747 void
748 broadcast( Cell< T > & aData, const proc_t aRoot=0 )
749 {
750#ifdef BELFEM_MPI
751
752 // get my id
753 proc_t tMyRank = gComm.rank();
754
755 // Get length of cell
756 index_t tSize = tMyRank == aRoot ? aData.size() : 0 ;
757
758 // communicate the cell size
759 MPI_Request tSizeRequest ;
760 comm_check( MPI_Ibcast(
761 & tSize,
762 1,
764 aRoot,
765 gComm.world(),
766 & tSizeRequest ) );
767
768 // wait until the size is received
769 MPI_Status tSizeStatus ;
770 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
771
772 // allocate memory for the cell
773 if ( tMyRank != aRoot ) aData.set_size( tSize );
774
775 if ( tSize == 0 ) return ;
776
777 // broadcast the cell data
778 MPI_Request tDataRequest ;
779 comm_check( MPI_Ibcast(
780 aData.data(),
781 tSize,
783 aRoot,
784 gComm.world(),
785 & tDataRequest ) );
786
787 // wait until data is received
788 MPI_Status tDataStatus ;
789 comm_check( MPI_Wait( &tDataRequest, &tDataStatus ) );
790
791#endif
792 }
793
794
795//------------------------------------------------------------------------------
796
802 template< typename T >
803 void
805 {
806#ifdef BELFEM_MPI
807 // get my id
808 proc_t tMyRank = gComm.rank();
809
810 // get the number of procs
811 proc_t tCommSize = gComm.size();
812
813 BELFEM_ASSERT( static_cast< proc_t >( aData.size() )== tCommSize,
814 "Length of cell does not match ( is %u, expect commsize %u ).",
815 ( unsigned int ) aData.size(), ( unsigned int ) tCommSize );
816
817 // Allocate memory for status/request cell
818 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
819 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
820
821 for( proc_t p=0; p<tCommSize; ++p )
822 {
823 if( p == tMyRank )
824 {
825 tRequest[ p ] = MPI_REQUEST_NULL;
826 continue ;
827 }
828
829 // send data
830 comm_check( MPI_Isend( &aData( p ),
831 1,
833 p,
834 comm_tag( tMyRank, p ),
835 gComm.world(),
836 &tRequest[ p ] ) );
837
838 }
839
840 // wait until send is complete
841 comm_check( MPI_Waitall( tCommSize, tRequest, tStatus ) );
842
843 // tidy up memory
844 free( tStatus );
845 free( tRequest );
846#endif
847 }
848
849//------------------------------------------------------------------------------
850
857 template< typename T >
858 void
859 collect( Cell< T > & aData, const T aMyValue = 0 )
860 {
861#ifdef BELFEM_MPI
862 // get my id
863 proc_t tMyRank = gComm.rank();
864
865 // get the number of procs
866 proc_t tCommSize = gComm.size();
867
868 aData.set_size( tCommSize, 0 );
869
870 // Allocate memory for status/request cell
871 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
872 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
873
874 for( proc_t p=0; p<tCommSize; ++p )
875 {
876 if( p == tMyRank )
877 {
878 tRequest[ p ] = MPI_REQUEST_NULL;
879 aData( p ) = aMyValue ;
880 continue ;
881 }
882
883 // send data
884 comm_check( MPI_Irecv( &aData( p ),
885 1,
887 p,
888 comm_tag( tMyRank, p ),
889 gComm.world(),
890 &tRequest[ p ] ) );
891
892
893 }
894 // wait until send is complete
895 comm_check( MPI_Waitall( tCommSize, tRequest, tStatus ) );
896
897 // tidy up memory
898 free( tStatus );
899 free( tRequest );
900#endif
901 }
902
903//==============================================================================
904// VECTORS
905//==============================================================================
906
913 template< typename T >
914 void
915 send( Vector< T > & aData, const proc_t aTarget=0 )
916 {
917#ifdef BELFEM_MPI
918 // get my id
919 proc_t tMyRank = gComm.rank();
920
921 // nothing to do here
922 if ( tMyRank == aTarget ) return ;
923
924 // get the length of the vector
925 index_t tSize = aData.length();
926
927 // status/request handlers
928 MPI_Status tSizeStatus;
929 MPI_Request tSizeRequest;
930
931 // compute the commtag
932 int tCommTag = comm_tag( tMyRank, aTarget );
933
934 // send the length
935 comm_check( MPI_Isend( &tSize,
936 1,
938 aTarget,
939 tCommTag++,
940 gComm.world(),
941 & tSizeRequest ) );
942
943 // wait until receive is complete
944 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
945
946 // nothing to do if the vector is empty
947 if ( tSize == 0 ) return ;
948
949 // get the communication type
950 comm_t tCommType = comm_type< T >();
951
952 // compute the chunks for this message
953 Cell< int > tChunkSizes = comm_split( aData.length() );
954
955 // offset in data container
956 index_t tOffset = 0 ;
957
958 // Allocate memory for status/request vector
959 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
960 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
961
962 // initialitze the counter
963 index_t tCount = 0 ;
964
965 // get the raw pointer of the vector
966 const T * tData = aData.data();
967
968 for ( index_t c : tChunkSizes )
969 {
970 // send data
971 comm_check( MPI_Isend( &tData[ tOffset ],
972 c,
973 tCommType,
974 aTarget,
975 tCommTag,
976 gComm.world(),
977 &tRequest[ tCount++ ] ) );
978
979 tOffset+= c;
980 }
981
982 // wait until send is complete
983 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
984
985 free( tStatus );
986 free( tRequest );
987#endif
988 }
989
990//------------------------------------------------------------------------------
991
998 template< typename T >
999 void
1000 receive( Vector< T > & aData, const proc_t aSource=0 )
1001 {
1002#ifdef BELFEM_MPI
1003
1004 // get my id
1005 proc_t tMyRank = gComm.rank();
1006
1007 // nothing to do here
1008 if ( tMyRank == aSource ) return ;
1009
1010 // obtain the length of the vector
1011 index_t tSize = 0 ;
1012 receive( tSize, aSource );
1013
1014 // allocate memory for the vector
1015 aData.set_size( tSize, 0 );
1016
1017 // nothing more to do if the vector is empty
1018 if ( tSize == 0 ) return ;
1019
1020 // get the communication type
1021 comm_t tCommType = comm_type< T >();
1022
1023 // compute the chunks for this message
1024 Cell< int > tChunkSizes = comm_split( aData.length() );
1025
1026 // offset in data container
1027 index_t tOffset = 0 ;
1028
1029 // Inside the length distrubution, we use comm_tag too.
1030 // For safety, the tag for the messages is incremented.
1031 int tCommTag = comm_tag( aSource, tMyRank ) + 1 ;
1032
1033 // Allocate memory for status/request vector
1034 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
1035 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
1036
1037 // initialitze the counter
1038 index_t tCount = 0 ;
1039
1040 // get the raw pointer of the vector
1041 T * tData = aData.data();
1042
1043 for ( index_t c : tChunkSizes )
1044 {
1045 // send data
1046 comm_check( MPI_Irecv(
1047 &tData[ tOffset ],
1048 c,
1049 tCommType,
1050 aSource,
1051 tCommTag,
1052 gComm.world(),
1053 &tRequest[ tCount++ ] ) );
1054
1055 tOffset+= c;
1056 }
1057
1058 // wait until send is complete
1059 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1060
1061 free( tStatus );
1062 free( tRequest );
1063#endif
1064 }
1065
1066//------------------------------------------------------------------------------
1067
1074 template< typename T >
1075 void
1076 broadcast( Vector< T > & aData, const proc_t aRoot=0 )
1077 {
1078#ifdef BELFEM_MPI
1079
1080 // get my id
1081 proc_t tMyRank = gComm.rank();
1082
1083 // Get length of vector
1084 index_t tSize = tMyRank == aRoot ? aData.length() : 0 ;
1085
1086 // communicate the vector size
1087 MPI_Request tSizeRequest ;
1088 comm_check( MPI_Ibcast(
1089 & tSize,
1090 1,
1092 aRoot,
1093 gComm.world(),
1094 & tSizeRequest ) );
1095
1096 // wait until the size is received
1097 MPI_Status tSizeStatus ;
1098 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
1099
1100 // allocate memory for the vector
1101 if ( tMyRank != aRoot ) aData.set_size( tSize );
1102
1103 if ( tSize == 0 ) return ;
1104
1105 // broadcast the vector data
1106 MPI_Request tDataRequest ;
1107 comm_check( MPI_Ibcast(
1108 aData.data(),
1109 tSize,
1111 aRoot,
1112 gComm.world(),
1113 & tDataRequest ) );
1114
1115 // wait until data is received
1116 MPI_Status tDataStatus ;
1117 comm_check( MPI_Wait( &tDataRequest, &tDataStatus ) );
1118
1119#endif
1120 }
1121
1122//------------------------------------------------------------------------------
1123
1129 template< typename T >
1130 void
1132 {
1133#ifdef BELFEM_MPI
1134 // get my id
1135 proc_t tMyRank = gComm.rank();
1136
1137 // get the number of procs
1138 proc_t tCommSize = gComm.size();
1139
1140 BELFEM_ASSERT( static_cast< proc_t>( aData.length() )== tCommSize,
1141 "Length of vector does not match ( is %u, expect commsize %u ).",
1142 ( unsigned int ) aData.length(), ( unsigned int ) tCommSize );
1143
1144 // Allocate memory for status/request vector
1145 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
1146 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
1147
1148 for( proc_t p=0; p<tCommSize; ++p )
1149 {
1150 if( p == tMyRank )
1151 {
1152 tRequest[ p ] = MPI_REQUEST_NULL;
1153 continue ;
1154 }
1155
1156 // send data
1157 comm_check( MPI_Isend( &aData( p ),
1158 1,
1160 p,
1161 comm_tag( tMyRank, p ),
1162 gComm.world(),
1163 &tRequest[ p ] ) );
1164
1165 }
1166
1167 // wait until send is complete
1168 comm_check( MPI_Waitall( tCommSize, tRequest, tStatus ) );
1169
1170 // tidy up memory
1171 free( tStatus );
1172 free( tRequest );
1173#endif
1174 }
1175
1176//------------------------------------------------------------------------------
1177
1184 template< typename T >
1185 void
1186 collect( Vector< T > & aData, const T aMyValue = 0 )
1187 {
1188#ifdef BELFEM_MPI
1189 // get my id
1190 proc_t tMyRank = gComm.rank();
1191
1192 // get the number of procs
1193 proc_t tCommSize = gComm.size();
1194
1195 aData.set_size( tCommSize, 0 );
1196
1197 // Allocate memory for status/request vector
1198 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
1199 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
1200
1201 for( proc_t p=0; p<tCommSize; ++p )
1202 {
1203 if( p == tMyRank )
1204 {
1205 tRequest[ p ] = MPI_REQUEST_NULL;
1206 aData( p ) = aMyValue ;
1207 continue ;
1208 }
1209
1210 // send data
1211 comm_check( MPI_Irecv( &aData( p ),
1212 1,
1214 p,
1215 comm_tag( tMyRank, p ),
1216 gComm.world(),
1217 &tRequest[ p ] ) );
1218
1219
1220 }
1221 // wait until send is complete
1222 comm_check( MPI_Waitall( tCommSize, tRequest, tStatus ) );
1223
1224 // tidy up memory
1225 free( tStatus );
1226 free( tRequest );
1227#endif
1228 }
1229
1230//------------------------------------------------------------------------------
1231
1237 template< typename T >
1238 void
1240 {
1241#ifdef BELFEM_MPI
1242
1243 // get my id
1244 proc_t tMyRank = gComm.rank();
1245
1246 // get the number of procs
1247 proc_t tCommSize = gComm.size();
1248
1249 BELFEM_ASSERT( static_cast< proc_t>( aData.size() )== tCommSize,
1250 "Length of data container does not match ( is %u, expect commsize %u ).",
1251 ( unsigned int ) aData.size(), ( unsigned int ) tCommSize );
1252
1253 // populate the vector lengths
1254 Vector< index_t > tSizes( tCommSize, 0 );
1255 for ( proc_t p=0; p<tCommSize; ++p )
1256 {
1257 tSizes( p ) = aData( p ).length();
1258 }
1259 index_t tCount = comm_splitcount( tSizes, tMyRank );
1260
1261 // send the vector length to the other procs
1262 distribute( tSizes );
1263
1264 // Allocate memory for request/status vector
1265 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
1266 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
1267
1268
1269 // resetting the counter
1270 tCount = 0 ;
1271
1272 // get the communication type
1273 comm_t tCommType = comm_type< T >();
1274
1275 for( proc_t p=0; p<tCommSize; ++p )
1276 {
1277 // no message if self or there is nothing to send
1278 if ( p == tMyRank || aData( p ).length() == 0 ) continue ;
1279
1280 // get the raw pointer of the container
1281 const T * tData = aData( p ).data();
1282
1283 // compute the chunks for this message
1284 Cell< int > tChunkSizes = comm_split( aData( p ).length() );
1285
1286 // offset in data container
1287 index_t tOffset = 0 ;
1288
1289 // Inside the length distrubution, we use comm_tag too.
1290 // For safety, the tag for the messages is incremented.
1291 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1292
1293 for ( index_t c : tChunkSizes )
1294 {
1295 // send data
1296 comm_check( MPI_Isend( &tData[ tOffset ],
1297 c,
1298 tCommType,
1299 p,
1300 tCommTag,
1301 gComm.world(),
1302 &tRequest[ tCount++ ] ) );
1303
1304 tOffset+= c;
1305 }
1306 }
1307
1308 // wait until send is complete
1309 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1310
1311 // tidy up memory
1312 free( tStatus );
1313 free( tRequest );
1314
1315#endif
1316 }
1317
1323 template< typename T >
1324 void
1326 {
1327#ifdef BELFEM_MPI
1328
1329 // get my id
1330 proc_t tMyRank = gComm.rank();
1331
1332 // get the number of procs
1333 proc_t tCommSize = gComm.size();
1334
1335 BELFEM_ASSERT( static_cast< proc_t>( aData.size() )== tCommSize,
1336 "Length of data container does not match ( is %u, expect commsize %u ).",
1337 ( unsigned int ) aData.size(), ( unsigned int ) tCommSize );
1338
1339 // populate the vector lengths
1340 Vector< index_t > tSizes( tCommSize, 0 );
1341 for ( proc_t p=0; p<tCommSize; ++p )
1342 {
1343 tSizes( p ) = aData( p ).size();
1344 }
1345 index_t tCount = comm_splitcount( tSizes, tMyRank );
1346
1347 // send the vector length to the other procs
1348 distribute( tSizes );
1349
1350 // Allocate memory for request/status vector
1351 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
1352 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
1353
1354
1355 // resetting the counter
1356 tCount = 0 ;
1357
1358 // get the communication type
1359 comm_t tCommType = comm_type< T >();
1360
1361 for( proc_t p=0; p<tCommSize; ++p )
1362 {
1363 // no message if self or there is nothing to send
1364 if ( p == tMyRank || aData( p ).size() == 0 ) continue ;
1365
1366 // get the raw pointer of the container
1367 const T * tData = aData( p ).data();
1368
1369 // compute the chunks for this message
1370 Cell< int > tChunkSizes = comm_split( aData( p ).size() );
1371
1372 // offset in data container
1373 index_t tOffset = 0 ;
1374
1375 // Inside the length distrubution, we use comm_tag too.
1376 // For safety, the tag for the messages is incremented.
1377 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1378
1379 for ( index_t c : tChunkSizes )
1380 {
1381 // send data
1382 comm_check( MPI_Isend( &tData[ tOffset ],
1383 c,
1384 tCommType,
1385 p,
1386 tCommTag,
1387 gComm.world(),
1388 &tRequest[ tCount++ ] ) );
1389
1390 tOffset+= c;
1391 }
1392 }
1393
1394 // wait until send is complete
1395 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1396
1397 // tidy up memory
1398 free( tStatus );
1399 free( tRequest );
1400
1401#endif
1402 }
1403
1404//------------------------------------------------------------------------------
1405
1412 template< typename T, typename U >
1413 void
1414 distribute( const T * aData, const Vector< U > & aOffsets )
1415 {
1416#ifdef BELFEM_MPI
1417
1418 // get my id
1419 proc_t tMyRank = gComm.rank();
1420
1421 // get the number of procs
1422 proc_t tCommSize = gComm.size();
1423
1424 BELFEM_ASSERT( static_cast< proc_t>( aOffsets.length() ) == tCommSize + 1,
1425 "Length of offset container does not match ( is %u, expect commsize %u ).",
1426 ( unsigned int ) aOffsets.length(), ( unsigned int ) tCommSize + 1 );
1427
1428
1429 // populate the vector lengths
1430 Vector< index_t > tSizes( tCommSize, 0 );
1431
1432 for ( proc_t p=0; p<tCommSize; ++p )
1433 {
1434 tSizes( p ) = aOffsets( p+1 ) - aOffsets( p );
1435 }
1436 index_t tCount = comm_splitcount( tSizes, tMyRank );
1437
1438 // send the vector length to the other procs
1439 distribute( tSizes );
1440
1441 // Allocate memory for request/status vector
1442 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
1443 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
1444
1445
1446 // resetting the counter
1447 tCount = 0 ;
1448
1449 // get the communication type (strip const qualifier if present)
1451
1452 for( proc_t p=0; p<tCommSize; ++p )
1453 {
1454 // no message if self or there is nothing to send
1455 if ( p == tMyRank || tSizes( p ) == 0 ) continue ;
1456
1457 // compute the chunks for this message
1458 Cell< int > tChunkSizes = comm_split( tSizes( p ) );
1459
1460 // offset in data container
1461 index_t tOffset = aOffsets( p ) ;
1462
1463 // Inside the length distrubution, we use comm_tag too.
1464 // For safety, the tag for the messages is incremented.
1465 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1466
1467 for ( index_t c : tChunkSizes )
1468 {
1469 // send data
1470 comm_check( MPI_Isend( aData + tOffset,
1471 c,
1472 tCommType,
1473 p,
1474 tCommTag,
1475 gComm.world(),
1476 &tRequest[ tCount++ ] ) );
1477
1478 tOffset+= c;
1479 }
1480 }
1481
1482 // wait until send is complete
1483 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1484
1485 // tidy up memory
1486 free( tStatus );
1487 free( tRequest );
1488
1489#endif
1490 }
1491
1492 template< typename T, typename U >
1493 void
1494 collect( T * aData, const Vector< U > & aOffsets )
1495 {
1496#ifdef BELFEM_MPI
1497 // get my id
1498 proc_t tMyRank = gComm.rank();
1499
1500 // get the number of procs
1501 proc_t tCommSize = gComm.size();
1502
1503 // obtain the vector lengths from non-root ranks
1504 Vector< index_t > tSizes ;
1505 collect( tSizes );
1506 // rank 0's data is already in aData; set its size from offsets for consistency
1507 tSizes( 0 ) = aOffsets( 0 );
1508
1509 index_t tCount = 0 ;
1510
1511 for ( proc_t p=1; p<tCommSize; ++p )
1512 {
1513 tCount += comm_split( tSizes( p ) ).size();
1514 }
1515
1516 // Allocate memory for status/request vector
1517 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCount );
1518 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCount );
1519
1520 // resetting the counter
1521 tCount = 0 ;
1522
1523 // get the communication type
1524 comm_t tCommType = comm_type< T >();
1525
1526 for( proc_t p=1; p<tCommSize; ++p )
1527 {
1528 // get the length
1529 index_t tSize = tSizes( p );
1530
1531 if ( tSize == 0 ) continue;
1532
1533 // compute the chunks for this message
1534 Cell< int > tChunkSizes = comm_split( tSize );
1535
1536 // offset in data container
1537 index_t tOffset = aOffsets( p ) ;
1538
1539 // Inside the length distribution, we use comm_tag too.
1540 // For safety, the tag for the messages is incremented.
1541 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1542
1543 for ( index_t c : tChunkSizes )
1544 {
1545 // receive data
1546 comm_check( MPI_Irecv( &aData[ tOffset ],
1547 c,
1548 tCommType,
1549 p,
1550 tCommTag,
1551 gComm.world(),
1552 &tRequest[ tCount++ ] ) );
1553
1554 tOffset+= c;
1555 }
1556 }
1557
1558 // wait until send is complete
1559 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1560
1561 // tidy up memory
1562 free( tStatus );
1563 free( tRequest );
1564#endif
1565 }
1566//------------------------------------------------------------------------------
1567
1568
1575 template< typename T >
1576 void
1577 collect( Cell< Vector< T > > & aData, Vector< T > aMyData={} )
1578 {
1579#ifdef BELFEM_MPI
1580
1581 // get my id
1582 proc_t tMyRank = gComm.rank();
1583
1584 // get the number of procs
1585 proc_t tCommSize = gComm.size();
1586
1587 // allocate memory
1588 aData.set_size( tCommSize, {} );
1589
1590 // obtain the vector lengths
1591 Vector< index_t > tSizes ;
1592 collect( tSizes );
1593
1594 index_t tCount = 0 ;
1595 for ( proc_t p=0; p<tCommSize; ++p )
1596 {
1597 if ( p == tMyRank )
1598 {
1599 if( aMyData.length() > 0 )
1600 {
1601 aData( p ).vector_data() = std::move( aMyData.vector_data() ) ;
1602 }
1603 continue ;
1604 }
1605
1606 // allocate memory of vector
1607 aData( p ).set_size( tSizes( p ), 0 );
1608
1609 tCount += comm_split( tSizes( p ) ).size();
1610 }
1611
1612 // Allocate memory for status/request vector
1613 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCount );
1614 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCount );
1615
1616 // resetting the counter
1617 tCount = 0 ;
1618
1619 // get the communication type
1620 comm_t tCommType = comm_type< T >();
1621
1622 for( proc_t p=0; p<tCommSize; ++p )
1623 {
1624 // get the length
1625 index_t tSize = tSizes( p );
1626
1627 if ( p == tMyRank || tSize == 0 ) continue;
1628
1629 // get the raw data container
1630 T * tData = aData( p ).data() ;
1631
1632 // compute the chunks for this message
1633 Cell< int > tChunkSizes = comm_split( tSize );
1634
1635 // offset in data container
1636 index_t tOffset = 0 ;
1637
1638 // Inside the length distribution, we use comm_tag too.
1639 // For safety, the tag for the messages is incremented.
1640 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1641
1642 for ( index_t c : tChunkSizes )
1643 {
1644 // receive data
1645 comm_check( MPI_Irecv( &tData[ tOffset ],
1646 c,
1647 tCommType,
1648 p,
1649 tCommTag,
1650 gComm.world(),
1651 &tRequest[ tCount++ ] ) );
1652
1653 tOffset+= c;
1654 }
1655 }
1656
1657 // wait until send is complete
1658 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1659
1660 // tidy up memory
1661 free( tStatus );
1662 free( tRequest );
1663
1664#endif
1665 }
1666
1667//------------------------------------------------------------------------------
1668
1669 template< typename T >
1670 void
1672 {
1673#ifdef BELFEM_MPI
1674 // get my id
1675 proc_t tMyRank = gComm.rank();
1676
1677 proc_t tCommSize = gComm.size();
1678
1679 index_t tSize = aData.length();
1680
1681 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * ( tCommSize-1 ) );
1682 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * ( tCommSize-1 ) );
1683
1684 // get the communication type
1685 comm_t tIndex_t = comm_type< index_t >();
1686
1687 // get the communication type
1688 comm_t tCommType = comm_type< T >();
1689
1690 index_t tCount = 0 ;
1691
1692 for ( proc_t p=0; p<tCommSize; ++p )
1693 {
1694 if ( p == tMyRank ) continue;
1695
1696 int tCommTag = comm_tag( tMyRank, p );
1697
1698 comm_check( MPI_Isend( &tSize,
1699 1,
1700 tIndex_t,
1701 p,
1702 tCommTag,
1703 gComm.world(),
1704 &tRequest[ tCount ++ ] ) );
1705 }
1706
1707 // wait until send is complete
1708 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1709
1710 // tidy up memory
1711 free( tStatus );
1712 free( tRequest );
1713
1714 tCount = comm_splitcount( tSize );
1715
1716 tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
1717 tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
1718
1719 if ( aData.length() > 0 )
1720 {
1721 tCount = 0 ;
1722 for( proc_t p=0; p<tCommSize; ++p )
1723 {
1724 // no message if self or there is nothing to send
1725 if ( p == tMyRank ) continue ;
1726
1727 // get the raw pointer of the container
1728 const T * tData = aData.data();
1729
1730 // compute the chunks for this message
1731 Cell< int > tChunkSizes = comm_split( aData.length() );
1732
1733 // offset in data container
1734 index_t tOffset = 0 ;
1735
1736 // Inside the length distrubution, we use comm_tag too.
1737 // For safety, the tag for the messages is incremented.
1738 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1739
1740 for ( index_t c : tChunkSizes )
1741 {
1742 // send data
1743 comm_check( MPI_Isend( &tData[ tOffset ],
1744 c,
1745 tCommType,
1746 p,
1747 tCommTag,
1748 gComm.world(),
1749 &tRequest[ tCount++ ] ) );
1750
1751 tOffset+= c;
1752 }
1753 }
1754
1755 // wait until send is complete
1756 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1757 }
1758
1759
1760
1761 // tidy up memory
1762 free( tStatus );
1763 free( tRequest );
1764#endif
1765 }
1766
1767//------------------------------------------------------------------------------
1768
1769 template< typename T >
1770 void
1771 share( Cell< T > & aData )
1772 {
1773#ifdef BELFEM_MPI
1774 // get my id
1775 proc_t tMyRank = gComm.rank();
1776
1777 proc_t tCommSize = gComm.size();
1778
1779 index_t tSize = aData.size();
1780
1781 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * ( tCommSize-1 ) );
1782 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * ( tCommSize-1 ) );
1783
1784 // get the communication type
1785 comm_t tIndex_t = comm_type< index_t >();
1786
1787 // get the communication type
1788 comm_t tCommType = comm_type< T >();
1789
1790 index_t tCount = 0 ;
1791
1792 for ( proc_t p=0; p<tCommSize; ++p )
1793 {
1794 if ( p == tMyRank ) continue;
1795
1796 int tCommTag = comm_tag( tMyRank, p );
1797
1798 comm_check( MPI_Isend( &tSize,
1799 1,
1800 tIndex_t,
1801 p,
1802 tCommTag,
1803 gComm.world(),
1804 &tRequest[ tCount ++ ] ) );
1805 }
1806
1807 // wait until send is complete
1808 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1809
1810 // tidy up memory
1811 free( tStatus );
1812 free( tRequest );
1813
1814 tCount = comm_splitcount( tSize );
1815
1816 tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
1817 tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
1818
1819 if ( aData.size() > 0 )
1820 {
1821 tCount = 0 ;
1822 for( proc_t p=0; p<tCommSize; ++p )
1823 {
1824 // no message if self or there is nothing to send
1825 if ( p == tMyRank ) continue ;
1826
1827 // get the raw pointer of the container
1828 const T * tData = aData.data();
1829
1830 // compute the chunks for this message
1831 Cell< int > tChunkSizes = comm_split( aData.size() );
1832
1833 // offset in data container
1834 index_t tOffset = 0 ;
1835
1836 // Inside the length distrubution, we use comm_tag too.
1837 // For safety, the tag for the messages is incremented.
1838 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
1839
1840 for ( index_t c : tChunkSizes )
1841 {
1842 // send data
1843 comm_check( MPI_Isend( &tData[ tOffset ],
1844 c,
1845 tCommType,
1846 p,
1847 tCommTag,
1848 gComm.world(),
1849 &tRequest[ tCount++ ] ) );
1850
1851 tOffset+= c;
1852 }
1853 }
1854
1855 // wait until send is complete
1856 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
1857 }
1858
1859
1860
1861 // tidy up memory
1862 free( tStatus );
1863 free( tRequest );
1864#endif
1865 }
1866
1867//==============================================================================
1868// MATRICES
1869//==============================================================================
1870
1877 template< typename T >
1878 void
1879 broadcast( Matrix< T > & aData, const proc_t aRoot=0 )
1880 {
1881#ifdef BELFEM_MPI
1882
1883 // get my id
1884 proc_t tMyRank = gComm.rank();
1885
1886 // row, columns and transfer length of matrix
1887 // We transmit spacing()*n_cols rather than n_rows*n_cols so that the
1888 // raw contiguous buffer (including backend padding, e.g. Blaze
1889 // SIMD alignment) is transferred as-is. This avoids disassembling
1890 // and reassembling the matrix. Both ranks use the same binary, so
1891 // padding layout is identical for a given (rows, cols) pair.
1892 // Never use capacity() here: a matrix that shrank keeps its old,
1893 // larger allocation, and transmitting that count overflows the
1894 // exact-fit buffer on the receiving side.
1895 index_t tSize[ 3 ];
1896
1897 tSize[ 0 ] = aData.n_rows();
1898 tSize[ 1 ] = aData.n_cols();
1899 tSize[ 2 ] = aData.spacing() * aData.n_cols();
1900
1901 // broadcast the matrix dimensions and transfer length
1902 MPI_Request tSizeRequest ;
1903 comm_check( MPI_Ibcast(
1904 & tSize,
1905 3,
1907 aRoot,
1908 gComm.world(),
1909 & tSizeRequest ) );
1910
1911 // wait until size is received
1912 MPI_Status tSizeStatus ;
1913 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
1914
1915 // allocate memory for the matrix
1916 if ( tMyRank != aRoot ) aData.set_size( tSize[ 0 ], tSize[ 1 ] );
1917
1918 // nothing to do if the matrix is empty
1919 if( tSize[ 0 ] == 0 || tSize[ 1 ] == 0 ) return ;
1920
1921 BELFEM_ERROR( aData.capacity() >= tSize[ 2 ],
1922 "broadcast( Matrix ) : transfer length %lu exceeds local buffer capacity %lu",
1923 ( long unsigned int ) tSize[ 2 ],
1924 ( long unsigned int ) aData.capacity() );
1925
1926 // broadcast is unchunked: the count must fit into MPI's int
1927 BELFEM_ERROR( tSize[ 2 ] <= ( index_t ) std::numeric_limits< int >::max(),
1928 "broadcast( Matrix ) : matrix too large for unchunked broadcast, use send/receive" );
1929
1930 // broadcast the matrix data
1931 MPI_Request tDataRequest ;
1932 comm_check( MPI_Ibcast(
1933 aData.data(),
1934 tSize[ 2 ],
1936 aRoot,
1937 gComm.world(),
1938 & tDataRequest ) );
1939
1940 // wait until data is received
1941 MPI_Status tDataStatus ;
1942 comm_check( MPI_Wait( &tDataRequest, &tDataStatus ) );
1943
1944#endif
1945 }
1946
1947 template< typename T >
1948 void
1949 send( Matrix< T > & aData, const proc_t aTarget=0 )
1950 {
1951#ifdef BELFEM_MPI
1952
1953 // get my id
1954 proc_t tMyRank = gComm.rank();
1955
1956 // nothing to do here
1957 if ( tMyRank == aTarget ) return ;
1958
1959 // row, columns and transfer length of matrix.
1960 // We transmit spacing()*n_cols rather than n_rows*n_cols so that the
1961 // raw contiguous buffer (including backend padding, e.g. Blaze
1962 // SIMD alignment) is transferred as-is. This avoids disassembling
1963 // and reassembling the matrix. Both ranks use the same binary, so
1964 // padding layout is identical for a given (rows, cols) pair.
1965 // Never use capacity() here: a matrix that shrank keeps its old,
1966 // larger allocation, and transmitting that count overflows the
1967 // exact-fit buffer on the receiving side.
1968 index_t tSize[ 3 ];
1969
1970 tSize[ 0 ] = aData.n_rows();
1971 tSize[ 1 ] = aData.n_cols();
1972 tSize[ 2 ] = aData.spacing() * aData.n_cols();
1973
1974 // status/request handlers
1975 MPI_Status tSizeStatus;
1976 MPI_Request tSizeRequest;
1977
1978 // compute the commtag
1979 int tCommTag = comm_tag( tMyRank, aTarget );
1980
1981 // send the length
1982 comm_check( MPI_Isend( &tSize,
1983 3,
1985 aTarget,
1986 tCommTag++,
1987 gComm.world(),
1988 & tSizeRequest ) );
1989
1990 // wait until receive is complete
1991 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
1992
1993 // nothing to do if the matrix is empty
1994 if( tSize[ 0 ] == 0 || tSize[ 1 ] == 0 ) return ;
1995
1996 // get the communication type
1997 comm_t tCommType = comm_type< T >();
1998
1999 // compute the chunks for this message
2000 Cell< int > tChunkSizes = comm_split( tSize[ 2 ] );
2001
2002 // offset in data container
2003 index_t tOffset = 0 ;
2004
2005 // Allocate memory for status/request vector
2006 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
2007 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
2008
2009 // initialitze the counter
2010 index_t tCount = 0 ;
2011
2012 // get raw pointer of matrix
2013 const T * tData = aData.data();
2014
2015 for ( index_t c : tChunkSizes )
2016 {
2017 // send data
2018 comm_check( MPI_Isend( &tData[ tOffset ],
2019 c,
2020 tCommType,
2021 aTarget,
2022 tCommTag,
2023 gComm.world(),
2024 &tRequest[ tCount++ ] ) );
2025
2026 tOffset+= c;
2027 }
2028
2029 // wait until send is complete
2030 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
2031
2032 free( tStatus );
2033 free( tRequest );
2034#endif
2035 }
2036
2037//------------------------------------------------------------------------------
2038
2045 template< typename T >
2046 void
2047 receive( Matrix< T > & aData, const proc_t aSource=0 )
2048 {
2049#ifdef BELFEM_MPI
2050
2051 // get my id
2052 proc_t tMyRank = gComm.rank();
2053
2054 // nothing to do here
2055 if ( tMyRank == aSource ) return ;
2056
2057 // status/request handlers
2058 MPI_Status tSizeStatus;
2059 MPI_Request tSizeRequest;
2060
2061 // compute the commtag
2062 int tCommTag = comm_tag( tMyRank, aSource );
2063
2064 // tSize[2] is the sender's spacing()*n_cols, not n_rows*n_cols. We
2065 // transfer the raw contiguous buffer including backend padding
2066 // (e.g. Blaze SIMD alignment) as-is, avoiding matrix disassembly/
2067 // reassembly. Both ranks use the same binary, so padding layout is
2068 // identical for a given (rows, cols) pair, and after set_size below
2069 // our own buffer holds at least spacing()*n_cols elements.
2070 index_t tSize[ 3 ];
2071
2072 // receive the dimensions
2073 comm_check( MPI_Irecv( tSize,
2074 3,
2076 aSource,
2077 tCommTag++,
2078 gComm.world(),
2079 & tSizeRequest ) );
2080
2081 // wait until receive is complete
2082 comm_check( MPI_Wait( &tSizeRequest, &tSizeStatus ) );
2083
2084 aData.set_size( tSize[ 0 ], tSize[ 1 ] );
2085
2086 // nothing more to do if the matrix is empty
2087 if ( tSize[ 0 ] == 0 || tSize[ 1 ] == 0 ) return ;
2088
2089 BELFEM_ERROR( aData.capacity() >= tSize[ 2 ],
2090 "receive( Matrix ) : transfer length %lu exceeds local buffer capacity %lu",
2091 ( long unsigned int ) tSize[ 2 ],
2092 ( long unsigned int ) aData.capacity() );
2093
2094 // get the communication type
2095 comm_t tCommType = comm_type< T >();
2096
2097 // compute the chunks for this message
2098 Cell< int > tChunkSizes = comm_split( tSize[ 2 ] );
2099
2100 // offset in data container
2101 index_t tOffset = 0 ;
2102
2103 // Allocate memory for request/status vector
2104 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tChunkSizes.size() );
2105 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tChunkSizes.size() );
2106
2107 // initialitze the counter
2108 index_t tCount = 0 ;
2109
2110 // get the raw pointer of the vector
2111 T * tData = aData.data();
2112
2113 for ( index_t c : tChunkSizes )
2114 {
2115 // send data
2116 comm_check( MPI_Irecv( &tData[ tOffset ],
2117 c,
2118 tCommType,
2119 aSource,
2120 tCommTag,
2121 gComm.world(),
2122 &tRequest[ tCount++ ] ) );
2123
2124 tOffset+= c;
2125 }
2126
2127 // wait until send is complete
2128 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
2129
2130 free( tStatus );
2131 free( tRequest );
2132#endif
2133 }
2134
2135//------------------------------------------------------------------------------
2136
2142 template< typename T >
2143 void
2145 {
2146#ifdef BELFEM_MPI
2147
2148 // get my id
2149 proc_t tMyRank = gComm.rank();
2150
2151 // get the number of procs
2152 proc_t tCommSize = gComm.size();
2153
2154 BELFEM_ASSERT( static_cast< proc_t>( aData.size() )== tCommSize,
2155 "Length of data container does not match ( is %u, expect commsize %u ).",
2156 ( unsigned int ) aData.size(), ( unsigned int ) tCommSize );
2157
2158 // populate the matrix lengths
2159 index_t * tSizes = ( index_t * ) malloc( sizeof( index_t ) * tCommSize * 3 );
2160 index_t tCount = 0 ;
2161 for ( proc_t p=0; p<tCommSize; ++p )
2162 {
2163 tSizes[ tCount++ ] = aData( p ).n_rows();
2164 tSizes[ tCount++ ] = aData( p ).n_cols();
2165
2166 // transfer length: padded footprint of the current shape,
2167 // NOT capacity() ( which can be stale-large after a shrink )
2168 tSizes[ tCount++ ] = aData( p ).spacing() * aData( p ).n_cols();
2169 }
2170
2171 MPI_Request* tSizeRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
2172 MPI_Status* tSizeStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
2173
2174 // send the data
2175 index_t tOffset = 0 ;
2176 for ( proc_t p=0; p<tCommSize; ++p )
2177 {
2178 if ( p == tMyRank )
2179 {
2180 tSizeRequest[ p ] = MPI_REQUEST_NULL;
2181 tOffset += 3 ;
2182 continue;
2183 }
2184
2185 // send data
2186 comm_check( MPI_Isend( &tSizes[ tOffset ],
2187 3,
2189 p,
2190 comm_tag( tMyRank, p ),
2191 gComm.world(),
2192 &tSizeRequest[ p ] ) );
2193
2194 tOffset += 3 ;
2195 }
2196
2197 // wait until send is complete
2198 comm_check( MPI_Waitall( tCommSize, tSizeRequest, tSizeStatus ) );
2199 free( tSizeRequest );
2200 free( tSizeStatus );
2201
2202 // count the chunk sizes
2203 tOffset = 2 ;
2204 tCount = 0 ;
2205 for ( proc_t p=0; p<tCommSize; ++p )
2206 {
2207 // no message if self or there is nothing to send
2208 if ( p == tMyRank || aData( p ).n_rows() == 0 || aData( p ).n_cols() == 0 )
2209 {
2210 tOffset += 3 ;
2211 continue ;
2212 }
2213 tCount += comm_split( tSizes[ tOffset ] ).size();
2214 tOffset += 3 ;
2215 }
2216
2217 // clear memory
2218 free( tSizes );
2219
2220
2221 // Allocate memory for status/request vector
2222 MPI_Status* tStatus = ( MPI_Status * ) malloc( sizeof( MPI_Status ) * tCount );
2223 MPI_Request* tRequest = ( MPI_Request * ) malloc( sizeof( MPI_Request ) * tCount );
2224
2225 // resetting the counter
2226 tCount = 0 ;
2227
2228 // get the communication type
2229 comm_t tCommType = comm_type< T >();
2230
2231 for( proc_t p=0; p<tCommSize; ++p )
2232 {
2233 // no message to self or if there is nothing to send
2234 if ( p == tMyRank || aData( p ).n_rows() == 0 || aData( p ).n_cols() == 0 )
2235 {
2236 continue;
2237 }
2238
2239 // get the raw pointer of the contaoner
2240 const T * tData = aData( p ).data();
2241
2242 // compute the chunks for this message ( same transfer length
2243 // as announced in tSizes above — never capacity() )
2244 Cell< int > tChunkSizes = comm_split( aData( p ).spacing() * aData( p ).n_cols() );
2245
2246 // offset in data container
2247 index_t tDataOffset = 0 ;
2248 // Inside the length distrubution, we use comm_tag too.
2249 // For safety, the tag for the messages is incremented.
2250 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
2251
2252 for ( index_t c : tChunkSizes )
2253 {
2254 // send data
2255 comm_check( MPI_Isend(
2256 &tData[ tDataOffset ],
2257 c,
2258 tCommType,
2259 p,
2260 tCommTag,
2261 gComm.world(),
2262 &tRequest[ tCount++ ] ) );
2263
2264 tDataOffset+= c;
2265 }
2266 }
2267
2268 // wait until send is complete
2269 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
2270
2271 // tidy up memory
2272 free( tStatus );
2273 free( tRequest );
2274
2275#endif
2276 }
2277
2278//------------------------------------------------------------------------------
2279
2285 template< typename T >
2286 void
2288 {
2289#ifdef BELFEM_MPI
2290
2291 // get my id
2292 proc_t tMyRank = gComm.rank();
2293
2294 // get the number of procs
2295 proc_t tCommSize = gComm.size();
2296
2297 // allocate memory
2298 aData.set_size( tCommSize, {} );
2299
2300 // obtain the matrix lengths
2301 index_t * tSizes = ( index_t * ) malloc( sizeof( index_t ) * tCommSize * 3 );
2302
2303
2304 MPI_Request* tSizeRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCommSize );
2305 MPI_Status* tSizeStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCommSize );
2306
2307 index_t tOffset = 0 ;
2308
2309 for ( proc_t p=0; p<tCommSize; ++p )
2310 {
2311 if ( p == tMyRank )
2312 {
2313 tSizeRequest[ p ] = MPI_REQUEST_NULL;
2314 tSizes[ tOffset++ ] = 0 ;
2315 tSizes[ tOffset++ ] = 0 ;
2316 tSizes[ tOffset++ ] = 0 ;
2317 continue ;
2318 }
2319
2320 // receive data
2321 comm_check( MPI_Irecv( &tSizes[ tOffset ],
2322 3,
2324 p,
2325 comm_tag( tMyRank, p ),
2326 gComm.world(),
2327 &tSizeRequest[ p ] ) );
2328
2329 tOffset += 3 ;
2330 }
2331
2332 // wait until send is complete
2333 comm_check( MPI_Waitall( tCommSize, tSizeRequest, tSizeStatus ) );
2334
2335 free( tSizeStatus );
2336 free( tSizeRequest );
2337
2338 index_t tCount = 0 ;
2339 tOffset = 0 ;
2340
2341 for ( proc_t p=0; p<tCommSize; ++p )
2342 {
2343 // skip if self
2344 if ( p == tMyRank )
2345 {
2346 tOffset += 3 ;
2347 continue ;
2348 }
2349
2350 aData( p ).set_size( tSizes[ tOffset ], tSizes[ tOffset+1 ] );
2351
2352 // skip if empty
2353 if ( tSizes[ tOffset ] == 0 || tSizes[ tOffset+1 ] == 0 )
2354 {
2355 tOffset += 3 ;
2356 continue ;
2357 }
2358
2359 // count memory needs
2360 tCount += comm_split( tSizes[ tOffset + 2 ] ).size();
2361 tOffset += 3 ;
2362 }
2363
2364 // Allocate memory for status/request vector
2365 MPI_Status* tStatus = ( MPI_Status* ) malloc( sizeof( MPI_Status ) * tCount );
2366 MPI_Request* tRequest = ( MPI_Request* ) malloc( sizeof( MPI_Request ) * tCount );
2367
2368
2369 // get the communication type
2370 comm_t tCommType = comm_type< T >();
2371
2372 // resetting the counter
2373 tCount = 0 ;
2374
2375 tOffset = 0 ;
2376
2377 for( proc_t p=0; p<tCommSize; ++p )
2378 {
2379 // no message to self or if there is nothing to receive
2380 if ( p == tMyRank )
2381 {
2382 tOffset += 3 ;
2383 continue;
2384 }
2385 if ( aData( p ).n_rows() == 0 || aData( p ).n_cols() == 0 )
2386 {
2387 tOffset += 3 ;
2388 continue ;
2389 }
2390
2391 BELFEM_ERROR( aData( p ).capacity() >= tSizes[ tOffset + 2 ],
2392 "collect( Matrix ) : transfer length %lu from proc %u exceeds local buffer capacity %lu",
2393 ( long unsigned int ) tSizes[ tOffset + 2 ],
2394 ( unsigned int ) p,
2395 ( long unsigned int ) aData( p ).capacity() );
2396
2397 // compute the chunks for this message
2398 Cell< int > tChunkSizes = comm_split( tSizes[ tOffset + 2 ] );
2399
2400 // get the raw data container
2401 T * tData = aData( p ).data() ;
2402
2403 // offset in data container
2404 index_t tDataOffset = 0 ;
2405
2406 // Inside the length distribution, we use comm_tag too.
2407 // For safety, the tag for the messages is incremented.
2408 int tCommTag = comm_tag( tMyRank, p ) + 1 ;
2409
2410 for ( index_t c : tChunkSizes )
2411 {
2412 // receive data
2413 comm_check( MPI_Irecv( &tData[ tDataOffset ],
2414 c,
2415 tCommType,
2416 p,
2417 tCommTag,
2418 gComm.world(),
2419 &tRequest[ tCount++ ] ) );
2420
2421 tDataOffset+= c;
2422 }
2423
2424 tOffset += 3 ;
2425 }
2426
2427 // wait until send is complete
2428 comm_check( MPI_Waitall( tCount, tRequest, tStatus ) );
2429
2430 // tidy up memory
2431 free( tStatus );
2432 free( tRequest );
2433 free( tSizes );
2434
2435
2436#endif
2437 }
2438
2439//==============================================================================
2440// STRINGS
2441//==============================================================================
2442
2443 void
2444 broadcast( Cell< string > & aData, const proc_t aRoot=0 );
2445
2446 void
2447 send( const string & aMessage, const proc_t aTarget=0 );
2448
2449 void
2450 receive( string & aMessage, const proc_t aSource=0 );
2451
2452//------------------------------------------------------------------------------
2453}
2454#endif //COMMTOOLS_HPP
#define BELFEM_ERROR(aCheck,...)
Definition assert.hpp:264
#define BELFEM_ASSERT(aCheck,...)
Definition assert.hpp:244
belfem::Communicator gComm
Definition belfem.cpp:35
Cell is a wrapper around the standard vector.
Definition cl_Cell.hpp:42
void set_size(const size_t aSize)
Definition cl_Cell.hpp:189
size_t size() const
return the size of the Cell
Definition cl_Cell.hpp:181
T * data()
Definition cl_Cell.hpp:120
Global MPI communicator manager.
Definition cl_Communicator.hpp:60
COMM_TYPE & world()
Definition cl_Communicator.cpp:249
proc_t rank() const
Definition cl_Communicator.hpp:192
proc_t size() const
Definition cl_Communicator.hpp:200
Dense column-major matrix.
Definition cl_BZ_Matrix.hpp:28
size_t spacing() const
inter-column stride of the data container ( the leading dimension in BLAS terms; here always n_rows )
Definition cl_AR_Matrix.hpp:236
size_t capacity() const
length of data container
Definition cl_AR_Matrix.hpp:224
void set_size(const size_t aNumRows, const size_t aNumCols)
Definition cl_AR_Matrix.hpp:186
size_t n_rows() const
Definition cl_AR_Matrix.hpp:205
size_t n_cols() const
Definition cl_AR_Matrix.hpp:213
T * data()
Definition cl_AR_Matrix.hpp:135
Column vector.
Definition cl_BZ_Vector.hpp:41
T * data()
expose the underlying raw pointer ( writable version )
Definition cl_AR_Vector.hpp:182
void set_size(const size_t aNumRows)
change the size of the vector
Definition cl_AR_Vector.hpp:237
size_t length() const
get the length of the vector
Definition cl_AR_Vector.hpp:257
const real c
speed of light in m/s ( exact ) http://physics.nist.gov/cgi-bin/cuu/Value?c
Definition constants.hpp:65
USER GUIDES:
Definition cl_Capacitor.cpp:16
proc_t comm_rank()
Returns the rank of the current process in the communicator.
Definition commtools.cpp:30
void receive(string &aMessage, const proc_t aSource)
Definition commtools.cpp:348
comm_t comm_type()
returns the MPI datatype handle for T
Definition commtypes.hpp:37
void share(Vector< T > &aData)
Definition commtools.hpp:1671
void collect(Cell< T > &aData, const T aMyValue=0)
Collects scalar values from all processes into a cell.
Definition commtools.hpp:859
@ T
Definition cl_Material.hpp:122
void comm_barrier()
Synchronizes all processes in the communicator.
Definition commtools.cpp:57
int comm_t
Definition commtypes.hpp:27
void comm_drain_check(const char *aLabel)
Debug-build tripwire for the two-tags-per-pair ordering contract of the point-to-point fabric ( see c...
Definition commtools.cpp:117
int proc_t
Definition commtypes.hpp:29
index_t comm_splitcount(const Vector< index_t > &aLengths, const proc_t aRoot)
Calculates the total number of chunks needed for a set of message lengths.
Definition commtools.cpp:228
void distribute(Cell< T > &aData)
Distributes elements of a cell to other processes.
Definition commtools.hpp:804
void allreduce(const T *aSend, T *aRecv, const int aCount)
Collective MAX-reduction visible on every rank.
Definition commtools.hpp:233
int comm_tag(const proc_t aSource, const proc_t aTarget)
Generates a unique tag for MPI communication between two processes.
Definition commtools.cpp:102
constexpr int gMaxCommChunkLength
Definition commtools.hpp:31
void broadcast(Cell< string > &aData, const proc_t aRoot)
Definition commtools.cpp:276
Cell< int > comm_split(const index_t aLength)
Splits a message into chunks for MPI communication.
Definition commtools.cpp:207
uint32_t index_t
Definition typedefs.hpp:52
void allreduce_min(const T *aSend, T *aRecv, const int aCount)
Collective MIN-reduction visible on every rank.
Definition commtools.hpp:275
void send(const string &aMessage, const proc_t aTarget)
Definition commtools.cpp:340
void comm_check(const int aErrorCode)
Checks the MPI error code and reports any errors.
Definition commtools.cpp:38
proc_t comm_size()
Returns the number of processes in the communicator.
Definition commtools.cpp:22
p
Definition test_curve_frame.py:64
Definition commtools.hpp:38