Skip to content

Commit 9aca420

Browse files
authored
Merge branch 'develop' into ppcg-v2
2 parents 86b95bc + cda0a73 commit 9aca420

69 files changed

Lines changed: 2859 additions & 390 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build_test_cmake.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
name: "Build without LCAO"
4242
- tag: gnu
4343
external_toolchain_args: ""
44-
build_args: "-DUSE_ELPA=OFF "
44+
build_args: "-DENABLE_ELPA=OFF "
4545
name: "Build without ELPA"
4646
- tag: gnu
4747
external_toolchain_args: ""

CMakeLists.txt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ option(ENABLE_RAPIDJSON "Enable rapid-json usage" OFF)
6565
option(ENABLE_CNPY "Enable cnpy usage" OFF)
6666

6767
# Options requiring MPI and LCAO
68-
cmake_dependent_option(ENABLE_ELPA "Enable ELPA for LCAO" ON "ENABLE_LCAO;ENABLE_MPI" OFF)
68+
cmake_dependent_option(ENABLE_ELPA "Enable ELPA eigensolver" ON "ENABLE_MPI" OFF)
6969
cmake_dependent_option(ENABLE_LIBRI "Enable LibRI for hybrid functional"
7070
OFF "ENABLE_LCAO;ENABLE_MPI" OFF)
7171
cmake_dependent_option(ENABLE_PEXSI "Enable PEXSI for LCAO" OFF "ENABLE_LCAO;ENABLE_MPI" OFF)
@@ -304,17 +304,15 @@ if(CMAKE_CXX_COMPILER_ID MATCHES Intel)
304304
set(ENABLE_ABACUS_LIBM OFF)
305305
endif()
306306

307-
if(ENABLE_LCAO)
308-
if(ENABLE_ELPA)
309-
find_package(ELPA REQUIRED)
310-
endif()
311-
if(ENABLE_PEXSI)
312-
find_package(PEXSI REQUIRED CONFIG)
313-
if(PEXSI_VERSION VERSION_LESS "2.0.0")
314-
message(FATAL_ERROR "PEXSI >= 2.0.0 is required")
315-
endif()
316-
set(CMAKE_CXX_STANDARD 14)
307+
if(ENABLE_ELPA)
308+
find_package(ELPA REQUIRED)
309+
endif()
310+
if(ENABLE_PEXSI)
311+
find_package(PEXSI REQUIRED CONFIG)
312+
if(PEXSI_VERSION VERSION_LESS "2.0.0")
313+
message(FATAL_ERROR "PEXSI >= 2.0.0 is required")
317314
endif()
315+
set(CMAKE_CXX_STANDARD 14)
318316
endif()
319317

320318
if(ENABLE_MPI)

cmake/CollectBuildInfoVars.cmake

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,6 @@ else()
126126
set(ABACUS_LCAO_ENABLED "False")
127127
endif()
128128

129-
# Core Math Libraries
130-
if(ENABLE_LCAO AND ENABLE_ELPA)
131-
set(ABACUS_ELPA_VERSION "yes (v${ELPA_VERSION})")
132-
else()
133-
set(ABACUS_ELPA_VERSION "no")
134-
endif()
135-
136129
# BLAS and FFTW libraries
137130
if(MKL_FOUND)
138131
set(ABACUS_BLAS_VENDOR "MKL")
@@ -179,6 +172,13 @@ else()
179172
endif()
180173
endif()
181174

175+
# ELPA eigensolver
176+
if(ENABLE_ELPA)
177+
set(ABACUS_ELPA_VERSION "yes (v${ELPA_VERSION})")
178+
else()
179+
set(ABACUS_ELPA_VERSION "no")
180+
endif()
181+
182182
if(ENABLE_LIBXC AND Libxc_VERSION)
183183
set(ABACUS_LIBXC_VERSION "yes (v${Libxc_VERSION})")
184184
elseif(ENABLE_LIBXC)

docs/advanced/input_files/input-main.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -673,13 +673,22 @@
673673
### kpar
674674

