Skip to content

Commit 87f0609

Browse files
author
dyzheng
committed
Feature: add MPI communicator injection for general-purpose parallel workflows
Replace hardcoded MPI_COMM_WORLD with configurable base_comm parameter in the MPI initialization path, enabling workflows like parallel NEB, phonon calculations, or replica exchange MD to split MPI ranks into independent groups, each running its own ESolver instance in a separate subprocess.
1 parent c039e92 commit 87f0609

9 files changed

Lines changed: 306 additions & 110 deletions

File tree

python/pyabacus/src/ModuleESolver/py_esolver_lcao.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -448,15 +448,20 @@ PyESolverLCAO<TK, TR>::~PyESolverLCAO()
448448
}
449449

450450
template <typename TK, typename TR>
451-
void PyESolverLCAO<TK, TR>::initialize(const std::string& input_dir)
451+
void PyESolverLCAO<TK, TR>::initialize(const std::string& input_dir, int mpi_comm_handle)
452452
{
453453
// Placeholder: will be implemented in Phase 3
454454
// This will:
455-
// 1. Read INPUT file from input_dir
456-
// 2. Initialize UnitCell
457-
// 3. Create ESolver_KS_LCAO instance
455+
// 1. Convert mpi_comm_handle to MPI_Comm via MPI_Comm_f2c()
456+
// (if mpi_comm_handle >= 0, otherwise use MPI_COMM_WORLD)
457+
// 2. Use the resulting communicator for MPI_Comm_size/rank
458+
// 3. Pass base_comm to Parallel_Global::init_pools(), split_diag_world(), split_grid_world()
459+
// 4. Read INPUT file from input_dir
460+
// 5. Initialize UnitCell
461+
// 6. Create ESolver_KS_LCAO instance
458462
initialized_ = true;
459-
std::cout << "[PyESolverLCAO] Initialized with input directory: " << input_dir << std::endl;
463+
std::cout << "[PyESolverLCAO] Initialized with input directory: " << input_dir
464+
<< ", mpi_comm_handle: " << mpi_comm_handle << std::endl;
460465
}
461466

462467
template <typename TK, typename TR>
@@ -861,7 +866,10 @@ void bind_esolver_lcao(py::module& m, const std::string& suffix)
861866
----------
862867
input_dir : str
863868
Directory containing INPUT, STRU, and other input files
864-
)pbdoc", "input_dir"_a)
869+
mpi_comm_handle : int, optional
870+
Fortran MPI communicator handle from mpi4py comm.py2f().
871+
Use -1 (default) to use MPI_COMM_WORLD.
872+
)pbdoc", "input_dir"_a, "mpi_comm_handle"_a = -1)
865873
.def("before_all_runners", &ESolver::before_all_runners,
866874
"Initialize calculation environment")
867875

python/pyabacus/src/ModuleESolver/py_esolver_lcao.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,10 @@ class PyESolverLCAO
284284
// ==================== Initialization ====================
285285

286286
/// Initialize from INPUT file directory
287-
void initialize(const std::string& input_dir);
287+
/// @param input_dir Directory containing INPUT, STRU, and other input files
288+
/// @param mpi_comm_handle Fortran MPI communicator handle from mpi4py comm.py2f().
289+
/// Use -1 (default) to use MPI_COMM_WORLD.
290+
void initialize(const std::string& input_dir, int mpi_comm_handle = -1);
288291

289292
/// Call before_all_runners
290293
void before_all_runners();

python/pyabacus/src/pyabacus/esolver/workflow.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class LCAOWorkflow:
5454
'after_scf', # Called after after_scf()
5555
]
5656

57-
def __init__(self, input_dir: str, gamma_only: bool = True):
57+
def __init__(self, input_dir: str, gamma_only: bool = True, mpi_comm=None):
5858
"""
5959
Initialize LCAOWorkflow.
6060
@@ -64,9 +64,13 @@ def __init__(self, input_dir: str, gamma_only: bool = True):
6464
Directory containing input files
6565
gamma_only : bool
6666
Use gamma-only calculation if True, multi-k if False
67+
mpi_comm : mpi4py.MPI.Comm, optional
68+
MPI communicator to use. If None, uses MPI_COMM_WORLD.
69+
Pass a sub-communicator to run this ESolver on a subset of MPI ranks.
6770
"""
6871
self._input_dir = input_dir
6972
self._gamma_only = gamma_only
73+
self._mpi_comm = mpi_comm
7074
self._esolver = None
7175
self._initialized = False
7276
self._scf_running = False
@@ -96,10 +100,17 @@ def initialize(self) -> None:
96100
"Make sure pyabacus is properly installed with ESolver support."
97101
) from e
98102

