From 4f90c977e85ab8c42d145df2519d8a3727d53de0 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 10:48:58 +0800 Subject: [PATCH 01/13] Refactor occupy to remove parameter.h dependency, pass nspin and npool explicitly Remove the `#include "source_io/module_parameter/parameter.h"` from occupy.cpp and replace all PARAM references with explicit parameters: - iweights gains `const int nspin` (replaces PARAM.inp.nspin) - gweights/efermig/sumkg gain `const int npool` (replaces GlobalV::KPAR * PARAM.inp.bndpar computed inside sumkg) - gweights weight loop bound changes from PARAM.globalv.nbands_l to the existing `nband` parameter (equivalent in production) Update the sole production caller (elecstate_tools.cpp) to pass the new arguments, extracting nspin/npool as locals so the global dependency budget net decreases by 7. Update the unit test to drop the parameter.h include hack and adjust the Gweights expectation to the exact half-filled single-band solution now that the weight loop actually executes. Drop `parameter` from the test target link list. --- source/source_estate/elecstate_tools.cpp | 23 ++++--- source/source_estate/occupy.cpp | 62 +++++++++++-------- source/source_estate/occupy.h | 24 ++++--- source/source_estate/test/CMakeLists.txt | 2 +- .../test/elecstate_occupy_test.cpp | 29 ++++----- 5 files changed, 80 insertions(+), 60 deletions(-) diff --git a/source/source_estate/elecstate_tools.cpp b/source/source_estate/elecstate_tools.cpp index e4e6c2930a..fc3efd6b23 100644 --- a/source/source_estate/elecstate_tools.cpp +++ b/source/source_estate/elecstate_tools.cpp @@ -100,25 +100,32 @@ void calculate_weights(const ModuleBase::matrix& ekb, const int nks = ekb.nr; if (!(Occupy::use_gaussian_broadening || Occupy::fixed_occupations)) { + const int nspin = PARAM.inp.nspin; // Taoni fix smearing_method=fixed for BPCG on 2026-08-21 // Integer occupations use global band indices even when ekb is a local // contiguous BPCG shard. const int band_offset = get_band_offset(nbands, global_nbands); if (PARAM.globalv.two_fermi) { - Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[0], ekb, eferm.ef_up, wg, 0, klist->isk); - Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[1], ekb, eferm.ef_dw, wg, 1, klist->isk); + Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[0], ekb, eferm.ef_up, wg, + nspin, 0, klist->isk); + Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[1], ekb, eferm.ef_dw, wg, + nspin, 1, klist->isk); // ef = ( ef_up + ef_dw ) / 2.0_dp need??? mohan add 2012-04-16 // Keep independent Fermi levels for the two spin channels. } else { // A spin selector of -1 requests the combined-spin occupation path. - Occupy::iweights(nks, klist->wk, nbands, band_offset, PARAM.inp.nelec, ekb, eferm.ef, wg, -1, klist->isk); + Occupy::iweights(nks, klist->wk, nbands, band_offset, PARAM.inp.nelec, ekb, eferm.ef, wg, + nspin, -1, klist->isk); } } else if (Occupy::use_gaussian_broadening) { + // The pool count is needed both by the Fermi-energy search inside + // gweights and by the all-pool demet reduction below. + const int npool = GlobalV::KPAR * PARAM.inp.bndpar; if (PARAM.globalv.two_fermi) { double demet_up = 0.0; @@ -134,7 +141,8 @@ void calculate_weights(const ModuleBase::matrix& ekb, demet_up, wg, 0, - klist->isk); + klist->isk, + npool); Occupy::gweights(nks, klist->wk, nbands, @@ -146,7 +154,8 @@ void calculate_weights(const ModuleBase::matrix& ekb, demet_dw, wg, 1, - klist->isk); + klist->isk, + npool); f_en.demet = demet_up + demet_dw; } else @@ -163,11 +172,11 @@ void calculate_weights(const ModuleBase::matrix& ekb, f_en.demet, wg, -1, - klist->isk); + klist->isk, + npool); } #ifdef __MPI // demet is accumulated independently on every k-point and band partition. - const int npool = GlobalV::KPAR * PARAM.inp.bndpar; Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, f_en.demet); #endif } diff --git a/source/source_estate/occupy.cpp b/source/source_estate/occupy.cpp index 0a492735c6..8bb1a428d5 100644 --- a/source/source_estate/occupy.cpp +++ b/source/source_estate/occupy.cpp @@ -3,7 +3,6 @@ #include "source_base/constants.h" #include "source_base/mymath.h" #include "source_base/parallel_reduce.h" -#include "source_io/module_parameter/parameter.h" Occupy::Occupy() { @@ -126,6 +125,7 @@ void Occupy::decision(const std::string& name, const std::string& smearing_metho * @param ekb the array save the band energy. * @param ef output: the highest occupied Kohn-Sham level. * @param wg output: weight for each k, each band. + * @param nspin number of spin components: 1 (spin-degenerate), 2 (collinear) or 4 (non-collinear). * @param is the spin index now. * @param isk distinguish k point belong to which spin. */ @@ -138,17 +138,21 @@ void Occupy::iweights( const ModuleBase::matrix& ekb, double& ef, ModuleBase::matrix& wg, + const int nspin, const int& is, //<- is should be -1, 0, or 1. -1 means set all spins, and 0 means spin up, 1 means spin down. const std::vector& isk) { - assert(is < 2); + assert(nspin == 1 || nspin == 2 || nspin == 4); + assert(is >= -1 && is < 2); double degspin = 2.0; - if (PARAM.inp.nspin == 4) { + if (nspin == 4) + { degspin = 1.0; -} - if (is != -1) { + } + if (is != -1) + { degspin = 1.0; -} + } double ib_mind = nelec / degspin; int ib_min = std::ceil(ib_mind); @@ -163,7 +167,7 @@ void Occupy::iweights( for (int ik = 0; ik < nks; ++ik) { // when NSPIN=2, only calculate spin up or spin down with TWO_FERMI mode(nupdown != 0) - if (PARAM.inp.nspin == 2 && isk[ik] != is && is != -1) + if (nspin == 2 && isk[ik] != is && is != -1) { continue; } @@ -182,9 +186,9 @@ void Occupy::iweights( } } } - #ifdef __MPI +#ifdef __MPI Parallel_Reduce::reduce_max(ef); - #endif +#endif return; } @@ -203,6 +207,7 @@ void Occupy::iweights( * @param wg output: weight of each band at each k point * @param is spin * @param isk array to point out each k belong to which spin + * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). */ void Occupy::gweights(const int nks, const std::vector& wk, @@ -215,24 +220,27 @@ void Occupy::gweights(const int nks, double& demet, ModuleBase::matrix& wg, const int& is, - const std::vector& isk) + const std::vector& isk, + const int npool) { + assert(npool >= 1); // ModuleBase::TITLE("Occupy","gweights"); //=============================== // Calculate the Fermi energy ef //=============================== // call efermig - Occupy::efermig(ekb, nband, nks, nelec, wk, smearing_sigma, ngauss, ef, is, isk); + Occupy::efermig(ekb, nband, nks, nelec, wk, smearing_sigma, ngauss, ef, is, isk, npool); demet = 0.0; for (int ik = 0; ik < nks; ik++) { // mohan add 2011-04-03 - if (is != -1 && is != isk[ik]) { + if (is != -1 && is != isk[ik]) + { continue; -} + } - for (int ib = 0; ib < PARAM.globalv.nbands_l; ib++) + for (int ib = 0; ib < nband; ib++) { //================================ // Calculate the gaussian weights @@ -266,6 +274,7 @@ void Occupy::gweights(const int nks, * @param ef output: fermi level * @param is spin * @param isk array to point out each k belong to which spin + * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). */ void Occupy::efermig(const ModuleBase::matrix& ekb, const int nband, @@ -276,7 +285,8 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, const int ngauss, double& ef, const int& is, - const std::vector& isk) + const std::vector& isk, + const int npool) { // ModuleBase::TITLE("Occupy","efermig"); //================================================================== @@ -309,10 +319,10 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, eup += 2 * smearing_sigma; elw -= 2 * smearing_sigma; // find min and max across pools - #ifdef __MPI +#ifdef __MPI Parallel_Reduce::reduce_max(eup); Parallel_Reduce::reduce_min(elw); - #endif +#endif //================= // Bisection method //================= @@ -320,8 +330,8 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, int changetime = 0; while (true) { - const double sumkup = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, eup, is, isk); - const double sumklw = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, elw, is, isk); + const double sumkup = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, eup, is, isk, npool); + const double sumklw = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, elw, is, isk, npool); if (changetime > 1000) { @@ -360,7 +370,7 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, // change ef value //====================== ef = (eup + elw) / 2.0; - const double sumkmid = sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, ef, is, isk); + const double sumkmid = sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, ef, is, isk, npool); if (std::abs(sumkmid - nelec) < eps) { @@ -390,6 +400,7 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, * @param e a givern energy * @param is spin * @param isk array to point out each k belong to which spin + * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). * @return (double) the number of states */ double Occupy::sumkg(const ModuleBase::matrix& ekb, @@ -400,15 +411,17 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, const int ngauss, const double& e, const int& is, - const std::vector& isk) + const std::vector& isk, + const int npool) { // ModuleBase::TITLE("Occupy","sumkg"); double sum2 = 0.0; for (int ik = 0; ik < nks; ik++) { - if (is != -1 && is != isk[ik]) { + if (is != -1 && is != isk[ik]) + { continue; -} + } double sum1 = 0.0; for (int ib = 0; ib < nband; ib++) @@ -424,7 +437,6 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, // GlobalV::ofs_running << "\n sum2 before reduce = " << sum2 << std::endl; #ifdef __MPI - const int npool = GlobalV::KPAR * PARAM.inp.bndpar; Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, sum2); #endif @@ -487,7 +499,7 @@ double Occupy::wgauss(const double& x, const int n) //==================== wga = 0.5 * (1 - erf(-x)); // wga = gauss_freq(x * ModuleBase::SQRT2); - // std::cout<<"\n x="< wk(1, 2.0); ModuleBase::matrix ekb(1, 1); std::vector isk(1); ekb(0, 0) = 0.1; - occupy.iweights(1, wk, 1, 0, 2.0, ekb, ef, wg, 0, isk); + occupy.iweights(1, wk, 1, 0, 2.0, ekb, ef, wg, 1, 0, isk); EXPECT_DOUBLE_EQ(wg(0, 0), 2.0); EXPECT_DOUBLE_EQ(ef, 0.1); } TEST_F(OccupyTest, IweightsSPIN) { - PARAM.input.nspin = 2; double ef_up = 0.0; double ef_dw = 0.0; ModuleBase::matrix wg(2, 1); @@ -207,8 +202,8 @@ TEST_F(OccupyTest, IweightsSPIN) isk[1] = 1; ekb(0, 0) = 0.1; ekb(1, 0) = 0.2; - occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_up, wg, 0, isk); - occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_dw, wg, 1, isk); + occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_up, wg, 2, 0, isk); + occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_dw, wg, 2, 1, isk); EXPECT_DOUBLE_EQ(wg(0, 0), 1.0); EXPECT_DOUBLE_EQ(wg(1, 0), 1.0); EXPECT_DOUBLE_EQ(ef_up, 0.1); @@ -217,7 +212,6 @@ TEST_F(OccupyTest, IweightsSPIN) TEST_F(OccupyTest, IweightsWarning) { - PARAM.input.nspin = 1; double ef = 0.0; ModuleBase::matrix wg(1, 1); std::vector wk(1, 2.0); @@ -226,7 +220,7 @@ TEST_F(OccupyTest, IweightsWarning) ekb(0, 0) = 0.1; testing::internal::CaptureStdout(); - EXPECT_EXIT(occupy.iweights(1, wk, 1, 0, 1.0, ekb, ef, wg, -1, isk);, ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(occupy.iweights(1, wk, 1, 0, 1.0, ekb, ef, wg, 1, -1, isk);, ::testing::ExitedWithCode(1), ""); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("It is not a semiconductor or insulator. Please do not set 'smearing_method=fixed', and try other options.")); } @@ -260,7 +254,7 @@ TEST_F(OccupyTest, Sumkg) double e = 0.0; int is = 0; std::vector isk = {0, 0}; - EXPECT_DOUBLE_EQ(occupy.sumkg(ekb, 1, 1, wk, smearing_sigma, ngauss, e, is, isk), 1.0); + EXPECT_DOUBLE_EQ(occupy.sumkg(ekb, 1, 1, wk, smearing_sigma, ngauss, e, is, isk, 1), 1.0); } TEST_F(OccupyTest, Efermig) @@ -274,7 +268,7 @@ TEST_F(OccupyTest, Efermig) int is = 0; std::vector isk = {0, 0}; double ef = 0.0; - occupy.efermig(ekb, 1, 1, 1.0, wk, smearing_sigma, ngauss, ef, is, isk); + occupy.efermig(ekb, 1, 1, 1.0, wk, smearing_sigma, ngauss, ef, is, isk, 1); EXPECT_NEAR(ef, -0.5, 1e-13); } @@ -290,10 +284,11 @@ TEST_F(OccupyTest, Gweights) std::vector isk = {0, 0}; double ef = 0.0; ModuleBase::matrix wg(1, 1); - wg(0, 0) = 1.0; double demet = 0.0; - occupy.gweights(1, wk, 1, 1.0, smearing_sigma, ngauss, ekb, ef, demet, wg, is, isk); - EXPECT_NEAR(ef, -0.5, 1e-13); - EXPECT_NEAR(demet, 0.0, 1e-13); - EXPECT_NEAR(wg(0, 0), 1.0, 1e-13); + // Half-filled single band: the Fermi energy stays at the band energy, the + // occupation is 1/2 and demet equals sigma * w1gauss(0, 0). + occupy.gweights(1, wk, 1, 0.5, smearing_sigma, ngauss, ekb, ef, demet, wg, is, isk, 1); + EXPECT_NEAR(ef, -1.0, 1e-13); + EXPECT_NEAR(wg(0, 0), 0.5, 1e-13); + EXPECT_NEAR(demet, smearing_sigma * (-0.28209479177387814), 1e-13); } From 53ec47a01295276574cd2dc53d4cf702a998951c Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 10:53:16 +0800 Subject: [PATCH 02/13] Remove #define private public hack from occupy test, rename to test_occupy.cpp Promote efermig, sumkg, wgauss, w1gauss from private to public in occupy.h so the unit test can call them without the access hack. Rename elecstate_occupy_test.cpp to test_occupy.cpp per the test_.cpp naming convention and update CMakeLists.txt. --- source/source_estate/occupy.h | 57 +++++++++---------- source/source_estate/test/CMakeLists.txt | 2 +- ...cstate_occupy_test.cpp => test_occupy.cpp} | 4 +- 3 files changed, 29 insertions(+), 34 deletions(-) rename source/source_estate/test/{elecstate_occupy_test.cpp => test_occupy.cpp} (99%) diff --git a/source/source_estate/occupy.h b/source/source_estate/occupy.h index 362babc67e..e770ce0372 100644 --- a/source/source_estate/occupy.h +++ b/source/source_estate/occupy.h @@ -67,37 +67,34 @@ class Occupy static double wsweight(const ModuleBase::Vector3 &r, ModuleBase::Vector3 *rws,const int nrws); -private: - static void efermig(const ModuleBase::matrix& ekb, - const int nbnd, - const int nks, - const double& nelec, - const std::vector& wk, - const double& smearing_sigma, - const int ngauss, - double& ef, - const int& is, - const std::vector& isk, - const int npool); - - static double sumkg(const ModuleBase::matrix& ekb, - const int nband, - const int nks, - const std::vector& wk, - const double& smearing_sigma, - const int ngauss, - const double& e, - const int& is, - const std::vector& isk, - const int npool); - - static double wgauss(const double& x, const int n); + static void efermig(const ModuleBase::matrix& ekb, + const int nbnd, + const int nks, + const double& nelec, + const std::vector& wk, + const double& smearing_sigma, + const int ngauss, + double& ef, + const int& is, + const std::vector& isk, + const int npool); + + static double sumkg(const ModuleBase::matrix& ekb, + const int nband, + const int nks, + const std::vector& wk, + const double& smearing_sigma, + const int ngauss, + const double& e, + const int& is, + const std::vector& isk, + const int npool); + + static double wgauss(const double& x, const int n); + + static double w1gauss(const double& x, const int n); - static double w1gauss(const double& x, const int n); - - //============================ - // Needed in tweights - //============================ +private: static void efermit(double** ekb, const int nband, const int nks, diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index b642a5911a..4f627cc352 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -29,7 +29,7 @@ endif() AddTest( TARGET MODULE_ESTATE_elecstate_occupy LIBS base device - SOURCES elecstate_occupy_test.cpp ../occupy.cpp + SOURCES test_occupy.cpp ../occupy.cpp ) AddTest( diff --git a/source/source_estate/test/elecstate_occupy_test.cpp b/source/source_estate/test/test_occupy.cpp similarity index 99% rename from source/source_estate/test/elecstate_occupy_test.cpp rename to source/source_estate/test/test_occupy.cpp index c7ca21a46d..06705f814a 100644 --- a/source/source_estate/test/elecstate_occupy_test.cpp +++ b/source/source_estate/test/test_occupy.cpp @@ -1,6 +1,7 @@ #include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "source_estate/occupy.h" /*************************************************************** * unit test of class Occupy @@ -11,9 +12,6 @@ * - Occupy::Occupy() * - Occupy::decision() */ -#define private public -#include "source_estate/occupy.h" -#undef private class OccupyTest : public ::testing::Test { protected: From 792c1c5cbdde59ef78a37107a38581de0bed3e97 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 11:13:16 +0800 Subject: [PATCH 03/13] small updates --- source/source_estate/occupy.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/source_estate/occupy.cpp b/source/source_estate/occupy.cpp index 8bb1a428d5..b189222116 100644 --- a/source/source_estate/occupy.cpp +++ b/source/source_estate/occupy.cpp @@ -434,14 +434,10 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, sum2 += wk[ik] * sum1; } - // GlobalV::ofs_running << "\n sum2 before reduce = " << sum2 << std::endl; - #ifdef __MPI Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, sum2); #endif - // GlobalV::ofs_running << "\n sum2 after reduce = " << sum2 << std::endl; - return sum2; } From d185d2e68e339dbc0f11bcee48908f8d3514962f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 14:02:46 +0800 Subject: [PATCH 04/13] Replace Parallel_Reduce with ParaKmeshWorld in occupy, fix ParaWorld ODR Migrate occupy.cpp and elecstate_tools.cpp from the legacy Parallel_Reduce::reduce_double_allpool / reduce_max / reduce_min to the new module_parallel ParaKmeshWorld API: - Add reduce_across_pools / reduce_max_across_pools / reduce_min_across_pools to ParaKmeshWorld (Allreduce on the inter-pool communicator KP_WORLD, no-op when kpar==1). - Add a reduce-only ParaKmeshWorld() default constructor and a no-arg make_kmesh_world() bridge overload for call sites like calEBand that have no k-point information to pass. - occupy.h/cpp: replace npool parameter with const ParaKmeshWorld&, remove #include parallel_reduce.h, remove GlobalV::NPROC_IN_POOL. - elecstate_tools.cpp: calEBand and calculate_weights now construct kmesh via the bridge; delete the old npool/bndpar local variable. Fix a latent ODR violation in ParaWorld that caused a free() crash in test_occupy: the MPI_Comm comm_ member only existed under #ifdef __MPI, so the class layout differed between translation units compiled with and without __MPI. Replace with a void* opaque handle (memcpy round-trip, layout-identical in both builds). --- .../module_parallel/para_bridge.cpp | 37 +++++++++++++++++ .../source_base/module_parallel/para_bridge.h | 27 ++++++++++++ .../module_parallel/para_kmesh_world.cpp | 41 +++++++++++++++++++ .../module_parallel/para_kmesh_world.h | 41 +++++++++++++++++++ .../module_parallel/para_world.cpp | 11 ++--- .../source_base/module_parallel/para_world.h | 34 +++++++++++---- source/source_estate/elecstate_tools.cpp | 40 +++++++++--------- source/source_estate/occupy.cpp | 41 ++++++++----------- source/source_estate/occupy.h | 14 +++++-- source/source_estate/test/test_occupy.cpp | 21 ++++++---- 10 files changed, 242 insertions(+), 65 deletions(-) diff --git a/source/source_base/module_parallel/para_bridge.cpp b/source/source_base/module_parallel/para_bridge.cpp index c2c009b158..9f0b765e0b 100644 --- a/source/source_base/module_parallel/para_bridge.cpp +++ b/source/source_base/module_parallel/para_bridge.cpp @@ -2,6 +2,7 @@ #include "para_tag.h" #ifdef __MPI +#include "source_base/global_variable.h" #include "source_base/parallel_comm.h" #endif @@ -19,4 +20,40 @@ ParaWorld make_pw_world() #endif } +// Reduce-only overload: no k-point distribution data. +ParaKmeshWorld make_kmesh_world() +{ +#ifdef __MPI + int mpi_initialized = 0; + MPI_Initialized(&mpi_initialized); + if (mpi_initialized && GlobalV::KPAR > 1 && KP_WORLD != MPI_COMM_NULL) + { + // Build from globals but skip distribute_kpoints (nkstot=0). + return ParaKmeshWorld(KP_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, + GlobalV::NPROC, 0, 1); + } +#endif + return ParaKmeshWorld(); +} + +// Temporary bridge: construct a kmesh-domain ParaKmeshWorld from the old +// globals. Delete this file once ParaCollection is wired into driver init. +ParaKmeshWorld make_kmesh_world(int nkstot, int nspin) +{ +#ifdef __MPI + // Fall back to a serial single-pool domain when MPI is not initialized + // (e.g. unit tests linked against the MPI-compiled base library) or + // when there is only one k-point pool, so that no MPI call is made on + // an unset communicator. + int mpi_initialized = 0; + MPI_Initialized(&mpi_initialized); + if (mpi_initialized && GlobalV::KPAR > 1 && KP_WORLD != MPI_COMM_NULL) + { + return ParaKmeshWorld(KP_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, + GlobalV::NPROC, nkstot, nspin); + } +#endif + return ParaKmeshWorld(nkstot, nspin); +} + } // namespace Parallel diff --git a/source/source_base/module_parallel/para_bridge.h b/source/source_base/module_parallel/para_bridge.h index c0df2a6194..87d3fe128d 100644 --- a/source/source_base/module_parallel/para_bridge.h +++ b/source/source_base/module_parallel/para_bridge.h @@ -1,6 +1,7 @@ #ifndef PARA_BRIDGE_H #define PARA_BRIDGE_H +#include "para_kmesh_world.h" #include "para_world.h" namespace Parallel @@ -16,6 +17,32 @@ namespace Parallel */ ParaWorld make_pw_world(); +/** + * @brief Temporary bridge: construct a kmesh-domain ParaKmeshWorld from + * the old globals KP_WORLD / GlobalV::KPAR (MPI) or as a serial domain + * (non-MPI). + * + * Falls back to a serial single-pool domain when MPI is not initialized + * (e.g. unit tests linked against the MPI-compiled base library) or when + * there is only one k-point pool, so that no MPI call is made on an + * unset communicator. + * + * @param[in] nkstot total number of k-points (without spin) + * @param[in] nspin number of spin components + */ +ParaKmeshWorld make_kmesh_world(int nkstot, int nspin); + +/** + * @brief Reduce-only overload: construct a kmesh domain for call sites + * that only need cross-pool reduction (reduce_across_pools etc.) and + * have no k-point information to pass. + * + * The k-point distribution data (nks_pool_, whichpool_, ...) is left + * empty; calling pool_collection / gather_kvec on the returned object + * is invalid. Use the (nkstot, nspin) overload when those are needed. + */ +ParaKmeshWorld make_kmesh_world(); + } // namespace Parallel #endif // PARA_BRIDGE_H diff --git a/source/source_base/module_parallel/para_kmesh_world.cpp b/source/source_base/module_parallel/para_kmesh_world.cpp index 319654df6a..a6a369d08f 100644 --- a/source/source_base/module_parallel/para_kmesh_world.cpp +++ b/source/source_base/module_parallel/para_kmesh_world.cpp @@ -15,6 +15,14 @@ ParaKmeshWorld::ParaKmeshWorld(int nkstot, int nspin) startk_global_ = 0; } +ParaKmeshWorld::ParaKmeshWorld() + : ParaWorld("kmesh"), kpar_(1), my_pool_(0), rank_in_pool_(0), + nproc_(1), nspin_(1), nkstot_(0), nks_local_(0), startk_global_(0) +{ + // Intentionally empty: no k-point distribution data. + // Only kpar_ / comm() are valid for reduce_across_pools. +} + #ifdef __MPI ParaKmeshWorld::ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nproc, int nkstot, int nspin) : ParaWorld("kmesh", comm), kpar_(kpar), my_pool_(my_pool), @@ -93,6 +101,39 @@ int ParaKmeshWorld::max_nks_pool() const return *std::max_element(nks_pool_.begin(), nks_pool_.end()); } +void ParaKmeshWorld::reduce_across_pools(double& value) const +{ + if (kpar_ == 1) + { + return; + } +#ifdef __MPI + MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_DOUBLE, MPI_SUM, comm()); +#endif +} + +void ParaKmeshWorld::reduce_max_across_pools(double& value) const +{ + if (kpar_ == 1) + { + return; + } +#ifdef __MPI + MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_DOUBLE, MPI_MAX, comm()); +#endif +} + +void ParaKmeshWorld::reduce_min_across_pools(double& value) const +{ + if (kpar_ == 1) + { + return; + } +#ifdef __MPI + MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_DOUBLE, MPI_MIN, comm()); +#endif +} + void ParaKmeshWorld::pool_collection(double& value, const double* wk, int ik) const { #ifdef __MPI diff --git a/source/source_base/module_parallel/para_kmesh_world.h b/source/source_base/module_parallel/para_kmesh_world.h index f78607112d..272aef8c2d 100644 --- a/source/source_base/module_parallel/para_kmesh_world.h +++ b/source/source_base/module_parallel/para_kmesh_world.h @@ -30,6 +30,17 @@ class ParaKmeshWorld : public ParaWorld */ ParaKmeshWorld(int nkstot, int nspin); + /** + * @brief Construct a reduce-only k-mesh domain with no k-point + * distribution data. + * + * kpar_/comm are set from the bridge globals so that + * reduce_across_pools / reduce_max/min_across_pools work correctly. + * The distribution arrays (nks_pool_, whichpool_, ...) are left + * empty; calling pool_collection / gather_kvec is invalid. + */ + ParaKmeshWorld(); + #ifdef __MPI /** * @brief Construct a k-mesh domain on an existing communicator. @@ -83,6 +94,36 @@ class ParaKmeshWorld : public ParaWorld /// Maximum number of k-points across all pools. int max_nks_pool() const; + // ===== Cross-pool reductions ===== + + /** + * @brief Sum a scalar across all k-point pools. + * + * Replaces Parallel_Reduce::reduce_double_allpool. Uses the inter-pool + * communicator (comm()) so that same-rank processes across pools + * participate. Since all processes in a pool share the same value, + * no normalization by pool size is needed. No-op when kpar()==1. + * + * @param[in,out] value local partial sum, overwritten with global total + */ + void reduce_across_pools(double& value) const; + + /** + * @brief Global max across all k-point pools. + * + * @param[in,out] value local value, overwritten with global max + */ + void reduce_max_across_pools(double& value) const; + + /** + * @brief Global min across all k-point pools. + * + * @param[in,out] value local value, overwritten with global min + */ + void reduce_min_across_pools(double& value) const; + + // ===== Cross-domain operations ===== + /** * @brief Collect a scalar value from the pool that owns k-point ik. * diff --git a/source/source_base/module_parallel/para_world.cpp b/source/source_base/module_parallel/para_world.cpp index 0a4ca51748..63138d0e32 100644 --- a/source/source_base/module_parallel/para_world.cpp +++ b/source/source_base/module_parallel/para_world.cpp @@ -3,22 +3,23 @@ namespace Parallel { -ParaWorld::ParaWorld(const std::string& tag) : tag_(tag), rank_(0), size_(1) +ParaWorld::ParaWorld(const std::string& tag) : tag_(tag), rank_(0), size_(1), comm_(nullptr) { #ifdef __MPI if (!tag.empty()) { - comm_ = MPI_COMM_SELF; + comm_ = handle_from_comm(MPI_COMM_SELF); } else { - comm_ = MPI_COMM_NULL; + comm_ = handle_from_comm(MPI_COMM_NULL); } #endif } #ifdef __MPI -ParaWorld::ParaWorld(const std::string& tag, const MPI_Comm& comm) : tag_(tag), comm_(comm) +ParaWorld::ParaWorld(const std::string& tag, const MPI_Comm& comm) + : tag_(tag), comm_(handle_from_comm(comm)) { if (comm == MPI_COMM_NULL) { @@ -34,7 +35,7 @@ ParaWorld::ParaWorld(const std::string& tag, const MPI_Comm& comm) : tag_(tag), bool ParaWorld::valid() const { #ifdef __MPI - return comm_ != MPI_COMM_NULL; + return comm() != MPI_COMM_NULL; #else return !tag_.empty(); #endif diff --git a/source/source_base/module_parallel/para_world.h b/source/source_base/module_parallel/para_world.h index a8291fad8b..975af2a7f7 100644 --- a/source/source_base/module_parallel/para_world.h +++ b/source/source_base/module_parallel/para_world.h @@ -1,6 +1,7 @@ #ifndef PARA_WORLD_H #define PARA_WORLD_H +#include #include #include @@ -20,9 +21,13 @@ namespace Parallel * GlobalV::RANK_IN_POOL / POOL_WORLD by an object that functions * receive explicitly. * - * In serial builds (no __MPI) the communicator member does not - * exist; rank() always returns 0 and size() always returns 1, so - * call sites compile unchanged in both serial and MPI builds. + * The communicator is stored as an opaque handle so that the class + * layout is identical in serial and MPI builds. Binaries that mix + * translation units compiled with different __MPI settings (e.g. unit + * tests linked against the MPI-compiled base library) would otherwise + * be an ODR violation with undefined behavior. In serial builds rank() + * always returns 0 and size() always returns 1, so call sites compile + * unchanged in both serial and MPI builds. */ class ParaWorld { @@ -59,7 +64,11 @@ class ParaWorld /// Underlying MPI communicator (MPI builds only). MPI_Comm comm() const { - return comm_; + MPI_Comm comm = MPI_COMM_NULL; + static_assert(sizeof(MPI_Comm) <= sizeof(comm_), + "MPI_Comm does not fit into the opaque handle"); + std::memcpy(&comm, &comm_, sizeof(MPI_Comm)); + return comm; } #endif @@ -127,12 +136,23 @@ class ParaWorld #endif private: +#ifdef __MPI + /// Wrap an MPI communicator into the opaque handle storage. + static void* handle_from_comm(const MPI_Comm& comm) + { + void* handle = nullptr; + std::memcpy(&handle, &comm, sizeof(MPI_Comm)); + return handle; + } +#endif + std::string tag_; ///< domain tag int rank_; ///< rank inside domain int size_; ///< number of processes in domain -#ifdef __MPI - MPI_Comm comm_; ///< wrapped communicator (never owned/freed here) -#endif + // Opaque communicator handle, present in both serial and MPI builds + // so that the class layout never depends on the __MPI macro (see the + // class comment). Never owned/freed here. + void* comm_; }; } // namespace Parallel diff --git a/source/source_estate/elecstate_tools.cpp b/source/source_estate/elecstate_tools.cpp index fc3efd6b23..c0ec682a43 100644 --- a/source/source_estate/elecstate_tools.cpp +++ b/source/source_estate/elecstate_tools.cpp @@ -1,8 +1,8 @@ #include "elecstate_tools.h" #include "occupy.h" +#include "source_base/module_parallel/para_bridge.h" #include "source_base/parallel_comm.h" -#include "source_base/parallel_reduce.h" #include #include @@ -74,11 +74,10 @@ void calEBand(const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, fener } f_en.eband = eband; -#ifdef __MPI - // Combine contributions distributed by both KPAR and BNDPAR. - const int npool = GlobalV::KPAR * PARAM.inp.bndpar; - Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, f_en.eband); -#endif + // Combine contributions distributed across k-point pools. + // Reduce-only kmesh: no k-point distribution data needed. + Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(); + kmesh.reduce_across_pools(f_en.eband); return; } @@ -98,19 +97,23 @@ void calculate_weights(const ModuleBase::matrix& ekb, const int nbands = ekb.nc; const int nks = ekb.nr; + const int nspin = PARAM.inp.nspin; if (!(Occupy::use_gaussian_broadening || Occupy::fixed_occupations)) { - const int nspin = PARAM.inp.nspin; // Taoni fix smearing_method=fixed for BPCG on 2026-08-21 // Integer occupations use global band indices even when ekb is a local // contiguous BPCG shard. const int band_offset = get_band_offset(nbands, global_nbands); + // The kmesh domain is only built in the branches that dereference + // klist, so that callers passing no k-list (fixed occupations) are + // not affected. + Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(klist->get_nkstot(), nspin); if (PARAM.globalv.two_fermi) { Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[0], ekb, eferm.ef_up, wg, - nspin, 0, klist->isk); + nspin, 0, klist->isk, kmesh); Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[1], ekb, eferm.ef_dw, wg, - nspin, 1, klist->isk); + nspin, 1, klist->isk, kmesh); // ef = ( ef_up + ef_dw ) / 2.0_dp need??? mohan add 2012-04-16 // Keep independent Fermi levels for the two spin channels. } @@ -118,14 +121,15 @@ void calculate_weights(const ModuleBase::matrix& ekb, { // A spin selector of -1 requests the combined-spin occupation path. Occupy::iweights(nks, klist->wk, nbands, band_offset, PARAM.inp.nelec, ekb, eferm.ef, wg, - nspin, -1, klist->isk); + nspin, -1, klist->isk, kmesh); } } else if (Occupy::use_gaussian_broadening) { - // The pool count is needed both by the Fermi-energy search inside - // gweights and by the all-pool demet reduction below. - const int npool = GlobalV::KPAR * PARAM.inp.bndpar; + // The kmesh domain is only built in the branches that dereference + // klist, so that callers passing no k-list (fixed occupations) are + // not affected. + Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(klist->get_nkstot(), nspin); if (PARAM.globalv.two_fermi) { double demet_up = 0.0; @@ -142,7 +146,7 @@ void calculate_weights(const ModuleBase::matrix& ekb, wg, 0, klist->isk, - npool); + kmesh); Occupy::gweights(nks, klist->wk, nbands, @@ -155,7 +159,7 @@ void calculate_weights(const ModuleBase::matrix& ekb, wg, 1, klist->isk, - npool); + kmesh); f_en.demet = demet_up + demet_dw; } else @@ -173,12 +177,10 @@ void calculate_weights(const ModuleBase::matrix& ekb, wg, -1, klist->isk, - npool); + kmesh); } -#ifdef __MPI // demet is accumulated independently on every k-point and band partition. - Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, f_en.demet); -#endif + kmesh.reduce_across_pools(f_en.demet); } else if (Occupy::fixed_occupations) { diff --git a/source/source_estate/occupy.cpp b/source/source_estate/occupy.cpp index b189222116..e0f94d253c 100644 --- a/source/source_estate/occupy.cpp +++ b/source/source_estate/occupy.cpp @@ -2,7 +2,7 @@ #include "source_base/constants.h" #include "source_base/mymath.h" -#include "source_base/parallel_reduce.h" +#include "source_base/module_parallel/para_kmesh_world.h" Occupy::Occupy() { @@ -128,6 +128,7 @@ void Occupy::decision(const std::string& name, const std::string& smearing_metho * @param nspin number of spin components: 1 (spin-degenerate), 2 (collinear) or 4 (non-collinear). * @param is the spin index now. * @param isk distinguish k point belong to which spin. + * @param kmesh k-point parallel domain for cross-pool reduction. */ void Occupy::iweights( const int nks, @@ -140,7 +141,8 @@ void Occupy::iweights( ModuleBase::matrix& wg, const int nspin, const int& is, //<- is should be -1, 0, or 1. -1 means set all spins, and 0 means spin up, 1 means spin down. - const std::vector& isk) + const std::vector& isk, + const Parallel::ParaKmeshWorld& kmesh) { assert(nspin == 1 || nspin == 2 || nspin == 4); assert(is >= -1 && is < 2); @@ -186,9 +188,7 @@ void Occupy::iweights( } } } -#ifdef __MPI - Parallel_Reduce::reduce_max(ef); -#endif + kmesh.reduce_max_across_pools(ef); return; } @@ -207,7 +207,7 @@ void Occupy::iweights( * @param wg output: weight of each band at each k point * @param is spin * @param isk array to point out each k belong to which spin - * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). + * @param kmesh k-point parallel domain for cross-pool reduction. */ void Occupy::gweights(const int nks, const std::vector& wk, @@ -221,15 +221,14 @@ void Occupy::gweights(const int nks, ModuleBase::matrix& wg, const int& is, const std::vector& isk, - const int npool) + const Parallel::ParaKmeshWorld& kmesh) { - assert(npool >= 1); // ModuleBase::TITLE("Occupy","gweights"); //=============================== // Calculate the Fermi energy ef //=============================== // call efermig - Occupy::efermig(ekb, nband, nks, nelec, wk, smearing_sigma, ngauss, ef, is, isk, npool); + Occupy::efermig(ekb, nband, nks, nelec, wk, smearing_sigma, ngauss, ef, is, isk, kmesh); demet = 0.0; for (int ik = 0; ik < nks; ik++) @@ -274,7 +273,7 @@ void Occupy::gweights(const int nks, * @param ef output: fermi level * @param is spin * @param isk array to point out each k belong to which spin - * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). + * @param kmesh k-point parallel domain for cross-pool reduction. */ void Occupy::efermig(const ModuleBase::matrix& ekb, const int nband, @@ -286,7 +285,7 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, double& ef, const int& is, const std::vector& isk, - const int npool) + const Parallel::ParaKmeshWorld& kmesh) { // ModuleBase::TITLE("Occupy","efermig"); //================================================================== @@ -319,10 +318,8 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, eup += 2 * smearing_sigma; elw -= 2 * smearing_sigma; // find min and max across pools -#ifdef __MPI - Parallel_Reduce::reduce_max(eup); - Parallel_Reduce::reduce_min(elw); -#endif + kmesh.reduce_max_across_pools(eup); + kmesh.reduce_min_across_pools(elw); //================= // Bisection method //================= @@ -330,8 +327,8 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, int changetime = 0; while (true) { - const double sumkup = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, eup, is, isk, npool); - const double sumklw = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, elw, is, isk, npool); + const double sumkup = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, eup, is, isk, kmesh); + const double sumklw = Occupy::sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, elw, is, isk, kmesh); if (changetime > 1000) { @@ -370,7 +367,7 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, // change ef value //====================== ef = (eup + elw) / 2.0; - const double sumkmid = sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, ef, is, isk, npool); + const double sumkmid = sumkg(ekb, nband, nks, wk, smearing_sigma, ngauss, ef, is, isk, kmesh); if (std::abs(sumkmid - nelec) < eps) { @@ -400,7 +397,7 @@ void Occupy::efermig(const ModuleBase::matrix& ekb, * @param e a givern energy * @param is spin * @param isk array to point out each k belong to which spin - * @param npool number of k-point/band pools used for the MPI all-pool reduction (1 in serial). + * @param kmesh k-point parallel domain for cross-pool reduction. * @return (double) the number of states */ double Occupy::sumkg(const ModuleBase::matrix& ekb, @@ -412,7 +409,7 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, const double& e, const int& is, const std::vector& isk, - const int npool) + const Parallel::ParaKmeshWorld& kmesh) { // ModuleBase::TITLE("Occupy","sumkg"); double sum2 = 0.0; @@ -434,9 +431,7 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, sum2 += wk[ik] * sum1; } -#ifdef __MPI - Parallel_Reduce::reduce_double_allpool(npool, GlobalV::NPROC_IN_POOL, sum2); -#endif + kmesh.reduce_across_pools(sum2); return sum2; } diff --git a/source/source_estate/occupy.h b/source/source_estate/occupy.h index e770ce0372..1bbbb49b0b 100644 --- a/source/source_estate/occupy.h +++ b/source/source_estate/occupy.h @@ -5,6 +5,11 @@ #include "source_base/matrix.h" #include "source_base/vector3.h" +namespace Parallel +{ +class ParaKmeshWorld; +} + class Occupy { @@ -45,7 +50,8 @@ class Occupy ModuleBase::matrix& wg, const int nspin, const int& is, - const std::vector& isk); + const std::vector& isk, + const Parallel::ParaKmeshWorld& kmesh); static void gweights(const int nks, const std::vector& wk, @@ -59,7 +65,7 @@ class Occupy ModuleBase::matrix& wg, const int& is, const std::vector& isk, - const int npool); + const Parallel::ParaKmeshWorld& kmesh); static void tweights(const int nks, const int nspin, const int nband, const double& nelec, const int ntetra, @@ -77,7 +83,7 @@ class Occupy double& ef, const int& is, const std::vector& isk, - const int npool); + const Parallel::ParaKmeshWorld& kmesh); static double sumkg(const ModuleBase::matrix& ekb, const int nband, @@ -88,7 +94,7 @@ class Occupy const double& e, const int& is, const std::vector& isk, - const int npool); + const Parallel::ParaKmeshWorld& kmesh); static double wgauss(const double& x, const int n); diff --git a/source/source_estate/test/test_occupy.cpp b/source/source_estate/test/test_occupy.cpp index 06705f814a..ee14467858 100644 --- a/source/source_estate/test/test_occupy.cpp +++ b/source/source_estate/test/test_occupy.cpp @@ -1,6 +1,7 @@ #include #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "source_base/module_parallel/para_kmesh_world.h" #include "source_estate/occupy.h" /*************************************************************** @@ -183,7 +184,8 @@ TEST_F(OccupyTest, IweightsNOSPIN) ModuleBase::matrix ekb(1, 1); std::vector isk(1); ekb(0, 0) = 0.1; - occupy.iweights(1, wk, 1, 0, 2.0, ekb, ef, wg, 1, 0, isk); + Parallel::ParaKmeshWorld kmesh(1, 1); + occupy.iweights(1, wk, 1, 0, 2.0, ekb, ef, wg, 1, 0, isk, kmesh); EXPECT_DOUBLE_EQ(wg(0, 0), 2.0); EXPECT_DOUBLE_EQ(ef, 0.1); } @@ -200,8 +202,9 @@ TEST_F(OccupyTest, IweightsSPIN) isk[1] = 1; ekb(0, 0) = 0.1; ekb(1, 0) = 0.2; - occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_up, wg, 2, 0, isk); - occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_dw, wg, 2, 1, isk); + Parallel::ParaKmeshWorld kmesh(2, 2); + occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_up, wg, 2, 0, isk, kmesh); + occupy.iweights(2, wk, 1, 0, 1.0, ekb, ef_dw, wg, 2, 1, isk, kmesh); EXPECT_DOUBLE_EQ(wg(0, 0), 1.0); EXPECT_DOUBLE_EQ(wg(1, 0), 1.0); EXPECT_DOUBLE_EQ(ef_up, 0.1); @@ -217,8 +220,9 @@ TEST_F(OccupyTest, IweightsWarning) std::vector isk(1); ekb(0, 0) = 0.1; + Parallel::ParaKmeshWorld kmesh(1, 1); testing::internal::CaptureStdout(); - EXPECT_EXIT(occupy.iweights(1, wk, 1, 0, 1.0, ekb, ef, wg, 1, -1, isk);, ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(occupy.iweights(1, wk, 1, 0, 1.0, ekb, ef, wg, 1, -1, isk, kmesh);, ::testing::ExitedWithCode(1), ""); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("It is not a semiconductor or insulator. Please do not set 'smearing_method=fixed', and try other options.")); } @@ -252,7 +256,8 @@ TEST_F(OccupyTest, Sumkg) double e = 0.0; int is = 0; std::vector isk = {0, 0}; - EXPECT_DOUBLE_EQ(occupy.sumkg(ekb, 1, 1, wk, smearing_sigma, ngauss, e, is, isk, 1), 1.0); + Parallel::ParaKmeshWorld kmesh(1, 1); + EXPECT_DOUBLE_EQ(occupy.sumkg(ekb, 1, 1, wk, smearing_sigma, ngauss, e, is, isk, kmesh), 1.0); } TEST_F(OccupyTest, Efermig) @@ -266,7 +271,8 @@ TEST_F(OccupyTest, Efermig) int is = 0; std::vector isk = {0, 0}; double ef = 0.0; - occupy.efermig(ekb, 1, 1, 1.0, wk, smearing_sigma, ngauss, ef, is, isk, 1); + Parallel::ParaKmeshWorld kmesh(1, 1); + occupy.efermig(ekb, 1, 1, 1.0, wk, smearing_sigma, ngauss, ef, is, isk, kmesh); EXPECT_NEAR(ef, -0.5, 1e-13); } @@ -285,7 +291,8 @@ TEST_F(OccupyTest, Gweights) double demet = 0.0; // Half-filled single band: the Fermi energy stays at the band energy, the // occupation is 1/2 and demet equals sigma * w1gauss(0, 0). - occupy.gweights(1, wk, 1, 0.5, smearing_sigma, ngauss, ekb, ef, demet, wg, is, isk, 1); + Parallel::ParaKmeshWorld kmesh(1, 1); + occupy.gweights(1, wk, 1, 0.5, smearing_sigma, ngauss, ekb, ef, demet, wg, is, isk, kmesh); EXPECT_NEAR(ef, -1.0, 1e-13); EXPECT_NEAR(wg(0, 0), 0.5, 1e-13); EXPECT_NEAR(demet, smearing_sigma * (-0.28209479177387814), 1e-13); From 4218bbe4d8bde4b5f7a9a475856b4347fc776837 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 14:56:57 +0800 Subject: [PATCH 05/13] Remove parameter.h dependency from write_elecstat_pot, pass Input_para to ctrl_output_fp - write_elecstat_pot: add nspin, efield_flag, dip_cor_flag, imp_sol, two_fermi as explicit parameters instead of reading PARAM directly - ctrl_output_fp: add const Input_para& inp parameter to replace PARAM.inp usage - esolver_fp: pass PARAM.inp at the call site --- source/source_esolver/esolver_fp.cpp | 4 +- source/source_estate/write_elecstat_pot.cpp | 25 ++++++----- source/source_estate/write_elecstat_pot.h | 14 +++++- .../source_io/module_ctrl/ctrl_output_fp.cpp | 45 +++++++++++-------- source/source_io/module_ctrl/ctrl_output_fp.h | 5 ++- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/source/source_esolver/esolver_fp.cpp b/source/source_esolver/esolver_fp.cpp index 95c363b7bc..7d415fc59d 100644 --- a/source/source_esolver/esolver_fp.cpp +++ b/source/source_esolver/esolver_fp.cpp @@ -153,8 +153,8 @@ void ESolver_FP::after_scf(UnitCell& ucell, const int istep, const bool conv_eso CE.update_delta_rho(ucell, &(this->chr), &(this->sf)); //! print out charge density, potential, elf, etc. - ModuleIO::ctrl_output_fp(ucell, this->pelec, this->pw_big, this->pw_rhod, - this->chr, this->solvent, this->Pgrid, istep); + ModuleIO::ctrl_output_fp(ucell, this->pelec, this->pw_big, this->pw_rhod, + this->chr, this->solvent, this->Pgrid, istep, PARAM.inp); } diff --git a/source/source_estate/write_elecstat_pot.cpp b/source/source_estate/write_elecstat_pot.cpp index 15a5363ed6..3f630696aa 100644 --- a/source/source_estate/write_elecstat_pot.cpp +++ b/source/source_estate/write_elecstat_pot.cpp @@ -1,12 +1,13 @@ #include "source_base/element_name.h" #include "source_base/timer.h" -#include "source_io/module_parameter/parameter.h" #include "source_estate/module_pot/h_hartree_pw.h" #include "source_estate/module_pot/efield.h" #include "source_io/module_output/cube_io.h" #include "source_io/module_output/output_log.h" #include "write_elecstat_pot.h" +#include + namespace ModuleIO { @@ -22,17 +23,19 @@ void write_elecstat_pot( const UnitCell* ucell, const double* v_eff, const surchem& solvent, - const int precision) + const int precision, + const int nspin, + const bool efield_flag, + const bool dip_cor_flag, + const bool imp_sol, + const bool two_fermi) { ModuleBase::TITLE("ModuleIO", "write_elecstat_pot"); ModuleBase::timer::start("ModuleIO", "write_elecstat_pot"); - std::vector v_elecstat(rho_basis->nrxx, 0.0); + assert(nspin == 1 || nspin == 2 || nspin == 4); - const int nspin = PARAM.inp.nspin; - const int efield = PARAM.inp.efield_flag; - const int dip_corr = PARAM.inp.dip_cor_flag; - const bool imp_sol = PARAM.inp.imp_sol; + std::vector v_elecstat(rho_basis->nrxx, 0.0); //========================================== // Hartree potential @@ -44,7 +47,7 @@ void write_elecstat_pot( //! Dipole correction //========================================== ModuleBase::matrix v_efield; - if (efield>0 && dip_corr>0) + if (efield_flag && dip_cor_flag) { v_efield.create(nspin, rho_basis->nrxx); v_efield = elecstate::Efield::add_efield(*ucell, @@ -62,11 +65,11 @@ void write_elecstat_pot( // the spin index is 0 v_elecstat[ir] = vh(0, ir) + v_eff[ir]; - if (efield>0 && dip_corr>0) + if (efield_flag && dip_cor_flag) { v_elecstat[ir] += v_efield(0, ir); } - if(imp_sol == true) + if(imp_sol) { v_elecstat[ir] += solvent.delta_phi[ir]; } @@ -103,7 +106,7 @@ void write_elecstat_pot( ucell, precision, out_fermi, - PARAM.globalv.two_fermi, + two_fermi, false); ModuleBase::timer::end("ModuleIO", "write_elecstat_pot"); diff --git a/source/source_estate/write_elecstat_pot.h b/source/source_estate/write_elecstat_pot.h index bee575b95c..967a6200e8 100644 --- a/source/source_estate/write_elecstat_pot.h +++ b/source/source_estate/write_elecstat_pot.h @@ -20,7 +20,12 @@ namespace ModuleIO /// @param ucell_ /// @param v_eff_fixed /// @param solvent: for solvation model -/// #param precision: output precision +/// @param precision: output precision +/// @param nspin: number of spin channels (1, 2, or 4) +/// @param efield_flag: whether electric field is applied +/// @param dip_cor_flag: whether dipole correction is applied +/// @param imp_sol: whether implicit solvation model is used +/// @param two_fermi: whether two Fermi levels are used void write_elecstat_pot( #ifdef __MPI const int& bz, @@ -33,7 +38,12 @@ void write_elecstat_pot( const UnitCell* ucell_, const double* v_eff_fixed, const surchem& solvent, - const int precision); + const int precision, + const int nspin, + const bool efield_flag, + const bool dip_cor_flag, + const bool imp_sol, + const bool two_fermi); } // namespace ModuleIO diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 318c571e3c..8114657f63 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -5,6 +5,7 @@ #include "source_hamilt/module_xc/xc_functional.h" // use XC_Functional #include "source_estate/write_elecstat_pot.h" // use write_elecstat_pot #include "source_io/module_elf/write_elf.h" +#include "source_io/module_parameter/input_parameter.h" #ifdef __LIBXC #include "source_io/module_chgpot/write_libxc_r.h" @@ -20,35 +21,36 @@ void ctrl_output_fp(UnitCell& ucell, Charge& chr, surchem& solvent, Parallel_Grid& para_grid, - const int istep) + const int istep, + const Input_para& inp) { ModuleBase::TITLE("ModuleIO", "ctrl_output_fp"); ModuleBase::timer::start("ModuleIO", "ctrl_output_fp"); - const bool out_app_flag = PARAM.inp.out_app_flag; + const bool out_app_flag = inp.out_app_flag; const bool gamma_only = PARAM.globalv.gamma_only_local; - const int nspin = PARAM.inp.nspin; + const int nspin = inp.nspin; const std::string global_out_dir = PARAM.globalv.global_out_dir; // print out the 'g' index when istep_in != -1 int istep_in = -1; - if (PARAM.inp.esolver_type != "tddft" && PARAM.inp.out_freq_ion > 0) // default value of out_freq_ion is 0 + if (inp.esolver_type != "tddft" && inp.out_freq_ion > 0) // default value of out_freq_ion is 0 { - if (istep % PARAM.inp.out_freq_ion == 0) + if (istep % inp.out_freq_ion == 0) { istep_in = istep; } } - else if (PARAM.inp.esolver_type == "tddft" && PARAM.inp.out_freq_td > 0) // default value of out_freq_td is 0 + else if (inp.esolver_type == "tddft" && inp.out_freq_td > 0) // default value of out_freq_td is 0 { - if (istep % PARAM.inp.out_freq_td == 0) + if (istep % inp.out_freq_td == 0) { istep_in = istep; } } std::string geom_block; - bool should_output = (PARAM.inp.out_freq_ion == 0); + bool should_output = (inp.out_freq_ion == 0); if (istep_in >= 0) { geom_block = "g" + std::to_string(istep + 1); @@ -56,7 +58,7 @@ void ctrl_output_fp(UnitCell& ucell, } // 4) write charge density - if (PARAM.inp.out_chg[0] > 0 && should_output) + if (inp.out_chg[0] > 0 && should_output) { for (int is = 0; is < nspin; ++is) { @@ -82,7 +84,7 @@ void ctrl_output_fp(UnitCell& ucell, fn, pelec->eferm.get_efval(is), &(ucell), - PARAM.inp.out_chg[1], + inp.out_chg[1], 1, PARAM.globalv.two_fermi, false); @@ -110,7 +112,7 @@ void ctrl_output_fp(UnitCell& ucell, } // 5) write potential - if ((PARAM.inp.out_pot[0] == 1 || PARAM.inp.out_pot[0] == 3) && should_output) + if ((inp.out_pot[0] == 1 || inp.out_pot[0] == 3) && should_output) { for (int is = 0; is < nspin; is++) { @@ -136,13 +138,13 @@ void ctrl_output_fp(UnitCell& ucell, fn, 0.0, // efermi &(ucell), - PARAM.inp.out_pot[1], // precision + inp.out_pot[1], // precision 0, // out_fermi PARAM.globalv.two_fermi, false); } } - else if (PARAM.inp.out_pot[0] == 2 && should_output) + else if (inp.out_pot[0] == 2 && should_output) { std::string fn = PARAM.globalv.global_out_dir + "potes"; fn += geom_block + ".cube"; @@ -159,11 +161,16 @@ void ctrl_output_fp(UnitCell& ucell, &(ucell), pelec->pot->get_fixed_v(), solvent, - PARAM.inp.out_pot[1]); + inp.out_pot[1], + nspin, + inp.efield_flag, + inp.dip_cor_flag, + inp.imp_sol, + PARAM.globalv.two_fermi); } // 6) write ELF - if (PARAM.inp.out_elf[0] > 0 && should_output) + if (inp.out_elf[0] > 0 && should_output) { chr.cal_elf = true; Symmetry_rho srho; @@ -181,16 +188,16 @@ void ctrl_output_fp(UnitCell& ucell, pw_rhod, para_grid, &(ucell), - PARAM.inp.out_elf[1], + inp.out_elf[1], geom_block, PARAM.globalv.two_fermi); } #ifdef __LIBXC // 7) write xc(r) - if (PARAM.inp.out_xc_r[0] >= 0 && should_output) + if (inp.out_xc_r[0] >= 0 && should_output) { - ModuleIO::write_libxc_r(PARAM.inp.out_xc_r[0], + ModuleIO::write_libxc_r(inp.out_xc_r[0], XC_Functional::get_func_id(), pw_rhod->nrxx, // number of real-space grid ucell.omega, // volume of cell @@ -202,7 +209,7 @@ void ctrl_output_fp(UnitCell& ucell, #endif // 8) write dipole moment - if (PARAM.inp.out_dipole == 1 && should_output) + if (inp.out_dipole == 1 && should_output) { for (int is = 0; is < nspin; ++is) { diff --git a/source/source_io/module_ctrl/ctrl_output_fp.h b/source/source_io/module_ctrl/ctrl_output_fp.h index ec9f4e20e7..a1203cd107 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.h +++ b/source/source_io/module_ctrl/ctrl_output_fp.h @@ -3,6 +3,8 @@ #include "source_estate/elecstate_lcao.h" +struct Input_para; + namespace ModuleIO { @@ -13,7 +15,8 @@ void ctrl_output_fp(UnitCell& ucell, Charge& chr, surchem& solvent, Parallel_Grid& para_grid, - const int istep); + const int istep, + const Input_para& inp); } #endif From 4e3f2d003c92c8e13636d46218e3773f77ee37a4 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 16:35:03 +0800 Subject: [PATCH 06/13] Restore bndpar dimension in ParaKmeshWorld reductions, fix scf_bpcg Commit d185d2e68 migrated the occupation reductions from Parallel_Reduce::reduce_double_allpool(KPAR*bndpar, ...) to ParaKmeshWorld, but the new domain only Allreduced over KP_WORLD and returned early when kpar==1, silently dropping the band-parallel (bndpar>1) contributions. This broke tests/11_PW_GPU/scf_bpcg (kpar=1, bndpar=2, gaussian smearing): the occupation sum was accumulated from the local band shard only (sumkup=24 instead of 28), failing the smearing consistency check. - ParaKmeshWorld gains a bndpar member with npool() = kpar*bndpar; reduce_across_pools restores the legacy semantics: divide by the pool size (nproc/npool) and Allreduce over MPI_COMM_WORLD. The max/min reductions span all pools likewise. npool()==1 is a no-op. - para_bridge derives bndpar from the pool layout (bndpar = NPROC / (KPAR * NPROC_IN_POOL)) to avoid depending on the INPUT-parameter module from source_base, and builds the kmesh domain on MPI_COMM_WORLD whenever KPAR*bndpar > 1. - Add MPI unit test test_para_kmesh_world_mpi.cpp (mpirun -np 4) covering the previously missing bndpar=2 band-group reduction path, the kpar=2 path, and the single-pool no-op; wired via add_executable plus a .sh runner so no single-process CTest entry is created. - Update para_collection_mpi_test.cpp for the new constructor. Verified: cmake build + OMP_NUM_THREADS=1 ctest -R "para_kmesh_world|para_collection|elecstate_occupy|elecstate_base" -- 7/7 passed. --- .../module_parallel/para_bridge.cpp | 40 ++++++++-- .../module_parallel/para_kmesh_world.cpp | 28 ++++--- .../module_parallel/para_kmesh_world.h | 41 +++++++--- .../module_parallel/test/CMakeLists.txt | 12 +++ .../test/para_collection_mpi_test.cpp | 6 +- .../test/test_para_kmesh_world_mpi.cpp | 76 +++++++++++++++++++ .../test/test_para_kmesh_world_mpi.sh | 18 +++++ 7 files changed, 188 insertions(+), 33 deletions(-) create mode 100644 source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp create mode 100644 source/source_base/module_parallel/test/test_para_kmesh_world_mpi.sh diff --git a/source/source_base/module_parallel/para_bridge.cpp b/source/source_base/module_parallel/para_bridge.cpp index 9f0b765e0b..d3e1a5f18b 100644 --- a/source/source_base/module_parallel/para_bridge.cpp +++ b/source/source_base/module_parallel/para_bridge.cpp @@ -8,6 +8,24 @@ namespace Parallel { +namespace +{ +#ifdef __MPI +// Number of band-parallel groups, derived from the pool layout to avoid a +// dependency on the INPUT-parameter module from source_base. +// nproc = (KPAR * bndpar) * NPROC_IN_POOL, so bndpar = NPROC / (KPAR * +// NPROC_IN_POOL). Falls back to 1 if the globals are inconsistent. +int bndpar_from_layout() +{ + const int denom = GlobalV::KPAR * GlobalV::NPROC_IN_POOL; + if (denom < 1 || GlobalV::NPROC % denom != 0) + { + return 1; + } + return GlobalV::NPROC / denom; +} +#endif +} // namespace // Temporary bridge: construct a pw-domain ParaWorld from the old globals. // Delete this file once ParaCollection is wired into driver initialization. @@ -26,11 +44,16 @@ ParaKmeshWorld make_kmesh_world() #ifdef __MPI int mpi_initialized = 0; MPI_Initialized(&mpi_initialized); - if (mpi_initialized && GlobalV::KPAR > 1 && KP_WORLD != MPI_COMM_NULL) + const int bndpar = bndpar_from_layout(); + if (mpi_initialized && GlobalV::KPAR * bndpar > 1) { + // Occupation/energy partial sums are distributed across both k-point + // pools and band groups, so the reduce domain must span all of + // MPI_COMM_WORLD (KP_WORLD alone only links same-band ranks across + // k-point pools and would drop the band-parallel contributions). // Build from globals but skip distribute_kpoints (nkstot=0). - return ParaKmeshWorld(KP_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, - GlobalV::NPROC, 0, 1); + return ParaKmeshWorld(MPI_COMM_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, + 0, 1, bndpar); } #endif return ParaKmeshWorld(); @@ -42,15 +65,16 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin) { #ifdef __MPI // Fall back to a serial single-pool domain when MPI is not initialized - // (e.g. unit tests linked against the MPI-compiled base library) or - // when there is only one k-point pool, so that no MPI call is made on + // (e.g. unit tests linked against the MPI-compiled base library) or when + // there is only one distribution pool, so that no MPI call is made on // an unset communicator. int mpi_initialized = 0; MPI_Initialized(&mpi_initialized); - if (mpi_initialized && GlobalV::KPAR > 1 && KP_WORLD != MPI_COMM_NULL) + const int bndpar = bndpar_from_layout(); + if (mpi_initialized && GlobalV::KPAR * bndpar > 1) { - return ParaKmeshWorld(KP_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, - GlobalV::NPROC, nkstot, nspin); + return ParaKmeshWorld(MPI_COMM_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, + nkstot, nspin, bndpar); } #endif return ParaKmeshWorld(nkstot, nspin); diff --git a/source/source_base/module_parallel/para_kmesh_world.cpp b/source/source_base/module_parallel/para_kmesh_world.cpp index a6a369d08f..0845490c07 100644 --- a/source/source_base/module_parallel/para_kmesh_world.cpp +++ b/source/source_base/module_parallel/para_kmesh_world.cpp @@ -7,30 +7,34 @@ namespace Parallel { ParaKmeshWorld::ParaKmeshWorld(int nkstot, int nspin) - : ParaWorld("kmesh"), kpar_(1), my_pool_(0), rank_in_pool_(0), - nproc_(1), nspin_(nspin), nkstot_(nkstot) + : ParaWorld("kmesh"), nspin_(nspin), nkstot_(nkstot) { distribute_kpoints(); nks_local_ = nkstot_; startk_global_ = 0; + nproc_ = size(); } ParaKmeshWorld::ParaKmeshWorld() - : ParaWorld("kmesh"), kpar_(1), my_pool_(0), rank_in_pool_(0), - nproc_(1), nspin_(1), nkstot_(0), nks_local_(0), startk_global_(0) + : ParaWorld("kmesh"), nspin_(1) { // Intentionally empty: no k-point distribution data. // Only kpar_ / comm() are valid for reduce_across_pools. + nproc_ = size(); } #ifdef __MPI -ParaKmeshWorld::ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nproc, int nkstot, int nspin) +ParaKmeshWorld::ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin, int bndpar) : ParaWorld("kmesh", comm), kpar_(kpar), my_pool_(my_pool), - rank_in_pool_(rank()), nproc_(nproc), nspin_(nspin), nkstot_(nkstot) + rank_in_pool_(rank()), nspin_(nspin), nkstot_(nkstot), + bndpar_(bndpar) { distribute_kpoints(); nks_local_ = nks_pool_[my_pool_]; startk_global_ = startk_pool_[my_pool_]; + // comm() spans every process of every distribution pool, so the number + // of processes sharing one partial sum is the pool size. + nproc_ = size(); } #endif @@ -103,18 +107,22 @@ int ParaKmeshWorld::max_nks_pool() const void ParaKmeshWorld::reduce_across_pools(double& value) const { - if (kpar_ == 1) + if (npool() == 1) { return; } #ifdef __MPI - MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_DOUBLE, MPI_SUM, comm()); + // Every process in a pool holds the same partial sum, so divide by the + // pool size (nproc/npool) before the world-wide Allreduce. This matches + // the legacy Parallel_Reduce::reduce_double_allpool semantics. + const double swap = value / (nproc_ / npool()); + MPI_Allreduce(&swap, &value, 1, MPI_DOUBLE, MPI_SUM, comm()); #endif } void ParaKmeshWorld::reduce_max_across_pools(double& value) const { - if (kpar_ == 1) + if (npool() == 1) { return; } @@ -125,7 +133,7 @@ void ParaKmeshWorld::reduce_max_across_pools(double& value) const void ParaKmeshWorld::reduce_min_across_pools(double& value) const { - if (kpar_ == 1) + if (npool() == 1) { return; } diff --git a/source/source_base/module_parallel/para_kmesh_world.h b/source/source_base/module_parallel/para_kmesh_world.h index 272aef8c2d..724aaddae2 100644 --- a/source/source_base/module_parallel/para_kmesh_world.h +++ b/source/source_base/module_parallel/para_kmesh_world.h @@ -45,14 +45,20 @@ class ParaKmeshWorld : public ParaWorld /** * @brief Construct a k-mesh domain on an existing communicator. * - * @param[in] comm k-point pool communicator (e.g. KP_WORLD) - * @param[in] kpar number of pools - * @param[in] my_pool pool index of this process - * @param[in] nproc total number of processes (MPI_COMM_WORLD size) + * @param[in] comm communicator spanning every process that holds a + * partial band/k-point sum (MPI_COMM_WORLD in the + * current bridge; must include both k-point pools and + * band groups) + * @param[in] kpar number of k-point pools + * @param[in] my_pool k-point pool index of this process * @param[in] nkstot total number of k-points (without spin) * @param[in] nspin number of spin components + * @param[in] bndpar number of band-parallel groups (1 when no band + * parallelization); the reduce_* operations treat + * npool = kpar * bndpar, matching the legacy + * Parallel_Reduce::reduce_double_allpool semantics */ - ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nproc, int nkstot, int nspin); + ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin, int bndpar); #endif /// Number of pools. @@ -97,31 +103,41 @@ class ParaKmeshWorld : public ParaWorld // ===== Cross-pool reductions ===== /** - * @brief Sum a scalar across all k-point pools. + * @brief Sum a scalar across all k-point pools and band groups. * - * Replaces Parallel_Reduce::reduce_double_allpool. Uses the inter-pool - * communicator (comm()) so that same-rank processes across pools - * participate. Since all processes in a pool share the same value, - * no normalization by pool size is needed. No-op when kpar()==1. + * Replaces Parallel_Reduce::reduce_double_allpool. The communicator + * spans every process of every pool; since all nproc()/npool() + * processes inside a pool share the same partial sum, each value is + * first divided by the pool size before the MPI_Allreduce so that the + * result equals the sum of the per-pool partial sums. No-op when + * npool() == 1. * * @param[in,out] value local partial sum, overwritten with global total */ void reduce_across_pools(double& value) const; /** - * @brief Global max across all k-point pools. + * @brief Global max across all k-point pools and band groups. + * + * Replaces Parallel_Reduce::reduce_max (all of MPI_COMM_WORLD): + * band-parallel shards see different eigenvalue windows, so the Fermi + * level must be extremized across both k-point pools and band groups. + * No-op when npool() == 1. * * @param[in,out] value local value, overwritten with global max */ void reduce_max_across_pools(double& value) const; /** - * @brief Global min across all k-point pools. + * @brief Global min across all k-point pools and band groups. * * @param[in,out] value local value, overwritten with global min */ void reduce_min_across_pools(double& value) const; + /// Total number of distribution pools: k-point pools * band groups. + int npool() const { return kpar_ * bndpar_; } + // ===== Cross-domain operations ===== /** @@ -169,6 +185,7 @@ class ParaKmeshWorld : public ParaWorld int nkstot_ = 0; int nks_local_ = 0; int startk_global_ = 0; + int bndpar_ = 1; std::vector nks_pool_; ///< k-points per pool std::vector startk_pool_; ///< global start index per pool diff --git a/source/source_base/module_parallel/test/CMakeLists.txt b/source/source_base/module_parallel/test/CMakeLists.txt index 6f3a2169a7..aad4e250e6 100644 --- a/source/source_base/module_parallel/test/CMakeLists.txt +++ b/source/source_base/module_parallel/test/CMakeLists.txt @@ -77,10 +77,18 @@ AddTest( ) target_compile_definitions(MODULE_BASE_para_setup_mpi PRIVATE __MPI) +# Built with add_executable (not AddTest) so that no direct-run CTest entry is +# created; the binary is only exercised through mpirun by the .sh test below, +# matching the multi-process requirement of these cases. +add_executable(MODULE_BASE_para_kmesh_world_mpi test_para_kmesh_world_mpi.cpp ../para_kmesh_world.cpp ../para_world.cpp) +target_link_libraries(MODULE_BASE_para_kmesh_world_mpi PRIVATE MPI::MPI_CXX GTest::gtest GTest::gtest_main abacus::linalg_libs) +target_compile_definitions(MODULE_BASE_para_kmesh_world_mpi PRIVATE __MPI) + file(COPY para_world_mpi_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY para_collection_mpi_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY para_mpi_func_mpi_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file(COPY para_setup_mpi_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY test_para_kmesh_world_mpi.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) find_program(BASH bash) add_test(NAME MODULE_BASE_para_world_mpi_test COMMAND ${BASH} para_world_mpi_test.sh @@ -98,3 +106,7 @@ add_test(NAME MODULE_BASE_para_setup_mpi_test COMMAND ${BASH} para_setup_mpi_test.sh WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) +add_test(NAME MODULE_BASE_para_kmesh_world_mpi_test + COMMAND ${BASH} test_para_kmesh_world_mpi.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/source/source_base/module_parallel/test/para_collection_mpi_test.cpp b/source/source_base/module_parallel/test/para_collection_mpi_test.cpp index a166ff0532..874f56d2c4 100644 --- a/source/source_base/module_parallel/test/para_collection_mpi_test.cpp +++ b/source/source_base/module_parallel/test/para_collection_mpi_test.cpp @@ -8,7 +8,7 @@ TEST(ParaCollectionMpiTest, AssembleAndFind) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 1, 4, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1, 1))); coll.add(Parallel::ParaWorld::make_serial(Parallel::ParaTag::pw)); EXPECT_EQ(coll.size(), 2u); @@ -25,7 +25,7 @@ TEST(ParaCollectionMpiTest, FindMissingReturnsInvalid) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 1, 4, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1, 1))); const Parallel::ParaWorld& missing = coll.find("nonexistent"); EXPECT_FALSE(missing.valid()); @@ -35,7 +35,7 @@ TEST(ParaCollectionMpiTest, FindAsSubclass) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 1, 8, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 8, 1, 1))); const Parallel::ParaKmeshWorld* kmesh = coll.find_as(Parallel::ParaTag::kmesh); ASSERT_NE(kmesh, nullptr); diff --git a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp new file mode 100644 index 0000000000..0b246ffd75 --- /dev/null +++ b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp @@ -0,0 +1,76 @@ +#include "gtest/gtest.h" + +#include "../para_kmesh_world.h" + +// Run with: mpirun -np 4 ./MODULE_BASE_para_kmesh_world_mpi +// +// Covers the band-parallel reduction path (bndpar > 1) that the legacy +// Parallel_Reduce::reduce_double_allpool provided and that was dropped in the +// first ParaKmeshWorld migration, breaking tests/11_PW_GPU/scf_bpcg +// (kpar=1, bndpar=2). + +TEST(ParaKmeshWorldMpiTest, ReduceAcrossBandGroupsBndpar2) +{ + int nprocs = 0; + int myrank = 0; + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + ASSERT_EQ(nprocs, 4); + + // kpar=1, bndpar=2, 4 ranks: 2 ranks per band group. Each band group + // holds a partial occupation sum of 14 (28 electrons split in two). + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1, 2); + EXPECT_EQ(kmesh.npool(), 2); + + double sumk = 14.0; + kmesh.reduce_across_pools(sumk); + EXPECT_DOUBLE_EQ(sumk, 28.0); + + // max/min must span the band groups as well: the two shards see + // different eigenvalue windows. + double eup = (myrank < 2) ? 40.0 : 45.0; + kmesh.reduce_max_across_pools(eup); + EXPECT_DOUBLE_EQ(eup, 45.0); + + double elw = (myrank < 2) ? -1.0 : -5.0; + kmesh.reduce_min_across_pools(elw); + EXPECT_DOUBLE_EQ(elw, -5.0); +} + +TEST(ParaKmeshWorldMpiTest, ReduceAcrossKpoolsKpar2) +{ + int nprocs = 0; + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + ASSERT_EQ(nprocs, 4); + + // kpar=2, bndpar=1: 2 k-point pools of 2 ranks each. + const int my_pool = (nprocs > 1) ? 0 : 0; // distribution detail unused here + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 2, my_pool, 4, 1, 1); + EXPECT_EQ(kmesh.npool(), 2); + + double sumk = 3.5; + kmesh.reduce_across_pools(sumk); + EXPECT_DOUBLE_EQ(sumk, 7.0); +} + +TEST(ParaKmeshWorldMpiTest, SinglePoolIsNoOp) +{ + int nprocs = 0; + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + ASSERT_EQ(nprocs, 4); + + // npool == 1: reduction must be a no-op regardless of the world size. + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1, 1); + double sumk = 42.0; + kmesh.reduce_across_pools(sumk); + EXPECT_DOUBLE_EQ(sumk, 42.0); +} + +int main(int argc, char** argv) +{ + MPI_Init(&argc, &argv); + testing::InitGoogleTest(&argc, argv); + const int result = RUN_ALL_TESTS(); + MPI_Finalize(); + return result; +} diff --git a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.sh b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.sh new file mode 100644 index 0000000000..dd934e2ad4 --- /dev/null +++ b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e + +np=`cat /proc/cpuinfo | grep "cpu cores" | uniq| awk '{print $NF}'` +echo "nprocs in this machine is $np" + +for i in 4;do + if [[ $i -gt $np ]];then + continue + fi + echo "TEST in parallel, nprocs=$i" + mpirun -np $i ./MODULE_BASE_para_kmesh_world_mpi + if [[ $? -ne 0 ]]; then + echo -e "\e[1;33m [ FAILED ] \e[0m"\ + "execute UT with $i cores error." + exit 1 + fi + break +done From a5bd1bc46a0463093bb3404a20f481b750ee84a5 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 16:49:07 +0800 Subject: [PATCH 07/13] Use reduce-only kmesh domain in calculate_weights, guard empty pools Two follow-ups from review of the bndpar reduction fix: - calculate_weights called make_kmesh_world(nkstot, nspin) on every SCF iteration, rebuilding the k-point distribution arrays although only the cross-pool reductions are used. Switch both branches (iweights and gweights) to the reduce-only make_kmesh_world() overload, matching what calEBand already does. - max_nks_pool() dereferences an empty vector on reduce-only domains; add an assert so the invalid query fails loudly instead of hitting undefined behavior. Verified: cmake build + OMP_NUM_THREADS=1 ctest -R "para_kmesh_world|para_collection|elecstate_occupy|elecstate_base" -- 7/7 passed. --- .../source_base/module_parallel/para_kmesh_world.cpp | 3 +++ source/source_estate/elecstate_tools.cpp | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/source/source_base/module_parallel/para_kmesh_world.cpp b/source/source_base/module_parallel/para_kmesh_world.cpp index 0845490c07..dd42003e3d 100644 --- a/source/source_base/module_parallel/para_kmesh_world.cpp +++ b/source/source_base/module_parallel/para_kmesh_world.cpp @@ -102,6 +102,9 @@ int ParaKmeshWorld::startpro_pool(int pool) const int ParaKmeshWorld::max_nks_pool() const { + // Reduce-only domains carry no distribution arrays (see the default + // constructor); querying them here would dereference an empty vector. + assert(!nks_pool_.empty()); return *std::max_element(nks_pool_.begin(), nks_pool_.end()); } diff --git a/source/source_estate/elecstate_tools.cpp b/source/source_estate/elecstate_tools.cpp index c0ec682a43..452334e25c 100644 --- a/source/source_estate/elecstate_tools.cpp +++ b/source/source_estate/elecstate_tools.cpp @@ -106,8 +106,10 @@ void calculate_weights(const ModuleBase::matrix& ekb, const int band_offset = get_band_offset(nbands, global_nbands); // The kmesh domain is only built in the branches that dereference // klist, so that callers passing no k-list (fixed occupations) are - // not affected. - Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(klist->get_nkstot(), nspin); + // not affected. Only the reduction is needed here, so use the + // reduce-only overload and skip building the distribution arrays + // on every SCF iteration. + Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(); if (PARAM.globalv.two_fermi) { Occupy::iweights(nks, klist->wk, nbands, band_offset, nelec_spin[0], ekb, eferm.ef_up, wg, @@ -128,8 +130,8 @@ void calculate_weights(const ModuleBase::matrix& ekb, { // The kmesh domain is only built in the branches that dereference // klist, so that callers passing no k-list (fixed occupations) are - // not affected. - Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(klist->get_nkstot(), nspin); + // not affected. Reduce-only overload: see the iweights branch above. + Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(); if (PARAM.globalv.two_fermi) { double demet_up = 0.0; From 81c372d4413e962ef4500c4405e08a636078910e Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 17:28:51 +0800 Subject: [PATCH 08/13] Move cube/output_log/orb_info/PAO I/O sources from source_io to source_cell Relocate cube_io.h, read_cube.cpp, write_cube.cpp, output_log.h/cpp, write_orb_info.h/cpp, and write_pao.h/cpp into source_cell to match their UnitCell-centric semantics. Update all include paths, CMake registrations, Makefile.Objects groups, and test CMakeLists.txt entries accordingly. Global state cleanups inside the moved files are deferred to a follow-up change. --- source/Makefile.Objects | 10 +++++----- source/source_cell/CMakeLists.txt | 8 ++++++++ .../{source_io/module_output => source_cell}/cube_io.h | 0 .../module_output => source_cell}/output_log.cpp | 0 .../module_output => source_cell}/output_log.h | 0 .../module_output => source_cell}/read_cube.cpp | 2 +- .../module_output => source_cell}/write_cube.cpp | 2 +- .../module_output => source_cell}/write_orb_info.cpp | 0 .../module_output => source_cell}/write_orb_info.h | 0 .../module_output => source_cell}/write_pao.cpp | 0 .../module_output => source_cell}/write_pao.h | 0 source/source_esolver/esolver_dm2rho.cpp | 2 +- source/source_esolver/esolver_dp.cpp | 2 +- source/source_esolver/esolver_fp.cpp | 2 +- source/source_esolver/esolver_ks.cpp | 2 +- source/source_esolver/esolver_ks_lcao_tddft.cpp | 2 +- source/source_esolver/esolver_lj.cpp | 2 +- source/source_esolver/esolver_lr_lcao_tddft.cpp | 2 +- source/source_esolver/esolver_nep.cpp | 2 +- source/source_esolver/test/CMakeLists.txt | 2 +- source/source_estate/module_charge/charge_extra.cpp | 2 +- source/source_estate/module_charge/charge_init.cpp | 2 +- source/source_estate/test/CMakeLists.txt | 2 +- source/source_estate/write_elecstat_pot.cpp | 4 ++-- source/source_estate/write_init.cpp | 2 +- source/source_io/CMakeLists.txt | 5 ----- source/source_io/module_chgpot/get_pchg_lcao.cpp | 2 +- source/source_io/module_chgpot/get_pchg_pw.h | 2 +- source/source_io/module_chgpot/write_libxc_r.cpp | 2 +- source/source_io/module_ctrl/ctrl_output_fp.cpp | 2 +- source/source_io/module_dos/cal_ldos.cpp | 2 +- source/source_io/module_dos/cal_pdos_gamma.cpp | 2 +- source/source_io/module_dos/cal_pdos_multik.cpp | 2 +- source/source_io/module_elf/write_elf.cpp | 2 +- .../source_io/module_energy/write_proj_band_lcao.cpp | 2 +- source/source_io/module_wf/get_wf_lcao.cpp | 2 +- source/source_io/test/CMakeLists.txt | 4 ++-- source/source_io/test/outputlog_test.cpp | 2 +- source/source_io/test/write_orb_info_test.cpp | 2 +- source/source_io/test_serial/CMakeLists.txt | 2 +- source/source_io/test_serial/rho_io_test.cpp | 4 ++-- source/source_lcao/force_stress_lcao.cpp | 2 +- source/source_lcao/module_lr/potentials/xc_kernel.cpp | 2 +- source/source_lcao/module_lr/utils/exciton_plotter.h | 2 +- source/source_md/md_func.cpp | 2 +- source/source_md/test/CMakeLists.txt | 2 +- source/source_psi/psi_init_atomic.cpp | 2 +- source/source_psi/test/CMakeLists.txt | 2 +- source/source_pw/module_ofdft/of_stress_pw.cpp | 2 +- source/source_pw/module_pwdft/force_pw.cpp | 2 +- source/source_pw/module_pwdft/stress_pw.cpp | 2 +- source/source_pw/module_stodft/sto_forces.cpp | 2 +- source/source_pw/module_stodft/sto_stress_pw.cpp | 2 +- source/source_relax/relax_driver.cpp | 2 +- 54 files changed, 60 insertions(+), 57 deletions(-) rename source/{source_io/module_output => source_cell}/cube_io.h (100%) rename source/{source_io/module_output => source_cell}/output_log.cpp (100%) rename source/{source_io/module_output => source_cell}/output_log.h (100%) rename source/{source_io/module_output => source_cell}/read_cube.cpp (99%) rename source/{source_io/module_output => source_cell}/write_cube.cpp (99%) rename source/{source_io/module_output => source_cell}/write_orb_info.cpp (100%) rename source/{source_io/module_output => source_cell}/write_orb_info.h (100%) rename source/{source_io/module_output => source_cell}/write_pao.cpp (100%) rename source/{source_io/module_output => source_cell}/write_pao.h (100%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 415b79fd1a..9f7b7294d9 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -230,6 +230,10 @@ OBJS_CELL=atom_pseudo.o\ md_cell.o\ cif_io.o\ ucell_io.o\ + read_cube.o\ + write_cube.o\ + write_pao.o\ + output_log.o\ OBJS_DEEPKS=lcao_deepks.o\ deepks_basic.o\ @@ -625,7 +629,6 @@ OBJS_IO=module_parameter/input_conv.o\ module_bessel/numerical_basis_output.o\ output.o\ module_output/print_info.o\ - module_output/read_cube.o\ module_wf/read_wfc_pw.o\ module_wf/read_wf2rho_pw.o\ module_restart/restart.o\ @@ -644,9 +647,7 @@ OBJS_IO=module_parameter/input_conv.o\ module_wannier/to_w90_pw_setup.o\ module_wannier/fr_overlap.o\ module_unk/unk_overlap_pw.o\ - module_output/write_pao.o\ module_wf/write_wfc_pw.o\ - module_output/write_cube.o\ module_elf/write_elf.o\ module_dipole/write_dipole.o\ module_current/td_current_io.o\ @@ -654,7 +655,6 @@ OBJS_IO=module_parameter/input_conv.o\ td_efield_io.o\ td_vector_pot_io.o\ module_chgpot/write_libxc_r.o\ - module_output/output_log.o\ module_hs/output_mat_sparse.o\ module_ctrl/ctrl_scf_lcao.o\ module_ctrl/ctrl_runner_lcao.o\ @@ -691,7 +691,7 @@ OBJS_IO=module_parameter/input_conv.o\ module_hs/cal_plpr.o\ OBJS_IO_LCAO=module_hs/cal_r_overlap_r.o\ - module_output/write_orb_info.o\ + write_orb_info.o\ module_dos/write_dos_lcao.o\ module_energy/write_proj_band_lcao.o\ module_energy/write_eig_occ.o\ diff --git a/source/source_cell/CMakeLists.txt b/source/source_cell/CMakeLists.txt index 3c74e1cd4f..8bb9d69106 100644 --- a/source/source_cell/CMakeLists.txt +++ b/source/source_cell/CMakeLists.txt @@ -43,8 +43,16 @@ add_library( cal_ux.cpp cif_io.cpp ucell_io.cpp + read_cube.cpp + write_cube.cpp + write_pao.cpp + output_log.cpp ) +if(ENABLE_LCAO) + target_sources(cell PRIVATE write_orb_info.cpp) +endif() + if(ENABLE_COVERAGE) add_coverage(cell) endif() diff --git a/source/source_io/module_output/cube_io.h b/source/source_cell/cube_io.h similarity index 100% rename from source/source_io/module_output/cube_io.h rename to source/source_cell/cube_io.h diff --git a/source/source_io/module_output/output_log.cpp b/source/source_cell/output_log.cpp similarity index 100% rename from source/source_io/module_output/output_log.cpp rename to source/source_cell/output_log.cpp diff --git a/source/source_io/module_output/output_log.h b/source/source_cell/output_log.h similarity index 100% rename from source/source_io/module_output/output_log.h rename to source/source_cell/output_log.h diff --git a/source/source_io/module_output/read_cube.cpp b/source/source_cell/read_cube.cpp similarity index 99% rename from source/source_io/module_output/read_cube.cpp rename to source/source_cell/read_cube.cpp index a4155f78f8..cbae974ed0 100644 --- a/source/source_io/module_output/read_cube.cpp +++ b/source/source_cell/read_cube.cpp @@ -1,4 +1,4 @@ -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include #include "source_base/parallel_grid.h" #include "source_io/module_parameter/parameter.h" diff --git a/source/source_io/module_output/write_cube.cpp b/source/source_cell/write_cube.cpp similarity index 99% rename from source/source_io/module_output/write_cube.cpp rename to source/source_cell/write_cube.cpp index 1771a28026..e328338f8f 100644 --- a/source/source_io/module_output/write_cube.cpp +++ b/source/source_cell/write_cube.cpp @@ -1,7 +1,7 @@ #include "source_base/element_name.h" #include "source_base/parallel_comm.h" #include "source_base/parallel_grid.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include diff --git a/source/source_io/module_output/write_orb_info.cpp b/source/source_cell/write_orb_info.cpp similarity index 100% rename from source/source_io/module_output/write_orb_info.cpp rename to source/source_cell/write_orb_info.cpp diff --git a/source/source_io/module_output/write_orb_info.h b/source/source_cell/write_orb_info.h similarity index 100% rename from source/source_io/module_output/write_orb_info.h rename to source/source_cell/write_orb_info.h diff --git a/source/source_io/module_output/write_pao.cpp b/source/source_cell/write_pao.cpp similarity index 100% rename from source/source_io/module_output/write_pao.cpp rename to source/source_cell/write_pao.cpp diff --git a/source/source_io/module_output/write_pao.h b/source/source_cell/write_pao.h similarity index 100% rename from source/source_io/module_output/write_pao.h rename to source/source_cell/write_pao.h diff --git a/source/source_esolver/esolver_dm2rho.cpp b/source/source_esolver/esolver_dm2rho.cpp index c5c323e3ea..7c01a83648 100644 --- a/source/source_esolver/esolver_dm2rho.cpp +++ b/source/source_esolver/esolver_dm2rho.cpp @@ -5,7 +5,7 @@ #include "source_cell/read_pp_ucell.h" #include "source_estate/elecstate_lcao.h" #include "source_io/module_ml/io_npz.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_lcao/lcao_domain.h" #include "source_lcao/hamilt_lcao.h" #include "source_lcao/module_operator_lcao/operator_lcao.h" diff --git a/source/source_esolver/esolver_dp.cpp b/source/source_esolver/esolver_dp.cpp index d91c54d66c..5bbf139fd2 100644 --- a/source/source_esolver/esolver_dp.cpp +++ b/source/source_esolver/esolver_dp.cpp @@ -23,7 +23,7 @@ #include "source_cell/md_cell.h" #include "source_cell/module_neighlist/neighbor_search.h" #include "source_cell/cif_io.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" #include diff --git a/source/source_esolver/esolver_fp.cpp b/source/source_esolver/esolver_fp.cpp index 7d415fc59d..84685e8a1e 100644 --- a/source/source_esolver/esolver_fp.cpp +++ b/source/source_esolver/esolver_fp.cpp @@ -7,7 +7,7 @@ #include "source_estate/param_update.h" #include "source_hamilt/module_ewald/h_ewald_pw.h" #include "source_hamilt/module_vdw/vdw.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_output/print_info.h" #include "source_estate/rhog_io.h" #include "source_io/module_parameter/parameter.h" diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index b847dc4024..1f1804641c 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -11,7 +11,7 @@ #include "source_io/module_energy/write_eig_occ.h" #include "source_io/module_energy/write_bands.h" #include "source_hamilt/module_xc/xc_functional.h" -#include "source_io/module_output/output_log.h" // use write_head +#include "source_cell/output_log.h" // use write_head #include "source_estate/elecstate_print.h" // print_etot #include "source_lcao/module_dftu/dftu_nao.h" // mohan add 2025-11-07 #include "source_hamilt/module_xc/general_exx_info.h" // for init_general_exx_info diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index f767c4b210..c2dbd1a43a 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -8,7 +8,7 @@ #include "source_io/module_ctrl/ctrl_output_td.h" #include "source_io/module_efield/td_efield_io.h" #include "source_io/module_efield/td_vector_pot_io.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" #include "source_io/module_wf/read_wfc_nao.h" //------LCAO HSolver ElecState------- diff --git a/source/source_esolver/esolver_lj.cpp b/source/source_esolver/esolver_lj.cpp index dd7ba1053f..5d853e5831 100644 --- a/source/source_esolver/esolver_lj.cpp +++ b/source/source_esolver/esolver_lj.cpp @@ -6,7 +6,7 @@ #include "source_cell/module_neighlist/neighbor_types.h" #include "source_io/module_parameter/parameter.h" #include "source_cell/cif_io.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #ifdef __MPI #include #endif diff --git a/source/source_esolver/esolver_lr_lcao_tddft.cpp b/source/source_esolver/esolver_lr_lcao_tddft.cpp index 52bd6ad0e8..879983229a 100644 --- a/source/source_esolver/esolver_lr_lcao_tddft.cpp +++ b/source/source_esolver/esolver_lr_lcao_tddft.cpp @@ -11,7 +11,7 @@ #include #include "source_lcao/hamilt_lcao.h" #include "source_io/module_wf/read_wfc_nao.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_io/module_output/print_info.h" #include "source_cell/module_neighbor/sltk_atom_arrange.h" #include "source_lcao/module_lr/utils/lr_util_print.h" diff --git a/source/source_esolver/esolver_nep.cpp b/source/source_esolver/esolver_nep.cpp index a23da6994a..a7c53943c4 100644 --- a/source/source_esolver/esolver_nep.cpp +++ b/source/source_esolver/esolver_nep.cpp @@ -21,7 +21,7 @@ #include "source_cell/md_cell.h" #include "source_cell/module_neighlist/neighbor_search.h" #include "source_cell/cif_io.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" #include diff --git a/source/source_esolver/test/CMakeLists.txt b/source/source_esolver/test/CMakeLists.txt index f8b9c302df..7be147b6bf 100644 --- a/source/source_esolver/test/CMakeLists.txt +++ b/source/source_esolver/test/CMakeLists.txt @@ -24,5 +24,5 @@ AddTest( ../esolver_dp.cpp ../../source_cell/base_cell.cpp ../../source_cell/cif_io.cpp - ../../source_io/module_output/output_log.cpp + ../../source_cell/output_log.cpp ) diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/charge_extra.cpp index 7513469a31..1b64ee5c80 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/charge_extra.cpp @@ -4,7 +4,7 @@ #include "source_base/global_variable.h" #include "source_base/timer.h" #include "source_base/tool_threading.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" Charge_Extra::Charge_Extra() { diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index 672d800f71..d3ce9534e5 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -13,7 +13,7 @@ #include "source_base/tool_threading.h" #include "source_cell/magnetism.h" #include "source_base/parallel_grid.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_estate/rhog_io.h" #include "source_io/module_wf/read_wf2rho_pw.h" #include "source_io/module_restart/restart.h" diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 4f627cc352..74a6558685 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -111,7 +111,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_charge_extra LIBS parameter base device cell_info - SOURCES charge_extra_test.cpp ../module_charge/charge_extra.cpp ../../source_io/module_output/read_cube.cpp ../../source_io/module_output/write_cube.cpp + SOURCES charge_extra_test.cpp ../module_charge/charge_extra.cpp ../../source_cell/read_cube.cpp ../../source_cell/write_cube.cpp ../../source_base/module_fft/fft_bundle.cpp ../../source_base/module_fft/fft_cpu.cpp ) diff --git a/source/source_estate/write_elecstat_pot.cpp b/source/source_estate/write_elecstat_pot.cpp index 3f630696aa..2ef5a2b9f6 100644 --- a/source/source_estate/write_elecstat_pot.cpp +++ b/source/source_estate/write_elecstat_pot.cpp @@ -2,8 +2,8 @@ #include "source_base/timer.h" #include "source_estate/module_pot/h_hartree_pw.h" #include "source_estate/module_pot/efield.h" -#include "source_io/module_output/cube_io.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/cube_io.h" +#include "source_cell/output_log.h" #include "write_elecstat_pot.h" #include diff --git a/source/source_estate/write_init.cpp b/source/source_estate/write_init.cpp index 58cdb53428..ccd8e125a9 100644 --- a/source/source_estate/write_init.cpp +++ b/source/source_estate/write_init.cpp @@ -15,7 +15,7 @@ // ===================================================================== #include "source_estate/write_init.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_base/tool_quit.h" #include diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 85ffd82d54..33a757c583 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -22,13 +22,10 @@ list(APPEND objects module_bessel/numerical_basis_jyjy.cpp module_bessel/numerical_descriptor.cpp module_output/print_info.cpp - module_output/read_cube.cpp module_wf/read_wfc_pw.cpp module_wf/read_wf2rho_pw.cpp module_restart/restart.cpp module_wf/write_wfc_pw.cpp - module_output/write_pao.cpp - module_output/write_cube.cpp module_elf/write_elf.cpp module_dipole/write_dipole.cpp module_ml/write_mlkedf_desc.cpp @@ -37,7 +34,6 @@ list(APPEND objects module_efield/td_efield_io.cpp module_efield/td_vector_pot_io.cpp module_chgpot/write_libxc_r.cpp - module_output/output_log.cpp module_json/para_json.cpp parse_args.cpp input_help.cpp @@ -64,7 +60,6 @@ if(ENABLE_LCAO) module_dos/write_dos_lcao.cpp module_dos/cal_pdos_gamma.cpp module_dos/cal_pdos_multik.cpp - module_output/write_orb_info.cpp module_energy/write_proj_band_lcao.cpp module_chgpot/get_pchg_lcao.cpp module_wf/get_wf_lcao.cpp diff --git a/source/source_io/module_chgpot/get_pchg_lcao.cpp b/source/source_io/module_chgpot/get_pchg_lcao.cpp index ae11f7b6e7..50e7d09d5a 100644 --- a/source/source_io/module_chgpot/get_pchg_lcao.cpp +++ b/source/source_io/module_chgpot/get_pchg_lcao.cpp @@ -3,7 +3,7 @@ #include "source_estate/module_charge/symm_rho.h" #include "source_estate/module_dm/cal_dm_psi.h" #include "source_hamilt/module_gint/gint_interface.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include #include diff --git a/source/source_io/module_chgpot/get_pchg_pw.h b/source/source_io/module_chgpot/get_pchg_pw.h index d7ad0b7cc8..56bd208a22 100644 --- a/source/source_io/module_chgpot/get_pchg_pw.h +++ b/source/source_io/module_chgpot/get_pchg_pw.h @@ -5,7 +5,7 @@ #include "source_base/parallel_comm.h" #include "source_estate/module_charge/symm_rho.h" #include "source_io/module_output/band_parallel_output.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" namespace ModuleIO { diff --git a/source/source_io/module_chgpot/write_libxc_r.cpp b/source/source_io/module_chgpot/write_libxc_r.cpp index d13e756385..9f49474b03 100644 --- a/source/source_io/module_chgpot/write_libxc_r.cpp +++ b/source/source_io/module_chgpot/write_libxc_r.cpp @@ -12,7 +12,7 @@ #include "source_estate/module_charge/charge.h" #include "source_basis/module_pw/pw_basis_big.h" #include "source_basis/module_pw/pw_basis.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_base/global_variable.h" #include "source_io/module_parameter/parameter.h" #include "source_base/timer.h" diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 8114657f63..faaaab9eba 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -1,5 +1,5 @@ #include "ctrl_output_fp.h" // use ctrl_output_fp() -#include "../module_output/cube_io.h" // use write_vdata_palgrid +#include "source_cell/cube_io.h" // use write_vdata_palgrid #include "../module_dipole/dipole_io.h" // use write_dipole #include "source_estate/module_charge/symm_rho.h" // use Symmetry_rho #include "source_hamilt/module_xc/xc_functional.h" // use XC_Functional diff --git a/source/source_io/module_dos/cal_ldos.cpp b/source/source_io/module_dos/cal_ldos.cpp index b6ae29fba8..195ff51c97 100644 --- a/source/source_io/module_dos/cal_ldos.cpp +++ b/source/source_io/module_dos/cal_ldos.cpp @@ -1,7 +1,7 @@ #include "cal_ldos.h" #include "cal_dos.h" -#include "../module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_estate/module_dm/cal_dm_psi.h" #include "source_hamilt/module_gint/gint_interface.h" #include "source_base/module_device/memory_op.h" diff --git a/source/source_io/module_dos/cal_pdos_gamma.cpp b/source/source_io/module_dos/cal_pdos_gamma.cpp index 50acad4f26..ad7ee387ce 100644 --- a/source/source_io/module_dos/cal_pdos_gamma.cpp +++ b/source/source_io/module_dos/cal_pdos_gamma.cpp @@ -6,7 +6,7 @@ #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_lcao/hamilt_lcao.h" -#include "source_io/module_output/write_orb_info.h" +#include "source_cell/write_orb_info.h" void ModuleIO::cal_pdos( diff --git a/source/source_io/module_dos/cal_pdos_multik.cpp b/source/source_io/module_dos/cal_pdos_multik.cpp index b7766b22f4..87bcc0aac2 100644 --- a/source/source_io/module_dos/cal_pdos_multik.cpp +++ b/source/source_io/module_dos/cal_pdos_multik.cpp @@ -3,7 +3,7 @@ #include "source_base/parallel_reduce.h" #include "source_base/module_external/blas_connector.h" #include "source_base/module_external/scalapack_connector.h" -#include "source_io/module_output/write_orb_info.h" +#include "source_cell/write_orb_info.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_lcao/hamilt_lcao.h" diff --git a/source/source_io/module_elf/write_elf.cpp b/source/source_io/module_elf/write_elf.cpp index ea6e9b8805..8c9eba0126 100644 --- a/source/source_io/module_elf/write_elf.cpp +++ b/source/source_io/module_elf/write_elf.cpp @@ -1,5 +1,5 @@ #include "write_elf.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #ifdef _OPENMP #include #endif diff --git a/source/source_io/module_energy/write_proj_band_lcao.cpp b/source/source_io/module_energy/write_proj_band_lcao.cpp index ecf83e6a4d..80c4e38dba 100644 --- a/source/source_io/module_energy/write_proj_band_lcao.cpp +++ b/source/source_io/module_energy/write_proj_band_lcao.cpp @@ -6,7 +6,7 @@ #include "source_base/module_external/scalapack_connector.h" #include "source_base/timer.h" #include "source_cell/module_neighbor/sltk_atom_arrange.h" -#include "source_io/module_output/write_orb_info.h" +#include "source_cell/write_orb_info.h" #include "source_lcao/hamilt_lcao.h" template<> diff --git a/source/source_io/module_wf/get_wf_lcao.cpp b/source/source_io/module_wf/get_wf_lcao.cpp index ea1189acae..5416786198 100644 --- a/source/source_io/module_wf/get_wf_lcao.cpp +++ b/source/source_io/module_wf/get_wf_lcao.cpp @@ -2,7 +2,7 @@ #include "source_hamilt/module_gint/gint_env_gamma.h" #include "source_hamilt/module_gint/gint_env_k.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include #include diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/test/CMakeLists.txt index 70d94d91a2..354e0fa792 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/test/CMakeLists.txt @@ -113,7 +113,7 @@ add_test(NAME MODULE_IO_write_wfc_nao_para AddTest( TARGET MODULE_IO_write_orb_info LIBS parameter base device cell_info - SOURCES write_orb_info_test.cpp ../module_output/write_orb_info.cpp + SOURCES write_orb_info_test.cpp ../../source_cell/write_orb_info.cpp ) AddTest( @@ -137,7 +137,7 @@ AddTest( AddTest( TARGET MODULE_IO_output_log_test LIBS parameter base device - SOURCES ../module_output/output_log.cpp outputlog_test.cpp ../../source_basis/module_pw/test/test_tool.cpp + SOURCES ../../source_cell/output_log.cpp outputlog_test.cpp ../../source_basis/module_pw/test/test_tool.cpp ) if(ENABLE_LCAO) diff --git a/source/source_io/test/outputlog_test.cpp b/source/source_io/test/outputlog_test.cpp index b84a9ef4a0..ef91ae4a48 100644 --- a/source/source_io/test/outputlog_test.cpp +++ b/source/source_io/test/outputlog_test.cpp @@ -10,7 +10,7 @@ #include "source_base/constants.h" #include "source_base/global_variable.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #ifdef __MPI #include "source_basis/module_pw/test/test_tool.h" diff --git a/source/source_io/test/write_orb_info_test.cpp b/source/source_io/test/write_orb_info_test.cpp index 1829cf69fc..5509c40c37 100644 --- a/source/source_io/test/write_orb_info_test.cpp +++ b/source/source_io/test/write_orb_info_test.cpp @@ -3,7 +3,7 @@ #define private public #include "source_io/module_parameter/parameter.h" #undef private -#include "source_io/module_output/write_orb_info.h" +#include "source_cell/write_orb_info.h" #include "source_cell/unitcell.h" #include "prepare_unitcell.h" #include "source_cell/read_pp_ucell.h" diff --git a/source/source_io/test_serial/CMakeLists.txt b/source/source_io/test_serial/CMakeLists.txt index aa7fec69f4..c0593378c0 100644 --- a/source/source_io/test_serial/CMakeLists.txt +++ b/source/source_io/test_serial/CMakeLists.txt @@ -51,7 +51,7 @@ AddTest( AddTest( TARGET MODULE_IO_rho_io LIBS parameter base device cell_info - SOURCES rho_io_test.cpp ../module_output/read_cube.cpp ../module_output/write_cube.cpp + SOURCES rho_io_test.cpp ../../source_cell/read_cube.cpp ../../source_cell/write_cube.cpp ) AddTest( diff --git a/source/source_io/test_serial/rho_io_test.cpp b/source/source_io/test_serial/rho_io_test.cpp index 5939eecaef..f3b600b7f8 100644 --- a/source/source_io/test_serial/rho_io_test.cpp +++ b/source/source_io/test_serial/rho_io_test.cpp @@ -1,9 +1,9 @@ -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "source_base/global_variable.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "prepare_unitcell.h" #include "source_base/parallel_grid.h" diff --git a/source/source_lcao/force_stress_lcao.cpp b/source/source_lcao/force_stress_lcao.cpp index 895e03c137..c5ce4f2d5d 100644 --- a/source/source_lcao/force_stress_lcao.cpp +++ b/source/source_lcao/force_stress_lcao.cpp @@ -3,7 +3,7 @@ #include "source_base/parallel_reduce.h" #include "source_lcao/module_dftu/dftu_nao.h" //Quxin add for DFT+U on 20201029 #include "source_lcao/module_dftu/dftu_nao_fs_k.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" // new #include "source_base/timer.h" diff --git a/source/source_lcao/module_lr/potentials/xc_kernel.cpp b/source/source_lcao/module_lr/potentials/xc_kernel.cpp index de7146db67..aa02bfca7e 100644 --- a/source/source_lcao/module_lr/potentials/xc_kernel.cpp +++ b/source/source_lcao/module_lr/potentials/xc_kernel.cpp @@ -6,7 +6,7 @@ #include "source_lcao/module_lr/utils/lr_util_xc.hpp" #include #include -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #ifdef __LIBXC #include #include "source_hamilt/module_xc/libxc_abacus.h" diff --git a/source/source_lcao/module_lr/utils/exciton_plotter.h b/source/source_lcao/module_lr/utils/exciton_plotter.h index 18ad752123..53113519d4 100644 --- a/source/source_lcao/module_lr/utils/exciton_plotter.h +++ b/source/source_lcao/module_lr/utils/exciton_plotter.h @@ -5,7 +5,7 @@ #include "source_cell/atom_spec.h" #include "source_cell/klist.h" #include "source_estate/module_dm/density_matrix.h" -#include "source_io/module_output/cube_io.h" +#include "source_cell/cube_io.h" #include "source_hamilt/module_gint/gint_interface.h" #include "source_lcao/module_lr/dm_trans/dm_trans.h" #include "source_lcao/module_lr/utils/lr_util.h" diff --git a/source/source_md/md_func.cpp b/source/source_md/md_func.cpp index 31d8111e24..7e2b35178a 100644 --- a/source/source_md/md_func.cpp +++ b/source/source_md/md_func.cpp @@ -2,7 +2,7 @@ #include "source_base/global_variable.h" #include "source_base/timer.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" #include diff --git a/source/source_md/test/CMakeLists.txt b/source/source_md/test/CMakeLists.txt index c2e6f61f8f..da6c406c68 100644 --- a/source/source_md/test/CMakeLists.txt +++ b/source/source_md/test/CMakeLists.txt @@ -53,7 +53,7 @@ list(APPEND depend_files ../../source_cell/module_neighlist/domain_decomposition.cpp ../../source_cell/md_cell.cpp ../../source_base/output.cpp - ../../source_io/module_output/output_log.cpp + ../../source_cell/output_log.cpp ../../source_io/module_output/print_info.cpp ../../source_cell/cif_io.cpp ../../source_esolver/esolver_lj.cpp diff --git a/source/source_psi/psi_init_atomic.cpp b/source/source_psi/psi_init_atomic.cpp index 39db75dbba..04592d79a5 100644 --- a/source/source_psi/psi_init_atomic.cpp +++ b/source/source_psi/psi_init_atomic.cpp @@ -10,7 +10,7 @@ #include "source_base/tool_quit.h" #include "source_base/timer.h" #include "source_base/global_variable.h" -#include "source_io/module_output/write_pao.h" +#include "source_cell/write_pao.h" template void psi_init_atomic::allocate_ps_table() diff --git a/source/source_psi/test/CMakeLists.txt b/source/source_psi/test/CMakeLists.txt index 79438154d9..69af9a2687 100644 --- a/source/source_psi/test/CMakeLists.txt +++ b/source/source_psi/test/CMakeLists.txt @@ -16,7 +16,7 @@ AddTest( ../../source_cell/atom_spec.cpp ../../source_cell/test/support/mock_unitcell.cpp - ../../source_io/module_output/write_pao.cpp + ../../source_cell/write_pao.cpp ../../source_io/module_wf/read_wfc_pw.cpp ) endif() diff --git a/source/source_pw/module_ofdft/of_stress_pw.cpp b/source/source_pw/module_ofdft/of_stress_pw.cpp index 6b988baf78..4dade34559 100644 --- a/source/source_pw/module_ofdft/of_stress_pw.cpp +++ b/source/source_pw/module_ofdft/of_stress_pw.cpp @@ -3,7 +3,7 @@ #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_hamilt/module_vdw/vdw.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" // Since the kinetic stress of OFDFT is calculated by kinetic functionals in esolver_of.cpp, here we regard it as an // input variable. diff --git a/source/source_pw/module_pwdft/force_pw.cpp b/source/source_pw/module_pwdft/force_pw.cpp index f02afc017b..a87b30614a 100644 --- a/source/source_pw/module_pwdft/force_pw.cpp +++ b/source/source_pw/module_pwdft/force_pw.cpp @@ -4,7 +4,7 @@ #include "source_base/parallel_reduce.h" #include "source_pw/module_pwdft/kernels/force_op.h" #include "source_io/module_parameter/parameter.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" // new #include "source_base/complexmatrix.h" #include "source_base/libm/libm.h" diff --git a/source/source_pw/module_pwdft/stress_pw.cpp b/source/source_pw/module_pwdft/stress_pw.cpp index bcf6aff8db..b0b6890ef3 100644 --- a/source/source_pw/module_pwdft/stress_pw.cpp +++ b/source/source_pw/module_pwdft/stress_pw.cpp @@ -4,7 +4,7 @@ #include "source_base/tool_quit.h" #include "source_base/global_variable.h" // use GlobalC #include "source_hamilt/module_vdw/vdw.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_hamilt/module_xc/general_exx_info.h" // for General_Exx_Info type diff --git a/source/source_pw/module_stodft/sto_forces.cpp b/source/source_pw/module_stodft/sto_forces.cpp index e092b8f932..2e0d6ef286 100644 --- a/source/source_pw/module_stodft/sto_forces.cpp +++ b/source/source_pw/module_stodft/sto_forces.cpp @@ -5,7 +5,7 @@ #include "source_estate/elecstate.h" #include "source_estate/module_pot/efield.h" #include "source_estate/module_pot/gatefield.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" #include "source_pw/module_pwdft/fs_nonlocal_tools.h" diff --git a/source/source_pw/module_stodft/sto_stress_pw.cpp b/source/source_pw/module_stodft/sto_stress_pw.cpp index b85ab58b50..887629e2e6 100644 --- a/source/source_pw/module_stodft/sto_stress_pw.cpp +++ b/source/source_pw/module_stodft/sto_stress_pw.cpp @@ -5,7 +5,7 @@ #include "source_pw/module_pwdft/fs_kin_tools.h" #include "source_pw/module_pwdft/fs_nonlocal_tools.h" #include "source_pw/module_pwdft/stru_fac.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_parameter/parameter.h" template diff --git a/source/source_relax/relax_driver.cpp b/source/source_relax/relax_driver.cpp index 1d2fd132ee..8fbfc6002a 100644 --- a/source/source_relax/relax_driver.cpp +++ b/source/source_relax/relax_driver.cpp @@ -4,7 +4,7 @@ #include "source_base/version.h" #include "source_cell/cif_io.h" #include "source_io/module_json/output_info.h" -#include "source_io/module_output/output_log.h" +#include "source_cell/output_log.h" #include "source_io/module_output/print_info.h" #include "source_base/module_out/read_exit_file.h" #include "source_io/module_parameter/parameter.h" From bcceeb7db9fb88da06e9e5906c038281260a7435 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 4 Sep 2026 23:06:09 +0800 Subject: [PATCH 09/13] Fix build: rename world_communication_domain to world_comm_domain upstream/develop renamed ModuleBase::world_communication_domain() to world_comm_domain() in parallel_cell; update the three test_parallel call sites merged in from upstream so the tree compiles again. --- source/source_base/test_parallel/parallel_device_test.cpp | 2 +- .../source_base/test_parallel/parallel_domain_grid_test.cpp | 6 +++--- source/source_base/test_parallel/test_para_gemm.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/source_base/test_parallel/parallel_device_test.cpp b/source/source_base/test_parallel/parallel_device_test.cpp index 0c6746267e..ba4a4d90b8 100644 --- a/source/source_base/test_parallel/parallel_device_test.cpp +++ b/source/source_base/test_parallel/parallel_device_test.cpp @@ -124,7 +124,7 @@ TEST(ParallelDevice, CoversGpuStagingWithoutAccelerator) TEST(ParallelDevice, CoversMpiTypeOverloads) { - const ModuleBase::CommunicationDomain domain = ModuleBase::world_communication_domain(); + const ModuleBase::CommunicationDomain domain = ModuleBase::world_comm_domain(); exercise_mpi_wrappers(domain); exercise_mpi_wrappers(domain); exercise_mpi_wrappers>(domain); diff --git a/source/source_base/test_parallel/parallel_domain_grid_test.cpp b/source/source_base/test_parallel/parallel_domain_grid_test.cpp index f71581e74e..5a066c6e0a 100644 --- a/source/source_base/test_parallel/parallel_domain_grid_test.cpp +++ b/source/source_base/test_parallel/parallel_domain_grid_test.cpp @@ -14,7 +14,7 @@ TEST(CommunicationDomainTest, ReportsDefaultAndWorldDomains) EXPECT_EQ(local_domain.rank(), 0); EXPECT_EQ(local_domain.communicator(), MPI_COMM_NULL); - const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_communication_domain(); + const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_comm_domain(); MPICommGroup world_group(world_domain.communicator()); EXPECT_EQ(world_domain.communicator(), MPI_COMM_WORLD); EXPECT_GE(world_domain.rank(), 0); @@ -28,7 +28,7 @@ TEST(CommunicationDomainTest, ReportsDefaultAndWorldDomains) TEST(MPICommGroupTest, DividesWorldIntoEvenGroups) { - const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_communication_domain(); + const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_comm_domain(); MPICommGroup group(MPI_COMM_WORLD); EXPECT_EQ(group.grank, world_domain.rank()); @@ -46,7 +46,7 @@ TEST(MPICommGroupTest, DividesWorldIntoEvenGroups) TEST(ParallelGridTest, BroadcastsAndReducesDistributedGrid) { - const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_communication_domain(); + const ModuleBase::CommunicationDomain world_domain = ModuleBase::world_comm_domain(); MPICommGroup world_group(world_domain.communicator()); const int nx = 2; const int ny = 1; diff --git a/source/source_base/test_parallel/test_para_gemm.cpp b/source/source_base/test_parallel/test_para_gemm.cpp index f116507e7f..0e62823c3b 100644 --- a/source/source_base/test_parallel/test_para_gemm.cpp +++ b/source/source_base/test_parallel/test_para_gemm.cpp @@ -74,7 +74,7 @@ void expect_near_value(const T& actual, const T& expected) template void test_additional_type_paths() { - const ModuleBase::CommunicationDomain domain = ModuleBase::world_communication_domain(); + const ModuleBase::CommunicationDomain domain = ModuleBase::world_comm_domain(); MPI_Comm world = domain.communicator(); const int rank = domain.rank(); MPICommGroup world_group(world); From fd7febda5d0231b0a8f86057d32bff61ac93ac62 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 5 Sep 2026 14:09:25 +0800 Subject: [PATCH 10/13] docs(parallel): clarify k-pool vs band-pool terminology in comm comments The term "pool" was overloaded: MY_POOL refers to a k-pool (the KPAR split, done first and independent of bndpar), while NPROC_IN_POOL / RANK_IN_POOL / POOL_WORLD refer to the (k-pool x band-group) cell created by the later BNDPAR split. Document the two-level split order and each communicator's exact semantics, including that KP_WORLD is MPI_COMM_NULL for uneven k-pool sizes. Comment-only change. --- source/source_base/module_parallel/para_tag.h | 17 +++++++++++------ source/source_base/parallel_comm.cpp | 16 ++++++++++++---- source/source_base/parallel_comm.h | 8 ++++---- source/source_base/parallel_global.cpp | 11 ++++++++--- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/source/source_base/module_parallel/para_tag.h b/source/source_base/module_parallel/para_tag.h index 7d2ff9543c..2573cf2fd2 100644 --- a/source/source_base/module_parallel/para_tag.h +++ b/source/source_base/module_parallel/para_tag.h @@ -9,14 +9,19 @@ namespace Parallel /** * @brief Domain tag constants for the parallel communication domains. * - * These tags replace raw string literals to avoid typo-induced runtime - * failures. They map to the legacy global communicators as follows: + * Pool terminology (see parallel_comm.cpp): + * - k-pool: one of the KPAR groups of processes that share one subset of + * k-points. This split happens first and is independent of bndpar. + * - band-pool: one of the BNDPAR sub-groups of a k-pool, holding one + * band window ("band group"). + * + * The tags map to the legacy global communicators as follows: * - esolver -> one esolver instance (intra-image communicator) * - images -> cross-image communicator (same rank_in_esolver) - * - pw -> POOL_WORLD - * - kmesh -> KP_WORLD - * - bsame_kdiff -> INT_BGROUP - * - bdiff_ksame -> BP_WORLD + * - pw -> POOL_WORLD (one band-pool) + * - kmesh -> KP_WORLD (links k-pools; only valid when the k-pool split is even) + * - bsame_kdiff -> INT_BGROUP (same band group across k-pools) + * - bdiff_ksame -> BP_WORLD (different band groups inside one k-pool) * - rgrid -> GRID_WORLD * - diag -> DIAG_WORLD * - matrix -> matrix domain diff --git a/source/source_base/parallel_comm.cpp b/source/source_base/parallel_comm.cpp index 5d03447b5a..27eac402b1 100644 --- a/source/source_base/parallel_comm.cpp +++ b/source/source_base/parallel_comm.cpp @@ -3,10 +3,18 @@ #include "mpi.h" #include "parallel_global.h" -MPI_Comm POOL_WORLD; //groups for different plane waves. In this group, only plane waves are different. K-points and bands are the same. -MPI_Comm KP_WORLD; // groups for differnt k. In this group, only k-points are different. Bands and plane waves are the same. -MPI_Comm BP_WORLD; // groups for differnt bands. In this group, only bands are different. K-points and plane waves are the same. -MPI_Comm INT_BGROUP; // internal comm groups for same bands. In this group, only bands are the same. K-points and plane waves are different. +// Two-level pool terminology used across the parallel layer: +// - k-pool: a group of processes that share one subset of k-points. The +// processes are split into KPAR k-pools first (divide_pools); this split +// is independent of bndpar. MY_POOL / KP_WORLD refer to this level. +// - band-pool: a sub-group of one k-pool, created afterwards by dividing +// the k-pool into BNDPAR band groups. NPROC_IN_POOL / RANK_IN_POOL / +// POOL_WORLD refer to this level, i.e. the term "pool" in those globals +// means the (k-pool, band-group) cell, NOT the k-pool itself. +MPI_Comm POOL_WORLD; // one band-pool (k-pool x band-group cell): plane waves are distributed, k-points and the band window are shared. +MPI_Comm KP_WORLD; // links k-pools: only k-points differ; same rank_in_pool position in every k-pool. Valid ONLY when k-pools are equally sized (NPROC % KPAR == 0), otherwise MPI_COMM_NULL. +MPI_Comm BP_WORLD; // links band groups inside one k-pool: only the band window differs; k-points and plane-wave slab are the same. One communicator per rank position. +MPI_Comm INT_BGROUP; // same band-group index across all k-pools (plus the plane-wave ranks of that band group): k-points differ, the band window is the same. Always valid, also for uneven k-pools. MPI_Comm GRID_WORLD; // mohan add 2012-01-13 MPI_Comm DIAG_WORLD; // mohan add 2012-01-13 diff --git a/source/source_base/parallel_comm.h b/source/source_base/parallel_comm.h index 2243aea729..1422272863 100644 --- a/source/source_base/parallel_comm.h +++ b/source/source_base/parallel_comm.h @@ -3,10 +3,10 @@ #ifdef __MPI #include "mpi.h" -extern MPI_Comm POOL_WORLD; -extern MPI_Comm KP_WORLD; // communicator among different pools -extern MPI_Comm INT_BGROUP; -extern MPI_Comm BP_WORLD; +extern MPI_Comm POOL_WORLD; // one band-pool (k-pool x band-group cell): only plane waves are distributed +extern MPI_Comm KP_WORLD; // links k-pools at the same rank_in_pool position; MPI_COMM_NULL when k-pools are uneven +extern MPI_Comm INT_BGROUP; // same band-group index across all k-pools +extern MPI_Comm BP_WORLD; // links band groups inside one k-pool (same k, different band windows) extern MPI_Comm GRID_WORLD; // mohan add 2012-01-13 extern MPI_Comm DIAG_WORLD; // mohan add 2012-01-13 diff --git a/source/source_base/parallel_global.cpp b/source/source_base/parallel_global.cpp index 697b7f1f70..fa7a5aa40a 100644 --- a/source/source_base/parallel_global.cpp +++ b/source/source_base/parallel_global.cpp @@ -230,9 +230,14 @@ void Parallel_Global::divide_pools(const int& NPROC, int& RANK_IN_POOL, int& MY_POOL) { - // note: the order of k-point parallelization and band parallelization is important - // The order will not change the behavior of KP_WORLD or BP_WORLD, and MY_POOL - // and MY_BNDGROUP will be the same as well. + // Two-level split, order matters: + // 1. k-point parallelization: NPROC processes are divided into KPAR + // k-pools FIRST, independent of BNDPAR. MY_POOL is the k-pool index. + // Uneven k-pool sizes (NPROC % KPAR != 0) are allowed here; in that + // case KP_WORLD is MPI_COMM_NULL (see MPICommGroup::divide_group_comm). + // 2. band parallelization: each k-pool is divided into BNDPAR band + // groups ("band-pools"). NPROC_IN_POOL / RANK_IN_POOL / POOL_WORLD + // belong to this (k-pool x band-group) cell, NOT to the k-pool. if(BNDPAR > 1 && NPROC %(BNDPAR * KPAR) != 0) { std::cout << "Error: When BNDPAR = " << BNDPAR << " > 1, number of processes (" << NPROC From e3c3f967aa6595994ba93c33dadefb827e614840 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 5 Sep 2026 14:10:50 +0800 Subject: [PATCH 11/13] feat(parallel): add band-group reduction to ParaBgroupWorld Add ParaBgroupWorld::reduce_across_bgroups, summing a scalar across the BNDPAR band groups of one k-pool on BP_WORLD (bdiff_ksame). BPCG shards the band range across band groups, so each process only accumulates its own band window; this combines those partial sums before the k-pool reduction. Also add the make_bgroup_world bridge from the old INT_BGROUP / BP_WORLD globals, guarded against uninitialized layouts. Pure addition, no caller yet. --- .../module_parallel/para_bgroup_world.cpp | 11 ++++++++++ .../module_parallel/para_bgroup_world.h | 19 ++++++++++++++++++ .../module_parallel/para_bridge.cpp | 20 +++++++++++++++++++ .../source_base/module_parallel/para_bridge.h | 11 ++++++++++ 4 files changed, 61 insertions(+) diff --git a/source/source_base/module_parallel/para_bgroup_world.cpp b/source/source_base/module_parallel/para_bgroup_world.cpp index 8349325375..f84659f745 100644 --- a/source/source_base/module_parallel/para_bgroup_world.cpp +++ b/source/source_base/module_parallel/para_bgroup_world.cpp @@ -19,4 +19,15 @@ ParaBgroupWorld::ParaBgroupWorld(const MPI_Comm& intra_comm, const MPI_Comm& int } #endif +void ParaBgroupWorld::reduce_across_bgroups(double& value) const +{ +#ifdef __MPI + if (inter_comm_ == MPI_COMM_NULL || nbndgroup_ <= 1) + { + return; + } + MPI_Allreduce(MPI_IN_PLACE, &value, 1, MPI_DOUBLE, MPI_SUM, inter_comm_); +#endif +} + } // namespace Parallel diff --git a/source/source_base/module_parallel/para_bgroup_world.h b/source/source_base/module_parallel/para_bgroup_world.h index e2d82cc99b..4e108f83ba 100644 --- a/source/source_base/module_parallel/para_bgroup_world.h +++ b/source/source_base/module_parallel/para_bgroup_world.h @@ -54,6 +54,25 @@ class ParaBgroupWorld : public ParaWorld MPI_Comm inter_comm() const { return inter_comm_; } #endif + /** + * @brief Sum a scalar across the band groups of this k-pool. + * + * Band-parallel eigensolvers (bpcg) shard the band range across the + * BNDPAR band groups of a k-pool: every process only accumulates the + * partial sum over its own band window. This reduction combines those + * partial sums on BP_WORLD (bdiff_ksame), which links the same rank + * position of every band group inside one k-pool, so each band window + * contributes exactly once. + * + * It must run BEFORE ParaKmeshWorld::reduce_across_pools so that the + * k-pool reduction receives one complete per-k-pool partial sum. + * No-op when there is only a single band group. + * + * @param[in,out] value local partial sum, overwritten with the + * k-pool-wide total + */ + void reduce_across_bgroups(double& value) const; + private: int my_bndgroup_ = 0; int nbndgroup_ = 1; diff --git a/source/source_base/module_parallel/para_bridge.cpp b/source/source_base/module_parallel/para_bridge.cpp index d3e1a5f18b..3e11d1428a 100644 --- a/source/source_base/module_parallel/para_bridge.cpp +++ b/source/source_base/module_parallel/para_bridge.cpp @@ -80,4 +80,24 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin) return ParaKmeshWorld(nkstot, nspin); } +// Temporary bridge: construct a bgroup-domain ParaBgroupWorld from the old +// globals. Delete this file once ParaCollection is wired into driver init. +ParaBgroupWorld make_bgroup_world() +{ +#ifdef __MPI + int mpi_initialized = 0; + MPI_Initialized(&mpi_initialized); + // NPROC_IN_BNDGROUP stays 0 until divide_pools has run, which also + // guards unit tests that link the MPI base library without a layout. + if (mpi_initialized && INT_BGROUP != MPI_COMM_NULL && BP_WORLD != MPI_COMM_NULL + && GlobalV::NPROC_IN_BNDGROUP > 1) + { + int nbndgroup = 1; + MPI_Comm_size(BP_WORLD, &nbndgroup); + return ParaBgroupWorld(INT_BGROUP, BP_WORLD, nbndgroup); + } +#endif + return ParaBgroupWorld(); +} + } // namespace Parallel diff --git a/source/source_base/module_parallel/para_bridge.h b/source/source_base/module_parallel/para_bridge.h index 87d3fe128d..7e6d0e3edf 100644 --- a/source/source_base/module_parallel/para_bridge.h +++ b/source/source_base/module_parallel/para_bridge.h @@ -1,6 +1,7 @@ #ifndef PARA_BRIDGE_H #define PARA_BRIDGE_H +#include "para_bgroup_world.h" #include "para_kmesh_world.h" #include "para_world.h" @@ -43,6 +44,16 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin); */ ParaKmeshWorld make_kmesh_world(); +/** + * @brief Temporary bridge: construct a bgroup-domain ParaBgroupWorld from + * the old globals INT_BGROUP / BP_WORLD (MPI) or as a serial domain. + * + * Falls back to a serial single-band-group domain when MPI is not + * initialized or the pool layout has not been set up yet (e.g. unit + * tests), so that no MPI call is made on an unset communicator. + */ +ParaBgroupWorld make_bgroup_world(); + } // namespace Parallel #endif // PARA_BRIDGE_H From 86499a285c45f6305a1760825de9bd75345d6a30 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 5 Sep 2026 14:17:06 +0800 Subject: [PATCH 12/13] fix(parallel): correct cross-pool reduction for uneven k-pool layouts ParaKmeshWorld::reduce_across_pools divided each partial sum by nproc/npool (an integer average pool size) before a world-wide Allreduce. With uneven k-pools (e.g. nproc=4, kpar=3 as in 007_PW_UPF201_USPP_Fe) that division collapsed to 1 and double-counted the front pool, corrupting the electron count, the Fermi level and thus the total energy (deviation ~8.8 Ry). Make the k-mesh domain a pure k-pool topology, independent of bndpar: - reduce_across_pools now lets only the first process of each k-pool contribute (one contribution per pool, no division), correct for both even and uneven pool sizes. - The band dimension moves out of the k-mesh domain: callers (Occupy::sumkg, calEBand, calculate_weights/demet) now run ParaBgroupWorld::reduce_across_bgroups first, then the k-pool reduction. This preserves the bpcg behavior previously folded into npool() = kpar * bndpar. - reduce_max/min_across_pools stay world-wide: max/min are idempotent, and band-parallel shards see different eigenvalue windows, so the Fermi bounds must be extremized across both dimensions. - Fix the member-order bug where distribute_kpoints() derived the per-pool first-rank table from an uninitialized nproc_ (always 1). - Drop the bndpar constructor argument and the bndpar_from_layout reverse derivation from the bridge. Update the MPI unit tests: the bndpar=2 case now exercises the two-step protocol, and a new uneven k-pool case (nproc=4, kpar=3) asserts exactly one contribution per pool. Verified: 4/4 MPI tests, 7 serial kmesh, 2 bgroup, 6 collection, 25 occupy tests pass. --- .../module_parallel/para_bridge.cpp | 41 ++----- .../module_parallel/para_kmesh_world.cpp | 40 +++---- .../module_parallel/para_kmesh_world.h | 101 ++++++++++-------- .../module_parallel/test/CMakeLists.txt | 2 +- .../test/para_collection_mpi_test.cpp | 6 +- .../test/test_para_kmesh_world_mpi.cpp | 89 ++++++++++++--- source/source_estate/elecstate_tools.cpp | 14 ++- source/source_estate/occupy.cpp | 8 ++ 8 files changed, 183 insertions(+), 118 deletions(-) diff --git a/source/source_base/module_parallel/para_bridge.cpp b/source/source_base/module_parallel/para_bridge.cpp index 3e11d1428a..022037f3aa 100644 --- a/source/source_base/module_parallel/para_bridge.cpp +++ b/source/source_base/module_parallel/para_bridge.cpp @@ -8,24 +8,6 @@ namespace Parallel { -namespace -{ -#ifdef __MPI -// Number of band-parallel groups, derived from the pool layout to avoid a -// dependency on the INPUT-parameter module from source_base. -// nproc = (KPAR * bndpar) * NPROC_IN_POOL, so bndpar = NPROC / (KPAR * -// NPROC_IN_POOL). Falls back to 1 if the globals are inconsistent. -int bndpar_from_layout() -{ - const int denom = GlobalV::KPAR * GlobalV::NPROC_IN_POOL; - if (denom < 1 || GlobalV::NPROC % denom != 0) - { - return 1; - } - return GlobalV::NPROC / denom; -} -#endif -} // namespace // Temporary bridge: construct a pw-domain ParaWorld from the old globals. // Delete this file once ParaCollection is wired into driver initialization. @@ -44,16 +26,14 @@ ParaKmeshWorld make_kmesh_world() #ifdef __MPI int mpi_initialized = 0; MPI_Initialized(&mpi_initialized); - const int bndpar = bndpar_from_layout(); - if (mpi_initialized && GlobalV::KPAR * bndpar > 1) + // Any distributed layout (k pools or band groups) may need the + // world-wide max/min reductions, so build the MPI domain whenever + // more than one process is running. The sum reduction no-ops for + // kpar <= 1 on its own. + if (mpi_initialized && GlobalV::NPROC > 1) { - // Occupation/energy partial sums are distributed across both k-point - // pools and band groups, so the reduce domain must span all of - // MPI_COMM_WORLD (KP_WORLD alone only links same-band ranks across - // k-point pools and would drop the band-parallel contributions). // Build from globals but skip distribute_kpoints (nkstot=0). - return ParaKmeshWorld(MPI_COMM_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, - 0, 1, bndpar); + return ParaKmeshWorld(MPI_COMM_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, 0, 1); } #endif return ParaKmeshWorld(); @@ -66,15 +46,14 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin) #ifdef __MPI // Fall back to a serial single-pool domain when MPI is not initialized // (e.g. unit tests linked against the MPI-compiled base library) or when - // there is only one distribution pool, so that no MPI call is made on - // an unset communicator. + // there is only one k-pool, so that no MPI call is made on an unset + // communicator. int mpi_initialized = 0; MPI_Initialized(&mpi_initialized); - const int bndpar = bndpar_from_layout(); - if (mpi_initialized && GlobalV::KPAR * bndpar > 1) + if (mpi_initialized && GlobalV::KPAR > 1) { return ParaKmeshWorld(MPI_COMM_WORLD, GlobalV::KPAR, GlobalV::MY_POOL, - nkstot, nspin, bndpar); + nkstot, nspin); } #endif return ParaKmeshWorld(nkstot, nspin); diff --git a/source/source_base/module_parallel/para_kmesh_world.cpp b/source/source_base/module_parallel/para_kmesh_world.cpp index dd42003e3d..8e58dc4f34 100644 --- a/source/source_base/module_parallel/para_kmesh_world.cpp +++ b/source/source_base/module_parallel/para_kmesh_world.cpp @@ -24,23 +24,23 @@ ParaKmeshWorld::ParaKmeshWorld() } #ifdef __MPI -ParaKmeshWorld::ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin, int bndpar) +ParaKmeshWorld::ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin) : ParaWorld("kmesh", comm), kpar_(kpar), my_pool_(my_pool), - rank_in_pool_(rank()), nspin_(nspin), nkstot_(nkstot), - bndpar_(bndpar) + rank_in_pool_(rank()), nspin_(nspin), nkstot_(nkstot) { + // nproc_ must be known before distribute_kpoints(), which derives the + // first rank of every k-pool from it. + nproc_ = size(); distribute_kpoints(); nks_local_ = nks_pool_[my_pool_]; startk_global_ = startk_pool_[my_pool_]; - // comm() spans every process of every distribution pool, so the number - // of processes sharing one partial sum is the pool size. - nproc_ = size(); + kpool_root_ = (rank_in_pool_ == startpro_pool_[my_pool_]); } #endif void ParaKmeshWorld::distribute_kpoints() { - // k-points per pool (evenly divided, remainder to front) + // k-points per k-pool (evenly divided, remainder to front) nks_pool_.resize(kpar_, 0); const int nks_ave = nkstot_ / kpar_; const int nks_rem = nkstot_ % kpar_; @@ -49,14 +49,14 @@ void ParaKmeshWorld::distribute_kpoints() nks_pool_[i] = nks_ave + (i < nks_rem ? 1 : 0); } - // global start index per pool + // global start index per k-pool startk_pool_.resize(kpar_, 0); for (int i = 1; i < kpar_; ++i) { startk_pool_[i] = startk_pool_[i - 1] + nks_pool_[i - 1]; } - // pool index per k-point + // k-pool index per k-point whichpool_.resize(nkstot_, 0); for (int p = 0; p < kpar_; ++p) { @@ -66,7 +66,9 @@ void ParaKmeshWorld::distribute_kpoints() } } - // first world rank per pool + // first communicator rank per k-pool (processes are split into + // consecutive rank blocks, remainder to the front k-pools; this + // mirrors Parallel_Global::divide_mpi_groups) startpro_pool_.resize(kpar_, 0); const int nproc_ave = nproc_ / kpar_; const int nproc_rem = nproc_ % kpar_; @@ -110,22 +112,24 @@ int ParaKmeshWorld::max_nks_pool() const void ParaKmeshWorld::reduce_across_pools(double& value) const { - if (npool() == 1) + if (kpar_ <= 1) { return; } #ifdef __MPI - // Every process in a pool holds the same partial sum, so divide by the - // pool size (nproc/npool) before the world-wide Allreduce. This matches - // the legacy Parallel_Reduce::reduce_double_allpool semantics. - const double swap = value / (nproc_ / npool()); - MPI_Allreduce(&swap, &value, 1, MPI_DOUBLE, MPI_SUM, comm()); + // Exactly one contribution per k-pool: the first process of each + // k-pool injects the partial sum, all other processes inject zero. + // A single world-wide Allreduce therefore returns the sum of the + // per-k-pool partial sums, with no normalization division and with + // uneven k-pool sizes handled naturally. + const double local = kpool_root_ ? value : 0.0; + MPI_Allreduce(&local, &value, 1, MPI_DOUBLE, MPI_SUM, comm()); #endif } void ParaKmeshWorld::reduce_max_across_pools(double& value) const { - if (npool() == 1) + if (nproc_ <= 1) { return; } @@ -136,7 +140,7 @@ void ParaKmeshWorld::reduce_max_across_pools(double& value) const void ParaKmeshWorld::reduce_min_across_pools(double& value) const { - if (npool() == 1) + if (nproc_ <= 1) { return; } diff --git a/source/source_base/module_parallel/para_kmesh_world.h b/source/source_base/module_parallel/para_kmesh_world.h index 724aaddae2..844a8a7d96 100644 --- a/source/source_base/module_parallel/para_kmesh_world.h +++ b/source/source_base/module_parallel/para_kmesh_world.h @@ -10,11 +10,17 @@ namespace Parallel { /** - * @brief k-mesh parallel domain: k-point distribution across pools. + * @brief k-mesh parallel domain: pure k-point pool (k-pool) topology. * * Self-contained replacement for Parallel_Kpoints + KP_WORLD + - * GlobalV::KPAR / MY_POOL / RANK_IN_POOL. Owns all k-point pool - * topology data and provides query / collection operations. + * GlobalV::KPAR / MY_POOL. Owns the k-point pool layout and provides + * query / collection operations. + * + * The k-pool split (kpar) is independent of bndpar: the domain knows + * nothing about band groups. Reductions that must span both dimensions + * (e.g. the total electron count under BPCG) therefore combine + * ParaBgroupWorld::reduce_across_bgroups (band dimension, run FIRST) + * with reduce_across_pools (k dimension, run SECOND). * * In serial builds all operations degenerate to single-pool behavior. * Tests only need this header; no GlobalV, no parallel_comm.h. @@ -32,7 +38,7 @@ class ParaKmeshWorld : public ParaWorld /** * @brief Construct a reduce-only k-mesh domain with no k-point - * distribution data. + * distribution data. * * kpar_/comm are set from the bridge globals so that * reduce_across_pools / reduce_max/min_across_pools work correctly. @@ -45,29 +51,23 @@ class ParaKmeshWorld : public ParaWorld /** * @brief Construct a k-mesh domain on an existing communicator. * - * @param[in] comm communicator spanning every process that holds a - * partial band/k-point sum (MPI_COMM_WORLD in the - * current bridge; must include both k-point pools and - * band groups) - * @param[in] kpar number of k-point pools - * @param[in] my_pool k-point pool index of this process + * @param[in] comm communicator spanning every process of every + * k-pool (MPI_COMM_WORLD in the current bridge) + * @param[in] kpar number of k-pools + * @param[in] my_pool k-pool index of this process * @param[in] nkstot total number of k-points (without spin) * @param[in] nspin number of spin components - * @param[in] bndpar number of band-parallel groups (1 when no band - * parallelization); the reduce_* operations treat - * npool = kpar * bndpar, matching the legacy - * Parallel_Reduce::reduce_double_allpool semantics */ - ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin, int bndpar); + ParaKmeshWorld(const MPI_Comm& comm, int kpar, int my_pool, int nkstot, int nspin); #endif - /// Number of pools. + /// Number of k-pools. int kpar() const { return kpar_; } - /// Pool index of this process. + /// k-pool index of this process. int my_pool() const { return my_pool_; } - /// Rank within the pool. + /// Rank within the communicator (world rank in the bridge layout). int rank_in_pool() const { return rank_in_pool_; } /// Total number of processes. @@ -79,65 +79,74 @@ class ParaKmeshWorld : public ParaWorld /// Total number of k-points (without spin). int nkstot() const { return nkstot_; } - /// Number of k-points in this pool. + /// Number of k-points in this k-pool. int nks_local() const { return nks_local_; } - /// Global start index of this pool's k-points. + /// Global start index of this k-pool's k-points. int startk_global() const { return startk_global_; } - /// Number of k-points in the given pool. + /// Number of k-points in the given k-pool. int nks_pool(int pool) const; - /// Global start index of the given pool's k-points. + /// Global start index of the given k-pool's k-points. int startk_pool(int pool) const; - /// Which pool owns the given global k-point index. + /// Which k-pool owns the given global k-point index. int which_pool(int ik_global) const; - /// First MPI_COMM_WORLD rank of the given pool. + /// First communicator rank of the given k-pool. int startpro_pool(int pool) const; - /// Maximum number of k-points across all pools. + /// Maximum number of k-points across all k-pools. int max_nks_pool() const; + /// Whether this process is the first process of its k-pool. + bool kpool_root() const { return kpool_root_; } + // ===== Cross-pool reductions ===== /** - * @brief Sum a scalar across all k-point pools and band groups. + * @brief Sum a scalar across the k-pools: one contribution per pool. + * + * Replaces Parallel_Reduce::reduce_double_allpool. The first process + * of each k-pool injects the partial sum, all other processes inject + * zero, so a single world-wide MPI_Allreduce yields the sum of the + * per-k-pool partial sums. Correct for uneven k-pool sizes and free + * of the legacy normalization division (which divided by an average + * pool size and double-counted pools of uneven layouts). * - * Replaces Parallel_Reduce::reduce_double_allpool. The communicator - * spans every process of every pool; since all nproc()/npool() - * processes inside a pool share the same partial sum, each value is - * first divided by the pool size before the MPI_Allreduce so that the - * result equals the sum of the per-pool partial sums. No-op when - * npool() == 1. + * Precondition: with band parallelism (bndpar > 1) the caller must + * first combine the band-group partial sums (e.g. + * ParaBgroupWorld::reduce_across_bgroups) so that every process of a + * k-pool holds one complete per-pool partial sum. + * + * No-op when kpar() <= 1. * * @param[in,out] value local partial sum, overwritten with global total */ void reduce_across_pools(double& value) const; /** - * @brief Global max across all k-point pools and band groups. + * @brief Global max across all k-pools and band groups. * - * Replaces Parallel_Reduce::reduce_max (all of MPI_COMM_WORLD): - * band-parallel shards see different eigenvalue windows, so the Fermi - * level must be extremized across both k-point pools and band groups. - * No-op when npool() == 1. + * Max/min are idempotent, so a plain world-wide Allreduce is correct + * for every pool layout and covers both the k-pool and the band-group + * dimension (band-parallel shards see different eigenvalue windows, + * so the Fermi-level bounds must be extremized across both). + * Replaces Parallel_Reduce::reduce_max (all of MPI_COMM_WORLD). + * No-op when this domain spans a single process. * * @param[in,out] value local value, overwritten with global max */ void reduce_max_across_pools(double& value) const; /** - * @brief Global min across all k-point pools and band groups. + * @brief Global min across all k-pools and band groups. * * @param[in,out] value local value, overwritten with global min */ void reduce_min_across_pools(double& value) const; - /// Total number of distribution pools: k-point pools * band groups. - int npool() const { return kpar_ * bndpar_; } - // ===== Cross-domain operations ===== /** @@ -185,12 +194,12 @@ class ParaKmeshWorld : public ParaWorld int nkstot_ = 0; int nks_local_ = 0; int startk_global_ = 0; - int bndpar_ = 1; + bool kpool_root_ = true; ///< first process of my k-pool (reduction contributor) - std::vector nks_pool_; ///< k-points per pool - std::vector startk_pool_; ///< global start index per pool - std::vector whichpool_; ///< pool index per k-point - std::vector startpro_pool_; ///< first world rank per pool + std::vector nks_pool_; ///< k-points per k-pool + std::vector startk_pool_; ///< global start index per k-pool + std::vector whichpool_; ///< k-pool index per k-point + std::vector startpro_pool_; ///< first communicator rank per k-pool }; } // namespace Parallel diff --git a/source/source_base/module_parallel/test/CMakeLists.txt b/source/source_base/module_parallel/test/CMakeLists.txt index aad4e250e6..891d3257cb 100644 --- a/source/source_base/module_parallel/test/CMakeLists.txt +++ b/source/source_base/module_parallel/test/CMakeLists.txt @@ -80,7 +80,7 @@ target_compile_definitions(MODULE_BASE_para_setup_mpi PRIVATE __MPI) # Built with add_executable (not AddTest) so that no direct-run CTest entry is # created; the binary is only exercised through mpirun by the .sh test below, # matching the multi-process requirement of these cases. -add_executable(MODULE_BASE_para_kmesh_world_mpi test_para_kmesh_world_mpi.cpp ../para_kmesh_world.cpp ../para_world.cpp) +add_executable(MODULE_BASE_para_kmesh_world_mpi test_para_kmesh_world_mpi.cpp ../para_bgroup_world.cpp ../para_kmesh_world.cpp ../para_world.cpp) target_link_libraries(MODULE_BASE_para_kmesh_world_mpi PRIVATE MPI::MPI_CXX GTest::gtest GTest::gtest_main abacus::linalg_libs) target_compile_definitions(MODULE_BASE_para_kmesh_world_mpi PRIVATE __MPI) diff --git a/source/source_base/module_parallel/test/para_collection_mpi_test.cpp b/source/source_base/module_parallel/test/para_collection_mpi_test.cpp index 874f56d2c4..6c06c4c4dd 100644 --- a/source/source_base/module_parallel/test/para_collection_mpi_test.cpp +++ b/source/source_base/module_parallel/test/para_collection_mpi_test.cpp @@ -8,7 +8,7 @@ TEST(ParaCollectionMpiTest, AssembleAndFind) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1))); coll.add(Parallel::ParaWorld::make_serial(Parallel::ParaTag::pw)); EXPECT_EQ(coll.size(), 2u); @@ -25,7 +25,7 @@ TEST(ParaCollectionMpiTest, FindMissingReturnsInvalid) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 4, 1))); const Parallel::ParaWorld& missing = coll.find("nonexistent"); EXPECT_FALSE(missing.valid()); @@ -35,7 +35,7 @@ TEST(ParaCollectionMpiTest, FindAsSubclass) { Parallel::ParaCollection coll; coll.add(std::unique_ptr( - new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 8, 1, 1))); + new Parallel::ParaKmeshWorld(MPI_COMM_WORLD, 1, 0, 8, 1))); const Parallel::ParaKmeshWorld* kmesh = coll.find_as(Parallel::ParaTag::kmesh); ASSERT_NE(kmesh, nullptr); diff --git a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp index 0b246ffd75..c1c78ab5bc 100644 --- a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp +++ b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp @@ -1,13 +1,17 @@ #include "gtest/gtest.h" +#include "../para_bgroup_world.h" #include "../para_kmesh_world.h" // Run with: mpirun -np 4 ./MODULE_BASE_para_kmesh_world_mpi // -// Covers the band-parallel reduction path (bndpar > 1) that the legacy -// Parallel_Reduce::reduce_double_allpool provided and that was dropped in the -// first ParaKmeshWorld migration, breaking tests/11_PW_GPU/scf_bpcg -// (kpar=1, bndpar=2). +// The sum reduction protocol has two layers: +// 1. ParaBgroupWorld::reduce_across_bgroups (band dimension, BPCG shards) +// 2. ParaKmeshWorld::reduce_across_pools (k dimension, one +// contribution per k-pool: the first rank of each k-pool injects the +// partial sum, everyone else injects zero) +// The band layer must run first so that the k layer receives one complete +// per-k-pool partial sum. TEST(ParaKmeshWorldMpiTest, ReduceAcrossBandGroupsBndpar2) { @@ -17,17 +21,22 @@ TEST(ParaKmeshWorldMpiTest, ReduceAcrossBandGroupsBndpar2) MPI_Comm_rank(MPI_COMM_WORLD, &myrank); ASSERT_EQ(nprocs, 4); - // kpar=1, bndpar=2, 4 ranks: 2 ranks per band group. Each band group - // holds a partial occupation sum of 14 (28 electrons split in two). - Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1, 2); - EXPECT_EQ(kmesh.npool(), 2); + // kpar=1, bndpar=2, 4 ranks: band group = myrank/2, rank position + // inside the band group = myrank%2. Reproduce the BP_WORLD layout, + // which links the same rank position of every band group. + MPI_Comm bp_world = MPI_COMM_NULL; + MPI_Comm_split(MPI_COMM_WORLD, myrank % 2, myrank / 2, &bp_world); + Parallel::ParaBgroupWorld bgroup(MPI_COMM_WORLD, bp_world, 2); + // Each band group holds a partial occupation sum of 14 (28 electrons + // split into two band windows). double sumk = 14.0; - kmesh.reduce_across_pools(sumk); + bgroup.reduce_across_bgroups(sumk); EXPECT_DOUBLE_EQ(sumk, 28.0); - // max/min must span the band groups as well: the two shards see - // different eigenvalue windows. + // max/min stay world-wide (idempotent) and must span the band groups + // as well: the two shards see different eigenvalue windows. + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1); double eup = (myrank < 2) ? 40.0 : 45.0; kmesh.reduce_max_across_pools(eup); EXPECT_DOUBLE_EQ(eup, 45.0); @@ -35,32 +44,78 @@ TEST(ParaKmeshWorldMpiTest, ReduceAcrossBandGroupsBndpar2) double elw = (myrank < 2) ? -1.0 : -5.0; kmesh.reduce_min_across_pools(elw); EXPECT_DOUBLE_EQ(elw, -5.0); + + MPI_Comm_free(&bp_world); } TEST(ParaKmeshWorldMpiTest, ReduceAcrossKpoolsKpar2) { int nprocs = 0; + int myrank = 0; MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); ASSERT_EQ(nprocs, 4); - // kpar=2, bndpar=1: 2 k-point pools of 2 ranks each. - const int my_pool = (nprocs > 1) ? 0 : 0; // distribution detail unused here - Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 2, my_pool, 4, 1, 1); - EXPECT_EQ(kmesh.npool(), 2); + // kpar=2, bndpar=1: k-pool 0 = ranks {0,1}, k-pool 1 = ranks {2,3} + // (consecutive rank blocks, divide_mpi_groups layout). + const int my_pool = myrank / 2; + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 2, my_pool, 4, 1); + EXPECT_EQ(kmesh.startpro_pool(0), 0); + EXPECT_EQ(kmesh.startpro_pool(1), 2); + EXPECT_EQ(kmesh.kpool_root(), (myrank % 2 == 0)); + // Every process holds its k-pool's partial sum; the reduction must + // count each pool exactly once. double sumk = 3.5; kmesh.reduce_across_pools(sumk); EXPECT_DOUBLE_EQ(sumk, 7.0); } +TEST(ParaKmeshWorldMpiTest, UnevenKpoolsKpar3) +{ + int nprocs = 0; + int myrank = 0; + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); + ASSERT_EQ(nprocs, 4); + + // nproc=4, kpar=3 (the 007_PW_UPF201_USPP_Fe layout): k-pool sizes + // are [2,1,1]. divide_mpi_groups puts ranks {0,1} in pool 0, rank 2 + // in pool 1 and rank 3 in pool 2. + int my_pool = 0; + if (myrank >= 3) + { + my_pool = 2; + } + else if (myrank >= 2) + { + my_pool = 1; + } + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 3, my_pool, 0, 1); + EXPECT_EQ(kmesh.startpro_pool(0), 0); + EXPECT_EQ(kmesh.startpro_pool(1), 2); + EXPECT_EQ(kmesh.startpro_pool(2), 3); + EXPECT_EQ(kmesh.kpool_root(), (myrank != 1)); + + // Every process holds its k-pool's partial sum. The reduction must + // count each pool exactly once even though the pools are uneven: + // the legacy average-pool-size division (4/3 = 1) double-counted + // pool 0 here and corrupted the electron count / Fermi level. + const double pool_sum = (my_pool == 0) ? 10.0 : ((my_pool == 1) ? 20.0 : 30.0); + double sumk = pool_sum; + kmesh.reduce_across_pools(sumk); + EXPECT_DOUBLE_EQ(sumk, 60.0); +} + TEST(ParaKmeshWorldMpiTest, SinglePoolIsNoOp) { int nprocs = 0; MPI_Comm_size(MPI_COMM_WORLD, &nprocs); ASSERT_EQ(nprocs, 4); - // npool == 1: reduction must be a no-op regardless of the world size. - Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1, 1); + // kpar == 1: the sum reduction must be a no-op regardless of the + // world size (the band dimension is handled by ParaBgroupWorld). + Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1); double sumk = 42.0; kmesh.reduce_across_pools(sumk); EXPECT_DOUBLE_EQ(sumk, 42.0); diff --git a/source/source_estate/elecstate_tools.cpp b/source/source_estate/elecstate_tools.cpp index 452334e25c..1efa988356 100644 --- a/source/source_estate/elecstate_tools.cpp +++ b/source/source_estate/elecstate_tools.cpp @@ -74,9 +74,15 @@ void calEBand(const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, fener } f_en.eband = eband; - // Combine contributions distributed across k-point pools. - // Reduce-only kmesh: no k-point distribution data needed. + // Two-step reduction, order matters: + // 1. Band dimension: BPCG shards the band range across the band + // groups of this k-pool, so combine the per-window partial sums + // first (no-op with a single band group). + // 2. k dimension: exactly one contribution per k-pool. + // Reduce-only domains: no k-point distribution data needed. Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(); + Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); + bgroup.reduce_across_bgroups(f_en.eband); kmesh.reduce_across_pools(f_en.eband); return; } @@ -182,6 +188,10 @@ void calculate_weights(const ModuleBase::matrix& ekb, kmesh); } // demet is accumulated independently on every k-point and band partition. + // Band dimension first (BPCG band shards), then one contribution + // per k-pool; see calEBand for the ordering rationale. + Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); + bgroup.reduce_across_bgroups(f_en.demet); kmesh.reduce_across_pools(f_en.demet); } else if (Occupy::fixed_occupations) diff --git a/source/source_estate/occupy.cpp b/source/source_estate/occupy.cpp index e0f94d253c..020b06f460 100644 --- a/source/source_estate/occupy.cpp +++ b/source/source_estate/occupy.cpp @@ -2,6 +2,7 @@ #include "source_base/constants.h" #include "source_base/mymath.h" +#include "source_base/module_parallel/para_bridge.h" #include "source_base/module_parallel/para_kmesh_world.h" Occupy::Occupy() @@ -431,6 +432,13 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, sum2 += wk[ik] * sum1; } + // Two-step reduction, order matters: + // 1. Band dimension: BPCG shards the band range across the band + // groups of this k-pool, so combine the per-window partial sums + // first (no-op with a single band group). + Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); + bgroup.reduce_across_bgroups(sum2); + // 2. k dimension: exactly one contribution per k-pool. kmesh.reduce_across_pools(sum2); return sum2; From ec6f1759e656c0307d75808cbaa94aff8ca6ab52 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Sat, 5 Sep 2026 19:38:30 +0800 Subject: [PATCH 13/13] refactor(parallel): rename bgroup domain to bdiff_ksame in module_parallel The bgroup name is misleading: it does not say which two axes the domain connects. Rename the new module_parallel band-group domain after its tag bdiff_ksame (different band groups, same k), matching ParaTag: para_bgroup_world.{h,cpp} -> para_bdiff_ksame_world.{h,cpp} ParaBgroupWorld -> ParaBdiffKsameWorld make_bgroup_world() -> make_bdiff_ksame_world() reduce_across_bgroups() -> reduce_across_bdiff_ksame() Legacy globals INT_BGROUP / BP_WORLD and the GlobalV::MY_BNDGROUP-style member names are the old parallel-layer API and are left unchanged; the new domain is only a self-contained bridge over them. --- source/Makefile.Objects | 2 +- source/source_base/CMakeLists.txt | 2 +- ...p_world.cpp => para_bdiff_ksame_world.cpp} | 8 ++--- ...group_world.h => para_bdiff_ksame_world.h} | 35 ++++++++++--------- .../module_parallel/para_bridge.cpp | 10 +++--- .../source_base/module_parallel/para_bridge.h | 9 ++--- .../module_parallel/para_kmesh_world.h | 4 +-- .../module_parallel/test/CMakeLists.txt | 6 ++-- ...st.cpp => para_bdiff_ksame_world_test.cpp} | 10 +++--- .../test/test_para_kmesh_world_mpi.cpp | 10 +++--- source/source_estate/elecstate_tools.cpp | 8 ++--- source/source_estate/occupy.cpp | 4 +-- 12 files changed, 55 insertions(+), 53 deletions(-) rename source/source_base/module_parallel/{para_bgroup_world.cpp => para_bdiff_ksame_world.cpp} (66%) rename source/source_base/module_parallel/{para_bgroup_world.h => para_bdiff_ksame_world.h} (63%) rename source/source_base/module_parallel/test/{para_bgroup_world_test.cpp => para_bdiff_ksame_world_test.cpp} (63%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 065ae093c1..51735fe0d6 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -797,7 +797,7 @@ OBJS_PARALLEL=parallel_common.o\ para_pw_world.o\ para_diag_world.o\ para_rgrid_world.o\ - para_bgroup_world.o\ + para_bdiff_ksame_world.o\ para_matrix_world.o\ para_mpi_func.o\ para_setup.o\ diff --git a/source/source_base/CMakeLists.txt b/source/source_base/CMakeLists.txt index e2ccaaf173..79bf625ae4 100644 --- a/source/source_base/CMakeLists.txt +++ b/source/source_base/CMakeLists.txt @@ -79,7 +79,7 @@ add_library( module_parallel/para_pw_world.cpp module_parallel/para_diag_world.cpp module_parallel/para_rgrid_world.cpp - module_parallel/para_bgroup_world.cpp + module_parallel/para_bdiff_ksame_world.cpp module_parallel/para_matrix_world.cpp module_parallel/para_mpi_func.cpp module_parallel/para_setup.cpp diff --git a/source/source_base/module_parallel/para_bgroup_world.cpp b/source/source_base/module_parallel/para_bdiff_ksame_world.cpp similarity index 66% rename from source/source_base/module_parallel/para_bgroup_world.cpp rename to source/source_base/module_parallel/para_bdiff_ksame_world.cpp index f84659f745..147ae3f347 100644 --- a/source/source_base/module_parallel/para_bgroup_world.cpp +++ b/source/source_base/module_parallel/para_bdiff_ksame_world.cpp @@ -1,15 +1,15 @@ -#include "para_bgroup_world.h" +#include "para_bdiff_ksame_world.h" namespace Parallel { -ParaBgroupWorld::ParaBgroupWorld() +ParaBdiffKsameWorld::ParaBdiffKsameWorld() : ParaWorld("bdiff_ksame"), my_bndgroup_(0), nbndgroup_(1) { } #ifdef __MPI -ParaBgroupWorld::ParaBgroupWorld(const MPI_Comm& intra_comm, const MPI_Comm& inter_comm, int nbndgroup) +ParaBdiffKsameWorld::ParaBdiffKsameWorld(const MPI_Comm& intra_comm, const MPI_Comm& inter_comm, int nbndgroup) : ParaWorld("bdiff_ksame", intra_comm), inter_comm_(inter_comm), nbndgroup_(nbndgroup) { if (inter_comm != MPI_COMM_NULL) @@ -19,7 +19,7 @@ ParaBgroupWorld::ParaBgroupWorld(const MPI_Comm& intra_comm, const MPI_Comm& int } #endif -void ParaBgroupWorld::reduce_across_bgroups(double& value) const +void ParaBdiffKsameWorld::reduce_across_bdiff_ksame(double& value) const { #ifdef __MPI if (inter_comm_ == MPI_COMM_NULL || nbndgroup_ <= 1) diff --git a/source/source_base/module_parallel/para_bgroup_world.h b/source/source_base/module_parallel/para_bdiff_ksame_world.h similarity index 63% rename from source/source_base/module_parallel/para_bgroup_world.h rename to source/source_base/module_parallel/para_bdiff_ksame_world.h index 4e108f83ba..2584a49153 100644 --- a/source/source_base/module_parallel/para_bgroup_world.h +++ b/source/source_base/module_parallel/para_bdiff_ksame_world.h @@ -1,5 +1,5 @@ -#ifndef PARA_BGROUP_WORLD_H -#define PARA_BGROUP_WORLD_H +#ifndef PARA_BDIFF_KSAME_WORLD_H +#define PARA_BDIFF_KSAME_WORLD_H #include "para_world.h" @@ -7,34 +7,35 @@ namespace Parallel { /** - * @brief bgroup parallel domain: band group communication topology. + * @brief bdiff_ksame parallel domain: band-group communication topology + * inside one k-pool. * * Self-contained replacement for INT_BGROUP + BP_WORLD + * GlobalV::MY_BNDGROUP/NPROC_IN_BNDGROUP/RANK_IN_BPGROUP. * - * The band group domain has two communicators: - * - intra: INT_BGROUP (same band group, different k/pw) - * - inter: BP_WORLD (different band groups, same k) + * The domain has two communicators: + * - intra: INT_BGROUP (bsame_kdiff; same band group, different k/pw) + * - inter: BP_WORLD (bdiff_ksame; different band groups, same k) * * Tests only need this header. */ -class ParaBgroupWorld : public ParaWorld +class ParaBdiffKsameWorld : public ParaWorld { public: /** - * @brief Construct a serial bgroup domain (single band group). + * @brief Construct a serial domain (single band group). */ - ParaBgroupWorld(); + ParaBdiffKsameWorld(); #ifdef __MPI /** - * @brief Construct a bgroup domain from intra and inter communicators. + * @brief Construct a domain from intra and inter communicators. * * @param[in] intra_comm intra-group communicator (e.g. INT_BGROUP) * @param[in] inter_comm inter-group communicator (e.g. BP_WORLD) * @param[in] nbndgroup number of band groups */ - ParaBgroupWorld(const MPI_Comm& intra_comm, const MPI_Comm& inter_comm, int nbndgroup); + ParaBdiffKsameWorld(const MPI_Comm& intra_comm, const MPI_Comm& inter_comm, int nbndgroup); #endif /// Band group index of this process. @@ -50,7 +51,7 @@ class ParaBgroupWorld : public ParaWorld int nproc_in_bndgroup() const { return size(); } #ifdef __MPI - /// Inter-group communicator (BP_WORLD equivalent). + /// Inter-group communicator (BP_WORLD / bdiff_ksame equivalent). MPI_Comm inter_comm() const { return inter_comm_; } #endif @@ -60,9 +61,9 @@ class ParaBgroupWorld : public ParaWorld * Band-parallel eigensolvers (bpcg) shard the band range across the * BNDPAR band groups of a k-pool: every process only accumulates the * partial sum over its own band window. This reduction combines those - * partial sums on BP_WORLD (bdiff_ksame), which links the same rank - * position of every band group inside one k-pool, so each band window - * contributes exactly once. + * partial sums on the bdiff_ksame (BP_WORLD) communicator, which links + * the same rank position of every band group inside one k-pool, so each + * band window contributes exactly once. * * It must run BEFORE ParaKmeshWorld::reduce_across_pools so that the * k-pool reduction receives one complete per-k-pool partial sum. @@ -71,7 +72,7 @@ class ParaBgroupWorld : public ParaWorld * @param[in,out] value local partial sum, overwritten with the * k-pool-wide total */ - void reduce_across_bgroups(double& value) const; + void reduce_across_bdiff_ksame(double& value) const; private: int my_bndgroup_ = 0; @@ -83,4 +84,4 @@ class ParaBgroupWorld : public ParaWorld } // namespace Parallel -#endif // PARA_BGROUP_WORLD_H +#endif // PARA_BDIFF_KSAME_WORLD_H diff --git a/source/source_base/module_parallel/para_bridge.cpp b/source/source_base/module_parallel/para_bridge.cpp index 022037f3aa..3736920236 100644 --- a/source/source_base/module_parallel/para_bridge.cpp +++ b/source/source_base/module_parallel/para_bridge.cpp @@ -59,9 +59,9 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin) return ParaKmeshWorld(nkstot, nspin); } -// Temporary bridge: construct a bgroup-domain ParaBgroupWorld from the old -// globals. Delete this file once ParaCollection is wired into driver init. -ParaBgroupWorld make_bgroup_world() +// Temporary bridge: construct a bdiff_ksame-domain ParaBdiffKsameWorld from +// the old globals. Delete this file once ParaCollection is wired into driver init. +ParaBdiffKsameWorld make_bdiff_ksame_world() { #ifdef __MPI int mpi_initialized = 0; @@ -73,10 +73,10 @@ ParaBgroupWorld make_bgroup_world() { int nbndgroup = 1; MPI_Comm_size(BP_WORLD, &nbndgroup); - return ParaBgroupWorld(INT_BGROUP, BP_WORLD, nbndgroup); + return ParaBdiffKsameWorld(INT_BGROUP, BP_WORLD, nbndgroup); } #endif - return ParaBgroupWorld(); + return ParaBdiffKsameWorld(); } } // namespace Parallel diff --git a/source/source_base/module_parallel/para_bridge.h b/source/source_base/module_parallel/para_bridge.h index 7e6d0e3edf..73319de8f5 100644 --- a/source/source_base/module_parallel/para_bridge.h +++ b/source/source_base/module_parallel/para_bridge.h @@ -1,7 +1,7 @@ #ifndef PARA_BRIDGE_H #define PARA_BRIDGE_H -#include "para_bgroup_world.h" +#include "para_bdiff_ksame_world.h" #include "para_kmesh_world.h" #include "para_world.h" @@ -45,14 +45,15 @@ ParaKmeshWorld make_kmesh_world(int nkstot, int nspin); ParaKmeshWorld make_kmesh_world(); /** - * @brief Temporary bridge: construct a bgroup-domain ParaBgroupWorld from - * the old globals INT_BGROUP / BP_WORLD (MPI) or as a serial domain. + * @brief Temporary bridge: construct a bdiff_ksame-domain + * ParaBdiffKsameWorld from the old globals INT_BGROUP / BP_WORLD (MPI) or + * as a serial domain. * * Falls back to a serial single-band-group domain when MPI is not * initialized or the pool layout has not been set up yet (e.g. unit * tests), so that no MPI call is made on an unset communicator. */ -ParaBgroupWorld make_bgroup_world(); +ParaBdiffKsameWorld make_bdiff_ksame_world(); } // namespace Parallel diff --git a/source/source_base/module_parallel/para_kmesh_world.h b/source/source_base/module_parallel/para_kmesh_world.h index 844a8a7d96..e65f7d0eba 100644 --- a/source/source_base/module_parallel/para_kmesh_world.h +++ b/source/source_base/module_parallel/para_kmesh_world.h @@ -19,7 +19,7 @@ namespace Parallel * The k-pool split (kpar) is independent of bndpar: the domain knows * nothing about band groups. Reductions that must span both dimensions * (e.g. the total electron count under BPCG) therefore combine - * ParaBgroupWorld::reduce_across_bgroups (band dimension, run FIRST) + * ParaBdiffKsameWorld::reduce_across_bdiff_ksame (band dimension, run FIRST) * with reduce_across_pools (k dimension, run SECOND). * * In serial builds all operations degenerate to single-pool behavior. @@ -117,7 +117,7 @@ class ParaKmeshWorld : public ParaWorld * * Precondition: with band parallelism (bndpar > 1) the caller must * first combine the band-group partial sums (e.g. - * ParaBgroupWorld::reduce_across_bgroups) so that every process of a + * ParaBdiffKsameWorld::reduce_across_bdiff_ksame) so that every process of a * k-pool holds one complete per-pool partial sum. * * No-op when kpar() <= 1. diff --git a/source/source_base/module_parallel/test/CMakeLists.txt b/source/source_base/module_parallel/test/CMakeLists.txt index 891d3257cb..f0dc517419 100644 --- a/source/source_base/module_parallel/test/CMakeLists.txt +++ b/source/source_base/module_parallel/test/CMakeLists.txt @@ -30,8 +30,8 @@ AddTest( ) AddTest( - TARGET MODULE_BASE_para_bgroup_world - SOURCES para_bgroup_world_test.cpp ../para_bgroup_world.cpp ../para_world.cpp + TARGET MODULE_BASE_para_bdiff_ksame_world + SOURCES para_bdiff_ksame_world_test.cpp ../para_bdiff_ksame_world.cpp ../para_world.cpp ) AddTest( @@ -80,7 +80,7 @@ target_compile_definitions(MODULE_BASE_para_setup_mpi PRIVATE __MPI) # Built with add_executable (not AddTest) so that no direct-run CTest entry is # created; the binary is only exercised through mpirun by the .sh test below, # matching the multi-process requirement of these cases. -add_executable(MODULE_BASE_para_kmesh_world_mpi test_para_kmesh_world_mpi.cpp ../para_bgroup_world.cpp ../para_kmesh_world.cpp ../para_world.cpp) +add_executable(MODULE_BASE_para_kmesh_world_mpi test_para_kmesh_world_mpi.cpp ../para_bdiff_ksame_world.cpp ../para_kmesh_world.cpp ../para_world.cpp) target_link_libraries(MODULE_BASE_para_kmesh_world_mpi PRIVATE MPI::MPI_CXX GTest::gtest GTest::gtest_main abacus::linalg_libs) target_compile_definitions(MODULE_BASE_para_kmesh_world_mpi PRIVATE __MPI) diff --git a/source/source_base/module_parallel/test/para_bgroup_world_test.cpp b/source/source_base/module_parallel/test/para_bdiff_ksame_world_test.cpp similarity index 63% rename from source/source_base/module_parallel/test/para_bgroup_world_test.cpp rename to source/source_base/module_parallel/test/para_bdiff_ksame_world_test.cpp index 8dceccf4ad..4fde3d5c6c 100644 --- a/source/source_base/module_parallel/test/para_bgroup_world_test.cpp +++ b/source/source_base/module_parallel/test/para_bdiff_ksame_world_test.cpp @@ -1,10 +1,10 @@ #include "gtest/gtest.h" -#include "../para_bgroup_world.h" +#include "../para_bdiff_ksame_world.h" -TEST(ParaBgroupWorldTest, SerialMode) +TEST(ParaBdiffKsameWorldTest, SerialMode) { - const Parallel::ParaBgroupWorld world; + const Parallel::ParaBdiffKsameWorld world; EXPECT_EQ(world.tag(), "bdiff_ksame"); EXPECT_EQ(world.my_bndgroup(), 0); EXPECT_EQ(world.nbndgroup(), 1); @@ -13,9 +13,9 @@ TEST(ParaBgroupWorldTest, SerialMode) EXPECT_TRUE(world.valid()); } -TEST(ParaBgroupWorldTest, AliasesMatchBase) +TEST(ParaBdiffKsameWorldTest, AliasesMatchBase) { - const Parallel::ParaBgroupWorld world; + const Parallel::ParaBdiffKsameWorld world; EXPECT_EQ(world.rank_in_bpgroup(), world.rank()); EXPECT_EQ(world.nproc_in_bndgroup(), world.size()); } diff --git a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp index c1c78ab5bc..20258140e0 100644 --- a/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp +++ b/source/source_base/module_parallel/test/test_para_kmesh_world_mpi.cpp @@ -1,12 +1,12 @@ #include "gtest/gtest.h" -#include "../para_bgroup_world.h" +#include "../para_bdiff_ksame_world.h" #include "../para_kmesh_world.h" // Run with: mpirun -np 4 ./MODULE_BASE_para_kmesh_world_mpi // // The sum reduction protocol has two layers: -// 1. ParaBgroupWorld::reduce_across_bgroups (band dimension, BPCG shards) +// 1. ParaBdiffKsameWorld::reduce_across_bdiff_ksame (band dimension, BPCG shards) // 2. ParaKmeshWorld::reduce_across_pools (k dimension, one // contribution per k-pool: the first rank of each k-pool injects the // partial sum, everyone else injects zero) @@ -26,12 +26,12 @@ TEST(ParaKmeshWorldMpiTest, ReduceAcrossBandGroupsBndpar2) // which links the same rank position of every band group. MPI_Comm bp_world = MPI_COMM_NULL; MPI_Comm_split(MPI_COMM_WORLD, myrank % 2, myrank / 2, &bp_world); - Parallel::ParaBgroupWorld bgroup(MPI_COMM_WORLD, bp_world, 2); + Parallel::ParaBdiffKsameWorld bdiff(MPI_COMM_WORLD, bp_world, 2); // Each band group holds a partial occupation sum of 14 (28 electrons // split into two band windows). double sumk = 14.0; - bgroup.reduce_across_bgroups(sumk); + bdiff.reduce_across_bdiff_ksame(sumk); EXPECT_DOUBLE_EQ(sumk, 28.0); // max/min stay world-wide (idempotent) and must span the band groups @@ -114,7 +114,7 @@ TEST(ParaKmeshWorldMpiTest, SinglePoolIsNoOp) ASSERT_EQ(nprocs, 4); // kpar == 1: the sum reduction must be a no-op regardless of the - // world size (the band dimension is handled by ParaBgroupWorld). + // world size (the band dimension is handled by ParaBdiffKsameWorld). Parallel::ParaKmeshWorld kmesh(MPI_COMM_WORLD, 1, 0, 0, 1); double sumk = 42.0; kmesh.reduce_across_pools(sumk); diff --git a/source/source_estate/elecstate_tools.cpp b/source/source_estate/elecstate_tools.cpp index 1efa988356..3f9147d29e 100644 --- a/source/source_estate/elecstate_tools.cpp +++ b/source/source_estate/elecstate_tools.cpp @@ -81,8 +81,8 @@ void calEBand(const ModuleBase::matrix& ekb, const ModuleBase::matrix& wg, fener // 2. k dimension: exactly one contribution per k-pool. // Reduce-only domains: no k-point distribution data needed. Parallel::ParaKmeshWorld kmesh = Parallel::make_kmesh_world(); - Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); - bgroup.reduce_across_bgroups(f_en.eband); + Parallel::ParaBdiffKsameWorld bdiff = Parallel::make_bdiff_ksame_world(); + bdiff.reduce_across_bdiff_ksame(f_en.eband); kmesh.reduce_across_pools(f_en.eband); return; } @@ -190,8 +190,8 @@ void calculate_weights(const ModuleBase::matrix& ekb, // demet is accumulated independently on every k-point and band partition. // Band dimension first (BPCG band shards), then one contribution // per k-pool; see calEBand for the ordering rationale. - Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); - bgroup.reduce_across_bgroups(f_en.demet); + Parallel::ParaBdiffKsameWorld bdiff = Parallel::make_bdiff_ksame_world(); + bdiff.reduce_across_bdiff_ksame(f_en.demet); kmesh.reduce_across_pools(f_en.demet); } else if (Occupy::fixed_occupations) diff --git a/source/source_estate/occupy.cpp b/source/source_estate/occupy.cpp index 020b06f460..9699d407a7 100644 --- a/source/source_estate/occupy.cpp +++ b/source/source_estate/occupy.cpp @@ -436,8 +436,8 @@ double Occupy::sumkg(const ModuleBase::matrix& ekb, // 1. Band dimension: BPCG shards the band range across the band // groups of this k-pool, so combine the per-window partial sums // first (no-op with a single band group). - Parallel::ParaBgroupWorld bgroup = Parallel::make_bgroup_world(); - bgroup.reduce_across_bgroups(sum2); + Parallel::ParaBdiffKsameWorld bdiff = Parallel::make_bdiff_ksame_world(); + bdiff.reduce_across_bdiff_ksame(sum2); // 2. k dimension: exactly one contribution per k-pool. kmesh.reduce_across_pools(sum2);