Skip to content

Commit d56eebc

Browse files
author
abacus_fixer
committed
feat(module_parallel): add esolver/images domains and parent-comm splits
Add two top-level parallel domains for multi-image calculations (e.g. NEB replicas with independent unit cells): - para_esolver_world [color = image_id]: all processes of one esolver instance; every existing solver domain is now derived from it instead of being hard-wired to MPI_COMM_WORLD. - para_images_world [color = rank_in_esolver]: cross-image communicator connecting corresponding ranks; MPI_COMM_NULL for nimage == 1 or uneven splits, following the KP_WORLD convention. split_pools/split_diag_world/split_grid_world now take a parent MPI_Comm, and setup_para_worlds takes nimage and builds the full hierarchy: WORLD -> images split -> esolver domain -> kmesh/pw/ bgroup/diag/rgrid/matrix. The hierarchy tree is documented in para_setup.h. New MPI test (mpirun -np 4) verifies single-image degradation, 2-image split ranks/sizes, derived-domain sizes, and kpar=2 inside images; multi-image tests skip under single-rank direct execution.
1 parent fc0642f commit d56eebc

6 files changed

Lines changed: 440 additions & 114 deletions

File tree

source/source_base/module_parallel/para_setup.cpp

Lines changed: 132 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,43 @@ void divide_mpi_groups(int nproc, int num_groups, int rank, bool even,
4343

4444
namespace {
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.
4752
struct 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

5762
GroupSplitResult 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

Comments
 (0)