99-
self._esolver.initialize(self._input_dir)
103+
self._esolver.initialize(self._input_dir, mpi_comm_handle=self._mpi_comm_handle)
100104
self._esolver.before_all_runners()
101105
self._initialized = True
102106

107+
@property
108+
def _mpi_comm_handle(self) -> int:
109+
"""Convert mpi4py communicator to Fortran handle, or -1 for default."""
110+
if self._mpi_comm is None:
111+
return -1
112+
return self._mpi_comm.py2f()
113+
103114
def register_callback(self, event: str, callback: Callable[['LCAOWorkflow'], None]) -> None:
104115
"""
105116
Register a callback function for a specific event.

source/source_base/parallel_common.cpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,75 +7,75 @@
77
#include <cstring>
88

99
#ifdef __MPI
10-
void Parallel_Common::bcast_string(std::string& object) // Peize Lin fix bug 2019-03-18
10+
void Parallel_Common::bcast_string(std::string& object, MPI_Comm comm) // Peize Lin fix bug 2019-03-18
1111
{
1212
int size = object.size();
13-
MPI_Bcast(&size, 1, MPI_INT, 0, MPI_COMM_WORLD);
14-
13+
MPI_Bcast(&size, 1, MPI_INT, 0, comm);
14+
1515
int my_rank;
16-
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
17-
16+
MPI_Comm_rank(comm, &my_rank);
17+
1818
if (0 != my_rank)
1919
{
2020
object.resize(size);
2121
}
2222

23-
MPI_Bcast(&object[0], size, MPI_CHAR, 0, MPI_COMM_WORLD);
23+
MPI_Bcast(&object[0], size, MPI_CHAR, 0, comm);
2424
return;
2525
}
2626

27-
void Parallel_Common::bcast_string(std::string* object, const int n) // Peize Lin fix bug 2019-03-18
27+
void Parallel_Common::bcast_string(std::string* object, const int n, MPI_Comm comm) // Peize Lin fix bug 2019-03-18
2828
{
2929
for (int i = 0; i < n; i++)
30-
bcast_string(object[i]);
30+
bcast_string(object[i], comm);
3131
return;
3232
}
3333

34-
void Parallel_Common::bcast_complex_double(std::complex<double>& object)
34+
void Parallel_Common::bcast_complex_double(std::complex<double>& object, MPI_Comm comm)
3535
{
36-
MPI_Bcast(&object, 1, MPI_DOUBLE_COMPLEX, 0, MPI_COMM_WORLD);
36+
MPI_Bcast(&object, 1, MPI_DOUBLE_COMPLEX, 0, comm);
3737
}
3838

39-
void Parallel_Common::bcast_complex_double(std::complex<double>* object, const int n)
39+
void Parallel_Common::bcast_complex_double(std::complex<double>* object, const int n, MPI_Comm comm)
4040
{
41-
MPI_Bcast(object, n, MPI_DOUBLE_COMPLEX, 0, MPI_COMM_WORLD);
41+
MPI_Bcast(object, n, MPI_DOUBLE_COMPLEX, 0, comm);
4242
}
4343

44-
void Parallel_Common::bcast_double(double& object)
44+
void Parallel_Common::bcast_double(double& object, MPI_Comm comm)
4545
{
46-
MPI_Bcast(&object, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
46+
MPI_Bcast(&object, 1, MPI_DOUBLE, 0, comm);
4747
}
4848

49-
void Parallel_Common::bcast_double(double* object, const int n)
49+
void Parallel_Common::bcast_double(double* object, const int n, MPI_Comm comm)
5050
{
51-
MPI_Bcast(object, n, MPI_DOUBLE, 0, MPI_COMM_WORLD);
51+
MPI_Bcast(object, n, MPI_DOUBLE, 0, comm);
5252
}
5353

54-
void Parallel_Common::bcast_int(int& object)
54+
void Parallel_Common::bcast_int(int& object, MPI_Comm comm)
5555
{
56-
MPI_Bcast(&object, 1, MPI_INT, 0, MPI_COMM_WORLD);
56+
MPI_Bcast(&object, 1, MPI_INT, 0, comm);
5757
}
5858

59-
void Parallel_Common::bcast_int(int* object, const int n)
59+
void Parallel_Common::bcast_int(int* object, const int n, MPI_Comm comm)
6060
{
61-
MPI_Bcast(object, n, MPI_INT, 0, MPI_COMM_WORLD);
61+
MPI_Bcast(object, n, MPI_INT, 0, comm);
6262
}
6363

64-
void Parallel_Common::bcast_bool(bool& object)
64+
void Parallel_Common::bcast_bool(bool& object, MPI_Comm comm)
6565
{
6666
int swap = object;
6767
int my_rank;
68-
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
68+
MPI_Comm_rank(comm, &my_rank);
6969
if (my_rank == 0)
7070
swap = object;
71-
MPI_Bcast(&swap, 1, MPI_INT, 0, MPI_COMM_WORLD);
71+
MPI_Bcast(&swap, 1, MPI_INT, 0, comm);
7272
if (my_rank != 0)
7373
object = static_cast<bool>(swap);
7474
}
7575

76-
void Parallel_Common::bcast_char(char* object, const int n)
76+
void Parallel_Common::bcast_char(char* object, const int n, MPI_Comm comm)
7777
{
78-
MPI_Bcast(object, n, MPI_CHAR, 0, MPI_COMM_WORLD);
78+
MPI_Bcast(object, n, MPI_CHAR, 0, comm);
7979
}
8080

8181
#endif

source/source_base/parallel_common.h

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,58 @@
1010
namespace Parallel_Common
1111
{
1212
//(1) bcast array
13-
void bcast_complex_double(std::complex<double>* object, const int n);
14-
void bcast_string(std::string* object, const int n);
15-
void bcast_double(double* object, const int n);
16-
void bcast_int(int* object, const int n);
17-
void bcast_char(char* object, const int n);
13+
void bcast_complex_double(std::complex<double>* object, const int n
14+
#ifdef __MPI
15+
, MPI_Comm comm = MPI_COMM_WORLD
16+
#endif
17+
);
18+
void bcast_string(std::string* object, const int n
19+
#ifdef __MPI
20+
, MPI_Comm comm = MPI_COMM_WORLD
21+
#endif
22+
);
23+
void bcast_double(double* object, const int n
24+
#ifdef __MPI
25+
, MPI_Comm comm = MPI_COMM_WORLD
26+
#endif
27+
);
28+
void bcast_int(int* object, const int n
29+
#ifdef __MPI
30+
, MPI_Comm comm = MPI_COMM_WORLD
31+
#endif
32+
);
33+
void bcast_char(char* object, const int n
34+
#ifdef __MPI
35+
, MPI_Comm comm = MPI_COMM_WORLD
36+
#endif
37+
);
1838

1939
//(2) bcast single
20-
void bcast_complex_double(std::complex<double>& object);
21-
void bcast_string(std::string& object);
22-
void bcast_double(double& object);
23-
void bcast_int(int& object);
24-
void bcast_bool(bool& object);
40+
void bcast_complex_double(std::complex<double>& object
41+
#ifdef __MPI
42+
, MPI_Comm comm = MPI_COMM_WORLD
43+
#endif
44+
);
45+
void bcast_string(std::string& object
46+
#ifdef __MPI
47+
, MPI_Comm comm = MPI_COMM_WORLD
48+
#endif
49+
);
50+
void bcast_double(double& object
51+
#ifdef __MPI
52+
, MPI_Comm comm = MPI_COMM_WORLD
53+
#endif
54+
);
55+
void bcast_int(int& object
56+
#ifdef __MPI
57+
, MPI_Comm comm = MPI_COMM_WORLD
58+
#endif
59+
);
60+
void bcast_bool(bool& object
61+
#ifdef __MPI
62+
, MPI_Comm comm = MPI_COMM_WORLD
63+
#endif
64+
);
2565

2666
} // namespace Parallel_Common
2767

source/source_base/parallel_global.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,19 @@ void Parallel_Global::split_diag_world(const int& diag_np,
4949
const int& my_rank,
5050
int& drank,
5151
int& dsize,
52-
int& dcolor)
52+
int& dcolor
53+
#ifdef __MPI
54+
, MPI_Comm base_comm
55+
#endif
56+
)
5357
{
5458
#ifdef __MPI
5559
assert(diag_np > 0);
5660
int group_grid_np = -1;
5761
int color = -1;
5862
int key = -1;
5963
divide_mpi_groups(nproc, diag_np, my_rank, group_grid_np, key, color);
60-
MPI_Comm_split(MPI_COMM_WORLD, color, key, &DIAG_WORLD);
64+
MPI_Comm_split(base_comm, color, key, &DIAG_WORLD);
6165
MPI_Comm_rank(DIAG_WORLD, &drank);
6266
MPI_Comm_size(DIAG_WORLD, &dsize);
6367
dcolor = color;
@@ -69,15 +73,19 @@ void Parallel_Global::split_diag_world(const int& diag_np,
6973
return;
7074
}
7175

72-
void Parallel_Global::split_grid_world(const int diag_np, const int& nproc, const int& my_rank, int& grank, int& gsize)
76+
void Parallel_Global::split_grid_world(const int diag_np, const int& nproc, const int& my_rank, int& grank, int& gsize
77+
#ifdef __MPI
78+
, MPI_Comm base_comm
79+
#endif
80+
)
7381
{
7482
#ifdef __MPI
7583
assert(diag_np > 0);
7684
int group_grid_np = -1;
7785
int color = -1;
7886
int key = -1;
7987
divide_mpi_groups(nproc, diag_np, my_rank, group_grid_np, color, key);
80-
MPI_Comm_split(MPI_COMM_WORLD, color, key, &GRID_WORLD);
88+
MPI_Comm_split(base_comm, color, key, &GRID_WORLD);
8189
MPI_Comm_rank(GRID_WORLD, &grank);
8290
MPI_Comm_size(GRID_WORLD, &gsize);
8391
#else
@@ -258,7 +266,11 @@ void Parallel_Global::init_pools(const int& NPROC,
258266
int& MY_BNDGROUP,
259267
int& NPROC_IN_POOL,
260268
int& RANK_IN_POOL,
261-
int& MY_POOL)
269+
int& MY_POOL
270+
#ifdef __MPI
271+
, MPI_Comm base_comm
272+
#endif
273+
)
262274
{
263275
#ifdef __MPI
264276
//----------------------------------------------------------
@@ -273,7 +285,8 @@ void Parallel_Global::init_pools(const int& NPROC,
273285
MY_BNDGROUP,
274286
NPROC_IN_POOL,
275287
RANK_IN_POOL,
276-
MY_POOL);
288+
MY_POOL,
289+
base_comm);
277290

278291
// for test
279292
// turn on when you want to check the index of pools.
@@ -321,7 +334,8 @@ void Parallel_Global::divide_pools(const int& NPROC,
321334
int& MY_BNDGROUP,
322335
int& NPROC_IN_POOL,
323336
int& RANK_IN_POOL,
324-
int& MY_POOL)
337+
int& MY_POOL,
338+
MPI_Comm base_comm)
325339
{
326340
// note: the order of k-point parallelization and band parallelization is important
327341
// The order will not change the behavior of KP_WORLD or BP_WORLD, and MY_POOL
@@ -334,7 +348,7 @@ void Parallel_Global::divide_pools(const int& NPROC,
334348
"When BNDPAR > 1, number of processes NPROC must be divisible by the number of groups BNDPAR * KPAR.");
335349
}
336350
// k-point parallelization
337-
MPICommGroup kpar_group(MPI_COMM_WORLD);
351+
MPICommGroup kpar_group(base_comm);
338352
kpar_group.divide_group_comm(KPAR, false);
339353

340354
// band parallelization
@@ -362,16 +376,16 @@ void Parallel_Global::divide_pools(const int& NPROC,
362376
NPROC_IN_BNDGROUP = kpar_group.ngroups * bndpar_group.nprocs_in_group;
363377
RANK_IN_BPGROUP = kpar_group.my_group * bndpar_group.nprocs_in_group + bndpar_group.rank_in_group;
364378
MY_BNDGROUP = bndpar_group.my_group;
365-
MPI_Comm_split(MPI_COMM_WORLD, MY_BNDGROUP, RANK_IN_BPGROUP, &INT_BGROUP);
379+
MPI_Comm_split(base_comm, MY_BNDGROUP, RANK_IN_BPGROUP, &INT_BGROUP);
366380
MPI_Comm_dup(bndpar_group.inter_comm, &BP_WORLD);
367381
}
368382
else
369383
{
370384
NPROC_IN_BNDGROUP = NPROC;
371385
RANK_IN_BPGROUP = MY_RANK;
372386
MY_BNDGROUP = 0;
373-
MPI_Comm_dup(MPI_COMM_WORLD, &INT_BGROUP);
374-
MPI_Comm_split(MPI_COMM_WORLD, MY_RANK, 0, &BP_WORLD);
387+
MPI_Comm_dup(base_comm, &INT_BGROUP);
388+
MPI_Comm_split(base_comm, MY_RANK, 0, &BP_WORLD);
375389
}
376390
return;
377391
}

0 commit comments

Comments
 (0)