Loading...
Searching...
No Matches
level_comms.hpp
Go to the documentation of this file.
1
2#pragma once
3
4#include <cassert>
5#include <stdexcept>
6#include <vector>
7
8#include "terra/mpi/mpi.hpp"
9
10namespace terra::mpi {
11
12/// @brief Build a nested ladder of sub-communicators for hierarchical agglomeration.
13///
14/// Given a parent communicator (typically MPI_COMM_WORLD) and per-level
15/// agglomeration factors, constructs a sequence of communicators where each
16/// successor is a subset of the preceding one. Used by multigrid to run coarse
17/// levels on fewer ranks so collectives cost less at the bottom of the V-cycle.
18///
19/// Factor semantics: at descent step i, ranks whose index on the parent comm
20/// is divisible by factors[i] are kept, the rest drop out and get
21/// MPI_COMM_NULL at that and every deeper level.
22///
23/// The result has size factors.size() + 1: index 0 is the finest (= root),
24/// the rest are progressively smaller. The sizes on surviving ranks are
25/// {N, N/factors[0], N/(factors[0]*factors[1]), ...}.
26///
27/// @param root_comm The finest-level communicator.
28/// @param factors Per-descent agglomeration factors. Must all be >= 1 and
29/// evenly divide the preceding level size.
30/// @return Per-level communicators. Entries past the first drop-out
31/// are MPI_COMM_NULL for dropped ranks.
32inline std::vector< MPI_Comm > build_level_comms( MPI_Comm root_comm, const std::vector< int >& factors )
33{
34 std::vector< MPI_Comm > level_comms;
35 level_comms.reserve( factors.size() + 1 );
36 level_comms.push_back( root_comm );
37
38 MPI_Comm cur = root_comm;
39
40 for ( size_t i = 0; i < factors.size(); ++i )
41 {
42 const int factor = factors[i];
43 if ( factor < 1 )
44 {
45 throw std::runtime_error( "build_level_comms: agglomeration factor must be >= 1" );
46 }
47
48 if ( cur == MPI_COMM_NULL )
49 {
50 // This rank has already dropped out above.
51 level_comms.push_back( MPI_COMM_NULL );
52 continue;
53 }
54
55 const int parent_size = num_processes( cur );
56 const int parent_rank = rank( cur );
57
58 if ( parent_size % factor != 0 )
59 {
60 throw std::runtime_error(
61 "build_level_comms: parent comm size must be a multiple of the agglomeration factor" );
62 }
63
64 // Keep ranks whose parent index is divisible by `factor` (contiguous stride).
65 // Key preserves the ordering so that sub-comm rank == parent_rank / factor.
66 const int color = ( parent_rank % factor == 0 ) ? 0 : MPI_UNDEFINED;
67 const int key = parent_rank / factor;
68
69 MPI_Comm sub = MPI_COMM_NULL;
70 MPI_Comm_split( cur, color, key, &sub );
71 level_comms.push_back( sub );
72
73 cur = sub;
74 }
75
76 return level_comms;
77}
78
79/// @brief Translate a parent-comm rank into its agglomerated sub-comm rank.
80///
81/// Inverse of the color/key rule in build_level_comms: if agglomeration groups
82/// every `factor` consecutive parent ranks into a single sub-comm rank, then
83/// parent rank r maps to sub-comm rank r / factor.
84///
85/// Useful when remapping a subdomain_to_rank function to the coarser comm.
86///
87/// @param parent_rank Rank on the parent comm.
88/// @param factor Agglomeration factor at this descent step.
89/// @return Rank on the sub-comm (valid only if parent_rank % factor == 0).
90inline MPIRank agglomerate_rank( MPIRank parent_rank, int factor )
91{
92 assert( factor >= 1 );
93 return parent_rank / factor;
94}
95
96/// @brief Free a level-comm ladder. Call once at MG teardown.
97///
98/// MPI_Comm_free is collective on the comm, so every rank that got a non-null
99/// handle must call it. Ranks that got MPI_COMM_NULL at a level must skip.
100/// The root comm (index 0) is not freed here — caller owns it.
101inline void free_level_comms( std::vector< MPI_Comm >& level_comms )
102{
103 for ( size_t i = 1; i < level_comms.size(); ++i )
104 {
105 if ( level_comms[i] != MPI_COMM_NULL )
106 {
107 MPI_Comm_free( &level_comms[i] );
108 level_comms[i] = MPI_COMM_NULL;
109 }
110 }
111}
112
113} // namespace terra::mpi
Definition level_comms.hpp:10
void free_level_comms(std::vector< MPI_Comm > &level_comms)
Free a level-comm ladder. Call once at MG teardown.
Definition level_comms.hpp:101
MPIRank agglomerate_rank(MPIRank parent_rank, int factor)
Translate a parent-comm rank into its agglomerated sub-comm rank.
Definition level_comms.hpp:90
int num_processes()
Definition mpi.hpp:27
int MPIRank
Definition mpi.hpp:11
std::vector< MPI_Comm > build_level_comms(MPI_Comm root_comm, const std::vector< int > &factors)
Build a nested ladder of sub-communicators for hierarchical agglomeration.
Definition level_comms.hpp:32
MPIRank rank()
Definition mpi.hpp:13