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/Makefile.Objects b/source/Makefile.Objects index 27158e05069..cedd880e8d6 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\ @@ -823,18 +824,21 @@ OBJS_SRCPW=h_ewald_pw.o\ vnl_pw_grad.o\ vnl_pw_init_vnl.o\ vnl_pw_qrad.o\ - chgmixing.o\ - gint_prec_ctrl.o\ + chg_routine.o\ charge.o\ + chg_atomic.o\ charge_init.o\ - charge_mpi.o\ - charge_extra.o\ + chg_tools.o\ + chg_parallel.o\ + chg_extra.o\ charge_mixing.o\ - charge_mixing_dmr.o\ - mix_resid.o\ - mix_precond.o\ + chg_dmr.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\ @@ -892,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_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_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_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_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.cpp b/source/source_esolver/esolver_ks.cpp index 65bcd52665f..37d9157c64d 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -5,7 +5,8 @@ #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" #include "source_io/module_energy/write_eig_occ.h" @@ -65,9 +66,24 @@ 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; + 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(); //! 4) setup plane wave for electronic wave functions @@ -97,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") { @@ -114,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); @@ -273,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_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index f28b0c98d98..47bea5dfedc 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -7,7 +7,8 @@ #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_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" @@ -18,7 +19,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() @@ -234,7 +235,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 +507,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); @@ -564,7 +565,24 @@ 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); + // 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_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_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..871e725506b 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" @@ -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 @@ -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 152c7166b94..e4eb55ebb70 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -27,20 +27,21 @@ 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/gint_prec_ctrl.cpp + module_charge/chg_routine.cpp module_charge/charge.cpp + module_charge/chg_atomic.cpp module_charge/charge_init.cpp - module_charge/charge_mpi.cpp - module_charge/charge_extra.cpp + module_charge/chg_tools.cpp + module_charge/chg_parallel.cpp + module_charge/chg_extra.cpp module_charge/charge_mixing.cpp - module_charge/charge_mixing_dmr.cpp - module_charge/mix_resid.cpp - module_charge/mix_precond.cpp + module_charge/chg_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/symm_rho.cpp - module_charge/symm_rhog.cpp + module_charge/chg_symm.cpp + module_charge/chg_uspp.cpp + module_charge/chg_symm_detail.cpp rhog_io.cpp write_elecstat_pot.cpp write_init.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/init_scf.cpp b/source/source_estate/init_scf.cpp index 4e5699e8074..3bf2e495a74 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/chg_tools.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); + module_charge::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.cpp b/source/source_estate/module_charge/charge.cpp index 5f25aefe06f..70e8c50922e 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 "chg_tools.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -52,14 +53,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() @@ -72,12 +73,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; @@ -90,10 +86,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; @@ -116,15 +112,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 +133,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); } } @@ -179,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 module_charge::sum_rho(this->rho, nspin0, this->nrxx, *this->omega_, this->rhopw->nxyz); } void Charge::renormalize_rho() @@ -233,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++) @@ -666,19 +217,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 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.h b/source/source_estate/module_charge/charge.h index 785985da40e..b0e4654d475 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" @@ -21,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" @@ -45,13 +53,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: @@ -88,63 +98,18 @@ 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); - 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; + 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: - /** - * @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 @@ -159,6 +124,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 672d800f718..8967604ea29 100644 --- a/source/source_estate/module_charge/charge_init.cpp +++ b/source/source_estate/module_charge/charge_init.cpp @@ -2,15 +2,13 @@ #include #include "charge.h" +#include "chg_atomic.h" +#include "chg_tools.h" #include "source_base/global_function.h" #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" @@ -47,163 +45,188 @@ 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; + 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); - // 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)) + 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; + std::stringstream ssc; - if(nspin==1) - { - ssc << PARAM.globalv.global_readin_dir << "chg.cube"; - } - else - { - ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; - } + if(nspin==1) + { + ssc << PARAM.globalv.global_readin_dir << "chg.cube"; + } + else + { + ssc << PARAM.globalv.global_readin_dir << "chgs" << is + 1 << ".cube"; + } - 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)) + 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 { - GlobalV::ofs_running << " Read electron density from file: " << ssc.str() << std::endl; + std::cout << " Incomplete electron density file." << std::endl; + read_error = true; + break; } - else if (is > 0) // nspin=2 or 4 + else if (is == 2) // read 2 files when nspin=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 << " Didn't read in the electron density but would rearrange it later. " - << std::endl; - } - 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; - } - } + GlobalV::ofs_running << " Didn't read in the electron density but would rearrange it later. " + << std::endl; } - else + else if (is == 3) // read 2 files when nspin=4 { - read_error = true; - break; + 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; + } } + } - 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) { 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 @@ -225,7 +248,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,208 +281,25 @@ void Charge::init_rho(const UnitCell& ucell, } GlobalC::restart.info_load.load_charge_finish = true; } - - if (PARAM.inp.init_chg == "wfc") - { - if (wfcpw == nullptr) - { - 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); - - 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); - } } -//========================================================== -// 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 +void Charge::init_rho_from_wfc(ModuleSymmetry::Symmetry& symm, + const int nspin, + const void* klist, + const void* wfcpw) { - 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 + if (wfcpw == nullptr) { - // here the case where the charge is in analytic form, - // check old version before 2008-12-9 + ModuleBase::WARNING_QUIT("Charge::init_rho", "wfc is only supported for PW-KSDFT."); } - }; // 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); + const ModulePW::PW_Basis_K* pw_wfc = reinterpret_cast(const_cast(wfcpw)); + const K_Vectors* kv = reinterpret_cast(klist); - return; + 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); } diff --git a/source/source_estate/module_charge/charge_mixing.cpp b/source/source_estate/module_charge/charge_mixing.cpp index 640fa480c0a..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() @@ -16,42 +21,36 @@ 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, - 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_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 +58,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 +87,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 +132,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 +142,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 +153,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 ((XC_Functional::get_ked_flag()) && cfg_.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)); } } @@ -201,7 +200,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(); } @@ -290,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.h b/source/source_estate/module_charge/charge_mixing.h index c24a866df91..700ff37bf5b 100644 --- a/source/source_estate/module_charge/charge_mixing.h +++ b/source/source_estate/module_charge/charge_mixing.h @@ -1,9 +1,10 @@ #ifndef CHARGE_MIXING_H #define CHARGE_MIXING_H #include "charge.h" -#include "source_estate/module_dm/density_matrix.h" +#include "chg_mix_cfg.h" #include "source_base/module_mixing/mixing.h" #include "source_base/module_mixing/plain_mixing.h" +#include class Charge_Mixing { @@ -13,39 +14,23 @@ 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(); ~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); @@ -57,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 @@ -90,14 +62,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(); @@ -116,6 +81,19 @@ 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() + */ + 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 @@ -138,12 +116,13 @@ 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 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 @@ -152,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. @@ -169,60 +146,6 @@ class Charge_Mixing * @param chr pointer of Charge object */ 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 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 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); - - /** - * @brief Inner product of two double vectors - * - */ - double inner_product_real(double* rho1, double* rho2); - - /** - * @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_dmr.cpp b/source/source_estate/module_charge/charge_mixing_dmr.cpp deleted file mode 100644 index 368ed63f257..00000000000 --- a/source/source_estate/module_charge/charge_mixing_dmr.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "charge_mixing.h" - -#include "source_io/module_parameter/parameter.h" -#include "source_base/timer.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(); - ModuleBase::timer::end("Charge_Mixing", "allocate_mixing_dmr"); - - return; -} - -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 = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - 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); - //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(); - 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; - } - - ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); - - return; -} - -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 = std::bind(&Charge_Mixing::Kerker_screen_real, this, std::placeholders::_1); - 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); - //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(); - 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; - } - - ModuleBase::timer::end("Charge_Mixing", "mix_dmr"); - - return; -} \ No newline at end of file diff --git a/source/source_estate/module_charge/charge_mixing_rho.cpp b/source/source_estate/module_charge/charge_mixing_rho.cpp index 3d8f302b572..92552d32b05 100644 --- a/source/source_estate/module_charge/charge_mixing_rho.cpp +++ b/source/source_estate/module_charge/charge_mixing_rho.cpp @@ -1,613 +1,115 @@ -#include "charge_mixing.h" -#include "source_io/module_parameter/parameter.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" -void Charge_Mixing::mix_rho_recip(Charge* chr) +namespace module_charge { +namespace detail { + +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_rho_recip"); - ModuleBase::timer::start("Charge_Mixing", "mix_rho_recip"); - - const int nspin = PARAM.inp.nspin; - assert(nspin==1 || nspin==2 || nspin==4); + ModuleBase::TITLE("Charge_Mixing", "mix_tau_recip"); + ModuleBase::timer::start("Charge_Mixing", "mix_tau_recip"); - std::complex* rhog_in = nullptr; - std::complex* rhog_out = nullptr; - // for smooth part - std::complex* rhogs_in = chr->rhog_save[0]; - std::complex* rhogs_out = chr->rhog[0]; - // for high_frequency part - std::complex* rhoghf_in = nullptr; - std::complex* rhoghf_out = nullptr; - - if ( PARAM.globalv.double_grid) + if (chr == nullptr) { - // 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); + ModuleBase::WARNING_QUIT("mix_tau_recip", "chr is null"); } - - // 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); - - // DIIS Mixing Only for smooth part, while high_frequency part is mixed by plain mixing method. - if (nspin == 1) + if (rhopw == nullptr || rhodpw == nullptr) { - rhog_in = rhogs_in; - rhog_out = rhogs_out; - auto screen = std::bind(&Charge_Mixing::Kerker_screen_recip, this, std::placeholders::_1); - 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); + ModuleBase::WARNING_QUIT("mix_tau_recip", "grid pointer is null"); } - else if (nspin == 2) + if (mixing == nullptr) { - // 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); - // 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]; - } - // - rhog_in = rhog_mag_save; - rhog_out = rhog_mag; - // - 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]; - } - }; - 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); - } - 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]); - } - // delete - delete[] rhog_mag; - delete[] rhog_mag_save; - // get rhogs_out for combine_data() - if ( PARAM.globalv.double_grid) - { - for (int ig = 0; ig < npw; ig++) - { - rhogs_out[ig] = chr->rhog[0][ig]; - rhogs_out[ig + npw] = chr->rhog[1][ig]; - } - } + ModuleBase::WARNING_QUIT("mix_tau_recip", "mixing is null"); } - else if (nspin == 4 && PARAM.inp.mixing_angle <= 0) + if (nspin < 1) { - // normal broyden mixing for {rho, mx, my, mz} - 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 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]; - } - }; - 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); + ModuleBase::WARNING_QUIT("mix_tau_recip", "nspin must be >= 1"); } - else if (nspin == 4 && PARAM.inp.mixing_angle > 0) + if (double_grid && mixing_highf == nullptr) { - // 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) - { - ModuleBase::WARNING_QUIT("Charge_Mixing", "double_grid is not supported for new mixing method yet."); - } - // allocate memory for rho_magabs and rho_magabs_save - 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); - // 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]); - } - // allocate memory for rhog_magabs and rhog_magabs_save - 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); - // 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, rhog_magabs + this->rhopw->npw); - this->rhopw->real2recip(rho_magabs_save, rhog_magabs_save + this->rhopw->npw); - // - rhog_in = rhog_magabs_save; - rhog_out = rhog_magabs; - 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]; - } - }; - 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); - // 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; - } - // delete - delete[] rhog_magabs; - delete[] rhog_magabs_save; - delete[] rho_magabs; - delete[] rho_magabs_save; + ModuleBase::WARNING_QUIT("mix_tau_recip", "mixing_highf is null when double_grid is on"); } - if ( PARAM.globalv.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 - combine_data(chr->rhog[0], rhogs_out, rhoghf_out); - clean_data(rhogs_in, rhoghf_in); - } - - // rhog to rho - if (nspin == 4 && PARAM.inp.mixing_angle > 0) + 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) { - // only tranfer rhog[0] - // do not support double_grid, use rhopw directly - chr->rhopw->recip2real(chr->rhog[0], chr->rho[0]); + 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 { - for (int is = 0; is < nspin; is++) - { - // use rhodpw for double_grid - // rhodpw is the same as rhopw for ! PARAM.globalv.double_grid - this->rhodpw->recip_to_real,double,base_device::DEVICE_CPU>(chr->rhog[is], chr->rho[is]); - } + taugs_in = kin_g_save.data(); + taugs_out = kin_g.data(); } - // For kinetic energy density - if ((XC_Functional::get_ked_flag()) && 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]); - } - // 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(); - // for high frequency part - std::complex*taughf_in = nullptr, *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); - } - - // 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 - combine_data(kin_g.data(), taugs_out, taughf_out); - clean_data(taugs_in, taughf_in); - } + // 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); - // 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_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 = PARAM.inp.nspin; - assert(nspin==1 || nspin==2 || nspin==4); - - double* rhor_in=nullptr; - double* rhor_out=nullptr; - - if (nspin == 1) - { - 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 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->rho_mdata, rhor_out); - } - 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); - // 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]; - } - // - rhor_in = rho_mag_save; - rhor_out = rho_mag; - 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]; - } - }; - 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); - 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]); - } - // delete - delete[] rho_mag; - delete[] rho_mag_save; - } - else if (nspin == 4 && PARAM.inp.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 = 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]; - } - }; - 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); - 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) + if (double_grid) { - // 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); - // 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; - rhor_out = rho_magabs; + // 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); - 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]; - } - }; - 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); - 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; - } - // delete - delete[] rho_magabs; - delete[] rho_magabs_save; - } - - double *taur_out=nullptr; - double *taur_in=nullptr; - if ((XC_Functional::get_ked_flag()) && 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 = PARAM.inp.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 || !PARAM.globalv.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()) && 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 (PARAM.inp.scf_thr_type == 1) - { - mix_rho_recip(chr); - } - else if (PARAM.inp.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 || !PARAM.globalv.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]; - } - } + // 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); } - if ((XC_Functional::get_ked_flag()) && mixing_tau) + // kin_g to kin_r + for (int is = 0; is < nspin; is++) { - 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]; - } - } + rhodpw->recip2real(&kin_g[is * rhodpw->npw], chr->kin_r[is]); } - if (new_e_iteration) - { - new_e_iteration = false; - } - - ModuleBase::timer::end("Charge_Mixing", "mix_rho"); - return; + ModuleBase::timer::end("Charge_Mixing", "mix_tau_recip"); } + +} // namespace detail +} // namespace module_charge 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/module_charge/chg_atomic.cpp b/source/source_estate/module_charge/chg_atomic.cpp new file mode 100644 index 00000000000..499781b96bc --- /dev/null +++ b/source/source_estate/module_charge/chg_atomic.cpp @@ -0,0 +1,442 @@ +#include "chg_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/chg_atomic.h b/source/source_estate/module_charge/chg_atomic.h new file mode 100644 index 00000000000..8bd4f48af6e --- /dev/null +++ b/source/source_estate/module_charge/chg_atomic.h @@ -0,0 +1,36 @@ +#ifndef CHG_ATOMIC_H +#define CHG_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 // CHG_ATOMIC_H 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..0ac793fa452 --- /dev/null +++ b/source/source_estate/module_charge/chg_dmr.cpp @@ -0,0 +1,183 @@ +#include "chg_dmr.h" + +#include +#include + +#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" + +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]; + } +} + +/** + * @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, + Base_Mixing::Mixing_Data& mdata, + 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"); + } + 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(); + ModuleBase::timer::end("module_charge", "init_mixing_dmr"); +} + +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"); + check_dmr_inputs(dmr_out, dmr_in, nnr, mixing, cfg); + + if (cfg.nspin == 1 || cfg.nspin == 4) + { + 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. + 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. + for (int ir = 0; ir < nnr; ++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. + for (int ir = 0; ir < nnr; ++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]; + } + + 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_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. + ModuleBase::GlobalFunc::ZEROS(dmr_out[0], nnr); + ModuleBase::GlobalFunc::ZEROS(dmr_out[1], nnr); + for (int ir = 0; ir < nnr; ++ir) + { + 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"); +} + +} // 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..db1983bae53 --- /dev/null +++ b/source/source_estate/module_charge/chg_dmr.h @@ -0,0 +1,68 @@ +#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. 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" + +namespace Base_Mixing +{ +class Mixing; +class Mixing_Data; +} // namespace Base_Mixing + +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. + * + * @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) + */ +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); + +} // namespace module_charge + +#endif // CHG_DMR_H diff --git a/source/source_estate/module_charge/mix_resid.cpp b/source/source_estate/module_charge/chg_drho.cpp similarity index 59% rename from source/source_estate/module_charge/mix_resid.cpp rename to source/source_estate/module_charge/chg_drho.cpp index c59c543712a..544af8603ec 100644 --- a/source/source_estate/module_charge/mix_resid.cpp +++ b/source/source_estate/module_charge/chg_drho.cpp @@ -1,18 +1,31 @@ -#include "charge_mixing.h" -#include "source_io/module_parameter/parameter.h" +#include "charge.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" -double Charge_Mixing::get_drho(Charge* chr, const double nelec) +#include + +namespace module_charge +{ + +// 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) { - ModuleBase::TITLE("Charge_Mixing", "get_drho"); - ModuleBase::timer::start("Charge_Mixing", "get_drho"); - const int nspin = PARAM.inp.nspin; + assert(chr != nullptr); + 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; - if (PARAM.inp.scf_thr_type == 1) + if (cfg.scf_thr_type == 1) { for (int is = 0; is < nspin; ++is) { @@ -24,20 +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 = this->inner_product_recip_rho(drhog.data(), drhog.data()); + drho = module_charge::detail::inner_product_recip_rho( + drhog.data(), drhog.data(), rhopw, cfg, omega, tpiba); } else { @@ -45,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 && PARAM.globalv.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]); } @@ -61,37 +75,43 @@ 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; } - ModuleBase::timer::end("Charge_Mixing", "get_drho"); + ModuleBase::timer::end("module_charge", "cal_drho"); 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; }; - 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. - for (int is = 0; is < PARAM.inp.nspin; is++) + for (int is = 0; is < cfg.nspin; is++) { - if (is != 0 && is != 3 && PARAM.globalv.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]); } @@ -100,29 +120,45 @@ 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"); + ModuleBase::timer::end("module_charge", "cal_dkin"); return dkin; } -double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::complex* rho2) +} // namespace module_charge + +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"); - 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 = 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; @@ -130,20 +166,20 @@ 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]; + if (ig == ig0) {continue;} + sum += (conj(rhog1[0][ig]) * rhog2[0][ig]).real() / rhopw.gg[ig]; } sum *= fac; return sum; }; - switch (PARAM.inp.nspin) + switch (nspin) { case 1: sum += part_of_noncolin(); @@ -151,18 +187,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 (PARAM.globalv.gamma_only_pw) + if (cfg.gamma_only_pw) { sum *= 2.0; } @@ -185,42 +221,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 (PARAM.globalv.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 (!PARAM.globalv.domag && !PARAM.globalv.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) - { - 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() / rhopw.gg[ig]; } sum *= fac; if (ig0 > 0) @@ -230,14 +264,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 (PARAM.globalv.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; @@ -253,96 +287,104 @@ double Charge_Mixing::inner_product_recip_rho(std::complex* rho1, std::c Parallel_Reduce::reduce_pool(sum); #endif - sum *= *this->omega * 0.5; - - delete[] rhog1; - delete[] rhog2; + sum *= omega * 0.5; ModuleBase::timer::end("Charge_Mixing", "recip_rho"); 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"); +} // 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 (PARAM.inp.nspin == 4 && this->mixing_angle > 0) { resize_tmp = 2; -} + if (cfg.nspin == 4 && cfg.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 ir = 0; ir < rhopw.nrxx * cfg.nspin / resize_tmp; ++ir) { - rnorm += (conj(rho1[ig]) * rho2[ig]).real(); + rnorm += rho1[ir] * rho2[ir]; } #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) +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 (PARAM.inp.nspin==1) + + if (cfg.nspin==1) { sum += part_of_rho(); } - else if (PARAM.inp.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 (PARAM.globalv.gamma_only_pw) + if (cfg.gamma_only_pw) { sum *= 2.0; } @@ -356,21 +398,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 (PARAM.globalv.gamma_only_pw) + if (cfg.gamma_only_pw) { mag *= 2.0; } @@ -378,23 +420,23 @@ double Charge_Mixing::inner_product_recip_hartree(std::complex* rhog1, s sum2 += mag; sum += sum2; } - else if (PARAM.inp.nspin==4) + else if (cfg.nspin==4) { - if (!PARAM.globalv.domag && !PARAM.globalv.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) @@ -404,14 +446,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 (PARAM.globalv.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; @@ -421,42 +463,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 (PARAM.globalv.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()); } } } @@ -464,32 +506,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 (PARAM.inp.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 * PARAM.inp.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..cae85dbb5bc --- /dev/null +++ b/source/source_estate/module_charge/chg_drho.h @@ -0,0 +1,90 @@ +#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 "chg_mix_cfg.h" + +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. + * + * @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..4a6d2553b09 --- /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 (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. + +#include + +#include "chg_mix_cfg.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/module_charge/charge_extra.cpp b/source/source_estate/module_charge/chg_extra.cpp similarity index 94% rename from source/source_estate/module_charge/charge_extra.cpp rename to source/source_estate/module_charge/chg_extra.cpp index 7513469a31b..07803b9f826 100644 --- a/source/source_estate/module_charge/charge_extra.cpp +++ b/source/source_estate/module_charge/chg_extra.cpp @@ -1,4 +1,6 @@ -#include "charge_extra.h" +#include "chg_extra.h" + +#include "chg_atomic.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" @@ -6,18 +8,11 @@ #include "source_base/tool_threading.h" #include "source_io/module_output/cube_io.h" -Charge_Extra::Charge_Extra() +namespace module_charge { -} -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) @@ -65,9 +60,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; @@ -176,7 +171,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 @@ -200,7 +195,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; @@ -293,7 +291,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) @@ -316,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 85% rename from source/source_estate/module_charge/charge_extra.h rename to source/source_estate/module_charge/chg_extra.h index bdfb103377a..4f44a583eca 100644 --- a/source/source_estate/module_charge/charge_extra.h +++ b/source/source_estate/module_charge/chg_extra.h @@ -1,5 +1,7 @@ -#ifndef CHARGE_EXTRA_H -#define CHARGE_EXTRA_H +#ifndef CHG_EXTRA_H +#define CHG_EXTRA_H + +#include #include "charge.h" #include "source_cell/unitcell.h" @@ -8,6 +10,9 @@ #include "source_base/parallel_grid.h" #endif +namespace module_charge +{ + /** * @brief charge extrapolation method * @@ -34,7 +39,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 +105,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 @@ -116,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/module_charge/chg_mix_cfg.h b/source/source_estate/module_charge/chg_mix_cfg.h new file mode 100644 index 00000000000..0f52e698567 --- /dev/null +++ b/source/source_estate/module_charge/chg_mix_cfg.h @@ -0,0 +1,31 @@ +#ifndef CHG_MIX_CFG_H +#define CHG_MIX_CFG_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 // CHG_MIX_CFG_H 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/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..8d8d2298ba5 --- /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 "chg_mix_cfg.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/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 diff --git a/source/source_estate/module_charge/chgmixing.cpp b/source/source_estate/module_charge/chg_routine.cpp similarity index 85% rename from source/source_estate/module_charge/chgmixing.cpp rename to source/source_estate/module_charge/chg_routine.cpp index 4dc059e3e56..804ce146a04 100644 --- a/source/source_estate/module_charge/chgmixing.cpp +++ b/source/source_estate/module_charge/chg_routine.cpp @@ -1,12 +1,13 @@ -#include "source_estate/module_charge/chgmixing.h" +#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" #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 @@ -17,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) @@ -92,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); @@ -106,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; @@ -121,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"); @@ -157,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; } @@ -181,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"); @@ -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/module_charge/chgmixing.h b/source/source_estate/module_charge/chg_routine.h similarity index 84% rename from source/source_estate/module_charge/chgmixing.h rename to source/source_estate/module_charge/chg_routine.h index 6b751128e36..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 @@ -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/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/chg_symm_detail.cpp b/source/source_estate/module_charge/chg_symm_detail.cpp new file mode 100644 index 00000000000..ca73d5e75df --- /dev/null +++ b/source/source_estate/module_charge/chg_symm_detail.cpp @@ -0,0 +1,396 @@ +#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 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 +#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; + } + } + + // (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]; + } + } + 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) + { +#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); +#ifdef __MPI + 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 + 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; +#ifdef __MPI + } + + // (4) send the result to other procs in the same pool + rhog_piece_to_all(rho_basis, rhogtot, rhog_part); + + if(GlobalV::RANK_IN_POOL==0) + { + delete[] rhogtot; + delete[] ig2isztot; + } +#endif + delete[] fftixy2is; + return; +} + +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. + 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 +#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; + } + } + + // (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]; + } + } + 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) + { +#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); +#ifdef __MPI + 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 + 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; +#ifdef __MPI + } + + // (4) send the result to other procs in the same pool + 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) + { + delete[] rhogtot_x; + delete[] rhogtot_y; + delete[] rhogtot_z; + delete[] ig2isztot; + } +#endif + delete[] fftixy2is; + 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/chg_tools.cpp b/source/source_estate/module_charge/chg_tools.cpp new file mode 100644 index 00000000000..ab18f92cf3e --- /dev/null +++ b/source/source_estate/module_charge/chg_tools.cpp @@ -0,0 +1,262 @@ +#include "chg_tools.h" + +#include "source_base/complexmatrix.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 "source_basis/module_pw/pw_basis.h" +#include "source_cell/unitcell.h" + +#include +#include +#include + +namespace module_charge +{ + +double sum_rho(double* const* rho, + const int nspin0, + const int nrxx, + const double omega, + const int nxyz) +{ + ModuleBase::TITLE("module_charge", "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("module_charge::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("module_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 (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; +} + +// 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("module_charge", "set_rho_core"); + ModuleBase::timer::start("module_charge", "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("module_charge", "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("module_charge", "set_rho_core"); +} + +} // namespace module_charge diff --git a/source/source_estate/module_charge/chg_tools.h b/source/source_estate/module_charge/chg_tools.h new file mode 100644 index 00000000000..42bbdaaacb7 --- /dev/null +++ b/source/source_estate/module_charge/chg_tools.h @@ -0,0 +1,69 @@ +#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 +// 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. + +#include + +class UnitCell; +namespace ModuleBase +{ +class ComplexMatrix; +} +namespace ModulePW +{ +class PW_Basis; +} + +namespace module_charge +{ + +// 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 +// 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 module_charge + +#endif // CHG_TOOLS_H 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/module_charge/mix_precond.cpp b/source/source_estate/module_charge/mix_precond.cpp deleted file mode 100644 index a0a32fef97c..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/module_charge/symm_rho.cpp b/source/source_estate/module_charge/symm_rho.cpp deleted file mode 100644 index a3417e96d9b..00000000000 --- a/source/source_estate/module_charge/symm_rho.cpp +++ /dev/null @@ -1,211 +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_estate/module_charge/symm_rhog.cpp b/source/source_estate/module_charge/symm_rhog.cpp deleted file mode 100644 index 4327ee68603..00000000000 --- a/source/source_estate/module_charge/symm_rhog.cpp +++ /dev/null @@ -1,356 +0,0 @@ -#include "symm_rho.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" - - -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 -#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; - - // (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) - { -#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); -#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); -#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); -#endif - 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); - - if(GlobalV::RANK_IN_POOL==0) - { - delete[] rhogtot; - delete[] ig2isztot; - } -#endif - 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 -{ - // 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 -#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; - - // (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) - { -#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); -#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); -#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); -#endif - 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); - - if(GlobalV::RANK_IN_POOL==0) - { - delete[] rhogtot_x; - delete[] rhogtot_y; - delete[] rhogtot_z; - delete[] ig2isztot; - } -#endif - 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 -{ - 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; -} diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index 7bd5f3bfd72..55adb3fbb5e 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 @@ -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 + SOURCES charge_test.cpp ../module_charge/charge.cpp ../module_charge/chg_atomic.cpp ../module_charge/chg_tools.cpp ) @@ -111,25 +111,19 @@ 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/mix_precond.cpp ../module_charge/charge_mixing_rho.cpp - ../module_charge/charge_mixing_uspp.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 ) 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 ) -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 @@ -141,16 +135,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_extra_test.cpp b/source/source_estate/test/charge_extra_test.cpp index cc5dd66189c..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 @@ -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 @@ -84,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 ***********************************************/ /** @@ -102,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; @@ -163,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) diff --git a/source/source_estate/test/charge_mixing_test.cpp b/source/source_estate/test/charge_mixing_test.cpp index c2bf0b1a2d0..c5ca513c5b9 100644 --- a/source/source_estate/test/charge_mixing_test.cpp +++ b/source/source_estate/test/charge_mixing_test.cpp @@ -1,7 +1,14 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +// 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 "../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" @@ -49,22 +56,20 @@ 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: 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) + * - 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) * 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 * */ @@ -99,7 +104,44 @@ 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; + 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) @@ -114,23 +156,12 @@ 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); 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); @@ -139,36 +170,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); + EXPECT_EQ(CMtest.get_mixing_config().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 +185,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 +194,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,45 +210,26 @@ 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; + 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); 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 +237,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,20 +246,10 @@ 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; + sync_cfg(CMtest); // a simple sum for inner product std::vector drho1(pw_basis.nrxx * PARAM.input.nspin); @@ -312,23 +259,12 @@ 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 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 @@ -339,41 +275,10 @@ 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); } -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_rhopw(&pw_basis, &pw_basis); - PARAM.input.nspin = 2; - - // 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 @@ -389,25 +294,15 @@ 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 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; + sync_cfg(CMtest); std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); for (int i = 0; i < pw_basis.nrxx; ++i) @@ -418,11 +313,12 @@ 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 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); @@ -441,14 +337,17 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) drhog2_mag[i+pw_basis.npw] = drhog2[i] - drhog2[i+pw_basis.npw]; } PARAM.sys.gamma_only_pw= false; - inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); + sync_cfg(CMtest); + 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; - inner = CMtest.inner_product_recip_hartree(drhog1_mag.data(), drhog2_mag.data()); + sync_cfg(CMtest); + 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 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) @@ -459,29 +358,20 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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; - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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 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) @@ -490,10 +380,12 @@ TEST_F(ChargeMixingTest, InnerDotRecipHartreeTest) drhog2[i] = std::complex(1.0, 1.0); } PARAM.sys.gamma_only_pw= false; - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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; - inner = CMtest.inner_product_recip_hartree(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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); } @@ -510,25 +402,15 @@ 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 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; + sync_cfg(CMtest); std::vector> drhog1(pw_basis.npw); std::vector> drhog2(pw_basis.npw); for (int i = 0; i < pw_basis.nrxx; ++i) @@ -539,10 +421,11 @@ 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; + 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) @@ -551,13 +434,16 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) drhog2[i] = std::complex(1.0, 1.0); } PARAM.sys.gamma_only_pw= false; - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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; - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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; + 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) @@ -568,34 +454,23 @@ TEST_F(ChargeMixingTest, InnerDotRecipRhoTest) PARAM.sys.domag = false; PARAM.sys.domag_z = false; - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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; - inner = CMtest.inner_product_recip_rho(drhog1.data(), drhog2.data()); + sync_cfg(CMtest); + 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); } 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); // 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) @@ -603,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); } @@ -625,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) @@ -634,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); @@ -660,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) @@ -667,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); @@ -691,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); @@ -707,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) { @@ -721,23 +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(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); // 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) @@ -745,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]); @@ -756,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]; @@ -765,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); } @@ -789,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); @@ -823,12 +688,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]; @@ -837,12 +702,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); @@ -863,18 +728,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 +758,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) { @@ -933,12 +776,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; @@ -962,12 +799,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]; @@ -976,12 +813,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); @@ -1003,18 +840,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) @@ -1042,12 +868,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; @@ -1063,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); + const int npw_smooth = pw_basis.npw; + const int npw_dense = pw_dbasis.npw; + const int npw_hf = npw_dense - npw_smooth; - CMtest.divide_data(data.data(), datas2, datahf2); - CMtest.clean_data(datas2, datahf2); - EXPECT_EQ(datas2, nullptr); - EXPECT_EQ(datahf2, nullptr); + // 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) 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&, 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_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 97% rename from source/source_estate/test/gint_prec_ctrl_test.cpp rename to source/source_hamilt/module_gint/test/test_gint_prec_ctrl.cpp index f471db80623..5cc6c053baa 100644 --- a/source/source_estate/test/gint_prec_ctrl_test.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) { 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..e4a689bf4fa 100644 --- a/source/source_io/module_wf/read_wf2rho_pw.cpp +++ b/source/source_io/module_wf/read_wf2rho_pw.cpp @@ -4,7 +4,8 @@ #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_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,15 +219,14 @@ 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 // 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/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 38f586f3a5e..fe0654c56e5 100644 --- a/source/source_io/test/read_wf2rho_pw_test.cpp +++ b/source/source_io/test/read_wf2rho_pw_test.cpp @@ -9,7 +9,8 @@ #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_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" #include "source_io/module_wf/read_wf2rho_pw.h" @@ -55,16 +56,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) { @@ -230,8 +231,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 +243,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; @@ -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 @@ -348,9 +349,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) 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); } //---------------------------------------------------------- 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);