@@ -43,34 +43,43 @@ void divide_mpi_groups(int nproc, int num_groups, int rank, bool even,
4343
4444namespace {
4545
46- // Helper: split a communicator into ngroup sub-communicators
46+ // Helper: split a parent communicator into ngroup sub-communicators.
47+ // Mirrors MPICommGroup::divide_group_comm in parallel_comm.cpp:
48+ // - group_comm: intra-group communicator (color = my_group)
49+ // - inter_comm: communicator of same-rank processes across groups
50+ // (color = rank_in_group); MPI_COMM_NULL for a single
51+ // group or an uneven split, exactly like KP_WORLD.
4752struct GroupSplitResult
4853{
49- MPI_Comm group_comm; // intra-group communicator
50- MPI_Comm inter_comm; // inter-group communicator ( MPI_COMM_NULL if single group)
51- int ngroups;
52- int nprocs_in_group;
53- int my_group;
54- int rank_in_group;
54+ MPI_Comm group_comm = MPI_COMM_NULL ;
55+ MPI_Comm inter_comm = MPI_COMM_NULL ;
56+ int ngroups = 0 ;
57+ int nprocs_in_group = 0 ;
58+ int my_group = 0 ;
59+ int rank_in_group = 0 ;
5560};
5661
5762GroupSplitResult split_comm_group (MPI_Comm parent, int ngroup, bool even)
5863{
5964 GroupSplitResult res;
60- res.group_comm = MPI_COMM_NULL ;
61- res.inter_comm = MPI_COMM_NULL ;
6265 res.ngroups = ngroup;
6366
64- int gsize = 0 , grank = 0 ;
67+ int gsize = 0 ;
68+ int grank = 0 ;
6569 MPI_Comm_size (parent, &gsize);
6670 MPI_Comm_rank (parent, &grank);
6771
6872 divide_mpi_groups (gsize, ngroup, grank, even,
6973 res.nprocs_in_group , res.my_group , res.rank_in_group );
7074
75+ // Intra-group communicator: one sub-communicator per group.
7176 MPI_Comm_split (parent, res.my_group , res.rank_in_group , &res.group_comm );
7277
73- if (ngroup > 1 )
78+ // Inter-group communicator: processes with the same rank inside
79+ // their group talk to each other. Only valid for an even split;
80+ // an uneven split leaves some groups without a corresponding rank.
81+ const bool is_even = (gsize % ngroup == 0 );
82+ if (ngroup > 1 && is_even)
7483 {
7584 MPI_Comm_split (parent, res.rank_in_group , res.my_group , &res.inter_comm );
7685 }
@@ -80,37 +89,71 @@ GroupSplitResult split_comm_group(MPI_Comm parent, int ngroup, bool even)
8089
8190} // anonymous namespace
8291
83- void split_pools (int nproc, int my_rank, int bndpar, int kpar,
92+ void split_images (int nproc, int my_rank, int nimage,
93+ int & image_id, int & rank_in_esolver, int & esolver_size,
94+ ParaWorld& esolver_world, ParaWorld& images_world)
95+ {
96+ assert (nimage > 0 );
97+ assert (nproc >= nimage);
98+
99+ int procs_in_image = 0 ;
100+ divide_mpi_groups (nproc, nimage, my_rank, false ,
101+ procs_in_image, image_id, rank_in_esolver);
102+ esolver_size = procs_in_image;
103+
104+ // Intra-image domain: all processes of one esolver.
105+ MPI_Comm esolver_comm;
106+ MPI_Comm_split (MPI_COMM_WORLD , image_id, rank_in_esolver, &esolver_comm);
107+ esolver_world = ParaWorld::make_mpi (ParaTag::esolver, esolver_comm);
108+
109+ // Inter-image domain: same rank_in_esolver across images. Follows the
110+ // KP_WORLD convention: absent for a single image or an uneven split.
111+ const bool is_even = (nproc % nimage == 0 );
112+ if (nimage > 1 && is_even)
113+ {
114+ MPI_Comm images_comm;
115+ MPI_Comm_split (MPI_COMM_WORLD , rank_in_esolver, image_id, &images_comm);
116+ images_world = ParaWorld::make_mpi (ParaTag::images, images_comm);
117+ }
118+ else
119+ {
120+ images_world = ParaWorld::make_mpi (ParaTag::images, MPI_COMM_NULL );
121+ }
122+ }
123+
124+ void split_pools (int parent_size, int parent_rank, int bndpar, int kpar,
125+ const MPI_Comm& parent_comm,
84126 int & nproc_in_pool, int & rank_in_pool, int & my_pool,
85127 int & nproc_in_bndgroup, int & rank_in_bpgroup, int & my_bndgroup,
86128 ParaWorld& pw_world, ParaWorld& kmesh_world,
87129 ParaWorld& bgroup_int, ParaWorld& bgroup_bp)
88130{
89- if (bndpar > 1 && nproc % (bndpar * kpar) != 0 )
131+ if (bndpar > 1 && parent_size % (bndpar * kpar) != 0 )
90132 {
91- std::cerr << " Error: NPROC ( " << nproc
92- << " ) must be divisible by BNDPAR*KPAR ( "
93- << bndpar * kpar << " )." << std::endl;
133+ std::cerr << " Error: " << parent_size
134+ << " processes in the parent domain must be divisible by "
135+ << " BNDPAR*KPAR ( " << bndpar * kpar << " )." << std::endl;
94136 assert (false );
95137 }
96138
97- // k-point parallelization: split WORLD into kpar pools
98- GroupSplitResult kpar_res = split_comm_group (MPI_COMM_WORLD , kpar, false );
139+ // k-point parallelization: split the parent domain into kpar pools.
140+ GroupSplitResult kpar_res = split_comm_group (parent_comm , kpar, false );
99141
100- // band parallelization: split each pool into bndpar groups
142+ // band parallelization: split each pool into bndpar groups.
101143 GroupSplitResult bndpar_res = split_comm_group (kpar_res.group_comm , bndpar, true );
102144
103- // Set output indices
145+ // Set output indices.
104146 nproc_in_pool = bndpar_res.nprocs_in_group ;
105147 rank_in_pool = bndpar_res.rank_in_group ;
106148 my_pool = kpar_res.my_group ;
107149
108- // POOL_WORLD
150+ // POOL_WORLD: processes with the same k point and the same bands
151+ // (plane-wave distribution lives inside it).
109152 MPI_Comm pool_comm;
110153 MPI_Comm_dup (bndpar_res.group_comm , &pool_comm);
111154 pw_world = ParaWorld::make_mpi (ParaTag::pw, pool_comm);
112155
113- // KP_WORLD ( inter-pool communicator)
156+ // KP_WORLD: inter-pool communicator (same rank across pools).
114157 if (kpar_res.inter_comm != MPI_COMM_NULL )
115158 {
116159 MPI_Comm kp_comm;
@@ -122,48 +165,55 @@ void split_pools(int nproc, int my_rank, int bndpar, int kpar,
122165 kmesh_world = ParaWorld::make_mpi (ParaTag::kmesh, MPI_COMM_NULL );
123166 }
124167
125- // Band group communicators
168+ // Band group communicators.
126169 if (bndpar > 1 )
127170 {
128171 nproc_in_bndgroup = kpar_res.ngroups * bndpar_res.nprocs_in_group ;
129172 rank_in_bpgroup = kpar_res.my_group * bndpar_res.nprocs_in_group + bndpar_res.rank_in_group ;
130173 my_bndgroup = bndpar_res.my_group ;
131174
175+ // INT_BGROUP: same bands across pools (bsame_kdiff).
132176 MPI_Comm int_bgroup;
133- MPI_Comm_split (MPI_COMM_WORLD , my_bndgroup, rank_in_bpgroup, &int_bgroup);
177+ MPI_Comm_split (parent_comm , my_bndgroup, rank_in_bpgroup, &int_bgroup);
134178 bgroup_int = ParaWorld::make_mpi (ParaTag::bsame_kdiff, int_bgroup);
135179
180+ // BP_WORLD: same k point across band groups (bdiff_ksame).
136181 MPI_Comm bp_comm;
137182 MPI_Comm_dup (bndpar_res.inter_comm , &bp_comm);
138183 bgroup_bp = ParaWorld::make_mpi (ParaTag::bdiff_ksame, bp_comm);
139184 }
140185 else
141186 {
142- nproc_in_bndgroup = nproc ;
143- rank_in_bpgroup = my_rank ;
187+ nproc_in_bndgroup = parent_size ;
188+ rank_in_bpgroup = parent_rank ;
144189 my_bndgroup = 0 ;
145190
191+ // No band parallelism: INT_BGROUP spans the whole parent domain,
192+ // BP_WORLD degenerates to one process per rank.
146193 MPI_Comm int_bgroup;
147- MPI_Comm_dup (MPI_COMM_WORLD , &int_bgroup);
194+ MPI_Comm_dup (parent_comm , &int_bgroup);
148195 bgroup_int = ParaWorld::make_mpi (ParaTag::bsame_kdiff, int_bgroup);
149196
150197 MPI_Comm bp_comm;
151- MPI_Comm_split (MPI_COMM_WORLD , my_rank , 0 , &bp_comm);
198+ MPI_Comm_split (parent_comm, parent_rank , 0 , &bp_comm);
152199 bgroup_bp = ParaWorld::make_mpi (ParaTag::bdiff_ksame, bp_comm);
153200 }
154201}
155202
156- ParaWorld split_diag_world (int diag_np, int nproc, int my_rank,
203+ ParaWorld split_diag_world (int diag_np, int parent_size, int parent_rank,
204+ const MPI_Comm& parent_comm,
157205 int & drank, int & dsize, int & dcolor)
158206{
159207 assert (diag_np > 0 );
160208
161- int procs_in_group = 0 , my_group = 0 , rank_in_group = 0 ;
162- divide_mpi_groups (nproc, diag_np, my_rank, false ,
209+ int procs_in_group = 0 ;
210+ int my_group = 0 ;
211+ int rank_in_group = 0 ;
212+ divide_mpi_groups (parent_size, diag_np, parent_rank, false ,
163213 procs_in_group, my_group, rank_in_group);
164214
165215 MPI_Comm diag_comm;
166- MPI_Comm_split (MPI_COMM_WORLD , my_group, rank_in_group, &diag_comm);
216+ MPI_Comm_split (parent_comm , my_group, rank_in_group, &diag_comm);
167217
168218 MPI_Comm_rank (diag_comm, &drank);
169219 MPI_Comm_size (diag_comm, &dsize);
@@ -172,38 +222,67 @@ ParaWorld split_diag_world(int diag_np, int nproc, int my_rank,
172222 return ParaWorld::make_mpi (ParaTag::diag, diag_comm);
173223}
174224
175- ParaWorld split_grid_world (int diag_np, int nproc, int my_rank,
225+ ParaWorld split_grid_world (int diag_np, int parent_size, int parent_rank,
226+ const MPI_Comm& parent_comm,
176227 int & grank, int & gsize)
177228{
178229 assert (diag_np > 0 );
179230
180- int procs_in_group = 0 , my_group = 0 , rank_in_group = 0 ;
181- divide_mpi_groups (nproc, diag_np, my_rank, false ,
231+ int procs_in_group = 0 ;
232+ int my_group = 0 ;
233+ int rank_in_group = 0 ;
234+ divide_mpi_groups (parent_size, diag_np, parent_rank, false ,
182235 procs_in_group, my_group, rank_in_group);
183236
184237 MPI_Comm grid_comm;
185- MPI_Comm_split (MPI_COMM_WORLD , my_group, rank_in_group, &grid_comm);
238+ MPI_Comm_split (parent_comm , my_group, rank_in_group, &grid_comm);
186239
187240 MPI_Comm_rank (grid_comm, &grank);
188241 MPI_Comm_size (grid_comm, &gsize);
189242
190243 return ParaWorld::make_mpi (ParaTag::rgrid, grid_comm);
191244}
192245
193- ParaCollection setup_para_worlds (int nproc, int my_rank, int bndpar, int kpar, int diag_np)
246+ ParaCollection setup_para_worlds (int nproc, int my_rank, int nimage,
247+ int bndpar, int kpar, int diag_np)
194248{
195249 ParaCollection worlds;
196250
197- // 1. POOL_WORLD + KP_WORLD + band group comms
198- int nproc_in_pool, rank_in_pool, my_pool;
199- int nproc_in_bndgroup, rank_in_bpgroup, my_bndgroup;
251+ // 0. Top-level split: independent images.
252+ // esolver_world contains all processes of one esolver instance;
253+ // images_world connects corresponding ranks across images.
254+ int image_id = 0 ;
255+ int rank_in_esolver = 0 ;
256+ int esolver_size = 0 ;
257+ ParaWorld esolver_world = ParaWorld::make_mpi (ParaTag::esolver, MPI_COMM_NULL );
258+ ParaWorld images_world = ParaWorld::make_mpi (ParaTag::images, MPI_COMM_NULL );
259+ split_images (nproc, my_rank, nimage,
260+ image_id, rank_in_esolver, esolver_size,
261+ esolver_world, images_world);
262+ worlds.add (ParaWorld::make_mpi_ptr (ParaTag::esolver, esolver_world.comm ()));
263+ // images_world may be an invalid domain (nimage == 1 or uneven split);
264+ // it is still registered so that find(ParaTag::images) returns it and
265+ // callers can test valid().
266+ worlds.add (ParaWorld::make_mpi_ptr (ParaTag::images, images_world.comm ()));
267+
268+ // All solver domains are derived from the esolver domain, never from
269+ // MPI_COMM_WORLD directly (see the hierarchy diagram in para_setup.h).
270+ const MPI_Comm esolver_comm = esolver_world.comm ();
271+
272+ // 1. k-pools and band groups.
273+ int nproc_in_pool = 0 ;
274+ int rank_in_pool = 0 ;
275+ int my_pool = 0 ;
276+ int nproc_in_bndgroup = 0 ;
277+ int rank_in_bpgroup = 0 ;
278+ int my_bndgroup = 0 ;
200279
201280 ParaWorld pw_world = ParaWorld::make_mpi (ParaTag::pw, MPI_COMM_NULL );
202281 ParaWorld kmesh_world = ParaWorld::make_mpi (ParaTag::kmesh, MPI_COMM_NULL );
203282 ParaWorld bgroup_int = ParaWorld::make_mpi (ParaTag::bsame_kdiff, MPI_COMM_NULL );
204283 ParaWorld bgroup_bp = ParaWorld::make_mpi (ParaTag::bdiff_ksame, MPI_COMM_NULL );
205284
206- split_pools (nproc, my_rank , bndpar, kpar,
285+ split_pools (esolver_size, rank_in_esolver , bndpar, kpar, esolver_comm ,
207286 nproc_in_pool, rank_in_pool, my_pool,
208287 nproc_in_bndgroup, rank_in_bpgroup, my_bndgroup,
209288 pw_world, kmesh_world, bgroup_int, bgroup_bp);
@@ -213,17 +292,22 @@ ParaCollection setup_para_worlds(int nproc, int my_rank, int bndpar, int kpar, i
213292 worlds.add (ParaWorld::make_mpi_ptr (ParaTag::bsame_kdiff, bgroup_int.comm ()));
214293 worlds.add (ParaWorld::make_mpi_ptr (ParaTag::bdiff_ksame, bgroup_bp.comm ()));
215294
216- // 2. DIAG_WORLD
217- int drank, dsize, dcolor;
218- ParaWorld diag_world = split_diag_world (diag_np, nproc, my_rank, drank, dsize, dcolor);
295+ // 2. Diagonalization domain.
296+ int drank = 0 ;
297+ int dsize = 0 ;
298+ int dcolor = 0 ;
299+ ParaWorld diag_world = split_diag_world (diag_np, esolver_size, rank_in_esolver,
300+ esolver_comm, drank, dsize, dcolor);
219301 worlds.add (ParaWorld::make_mpi_ptr (ParaTag::diag, diag_world.comm ()));
220302
221- // 3. GRID_WORLD
222- int grank, gsize;
223- ParaWorld grid_world = split_grid_world (diag_np, nproc, my_rank, grank, gsize);
303+ // 3. Real-space grid domain.
304+ int grank = 0 ;
305+ int gsize = 0 ;
306+ ParaWorld grid_world = split_grid_world (diag_np, esolver_size, rank_in_esolver,
307+ esolver_comm, grank, gsize);
224308 worlds.add (ParaWorld::make_mpi_ptr (ParaTag::rgrid, grid_world.comm ()));
225309
226- // 4. matrix domain ( serial for now, will get its own split later)
310+ // 4. Matrix domain: serial for now until its own 2D-grid split lands.
227311 worlds.add (ParaWorld::make_serial (ParaTag::matrix));
228312
229313 return worlds;
0 commit comments