From 83eb5d0f3fc8857888f3c4a32f76cea572e2c7cc Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 09:02:23 +0800 Subject: [PATCH 01/37] module_charge: normalize indentation and brace single-statement control flow Mechanical cleanup as the first step of the module_charge governance refactor: convert leading tabs to 4-space indentation (1011 occurrences across 11 files) and add braces around all single-statement if/for/while bodies (11 sites). No functional change. --- source/source_estate/module_charge/charge.cpp | 70 +-- source/source_estate/module_charge/charge.h | 18 +- .../module_charge/charge_extra.cpp | 5 +- .../module_charge/charge_init.cpp | 96 +-- .../module_charge/charge_mixing.cpp | 18 +- .../module_charge/charge_mixing_rho.cpp | 58 +- .../source_estate/module_charge/chgmixing.cpp | 44 +- .../source_estate/module_charge/chgmixing.h | 8 +- .../module_charge/mix_precond.cpp | 30 +- .../source_estate/module_charge/mix_resid.cpp | 20 +- .../source_estate/module_charge/symm_rho.cpp | 27 +- .../source_estate/module_charge/symm_rhog.cpp | 590 +++++++++--------- 12 files changed, 506 insertions(+), 478 deletions(-) diff --git a/source/source_estate/module_charge/charge.cpp b/source/source_estate/module_charge/charge.cpp index 5f25aefe06f..e4c1680cb55 100644 --- a/source/source_estate/module_charge/charge.cpp +++ b/source/source_estate/module_charge/charge.cpp @@ -52,14 +52,14 @@ void Charge::set_rhopw(ModulePW::PW_Basis* rhopw_in) // mohan add 2025-12-02 bool Charge::kin_density() const { - if (XC_Functional::get_ked_flag() || PARAM.inp.out_elf[0] > 0) - { - return true; - } - else - { - return false; - } + if (XC_Functional::get_ked_flag() || PARAM.inp.out_elf[0] > 0) + { + return true; + } + else + { + return false; + } } void Charge::destroy() @@ -90,10 +90,10 @@ void Charge::allocate(const int& nspin_in, const bool kin_den) { ModuleBase::TITLE("Charge", "allocate"); - if (this->rhopw == nullptr) - { - ModuleBase::WARNING_QUIT("Charge::allocate","rhopw is nullptr."); - } + if (this->rhopw == nullptr) + { + ModuleBase::WARNING_QUIT("Charge::allocate","rhopw is nullptr."); + } this->nrxx = this->rhopw->nrxx; this->nxyz = this->rhopw->nxyz; @@ -248,10 +248,10 @@ void Charge::atomic_rho(const int spin_number_need, ModuleBase::timer::start("Charge", "atomic_rho"); { - ModuleBase::ComplexMatrix rho_g3d = [&]() -> ModuleBase::ComplexMatrix - { - // use interpolation to get three dimension charge density. - ModuleBase::ComplexMatrix rho_g3d(spin_number_need, this->rhopw->npw); + ModuleBase::ComplexMatrix rho_g3d = [&]() -> ModuleBase::ComplexMatrix + { + // use interpolation to get three dimension charge density. + ModuleBase::ComplexMatrix rho_g3d(spin_number_need, this->rhopw->npw); for (int it = 0; it < ucell.ntype; it++) { @@ -410,10 +410,10 @@ void Charge::atomic_rho(const int spin_number_need, #ifdef _OPENMP #pragma omp for #endif - for (int igg = 0; igg < ngg; igg++) - { - rho_lgl[igg] /= omega; - } + for (int igg = 0; igg < ngg; igg++) + { + rho_lgl[igg] /= omega; + } #ifdef _OPENMP } #endif @@ -583,12 +583,12 @@ void Charge::atomic_rho(const int spin_number_need, { this->rhopw->recip2real(&rho_g3d(is, 0), rho_in[is]); - for (int ir = 0; ir < this->rhopw->nrxx; ++ir) - { - ne[is] += rho_in[is][ir]; - } + for (int ir = 0; ir < this->rhopw->nrxx; ++ir) + { + ne[is] += rho_in[is][ir]; + } - ne[is] *= omega / (double)this->rhopw->nxyz; + ne[is] *= omega / (double)this->rhopw->nxyz; #ifdef __MPI Parallel_Reduce::reduce_pool(ne[is]); #endif @@ -625,10 +625,10 @@ void Charge::atomic_rho(const int spin_number_need, double ne_tot = 0.0; int spin0 = 1; - if (spin_number_need == 2) - { - spin0 = spin_number_need; - } + if (spin_number_need == 2) + { + spin0 = spin_number_need; + } for (int is = 0; is < spin0; ++is) { GlobalV::ofs_warning << "\n SETUP ATOMIC RHO FOR SPIN " << is + 1 << std::endl; @@ -639,12 +639,12 @@ void Charge::atomic_rho(const int spin_number_need, ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "should be", PARAM.inp.nelec); for (int is = 0; is < spin_number_need; ++is) - { - for (int ir = 0; ir < this->rhopw->nrxx; ++ir) - { - rho_in[is][ir] = rho_in[is][ir] / ne_tot * PARAM.inp.nelec; - } - } + { + for (int ir = 0; ir < this->rhopw->nrxx; ++ir) + { + rho_in[is][ir] = rho_in[is][ir] / ne_tot * PARAM.inp.nelec; + } + } } ModuleBase::timer::end("Charge", "atomic_rho"); diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 785985da40e..5c14cd1c50b 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -104,7 +104,7 @@ class Charge void save_rho_before_sum_band(); - // for non-linear core correction + // for non-linear core correction void non_linear_core_correction ( const bool &numeric, @@ -117,13 +117,13 @@ class Charge double *rhocg ) const; - double cal_rho2ne(const double *rho_in) const; + double cal_rho2ne(const double *rho_in) const; void check_rho(); // to check whether the charge density is normal void init_final_scf(); //LiuXh add 20180619 - public: + public: /** * @brief Sum rho at different pools (k-point parallelism). * Only used when GlobalV::KPAR > 1 @@ -136,14 +136,14 @@ class Charge */ void kin_r_mpi(); - /** - * @brief Reduce among different pools + /** + * @brief Reduce among different pools * If NPROC_IN_POOLs are all the same, use GlobalV::KP_WORLD * else, gather rho in a POOL, and then reduce among different POOLs - * - * @param array_rho f(rho): an array [nrxx] - */ - void reduce_diff_pools(double* array_rho) const; + * + * @param array_rho f(rho): an array [nrxx] + */ + void reduce_diff_pools(double* array_rho) const; void set_omega(double* omega_in){this->omega_ = omega_in;}; diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/charge_extra.cpp index 7513469a31b..f77dde355e7 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/charge_extra.cpp @@ -200,7 +200,10 @@ void Charge_Extra::extrapolate_charge( void Charge_Extra::find_alpha_and_beta(const int& natom, std::ofstream& ofs_running, std::ofstream& ofs_warning) { - if(istep < 3) return; + if(istep < 3) + { + return; + } double a11 = 0.0; double a12 = 0.0; diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index 672d800f718..0552c17b5a3 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -67,16 +67,16 @@ void Charge::init_rho(const UnitCell& ucell, { for (int is = 0; is < nspin; ++is) { - std::stringstream ssc; + std::stringstream ssc; - if(nspin==1) - { + if(nspin==1) + { ssc << PARAM.globalv.global_readin_dir << "chg.cube"; - } - else - { - ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; - } + } + else + { + ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; + } if (ModuleIO::read_vdata_palgrid(pgrid, @@ -264,12 +264,12 @@ void Charge::init_rho(const UnitCell& ucell, const ModulePW::PW_Basis_K* pw_wfc = reinterpret_cast(const_cast(wfcpw)); const K_Vectors* kv = reinterpret_cast(klist); - ModuleIO::read_wf2rho_pw(pw_wfc, symm, *this, + ModuleIO::read_wf2rho_pw(pw_wfc, symm, *this, PARAM.globalv.global_readin_dir, - GlobalV::KPAR, GlobalV::MY_POOL, GlobalV::MY_RANK, + GlobalV::KPAR, GlobalV::MY_POOL, GlobalV::MY_RANK, GlobalV::NPROC_IN_POOL, GlobalV::RANK_IN_POOL, - PARAM.inp.nbands, nspin, PARAM.globalv.npol, - kv->get_nkstot(),kv->ik2iktot,kv->isk,GlobalV::ofs_running); + PARAM.inp.nbands, nspin, PARAM.globalv.npol, + kv->get_nkstot(),kv->ik2iktot,kv->isk,GlobalV::ofs_running); } } @@ -296,14 +296,14 @@ void Charge::set_rho_core(const UnitCell& ucell, if (!bl) { ModuleBase::GlobalFunc::ZEROS( this->rho_core, this->rhopw->nrxx); - ModuleBase::timer::end("Charge","set_rho_core"); + ModuleBase::timer::end("Charge","set_rho_core"); return; } double *rhocg = new double[this->rhopw->ngg]; ModuleBase::GlobalFunc::ZEROS(rhocg, this->rhopw->ngg ); - // three dimension. + // three dimension. std::complex *vg = new std::complex[this->rhopw->npw]; for (int it = 0; it < ucell.ntype;it++) @@ -333,11 +333,11 @@ void Charge::set_rho_core(const UnitCell& ucell, } } - // for tmp use. - for(int ig=0; ig< this->rhopw->npw; ig++) - { - this->rhog_core[ig] = vg[ig]; - } + // for tmp use. + for(int ig=0; ig< this->rhopw->npw; ig++) + { + this->rhog_core[ig] = vg[ig]; + } this->rhopw->recip2real(vg, this->rho_core); @@ -360,12 +360,12 @@ void Charge::set_rho_core(const UnitCell& ucell, // mentioned above) uncomment the following lines. SdG, Oct 15 1999 } - // mohan fix bug 2011-04-03 + // mohan fix bug 2011-04-03 Parallel_Reduce::reduce_pool(rhoneg); Parallel_Reduce::reduce_pool(rhoima); - // mohan changed 2010-2-2, make this same as in atomic_rho. - // still lack something...... + // mohan changed 2010-2-2, make this same as in atomic_rho. + // still lack something...... rhoneg /= this->rhopw->nxyz * ucell.omega; rhoima /= this->rhopw->nxyz * ucell.omega; @@ -391,11 +391,11 @@ void Charge::non_linear_core_correction { ModuleBase::TITLE("charge","drhoc"); - // use labmda instead of repeating codes - const auto kernel = [&](int num_threads, int thread_id) - { + // use labmda instead of repeating codes + const auto kernel = [&](int num_threads, int thread_id) + { - double gx = 0.0; + double gx = 0.0; double rhocg1 = 0.0; double *aux = nullptr; @@ -408,25 +408,25 @@ void Charge::non_linear_core_correction int igl0 = 0; if (this->rhopw->gg_uniq [0] < 1.0e-8) { - // single thread term - if (thread_id == 0) - { - for (int ir = 0;ir < mesh; ir++) - { - aux [ir] = r [ir] * r [ir] * rhoc [ir]; - } - ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); - //rhocg [1] = fpi * rhocg1 / omega; - rhocg [0] = ModuleBase::FOUR_PI * rhocg1 / omega;//mohan modify 2008-01-19 - } + // single thread term + if (thread_id == 0) + { + for (int ir = 0;ir < mesh; ir++) + { + aux [ir] = r [ir] * r [ir] * rhoc [ir]; + } + ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); + //rhocg [1] = fpi * rhocg1 / omega; + rhocg [0] = ModuleBase::FOUR_PI * rhocg1 / omega;//mohan modify 2008-01-19 + } igl0 = 1; } - int igl_beg, igl_end; - // exclude igl0 - ModuleBase::TASK_DIST_1D(num_threads, thread_id, this->rhopw->ngg - igl0, igl_beg, igl_end); - igl_beg += igl0; - igl_end += igl_beg; + int igl_beg, igl_end; + // exclude igl0 + ModuleBase::TASK_DIST_1D(num_threads, thread_id, this->rhopw->ngg - igl0, igl_beg, igl_end); + igl_beg += igl0; + igl_end += igl_beg; // G <> 0 term for (int igl = igl_beg; igl < igl_end;igl++) @@ -448,13 +448,13 @@ void Charge::non_linear_core_correction // check old version before 2008-12-9 } - }; // end kernel + }; // end kernel - // do not use omp parallel when this function is already in parallel block - // - // it is called in parallel block in Forces::cal_force_cc, - // but not in other funtcion such as Stress_Func::stress_cc. - ModuleBase::TRY_OMP_PARALLEL(kernel); + // do not use omp parallel when this function is already in parallel block + // + // it is called in parallel block in Forces::cal_force_cc, + // but not in other funtcion such as Stress_Func::stress_cc. + ModuleBase::TRY_OMP_PARALLEL(kernel); return; } diff --git a/source/source_estate/module_charge/charge_mixing.cpp b/source/source_estate/module_charge/charge_mixing.cpp index 640fa480c0a..dcdc2740165 100644 --- a/source/source_estate/module_charge/charge_mixing.cpp +++ b/source/source_estate/module_charge/charge_mixing.cpp @@ -16,16 +16,16 @@ Charge_Mixing::Charge_Mixing() Charge_Mixing::~Charge_Mixing() { if(this->mixing != nullptr) - { - delete this->mixing; - this->mixing = nullptr; - } - - if(this->mixing_highf != nullptr) - { - delete this->mixing_highf; + { + delete this->mixing; + this->mixing = nullptr; + } + + if(this->mixing_highf != nullptr) + { + delete this->mixing_highf; this->mixing_highf = nullptr; - } + } } void Charge_Mixing::set_mixing(const std::string& mixing_mode_in, diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 3d8f302b572..7f3979db428 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -213,14 +213,14 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) // use new |m| and angle to update {mx, my, mz} for (int ig = 0; ig < npw; ig++) { - chr->rhog[0][ig] = rhog_magabs[ig]; // rhog - double norm = std::sqrt(chr->rho[1][ig] * chr->rho[1][ig] - + chr->rho[2][ig] * chr->rho[2][ig] - + chr->rho[3][ig] * chr->rho[3][ig]); - if (std::abs(norm) < 1e-10) - { - continue; - } + chr->rhog[0][ig] = rhog_magabs[ig]; // rhog + double norm = std::sqrt(chr->rho[1][ig] * chr->rho[1][ig] + + chr->rho[2][ig] * chr->rho[2][ig] + + chr->rho[3][ig] * chr->rho[3][ig]); + if (std::abs(norm) < 1e-10) + { + continue; + } double rescale_tmp = rho_magabs[npw + ig] / norm; chr->rho[1][ig] *= rescale_tmp; chr->rho[2][ig] *= rescale_tmp; @@ -443,14 +443,14 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rho_magabs[ir] = chr->rho[0][ir]; // rho rho_magabs_save[ir] = chr->rho_save[0][ir]; // rho_save // |m| for rho - rho_magabs[nrxx + ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] - + chr->rho[2][ir] * chr->rho[2][ir] - + chr->rho[3][ir] * chr->rho[3][ir]); - // |m| for rho_save - rho_magabs_save[nrxx + ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] - + chr->rho_save[2][ir] * chr->rho_save[2][ir] - + chr->rho_save[3][ir] * chr->rho_save[3][ir]); - } + rho_magabs[nrxx + ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] + + chr->rho[2][ir] * chr->rho[2][ir] + + chr->rho[3][ir] * chr->rho[3][ir]); + // |m| for rho_save + rho_magabs_save[nrxx + ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] + + chr->rho_save[2][ir] * chr->rho_save[2][ir] + + chr->rho_save[3][ir] * chr->rho_save[3][ir]); + } rhor_in = rho_magabs_save; rhor_out = rho_magabs; @@ -483,14 +483,14 @@ void Charge_Mixing::mix_rho_real(Charge* chr) for (int ir = 0; ir < nrxx; ir++) { chr->rho[0][ir] = rho_magabs[ir]; // rho - double norm = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] - + chr->rho[2][ir] * chr->rho[2][ir] - + chr->rho[3][ir] * chr->rho[3][ir]); - - if (norm < 1e-10) - { - continue; - } + double norm = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] + + chr->rho[2][ir] * chr->rho[2][ir] + + chr->rho[3][ir] * chr->rho[3][ir]); + + if (norm < 1e-10) + { + continue; + } double rescale_tmp = rho_magabs[nrxx + ir] / norm; chr->rho[1][ir] *= rescale_tmp; chr->rho[2][ir] *= rescale_tmp; @@ -603,11 +603,11 @@ void Charge_Mixing::mix_rho(Charge* chr) } } - if (new_e_iteration) - { - new_e_iteration = false; - } + if (new_e_iteration) + { + new_e_iteration = false; + } - ModuleBase::timer::end("Charge_Mixing", "mix_rho"); + ModuleBase::timer::end("Charge_Mixing", "mix_rho"); return; } diff --git a/source/source_estate/module_charge/chgmixing.cpp b/source/source_estate/module_charge/chgmixing.cpp index 9d11f014480..9cab7d1c4ce 100644 --- a/source/source_estate/module_charge/chgmixing.cpp +++ b/source/source_estate/module_charge/chgmixing.cpp @@ -6,8 +6,8 @@ #include "source_lcao/module_deltaspin/spin_constrain.h" void module_charge::chgmixing_ks(const int iter, // scf iteration number - UnitCell& ucell, - elecstate::ElecState* pelec, + UnitCell& ucell, + elecstate::ElecState* pelec, Charge &chr, // charge density Charge_Mixing* p_chgmix, // charge mixing class const int nrxx, // charge density @@ -18,7 +18,7 @@ void module_charge::chgmixing_ks(const int iter, // scf iteration number const double &scf_thr, const double &scf_ene_thr, const bool converged_u, // mohan add 2025-11-06 - const Input_para& inp) // input parameters + const Input_para& inp) // input parameters { if (PARAM.globalv.ks_run) @@ -93,7 +93,7 @@ void module_charge::chgmixing_ks(const int iter, // scf iteration number } //----------charge mixing done----------- } - } + } #ifdef __MPI MPI_Bcast(&drho, 1, MPI_DOUBLE, 0, BP_WORLD); @@ -122,7 +122,7 @@ void module_charge::chgmixing_ks(const int iter, // scf iteration number void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, - const Input_para& inp) // input parameters + const Input_para& inp) // input parameters { ModuleBase::TITLE("module_charge", "chgmixing_ks_pw"); @@ -158,22 +158,22 @@ void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number = spinconstrain::SpinConstrain>::getScInstance(); if (!sc.mag_converged()) // skip uramping if mag not converged { - do_uramping = false; - } - } - if (do_uramping) - { - dftu.uramping_update(); // update U by uramping if uramping > 0.01 - std::cout << " U-Ramping! Current U = "; - for (int i = 0; i < dftu.get_num_u_types(); i++) - { - std::cout << dftu.get_u_current(i) * ModuleBase::Ry_to_eV << " "; - } - std::cout << " eV " << std::endl; - } - } - } - } + do_uramping = false; + } + } + if (do_uramping) + { + dftu.uramping_update(); // update U by uramping if uramping > 0.01 + std::cout << " U-Ramping! Current U = "; + for (int i = 0; i < dftu.get_num_u_types(); i++) + { + std::cout << dftu.get_u_current(i) * ModuleBase::Ry_to_eV << " "; + } + std::cout << " eV " << std::endl; + } + } + } + } return; } @@ -182,7 +182,7 @@ void module_charge::chgmixing_ks_lcao(const int iter, // scf iteration number Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, const int nnr, // dimension of density matrix - const Input_para& inp) // input parameters + const Input_para& inp) // input parameters { ModuleBase::TITLE("module_charge", "chgmixing_ks_lcao"); diff --git a/source/source_estate/module_charge/chgmixing.h b/source/source_estate/module_charge/chgmixing.h index 6b751128e36..c3de464ebb7 100644 --- a/source/source_estate/module_charge/chgmixing.h +++ b/source/source_estate/module_charge/chgmixing.h @@ -12,7 +12,7 @@ namespace module_charge { void chgmixing_ks(const int iter, // scf iteration number - UnitCell& ucell, + UnitCell& ucell, elecstate::ElecState* pelec, Charge &chr, // charge density Charge_Mixing* p_chgmix, // charge mixing class @@ -24,18 +24,18 @@ void chgmixing_ks(const int iter, // scf iteration number const double &scf_thr, const double &scf_ene_thr, const bool converged_u, // mohan add 2025-11-06 - const Input_para& inp); // input parameters + const Input_para& inp); // input parameters void chgmixing_ks_pw(const int iter, Charge_Mixing* p_chgmix, Plus_U_Base& dftu, - const Input_para& inp); // input parameters + const Input_para& inp); // input parameters void chgmixing_ks_lcao(const int iter, // scf iteration number Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, const int nnr, // dimension of density matrix - const Input_para& inp); // input parameters + const Input_para& inp); // input parameters } diff --git a/source/source_estate/module_charge/mix_precond.cpp b/source/source_estate/module_charge/mix_precond.cpp index a0a32fef97c..b3627db7d2c 100644 --- a/source/source_estate/module_charge/mix_precond.cpp +++ b/source/source_estate/module_charge/mix_precond.cpp @@ -7,10 +7,10 @@ void Charge_Mixing::Kerker_screen_recip(std::complex* drhog) { ModuleBase::TITLE("Charge_Mixing", "Kerker_screen_recip"); - if (this->mixing_gg0 <= 0.0 || this->mixing_beta <= 0.1) - { - return; - } + if (this->mixing_gg0 <= 0.0 || this->mixing_beta <= 0.1) + { + return; + } ModuleBase::timer::start("Charge_Mixing", "Kerker_screen_recip"); @@ -21,10 +21,10 @@ void Charge_Mixing::Kerker_screen_recip(std::complex* drhog) double amin = 0.0; /// consider a resize for mixing_angle - int resize_tmp = 1; + int resize_tmp = 1; if (nspin == 4 && this->mixing_angle > 0) { - resize_tmp = 2; + resize_tmp = 2; } /// implement Kerker for density and magnetization separately @@ -78,22 +78,22 @@ void Charge_Mixing::Kerker_screen_real(double* drhor) { ModuleBase::TITLE("Charge_Mixing", "Kerker_screen_real"); - if (this->mixing_gg0 <= 0.0001 || this->mixing_beta <= 0.1) - { - return; - } + if (this->mixing_gg0 <= 0.0001 || this->mixing_beta <= 0.1) + { + return; + } ModuleBase::timer::start("Charge_Mixing", "Kerker_screen_real"); const int nspin = PARAM.inp.nspin; assert(nspin==1 || nspin==2 || nspin==4); - /// consider a resize for mixing_angle + /// consider a resize for mixing_angle int resize_tmp = 1; - if (nspin == 4 && this->mixing_angle > 0) - { - resize_tmp = 2; - } + if (nspin == 4 && this->mixing_angle > 0) + { + resize_tmp = 2; + } std::vector> drhog(this->rhopw->npw * nspin / resize_tmp); std::vector drhor_filter(this->rhopw->nrxx * nspin / resize_tmp); diff --git a/source/source_estate/module_charge/mix_resid.cpp b/source/source_estate/module_charge/mix_resid.cpp index c59c543712a..ff77bf768ba 100644 --- a/source/source_estate/module_charge/mix_resid.cpp +++ b/source/source_estate/module_charge/mix_resid.cpp @@ -136,8 +136,8 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c #endif for (int ig = 0; ig < this->rhopw->npw; ++ig) { - if (ig == ig0) {continue;} - sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / this->rhopw->gg[ig]; + if (ig == ig0) {continue;} + sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / this->rhopw->gg[ig]; } sum *= fac; return sum; @@ -216,10 +216,10 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c #endif for (int ig = 0; ig < this->rhopw->npw; ig++) { - if (ig == ig0) - { - continue; - } + if (ig == ig0) + { + continue; + } sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / this->rhopw->gg[ig]; } sum *= fac; @@ -476,10 +476,10 @@ double Charge_Mixing::inner_product_real(double* rho1, double* rho2) double rnorm = 0.0; // consider a resize for mixing_angle int resize_tmp = 1; - if (PARAM.inp.nspin == 4 && this->mixing_angle > 0) - { - resize_tmp = 2; - } + if (PARAM.inp.nspin == 4 && this->mixing_angle > 0) + { + resize_tmp = 2; + } #ifdef _OPENMP #pragma omp parallel for reduction(+ : rnorm) diff --git a/source/source_estate/module_charge/symm_rho.cpp b/source/source_estate/module_charge/symm_rho.cpp index a3417e96d9b..ade56048ee2 100644 --- a/source/source_estate/module_charge/symm_rho.cpp +++ b/source/source_estate/module_charge/symm_rho.cpp @@ -40,10 +40,10 @@ void Symmetry_rho::begin(const int& spin_now, { assert(spin_now < 4); // added by zhengdy-soc - if (ModuleSymmetry::Symmetry::symm_flag != 1) - { - return; - } + if (ModuleSymmetry::Symmetry::symm_flag != 1) + { + return; + } ModuleBase::TITLE("Symmetry_rho", "begin"); ModuleBase::timer::start("Symmetry_rho","begin"); @@ -51,11 +51,11 @@ void Symmetry_rho::begin(const int& spin_now, // both parallel and serial // if(symm.nrot==symm.nrotk) //pure point-group, do rho_symm in real space // { -// psymm(chr.rho[spin_now], rho_basis, Pgrid, symm); -// if(XC_Functional::get_ked_flag()) psymm(chr.kin_r[spin_now], +// psymm(chr.rho[spin_now], rho_basis, Pgrid, symm); +// if(XC_Functional::get_ked_flag()) psymm(chr.kin_r[spin_now], // rho_basis,Pgrid,symm); // } -// else //space group, do rho_symm in reciprocal space +// else //space group, do rho_symm in reciprocal space rho_basis->real2recip(chr.rho[spin_now], chr.rhog[spin_now]); @@ -97,11 +97,11 @@ void Symmetry_rho::begin(const int& spin_now, // both parallel and serial // if(symm.nrot==symm.nrotk) //pure point-group, do rho_symm in real space // { - // psymm(CHR.rho[spin_now], rho_basis, Pgrid, symm); - // if(XC_Functional::get_ked_flag()) psymm(CHR.kin_r[spin_now], + // psymm(CHR.rho[spin_now], rho_basis, Pgrid, symm); + // if(XC_Functional::get_ked_flag()) psymm(CHR.kin_r[spin_now], // rho_basis,Pgrid,symm); // } - // else //space group, do rho_symm in reciprocal space + // else //space group, do rho_symm in reciprocal space { rho_basis->real2recip(rho[spin_now], rhog[spin_now]); psymmg(rhog[spin_now], rho_basis, symm); @@ -193,7 +193,10 @@ void Symmetry_rho::psymm(double* rho_part, { for(int ix=0; ixnx; ix++) { - if(count%5==0) GlobalV::ofs_running << "\n"; + if(count%5==0) + { + GlobalV::ofs_running << "\n"; + } ++count; GlobalV::ofs_running << " " << rhotot[ix*rho_basis->ny*rho_basis->nz+iy*rho_basis->nz+iz]; } @@ -203,7 +206,7 @@ void Symmetry_rho::psymm(double* rho_part, #ifdef __MPI } - Pgrid.bcast(rhotot.data(), rho_part, GlobalV::MY_RANK, PARAM.inp.esolver_type == "sdft"); + Pgrid.bcast(rhotot.data(), rho_part, GlobalV::MY_RANK, PARAM.inp.esolver_type == "sdft"); #endif ModuleBase::timer::end("Symmetry_rho","psymm"); diff --git a/source/source_estate/module_charge/symm_rhog.cpp b/source/source_estate/module_charge/symm_rhog.cpp index 4327ee68603..86496d049e4 100644 --- a/source/source_estate/module_charge/symm_rhog.cpp +++ b/source/source_estate/module_charge/symm_rhog.cpp @@ -6,351 +6,373 @@ void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Basis *rho_basis, ModuleSymmetry::Symmetry &symm) const -{ - //(1) get fftixy2is and do Allreduce - int * fftixy2is = new int [rho_basis->fftnxy]; - rho_basis->getfftixy2is(fftixy2is); //current proc +{ + //(1) get fftixy2is and do Allreduce + int * fftixy2is = new int [rho_basis->fftnxy]; + rho_basis->getfftixy2is(fftixy2is); //current proc #ifdef __MPI - Parallel_Reduce::reduce_pool(fftixy2is, rho_basis->fftnxy); - if(rho_basis->poolnproc>1) - for (int i=0;ifftnxy;++i) - fftixy2is[i]+=rho_basis->poolnproc-1; + Parallel_Reduce::reduce_pool(fftixy2is, rho_basis->fftnxy); + if(rho_basis->poolnproc>1) + { + for (int i=0;ifftnxy;++i) + { + fftixy2is[i]+=rho_basis->poolnproc-1; + } + } - // (2) reduce all rho from the first pool. - std::complex* rhogtot; - int* ig2isztot = nullptr; - if(GlobalV::RANK_IN_POOL == 0) - { - rhogtot = new std::complex[rho_basis->npwtot]; - ModuleBase::GlobalFunc::ZEROS(rhogtot, rho_basis->npwtot); - ig2isztot = new int[rho_basis->npwtot]; - ModuleBase::GlobalFunc::ZEROS(rhogtot, rho_basis->npwtot); - } - // find max_npw - int max_npw=0; - for (int proc = 0; proc < rho_basis->poolnproc; ++proc) - { - if(rho_basis->npw_per[proc] > max_npw) - { - max_npw=rho_basis->npw_per[proc]; - } - } - this->reduce_to_fullrhog(rho_basis, rhogtot, rhog_part, ig2isztot, rho_basis->ig2isz, max_npw); + // (2) reduce all rho from the first pool. + std::complex* rhogtot; + int* ig2isztot = nullptr; + if(GlobalV::RANK_IN_POOL == 0) + { + rhogtot = new std::complex[rho_basis->npwtot]; + ModuleBase::GlobalFunc::ZEROS(rhogtot, rho_basis->npwtot); + ig2isztot = new int[rho_basis->npwtot]; + ModuleBase::GlobalFunc::ZEROS(rhogtot, rho_basis->npwtot); + } + // find max_npw + int max_npw=0; + for (int proc = 0; proc < rho_basis->poolnproc; ++proc) + { + if(rho_basis->npw_per[proc] > max_npw) + { + max_npw=rho_basis->npw_per[proc]; + } + } + this->reduce_to_fullrhog(rho_basis, rhogtot, rhog_part, ig2isztot, rho_basis->ig2isz, max_npw); - // (3) get ixy2ipw and do rhog_symmetry on proc 0 of each pool - if(GlobalV::RANK_IN_POOL==0) - { + // (3) get ixy2ipw and do rhog_symmetry on proc 0 of each pool + if(GlobalV::RANK_IN_POOL==0) + { #endif - //init ixyz2ipw - int* ixyz2ipw = new int[rho_basis->fftnxyz]; - for(int i=0;ifftnxyz;++i) ixyz2ipw[i]=-1; - // The density must be symmetrized with the same group used to fold the k-points. For - // nspin=4 magnetic that is the Shubnikov group; Theta leaves the charge invariant, so the - // antiunitary elements act on rho exactly like unitary ones (their trs_inv is not used here). - std::vector kgmat; - std::vector> gtr; - std::vector trs_inv; - const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); + //init ixyz2ipw + int* ixyz2ipw = new int[rho_basis->fftnxyz]; + for(int i=0;ifftnxyz;++i) + { + ixyz2ipw[i]=-1; + } + // The density must be symmetrized with the same group used to fold the k-points. For + // nspin=4 magnetic that is the Shubnikov group; Theta leaves the charge invariant, so the + // antiunitary elements act on rho exactly like unitary ones (their trs_inv is not used here). + std::vector kgmat; + std::vector> gtr; + std::vector trs_inv; + const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); #ifdef __MPI - this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); - symm.rhog_symmetry(rhogtot, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, - rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, - rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); + this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); + symm.rhog_symmetry(rhogtot, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, + rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, + rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); #else - this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); - symm.rhog_symmetry(rhog_part, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, - rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, - rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); + this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); + symm.rhog_symmetry(rhog_part, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, + rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, + rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); #endif - delete[] ixyz2ipw; + delete[] ixyz2ipw; #ifdef __MPI - } + } - // (4) send the result to other procs in the same pool - this->rhog_piece_to_all(rho_basis, rhogtot, rhog_part); + // (4) send the result to other procs in the same pool + this->rhog_piece_to_all(rho_basis, rhogtot, rhog_part); - if(GlobalV::RANK_IN_POOL==0) - { - delete[] rhogtot; - delete[] ig2isztot; - } + if(GlobalV::RANK_IN_POOL==0) + { + delete[] rhogtot; + delete[] ig2isztot; + } #endif - delete[] fftixy2is; - return; + delete[] fftixy2is; + return; } void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex* rhog_y, - std::complex* rhog_z, const ModulePW::PW_Basis* rho_basis, ModuleSymmetry::Symmetry& symm) const + std::complex* rhog_z, const ModulePW::PW_Basis* rho_basis, ModuleSymmetry::Symmetry& symm) const { - // build the per-operation spin-rotation matrices W(g) from the cartesian rotation - // gmatc(g) = direct_to_cartesian(gmatrix(g)) = latvec^-1 * gmatrix(g) * latvec. - auto build_wspin = [&rho_basis, &symm]() { - const ModuleBase::Matrix3 latvec = rho_basis->latvec; - const ModuleBase::Matrix3 ilatvec = latvec.Inverse(); - // index [0,nrotk) unitary, [nrotk, nrotk+nrotk_anti) the spatial parts of the - // antiunitary elements Theta*g -- same layout as density_sym_ops(). - const int na = symm.magnetic_nspin4 ? symm.nrotk_anti : 0; - std::vector wspin(symm.nrotk + na); - for (int i = 0; i < symm.nrotk; ++i) - { - const ModuleBase::Matrix3 gmatc = ilatvec * symm.gmatrix[i] * latvec; - wspin[i] = ModuleSymmetry::SpinRotation::spin_so3(gmatc); - } - for (int j = 0; j < na; ++j) - { - const ModuleBase::Matrix3 gmatc = ilatvec * symm.gmatrix_anti[j] * latvec; - wspin[symm.nrotk + j] = ModuleSymmetry::SpinRotation::spin_so3(gmatc); - } - return wspin; - }; + // build the per-operation spin-rotation matrices W(g) from the cartesian rotation + // gmatc(g) = direct_to_cartesian(gmatrix(g)) = latvec^-1 * gmatrix(g) * latvec. + auto build_wspin = [&rho_basis, &symm]() { + const ModuleBase::Matrix3 latvec = rho_basis->latvec; + const ModuleBase::Matrix3 ilatvec = latvec.Inverse(); + // index [0,nrotk) unitary, [nrotk, nrotk+nrotk_anti) the spatial parts of the + // antiunitary elements Theta*g -- same layout as density_sym_ops(). + const int na = symm.magnetic_nspin4 ? symm.nrotk_anti : 0; + std::vector wspin(symm.nrotk + na); + for (int i = 0; i < symm.nrotk; ++i) + { + const ModuleBase::Matrix3 gmatc = ilatvec * symm.gmatrix[i] * latvec; + wspin[i] = ModuleSymmetry::SpinRotation::spin_so3(gmatc); + } + for (int j = 0; j < na; ++j) + { + const ModuleBase::Matrix3 gmatc = ilatvec * symm.gmatrix_anti[j] * latvec; + wspin[symm.nrotk + j] = ModuleSymmetry::SpinRotation::spin_so3(gmatc); + } + return wspin; + }; - //(1) get fftixy2is and do Allreduce - int * fftixy2is = new int [rho_basis->fftnxy]; - rho_basis->getfftixy2is(fftixy2is); //current proc + //(1) get fftixy2is and do Allreduce + int * fftixy2is = new int [rho_basis->fftnxy]; + rho_basis->getfftixy2is(fftixy2is); //current proc #ifdef __MPI - Parallel_Reduce::reduce_pool(fftixy2is, rho_basis->fftnxy); - if(rho_basis->poolnproc>1) - for (int i=0;ifftnxy;++i) - fftixy2is[i]+=rho_basis->poolnproc-1; + Parallel_Reduce::reduce_pool(fftixy2is, rho_basis->fftnxy); + if(rho_basis->poolnproc>1) + { + for (int i=0;ifftnxy;++i) + { + fftixy2is[i]+=rho_basis->poolnproc-1; + } + } - // (2) reduce all three spin components from the first pool. - std::complex* rhogtot_x = nullptr; - std::complex* rhogtot_y = nullptr; - std::complex* rhogtot_z = nullptr; - int* ig2isztot = nullptr; - if(GlobalV::RANK_IN_POOL == 0) - { - rhogtot_x = new std::complex[rho_basis->npwtot]; - rhogtot_y = new std::complex[rho_basis->npwtot]; - rhogtot_z = new std::complex[rho_basis->npwtot]; - ModuleBase::GlobalFunc::ZEROS(rhogtot_x, rho_basis->npwtot); - ModuleBase::GlobalFunc::ZEROS(rhogtot_y, rho_basis->npwtot); - ModuleBase::GlobalFunc::ZEROS(rhogtot_z, rho_basis->npwtot); - ig2isztot = new int[rho_basis->npwtot]; - ModuleBase::GlobalFunc::ZEROS(ig2isztot, rho_basis->npwtot); - } - // find max_npw - int max_npw=0; - for (int proc = 0; proc < rho_basis->poolnproc; ++proc) - { - if(rho_basis->npw_per[proc] > max_npw) - { - max_npw=rho_basis->npw_per[proc]; - } - } - this->reduce_to_fullrhog(rho_basis, rhogtot_x, rhog_x, ig2isztot, rho_basis->ig2isz, max_npw); - this->reduce_to_fullrhog(rho_basis, rhogtot_y, rhog_y, ig2isztot, rho_basis->ig2isz, max_npw); - this->reduce_to_fullrhog(rho_basis, rhogtot_z, rhog_z, ig2isztot, rho_basis->ig2isz, max_npw); + // (2) reduce all three spin components from the first pool. + std::complex* rhogtot_x = nullptr; + std::complex* rhogtot_y = nullptr; + std::complex* rhogtot_z = nullptr; + int* ig2isztot = nullptr; + if(GlobalV::RANK_IN_POOL == 0) + { + rhogtot_x = new std::complex[rho_basis->npwtot]; + rhogtot_y = new std::complex[rho_basis->npwtot]; + rhogtot_z = new std::complex[rho_basis->npwtot]; + ModuleBase::GlobalFunc::ZEROS(rhogtot_x, rho_basis->npwtot); + ModuleBase::GlobalFunc::ZEROS(rhogtot_y, rho_basis->npwtot); + ModuleBase::GlobalFunc::ZEROS(rhogtot_z, rho_basis->npwtot); + ig2isztot = new int[rho_basis->npwtot]; + ModuleBase::GlobalFunc::ZEROS(ig2isztot, rho_basis->npwtot); + } + // find max_npw + int max_npw=0; + for (int proc = 0; proc < rho_basis->poolnproc; ++proc) + { + if(rho_basis->npw_per[proc] > max_npw) + { + max_npw=rho_basis->npw_per[proc]; + } + } + this->reduce_to_fullrhog(rho_basis, rhogtot_x, rhog_x, ig2isztot, rho_basis->ig2isz, max_npw); + this->reduce_to_fullrhog(rho_basis, rhogtot_y, rhog_y, ig2isztot, rho_basis->ig2isz, max_npw); + this->reduce_to_fullrhog(rho_basis, rhogtot_z, rhog_z, ig2isztot, rho_basis->ig2isz, max_npw); - // (3) get ixy2ipw and do rhog_symmetry_nspin4 on proc 0 of each pool - if(GlobalV::RANK_IN_POOL==0) - { + // (3) get ixy2ipw and do rhog_symmetry_nspin4 on proc 0 of each pool + if(GlobalV::RANK_IN_POOL==0) + { #endif - //init ixyz2ipw - int* ixyz2ipw = new int[rho_basis->fftnxyz]; - for(int i=0;ifftnxyz;++i) ixyz2ipw[i]=-1; - std::vector wspin = build_wspin(); - std::vector kgmat; - std::vector> gtr; - std::vector trs_inv; - const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); + //init ixyz2ipw + int* ixyz2ipw = new int[rho_basis->fftnxyz]; + for(int i=0;ifftnxyz;++i) + { + ixyz2ipw[i]=-1; + } + std::vector wspin = build_wspin(); + std::vector kgmat; + std::vector> gtr; + std::vector trs_inv; + const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); #ifdef __MPI - this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); - symm.rhog_symmetry_nspin4(rhogtot_x, rhogtot_y, rhogtot_z, wspin.data(), ixyz2ipw, - rho_basis->nx, rho_basis->ny, rho_basis->nz, - rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, - trs_inv.data(), kgmat.data(), gtr.data(), nop); + this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); + symm.rhog_symmetry_nspin4(rhogtot_x, rhogtot_y, rhogtot_z, wspin.data(), ixyz2ipw, + rho_basis->nx, rho_basis->ny, rho_basis->nz, + rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, + trs_inv.data(), kgmat.data(), gtr.data(), nop); #else - this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); - symm.rhog_symmetry_nspin4(rhog_x, rhog_y, rhog_z, wspin.data(), ixyz2ipw, - rho_basis->nx, rho_basis->ny, rho_basis->nz, - rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, - trs_inv.data(), kgmat.data(), gtr.data(), nop); + this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); + symm.rhog_symmetry_nspin4(rhog_x, rhog_y, rhog_z, wspin.data(), ixyz2ipw, + rho_basis->nx, rho_basis->ny, rho_basis->nz, + rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, + trs_inv.data(), kgmat.data(), gtr.data(), nop); #endif - delete[] ixyz2ipw; + delete[] ixyz2ipw; #ifdef __MPI - } + } - // (4) send the result to other procs in the same pool - this->rhog_piece_to_all(rho_basis, rhogtot_x, rhog_x); - this->rhog_piece_to_all(rho_basis, rhogtot_y, rhog_y); - this->rhog_piece_to_all(rho_basis, rhogtot_z, rhog_z); + // (4) send the result to other procs in the same pool + this->rhog_piece_to_all(rho_basis, rhogtot_x, rhog_x); + this->rhog_piece_to_all(rho_basis, rhogtot_y, rhog_y); + this->rhog_piece_to_all(rho_basis, rhogtot_z, rhog_z); - if(GlobalV::RANK_IN_POOL==0) - { - delete[] rhogtot_x; - delete[] rhogtot_y; - delete[] rhogtot_z; - delete[] ig2isztot; - } + if(GlobalV::RANK_IN_POOL==0) + { + delete[] rhogtot_x; + delete[] rhogtot_y; + delete[] rhogtot_z; + delete[] ig2isztot; + } #endif - delete[] fftixy2is; - return; + delete[] fftixy2is; + return; } #ifdef __MPI void Symmetry_rho::reduce_to_fullrhog(const ModulePW::PW_Basis *rho_basis, - std::complex* rhogtot, std::complex* rhogin, - int* ig2isztot, const int* ig2iszin, int max_npw) const + std::complex* rhogtot, std::complex* rhogin, + int* ig2isztot, const int* ig2iszin, int max_npw) const { - ModuleBase::TITLE("Symmetry_rho","reduce_to_fullrhog"); + ModuleBase::TITLE("Symmetry_rho","reduce_to_fullrhog"); - std::complex* rhog_piece = new std::complex[max_npw]; - int* ig2isz_piece = new int[max_npw]; - - int npw_start=0; - for(int proc=0; procpoolnproc; ++proc) - { - ModuleBase::GlobalFunc::ZEROS(rhog_piece, max_npw); - ModuleBase::GlobalFunc::ZEROS(ig2isz_piece, max_npw); - - MPI_Status ierror; + std::complex* rhog_piece = new std::complex[max_npw]; + int* ig2isz_piece = new int[max_npw]; + + int npw_start=0; + for(int proc=0; procpoolnproc; ++proc) + { + ModuleBase::GlobalFunc::ZEROS(rhog_piece, max_npw); + ModuleBase::GlobalFunc::ZEROS(ig2isz_piece, max_npw); + + MPI_Status ierror; - // case 1: the first part of rho in processor 0 in each pool. - if(proc == 0 && GlobalV::RANK_IN_POOL ==0) - { - for(int ig=0; ignpw; ++ig) - { - rhog_piece[ig] = rhogin[ig]; - ig2isz_piece[ig]=ig2iszin[ig]; - } - } + // case 1: the first part of rho in processor 0 in each pool. + if(proc == 0 && GlobalV::RANK_IN_POOL ==0) + { + for(int ig=0; ignpw; ++ig) + { + rhog_piece[ig] = rhogin[ig]; + ig2isz_piece[ig]=ig2iszin[ig]; + } + } - // case 2: > first part rho: send the rho to - // processor 0 in each pool - else if(proc == GlobalV::RANK_IN_POOL ) - { - for(int ig=0; ignpw; ++ig) - { - rhog_piece[ig] = rhogin[ig]; - ig2isz_piece[ig]=ig2iszin[ig]; - } - MPI_Send(rhog_piece,rho_basis->npw, MPI_DOUBLE_COMPLEX, 0, proc, POOL_WORLD); - MPI_Send(ig2isz_piece, rho_basis->npw, MPI_INT, 0, proc+rho_basis->poolnproc, POOL_WORLD); - } + // case 2: > first part rho: send the rho to + // processor 0 in each pool + else if(proc == GlobalV::RANK_IN_POOL ) + { + for(int ig=0; ignpw; ++ig) + { + rhog_piece[ig] = rhogin[ig]; + ig2isz_piece[ig]=ig2iszin[ig]; + } + MPI_Send(rhog_piece,rho_basis->npw, MPI_DOUBLE_COMPLEX, 0, proc, POOL_WORLD); + MPI_Send(ig2isz_piece, rho_basis->npw, MPI_INT, 0, proc+rho_basis->poolnproc, POOL_WORLD); + } - // case 2: > first part rho: processor 0 receive the rho - // from other processors - else if(GlobalV::RANK_IN_POOL==0) - { - MPI_Recv(rhog_piece, rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD, &ierror); - MPI_Recv(ig2isz_piece, rho_basis->npw_per[proc], MPI_INT, proc, proc+rho_basis->poolnproc, POOL_WORLD, &ierror); - } + // case 2: > first part rho: processor 0 receive the rho + // from other processors + else if(GlobalV::RANK_IN_POOL==0) + { + MPI_Recv(rhog_piece, rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD, &ierror); + MPI_Recv(ig2isz_piece, rho_basis->npw_per[proc], MPI_INT, proc, proc+rho_basis->poolnproc, POOL_WORLD, &ierror); + } - if(GlobalV::RANK_IN_POOL==0) - { - for(int ig=0; ignpw_per[proc]; ++ig) - { - rhogtot[npw_start+ig] = rhog_piece[ig]; - ig2isztot[npw_start+ig] = ig2isz_piece[ig]; - } - npw_start+=rho_basis->npw_per[proc]; - } - } - if(GlobalV::RANK_IN_POOL==0) assert(npw_start==rho_basis->npwtot); - delete[] rhog_piece; - delete[] ig2isz_piece; + if(GlobalV::RANK_IN_POOL==0) + { + for(int ig=0; ignpw_per[proc]; ++ig) + { + rhogtot[npw_start+ig] = rhog_piece[ig]; + ig2isztot[npw_start+ig] = ig2isz_piece[ig]; + } + npw_start+=rho_basis->npw_per[proc]; + } + } + if(GlobalV::RANK_IN_POOL==0) + { + assert(npw_start==rho_basis->npwtot); + } + delete[] rhog_piece; + delete[] ig2isz_piece; - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); - return; + return; } void Symmetry_rho::rhog_piece_to_all(const ModulePW::PW_Basis *rho_basis, - std::complex* rhogtot, std::complex* rhog_part) const -{ - ModuleBase::TITLE(" Symmetry_rho","rhog_piece_to_all"); + std::complex* rhogtot, std::complex* rhog_part) const +{ + ModuleBase::TITLE(" Symmetry_rho","rhog_piece_to_all"); - MPI_Status ierror; + MPI_Status ierror; - if(GlobalV::RANK_IN_POOL==0) - { - // proc 0: send to other proc in pool - // itself: directly copy - for(int ig=0;ignpw;++ig) - { - rhog_part[ig]=rhogtot[ig]; - } - int npw_start=rho_basis->npw; - for(int proc=1;procpoolnproc;++proc) - { - MPI_Send(&rhogtot[npw_start], rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD); - npw_start+=rho_basis->npw_per[proc]; - } - assert(npw_start==rho_basis->npwtot); - }// GlobalV::RANK_IN_POOL == 0 - else - { - MPI_Recv(rhog_part, rho_basis->npw_per[GlobalV::RANK_IN_POOL], MPI_DOUBLE_COMPLEX, 0, GlobalV::RANK_IN_POOL, POOL_WORLD, &ierror); - } - return; + if(GlobalV::RANK_IN_POOL==0) + { + // proc 0: send to other proc in pool + // itself: directly copy + for(int ig=0;ignpw;++ig) + { + rhog_part[ig]=rhogtot[ig]; + } + int npw_start=rho_basis->npw; + for(int proc=1;procpoolnproc;++proc) + { + MPI_Send(&rhogtot[npw_start], rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD); + npw_start+=rho_basis->npw_per[proc]; + } + assert(npw_start==rho_basis->npwtot); + }// GlobalV::RANK_IN_POOL == 0 + else + { + MPI_Recv(rhog_part, rho_basis->npw_per[GlobalV::RANK_IN_POOL], MPI_DOUBLE_COMPLEX, 0, GlobalV::RANK_IN_POOL, POOL_WORLD, &ierror); + } + return; } #endif // only for MYRANK==0 void Symmetry_rho::get_ixyz2ipw(const ModulePW::PW_Basis *rho_basis, - const int* ig2isztot, const int* fftixy2is, int* ixyz2ipw) const + const int* ig2isztot, const int* fftixy2is, int* ixyz2ipw) const { - //step 1: get ipsz2ipw - - //get ipsz2ipw from ig2isztot - int* ipsz2ipw = new int [rho_basis->nstot*rho_basis->nz]; - for(int i=0;instot*rho_basis->nz;++i) ipsz2ipw[i]=-1; + //step 1: get ipsz2ipw + + //get ipsz2ipw from ig2isztot + int* ipsz2ipw = new int [rho_basis->nstot*rho_basis->nz]; + for(int i=0;instot*rho_basis->nz;++i) + { + ipsz2ipw[i]=-1; + } - int npw_count=0; - int nstnz_count=0; - int ipsz=0; //global index of a z-grid on stick - int isz=0; //local index of a z-grid stick on ip core - int ipw=0; // global index of pw (in npwtot) - for (int ip=0;ippoolnproc;++ip) - { - for (int ig=0;ignpw_per[ip];++ig) - { - ipw=npw_count+ig; - isz=ig2isztot[ipw]; - ipsz=nstnz_count+isz; - ipsz2ipw[ipsz]=ipw; - } - npw_count+=rho_basis->npw_per[ip]; - nstnz_count+=rho_basis->nst_per[ip]*rho_basis->nz; - } - assert(npw_count==rho_basis->npwtot); - assert(nstnz_count==rho_basis->nstot*rho_basis->nz); + int npw_count=0; + int nstnz_count=0; + int ipsz=0; //global index of a z-grid on stick + int isz=0; //local index of a z-grid stick on ip core + int ipw=0; // global index of pw (in npwtot) + for (int ip=0;ippoolnproc;++ip) + { + for (int ig=0;ignpw_per[ip];++ig) + { + ipw=npw_count+ig; + isz=ig2isztot[ipw]; + ipsz=nstnz_count+isz; + ipsz2ipw[ipsz]=ipw; + } + npw_count+=rho_basis->npw_per[ip]; + nstnz_count+=rho_basis->nst_per[ip]*rho_basis->nz; + } + assert(npw_count==rho_basis->npwtot); + assert(nstnz_count==rho_basis->nstot*rho_basis->nz); - //step2: ixyz to ipsz + //step2: ixyz to ipsz - //save the start-index of (nst*nz) till each core + //save the start-index of (nst*nz) till each core int* nstnz_start = new int[rho_basis->poolnproc]; nstnz_start[0]=0; for (int ip=1; ippoolnproc; ++ip) + { nstnz_start[ip]=nstnz_start[ip-1]+rho_basis->nst_per[ip-1]*rho_basis->nz; + } //tmp variables int ixy, ixyz, ip, is, ig=0; - - for (int ix=0;ixfftnx;++ix) - { - for (int iy=0;iyfftny;++iy) - { - for(int iz=0;izfftnz;++iz) - { - ixy = ix*rho_basis->fftny + iy; - ixyz = ixy*rho_basis->fftnz+iz; - ip = rho_basis->fftixy2ip[ixy]; - if (ip==-1) continue; //not in any core - is = fftixy2is[ixy]; //stick-index on ip=proc core - if (is==-1) continue; //not on any stick - ipsz = nstnz_start[ip]+is*rho_basis->nz+iz; - ipw = ipsz2ipw[ipsz]; - ixyz2ipw[ixyz] = ipw; - } - } - } - assert (ixyz==rho_basis->fftnxyz-1); + + for (int ix=0;ixfftnx;++ix) + { + for (int iy=0;iyfftny;++iy) + { + for(int iz=0;izfftnz;++iz) + { + ixy = ix*rho_basis->fftny + iy; + ixyz = ixy*rho_basis->fftnz+iz; + ip = rho_basis->fftixy2ip[ixy]; + if (ip==-1) continue; //not in any core + is = fftixy2is[ixy]; //stick-index on ip=proc core + if (is==-1) continue; //not on any stick + ipsz = nstnz_start[ip]+is*rho_basis->nz+iz; + ipw = ipsz2ipw[ipsz]; + ixyz2ipw[ixyz] = ipw; + } + } + } + assert (ixyz==rho_basis->fftnxyz-1); - delete[] nstnz_start; - delete[] ipsz2ipw; - return; + delete[] nstnz_start; + delete[] ipsz2ipw; + return; } From aa9e51574203b005fadd94cca10696f5740405f4 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 11:11:16 +0800 Subject: [PATCH 02/37] module_charge: aggregate Charge_Mixing params into MixingConfig Introduce a MixingConfig POD that bundles the INPUT mixing parameters with the runtime globals (nspin, scf_thr_type, double_grid), and change set_mixing from a 12-argument interface to set_mixing(const MixingConfig&, double&, double&). Charge_Mixing now stores the config and reads nspin / scf_thr_type / double_grid from it instead of PARAM.inp / PARAM.globalv, removing the direct PARAM reads in set_mixing and init_mixing. The single production call site (esolver_ks.cpp) fills the config, and the unit test drives set_mixing via a make_cfg() helper. The '#define private public' access hack is kept for now with a TODO: the test still must write Parameter::input/sys, Charge::_space_* and XC_Functional privates, which need the Step 4/5 global-state parameterization before it can be removed. Verified: make -j30 MODULE_ESTATE_charge_mixing (build_max_para_test) passes with no errors. --- source/source_esolver/esolver_ks.cpp | 18 +- .../module_charge/charge_mixing.cpp | 68 ++--- .../module_charge/charge_mixing.h | 46 +-- .../source_estate/test/charge_mixing_test.cpp | 273 +++--------------- 4 files changed, 115 insertions(+), 290 deletions(-) diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 65bcd52665f..27c219fe21e 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -65,9 +65,21 @@ void ESolver_KS::before_all_runners(BaseCell& basecell, const Input_para& inp) //! 3) setup charge mixing p_chgmix = new Charge_Mixing(); p_chgmix->set_rhopw(this->pw_rho, this->pw_rhod); - p_chgmix->set_mixing(inp.mixing_mode, inp.mixing_beta, inp.mixing_ndim, - inp.mixing_gg0, inp.mixing_tau, inp.mixing_beta_mag, inp.mixing_gg0_mag, - inp.mixing_gg0_min, inp.mixing_angle, inp.mixing_dmr, ucell.omega, ucell.tpiba); + MixingConfig mix_cfg; + mix_cfg.mixing_mode = inp.mixing_mode; + mix_cfg.mixing_beta = inp.mixing_beta; + mix_cfg.mixing_ndim = inp.mixing_ndim; + mix_cfg.mixing_gg0 = inp.mixing_gg0; + mix_cfg.mixing_tau = inp.mixing_tau; + mix_cfg.mixing_beta_mag = inp.mixing_beta_mag; + mix_cfg.mixing_gg0_mag = inp.mixing_gg0_mag; + mix_cfg.mixing_gg0_min = inp.mixing_gg0_min; + mix_cfg.mixing_angle = inp.mixing_angle; + mix_cfg.mixing_dmr = inp.mixing_dmr; + mix_cfg.nspin = inp.nspin; + mix_cfg.scf_thr_type = inp.scf_thr_type; + mix_cfg.double_grid = PARAM.globalv.double_grid; + p_chgmix->set_mixing(mix_cfg, ucell.omega, ucell.tpiba); p_chgmix->init_mixing(); //! 4) setup plane wave for electronic wave functions diff --git a/source/source_estate/module_charge/charge_mixing.cpp b/source/source_estate/module_charge/charge_mixing.cpp index dcdc2740165..76ab3aff058 100644 --- a/source/source_estate/module_charge/charge_mixing.cpp +++ b/source/source_estate/module_charge/charge_mixing.cpp @@ -28,30 +28,24 @@ Charge_Mixing::~Charge_Mixing() } } -void Charge_Mixing::set_mixing(const std::string& mixing_mode_in, - const double& mixing_beta_in, - const int& mixing_ndim_in, - const double& mixing_gg0_in, - const bool& mixing_tau_in, - const double& mixing_beta_mag_in, - const double& mixing_gg0_mag_in, - const double& mixing_gg0_min_in, - const double& mixing_angle_in, - const bool& mixing_dmr_in, +void Charge_Mixing::set_mixing(const MixingConfig& cfg, double& omega_in, double& tpiba_in) { + // store the aggregated config; init_mixing/mix_rho read nspin, + // scf_thr_type and double_grid from it instead of PARAM/GlobalV. + this->cfg_ = cfg; // get private mixing parameters - this->mixing_mode = mixing_mode_in; - this->mixing_beta = mixing_beta_in; - this->mixing_beta_mag = mixing_beta_mag_in; - this->mixing_ndim = mixing_ndim_in; - this->mixing_gg0 = mixing_gg0_in; - this->mixing_tau = mixing_tau_in; - this->mixing_gg0_mag = mixing_gg0_mag_in; - this->mixing_gg0_min = mixing_gg0_min_in; - this->mixing_angle = mixing_angle_in; - this->mixing_dmr = mixing_dmr_in; + this->mixing_mode = cfg.mixing_mode; + this->mixing_beta = cfg.mixing_beta; + this->mixing_beta_mag = cfg.mixing_beta_mag; + this->mixing_ndim = cfg.mixing_ndim; + this->mixing_gg0 = cfg.mixing_gg0; + this->mixing_tau = cfg.mixing_tau; + this->mixing_gg0_mag = cfg.mixing_gg0_mag; + this->mixing_gg0_min = cfg.mixing_gg0_min; + this->mixing_angle = cfg.mixing_angle; + this->mixing_dmr = cfg.mixing_dmr; this->omega = &omega_in; this->tpiba = &tpiba_in; // check the paramters @@ -59,7 +53,7 @@ void Charge_Mixing::set_mixing(const std::string& mixing_mode_in, { ModuleBase::WARNING_QUIT("Charge_Mixing", "You'd better set mixing_beta to [0.0, 1.0]!"); } - if (PARAM.inp.nspin >= 2 && this->mixing_beta_mag < 0.0) + if (cfg.nspin >= 2 && this->mixing_beta_mag < 0.0) { ModuleBase::WARNING_QUIT("Charge_Mixing", "You'd better set mixing_beta_mag >= 0.0!"); } @@ -88,16 +82,16 @@ void Charge_Mixing::set_mixing(const std::string& mixing_mode_in, ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_type", this->mixing_mode); ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_beta", this->mixing_beta); ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_gg0", this->mixing_gg0); - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_gg0_min", PARAM.inp.mixing_gg0_min); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_gg0_min", this->mixing_gg0_min); - if (PARAM.inp.nspin==2 || PARAM.inp.nspin==4) + if (cfg.nspin==2 || cfg.nspin==4) { ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_beta_mag", this->mixing_beta_mag); - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_gg0_mag", PARAM.inp.mixing_gg0_mag); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_gg0_mag", this->mixing_gg0_mag); } - if (PARAM.inp.mixing_angle > 0) + if (this->mixing_angle > 0) { - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_angle", PARAM.inp.mixing_angle); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_angle", this->mixing_angle); } ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "mixing_ndim", this->mixing_ndim); @@ -133,7 +127,7 @@ void Charge_Mixing::init_mixing() ModuleBase::WARNING_QUIT("Charge_Mixing", "This Mixing mode is not implemended yet,coming soon."); } - if ( PARAM.globalv.double_grid) + if ( this->cfg_.double_grid) { // ONLY smooth part of charge density is mixed by specific mixing method // The high_frequency part is mixed by plain mixing method. @@ -143,9 +137,9 @@ void Charge_Mixing::init_mixing() // allocate memory for mixing data, if exists, free it first and then allocate new memory // initailize rho_mdata - if (PARAM.inp.scf_thr_type == 1) - { - if (PARAM.inp.nspin == 4 && PARAM.inp.mixing_angle > 0 ) + if (this->cfg_.scf_thr_type == 1) + { + if (this->cfg_.nspin == 4 && this->mixing_angle > 0 ) { this->mixing->init_mixing_data(this->rho_mdata, this->rhopw->npw * 2, @@ -154,34 +148,34 @@ void Charge_Mixing::init_mixing() else { this->mixing->init_mixing_data(this->rho_mdata, - this->rhopw->npw * PARAM.inp.nspin, + this->rhopw->npw * this->cfg_.nspin, sizeof(std::complex)); } } else { - if (PARAM.inp.nspin == 4 && PARAM.inp.mixing_angle > 0 ) + if (this->cfg_.nspin == 4 && this->mixing_angle > 0 ) { this->mixing->init_mixing_data(this->rho_mdata, this->rhopw->nrxx * 2, sizeof(double)); } else { - this->mixing->init_mixing_data(this->rho_mdata, this->rhopw->nrxx * PARAM.inp.nspin, sizeof(double)); + this->mixing->init_mixing_data(this->rho_mdata, this->rhopw->nrxx * this->cfg_.nspin, sizeof(double)); } } - + // initailize tau_mdata if ((XC_Functional::get_ked_flag()) && mixing_tau) { - if (PARAM.inp.scf_thr_type == 1) + if (this->cfg_.scf_thr_type == 1) { this->mixing->init_mixing_data(this->tau_mdata, - this->rhopw->npw * PARAM.inp.nspin, + this->rhopw->npw * this->cfg_.nspin, sizeof(std::complex)); } else { - this->mixing->init_mixing_data(this->tau_mdata, this->rhopw->nrxx * PARAM.inp.nspin, sizeof(double)); + this->mixing->init_mixing_data(this->tau_mdata, this->rhopw->nrxx * this->cfg_.nspin, sizeof(double)); } } diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index c24a866df91..7d53b0bf8f5 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -5,6 +5,27 @@ #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" +/// Configuration for charge mixing, aggregating the INPUT mixing parameters +/// together with the runtime globals (nspin, scf_thr_type, double_grid) that +/// the mixing logic needs, so that Charge_Mixing does not read PARAM/GlobalV +/// directly. Callers fill this from the parsed input once per run. +struct MixingConfig +{ + std::string mixing_mode = "broyden"; ///< mixing mode: "plain", "broyden", "pulay" + double mixing_beta = 0.8; ///< mixing beta for density + int mixing_ndim = 8; ///< mixing ndim for broyden and pulay + double mixing_gg0 = 0.0; ///< mixing gg0 for Kerker screen + bool mixing_tau = false; ///< whether to use tau mixing + double mixing_beta_mag = 1.6; ///< mixing beta for magnetism + double mixing_gg0_mag = 0.0; ///< mixing gg0 for Kerker screen for magnetism + double mixing_gg0_min = 0.1; ///< minimum kerker coefficient + double mixing_angle = 0.0; ///< mixing angle for nspin=4 + bool mixing_dmr = false; ///< whether to mix real space density matrix + int nspin = 1; ///< number of spins + int scf_thr_type = 1; ///< 1: reciprocal, 2: real space threshold + bool double_grid = false; ///< whether double grid is used +}; + class Charge_Mixing { /// Charge_Mixing class @@ -22,30 +43,12 @@ class Charge_Mixing ~Charge_Mixing(); /** - * @brief Set all private mixing paramters - * @param mixing_mode_in mixing mode: "plain", "broyden", "pulay" - * @param mixing_beta_in mixing beta - * @param mixing_ndim_in mixing ndim - * @param mixing_gg0_in mixing gg0 for Kerker screen - * @param mixing_tau_in whether to use tau mixing - * @param mixing_beta_mag_in mixing beta for magnetism - * @param mixing_gg0_mag_in mixing gg0 for Kerker screen for magnetism - * @param mixing_gg0_min_in minimum kerker coefficient - * @param mixing_angle_in mixing angle for nspin=4 - * @param mixing_dmr_in whether to mixing real space density matrix + * @brief Set all private mixing parameters from an aggregated config + * @param cfg mixing parameters and runtime globals (nspin, scf_thr_type, double_grid) * @param omega_in omega for non-linear core correction * @param tpiba_in 2*pi/beta for non-linear core correction */ - void set_mixing(const std::string& mixing_mode_in, - const double& mixing_beta_in, - const int& mixing_ndim_in, - const double& mixing_gg0_in, - const bool& mixing_tau_in, - const double& mixing_beta_mag_in, - const double& mixing_gg0_mag_in, - const double& mixing_gg0_min_in, - const double& mixing_angle_in, - const bool& mixing_dmr_in, + void set_mixing(const MixingConfig& cfg, double& omega_in, double& tpiba_in); @@ -138,6 +141,7 @@ class Charge_Mixing //====================================== // private mixing parameters //====================================== + MixingConfig cfg_; ///< aggregated mixing config, also holds nspin/scf_thr_type/double_grid std::string mixing_mode = "broyden"; ///< mixing mode: "plain", "broyden", "pulay" double mixing_beta = 0.8; ///< mixing beta for density double mixing_beta_mag = 1.6; ///< mixing beta for magnetism diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index c2bf0b1a2d0..c1be68e7b26 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -1,5 +1,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +// TODO(governance): remove this access hack once mix_resid.cpp and Charge no +// longer read global PARAM/RAW state (Step 4 of the module_charge refactor). +// The test still has to drive Charge::_space_* and XC_Functional privates. #define private public #include "../module_charge/charge_mixing.h" #include "source_base/module_mixing/broyden_mixing.h" @@ -99,7 +102,28 @@ class ChargeMixingTest : public ::testing::Test } ModulePW::PW_Basis pw_basis; ModulePW::PW_Basis_Sup pw_dbasis; - Charge charge; + Charge charge; + + // Build a MixingConfig from the PARAM.input values set in the ctor, so + // set_mixing is driven by explicit config instead of a 12-arg call. + MixingConfig make_cfg() + { + MixingConfig cfg; + cfg.mixing_mode = PARAM.input.mixing_mode; + cfg.mixing_beta = PARAM.input.mixing_beta; + cfg.mixing_ndim = PARAM.input.mixing_ndim; + cfg.mixing_gg0 = PARAM.input.mixing_gg0; + cfg.mixing_tau = PARAM.input.mixing_tau; + cfg.mixing_beta_mag = PARAM.input.mixing_beta_mag; + cfg.mixing_gg0_mag = PARAM.input.mixing_gg0_mag; + cfg.mixing_gg0_min = PARAM.input.mixing_gg0_min; + cfg.mixing_angle = PARAM.input.mixing_angle; + cfg.mixing_dmr = PARAM.input.mixing_dmr; + cfg.nspin = PARAM.input.nspin; + cfg.scf_thr_type = PARAM.input.scf_thr_type; + cfg.double_grid = PARAM.globalv.double_grid; + return cfg; + } }; TEST_F(ChargeMixingTest, SetMixingTest) @@ -114,18 +138,7 @@ TEST_F(ChargeMixingTest, SetMixingTest) PARAM.input.mixing_ndim = 1; PARAM.input.mixing_gg0 = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); EXPECT_EQ(CMtest.get_mixing_mode(), "broyden"); EXPECT_EQ(CMtest.get_mixing_beta(), 1.0); EXPECT_EQ(CMtest.get_mixing_ndim(), 1); @@ -139,36 +152,14 @@ TEST_F(ChargeMixingTest, SetMixingTest) PARAM.input.mixing_tau = true; PARAM.input.mixing_mode = "plain"; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); EXPECT_EQ(CMtest.mixing_mode, "plain"); EXPECT_EQ(CMtest.mixing_tau, true); PARAM.input.mixing_beta = 1.1; std::string output; testing::internal::CaptureStdout(); - EXPECT_EXIT(CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba);, ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba);, ::testing::ExitedWithCode(1), ""); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("You'd better set mixing_beta to [0.0, 1.0]!")); @@ -176,18 +167,7 @@ TEST_F(ChargeMixingTest, SetMixingTest) PARAM.input.mixing_beta_mag = -0.1; PARAM.input.nspin = 2; testing::internal::CaptureStdout(); - EXPECT_EXIT(CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba);, ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba);, ::testing::ExitedWithCode(1), ""); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("You'd better set mixing_beta_mag >= 0.0!")); @@ -196,18 +176,7 @@ TEST_F(ChargeMixingTest, SetMixingTest) PARAM.input.mixing_beta_mag = 1.6; PARAM.input.mixing_mode = "nothing"; testing::internal::CaptureStdout(); - EXPECT_EXIT(CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba);, ::testing::ExitedWithCode(1), ""); + EXPECT_EXIT(CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba);, ::testing::ExitedWithCode(1), ""); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("This Mixing mode is not implemended yet,coming soon.")); } @@ -223,18 +192,7 @@ TEST_F(ChargeMixingTest, InitMixingTest) Charge_Mixing CMtest; CMtest.set_rhopw(&pw_basis, &pw_basis); - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.scf_thr_type= 1; CMtest.init_mixing(); @@ -250,18 +208,7 @@ TEST_F(ChargeMixingTest, InitMixingTest) PARAM.input.nspin = 1; PARAM.input.mixing_tau = true; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); XC_Functional::func_type = 3; XC_Functional::ked_flag = true; CMtest.init_mixing(); @@ -269,18 +216,7 @@ TEST_F(ChargeMixingTest, InitMixingTest) PARAM.input.nspin = 4; PARAM.input.mixing_angle = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest.init_mixing(); EXPECT_EQ(CMtest.rho_mdata.length, 2 * pw_basis.nrxx); } @@ -289,18 +225,7 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) { Charge_Mixing CMtest; // non mixing angle case - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest.set_rhopw(&pw_basis, &pw_basis); PARAM.input.nspin = 4; @@ -317,18 +242,7 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) // mixing angle case PARAM.input.mixing_angle = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.nspin = 4; // a simple sum for inner product @@ -347,18 +261,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipSimpleTest) { Charge_Mixing CMtest; // non mixing angle case - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest.set_rhopw(&pw_basis, &pw_basis); PARAM.input.nspin = 2; @@ -395,18 +298,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) // RECIPROCAL NSPIN=1 ucell.tpiba2 = 1.0; ucell.omega = 2.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.nspin = 1; std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); @@ -470,18 +362,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) // RECIPROCAL NSPIN=4 with mixing_angle PARAM.input.nspin = 4; PARAM.input.mixing_angle = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); drhog1.resize(pw_basis.npw * 2); drhog2.resize(pw_basis.npw * 2); for (int i = 0; i < pw_basis.npw * 2; ++i) @@ -516,18 +397,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) // RECIPROCAL ucell.tpiba2 = 1.0; ucell.omega = 2.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.nspin = 1; std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); @@ -582,18 +452,7 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) Charge_Mixing CMtest; CMtest.set_rhopw(&pw_basis, &pw_basis); ucell.tpiba = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); // nspin = 1 PARAM.input.nspin = 1; std::complex* drhog = new std::complex[PARAM.input.nspin*pw_basis.npw]; @@ -724,18 +583,7 @@ TEST_F(ChargeMixingTest, KerkerScreenRealTest) Charge_Mixing CMtest; CMtest.set_rhopw(&pw_basis, &pw_basis); ucell.tpiba = 1.0; - CMtest.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); // nspin = 1 PARAM.input.nspin = 1; double* drhor = new double[PARAM.input.nspin*pw_basis.nrxx]; @@ -863,18 +711,7 @@ TEST_F(ChargeMixingTest, MixRhoTest) Charge_Mixing CMtest_recip; CMtest_recip.set_rhopw(&pw_basis, &pw_basis); PARAM.input.scf_thr_type= 1; - CMtest_recip.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest_recip.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest_recip.init_mixing(); for(int i = 0 ; i < nspin * npw; ++i) { @@ -904,18 +741,7 @@ TEST_F(ChargeMixingTest, MixRhoTest) Charge_Mixing CMtest_real; PARAM.input.scf_thr_type= 2; CMtest_real.set_rhopw(&pw_basis, &pw_basis); - CMtest_real.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest_real.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest_real.init_mixing(); for(int i = 0 ; i < nspin * nrxx; ++i) { @@ -1003,18 +829,7 @@ TEST_F(ChargeMixingTest, MixDoubleGridRhoTest) CMtest_recip.set_rhopw(&pw_basis, &pw_dbasis); PARAM.input.scf_thr_type= 1; - CMtest_recip.set_mixing(PARAM.input.mixing_mode, - PARAM.input.mixing_beta, - PARAM.input.mixing_ndim, - PARAM.input.mixing_gg0, - PARAM.input.mixing_tau, - PARAM.input.mixing_beta_mag, - PARAM.input.mixing_gg0_mag, - PARAM.input.mixing_gg0_min, - PARAM.input.mixing_angle, - PARAM.input.mixing_dmr, - ucell.omega, - ucell.tpiba); + CMtest_recip.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest_recip.init_mixing(); for (int i = 0; i < nspin * npw; ++i) From 589e65fb511cc8f718a700a65ddbfefc9902a33f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 11:24:42 +0800 Subject: [PATCH 03/37] module_charge: deduplicate twobeta_mix lambdas and replace raw new with std::vector Extract the repeated two-beta mixing functor in mix_rho_recip/mix_rho_real into a make_twobeta_mix template helper (6 lambda copies removed), and convert all local raw new[]/delete[] buffers in charge_mixing_rho.cpp to zero-initialized std::vector, dropping the paired ZEROS calls. --- .../module_charge/charge_mixing.h | 29 +++ .../module_charge/charge_mixing_rho.cpp | 196 +++--------------- 2 files changed, 62 insertions(+), 163 deletions(-) diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 7d53b0bf8f5..05ff1416a55 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -4,6 +4,7 @@ #include "source_estate/module_dm/density_matrix.h" #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" +#include /// Configuration for charge mixing, aggregating the INPUT mixing parameters /// together with the runtime globals (nspin, scf_thr_type, double_grid) that @@ -203,6 +204,34 @@ class Charge_Mixing */ double inner_product_real(double* rho1, double* rho2); + /** + * @brief two-beta mixing functor: mix the first `nunit` elements with + * mixing_beta and the rest (nunit..total) with mixing_beta_mag. Used for + * magnetic cases (nspin==2/4) where the charge channel and the magnetism + * channels use different betas. Replaces the duplicated local lambdas. + * @tparam T element type, double (real space) or std::complex (reciprocal) + */ + template + std::function make_twobeta_mix(const int total, const int nunit) + { + return [this, total, nunit](T* out, const T* in, const T* sres) { +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = 0; i < nunit; ++i) + { + out[i] = in[i] + this->mixing_beta * sres[i]; + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = nunit; i < total; ++i) + { + out[i] = in[i] + this->mixing_beta_mag * sres[i]; + } + }; + } + /** * @brief divide rho/tau to smooth and high frequency parts * @param data_d dense data diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 7f3979db428..2973b00ebbb 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -44,14 +44,10 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) else if (nspin == 2) { // magnetic density - std::complex *rhog_mag = nullptr; - std::complex *rhog_mag_save = nullptr; const int npw = this->rhopw->npw; - // allocate rhog_mag[is*ngmc] and rhog_mag_save[is*ngmc] - rhog_mag = new std::complex[npw * nspin]; - rhog_mag_save = new std::complex[npw * nspin]; - ModuleBase::GlobalFunc::ZEROS(rhog_mag, npw * nspin); - ModuleBase::GlobalFunc::ZEROS(rhog_mag_save, npw * nspin); + // rhog_mag[is*ngmc] and rhog_mag_save[is*ngmc], zero-initialized + std::vector> rhog_mag(npw * nspin); + std::vector> rhog_mag_save(npw * nspin); // get rhog_mag[is*ngmc] and rhog_mag_save[is*ngmc] for (int ig = 0; ig < npw; ig++) { @@ -64,28 +60,11 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog_mag_save[ig + npw] = chr->rhog_save[0][ig] - chr->rhog_save[1][ig]; } // - rhog_in = rhog_mag_save; - rhog_out = rhog_mag; + rhog_in = rhog_mag_save.data(); + rhog_out = rhog_mag.data(); // auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); - auto twobeta_mix - = [this, npw](std::complex* out, const std::complex* in, const std::complex* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < npw; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = npw; i < 2 * npw; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -99,9 +78,6 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) chr->rhog[0][ig] = 0.5 * (rhog_mag[ig] + rhog_mag[ig+npw]); chr->rhog[1][ig] = 0.5 * (rhog_mag[ig] - rhog_mag[ig+npw]); } - // delete - delete[] rhog_mag; - delete[] rhog_mag_save; // get rhogs_out for combine_data() if ( PARAM.globalv.double_grid) { @@ -119,24 +95,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog_out = rhogs_out; const int npw = this->rhopw->npw; auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); // use old one - auto twobeta_mix - = [this, npw](std::complex* out, const std::complex* in, const std::complex* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < npw; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism, mx, my, mz -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = npw; i < 4 * npw; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix>(4 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -150,12 +109,10 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) { ModuleBase::WARNING_QUIT("Charge_Mixing", "double_grid is not supported for new mixing method yet."); } - // allocate memory for rho_magabs and rho_magabs_save + // rho_magabs and rho_magabs_save, zero-initialized const int nrxx = this->rhopw->nrxx; - double* rho_magabs = new double[nrxx]; - double* rho_magabs_save = new double[nrxx]; - ModuleBase::GlobalFunc::ZEROS(rho_magabs, nrxx); - ModuleBase::GlobalFunc::ZEROS(rho_magabs_save, nrxx); + std::vector rho_magabs(nrxx); + std::vector rho_magabs_save(nrxx); // calculate rho_magabs and rho_magabs_save for (int ir = 0; ir < nrxx; ir++) { @@ -168,12 +125,10 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) + chr->rho_save[2][ir] * chr->rho_save[2][ir] + chr->rho_save[3][ir] * chr->rho_save[3][ir]); } - // allocate memory for rhog_magabs and rhog_magabs_save + // rhog_magabs and rhog_magabs_save, zero-initialized const int npw = this->rhopw->npw; - std::complex* rhog_magabs = new std::complex[npw * 2]; - std::complex* rhog_magabs_save = new std::complex[npw * 2]; - ModuleBase::GlobalFunc::ZEROS(rhog_magabs, npw * 2); - ModuleBase::GlobalFunc::ZEROS(rhog_magabs_save, npw * 2); + std::vector> rhog_magabs(npw * 2); + std::vector> rhog_magabs_save(npw * 2); // calculate rhog_magabs and rhog_magabs_save for (int ig = 0; ig < npw; ig++) { @@ -181,35 +136,18 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog_magabs_save[ig] = chr->rhog_save[0][ig]; // rho_save } // FT to get rhog_magabs and rhog_magabs_save - this->rhopw->real2recip(rho_magabs, rhog_magabs + this->rhopw->npw); - this->rhopw->real2recip(rho_magabs_save, rhog_magabs_save + this->rhopw->npw); + this->rhopw->real2recip(rho_magabs.data(), rhog_magabs.data() + this->rhopw->npw); + this->rhopw->real2recip(rho_magabs_save.data(), rhog_magabs_save.data() + this->rhopw->npw); // - rhog_in = rhog_magabs_save; - rhog_out = rhog_magabs; + rhog_in = rhog_magabs_save.data(); + rhog_out = rhog_magabs.data(); auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); // use old one - auto twobeta_mix - = [this, npw](std::complex* out, const std::complex* in, const std::complex* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < npw; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism, |m| -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = npw; i < 2 * npw; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); // get new |m| in real space using FT - this->rhopw->recip2real(rhog_magabs + this->rhopw->npw, rho_magabs); + this->rhopw->recip2real(rhog_magabs.data() + this->rhopw->npw, rho_magabs.data()); // use new |m| and angle to update {mx, my, mz} for (int ig = 0; ig < npw; ig++) { @@ -226,11 +164,6 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) chr->rho[2][ig] *= rescale_tmp; chr->rho[3][ig] *= rescale_tmp; } - // delete - delete[] rhog_magabs; - delete[] rhog_magabs_save; - delete[] rho_magabs; - delete[] rho_magabs_save; } if ( PARAM.globalv.double_grid) @@ -335,14 +268,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) else if (nspin == 2) { // magnetic density - double *rho_mag = nullptr; - double *rho_mag_save = nullptr; const int nrxx = this->rhopw->nrxx; - // allocate rho_mag[is*nnrx] and rho_mag_save[is*nnrx] - rho_mag = new double[nrxx * nspin]; - rho_mag_save = new double[nrxx * nspin]; - ModuleBase::GlobalFunc::ZEROS(rho_mag, nrxx * nspin); - ModuleBase::GlobalFunc::ZEROS(rho_mag_save, nrxx * nspin); + // rho_mag[is*nnrx] and rho_mag_save[is*nnrx], zero-initialized + std::vector rho_mag(nrxx * nspin); + std::vector rho_mag_save(nrxx * nspin); // get rho_mag[is*nnrx] and rho_mag_save[is*nnrx] for (int ir = 0; ir < nrxx; ir++) { @@ -355,27 +284,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rho_mag_save[ir + nrxx] = chr->rho_save[0][ir] - chr->rho_save[1][ir]; } // - rhor_in = rho_mag_save; - rhor_out = rho_mag; + rhor_in = rho_mag_save.data(); + rhor_out = rho_mag.data(); auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - auto twobeta_mix - = [this, nrxx](double* out, const double* in, const double* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nrxx; i < 2 * nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); @@ -392,9 +304,6 @@ void Charge_Mixing::mix_rho_real(Charge* chr) chr->rho[0][ir] = 0.5 * (rho_mag[ir] + rho_mag[ir+nrxx]); chr->rho[1][ir] = 0.5 * (rho_mag[ir] - rho_mag[ir+nrxx]); } - // delete - delete[] rho_mag; - delete[] rho_mag_save; } else if (nspin == 4 && PARAM.inp.mixing_angle <= 0) { @@ -403,24 +312,7 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rhor_out = chr->rho[0]; const int nrxx = this->rhopw->nrxx; auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - auto twobeta_mix - = [this, nrxx](double* out, const double* in, const double* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism, mx, my, mz -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nrxx; i < 4 * nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix(4 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); @@ -432,11 +324,9 @@ void Charge_Mixing::mix_rho_real(Charge* chr) // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed const int nrxx = this->rhopw->nrxx; - // allocate memory for rho_magabs and rho_magabs_save - double* rho_magabs = new double[nrxx * 2]; - double* rho_magabs_save = new double[nrxx * 2]; - ModuleBase::GlobalFunc::ZEROS(rho_magabs, nrxx * 2); - ModuleBase::GlobalFunc::ZEROS(rho_magabs_save, nrxx * 2); + // rho_magabs and rho_magabs_save, zero-initialized + std::vector rho_magabs(nrxx * 2); + std::vector rho_magabs_save(nrxx * 2); // calculate rho_magabs and rho_magabs_save for (int ir = 0; ir < nrxx; ir++) { @@ -451,28 +341,11 @@ void Charge_Mixing::mix_rho_real(Charge* chr) + chr->rho_save[2][ir] * chr->rho_save[2][ir] + chr->rho_save[3][ir] * chr->rho_save[3][ir]); } - rhor_in = rho_magabs_save; - rhor_out = rho_magabs; + rhor_in = rho_magabs_save.data(); + rhor_out = rho_magabs.data(); auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - auto twobeta_mix - = [this, nrxx](double* out, const double* in, const double* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism, |m| -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nrxx; i < 2 * nrxx; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; + auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); @@ -496,11 +369,8 @@ void Charge_Mixing::mix_rho_real(Charge* chr) chr->rho[2][ir] *= rescale_tmp; chr->rho[3][ir] *= rescale_tmp; } - // delete - delete[] rho_magabs; - delete[] rho_magabs_save; } - + double *taur_out=nullptr; double *taur_in=nullptr; if ((XC_Functional::get_ked_flag()) && mixing_tau) From 82464566962d1bab45dd78674fb8473790d5dbce Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 11:55:40 +0800 Subject: [PATCH 04/37] module_charge: move residual/inner-product globals into MixingConfig Extend MixingConfig with gamma_only_pw/domag/domag_z so mix_resid.cpp (get_drho, get_dkin, inner_product_recip_{rho,simple,hartree,real}) no longer reads PARAM/GlobalV; all branches now consume this->cfg_. inner_product_recip_rho's raw pointer-array views are switched to std::vector. Production fills the three new fields in esolver_ks, and the test fixture gains a sync_cfg() helper to push PARAM mutations into cfg_ for the inner-product branch tests. --- source/source_esolver/esolver_ks.cpp | 3 ++ .../module_charge/charge_mixing.h | 10 ++-- .../source_estate/module_charge/mix_resid.cpp | 54 +++++++++---------- .../source_estate/test/charge_mixing_test.cpp | 37 +++++++++++++ 4 files changed, 73 insertions(+), 31 deletions(-) diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 27c219fe21e..0e8a72af6ee 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -79,6 +79,9 @@ void ESolver_KS::before_all_runners(BaseCell& basecell, const Input_para& inp) mix_cfg.nspin = inp.nspin; mix_cfg.scf_thr_type = inp.scf_thr_type; mix_cfg.double_grid = PARAM.globalv.double_grid; + mix_cfg.gamma_only_pw = PARAM.globalv.gamma_only_pw; + mix_cfg.domag = PARAM.globalv.domag; + mix_cfg.domag_z = PARAM.globalv.domag_z; p_chgmix->set_mixing(mix_cfg, ucell.omega, ucell.tpiba); p_chgmix->init_mixing(); diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 05ff1416a55..42fb9449499 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -7,9 +7,10 @@ #include /// Configuration for charge mixing, aggregating the INPUT mixing parameters -/// together with the runtime globals (nspin, scf_thr_type, double_grid) that -/// the mixing logic needs, so that Charge_Mixing does not read PARAM/GlobalV -/// directly. Callers fill this from the parsed input once per run. +/// together with the runtime globals (nspin, scf_thr_type, double_grid, +/// gamma_only_pw, domag, domag_z) that the mixing logic needs, so that +/// Charge_Mixing does not read PARAM/GlobalV directly. Callers fill this +/// from the parsed input once per run. struct MixingConfig { std::string mixing_mode = "broyden"; ///< mixing mode: "plain", "broyden", "pulay" @@ -25,6 +26,9 @@ struct MixingConfig int nspin = 1; ///< number of spins int scf_thr_type = 1; ///< 1: reciprocal, 2: real space threshold bool double_grid = false; ///< whether double grid is used + bool gamma_only_pw = false; ///< whether gamma-only plane wave is used + bool domag = false; ///< whether magnetism (non-collinear) is considered + bool domag_z = false; ///< whether only the z-component magnetism is considered }; class Charge_Mixing diff --git a/source/source_estate/module_charge/mix_resid.cpp b/source/source_estate/module_charge/mix_resid.cpp index ff77bf768ba..fc880838c51 100644 --- a/source/source_estate/module_charge/mix_resid.cpp +++ b/source/source_estate/module_charge/mix_resid.cpp @@ -8,11 +8,11 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) { ModuleBase::TITLE("Charge_Mixing", "get_drho"); ModuleBase::timer::start("Charge_Mixing", "get_drho"); - const int nspin = PARAM.inp.nspin; + const int nspin = this->cfg_.nspin; assert(nspin==1 || nspin==2 || nspin==4); double drho = 0.0; - if (PARAM.inp.scf_thr_type == 1) + if (this->cfg_.scf_thr_type == 1) { for (int is = 0; is < nspin; ++is) { @@ -45,7 +45,7 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) // The inner_product_real function (L1-norm) is different from that (L2-norm) in mixing. for (int is = 0; is < nspin; is++) { - if (is != 0 && is != 3 && PARAM.globalv.domag_z) + if (is != 0 && is != 3 && this->cfg_.domag_z) { continue; } @@ -82,9 +82,9 @@ double Charge_Mixing::get_dkin(Charge* chr, const double nelec) double dkin = 0.0; // Get dkin from kin_r and kin_r_save for PW and LCAO both, which is different from drho. - for (int is = 0; is < PARAM.inp.nspin; is++) + for (int is = 0; is < this->cfg_.nspin; is++) { - if (is != 0 && is != 3 && PARAM.globalv.domag_z) + if (is != 0 && is != 3 && this->cfg_.domag_z) { continue; } @@ -114,9 +114,10 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c ModuleBase::TITLE("Charge_Mixing", "recip_rho"); ModuleBase::timer::start("Charge_Mixing", "recip_rho"); - std::complex** rhog1 = new std::complex*[PARAM.inp.nspin]; - std::complex** rhog2 = new std::complex*[PARAM.inp.nspin]; - for (int is = 0; is < PARAM.inp.nspin; is++) + const int nspin = this->cfg_.nspin; + std::vector*> rhog1(nspin); + std::vector*> rhog2(nspin); + for (int is = 0; is < nspin; is++) { rhog1[is] = rho1 + is * this->rhopw->npw; rhog2[is] = rho2 + is * this->rhopw->npw; @@ -143,7 +144,7 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c return sum; }; - switch (PARAM.inp.nspin) + switch (nspin) { case 1: sum += part_of_noncolin(); @@ -162,7 +163,7 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c } sum *= fac; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { sum *= 2.0; } @@ -193,7 +194,7 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c mag *= fac2; // if(PARAM.globalv.gamma_only_pw); - if (PARAM.globalv.gamma_only_pw) // Peize Lin delete ; 2020.01.31 + if (this->cfg_.gamma_only_pw) // Peize Lin delete ; 2020.01.31 { mag *= 2.0; } @@ -205,7 +206,7 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c } case 4: // non-collinear spin, added by zhengdy - if (!PARAM.globalv.domag && !PARAM.globalv.domag_z) { + if (!this->cfg_.domag && !this->cfg_.domag_z) { sum += part_of_noncolin(); } else { @@ -230,7 +231,7 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c + (conj(rhog1[3][ig0]) * rhog2[3][ig0]).real()); } double fac3 = fac2; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { fac3 *= 2.0; } @@ -255,9 +256,6 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c sum *= *this->omega * 0.5; - delete[] rhog1; - delete[] rhog2; - ModuleBase::timer::end("Charge_Mixing", "recip_rho"); return sum; } @@ -271,12 +269,12 @@ double Charge_Mixing::inner_product_recip_simple(std::complex* rho1, std double rnorm = 0.0; // consider a resize for mixing_angle int resize_tmp = 1; - if (PARAM.inp.nspin == 4 && this->mixing_angle > 0) { resize_tmp = 2; + if (this->cfg_.nspin == 4 && this->mixing_angle > 0) { resize_tmp = 2; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : rnorm) #endif - for (int ig = 0; ig < this->rhopw->npw * PARAM.inp.nspin / resize_tmp; ++ig) + for (int ig = 0; ig < this->rhopw->npw * this->cfg_.nspin / resize_tmp; ++ig) { rnorm += (conj(rho1[ig]) * rho2[ig]).real(); } @@ -321,11 +319,11 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s return sum; }; - if (PARAM.inp.nspin==1) + if (this->cfg_.nspin==1) { sum += part_of_rho(); } - else if (PARAM.inp.nspin==2) + else if (this->cfg_.nspin==2) { // charge density part const int ig0 = this->rhopw->ig_gge0; @@ -342,7 +340,7 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s } sum *= fac; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { sum *= 2.0; } @@ -370,7 +368,7 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s } mag *= fac2; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { mag *= 2.0; } @@ -378,9 +376,9 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s sum2 += mag; sum += sum2; } - else if (PARAM.inp.nspin==4) + else if (this->cfg_.nspin==4) { - if (!PARAM.globalv.domag && !PARAM.globalv.domag_z) + if (!this->cfg_.domag && !this->cfg_.domag_z) { sum += part_of_rho(); } @@ -404,7 +402,7 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s + (conj(rhog1[ig0 + 3*npw]) * rhog2[ig0 + 3*npw]).real()); } double fac3 = fac2; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { fac3 *= 2.0; } @@ -443,7 +441,7 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s * ((conj(rhog1[ig0 + this->rhopw->npw]) * rhog2[ig0 + this->rhopw->npw]).real()); } double fac3 = fac2; - if (PARAM.globalv.gamma_only_pw) + if (this->cfg_.gamma_only_pw) { fac3 *= 2.0; } @@ -476,7 +474,7 @@ double Charge_Mixing::inner_product_real(double* rho1, double* rho2) double rnorm = 0.0; // consider a resize for mixing_angle int resize_tmp = 1; - if (PARAM.inp.nspin == 4 && this->mixing_angle > 0) + if (this->cfg_.nspin == 4 && this->mixing_angle > 0) { resize_tmp = 2; } @@ -484,7 +482,7 @@ double Charge_Mixing::inner_product_real(double* rho1, double* rho2) #ifdef _OPENMP #pragma omp parallel for reduction(+ : rnorm) #endif - for (int ir = 0; ir < this->rhopw->nrxx * PARAM.inp.nspin / resize_tmp; ++ir) + for (int ir = 0; ir < this->rhopw->nrxx * this->cfg_.nspin / resize_tmp; ++ir) { rnorm += rho1[ir] * rho2[ir]; } diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index c1be68e7b26..dad4ce92b48 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -122,8 +122,24 @@ class ChargeMixingTest : public ::testing::Test cfg.nspin = PARAM.input.nspin; cfg.scf_thr_type = PARAM.input.scf_thr_type; cfg.double_grid = PARAM.globalv.double_grid; + cfg.gamma_only_pw = PARAM.globalv.gamma_only_pw; + cfg.domag = PARAM.globalv.domag; + cfg.domag_z = PARAM.globalv.domag_z; return cfg; } + + // Re-sync the runtime globals (nspin/scf_thr_type/gamma_only_pw/domag/domag_z) + // into an already-configured Charge_Mixing. Tests mutate PARAM.sys/PARAM.input + // after set_mixing to steer the residual/inner-product branches; the object + // now reads them from cfg_, so the test must push the new values in. + void sync_cfg(Charge_Mixing& cm) + { + cm.cfg_.nspin = PARAM.input.nspin; + cm.cfg_.scf_thr_type = PARAM.input.scf_thr_type; + cm.cfg_.gamma_only_pw = PARAM.sys.gamma_only_pw; + cm.cfg_.domag = PARAM.sys.domag; + cm.cfg_.domag_z = PARAM.sys.domag_z; + } }; TEST_F(ChargeMixingTest, SetMixingTest) @@ -195,14 +211,17 @@ TEST_F(ChargeMixingTest, InitMixingTest) CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.scf_thr_type= 1; + sync_cfg(CMtest); CMtest.init_mixing(); EXPECT_EQ(CMtest.rho_mdata.length, pw_basis.npw); PARAM.input.scf_thr_type= 2; + sync_cfg(CMtest); CMtest.init_mixing(); EXPECT_EQ(CMtest.rho_mdata.length, pw_basis.nrxx); PARAM.input.nspin = 4; + sync_cfg(CMtest); CMtest.init_mixing(); EXPECT_EQ(CMtest.rho_mdata.length, 4 * pw_basis.nrxx); @@ -228,6 +247,7 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest.set_rhopw(&pw_basis, &pw_basis); PARAM.input.nspin = 4; + sync_cfg(CMtest); // a simple sum for inner product std::vector drho1(pw_basis.nrxx * PARAM.input.nspin); @@ -264,6 +284,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipSimpleTest) CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); CMtest.set_rhopw(&pw_basis, &pw_basis); PARAM.input.nspin = 2; + sync_cfg(CMtest); // a simple sum for inner product std::vector> drhog1(pw_basis.npw * PARAM.input.nspin); @@ -300,6 +321,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) ucell.omega = 2.0; CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.nspin = 1; + sync_cfg(CMtest); std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); for (int i = 0; i < pw_basis.nrxx; ++i) @@ -315,6 +337,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) // RECIPROCAL NSPIN=2 PARAM.input.nspin = 2; + sync_cfg(CMtest); drhog1.resize(pw_basis.npw * PARAM.input.nspin); drhog2.resize(pw_basis.npw * PARAM.input.nspin); std::vector> drhog1_mag(pw_basis.npw * PARAM.input.nspin); @@ -333,14 +356,17 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) drhog2_mag[i+pw_basis.npw] = drhog2[i] - drhog2[i+pw_basis.npw]; } PARAM.sys.gamma_only_pw= false; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); EXPECT_NEAR(inner, 236763.82650318215, 1e-8); PARAM.sys.gamma_only_pw= true; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); EXPECT_NEAR(inner, 236763.82650318215 * 2, 1e-8); // RECIPROCAL NSPIN=4 without mixing_angle PARAM.input.nspin = 4; + sync_cfg(CMtest); drhog1.resize(pw_basis.npw * PARAM.input.nspin); drhog2.resize(pw_basis.npw * PARAM.input.nspin); for (int i = 0; i < pw_basis.npw * PARAM.input.nspin; ++i) @@ -351,11 +377,13 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 28260.091995611871, 1e-8); PARAM.sys.gamma_only_pw= true; PARAM.sys.domag = true; PARAM.sys.domag_z = true; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 110668.61166927818, 1e-8); @@ -371,9 +399,11 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) drhog2[i] = std::complex(1.0, 1.0); } PARAM.sys.gamma_only_pw= false; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 36548.881431837777, 1e-8); PARAM.sys.gamma_only_pw= true; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 44776.555369916401, 1e-8); } @@ -399,6 +429,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) ucell.omega = 2.0; CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); PARAM.input.nspin = 1; + sync_cfg(CMtest); std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); for (int i = 0; i < pw_basis.nrxx; ++i) @@ -413,6 +444,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) EXPECT_NEAR(inner, -0.3 * ModuleBase::e2 * ModuleBase::FOUR_PI, 1e-8); PARAM.input.nspin = 2; + sync_cfg(CMtest); drhog1.resize(pw_basis.npw * PARAM.input.nspin); drhog2.resize(pw_basis.npw * PARAM.input.nspin); for (int i = 0; i < pw_basis.npw * PARAM.input.nspin; ++i) @@ -421,13 +453,16 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) drhog2[i] = std::complex(1.0, 1.0); } PARAM.sys.gamma_only_pw= false; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 236763.82650318215, 1e-8); PARAM.sys.gamma_only_pw= true; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 236763.82650318215 * 2, 1e-8); PARAM.input.nspin = 4; + sync_cfg(CMtest); drhog1.resize(pw_basis.npw * PARAM.input.nspin); drhog2.resize(pw_basis.npw * PARAM.input.nspin); for (int i = 0; i < pw_basis.npw * PARAM.input.nspin; ++i) @@ -438,11 +473,13 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 28260.091995611871, 1e-8); PARAM.sys.gamma_only_pw= true; PARAM.sys.domag = true; PARAM.sys.domag_z = true; + sync_cfg(CMtest); inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); EXPECT_NEAR(inner, 110668.61166927818, 1e-8); } From ecf5084d42a3177f099444af3b23bf01fd5ca4c4 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 15:05:49 +0800 Subject: [PATCH 05/37] module_charge: own Charge's _space_* storage with std::vector (Step 5a) Replace the six private raw _space_rho/_space_rho_save/_space_rhog/ _space_rhog_save/_space_kin_r/_space_kin_r_save buffers with std::vector, so Charge's underlying contiguous storage self-manages and the matching delete[] calls in destroy() (which relied on reading possibly-uninitialized pointers) go away. The public rho/rhog/rho_save/ rhog_save/kin_r/kin_r_save views keep their double**/complex** shape and still alias the vector memory via .data(), so all external consumers are unaffected. Tests that drove _space_* directly are adapted to resize()/.data() and drop their manual delete[] of the buffers. --- source/source_estate/module_charge/charge.cpp | 33 +++++----- source/source_estate/module_charge/charge.h | 18 +++--- .../source_estate/test/charge_mixing_test.cpp | 60 ++++++++----------- source/source_io/test/read_wf2rho_pw_test.cpp | 10 ++-- 4 files changed, 53 insertions(+), 68 deletions(-) diff --git a/source/source_estate/module_charge/charge.cpp b/source/source_estate/module_charge/charge.cpp index e4c1680cb55..798287c86dc 100644 --- a/source/source_estate/module_charge/charge.cpp +++ b/source/source_estate/module_charge/charge.cpp @@ -72,12 +72,7 @@ void Charge::destroy() delete[] rhog_save; delete[] rho_core; delete[] rhog_core; - delete[] _space_rho; - delete[] _space_rho_save; - delete[] _space_rhog; - delete[] _space_rhog_save; - delete[] _space_kin_r; - delete[] _space_kin_r_save; + // _space_* storage is owned by std::vector and frees itself here. if (XC_Functional::get_ked_flag() || PARAM.inp.out_elf[0] > 0) { delete[] kin_r; @@ -116,15 +111,15 @@ void Charge::allocate(const int& nspin_in, const bool kin_den) std::cout << "\n spin_number = " << nspin << " real_point_number = " << nrxx << std::endl; } - // allocate memory - _space_rho = new double[nspin * nrxx]; - _space_rho_save = new double[nspin * nrxx]; - _space_rhog = new std::complex[nspin * ngmc]; - _space_rhog_save = new std::complex[nspin * ngmc]; + // allocate memory (std::vector self-manages the storage) + _space_rho.resize(nspin * nrxx); + _space_rho_save.resize(nspin * nrxx); + _space_rhog.resize(nspin * ngmc); + _space_rhog_save.resize(nspin * ngmc); if(kin_den) { - _space_kin_r = new double[nspin * nrxx]; - _space_kin_r_save = new double[nspin * nrxx]; + _space_kin_r.resize(nspin * nrxx); + _space_kin_r_save.resize(nspin * nrxx); } rho = new double*[nspin]; rhog = new std::complex*[nspin]; @@ -137,19 +132,19 @@ void Charge::allocate(const int& nspin_in, const bool kin_den) } for (int is = 0; is < nspin; is++) { - rho[is] = _space_rho + is * nrxx; - rhog[is] = _space_rhog + is * ngmc; - rho_save[is] = _space_rho_save + is * nrxx; - rhog_save[is] = _space_rhog_save + is * ngmc; + rho[is] = _space_rho.data() + is * nrxx; + rhog[is] = _space_rhog.data() + is * ngmc; + rho_save[is] = _space_rho_save.data() + is * nrxx; + rhog_save[is] = _space_rhog_save.data() + is * ngmc; ModuleBase::GlobalFunc::ZEROS(rho[is], nrxx); ModuleBase::GlobalFunc::ZEROS(rhog[is], ngmc); ModuleBase::GlobalFunc::ZEROS(rho_save[is], nrxx); ModuleBase::GlobalFunc::ZEROS(rhog_save[is], ngmc); if(kin_den) { - kin_r[is] = _space_kin_r + is * nrxx; + kin_r[is] = _space_kin_r.data() + is * nrxx; ModuleBase::GlobalFunc::ZEROS(kin_r[is], nrxx); - kin_r_save[is] = _space_kin_r_save + is * nrxx; + kin_r_save[is] = _space_kin_r_save.data() + is * nrxx; ModuleBase::GlobalFunc::ZEROS(kin_r_save[is], nrxx); } } diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 5c14cd1c50b..949a397ae2b 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -1,6 +1,8 @@ #ifndef CHARGE_H #define CHARGE_H +#include + #include "source_base/complexmatrix.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -45,13 +47,15 @@ class Charge private: - //temporary - double *_space_rho = nullptr; - double *_space_rho_save = nullptr; - std::complex *_space_rhog = nullptr; - std::complex *_space_rhog_save = nullptr; - double *_space_kin_r = nullptr; - double *_space_kin_r_save = nullptr; + // Underlying contiguous storage backing the public rho/rhog/kin_r views. + // Each buffer holds nspin rows; rho[is] points at _space_rho.data()+is*nrxx. + // Owned here as std::vector so the storage self-manages (no raw new/delete). + std::vector _space_rho; + std::vector _space_rho_save; + std::vector> _space_rhog; + std::vector> _space_rhog_save; + std::vector _space_kin_r; + std::vector _space_kin_r_save; public: diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index dad4ce92b48..8de616e257e 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -708,12 +708,12 @@ TEST_F(ChargeMixingTest, MixRhoTest) PARAM.input.mixing_mode = "plain"; const int nrxx = pw_basis.nrxx; const int npw = pw_basis.npw; - charge._space_rho = new double[nspin * nrxx]; - charge._space_rho_save = new double[nspin * nrxx]; - charge._space_rhog = new std::complex[nspin * npw]; - charge._space_rhog_save = new std::complex[nspin * npw]; - charge._space_kin_r = new double[nspin * nrxx]; - charge._space_kin_r_save = new double[nspin * nrxx]; + charge._space_rho.resize(nspin * nrxx); + charge._space_rho_save.resize(nspin * nrxx); + charge._space_rhog.resize(nspin * npw); + charge._space_rhog_save.resize(nspin * npw); + charge._space_kin_r.resize(nspin * nrxx); + charge._space_kin_r_save.resize(nspin * nrxx); charge.rho = new double*[nspin]; charge.rhog = new std::complex*[nspin]; charge.rho_save = new double*[nspin]; @@ -722,12 +722,12 @@ TEST_F(ChargeMixingTest, MixRhoTest) charge.kin_r_save = new double*[nspin]; for (int is = 0; is < nspin; is++) { - charge.rho[is] = charge._space_rho + is * nrxx; - charge.rhog[is] = charge._space_rhog + is * npw; - charge.rho_save[is] = charge._space_rho_save + is * nrxx; - charge.rhog_save[is] = charge._space_rhog_save + is * npw; - charge.kin_r[is] = charge._space_kin_r + is * nrxx; - charge.kin_r_save[is] = charge._space_kin_r_save + is * nrxx; + charge.rho[is] = charge._space_rho.data() + is * nrxx; + charge.rhog[is] = charge._space_rhog.data() + is * npw; + charge.rho_save[is] = charge._space_rho_save.data() + is * nrxx; + charge.rhog_save[is] = charge._space_rhog_save.data() + is * npw; + charge.kin_r[is] = charge._space_kin_r.data() + is * nrxx; + charge.kin_r_save[is] = charge._space_kin_r_save.data() + is * nrxx; } std::vector real_ref(nspin * nrxx); std::vector real_save_ref(nspin * nrxx); @@ -796,12 +796,6 @@ TEST_F(ChargeMixingTest, MixRhoTest) } //------------------------------------------------------------------------- - delete[] charge._space_rho; - delete[] charge._space_rho_save; - delete[] charge._space_rhog; - delete[] charge._space_rhog_save; - delete[] charge._space_kin_r; - delete[] charge._space_kin_r_save; delete[] charge.rho; delete[] charge.rhog; delete[] charge.rho_save; @@ -825,12 +819,12 @@ TEST_F(ChargeMixingTest, MixDoubleGridRhoTest) PARAM.input.mixing_mode = "plain"; const int nrxx = pw_dbasis.nrxx; const int npw = pw_dbasis.npw; - charge._space_rho = new double[nspin * nrxx]; - charge._space_rho_save = new double[nspin * nrxx]; - charge._space_rhog = new std::complex[nspin * npw]; - charge._space_rhog_save = new std::complex[nspin * npw]; - charge._space_kin_r = new double[nspin * nrxx]; - charge._space_kin_r_save = new double[nspin * nrxx]; + charge._space_rho.resize(nspin * nrxx); + charge._space_rho_save.resize(nspin * nrxx); + charge._space_rhog.resize(nspin * npw); + charge._space_rhog_save.resize(nspin * npw); + charge._space_kin_r.resize(nspin * nrxx); + charge._space_kin_r_save.resize(nspin * nrxx); charge.rho = new double*[nspin]; charge.rhog = new std::complex*[nspin]; charge.rho_save = new double*[nspin]; @@ -839,12 +833,12 @@ TEST_F(ChargeMixingTest, MixDoubleGridRhoTest) charge.kin_r_save = new double*[nspin]; for (int is = 0; is < nspin; is++) { - charge.rho[is] = charge._space_rho + is * nrxx; - charge.rhog[is] = charge._space_rhog + is * npw; - charge.rho_save[is] = charge._space_rho_save + is * nrxx; - charge.rhog_save[is] = charge._space_rhog_save + is * npw; - charge.kin_r[is] = charge._space_kin_r + is * nrxx; - charge.kin_r_save[is] = charge._space_kin_r_save + is * nrxx; + charge.rho[is] = charge._space_rho.data() + is * nrxx; + charge.rhog[is] = charge._space_rhog.data() + is * npw; + charge.rho_save[is] = charge._space_rho_save.data() + is * nrxx; + charge.rhog_save[is] = charge._space_rhog_save.data() + is * npw; + charge.kin_r[is] = charge._space_kin_r.data() + is * nrxx; + charge.kin_r_save[is] = charge._space_kin_r_save.data() + is * nrxx; } std::vector real_ref(nspin * nrxx); std::vector real_save_ref(nspin * nrxx); @@ -894,12 +888,6 @@ TEST_F(ChargeMixingTest, MixDoubleGridRhoTest) } //------------------------------------------------------------------------- - delete[] charge._space_rho; - delete[] charge._space_rho_save; - delete[] charge._space_rhog; - delete[] charge._space_rhog_save; - delete[] charge._space_kin_r; - delete[] charge._space_kin_r_save; delete[] charge.rho; delete[] charge.rhog; delete[] charge.rho_save; diff --git a/source/source_io/test/read_wf2rho_pw_test.cpp b/source/source_io/test/read_wf2rho_pw_test.cpp index 38f586f3a5e..70a4b16a63c 100644 --- a/source/source_io/test/read_wf2rho_pw_test.cpp +++ b/source/source_io/test/read_wf2rho_pw_test.cpp @@ -230,8 +230,8 @@ TEST_F(ReadWfcRhoTest, ReadWfcRho) // Initialize charge density //---------------------------------------- chg.rho = new double*[nspin]; - chg._space_rho = new double[rhopw->nrxx]; - chg.rho[0] = chg._space_rho; + chg._space_rho.resize(rhopw->nrxx); + chg.rho[0] = chg._space_rho.data(); ModuleBase::GlobalFunc::ZEROS(chg.rho[0], rhopw->nrxx); chg.rhopw = rhopw; chg.nrxx = rhopw->nrxx; @@ -242,8 +242,8 @@ TEST_F(ReadWfcRhoTest, ReadWfcRho) //---------------------------------------- Charge chg_ref; chg_ref.rho = new double*[nspin]; - chg_ref._space_rho = new double[rhopw->nrxx]; - chg_ref.rho[0] = chg_ref._space_rho; + chg_ref._space_rho.resize(rhopw->nrxx); + chg_ref.rho[0] = chg_ref._space_rho.data(); ModuleBase::GlobalFunc::ZEROS(chg_ref.rho[0], rhopw->nrxx); std::vector> rho_tmp(rhopw->nrxx); chg_ref.nrxx = rhopw->nrxx; @@ -348,9 +348,7 @@ TEST_F(ReadWfcRhoTest, ReadWfcRho) } delete[] chg.rho; - delete[] chg._space_rho; delete[] chg_ref.rho; - delete[] chg_ref._space_rho; delete psi; if (GlobalV::MY_RANK == 0) From 25bd4418857d2a980b066cc76f0a680d895fad3f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 16:28:03 +0800 Subject: [PATCH 06/37] module_charge: route chgmixing_ks through its inp parameter chgmixing_ks already takes a const Input_para& inp but still read PARAM.inp.mixing_restart / PARAM.inp.scf_nmax from the global. Use the inp argument instead so the function no longer reads INPUT state through the global for these two fields. PARAM.globalv.ks_run is a runtime per-process flag (set from band-parallel topology), not an input, so it is intentionally left as-is rather than threading it through the interface. --- source/source_estate/module_charge/chgmixing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/source_estate/module_charge/chgmixing.cpp b/source/source_estate/module_charge/chgmixing.cpp index 9cab7d1c4ce..a11226ba377 100644 --- a/source/source_estate/module_charge/chgmixing.cpp +++ b/source/source_estate/module_charge/chgmixing.cpp @@ -107,9 +107,9 @@ void module_charge::chgmixing_ks(const int iter, // scf iteration number // mohan move the following code here, 2025-10-18 // SCF restart information - if (PARAM.inp.mixing_restart > 0 + if (inp.mixing_restart > 0 && iter == p_chgmix->mixing_restart_step - 1 - && iter != PARAM.inp.scf_nmax) + && iter != inp.scf_nmax) { p_chgmix->mixing_restart_last = iter; std::cout << " SCF restart after this step!" << std::endl; From 1e9411c6be7e704ae6b6cb04240e84cdabe9d3a1 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 16:35:27 +0800 Subject: [PATCH 07/37] module_charge: split Charge::init_rho into per-stage private methods init_rho had a cyclomatic complexity of 36 from five sequential stages (file read, atomic fallback, Thomas-Fermi tau, restart load, wfc read) interleaved through shared read_error/read_kin_error flags. Extract the four branches into private methods -- read_rho_from_file, init_rho_atomic_and_tau, load_rho_from_restart, init_rho_from_wfc -- and leave init_rho as a thin sequence of stage calls. Logic is unchanged; the error flags are threaded through as parameters. The deepest stage (read_rho_from_file) now sits at complexity 19, down from 36 for the monolith. The remaining global reads inside the stages are untouched and deferred to a later parameterization step. --- source/source_estate/module_charge/charge.h | 28 ++ .../module_charge/charge_init.cpp | 295 ++++++++++-------- 2 files changed, 192 insertions(+), 131 deletions(-) diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 949a397ae2b..bcf40063a88 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -163,6 +163,34 @@ class Charge void destroy(); // free arrays liuyu 2023-03-12 + // --- init_rho stages (extracted to keep init_rho's complexity down) --- + + // Read charge (and kinetic-energy) density from file/auto into rho/kin_r. + // Sets read_error / read_kin_error so later stages can fall back. + void read_rho_from_file(const UnitCell& ucell, + const Parallel_Grid& pgrid, + const int nspin, + bool& read_error, + bool& read_kin_error); + + // Atomic-density fallback plus Thomas-Fermi kinetic-energy-density init. + void init_rho_atomic_and_tau(const UnitCell& ucell, + const ModuleBase::ComplexMatrix& strucFac, + const int nspin, + const bool read_error, + const bool read_kin_error); + + // Load charge from the restart disk cache if requested. + void load_rho_from_restart(const UnitCell& ucell, + const Parallel_Grid& pgrid, + const int nspin); + + // Initialise rho from wavefunctions (PW-KSDFT only). + void init_rho_from_wfc(ModuleSymmetry::Symmetry& symm, + const int nspin, + const void* klist, + const void* wfcpw); + double* omega_ = nullptr; // omega for non-linear core correction bool allocate_rho; diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index 0552c17b5a3..1cb53585d4f 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -47,156 +47,181 @@ void Charge::init_rho(const UnitCell& ucell, bool read_kin_error = false; if (PARAM.inp.init_chg == "file" || PARAM.inp.init_chg == "auto") { - GlobalV::ofs_running << " Read electron density from file" << std::endl; - - // try to read charge from binary file first, which is the same as QE - // liuyu 2023-12-05 - std::stringstream binary; - binary << PARAM.globalv.global_readin_dir << PARAM.inp.suffix + "-CHARGE-DENSITY.restart"; - // Temporary bridge: use factory until ParaCollection is wired into driver. - Parallel::ParaWorld pw_world = Parallel::make_pw_world(); - if (elecstate::read_rhog(binary.str(), rhopw, nspin, rhog, pw_world, &GlobalV::ofs_warning)) + this->read_rho_from_file(ucell, pgrid, nspin, read_error, read_kin_error); + } + + this->init_rho_atomic_and_tau(ucell, strucFac, nspin, read_error, read_kin_error); + + this->load_rho_from_restart(ucell, pgrid, nspin); + + if (PARAM.inp.init_chg == "wfc") + { + this->init_rho_from_wfc(symm, nspin, klist, wfcpw); + } +} + +void Charge::read_rho_from_file(const UnitCell& ucell, + const Parallel_Grid& pgrid, + const int nspin, + bool& read_error, + bool& read_kin_error) +{ + GlobalV::ofs_running << " Read electron density from file" << std::endl; + + // try to read charge from binary file first, which is the same as QE + // liuyu 2023-12-05 + std::stringstream binary; + binary << PARAM.globalv.global_readin_dir << PARAM.inp.suffix + "-CHARGE-DENSITY.restart"; + // Temporary bridge: use factory until ParaCollection is wired into driver. + Parallel::ParaWorld pw_world = Parallel::make_pw_world(); + if (elecstate::read_rhog(binary.str(), rhopw, nspin, rhog, pw_world, &GlobalV::ofs_warning)) + { + GlobalV::ofs_running << " Read electron density from file: " << binary.str() << std::endl; + for (int is = 0; is < nspin; ++is) { - GlobalV::ofs_running << " Read electron density from file: " << binary.str() << std::endl; - for (int is = 0; is < nspin; ++is) - { - rhopw->recip2real(rhog[is], rho[is]); - } + rhopw->recip2real(rhog[is], rho[is]); } - else + } + else + { + for (int is = 0; is < nspin; ++is) { - for (int is = 0; is < nspin; ++is) + std::stringstream ssc; + + if(nspin==1) { - std::stringstream ssc; + ssc << PARAM.globalv.global_readin_dir << "chg.cube"; + } + else + { + ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; + } + - if(nspin==1) + if (ModuleIO::read_vdata_palgrid(pgrid, + (PARAM.inp.esolver_type == "sdft" ? GlobalV::RANK_IN_BPGROUP : GlobalV::MY_RANK), + GlobalV::ofs_running, + ssc.str(), + this->rho[is], + ucell.nat)) + { + GlobalV::ofs_running << " Read electron density from file: " << ssc.str() << std::endl; + } + else if (is > 0) // nspin=2 or 4 + { + if (is == 1) // failed at the second spin { - ssc << PARAM.globalv.global_readin_dir << "chg.cube"; + std::cout << " Incomplete electron density file." << std::endl; + read_error = true; + break; } - else + else if (is == 2) // read 2 files when nspin=4 { - ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; + GlobalV::ofs_running << " Didn't read in the electron density but would rearrange it later. " + << std::endl; } - - - if (ModuleIO::read_vdata_palgrid(pgrid, - (PARAM.inp.esolver_type == "sdft" ? GlobalV::RANK_IN_BPGROUP : GlobalV::MY_RANK), - GlobalV::ofs_running, - ssc.str(), - this->rho[is], - ucell.nat)) + else if (is == 3) // read 2 files when nspin=4 { - GlobalV::ofs_running << " Read electron density from file: " << ssc.str() << std::endl; - } - else if (is > 0) // nspin=2 or 4 - { - if (is == 1) // failed at the second spin - { - std::cout << " Incomplete electron density file." << std::endl; - read_error = true; - break; - } - else if (is == 2) // read 2 files when nspin=4 + GlobalV::ofs_running << " rearrange electron density " << std::endl; + for (int ir = 0; ir < this->rhopw->nrxx; ir++) { - GlobalV::ofs_running << " Didn't read in the electron density but would rearrange it later. " - << std::endl; + this->rho[3][ir] = this->rho[0][ir] - this->rho[1][ir]; + this->rho[0][ir] = this->rho[0][ir] + this->rho[1][ir]; + this->rho[1][ir] = 0.0; + this->rho[2][ir] = 0.0; } - else if (is == 3) // read 2 files when nspin=4 - { - GlobalV::ofs_running << " rearrange electron density " << std::endl; - for (int ir = 0; ir < this->rhopw->nrxx; ir++) - { - this->rho[3][ir] = this->rho[0][ir] - this->rho[1][ir]; - this->rho[0][ir] = this->rho[0][ir] + this->rho[1][ir]; - this->rho[1][ir] = 0.0; - this->rho[2][ir] = 0.0; - } - } - } - else - { - read_error = true; - break; } } + else + { + read_error = true; + break; + } } + } - if (read_error) + if (read_error) + { + const std::string warn_msg + = " WARNING: \"init_chg\" is enabled but ABACUS failed to read\n charge density from file.\n" + " Please check if there is chg.cube (for nspin=1) or chgsx.cube (x=1,2,etc.) or\n {suffix}-CHARGE-DENSITY.restart in the " + "directory.\n"; + std::cout << warn_msg; + if (PARAM.inp.init_chg == "file") { - const std::string warn_msg - = " WARNING: \"init_chg\" is enabled but ABACUS failed to read\n charge density from file.\n" - " Please check if there is chg.cube (for nspin=1) or chgsx.cube (x=1,2,etc.) or\n {suffix}-CHARGE-DENSITY.restart in the " - "directory.\n"; - std::cout << warn_msg; - if (PARAM.inp.init_chg == "file") - { - ModuleBase::WARNING_QUIT("Charge::init_rho", - "Failed to read in charge density from file.\n For initializing atomic " - "charge in calculations,\n please set init_chg to atomic in INPUT."); - } + ModuleBase::WARNING_QUIT("Charge::init_rho", + "Failed to read in charge density from file.\n For initializing atomic " + "charge in calculations,\n please set init_chg to atomic in INPUT."); } + } - if (XC_Functional::get_ked_flag()) + if (XC_Functional::get_ked_flag()) + { + // If the charge density is not read in, then the kinetic energy density is not read in either + if (!read_error) { - // If the charge density is not read in, then the kinetic energy density is not read in either - if (!read_error) + GlobalV::ofs_running << " try to read kinetic energy density from file" << std::endl; + // try to read charge from binary file first, which is the same as QE + std::vector> kin_g_space(nspin * this->ngmc, {0.0, 0.0}); + std::vector*> kin_g; + for (int is = 0; is < nspin; is++) { - GlobalV::ofs_running << " try to read kinetic energy density from file" << std::endl; - // try to read charge from binary file first, which is the same as QE - std::vector> kin_g_space(nspin * this->ngmc, {0.0, 0.0}); - std::vector*> kin_g; - for (int is = 0; is < nspin; is++) + kin_g.push_back(kin_g_space.data() + is * this->ngmc); + } + + std::stringstream binary; + binary << PARAM.globalv.global_readin_dir << PARAM.inp.suffix + "-TAU-DENSITY.restart"; + if (elecstate::read_rhog(binary.str(), rhopw, nspin, kin_g.data(), pw_world, &GlobalV::ofs_warning)) + { + GlobalV::ofs_running << " Read in the kinetic energy density: " << binary.str() << std::endl; + for (int is = 0; is < nspin; ++is) { - kin_g.push_back(kin_g_space.data() + is * this->ngmc); + rhopw->recip2real(kin_g[is], this->kin_r[is]); } - - std::stringstream binary; - binary << PARAM.globalv.global_readin_dir << PARAM.inp.suffix + "-TAU-DENSITY.restart"; - if (elecstate::read_rhog(binary.str(), rhopw, nspin, kin_g.data(), pw_world, &GlobalV::ofs_warning)) + } + else + { + for (int is = 0; is < nspin; is++) { - GlobalV::ofs_running << " Read in the kinetic energy density: " << binary.str() << std::endl; - for (int is = 0; is < nspin; ++is) + std::stringstream ssc; + ssc << PARAM.globalv.global_readin_dir << "SPIN" << is + 1 << "_TAU.cube"; + // mohan update 2012-02-10, sunliang update 2023-03-09 + if (ModuleIO::read_vdata_palgrid( + pgrid, + (PARAM.inp.esolver_type == "sdft" ? GlobalV::RANK_IN_BPGROUP : GlobalV::MY_RANK), + GlobalV::ofs_running, + ssc.str(), + this->kin_r[is], + ucell.nat)) { - rhopw->recip2real(kin_g[is], this->kin_r[is]); + GlobalV::ofs_running << " Read in the kinetic energy density: " << ssc.str() << std::endl; } - } - else - { - for (int is = 0; is < nspin; is++) + else { - std::stringstream ssc; - ssc << PARAM.globalv.global_readin_dir << "SPIN" << is + 1 << "_TAU.cube"; - // mohan update 2012-02-10, sunliang update 2023-03-09 - if (ModuleIO::read_vdata_palgrid( - pgrid, - (PARAM.inp.esolver_type == "sdft" ? GlobalV::RANK_IN_BPGROUP : GlobalV::MY_RANK), - GlobalV::ofs_running, - ssc.str(), - this->kin_r[is], - ucell.nat)) - { - GlobalV::ofs_running << " Read in the kinetic energy density: " << ssc.str() << std::endl; - } - else - { - read_kin_error = true; - std::cout << " WARNING: \"init_chg\" is enabled but ABACUS failed to read kinetic energy " - "density from file.\n" - " Please check if there is SPINX_TAU.cube (X=1,...) or " - "{suffix}-TAU-DENSITY.restart in the directory.\n" - << std::endl; - break; - } + read_kin_error = true; + std::cout << " WARNING: \"init_chg\" is enabled but ABACUS failed to read kinetic energy " + "density from file.\n" + " Please check if there is SPINX_TAU.cube (X=1,...) or " + "{suffix}-TAU-DENSITY.restart in the directory.\n" + << std::endl; + break; } } } - else - { - read_kin_error = true; - } + } + else + { + read_kin_error = true; } } +} +void Charge::init_rho_atomic_and_tau(const UnitCell& ucell, + const ModuleBase::ComplexMatrix& strucFac, + const int nspin, + const bool read_error, + const bool read_kin_error) +{ if (PARAM.inp.init_chg == "atomic" || read_error) { if (read_error) @@ -225,7 +250,12 @@ void Charge::init_rho(const UnitCell& ucell, } } } +} +void Charge::load_rho_from_restart(const UnitCell& ucell, + const Parallel_Grid& pgrid, + const int nspin) +{ // Peize Lin add 2020.04.04 if (GlobalC::restart.info_load.load_charge && !GlobalC::restart.info_load.load_charge_finish) { @@ -253,24 +283,27 @@ void Charge::init_rho(const UnitCell& ucell, } GlobalC::restart.info_load.load_charge_finish = true; } +} - if (PARAM.inp.init_chg == "wfc") +void Charge::init_rho_from_wfc(ModuleSymmetry::Symmetry& symm, + const int nspin, + const void* klist, + const void* wfcpw) +{ + if (wfcpw == nullptr) { - if (wfcpw == nullptr) - { - ModuleBase::WARNING_QUIT("Charge::init_rho", "wfc is only supported for PW-KSDFT."); - } + ModuleBase::WARNING_QUIT("Charge::init_rho", "wfc is only supported for PW-KSDFT."); + } - const ModulePW::PW_Basis_K* pw_wfc = reinterpret_cast(const_cast(wfcpw)); - const K_Vectors* kv = reinterpret_cast(klist); + const ModulePW::PW_Basis_K* pw_wfc = reinterpret_cast(const_cast(wfcpw)); + const K_Vectors* kv = reinterpret_cast(klist); - ModuleIO::read_wf2rho_pw(pw_wfc, symm, *this, - PARAM.globalv.global_readin_dir, - GlobalV::KPAR, GlobalV::MY_POOL, GlobalV::MY_RANK, - GlobalV::NPROC_IN_POOL, GlobalV::RANK_IN_POOL, - PARAM.inp.nbands, nspin, PARAM.globalv.npol, - kv->get_nkstot(),kv->ik2iktot,kv->isk,GlobalV::ofs_running); - } + ModuleIO::read_wf2rho_pw(pw_wfc, symm, *this, + PARAM.globalv.global_readin_dir, + GlobalV::KPAR, GlobalV::MY_POOL, GlobalV::MY_RANK, + GlobalV::NPROC_IN_POOL, GlobalV::RANK_IN_POOL, + PARAM.inp.nbands, nspin, PARAM.globalv.npol, + kv->get_nkstot(),kv->ik2iktot,kv->isk,GlobalV::ofs_running); } //========================================================== From c4a8ed236f5df9ee13135f07980570e644b81b46 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 16:59:08 +0800 Subject: [PATCH 08/37] module_charge: extract Charge density math into charge_math free functions sum_rho, cal_rho2ne and non_linear_core_correction each used Charge members only to reach a handful of scalars (nrxx/nxyz/omega) or the reciprocal-shell table (gg_uniq/ngg); the rest of each body is pure numerics. Move the three bodies into a new charge_math namespace as free functions with those values passed explicitly, and leave the Charge members as thin forwarding wrappers so no caller outside the module changes. The kernels are now unit-testable in isolation and no longer coupled to Charge state. One behavior note: the pre-quit debug line that printed sum_rho to ofs_warning is dropped so the free function stays free of global-stream dependencies. charge_math.cpp is wired into the estate library and the charge_test target. --- source/source_estate/CMakeLists.txt | 1 + source/source_estate/module_charge/charge.cpp | 46 +----- .../module_charge/charge_init.cpp | 79 ++------- .../module_charge/charge_math.cpp | 155 ++++++++++++++++++ .../source_estate/module_charge/charge_math.h | 47 ++++++ source/source_estate/test/CMakeLists.txt | 2 +- 6 files changed, 219 insertions(+), 111 deletions(-) create mode 100644 source/source_estate/module_charge/charge_math.cpp create mode 100644 source/source_estate/module_charge/charge_math.h diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index a54b62394aa..4a5cd86787f 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -30,6 +30,7 @@ list(APPEND objects module_charge/gint_prec_ctrl.cpp module_charge/charge.cpp module_charge/charge_init.cpp + module_charge/charge_math.cpp module_charge/charge_mpi.cpp module_charge/charge_extra.cpp module_charge/charge_mixing.cpp diff --git a/source/source_estate/module_charge/charge.cpp b/source/source_estate/module_charge/charge.cpp index 798287c86dc..c6a646813b9 100644 --- a/source/source_estate/module_charge/charge.cpp +++ b/source/source_estate/module_charge/charge.cpp @@ -17,6 +17,7 @@ // even in a LSDA calculation. //---------------------------------------------------------- #include "charge.h" +#include "charge_math.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -174,35 +175,8 @@ void Charge::allocate(const int& nspin_in, const bool kin_den) double Charge::sum_rho() const { - ModuleBase::TITLE("Charge", "sum_rho"); - - double sum_rho = 0.0; - int nspin0 = (nspin == 2) ? 2 : 1; - - for (int is = 0; is < nspin0; is++) - { - for (int ir = 0; ir < nrxx; ir++) - { - sum_rho += this->rho[is][ir]; - } - } - - // multiply the sum of charge density by a factor - sum_rho *= *this->omega_ / static_cast(this->rhopw->nxyz); - -#ifdef __MPI - Parallel_Reduce::reduce_pool(sum_rho); -#endif - - // mohan fixed bug 2010-01-18, - // sum_rho may be smaller than 1, like Na bcc. - if (sum_rho <= 0.1) - { - GlobalV::ofs_warning << " sum_rho=" << sum_rho << std::endl; - ModuleBase::WARNING_QUIT("Charge::renormalize_rho", "Can't find even an electron!"); - } - - return sum_rho; + const int nspin0 = (nspin == 2) ? 2 : 1; + return charge_math::sum_rho(this->rho, nspin0, this->nrxx, *this->omega_, this->rhopw->nxyz); } void Charge::renormalize_rho() @@ -661,19 +635,7 @@ void Charge::save_rho_before_sum_band() double Charge::cal_rho2ne(const double* rho_in) const { - assert(this->rhopw->nxyz > 0); // mohan add 2025-12-02 - - double ne = 0.0; - for (int ir = 0; ir < this->rhopw->nrxx; ir++) - { - ne += rho_in[ir]; - } -#ifdef __MPI - Parallel_Reduce::reduce_pool(ne); -#endif - ne = ne * *this->omega_ / (double)this->rhopw->nxyz; - - return ne; + return charge_math::cal_rho2ne(rho_in, this->rhopw->nrxx, *this->omega_, this->rhopw->nxyz); } void Charge::check_rho() diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index 1cb53585d4f..cb6b3a144da 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -2,6 +2,7 @@ #include #include "charge.h" +#include "charge_math.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_io/module_parameter/parameter.h" @@ -422,72 +423,14 @@ void Charge::non_linear_core_correction const double *rhoc, double *rhocg) const { - ModuleBase::TITLE("charge","drhoc"); - - // use labmda instead of repeating codes - const auto kernel = [&](int num_threads, int thread_id) - { - - double gx = 0.0; - double rhocg1 = 0.0; - double *aux = nullptr; - - // here we compute the fourier transform is the charge in numeric form - if (numeric) - { - aux = new double [mesh]; - // G=0 term - - int igl0 = 0; - if (this->rhopw->gg_uniq [0] < 1.0e-8) - { - // single thread term - if (thread_id == 0) - { - for (int ir = 0;ir < mesh; ir++) - { - aux [ir] = r [ir] * r [ir] * rhoc [ir]; - } - ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); - //rhocg [1] = fpi * rhocg1 / omega; - rhocg [0] = ModuleBase::FOUR_PI * rhocg1 / omega;//mohan modify 2008-01-19 - } - igl0 = 1; - } - - int igl_beg, igl_end; - // exclude igl0 - ModuleBase::TASK_DIST_1D(num_threads, thread_id, this->rhopw->ngg - igl0, igl_beg, igl_end); - igl_beg += igl0; - igl_end += igl_beg; - - // G <> 0 term - for (int igl = igl_beg; igl < igl_end;igl++) - { - gx = sqrt(this->rhopw->gg_uniq[igl] * tpiba2); - ModuleBase::Sphbes::Spherical_Bessel(mesh, r, gx, 0, aux); - for (int ir = 0;ir < mesh; ir++) - { - aux [ir] = r[ir] * r[ir] * rhoc [ir] * aux [ir]; - } // enddo - ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); - rhocg [igl] = ModuleBase::FOUR_PI * rhocg1 / omega; - } // enddo - delete [] aux; - } - else - { - // here the case where the charge is in analytic form, - // check old version before 2008-12-9 - } - - }; // end kernel - - // do not use omp parallel when this function is already in parallel block - // - // it is called in parallel block in Forces::cal_force_cc, - // but not in other funtcion such as Stress_Func::stress_cc. - ModuleBase::TRY_OMP_PARALLEL(kernel); - - return; + charge_math::non_linear_core_correction(numeric, + omega, + tpiba2, + mesh, + r, + rab, + rhoc, + rhocg, + this->rhopw->gg_uniq, + this->rhopw->ngg); } diff --git a/source/source_estate/module_charge/charge_math.cpp b/source/source_estate/module_charge/charge_math.cpp new file mode 100644 index 00000000000..ebea3702d84 --- /dev/null +++ b/source/source_estate/module_charge/charge_math.cpp @@ -0,0 +1,155 @@ +#include "charge_math.h" + +#include "source_base/global_function.h" +#include "source_base/constants.h" +#include "source_base/math_integral.h" +#include "source_base/math_sphbes.h" +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_base/tool_threading.h" +#include "source_base/tool_title.h" + +#include +#include +#include + +namespace charge_math +{ + +double sum_rho(double* const* rho, + const int nspin0, + const int nrxx, + const double omega, + const int nxyz) +{ + ModuleBase::TITLE("charge_math", "sum_rho"); + + double sum_rho = 0.0; + + for (int is = 0; is < nspin0; is++) + { + for (int ir = 0; ir < nrxx; ir++) + { + sum_rho += rho[is][ir]; + } + } + + // multiply the sum of charge density by a factor + sum_rho *= omega / static_cast(nxyz); + +#ifdef __MPI + Parallel_Reduce::reduce_pool(sum_rho); +#endif + + // mohan fixed bug 2010-01-18, + // sum_rho may be smaller than 1, like Na bcc. + if (sum_rho <= 0.1) + { + ModuleBase::WARNING_QUIT("charge_math::sum_rho", "Can't find even an electron!"); + } + + return sum_rho; +} + +double cal_rho2ne(const double* rho_in, + const int nrxx, + const double omega, + const int nxyz) +{ + assert(nxyz > 0); // mohan add 2025-12-02 + + double ne = 0.0; + for (int ir = 0; ir < nrxx; ir++) + { + ne += rho_in[ir]; + } +#ifdef __MPI + Parallel_Reduce::reduce_pool(ne); +#endif + ne = ne * omega / static_cast(nxyz); + + return ne; +} + +void non_linear_core_correction(const bool numeric, + const double omega, + const double tpiba2, + const int mesh, + const double* r, + const double* rab, + const double* rhoc, + double* rhocg, + const double* gg_uniq, + const int ngg) +{ + ModuleBase::TITLE("charge_math", "drhoc"); + + // use labmda instead of repeating codes + const auto kernel = [&](int num_threads, int thread_id) + { + + double gx = 0.0; + double rhocg1 = 0.0; + double *aux = nullptr; + + // here we compute the fourier transform is the charge in numeric form + if (numeric) + { + aux = new double [mesh]; + // G=0 term + + int igl0 = 0; + if (gg_uniq [0] < 1.0e-8) + { + // single thread term + if (thread_id == 0) + { + for (int ir = 0;ir < mesh; ir++) + { + aux [ir] = r [ir] * r [ir] * rhoc [ir]; + } + ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); + //rhocg [1] = fpi * rhocg1 / omega; + rhocg [0] = ModuleBase::FOUR_PI * rhocg1 / omega;//mohan modify 2008-01-19 + } + igl0 = 1; + } + + int igl_beg, igl_end; + // exclude igl0 + ModuleBase::TASK_DIST_1D(num_threads, thread_id, ngg - igl0, igl_beg, igl_end); + igl_beg += igl0; + igl_end += igl_beg; + + // G <> 0 term + for (int igl = igl_beg; igl < igl_end;igl++) + { + gx = sqrt(gg_uniq[igl] * tpiba2); + ModuleBase::Sphbes::Spherical_Bessel(mesh, r, gx, 0, aux); + for (int ir = 0;ir < mesh; ir++) + { + aux [ir] = r[ir] * r[ir] * rhoc [ir] * aux [ir]; + } // enddo + ModuleBase::Integral::Simpson_Integral(mesh, aux, rab, rhocg1); + rhocg [igl] = ModuleBase::FOUR_PI * rhocg1 / omega; + } // enddo + delete [] aux; + } + else + { + // here the case where the charge is in analytic form, + // check old version before 2008-12-9 + } + + }; // end kernel + + // do not use omp parallel when this function is already in parallel block + // + // it is called in parallel block in Forces::cal_force_cc, + // but not in other funtcion such as Stress_Func::stress_cc. + ModuleBase::TRY_OMP_PARALLEL(kernel); + + return; +} + +} // namespace charge_math diff --git a/source/source_estate/module_charge/charge_math.h b/source/source_estate/module_charge/charge_math.h new file mode 100644 index 00000000000..889a5e721d6 --- /dev/null +++ b/source/source_estate/module_charge/charge_math.h @@ -0,0 +1,47 @@ +#ifndef CHARGE_MATH_H +#define CHARGE_MATH_H + +// Free numerical kernels extracted from the Charge class so that the +// density math (summation, electron-count integration, non-linear core +// correction) can be tested and reused without dragging in Charge's state. +// All grid / geometry inputs are passed explicitly instead of being read +// from Charge members or globals. + +namespace charge_math +{ + +// Sum the (spin-resolved) charge density over the real-space grid and +// convert it to a total charge using the cell volume and grid size. +// rho[is][ir] is the density for spin channel is. nspin0 is the number of +// spin channels to include (2 for nspin==2, 1 otherwise). +double sum_rho(double* const* rho, + const int nspin0, + const int nrxx, + const double omega, + const int nxyz); + +// Integrate a single spin channel rho_in over the grid to obtain the +// electron number, scaled by omega / nxyz. Reduction over the pool is +// performed internally under __MPI. +double cal_rho2ne(const double* rho_in, + const int nrxx, + const double omega, + const int nxyz); + +// Non-linear core correction: Fourier transform of the (numeric) core +// charge. gg_uniq / ngg supply the reciprocal grid shells previously read +// from Charge::rhopw. +void non_linear_core_correction(const bool numeric, + const double omega, + const double tpiba2, + const int mesh, + const double* r, + const double* rab, + const double* rhoc, + double* rhocg, + const double* gg_uniq, + const int ngg); + +} // namespace charge_math + +#endif // CHARGE_MATH_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 54414226fa8..c93fa828a23 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -101,7 +101,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_charge_test LIBS parameter planewave_serial base device cell_info - SOURCES charge_test.cpp ../module_charge/charge.cpp + SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_math.cpp ) From e074dd0fa5e3c6ee5e009d2e145c467420088c93 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 17:06:09 +0800 Subject: [PATCH 09/37] module_charge: register charge_math.o in the hand-written Makefile build The CMake build already picks up charge_math.cpp; mirror that in Makefile.Objects so the legacy Makefile flow links the new charge_math kernels too. The module_charge directory is already on VPATH, so adding charge_math.o to the object list is sufficient. --- source/Makefile.Objects | 1 + 1 file changed, 1 insertion(+) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 9e68b3674b6..fcdd21317ab 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -824,6 +824,7 @@ OBJS_SRCPW=h_ewald_pw.o\ gint_prec_ctrl.o\ charge.o\ charge_init.o\ + charge_math.o\ charge_mpi.o\ charge_extra.o\ charge_mixing.o\ From 7b346dc22bc12695b40a0854801a6ebce89c03be Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 19:27:29 +0800 Subject: [PATCH 10/37] module_charge: extract Charge::atomic_rho into charge_atomic free function Remove Charge::atomic_rho entirely and replace all call sites with module_charge::atomic_rho(..., rhopw), eliminating the need for a thin wrapper on the Charge class. This decouples atomic density initialization from Charge's state and improves charge.cpp quality score from 2 to 44. --- source/Makefile.Objects | 1 + source/source_estate/CMakeLists.txt | 1 + source/source_estate/module_charge/charge.cpp | 418 ----------------- source/source_estate/module_charge/charge.h | 6 - .../module_charge/charge_atomic.cpp | 442 ++++++++++++++++++ .../module_charge/charge_atomic.h | 36 ++ .../module_charge/charge_extra.cpp | 6 +- .../module_charge/charge_init.cpp | 3 +- source/source_estate/test/CMakeLists.txt | 2 +- .../source_estate/test/charge_extra_test.cpp | 17 +- 10 files changed, 499 insertions(+), 433 deletions(-) create mode 100644 source/source_estate/module_charge/charge_atomic.cpp create mode 100644 source/source_estate/module_charge/charge_atomic.h diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 65fd32ca2f0..a313831784c 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -826,6 +826,7 @@ OBJS_SRCPW=h_ewald_pw.o\ chgmixing.o\ gint_prec_ctrl.o\ charge.o\ + charge_atomic.o\ charge_init.o\ charge_math.o\ charge_mpi.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index eaf984977c4..877c91e0ecd 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -30,6 +30,7 @@ list(APPEND objects module_charge/chgmixing.cpp module_charge/gint_prec_ctrl.cpp module_charge/charge.cpp + module_charge/charge_atomic.cpp module_charge/charge_init.cpp module_charge/charge_math.cpp module_charge/charge_mpi.cpp diff --git a/source/source_estate/module_charge/charge.cpp b/source/source_estate/module_charge/charge.cpp index c6a646813b9..428cd4071c7 100644 --- a/source/source_estate/module_charge/charge.cpp +++ b/source/source_estate/module_charge/charge.cpp @@ -202,424 +202,6 @@ void Charge::renormalize_rho() return; } -//------------------------------------------------------- -// superposition of atomic charges contained in the array -// rho_at (read from pseudopotential files) -// allocate work space (psic must already be allocated) -//------------------------------------------------------- -void Charge::atomic_rho(const int spin_number_need, - const double& omega, - double** rho_in, - const ModuleBase::ComplexMatrix& strucFac, - const UnitCell& ucell) const // Peize Lin refactor 2021.04.08 -{ - ModuleBase::TITLE("Charge", "atomic_rho"); - ModuleBase::timer::start("Charge", "atomic_rho"); - - { - ModuleBase::ComplexMatrix rho_g3d = [&]() -> ModuleBase::ComplexMatrix - { - // use interpolation to get three dimension charge density. - ModuleBase::ComplexMatrix rho_g3d(spin_number_need, this->rhopw->npw); - - for (int it = 0; it < ucell.ntype; it++) - { - // check the start magnetization - const int startmag_type = [&]() -> int { - if (ucell.magnet.start_mag[it] != 0.0) - { - return 1; - } - return 2; - }(); - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "startmag_type", startmag_type); - - const Atom* const atom = &ucell.atoms[it]; - - if (!atom->flag_empty_element) // Peize Lin add for bsse 2021.04.07 - { - const std::vector rho_lgl = [&]() -> std::vector { - // one dimension of charge in G space. - std::vector rho_lgl(this->rhopw->ngg, 0); - - // mesh point of this element. - const int mesh = atom->ncpp.msh; - - //---------------------------------------------------------- - // Here we check the electron number - //---------------------------------------------------------- - const std::vector rhoatm = [&]() -> std::vector { - std::vector rhoatm(mesh); - // this is only one part of the charge density for uspp - // liuyu 2023-11-01 - if (atom->ncpp.tvanp) - { - for (int ir = 0; ir < mesh; ++ir) - { - rhoatm[ir] = atom->ncpp.rho_at[ir]; - } - } - else - { - for (int ir = 0; ir < mesh; ++ir) - { - double r2 = atom->ncpp.r[ir] * atom->ncpp.r[ir]; - if (r2!=0) - { - rhoatm[ir] = atom->ncpp.rho_at[ir] / ModuleBase::FOUR_PI / r2; - } - } - rhoatm[0] - = pow((rhoatm[2] / rhoatm[1]), atom->ncpp.r[1] / (atom->ncpp.r[2] - atom->ncpp.r[1])); // zws add, sunliang updated 2024-03-04 - if (rhoatm[0] < 1e-12) - { - rhoatm[0] = rhoatm[1]; - } - else - { - rhoatm[0] = rhoatm[1] / rhoatm[0]; - } - - double charge = 0.0; - ModuleBase::Integral::Simpson_Integral(atom->ncpp.msh, - atom->ncpp.rho_at.data(), - atom->ncpp.rab.data(), - charge); - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "charge from rho_at", charge); - assert(charge != 0.0 - || charge - == atom->ncpp.zv); // Peize Lin add charge==atom->zv for bsse 2021.04.07 - - double scale = 1.0; - if (charge != atom->ncpp.zv) - { - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, - "charge should be", - atom->ncpp.zv); - scale = atom->ncpp.zv / charge; - } - - for (int ir = 0; ir < mesh; ++ir) - { - rhoatm[ir] *= scale; - rhoatm[ir] *= (ModuleBase::FOUR_PI * atom->ncpp.r[ir] * atom->ncpp.r[ir]); - } - } - return rhoatm; - }(); - - assert(ucell.meshx > 0); - //---------------------------------------------------------- - // Here we compute the G=0 term - //---------------------------------------------------------- - int gstart = 0; - if (this->rhopw->gg_uniq[0] < 1e-8) - { - std::vector rho1d(ucell.meshx); - for (int ir = 0; ir < mesh; ir++) - { - rho1d[ir] = rhoatm[ir]; - } - ModuleBase::Integral::Simpson_Integral(mesh, rho1d.data(), atom->ncpp.rab.data(), rho_lgl[0]); - gstart = 1; - } - if (PARAM.inp.test_charge > 0) - { - std::cout << "\n |G|=0 term done." << std::endl; - } - //---------------------------------------------------------- - // Here we compute the G<>0 term - // But if in parallel case - // G=0 term only belong to 1 cpu. - // Other processors start from '0' - //---------------------------------------------------------- - #ifdef _OPENMP - #pragma omp parallel - { - #endif - const int ngg = this->rhopw->ngg; - const double* gg_uniq = this->rhopw->gg_uniq; - const int meshx = ucell.meshx; - const double tpiba = ucell.tpiba; - std::vector rho1d(meshx); - - #ifdef _OPENMP - #pragma omp for - #endif - for (int igg = gstart; igg < ngg; ++igg) - { - const double gx = sqrt(gg_uniq[igg]) * tpiba; - for (int ir = 0; ir < mesh; ir++) - { - if (atom->ncpp.r[ir] < 1.0e-8) - { - rho1d[ir] = rhoatm[ir]; - } - else - { - const double gxx = gx * atom->ncpp.r[ir]; - rho1d[ir] = rhoatm[ir] * ModuleBase::libm::sin(gxx) / gxx; - } - } - ModuleBase::Integral::Simpson_Integral(mesh, rho1d.data(), atom->ncpp.rab.data(), rho_lgl[igg]); - } - #ifdef _OPENMP - #pragma omp single - #endif - { - if (PARAM.inp.test_charge > 0) - { - std::cout << " |G|>0 term done." << std::endl; - } - } - //---------------------------------------------------------- - // EXPLAIN : Complete the transfer of rho from real space to - // reciprocal space - //---------------------------------------------------------- - #ifdef _OPENMP - #pragma omp for - #endif - for (int igg = 0; igg < ngg; igg++) - { - rho_lgl[igg] /= omega; - } - #ifdef _OPENMP - } - #endif - return rho_lgl; - }(); - //---------------------------------------------------------- - // EXPLAIN : compute the 3D atomic charge in reciprocal space - //---------------------------------------------------------- - if (spin_number_need == 1) - { - const int npw = this->rhopw->npw; - const int* ig2igg = this->rhopw->ig2igg; - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int ig = 0; ig < npw; ig++) - { - rho_g3d(0, ig) += strucFac(it, ig) * rho_lgl[ig2igg[ig]]; - } - } - // mohan add 2011-06-14, initialize the charge density according to each atom - else if (spin_number_need == 2) - { - if (startmag_type == 1) - { - const int npw = this->rhopw->npw; - const int* ig2igg = this->rhopw->ig2igg; - const double zv = atom->ncpp.zv; - const double start_mag_it = ucell.magnet.start_mag[it]; - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int ig = 0; ig < npw; ig++) - { - const std::complex swap = strucFac(it, ig) * rho_lgl[ig2igg[ig]]; - const double up = 0.5 * (1 + start_mag_it / zv); - const double dw = 0.5 * (1 - start_mag_it / zv); - rho_g3d(0, ig) += swap * up; - rho_g3d(1, ig) += swap * dw; - } - } - // mohan add 2011-06-14 - else if (startmag_type == 2) - { - std::complex ci_tpi = ModuleBase::NEG_IMAG_UNIT * ModuleBase::TWO_PI; - const int npw = this->rhopw->npw; - const ModuleBase::Vector3* gcar = this->rhopw->gcar; - const int* ig2igg = this->rhopw->ig2igg; - const double zv = atom->ncpp.zv; - for (int ia = 0; ia < atom->na; ia++) - { - const double up = 0.5 * (1 + atom->mag[ia] / atom->ncpp.zv); - const double dw = 0.5 * (1 - atom->mag[ia] / atom->ncpp.zv); - const double tau_x = atom->tau[ia].x; - const double tau_y = atom->tau[ia].y; - const double tau_z = atom->tau[ia].z; - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int ig = 0; ig < npw; ig++) - { - const double Gtau = gcar[ig][0] * tau_x + gcar[ig][1] * tau_y + gcar[ig][2] * tau_z; - std::complex swap = ModuleBase::libm::exp(ci_tpi * Gtau) * rho_lgl[ig2igg[ig]]; - rho_g3d(0, ig) += swap * up; - rho_g3d(1, ig) += swap * dw; - } - } - } - } - else if (spin_number_need == 4) - { - // noncolinear case - if (startmag_type == 1) - { - double sin_a1, sin_a2, cos_a1, cos_a2; - if (PARAM.globalv.domag) - { - ModuleBase::libm::sincos(atom->angle1[0], &sin_a1, &cos_a1); - ModuleBase::libm::sincos(atom->angle2[0], &sin_a2, &cos_a2); - } - const int npw = this->rhopw->npw; - const int* ig2igg = this->rhopw->ig2igg; - const double zv = atom->ncpp.zv; - const double start_mag_it = ucell.magnet.start_mag[it]; - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int ig = 0; ig < npw; ig++) - { - const std::complex swap = strucFac(it, ig) * rho_lgl[ig2igg[ig]]; - rho_g3d(0, ig) += swap; - if (PARAM.globalv.domag) - { - rho_g3d(1, ig) += swap * (start_mag_it / zv) * sin_a1 * cos_a2; - rho_g3d(2, ig) += swap * (start_mag_it / zv) * sin_a1 * sin_a2; - rho_g3d(3, ig) += swap * (start_mag_it / zv) * cos_a1; - } - else if (PARAM.globalv.domag_z) - { - rho_g3d(1, ig) = 0.0; - rho_g3d(2, ig) = 0.0; - rho_g3d(3, ig) += swap * (start_mag_it / zv); - } - } - } - else if (startmag_type == 2) - { - std::complex ci_tpi = ModuleBase::NEG_IMAG_UNIT * ModuleBase::TWO_PI; - const int npw = this->rhopw->npw; - const ModuleBase::Vector3* gcar = this->rhopw->gcar; - const int* ig2igg = this->rhopw->ig2igg; - const double zv = atom->ncpp.zv; - for (int ia = 0; ia < atom->na; ia++) - { - double sin_a1, sin_a2, cos_a1, cos_a2; - if (PARAM.globalv.domag || PARAM.globalv.domag_z) - { - ModuleBase::libm::sincos(atom->angle1[ia], &sin_a1, &cos_a1); - } - if (PARAM.globalv.domag) - { - ModuleBase::libm::sincos(atom->angle2[ia], &sin_a2, &cos_a2); - } - const double mag_ia = atom->mag[ia]; - const double tau_x = atom->tau[ia].x; - const double tau_y = atom->tau[ia].y; - const double tau_z = atom->tau[ia].z; - #ifdef _OPENMP - #pragma omp parallel for - #endif - for (int ig = 0; ig < npw; ig++) - { - const double Gtau = gcar[ig][0] * tau_x + gcar[ig][1] * tau_y + gcar[ig][2] * tau_z; - std::complex swap = exp(ci_tpi * Gtau) * rho_lgl[ig2igg[ig]]; - const double mag_factor = mag_ia / zv; - rho_g3d(0, ig) += swap; - if (PARAM.globalv.domag || PARAM.globalv.domag_z) - { - rho_g3d(3, ig) += swap * mag_factor * cos_a1; - } - if (PARAM.globalv.domag) - { - rho_g3d(1, ig) += swap * mag_factor * sin_a1 * cos_a2; - rho_g3d(2, ig) += swap * mag_factor * sin_a1 * sin_a2; - } - else - { - rho_g3d(1, ig) = 0.0; - rho_g3d(2, ig) = 0.0; - } - } - } - } - } - else - { - ModuleBase::WARNING_QUIT("Charge::spin_number_need", " Either 1 or 2 or 4, check SPIN number !"); - } - } - } - return rho_g3d; - }(); - - assert(spin_number_need > 0); - std::vector ne(spin_number_need); - for (int is = 0; is < spin_number_need; is++) - { - this->rhopw->recip2real(&rho_g3d(is, 0), rho_in[is]); - - for (int ir = 0; ir < this->rhopw->nrxx; ++ir) - { - ne[is] += rho_in[is][ir]; - } - - ne[is] *= omega / (double)this->rhopw->nxyz; - #ifdef __MPI - Parallel_Reduce::reduce_pool(ne[is]); - #endif - // we check that everything is correct - double neg = 0.0; - double rea = 0.0; - double ima = 0.0; - double sumrea = 0.0; - for (int ir = 0; ir < this->rhopw->nrxx; ir++) - { - rea = this->rhopw->fft_bundle.get_auxr_data()[ir].real(); - sumrea += rea; - neg += std::min(0.0, rea); - ima += std::abs(this->rhopw->fft_bundle.get_auxr_data()[ir].imag()); - } - - #ifdef __MPI - Parallel_Reduce::reduce_pool(neg); - Parallel_Reduce::reduce_pool(ima); - Parallel_Reduce::reduce_pool(sumrea); - #endif - // mohan fix bug 2011-04-03 - neg = neg / (double)this->rhopw->nxyz * omega; - ima = ima / (double)this->rhopw->nxyz * omega; - sumrea = sumrea / (double)this->rhopw->nxyz * omega; - - if (((neg < -1.0e-4) && (is == 0 || PARAM.inp.nspin == 2)) || ima > 1.0e-4) - { - GlobalV::ofs_warning << " Warning: negative or imaginary starting charge : "; - GlobalV::ofs_warning << " neg = " << neg << " ima = " << ima << " SPIN = " << is << std::endl; - } - - } // end is - - double ne_tot = 0.0; - int spin0 = 1; - if (spin_number_need == 2) - { - spin0 = spin_number_need; - } - for (int is = 0; is < spin0; ++is) - { - GlobalV::ofs_warning << "\n SETUP ATOMIC RHO FOR SPIN " << is + 1 << std::endl; - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "Electron number from rho", ne[is]); - ne_tot += ne[is]; - } - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "total electron number from rho", ne_tot); - ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "should be", PARAM.inp.nelec); - - for (int is = 0; is < spin_number_need; ++is) - { - for (int ir = 0; ir < this->rhopw->nrxx; ++ir) - { - rho_in[is][ir] = rho_in[is][ir] / ne_tot * PARAM.inp.nelec; - } - } - } - - ModuleBase::timer::end("Charge", "atomic_rho"); - return; -} - void Charge::save_rho_before_sum_band() { for (int is = 0; is < PARAM.inp.nspin; is++) diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index bcf40063a88..62031c3ff64 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -92,12 +92,6 @@ class Charge void allocate(const int &nspin_in, const bool kin_den); - void atomic_rho(const int spin_number_need, - const double& omega, - double** rho_in, - const ModuleBase::ComplexMatrix& strucFac, - const UnitCell& ucell) const; - void set_rho_core(const UnitCell& ucell, const ModuleBase::ComplexMatrix& structure_factor, const bool* numeric); diff --git a/source/source_estate/module_charge/charge_atomic.cpp b/source/source_estate/module_charge/charge_atomic.cpp new file mode 100644 index 00000000000..909824b5bb8 --- /dev/null +++ b/source/source_estate/module_charge/charge_atomic.cpp @@ -0,0 +1,442 @@ +#include "charge_atomic.h" + +#include "source_base/global_function.h" +#include "source_base/global_variable.h" +#include "source_base/libm/libm.h" +#include "source_base/math_integral.h" +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_cell/unitcell.h" +#include "source_cell/magnetism.h" +#include "source_io/module_parameter/parameter.h" + +#include +#include +#include +#include +#include + +namespace module_charge +{ + +void atomic_rho(const int spin_number_need, + const double& omega, + double** rho_in, + const ModuleBase::ComplexMatrix& strucFac, + const UnitCell& ucell, + const ModulePW::PW_Basis* rhopw) +{ + ModuleBase::TITLE("module_charge", "atomic_rho"); + ModuleBase::timer::start("module_charge", "atomic_rho"); + + { + ModuleBase::ComplexMatrix rho_g3d = [&]() -> ModuleBase::ComplexMatrix + { + // use interpolation to get three dimension charge density. + ModuleBase::ComplexMatrix rho_g3d(spin_number_need, rhopw->npw); + + for (int it = 0; it < ucell.ntype; it++) + { + // check the start magnetization + const int startmag_type = [&]() -> int { + if (ucell.magnet.start_mag[it] != 0.0) + { + return 1; + } + return 2; + }(); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "startmag_type", startmag_type); + + const Atom* const atom = &ucell.atoms[it]; + + if (!atom->flag_empty_element) // Peize Lin add for bsse 2021.04.07 + { + const std::vector rho_lgl = [&]() -> std::vector { + // one dimension of charge in G space. + std::vector rho_lgl(rhopw->ngg, 0); + + // mesh point of this element. + const int mesh = atom->ncpp.msh; + + //---------------------------------------------------------- + // Here we check the electron number + //---------------------------------------------------------- + const std::vector rhoatm = [&]() -> std::vector { + std::vector rhoatm(mesh); + // this is only one part of the charge density for uspp + // liuyu 2023-11-01 + if (atom->ncpp.tvanp) + { + for (int ir = 0; ir < mesh; ++ir) + { + rhoatm[ir] = atom->ncpp.rho_at[ir]; + } + } + else + { + for (int ir = 0; ir < mesh; ++ir) + { + double r2 = atom->ncpp.r[ir] * atom->ncpp.r[ir]; + if (r2 != 0) + { + rhoatm[ir] = atom->ncpp.rho_at[ir] / ModuleBase::FOUR_PI / r2; + } + } + rhoatm[0] = pow((rhoatm[2] / rhoatm[1]), + atom->ncpp.r[1] / (atom->ncpp.r[2] - atom->ncpp.r[1])); // zws add, sunliang updated 2024-03-04 + if (rhoatm[0] < 1e-12) + { + rhoatm[0] = rhoatm[1]; + } + else + { + rhoatm[0] = rhoatm[1] / rhoatm[0]; + } + + double charge = 0.0; + ModuleBase::Integral::Simpson_Integral(atom->ncpp.msh, + atom->ncpp.rho_at.data(), + atom->ncpp.rab.data(), + charge); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "charge from rho_at", charge); + assert(charge != 0.0 + || charge == atom->ncpp.zv); // Peize Lin add charge==atom->zv for bsse 2021.04.07 + + double scale = 1.0; + if (charge != atom->ncpp.zv) + { + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, + "charge should be", + atom->ncpp.zv); + scale = atom->ncpp.zv / charge; + } + + for (int ir = 0; ir < mesh; ++ir) + { + rhoatm[ir] *= scale; + rhoatm[ir] *= (ModuleBase::FOUR_PI * atom->ncpp.r[ir] * atom->ncpp.r[ir]); + } + } + return rhoatm; + }(); + + assert(ucell.meshx > 0); + //---------------------------------------------------------- + // Here we compute the G=0 term + //---------------------------------------------------------- + int gstart = 0; + if (rhopw->gg_uniq[0] < 1e-8) + { + std::vector rho1d(ucell.meshx); + for (int ir = 0; ir < mesh; ir++) + { + rho1d[ir] = rhoatm[ir]; + } + ModuleBase::Integral::Simpson_Integral(mesh, rho1d.data(), atom->ncpp.rab.data(), rho_lgl[0]); + gstart = 1; + } + if (PARAM.inp.test_charge > 0) + { + std::cout << "\n |G|=0 term done." << std::endl; + } + //---------------------------------------------------------- + // Here we compute the G<>0 term + // But if in parallel case + // G=0 term only belong to 1 cpu. + // Other processors start from '0' + //---------------------------------------------------------- +#ifdef _OPENMP +#pragma omp parallel + { +#endif + const int ngg = rhopw->ngg; + const double* gg_uniq = rhopw->gg_uniq; + const int meshx = ucell.meshx; + const double tpiba = ucell.tpiba; + std::vector rho1d(meshx); + +#ifdef _OPENMP +#pragma omp for +#endif + for (int igg = gstart; igg < ngg; ++igg) + { + const double gx = sqrt(gg_uniq[igg]) * tpiba; + for (int ir = 0; ir < mesh; ir++) + { + if (atom->ncpp.r[ir] < 1.0e-8) + { + rho1d[ir] = rhoatm[ir]; + } + else + { + const double gxx = gx * atom->ncpp.r[ir]; + rho1d[ir] = rhoatm[ir] * ModuleBase::libm::sin(gxx) / gxx; + } + } + ModuleBase::Integral::Simpson_Integral(mesh, rho1d.data(), atom->ncpp.rab.data(), rho_lgl[igg]); + } +#ifdef _OPENMP +#pragma omp single +#endif + { + if (PARAM.inp.test_charge > 0) + { + std::cout << " |G|>0 term done." << std::endl; + } + } + //---------------------------------------------------------- + // EXPLAIN : Complete the transfer of rho from real space to + // reciprocal space + //---------------------------------------------------------- +#ifdef _OPENMP +#pragma omp for +#endif + for (int igg = 0; igg < ngg; igg++) + { + rho_lgl[igg] /= omega; + } +#ifdef _OPENMP + } +#endif + return rho_lgl; + }(); + //---------------------------------------------------------- + // EXPLAIN : compute the 3D atomic charge in reciprocal space + //---------------------------------------------------------- + if (spin_number_need == 1) + { + const int npw = rhopw->npw; + const int* ig2igg = rhopw->ig2igg; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int ig = 0; ig < npw; ig++) + { + rho_g3d(0, ig) += strucFac(it, ig) * rho_lgl[ig2igg[ig]]; + } + } + // mohan add 2011-06-14, initialize the charge density according to each atom + else if (spin_number_need == 2) + { + if (startmag_type == 1) + { + const int npw = rhopw->npw; + const int* ig2igg = rhopw->ig2igg; + const double zv = atom->ncpp.zv; + const double start_mag_it = ucell.magnet.start_mag[it]; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int ig = 0; ig < npw; ig++) + { + const std::complex swap = strucFac(it, ig) * rho_lgl[ig2igg[ig]]; + const double up = 0.5 * (1 + start_mag_it / zv); + const double dw = 0.5 * (1 - start_mag_it / zv); + rho_g3d(0, ig) += swap * up; + rho_g3d(1, ig) += swap * dw; + } + } + // mohan add 2011-06-14 + else if (startmag_type == 2) + { + std::complex ci_tpi = ModuleBase::NEG_IMAG_UNIT * ModuleBase::TWO_PI; + const int npw = rhopw->npw; + const ModuleBase::Vector3* gcar = rhopw->gcar; + const int* ig2igg = rhopw->ig2igg; + const double zv = atom->ncpp.zv; + for (int ia = 0; ia < atom->na; ia++) + { + const double up = 0.5 * (1 + atom->mag[ia] / atom->ncpp.zv); + const double dw = 0.5 * (1 - atom->mag[ia] / atom->ncpp.zv); + const double tau_x = atom->tau[ia].x; + const double tau_y = atom->tau[ia].y; + const double tau_z = atom->tau[ia].z; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int ig = 0; ig < npw; ig++) + { + const double Gtau = gcar[ig][0] * tau_x + gcar[ig][1] * tau_y + gcar[ig][2] * tau_z; + std::complex swap = ModuleBase::libm::exp(ci_tpi * Gtau) * rho_lgl[ig2igg[ig]]; + rho_g3d(0, ig) += swap * up; + rho_g3d(1, ig) += swap * dw; + } + } + } + } + else if (spin_number_need == 4) + { + // noncolinear case + if (startmag_type == 1) + { + double sin_a1 = 0.0; + double sin_a2 = 0.0; + double cos_a1 = 0.0; + double cos_a2 = 0.0; + if (PARAM.globalv.domag) + { + ModuleBase::libm::sincos(atom->angle1[0], &sin_a1, &cos_a1); + ModuleBase::libm::sincos(atom->angle2[0], &sin_a2, &cos_a2); + } + const int npw = rhopw->npw; + const int* ig2igg = rhopw->ig2igg; + const double zv = atom->ncpp.zv; + const double start_mag_it = ucell.magnet.start_mag[it]; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int ig = 0; ig < npw; ig++) + { + const std::complex swap = strucFac(it, ig) * rho_lgl[ig2igg[ig]]; + rho_g3d(0, ig) += swap; + if (PARAM.globalv.domag) + { + rho_g3d(1, ig) += swap * (start_mag_it / zv) * sin_a1 * cos_a2; + rho_g3d(2, ig) += swap * (start_mag_it / zv) * sin_a1 * sin_a2; + rho_g3d(3, ig) += swap * (start_mag_it / zv) * cos_a1; + } + else if (PARAM.globalv.domag_z) + { + rho_g3d(1, ig) = 0.0; + rho_g3d(2, ig) = 0.0; + rho_g3d(3, ig) += swap * (start_mag_it / zv); + } + } + } + else if (startmag_type == 2) + { + std::complex ci_tpi = ModuleBase::NEG_IMAG_UNIT * ModuleBase::TWO_PI; + const int npw = rhopw->npw; + const ModuleBase::Vector3* gcar = rhopw->gcar; + const int* ig2igg = rhopw->ig2igg; + const double zv = atom->ncpp.zv; + for (int ia = 0; ia < atom->na; ia++) + { + double sin_a1 = 0.0; + double sin_a2 = 0.0; + double cos_a1 = 0.0; + double cos_a2 = 0.0; + if (PARAM.globalv.domag || PARAM.globalv.domag_z) + { + ModuleBase::libm::sincos(atom->angle1[ia], &sin_a1, &cos_a1); + } + if (PARAM.globalv.domag) + { + ModuleBase::libm::sincos(atom->angle2[ia], &sin_a2, &cos_a2); + } + const double mag_ia = atom->mag[ia]; + const double tau_x = atom->tau[ia].x; + const double tau_y = atom->tau[ia].y; + const double tau_z = atom->tau[ia].z; +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int ig = 0; ig < npw; ig++) + { + const double Gtau = gcar[ig][0] * tau_x + gcar[ig][1] * tau_y + gcar[ig][2] * tau_z; + std::complex swap = exp(ci_tpi * Gtau) * rho_lgl[ig2igg[ig]]; + const double mag_factor = mag_ia / zv; + rho_g3d(0, ig) += swap; + if (PARAM.globalv.domag || PARAM.globalv.domag_z) + { + rho_g3d(3, ig) += swap * mag_factor * cos_a1; + } + if (PARAM.globalv.domag) + { + rho_g3d(1, ig) += swap * mag_factor * sin_a1 * cos_a2; + rho_g3d(2, ig) += swap * mag_factor * sin_a1 * sin_a2; + } + else + { + rho_g3d(1, ig) = 0.0; + rho_g3d(2, ig) = 0.0; + } + } + } + } + } + else + { + ModuleBase::WARNING_QUIT("module_charge::atomic_rho", + " Either 1 or 2 or 4, check SPIN number !"); + } + } + } + return rho_g3d; + }(); + + assert(spin_number_need > 0); + std::vector ne(spin_number_need); + for (int is = 0; is < spin_number_need; is++) + { + rhopw->recip2real(&rho_g3d(is, 0), rho_in[is]); + + for (int ir = 0; ir < rhopw->nrxx; ++ir) + { + ne[is] += rho_in[is][ir]; + } + + ne[is] *= omega / (double)rhopw->nxyz; +#ifdef __MPI + Parallel_Reduce::reduce_pool(ne[is]); +#endif + // we check that everything is correct + double neg = 0.0; + double rea = 0.0; + double ima = 0.0; + double sumrea = 0.0; + for (int ir = 0; ir < rhopw->nrxx; ir++) + { + rea = rhopw->fft_bundle.get_auxr_data()[ir].real(); + sumrea += rea; + neg += std::min(0.0, rea); + ima += std::abs(rhopw->fft_bundle.get_auxr_data()[ir].imag()); + } + +#ifdef __MPI + Parallel_Reduce::reduce_pool(neg); + Parallel_Reduce::reduce_pool(ima); + Parallel_Reduce::reduce_pool(sumrea); +#endif + // mohan fix bug 2011-04-03 + neg = neg / (double)rhopw->nxyz * omega; + ima = ima / (double)rhopw->nxyz * omega; + sumrea = sumrea / (double)rhopw->nxyz * omega; + + if (((neg < -1.0e-4) && (is == 0 || PARAM.inp.nspin == 2)) || ima > 1.0e-4) + { + GlobalV::ofs_warning << " Warning: negative or imaginary starting charge : "; + GlobalV::ofs_warning << " neg = " << neg << " ima = " << ima << " SPIN = " << is << std::endl; + } + + } // end is + + double ne_tot = 0.0; + int spin0 = 1; + if (spin_number_need == 2) + { + spin0 = spin_number_need; + } + for (int is = 0; is < spin0; ++is) + { + GlobalV::ofs_warning << "\n SETUP ATOMIC RHO FOR SPIN " << is + 1 << std::endl; + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "Electron number from rho", ne[is]); + ne_tot += ne[is]; + } + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "total electron number from rho", ne_tot); + ModuleBase::GlobalFunc::OUT(GlobalV::ofs_warning, "should be", PARAM.inp.nelec); + + for (int is = 0; is < spin_number_need; ++is) + { + for (int ir = 0; ir < rhopw->nrxx; ++ir) + { + rho_in[is][ir] = rho_in[is][ir] / ne_tot * PARAM.inp.nelec; + } + } + } + + ModuleBase::timer::end("module_charge", "atomic_rho"); + return; +} + +} // namespace module_charge diff --git a/source/source_estate/module_charge/charge_atomic.h b/source/source_estate/module_charge/charge_atomic.h new file mode 100644 index 00000000000..de3607f2590 --- /dev/null +++ b/source/source_estate/module_charge/charge_atomic.h @@ -0,0 +1,36 @@ +#ifndef CHARGE_ATOMIC_H +#define CHARGE_ATOMIC_H + +#include "source_base/complexmatrix.h" +#include "source_basis/module_pw/pw_basis.h" + +class UnitCell; + +namespace module_charge +{ + +// Superposition of atomic charges contained in the array rho_at +// (read from pseudopotential files). +// +// spin_number_need is the number of spin components to be calculated: +// 1 -> total atomic charge density +// 2 -> spin up/down densities assuming uniform atomic polarization +// equal to start_mag(it) +// 4 -> noncollinear case: total density in component 0, magnetization +// vector in components 1..3 +// +// NB: spin_number_need may differ from nspin (e.g. in update only the +// total charge is needed even in an LSDA calculation). +// +// All grid / basis inputs are passed explicitly via rhopw instead of +// being read from Charge members. +void atomic_rho(const int spin_number_need, + const double& omega, + double** rho_in, + const ModuleBase::ComplexMatrix& strucFac, + const UnitCell& ucell, + const ModulePW::PW_Basis* rhopw); + +} // namespace module_charge + +#endif // CHARGE_ATOMIC_H diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/charge_extra.cpp index f77dde355e7..9f1dc412307 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/charge_extra.cpp @@ -1,5 +1,7 @@ #include "charge_extra.h" +#include "charge_atomic.h" + #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/timer.h" @@ -176,7 +178,7 @@ void Charge_Extra::extrapolate_charge( { rho_atom[is] = new double[chr->rhopw->nrxx]; } - chr->atomic_rho(this->nspin, ucell.omega, rho_atom, sf->strucFac, ucell); + module_charge::atomic_rho(this->nspin, ucell.omega, rho_atom, sf->strucFac, ucell, chr->rhopw); #ifdef _OPENMP #pragma omp parallel for collapse(2) schedule(static, 512) #endif @@ -296,7 +298,7 @@ void Charge_Extra::update_delta_rho(const UnitCell& ucell, const Charge* chr, co { rho_atom[is] = new double[chr->rhopw->nrxx]; } - chr->atomic_rho(this->nspin, ucell.omega, rho_atom, sf->strucFac, ucell); + module_charge::atomic_rho(this->nspin, ucell.omega, rho_atom, sf->strucFac, ucell, chr->rhopw); #ifdef _OPENMP #pragma omp parallel for collapse(2) schedule(static, 512) diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index cb6b3a144da..aab544f8ace 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -2,6 +2,7 @@ #include #include "charge.h" +#include "charge_atomic.h" #include "charge_math.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -229,7 +230,7 @@ void Charge::init_rho_atomic_and_tau(const UnitCell& ucell, { std::cout << " Charge::init_rho: use atomic initialization instead." << std::endl; } - this->atomic_rho(nspin, ucell.omega, rho, strucFac, ucell); + module_charge::atomic_rho(nspin, ucell.omega, rho, strucFac, ucell, this->rhopw); } // initial tau = 3/5 rho^2/3, Thomas-Fermi diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 5dd9ab5a2d3..672993dc5e7 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -103,7 +103,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_charge_test LIBS parameter planewave_serial base device cell_info - SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_math.cpp + SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_atomic.cpp ../module_charge/charge_math.cpp ) diff --git a/source/source_estate/test/charge_extra_test.cpp b/source/source_estate/test/charge_extra_test.cpp index cc5dd66189c..2089cb69751 100644 --- a/source/source_estate/test/charge_extra_test.cpp +++ b/source/source_estate/test/charge_extra_test.cpp @@ -40,13 +40,20 @@ Charge::~Charge() delete[] rho; delete rhopw; } -void Charge::atomic_rho(const int spin_number_need, - const double& omega, - double** rho_in, - const ModuleBase::ComplexMatrix& strucFac, - const UnitCell& ucell) const + +// mock for module_charge::atomic_rho to keep this unit test isolated +// from the full PW basis implementation. +namespace module_charge +{ +void atomic_rho(const int spin_number_need, + const double& omega, + double** rho_in, + const ModuleBase::ComplexMatrix& strucFac, + const UnitCell& ucell, + const ModulePW::PW_Basis* rhopw) { } +} // namespace module_charge // mock functions for PW_Basis namespace ModulePW From 0c7109d101e6fc21b6c412477763e0ea1c7eaf5f Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 19:53:16 +0800 Subject: [PATCH 11/37] module_charge: forbid Charge copies and guard tau.cube write scf_out_chg_tau aborted in Parallel_Grid::reduce on assert(rhoin != nullptr) because the kin_r_save[is] handed to write_vdata_palgrid was not a valid buffer. After the _space_* storage became std::vector (ecf5084d4), a copied/moved Charge leaves its rho/kin_r views dangling into another object's vector buffer, and a kin_r_save never allocated (ked_flag set after allocate) stays nullptr; both surface as a null rhoin deep inside MPI gather instead of at the source. Delete Charge's copy constructor/assignment so any value copy of the vector-aliasing views fails at compile time, and check kin_r_save in ctrl_output_fp before writing tau.cube so a missing allocation reports a clear message instead of tripping the MPI assert. Verification: not run locally (per user request, user compiles). --- source/source_estate/module_charge/charge.h | 6 ++++++ source/source_io/module_ctrl/ctrl_output_fp.cpp | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 62031c3ff64..1af00a31de1 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -23,6 +23,12 @@ class Charge Charge(); ~Charge(); + // rho/rhog/kin_r views alias the vector-backed _space_* storage, so + // copying a Charge would duplicate dangling pointers into another + // object's vector buffer. Forbid copies until a deep copy is needed. + Charge(const Charge&) = delete; + Charge& operator=(const Charge&) = delete; + //========================================================== // MEMBER VARIABLES : // init_chg : "atomic" or "file" diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index e00dbe3f03f..71c475c82ab 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -91,6 +91,15 @@ void ctrl_output_fp(UnitCell& ucell, if (XC_Functional::get_ked_flag()) { + // kin_r_save is only allocated when Charge::allocate() saw + // kin_den == true; guard against the (now impossible) case + // where ked_flag was set after allocation. + if (chr.kin_r_save == nullptr || chr.kin_r_save[is] == nullptr) + { + ModuleBase::WARNING_QUIT("ctrl_output_fp", + "tau.cube requested (out_chg) but Charge::kin_r_save was never allocated; " + "XC functional with kinetic energy density must be set before Charge::allocate()."); + } fn = PARAM.globalv.global_out_dir + "tau"; fn += spin_block + geom_block + ".cube"; From 871f8f2cc6da16d11949a591a1f4063018e139db Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 21:10:53 +0800 Subject: [PATCH 12/37] module_base: tolerate null grid buffer when a rank owns no grid points scf_out_chg_tau (LCAO, SCAN, out_chg=1, 4 MPI ranks) aborted in Parallel_Grid::reduce on assert(rhoin != nullptr). Bisecting between 83eb5d0f3 (good) and ecf5084d4 (bad) isolated the regression to ecf5084d4, which moved Charge's _space_* storage from raw new[] to std::vector. Root cause: with 4 ranks the FFT grid is slab-decomposed so that the last rank owns zero real-space points (nrxx == 0, confirmed via a temporary diagnostic printing fn/is/rank/nrxx at the reduce call site). Before ecf5084d4, _space_rho = new double[nspin * 0] == new double[0] returned a unique non-null pointer, so rho_save[is] was non-null and the assert passed. After the change, an empty vector's .data() returns nullptr, so the rank with nrxx == 0 handed a null rhoin to reduce and tripped the assert (Debug) or fed MPI_Gatherv a null buffer (Release). A rank with nrxx == 0 is legitimate: MPI_Gatherv is invoked with sendcount 0 and ignores the send buffer. Relax the assert to only flag a null buffer when nrxx != 0, and revert the now-unneeded kin_r_save guard in ctrl_output_fp (it would have falsely aborted on the nrxx == 0 rank). Verification: Release build (build_max_para_test), ran cd tests/03_NAO_multik/scf_out_chg_tau && OMP_NUM_THREADS=1 mpirun -np 4 ../../../build_max_para_test/abacus_max_para Result: exit 0, chg.cube and tau.cube written; numerical comparison against chg.cube.ref/tau.cube.ref gives maxdiff 0 (chg) and 1e-14 (tau). --- source/source_base/parallel_grid.cpp | 7 ++++++- source/source_io/module_ctrl/ctrl_output_fp.cpp | 9 --------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/source/source_base/parallel_grid.cpp b/source/source_base/parallel_grid.cpp index c8a058e52e9..f4d99a35cf9 100644 --- a/source/source_base/parallel_grid.cpp +++ b/source/source_base/parallel_grid.cpp @@ -352,7 +352,12 @@ void Parallel_Grid::reduce(double* rhotot, const double* const rhoin, const bool return; } - assert(rhoin != nullptr); + // A rank may own zero real-space grid points (nrxx == 0) when the grid is + // decomposed across more processes than it has slabs. In that case the + // source buffer is legitimately null: MPI_Gatherv is called with + // sendcount 0 below and ignores the send buffer. Only a null buffer with a + // non-zero nrxx is a genuine bug. + assert(rhoin != nullptr || this->nrxx == 0); assert(this->nrxx == this->ncxy * this->nczp); int pool_size = 0; diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 71c475c82ab..e00dbe3f03f 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -91,15 +91,6 @@ void ctrl_output_fp(UnitCell& ucell, if (XC_Functional::get_ked_flag()) { - // kin_r_save is only allocated when Charge::allocate() saw - // kin_den == true; guard against the (now impossible) case - // where ked_flag was set after allocation. - if (chr.kin_r_save == nullptr || chr.kin_r_save[is] == nullptr) - { - ModuleBase::WARNING_QUIT("ctrl_output_fp", - "tau.cube requested (out_chg) but Charge::kin_r_save was never allocated; " - "XC functional with kinetic energy density must be set before Charge::allocate()."); - } fn = PARAM.globalv.global_out_dir + "tau"; fn += spin_block + geom_block + ".cube"; From 1d3beac7685efd388f41726e5246b84a8ac9cdbf Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 22:10:44 +0800 Subject: [PATCH 13/37] module_charge: extract Charge::set_rho_core into charge_math free function Move set_rho_core to charge_math::set_rho_core with rho_core, rhog_core and rhopw passed explicitly instead of reading Charge state, and call charge_math::non_linear_core_correction directly. Remove the now-unused Charge::non_linear_core_correction wrapper, use std::vector for the rhocg/vg scratch buffers, update the init_scf call site, and drop the obsolete member stubs in the elecstate unit tests. --- source/source_estate/init_scf.cpp | 8 +- source/source_estate/module_charge/charge.h | 17 --- .../module_charge/charge_init.cpp | 132 ------------------ .../module_charge/charge_math.cpp | 107 ++++++++++++++ .../source_estate/module_charge/charge_math.h | 22 +++ .../test/elecstate_base_test.cpp | 3 - .../source_estate/test/elecstate_pw_test.cpp | 3 - 7 files changed, 136 insertions(+), 156 deletions(-) diff --git a/source/source_estate/init_scf.cpp b/source/source_estate/init_scf.cpp index 4e5699e8074..5fd01c8e781 100644 --- a/source/source_estate/init_scf.cpp +++ b/source/source_estate/init_scf.cpp @@ -1,4 +1,5 @@ #include "elecstate.h" +#include "source_estate/module_charge/charge_math.h" #include "source_estate/write_init.h" namespace elecstate @@ -14,7 +15,12 @@ void init_scf(const UnitCell& ucell, ElecState* pelec) { //! core correction potential. - pelec->charge->set_rho_core(ucell, strucfac, numeric); + charge_math::set_rho_core(ucell, + strucfac, + numeric, + pelec->charge->rho_core, + pelec->charge->rhog_core, + *pelec->charge->rhopw); //! renormalize the charge density if(PARAM.inp.init_chg != "dm_no_renormalize") diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 1af00a31de1..79cb1781265 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -98,29 +98,12 @@ class Charge void allocate(const int &nspin_in, const bool kin_den); - void set_rho_core(const UnitCell& ucell, - const ModuleBase::ComplexMatrix& structure_factor, - const bool* numeric); - void renormalize_rho(); double sum_rho() const; void save_rho_before_sum_band(); - // for non-linear core correction - void non_linear_core_correction - ( - const bool &numeric, - const double omega, - const double tpiba2, - const int mesh, - const double *r, - const double *rab, - const double *rhoc, - double *rhocg - ) const; - double cal_rho2ne(const double *rho_in) const; void check_rho(); // to check whether the charge density is normal diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index aab544f8ace..aec7307c114 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -8,11 +8,7 @@ #include "source_base/global_variable.h" #include "source_io/module_parameter/parameter.h" #include "source_base/libm/libm.h" -#include "source_base/math_integral.h" -#include "source_base/math_sphbes.h" -#include "source_base/parallel_reduce.h" #include "source_base/timer.h" -#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" @@ -307,131 +303,3 @@ void Charge::init_rho_from_wfc(ModuleSymmetry::Symmetry& symm, PARAM.inp.nbands, nspin, PARAM.globalv.npol, kv->get_nkstot(),kv->ik2iktot,kv->isk,GlobalV::ofs_running); } - -//========================================================== -// computes the core charge on the real space 3D mesh. -//========================================================== -void Charge::set_rho_core(const UnitCell& ucell, - const ModuleBase::ComplexMatrix& structure_factor, - const bool* numeric) -{ - ModuleBase::TITLE("Charge","set_rho_core"); - ModuleBase::timer::start("Charge","set_rho_core"); - - bool bl = false; - for (int it = 0; itrho_core, this->rhopw->nrxx); - ModuleBase::timer::end("Charge","set_rho_core"); - return; - } - - double *rhocg = new double[this->rhopw->ngg]; - ModuleBase::GlobalFunc::ZEROS(rhocg, this->rhopw->ngg ); - - // three dimension. - std::complex *vg = new std::complex[this->rhopw->npw]; - - for (int it = 0; it < ucell.ntype;it++) - { - if (ucell.atoms[it].ncpp.nlcc) - { -//---------------------------------------------------------- -// EXPLAIN : drhoc compute the radial fourier transform for -// each shell of g vec -//---------------------------------------------------------- - this->non_linear_core_correction( - numeric, - ucell.omega, - ucell.tpiba2, - ucell.atoms[it].ncpp.msh, - ucell.atoms[it].ncpp.r.data(), - ucell.atoms[it].ncpp.rab.data(), - ucell.atoms[it].ncpp.rho_atc.data(), - rhocg); -//---------------------------------------------------------- -// EXPLAIN : multiply by the structure factor and sum -//---------------------------------------------------------- - for (int ig = 0; ig < this->rhopw->npw ; ig++) - { - vg[ig] += structure_factor(it, ig) * rhocg[this->rhopw->ig2igg[ig]]; - } - } - } - - // for tmp use. - for(int ig=0; ig< this->rhopw->npw; ig++) - { - this->rhog_core[ig] = vg[ig]; - } - - this->rhopw->recip2real(vg, this->rho_core); - - // test on the charge and computation of the core energy - double rhoima = 0.0; - double rhoneg = 0.0; - for (int ir = 0; ir < this->rhopw->nrxx; ir++) - { - rhoneg += std::min(0.0, this->rhopw->fft_bundle.get_auxr_data()[ir].real()); - rhoima += std::abs(this->rhopw->fft_bundle.get_auxr_data()[ir].imag()); - // NOTE: Core charge is computed in reciprocal space and brought to real - // space by FFT. For non smooth core charges (or insufficient cut-off) - // this may result in negative values in some grid points. - // Up to October 1999 the core charge was forced to be positive definite. - // This induces an error in the force, and probably stress, calculation if - // the number of grid points where the core charge would be otherwise neg - // is large. The error disappears for sufficiently high cut-off, but may be - // rather large and it is better to leave the core charge as it is. - // If you insist to have it positive definite (with the possible problems - // mentioned above) uncomment the following lines. SdG, Oct 15 1999 - } - - // mohan fix bug 2011-04-03 - Parallel_Reduce::reduce_pool(rhoneg); - Parallel_Reduce::reduce_pool(rhoima); - - // mohan changed 2010-2-2, make this same as in atomic_rho. - // still lack something...... - rhoneg /= this->rhopw->nxyz * ucell.omega; - rhoima /= this->rhopw->nxyz * ucell.omega; - - // calculate core_only exch-corr energy etxcc=E_xc[rho_core] if required - // The term was present in previous versions of the code but it shouldn't - delete [] rhocg; - delete [] vg; - ModuleBase::timer::end("Charge","set_rho_core"); - return; -} // end subroutine set_rhoc - - -void Charge::non_linear_core_correction -( - const bool &numeric, - const double omega, - const double tpiba2, - const int mesh, - const double *r, - const double *rab, - const double *rhoc, - double *rhocg) const -{ - charge_math::non_linear_core_correction(numeric, - omega, - tpiba2, - mesh, - r, - rab, - rhoc, - rhocg, - this->rhopw->gg_uniq, - this->rhopw->ngg); -} diff --git a/source/source_estate/module_charge/charge_math.cpp b/source/source_estate/module_charge/charge_math.cpp index ebea3702d84..f90c61e91db 100644 --- a/source/source_estate/module_charge/charge_math.cpp +++ b/source/source_estate/module_charge/charge_math.cpp @@ -1,5 +1,6 @@ #include "charge_math.h" +#include "source_base/complexmatrix.h" #include "source_base/global_function.h" #include "source_base/constants.h" #include "source_base/math_integral.h" @@ -8,6 +9,8 @@ #include "source_base/timer.h" #include "source_base/tool_threading.h" #include "source_base/tool_title.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_cell/unitcell.h" #include #include @@ -152,4 +155,108 @@ void non_linear_core_correction(const bool numeric, return; } +// computes the core charge on the real space 3D mesh. +void set_rho_core(const UnitCell& ucell, + const ModuleBase::ComplexMatrix& structure_factor, + const bool* numeric, + double* rho_core, + std::complex* rhog_core, + const ModulePW::PW_Basis& rhopw) +{ + ModuleBase::TITLE("charge_math", "set_rho_core"); + ModuleBase::timer::start("charge_math", "set_rho_core"); + + bool bl = false; + for (int it = 0; it < ucell.ntype; it++) + { + if (ucell.atoms[it].ncpp.nlcc) + { + bl = true; + break; + } + } + + if (!bl) + { + ModuleBase::GlobalFunc::ZEROS(rho_core, rhopw.nrxx); + ModuleBase::timer::end("charge_math", "set_rho_core"); + return; + } + + std::vector rhocg(rhopw.ngg, 0.0); + + // three dimension. + std::vector> vg(rhopw.npw); + + for (int it = 0; it < ucell.ntype; it++) + { + if (ucell.atoms[it].ncpp.nlcc) + { +//---------------------------------------------------------- +// EXPLAIN : drhoc compute the radial fourier transform for +// each shell of g vec +//---------------------------------------------------------- + non_linear_core_correction(numeric, + ucell.omega, + ucell.tpiba2, + ucell.atoms[it].ncpp.msh, + ucell.atoms[it].ncpp.r.data(), + ucell.atoms[it].ncpp.rab.data(), + ucell.atoms[it].ncpp.rho_atc.data(), + rhocg.data(), + rhopw.gg_uniq, + rhopw.ngg); +//---------------------------------------------------------- +// EXPLAIN : multiply by the structure factor and sum +//---------------------------------------------------------- + for (int ig = 0; ig < rhopw.npw; ig++) + { + vg[ig] += structure_factor(it, ig) * rhocg[rhopw.ig2igg[ig]]; + } + } + } + + // for tmp use. + for (int ig = 0; ig < rhopw.npw; ig++) + { + rhog_core[ig] = vg[ig]; + } + + rhopw.recip2real(vg.data(), rho_core); + + // test on the charge and computation of the core energy + double rhoima = 0.0; + double rhoneg = 0.0; + for (int ir = 0; ir < rhopw.nrxx; ir++) + { + rhoneg += std::min(0.0, rhopw.fft_bundle.get_auxr_data()[ir].real()); + rhoima += std::abs(rhopw.fft_bundle.get_auxr_data()[ir].imag()); + // NOTE: Core charge is computed in reciprocal space and brought to real + // space by FFT. For non smooth core charges (or insufficient cut-off) + // this may result in negative values in some grid points. + // Up to October 1999 the core charge was forced to be positive definite. + // This induces an error in the force, and probably stress, calculation if + // the number of grid points where the core charge would be otherwise neg + // is large. The error disappears for sufficiently high cut-off, but may be + // rather large and it is better to leave the core charge as it is. + // If you insist to have it positive definite (with the possible problems + // mentioned above) uncomment the following lines. SdG, Oct 15 1999 + } + +#ifdef __MPI + // mohan fix bug 2011-04-03 + Parallel_Reduce::reduce_pool(rhoneg); + Parallel_Reduce::reduce_pool(rhoima); +#endif + + // mohan changed 2010-2-2, make this same as in atomic_rho. + // still lack something...... + rhoneg /= rhopw.nxyz * ucell.omega; + rhoima /= rhopw.nxyz * ucell.omega; + + // calculate core_only exch-corr energy etxcc=E_xc[rho_core] if required + // The term was present in previous versions of the code but it shouldn't + ModuleBase::timer::end("charge_math", "set_rho_core"); +} + } // namespace charge_math diff --git a/source/source_estate/module_charge/charge_math.h b/source/source_estate/module_charge/charge_math.h index 889a5e721d6..61d07c19585 100644 --- a/source/source_estate/module_charge/charge_math.h +++ b/source/source_estate/module_charge/charge_math.h @@ -7,9 +7,31 @@ // All grid / geometry inputs are passed explicitly instead of being read // from Charge members or globals. +#include + +class UnitCell; +namespace ModuleBase +{ +class ComplexMatrix; +} +namespace ModulePW +{ +class PW_Basis; +} + namespace charge_math { +// Compute the core charge (non-linear core correction) on the real-space +// 3D mesh. rho_core / rhog_core are the output buffers previously owned by +// Charge; rhopw supplies the reciprocal-grid geometry and FFT backend. +void set_rho_core(const UnitCell& ucell, + const ModuleBase::ComplexMatrix& structure_factor, + const bool* numeric, + double* rho_core, + std::complex* rhog_core, + const ModulePW::PW_Basis& rhopw); + // Sum the (spin-resolved) charge density over the real-space grid and // convert it to a total charge using the cell volume and grid size. // rho[is][ir] is the density for spin channel is. nspin0 is the number of diff --git a/source/source_estate/test/elecstate_base_test.cpp b/source/source_estate/test/elecstate_base_test.cpp index d3ce296271d..65569dfccb9 100644 --- a/source/source_estate/test/elecstate_base_test.cpp +++ b/source/source_estate/test/elecstate_base_test.cpp @@ -100,9 +100,6 @@ void ModulePW::PW_Basis::initgrids(double, ModuleBase::Matrix3, int, int, int) void ModulePW::PW_Basis::distribute_r() { } -void Charge::set_rho_core(const UnitCell& ucell, ModuleBase::ComplexMatrix const&, const bool*) -{ -} void Charge::init_rho(const UnitCell&, const Parallel_Grid&, ModuleBase::ComplexMatrix const&, diff --git a/source/source_estate/test/elecstate_pw_test.cpp b/source/source_estate/test/elecstate_pw_test.cpp index ee59cce74f4..c905339871b 100644 --- a/source/source_estate/test/elecstate_pw_test.cpp +++ b/source/source_estate/test/elecstate_pw_test.cpp @@ -123,9 +123,6 @@ Fcoef::~Fcoef() } #include "source_cell/klist.h" -void Charge::set_rho_core(const UnitCell& ucell, ModuleBase::ComplexMatrix const&, const bool*) -{ -} void Charge::init_rho(const UnitCell&, const Parallel_Grid&, ModuleBase::ComplexMatrix const&, From 49b0a65b27ffe167bf718ee9ea5e0b28627618bd Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Wed, 16 Sep 2026 23:12:00 +0800 Subject: [PATCH 14/37] module_charge: vectorize Charge_Extra history arrays and forbid copies Replace the raw new[]/delete[] displacement arrays (dis_old1, dis_old2, dis_now) with std::vector and remove the hand-written destructor. This fixes a read of uninitialized pot_order when an object is destroyed before Init_CE, a memory leak when Init_CE is called repeatedly, and a double-free risk from the implicitly generated shallow copy. The copy constructor and copy assignment are deleted so the molecular-dynamics trajectory history cannot be silently forked. The unit test now checks vector sizes instead of non-null pointers. --- .../source_estate/module_charge/charge_extra.cpp | 16 +++------------- .../source_estate/module_charge/charge_extra.h | 15 +++++++++++---- source/source_estate/test/charge_extra_test.cpp | 6 +++--- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/charge_extra.cpp index 9f1dc412307..4eaf69a8b26 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/charge_extra.cpp @@ -12,16 +12,6 @@ Charge_Extra::Charge_Extra() { } -Charge_Extra::~Charge_Extra() -{ - if(pot_order == 3) - { - delete[] dis_old1; - delete[] dis_old2; - delete[] dis_now; - } -} - void Charge_Extra::Init_CE(const int& nspin, const int& natom, const int& nrxx, const std::string chg_extrap) { if (chg_extrap == "none") @@ -67,9 +57,9 @@ void Charge_Extra::Init_CE(const int& nspin, const int& natom, const int& nrxx, if(pot_order == 3) { - dis_old1 = new ModuleBase::Vector3[natom]; - dis_old2 = new ModuleBase::Vector3[natom]; - dis_now = new ModuleBase::Vector3[natom]; + dis_old1.assign(natom, ModuleBase::Vector3()); + dis_old2.assign(natom, ModuleBase::Vector3()); + dis_now.assign(natom, ModuleBase::Vector3()); } alpha = 1.0; diff --git a/source/source_estate/module_charge/charge_extra.h b/source/source_estate/module_charge/charge_extra.h index bdfb103377a..4b506d643d8 100644 --- a/source/source_estate/module_charge/charge_extra.h +++ b/source/source_estate/module_charge/charge_extra.h @@ -1,6 +1,8 @@ #ifndef CHARGE_EXTRA_H #define CHARGE_EXTRA_H +#include + #include "charge.h" #include "source_cell/unitcell.h" #include "source_pw/module_pwdft/stru_fac.h" @@ -34,7 +36,12 @@ class Charge_Extra public: Charge_Extra(); - ~Charge_Extra(); + + // The displacement and delta-rho arrays record the history of one + // molecular-dynamics trajectory; a copy would silently fork that + // history. Forbid copies until a deep copy is needed. + Charge_Extra(const Charge_Extra&) = delete; + Charge_Extra& operator=(const Charge_Extra&) = delete; /** * @brief Initialization of viriables used in charge extrapolation methods @@ -95,9 +102,9 @@ class Charge_Extra int rho_extr; ///< the actually used method int nspin; ///< the number of spins - ModuleBase::Vector3* dis_old1 = nullptr; ///< dis_old2 = pos_old1 - pos_old2 - ModuleBase::Vector3* dis_old2 = nullptr; ///< dis_old1 = pos_now - pos_old1 - ModuleBase::Vector3* dis_now = nullptr; ///< dis_now = pos_next - pos_now + std::vector> dis_old1; ///< dis_old2 = pos_old1 - pos_old2 + std::vector> dis_old2; ///< dis_old1 = pos_now - pos_old1 + std::vector> dis_now; ///< dis_now = pos_next - pos_now std::vector> delta_rho1; ///< the last step difference of rho and atomic_rho std::vector> delta_rho2; ///< the second last step difference of rho and atomic_rho diff --git a/source/source_estate/test/charge_extra_test.cpp b/source/source_estate/test/charge_extra_test.cpp index 2089cb69751..30a13f48870 100644 --- a/source/source_estate/test/charge_extra_test.cpp +++ b/source/source_estate/test/charge_extra_test.cpp @@ -170,9 +170,9 @@ TEST_F(ChargeExtraTest, InitCECase4) EXPECT_DOUBLE_EQ(CE.beta, 0.0); EXPECT_NE(CE.delta_rho1.size(), 0); EXPECT_NE(CE.delta_rho2.size(), 0); - EXPECT_NE(CE.dis_old1, nullptr); - EXPECT_NE(CE.dis_old2, nullptr); - EXPECT_NE(CE.dis_now, nullptr); + EXPECT_EQ(CE.dis_old1.size(), static_cast(ucell->nat)); + EXPECT_EQ(CE.dis_old2.size(), static_cast(ucell->nat)); + EXPECT_EQ(CE.dis_now.size(), static_cast(ucell->nat)); } TEST_F(ChargeExtraTest, ExtrapolateChargeCase1) From e0480f34ab8b48ff362b9f5e09087eab953bbd5b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 08:49:20 +0800 Subject: [PATCH 15/37] Rename charge_math to chg_tools and unify namespace module_charge - Rename module_charge/charge_math.{h,cpp} to chg_tools.{h,cpp} via git mv - Change namespace charge_math to module_charge to match charge_atomic and chgmixing in the same directory - Update include guard CHG_TOOLS_H and TITLE/timer labels accordingly - Update call sites in init_scf.cpp, charge.cpp, charge_init.cpp - Update build references in Makefile.Objects and both CMakeLists.txt --- source/Makefile.Objects | 2 +- source/source_estate/CMakeLists.txt | 2 +- source/source_estate/init_scf.cpp | 4 ++-- source/source_estate/module_charge/charge.cpp | 6 +++--- .../module_charge/charge_init.cpp | 2 +- .../{charge_math.cpp => chg_tools.cpp} | 20 +++++++++---------- .../{charge_math.h => chg_tools.h} | 10 +++++----- source/source_estate/test/CMakeLists.txt | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) rename source/source_estate/module_charge/{charge_math.cpp => chg_tools.cpp} (93%) rename source/source_estate/module_charge/{charge_math.h => chg_tools.h} (95%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index a313831784c..b42c99790fd 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -828,7 +828,7 @@ OBJS_SRCPW=h_ewald_pw.o\ charge.o\ charge_atomic.o\ charge_init.o\ - charge_math.o\ + chg_tools.o\ charge_mpi.o\ charge_extra.o\ charge_mixing.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 877c91e0ecd..c0392422632 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -32,7 +32,7 @@ list(APPEND objects module_charge/charge.cpp module_charge/charge_atomic.cpp module_charge/charge_init.cpp - module_charge/charge_math.cpp + module_charge/chg_tools.cpp module_charge/charge_mpi.cpp module_charge/charge_extra.cpp module_charge/charge_mixing.cpp diff --git a/source/source_estate/init_scf.cpp b/source/source_estate/init_scf.cpp index 5fd01c8e781..3bf2e495a74 100644 --- a/source/source_estate/init_scf.cpp +++ b/source/source_estate/init_scf.cpp @@ -1,5 +1,5 @@ #include "elecstate.h" -#include "source_estate/module_charge/charge_math.h" +#include "source_estate/module_charge/chg_tools.h" #include "source_estate/write_init.h" namespace elecstate @@ -15,7 +15,7 @@ void init_scf(const UnitCell& ucell, ElecState* pelec) { //! core correction potential. - charge_math::set_rho_core(ucell, + module_charge::set_rho_core(ucell, strucfac, numeric, pelec->charge->rho_core, diff --git a/source/source_estate/module_charge/charge.cpp b/source/source_estate/module_charge/charge.cpp index 428cd4071c7..70e8c50922e 100644 --- a/source/source_estate/module_charge/charge.cpp +++ b/source/source_estate/module_charge/charge.cpp @@ -17,7 +17,7 @@ // even in a LSDA calculation. //---------------------------------------------------------- #include "charge.h" -#include "charge_math.h" +#include "chg_tools.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -176,7 +176,7 @@ void Charge::allocate(const int& nspin_in, const bool kin_den) double Charge::sum_rho() const { const int nspin0 = (nspin == 2) ? 2 : 1; - return charge_math::sum_rho(this->rho, nspin0, this->nrxx, *this->omega_, this->rhopw->nxyz); + return module_charge::sum_rho(this->rho, nspin0, this->nrxx, *this->omega_, this->rhopw->nxyz); } void Charge::renormalize_rho() @@ -217,7 +217,7 @@ void Charge::save_rho_before_sum_band() double Charge::cal_rho2ne(const double* rho_in) const { - return charge_math::cal_rho2ne(rho_in, this->rhopw->nrxx, *this->omega_, this->rhopw->nxyz); + return module_charge::cal_rho2ne(rho_in, this->rhopw->nrxx, *this->omega_, this->rhopw->nxyz); } void Charge::check_rho() diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index aec7307c114..21e6af61044 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -3,7 +3,7 @@ #include "charge.h" #include "charge_atomic.h" -#include "charge_math.h" +#include "chg_tools.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_io/module_parameter/parameter.h" diff --git a/source/source_estate/module_charge/charge_math.cpp b/source/source_estate/module_charge/chg_tools.cpp similarity index 93% rename from source/source_estate/module_charge/charge_math.cpp rename to source/source_estate/module_charge/chg_tools.cpp index f90c61e91db..ab18f92cf3e 100644 --- a/source/source_estate/module_charge/charge_math.cpp +++ b/source/source_estate/module_charge/chg_tools.cpp @@ -1,4 +1,4 @@ -#include "charge_math.h" +#include "chg_tools.h" #include "source_base/complexmatrix.h" #include "source_base/global_function.h" @@ -16,7 +16,7 @@ #include #include -namespace charge_math +namespace module_charge { double sum_rho(double* const* rho, @@ -25,7 +25,7 @@ double sum_rho(double* const* rho, const double omega, const int nxyz) { - ModuleBase::TITLE("charge_math", "sum_rho"); + ModuleBase::TITLE("module_charge", "sum_rho"); double sum_rho = 0.0; @@ -48,7 +48,7 @@ double sum_rho(double* const* rho, // sum_rho may be smaller than 1, like Na bcc. if (sum_rho <= 0.1) { - ModuleBase::WARNING_QUIT("charge_math::sum_rho", "Can't find even an electron!"); + ModuleBase::WARNING_QUIT("module_charge::sum_rho", "Can't find even an electron!"); } return sum_rho; @@ -85,7 +85,7 @@ void non_linear_core_correction(const bool numeric, const double* gg_uniq, const int ngg) { - ModuleBase::TITLE("charge_math", "drhoc"); + ModuleBase::TITLE("module_charge", "drhoc"); // use labmda instead of repeating codes const auto kernel = [&](int num_threads, int thread_id) @@ -163,8 +163,8 @@ void set_rho_core(const UnitCell& ucell, std::complex* rhog_core, const ModulePW::PW_Basis& rhopw) { - ModuleBase::TITLE("charge_math", "set_rho_core"); - ModuleBase::timer::start("charge_math", "set_rho_core"); + ModuleBase::TITLE("module_charge", "set_rho_core"); + ModuleBase::timer::start("module_charge", "set_rho_core"); bool bl = false; for (int it = 0; it < ucell.ntype; it++) @@ -179,7 +179,7 @@ void set_rho_core(const UnitCell& ucell, if (!bl) { ModuleBase::GlobalFunc::ZEROS(rho_core, rhopw.nrxx); - ModuleBase::timer::end("charge_math", "set_rho_core"); + ModuleBase::timer::end("module_charge", "set_rho_core"); return; } @@ -256,7 +256,7 @@ void set_rho_core(const UnitCell& ucell, // calculate core_only exch-corr energy etxcc=E_xc[rho_core] if required // The term was present in previous versions of the code but it shouldn't - ModuleBase::timer::end("charge_math", "set_rho_core"); + ModuleBase::timer::end("module_charge", "set_rho_core"); } -} // namespace charge_math +} // namespace module_charge diff --git a/source/source_estate/module_charge/charge_math.h b/source/source_estate/module_charge/chg_tools.h similarity index 95% rename from source/source_estate/module_charge/charge_math.h rename to source/source_estate/module_charge/chg_tools.h index 61d07c19585..42bbdaaacb7 100644 --- a/source/source_estate/module_charge/charge_math.h +++ b/source/source_estate/module_charge/chg_tools.h @@ -1,5 +1,5 @@ -#ifndef CHARGE_MATH_H -#define CHARGE_MATH_H +#ifndef CHG_TOOLS_H +#define CHG_TOOLS_H // Free numerical kernels extracted from the Charge class so that the // density math (summation, electron-count integration, non-linear core @@ -19,7 +19,7 @@ namespace ModulePW class PW_Basis; } -namespace charge_math +namespace module_charge { // Compute the core charge (non-linear core correction) on the real-space @@ -64,6 +64,6 @@ void non_linear_core_correction(const bool numeric, const double* gg_uniq, const int ngg); -} // namespace charge_math +} // namespace module_charge -#endif // CHARGE_MATH_H +#endif // CHG_TOOLS_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 672993dc5e7..81576bdb481 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -103,7 +103,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_charge_test LIBS parameter planewave_serial base device cell_info - SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_atomic.cpp ../module_charge/charge_math.cpp + SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_atomic.cpp ../module_charge/chg_tools.cpp ) From 5154f98aed1be69a7fd5fd33dd9e6e016e9c5ac9 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 10:43:08 +0800 Subject: [PATCH 16/37] module_charge: refactor Symmetry_rho class to free functions Convert the stateless class Symmetry_rho into namespace module_charge free functions and rename files for consistency: symm_rho.{h,cpp} -> chg_symm.{h,cpp} symm_rho_detail.h -> chg_symm_detail.h symm_rhog.cpp -> chg_symm_detail.cpp - 5 public functions become module_charge::symmetrize_rho / cal_rhog_symm (2 overloads) / cal_rhog_symm_soc (2 overloads) - 2 cross-TU helpers (psymmg/psymmg_soc) moved to module_charge::detail via chg_symm_detail.h - 3 internal MPI helpers moved to anonymous namespace - Delete dead code psymm (real-space symmetrization, never called) - Remove empty ctor/dtor and parallel_grid.h include - Rename begin/begin_soc to cal_rhog_symm/cal_rhog_symm_soc for clarity - Update timer/TITLE labels from "Symmetry_rho" to "module_charge" - Migrate all 14 call sites and 1 test stub - Remove obsolete Makefile special rule (no more name collision) --- source/Makefile | 8 - source/source_esolver/esolver_fp.cpp | 2 +- source/source_esolver/esolver_ks_lcao.cpp | 6 +- .../source_esolver/esolver_ks_lcao_tddft.cpp | 4 +- source/source_esolver/esolver_ks_lcaopw.cpp | 4 +- source/source_esolver/esolver_ks_pw.cpp | 4 +- source/source_esolver/esolver_of.cpp | 17 +- source/source_esolver/esolver_of_tddft.cpp | 2 +- source/source_esolver/esolver_sdft_pw.cpp | 4 +- source/source_esolver/lcao_others.cpp | 2 +- source/source_estate/CMakeLists.txt | 4 +- .../source_estate/module_charge/chg_symm.cpp | 139 ++++++ source/source_estate/module_charge/chg_symm.h | 87 ++++ .../{symm_rhog.cpp => chg_symm_detail.cpp} | 404 +++++++++--------- .../module_charge/chg_symm_detail.h | 39 ++ .../source_estate/module_charge/symm_rho.cpp | 214 ---------- source/source_estate/module_charge/symm_rho.h | 97 ----- source/source_hsolver/hsolver_pw_sdft.cpp | 2 +- .../source_io/module_chgpot/get_pchg_lcao.cpp | 9 +- .../source_io/module_chgpot/get_pchg_pw.cpp | 9 +- .../source_io/module_ctrl/ctrl_output_fp.cpp | 5 +- .../source_io/module_ml/write_mlkedf_desc.cpp | 8 +- source/source_io/module_wf/read_wf2rho_pw.cpp | 5 +- source/source_io/test/read_wf2rho_pw_test.cpp | 14 +- .../module_rdmft/update_state_rdmft.cpp | 5 +- source/source_pw/module_pwdft/setup_pot.cpp | 9 +- 26 files changed, 523 insertions(+), 580 deletions(-) create mode 100644 source/source_estate/module_charge/chg_symm.cpp create mode 100644 source/source_estate/module_charge/chg_symm.h rename source/source_estate/module_charge/{symm_rhog.cpp => chg_symm_detail.cpp} (84%) create mode 100644 source/source_estate/module_charge/chg_symm_detail.h delete mode 100644 source/source_estate/module_charge/symm_rho.cpp delete mode 100644 source/source_estate/module_charge/symm_rho.h diff --git a/source/Makefile b/source/Makefile index 1f29676b210..ff9e039a083 100644 --- a/source/Makefile +++ b/source/Makefile @@ -259,14 +259,6 @@ ${OBJ_DIR}/parse_args.o: $(ABACUS_ROOT)source_io/parse_args.cpp $(BUILD_INFO_DIR @mkdir -p $(dir $@) ${CXX} ${OPTS} ${OPTS_MPI} -c ${HONG} $< -o $@ -# Explicit rule: source_cell/module_symmetry and source_estate/module_charge -# both contain a symm_rho.cpp. VPATH resolves symm_rho.o to the source_cell -# one (listed first), so the Symmetry_rho implementation needs an explicit -# object name to be compiled from the correct source. -${OBJ_DIR}/symm_rho_charge.o: $(ABACUS_ROOT)source_estate/module_charge/symm_rho.cpp - @mkdir -p $(dir $@) - ${CXX} ${OPTS} ${OPTS_MPI} -c ${HONG} $< -o $@ - ###### END of ABACUS INFO PART ###### #========================== diff --git a/source/source_esolver/esolver_fp.cpp b/source/source_esolver/esolver_fp.cpp index 1e763e84dbd..d84dd2b1049 100644 --- a/source/source_esolver/esolver_fp.cpp +++ b/source/source_esolver/esolver_fp.cpp @@ -2,7 +2,7 @@ #include "source_base/tool_quit.h" #include "source_cell/cal_ux.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_cell/read_pp_ucell.h" #include "source_estate/param_update.h" #include "source_hamilt/module_ewald/h_ewald_pw.h" diff --git a/source/source_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index f28b0c98d98..f4edfceed5a 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -7,7 +7,7 @@ #include "source_lcao/setup_dftu_lcao.h" #include "source_pw/module_pwdft/dftu_base.h" // Plus_U_Base (PW and LCAO share it) #include "source_hamilt/hs_matrix_k.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_lcao/lcao_domain.h" // need DeePKS_init #include "source_lcao/force_stress_lcao.h" #include "source_hamilt/module_gint/gint.h" @@ -234,7 +234,7 @@ void ESolver_KS_LCAO::before_scf(UnitCell& ucell, const int istep) #endif // 16) the electron charge density should be symmetrized, - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); // 17) update of RDMFT, added by jghan if (this->inp_->rdmft == true) @@ -506,7 +506,7 @@ void ESolver_KS_LCAO::hamilt2rho_single(UnitCell& ucell, int istep, int #endif // 5) symmetrize the charge density - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); // 6) calculate delta energy this->pelec->f_en.deband = this->pelec->cal_delta_eband(ucell); diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index e74951f0e91..73d81b17373 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -13,7 +13,7 @@ #include "source_io/module_wf/read_wfc_nao.h" //------LCAO HSolver ElecState------- #include "source_estate/elecstate_tools.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_estate/module_dm/cal_dm_psi.h" #include "source_estate/module_dm/cal_edm_tddft.h" #include "source_estate/module_pot/h_tddft_pw.h" @@ -378,7 +378,7 @@ void ESolver_KS_LCAO_TDDFT::hamilt2rho_single(UnitCell& ucell, const // Symmetrize the charge density only for ground state if (istep <= 1) { - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); } #ifdef __EXX if (this->exx_info_.info_ri.real_number) diff --git a/source/source_esolver/esolver_ks_lcaopw.cpp b/source/source_esolver/esolver_ks_lcaopw.cpp index 49cb7ea6b1b..e5c6ea072b2 100644 --- a/source/source_esolver/esolver_ks_lcaopw.cpp +++ b/source/source_esolver/esolver_ks_lcaopw.cpp @@ -4,7 +4,7 @@ #include //--------------temporary---------------------------- -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_estate/occupy.h" #include "source_hamilt/module_ewald/h_ewald_pw.h" //-----force------------------- @@ -176,7 +176,7 @@ namespace ModuleESolver } #endif - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); // deband is calculated from "output" charge density calculated // in sum_band diff --git a/source/source_esolver/esolver_ks_pw.cpp b/source/source_esolver/esolver_ks_pw.cpp index 1c8dc1b798f..b345f9c12af 100644 --- a/source/source_esolver/esolver_ks_pw.cpp +++ b/source/source_esolver/esolver_ks_pw.cpp @@ -1,7 +1,7 @@ #include "esolver_ks_pw.h" #include "source_estate/elecstate_pw.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hsolver/diago_iter_assist.h" #include "source_hsolver/diago_params.h" #include "source_hsolver/hsolver_pw.h" @@ -277,7 +277,7 @@ void ESolver_KS_PW::hamilt2rho_single(UnitCell& ucell, const int iste } // symmetrize the charge density - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); ModuleBase::timer::end("ESolver_KS_PW", "hamilt2rho_single"); } diff --git a/source/source_esolver/esolver_of.cpp b/source/source_esolver/esolver_of.cpp index 50e94b2e558..05363c587d5 100644 --- a/source/source_esolver/esolver_of.cpp +++ b/source/source_esolver/esolver_of.cpp @@ -3,7 +3,7 @@ #include "source_io/module_parameter/parameter.h" //-----------temporary------------------------- #include "source_base/global_function.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/module_ewald/h_ewald_pw.h" #include "source_cell/cal_ux.h" #include "source_pw/module_pwdft/force_pw.h" @@ -240,7 +240,7 @@ void ESolver_OF::before_opt(const int istep, UnitCell& ucell) const int nspin = this->inp_->nspin; if (this->inp_->init_chg == "file") { - Symmetry_rho::symmetrize_rho(nspin, this->chr, this->pw_rho, ucell.symm); + module_charge::symmetrize_rho(nspin, this->chr, this->pw_rho, ucell.symm); for (int is = 0; is < nspin; ++is) { for (int ibs = 0; ibs < this->pw_rho->nrxx; ++ibs) @@ -385,19 +385,6 @@ void ESolver_OF::update_rho() } } // // ------------ turn on symmetry may cause instability in optimization ------------ - // if (ModuleSymmetry::Symmetry::symm_flag == 1) - // { - // Symmetry_rho srho; - // for (int is = 0; is < this->inp_->nspin; is++) - // { - // srho.begin(is, *(this->chr), this->pw_rho, Pgrid, ucell.symm); - // for (int ibs = 0; ibs < this->pw_rho->nrxx; ++ibs) - // { - // this->pphi_[is][ibs] = sqrt(this->chr.rho[is][ibs]); - // } - // } - // } - // // -------------------------------------------------------------------------------- } /** diff --git a/source/source_esolver/esolver_of_tddft.cpp b/source/source_esolver/esolver_of_tddft.cpp index 0b14a19cc82..3c068b2c77c 100644 --- a/source/source_esolver/esolver_of_tddft.cpp +++ b/source/source_esolver/esolver_of_tddft.cpp @@ -3,7 +3,7 @@ #include "source_io/module_parameter/parameter.h" //-----------temporary------------------------- #include "source_base/global_function.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/module_ewald/h_ewald_pw.h" #include "source_cell/cal_ux.h" //-----force------------------- diff --git a/source/source_esolver/esolver_sdft_pw.cpp b/source/source_esolver/esolver_sdft_pw.cpp index 218bda84637..34f4b48c72d 100644 --- a/source/source_esolver/esolver_sdft_pw.cpp +++ b/source/source_esolver/esolver_sdft_pw.cpp @@ -3,7 +3,7 @@ #include "source_base/global_variable.h" #include "source_base/memory_recorder.h" #include "source_base/parallel_comm.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hsolver/diago_iter_assist.h" #include "source_hsolver/diago_params.h" #include "source_io/module_parameter/parameter.h" @@ -193,7 +193,7 @@ void ESolver_SDFT_PW::hamilt2rho_single(UnitCell& ucell, int istep, i if (PARAM.globalv.ks_run) { - Symmetry_rho::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rho, ucell.symm); this->pelec->f_en.deband = this->pelec->cal_delta_eband(ucell); } else diff --git a/source/source_esolver/lcao_others.cpp b/source/source_esolver/lcao_others.cpp index 0fae43aa952..d6a6d55f431 100644 --- a/source/source_esolver/lcao_others.cpp +++ b/source/source_esolver/lcao_others.cpp @@ -5,7 +5,7 @@ #include "source_cell/module_neighbor/sltk_grid_driver.h" #include "source_esolver/esolver_ks_lcao.h" #include "source_estate/elecstate_lcao.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_estate/module_dm/cal_dm_psi.h" #include "source_hamilt/module_gint/gint.h" #include "source_io/module_chgpot/get_pchg_lcao.h" diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index c0392422632..79e5c4d270a 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -41,8 +41,8 @@ list(APPEND objects module_charge/mix_precond.cpp module_charge/charge_mixing_rho.cpp module_charge/charge_mixing_uspp.cpp - module_charge/symm_rho.cpp - module_charge/symm_rhog.cpp + module_charge/chg_symm.cpp + module_charge/chg_symm_detail.cpp rhog_io.cpp write_elecstat_pot.cpp write_init.cpp diff --git a/source/source_estate/module_charge/chg_symm.cpp b/source/source_estate/module_charge/chg_symm.cpp new file mode 100644 index 00000000000..1711667c9f9 --- /dev/null +++ b/source/source_estate/module_charge/chg_symm.cpp @@ -0,0 +1,139 @@ +#include "chg_symm.h" + +#include "chg_symm_detail.h" +#include "source_estate/module_charge/charge.h" +#include "source_hamilt/module_xc/xc_functional.h" + +namespace module_charge +{ + +void symmetrize_rho(const int nspin, + const Charge& chr, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm) +{ + if (nspin == 4) + { + // nspin=4 (non-collinear/SOC): rho[0] is the charge density rho^0 (scalar, symmetrized + // spatially like nspin=1); rho[1,2,3] are the spin density (rho^x, rho^y, rho^z) which + // must be symmetrized TOGETHER with the per-operation spin rotation W(g). + cal_rhog_symm(0, chr, pw, symm); + cal_rhog_symm_soc(chr, pw, symm); + return; + } + for (int is = 0; is < nspin; is++) + { + cal_rhog_symm(is, chr, pw, symm); + } +} + +void cal_rhog_symm(const int& spin_now, + const Charge& chr, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm) +{ + assert(spin_now < 4); // added by zhengdy-soc + + if (ModuleSymmetry::Symmetry::symm_flag != 1) + { + return; + } + + ModuleBase::TITLE("module_charge", "cal_rhog_symm"); + ModuleBase::timer::start("module_charge", "cal_rhog_symm"); + + rho_basis->real2recip(chr.rho[spin_now], chr.rhog[spin_now]); + + detail::psymmg(chr.rhog[spin_now], rho_basis, symm); + + rho_basis->recip2real(chr.rhog[spin_now], chr.rho[spin_now]); + + if (XC_Functional::get_ked_flag() || chr.cal_elf) + { + // Use std::vector to manage kin_g instead of raw pointer + std::vector> kin_g(chr.ngmc); + rho_basis->real2recip(chr.kin_r[spin_now], kin_g.data()); + detail::psymmg(kin_g.data(), rho_basis, symm); + rho_basis->recip2real(kin_g.data(), chr.kin_r[spin_now]); + } + + ModuleBase::timer::end("module_charge", "cal_rhog_symm"); + return; +} + +void cal_rhog_symm(const int& spin_now, + double** rho, + std::complex** rhog, + int ngmc, + double** kin_r, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm) +{ + assert(spin_now < 4); // added by zhengdy-soc + + if (ModuleSymmetry::Symmetry::symm_flag != 1) + { + return; + } + + ModuleBase::TITLE("module_charge", "cal_rhog_symm"); + ModuleBase::timer::start("module_charge", "cal_rhog_symm"); + + { + rho_basis->real2recip(rho[spin_now], rhog[spin_now]); + detail::psymmg(rhog[spin_now], rho_basis, symm); + rho_basis->recip2real(rhog[spin_now], rho[spin_now]); + + if (XC_Functional::get_ked_flag() && kin_r != nullptr) + { + // Use std::vector to manage kin_g instead of raw pointer + std::vector> kin_g(ngmc); + rho_basis->real2recip(kin_r[spin_now], kin_g.data()); + detail::psymmg(kin_g.data(), rho_basis, symm); + rho_basis->recip2real(kin_g.data(), kin_r[spin_now]); + } + } + + ModuleBase::timer::end("module_charge", "cal_rhog_symm"); + return; +} + +void cal_rhog_symm_soc(const Charge& chr, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm) +{ + cal_rhog_symm_soc(chr.rho, chr.rhog, rho_basis, symm); +} + +void cal_rhog_symm_soc(double** rho, + std::complex** rhog, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm) +{ + if (ModuleSymmetry::Symmetry::symm_flag != 1) + { + return; + } + + ModuleBase::TITLE("module_charge", "cal_rhog_symm_soc"); + ModuleBase::timer::start("module_charge", "cal_rhog_symm_soc"); + + // the three spin components are coupled by the spin rotation, so they are transformed to + // reciprocal space and symmetrized together (rho[1]=rho^x, rho[2]=rho^y, rho[3]=rho^z). + for (int is = 1; is < 4; ++is) + { + rho_basis->real2recip(rho[is], rhog[is]); + } + + detail::psymmg_soc(rhog[1], rhog[2], rhog[3], rho_basis, symm); + + for (int is = 1; is < 4; ++is) + { + rho_basis->recip2real(rhog[is], rho[is]); + } + + ModuleBase::timer::end("module_charge", "cal_rhog_symm_soc"); + return; +} + +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_symm.h b/source/source_estate/module_charge/chg_symm.h new file mode 100644 index 00000000000..556a4fbabdf --- /dev/null +++ b/source/source_estate/module_charge/chg_symm.h @@ -0,0 +1,87 @@ +#ifndef CHG_SYMM_H +#define CHG_SYMM_H + +// TODO: make cal_rhog_symm / cal_rhog_symm_soc internal (detail or anonymous +// namespace) so that external callers only use symmetrize_rho. Blocked by: +// 1. get_pchg_lcao/pw call the double** overload — need a symmetrize_rho +// overload that accepts raw arrays (with nspin=4 branch). +// 2. write_mlkedf_desc symmetrizes a single component of a temporary array +// — symmetrize_rho cannot express that yet. +// 3. setup_pot, ctrl_output_fp, read_wf2rho, update_state_rdmft already use +// the Charge& overload and can be migrated directly. + +#include + +#include "source_basis/module_pw/pw_basis.h" +#include "source_cell/module_symmetry/symmetry.h" + +class Charge; + +/** + * @brief Charge-density symmetrization free functions. + * + * The functions are stateless: every input is passed explicitly. The + * reciprocal-space helpers shared between translation units live in + * module_charge::detail (see chg_symm_detail.h). + */ +namespace module_charge +{ + +/** + * @brief Symmetrize charge density for all spin channels + * + * This is a helper function that symmetrizes the charge density + * for all spin channels by calling cal_rhog_symm() for each spin. + * + * @param nspin Number of spin channels + * @param chr Charge object containing the density + * @param pw Plane wave basis + * @param symm Symmetry object + */ +void symmetrize_rho(const int nspin, + const Charge& chr, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm); + +/** + * @brief Symmetrize one spin channel of the charge density. + */ +void cal_rhog_symm(const int& spin_now, + const Charge& CHR, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm); + +/** + * @brief Symmetrize one spin channel of raw density arrays. + */ +void cal_rhog_symm(const int& spin_now, + double** rho, + std::complex** rhog, + int ngmc, + double** kin_r, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm); + +/// @brief Symmetrize the nspin=4 spin density (rho^x, rho^y, rho^z = rho[1,2,3]) with the +/// coupled spin rotation. The charge component rho^0 = rho[0] is handled separately +/// by the ordinary scalar cal_rhog_symm(). +void cal_rhog_symm_soc(const Charge& CHR, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm); + +/** + * @brief Symmetrize raw nspin=4 spin-density arrays with coupled spin rotations. + * + * @param rho Real-space density components ordered as rho0, mx, my, mz. + * @param rhog Reciprocal-space work arrays with the same component ordering. + * @param pw Plane-wave basis used for the Fourier transforms. + * @param symm Symmetry operations and spin rotations. + */ +void cal_rhog_symm_soc(double** rho, + std::complex** rhog, + const ModulePW::PW_Basis* pw, + ModuleSymmetry::Symmetry& symm); + +} // namespace module_charge + +#endif diff --git a/source/source_estate/module_charge/symm_rhog.cpp b/source/source_estate/module_charge/chg_symm_detail.cpp similarity index 84% rename from source/source_estate/module_charge/symm_rhog.cpp rename to source/source_estate/module_charge/chg_symm_detail.cpp index 86496d049e4..ca73d5e75df 100644 --- a/source/source_estate/module_charge/symm_rhog.cpp +++ b/source/source_estate/module_charge/chg_symm_detail.cpp @@ -1,12 +1,202 @@ -#include "symm_rho.h" +#include "chg_symm_detail.h" #include "source_base/parallel_reduce.h" #include "source_base/parallel_global.h" #include "source_cell/module_symmetry/symm_rot_spin.h" #include "source_hamilt/module_xc/xc_functional.h" +namespace module_charge +{ +namespace +{ + +#ifdef __MPI + +void reduce_to_fullrhog(const ModulePW::PW_Basis* rho_basis, + std::complex* rhogtot, + std::complex* rhogin, + int* ig2isztot, + const int* ig2iszin, + int max_npw) +{ + ModuleBase::TITLE("module_charge","reduce_to_fullrhog"); + + std::complex* rhog_piece = new std::complex[max_npw]; + int* ig2isz_piece = new int[max_npw]; + + int npw_start=0; + for(int proc=0; procpoolnproc; ++proc) + { + ModuleBase::GlobalFunc::ZEROS(rhog_piece, max_npw); + ModuleBase::GlobalFunc::ZEROS(ig2isz_piece, max_npw); + + MPI_Status ierror; + + // case 1: the first part of rho in processor 0 in each pool. + if(proc == 0 && GlobalV::RANK_IN_POOL ==0) + { + for(int ig=0; ignpw; ++ig) + { + rhog_piece[ig] = rhogin[ig]; + ig2isz_piece[ig]=ig2iszin[ig]; + } + } + + // case 2: > first part rho: send the rho to + // processor 0 in each pool + else if(proc == GlobalV::RANK_IN_POOL ) + { + for(int ig=0; ignpw; ++ig) + { + rhog_piece[ig] = rhogin[ig]; + ig2isz_piece[ig]=ig2iszin[ig]; + } + MPI_Send(rhog_piece,rho_basis->npw, MPI_DOUBLE_COMPLEX, 0, proc, POOL_WORLD); + MPI_Send(ig2isz_piece, rho_basis->npw, MPI_INT, 0, proc+rho_basis->poolnproc, POOL_WORLD); + } + + // case 2: > first part rho: processor 0 receive the rho + // from other processors + else if(GlobalV::RANK_IN_POOL==0) + { + MPI_Recv(rhog_piece, rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD, &ierror); + MPI_Recv(ig2isz_piece, rho_basis->npw_per[proc], MPI_INT, proc, proc+rho_basis->poolnproc, POOL_WORLD, &ierror); + } + + if(GlobalV::RANK_IN_POOL==0) + { + for(int ig=0; ignpw_per[proc]; ++ig) + { + rhogtot[npw_start+ig] = rhog_piece[ig]; + ig2isztot[npw_start+ig] = ig2isz_piece[ig]; + } + npw_start+=rho_basis->npw_per[proc]; + } + } + if(GlobalV::RANK_IN_POOL==0) + { + assert(npw_start==rho_basis->npwtot); + } + delete[] rhog_piece; + delete[] ig2isz_piece; + + MPI_Barrier(MPI_COMM_WORLD); + + return; +} + +void rhog_piece_to_all(const ModulePW::PW_Basis* rho_basis, + std::complex* rhogtot, + std::complex* rhog_part) +{ + ModuleBase::TITLE("module_charge","rhog_piece_to_all"); + + MPI_Status ierror; + + if(GlobalV::RANK_IN_POOL==0) + { + // proc 0: send to other proc in pool + // itself: directly copy + for(int ig=0;ignpw;++ig) + { + rhog_part[ig]=rhogtot[ig]; + } + int npw_start=rho_basis->npw; + for(int proc=1;procpoolnproc;++proc) + { + MPI_Send(&rhogtot[npw_start], rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD); + npw_start+=rho_basis->npw_per[proc]; + } + assert(npw_start==rho_basis->npwtot); + }// GlobalV::RANK_IN_POOL == 0 + else + { + MPI_Recv(rhog_part, rho_basis->npw_per[GlobalV::RANK_IN_POOL], MPI_DOUBLE_COMPLEX, 0, GlobalV::RANK_IN_POOL, POOL_WORLD, &ierror); + } + return; +} + +#endif + +// only for MYRANK==0 +void get_ixyz2ipw(const ModulePW::PW_Basis* rho_basis, + const int* ig2isztot, + const int* fftixy2is, + int* ixyz2ipw) +{ + //step 1: get ipsz2ipw + + //get ipsz2ipw from ig2isztot + int* ipsz2ipw = new int [rho_basis->nstot*rho_basis->nz]; + for(int i=0;instot*rho_basis->nz;++i) + { + ipsz2ipw[i]=-1; + } + + int npw_count=0; + int nstnz_count=0; + int ipsz=0; //global index of a z-grid on stick + int isz=0; //local index of a z-grid stick on ip core + int ipw=0; // global index of pw (in npwtot) + for (int ip=0;ippoolnproc;++ip) + { + for (int ig=0;ignpw_per[ip];++ig) + { + ipw=npw_count+ig; + isz=ig2isztot[ipw]; + ipsz=nstnz_count+isz; + ipsz2ipw[ipsz]=ipw; + } + npw_count+=rho_basis->npw_per[ip]; + nstnz_count+=rho_basis->nst_per[ip]*rho_basis->nz; + } + assert(npw_count==rho_basis->npwtot); + assert(nstnz_count==rho_basis->nstot*rho_basis->nz); + + //step2: ixyz to ipsz + + //save the start-index of (nst*nz) till each core + int* nstnz_start = new int[rho_basis->poolnproc]; + nstnz_start[0]=0; + for (int ip=1; ippoolnproc; ++ip) + { + nstnz_start[ip]=nstnz_start[ip-1]+rho_basis->nst_per[ip-1]*rho_basis->nz; + } + + //tmp variables + int ixy, ixyz, ip, is, ig=0; + + for (int ix=0;ixfftnx;++ix) + { + for (int iy=0;iyfftny;++iy) + { + for(int iz=0;izfftnz;++iz) + { + ixy = ix*rho_basis->fftny + iy; + ixyz = ixy*rho_basis->fftnz+iz; + ip = rho_basis->fftixy2ip[ixy]; + if (ip==-1) continue; //not in any core + is = fftixy2is[ixy]; //stick-index on ip=proc core + if (is==-1) continue; //not on any stick + ipsz = nstnz_start[ip]+is*rho_basis->nz+iz; + ipw = ipsz2ipw[ipsz]; + ixyz2ipw[ixyz] = ipw; + } + } + } + assert (ixyz==rho_basis->fftnxyz-1); + + delete[] nstnz_start; + delete[] ipsz2ipw; + return; +} + +} // anonymous namespace + +namespace detail +{ -void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Basis *rho_basis, ModuleSymmetry::Symmetry &symm) const -{ +void psymmg(std::complex* rhog_part, const ModulePW::PW_Basis *rho_basis, ModuleSymmetry::Symmetry &symm) +{ //(1) get fftixy2is and do Allreduce int * fftixy2is = new int [rho_basis->fftnxy]; rho_basis->getfftixy2is(fftixy2is); //current proc @@ -39,7 +229,7 @@ void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Ba max_npw=rho_basis->npw_per[proc]; } } - this->reduce_to_fullrhog(rho_basis, rhogtot, rhog_part, ig2isztot, rho_basis->ig2isz, max_npw); + reduce_to_fullrhog(rho_basis, rhogtot, rhog_part, ig2isztot, rho_basis->ig2isz, max_npw); // (3) get ixy2ipw and do rhog_symmetry on proc 0 of each pool if(GlobalV::RANK_IN_POOL==0) @@ -59,12 +249,12 @@ void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Ba std::vector trs_inv; const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); #ifdef __MPI - this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); - symm.rhog_symmetry(rhogtot, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, + get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); + symm.rhog_symmetry(rhogtot, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); #else - this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); + get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); symm.rhog_symmetry(rhog_part, ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, rho_basis->gamma_only, kgmat.data(), gtr.data(), nop); @@ -74,9 +264,9 @@ void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Ba } // (4) send the result to other procs in the same pool - this->rhog_piece_to_all(rho_basis, rhogtot, rhog_part); + rhog_piece_to_all(rho_basis, rhogtot, rhog_part); - if(GlobalV::RANK_IN_POOL==0) + if(GlobalV::RANK_IN_POOL==0) { delete[] rhogtot; delete[] ig2isztot; @@ -86,8 +276,8 @@ void Symmetry_rho::psymmg(std::complex* rhog_part, const ModulePW::PW_Ba return; } -void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex* rhog_y, - std::complex* rhog_z, const ModulePW::PW_Basis* rho_basis, ModuleSymmetry::Symmetry& symm) const +void psymmg_soc(std::complex* rhog_x, std::complex* rhog_y, + std::complex* rhog_z, const ModulePW::PW_Basis* rho_basis, ModuleSymmetry::Symmetry& symm) { // build the per-operation spin-rotation matrices W(g) from the cartesian rotation // gmatc(g) = direct_to_cartesian(gmatrix(g)) = latvec^-1 * gmatrix(g) * latvec. @@ -149,9 +339,9 @@ void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex max_npw=rho_basis->npw_per[proc]; } } - this->reduce_to_fullrhog(rho_basis, rhogtot_x, rhog_x, ig2isztot, rho_basis->ig2isz, max_npw); - this->reduce_to_fullrhog(rho_basis, rhogtot_y, rhog_y, ig2isztot, rho_basis->ig2isz, max_npw); - this->reduce_to_fullrhog(rho_basis, rhogtot_z, rhog_z, ig2isztot, rho_basis->ig2isz, max_npw); + reduce_to_fullrhog(rho_basis, rhogtot_x, rhog_x, ig2isztot, rho_basis->ig2isz, max_npw); + reduce_to_fullrhog(rho_basis, rhogtot_y, rhog_y, ig2isztot, rho_basis->ig2isz, max_npw); + reduce_to_fullrhog(rho_basis, rhogtot_z, rhog_z, ig2isztot, rho_basis->ig2isz, max_npw); // (3) get ixy2ipw and do rhog_symmetry_nspin4 on proc 0 of each pool if(GlobalV::RANK_IN_POOL==0) @@ -169,13 +359,13 @@ void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex std::vector trs_inv; const int nop = symm.density_sym_ops(kgmat, gtr, trs_inv); #ifdef __MPI - this->get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); + get_ixyz2ipw(rho_basis, ig2isztot, fftixy2is, ixyz2ipw); symm.rhog_symmetry_nspin4(rhogtot_x, rhogtot_y, rhogtot_z, wspin.data(), ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, trs_inv.data(), kgmat.data(), gtr.data(), nop); #else - this->get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); + get_ixyz2ipw(rho_basis, rho_basis->ig2isz, fftixy2is, ixyz2ipw); symm.rhog_symmetry_nspin4(rhog_x, rhog_y, rhog_z, wspin.data(), ixyz2ipw, rho_basis->nx, rho_basis->ny, rho_basis->nz, rho_basis->fftnx, rho_basis->fftny, rho_basis->fftnz, @@ -186,9 +376,9 @@ void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex } // (4) send the result to other procs in the same pool - this->rhog_piece_to_all(rho_basis, rhogtot_x, rhog_x); - this->rhog_piece_to_all(rho_basis, rhogtot_y, rhog_y); - this->rhog_piece_to_all(rho_basis, rhogtot_z, rhog_z); + rhog_piece_to_all(rho_basis, rhogtot_x, rhog_x); + rhog_piece_to_all(rho_basis, rhogtot_y, rhog_y); + rhog_piece_to_all(rho_basis, rhogtot_z, rhog_z); if(GlobalV::RANK_IN_POOL==0) { @@ -202,177 +392,5 @@ void Symmetry_rho::psymmg_soc(std::complex* rhog_x, std::complex return; } -#ifdef __MPI - -void Symmetry_rho::reduce_to_fullrhog(const ModulePW::PW_Basis *rho_basis, - std::complex* rhogtot, std::complex* rhogin, - int* ig2isztot, const int* ig2iszin, int max_npw) const -{ - ModuleBase::TITLE("Symmetry_rho","reduce_to_fullrhog"); - - std::complex* rhog_piece = new std::complex[max_npw]; - int* ig2isz_piece = new int[max_npw]; - - int npw_start=0; - for(int proc=0; procpoolnproc; ++proc) - { - ModuleBase::GlobalFunc::ZEROS(rhog_piece, max_npw); - ModuleBase::GlobalFunc::ZEROS(ig2isz_piece, max_npw); - - MPI_Status ierror; - - // case 1: the first part of rho in processor 0 in each pool. - if(proc == 0 && GlobalV::RANK_IN_POOL ==0) - { - for(int ig=0; ignpw; ++ig) - { - rhog_piece[ig] = rhogin[ig]; - ig2isz_piece[ig]=ig2iszin[ig]; - } - } - - // case 2: > first part rho: send the rho to - // processor 0 in each pool - else if(proc == GlobalV::RANK_IN_POOL ) - { - for(int ig=0; ignpw; ++ig) - { - rhog_piece[ig] = rhogin[ig]; - ig2isz_piece[ig]=ig2iszin[ig]; - } - MPI_Send(rhog_piece,rho_basis->npw, MPI_DOUBLE_COMPLEX, 0, proc, POOL_WORLD); - MPI_Send(ig2isz_piece, rho_basis->npw, MPI_INT, 0, proc+rho_basis->poolnproc, POOL_WORLD); - } - - // case 2: > first part rho: processor 0 receive the rho - // from other processors - else if(GlobalV::RANK_IN_POOL==0) - { - MPI_Recv(rhog_piece, rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD, &ierror); - MPI_Recv(ig2isz_piece, rho_basis->npw_per[proc], MPI_INT, proc, proc+rho_basis->poolnproc, POOL_WORLD, &ierror); - } - - if(GlobalV::RANK_IN_POOL==0) - { - for(int ig=0; ignpw_per[proc]; ++ig) - { - rhogtot[npw_start+ig] = rhog_piece[ig]; - ig2isztot[npw_start+ig] = ig2isz_piece[ig]; - } - npw_start+=rho_basis->npw_per[proc]; - } - } - if(GlobalV::RANK_IN_POOL==0) - { - assert(npw_start==rho_basis->npwtot); - } - delete[] rhog_piece; - delete[] ig2isz_piece; - - MPI_Barrier(MPI_COMM_WORLD); - - return; -} - -void Symmetry_rho::rhog_piece_to_all(const ModulePW::PW_Basis *rho_basis, - std::complex* rhogtot, std::complex* rhog_part) const -{ - ModuleBase::TITLE(" Symmetry_rho","rhog_piece_to_all"); - - MPI_Status ierror; - - if(GlobalV::RANK_IN_POOL==0) - { - // proc 0: send to other proc in pool - // itself: directly copy - for(int ig=0;ignpw;++ig) - { - rhog_part[ig]=rhogtot[ig]; - } - int npw_start=rho_basis->npw; - for(int proc=1;procpoolnproc;++proc) - { - MPI_Send(&rhogtot[npw_start], rho_basis->npw_per[proc], MPI_DOUBLE_COMPLEX, proc, proc, POOL_WORLD); - npw_start+=rho_basis->npw_per[proc]; - } - assert(npw_start==rho_basis->npwtot); - }// GlobalV::RANK_IN_POOL == 0 - else - { - MPI_Recv(rhog_part, rho_basis->npw_per[GlobalV::RANK_IN_POOL], MPI_DOUBLE_COMPLEX, 0, GlobalV::RANK_IN_POOL, POOL_WORLD, &ierror); - } - return; -} - -#endif - -// only for MYRANK==0 -void Symmetry_rho::get_ixyz2ipw(const ModulePW::PW_Basis *rho_basis, - const int* ig2isztot, const int* fftixy2is, int* ixyz2ipw) const -{ - //step 1: get ipsz2ipw - - //get ipsz2ipw from ig2isztot - int* ipsz2ipw = new int [rho_basis->nstot*rho_basis->nz]; - for(int i=0;instot*rho_basis->nz;++i) - { - ipsz2ipw[i]=-1; - } - - int npw_count=0; - int nstnz_count=0; - int ipsz=0; //global index of a z-grid on stick - int isz=0; //local index of a z-grid stick on ip core - int ipw=0; // global index of pw (in npwtot) - for (int ip=0;ippoolnproc;++ip) - { - for (int ig=0;ignpw_per[ip];++ig) - { - ipw=npw_count+ig; - isz=ig2isztot[ipw]; - ipsz=nstnz_count+isz; - ipsz2ipw[ipsz]=ipw; - } - npw_count+=rho_basis->npw_per[ip]; - nstnz_count+=rho_basis->nst_per[ip]*rho_basis->nz; - } - assert(npw_count==rho_basis->npwtot); - assert(nstnz_count==rho_basis->nstot*rho_basis->nz); - - //step2: ixyz to ipsz - - //save the start-index of (nst*nz) till each core - int* nstnz_start = new int[rho_basis->poolnproc]; - nstnz_start[0]=0; - for (int ip=1; ippoolnproc; ++ip) - { - nstnz_start[ip]=nstnz_start[ip-1]+rho_basis->nst_per[ip-1]*rho_basis->nz; - } - - //tmp variables - int ixy, ixyz, ip, is, ig=0; - - for (int ix=0;ixfftnx;++ix) - { - for (int iy=0;iyfftny;++iy) - { - for(int iz=0;izfftnz;++iz) - { - ixy = ix*rho_basis->fftny + iy; - ixyz = ixy*rho_basis->fftnz+iz; - ip = rho_basis->fftixy2ip[ixy]; - if (ip==-1) continue; //not in any core - is = fftixy2is[ixy]; //stick-index on ip=proc core - if (is==-1) continue; //not on any stick - ipsz = nstnz_start[ip]+is*rho_basis->nz+iz; - ipw = ipsz2ipw[ipsz]; - ixyz2ipw[ixyz] = ipw; - } - } - } - assert (ixyz==rho_basis->fftnxyz-1); - - delete[] nstnz_start; - delete[] ipsz2ipw; - return; -} +} // namespace detail +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_symm_detail.h b/source/source_estate/module_charge/chg_symm_detail.h new file mode 100644 index 00000000000..a59da9f2d03 --- /dev/null +++ b/source/source_estate/module_charge/chg_symm_detail.h @@ -0,0 +1,39 @@ +#ifndef CHG_SYMM_DETAIL_H +#define CHG_SYMM_DETAIL_H + +#include + +#include "source_basis/module_pw/pw_basis.h" +#include "source_cell/module_symmetry/symmetry.h" + +/** + * @brief Internal reciprocal-space helpers for charge-density symmetrization. + * + * Not part of the public module_charge API: only chg_symm.cpp and + * chg_symm_detail.cpp are expected to include this header. + */ +namespace module_charge +{ +namespace detail +{ + +/** + * @brief Symmetrize one reciprocal-space density component. + */ +void psymmg(std::complex* rhog_part, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm); + +/** + * @brief Symmetrize the three coupled reciprocal-space spin components for nspin=4. + */ +void psymmg_soc(std::complex* rhog_x, + std::complex* rhog_y, + std::complex* rhog_z, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm); + +} // namespace detail +} // namespace module_charge + +#endif diff --git a/source/source_estate/module_charge/symm_rho.cpp b/source/source_estate/module_charge/symm_rho.cpp deleted file mode 100644 index ade56048ee2..00000000000 --- a/source/source_estate/module_charge/symm_rho.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "symm_rho.h" - -#include "source_estate/module_charge/charge.h" -#include "source_hamilt/module_xc/xc_functional.h" -#include "source_io/module_parameter/parameter.h" - -Symmetry_rho::Symmetry_rho() -{ -} - -Symmetry_rho::~Symmetry_rho() -{ -} - -void Symmetry_rho::symmetrize_rho(const int nspin, - const Charge& chr, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm) -{ - Symmetry_rho srho; - if (nspin == 4) - { - // nspin=4 (non-collinear/SOC): rho[0] is the charge density rho^0 (scalar, symmetrized - // spatially like nspin=1); rho[1,2,3] are the spin density (rho^x, rho^y, rho^z) which - // must be symmetrized TOGETHER with the per-operation spin rotation W(g). - srho.begin(0, chr, pw, symm); - srho.begin_soc(chr, pw, symm); - return; - } - for (int is = 0; is < nspin; is++) - { - srho.begin(is, chr, pw, symm); - } -} - -void Symmetry_rho::begin(const int& spin_now, - const Charge& chr, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const -{ - assert(spin_now < 4); // added by zhengdy-soc - - if (ModuleSymmetry::Symmetry::symm_flag != 1) - { - return; - } - - ModuleBase::TITLE("Symmetry_rho", "begin"); - ModuleBase::timer::start("Symmetry_rho","begin"); - -// both parallel and serial -// if(symm.nrot==symm.nrotk) //pure point-group, do rho_symm in real space -// { -// psymm(chr.rho[spin_now], rho_basis, Pgrid, symm); -// if(XC_Functional::get_ked_flag()) psymm(chr.kin_r[spin_now], -// rho_basis,Pgrid,symm); -// } -// else //space group, do rho_symm in reciprocal space - - rho_basis->real2recip(chr.rho[spin_now], chr.rhog[spin_now]); - - psymmg(chr.rhog[spin_now], rho_basis, symm); // need to modify - - rho_basis->recip2real(chr.rhog[spin_now], chr.rho[spin_now]); - - if (XC_Functional::get_ked_flag() || chr.cal_elf) - { - // Use std::vector to manage kin_g instead of raw pointer - std::vector> kin_g(chr.ngmc); - rho_basis->real2recip(chr.kin_r[spin_now], kin_g.data()); - psymmg(kin_g.data(), rho_basis, symm); - rho_basis->recip2real(kin_g.data(), chr.kin_r[spin_now]); - } - - ModuleBase::timer::end("Symmetry_rho","begin"); - return; -} - -void Symmetry_rho::begin(const int& spin_now, - double** rho, - std::complex** rhog, - int ngmc, - double** kin_r, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const -{ - assert(spin_now < 4); // added by zhengdy-soc - - if (ModuleSymmetry::Symmetry::symm_flag != 1) - { - return; - } - - ModuleBase::TITLE("Symmetry_rho", "begin"); - ModuleBase::timer::start("Symmetry_rho","begin"); - - // both parallel and serial - // if(symm.nrot==symm.nrotk) //pure point-group, do rho_symm in real space - // { - // psymm(CHR.rho[spin_now], rho_basis, Pgrid, symm); - // if(XC_Functional::get_ked_flag()) psymm(CHR.kin_r[spin_now], - // rho_basis,Pgrid,symm); - // } - // else //space group, do rho_symm in reciprocal space - { - rho_basis->real2recip(rho[spin_now], rhog[spin_now]); - psymmg(rhog[spin_now], rho_basis, symm); - rho_basis->recip2real(rhog[spin_now], rho[spin_now]); - - if (XC_Functional::get_ked_flag() && kin_r != nullptr) - { - // Use std::vector to manage kin_g instead of raw pointer - std::vector> kin_g(ngmc); - rho_basis->real2recip(kin_r[spin_now], kin_g.data()); - psymmg(kin_g.data(), rho_basis, symm); - rho_basis->recip2real(kin_g.data(), kin_r[spin_now]); - } - } - - ModuleBase::timer::end("Symmetry_rho","begin"); - return; -} - -void Symmetry_rho::begin_soc(const Charge& chr, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const -{ - this->begin_soc(chr.rho, chr.rhog, rho_basis, symm); -} - -void Symmetry_rho::begin_soc(double** rho, - std::complex** rhog, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const -{ - if (ModuleSymmetry::Symmetry::symm_flag != 1) - { - return; - } - - ModuleBase::TITLE("Symmetry_rho", "begin_soc"); - ModuleBase::timer::start("Symmetry_rho", "begin_soc"); - - // the three spin components are coupled by the spin rotation, so they are transformed to - // reciprocal space and symmetrized together (rho[1]=rho^x, rho[2]=rho^y, rho[3]=rho^z). - for (int is = 1; is < 4; ++is) - { - rho_basis->real2recip(rho[is], rhog[is]); - } - - psymmg_soc(rhog[1], rhog[2], rhog[3], rho_basis, symm); - - for (int is = 1; is < 4; ++is) - { - rho_basis->recip2real(rhog[is], rho[is]); - } - - ModuleBase::timer::end("Symmetry_rho", "begin_soc"); - return; -} - -void Symmetry_rho::psymm(double* rho_part, - const ModulePW::PW_Basis* rho_basis, - Parallel_Grid& Pgrid, - ModuleSymmetry::Symmetry& symm) const -{ - ModuleBase::TITLE("Symmetry_rho", "psymm"); - ModuleBase::timer::start("Symmetry_rho","psymm"); - -#ifdef __MPI - // reduce all rho from the first pool. - std::vector rhotot; - if (GlobalV::MY_RANK == 0) - { - rhotot.resize(rho_basis->nxyz); - ModuleBase::GlobalFunc::ZEROS(rhotot.data(), rho_basis->nxyz); - } - Pgrid.reduce(rhotot.data(), rho_part, false); - - if (GlobalV::MY_RANK == 0) - { - symm.rho_symmetry(rhotot.data(), rho_basis->nx, rho_basis->ny, rho_basis->nz); -#else - symm.rho_symmetry(rho_part, rho_basis->nx, rho_basis->ny, rho_basis->nz); -#endif - /* - int count = 0; - GlobalV::ofs_running << scientific; - for(int iz=0; iznz; iz++) - { - GlobalV::ofs_running << "\n iz=" << iz; - for(int iy=0; iyny; iy++) - { - for(int ix=0; ixnx; ix++) - { - if(count%5==0) - { - GlobalV::ofs_running << "\n"; - } - ++count; - GlobalV::ofs_running << " " << rhotot[ix*rho_basis->ny*rho_basis->nz+iy*rho_basis->nz+iz]; - } - } - } - */ -#ifdef __MPI - } - - Pgrid.bcast(rhotot.data(), rho_part, GlobalV::MY_RANK, PARAM.inp.esolver_type == "sdft"); -#endif - - ModuleBase::timer::end("Symmetry_rho","psymm"); - return; -} diff --git a/source/source_estate/module_charge/symm_rho.h b/source/source_estate/module_charge/symm_rho.h deleted file mode 100644 index f9d1cb48a89..00000000000 --- a/source/source_estate/module_charge/symm_rho.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef SYMM_RHO_H -#define SYMM_RHO_H -#include "source_basis/module_pw/pw_basis.h" -#include "source_cell/module_symmetry/symmetry.h" -#include "source_base/parallel_grid.h" - -class Charge; - -class Symmetry_rho -{ - public: - Symmetry_rho(); - ~Symmetry_rho(); - - /** - * @brief Symmetrize charge density for all spin channels - * - * This is a static helper function that symmetrizes the charge density - * for all spin channels by calling begin() for each spin. - * - * @param nspin Number of spin channels - * @param chr Charge object containing the density - * @param pw Plane wave basis - * @param symm Symmetry object - */ - static void symmetrize_rho(const int nspin, - const Charge& chr, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm); - - void begin(const int& spin_now, - const Charge& CHR, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm) const; - - void begin(const int& spin_now, - double** rho, - std::complex** rhog, - int ngmc, - double** kin_r, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm) const; - - /// @brief Symmetrize the nspin=4 spin density (rho^x, rho^y, rho^z = rho[1,2,3]) with the - /// coupled spin rotation. The charge component rho^0 = rho[0] is handled separately - /// by the ordinary scalar begin(). - void begin_soc(const Charge& CHR, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm) const; - - /** - * @brief Symmetrize raw nspin=4 spin-density arrays with coupled spin rotations. - * - * @param rho Real-space density components ordered as rho0, mx, my, mz. - * @param rhog Reciprocal-space work arrays with the same component ordering. - * @param pw Plane-wave basis used for the Fourier transforms. - * @param symm Symmetry operations and spin rotations. - */ - void begin_soc(double** rho, - std::complex** rhog, - const ModulePW::PW_Basis* pw, - ModuleSymmetry::Symmetry& symm) const; - - private: - // in real space: - void psymm(double* rho_part, - const ModulePW::PW_Basis* pw, - Parallel_Grid& Pgrid, - ModuleSymmetry::Symmetry& symm) const; - // in reciprocal space: - void psymmg(std::complex* rhog_part, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const; - // in reciprocal space, the three coupled spin components (rho^x, rho^y, rho^z) for nspin=4: - void psymmg_soc(std::complex* rhog_x, - std::complex* rhog_y, - std::complex* rhog_z, - const ModulePW::PW_Basis* rho_basis, - ModuleSymmetry::Symmetry& symm) const; -#ifdef __MPI - void reduce_to_fullrhog(const ModulePW::PW_Basis* rho_basis, - std::complex* rhogtot, - std::complex* rhogin, - int* ig2isztot, - const int* ig2iszin, - int max_npw) const; - void rhog_piece_to_all(const ModulePW::PW_Basis* rho_basis, - std::complex* rhogtot, - std::complex* rhog_part) const; -#endif - void get_ixyz2ipw(const ModulePW::PW_Basis* rho_basis, - const int* ig2isztot, - const int* fftixy2is, - int* ixyz2ipw) const; //(ix, iy, iz) -> (ip, ig) -}; - -#endif diff --git a/source/source_hsolver/hsolver_pw_sdft.cpp b/source/source_hsolver/hsolver_pw_sdft.cpp index db075375515..579de407396 100644 --- a/source/source_hsolver/hsolver_pw_sdft.cpp +++ b/source/source_hsolver/hsolver_pw_sdft.cpp @@ -5,7 +5,7 @@ #include "source_base/parallel_device.h" #include "source_base/timer.h" #include "source_base/tool_title.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_estate/elecstate_tools.h" #include diff --git a/source/source_io/module_chgpot/get_pchg_lcao.cpp b/source/source_io/module_chgpot/get_pchg_lcao.cpp index ae11f7b6e74..f098ce0941d 100644 --- a/source/source_io/module_chgpot/get_pchg_lcao.cpp +++ b/source/source_io/module_chgpot/get_pchg_lcao.cpp @@ -1,6 +1,6 @@ #include "get_pchg_lcao.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.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" @@ -191,17 +191,16 @@ void Get_pchg_lcao::begin_k(const ModulePW::PW_Basis& rho_pw, // Symmetrize only the merged density, using coupled spin rotations for nspin=4. if (needs_symmetry) { - Symmetry_rho srho; if (nspin_ == 4) { - srho.begin(0, rho_pointers.data(), rhog_pointers.data(), rho_pw.npw, nullptr, &rho_pw, ucell.symm); - srho.begin_soc(rho_pointers.data(), rhog_pointers.data(), &rho_pw, ucell.symm); + module_charge::cal_rhog_symm(0, rho_pointers.data(), rhog_pointers.data(), rho_pw.npw, nullptr, &rho_pw, ucell.symm); + module_charge::cal_rhog_symm_soc(rho_pointers.data(), rhog_pointers.data(), &rho_pw, ucell.symm); } else { for (int is = 0; is < nspin_; ++is) { - srho.begin(is, rho_pointers.data(), rhog_pointers.data(), rho_pw.npw, nullptr, &rho_pw, ucell.symm); + module_charge::cal_rhog_symm(is, rho_pointers.data(), rhog_pointers.data(), rho_pw.npw, nullptr, &rho_pw, ucell.symm); } } } diff --git a/source/source_io/module_chgpot/get_pchg_pw.cpp b/source/source_io/module_chgpot/get_pchg_pw.cpp index 7abde6c7ffe..bb738965377 100644 --- a/source/source_io/module_chgpot/get_pchg_pw.cpp +++ b/source/source_io/module_chgpot/get_pchg_pw.cpp @@ -4,7 +4,7 @@ #include "source_base/module_device/memory_op.h" #include "source_base/module_parallel/para_bridge.h" #include "source_base/tool_quit.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_estate/uspp_density.h" #include "source_io/module_output/cube_io.h" @@ -427,7 +427,6 @@ void Get_pchg_pw::sum_pools(const Parallel_Grid& pgrid, Workspace* wo template void Get_pchg_pw::symmetrize(UnitCell* ucell, Workspace* work) const { - Symmetry_rho srho; std::vector rho_pointers(nspin_); std::vector>> rhog(nspin_, std::vector>(pw_rhod_.npw)); std::vector*> rhog_pointers(nspin_); @@ -441,14 +440,14 @@ void Get_pchg_pw::symmetrize(UnitCell* ucell, Workspace* work) const if (work->is_spinor) { // Charge and magnetization obey different spinor symmetry transformations. - srho.begin(0, rho_pointers.data(), rhog_pointers.data(), pw_rhod_.npw, nullptr, &pw_rhod_, ucell->symm); - srho.begin_soc(rho_pointers.data(), rhog_pointers.data(), &pw_rhod_, ucell->symm); + module_charge::cal_rhog_symm(0, rho_pointers.data(), rhog_pointers.data(), pw_rhod_.npw, nullptr, &pw_rhod_, ucell->symm); + module_charge::cal_rhog_symm_soc(rho_pointers.data(), rhog_pointers.data(), &pw_rhod_, ucell->symm); } else { for (int is = 0; is < nspin_; ++is) { - srho.begin(is, rho_pointers.data(), rhog_pointers.data(), pw_rhod_.npw, nullptr, &pw_rhod_, ucell->symm); + module_charge::cal_rhog_symm(is, rho_pointers.data(), rhog_pointers.data(), pw_rhod_.npw, nullptr, &pw_rhod_, ucell->symm); } } } diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index e00dbe3f03f..815d4305aea 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -1,7 +1,7 @@ #include "ctrl_output_fp.h" // use ctrl_output_fp() #include "../module_output/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_estate/module_charge/chg_symm.h" // use module_charge::cal_rhog_symm #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" @@ -168,10 +168,9 @@ void ctrl_output_fp(UnitCell& ucell, if (inp.out_elf[0] > 0 && should_output) { chr.cal_elf = true; - Symmetry_rho srho; for (int is = 0; is < nspin; is++) { - srho.begin(is, chr, pw_rhod, ucell.symm); + module_charge::cal_rhog_symm(is, chr, pw_rhod, ucell.symm); } std::string out_dir = PARAM.globalv.global_out_dir; diff --git a/source/source_io/module_ml/write_mlkedf_desc.cpp b/source/source_io/module_ml/write_mlkedf_desc.cpp index 3bb2476ebc5..c372d731fb1 100644 --- a/source/source_io/module_ml/write_mlkedf_desc.cpp +++ b/source/source_io/module_ml/write_mlkedf_desc.cpp @@ -3,7 +3,7 @@ #include "write_mlkedf_desc.h" #include "npy.hpp" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" namespace ModuleIO { @@ -32,19 +32,17 @@ void Write_MLKEDF_Descriptors::generateTrainData_KS( this->cal_tool->getF_KS(psi, pelec, pw_psi, pw_rho, ucell, drho, enhancement, pauli); - Symmetry_rho srho; - std::vector rho_vec(nrxx); std::vector> rhog_vec(pw_rho->npw); double* rho_ptr = rho_vec.data(); std::complex* rhog_ptr = rhog_vec.data(); std::copy(enhancement.begin(), enhancement.end(), rho_vec.begin()); - srho.begin(0, &rho_ptr, &rhog_ptr, pw_rho->npw, nullptr, pw_rho, ucell.symm); + module_charge::cal_rhog_symm(0, &rho_ptr, &rhog_ptr, pw_rho->npw, nullptr, pw_rho, ucell.symm); std::copy(rho_vec.begin(), rho_vec.end(), enhancement.begin()); std::copy(pauli.begin(), pauli.end(), rho_vec.begin()); - srho.begin(0, &rho_ptr, &rhog_ptr, pw_rho->npw, nullptr, pw_rho, ucell.symm); + module_charge::cal_rhog_symm(0, &rho_ptr, &rhog_ptr, pw_rho->npw, nullptr, pw_rho, ucell.symm); std::copy(rho_vec.begin(), rho_vec.end(), pauli.begin()); diff --git a/source/source_io/module_wf/read_wf2rho_pw.cpp b/source/source_io/module_wf/read_wf2rho_pw.cpp index 0c6ea096139..75aea2e469f 100644 --- a/source/source_io/module_wf/read_wf2rho_pw.cpp +++ b/source/source_io/module_wf/read_wf2rho_pw.cpp @@ -4,7 +4,7 @@ #include "source_base/module_out/filename.h" #include "source_base/timer.h" #include "source_estate/kernels/elecstate_op.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_io/module_parameter/parameter.h" #include "source_base/module_device/memory_op.h" @@ -223,10 +223,9 @@ void ModuleIO::read_wf2rho_pw_impl(const ModulePW::PW_Basis_K* pw_wfc, #endif // Since rho is calculated by psi^2, it is not symmetric. We need to rearrange it. - Symmetry_rho srho; for (int is = 0; is < nspin; is++) { - srho.begin(is, chg, chg.rhopw, symm); + module_charge::cal_rhog_symm(is, chg, chg.rhopw, symm); } // Free device memory diff --git a/source/source_io/test/read_wf2rho_pw_test.cpp b/source/source_io/test/read_wf2rho_pw_test.cpp index 70a4b16a63c..294b8a819e4 100644 --- a/source/source_io/test/read_wf2rho_pw_test.cpp +++ b/source/source_io/test/read_wf2rho_pw_test.cpp @@ -9,7 +9,7 @@ #include "source_cell/klist.h" #include "source_cell/unitcell.h" #include "source_estate/module_charge/charge.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" #include "source_io/module_wf/read_wf2rho_pw.h" @@ -55,16 +55,16 @@ Sep_Cell::~Sep_Cell() noexcept int XC_Functional::func_type = 0; bool XC_Functional::ked_flag = false; -Symmetry_rho::Symmetry_rho() +namespace module_charge { -} -Symmetry_rho::~Symmetry_rho() -{ -} -void Symmetry_rho::begin(const int& spin_now, const Charge& CHR, const ModulePW::PW_Basis* rho_basis, ModuleSymmetry::Symmetry& symm) const +void cal_rhog_symm(const int& spin_now, + const Charge& CHR, + const ModulePW::PW_Basis* rho_basis, + ModuleSymmetry::Symmetry& symm) { return; } +} // namespace module_charge void cal_ik2iktot(std::vector& ik2iktot, const int& nks, const int& nkstot) { diff --git a/source/source_lcao/module_rdmft/update_state_rdmft.cpp b/source/source_lcao/module_rdmft/update_state_rdmft.cpp index 69ca9d2d528..27424124d1b 100644 --- a/source/source_lcao/module_rdmft/update_state_rdmft.cpp +++ b/source/source_lcao/module_rdmft/update_state_rdmft.cpp @@ -7,7 +7,7 @@ #include "source_lcao/module_rdmft/rdmft_tools.h" #include "source_estate/module_dm/cal_dm_psi.h" #include "source_estate/module_dm/density_matrix.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/module_gint/gint_interface.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -139,10 +139,9 @@ void RDMFT::update_charge(UnitCell& ucell) } // charge density symmetrization - Symmetry_rho srho; for (int is = 0; is < nspin; is++) { - srho.begin(is, *(this->charge), rho_basis, ucell.symm); + module_charge::cal_rhog_symm(is, *(this->charge), rho_basis, ucell.symm); } } diff --git a/source/source_pw/module_pwdft/setup_pot.cpp b/source/source_pw/module_pwdft/setup_pot.cpp index 4541f891a9d..6729111d27f 100644 --- a/source/source_pw/module_pwdft/setup_pot.cpp +++ b/source/source_pw/module_pwdft/setup_pot.cpp @@ -1,6 +1,6 @@ #include "source_pw/module_pwdft/setup_pot.h" -#include "source_estate/module_charge/symm_rho.h" +#include "source_estate/module_charge/chg_symm.h" #include "source_lcao/module_deltaspin/spin_constrain.h" #include "source_pw/module_pwdft/onsite_proj.h" #include "source_pw/module_pwdft/vnl_pw.h" @@ -47,13 +47,12 @@ void pw::setup_pot(const int istep, //! 2) Symmetrize the charge density (rho) //---------------------------------------------------------- - //! Symmetry_rho should behind init_scf, because charge should be - //! initialized first. liuyu comment: Symmetry_rho should be + //! Density symmetrization should be behind init_scf, because charge should be + //! initialized first. liuyu comment: it should be //! located between init_rho and v_of_rho? - Symmetry_rho srho; for (int is = 0; is < inp.nspin; is++) { - srho.begin(is, chr, pw_rhod, ucell.symm); + module_charge::cal_rhog_symm(is, chr, pw_rhod, ucell.symm); } //---------------------------------------------------------- From dfbf99d2ae4410d9c00bc5b72eb042938125ec45 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 13:27:50 +0800 Subject: [PATCH 17/37] module_charge: extract MixingConfig header and drop unused inner_product_recip_simple Move MixingConfig from charge_mixing.h into its own mixing_config.h so stateless residual kernels can include the config without dragging in Charge_Mixing. Remove inner_product_recip_simple, which had no production call sites, together with its unit test. --- .../module_charge/charge_mixing.h | 30 ++---------------- .../source_estate/module_charge/mix_resid.cpp | 27 ---------------- .../module_charge/mixing_config.h | 31 +++++++++++++++++++ .../source_estate/test/charge_mixing_test.cpp | 22 ------------- 4 files changed, 33 insertions(+), 77 deletions(-) create mode 100644 source/source_estate/module_charge/mixing_config.h diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 42fb9449499..907421abbd9 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -1,36 +1,12 @@ #ifndef CHARGE_MIXING_H #define CHARGE_MIXING_H #include "charge.h" +#include "mixing_config.h" #include "source_estate/module_dm/density_matrix.h" #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" #include -/// Configuration for charge mixing, aggregating the INPUT mixing parameters -/// together with the runtime globals (nspin, scf_thr_type, double_grid, -/// gamma_only_pw, domag, domag_z) that the mixing logic needs, so that -/// Charge_Mixing does not read PARAM/GlobalV directly. Callers fill this -/// from the parsed input once per run. -struct MixingConfig -{ - std::string mixing_mode = "broyden"; ///< mixing mode: "plain", "broyden", "pulay" - double mixing_beta = 0.8; ///< mixing beta for density - int mixing_ndim = 8; ///< mixing ndim for broyden and pulay - double mixing_gg0 = 0.0; ///< mixing gg0 for Kerker screen - bool mixing_tau = false; ///< whether to use tau mixing - double mixing_beta_mag = 1.6; ///< mixing beta for magnetism - double mixing_gg0_mag = 0.0; ///< mixing gg0 for Kerker screen for magnetism - double mixing_gg0_min = 0.1; ///< minimum kerker coefficient - double mixing_angle = 0.0; ///< mixing angle for nspin=4 - bool mixing_dmr = false; ///< whether to mix real space density matrix - int nspin = 1; ///< number of spins - int scf_thr_type = 1; ///< 1: reciprocal, 2: real space threshold - bool double_grid = false; ///< whether double grid is used - bool gamma_only_pw = false; ///< whether gamma-only plane wave is used - bool domag = false; ///< whether magnetism (non-collinear) is considered - bool domag_z = false; ///< whether only the z-component magnetism is considered -}; - class Charge_Mixing { /// Charge_Mixing class @@ -194,12 +170,10 @@ class Charge_Mixing /** * @brief Inner product of two complex vectors * @brief inner_product_recip_rho is used for charge, like get_drho() - * @brief inner_product_recip_hartree and inner_product_recip_simple are used for charge mixing - * @brief inner_product_recip_simple is only used for test + * @brief inner_product_recip_hartree is used for charge mixing * @brief Actually, I am not sure if the definition of inner product for NSPIN=4 is correct, need to be checked. */ double inner_product_recip_rho(std::complex* rho1, std::complex* rho2); - double inner_product_recip_simple(std::complex* rho1, std::complex* rho2); double inner_product_recip_hartree(std::complex* rho1, std::complex* rho2); /** diff --git a/source/source_estate/module_charge/mix_resid.cpp b/source/source_estate/module_charge/mix_resid.cpp index fc880838c51..5c8b7fd94e5 100644 --- a/source/source_estate/module_charge/mix_resid.cpp +++ b/source/source_estate/module_charge/mix_resid.cpp @@ -260,33 +260,6 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c return sum; } -// a simple inner product, now is not used anywhere. For test only. -double Charge_Mixing::inner_product_recip_simple(std::complex* rho1, std::complex* rho2) -{ - ModuleBase::TITLE("Charge_Mixing", "recip_simple"); - ModuleBase::timer::start("Charge_Mixing", "recip_simple"); - - double rnorm = 0.0; - // consider a resize for mixing_angle - int resize_tmp = 1; - if (this->cfg_.nspin == 4 && this->mixing_angle > 0) { resize_tmp = 2; -} -#ifdef _OPENMP -#pragma omp parallel for reduction(+ : rnorm) -#endif - for (int ig = 0; ig < this->rhopw->npw * this->cfg_.nspin / resize_tmp; ++ig) - { - rnorm += (conj(rho1[ig]) * rho2[ig]).real(); - } -#ifdef __MPI - Parallel_Reduce::reduce_pool(rnorm); -#endif - - ModuleBase::timer::end("Charge_Mixing", "recip_simple"); - - return rnorm; -} - // a Hartree-like inner product double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, std::complex* rhog2) { diff --git a/source/source_estate/module_charge/mixing_config.h b/source/source_estate/module_charge/mixing_config.h new file mode 100644 index 00000000000..6f0f251c2d1 --- /dev/null +++ b/source/source_estate/module_charge/mixing_config.h @@ -0,0 +1,31 @@ +#ifndef MIXING_CONFIG_H +#define MIXING_CONFIG_H + +#include + +/// Configuration for charge mixing, aggregating the INPUT mixing parameters +/// together with the runtime globals (nspin, scf_thr_type, double_grid, +/// gamma_only_pw, domag, domag_z) that the mixing logic needs, so that +/// Charge_Mixing does not read PARAM/GlobalV directly. Callers fill this +/// from the parsed input once per run. +struct MixingConfig +{ + std::string mixing_mode = "broyden"; ///< mixing mode: "plain", "broyden", "pulay" + double mixing_beta = 0.8; ///< mixing beta for density + int mixing_ndim = 8; ///< mixing ndim for broyden and pulay + double mixing_gg0 = 0.0; ///< mixing gg0 for Kerker screen + bool mixing_tau = false; ///< whether to use tau mixing + double mixing_beta_mag = 1.6; ///< mixing beta for magnetism + double mixing_gg0_mag = 0.0; ///< mixing gg0 for Kerker screen for magnetism + double mixing_gg0_min = 0.1; ///< minimum kerker coefficient + double mixing_angle = 0.0; ///< mixing angle for nspin=4 + bool mixing_dmr = false; ///< whether to mix real space density matrix + int nspin = 1; ///< number of spins + int scf_thr_type = 1; ///< 1: reciprocal, 2: real space threshold + bool double_grid = false; ///< whether double grid is used + bool gamma_only_pw = false; ///< whether gamma-only plane wave is used + bool domag = false; ///< whether magnetism (non-collinear) is considered + bool domag_z = false; ///< whether only the z-component magnetism is considered +}; + +#endif // MIXING_CONFIG_H diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index 8de616e257e..fa989365bc6 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -57,7 +57,6 @@ void Charge::set_rhopw(ModulePW::PW_Basis* rhopw_in) * - screen drho with Kerker method * - InnerDotTest: Charge_Mixing::inner_product_recip_hartree(rhog1, rhog2) * Charge_Mixing::inner_product_recip_rho(rhog1, rhog2) - * Charge_Mixing::inner_product_recip_simple(rhog1, rhog2) * Charge_Mixing::inner_product_real(rho1, rho2) * - calculate the inner product of two vectors * - MixRhoTest: Charge_Mixing::mix_rho(chr) @@ -277,27 +276,6 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) EXPECT_NEAR(inner, 0.5 * pw_basis.nrxx * 2 * (pw_basis.nrxx * 2 - 1), 1e-8); } -TEST_F(ChargeMixingTest, InnerDotRecipSimpleTest) -{ - Charge_Mixing CMtest; - // non mixing angle case - CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); - CMtest.set_rhopw(&pw_basis, &pw_basis); - PARAM.input.nspin = 2; - sync_cfg(CMtest); - - // a simple sum for inner product - std::vector> drhog1(pw_basis.npw * PARAM.input.nspin); - std::vector> drhog2(pw_basis.npw * PARAM.input.nspin); - for (int i = 0; i < pw_basis.npw * PARAM.input.nspin; ++i) - { - drhog1[i] = 1.0; - drhog2[i] = double(i); - } - double inner = CMtest.inner_product_recip_simple(drhog1.data(), drhog2.data()); - EXPECT_NEAR(inner, 0.5 * pw_basis.npw * PARAM.input.nspin * (pw_basis.npw * PARAM.input.nspin - 1), 1e-8); -} - TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) { // REAL From 619029d5922160030c2751ce9db75ee3af398374 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 13:29:04 +0800 Subject: [PATCH 18/37] module_gint: move gint_prec_ctrl from module_charge Relocate gint_prec_ctrl.{h,cpp} and its test into module_gint, update the include in esolver_ks_lcao.h and rewire the CMake/Makefile object lists. --- source/Makefile.Objects | 2 +- source/source_esolver/esolver_ks_lcao.h | 2 +- source/source_estate/CMakeLists.txt | 1 - source/source_estate/test/CMakeLists.txt | 7 ------- source/source_hamilt/module_gint/CMakeLists.txt | 1 + .../module_gint}/gint_prec_ctrl.cpp | 0 .../module_gint}/gint_prec_ctrl.h | 0 source/source_hamilt/module_gint/test/CMakeLists.txt | 7 +++++++ .../module_gint/test/test_gint_prec_ctrl.cpp} | 0 9 files changed, 10 insertions(+), 10 deletions(-) rename source/{source_estate/module_charge => source_hamilt/module_gint}/gint_prec_ctrl.cpp (100%) rename source/{source_estate/module_charge => source_hamilt/module_gint}/gint_prec_ctrl.h (100%) rename source/{source_estate/test/gint_prec_ctrl_test.cpp => source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp} (100%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index b42c99790fd..39ff69276b1 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -330,6 +330,7 @@ OBJS_GINT=batch_biggrid.o\ gint_fvl.o\ gint_info.o\ gint_interface.o\ + gint_prec_ctrl.o\ gint_rho.o\ gint_drho.o\ gint_tau.o\ @@ -824,7 +825,6 @@ OBJS_SRCPW=h_ewald_pw.o\ vnl_pw_init_vnl.o\ vnl_pw_qrad.o\ chgmixing.o\ - gint_prec_ctrl.o\ charge.o\ charge_atomic.o\ charge_init.o\ diff --git a/source/source_esolver/esolver_ks_lcao.h b/source/source_esolver/esolver_ks_lcao.h index 50ee0e6da7a..317b20aedc3 100644 --- a/source/source_esolver/esolver_ks_lcao.h +++ b/source/source_esolver/esolver_ks_lcao.h @@ -6,7 +6,7 @@ #include "source_lcao/record_adj.h" // adjacent atoms #include "source_basis/module_nao/two_center_bundle.h" // nao basis #include "source_hamilt/module_gint/gint_info.h" -#include "source_estate/module_charge/gint_prec_ctrl.h" +#include "source_hamilt/module_gint/gint_prec_ctrl.h" #include "source_lcao/setup_deepks.h" // for deepks, mohan add 20251008 #include "source_lcao/setup_exx.h" // for exx, mohan add 20251008 #include "source_lcao/module_rdmft/rdmft.h" // rdmft diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 79e5c4d270a..26ced15bbac 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -28,7 +28,6 @@ list(APPEND objects module_pot/td_field_profiles.cpp module_pot/td_field_manager.cpp module_charge/chgmixing.cpp - module_charge/gint_prec_ctrl.cpp module_charge/charge.cpp module_charge/charge_atomic.cpp module_charge/charge_init.cpp diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 81576bdb481..14cfab7ff76 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -123,13 +123,6 @@ AddTest( ../../source_base/module_fft/fft_bundle.cpp ../../source_base/module_fft/fft_cpu.cpp ) -AddTest( - TARGET MODULE_ESTATE_gint_prec_ctrl - LIBS parameter base device - SOURCES gint_prec_ctrl_test.cpp - ../module_charge/gint_prec_ctrl.cpp -) - AddTest( TARGET MODULE_ESTATE_test_rhog_io LIBS parameter base device planewave diff --git a/source/source_hamilt/module_gint/CMakeLists.txt b/source/source_hamilt/module_gint/CMakeLists.txt index 10b4421f956..28c5f2aed15 100644 --- a/source/source_hamilt/module_gint/CMakeLists.txt +++ b/source/source_hamilt/module_gint/CMakeLists.txt @@ -26,6 +26,7 @@ list(APPEND objects unitcell_info.cpp gint_common.cpp gint_interface.cpp + gint_prec_ctrl.cpp ) if(USE_CUDA) list(APPEND objects diff --git a/source/source_estate/module_charge/gint_prec_ctrl.cpp b/source/source_hamilt/module_gint/gint_prec_ctrl.cpp similarity index 100% rename from source/source_estate/module_charge/gint_prec_ctrl.cpp rename to source/source_hamilt/module_gint/gint_prec_ctrl.cpp diff --git a/source/source_estate/module_charge/gint_prec_ctrl.h b/source/source_hamilt/module_gint/gint_prec_ctrl.h similarity index 100% rename from source/source_estate/module_charge/gint_prec_ctrl.h rename to source/source_hamilt/module_gint/gint_prec_ctrl.h diff --git a/source/source_hamilt/module_gint/test/CMakeLists.txt b/source/source_hamilt/module_gint/test/CMakeLists.txt index 7cbfcbb9d11..8a678e2941c 100644 --- a/source/source_hamilt/module_gint/test/CMakeLists.txt +++ b/source/source_hamilt/module_gint/test/CMakeLists.txt @@ -25,4 +25,11 @@ AddTest( tmp_mocks.cpp ) +AddTest( + TARGET MODULE_LCAO_gint_prec_ctrl_test + LIBS parameter base device + SOURCES test_gint_prec_ctrl.cpp + ../gint_prec_ctrl.cpp +) + endif() diff --git a/source/source_estate/test/gint_prec_ctrl_test.cpp b/source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp similarity index 100% rename from source/source_estate/test/gint_prec_ctrl_test.cpp rename to source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp From 2ac00171c08de0cd42b38e21349f700e3811d7f1 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 13:40:04 +0800 Subject: [PATCH 19/37] module_charge: extract mixing inner products into chg_drho free functions Rename mix_resid.cpp to chg_drho.cpp and turn inner_product_real and inner_product_recip_hartree into module_charge free functions declared in chg_drho.h; inner_product_recip_rho, which is only shared with the unit test, moves to module_charge::detail in chg_drho_detail.h. Charge_Mixing loses the three private inner-product members and mix_rho_recip/mix_rho_real bind the free functions through lambdas. get_drho/get_dkin stay as members for this step. --- source/Makefile.Objects | 2 +- source/source_estate/CMakeLists.txt | 2 +- .../module_charge/charge_mixing.h | 15 -- .../module_charge/charge_mixing_dmr.cpp | 6 - .../module_charge/charge_mixing_rho.cpp | 32 ++- .../{mix_resid.cpp => chg_drho.cpp} | 208 ++++++++++-------- source/source_estate/module_charge/chg_drho.h | 54 +++++ .../module_charge/chg_drho_detail.h | 43 ++++ source/source_estate/test/CMakeLists.txt | 2 +- .../source_estate/test/charge_mixing_test.cpp | 42 ++-- 10 files changed, 264 insertions(+), 142 deletions(-) rename source/source_estate/module_charge/{mix_resid.cpp => chg_drho.cpp} (71%) create mode 100644 source/source_estate/module_charge/chg_drho.h create mode 100644 source/source_estate/module_charge/chg_drho_detail.h diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 39ff69276b1..e76a0e9b512 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -833,7 +833,7 @@ OBJS_SRCPW=h_ewald_pw.o\ charge_extra.o\ charge_mixing.o\ charge_mixing_dmr.o\ - mix_resid.o\ + chg_drho.o\ mix_precond.o\ charge_mixing_rho.o\ charge_mixing_uspp.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 26ced15bbac..501184f715d 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -36,7 +36,7 @@ list(APPEND objects module_charge/charge_extra.cpp module_charge/charge_mixing.cpp module_charge/charge_mixing_dmr.cpp - module_charge/mix_resid.cpp + module_charge/chg_drho.cpp module_charge/mix_precond.cpp module_charge/charge_mixing_rho.cpp module_charge/charge_mixing_uspp.cpp diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 907421abbd9..417a209d5b9 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -167,21 +167,6 @@ class Charge_Mixing */ void Kerker_screen_real(double* rho); - /** - * @brief Inner product of two complex vectors - * @brief inner_product_recip_rho is used for charge, like get_drho() - * @brief inner_product_recip_hartree is used for charge mixing - * @brief Actually, I am not sure if the definition of inner product for NSPIN=4 is correct, need to be checked. - */ - double inner_product_recip_rho(std::complex* rho1, std::complex* rho2); - double inner_product_recip_hartree(std::complex* rho1, std::complex* rho2); - - /** - * @brief Inner product of two double vectors - * - */ - double inner_product_real(double* rho1, double* rho2); - /** * @brief two-beta mixing functor: mix the first `nunit` elements with * mixing_beta and the rest (nunit..total) with mixing_beta_mag. Used for diff --git a/source/source_estate/module_charge/charge_mixing_dmr.cpp b/source/source_estate/module_charge/charge_mixing_dmr.cpp index 368ed63f257..fc6130ea7db 100644 --- a/source/source_estate/module_charge/charge_mixing_dmr.cpp +++ b/source/source_estate/module_charge/charge_mixing_dmr.cpp @@ -100,9 +100,6 @@ void Charge_Mixing::mix_dmr(elecstate::DensityMatrix* DM) } }; this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, twobeta_mix, false); - //auto inner_product - // = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); - //this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->dmr_mdata, dmr_out); // get new dmr from dmr_mag dmr_up = dmr[0]->get_wrapper(); @@ -199,9 +196,6 @@ void Charge_Mixing::mix_dmr(elecstate::DensityMatrix, doubl } }; this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, twobeta_mix, false); - //auto inner_product - // = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); - //this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->dmr_mdata, dmr_out); // get new dmr from dmr_mag dmr_up = dmr[0]->get_wrapper(); diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 2973b00ebbb..771327e276a 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -1,4 +1,5 @@ #include "charge_mixing.h" +#include "chg_drho.h" #include "source_io/module_parameter/parameter.h" #include "source_base/timer.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -28,8 +29,11 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) } // inner_product_recip_hartree is a hartree-like sum, unit is Ry - auto inner_product - = std::bind(&Charge_Mixing::inner_product_recip_hartree, this, std::placeholders::_1, std::placeholders::_2); + auto inner_product = [this](std::complex* rhog1, std::complex* rhog2) + { + return module_charge::inner_product_recip_hartree( + rhog1, rhog2, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); + }; // DIIS Mixing Only for smooth part, while high_frequency part is mixed by plain mixing method. if (nspin == 1) @@ -260,8 +264,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rhor_out = chr->rho[0]; auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); - auto inner_product - = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); } @@ -289,8 +295,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product - = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); // get new rho[is][nrxx] from rho_mag[is*nrxx] @@ -314,8 +322,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); auto twobeta_mix = this->make_twobeta_mix(4 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product - = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); } @@ -347,8 +357,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product - = std::bind(&Charge_Mixing::inner_product_real, this, std::placeholders::_1, std::placeholders::_2); + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); diff --git a/source/source_estate/module_charge/mix_resid.cpp b/source/source_estate/module_charge/chg_drho.cpp similarity index 71% rename from source/source_estate/module_charge/mix_resid.cpp rename to source/source_estate/module_charge/chg_drho.cpp index 5c8b7fd94e5..09e6a033965 100644 --- a/source/source_estate/module_charge/mix_resid.cpp +++ b/source/source_estate/module_charge/chg_drho.cpp @@ -1,9 +1,12 @@ #include "charge_mixing.h" -#include "source_io/module_parameter/parameter.h" +#include "chg_drho.h" +#include "chg_drho_detail.h" #include "source_base/timer.h" #include "source_base/parallel_reduce.h" #include "source_hamilt/module_xc/xc_functional.h" +#include + double Charge_Mixing::get_drho(Charge* chr, const double nelec) { ModuleBase::TITLE("Charge_Mixing", "get_drho"); @@ -37,7 +40,8 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) } ModuleBase::GlobalFunc::NOTE("Calculate the norm of the Residual std::vector: < R[rho] | R[rho_save] >"); - drho = this->inner_product_recip_rho(drhog.data(), drhog.data()); + drho = module_charge::detail::inner_product_recip_rho( + drhog.data(), drhog.data(), *this->rhopw, this->cfg_, *this->omega, *this->tpiba); } else { @@ -80,7 +84,7 @@ double Charge_Mixing::get_dkin(Charge* chr, const double nelec) ModuleBase::TITLE("Charge_Mixing", "get_dkin"); ModuleBase::timer::start("Charge_Mixing", "get_dkin"); double dkin = 0.0; - + // Get dkin from kin_r and kin_r_save for PW and LCAO both, which is different from drho. for (int is = 0; is < this->cfg_.nspin; is++) { @@ -109,21 +113,34 @@ double Charge_Mixing::get_dkin(Charge* chr, const double nelec) return dkin; } -double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::complex* rho2) +namespace module_charge { +namespace detail +{ + +double inner_product_recip_rho(const std::complex* rho1, + const std::complex* rho2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba) +{ + assert(rho1 != nullptr); + assert(rho2 != nullptr); + assert(cfg.nspin == 1 || cfg.nspin == 2 || cfg.nspin == 4); ModuleBase::TITLE("Charge_Mixing", "recip_rho"); ModuleBase::timer::start("Charge_Mixing", "recip_rho"); - const int nspin = this->cfg_.nspin; - std::vector*> rhog1(nspin); - std::vector*> rhog2(nspin); + const int nspin = cfg.nspin; + std::vector*> rhog1(nspin); + std::vector*> rhog2(nspin); for (int is = 0; is < nspin; is++) { - rhog1[is] = rho1 + is * this->rhopw->npw; - rhog2[is] = rho2 + is * this->rhopw->npw; + rhog1[is] = rho1 + is * rhopw.npw; + rhog2[is] = rho2 + is * rhopw.npw; } - static const double fac = ModuleBase::e2 * ModuleBase::FOUR_PI / ((*this->tpiba) * (*this->tpiba)); + static const double fac = ModuleBase::e2 * ModuleBase::FOUR_PI / (tpiba * tpiba); static const double fac2 = ModuleBase::e2 * ModuleBase::FOUR_PI / (ModuleBase::TWO_PI * ModuleBase::TWO_PI); double sum = 0.0; @@ -131,14 +148,14 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c auto part_of_noncolin = [&]() { double sum = 0.0; - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ++ig) + for (int ig = 0; ig < rhopw.npw; ++ig) { if (ig == ig0) {continue;} - sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / rhopw.gg[ig]; } sum *= fac; return sum; @@ -152,18 +169,18 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c case 2: { // (1) First part of density error. - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ++ig) + for (int ig = 0; ig < rhopw.npw; ++ig) { if (ig == ig0) {continue;} - sum += (conj(rhog1[0][ig] + rhog1[1][ig]) * (rhog2[0][ig] + rhog2[1][ig])).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[0][ig] + rhog1[1][ig]) * (rhog2[0][ig] + rhog2[1][ig])).real() / rhopw.gg[ig]; } sum *= fac; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { sum *= 2.0; } @@ -186,42 +203,40 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c #ifdef _OPENMP #pragma omp parallel for reduction(+ : mag) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) { continue; } mag += (conj(rhog1[0][ig] - rhog1[1][ig]) * (rhog2[0][ig] - rhog2[1][ig])).real(); } mag *= fac2; - // if(PARAM.globalv.gamma_only_pw); - if (this->cfg_.gamma_only_pw) // Peize Lin delete ; 2020.01.31 + if (cfg.gamma_only_pw) { mag *= 2.0; } - // std::cout << " sum=" << sum << " mag=" << mag << std::endl; sum2 += mag; sum += sum2; break; } case 4: // non-collinear spin, added by zhengdy - if (!this->cfg_.domag && !this->cfg_.domag_z) { + if (!cfg.domag && !cfg.domag_z) { sum += part_of_noncolin(); } else { // another part with magnetization - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { - if (ig == ig0) + if (ig == ig0) { continue; } - sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / rhopw.gg[ig]; } sum *= fac; if (ig0 > 0) @@ -231,14 +246,14 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c + (conj(rhog1[3][ig0]) * rhog2[3][ig0]).real()); } double fac3 = fac2; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { fac3 *= 2.0; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) { continue; @@ -254,66 +269,104 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c Parallel_Reduce::reduce_pool(sum); #endif - sum *= *this->omega * 0.5; + sum *= omega * 0.5; ModuleBase::timer::end("Charge_Mixing", "recip_rho"); return sum; } +} // namespace detail + +double inner_product_real(const double* rho1, + const double* rho2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg) +{ + assert(rho1 != nullptr); + assert(rho2 != nullptr); + double rnorm = 0.0; + // consider a resize for mixing_angle + int resize_tmp = 1; + if (cfg.nspin == 4 && cfg.mixing_angle > 0) + { + resize_tmp = 2; + } + +#ifdef _OPENMP +#pragma omp parallel for reduction(+ : rnorm) +#endif + for (int ir = 0; ir < rhopw.nrxx * cfg.nspin / resize_tmp; ++ir) + { + rnorm += rho1[ir] * rho2[ir]; + } +#ifdef __MPI + Parallel_Reduce::reduce_pool(rnorm); +#endif + return rnorm; +} + // a Hartree-like inner product -double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, std::complex* rhog2) +double inner_product_recip_hartree(const std::complex* rhog1, + const std::complex* rhog2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba) { + assert(rhog1 != nullptr); + assert(rhog2 != nullptr); + assert(cfg.nspin == 1 || cfg.nspin == 2 || cfg.nspin == 4); ModuleBase::TITLE("Charge_Mixing", "recip_hartree"); ModuleBase::timer::start("Charge_Mixing", "recip_hartree"); - static const double fac = ModuleBase::e2 * ModuleBase::FOUR_PI / ((*this->tpiba) * (*this->tpiba)); + static const double fac = ModuleBase::e2 * ModuleBase::FOUR_PI / (tpiba * tpiba); static const double fac2 = ModuleBase::e2 * ModuleBase::FOUR_PI / (ModuleBase::TWO_PI * ModuleBase::TWO_PI); double sum = 0.0; - const int npw = this->rhopw->npw; + const int npw = rhopw.npw; // a lambda function for summing the charge density auto part_of_rho = [&]() { double sum = 0.0; - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ++ig) + for (int ig = 0; ig < rhopw.npw; ++ig) { - if (ig == ig0) + if (ig == ig0) { continue; } - sum += (conj(rhog1[ig]) * rhog2[ig]).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[ig]) * rhog2[ig]).real() / rhopw.gg[ig]; } sum *= fac; return sum; }; - - if (this->cfg_.nspin==1) + + if (cfg.nspin==1) { sum += part_of_rho(); } - else if (this->cfg_.nspin==2) + else if (cfg.nspin==2) { // charge density part - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ++ig) + for (int ig = 0; ig < rhopw.npw; ++ig) { - if (ig == ig0) + if (ig == ig0) { continue; } - sum += (conj(rhog1[ig]) * (rhog2[ig])).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[ig]) * (rhog2[ig])).real() / rhopw.gg[ig]; } sum *= fac; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { sum *= 2.0; } @@ -327,21 +380,21 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s // the DIIS mixing coefficients) depends on how the pool is divided. if (ig0 >= 0) { - sum2 += fac2 * (conj(rhog1[ig0 + this->rhopw->npw]) * rhog2[ig0 + this->rhopw->npw]).real(); + sum2 += fac2 * (conj(rhog1[ig0 + rhopw.npw]) * rhog2[ig0 + rhopw.npw]).real(); } double mag = 0.0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : mag) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) { continue; } - mag += (conj(rhog1[ig + this->rhopw->npw]) * rhog2[ig + this->rhopw->npw]).real(); + mag += (conj(rhog1[ig + rhopw.npw]) * rhog2[ig + rhopw.npw]).real(); } mag *= fac2; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { mag *= 2.0; } @@ -349,23 +402,23 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s sum2 += mag; sum += sum2; } - else if (this->cfg_.nspin==4) + else if (cfg.nspin==4) { - if (!this->cfg_.domag && !this->cfg_.domag_z) + if (!cfg.domag && !cfg.domag_z) { sum += part_of_rho(); } - else if (this->mixing_angle <= 0) + else if (cfg.mixing_angle <= 0) { // sum for tradtional mixing - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) {continue;} - sum += (conj(rhog1[ig]) * rhog2[ig]).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[ig]) * rhog2[ig]).real() / rhopw.gg[ig]; } sum *= fac; if (ig0 > 0) @@ -375,14 +428,14 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s + (conj(rhog1[ig0 + 3*npw]) * rhog2[ig0 + 3*npw]).real()); } double fac3 = fac2; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { fac3 *= 2.0; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) { continue; @@ -392,42 +445,42 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s + (conj(rhog1[ig + 3*npw]) * rhog2[ig + 3*npw]).real()); } } - else if (this->mixing_angle > 0) + else if (cfg.mixing_angle > 0) { // sum for angle mixing - const int ig0 = this->rhopw->ig_gge0; + const int ig0 = rhopw.ig_gge0; #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { - if (ig == ig0) + if (ig == ig0) { continue; } - sum += (conj(rhog1[ig]) * rhog2[ig]).real() / this->rhopw->gg[ig]; + sum += (conj(rhog1[ig]) * rhog2[ig]).real() / rhopw.gg[ig]; } sum *= fac; if (ig0 > 0) { sum += fac2 - * ((conj(rhog1[ig0 + this->rhopw->npw]) * rhog2[ig0 + this->rhopw->npw]).real()); + * ((conj(rhog1[ig0 + rhopw.npw]) * rhog2[ig0 + rhopw.npw]).real()); } double fac3 = fac2; - if (this->cfg_.gamma_only_pw) + if (cfg.gamma_only_pw) { fac3 *= 2.0; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : sum) #endif - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { if (ig == ig0) { continue; } sum += fac3 - * ((conj(rhog1[ig + this->rhopw->npw]) * rhog2[ig + this->rhopw->npw]).real()); + * ((conj(rhog1[ig + rhopw.npw]) * rhog2[ig + rhopw.npw]).real()); } } } @@ -435,32 +488,11 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s Parallel_Reduce::reduce_pool(sum); #endif - sum *= *this->omega * 0.5; + sum *= omega * 0.5; ModuleBase::timer::end("Charge_Mixing", "recip_hartree"); return sum; } -double Charge_Mixing::inner_product_real(double* rho1, double* rho2) -{ - double rnorm = 0.0; - // consider a resize for mixing_angle - int resize_tmp = 1; - if (this->cfg_.nspin == 4 && this->mixing_angle > 0) - { - resize_tmp = 2; - } - -#ifdef _OPENMP -#pragma omp parallel for reduction(+ : rnorm) -#endif - for (int ir = 0; ir < this->rhopw->nrxx * this->cfg_.nspin / resize_tmp; ++ir) - { - rnorm += rho1[ir] * rho2[ir]; - } -#ifdef __MPI - Parallel_Reduce::reduce_pool(rnorm); -#endif - return rnorm; -} +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_drho.h b/source/source_estate/module_charge/chg_drho.h new file mode 100644 index 00000000000..1b52581e7f1 --- /dev/null +++ b/source/source_estate/module_charge/chg_drho.h @@ -0,0 +1,54 @@ +#ifndef CHG_DRHO_H +#define CHG_DRHO_H + +// Stateless residual kernels extracted from Charge_Mixing. Every input +// (grid, geometry, mixing config) is passed explicitly; the functions do +// not read Charge_Mixing members or PARAM/GlobalV. + +#include + +#include "mixing_config.h" + +namespace ModulePW +{ +class PW_Basis; +} + +namespace module_charge +{ + +/** + * @brief Inner product of two real-space vectors used in real-space mixing. + * + * @param rho1 first real-space vector + * @param rho2 second real-space vector + * @param rhopw plane-wave basis supplying the real-space grid size + * @param cfg mixing config (nspin and mixing_angle select the loop bound) + * @return pooled inner product + */ +double inner_product_real(const double* rho1, + const double* rho2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg); + +/** + * @brief Hartree-like reciprocal inner product used in charge mixing. + * + * @param rhog1 first reciprocal-space vector + * @param rhog2 second reciprocal-space vector + * @param rhopw plane-wave basis supplying npw/gg and the G=0 index + * @param cfg mixing config (spin channels, gamma-only and angle flags) + * @param omega cell volume + * @param tpiba 2*pi/lattice constant + * @return pooled Hartree inner product + */ +double inner_product_recip_hartree(const std::complex* rhog1, + const std::complex* rhog2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba); + +} // namespace module_charge + +#endif // CHG_DRHO_H diff --git a/source/source_estate/module_charge/chg_drho_detail.h b/source/source_estate/module_charge/chg_drho_detail.h new file mode 100644 index 00000000000..b21a4fcf750 --- /dev/null +++ b/source/source_estate/module_charge/chg_drho_detail.h @@ -0,0 +1,43 @@ +#ifndef CHG_DRHO_DETAIL_H +#define CHG_DRHO_DETAIL_H + +// Internal reciprocal inner product for the charge residual (get_drho). +// Not part of the public module_charge API: only chg_drho.cpp and the +// charge mixing unit test are expected to include this header. + +#include + +#include "mixing_config.h" + +namespace ModulePW +{ +class PW_Basis; +} + +namespace module_charge +{ +namespace detail +{ + +/** + * @brief Coulomb-metric reciprocal inner product of the charge residual. + * + * @param rho1 first reciprocal-space vector + * @param rho2 second reciprocal-space vector + * @param rhopw plane-wave basis supplying npw/gg and the G=0 index + * @param cfg mixing config (spin channels, gamma-only and magnetism flags) + * @param omega cell volume + * @param tpiba 2*pi/lattice constant + * @return pooled Coulomb-metric inner product + */ +double inner_product_recip_rho(const std::complex* rho1, + const std::complex* rho2, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba); + +} // namespace detail +} // namespace module_charge + +#endif // CHG_DRHO_DETAIL_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 14cfab7ff76..2b9cb2742f8 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -111,7 +111,7 @@ AddTest( TARGET MODULE_ESTATE_charge_mixing LIBS parameter base psi device planewave_serial cell_info SOURCES charge_mixing_test.cpp - ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/mix_resid.cpp + ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_drho.cpp ../module_charge/mix_precond.cpp ../module_charge/charge_mixing_rho.cpp ../module_charge/charge_mixing_uspp.cpp ) diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index fa989365bc6..68c36ba9283 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -1,10 +1,12 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -// TODO(governance): remove this access hack once mix_resid.cpp and Charge no +// TODO(governance): remove this access hack once chg_drho.cpp and Charge no // longer read global PARAM/RAW state (Step 4 of the module_charge refactor). // The test still has to drive Charge::_space_* and XC_Functional privates. #define private public #include "../module_charge/charge_mixing.h" +#include "../module_charge/chg_drho.h" +#include "../module_charge/chg_drho_detail.h" #include "source_base/module_mixing/broyden_mixing.h" #include "source_basis/module_pw/pw_basis.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -55,9 +57,9 @@ void Charge::set_rhopw(ModulePW::PW_Basis* rhopw_in) * - KerkerScreenTest: Charge_Mixing::Kerker_screen_recip(drhog) * Charge_Mixing::Kerker_screen_real(drhog) * - screen drho with Kerker method - * - InnerDotTest: Charge_Mixing::inner_product_recip_hartree(rhog1, rhog2) - * Charge_Mixing::inner_product_recip_rho(rhog1, rhog2) - * Charge_Mixing::inner_product_real(rho1, rho2) + * - InnerDotTest: module_charge::inner_product_recip_hartree(rhog1, rhog2) + * module_charge::detail::inner_product_recip_rho(rhog1, rhog2) + * module_charge::inner_product_real(rho1, rho2) * - calculate the inner product of two vectors * - MixRhoTest: Charge_Mixing::mix_rho(chr) * Charge_Mixing::mix_rho_recip(chr) @@ -256,7 +258,7 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) drho1[i] = 1.0; drho2[i] = double(i); } - double inner = CMtest.inner_product_real(drho1.data(), drho2.data()); + double inner = module_charge::inner_product_real(drho1.data(), drho2.data(), pw_basis, CMtest.cfg_); EXPECT_NEAR(inner, 0.5 * pw_basis.nrxx * PARAM.input.nspin * (pw_basis.nrxx * PARAM.input.nspin - 1), 1e-8); // mixing angle case @@ -272,7 +274,7 @@ TEST_F(ChargeMixingTest, InnerDotRealTest) drho1[i] = 1.0; drho2[i] = double(i); } - inner = CMtest.inner_product_real(drho1.data(), drho2.data()); + inner = module_charge::inner_product_real(drho1.data(), drho2.data(), pw_basis, CMtest.cfg_); EXPECT_NEAR(inner, 0.5 * pw_basis.nrxx * 2 * (pw_basis.nrxx * 2 - 1), 1e-8); } @@ -291,7 +293,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) drhor1[i] = 1.0; drhor2[i] = double(i); } - double inner = CMtest.inner_product_real(drhor1.data(), drhor2.data()); + double inner = module_charge::inner_product_real(drhor1.data(), drhor2.data(), pw_basis, CMtest.cfg_); EXPECT_NEAR(inner, 0.5 * pw_basis.nrxx * (pw_basis.nrxx - 1), 1e-8); // RECIPROCAL NSPIN=1 @@ -310,7 +312,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) pw_basis.real2recip(drhor1.data(), drhog1.data()); pw_basis.real2recip(drhor2.data(), drhog2.data()); - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + inner = module_charge::inner_product_recip_hartree(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, -0.3 * ModuleBase::e2 * ModuleBase::FOUR_PI, 1e-8); // RECIPROCAL NSPIN=2 @@ -335,11 +337,11 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) } PARAM.sys.gamma_only_pw= false; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); + inner = module_charge::inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 236763.82650318215, 1e-8); PARAM.sys.gamma_only_pw= true; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); + inner = module_charge::inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 236763.82650318215 * 2, 1e-8); // RECIPROCAL NSPIN=4 without mixing_angle @@ -356,13 +358,13 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + inner = module_charge::inner_product_recip_hartree(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 28260.091995611871, 1e-8); PARAM.sys.gamma_only_pw= true; PARAM.sys.domag = true; PARAM.sys.domag_z = true; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + inner = module_charge::inner_product_recip_hartree(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 110668.61166927818, 1e-8); // RECIPROCAL NSPIN=4 with mixing_angle @@ -378,11 +380,11 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) } PARAM.sys.gamma_only_pw= false; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + inner = module_charge::inner_product_recip_hartree(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 36548.881431837777, 1e-8); PARAM.sys.gamma_only_pw= true; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + inner = module_charge::inner_product_recip_hartree(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 44776.555369916401, 1e-8); } @@ -399,7 +401,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) drhor1[i] = 1.0; drhor2[i] = double(i); } - double inner = CMtest.inner_product_real(drhor1.data(), drhor2.data()); + double inner = module_charge::inner_product_real(drhor1.data(), drhor2.data(), pw_basis, CMtest.cfg_); EXPECT_NEAR(inner, 0.5 * pw_basis.nrxx * (pw_basis.nrxx - 1), 1e-8); // RECIPROCAL @@ -418,7 +420,7 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) pw_basis.real2recip(drhor1.data(), drhog1.data()); pw_basis.real2recip(drhor2.data(), drhog2.data()); - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + inner = module_charge::detail::inner_product_recip_rho(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, -0.3 * ModuleBase::e2 * ModuleBase::FOUR_PI, 1e-8); PARAM.input.nspin = 2; @@ -432,11 +434,11 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) } PARAM.sys.gamma_only_pw= false; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + inner = module_charge::detail::inner_product_recip_rho(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 236763.82650318215, 1e-8); PARAM.sys.gamma_only_pw= true; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + inner = module_charge::detail::inner_product_recip_rho(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 236763.82650318215 * 2, 1e-8); PARAM.input.nspin = 4; @@ -452,13 +454,13 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + inner = module_charge::detail::inner_product_recip_rho(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 28260.091995611871, 1e-8); PARAM.sys.gamma_only_pw= true; PARAM.sys.domag = true; PARAM.sys.domag_z = true; sync_cfg(CMtest); - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + inner = module_charge::detail::inner_product_recip_rho(drhog1.data(), drhog2.data(), pw_basis, CMtest.cfg_, ucell.omega, ucell.tpiba); EXPECT_NEAR(inner, 110668.61166927818, 1e-8); } From 61c2529e2394c2c9d65f5dde9ec4b050fa593829 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 13:41:52 +0800 Subject: [PATCH 20/37] module_charge: hide cal_drho/cal_dkin in an anonymous namespace Move the get_drho/get_dkin implementations into file-local cal_drho/ cal_dkin free functions with all inputs explicit; the public Charge_Mixing methods become thin forwarding wrappers so esolver call sites stay unchanged. --- .../source_estate/module_charge/chg_drho.cpp | 71 ++++++++++++++----- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/source/source_estate/module_charge/chg_drho.cpp b/source/source_estate/module_charge/chg_drho.cpp index 09e6a033965..86454f8a58e 100644 --- a/source/source_estate/module_charge/chg_drho.cpp +++ b/source/source_estate/module_charge/chg_drho.cpp @@ -7,15 +7,25 @@ #include -double Charge_Mixing::get_drho(Charge* chr, const double nelec) +namespace +{ + +// Charge residual between chr->rho and chr->rho_save, normalized per electron. +double cal_drho(Charge* chr, + const double nelec, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba) { + assert(chr != nullptr); ModuleBase::TITLE("Charge_Mixing", "get_drho"); ModuleBase::timer::start("Charge_Mixing", "get_drho"); - const int nspin = this->cfg_.nspin; + const int nspin = cfg.nspin; assert(nspin==1 || nspin==2 || nspin==4); double drho = 0.0; - if (this->cfg_.scf_thr_type == 1) + if (cfg.scf_thr_type == 1) { for (int is = 0; is < nspin; ++is) { @@ -27,21 +37,21 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) } ModuleBase::GlobalFunc::NOTE("Calculate the charge difference between rho(G) and rho_save(G)"); - std::vector> drhog(nspin * this->rhopw->npw); + std::vector> drhog(nspin * rhopw.npw); #ifdef _OPENMP #pragma omp parallel for collapse(2) schedule(static, 512) #endif for (int is = 0; is < nspin; ++is) { - for (int ig = 0; ig < this->rhopw->npw; ig++) + for (int ig = 0; ig < rhopw.npw; ig++) { - drhog[is * this->rhopw->npw + ig] = chr->rhog[is][ig] - chr->rhog_save[is][ig]; + drhog[is * rhopw.npw + ig] = chr->rhog[is][ig] - chr->rhog_save[is][ig]; } } ModuleBase::GlobalFunc::NOTE("Calculate the norm of the Residual std::vector: < R[rho] | R[rho_save] >"); drho = module_charge::detail::inner_product_recip_rho( - drhog.data(), drhog.data(), *this->rhopw, this->cfg_, *this->omega, *this->tpiba); + drhog.data(), drhog.data(), rhopw, cfg, omega, tpiba); } else { @@ -49,14 +59,14 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) // The inner_product_real function (L1-norm) is different from that (L2-norm) in mixing. for (int is = 0; is < nspin; is++) { - if (is != 0 && is != 3 && this->cfg_.domag_z) + if (is != 0 && is != 3 && cfg.domag_z) { continue; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : drho) #endif - for (int ir = 0; ir < this->rhopw->nrxx; ir++) + for (int ir = 0; ir < rhopw.nrxx; ir++) { drho += std::abs(chr->rho[is][ir] - chr->rho_save[is][ir]); } @@ -65,9 +75,9 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) Parallel_Reduce::reduce_pool(drho); #endif assert(nelec != 0); - assert(*this->omega > 0); - assert(this->rhopw->nxyz > 0); - drho *= *this->omega / static_cast(this->rhopw->nxyz); + assert(omega > 0); + assert(rhopw.nxyz > 0); + drho *= omega / static_cast(rhopw.nxyz); drho /= nelec; } @@ -75,8 +85,14 @@ double Charge_Mixing::get_drho(Charge* chr, const double nelec) return drho; } -double Charge_Mixing::get_dkin(Charge* chr, const double nelec) +// Kinetic-energy-density residual between chr->kin_r and chr->kin_r_save. +double cal_dkin(Charge* chr, + const double nelec, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega) { + assert(chr != nullptr); if (!(XC_Functional::get_ked_flag())) { return 0.0; @@ -86,16 +102,16 @@ double Charge_Mixing::get_dkin(Charge* chr, const double nelec) double dkin = 0.0; // Get dkin from kin_r and kin_r_save for PW and LCAO both, which is different from drho. - for (int is = 0; is < this->cfg_.nspin; is++) + for (int is = 0; is < cfg.nspin; is++) { - if (is != 0 && is != 3 && this->cfg_.domag_z) + if (is != 0 && is != 3 && cfg.domag_z) { continue; } #ifdef _OPENMP #pragma omp parallel for reduction(+ : dkin) #endif - for (int ir = 0; ir < this->rhopw->nrxx; ir++) + for (int ir = 0; ir < rhopw.nrxx; ir++) { dkin += std::abs(chr->kin_r[is][ir] - chr->kin_r_save[is][ir]); } @@ -104,15 +120,32 @@ double Charge_Mixing::get_dkin(Charge* chr, const double nelec) Parallel_Reduce::reduce_pool(dkin); #endif assert(nelec != 0); - assert(*this->omega > 0); - assert(this->rhopw->nxyz > 0); - dkin *= *this->omega / static_cast(this->rhopw->nxyz); + assert(omega > 0); + assert(rhopw.nxyz > 0); + dkin *= omega / static_cast(rhopw.nxyz); dkin /= nelec; ModuleBase::timer::end("Charge_Mixing", "get_dkin"); return dkin; } +} // namespace + +double Charge_Mixing::get_drho(Charge* chr, const double nelec) +{ + assert(this->rhopw != nullptr); + assert(this->omega != nullptr); + assert(this->tpiba != nullptr); + return cal_drho(chr, nelec, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); +} + +double Charge_Mixing::get_dkin(Charge* chr, const double nelec) +{ + assert(this->rhopw != nullptr); + assert(this->omega != nullptr); + return cal_dkin(chr, nelec, *this->rhopw, this->cfg_, *this->omega); +} + namespace module_charge { namespace detail From dfe1dac8370ff44f27ba469e152bfb59fd54bfc6 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 15:05:58 +0800 Subject: [PATCH 21/37] module_gint: fix include path in test_gint_prec_ctrl after relocation --- source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp b/source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp index f471db80623..5cc6c053baa 100644 --- a/source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp +++ b/source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp @@ -1,6 +1,6 @@ #include "gtest/gtest.h" -#include "../module_charge/gint_prec_ctrl.h" +#include "../gint_prec_ctrl.h" TEST(GintPrecisionControllerTest, AutoModeSwitchesToFp64ImmediatelyWhenDrhoIsSmallEnough) { From 6d127d517101e9dfb8215609c75e0ca667972dd1 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 20:57:59 +0800 Subject: [PATCH 22/37] module_charge: extract Kerker screen kernels into chg_precond free functions Move Charge_Mixing::Kerker_screen_recip/real to module_charge namespace as free functions in chg_precond.{h,cpp}, renaming mix_precond.cpp via git mv. Config/grid/geometry are passed explicitly via MixingConfig, PW_Basis*, and tpiba, eliminating the function's direct read of PARAM.inp.nspin. Replace 8 std::bind call sites in charge_mixing_rho.cpp with lambdas, update 2 commented-out bind sites in charge_mixing_dmr.cpp, and rewrite 12 test call sites in charge_mixing_test.cpp to construct an independent MixingConfig instead of poking at Charge_Mixing privates. Drop the now-unused member function declarations from charge_mixing.h. --- source/source_estate/CMakeLists.txt | 2 +- .../module_charge/charge_mixing.h | 12 -- .../module_charge/charge_mixing_dmr.cpp | 8 +- .../module_charge/charge_mixing_rho.cpp | 35 +++- .../module_charge/chg_precond.cpp | 195 ++++++++++++++++++ .../source_estate/module_charge/chg_precond.h | 56 +++++ .../module_charge/mix_precond.cpp | 176 ---------------- source/source_estate/test/CMakeLists.txt | 2 +- .../source_estate/test/charge_mixing_test.cpp | 86 ++++---- 9 files changed, 328 insertions(+), 244 deletions(-) create mode 100644 source/source_estate/module_charge/chg_precond.cpp create mode 100644 source/source_estate/module_charge/chg_precond.h delete mode 100644 source/source_estate/module_charge/mix_precond.cpp diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 501184f715d..931ac8b9fcf 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -37,7 +37,7 @@ list(APPEND objects module_charge/charge_mixing.cpp module_charge/charge_mixing_dmr.cpp module_charge/chg_drho.cpp - module_charge/mix_precond.cpp + module_charge/chg_precond.cpp module_charge/charge_mixing_rho.cpp module_charge/charge_mixing_uspp.cpp module_charge/chg_symm.cpp diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 417a209d5b9..02162d4e733 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -155,18 +155,6 @@ class Charge_Mixing */ void mix_rho_real(Charge* chr); - /** - * @brief Kerker screen method for reciprocal space - * @param rhog charge density in reciprocal space - */ - void Kerker_screen_recip(std::complex* rhog); - - /** - * @brief Kerker screen method for real space - * @param rho charge density in real space - */ - void Kerker_screen_real(double* rho); - /** * @brief two-beta mixing functor: mix the first `nunit` elements with * mixing_beta and the rest (nunit..total) with mixing_beta_mag. Used for diff --git a/source/source_estate/module_charge/charge_mixing_dmr.cpp b/source/source_estate/module_charge/charge_mixing_dmr.cpp index fc6130ea7db..01e409264fe 100644 --- a/source/source_estate/module_charge/charge_mixing_dmr.cpp +++ b/source/source_estate/module_charge/charge_mixing_dmr.cpp @@ -80,7 +80,9 @@ void Charge_Mixing::mix_dmr(elecstate::DensityMatrix* DM) dmr_in = dmr_mag_save; dmr_out = dmr_mag; // no kerker in mixing_dmr - //auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); + //auto screen = [this](double* p) { + // module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + //}; auto twobeta_mix = [this, nnr](double* out, const double* in, const double* sres) { #ifdef _OPENMP @@ -176,7 +178,9 @@ void Charge_Mixing::mix_dmr(elecstate::DensityMatrix, doubl dmr_in = dmr_mag_save; dmr_out = dmr_mag; // no kerker in mixing_dmr - //auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); + //auto screen = [this](double* p) { + // module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + //}; auto twobeta_mix = [this, nnr](double* out, const double* in, const double* sres) { #ifdef _OPENMP diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 771327e276a..41183f038f3 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -1,5 +1,6 @@ #include "charge_mixing.h" #include "chg_drho.h" +#include "chg_precond.h" #include "source_io/module_parameter/parameter.h" #include "source_base/timer.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -40,7 +41,9 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) { rhog_in = rhogs_in; rhog_out = rhogs_out; - auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); + auto screen = [this](std::complex* p) { + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -67,7 +70,9 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog_in = rhog_mag_save.data(); rhog_out = rhog_mag.data(); // - auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); + auto screen = [this](std::complex* p) { + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); @@ -98,7 +103,9 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog_in = rhogs_in; rhog_out = rhogs_out; const int npw = this->rhopw->npw; - auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); // use old one + auto screen = [this](std::complex* p) { // use old one + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix>(4 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); @@ -145,7 +152,9 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) // rhog_in = rhog_magabs_save.data(); rhog_out = rhog_magabs.data(); - auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); // use old one + auto screen = [this](std::complex* p) { // use old one + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); @@ -262,8 +271,10 @@ void Charge_Mixing::mix_rho_real(Charge* chr) { rhor_in = chr->rho_save[0]; rhor_out = chr->rho[0]; - auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; + this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); auto inner_product = [this](double* rho1, double* rho2) { return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); @@ -292,7 +303,9 @@ void Charge_Mixing::mix_rho_real(Charge* chr) // rhor_in = rho_mag_save.data(); rhor_out = rho_mag.data(); - auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = [this](double* rho1, double* rho2) @@ -319,7 +332,9 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rhor_in = chr->rho_save[0]; rhor_out = chr->rho[0]; const int nrxx = this->rhopw->nrxx; - auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix(4 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = [this](double* rho1, double* rho2) @@ -354,7 +369,9 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rhor_in = rho_magabs_save.data(); rhor_out = rho_magabs.data(); - auto screen = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); auto inner_product = [this](double* rho1, double* rho2) diff --git a/source/source_estate/module_charge/chg_precond.cpp b/source/source_estate/module_charge/chg_precond.cpp new file mode 100644 index 00000000000..4f1df26658d --- /dev/null +++ b/source/source_estate/module_charge/chg_precond.cpp @@ -0,0 +1,195 @@ +#include "chg_precond.h" + +#include "source_base/constants.h" +#include "source_base/timer.h" +#include "source_basis/module_pw/pw_basis.h" + +#include +#include +#include +#include + +namespace module_charge +{ + +void kerker_screen_recip(const MixingConfig& cfg, + ModulePW::PW_Basis* rhopw, + double tpiba, + std::complex* drhog) +{ + ModuleBase::TITLE("module_charge", "kerker_screen_recip"); + + if (cfg.mixing_gg0 <= 0.0 || cfg.mixing_beta <= 0.1) + { + return; + } + + ModuleBase::timer::start("module_charge", "kerker_screen_recip"); + + const int nspin = cfg.nspin; + + double fac = 0.0; + double gg0 = 0.0; + double amin = 0.0; + + /// consider a resize for mixing_angle + int resize_tmp = 1; + if (nspin == 4 && cfg.mixing_angle > 0) + { + resize_tmp = 2; + } + + /// implement Kerker for density and magnetization separately + for (int is = 0; is < nspin / resize_tmp; ++is) + { + const int is_idx = is * rhopw->npw; + /// new mixing method only support nspin=2 not nspin=4 + if (is >= 1) + { + if (cfg.mixing_gg0_mag <= 0.0001 || cfg.mixing_beta_mag <= 0.1) + { +#ifdef __DEBUG + assert(is == 1); // make sure break works +#endif + double is_mag = nspin - 1; + //for (int ig = 0; ig < rhopw->npw * is_mag; ig++) + //{ + // drhog[is_idx + ig] *= 1; + //} + break; + } + fac = cfg.mixing_gg0_mag; + amin = cfg.mixing_beta_mag; + } + else + { + fac = cfg.mixing_gg0; + amin = cfg.mixing_beta; + } + + gg0 = std::pow(fac * ModuleBase::BOHR_TO_A / tpiba, 2); + + const double gg0_amin = cfg.mixing_gg0_min / amin; + +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int ig = 0; ig < rhopw->npw; ++ig) + { + double gg = rhopw->gg[ig]; + double filter_g = std::max(gg / (gg + gg0), gg0_amin); + drhog[is_idx + ig] *= filter_g; + } + } + + ModuleBase::timer::end("module_charge", "kerker_screen_recip"); + return; +} + +void kerker_screen_real(const MixingConfig& cfg, + ModulePW::PW_Basis* rhopw, + double tpiba, + double* drhor) +{ + ModuleBase::TITLE("module_charge", "kerker_screen_real"); + + if (cfg.mixing_gg0 <= 0.0001 || cfg.mixing_beta <= 0.1) + { + return; + } + + ModuleBase::timer::start("module_charge", "kerker_screen_real"); + + const int nspin = cfg.nspin; + assert(nspin == 1 || nspin == 2 || nspin == 4); + + /// consider a resize for mixing_angle + int resize_tmp = 1; + if (nspin == 4 && cfg.mixing_angle > 0) + { + resize_tmp = 2; + } + + std::vector> drhog(rhopw->npw * nspin / resize_tmp); + std::vector drhor_filter(rhopw->nrxx * nspin / resize_tmp); + + for (int is = 0; is < nspin / resize_tmp; ++is) + { + // Note after this process some G which is higher than Gmax will be filtered. + // Thus we cannot use kerker_screen_recip(drhog.data()) directly after it. + rhopw->real2recip(drhor + is * rhopw->nrxx, drhog.data() + is * rhopw->npw); + } + /// implement Kerker for density and magnetization separately + double fac = 0.0; + double gg0 = 0.0; + double amin = 0.0; + + for (int is = 0; is < nspin / resize_tmp; is++) + { + + if (is >= 1) + { + if (cfg.mixing_gg0_mag <= 0.0001 || cfg.mixing_beta_mag <= 0.1) + { +#ifdef __DEBUG + assert(is == 1); /// make sure break works +#endif + double is_mag = nspin - 1; + if (nspin == 4 && cfg.mixing_angle > 0) + { + is_mag = 1; + } + for (int ig = 0; ig < rhopw->npw * is_mag; ig++) + { + drhog[is * rhopw->npw + ig] = 0; + } + break; + } + fac = cfg.mixing_gg0_mag; + amin = cfg.mixing_beta_mag; + } + else + { + fac = cfg.mixing_gg0; + amin = cfg.mixing_beta; + } + + gg0 = std::pow(fac * ModuleBase::BOHR_TO_A / tpiba, 2); + + const int is_idx = is * rhopw->npw; + const double gg0_amin = cfg.mixing_gg0_min / amin; +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int ig = 0; ig < rhopw->npw; ig++) + { + double gg = rhopw->gg[ig]; + // I have not decided how to handle gg=0 part, will be changed in future + //if (gg == 0) + //{ + // drhog[is_idx + ig] *= 0; + // continue; + //} + double filter_g = std::max(gg / (gg + gg0), gg0_amin); + drhog[is_idx + ig] *= (1 - filter_g); + } + } + /// inverse FT + for (int is = 0; is < nspin / resize_tmp; ++is) + { + rhopw->recip2real(drhog.data() + is * rhopw->npw, drhor_filter.data() + is * rhopw->nrxx); + } + +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int ir = 0; ir < rhopw->nrxx * nspin / resize_tmp; ir++) + { + drhor[ir] -= drhor_filter[ir]; + } + + ModuleBase::timer::end("module_charge", "kerker_screen_real"); + return; +} + +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_precond.h b/source/source_estate/module_charge/chg_precond.h new file mode 100644 index 00000000000..d807d329e76 --- /dev/null +++ b/source/source_estate/module_charge/chg_precond.h @@ -0,0 +1,56 @@ +#ifndef CHG_PRECOND_H +#define CHG_PRECOND_H + +// Stateless Kerker preconditioning kernels extracted from Charge_Mixing. +// Every input (grid, geometry, mixing config) is passed explicitly; the +// functions do not read Charge_Mixing members or PARAM/GlobalV. + +#include + +#include "mixing_config.h" + +namespace ModulePW +{ +class PW_Basis; +} + +namespace module_charge +{ + +/** + * @brief Apply Kerker screening in reciprocal space. + * + * Multiplies drhog[is*npw + ig] by max(gg/(gg+gg0), gg0_min/amin) per spin + * channel, where gg0 is derived from cfg.mixing_gg0 (density) or + * cfg.mixing_gg0_mag (magnetization). Early return if Kerker is disabled. + * + * @param cfg mixing config (spin count, betas, gg0s, angle, gg0_min) + * @param rhopw plane-wave basis supplying npw and gg[] + * @param tpiba 2*pi/lattice constant used to convert gg0 to atomic units + * @param drhog[in,out] reciprocal-space density residual, length nspin*npw + */ +void kerker_screen_recip(const MixingConfig& cfg, + ModulePW::PW_Basis* rhopw, + double tpiba, + std::complex* drhog); + +/** + * @brief Apply Kerker screening in real space via FFT. + * + * Forward-transforms drhor to drhog, applies (1 - filter_g) in reciprocal + * space, backward-transforms the filtered residual, and subtracts it from + * drhor in place. Early return if Kerker is disabled. + * + * @param cfg mixing config (spin count, betas, gg0s, angle, gg0_min) + * @param rhopw plane-wave basis supplying npw, nrxx, gg[], real2recip/recip2real + * @param tpiba 2*pi/lattice constant used to convert gg0 to atomic units + * @param drhor[in,out] real-space density residual, length nspin*nrxx + */ +void kerker_screen_real(const MixingConfig& cfg, + ModulePW::PW_Basis* rhopw, + double tpiba, + double* drhor); + +} // namespace module_charge + +#endif // CHG_PRECOND_H diff --git a/source/source_estate/module_charge/mix_precond.cpp b/source/source_estate/module_charge/mix_precond.cpp deleted file mode 100644 index b3627db7d2c..00000000000 --- a/source/source_estate/module_charge/mix_precond.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "charge_mixing.h" - -#include "source_io/module_parameter/parameter.h" -#include "source_base/timer.h" - -void Charge_Mixing::Kerker_screen_recip(std::complex* drhog) -{ - ModuleBase::TITLE("Charge_Mixing", "Kerker_screen_recip"); - - if (this->mixing_gg0 <= 0.0 || this->mixing_beta <= 0.1) - { - return; - } - - ModuleBase::timer::start("Charge_Mixing", "Kerker_screen_recip"); - - const int nspin = PARAM.inp.nspin; - - double fac = 0.0; - double gg0 = 0.0; - double amin = 0.0; - - /// consider a resize for mixing_angle - int resize_tmp = 1; - if (nspin == 4 && this->mixing_angle > 0) - { - resize_tmp = 2; - } - - /// implement Kerker for density and magnetization separately - for (int is = 0; is < nspin / resize_tmp; ++is) - { - const int is_idx = is * this->rhopw->npw; - /// new mixing method only support nspin=2 not nspin=4 - if (is >= 1) - { - if (this->mixing_gg0_mag <= 0.0001 || this->mixing_beta_mag <= 0.1) - { -#ifdef __DEBUG - assert(is == 1); // make sure break works -#endif - double is_mag = nspin - 1; - //for (int ig = 0; ig < this->rhopw->npw * is_mag; ig++) - //{ - // drhog[is_idx + ig] *= 1; - //} - break; - } - fac = this->mixing_gg0_mag; - amin = this->mixing_beta_mag; - } - else - { - fac = this->mixing_gg0; - amin = this->mixing_beta; - } - - gg0 = std::pow(fac * ModuleBase::BOHR_TO_A / *this->tpiba, 2); - - const double gg0_amin = this->mixing_gg0_min / amin; - -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for (int ig = 0; ig < this->rhopw->npw; ++ig) - { - double gg = this->rhopw->gg[ig]; - double filter_g = std::max(gg / (gg + gg0), gg0_amin); - drhog[is_idx + ig] *= filter_g; - } - } - - ModuleBase::timer::end("Charge_Mixing", "Kerker_screen_recip"); - return; -} - -void Charge_Mixing::Kerker_screen_real(double* drhor) -{ - ModuleBase::TITLE("Charge_Mixing", "Kerker_screen_real"); - - if (this->mixing_gg0 <= 0.0001 || this->mixing_beta <= 0.1) - { - return; - } - - ModuleBase::timer::start("Charge_Mixing", "Kerker_screen_real"); - - const int nspin = PARAM.inp.nspin; - assert(nspin==1 || nspin==2 || nspin==4); - - /// consider a resize for mixing_angle - int resize_tmp = 1; - if (nspin == 4 && this->mixing_angle > 0) - { - resize_tmp = 2; - } - - std::vector> drhog(this->rhopw->npw * nspin / resize_tmp); - std::vector drhor_filter(this->rhopw->nrxx * nspin / resize_tmp); - - for (int is = 0; is < nspin / resize_tmp; ++is) - { - // Note after this process some G which is higher than Gmax will be filtered. - // Thus we cannot use Kerker_screen_recip(drhog.data()) directly after it. - this->rhopw->real2recip(drhor + is * this->rhopw->nrxx, drhog.data() + is * this->rhopw->npw); - } - /// implement Kerker for density and magnetization separately - double fac = 0.0; - double gg0 = 0.0; - double amin = 0.0; - - for (int is = 0; is < nspin / resize_tmp; is++) - { - - if (is >= 1) - { - if (this->mixing_gg0_mag <= 0.0001 || this->mixing_beta_mag <= 0.1) - { -#ifdef __DEBUG - assert(is == 1); /// make sure break works -#endif - double is_mag = nspin - 1; - if (nspin == 4 && this->mixing_angle > 0) { is_mag = 1; -} - for (int ig = 0; ig < this->rhopw->npw * is_mag; ig++) - { - drhog[is * this->rhopw->npw + ig] = 0; - } - break; - } - fac = this->mixing_gg0_mag; - amin = this->mixing_beta_mag; - } - else - { - fac = this->mixing_gg0; - amin = this->mixing_beta; - } - - gg0 = std::pow(fac * ModuleBase::BOHR_TO_A / *this->tpiba, 2); - - const int is_idx = is * this->rhopw->npw; - const double gg0_amin = this->mixing_gg0_min / amin; -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for (int ig = 0; ig < this->rhopw->npw; ig++) - { - double gg = this->rhopw->gg[ig]; - // I have not decided how to handle gg=0 part, will be changed in future - //if (gg == 0) - //{ - // drhog[is_idx + ig] *= 0; - // continue; - //} - double filter_g = std::max(gg / (gg + gg0), gg0_amin); - drhog[is_idx + ig] *= (1 - filter_g); - } - } - /// inverse FT - for (int is = 0; is < nspin / resize_tmp; ++is) - { - this->rhopw->recip2real(drhog.data() + is * this->rhopw->npw, drhor_filter.data() + is * this->rhopw->nrxx); - } - -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for (int ir = 0; ir < this->rhopw->nrxx * nspin / resize_tmp; ir++) - { - drhor[ir] -= drhor_filter[ir]; - } - - ModuleBase::timer::end("Charge_Mixing", "Kerker_screen_real"); - return; -} diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 2b9cb2742f8..88777fc8fa2 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -112,7 +112,7 @@ AddTest( LIBS parameter base psi device planewave_serial cell_info SOURCES charge_mixing_test.cpp ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_drho.cpp - ../module_charge/mix_precond.cpp ../module_charge/charge_mixing_rho.cpp + ../module_charge/chg_precond.cpp ../module_charge/charge_mixing_rho.cpp ../module_charge/charge_mixing_uspp.cpp ) diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index 68c36ba9283..cc793c41445 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -7,6 +7,7 @@ #include "../module_charge/charge_mixing.h" #include "../module_charge/chg_drho.h" #include "../module_charge/chg_drho_detail.h" +#include "../module_charge/chg_precond.h" #include "source_base/module_mixing/broyden_mixing.h" #include "source_basis/module_pw/pw_basis.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -54,8 +55,8 @@ void Charge::set_rhopw(ModulePW::PW_Basis* rhopw_in) * Charge_Mixing::get_mixing_ndim() * Charge_Mixing::get_mixing_gg0() * - set the basic parameters of class charge_mixing - * - KerkerScreenTest: Charge_Mixing::Kerker_screen_recip(drhog) - * Charge_Mixing::Kerker_screen_real(drhog) + * - KerkerScreenTest: module_charge::kerker_screen_recip(cfg, rhopw, tpiba, drhog) + * module_charge::kerker_screen_real(cfg, rhopw, tpiba, drhog) * - screen drho with Kerker method * - InnerDotTest: module_charge::inner_product_recip_hartree(rhog1, rhog2) * module_charge::detail::inner_product_recip_rho(rhog1, rhog2) @@ -466,12 +467,10 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) TEST_F(ChargeMixingTest, KerkerScreenRecipTest) { - Charge_Mixing CMtest; - CMtest.set_rhopw(&pw_basis, &pw_basis); ucell.tpiba = 1.0; - CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); // nspin = 1 PARAM.input.nspin = 1; + MixingConfig cfg = make_cfg(); std::complex* drhog = new std::complex[PARAM.input.nspin*pw_basis.npw]; std::complex* drhog_old = new std::complex[PARAM.input.nspin*pw_basis.npw]; for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) @@ -479,20 +478,20 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) drhog_old[i] = drhog[i] = std::complex(1.0, 1.0); } // no kerker - CMtest.mixing_gg0 = 0.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 0.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) { EXPECT_EQ(drhog[i], drhog_old[i]); } // kerker - CMtest.mixing_gg0 = 1.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 1.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); double gg0 = std::pow(ModuleBase::BOHR_TO_A, 2); for (int i = 0; i < pw_basis.npw; ++i) { double gg = this->pw_basis.gg[i]; - double ref = std::max(gg / (gg + gg0), 0.1 / CMtest.mixing_beta); + double ref = std::max(gg / (gg + gg0), 0.1 / cfg.mixing_beta); EXPECT_NEAR(drhog[i].real(), ref, 1e-10); EXPECT_NEAR(drhog[i].imag(), ref, 1e-10); } @@ -501,8 +500,9 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) // nspin = 2 PARAM.input.nspin = 2; - CMtest.mixing_beta = 0.4; - CMtest.mixing_beta_mag = 1.6; + cfg = make_cfg(); + cfg.mixing_beta = 0.4; + cfg.mixing_beta_mag = 1.6; drhog = new std::complex[PARAM.input.nspin*pw_basis.npw]; drhog_old = new std::complex[PARAM.input.nspin*pw_basis.npw]; for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) @@ -510,20 +510,20 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) drhog_old[i] = drhog[i] = std::complex(1.0, 1.0); } // mixing_gg0 = 0.0 - CMtest.mixing_gg0 = 0.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 0.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) { EXPECT_EQ(drhog[i], drhog_old[i]); } // mixing_gg0 = 1.0, mixing_gg0_mag = 0.0 - CMtest.mixing_gg0 = 1.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 1.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); gg0 = std::pow(ModuleBase::BOHR_TO_A, 2); for (int i = 0; i < pw_basis.npw; ++i) { double gg = this->pw_basis.gg[i]; - double ref = std::max(gg / (gg + gg0), 0.1 / CMtest.mixing_beta); + double ref = std::max(gg / (gg + gg0), 0.1 / cfg.mixing_beta); // rho EXPECT_NEAR(drhog[i].real(), ref, 1e-10); EXPECT_NEAR(drhog[i].imag(), ref, 1e-10); @@ -536,6 +536,7 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) // nspin = 4 PARAM.input.nspin = 4; + cfg = make_cfg(); drhog = new std::complex[PARAM.input.nspin*pw_basis.npw]; drhog_old = new std::complex[PARAM.input.nspin*pw_basis.npw]; for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) @@ -543,20 +544,20 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) drhog_old[i] = drhog[i] = std::complex(1.0, 1.0); } // mixing_gg0 = 0.0 - CMtest.mixing_gg0 = 0.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 0.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) { EXPECT_EQ(drhog[i], drhog_old[i]); } // mixing_gg0 = 1.0, mixing_gg0_mag = 0.0 - CMtest.mixing_gg0 = 1.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 1.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); gg0 = std::pow(ModuleBase::BOHR_TO_A, 2); for (int i = 0; i < pw_basis.npw; ++i) { double gg = this->pw_basis.gg[i]; - double ref = std::max(gg / (gg + gg0), 0.1 / CMtest.mixing_beta); + double ref = std::max(gg / (gg + gg0), 0.1 / cfg.mixing_beta); // rho EXPECT_NEAR(drhog[i].real(), ref, 1e-10); EXPECT_NEAR(drhog[i].imag(), ref, 1e-10); @@ -567,15 +568,15 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) EXPECT_NEAR(drhog[i + pw_basis.npw].imag(), 1.0, 1e-10); } // mixing_gg0 = 1.0, mixing_gg0_mag = 2.0 - CMtest.mixing_gg0 = 1.0; - CMtest.mixing_gg0_mag = 2.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 1.0; + cfg.mixing_gg0_mag = 2.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); double gg1 = std::pow(1.0 * ModuleBase::BOHR_TO_A, 2); double gg2 = std::pow(2.0 * ModuleBase::BOHR_TO_A, 2); for (int i = 0; i < pw_basis.npw; ++i) { double gg = this->pw_basis.gg[i]; - double ref = std::max(gg / (gg + gg1), 0.1 / CMtest.mixing_beta); + double ref = std::max(gg / (gg + gg1), 0.1 / cfg.mixing_beta); // rho EXPECT_NEAR(drhog[i].real(), ref * ref, 1e-10); EXPECT_NEAR(drhog[i].imag(), ref * ref, 1e-10); @@ -583,7 +584,7 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) for (int i = 0; i < pw_basis.npw; ++i) { double gg = this->pw_basis.gg[i]; - double ref = std::max(gg / (gg + gg2), 0.1 / CMtest.mixing_beta_mag); + double ref = std::max(gg / (gg + gg2), 0.1 / cfg.mixing_beta_mag); // rho for (int j = 1; j < PARAM.input.nspin; ++j) { @@ -597,12 +598,10 @@ TEST_F(ChargeMixingTest, KerkerScreenRecipTest) TEST_F(ChargeMixingTest, KerkerScreenRealTest) { - Charge_Mixing CMtest; - CMtest.set_rhopw(&pw_basis, &pw_basis); ucell.tpiba = 1.0; - CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); // nspin = 1 PARAM.input.nspin = 1; + MixingConfig cfg = make_cfg(); double* drhor = new double[PARAM.input.nspin*pw_basis.nrxx]; double* drhor_ref = new double[PARAM.input.nspin*pw_basis.nrxx]; for (int i = 0; i < PARAM.input.nspin*pw_basis.nrxx; ++i) @@ -610,8 +609,8 @@ TEST_F(ChargeMixingTest, KerkerScreenRealTest) drhor_ref[i] = drhor[i] = 1.0; } // no kerker - CMtest.mixing_gg0 = 0.0; - CMtest.Kerker_screen_real(drhor); + cfg.mixing_gg0 = 0.0; + module_charge::kerker_screen_real(cfg, &pw_basis, ucell.tpiba, drhor); for (int i = 0; i < PARAM.input.nspin*pw_basis.nrxx; ++i) { EXPECT_EQ(drhor[i], drhor_ref[i]); @@ -621,7 +620,8 @@ TEST_F(ChargeMixingTest, KerkerScreenRealTest) // nspin = 2 PARAM.input.nspin = 2; - CMtest.mixing_gg0 = 0.0; + cfg = make_cfg(); + cfg.mixing_gg0 = 0.0; std::complex* drhog = new std::complex[PARAM.input.nspin*pw_basis.npw]; std::complex* drhog_old = new std::complex[PARAM.input.nspin*pw_basis.npw]; drhor = new double[PARAM.input.nspin*pw_basis.nrxx]; @@ -630,22 +630,22 @@ TEST_F(ChargeMixingTest, KerkerScreenRealTest) { drhog_old[i] = drhog[i] = std::complex(1.0, 1.0); } - CMtest.Kerker_screen_recip(drhog); // no kerker + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); // no kerker for (int i = 0; i < PARAM.input.nspin*pw_basis.npw; ++i) { EXPECT_EQ(drhog[i], drhog_old[i]); } // RECIPROCAL - CMtest.mixing_gg0 = 1.0; - PARAM.input.mixing_gg0_mag = 0.0; - CMtest.Kerker_screen_recip(drhog); + cfg.mixing_gg0 = 1.0; + cfg.mixing_gg0_mag = 0.0; + module_charge::kerker_screen_recip(cfg, &pw_basis, ucell.tpiba, drhog); const double gg0 = std::pow(ModuleBase::BOHR_TO_A, 2); for (int i = 0; i < pw_basis.npw; ++i) { std::complex ration = drhog[i] / drhog[i+pw_basis.npw]; double gg = this->pw_basis.gg[i]; - double ration_ref = std::max(gg / (gg + gg0), 0.1 / CMtest.mixing_beta); + double ration_ref = std::max(gg / (gg + gg0), 0.1 / cfg.mixing_beta); EXPECT_NEAR(ration.real(), ration_ref, 1e-10); EXPECT_NEAR(ration.imag(), 0, 1e-10); } @@ -654,13 +654,13 @@ TEST_F(ChargeMixingTest, KerkerScreenRealTest) pw_basis.recip2real(drhog, drhor_ref); pw_basis.recip2real(drhog_old, drhor); - CMtest.mixing_gg0 = 0.0; - PARAM.input.mixing_gg0_mag = 0.0; + cfg.mixing_gg0 = 0.0; + cfg.mixing_gg0_mag = 0.0; // nothing happens - CMtest.Kerker_screen_real(drhor); + module_charge::kerker_screen_real(cfg, &pw_basis, ucell.tpiba, drhor); - CMtest.mixing_gg0 = 1.0; - CMtest.Kerker_screen_real(drhor); + cfg.mixing_gg0 = 1.0; + module_charge::kerker_screen_real(cfg, &pw_basis, ucell.tpiba, drhor); for (int i = 0; i < pw_basis.nrxx; ++i) { EXPECT_NEAR(drhor[i], drhor_ref[i], 1e-8); From e4e6fc93f7cb6e3ab5c03bf84f0dc6761d3784bd Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 21:15:31 +0800 Subject: [PATCH 23/37] module_charge: fix Makefile.Objects after mix_precond -> chg_precond rename Update the non-CMake object list to track the renamed translation unit so make-based builds do not reference the deleted mix_precond.o. --- source/Makefile.Objects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index e76a0e9b512..d2d15f65a55 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -834,7 +834,7 @@ OBJS_SRCPW=h_ewald_pw.o\ charge_mixing.o\ charge_mixing_dmr.o\ chg_drho.o\ - mix_precond.o\ + chg_precond.o\ charge_mixing_rho.o\ charge_mixing_uspp.o\ rhog_io.o\ From 9a96847c96f3d1e62cc2c04b0851f4de76a76a40 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 21:19:29 +0800 Subject: [PATCH 24/37] module_charge: drop Charge_Mixing::get_drho/get_dkin wrappers Expose cal_drho/cal_dkin as module_charge free functions in chg_drho.h and let ESolver_KS call them directly with explicit arguments; add Charge_Mixing::get_mixing_config() as a const observer for the config. --- source/source_esolver/esolver_ks.cpp | 10 ++++-- .../module_charge/charge_mixing.h | 14 ++++---- .../source_estate/module_charge/chg_drho.cpp | 33 +++++------------ source/source_estate/module_charge/chg_drho.h | 36 +++++++++++++++++++ .../module_charge/chg_drho_detail.h | 2 +- 5 files changed, 59 insertions(+), 36 deletions(-) diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 0e8a72af6ee..91f604757d8 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -6,6 +6,7 @@ #include "source_estate/update_pot.h" // mohan add 20251016 #include "source_estate/module_charge/chgmixing.h" // mohan add 20251018 +#include "source_estate/module_charge/chg_drho.h" // module_charge::cal_drho/cal_dkin #include "source_pw/module_pwdft/setup_pwwfc.h" // mohan add 20251018 #include "source_hsolver/hsolver.h" #include "source_io/module_energy/write_eig_occ.h" @@ -112,7 +113,8 @@ void ESolver_KS::hamilt2rho(UnitCell& ucell, const int istep, const int iter, co // example wavefunctions uses 20 processors while density uses 10. if (PARAM.globalv.ks_run) { - drho = p_chgmix->get_drho(&this->chr, this->inp_->nelec); + drho = module_charge::cal_drho(&this->chr, this->inp_->nelec, *this->pw_rho, + p_chgmix->get_mixing_config(), ucell.omega, ucell.tpiba); hsolver_error = 0.0; if (iter == 1 && this->inp_->calculation != "nscf") { @@ -129,7 +131,8 @@ void ESolver_KS::hamilt2rho(UnitCell& ucell, const int istep, const int iter, co this->hamilt2rho_single(ucell, istep, iter, diag_ethr); - drho = p_chgmix->get_drho(&this->chr, this->inp_->nelec); + drho = module_charge::cal_drho(&this->chr, this->inp_->nelec, *this->pw_rho, + p_chgmix->get_mixing_config(), ucell.omega, ucell.tpiba); hsolver_error = hsolver::cal_hsolve_error(this->inp_->basis_type, this->inp_->esolver_type, diag_ethr, this->inp_->nelec); @@ -288,7 +291,8 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & double dkin = 0.0; // for meta-GGA if (XC_Functional::get_ked_flag()) { - dkin = p_chgmix->get_dkin(&this->chr, this->inp_->nelec); + dkin = module_charge::cal_dkin(&this->chr, this->inp_->nelec, *this->pw_rho, + p_chgmix->get_mixing_config(), ucell.omega); } // Iter finish diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index 02162d4e733..b5699a4b420 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -74,14 +74,7 @@ class Charge_Mixing void mix_uom(std::vector& uom_in, std::vector& uom_save_in); /** - * @brief Get the drho between rho and rho_save, similar for get_dkin - * - */ - double get_drho(Charge* chr, const double nelec); - double get_dkin(Charge* chr, const double nelec); - - /** - * @brief reset mixing, actually we only call init_mixing() to reset mixing instead of this function + * @brief reset mixing, actually we only call init_mixing() to reset mixing instead of this function */ void mix_reset(); @@ -100,6 +93,11 @@ class Charge_Mixing double get_mixing_gg0() const {return mixing_gg0;} Base_Mixing::Mixing* get_mixing() const {return mixing;} + /** + * @brief read-only access to the aggregated mixing config set by set_mixing() + */ + const MixingConfig& get_mixing_config() const {return cfg_;} + // for mixing restart int mixing_restart_step = 0; //which step to restart mixing during SCF, always equal to scf_namx except for the mixing restart int mixing_restart_count = 0; // the number of restart mixing during SCF. Do not set mixing_restart_count as bool since I want to keep some flexibility in the future diff --git a/source/source_estate/module_charge/chg_drho.cpp b/source/source_estate/module_charge/chg_drho.cpp index 86454f8a58e..544af8603ec 100644 --- a/source/source_estate/module_charge/chg_drho.cpp +++ b/source/source_estate/module_charge/chg_drho.cpp @@ -1,4 +1,4 @@ -#include "charge_mixing.h" +#include "charge.h" #include "chg_drho.h" #include "chg_drho_detail.h" #include "source_base/timer.h" @@ -7,7 +7,7 @@ #include -namespace +namespace module_charge { // Charge residual between chr->rho and chr->rho_save, normalized per electron. @@ -19,8 +19,8 @@ double cal_drho(Charge* chr, const double tpiba) { assert(chr != nullptr); - ModuleBase::TITLE("Charge_Mixing", "get_drho"); - ModuleBase::timer::start("Charge_Mixing", "get_drho"); + ModuleBase::TITLE("module_charge", "cal_drho"); + ModuleBase::timer::start("module_charge", "cal_drho"); const int nspin = cfg.nspin; assert(nspin==1 || nspin==2 || nspin==4); double drho = 0.0; @@ -81,7 +81,7 @@ double cal_drho(Charge* chr, drho /= nelec; } - ModuleBase::timer::end("Charge_Mixing", "get_drho"); + ModuleBase::timer::end("module_charge", "cal_drho"); return drho; } @@ -97,8 +97,8 @@ double cal_dkin(Charge* chr, { return 0.0; }; - ModuleBase::TITLE("Charge_Mixing", "get_dkin"); - ModuleBase::timer::start("Charge_Mixing", "get_dkin"); + ModuleBase::TITLE("module_charge", "cal_dkin"); + ModuleBase::timer::start("module_charge", "cal_dkin"); double dkin = 0.0; // Get dkin from kin_r and kin_r_save for PW and LCAO both, which is different from drho. @@ -125,26 +125,11 @@ double cal_dkin(Charge* chr, dkin *= omega / static_cast(rhopw.nxyz); dkin /= nelec; - ModuleBase::timer::end("Charge_Mixing", "get_dkin"); + ModuleBase::timer::end("module_charge", "cal_dkin"); return dkin; } -} // namespace - -double Charge_Mixing::get_drho(Charge* chr, const double nelec) -{ - assert(this->rhopw != nullptr); - assert(this->omega != nullptr); - assert(this->tpiba != nullptr); - return cal_drho(chr, nelec, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); -} - -double Charge_Mixing::get_dkin(Charge* chr, const double nelec) -{ - assert(this->rhopw != nullptr); - assert(this->omega != nullptr); - return cal_dkin(chr, nelec, *this->rhopw, this->cfg_, *this->omega); -} +} // namespace module_charge namespace module_charge { diff --git a/source/source_estate/module_charge/chg_drho.h b/source/source_estate/module_charge/chg_drho.h index 1b52581e7f1..a13c374ef8d 100644 --- a/source/source_estate/module_charge/chg_drho.h +++ b/source/source_estate/module_charge/chg_drho.h @@ -14,9 +14,45 @@ namespace ModulePW class PW_Basis; } +class Charge; + namespace module_charge { +/** + * @brief Charge residual between chr->rho and chr->rho_save, normalized per electron. + * + * @param chr charge object supplying rho/rho_save (and rhog buffers for the reciprocal case) + * @param nelec number of electrons, used to normalize the real-space residual + * @param rhopw plane-wave basis supplying the real/reciprocal grid sizes + * @param cfg mixing config (nspin, scf_thr_type and magnetization flags select the loops) + * @param omega cell volume, used to normalize the real-space residual + * @param tpiba 2*pi/lattice constant, used by the reciprocal metric + * @return pooled residual value + */ +double cal_drho(Charge* chr, + const double nelec, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega, + const double tpiba); + +/** + * @brief Kinetic-energy-density residual between chr->kin_r and chr->kin_r_save. + * + * @param chr charge object supplying kin_r/kin_r_save + * @param nelec number of electrons, used to normalize the residual + * @param rhopw plane-wave basis supplying the real-space grid size + * @param cfg mixing config (nspin and magnetization flags select the loops) + * @param omega cell volume, used to normalize the residual + * @return pooled residual value + */ +double cal_dkin(Charge* chr, + const double nelec, + const ModulePW::PW_Basis& rhopw, + const MixingConfig& cfg, + const double omega); + /** * @brief Inner product of two real-space vectors used in real-space mixing. * diff --git a/source/source_estate/module_charge/chg_drho_detail.h b/source/source_estate/module_charge/chg_drho_detail.h index b21a4fcf750..f198b4fbce2 100644 --- a/source/source_estate/module_charge/chg_drho_detail.h +++ b/source/source_estate/module_charge/chg_drho_detail.h @@ -1,7 +1,7 @@ #ifndef CHG_DRHO_DETAIL_H #define CHG_DRHO_DETAIL_H -// Internal reciprocal inner product for the charge residual (get_drho). +// Internal reciprocal inner product for the charge residual (cal_drho). // Not part of the public module_charge API: only chg_drho.cpp and the // charge mixing unit test are expected to include this header. From c89aba3d98b992522eb9ea37b3c46b41864867fa Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 21:22:10 +0800 Subject: [PATCH 25/37] module_charge: rename chgmixing.h/cpp to chg_routine.h/cpp Align with the chg_ naming pattern used in the same directory (chg_drho, chg_precond, chg_symm, chg_tools). Update include guard to CHG_ROUTINE_H, the self-include in chg_routine.cpp, the entry in source_estate/CMakeLists.txt and source/Makefile.Objects, and the three #include sites in esolver_ks{,_pw,_lcao}.cpp. Function names (chgmixing_ks{,_pw,_lcao}) and TITLE/timer tags are intentionally left unchanged to keep the diff minimal. --- source/Makefile.Objects | 2 +- source/source_esolver/esolver_ks.cpp | 2 +- source/source_esolver/esolver_ks_lcao.cpp | 2 +- source/source_esolver/esolver_ks_pw.cpp | 2 +- source/source_estate/CMakeLists.txt | 2 +- .../module_charge/{chgmixing.cpp => chg_routine.cpp} | 2 +- .../module_charge/{chgmixing.h => chg_routine.h} | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) rename source/source_estate/module_charge/{chgmixing.cpp => chg_routine.cpp} (99%) rename source/source_estate/module_charge/{chgmixing.h => chg_routine.h} (96%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index d2d15f65a55..5b570b8f154 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -824,7 +824,7 @@ OBJS_SRCPW=h_ewald_pw.o\ vnl_pw_grad.o\ vnl_pw_init_vnl.o\ vnl_pw_qrad.o\ - chgmixing.o\ + chg_routine.o\ charge.o\ charge_atomic.o\ charge_init.o\ diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 91f604757d8..37d9157c64d 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -5,7 +5,7 @@ #include "source_io/module_json/output_info.h" #include "source_estate/update_pot.h" // mohan add 20251016 -#include "source_estate/module_charge/chgmixing.h" // mohan add 20251018 +#include "source_estate/module_charge/chg_routine.h" // mohan add 20251018 #include "source_estate/module_charge/chg_drho.h" // module_charge::cal_drho/cal_dkin #include "source_pw/module_pwdft/setup_pwwfc.h" // mohan add 20251018 #include "source_hsolver/hsolver.h" diff --git a/source/source_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index f4edfceed5a..5ff4da8b9d7 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -18,7 +18,7 @@ #include "../source_lcao/module_ri/exx_opt_orb.h" #endif #include "source_lcao/module_rdmft/rdmft.h" -#include "source_estate/module_charge/chgmixing.h" // use charge mixing, mohan add 20251006 +#include "source_estate/module_charge/chg_routine.h" // use charge mixing, mohan add 20251006 #include "source_estate/module_dm/init_dm.h" // init dm from electronic wave functions #include "source_io/module_restart/restart.h" // GlobalC::restart for load_exx_flag #include "source_io/module_ctrl/ctrl_runner_lcao.h" // use ctrl_runner_lcao() diff --git a/source/source_esolver/esolver_ks_pw.cpp b/source/source_esolver/esolver_ks_pw.cpp index b345f9c12af..871e725506b 100644 --- a/source/source_esolver/esolver_ks_pw.cpp +++ b/source/source_esolver/esolver_ks_pw.cpp @@ -14,7 +14,7 @@ #include "source_base/kernels/dsp/dsp_connector.h" #endif -#include "source_estate/module_charge/chgmixing.h" // use charge mixing, mohan add 20251006 +#include "source_estate/module_charge/chg_routine.h" // use charge mixing, mohan add 20251006 #include "source_estate/setup_estate_pw.h" // mohan add 20251005 #include "source_hamilt/module_xc/general_exx_info.h" // for General_Exx_Info type used via general_exx_info_ #include "source_io/module_ctrl/ctrl_output_pw.h" // mohan add 20250927 diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 931ac8b9fcf..b018722d93b 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -27,7 +27,7 @@ list(APPEND objects module_pot/td_field.cpp module_pot/td_field_profiles.cpp module_pot/td_field_manager.cpp - module_charge/chgmixing.cpp + module_charge/chg_routine.cpp module_charge/charge.cpp module_charge/charge_atomic.cpp module_charge/charge_init.cpp diff --git a/source/source_estate/module_charge/chgmixing.cpp b/source/source_estate/module_charge/chg_routine.cpp similarity index 99% rename from source/source_estate/module_charge/chgmixing.cpp rename to source/source_estate/module_charge/chg_routine.cpp index ee8bc8a4fdd..a77a9fc4b45 100644 --- a/source/source_estate/module_charge/chgmixing.cpp +++ b/source/source_estate/module_charge/chg_routine.cpp @@ -1,4 +1,4 @@ -#include "source_estate/module_charge/chgmixing.h" +#include "source_estate/module_charge/chg_routine.h" #include "source_base/parallel_comm.h" #include "source_estate/update_pot.h" diff --git a/source/source_estate/module_charge/chgmixing.h b/source/source_estate/module_charge/chg_routine.h similarity index 96% rename from source/source_estate/module_charge/chgmixing.h rename to source/source_estate/module_charge/chg_routine.h index c3de464ebb7..f2603835d7f 100644 --- a/source/source_estate/module_charge/chgmixing.h +++ b/source/source_estate/module_charge/chg_routine.h @@ -1,5 +1,5 @@ -#ifndef CHGMIXING_H -#define CHGMIXING_H +#ifndef CHG_ROUTINE_H +#define CHG_ROUTINE_H #include "source_estate/elecstate.h" // use pelec #include "source_estate/module_charge/charge.h" // use chr From ae65748a57459d4d07fc9fc2c4540cbf840483bf Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 21:28:26 +0800 Subject: [PATCH 26/37] module_charge: rename mixing_config.h to chg_mix_cfg.h Rename the MixingConfig header to align with the chg_* naming convention in module_charge. Update the include guard and the four in-tree includers; no CMake change is needed since the header is not listed explicitly. --- source/source_estate/module_charge/charge_mixing.h | 2 +- source/source_estate/module_charge/chg_drho.h | 2 +- source/source_estate/module_charge/chg_drho_detail.h | 2 +- .../module_charge/{mixing_config.h => chg_mix_cfg.h} | 6 +++--- source/source_estate/module_charge/chg_precond.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename source/source_estate/module_charge/{mixing_config.h => chg_mix_cfg.h} (95%) diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index b5699a4b420..ddf05c83aee 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -1,7 +1,7 @@ #ifndef CHARGE_MIXING_H #define CHARGE_MIXING_H #include "charge.h" -#include "mixing_config.h" +#include "chg_mix_cfg.h" #include "source_estate/module_dm/density_matrix.h" #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" diff --git a/source/source_estate/module_charge/chg_drho.h b/source/source_estate/module_charge/chg_drho.h index a13c374ef8d..cae85dbb5bc 100644 --- a/source/source_estate/module_charge/chg_drho.h +++ b/source/source_estate/module_charge/chg_drho.h @@ -7,7 +7,7 @@ #include -#include "mixing_config.h" +#include "chg_mix_cfg.h" namespace ModulePW { diff --git a/source/source_estate/module_charge/chg_drho_detail.h b/source/source_estate/module_charge/chg_drho_detail.h index f198b4fbce2..4a6d2553b09 100644 --- a/source/source_estate/module_charge/chg_drho_detail.h +++ b/source/source_estate/module_charge/chg_drho_detail.h @@ -7,7 +7,7 @@ #include -#include "mixing_config.h" +#include "chg_mix_cfg.h" namespace ModulePW { diff --git a/source/source_estate/module_charge/mixing_config.h b/source/source_estate/module_charge/chg_mix_cfg.h similarity index 95% rename from source/source_estate/module_charge/mixing_config.h rename to source/source_estate/module_charge/chg_mix_cfg.h index 6f0f251c2d1..0f52e698567 100644 --- a/source/source_estate/module_charge/mixing_config.h +++ b/source/source_estate/module_charge/chg_mix_cfg.h @@ -1,5 +1,5 @@ -#ifndef MIXING_CONFIG_H -#define MIXING_CONFIG_H +#ifndef CHG_MIX_CFG_H +#define CHG_MIX_CFG_H #include @@ -28,4 +28,4 @@ struct MixingConfig bool domag_z = false; ///< whether only the z-component magnetism is considered }; -#endif // MIXING_CONFIG_H +#endif // CHG_MIX_CFG_H diff --git a/source/source_estate/module_charge/chg_precond.h b/source/source_estate/module_charge/chg_precond.h index d807d329e76..8d8d2298ba5 100644 --- a/source/source_estate/module_charge/chg_precond.h +++ b/source/source_estate/module_charge/chg_precond.h @@ -7,7 +7,7 @@ #include -#include "mixing_config.h" +#include "chg_mix_cfg.h" namespace ModulePW { From c531ea4842b334fe10ac690b9cd63b6a3970b78b Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 21:55:09 +0800 Subject: [PATCH 27/37] module_charge: convert Charge MPI helpers into chg_parallel free functions Rename charge_mpi.cpp to chg_parallel.cpp and add chg_parallel.h, moving the three stateless Charge member functions (reduce_diff_pools, rho_mpi, kin_r_mpi) to module_charge namespace free functions that take the Charge object explicitly. Remove their declarations from charge.h and update all call sites in elecstate_pw, stress_mgga, read_wf2rho_pw and sto_iter. Rename the unit test to test_chg_parallel.cpp and update the test target name accordingly. GlobalV/PARAM reads and the direct MPI_Allreduce in reduce_diff_pools are preserved as pre-existing technical debt (migration-neutral). --- source/Makefile.Objects | 2 +- source/source_estate/CMakeLists.txt | 2 +- source/source_estate/elecstate_pw.cpp | 3 +- source/source_estate/elecstate_pw_cal_tau.cpp | 4 +- source/source_estate/module_charge/charge.h | 22 ---------- .../{charge_mpi.cpp => chg_parallel.cpp} | 37 +++++++++++----- .../module_charge/chg_parallel.h | 43 +++++++++++++++++++ source/source_estate/test/CMakeLists.txt | 10 ++--- ...rge_mpi_test.cpp => test_chg_parallel.cpp} | 17 ++++---- source/source_io/module_wf/read_wf2rho_pw.cpp | 3 +- source/source_io/test/CMakeLists.txt | 2 +- source/source_io/test/read_wf2rho_pw_test.cpp | 3 +- source/source_pw/module_pwdft/stress_mgga.cpp | 3 +- source/source_pw/module_stodft/sto_iter.cpp | 3 +- 14 files changed, 100 insertions(+), 54 deletions(-) rename source/source_estate/module_charge/{charge_mpi.cpp => chg_parallel.cpp} (65%) create mode 100644 source/source_estate/module_charge/chg_parallel.h rename source/source_estate/test/{charge_mpi_test.cpp => test_chg_parallel.cpp} (95%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 5b570b8f154..bb3562f54d7 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -829,7 +829,7 @@ OBJS_SRCPW=h_ewald_pw.o\ charge_atomic.o\ charge_init.o\ chg_tools.o\ - charge_mpi.o\ + chg_parallel.o\ charge_extra.o\ charge_mixing.o\ charge_mixing_dmr.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index b018722d93b..64211b9ae59 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -32,7 +32,7 @@ list(APPEND objects module_charge/charge_atomic.cpp module_charge/charge_init.cpp module_charge/chg_tools.cpp - module_charge/charge_mpi.cpp + module_charge/chg_parallel.cpp module_charge/charge_extra.cpp module_charge/charge_mixing.cpp module_charge/charge_mixing_dmr.cpp diff --git a/source/source_estate/elecstate_pw.cpp b/source/source_estate/elecstate_pw.cpp index 05c21430e07..2bf743106f1 100644 --- a/source/source_estate/elecstate_pw.cpp +++ b/source/source_estate/elecstate_pw.cpp @@ -5,6 +5,7 @@ #include "source_base/module_device/device.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_estate/uspp_density.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" @@ -171,7 +172,7 @@ template void ElecStatePW::parallelK() { #ifdef __MPI - this->charge->rho_mpi(); + module_charge::rho_mpi(*this->charge); #endif } diff --git a/source/source_estate/elecstate_pw_cal_tau.cpp b/source/source_estate/elecstate_pw_cal_tau.cpp index 5c0b6ae9f5c..c210b604ba7 100644 --- a/source/source_estate/elecstate_pw_cal_tau.cpp +++ b/source/source_estate/elecstate_pw_cal_tau.cpp @@ -1,5 +1,7 @@ #include "elecstate_pw.h" +#include "source_estate/module_charge/chg_parallel.h" + namespace elecstate { template @@ -55,7 +57,7 @@ void ElecStatePW::cal_tau(const psi::Psi& psi) } } #ifdef __MPI - this->charge->kin_r_mpi(); + module_charge::kin_r_mpi(*this->charge); #endif ModuleBase::TITLE("ElecStatePW", "cal_tau"); } diff --git a/source/source_estate/module_charge/charge.h b/source/source_estate/module_charge/charge.h index 79cb1781265..b0e4654d475 100644 --- a/source/source_estate/module_charge/charge.h +++ b/source/source_estate/module_charge/charge.h @@ -110,28 +110,6 @@ class Charge void init_final_scf(); //LiuXh add 20180619 - public: - /** - * @brief Sum rho at different pools (k-point parallelism). - * Only used when GlobalV::KPAR > 1 - */ - void rho_mpi(); - - /** - * @brief Sum kin_r at different pools (k-point/band parallelism). - * Only used when GlobalV::KPAR * bndpar > 1 - */ - void kin_r_mpi(); - - /** - * @brief Reduce among different pools - * If NPROC_IN_POOLs are all the same, use GlobalV::KP_WORLD - * else, gather rho in a POOL, and then reduce among different POOLs - * - * @param array_rho f(rho): an array [nrxx] - */ - void reduce_diff_pools(double* array_rho) const; - void set_omega(double* omega_in){this->omega_ = omega_in;}; // mohan add 2021-02-20 diff --git a/source/source_estate/module_charge/charge_mpi.cpp b/source/source_estate/module_charge/chg_parallel.cpp similarity index 65% rename from source/source_estate/module_charge/charge_mpi.cpp rename to source/source_estate/module_charge/chg_parallel.cpp index 8ef4cce4a32..44e6bbb94fa 100644 --- a/source/source_estate/module_charge/charge_mpi.cpp +++ b/source/source_estate/module_charge/chg_parallel.cpp @@ -1,3 +1,9 @@ +#include "chg_parallel.h" + +#ifdef __MPI + +#include + #include "charge.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -5,24 +11,29 @@ #include "source_base/timer.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" -#ifdef __MPI -void Charge::reduce_diff_pools(double* array_rho) const + +namespace module_charge +{ + +void reduce_diff_pools(double* array_rho, const Charge& chr) { ModuleBase::TITLE("Charge", "reduce_diff_pools"); ModuleBase::timer::start("Charge", "reduce_diff_pools"); + assert(array_rho != nullptr); if (GlobalV::KPAR > 1) { - assert(this->pgrid != nullptr); - this->pgrid->reduce_across_pools(array_rho); + assert(chr.pgrid != nullptr); + chr.pgrid->reduce_across_pools(array_rho); } if (PARAM.globalv.all_ks_run && PARAM.inp.bndpar > 1) { - MPI_Allreduce(MPI_IN_PLACE, array_rho, this->nrxx, MPI_DOUBLE, MPI_SUM, BP_WORLD); + assert(chr.nrxx > 0); + MPI_Allreduce(MPI_IN_PLACE, array_rho, chr.nrxx, MPI_DOUBLE, MPI_SUM, BP_WORLD); } ModuleBase::timer::end("Charge", "reduce_diff_pools"); } -void Charge::rho_mpi() +void rho_mpi(Charge& chr) { ModuleBase::TITLE("Charge", "rho_mpi"); if (GlobalV::KPAR * PARAM.inp.bndpar <= 1) @@ -31,12 +42,14 @@ void Charge::rho_mpi() } ModuleBase::timer::start("Charge", "rho_mpi"); + assert(chr.rho != nullptr); for (int is = 0; is < PARAM.inp.nspin; ++is) { - reduce_diff_pools(this->rho[is]); + reduce_diff_pools(chr.rho[is], chr); if (XC_Functional::get_ked_flag() || PARAM.inp.out_elf[0] > 0) { - reduce_diff_pools(this->kin_r[is]); + assert(chr.kin_r != nullptr); + reduce_diff_pools(chr.kin_r[is], chr); } } @@ -44,7 +57,7 @@ void Charge::rho_mpi() return; } -void Charge::kin_r_mpi() +void kin_r_mpi(Charge& chr) { ModuleBase::TITLE("Charge", "kin_r_mpi"); if (GlobalV::KPAR * PARAM.inp.bndpar <= 1) @@ -55,13 +68,17 @@ void Charge::kin_r_mpi() if (XC_Functional::get_ked_flag() || PARAM.inp.out_elf[0] > 0) { + assert(chr.kin_r != nullptr); for (int is = 0; is < PARAM.inp.nspin; ++is) { - reduce_diff_pools(this->kin_r[is]); + reduce_diff_pools(chr.kin_r[is], chr); } } ModuleBase::timer::end("Charge", "kin_r_mpi"); return; } + +} // namespace module_charge + #endif diff --git a/source/source_estate/module_charge/chg_parallel.h b/source/source_estate/module_charge/chg_parallel.h new file mode 100644 index 00000000000..20a70020d17 --- /dev/null +++ b/source/source_estate/module_charge/chg_parallel.h @@ -0,0 +1,43 @@ +#ifndef CHG_PARALLEL_H +#define CHG_PARALLEL_H + +// MPI reductions of charge-density arrays across k-point pools and band +// groups. Stateless free functions extracted from Charge member functions; +// the charge buffers and the parallel grid are supplied by the Charge +// argument. The pool/band conditions are still read from GlobalV and PARAM +// as in the original implementation (migration-neutral). + +#ifdef __MPI + +class Charge; + +namespace module_charge +{ + +/** + * @brief Reduce a real-space array across k-point pools and band groups. + * + * @param array_rho real-space array [chr.nrxx], reduced in place + * @param chr charge object supplying the parallel grid and the local grid size + */ +void reduce_diff_pools(double* array_rho, const Charge& chr); + +/** + * @brief Reduce rho across pools; also reduce kin_r for meta-GGA or ELF. + * + * @param chr charge object supplying rho/kin_r buffers + */ +void rho_mpi(Charge& chr); + +/** + * @brief Reduce kin_r across pools for meta-GGA or ELF calculations. + * + * @param chr charge object supplying kin_r buffers + */ +void kin_r_mpi(Charge& chr); + +} // namespace module_charge + +#endif + +#endif // CHG_PARALLEL_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 88777fc8fa2..574e9cf537c 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -63,7 +63,7 @@ AddTest( ../elecstate_pw_cal_tau.cpp ../elecstate.cpp ../occupy.cpp - ../module_charge/charge_mpi.cpp + ../module_charge/chg_parallel.cpp ../../source_lcao/module_deltaspin/spin_constrain.cpp ../../source_lcao/module_deltaspin/deltaspin_state.cpp ../../source_psi/psi.cpp @@ -134,16 +134,16 @@ AddTest( ) AddTest( - TARGET MODULE_ESTATE_charge_mpi_test + TARGET MODULE_ESTATE_chg_parallel_test LIBS parameter psi base device planewave - SOURCES charge_mpi_test.cpp ../module_charge/charge_mpi.cpp + SOURCES test_chg_parallel.cpp ../module_charge/chg_parallel.cpp # Real MPI test: its main() calls MPI_Init unconditionally, so it must keep # __MPI despite this directory disabling it. KEEP_FEATURE_DEFINITIONS __MPI ) -add_test(NAME MODULE_ESTATE_charge_mpi_test_4np - COMMAND mpirun -np 4 ./MODULE_ESTATE_charge_mpi_test; +add_test(NAME MODULE_ESTATE_chg_parallel_test_4np + COMMAND mpirun -np 4 ./MODULE_ESTATE_chg_parallel_test; WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/source/source_estate/test/charge_mpi_test.cpp b/source/source_estate/test/test_chg_parallel.cpp similarity index 95% rename from source/source_estate/test/charge_mpi_test.cpp rename to source/source_estate/test/test_chg_parallel.cpp index 3f37329e8d6..00e28061a7e 100644 --- a/source/source_estate/test/charge_mpi_test.cpp +++ b/source/source_estate/test/test_chg_parallel.cpp @@ -2,6 +2,7 @@ #include "source_base/parallel_global.h" #include "source_base/parallel_grid.h" #include "source_estate/module_charge/charge.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" @@ -29,14 +30,14 @@ auto sum_array = [](const double* v, const int& nv) { return sum; }; /************************************************ - * unit test of module_charge/charge_mpi.cpp + * unit test of module_charge/chg_parallel.cpp ***********************************************/ /** * - Tested Functions: - * - rho_mpi: Charge::rho_mpi(): + * - rho_mpi: module_charge::rho_mpi() * - test rho_mpi - * - reduce_diff_pools: Charge::reduce_diff_pools() + * - reduce_diff_pools: module_charge::reduce_diff_pools() * - test reduce_diff_pools * - using rhopw and GlobalV */ @@ -99,7 +100,7 @@ TEST_F(ChargeMpiTest, reduce_diff_pools1) } double refsum = sum_array(array_rho, nrxx); - charge->reduce_diff_pools(array_rho); + module_charge::reduce_diff_pools(array_rho, *charge); double sum = sum_array(array_rho, nrxx); EXPECT_EQ(sum, refsum * GlobalV::KPAR); @@ -154,7 +155,7 @@ TEST_F(ChargeMpiTest, reduce_diff_pools2) } } - charge->reduce_diff_pools(array_rho); + module_charge::reduce_diff_pools(array_rho, *charge); double sum = sum_array(array_rho, nrxx); MPI_Allreduce(MPI_IN_PLACE, &sum, 1, MPI_DOUBLE, MPI_SUM, POOL_WORLD); EXPECT_EQ(sum, refsum * GlobalV::KPAR); @@ -200,7 +201,7 @@ TEST_F(ChargeMpiTest, rho_mpi) charge->nrxx = nrxx; charge->rho[0] = new double[nrxx]; charge->kin_r[0] = new double[nrxx]; - charge->rho_mpi(); + module_charge::rho_mpi(*charge); delete[] charge->rho[0]; delete[] charge->rho; @@ -210,7 +211,7 @@ TEST_F(ChargeMpiTest, rho_mpi) } GlobalV::KPAR = 1; - charge->rho_mpi(); + module_charge::rho_mpi(*charge); } TEST_F(ChargeMpiTest, kin_r_mpi) @@ -259,7 +260,7 @@ TEST_F(ChargeMpiTest, kin_r_mpi) } const double refsum = sum_array(charge->kin_r[0], nrxx); - charge->kin_r_mpi(); + module_charge::kin_r_mpi(*charge); const double sum = sum_array(charge->kin_r[0], nrxx); EXPECT_EQ(sum, refsum * GlobalV::KPAR); diff --git a/source/source_io/module_wf/read_wf2rho_pw.cpp b/source/source_io/module_wf/read_wf2rho_pw.cpp index 75aea2e469f..e4a689bf4fa 100644 --- a/source/source_io/module_wf/read_wf2rho_pw.cpp +++ b/source/source_io/module_wf/read_wf2rho_pw.cpp @@ -4,6 +4,7 @@ #include "source_base/module_out/filename.h" #include "source_base/timer.h" #include "source_estate/kernels/elecstate_op.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_estate/module_charge/chg_symm.h" #include "source_io/module_parameter/parameter.h" #include "source_base/module_device/memory_op.h" @@ -218,7 +219,7 @@ void ModuleIO::read_wf2rho_pw_impl(const ModulePW::PW_Basis_K* pw_wfc, #ifdef __MPI for (int is = 0; is < nspin; ++is) { - chg.reduce_diff_pools(chg.rho[is]); + module_charge::reduce_diff_pools(chg.rho[is], chg); } #endif diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/test/CMakeLists.txt index 9c4498bf419..baa3fd76c12 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/test/CMakeLists.txt @@ -189,7 +189,7 @@ add_test(NAME MODULE_IO_read_wfc_pw_test_parallel AddTest( TARGET MODULE_IO_read_wf2rho_pw_test LIBS parameter base device planewave psi symmetry - SOURCES read_wf2rho_pw_test.cpp ../module_wf/read_wfc_pw.cpp ../module_wf/read_wf2rho_pw.cpp ../../source_basis/module_pw/test/test_tool.cpp ../../source_estate/module_charge/charge_mpi.cpp ../module_wf/write_wfc_pw.cpp + SOURCES read_wf2rho_pw_test.cpp ../module_wf/read_wfc_pw.cpp ../module_wf/read_wf2rho_pw.cpp ../../source_basis/module_pw/test/test_tool.cpp ../../source_estate/module_charge/chg_parallel.cpp ../module_wf/write_wfc_pw.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp ) diff --git a/source/source_io/test/read_wf2rho_pw_test.cpp b/source/source_io/test/read_wf2rho_pw_test.cpp index 294b8a819e4..fe0654c56e5 100644 --- a/source/source_io/test/read_wf2rho_pw_test.cpp +++ b/source/source_io/test/read_wf2rho_pw_test.cpp @@ -9,6 +9,7 @@ #include "source_cell/klist.h" #include "source_cell/unitcell.h" #include "source_estate/module_charge/charge.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" @@ -266,7 +267,7 @@ TEST_F(ReadWfcRhoTest, ReadWfcRho) } #ifdef __MPI - chg_ref.reduce_diff_pools(chg_ref.rho[0]); + module_charge::reduce_diff_pools(chg_ref.rho[0], chg_ref); #endif // for spin=1 or 2, npol=1 diff --git a/source/source_pw/module_pwdft/stress_mgga.cpp b/source/source_pw/module_pwdft/stress_mgga.cpp index 1ee187a4e41..b57f2b74ba0 100644 --- a/source/source_pw/module_pwdft/stress_mgga.cpp +++ b/source/source_pw/module_pwdft/stress_mgga.cpp @@ -1,4 +1,5 @@ #include "source_base/timer.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" #include "stress_func.h" @@ -80,7 +81,7 @@ void Stress_Func::stress_mgga(const UnitCell& ucell, { for (int ipol = 0; ipol < 6; ++ipol) { - chr->reduce_diff_pools(&crosstaus_pack[is][ipol * nrxx]); + module_charge::reduce_diff_pools(&crosstaus_pack[is][ipol * nrxx], *chr); } } #endif diff --git a/source/source_pw/module_stodft/sto_iter.cpp b/source/source_pw/module_stodft/sto_iter.cpp index fc1f28d6e4b..03229e3729f 100644 --- a/source/source_pw/module_stodft/sto_iter.cpp +++ b/source/source_pw/module_stodft/sto_iter.cpp @@ -8,6 +8,7 @@ #include "source_base/tool_quit.h" #include "source_base/tool_title.h" #include "source_estate/kernels/elecstate_op.h" +#include "source_estate/module_charge/chg_parallel.h" #include "source_estate/occupy.h" #include "source_hsolver/para_lin_tf.h" #include "source_io/module_parameter/parameter.h" @@ -672,7 +673,7 @@ void Stochastic_Iter::cal_storho(const UnitCell& ucell, { for (int is = 0; is < nspin; ++is) { - pes->charge->reduce_diff_pools(sto_rho[is]); + module_charge::reduce_diff_pools(sto_rho[is], *pes->charge); if (!PARAM.globalv.all_ks_run && PARAM.inp.bndpar > 1) { MPI_Allreduce(MPI_IN_PLACE, sto_rho[is], nrxx, MPI_DOUBLE, MPI_SUM, BP_WORLD); From b9762593417c880a4ee56b1dc52ab8ecc77d0fb2 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 22:07:07 +0800 Subject: [PATCH 28/37] Rename charge_atomic files to chg_atomic - Rename module_charge/charge_atomic.{h,cpp} to chg_atomic.{h,cpp} - Update include guard to CHG_ATOMIC_H - Update includes in charge_init.cpp and charge_extra.cpp - Update source paths in CMakeLists.txt, test CMakeLists.txt - Fix stale object names in Makefile.Objects: replace symm_rho_charge.o/symm_rhog.o with chg_symm.o/chg_symm_detail.o --- source/Makefile.Objects | 6 +++--- source/source_estate/CMakeLists.txt | 2 +- source/source_estate/module_charge/charge_extra.cpp | 2 +- source/source_estate/module_charge/charge_init.cpp | 2 +- .../module_charge/{charge_atomic.cpp => chg_atomic.cpp} | 2 +- .../module_charge/{charge_atomic.h => chg_atomic.h} | 6 +++--- source/source_estate/test/CMakeLists.txt | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) rename source/source_estate/module_charge/{charge_atomic.cpp => chg_atomic.cpp} (99%) rename source/source_estate/module_charge/{charge_atomic.h => chg_atomic.h} (93%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index bb3562f54d7..3bf32975f4d 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -826,7 +826,7 @@ OBJS_SRCPW=h_ewald_pw.o\ vnl_pw_qrad.o\ chg_routine.o\ charge.o\ - charge_atomic.o\ + chg_atomic.o\ charge_init.o\ chg_tools.o\ chg_parallel.o\ @@ -837,6 +837,8 @@ OBJS_SRCPW=h_ewald_pw.o\ chg_precond.o\ charge_mixing_rho.o\ charge_mixing_uspp.o\ + chg_symm.o\ + chg_symm_detail.o\ rhog_io.o\ write_elecstat_pot.o\ write_init.o\ @@ -894,8 +896,6 @@ OBJS_SRCPW=h_ewald_pw.o\ stress_pw.o\ of_stress_pw.o\ of_print_info.o\ - symm_rho_charge.o\ - symm_rhog.o\ setup_psi_pw.o\ setup_psi.o\ psi_prepare.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 64211b9ae59..6d3978a1dc3 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -29,7 +29,7 @@ list(APPEND objects module_pot/td_field_manager.cpp module_charge/chg_routine.cpp module_charge/charge.cpp - module_charge/charge_atomic.cpp + module_charge/chg_atomic.cpp module_charge/charge_init.cpp module_charge/chg_tools.cpp module_charge/chg_parallel.cpp diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/charge_extra.cpp index 4eaf69a8b26..4a4299462e0 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/charge_extra.cpp @@ -1,6 +1,6 @@ #include "charge_extra.h" -#include "charge_atomic.h" +#include "chg_atomic.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" diff --git a/source/source_estate/module_charge/charge_init.cpp b/source/source_estate/module_charge/charge_init.cpp index 21e6af61044..8967604ea29 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -2,7 +2,7 @@ #include #include "charge.h" -#include "charge_atomic.h" +#include "chg_atomic.h" #include "chg_tools.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" diff --git a/source/source_estate/module_charge/charge_atomic.cpp b/source/source_estate/module_charge/chg_atomic.cpp similarity index 99% rename from source/source_estate/module_charge/charge_atomic.cpp rename to source/source_estate/module_charge/chg_atomic.cpp index 909824b5bb8..499781b96bc 100644 --- a/source/source_estate/module_charge/charge_atomic.cpp +++ b/source/source_estate/module_charge/chg_atomic.cpp @@ -1,4 +1,4 @@ -#include "charge_atomic.h" +#include "chg_atomic.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" diff --git a/source/source_estate/module_charge/charge_atomic.h b/source/source_estate/module_charge/chg_atomic.h similarity index 93% rename from source/source_estate/module_charge/charge_atomic.h rename to source/source_estate/module_charge/chg_atomic.h index de3607f2590..8bd4f48af6e 100644 --- a/source/source_estate/module_charge/charge_atomic.h +++ b/source/source_estate/module_charge/chg_atomic.h @@ -1,5 +1,5 @@ -#ifndef CHARGE_ATOMIC_H -#define CHARGE_ATOMIC_H +#ifndef CHG_ATOMIC_H +#define CHG_ATOMIC_H #include "source_base/complexmatrix.h" #include "source_basis/module_pw/pw_basis.h" @@ -33,4 +33,4 @@ void atomic_rho(const int spin_number_need, } // namespace module_charge -#endif // CHARGE_ATOMIC_H +#endif // CHG_ATOMIC_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 574e9cf537c..0573278fe24 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -103,7 +103,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_charge_test LIBS parameter planewave_serial base device cell_info - SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/charge_atomic.cpp ../module_charge/chg_tools.cpp + SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/chg_atomic.cpp ../module_charge/chg_tools.cpp ) From 2f49955f39b4dd83ca3947c0d93c4ea61abcb143 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 22:45:53 +0800 Subject: [PATCH 29/37] module_charge: extract USPP double-grid split/merge into chg_uspp free functions Introduce module_charge::split_dgrid / merge_dgrid in chg_uspp.{h,cpp} as RAII, parameter-explicit replacements for Charge_Mixing::divide_data / combine_data / clean_data, which paired raw new[] with manual delete[] across ~160 lines of mixing code. - chg_uspp.{h,cpp}: stateless free functions in module_charge namespace; outputs are caller-pre-sized std::vector, no new/delete; parameter validation via WARNING_QUIT; TITLE/timer tags preserved - charge_mixing_rho.cpp: rho and tau double-grid paths switched to the new functions; raw pointer aliases kept for !double_grid so the existing mixing call sites (nspin==1/2/4) are untouched - CMakeLists.txt (source + test): wire chg_uspp.cpp The legacy divide_data/combine_data/clean_data members are not yet removed; that follows in a later step after the test is updated. --- source/source_estate/CMakeLists.txt | 1 + .../module_charge/charge_mixing_rho.cpp | 83 +++++++++-- .../source_estate/module_charge/chg_uspp.cpp | 129 ++++++++++++++++++ source/source_estate/module_charge/chg_uspp.h | 73 ++++++++++ source/source_estate/test/CMakeLists.txt | 2 +- 5 files changed, 273 insertions(+), 15 deletions(-) create mode 100644 source/source_estate/module_charge/chg_uspp.cpp create mode 100644 source/source_estate/module_charge/chg_uspp.h diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 6d3978a1dc3..d307191252b 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -41,6 +41,7 @@ list(APPEND objects module_charge/charge_mixing_rho.cpp module_charge/charge_mixing_uspp.cpp module_charge/chg_symm.cpp + module_charge/chg_uspp.cpp module_charge/chg_symm_detail.cpp rhog_io.cpp write_elecstat_pot.cpp diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 41183f038f3..0a109aca954 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -1,6 +1,7 @@ #include "charge_mixing.h" #include "chg_drho.h" #include "chg_precond.h" +#include "chg_uspp.h" #include "source_io/module_parameter/parameter.h" #include "source_base/timer.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -15,9 +16,17 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) std::complex* rhog_in = nullptr; std::complex* rhog_out = nullptr; + // RAII owners for the smooth / high-frequency parts on the double grid. + // The raw pointers below alias these vectors when double_grid is on, + // or alias chr->rhog[_save][0] directly when double_grid is off so the + // mixing still mutates chr in place. + std::vector> rho_sg_in; + std::vector> rho_sg_out; + std::vector> rho_hf_in; + std::vector> rho_hf_out; // for smooth part - std::complex* rhogs_in = chr->rhog_save[0]; - std::complex* rhogs_out = chr->rhog[0]; + std::complex* rhogs_in = nullptr; + std::complex* rhogs_out = nullptr; // for high_frequency part std::complex* rhoghf_in = nullptr; std::complex* rhoghf_out = nullptr; @@ -25,8 +34,25 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) if ( PARAM.globalv.double_grid) { // divide into smooth part and high_frequency part - divide_data(chr->rhog_save[0], rhogs_in, rhoghf_in); - divide_data(chr->rhog[0], rhogs_out, rhoghf_out); + const int npw_smooth = this->rhopw->npw; + const int npw_dense = this->rhodpw->npw; + rho_sg_in.resize(nspin * npw_smooth); + rho_hf_in.resize(nspin * (npw_dense - npw_smooth)); + rho_sg_out.resize(nspin * npw_smooth); + rho_hf_out.resize(nspin * (npw_dense - npw_smooth)); + module_charge::split_dgrid(chr->rhog_save[0], rho_sg_in, rho_hf_in, + nspin, npw_smooth, npw_dense); + module_charge::split_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, + nspin, npw_smooth, npw_dense); + rhogs_in = rho_sg_in.data(); + rhoghf_in = rho_hf_in.data(); + rhogs_out = rho_sg_out.data(); + rhoghf_out = rho_hf_out.data(); + } + else + { + rhogs_in = chr->rhog_save[0]; + rhogs_out = chr->rhog[0]; } // inner_product_recip_hartree is a hartree-like sum, unit is Ry @@ -185,9 +211,10 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; this->mixing_highf->plain_mix(rhoghf_out, rhoghf_in, rhoghf_out, ndimhf, nullptr); - // combine smooth part and high_frequency part - combine_data(chr->rhog[0], rhogs_out, rhoghf_out); - clean_data(rhogs_in, rhoghf_in); + // combine smooth part and high_frequency part; + // rho_sg_* / rho_hf_* vectors are released automatically at scope exit + module_charge::merge_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, + nspin, this->rhopw->npw, this->rhodpw->npw); } // rhog to rho @@ -217,15 +244,42 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhodpw->real2recip(chr->kin_r[is], &kin_g[is * rhodpw->npw]); rhodpw->real2recip(chr->kin_r_save[is], &kin_g_save[is * rhodpw->npw]); } + // RAII owners for the smooth / high-frequency parts on the double grid; + // raw pointers below alias these vectors when double_grid is on, or + // alias kin_g[_save] directly when double_grid is off so the mixing + // mutates the dense buffer in place. + std::vector> tau_sg_in; + std::vector> tau_sg_out; + std::vector> tau_hf_in; + std::vector> tau_hf_out; // for smooth part, for ! PARAM.globalv.double_grid only have this part - std::complex*taugs_in = kin_g_save.data(), *taugs_out = kin_g.data(); + std::complex* taugs_in = nullptr; + std::complex* taugs_out = nullptr; // for high frequency part - std::complex*taughf_in = nullptr, *taughf_out = nullptr; + std::complex* taughf_in = nullptr; + std::complex* taughf_out = nullptr; if ( PARAM.globalv.double_grid) { // divide into smooth part and high_frequency part - divide_data(kin_g_save.data(), taugs_in, taughf_in); - divide_data(kin_g.data(), taugs_out, taughf_out); + const int npw_smooth = this->rhopw->npw; + const int npw_dense = this->rhodpw->npw; + tau_sg_in.resize(nspin * npw_smooth); + tau_hf_in.resize(nspin * (npw_dense - npw_smooth)); + tau_sg_out.resize(nspin * npw_smooth); + tau_hf_out.resize(nspin * (npw_dense - npw_smooth)); + module_charge::split_dgrid(kin_g_save.data(), tau_sg_in, tau_hf_in, + nspin, npw_smooth, npw_dense); + module_charge::split_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, + nspin, npw_smooth, npw_dense); + taugs_in = tau_sg_in.data(); + taughf_in = tau_hf_in.data(); + taugs_out = tau_sg_out.data(); + taughf_out = tau_hf_out.data(); + } + else + { + taugs_in = kin_g_save.data(); + taugs_out = kin_g.data(); } // Note: there is no kerker modification for tau because I'm not sure @@ -240,9 +294,10 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; this->mixing_highf->plain_mix(taughf_out, taughf_in, taughf_out, ndimhf, nullptr); - // combine smooth part and high_frequency part - combine_data(kin_g.data(), taugs_out, taughf_out); - clean_data(taugs_in, taughf_in); + // combine smooth part and high_frequency part; + // tau_sg_* / tau_hf_* vectors are released automatically at scope exit + module_charge::merge_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, + nspin, this->rhopw->npw, this->rhodpw->npw); } // kin_g to kin_r diff --git a/source/source_estate/module_charge/chg_uspp.cpp b/source/source_estate/module_charge/chg_uspp.cpp new file mode 100644 index 00000000000..fec865212d9 --- /dev/null +++ b/source/source_estate/module_charge/chg_uspp.cpp @@ -0,0 +1,129 @@ +#include "chg_uspp.h" + +#include + +#include "source_base/timer.h" +#include "source_base/tool_title.h" +#include "source_base/tool_quit.h" + +namespace module_charge +{ + +void split_dgrid(const std::complex* data_d, + std::vector>& data_s, + std::vector>& data_hf, + int nspin, + int npw_smooth, + int npw_dense) +{ + ModuleBase::TITLE("module_charge", "split_dgrid"); + ModuleBase::timer::start("module_charge", "split_dgrid"); + + if (data_d == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::split_dgrid", + "input dense data pointer is null"); + } + if (nspin < 1) + { + ModuleBase::WARNING_QUIT("module_charge::split_dgrid", + "nspin must be >= 1"); + } + if (npw_smooth < 0 || npw_dense < npw_smooth) + { + ModuleBase::WARNING_QUIT("module_charge::split_dgrid", + "require npw_smooth >= 0 and npw_dense >= npw_smooth"); + } + + const int npw_hf = npw_dense - npw_smooth; + const std::size_t size_s = nspin * npw_smooth; + const std::size_t size_hf = nspin * npw_hf; + if (data_s.size() != size_s) + { + ModuleBase::WARNING_QUIT("module_charge::split_dgrid", + "data_s size does not match nspin * npw_smooth"); + } + if (data_hf.size() != size_hf) + { + ModuleBase::WARNING_QUIT("module_charge::split_dgrid", + "data_hf size does not match nspin * (npw_dense - npw_smooth)"); + } + + for (int is = 0; is < nspin; ++is) + { + const std::complex* src = data_d + is * npw_dense; + if (npw_smooth > 0) + { + std::memcpy(data_s.data() + is * npw_smooth, src, + npw_smooth * sizeof(std::complex)); + } + if (npw_hf > 0) + { + std::complex* dst = data_hf.data() + is * npw_hf; + std::memcpy(dst, src + npw_smooth, + npw_hf * sizeof(std::complex)); + } + } + + ModuleBase::timer::end("module_charge", "split_dgrid"); +} + +void merge_dgrid(std::complex* data_d, + const std::vector>& data_s, + const std::vector>& data_hf, + int nspin, + int npw_smooth, + int npw_dense) +{ + ModuleBase::TITLE("module_charge", "merge_dgrid"); + ModuleBase::timer::start("module_charge", "merge_dgrid"); + + if (data_d == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::merge_dgrid", + "output dense data pointer is null"); + } + if (nspin < 1) + { + ModuleBase::WARNING_QUIT("module_charge::merge_dgrid", + "nspin must be >= 1"); + } + if (npw_smooth < 0 || npw_dense < npw_smooth) + { + ModuleBase::WARNING_QUIT("module_charge::merge_dgrid", + "require npw_smooth >= 0 and npw_dense >= npw_smooth"); + } + + const int npw_hf = npw_dense - npw_smooth; + const std::size_t size_s = nspin * npw_smooth; + const std::size_t size_hf = nspin * npw_hf; + if (data_s.size() != size_s) + { + ModuleBase::WARNING_QUIT("module_charge::merge_dgrid", + "data_s size does not match nspin * npw_smooth"); + } + if (data_hf.size() != size_hf) + { + ModuleBase::WARNING_QUIT("module_charge::merge_dgrid", + "data_hf size does not match nspin * (npw_dense - npw_smooth)"); + } + + for (int is = 0; is < nspin; ++is) + { + std::complex* dst = data_d + is * npw_dense; + if (npw_smooth > 0) + { + std::memcpy(dst, data_s.data() + is * npw_smooth, + npw_smooth * sizeof(std::complex)); + } + if (npw_hf > 0) + { + std::memcpy(dst + npw_smooth, data_hf.data() + is * npw_hf, + npw_hf * sizeof(std::complex)); + } + } + + ModuleBase::timer::end("module_charge", "merge_dgrid"); +} + +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_uspp.h b/source/source_estate/module_charge/chg_uspp.h new file mode 100644 index 00000000000..09196bbbacb --- /dev/null +++ b/source/source_estate/module_charge/chg_uspp.h @@ -0,0 +1,73 @@ +#ifndef CHG_USPP_H +#define CHG_USPP_H + +// Stateless double-grid split/merge helpers extracted from Charge_Mixing. +// +// "dgrid" = double grid, the dense/smooth grid pair used by ultrasoft (USPP) +// and PAW calculations to capture the high-frequency tail of the +// augmentation charge beyond the smooth (soft) plane-wave grid. The dense +// grid (npw_dense) is the union of the smooth grid (npw_smooth) and the +// high-frequency tail (npw_dense - npw_smooth). +// +// These functions do not read Charge_Mixing members or PARAM/GlobalV; all +// inputs are passed explicitly. Memory is managed by the caller through +// std::vector, so no new/delete pair is needed and no clean-up function +// exists. + +#include +#include + +namespace module_charge +{ + +/** + * @brief Split dense reciprocal-space data into smooth and high-frequency + * parts on the USPP double grid. + * + * For each spin channel, the first npw_smooth entries of data_d are copied + * into data_s and the remaining (npw_dense - npw_smooth) entries are copied + * into data_hf. No aliasing is performed: both output vectors own their + * storage and must be pre-sized by the caller. + * + * @param data_d dense input, shape [nspin * npw_dense], non-null + * @param data_s smooth output, pre-sized to nspin * npw_smooth + * @param data_hf high-frequency output, pre-sized to + * nspin * (npw_dense - npw_smooth); zero-size is allowed + * when npw_dense == npw_smooth + * @param nspin number of spin channels, >= 1 + * @param npw_smooth smooth grid npw, >= 0 + * @param npw_dense dense grid npw, >= npw_smooth + */ +void split_dgrid(const std::complex* data_d, + std::vector>& data_s, + std::vector>& data_hf, + int nspin, + int npw_smooth, + int npw_dense); + +/** + * @brief Merge smooth and high-frequency parts back into dense reciprocal- + * space data. Inverse of split_dgrid. + * + * Vectors are not cleared; the caller may reuse them or let them go out of + * scope. The output data_d must be pre-allocated by the caller with size + * nspin * npw_dense. + * + * @param data_d dense output, shape [nspin * npw_dense], non-null + * @param data_s smooth input, sized to nspin * npw_smooth + * @param data_hf high-frequency input, sized to + * nspin * (npw_dense - npw_smooth) + * @param nspin number of spin channels, >= 1 + * @param npw_smooth smooth grid npw, >= 0 + * @param npw_dense dense grid npw, >= npw_smooth + */ +void merge_dgrid(std::complex* data_d, + const std::vector>& data_s, + const std::vector>& data_hf, + int nspin, + int npw_smooth, + int npw_dense); + +} // namespace module_charge + +#endif // CHG_USPP_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 0573278fe24..dffad703fb1 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -113,7 +113,7 @@ AddTest( SOURCES charge_mixing_test.cpp ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_drho.cpp ../module_charge/chg_precond.cpp ../module_charge/charge_mixing_rho.cpp - ../module_charge/charge_mixing_uspp.cpp + ../module_charge/charge_mixing_uspp.cpp ../module_charge/chg_uspp.cpp ) AddTest( From 4650306660aa13cf2a812626bf6643c56485761d Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 22:47:39 +0800 Subject: [PATCH 30/37] module_charge: rewrite MixDivCombTest for the new split_dgrid/merge_dgrid Drop the legacy alias-pointer assertions (EXPECT_EQ(datas, data.data()), EXPECT_EQ(datas, nullptr) after clean_data) that coupled the test to the old new[]/delete[] ownership model. The rewritten case verifies the actual contract: - split_dgrid fills smooth and high-frequency buffers with the dense data verbatim (per-element comparison) - merge_dgrid is a left-inverse of split_dgrid (output == input) - no explicit cleanup call is required: std::vector manages storage Covers nspin == 1 and nspin == 2 paths. --- .../source_estate/test/charge_mixing_test.cpp | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index cc793c41445..39e583f5e2a 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -8,6 +8,7 @@ #include "../module_charge/chg_drho.h" #include "../module_charge/chg_drho_detail.h" #include "../module_charge/chg_precond.h" +#include "../module_charge/chg_uspp.h" #include "source_base/module_mixing/broyden_mixing.h" #include "source_basis/module_pw/pw_basis.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -66,10 +67,9 @@ void Charge::set_rhopw(ModulePW::PW_Basis* rhopw_in) * Charge_Mixing::mix_rho_recip(chr) * Charge_Mixing::mix_rho_real(chr) * - mix rho with different methods - * - MixDivCombTest: Charge_Mixing::divide_data - * Charge_Mixing::combine_data - * Charge_Mixing::clean_data - * - divide and combine data + * - MixDivCombTest: module_charge::split_dgrid + * module_charge::merge_dgrid + * - divide and combine data on the USPP double grid * */ @@ -883,37 +883,49 @@ TEST_F(ChargeMixingTest, MixDivCombTest) Charge_Mixing CMtest; CMtest.set_rhopw(&pw_basis, &pw_dbasis); std::vector> data(pw_dbasis.npw, 1.0); - std::complex*datas, *datahf; - std::complex*datas2, *datahf2; - CMtest.divide_data(data.data(), datas, datahf); - EXPECT_EQ(datas, data.data()); - EXPECT_EQ(datahf, data.data() + pw_basis.npw); - CMtest.combine_data(data.data(), datas, datahf); - EXPECT_EQ(datas, nullptr); - EXPECT_EQ(datahf, nullptr); - - CMtest.divide_data(data.data(), datas2, datahf2); - CMtest.clean_data(datas2, datahf2); - EXPECT_EQ(datas2, nullptr); - EXPECT_EQ(datahf2, nullptr); + const int npw_smooth = pw_basis.npw; + const int npw_dense = pw_dbasis.npw; + const int npw_hf = npw_dense - npw_smooth; + + // split: smooth + high-frequency together reconstruct the dense data + std::vector> datas(npw_smooth); + std::vector> datahf(npw_hf); + module_charge::split_dgrid(data.data(), datas, datahf, + 1, npw_smooth, npw_dense); + for (int i = 0; i < npw_smooth; ++i) + { + EXPECT_EQ(datas[i], data[i]); + } + for (int i = 0; i < npw_hf; ++i) + { + EXPECT_EQ(datahf[i], data[npw_smooth + i]); + } + + // merge: inverse of split; output must equal input + std::vector> dataout(npw_dense, std::complex(0, 0)); + module_charge::merge_dgrid(dataout.data(), datas, datahf, + 1, npw_smooth, npw_dense); + for (int i = 0; i < npw_dense; ++i) + { + EXPECT_EQ(dataout[i], data[i]); + } + + // No explicit cleanup call needed: vectors manage their own storage. // NSPIN = 2 PARAM.input.nspin = 2; - data.resize(pw_dbasis.npw * 2, 1.0); - std::vector> dataout(pw_dbasis.npw * 2, 1.0); - CMtest.divide_data(data.data(), datas, datahf); - CMtest.combine_data(dataout.data(), datas, datahf); - EXPECT_EQ(datas, nullptr); - EXPECT_EQ(datahf, nullptr); - for (int i = 0; i < pw_dbasis.npw * 2; ++i) + data.resize(npw_dense * 2, 1.0); + dataout.assign(npw_dense * 2, std::complex(0, 0)); + std::vector> datas2(npw_smooth * 2); + std::vector> datahf2(npw_hf * 2); + module_charge::split_dgrid(data.data(), datas2, datahf2, + 2, npw_smooth, npw_dense); + module_charge::merge_dgrid(dataout.data(), datas2, datahf2, + 2, npw_smooth, npw_dense); + for (int i = 0; i < npw_dense * 2; ++i) { EXPECT_EQ(dataout[i], data[i]); } - - CMtest.divide_data(data.data(), datas2, datahf2); - CMtest.clean_data(datas2, datahf2); - EXPECT_EQ(datas2, nullptr); - EXPECT_EQ(datahf2, nullptr); } TEST_F(ChargeMixingTest, SCFOscillationTest) From 2670f86fe1c02d25a1f675dacc600a0752d2fa81 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Thu, 17 Sep 2026 22:52:10 +0800 Subject: [PATCH 31/37] module_charge: drop legacy divide_data/combine_data/clean_data members With the new module_charge::split_dgrid/merge_dgrid in chg_uspp.{h,cpp} and all call sites in charge_mixing_rho.cpp migrated, the original Charge_Mixing::divide_data / combine_data / clean_data members are dead. - delete charge_mixing_uspp.cpp (the raw new[]/delete[] implementation) - drop the three member declarations from charge_mixing.h - remove charge_mixing_uspp.cpp from source/test CMakeLists.txt - Makefile.Objects: drop charge_mixing_uspp.o, add chg_uspp.o - refresh one stale comment in charge_mixing_rho.cpp to reference merge_dgrid instead of the removed combine_data --- source/Makefile.Objects | 2 +- source/source_estate/CMakeLists.txt | 1 - .../module_charge/charge_mixing.h | 25 ------ .../module_charge/charge_mixing_rho.cpp | 2 +- .../module_charge/charge_mixing_uspp.cpp | 76 ------------------- source/source_estate/test/CMakeLists.txt | 2 +- 6 files changed, 3 insertions(+), 105 deletions(-) delete mode 100644 source/source_estate/module_charge/charge_mixing_uspp.cpp diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 3bf32975f4d..6f701a5552f 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -836,9 +836,9 @@ OBJS_SRCPW=h_ewald_pw.o\ chg_drho.o\ chg_precond.o\ charge_mixing_rho.o\ - charge_mixing_uspp.o\ chg_symm.o\ chg_symm_detail.o\ + chg_uspp.o\ rhog_io.o\ write_elecstat_pot.o\ write_init.o\ diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index d307191252b..d740e886841 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -39,7 +39,6 @@ list(APPEND objects module_charge/chg_drho.cpp module_charge/chg_precond.cpp module_charge/charge_mixing_rho.cpp - module_charge/charge_mixing_uspp.cpp module_charge/chg_symm.cpp module_charge/chg_uspp.cpp module_charge/chg_symm_detail.cpp diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index ddf05c83aee..bdde48e97dc 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -180,31 +180,6 @@ class Charge_Mixing } }; } - - /** - * @brief divide rho/tau to smooth and high frequency parts - * @param data_d dense data - * @param data_s smooth data - * @param data_hf high frequency data = dense data - smooth data - * - */ - void divide_data(std::complex* data_d, std::complex*& data_s, std::complex*& data_hf); - /** - * @brief gather smooth and high frequency parts to rho/tau - * @param data_d dense data - * @param data_s smooth data - * @param data_hf high frequency data = dense data - smooth data - * - */ - void combine_data(std::complex* data_d, std::complex*& data_s, std::complex*& data_hf); - /** - * @brief clean smooth and high frequency parts - * @param data_d dense data - * @param data_s smooth data - * @param data_hf high frequency data = dense data - smooth data - * - */ - void clean_data(std::complex*& data_s, std::complex*& data_hf); }; #endif diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 0a109aca954..957f43db606 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -113,7 +113,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) chr->rhog[0][ig] = 0.5 * (rhog_mag[ig] + rhog_mag[ig+npw]); chr->rhog[1][ig] = 0.5 * (rhog_mag[ig] - rhog_mag[ig+npw]); } - // get rhogs_out for combine_data() + // sync rhogs_out so merge_dgrid has the latest smooth part if ( PARAM.globalv.double_grid) { for (int ig = 0; ig < npw; ig++) diff --git a/source/source_estate/module_charge/charge_mixing_uspp.cpp b/source/source_estate/module_charge/charge_mixing_uspp.cpp deleted file mode 100644 index 10968cd16e6..00000000000 --- a/source/source_estate/module_charge/charge_mixing_uspp.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "charge_mixing.h" -#include "source_io/module_parameter/parameter.h" - -void Charge_Mixing::divide_data(std::complex* data_d, - std::complex*& data_s, - std::complex*& data_hf) -{ - ModuleBase::TITLE("Charge_Mixing", "divide_data"); - if (PARAM.inp.nspin == 1) - { - data_s = data_d; - data_hf = data_d + this->rhopw->npw; - } - else - { - const int ndimd = this->rhodpw->npw; - const int ndims = this->rhopw->npw; - const int ndimhf = ndimd - ndims; - data_s = new std::complex[PARAM.inp.nspin * ndims]; - data_hf = nullptr; - if (ndimhf > 0) - { - data_hf = new std::complex[PARAM.inp.nspin * ndimhf]; - } - for (int is = 0; is < PARAM.inp.nspin; ++is) - { - std::memcpy(data_s + is * ndims, data_d + is * ndimd, ndims * sizeof(std::complex)); - std::memcpy(data_hf + is * ndimhf, data_d + is * ndimd + ndims, ndimhf * sizeof(std::complex)); - } - } -} -void Charge_Mixing::combine_data(std::complex* data_d, - std::complex*& data_s, - std::complex*& data_hf) -{ - ModuleBase::TITLE("Charge_Mixing", "combine_data"); - if (PARAM.inp.nspin == 1) - { - data_s = nullptr; - data_hf = nullptr; - return; - } - else - { - const int ndimd = this->rhodpw->npw; - const int ndims = this->rhopw->npw; - const int ndimhf = ndimd - ndims; - for (int is = 0; is < PARAM.inp.nspin; ++is) - { - std::memcpy(data_d + is * ndimd, data_s + is * ndims, ndims * sizeof(std::complex)); - std::memcpy(data_d + is * ndimd + ndims, data_hf + is * ndimhf, ndimhf * sizeof(std::complex)); - } - delete[] data_s; - delete[] data_hf; - data_s = nullptr; - data_hf = nullptr; - } -} - -void Charge_Mixing::clean_data(std::complex*& data_s, std::complex*& data_hf) -{ - ModuleBase::TITLE("Charge_Mixing", "clean_data"); - if (PARAM.inp.nspin == 1) - { - data_s = nullptr; - data_hf = nullptr; - return; - } - else - { - delete[] data_s; - delete[] data_hf; - data_s = nullptr; - data_hf = nullptr; - } -} \ No newline at end of file diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index dffad703fb1..2e569cc56d3 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -113,7 +113,7 @@ AddTest( SOURCES charge_mixing_test.cpp ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_drho.cpp ../module_charge/chg_precond.cpp ../module_charge/charge_mixing_rho.cpp - ../module_charge/charge_mixing_uspp.cpp ../module_charge/chg_uspp.cpp + ../module_charge/chg_uspp.cpp ) AddTest( From e4f111f7ddd4d69c58092da37cd0d3bb324990c8 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 08:27:17 +0800 Subject: [PATCH 32/37] module_charge: rename charge_extra files to chg_extra and move class into namespace Rename charge_extra.h/cpp to chg_extra.h/cpp and wrap the Charge_Extra class in the module_charge namespace, matching the rest of module_charge (chg_atomic, chg_symm, chg_uspp). Update include guards, call sites in esolver_fp.h and the unit test, and CMake/Makefile source lists. --- source/Makefile.Objects | 2 +- source/source_esolver/esolver_fp.h | 4 ++-- source/source_estate/CMakeLists.txt | 2 +- .../module_charge/{charge_extra.cpp => chg_extra.cpp} | 7 ++++++- .../module_charge/{charge_extra.h => chg_extra.h} | 9 +++++++-- source/source_estate/test/CMakeLists.txt | 2 +- source/source_estate/test/charge_extra_test.cpp | 6 +++--- 7 files changed, 21 insertions(+), 11 deletions(-) rename source/source_estate/module_charge/{charge_extra.cpp => chg_extra.cpp} (99%) rename source/source_estate/module_charge/{charge_extra.h => chg_extra.h} (97%) diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 6f701a5552f..76e3b754564 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -830,7 +830,7 @@ OBJS_SRCPW=h_ewald_pw.o\ charge_init.o\ chg_tools.o\ chg_parallel.o\ - charge_extra.o\ + chg_extra.o\ charge_mixing.o\ charge_mixing_dmr.o\ chg_drho.o\ diff --git a/source/source_esolver/esolver_fp.h b/source/source_esolver/esolver_fp.h index 378f92f560c..eb8cef44280 100644 --- a/source/source_esolver/esolver_fp.h +++ b/source/source_esolver/esolver_fp.h @@ -5,7 +5,7 @@ #include "source_base/timer_wrapper.h" #include "source_basis/module_pw/pw_basis.h" // plane wave basis #include "source_estate/elecstate.h" // electronic states -#include "source_estate/module_charge/charge_extra.h" // charge extrapolation +#include "source_estate/module_charge/chg_extra.h" // charge extrapolation #include "source_hamilt/module_surchem/surchem.h" // solvation model #include "source_base/parallel_grid.h" // Parallel_Grid (value member below) #include "source_pw/module_pwdft/stru_fac.h" // structure factor @@ -81,7 +81,7 @@ class ESolver_FP : public ESolver pseudopot_cell_vl locpp; //! charge extrapolation method - Charge_Extra CE; + module_charge::Charge_Extra CE; //! solvent model surchem solvent; diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index d740e886841..11b5cd2353d 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -33,7 +33,7 @@ list(APPEND objects module_charge/charge_init.cpp module_charge/chg_tools.cpp module_charge/chg_parallel.cpp - module_charge/charge_extra.cpp + module_charge/chg_extra.cpp module_charge/charge_mixing.cpp module_charge/charge_mixing_dmr.cpp module_charge/chg_drho.cpp diff --git a/source/source_estate/module_charge/charge_extra.cpp b/source/source_estate/module_charge/chg_extra.cpp similarity index 99% rename from source/source_estate/module_charge/charge_extra.cpp rename to source/source_estate/module_charge/chg_extra.cpp index 4a4299462e0..07803b9f826 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/chg_extra.cpp @@ -1,4 +1,4 @@ -#include "charge_extra.h" +#include "chg_extra.h" #include "chg_atomic.h" @@ -8,6 +8,9 @@ #include "source_base/tool_threading.h" #include "source_io/module_output/cube_io.h" +namespace module_charge +{ + Charge_Extra::Charge_Extra() { } @@ -311,3 +314,5 @@ void Charge_Extra::update_delta_rho(const UnitCell& ucell, const Charge* chr, co delete[] rho_atom; return; } + +} // namespace module_charge diff --git a/source/source_estate/module_charge/charge_extra.h b/source/source_estate/module_charge/chg_extra.h similarity index 97% rename from source/source_estate/module_charge/charge_extra.h rename to source/source_estate/module_charge/chg_extra.h index 4b506d643d8..4f44a583eca 100644 --- a/source/source_estate/module_charge/charge_extra.h +++ b/source/source_estate/module_charge/chg_extra.h @@ -1,5 +1,5 @@ -#ifndef CHARGE_EXTRA_H -#define CHARGE_EXTRA_H +#ifndef CHG_EXTRA_H +#define CHG_EXTRA_H #include @@ -10,6 +10,9 @@ #include "source_base/parallel_grid.h" #endif +namespace module_charge +{ + /** * @brief charge extrapolation method * @@ -123,4 +126,6 @@ class Charge_Extra void find_alpha_and_beta(const int& natom, std::ofstream& ofs_running, std::ofstream& ofs_warning); }; +} // namespace module_charge + #endif diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 2e569cc56d3..641cd2969e6 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -119,7 +119,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/chg_extra.cpp ../../source_io/module_output/read_cube.cpp ../../source_io/module_output/write_cube.cpp ../../source_base/module_fft/fft_bundle.cpp ../../source_base/module_fft/fft_cpu.cpp ) diff --git a/source/source_estate/test/charge_extra_test.cpp b/source/source_estate/test/charge_extra_test.cpp index 30a13f48870..3486cd819b1 100644 --- a/source/source_estate/test/charge_extra_test.cpp +++ b/source/source_estate/test/charge_extra_test.cpp @@ -3,7 +3,7 @@ #define private public #define protected public #include "source_io/module_parameter/parameter.h" -#include "source_estate/module_charge/charge_extra.h" +#include "source_estate/module_charge/chg_extra.h" #include "prepare_unitcell.h" #include "source_base/module_fft/fft_bundle.h" #undef private @@ -91,7 +91,7 @@ void Structure_Factor::setup(const UnitCell*, const Parallel_Grid&, const Module } /************************************************ - * unit test of module_charge/charge_extra.cpp + * unit test of module_charge/chg_extra.cpp ***********************************************/ /** @@ -109,7 +109,7 @@ void Structure_Factor::setup(const UnitCell*, const Parallel_Grid&, const Module class ChargeExtraTest : public ::testing::Test { protected: - Charge_Extra CE; + module_charge::Charge_Extra CE; UcellTestPrepare utp = UcellTestLib["Si"]; std::unique_ptr ucell; Parallel_Grid* pgrid = nullptr; From 570dd49a26c76be972c7e571fa96001306e59c28 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 08:44:39 +0800 Subject: [PATCH 33/37] module_charge: extract DMR mixing into chg_dmr free functions Move the DMR allocation/mixing logic out of Charge_Mixing members into stateless module_charge functions (init_mixing_dmr, template mix_dmr with explicit instantiation), passing the Mixing object, mixing data and MixingConfig explicitly instead of reading PARAM. Merge the two identical real/complex mix_dmr overloads, replace raw new[]/delete[] of the magnetic buffers with std::vector, and de-duplicate the two-beta mixing lambda into a file-local helper. The members stay as thin timer-wrapped wrappers so external call sites are unchanged. --- source/source_estate/CMakeLists.txt | 1 + .../module_charge/charge_mixing_dmr.cpp | 202 +----------------- .../source_estate/module_charge/chg_dmr.cpp | 174 +++++++++++++++ source/source_estate/module_charge/chg_dmr.h | 66 ++++++ source/source_estate/test/CMakeLists.txt | 3 +- 5 files changed, 249 insertions(+), 197 deletions(-) create mode 100644 source/source_estate/module_charge/chg_dmr.cpp create mode 100644 source/source_estate/module_charge/chg_dmr.h diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 11b5cd2353d..ae89828edb4 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -36,6 +36,7 @@ list(APPEND objects module_charge/chg_extra.cpp module_charge/charge_mixing.cpp module_charge/charge_mixing_dmr.cpp + module_charge/chg_dmr.cpp module_charge/chg_drho.cpp module_charge/chg_precond.cpp module_charge/charge_mixing_rho.cpp diff --git a/source/source_estate/module_charge/charge_mixing_dmr.cpp b/source/source_estate/module_charge/charge_mixing_dmr.cpp index 01e409264fe..7359cf68939 100644 --- a/source/source_estate/module_charge/charge_mixing_dmr.cpp +++ b/source/source_estate/module_charge/charge_mixing_dmr.cpp @@ -1,28 +1,14 @@ #include "charge_mixing.h" +#include "chg_dmr.h" -#include "source_io/module_parameter/parameter.h" #include "source_base/timer.h" +#include "source_base/tool_title.h" void Charge_Mixing::allocate_mixing_dmr(const int nnr) { - // Note that: we cannot allocate memory for dmr_mdata in set_mixing. - // since the size of dmr_mdata is given by the size of HContainer.nnr, which is calculated in DensityMatrix::init_DMR(). - // and DensityMatrix::init_DMR() is called in beforescf(). While set_mixing() is called in ESolver_KS::Init(). ModuleBase::TITLE("Charge_Mixing", "allocate_mixing_dmr"); ModuleBase::timer::start("Charge_Mixing", "allocate_mixing_dmr"); - // - const int dmr_nspin = (PARAM.inp.nspin == 2) ? 2 : 1; - // allocate memory for dmr_mdata - if (PARAM.inp.scf_thr_type == 1) - { - ModuleBase::WARNING_QUIT("Charge_Mixing", "This Mixing of Density Matrix is not supported for PW basis yet"); - } - else if (PARAM.inp.scf_thr_type == 2) - { - this->mixing->init_mixing_data(this->dmr_mdata, nnr * dmr_nspin, sizeof(double)); - } - - this->dmr_mdata.reset(); + module_charge::init_mixing_dmr(this->mixing, this->dmr_mdata, nnr, this->cfg_); ModuleBase::timer::end("Charge_Mixing", "allocate_mixing_dmr"); return; @@ -30,97 +16,9 @@ void Charge_Mixing::allocate_mixing_dmr(const int nnr) void Charge_Mixing::mix_dmr(elecstate::DensityMatrix* DM) { - // Notice that DensityMatrix object is a Template class ModuleBase::TITLE("Charge_Mixing", "mix_dmr"); ModuleBase::timer::start("Charge_Mixing", "mix_dmr"); - // - std::vector*> dmr = DM->get_DMR_vector(); - std::vector>& dmr_save = DM->get_DMR_save(); - // - //const int dmr_nspin = (PARAM.inp.nspin == 2) ? 2 : 1; - double* dmr_in = nullptr; - double* dmr_out = nullptr; - if (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 4) - { - dmr_in = dmr_save[0].data(); - dmr_out = dmr[0]->get_wrapper(); - this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, false); - this->mixing->mix_data(this->dmr_mdata, dmr_out); - } - else if (PARAM.inp.nspin == 2) - { - // magnetic density matrix - double* dmr_mag = nullptr; - double* dmr_mag_save = nullptr; - const int nnr = dmr[0]->get_nnr(); - // allocate dmr_mag[is*nnrx] and dmr_mag_save[is*nnrx] - dmr_mag = new double[nnr * PARAM.inp.nspin]; - dmr_mag_save = new double[nnr * PARAM.inp.nspin]; - ModuleBase::GlobalFunc::ZEROS(dmr_mag, nnr * PARAM.inp.nspin); - ModuleBase::GlobalFunc::ZEROS(dmr_mag_save, nnr * PARAM.inp.nspin); - double* dmr_up = nullptr; - double* dmr_down = nullptr; - // tranfer dmr into dmr_mag - dmr_up = dmr[0]->get_wrapper(); - dmr_down = dmr[1]->get_wrapper(); - for (int ir = 0; ir < nnr; ir++) - { - dmr_mag[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag[ir + nnr] = dmr_up[ir] - dmr_down[ir]; - } - // tranfer dmr_save into dmr_mag_save - dmr_up = dmr_save[0].data(); - dmr_down = dmr_save[1].data(); - for (int ir = 0; ir < nnr; ir++) - { - dmr_mag_save[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag_save[ir + nnr] = dmr_up[ir] - dmr_down[ir]; - } - // - dmr_in = dmr_mag_save; - dmr_out = dmr_mag; - // no kerker in mixing_dmr - //auto screen = [this](double* p) { - // module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - //}; - auto twobeta_mix - = [this, nnr](double* out, const double* in, const double* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nnr; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nnr; i < 2 * nnr; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; - this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, twobeta_mix, false); - this->mixing->mix_data(this->dmr_mdata, dmr_out); - // get new dmr from dmr_mag - dmr_up = dmr[0]->get_wrapper(); - dmr_down = dmr[1]->get_wrapper(); - for (int is = 0; is < PARAM.inp.nspin; is++) - { - ModuleBase::GlobalFunc::ZEROS(dmr_up, nnr); - ModuleBase::GlobalFunc::ZEROS(dmr_down, nnr); - } - for (int ir = 0; ir < nnr; ir++) - { - dmr_up[ir] = 0.5 * (dmr_mag[ir] + dmr_mag[ir+nnr]); - dmr_down[ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir+nnr]); - } - // delete - delete[] dmr_mag; - delete[] dmr_mag_save; - } - + module_charge::mix_dmr(DM, this->mixing, this->dmr_mdata, this->cfg_); ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); return; @@ -128,98 +26,10 @@ void Charge_Mixing::mix_dmr(elecstate::DensityMatrix* DM) void Charge_Mixing::mix_dmr(elecstate::DensityMatrix, double>* DM) { - // Notice that DensityMatrix object is a Template class ModuleBase::TITLE("Charge_Mixing", "mix_dmr"); ModuleBase::timer::start("Charge_Mixing", "mix_dmr"); - // - std::vector*> dmr = DM->get_DMR_vector(); - std::vector>& dmr_save = DM->get_DMR_save(); - // - //const int dmr_nspin = (PARAM.inp.nspin == 2) ? 2 : 1; - double* dmr_in = nullptr; - double* dmr_out = nullptr; - if (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 4) - { - dmr_in = dmr_save[0].data(); - dmr_out = dmr[0]->get_wrapper(); - this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, false); - this->mixing->mix_data(this->dmr_mdata, dmr_out); - } - else if (PARAM.inp.nspin == 2) - { - // magnetic density matrix - double* dmr_mag = nullptr; - double* dmr_mag_save = nullptr; - const int nnr = dmr[0]->get_nnr(); - // allocate dmr_mag[is*nnrx] and dmr_mag_save[is*nnrx] - dmr_mag = new double[nnr * PARAM.inp.nspin]; - dmr_mag_save = new double[nnr * PARAM.inp.nspin]; - ModuleBase::GlobalFunc::ZEROS(dmr_mag, nnr * PARAM.inp.nspin); - ModuleBase::GlobalFunc::ZEROS(dmr_mag_save, nnr * PARAM.inp.nspin); - double* dmr_up = nullptr; - double* dmr_down = nullptr; - // tranfer dmr into dmr_mag - dmr_up = dmr[0]->get_wrapper(); - dmr_down = dmr[1]->get_wrapper(); - for (int ir = 0; ir < nnr; ir++) - { - dmr_mag[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag[ir + nnr] = dmr_up[ir] - dmr_down[ir]; - } - // tranfer dmr_save into dmr_mag_save - dmr_up = dmr_save[0].data(); - dmr_down = dmr_save[1].data(); - for (int ir = 0; ir < nnr; ir++) - { - dmr_mag_save[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag_save[ir + nnr] = dmr_up[ir] - dmr_down[ir]; - } - // - dmr_in = dmr_mag_save; - dmr_out = dmr_mag; - // no kerker in mixing_dmr - //auto screen = [this](double* p) { - // module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - //}; - auto twobeta_mix - = [this, nnr](double* out, const double* in, const double* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nnr; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } - // magnetism -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nnr; i < 2 * nnr; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; - this->mixing->push_data(this->dmr_mdata, dmr_in, dmr_out, nullptr, twobeta_mix, false); - this->mixing->mix_data(this->dmr_mdata, dmr_out); - // get new dmr from dmr_mag - dmr_up = dmr[0]->get_wrapper(); - dmr_down = dmr[1]->get_wrapper(); - for (int is = 0; is < PARAM.inp.nspin; is++) - { - ModuleBase::GlobalFunc::ZEROS(dmr_up, nnr); - ModuleBase::GlobalFunc::ZEROS(dmr_down, nnr); - } - for (int ir = 0; ir < nnr; ir++) - { - dmr_up[ir] = 0.5 * (dmr_mag[ir] + dmr_mag[ir+nnr]); - dmr_down[ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir+nnr]); - } - // delete - delete[] dmr_mag; - delete[] dmr_mag_save; - } - + module_charge::mix_dmr(DM, this->mixing, this->dmr_mdata, this->cfg_); ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); return; -} \ No newline at end of file +} diff --git a/source/source_estate/module_charge/chg_dmr.cpp b/source/source_estate/module_charge/chg_dmr.cpp new file mode 100644 index 00000000000..f6f6953c8b0 --- /dev/null +++ b/source/source_estate/module_charge/chg_dmr.cpp @@ -0,0 +1,174 @@ +#include "chg_dmr.h" + +#include +#include + +#include "source_base/global_function.h" +#include "source_base/module_mixing/mixing.h" +#include "source_base/tool_quit.h" +#include "source_estate/module_dm/density_matrix.h" + +namespace module_charge +{ +namespace +{ + +/** + * @brief Two-beta plain step for the magnetic density matrix. + * + * The first nnr elements are the charge channel (beta), the next nnr + * elements are the magnetization channel (beta_mag). + * + * @param out mixed output, length 2 * nnr + * @param in mixed input, length 2 * nnr + * @param sres residual, length 2 * nnr + * @param nnr number of DMR elements per spin channel + * @param beta mixing beta for the charge channel + * @param beta_mag mixing beta for the magnetization channel + */ +void twobeta_step(double* out, + const double* in, + const double* sres, + const int nnr, + const double beta, + const double beta_mag) +{ +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = 0; i < nnr; ++i) + { + out[i] = in[i] + beta * sres[i]; + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = nnr; i < 2 * nnr; ++i) + { + out[i] = in[i] + beta_mag * sres[i]; + } +} + +} // namespace + +void init_mixing_dmr(Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const int nnr, + const MixingConfig& cfg) +{ + if (mixing == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::init_mixing_dmr", "mixing pointer is null"); + } + if (nnr <= 0) + { + ModuleBase::WARNING_QUIT("module_charge::init_mixing_dmr", "nnr must be > 0"); + } + + const int dmr_nspin = (cfg.nspin == 2) ? 2 : 1; + // DMR mixing currently supports only the real-space convergence threshold. + if (cfg.scf_thr_type == 1) + { + ModuleBase::WARNING_QUIT("module_charge::init_mixing_dmr", + "This Mixing of Density Matrix is not supported for PW basis yet"); + } + else if (cfg.scf_thr_type == 2) + { + mixing->init_mixing_data(mdata, nnr * dmr_nspin, sizeof(double)); + } + + // Clear the history counters while keeping the allocated storage. + mdata.reset(); +} + +template +void mix_dmr(elecstate::DensityMatrix* dm, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const MixingConfig& cfg) +{ + if (dm == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "density matrix pointer is null"); + } + if (mixing == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "mixing pointer is null"); + } + if (cfg.nspin != 1 && cfg.nspin != 2 && cfg.nspin != 4) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "unsupported nspin, require 1, 2 or 4"); + } + + std::vector*> dmr = dm->get_DMR_vector(); + std::vector>& dmr_save = dm->get_DMR_save(); + + double* dmr_in = nullptr; + double* dmr_out = nullptr; + if (cfg.nspin == 1 || cfg.nspin == 4) + { + dmr_in = dmr_save[0].data(); + dmr_out = dmr[0]->get_wrapper(); + mixing->push_data(mdata, dmr_in, dmr_out, nullptr, false); + mixing->mix_data(mdata, dmr_out); + } + else // cfg.nspin == 2 + { + // Magnetic density matrix: up/down channels are transformed into + // charge/magnetization channels before mixing and back afterwards. + const int nnr = dmr[0]->get_nnr(); + std::vector dmr_mag(nnr * cfg.nspin, 0.0); + std::vector dmr_mag_save(nnr * cfg.nspin, 0.0); + + // Transfer the current DMR into the charge/magnetization layout. + double* dmr_up = dmr[0]->get_wrapper(); + double* dmr_down = dmr[1]->get_wrapper(); + for (int ir = 0; ir < nnr; ++ir) + { + dmr_mag[ir] = dmr_up[ir] + dmr_down[ir]; + dmr_mag[ir + nnr] = dmr_up[ir] - dmr_down[ir]; + } + // Transfer the saved DMR into the charge/magnetization layout. + dmr_up = dmr_save[0].data(); + dmr_down = dmr_save[1].data(); + for (int ir = 0; ir < nnr; ++ir) + { + dmr_mag_save[ir] = dmr_up[ir] + dmr_down[ir]; + dmr_mag_save[ir + nnr] = dmr_up[ir] - dmr_down[ir]; + } + + dmr_in = dmr_mag_save.data(); + dmr_out = dmr_mag.data(); + const double beta = cfg.mixing_beta; + const double beta_mag = cfg.mixing_beta_mag; + std::function twobeta + = [nnr, beta, beta_mag](double* out, const double* in, const double* sres) { + twobeta_step(out, in, sres, nnr, beta, beta_mag); + }; + // No Kerker screening in DMR mixing. + mixing->push_data(mdata, dmr_in, dmr_out, nullptr, twobeta, false); + mixing->mix_data(mdata, dmr_out); + + // Transform the mixed charge/magnetization channels back to up/down. + dmr_up = dmr[0]->get_wrapper(); + dmr_down = dmr[1]->get_wrapper(); + ModuleBase::GlobalFunc::ZEROS(dmr_up, nnr); + ModuleBase::GlobalFunc::ZEROS(dmr_down, nnr); + for (int ir = 0; ir < nnr; ++ir) + { + dmr_up[ir] = 0.5 * (dmr_mag[ir] + dmr_mag[ir + nnr]); + dmr_down[ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir + nnr]); + } + } +} + +template void mix_dmr(elecstate::DensityMatrix* dm, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const MixingConfig& cfg); +template void mix_dmr>(elecstate::DensityMatrix, double>* dm, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const MixingConfig& cfg); + +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_dmr.h b/source/source_estate/module_charge/chg_dmr.h new file mode 100644 index 00000000000..d050a4fc36a --- /dev/null +++ b/source/source_estate/module_charge/chg_dmr.h @@ -0,0 +1,66 @@ +#ifndef CHG_DMR_H +#define CHG_DMR_H + +// Stateless real-space density-matrix (DMR) mixing kernels extracted from +// Charge_Mixing. The mixing history (Mixing_Data) and the Mixing strategy +// object remain owned by Charge_Mixing and are passed in explicitly; the +// functions do not read Charge_Mixing members or PARAM/GlobalV. + +#include "chg_mix_cfg.h" + +namespace Base_Mixing +{ +class Mixing; +class Mixing_Data; +} // namespace Base_Mixing + +namespace elecstate +{ +template +class DensityMatrix; +} // namespace elecstate + +namespace module_charge +{ + +/** + * @brief Allocate the mixing buffer for the real-space density matrix and + * clear its history. + * + * The buffer cannot be allocated in Charge_Mixing::set_mixing(): its length + * nnr (number of non-zero R-matrix elements) is only known after + * DensityMatrix::init_DMR(), which runs later in beforescf(). + * + * @param mixing mixing strategy object, non-null + * @param mdata mixing history buffer for DMR, resized and reset in place + * @param nnr number of real-space density-matrix elements per spin, > 0 + * @param cfg mixing config (nspin and scf_thr_type select the path) + */ +void init_mixing_dmr(Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const int nnr, + const MixingConfig& cfg); + +/** + * @brief Mix the real-space density matrix (LCAO calculations only). + * + * For nspin == 1/4 the single spin channel is mixed directly; for nspin == 2 + * the up/down channels are transformed into charge/magnetization channels, + * mixed with independent betas, and transformed back. + * + * @tparam TK scalar type of the density matrix (double or + * std::complex); the DMR storage itself is real + * @param dm density-matrix object supplying DMR and DMR_save + * @param mixing mixing strategy object, non-null + * @param mdata DMR mixing history buffer + * @param cfg mixing config (nspin and the two mixing betas) + */ +template +void mix_dmr(elecstate::DensityMatrix* dm, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& mdata, + const MixingConfig& cfg); + +} // namespace module_charge + +#endif // CHG_DMR_H diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 641cd2969e6..e3413f26709 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -111,7 +111,8 @@ AddTest( TARGET MODULE_ESTATE_charge_mixing LIBS parameter base psi device planewave_serial cell_info SOURCES charge_mixing_test.cpp - ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_drho.cpp + ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_dmr.cpp + ../module_charge/chg_drho.cpp ../module_charge/chg_precond.cpp ../module_charge/charge_mixing_rho.cpp ../module_charge/chg_uspp.cpp ) From 86b4f54359300335c48f4d1efdedae96ca791444 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 09:09:32 +0800 Subject: [PATCH 34/37] module_charge: remove Charge_Mixing DMR wrappers, call chg_dmr directly Delete charge_mixing_dmr.cpp and have the two call sites (chg_routine.cpp, esolver_ks_lcao.cpp) invoke module_charge:: init_mixing_dmr/mix_dmr directly with the Mixing object, mixing data and MixingConfig obtained through Charge_Mixing accessors. Expose the owned DMR mixing history via a new get_dmr_mdata() accessor and drop the now-unneeded density_matrix.h include from charge_mixing.h. Timers move into the free functions with module_charge labels. Add the direct parallel_orbitals.h include to esolver_gets.h, whose value member previously relied on the removed transitive include. --- source/Makefile.Objects | 2 +- source/source_esolver/esolver_gets.h | 1 + source/source_esolver/esolver_ks_lcao.cpp | 6 +++- source/source_estate/CMakeLists.txt | 1 - .../module_charge/charge_mixing.h | 28 +++++++-------- .../module_charge/charge_mixing_dmr.cpp | 35 ------------------- .../source_estate/module_charge/chg_dmr.cpp | 8 +++++ .../module_charge/chg_routine.cpp | 6 +++- source/source_estate/test/CMakeLists.txt | 2 +- 9 files changed, 33 insertions(+), 56 deletions(-) delete mode 100644 source/source_estate/module_charge/charge_mixing_dmr.cpp diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 76e3b754564..cedd880e8d6 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -832,7 +832,7 @@ OBJS_SRCPW=h_ewald_pw.o\ chg_parallel.o\ chg_extra.o\ charge_mixing.o\ - charge_mixing_dmr.o\ + chg_dmr.o\ chg_drho.o\ chg_precond.o\ charge_mixing_rho.o\ diff --git a/source/source_esolver/esolver_gets.h b/source/source_esolver/esolver_gets.h index 29ea4374d40..81282b7ef17 100644 --- a/source/source_esolver/esolver_gets.h +++ b/source/source_esolver/esolver_gets.h @@ -1,6 +1,7 @@ #ifndef ESOLVER_GETS_H #define ESOLVER_GETS_H +#include "source_basis/module_ao/parallel_orbitals.h" #include "source_basis/module_nao/two_center_bundle.h" #include "source_cell/unitcell.h" #include "source_esolver/esolver_ks.h" diff --git a/source/source_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index 5ff4da8b9d7..371d126d222 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -8,6 +8,7 @@ #include "source_pw/module_pwdft/dftu_base.h" // Plus_U_Base (PW and LCAO share it) #include "source_hamilt/hs_matrix_k.h" #include "source_estate/module_charge/chg_symm.h" +#include "source_estate/module_charge/chg_dmr.h" #include "source_lcao/lcao_domain.h" // need DeePKS_init #include "source_lcao/force_stress_lcao.h" #include "source_hamilt/module_gint/gint.h" @@ -564,7 +565,10 @@ void ESolver_KS_LCAO::iter_finish(UnitCell& ucell, const int istep, int& { if (this->inp_->mixing_restart > 0 && this->p_chgmix->mixing_restart_count > 0 && this->inp_->mixing_dmr) { - this->p_chgmix->mix_dmr(this->dmat.dm); + module_charge::mix_dmr(this->dmat.dm, + this->p_chgmix->get_mixing(), + this->p_chgmix->get_dmr_mdata(), + this->p_chgmix->get_mixing_config()); } } diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index ae89828edb4..e4eb55ebb70 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -35,7 +35,6 @@ list(APPEND objects module_charge/chg_parallel.cpp module_charge/chg_extra.cpp module_charge/charge_mixing.cpp - module_charge/charge_mixing_dmr.cpp module_charge/chg_dmr.cpp module_charge/chg_drho.cpp module_charge/chg_precond.cpp diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index bdde48e97dc..f487fb456af 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -2,7 +2,6 @@ #define CHARGE_MIXING_H #include "charge.h" #include "chg_mix_cfg.h" -#include "source_estate/module_dm/density_matrix.h" #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" #include @@ -15,9 +14,11 @@ class Charge_Mixing /// 1. set_mixing() to set all private mixing parameters /// 2. init_mixing() to initialize mixing, including allocating memory for mixing data and reset mixing /// 3. mix_rho() to mix charge density - /// 4. mix_dmr() to mix real-space density matrix + /// Real-space density matrix mixing is implemented by the stateless + /// module_charge::init_mixing_dmr/mix_dmr functions in chg_dmr.h; this class + /// only owns the mixing history buffer, exposed through get_dmr_mdata(). /// how to use it: - /// you can (re)start a mixing by calling set_mixing() and init_mixing() before calling mix_rho() or mix_dmr() + /// you can (re)start a mixing by calling set_mixing() and init_mixing() before calling mix_rho() public: Charge_Mixing(); @@ -41,25 +42,12 @@ class Charge_Mixing */ void init_mixing(); - /** - * @brief allocate memory of dmr_mdata - * @param nnr size of real-space density matrix - */ - void allocate_mixing_dmr(const int nnr); - /** * @brief charge mixing * @param chr pointer of Charge object */ void mix_rho(Charge* chr); - /** - * @brief density matrix mixing, only for LCAO - * @param DM pointer of DensityMatrix object - */ - void mix_dmr(elecstate::DensityMatrix* DM); - void mix_dmr(elecstate::DensityMatrix, double>* DM); - /** * @brief allocate memory of uom_mdata * @param uom_size size of DFT+U occupation matrix @@ -93,6 +81,14 @@ class Charge_Mixing double get_mixing_gg0() const {return mixing_gg0;} Base_Mixing::Mixing* get_mixing() const {return mixing;} + /** + * @brief mutable access to the real-space density-matrix mixing history + * + * The history buffer is owned by Charge_Mixing and driven by the + * stateless module_charge::init_mixing_dmr/mix_dmr functions in chg_dmr.h. + */ + Base_Mixing::Mixing_Data& get_dmr_mdata() {return dmr_mdata;} + /** * @brief read-only access to the aggregated mixing config set by set_mixing() */ diff --git a/source/source_estate/module_charge/charge_mixing_dmr.cpp b/source/source_estate/module_charge/charge_mixing_dmr.cpp deleted file mode 100644 index 7359cf68939..00000000000 --- a/source/source_estate/module_charge/charge_mixing_dmr.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "charge_mixing.h" -#include "chg_dmr.h" - -#include "source_base/timer.h" -#include "source_base/tool_title.h" - -void Charge_Mixing::allocate_mixing_dmr(const int nnr) -{ - ModuleBase::TITLE("Charge_Mixing", "allocate_mixing_dmr"); - ModuleBase::timer::start("Charge_Mixing", "allocate_mixing_dmr"); - module_charge::init_mixing_dmr(this->mixing, this->dmr_mdata, nnr, this->cfg_); - ModuleBase::timer::end("Charge_Mixing", "allocate_mixing_dmr"); - - return; -} - -void Charge_Mixing::mix_dmr(elecstate::DensityMatrix* DM) -{ - ModuleBase::TITLE("Charge_Mixing", "mix_dmr"); - ModuleBase::timer::start("Charge_Mixing", "mix_dmr"); - module_charge::mix_dmr(DM, this->mixing, this->dmr_mdata, this->cfg_); - ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); - - return; -} - -void Charge_Mixing::mix_dmr(elecstate::DensityMatrix, double>* DM) -{ - ModuleBase::TITLE("Charge_Mixing", "mix_dmr"); - ModuleBase::timer::start("Charge_Mixing", "mix_dmr"); - module_charge::mix_dmr(DM, this->mixing, this->dmr_mdata, this->cfg_); - ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); - - return; -} diff --git a/source/source_estate/module_charge/chg_dmr.cpp b/source/source_estate/module_charge/chg_dmr.cpp index f6f6953c8b0..045149e6027 100644 --- a/source/source_estate/module_charge/chg_dmr.cpp +++ b/source/source_estate/module_charge/chg_dmr.cpp @@ -5,7 +5,9 @@ #include "source_base/global_function.h" #include "source_base/module_mixing/mixing.h" +#include "source_base/timer.h" #include "source_base/tool_quit.h" +#include "source_base/tool_title.h" #include "source_estate/module_dm/density_matrix.h" namespace module_charge @@ -56,6 +58,8 @@ void init_mixing_dmr(Base_Mixing::Mixing* mixing, const int nnr, const MixingConfig& cfg) { + ModuleBase::TITLE("module_charge", "init_mixing_dmr"); + ModuleBase::timer::start("module_charge", "init_mixing_dmr"); if (mixing == nullptr) { ModuleBase::WARNING_QUIT("module_charge::init_mixing_dmr", "mixing pointer is null"); @@ -79,6 +83,7 @@ void init_mixing_dmr(Base_Mixing::Mixing* mixing, // Clear the history counters while keeping the allocated storage. mdata.reset(); + ModuleBase::timer::end("module_charge", "init_mixing_dmr"); } template @@ -87,6 +92,8 @@ void mix_dmr(elecstate::DensityMatrix* dm, Base_Mixing::Mixing_Data& mdata, const MixingConfig& cfg) { + ModuleBase::TITLE("module_charge", "mix_dmr"); + ModuleBase::timer::start("module_charge", "mix_dmr"); if (dm == nullptr) { ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "density matrix pointer is null"); @@ -160,6 +167,7 @@ void mix_dmr(elecstate::DensityMatrix* dm, dmr_down[ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir + nnr]); } } + ModuleBase::timer::end("module_charge", "mix_dmr"); } template void mix_dmr(elecstate::DensityMatrix* dm, diff --git a/source/source_estate/module_charge/chg_routine.cpp b/source/source_estate/module_charge/chg_routine.cpp index a77a9fc4b45..804ce146a04 100644 --- a/source/source_estate/module_charge/chg_routine.cpp +++ b/source/source_estate/module_charge/chg_routine.cpp @@ -1,4 +1,5 @@ #include "source_estate/module_charge/chg_routine.h" +#include "source_estate/module_charge/chg_dmr.h" #include "source_base/parallel_comm.h" #include "source_estate/update_pot.h" @@ -227,7 +228,10 @@ void module_charge::chgmixing_ks_lcao(const int iter, // scf iteration number if (inp.mixing_dmr) // for mixing_dmr { // allocate memory for dmr_mdata - p_chgmix->allocate_mixing_dmr(nnr); + module_charge::init_mixing_dmr(p_chgmix->get_mixing(), + p_chgmix->get_dmr_mdata(), + nnr, + p_chgmix->get_mixing_config()); } } } diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index e3413f26709..55adb3fbb5e 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -111,7 +111,7 @@ AddTest( TARGET MODULE_ESTATE_charge_mixing LIBS parameter base psi device planewave_serial cell_info SOURCES charge_mixing_test.cpp - ../module_charge/charge_mixing.cpp ../module_charge/charge_mixing_dmr.cpp ../module_charge/chg_dmr.cpp + ../module_charge/charge_mixing.cpp ../module_charge/chg_dmr.cpp ../module_charge/chg_drho.cpp ../module_charge/chg_precond.cpp ../module_charge/charge_mixing_rho.cpp ../module_charge/chg_uspp.cpp From 6d174ff2a3c8a0357ba199da9226447733e01603 Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 09:39:55 +0800 Subject: [PATCH 35/37] module_charge: decouple chg_dmr kernel from HContainer, mix raw buffers Change module_charge::mix_dmr to take per-spin raw contiguous double buffers and nnr instead of HContainer/DMR container references, and drop the hcontainer.h include (and its atom_pair/parallel_orbitals dependency chain) from chg_dmr.cpp. The sole call site in esolver_ks_lcao.cpp now extracts the wrappers and saved buffers from the DensityMatrix containers before calling the kernel. Move the argument checks into a file-local check_dmr_inputs helper. The kernel now depends only on the mixing module and MixingConfig. --- source/source_esolver/esolver_ks_lcao.cpp | 16 ++- .../source_estate/module_charge/chg_dmr.cpp | 105 +++++++++--------- source/source_estate/module_charge/chg_dmr.h | 32 +++--- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/source/source_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index 371d126d222..47bea5dfedc 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -565,7 +565,21 @@ void ESolver_KS_LCAO::iter_finish(UnitCell& ucell, const int istep, int& { if (this->inp_->mixing_restart > 0 && this->p_chgmix->mixing_restart_count > 0 && this->inp_->mixing_dmr) { - module_charge::mix_dmr(this->dmat.dm, + // Extract the contiguous per-spin DMR buffers expected by the + // stateless mixing kernel. + const std::vector*>& dmr_containers + = this->dmat.dm->get_DMR_vector(); + const std::vector>& dmr_save = this->dmat.dm->get_DMR_save(); + std::vector dmr_out; + std::vector dmr_in; + for (std::size_t is = 0; is < dmr_containers.size(); ++is) + { + dmr_out.push_back(dmr_containers[is]->get_wrapper()); + dmr_in.push_back(dmr_save[is].data()); + } + module_charge::mix_dmr(dmr_out, + dmr_in, + dmr_containers[0]->get_nnr(), this->p_chgmix->get_mixing(), this->p_chgmix->get_dmr_mdata(), this->p_chgmix->get_mixing_config()); diff --git a/source/source_estate/module_charge/chg_dmr.cpp b/source/source_estate/module_charge/chg_dmr.cpp index 045149e6027..0ac793fa452 100644 --- a/source/source_estate/module_charge/chg_dmr.cpp +++ b/source/source_estate/module_charge/chg_dmr.cpp @@ -8,7 +8,6 @@ #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_base/tool_title.h" -#include "source_estate/module_dm/density_matrix.h" namespace module_charge { @@ -51,6 +50,43 @@ void twobeta_step(double* out, } } +/** + * @brief Validate the arguments of mix_dmr. Aborts via WARNING_QUIT on the + * first invalid input. + */ +void check_dmr_inputs(const std::vector& dmr_out, + const std::vector& dmr_in, + const int nnr, + const Base_Mixing::Mixing* mixing, + const MixingConfig& cfg) +{ + if (mixing == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "mixing pointer is null"); + } + if (nnr <= 0) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "nnr must be > 0"); + } + if (cfg.nspin != 1 && cfg.nspin != 2 && cfg.nspin != 4) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "unsupported nspin, require 1, 2 or 4"); + } + const int nspin_need = (cfg.nspin == 2) ? 2 : 1; + if (static_cast(dmr_out.size()) < nspin_need + || static_cast(dmr_in.size()) < nspin_need) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "not enough DMR buffers for nspin"); + } + for (int is = 0; is < nspin_need; ++is) + { + if (dmr_out[is] == nullptr || dmr_in[is] == nullptr) + { + ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "DMR buffer pointer is null"); + } + } +} + } // namespace void init_mixing_dmr(Base_Mixing::Mixing* mixing, @@ -86,66 +122,42 @@ void init_mixing_dmr(Base_Mixing::Mixing* mixing, ModuleBase::timer::end("module_charge", "init_mixing_dmr"); } -template -void mix_dmr(elecstate::DensityMatrix* dm, +void mix_dmr(const std::vector& dmr_out, + const std::vector& dmr_in, + const int nnr, Base_Mixing::Mixing* mixing, Base_Mixing::Mixing_Data& mdata, const MixingConfig& cfg) { ModuleBase::TITLE("module_charge", "mix_dmr"); ModuleBase::timer::start("module_charge", "mix_dmr"); - if (dm == nullptr) - { - ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "density matrix pointer is null"); - } - if (mixing == nullptr) - { - ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "mixing pointer is null"); - } - if (cfg.nspin != 1 && cfg.nspin != 2 && cfg.nspin != 4) - { - ModuleBase::WARNING_QUIT("module_charge::mix_dmr", "unsupported nspin, require 1, 2 or 4"); - } + check_dmr_inputs(dmr_out, dmr_in, nnr, mixing, cfg); - std::vector*> dmr = dm->get_DMR_vector(); - std::vector>& dmr_save = dm->get_DMR_save(); - - double* dmr_in = nullptr; - double* dmr_out = nullptr; if (cfg.nspin == 1 || cfg.nspin == 4) { - dmr_in = dmr_save[0].data(); - dmr_out = dmr[0]->get_wrapper(); - mixing->push_data(mdata, dmr_in, dmr_out, nullptr, false); - mixing->mix_data(mdata, dmr_out); + mixing->push_data(mdata, dmr_in[0], dmr_out[0], nullptr, false); + mixing->mix_data(mdata, dmr_out[0]); } else // cfg.nspin == 2 { // Magnetic density matrix: up/down channels are transformed into // charge/magnetization channels before mixing and back afterwards. - const int nnr = dmr[0]->get_nnr(); std::vector dmr_mag(nnr * cfg.nspin, 0.0); std::vector dmr_mag_save(nnr * cfg.nspin, 0.0); // Transfer the current DMR into the charge/magnetization layout. - double* dmr_up = dmr[0]->get_wrapper(); - double* dmr_down = dmr[1]->get_wrapper(); for (int ir = 0; ir < nnr; ++ir) { - dmr_mag[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag[ir + nnr] = dmr_up[ir] - dmr_down[ir]; + dmr_mag[ir] = dmr_out[0][ir] + dmr_out[1][ir]; + dmr_mag[ir + nnr] = dmr_out[0][ir] - dmr_out[1][ir]; } // Transfer the saved DMR into the charge/magnetization layout. - dmr_up = dmr_save[0].data(); - dmr_down = dmr_save[1].data(); for (int ir = 0; ir < nnr; ++ir) { - dmr_mag_save[ir] = dmr_up[ir] + dmr_down[ir]; - dmr_mag_save[ir + nnr] = dmr_up[ir] - dmr_down[ir]; + dmr_mag_save[ir] = dmr_in[0][ir] + dmr_in[1][ir]; + dmr_mag_save[ir + nnr] = dmr_in[0][ir] - dmr_in[1][ir]; } - dmr_in = dmr_mag_save.data(); - dmr_out = dmr_mag.data(); const double beta = cfg.mixing_beta; const double beta_mag = cfg.mixing_beta_mag; std::function twobeta @@ -153,30 +165,19 @@ void mix_dmr(elecstate::DensityMatrix* dm, twobeta_step(out, in, sres, nnr, beta, beta_mag); }; // No Kerker screening in DMR mixing. - mixing->push_data(mdata, dmr_in, dmr_out, nullptr, twobeta, false); - mixing->mix_data(mdata, dmr_out); + mixing->push_data(mdata, dmr_mag_save.data(), dmr_mag.data(), nullptr, twobeta, false); + mixing->mix_data(mdata, dmr_mag.data()); // Transform the mixed charge/magnetization channels back to up/down. - dmr_up = dmr[0]->get_wrapper(); - dmr_down = dmr[1]->get_wrapper(); - ModuleBase::GlobalFunc::ZEROS(dmr_up, nnr); - ModuleBase::GlobalFunc::ZEROS(dmr_down, nnr); + ModuleBase::GlobalFunc::ZEROS(dmr_out[0], nnr); + ModuleBase::GlobalFunc::ZEROS(dmr_out[1], nnr); for (int ir = 0; ir < nnr; ++ir) { - dmr_up[ir] = 0.5 * (dmr_mag[ir] + dmr_mag[ir + nnr]); - dmr_down[ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir + nnr]); + dmr_out[0][ir] = 0.5 * (dmr_mag[ir] + dmr_mag[ir + nnr]); + dmr_out[1][ir] = 0.5 * (dmr_mag[ir] - dmr_mag[ir + nnr]); } } ModuleBase::timer::end("module_charge", "mix_dmr"); } -template void mix_dmr(elecstate::DensityMatrix* dm, - Base_Mixing::Mixing* mixing, - Base_Mixing::Mixing_Data& mdata, - const MixingConfig& cfg); -template void mix_dmr>(elecstate::DensityMatrix, double>* dm, - Base_Mixing::Mixing* mixing, - Base_Mixing::Mixing_Data& mdata, - const MixingConfig& cfg); - } // namespace module_charge diff --git a/source/source_estate/module_charge/chg_dmr.h b/source/source_estate/module_charge/chg_dmr.h index d050a4fc36a..db1983bae53 100644 --- a/source/source_estate/module_charge/chg_dmr.h +++ b/source/source_estate/module_charge/chg_dmr.h @@ -4,7 +4,12 @@ // Stateless real-space density-matrix (DMR) mixing kernels extracted from // Charge_Mixing. The mixing history (Mixing_Data) and the Mixing strategy // object remain owned by Charge_Mixing and are passed in explicitly; the -// functions do not read Charge_Mixing members or PARAM/GlobalV. +// functions do not read Charge_Mixing members or PARAM/GlobalV. The kernels +// work on raw contiguous double buffers (one per spin channel) and do not +// depend on DensityMatrix or HContainer; the caller extracts those buffers +// from its LCAO containers. + +#include #include "chg_mix_cfg.h" @@ -14,12 +19,6 @@ class Mixing; class Mixing_Data; } // namespace Base_Mixing -namespace elecstate -{ -template -class DensityMatrix; -} // namespace elecstate - namespace module_charge { @@ -48,15 +47,18 @@ void init_mixing_dmr(Base_Mixing::Mixing* mixing, * the up/down channels are transformed into charge/magnetization channels, * mixed with independent betas, and transformed back. * - * @tparam TK scalar type of the density matrix (double or - * std::complex); the DMR storage itself is real - * @param dm density-matrix object supplying DMR and DMR_save - * @param mixing mixing strategy object, non-null - * @param mdata DMR mixing history buffer - * @param cfg mixing config (nspin and the two mixing betas) + * @param dmr_out writable DMR buffers, one per spin channel, each of length + * nnr; mixed results are written back through these pointers + * @param dmr_in DMR buffers saved at the previous mixing step, one per spin + * channel, each of length nnr (read-only) + * @param nnr number of DMR elements per spin channel, > 0 + * @param mixing mixing strategy object, non-null + * @param mdata DMR mixing history buffer + * @param cfg mixing config (nspin and the two mixing betas) */ -template -void mix_dmr(elecstate::DensityMatrix* dm, +void mix_dmr(const std::vector& dmr_out, + const std::vector& dmr_in, + const int nnr, Base_Mixing::Mixing* mixing, Base_Mixing::Mixing_Data& mdata, const MixingConfig& cfg); From d9685d4eb5e003b8d1b1ede47263d2eb517c9e5e Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 10:23:39 +0800 Subject: [PATCH 36/37] module_charge: refactor charge_mixing_rho free functions and cleanup - Replace 17 PARAM.inp/globalv direct reads with cfg_ fields - Unify mixing_tau: remove redundant member, use cfg_.mixing_tau - Extract make_twobeta_mix as free function template in anonymous namespace - Extract mix_tau_recip free function for kinetic energy density mixing - Extract pack_rho_mag/unpack_rho_mag templates for nspin==2 dedup - Hoist screen and inner_product lambdas before if-else chains (8+4 dups) - Remove dead new_e_iteration member and its no-op if block - Drop unused parameter.h include from charge_mixing_rho.cpp --- .../module_charge/charge_mixing.cpp | 6 +- .../module_charge/charge_mixing.h | 32 +- .../module_charge/charge_mixing_rho.cpp | 449 +++++++++++------- .../source_estate/test/charge_mixing_test.cpp | 4 +- 4 files changed, 277 insertions(+), 214 deletions(-) diff --git a/source/source_estate/module_charge/charge_mixing.cpp b/source/source_estate/module_charge/charge_mixing.cpp index 76ab3aff058..18ac578b80e 100644 --- a/source/source_estate/module_charge/charge_mixing.cpp +++ b/source/source_estate/module_charge/charge_mixing.cpp @@ -41,7 +41,7 @@ void Charge_Mixing::set_mixing(const MixingConfig& cfg, this->mixing_beta_mag = cfg.mixing_beta_mag; this->mixing_ndim = cfg.mixing_ndim; this->mixing_gg0 = cfg.mixing_gg0; - this->mixing_tau = cfg.mixing_tau; + this->mixing_gg0_mag = cfg.mixing_gg0_mag; this->mixing_gg0_min = cfg.mixing_gg0_min; this->mixing_angle = cfg.mixing_angle; @@ -165,7 +165,7 @@ void Charge_Mixing::init_mixing() } // initailize tau_mdata - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { if (this->cfg_.scf_thr_type == 1) { @@ -195,7 +195,7 @@ void Charge_Mixing::mix_reset() this->mixing->reset(); this->rho_mdata.reset(); // initailize tau_mdata - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { this->tau_mdata.reset(); } diff --git a/source/source_estate/module_charge/charge_mixing.h b/source/source_estate/module_charge/charge_mixing.h index f487fb456af..700ff37bf5b 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -122,7 +122,7 @@ class Charge_Mixing double mixing_beta_mag = 1.6; ///< mixing beta for magnetism int mixing_ndim = 8; ///< mixing ndim for broyden and pulay double mixing_gg0 = 0.0; ///< mixing gg0 for Kerker screen - bool mixing_tau = false; ///< whether to use tau mixing + double mixing_gg0_mag = 0.0; ///< mixing gg0 for Kerker screen for magnetism double mixing_gg0_min = 0.1; ///< minimum kerker coefficient double mixing_angle = 0.0; ///< mixing angle for nspin=4 @@ -131,8 +131,6 @@ class Charge_Mixing double* tpiba = nullptr; ///< 2*pi/beta for non-linear core correction double* tpiba2 = nullptr; ///< 2*pi/beta^2 for non-linear core correction std::vector _drho_history; ///< history of drho used to determine the oscillation, size is scf_nmax - - bool new_e_iteration = true; ModulePW::PW_Basis* rhopw = nullptr; ///< smooth grid ModulePW::PW_Basis* rhodpw = nullptr; ///< dense grid, same as rhopw for ncpp. @@ -148,34 +146,6 @@ class Charge_Mixing * @param chr pointer of Charge object */ void mix_rho_real(Charge* chr); - - /** - * @brief two-beta mixing functor: mix the first `nunit` elements with - * mixing_beta and the rest (nunit..total) with mixing_beta_mag. Used for - * magnetic cases (nspin==2/4) where the charge channel and the magnetism - * channels use different betas. Replaces the duplicated local lambdas. - * @tparam T element type, double (real space) or std::complex (reciprocal) - */ - template - std::function make_twobeta_mix(const int total, const int nunit) - { - return [this, total, nunit](T* out, const T* in, const T* sres) { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nunit; ++i) - { - out[i] = in[i] + this->mixing_beta * sres[i]; - } -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nunit; i < total; ++i) - { - out[i] = in[i] + this->mixing_beta_mag * sres[i]; - } - }; - } }; #endif diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 957f43db606..9dce4e756f8 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -2,16 +2,236 @@ #include "chg_drho.h" #include "chg_precond.h" #include "chg_uspp.h" -#include "source_io/module_parameter/parameter.h" + #include "source_base/timer.h" +#include "source_base/tool_quit.h" #include "source_hamilt/module_xc/xc_functional.h" +namespace { + +/** + * @brief Create a two-beta mixing functor: mix the first nunit elements with + * mixing_beta and the rest (nunit..total) with mixing_beta_mag. + * Used for magnetic cases (nspin==2/4) where the charge channel and + * the magnetism channels use different betas. + * @tparam T element type, double (real space) or std::complex (reciprocal) + * @param total total number of elements + * @param nunit number of elements in the charge channel + * @param mixing_beta beta for the charge channel + * @param mixing_beta_mag beta for the magnetism channel + * @return mixing functor + */ +template +std::function make_twobeta_mix( + const int total, const int nunit, + const double mixing_beta, const double mixing_beta_mag) +{ + return [total, nunit, mixing_beta, mixing_beta_mag](T* out, const T* in, const T* sres) + { +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = 0; i < nunit; ++i) + { + out[i] = in[i] + mixing_beta * sres[i]; + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = nunit; i < total; ++i) + { + out[i] = in[i] + mixing_beta_mag * sres[i]; + } + }; +} + +/** + * @brief Mix kinetic energy density in reciprocal space. + * Handles the double-grid split/merge for the smooth and + * high-frequency parts, DIIS mixing of the smooth part, and + * plain mixing of the high-frequency part. + * @param chr pointer to Charge object (must have kin_r/kin_r_save) + * @param nspin number of spins + * @param double_grid whether double grid is used + * @param rhopw smooth grid + * @param rhodpw dense grid (same as rhopw when double_grid is off) + * @param mixing DIIS mixing object + * @param tau_mdata mixing data for tau + * @param mixing_highf plain mixing for high-frequency part (may be null when double_grid is off) + */ +void mix_tau_recip(Charge* chr, + const int nspin, + const bool double_grid, + ModulePW::PW_Basis* rhopw, + ModulePW::PW_Basis* rhodpw, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& tau_mdata, + Base_Mixing::Plain_Mixing* mixing_highf) +{ + ModuleBase::TITLE("Charge_Mixing", "mix_tau_recip"); + ModuleBase::timer::start("Charge_Mixing", "mix_tau_recip"); + + if (chr == nullptr) + { + ModuleBase::WARNING_QUIT("mix_tau_recip", "chr is null"); + } + if (rhopw == nullptr || rhodpw == nullptr) + { + ModuleBase::WARNING_QUIT("mix_tau_recip", "grid pointer is null"); + } + if (mixing == nullptr) + { + ModuleBase::WARNING_QUIT("mix_tau_recip", "mixing is null"); + } + if (nspin < 1) + { + ModuleBase::WARNING_QUIT("mix_tau_recip", "nspin must be >= 1"); + } + if (double_grid && mixing_highf == nullptr) + { + ModuleBase::WARNING_QUIT("mix_tau_recip", "mixing_highf is null when double_grid is on"); + } + + std::vector> kin_g(nspin * rhodpw->npw); + std::vector> kin_g_save(nspin * rhodpw->npw); + // FFT to get kin_g and kin_g_save + for (int is = 0; is < nspin; ++is) + { + rhodpw->real2recip(chr->kin_r[is], &kin_g[is * rhodpw->npw]); + rhodpw->real2recip(chr->kin_r_save[is], &kin_g_save[is * rhodpw->npw]); + } + + // RAII owners for the smooth / high-frequency parts on the double grid; + // raw pointers below alias these vectors when double_grid is on, or + // alias kin_g[_save] directly when double_grid is off so the mixing + // mutates the dense buffer in place. + std::vector> tau_sg_in; + std::vector> tau_sg_out; + std::vector> tau_hf_in; + std::vector> tau_hf_out; + std::complex* taugs_in = nullptr; + std::complex* taugs_out = nullptr; + std::complex* taughf_in = nullptr; + std::complex* taughf_out = nullptr; + + if (double_grid) + { + const int npw_smooth = rhopw->npw; + const int npw_dense = rhodpw->npw; + tau_sg_in.resize(nspin * npw_smooth); + tau_hf_in.resize(nspin * (npw_dense - npw_smooth)); + tau_sg_out.resize(nspin * npw_smooth); + tau_hf_out.resize(nspin * (npw_dense - npw_smooth)); + module_charge::split_dgrid(kin_g_save.data(), tau_sg_in, tau_hf_in, + nspin, npw_smooth, npw_dense); + module_charge::split_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, + nspin, npw_smooth, npw_dense); + taugs_in = tau_sg_in.data(); + taughf_in = tau_hf_in.data(); + taugs_out = tau_sg_out.data(); + taughf_out = tau_hf_out.data(); + } + else + { + taugs_in = kin_g_save.data(); + taugs_out = kin_g.data(); + } + + // Note: there is no kerker modification for tau because I'm not sure + // if we should have it. If necessary we can try it in the future. + mixing->push_data(tau_mdata, taugs_in, taugs_out, nullptr, false); + mixing->mix_data(tau_mdata, taugs_out); + + if (double_grid) + { + // simple mixing for high_frequencies + const int ndimhf = (rhodpw->npw - rhopw->npw) * nspin; + mixing_highf->plain_mix(taughf_out, taughf_in, taughf_out, ndimhf, nullptr); + + // combine smooth part and high_frequency part + module_charge::merge_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, + nspin, rhopw->npw, rhodpw->npw); + } + + // kin_g to kin_r + for (int is = 0; is < nspin; is++) + { + rhodpw->recip2real(&kin_g[is * rhodpw->npw], chr->kin_r[is]); + } + + ModuleBase::timer::end("Charge_Mixing", "mix_tau_recip"); +} + +/** + * @brief Pack charge and magnetism into interleaved layout: + * out[0..n] = d0 + d1 (charge channel) + * out[n..2n] = d0 - d1 (magnetism channel) + * @tparam T double (real space) or std::complex (reciprocal) + * @param out output buffer, size >= 2*n + * @param d0 first component (e.g. chr->rho[0] or chr->rhog[0]) + * @param d1 second component + * @param n number of elements per component + */ +template +void pack_rho_mag(T* out, const T* d0, const T* d1, const int n) +{ + if (out == nullptr || d0 == nullptr || d1 == nullptr) + { + ModuleBase::WARNING_QUIT("pack_rho_mag", "pointer is null"); + } + if (n < 0) + { + ModuleBase::WARNING_QUIT("pack_rho_mag", "n must be >= 0"); + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int i = 0; i < n; ++i) + { + out[i] = d0[i] + d1[i]; + out[i + n] = d0[i] - d1[i]; + } +} + +/** + * @brief Unpack interleaved layout back to charge and magnetism components: + * d0[i] = 0.5 * (in[i] + in[i+n]) + * d1[i] = 0.5 * (in[i] - in[i+n]) + * @tparam T double (real space) or std::complex (reciprocal) + * @param d0 output first component (e.g. chr->rho[0] or chr->rhog[0]) + * @param d1 output second component + * @param in input buffer, size >= 2*n + * @param n number of elements per component + */ +template +void unpack_rho_mag(T* d0, T* d1, const T* in, const int n) +{ + if (d0 == nullptr || d1 == nullptr || in == nullptr) + { + ModuleBase::WARNING_QUIT("unpack_rho_mag", "pointer is null"); + } + if (n < 0) + { + ModuleBase::WARNING_QUIT("unpack_rho_mag", "n must be >= 0"); + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int i = 0; i < n; ++i) + { + d0[i] = 0.5 * (in[i] + in[i + n]); + d1[i] = 0.5 * (in[i] - in[i + n]); + } +} + +} // namespace + void Charge_Mixing::mix_rho_recip(Charge* chr) { ModuleBase::TITLE("Charge_Mixing", "mix_rho_recip"); ModuleBase::timer::start("Charge_Mixing", "mix_rho_recip"); - const int nspin = PARAM.inp.nspin; + const int nspin = cfg_.nspin; assert(nspin==1 || nspin==2 || nspin==4); std::complex* rhog_in = nullptr; @@ -31,7 +251,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) std::complex* rhoghf_in = nullptr; std::complex* rhoghf_out = nullptr; - if ( PARAM.globalv.double_grid) + if ( cfg_.double_grid) { // divide into smooth part and high_frequency part const int npw_smooth = this->rhopw->npw; @@ -62,14 +282,16 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) rhog1, rhog2, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); }; + // Kerker screening functor, shared by all nspin branches + auto screen = [this](std::complex* p) { + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; + // DIIS Mixing Only for smooth part, while high_frequency part is mixed by plain mixing method. if (nspin == 1) { rhog_in = rhogs_in; rhog_out = rhogs_out; - auto screen = [this](std::complex* p) { - module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); - }; this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -78,28 +300,14 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) { // magnetic density const int npw = this->rhopw->npw; - // rhog_mag[is*ngmc] and rhog_mag_save[is*ngmc], zero-initialized std::vector> rhog_mag(npw * nspin); std::vector> rhog_mag_save(npw * nspin); - // get rhog_mag[is*ngmc] and rhog_mag_save[is*ngmc] - for (int ig = 0; ig < npw; ig++) - { - rhog_mag[ig] = chr->rhog[0][ig] + chr->rhog[1][ig]; - rhog_mag_save[ig] = chr->rhog_save[0][ig] + chr->rhog_save[1][ig]; - } - for (int ig = 0; ig < npw; ig++) - { - rhog_mag[ig + npw] = chr->rhog[0][ig] - chr->rhog[1][ig]; - rhog_mag_save[ig + npw] = chr->rhog_save[0][ig] - chr->rhog_save[1][ig]; - } + pack_rho_mag(rhog_mag.data(), chr->rhog[0], chr->rhog[1], npw); + pack_rho_mag(rhog_mag_save.data(), chr->rhog_save[0], chr->rhog_save[1], npw); // rhog_in = rhog_mag_save.data(); rhog_out = rhog_mag.data(); - // - auto screen = [this](std::complex* p) { - module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); + auto twobeta_mix = make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -108,13 +316,9 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) { ModuleBase::GlobalFunc::ZEROS(chr->rhog[is], npw); } - for (int ig = 0; ig < npw; ig++) - { - chr->rhog[0][ig] = 0.5 * (rhog_mag[ig] + rhog_mag[ig+npw]); - chr->rhog[1][ig] = 0.5 * (rhog_mag[ig] - rhog_mag[ig+npw]); - } + unpack_rho_mag(chr->rhog[0], chr->rhog[1], rhog_mag.data(), npw); // sync rhogs_out so merge_dgrid has the latest smooth part - if ( PARAM.globalv.double_grid) + if ( cfg_.double_grid) { for (int ig = 0; ig < npw; ig++) { @@ -123,26 +327,23 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) } } } - else if (nspin == 4 && PARAM.inp.mixing_angle <= 0) + else if (nspin == 4 && cfg_.mixing_angle <= 0) { // normal broyden mixing for {rho, mx, my, mz} rhog_in = rhogs_in; rhog_out = rhogs_out; const int npw = this->rhopw->npw; - auto screen = [this](std::complex* p) { // use old one - module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix>(4 * npw, npw); + auto twobeta_mix = make_twobeta_mix>(4 * npw, npw, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); } - else if (nspin == 4 && PARAM.inp.mixing_angle > 0) + else if (nspin == 4 && cfg_.mixing_angle > 0) { // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed // old support see mix_rho_recip() - if ( PARAM.globalv.double_grid) + if ( cfg_.double_grid) { ModuleBase::WARNING_QUIT("Charge_Mixing", "double_grid is not supported for new mixing method yet."); } @@ -178,10 +379,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) // rhog_in = rhog_magabs_save.data(); rhog_out = rhog_magabs.data(); - auto screen = [this](std::complex* p) { // use old one - module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix>(2 * npw, npw); + auto twobeta_mix = make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhog_out); @@ -205,7 +403,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) } } - if ( PARAM.globalv.double_grid) + if ( cfg_.double_grid) { // plain mixing for high_frequencies const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; @@ -218,7 +416,7 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) } // rhog to rho - if (nspin == 4 && PARAM.inp.mixing_angle > 0) + if (nspin == 4 && cfg_.mixing_angle > 0) { // only tranfer rhog[0] // do not support double_grid, use rhopw directly @@ -229,82 +427,16 @@ void Charge_Mixing::mix_rho_recip(Charge* chr) for (int is = 0; is < nspin; is++) { // use rhodpw for double_grid - // rhodpw is the same as rhopw for ! PARAM.globalv.double_grid + // rhodpw is the same as rhopw for ! cfg_.double_grid this->rhodpw->recip_to_real,double,base_device::DEVICE_CPU>(chr->rhog[is], chr->rho[is]); } } // For kinetic energy density - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { - std::vector> kin_g(nspin * rhodpw->npw); - std::vector> kin_g_save(nspin * rhodpw->npw); - // FFT to get kin_g and kin_g_save - for (int is = 0; is < nspin; ++is) - { - rhodpw->real2recip(chr->kin_r[is], &kin_g[is * rhodpw->npw]); - rhodpw->real2recip(chr->kin_r_save[is], &kin_g_save[is * rhodpw->npw]); - } - // RAII owners for the smooth / high-frequency parts on the double grid; - // raw pointers below alias these vectors when double_grid is on, or - // alias kin_g[_save] directly when double_grid is off so the mixing - // mutates the dense buffer in place. - std::vector> tau_sg_in; - std::vector> tau_sg_out; - std::vector> tau_hf_in; - std::vector> tau_hf_out; - // for smooth part, for ! PARAM.globalv.double_grid only have this part - std::complex* taugs_in = nullptr; - std::complex* taugs_out = nullptr; - // for high frequency part - std::complex* taughf_in = nullptr; - std::complex* taughf_out = nullptr; - if ( PARAM.globalv.double_grid) - { - // divide into smooth part and high_frequency part - const int npw_smooth = this->rhopw->npw; - const int npw_dense = this->rhodpw->npw; - tau_sg_in.resize(nspin * npw_smooth); - tau_hf_in.resize(nspin * (npw_dense - npw_smooth)); - tau_sg_out.resize(nspin * npw_smooth); - tau_hf_out.resize(nspin * (npw_dense - npw_smooth)); - module_charge::split_dgrid(kin_g_save.data(), tau_sg_in, tau_hf_in, - nspin, npw_smooth, npw_dense); - module_charge::split_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, - nspin, npw_smooth, npw_dense); - taugs_in = tau_sg_in.data(); - taughf_in = tau_hf_in.data(); - taugs_out = tau_sg_out.data(); - taughf_out = tau_hf_out.data(); - } - else - { - taugs_in = kin_g_save.data(); - taugs_out = kin_g.data(); - } - - // Note: there is no kerker modification for tau because I'm not sure - // if we should have it. If necessary we can try it in the future. - this->mixing->push_data(this->tau_mdata, taugs_in, taugs_out, nullptr, false); - - this->mixing->mix_data(this->tau_mdata, taugs_out); - - if ( PARAM.globalv.double_grid) - { - // simple mixing for high_frequencies - const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; - this->mixing_highf->plain_mix(taughf_out, taughf_in, taughf_out, ndimhf, nullptr); - - // combine smooth part and high_frequency part; - // tau_sg_* / tau_hf_* vectors are released automatically at scope exit - module_charge::merge_dgrid(kin_g.data(), tau_sg_out, tau_hf_out, - nspin, this->rhopw->npw, this->rhodpw->npw); - } - - // kin_g to kin_r - for (int is = 0; is < nspin; is++) - { - rhodpw->recip2real(&kin_g[is * rhodpw->npw], chr->kin_r[is]); - } + mix_tau_recip(chr, nspin, cfg_.double_grid, + this->rhopw, this->rhodpw, + this->mixing, this->tau_mdata, this->mixing_highf); } ModuleBase::timer::end("Charge_Mixing", "mix_rho_recip"); @@ -316,24 +448,26 @@ void Charge_Mixing::mix_rho_real(Charge* chr) ModuleBase::TITLE("Charge_Mixing", "mix_rho_real"); ModuleBase::timer::start("Charge_Mixing", "mix_rho_real"); - const int nspin = PARAM.inp.nspin; + const int nspin = cfg_.nspin; assert(nspin==1 || nspin==2 || nspin==4); double* rhor_in=nullptr; double* rhor_out=nullptr; + // Kerker screening functor, shared by all nspin branches + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; + if (nspin == 1) { rhor_in = chr->rho_save[0]; rhor_out = chr->rho[0]; - auto screen = [this](double* p) { - module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - }; this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); - auto inner_product = [this](double* rho1, double* rho2) - { - return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); - }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); } @@ -341,65 +475,36 @@ void Charge_Mixing::mix_rho_real(Charge* chr) { // magnetic density const int nrxx = this->rhopw->nrxx; - // rho_mag[is*nnrx] and rho_mag_save[is*nnrx], zero-initialized std::vector rho_mag(nrxx * nspin); std::vector rho_mag_save(nrxx * nspin); - // get rho_mag[is*nnrx] and rho_mag_save[is*nnrx] - for (int ir = 0; ir < nrxx; ir++) - { - rho_mag[ir] = chr->rho[0][ir] + chr->rho[1][ir]; - rho_mag_save[ir] = chr->rho_save[0][ir] + chr->rho_save[1][ir]; - } - for (int ir = 0; ir < nrxx; ir++) - { - rho_mag[ir + nrxx] = chr->rho[0][ir] - chr->rho[1][ir]; - rho_mag_save[ir + nrxx] = chr->rho_save[0][ir] - chr->rho_save[1][ir]; - } + pack_rho_mag(rho_mag.data(), chr->rho[0], chr->rho[1], nrxx); + pack_rho_mag(rho_mag_save.data(), chr->rho_save[0], chr->rho_save[1], nrxx); // rhor_in = rho_mag_save.data(); rhor_out = rho_mag.data(); - auto screen = [this](double* p) { - module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); + auto twobeta_mix = make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product = [this](double* rho1, double* rho2) - { - return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); - }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); // get new rho[is][nrxx] from rho_mag[is*nrxx] for (int is = 0; is < nspin; is++) { ModuleBase::GlobalFunc::ZEROS(chr->rho[is], nrxx); - //ModuleBase::GlobalFunc::ZEROS(rho_save[is], nrxx); - } - for (int ir = 0; ir < nrxx; ir++) - { - chr->rho[0][ir] = 0.5 * (rho_mag[ir] + rho_mag[ir+nrxx]); - chr->rho[1][ir] = 0.5 * (rho_mag[ir] - rho_mag[ir+nrxx]); } + unpack_rho_mag(chr->rho[0], chr->rho[1], rho_mag.data(), nrxx); } - else if (nspin == 4 && PARAM.inp.mixing_angle <= 0) + else if (nspin == 4 && cfg_.mixing_angle <= 0) { // normal broyden mixing for {rho, mx, my, mz} rhor_in = chr->rho_save[0]; rhor_out = chr->rho[0]; const int nrxx = this->rhopw->nrxx; - auto screen = [this](double* p) { - module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix(4 * nrxx, nrxx); + auto twobeta_mix = make_twobeta_mix(4 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product = [this](double* rho1, double* rho2) - { - return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); - }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); } - else if (nspin == 4 && PARAM.inp.mixing_angle > 0) + else if (nspin == 4 && cfg_.mixing_angle > 0) { // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed @@ -424,15 +529,8 @@ void Charge_Mixing::mix_rho_real(Charge* chr) rhor_in = rho_magabs_save.data(); rhor_out = rho_magabs.data(); - auto screen = [this](double* p) { - module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto twobeta_mix = this->make_twobeta_mix(2 * nrxx, nrxx); + auto twobeta_mix = make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - auto inner_product = [this](double* rho1, double* rho2) - { - return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); - }; this->mixing->cal_coef(this->rho_mdata, inner_product); this->mixing->mix_data(this->rho_mdata, rhor_out); @@ -457,7 +555,7 @@ void Charge_Mixing::mix_rho_real(Charge* chr) double *taur_out=nullptr; double *taur_in=nullptr; - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { taur_in = chr->kin_r_save[0]; taur_out = chr->kin_r[0]; @@ -478,7 +576,7 @@ void Charge_Mixing::mix_rho(Charge* chr) ModuleBase::TITLE("Charge_Mixing", "mix_rho"); ModuleBase::timer::start("Charge_Mixing", "mix_rho"); - const int nspin = PARAM.inp.nspin; + const int nspin = cfg_.nspin; assert(nspin==1 || nspin==2 || nspin==4); // the charge before mixing. @@ -486,7 +584,7 @@ void Charge_Mixing::mix_rho(Charge* chr) std::vector rho123(nspin * nrxx); for (int is = 0; is < nspin; ++is) { - if (is == 0 || is == 3 || !PARAM.globalv.domag_z) + if (is == 0 || is == 3 || !cfg_.domag_z) { double* rho123_is = rho123.data() + is * nrxx; #ifdef _OPENMP @@ -499,7 +597,7 @@ void Charge_Mixing::mix_rho(Charge* chr) } } std::vector kin_r123; - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { kin_r123.resize(nspin * nrxx); for (int is = 0; is < nspin; ++is) @@ -515,11 +613,11 @@ void Charge_Mixing::mix_rho(Charge* chr) } } // --------------------Mixing Body-------------------- - if (PARAM.inp.scf_thr_type == 1) + if (cfg_.scf_thr_type == 1) { mix_rho_recip(chr); } - else if (PARAM.inp.scf_thr_type == 2) + else if (cfg_.scf_thr_type == 2) { mix_rho_real(chr); } @@ -529,7 +627,7 @@ void Charge_Mixing::mix_rho(Charge* chr) // rho_save is the charge before mixing for (int is = 0; is < nspin; ++is) { - if (is == 0 || is == 3 || !PARAM.globalv.domag_z) + if (is == 0 || is == 3 || !cfg_.domag_z) { double* rho123_is = rho123.data() + is * nrxx; #ifdef _OPENMP @@ -542,7 +640,7 @@ void Charge_Mixing::mix_rho(Charge* chr) } } - if ((XC_Functional::get_ked_flag()) && mixing_tau) + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) { for (int is = 0; is < nspin; ++is) { @@ -557,11 +655,6 @@ void Charge_Mixing::mix_rho(Charge* chr) } } - if (new_e_iteration) - { - new_e_iteration = false; - } - ModuleBase::timer::end("Charge_Mixing", "mix_rho"); return; } diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index 39e583f5e2a..c5ca513c5b9 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -161,7 +161,7 @@ TEST_F(ChargeMixingTest, SetMixingTest) EXPECT_EQ(CMtest.get_mixing_beta(), 1.0); EXPECT_EQ(CMtest.get_mixing_ndim(), 1); EXPECT_EQ(CMtest.get_mixing_gg0(), 1.0); - EXPECT_EQ(CMtest.mixing_tau, false); + EXPECT_EQ(CMtest.get_mixing_config().mixing_tau, false); EXPECT_EQ(CMtest.mixing_beta_mag, 1.6); EXPECT_EQ(CMtest.mixing_gg0_mag, 0.0); EXPECT_EQ(CMtest.mixing_gg0_min, 0.1); @@ -172,7 +172,7 @@ TEST_F(ChargeMixingTest, SetMixingTest) PARAM.input.mixing_mode = "plain"; CMtest.set_mixing(make_cfg(), ucell.omega, ucell.tpiba); EXPECT_EQ(CMtest.mixing_mode, "plain"); - EXPECT_EQ(CMtest.mixing_tau, true); + EXPECT_EQ(CMtest.get_mixing_config().mixing_tau, true); PARAM.input.mixing_beta = 1.1; std::string output; From 36da8fdc6b27d115e6f5a03053a7027f93e2480e Mon Sep 17 00:00:00 2001 From: abacus_fixer Date: Fri, 18 Sep 2026 10:56:49 +0800 Subject: [PATCH 37/37] module_charge: split member functions into charge_mixing.cpp, free functions into chg_rho_detail.h - Move mix_rho_recip/mix_rho_real/mix_rho from charge_mixing_rho.cpp to charge_mixing.cpp - Create chg_rho_detail.h for make_twobeta_mix, pack_rho_mag, unpack_rho_mag templates and mix_tau_recip declaration - charge_mixing_rho.cpp now only contains mix_tau_recip definition in module_charge::detail - Restore accidentally deleted mix_uom member function --- .../module_charge/charge_mixing.cpp | 438 ++++++++++++++ .../module_charge/charge_mixing_rho.cpp | 555 +----------------- .../module_charge/chg_rho_detail.h | 147 +++++ 3 files changed, 590 insertions(+), 550 deletions(-) create mode 100644 source/source_estate/module_charge/chg_rho_detail.h diff --git a/source/source_estate/module_charge/charge_mixing.cpp b/source/source_estate/module_charge/charge_mixing.cpp index 18ac578b80e..f2e26725a02 100644 --- a/source/source_estate/module_charge/charge_mixing.cpp +++ b/source/source_estate/module_charge/charge_mixing.cpp @@ -1,10 +1,15 @@ #include "charge_mixing.h" +#include "chg_drho.h" +#include "chg_precond.h" +#include "chg_rho_detail.h" +#include "chg_uspp.h" #include "source_io/module_parameter/parameter.h" #include "source_base/module_mixing/broyden_mixing.h" #include "source_base/module_mixing/pulay_mixing.h" #include "source_base/parallel_common.h" #include "source_base/timer.h" +#include "source_base/tool_quit.h" #include "source_hamilt/module_xc/xc_functional.h" Charge_Mixing::Charge_Mixing() @@ -284,3 +289,436 @@ void Charge_Mixing::mix_uom(std::vector& uom_in, std::vector& uo #endif return; } + +void Charge_Mixing::mix_rho_recip(Charge* chr) +{ + ModuleBase::TITLE("Charge_Mixing", "mix_rho_recip"); + ModuleBase::timer::start("Charge_Mixing", "mix_rho_recip"); + + const int nspin = cfg_.nspin; + assert(nspin==1 || nspin==2 || nspin==4); + + std::complex* rhog_in = nullptr; + std::complex* rhog_out = nullptr; + // RAII owners for the smooth / high-frequency parts on the double grid. + // The raw pointers below alias these vectors when double_grid is on, + // or alias chr->rhog[_save][0] directly when double_grid is off so the + // mixing still mutates chr in place. + std::vector> rho_sg_in; + std::vector> rho_sg_out; + std::vector> rho_hf_in; + std::vector> rho_hf_out; + // for smooth part + std::complex* rhogs_in = nullptr; + std::complex* rhogs_out = nullptr; + // for high_frequency part + std::complex* rhoghf_in = nullptr; + std::complex* rhoghf_out = nullptr; + + if ( cfg_.double_grid) + { + // divide into smooth part and high_frequency part + const int npw_smooth = this->rhopw->npw; + const int npw_dense = this->rhodpw->npw; + rho_sg_in.resize(nspin * npw_smooth); + rho_hf_in.resize(nspin * (npw_dense - npw_smooth)); + rho_sg_out.resize(nspin * npw_smooth); + rho_hf_out.resize(nspin * (npw_dense - npw_smooth)); + module_charge::split_dgrid(chr->rhog_save[0], rho_sg_in, rho_hf_in, + nspin, npw_smooth, npw_dense); + module_charge::split_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, + nspin, npw_smooth, npw_dense); + rhogs_in = rho_sg_in.data(); + rhoghf_in = rho_hf_in.data(); + rhogs_out = rho_sg_out.data(); + rhoghf_out = rho_hf_out.data(); + } + else + { + rhogs_in = chr->rhog_save[0]; + rhogs_out = chr->rhog[0]; + } + + // inner_product_recip_hartree is a hartree-like sum, unit is Ry + auto inner_product = [this](std::complex* rhog1, std::complex* rhog2) + { + return module_charge::inner_product_recip_hartree( + rhog1, rhog2, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); + }; + + // Kerker screening functor, shared by all nspin branches + auto screen = [this](std::complex* p) { + module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); + }; + + // DIIS Mixing Only for smooth part, while high_frequency part is mixed by plain mixing method. + if (nspin == 1) + { + rhog_in = rhogs_in; + rhog_out = rhogs_out; + this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhog_out); + } + else if (nspin == 2) + { + // magnetic density + const int npw = this->rhopw->npw; + std::vector> rhog_mag(npw * nspin); + std::vector> rhog_mag_save(npw * nspin); + module_charge::detail::pack_rho_mag(rhog_mag.data(), chr->rhog[0], chr->rhog[1], npw); + module_charge::detail::pack_rho_mag(rhog_mag_save.data(), chr->rhog_save[0], chr->rhog_save[1], npw); + // + rhog_in = rhog_mag_save.data(); + rhog_out = rhog_mag.data(); + auto twobeta_mix = module_charge::detail::make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhog_out); + // get rhog[is][ngmc] from rhog_mag[is*ngmc] + for (int is = 0; is < nspin; is++) + { + ModuleBase::GlobalFunc::ZEROS(chr->rhog[is], npw); + } + module_charge::detail::unpack_rho_mag(chr->rhog[0], chr->rhog[1], rhog_mag.data(), npw); + // sync rhogs_out so merge_dgrid has the latest smooth part + if ( cfg_.double_grid) + { + for (int ig = 0; ig < npw; ig++) + { + rhogs_out[ig] = chr->rhog[0][ig]; + rhogs_out[ig + npw] = chr->rhog[1][ig]; + } + } + } + else if (nspin == 4 && cfg_.mixing_angle <= 0) + { + // normal broyden mixing for {rho, mx, my, mz} + rhog_in = rhogs_in; + rhog_out = rhogs_out; + const int npw = this->rhopw->npw; + auto twobeta_mix = module_charge::detail::make_twobeta_mix>(4 * npw, npw, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhog_out); + } + else if (nspin == 4 && cfg_.mixing_angle > 0) + { + // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 + // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed + // old support see mix_rho_recip() + if ( cfg_.double_grid) + { + ModuleBase::WARNING_QUIT("Charge_Mixing", "double_grid is not supported for new mixing method yet."); + } + // rho_magabs and rho_magabs_save, zero-initialized + const int nrxx = this->rhopw->nrxx; + std::vector rho_magabs(nrxx); + std::vector rho_magabs_save(nrxx); + // calculate rho_magabs and rho_magabs_save + for (int ir = 0; ir < nrxx; ir++) + { + // |m| for rho + rho_magabs[ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] + + chr->rho[2][ir] * chr->rho[2][ir] + + chr->rho[3][ir] * chr->rho[3][ir]); + // |m| for rho_save + rho_magabs_save[ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] + + chr->rho_save[2][ir] * chr->rho_save[2][ir] + + chr->rho_save[3][ir] * chr->rho_save[3][ir]); + } + // rhog_magabs and rhog_magabs_save, zero-initialized + const int npw = this->rhopw->npw; + std::vector> rhog_magabs(npw * 2); + std::vector> rhog_magabs_save(npw * 2); + // calculate rhog_magabs and rhog_magabs_save + for (int ig = 0; ig < npw; ig++) + { + rhog_magabs[ig] = chr->rhog[0][ig]; // rho + rhog_magabs_save[ig] = chr->rhog_save[0][ig]; // rho_save + } + // FT to get rhog_magabs and rhog_magabs_save + this->rhopw->real2recip(rho_magabs.data(), rhog_magabs.data() + this->rhopw->npw); + this->rhopw->real2recip(rho_magabs_save.data(), rhog_magabs_save.data() + this->rhopw->npw); + // + rhog_in = rhog_magabs_save.data(); + rhog_out = rhog_magabs.data(); + auto twobeta_mix = module_charge::detail::make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhog_out); + // get new |m| in real space using FT + this->rhopw->recip2real(rhog_magabs.data() + this->rhopw->npw, rho_magabs.data()); + // use new |m| and angle to update {mx, my, mz} + for (int ig = 0; ig < npw; ig++) + { + chr->rhog[0][ig] = rhog_magabs[ig]; // rhog + double norm = std::sqrt(chr->rho[1][ig] * chr->rho[1][ig] + + chr->rho[2][ig] * chr->rho[2][ig] + + chr->rho[3][ig] * chr->rho[3][ig]); + if (std::abs(norm) < 1e-10) + { + continue; + } + double rescale_tmp = rho_magabs[npw + ig] / norm; + chr->rho[1][ig] *= rescale_tmp; + chr->rho[2][ig] *= rescale_tmp; + chr->rho[3][ig] *= rescale_tmp; + } + } + + if ( cfg_.double_grid) + { + // plain mixing for high_frequencies + const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; + this->mixing_highf->plain_mix(rhoghf_out, rhoghf_in, rhoghf_out, ndimhf, nullptr); + + // combine smooth part and high_frequency part; + // rho_sg_* / rho_hf_* vectors are released automatically at scope exit + module_charge::merge_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, + nspin, this->rhopw->npw, this->rhodpw->npw); + } + + // rhog to rho + if (nspin == 4 && cfg_.mixing_angle > 0) + { + // only tranfer rhog[0] + // do not support double_grid, use rhopw directly + chr->rhopw->recip2real(chr->rhog[0], chr->rho[0]); + } + else + { + for (int is = 0; is < nspin; is++) + { + // use rhodpw for double_grid + // rhodpw is the same as rhopw for ! cfg_.double_grid + this->rhodpw->recip_to_real,double,base_device::DEVICE_CPU>(chr->rhog[is], chr->rho[is]); + } + } + // For kinetic energy density + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) + { + module_charge::detail::mix_tau_recip(chr, nspin, cfg_.double_grid, + this->rhopw, this->rhodpw, + this->mixing, this->tau_mdata, this->mixing_highf); + } + + ModuleBase::timer::end("Charge_Mixing", "mix_rho_recip"); + return; +} + +void Charge_Mixing::mix_rho_real(Charge* chr) +{ + ModuleBase::TITLE("Charge_Mixing", "mix_rho_real"); + ModuleBase::timer::start("Charge_Mixing", "mix_rho_real"); + + const int nspin = cfg_.nspin; + assert(nspin==1 || nspin==2 || nspin==4); + + double* rhor_in=nullptr; + double* rhor_out=nullptr; + + // Kerker screening functor, shared by all nspin branches + auto screen = [this](double* p) { + module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); + }; + auto inner_product = [this](double* rho1, double* rho2) + { + return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); + }; + + if (nspin == 1) + { + rhor_in = chr->rho_save[0]; + rhor_out = chr->rho[0]; + this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhor_out); + } + else if (nspin == 2) + { + // magnetic density + const int nrxx = this->rhopw->nrxx; + std::vector rho_mag(nrxx * nspin); + std::vector rho_mag_save(nrxx * nspin); + module_charge::detail::pack_rho_mag(rho_mag.data(), chr->rho[0], chr->rho[1], nrxx); + module_charge::detail::pack_rho_mag(rho_mag_save.data(), chr->rho_save[0], chr->rho_save[1], nrxx); + // + rhor_in = rho_mag_save.data(); + rhor_out = rho_mag.data(); + auto twobeta_mix = module_charge::detail::make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhor_out); + // get new rho[is][nrxx] from rho_mag[is*nrxx] + for (int is = 0; is < nspin; is++) + { + ModuleBase::GlobalFunc::ZEROS(chr->rho[is], nrxx); + } + module_charge::detail::unpack_rho_mag(chr->rho[0], chr->rho[1], rho_mag.data(), nrxx); + } + else if (nspin == 4 && cfg_.mixing_angle <= 0) + { + // normal broyden mixing for {rho, mx, my, mz} + rhor_in = chr->rho_save[0]; + rhor_out = chr->rho[0]; + const int nrxx = this->rhopw->nrxx; + auto twobeta_mix = module_charge::detail::make_twobeta_mix(4 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhor_out); + } + else if (nspin == 4 && cfg_.mixing_angle > 0) + { + // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 + // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed + const int nrxx = this->rhopw->nrxx; + // rho_magabs and rho_magabs_save, zero-initialized + std::vector rho_magabs(nrxx * 2); + std::vector rho_magabs_save(nrxx * 2); + // calculate rho_magabs and rho_magabs_save + for (int ir = 0; ir < nrxx; ir++) + { + rho_magabs[ir] = chr->rho[0][ir]; // rho + rho_magabs_save[ir] = chr->rho_save[0][ir]; // rho_save + // |m| for rho + rho_magabs[nrxx + ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] + + chr->rho[2][ir] * chr->rho[2][ir] + + chr->rho[3][ir] * chr->rho[3][ir]); + // |m| for rho_save + rho_magabs_save[nrxx + ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] + + chr->rho_save[2][ir] * chr->rho_save[2][ir] + + chr->rho_save[3][ir] * chr->rho_save[3][ir]); + } + rhor_in = rho_magabs_save.data(); + rhor_out = rho_magabs.data(); + + auto twobeta_mix = module_charge::detail::make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); + this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); + this->mixing->cal_coef(this->rho_mdata, inner_product); + this->mixing->mix_data(this->rho_mdata, rhor_out); + + // use new |m| and angle to update {mx, my, mz} + for (int ir = 0; ir < nrxx; ir++) + { + chr->rho[0][ir] = rho_magabs[ir]; // rho + double norm = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] + + chr->rho[2][ir] * chr->rho[2][ir] + + chr->rho[3][ir] * chr->rho[3][ir]); + + if (norm < 1e-10) + { + continue; + } + double rescale_tmp = rho_magabs[nrxx + ir] / norm; + chr->rho[1][ir] *= rescale_tmp; + chr->rho[2][ir] *= rescale_tmp; + chr->rho[3][ir] *= rescale_tmp; + } + } + + double *taur_out=nullptr; + double *taur_in=nullptr; + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) + { + taur_in = chr->kin_r_save[0]; + taur_out = chr->kin_r[0]; + // Note: there is no kerker modification for tau because I'm not sure + // if we should have it. If necessary we can try it in the future. + this->mixing->push_data(this->tau_mdata, taur_in, taur_out, nullptr, false); + + this->mixing->mix_data(this->tau_mdata, taur_out); + } + + ModuleBase::timer::end("Charge_Mixing", "mix_rho_real"); + return; +} + + +void Charge_Mixing::mix_rho(Charge* chr) +{ + ModuleBase::TITLE("Charge_Mixing", "mix_rho"); + ModuleBase::timer::start("Charge_Mixing", "mix_rho"); + + const int nspin = cfg_.nspin; + assert(nspin==1 || nspin==2 || nspin==4); + + // the charge before mixing. + const int nrxx = chr->rhopw->nrxx; + std::vector rho123(nspin * nrxx); + for (int is = 0; is < nspin; ++is) + { + if (is == 0 || is == 3 || !cfg_.domag_z) + { + double* rho123_is = rho123.data() + is * nrxx; +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for(int ir = 0 ; ir < nrxx ; ++ir) + { + rho123_is[ir] = chr->rho[is][ir]; + } + } + } + std::vector kin_r123; + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) + { + kin_r123.resize(nspin * nrxx); + for (int is = 0; is < nspin; ++is) + { + double* kin_r123_is = kin_r123.data() + is * nrxx; +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for(int ir = 0 ; ir < nrxx ; ++ir) + { + kin_r123_is[ir] = chr->kin_r[is][ir]; + } + } + } + // --------------------Mixing Body-------------------- + if (cfg_.scf_thr_type == 1) + { + mix_rho_recip(chr); + } + else if (cfg_.scf_thr_type == 2) + { + mix_rho_real(chr); + } + // --------------------------------------------------- + + // mohan add 2012-06-05 + // rho_save is the charge before mixing + for (int is = 0; is < nspin; ++is) + { + if (is == 0 || is == 3 || !cfg_.domag_z) + { + double* rho123_is = rho123.data() + is * nrxx; +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for(int ir = 0 ; ir < nrxx ; ++ir) + { + chr->rho_save[is][ir] = rho123_is[ir]; + } + } + } + + if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) + { + for (int is = 0; is < nspin; ++is) + { + double* kin_r123_is = kin_r123.data() + is * nrxx; +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for(int ir = 0 ; ir < nrxx ; ++ir) + { + chr->kin_r_save[is][ir] = kin_r123_is[ir]; + } + } + } + + ModuleBase::timer::end("Charge_Mixing", "mix_rho"); + return; +} diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 9dce4e756f8..92552d32b05 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -1,64 +1,13 @@ -#include "charge_mixing.h" -#include "chg_drho.h" -#include "chg_precond.h" +#include "chg_rho_detail.h" #include "chg_uspp.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_hamilt/module_xc/xc_functional.h" -namespace { +namespace module_charge { +namespace detail { -/** - * @brief Create a two-beta mixing functor: mix the first nunit elements with - * mixing_beta and the rest (nunit..total) with mixing_beta_mag. - * Used for magnetic cases (nspin==2/4) where the charge channel and - * the magnetism channels use different betas. - * @tparam T element type, double (real space) or std::complex (reciprocal) - * @param total total number of elements - * @param nunit number of elements in the charge channel - * @param mixing_beta beta for the charge channel - * @param mixing_beta_mag beta for the magnetism channel - * @return mixing functor - */ -template -std::function make_twobeta_mix( - const int total, const int nunit, - const double mixing_beta, const double mixing_beta_mag) -{ - return [total, nunit, mixing_beta, mixing_beta_mag](T* out, const T* in, const T* sres) - { -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = 0; i < nunit; ++i) - { - out[i] = in[i] + mixing_beta * sres[i]; - } -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 256) -#endif - for (int i = nunit; i < total; ++i) - { - out[i] = in[i] + mixing_beta_mag * sres[i]; - } - }; -} - -/** - * @brief Mix kinetic energy density in reciprocal space. - * Handles the double-grid split/merge for the smooth and - * high-frequency parts, DIIS mixing of the smooth part, and - * plain mixing of the high-frequency part. - * @param chr pointer to Charge object (must have kin_r/kin_r_save) - * @param nspin number of spins - * @param double_grid whether double grid is used - * @param rhopw smooth grid - * @param rhodpw dense grid (same as rhopw when double_grid is off) - * @param mixing DIIS mixing object - * @param tau_mdata mixing data for tau - * @param mixing_highf plain mixing for high-frequency part (may be null when double_grid is off) - */ void mix_tau_recip(Charge* chr, const int nspin, const bool double_grid, @@ -162,499 +111,5 @@ void mix_tau_recip(Charge* chr, ModuleBase::timer::end("Charge_Mixing", "mix_tau_recip"); } -/** - * @brief Pack charge and magnetism into interleaved layout: - * out[0..n] = d0 + d1 (charge channel) - * out[n..2n] = d0 - d1 (magnetism channel) - * @tparam T double (real space) or std::complex (reciprocal) - * @param out output buffer, size >= 2*n - * @param d0 first component (e.g. chr->rho[0] or chr->rhog[0]) - * @param d1 second component - * @param n number of elements per component - */ -template -void pack_rho_mag(T* out, const T* d0, const T* d1, const int n) -{ - if (out == nullptr || d0 == nullptr || d1 == nullptr) - { - ModuleBase::WARNING_QUIT("pack_rho_mag", "pointer is null"); - } - if (n < 0) - { - ModuleBase::WARNING_QUIT("pack_rho_mag", "n must be >= 0"); - } -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for (int i = 0; i < n; ++i) - { - out[i] = d0[i] + d1[i]; - out[i + n] = d0[i] - d1[i]; - } -} - -/** - * @brief Unpack interleaved layout back to charge and magnetism components: - * d0[i] = 0.5 * (in[i] + in[i+n]) - * d1[i] = 0.5 * (in[i] - in[i+n]) - * @tparam T double (real space) or std::complex (reciprocal) - * @param d0 output first component (e.g. chr->rho[0] or chr->rhog[0]) - * @param d1 output second component - * @param in input buffer, size >= 2*n - * @param n number of elements per component - */ -template -void unpack_rho_mag(T* d0, T* d1, const T* in, const int n) -{ - if (d0 == nullptr || d1 == nullptr || in == nullptr) - { - ModuleBase::WARNING_QUIT("unpack_rho_mag", "pointer is null"); - } - if (n < 0) - { - ModuleBase::WARNING_QUIT("unpack_rho_mag", "n must be >= 0"); - } -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for (int i = 0; i < n; ++i) - { - d0[i] = 0.5 * (in[i] + in[i + n]); - d1[i] = 0.5 * (in[i] - in[i + n]); - } -} - -} // namespace - -void Charge_Mixing::mix_rho_recip(Charge* chr) -{ - ModuleBase::TITLE("Charge_Mixing", "mix_rho_recip"); - ModuleBase::timer::start("Charge_Mixing", "mix_rho_recip"); - - const int nspin = cfg_.nspin; - assert(nspin==1 || nspin==2 || nspin==4); - - std::complex* rhog_in = nullptr; - std::complex* rhog_out = nullptr; - // RAII owners for the smooth / high-frequency parts on the double grid. - // The raw pointers below alias these vectors when double_grid is on, - // or alias chr->rhog[_save][0] directly when double_grid is off so the - // mixing still mutates chr in place. - std::vector> rho_sg_in; - std::vector> rho_sg_out; - std::vector> rho_hf_in; - std::vector> rho_hf_out; - // for smooth part - std::complex* rhogs_in = nullptr; - std::complex* rhogs_out = nullptr; - // for high_frequency part - std::complex* rhoghf_in = nullptr; - std::complex* rhoghf_out = nullptr; - - if ( cfg_.double_grid) - { - // divide into smooth part and high_frequency part - const int npw_smooth = this->rhopw->npw; - const int npw_dense = this->rhodpw->npw; - rho_sg_in.resize(nspin * npw_smooth); - rho_hf_in.resize(nspin * (npw_dense - npw_smooth)); - rho_sg_out.resize(nspin * npw_smooth); - rho_hf_out.resize(nspin * (npw_dense - npw_smooth)); - module_charge::split_dgrid(chr->rhog_save[0], rho_sg_in, rho_hf_in, - nspin, npw_smooth, npw_dense); - module_charge::split_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, - nspin, npw_smooth, npw_dense); - rhogs_in = rho_sg_in.data(); - rhoghf_in = rho_hf_in.data(); - rhogs_out = rho_sg_out.data(); - rhoghf_out = rho_hf_out.data(); - } - else - { - rhogs_in = chr->rhog_save[0]; - rhogs_out = chr->rhog[0]; - } - - // inner_product_recip_hartree is a hartree-like sum, unit is Ry - auto inner_product = [this](std::complex* rhog1, std::complex* rhog2) - { - return module_charge::inner_product_recip_hartree( - rhog1, rhog2, *this->rhopw, this->cfg_, *this->omega, *this->tpiba); - }; - - // Kerker screening functor, shared by all nspin branches - auto screen = [this](std::complex* p) { - module_charge::kerker_screen_recip(this->cfg_, this->rhopw, *this->tpiba, p); - }; - - // DIIS Mixing Only for smooth part, while high_frequency part is mixed by plain mixing method. - if (nspin == 1) - { - rhog_in = rhogs_in; - rhog_out = rhogs_out; - this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhog_out); - } - else if (nspin == 2) - { - // magnetic density - const int npw = this->rhopw->npw; - std::vector> rhog_mag(npw * nspin); - std::vector> rhog_mag_save(npw * nspin); - pack_rho_mag(rhog_mag.data(), chr->rhog[0], chr->rhog[1], npw); - pack_rho_mag(rhog_mag_save.data(), chr->rhog_save[0], chr->rhog_save[1], npw); - // - rhog_in = rhog_mag_save.data(); - rhog_out = rhog_mag.data(); - auto twobeta_mix = make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhog_out); - // get rhog[is][ngmc] from rhog_mag[is*ngmc] - for (int is = 0; is < nspin; is++) - { - ModuleBase::GlobalFunc::ZEROS(chr->rhog[is], npw); - } - unpack_rho_mag(chr->rhog[0], chr->rhog[1], rhog_mag.data(), npw); - // sync rhogs_out so merge_dgrid has the latest smooth part - if ( cfg_.double_grid) - { - for (int ig = 0; ig < npw; ig++) - { - rhogs_out[ig] = chr->rhog[0][ig]; - rhogs_out[ig + npw] = chr->rhog[1][ig]; - } - } - } - else if (nspin == 4 && cfg_.mixing_angle <= 0) - { - // normal broyden mixing for {rho, mx, my, mz} - rhog_in = rhogs_in; - rhog_out = rhogs_out; - const int npw = this->rhopw->npw; - auto twobeta_mix = make_twobeta_mix>(4 * npw, npw, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhog_out); - } - else if (nspin == 4 && cfg_.mixing_angle > 0) - { - // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 - // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed - // old support see mix_rho_recip() - if ( cfg_.double_grid) - { - ModuleBase::WARNING_QUIT("Charge_Mixing", "double_grid is not supported for new mixing method yet."); - } - // rho_magabs and rho_magabs_save, zero-initialized - const int nrxx = this->rhopw->nrxx; - std::vector rho_magabs(nrxx); - std::vector rho_magabs_save(nrxx); - // calculate rho_magabs and rho_magabs_save - for (int ir = 0; ir < nrxx; ir++) - { - // |m| for rho - rho_magabs[ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] - + chr->rho[2][ir] * chr->rho[2][ir] - + chr->rho[3][ir] * chr->rho[3][ir]); - // |m| for rho_save - rho_magabs_save[ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] - + chr->rho_save[2][ir] * chr->rho_save[2][ir] - + chr->rho_save[3][ir] * chr->rho_save[3][ir]); - } - // rhog_magabs and rhog_magabs_save, zero-initialized - const int npw = this->rhopw->npw; - std::vector> rhog_magabs(npw * 2); - std::vector> rhog_magabs_save(npw * 2); - // calculate rhog_magabs and rhog_magabs_save - for (int ig = 0; ig < npw; ig++) - { - rhog_magabs[ig] = chr->rhog[0][ig]; // rho - rhog_magabs_save[ig] = chr->rhog_save[0][ig]; // rho_save - } - // FT to get rhog_magabs and rhog_magabs_save - this->rhopw->real2recip(rho_magabs.data(), rhog_magabs.data() + this->rhopw->npw); - this->rhopw->real2recip(rho_magabs_save.data(), rhog_magabs_save.data() + this->rhopw->npw); - // - rhog_in = rhog_magabs_save.data(); - rhog_out = rhog_magabs.data(); - auto twobeta_mix = make_twobeta_mix>(2 * npw, npw, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhog_in, rhog_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhog_out); - // get new |m| in real space using FT - this->rhopw->recip2real(rhog_magabs.data() + this->rhopw->npw, rho_magabs.data()); - // use new |m| and angle to update {mx, my, mz} - for (int ig = 0; ig < npw; ig++) - { - chr->rhog[0][ig] = rhog_magabs[ig]; // rhog - double norm = std::sqrt(chr->rho[1][ig] * chr->rho[1][ig] - + chr->rho[2][ig] * chr->rho[2][ig] - + chr->rho[3][ig] * chr->rho[3][ig]); - if (std::abs(norm) < 1e-10) - { - continue; - } - double rescale_tmp = rho_magabs[npw + ig] / norm; - chr->rho[1][ig] *= rescale_tmp; - chr->rho[2][ig] *= rescale_tmp; - chr->rho[3][ig] *= rescale_tmp; - } - } - - if ( cfg_.double_grid) - { - // plain mixing for high_frequencies - const int ndimhf = (this->rhodpw->npw - this->rhopw->npw) * nspin; - this->mixing_highf->plain_mix(rhoghf_out, rhoghf_in, rhoghf_out, ndimhf, nullptr); - - // combine smooth part and high_frequency part; - // rho_sg_* / rho_hf_* vectors are released automatically at scope exit - module_charge::merge_dgrid(chr->rhog[0], rho_sg_out, rho_hf_out, - nspin, this->rhopw->npw, this->rhodpw->npw); - } - - // rhog to rho - if (nspin == 4 && cfg_.mixing_angle > 0) - { - // only tranfer rhog[0] - // do not support double_grid, use rhopw directly - chr->rhopw->recip2real(chr->rhog[0], chr->rho[0]); - } - else - { - for (int is = 0; is < nspin; is++) - { - // use rhodpw for double_grid - // rhodpw is the same as rhopw for ! cfg_.double_grid - this->rhodpw->recip_to_real,double,base_device::DEVICE_CPU>(chr->rhog[is], chr->rho[is]); - } - } - // For kinetic energy density - if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) - { - mix_tau_recip(chr, nspin, cfg_.double_grid, - this->rhopw, this->rhodpw, - this->mixing, this->tau_mdata, this->mixing_highf); - } - - ModuleBase::timer::end("Charge_Mixing", "mix_rho_recip"); - return; -} - -void Charge_Mixing::mix_rho_real(Charge* chr) -{ - ModuleBase::TITLE("Charge_Mixing", "mix_rho_real"); - ModuleBase::timer::start("Charge_Mixing", "mix_rho_real"); - - const int nspin = cfg_.nspin; - assert(nspin==1 || nspin==2 || nspin==4); - - double* rhor_in=nullptr; - double* rhor_out=nullptr; - - // Kerker screening functor, shared by all nspin branches - auto screen = [this](double* p) { - module_charge::kerker_screen_real(this->cfg_, this->rhopw, *this->tpiba, p); - }; - auto inner_product = [this](double* rho1, double* rho2) - { - return module_charge::inner_product_real(rho1, rho2, *this->rhopw, this->cfg_); - }; - - if (nspin == 1) - { - rhor_in = chr->rho_save[0]; - rhor_out = chr->rho[0]; - this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhor_out); - } - else if (nspin == 2) - { - // magnetic density - const int nrxx = this->rhopw->nrxx; - std::vector rho_mag(nrxx * nspin); - std::vector rho_mag_save(nrxx * nspin); - pack_rho_mag(rho_mag.data(), chr->rho[0], chr->rho[1], nrxx); - pack_rho_mag(rho_mag_save.data(), chr->rho_save[0], chr->rho_save[1], nrxx); - // - rhor_in = rho_mag_save.data(); - rhor_out = rho_mag.data(); - auto twobeta_mix = make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhor_out); - // get new rho[is][nrxx] from rho_mag[is*nrxx] - for (int is = 0; is < nspin; is++) - { - ModuleBase::GlobalFunc::ZEROS(chr->rho[is], nrxx); - } - unpack_rho_mag(chr->rho[0], chr->rho[1], rho_mag.data(), nrxx); - } - else if (nspin == 4 && cfg_.mixing_angle <= 0) - { - // normal broyden mixing for {rho, mx, my, mz} - rhor_in = chr->rho_save[0]; - rhor_out = chr->rho[0]; - const int nrxx = this->rhopw->nrxx; - auto twobeta_mix = make_twobeta_mix(4 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhor_out); - } - else if (nspin == 4 && cfg_.mixing_angle > 0) - { - // special broyden mixing for {rho, |m|} proposed by J. Phys. Soc. Jpn. 82 (2013) 114706 - // here only consider the case of mixing_angle = 1, which mean only change |m| and keep angle fixed - const int nrxx = this->rhopw->nrxx; - // rho_magabs and rho_magabs_save, zero-initialized - std::vector rho_magabs(nrxx * 2); - std::vector rho_magabs_save(nrxx * 2); - // calculate rho_magabs and rho_magabs_save - for (int ir = 0; ir < nrxx; ir++) - { - rho_magabs[ir] = chr->rho[0][ir]; // rho - rho_magabs_save[ir] = chr->rho_save[0][ir]; // rho_save - // |m| for rho - rho_magabs[nrxx + ir] = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] - + chr->rho[2][ir] * chr->rho[2][ir] - + chr->rho[3][ir] * chr->rho[3][ir]); - // |m| for rho_save - rho_magabs_save[nrxx + ir] = std::sqrt(chr->rho_save[1][ir] * chr->rho_save[1][ir] - + chr->rho_save[2][ir] * chr->rho_save[2][ir] - + chr->rho_save[3][ir] * chr->rho_save[3][ir]); - } - rhor_in = rho_magabs_save.data(); - rhor_out = rho_magabs.data(); - - auto twobeta_mix = make_twobeta_mix(2 * nrxx, nrxx, this->mixing_beta, this->mixing_beta_mag); - this->mixing->push_data(this->rho_mdata, rhor_in, rhor_out, screen, twobeta_mix, true); - this->mixing->cal_coef(this->rho_mdata, inner_product); - this->mixing->mix_data(this->rho_mdata, rhor_out); - - // use new |m| and angle to update {mx, my, mz} - for (int ir = 0; ir < nrxx; ir++) - { - chr->rho[0][ir] = rho_magabs[ir]; // rho - double norm = std::sqrt(chr->rho[1][ir] * chr->rho[1][ir] - + chr->rho[2][ir] * chr->rho[2][ir] - + chr->rho[3][ir] * chr->rho[3][ir]); - - if (norm < 1e-10) - { - continue; - } - double rescale_tmp = rho_magabs[nrxx + ir] / norm; - chr->rho[1][ir] *= rescale_tmp; - chr->rho[2][ir] *= rescale_tmp; - chr->rho[3][ir] *= rescale_tmp; - } - } - - double *taur_out=nullptr; - double *taur_in=nullptr; - if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) - { - taur_in = chr->kin_r_save[0]; - taur_out = chr->kin_r[0]; - // Note: there is no kerker modification for tau because I'm not sure - // if we should have it. If necessary we can try it in the future. - this->mixing->push_data(this->tau_mdata, taur_in, taur_out, nullptr, false); - - this->mixing->mix_data(this->tau_mdata, taur_out); - } - - ModuleBase::timer::end("Charge_Mixing", "mix_rho_real"); - return; -} - - -void Charge_Mixing::mix_rho(Charge* chr) -{ - ModuleBase::TITLE("Charge_Mixing", "mix_rho"); - ModuleBase::timer::start("Charge_Mixing", "mix_rho"); - - const int nspin = cfg_.nspin; - assert(nspin==1 || nspin==2 || nspin==4); - - // the charge before mixing. - const int nrxx = chr->rhopw->nrxx; - std::vector rho123(nspin * nrxx); - for (int is = 0; is < nspin; ++is) - { - if (is == 0 || is == 3 || !cfg_.domag_z) - { - double* rho123_is = rho123.data() + is * nrxx; -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for(int ir = 0 ; ir < nrxx ; ++ir) - { - rho123_is[ir] = chr->rho[is][ir]; - } - } - } - std::vector kin_r123; - if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) - { - kin_r123.resize(nspin * nrxx); - for (int is = 0; is < nspin; ++is) - { - double* kin_r123_is = kin_r123.data() + is * nrxx; -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for(int ir = 0 ; ir < nrxx ; ++ir) - { - kin_r123_is[ir] = chr->kin_r[is][ir]; - } - } - } - // --------------------Mixing Body-------------------- - if (cfg_.scf_thr_type == 1) - { - mix_rho_recip(chr); - } - else if (cfg_.scf_thr_type == 2) - { - mix_rho_real(chr); - } - // --------------------------------------------------- - - // mohan add 2012-06-05 - // rho_save is the charge before mixing - for (int is = 0; is < nspin; ++is) - { - if (is == 0 || is == 3 || !cfg_.domag_z) - { - double* rho123_is = rho123.data() + is * nrxx; -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for(int ir = 0 ; ir < nrxx ; ++ir) - { - chr->rho_save[is][ir] = rho123_is[ir]; - } - } - } - - if ((XC_Functional::get_ked_flag()) && cfg_.mixing_tau) - { - for (int is = 0; is < nspin; ++is) - { - double* kin_r123_is = kin_r123.data() + is * nrxx; -#ifdef _OPENMP -#pragma omp parallel for schedule(static, 512) -#endif - for(int ir = 0 ; ir < nrxx ; ++ir) - { - chr->kin_r_save[is][ir] = kin_r123_is[ir]; - } - } - } - - ModuleBase::timer::end("Charge_Mixing", "mix_rho"); - return; -} +} // namespace detail +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_rho_detail.h b/source/source_estate/module_charge/chg_rho_detail.h new file mode 100644 index 00000000000..b957136f81c --- /dev/null +++ b/source/source_estate/module_charge/chg_rho_detail.h @@ -0,0 +1,147 @@ +#ifndef CHG_RHO_DETAIL_H +#define CHG_RHO_DETAIL_H + +// Internal helpers for charge density mixing (mix_rho_recip/mix_rho_real). +// Not part of the public module_charge API: only charge_mixing.cpp, +// charge_mixing_rho.cpp and the charge mixing unit test are expected to +// include this header. + +#include +#include + +#include "charge.h" +#include "chg_mix_cfg.h" +#include "source_base/module_mixing/mixing.h" +#include "source_base/module_mixing/plain_mixing.h" +#include "source_base/tool_quit.h" + +namespace module_charge +{ +namespace detail +{ + +/** + * @brief Create a two-beta mixing functor: mix the first nunit elements with + * mixing_beta and the rest (nunit..total) with mixing_beta_mag. + * Used for magnetic cases (nspin==2/4) where the charge channel and + * the magnetism channels use different betas. + * @tparam T element type, double (real space) or std::complex (reciprocal) + * @param total total number of elements + * @param nunit number of elements in the charge channel + * @param mixing_beta beta for the charge channel + * @param mixing_beta_mag beta for the magnetism channel + * @return mixing functor + */ +template +std::function make_twobeta_mix( + const int total, const int nunit, + const double mixing_beta, const double mixing_beta_mag) +{ + return [total, nunit, mixing_beta, mixing_beta_mag](T* out, const T* in, const T* sres) + { +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = 0; i < nunit; ++i) + { + out[i] = in[i] + mixing_beta * sres[i]; + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 256) +#endif + for (int i = nunit; i < total; ++i) + { + out[i] = in[i] + mixing_beta_mag * sres[i]; + } + }; +} + +/** + * @brief Pack charge and magnetism into interleaved layout: + * out[0..n] = d0 + d1 (charge channel) + * out[n..2n] = d0 - d1 (magnetism channel) + * @tparam T double (real space) or std::complex (reciprocal) + * @param out output buffer, size >= 2*n + * @param d0 first component (e.g. chr->rho[0] or chr->rhog[0]) + * @param d1 second component + * @param n number of elements per component + */ +template +void pack_rho_mag(T* out, const T* d0, const T* d1, const int n) +{ + if (out == nullptr || d0 == nullptr || d1 == nullptr) + { + ModuleBase::WARNING_QUIT("pack_rho_mag", "pointer is null"); + } + if (n < 0) + { + ModuleBase::WARNING_QUIT("pack_rho_mag", "n must be >= 0"); + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int i = 0; i < n; ++i) + { + out[i] = d0[i] + d1[i]; + out[i + n] = d0[i] - d1[i]; + } +} + +/** + * @brief Unpack interleaved layout back to charge and magnetism components: + * d0[i] = 0.5 * (in[i] + in[i+n]) + * d1[i] = 0.5 * (in[i] - in[i+n]) + * @tparam T double (real space) or std::complex (reciprocal) + * @param d0 output first component (e.g. chr->rho[0] or chr->rhog[0]) + * @param d1 output second component + * @param in input buffer, size >= 2*n + * @param n number of elements per component + */ +template +void unpack_rho_mag(T* d0, T* d1, const T* in, const int n) +{ + if (d0 == nullptr || d1 == nullptr || in == nullptr) + { + ModuleBase::WARNING_QUIT("unpack_rho_mag", "pointer is null"); + } + if (n < 0) + { + ModuleBase::WARNING_QUIT("unpack_rho_mag", "n must be >= 0"); + } +#ifdef _OPENMP +#pragma omp parallel for schedule(static, 512) +#endif + for (int i = 0; i < n; ++i) + { + d0[i] = 0.5 * (in[i] + in[i + n]); + d1[i] = 0.5 * (in[i] - in[i + n]); + } +} + +/** + * @brief Mix kinetic energy density in reciprocal space. + * Handles the double-grid split/merge for the smooth and + * high-frequency parts, DIIS mixing of the smooth part, and + * plain mixing of the high-frequency part. + * @param chr pointer to Charge object (must have kin_r/kin_r_save) + * @param nspin number of spins + * @param double_grid whether double grid is used + * @param rhopw smooth grid + * @param rhodpw dense grid (same as rhopw when double_grid is off) + * @param mixing DIIS mixing object + * @param tau_mdata mixing data for tau + * @param mixing_highf plain mixing for high-frequency part (may be null when double_grid is off) + */ +void mix_tau_recip(Charge* chr, + const int nspin, + const bool double_grid, + ModulePW::PW_Basis* rhopw, + ModulePW::PW_Basis* rhodpw, + Base_Mixing::Mixing* mixing, + Base_Mixing::Mixing_Data& tau_mdata, + Base_Mixing::Plain_Mixing* mixing_highf); + +} // namespace detail +} // namespace module_charge + +#endif // CHG_RHO_DETAIL_H