675675
- **Type**: Integer
676-
- **Description**: Divide all processors into kpar groups, and k points will be distributed among each group. The value taken should be less than or equal to the number of k points as well as the number of MPI processes.
676+
- **Description**: Controls k-point parallelism. The value must be positive and should not exceed either the number of k-points or the number of MPI processes.
677+
- For PW calculations, divide all MPI processes into persistent k-point pools. Each pool stores and processes a subset of the k-points.
678+
- For LCAO calculations with lapack, genelpa, elpa, or scalapack_gvx, divide the diagonalization work into temporary k-point pools. After diagonalization, the eigenvalues and distributed wavefunctions are restored for all k-points before occupations, density matrices, and output are evaluated.
679+
- Multi-process LCAO cusolver uses its own active-GPU distribution and does not use this value to define its k-point layout. Other LCAO eigensolvers do not use the temporary k-point-pool implementation.
677680
- **Default**: 1
678681

679682
### bndpar
680683

681684
- **Type**: Integer
682-
- **Description**: Divide all processors into bndpar groups for SDFT or the BPCG solver. bndpar must be positive, no greater than the number of MPI processes, and kpar * bndpar must divide the number of MPI processes exactly.
685+
- **Availability**: *([`basis_type`](#basis_type)==pw and [`esolver_type`](#esolver_type)==sdft) or ([`basis_type`](#basis_type)==pw and [`esolver_type`](#esolver_type)==ksdft and [`ks_solver`](#ks_solver)==bpcg)*
686+
- **Description**: Controls band-group parallelism for PW SDFT and PW KSDFT calculations using the BPCG eigensolver.
687+
- Within each k-point pool, divide the MPI processes into bndpar band groups. Each group contains NPROC / (kpar * bndpar) processes when bndpar is greater than 1.
688+
- With BPCG, distribute contiguous ranges of global Kohn-Sham bands among the band groups. nbands does not need to be divisible by bndpar, but bndpar cannot exceed a positive nbands. Groups with lower indices receive one additional band when necessary.
689+
- In SDFT, distribute stochastic orbitals among the band groups. When the deterministic Kohn-Sham eigensolver is not BPCG, band group 0 calculates the deterministic orbitals and broadcasts them to the other groups.
690+
- bndpar must be positive and no greater than the number of MPI processes. When bndpar is greater than 1, kpar * bndpar must divide the number of MPI processes exactly.
691+
> Note: For PW calculations on GPU, if the input kpar * bndpar differs from the number of MPI processes, ABACUS automatically sets the effective kpar to NPROC / bndpar.
683692
- **Default**: 1
684693

685694
### latname
@@ -4516,9 +4525,9 @@
45164525
### ocp_set
45174526

45184527
- **Type**: String
4519-
- **Description**: Fixed occupation weights used when ocp is true. Values are assigned band by band for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x.
4528+
- **Description**: Fixed occupation weights used when ocp is true. Values are assigned in band order for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x.
45204529
- Example: 1 10*1 0 1 expands to 13 values, with the 12th value equal to 0 and all other values equal to 1.
4521-
- After expansion, the array length must equal nks * nbands.
4530+
- After expansion, provide one block of nbands values for each k-point. If nspin is 2, provide all k-point blocks for spin up followed by all k-point blocks for spin down; otherwise, provide one block per k-point.
45224531
- The sum of all weights must equal nelec; otherwise the calculation terminates with an error.
45234532
- **Default**: None
45244533

docs/parameters.yaml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,26 @@ parameters:
100100
category: System variables
101101
type: Integer
102102
description: |
103-
Divide all processors into kpar groups, and k points will be distributed among each group. The value taken should be less than or equal to the number of k points as well as the number of MPI processes.
103+
Controls k-point parallelism. The value must be positive and should not exceed either the number of k-points or the number of MPI processes.
104+
* For PW calculations, divide all MPI processes into persistent k-point pools. Each pool stores and processes a subset of the k-points.
105+
* For LCAO calculations with lapack, genelpa, elpa, or scalapack_gvx, divide the diagonalization work into temporary k-point pools. After diagonalization, the eigenvalues and distributed wavefunctions are restored for all k-points before occupations, density matrices, and output are evaluated.
106+
* Multi-process LCAO cusolver uses its own active-GPU distribution and does not use this value to define its k-point layout. Other LCAO eigensolvers do not use the temporary k-point-pool implementation.
104107
default_value: "1"
105108
unit: ""
106109
availability: ""
107110
- name: bndpar
108111
category: System variables
109112
type: Integer
110113
description: |
111-
Divide all processors into bndpar groups for SDFT or the BPCG solver. bndpar must be positive, no greater than the number of MPI processes, and kpar * bndpar must divide the number of MPI processes exactly.
114+
Controls band-group parallelism for PW SDFT and PW KSDFT calculations using the BPCG eigensolver.
115+
* Within each k-point pool, divide the MPI processes into bndpar band groups. Each group contains NPROC / (kpar * bndpar) processes when bndpar is greater than 1.
116+
* With BPCG, distribute contiguous ranges of global Kohn-Sham bands among the band groups. nbands does not need to be divisible by bndpar, but bndpar cannot exceed a positive nbands. Groups with lower indices receive one additional band when necessary.
117+
* In SDFT, distribute stochastic orbitals among the band groups. When the deterministic Kohn-Sham eigensolver is not BPCG, band group 0 calculates the deterministic orbitals and broadcasts them to the other groups.
118+
* bndpar must be positive and no greater than the number of MPI processes. When bndpar is greater than 1, kpar * bndpar must divide the number of MPI processes exactly.
119+
[NOTE] For PW calculations on GPU, if the input kpar * bndpar differs from the number of MPI processes, ABACUS automatically sets the effective kpar to NPROC / bndpar.
112120
default_value: "1"
113121
unit: ""
114-
availability: ""
122+
availability: (basis_type==pw and esolver_type==sdft) or (basis_type==pw and esolver_type==ksdft and ks_solver==bpcg)
115123
- name: latname
116124
category: System variables
117125
type: String
@@ -2745,9 +2753,9 @@ parameters:
27452753
category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"
27462754
type: String
27472755
description: |
2748-
Fixed occupation weights used when ocp is true. Values are assigned band by band for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x.
2756+
Fixed occupation weights used when ocp is true. Values are assigned in band order for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x.
27492757
* Example: 1 10*1 0 1 expands to 13 values, with the 12th value equal to 0 and all other values equal to 1.
2750-
* After expansion, the array length must equal nks * nbands.
2758+
* After expansion, provide one block of nbands values for each k-point. If nspin is 2, provide all k-point blocks for spin up followed by all k-point blocks for spin down; otherwise, provide one block per k-point.
27512759
* The sum of all weights must equal nelec; otherwise the calculation terminates with an error.
27522760
default_value: None
27532761
unit: ""

source/CMakeLists.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,10 @@ target_link_libraries(
687687
device
688688
base)
689689

690+
if(ENABLE_ELPA)
691+
target_link_libraries(${ABACUS_BIN_NAME} PRIVATE genelpa)
692+
endif()
693+
690694
if(ENABLE_LCAO)
691695
target_link_libraries(
692696
${ABACUS_BIN_NAME}
@@ -702,9 +706,6 @@ if(ENABLE_LCAO)
702706
if(ENABLE_LIBRI)
703707
target_link_libraries(${ABACUS_BIN_NAME} PRIVATE bse)
704708
endif()
705-
if(ENABLE_ELPA)
706-
target_link_libraries(${ABACUS_BIN_NAME} PRIVATE genelpa)
707-
endif()
708709
if(ENABLE_PEXSI)
709710
target_link_libraries(${ABACUS_BIN_NAME} PRIVATE pexsi)
710711
endif()

source/Makefile.Objects

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ OBJS_IO=module_parameter/input_conv.o\
552552
cal_test.o\
553553
module_dos/write_dos_pw.o\
554554
nscf_fermi_surf.o\
555+
band_parallel_output.o\
555556
write_bands.o\
556557
module_dos/cal_dos.o\
557558
module_dos/cal_pdos_gamma.o\

source/source_base/parallel_grid.cpp

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -422,60 +422,81 @@ void Parallel_Grid::zpiece_to_stogroup(double* zpiece, const int& iz, double* rh
422422
// ofs_running << "\n iz = " << iz << " Done.";
423423
return;
424424
}
425+
426+
// Taoni modified on 2026-08-21, fixed BPCG out_chg MPI_ERR_RANK
425427
void Parallel_Grid::reduce(double* rhotot, const double* const rhoin, const bool reduce_all_pool) const
426428
{
427-
// ModuleBase::TITLE("Parallel_Grid","reduce");
428-
429-
// if not the first pool, wait here until processpr 0
430-
// send the Barrier command.
429+
// POOL_WORLD communicators are disjoint, so inactive pools may return
430+
// without skipping a collective required by an active pool.
431431
if (!reduce_all_pool && GlobalV::MY_POOL != 0)
432432
{
433433
return;
434434
}
435435

436-
double* zpiece = new double[this->ncxy];
436+
assert(rhoin != nullptr);
437+
assert(this->nrxx == this->ncxy * this->nczp);
437438

438-
for (int iz = 0; iz < this->ncz; iz++)
439-
{
440-
const int znow = iz - this->startz[GlobalV::MY_POOL][GlobalV::RANK_IN_POOL];
441-
const int proc = this->whichpro[GlobalV::MY_POOL][iz];
442-
const int proc_loc = this->whichpro_loc[GlobalV::MY_POOL][iz]; // Obtain the local processor index in the pool
443-
ModuleBase::GlobalFunc::ZEROS(zpiece, this->ncxy);
444-
int tag = iz;
445-
MPI_Status ierror;
446-
447-
// Local processor 0 collects data from all other processors in the pool
448-
// proc = proc_loc if GlobalV::MY_POOL == 0
449-
if (proc_loc == GlobalV::RANK_IN_POOL)
450-
{
451-
for (int ir = 0; ir < ncxy; ir++)
452-
{
453-
zpiece[ir] = rhoin[ir * this->nczp + znow];
454-
}
455-
// Send data to the root of the pool
456-
if (GlobalV::RANK_IN_POOL != 0)
457-
{
458-
MPI_Send(zpiece, ncxy, MPI_DOUBLE, 0, tag, POOL_WORLD);
459-
}
460-
}
439+
int pool_size = 0;
440+
MPI_Comm_size(POOL_WORLD, &pool_size);
441+
442+
// Derive slab ownership from the communicator used below. Precomputed
443+
// source ranks may belong to a larger BPCG layout and are not necessarily
444+
// valid ranks in the current POOL_WORLD.
445+
std::vector<int> local_z_counts(pool_size);
446+
MPI_Allgather(&this->nczp, 1, MPI_INT, local_z_counts.data(), 1, MPI_INT, POOL_WORLD);
461447

462-
// The root of the pool receives data from other processors
463-
if (GlobalV::RANK_IN_POOL == 0 && proc_loc != GlobalV::RANK_IN_POOL)
448+
// MPI_Gatherv concatenates complete [xy][local_z] buffers by rank.
449+
// These displacements therefore address rank blocks, not global z coordinates.
450+
std::vector<int> receive_counts(pool_size);
451+
std::vector<int> displacements(pool_size, 0);
452+
int total_z = 0;
453+
for (int rank = 0; rank < pool_size; ++rank)
454+
{
455+
receive_counts[rank] = local_z_counts[rank] * this->ncxy;
456+
if (rank > 0)
464457
{
465-
MPI_Recv(zpiece, ncxy, MPI_DOUBLE, proc_loc, tag, POOL_WORLD, &ierror);
458+
displacements[rank] = displacements[rank - 1] + receive_counts[rank - 1];
466459
}
460+
total_z += local_z_counts[rank];
461+
}
462+
assert(total_z == this->ncz);
467463

468-
if (GlobalV::RANK_IN_POOL == 0)
464+
// Only the pool root needs storage for the gathered global grid.
465+
// MPI ignores the receive buffer on all non-root ranks.
466+
std::vector<double> gathered_data;
467+
if (GlobalV::RANK_IN_POOL == 0)
468+
{
469+
assert(rhotot != nullptr);
470+
gathered_data.resize(this->ncxyz);
471+
}
472+
MPI_Gatherv(rhoin,
473+
this->nrxx,
474+
MPI_DOUBLE,
475+
gathered_data.data(),
476+
receive_counts.data(),
477+
displacements.data(),
478+
MPI_DOUBLE,
479+
0,
480+
POOL_WORLD);
481+
482+
if (GlobalV::RANK_IN_POOL == 0)
483+
{
484+
// Rank blocks cannot be copied directly to rhotot: each block stores
485+
// [xy][local_z], whereas Cube output expects [xy][global_z].
486+
// The slab decomposition is contiguous and ordered by POOL_WORLD rank.
487+
int global_z_start = 0;
488+
for (int rank = 0; rank < pool_size; ++rank)
469489
{
490+
const int local_z = local_z_counts[rank];
470491
for (int ixy = 0; ixy < this->ncxy; ++ixy)
471492
{
472-
rhotot[ixy * ncz + iz] = zpiece[ixy];
493+
for (int iz = 0; iz < local_z; ++iz)
494+
{
495+
rhotot[ixy * this->ncz + global_z_start + iz] = gathered_data[displacements[rank] + ixy * local_z + iz];
496+
}
473497
}
498+
global_z_start += local_z;
474499
}
475500
}
476-
477-
delete[] zpiece;
478-
479-
return;
480501
}
481502
#endif

source/source_cell/cal_atoms_info.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ class CalAtomsInfo
143143
result.nbands = nbands;
144144
unitcell::cal_nbands(static_cast<int>(result.nelec), result.nlocal, nelec_spin, result.nbands,
145145
esolver_type, lspinorb, nspin, basis_type, smearing_method);
146+
// Taoni add check for bndpar > nbands on 2026-08-21
147+
if (ks_solver == "bpcg" && result.nbands > 0 && bndpar > result.nbands)
148+
{
149+
ModuleBase::WARNING_QUIT("CalAtomsInfo", "bndpar cannot exceed nbands when ks_solver is bpcg");
150+
}
146151

147152
// calculate the number of nbands_local
148153
result.nbands_l = result.nbands;

source/source_cell/parallel_kpoints.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,20 @@ void Parallel_Kpoints::set_startpro_pool()
113113
return;
114114
}
115115

116-
117116
// gather kpoints from all processor pools, only need to be called by the first processor of each pool.
118117
void Parallel_Kpoints::gatherkvec(const std::vector<ModuleBase::Vector3<double>>& vec_local,
119118
std::vector<ModuleBase::Vector3<double>>& vec_global) const
120119
{
120+
int world_rank = 0;
121+
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
122+
// Taoni fix bndpar on 2026-08-21
123+
// rank_in_pool is repeated in every band group under BPCG. The first
124+
// world rank of the k-point pool instead identifies one unique contributor.
125+
const bool is_pool_root = (world_rank == this->startpro_pool[this->my_pool]);
121126
vec_global.resize(this->nkstot_np, ModuleBase::Vector3<double>(0.0, 0.0, 0.0));
122127
for (int i = 0; i < this->nks_np; ++i)
123128
{
124-
125-
if (this->rank_in_pool == 0)
129+
if (is_pool_root)
126130
{
127131
vec_global[i + startk_pool[this->my_pool]] = vec_local[i];
128132
}
@@ -154,7 +158,6 @@ void Parallel_Kpoints::pool_collection(double& value, const double* wk, const in
154158
{
155159
MPI_Status ierror;
156160
MPI_Recv(&value, 1, MPI_DOUBLE, this->startpro_pool[pool], ik, MPI_COMM_WORLD, &ierror);
157-
158161
}
159162
}
160163
else
@@ -215,7 +218,7 @@ void Parallel_Kpoints::pool_collection_aux(T* value, const V& w, const int& dim,
215218
T* p = &w.ptr[begin];
216219
// temprary restrict kpar=1 for NSPIN=2 case for generating_orbitals
217220
int pool = 0;
218-
if (this->nspin != 2)
221+
if (this->nspin != 2)
219222
{
220223
pool = this->whichpool[ik];
221224
}

0 commit comments

Comments
 (0)