Cabana 0.8.0-dev
 
Loading...
Searching...
No Matches
Cabana_CommunicationPlan_Mpi.hpp
Go to the documentation of this file.
1/****************************************************************************
2 * Copyright (c) 2018-2023 by the Cabana authors *
3 * All rights reserved. *
4 * *
5 * This file is part of the Cabana library. Cabana is distributed under a *
6 * BSD 3-clause license. For the licensing terms see the LICENSE file in *
7 * the top-level directory. *
8 * *
9 * SPDX-License-Identifier: BSD-3-Clause *
10 ****************************************************************************/
11
17#ifndef CABANA_COMMUNICATIONPLAN_MPI_HPP
18#define CABANA_COMMUNICATIONPLAN_MPI_HPP
19
20#include <Cabana_Utils.hpp>
21
22#include <Kokkos_Core.hpp>
23#include <Kokkos_ScatterView.hpp>
24
25#include <mpi.h>
26
27#include <algorithm>
28#include <exception>
29#include <memory>
30#include <numeric>
31#include <type_traits>
32#include <vector>
33
34namespace Cabana
35{
36
37//---------------------------------------------------------------------------//
42
43template <class MemorySpace>
44class CommunicationPlan<MemorySpace, Mpi>
45 : public CommunicationPlanBase<MemorySpace>
46{
47 public:
51
52 protected:
60 : CommunicationPlanBase<MemorySpace>( comm )
61 {
62 }
63
64 // The functions in the public block below would normally be protected but
65 // we make them public to allow using private class data in CUDA kernels
66 // with lambda functions.
67 public:
105 template <class ExecutionSpace, class RankViewType>
106 Kokkos::View<size_type*, memory_space>
107 createWithTopology( ExecutionSpace exec_space, Export,
108 const RankViewType& element_export_ranks,
109 const std::vector<int>& neighbor_ranks )
110 {
112
113 // Store the number of export elements.
114 this->_num_export_element = element_export_ranks.size();
115
116 // Store the unique neighbors (this rank first).
117 this->_neighbors = getUniqueTopology( this->comm(), neighbor_ranks );
118 int num_n = this->_neighbors.size();
119
120 // Get the size of this communicator.
121 int comm_size = -1;
122 MPI_Comm_size( this->comm(), &comm_size );
123
124 // Get the MPI rank we are currently on.
125 int my_rank = -1;
126 MPI_Comm_rank( this->comm(), &my_rank );
127
128 // Pick an mpi tag for communication. This object has it's own
129 // communication space so any mpi tag will do.
130 const int mpi_tag = 1221;
131
132 // Initialize import/export sizes.
133 this->_num_export.assign( num_n, 0 );
134 this->_num_import.assign( num_n, 0 );
135
136 // Count the number of sends this rank will do to other ranks. Keep
137 // track of which slot we get in our neighbor's send buffer.
138 auto counts_and_ids = Impl::countSendsAndCreateSteering(
139 exec_space, element_export_ranks, comm_size,
140 typename Impl::CountSendsAndCreateSteeringAlgorithm<
141 ExecutionSpace>::type() );
142
143 // Copy the counts to the host.
144 auto neighbor_counts_host = Kokkos::create_mirror_view_and_copy(
145 Kokkos::HostSpace(), counts_and_ids.first );
146
147 // Get the export counts.
148 for ( int n = 0; n < num_n; ++n )
149 this->_num_export[n] = neighbor_counts_host( this->_neighbors[n] );
150
151 // Post receives for the number of imports we will get.
152 std::vector<MPI_Request> requests;
153 requests.reserve( num_n );
154 for ( int n = 0; n < num_n; ++n )
155 if ( my_rank != this->_neighbors[n] )
156 {
157 requests.push_back( MPI_Request() );
158 MPI_Irecv( &this->_num_import[n], 1, MPI_UNSIGNED_LONG,
159 this->_neighbors[n], mpi_tag, this->comm(),
160 &( requests.back() ) );
161 }
162 else
163 this->_num_import[n] = this->_num_export[n];
164
165 // Send the number of exports to each of our neighbors.
166 for ( int n = 0; n < num_n; ++n )
167 if ( my_rank != this->_neighbors[n] )
168 MPI_Send( &this->_num_export[n], 1, MPI_UNSIGNED_LONG,
169 this->_neighbors[n], mpi_tag, this->comm() );
170
171 // Wait on receives.
172 std::vector<MPI_Status> status( requests.size() );
173 const int ec =
174 MPI_Waitall( requests.size(), requests.data(), status.data() );
175 if ( MPI_SUCCESS != ec )
176 throw std::logic_error(
177 "Cabana::CommunicationPlan::createFromExportsAndTopology: "
178 "Failed MPI Communication" );
179
180 // Get the total number of imports/exports.
181 this->_total_num_export =
182 std::accumulate( this->_num_export.begin(), this->_num_export.end(),
183 std::size_t{ 0u } );
184 this->_total_num_import =
185 std::accumulate( this->_num_import.begin(), this->_num_import.end(),
186 std::size_t{ 0u } );
187
188 // No barrier is needed because all ranks know who they are receiving
189 // and sending to.
190
191 // Return the neighbor ids.
192 return counts_and_ids.second;
193 }
194
230 template <class RankViewType>
231 Kokkos::View<size_type*, memory_space>
232 createWithTopology( Export, const RankViewType& element_export_ranks,
233 const std::vector<int>& neighbor_ranks )
234 {
235 // Use the default execution space.
237 element_export_ranks, neighbor_ranks );
238 }
239
270 template <class ExecutionSpace, class RankViewType>
271 Kokkos::View<size_type*, memory_space>
272 createWithoutTopology( ExecutionSpace exec_space, Export,
273 const RankViewType& element_export_ranks )
274 {
276
277 // Store the number of export elements.
278 this->_num_export_element = element_export_ranks.size();
279
280 // Get the size of this communicator.
281 int comm_size = -1;
282 MPI_Comm_size( this->comm(), &comm_size );
283
284 // Get the MPI rank we are currently on.
285 int my_rank = -1;
286 MPI_Comm_rank( this->comm(), &my_rank );
287
288 // Pick an mpi tag for communication. This object has it's own
289 // communication space so any mpi tag will do.
290 const int mpi_tag = 1221;
291
292 // Count the number of sends this rank will do to other ranks. Keep
293 // track of which slot we get in our neighbor's send buffer.
294 auto counts_and_ids = Impl::countSendsAndCreateSteering(
295 exec_space, element_export_ranks, comm_size,
296 typename Impl::CountSendsAndCreateSteeringAlgorithm<
297 ExecutionSpace>::type() );
298
299 // Copy the counts to the host.
300 auto neighbor_counts_host = Kokkos::create_mirror_view_and_copy(
301 Kokkos::HostSpace(), counts_and_ids.first );
302
303 // Extract the export ranks and number of exports and then flag the
304 // send ranks.
305 this->_neighbors.clear();
306 this->_num_export.clear();
307 this->_total_num_export = 0;
308 for ( int r = 0; r < comm_size; ++r )
309 if ( neighbor_counts_host( r ) > 0 )
310 {
311 this->_neighbors.push_back( r );
312 this->_num_export.push_back( neighbor_counts_host( r ) );
313 this->_total_num_export += neighbor_counts_host( r );
314 neighbor_counts_host( r ) = 1;
315 }
316
317 // Get the number of export ranks and initially allocate the import
318 // sizes.
319 int num_export_rank = this->_neighbors.size();
320 this->_num_import.assign( num_export_rank, 0 );
321
322 // If we are sending to ourself put that one first in the neighbor
323 // list and assign the number of imports to be the number of exports.
324 bool self_send = false;
325 for ( int n = 0; n < num_export_rank; ++n )
326 if ( this->_neighbors[n] == my_rank )
327 {
328 std::swap( this->_neighbors[n], this->_neighbors[0] );
329 std::swap( this->_num_export[n], this->_num_export[0] );
330 this->_num_import[0] = this->_num_export[0];
331 self_send = true;
332 break;
333 }
334
335 // Determine how many total import ranks each neighbor has.
336 int num_import_rank = -1;
337 std::vector<int> recv_counts( comm_size, 1 );
338 MPI_Reduce_scatter( neighbor_counts_host.data(), &num_import_rank,
339 recv_counts.data(), MPI_INT, MPI_SUM,
340 this->comm() );
341 if ( self_send )
342 --num_import_rank;
343
344 // Post the expected number of receives and indicate we might get them
345 // from any rank.
346 std::vector<std::size_t> import_sizes( num_import_rank );
347 std::vector<MPI_Request> requests( num_import_rank );
348 for ( int n = 0; n < num_import_rank; ++n )
349 MPI_Irecv( &import_sizes[n], 1, MPI_UNSIGNED_LONG, MPI_ANY_SOURCE,
350 mpi_tag, this->comm(), &requests[n] );
351
352 // Do blocking sends. Dont do any self sends.
353 int self_offset = ( self_send ) ? 1 : 0;
354 for ( int n = self_offset; n < num_export_rank; ++n )
355 MPI_Send( &this->_num_export[n], 1, MPI_UNSIGNED_LONG,
356 this->_neighbors[n], mpi_tag, this->comm() );
357
358 // Wait on non-blocking receives.
359 std::vector<MPI_Status> status( requests.size() );
360 const int ec =
361 MPI_Waitall( requests.size(), requests.data(), status.data() );
362 if ( MPI_SUCCESS != ec )
363 throw std::logic_error(
364 "Cabana::CommunicationPlan::createFromExportsOnly: Failed MPI "
365 "Communication" );
366
367 // Compute the total number of imports.
368 this->_total_num_import =
369 std::accumulate( import_sizes.begin(), import_sizes.end(),
370 ( self_send ) ? this->_num_import[0] : 0 );
371
372 // Extract the imports. If we did self sends we already know what
373 // imports we got from that.
374 for ( int i = 0; i < num_import_rank; ++i )
375 {
376 // Get the message source.
377 const auto source = status[i].MPI_SOURCE;
378
379 // See if the neighbor we received stuff from was someone we also
380 // sent stuff to.
381 auto found_neighbor = std::find( this->_neighbors.begin(),
382 this->_neighbors.end(), source );
383
384 // If this is a new neighbor (i.e. someone we didn't send anything
385 // to) record this.
386 if ( found_neighbor == std::end( this->_neighbors ) )
387 {
388 this->_neighbors.push_back( source );
389 this->_num_import.push_back( import_sizes[i] );
390 this->_num_export.push_back( 0 );
391 }
392
393 // Otherwise if we already sent something to this neighbor that
394 // means we already have a neighbor/export entry. Just assign the
395 // import entry for that neighbor.
396 else
397 {
398 auto n =
399 std::distance( this->_neighbors.begin(), found_neighbor );
400 this->_num_import[n] = import_sizes[i];
401 }
402 }
403
404 // A barrier is needed because of the use of wildcard receives. This
405 // avoids successive calls interfering with each other.
406 MPI_Barrier( this->comm() );
407
408 // Return the neighbor ids.
409 return counts_and_ids.second;
410 }
411
440 template <class RankViewType>
441 Kokkos::View<size_type*, memory_space>
442 createWithoutTopology( Export, const RankViewType& element_export_ranks )
443 {
444 // Use the default execution space.
446 element_export_ranks );
447 }
448
488 template <class ExecutionSpace, class RankViewType, class IdViewType>
489 auto createWithTopology( ExecutionSpace exec_space, Import,
490 const RankViewType& element_import_ranks,
491 const IdViewType& element_import_ids,
492 const std::vector<int>& neighbor_ranks )
493 -> std::tuple<Kokkos::View<typename RankViewType::size_type*,
494 typename RankViewType::memory_space>,
495 Kokkos::View<int*, typename RankViewType::memory_space>,
496 Kokkos::View<int*, typename IdViewType::memory_space>>
497 {
499
500 if ( element_import_ids.size() != element_import_ranks.size() )
501 throw std::runtime_error( "Export ids and ranks different sizes!" );
502
503 // Store the unique neighbors (this rank first).
504 this->_neighbors = getUniqueTopology( this->comm(), neighbor_ranks );
505 std::size_t num_n = this->_neighbors.size();
506
507 // Get the size of this communicator.
508 int comm_size = -1;
509 MPI_Comm_size( this->comm(), &comm_size );
510
511 // Get the MPI rank we are currently on.
512 int my_rank = -1;
513 MPI_Comm_rank( this->comm(), &my_rank );
514
515 // Pick an mpi tag for communication. This object has it's own
516 // communication space so any mpi tag will do.
517 const int mpi_tag = 1221;
518
519 // Initialize import/export sizes.
520 this->_num_export.assign( num_n, 0 );
521 this->_num_import.assign( num_n, 0 );
522
523 // Count the number of imports this rank needs from other ranks. Keep
524 // track of which slot we get in our neighbor's send buffer?
525 auto counts_and_ids = Impl::countSendsAndCreateSteering(
526 exec_space, element_import_ranks, comm_size,
527 typename Impl::CountSendsAndCreateSteeringAlgorithm<
528 ExecutionSpace>::type() );
529
530 // Copy the counts to the host.
531 auto neighbor_counts_host = Kokkos::create_mirror_view_and_copy(
532 Kokkos::HostSpace(), counts_and_ids.first );
533
534 // Get the import counts.
535 for ( std::size_t n = 0; n < num_n; ++n )
536 this->_num_import[n] = neighbor_counts_host( this->_neighbors[n] );
537
538 // Post receives to get the number of indices I will send to each rank.
539 // Post that many wildcard recieves to get the number of indices I will
540 // send to each rank
541 std::vector<MPI_Request> requests;
542 requests.reserve( num_n * 2 );
543 for ( std::size_t n = 0; n < num_n; ++n )
544 if ( my_rank != this->_neighbors[n] )
545 {
546 requests.push_back( MPI_Request() );
547 MPI_Irecv( &this->_num_export[n], 1, MPI_UNSIGNED_LONG,
548 this->_neighbors[n], mpi_tag, this->comm(),
549 &( requests.back() ) );
550 }
551 else // Self import
552 {
553 this->_num_export[n] = this->_num_import[n];
554 }
555
556 // Send the number of imports to each of our neighbors.
557 for ( std::size_t n = 0; n < num_n; ++n )
558 if ( my_rank != this->_neighbors[n] )
559 {
560 requests.push_back( MPI_Request() );
561 MPI_Isend( &this->_num_import[n], 1, MPI_UNSIGNED_LONG,
562 this->_neighbors[n], mpi_tag, this->comm(),
563 &( requests.back() ) );
564 }
565
566 // Wait on messages.
567 std::vector<MPI_Status> status( requests.size() );
568 const int ec =
569 MPI_Waitall( requests.size(), requests.data(), status.data() );
570 if ( MPI_SUCCESS != ec )
571 throw std::logic_error( "Failed MPI Communication" );
572
573 // Get the total number of imports/exports.
574 this->_total_num_export = std::accumulate( this->_num_export.begin(),
575 this->_num_export.end(), 0 );
576 this->_total_num_import = std::accumulate( this->_num_import.begin(),
577 this->_num_import.end(), 0 );
579
580 // Post receives to get the indices other processes are requesting
581 // i.e. our export indices
582 Kokkos::View<int*, memory_space> export_indices(
583 "export_indices", this->_total_num_export );
584 std::size_t idx = 0;
585 int num_messages =
586 this->_total_num_export + element_import_ranks.extent( 0 );
587 std::vector<MPI_Request> mpi_requests( num_messages );
588 std::vector<MPI_Status> mpi_statuses( num_messages );
589
590 // Increment the mpi_tag for this round of messages to ensure messages
591 // are processed in the correct order from the previous round of Isends
592 // and Irecvs.
593 for ( std::size_t i = 0; i < num_n; i++ )
594 {
595 for ( std::size_t j = 0; j < this->_num_export[i]; j++ )
596 {
597 MPI_Irecv( export_indices.data() + idx, 1, MPI_INT,
598 this->_neighbors[i], mpi_tag + 1, this->comm(),
599 &mpi_requests[idx] );
600 idx++;
601 }
602 }
603
604 // Send the indices we need
605 for ( std::size_t i = 0; i < element_import_ranks.extent( 0 ); i++ )
606 {
607 MPI_Isend( element_import_ids.data() + i, 1, MPI_INT,
608 *( element_import_ranks.data() + i ), mpi_tag + 1,
609 this->comm(), &mpi_requests[idx++] );
610 }
611
612 // Wait for all count exchanges to complete
613 const int ec1 = MPI_Waitall( num_messages, mpi_requests.data(),
614 mpi_statuses.data() );
615 if ( MPI_SUCCESS != ec1 )
616 throw std::logic_error( "Failed MPI Communication" );
617
618 // Now, build the export steering
619 // Export rank in mpi_statuses[i].MPI_SOURCE
620 // Export ID in export_indices(i)
621 Kokkos::View<int*, Kokkos::HostSpace> element_export_ranks_h(
622 "element_export_ranks_h", this->_total_num_export );
623 for ( std::size_t i = 0; i < this->_total_num_export; i++ )
624 {
625 element_export_ranks_h[i] = mpi_statuses[i].MPI_SOURCE;
626 }
627 auto element_export_ranks = Kokkos::create_mirror_view_and_copy(
628 memory_space(), element_export_ranks_h );
629
630 auto counts_and_ids2 = Impl::countSendsAndCreateSteering(
631 exec_space, element_export_ranks, comm_size,
632 typename Impl::CountSendsAndCreateSteeringAlgorithm<
633 ExecutionSpace>::type() );
634
635 // No barrier is needed because all ranks know who they are receiving
636 // from and sending to.
637
638 // Return the neighbor ids, export ranks, and export indices
639 return std::tuple{ counts_and_ids2.second, element_export_ranks,
640 export_indices };
641 }
642
680 template <class RankViewType, class IdViewType>
681 auto createWithTopology( Import, const RankViewType& element_import_ranks,
682 const IdViewType& element_import_ids,
683 const std::vector<int>& neighbor_ranks )
684 {
685 // Use the default execution space.
687 element_import_ranks, element_import_ids,
688 neighbor_ranks );
689 }
690
723 template <class ExecutionSpace, class RankViewType, class IdViewType>
724 auto createWithoutTopology( ExecutionSpace exec_space, Import,
725 const RankViewType& element_import_ranks,
726 const IdViewType& element_import_ids )
727 -> std::tuple<Kokkos::View<typename RankViewType::size_type*,
728 typename RankViewType::memory_space>,
729 Kokkos::View<int*, typename RankViewType::memory_space>,
730 Kokkos::View<int*, typename IdViewType::memory_space>>
731 {
733
734 if ( element_import_ids.size() != element_import_ranks.size() )
735 throw std::runtime_error( "Export ids and ranks different sizes!" );
736
737 // Get the size of this communicator.
738 int comm_size = -1;
739 MPI_Comm_size( this->comm(), &comm_size );
740
741 // Get the MPI rank we are currently on.
742 int rank = -1;
743 MPI_Comm_rank( this->comm(), &rank );
744
745 // Pick an mpi tag for communication. This object has it's own
746 // communication space so any mpi tag will do.
747 const int mpi_tag = 1221;
748
749 // Store which ranks I need to recieve from (i.e. send data to me)
750 Kokkos::View<int*, memory_space> importing_ranks( "importing_ranks",
751 comm_size );
752 Kokkos::deep_copy( importing_ranks, 0 );
753 Kokkos::parallel_for(
754 "Cabana::storeImportRanks",
755 Kokkos::RangePolicy<ExecutionSpace>(
756 0, element_import_ranks.extent( 0 ) ),
757 KOKKOS_LAMBDA( const int i ) {
758 int import_rank = element_import_ranks( i );
759 Kokkos::atomic_store( &importing_ranks( import_rank ), 1 );
760 } );
761 Kokkos::fence();
762 auto importing_ranks_h = Kokkos::create_mirror_view_and_copy(
763 Kokkos::HostSpace(), importing_ranks );
764
765 // Allreduce to count number of ranks I am communicating with
766 Kokkos::View<int*, Kokkos::HostSpace> num_ranks_communicate(
767 "num_ranks_communicate", comm_size );
768 MPI_Allreduce( importing_ranks_h.data(), num_ranks_communicate.data(),
769 comm_size, MPI_INT, MPI_SUM, this->comm() );
770
771 // Post that many wildcard recieves to get the number of indices I will
772 // send to each rank Allocate buffers based on num_ranks_communicate
773 int num_recvs = num_ranks_communicate( rank );
774 Kokkos::View<int*, Kokkos::HostSpace> send_counts( "send_counts",
775 num_recvs );
776 Kokkos::View<int*, Kokkos::HostSpace> send_to( "send_to", num_recvs );
777
778 std::vector<MPI_Request> mpi_requests( num_recvs );
779 std::vector<MPI_Status> mpi_statuses( num_recvs );
780
781 // Receive counts for indices this process will send
782 for ( int i = 0; i < num_recvs; i++ )
783 {
784 MPI_Irecv( &send_counts( i ), 1, MPI_INT, MPI_ANY_SOURCE, mpi_tag,
785 this->comm(), &mpi_requests[i] );
786 }
787
788 // Count the number of imports this rank needs from other ranks. Keep
789 // track of which slot we get in our neighbor's send buffer?
790 auto counts_and_ids = Impl::countSendsAndCreateSteering(
791 exec_space, element_import_ranks, comm_size,
792 typename Impl::CountSendsAndCreateSteeringAlgorithm<
793 ExecutionSpace>::type() );
794
795 // Copy the counts to the host.
796 auto neighbor_counts_host = Kokkos::create_mirror_view_and_copy(
797 Kokkos::HostSpace(), counts_and_ids.first );
798
799 // Clear vectors before we use them
800 this->_neighbors.clear();
801 this->_num_export.clear();
802 this->_num_import.clear();
803
804 for ( std::size_t i = 0; i < neighbor_counts_host.extent( 0 ); i++ )
805 {
806 if ( neighbor_counts_host( i ) != 0 )
807 {
808 // Send counts of needed indices
809 MPI_Send( &neighbor_counts_host( i ), 1, MPI_INT, i, mpi_tag,
810 this->comm() );
811
812 // Store we are importing this count from this rank
813 this->_neighbors.push_back( i );
814 this->_num_import.push_back( neighbor_counts_host( i ) );
815 }
816 }
817 // Assign all exports to zero
818 this->_num_export.assign( this->_num_import.size(), 0 );
819
820 // Wait for all count exchanges to complete
821 const int ec0 =
822 MPI_Waitall( num_recvs, mpi_requests.data(), mpi_statuses.data() );
823 if ( MPI_SUCCESS != ec0 )
824 throw std::logic_error( "Failed MPI Communication" );
825
826 // Save ranks we got messages from and track total messages to size
827 // buffers
828 this->_total_num_export = 0;
829 for ( int i = 0; i < num_recvs; i++ )
830 {
831 send_to( i ) = mpi_statuses[i].MPI_SOURCE;
832 this->_total_num_export += send_counts( i );
833 }
834
835 // Extract the export ranks and number of exports and then flag the
836 // send ranks.
837 for ( int r = 0; r < num_recvs; ++r )
838 {
839 int export_to = send_to( r );
840 if ( export_to > -1 )
841 {
842 // See if the neighbor we are exporting to is someone we are
843 // also importing from
844 auto found_neighbor =
845 std::find( this->_neighbors.begin(), this->_neighbors.end(),
846 export_to );
847
848 // If this is a new neighbor (i.e. someone we are not importing
849 // from) record this.
850 if ( found_neighbor == std::end( this->_neighbors ) )
851 {
852 this->_neighbors.push_back( export_to );
853 this->_num_import.push_back( 0 );
854 this->_num_export.push_back( send_counts( r ) );
855 }
856
857 // Otherwise if we are already importing from this neighbor that
858 // means we already have a neighbor/import entry. Just assign
859 // the export entry for that neighbor.
860 else
861 {
862 auto n = std::distance( this->_neighbors.begin(),
863 found_neighbor );
864 this->_num_export[n] = send_counts( r );
865 }
866 }
867 else
868 {
869 // This block should never be reached as
870 // mpi_statuses[i].MPI_SOURCE will never be less than 0.
871 throw std::runtime_error(
872 "CommunicationPlan::createFromImportsOnly: "
873 "mpi_statuses[i].MPI_SOURCE returned a value >= -1" );
874 }
875 }
876 // If we are sending to ourself put that one first in the neighbor
877 // list and assign the number of exports to be the number of imports.
878 for ( std::size_t n = 0; n < this->_neighbors.size(); ++n )
879 if ( this->_neighbors[n] == rank )
880 {
881 std::swap( this->_neighbors[n], this->_neighbors[0] );
882 std::swap( this->_num_export[n], this->_num_export[0] );
883 std::swap( this->_num_import[n], this->_num_import[0] );
884 this->_num_export[0] = this->_num_import[0];
885 break;
886 }
887
888 // Total number of imports and exports are now known
889 this->_total_num_import = element_import_ranks.extent( 0 );
891
892 // Post receives to get the indices other processes are requesting
893 // i.e. our export indices
894 Kokkos::View<int*, memory_space> export_indices(
895 "export_indices", this->_total_num_export );
896 std::size_t idx = 0;
897 mpi_requests.clear();
898 mpi_statuses.clear();
899 int num_messages =
900 this->_total_num_export + element_import_ranks.extent( 0 );
901 mpi_requests.resize( num_messages );
902 mpi_statuses.resize( num_messages );
903
904 // Increment the mpi_tag for this round of messages to ensure messages
905 // are processed in the correct order from the previous round of Isends
906 // and Irecvs.
907 for ( int i = 0; i < num_recvs; i++ )
908 {
909 for ( int j = 0; j < send_counts( i ); j++ )
910 {
911 MPI_Irecv( export_indices.data() + idx, 1, MPI_INT,
912 send_to( i ), mpi_tag + 1, this->comm(),
913 &mpi_requests[idx] );
914 idx++;
915 }
916 }
917
918 // Send the indices we need
919 for ( std::size_t i = 0; i < element_import_ranks.extent( 0 ); i++ )
920 {
921 MPI_Isend( element_import_ids.data() + i, 1, MPI_INT,
922 *( element_import_ranks.data() + i ), mpi_tag + 1,
923 this->comm(), &mpi_requests[idx++] );
924 }
925
926 // Wait for all count exchanges to complete
927 const int ec1 = MPI_Waitall( num_messages, mpi_requests.data(),
928 mpi_statuses.data() );
929 if ( MPI_SUCCESS != ec1 )
930 throw std::logic_error( "Failed MPI Communication" );
931
932 // Now, build the export steering
933 // Export rank in mpi_statuses[i].MPI_SOURCE
934 // Export ID in export_indices(i)
935 Kokkos::View<int*, Kokkos::HostSpace> element_export_ranks_h(
936 "element_export_ranks_h", this->_total_num_export );
937 for ( std::size_t i = 0; i < this->_total_num_export; i++ )
938 {
939 element_export_ranks_h[i] = mpi_statuses[i].MPI_SOURCE;
940 }
941 auto element_export_ranks = Kokkos::create_mirror_view_and_copy(
942 memory_space(), element_export_ranks_h );
943
944 auto counts_and_ids2 = Impl::countSendsAndCreateSteering(
945 exec_space, element_export_ranks, comm_size,
946 typename Impl::CountSendsAndCreateSteeringAlgorithm<
947 ExecutionSpace>::type() );
948
949 // A barrier is needed because of the use of wildcard receives. This
950 // avoids successive calls interfering with each other.
951 MPI_Barrier( this->comm() );
952
953 return std::tuple{ counts_and_ids2.second, element_export_ranks,
954 export_indices };
955 }
956
987 template <class RankViewType, class IdViewType>
989 const RankViewType& element_import_ranks,
990 const IdViewType& element_import_ids )
991 {
992 // Use the default execution space.
994 element_import_ranks,
995 element_import_ids );
996 }
997};
998
1002template <class CommPlanType, class CommDataType>
1003class CommunicationData<CommPlanType, CommDataType, Mpi>
1004 : public CommunicationDataBase<CommPlanType, CommDataType>
1005{
1006 protected:
1007 using typename CommunicationDataBase<CommPlanType,
1008 CommDataType>::particle_data_type;
1015 CommunicationData( const CommPlanType& comm_plan,
1016 const particle_data_type& particles,
1017 const double overallocation = 1.0 )
1018 : CommunicationDataBase<CommPlanType, CommDataType>(
1019 comm_plan, particles, overallocation )
1020 {
1021 }
1022};
1023
1024} // end namespace Cabana
1025
1026#endif // end CABANA_COMMUNICATIONPLAN_MPI_HPP
Cabana utilities.
typename comm_data_type::particle_data_type particle_data_type
Particle data type.
Definition Cabana_CommunicationPlanBase.hpp:823
CommunicationDataBase(const CommPlanType &comm_plan, const particle_data_type &particles, const double overallocation=1.0)
Definition Cabana_CommunicationPlanBase.hpp:838
CommunicationData(const CommPlanType &comm_plan, const particle_data_type &particles, const double overallocation=1.0)
Definition Cabana_CommunicationPlan_Mpi.hpp:1015
std::size_t _total_num_export
Number of elements exported.
Definition Cabana_CommunicationPlanBase.hpp:677
std::vector< std::size_t > _num_export
Number of elements exported to each neighbor.
Definition Cabana_CommunicationPlanBase.hpp:681
CommunicationPlanBase(MPI_Comm comm)
Constructor.
Definition Cabana_CommunicationPlanBase.hpp:442
std::size_t _total_num_import
Number of elements imported.
Definition Cabana_CommunicationPlanBase.hpp:679
typename memory_space::memory_space::size_type size_type
Size type.
Definition Cabana_CommunicationPlanBase.hpp:434
std::size_t _num_export_element
Definition Cabana_CommunicationPlanBase.hpp:687
std::vector< std::size_t > _num_import
Number of elements imported from each neighbor.
Definition Cabana_CommunicationPlanBase.hpp:683
std::vector< int > _neighbors
List of Mpi neighbors.
Definition Cabana_CommunicationPlanBase.hpp:675
typename memory_space::execution_space execution_space
Default execution space.
Definition Cabana_CommunicationPlanBase.hpp:431
MemorySpace memory_space
Kokkos memory space.
Definition Cabana_CommunicationPlanBase.hpp:427
MPI_Comm comm() const
Get the MPI communicator.
Definition Cabana_CommunicationPlanBase.hpp:465
Kokkos::View< size_type *, memory_space > createWithTopology(ExecutionSpace exec_space, Export, const RankViewType &element_export_ranks, const std::vector< int > &neighbor_ranks)
Neighbor and export rank creator. Use this when you already know which ranks neighbor each other (i....
Definition Cabana_CommunicationPlan_Mpi.hpp:107
auto createWithTopology(Import, const RankViewType &element_import_ranks, const IdViewType &element_import_ids, const std::vector< int > &neighbor_ranks)
Neighbor and import rank creator. Use this when you already know which ranks neighbor each other (i....
Definition Cabana_CommunicationPlan_Mpi.hpp:681
Kokkos::View< size_type *, memory_space > createWithoutTopology(ExecutionSpace exec_space, Export, const RankViewType &element_export_ranks)
Export rank creator. Use this when you don't know who you will receiving from - only who you are send...
Definition Cabana_CommunicationPlan_Mpi.hpp:272
auto createWithTopology(ExecutionSpace exec_space, Import, const RankViewType &element_import_ranks, const IdViewType &element_import_ids, const std::vector< int > &neighbor_ranks) -> std::tuple< Kokkos::View< typename RankViewType::size_type *, typename RankViewType::memory_space >, Kokkos::View< int *, typename RankViewType::memory_space >, Kokkos::View< int *, typename IdViewType::memory_space > >
Neighbor and import rank creator. Use this when you already know which ranks neighbor each other (i....
Definition Cabana_CommunicationPlan_Mpi.hpp:489
CommunicationPlan(MPI_Comm comm)
Constructor.
Definition Cabana_CommunicationPlan_Mpi.hpp:59
Kokkos::View< size_type *, memory_space > createWithoutTopology(Export, const RankViewType &element_export_ranks)
Export rank creator. Use this when you don't know who you will receiving from - only who you are send...
Definition Cabana_CommunicationPlan_Mpi.hpp:442
Kokkos::View< size_type *, memory_space > createWithTopology(Export, const RankViewType &element_export_ranks, const std::vector< int > &neighbor_ranks)
Neighbor and export rank creator. Use this when you already know which ranks neighbor each other (i....
Definition Cabana_CommunicationPlan_Mpi.hpp:232
auto createWithoutTopology(ExecutionSpace exec_space, Import, const RankViewType &element_import_ranks, const IdViewType &element_import_ids) -> std::tuple< Kokkos::View< typename RankViewType::size_type *, typename RankViewType::memory_space >, Kokkos::View< int *, typename RankViewType::memory_space >, Kokkos::View< int *, typename IdViewType::memory_space > >
Import rank creator. Use this when you don't know who you will be receiving from - only who you are i...
Definition Cabana_CommunicationPlan_Mpi.hpp:724
auto createWithoutTopology(Import, const RankViewType &element_import_ranks, const IdViewType &element_import_ids)
Import rank creator. Use this when you don't know who you will be receiving from - only who you are i...
Definition Cabana_CommunicationPlan_Mpi.hpp:988
Core: particle data structures and algorithms.
Definition Cabana_AoSoA.hpp:36
std::vector< int > getUniqueTopology(MPI_Comm comm, std::vector< int > topology)
Return unique neighbor ranks, with the current rank first.
Definition Cabana_CommunicationPlanBase.hpp:374
Export-based tag - default.
Definition Cabana_Tags.hpp:38
Import-based tag.
Definition Cabana_Tags.hpp:45
Vanilla MPI backend tag - default.
Definition Cabana_Tags.hpp:28
Definition Cabana_Types.hpp:88