From 7335693ebca9d4f35b1785d41c70b739232e930e Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Sat, 17 Oct 2026 21:51:09 +0800 Subject: [PATCH 01/33] Feature: Support nspin=4 PW RT-TDDFT with SOC, magnetization, spin-current, and torque outputs --- source/Makefile.Objects | 5 +- .../module_device/rocm/memory_op.hip.cu | 18 +- .../source_esolver/esolver_ks_lcao_tddft.cpp | 15 +- source/source_esolver/esolver_ks_pw.cpp | 8 + source/source_esolver/esolver_ks_pw_tddft.cpp | 128 ++++-- source/source_esolver/esolver_ks_pw_tddft.h | 11 +- source/source_hsolver/hsolver_pw_tddft.cpp | 138 ++++-- source/source_hsolver/hsolver_pw_tddft.h | 8 +- source/source_hsolver/orthonormal.cpp | 2 +- source/source_io/CMakeLists.txt | 3 +- .../source_io/module_ctrl/ctrl_output_fp.cpp | 4 +- .../source_io/module_ctrl/ctrl_output_pw.cpp | 9 +- .../module_current/td_current_io.cpp | 40 +- .../module_current/td_current_io_comm.cpp | 26 +- .../module_current/td_current_pw.cpp | 218 ++-------- .../source_io/module_current/td_current_pw.h | 58 +-- .../module_current/td_spin_pw_io.cpp | 180 ++++++++ .../source_io/module_current/td_spin_pw_io.h | 23 + source/source_io/module_dipole/dipole_io.h | 3 +- .../source_io/module_dipole/write_dipole.cpp | 18 +- .../source_io/module_efield/td_efield_io.cpp | 42 +- source/source_io/module_efield/td_efield_io.h | 8 +- ...{td_vector_pot_io.cpp => td_vecpot_io.cpp} | 54 +-- .../{td_vector_pot_io.h => td_vecpot_io.h} | 12 +- .../module_parameter/input_parameter.h | 3 + .../module_parameter/read_inp_estruc.cpp | 21 +- .../module_parameter/read_inp_exx_dftu.cpp | 31 +- .../module_parameter/read_inp_out.cpp | 34 +- .../module_parameter/read_inp_tddft.cpp | 70 ++- source/source_io/test/CMakeLists.txt | 4 +- source/source_io/test/td_efield_io_test.cpp | 19 +- ...tor_pot_io_test.cpp => test_vecpot_io.cpp} | 46 +- source/source_psi/setup_psi_pw.cpp | 25 +- source/source_pw/module_proj/onsite_proj.h | 24 +- .../module_proj/onsite_proj_init.cpp | 41 +- .../module_proj/onsite_proj_overlap.cpp | 72 +++- .../source_pw/module_proj/onsite_proj_tools.h | 15 + .../module_proj/onsite_proj_tools_becp.cpp | 108 ++++- source/source_pw/module_pwdft/CMakeLists.txt | 2 + source/source_pw/module_pwdft/hamilt_pw.cpp | 6 +- source/source_pw/module_pwdft/hamilt_pw.h | 2 +- .../module_pwdft/nonlocal_workspace.cpp | 25 +- .../module_pwdft/nonlocal_workspace.h | 10 + source/source_pw/module_pwdft/op_pw_meta.h | 2 - source/source_pw/module_pwdft/op_pw_vel.cpp | 6 +- source/source_pw/module_pwdft/op_pw_vel.h | 8 - source/source_pw/module_pwdft/setup_pot.cpp | 11 +- source/source_pw/module_pwdft/setup_pot.h | 1 + .../source_pw/module_pwdft/td_history_pw.cpp | 24 +- source/source_pw/module_pwdft/td_history_pw.h | 18 +- source/source_pw/module_pwdft/td_obs_pw.h | 47 ++ source/source_pw/module_pwdft/td_pw.cpp | 25 +- source/source_pw/module_pwdft/td_pw.h | 5 +- source/source_pw/module_pwdft/td_spin_pw.cpp | 406 ++++++++++++++++++ source/source_pw/module_pwdft/td_spin_pw.h | 69 +++ source/source_pw/module_pwdft/td_vel_pw.cpp | 207 +++++++++ source/source_pw/module_pwdft/td_vel_pw.h | 44 ++ .../module_pwdft/velocity_workspace.cpp | 51 ++- .../module_pwdft/velocity_workspace.h | 3 +- source/source_pw/module_pwdft/vnl_pw.cpp | 5 +- source/source_pw/module_pwdft/vnl_pw_deeq.cpp | 10 +- .../19_NO_SUPERSINE_TDDFT/efield_1.txt.ref | 15 +- .../19_NO_SUPERSINE_TDDFT/efield_2.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref | 15 +- .../efield_10.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref | 15 +- .../20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref | 15 +- 73 files changed, 2012 insertions(+), 699 deletions(-) create mode 100644 source/source_io/module_current/td_spin_pw_io.cpp create mode 100644 source/source_io/module_current/td_spin_pw_io.h rename source/source_io/module_efield/{td_vector_pot_io.cpp => td_vecpot_io.cpp} (54%) rename source/source_io/module_efield/{td_vector_pot_io.h => td_vecpot_io.h} (68%) rename source/source_io/test/{td_vector_pot_io_test.cpp => test_vecpot_io.cpp} (66%) create mode 100644 source/source_pw/module_pwdft/td_obs_pw.h create mode 100644 source/source_pw/module_pwdft/td_spin_pw.cpp create mode 100644 source/source_pw/module_pwdft/td_spin_pw.h create mode 100644 source/source_pw/module_pwdft/td_vel_pw.cpp create mode 100644 source/source_pw/module_pwdft/td_vel_pw.h diff --git a/source/Makefile.Objects b/source/Makefile.Objects index b2361a72e26..1650756dad5 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -381,6 +381,8 @@ OBJS_HAMILT=hamilt_pw.o\ pw_hybrid_nscf.o\ pw_hybrid_source.o\ td_history_pw.o\ + td_vel_pw.o\ + td_spin_pw.o\ td_pw.o\ hs_matrix_k.o\ sto_hamilt_pw.o\ @@ -741,8 +743,9 @@ OBJS_IO=module_parameter/input_conv.o\ module_current/td_current_io.o\ module_current/td_current_io_comm.o\ module_current/td_current_pw.o\ + module_current/td_spin_pw_io.o\ td_efield_io.o\ - td_vector_pot_io.o\ + td_vecpot_io.o\ module_chgpot/write_libxc_r.o\ module_chgpot/get_pchg_pw.o\ module_output/output_log.o\ diff --git a/source/source_base/module_device/rocm/memory_op.hip.cu b/source/source_base/module_device/rocm/memory_op.hip.cu index 56eb6f06b70..6b3f1c9d3e9 100644 --- a/source/source_base/module_device/rocm/memory_op.hip.cu +++ b/source/source_base/module_device/rocm/memory_op.hip.cu @@ -58,6 +58,16 @@ void set_memory_op::operator()(FPTYPE* arr, hipErrcheck(hipMemset(arr, var, sizeof(FPTYPE) * size)); } +template +void set_memory_2d_op::operator()(FPTYPE* arr, + const size_t pitch, + const int var, + const size_t width, + const size_t height) +{ + hipErrcheck(hipMemset2D(arr, sizeof(FPTYPE) * pitch, var, sizeof(FPTYPE) * width, height)); +} + template void synchronize_memory_op::operator()( FPTYPE* arr_out, @@ -244,6 +254,12 @@ template struct cast_memory_op, base_device::DEVICE_CPU, base_device::DEVICE_GPU>; +template struct set_memory_2d_op; +template struct set_memory_2d_op; +template struct set_memory_2d_op; +template struct set_memory_2d_op, base_device::DEVICE_GPU>; +template struct set_memory_2d_op, base_device::DEVICE_GPU>; + template struct delete_memory_op; template struct delete_memory_op; template struct delete_memory_op; @@ -255,4 +271,4 @@ template struct delete_memory_op*, base_device::DEVICE_GPU>; template struct delete_memory_op*, base_device::DEVICE_GPU>; } // namespace memory -} // end of namespace base_device \ No newline at end of file +} // end of namespace base_device diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index ff2b1e06e40..a5b2bb967d1 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -7,7 +7,7 @@ #include "source_base/global_variable.h" #include "source_io/module_ctrl/ctrl_output_td.h" #include "source_io/module_efield/td_efield_io.h" -#include "source_io/module_efield/td_vector_pot_io.h" +#include "source_io/module_efield/td_vecpot_io.h" #include "source_io/module_output/output_log.h" #include "source_io/module_parameter/parameter.h" #include "source_io/module_wf/read_wfc_nao.h" @@ -88,7 +88,7 @@ void ESolver_KS_LCAO_TDDFT::before_all_runners(BaseCell& basecell, c if (inp.init_vecpot) { // An empty directory selects the calculation working directory, not OUT.. - const std::vector> samples = ModuleIO::read_td_vector_pot(""); + const std::vector> samples = ModuleIO::read_td_vecpot(""); td_field_manager_->set_A_samples(samples); } if (inp.mdp.md_restart) @@ -97,15 +97,16 @@ void ESolver_KS_LCAO_TDDFT::before_all_runners(BaseCell& basecell, c } if (inp.out_efield && GlobalV::MY_RANK == 0) { - ModuleIO::prepare_td_field_output(output_dir, *td_field_manager_, inp.mdp.md_restart); + ModuleIO::prep_td_field_out(output_dir, *td_field_manager_, inp.mdp.md_restart); } if (inp.out_vecpot && GlobalV::MY_RANK == 0) { if (inp.init_vecpot) { - ModuleIO::check_td_vector_pot_paths("", output_dir); + // Reject output that aliases the input file before truncating it. + ModuleIO::check_td_vecpot_paths("", output_dir); } - ModuleIO::prepare_td_vector_pot_output(output_dir, inp.mdp.md_restart); + ModuleIO::prep_td_vecpot_out(output_dir, inp.mdp.md_restart); } // Run before_all_runners in ESolver_KS_LCAO @@ -227,7 +228,7 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist const bool replay_restart = this->inp_->mdp.md_restart && !restart_done; if (this->inp_->out_vecpot && GlobalV::MY_RANK == 0 && !replay_restart) { - ModuleIO::write_td_vector_pot(PARAM.globalv.global_out_dir, this->totstep, td_field_manager_->A_right_ha()); + ModuleIO::write_td_vecpot(PARAM.globalv.global_out_dir, this->totstep, td_field_manager_->A_right_ha()); } ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "Cartesian vector potential Ax(t)", td_p->endpoint_A()[0]); ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "Cartesian vector potential Ay(t)", td_p->endpoint_A()[1]); @@ -242,7 +243,7 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist const bool replay_restart = this->inp_->mdp.md_restart && !restart_done; if (this->inp_->out_efield && GlobalV::MY_RANK == 0 && !replay_restart) { - ModuleIO::write_td_field_values(*td_field_manager_, PARAM.globalv.global_out_dir); + ModuleIO::write_td_field(*td_field_manager_, PARAM.globalv.global_out_dir); } if (estep != 0) diff --git a/source/source_esolver/esolver_ks_pw.cpp b/source/source_esolver/esolver_ks_pw.cpp index f1e6c5fa47c..c804141b14a 100644 --- a/source/source_esolver/esolver_ks_pw.cpp +++ b/source/source_esolver/esolver_ks_pw.cpp @@ -188,6 +188,13 @@ void ESolver_KS_PW::before_scf(UnitCell& ucell, const int istep) this->allocate_hamilt(ucell); //! Setup potentials (local, non-local, spin constraint, DFT-1/2) + psi::Psi, Device>* onsite_psi = nullptr; + const bool spinor_rt = this->inp_->esolver_type == "tddft" && this->inp_->nspin == 4; + if (this->inp_->onsite_radius > 0 && !spinor_rt) + { + this->stp.update_psi_d(); + onsite_psi = this->stp.template get_psi_d(); + } pw::setup_pot(istep, ucell, this->kv, @@ -199,6 +206,7 @@ void ESolver_KS_PW::before_scf(UnitCell& ucell, const int istep) this->ppcell, this->vsep_cell, this->stp.template get_psi_t(), + onsite_psi, this->p_hamilt, this->pw_wfc, this->pw_rhod, diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 0008a72f3fe..63609f0631a 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -1,5 +1,6 @@ #include "source_esolver/esolver_ks_pw_tddft.h" +#include "source_base/constants.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/parallel_comm.h" @@ -11,8 +12,10 @@ #include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/hamilt_hs_adapter.h" #include "source_hamilt/module_xc/xc_functional.h" +#include "source_io/module_current/td_current_pw.h" +#include "source_io/module_current/td_spin_pw_io.h" #include "source_io/module_efield/td_efield_io.h" -#include "source_io/module_efield/td_vector_pot_io.h" +#include "source_io/module_efield/td_vecpot_io.h" #include "source_io/module_energy/td_eij_io.h" #include "source_io/module_output/output_log.h" #include "source_io/module_parameter/parameter.h" @@ -45,13 +48,26 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons if (inp.init_vecpot) { // An empty directory selects the calculation working directory, not OUT.. - const std::vector> samples = ModuleIO::read_td_vector_pot(""); + const std::vector> samples = ModuleIO::read_td_vecpot(""); this->td_field_manager_->set_A_samples(samples); } ESolver_KS_PW::before_all_runners(ucell, inp); // The parent resolves the XC functional, including pseudopotential defaults. - pw::check_td_input(inp, ucell, XC_Functional::get_ked_flag(), this->general_exx_info_.cal_exx); + const int xc_type = XC_Functional::get_func_type(); + pw::check_td_input(inp, ucell, xc_type, this->general_exx_info_.cal_exx); + spin_options_.method = inp.td_out_mag; + spin_options_.orbital_dir = inp.orbital_dir; + spin_options_.radius = inp.onsite_radius; + spin_options_.torque = inp.td_out_torque; + vel_request_.charge_total = inp.out_current == 1; + vel_request_.charge_per_k = vel_request_.charge_total && inp.out_current_k; + vel_request_.spin_current = inp.nspin == 4 && inp.td_out_spincur; + vel_.initialize(ucell, *this->pw_wfc, this->kv, this->ppcell); + if (inp.nspin == 4) + { + spin_.initialize(ucell, *this->pw_rhod, *this->pw_wfc, this->kv, this->sf, this->ppcell, spin_options_); + } this->pelec->pot->set_td_field_manager(this->td_field_manager_); #ifdef __MPI const hsolver::diag_comm_info comm(this->pw_wfc->pool_world, this->pw_wfc->poolrank, this->pw_wfc->poolnproc); @@ -59,6 +75,7 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons const hsolver::diag_comm_info comm(0, 1); #endif hsolver::PWLinearOptions options; + options.npol = ucell.get_npol(); options.orthonormal = hsolver::parse_orth_method(inp.td_orthonormal); options.out_stat = inp.td_out_stat; options.global_k_indices = this->kv.ik2iktot; @@ -71,21 +88,22 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons options.cn_init = inp.td_cn_init; options.kinetic_enabled = inp.t_in_h; this->td_solver_.reset(new hsolver::HSolverPWTDDFT(*this->pw_wfc, options, comm, log_)); - this->history_.prepare(*this->pelec->pot, XC_Functional::get_ked_flag()); + this->history_.prepare(*this->pelec->pot); // Preserve existing field history until input validation and initialization succeed. if (GlobalV::MY_RANK == 0) { if (inp.out_efield) { - ModuleIO::prepare_td_field_output(output_dir, *this->td_field_manager_, false); + ModuleIO::prep_td_field_out(output_dir, *this->td_field_manager_, false); } if (inp.out_vecpot) { if (inp.init_vecpot) { - ModuleIO::check_td_vector_pot_paths("", output_dir); + // Reject output that aliases the input file before truncating it. + ModuleIO::check_td_vecpot_paths("", output_dir); } - ModuleIO::prepare_td_vector_pot_output(output_dir, false); + ModuleIO::prep_td_vecpot_out(output_dir, false); } } } @@ -125,7 +143,7 @@ template void ESolver_KS_PW_TDDFT::before_scf(UnitCell& ucell, const int istep) { this->print_step(istep); - this->prepare_td_step(istep); + this->prep_td_step(istep); this->td_solver_->reset_orth_stats(); const bool basis_updated = ucell.cell_parameter_updated; ESolver_KS_PW::before_scf(ucell, istep); @@ -133,7 +151,7 @@ void ESolver_KS_PW_TDDFT::before_scf(UnitCell& ucell, const int istep { this->td_solver_->invalidate_basis(); } - this->history_.prepare(*this->pelec->pot, XC_Functional::get_ked_flag()); + this->history_.prepare(*this->pelec->pot); } template @@ -146,20 +164,19 @@ void ESolver_KS_PW_TDDFT::prepare_init_hamilt(const UnitCell& ucell) pw::ensure_td_vnl(ucell, q_unshifted, this->td_field_manager_->A_right_ha(), this->td_field_manager_->A_prop_ha(), &this->ppcell); elecstate::Potential& potential = *this->pelec->pot; const Real* veff = potential.template get_veff_smooth_data(); - const Real* vofk = potential.template get_vofk_smooth_data(); auto* hamiltonian = static_cast*>(this->p_hamilt); // A file can supply nonzero A(0), which must enter H before the initial wavefunctions are prepared. - hamiltonian->bind_td_state(veff, vofk, this->td_field_manager_->A_right_ha()); + hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); } } template -void ESolver_KS_PW_TDDFT::prepare_td_step(const int istep) +void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) { - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "prepare_td_step"); + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "prep_td_step"); if (this->td_field_manager_->current_step() == istep) { - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prepare_td_step"); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); return; } if (this->td_field_manager_->gauge() == 0 || istep == 0) @@ -182,14 +199,14 @@ void ESolver_KS_PW_TDDFT::prepare_td_step(const int istep) { if (this->inp_->out_vecpot) { - ModuleIO::write_td_vector_pot(PARAM.globalv.global_out_dir, istep, this->td_field_manager_->A_right_ha()); + ModuleIO::write_td_vecpot(PARAM.globalv.global_out_dir, istep, this->td_field_manager_->A_right_ha()); } if (this->inp_->out_efield) { - ModuleIO::write_td_field_values(*this->td_field_manager_, PARAM.globalv.global_out_dir); + ModuleIO::write_td_field(*this->td_field_manager_, PARAM.globalv.global_out_dir); } } - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prepare_td_step"); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); } template @@ -231,10 +248,12 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in if (istep == 0) { ESolver_KS_PW::hamilt2rho_single(ucell, istep, iter, ethr); + psi::Psi* current = this->stp.template get_psi_t(); + // Padding is a storage invariant even with orthogonalization disabled; clearing it does not change the density. + this->td_solver_->zero_padding(current); if (this->inp_->td_orthonormal != "none") { - psi::Psi* current = this->stp.template get_psi_t(); - int changed = this->td_solver_->correct_initial(current, iter); + int changed = this->td_solver_->orth_initial(current, iter); // Occupations and density construction contain collectives across k-point pools. Parallel_Reduce::reduce_all(changed); if (changed > 0) @@ -268,16 +287,15 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in // Predict with the current potential, then correct with the midpoint. const Real* endpoint_veff = potential.template get_veff_smooth_data(); - const Real* endpoint_vofk = potential.template get_vofk_smooth_data(); const ESolver_KS::DensityStage stage = this->density_stage(istep, iter); const bool corrector = stage != ESolver_KS::DensityStage::predictor; - const pw::TDPotentialView propagation = this->history_.prop_pot(potential, XC_Functional::get_ked_flag(), corrector); + const Real* propagation = this->history_.prop_pot(potential, corrector); ModuleBase::Vector3 momentum_shift(0.0, 0.0, 0.0); if (this->td_field_manager_->gauge() == 1) { momentum_shift = this->td_field_manager_->A_prop_ha(); } - hamiltonian->bind_td_state(propagation.veff, propagation.vofk, momentum_shift); + hamiltonian->bind_td_state(propagation, momentum_shift); // The solver sequence follows electronic steps across MD boundaries. this->td_solver_->solve(op, this->history_.previous(), @@ -289,7 +307,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in this->inp_->out_level == "ie"); // Restore the endpoint Hamiltonian before evaluating density and energy. - hamiltonian->bind_td_state(endpoint_veff, endpoint_vofk, this->td_field_manager_->A_right_ha()); + hamiltonian->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); elecstate::ElecStatePW* estate = static_cast*>(this->pelec); // Propagation keeps the occupations of the initial state fixed. estate->psiToRho(*current); @@ -330,6 +348,10 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const psi::Psi* current = this->stp.template get_psi_t(); this->td_solver_->check_initial(*current, this->niter); } + // Endpoint diagnostics must see the converged potential and endpoint A, not the propagation midpoint state. + auto* endpoint_hamilt = static_cast*>(this->p_hamilt); + const Real* endpoint_veff = this->pelec->pot->template get_veff_smooth_data(); + endpoint_hamilt->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); if (this->inp_->td_print_eij >= 0.0) { print_eij(istep); @@ -341,24 +363,9 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, if (istep >= 0) { psi::Psi* current = this->stp.template get_psi_t(); - this->history_.save(*current, *this->pelec->pot, XC_Functional::get_ked_flag()); + this->history_.save(*current, *this->pelec->pot); - if (this->inp_->out_current == 1) - { - const ModuleBase::Vector3& A_right_ha = this->td_field_manager_->A_right_ha(); - this->current_output_.write(istep, - ucell, - this->pw_wfc, - current, - this->pelec, - this->kv, - &this->ppcell, - this->td_field_manager_->gauge(), - A_right_ha, - this->inp_->out_current_k, - PARAM.globalv.global_out_dir, - GlobalV::MY_RANK); - } + sample_endpoint(ucell, istep); } if (this->inp_->td_out_stat) { @@ -366,6 +373,44 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, } } +template +void ESolver_KS_PW_TDDFT::sample_endpoint(UnitCell& cell, int step) +{ + psi::Psi* current = this->stp.template get_psi_t(); + const ModuleBase::Vector3& a = this->td_field_manager_->A_right_ha(); + // All endpoint currents share one velocity action; disabled requests perform no collectives. + const pw::TDVelResultPW currents = vel_.calculate(*current, *this->pelec, a, vel_request_); + pw::TDSpinResultPW moments; + if (this->inp_->nspin == 4 && (spin_options_.method != "none" || spin_options_.torque)) + { + moments = spin_.calculate(*current, *this->pelec, a); + } + const std::string& directory = PARAM.globalv.global_out_dir; + const int rank = GlobalV::MY_RANK; + if (rank == 0) + { + if (this->inp_->out_current == 1) + { + const int spin_mult = this->inp_->nspin == 2 ? 2 : 1; + const int nk = this->kv.get_nkstot() / spin_mult; + ModuleIO::write_pw_current(currents, step, nk, this->inp_->out_current_k, directory); + } + if (this->inp_->nspin == 4) + { + const double time = step * this->td_field_manager_->dt_ha() * ModuleBase::AU_to_FS; + ModuleIO::write_td_spin_pw(directory, + step, + time, + cell.orbital_fn, + spin_options_, + vel_request_.spin_current, + moments, + currents, + log_); + } + } +} + template void ESolver_KS_PW_TDDFT::print_eij(const int istep) { @@ -380,9 +425,8 @@ void ESolver_KS_PW_TDDFT::print_eij(const int istep) const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; auto* hamiltonian = static_cast*>(this->p_hamilt); const Real* veff = this->pelec->pot->template get_veff_smooth_data(); - const Real* vofk = this->pelec->pot->template get_vofk_smooth_data(); // cal_converged() updates the potential from the final density, after the last band-energy evaluation. - hamiltonian->bind_td_state(veff, vofk, this->td_field_manager_->A_right_ha()); + hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); for (int global_k = 0; global_k < this->kv.get_nkstot(); ++global_k) { diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index 3ab6bd7cfcd..ccd58105061 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -4,8 +4,9 @@ #include "source_esolver/esolver_ks_pw.h" #include "source_estate/module_pot/td_field_manager.h" #include "source_hsolver/hsolver_pw_tddft.h" -#include "source_io/module_current/td_current_pw.h" #include "source_pw/module_pwdft/td_history_pw.h" +#include "source_pw/module_pwdft/td_spin_pw.h" +#include "source_pw/module_pwdft/td_vel_pw.h" #include #include @@ -56,7 +57,10 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW std::unique_ptr> td_solver_; std::shared_ptr td_field_manager_; - ModuleIO::CurrentPW current_output_; + pw::TDVelPW vel_; + pw::TDSpinPW spin_; + pw::TDVelRequestPW vel_request_; + pw::TDSpinOptsPW spin_options_; pw::TDHistoryPW history_; double initial_wave_electrons_ = 0.0; @@ -66,7 +70,8 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW /** @brief Print Eij from the converged density, collecting all k-point pools on the main rank. */ void print_eij(const int istep); void report_orth(const UnitCell& ucell, int istep); - void prepare_td_step(const int istep); + void prep_td_step(const int istep); + void sample_endpoint(UnitCell& cell, int step); }; } // namespace ModuleESolver diff --git a/source/source_hsolver/hsolver_pw_tddft.cpp b/source/source_hsolver/hsolver_pw_tddft.cpp index e65d7a541f0..8c059f8113f 100644 --- a/source/source_hsolver/hsolver_pw_tddft.cpp +++ b/source/source_hsolver/hsolver_pw_tddft.cpp @@ -16,6 +16,42 @@ namespace hsolver namespace { +template +void clear_padding(T* data, int ld, int npw, int bands, int npol) +{ + if (npol != 2) + { + return; + } + const int stride = ld / npol; + if (stride > npw && bands > 0) + { + // Clear all spin-component tails together, including any NaNs in padding. + T* tail = data + npw; + const size_t width = stride - npw; + const size_t columns = static_cast(bands) * npol; + base_device::memory::set_memory_2d_op()(tail, stride, 0, width, columns); + } +} + +template +const T* clean_input(const T* input, int ld, int npw, int bands, int npol, ct::Tensor* scratch) +{ + if (npol == 1) + { + return input; + } + T* data = scratch->template data(); + const size_t count = static_cast(ld) * bands; + if (count > 0) + { + // History and other borrowed inputs remain unchanged while the solver uses a clean scratch copy. + base_device::memory::synchronize_memory_op()(data, input, count); + } + clear_padding(data, ld, npw, bands, npol); + return data; +} + struct LinearMethodName { const char* name; @@ -40,12 +76,15 @@ template class ShiftedHOperator final : public LinearOperator { public: - ShiftedHOperator(const HSOperator& op, const T coefficient, const int dim) : op_(op), coefficient_(coefficient), dim_(dim) + ShiftedHOperator(const HSOperator& op, const T coefficient, const int dim, const int npw, const int npol) + : op_(op), coefficient_(coefficient), dim_(dim), npw_(npw), npol_(npol) { } void apply(const T* x, T* y, const int ld, const int nvec) const override { op_.hpsi(x, y, ld, nvec); + // Keep H zero on padding so the shifted operator retains only its identity term there. + clear_padding(y, ld, npw_, nvec, npol_); linear_op().batch(ld, dim_, nvec, y, x, y, T(1), coefficient_, nullptr, nullptr, nullptr); } @@ -53,6 +92,8 @@ class ShiftedHOperator final : public LinearOperator const HSOperator& op_; const T coefficient_; const int dim_; + const int npw_; + const int npol_; }; } // namespace @@ -154,6 +195,7 @@ void HSolverPWTDDFT::prepare_buffers(const int nbands, const int nbas { rhs_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); hpsi_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); + clean_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); } ModuleBase::timer::end("HSolverPWTDDFT", "prepare_buffers"); } @@ -173,10 +215,16 @@ void HSolverPWTDDFT::update_precond(const int ik, { inverse_kinetic_ = ct::Tensor(ct::DataTypeToEnum::value, device, {elements}); } - std::vector inverse(dim); + std::vector inverse(dim, T(1)); for (int ig = 0; ig < dim; ++ig) { - const double kinetic = options_.kinetic_enabled ? ModulePW::shifted_kinetic(basis_, ik, ig, momentum_shift) : 0.0; + // The two spin components share the same kinetic diagonal; padding keeps an identity preconditioner. + const int physical_g = options_.npol == 2 ? ig % basis_.npwk_max : ig; + if (physical_g >= basis_.npwk[ik]) + { + continue; + } + const double kinetic = options_.kinetic_enabled ? ModulePW::shifted_kinetic(basis_, ik, physical_g, momentum_shift) : 0.0; inverse[ig] = T(1) / (T(1) + coefficient * static_cast(kinetic)); } if (dim > 0) @@ -347,7 +395,7 @@ void HSolverPWTDDFT::report_solve(const SolveDetails& details, int ik const int evolution_step = step + 1; std::ostringstream record; record << std::scientific << std::setprecision(6) << " PW RT-TDDFT linear solve: evolution_step=" << evolution_step - << " scf_iter=" << iteration << " global_k=" << options_.global_k_indices.at(ik) << '\n' + << " scf_iter=" << iteration << " global_k=" << options_.global_k_indices.at(ik) + 1 << '\n' << " iterations=" << result.iterations << " restarts=" << result.restarts << " operator_calls=" << result.operator_calls << " operator_columns=" << result.operator_columns << " pre_orth_residual=" << result.max_residual << " residual_kind=" << (result.reconstructed ? "reconstructed" : "independent") << " true_checks=" << result.true_checks << '\n' @@ -385,12 +433,15 @@ void HSolverPWTDDFT::solve(HSOperator& op, op.update_k(ik); current->fix_k(ik); previous.fix_k(ik); - const int dim = current->get_ngk(ik); + const int npw = current->get_ngk(ik); + // A spinor spans both npwx-strided blocks, including their explicitly zeroed padding. + const int dim = options_.npol == 2 ? ld : npw; KPointState* state = tracks_state() ? &states_[ik] : nullptr; - const ShiftedHOperator rhs_op(op, rhs_coefficient, dim); - const ShiftedHOperator lhs_op(op, coefficient, dim); - const T* previous_data = previous.get_pointer(); + const ShiftedHOperator rhs_op(op, rhs_coefficient, dim, npw, options_.npol); + const ShiftedHOperator lhs_op(op, coefficient, dim, npw, options_.npol); + const T* previous_data = clean_input(previous.get_pointer(), ld, npw, bands, options_.npol, &clean_); T* current_data = current->get_pointer(); + clear_padding(current_data, ld, npw, bands, options_.npol); rhs_op.apply(previous_data, rhs, ld, bands); if (options_.preconditioner != PWPreconditioner::none) { @@ -407,8 +458,13 @@ void HSolverPWTDDFT::solve(HSOperator& op, const LinearSolveResult& result = details.linear; std::ostringstream message; const int evolution_step = istep + 1; + std::string band_label = "unknown"; + if (result.failed_band >= 0) + { + band_label = std::to_string(result.failed_band + 1); + } message << "PW RT-TDDFT linear solve failed at electronic evolution step " << evolution_step << ", SCF iteration " << iter - << ", global_k=" << options_.global_k_indices.at(ik) << ", band " << result.failed_band << ", after " + << ", global_k=" << options_.global_k_indices.at(ik) + 1 << ", band " << band_label << ", after " << result.iterations << " iterations: " << linear_status_name(result.status) << "; residual = " << result.max_residual; ModuleBase::WARNING_QUIT("HSolverPWTDDFT", message.str()); } @@ -430,8 +486,12 @@ void HSolverPWTDDFT::cal_band_energy(HSOperator& op, const { op.update_k(ik); current.fix_k(ik); - op.hpsi(current.get_pointer(), hpsi, ld, nband); - band_products_.dot(ld, current.get_ngk(ik), nband, current.get_pointer(), hpsi, expectations.data()); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; + const T* data = clean_input(current.get_pointer(), ld, npw, nband, options_.npol, &clean_); + op.hpsi(data, hpsi, ld, nband); + clear_padding(hpsi, ld, npw, nband, options_.npol); + band_products_.dot(ld, dim, nband, data, hpsi, expectations.data()); for (int band = 0; band < nband; ++band) { (*energies)(ik, band) = std::real(expectations[band]); @@ -448,13 +508,15 @@ std::vector> HSolverPWTDDFT::cal_eij(HSOperator< ModuleBase::timer::start("HSolverPWTDDFT", "cal_eij"); const int nband = current.get_nbands(); const int ld = current.get_nbasis(); - const int dim = current.get_ngk(ik); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; prepare_buffers(nband, ld); op.update_k(ik); current.fix_k(ik); - const T* psi = current.get_pointer(); + const T* psi = clean_input(current.get_pointer(), ld, npw, nband, options_.npol, &clean_); T* hpsi = hpsi_.template data(); op.hpsi(psi, hpsi, ld, nband); + clear_padding(hpsi, ld, npw, nband, options_.npol); std::vector> result = algebra_.cross(ld, dim, nband, nband, psi, hpsi); ModuleBase::timer::end("HSolverPWTDDFT", "cal_eij"); return result; @@ -481,7 +543,7 @@ void HSolverPWTDDFT::correct_orbitals(T* current, int ld, int dim, in const int evolution_step = istep + 1; std::ostringstream record; record << std::scientific << std::setprecision(6) << " PW RT-TDDFT orth: evolution_step=" << evolution_step << " scf_iter=" << iter - << " global_k=" << options_.global_k_indices.at(ik) << " requested=" << orth_method_name(options_.orthonormal) + << " global_k=" << options_.global_k_indices.at(ik) + 1 << " requested=" << orth_method_name(options_.orthonormal) << " orth_before=" << result.before << " orth_after=" << result.after << '\n'; log_ << record.str(); } @@ -497,7 +559,7 @@ void HSolverPWTDDFT::record_orth(const OrthResult& result, int ik, in std::ostringstream message; const int evolution_step = istep + 1; message << std::setprecision(16) << " PW RT-TDDFT orbital validation failed (" << stage << "): evolution_step=" << evolution_step - << " scf_iter=" << iter << " global_k=" << options_.global_k_indices.at(ik) + << " scf_iter=" << iter << " global_k=" << options_.global_k_indices.at(ik) + 1 << " requested=" << orth_method_name(options_.orthonormal) << '\n' << " last_attempted=" << orth_method_name(result.actual) << " passes=" << result.passes << " fallbacks=" << result.fallbacks << '\n' @@ -555,7 +617,7 @@ void HSolverPWTDDFT::report_orth_warning(const OrthResult& result, in const int evolution_step = istep + 1; std::ostringstream message; message << std::setprecision(16) << " PW RT-TDDFT orbital warning: evolution_step=" << evolution_step << " scf_iter=" << iter; - message << " global_k=" << options_.global_k_indices.at(ik); + message << " global_k=" << options_.global_k_indices.at(ik) + 1; message << " requested=" << orth_method_name(options_.orthonormal) << '\n' << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance() << '\n' << " "; @@ -580,9 +642,31 @@ void HSolverPWTDDFT::report_orth_warning(const OrthResult& result, in } template -bool HSolverPWTDDFT::correct_initial(psi::Psi* current, int iter) +void HSolverPWTDDFT::zero_padding(psi::Psi* current) { - ModuleBase::timer::start("HSolverPWTDDFT", "correct_initial"); + ModuleBase::timer::start("HSolverPWTDDFT", "zero_padding"); + if (options_.npol != 2) + { + ModuleBase::timer::end("HSolverPWTDDFT", "zero_padding"); + return; + } + const int old_k = current->get_current_k(); + for (int ik = 0; ik < current->get_nk(); ++ik) + { + current->fix_k(ik); + const int npw = current->get_ngk(ik); + const int ld = current->get_nbasis(); + const int bands = current->get_nbands(); + clear_padding(current->get_pointer(), ld, npw, bands, options_.npol); + } + current->fix_k(old_k); + ModuleBase::timer::end("HSolverPWTDDFT", "zero_padding"); +} + +template +bool HSolverPWTDDFT::orth_initial(psi::Psi* current, int iter) +{ + ModuleBase::timer::start("HSolverPWTDDFT", "orth_initial"); bool changed = false; if (options_.orthonormal != OrthMethod::none) { @@ -595,9 +679,10 @@ bool HSolverPWTDDFT::correct_initial(psi::Psi* current, in for (int ik = 0; ik < current->get_nk(); ++ik) { current->fix_k(ik); + const int dim = options_.npol == 2 ? current->get_nbasis() : current->get_ngk(ik); const OrthResult result = orthonormal_.apply(current->get_pointer(), current->get_nbasis(), - current->get_ngk(ik), + dim, current->get_nbands(), options_.orthonormal, options_.out_stat); @@ -605,7 +690,7 @@ bool HSolverPWTDDFT::correct_initial(psi::Psi* current, in changed = changed || result.status == OrthStatus::accepted; } } - ModuleBase::timer::end("HSolverPWTDDFT", "correct_initial"); + ModuleBase::timer::end("HSolverPWTDDFT", "orth_initial"); return changed; } @@ -614,6 +699,9 @@ void HSolverPWTDDFT::check_initial(const psi::Psi& current { ModuleBase::timer::start("HSolverPWTDDFT", "check_initial"); const bool full_gram = options_.orthonormal != OrthMethod::none; + const int ld = current.get_nbasis(); + const int bands = current.get_nbands(); + prepare_buffers(bands, ld); if (options_.out_stat) { orth_norms_.resize(current.get_nk()); @@ -621,12 +709,10 @@ void HSolverPWTDDFT::check_initial(const psi::Psi& current for (int ik = 0; ik < current.get_nk(); ++ik) { current.fix_k(ik); - const OrthResult result = orthonormal_.inspect(current.get_pointer(), - current.get_nbasis(), - current.get_ngk(ik), - current.get_nbands(), - full_gram, - options_.out_stat); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; + const T* data = clean_input(current.get_pointer(), ld, npw, bands, options_.npol, &clean_); + const OrthResult result = orthonormal_.inspect(data, current.get_nbasis(), dim, current.get_nbands(), full_gram, options_.out_stat); record_orth(result, ik, 0, iter); } ModuleBase::timer::end("HSolverPWTDDFT", "check_initial"); diff --git a/source/source_hsolver/hsolver_pw_tddft.h b/source/source_hsolver/hsolver_pw_tddft.h index 53f882c1915..5a92acfa8c6 100644 --- a/source/source_hsolver/hsolver_pw_tddft.h +++ b/source/source_hsolver/hsolver_pw_tddft.h @@ -28,6 +28,7 @@ enum class PWPreconditioner /** @brief Numerical and propagation options, resolved once at the input boundary. */ struct PWLinearOptions { + int npol = 1; ///< Spin components per orbital; fixed throughout propagation. OrthMethod orthonormal = OrthMethod::cholesky; LinearSolveOptions linear; PWPreconditioner preconditioner = PWPreconditioner::kinetic; @@ -65,8 +66,10 @@ class HSolverPWTDDFT void invalidate_basis(); /** @brief Reset diagnostics without invalidating preconditioner history. */ void reset_orth_stats(); - /** @brief Correct initial orbitals when needed; return whether this pool changed any orbital. */ - bool correct_initial(psi::Psi* current, int iter); + /** @brief Clear spinor padding without changing physical coefficients or the k-point cursor; no communication. */ + void zero_padding(psi::Psi* current); + /** @brief Orthogonalize initial orbitals with clean padding; return whether this pool changed any physical orbital. */ + bool orth_initial(psi::Psi* current, int iter); /** @brief Check the converged initial state once; finite orthogonality errors are diagnostic only. */ void check_initial(const psi::Psi& current, int iter); /** @brief Occupation-weighted electron count for the local k-point pool. */ @@ -130,6 +133,7 @@ class HSolverPWTDDFT const ModulePW::PW_Basis_K& basis_; const diag_comm_info comm_; PWLinearOptions options_; + ct::Tensor clean_; LinearAlgebra algebra_; Orthonormal orthonormal_; TDOrthStats orth_stats_; diff --git a/source/source_hsolver/orthonormal.cpp b/source/source_hsolver/orthonormal.cpp index d4fd9272676..bd5c83d9c82 100644 --- a/source/source_hsolver/orthonormal.cpp +++ b/source/source_hsolver/orthonormal.cpp @@ -254,7 +254,7 @@ OrthResult Orthonormal::inspect(const T* input, int ld, int dim, int if (result.failure != OrthFailure::none) { result.status = OrthStatus::failed; - result.reason = "band=" + std::to_string(band); + result.reason = "band=" + std::to_string(band + 1); } } } diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 6444267178b..99c4c5876ac 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -34,8 +34,9 @@ list(APPEND objects module_current/td_current_io.cpp module_current/td_current_io_comm.cpp module_current/td_current_pw.cpp + module_current/td_spin_pw_io.cpp module_efield/td_efield_io.cpp - module_efield/td_vector_pot_io.cpp + module_efield/td_vecpot_io.cpp module_chgpot/write_libxc_r.cpp module_output/output_log.cpp module_json/para_json.cpp diff --git a/source/source_io/module_ctrl/ctrl_output_fp.cpp b/source/source_io/module_ctrl/ctrl_output_fp.cpp index 73e0d679e5b..a57166a82bf 100644 --- a/source/source_io/module_ctrl/ctrl_output_fp.cpp +++ b/source/source_io/module_ctrl/ctrl_output_fp.cpp @@ -225,7 +225,9 @@ void ctrl_output_fp(UnitCell& ucell, const bool output_dipole = inp.esolver_type == "tddft" || should_output; if (inp.out_dipole == 1 && output_dipole) { - for (int is = 0; is < nspin; ++is) + const int dipole_channels = nspin == 4 ? 1 : nspin; + // In a spinor density only channel zero is charge; the other three channels are magnetization. + for (int is = 0; is < dipole_channels; ++is) { std::stringstream ss_dipole; ss_dipole << global_out_dir << "dipole_s" << is + 1 << ".txt"; diff --git a/source/source_io/module_ctrl/ctrl_output_pw.cpp b/source/source_io/module_ctrl/ctrl_output_pw.cpp index 8294d7cbae6..ee1b36e18bf 100644 --- a/source/source_io/module_ctrl/ctrl_output_pw.cpp +++ b/source/source_io/module_ctrl/ctrl_output_pw.cpp @@ -246,10 +246,13 @@ void ModuleIO::ctrl_scf_pw(const int istep, //------------------------------------------------------------------ // 9) write onsite occupations for charge and magnetizations //------------------------------------------------------------------ - if (inp.onsite_radius > 0) - { // float type has not been implemented + const bool spinor_rt = inp.esolver_type == "tddft" && inp.nspin == 4; + // RT writes endpoint magnetic series instead of this static occupation report. + if (inp.onsite_radius > 0 && !spinor_rt) + { + stp.update_psi_d(); auto* onsite_p = projectors::OnsiteProjector::get_instance(); - onsite_p->cal_occupations(reinterpret_cast, Device>*>(stp.template get_psi_t()), + onsite_p->cal_occupations(stp.template get_psi_d(), pelec->wg, inp.nspin); } diff --git a/source/source_io/module_current/td_current_io.cpp b/source/source_io/module_current/td_current_io.cpp index 22100886e01..609de66ece6 100644 --- a/source/source_io/module_current/td_current_io.cpp +++ b/source/source_io/module_current/td_current_io.cpp @@ -28,6 +28,8 @@ void ModuleIO::write_current(const UnitCell& ucell, ModuleBase::TITLE("ModuleIO", "write_current"); ModuleBase::timer::start("ModuleIO", "write_current"); + // Restart replay is skipped by the caller, so only a fresh run writes step zero. + const std::ios::openmode mode = istep == 0 ? std::ios::out | std::ios::trunc : std::ios::app; if (td_p == nullptr) { ModuleBase::WARNING_QUIT("ModuleIO::write_current", "RT-TDDFT current requires an explicit TD context."); @@ -163,11 +165,13 @@ void ModuleIO::write_current(const UnitCell& ucell, { std::string filename = PARAM.globalv.global_out_dir + "current_tot.txt"; std::ofstream fout; - fout.open(filename, std::ios::app); - fout << std::setprecision(16); - fout << std::scientific; - fout << istep + 1 << " " << current_total[0] / omega << " " << current_total[1] / omega << " " << current_total[2] / omega - << std::endl; + fout.open(filename, mode); + fout << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout << " " << std::setw(19) << current_total[direction] / omega; + } + fout << std::endl; fout.close(); } @@ -190,6 +194,8 @@ void ModuleIO::write_current_eachk(const UnitCell& ucell, ModuleBase::TITLE("ModuleIO", "write_current"); ModuleBase::timer::start("ModuleIO", "write_current"); + // Restart replay is skipped by the caller, so only a fresh run writes step zero. + const std::ios::openmode mode = istep == 0 ? std::ios::out | std::ios::trunc : std::ios::app; if (td_p == nullptr) { ModuleBase::WARNING_QUIT("ModuleIO::write_current", "RT-TDDFT current requires an explicit TD context."); @@ -341,11 +347,13 @@ void ModuleIO::write_current_eachk(const UnitCell& ucell, std::string filename = PARAM.globalv.global_out_dir + "current_s" + std::to_string(is) + "k" + std::to_string(ik + 1) + ".txt"; std::ofstream fout; - fout.open(filename, std::ios::app); - fout << std::setprecision(16); - fout << std::scientific; - fout << istep + 1 << " " << current_ik[0] / omega << " " << current_ik[1] / omega << " " << current_ik[2] / omega - << std::endl; + fout.open(filename, mode); + fout << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout << " " << std::setw(19) << current_ik[direction] / omega; + } + fout << std::endl; fout.close(); } // write end @@ -355,11 +363,13 @@ void ModuleIO::write_current_eachk(const UnitCell& ucell, { std::string filename = PARAM.globalv.global_out_dir + "current_tot.txt"; std::ofstream fout; - fout.open(filename, std::ios::app); - fout << std::setprecision(16); - fout << std::scientific; - fout << istep + 1 << " " << current_total[0] / omega << " " << current_total[1] / omega << " " << current_total[2] / omega - << std::endl; + fout.open(filename, mode); + fout << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout << " " << std::setw(19) << current_total[direction] / omega; + } + fout << std::endl; fout.close(); } diff --git a/source/source_io/module_current/td_current_io_comm.cpp b/source/source_io/module_current/td_current_io_comm.cpp index 9b052451181..b4a66606361 100644 --- a/source/source_io/module_current/td_current_io_comm.cpp +++ b/source/source_io/module_current/td_current_io_comm.cpp @@ -695,6 +695,8 @@ void ModuleIO::write_current(const UnitCell& ucell, { ModuleBase::TITLE("ModuleIO", "write_current"); ModuleBase::timer::start("ModuleIO", "write_current"); + // Restart replay is skipped by the caller, so only a fresh run writes step zero. + const std::ios::openmode mode = istep == 0 ? std::ios::out | std::ios::trunc : std::ios::app; double omega = ucell.omega; std::vector> current_k; @@ -723,11 +725,13 @@ void ModuleIO::write_current(const UnitCell& ucell, std::string filename = PARAM.globalv.global_out_dir + "current_s" + std::to_string(is + 1) + "k" + std::to_string(kpoint_index) + "_comm.txt"; std::ofstream fout; - fout.open(filename, std::ios::app); - fout << std::setprecision(16); - fout << std::scientific; - fout << istep + 1 << " " << current_k[ik][0] / omega << " " << current_k[ik][1] / omega << " " - << current_k[ik][2] / omega << std::endl; + fout.open(filename, mode); + fout << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout << " " << std::setw(19) << current_k[ik][direction] / omega; + } + fout << std::endl; fout.close(); } } @@ -746,11 +750,13 @@ void ModuleIO::write_current(const UnitCell& ucell, { std::string filename = PARAM.globalv.global_out_dir + "current_tot_comm.txt"; std::ofstream fout; - fout.open(filename, std::ios::app); - fout << std::setprecision(16); - fout << std::scientific; - fout << istep + 1 << " " << current_total[0] / omega << " " << current_total[1] / omega << " " << current_total[2] / omega - << std::endl; + fout.open(filename, mode); + fout << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout << " " << std::setw(19) << current_total[direction] / omega; + } + fout << std::endl; fout.close(); } diff --git a/source/source_io/module_current/td_current_pw.cpp b/source/source_io/module_current/td_current_pw.cpp index a9a033aa4f1..8facc7c1d02 100644 --- a/source/source_io/module_current/td_current_pw.cpp +++ b/source/source_io/module_current/td_current_pw.cpp @@ -1,171 +1,40 @@ -#include "td_current_pw.h" +#include "source_io/module_current/td_current_pw.h" +#include "source_base/tool_quit.h" -#include "source_base/module_container/ATen/core/tensor.h" -#include "source_base/module_device/memory_op.h" -#include "source_base/parallel_comm.h" -#include "source_base/parallel_reduce.h" -#include "source_base/timer.h" -#include "source_hamilt/module_xc/xc_functional.h" -#include "source_hsolver/kernels/linear_op.h" - -// Keep operator dependencies local to this implementation file. -#include "source_pw/module_pwdft/op_pw_vel.h" - -#include #include #include #include -#include namespace ModuleIO { -template -PWCurrentResult CurrentPW::calculate(const UnitCell& ucell, - const ModulePW::PW_Basis_K* wfcpw, - psi::Psi, Device>* psi, - const elecstate::ElecState* pelec, - const K_Vectors& kv, - pseudopot_cell_vnl* ppcell, - const int gauge, - const ModuleBase::Vector3& vector_potential) -{ - ModuleBase::timer::start("ModuleIO", "calculate"); - - using Complex = std::complex; - using syncmem_complex_d2h_op = base_device::memory::synchronize_memory_op; - using setmem_complex_op = base_device::memory::set_memory_op; - - double current_total[3] = {0.0, 0.0, 0.0}; - const int nks = wfcpw->nks; - const int max_npw = wfcpw->npwk_max; - const int nbands = psi->get_nbands(); - const int nkstot = kv.get_nkstot(); - const int* isk = kv.isk.data(); - - const int n_npwx = nbands; - - // ============================================================== - // Store Cartesian current components for each k point. - // ============================================================== - const std::int64_t current_elements = 3 * static_cast(nkstot); - std::vector current_k(current_elements, 0.0); - - // Refresh the potential view used by the shared velocity operator. - const FPTYPE* vtau = nullptr; - int vtau_col = 0; - int vtau_row = 0; - if (gauge == 0 && XC_Functional::get_ked_flag()) - { - if (pelec->pot == nullptr) - { - ModuleBase::WARNING_QUIT("calculate", "Missing potential for meta-GGA current."); - } - vtau = pelec->pot->template get_vofk_smooth_data(); - vtau_col = pelec->pot->get_vofk_smooth().nc; - vtau_row = pelec->pot->get_vofk_smooth().nr; - if (vtau_col != wfcpw->nrxx || (wfcpw->nrxx > 0 && (vtau == nullptr || vtau_row != kv.get_spin_mult()))) - { - ModuleBase::WARNING_QUIT("calculate", "Invalid smooth-grid potential for meta-GGA current."); - } - // Preserve the enabled correction on ranks without real-space planes, which still join FFTs. - vtau_row = kv.get_spin_mult(); - } - if (!velocity_) - { - velocity_.reset(new hamilt::Velocity(wfcpw, isk, ppcell, &ucell, true, vtau, vtau_col, vtau_row)); - } - velocity_->set_state(isk, vtau, vtau_col, vtau_row); - - using CtDevice = typename ct::PsiToContainer::type; - const ct::DeviceType device = ct::DeviceTypeToEnum::value; - const std::int64_t component_elements = static_cast(n_npwx) * max_npw; - const std::int64_t velocity_elements = 3 * component_elements; - const std::int64_t velocity_size = std::max(1, velocity_elements); - const int64_t dot_size = std::max(1, n_npwx); - if (vpsi_.NumElements() < velocity_size || vpsi_.data_type() != ct::DataTypeToEnum::value || vpsi_.device_type() != device) - { - vpsi_ = ct::Tensor(ct::DataTypeToEnum::value, device, {velocity_size}); - } - if (dots_.NumElements() < dot_size || dots_.data_type() != ct::DataTypeToEnum::value || dots_.device_type() != device) - { - dots_ = ct::Tensor(ct::DataTypeToEnum::value, device, {dot_size}); - } - Complex* d_vpsi = vpsi_.template data(); - ct::Tensor& dot_buffer = dots_; - band_current_.resize(n_npwx); - std::vector& band_current = band_current_; - - for (int ik = 0; ik < nks; ++ik) - { - psi->fix_k(ik); - Complex* current_psi_ptr = psi->get_pointer(); - const int npw = wfcpw->npwk[ik]; - - if (velocity_elements > 0) - { - setmem_complex_op()(d_vpsi, 0, velocity_elements); - } - - velocity_->init(ik, vector_potential); - velocity_->act(psi, n_npwx, current_psi_ptr, d_vpsi, false); - - for (int id = 0; id < 3; ++id) - { - const Complex* component = d_vpsi + id * component_elements; - hsolver::linear_op().dot(max_npw, npw, n_npwx, current_psi_ptr, component, dot_buffer.data()); - syncmem_complex_d2h_op()(band_current.data(), dot_buffer.data(), n_npwx); - for (int ib = 0; ib < nbands; ++ib) - { - const double contribution = -pelec->wg(ik, ib) * std::real(band_current[ib]); - current_total[id] += contribution; - const std::int64_t current_index = static_cast(kv.ik2iktot[ik]) * 3 + id; - current_k[current_index] += contribution; - } - } - } - - // ============================================================== - // Reduce all current components together across MPI ranks. - // ============================================================== - Parallel_Reduce::reduce_all(current_total, 3); - const std::int64_t max_chunk = std::numeric_limits::max(); - for (std::int64_t offset = 0; offset < current_elements;) - { - const std::int64_t remaining = current_elements - offset; - const int count = static_cast(std::min(max_chunk, remaining)); - double* chunk = current_k.data() + offset; - Parallel_Reduce::reduce_all(chunk, count); - offset += count; - } - - PWCurrentResult result; - for (int d = 0; d < 3; ++d) - { - result.total[d] = current_total[d] / ucell.omega; - } - for (double& value: current_k) - { - value /= ucell.omega; - } - result.per_k = std::move(current_k); - ModuleBase::timer::end("ModuleIO", "calculate"); - return result; -} - -void write_pw_current(const PWCurrentResult& current, +void write_pw_current(const pw::TDVelResultPW& current, const int istep, const int nk_per_spin, const bool out_current_k, const std::string& out_dir) { + // The initial endpoint starts a new series; later endpoints append. + const std::ios::openmode mode = istep == 0 ? std::ios::out | std::ios::trunc : std::ios::app; // Always write the total current when this routine is enabled. std::string filename_tot = out_dir + "current_tot.txt"; std::ofstream fout_tot; - fout_tot.open(filename_tot, std::ios::app); - fout_tot << std::setprecision(16) << std::scientific; - fout_tot << istep + 1 << " " << current.total[0] << " " << current.total[1] << " " << current.total[2] << std::endl; + fout_tot.open(filename_tot, mode); + if (!fout_tot) + { + ModuleBase::WARNING_QUIT("write_pw_current", "Cannot open " + filename_tot); + } + fout_tot << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (double value: current.total) + { + fout_tot << " " << std::setw(19) << value; + } + fout_tot << std::endl; fout_tot.close(); + if (!fout_tot) + { + ModuleBase::WARNING_QUIT("write_pw_current", "Failed to write " + filename_tot); + } // Optionally write the contribution from each k point. if (out_current_k) @@ -178,42 +47,25 @@ void write_pw_current(const PWCurrentResult& current, std::string filename_k = out_dir + "current_s" + std::to_string(is) + "k" + std::to_string(k_idx) + ".txt"; std::ofstream fout_k; - fout_k.open(filename_k, std::ios::app); - fout_k << std::setprecision(16) << std::scientific; + fout_k.open(filename_k, mode); + if (!fout_k) + { + ModuleBase::WARNING_QUIT("write_pw_current", "Cannot open " + filename_k); + } const std::int64_t offset = static_cast(ik) * 3; - fout_k << istep + 1 << " " << current.per_k[offset] << " " << current.per_k[offset + 1] << " " << current.per_k[offset + 2] - << std::endl; + fout_k << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); + for (int direction = 0; direction < 3; ++direction) + { + fout_k << " " << std::setw(19) << current.per_k[offset + direction]; + } + fout_k << std::endl; fout_k.close(); + if (!fout_k) + { + ModuleBase::WARNING_QUIT("write_pw_current", "Failed to write " + filename_k); + } } } } -template -void CurrentPW::write(const int istep, - const UnitCell& ucell, - const ModulePW::PW_Basis_K* wfcpw, - psi::Psi, Device>* psi, - const elecstate::ElecState* pelec, - const K_Vectors& kv, - pseudopot_cell_vnl* ppcell, - const int gauge, - const ModuleBase::Vector3& A_right_ha, - const bool out_current_k, - const std::string& out_dir, - const int world_rank) -{ - const PWCurrentResult result = calculate(ucell, wfcpw, psi, pelec, kv, ppcell, gauge, A_right_ha); - if (world_rank == 0) - { - const int nk_per_spin = kv.get_nkstot() / kv.get_spin_mult(); - write_pw_current(result, istep, nk_per_spin, out_current_k, out_dir); - } -} - -template class CurrentPW; -template class CurrentPW; -#if defined(__CUDA) || defined(__ROCM) -template class CurrentPW; -template class CurrentPW; -#endif } // namespace ModuleIO diff --git a/source/source_io/module_current/td_current_pw.h b/source/source_io/module_current/td_current_pw.h index c240cef8746..825b79949dd 100644 --- a/source/source_io/module_current/td_current_pw.h +++ b/source/source_io/module_current/td_current_pw.h @@ -1,65 +1,13 @@ #ifndef TD_CURRENT_PW_H #define TD_CURRENT_PW_H -#include "source_cell/klist.h" -#include "source_cell/unitcell.h" -#include "source_estate/elecstate.h" -#include "source_psi/psi.h" -#include "source_pw/module_pwdft/op_pw_vel.h" -#include "source_pw/module_pwdft/vnl_pw.h" +#include "source_pw/module_pwdft/td_obs_pw.h" -#include #include namespace ModuleIO { -/** @brief Reduced current densities in Hartree atomic units. */ -struct PWCurrentResult -{ - ModuleBase::Vector3 total; - std::vector per_k; -}; - -/** @brief Write already reduced current densities; performs no collective operations. */ -void write_pw_current(const PWCurrentResult& current, - const int istep, - const int nk_per_spin, - const bool out_current_k, - const std::string& out_dir); - -/** @brief Persistent one-k-point current workspace for a propagation run. */ -template -class CurrentPW -{ - private: - std::unique_ptr> velocity_; - ct::Tensor vpsi_; - ct::Tensor dots_; - std::vector> band_current_; - - public: - /** @brief Compute globally reduced current densities without file output. */ - PWCurrentResult calculate(const UnitCell& ucell, - const ModulePW::PW_Basis_K* wfcpw, - psi::Psi, Device>* psi, - const elecstate::ElecState* pelec, - const K_Vectors& kv, - pseudopot_cell_vnl* ppcell, - const int gauge, - const ModuleBase::Vector3& A_right_ha); - /** @brief Evaluate and write the current using native wavefunctions. */ - void write(const int istep, - const UnitCell& ucell, - const ModulePW::PW_Basis_K* wfcpw, - psi::Psi, Device>* psi, - const elecstate::ElecState* pelec, - const K_Vectors& kv, - pseudopot_cell_vnl* ppcell, - const int gauge, - const ModuleBase::Vector3& vector_potential, - const bool out_current_k, - const std::string& out_dir, - const int world_rank); -}; +/** @brief Write reduced charge currents on the output rank; performs no collectives. */ +void write_pw_current(const pw::TDVelResultPW& current, int istep, int nk_per_spin, bool out_current_k, const std::string& out_dir); } // namespace ModuleIO #endif diff --git a/source/source_io/module_current/td_spin_pw_io.cpp b/source/source_io/module_current/td_spin_pw_io.cpp new file mode 100644 index 00000000000..2d36db90c3b --- /dev/null +++ b/source/source_io/module_current/td_spin_pw_io.cpp @@ -0,0 +1,180 @@ +#include "source_io/module_current/td_spin_pw_io.h" + +#include "source_base/tool_quit.h" + +#include +#include +#include +#include +#include +#include + +namespace ModuleIO +{ +namespace +{ +std::ofstream open_series(const std::string& path, int step, const std::string& header, std::ostream& log) +{ + const std::ios::openmode mode = step == 0 ? std::ios::out : std::ios::app; + std::ofstream output(path.c_str(), mode); + if (!output) + { + ModuleBase::WARNING_QUIT("write_td_spin_pw", "Cannot open " + path); + } + if (step == 0) + { + output << header << '\n'; + char cwd[PATH_MAX]; + std::string full_path = path; + if (!path.empty() && path[0] != '/' && getcwd(cwd, sizeof(cwd)) != nullptr) + { + full_path = std::string(cwd) + "/" + path; + } + log << "Write file: " << full_path << '\n'; + } + return output; +} + +void finish_series(std::ofstream* output) +{ + output->flush(); + if (!output->good()) + { + ModuleBase::WARNING_QUIT("write_td_spin_pw", "Failed to write magnetic time series."); + } +} + +void write_endpoint(std::ostream& output, int step, double time_fs) +{ + // Internal step zero is the initial state at time zero, displayed as step one. + output << std::right << std::setw(6) << step + 1 << " " << std::fixed << std::setprecision(6) << std::setw(10) << time_fs; + output << std::scientific << std::setprecision(12); +} + +void write_atoms(std::ofstream* output, int step, double time, const std::vector& values) +{ + for (size_t atom = 0; atom < values.size() / 3; ++atom) + { + write_endpoint(*output, step, time); + *output << " " << std::setw(6) << atom + 1; + for (int a = 0; a < 3; ++a) + { + *output << " " << std::setw(19) << values[3 * atom + a]; + } + *output << '\n'; + } + finish_series(output); +} +} // namespace + +void write_td_spin_pw(const std::string& directory, + int step, + double time_fs, + const std::vector& orbitals, + const pw::TDSpinOptsPW& options, + bool spin_current, + const pw::TDSpinResultPW& spin, + const pw::TDVelResultPW& current, + std::ostream& log) +{ + if (options.method != "none") + { + std::string header = "# method=" + options.method + "; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign"; + if (options.method == "onsite") + { + std::ostringstream radius; + radius << std::scientific << std::setprecision(12) << options.radius; + header += "\n# onsite_radius(Bohr)=" + radius.str() + "; projector=q(k+G+A); atom_phase_cancels"; + for (int it = 0; it < orbitals.size(); ++it) + { + header += "\n# orbital=" + options.orbital_dir + orbitals[it]; + } + } + std::ostringstream total_columns; + total_columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs"; + for (const char* label: {"Mx", "My", "Mz", "M_norm", "M_abs_integral", "N_elec"}) + { + total_columns << " " << std::setw(19) << label; + } + std::ostringstream atom_columns; + atom_columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs" + << " " << std::setw(6) << "atom"; + for (const char* label: {"Mx", "My", "Mz"}) + { + atom_columns << " " << std::setw(19) << label; + } + const std::string total_header = header + '\n' + total_columns.str(); + const std::string atom_header = header + '\n' + atom_columns.str(); + const std::string total_path = directory + "mag_tot.txt"; + const std::string atom_path = directory + "mag_atom.txt"; + std::ofstream total = open_series(total_path, step, total_header, log); + std::ofstream atoms = open_series(atom_path, step, atom_header, log); + write_endpoint(total, step, time_fs); + double norm = 0.0; + for (double value: spin.mag) + { + total << " " << std::setw(19) << value; + norm += value * value; + } + total << " " << std::setw(19) << std::sqrt(norm) << " " << std::setw(19) << spin.mag_abs << " " << std::setw(19) + << spin.electrons << '\n'; + finish_series(&total); + write_atoms(&atoms, step, time_fs, spin.atom_mag); + } + if (options.torque) + { + std::ostringstream total_columns; + total_columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs"; + for (const char* label: {"tau_xc_x", "tau_xc_y", "tau_xc_z", "tau_soc_x", "tau_soc_y", "tau_soc_z"}) + { + total_columns << " " << std::setw(19) << label; + } + std::ostringstream atom_columns; + atom_columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs" + << " " << std::setw(6) << "atom"; + for (const char* label: {"tau_soc_x", "tau_soc_y", "tau_soc_z"}) + { + atom_columns << " " << std::setw(19) << label; + } + const std::string total_header = "# torque=dS/dt; S=M/2; unit=hbar/Hartree_atomic_time\n" + total_columns.str(); + const std::string atom_header = "# SOC_pseudopotential_atom_decomposition; unit=hbar/Hartree_atomic_time\n" + "# not_the_derivative_of_atomic_moments\n" + + atom_columns.str(); + const std::string total_path = directory + "torque_tot.txt"; + const std::string atom_path = directory + "torque_atom.txt"; + std::ofstream total = open_series(total_path, step, total_header, log); + std::ofstream atoms = open_series(atom_path, step, atom_header, log); + write_endpoint(total, step, time_fs); + for (double value: spin.xc) + { + total << " " << std::setw(19) << value; + } + for (double value: spin.soc) + { + total << " " << std::setw(19) << value; + } + total << '\n'; + finish_series(&total); + write_atoms(&atoms, step, time_fs, spin.atom_soc); + } + if (spin_current) + { + std::ostringstream columns; + columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs"; + for (const char* label: {"Jxx", "Jxy", "Jxz", "Jyx", "Jyy", "Jyz", "Jzx", "Jzy", "Jzz"}) + { + columns << " " << std::setw(19) << label; + } + const std::string header = "# S=M/2; unit=hbar/(Bohr^2*Hartree_atomic_time); flow_index_first\n" + columns.str(); + const std::string path = directory + "spincur_tot.txt"; + std::ofstream output = open_series(path, step, header, log); + write_endpoint(output, step, time_fs); + for (double value: current.spin) + { + output << " " << std::setw(19) << value; + } + output << '\n'; + finish_series(&output); + } +} +} // namespace ModuleIO diff --git a/source/source_io/module_current/td_spin_pw_io.h b/source/source_io/module_current/td_spin_pw_io.h new file mode 100644 index 00000000000..f2bf14d5cff --- /dev/null +++ b/source/source_io/module_current/td_spin_pw_io.h @@ -0,0 +1,23 @@ +#ifndef TD_SPIN_PW_IO_H +#define TD_SPIN_PW_IO_H +#include "source_pw/module_pwdft/td_obs_pw.h" + +#include +namespace ModuleIO +{ +/** + * @brief Write globally reduced endpoint data on the designated output rank only. + * @param step Zero-based internal electronic step, written as step + 1. + * @param time_fs Physical time in fs, written with six decimal places; the initial state is at zero. + */ +void write_td_spin_pw(const std::string& directory, + int step, + double time_fs, + const std::vector& orbitals, + const pw::TDSpinOptsPW& options, + bool spin_current, + const pw::TDSpinResultPW& spin, + const pw::TDVelResultPW& current, + std::ostream& log); +} // namespace ModuleIO +#endif diff --git a/source/source_io/module_dipole/dipole_io.h b/source/source_io/module_dipole/dipole_io.h index c38f5f93b7c..8ac8f72a1a7 100644 --- a/source/source_io/module_dipole/dipole_io.h +++ b/source/source_io/module_dipole/dipole_io.h @@ -9,13 +9,14 @@ namespace ModuleIO { +/** @brief Write aligned electronic dipoles; precision counts significant digits. */ void write_dipole(const UnitCell& ucell, const double* rho, const ModulePW::PW_Basis* rhopw, const int& istep, const std::string& fn, std::ofstream& ofs_running, - const int& precision = 11); + const int& precision = 13); } // namespace ModuleIO diff --git a/source/source_io/module_dipole/write_dipole.cpp b/source/source_io/module_dipole/write_dipole.cpp index 74020c0492e..19fb6bdcea6 100644 --- a/source/source_io/module_dipole/write_dipole.cpp +++ b/source/source_io/module_dipole/write_dipole.cpp @@ -1,6 +1,8 @@ #include "source_base/parallel_reduce.h" #include "source_io/module_dipole/dipole_io.h" +#include + namespace ModuleIO { @@ -40,7 +42,7 @@ void print_dipole_moment(std::ofstream& ofs_running, const std::string& name, do // - istep: current time step // - fn: output file name // - ofs_running: output stream for logging -// - precision: floating-point precision for output +// - precision: number of significant digits for output void write_dipole(const UnitCell& ucell, const double* rho, const ModulePW::PW_Basis* rhopw, @@ -67,10 +69,10 @@ void write_dipole(const UnitCell& ucell, ofs_running << " Write dipole data to file: " << fn << std::endl; - // Validate grid dimensions to prevent division by zero - if (rhopw->nx == 0 || rhopw->ny == 0 || rhopw->nz == 0 || rhopw->nxyz == 0 || rhopw->nplane == 0) + // Empty local FFT partitions contribute zero but must still join the subsequent reduction. + if (rhopw->nx == 0 || rhopw->ny == 0 || rhopw->nz == 0 || rhopw->nxyz == 0 || (rhopw->nplane == 0 && rhopw->nrxx != 0)) { - ModuleBase::WARNING_QUIT("ModuleIO::write_dipole", "Invalid grid parameters: nx, ny, nz, nxyz, or nplane is zero"); + ModuleBase::WARNING_QUIT("ModuleIO::write_dipole", "Invalid global grid dimensions or inconsistent local plane count"); } // Accumulate the electronic first moment in fractional coordinates. @@ -118,8 +120,12 @@ void write_dipole(const UnitCell& ucell, print_dipole_moment(ofs_running, "Electronic dipole moment", dipole_elec[0], dipole_elec[1], dipole_elec[2]); // Write to file: step index and three dipole components - ofs << std::setprecision(precision) << istep + 1 << " " << dipole_elec[0] << " " << dipole_elec[1] << " " << dipole_elec[2] - << std::endl; + ofs << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(precision - 1); + for (int direction = 0; direction < 3; ++direction) + { + ofs << " " << std::setw(19) << dipole_elec[direction]; + } + ofs << std::endl; // Calculate ionic dipole moment // Accumulate Z_v * taud, then transform to Cartesian coordinates. diff --git a/source/source_io/module_efield/td_efield_io.cpp b/source/source_io/module_efield/td_efield_io.cpp index 7e081639664..289ec6dd9e0 100644 --- a/source/source_io/module_efield/td_efield_io.cpp +++ b/source/source_io/module_efield/td_efield_io.cpp @@ -1,4 +1,4 @@ -#include "td_efield_io.h" +#include "source_io/module_efield/td_efield_io.h" #include "source_base/constants.h" #include "source_base/tool_quit.h" @@ -11,7 +11,7 @@ namespace { -std::string td_field_output_path(const std::string& output_dir, const std::size_t field_index) +std::string field_out_path(const std::string& output_dir, const std::size_t field_index) { std::stringstream path; path << output_dir << "efield_" << field_index + 1 << ".txt"; @@ -23,7 +23,7 @@ std::string td_field_output_path(const std::string& output_dir, const std::size_ namespace ModuleIO { -void prepare_td_field_output(const std::string& output_dir, const std::size_t field_count, const bool restart) +void prep_td_field_out(const std::string& output_dir, const std::size_t field_count, const bool restart) { // A restart continues an existing time series. A fresh calculation opens // every configured file with `out`, which truncates any previous series. @@ -34,29 +34,34 @@ void prepare_td_field_output(const std::string& output_dir, const std::size_t fi for (std::size_t field_index = 0; field_index < field_count; ++field_index) { - const std::string output_path = td_field_output_path(output_dir, field_index); + const std::string output_path = field_out_path(output_dir, field_index); std::ofstream output(output_path.c_str(), std::ofstream::out); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::prepare_td_field_output", "Cannot prepare electric-field file " + output_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::prep_td_field_out", "Cannot prepare electric-field file " + output_path + "!"); } } } -void prepare_td_field_output(const std::string& output_dir, const elecstate::TDFieldManager& manager, const bool restart) +void prep_td_field_out(const std::string& output_dir, const elecstate::TDFieldManager& manager, const bool restart) { if (!manager.file_source()) { - prepare_td_field_output(output_dir, manager.fields().size(), restart); + prep_td_field_out(output_dir, manager.fields().size(), restart); } if (!restart) { std::ofstream output((output_dir + "efield_total.txt").c_str()); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::prepare_td_field_output", "Cannot prepare efield_total.txt."); + ModuleBase::WARNING_QUIT("ModuleIO::prep_td_field_out", "Cannot prepare efield_total.txt."); } - output << "# time(fs) Ex(V/Angstrom) Ey(V/Angstrom) Ez(V/Angstrom); "; + output << std::left << std::setw(10) << "# time(fs)" << std::right; + for (const char* label: {"Ex(V/Angstrom)", "Ey(V/Angstrom)", "Ez(V/Angstrom)"}) + { + output << " " << std::setw(19) << label; + } + output << " # "; if (manager.file_source()) { output << "endpoint finite differences\n"; @@ -68,19 +73,21 @@ void prepare_td_field_output(const std::string& output_dir, const elecstate::TDF } } -void write_td_field_values(const elecstate::TDFieldManager& manager, const std::string& output_dir) +void write_td_field(const elecstate::TDFieldManager& manager, const std::string& output_dir) { + const double time_fs = manager.current_step() * manager.dt_ha() * ModuleBase::AU_to_FS; // Keep the complete time series, including zero fields outside the analytic window. { std::ofstream output((output_dir + "efield_total.txt").c_str(), std::ofstream::app); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::write_td_field_values", "Cannot append efield_total.txt."); + ModuleBase::WARNING_QUIT("ModuleIO::write_td_field", "Cannot append efield_total.txt."); } - output << std::scientific << std::setprecision(16) << manager.current_step() * manager.dt_ha() * ModuleBase::AU_to_FS; + output << std::right << std::fixed << std::setprecision(6) << std::setw(10) << time_fs; + output << std::scientific << std::setprecision(12); for (int direction = 0; direction < 3; ++direction) { - output << '\t' << manager.efield_ha()[direction] * ModuleBase::Hartree_to_eV / ModuleBase::BOHR_TO_A; + output << " " << std::setw(19) << manager.efield_ha()[direction] * ModuleBase::Hartree_to_eV / ModuleBase::BOHR_TO_A; } output << '\n'; } @@ -93,16 +100,17 @@ void write_td_field_values(const elecstate::TDFieldManager& manager, const std:: for (std::size_t field_index = 0; field_index < field_values.size(); ++field_index) { // Keep one file per input occurrence even when directions repeat. - const std::string output_path = td_field_output_path(output_dir, field_index); + const std::string output_path = field_out_path(output_dir, field_index); std::ofstream output(output_path.c_str(), std::ofstream::app); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::write_td_field_values", "Cannot append electric-field file " + output_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::write_td_field", "Cannot append electric-field file " + output_path + "!"); } // Convert only at the user-visible output boundary: time to fs and the // electric field to V/Angstrom. - output << manager.current_step() * manager.dt_ha() * ModuleBase::AU_to_FS << "\t" - << field_values[field_index] * ModuleBase::Hartree_to_eV / ModuleBase::BOHR_TO_A << std::endl; + output << std::right << std::fixed << std::setprecision(6) << std::setw(10) << time_fs << " " << std::scientific + << std::setprecision(12) << std::setw(19) << field_values[field_index] * ModuleBase::Hartree_to_eV / ModuleBase::BOHR_TO_A + << std::endl; } } diff --git a/source/source_io/module_efield/td_efield_io.h b/source/source_io/module_efield/td_efield_io.h index 6c6b83ac3e5..474b2a7e701 100644 --- a/source/source_io/module_efield/td_efield_io.h +++ b/source/source_io/module_efield/td_efield_io.h @@ -19,18 +19,20 @@ namespace ModuleIO * @param field_count Number of configured time-dependent electric fields. * @param restart Whether the calculation continues from an MD/RT-TDDFT restart. */ -void prepare_td_field_output(const std::string& output_dir, std::size_t field_count, bool restart); +void prep_td_field_out(const std::string& output_dir, std::size_t field_count, bool restart); /** @brief Prepare total endpoint E and, for analytic fields, the configured waveform files. */ -void prepare_td_field_output(const std::string& output_dir, const elecstate::TDFieldManager& manager, bool restart); +void prep_td_field_out(const std::string& output_dir, const elecstate::TDFieldManager& manager, bool restart); /** * @brief Append the current electric-field samples to their output files. + * Times are written in fs with six decimal places, starting at physical time zero. + * Field components use scientific notation with twelve decimal places; columns are right-aligned. * * @param manager Time-dependent electric-field state for the current electronic step. * @param output_dir Output directory including its trailing path separator. */ -void write_td_field_values(const elecstate::TDFieldManager& manager, const std::string& output_dir); +void write_td_field(const elecstate::TDFieldManager& manager, const std::string& output_dir); } // namespace ModuleIO diff --git a/source/source_io/module_efield/td_vector_pot_io.cpp b/source/source_io/module_efield/td_vecpot_io.cpp similarity index 54% rename from source/source_io/module_efield/td_vector_pot_io.cpp rename to source/source_io/module_efield/td_vecpot_io.cpp index 9de3561f481..c8ff3db213e 100644 --- a/source/source_io/module_efield/td_vector_pot_io.cpp +++ b/source/source_io/module_efield/td_vecpot_io.cpp @@ -1,4 +1,4 @@ -#include "td_vector_pot_io.h" +#include "source_io/module_efield/td_vecpot_io.h" #include "source_base/tool_quit.h" @@ -13,15 +13,19 @@ namespace const char* VECTOR_POT_FILENAME = "vector_pot.txt"; -std::string vector_pot_path(const std::string& directory) +std::string vecpot_path(const std::string& directory) { return directory + VECTOR_POT_FILENAME; } -void write_vector_pot_header(std::ofstream& output) +void wr_vecpot_header(std::ofstream& output) { - output << std::left << std::setw(8) << "#istep" << std::setw(15) << "A_x" << std::setw(15) << "A_y" << std::setw(15) << "A_z" - << "# endpoint values, Hartree atomic units" << std::endl; + output << std::left << std::setw(6) << "#istep" << std::right; + for (const char* label: {"A_x", "A_y", "A_z"}) + { + output << " " << std::setw(19) << label; + } + output << " # endpoint values, Hartree atomic units" << std::endl; } bool is_blank_or_comment(const std::string& line) @@ -35,13 +39,13 @@ bool is_blank_or_comment(const std::string& line) namespace ModuleIO { -std::vector> read_td_vector_pot(const std::string& input_dir) +std::vector> read_td_vecpot(const std::string& input_dir) { - const std::string input_path = vector_pot_path(input_dir); + const std::string input_path = vecpot_path(input_dir); std::ifstream input(input_path.c_str()); if (!input) { - ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", "Cannot open vector-potential file " + input_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vecpot", "Cannot open vector-potential file " + input_path + "!"); } std::vector> vector_potentials; @@ -60,7 +64,7 @@ std::vector> read_td_vector_pot(const std::string& i ModuleBase::Vector3 vector_pot; if (!(row >> step_label >> vector_pot[0] >> vector_pot[1] >> vector_pot[2])) { - ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vecpot", "Invalid vector-potential data on line " + std::to_string(line_number) + " of " + input_path + "!"); } row >> std::ws; @@ -68,12 +72,12 @@ std::vector> read_td_vector_pot(const std::string& i { if (!std::isfinite(vector_pot[direction])) { - ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", "Non-finite vector potential in " + input_path); + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vecpot", "Non-finite vector potential in " + input_path); } } if (!row.eof()) { - ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vecpot", "Unexpected vector-potential data on line " + std::to_string(line_number) + " of " + input_path + "!"); } vector_potentials.push_back(vector_pot); @@ -81,28 +85,28 @@ std::vector> read_td_vector_pot(const std::string& i if (vector_potentials.empty()) { - ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", "No vector-potential data found in " + input_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vecpot", "No vector-potential data found in " + input_path + "!"); } return vector_potentials; } -void check_td_vector_pot_paths(const std::string& input_dir, const std::string& output_dir) +void check_td_vecpot_paths(const std::string& input_dir, const std::string& output_dir) { struct stat input_state; struct stat output_state; - const std::string input_path = vector_pot_path(input_dir); - const std::string output_path = vector_pot_path(output_dir); + const std::string input_path = vecpot_path(input_dir); + const std::string output_path = vecpot_path(output_dir); // Different paths can name the same file through symbolic or hard links; compare file identity before writing. if (stat(input_path.c_str(), &input_state) == 0 && stat(output_path.c_str(), &output_state) == 0 && input_state.st_dev == output_state.st_dev && input_state.st_ino == output_state.st_ino) { - ModuleBase::WARNING_QUIT("ModuleIO::check_td_vector_pot_paths", "Vector-potential output would overwrite its input file."); + ModuleBase::WARNING_QUIT("ModuleIO::check_td_vecpot_paths", "Vector-potential output would overwrite its input file."); } } -void prepare_td_vector_pot_output(const std::string& output_dir, const bool restart) +void prep_td_vecpot_out(const std::string& output_dir, const bool restart) { - const std::string output_path = vector_pot_path(output_dir); + const std::string output_path = vecpot_path(output_dir); if (restart) { std::ifstream existing(output_path.c_str(), std::ifstream::binary | std::ifstream::ate); @@ -115,24 +119,24 @@ void prepare_td_vector_pot_output(const std::string& output_dir, const bool rest std::ofstream output(output_path.c_str(), std::ofstream::out); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::prepare_td_vector_pot_output", "Cannot prepare vector-potential file " + output_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::prep_td_vecpot_out", "Cannot prepare vector-potential file " + output_path + "!"); } - write_vector_pot_header(output); + wr_vecpot_header(output); } -void write_td_vector_pot(const std::string& output_dir, const int electronic_step, const ModuleBase::Vector3& vector_pot) +void write_td_vecpot(const std::string& output_dir, const int electronic_step, const ModuleBase::Vector3& vector_pot) { - const std::string output_path = vector_pot_path(output_dir); + const std::string output_path = vecpot_path(output_dir); std::ofstream output(output_path.c_str(), std::ofstream::app); if (!output) { - ModuleBase::WARNING_QUIT("ModuleIO::write_td_vector_pot", "Cannot append vector-potential file " + output_path + "!"); + ModuleBase::WARNING_QUIT("ModuleIO::write_td_vecpot", "Cannot append vector-potential file " + output_path + "!"); } - output << std::left << std::setw(8) << electronic_step + 1; + output << std::right << std::setw(6) << electronic_step + 1 << std::scientific << std::setprecision(12); for (int direction = 0; direction < 3; ++direction) { - output << std::scientific << std::setprecision(16) << std::setw(25) << vector_pot[direction]; + output << " " << std::setw(19) << vector_pot[direction]; } output << std::endl; } diff --git a/source/source_io/module_efield/td_vector_pot_io.h b/source/source_io/module_efield/td_vecpot_io.h similarity index 68% rename from source/source_io/module_efield/td_vector_pot_io.h rename to source/source_io/module_efield/td_vecpot_io.h index d61400fd663..7247fdf34c3 100644 --- a/source/source_io/module_efield/td_vector_pot_io.h +++ b/source/source_io/module_efield/td_vecpot_io.h @@ -1,5 +1,5 @@ -#ifndef TD_VECTOR_POT_IO_H -#define TD_VECTOR_POT_IO_H +#ifndef TD_VECPOT_IO_H +#define TD_VECPOT_IO_H #include "source_base/vector3.h" @@ -15,10 +15,10 @@ namespace ModuleIO * @param input_dir Input directory including its trailing path separator. * @return Vector potentials in atomic units, ordered by file row. */ -std::vector> read_td_vector_pot(const std::string& input_dir); +std::vector> read_td_vecpot(const std::string& input_dir); /** @brief Reject output that aliases the input file, including symbolic and hard links. */ -void check_td_vector_pot_paths(const std::string& input_dir, const std::string& output_dir); +void check_td_vecpot_paths(const std::string& input_dir, const std::string& output_dir); /** * @brief Prepare the vector-potential output file for an RT-TDDFT calculation. @@ -26,7 +26,7 @@ void check_td_vector_pot_paths(const std::string& input_dir, const std::string& * @param output_dir Output directory including its trailing path separator. * @param restart Whether the calculation continues from an MD/RT-TDDFT restart. */ -void prepare_td_vector_pot_output(const std::string& output_dir, bool restart); +void prep_td_vecpot_out(const std::string& output_dir, bool restart); /** * @brief Append one Cartesian vector-potential sample. @@ -35,7 +35,7 @@ void prepare_td_vector_pot_output(const std::string& output_dir, bool restart); * @param electronic_step Zero-based electronic-step index. * @param vector_pot Cartesian vector potential in atomic units. */ -void write_td_vector_pot(const std::string& output_dir, int electronic_step, const ModuleBase::Vector3& vector_pot); +void write_td_vecpot(const std::string& output_dir, int electronic_step, const ModuleBase::Vector3& vector_pot); } // namespace ModuleIO diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index 75be1fba4b8..f085028f9f9 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -320,6 +320,9 @@ struct Input_para std::string lin_solver = "gmres"; ///< linear solver for real-time propagation std::string td_orthonormal = "cholesky"; ///< Orthonormalization for PW real-time propagation. bool td_out_stat = false; ///< Output PW real-time conservation diagnostics. + std::string td_out_mag = "voronoi"; ///< RT-TDDFT spinor magnetic moment partition. + bool td_out_torque = false; ///< Output XC and SOC spin angular momentum torques. + bool td_out_spincur = false; ///< Output the macroscopic spin angular momentum current. int lin_gmres_restart = 20; ///< Maximum Arnoldi steps per GMRES cycle. bool td_cn_init = true; ///< CN subspace initial guess and residual reuse. bool lin_reconstruct = true; ///< Explicit GMRES residual reconstruction with periodic audits. diff --git a/source/source_io/module_parameter/read_inp_estruc.cpp b/source/source_io/module_parameter/read_inp_estruc.cpp index 8867fb9e268..d7d90ecb422 100644 --- a/source/source_io/module_parameter/read_inp_estruc.cpp +++ b/source/source_io/module_parameter/read_inp_estruc.cpp @@ -444,15 +444,22 @@ The other way is only available when compiling with LIBXC, and it allows for sup item.annotation = "Noncollinear GGA gradient method: 0 original, 1 local axis, 2 regularized mapped-density LCA"; item.category = "Electronic structure"; item.type = "Integer"; - item.description = R"(Selects the local spin mapping for LDA/GGA functionals in magnetic nspin=4 calculations. -* 0: preserves the original algorithm (default). -* 1: uses the local magnetization magnitude instead of the global quantization axis in the built-in GGA gradient correction. For LIBXC functionals, 0 and 1 are equivalent. -* 2: uses a C2-regularized magnetization magnitude with eta = 1e-3 in atomic density units. The spin densities are (abs(n + rho_core) +/- min(S_eta(m), abs(n + rho_core)))/2. GGA gradients are the FFT gradients of these mapped spin densities. The potential reverses this same discrete energy graph: apply the FFT divergence to each spin-channel flux, then apply the local-map Jacobian. Density/sigma clipping branches and the GGA metric stress derivative use the same mapped densities. This changes the finite-grid functional relative to projecting the four density-channel gradients; converged energies can therefore change. -For r = |m| and x = r/eta, S_eta = eta*x^3*(3*x^2 - 8*x + 6) for r < eta, and S_eta = r otherwise. The regularization is part of the functional definition, including its first and second derivatives. -Mode 2 also uses this local map for the LDA contribution. Other spin configurations retain their existing behavior.)"; - item.default_value = "0"; + item.description = R"(Selects the local spin mapping for LDA/GGA functionals in magnetic `nspin=4` calculations. +* `0`: In the built-in GGA gradient correction, constructs spin densities using the local magnetization magnitude, with its sign determined by the projection onto the global quantization axis when that axis is enabled. Otherwise, uses the unsigned magnitude. +* `1`: Always uses the unsigned local magnetization magnitude in the built-in GGA gradient correction, independent of the global quantization axis. For Libxc functionals, `0` and `1` are equivalent. +* `2`: Uses the regularized spin densities $\rho_{\pm}=\frac{|n+\rho_{\mathrm{core}}|\pm\min(S_{\eta}(r),|n+\rho_{\mathrm{core}}|)}{2}$, where $n$ is the valence electron density, $\rho_{\mathrm{core}}$ is the NLCC core density, and $r=|\boldsymbol{m}|$. With $x=r/\eta$ and fixed $\eta=10^{-3}\,\mathrm{electrons}/\mathrm{bohr}^{3}$, the $C^2$ mapping is $S_{\eta}(r)=\eta x^3(3x^2-8x+6)$ for $r\lt\eta$, and $S_{\eta}(r)=r$ otherwise. GGA gradients use these mapped densities. This regularization affects both LDA and GGA and can change energies and magnetic responses. + +When omitted, the value is `1` for `esolver_type=tddft` and `0` otherwise. Explicit values are preserved.)"; + item.default_value = "`1` for `esolver_type=tddft`; `0` otherwise"; + item.unit = ""; item.domain.set_choices({"0", "1", "2"}); read_sync_int(input.gga_grad); + item.reset_value = [](const Input_Item& item, Parameter& para) { + if (!item.is_read() && para.inp.esolver_type == "tddft") + { + para.input.gga_grad = 1; + } + }; this->add_item(item); } { diff --git a/source/source_io/module_parameter/read_inp_exx_dftu.cpp b/source/source_io/module_parameter/read_inp_exx_dftu.cpp index 2d60bdc7aaa..c4c9bc8c441 100644 --- a/source/source_io/module_parameter/read_inp_exx_dftu.cpp +++ b/source/source_io/module_parameter/read_inp_exx_dftu.cpp @@ -2,6 +2,7 @@ #include "source_base/tool_quit.h" #include "read_input.h" #include "read_input_tool.h" +#include namespace ModuleIO { void ReadInput::item_exx() @@ -888,20 +889,34 @@ void ReadInput::item_dftu() } { Input_Item item("onsite_radius"); - item.annotation = "radius of the sphere for onsite projection (Bohr)"; + item.annotation = "cutoff radius of onsite projection orbitals (Bohr)"; item.category = "DFT+U correction"; item.type = "Real"; - item.description = R"(* The onsite_radius parameter facilitates modulation of the single-zeta portion of numerical atomic orbitals used for DFT+U projections. -* The modulation algorithm applies a smooth truncation to the orbital tail followed by normalization. A representative profile is $f(r)=\frac{1}{2}\left[1+\operatorname{erf}\!\left(\frac{r_c-r}{\sigma}\right)\right]$, where $r_c$ is the cutoff radius and $\sigma=\gamma r_c$ controls smoothness.)"; - item.default_value = "3.0"; + item.description = R"(Cutoff radius for numerical atomic orbitals used in onsite projections. +* Controls the localized projections for `dft_plus_u=1` and spin constraints (`sc_mag_switch=true`) in PW and LCAO calculations. +* Enables onsite population analysis in ordinary PW calculations when set to a positive value. +* Controls atomic magnetic moment projections for `nspin=4` PW RT-TDDFT with `td_out_mag=onsite`. + +When omitted, the value is `3.0` for `dft_plus_u=1`, `sc_mag_switch=true`, or `nspin=4` PW RT-TDDFT with `td_out_mag=onsite`; otherwise it is `0.0`. Explicit values must be positive. + +Provide numerical atomic orbital files through `NUMERICAL_ORBITAL` in `STRU`, with their directory specified by `orbital_dir`. Projected magnetic moments depend on the chosen orbitals and radius.)"; + item.default_value = "`3.0` for `dft_plus_u=1`, `sc_mag_switch=true`, or `nspin=4` PW RT-TDDFT with `td_out_mag=onsite`; `0.0` otherwise"; item.unit = "Bohr"; - item.set_availability("dft_plus_u==1"); + item.set_availability("basis_type in [pw, lcao]"); read_sync_double(input.onsite_radius); item.reset_value = [](const Input_Item& item, Parameter& para) { - if ((para.input.dft_plus_u == 1 || para.input.sc_mag_switch) && para.input.onsite_radius == 0.0) + if (!item.is_read()) + { + const bool rt_onsite = para.inp.basis_type == "pw" && para.inp.esolver_type == "tddft" + && para.inp.nspin == 4 && para.inp.td_out_mag == "onsite"; + const bool use_onsite = para.inp.dft_plus_u == 1 || para.inp.sc_mag_switch || rt_onsite; + para.input.onsite_radius = use_onsite ? 3.0 : 0.0; + } + }; + item.check_value = [](const Input_Item& item, const Parameter& para) { + if (item.is_read() && (!(para.inp.onsite_radius > 0.0) || !std::isfinite(para.inp.onsite_radius))) { - // autoset onsite_radius to 3.0 as default, this default value comes from the systematic atomic magnetism test - para.input.onsite_radius = 3.0; + ModuleBase::WARNING_QUIT("ReadInput", "Explicit onsite_radius must be finite and positive."); } }; this->add_item(item); diff --git a/source/source_io/module_parameter/read_inp_out.cpp b/source/source_io/module_parameter/read_inp_out.cpp index 880234ed6da..1755de440c0 100644 --- a/source/source_io/module_parameter/read_inp_out.cpp +++ b/source/source_io/module_parameter/read_inp_out.cpp @@ -1654,9 +1654,16 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output dipole or not"; item.category = "Real-Time TDDFT (Common)"; item.type = "Boolean"; - item.description = R"(Controls electric-dipole output. In RT-TDDFT, each enabled spin channel is written to `OUT.{suffix}/dipole_s[spin].txt` using a one-based spin number. Every row contains the one-based electronic-step index followed by the Cartesian electronic-dipole components $P_x$, $P_y$, and $P_z$ in atomic units. The running log additionally reports the electronic, ionic, and total dipoles and the norm of the total dipole. -* `True`: Output the electric dipole information. -* `False`: Do not output the electric dipole information.)"; + item.description = R"(Output the electronic dipole moment to `OUT.{suffix}/dipole_s[spin].txt`. In Hartree atomic units, + +$$\boldsymbol{P}(t)=-\int_{\Omega}\boldsymbol{r}\,\rho(\boldsymbol{r},t)\,\mathrm{d}^3r,$$ + +where the integral is over the simulation cell and $\rho$ is the electron number density. For `nspin=2`, the two spin channels are written separately; for `nspin=1` or `4`, a single file contains the dipole of the total electron density. + +Each row contains the step index followed by $P_x$, $P_y$, and $P_z$. The running log additionally reports the ionic and combined dipoles. + +* `True`: Enable dipole output. +* `False`: Disable dipole output.)"; item.default_value = "False"; item.unit = ""; read_sync_bool(input.out_dipole); @@ -1667,10 +1674,17 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output current or not"; item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; - item.description = R"(Controls the current-density output method for RT-TDDFT. Each row contains the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units; the initial ground state is step 1 and subsequent steps increase by one. -* `0`: Do not output current. -* `1`: Available for PW and LCAO. PW evaluates the occupied-state expectation values of the velocity operator, using plane-wave momentum and nonlocal-projector derivatives; in the velocity gauge these are evaluated at the vector-potential-shifted momentum. In the length gauge, a kinetic-energy-density functional also contributes its velocity term. LCAO explicitly constructs the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center and spherical-grid integrals: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (`td_stype=1`); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms, such as EXX, are not included explicitly. The total current is written to `OUT.{suffix}/current_tot.txt`. -* `2`: Available only for LCAO. Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis, $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. The total current is written to `OUT.{suffix}/current_tot_comm.txt`.)"; + item.description = R"(Output the RT-TDDFT charge current density. In Hartree atomic units, + +$$\boldsymbol{J}(t)=-\frac{1}{\Omega}\sum_{n\boldsymbol{k}}f_{n\boldsymbol{k}}\operatorname{Re}\Braket{\psi_{n\boldsymbol{k}}(t)|\hat{\boldsymbol{v}}(t)|\psi_{n\boldsymbol{k}}(t)},$$ + +where $\Omega$ is the cell volume, $f_{n\boldsymbol{k}}$ includes occupations and k-point weights, and $\hat{\boldsymbol{v}}$ is the velocity operator. + +* `0`: Disable current output. +* `1`: Available for PW and LCAO. Evaluate the velocity from momentum and nonlocal pseudopotential contributions, including coupling to the vector potential in the velocity gauge. Other nonlocal terms, such as exact exchange, are not included. Write the total current to `OUT.{suffix}/current_tot.txt`. +* `2`: Available only for LCAO. Construct the velocity matrix from the full Hamiltonian in the nonorthogonal atomic-orbital basis: $\widetilde{v}_{\alpha}=\partial_{k_{\alpha}}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{k_{\alpha}}S$, where $H$, $S$, and $\mathcal{R}_{\alpha}$ are the Hamiltonian, overlap, and position matrices in k space. This automatically includes nonlocal contributions present in the Hamiltonian, including exact exchange. Write the total current to `OUT.{suffix}/current_tot_comm.txt`. + +Each row contains the step index followed by $J_x$, $J_y$, and $J_z$.)"; item.default_value = "0"; item.unit = ""; item.set_availability("basis_type in [pw, lcao] and esolver_type==tddft"); @@ -1683,7 +1697,7 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.category = "Real-Time TDDFT (Common)"; item.type = "Boolean"; item.description = R"(Controls whether RT-TDDFT current density is also resolved by spin and k-point. PW supports `out_current=1`; LCAO supports `out_current=1` or `2`. The total-current file is always written when current output is enabled. -* `True`: In addition to the total, `out_current=1` writes `OUT.{suffix}/current_s[spin]k[kpoint].txt`; `out_current=2` writes `OUT.{suffix}/current_s[spin]k[kpoint]_comm.txt`. Both use one-based spin and k-point numbers, with k-points numbered independently within each spin channel. Rows follow the step numbering, atomic units, precision, and append convention described in `out_current`. These weighted contributions sum to the total current. +* `True`: In addition to the total, `out_current=1` writes `OUT.{suffix}/current_s[spin]k[kpoint].txt`; `out_current=2` writes `OUT.{suffix}/current_s[spin]k[kpoint]_comm.txt`. These weighted contributions sum to the total current. * `False`: Output only `current_tot.txt` for `out_current=1` or `current_tot_comm.txt` for `out_current=2`.)"; item.default_value = "False"; item.unit = ""; @@ -1696,7 +1710,7 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output electric fields"; item.category = "Real-Time TDDFT (Common)"; item.type = "Boolean"; - item.description = R"(Controls time-dependent electric-field output in `OUT.{suffix}/`. `efield_total.txt` contains time in fs followed by the total-field components $E_x$, $E_y$, and $E_z$ in V/Angstrom. Each configured analytic field is additionally written to `efield_[index].txt`, containing time in fs and the field value in V/Angstrom. The one-based index follows the occurrence order in `td_ttype` and `td_vext_dire`; fields assigned to the same direction remain in separate files. Output is written on active electronic steps. Fresh calculations overwrite the output; runs with `md_restart=True` append to it. + item.description = R"(Controls time-dependent electric-field output in `OUT.{suffix}/`. `efield_total.txt` contains time in fs followed by the total-field components $E_x$, $E_y$, and $E_z$ in V/Angstrom. Each configured analytic field is additionally written to `efield_[index].txt`, containing time in fs and the field value in V/Angstrom. The index follows the occurrence order in `td_ttype` and `td_vext_dire`; fields assigned to the same direction remain in separate files. Output is written on active electronic steps. Fresh calculations overwrite the output; runs with `md_restart=True` append to it. * `True`: Output electric-field values. * `False`: Do not output electric-field values.)"; item.default_value = "False"; @@ -1710,7 +1724,7 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output endpoint vector potentials"; item.category = "Real-Time TDDFT (Common)"; item.type = "Boolean"; - item.description = R"(Controls vector-potential output to `OUT.{suffix}/vector_pot.txt`. Each row contains the one-based electronic-step index followed by $A_x$, $A_y$, and $A_z$ in Hartree atomic units. The file can be used as input for `init_vecpot` with the same `td_dt`. Fresh calculations overwrite the output; runs with `md_restart=True` append to it. + item.description = R"(Controls vector-potential output to `OUT.{suffix}/vector_pot.txt`. Each row contains the step index followed by $A_x$, $A_y$, and $A_z$ in Hartree atomic units. The file can be used as input for `init_vecpot` with the same `td_dt`. Fresh calculations overwrite the output; runs with `md_restart=True` append to it. * `True`: Output the vector potential. * `False`: Do not output the vector potential.)"; item.default_value = "False"; diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index 484aedf8331..843624a46d2 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -234,6 +234,68 @@ void ReadInput::item_rt_tddft() read_sync_bool(input.td_out_stat); this->add_item(item); } + { + Input_Item item("td_out_mag"); + item.annotation = "RT-TDDFT spinor magnetic moment output"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "String"; + item.description = R"(Write total and atomic magnetic moments to `mag_tot.txt` and `mag_atom.txt` at every step. +* `voronoi`: Integrate magnetization over periodic nearest-atom regions, including vacuum. +* `onsite`: Project onto the smoothed single-zeta numerical atomic orbitals. `onsite_radius` controls their cutoff. +* `none`: Disable these two files. + +Total moments always come from the magnetization density. Atomic onsite moments need not sum to the total. Moments follow the ABACUS Pauli convention in Bohr magnetons.)"; + item.default_value = "voronoi"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and nspin==4"); + read_sync_string(input.td_out_mag); + item.check_value = [](const Input_Item&, const Parameter& para) { + const std::string& method = para.inp.td_out_mag; + if (method != "voronoi" && method != "onsite" && method != "none") + { + ModuleBase::WARNING_QUIT("ReadInput", "td_out_mag must be voronoi, onsite or none."); + } + }; + this->add_item(item); + } + { + Input_Item item("td_out_torque"); + item.annotation = "output PW spin angular momentum torques"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Boolean"; + item.description = R"(Write total exchange-correlation and SOC torques to `torque_tot.txt` and the atomic SOC contributions to `torque_atom.txt` at every step. In Hartree atomic units, + +$$\boldsymbol{\tau}_{\mathrm{xc}}=\mathrm{i}\sum_{n\boldsymbol{k}}f_{n\boldsymbol{k}}\Braket{\psi_{n\boldsymbol{k}}|\left[\hat{V}_{\mathrm{xc}},\hat{\boldsymbol{S}}\right]|\psi_{n\boldsymbol{k}}},$$ + +$$\boldsymbol{\tau}_{\mathrm{SOC}}=\mathrm{i}\sum_{n\boldsymbol{k}}f_{n\boldsymbol{k}}\Braket{\psi_{n\boldsymbol{k}}|\left[\hat{V}_{\mathrm{SOC}},\hat{\boldsymbol{S}}\right]|\psi_{n\boldsymbol{k}}}.$$ + +Here $\hat{\boldsymbol{S}}=\boldsymbol{\sigma}/2$, with $\boldsymbol{\sigma}$ the Pauli matrices, and $f_{n\boldsymbol{k}}$ includes occupations and k-point weights. $\hat{V}_{\mathrm{xc}}$ is the spin-dependent exchange-correlation potential, and $\hat{V}_{\mathrm{SOC}}$ is the SOC part of the nonlocal pseudopotential. + +The torques describe changes in spin angular momentum $\boldsymbol{S}=\boldsymbol{M}/2$, where $\boldsymbol{M}$ follows the ABACUS Pauli convention. Atomic SOC contributions use the corresponding atomic pseudopotential terms.)"; + item.default_value = "false"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and nspin==4"); + read_sync_bool(input.td_out_torque); + this->add_item(item); + } + { + Input_Item item("td_out_spincur"); + item.annotation = "output PW spin angular momentum current"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Boolean"; + item.description = R"(Write the nine macroscopic spin angular momentum current components to `spincur_tot.txt` at every step. In Hartree atomic units, + +$$J_i^a=\frac{1}{2\Omega}\sum_{n\boldsymbol{k}}f_{n\boldsymbol{k}}\Braket{\psi_{n\boldsymbol{k}}|\left\{\hat{v}_i,\hat{S}_a\right\}|\psi_{n\boldsymbol{k}}},\qquad \hat{S}_a=\frac{\sigma_a}{2}.$$ + +Here $\Omega$ is the cell volume, $f_{n\boldsymbol{k}}$ includes occupations and k-point weights, $\sigma_a$ is a Pauli matrix, and $\hat{v}_i$ is the velocity operator including the nonlocal pseudopotential contribution. + +Components are ordered $xx,xy,xz,yx,yy,yz,zx,zy,zz$, with flow direction first and spin polarization second.)"; + item.default_value = "false"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and nspin==4"); + read_sync_bool(input.td_out_spincur); + this->add_item(item); + } { Input_Item item("lin_solver"); item.annotation = "linear solver for real-time propagation"; @@ -450,7 +512,7 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; item.annotation = "print eij or not"; item.category = "Real-Time TDDFT (Common)"; item.type = "Real"; - item.description = R"(Controls output of the propagated-state Hamiltonian matrix elements $E_{ij}=\Braket{\psi_i | \hat{H} | \psi_j}$ to the running log. The printed band indices $i$ and $j$ are one-based global indices. Both the threshold and the printed matrix elements are in Ry. + item.description = R"(Controls output of the propagated-state Hamiltonian matrix elements $E_{ij}=\Braket{\psi_i | \hat{H} | \psi_j}$ to the running log. The printed band indices $i$ and $j$ are global indices. Both the threshold and the printed matrix elements are in Ry. * $\lt 0$: Disable the output. * $\geqslant 0$: Print an element when either $\left|\operatorname{Re}E_{ij}\right|$ or $\left|\operatorname{Im}E_{ij}\right|$ is greater than or equal to `td_print_eij`.)"; item.default_value = "-1"; @@ -699,7 +761,7 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually item.annotation = "t1 of Trapezoid type electric field"; item.category = "Real-Time TDDFT (Common)"; item.type = "Vector of Real"; - item.description = R"(Electronic step defining the end of the linear rise, $t_1=\mathtt{td\_trape\_t1}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy `td_trape_t1 <= td_trape_t2 <= td_trape_t3`. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; + item.description = R"(Electronic step defining the end of the linear rise, $t_1=\mathtt{td\_trape\_t1}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy $\mathtt{td\_trape\_t1}\leqslant\mathtt{td\_trape\_t2}\leqslant\mathtt{td\_trape\_t3}$. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; item.default_value = "1875"; item.unit = ""; item.set_availability("td_ttype contains 1"); @@ -714,7 +776,7 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually item.annotation = "t2 of Trapezoid type electric field"; item.category = "Real-Time TDDFT (Common)"; item.type = "Vector of Real"; - item.description = R"(Electronic step defining the end of the plateau, $t_2=\mathtt{td\_trape\_t2}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy `td_trape_t1 <= td_trape_t2 <= td_trape_t3`. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; + item.description = R"(Electronic step defining the end of the plateau, $t_2=\mathtt{td\_trape\_t2}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy $\mathtt{td\_trape\_t1}\leqslant\mathtt{td\_trape\_t2}\leqslant\mathtt{td\_trape\_t3}$. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; item.default_value = "5625"; item.unit = ""; item.set_availability("td_ttype contains 1"); @@ -729,7 +791,7 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually item.annotation = "t3 of Trapezoid type electric field"; item.category = "Real-Time TDDFT (Common)"; item.type = "Vector of Real"; - item.description = R"(Electronic step defining the end of the linear fall, $t_3=\mathtt{td\_trape\_t3}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy `td_trape_t1 <= td_trape_t2 <= td_trape_t3`. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; + item.description = R"(Electronic step defining the end of the linear fall, $t_3=\mathtt{td\_trape\_t3}\Delta t$. The value must be a nonnegative integer; integer-valued real inputs such as `2.0` are accepted. Each field must satisfy $\mathtt{td\_trape\_t1}\leqslant\mathtt{td\_trape\_t2}\leqslant\mathtt{td\_trape\_t3}$. Supply exactly one value for each `td_ttype 1` occurrence, in occurrence order.)"; item.default_value = "7500"; item.unit = ""; item.set_availability("td_ttype contains 1"); diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/test/CMakeLists.txt index ef8609a0afb..945e8e92cf9 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/test/CMakeLists.txt @@ -89,9 +89,9 @@ AddTest( ) AddTest( - TARGET MODULE_IO_td_vector_pot_io_test + TARGET MODULE_IO_td_vecpot_io_test LIBS parameter base device - SOURCES td_vector_pot_io_test.cpp ../module_efield/td_vector_pot_io.cpp + SOURCES test_vecpot_io.cpp ../module_efield/td_vecpot_io.cpp ) AddTest( diff --git a/source/source_io/test/td_efield_io_test.cpp b/source/source_io/test/td_efield_io_test.cpp index 25c3afc1856..6c91a43393f 100644 --- a/source/source_io/test/td_efield_io_test.cpp +++ b/source/source_io/test/td_efield_io_test.cpp @@ -92,14 +92,14 @@ TEST_F(TDEFieldIOTest, FreshCalculationTruncatesAndUsesOneBasedFiles) } std::shared_ptr manager = create_manager({2.0, 3.0}); - ModuleIO::prepare_td_field_output(output_prefix_, manager->fields().size(), false); + ModuleIO::prep_td_field_out(output_prefix_, manager->fields().size(), false); EXPECT_TRUE(read_samples(1).empty()); EXPECT_TRUE(read_samples(2).empty()); manager->prepare_sample(0); - ModuleIO::write_td_field_values(*manager, output_prefix_); + ModuleIO::write_td_field(*manager, output_prefix_); manager->prepare_sample(1); - ModuleIO::write_td_field_values(*manager, output_prefix_); + ModuleIO::write_td_field(*manager, output_prefix_); const std::vector> samples_1 = read_samples(1); const std::vector> samples_2 = read_samples(2); @@ -109,8 +109,9 @@ TEST_F(TDEFieldIOTest, FreshCalculationTruncatesAndUsesOneBasedFiles) EXPECT_DOUBLE_EQ(samples_2[0].first, 0.0); EXPECT_NEAR(samples_1[0].second, 2.0, 1.0e-12); EXPECT_NEAR(samples_2[0].second, 3.0, 1.0e-12); - EXPECT_NEAR(samples_1[1].first, ModuleBase::AU_to_FS, 1.0e-7); - EXPECT_NEAR(samples_2[1].first, ModuleBase::AU_to_FS, 1.0e-7); + // One atomic time unit is rounded to six decimal places in fs. + EXPECT_DOUBLE_EQ(samples_1[1].first, 0.024189); + EXPECT_DOUBLE_EQ(samples_2[1].first, 0.024189); std::ifstream zero_based_file(output_path(0).c_str()); EXPECT_FALSE(zero_based_file.good()); @@ -124,10 +125,10 @@ TEST_F(TDEFieldIOTest, RestartPreservesExistingSamples) } std::shared_ptr manager = create_manager({4.0}); - ModuleIO::prepare_td_field_output(output_prefix_, manager->fields().size(), true); + ModuleIO::prep_td_field_out(output_prefix_, manager->fields().size(), true); const int next_step = manager->current_step() + 1; manager->prepare_sample(next_step); - ModuleIO::write_td_field_values(*manager, output_prefix_); + ModuleIO::write_td_field(*manager, output_prefix_); const std::vector> samples = read_samples(1); ASSERT_EQ(samples.size(), 2U); @@ -146,11 +147,11 @@ TEST_F(TDEFieldIOTest, ReportsOutputOpenFailures) const std::string invalid_directory = blocking_path_ + "/"; std::shared_ptr manager = create_manager({4.0}); - EXPECT_EXIT(ModuleIO::prepare_td_field_output(invalid_directory, manager->fields().size(), false), testing::ExitedWithCode(1), ""); + EXPECT_EXIT(ModuleIO::prep_td_field_out(invalid_directory, manager->fields().size(), false), testing::ExitedWithCode(1), ""); const int next_step = manager->current_step() + 1; manager->prepare_sample(next_step); - EXPECT_EXIT(ModuleIO::write_td_field_values(*manager, invalid_directory), testing::ExitedWithCode(1), ""); + EXPECT_EXIT(ModuleIO::write_td_field(*manager, invalid_directory), testing::ExitedWithCode(1), ""); } } // namespace diff --git a/source/source_io/test/td_vector_pot_io_test.cpp b/source/source_io/test/test_vecpot_io.cpp similarity index 66% rename from source/source_io/test/td_vector_pot_io_test.cpp rename to source/source_io/test/test_vecpot_io.cpp index 1deb83ca133..7de31704065 100644 --- a/source/source_io/test/td_vector_pot_io_test.cpp +++ b/source/source_io/test/test_vecpot_io.cpp @@ -1,4 +1,4 @@ -#include "source_io/module_efield/td_vector_pot_io.h" +#include "source_io/module_efield/td_vecpot_io.h" #include "gtest/gtest.h" #include @@ -9,7 +9,7 @@ namespace { -class TDVectorPotIOTest : public testing::Test +class TDVecpotIOTest : public testing::Test { protected: void SetUp() override @@ -39,28 +39,28 @@ class TDVectorPotIOTest : public testing::Test return lines; } - const std::string output_prefix_ = "td_vector_pot_io_test_"; + const std::string output_prefix_ = "td_vecpot_io_test_"; const std::string output_path_ = output_prefix_ + "vector_pot.txt"; }; -TEST_F(TDVectorPotIOTest, FreshCalculationTruncatesAndWritesHeader) +TEST_F(TDVecpotIOTest, FreshCalculationTruncatesAndWritesHeader) { { std::ofstream output(output_path_.c_str()); output << "old data\n"; } - ModuleIO::prepare_td_vector_pot_output(output_prefix_, false); - ModuleIO::write_td_vector_pot(output_prefix_, 0, ModuleBase::Vector3(1.0, 2.0, 3.0)); - ModuleIO::write_td_vector_pot(output_prefix_, 1, ModuleBase::Vector3(4.0, 5.0, 6.0)); + ModuleIO::prep_td_vecpot_out(output_prefix_, false); + ModuleIO::write_td_vecpot(output_prefix_, 0, ModuleBase::Vector3(1.0, 2.0, 3.0)); + ModuleIO::write_td_vecpot(output_prefix_, 1, ModuleBase::Vector3(4.0, 5.0, 6.0)); const std::vector lines = read_lines(); ASSERT_EQ(lines.size(), 3U); EXPECT_EQ(lines[0].find("#istep"), 0U); - EXPECT_EQ(lines[1].find("1"), 0U); - EXPECT_EQ(lines[2].find("2"), 0U); + EXPECT_EQ(lines[1].substr(0, 6), " 1"); + EXPECT_EQ(lines[2].substr(0, 6), " 2"); - const std::vector> vector_potentials = ModuleIO::read_td_vector_pot(output_prefix_); + const std::vector> vector_potentials = ModuleIO::read_td_vecpot(output_prefix_); ASSERT_EQ(vector_potentials.size(), 2U); EXPECT_DOUBLE_EQ(vector_potentials[0][0], 1.0); EXPECT_DOUBLE_EQ(vector_potentials[0][1], 2.0); @@ -70,7 +70,7 @@ TEST_F(TDVectorPotIOTest, FreshCalculationTruncatesAndWritesHeader) EXPECT_DOUBLE_EQ(vector_potentials[1][2], 6.0); } -TEST_F(TDVectorPotIOTest, RestartPreservesExistingSamples) +TEST_F(TDVecpotIOTest, RestartPreservesExistingSamples) { { std::ofstream output(output_path_.c_str()); @@ -78,19 +78,19 @@ TEST_F(TDVectorPotIOTest, RestartPreservesExistingSamples) output << "7 1.0 2.0 3.0\n"; } - ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); - ModuleIO::write_td_vector_pot(output_prefix_, 7, ModuleBase::Vector3(4.0, 5.0, 6.0)); + ModuleIO::prep_td_vecpot_out(output_prefix_, true); + ModuleIO::write_td_vecpot(output_prefix_, 7, ModuleBase::Vector3(4.0, 5.0, 6.0)); const std::vector lines = read_lines(); ASSERT_EQ(lines.size(), 3U); EXPECT_EQ(lines[0], "#istep A_x A_y A_z"); EXPECT_EQ(lines[1], "7 1.0 2.0 3.0"); - EXPECT_EQ(lines[2].find("8"), 0U); + EXPECT_EQ(lines[2].substr(0, 6), " 8"); } -TEST_F(TDVectorPotIOTest, RestartCreatesHeaderForMissingOrEmptyFile) +TEST_F(TDVecpotIOTest, RestartCreatesHeaderForMissingOrEmptyFile) { - ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); + ModuleIO::prep_td_vecpot_out(output_prefix_, true); std::vector lines = read_lines(); ASSERT_EQ(lines.size(), 1U); EXPECT_EQ(lines[0].find("#istep"), 0U); @@ -98,13 +98,13 @@ TEST_F(TDVectorPotIOTest, RestartCreatesHeaderForMissingOrEmptyFile) { std::ofstream output(output_path_.c_str(), std::ofstream::out); } - ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); + ModuleIO::prep_td_vecpot_out(output_prefix_, true); lines = read_lines(); ASSERT_EQ(lines.size(), 1U); EXPECT_EQ(lines[0].find("#istep"), 0U); } -TEST_F(TDVectorPotIOTest, ReaderSkipsBlankAndCommentLinesAndIgnoresLabels) +TEST_F(TDVecpotIOTest, ReaderSkipsBlankAndCommentLinesAndIgnoresLabels) { { std::ofstream output(output_path_.c_str()); @@ -114,7 +114,7 @@ TEST_F(TDVectorPotIOTest, ReaderSkipsBlankAndCommentLinesAndIgnoresLabels) output << "42 4.5 5.5 -6.5\n"; } - const std::vector> vector_potentials = ModuleIO::read_td_vector_pot(output_prefix_); + const std::vector> vector_potentials = ModuleIO::read_td_vecpot(output_prefix_); ASSERT_EQ(vector_potentials.size(), 2U); EXPECT_DOUBLE_EQ(vector_potentials[0][0], 1.5); EXPECT_DOUBLE_EQ(vector_potentials[0][1], -2.5); @@ -124,25 +124,25 @@ TEST_F(TDVectorPotIOTest, ReaderSkipsBlankAndCommentLinesAndIgnoresLabels) EXPECT_DOUBLE_EQ(vector_potentials[1][2], -6.5); } -TEST_F(TDVectorPotIOTest, ReaderRejectsEmptyAndMalformedFiles) +TEST_F(TDVecpotIOTest, ReaderRejectsEmptyAndMalformedFiles) { { std::ofstream output(output_path_.c_str()); output << "# no samples\n"; } - EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); + EXPECT_EXIT(ModuleIO::read_td_vecpot(output_prefix_), testing::ExitedWithCode(1), ""); { std::ofstream output(output_path_.c_str(), std::ofstream::out); output << "1 2.0 invalid 4.0\n"; } - EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); + EXPECT_EXIT(ModuleIO::read_td_vecpot(output_prefix_), testing::ExitedWithCode(1), ""); { std::ofstream output(output_path_.c_str(), std::ofstream::out); output << "1 2.0 3.0 4.0 extra\n"; } - EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); + EXPECT_EXIT(ModuleIO::read_td_vecpot(output_prefix_), testing::ExitedWithCode(1), ""); } } // namespace diff --git a/source/source_psi/setup_psi_pw.cpp b/source/source_psi/setup_psi_pw.cpp index cb56267631a..1f4bfd4239d 100644 --- a/source/source_psi/setup_psi_pw.cpp +++ b/source/source_psi/setup_psi_pw.cpp @@ -116,14 +116,27 @@ void Setup_Psi_pw::before_runner(const UnitCell& ucell, template void Setup_Psi_pw::update_psi_d_impl() { - if (this->psi_d != nullptr && this->precision_type_ == PrecisionType::ComplexFloat) - { - delete this->get_psi_d(); - } - if (this->precision_type_ == PrecisionType::ComplexFloat) { - this->psi_d = static_cast(new psi::Psi, Device>(*this->get_psi_t())); + const auto* native = this->get_psi_t(); + auto* mirror = this->get_psi_d(); + if (mirror == nullptr) + { + this->psi_d = new psi::Psi, Device>(*native); + } + else if (mirror->get_nk() != native->get_nk() || mirror->get_nbands() != native->get_nbands() + || mirror->get_nbasis() != native->get_nbasis() || mirror->get_ngk_pointer() != native->get_ngk_pointer()) + { + // Preserve the object address borrowed by onsite projectors even when storage changes. + *mirror = psi::Psi, Device>(*native); + } + else + { + const T* source = native->get_pointer() - native->get_psi_bias(); + std::complex* destination = mirror->get_pointer() - mirror->get_psi_bias(); + base_device::memory::cast_memory_op, T, Device, Device>()(destination, source, native->size()); + mirror->fix_k(native->get_current_k()); + } } else { diff --git a/source/source_pw/module_proj/onsite_proj.h b/source/source_pw/module_proj/onsite_proj.h index fcfe8553b14..f9618c427bc 100644 --- a/source/source_pw/module_proj/onsite_proj.h +++ b/source/source_pw/module_proj/onsite_proj.h @@ -18,6 +18,14 @@ namespace projectors { public: + /** @brief Own an independent projector and its workspaces. */ + OnsiteProjector() = default; + ~OnsiteProjector(); + OnsiteProjector(const OnsiteProjector&) = delete; + OnsiteProjector& operator=(const OnsiteProjector&) = delete; + OnsiteProjector(OnsiteProjector&&) = delete; + OnsiteProjector& operator=(OnsiteProjector&&) = delete; + /** * @brief initialize the radial projector for real-space projection involving operators * @@ -71,6 +79,13 @@ namespace projectors const int nspin_in); int get_size_becp() const { return size_becp; } + /** @brief Extend this projector's radial table to cover shifted momenta. */ + void ensure_range(double momentum, double dq); + /** @brief Invalidate position-dependent overlap projectors without changing radial channels. */ + void invalidate_proj(); + /** @brief Project endpoint spin-component columns without printing static occupations. */ + void overlap_td(int ik, int columns, const std::complex* psi, int stride, + const ModuleBase::Vector3& vector_potential); std::complex* get_becp() const { return becp; } std::complex* get_h_becp() const { return h_becp; } std::complex* get_tab_atomic() const { return tab_atomic_; } @@ -102,20 +117,19 @@ namespace projectors const double* wg_ik) const; private: - OnsiteProjector(){}; - ~OnsiteProjector(); + void overlap_impl(int columns, const std::complex* psi, int stride, + const ModuleBase::Vector3& vector_potential); Device* ctx = {}; base_device::DEVICE_CPU* cpu_ctx = {}; base_device::AbacusDevice_t device = {}; - static OnsiteProjector *instance; hamilt::Onsite_Proj_tools* fs_tools = nullptr; std::complex* tab_atomic_ = nullptr; std::complex* becp = nullptr; // nbands * nkb // save becp in CPU memory, only used when Device is GPU - std::complex* h_becp; + std::complex* h_becp = nullptr; int size_becp = 0; int size_vproj = 0; @@ -175,4 +189,4 @@ namespace projectors }; }// namespace projectors -#endif // MODULEHAMILTPW_ONSITEPROJECTOR_H \ No newline at end of file +#endif // MODULEHAMILTPW_ONSITEPROJECTOR_H diff --git a/source/source_pw/module_proj/onsite_proj_init.cpp b/source/source_pw/module_proj/onsite_proj_init.cpp index e2c0a4fe411..5b84d7f4a87 100644 --- a/source/source_pw/module_proj/onsite_proj_init.cpp +++ b/source/source_pw/module_proj/onsite_proj_init.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "source_base/module_out/orb_io.h" #include "source_pw/module_proj/onsite_proj.h" @@ -141,7 +142,7 @@ void projectors::OnsiteProjector::init(const std::string& orbital_dir } } - this->init_proj(PARAM.inp.orbital_dir, + this->init_proj(orbital_dir, orb_files, nproj, lproj, @@ -209,6 +210,10 @@ void projectors::OnsiteProjector::init_proj(const std::string& orbita continue; } std::ifstream ifs(orbital_dir + orb_files[it]); + if (!ifs) + { + ModuleBase::WARNING_QUIT("OnsiteProjector", "Cannot open orbital file: " + orbital_dir + orb_files[it]); + } std::string elem = ""; double ecut = -1.0; int nr_ = -1; @@ -216,6 +221,10 @@ void projectors::OnsiteProjector::init_proj(const std::string& orbita std::vector nzeta; // number of radials for each l std::vector> radials; // radials arranged in serial ModuleIO::read_abacus_orb(ifs, elem, ecut, nr_, dr_, nzeta, radials); + if (nr_ < 2 || !(dr_ > 0.0) || radials.empty()) + { + ModuleBase::WARNING_QUIT("OnsiteProjector", "Invalid orbital radial grid."); + } #ifdef __DEBUG assert(elem != ""); assert(ecut != -1.0); @@ -227,16 +236,20 @@ void projectors::OnsiteProjector::init_proj(const std::string& orbita dr = (dr == -1.0) ? dr_ : dr; for(int ip = 0; ip < nproj_it; ++ip) { + if (!(onsite_r[idx] > 0.0) || onsite_r[idx] >= (nr_ - 1) * dr_) + { + ModuleBase::WARNING_QUIT("OnsiteProjector", "onsite_radius must lie inside the orbital radial grid."); + } int l = lproj[idx]; int izeta = iproj[idx]; int irad = 0; irad = std::accumulate(nzeta.begin(), nzeta.begin() + l, irad); irad += izeta; std::vector temp = radials[irad]; - rgrid.resize(nr); + rgrid.resize(nr_); std::iota(rgrid.begin(), rgrid.end(), 0); std::for_each(rgrid.begin(), rgrid.end(), [dr](double& r_i) { r_i *= dr; }); - smoothgen(nr, rgrid.data(), temp.data(), onsite_r[idx], projs[idx]); + smoothgen(nr_, rgrid.data(), temp.data(), onsite_r[idx], projs[idx]); it2iproj[it][ip] = idx; ++idx; } @@ -253,6 +266,27 @@ void projectors::OnsiteProjector::init_proj(const std::string& orbita } // explicit method instantiation +template +void projectors::OnsiteProjector::ensure_range(double momentum, double dq) +{ + // Four extra samples cover the radial interpolation stencil at the largest shifted momentum. + const int required = static_cast(std::ceil(momentum / dq)) + 4; + if (required <= tab.getBound3()) + { + return; + } + std::vector counts(ntype); + for (int it = 0; it < ntype; ++it) + { + counts[it] = it2iproj[it].size(); + } + const int capacity = std::max(required + 100, static_cast(std::ceil(1.25 * tab.getBound3()))); + const int npol = ucell->get_npol(); + RadialProjection::build_sbt_tab(counts, rgrid, projs, lproj, capacity, dq, ucell->omega, npol, tab, nhtol); + fs_tools->refresh_table(); +} + +template void projectors::OnsiteProjector::ensure_range(double, double); template void projectors::OnsiteProjector::init( const std::string&, @@ -277,6 +311,7 @@ void projectors::OnsiteProjector::init_proj( const std::vector&); #if ((defined __CUDA) || (defined __ROCM)) +template void projectors::OnsiteProjector::ensure_range(double, double); template void projectors::OnsiteProjector::init( const std::string&, diff --git a/source/source_pw/module_proj/onsite_proj_overlap.cpp b/source/source_pw/module_proj/onsite_proj_overlap.cpp index 79776c7496e..c94c6b1e56a 100644 --- a/source/source_pw/module_proj/onsite_proj_overlap.cpp +++ b/source/source_pw/module_proj/onsite_proj_overlap.cpp @@ -29,27 +29,9 @@ void projectors::OnsiteProjector::overlap_proj_psi( const int ld_psi) { ModuleBase::timer::start("OnsiteProj", "overlap"); - // notes on refactor for DCU calculation - // the npm here is nbands(occ) * npol, for calling cal_becp, the npol should be divided. - int npol = this->ucell->get_npol(); - if(this->becp == nullptr || this->size_becp < npm*this->tot_nproj) - { - this->size_becp = npm*this->tot_nproj; - resmem_complex_op()(this->becp, this->size_becp); - if(this->device == base_device::GpuDevice ) - { - resmem_complex_h_op()(this->h_becp, this->size_becp); - } - else - { - this->h_becp = this->becp; - } - } - this->fs_tools->cal_becp(ik_, npm/npol, this->becp, ppsi, ld_psi > 0 ? ld_psi : this->npwx_); // in cal_becp, npm should be the one not multiplied by npol - if(this->device == base_device::GpuDevice) - { - syncmem_complex_d2h_op()(h_becp, this->becp, this->size_becp); - } + const int stride = ld_psi > 0 ? ld_psi : npwx_; + const ModuleBase::Vector3 zero; + overlap_impl(npm, ppsi, stride, zero); ModuleBase::timer::end("OnsiteProj", "overlap"); } @@ -107,7 +89,50 @@ void projectors::OnsiteProjector::cal_occupations( ModuleBase::timer::end("OnsiteProj", "cal_occupation"); } -// explicit method instantiation +template +void projectors::OnsiteProjector::invalidate_proj() +{ + if (fs_tools != nullptr) + { + fs_tools->invalidate_proj(); + } +} + +template +void projectors::OnsiteProjector::overlap_td(int ik, int columns, + const std::complex* psi, int stride, + const ModuleBase::Vector3& vector_potential) +{ + tabulate_atomic(ik); + overlap_impl(columns, psi, stride, vector_potential); +} + +template +void projectors::OnsiteProjector::overlap_impl(int columns, + const std::complex* psi, int stride, + const ModuleBase::Vector3& vector_potential) +{ + const int count = columns * tot_nproj; + if (count > size_becp || becp == nullptr) + { + size_becp = count; + resmem_complex_op()(becp, std::max(1, count)); + if (device == base_device::GpuDevice) + { + resmem_complex_h_op()(h_becp, std::max(1, count)); + } + else + { + h_becp = becp; + } + } + const int bands = columns / ucell->get_npol(); + fs_tools->cal_becp(ik_, bands, becp, psi, stride, vector_potential, h_becp); +} + +template void projectors::OnsiteProjector::invalidate_proj(); +template void projectors::OnsiteProjector::overlap_td( + int, int, const std::complex*, int, const ModuleBase::Vector3&); template void projectors::OnsiteProjector::tabulate_atomic( const int, const char); @@ -123,6 +148,9 @@ void projectors::OnsiteProjector::cal_occupatio const int); #if ((defined __CUDA) || (defined __ROCM)) +template void projectors::OnsiteProjector::invalidate_proj(); +template void projectors::OnsiteProjector::overlap_td( + int, int, const std::complex*, int, const ModuleBase::Vector3&); template void projectors::OnsiteProjector::tabulate_atomic( const int, const char); diff --git a/source/source_pw/module_proj/onsite_proj_tools.h b/source/source_pw/module_proj/onsite_proj_tools.h index facbb9faf14..4e1ef4f47b3 100644 --- a/source/source_pw/module_proj/onsite_proj_tools.h +++ b/source/source_pw/module_proj/onsite_proj_tools.h @@ -63,6 +63,19 @@ class Onsite_Proj_tools * @brief calculate the becp = for all beta functions */ void cal_becp(int ik, int npm, std::complex* becp_in = nullptr, const std::complex* ppsi_in = nullptr, int npwx = 0); + /** @brief Gauge-covariant projection at the explicit endpoint vector potential (inverse Bohr). */ + void cal_becp(int ik, int npm, std::complex* becp_in, + const std::complex* ppsi_in, int npwx, + const ModuleBase::Vector3& vector_potential); + /** @brief Also retain the pool-reduced overlaps in the caller's host buffer. */ + void cal_becp(int ik, int npm, std::complex* becp_in, + const std::complex* ppsi_in, int npwx, + const ModuleBase::Vector3& vector_potential, + std::complex* host_becp); + /** @brief Invalidate projectors after resizing the borrowed radial interpolation table. */ + void refresh_table(); + /** @brief Rebuild overlap projectors on the next call after atomic coordinates change. */ + void invalidate_proj(); /** * @brief calculate the dbecp_{ij} = for all beta functions * stress_{ij} = -1/omega \sum_{n,k}f_{nk} \sum_I \sum_{lm,l'm'}D_{l,l'}^{I} becp * dbecp_{ij} also calculated @@ -147,6 +160,7 @@ class Onsite_Proj_tools int deeq_nc_dims[4] = {0, 0, 0, 0}; int current_ik = -1; + ModuleBase::Vector3 current_a_; int max_nh = 0; int max_npw = 0; @@ -213,6 +227,7 @@ class Onsite_Proj_tools /// becp and dbecp: std::complex* dbecp = nullptr; // nbands * nkb (for stress) or nbands * nkb * 3 (for force) std::complex* becp = nullptr; // nbands * nkb + std::vector> host_becp_; /// @brief rename the operators for CPU/GPU device using gemm_op = ModuleBase::gemm_op, Device>; diff --git a/source/source_pw/module_proj/onsite_proj_tools_becp.cpp b/source/source_pw/module_proj/onsite_proj_tools_becp.cpp index 170f1ecb931..1199ed4dff2 100644 --- a/source/source_pw/module_proj/onsite_proj_tools_becp.cpp +++ b/source/source_pw/module_proj/onsite_proj_tools_becp.cpp @@ -7,6 +7,7 @@ #include "source_base/tool_title.h" #include "source_io/module_parameter/parameter.h" #include "source_pw/module_pwdft/nonlocal_maths.h" +#include // cal_becp // starts from vkb (nkb, ng) table @@ -22,20 +23,64 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, std::complex* becp_in, const std::complex* ppsi_in, int npwx) +{ + const ModuleBase::Vector3 zero; + cal_becp(ik, npm, becp_in, ppsi_in, npwx, zero); +} + +template +void hamilt::Onsite_Proj_tools::refresh_table() +{ + // Resizing the radial table changes both its device storage and every projector interpolated from it. + invalidate_proj(); + if (this->device == base_device::GpuDevice) + { + resmem_var_op()(d_vq_tab, this->tabtpr->getSize()); + } +} + +template +void hamilt::Onsite_Proj_tools::invalidate_proj() +{ + // Coordinate updates change atomic phases even when the k point and A are unchanged. + current_ik = -1; +} + +template +void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, + std::complex* becp_in, + const std::complex* ppsi_in, int npwx, + const ModuleBase::Vector3& vector_potential) +{ + cal_becp(ik, npm, becp_in, ppsi_in, npwx, vector_potential, nullptr); +} + +template +void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, + std::complex* becp_in, + const std::complex* ppsi_in, int npwx, + const ModuleBase::Vector3& vector_potential, + std::complex* host_becp) { ModuleBase::TITLE("Onsite_Proj_tools", "cal_becp"); ModuleBase::timer::start("Onsite_Proj_tools", "cal_becp"); const int npol = this->ucell_->get_npol(); - const std::complex* ppsi = ppsi_in == nullptr ? &(this->psi_[0](ik, 0, 0)) : ppsi_in; const int npw = this->wfc_basis_->npwk[ik]; + const std::complex* ppsi = ppsi_in; + if (npw > 0 && ppsi == nullptr) + { + ppsi = &(this->psi_[0](ik, 0, 0)); + } if (becp_in == nullptr && this->becp == nullptr) { resmem_complex_op()(becp, this->nbands * npol * this->nkb); } std::complex* becp_tmp = becp_in == nullptr ? this->becp : becp_in; const int size_becp_act = npm * npol * this->nkb; - if (ik != this->current_ik) // different ik, need to recalculate vkb + // Coordinates and radial-table changes invalidate current_ik explicitly; k and A are checked on each call. + const bool changed_a = (vector_potential - current_a_).norm2() != 0.0; + if (npw > 0 && (ik != this->current_ik || changed_a)) { const int size_becp = this->nbands * npol * this->nkb; if (this->becp == nullptr) @@ -50,6 +95,20 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, // calculate G+K this->g_plus_k = maths.cal_gk(ik, this->wfc_basis_); + if (vector_potential.norm2() != 0.0) + { + const double tpiba = this->ucell_->tpiba; + for (int ig = 0; ig < npw; ++ig) + { + const ModuleBase::Vector3 q = this->wfc_basis_->getgpluskcar(ik, ig) + vector_potential / tpiba; + g_plus_k[3 * ig] = q.x; + g_plus_k[3 * ig + 1] = q.y; + g_plus_k[3 * ig + 2] = q.z; + const double norm = q.norm(); + g_plus_k[3 * npw + ig] = norm * tpiba; + g_plus_k[4 * npw + ig] = norm < 1e-8 ? 0.0 : tpiba / norm; + } + } FPTYPE *gk = g_plus_k.data(), *vq_tb = this->tabtpr->ptr; // vq_tb has dimension (ntype, nproj, GlobalV::NQX) @@ -151,6 +210,7 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, } } this->current_ik = ik; + this->current_a_ = vector_potential; } // DEBUG: ONCE YOU CHECK vkb VALUES, YOU UNCOMMENT THE FOLLOWING LINE // for (int i = 0; i < vkb_size; i++) @@ -168,6 +228,13 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, const char transa = 'C'; const char transb = 'N'; int npm_npol = npm * npol; + if (npw == 0) + { + // An empty local PW partition contributes zero but must still join the pool reduction below. + setmem_complex_op()(becp_tmp, 0, size_becp_act); + } + else + { gemm_op()(transa, transb, this->nkb, @@ -181,19 +248,32 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, &ModuleBase::ZERO, becp_tmp, this->nkb); + } if (this->device == base_device::GpuDevice) { - std::complex* h_becp = nullptr; - resmem_complex_h_op()(h_becp, size_becp_act); - syncmem_complex_d2h_op()(h_becp, becp_tmp, size_becp_act); - Parallel_Reduce::reduce_pool(h_becp, size_becp_act); - syncmem_complex_h2d_op()(becp_tmp, h_becp, size_becp_act); - delmem_complex_h_op()(h_becp); + if (host_becp == nullptr) + { + host_becp_.resize(size_becp_act); + host_becp = host_becp_.data(); + } + if (size_becp_act > 0) + { + syncmem_complex_d2h_op()(host_becp, becp_tmp, size_becp_act); + } + Parallel_Reduce::reduce_pool(host_becp, size_becp_act); + if (size_becp_act > 0) + { + syncmem_complex_h2d_op()(becp_tmp, host_becp, size_becp_act); + } } else { Parallel_Reduce::reduce_pool(becp_tmp, size_becp_act); + if (host_becp != nullptr && host_becp != becp_tmp && size_becp_act > 0) + { + std::copy(becp_tmp, becp_tmp + size_becp_act, host_becp); + } } // DEBUG: ONCE YOU CHECK becp VALUES, YOU UNCOMMENT THE FOLLOWING LINE // std::cout << "ik: " << ik << std::endl; @@ -205,11 +285,23 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, } // explicit method instantiation +template void hamilt::Onsite_Proj_tools::cal_becp( + int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&, std::complex*); +template void hamilt::Onsite_Proj_tools::refresh_table(); +template void hamilt::Onsite_Proj_tools::invalidate_proj(); +template void hamilt::Onsite_Proj_tools::cal_becp( + int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&); template void hamilt::Onsite_Proj_tools::cal_becp( int, int, std::complex*, const std::complex*, int); #if ((defined __CUDA) || (defined __ROCM)) +template void hamilt::Onsite_Proj_tools::cal_becp( + int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&, std::complex*); +template void hamilt::Onsite_Proj_tools::refresh_table(); +template void hamilt::Onsite_Proj_tools::invalidate_proj(); +template void hamilt::Onsite_Proj_tools::cal_becp( + int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&); template void hamilt::Onsite_Proj_tools::cal_becp( int, int, std::complex*, const std::complex*, int); diff --git a/source/source_pw/module_pwdft/CMakeLists.txt b/source/source_pw/module_pwdft/CMakeLists.txt index 2b0af1ec744..7686ef85a63 100644 --- a/source/source_pw/module_pwdft/CMakeLists.txt +++ b/source/source_pw/module_pwdft/CMakeLists.txt @@ -7,6 +7,8 @@ list( pw_hybrid_source.cpp td_history_pw.cpp td_pw.cpp + td_vel_pw.cpp + td_spin_pw.cpp op_pw_ekin.cpp op_pw_ekin_td.cpp op_pw_veff.cpp diff --git a/source/source_pw/module_pwdft/hamilt_pw.cpp b/source/source_pw/module_pwdft/hamilt_pw.cpp index d3d284643f5..071096637ac 100644 --- a/source/source_pw/module_pwdft/hamilt_pw.cpp +++ b/source/source_pw/module_pwdft/hamilt_pw.cpp @@ -214,7 +214,7 @@ HamiltPW::~HamiltPW() } template -void HamiltPW::bind_td_state(const Real* veff, const Real* vofk, const ModuleBase::Vector3& A_ha) +void HamiltPW::bind_td_state(const Real* veff, const ModuleBase::Vector3& A_ha) { Operator* op = this->ops; while (op != nullptr) @@ -223,10 +223,6 @@ void HamiltPW::bind_td_state(const Real* veff, const Real* vofk, cons { static_cast>*>(op)->set_veff(veff); } - else if (op->get_cal_type() == calculation_type::pw_meta) - { - static_cast>*>(op)->set_vk(vofk); - } else if (op->get_cal_type() == calculation_type::pw_ekinetic_td) { static_cast*>(op)->set_A_ha(A_ha); diff --git a/source/source_pw/module_pwdft/hamilt_pw.h b/source/source_pw/module_pwdft/hamilt_pw.h index 72158eadaf4..d7d93a9c8bd 100644 --- a/source/source_pw/module_pwdft/hamilt_pw.h +++ b/source/source_pw/module_pwdft/hamilt_pw.h @@ -43,7 +43,7 @@ class HamiltPW : public Hamilt /** @brief Bind local-potential buffers and Hartree vector potential for the same operation. * @note The caller must keep the buffers alive and restore endpoint state after propagation. */ - void bind_td_state(const Real* veff, const Real* vofk, const ModuleBase::Vector3& A_ha); + void bind_td_state(const Real* veff, const ModuleBase::Vector3& A_ha); void sPsi(const T* psi_in, // psi T* spsi, // spsi diff --git a/source/source_pw/module_pwdft/nonlocal_workspace.cpp b/source/source_pw/module_pwdft/nonlocal_workspace.cpp index 5530baef6a5..297f25e26ff 100644 --- a/source/source_pw/module_pwdft/nonlocal_workspace.cpp +++ b/source/source_pw/module_pwdft/nonlocal_workspace.cpp @@ -48,6 +48,7 @@ void NonlocalWorkspace::project(const T* vkb, const T* psi, const int const T zero(0, 0); if (npw == 0) { + // Empty PW partitions still supply zero coefficients to the collective projector reduction. const size_t count = static_cast(nkb) * bands; Zero()(becp_, 0, count); } @@ -115,6 +116,22 @@ void NonlocalWorkspace::apply(const UnitCell& cell, const pseudopot_c const size_t count = static_cast(nbasis) * bands / npol; Zero()(hpsi, 0, count); } + // Here bands counts spin-component columns, while nbasis includes all components of one orbital. + project_contract(cell, pp, basis, spin, bands, nbasis, npol, npw, vkb, psi); + if (pp.nkb > 0 && bands > 0 && npw > 0) + { + this->back_project(vkb, hpsi, npw, pp.vkbnc, nbasis / npol, pp.nkb, bands); + } + ModuleBase::timer::end("NonlocalWorkspace", "apply"); +} + + +template +void NonlocalWorkspace::project_contract(const UnitCell& cell, const pseudopot_cell_vnl& pp, + const ModulePW::PW_Basis_K& basis, int spin, + int bands, int nbasis, int npol, int npw, + const T* vkb, const T* psi) +{ if (pp.nkb > 0 && bands > 0) { const size_t count = static_cast(pp.nkb) * bands; @@ -144,15 +161,9 @@ void NonlocalWorkspace::apply(const UnitCell& cell, const pseudopot_c } } #endif - if (npw > 0) - { - this->contract(cell, pp, spin, npol, bands); - this->back_project(vkb, hpsi, npw, pp.vkbnc, nbasis / npol, pp.nkb, bands); - } + this->contract(cell, pp, spin, npol, bands); } - ModuleBase::timer::end("NonlocalWorkspace", "apply"); } - template class NonlocalWorkspace, base_device::DEVICE_CPU>; template class NonlocalWorkspace, base_device::DEVICE_CPU>; #if defined(__CUDA) || defined(__ROCM) diff --git a/source/source_pw/module_pwdft/nonlocal_workspace.h b/source/source_pw/module_pwdft/nonlocal_workspace.h index 5a659645353..6ff8ab467dc 100644 --- a/source/source_pw/module_pwdft/nonlocal_workspace.h +++ b/source/source_pw/module_pwdft/nonlocal_workspace.h @@ -37,6 +37,16 @@ class NonlocalWorkspace /** @brief Borrow the most recently computed projection coefficients. */ T* get_becp() const { return becp_; } + + /** @brief Project and contract on every pool rank, including empty PW partitions; bands counts spin-component columns. + * B uses [column][projector] and DB [projector][column]; both borrowed arrays are pool-reduced and valid until the next operation. + */ + void project_contract(const UnitCell& cell, const pseudopot_cell_vnl& pp, + const ModulePW::PW_Basis_K& basis, int spin, + int bands, int nbasis, int npol, int npw, + const T* vkb, const T* psi); + const T* projections() const { return becp_; } + const T* contracted() const { return ps_; } }; } // namespace hamilt #endif diff --git a/source/source_pw/module_pwdft/op_pw_meta.h b/source/source_pw/module_pwdft/op_pw_meta.h index 533eba97d1e..bb9ffbc21b3 100644 --- a/source/source_pw/module_pwdft/op_pw_meta.h +++ b/source/source_pw/module_pwdft/op_pw_meta.h @@ -53,8 +53,6 @@ class Meta> : public OperatorPW } const int * get_isk() const {return this->isk;} const Real* get_vk() const {return this->vk;} - /** @brief Select the borrowed endpoint or midpoint kinetic-energy-density potential. */ - void set_vk(const Real* potential) { this->vk = potential; } int get_vk_row() const {return this->vk_row;} int get_vk_col() const {return this->vk_col;} const ModulePW::PW_Basis_K* get_wfcpw() const diff --git a/source/source_pw/module_pwdft/op_pw_vel.cpp b/source/source_pw/module_pwdft/op_pw_vel.cpp index 4038442bbee..980720da9a0 100644 --- a/source/source_pw/module_pwdft/op_pw_vel.cpp +++ b/source/source_pw/module_pwdft/op_pw_vel.cpp @@ -281,12 +281,8 @@ void Velocity::act(const psi::Psi, Device>* ModuleBase::gemm_op()('C', 'N', nkb3, n_npwx, npw, &one, gradvkb_d, max_npw, psi0, max_npw, &zero, becp2_, nkb3); } - if (npol != 1) - { - ModuleBase::WARNING_QUIT("Velocity", "Non-collinear velocity is not supported."); - } // deeq stores energies in Ry; velocity uses Hartree atomic units like p. - this->contraction_.contract(*this->ucell, *this->ppcell, this->isk[this->ik], n_npwx, FPTYPE(0.5), *this->wfcpw, becp1_); + this->contraction_.contract(*this->ucell, *this->ppcell, this->isk[this->ik], n_npwx, npol, FPTYPE(0.5), *this->wfcpw, becp1_); if (npw == 0) { diff --git a/source/source_pw/module_pwdft/op_pw_vel.h b/source/source_pw/module_pwdft/op_pw_vel.h index 1c5096e2389..8730d89d1c3 100644 --- a/source/source_pw/module_pwdft/op_pw_vel.h +++ b/source/source_pw/module_pwdft/op_pw_vel.h @@ -30,14 +30,6 @@ class Velocity void init(const int ik_in); /** @brief Refresh momentum and projectors with a Hartree-unit vector potential. */ void init(const int ik_in, const ModuleBase::Vector3& vector_potential); - /** @brief Refresh borrowed spin and meta-GGA potential views before reuse. */ - void set_state(const int* isk, const FPTYPE* vtau, const int cols, const int rows) - { - this->isk = isk; - vtau_ = vtau; - vtau_col_ = cols; - vtau_row_ = rows; - } /** * @brief calculate \hat{v}|\psi> diff --git a/source/source_pw/module_pwdft/setup_pot.cpp b/source/source_pw/module_pwdft/setup_pot.cpp index 52761bb5270..4d4fd83e631 100644 --- a/source/source_pw/module_pwdft/setup_pot.cpp +++ b/source/source_pw/module_pwdft/setup_pot.cpp @@ -18,6 +18,7 @@ void pw::setup_pot(const int istep, pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi* kspw_psi, // electronic wave functions + psi::Psi, Device>* onsite_psi, // persistent double-precision mirror hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod @@ -67,12 +68,14 @@ void pw::setup_pot(const int istep, //---------------------------------------------------------- //! 4) Onsite projectors //---------------------------------------------------------- - if (inp.onsite_radius > 0) + const bool spinor_rt = inp.esolver_type == "tddft" && inp.nspin == 4; + // The RT observer owns endpoint projection setup, including spinor precision conversion. + if (inp.onsite_radius > 0 && !spinor_rt) { auto* onsite_p = projectors::OnsiteProjector::get_instance(); onsite_p->init(inp.orbital_dir, &ucell, - *(kspw_psi), + *onsite_psi, kv, *(pw_wfc), sf, @@ -125,6 +128,7 @@ template void pw::setup_pot, base_device::DEVICE_CPU>( pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi, base_device::DEVICE_CPU>* kspw_psi, // electronic wave functions + psi::Psi, base_device::DEVICE_CPU>* onsite_psi, hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod @@ -144,6 +148,7 @@ template void pw::setup_pot, base_device::DEVICE_CPU>( pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi, base_device::DEVICE_CPU>* kspw_psi, // electronic wave functions + psi::Psi, base_device::DEVICE_CPU>* onsite_psi, hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod @@ -164,6 +169,7 @@ template void pw::setup_pot, base_device::DEVICE_GPU>( pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi, base_device::DEVICE_GPU>* kspw_psi, // electronic wave functions + psi::Psi, base_device::DEVICE_GPU>* onsite_psi, hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod @@ -182,6 +188,7 @@ template void pw::setup_pot, base_device::DEVICE_GPU>( pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi, base_device::DEVICE_GPU>* kspw_psi, // electronic wave functions + psi::Psi, base_device::DEVICE_GPU>* onsite_psi, hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod diff --git a/source/source_pw/module_pwdft/setup_pot.h b/source/source_pw/module_pwdft/setup_pot.h index 6842159e261..d865f163b07 100644 --- a/source/source_pw/module_pwdft/setup_pot.h +++ b/source/source_pw/module_pwdft/setup_pot.h @@ -25,6 +25,7 @@ void setup_pot(const int istep, pseudopot_cell_vnl &ppcell, // non-local pseudopotentials VSep* vsep_cell, // U-1/2 method psi::Psi* kspw_psi, // electronic wave functions + psi::Psi, Device>* onsite_psi, // persistent double-precision mirror hamilt::HamiltBase* p_hamilt, // hamiltonian ModulePW::PW_Basis_K *pw_wfc, // pw for wfc const ModulePW::PW_Basis *pw_rhod, // pw for rhod diff --git a/source/source_pw/module_pwdft/td_history_pw.cpp b/source/source_pw/module_pwdft/td_history_pw.cpp index 7e981682939..4cf9e48be94 100644 --- a/source/source_pw/module_pwdft/td_history_pw.cpp +++ b/source/source_pw/module_pwdft/td_history_pw.cpp @@ -51,38 +51,28 @@ void save_potential(const ModuleBase::matrix& shape, const Real* current, ct::Te } // namespace template -void TDHistoryPW::prepare(const elecstate::Potential& potential, const bool needs_ked) +void TDHistoryPW::prepare(const elecstate::Potential& potential) { ModuleBase::timer::start("TDHistoryPW", "prepare"); prepare_pair(potential.get_veff_smooth(), &veff_prev_, &veff_mid_); - if (needs_ked) - { - prepare_pair(potential.get_vofk_smooth(), &vofk_prev_, &vofk_mid_); - } ModuleBase::timer::end("TDHistoryPW", "prepare"); } template -TDPotentialView::type> TDHistoryPW::prop_pot(elecstate::Potential& potential, - const bool needs_ked, - const bool corrector) +const typename GetTypeReal::type* TDHistoryPW::prop_pot(elecstate::Potential& potential, const bool corrector) { ModuleBase::timer::start("TDHistoryPW", "prop_pot"); - TDPotentialView view{potential.template get_veff_smooth_data(), potential.template get_vofk_smooth_data()}; + const Real* view = potential.template get_veff_smooth_data(); if (corrector) { - view.veff = average_potential(potential.get_veff_smooth(), view.veff, veff_prev_, &veff_mid_); - if (needs_ked) - { - view.vofk = average_potential(potential.get_vofk_smooth(), view.vofk, vofk_prev_, &vofk_mid_); - } + view = average_potential(potential.get_veff_smooth(), view, veff_prev_, &veff_mid_); } ModuleBase::timer::end("TDHistoryPW", "prop_pot"); return view; } template -void TDHistoryPW::save(const psi::Psi& current, elecstate::Potential& potential, const bool needs_ked) +void TDHistoryPW::save(const psi::Psi& current, elecstate::Potential& potential) { ModuleBase::timer::start("TDHistoryPW", "save"); if (!previous_) @@ -91,10 +81,6 @@ void TDHistoryPW::save(const psi::Psi& current, elecstate: } *previous_ = current; save_potential(potential.get_veff_smooth(), potential.template get_veff_smooth_data(), &veff_prev_); - if (needs_ked) - { - save_potential(potential.get_vofk_smooth(), potential.template get_vofk_smooth_data(), &vofk_prev_); - } ModuleBase::timer::end("TDHistoryPW", "save"); } diff --git a/source/source_pw/module_pwdft/td_history_pw.h b/source/source_pw/module_pwdft/td_history_pw.h index 5ebfdd90550..6d03fe748aa 100644 --- a/source/source_pw/module_pwdft/td_history_pw.h +++ b/source/source_pw/module_pwdft/td_history_pw.h @@ -10,29 +10,19 @@ namespace pw { -/** @brief Non-owning device pointers to the smooth local potentials. */ -template -struct TDPotentialView -{ - const Real* veff; - const Real* vofk; -}; - /** @brief Converged time-step history and predictor-corrector potential storage. */ template class TDHistoryPW { public: /** @brief Prepare storage after the potential grids have been updated. */ - void prepare(const elecstate::Potential& potential, const bool needs_ked); + void prepare(const elecstate::Potential& potential); /** @brief Use the current potential for the predictor, then average with the saved potential. */ - TDPotentialView::type> prop_pot(elecstate::Potential& potential, - const bool needs_ked, - const bool corrector); + const typename GetTypeReal::type* prop_pot(elecstate::Potential& potential, const bool corrector); /** @brief Save the converged state; the caller owns the convergence decision. */ - void save(const psi::Psi& current, elecstate::Potential& potential, const bool needs_ked); + void save(const psi::Psi& current, elecstate::Potential& potential); /** @brief Access the fixed origin of every corrector solve in the next step. */ const psi::Psi& previous() const; @@ -42,8 +32,6 @@ class TDHistoryPW std::unique_ptr> previous_; ct::Tensor veff_prev_; ct::Tensor veff_mid_; - ct::Tensor vofk_prev_; - ct::Tensor vofk_mid_; }; } // namespace pw diff --git a/source/source_pw/module_pwdft/td_obs_pw.h b/source/source_pw/module_pwdft/td_obs_pw.h new file mode 100644 index 00000000000..abdccfa03e1 --- /dev/null +++ b/source/source_pw/module_pwdft/td_obs_pw.h @@ -0,0 +1,47 @@ +#ifndef TD_OBS_PW_H +#define TD_OBS_PW_H + +#include +#include +#include + +namespace pw +{ +/** @brief Independent requests; all ranks must use the same choices. */ +struct TDVelRequestPW +{ + bool charge_total = false; + bool charge_per_k = false; + bool spin_current = false; +}; + +/** @brief Globally reduced currents; unrequested arrays stay zero or empty. */ +struct TDVelResultPW +{ + std::array total{}; ///< Electron charge current per volume, in Hartree atomic units. + std::vector per_k; ///< Same units, [global k including spin][Cartesian direction]. + std::array spin{}; ///< Spin angular momentum flux, [flow][spin], hbar/(Bohr^2*atomic time). +}; + +/** @brief Magnetic choices fixed at initialization, independent of ionic positions. */ +struct TDSpinOptsPW +{ + std::string method = "none"; + std::string orbital_dir; + double radius = 0.0; ///< Onsite cutoff in Bohr. + bool torque = false; +}; + +/** @brief Endpoint observables replicated on all ranks after the appropriate reductions. */ +struct TDSpinResultPW +{ + std::array mag{}; ///< ABACUS Pauli moment in mu_B; no electron minus sign. + double mag_abs = 0.0; ///< Integral of the magnetization magnitude, in mu_B. + double electrons = 0.0; ///< Valence electron count; excludes the NLCC core density. + std::vector atom_mag; ///< [atom][Cartesian direction], in mu_B. + std::array xc{}; ///< Rate of spin angular momentum S=M/2, hbar/atomic time. + std::array soc{}; ///< Same units as xc. + std::vector atom_soc; ///< Pseudopotential atom decomposition, [atom][direction]. +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_pwdft/td_pw.cpp b/source/source_pw/module_pwdft/td_pw.cpp index 1cf36b05880..0bd8cf58906 100644 --- a/source/source_pw/module_pwdft/td_pw.cpp +++ b/source/source_pw/module_pwdft/td_pw.cpp @@ -9,8 +9,7 @@ namespace pw { - -void check_td_input(const Input_para& input, const UnitCell& cell, const bool needs_ked, const bool cal_exx) +void check_td_input(const Input_para& input, const UnitCell& cell, int xc_type, bool cal_exx) { if (input.out_current != 0 && input.out_current != 1) { @@ -38,23 +37,29 @@ void check_td_input(const Input_para& input, const UnitCell& cell, const bool ne { ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT does not support spin constraints; set sc_mag_switch=false."); } + if (xc_type == 3 || xc_type == 5) + { + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT does not support meta-GGA."); + } if (cal_exx) { ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT does not support exact exchange (EXX); use a non-hybrid functional."); } - if ((input.nspin != 1 && input.nspin != 2) || input.bndpar != 1 || (input.td_stype != 0 && input.td_stype != 1)) + if ((input.nspin != 1 && input.nspin != 2 && input.nspin != 4) || input.bndpar != 1 || (input.td_stype != 0 && input.td_stype != 1)) { - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT requires nspin=1 or 2, bndpar=1 and td_stype=0 or 1."); + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT requires nspin=1, 2 or 4, bndpar=1 and td_stype=0 or 1."); } - if (input.mdp.md_restart || input.restart_load) + if (input.nspin == 4 && !input.noncolin) { - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT restart is not supported yet."); + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "nspin=4 PW RT-TDDFT requires noncolin=true."); } - if (input.td_stype == 1 && needs_ked) + if (input.out_elf[0] > 0) { - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", - "PW RT-TDDFT with a kinetic-energy-density functional requires td_stype=0; " - "the velocity-gauge coupling of the functional is not implemented."); + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT does not support ELF."); + } + if (input.mdp.md_restart || input.restart_load) + { + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT restart is not supported yet."); } } diff --git a/source/source_pw/module_pwdft/td_pw.h b/source/source_pw/module_pwdft/td_pw.h index 9bc6908b896..787f6ed800d 100644 --- a/source/source_pw/module_pwdft/td_pw.h +++ b/source/source_pw/module_pwdft/td_pw.h @@ -10,13 +10,14 @@ namespace pw { /** @brief Check PW propagation restrictions after resolving the XC functional and EXX configuration. */ -void check_td_input(const Input_para& input, const UnitCell& cell, const bool needs_ked, const bool cal_exx); +void check_td_input(const Input_para& input, const UnitCell& cell, int xc_type, bool cal_exx); /** @brief Return the global unshifted momentum bound after updating the distributed basis. */ double td_momentum_bound(const ModulePW::PW_Basis_K& basis, const double tpiba); /** @brief Extend the projector table to cover both endpoint and propagation fields in Hartree atomic units. */ -void ensure_td_vnl(const UnitCell& cell, const double unshifted_bound, +void ensure_td_vnl(const UnitCell& cell, + const double unshifted_bound, const ModuleBase::Vector3& A_right_ha, const ModuleBase::Vector3& A_prop_ha, pseudopot_cell_vnl* projectors); diff --git a/source/source_pw/module_pwdft/td_spin_pw.cpp b/source/source_pw/module_pwdft/td_spin_pw.cpp new file mode 100644 index 00000000000..697d3f3d7ec --- /dev/null +++ b/source/source_pw/module_pwdft/td_spin_pw.cpp @@ -0,0 +1,406 @@ +#include "source_pw/module_pwdft/td_spin_pw.h" + +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_pw/module_proj/onsite_proj.h" +#include "source_pw/module_pwdft/td_pw.h" + +#include +#include +#include +#include +#include + +namespace pw +{ +namespace +{ +double image_dist(const ModuleBase::Vector3& delta, const ModuleBase::Matrix3& lattice, const std::array& inverse_norm) +{ + ModuleBase::Vector3 nearest; + for (int a = 0; a < 3; ++a) + { + nearest[a] = delta[a] - std::round(delta[a]); + } + double best = (nearest * lattice).norm2(); + const double radius = std::sqrt(best); + int lower[3]; + int upper[3]; + for (int a = 0; a < 3; ++a) + { + // Every better image lies inside these bounds, even for highly skew cells. + const double extent = radius * inverse_norm[a] + 1e-12; + lower[a] = static_cast(std::ceil(-delta[a] - extent)); + upper[a] = static_cast(std::floor(-delta[a] + extent)); + } + for (int i = lower[0]; i <= upper[0]; ++i) + { + for (int j = lower[1]; j <= upper[1]; ++j) + { + for (int k = lower[2]; k <= upper[2]; ++k) + { + const ModuleBase::Vector3 image(delta.x + i, delta.y + j, delta.z + k); + best = std::min(best, (image * lattice).norm2()); + } + } + } + return best; +} +} // namespace + +std::vector td_voronoi_pw(const UnitCell& cell, const ModulePW::PW_Basis& grid) +{ + const ModuleBase::Matrix3 inverse = cell.latvec.Inverse(); + const std::array inverse_norm{ + {std::sqrt(inverse.e11 * inverse.e11 + inverse.e21 * inverse.e21 + inverse.e31 * inverse.e31), + std::sqrt(inverse.e12 * inverse.e12 + inverse.e22 * inverse.e22 + inverse.e32 * inverse.e32), + std::sqrt(inverse.e13 * inverse.e13 + inverse.e23 * inverse.e23 + inverse.e33 * inverse.e33)}}; + std::vector> atoms; + for (int it = 0; it < cell.ntype; ++it) + { + for (int ia = 0; ia < cell.atoms[it].na; ++ia) + { + atoms.push_back(cell.atoms[it].taud[ia]); + } + } + std::vector labels(grid.nrxx, 0); + for (int ir = 0; ir < grid.nrxx; ++ir) + { + const int ix = ir / (grid.ny * grid.nplane); + const int iy = ir / grid.nplane % grid.ny; + const int iz = ir % grid.nplane + grid.startz_current; + const ModuleBase::Vector3 point(double(ix) / grid.nx, double(iy) / grid.ny, double(iz) / grid.nz); + double best = std::numeric_limits::infinity(); + for (int atom = 0; atom < cell.nat; ++atom) + { + const ModuleBase::Vector3 delta = point - atoms[atom]; + const double distance = image_dist(delta, cell.latvec, inverse_norm); + const double tolerance = 64 * std::numeric_limits::epsilon() * std::max(1.0, distance); + if (atom == 0 || distance < best - tolerance) + { + best = distance; + labels[ir] = atom; + } + } + } + return labels; +} + +template +void TDSpinPW::initialize(const UnitCell& cell, + const ModulePW::PW_Basis& grid, + const ModulePW::PW_Basis_K& basis, + const K_Vectors& kv, + Structure_Factor& sf, + pseudopot_cell_vnl& pp, + const TDSpinOptsPW& options) +{ + if (cell_ != nullptr || cell.get_npol() != 2) + { + ModuleBase::WARNING_QUIT("TDSpinPW", "Initialize once with a spinor basis."); + } + if (options.method != "none" && options.method != "voronoi" && options.method != "onsite") + { + ModuleBase::WARNING_QUIT("TDSpinPW", "Unknown magnetic partition."); + } + cell_ = &cell; + grid_ = &grid; + basis_ = &basis; + kv_ = &kv; + sf_ = &sf; + pp_ = &pp; + options_ = options; +} + +template +void TDSpinPW::sync_positions() +{ + bool changed = pos_cache_.size() != static_cast(cell_->nat); + int atom = 0; + for (int it = 0; !changed && it < cell_->ntype; ++it) + { + for (int ia = 0; !changed && ia < cell_->atoms[it].na; ++ia, ++atom) + { + changed = !(pos_cache_[atom] == cell_->atoms[it].taud[ia]); + } + } + if (!changed) + { + return; + } + // Atomic phases and nearest-atom labels change even at fixed grid, k point and vector potential. + labels_.clear(); + if (onsite_projector_) + { + onsite_projector_->invalidate_proj(); + } + pos_cache_.clear(); + for (int it = 0; it < cell_->ntype; ++it) + { + for (int ia = 0; ia < cell_->atoms[it].na; ++ia) + { + pos_cache_.push_back(cell_->atoms[it].taud[ia]); + } + } +} + +template +void TDSpinPW::density(const elecstate::ElecState& state, TDSpinResultPW* result) +{ + const UnitCell& cell = *cell_; + const ModulePW::PW_Basis& grid = *grid_; + const TDSpinOptsPW& options = options_; + const bool voronoi = options.method == "voronoi"; + if (voronoi && labels_.size() != static_cast(grid.nrxx)) + { + labels_ = td_voronoi_pw(cell, grid); + } + const double volume = cell.omega / grid.nxyz; + const double* const* rho = state.charge->rho; + const ModuleBase::matrix& potential = state.pot->get_eff_v(); + double totals[8] = {}; + if (voronoi) + { + result->atom_mag.assign(3 * cell.nat, 0.0); + } + for (int ir = 0; ir < grid.nrxx; ++ir) + { + double norm = 0.0; + for (int a = 0; a < 3; ++a) + { + const double m = rho[a + 1][ir]; + totals[a] += m * volume; + norm += m * m; + if (voronoi) + { + result->atom_mag[3 * labels_[ir] + a] += m * volume; + } + if (options.torque) + { + // The effective spin field is in Ry; the angular-momentum torque here uses Hartree time. + const int b = (a + 1) % 3; + const int c = (a + 2) % 3; + totals[5 + a] += 0.5 * volume * (potential(b + 1, ir) * rho[c + 1][ir] - potential(c + 1, ir) * rho[b + 1][ir]); + } + } + totals[3] += std::sqrt(norm) * volume; + totals[4] += rho[0][ir] * volume; + } + // Density already contains all k pools. Reduce only the grid partition. + Parallel_Reduce::reduce_pool(totals, 8); + if (voronoi) + { + const int count = result->atom_mag.size(); + Parallel_Reduce::reduce_pool(result->atom_mag.data(), count); + } + for (int a = 0; a < 3; ++a) + { + result->mag[a] = totals[a]; + result->xc[a] = totals[5 + a]; + } + result->mag_abs = totals[3]; + result->electrons = totals[4]; +} + +template +void TDSpinPW::soc(const psi::Psi& psi, + const ModuleBase::matrix& weights, + const ModuleBase::Vector3& a, + TDSpinResultPW* result) +{ + const UnitCell& cell = *cell_; + const ModulePW::PW_Basis_K& basis = *basis_; + pseudopot_cell_vnl& pp = *pp_; + result->atom_soc.assign(3 * cell.nat, 0.0); + if (pp.nkb == 0) + { + return; + } + using CtDevice = typename ct::PsiToContainer::type; + const int nbands = psi.get_nbands(); + // Projection kernels count spin-component columns, not physical bands. + const int ncols = 2 * nbands; + const int ld_psi = psi.get_nbasis(); + const std::int64_t size = std::max(1, static_cast(pp.nkb) * basis.npwk_max); + if (beta_.NumElements() < size) + { + beta_ = ct::Tensor(ct::DataTypeToEnum::value, ct::DeviceTypeToEnum::value, {size}); + } + Complex* beta = beta_.template data(); + const size_t count = static_cast(pp.nkb) * ncols; + projections_.resize(count); + contracted_.resize(count); + Device* context = nullptr; + for (int ik = 0; ik < basis.nks; ++ik) + { + psi.fix_k(ik); + const int npw = basis.npwk[ik]; + if (npw > 0) + { + pp.getvnl(context, cell, ik, a, beta); + } + nonlocal_.project_contract(cell, pp, basis, 0, ncols, ld_psi, 2, npw, beta, psi.get_pointer()); + if (basis.poolrank != 0) + { + // B and DB already contain the full pool sum; count each k-point contribution only once. + continue; + } + base_device::memory::synchronize_memory_op()(projections_.data(), + nonlocal_.projections(), + count); + base_device::memory::synchronize_memory_op()(contracted_.data(), + nonlocal_.contracted(), + count); + int offset = 0; + int atom = 0; + const Complex imaginary(0, 1); + for (int it = 0; it < cell.ntype; ++it) + { + const int nh = cell.atoms[it].ncpp.nh; + for (int ia = 0; ia < cell.atoms[it].na; ++ia, ++atom) + { + for (int p = offset; p < offset + nh; ++p) + { + for (int band = 0; band < nbands; ++band) + { + const Complex up = std::conj(projections_[(2 * band) * pp.nkb + p]); + const Complex down = std::conj(projections_[(2 * band + 1) * pp.nkb + p]); + const Complex du = contracted_[p * ncols + 2 * band]; + const Complex dd = contracted_[p * ncols + 2 * band + 1]; + const double weight = 0.5 * weights(ik, band); + result->atom_soc[3 * atom] += weight * std::imag(up * dd + down * du); + result->atom_soc[3 * atom + 1] += weight * std::imag(-imaginary * up * dd + imaginary * down * du); + result->atom_soc[3 * atom + 2] += weight * std::imag(up * du - down * dd); + } + } + offset += nh; + } + } + } + const int values = result->atom_soc.size(); + Parallel_Reduce::reduce_all(result->atom_soc.data(), values); + for (int atom = 0; atom < cell.nat; ++atom) + { + for (int a = 0; a < 3; ++a) + { + result->soc[a] += result->atom_soc[3 * atom + a]; + } + } +} + +template +void TDSpinPW::onsite(const psi::Psi& psi, + const elecstate::ElecState& state, + const ModuleBase::Vector3& a, + TDSpinResultPW* result) +{ + const UnitCell& cell = *cell_; + const ModulePW::PW_Basis_K& basis = *basis_; + const K_Vectors& kv = *kv_; + Structure_Factor& sf = *sf_; + const TDSpinOptsPW& options = options_; + // Onsite owns its atomic result; the independently integrated total moment remains unchanged. + result->atom_mag.assign(3 * cell.nat, 0.0); + const double dq = pp_->table_dq(); + using OnsiteValue = std::complex; + const int nbands = psi.get_nbands(); + const int ld_psi = psi.get_nbasis(); + if (!onsite_psi_) + { + const int npw = basis.npwk[0]; + onsite_psi_.reset(new psi::Psi(1, nbands, ld_psi, npw, true)); + onsite_projector_.reset(new projectors::OnsiteProjector()); + q_bound_ = td_momentum_bound(basis, cell.tpiba); + const int nq = static_cast(std::ceil(q_bound_ / dq)) + 4; + onsite_projector_->init(options.orbital_dir, &cell, *onsite_psi_, kv, basis, sf, options.radius, nq, dq, state.wg, state.ekb); + } + auto* projector = onsite_projector_.get(); + if (onsite_psi_->get_nbands() != nbands || onsite_psi_->get_nbasis() != ld_psi) + { + ModuleBase::WARNING_QUIT("TDSpinPW", "The onsite wavefunction layout changed after initialization."); + } + const double bound = q_bound_ + a.norm(); + projector->ensure_range(bound, dq); + const int nkb = projector->get_tot_nproj(); + const int npwx = ld_psi / 2; + const size_t size = static_cast(nbands) * ld_psi; + for (int ik = 0; ik < basis.nks; ++ik) + { + psi.fix_k(ik); + OnsiteValue* converted = onsite_psi_->get_pointer(); + if (size > 0) + { + // Onsite projections use double precision, so float spinors require a value conversion, not a pointer cast. + base_device::memory::cast_memory_op()(converted, psi.get_pointer(), size); + } + const int columns = 2 * nbands; + projector->overlap_td(ik, columns, converted, npwx, a); + if (basis.poolrank != 0) + { + // Overlaps are replicated after the pool reduction; count each occupied orbital only once. + continue; + } + const OnsiteValue* b = projector->get_h_becp(); + int offset = 0; + for (int atom = 0; atom < cell.nat; ++atom) + { + const int nh = projector->get_nh(atom); + for (int p = offset; p < offset + nh; ++p) + { + for (int band = 0; band < nbands; ++band) + { + const OnsiteValue up = b[2 * band * nkb + p]; + const OnsiteValue down = b[(2 * band + 1) * nkb + p]; + const double weight = state.wg(ik, band); + const OnsiteValue cross = std::conj(up) * down; + result->atom_mag[3 * atom] += 2 * weight * cross.real(); + result->atom_mag[3 * atom + 1] += 2 * weight * cross.imag(); + result->atom_mag[3 * atom + 2] += weight * (std::norm(up) - std::norm(down)); + } + } + offset += nh; + } + } + const int count = result->atom_mag.size(); + Parallel_Reduce::reduce_all(result->atom_mag.data(), count); +} + +template +TDSpinResultPW TDSpinPW::calculate(const psi::Psi& psi, + const elecstate::ElecState& state, + const ModuleBase::Vector3& a) +{ + ModuleBase::timer::start("TDSpinPW", "calculate"); + if (cell_ == nullptr) + { + ModuleBase::WARNING_QUIT("TDSpinPW", "Initialize before sampling observables."); + } + if (psi.get_npol() != 2 || psi.get_nbasis() != 2 * basis_->npwk_max) + { + ModuleBase::WARNING_QUIT("TDSpinPW", "Magnetic observables require the initialized spinor layout."); + } + sync_positions(); + const int old_k = psi.get_current_k(); + TDSpinResultPW result; + density(state, &result); + if (options_.method == "onsite") + { + onsite(psi, state, a, &result); + } + if (options_.torque) + { + soc(psi, state.wg, a, &result); + } + // Observable evaluation must preserve the caller's k-point cursor. + psi.fix_k(old_k); + ModuleBase::timer::end("TDSpinPW", "calculate"); + return result; +} + +template class TDSpinPW; +template class TDSpinPW; +#if defined(__CUDA) || defined(__ROCM) +template class TDSpinPW; +template class TDSpinPW; +#endif +} // namespace pw diff --git a/source/source_pw/module_pwdft/td_spin_pw.h b/source/source_pw/module_pwdft/td_spin_pw.h new file mode 100644 index 00000000000..3c6757756a1 --- /dev/null +++ b/source/source_pw/module_pwdft/td_spin_pw.h @@ -0,0 +1,69 @@ +#ifndef TD_SPIN_PW_H +#define TD_SPIN_PW_H + +#include "source_base/module_container/ATen/core/tensor.h" +#include "source_estate/elecstate.h" +#include "source_pw/module_proj/onsite_proj.h" +#include "source_pw/module_pwdft/nonlocal_workspace.h" +#include "source_pw/module_pwdft/td_obs_pw.h" + +#include +#include +#include + +namespace pw +{ +/** @brief Periodic nearest-atom labels on the local charge grid, in global atom order. */ +std::vector td_voronoi_pw(const UnitCell& cell, const ModulePW::PW_Basis& grid); + +/** @brief Endpoint magnetism with reusable device workspaces and position-aware caches. */ +template +class TDSpinPW +{ + private: + using Complex = std::complex; + const UnitCell* cell_ = nullptr; + const ModulePW::PW_Basis* grid_ = nullptr; + const ModulePW::PW_Basis_K* basis_ = nullptr; + const K_Vectors* kv_ = nullptr; + Structure_Factor* sf_ = nullptr; + pseudopot_cell_vnl* pp_ = nullptr; + TDSpinOptsPW options_; + std::vector> pos_cache_; + std::vector labels_; + hamilt::NonlocalWorkspace nonlocal_; + ct::Tensor beta_; + std::vector projections_; + std::vector contracted_; + std::unique_ptr, Device>> onsite_psi_; + // Destroy the projector before the wavefunction it borrows. + std::unique_ptr> onsite_projector_; + double q_bound_ = 0.0; + + void sync_positions(); + void density(const elecstate::ElecState& state, TDSpinResultPW* result); + void soc(const psi::Psi& psi, + const ModuleBase::matrix& weights, + const ModuleBase::Vector3& a, + TDSpinResultPW* result); + void onsite(const psi::Psi& psi, + const elecstate::ElecState& state, + const ModuleBase::Vector3& a, + TDSpinResultPW* result); + + public: + /** @brief Bind once; dependencies outlive this object and retain their cell and layout. + * The caller must refresh structure factors and electronic data before sampling moved ions. + */ + void initialize(const UnitCell& cell, + const ModulePW::PW_Basis& grid, + const ModulePW::PW_Basis_K& basis, + const K_Vectors& kv, + Structure_Factor& sf, + pseudopot_cell_vnl& pp, + const TDSpinOptsPW& options); + /** @brief Sample all ranks and restore the k cursor; coordinate changes invalidate geometric caches. */ + TDSpinResultPW calculate(const psi::Psi& psi, const elecstate::ElecState& state, const ModuleBase::Vector3& a); +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_pwdft/td_vel_pw.cpp b/source/source_pw/module_pwdft/td_vel_pw.cpp new file mode 100644 index 00000000000..1dc363c0ea0 --- /dev/null +++ b/source/source_pw/module_pwdft/td_vel_pw.cpp @@ -0,0 +1,207 @@ +#include "source_pw/module_pwdft/td_vel_pw.h" + +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_hsolver/kernels/linear_op.h" + +#include +#include +#include + +namespace pw +{ +template +void TDVelPW::initialize(const UnitCell& cell, + const ModulePW::PW_Basis_K& basis, + const K_Vectors& kv, + pseudopot_cell_vnl& pp) +{ + if (cell_ != nullptr) + { + ModuleBase::WARNING_QUIT("TDVelPW", "Initialize once before sampling."); + } + cell_ = &cell; + basis_ = &basis; + kv_ = &kv; + pp_ = &pp; +} + +template +TDVelResultPW TDVelPW::calculate(const psi::Psi& psi, + const elecstate::ElecState& state, + const ModuleBase::Vector3& a, + const TDVelRequestPW& request) +{ + ModuleBase::timer::start("TDVelPW", "calculate"); + if (cell_ == nullptr) + { + ModuleBase::WARNING_QUIT("TDVelPW", "Initialize before sampling observables."); + } + TDVelResultPW result; + if (!request.charge_total && !request.charge_per_k && !request.spin_current) + { + ModuleBase::timer::end("TDVelPW", "calculate"); + return result; + } + const int npol = psi.get_npol(); + const int npwx = basis_->npwk_max; + const int nbands = psi.get_nbands(); + const int ncols = nbands * npol; + const int old_k = psi.get_current_k(); + if (psi.get_nbasis() != npwx * npol || npol != cell_->get_npol() || (request.spin_current && npol != 2)) + { + ModuleBase::WARNING_QUIT("TDVelPW", "Incompatible wavefunction layout or spin-current request."); + } + + if (!vel_) + { + vel_.reset(new hamilt::Velocity(basis_, kv_->isk.data(), pp_, cell_, true, nullptr, 0, 0)); + } + + using CtDevice = typename ct::PsiToContainer::type; + using CopyToHost = base_device::memory::synchronize_memory_op; + using Zero = base_device::memory::set_memory_op; + const ct::DeviceType device = ct::DeviceTypeToEnum::value; + const std::int64_t comp_size = static_cast(ncols) * npwx; + const std::int64_t vel_size = 3 * comp_size; + const std::int64_t capacity = std::max(1, vel_size); + const std::int64_t dot_size = std::max(1, ncols); + if (vpsi_.NumElements() < capacity) + { + vpsi_ = ct::Tensor(ct::DataTypeToEnum::value, device, {capacity}); + } + if (dots_.NumElements() < dot_size) + { + dots_ = ct::Tensor(ct::DataTypeToEnum::value, device, {dot_size}); + } + Complex* vpsi = vpsi_.template data(); + Complex* dots = dots_.template data(); + diag_.resize(ncols); + if (request.spin_current) + { + cross_.resize(nbands); + } + if (request.charge_per_k) + { + const std::int64_t size = 3 * static_cast(kv_->get_nkstot()); + result.per_k.resize(size, 0.0); + } + + for (int ik = 0; ik < basis_->nks; ++ik) + { + psi.fix_k(ik); + const Complex* data = psi.get_pointer(); + const int npw = basis_->npwk[ik]; + if (vel_size > 0) + { + Zero()(vpsi, 0, vel_size); + } + vel_->init(ik, a); + vel_->act(&psi, ncols, data, vpsi, false); + if (npw == 0) + { + // Projection reductions above are collective; only local inner products can be skipped. + continue; + } + for (int id = 0; id < 3; ++id) + { + const Complex* component = vpsi + id * comp_size; + // Each spin component is one npwx-strided column; reuse its diagonal product for both currents. + hsolver::linear_op().dot(npwx, npw, ncols, data, component, dots); + CopyToHost()(diag_.data(), dots, ncols); + if (request.charge_total || request.charge_per_k) + { + for (int ib = 0; ib < nbands; ++ib) + { + double value = 0.0; + for (int spin = 0; spin < npol; ++spin) + { + value += std::real(diag_[ib * npol + spin]); + } + const double charge = -state.wg(ik, ib) * value; + if (request.charge_total) + { + result.total[id] += charge; + } + if (request.charge_per_k) + { + const std::int64_t index = 3 * static_cast(kv_->ik2iktot[ik]) + id; + result.per_k[index] += charge; + } + } + } + if (request.spin_current) + { + const int ld_psi = 2 * npwx; + // Sigma_z uses the diagonal products already needed by charge current; S = sigma/2. + for (int spin = 0; spin < 2; ++spin) + { + const double sign = spin == 0 ? 1.0 : -1.0; + for (int ib = 0; ib < nbands; ++ib) + { + const double weight = 0.5 * state.wg(ik, ib); + result.spin[3 * id + 2] += weight * sign * std::real(diag_[2 * ib + spin]); + } + } + // Sigma_x adds the cross products, while sigma_y takes their imaginary parts with opposite signs. + for (int left = 0; left < 2; ++left) + { + const int right = 1 - left; + const Complex* bra = data + left * npwx; + const Complex* ket = component + right * npwx; + hsolver::linear_op().dot(ld_psi, npw, nbands, bra, ket, dots); + CopyToHost()(cross_.data(), dots, nbands); + const double sign = left == 0 ? 1.0 : -1.0; + for (int ib = 0; ib < nbands; ++ib) + { + const double weight = 0.5 * state.wg(ik, ib); + result.spin[3 * id] += weight * std::real(cross_[ib]); + result.spin[3 * id + 1] += weight * sign * std::imag(cross_[ib]); + } + } + } + } + } + // Sampling must not leave a different k-point selected for the propagation driver. + psi.fix_k(old_k); + if (request.charge_total) + { + Parallel_Reduce::reduce_all(result.total.data(), 3); + } + const std::int64_t max_chunk = std::numeric_limits::max(); + const std::int64_t k_size = result.per_k.size(); + for (std::int64_t offset = 0; offset < k_size;) + { + const std::int64_t remaining = k_size - offset; + const int count = static_cast(std::min(max_chunk, remaining)); + double* chunk = result.per_k.data() + offset; + Parallel_Reduce::reduce_all(chunk, count); + offset += count; + } + if (request.spin_current) + { + Parallel_Reduce::reduce_all(result.spin.data(), 9); + } + for (double& value: result.total) + { + value /= cell_->omega; + } + for (double& value: result.per_k) + { + value /= cell_->omega; + } + for (double& value: result.spin) + { + value /= cell_->omega; + } + ModuleBase::timer::end("TDVelPW", "calculate"); + return result; +} + +template class TDVelPW; +template class TDVelPW; +#if defined(__CUDA) || defined(__ROCM) +template class TDVelPW; +template class TDVelPW; +#endif +} // namespace pw diff --git a/source/source_pw/module_pwdft/td_vel_pw.h b/source/source_pw/module_pwdft/td_vel_pw.h new file mode 100644 index 00000000000..1d8a2fdc6db --- /dev/null +++ b/source/source_pw/module_pwdft/td_vel_pw.h @@ -0,0 +1,44 @@ +#ifndef TD_VEL_PW_H +#define TD_VEL_PW_H + +#include "source_estate/elecstate.h" +#include "source_pw/module_pwdft/op_pw_vel.h" +#include "source_pw/module_pwdft/td_obs_pw.h" + +#include +#include +#include + +namespace pw +{ +/** @brief Charge and spin currents sharing one velocity action per k point. */ +template +class TDVelPW +{ + private: + using Complex = std::complex; + const UnitCell* cell_ = nullptr; + const ModulePW::PW_Basis_K* basis_ = nullptr; + const K_Vectors* kv_ = nullptr; + pseudopot_cell_vnl* pp_ = nullptr; + std::unique_ptr> vel_; + ct::Tensor vpsi_; + ct::Tensor dots_; + std::vector diag_; + std::vector cross_; + + public: + /** @brief Bind once; dependencies must outlive this object and retain their layout. + * Ionic positions may change after the caller updates the structure factors. + */ + void initialize(const UnitCell& cell, const ModulePW::PW_Basis_K& basis, const K_Vectors& kv, pseudopot_cell_vnl& pp); + /** @brief Sample an endpoint on all ranks, restoring the wavefunction's k-point cursor. + * Spin current requires npol=2; requests must agree across ranks. + */ + TDVelResultPW calculate(const psi::Psi& psi, + const elecstate::ElecState& state, + const ModuleBase::Vector3& a, + const TDVelRequestPW& request); +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_pwdft/velocity_workspace.cpp b/source/source_pw/module_pwdft/velocity_workspace.cpp index c7147c5594e..f9e2c7a7486 100644 --- a/source/source_pw/module_pwdft/velocity_workspace.cpp +++ b/source/source_pw/module_pwdft/velocity_workspace.cpp @@ -33,6 +33,7 @@ void VelocityWorkspace::contract(const UnitCell& cell, const pseudopot_cell_vnl& pp, const int spin, const int bands, + const int npol, const Real scale, const ModulePW::PW_Basis_K& basis, Complex* buffer) @@ -65,6 +66,7 @@ void VelocityWorkspace::contract(const UnitCell& cell, base_device::memory::set_memory_op()(output, 0, projection_elements); for (int component = 0; component < 4; ++component) { + // The ordinary overlap is packed separately from the three interleaved gradient overlaps. int sum = 0; int atom = 0; const int stride = component == 0 ? pp.nkb : 3 * pp.nkb; @@ -73,24 +75,45 @@ void VelocityWorkspace::contract(const UnitCell& cell, Complex* component_output = output + component * count; for (int type = 0; type < cell.ntype; ++type) { - nonlocal_pw_op()(nullptr, - cell.atoms[type].na, - bands, - cell.atoms[type].ncpp.nh, - sum, - atom, - spin, - stride, - pp.deeq.getBound2(), - pp.deeq.getBound3(), - pp.deeq.getBound4(), - pp.template get_deeq_data(), - component_output, - input); + // Both kernels advance sum and atom by reference. + if (npol == 2) + { + nonlocal_pw_op()(nullptr, + cell.atoms[type].na, + bands, + cell.atoms[type].ncpp.nh, + sum, + atom, + stride, + pp.deeq_nc.getBound2(), + pp.deeq_nc.getBound3(), + pp.deeq_nc.getBound4(), + pp.template get_deeq_nc_data(), + component_output, + input); + } + else + { + nonlocal_pw_op()(nullptr, + cell.atoms[type].na, + bands, + cell.atoms[type].ncpp.nh, + sum, + atom, + spin, + stride, + pp.deeq.getBound2(), + pp.deeq.getBound3(), + pp.deeq.getBound4(), + pp.template get_deeq_data(), + component_output, + input); + } } } if (scale != Real(1)) { + // Velocity contractions convert the Ry-valued D matrix to Hartree without changing the Hamiltonian's storage. const Complex factor(scale, 0); const std::int64_t max_chunk = std::numeric_limits::max(); for (std::int64_t offset = 0; offset < projection_elements;) diff --git a/source/source_pw/module_pwdft/velocity_workspace.h b/source/source_pw/module_pwdft/velocity_workspace.h index 7dd2665f63b..85a9b79d94d 100644 --- a/source/source_pw/module_pwdft/velocity_workspace.h +++ b/source/source_pw/module_pwdft/velocity_workspace.h @@ -22,11 +22,12 @@ class VelocityWorkspace /** @brief Reserve four input and four output projector blocks. */ Complex* prepare(const std::int64_t count); - /** @brief Reduce projections and contract D on their native device. */ + /** @brief Reduce four projection blocks and contract D on their native device; bands counts spin-component columns. */ void contract(const UnitCell& cell, const pseudopot_cell_vnl& pp, const int spin, const int bands, + const int npol, const Real scale, const ModulePW::PW_Basis_K& basis, Complex* buffer); diff --git a/source/source_pw/module_pwdft/vnl_pw.cpp b/source/source_pw/module_pwdft/vnl_pw.cpp index 233e0590bca..90a74bbcd8c 100644 --- a/source/source_pw/module_pwdft/vnl_pw.cpp +++ b/source/source_pw/module_pwdft/vnl_pw.cpp @@ -169,9 +169,10 @@ void pseudopot_cell_vnl::init(const UnitCell& ucell, resmem_cd_op()(c_deeq_nc, PARAM.inp.nspin * ucell.nat * this->nhm * this->nhm); resmem_cd_op()(c_qq_so, ntype * 4 * this->nhm * this->nhm); } + // Forces use double precision even when propagation uses float. + resmem_zd_op()(z_deeq_nc, PARAM.inp.nspin * ucell.nat * this->nhm * this->nhm); if (PARAM.globalv.has_double_data) { - resmem_zd_op()(z_deeq_nc, PARAM.inp.nspin * ucell.nat * this->nhm * this->nhm); resmem_zd_op()(z_qq_so, ntype * 4 * this->nhm * this->nhm); } resmem_dd_op()(d_deeq, PARAM.inp.nspin * ucell.nat * this->nhm * this->nhm); @@ -192,9 +193,9 @@ void pseudopot_cell_vnl::init(const UnitCell& ucell, resmem_ch_op()(c_deeq_nc, PARAM.inp.nspin * ucell.nat * this->nhm * this->nhm, "VNL::c_deeq_nc"); resmem_ch_op()(c_qq_so, ntype * 4 * this->nhm * this->nhm, "VNL::c_qq_so"); } + this->z_deeq_nc = this->deeq_nc.ptr; if (PARAM.globalv.has_double_data) { - this->z_deeq_nc = this->deeq_nc.ptr; this->z_qq_so = this->qq_so.ptr; } this->d_deeq = this->deeq.ptr; diff --git a/source/source_pw/module_pwdft/vnl_pw_deeq.cpp b/source/source_pw/module_pwdft/vnl_pw_deeq.cpp index 54ad733b274..352a8d8f450 100644 --- a/source/source_pw/module_pwdft/vnl_pw_deeq.cpp +++ b/source/source_pw/module_pwdft/vnl_pw_deeq.cpp @@ -151,12 +151,10 @@ void pseudopot_cell_vnl::cal_effective_D(const ModuleBase::matrix& veff, this->deeq_nc.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); } - if (PARAM.globalv.has_double_data) - { - syncmem_z2z_h2d_op()(this->z_deeq_nc, - this->deeq_nc.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); - } + // Keep the double-precision force view current in single-precision runs. + syncmem_z2z_h2d_op()(this->z_deeq_nc, + this->deeq_nc.ptr, + PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); syncmem_d2d_h2d_op()(this->d_deeq, this->deeq.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref index 3824b279feb..4760fa30298 100644 --- a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref @@ -1,7 +1,8 @@ -0 0 -0.05 0 -0.1 -0.0350185 -0.15 0.27 -0.2 -0.0350185 -0.25 0 -0.3 0 + 0.000000 0.000000000000e+00 + 0.050000 0.000000000000e+00 + 0.100000 -3.501845272301e-02 + 0.150000 2.700000000000e-01 + 0.200000 -3.501845272301e-02 + 0.250000 0.000000000000e+00 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref index 0ec5febc62e..83f36eb516f 100644 --- a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref @@ -1,7 +1,8 @@ -0 0 -0.05 0 -0.1 1.54425 -0.15 1.10218e-17 -0.2 -1.54425 -0.25 0 -0.3 0 + 0.000000 0.000000000000e+00 + 0.050000 0.000000000000e+00 + 0.100000 1.544251129818e+00 + 0.150000 1.102182119233e-17 + 0.200000 -1.544251129818e+00 + 0.250000 0.000000000000e+00 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref index f7029d49cfb..b4e80df658c 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref @@ -1,7 +1,8 @@ -0 0.0266495 -0.05 0.0364109 -0.1 0.0398002 -0.15 0.0348135 -0.2 0.0242465 -0.25 0.0132775 -0.3 0.00555872 + 0.000000 2.664950674378e-02 + 0.050000 3.641086506598e-02 + 0.100000 3.980016661112e-02 + 0.150000 3.481348408339e-02 + 0.200000 2.424650679607e-02 + 0.250000 1.327751403966e-02 + 0.300000 5.558724561591e-03 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref index c9c61917323..5ff61e446a4 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref @@ -1,7 +1,8 @@ -0 -0.00732139 -0.05 -0.013089 -0.1 -0.0135533 -0.15 -0.00763671 -0.2 -0.00015746 -0.25 0.00320401 -0.3 0.00177906 + 0.000000 -7.321387155782e-03 + 0.050000 -1.308904129194e-02 + 0.100000 -1.355332758075e-02 + 0.150000 -7.636709282321e-03 + 0.200000 -1.574600767951e-04 + 0.250000 3.204008572905e-03 + 0.300000 1.779061764225e-03 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref index d5fd25849f4..6b9a47dbbd0 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref @@ -1,7 +1,8 @@ -0 0 -0.05 0 -0.1 0.031042 -0.15 0.0490033 -0.2 -0.0446739 -0.25 0 -0.3 0 + 0.000000 0.000000000000e+00 + 0.050000 0.000000000000e+00 + 0.100000 3.104196091355e-02 + 0.150000 4.900332889206e-02 + 0.200000 -4.467390243688e-02 + 0.250000 0.000000000000e+00 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref index 989ccf3f7e1..291ee866a92 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref @@ -1,7 +1,8 @@ -0 0 -0.05 0.0224967 -0.1 0.0190782 -0.15 0.014461 -0.2 0.00446755 -0.25 0 -0.3 -0 + 0.000000 0.000000000000e+00 + 0.050000 2.249672326559e-02 + 0.100000 1.907823022520e-02 + 0.150000 1.446098181495e-02 + 0.200000 4.467548366745e-03 + 0.250000 0.000000000000e+00 + 0.300000 -0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref index 47ba81f3d5b..36556a33159 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref @@ -1,7 +1,8 @@ -0 0.00381989 -0.05 -0.00302697 -0.1 -0.0122172 -0.15 -0.0213628 -0.2 -0.0276318 -0.25 -0.0290339 -0.3 -0.0253607 + 0.000000 3.819888601740e-03 + 0.050000 -3.026970038439e-03 + 0.100000 -1.221724563800e-02 + 0.150000 -2.136280624345e-02 + 0.200000 -2.763182982009e-02 + 0.250000 -2.903394376196e-02 + 0.300000 -2.536069172914e-02 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref index bc3b37422d8..e5bd7ef0add 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref @@ -1,7 +1,8 @@ -0 0.01 -0.05 0.01 -0.1 0.01 -0.15 0 -0.2 0 -0.25 0 -0.3 0 + 0.000000 1.000000000000e-02 + 0.050000 1.000000000000e-02 + 0.100000 1.000000000000e-02 + 0.150000 0.000000000000e+00 + 0.200000 0.000000000000e+00 + 0.250000 0.000000000000e+00 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref index d29952a4c9a..f74e2e88f63 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref @@ -1,7 +1,8 @@ -0 0.000142823 -0.05 0.00477471 -0.1 0.00864368 -0.15 0.00584356 -0.2 0.00103323 -0.25 -9.65006e-05 -0.3 -0.000733575 + 0.000000 1.428234415606e-04 + 0.050000 4.774713083834e-03 + 0.100000 8.643680264292e-03 + 0.150000 5.843555737827e-03 + 0.200000 1.033233769422e-03 + 0.250000 -9.650057692743e-05 + 0.300000 -7.335746621943e-04 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref index a316aa5db4d..4595393a176 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref @@ -1,7 +1,8 @@ -0 0 -0.05 -0 -0.1 -0 -0.15 0.0327856 -0.2 -0.0351033 -0.25 -0.0452257 -0.3 -0 + 0.000000 0.000000000000e+00 + 0.050000 -0.000000000000e+00 + 0.100000 -0.000000000000e+00 + 0.150000 3.278561314636e-02 + 0.200000 -3.510330247561e-02 + 0.250000 -4.522572821072e-02 + 0.300000 -0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref index 558f86c14b7..ea5ca90be1a 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref @@ -1,7 +1,8 @@ -0 -0 -0.05 -0.00981702 -0.1 -0.0195318 -0.15 -0.0162167 -0.2 -0.0102341 -0.25 -0.00128406 -0.3 0 + 0.000000 -0.000000000000e+00 + 0.050000 -9.817021455967e-03 + 0.100000 -1.953175510842e-02 + 0.150000 -1.621667428502e-02 + 0.200000 -1.023410056816e-02 + 0.250000 -1.284055993631e-03 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref index a58d4028d6c..dafaf3e4866 100644 --- a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref @@ -1,7 +1,8 @@ -0 -0.012 -0.05 -0.012 -0.1 -0.012 -0.15 -0.012 -0.2 -0.012 -0.25 0 -0.3 0 + 0.000000 -1.200000000000e-02 + 0.050000 -1.200000000000e-02 + 0.100000 -1.200000000000e-02 + 0.150000 -1.200000000000e-02 + 0.200000 -1.200000000000e-02 + 0.250000 0.000000000000e+00 + 0.300000 0.000000000000e+00 + 0.350000 0.000000000000e+00 From 4ea340772ffafca058aed3ce94311052c59fab28 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Sat, 17 Oct 2026 22:39:33 +0800 Subject: [PATCH 02/33] Test: Add unit tests and SOC integration tests for nspin=4 PW RT-TDDFT --- .../test/test_hsolver_pw_tddft.cpp | 124 +++++++++- .../module_pwdft/test/CMakeLists.txt | 41 ++++ .../module_pwdft/test/td_test_utils.h | 52 ++++ .../module_pwdft/test/test_td_obs_pw.cpp | 230 ++++++++++++++++++ .../module_pwdft/test/test_td_spin_pw.cpp | 116 +++++++++ .../module_pwdft/test/test_td_vel_pw.cpp | 73 ++++++ tests/05_rtTDDFT/29_PW_soc_len_TDDFT/INPUT | 61 +++++ tests/05_rtTDDFT/29_PW_soc_len_TDDFT/KPT | 5 + tests/05_rtTDDFT/29_PW_soc_len_TDDFT/README | 1 + tests/05_rtTDDFT/29_PW_soc_len_TDDFT/STRU | 21 ++ .../29_PW_soc_len_TDDFT/current_tot.txt.ref | 6 + .../29_PW_soc_len_TDDFT/mag_atom.txt.ref | 14 ++ .../29_PW_soc_len_TDDFT/mag_tot.txt.ref | 8 + .../05_rtTDDFT/29_PW_soc_len_TDDFT/result.ref | 9 + .../29_PW_soc_len_TDDFT/spincur_tot.txt.ref | 8 + .../29_PW_soc_len_TDDFT/torque_atom.txt.ref | 15 ++ .../29_PW_soc_len_TDDFT/torque_tot.txt.ref | 8 + tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT | 62 +++++ tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/KPT | 5 + .../05_rtTDDFT/30_PW_soc_onsite_TDDFT/README | 1 + tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU | 21 ++ .../current_tot.txt.ref | 6 + .../30_PW_soc_onsite_TDDFT/mag_atom.txt.ref | 16 ++ .../30_PW_soc_onsite_TDDFT/mag_tot.txt.ref | 10 + .../30_PW_soc_onsite_TDDFT/result.ref | 9 + .../spincur_tot.txt.ref | 8 + .../torque_atom.txt.ref | 15 ++ .../30_PW_soc_onsite_TDDFT/torque_tot.txt.ref | 8 + tests/05_rtTDDFT/CASES_CPU.txt | 2 + tests/05_rtTDDFT/CASES_GPU.txt | 2 + .../integrate/validation_tools/props_tddft.sh | 12 +- 31 files changed, 966 insertions(+), 3 deletions(-) create mode 100644 source/source_pw/module_pwdft/test/td_test_utils.h create mode 100644 source/source_pw/module_pwdft/test/test_td_obs_pw.cpp create mode 100644 source/source_pw/module_pwdft/test/test_td_spin_pw.cpp create mode 100644 source/source_pw/module_pwdft/test/test_td_vel_pw.cpp create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/README create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_atom.txt.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_tot.txt.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/spincur_tot.txt.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_atom.txt.ref create mode 100644 tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_tot.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref diff --git a/source/source_hsolver/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/test/test_hsolver_pw_tddft.cpp index 8500e142329..857dd8e77e7 100644 --- a/source/source_hsolver/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/test/test_hsolver_pw_tddft.cpp @@ -1,6 +1,7 @@ #include "source_base/parallel_global.h" #include "source_hsolver/hsolver_pw_tddft.h" #include "source_hsolver/test/linear_test_utils.h" +#include "source_io/module_parameter/parameter.h" #include #include @@ -10,6 +11,22 @@ #include #include +class TestParameters +{ + public: + TestParameters() : saved_spin_(PARAM.inp.nspin) + { + PARAM.input.nspin = 4; + } + ~TestParameters() + { + PARAM.input.nspin = saved_spin_; + } + + private: + int saved_spin_; +}; + namespace { using Device = base_device::DEVICE_CPU; @@ -20,6 +37,7 @@ class DenseHamiltonian final : public hsolver::HSOperator public: std::vector> matrices; std::vector dimensions; + bool spinor = false; explicit DenseHamiltonian(const std::vector& sizes) : dimensions(sizes) { @@ -38,12 +56,14 @@ class DenseHamiltonian final : public hsolver::HSOperator const std::vector& h = matrices.at(current_k_); for (int band = 0; band < nvec; ++band) { + std::fill_n(y + band * ld, ld, T(0)); for (int i = 0; i < n; ++i) { - y[band * ld + i] = T(0); + const int row = spinor ? (i / (n / 2)) * (ld / 2) + i % (n / 2) : i; for (int j = 0; j < n; ++j) { - y[band * ld + i] += h[j * n + i] * x[band * ld + j]; + const int col = spinor ? (j / (n / 2)) * (ld / 2) + j % (n / 2) : j; + y[band * ld + row] += h[j * n + i] * x[band * ld + col]; } } } @@ -147,6 +167,106 @@ class PWTDDFTTest : public testing::Test using Precisions = testing::Types; TYPED_TEST_SUITE(PWTDDFTTest, Precisions); +TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) +{ + using T = std::complex; + using Wide = linear_test::Complex; + TestParameters spinor_parameters; + const bool single = std::is_same::value; + const double tolerance = single ? 1e-4 : 1e-10; + const std::vector sizes{5, 3}; + const int npwx = 5; + const int bands = 3; + const double dt = 0.27; + ModulePW::PW_Basis_K basis; + initialize_basis(sizes, &basis); + const std::vector dimensions{2 * sizes[0], 2 * sizes[1]}; + DenseHamiltonian op(dimensions); + op.spinor = true; + hsolver::PWLinearOptions options; + options.npol = 2; + options.global_k_indices = {0, 1}; + options.linear.method = hsolver::LinearMethod::bicgstab; + options.linear.tolerance = single ? 2e-6 : 1e-13; + options.linear.max_iterations = 100; + options.kinetic_enabled = true; +#ifdef __MPI + const hsolver::diag_comm_info comm(MPI_COMM_SELF, 0, 1); +#else + const hsolver::diag_comm_info comm(0, 1); +#endif + std::ostringstream log; + hsolver::HSolverPWTDDFT solver(basis, options, comm, log); + const int ld = 2 * npwx; + psi::Psi previous(2, bands, ld, sizes, true); + std::vector> packed(2); + const T padding(std::numeric_limits::quiet_NaN()); + for (int ik = 0; ik < 2; ++ik) + { + previous.fix_k(ik); + const int n = dimensions[ik]; + packed[ik].resize(n * bands); + std::fill_n(previous.get_pointer(), ld * bands, padding); + for (int band = 0; band < bands; ++band) + { + for (int i = 0; i < n; ++i) + { + const double phase = 2 * std::acos(-1.0) * i * band / n; + const T value = T(std::polar(1.0 / std::sqrt(n), phase)); + const int row = (i / sizes[ik]) * npwx + i % sizes[ik]; + previous(band, row) = value; + packed[ik][band * n + i] = value; + } + } + } + psi::Psi current(previous); + const ModuleBase::Vector3 shift(0.13, -0.07, 0.02); + solver.solve(op, previous, ¤t, dt, shift, 1, 1, false); + for (int ik = 0; ik < 2; ++ik) + { + current.fix_k(ik); + const int n = dimensions[ik]; + const auto reference = dense_cn(op.matrices[ik], packed[ik], n, bands, dt); + const auto eij = solver.cal_eij(op, current, ik); + for (int band = 0; band < bands; ++band) + { + for (int spin = 0; spin < 2; ++spin) + { + for (int g = 0; g < npwx; ++g) + { + const int row = spin * npwx + g; + if (g < sizes[ik]) + { + const Wide expected = reference[band * n + spin * sizes[ik] + g]; + EXPECT_NEAR(std::abs(Wide(current(band, row)) - expected), 0.0, tolerance); + } + else + { + EXPECT_EQ(current(band, row), T(0)); + } + } + } + for (int other = 0; other < bands; ++other) + { + Wide overlap(0); + Wide expectation(0); + for (int i = 0; i < n; ++i) + { + const int row = (i / sizes[ik]) * npwx + i % sizes[ik]; + overlap += std::conj(Wide(current(other, row))) * Wide(current(band, row)); + for (int j = 0; j < n; ++j) + { + expectation += std::conj(reference[other * n + i]) * Wide(op.matrices[ik][j * n + i]) * reference[band * n + j]; + } + } + const Wide identity = band == other ? Wide(1) : Wide(0); + EXPECT_NEAR(std::abs(overlap - identity), 0.0, tolerance); + EXPECT_NEAR(std::abs(eij[band * bands + other] - expectation), 0.0, tolerance); + } + } + } +} + TYPED_TEST(PWTDDFTTest, ComplexEijExcludesPadding) { using T = std::complex; diff --git a/source/source_pw/module_pwdft/test/CMakeLists.txt b/source/source_pw/module_pwdft/test/CMakeLists.txt index 91e0ce07bcc..fa570403475 100644 --- a/source/source_pw/module_pwdft/test/CMakeLists.txt +++ b/source/source_pw/module_pwdft/test/CMakeLists.txt @@ -3,6 +3,47 @@ abacus_disable_feature_definitions(__CUDA) abacus_disable_feature_definitions(__ROCM) abacus_disable_feature_definitions(__EXX) +AddTest( + TARGET MODULE_PW_td_observables + LIBS parameter base device container psi planewave nao_smoothgen symmetry + SOURCES + test_td_obs_pw.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp + ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp + ../op_pw_vel.cpp ../projector_gradient.cpp ../velocity_workspace.cpp ../nonlocal_workspace.cpp + ../vnl_pw.cpp ../vnl_pw_init_vnl.cpp ../vnl_pw_getvnl.cpp ../vnl_pw_deeq.cpp + ../vnl_pw_grad.cpp ../vnl_pw_table.cpp ../vnl_pw_qrad.cpp + ../stru_fac.cpp ../stru_fac_k.cpp ../soc.cpp ../nonlocal_maths.cpp + ../../module_proj/onsite_proj.cpp ../../module_proj/onsite_proj_init.cpp + ../../module_proj/onsite_proj_overlap.cpp ../../module_proj/onsite_proj_tools.cpp + ../../module_proj/onsite_proj_tools_becp.cpp ../../module_proj/radial_proj.cpp + ../../module_proj/onsite_proj_print.cpp + ../../../source_basis/module_ao/orb_gaunt_table.cpp + ../../../source_cell/klist.cpp ../../../source_cell/reciprocal_grid.cpp + ../../../source_cell/klist_io.cpp + ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp + ../../../source_cell/atom_spec.cpp ../../../source_cell/atom_pseudo.cpp ../../../source_cell/pseudo.cpp + ../../../source_cell/magnetism.cpp ../../../source_cell/sep.cpp ../../../source_cell/sep_cell.cpp + ../../../source_cell/read_pp.cpp ../../../source_cell/read_pp_complete.cpp + ../../../source_cell/read_pp_upf100.cpp ../../../source_cell/read_pp_upf201.cpp + ../../../source_cell/read_pp_vwr.cpp ../../../source_cell/read_pp_blps.cpp + ../../../source_estate/elecstate.cpp ../../../source_estate/module_charge/charge.cpp + ../../../source_estate/module_pot/potential_new.cpp + ../../../source_hamilt/module_xc/xc_functional.cpp +) +target_compile_definitions(MODULE_PW_td_observables PRIVATE TD_TEST_PP_DIR="${PROJECT_SOURCE_DIR}/tests/PP_ORB") +# Keep real PW initialization and observable implementations without linking the SCF driver. +if(MSVC) + target_compile_options(MODULE_PW_td_observables PRIVATE /Gy /Gw) + target_link_options(MODULE_PW_td_observables PRIVATE /OPT:REF) +else() + target_compile_options(MODULE_PW_td_observables PRIVATE -ffunction-sections -fdata-sections) + if(APPLE) + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,-dead_strip) + else() + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,--gc-sections) + endif() +endif() + addtest( TARGET MODULE_PW_pwdft_soc diff --git a/source/source_pw/module_pwdft/test/td_test_utils.h b/source/source_pw/module_pwdft/test/td_test_utils.h new file mode 100644 index 00000000000..3b232ccf3d9 --- /dev/null +++ b/source/source_pw/module_pwdft/test/td_test_utils.h @@ -0,0 +1,52 @@ +#ifndef TD_TEST_UTILS_H +#define TD_TEST_UTILS_H + +#include "source_pw/module_pwdft/td_spin_pw.h" +#include "source_pw/module_pwdft/td_vel_pw.h" + +#include + +/** @brief Restore the global input used by legacy PW initialization. */ +class TestParameters +{ + public: + TestParameters(); + ~TestParameters(); + + private: + Input_para input_; + System_para system_; +}; + +namespace td_test +{ +using Complex = std::complex; +using Device = base_device::DEVICE_CPU; +using Vector = std::vector; + +/** @brief Small real PW basis and relativistic projectors, without an SCF calculation. */ +class Observables : public testing::Test +{ + protected: + TestParameters parameters_; + UnitCell cell_; + ModulePW::PW_Basis_K basis_; + K_Vectors kv_; + Structure_Factor sf_; + pseudopot_cell_vnl pp_; + Charge charge_; + elecstate::ElecState state_; + std::unique_ptr> psi_; + + void SetUp() override; + void TearDown() override; + Vector projectors(const ModuleBase::Vector3& a); + Vector nonlocal(const Vector& beta, const Vector& x, int atom) const; + Vector hamiltonian(const ModuleBase::Vector3& a, const Vector& x); +}; + +/** @brief Apply a Pauli matrix to a packed two-component vector. */ +Vector pauli(const Vector& x, int axis); +Complex inner(const Vector& x, const Vector& y); +} // namespace td_test +#endif diff --git a/source/source_pw/module_pwdft/test/test_td_obs_pw.cpp b/source/source_pw/module_pwdft/test/test_td_obs_pw.cpp new file mode 100644 index 00000000000..86aa7c77101 --- /dev/null +++ b/source/source_pw/module_pwdft/test/test_td_obs_pw.cpp @@ -0,0 +1,230 @@ +#include "source_base/parallel_global.h" +#include "source_cell/read_pp.h" +#include "source_pw/module_pwdft/test/td_test_utils.h" + +#include + +TestParameters::TestParameters() : input_(PARAM.inp), system_(PARAM.globalv) +{ + PARAM.input.nspin = 4; + PARAM.input.noncolin = true; + PARAM.input.lspinorb = true; + PARAM.input.device = "cpu"; + PARAM.input.basis_type = "pw"; + PARAM.input.precision = "double"; + PARAM.sys.nqx = 600; + PARAM.sys.nqxq = 600; + PARAM.sys.has_double_data = true; + PARAM.sys.has_float_data = false; +} + +TestParameters::~TestParameters() +{ + PARAM.input = input_; + PARAM.sys = system_; +} + +namespace td_test +{ +void Observables::SetUp() +{ + cell_.ntype = 1; + cell_.nat = 2; + cell_.atoms = new Atom[1]; + cell_.set_atom_flag = true; + cell_.atoms[0].na = 2; + cell_.atoms[0].label = "Fe"; + cell_.atoms[0].taud = {{0.11, 0.21, 0.31}, {0.59, 0.43, 0.77}}; + cell_.lat0 = 6.0; + cell_.latvec = ModuleBase::Matrix3(1, 0, 0, 0.4, 0.8, 0, 0.1, 0.2, 1); + cell_.G = cell_.latvec.Inverse().Transpose(); + cell_.omega = std::pow(cell_.lat0, 3) * cell_.latvec.Det(); + cell_.tpiba = ModuleBase::TWO_PI / cell_.lat0; + cell_.tpiba2 = cell_.tpiba * cell_.tpiba; + for (const auto& position: cell_.atoms[0].taud) + { + cell_.atoms[0].tau.push_back(position * cell_.latvec); + } + cell_.set_iat2itia(); + cell_.set_iat2iwt(2); + + Pseudopot_upf reader; + std::string type = "upf201"; + const std::string filename = std::string(TD_TEST_PP_DIR) + "/Fe.upf"; + ASSERT_EQ(reader.init_pseudo_reader(filename, type, cell_.atoms[0].ncpp), 0); + Atom_pseudo& atom_pp = cell_.atoms[0].ncpp; + atom_pp.nh = 0; + for (const int l: atom_pp.lll) + { + atom_pp.nh += 2 * l + 1; + } + +#ifdef __MPI + basis_.initmpi(1, 0, POOL_WORLD); +#endif + basis_.initgrids(cell_.lat0, cell_.latvec, 12.0); + const ModuleBase::Vector3 k(0.17, 0.11, 0.07); + basis_.initparameters(false, 3.0, 1, &k); + basis_.setuptransform(); + basis_.collect_local_pw(); + kv_.set_nkstot(1); + kv_.set_nks(1); + kv_.isk = {0}; + kv_.ik2iktot = {0}; + sf_.set(&basis_, 0); + Parallel_Grid grid; + sf_.setup(&cell_, grid, &basis_, false); + pp_.init(cell_, &sf_, &basis_, true); + pp_.init_vnl(cell_, &basis_); + charge_.set_rhopw(&basis_); + charge_.allocate(4, false, false, 0); + state_.charge = &charge_; + state_.pot = new elecstate::Potential; + state_.pot->get_eff_v().create(4, basis_.nrxx); + pp_.cal_effective_D(state_.pot->get_eff_v(), &basis_, cell_); + state_.wg.create(1, 2); + state_.wg(0, 0) = 0.8; + state_.wg(0, 1) = 0.3; + const int n = 2 * basis_.npwk_max; + const std::vector sizes{basis_.npwk[0]}; + psi_.reset(new psi::Psi(1, 2, n, sizes, true)); + for (int band = 0; band < 2; ++band) + { + double norm = 0; + for (int i = 0; i < n; ++i) + { + const Complex value(std::sin(0.37 * (i + 1) * (band + 1)), std::cos(0.23 * (i + 2) * (band + 2))); + (*psi_)(band, i) = value; + norm += std::norm(value); + } + for (int i = 0; i < n; ++i) + { + (*psi_)(band, i) /= std::sqrt(norm); + } + } +} + +void Observables::TearDown() +{ + pp_.release_memory(); +} + +Vector Observables::projectors(const ModuleBase::Vector3& a) +{ + Vector beta(pp_.nkb * basis_.npwk_max); + pp_.getvnl(nullptr, cell_, 0, a, beta.data()); + return beta; +} + +Vector Observables::nonlocal(const Vector& beta, const Vector& x, int atom) const +{ + // Explicit beta D beta-adjoint action in Hartree units, independent of the production contraction kernels. + const int npw = basis_.npwk[0]; + const int nh = cell_.atoms[0].ncpp.nh; + Vector b(2 * nh, 0.0); + for (int s = 0; s < 2; ++s) + { + for (int p = 0; p < nh; ++p) + { + for (int g = 0; g < npw; ++g) + { + b[s * nh + p] += std::conj(beta[(atom * nh + p) * npw + g]) * x[s * npw + g]; + } + } + } + Vector y(x.size(), 0.0); + for (int s = 0; s < 2; ++s) + { + for (int t = 0; t < 2; ++t) + { + for (int p = 0; p < nh; ++p) + { + for (int q = 0; q < nh; ++q) + { + const Complex db = 0.5 * pp_.deeq_nc(2 * s + t, atom, p, q) * b[t * nh + q]; + for (int g = 0; g < npw; ++g) + { + y[s * npw + g] += beta[(atom * nh + p) * npw + g] * db; + } + } + } + } + } + return y; +} + +Vector Observables::hamiltonian(const ModuleBase::Vector3& a, const Vector& x) +{ + const Vector beta = projectors(a); + Vector y(x.size(), 0.0); + for (int atom = 0; atom < cell_.nat; ++atom) + { + const Vector term = nonlocal(beta, x, atom); + for (size_t i = 0; i < x.size(); ++i) + { + y[i] += term[i]; + } + } + const int npw = basis_.npwk[0]; + for (int g = 0; g < npw; ++g) + { + const auto q = basis_.getgpluskcar(0, g) * cell_.tpiba + a; + for (int s = 0; s < 2; ++s) + { + y[s * npw + g] += 0.5 * q.norm2() * x[s * npw + g]; + } + } + return y; +} + +Vector pauli(const Vector& x, int axis) +{ + const Complex matrices[3][4] = {{0.0, 1.0, 1.0, 0.0}, {0.0, Complex(0, -1), Complex(0, 1), 0.0}, {1.0, 0.0, 0.0, -1.0}}; + const int npw = x.size() / 2; + Vector y(x.size(), 0.0); + for (int s = 0; s < 2; ++s) + { + for (int t = 0; t < 2; ++t) + { + for (int g = 0; g < npw; ++g) + { + y[s * npw + g] += matrices[axis][2 * s + t] * x[t * npw + g]; + } + } + } + return y; +} + +Complex inner(const Vector& x, const Vector& y) +{ + Complex value(0); + for (size_t i = 0; i < x.size(); ++i) + { + value += std::conj(x[i]) * y[i]; + } + return value; +} +} // namespace td_test + +int main(int argc, char** argv) +{ + int nproc = 1; + int nthread = 1; + int rank = 0; + Parallel_Global::read_pal_param(argc, argv, nproc, nthread, rank); +#ifdef __MPI + POOL_WORLD = MPI_COMM_SELF; +#endif + testing::InitGoogleTest(&argc, argv); + const int result = RUN_ALL_TESTS(); +#ifdef __MPI + POOL_WORLD = MPI_COMM_NULL; + KP_WORLD = MPI_COMM_NULL; + INT_BGROUP = MPI_COMM_NULL; + BP_WORLD = MPI_COMM_NULL; + GRID_WORLD = MPI_COMM_NULL; + DIAG_WORLD = MPI_COMM_NULL; + Parallel_Global::finalize_mpi(); +#endif + return result; +} diff --git a/source/source_pw/module_pwdft/test/test_td_spin_pw.cpp b/source/source_pw/module_pwdft/test/test_td_spin_pw.cpp new file mode 100644 index 00000000000..f25c5762c78 --- /dev/null +++ b/source/source_pw/module_pwdft/test/test_td_spin_pw.cpp @@ -0,0 +1,116 @@ +#include "source_pw/module_pwdft/test/td_test_utils.h" + +#include +#include + +namespace td_test +{ +TEST_F(Observables, SOCTorqueMatchesCommutator) +{ + const ModuleBase::Vector3 a(0.13, -0.07, 0.09); + pw::TDSpinOptsPW options; + options.torque = true; + pw::TDSpinPW observer; + observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, options); + const auto actual = observer.calculate(*psi_, state_, a); + const Vector beta = projectors(a); + const int n = psi_->get_nbasis(); + double signal = 0; + for (int axis = 0; axis < 3; ++axis) + { + double total = 0; + for (int atom = 0; atom < cell_.nat; ++atom) + { + double expected = 0; + for (int band = 0; band < psi_->get_nbands(); ++band) + { + const Complex* data = psi_->get_pointer() + band * n; + const Vector x(data, data + n); + const Vector sx = pauli(x, axis); + const Vector hx = nonlocal(beta, x, atom); + const Vector hsx = nonlocal(beta, sx, atom); + const Vector shx = pauli(hx, axis); + const Complex commutator = inner(x, hsx) - inner(x, shx); + expected += state_.wg(0, band) * std::real(Complex(0, 0.5) * commutator); + } + EXPECT_NEAR(actual.atom_soc[3 * atom + axis], expected, 1e-10); + total += expected; + signal += std::abs(expected); + } + EXPECT_NEAR(actual.soc[axis], total, 1e-10); + } + EXPECT_GT(signal, 1e-8); +} + +TEST_F(Observables, DensityMomentsAndPeriodicVoronoi) +{ + const auto labels = pw::td_voronoi_pw(cell_, basis_); + std::vector atom_integrals(cell_.nat, 0.0); + const double dv = cell_.omega / basis_.nxyz; + for (int ir = 0; ir < basis_.nrxx; ++ir) + { + const int ix = ir / (basis_.ny * basis_.nplane); + const int iy = ir / basis_.nplane % basis_.ny; + const int iz = ir % basis_.nplane + basis_.startz_current; + const ModuleBase::Vector3 point(double(ix) / basis_.nx, double(iy) / basis_.ny, double(iz) / basis_.nz); + double best = std::numeric_limits::infinity(); + int nearest = -1; + // Independent exhaustive periodic-image reference for this moderately skew cell. + for (int atom = 0; atom < cell_.nat; ++atom) + { + for (int i = -3; i <= 3; ++i) + { + for (int j = -3; j <= 3; ++j) + { + for (int k = -3; k <= 3; ++k) + { + const ModuleBase::Vector3 image(i, j, k); + const auto delta = (point - cell_.atoms[0].taud[atom] + image) * cell_.latvec; + const double distance = delta.norm2(); + if (distance < best - 1e-12) + { + best = distance; + nearest = atom; + } + } + } + } + } + ASSERT_EQ(labels[ir], nearest); + const double f = 1.0 + 0.01 * ir; + atom_integrals[nearest] += f * dv; + charge_.rho[0][ir] = 2 * f; + charge_.rho[1][ir] = 0.2 * f; + charge_.rho[2][ir] = -0.3 * f; + charge_.rho[3][ir] = 0.5 * f; + // Potential storage is in Ry; the reference spin field is (0.4, 0.1, -0.2) Ha. + state_.pot->get_eff_v()(1, ir) = 0.8; + state_.pot->get_eff_v()(2, ir) = 0.2; + state_.pot->get_eff_v()(3, ir) = -0.4; + } + pw::TDSpinOptsPW options; + options.method = "voronoi"; + options.torque = true; + pw::TDSpinPW observer; + observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, options); + const ModuleBase::Vector3 zero(0, 0, 0); + const auto actual = observer.calculate(*psi_, state_, zero); + const double integral = cell_.omega * (1.0 + 0.005 * (basis_.nxyz - 1)); + EXPECT_NEAR(actual.electrons, 2 * integral, 1e-10); + EXPECT_NEAR(actual.mag_abs, std::sqrt(0.38) * integral, 1e-10); + const double m[3] = {0.2, -0.3, 0.5}; + const double torque[3] = {-0.01, -0.24, -0.14}; + for (int axis = 0; axis < 3; ++axis) + { + EXPECT_NEAR(actual.mag[axis], m[axis] * integral, 1e-10); + EXPECT_NEAR(actual.xc[axis], torque[axis] * integral, 1e-10); + double sum = 0; + for (int atom = 0; atom < cell_.nat; ++atom) + { + EXPECT_NEAR(actual.atom_mag[3 * atom + axis], m[axis] * atom_integrals[atom], 1e-10); + sum += actual.atom_mag[3 * atom + axis]; + } + EXPECT_NEAR(sum, actual.mag[axis], 1e-10); + } +} +} // namespace td_test diff --git a/source/source_pw/module_pwdft/test/test_td_vel_pw.cpp b/source/source_pw/module_pwdft/test/test_td_vel_pw.cpp new file mode 100644 index 00000000000..2a79fb78db3 --- /dev/null +++ b/source/source_pw/module_pwdft/test/test_td_vel_pw.cpp @@ -0,0 +1,73 @@ +#include "source_pw/module_pwdft/test/td_test_utils.h" + +#include + +namespace td_test +{ +TEST_F(Observables, CurrentsMatchPauliAndHamiltonianDerivative) +{ + const ModuleBase::Vector3 a(0.13, -0.07, 0.09); + pw::TDVelPW observer; + observer.initialize(cell_, basis_, kv_, pp_); + pw::TDVelRequestPW request; + request.charge_total = true; + request.charge_per_k = true; + request.spin_current = true; + const auto actual = observer.calculate(*psi_, state_, a, request); + hamilt::Velocity velocity(&basis_, kv_.isk.data(), &pp_, &cell_, true, nullptr, 0, 0); + velocity.init(0, a); + const int n = psi_->get_nbasis(); + std::array charge{}; + std::array spin{}; + const double epsilon = 1e-4; + for (int band = 0; band < psi_->get_nbands(); ++band) + { + const Complex* data = psi_->get_pointer() + band * n; + const Vector x(data, data + n); + Vector vx(3 * n); + velocity.act(psi_.get(), 2, x.data(), vx.data(), false); + for (int direction = 0; direction < 3; ++direction) + { + const Vector component(vx.begin() + direction * n, vx.begin() + (direction + 1) * n); + auto plus = a; + auto minus = a; + plus[direction] += epsilon; + minus[direction] -= epsilon; + const Vector hp = hamiltonian(plus, x); + const Vector hm = hamiltonian(minus, x); + double error = 0; + double norm = 0; + for (int i = 0; i < n; ++i) + { + const Complex derivative = (hp[i] - hm[i]) / (2 * epsilon); + error += std::norm(component[i] - derivative); + norm += std::norm(derivative); + } + EXPECT_LE(std::sqrt(error), 1e-10 + 1e-6 * std::sqrt(norm)); + charge[direction] -= state_.wg(0, band) * inner(x, component).real() / cell_.omega; + for (int axis = 0; axis < 3; ++axis) + { + const Vector sx = pauli(x, axis); + Vector vsx(3 * n); + velocity.act(psi_.get(), 2, sx.data(), vsx.data(), false); + const Vector v_sigma(vsx.begin() + direction * n, vsx.begin() + (direction + 1) * n); + const Vector sigma_v = pauli(component, axis); + const Complex anticommutator = inner(x, v_sigma) + inner(x, sigma_v); + spin[3 * direction + axis] += 0.25 * state_.wg(0, band) * anticommutator.real() / cell_.omega; + } + } + } + double signal = 0; + for (int i = 0; i < 3; ++i) + { + EXPECT_NEAR(actual.total[i], charge[i], 1e-10); + EXPECT_NEAR(actual.per_k[i], charge[i], 1e-10); + } + for (int i = 0; i < 9; ++i) + { + EXPECT_NEAR(actual.spin[i], spin[i], 1e-10); + signal += std::abs(spin[i]); + } + EXPECT_GT(signal, 1e-8); +} +} // namespace td_test diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/INPUT b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/INPUT new file mode 100644 index 00000000000..fa3b0adf05a --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/INPUT @@ -0,0 +1,61 @@ +INPUT_PARAMETERS +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +dft_functional PBE +gga_grad 1 +ecutwfc 20 +ecutrho 80 +nbands 12 +kpar 2 +nspin 4 +noncolin 1 +lspinorb 1 +symmetry -1 +init_wfc atomic+random +pw_seed 1 +ks_solver cg +pw_diag_thr 1e-13 +scf_thr 1e-12 +scf_nmax 150 +mixing_type broyden +mixing_beta 0.5 +mixing_beta_mag 0.5 +mixing_gg0 0 +mixing_gg0_mag 0 +smearing_method fd +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.002 +md_nstep 1 +estep_per_md 5 +cal_force 0 +cal_stress 0 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 1.5707963267948966 +td_gauss_sigma 0.04 +td_gauss_t0 0 +td_gauss_amp 0.1 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-13 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +td_out_torque 1 +td_out_spincur 1 +out_current 1 +out_level ie +td_stype 0 +td_out_mag voronoi diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/KPT b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/README b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/README new file mode 100644 index 00000000000..5cf04d9fdb4 --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/README @@ -0,0 +1 @@ +Test nspin=4 PW RT-TDDFT with SOC in the length gauge using Voronoi magnetic moments. diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/STRU b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/STRU new file mode 100644 index 00000000000..139b8c551ed --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/STRU @@ -0,0 +1,21 @@ +ATOMIC_SPECIES +Si 28 Si_dojo_soc.upf upf201 + +NUMERICAL_ORBITAL +Si_dojo_6au.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..e3f89d80b4e --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/current_tot.txt.ref @@ -0,0 +1,6 @@ + 1 2.289540486381e-14 2.466371358853e-13 -4.322199982921e-13 + 2 1.562262789043e-08 -2.583938069128e-11 3.356100270946e-11 + 3 6.239644885742e-08 -1.032314740651e-10 1.348687369519e-10 + 4 1.399851370645e-07 -2.294087173190e-10 3.010488410832e-10 + 5 2.477204874654e-07 -4.005758998224e-10 5.279601386958e-10 + 6 3.844843676686e-07 -6.122000986244e-10 8.097312782081e-10 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_atom.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_atom.txt.ref new file mode 100644 index 00000000000..f5623d5b304 --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_atom.txt.ref @@ -0,0 +1,14 @@ +# method=voronoi; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +#step time_fs atom Mx My Mz + 1 0.000000 1 9.688147526310e-11 3.770607647993e-11 2.128422814803e-09 + 1 0.000000 2 -9.604735803907e-11 -3.839591778316e-11 -2.128529273649e-09 + 2 0.002000 1 8.539427389207e-11 3.884726156062e-11 2.137434253980e-09 + 2 0.002000 2 -9.445069996995e-11 -3.969803210671e-11 -2.137047195265e-09 + 3 0.004000 1 6.047128818960e-11 3.909235016855e-11 2.147458676175e-09 + 3 0.004000 2 -7.921511486490e-11 -4.011686659959e-11 -2.146556076116e-09 + 4 0.006000 1 2.495133773993e-11 3.819630426244e-11 2.159145272901e-09 + 4 0.006000 2 -5.282085091925e-11 -3.942860167351e-11 -2.157666045077e-09 + 5 0.008000 1 -1.508797726460e-11 3.623372709274e-11 2.172333075148e-09 + 5 0.008000 2 -2.079044574547e-11 -3.774360104556e-11 -2.170151148038e-09 + 6 0.010000 1 -4.949855664212e-11 3.435012111971e-11 2.186333023229e-09 + 6 0.010000 2 7.433319529666e-12 -3.625400515907e-11 -2.183238486660e-09 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_tot.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_tot.txt.ref new file mode 100644 index 00000000000..4ee8773f48c --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/mag_tot.txt.ref @@ -0,0 +1,8 @@ +# method=voronoi; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +#step time_fs Mx My Mz M_norm M_abs_integral N_elec + 1 0.000000 8.341172240297e-13 -6.898413032241e-13 -1.064588455753e-13 1.087642428769e-12 5.505863834088e-09 8.000000000000e+00 + 2 0.002000 -9.056426077882e-12 -8.507705460825e-13 3.870587151793e-13 9.104530645520e-12 1.145327466665e-08 8.000000000000e+00 + 3 0.004000 -1.874382667530e-11 -1.024516431041e-12 9.026000588120e-13 1.879349246993e-11 3.611909443096e-08 8.000000000000e+00 + 4 0.006000 -2.786951317932e-11 -1.232297411070e-12 1.479227824074e-12 2.793593450588e-11 7.588204424652e-08 8.000000000000e+00 + 5 0.008000 -3.587842301006e-11 -1.509873952813e-12 2.181927110568e-12 3.597640564256e-11 1.311753129465e-07 8.000000000000e+00 + 6 0.010000 -4.206523711245e-11 -1.903884039364e-12 3.094536569146e-12 4.222185576618e-11 2.105217060092e-07 8.000000000000e+00 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/result.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/result.ref new file mode 100644 index 00000000000..e40f0dd93d9 --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/result.ref @@ -0,0 +1,9 @@ +etotref -218.5380463230584 +etotperatomref -109.2690231615 +Comparemag_tot_txt_pass 0 +Comparemag_atom_txt_pass 0 +Comparetorque_tot_txt_pass 0 +Comparetorque_atom_txt_pass 0 +Comparespincur_tot_txt_pass 0 +CompareCurrent_pass 0 +totaltimeref 1.84 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/spincur_tot.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/spincur_tot.txt.ref new file mode 100644 index 00000000000..fcafaa8f4ab --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/spincur_tot.txt.ref @@ -0,0 +1,8 @@ +# S=M/2; unit=hbar/(Bohr^2*Hartree_atomic_time); flow_index_first +#step time_fs Jxx Jxy Jxz Jyx Jyy Jyz Jzx Jzy Jzz + 1 0.000000 2.576695605481e-13 -4.742647922919e-14 9.611979317132e-14 1.530638900597e-13 -5.854886400028e-14 -1.909896222278e-13 4.600179363465e-13 -7.668010812926e-14 -7.381817189594e-14 + 2 0.002000 2.666244553670e-13 -4.566801709470e-14 6.881527168337e-14 1.594035634671e-13 -1.060391529299e-13 3.158170743608e-13 4.597429491125e-13 -5.645242488122e-13 -6.178048811495e-14 + 3 0.004000 3.194417445462e-13 -3.290708233150e-14 2.223295211368e-14 1.788246042956e-13 -3.359365445450e-13 2.735281517485e-12 4.640285387289e-13 -2.947878631628e-12 7.714946482476e-14 + 4 0.006000 4.737689539638e-13 2.488245661390e-15 -5.499124485891e-14 2.124186851534e-13 -8.666675894213e-13 8.819369272723e-12 4.771283582899e-13 -8.952673404709e-12 4.136128867866e-13 + 5 0.008000 8.233776327372e-13 6.543659216163e-14 -1.649578619439e-13 2.606045836353e-13 -1.730620705866e-12 2.000822579890e-11 4.967411833514e-13 -1.999969346430e-11 9.363914618553e-13 + 6 0.010000 1.485002580136e-12 1.643233710189e-13 -2.951824996434e-13 3.085249972122e-13 -2.850602556591e-12 3.737866329105e-11 5.439058945035e-13 -3.712068776630e-11 1.523132392515e-12 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_atom.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_atom.txt.ref new file mode 100644 index 00000000000..d13902a5df7 --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_atom.txt.ref @@ -0,0 +1,15 @@ +# SOC_pseudopotential_atom_decomposition; unit=hbar/Hartree_atomic_time +# not_the_derivative_of_atomic_moments +#step time_fs atom tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 1 -2.995874673439e-11 -5.723169571643e-13 1.571952691671e-12 + 1 0.000000 2 -3.009781223775e-11 -3.855840343387e-13 1.389312398266e-12 + 2 0.002000 1 -2.984644608980e-11 -5.771777980337e-13 1.591897847631e-12 + 2 0.002000 2 -2.981260130952e-11 -4.067947693300e-13 1.411974598751e-12 + 3 0.004000 1 -2.929531796337e-11 -6.112015326554e-13 1.687776576578e-12 + 3 0.004000 2 -2.845512812076e-11 -4.872891471761e-13 1.522043087905e-12 + 4 0.006000 1 -2.789617528015e-11 -7.069464405613e-13 1.932946454901e-12 + 4 0.006000 2 -2.511199481477e-11 -6.816847298422e-13 1.802468307083e-12 + 5 0.008000 1 -2.526376886166e-11 -8.982945714778e-13 2.392423288787e-12 + 5 0.008000 2 -1.910729440220e-11 -1.032876122974e-12 2.325937738134e-12 + 6 0.010000 1 -2.104367580952e-11 -1.220256785447e-12 3.122243721933e-12 + 6 0.010000 2 -1.003619544124e-11 -1.570323217182e-12 3.147485191953e-12 diff --git a/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_tot.txt.ref b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_tot.txt.ref new file mode 100644 index 00000000000..c7b30fe4c8b --- /dev/null +++ b/tests/05_rtTDDFT/29_PW_soc_len_TDDFT/torque_tot.txt.ref @@ -0,0 +1,8 @@ +# torque=dS/dt; S=M/2; unit=hbar/Hartree_atomic_time +#step time_fs tau_xc_x tau_xc_y tau_xc_z tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 1.874501167490e-38 -2.980082427274e-38 3.637993661690e-39 -6.005655897214e-11 -9.579009915030e-13 2.961265089937e-12 + 2 0.002000 -2.543729223323e-36 2.483042096919e-36 -3.734690595686e-36 -5.965904739932e-11 -9.839725673636e-13 3.003872446382e-12 + 3 0.004000 -2.413108644475e-35 4.864211530974e-35 -1.596348454371e-36 -5.775044608414e-11 -1.098490679832e-12 3.209819664483e-12 + 4 0.006000 -1.644709518019e-34 4.132866997294e-34 2.582675062921e-35 -5.300817009492e-11 -1.388631170403e-12 3.735414761984e-12 + 5 0.008000 -1.887940273325e-33 6.111686779488e-34 8.355449444240e-35 -4.437106326385e-11 -1.931170694452e-12 4.718361026921e-12 + 6 0.010000 -1.081988789974e-33 -2.628252615571e-33 -8.160792640079e-34 -3.107987125076e-11 -2.790580002629e-12 6.269728913885e-12 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT new file mode 100644 index 00000000000..5910b7f62cb --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT @@ -0,0 +1,62 @@ +INPUT_PARAMETERS +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +dft_functional PBE +gga_grad 1 +ecutwfc 20 +ecutrho 80 +nbands 12 +kpar 2 +nspin 4 +noncolin 1 +lspinorb 1 +symmetry -1 +init_wfc atomic+random +pw_seed 1 +ks_solver cg +pw_diag_thr 1e-13 +scf_thr 1e-12 +scf_nmax 150 +mixing_type broyden +mixing_beta 0.5 +mixing_beta_mag 0.5 +mixing_gg0 0 +mixing_gg0_mag 0 +smearing_method fd +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.002 +md_nstep 1 +estep_per_md 5 +cal_force 0 +cal_stress 0 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 1.5707963267948966 +td_gauss_sigma 0.04 +td_gauss_t0 0 +td_gauss_amp 0.1 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-13 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +td_out_torque 1 +td_out_spincur 1 +out_current 1 +out_level ie +td_stype 1 +td_out_mag onsite +onsite_radius 3.0 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/KPT b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README new file mode 100644 index 00000000000..2bc0417ecdd --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README @@ -0,0 +1 @@ +Test nspin=4 PW RT-TDDFT with SOC in the velocity gauge using onsite magnetic moments. diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU new file mode 100644 index 00000000000..139b8c551ed --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU @@ -0,0 +1,21 @@ +ATOMIC_SPECIES +Si 28 Si_dojo_soc.upf upf201 + +NUMERICAL_ORBITAL +Si_dojo_6au.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..a27bd87d37f --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref @@ -0,0 +1,6 @@ + 1 -6.847139035727e-13 3.153189564873e-14 -9.600988197277e-13 + 2 -5.747743492724e-08 1.230373526210e-13 7.787470761806e-12 + 3 -2.291372667484e-07 -4.370960711008e-13 3.327548964774e-11 + 4 -5.127178441324e-07 -3.987766754068e-12 7.337570647441e-11 + 5 -9.046018498177e-07 -1.398263237042e-11 1.248606891581e-10 + 6 -1.400015393069e-06 -3.450823362661e-11 1.837914207926e-10 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref new file mode 100644 index 00000000000..ff611fba3e9 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref @@ -0,0 +1,16 @@ +# method=onsite; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +# onsite_radius(Bohr)=3.000000000000e+00; projector=q(k+G+A); atom_phase_cancels +# orbital=../../PP_ORB/Si_dojo_6au.orb +#step time_fs atom Mx My Mz + 1 0.000000 1 8.053904765326e-11 -2.590379299949e-12 2.695932525831e-09 + 1 0.000000 2 -1.374186042735e-10 3.752895563758e-11 -2.670477963762e-09 + 2 0.002000 1 -1.733702764684e-10 1.959491596759e-12 2.764673004418e-09 + 2 0.002000 2 1.233804308720e-10 6.657203954763e-11 -2.648813383910e-09 + 3 0.004000 1 -9.423045815860e-10 -1.784173156438e-11 2.872179761387e-09 + 3 0.004000 2 9.030423075984e-10 1.221199252832e-10 -2.681691077533e-09 + 4 0.006000 1 -2.228605938681e-09 -6.084489509450e-11 3.011140913001e-09 + 4 0.006000 2 2.204617974289e-09 2.051458551644e-10 -2.775666130966e-09 + 5 0.008000 1 -4.035709946781e-09 -1.259454034119e-10 3.175208544010e-09 + 5 0.008000 2 4.031702471874e-09 3.163418381413e-10 -2.935733298820e-09 + 6 0.010000 1 -6.367397373253e-09 -2.121199257910e-10 3.359621560117e-09 + 6 0.010000 2 6.387599005286e-09 4.559797643633e-10 -3.164769165481e-09 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref new file mode 100644 index 00000000000..37d5d6f66fc --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref @@ -0,0 +1,10 @@ +# method=onsite; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +# onsite_radius(Bohr)=3.000000000000e+00; projector=q(k+G+A); atom_phase_cancels +# orbital=../../PP_ORB/Si_dojo_6au.orb +#step time_fs Mx My Mz M_norm M_abs_integral N_elec + 1 0.000000 1.214529056807e-12 1.353857486371e-12 6.594936585360e-14 1.819989077464e-12 5.655579705540e-09 8.000000000000e+00 + 2 0.002000 -8.743307074901e-12 1.201800440839e-12 5.532947808767e-13 8.842843322154e-12 6.304278379745e-09 8.000000000000e+00 + 3 0.004000 -1.869532759260e-11 1.050334617645e-12 1.039838784347e-12 1.875365941092e-11 7.463987294102e-09 8.000000000000e+00 + 4 0.006000 -2.863535903604e-11 9.001715900077e-13 1.526237762642e-12 2.869012892482e-11 8.924820380402e-09 8.000000000000e+00 + 5 0.008000 -3.856000509796e-11 7.514442616677e-13 2.012452748025e-12 3.861979580081e-11 1.146611739707e-08 8.000000000000e+00 + 6 0.010000 -4.846632928944e-11 6.050931672159e-13 2.498409941345e-12 4.853445440888e-11 1.690400989176e-08 8.000000000000e+00 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref new file mode 100644 index 00000000000..3da526f03f6 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref @@ -0,0 +1,9 @@ +etotref -218.3114043373177 +etotperatomref -109.1557021687 +Comparemag_tot_txt_pass 0 +Comparemag_atom_txt_pass 0 +Comparetorque_tot_txt_pass 0 +Comparetorque_atom_txt_pass 0 +Comparespincur_tot_txt_pass 0 +CompareCurrent_pass 0 +totaltimeref 1.74 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref new file mode 100644 index 00000000000..802b7dd5d90 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref @@ -0,0 +1,8 @@ +# S=M/2; unit=hbar/(Bohr^2*Hartree_atomic_time); flow_index_first +#step time_fs Jxx Jxy Jxz Jyx Jyy Jyz Jzx Jzy Jzz + 1 0.000000 1.267919086754e-13 5.145198692921e-14 -1.931316544630e-13 6.015257225557e-14 -2.843939266804e-14 1.241297853503e-13 2.242746640684e-13 -7.321375110977e-14 -2.773781395967e-13 + 2 0.002000 1.265191674762e-13 5.027723637210e-14 -2.111502018772e-13 6.301063841080e-14 -4.353736297079e-14 -5.019406639149e-13 2.238088766720e-13 4.769497155670e-13 -2.840764609462e-13 + 3 0.004000 1.241979771888e-13 5.005964562469e-14 -2.287128140430e-13 7.384543326544e-14 -1.308171867044e-13 -3.695184459769e-12 2.204639195477e-13 3.250686596645e-12 -2.343787538517e-13 + 4 0.006000 1.199354674595e-13 5.051304223258e-14 -2.457402062605e-13 9.362456624868e-14 -3.694391656904e-13 -1.208259910631e-11 2.112346292449e-13 1.054715434315e-11 -6.400333836950e-14 + 5 0.008000 1.140969671213e-13 5.136125791370e-14 -2.621834659735e-13 1.205675153723e-13 -8.524092920212e-13 -2.838206911878e-11 1.967367985310e-13 2.478945666354e-11 3.050166876471e-13 + 6 0.010000 1.048518041856e-13 5.354724230295e-14 -2.781142930194e-13 1.494849350185e-13 -1.691153805006e-12 -5.537884770040e-11 1.803633849414e-13 4.851983981969e-11 9.702624997229e-13 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref new file mode 100644 index 00000000000..3ae96b8f797 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref @@ -0,0 +1,15 @@ +# SOC_pseudopotential_atom_decomposition; unit=hbar/Hartree_atomic_time +# not_the_derivative_of_atomic_moments +#step time_fs atom tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 1 -3.009732175824e-11 -4.108455146649e-13 1.592854534076e-12 + 1 0.000000 2 -3.013959072163e-11 -5.077888556064e-13 1.361201150127e-12 + 2 0.002000 1 -3.008101003142e-11 -4.060717081249e-13 1.589232899020e-12 + 2 0.002000 2 -3.013862395211e-11 -5.111015811344e-13 1.359790199913e-12 + 3 0.004000 1 -3.000253710448e-11 -4.006229281343e-13 1.580779967604e-12 + 3 0.004000 2 -3.016147570840e-11 -5.124207604108e-13 1.363333315015e-12 + 4 0.006000 1 -2.982498689633e-11 -3.939800790217e-13 1.564088146610e-12 + 4 0.006000 2 -3.025383100391e-11 -5.112950942815e-13 1.376052808984e-12 + 5 0.008000 1 -2.951828189043e-11 -3.842421478030e-13 1.536275135384e-12 + 5 0.008000 2 -3.045570157440e-11 -5.084908527962e-13 1.402227800791e-12 + 6 0.010000 1 -2.906499726380e-11 -3.679581121867e-13 1.493728904225e-12 + 6 0.010000 2 -3.079180567089e-11 -5.073317593587e-13 1.447025652201e-12 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref new file mode 100644 index 00000000000..5a13499a2db --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref @@ -0,0 +1,8 @@ +# torque=dS/dt; S=M/2; unit=hbar/Hartree_atomic_time +#step time_fs tau_xc_x tau_xc_y tau_xc_z tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 2.426514743516e-38 -6.124579624877e-38 -1.069739881488e-38 -6.023691247987e-11 -9.186343702713e-13 2.954055684203e-12 + 2 0.002000 2.422536136501e-37 9.349947222335e-39 -1.494907395682e-38 -6.021963398353e-11 -9.171732892593e-13 2.949023098933e-12 + 3 0.004000 8.495004323470e-37 -8.238992634422e-38 1.930734755893e-38 -6.016401281288e-11 -9.130436885451e-13 2.944113282619e-12 + 4 0.006000 9.953905345459e-37 -2.330657888322e-37 -1.101676296253e-37 -6.007881790024e-11 -9.052751733032e-13 2.940140955594e-12 + 5 0.008000 1.543717346039e-36 4.410489552227e-37 8.114869589385e-37 -5.997398346483e-11 -8.927330005992e-13 2.938502936176e-12 + 6 0.010000 -1.719112621135e-36 2.527790279275e-36 -2.394175326990e-36 -5.985680293469e-11 -8.752898715454e-13 2.940754556426e-12 diff --git a/tests/05_rtTDDFT/CASES_CPU.txt b/tests/05_rtTDDFT/CASES_CPU.txt index 8cbca6fadab..6ba3fd96a43 100644 --- a/tests/05_rtTDDFT/CASES_CPU.txt +++ b/tests/05_rtTDDFT/CASES_CPU.txt @@ -26,3 +26,5 @@ 26_PW_single_TDDFT 27_PW_gmres_recycle_TDDFT 28_PW_gmres_subspace_TDDFT +29_PW_soc_len_TDDFT +30_PW_soc_onsite_TDDFT diff --git a/tests/05_rtTDDFT/CASES_GPU.txt b/tests/05_rtTDDFT/CASES_GPU.txt index c2cf48ba997..5a135595e86 100644 --- a/tests/05_rtTDDFT/CASES_GPU.txt +++ b/tests/05_rtTDDFT/CASES_GPU.txt @@ -26,3 +26,5 @@ 26_PW_single_TDDFT 27_PW_gmres_recycle_TDDFT 28_PW_gmres_subspace_TDDFT +29_PW_soc_len_TDDFT +30_PW_soc_onsite_TDDFT diff --git a/tests/integrate/validation_tools/props_tddft.sh b/tests/integrate/validation_tools/props_tddft.sh index 5d3f7d1eace..fa9a85efe9c 100644 --- a/tests/integrate/validation_tools/props_tddft.sh +++ b/tests/integrate/validation_tools/props_tddft.sh @@ -9,7 +9,17 @@ run_tddft_props(){ #-------------------------------------------- -# check currents in rt-TDDFT +# Check spin observables in rt-TDDFT +#-------------------------------------------- +for spin_file in mag_tot.txt mag_atom.txt torque_tot.txt torque_atom.txt spincur_tot.txt; do + if [ -f "$spin_file.ref" ]; then + spin_key=$(sanitize_result_key "$spin_file") + record_compare_result "$props_result_file" "Compare${spin_key}_pass" "$spin_file.ref" "OUT.autotest/$spin_file" 8 + fi +done + +#-------------------------------------------- +# Check currents in rt-TDDFT #-------------------------------------------- if ! test -z "$out_current" && [ $out_current ]; then current1ref=current_tot.txt.ref From 019fc9bc560e765e497e93163cb1a0db08407b63 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 20 Oct 2026 15:13:13 +0800 Subject: [PATCH 03/33] Continue when `scf_nmax` is reached with consistent endpoint states --- source/source_esolver/esolver_ks_pw_tddft.cpp | 47 +++++++++++++------ source/source_esolver/esolver_ks_pw_tddft.h | 9 ++-- .../source_pw/module_pwdft/td_history_pw.cpp | 2 +- source/source_pw/module_pwdft/td_history_pw.h | 4 +- source/source_pw/module_pwdft/td_pw.cpp | 5 +- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 63609f0631a..3ab31706b44 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -320,23 +320,10 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); } -template -void ESolver_KS_PW_TDDFT::iter_finish(UnitCell& ucell, const int istep, int& iter, bool& conv_esolver) -{ - ESolver_KS_PW::iter_finish(ucell, istep, iter, conv_esolver); - if (istep > 0 && !conv_esolver && iter >= this->inp_->scf_nmax) - { - const int evolution_step = istep + 1; - const std::string message = "The time-dependent SCF step did not converge. Electronic evolution step: " - + std::to_string(evolution_step) + ", SCF iteration: " + std::to_string(iter) + "."; - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", message); - } -} - template void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) { - if (!conv_esolver) + if (!conv_esolver && this->niter < this->maxniter) { const int evolution_step = istep + 1; const std::string message @@ -348,7 +335,11 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const psi::Psi* current = this->stp.template get_psi_t(); this->td_solver_->check_initial(*current, this->niter); } - // Endpoint diagnostics must see the converged potential and endpoint A, not the propagation midpoint state. + if (!conv_esolver) + { + finalize_endpoint(ucell); + } + // Endpoint diagnostics use the accepted endpoint, not the propagation midpoint or a mixed trial density. auto* endpoint_hamilt = static_cast*>(this->p_hamilt); const Real* endpoint_veff = this->pelec->pot->template get_veff_smooth_data(); endpoint_hamilt->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); @@ -373,6 +364,32 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, } } +template +void ESolver_KS_PW_TDDFT::finalize_endpoint(UnitCell& cell) +{ + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "finalize_endpoint"); + psi::Psi* current = this->stp.template get_psi_t(); + auto* estate = static_cast*>(this->pelec); + // The last SCF iteration may have mixed the density. Keep the actual orbitals and their occupations. + estate->psiToRho(*current); + module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, cell.symm); + this->chr.save_rho_before_sum_band(); + this->pelec->pot->update_from_charge(&this->chr, &cell); + + auto* hamiltonian = static_cast*>(this->p_hamilt); + const Real* veff = this->pelec->pot->template get_veff_smooth_data(); + hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); + hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); + this->td_solver_->cal_band_energy(op, *current, &this->pelec->ekb); + elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); + this->pelec->f_en.deband = this->pelec->cal_delta_eband(cell); + this->pelec->f_en.descf = 0.0; + // Do not reuse a self-consistency force correction from an earlier endpoint. + this->pelec->vnew_exist = false; + this->pelec->cal_energies(2, this->inp_->imp_sol, this->inp_->sc_mag_switch, this->inp_->dft_plus_u, this->inp_->assume_isolated); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "finalize_endpoint"); +} + template void ESolver_KS_PW_TDDFT::sample_endpoint(UnitCell& cell, int step) { diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index ccd58105061..f28cee6f484 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -45,10 +45,7 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW /** @brief Switch between ground-state diagonalization and real-time propagation. */ void hamilt2rho_single(UnitCell& ucell, const int istep, const int iter, const double ethr) override; - /** @brief Apply the convergence policy appropriate to the current stage. */ - void iter_finish(UnitCell& ucell, const int istep, int& iter, bool& conv_esolver) override; - - /** @brief Save the converged state as the origin of the next propagation step. */ + /** @brief Save the converged or iteration-limited endpoint for the next propagation step. */ void after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) override; private: @@ -67,7 +64,9 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW double initial_rho_electrons_ = 0.0; /** @brief Print the one-based electronic step header. */ void print_step(const int istep) const; - /** @brief Print Eij from the converged density, collecting all k-point pools on the main rank. */ + /** @brief Rebuild an iteration-limited endpoint from its final orbitals without changing occupations. */ + void finalize_endpoint(UnitCell& cell); + /** @brief Print Eij from the accepted endpoint, collecting all k-point pools on the main rank. */ void print_eij(const int istep); void report_orth(const UnitCell& ucell, int istep); void prep_td_step(const int istep); diff --git a/source/source_pw/module_pwdft/td_history_pw.cpp b/source/source_pw/module_pwdft/td_history_pw.cpp index 4cf9e48be94..8bbddaac0c5 100644 --- a/source/source_pw/module_pwdft/td_history_pw.cpp +++ b/source/source_pw/module_pwdft/td_history_pw.cpp @@ -89,7 +89,7 @@ const psi::Psi& TDHistoryPW::previous() const { if (!previous_) { - ModuleBase::WARNING_QUIT("TDHistoryPW", "No converged state has been saved for propagation."); + ModuleBase::WARNING_QUIT("TDHistoryPW", "No endpoint state has been saved for propagation."); } return *previous_; } diff --git a/source/source_pw/module_pwdft/td_history_pw.h b/source/source_pw/module_pwdft/td_history_pw.h index 6d03fe748aa..c521c68701f 100644 --- a/source/source_pw/module_pwdft/td_history_pw.h +++ b/source/source_pw/module_pwdft/td_history_pw.h @@ -10,7 +10,7 @@ namespace pw { -/** @brief Converged time-step history and predictor-corrector potential storage. */ +/** @brief Accepted time-step history and predictor-corrector potential storage. */ template class TDHistoryPW { @@ -21,7 +21,7 @@ class TDHistoryPW /** @brief Use the current potential for the predictor, then average with the saved potential. */ const typename GetTypeReal::type* prop_pot(elecstate::Potential& potential, const bool corrector); - /** @brief Save the converged state; the caller owns the convergence decision. */ + /** @brief Save the accepted endpoint; the caller owns the convergence decision. */ void save(const psi::Psi& current, elecstate::Potential& potential); /** @brief Access the fixed origin of every corrector solve in the next step. */ diff --git a/source/source_pw/module_pwdft/td_pw.cpp b/source/source_pw/module_pwdft/td_pw.cpp index 0bd8cf58906..b0e7cce2acc 100644 --- a/source/source_pw/module_pwdft/td_pw.cpp +++ b/source/source_pw/module_pwdft/td_pw.cpp @@ -15,10 +15,9 @@ void check_td_input(const Input_para& input, const UnitCell& cell, int xc_type, { ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT requires out_current=0 or 1."); } - if (input.scf_nmax < 2) + if (input.scf_nmax < 1) { - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", - "PW RT-TDDFT requires scf_nmax >= 2: one predictor and at least one corrector iteration."); + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "PW RT-TDDFT requires scf_nmax >= 1."); } if (unitcell::if_atoms_can_move(cell.atoms, cell.ntype)) { From c9d9b51a58b759296685a28775347abca00bb90f Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 20 Oct 2026 20:37:00 +0800 Subject: [PATCH 04/33] Feature: Add automatic half-step retries for unconverged PW RT-TDDFT propagation --- source/source_esolver/esolver_ks.cpp | 65 ++++-- source/source_esolver/esolver_ks.h | 10 + source/source_esolver/esolver_ks_pw.cpp | 5 +- source/source_esolver/esolver_ks_pw_tddft.cpp | 189 +++++++++++++++++- source/source_esolver/esolver_ks_pw_tddft.h | 22 ++ .../source_estate/module_charge/chg_mix.cpp | 26 ++- source/source_estate/module_charge/chg_mix.h | 5 +- .../module_pot/td_field_manager.cpp | 110 +++++++++- .../module_pot/td_field_manager.h | 23 +++ source/source_hsolver/hsolver_pw_tddft.h | 5 + source/source_hsolver/linear_gmres.cpp | 4 +- .../source_io/module_ctrl/ctrl_output_pw.cpp | 5 +- source/source_io/module_ctrl/ctrl_output_pw.h | 1 + .../module_parameter/input_parameter.h | 2 + .../module_parameter/read_inp_tddft.cpp | 49 ++++- 15 files changed, 477 insertions(+), 44 deletions(-) diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index 9d12dfe07dd..b510a2bcb8f 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -181,9 +181,18 @@ void ESolver_KS::runner(BaseCell& basecell, const int istep) this->before_scf(ucell, istep); ModuleBase::GlobalFunc::DONE(GlobalV::ofs_running, "INIT SCF"); + const bool converged = this->scf_loop(ucell, istep, this->maxniter); + this->after_scf(ucell, istep, converged); + this->conv_esolver = converged; + ModuleBase::timer::end(this->classname, "runner"); +} + +bool ESolver_KS::scf_loop(UnitCell& ucell, int istep, int iter_limit) +{ + ModuleBase::timer::start("ESolver_KS", "scf_loop"); // 2) SCF iterations bool conv_esolver = false; - this->niter = this->maxniter; + this->niter = iter_limit; this->diag_ethr = this->inp_->pw_diag_thr; this->scf_nmax_flag = false; // mohan add 2025-09-21 this->scf_rerun_ = false; // clear any stale rerun request before the loop @@ -191,9 +200,9 @@ void ESolver_KS::runner(BaseCell& basecell, const int istep) int iter = 1; int rerun_count = 0; const int max_rerun = 100; // safety net: EXX rerun should never exceed this - while (iter <= this->maxniter) + while (iter <= iter_limit) { - if(iter == this->maxniter) + if(iter == iter_limit) { this->scf_nmax_flag=true; } @@ -211,7 +220,7 @@ void ESolver_KS::runner(BaseCell& basecell, const int istep) if (conv_esolver || this->oscillate_esolver) { this->niter = iter; - if (this->oscillate_esolver) + if (this->oscillate_esolver && !this->defer_iter_output(istep)) { std::cout << " !! Density oscillation is found, STOP HERE !!" << std::endl; } @@ -242,13 +251,9 @@ void ESolver_KS::runner(BaseCell& basecell, const int istep) ++iter; } // end scf iterations - // 7) after scf - this->after_scf(ucell, istep, conv_esolver); - this->conv_esolver = conv_esolver; - - ModuleBase::timer::end(this->classname, "runner"); - return; -}; + ModuleBase::timer::end("ESolver_KS", "scf_loop"); + return conv_esolver; +} void ESolver_KS::before_scf(UnitCell& ucell, const int istep) { @@ -293,6 +298,11 @@ ESolver_KS::DensityStage ESolver_KS::density_stage(const int istep, const int it return DensityStage::standard; } +bool ESolver_KS::defer_iter_output(const int istep) const +{ + return false; +} + void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool &conv_esolver) { @@ -307,15 +317,11 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & } // 1.2) print out eigenvalues and occupations - if (this->inp_->out_band[0]) + if (this->inp_->out_band[0] && !this->defer_iter_output(istep)) { if (iter % this->inp_->out_freq_elec == 0 || iter == this->inp_->scf_nmax || conv_esolver) { - ModuleIO::write_eig_iter(this->pelec->ekb, - this->pelec->wg, - *this->pelec->klist, - this->inp_->nbands, - this->inp_->nspin); + this->write_iter_bands(); } } @@ -392,8 +398,10 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & } // Iter finish - ESolver_FP::iter_finish(ucell, istep, iter, conv_esolver); - + if (!this->defer_iter_output(istep)) + { + ESolver_FP::iter_finish(ucell, istep, iter, conv_esolver); + } // the end, print time double duration = ModuleBase::get_duration(iter_time, ModuleBase::get_time()); @@ -403,14 +411,29 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & dkin, duration, *this->inp_, PARAM.globalv.two_fermi, diag_ethr, 0, true, this->ds_rms_); + if (!this->defer_iter_output(istep)) + { + this->record_iteration(ucell, duration); + } +} + +void ESolver_KS::write_iter_bands() +{ + if (this->inp_->out_band[0]) + { + ModuleIO::write_eig_iter(this->pelec->ekb, this->pelec->wg, *this->pelec->klist, + this->inp_->nbands, this->inp_->nspin); + } +} + +void ESolver_KS::record_iteration(const UnitCell& ucell, double duration) +{ #ifdef __JSON - // add Json of scf mag Json::add_output_scf_mag(ucell.magnet.tot_mag, ucell.magnet.abs_mag, this->pelec->f_en.etot * ModuleBase::Ry_to_eV, this->pelec->f_en.etot_delta * ModuleBase::Ry_to_eV, drho, duration); #endif //__JSON - } //! Something to do after SCF iterations when SCF is converged or comes to the max iter step. diff --git a/source/source_esolver/esolver_ks.h b/source/source_esolver/esolver_ks.h index 29001c4180a..8a138fed150 100644 --- a/source/source_esolver/esolver_ks.h +++ b/source/source_esolver/esolver_ks.h @@ -32,6 +32,13 @@ class ESolver_KS : public ESolver_FP virtual void after_all_runners(BaseCell& basecell) override; protected: + /** @brief Run SCF iterations without preparing or committing a physical endpoint. */ + bool scf_loop(UnitCell& ucell, int istep, int iter_limit); + /** @brief Record an accepted iteration in JSON without changing its convergence status. */ + void record_iteration(const UnitCell& ucell, double duration); + /** @brief Write current band expectations when iterative band output is requested. */ + void write_iter_bands(); + /** @brief Select whether the density is an endpoint predictor or an SCF iterate. */ enum class DensityStage { @@ -48,6 +55,9 @@ class ESolver_KS : public ESolver_FP //! Something to do before hamilt2rho function in each iter loop. virtual void iter_init(UnitCell& ucell, const int istep, const int iter); + //! Whether KS iterations defer output until the final state is accepted. + virtual bool defer_iter_output(const int istep) const; + //! Something to do after hamilt2rho function in each iter loop. virtual void iter_finish(UnitCell& ucell, const int istep, int& iter, bool& conv_esolver) override; diff --git a/source/source_esolver/esolver_ks_pw.cpp b/source/source_esolver/esolver_ks_pw.cpp index c804141b14a..3af0fecbebe 100644 --- a/source/source_esolver/esolver_ks_pw.cpp +++ b/source/source_esolver/esolver_ks_pw.cpp @@ -438,7 +438,10 @@ void ESolver_KS_PW::iter_finish(UnitCell& ucell, const int istep, int DFTU_BASE::SOC_LAYOUT_PAULI); // the output quantities - ModuleIO::ctrl_iter_pw(istep, iter, conv_esolver, this->stp, this->kv, this->pw_wfc, *this->inp_, GlobalV::ofs_running); + if (!this->defer_iter_output(istep)) + { + ModuleIO::ctrl_iter_pw(istep, iter, conv_esolver, this->stp, this->kv, this->pw_wfc, *this->inp_, false, GlobalV::ofs_running); + } } template diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 3ab31706b44..a28629ccd8d 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -12,6 +12,7 @@ #include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/hamilt_hs_adapter.h" #include "source_hamilt/module_xc/xc_functional.h" +#include "source_io/module_ctrl/ctrl_output_pw.h" #include "source_io/module_current/td_current_pw.h" #include "source_io/module_current/td_spin_pw_io.h" #include "source_io/module_efield/td_efield_io.h" @@ -26,6 +27,7 @@ #include #include #include +#include namespace ModuleESolver { @@ -45,6 +47,17 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons const std::string& output_dir = PARAM.globalv.global_out_dir; this->td_field_manager_ = elecstate::create_td_field_manager(inp); + if (inp.td_max_halvings > 0) + { + const int parts = 1 << inp.td_max_halvings; + for (const elecstate::TDField& field: this->td_field_manager_->fields()) + { + if (field.subdivisions() > std::numeric_limits::max() / parts) + { + ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", "td_max_halvings exceeds the field sampling integer range."); + } + } + } if (inp.init_vecpot) { // An empty directory selects the calculation working directory, not OUT.. @@ -126,8 +139,152 @@ void ESolver_KS_PW_TDDFT::runner(BaseCell& basecell, const int istep) } for (int electronic_step = first_step; electronic_step <= last_step; ++electronic_step) { - ESolver_KS::runner(basecell, electronic_step); + if (this->retry_enabled(electronic_step)) + { + basecell.require_kind(BaseCell::Kind::unitcell, __FUNCTION__); + UnitCell& cell = static_cast(basecell); + this->run_with_retries(cell, electronic_step); + } + else + { + ESolver_KS::runner(basecell, electronic_step); + } + } +} + +template +bool ESolver_KS_PW_TDDFT::defer_iter_output(const int istep) const +{ + return this->retry_enabled(istep); +} + +template +bool ESolver_KS_PW_TDDFT::retry_enabled(int istep) const +{ + return istep > 0 && this->inp_->td_max_halvings > 0; +} + +template +typename ESolver_KS_PW_TDDFT::AcceptedState ESolver_KS_PW_TDDFT::capture_state() const +{ + AcceptedState state; + state.energy = this->pelec->f_en; + state.eigenvalues = this->pelec->ekb; + state.density.resize(this->chr.nspin * this->chr.nrxx); + for (int spin = 0; spin < this->chr.nspin; ++spin) + { + std::copy(this->chr.rho[spin], this->chr.rho[spin] + this->chr.nrxx, state.density.begin() + spin * this->chr.nrxx); + } + return state; +} + +template +void ESolver_KS_PW_TDDFT::restore_state(const AcceptedState& state) +{ + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "restore_state"); + *this->stp.template get_psi_t() = this->history_.previous(); + this->pelec->f_en = state.energy; + this->pelec->ekb = state.eigenvalues; + this->pelec->vnew_exist = false; + for (int spin = 0; spin < this->chr.nspin; ++spin) + { + const double* density = state.density.data() + spin * this->chr.nrxx; + std::copy(density, density + this->chr.nrxx, this->chr.rho[spin]); } + this->chr.save_rho_before_sum_band(); + // The caller binds the trial field and rebuilds the endpoint potential. The saved + // left-endpoint potential used by the corrector is already held in history_. + this->td_solver_->invalidate_basis(); + this->td_solver_->reset_orth_stats(); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "restore_state"); +} + +template +bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, + int step, + int index, + int depth, + const elecstate::TDFieldState& full, + AcceptedState& accepted, + hsolver::TDOrthStats& stats) +{ + const int parts = 1 << depth; + if (depth > 0) + { + this->restore_state(accepted); + const elecstate::TDFieldState trial = this->td_field_manager_->subinterval(full, index, parts); + this->td_field_manager_->bind_field(trial); + // Length-gauge fields are fixed potential components and must be rebuilt + // at the trial endpoint, just as before_scf does for an original step. + this->pelec->pot->init_pot(&this->chr); + this->prepare_init_hamilt(cell); + } + const double left_time = (step - 1 + static_cast(index) / parts) * full.dt * ModuleBase::AU_to_FS; + const double right_time = (step - 1 + static_cast(index + 1) / parts) * full.dt * ModuleBase::AU_to_FS; + const std::streamsize precision = log_.precision(); + log_ << " TD trial: [" << std::setprecision(12) << left_time << ", " << right_time << "] fs, depth=" << depth << '\n'; + log_.precision(precision); + std::ostringstream interval; + interval << std::setprecision(12) << "[" << left_time << ", " << right_time << "] fs"; + const std::string interval_text = interval.str(); + if (depth > 0) + { + std::cout << " TD substep: " << interval_text << std::endl; + } + const bool converged = this->scf_loop(cell, step, this->inp_->td_retry_nmax); + const bool accept_trial = converged || depth == this->inp_->td_max_halvings; + log_ << " TD trial result: iterations=" << this->niter << ", residual=" << this->drho << ", converged=" << converged + << ", accepted=" << accept_trial << '\n'; + if (!converged && depth < this->inp_->td_max_halvings) + { + std::cout << " TD retry: SCF not converged; splitting " << interval_text << " into two half steps." << std::endl; + const int next_depth = depth + 1; + const int left_index = 2 * index; + const int right_index = left_index + 1; + const bool left_converged = advance_interval(cell, step, left_index, next_depth, full, accepted, stats); + const bool right_converged = advance_interval(cell, step, right_index, next_depth, full, accepted, stats); + return left_converged && right_converged; + } + if (!converged) + { + this->finalize_endpoint(cell); + } + const hsolver::TDOrthStats& trial_stats = this->td_solver_->orth_stats(); + stats.before = std::max(stats.before, trial_stats.before); + stats.after = std::max(stats.after, trial_stats.after); + const psi::Psi* current = this->stp.template get_psi_t(); + this->history_.save(*current, *this->pelec->pot); + accepted = this->capture_state(); + return converged; +} + +template +void ESolver_KS_PW_TDDFT::run_with_retries(UnitCell& cell, int step) +{ + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_with_retries"); + const ModuleBase::TimePoint started = ModuleBase::get_time(); + AcceptedState accepted = this->capture_state(); + this->before_scf(cell, step); + ModuleBase::GlobalFunc::DONE(log_, "INIT SCF"); + const elecstate::TDFieldState full = this->td_field_manager_->field_state(); + hsolver::TDOrthStats stats; + const bool converged = advance_interval(cell, step, 0, 0, full, accepted, stats); + // Restore original-grid sampling metadata without altering the accepted endpoint. + this->td_field_manager_->bind_field(full); + this->td_solver_->set_orth_stats(stats); + this->chr.save_rho_before_sum_band(); + // Taoni add 2026-10-20: ESolver_FP::iter_finish currently only writes density backups. + // Revisit this call if the base hook gains responsibilities beyond output. + bool force_output = true; + ESolver_FP::iter_finish(cell, step, this->niter, force_output); + this->write_iter_bands(); + ModuleIO::ctrl_iter_pw(step, this->niter, converged, this->stp, this->kv, this->pw_wfc, *this->inp_, true, log_); + const double duration = ModuleBase::get_duration(started, ModuleBase::get_time()); + this->record_iteration(cell, duration); + this->output_field(step); + this->after_scf(cell, step, converged); + this->conv_esolver = converged; + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_with_retries"); } template @@ -195,6 +352,16 @@ void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) ModuleBase::GlobalFunc::OUT(log_, "Cartesian vector potential Ay(t)", A_right_ha[1]); ModuleBase::GlobalFunc::OUT(log_, "Cartesian vector potential Az(t)", A_right_ha[2]); } + if (!this->defer_iter_output(istep)) + { + this->output_field(istep); + } + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); +} + +template +void ESolver_KS_PW_TDDFT::output_field(int istep) const +{ if (GlobalV::MY_RANK == 0) { if (this->inp_->out_vecpot) @@ -206,7 +373,6 @@ void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) ModuleIO::write_td_field(*this->td_field_manager_, PARAM.globalv.global_out_dir); } } - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); } template @@ -229,6 +395,11 @@ void ESolver_KS_PW_TDDFT::iter_init(UnitCell& ucell, const int istep, } ModuleIO::write_head_td(GlobalV::ofs_running, ionic_step, istep, iter, this->basisname); ESolver_KS_PW::iter_init(ucell, istep, iter); + if (this->retry_enabled(istep) && iter == 1) + { + // Apply the trial limit after the parent initializes the mixing machinery. + this->p_chgmix->reset_scf_history(this->inp_->td_retry_nmax); + } } template @@ -300,7 +471,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in this->td_solver_->solve(op, this->history_.previous(), current, - this->td_field_manager_->dt_ha(), + this->td_field_manager_->interval_dt_ha(), momentum_shift, istep, iter, @@ -323,7 +494,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in template void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) { - if (!conv_esolver && this->niter < this->maxniter) + if (!conv_esolver && this->niter < this->maxniter && !this->retry_enabled(istep)) { const int evolution_step = istep + 1; const std::string message @@ -335,7 +506,7 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const psi::Psi* current = this->stp.template get_psi_t(); this->td_solver_->check_initial(*current, this->niter); } - if (!conv_esolver) + if (!conv_esolver && !this->retry_enabled(istep)) { finalize_endpoint(ucell); } @@ -353,8 +524,12 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, std::cout.precision(original_precision); if (istep >= 0) { - psi::Psi* current = this->stp.template get_psi_t(); - this->history_.save(*current, *this->pelec->pot); + // Retry intervals already saved their accepted endpoint before reaching this hook. + if (!this->retry_enabled(istep)) + { + psi::Psi* current = this->stp.template get_psi_t(); + this->history_.save(*current, *this->pelec->pot); + } sample_endpoint(ucell, istep); } diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index f28cee6f484..c0d62b7702b 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -10,6 +10,7 @@ #include #include +#include namespace ModuleESolver { @@ -33,6 +34,7 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW /** @brief Require an unmixed endpoint prediction before accepting a corrected step. */ ESolver_KS::DensityStage density_stage(const int istep, const int iter) const override; + bool defer_iter_output(const int istep) const override; void before_scf(UnitCell& ucell, const int istep) override; @@ -62,6 +64,26 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW pw::TDHistoryPW history_; double initial_wave_electrons_ = 0.0; double initial_rho_electrons_ = 0.0; + /** @brief Small endpoint data; the accepted wavefunctions remain owned by history_. */ + struct AcceptedState + { + elecstate::fenergy energy; + ModuleBase::matrix eigenvalues; + std::vector density; + }; + /** @brief Whether the current electronic step uses half-step retries. */ + bool retry_enabled(int istep) const; + AcceptedState capture_state() const; + void restore_state(const AcceptedState& state); + void run_with_retries(UnitCell& cell, int step); + bool advance_interval(UnitCell& cell, + int step, + int index, + int depth, + const elecstate::TDFieldState& full, + AcceptedState& accepted, + hsolver::TDOrthStats& stats); + void output_field(int step) const; /** @brief Print the one-based electronic step header. */ void print_step(const int istep) const; /** @brief Rebuild an iteration-limited endpoint from its final orbitals without changing occupations. */ diff --git a/source/source_estate/module_charge/chg_mix.cpp b/source/source_estate/module_charge/chg_mix.cpp index 89a2f94abc7..45d93180a4d 100644 --- a/source/source_estate/module_charge/chg_mix.cpp +++ b/source/source_estate/module_charge/chg_mix.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "source_base/module_mixing/broyden_mixing.h" #include "source_base/module_mixing/pulay_mixing.h" @@ -200,6 +201,14 @@ void Charge_Mixing::mix_reset() } } +void Charge_Mixing::reset_scf_history(int iter_limit) +{ + this->_drho_history.assign(iter_limit, std::numeric_limits::quiet_NaN()); + this->mixing_restart_step = iter_limit + 1; + this->mixing_restart_last = 0; + this->mixing_restart_count = 0; +} + bool Charge_Mixing::if_scf_oscillate(const int iteration, const double drho, const int iternum_used, const double threshold) { @@ -213,16 +222,15 @@ bool Charge_Mixing::if_scf_oscillate(const int iteration, const double drho, ModuleBase::WARNING_QUIT("Charge_Mixing", "set_mixing must be called before if_scf_oscillate"); } - if (iteration < 1 || iteration > this->cfg_.scf_nmax) - { - ModuleBase::WARNING_QUIT("Charge_Mixing", - "iteration must be within [1, scf_nmax]"); - } - if(this->_drho_history.size() == 0) { this->_drho_history.resize(this->cfg_.scf_nmax); } + if (iteration < 1 || static_cast(iteration) > this->_drho_history.size()) + { + ModuleBase::WARNING_QUIT("Charge_Mixing", + "iteration must be within the active SCF history limit"); + } // add drho into history this->_drho_history[iteration - 1] = drho; @@ -246,6 +254,12 @@ bool Charge_Mixing::if_scf_oscillate(const int iteration, const double drho, double sumX = 0, sumY = 0, sumXY = 0, sumXX = 0; for (int i = iteration - iternum_used; i < iteration; i++) { + // A predictor does not enter mixing. Wait until the whole fitting + // window contains residuals from correctors in this trial. + if (std::isnan(this->_drho_history[i])) + { + return false; + } sumX += i; sumY += std::log10(this->_drho_history[i]); sumXY += i * std::log10(this->_drho_history[i]); diff --git a/source/source_estate/module_charge/chg_mix.h b/source/source_estate/module_charge/chg_mix.h index b52d9b96f82..db9d7a32494 100644 --- a/source/source_estate/module_charge/chg_mix.h +++ b/source/source_estate/module_charge/chg_mix.h @@ -74,6 +74,9 @@ class Charge_Mixing * @brief reset mixing, actually we only call init_mixing() to reset mixing instead of this function */ void mix_reset(); + + /** @brief Reset restart counters and residual history for an independently limited SCF trial. */ + void reset_scf_history(int iter_limit); // extracting parameters normally these parameters will not be used outside charge mixing // while Exx is using them as well as some other places @@ -149,7 +152,7 @@ class Charge_Mixing MixingConfig cfg_; ///< aggregated mixing config, also holds nspin/scf_thr_type/double_grid double* omega = nullptr; ///< omega for non-linear core correction double* tpiba = nullptr; ///< 2*pi/beta for non-linear core correction - std::vector _drho_history; ///< history of drho used to determine the oscillation, size is scf_nmax + std::vector _drho_history; ///< Residual history for oscillation detection, sized to the active SCF limit. ModulePW::PW_Basis* rhopw = nullptr; ///< smooth grid ModulePW::PW_Basis* rhodpw = nullptr; ///< dense grid, same as rhopw for ncpp. diff --git a/source/source_estate/module_pot/td_field_manager.cpp b/source/source_estate/module_pot/td_field_manager.cpp index e18f8d66a21..687638dd176 100644 --- a/source/source_estate/module_pot/td_field_manager.cpp +++ b/source/source_estate/module_pot/td_field_manager.cpp @@ -85,8 +85,8 @@ TDFieldManager::TDFieldManager(const bool enabled, const double length_cut1, const double length_cut2, std::vector fields) - : enabled_(enabled), gauge_(gauge), start_step_(start_step), end_step_(end_step), dt_ha_(dt), length_cut1_(length_cut1), - length_cut2_(length_cut2), fields_(std::move(fields)), current_step_(-1), active_(false) + : enabled_(enabled), gauge_(gauge), start_step_(start_step), end_step_(end_step), dt_ha_(dt), interval_dt_ha_(dt), + length_cut1_(length_cut1), length_cut2_(length_cut2), fields_(std::move(fields)), current_step_(-1), active_(false) { field_vals_ha_.resize(fields_.size(), 0.0); } @@ -249,6 +249,112 @@ void TDFieldManager::read_restart(const std::string& file_dir) select_A_prop(); } +TDFieldState TDFieldManager::field_state() const +{ + TDFieldState state; + state.active = active_; + state.dt = interval_dt_ha_; + state.values = field_vals_ha_; + state.left = A_left_ha_; + state.right = A_right_ha_; + state.propagation = A_prop_ha_; + state.electric = efield_ha_; + return state; +} + +void TDFieldManager::bind_field(const TDFieldState& state) +{ + active_ = state.active; + interval_dt_ha_ = state.dt; + field_vals_ha_ = state.values; + A_left_ha_ = state.left; + A_right_ha_ = state.right; + A_prop_ha_ = state.propagation; + efield_ha_ = state.electric; +} + +ModuleBase::Vector3 TDFieldManager::substep_a(const TDFieldState& full, int node, int parts) const +{ + if (node == 0) + { + return full.left; + } + if (node == parts) + { + return full.right; + } + const double fraction = static_cast(node) / parts; + if (file_source()) + { + return full.left + (full.right - full.left) * fraction; + } + ModuleBase::Vector3 result = full.left; + const int left_step = current_step_ - 1; + if (gauge_ != 0 && enabled_ && left_step >= start_step_ && left_step <= end_step_) + { + // Anchor each prefix integral to the original left endpoint. A failed trial + // never contributes to the field integral or changes the original right endpoint. + for (const TDField& field: fields_) + { + const int count = field.subdivisions(); + std::vector samples(count + 1); + const int denominator = parts * count; + for (int j = 0; j <= count; ++j) + { + const int numerator = node * j; + const double time = (left_step + static_cast(numerator) / denominator) * dt_ha_; + const TDFieldSample sample(left_step, numerator, denominator, time); + samples[j] = field.electric_field(sample); + } + double integral = 0.0; + const double spacing = dt_ha_ * fraction / count; + const int points = count + 1; + ModuleBase::Integral::Simpson_Integral(points, samples.data(), spacing, integral); + result[field.direction()] -= integral; + } + } + return result; +} + +TDFieldState TDFieldManager::subinterval(const TDFieldState& full, int index, int parts) const +{ + TDFieldState state = full; + state.dt = dt_ha_ / parts; + state.left = substep_a(full, index, parts); + const int right = index + 1; + state.right = substep_a(full, right, parts); + state.propagation = (state.left + state.right) * 0.5; + if (right == parts) + { + return state; + } + const int left_step = current_step_ - 1; + state.active = enabled_ && left_step >= start_step_ && left_step <= end_step_; + std::fill(state.values.begin(), state.values.end(), 0.0); + state.electric.set(0.0, 0.0, 0.0); + if (file_source()) + { + state.active = true; + state.electric = (full.left - full.right) * (1.0 / dt_ha_); + } + else if (state.active) + { + const double time = (left_step + static_cast(right) / parts) * dt_ha_; + const TDFieldSample sample(left_step, right, parts, time); + for (std::size_t i = 0; i < fields_.size(); ++i) + { + state.values[i] = fields_[i].electric_field(sample); + state.electric[fields_[i].direction()] += state.values[i]; + } + } + return state; +} + +double TDFieldManager::interval_dt_ha() const +{ + return interval_dt_ha_; +} + void TDFieldManager::write_restart(const std::string& file_dir) const { std::ofstream file((file_dir + "Restart_td.txt").c_str()); diff --git a/source/source_estate/module_pot/td_field_manager.h b/source/source_estate/module_pot/td_field_manager.h index fc905ec4874..e38d0553f0c 100644 --- a/source/source_estate/module_pot/td_field_manager.h +++ b/source/source_estate/module_pot/td_field_manager.h @@ -13,6 +13,18 @@ struct Input_para; namespace elecstate { +/** @brief Field values for one trial interval, independent of the committed step counter. */ +struct TDFieldState +{ + bool active = false; + double dt = 0.0; + std::vector values; + ModuleBase::Vector3 left; + ModuleBase::Vector3 right; + ModuleBase::Vector3 propagation; + ModuleBase::Vector3 electric; +}; + /** * @brief Own and advance all time-dependent electric fields in RT-TDDFT. * @@ -32,6 +44,15 @@ class TDFieldManager */ void prepare_interval(const int left_step, const int sample_step); + /** @brief Capture the current field without advancing the original sampling grid. */ + TDFieldState field_state() const; + /** @brief Bind trial fields without advancing or integrating the original interval again. */ + void bind_field(const TDFieldState& state); + /** @brief Evaluate interval [index/parts,(index+1)/parts] inside the prepared original step. */ + TDFieldState subinterval(const TDFieldState& full, int index, int parts) const; + /** @brief Duration of the currently bound trial interval in Hartree atomic units. */ + double interval_dt_ha() const; + /** @brief Supply endpoint samples starting at t=0 before preparing any steps. */ void set_A_samples(const std::vector>& samples_ha); @@ -95,6 +116,7 @@ class TDFieldManager int start_step_; int end_step_; double dt_ha_; + double interval_dt_ha_; double length_cut1_; double length_cut2_; std::vector fields_; @@ -109,6 +131,7 @@ class TDFieldManager ModuleBase::Vector3 efield_ha_; void sample_field(const int step); void select_A_prop(); + ModuleBase::Vector3 substep_a(const TDFieldState& full, int node, int parts) const; friend std::shared_ptr create_td_field_manager(const Input_para& input); }; diff --git a/source/source_hsolver/hsolver_pw_tddft.h b/source/source_hsolver/hsolver_pw_tddft.h index 5a92acfa8c6..929e2b964ff 100644 --- a/source/source_hsolver/hsolver_pw_tddft.h +++ b/source/source_hsolver/hsolver_pw_tddft.h @@ -66,6 +66,11 @@ class HSolverPWTDDFT void invalidate_basis(); /** @brief Reset diagnostics without invalidating preconditioner history. */ void reset_orth_stats(); + /** @brief Publish diagnostics accumulated over accepted trial intervals only. */ + void set_orth_stats(const TDOrthStats& stats) + { + orth_stats_ = stats; + } /** @brief Clear spinor padding without changing physical coefficients or the k-point cursor; no communication. */ void zero_padding(psi::Psi* current); /** @brief Orthogonalize initial orbitals with clean padding; return whether this pool changed any physical orbital. */ diff --git a/source/source_hsolver/linear_gmres.cpp b/source/source_hsolver/linear_gmres.cpp index 39471e34a4c..a880ca12deb 100644 --- a/source/source_hsolver/linear_gmres.cpp +++ b/source/source_hsolver/linear_gmres.cpp @@ -9,8 +9,8 @@ template LinearGMRES::LinearGMRES(double tolerance, const LinearSolveOptions& options, const diag_comm_info& comm) : tolerance_(tolerance), restart_(0), work_(comm, 2), algebra_(comm) { - const int iteration_limit = std::max(1, options.max_iterations); - restart_ = std::min(options.restart, iteration_limit); + const int iter_limit = std::max(1, options.max_iterations); + restart_ = std::min(options.restart, iter_limit); if (restart_ <= 0 || restart_ > (std::numeric_limits::max() - 1) / 3) { throw std::invalid_argument("Invalid GMRES restart dimension."); diff --git a/source/source_io/module_ctrl/ctrl_output_pw.cpp b/source/source_io/module_ctrl/ctrl_output_pw.cpp index ee1b36e18bf..0a3b13120dd 100644 --- a/source/source_io/module_ctrl/ctrl_output_pw.cpp +++ b/source/source_io/module_ctrl/ctrl_output_pw.cpp @@ -27,6 +27,7 @@ void ModuleIO::ctrl_iter_pw(const int istep, const K_Vectors& kv, const ModulePW::PW_Basis_K* pw_wfc, const Input_para& inp, + bool final_output, std::ofstream& ofs_running) { ModuleBase::TITLE("ModuleIO", "ctrl_iter_pw"); @@ -46,7 +47,7 @@ void ModuleIO::ctrl_iter_pw(const int istep, { if (istep % output_frequency == 0) { - if (iter % inp.out_freq_elec == 0 || iter == inp.scf_nmax || conv_esolver) + if (iter % inp.out_freq_elec == 0 || iter == inp.scf_nmax || conv_esolver || final_output) { istep_in = istep; iter_in = iter; @@ -54,7 +55,7 @@ void ModuleIO::ctrl_iter_pw(const int istep, } } } - else if (iter == inp.scf_nmax || conv_esolver) + else if (iter == inp.scf_nmax || conv_esolver || final_output) { out_wfc_flag = true; } diff --git a/source/source_io/module_ctrl/ctrl_output_pw.h b/source/source_io/module_ctrl/ctrl_output_pw.h index 9491afbf787..a3c05b0d4b3 100644 --- a/source/source_io/module_ctrl/ctrl_output_pw.h +++ b/source/source_io/module_ctrl/ctrl_output_pw.h @@ -21,6 +21,7 @@ void ctrl_iter_pw(const int istep, const K_Vectors& kv, const ModulePW::PW_Basis_K* pw_wfc, const Input_para& inp, + bool final_output, std::ofstream& ofs_running); // print out information in 'after_scf' in ESolver_KS_PW diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index f085028f9f9..c0a3f389da5 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -316,6 +316,8 @@ struct Input_para // ============== #Parameters (9.rt-tddft) =========================== double td_dt = -1.0; ///< time step for propagation + int td_max_halvings = 0; ///< Maximum depth of PW real-time interval bisection; zero disables retries. + int td_retry_nmax = 20; ///< SCF limit per trial interval when PW real-time retries are enabled. int estep_per_md = 1; ///< number of electronic steps per MD step std::string lin_solver = "gmres"; ///< linear solver for real-time propagation std::string td_orthonormal = "cholesky"; ///< Orthonormalization for PW real-time propagation. diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index 843624a46d2..661ed5d2a0c 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace ModuleIO @@ -225,7 +226,7 @@ void ReadInput::item_rt_tddft() item.annotation = "output PW real-time conservation diagnostics"; item.category = "Real-Time TDDFT (PW)"; item.type = "Boolean"; - item.description = R"(Write wavefunction and density electron counts, their changes from the initial state, and orthogonality errors to the running log after each electronic evolution step. Electron counts describe the end-of-step state; orthogonality errors before and after correction are separate maxima over all SCF iterations and k points within that step. Orthogonality errors are omitted when `td_orthonormal=none`. + item.description = R"(Write wavefunction and density electron counts, their changes from the initial state, and orthogonality errors to the running log after each electronic evolution step. Electron counts describe the end-of-step state; orthogonality errors before and after correction are separate maxima over all SCF iterations and k points within that step. When half-step retries are enabled, only accepted substeps contribute to these maxima. Orthogonality errors are omitted when `td_orthonormal=none`. [NOTE] Enabling this output adds density integration and diagnostic communication.)"; item.default_value = "false"; @@ -234,6 +235,50 @@ void ReadInput::item_rt_tddft() read_sync_bool(input.td_out_stat); this->add_item(item); } + { + Input_Item item("td_max_halvings"); + item.annotation = "maximum depth of PW real-time half-step retries"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Integer"; + item.description = R"(Maximum number of recursive halvings of an electronic propagation interval when self-consistency fails. Zero disables retries and retains the `scf_nmax` policy. A positive value enables trials with at most `td_retry_nmax` iterations each. The smallest internal time step is `td_dt / 2^td_max_halvings`; output times and the physical field profile retain the original `td_dt` grid. A value of 3 allows internal steps down to `td_dt / 8`.)"; + item.default_value = "0"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft"); + item.read_value = [](const Input_Item& item, Parameter& para) { + para.input.td_max_halvings = parse_td_integer(item); + }; + sync_int(input.td_max_halvings); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.td_max_halvings < 0 || para.inp.td_max_halvings >= std::numeric_limits::digits) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_max_halvings must be nonnegative and representable as an integer subdivision count."); + } + }; + this->add_item(item); + } + { + Input_Item item("td_retry_nmax"); + item.annotation = "SCF iteration limit per PW real-time trial interval"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Integer"; + item.description = R"(Maximum number of self-consistency iterations for each full-step or subdivided trial when `td_max_halvings` is positive. This includes the initial predictor and must be at least 2 when retries are enabled. + +Initial-state SCF still uses `scf_nmax`. This parameter has no effect when `td_max_halvings=0`.)"; + item.default_value = "20"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft"); + item.read_value = [](const Input_Item& item, Parameter& para) { + para.input.td_retry_nmax = parse_td_integer(item); + }; + sync_int(input.td_retry_nmax); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.td_retry_nmax < 1 || (para.inp.td_max_halvings > 0 && para.inp.td_retry_nmax < 2)) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_retry_nmax must be positive, and at least 2 when retries are enabled."); + } + }; + this->add_item(item); + } { Input_Item item("td_out_mag"); item.annotation = "RT-TDDFT spinor magnetic moment output"; @@ -390,7 +435,7 @@ All methods check the residual at the end of the linear solve, before orthonorma item.annotation = "maximum iterations per linear solve"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; - item.description = "Positive maximum number of iterations for each PW real-time linear solve. Failure to converge stops the calculation. The number of self-consistency iterations is controlled separately by `scf_nmax`."; + item.description = "Positive maximum number of iterations for each PW real-time linear solve. Failure to converge stops the calculation. Self-consistency uses `scf_nmax`, or `td_retry_nmax` for real-time trials when `td_max_halvings` is positive."; item.default_value = "500"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); From 85c45c8498f6f9b0b67b65d6df8ccc05526e1424 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 21 Oct 2026 20:04:07 +0800 Subject: [PATCH 05/33] Feature: Add independent td_scf_nmax limits for PW and LCAO RT-TDDFT --- source/source_esolver/esolver_ks.cpp | 24 ++++++++--- source/source_esolver/esolver_ks.h | 3 ++ source/source_esolver/esolver_ks_lcao.cpp | 9 ++-- .../source_esolver/esolver_ks_lcao_tddft.cpp | 32 ++++++++++---- source/source_esolver/esolver_ks_lcao_tddft.h | 3 ++ source/source_esolver/esolver_ks_pw.cpp | 8 ++-- source/source_esolver/esolver_ks_pw_tddft.cpp | 43 ++++++++++--------- source/source_esolver/esolver_ks_pw_tddft.h | 6 +-- source/source_estate/elecstate_print.cpp | 3 +- source/source_estate/elecstate_print.h | 2 + .../module_charge/chg_routine.cpp | 13 +++--- .../source_estate/module_charge/chg_routine.h | 3 ++ .../unittests/test_chg_routine.cpp | 6 ++- .../test/elecstate_print_test.cpp | 12 +++--- .../module_parameter/input_parameter.h | 2 +- .../module_parameter/read_inp_estruc.cpp | 2 +- .../module_parameter/read_inp_tddft.cpp | 26 ++++++----- 17 files changed, 124 insertions(+), 73 deletions(-) diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index b510a2bcb8f..c19d5656de0 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -181,12 +181,18 @@ void ESolver_KS::runner(BaseCell& basecell, const int istep) this->before_scf(ucell, istep); ModuleBase::GlobalFunc::DONE(GlobalV::ofs_running, "INIT SCF"); - const bool converged = this->scf_loop(ucell, istep, this->maxniter); + const int iter_limit = this->scf_iter_limit(istep); + const bool converged = this->scf_loop(ucell, istep, iter_limit); this->after_scf(ucell, istep, converged); this->conv_esolver = converged; ModuleBase::timer::end(this->classname, "runner"); } +int ESolver_KS::scf_iter_limit(const int istep) const +{ + return this->maxniter; +} + bool ESolver_KS::scf_loop(UnitCell& ucell, int istep, int iter_limit) { ModuleBase::timer::start("ESolver_KS", "scf_loop"); @@ -305,6 +311,7 @@ bool ESolver_KS::defer_iter_output(const int istep) const void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool &conv_esolver) { + const int iter_limit = this->scf_iter_limit(istep); // 1.1) print out band gap if (!PARAM.globalv.two_fermi) @@ -319,7 +326,7 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & // 1.2) print out eigenvalues and occupations if (this->inp_->out_band[0] && !this->defer_iter_output(istep)) { - if (iter % this->inp_->out_freq_elec == 0 || iter == this->inp_->scf_nmax || conv_esolver) + if (iter % this->inp_->out_freq_elec == 0 || iter == iter_limit || conv_esolver) { this->write_iter_bands(); } @@ -342,6 +349,7 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & #endif module_charge::ScfMixingCtx ctx; + ctx.iter_limit = iter_limit; ctx.hsolver_error = hsolver_error; ctx.scf_thr = this->scf_thr; ctx.scf_ene_thr = this->scf_ene_thr; @@ -398,16 +406,22 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & } // Iter finish - if (!this->defer_iter_output(istep)) + const bool output_charge = iter % this->inp_->out_freq_elec == 0 || conv_esolver || iter == iter_limit; + if (!this->defer_iter_output(istep) && output_charge) { - ESolver_FP::iter_finish(ucell, istep, iter, conv_esolver); + // The FP hook only writes density backups. Select output here using the + // active limit so its legacy scf_nmax check cannot trigger extra writes. + // This local flag forces output without changing actual convergence. + // Revisit this call if the base hook gains responsibilities beyond output. + bool force_output = true; + ESolver_FP::iter_finish(ucell, istep, iter, force_output); } // the end, print time double duration = ModuleBase::get_duration(iter_time, ModuleBase::get_time()); // print energies - elecstate::print_etot(ucell.magnet, *pelec, conv_esolver, iter, drho, + elecstate::print_etot(ucell.magnet, *pelec, conv_esolver, iter, iter_limit, drho, dkin, duration, *this->inp_, PARAM.globalv.two_fermi, diag_ethr, 0, true, this->ds_rms_); diff --git a/source/source_esolver/esolver_ks.h b/source/source_esolver/esolver_ks.h index 8a138fed150..6fde06b0d33 100644 --- a/source/source_esolver/esolver_ks.h +++ b/source/source_esolver/esolver_ks.h @@ -32,6 +32,9 @@ class ESolver_KS : public ESolver_FP virtual void after_all_runners(BaseCell& basecell) override; protected: + //! Maximum SCF iterations for the current electronic step. + virtual int scf_iter_limit(const int istep) const; + /** @brief Run SCF iterations without preparing or committing a physical endpoint. */ bool scf_loop(UnitCell& ucell, int istep, int iter_limit); /** @brief Record an accepted iteration in JSON without changing its convergence status. */ diff --git a/source/source_esolver/esolver_ks_lcao.cpp b/source/source_esolver/esolver_ks_lcao.cpp index 2622e593074..f476cf78d90 100644 --- a/source/source_esolver/esolver_ks_lcao.cpp +++ b/source/source_esolver/esolver_ks_lcao.cpp @@ -474,7 +474,9 @@ void ESolver_KS_LCAO::iter_init(UnitCell& ucell, const int istep, const // call iter_init() of ESolver_KS ESolver_KS::iter_init(ucell, istep, iter); - module_charge::chgmixing_ks_lcao(iter, this->p_chgmix, *this->dftu_, this->dmat.dm->get_dmr_ptr(1)->get_nnr(), *this->inp_); + const int iter_limit = this->scf_iter_limit(istep); + const int nnr = this->dmat.dm->get_dmr_ptr(1)->get_nnr(); + module_charge::chgmixing_ks_lcao(iter, iter_limit, this->p_chgmix, *this->dftu_, nnr, *this->inp_); if (iter == 1) { @@ -670,9 +672,10 @@ void ESolver_KS_LCAO::iter_finish(UnitCell& ucell, const int istep, int& const std::vector>& dm_vec = this->dmat.dm->get_dmk_vec(); // 1) calculate the local occupation number matrix and energy correction in DFT+U + const int iter_limit = this->scf_iter_limit(istep); const DFTU_BASE::OccmatOutputCfg occmat_cfg{this->inp_->out_freq_ion, this->inp_->out_freq_elec, - this->inp_->scf_nmax, + iter_limit, this->inp_->out_occ_mat, this->inp_->dft_plus_u}; // Read globalv values once into locals so the repeated DFT+U call sites @@ -751,7 +754,7 @@ void ESolver_KS_LCAO::iter_finish(UnitCell& ucell, const int istep, int& // Mix the density matrix after a mixing restart when mixing_dmr is enabled, // except at convergence or the maximum iteration count. - if (iter != this->inp_->scf_nmax && !conv_esolver) + if (iter != iter_limit && !conv_esolver) { if (this->inp_->mixing_restart > 0 && this->p_chgmix->mixing_restart_count > 0 && this->inp_->mixing_dmr) { diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index a5b2bb967d1..bc4c5bbc448 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -136,6 +136,13 @@ void ESolver_KS_LCAO_TDDFT::before_all_runners(BaseCell& basecell, c } } +template +int ESolver_KS_LCAO_TDDFT::scf_iter_limit(const int istep) const +{ + const int initial_step = this->inp_->init_wfc == "file" ? restart_step_ : 0; + return istep > initial_step ? this->inp_->td_scf_nmax : this->inp_->scf_nmax; +} + template void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int istep) { @@ -291,10 +298,13 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist // 2) SCF iterations //---------------------------------------------------------------- bool conv_esolver = false; - this->niter = this->maxniter; + const int iter_limit = this->scf_iter_limit(totstep); + this->niter = iter_limit; + this->scf_nmax_flag = false; this->diag_ethr = this->inp_->pw_diag_thr; - for (int iter = 1; iter <= this->maxniter; ++iter) + for (int iter = 1; iter <= iter_limit; ++iter) { + this->scf_nmax_flag = iter == iter_limit; ModuleIO::write_head_td(GlobalV::ofs_running, istep, totstep, iter, this->basisname); // 2.1) Initialization of the SCF iteration @@ -507,13 +517,19 @@ void ESolver_KS_LCAO_TDDFT::iter_finish(UnitCell& ucell, ESolver_KS_LCAO, TR>::iter_finish(ucell, istep, iter, conv_esolver); - // Store wave functions, Hamiltonian and overlap matrix, to be used in the next electronic step - // Store when converged or when the maximum iteration count is reached - bool force_save = conv_esolver || (iter == this->maxniter); - this->store_h_s_psi(ucell, istep, iter, force_save); + // Save the endpoint at convergence or the iteration limit for the next electronic step. + const int iter_limit = this->scf_iter_limit(istep); + const bool accept_state = conv_esolver || iter == iter_limit; + this->store_h_s_psi(ucell, istep, iter, accept_state); + + // At an ionic-step boundary, runner exits immediately after replaying the restart endpoint. + // Refresh EDM here before that early exit leads to force and stress calculations. + const bool replay_endpoint = this->inp_->mdp.md_restart && !restart_done && estep == 0 + && restart_step_ % this->inp_->estep_per_md == 0; + const bool last_endpoint = estep == estep_max - 1 || replay_endpoint; - // Calculate the RT-TDDFT energy-density matrix at the last converged substep when required by td_edm and the gauge - if (conv_esolver && estep == estep_max - 1 && istep >= (this->inp_->init_wfc == "file" ? 0 : 1) && this->inp_->td_edm == 0 + // Refresh the energy-density matrix at convergence or the iteration limit for forces and stress. + if (accept_state && last_endpoint && istep >= (this->inp_->init_wfc == "file" ? 0 : 1) && this->inp_->td_edm == 0 && this->inp_->td_stype != 2) { if (use_tensor && use_lapack) diff --git a/source/source_esolver/esolver_ks_lcao_tddft.h b/source/source_esolver/esolver_ks_lcao_tddft.h index b5fa43c01f9..b1401c16da9 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.h +++ b/source/source_esolver/esolver_ks_lcao_tddft.h @@ -33,6 +33,9 @@ class ESolver_KS_LCAO_TDDFT : public ESolver_KS_LCAO, TR> void before_all_runners(BaseCell& basecell, const Input_para& inp) override; protected: + //! Use the initial-state limit for initialization or restart replay, and the RT limit for propagation. + int scf_iter_limit(const int istep) const override; + //! Return the solver-owned RT state used by LCAO operators and output. TD_info* get_td_info() const override { return td_p; } diff --git a/source/source_esolver/esolver_ks_pw.cpp b/source/source_esolver/esolver_ks_pw.cpp index 3af0fecbebe..c700eefa055 100644 --- a/source/source_esolver/esolver_ks_pw.cpp +++ b/source/source_esolver/esolver_ks_pw.cpp @@ -251,7 +251,8 @@ void ESolver_KS_PW::iter_init(UnitCell& ucell, const int istep, const mag_converged = sc.mag_converged(); } - module_charge::chgmixing_ks_pw(iter, this->p_chgmix, *this->dftu_, mag_converged, *this->inp_); + const int iter_limit = this->scf_iter_limit(istep); + module_charge::chgmixing_ks_pw(iter, iter_limit, this->p_chgmix, *this->dftu_, mag_converged, *this->inp_); // mohan move harris functional here, 2012-06-05 // use 'rho(in)' and 'v_h and v_xc'(in) @@ -261,7 +262,7 @@ void ESolver_KS_PW::iter_init(UnitCell& ucell, const int istep, const // should before lambda loop in DeltaSpin const DFTU_BASE::OccmatOutputCfg init_occmat_cfg{this->inp_->out_freq_ion, this->inp_->out_freq_elec, - this->inp_->scf_nmax, + iter_limit, this->inp_->out_occ_mat, this->inp_->dft_plus_u}; DFTU_BASE::iter_init_dftu_pw(iter, @@ -400,9 +401,10 @@ void ESolver_KS_PW::iter_finish(UnitCell& ucell, const int istep, int // first step (istep 0, iter 1) the PW occupation matrix does not exist // yet (iter_init_dftu_pw returns before calculating it), so skip it and // keep the previous behavior where occ_mat.txt first appears at iter 2. + const int iter_limit = this->scf_iter_limit(istep); const DFTU_BASE::OccmatOutputCfg occmat_cfg{this->inp_->out_freq_ion, this->inp_->out_freq_elec, - this->inp_->scf_nmax, + iter_limit, this->inp_->out_occ_mat, this->inp_->dft_plus_u}; const DFTU_BASE::OccmatStepInfo occmat_step{istep, diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index a28629ccd8d..5a1f62c99e1 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -139,11 +139,11 @@ void ESolver_KS_PW_TDDFT::runner(BaseCell& basecell, const int istep) } for (int electronic_step = first_step; electronic_step <= last_step; ++electronic_step) { - if (this->retry_enabled(electronic_step)) + if (electronic_step > 0) { basecell.require_kind(BaseCell::Kind::unitcell, __FUNCTION__); UnitCell& cell = static_cast(basecell); - this->run_with_retries(cell, electronic_step); + this->run_propagation(cell, electronic_step); } else { @@ -155,13 +155,13 @@ void ESolver_KS_PW_TDDFT::runner(BaseCell& basecell, const int istep) template bool ESolver_KS_PW_TDDFT::defer_iter_output(const int istep) const { - return this->retry_enabled(istep); + return istep > 0; } template -bool ESolver_KS_PW_TDDFT::retry_enabled(int istep) const +int ESolver_KS_PW_TDDFT::scf_iter_limit(const int istep) const { - return istep > 0 && this->inp_->td_max_halvings > 0; + return istep > 0 ? this->inp_->td_scf_nmax : this->inp_->scf_nmax; } template @@ -231,7 +231,8 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, { std::cout << " TD substep: " << interval_text << std::endl; } - const bool converged = this->scf_loop(cell, step, this->inp_->td_retry_nmax); + const int iter_limit = this->scf_iter_limit(step); + const bool converged = this->scf_loop(cell, step, iter_limit); const bool accept_trial = converged || depth == this->inp_->td_max_halvings; log_ << " TD trial result: iterations=" << this->niter << ", residual=" << this->drho << ", converged=" << converged << ", accepted=" << accept_trial << '\n'; @@ -254,16 +255,23 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, stats.after = std::max(stats.after, trial_stats.after); const psi::Psi* current = this->stp.template get_psi_t(); this->history_.save(*current, *this->pelec->pot); - accepted = this->capture_state(); + if (this->inp_->td_max_halvings > 0) + { + accepted = this->capture_state(); + } return converged; } template -void ESolver_KS_PW_TDDFT::run_with_retries(UnitCell& cell, int step) +void ESolver_KS_PW_TDDFT::run_propagation(UnitCell& cell, int step) { - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_with_retries"); + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_propagation"); const ModuleBase::TimePoint started = ModuleBase::get_time(); - AcceptedState accepted = this->capture_state(); + AcceptedState accepted; + if (this->inp_->td_max_halvings > 0) + { + accepted = this->capture_state(); + } this->before_scf(cell, step); ModuleBase::GlobalFunc::DONE(log_, "INIT SCF"); const elecstate::TDFieldState full = this->td_field_manager_->field_state(); @@ -284,7 +292,7 @@ void ESolver_KS_PW_TDDFT::run_with_retries(UnitCell& cell, int step) this->output_field(step); this->after_scf(cell, step, converged); this->conv_esolver = converged; - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_with_retries"); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_propagation"); } template @@ -395,11 +403,6 @@ void ESolver_KS_PW_TDDFT::iter_init(UnitCell& ucell, const int istep, } ModuleIO::write_head_td(GlobalV::ofs_running, ionic_step, istep, iter, this->basisname); ESolver_KS_PW::iter_init(ucell, istep, iter); - if (this->retry_enabled(istep) && iter == 1) - { - // Apply the trial limit after the parent initializes the mixing machinery. - this->p_chgmix->reset_scf_history(this->inp_->td_retry_nmax); - } } template @@ -494,7 +497,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in template void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) { - if (!conv_esolver && this->niter < this->maxniter && !this->retry_enabled(istep)) + if (!conv_esolver && this->niter < this->maxniter && istep == 0) { const int evolution_step = istep + 1; const std::string message @@ -506,7 +509,7 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const psi::Psi* current = this->stp.template get_psi_t(); this->td_solver_->check_initial(*current, this->niter); } - if (!conv_esolver && !this->retry_enabled(istep)) + if (!conv_esolver && istep == 0) { finalize_endpoint(ucell); } @@ -524,8 +527,8 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, std::cout.precision(original_precision); if (istep >= 0) { - // Retry intervals already saved their accepted endpoint before reaching this hook. - if (!this->retry_enabled(istep)) + // Propagation intervals already saved their accepted endpoint before reaching this hook. + if (istep == 0) { psi::Psi* current = this->stp.template get_psi_t(); this->history_.save(*current, *this->pelec->pot); diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index c0d62b7702b..3af62a465aa 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -29,6 +29,8 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW void runner(BaseCell& basecell, const int istep) override; protected: + /** @brief Use independent iteration limits for initial-state SCF and propagation. */ + int scf_iter_limit(const int istep) const override; /** @brief Use ground-state diagonalization for the initial electronic step. */ std::string diag_policy(const int istep) const override; @@ -71,11 +73,9 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW ModuleBase::matrix eigenvalues; std::vector density; }; - /** @brief Whether the current electronic step uses half-step retries. */ - bool retry_enabled(int istep) const; AcceptedState capture_state() const; void restore_state(const AcceptedState& state); - void run_with_retries(UnitCell& cell, int step); + void run_propagation(UnitCell& cell, int step); bool advance_interval(UnitCell& cell, int step, int index, diff --git a/source/source_estate/elecstate_print.cpp b/source/source_estate/elecstate_print.cpp index 457c9483c89..4c4a2d9edf5 100644 --- a/source/source_estate/elecstate_print.cpp +++ b/source/source_estate/elecstate_print.cpp @@ -172,6 +172,7 @@ void print_etot(const Magnetism& magnet, const ElecState& elec, const bool converged, const int& iter_in, + const int iter_limit, const double& scf_thr, const double& scf_thr_kin, const double& duration, @@ -205,7 +206,7 @@ void print_etot(const Magnetism& magnet, // electronic step so E_plusU can be monitored. Charge-density and // wavefunction output remain gated by out_freq_elec elsewhere. const bool print_table = (iter % inp.out_freq_elec == 0) || converged - || (iter == inp.scf_nmax) || inp.dft_plus_u; + || (iter == iter_limit) || inp.dft_plus_u; if (print_table) { int n_order = std::max(0, Occupy::gaussian_type); diff --git a/source/source_estate/elecstate_print.h b/source/source_estate/elecstate_print.h index e6f89f5b969..1686eb2ff7e 100644 --- a/source/source_estate/elecstate_print.h +++ b/source/source_estate/elecstate_print.h @@ -12,10 +12,12 @@ namespace elecstate /// headers are printed /// @param two_fermi whether the run keeps two Fermi levels; derived, so it /// does not live in Input_para + /// @param iter_limit maximum iterations for this SCF attempt, including propagation void print_etot(const Magnetism& magnet, const ElecState& elec, const bool converged, const int& iter_in, + const int iter_limit, const double& scf_thr, const double& scf_thr_kin, const double& duration, diff --git a/source/source_estate/module_charge/chg_routine.cpp b/source/source_estate/module_charge/chg_routine.cpp index 938de4dcaab..020af1c3cd9 100644 --- a/source/source_estate/module_charge/chg_routine.cpp +++ b/source/source_estate/module_charge/chg_routine.cpp @@ -115,7 +115,7 @@ void module_charge::chgmixing_ks(const int iter, // SCF restart information if (inp.mixing_restart > 0 && iter == p_chgmix->mixing_restart_step - 1 - && iter != inp.scf_nmax) + && iter != ctx.iter_limit) { p_chgmix->mixing_restart_last = iter; std::cout << " SCF restart after this step!" << std::endl; @@ -126,6 +126,7 @@ void module_charge::chgmixing_ks(const int iter, void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number + const int iter_limit, Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, const bool mag_converged, ///< whether DeltaSpin magnetization converged (true when disabled) @@ -136,7 +137,7 @@ void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number if (iter == 1) { p_chgmix->init_mixing(); - p_chgmix->mixing_restart_step = inp.scf_nmax + 1; + p_chgmix->reset_scf_history(iter_limit); if (inp.dft_plus_u && dftu.has_occ_mixer()) { // allocate memory for uom_mdata sized to the flat occupation buffer @@ -154,7 +155,7 @@ void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number { if (dftu.get_uramping() > 0.01 && !dftu.u_converged()) { - p_chgmix->mixing_restart_step = inp.scf_nmax + 1; + p_chgmix->mixing_restart_step = iter_limit + 1; } if (dftu.get_uramping() > 0.01) { @@ -176,6 +177,7 @@ void module_charge::chgmixing_ks_pw(const int iter, // scf iteration number } void module_charge::chgmixing_ks_lcao(const int iter, // scf iteration number + const int iter_limit, Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, const int nnr, // dimension of density matrix @@ -186,8 +188,7 @@ void module_charge::chgmixing_ks_lcao(const int iter, // scf iteration number if (iter == 1) { p_chgmix->mix_reset(); // init mixing - p_chgmix->mixing_restart_step = inp.scf_nmax + 1; - p_chgmix->mixing_restart_count = 0; + p_chgmix->reset_scf_history(iter_limit); // this output will be removed once the feeature is stable if (dftu.get_uramping() > 0.01) { @@ -219,7 +220,7 @@ void module_charge::chgmixing_ks_lcao(const int iter, // scf iteration number } if (dftu.get_uramping() > 0.01 && !dftu.u_converged()) { - p_chgmix->mixing_restart_step = inp.scf_nmax + 1; + p_chgmix->mixing_restart_step = iter_limit + 1; } } if (inp.mixing_dmr) // for mixing_dmr diff --git a/source/source_estate/module_charge/chg_routine.h b/source/source_estate/module_charge/chg_routine.h index 19a67cd1335..2bfe5c83c39 100644 --- a/source/source_estate/module_charge/chg_routine.h +++ b/source/source_estate/module_charge/chg_routine.h @@ -17,6 +17,7 @@ namespace module_charge /// Aggregated SCF convergence thresholds and status flags for chgmixing_ks struct ScfMixingCtx { + int iter_limit; ///< maximum iterations for the current SCF attempt double hsolver_error; ///< solver error from diagonalization double scf_thr; ///< charge density convergence threshold double scf_ene_thr; ///< energy convergence threshold @@ -37,12 +38,14 @@ void chgmixing_ks(const int iter, const Input_para& inp); void chgmixing_ks_pw(const int iter, + const int iter_limit, Charge_Mixing* p_chgmix, Plus_U_Base& dftu, const bool mag_converged, ///< whether DeltaSpin magnetization converged; pass true when sc_mag_switch is off const Input_para& inp); // input parameters void chgmixing_ks_lcao(const int iter, // scf iteration number + const int iter_limit, Charge_Mixing* p_chgmix, // charge mixing class Plus_U_Base& dftu, const int nnr, // dimension of density matrix diff --git a/source/source_estate/module_charge/unittests/test_chg_routine.cpp b/source/source_estate/module_charge/unittests/test_chg_routine.cpp index 8755d6329b6..3b4de081d5f 100644 --- a/source/source_estate/module_charge/unittests/test_chg_routine.cpp +++ b/source/source_estate/module_charge/unittests/test_chg_routine.cpp @@ -75,7 +75,7 @@ TEST_F(ChgRoutineTest, ChgmixingKsPwIter1SetsRestartStep) inp.mixing_restart = 0.0; inp.dft_plus_u = false; - module_charge::chgmixing_ks_pw(1, &cm, dftu, true, inp); + module_charge::chgmixing_ks_pw(1, inp.scf_nmax, &cm, dftu, true, inp); EXPECT_EQ(cm.mixing_restart_step, inp.scf_nmax + 1); } @@ -90,7 +90,7 @@ TEST_F(ChgRoutineTest, ChgmixingKsLcaoIter1SetsRestartStep) inp.mixing_restart = 0.0; inp.dft_plus_u = false; - module_charge::chgmixing_ks_lcao(1, &cm, dftu, 0, inp); + module_charge::chgmixing_ks_lcao(1, inp.scf_nmax, &cm, dftu, 0, inp); EXPECT_EQ(cm.mixing_restart_step, inp.scf_nmax + 1); } @@ -107,6 +107,7 @@ TEST_F(ChgRoutineTest, ChgmixingKsConvergedSkipsMixing) inp.nelec = 1.0; module_charge::ScfMixingCtx ctx; + ctx.iter_limit = inp.scf_nmax; ctx.hsolver_error = 1e-6; ctx.scf_thr = 1e-4; ctx.scf_ene_thr = 0.0; @@ -138,6 +139,7 @@ TEST_F(ChgRoutineTest, ChgmixingKsDrhoBelowHsolverSkipsMixing) inp.nelec = 1.0; module_charge::ScfMixingCtx ctx; + ctx.iter_limit = inp.scf_nmax; ctx.hsolver_error = 1e-3; ctx.scf_thr = 1e-4; ctx.scf_ene_thr = 0.0; diff --git a/source/source_estate/test/elecstate_print_test.cpp b/source/source_estate/test/elecstate_print_test.cpp index c2d5bec1743..d2a8e0d058f 100644 --- a/source/source_estate/test/elecstate_print_test.cpp +++ b/source/source_estate/test/elecstate_print_test.cpp @@ -145,7 +145,7 @@ TEST_F(ElecStatePrintTest, PrintEtot) for (int i = 0; i < vdw_methods.size(); i++) { inp.vdw_method = vdw_methods[i]; - elecstate::print_etot(ucell.magnet,elecstate, converged, iter, scf_thr, + elecstate::print_etot(ucell.magnet,elecstate, converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, false); } @@ -156,7 +156,7 @@ TEST_F(ElecStatePrintTest, PrintEtot) inp.ks_solver = ks_solvers[i]; testing::internal::CaptureStdout(); - elecstate::print_etot(ucell.magnet,elecstate,converged, iter, scf_thr, + elecstate::print_etot(ucell.magnet,elecstate,converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, print); output = testing::internal::GetCapturedStdout(); @@ -224,7 +224,7 @@ TEST_F(ElecStatePrintTest, PrintEtotColorS2) inp.nspin = 2; GlobalV::MY_RANK = 0; - elecstate::print_etot(ucell.magnet,elecstate,converged, iter, scf_thr, + elecstate::print_etot(ucell.magnet,elecstate,converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, print); delete elecstate.charge; @@ -254,7 +254,7 @@ TEST_F(ElecStatePrintTest, PrintEtotColorS4) inp.noncolin = true; GlobalV::MY_RANK = 0; - elecstate::print_etot(ucell.magnet,elecstate, converged, iter, scf_thr, scf_thr_kin, + elecstate::print_etot(ucell.magnet,elecstate, converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, print); delete elecstate.charge; @@ -283,7 +283,7 @@ TEST_F(ElecStatePrintTest, PrintEtotSDFTPure) inp.ks_solver = "cg"; testing::internal::CaptureStdout(); - elecstate::print_etot(ucell.magnet, elecstate, converged, iter, scf_thr, + elecstate::print_etot(ucell.magnet, elecstate, converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, print); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("CT")); @@ -314,7 +314,7 @@ TEST_F(ElecStatePrintTest, PrintEtotSDFTMixed) inp.ks_solver = "dav"; testing::internal::CaptureStdout(); - elecstate::print_etot(ucell.magnet, elecstate, converged, iter, scf_thr, + elecstate::print_etot(ucell.magnet, elecstate, converged, iter, inp.scf_nmax, scf_thr, scf_thr_kin, duration, inp, two_fermi, pw_diag_thr, avg_iter, print); output = testing::internal::GetCapturedStdout(); EXPECT_THAT(output, testing::HasSubstr("DA")); diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index c0a3f389da5..64da203fa41 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -317,7 +317,7 @@ struct Input_para // ============== #Parameters (9.rt-tddft) =========================== double td_dt = -1.0; ///< time step for propagation int td_max_halvings = 0; ///< Maximum depth of PW real-time interval bisection; zero disables retries. - int td_retry_nmax = 20; ///< SCF limit per trial interval when PW real-time retries are enabled. + int td_scf_nmax = 100; ///< SCF limit per real-time propagation attempt, independent of initial-state SCF. int estep_per_md = 1; ///< number of electronic steps per MD step std::string lin_solver = "gmres"; ///< linear solver for real-time propagation std::string td_orthonormal = "cholesky"; ///< Orthonormalization for PW real-time propagation. diff --git a/source/source_io/module_parameter/read_inp_estruc.cpp b/source/source_io/module_parameter/read_inp_estruc.cpp index d7d90ecb422..4aeae3d1e83 100644 --- a/source/source_io/module_parameter/read_inp_estruc.cpp +++ b/source/source_io/module_parameter/read_inp_estruc.cpp @@ -787,7 +787,7 @@ Note: If gamma_only is set to 1, the KPT file will be overwritten. So make sure item.annotation = "number of electron iterations"; item.category = "Electronic structure"; item.type = "Integer"; - item.description = "This variable indicates the maximal iteration number for electronic iterations."; + item.description = "Maximum number of electronic self-consistency iterations. In PW and LCAO RT-TDDFT, this controls initial-state calculations; real-time propagation uses the independent `td_scf_nmax` limit."; item.default_value = "100"; item.unit = ""; read_sync_int(input.scf_nmax); diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index 661ed5d2a0c..9273345eee2 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -240,7 +240,7 @@ void ReadInput::item_rt_tddft() item.annotation = "maximum depth of PW real-time half-step retries"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; - item.description = R"(Maximum number of recursive halvings of an electronic propagation interval when self-consistency fails. Zero disables retries and retains the `scf_nmax` policy. A positive value enables trials with at most `td_retry_nmax` iterations each. The smallest internal time step is `td_dt / 2^td_max_halvings`; output times and the physical field profile retain the original `td_dt` grid. A value of 3 allows internal steps down to `td_dt / 8`.)"; + item.description = R"(Maximum number of recursive halvings of an electronic propagation interval when self-consistency fails. Zero disables retries. Each full-step or subdivided trial uses at most `td_scf_nmax` iterations. The smallest internal time step is `td_dt / 2^td_max_halvings`; output times and the physical field profile retain the original `td_dt` grid. A value of 3 allows internal steps down to `td_dt / 8`.)"; item.default_value = "0"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); @@ -257,24 +257,22 @@ void ReadInput::item_rt_tddft() this->add_item(item); } { - Input_Item item("td_retry_nmax"); - item.annotation = "SCF iteration limit per PW real-time trial interval"; - item.category = "Real-Time TDDFT (PW)"; + Input_Item item("td_scf_nmax"); + item.annotation = "SCF iteration limit per real-time propagation attempt"; + item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; - item.description = R"(Maximum number of self-consistency iterations for each full-step or subdivided trial when `td_max_halvings` is positive. This includes the initial predictor and must be at least 2 when retries are enabled. - -Initial-state SCF still uses `scf_nmax`. This parameter has no effect when `td_max_halvings=0`.)"; - item.default_value = "20"; + item.description = R"(Maximum number of self-consistency iterations for each real-time propagation step in PW and LCAO calculations. Must be an integer of at least 2. Convergence can end the iterations early. In PW calculations, when `td_max_halvings` is positive, the limit applies independently to each full-step or subdivided trial. This parameter is independent of `scf_nmax`.)"; + item.default_value = "100"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); + item.set_availability("esolver_type==tddft and basis_type in [pw, lcao]"); item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_retry_nmax = parse_td_integer(item); + para.input.td_scf_nmax = parse_td_integer(item); }; - sync_int(input.td_retry_nmax); + sync_int(input.td_scf_nmax); item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_retry_nmax < 1 || (para.inp.td_max_halvings > 0 && para.inp.td_retry_nmax < 2)) + if (para.inp.td_scf_nmax < 2) { - ModuleBase::WARNING_QUIT("ReadInput", "td_retry_nmax must be positive, and at least 2 when retries are enabled."); + ModuleBase::WARNING_QUIT("ReadInput", "td_scf_nmax must be an integer of at least 2."); } }; this->add_item(item); @@ -435,7 +433,7 @@ All methods check the residual at the end of the linear solve, before orthonorma item.annotation = "maximum iterations per linear solve"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; - item.description = "Positive maximum number of iterations for each PW real-time linear solve. Failure to converge stops the calculation. Self-consistency uses `scf_nmax`, or `td_retry_nmax` for real-time trials when `td_max_halvings` is positive."; + item.description = "Positive maximum number of iterations for each PW real-time linear solve. Failure to converge stops the calculation. Initial-state self-consistency uses `scf_nmax`; real-time self-consistency uses `td_scf_nmax`."; item.default_value = "500"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); From c5ff35e43308060f5b4b8073e5ed57d1596cde5b Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Fri, 23 Oct 2026 02:19:46 +0800 Subject: [PATCH 06/33] Change default gga_grad in RT-TDDFT to 2 --- source/source_io/module_parameter/read_inp_estruc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/source_io/module_parameter/read_inp_estruc.cpp b/source/source_io/module_parameter/read_inp_estruc.cpp index 4aeae3d1e83..777dbdbfe78 100644 --- a/source/source_io/module_parameter/read_inp_estruc.cpp +++ b/source/source_io/module_parameter/read_inp_estruc.cpp @@ -449,15 +449,15 @@ The other way is only available when compiling with LIBXC, and it allows for sup * `1`: Always uses the unsigned local magnetization magnitude in the built-in GGA gradient correction, independent of the global quantization axis. For Libxc functionals, `0` and `1` are equivalent. * `2`: Uses the regularized spin densities $\rho_{\pm}=\frac{|n+\rho_{\mathrm{core}}|\pm\min(S_{\eta}(r),|n+\rho_{\mathrm{core}}|)}{2}$, where $n$ is the valence electron density, $\rho_{\mathrm{core}}$ is the NLCC core density, and $r=|\boldsymbol{m}|$. With $x=r/\eta$ and fixed $\eta=10^{-3}\,\mathrm{electrons}/\mathrm{bohr}^{3}$, the $C^2$ mapping is $S_{\eta}(r)=\eta x^3(3x^2-8x+6)$ for $r\lt\eta$, and $S_{\eta}(r)=r$ otherwise. GGA gradients use these mapped densities. This regularization affects both LDA and GGA and can change energies and magnetic responses. -When omitted, the value is `1` for `esolver_type=tddft` and `0` otherwise. Explicit values are preserved.)"; - item.default_value = "`1` for `esolver_type=tddft`; `0` otherwise"; +When omitted, the value is `2` for `esolver_type=tddft` and `0` otherwise. Explicit values are preserved.)"; + item.default_value = "`2` for `esolver_type=tddft`; `0` otherwise"; item.unit = ""; item.domain.set_choices({"0", "1", "2"}); read_sync_int(input.gga_grad); item.reset_value = [](const Input_Item& item, Parameter& para) { if (!item.is_read() && para.inp.esolver_type == "tddft") { - para.input.gga_grad = 1; + para.input.gga_grad = 2; } }; this->add_item(item); From 2ee86119b4707269d7f173b7667355a9f12ecde2 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Fri, 23 Oct 2026 17:16:38 +0800 Subject: [PATCH 07/33] Refactor: Move PW RT-TDDFT code and tests into dedicated modules --- .../{hsolver_pw_tddft.cpp => module_rt_pw/cn_solver_pw.cpp} | 0 .../{hsolver_pw_tddft.h => module_rt_pw/cn_solver_pw.h} | 0 source/source_hsolver/{ => module_rt_pw}/cn_subspace.cpp | 0 source/source_hsolver/{ => module_rt_pw}/cn_subspace.h | 0 source/source_hsolver/{ => module_rt_pw}/hsolver_linear.cpp | 0 source/source_hsolver/{ => module_rt_pw}/hsolver_linear.h | 0 .../source_hsolver/{ => module_rt_pw}/kernels/cuda/linear_op.cu | 0 source/source_hsolver/{ => module_rt_pw}/kernels/linear_op.cpp | 0 source/source_hsolver/{ => module_rt_pw}/kernels/linear_op.h | 0 .../{ => module_rt_pw}/kernels/rocm/linear_op.hip.cu | 0 source/source_hsolver/{ => module_rt_pw}/linear_algebra.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_algebra.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_bicgstab.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_bicgstab.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_cgs.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_cgs.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_gmres.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_gmres.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_low_rank.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_low_rank.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_operator.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_solver_types.h | 0 source/source_hsolver/{ => module_rt_pw}/linear_workspace.cpp | 0 source/source_hsolver/{ => module_rt_pw}/linear_workspace.h | 0 source/source_hsolver/{ => module_rt_pw}/orthonormal.cpp | 0 source/source_hsolver/{ => module_rt_pw}/orthonormal.h | 0 source/source_hsolver/{ => module_rt_pw}/orthonormal_small.cpp | 0 source/source_hsolver/{ => module_rt_pw}/test/linear_test_utils.h | 0 .../source_hsolver/{ => module_rt_pw}/test/test_cn_subspace.cpp | 0 .../{ => module_rt_pw}/test/test_hsolver_linear.cpp | 0 .../{ => module_rt_pw}/test/test_hsolver_pw_tddft.cpp | 0 .../{ => module_rt_pw}/test/test_linear_low_rank.cpp | 0 .../source_hsolver/{ => module_rt_pw}/test/test_orthonormal.cpp | 0 source/source_pw/{module_pwdft => module_rt}/op_pw_ekin_td.cpp | 0 source/source_pw/{module_pwdft => module_rt}/op_pw_ekin_td.h | 0 source/source_pw/{module_pwdft => module_rt}/op_pw_nl_td.cpp | 0 source/source_pw/{module_pwdft => module_rt}/op_pw_nl_td.h | 0 source/source_pw/{module_pwdft => module_rt}/td_history_pw.cpp | 0 source/source_pw/{module_pwdft => module_rt}/td_history_pw.h | 0 source/source_pw/{module_pwdft => module_rt}/td_obs_pw.h | 0 source/source_pw/{module_pwdft => module_rt}/td_pw.cpp | 0 source/source_pw/{module_pwdft => module_rt}/td_pw.h | 0 source/source_pw/{module_pwdft => module_rt}/td_spin_pw.cpp | 0 source/source_pw/{module_pwdft => module_rt}/td_spin_pw.h | 0 source/source_pw/{module_pwdft => module_rt}/td_vel_pw.cpp | 0 source/source_pw/{module_pwdft => module_rt}/td_vel_pw.h | 0 source/source_pw/{module_pwdft => module_rt}/test/td_test_utils.h | 0 .../source_pw/{module_pwdft => module_rt}/test/test_td_obs_pw.cpp | 0 .../{module_pwdft => module_rt}/test/test_td_spin_pw.cpp | 0 .../source_pw/{module_pwdft => module_rt}/test/test_td_vel_pw.cpp | 0 50 files changed, 0 insertions(+), 0 deletions(-) rename source/source_hsolver/{hsolver_pw_tddft.cpp => module_rt_pw/cn_solver_pw.cpp} (100%) rename source/source_hsolver/{hsolver_pw_tddft.h => module_rt_pw/cn_solver_pw.h} (100%) rename source/source_hsolver/{ => module_rt_pw}/cn_subspace.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/cn_subspace.h (100%) rename source/source_hsolver/{ => module_rt_pw}/hsolver_linear.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/hsolver_linear.h (100%) rename source/source_hsolver/{ => module_rt_pw}/kernels/cuda/linear_op.cu (100%) rename source/source_hsolver/{ => module_rt_pw}/kernels/linear_op.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/kernels/linear_op.h (100%) rename source/source_hsolver/{ => module_rt_pw}/kernels/rocm/linear_op.hip.cu (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_algebra.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_algebra.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_bicgstab.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_bicgstab.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_cgs.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_cgs.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_gmres.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_gmres.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_low_rank.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_low_rank.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_operator.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_solver_types.h (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_workspace.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/linear_workspace.h (100%) rename source/source_hsolver/{ => module_rt_pw}/orthonormal.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/orthonormal.h (100%) rename source/source_hsolver/{ => module_rt_pw}/orthonormal_small.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/test/linear_test_utils.h (100%) rename source/source_hsolver/{ => module_rt_pw}/test/test_cn_subspace.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/test/test_hsolver_linear.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/test/test_hsolver_pw_tddft.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/test/test_linear_low_rank.cpp (100%) rename source/source_hsolver/{ => module_rt_pw}/test/test_orthonormal.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/op_pw_ekin_td.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/op_pw_ekin_td.h (100%) rename source/source_pw/{module_pwdft => module_rt}/op_pw_nl_td.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/op_pw_nl_td.h (100%) rename source/source_pw/{module_pwdft => module_rt}/td_history_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/td_history_pw.h (100%) rename source/source_pw/{module_pwdft => module_rt}/td_obs_pw.h (100%) rename source/source_pw/{module_pwdft => module_rt}/td_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/td_pw.h (100%) rename source/source_pw/{module_pwdft => module_rt}/td_spin_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/td_spin_pw.h (100%) rename source/source_pw/{module_pwdft => module_rt}/td_vel_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/td_vel_pw.h (100%) rename source/source_pw/{module_pwdft => module_rt}/test/td_test_utils.h (100%) rename source/source_pw/{module_pwdft => module_rt}/test/test_td_obs_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/test/test_td_spin_pw.cpp (100%) rename source/source_pw/{module_pwdft => module_rt}/test/test_td_vel_pw.cpp (100%) diff --git a/source/source_hsolver/hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp similarity index 100% rename from source/source_hsolver/hsolver_pw_tddft.cpp rename to source/source_hsolver/module_rt_pw/cn_solver_pw.cpp diff --git a/source/source_hsolver/hsolver_pw_tddft.h b/source/source_hsolver/module_rt_pw/cn_solver_pw.h similarity index 100% rename from source/source_hsolver/hsolver_pw_tddft.h rename to source/source_hsolver/module_rt_pw/cn_solver_pw.h diff --git a/source/source_hsolver/cn_subspace.cpp b/source/source_hsolver/module_rt_pw/cn_subspace.cpp similarity index 100% rename from source/source_hsolver/cn_subspace.cpp rename to source/source_hsolver/module_rt_pw/cn_subspace.cpp diff --git a/source/source_hsolver/cn_subspace.h b/source/source_hsolver/module_rt_pw/cn_subspace.h similarity index 100% rename from source/source_hsolver/cn_subspace.h rename to source/source_hsolver/module_rt_pw/cn_subspace.h diff --git a/source/source_hsolver/hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/hsolver_linear.cpp similarity index 100% rename from source/source_hsolver/hsolver_linear.cpp rename to source/source_hsolver/module_rt_pw/hsolver_linear.cpp diff --git a/source/source_hsolver/hsolver_linear.h b/source/source_hsolver/module_rt_pw/hsolver_linear.h similarity index 100% rename from source/source_hsolver/hsolver_linear.h rename to source/source_hsolver/module_rt_pw/hsolver_linear.h diff --git a/source/source_hsolver/kernels/cuda/linear_op.cu b/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu similarity index 100% rename from source/source_hsolver/kernels/cuda/linear_op.cu rename to source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu diff --git a/source/source_hsolver/kernels/linear_op.cpp b/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp similarity index 100% rename from source/source_hsolver/kernels/linear_op.cpp rename to source/source_hsolver/module_rt_pw/kernels/linear_op.cpp diff --git a/source/source_hsolver/kernels/linear_op.h b/source/source_hsolver/module_rt_pw/kernels/linear_op.h similarity index 100% rename from source/source_hsolver/kernels/linear_op.h rename to source/source_hsolver/module_rt_pw/kernels/linear_op.h diff --git a/source/source_hsolver/kernels/rocm/linear_op.hip.cu b/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu similarity index 100% rename from source/source_hsolver/kernels/rocm/linear_op.hip.cu rename to source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu diff --git a/source/source_hsolver/linear_algebra.cpp b/source/source_hsolver/module_rt_pw/linear_algebra.cpp similarity index 100% rename from source/source_hsolver/linear_algebra.cpp rename to source/source_hsolver/module_rt_pw/linear_algebra.cpp diff --git a/source/source_hsolver/linear_algebra.h b/source/source_hsolver/module_rt_pw/linear_algebra.h similarity index 100% rename from source/source_hsolver/linear_algebra.h rename to source/source_hsolver/module_rt_pw/linear_algebra.h diff --git a/source/source_hsolver/linear_bicgstab.cpp b/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp similarity index 100% rename from source/source_hsolver/linear_bicgstab.cpp rename to source/source_hsolver/module_rt_pw/linear_bicgstab.cpp diff --git a/source/source_hsolver/linear_bicgstab.h b/source/source_hsolver/module_rt_pw/linear_bicgstab.h similarity index 100% rename from source/source_hsolver/linear_bicgstab.h rename to source/source_hsolver/module_rt_pw/linear_bicgstab.h diff --git a/source/source_hsolver/linear_cgs.cpp b/source/source_hsolver/module_rt_pw/linear_cgs.cpp similarity index 100% rename from source/source_hsolver/linear_cgs.cpp rename to source/source_hsolver/module_rt_pw/linear_cgs.cpp diff --git a/source/source_hsolver/linear_cgs.h b/source/source_hsolver/module_rt_pw/linear_cgs.h similarity index 100% rename from source/source_hsolver/linear_cgs.h rename to source/source_hsolver/module_rt_pw/linear_cgs.h diff --git a/source/source_hsolver/linear_gmres.cpp b/source/source_hsolver/module_rt_pw/linear_gmres.cpp similarity index 100% rename from source/source_hsolver/linear_gmres.cpp rename to source/source_hsolver/module_rt_pw/linear_gmres.cpp diff --git a/source/source_hsolver/linear_gmres.h b/source/source_hsolver/module_rt_pw/linear_gmres.h similarity index 100% rename from source/source_hsolver/linear_gmres.h rename to source/source_hsolver/module_rt_pw/linear_gmres.h diff --git a/source/source_hsolver/linear_low_rank.cpp b/source/source_hsolver/module_rt_pw/linear_low_rank.cpp similarity index 100% rename from source/source_hsolver/linear_low_rank.cpp rename to source/source_hsolver/module_rt_pw/linear_low_rank.cpp diff --git a/source/source_hsolver/linear_low_rank.h b/source/source_hsolver/module_rt_pw/linear_low_rank.h similarity index 100% rename from source/source_hsolver/linear_low_rank.h rename to source/source_hsolver/module_rt_pw/linear_low_rank.h diff --git a/source/source_hsolver/linear_operator.h b/source/source_hsolver/module_rt_pw/linear_operator.h similarity index 100% rename from source/source_hsolver/linear_operator.h rename to source/source_hsolver/module_rt_pw/linear_operator.h diff --git a/source/source_hsolver/linear_solver_types.h b/source/source_hsolver/module_rt_pw/linear_solver_types.h similarity index 100% rename from source/source_hsolver/linear_solver_types.h rename to source/source_hsolver/module_rt_pw/linear_solver_types.h diff --git a/source/source_hsolver/linear_workspace.cpp b/source/source_hsolver/module_rt_pw/linear_workspace.cpp similarity index 100% rename from source/source_hsolver/linear_workspace.cpp rename to source/source_hsolver/module_rt_pw/linear_workspace.cpp diff --git a/source/source_hsolver/linear_workspace.h b/source/source_hsolver/module_rt_pw/linear_workspace.h similarity index 100% rename from source/source_hsolver/linear_workspace.h rename to source/source_hsolver/module_rt_pw/linear_workspace.h diff --git a/source/source_hsolver/orthonormal.cpp b/source/source_hsolver/module_rt_pw/orthonormal.cpp similarity index 100% rename from source/source_hsolver/orthonormal.cpp rename to source/source_hsolver/module_rt_pw/orthonormal.cpp diff --git a/source/source_hsolver/orthonormal.h b/source/source_hsolver/module_rt_pw/orthonormal.h similarity index 100% rename from source/source_hsolver/orthonormal.h rename to source/source_hsolver/module_rt_pw/orthonormal.h diff --git a/source/source_hsolver/orthonormal_small.cpp b/source/source_hsolver/module_rt_pw/orthonormal_small.cpp similarity index 100% rename from source/source_hsolver/orthonormal_small.cpp rename to source/source_hsolver/module_rt_pw/orthonormal_small.cpp diff --git a/source/source_hsolver/test/linear_test_utils.h b/source/source_hsolver/module_rt_pw/test/linear_test_utils.h similarity index 100% rename from source/source_hsolver/test/linear_test_utils.h rename to source/source_hsolver/module_rt_pw/test/linear_test_utils.h diff --git a/source/source_hsolver/test/test_cn_subspace.cpp b/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp similarity index 100% rename from source/source_hsolver/test/test_cn_subspace.cpp rename to source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp diff --git a/source/source_hsolver/test/test_hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp similarity index 100% rename from source/source_hsolver/test/test_hsolver_linear.cpp rename to source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp diff --git a/source/source_hsolver/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp similarity index 100% rename from source/source_hsolver/test/test_hsolver_pw_tddft.cpp rename to source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp diff --git a/source/source_hsolver/test/test_linear_low_rank.cpp b/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp similarity index 100% rename from source/source_hsolver/test/test_linear_low_rank.cpp rename to source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp diff --git a/source/source_hsolver/test/test_orthonormal.cpp b/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp similarity index 100% rename from source/source_hsolver/test/test_orthonormal.cpp rename to source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp diff --git a/source/source_pw/module_pwdft/op_pw_ekin_td.cpp b/source/source_pw/module_rt/op_pw_ekin_td.cpp similarity index 100% rename from source/source_pw/module_pwdft/op_pw_ekin_td.cpp rename to source/source_pw/module_rt/op_pw_ekin_td.cpp diff --git a/source/source_pw/module_pwdft/op_pw_ekin_td.h b/source/source_pw/module_rt/op_pw_ekin_td.h similarity index 100% rename from source/source_pw/module_pwdft/op_pw_ekin_td.h rename to source/source_pw/module_rt/op_pw_ekin_td.h diff --git a/source/source_pw/module_pwdft/op_pw_nl_td.cpp b/source/source_pw/module_rt/op_pw_nl_td.cpp similarity index 100% rename from source/source_pw/module_pwdft/op_pw_nl_td.cpp rename to source/source_pw/module_rt/op_pw_nl_td.cpp diff --git a/source/source_pw/module_pwdft/op_pw_nl_td.h b/source/source_pw/module_rt/op_pw_nl_td.h similarity index 100% rename from source/source_pw/module_pwdft/op_pw_nl_td.h rename to source/source_pw/module_rt/op_pw_nl_td.h diff --git a/source/source_pw/module_pwdft/td_history_pw.cpp b/source/source_pw/module_rt/td_history_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/td_history_pw.cpp rename to source/source_pw/module_rt/td_history_pw.cpp diff --git a/source/source_pw/module_pwdft/td_history_pw.h b/source/source_pw/module_rt/td_history_pw.h similarity index 100% rename from source/source_pw/module_pwdft/td_history_pw.h rename to source/source_pw/module_rt/td_history_pw.h diff --git a/source/source_pw/module_pwdft/td_obs_pw.h b/source/source_pw/module_rt/td_obs_pw.h similarity index 100% rename from source/source_pw/module_pwdft/td_obs_pw.h rename to source/source_pw/module_rt/td_obs_pw.h diff --git a/source/source_pw/module_pwdft/td_pw.cpp b/source/source_pw/module_rt/td_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/td_pw.cpp rename to source/source_pw/module_rt/td_pw.cpp diff --git a/source/source_pw/module_pwdft/td_pw.h b/source/source_pw/module_rt/td_pw.h similarity index 100% rename from source/source_pw/module_pwdft/td_pw.h rename to source/source_pw/module_rt/td_pw.h diff --git a/source/source_pw/module_pwdft/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/td_spin_pw.cpp rename to source/source_pw/module_rt/td_spin_pw.cpp diff --git a/source/source_pw/module_pwdft/td_spin_pw.h b/source/source_pw/module_rt/td_spin_pw.h similarity index 100% rename from source/source_pw/module_pwdft/td_spin_pw.h rename to source/source_pw/module_rt/td_spin_pw.h diff --git a/source/source_pw/module_pwdft/td_vel_pw.cpp b/source/source_pw/module_rt/td_vel_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/td_vel_pw.cpp rename to source/source_pw/module_rt/td_vel_pw.cpp diff --git a/source/source_pw/module_pwdft/td_vel_pw.h b/source/source_pw/module_rt/td_vel_pw.h similarity index 100% rename from source/source_pw/module_pwdft/td_vel_pw.h rename to source/source_pw/module_rt/td_vel_pw.h diff --git a/source/source_pw/module_pwdft/test/td_test_utils.h b/source/source_pw/module_rt/test/td_test_utils.h similarity index 100% rename from source/source_pw/module_pwdft/test/td_test_utils.h rename to source/source_pw/module_rt/test/td_test_utils.h diff --git a/source/source_pw/module_pwdft/test/test_td_obs_pw.cpp b/source/source_pw/module_rt/test/test_td_obs_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/test_td_obs_pw.cpp rename to source/source_pw/module_rt/test/test_td_obs_pw.cpp diff --git a/source/source_pw/module_pwdft/test/test_td_spin_pw.cpp b/source/source_pw/module_rt/test/test_td_spin_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/test_td_spin_pw.cpp rename to source/source_pw/module_rt/test/test_td_spin_pw.cpp diff --git a/source/source_pw/module_pwdft/test/test_td_vel_pw.cpp b/source/source_pw/module_rt/test/test_td_vel_pw.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/test_td_vel_pw.cpp rename to source/source_pw/module_rt/test/test_td_vel_pw.cpp From 0aa3f2850cc477f39bd56326d3a335c8fb4ed30f Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Sat, 24 Oct 2026 00:26:44 +0800 Subject: [PATCH 08/33] Feature: Add PW RT-TDDFT ETRS propagation with Lanczos and Taylor exponentials --- source/CMakeLists.txt | 8 +- source/Makefile.Objects | 14 +- source/source_esolver/esolver_ks_pw_tddft.cpp | 93 ++-- source/source_esolver/esolver_ks_pw_tddft.h | 10 +- source/source_estate/test/td_field_test.cpp | 62 +++ source/source_hsolver/CMakeLists.txt | 13 +- .../module_rt_pw/CMakeLists.txt | 26 ++ .../module_rt_pw/cn_solver_pw.cpp | 437 +++--------------- .../module_rt_pw/cn_solver_pw.h | 62 +-- .../module_rt_pw/cn_subspace.cpp | 6 +- .../source_hsolver/module_rt_pw/cn_subspace.h | 2 +- .../module_rt_pw/exp_action.cpp | 71 +++ .../source_hsolver/module_rt_pw/exp_action.h | 74 +++ .../module_rt_pw/exp_lanczos.cpp | 401 ++++++++++++++++ .../source_hsolver/module_rt_pw/exp_lanczos.h | 34 ++ .../module_rt_pw/exp_taylor.cpp | 98 ++++ .../source_hsolver/module_rt_pw/exp_taylor.h | 21 + .../module_rt_pw/hsolver_linear.cpp | 2 +- .../module_rt_pw/hsolver_linear.h | 6 +- .../module_rt_pw/kernels/cuda/linear_op.cu | 2 +- .../module_rt_pw/kernels/linear_op.cpp | 2 +- .../kernels/rocm/linear_op.hip.cu | 2 +- .../module_rt_pw/linear_algebra.cpp | 4 +- .../module_rt_pw/linear_bicgstab.cpp | 4 +- .../module_rt_pw/linear_bicgstab.h | 2 +- .../module_rt_pw/linear_cgs.cpp | 2 +- .../source_hsolver/module_rt_pw/linear_cgs.h | 2 +- .../module_rt_pw/linear_gmres.cpp | 2 +- .../module_rt_pw/linear_gmres.h | 4 +- .../module_rt_pw/linear_low_rank.cpp | 28 +- .../module_rt_pw/linear_low_rank.h | 4 +- .../module_rt_pw/linear_workspace.cpp | 4 +- .../module_rt_pw/linear_workspace.h | 4 +- .../module_rt_pw/orb_buffer.cpp | 55 +++ .../source_hsolver/module_rt_pw/orb_buffer.h | 28 ++ .../module_rt_pw/orthonormal.cpp | 2 +- .../source_hsolver/module_rt_pw/orthonormal.h | 2 +- .../module_rt_pw/orthonormal_small.cpp | 2 +- .../module_rt_pw/td_orbital_ops.cpp | 321 +++++++++++++ .../module_rt_pw/td_orbital_ops.h | 80 ++++ .../module_rt_pw/test/CMakeLists.txt | 34 ++ .../module_rt_pw/test/test_cn_subspace.cpp | 4 +- .../module_rt_pw/test/test_exp_action.cpp | 266 +++++++++++ .../module_rt_pw/test/test_hsolver_linear.cpp | 4 +- .../test/test_hsolver_pw_tddft.cpp | 55 ++- .../test/test_linear_low_rank.cpp | 4 +- .../module_rt_pw/test/test_orthonormal.cpp | 2 +- source/source_hsolver/test/CMakeLists.txt | 28 -- .../source_io/module_current/td_current_pw.h | 2 +- .../source_io/module_current/td_spin_pw_io.h | 2 +- .../module_parameter/input_parameter.h | 4 + .../module_parameter/read_inp_exx_dftu.cpp | 9 +- .../module_parameter/read_inp_tddft.cpp | 220 +++++---- source/source_pw/CMakeLists.txt | 1 + source/source_pw/module_pwdft/CMakeLists.txt | 6 - source/source_pw/module_pwdft/hamilt_pw.cpp | 50 +- .../module_pwdft/test/CMakeLists.txt | 40 -- source/source_pw/module_rt/CMakeLists.txt | 20 + source/source_pw/module_rt/op_pw_ekin_td.cpp | 2 +- source/source_pw/module_rt/op_pw_ekin_td.h | 7 +- source/source_pw/module_rt/op_pw_nl_td.cpp | 2 +- source/source_pw/module_rt/op_pw_nl_td.h | 2 +- source/source_pw/module_rt/prop_cn_pw.cpp | 51 ++ source/source_pw/module_rt/prop_cn_pw.h | 29 ++ source/source_pw/module_rt/prop_etrs_pw.cpp | 133 ++++++ source/source_pw/module_rt/prop_etrs_pw.h | 29 ++ source/source_pw/module_rt/td_hamilt_pw.cpp | 39 ++ source/source_pw/module_rt/td_hamilt_pw.h | 28 ++ source/source_pw/module_rt/td_history_pw.cpp | 32 +- source/source_pw/module_rt/td_history_pw.h | 12 +- source/source_pw/module_rt/td_prop_pw.cpp | 109 +++++ source/source_pw/module_rt/td_prop_pw.h | 90 ++++ source/source_pw/module_rt/td_pw.cpp | 2 +- source/source_pw/module_rt/td_spin_pw.cpp | 4 +- source/source_pw/module_rt/td_spin_pw.h | 2 +- source/source_pw/module_rt/td_vel_pw.cpp | 9 +- source/source_pw/module_rt/td_vel_pw.h | 2 +- .../source_pw/module_rt/test/CMakeLists.txt | 45 ++ .../source_pw/module_rt/test/td_test_utils.h | 4 +- .../module_rt/test/test_td_obs_pw.cpp | 2 +- .../module_rt/test/test_td_spin_pw.cpp | 2 +- .../module_rt/test/test_td_vel_pw.cpp | 2 +- tests/05_rtTDDFT/31_PW_retry_TDDFT/INPUT | 54 +++ tests/05_rtTDDFT/31_PW_retry_TDDFT/KPT | 5 + tests/05_rtTDDFT/31_PW_retry_TDDFT/README | 1 + tests/05_rtTDDFT/31_PW_retry_TDDFT/STRU | 18 + .../31_PW_retry_TDDFT/current_tot.txt.ref | 3 + tests/05_rtTDDFT/31_PW_retry_TDDFT/result.ref | 5 + .../31_PW_retry_TDDFT/vector_pot.txt.ref | 4 + .../05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT | 63 +++ tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/KPT | 5 + .../32_PW_ETRS_lanczos_TDDFT/README | 1 + .../05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/STRU | 21 + .../current_tot.txt.ref | 3 + .../32_PW_ETRS_lanczos_TDDFT/mag_atom.txt.ref | 8 + .../32_PW_ETRS_lanczos_TDDFT/mag_tot.txt.ref | 5 + .../32_PW_ETRS_lanczos_TDDFT/result.ref | 9 + .../spincur_tot.txt.ref | 5 + .../torque_atom.txt.ref | 9 + .../torque_tot.txt.ref | 5 + .../05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT | 55 +++ tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/KPT | 5 + .../05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/README | 1 + tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/STRU | 18 + .../current_tot.txt.ref | 3 + .../33_PW_ETRS_taylor_TDDFT/result.ref | 5 + .../vector_pot.txt.ref | 4 + tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT | 56 +++ tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/KPT | 5 + .../05_rtTDDFT/34_PW_ETRS_retry_TDDFT/README | 1 + tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/STRU | 18 + .../current_tot.txt.ref | 3 + .../34_PW_ETRS_retry_TDDFT/result.ref | 5 + .../34_PW_ETRS_retry_TDDFT/vector_pot.txt.ref | 4 + tests/05_rtTDDFT/CASES_CPU.txt | 4 + tests/05_rtTDDFT/CASES_GPU.txt | 4 + 116 files changed, 2963 insertions(+), 838 deletions(-) create mode 100644 source/source_hsolver/module_rt_pw/CMakeLists.txt create mode 100644 source/source_hsolver/module_rt_pw/exp_action.cpp create mode 100644 source/source_hsolver/module_rt_pw/exp_action.h create mode 100644 source/source_hsolver/module_rt_pw/exp_lanczos.cpp create mode 100644 source/source_hsolver/module_rt_pw/exp_lanczos.h create mode 100644 source/source_hsolver/module_rt_pw/exp_taylor.cpp create mode 100644 source/source_hsolver/module_rt_pw/exp_taylor.h create mode 100644 source/source_hsolver/module_rt_pw/orb_buffer.cpp create mode 100644 source/source_hsolver/module_rt_pw/orb_buffer.h create mode 100644 source/source_hsolver/module_rt_pw/td_orbital_ops.cpp create mode 100644 source/source_hsolver/module_rt_pw/td_orbital_ops.h create mode 100644 source/source_hsolver/module_rt_pw/test/CMakeLists.txt create mode 100644 source/source_hsolver/module_rt_pw/test/test_exp_action.cpp create mode 100644 source/source_pw/module_rt/CMakeLists.txt create mode 100644 source/source_pw/module_rt/prop_cn_pw.cpp create mode 100644 source/source_pw/module_rt/prop_cn_pw.h create mode 100644 source/source_pw/module_rt/prop_etrs_pw.cpp create mode 100644 source/source_pw/module_rt/prop_etrs_pw.h create mode 100644 source/source_pw/module_rt/td_hamilt_pw.cpp create mode 100644 source/source_pw/module_rt/td_hamilt_pw.h create mode 100644 source/source_pw/module_rt/td_prop_pw.cpp create mode 100644 source/source_pw/module_rt/td_prop_pw.h create mode 100644 source/source_pw/module_rt/test/CMakeLists.txt create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/README create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/vector_pot.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/README create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_atom.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_tot.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/spincur_tot.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_atom.txt.ref create mode 100644 tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_tot.txt.ref create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/README create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/vector_pot.txt.ref create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/KPT create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/README create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/STRU create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/current_tot.txt.ref create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/result.ref create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/vector_pot.txt.ref diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index d849396a7d0..d2d0e6e99a9 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -641,7 +641,7 @@ list( source_basis/module_pw/kernels/pw_op.cpp source_hsolver/kernels/hegvd_op.cpp source_hsolver/kernels/bpcg_kernel_op.cpp - source_hsolver/kernels/linear_op.cpp + source_hsolver/module_rt_pw/kernels/linear_op.cpp source_estate/kernels/elecstate_op.cpp source_base/module_device/device.cpp source_base/module_device/device_helpers.cpp @@ -675,7 +675,7 @@ if(USE_CUDA) source_basis/module_pw/kernels/cuda/pw_op.cu source_hsolver/kernels/cuda/hegvd_op.cu source_hsolver/kernels/cuda/bpcg_kernel_op.cu - source_hsolver/kernels/cuda/linear_op.cu + source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu source_estate/kernels/cuda/elecstate_op.cu source_base/module_device/cuda/memory_op.cu source_pw/module_pwdft/kernels/cuda/force_op.cu @@ -714,7 +714,7 @@ if(USE_ROCM) source_basis/module_pw/kernels/rocm/pw_op.hip.cu source_hsolver/kernels/rocm/hegvd_op.hip.cu source_hsolver/kernels/rocm/bpcg_kernel_op.hip.cu - source_hsolver/kernels/rocm/linear_op.hip.cu + source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu source_estate/kernels/rocm/elecstate_op.hip.cu source_base/module_device/rocm/memory_op.hip.cu source_pw/module_pwdft/kernels/rocm/force_op.hip.cu @@ -757,9 +757,11 @@ target_link_libraries( driver esolver hsolver + hsolver_rt hamilt_general elecstate module_pwdft + module_rt_pw module_proj nao_smoothgen module_ofdft diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 1650756dad5..3b77bd677c3 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -36,6 +36,8 @@ VPATH=./src_global:\ ./source_base/module_fft:\ ./source_esolver:\ ./source_hsolver:\ +./source_hsolver/module_rt_pw:\ +./source_hsolver/module_rt_pw/kernels:\ ./source_hsolver/kernels:\ ./source_hsolver/module_genelpa:\ ./source_hsolver/module_pexsi:\ @@ -54,6 +56,7 @@ VPATH=./src_global:\ ./source_hamilt/module_xc/kernels:\ ./source_pw/module_proj:\ ./source_pw/module_pwdft:\ +./source_pw/module_rt:\ ./source_pw/module_ofdft:\ ./source_pw/module_stodft:\ ./source_pw/module_pwdft/kernels:\ @@ -380,6 +383,10 @@ OBJS_GINT=batch_biggrid.o\ OBJS_HAMILT=hamilt_pw.o\ pw_hybrid_nscf.o\ pw_hybrid_source.o\ + td_prop_pw.o\ + td_hamilt_pw.o\ + prop_cn_pw.o\ + prop_etrs_pw.o\ td_history_pw.o\ td_vel_pw.o\ td_spin_pw.o\ @@ -491,7 +498,12 @@ OBJS_HSOLVER=diago_cg.o\ para_lin_tf.o\ hsolver.o\ hsolver_pw.o\ - hsolver_pw_tddft.o\ + cn_solver_pw.o\ + td_orbital_ops.o\ + orb_buffer.o\ + exp_action.o\ + exp_taylor.o\ + exp_lanczos.o\ orthonormal.o\ orthonormal_small.o\ hsolver_lcaopw.o\ diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 5a1f62c99e1..83df000aaf8 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -21,7 +21,7 @@ #include "source_io/module_output/output_log.h" #include "source_io/module_parameter/parameter.h" #include "source_pw/module_pwdft/hamilt_pw.h" -#include "source_pw/module_pwdft/td_pw.h" +#include "source_pw/module_rt/td_pw.h" #include #include @@ -87,20 +87,9 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons #else const hsolver::diag_comm_info comm(0, 1); #endif - hsolver::PWLinearOptions options; - options.npol = ucell.get_npol(); - options.orthonormal = hsolver::parse_orth_method(inp.td_orthonormal); - options.out_stat = inp.td_out_stat; - options.global_k_indices = this->kv.ik2iktot; - options.linear.method = hsolver::parse_linear_method(inp.lin_solver); - options.linear.tolerance = inp.lin_thr; - options.linear.max_iterations = inp.lin_maxiter; - options.linear.restart = inp.lin_gmres_restart; - options.linear.reconstruct = inp.lin_reconstruct; - options.preconditioner = hsolver::parse_pw_precond(inp.lin_precond); - options.cn_init = inp.td_cn_init; - options.kinetic_enabled = inp.t_in_h; - this->td_solver_.reset(new hsolver::HSolverPWTDDFT(*this->pw_wfc, options, comm, log_)); + const int npol = ucell.get_npol(); + const pw::TDPropOpts options = pw::td_prop_options(inp, npol, this->kv.ik2iktot); + this->td_prop_.reset(new pw::TDPropPW(*this->pw_wfc, options, comm, log_)); this->history_.prepare(*this->pelec->pot); // Preserve existing field history until input validation and initialization succeed. if (GlobalV::MY_RANK == 0) @@ -194,8 +183,8 @@ void ESolver_KS_PW_TDDFT::restore_state(const AcceptedState& state) this->chr.save_rho_before_sum_band(); // The caller binds the trial field and rebuilds the endpoint potential. The saved // left-endpoint potential used by the corrector is already held in history_. - this->td_solver_->invalidate_basis(); - this->td_solver_->reset_orth_stats(); + this->td_prop_->invalidate_basis(); + this->td_prop_->orbitals().reset_orth_stats(); ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "restore_state"); } @@ -232,6 +221,7 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, std::cout << " TD substep: " << interval_text << std::endl; } const int iter_limit = this->scf_iter_limit(step); + this->td_prop_->begin_trial(); const bool converged = this->scf_loop(cell, step, iter_limit); const bool accept_trial = converged || depth == this->inp_->td_max_halvings; log_ << " TD trial result: iterations=" << this->niter << ", residual=" << this->drho << ", converged=" << converged @@ -239,6 +229,7 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, if (!converged && depth < this->inp_->td_max_halvings) { std::cout << " TD retry: SCF not converged; splitting " << interval_text << " into two half steps." << std::endl; + this->td_prop_->discard_trial(); const int next_depth = depth + 1; const int left_index = 2 * index; const int right_index = left_index + 1; @@ -250,11 +241,12 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, { this->finalize_endpoint(cell); } - const hsolver::TDOrthStats& trial_stats = this->td_solver_->orth_stats(); + const hsolver::TDOrthStats& trial_stats = this->td_prop_->orbitals().orth_stats(); stats.before = std::max(stats.before, trial_stats.before); stats.after = std::max(stats.after, trial_stats.after); const psi::Psi* current = this->stp.template get_psi_t(); this->history_.save(*current, *this->pelec->pot); + this->td_prop_->finish_trial(); if (this->inp_->td_max_halvings > 0) { accepted = this->capture_state(); @@ -279,7 +271,7 @@ void ESolver_KS_PW_TDDFT::run_propagation(UnitCell& cell, int step) const bool converged = advance_interval(cell, step, 0, 0, full, accepted, stats); // Restore original-grid sampling metadata without altering the accepted endpoint. this->td_field_manager_->bind_field(full); - this->td_solver_->set_orth_stats(stats); + this->td_prop_->orbitals().set_orth_stats(stats); this->chr.save_rho_before_sum_band(); // Taoni add 2026-10-20: ESolver_FP::iter_finish currently only writes density backups. // Revisit this call if the base hook gains responsibilities beyond output. @@ -309,12 +301,12 @@ void ESolver_KS_PW_TDDFT::before_scf(UnitCell& ucell, const int istep { this->print_step(istep); this->prep_td_step(istep); - this->td_solver_->reset_orth_stats(); + this->td_prop_->orbitals().reset_orth_stats(); const bool basis_updated = ucell.cell_parameter_updated; ESolver_KS_PW::before_scf(ucell, istep); if (basis_updated) { - this->td_solver_->invalidate_basis(); + this->td_prop_->invalidate_basis(); } this->history_.prepare(*this->pelec->pot); } @@ -324,9 +316,11 @@ void ESolver_KS_PW_TDDFT::prepare_init_hamilt(const UnitCell& ucell) { if (this->td_field_manager_->gauge() == 1) { - // The updated basis and endpoint A determine the required nonlocal projector range. + // Both endpoints also bound any interpolated sample inside the interval. const double q_unshifted = pw::td_momentum_bound(*this->pw_wfc, ucell.tpiba); - pw::ensure_td_vnl(ucell, q_unshifted, this->td_field_manager_->A_right_ha(), this->td_field_manager_->A_prop_ha(), &this->ppcell); + const auto& left = this->td_field_manager_->A_left_ha(); + const auto& right = this->td_field_manager_->A_right_ha(); + pw::ensure_td_vnl(ucell, q_unshifted, right, left, &this->ppcell); elecstate::Potential& potential = *this->pelec->pot; const Real* veff = potential.template get_veff_smooth_data(); auto* hamiltonian = static_cast*>(this->p_hamilt); @@ -408,7 +402,7 @@ void ESolver_KS_PW_TDDFT::iter_init(UnitCell& ucell, const int istep, template ESolver_KS::DensityStage ESolver_KS_PW_TDDFT::density_stage(const int istep, const int iter) const { - if (istep > 0 && iter == 1) + if (istep > 0 && this->td_prop_->stage(iter) == pw::PropStage::predictor) { return ESolver_KS::DensityStage::predictor; } @@ -424,17 +418,17 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in ESolver_KS_PW::hamilt2rho_single(ucell, istep, iter, ethr); psi::Psi* current = this->stp.template get_psi_t(); // Padding is a storage invariant even with orthogonalization disabled; clearing it does not change the density. - this->td_solver_->zero_padding(current); + this->td_prop_->orbitals().zero_padding(current); if (this->inp_->td_orthonormal != "none") { - int changed = this->td_solver_->orth_initial(current, iter); + int changed = this->td_prop_->orbitals().orth_initial(current, iter); // Occupations and density construction contain collectives across k-point pools. Parallel_Reduce::reduce_all(changed); if (changed > 0) { hamilt::Hamilt* hamiltonian = static_cast*>(this->p_hamilt); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - this->td_solver_->cal_band_energy(op, *current, &this->pelec->ekb); + this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); elecstate::calculate_weights(this->pelec->ekb, this->pelec->wg, this->pelec->klist, @@ -459,29 +453,9 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in hamilt::HamiltPW* hamiltonian = static_cast*>(this->p_hamilt); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - // Predict with the current potential, then correct with the midpoint. - const Real* endpoint_veff = potential.template get_veff_smooth_data(); - const ESolver_KS::DensityStage stage = this->density_stage(istep, iter); - const bool corrector = stage != ESolver_KS::DensityStage::predictor; - const Real* propagation = this->history_.prop_pot(potential, corrector); - ModuleBase::Vector3 momentum_shift(0.0, 0.0, 0.0); - if (this->td_field_manager_->gauge() == 1) - { - momentum_shift = this->td_field_manager_->A_prop_ha(); - } - hamiltonian->bind_td_state(propagation, momentum_shift); - // The solver sequence follows electronic steps across MD boundaries. - this->td_solver_->solve(op, - this->history_.previous(), - current, - this->td_field_manager_->interval_dt_ha(), - momentum_shift, - istep, - iter, - this->inp_->out_level == "ie"); - - // Restore the endpoint Hamiltonian before evaluating density and energy. - hamiltonian->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); + const bool detailed = this->inp_->out_level == "ie"; + this->td_prop_->evolve(*hamiltonian, potential, this->history_, *this->td_field_manager_, current, istep, iter, detailed); + elecstate::ElecStatePW* estate = static_cast*>(this->pelec); // Propagation keeps the occupations of the initial state fixed. estate->psiToRho(*current); @@ -489,7 +463,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in // Pair the band sum with KS double-counting terms using this same potential. // The later KS potential update belongs to the next SCF iteration. - this->td_solver_->cal_band_energy(op, *current, &this->pelec->ekb); + this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); } @@ -497,17 +471,10 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in template void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) { - if (!conv_esolver && this->niter < this->maxniter && istep == 0) - { - const int evolution_step = istep + 1; - const std::string message - = "Cannot propagate an unconverged electronic state. Electronic evolution step: " + std::to_string(evolution_step) + "."; - ModuleBase::WARNING_QUIT("ESolver_KS_PW_TDDFT", message); - } if (istep == 0 && this->inp_->td_orthonormal == "none") { const psi::Psi* current = this->stp.template get_psi_t(); - this->td_solver_->check_initial(*current, this->niter); + this->td_prop_->orbitals().check_initial(*current, this->niter); } if (!conv_esolver && istep == 0) { @@ -558,7 +525,7 @@ void ESolver_KS_PW_TDDFT::finalize_endpoint(UnitCell& cell) const Real* veff = this->pelec->pot->template get_veff_smooth_data(); hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - this->td_solver_->cal_band_energy(op, *current, &this->pelec->ekb); + this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); this->pelec->f_en.deband = this->pelec->cal_delta_eband(cell); this->pelec->f_en.descf = 0.0; @@ -636,7 +603,7 @@ void ESolver_KS_PW_TDDFT::print_eij(const int istep) std::vector> eij; if (pools.my_pool == owner) { - eij = this->td_solver_->cal_eij(op, *current, local_k); + eij = this->td_prop_->orbitals().cal_eij(op, *current, local_k); } #ifdef __MPI // Each pool already has the full matrix; transfer only one copy to the main log. @@ -678,7 +645,7 @@ template void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int istep) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "report_orth"); - double electrons = this->td_solver_->wave_electrons(this->pelec->wg); + double electrons = this->td_prop_->orbitals().wave_electrons(this->pelec->wg); // Norms are replicated within a pool; only its root contributes to the total. if (this->pw_wfc->poolrank != 0) { @@ -701,7 +668,7 @@ void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int iste initial_wave_electrons_ = electrons; initial_rho_electrons_ = rho_electrons; } - const hsolver::TDOrthStats& stats = this->td_solver_->orth_stats(); + const hsolver::TDOrthStats& stats = this->td_prop_->orbitals().orth_stats(); const bool full_gram = this->inp_->td_orthonormal != "none"; double maxima[] = {stats.before, stats.after}; if (full_gram) diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index 3af62a465aa..69824b80faf 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -3,10 +3,10 @@ #include "source_esolver/esolver_ks_pw.h" #include "source_estate/module_pot/td_field_manager.h" -#include "source_hsolver/hsolver_pw_tddft.h" -#include "source_pw/module_pwdft/td_history_pw.h" -#include "source_pw/module_pwdft/td_spin_pw.h" -#include "source_pw/module_pwdft/td_vel_pw.h" +#include "source_pw/module_rt/td_history_pw.h" +#include "source_pw/module_rt/td_prop_pw.h" +#include "source_pw/module_rt/td_spin_pw.h" +#include "source_pw/module_rt/td_vel_pw.h" #include #include @@ -55,7 +55,7 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW private: using Real = typename GetTypeReal::type; std::ofstream& log_; - std::unique_ptr> td_solver_; + std::unique_ptr> td_prop_; std::shared_ptr td_field_manager_; pw::TDVelPW vel_; diff --git a/source/source_estate/test/td_field_test.cpp b/source/source_estate/test/td_field_test.cpp index 912eec9c1ab..5fe3827eb71 100644 --- a/source/source_estate/test/td_field_test.cpp +++ b/source/source_estate/test/td_field_test.cpp @@ -4,6 +4,7 @@ #include "source_io/module_parameter/input_parameter.h" #include "gtest/gtest.h" +#include #include #include #include @@ -98,6 +99,67 @@ TEST(TDFieldManagerTest, MixedFieldsSumAndAccumulate) EXPECT_EQ(oscillatory_manager->fields()[1].subdivisions(), 12); } +TEST(TDFieldManagerTest, SubintervalsPreserveFieldAndOriginalGrid) +{ + Input_para input; + input.td_vext = true; + input.td_stype = 1; + input.td_dt = ModuleBase::AU_to_FS; + input.td_tstart = 0; + input.td_tend = 10; + input.td_ttype = std::vector{2}; + input.td_vext_dire = std::vector{1}; + const double omega = 0.02; + input.td_trigo_freq1 = {omega / (2 * ModuleBase::PI * ModuleBase::AU_to_FS)}; + input.td_trigo_freq2 = {0.0}; + input.td_trigo_phase1 = {0.0}; + input.td_trigo_phase2 = {ModuleBase::PI / 2}; + input.td_trigo_amp = {1.0}; + const double amp = ModuleBase::BOHR_TO_A / ModuleBase::Hartree_to_eV; + + for (bool from_file: {false, true}) + { + SCOPED_TRACE(from_file); + auto manager = elecstate::create_td_field_manager(input); + if (from_file) + { + const std::vector> samples = {{0, 0, 0}, {2, 0, 0}, {5, 0, 0}}; + manager->set_A_samples(samples); + } + manager->prepare_sample(0); + manager->prepare_interval(0, 1); + const auto full = manager->field_state(); + const auto left = manager->subinterval(full, 0, 2); + manager->bind_field(left); + const auto right = manager->subinterval(full, 1, 2); + EXPECT_DOUBLE_EQ(left.dt, 0.5); + EXPECT_DOUBLE_EQ(right.dt, 0.5); + EXPECT_DOUBLE_EQ(left.left.x, full.left.x); + EXPECT_DOUBLE_EQ(left.right.x, right.left.x); + EXPECT_DOUBLE_EQ(right.right.x, full.right.x); + const double mid_a = from_file ? 1.0 : -amp * std::sin(omega * 0.5) / omega; + const double mid_e = from_file ? -2.0 : amp * std::cos(omega * 0.5); + EXPECT_NEAR(left.right.x, mid_a, 1e-11); + EXPECT_NEAR(left.electric.x, mid_e, 1e-14); + EXPECT_DOUBLE_EQ(left.propagation.x, (left.left.x + left.right.x) * 0.5); + EXPECT_DOUBLE_EQ(right.propagation.x, (right.left.x + right.right.x) * 0.5); + manager->bind_field(right); + EXPECT_EQ(manager->current_step(), 1); + EXPECT_DOUBLE_EQ(manager->interval_dt_ha(), 0.5); + manager->bind_field(full); + manager->prepare_interval(0, 1); + EXPECT_EQ(manager->current_step(), 1); + EXPECT_DOUBLE_EQ(manager->interval_dt_ha(), 1.0); + EXPECT_DOUBLE_EQ(manager->A_right_ha().x, full.right.x); + EXPECT_DOUBLE_EQ(manager->efield_ha().x, full.electric.x); + manager->prepare_interval(1, 2); + const double end_a = from_file ? 5.0 : -amp * std::sin(2 * omega) / omega; + EXPECT_EQ(manager->current_step(), 2); + EXPECT_DOUBLE_EQ(manager->A_left_ha().x, full.right.x); + EXPECT_NEAR(manager->A_right_ha().x, end_a, 1e-10); + } +} + TEST(TDFieldManagerTest, RestartRequiresCompleteState) { Input_para input; diff --git a/source/source_hsolver/CMakeLists.txt b/source/source_hsolver/CMakeLists.txt index e7adc3fb5ad..69cd57b0461 100644 --- a/source/source_hsolver/CMakeLists.txt +++ b/source/source_hsolver/CMakeLists.txt @@ -6,23 +6,12 @@ list(APPEND objects diago_bpcg.cpp para_lin_tf.cpp hsolver_pw.cpp - hsolver_pw_tddft.cpp hsolver_lcaopw.cpp diago_iter_assist.cpp hsolver.cpp diago_pxxxgvx.cpp diag_hs_para.cpp diago_params.cpp - linear_bicgstab.cpp - linear_cgs.cpp - linear_gmres.cpp - linear_algebra.cpp - orthonormal.cpp - orthonormal_small.cpp - cn_subspace.cpp - linear_low_rank.cpp - linear_workspace.cpp - hsolver_linear.cpp ) if(ENABLE_LCAO) @@ -100,3 +89,5 @@ if(BUILD_TESTING) endif() message(STATUS "Building tests") endif() + +add_subdirectory(module_rt_pw) diff --git a/source/source_hsolver/module_rt_pw/CMakeLists.txt b/source/source_hsolver/module_rt_pw/CMakeLists.txt new file mode 100644 index 00000000000..c970b287e0a --- /dev/null +++ b/source/source_hsolver/module_rt_pw/CMakeLists.txt @@ -0,0 +1,26 @@ +add_library(hsolver_rt OBJECT + cn_solver_pw.cpp + td_orbital_ops.cpp + orb_buffer.cpp + exp_action.cpp + exp_taylor.cpp + exp_lanczos.cpp + hsolver_linear.cpp + linear_bicgstab.cpp + linear_cgs.cpp + linear_gmres.cpp + linear_workspace.cpp + linear_algebra.cpp + linear_low_rank.cpp + cn_subspace.cpp + orthonormal.cpp + orthonormal_small.cpp +) + +if(ENABLE_COVERAGE) + add_coverage(hsolver_rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp index 8c059f8113f..5fe402a3daf 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp @@ -1,11 +1,12 @@ -#include "source_hsolver/hsolver_pw_tddft.h" +#include "source_hsolver/module_rt_pw/cn_solver_pw.h" #include "source_base/module_device/memory_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_basis/module_pw/pw_momentum.h" -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/orb_buffer.h" #include #include @@ -16,42 +17,6 @@ namespace hsolver namespace { -template -void clear_padding(T* data, int ld, int npw, int bands, int npol) -{ - if (npol != 2) - { - return; - } - const int stride = ld / npol; - if (stride > npw && bands > 0) - { - // Clear all spin-component tails together, including any NaNs in padding. - T* tail = data + npw; - const size_t width = stride - npw; - const size_t columns = static_cast(bands) * npol; - base_device::memory::set_memory_2d_op()(tail, stride, 0, width, columns); - } -} - -template -const T* clean_input(const T* input, int ld, int npw, int bands, int npol, ct::Tensor* scratch) -{ - if (npol == 1) - { - return input; - } - T* data = scratch->template data(); - const size_t count = static_cast(ld) * bands; - if (count > 0) - { - // History and other borrowed inputs remain unchanged while the solver uses a clean scratch copy. - base_device::memory::synchronize_memory_op()(data, input, count); - } - clear_padding(data, ld, npw, bands, npol); - return data; -} - struct LinearMethodName { const char* name; @@ -84,7 +49,11 @@ class ShiftedHOperator final : public LinearOperator { op_.hpsi(x, y, ld, nvec); // Keep H zero on padding so the shifted operator retains only its identity term there. - clear_padding(y, ld, npw_, nvec, npol_); + if (npol_ == 2) + { + const OrbLayout layout{ld, npw_, npol_, nvec}; + zero_pad(y, layout); + } linear_op().batch(ld, dim_, nvec, y, x, y, T(1), coefficient_, nullptr, nullptr, nullptr); } @@ -108,7 +77,7 @@ LinearMethod parse_linear_method(const std::string& name) } } const std::string message = "Unsupported linear solver: " + name; - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", message); + ModuleBase::WARNING_QUIT("CNSolverPW", message); return LinearMethod::bicgstab; } @@ -122,23 +91,22 @@ PWPreconditioner parse_pw_precond(const std::string& name) } } const std::string message = "Unsupported preconditioner: " + name; - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", message); + ModuleBase::WARNING_QUIT("CNSolverPW", message); return PWPreconditioner::kinetic; } template -HSolverPWTDDFT::HSolverPWTDDFT(const ModulePW::PW_Basis_K& basis, - const PWLinearOptions& options, - const diag_comm_info& comm, - std::ostream& log) - : basis_(basis), comm_(comm), options_(options), algebra_(comm), orthonormal_(comm, algebra_), log_(log), band_products_(comm, 9) +CNSolverPW::CNSolverPW(const ModulePW::PW_Basis_K& basis, + const PWLinearOptions& options, + const diag_comm_info& comm, + std::ostream& log) + : basis_(basis), comm_(comm), options_(options), algebra_(comm), log_(log), band_products_(comm, 9) { initialize(); - log_ << " PW RT-TDDFT orthonormalization: " << orth_method_name(options_.orthonormal) << '\n'; } template -void HSolverPWTDDFT::initialize() +void CNSolverPW::initialize() { const char* method = nullptr; for (const LinearMethodName& entry: linear_method_names) @@ -151,7 +119,7 @@ void HSolverPWTDDFT::initialize() } if (method == nullptr) { - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", "Unsupported linear solver."); + ModuleBase::WARNING_QUIT("CNSolverPW", "Unsupported linear solver."); return; } const char* preconditioner = nullptr; @@ -165,7 +133,7 @@ void HSolverPWTDDFT::initialize() } if (preconditioner == nullptr) { - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", "Unsupported preconditioner."); + ModuleBase::WARNING_QUIT("CNSolverPW", "Unsupported preconditioner."); return; } if (options_.linear.method != LinearMethod::gmres) @@ -184,9 +152,9 @@ void HSolverPWTDDFT::initialize() } template -void HSolverPWTDDFT::prepare_buffers(const int nbands, const int nbasis) +void CNSolverPW::prepare_buffers(const int nbands, const int nbasis) { - ModuleBase::timer::start("HSolverPWTDDFT", "prepare_buffers"); + ModuleBase::timer::start("CNSolverPW", "prepare_buffers"); using CtDevice = typename ct::PsiToContainer::type; const ct::DeviceType device = ct::DeviceTypeToEnum::value; const int64_t elements = static_cast(nbands) * nbasis; @@ -194,19 +162,18 @@ void HSolverPWTDDFT::prepare_buffers(const int nbands, const int nbas if (rhs_.NumElements() < size || rhs_.data_type() != ct::DataTypeToEnum::value || rhs_.device_type() != device) { rhs_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); - hpsi_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); clean_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); } - ModuleBase::timer::end("HSolverPWTDDFT", "prepare_buffers"); + ModuleBase::timer::end("CNSolverPW", "prepare_buffers"); } template -void HSolverPWTDDFT::update_precond(const int ik, - const int dim, - const T coefficient, - const ModuleBase::Vector3& momentum_shift) +void CNSolverPW::update_precond(const int ik, + const int dim, + const T coefficient, + const ModuleBase::Vector3& momentum_shift) { - ModuleBase::timer::start("HSolverPWTDDFT", "update_precond"); + ModuleBase::timer::start("CNSolverPW", "update_precond"); using CtDevice = typename ct::PsiToContainer::type; const ct::DeviceType device = ct::DeviceTypeToEnum::value; const int elements = std::max(1, dim); @@ -233,17 +200,17 @@ void HSolverPWTDDFT::update_precond(const int ik, inverse.data(), dim); } - ModuleBase::timer::end("HSolverPWTDDFT", "update_precond"); + ModuleBase::timer::end("CNSolverPW", "update_precond"); } template -bool HSolverPWTDDFT::tracks_state() const +bool CNSolverPW::tracks_state() const { return options_.preconditioner == PWPreconditioner::kinetic_recycle || options_.linear.reconstruct; } template -void HSolverPWTDDFT::prepare_sequence(int nk, int ld, int bands, double dt, int step, int iteration) +void CNSolverPW::prepare_sequence(int nk, int ld, int bands, double dt, int step, int iteration) { if (!tracks_state()) { @@ -264,7 +231,7 @@ void HSolverPWTDDFT::prepare_sequence(int nk, int ld, int bands, doub } template -void HSolverPWTDDFT::invalidate_basis() +void CNSolverPW::invalidate_basis() { for (KPointState& state: states_) { @@ -275,7 +242,7 @@ void HSolverPWTDDFT::invalidate_basis() } template -bool HSolverPWTDDFT::require_audit(int step, KPointState* state) const +bool CNSolverPW::require_audit(int step, KPointState* state) const { if (!options_.linear.reconstruct) { @@ -288,10 +255,10 @@ bool HSolverPWTDDFT::require_audit(int step, KPointState* state) cons } template -void HSolverPWTDDFT::retry_kinetic(const LinearOperator& op, - T* current, - const SolveBatch& batch, - LinearSolveResult* result) +void CNSolverPW::retry_kinetic(const LinearOperator& op, + T* current, + const SolveBatch& batch, + LinearSolveResult* result) { const int remaining_iterations = options_.linear.max_iterations - result->iterations; const LinearSolveControl control{remaining_iterations, false}; @@ -309,11 +276,11 @@ void HSolverPWTDDFT::retry_kinetic(const LinearOperator& o } template -void HSolverPWTDDFT::update_state(KPointState* state, - const SolveDetails& details, - int step, - const SolveBatch& batch, - const T* current) +void CNSolverPW::update_state(KPointState* state, + const SolveDetails& details, + int step, + const SolveBatch& batch, + const T* current) { if (!state) { @@ -338,13 +305,13 @@ void HSolverPWTDDFT::update_state(KPointState* state, } template -typename HSolverPWTDDFT::SolveDetails HSolverPWTDDFT::solve_kpoint(const LinearOperator& op, - const T* previous, - T* current, - const SolveBatch& batch, - int step, - int iteration, - KPointState* state) +typename CNSolverPW::SolveDetails CNSolverPW::solve_kpoint(const LinearOperator& op, + const T* previous, + T* current, + const SolveBatch& batch, + int step, + int iteration, + KPointState* state) { SolveDetails details; const T* rhs = rhs_.template data(); @@ -389,7 +356,7 @@ typename HSolverPWTDDFT::SolveDetails HSolverPWTDDFT::solv } template -void HSolverPWTDDFT::report_solve(const SolveDetails& details, int ik, int step, int iteration) const +void CNSolverPW::report_solve(const SolveDetails& details, int ik, int step, int iteration) const { const LinearSolveResult& result = details.linear; const int evolution_step = step + 1; @@ -405,23 +372,19 @@ void HSolverPWTDDFT::report_solve(const SolveDetails& details, int ik } template -void HSolverPWTDDFT::solve(HSOperator& op, - const psi::Psi& previous, - psi::Psi* current, - const double dt, - const ModuleBase::Vector3& momentum_shift, - const int istep, - const int iter, - const bool detailed_output) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "solve"); +void CNSolverPW::solve(HSOperator& op, + const psi::Psi& previous, + psi::Psi* current, + const double dt, + const ModuleBase::Vector3& momentum_shift, + const int istep, + const int iter, + const bool detailed_output) +{ + ModuleBase::timer::start("CNSolverPW", "solve"); const int bands = current->get_nbands(); const int ld = current->get_nbasis(); const int nk = current->get_nk(); - if (options_.out_stat) - { - orth_norms_.resize(nk); - } prepare_buffers(bands, ld); prepare_sequence(nk, ld, bands, dt, istep, iter); T* rhs = rhs_.template data(); @@ -439,9 +402,13 @@ void HSolverPWTDDFT::solve(HSOperator& op, KPointState* state = tracks_state() ? &states_[ik] : nullptr; const ShiftedHOperator rhs_op(op, rhs_coefficient, dim, npw, options_.npol); const ShiftedHOperator lhs_op(op, coefficient, dim, npw, options_.npol); - const T* previous_data = clean_input(previous.get_pointer(), ld, npw, bands, options_.npol, &clean_); + const OrbLayout layout{ld, npw, options_.npol, bands}; + const T* previous_data = clean_orbs(previous.get_pointer(), layout, &clean_); T* current_data = current->get_pointer(); - clear_padding(current_data, ld, npw, bands, options_.npol); + if (options_.npol == 2) + { + zero_pad(current_data, layout); + } rhs_op.apply(previous_data, rhs, ld, bands); if (options_.preconditioner != PWPreconditioner::none) { @@ -464,278 +431,18 @@ void HSolverPWTDDFT::solve(HSOperator& op, band_label = std::to_string(result.failed_band + 1); } message << "PW RT-TDDFT linear solve failed at electronic evolution step " << evolution_step << ", SCF iteration " << iter - << ", global_k=" << options_.global_k_indices.at(ik) + 1 << ", band " << band_label << ", after " - << result.iterations << " iterations: " << linear_status_name(result.status) << "; residual = " << result.max_residual; - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", message.str()); - } - correct_orbitals(current_data, ld, dim, bands, ik, istep, iter, detailed_output); - } - ModuleBase::timer::end("HSolverPWTDDFT", "solve"); -} - -template -void HSolverPWTDDFT::cal_band_energy(HSOperator& op, const psi::Psi& current, ModuleBase::matrix* energies) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "cal_band_energy"); - const int nband = current.get_nbands(); - const int ld = current.get_nbasis(); - prepare_buffers(nband, ld); - std::vector expectations(nband); - T* hpsi = hpsi_.template data(); - for (int ik = 0; ik < current.get_nk(); ++ik) - { - op.update_k(ik); - current.fix_k(ik); - const int npw = current.get_ngk(ik); - const int dim = options_.npol == 2 ? ld : npw; - const T* data = clean_input(current.get_pointer(), ld, npw, nband, options_.npol, &clean_); - op.hpsi(data, hpsi, ld, nband); - clear_padding(hpsi, ld, npw, nband, options_.npol); - band_products_.dot(ld, dim, nband, data, hpsi, expectations.data()); - for (int band = 0; band < nband; ++band) - { - (*energies)(ik, band) = std::real(expectations[band]); - } - } - ModuleBase::timer::end("HSolverPWTDDFT", "cal_band_energy"); -} - -template -std::vector> HSolverPWTDDFT::cal_eij(HSOperator& op, - const psi::Psi& current, - const int ik) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "cal_eij"); - const int nband = current.get_nbands(); - const int ld = current.get_nbasis(); - const int npw = current.get_ngk(ik); - const int dim = options_.npol == 2 ? ld : npw; - prepare_buffers(nband, ld); - op.update_k(ik); - current.fix_k(ik); - const T* psi = clean_input(current.get_pointer(), ld, npw, nband, options_.npol, &clean_); - T* hpsi = hpsi_.template data(); - op.hpsi(psi, hpsi, ld, nband); - clear_padding(hpsi, ld, npw, nband, options_.npol); - std::vector> result = algebra_.cross(ld, dim, nband, nband, psi, hpsi); - ModuleBase::timer::end("HSolverPWTDDFT", "cal_eij"); - return result; -} - -template -void HSolverPWTDDFT::reset_orth_stats() -{ - if (options_.out_stat) - { - orth_stats_ = TDOrthStats(); - } - warned_events_ = 0; -} - -template -void HSolverPWTDDFT::correct_orbitals(T* current, int ld, int dim, int bands, int ik, int istep, int iter, bool detailed_output) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "correct_orbitals"); - const OrthResult result = orthonormal_.apply(current, ld, dim, bands, options_.orthonormal, options_.out_stat); - record_orth(result, ik, istep, iter); - if (detailed_output && result.gram_checked && log_.good()) - { - const int evolution_step = istep + 1; - std::ostringstream record; - record << std::scientific << std::setprecision(6) << " PW RT-TDDFT orth: evolution_step=" << evolution_step << " scf_iter=" << iter - << " global_k=" << options_.global_k_indices.at(ik) + 1 << " requested=" << orth_method_name(options_.orthonormal) - << " orth_before=" << result.before << " orth_after=" << result.after << '\n'; - log_ << record.str(); - } - ModuleBase::timer::end("HSolverPWTDDFT", "correct_orbitals"); -} - -template -void HSolverPWTDDFT::record_orth(const OrthResult& result, int ik, int istep, int iter) -{ - if (result.status == OrthStatus::failed) - { - const char* stage = istep == 0 ? "initialization" : "propagation"; - std::ostringstream message; - const int evolution_step = istep + 1; - message << std::setprecision(16) << " PW RT-TDDFT orbital validation failed (" << stage << "): evolution_step=" << evolution_step - << " scf_iter=" << iter << " global_k=" << options_.global_k_indices.at(ik) + 1 - << " requested=" << orth_method_name(options_.orthonormal) << '\n' - << " last_attempted=" << orth_method_name(result.actual) << " passes=" << result.passes - << " fallbacks=" << result.fallbacks << '\n' - << " " << orth_failure_name(result.failure) << "; " << result.reason; - if (result.gram_checked) - { - message << '\n' << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance(); - } - // stdout is disabled on non-world-root ranks; a failing pool must still report its error. - if (comm_.rank == 0) - { - std::cerr << message.str() << std::endl; - } -#ifdef __MPI - // Let the pool root flush its diagnostic before another rank's exit stops the MPI job. - double diagnostic_written = 1.0; - Parallel_Common::bcast_data(&diagnostic_written, 1, comm_.comm, 0); -#endif - ModuleBase::WARNING_QUIT("HSolverPWTDDFT", message.str()); - } - report_orth_warning(result, ik, istep, iter); - if (options_.out_stat) - { - orth_norms_[ik] = result.norms; - if (result.gram_checked) - { - orth_stats_.before = std::max(orth_stats_.before, result.before); - orth_stats_.after = std::max(orth_stats_.after, result.after); - } - } -} - -template -void HSolverPWTDDFT::report_orth_warning(const OrthResult& result, int ik, int istep, int iter) -{ - if (comm_.rank != 0) - { - return; - } - unsigned int events = 0; - if (result.fallbacks > 0) - { - events |= 1; - } - if (result.rejected > 0) - { - events |= 2; - } - const unsigned int fresh_events = events & ~warned_events_; - if (fresh_events == 0) - { - return; - } - warned_events_ |= events; - const int evolution_step = istep + 1; - std::ostringstream message; - message << std::setprecision(16) << " PW RT-TDDFT orbital warning: evolution_step=" << evolution_step << " scf_iter=" << iter; - message << " global_k=" << options_.global_k_indices.at(ik) + 1; - message << " requested=" << orth_method_name(options_.orthonormal) << '\n' - << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance() << '\n' - << " "; - message << "last_attempted=" << orth_method_name(result.actual) << ". "; - if (fresh_events & 1) - { - message << "A fallback was attempted. "; - } - if (fresh_events & 2) - { - message << "A candidate was rejected. "; - } - message << "The retained state satisfies the tolerance." << '\n' - << " " << result.reason << '\n' - << " Further events of these types in this pool are suppressed for this electronic evolution step."; - const std::string text = message.str(); - std::cerr << text << std::endl; - if (log_.good()) - { - log_ << text << std::endl; - } -} - -template -void HSolverPWTDDFT::zero_padding(psi::Psi* current) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "zero_padding"); - if (options_.npol != 2) - { - ModuleBase::timer::end("HSolverPWTDDFT", "zero_padding"); - return; - } - const int old_k = current->get_current_k(); - for (int ik = 0; ik < current->get_nk(); ++ik) - { - current->fix_k(ik); - const int npw = current->get_ngk(ik); - const int ld = current->get_nbasis(); - const int bands = current->get_nbands(); - clear_padding(current->get_pointer(), ld, npw, bands, options_.npol); - } - current->fix_k(old_k); - ModuleBase::timer::end("HSolverPWTDDFT", "zero_padding"); -} - -template -bool HSolverPWTDDFT::orth_initial(psi::Psi* current, int iter) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "orth_initial"); - bool changed = false; - if (options_.orthonormal != OrthMethod::none) - { - if (options_.out_stat) - { - // Initial diagnostics describe this SCF state, not earlier discarded iterates. - orth_stats_ = TDOrthStats(); - orth_norms_.resize(current->get_nk()); - } - for (int ik = 0; ik < current->get_nk(); ++ik) - { - current->fix_k(ik); - const int dim = options_.npol == 2 ? current->get_nbasis() : current->get_ngk(ik); - const OrthResult result = orthonormal_.apply(current->get_pointer(), - current->get_nbasis(), - dim, - current->get_nbands(), - options_.orthonormal, - options_.out_stat); - record_orth(result, ik, 0, iter); - changed = changed || result.status == OrthStatus::accepted; - } - } - ModuleBase::timer::end("HSolverPWTDDFT", "orth_initial"); - return changed; -} - -template -void HSolverPWTDDFT::check_initial(const psi::Psi& current, int iter) -{ - ModuleBase::timer::start("HSolverPWTDDFT", "check_initial"); - const bool full_gram = options_.orthonormal != OrthMethod::none; - const int ld = current.get_nbasis(); - const int bands = current.get_nbands(); - prepare_buffers(bands, ld); - if (options_.out_stat) - { - orth_norms_.resize(current.get_nk()); - } - for (int ik = 0; ik < current.get_nk(); ++ik) - { - current.fix_k(ik); - const int npw = current.get_ngk(ik); - const int dim = options_.npol == 2 ? ld : npw; - const T* data = clean_input(current.get_pointer(), ld, npw, bands, options_.npol, &clean_); - const OrthResult result = orthonormal_.inspect(data, current.get_nbasis(), dim, current.get_nbands(), full_gram, options_.out_stat); - record_orth(result, ik, 0, iter); - } - ModuleBase::timer::end("HSolverPWTDDFT", "check_initial"); -} - -template -double HSolverPWTDDFT::wave_electrons(const ModuleBase::matrix& occupations) const -{ - double electrons = 0.0; - for (std::size_t ik = 0; ik < orth_norms_.size(); ++ik) - { - for (std::size_t band = 0; band < orth_norms_[ik].size(); ++band) - { - electrons += occupations(ik, band) * orth_norms_[ik][band]; + << ", global_k=" << options_.global_k_indices.at(ik) + 1 << ", band " << band_label << ", after " << result.iterations + << " iterations: " << linear_status_name(result.status) << "; residual = " << result.max_residual; + ModuleBase::WARNING_QUIT("CNSolverPW", message.str()); } } - return electrons; + ModuleBase::timer::end("CNSolverPW", "solve"); } -template class HSolverPWTDDFT, base_device::DEVICE_CPU>; -template class HSolverPWTDDFT, base_device::DEVICE_CPU>; +template class CNSolverPW, base_device::DEVICE_CPU>; +template class CNSolverPW, base_device::DEVICE_CPU>; #if defined(__CUDA) || defined(__ROCM) -template class HSolverPWTDDFT, base_device::DEVICE_GPU>; -template class HSolverPWTDDFT, base_device::DEVICE_GPU>; +template class CNSolverPW, base_device::DEVICE_GPU>; +template class CNSolverPW, base_device::DEVICE_GPU>; #endif } // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.h b/source/source_hsolver/module_rt_pw/cn_solver_pw.h index 929e2b964ff..e2506e1012a 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.h +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.h @@ -1,13 +1,12 @@ -#ifndef HSOLVER_PW_TDDFT_H -#define HSOLVER_PW_TDDFT_H +#ifndef HSOLVER_CN_SOLVER_PW_H +#define HSOLVER_CN_SOLVER_PW_H #include "source_base/matrix.h" #include "source_basis/module_pw/pw_basis_k.h" -#include "source_hsolver/cn_subspace.h" #include "source_hsolver/hs_operator.h" -#include "source_hsolver/hsolver_linear.h" -#include "source_hsolver/linear_low_rank.h" -#include "source_hsolver/orthonormal.h" +#include "source_hsolver/module_rt_pw/cn_subspace.h" +#include "source_hsolver/module_rt_pw/hsolver_linear.h" +#include "source_hsolver/module_rt_pw/linear_low_rank.h" #include "source_psi/psi.h" #include @@ -29,34 +28,23 @@ enum class PWPreconditioner struct PWLinearOptions { int npol = 1; ///< Spin components per orbital; fixed throughout propagation. - OrthMethod orthonormal = OrthMethod::cholesky; LinearSolveOptions linear; PWPreconditioner preconditioner = PWPreconditioner::kinetic; bool cn_init = false; bool kinetic_enabled = true; - bool out_stat = false; std::vector global_k_indices; ///< Required zero-based global index for every local k point. }; LinearMethod parse_linear_method(const std::string& name); -/** @brief Independent maxima over all SCF iterations and local k points in one electronic step. - * Values are replicated within each pool and reduced across pools for step-end output. - * The before and after maxima need not come from the same correction. - */ -struct TDOrthStats -{ - double before = 0.0; - double after = 0.0; -}; PWPreconditioner parse_pw_precond(const std::string& name); -/** @brief PW Crank-Nicolson solves and endpoint Hamiltonian expectations. */ +/** @brief PW Crank-Nicolson numerical solves with fixed previous-step orbitals. */ template -class HSolverPWTDDFT +class CNSolverPW { public: /** @brief Configure propagation using typed, explicit solver options. */ - HSolverPWTDDFT(const ModulePW::PW_Basis_K& basis, const PWLinearOptions& options, const diag_comm_info& comm, std::ostream& log); + CNSolverPW(const ModulePW::PW_Basis_K& basis, const PWLinearOptions& options, const diag_comm_info& comm, std::ostream& log); /** @brief Invalidate basis-dependent history and restart independent residual checks without releasing buffers. * Call on every rank in the pool after changing the basis or its distribution, including @@ -64,26 +52,6 @@ class HSolverPWTDDFT * updates and ionic motion at fixed basis do not require this notification. */ void invalidate_basis(); - /** @brief Reset diagnostics without invalidating preconditioner history. */ - void reset_orth_stats(); - /** @brief Publish diagnostics accumulated over accepted trial intervals only. */ - void set_orth_stats(const TDOrthStats& stats) - { - orth_stats_ = stats; - } - /** @brief Clear spinor padding without changing physical coefficients or the k-point cursor; no communication. */ - void zero_padding(psi::Psi* current); - /** @brief Orthogonalize initial orbitals with clean padding; return whether this pool changed any physical orbital. */ - bool orth_initial(psi::Psi* current, int iter); - /** @brief Check the converged initial state once; finite orthogonality errors are diagnostic only. */ - void check_initial(const psi::Psi& current, int iter); - /** @brief Occupation-weighted electron count for the local k-point pool. */ - double wave_electrons(const ModuleBase::matrix& occupations) const; - const TDOrthStats& orth_stats() const - { - return orth_stats_; - } - /** @brief Propagate from the fixed previous step, retaining the current iterate as the initial guess. * @param dt Electronic time step in Hartree atomic units. * @param momentum_shift Propagation vector potential in Hartree atomic units (inverse Bohr). @@ -99,12 +67,6 @@ class HSolverPWTDDFT const int iter, const bool detailed_output); - /** @brief Evaluate band expectations after the caller restores the endpoint Hamiltonian. */ - void cal_band_energy(HSOperator& op, const psi::Psi& current, ModuleBase::matrix* energies); - - /** @brief Return the pool-reduced, column-major Psi-adjoint H Psi for one local k point. */ - std::vector> cal_eij(HSOperator& op, const psi::Psi& current, const int ik); - private: using Real = typename GetTypeReal::type; struct KPointState @@ -140,18 +102,13 @@ class HSolverPWTDDFT PWLinearOptions options_; ct::Tensor clean_; LinearAlgebra algebra_; - Orthonormal orthonormal_; - TDOrthStats orth_stats_; - std::vector> orth_norms_; std::ostream& log_; - unsigned int warned_events_ = 0; CNSubspace projection_; std::vector states_; SequenceState sequence_; std::unique_ptr> linear_solver_; LinearWorkspace band_products_; ct::Tensor rhs_; - ct::Tensor hpsi_; ct::Tensor inverse_kinetic_; ct::Tensor response_workspace_; ct::Tensor correction_workspace_; @@ -172,9 +129,6 @@ class HSolverPWTDDFT void report_solve(const SolveDetails& details, int ik, int step, int iteration) const; void prepare_buffers(const int nbands, const int nbasis); void update_precond(const int ik, const int dim, const T coefficient, const ModuleBase::Vector3& momentum_shift); - void correct_orbitals(T* current, int ld, int dim, int bands, int ik, int istep, int iter, bool detailed_output); - void record_orth(const OrthResult& result, int ik, int istep, int iter); - void report_orth_warning(const OrthResult& result, int ik, int istep, int iter); }; } // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/cn_subspace.cpp b/source/source_hsolver/module_rt_pw/cn_subspace.cpp index f11b661836a..24080b156c9 100644 --- a/source/source_hsolver/module_rt_pw/cn_subspace.cpp +++ b/source/source_hsolver/module_rt_pw/cn_subspace.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/cn_subspace.h" +#include "source_hsolver/module_rt_pw/cn_subspace.h" -#include "source_hsolver/kernels/linear_op.h" -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/cn_subspace.h b/source/source_hsolver/module_rt_pw/cn_subspace.h index 0e822b7c3d2..ffc062025ff 100644 --- a/source/source_hsolver/module_rt_pw/cn_subspace.h +++ b/source/source_hsolver/module_rt_pw/cn_subspace.h @@ -1,6 +1,6 @@ #ifndef HSOLVER_CN_SUBSPACE_H #define HSOLVER_CN_SUBSPACE_H -#include "source_hsolver/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/exp_action.cpp b/source/source_hsolver/module_rt_pw/exp_action.cpp new file mode 100644 index 00000000000..5e91becd642 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_action.cpp @@ -0,0 +1,71 @@ +#include "source_hsolver/module_rt_pw/exp_action.h" + +#include "source_hsolver/module_rt_pw/exp_lanczos.h" +#include "source_hsolver/module_rt_pw/exp_taylor.h" + +#include +#include + +namespace hsolver +{ +template +ExpAction::ExpAction(const ExpOpts& exp_opts, const diag_comm_info& comm) : exp_opts_(exp_opts), algebra_(comm), work_(comm, 1) +{ + using Real = typename T::value_type; + if (exp_opts_.rel_tol == 0.0) + { + exp_opts_.rel_tol = std::max(1e-10, 100.0 * std::numeric_limits::epsilon()); + } + if (exp_opts_.taylor_order < 2 || exp_opts_.krylov_max < 2 || !std::isfinite(exp_opts_.rel_tol) || exp_opts_.rel_tol <= 0) + { + throw std::invalid_argument("Invalid exponential controls."); + } +} + +template +ExpResult ExpAction::apply_pair(const HSOperator& op, + const OrbLayout& layout, + double alpha, + const T* input, + T* half, + T* full) +{ + ExpResult result = apply(op, layout, alpha, input, half); + if (result.state != ExpState::success) + { + return result; + } + const double full_alpha = 2.0 * alpha; + const ExpResult second = apply(op, layout, full_alpha, input, full); + result.state = second.state; + result.has_err_est = result.has_err_est && second.has_err_est; + result.failed_band = second.failed_band; + result.rel_err_est = std::max(result.rel_err_est, second.rel_err_est); + result.order_or_dim = std::max(result.order_or_dim, second.order_or_dim); + result.bisect_count += second.bisect_count; + result.hpsi_calls += second.hpsi_calls; + result.hpsi_cols += second.hpsi_cols; + return result; +} + +template +std::unique_ptr> make_exp_action(const ExpOpts& exp_opts, const diag_comm_info& comm) +{ + if (exp_opts.method == ExpMethod::taylor) + { + return std::unique_ptr>(new ExpTaylor(exp_opts, comm)); + } + return std::unique_ptr>(new ExpLanczos(exp_opts, comm)); +} + +template class ExpAction, base_device::DEVICE_CPU>; +template class ExpAction, base_device::DEVICE_CPU>; +template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +#if defined(__CUDA) || defined(__ROCM) +template class ExpAction, base_device::DEVICE_GPU>; +template class ExpAction, base_device::DEVICE_GPU>; +template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +#endif +} // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/exp_action.h b/source/source_hsolver/module_rt_pw/exp_action.h new file mode 100644 index 00000000000..384eb09a8d8 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_action.h @@ -0,0 +1,74 @@ +#ifndef HSOLVER_EXP_ACTION_H +#define HSOLVER_EXP_ACTION_H + +#include "source_hsolver/hs_operator.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_rt_pw/orb_buffer.h" + +#include + +namespace hsolver +{ +enum class ExpMethod +{ + taylor, + lanczos +}; + +/** @brief Explicit controls for a frozen Hermitian Hamiltonian exponential. */ +struct ExpOpts +{ + ExpMethod method = ExpMethod::lanczos; + int taylor_order = 4; + double rel_tol = 0.0; + int krylov_max = 32; +}; + +/** @brief Outcome of an exponential evaluation; retry requests a shorter interval. */ +enum class ExpState +{ + success, + retry, + failed +}; + +/** @brief Error is relative to the input norm and only estimated by Lanczos. */ +struct ExpResult +{ + ExpState state = ExpState::success; + bool has_err_est = false; + double rel_err_est = 0.0; + int order_or_dim = 0; + int bisect_count = 0; + int failed_band = -1; + long long hpsi_calls = 0; + long long hpsi_cols = 0; +}; + +/** @brief Apply exp(-i*alpha*H_Ry); caller selects k, buffers must not alias. */ +template +class ExpAction +{ + protected: + ExpOpts exp_opts_; + LinearAlgebra algebra_; + LinearWorkspace work_; + + public: + ExpAction(const ExpOpts& exp_opts, const diag_comm_info& comm); + virtual ~ExpAction() = default; + /** @brief Effective relative tolerance after resolving the precision-dependent default. */ + double rel_tol() const + { + return exp_opts_.rel_tol; + } + virtual ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) = 0; + virtual ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full); +}; + +/** @brief Construct a backend without reading global input parameters. */ +template +std::unique_ptr> make_exp_action(const ExpOpts& exp_opts, const diag_comm_info& comm); +} // namespace hsolver +#endif diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp new file mode 100644 index 00000000000..5b9942f0bf0 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -0,0 +1,401 @@ +#include "source_hsolver/module_rt_pw/exp_lanczos.h" + +#include "source_base/module_container/base/third_party/lapack.h" +#include "source_base/timer.h" + +#include + +namespace hsolver +{ +namespace +{ +using Wide = std::complex; + +void add_work(ExpResult& total, const ExpResult& part) +{ + total.hpsi_calls += part.hpsi_calls; + total.hpsi_cols += part.hpsi_cols; + total.bisect_count += part.bisect_count; + total.order_or_dim = std::max(total.order_or_dim, part.order_or_dim); +} + +bool diagonalize(std::vector* vectors, int n, std::vector* values) +{ + const char job = 'V'; + const char upper = 'U'; + const int lwork = std::max(1, n * n + 2 * n); + const int lrwork = std::max(1, 1 + 5 * n + 2 * n * n); + const int liwork = std::max(1, 3 + 5 * n); + std::vector work(lwork); + std::vector rwork(lrwork); + std::vector iwork(liwork); + values->resize(n); + int info = 0; + zheevd_(&job, + &upper, + &n, + vectors->data(), + &n, + values->data(), + work.data(), + &lwork, + rwork.data(), + &lrwork, + iwork.data(), + &liwork, + &info); + return info == 0; +} + +std::vector gauss_rule(int order) +{ + std::vector rule(2 * order); + const double pi = std::acos(-1.0); + for (int i = 0; i < order; ++i) + { + double x = std::cos(pi * (i + 0.75) / (order + 0.5)); + double derivative = 0.0; + for (int iteration = 0; iteration < 20; ++iteration) + { + double p = 1.0; + double previous = 0.0; + for (int k = 1; k <= order; ++k) + { + const double next = ((2 * k - 1) * x * p - (k - 1) * previous) / k; + previous = p; + p = next; + } + derivative = order * (x * p - previous) / (x * x - 1); + const double change = p / derivative; + x -= change; + if (std::abs(change) < 1e-15) + { + break; + } + } + rule[i] = x; + rule[order + i] = 2.0 / ((1.0 - x * x) * derivative * derivative); + } + return rule; +} + +double defect_integral(const std::vector& values, + const std::vector& vectors, + double time, + int panels, + const std::vector& rule, + double sign) +{ + const int n = values.size(); + const int order = rule.size() / 2; + const double width = time / panels; + double integral = 0.0; + for (int panel = 0; panel < panels; ++panel) + { + for (int node = 0; node < order; ++node) + { + const double t = sign * (panel + 0.5 * (rule[node] + 1.0)) * width; + Wide amplitude(0); + for (int k = 0; k < n; ++k) + { + // Removing a scalar phase improves evaluation for shifted spectra. + amplitude += vectors[n - 1 + k * n] * std::conj(vectors[k * n]) * std::exp(Wide(0, -t * (values[k] - values[0]))); + } + integral += rule[order + node] * std::abs(amplitude); + } + } + return 0.5 * width * integral; +} + +double estimate_error(const std::vector& values, const std::vector& vectors, double alpha, double beta, double rel_tol) +{ + static const std::vector rule8 = gauss_rule(8); + static const std::vector rule16 = gauss_rule(16); + const double time = std::abs(alpha); + const double sign = alpha < 0 ? -1.0 : 1.0; + const double phase = time * (values.back() - values.front()); + const double requested = std::ceil(phase / (std::acos(-1.0) / 4.0)); + if (!std::isfinite(requested) || requested > 4096) + { + return std::numeric_limits::infinity(); + } + int panels = std::max(1, static_cast(requested)); + for (; panels <= 4096; panels *= 2) + { + const double low = beta * defect_integral(values, vectors, time, panels, rule8, sign); + const double high = beta * defect_integral(values, vectors, time, panels, rule16, sign); + if (std::abs(low - high) <= rel_tol / 8.0) + { + return 2.0 * std::max(low, high); + } + } + return std::numeric_limits::infinity(); +} +} // namespace + +template +ExpLanczos::ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm) +{ +} + +template +ExpResult ExpLanczos::attempt(const HSOperator& op, + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output) +{ + ExpResult result; + using Real = typename T::value_type; + const int ld = layout.ld; + const int bands = layout.bands; + const int dim = layout.npol == 2 ? ld : layout.npw; + const int stride = ld * bands; + const int limit = this->exp_opts_.krylov_max; + const int64_t size = std::max(1, stride); + linear_buffer(&basis_, size * (limit + 1)); + linear_buffer(&image_, size); + linear_buffer(&packed_, size); + linear_buffer(&hpacked_, size); + T* q = basis_.template data(); + T* w = image_.template data(); + T* packed = packed_.template data(); + T* hpacked = hpacked_.template data(); + base_device::memory::set_memory_op()(q, 0, size * (limit + 1)); + base_device::memory::set_memory_op()(output, 0, stride); + base_device::memory::synchronize_memory_op()(q, input, stride); + zero_pad(q, layout); + const auto norms = this->algebra_.dots(ld, dim, bands, 1, stride, q, q); + std::vector scale(bands); + std::vector factors(bands); + std::vector done(bands, false); + std::vector matrix(static_cast(bands) * limit * limit, Wide(0)); + result.has_err_est = true; + for (int b = 0; b < bands; ++b) + { + scale[b] = std::sqrt(std::max(0.0, norms[b].real())); + if (!std::isfinite(norms[b].real())) + { + result.state = ExpState::failed; + result.has_err_est = false; + result.failed_band = b; + return result; + } + done[b] = scale[b] == 0.0; + factors[b] = done[b] ? T(0) : T(1.0 / scale[b]); + } + this->work_.batch(ld, dim, bands, q, q, q, T(1), T(0), factors.data(), nullptr, nullptr); + for (int j = 0; j < limit; ++j) + { + std::vector active; + T* column = q + static_cast(j) * stride; + for (int b = 0; b < bands; ++b) + { + if (done[b]) + { + continue; + } + T* destination = packed + active.size() * ld; + base_device::memory::synchronize_memory_op()(destination, column + b * ld, ld); + active.push_back(b); + } + if (active.empty()) + { + result.failed_band = -1; + return result; + } + const int count = active.size(); + op.hpsi(packed, hpacked, ld, count); + const OrbLayout batch_layout{ld, layout.npw, layout.npol, count}; + zero_pad(hpacked, batch_layout); + ++result.hpsi_calls; + result.hpsi_cols += count; + base_device::memory::set_memory_op()(w, 0, stride); + for (int a = 0; a < count; ++a) + { + base_device::memory::synchronize_memory_op()(w + active[a] * ld, hpacked + a * ld, ld); + } + // Two complete projections retain small off-tridiagonal corrections. + for (int pass = 0; pass < 2; ++pass) + { + const auto products = this->algebra_.dots(ld, dim, bands, j + 1, stride, q, w); + for (int i = 0; i <= j; ++i) + { + for (int b = 0; b < bands; ++b) + { + const Wide value = products[i * bands + b]; + matrix[(static_cast(b) * limit + j) * limit + i] += value; + factors[b] = T(-value); + } + const T* qi = q + static_cast(i) * stride; + this->work_.batch(ld, dim, bands, w, w, qi, T(1), T(1), nullptr, factors.data(), nullptr); + } + } + const auto residuals = this->algebra_.dots(ld, dim, bands, 1, stride, w, w); + const int m = j + 1; + result.order_or_dim = std::max(result.order_or_dim, m); + double max_error = 0.0; + for (int b: active) + { + const double beta = std::sqrt(std::max(0.0, residuals[b].real())); + if (!std::isfinite(residuals[b].real())) + { + result.state = ExpState::failed; + result.has_err_est = false; + result.failed_band = b; + return result; + } + const bool breakdown = beta <= 100.0 * std::numeric_limits::epsilon(); + const bool evaluate = breakdown || m == limit || (m >= 4 && m % 2 == 0); + if (evaluate) + { + std::vector vectors(m * m); + const Wide* block = matrix.data() + static_cast(b) * limit * limit; + for (int col = 0; col < m; ++col) + { + for (int row = 0; row <= col; ++row) + { + // The upper triangle contains explicitly computed projections. + Wide value = block[row + col * limit]; + if (row == col) + { + value = Wide(value.real(), 0); + } + vectors[row + col * m] = value; + vectors[col + row * m] = std::conj(value); + } + } + std::vector values; + if (!diagonalize(&vectors, m, &values)) + { + result.state = ExpState::failed; + result.has_err_est = false; + result.failed_band = b; + return result; + } + const double error = estimate_error(values, vectors, alpha, beta, rel_tol); + max_error = std::max(max_error, error); + if (error <= rel_tol) + { + std::vector coefficients(m, Wide(0)); + for (int k = 0; k < m; ++k) + { + const Wide phase = scale[b] * std::exp(Wide(0, -alpha * values[k])) * std::conj(vectors[k * m]); + for (int i = 0; i < m; ++i) + { + coefficients[i] += vectors[i + k * m] * phase; + } + } + this->algebra_.expand(stride, dim, m, 1, q + b * ld, coefficients, output + b * ld, T(0)); + result.rel_err_est = std::max(result.rel_err_est, error); + done[b] = true; + } + } + factors[b] = done[b] || beta == 0.0 ? T(0) : T(1.0 / beta); + } + for (int b = 0; b < bands; ++b) + { + if (done[b]) + { + factors[b] = T(0); + } + } + T* next = q + static_cast(m) * stride; + this->work_.batch(ld, dim, bands, next, w, w, T(1), T(0), factors.data(), nullptr, nullptr); + if (m == limit) + { + result.rel_err_est = std::max(result.rel_err_est, max_error); + } + } + for (int b = 0; b < bands; ++b) + { + if (!done[b]) + { + result.state = ExpState::retry; + result.failed_band = b; + return result; + } + } + result.failed_band = -1; + return result; +} + +template +ExpResult ExpLanczos::interval(const HSOperator& op, + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + int depth) +{ + ExpResult trial = attempt(op, layout, alpha, rel_tol, input, output); + if (trial.state != ExpState::retry) + { + return trial; + } + if (depth == 10) + { + trial.state = ExpState::failed; + return trial; + } + ++trial.bisect_count; + ct::Tensor midpoint; + const int64_t size = static_cast(layout.ld) * layout.bands; + linear_buffer(&midpoint, size); + T* middle = midpoint.template data(); + const double half_alpha = alpha / 2.0; + const double half_tol = rel_tol / 2.0; + const int next_depth = depth + 1; + ExpResult left = interval(op, layout, half_alpha, half_tol, input, middle, next_depth); + add_work(left, trial); + if (left.state != ExpState::success) + { + return left; + } + ExpResult right = interval(op, layout, half_alpha, half_tol, middle, output, next_depth); + add_work(right, left); + if (right.state == ExpState::success) + { + right.rel_err_est += left.rel_err_est; + } + return right; +} + +template +ExpResult ExpLanczos::apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) +{ + ModuleBase::timer::start("ExpLanczos", "apply"); + ExpResult result; + result.has_err_est = true; + // Limit basis storage independently of the number of occupied bands. + for (int first = 0; first < layout.bands; first += 8) + { + const int bands = std::min(8, layout.bands - first); + const OrbLayout batch{layout.ld, layout.npw, layout.npol, bands}; + const int64_t offset = static_cast(first) * layout.ld; + const T* src = input + offset; + T* dst = output + offset; + const ExpResult local = interval(op, batch, alpha, this->exp_opts_.rel_tol, src, dst, 0); + result.has_err_est = result.has_err_est && local.has_err_est; + add_work(result, local); + result.rel_err_est = std::max(result.rel_err_est, local.rel_err_est); + if (local.state != ExpState::success) + { + result.state = local.state; + result.failed_band = first + local.failed_band; + break; + } + } + ModuleBase::timer::end("ExpLanczos", "apply"); + return result; +} + +template class ExpLanczos, base_device::DEVICE_CPU>; +template class ExpLanczos, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class ExpLanczos, base_device::DEVICE_GPU>; +template class ExpLanczos, base_device::DEVICE_GPU>; +#endif +} // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.h b/source/source_hsolver/module_rt_pw/exp_lanczos.h new file mode 100644 index 00000000000..b68aa5d0b1c --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.h @@ -0,0 +1,34 @@ +#ifndef HSOLVER_EXP_LANCZOS_H +#define HSOLVER_EXP_LANCZOS_H +#include "source_hsolver/module_rt_pw/exp_action.h" + +namespace hsolver +{ +template +class ExpLanczos final : public ExpAction +{ + private: + ct::Tensor basis_; + ct::Tensor image_; + ct::Tensor packed_; + ct::Tensor hpacked_; + ExpResult attempt(const HSOperator& op, + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output); + ExpResult interval(const HSOperator& op, + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + int depth); + + public: + ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm); + ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) override; +}; +} // namespace hsolver +#endif diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.cpp b/source/source_hsolver/module_rt_pw/exp_taylor.cpp new file mode 100644 index 00000000000..72f3c2ecbdf --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_taylor.cpp @@ -0,0 +1,98 @@ +#include "source_hsolver/module_rt_pw/exp_taylor.h" + +#include "source_base/timer.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" + +namespace hsolver +{ +template +ExpTaylor::ExpTaylor(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm) +{ +} + +template +ExpResult ExpTaylor::apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) +{ + return evaluate(op, layout, alpha, input, output, nullptr); +} + +template +ExpResult ExpTaylor::apply_pair(const HSOperator& op, + const OrbLayout& layout, + double alpha, + const T* input, + T* half, + T* full) +{ + return evaluate(op, layout, alpha, input, half, full); +} + +template +ExpResult ExpTaylor::evaluate(const HSOperator& op, + const OrbLayout& layout, + double alpha, + const T* input, + T* half, + T* full) +{ + ModuleBase::timer::start("ExpTaylor", "evaluate"); + const int ld = layout.ld; + const int bands = layout.bands; + const int dim = layout.npol == 2 ? ld : layout.npw; + const int64_t count = static_cast(ld) * bands; + linear_buffer(&term_, count); + linear_buffer(&image_, count); + T* term = term_.template data(); + T* image = image_.template data(); + base_device::memory::synchronize_memory_op()(term, input, count); + zero_pad(term, layout); + base_device::memory::synchronize_memory_op()(half, term, count); + if (full) + { + base_device::memory::synchronize_memory_op()(full, term, count); + } + ExpResult result; + result.order_or_dim = this->exp_opts_.taylor_order; + // Scale at the longer requested time; half-step weights cannot overflow at high order. + const double series_alpha = full ? 2.0 * alpha : alpha; + for (int k = 1; k <= this->exp_opts_.taylor_order; ++k) + { + op.hpsi(term, image, ld, bands); + zero_pad(image, layout); + const T coefficient(0, -series_alpha / k); + linear_op().batch(ld, dim, bands, term, image, image, coefficient, T(0), nullptr, nullptr, nullptr); + const T half_weight = full ? T(std::ldexp(1.0, -k)) : T(1); + linear_op().batch(ld, dim, bands, half, half, term, T(1), half_weight, nullptr, nullptr, nullptr); + if (full) + { + linear_op().batch(ld, dim, bands, full, full, term, T(1), T(1), nullptr, nullptr, nullptr); + } + ++result.hpsi_calls; + result.hpsi_cols += bands; + } + const auto norms = this->algebra_.dots(ld, dim, bands, 1, ld * bands, half, half); + std::vector> full_norms; + if (full) + { + full_norms = this->algebra_.dots(ld, dim, bands, 1, ld * bands, full, full); + } + for (int b = 0; b < bands; ++b) + { + if (!std::isfinite(norms[b].real()) || (full && !std::isfinite(full_norms[b].real()))) + { + result.state = ExpState::failed; + result.failed_band = b; + break; + } + } + ModuleBase::timer::end("ExpTaylor", "evaluate"); + return result; +} + +template class ExpTaylor, base_device::DEVICE_CPU>; +template class ExpTaylor, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class ExpTaylor, base_device::DEVICE_GPU>; +template class ExpTaylor, base_device::DEVICE_GPU>; +#endif +} // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.h b/source/source_hsolver/module_rt_pw/exp_taylor.h new file mode 100644 index 00000000000..c4fab74e31f --- /dev/null +++ b/source/source_hsolver/module_rt_pw/exp_taylor.h @@ -0,0 +1,21 @@ +#ifndef HSOLVER_EXP_TAYLOR_H +#define HSOLVER_EXP_TAYLOR_H +#include "source_hsolver/module_rt_pw/exp_action.h" + +namespace hsolver +{ +template +class ExpTaylor final : public ExpAction +{ + private: + ct::Tensor term_; + ct::Tensor image_; + ExpResult evaluate(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full); + + public: + ExpTaylor(const ExpOpts& exp_opts, const diag_comm_info& comm); + ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) override; + ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) override; +}; +} // namespace hsolver +#endif diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/hsolver_linear.cpp index f86dce35e14..e9e7a91b684 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp +++ b/source/source_hsolver/module_rt_pw/hsolver_linear.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/hsolver_linear.h" +#include "source_hsolver/module_rt_pw/hsolver_linear.h" #include "source_base/tool_quit.h" diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.h b/source/source_hsolver/module_rt_pw/hsolver_linear.h index 776797db20b..678c47cebb3 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.h +++ b/source/source_hsolver/module_rt_pw/hsolver_linear.h @@ -1,8 +1,8 @@ #ifndef HSOLVER_LINEAR_H #define HSOLVER_LINEAR_H -#include "source_hsolver/linear_bicgstab.h" -#include "source_hsolver/linear_cgs.h" -#include "source_hsolver/linear_gmres.h" +#include "source_hsolver/module_rt_pw/linear_bicgstab.h" +#include "source_hsolver/module_rt_pw/linear_cgs.h" +#include "source_hsolver/module_rt_pw/linear_gmres.h" #include diff --git a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu b/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu index fd59d2d8d29..6950747456a 100644 --- a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu +++ b/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu @@ -1,4 +1,4 @@ -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp b/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp index fcaff888ce2..971c508e22c 100644 --- a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp +++ b/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu b/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu index e71c038e1e7..c48d752398f 100644 --- a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu +++ b/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu @@ -1,4 +1,4 @@ -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_algebra.cpp b/source/source_hsolver/module_rt_pw/linear_algebra.cpp index c4a7aa441c9..eea45efc116 100644 --- a/source/source_hsolver/module_rt_pw/linear_algebra.cpp +++ b/source/source_hsolver/module_rt_pw/linear_algebra.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" #include "source_base/kernels/math_kernel_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp b/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp index d3700a74d78..3fd8f822ae0 100644 --- a/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp +++ b/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp @@ -1,6 +1,6 @@ -#include "source_hsolver/linear_bicgstab.h" +#include "source_hsolver/module_rt_pw/linear_bicgstab.h" -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include diff --git a/source/source_hsolver/module_rt_pw/linear_bicgstab.h b/source/source_hsolver/module_rt_pw/linear_bicgstab.h index 2e21510df35..4bf7c34b05e 100644 --- a/source/source_hsolver/module_rt_pw/linear_bicgstab.h +++ b/source/source_hsolver/module_rt_pw/linear_bicgstab.h @@ -1,6 +1,6 @@ #ifndef MODULE_HSOLVER_LINEAR_BICGSTAB_H_ #define MODULE_HSOLVER_LINEAR_BICGSTAB_H_ -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { /** @brief Right-preconditioned BiCGSTAB for independent complex right-hand sides. */ diff --git a/source/source_hsolver/module_rt_pw/linear_cgs.cpp b/source/source_hsolver/module_rt_pw/linear_cgs.cpp index f5f0f488f12..1c2006aa406 100644 --- a/source/source_hsolver/module_rt_pw/linear_cgs.cpp +++ b/source/source_hsolver/module_rt_pw/linear_cgs.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/linear_cgs.h" +#include "source_hsolver/module_rt_pw/linear_cgs.h" #include diff --git a/source/source_hsolver/module_rt_pw/linear_cgs.h b/source/source_hsolver/module_rt_pw/linear_cgs.h index d3f671fe660..d5bc08669de 100644 --- a/source/source_hsolver/module_rt_pw/linear_cgs.h +++ b/source/source_hsolver/module_rt_pw/linear_cgs.h @@ -1,6 +1,6 @@ #ifndef HSOLVER_LINEAR_CGS_H #define HSOLVER_LINEAR_CGS_H -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.cpp b/source/source_hsolver/module_rt_pw/linear_gmres.cpp index a880ca12deb..2a7bd7ad38a 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.cpp +++ b/source/source_hsolver/module_rt_pw/linear_gmres.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/linear_gmres.h" +#include "source_hsolver/module_rt_pw/linear_gmres.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.h b/source/source_hsolver/module_rt_pw/linear_gmres.h index 8b6d3868af8..a6b80ab1856 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.h +++ b/source/source_hsolver/module_rt_pw/linear_gmres.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_LINEAR_GMRES_H #define HSOLVER_LINEAR_GMRES_H -#include "source_hsolver/linear_algebra.h" -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_low_rank.cpp b/source/source_hsolver/module_rt_pw/linear_low_rank.cpp index 8bf4f734df4..4037669d4d6 100644 --- a/source/source_hsolver/module_rt_pw/linear_low_rank.cpp +++ b/source/source_hsolver/module_rt_pw/linear_low_rank.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/linear_low_rank.h" +#include "source_hsolver/module_rt_pw/linear_low_rank.h" -#include "source_hsolver/kernels/linear_op.h" -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { @@ -83,23 +83,23 @@ void LinearLowRank::prepare_response(int ld, int rank, const T* direc template void LinearLowRank::prepare_subspace(int ld, - int rank, - const T* basis, - const T* images, - const LinearSmallLU& factor, - ct::Tensor* workspace) + int rank, + const T* basis, + const T* images, + const LinearSmallLU& factor, + ct::Tensor* workspace) { prepare(ld, rank, basis, images, basis, &factor, workspace); } template void LinearLowRank::prepare(int ld, - int rank, - const T* z, - const T* w, - const T* test, - const LinearSmallLU* factor, - ct::Tensor* workspace) + int rank, + const T* z, + const T* w, + const T* test, + const LinearSmallLU* factor, + ct::Tensor* workspace) { rank_ = rank; test_ = rank > 0 ? test : nullptr; diff --git a/source/source_hsolver/module_rt_pw/linear_low_rank.h b/source/source_hsolver/module_rt_pw/linear_low_rank.h index ce3eb044950..d5edd570e57 100644 --- a/source/source_hsolver/module_rt_pw/linear_low_rank.h +++ b/source/source_hsolver/module_rt_pw/linear_low_rank.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_LINEAR_LOW_RANK_H #define HSOLVER_LINEAR_LOW_RANK_H -#include "source_hsolver/linear_algebra.h" -#include "source_hsolver/linear_operator.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_operator.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_workspace.cpp b/source/source_hsolver/module_rt_pw/linear_workspace.cpp index fa2e14fe0dc..f25883a3a62 100644 --- a/source/source_hsolver/module_rt_pw/linear_workspace.cpp +++ b/source/source_hsolver/module_rt_pw/linear_workspace.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/linear_workspace.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" #include "source_base/module_device/memory_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_workspace.h b/source/source_hsolver/module_rt_pw/linear_workspace.h index 0b93a0f50ae..fde45f4c30e 100644 --- a/source/source_hsolver/module_rt_pw/linear_workspace.h +++ b/source/source_hsolver/module_rt_pw/linear_workspace.h @@ -4,8 +4,8 @@ #include "source_base/macros.h" #include "source_base/module_container/ATen/core/tensor.h" #include "source_hsolver/diag_comm_info.h" -#include "source_hsolver/linear_operator.h" -#include "source_hsolver/linear_solver_types.h" +#include "source_hsolver/module_rt_pw/linear_operator.h" +#include "source_hsolver/module_rt_pw/linear_solver_types.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/orb_buffer.cpp b/source/source_hsolver/module_rt_pw/orb_buffer.cpp new file mode 100644 index 00000000000..20e84515b18 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/orb_buffer.cpp @@ -0,0 +1,55 @@ +#include "source_hsolver/module_rt_pw/orb_buffer.h" + +#include + +namespace hsolver +{ +template +void zero_pad(T* data, const OrbLayout& layout) +{ + const int stride = layout.ld / layout.npol; + if (stride > layout.npw && layout.bands > 0) + { + T* tail = data + layout.npw; + const size_t width = stride - layout.npw; + const size_t columns = static_cast(layout.bands) * layout.npol; + base_device::memory::set_memory_2d_op()(tail, stride, 0, width, columns); + } +} + +template +const T* clean_orbs(const T* input, const OrbLayout& layout, ct::Tensor* scratch) +{ + if (layout.npol == 1) + { + return input; + } + T* data = scratch->template data(); + const size_t count = static_cast(layout.ld) * layout.bands; + if (count > 0) + { + base_device::memory::synchronize_memory_op()(data, input, count); + } + zero_pad(data, layout); + return data; +} + +template void zero_pad, base_device::DEVICE_CPU>(std::complex*, const OrbLayout&); +template void zero_pad, base_device::DEVICE_CPU>(std::complex*, const OrbLayout&); +template const std::complex* clean_orbs, base_device::DEVICE_CPU>(const std::complex*, + const OrbLayout&, + ct::Tensor*); +template const std::complex* clean_orbs, base_device::DEVICE_CPU>(const std::complex*, + const OrbLayout&, + ct::Tensor*); +#if defined(__CUDA) || defined(__ROCM) +template void zero_pad, base_device::DEVICE_GPU>(std::complex*, const OrbLayout&); +template void zero_pad, base_device::DEVICE_GPU>(std::complex*, const OrbLayout&); +template const std::complex* clean_orbs, base_device::DEVICE_GPU>(const std::complex*, + const OrbLayout&, + ct::Tensor*); +template const std::complex* clean_orbs, base_device::DEVICE_GPU>(const std::complex*, + const OrbLayout&, + ct::Tensor*); +#endif +} // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/orb_buffer.h b/source/source_hsolver/module_rt_pw/orb_buffer.h new file mode 100644 index 00000000000..1f6960d832d --- /dev/null +++ b/source/source_hsolver/module_rt_pw/orb_buffer.h @@ -0,0 +1,28 @@ +#ifndef HSOLVER_ORB_BUFFER_H +#define HSOLVER_ORB_BUFFER_H + +#include "source_base/module_container/ATen/core/tensor.h" +#include "source_base/module_device/memory_op.h" + +namespace hsolver +{ +/** @brief Column-major orbitals; spin components have ld/npol rows each. */ +struct OrbLayout +{ + int ld; + int npw; + int npol; + int bands; +}; + +/** @brief Clear all spin-component tails, including scalar-orbital padding. */ +template +void zero_pad(T* data, const OrbLayout& layout); + +/** @brief Borrow scalar input, or copy spinors into scratch and clear their padding. + * Scratch must have room for ld * bands coefficients; input remains unchanged. + */ +template +const T* clean_orbs(const T* input, const OrbLayout& layout, ct::Tensor* scratch); +} // namespace hsolver +#endif diff --git a/source/source_hsolver/module_rt_pw/orthonormal.cpp b/source/source_hsolver/module_rt_pw/orthonormal.cpp index bd5c83d9c82..3e2af196f43 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal.cpp +++ b/source/source_hsolver/module_rt_pw/orthonormal.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/orthonormal.h" +#include "source_hsolver/module_rt_pw/orthonormal.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" diff --git a/source/source_hsolver/module_rt_pw/orthonormal.h b/source/source_hsolver/module_rt_pw/orthonormal.h index 08deb139749..3248a54e3be 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal.h +++ b/source/source_hsolver/module_rt_pw/orthonormal.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_ORTHONORMAL_H #define HSOLVER_ORTHONORMAL_H -#include "source_hsolver/linear_algebra.h" +#include "source_hsolver/module_rt_pw/linear_algebra.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp b/source/source_hsolver/module_rt_pw/orthonormal_small.cpp index 30d25b305a5..a34c3ad83a0 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp +++ b/source/source_hsolver/module_rt_pw/orthonormal_small.cpp @@ -1,7 +1,7 @@ #include "source_base/kernels/math_kernel_op.h" #include "source_base/module_container/base/third_party/lapack.h" #include "source_base/timer.h" -#include "source_hsolver/orthonormal.h" +#include "source_hsolver/module_rt_pw/orthonormal.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp b/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp new file mode 100644 index 00000000000..fc562843c1b --- /dev/null +++ b/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp @@ -0,0 +1,321 @@ +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" + +#include "source_base/parallel_device.h" +#include "source_base/timer.h" +#include "source_base/tool_quit.h" +#include "source_hsolver/module_rt_pw/orb_buffer.h" + +#include +#include +#include + +namespace hsolver +{ +template +TDOrbOps::TDOrbOps(const TDOrbOpts& options, const diag_comm_info& comm, std::ostream& log) + : comm_(comm), options_(options), algebra_(comm), orthonormal_(comm, algebra_), log_(log), band_products_(comm, 9) +{ + log_ << " PW RT-TDDFT orthonormalization: " << orth_method_name(options_.orthonormal) << '\n'; +} + +template +void TDOrbOps::prepare_buffers(int nbands, int nbasis) +{ + const int64_t size = static_cast(nbands) * nbasis; + linear_buffer(&clean_, size); + linear_buffer(&hpsi_, size); +} + +template +void TDOrbOps::correct(psi::Psi* current, int step, int iter, bool detailed_output) +{ + if (options_.out_stat) + { + orth_norms_.resize(current->get_nk()); + } + zero_padding(current); + for (int ik = 0; ik < current->get_nk(); ++ik) + { + current->fix_k(ik); + const int ld = current->get_nbasis(); + const int dim = options_.npol == 2 ? ld : current->get_ngk(ik); + const int bands = current->get_nbands(); + T* data = current->get_pointer(); + correct_orbitals(data, ld, dim, bands, ik, step, iter, detailed_output); + } +} + +template +void TDOrbOps::cal_band_energy(HSOperator& op, const psi::Psi& current, ModuleBase::matrix* energies) +{ + ModuleBase::timer::start("TDOrbOps", "cal_band_energy"); + const int nband = current.get_nbands(); + const int ld = current.get_nbasis(); + prepare_buffers(nband, ld); + std::vector expectations(nband); + T* hpsi = hpsi_.template data(); + for (int ik = 0; ik < current.get_nk(); ++ik) + { + op.update_k(ik); + current.fix_k(ik); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; + const OrbLayout layout{ld, npw, options_.npol, nband}; + const T* data = clean_orbs(current.get_pointer(), layout, &clean_); + op.hpsi(data, hpsi, ld, nband); + if (options_.npol == 2) + { + zero_pad(hpsi, layout); + } + band_products_.dot(ld, dim, nband, data, hpsi, expectations.data()); + for (int band = 0; band < nband; ++band) + { + (*energies)(ik, band) = std::real(expectations[band]); + } + } + ModuleBase::timer::end("TDOrbOps", "cal_band_energy"); +} + +template +std::vector> TDOrbOps::cal_eij(HSOperator& op, const psi::Psi& current, const int ik) +{ + ModuleBase::timer::start("TDOrbOps", "cal_eij"); + const int nband = current.get_nbands(); + const int ld = current.get_nbasis(); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; + prepare_buffers(nband, ld); + op.update_k(ik); + current.fix_k(ik); + const OrbLayout layout{ld, npw, options_.npol, nband}; + const T* psi = clean_orbs(current.get_pointer(), layout, &clean_); + T* hpsi = hpsi_.template data(); + op.hpsi(psi, hpsi, ld, nband); + if (options_.npol == 2) + { + zero_pad(hpsi, layout); + } + std::vector> result = algebra_.cross(ld, dim, nband, nband, psi, hpsi); + ModuleBase::timer::end("TDOrbOps", "cal_eij"); + return result; +} + +template +void TDOrbOps::reset_orth_stats() +{ + if (options_.out_stat) + { + orth_stats_ = TDOrthStats(); + } + warned_events_ = 0; +} + +template +void TDOrbOps::correct_orbitals(T* current, int ld, int dim, int bands, int ik, int istep, int iter, bool detailed_output) +{ + ModuleBase::timer::start("TDOrbOps", "correct_orbitals"); + const OrthResult result = orthonormal_.apply(current, ld, dim, bands, options_.orthonormal, options_.out_stat); + record_orth(result, ik, istep, iter); + if (detailed_output && result.gram_checked && log_.good()) + { + const int evolution_step = istep + 1; + std::ostringstream record; + record << std::scientific << std::setprecision(6) << " PW RT-TDDFT orth: evolution_step=" << evolution_step << " scf_iter=" << iter + << " global_k=" << options_.global_k_indices.at(ik) + 1 << " requested=" << orth_method_name(options_.orthonormal) + << " orth_before=" << result.before << " orth_after=" << result.after << '\n'; + log_ << record.str(); + } + ModuleBase::timer::end("TDOrbOps", "correct_orbitals"); +} + +template +void TDOrbOps::record_orth(const OrthResult& result, int ik, int istep, int iter) +{ + if (result.status == OrthStatus::failed) + { + const char* stage = istep == 0 ? "initialization" : "propagation"; + std::ostringstream message; + const int evolution_step = istep + 1; + message << std::setprecision(16) << " PW RT-TDDFT orbital validation failed (" << stage << "): evolution_step=" << evolution_step + << " scf_iter=" << iter << " global_k=" << options_.global_k_indices.at(ik) + 1 + << " requested=" << orth_method_name(options_.orthonormal) << '\n' + << " last_attempted=" << orth_method_name(result.actual) << " passes=" << result.passes + << " fallbacks=" << result.fallbacks << '\n' + << " " << orth_failure_name(result.failure) << "; " << result.reason; + if (result.gram_checked) + { + message << '\n' << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance(); + } + // stdout is disabled on non-world-root ranks; a failing pool must still report its error. + if (comm_.rank == 0) + { + std::cerr << message.str() << std::endl; + } +#ifdef __MPI + // Let the pool root flush its diagnostic before another rank's exit stops the MPI job. + double diagnostic_written = 1.0; + Parallel_Common::bcast_data(&diagnostic_written, 1, comm_.comm, 0); +#endif + ModuleBase::WARNING_QUIT("TDOrbOps", message.str()); + } + report_orth_warning(result, ik, istep, iter); + if (options_.out_stat) + { + orth_norms_[ik] = result.norms; + if (result.gram_checked) + { + orth_stats_.before = std::max(orth_stats_.before, result.before); + orth_stats_.after = std::max(orth_stats_.after, result.after); + } + } +} + +template +void TDOrbOps::report_orth_warning(const OrthResult& result, int ik, int istep, int iter) +{ + if (comm_.rank != 0) + { + return; + } + unsigned int events = 0; + if (result.fallbacks > 0) + { + events |= 1; + } + if (result.rejected > 0) + { + events |= 2; + } + const unsigned int fresh_events = events & ~warned_events_; + if (fresh_events == 0) + { + return; + } + warned_events_ |= events; + const int evolution_step = istep + 1; + std::ostringstream message; + message << std::setprecision(16) << " PW RT-TDDFT orbital warning: evolution_step=" << evolution_step << " scf_iter=" << iter; + message << " global_k=" << options_.global_k_indices.at(ik) + 1; + message << " requested=" << orth_method_name(options_.orthonormal) << '\n' + << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance() << '\n' + << " "; + message << "last_attempted=" << orth_method_name(result.actual) << ". "; + if (fresh_events & 1) + { + message << "A fallback was attempted. "; + } + if (fresh_events & 2) + { + message << "A candidate was rejected. "; + } + message << "The retained state satisfies the tolerance." << '\n' + << " " << result.reason << '\n' + << " Further events of these types in this pool are suppressed for this electronic evolution step."; + const std::string text = message.str(); + std::cerr << text << std::endl; + if (log_.good()) + { + log_ << text << std::endl; + } +} + +template +void TDOrbOps::zero_padding(psi::Psi* current) +{ + ModuleBase::timer::start("TDOrbOps", "zero_padding"); + if (options_.npol != 2) + { + ModuleBase::timer::end("TDOrbOps", "zero_padding"); + return; + } + const int old_k = current->get_current_k(); + for (int ik = 0; ik < current->get_nk(); ++ik) + { + current->fix_k(ik); + const int npw = current->get_ngk(ik); + const int ld = current->get_nbasis(); + const int bands = current->get_nbands(); + const OrbLayout layout{ld, npw, options_.npol, bands}; + zero_pad(current->get_pointer(), layout); + } + current->fix_k(old_k); + ModuleBase::timer::end("TDOrbOps", "zero_padding"); +} + +template +bool TDOrbOps::orth_initial(psi::Psi* current, int iter) +{ + ModuleBase::timer::start("TDOrbOps", "orth_initial"); + bool changed = false; + if (options_.orthonormal != OrthMethod::none) + { + if (options_.out_stat) + { + // Initial diagnostics describe this SCF state, not earlier discarded iterates. + orth_stats_ = TDOrthStats(); + orth_norms_.resize(current->get_nk()); + } + for (int ik = 0; ik < current->get_nk(); ++ik) + { + current->fix_k(ik); + const int dim = options_.npol == 2 ? current->get_nbasis() : current->get_ngk(ik); + const OrthResult result = orthonormal_.apply(current->get_pointer(), + current->get_nbasis(), + dim, + current->get_nbands(), + options_.orthonormal, + options_.out_stat); + record_orth(result, ik, 0, iter); + changed = changed || result.status == OrthStatus::accepted; + } + } + ModuleBase::timer::end("TDOrbOps", "orth_initial"); + return changed; +} + +template +void TDOrbOps::check_initial(const psi::Psi& current, int iter) +{ + ModuleBase::timer::start("TDOrbOps", "check_initial"); + const bool full_gram = options_.orthonormal != OrthMethod::none; + const int ld = current.get_nbasis(); + const int bands = current.get_nbands(); + prepare_buffers(bands, ld); + if (options_.out_stat) + { + orth_norms_.resize(current.get_nk()); + } + for (int ik = 0; ik < current.get_nk(); ++ik) + { + current.fix_k(ik); + const int npw = current.get_ngk(ik); + const int dim = options_.npol == 2 ? ld : npw; + const OrbLayout layout{ld, npw, options_.npol, bands}; + const T* data = clean_orbs(current.get_pointer(), layout, &clean_); + const OrthResult result = orthonormal_.inspect(data, current.get_nbasis(), dim, current.get_nbands(), full_gram, options_.out_stat); + record_orth(result, ik, 0, iter); + } + ModuleBase::timer::end("TDOrbOps", "check_initial"); +} + +template +double TDOrbOps::wave_electrons(const ModuleBase::matrix& occupations) const +{ + double electrons = 0.0; + for (std::size_t ik = 0; ik < orth_norms_.size(); ++ik) + { + for (std::size_t band = 0; band < orth_norms_[ik].size(); ++band) + { + electrons += occupations(ik, band) * orth_norms_[ik][band]; + } + } + return electrons; +} + +template class TDOrbOps, base_device::DEVICE_CPU>; +template class TDOrbOps, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class TDOrbOps, base_device::DEVICE_GPU>; +template class TDOrbOps, base_device::DEVICE_GPU>; +#endif +} // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.h b/source/source_hsolver/module_rt_pw/td_orbital_ops.h new file mode 100644 index 00000000000..e333bbcbd18 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/td_orbital_ops.h @@ -0,0 +1,80 @@ +#ifndef HSOLVER_TD_ORBITAL_OPS_H +#define HSOLVER_TD_ORBITAL_OPS_H +#include "source_base/matrix.h" +#include "source_hsolver/hs_operator.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_rt_pw/orthonormal.h" +#include "source_psi/psi.h" + +#include + +namespace hsolver +{ +/** @brief Method-independent orbital layout and diagnostics. */ +struct TDOrbOpts +{ + int npol = 1; + OrthMethod orthonormal = OrthMethod::cholesky; + bool out_stat = false; + std::vector global_k_indices; +}; +/** @brief Independent orthogonality maxima over accepted trial iterations and local k points. */ +struct TDOrthStats +{ + double before = 0.0; + double after = 0.0; +}; + +template +class TDOrbOps +{ + private: + const diag_comm_info comm_; + TDOrbOpts options_; + ct::Tensor clean_; + ct::Tensor hpsi_; + LinearAlgebra algebra_; + Orthonormal orthonormal_; + TDOrthStats orth_stats_; + std::vector> orth_norms_; + std::ostream& log_; + unsigned int warned_events_ = 0; + LinearWorkspace band_products_; + void prepare_buffers(int nbands, int nbasis); + void correct_orbitals(T* current, int ld, int dim, int bands, int ik, int istep, int iter, bool detailed_output); + void record_orth(const OrthResult& result, int ik, int istep, int iter); + void report_orth_warning(const OrthResult& result, int ik, int istep, int iter); + + public: + /** @brief Configure common orbital processing independently of a propagator. */ + TDOrbOps(const TDOrbOpts& options, const diag_comm_info& comm, std::ostream& log); + /** @brief Apply common postprocessing to a completed predictor or corrector. */ + void correct(psi::Psi* current, int step, int iter, bool detailed_output); + /** @brief Reset diagnostics without invalidating preconditioner history. */ + void reset_orth_stats(); + /** @brief Publish diagnostics accumulated over accepted trial intervals only. */ + void set_orth_stats(const TDOrthStats& stats) + { + orth_stats_ = stats; + } + /** @brief Clear spinor padding without changing physical coefficients or the k-point cursor; no communication. */ + void zero_padding(psi::Psi* current); + /** @brief Orthogonalize initial orbitals with clean padding; return whether this pool changed any physical orbital. */ + bool orth_initial(psi::Psi* current, int iter); + /** @brief Check the converged initial state once; finite orthogonality errors are diagnostic only. */ + void check_initial(const psi::Psi& current, int iter); + /** @brief Occupation-weighted electron count for the local k-point pool. */ + double wave_electrons(const ModuleBase::matrix& occupations) const; + const TDOrthStats& orth_stats() const + { + return orth_stats_; + } + + /** @brief Evaluate band expectations after the caller restores the endpoint Hamiltonian. */ + void cal_band_energy(HSOperator& op, const psi::Psi& current, ModuleBase::matrix* energies); + + /** @brief Return the pool-reduced, column-major Psi-adjoint H Psi for one local k point. */ + std::vector> cal_eij(HSOperator& op, const psi::Psi& current, const int ik); +}; +} // namespace hsolver +#endif diff --git a/source/source_hsolver/module_rt_pw/test/CMakeLists.txt b/source/source_hsolver/module_rt_pw/test/CMakeLists.txt new file mode 100644 index 00000000000..af40d55dd96 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/test/CMakeLists.txt @@ -0,0 +1,34 @@ +abacus_disable_feature_definitions(__CUDA) +abacus_disable_feature_definitions(__ROCM) +abacus_disable_feature_definitions(__EXX) + +set(TD_LINEAR_TEST_SOURCES + ../hsolver_linear.cpp + ../linear_bicgstab.cpp + ../linear_cgs.cpp + ../linear_gmres.cpp + ../linear_algebra.cpp + ../cn_subspace.cpp + ../linear_low_rank.cpp + ../linear_workspace.cpp +) +AddTest( + TARGET MODULE_HSOLVER_linear + LIBS parameter base device container + SOURCES test_hsolver_linear.cpp test_cn_subspace.cpp test_linear_low_rank.cpp ${TD_LINEAR_TEST_SOURCES} +) +AddTest( + TARGET MODULE_HSOLVER_pw_tddft + LIBS parameter base device container psi planewave + SOURCES test_hsolver_pw_tddft.cpp test_orthonormal.cpp test_exp_action.cpp + ../exp_action.cpp ../exp_lanczos.cpp ../exp_taylor.cpp + ../cn_solver_pw.cpp ../td_orbital_ops.cpp ../orb_buffer.cpp ../orthonormal.cpp ../orthonormal_small.cpp ${TD_LINEAR_TEST_SOURCES} +) +if(ENABLE_MPI) + add_test( + NAME MODULE_HSOLVER_linear_parallel + COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} + $ ${MPIEXEC_POSTFLAGS} + ) + set_tests_properties(MODULE_HSOLVER_linear_parallel PROPERTIES PROCESSORS 2 TIMEOUT 120) +endif() diff --git a/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp b/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp index 16fee4d1e10..366b8b8cf08 100644 --- a/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp @@ -1,6 +1,6 @@ #include "source_base/parallel_device.h" -#include "source_hsolver/cn_subspace.h" -#include "source_hsolver/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/cn_subspace.h" +#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp new file mode 100644 index 00000000000..476cf5e1b18 --- /dev/null +++ b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp @@ -0,0 +1,266 @@ +#include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" + +#include +#include +#include +#include +#include + +namespace +{ +using Wide = std::complex; +using Device = base_device::DEVICE_CPU; + +// A full complex Hermitian matrix, with spin components separated by padding. +template +class DenseExp final : public hsolver::HSOperator +{ + public: + std::vector matrix; + int npol; + mutable int calls = 0; + mutable int cols = 0; + + explicit DenseExp(int spins) : npol(spins) + { + matrix = linear_test::hermitian(6); + } + int row(int i, int ld) const + { + return i / (6 / npol) * (ld / npol) + i % (6 / npol); + } + void update_k(int) override + { + } + void hpsi(const T* x, T* y, int ld, int bands) const override + { + ++calls; + cols += bands; + std::fill_n(y, ld * bands, T(0)); + for (int b = 0; b < bands; ++b) + { + for (int i = 0; i < 6; ++i) + { + for (int j = 0; j < 6; ++j) + { + y[b * ld + row(i, ld)] += matrix[j * 6 + i] * x[b * ld + row(j, ld)]; + } + } + } + } + void spsi(const T*, T*, int, int) const override + { + FAIL() << "PW exponential must not request an overlap operator"; + } +}; + +template +std::vector pack_input(const DenseExp& op, const hsolver::OrbLayout& layout) +{ + std::vector input(layout.ld * layout.bands, T(30, -20)); + for (int b = 0; b < layout.bands; ++b) + { + for (int i = 0; i < 6; ++i) + { + input[b * layout.ld + op.row(i, layout.ld)] = T(std::sin(i + 2 * b + 1), std::cos(2 * i + b)); + } + } + return input; +} + +// Diagonalize the entire matrix, independently of the Krylov approximation. +template +std::vector exact_exp(const std::vector& matrix, const std::vector& input, double alpha) +{ + const int n = 6; + const int lwork = 64; + const char job = 'V'; + const char upper = 'U'; + std::vector vectors(matrix.begin(), matrix.end()); + std::vector work(lwork); + std::vector rwork(3 * n); + std::vector values(n); + int status = 0; + zheev_(&job, &upper, &n, vectors.data(), &n, values.data(), work.data(), &lwork, rwork.data(), &status); + EXPECT_EQ(status, 0); + std::vector output(n, Wide(0)); + for (int k = 0; k < n; ++k) + { + Wide weight(0); + for (int i = 0; i < n; ++i) + { + weight += std::conj(vectors[k * n + i]) * input[i]; + } + weight *= std::exp(Wide(0, -alpha * values[k])); + for (int i = 0; i < n; ++i) + { + output[i] += vectors[k * n + i] * weight; + } + } + return output; +} + +template +void check_output(const DenseExp& op, + const hsolver::OrbLayout& layout, + const std::vector& input, + const std::vector& output, + double alpha, + int order, + double tol) +{ + for (int b = 0; b < layout.bands; ++b) + { + std::vector src(6); + for (int i = 0; i < 6; ++i) + { + src[i] = input[b * layout.ld + op.row(i, layout.ld)]; + } + std::vector expected; + if (order == 0) + { + expected = exact_exp(op.matrix, src, alpha); + } + else + { + // Direct dense polynomial in double precision, without batched kernels. + expected = src; + std::vector term = src; + const std::vector matrix(op.matrix.begin(), op.matrix.end()); + for (int k = 1; k <= order; ++k) + { + term = linear_test::multiply(matrix, term, 6, 1); + for (int i = 0; i < 6; ++i) + { + term[i] *= Wide(0, -alpha / k); + expected[i] += term[i]; + } + } + } + double norm_in = 0; + double norm_out = 0; + for (int i = 0; i < 6; ++i) + { + const Wide actual = output[b * layout.ld + op.row(i, layout.ld)]; + EXPECT_NEAR(std::abs(actual - expected[i]), 0.0, tol); + norm_in += std::norm(src[i]); + norm_out += std::norm(actual); + } + if (order == 0) + { + EXPECT_NEAR(norm_out, norm_in, 20 * tol); + } + for (int spin = 0; spin < layout.npol; ++spin) + { + for (int i = layout.npw; i < layout.ld / layout.npol; ++i) + { + EXPECT_EQ(output[b * layout.ld + spin * (layout.ld / layout.npol) + i], T(0)); + } + } + } +} + +template +class ExpTest : public testing::Test +{ +}; +using Precisions = testing::Types; +TYPED_TEST_SUITE(ExpTest, Precisions); + +TYPED_TEST(ExpTest, LanczosMatchesFullExponential) +{ + using T = std::complex; + const double tol = std::is_same::value ? 2e-5 : 2e-10; + for (int npol: {1, 2}) + { + SCOPED_TRACE(npol); + DenseExp op(npol); + const hsolver::OrbLayout layout{10, 6 / npol, npol, 10}; + const auto input = pack_input(op, layout); + std::vector output(input.size()); + hsolver::ExpOpts opts; + auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + const auto result = exp->apply(op, layout, 0.4, input.data(), output.data()); + ASSERT_EQ(result.state, hsolver::ExpState::success); + EXPECT_TRUE(result.has_err_est); + EXPECT_LE(result.rel_err_est, exp->rel_tol()); + EXPECT_EQ(result.hpsi_calls, op.calls); + EXPECT_EQ(result.hpsi_cols, op.cols); + check_output(op, layout, input, output, 0.4, 0, tol); + } +} + +TYPED_TEST(ExpTest, TaylorPairMatchesBothPolynomials) +{ + using T = std::complex; + const double tol = std::is_same::value ? 2e-6 : 2e-14; + for (int npol: {1, 2}) + { + SCOPED_TRACE(npol); + DenseExp op(npol); + const hsolver::OrbLayout layout{10, 6 / npol, npol, 3}; + const auto input = pack_input(op, layout); + std::vector half(input.size()); + std::vector full(input.size()); + hsolver::ExpOpts opts; + opts.method = hsolver::ExpMethod::taylor; + opts.taylor_order = 6; + auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + const auto result = exp->apply_pair(op, layout, 0.15, input.data(), half.data(), full.data()); + ASSERT_EQ(result.state, hsolver::ExpState::success); + EXPECT_FALSE(result.has_err_est); + EXPECT_EQ(op.calls, opts.taylor_order); + EXPECT_EQ(result.hpsi_calls, op.calls); + EXPECT_EQ(result.hpsi_cols, opts.taylor_order * layout.bands); + check_output(op, layout, input, half, 0.15, opts.taylor_order, tol); + check_output(op, layout, input, full, 0.3, opts.taylor_order, tol); + } +} + +TEST(ExpAction, LanczosSubdividesOrReportsExhaustion) +{ + DenseExp op(1); + const hsolver::OrbLayout layout{8, 6, 1, 2}; + const auto input = pack_input(op, layout); + std::vector output(input.size()); + hsolver::ExpOpts opts; + opts.krylov_max = 4; + opts.rel_tol = 1e-8; + auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + const auto result = exp->apply(op, layout, 1.0, input.data(), output.data()); + ASSERT_EQ(result.state, hsolver::ExpState::success); + EXPECT_GT(result.bisect_count, 0); + EXPECT_LE(result.rel_err_est, opts.rel_tol); + EXPECT_EQ(result.hpsi_calls, op.calls); + EXPECT_EQ(result.hpsi_cols, op.cols); + check_output(op, layout, input, output, 1.0, 0, 5e-8); + + opts.krylov_max = 2; + opts.rel_tol = 1e-14; + exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + const auto failed = exp->apply(op, layout, 1.0, input.data(), output.data()); + EXPECT_EQ(failed.state, hsolver::ExpState::failed); + EXPECT_GT(failed.bisect_count, 0); + EXPECT_GE(failed.failed_band, 0); + EXPECT_LT(failed.failed_band, layout.bands); +} + +TEST(ExpAction, NonfiniteInputReportsOriginalBand) +{ + DenseExp op(2); + const hsolver::OrbLayout layout{10, 3, 2, 10}; + auto input = pack_input(op, layout); + input[9 * layout.ld] = Wide(std::numeric_limits::quiet_NaN(), 0); + std::vector output(input.size()); + for (auto method: {hsolver::ExpMethod::lanczos, hsolver::ExpMethod::taylor}) + { + hsolver::ExpOpts opts; + opts.method = method; + auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + const auto result = exp->apply(op, layout, 0.1, input.data(), output.data()); + EXPECT_EQ(result.state, hsolver::ExpState::failed); + EXPECT_EQ(result.failed_band, 9); + } +} +} // namespace diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp index 14efc6a96d8..275f5b14921 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp @@ -1,7 +1,7 @@ #include "source_base/parallel_device.h" #include "source_base/parallel_global.h" -#include "source_hsolver/hsolver_linear.h" -#include "source_hsolver/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/hsolver_linear.h" +#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp index 857dd8e77e7..db36e9037cb 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp @@ -1,6 +1,7 @@ #include "source_base/parallel_global.h" -#include "source_hsolver/hsolver_pw_tddft.h" -#include "source_hsolver/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/cn_solver_pw.h" +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" #include "source_io/module_parameter/parameter.h" #include @@ -184,8 +185,9 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) DenseHamiltonian op(dimensions); op.spinor = true; hsolver::PWLinearOptions options; + hsolver::TDOrbOpts orb_opts; options.npol = 2; - options.global_k_indices = {0, 1}; + options.global_k_indices = std::vector{0, 1}; options.linear.method = hsolver::LinearMethod::bicgstab; options.linear.tolerance = single ? 2e-6 : 1e-13; options.linear.max_iterations = 100; @@ -196,7 +198,10 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) const hsolver::diag_comm_info comm(0, 1); #endif std::ostringstream log; - hsolver::HSolverPWTDDFT solver(basis, options, comm, log); + orb_opts.npol = options.npol; + orb_opts.global_k_indices = options.global_k_indices; + hsolver::TDOrbOps orbitals(orb_opts, comm, log); + hsolver::CNSolverPW solver(basis, options, comm, log); const int ld = 2 * npwx; psi::Psi previous(2, bands, ld, sizes, true); std::vector> packed(2); @@ -222,12 +227,13 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) psi::Psi current(previous); const ModuleBase::Vector3 shift(0.13, -0.07, 0.02); solver.solve(op, previous, ¤t, dt, shift, 1, 1, false); + orbitals.correct(¤t, 1, 1, false); for (int ik = 0; ik < 2; ++ik) { current.fix_k(ik); const int n = dimensions[ik]; const auto reference = dense_cn(op.matrices[ik], packed[ik], n, bands, dt); - const auto eij = solver.cal_eij(op, current, ik); + const auto eij = orbitals.cal_eij(op, current, ik); for (int band = 0; band < bands; ++band) { for (int spin = 0; spin < 2; ++spin) @@ -277,14 +283,18 @@ TYPED_TEST(PWTDDFTTest, ComplexEijExcludesPadding) initialize_basis(sizes, &basis); DenseHamiltonian op(sizes); hsolver::PWLinearOptions options; - options.global_k_indices = {0}; + hsolver::TDOrbOpts orb_opts; + options.global_k_indices = std::vector{0}; #ifdef __MPI const hsolver::diag_comm_info comm(MPI_COMM_SELF, 0, 1); #else const hsolver::diag_comm_info comm(0, 1); #endif std::ostringstream log; - hsolver::HSolverPWTDDFT solver(basis, options, comm, log); + orb_opts.npol = options.npol; + orb_opts.global_k_indices = options.global_k_indices; + hsolver::TDOrbOps orbitals(orb_opts, comm, log); + hsolver::CNSolverPW solver(basis, options, comm, log); psi::Psi current(1, bands, ld, sizes, true); const T phases[]{T(1), T(0, 1), T(-1)}; const T padding(std::numeric_limits::quiet_NaN()); @@ -296,7 +306,7 @@ TYPED_TEST(PWTDDFTTest, ComplexEijExcludesPadding) } current(band, band) = phases[band]; } - const std::vector> eij = solver.cal_eij(op, current, 0); + const std::vector> eij = orbitals.cal_eij(op, current, 0); ASSERT_EQ(eij.size(), bands * bands); // These orbitals select H's leading block, with an independent phase on each column. for (int j = 0; j < bands; ++j) @@ -344,7 +354,8 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) DenseHamiltonian op(sizes); std::ostringstream log; hsolver::PWLinearOptions options; - options.global_k_indices = {0, 1}; + hsolver::TDOrbOpts orb_opts; + options.global_k_indices = std::vector{0, 1}; const std::string solver_name = cn_variant ? method.substr(0, method.size() - 3) : method; options.linear.method = hsolver::parse_linear_method(solver_name); options.linear.tolerance = single_precision ? 2e-6 : 1e-13; @@ -354,7 +365,10 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) options.linear.restart = 2; options.linear.reconstruct = method == "gmres" && recycle; options.cn_init = method == "gmres" || cn_variant; - hsolver::HSolverPWTDDFT solver(basis, options, comm, log); + orb_opts.npol = options.npol; + orb_opts.global_k_indices = options.global_k_indices; + hsolver::TDOrbOps orbitals(orb_opts, comm, log); + hsolver::CNSolverPW solver(basis, options, comm, log); psi::Psi previous(2, bands, ld, sizes, true); for (int ik = 0; ik < 2; ++ik) { @@ -376,6 +390,7 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) log.str(""); log.clear(); solver.solve(op, previous, ¤t, step_dt, shift, step, 1, true); + orbitals.correct(¤t, step, 1, true); check_dense_step(op, previous, current, step_dt); if (recycle || precond == "kinetic_subspace") { @@ -427,6 +442,7 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) log.str(""); log.clear(); solver.solve(op, previous, ¤t, step_dt, shift, step, 2, true); + orbitals.correct(¤t, step, 2, true); check_dense_step(op, previous, current, step_dt); EXPECT_EQ(log.str().find("cn_initial=1"), std::string::npos); } @@ -441,7 +457,7 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) } } ModuleBase::matrix energies(2, bands); - solver.cal_band_energy(op, current, &energies); + orbitals.cal_band_energy(op, current, &energies); for (int ik = 0; ik < 2; ++ik) { current.fix_k(ik); @@ -492,14 +508,18 @@ TYPED_TEST(PWTDDFTTest, UnequalOccupationsAndDiagnosticsPreservePropagation) { std::ostringstream log; hsolver::PWLinearOptions options; - options.global_k_indices = {0}; + hsolver::TDOrbOpts orb_opts; + options.global_k_indices = std::vector{0}; options.linear.method = hsolver::LinearMethod::gmres; options.linear.tolerance = single ? 2e-6 : 1e-13; options.linear.max_iterations = 100; options.linear.restart = n; options.preconditioner = hsolver::PWPreconditioner::none; - options.out_stat = out_stat; - hsolver::HSolverPWTDDFT solver(basis, options, comm, log); + orb_opts.out_stat = out_stat; + orb_opts.npol = options.npol; + orb_opts.global_k_indices = options.global_k_indices; + hsolver::TDOrbOps orbitals(orb_opts, comm, log); + hsolver::CNSolverPW solver(basis, options, comm, log); psi::Psi previous(1, bands, ld, sizes, true); std::fill_n(previous.get_pointer(), ld * bands, T(0)); std::vector reference(n * bands, Wide(0)); @@ -511,14 +531,15 @@ TYPED_TEST(PWTDDFTTest, UnequalOccupationsAndDiagnosticsPreservePropagation) psi::Psi initial(previous); initial(0, 0) = T(1.01); const psi::Psi original(initial); - solver.check_initial(initial, 1); + orbitals.check_initial(initial, 1); EXPECT_EQ(std::memcmp(initial.get_pointer(), original.get_pointer(), ld * bands * sizeof(T)), 0); // The finite initial defect is diagnostic; propagate the orthonormal reference state. psi::Psi current(previous); for (int step = 1; step <= 8; ++step) { - solver.reset_orth_stats(); + orbitals.reset_orth_stats(); solver.solve(op, previous, ¤t, dt, shift, step, 1, false); + orbitals.correct(¤t, step, 1, false); reference = dense_cn(h, reference, n, bands, dt); for (int band = 0; band < bands; ++band) { @@ -544,7 +565,7 @@ TYPED_TEST(PWTDDFTTest, UnequalOccupationsAndDiagnosticsPreservePropagation) } } const double expected_electrons = out_stat ? 1.4 : 0.0; - EXPECT_NEAR(solver.wave_electrons(occupations), expected_electrons, tolerance); + EXPECT_NEAR(orbitals.wave_electrons(occupations), expected_electrons, tolerance); const T* data = current.get_pointer(); if (!out_stat) { diff --git a/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp b/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp index 6a4d110fba0..48ebb146bab 100644 --- a/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp @@ -1,5 +1,5 @@ -#include "source_hsolver/linear_low_rank.h" -#include "source_hsolver/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/linear_low_rank.h" +#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp b/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp index 50ff4231d75..49a354945ae 100644 --- a/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/orthonormal.h" +#include "source_hsolver/module_rt_pw/orthonormal.h" #include #include diff --git a/source/source_hsolver/test/CMakeLists.txt b/source/source_hsolver/test/CMakeLists.txt index f98af874b45..7b70f32d34b 100644 --- a/source/source_hsolver/test/CMakeLists.txt +++ b/source/source_hsolver/test/CMakeLists.txt @@ -2,34 +2,6 @@ abacus_disable_feature_definitions(__CUDA) abacus_disable_feature_definitions(__ROCM) abacus_disable_feature_definitions(__EXX) -set(TD_LINEAR_TEST_SOURCES - ../hsolver_linear.cpp - ../linear_bicgstab.cpp - ../linear_cgs.cpp - ../linear_gmres.cpp - ../linear_algebra.cpp - ../cn_subspace.cpp - ../linear_low_rank.cpp - ../linear_workspace.cpp -) -AddTest( - TARGET MODULE_HSOLVER_linear - LIBS parameter base device container - SOURCES test_hsolver_linear.cpp test_cn_subspace.cpp test_linear_low_rank.cpp ${TD_LINEAR_TEST_SOURCES} -) -AddTest( - TARGET MODULE_HSOLVER_pw_tddft - LIBS parameter base device container psi planewave - SOURCES test_hsolver_pw_tddft.cpp test_orthonormal.cpp ../hsolver_pw_tddft.cpp ../orthonormal.cpp ../orthonormal_small.cpp ${TD_LINEAR_TEST_SOURCES} -) -if(ENABLE_MPI) - add_test( - NAME MODULE_HSOLVER_linear_parallel - COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} - $ ${MPIEXEC_POSTFLAGS} - ) - set_tests_properties(MODULE_HSOLVER_linear_parallel PROPERTIES PROCESSORS 2 TIMEOUT 120) -endif() if (ENABLE_MPI) AddTest( diff --git a/source/source_io/module_current/td_current_pw.h b/source/source_io/module_current/td_current_pw.h index 825b79949dd..e6295280ca7 100644 --- a/source/source_io/module_current/td_current_pw.h +++ b/source/source_io/module_current/td_current_pw.h @@ -1,7 +1,7 @@ #ifndef TD_CURRENT_PW_H #define TD_CURRENT_PW_H -#include "source_pw/module_pwdft/td_obs_pw.h" +#include "source_pw/module_rt/td_obs_pw.h" #include diff --git a/source/source_io/module_current/td_spin_pw_io.h b/source/source_io/module_current/td_spin_pw_io.h index f2bf14d5cff..edb315f3886 100644 --- a/source/source_io/module_current/td_spin_pw_io.h +++ b/source/source_io/module_current/td_spin_pw_io.h @@ -1,6 +1,6 @@ #ifndef TD_SPIN_PW_IO_H #define TD_SPIN_PW_IO_H -#include "source_pw/module_pwdft/td_obs_pw.h" +#include "source_pw/module_rt/td_obs_pw.h" #include namespace ModuleIO diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index 64da203fa41..672afba6478 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -340,6 +340,10 @@ struct Input_para double td_print_eij = -1.0; ///< threshold to output Eij elements int td_edm = 0; ///< 0: new edm method 1: old edm method int propagator = 0; ///< method of propagator + std::string td_exp_method = "lanczos"; ///< PW ETRS exponential backend. + int td_exp_order = 4; ///< Fixed Taylor polynomial order. + double td_exp_thr = 0.0; ///< Zero selects the precision-dependent exponential tolerance. + int td_exp_krylov_dim = 32; ///< Maximum Lanczos dimension before frozen-operator subdivision. int td_stype = 0; ///< type of space domain 0 : length gauge 1: velocity gauge std::vector td_ttype = {0}; ///< type of time domain ///< 0 Gauss type function. diff --git a/source/source_io/module_parameter/read_inp_exx_dftu.cpp b/source/source_io/module_parameter/read_inp_exx_dftu.cpp index c4c9bc8c441..bbbcbe7e0f2 100644 --- a/source/source_io/module_parameter/read_inp_exx_dftu.cpp +++ b/source/source_io/module_parameter/read_inp_exx_dftu.cpp @@ -2,7 +2,6 @@ #include "source_base/tool_quit.h" #include "read_input.h" #include "read_input_tool.h" -#include namespace ModuleIO { void ReadInput::item_exx() @@ -889,6 +888,8 @@ void ReadInput::item_dftu() } { Input_Item item("onsite_radius"); + item.domain.set_rule("value>0"); + item.domain.set_active_when("onsite_radius!=0"); item.annotation = "cutoff radius of onsite projection orbitals (Bohr)"; item.category = "DFT+U correction"; item.type = "Real"; @@ -913,12 +914,6 @@ Provide numerical atomic orbital files through `NUMERICAL_ORBITAL` in `STRU`, wi para.input.onsite_radius = use_onsite ? 3.0 : 0.0; } }; - item.check_value = [](const Input_Item& item, const Parameter& para) { - if (item.is_read() && (!(para.inp.onsite_radius > 0.0) || !std::isfinite(para.inp.onsite_radius))) - { - ModuleBase::WARNING_QUIT("ReadInput", "Explicit onsite_radius must be finite and positive."); - } - }; this->add_item(item); } } diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index 9273345eee2..cd54cefdc4f 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -8,56 +8,12 @@ #include #include #include -#include namespace ModuleIO { namespace { -int parse_td_integer(const Input_Item& item) -{ - std::size_t consumed = 0; - try - { - if (item.str_values.size() == 1) - { - const int value = std::stoi(item.str_values[0], &consumed); - if (consumed == item.str_values[0].size()) - { - return value; - } - } - } - catch (const std::exception&) - { - } - const std::string message = item.label + " must be a positive integer."; - ModuleBase::WARNING_QUIT("ReadInput", message); - return 0; -} - -std::vector parse_supersine_steps(const Input_Item& item, const int default_step) -{ - const std::vector tokens - = item.is_read() ? item.str_values : std::vector(1, "default"); - std::vector values; - for (const std::string& token: tokens) - { - if (token == "default") - { - values.push_back(default_step); - } - else - { - std::vector parsed; - parse_expression(std::vector(1, token), parsed); - values.insert(values.end(), parsed.begin(), parsed.end()); - } - } - return values; -} - struct FieldParameterRule { const char* name; @@ -207,7 +163,7 @@ void ReadInput::item_rt_tddft() * `newton_schulz`: Newton-Schulz iteration for a Gram matrix close to the identity. Starting from $\boldsymbol{C}_0=\boldsymbol{I}$, approximate $\boldsymbol{S}^{-1/2}$ using $\boldsymbol{C}_{j+1}=\boldsymbol{C}_j(3\boldsymbol{I}-\boldsymbol{S}\boldsymbol{C}_j^2)/2$. * `none`: Disable orthonormalization. -[NOTE] Orthonormalization is applied after the Crank-Nicolson linear solve and uses an orthogonality tolerance independent of `lin_thr`. With orthonormalization enabled, the propagated Gram matrix must differ from the identity by at most `1e-6` (single precision) or `1e-12` (double precision) per element; unsuccessful corrections stop the calculation. Wavefunctions within this tolerance are left unchanged. Nonfinite or nonpositive orbital norms are rejected even with `none`.)"; +[NOTE] Orthonormalization is applied after each wavefunction propagation. With orthonormalization enabled, the propagated Gram matrix must differ from the identity by at most `1e-6` (single precision) or `1e-12` (double precision) per element; unsuccessful corrections stop the calculation. Wavefunctions within this tolerance are left unchanged. Nonfinite or nonpositive orbital norms are rejected even with `none`.)"; item.default_value = "cholesky"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); @@ -244,10 +200,7 @@ void ReadInput::item_rt_tddft() item.default_value = "0"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_max_halvings = parse_td_integer(item); - }; - sync_int(input.td_max_halvings); + read_sync_int(input.td_max_halvings); item.check_value = [](const Input_Item&, const Parameter& para) { if (para.inp.td_max_halvings < 0 || para.inp.td_max_halvings >= std::numeric_limits::digits) { @@ -265,10 +218,7 @@ void ReadInput::item_rt_tddft() item.default_value = "100"; item.unit = ""; item.set_availability("esolver_type==tddft and basis_type in [pw, lcao]"); - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_scf_nmax = parse_td_integer(item); - }; - sync_int(input.td_scf_nmax); + read_sync_int(input.td_scf_nmax); item.check_value = [](const Input_Item&, const Parameter& para) { if (para.inp.td_scf_nmax < 2) { @@ -352,7 +302,7 @@ Components are ordered $xx,xy,xz,yx,yy,yz,zx,zy,zz$, with flow direction first a The initial ground-state diagonalization is controlled by `ks_solver`.)"; item.default_value = "gmres"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_string(input.lin_solver); item.check_value = [](const Input_Item&, const Parameter& para) { if (para.inp.lin_solver != "bicgstab" && para.inp.lin_solver != "cgs" && para.inp.lin_solver != "gmres") @@ -376,7 +326,7 @@ The initial ground-state diagonalization is controlled by `ks_solver`.)"; Preconditioning changes the convergence rate, while `lin_thr` still controls the residual of the original equation.)"; item.default_value = "kinetic_recycle"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_string(input.lin_precond); item.check_value = [](const Input_Item&, const Parameter& para) { if (para.inp.lin_precond != "kinetic" && para.inp.lin_precond != "none" @@ -399,26 +349,8 @@ Preconditioning changes the convergence rate, while `lin_thr` still controls the All methods check the residual at the end of the linear solve, before orthonormalization. GMRES can use explicit residual reconstruction with periodic independent checks when `lin_reconstruct` is enabled. Subsequent orthonormalization uses a separate orthogonality tolerance and does not guarantee that the corrected wavefunctions satisfy the same linear residual tolerance.)"; item.default_value = "0"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); - item.read_value = [](const Input_Item& item, Parameter& para) { - std::size_t consumed = 0; - try - { - if (item.str_values.size() == 1) - { - para.input.lin_thr = std::stod(item.str_values[0], &consumed); - if (consumed == item.str_values[0].size()) - { - return; - } - } - } - catch (const std::exception&) - { - } - ModuleBase::WARNING_QUIT("ReadInput", "lin_thr must be a nonnegative finite real number."); - }; - sync_double(input.lin_thr); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); + read_sync_double(input.lin_thr); item.check_value = [](const Input_Item&, const Parameter& para) { if (!std::isfinite(para.inp.lin_thr) || para.inp.lin_thr < 0.0) { @@ -436,11 +368,8 @@ All methods check the residual at the end of the linear solve, before orthonorma item.description = "Positive maximum number of iterations for each PW real-time linear solve. Failure to converge stops the calculation. Initial-state self-consistency uses `scf_nmax`; real-time self-consistency uses `td_scf_nmax`."; item.default_value = "500"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.lin_maxiter = parse_td_integer(item); - }; - sync_int(input.lin_maxiter); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); + read_sync_int(input.lin_maxiter); this->add_item(item); } { @@ -452,11 +381,8 @@ All methods check the residual at the end of the linear solve, before orthonorma item.description = "Positive maximum Arnoldi dimension per GMRES cycle. `lin_maxiter` limits the total iterations across all cycles."; item.default_value = "20"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft and lin_solver==gmres"); - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.lin_gmres_restart = parse_td_integer(item); - }; - sync_int(input.lin_gmres_restart); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0 and lin_solver==gmres"); + read_sync_int(input.lin_gmres_restart); this->add_item(item); } { @@ -467,7 +393,7 @@ All methods check the residual at the end of the linear solve, before orthonorma item.description = R"(Project the Crank-Nicolson equation $\boldsymbol{L}\boldsymbol{x}=\boldsymbol{b}$ onto the previous-time wavefunction subspace to initialize the first propagation solve of each time step. With those wavefunctions as the columns of $\boldsymbol{U}$, the projected initial guess is $\boldsymbol{x}_0=\boldsymbol{U}(\boldsymbol{U}^{\dagger}\boldsymbol{L}\boldsymbol{U})^{-1}\boldsymbol{U}^{\dagger}\boldsymbol{b}$. Evaluate and reuse the initial residual $\boldsymbol{r}_0=\boldsymbol{b}-\boldsymbol{L}\boldsymbol{x}_0$ using the stored subspace operator images $\boldsymbol{L}\boldsymbol{U}$, without an additional Hamiltonian application. Available with all linear solvers and preconditioners; later self-consistency iterations retain their current wavefunction guess.)"; item.default_value = "true"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft"); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_bool(input.td_cn_init); this->add_item(item); } @@ -483,7 +409,7 @@ Use an internal tolerance of 0.8 times the effective `lin_thr` and independently Only effective for `lin_solver=gmres`; ignored otherwise.)"; item.default_value = "true"; item.unit = ""; - item.set_availability("basis_type==pw and esolver_type==tddft and lin_solver==gmres"); + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0 and lin_solver==gmres"); read_sync_bool(input.lin_reconstruct); this->add_item(item); } @@ -495,14 +421,13 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; item.description = "The number of electronic propagation steps between two ionic steps."; item.default_value = "1"; item.unit = ""; - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.estep_per_md = parse_td_integer(item); + read_sync_int(input.estep_per_md); + item.reset_value = [](const Input_Item&, Parameter& para) { if (para.input.estep_per_md <= 0) { ModuleBase::WARNING_QUIT("ReadInput", "estep_per_md must be a positive integer."); } }; - sync_int(input.estep_per_md); this->add_item(item); } // real time TDDFT @@ -567,19 +492,100 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; { Input_Item item("td_propagator"); item.annotation = "method of propagator"; - item.category = "Real-Time TDDFT (LCAO)"; + item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; - item.description = R"(Method used to propagate the electronic states in a nonorthogonal LCAO basis. The formulas below use Hartree atomic units, with $S$, $H$, and $\Delta t=\mathtt{td\_dt}$ evaluated as required by each approximation. -* `0`: Crank-Nicolson through an explicitly constructed evolution matrix, $U=\left[S+\mathrm{i}H\Delta t/2\right]^{-1}\left[S-\mathrm{i}H\Delta t/2\right]$. + item.description = R"(Method used to propagate the electronic states. The formulas below use Hartree atomic units, with $\Delta t$ denoting the electronic propagation time step and $S$ the LCAO overlap matrix. The Hamiltonian $H$ and overlap matrix $S$ are evaluated as required by each approximation. + +For a plane-wave (PW) basis: +* `0`: Crank-Nicolson, $\left[I+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[I-\mathrm{i}H\Delta t/2\right]\psi(t)$. The linear system uses the same midpoint Hamiltonian on both sides. +* `2`: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}H(t)\Delta t/2\right]$. Select the exponential algorithm with `td_exp_method`. + +For a nonorthogonal LCAO basis: +* `0`: Crank-Nicolson, $U=\left[S+\mathrm{i}H\Delta t/2\right]^{-1}\left[S-\mathrm{i}H\Delta t/2\right]$. The evolution matrix is constructed explicitly and applied to the wavefunctions. * `1`: Fourth-order Taylor approximation to the exponential. With $\mathcal{A}=-\mathrm{i}S^{-1}H\Delta t$, $U=I+\mathcal{A}+\mathcal{A}^2/2+\mathcal{A}^3/6+\mathcal{A}^4/24$. -* `2`: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}S^{-1}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}S^{-1}H(t)\Delta t/2\right]$. In the implementation, each exponential is replaced by the fourth-order Taylor polynomial from method `1` evaluated with a half time step. -* `3`: Crank-Nicolson by directly solving $\left[S+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[S-\mathrm{i}H\Delta t/2\right]\psi(t)$. +* `2`: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}S^{-1}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}S^{-1}H(t)\Delta t/2\right]$. Each exponential uses the fourth-order Taylor approximation from method `1` with a half time step. +* `3`: Crank-Nicolson, $\left[S+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[S-\mathrm{i}H\Delta t/2\right]\psi(t)$. The linear system is solved directly. -[NOTE] GPU execution currently supports only method `0` in both single-GPU and multi-GPU solver configurations. CPU execution supports methods `0` through `3`.)"; +[NOTE] PW supports methods `0` and `2` on CPU and GPU. LCAO supports methods `0` through `3` on CPU and only method `0` on GPU.)"; item.default_value = "0"; item.unit = ""; - item.set_availability("basis_type==lcao and esolver_type==tddft"); + item.set_availability("(basis_type==pw or basis_type==lcao) and esolver_type==tddft"); read_sync_int(input.propagator); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.basis_type == "pw" && para.inp.esolver_type == "tddft" + && para.inp.propagator != 0 && para.inp.propagator != 2) + { + ModuleBase::WARNING_QUIT("ReadInput", "PW td_propagator must be 0 (CN) or 2 (ETRS)."); + } + }; + this->add_item(item); + } + { + Input_Item item("td_exp_method"); + item.annotation = "exponential algorithm for PW ETRS"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "String"; + item.description = R"(Algorithm used to evaluate $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}$ for Enforced time-reversal symmetry (ETRS) propagation with a plane-wave basis. The Hamiltonian $\boldsymbol{H}$ is held fixed over the evolution interval $\tau$. The formulas below use Hartree atomic units. +* `lanczos`: Lanczos method with full reorthogonalization, $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}\approx\lVert\boldsymbol{\psi}\rVert\boldsymbol{Q}_m\exp(-\mathrm{i}\tau\boldsymbol{H}_m)\boldsymbol{e}_1$, where $\boldsymbol{Q}_m$ contains the orthonormal Krylov basis vectors, $\boldsymbol{H}_m=\boldsymbol{Q}_m^\dagger\boldsymbol{H}\boldsymbol{Q}_m$, and $\boldsymbol{e}_1$ is the first unit vector. The estimated relative error is controlled by `td_exp_thr`, and the maximum Krylov dimension by `td_exp_krylov_dim`. +* `taylor`: Fixed-order Taylor approximation, $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}\approx\sum_{k=0}^{p}(-\mathrm{i}\tau\boldsymbol{H})^k\boldsymbol{\psi}/k!$, where the order $p$ is specified by `td_exp_order`.)"; + item.default_value = "lanczos"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2"); + read_sync_string(input.td_exp_method); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.td_exp_method != "taylor" && para.inp.td_exp_method != "lanczos") + { + ModuleBase::WARNING_QUIT("ReadInput", "td_exp_method must be taylor or lanczos."); + } + }; + this->add_item(item); + } + { + Input_Item item("td_exp_order"); + item.annotation = "Taylor polynomial order for PW ETRS"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Integer"; + item.description = "Taylor polynomial order $p$ used by `td_exp_method=taylor`. Must be an integer of at least 2."; + item.default_value = "4"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==taylor"); + read_sync_int(input.td_exp_order); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.td_exp_order < 2) { ModuleBase::WARNING_QUIT("ReadInput", "td_exp_order must be at least 2."); } + }; + this->add_item(item); + } + { + Input_Item item("td_exp_thr"); + item.annotation = "relative exponential error target for PW Lanczos"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Real"; + item.description = R"(Target for the estimated error in each Lanczos exponential calculation, relative to the input wavefunction norm. Must be nonnegative and finite. Zero selects $\max(10^{-10},100\epsilon)$, where $\epsilon$ is machine epsilon for the wavefunction precision, giving `1e-10` in double precision and approximately `1.19209e-5` in single precision. A positive value specifies the target directly.)"; + item.default_value = "0"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==lanczos"); + read_sync_double(input.td_exp_thr); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (!std::isfinite(para.inp.td_exp_thr) || para.inp.td_exp_thr < 0) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_exp_thr must be a nonnegative finite real number."); + } + }; + this->add_item(item); + } + { + Input_Item item("td_exp_krylov_dim"); + item.annotation = "maximum Lanczos dimension for PW ETRS"; + item.category = "Real-Time TDDFT (PW)"; + item.type = "Integer"; + item.description = "Maximum number of Krylov basis vectors used in each Lanczos exponential calculation. Must be an integer of at least 2. Larger values increase wavefunction storage and orthogonalization work."; + item.default_value = "32"; + item.unit = ""; + item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==lanczos"); + read_sync_int(input.td_exp_krylov_dim); + item.check_value = [](const Input_Item&, const Parameter& para) { + if (para.inp.td_exp_krylov_dim < 2) { ModuleBase::WARNING_QUIT("ReadInput", "td_exp_krylov_dim must be at least 2."); } + }; this->add_item(item); } { @@ -1034,7 +1040,15 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually item.unit = ""; item.set_availability("td_ttype contains 4"); item.reset_value = [](const Input_Item& item, Parameter& para) { - para.input.td_supsine_tstart = parse_supersine_steps(item, para.input.td_tstart); + std::vector tokens = item.is_read() ? item.str_values : std::vector(1, "default"); + for (std::string& token: tokens) + { + if (token == "default") + { + token = std::to_string(para.input.td_tstart); + } + } + parse_expression(tokens, para.input.td_supsine_tstart); }; item.get_final_value = [](Input_Item& item, const Parameter&) { item.final_value << (item.is_read() ? longstring(item.str_values) : "default"); @@ -1052,7 +1066,15 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually item.unit = ""; item.set_availability("td_ttype contains 4"); item.reset_value = [](const Input_Item& item, Parameter& para) { - para.input.td_supsine_tend = parse_supersine_steps(item, para.input.td_tend); + std::vector tokens = item.is_read() ? item.str_values : std::vector(1, "default"); + for (std::string& token: tokens) + { + if (token == "default") + { + token = std::to_string(para.input.td_tend); + } + } + parse_expression(tokens, para.input.td_supsine_tend); }; item.get_final_value = [](Input_Item& item, const Parameter&) { item.final_value << (item.is_read() ? longstring(item.str_values) : "default"); diff --git a/source/source_pw/CMakeLists.txt b/source/source_pw/CMakeLists.txt index 0efea3ccb93..a4adc51e172 100644 --- a/source/source_pw/CMakeLists.txt +++ b/source/source_pw/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(module_pwdft) add_subdirectory(module_ofdft) add_subdirectory(module_stodft) add_subdirectory(module_dfpt) +add_subdirectory(module_rt) diff --git a/source/source_pw/module_pwdft/CMakeLists.txt b/source/source_pw/module_pwdft/CMakeLists.txt index 7686ef85a63..10308dc0c7a 100644 --- a/source/source_pw/module_pwdft/CMakeLists.txt +++ b/source/source_pw/module_pwdft/CMakeLists.txt @@ -5,18 +5,12 @@ list( hamilt_pw.cpp pw_hybrid_nscf.cpp pw_hybrid_source.cpp - td_history_pw.cpp - td_pw.cpp - td_vel_pw.cpp - td_spin_pw.cpp op_pw_ekin.cpp - op_pw_ekin_td.cpp op_pw_veff.cpp projector_gradient.cpp velocity_workspace.cpp nonlocal_workspace.cpp op_pw_nl.cpp - op_pw_nl_td.cpp op_pw_meta.cpp op_pw_vel.cpp op_pw.cpp diff --git a/source/source_pw/module_pwdft/hamilt_pw.cpp b/source/source_pw/module_pwdft/hamilt_pw.cpp index 071096637ac..8b4a23de6cd 100644 --- a/source/source_pw/module_pwdft/hamilt_pw.cpp +++ b/source/source_pw/module_pwdft/hamilt_pw.cpp @@ -1,19 +1,19 @@ #include "hamilt_pw.h" #include "op_pw_ekin.h" -#include "op_pw_ekin_td.h" #include "op_pw_exx.h" #include "op_pw_meta.h" #include "op_pw_nl.h" -#include "op_pw_nl_td.h" #include "op_pw_proj.h" #include "op_pw_veff.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/parallel_reduce.h" +#include "source_estate/module_pot/pot_pcc.h" #include "source_hamilt/module_xc/general_exx_info.h" // for General_Exx_Info type #include "source_io/module_parameter/parameter.h" -#include "source_estate/module_pot/pot_pcc.h" +#include "source_pw/module_rt/op_pw_ekin_td.h" +#include "source_pw/module_rt/op_pw_nl_td.h" namespace hamilt { @@ -43,8 +43,7 @@ HamiltPW::HamiltPW(elecstate::Potential* pot_in, if (PARAM.inp.t_in_h) { // Operator* ekinetic = new Ekinetic> - Operator* ekinetic - = new Ekinetic>(tpiba2, gk2, wfc_basis->nks, wfc_basis->npwk_max); + Operator* ekinetic = new Ekinetic>(tpiba2, gk2, wfc_basis->nks, wfc_basis->npwk_max); if (this->ops == nullptr) { this->ops = ekinetic; @@ -87,7 +86,10 @@ HamiltPW::HamiltPW(elecstate::Potential* pot_in, { pot_register_in.push_back("surchem"); } - if (input.imp_sol == 2) { pot_register_in.push_back("sccs"); } + if (input.imp_sol == 2) + { + pot_register_in.push_back("sccs"); + } if (input.assume_isolated == "pcc_0d") { pot_register_in.push_back("pcc"); @@ -172,11 +174,8 @@ HamiltPW::HamiltPW(elecstate::Potential* pot_in, } if (PARAM.inp.sc_mag_switch || PARAM.inp.dft_plus_u) { - Operator* onsite_proj = new OnsiteProj>(isk, - ucell, - p_dftu, - PARAM.inp.sc_mag_switch, - (PARAM.inp.dft_plus_u > 0)); + Operator* onsite_proj + = new OnsiteProj>(isk, ucell, p_dftu, PARAM.inp.sc_mag_switch, (PARAM.inp.dft_plus_u > 0)); this->ops->add(onsite_proj); } if (exx_info && exx_info->cal_exx) @@ -230,7 +229,8 @@ void HamiltPW::bind_td_state(const Real* veff, const ModuleBase::Vect else if (op->get_cal_type() == calculation_type::pw_nonlocal) { TDNonlocalPW* td_nonlocal = dynamic_cast*>(op); - if (td_nonlocal != nullptr) td_nonlocal->set_A_ha(A_ha); + if (td_nonlocal != nullptr) + td_nonlocal->set_A_ha(A_ha); } op = op->next_op; } @@ -275,17 +275,7 @@ void HamiltPW::sPsi(const T* psi_in, // psi if (nbands == 1) { int inc = 1; - gemv_op()(transa, - npw, - this->ppcell->nkb, - &one, - this->vkb, - this->ppcell->vkbnc, - psi_in, - inc, - &zero, - becp, - inc); + gemv_op()(transa, npw, this->ppcell->nkb, &one, this->vkb, this->ppcell->vkbnc, psi_in, inc, &zero, becp, inc); } else { @@ -322,7 +312,7 @@ void HamiltPW::sPsi(const T* psi_in, // psi const int nh = atoms->ncpp.nh; T* qqc = nullptr; resmem_complex_op()(qqc, nh * nh, "Hamilt::qqc"); - std::vector qqc_host(nh*nh); + std::vector qqc_host(nh * nh); const double* qq_now_host = &this->ppcell->qq_nt.ptr[it * this->ppcell->nhm * this->ppcell->nhm]; for (int i = 0; i < nh; i++) @@ -362,17 +352,7 @@ void HamiltPW::sPsi(const T* psi_in, // psi if (nbands == 1) { const int inc = 1; - gemv_op()(transa, - npw, - this->ppcell->nkb, - &one, - this->vkb, - this->ppcell->vkbnc, - ps, - inc, - &one, - spsi, - inc); + gemv_op()(transa, npw, this->ppcell->nkb, &one, this->vkb, this->ppcell->vkbnc, ps, inc, &one, spsi, inc); } else { diff --git a/source/source_pw/module_pwdft/test/CMakeLists.txt b/source/source_pw/module_pwdft/test/CMakeLists.txt index fa570403475..9fb5e50b28c 100644 --- a/source/source_pw/module_pwdft/test/CMakeLists.txt +++ b/source/source_pw/module_pwdft/test/CMakeLists.txt @@ -3,46 +3,6 @@ abacus_disable_feature_definitions(__CUDA) abacus_disable_feature_definitions(__ROCM) abacus_disable_feature_definitions(__EXX) -AddTest( - TARGET MODULE_PW_td_observables - LIBS parameter base device container psi planewave nao_smoothgen symmetry - SOURCES - test_td_obs_pw.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp - ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp - ../op_pw_vel.cpp ../projector_gradient.cpp ../velocity_workspace.cpp ../nonlocal_workspace.cpp - ../vnl_pw.cpp ../vnl_pw_init_vnl.cpp ../vnl_pw_getvnl.cpp ../vnl_pw_deeq.cpp - ../vnl_pw_grad.cpp ../vnl_pw_table.cpp ../vnl_pw_qrad.cpp - ../stru_fac.cpp ../stru_fac_k.cpp ../soc.cpp ../nonlocal_maths.cpp - ../../module_proj/onsite_proj.cpp ../../module_proj/onsite_proj_init.cpp - ../../module_proj/onsite_proj_overlap.cpp ../../module_proj/onsite_proj_tools.cpp - ../../module_proj/onsite_proj_tools_becp.cpp ../../module_proj/radial_proj.cpp - ../../module_proj/onsite_proj_print.cpp - ../../../source_basis/module_ao/orb_gaunt_table.cpp - ../../../source_cell/klist.cpp ../../../source_cell/reciprocal_grid.cpp - ../../../source_cell/klist_io.cpp - ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp - ../../../source_cell/atom_spec.cpp ../../../source_cell/atom_pseudo.cpp ../../../source_cell/pseudo.cpp - ../../../source_cell/magnetism.cpp ../../../source_cell/sep.cpp ../../../source_cell/sep_cell.cpp - ../../../source_cell/read_pp.cpp ../../../source_cell/read_pp_complete.cpp - ../../../source_cell/read_pp_upf100.cpp ../../../source_cell/read_pp_upf201.cpp - ../../../source_cell/read_pp_vwr.cpp ../../../source_cell/read_pp_blps.cpp - ../../../source_estate/elecstate.cpp ../../../source_estate/module_charge/charge.cpp - ../../../source_estate/module_pot/potential_new.cpp - ../../../source_hamilt/module_xc/xc_functional.cpp -) -target_compile_definitions(MODULE_PW_td_observables PRIVATE TD_TEST_PP_DIR="${PROJECT_SOURCE_DIR}/tests/PP_ORB") -# Keep real PW initialization and observable implementations without linking the SCF driver. -if(MSVC) - target_compile_options(MODULE_PW_td_observables PRIVATE /Gy /Gw) - target_link_options(MODULE_PW_td_observables PRIVATE /OPT:REF) -else() - target_compile_options(MODULE_PW_td_observables PRIVATE -ffunction-sections -fdata-sections) - if(APPLE) - target_link_options(MODULE_PW_td_observables PRIVATE -Wl,-dead_strip) - else() - target_link_options(MODULE_PW_td_observables PRIVATE -Wl,--gc-sections) - endif() -endif() addtest( TARGET diff --git a/source/source_pw/module_rt/CMakeLists.txt b/source/source_pw/module_rt/CMakeLists.txt new file mode 100644 index 00000000000..f95a53bbd18 --- /dev/null +++ b/source/source_pw/module_rt/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(module_rt_pw OBJECT + td_prop_pw.cpp + td_hamilt_pw.cpp + prop_cn_pw.cpp + prop_etrs_pw.cpp + td_history_pw.cpp + td_pw.cpp + td_vel_pw.cpp + td_spin_pw.cpp + op_pw_ekin_td.cpp + op_pw_nl_td.cpp +) + +if(ENABLE_COVERAGE) + add_coverage(module_rt_pw) +endif() + +if(BUILD_TESTING AND ENABLE_MPI) + add_subdirectory(test) +endif() diff --git a/source/source_pw/module_rt/op_pw_ekin_td.cpp b/source/source_pw/module_rt/op_pw_ekin_td.cpp index f604ea3d9c0..602e41bf0c8 100644 --- a/source/source_pw/module_rt/op_pw_ekin_td.cpp +++ b/source/source_pw/module_rt/op_pw_ekin_td.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_pwdft/op_pw_ekin_td.h" +#include "source_pw/module_rt/op_pw_ekin_td.h" #include "source_base/module_device/memory_op.h" #include "source_base/timer.h" diff --git a/source/source_pw/module_rt/op_pw_ekin_td.h b/source/source_pw/module_rt/op_pw_ekin_td.h index a02663a5011..e0d06b73dfd 100644 --- a/source/source_pw/module_rt/op_pw_ekin_td.h +++ b/source/source_pw/module_rt/op_pw_ekin_td.h @@ -1,9 +1,9 @@ #ifndef OP_PW_EKIN_TD_H #define OP_PW_EKIN_TD_H -#include "op_pw.h" #include "source_base/module_container/ATen/core/tensor.h" #include "source_basis/module_pw/pw_basis_k.h" +#include "source_pw/module_pwdft/op_pw.h" namespace hamilt { @@ -16,7 +16,10 @@ class TDEkineticPW : public OperatorPW public: /** @brief Bind the vector potential in Hartree atomic units before init(). */ - void set_A_ha(const ModuleBase::Vector3& A) { A_ha_ = A; } + void set_A_ha(const ModuleBase::Vector3& A) + { + A_ha_ = A; + } TDEkineticPW(const ModulePW::PW_Basis_K* wfc_basis_in); virtual ~TDEkineticPW(); diff --git a/source/source_pw/module_rt/op_pw_nl_td.cpp b/source/source_pw/module_rt/op_pw_nl_td.cpp index a2856e3b68a..08b2953a15a 100644 --- a/source/source_pw/module_rt/op_pw_nl_td.cpp +++ b/source/source_pw/module_rt/op_pw_nl_td.cpp @@ -1,4 +1,4 @@ -#include "op_pw_nl_td.h" +#include "source_pw/module_rt/op_pw_nl_td.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" diff --git a/source/source_pw/module_rt/op_pw_nl_td.h b/source/source_pw/module_rt/op_pw_nl_td.h index ad3756c6057..9e8b35ac116 100644 --- a/source/source_pw/module_rt/op_pw_nl_td.h +++ b/source/source_pw/module_rt/op_pw_nl_td.h @@ -1,8 +1,8 @@ #ifndef OP_PW_NL_TD_H #define OP_PW_NL_TD_H -#include "op_pw.h" #include "source_pw/module_pwdft/nonlocal_workspace.h" +#include "source_pw/module_pwdft/op_pw.h" #include "source_pw/module_pwdft/vnl_pw.h" namespace hamilt diff --git a/source/source_pw/module_rt/prop_cn_pw.cpp b/source/source_pw/module_rt/prop_cn_pw.cpp new file mode 100644 index 00000000000..0bc08078129 --- /dev/null +++ b/source/source_pw/module_rt/prop_cn_pw.cpp @@ -0,0 +1,51 @@ +#include "source_pw/module_rt/prop_cn_pw.h" + +#include "source_base/kernels/math_kernel_op.h" + +namespace pw +{ +template +CNPropPW::CNPropPW(const ModulePW::PW_Basis_K& basis, + const hsolver::PWLinearOptions& cn_opts, + const hsolver::diag_comm_info& comm, + std::ostream& log) + : solver_(basis, cn_opts, comm, log) +{ +} +template +void CNPropPW::invalidate_basis() +{ + solver_.invalidate_basis(); +} +template +void CNPropPW::evolve(PropArgs& args, psi::Psi* current) +{ + using Real = typename T::value_type; + const Real* potential = args.potential.template get_veff_smooth_data(); + if (args.scf_iter > 1) + { + const ModuleBase::matrix& shape = args.potential.get_veff_smooth(); + const int size = shape.nr * shape.nc; + hsolver::linear_buffer(&veff_mid_, size); + Real* buffer = veff_mid_.template data(); + const Real* previous = args.history.left_potential(); + ModuleBase::vector_add_vector_op()(size, buffer, previous, Real(0.5), potential, Real(0.5)); + potential = buffer; + } + ModuleBase::Vector3 vecpot_ha(0, 0, 0); + if (args.field.gauge() == 1) + { + vecpot_ha = args.field.A_prop_ha(); + } + args.hamilt.bind(potential, vecpot_ha); + const auto& previous = args.history.previous(); + const double dt = args.field.interval_dt_ha(); + solver_.solve(args.hamilt, previous, current, dt, vecpot_ha, args.elec_step, args.scf_iter, args.log_detail); +} +template class CNPropPW, base_device::DEVICE_CPU>; +template class CNPropPW, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class CNPropPW, base_device::DEVICE_GPU>; +template class CNPropPW, base_device::DEVICE_GPU>; +#endif +} // namespace pw diff --git a/source/source_pw/module_rt/prop_cn_pw.h b/source/source_pw/module_rt/prop_cn_pw.h new file mode 100644 index 00000000000..353d40b6de1 --- /dev/null +++ b/source/source_pw/module_rt/prop_cn_pw.h @@ -0,0 +1,29 @@ +#ifndef PW_PROP_CN_PW_H +#define PW_PROP_CN_PW_H +#include "source_pw/module_rt/td_prop_pw.h" + +namespace pw +{ +template +class CNPropPW final : public PWProp +{ + private: + hsolver::CNSolverPW solver_; + ct::Tensor veff_mid_; + + public: + CNPropPW(const ModulePW::PW_Basis_K& basis, + const hsolver::PWLinearOptions& cn_opts, + const hsolver::diag_comm_info& comm, + std::ostream& log); + void begin_trial() override + { + } + void discard_trial() override + { + } + void invalidate_basis() override; + void evolve(PropArgs& args, psi::Psi* current) override; +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/prop_etrs_pw.cpp b/source/source_pw/module_rt/prop_etrs_pw.cpp new file mode 100644 index 00000000000..dc2fa10a30e --- /dev/null +++ b/source/source_pw/module_rt/prop_etrs_pw.cpp @@ -0,0 +1,133 @@ +#include "source_pw/module_rt/prop_etrs_pw.h" + +#include "source_base/parallel_device.h" +#include "source_base/tool_quit.h" + +#include +#include + +namespace pw +{ +template +ETRSPropPW::ETRSPropPW(const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log) + : pool_comm_(comm), orb_opts_(prop_opts.orb), log_(log) +{ + exponential_ = hsolver::make_exp_action(prop_opts.exp, comm); + log_ << " PW RT-TDDFT propagator: ETRS; exponential: "; + if (prop_opts.exp.method == hsolver::ExpMethod::taylor) + { + log_ << "taylor; order=" << prop_opts.exp.taylor_order << "; predictor=shared powers\n"; + } + else + { + const double rel_tol = exponential_->rel_tol(); + log_ << "lanczos; tolerance=" << rel_tol << "; maximum dimension=" << prop_opts.exp.krylov_max << '\n'; + } +} +template +void ETRSPropPW::begin_trial() +{ + half_ready_ = false; +} +template +void ETRSPropPW::discard_trial() +{ + half_ready_ = false; +} +template +void ETRSPropPW::invalidate_basis() +{ + half_ready_ = false; +} +template +void ETRSPropPW::report(const hsolver::ExpResult& result, const PropArgs& args, int ik) const +{ + std::ostringstream text; + text << " PW ETRS exponential: step=" << args.elec_step << " scf_iter=" << args.scf_iter + << " global_k=" << orb_opts_.global_k_indices.at(ik) + 1 << " order_or_dimension=" << result.order_or_dim + << " operator_calls=" << result.hpsi_calls << " operator_columns=" << result.hpsi_cols << " subdivisions=" << result.bisect_count; + if (result.has_err_est) + { + text << " estimated_error=" << result.rel_err_est; + } + if (args.log_detail) + { + log_ << text.str() << '\n'; + } + if (result.state != hsolver::ExpState::success) + { + text << " failed_band=" << result.failed_band + 1 << "; reduce td_dt or adjust exponential controls."; + const std::string message = text.str(); + // stdout is disabled on non-world-root ranks; report from the failing pool. + if (pool_comm_.rank == 0) + { + std::cerr << message << std::endl; + } +#ifdef __MPI + // Flush the pool root's diagnostic before any peer can terminate the MPI job. + double diag_sent = 1.0; + Parallel_Common::bcast_data(&diag_sent, 1, pool_comm_.comm, 0); +#endif + ModuleBase::WARNING_QUIT("ETRSPropPW", message); + } +} +template +void ETRSPropPW::evolve(PropArgs& args, psi::Psi* current) +{ + using Real = typename T::value_type; + const bool predictor = args.scf_iter == 1; + const auto& previous = args.history.previous(); + const int ld = current->get_nbasis(); + const int bands = current->get_nbands(); + const int64_t stride = static_cast(ld) * bands; + if (predictor) + { + const int64_t size = stride * current->get_nk(); + if (half_psi_.NumElements() < size) + { + // Release obsolete storage before allocating a larger half-step buffer. + half_psi_ = ct::Tensor(); + } + hsolver::linear_buffer(&half_psi_, size); + left_gen_ = args.history.generation(); + args.hamilt.bind(args.history.left_potential(), args.field.A_left_ha()); + } + else + { + if (!half_ready_ || left_gen_ != args.history.generation()) + { + ModuleBase::WARNING_QUIT("ETRSPropPW", "Missing left half-step for the current trial."); + } + const Real* endpoint = args.potential.template get_veff_smooth_data(); + args.hamilt.bind(endpoint, args.field.A_right_ha()); + } + const double alpha = args.field.interval_dt_ha() / 4.0; + for (int ik = 0; ik < current->get_nk(); ++ik) + { + args.hamilt.update_k(ik); + current->fix_k(ik); + previous.fix_k(ik); + const hsolver::OrbLayout layout{ld, current->get_ngk(ik), orb_opts_.npol, bands}; + T* output = current->get_pointer(); + T* half = half_psi_.template data() + ik * stride; + hsolver::ExpResult result; + if (predictor) + { + const T* input = previous.get_pointer(); + result = exponential_->apply_pair(args.hamilt, layout, alpha, input, half, output); + } + else + { + result = exponential_->apply(args.hamilt, layout, alpha, half, output); + } + report(result, args, ik); + } + half_ready_ = true; +} +template class ETRSPropPW, base_device::DEVICE_CPU>; +template class ETRSPropPW, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class ETRSPropPW, base_device::DEVICE_GPU>; +template class ETRSPropPW, base_device::DEVICE_GPU>; +#endif +} // namespace pw diff --git a/source/source_pw/module_rt/prop_etrs_pw.h b/source/source_pw/module_rt/prop_etrs_pw.h new file mode 100644 index 00000000000..2cc838e2a66 --- /dev/null +++ b/source/source_pw/module_rt/prop_etrs_pw.h @@ -0,0 +1,29 @@ +#ifndef PW_PROP_ETRS_PW_H +#define PW_PROP_ETRS_PW_H +#include "source_hsolver/diag_comm_info.h" +#include "source_pw/module_rt/td_prop_pw.h" + +namespace pw +{ +template +class ETRSPropPW final : public PWProp +{ + private: + const hsolver::diag_comm_info pool_comm_; + std::unique_ptr> exponential_; + ct::Tensor half_psi_; + hsolver::TDOrbOpts orb_opts_; + std::ostream& log_; + bool half_ready_ = false; + unsigned long long left_gen_ = 0; + void report(const hsolver::ExpResult& result, const PropArgs& args, int ik) const; + + public: + ETRSPropPW(const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log); + void begin_trial() override; + void discard_trial() override; + void invalidate_basis() override; + void evolve(PropArgs& args, psi::Psi* current) override; +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/td_hamilt_pw.cpp b/source/source_pw/module_rt/td_hamilt_pw.cpp new file mode 100644 index 00000000000..cccf6bddf99 --- /dev/null +++ b/source/source_pw/module_rt/td_hamilt_pw.cpp @@ -0,0 +1,39 @@ +#include "source_pw/module_rt/td_hamilt_pw.h" + +namespace pw +{ +template +TDHamiltPW::TDHamiltPW(hamilt::HamiltPW& hamilt, const ModulePW::PW_Basis_K& basis) + : hamilt_(hamilt), op_(&hamilt, &basis) +{ +} +template +void TDHamiltPW::bind(const Real* veff, const ModuleBase::Vector3& vecpot_ha) +{ + veff_ = veff; + vecpot_ha_ = vecpot_ha; + hamilt_.bind_td_state(veff_, vecpot_ha_); +} +template +void TDHamiltPW::update_k(int ik) +{ + hamilt_.bind_td_state(veff_, vecpot_ha_); + op_.update_k(ik); +} +template +void TDHamiltPW::hpsi(const T* x, T* hx, int ld, int bands) const +{ + op_.hpsi(x, hx, ld, bands); +} +template +void TDHamiltPW::spsi(const T* x, T* sx, int ld, int bands) const +{ + op_.spsi(x, sx, ld, bands); +} +template class TDHamiltPW, base_device::DEVICE_CPU>; +template class TDHamiltPW, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class TDHamiltPW, base_device::DEVICE_GPU>; +template class TDHamiltPW, base_device::DEVICE_GPU>; +#endif +} // namespace pw diff --git a/source/source_pw/module_rt/td_hamilt_pw.h b/source/source_pw/module_rt/td_hamilt_pw.h new file mode 100644 index 00000000000..ec0ea87fb5d --- /dev/null +++ b/source/source_pw/module_rt/td_hamilt_pw.h @@ -0,0 +1,28 @@ +#ifndef PW_TD_HAMILT_PW_H +#define PW_TD_HAMILT_PW_H +#include "source_hamilt/hamilt_hs_adapter.h" +#include "source_pw/module_pwdft/hamilt_pw.h" + +namespace pw +{ +/** @brief A call-scoped PW sample; every k update refreshes shifted projectors. */ +template +class TDHamiltPW final : public hsolver::HSOperator +{ + private: + using Real = typename T::value_type; + hamilt::HamiltPW& hamilt_; + hamilt::HamiltHSOperator op_; + const Real* veff_ = nullptr; + ModuleBase::Vector3 vecpot_ha_; + + public: + TDHamiltPW(hamilt::HamiltPW& hamilt, const ModulePW::PW_Basis_K& basis); + /** @brief Select a potential sample whose storage outlives its applications. */ + void bind(const Real* veff, const ModuleBase::Vector3& vecpot_ha); + void update_k(int ik) override; + void hpsi(const T* x, T* hx, int ld, int bands) const override; + void spsi(const T* x, T* sx, int ld, int bands) const override; +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/td_history_pw.cpp b/source/source_pw/module_rt/td_history_pw.cpp index 8bbddaac0c5..cece1a71d33 100644 --- a/source/source_pw/module_rt/td_history_pw.cpp +++ b/source/source_pw/module_rt/td_history_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_pwdft/td_history_pw.h" +#include "source_pw/module_rt/td_history_pw.h" #include "source_base/kernels/math_kernel_op.h" #include "source_base/module_device/memory_op.h" @@ -13,7 +13,7 @@ namespace { template -void prepare_pair(const ModuleBase::matrix& potential, ct::Tensor* previous, ct::Tensor* midpoint) +void prepare_potential(const ModuleBase::matrix& potential, ct::Tensor* previous) { using CtDevice = typename ct::PsiToContainer::type; const ct::DeviceType device = ct::DeviceTypeToEnum::value; @@ -21,23 +21,9 @@ void prepare_pair(const ModuleBase::matrix& potential, ct::Tensor* previous, ct: if (previous->NumElements() < size || previous->data_type() != ct::DataTypeToEnum::value || previous->device_type() != device) { *previous = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); - *midpoint = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); } } -template -const Real* average_potential(const ModuleBase::matrix& shape, const Real* current, const ct::Tensor& previous, ct::Tensor* midpoint) -{ - const int size = shape.nr * shape.nc; - if (size == 0) - { - return current; - } - Real* buffer = midpoint->template data(); - ModuleBase::vector_add_vector_op()(size, buffer, previous.template data(), Real(0.5), current, Real(0.5)); - return buffer; -} - template void save_potential(const ModuleBase::matrix& shape, const Real* current, ct::Tensor* previous) { @@ -54,21 +40,14 @@ template void TDHistoryPW::prepare(const elecstate::Potential& potential) { ModuleBase::timer::start("TDHistoryPW", "prepare"); - prepare_pair(potential.get_veff_smooth(), &veff_prev_, &veff_mid_); + prepare_potential(potential.get_veff_smooth(), &veff_prev_); ModuleBase::timer::end("TDHistoryPW", "prepare"); } template -const typename GetTypeReal::type* TDHistoryPW::prop_pot(elecstate::Potential& potential, const bool corrector) +const typename GetTypeReal::type* TDHistoryPW::left_potential() const { - ModuleBase::timer::start("TDHistoryPW", "prop_pot"); - const Real* view = potential.template get_veff_smooth_data(); - if (corrector) - { - view = average_potential(potential.get_veff_smooth(), view, veff_prev_, &veff_mid_); - } - ModuleBase::timer::end("TDHistoryPW", "prop_pot"); - return view; + return veff_prev_.template data(); } template @@ -80,6 +59,7 @@ void TDHistoryPW::save(const psi::Psi& current, elecstate: previous_.reset(new psi::Psi(current)); } *previous_ = current; + ++generation_; save_potential(potential.get_veff_smooth(), potential.template get_veff_smooth_data(), &veff_prev_); ModuleBase::timer::end("TDHistoryPW", "save"); } diff --git a/source/source_pw/module_rt/td_history_pw.h b/source/source_pw/module_rt/td_history_pw.h index c521c68701f..04b607fc64d 100644 --- a/source/source_pw/module_rt/td_history_pw.h +++ b/source/source_pw/module_rt/td_history_pw.h @@ -10,7 +10,7 @@ namespace pw { -/** @brief Accepted time-step history and predictor-corrector potential storage. */ +/** @brief Accepted endpoint wavefunctions and potential snapshots. */ template class TDHistoryPW { @@ -18,8 +18,12 @@ class TDHistoryPW /** @brief Prepare storage after the potential grids have been updated. */ void prepare(const elecstate::Potential& potential); - /** @brief Use the current potential for the predictor, then average with the saved potential. */ - const typename GetTypeReal::type* prop_pot(elecstate::Potential& potential, const bool corrector); + /** @brief Borrow the immutable accepted left-endpoint potential. */ + const typename GetTypeReal::type* left_potential() const; + unsigned long long generation() const + { + return generation_; + } /** @brief Save the accepted endpoint; the caller owns the convergence decision. */ void save(const psi::Psi& current, elecstate::Potential& potential); @@ -31,7 +35,7 @@ class TDHistoryPW using Real = typename GetTypeReal::type; std::unique_ptr> previous_; ct::Tensor veff_prev_; - ct::Tensor veff_mid_; + unsigned long long generation_ = 0; }; } // namespace pw diff --git a/source/source_pw/module_rt/td_prop_pw.cpp b/source/source_pw/module_rt/td_prop_pw.cpp new file mode 100644 index 00000000000..662c65af7f3 --- /dev/null +++ b/source/source_pw/module_rt/td_prop_pw.cpp @@ -0,0 +1,109 @@ +#include "source_pw/module_rt/td_prop_pw.h" + +#include "source_pw/module_rt/prop_cn_pw.h" +#include "source_pw/module_rt/prop_etrs_pw.h" + +namespace pw +{ +TDPropOpts td_prop_options(const Input_para& input, int npol, const std::vector& global_k) +{ + TDPropOpts prop_opts; + prop_opts.orb.npol = npol; + prop_opts.orb.global_k_indices = global_k; + prop_opts.orb.orthonormal = hsolver::parse_orth_method(input.td_orthonormal); + prop_opts.orb.out_stat = input.td_out_stat; + if (input.propagator == 0) + { + prop_opts.prop_kind = PropKind::cn; + prop_opts.cn.npol = npol; + prop_opts.cn.global_k_indices = global_k; + prop_opts.cn.linear.method = hsolver::parse_linear_method(input.lin_solver); + prop_opts.cn.linear.tolerance = input.lin_thr; + prop_opts.cn.linear.max_iterations = input.lin_maxiter; + prop_opts.cn.linear.restart = input.lin_gmres_restart; + prop_opts.cn.linear.reconstruct = input.lin_reconstruct; + prop_opts.cn.preconditioner = hsolver::parse_pw_precond(input.lin_precond); + prop_opts.cn.cn_init = input.td_cn_init; + prop_opts.cn.kinetic_enabled = input.t_in_h; + } + else + { + prop_opts.prop_kind = PropKind::etrs; + if (input.td_exp_method == "taylor") + { + prop_opts.exp.method = hsolver::ExpMethod::taylor; + } + prop_opts.exp.taylor_order = input.td_exp_order; + prop_opts.exp.rel_tol = input.td_exp_thr; + prop_opts.exp.krylov_max = input.td_exp_krylov_dim; + } + return prop_opts; +} + +template +TDPropPW::TDPropPW(const ModulePW::PW_Basis_K& basis, + const TDPropOpts& prop_opts, + const hsolver::diag_comm_info& comm, + std::ostream& log) + : basis_(basis), orb_ops_(prop_opts.orb, comm, log) +{ + if (prop_opts.prop_kind == PropKind::cn) + { + prop_.reset(new CNPropPW(basis, prop_opts.cn, comm, log)); + } + else + { + prop_.reset(new ETRSPropPW(prop_opts, comm, log)); + } +} +template +PropStage TDPropPW::stage(int scf_iter) const +{ + return scf_iter == 1 ? PropStage::predictor : PropStage::corrector; +} +template +void TDPropPW::begin_trial() +{ + prop_->begin_trial(); +} +template +void TDPropPW::finish_trial() +{ + prop_->discard_trial(); +} +template +void TDPropPW::discard_trial() +{ + prop_->discard_trial(); +} +template +void TDPropPW::invalidate_basis() +{ + prop_->invalidate_basis(); +} +template +void TDPropPW::evolve(hamilt::HamiltPW& hamilt, + elecstate::Potential& potential, + const TDHistoryPW& history, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) +{ + using Real = typename T::value_type; + TDHamiltPW td_hamilt(hamilt, basis_); + PropArgs args{td_hamilt, history, potential, field, elec_step, scf_iter, log_detail}; + prop_->evolve(args, current); + orb_ops_.correct(current, elec_step, scf_iter, log_detail); + const Real* endpoint = potential.template get_veff_smooth_data(); + const auto& vecpot_ha = field.A_right_ha(); + td_hamilt.bind(endpoint, vecpot_ha); +} +template class TDPropPW, base_device::DEVICE_CPU>; +template class TDPropPW, base_device::DEVICE_CPU>; +#if defined(__CUDA) || defined(__ROCM) +template class TDPropPW, base_device::DEVICE_GPU>; +template class TDPropPW, base_device::DEVICE_GPU>; +#endif +} // namespace pw diff --git a/source/source_pw/module_rt/td_prop_pw.h b/source/source_pw/module_rt/td_prop_pw.h new file mode 100644 index 00000000000..16a91af3e3e --- /dev/null +++ b/source/source_pw/module_rt/td_prop_pw.h @@ -0,0 +1,90 @@ +#ifndef PW_TD_PROP_PW_H +#define PW_TD_PROP_PW_H +#include "source_estate/module_pot/td_field_manager.h" +#include "source_hsolver/module_rt_pw/cn_solver_pw.h" +#include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_io/module_parameter/input_parameter.h" +#include "source_pw/module_rt/td_hamilt_pw.h" +#include "source_pw/module_rt/td_history_pw.h" + +namespace pw +{ +enum class PropStage +{ + predictor, + corrector +}; +enum class PropKind +{ + cn, + etrs +}; + +/** @brief Resolved input controls, passed explicitly to numerical services. */ +struct TDPropOpts +{ + PropKind prop_kind = PropKind::cn; + hsolver::TDOrbOpts orb; + hsolver::PWLinearOptions cn; + hsolver::ExpOpts exp; +}; +TDPropOpts td_prop_options(const Input_para& input, int npol, const std::vector& global_k); + +/** @brief Borrowed state for a single predictor or endpoint iteration. */ +template +struct PropArgs +{ + TDHamiltPW& hamilt; + const TDHistoryPW& history; + elecstate::Potential& potential; + const elecstate::TDFieldManager& field; + int elec_step; + int scf_iter; + bool log_detail; +}; + +/** @brief Method-specific wavefunction map; density and acceptance stay above it. */ +template +class PWProp +{ + public: + virtual ~PWProp() = default; + virtual void begin_trial() = 0; + virtual void discard_trial() = 0; + virtual void invalidate_basis() = 0; + virtual void evolve(PropArgs& args, psi::Psi* current) = 0; +}; + +/** @brief Sole PW propagation entry point, independent of the selected method. */ +template +class TDPropPW +{ + private: + const ModulePW::PW_Basis_K& basis_; + hsolver::TDOrbOps orb_ops_; + std::unique_ptr> prop_; + + public: + TDPropPW(const ModulePW::PW_Basis_K& basis, const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log); + PropStage stage(int scf_iter) const; + void begin_trial(); + void finish_trial(); + void discard_trial(); + void invalidate_basis(); + hsolver::TDOrbOps& orbitals() + { + return orb_ops_; + } + /** @brief Restore the endpoint sample before returning to density construction. */ + void evolve(hamilt::HamiltPW& hamilt, + elecstate::Potential& potential, + const TDHistoryPW& history, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail); +}; +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/td_pw.cpp b/source/source_pw/module_rt/td_pw.cpp index b0e7cce2acc..796c9e04d49 100644 --- a/source/source_pw/module_rt/td_pw.cpp +++ b/source/source_pw/module_rt/td_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_pwdft/td_pw.h" +#include "source_pw/module_rt/td_pw.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" diff --git a/source/source_pw/module_rt/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp index 697d3f3d7ec..3821de4a333 100644 --- a/source/source_pw/module_rt/td_spin_pw.cpp +++ b/source/source_pw/module_rt/td_spin_pw.cpp @@ -1,9 +1,9 @@ -#include "source_pw/module_pwdft/td_spin_pw.h" +#include "source_pw/module_rt/td_spin_pw.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" #include "source_pw/module_proj/onsite_proj.h" -#include "source_pw/module_pwdft/td_pw.h" +#include "source_pw/module_rt/td_pw.h" #include #include diff --git a/source/source_pw/module_rt/td_spin_pw.h b/source/source_pw/module_rt/td_spin_pw.h index 3c6757756a1..3774265fba5 100644 --- a/source/source_pw/module_rt/td_spin_pw.h +++ b/source/source_pw/module_rt/td_spin_pw.h @@ -5,7 +5,7 @@ #include "source_estate/elecstate.h" #include "source_pw/module_proj/onsite_proj.h" #include "source_pw/module_pwdft/nonlocal_workspace.h" -#include "source_pw/module_pwdft/td_obs_pw.h" +#include "source_pw/module_rt/td_obs_pw.h" #include #include diff --git a/source/source_pw/module_rt/td_vel_pw.cpp b/source/source_pw/module_rt/td_vel_pw.cpp index 1dc363c0ea0..05dcd1bdd61 100644 --- a/source/source_pw/module_rt/td_vel_pw.cpp +++ b/source/source_pw/module_rt/td_vel_pw.cpp @@ -1,8 +1,8 @@ -#include "source_pw/module_pwdft/td_vel_pw.h" +#include "source_pw/module_rt/td_vel_pw.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" -#include "source_hsolver/kernels/linear_op.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include #include @@ -11,10 +11,7 @@ namespace pw { template -void TDVelPW::initialize(const UnitCell& cell, - const ModulePW::PW_Basis_K& basis, - const K_Vectors& kv, - pseudopot_cell_vnl& pp) +void TDVelPW::initialize(const UnitCell& cell, const ModulePW::PW_Basis_K& basis, const K_Vectors& kv, pseudopot_cell_vnl& pp) { if (cell_ != nullptr) { diff --git a/source/source_pw/module_rt/td_vel_pw.h b/source/source_pw/module_rt/td_vel_pw.h index 1d8a2fdc6db..927f45b7123 100644 --- a/source/source_pw/module_rt/td_vel_pw.h +++ b/source/source_pw/module_rt/td_vel_pw.h @@ -3,7 +3,7 @@ #include "source_estate/elecstate.h" #include "source_pw/module_pwdft/op_pw_vel.h" -#include "source_pw/module_pwdft/td_obs_pw.h" +#include "source_pw/module_rt/td_obs_pw.h" #include #include diff --git a/source/source_pw/module_rt/test/CMakeLists.txt b/source/source_pw/module_rt/test/CMakeLists.txt new file mode 100644 index 00000000000..a7b241b2973 --- /dev/null +++ b/source/source_pw/module_rt/test/CMakeLists.txt @@ -0,0 +1,45 @@ +abacus_disable_feature_definitions(__MLALGO) +abacus_disable_feature_definitions(__CUDA) +abacus_disable_feature_definitions(__ROCM) +abacus_disable_feature_definitions(__EXX) + +AddTest( + TARGET MODULE_PW_td_observables + LIBS parameter base device container psi planewave nao_smoothgen symmetry + SOURCES + test_td_obs_pw.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp + ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp + ../../module_pwdft/op_pw_vel.cpp ../../module_pwdft/projector_gradient.cpp ../../module_pwdft/velocity_workspace.cpp ../../module_pwdft/nonlocal_workspace.cpp + ../../module_pwdft/vnl_pw.cpp ../../module_pwdft/vnl_pw_init_vnl.cpp ../../module_pwdft/vnl_pw_getvnl.cpp ../../module_pwdft/vnl_pw_deeq.cpp + ../../module_pwdft/vnl_pw_grad.cpp ../../module_pwdft/vnl_pw_table.cpp ../../module_pwdft/vnl_pw_qrad.cpp + ../../module_pwdft/stru_fac.cpp ../../module_pwdft/stru_fac_k.cpp ../../module_pwdft/soc.cpp ../../module_pwdft/nonlocal_maths.cpp + ../../module_proj/onsite_proj.cpp ../../module_proj/onsite_proj_init.cpp + ../../module_proj/onsite_proj_overlap.cpp ../../module_proj/onsite_proj_tools.cpp + ../../module_proj/onsite_proj_tools_becp.cpp ../../module_proj/radial_proj.cpp + ../../module_proj/onsite_proj_print.cpp + ../../../source_basis/module_ao/orb_gaunt_table.cpp + ../../../source_cell/klist.cpp ../../../source_cell/reciprocal_grid.cpp + ../../../source_cell/klist_io.cpp + ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp + ../../../source_cell/atom_spec.cpp ../../../source_cell/atom_pseudo.cpp ../../../source_cell/pseudo.cpp + ../../../source_cell/magnetism.cpp ../../../source_cell/sep.cpp ../../../source_cell/sep_cell.cpp + ../../../source_cell/read_pp.cpp ../../../source_cell/read_pp_complete.cpp + ../../../source_cell/read_pp_upf100.cpp ../../../source_cell/read_pp_upf201.cpp + ../../../source_cell/read_pp_vwr.cpp ../../../source_cell/read_pp_blps.cpp + ../../../source_estate/elecstate.cpp ../../../source_estate/module_charge/charge.cpp + ../../../source_estate/module_pot/potential_new.cpp + ../../../source_hamilt/module_xc/xc_functional.cpp +) +target_compile_definitions(MODULE_PW_td_observables PRIVATE TD_TEST_PP_DIR="${PROJECT_SOURCE_DIR}/tests/PP_ORB") +# Keep real PW initialization and observable implementations without linking the SCF driver. +if(MSVC) + target_compile_options(MODULE_PW_td_observables PRIVATE /Gy /Gw) + target_link_options(MODULE_PW_td_observables PRIVATE /OPT:REF) +else() + target_compile_options(MODULE_PW_td_observables PRIVATE -ffunction-sections -fdata-sections) + if(APPLE) + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,-dead_strip) + else() + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,--gc-sections) + endif() +endif() diff --git a/source/source_pw/module_rt/test/td_test_utils.h b/source/source_pw/module_rt/test/td_test_utils.h index 3b232ccf3d9..840db4602af 100644 --- a/source/source_pw/module_rt/test/td_test_utils.h +++ b/source/source_pw/module_rt/test/td_test_utils.h @@ -1,8 +1,8 @@ #ifndef TD_TEST_UTILS_H #define TD_TEST_UTILS_H -#include "source_pw/module_pwdft/td_spin_pw.h" -#include "source_pw/module_pwdft/td_vel_pw.h" +#include "source_pw/module_rt/td_spin_pw.h" +#include "source_pw/module_rt/td_vel_pw.h" #include diff --git a/source/source_pw/module_rt/test/test_td_obs_pw.cpp b/source/source_pw/module_rt/test/test_td_obs_pw.cpp index 86aa7c77101..06555a1f390 100644 --- a/source/source_pw/module_rt/test/test_td_obs_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_obs_pw.cpp @@ -1,6 +1,6 @@ #include "source_base/parallel_global.h" #include "source_cell/read_pp.h" -#include "source_pw/module_pwdft/test/td_test_utils.h" +#include "source_pw/module_rt/test/td_test_utils.h" #include diff --git a/source/source_pw/module_rt/test/test_td_spin_pw.cpp b/source/source_pw/module_rt/test/test_td_spin_pw.cpp index f25c5762c78..b72c02d6517 100644 --- a/source/source_pw/module_rt/test/test_td_spin_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_spin_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_pwdft/test/td_test_utils.h" +#include "source_pw/module_rt/test/td_test_utils.h" #include #include diff --git a/source/source_pw/module_rt/test/test_td_vel_pw.cpp b/source/source_pw/module_rt/test/test_td_vel_pw.cpp index 2a79fb78db3..fe7d1d07023 100644 --- a/source/source_pw/module_rt/test/test_td_vel_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_vel_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_pwdft/test/td_test_utils.h" +#include "source_pw/module_rt/test/td_test_utils.h" #include diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/INPUT b/tests/05_rtTDDFT/31_PW_retry_TDDFT/INPUT new file mode 100644 index 00000000000..8fae3711ed0 --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/INPUT @@ -0,0 +1,54 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +ecutwfc 20 +ecutrho 80 +nbands 6 +nspin 1 +kpar 2 +symmetry 0 +init_wfc atomic+random +pw_seed 1 +pw_diag_thr 1e-14 +scf_thr 1e-14 +scf_nmax 100 +mixing_type broyden +mixing_beta 0.5 +mixing_gg0 0 +smearing_method gauss +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.01 +md_nstep 1 +estep_per_md 2 +cal_force 0 +cal_stress 0 +td_stype 1 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 0 +td_gauss_sigma 0.04 +td_gauss_t0 4 +td_gauss_amp 0.1 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-14 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +out_current 1 +out_level ie +td_scf_nmax 2 +td_max_halvings 1 +out_vecpot 1 diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/KPT b/tests/05_rtTDDFT/31_PW_retry_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/README b/tests/05_rtTDDFT/31_PW_retry_TDDFT/README new file mode 100644 index 00000000000..39590d9ef9b --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/README @@ -0,0 +1 @@ +CN rollback and two accepted half steps after a rejected full step. diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/STRU b/tests/05_rtTDDFT/31_PW_retry_TDDFT/STRU new file mode 100644 index 00000000000..e059e5d6062 --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/STRU @@ -0,0 +1,18 @@ +ATOMIC_SPECIES +Si 28 Si.pz-n-nc.UPF upf201 + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/31_PW_retry_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..476c6886e4e --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/current_tot.txt.ref @@ -0,0 +1,3 @@ + 1 -6.663532778066e-11 4.887391160452e-11 -5.307373793645e-11 + 2 1.378429778724e-05 1.617326170941e-09 -9.117934784250e-10 + 3 3.035454377592e-05 6.377589400826e-09 -6.017071519211e-10 diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/result.ref b/tests/05_rtTDDFT/31_PW_retry_TDDFT/result.ref new file mode 100644 index 00000000000..d2ae790ec47 --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/result.ref @@ -0,0 +1,5 @@ +etotref -251.7683038351815 +etotperatomref -125.8841519176 +CompareCurrent_pass 0 +CompareVectorPot_pass 0 +totaltimeref 0.60 diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/vector_pot.txt.ref b/tests/05_rtTDDFT/31_PW_retry_TDDFT/vector_pot.txt.ref new file mode 100644 index 00000000000..91c8736625c --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/vector_pot.txt.ref @@ -0,0 +1,4 @@ +#istep A_x A_y A_z # endpoint values, Hartree atomic units + 1 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 + 2 -4.959777768934e-04 0.000000000000e+00 0.000000000000e+00 + 3 -1.123854083691e-03 0.000000000000e+00 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT new file mode 100644 index 00000000000..aeb667f2c08 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT @@ -0,0 +1,63 @@ +INPUT_PARAMETERS +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +dft_functional PBE +gga_grad 1 +ecutwfc 20 +ecutrho 80 +nbands 12 +kpar 2 +nspin 4 +noncolin 1 +lspinorb 1 +symmetry -1 +init_wfc atomic+random +pw_seed 1 +ks_solver cg +pw_diag_thr 1e-13 +scf_thr 1e-12 +scf_nmax 150 +mixing_type broyden +mixing_beta 0.5 +mixing_beta_mag 0.5 +mixing_gg0 0 +mixing_gg0_mag 0 +smearing_method fd +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.002 +md_nstep 1 +estep_per_md 2 +cal_force 0 +cal_stress 0 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 1.5707963267948966 +td_gauss_sigma 0.04 +td_gauss_t0 0 +td_gauss_amp 0.1 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-13 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +td_out_torque 1 +td_out_spincur 1 +out_current 1 +out_level ie +td_stype 0 +td_out_mag voronoi +td_propagator 2 +td_exp_method lanczos diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/KPT b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/README b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/README new file mode 100644 index 00000000000..be8b6ce0a7d --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/README @@ -0,0 +1 @@ +PW ETRS with Lanczos exponentials, SOC spinors, and length-gauge observables. diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/STRU b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/STRU new file mode 100644 index 00000000000..139b8c551ed --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/STRU @@ -0,0 +1,21 @@ +ATOMIC_SPECIES +Si 28 Si_dojo_soc.upf upf201 + +NUMERICAL_ORBITAL +Si_dojo_6au.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..657fc28c1e9 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/current_tot.txt.ref @@ -0,0 +1,3 @@ + 1 8.346916421568e-13 -3.644420840810e-13 5.472776569248e-13 + 2 1.562307811783e-08 -2.662220923960e-11 3.476005420532e-11 + 3 6.240155549478e-08 -1.044462928688e-10 1.365536172091e-10 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_atom.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_atom.txt.ref new file mode 100644 index 00000000000..705d3eb33ea --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_atom.txt.ref @@ -0,0 +1,8 @@ +# method=voronoi; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +#step time_fs atom Mx My Mz + 1 0.000000 1 1.177025800765e-10 9.970419779322e-11 2.113907267441e-09 + 1 0.000000 2 -1.189354448948e-10 -1.002641160646e-10 -2.114796830022e-09 + 2 0.002000 1 1.085351767468e-10 1.022288489815e-10 2.113262988996e-09 + 2 0.002000 2 -1.197178364281e-10 -1.029410352339e-10 -2.113661836848e-09 + 3 0.004000 1 8.670318211369e-11 1.036770757901e-10 2.113310126670e-09 + 3 0.004000 2 -1.076915164488e-10 -1.045493576988e-10 -2.113203545705e-09 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_tot.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_tot.txt.ref new file mode 100644 index 00000000000..210b6f9a702 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/mag_tot.txt.ref @@ -0,0 +1,5 @@ +# method=voronoi; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign +#step time_fs Mx My Mz M_norm M_abs_integral N_elec + 1 0.000000 -1.232864818329e-12 -5.599182713832e-13 -8.895625810865e-13 1.620119043951e-12 5.503821331260e-09 8.000000000000e+00 + 2 0.002000 -1.118265968130e-11 -7.121862524545e-13 -3.988478519339e-13 1.121241126676e-11 1.166414660609e-08 8.000000000000e+00 + 3 0.004000 -2.098833433509e-11 -8.722819086693e-13 1.065809653229e-13 2.100672305220e-11 3.705155731334e-08 8.000000000000e+00 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/result.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/result.ref new file mode 100644 index 00000000000..7f2fb249f19 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/result.ref @@ -0,0 +1,9 @@ +etotref -218.4046782984434 +etotperatomref -109.2023391492 +Comparemag_tot_txt_pass 0 +Comparemag_atom_txt_pass 0 +Comparetorque_tot_txt_pass 0 +Comparetorque_atom_txt_pass 0 +Comparespincur_tot_txt_pass 0 +CompareCurrent_pass 0 +totaltimeref 1.29 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/spincur_tot.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/spincur_tot.txt.ref new file mode 100644 index 00000000000..5acbeb4c566 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/spincur_tot.txt.ref @@ -0,0 +1,5 @@ +# S=M/2; unit=hbar/(Bohr^2*Hartree_atomic_time); flow_index_first +#step time_fs Jxx Jxy Jxz Jyx Jyy Jyz Jzx Jzy Jzz + 1 0.000000 1.859077495368e-13 8.526814815197e-14 -1.860620491824e-13 -1.306729619305e-13 5.519297114688e-14 -2.764062734902e-14 -4.795053561659e-14 -6.445645299704e-15 4.152336329892e-14 + 2 0.002000 1.915436065379e-13 8.702021137592e-14 -2.048851445560e-13 -1.294838712425e-13 2.860844356380e-14 2.352088876453e-13 -4.984949425441e-14 -2.548311377977e-13 4.328114214190e-14 + 3 0.004000 2.340287009975e-13 9.960395459336e-14 -2.426694255743e-13 -1.137775332460e-13 -1.988087124708e-13 2.461600921603e-12 -5.312188516685e-14 -2.447910192201e-12 1.899872041638e-13 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_atom.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_atom.txt.ref new file mode 100644 index 00000000000..f02138456d7 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_atom.txt.ref @@ -0,0 +1,9 @@ +# SOC_pseudopotential_atom_decomposition; unit=hbar/Hartree_atomic_time +# not_the_derivative_of_atomic_moments +#step time_fs atom tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 1 -3.016287850338e-11 -4.414642547657e-13 1.463844284191e-12 + 1 0.000000 2 -3.004690151897e-11 -4.736198616105e-13 1.495463319381e-12 + 2 0.002000 1 -3.010134936602e-11 -4.417659001389e-13 1.475222396457e-12 + 2 0.002000 2 -2.989205187512e-11 -4.873992546830e-13 1.508822492632e-12 + 3 0.004000 1 -2.959723834443e-11 -4.699140823018e-13 1.562935778614e-12 + 3 0.004000 2 -2.862180975779e-11 -5.629465128838e-13 1.612074426545e-12 diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_tot.txt.ref b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_tot.txt.ref new file mode 100644 index 00000000000..a3077b961a9 --- /dev/null +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/torque_tot.txt.ref @@ -0,0 +1,5 @@ +# torque=dS/dt; S=M/2; unit=hbar/Hartree_atomic_time +#step time_fs tau_xc_x tau_xc_y tau_xc_z tau_soc_x tau_soc_y tau_soc_z + 1 0.000000 -2.057950508879e-38 -1.013851825821e-37 3.832544962797e-39 -6.020978002235e-11 -9.150841163762e-13 2.959307603572e-12 + 2 0.002000 1.902795731857e-36 4.462790076828e-36 -4.882736102721e-36 -5.999340124114e-11 -9.291651548219e-13 2.984044889089e-12 + 3 0.004000 1.264236073095e-34 7.714219943401e-35 3.287948203490e-35 -5.821904810222e-11 -1.032860595186e-12 3.175010205159e-12 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT new file mode 100644 index 00000000000..486d149481f --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT @@ -0,0 +1,55 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +ecutwfc 20 +ecutrho 80 +nbands 6 +nspin 1 +kpar 2 +symmetry 0 +init_wfc atomic+random +pw_seed 1 +pw_diag_thr 1e-14 +scf_thr 1e-12 +scf_nmax 100 +mixing_type broyden +mixing_beta 0.5 +mixing_gg0 0 +smearing_method gauss +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.01 +md_nstep 1 +estep_per_md 2 +cal_force 0 +cal_stress 0 +td_stype 1 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 0 +td_gauss_sigma 0.04 +td_gauss_t0 4 +td_gauss_amp 0.001 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-14 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +out_current 1 +out_level ie +td_propagator 2 +td_exp_method taylor +td_exp_order 4 +out_vecpot 1 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/KPT b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/README b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/README new file mode 100644 index 00000000000..e4647c621da --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/README @@ -0,0 +1 @@ +PW ETRS with fourth-order Taylor exponentials and velocity-gauge current. diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/STRU b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/STRU new file mode 100644 index 00000000000..e059e5d6062 --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/STRU @@ -0,0 +1,18 @@ +ATOMIC_SPECIES +Si 28 Si.pz-n-nc.UPF upf201 + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..0d8bc108dd7 --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/current_tot.txt.ref @@ -0,0 +1,3 @@ + 1 -7.675792081022e-13 5.456748969816e-14 -8.794013212544e-13 + 2 1.383341747462e-07 1.354579554469e-11 -1.075264988975e-11 + 3 3.036909428735e-07 6.138129509335e-11 -7.848342998581e-12 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/result.ref b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/result.ref new file mode 100644 index 00000000000..75dc8f29420 --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/result.ref @@ -0,0 +1,5 @@ +etotref -251.7684289155609 +etotperatomref -125.8842144578 +CompareCurrent_pass 0 +CompareVectorPot_pass 0 +totaltimeref 0.41 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/vector_pot.txt.ref b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/vector_pot.txt.ref new file mode 100644 index 00000000000..1d89655bf1a --- /dev/null +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/vector_pot.txt.ref @@ -0,0 +1,4 @@ +#istep A_x A_y A_z # endpoint values, Hartree atomic units + 1 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 + 2 -4.959777768934e-06 0.000000000000e+00 0.000000000000e+00 + 3 -1.123854083691e-05 0.000000000000e+00 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT new file mode 100644 index 00000000000..d8f2a0ff486 --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT @@ -0,0 +1,56 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +esolver_type tddft +basis_type pw +calculation md +device cpu +precision double +ecutwfc 20 +ecutrho 80 +nbands 6 +nspin 1 +kpar 2 +symmetry 0 +init_wfc atomic+random +pw_seed 1 +pw_diag_thr 1e-14 +scf_thr 1e-14 +scf_nmax 100 +mixing_type broyden +mixing_beta 0.5 +mixing_gg0 0 +smearing_method gauss +smearing_sigma 0.002 +md_type nve +init_vel 1 +td_dt 0.01 +md_nstep 1 +estep_per_md 2 +cal_force 0 +cal_stress 0 +td_stype 1 +td_vext 1 +td_vext_dire 1 +td_ttype 0 +td_tstart 0 +td_tend 100 +td_gauss_freq 2 +td_gauss_phase 0 +td_gauss_sigma 0.04 +td_gauss_t0 4 +td_gauss_amp 0.1 +lin_solver bicgstab +lin_precond kinetic +lin_thr 1e-14 +lin_maxiter 100 +td_cn_init 0 +lin_reconstruct 0 +out_current 1 +out_level ie +td_propagator 2 +td_exp_method lanczos +td_scf_nmax 2 +td_max_halvings 1 +out_vecpot 1 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/KPT b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/KPT new file mode 100644 index 00000000000..23f1466d0f8 --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/KPT @@ -0,0 +1,5 @@ +K_POINTS +2 +Direct +0.17 0.11 0.07 0.5 +-0.17 -0.11 -0.07 0.5 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/README b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/README new file mode 100644 index 00000000000..a93ab130d21 --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/README @@ -0,0 +1 @@ +ETRS rollback and rebuilding the left half-step after a rejected full step. diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/STRU b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/STRU new file mode 100644 index 00000000000..e059e5d6062 --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/STRU @@ -0,0 +1,18 @@ +ATOMIC_SPECIES +Si 28 Si.pz-n-nc.UPF upf201 + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Direct +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 v 0 0 0 +0.25 0.25 0.25 0 0 0 v 0 0 0 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/current_tot.txt.ref new file mode 100644 index 00000000000..15f12d0501b --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/current_tot.txt.ref @@ -0,0 +1,3 @@ + 1 -6.663532778066e-11 4.887391160452e-11 -5.307373793645e-11 + 2 1.380219064060e-05 1.534993735131e-09 -9.581662699563e-10 + 3 3.035552361407e-05 6.279641504215e-09 -6.909372404875e-10 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/result.ref b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/result.ref new file mode 100644 index 00000000000..6ff7228b620 --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/result.ref @@ -0,0 +1,5 @@ +etotref -251.7683034865009 +etotperatomref -125.8841517433 +CompareCurrent_pass 0 +CompareVectorPot_pass 0 +totaltimeref 0.50 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/vector_pot.txt.ref b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/vector_pot.txt.ref new file mode 100644 index 00000000000..91c8736625c --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/vector_pot.txt.ref @@ -0,0 +1,4 @@ +#istep A_x A_y A_z # endpoint values, Hartree atomic units + 1 0.000000000000e+00 0.000000000000e+00 0.000000000000e+00 + 2 -4.959777768934e-04 0.000000000000e+00 0.000000000000e+00 + 3 -1.123854083691e-03 0.000000000000e+00 0.000000000000e+00 diff --git a/tests/05_rtTDDFT/CASES_CPU.txt b/tests/05_rtTDDFT/CASES_CPU.txt index 6ba3fd96a43..e3615075024 100644 --- a/tests/05_rtTDDFT/CASES_CPU.txt +++ b/tests/05_rtTDDFT/CASES_CPU.txt @@ -28,3 +28,7 @@ 28_PW_gmres_subspace_TDDFT 29_PW_soc_len_TDDFT 30_PW_soc_onsite_TDDFT +31_PW_retry_TDDFT +32_PW_ETRS_lanczos_TDDFT +33_PW_ETRS_taylor_TDDFT +34_PW_ETRS_retry_TDDFT diff --git a/tests/05_rtTDDFT/CASES_GPU.txt b/tests/05_rtTDDFT/CASES_GPU.txt index 5a135595e86..e8808ed7ba0 100644 --- a/tests/05_rtTDDFT/CASES_GPU.txt +++ b/tests/05_rtTDDFT/CASES_GPU.txt @@ -28,3 +28,7 @@ 28_PW_gmres_subspace_TDDFT 29_PW_soc_len_TDDFT 30_PW_soc_onsite_TDDFT +31_PW_retry_TDDFT +32_PW_ETRS_lanczos_TDDFT +33_PW_ETRS_taylor_TDDFT +34_PW_ETRS_retry_TDDFT From 42b6b360a8d93fd40100265b786fc0c76bebeafe Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Sat, 24 Oct 2026 01:08:56 +0800 Subject: [PATCH 09/33] Refactor RT-TDDFT parameter checks to use the InputDomain API --- .../module_parameter/read_inp_tddft.cpp | 120 +++--------------- 1 file changed, 18 insertions(+), 102 deletions(-) diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index cd54cefdc4f..42b49aac047 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -35,10 +35,6 @@ void check_td_efield_parameters(const Input_para& input) ModuleBase::WARNING_QUIT("ReadInput", "td_ttype and td_vext_dire must contain the same number of fields."); } - if (input.td_stype < 0 || input.td_stype > 2) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_stype must be 0, 1, or 2."); - } for (const int direction: input.td_vext_dire) { if (direction < 1 || direction > 3) @@ -154,6 +150,7 @@ void ReadInput::item_rt_tddft() // Please preserve this ordering when adding new parameters. { Input_Item item("td_orthonormal"); + item.domain.set_choices({"cholesky", "lowdin", "newton_schulz", "none"}); item.annotation = "orthonormalization method for PW real-time propagation"; item.category = "Real-Time TDDFT (PW)"; item.type = "String"; @@ -168,13 +165,6 @@ void ReadInput::item_rt_tddft() item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); read_sync_string(input.td_orthonormal); - item.check_value = [](const Input_Item& item, const Parameter& para) { - const std::string& method = para.inp.td_orthonormal; - if (method != "none" && method != "cholesky" && method != "lowdin" && method != "newton_schulz") - { - ModuleBase::WARNING_QUIT("ReadInput", "td_orthonormal must be cholesky, lowdin, newton_schulz or none."); - } - }; this->add_item(item); } { @@ -193,6 +183,8 @@ void ReadInput::item_rt_tddft() } { Input_Item item("td_max_halvings"); + const std::string depth_rule = "value>=0 and value<" + std::to_string(std::numeric_limits::digits); + item.domain.set_rule(depth_rule); item.annotation = "maximum depth of PW real-time half-step retries"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; @@ -201,16 +193,11 @@ void ReadInput::item_rt_tddft() item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft"); read_sync_int(input.td_max_halvings); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_max_halvings < 0 || para.inp.td_max_halvings >= std::numeric_limits::digits) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_max_halvings must be nonnegative and representable as an integer subdivision count."); - } - }; this->add_item(item); } { Input_Item item("td_scf_nmax"); + item.domain.set_rule("value>=2"); item.annotation = "SCF iteration limit per real-time propagation attempt"; item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; @@ -219,16 +206,11 @@ void ReadInput::item_rt_tddft() item.unit = ""; item.set_availability("esolver_type==tddft and basis_type in [pw, lcao]"); read_sync_int(input.td_scf_nmax); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_scf_nmax < 2) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_scf_nmax must be an integer of at least 2."); - } - }; this->add_item(item); } { Input_Item item("td_out_mag"); + item.domain.set_choices({"voronoi", "onsite", "none"}); item.annotation = "RT-TDDFT spinor magnetic moment output"; item.category = "Real-Time TDDFT (PW)"; item.type = "String"; @@ -242,13 +224,6 @@ Total moments always come from the magnetization density. Atomic onsite moments item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and nspin==4"); read_sync_string(input.td_out_mag); - item.check_value = [](const Input_Item&, const Parameter& para) { - const std::string& method = para.inp.td_out_mag; - if (method != "voronoi" && method != "onsite" && method != "none") - { - ModuleBase::WARNING_QUIT("ReadInput", "td_out_mag must be voronoi, onsite or none."); - } - }; this->add_item(item); } { @@ -291,6 +266,7 @@ Components are ordered $xx,xy,xz,yx,yy,yz,zx,zy,zz$, with flow direction first a } { Input_Item item("lin_solver"); + item.domain.set_choices({"bicgstab", "cgs", "gmres"}); item.annotation = "linear solver for real-time propagation"; item.category = "Real-Time TDDFT (PW)"; item.type = "String"; @@ -304,16 +280,11 @@ The initial ground-state diagonalization is controlled by `ks_solver`.)"; item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_string(input.lin_solver); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.lin_solver != "bicgstab" && para.inp.lin_solver != "cgs" && para.inp.lin_solver != "gmres") - { - ModuleBase::WARNING_QUIT("ReadInput", "lin_solver must be bicgstab, cgs or gmres."); - } - }; this->add_item(item); } { Input_Item item("lin_precond"); + item.domain.set_choices({"kinetic", "kinetic_recycle", "kinetic_subspace", "none"}); item.annotation = "right preconditioner for PW real-time propagation"; item.category = "Real-Time TDDFT (PW)"; item.type = "String"; @@ -328,17 +299,11 @@ Preconditioning changes the convergence rate, while `lin_thr` still controls the item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_string(input.lin_precond); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.lin_precond != "kinetic" && para.inp.lin_precond != "none" - && para.inp.lin_precond != "kinetic_recycle" && para.inp.lin_precond != "kinetic_subspace") - { - ModuleBase::WARNING_QUIT("ReadInput", "lin_precond must be none, kinetic, kinetic_recycle or kinetic_subspace."); - } - }; this->add_item(item); } { Input_Item item("lin_thr"); + item.domain.set_rule("value>=0"); item.annotation = "residual tolerance of the linear solver"; item.category = "Real-Time TDDFT (PW)"; item.type = "Real"; @@ -351,12 +316,6 @@ All methods check the residual at the end of the linear solve, before orthonorma item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==0"); read_sync_double(input.lin_thr); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (!std::isfinite(para.inp.lin_thr) || para.inp.lin_thr < 0.0) - { - ModuleBase::WARNING_QUIT("ReadInput", "lin_thr must be a nonnegative finite real number."); - } - }; this->add_item(item); } { @@ -415,6 +374,7 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; } { Input_Item item("estep_per_md"); + item.domain.set_rule("value>0"); item.annotation = "steps of force change"; item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; @@ -422,12 +382,6 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; item.default_value = "1"; item.unit = ""; read_sync_int(input.estep_per_md); - item.reset_value = [](const Input_Item&, Parameter& para) { - if (para.input.estep_per_md <= 0) - { - ModuleBase::WARNING_QUIT("ReadInput", "estep_per_md must be a positive integer."); - } - }; this->add_item(item); } // real time TDDFT @@ -446,19 +400,8 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; GlobalV::ofs_running << "td_dt don't exist, set td_dt with md_dt" << std::endl; para.input.td_dt = para.input.mdp.md_dt / para.input.estep_per_md; } - if (para.input.td_dt <= 0.0) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_dt must be greater than zero."); - } }; - item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_dt = doublevalue; - if (para.input.td_dt <= 0.0) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_dt must be greater than zero."); - } - }; - sync_double(input.td_dt); + read_sync_double(input.td_dt); this->add_item(item); } { @@ -491,6 +434,7 @@ Only effective for `lin_solver=gmres`; ignored otherwise.)"; } { Input_Item item("td_propagator"); + item.domain.add_requirement("basis_type==pw and esolver_type==tddft", "td_propagator in [0, 2]"); item.annotation = "method of propagator"; item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; @@ -511,17 +455,11 @@ For a nonorthogonal LCAO basis: item.unit = ""; item.set_availability("(basis_type==pw or basis_type==lcao) and esolver_type==tddft"); read_sync_int(input.propagator); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.basis_type == "pw" && para.inp.esolver_type == "tddft" - && para.inp.propagator != 0 && para.inp.propagator != 2) - { - ModuleBase::WARNING_QUIT("ReadInput", "PW td_propagator must be 0 (CN) or 2 (ETRS)."); - } - }; this->add_item(item); } { Input_Item item("td_exp_method"); + item.domain.set_choices({"lanczos", "taylor"}); item.annotation = "exponential algorithm for PW ETRS"; item.category = "Real-Time TDDFT (PW)"; item.type = "String"; @@ -532,16 +470,11 @@ For a nonorthogonal LCAO basis: item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2"); read_sync_string(input.td_exp_method); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_exp_method != "taylor" && para.inp.td_exp_method != "lanczos") - { - ModuleBase::WARNING_QUIT("ReadInput", "td_exp_method must be taylor or lanczos."); - } - }; this->add_item(item); } { Input_Item item("td_exp_order"); + item.domain.set_rule("value>=2"); item.annotation = "Taylor polynomial order for PW ETRS"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; @@ -550,13 +483,11 @@ For a nonorthogonal LCAO basis: item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==taylor"); read_sync_int(input.td_exp_order); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_exp_order < 2) { ModuleBase::WARNING_QUIT("ReadInput", "td_exp_order must be at least 2."); } - }; this->add_item(item); } { Input_Item item("td_exp_thr"); + item.domain.set_rule("value>=0"); item.annotation = "relative exponential error target for PW Lanczos"; item.category = "Real-Time TDDFT (PW)"; item.type = "Real"; @@ -565,16 +496,11 @@ For a nonorthogonal LCAO basis: item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==lanczos"); read_sync_double(input.td_exp_thr); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (!std::isfinite(para.inp.td_exp_thr) || para.inp.td_exp_thr < 0) - { - ModuleBase::WARNING_QUIT("ReadInput", "td_exp_thr must be a nonnegative finite real number."); - } - }; this->add_item(item); } { Input_Item item("td_exp_krylov_dim"); + item.domain.set_rule("value>=2"); item.annotation = "maximum Lanczos dimension for PW ETRS"; item.category = "Real-Time TDDFT (PW)"; item.type = "Integer"; @@ -583,9 +509,6 @@ For a nonorthogonal LCAO basis: item.unit = ""; item.set_availability("basis_type==pw and esolver_type==tddft and td_propagator==2 and td_exp_method==lanczos"); read_sync_int(input.td_exp_krylov_dim); - item.check_value = [](const Input_Item&, const Parameter& para) { - if (para.inp.td_exp_krylov_dim < 2) { ModuleBase::WARNING_QUIT("ReadInput", "td_exp_krylov_dim must be at least 2."); } - }; this->add_item(item); } { @@ -620,6 +543,7 @@ For a nonorthogonal LCAO basis: } { Input_Item item("td_stype"); + item.domain.add_requirement("init_vecpot==false", "td_stype in [0, 1, 2]"); item.annotation = "type of electric field in space domain"; item.category = "Real-Time TDDFT (Common)"; item.type = "Integer"; @@ -1084,6 +1008,8 @@ In the velocity and hybrid gauges, ABACUS obtains the vector potential actually } { Input_Item item("init_vecpot"); + item.domain.add_requirement("init_vecpot==true", + "esolver_type==tddft and ((basis_type==pw and td_stype==1) or (basis_type==lcao and td_stype in [1, 2]))"); item.annotation = "read endpoint vector potentials from file"; item.category = "Real-Time TDDFT (Common)"; item.type = "Boolean"; @@ -1096,16 +1022,6 @@ The file takes precedence over analytic waveforms, independently of `td_vext`, ` item.unit = ""; item.set_availability("esolver_type==tddft and ((basis_type==pw and td_stype==1) or (basis_type==lcao and td_stype in [1, 2]))"); read_sync_bool(input.init_vecpot); - item.check_value = [](const Input_Item&, const Parameter& para) { - const Input_para& inp = para.inp; - const bool supported = inp.esolver_type == "tddft" - && ((inp.basis_type == "pw" && inp.td_stype == 1) - || (inp.basis_type == "lcao" && (inp.td_stype == 1 || inp.td_stype == 2))); - if (inp.init_vecpot && !supported) - { - ModuleBase::WARNING_QUIT("ReadInput", "init_vecpot requires PW velocity gauge or LCAO velocity/hybrid gauge RT-TDDFT."); - } - }; this->add_item(item); } { From b879c157499e430c4c38b6a34455686d868251f1 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Mon, 26 Oct 2026 23:34:18 +0800 Subject: [PATCH 10/33] Perf: Accelerate PW RT-TDDFT ETRS Lanczos propagation --- .../module_rt_pw/exp_action.cpp | 26 - .../source_hsolver/module_rt_pw/exp_action.h | 4 +- .../module_rt_pw/exp_lanczos.cpp | 578 +++++++++++------- .../source_hsolver/module_rt_pw/exp_lanczos.h | 40 +- .../module_rt_pw/kernels/cuda/linear_op.cu | 120 ++++ .../module_rt_pw/kernels/linear_op.cpp | 56 ++ .../module_rt_pw/kernels/linear_op.h | 12 + .../kernels/rocm/linear_op.hip.cu | 120 ++++ .../module_parameter/read_inp_tddft.cpp | 2 +- source/source_pw/module_rt/prop_etrs_pw.cpp | 3 +- 10 files changed, 708 insertions(+), 253 deletions(-) diff --git a/source/source_hsolver/module_rt_pw/exp_action.cpp b/source/source_hsolver/module_rt_pw/exp_action.cpp index 5e91becd642..5eb7c06d993 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/exp_action.cpp @@ -22,32 +22,6 @@ ExpAction::ExpAction(const ExpOpts& exp_opts, const diag_comm_info& c } } -template -ExpResult ExpAction::apply_pair(const HSOperator& op, - const OrbLayout& layout, - double alpha, - const T* input, - T* half, - T* full) -{ - ExpResult result = apply(op, layout, alpha, input, half); - if (result.state != ExpState::success) - { - return result; - } - const double full_alpha = 2.0 * alpha; - const ExpResult second = apply(op, layout, full_alpha, input, full); - result.state = second.state; - result.has_err_est = result.has_err_est && second.has_err_est; - result.failed_band = second.failed_band; - result.rel_err_est = std::max(result.rel_err_est, second.rel_err_est); - result.order_or_dim = std::max(result.order_or_dim, second.order_or_dim); - result.bisect_count += second.bisect_count; - result.hpsi_calls += second.hpsi_calls; - result.hpsi_cols += second.hpsi_cols; - return result; -} - template std::unique_ptr> make_exp_action(const ExpOpts& exp_opts, const diag_comm_info& comm) { diff --git a/source/source_hsolver/module_rt_pw/exp_action.h b/source/source_hsolver/module_rt_pw/exp_action.h index 384eb09a8d8..dceefe9f115 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.h +++ b/source/source_hsolver/module_rt_pw/exp_action.h @@ -41,6 +41,7 @@ struct ExpResult double rel_err_est = 0.0; int order_or_dim = 0; int bisect_count = 0; + int reorth_count = 0; int failed_band = -1; long long hpsi_calls = 0; long long hpsi_cols = 0; @@ -64,7 +65,8 @@ class ExpAction return exp_opts_.rel_tol; } virtual ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) = 0; - virtual ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full); + virtual ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) + = 0; }; /** @brief Construct a backend without reading global input parameters. */ diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp index 5b9942f0bf0..7952893eac6 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -1,7 +1,9 @@ #include "source_hsolver/module_rt_pw/exp_lanczos.h" #include "source_base/module_container/base/third_party/lapack.h" +#include "source_base/parallel_device.h" #include "source_base/timer.h" +#include "source_hsolver/module_rt_pw/kernels/linear_op.h" #include @@ -16,137 +18,120 @@ void add_work(ExpResult& total, const ExpResult& part) total.hpsi_calls += part.hpsi_calls; total.hpsi_cols += part.hpsi_cols; total.bisect_count += part.bisect_count; + total.reorth_count += part.reorth_count; total.order_or_dim = std::max(total.order_or_dim, part.order_or_dim); } -bool diagonalize(std::vector* vectors, int n, std::vector* values) +ExpResult failed_band(ExpResult result, int band, bool retry) { - const char job = 'V'; - const char upper = 'U'; - const int lwork = std::max(1, n * n + 2 * n); - const int lrwork = std::max(1, 1 + 5 * n + 2 * n * n); - const int liwork = std::max(1, 3 + 5 * n); - std::vector work(lwork); - std::vector rwork(lrwork); - std::vector iwork(liwork); - values->resize(n); - int info = 0; - zheevd_(&job, - &upper, - &n, - vectors->data(), - &n, - values->data(), - work.data(), - &lwork, - rwork.data(), - &lrwork, - iwork.data(), - &liwork, - &info); - return info == 0; + result.failed_band = band; + result.state = retry ? ExpState::retry : ExpState::failed; + result.has_err_est = false; + return result; } +} // namespace -std::vector gauss_rule(int order) +template +ExpLanczos::ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm), comm_(comm) { - std::vector rule(2 * order); - const double pi = std::acos(-1.0); - for (int i = 0; i < order; ++i) - { - double x = std::cos(pi * (i + 0.75) / (order + 0.5)); - double derivative = 0.0; - for (int iteration = 0; iteration < 20; ++iteration) - { - double p = 1.0; - double previous = 0.0; - for (int k = 1; k <= order; ++k) - { - const double next = ((2 * k - 1) * x * p - (k - 1) * previous) / k; - previous = p; - p = next; - } - derivative = order * (x * p - previous) / (x * x - 1); - const double change = p / derivative; - x -= change; - if (std::abs(change) < 1e-15) - { - break; - } - } - rule[i] = x; - rule[order + i] = 2.0 / ((1.0 - x * x) * derivative * derivative); - } - return rule; } -double defect_integral(const std::vector& values, - const std::vector& vectors, - double time, - int panels, - const std::vector& rule, - double sign) +template +int ExpLanczos::batch_size(const OrbLayout& layout) const { - const int n = values.size(); - const int order = rule.size() / 2; - const double width = time / panels; - double integral = 0.0; - for (int panel = 0; panel < panels; ++panel) + // The layout can differ between ranks, but operator column counts must agree. + double rows = std::max(1, layout.ld); +#ifdef __MPI + if (comm_.nproc > 1) { - for (int node = 0; node < order; ++node) - { - const double t = sign * (panel + 0.5 * (rule[node] + 1.0)) * width; - Wide amplitude(0); - for (int k = 0; k < n; ++k) - { - // Removing a scalar phase improves evaluation for shifted spectra. - amplitude += vectors[n - 1 + k * n] * std::conj(vectors[k * n]) * std::exp(Wide(0, -t * (values[k] - values[0]))); - } - integral += rule[order + node] * std::abs(amplitude); - } + std::vector sizes(comm_.nproc, 0.0); + sizes[comm_.rank] = rows; + Parallel_Common::reduce_data(sizes.data(), comm_.nproc, comm_.comm); + rows = *std::max_element(sizes.begin(), sizes.end()); } - return 0.5 * width * integral; +#endif + const double vectors = static_cast(this->exp_opts_.krylov_max) + 6.0; + const double bytes_per_band = sizeof(T) * rows * vectors; + const double budget = 256.0 * 1024.0 * 1024.0; + const int memory_limit = static_cast(std::min(64.0, budget / bytes_per_band)); + return std::max(1, std::min(layout.bands, memory_limit)); } -double estimate_error(const std::vector& values, const std::vector& vectors, double alpha, double beta, double rel_tol) +template +void ExpLanczos::combine(const OrbLayout& layout, int count, const T* basis, const std::vector& coefficients, T* output) { - static const std::vector rule8 = gauss_rule(8); - static const std::vector rule16 = gauss_rule(16); - const double time = std::abs(alpha); - const double sign = alpha < 0 ? -1.0 : 1.0; - const double phase = time * (values.back() - values.front()); - const double requested = std::ceil(phase / (std::acos(-1.0) / 4.0)); - if (!std::isfinite(requested) || requested > 4096) - { - return std::numeric_limits::infinity(); - } - int panels = std::max(1, static_cast(requested)); - for (; panels <= 4096; panels *= 2) + ModuleBase::timer::start("ExpLanczos", "combine"); + linear_buffer(&coefficients_, coefficients.size()); + T* data = coefficients_.template data(); + base_device::memory::synchronize_memory_op()(data, coefficients.data(), coefficients.size()); + const int dim = layout.npol == 2 ? layout.ld : layout.npw; + const int stride = layout.ld * layout.bands; + linear_op().combine(layout.ld, dim, layout.bands, count, stride, basis, data, output); + ModuleBase::timer::end("ExpLanczos", "combine"); +} + +template +std::vector ExpLanczos::basis_gram(const OrbLayout& layout, int count, const T* basis) +{ + ModuleBase::timer::start("ExpLanczos", "basis_gram"); + const int size = layout.bands * count * count; + linear_buffer(&products_, size); + Wide* data = products_.template data(); + const int dim = layout.npol == 2 ? layout.ld : layout.npw; + const int stride = layout.ld * layout.bands; + linear_op().block_gram(layout.ld, dim, layout.bands, count, stride, basis, data); + std::vector gram(size); + base_device::memory::synchronize_memory_op()(gram.data(), data, size); +#ifdef __MPI + if (comm_.nproc > 1) { - const double low = beta * defect_integral(values, vectors, time, panels, rule8, sign); - const double high = beta * defect_integral(values, vectors, time, panels, rule16, sign); - if (std::abs(low - high) <= rel_tol / 8.0) - { - return 2.0 * std::max(low, high); - } + Parallel_Common::reduce_data(gram.data(), size, comm_.comm); } - return std::numeric_limits::infinity(); +#endif + ModuleBase::timer::end("ExpLanczos", "basis_gram"); + return gram; } -} // namespace template -ExpLanczos::ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm) +bool ExpLanczos::diagonalize(const std::vector& diagonal, + const std::vector& off_diagonal, + int band, + int bands, + int count) { + ModuleBase::timer::start("ExpLanczos", "diagonalize"); + small_vectors_.assign(count * count, 0.0); + small_values_.resize(count); + const int work_size = std::max(1, 3 * count); + small_work_.resize(work_size); + for (int i = 0; i < count; ++i) + { + small_vectors_[i * count + i] = diagonal[i * bands + band]; + if (i + 1 < count) + { + small_vectors_[(i + 1) * count + i] = off_diagonal[i * bands + band]; + } + } + // Only the real symmetric tridiagonal projection is diagonalized. + const char job = 'V'; + const char upper = 'U'; + int info = 0; + dsyev_(&job, &upper, &count, small_vectors_.data(), &count, small_values_.data(), small_work_.data(), &work_size, &info); + ModuleBase::timer::end("ExpLanczos", "diagonalize"); + return info == 0; } template ExpResult ExpLanczos::attempt(const HSOperator& op, - const OrbLayout& layout, - double alpha, - double rel_tol, - const T* input, - T* output) + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + T* full, + bool reorthogonalize, + bool* lost_orthogonality) { - ExpResult result; using Real = typename T::value_type; const int ld = layout.ld; const int bands = layout.bands; @@ -154,183 +139,320 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, const int stride = ld * bands; const int limit = this->exp_opts_.krylov_max; const int64_t size = std::max(1, stride); - linear_buffer(&basis_, size * (limit + 1)); + const double time = full ? 2.0 * std::abs(alpha) : std::abs(alpha); + *lost_orthogonality = false; + ExpResult result; + result.has_err_est = true; + result.reorth_count = reorthogonalize ? 1 : 0; + const int64_t basis_size = size * static_cast(limit); + if (basis_.NumElements() < basis_size) + { + // Avoid holding two large Krylov allocations while growing the workspace. + basis_ = ct::Tensor(); + } + linear_buffer(&basis_, basis_size); linear_buffer(&image_, size); linear_buffer(&packed_, size); linear_buffer(&hpacked_, size); + linear_buffer(&permutation_, bands); T* q = basis_.template data(); T* w = image_.template data(); T* packed = packed_.template data(); T* hpacked = hpacked_.template data(); - base_device::memory::set_memory_op()(q, 0, size * (limit + 1)); + int* map = permutation_.template data(); base_device::memory::set_memory_op()(output, 0, stride); + if (full) + { + base_device::memory::set_memory_op()(full, 0, stride); + } base_device::memory::synchronize_memory_op()(q, input, stride); zero_pad(q, layout); const auto norms = this->algebra_.dots(ld, dim, bands, 1, stride, q, q); std::vector scale(bands); std::vector factors(bands); std::vector done(bands, false); - std::vector matrix(static_cast(bands) * limit * limit, Wide(0)); - result.has_err_est = true; + std::vector diagonal(static_cast(bands) * limit); + std::vector beta(static_cast(bands) * limit); + std::vector log_bound(bands, 0.0); + std::vector correction_sq(bands, 0.0); + std::vector errors(bands, 0.0); + std::vector active; + active.reserve(bands); for (int b = 0; b < bands; ++b) { - scale[b] = std::sqrt(std::max(0.0, norms[b].real())); - if (!std::isfinite(norms[b].real())) + if (!std::isfinite(norms[b].real()) || norms[b].real() < 0.0) { - result.state = ExpState::failed; - result.has_err_est = false; - result.failed_band = b; - return result; + return failed_band(result, b, false); } + scale[b] = std::sqrt(norms[b].real()); done[b] = scale[b] == 0.0; factors[b] = done[b] ? T(0) : T(1.0 / scale[b]); } this->work_.batch(ld, dim, bands, q, q, q, T(1), T(0), factors.data(), nullptr, nullptr); for (int j = 0; j < limit; ++j) { - std::vector active; - T* column = q + static_cast(j) * stride; + active.clear(); for (int b = 0; b < bands; ++b) { - if (done[b]) + if (!done[b]) { - continue; + active.push_back(b); } - T* destination = packed + active.size() * ld; - base_device::memory::synchronize_memory_op()(destination, column + b * ld, ld); - active.push_back(b); } if (active.empty()) { - result.failed_band = -1; return result; } const int count = active.size(); - op.hpsi(packed, hpacked, ld, count); - const OrbLayout batch_layout{ld, layout.npw, layout.npol, count}; - zero_pad(hpacked, batch_layout); + T* column = q + static_cast(j) * stride; + if (count == bands) + { + op.hpsi(column, w, ld, bands); + } + else + { + base_device::memory::synchronize_memory_op()(map, active.data(), count); + linear_op().gather(ld, ld, count, 1, stride, column, packed, map); + op.hpsi(packed, hpacked, ld, count); + base_device::memory::set_memory_op()(w, 0, stride); + linear_op().scatter(ld, ld, count, hpacked, w, map); + } + zero_pad(w, layout); ++result.hpsi_calls; result.hpsi_cols += count; - base_device::memory::set_memory_op()(w, 0, stride); - for (int a = 0; a < count; ++a) + const auto projection = this->algebra_.dots(ld, dim, bands, 1, stride, column, w); + const int m = j + 1; + result.order_or_dim = m; + const int first = std::max(0, j - 1); + const int columns = j - first + 1; + std::vector recurrence(columns * bands, T(0)); + for (int b: active) { - base_device::memory::synchronize_memory_op()(w + active[a] * ld, hpacked + a * ld, ld); + if (!std::isfinite(std::abs(projection[b]))) + { + return failed_band(result, b, false); + } + diagonal[j * bands + b] = projection[b].real(); + recurrence[(columns - 1) * bands + b] = T(-projection[b].real()); + if (j > 0) + { + recurrence[b] = T(-beta[(j - 1) * bands + b]); + } } - // Two complete projections retain small off-tridiagonal corrections. - for (int pass = 0; pass < 2; ++pass) + const T* previous = q + static_cast(first) * stride; + combine(layout, columns, previous, recurrence, w); + if (reorthogonalize) { - const auto products = this->algebra_.dots(ld, dim, bands, j + 1, stride, q, w); - for (int i = 0; i <= j; ++i) + std::vector removed(bands, 0.0); + for (int pass = 0; pass < 2; ++pass) { - for (int b = 0; b < bands; ++b) + const auto projections = this->algebra_.dots(ld, dim, bands, m, stride, q, w); + std::vector correction(m * bands, T(0)); + for (int i = 0; i < m; ++i) { - const Wide value = products[i * bands + b]; - matrix[(static_cast(b) * limit + j) * limit + i] += value; - factors[b] = T(-value); + for (int b: active) + { + const Wide value = projections[i * bands + b]; + if (!std::isfinite(std::abs(value))) + { + return failed_band(result, b, false); + } + removed[b] += std::abs(value); + correction[i * bands + b] = T(-value); + } } - const T* qi = q + static_cast(i) * stride; - this->work_.batch(ld, dim, bands, w, w, qi, T(1), T(1), nullptr, factors.data(), nullptr); + combine(layout, m, q, correction, w); + } + for (int b: active) + { + // ||Q*C|| <= sum_i |C_i| for normalized columns. Accumulate + // a Frobenius bound on the discarded recurrence corrections. + correction_sq[b] += removed[b] * removed[b]; } } const auto residuals = this->algebra_.dots(ld, dim, bands, 1, stride, w, w); - const int m = j + 1; - result.order_or_dim = std::max(result.order_or_dim, m); - double max_error = 0.0; + std::vector candidates; for (int b: active) { - const double beta = std::sqrt(std::max(0.0, residuals[b].real())); - if (!std::isfinite(residuals[b].real())) + if (!std::isfinite(residuals[b].real()) || residuals[b].real() < 0.0) { - result.state = ExpState::failed; - result.has_err_est = false; - result.failed_band = b; - return result; + return failed_band(result, b, false); + } + const double residual = std::sqrt(residuals[b].real()); + beta[j * bands + b] = residual; + if (residual == 0.0 || time == 0.0) + { + log_bound[b] = -std::numeric_limits::infinity(); + } + else + { + log_bound[b] += std::log(time) + std::log(residual) - std::log(static_cast(m)); + } + // Jawecki et al., Theorem 1: |t|^m product(beta_1,...,beta_m)/m!. + // Duhamel adds |t|*||F|| for the discarded reorthogonalization terms. + errors[b] = std::exp(log_bound[b]) + time * std::sqrt(correction_sq[b]); + if (errors[b] <= rel_tol) + { + candidates.push_back(b); } - const bool breakdown = beta <= 100.0 * std::numeric_limits::epsilon(); - const bool evaluate = breakdown || m == limit || (m >= 4 && m % 2 == 0); - if (evaluate) + else if (residual <= std::numeric_limits::min()) { - std::vector vectors(m * m); - const Wide* block = matrix.data() + static_cast(b) * limit * limit; - for (int col = 0; col < m; ++col) + return failed_band(result, b, true); + } + } + if (!candidates.empty()) + { + const auto gram = basis_gram(layout, m, q); + // The defect bound follows the three-term relation, not Q-adjoint*Q=I. + // Rebuilding for insignificant loss of orthogonality wastes H applications. + // Check the actual output norm separately at the requested precision. + const double roundoff = std::numeric_limits::epsilon(); + const double orth_tol = std::max(std::sqrt(roundoff), 0.1 * rel_tol); + const double norm_tol = std::max(100.0 * m * std::numeric_limits::epsilon(), 0.1 * rel_tol); + for (int b: candidates) + { + double defect = 0.0; + for (int row = 0; row < m; ++row) { - for (int row = 0; row <= col; ++row) + double row_sum = 0.0; + for (int col = 0; col < m; ++col) { - // The upper triangle contains explicitly computed projections. - Wide value = block[row + col * limit]; - if (row == col) + const Wide identity = row == col ? Wide(1) : Wide(0); + const double value = std::abs(gram[(col * m + row) * bands + b] - identity); + if (!std::isfinite(value)) { - value = Wide(value.real(), 0); + return failed_band(result, b, false); } - vectors[row + col * m] = value; - vectors[col + row * m] = std::conj(value); + row_sum += value; } + defect = std::max(defect, row_sum); + } + if (defect > orth_tol) + { + *lost_orthogonality = true; + return failed_band(result, b, true); } - std::vector values; - if (!diagonalize(&vectors, m, &values)) + } + std::vector coefficients(m * bands, T(0)); + std::vector full_coefficients; + if (full) + { + full_coefficients.assign(m * bands, T(0)); + } + for (int b: candidates) + { + if (!diagonalize(diagonal, beta, b, bands, m)) { - result.state = ExpState::failed; - result.has_err_est = false; - result.failed_band = b; - return result; + return failed_band(result, b, false); } - const double error = estimate_error(values, vectors, alpha, beta, rel_tol); - max_error = std::max(max_error, error); - if (error <= rel_tol) + std::vector half_values(m, Wide(0)); + std::vector full_values(m, Wide(0)); + for (int k = 0; k < m; ++k) { - std::vector coefficients(m, Wide(0)); - for (int k = 0; k < m; ++k) + const double weight = scale[b] * small_vectors_[k * m]; + const Wide half_phase = weight * std::exp(Wide(0, -alpha * small_values_[k])); + Wide full_phase(0); + if (full) { - const Wide phase = scale[b] * std::exp(Wide(0, -alpha * values[k])) * std::conj(vectors[k * m]); - for (int i = 0; i < m; ++i) + full_phase = weight * std::exp(Wide(0, -2.0 * alpha * small_values_[k])); + } + for (int i = 0; i < m; ++i) + { + const double component = small_vectors_[k * m + i]; + half_values[i] += component * half_phase; + if (full) { - coefficients[i] += vectors[i + k * m] * phase; + full_values[i] += component * full_phase; } } - this->algebra_.expand(stride, dim, m, 1, q + b * ld, coefficients, output + b * ld, T(0)); - result.rel_err_est = std::max(result.rel_err_est, error); - done[b] = true; } + for (int i = 0; i < m; ++i) + { + coefficients[i * bands + b] = T(half_values[i]); + if (full) + { + full_coefficients[i * bands + b] = T(full_values[i]); + } + } + } + combine(layout, m, q, coefficients, output); + if (full) + { + combine(layout, m, q, full_coefficients, full); + } + const auto output_norms = this->algebra_.dots(ld, dim, bands, 1, stride, output, output); + std::vector full_norms; + if (full) + { + full_norms = this->algebra_.dots(ld, dim, bands, 1, stride, full, full); + } + for (int b: candidates) + { + const double norm_error = std::abs(std::sqrt(output_norms[b].real()) / scale[b] - 1.0); + double full_error = 0.0; + if (full) + { + full_error = std::abs(std::sqrt(full_norms[b].real()) / scale[b] - 1.0); + } + if (!std::isfinite(norm_error) || !std::isfinite(full_error)) + { + return failed_band(result, b, false); + } + if (norm_error > norm_tol || full_error > norm_tol) + { + *lost_orthogonality = true; + return failed_band(result, b, true); + } + done[b] = true; + result.rel_err_est = std::max(result.rel_err_est, errors[b]); } - factors[b] = done[b] || beta == 0.0 ? T(0) : T(1.0 / beta); } + bool finished = true; for (int b = 0; b < bands; ++b) { - if (done[b]) + factors[b] = T(0); + if (!done[b]) { - factors[b] = T(0); + finished = false; + factors[b] = T(1.0 / beta[j * bands + b]); + result.failed_band = b; } } - T* next = q + static_cast(m) * stride; - this->work_.batch(ld, dim, bands, next, w, w, T(1), T(0), factors.data(), nullptr, nullptr); - if (m == limit) + if (finished) { - result.rel_err_est = std::max(result.rel_err_est, max_error); + result.failed_band = -1; + return result; } - } - for (int b = 0; b < bands; ++b) - { - if (!done[b]) + if (m < limit) { - result.state = ExpState::retry; - result.failed_band = b; - return result; + T* next = q + static_cast(m) * stride; + base_device::memory::set_memory_op()(next, 0, stride); + this->work_.batch(ld, dim, bands, next, w, w, T(1), T(0), factors.data(), nullptr, nullptr); } } - result.failed_band = -1; + result.state = ExpState::retry; + result.rel_err_est = *std::max_element(errors.begin(), errors.end()); return result; } template ExpResult ExpLanczos::interval(const HSOperator& op, - const OrbLayout& layout, - double alpha, - double rel_tol, - const T* input, - T* output, - int depth) + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + T* full, + int depth) { - ExpResult trial = attempt(op, layout, alpha, rel_tol, input, output); + bool lost_orthogonality = false; + ExpResult trial = attempt(op, layout, alpha, rel_tol, input, output, full, false, &lost_orthogonality); + if (lost_orthogonality) + { + const ExpResult previous = trial; + trial = attempt(op, layout, alpha, rel_tol, input, output, full, true, &lost_orthogonality); + add_work(trial, previous); + } if (trial.state != ExpState::retry) { return trial; @@ -343,18 +465,25 @@ ExpResult ExpLanczos::interval(const HSOperator& op, ++trial.bisect_count; ct::Tensor midpoint; const int64_t size = static_cast(layout.ld) * layout.bands; - linear_buffer(&midpoint, size); - T* middle = midpoint.template data(); - const double half_alpha = alpha / 2.0; - const double half_tol = rel_tol / 2.0; + T* middle = output; + T* endpoint = full; + double sub_alpha = alpha; + if (!full) + { + linear_buffer(&midpoint, size); + middle = midpoint.template data(); + endpoint = output; + sub_alpha = alpha / 2.0; + } + const double sub_tol = rel_tol / 2.0; const int next_depth = depth + 1; - ExpResult left = interval(op, layout, half_alpha, half_tol, input, middle, next_depth); + ExpResult left = interval(op, layout, sub_alpha, sub_tol, input, middle, nullptr, next_depth); add_work(left, trial); if (left.state != ExpState::success) { return left; } - ExpResult right = interval(op, layout, half_alpha, half_tol, middle, output, next_depth); + ExpResult right = interval(op, layout, sub_alpha, sub_tol, middle, endpoint, nullptr, next_depth); add_work(right, left); if (right.state == ExpState::success) { @@ -364,20 +493,26 @@ ExpResult ExpLanczos::interval(const HSOperator& op, } template -ExpResult ExpLanczos::apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) +ExpResult ExpLanczos::evaluate(const HSOperator& op, + const OrbLayout& layout, + double alpha, + const T* input, + T* output, + T* full) { - ModuleBase::timer::start("ExpLanczos", "apply"); + ModuleBase::timer::start("ExpLanczos", "evaluate"); ExpResult result; result.has_err_est = true; - // Limit basis storage independently of the number of occupied bands. - for (int first = 0; first < layout.bands; first += 8) + const int batch_limit = batch_size(layout); + for (int first = 0; first < layout.bands; first += batch_limit) { - const int bands = std::min(8, layout.bands - first); + const int bands = std::min(batch_limit, layout.bands - first); const OrbLayout batch{layout.ld, layout.npw, layout.npol, bands}; const int64_t offset = static_cast(first) * layout.ld; const T* src = input + offset; T* dst = output + offset; - const ExpResult local = interval(op, batch, alpha, this->exp_opts_.rel_tol, src, dst, 0); + T* full_dst = full ? full + offset : nullptr; + const ExpResult local = interval(op, batch, alpha, this->exp_opts_.rel_tol, src, dst, full_dst, 0); result.has_err_est = result.has_err_est && local.has_err_est; add_work(result, local); result.rel_err_est = std::max(result.rel_err_est, local.rel_err_est); @@ -388,10 +523,27 @@ ExpResult ExpLanczos::apply(const HSOperator& op, const Or break; } } - ModuleBase::timer::end("ExpLanczos", "apply"); + ModuleBase::timer::end("ExpLanczos", "evaluate"); return result; } +template +ExpResult ExpLanczos::apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) +{ + return evaluate(op, layout, alpha, input, output, nullptr); +} + +template +ExpResult ExpLanczos::apply_pair(const HSOperator& op, + const OrbLayout& layout, + double alpha, + const T* input, + T* half, + T* full) +{ + return evaluate(op, layout, alpha, input, half, full); +} + template class ExpLanczos, base_device::DEVICE_CPU>; template class ExpLanczos, base_device::DEVICE_CPU>; #if defined(__CUDA) || defined(__ROCM) diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.h b/source/source_hsolver/module_rt_pw/exp_lanczos.h index b68aa5d0b1c..a1ba4ac06a9 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.h +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.h @@ -8,27 +8,45 @@ template class ExpLanczos final : public ExpAction { private: + const diag_comm_info comm_; ct::Tensor basis_; ct::Tensor image_; ct::Tensor packed_; ct::Tensor hpacked_; + ct::Tensor coefficients_; + ct::Tensor products_; + ct::Tensor permutation_; + std::vector small_vectors_; + std::vector small_values_; + std::vector small_work_; + int batch_size(const OrbLayout& layout) const; + void combine(const OrbLayout& layout, int count, const T* basis, const std::vector& coefficients, T* output); + std::vector> basis_gram(const OrbLayout& layout, int count, const T* basis); + bool diagonalize(const std::vector& diagonal, const std::vector& off_diagonal, int band, int bands, int count); ExpResult attempt(const HSOperator& op, - const OrbLayout& layout, - double alpha, - double rel_tol, - const T* input, - T* output); + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + T* full, + bool reorthogonalize, + bool* lost_orthogonality); ExpResult interval(const HSOperator& op, - const OrbLayout& layout, - double alpha, - double rel_tol, - const T* input, - T* output, - int depth); + const OrbLayout& layout, + double alpha, + double rel_tol, + const T* input, + T* output, + T* full, + int depth); + ExpResult evaluate(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output, T* full); public: ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm); ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) override; + /** @brief Share one Krylov construction for alpha and twice alpha. */ + ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) override; }; } // namespace hsolver #endif diff --git a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu b/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu index 6950747456a..f5fa9a6d729 100644 --- a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu +++ b/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu @@ -407,6 +407,126 @@ void linear_op::native_dots(int ld, int dim, int nve check_launch(); } +namespace +{ +template +__global__ void combine_kernel(int ld, int dim, int count, int stride, int nvec, + const C* basis, const C* coefficients, C* output) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int b = blockIdx.y; + if (i < dim) + { + const std::int64_t offset = static_cast(b) * ld + i; + C sum(0); + for (int j = 0; j < count; ++j) + { + sum += basis[static_cast(j) * stride + offset] * coefficients[j * nvec + b]; + } + output[offset] += sum; + } +} + +template +__global__ void scatter_kernel(int ld, int dim, const C* input, C* output, const int* map) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int b = blockIdx.y; + if (i < dim) + { + output[static_cast(map[b]) * ld + i] = input[static_cast(b) * ld + i]; + } +} + +template +__global__ void block_gram_kernel(int ld, int dim, int nvec, int count, int stride, + const thrust::complex* basis, thrust::complex* output) +{ + const int b = blockIdx.x % nvec; + const int pair = blockIdx.x / nvec; + const int row = pair % count; + const int col = pair / count; + if (row > col) + { + return; + } + const std::int64_t first = static_cast(row) * stride + static_cast(b) * ld; + const std::int64_t second = static_cast(col) * stride + static_cast(b) * ld; + thrust::complex sum(0); + for (int i = threadIdx.x; i < dim; i += blockDim.x) + { + sum += thrust::conj(thrust::complex(basis[first + i])) * thrust::complex(basis[second + i]); + } + __shared__ double re[linear_threads]; + __shared__ double im[linear_threads]; + const int tid = threadIdx.x; + re[tid] = sum.real(); + im[tid] = sum.imag(); + __syncthreads(); + for (int step = blockDim.x / 2; step > 0; step /= 2) + { + if (tid < step) + { + re[tid] += re[tid + step]; + im[tid] += im[tid + step]; + } + __syncthreads(); + } + if (tid == 0) + { + output[(col * count + row) * nvec + b] = thrust::complex(re[0], im[0]); + if (row != col) + { + output[(row * count + col) * nvec + b] = thrust::complex(re[0], -im[0]); + } + } +} +} // namespace + +template +void linear_op::combine(int ld, int dim, int nvec, int count, int stride, + const T* basis, const T* coefficients, T* output) const +{ + if (dim == 0 || nvec == 0) + { + return; + } + using C = thrust::complex; + const dim3 grid((dim + linear_threads - 1) / linear_threads, nvec); + combine_kernel<<>>(ld, dim, count, stride, nvec, + reinterpret_cast(basis), reinterpret_cast(coefficients), reinterpret_cast(output)); + check_launch(); +} + +template +void linear_op::scatter(int ld, int dim, int nvec, + const T* input, T* output, const int* map) const +{ + if (dim == 0 || nvec == 0) + { + return; + } + using C = thrust::complex; + const dim3 grid((dim + linear_threads - 1) / linear_threads, nvec); + scatter_kernel<<>>(ld, dim, reinterpret_cast(input), reinterpret_cast(output), map); + check_launch(); +} + +template +void linear_op::block_gram(int ld, int dim, int nvec, int count, int stride, + const T* basis, std::complex* output) const +{ + if (nvec == 0 || count == 0) + { + return; + } + using C = thrust::complex; + const int blocks = nvec * count * count; + block_gram_kernel<<>>(ld, dim, nvec, count, stride, reinterpret_cast(basis), + reinterpret_cast*>(output)); + check_launch(); +} + template class linear_op, base_device::DEVICE_GPU>; template class linear_op, base_device::DEVICE_GPU>; diff --git a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp b/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp index 971c508e22c..15a4e1a4120 100644 --- a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp +++ b/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp @@ -1,5 +1,6 @@ #include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include #include #include #ifdef _OPENMP @@ -291,6 +292,61 @@ void linear_op::native_dots(int ld, int dim, int nvec, int count, int } } +template +void linear_op::combine(int ld, int dim, int nvec, int count, int stride, const T* basis, const T* coefficients, T* output) const +{ +#pragma omp parallel for schedule(static) if (parallel_work(dim, nvec)) + for (int b = 0; b < nvec; ++b) + { + for (int i = 0; i < dim; ++i) + { + const std::int64_t offset = static_cast(b) * ld + i; + T sum(0); + for (int j = 0; j < count; ++j) + { + sum += basis[static_cast(j) * stride + offset] * coefficients[j * nvec + b]; + } + output[offset] += sum; + } + } +} + +template +void linear_op::scatter(int ld, int dim, int nvec, const T* input, T* output, const int* map) const +{ +#pragma omp parallel for schedule(static) if (parallel_work(dim, nvec)) + for (int b = 0; b < nvec; ++b) + { + const std::int64_t source = static_cast(b) * ld; + const std::int64_t destination = static_cast(map[b]) * ld; + std::copy_n(input + source, dim, output + destination); + } +} + +template +void linear_op::block_gram(int ld, int dim, int nvec, int count, int stride, const T* basis, std::complex* output) const +{ +#pragma omp parallel for collapse(2) schedule(static) if (parallel_work(dim, nvec * count)) + for (int col = 0; col < count; ++col) + { + for (int b = 0; b < nvec; ++b) + { + for (int row = 0; row <= col; ++row) + { + const std::int64_t first = static_cast(row) * stride + static_cast(b) * ld; + const std::int64_t second = static_cast(col) * stride + static_cast(b) * ld; + std::complex sum(0); + for (int i = 0; i < dim; ++i) + { + sum += std::conj(std::complex(basis[first + i])) * std::complex(basis[second + i]); + } + output[(col * count + row) * nvec + b] = sum; + output[(row * count + col) * nvec + b] = std::conj(sum); + } + } + } +} + template class linear_op, base_device::DEVICE_CPU>; template class linear_op, base_device::DEVICE_CPU>; diff --git a/source/source_hsolver/module_rt_pw/kernels/linear_op.h b/source/source_hsolver/module_rt_pw/kernels/linear_op.h index e6ffe6ff3e7..7f59d8c418d 100644 --- a/source/source_hsolver/module_rt_pw/kernels/linear_op.h +++ b/source/source_hsolver/module_rt_pw/kernels/linear_op.h @@ -13,6 +13,12 @@ template class linear_op { public: + /** @brief Add per-band linear combinations of Krylov columns; coefficients are column-major blocks. */ + void combine(int ld, int dim, int nvec, int count, int stride, const T* basis, const T* coefficients, T* output) const; + /** @brief Scatter packed columns to distinct original-band destinations. */ + void scatter(int ld, int dim, int nvec, const T* input, T* output, const int* map) const; + /** @brief Per-band Krylov Gram matrices in double precision, indexed as (column * count + row) * nvec + band. */ + void block_gram(int ld, int dim, int nvec, int count, int stride, const T* basis, std::complex* output) const; /** @brief Skip zero-coefficient columns; a null residual disables the paired residual update. */ void gmres_update(int ld, int dim, int nvec, T* solution, T* residual, const T* direction, const T* image, const T* coefficients) const; /** @brief Batched products accumulated in wavefunction precision. */ @@ -45,6 +51,12 @@ template class linear_op { public: + /** @brief Add per-band linear combinations of Krylov columns; coefficients are column-major blocks. */ + void combine(int ld, int dim, int nvec, int count, int stride, const T* basis, const T* coefficients, T* output) const; + /** @brief Scatter packed columns to distinct original-band destinations. */ + void scatter(int ld, int dim, int nvec, const T* input, T* output, const int* map) const; + /** @brief Per-band Krylov Gram matrices in double precision, indexed as (column * count + row) * nvec + band. */ + void block_gram(int ld, int dim, int nvec, int count, int stride, const T* basis, std::complex* output) const; /** @brief Skip zero-coefficient columns; a null residual disables the paired residual update. */ void gmres_update(int ld, int dim, int nvec, T* solution, T* residual, const T* direction, const T* image, const T* coefficients) const; /** @brief Batched products accumulated in wavefunction precision. */ diff --git a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu b/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu index c48d752398f..fe69e067489 100644 --- a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu +++ b/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu @@ -407,6 +407,126 @@ void linear_op::native_dots(int ld, int dim, int nve check_launch(); } +namespace +{ +template +__global__ void combine_kernel(int ld, int dim, int count, int stride, int nvec, + const C* basis, const C* coefficients, C* output) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int b = blockIdx.y; + if (i < dim) + { + const std::int64_t offset = static_cast(b) * ld + i; + C sum(0); + for (int j = 0; j < count; ++j) + { + sum += basis[static_cast(j) * stride + offset] * coefficients[j * nvec + b]; + } + output[offset] += sum; + } +} + +template +__global__ void scatter_kernel(int ld, int dim, const C* input, C* output, const int* map) +{ + const int i = blockIdx.x * blockDim.x + threadIdx.x; + const int b = blockIdx.y; + if (i < dim) + { + output[static_cast(map[b]) * ld + i] = input[static_cast(b) * ld + i]; + } +} + +template +__global__ void block_gram_kernel(int ld, int dim, int nvec, int count, int stride, + const thrust::complex* basis, thrust::complex* output) +{ + const int b = blockIdx.x % nvec; + const int pair = blockIdx.x / nvec; + const int row = pair % count; + const int col = pair / count; + if (row > col) + { + return; + } + const std::int64_t first = static_cast(row) * stride + static_cast(b) * ld; + const std::int64_t second = static_cast(col) * stride + static_cast(b) * ld; + thrust::complex sum(0); + for (int i = threadIdx.x; i < dim; i += blockDim.x) + { + sum += thrust::conj(thrust::complex(basis[first + i])) * thrust::complex(basis[second + i]); + } + __shared__ double re[linear_threads]; + __shared__ double im[linear_threads]; + const int tid = threadIdx.x; + re[tid] = sum.real(); + im[tid] = sum.imag(); + __syncthreads(); + for (int step = blockDim.x / 2; step > 0; step /= 2) + { + if (tid < step) + { + re[tid] += re[tid + step]; + im[tid] += im[tid + step]; + } + __syncthreads(); + } + if (tid == 0) + { + output[(col * count + row) * nvec + b] = thrust::complex(re[0], im[0]); + if (row != col) + { + output[(row * count + col) * nvec + b] = thrust::complex(re[0], -im[0]); + } + } +} +} // namespace + +template +void linear_op::combine(int ld, int dim, int nvec, int count, int stride, + const T* basis, const T* coefficients, T* output) const +{ + if (dim == 0 || nvec == 0) + { + return; + } + using C = thrust::complex; + const dim3 grid((dim + linear_threads - 1) / linear_threads, nvec); + combine_kernel<<>>(ld, dim, count, stride, nvec, + reinterpret_cast(basis), reinterpret_cast(coefficients), reinterpret_cast(output)); + check_launch(); +} + +template +void linear_op::scatter(int ld, int dim, int nvec, + const T* input, T* output, const int* map) const +{ + if (dim == 0 || nvec == 0) + { + return; + } + using C = thrust::complex; + const dim3 grid((dim + linear_threads - 1) / linear_threads, nvec); + scatter_kernel<<>>(ld, dim, reinterpret_cast(input), reinterpret_cast(output), map); + check_launch(); +} + +template +void linear_op::block_gram(int ld, int dim, int nvec, int count, int stride, + const T* basis, std::complex* output) const +{ + if (nvec == 0 || count == 0) + { + return; + } + using C = thrust::complex; + const int blocks = nvec * count * count; + block_gram_kernel<<>>(ld, dim, nvec, count, stride, reinterpret_cast(basis), + reinterpret_cast*>(output)); + check_launch(); +} + template class linear_op, base_device::DEVICE_GPU>; template class linear_op, base_device::DEVICE_GPU>; diff --git a/source/source_io/module_parameter/read_inp_tddft.cpp b/source/source_io/module_parameter/read_inp_tddft.cpp index 42b49aac047..8a7d8f28274 100644 --- a/source/source_io/module_parameter/read_inp_tddft.cpp +++ b/source/source_io/module_parameter/read_inp_tddft.cpp @@ -464,7 +464,7 @@ For a nonorthogonal LCAO basis: item.category = "Real-Time TDDFT (PW)"; item.type = "String"; item.description = R"(Algorithm used to evaluate $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}$ for Enforced time-reversal symmetry (ETRS) propagation with a plane-wave basis. The Hamiltonian $\boldsymbol{H}$ is held fixed over the evolution interval $\tau$. The formulas below use Hartree atomic units. -* `lanczos`: Lanczos method with full reorthogonalization, $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}\approx\lVert\boldsymbol{\psi}\rVert\boldsymbol{Q}_m\exp(-\mathrm{i}\tau\boldsymbol{H}_m)\boldsymbol{e}_1$, where $\boldsymbol{Q}_m$ contains the orthonormal Krylov basis vectors, $\boldsymbol{H}_m=\boldsymbol{Q}_m^\dagger\boldsymbol{H}\boldsymbol{Q}_m$, and $\boldsymbol{e}_1$ is the first unit vector. The estimated relative error is controlled by `td_exp_thr`, and the maximum Krylov dimension by `td_exp_krylov_dim`. +* `lanczos`: Adaptive Lanczos approximation with automatic reorthogonalization when needed. The estimated error relative to the input wavefunction norm is controlled by `td_exp_thr`, and the maximum Krylov dimension by `td_exp_krylov_dim`. The half-step and full-step predictor share a Krylov basis. If the error target cannot be met within the maximum dimension, the frozen-Hamiltonian interval is subdivided internally. * `taylor`: Fixed-order Taylor approximation, $\exp(-\mathrm{i}\tau\boldsymbol{H})\boldsymbol{\psi}\approx\sum_{k=0}^{p}(-\mathrm{i}\tau\boldsymbol{H})^k\boldsymbol{\psi}/k!$, where the order $p$ is specified by `td_exp_order`.)"; item.default_value = "lanczos"; item.unit = ""; diff --git a/source/source_pw/module_rt/prop_etrs_pw.cpp b/source/source_pw/module_rt/prop_etrs_pw.cpp index dc2fa10a30e..af2886d7920 100644 --- a/source/source_pw/module_rt/prop_etrs_pw.cpp +++ b/source/source_pw/module_rt/prop_etrs_pw.cpp @@ -45,7 +45,8 @@ void ETRSPropPW::report(const hsolver::ExpResult& result, const PropA std::ostringstream text; text << " PW ETRS exponential: step=" << args.elec_step << " scf_iter=" << args.scf_iter << " global_k=" << orb_opts_.global_k_indices.at(ik) + 1 << " order_or_dimension=" << result.order_or_dim - << " operator_calls=" << result.hpsi_calls << " operator_columns=" << result.hpsi_cols << " subdivisions=" << result.bisect_count; + << " operator_calls=" << result.hpsi_calls << " operator_columns=" << result.hpsi_cols << " subdivisions=" << result.bisect_count + << " reorthogonalizations=" << result.reorth_count; if (result.has_err_est) { text << " estimated_error=" << result.rel_err_est; From 8829896170980f0ceeefc215317a0827685c89f7 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 16:13:58 +0800 Subject: [PATCH 11/33] Remove redundant workspaces, projection overload, and unused CN settings from ETRS tests --- .../module_rt_pw/cn_solver_pw.cpp | 7 +- .../module_rt_pw/cn_solver_pw.h | 1 - .../source_pw/module_proj/onsite_proj_tools.h | 8 +- .../module_proj/onsite_proj_tools_becp.cpp | 99 ++++++++++--------- .../05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT | 6 -- .../05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT | 6 -- tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT | 6 -- 7 files changed, 59 insertions(+), 74 deletions(-) diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp index 5fe402a3daf..b5df2a13652 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp @@ -100,7 +100,7 @@ CNSolverPW::CNSolverPW(const ModulePW::PW_Basis_K& basis, const PWLinearOptions& options, const diag_comm_info& comm, std::ostream& log) - : basis_(basis), comm_(comm), options_(options), algebra_(comm), log_(log), band_products_(comm, 9) + : basis_(basis), comm_(comm), options_(options), algebra_(comm), log_(log) { initialize(); } @@ -322,7 +322,10 @@ typename CNSolverPW::SolveDetails CNSolverPW::solve_kpoint if (options_.cn_init && iteration == 1 && details.projected) { const T* seed = projection_.seed(); - band_products_.copy(batch.ld, batch.dim, batch.bands, seed, current); + if (batch.dim > 0) + { + base_device::memory::synchronize_memory_2d_op()(current, batch.ld, seed, batch.ld, batch.dim, batch.bands); + } initial_residual = projection_.residual(); details.cn_initial = true; } diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.h b/source/source_hsolver/module_rt_pw/cn_solver_pw.h index e2506e1012a..75d34525820 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.h +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.h @@ -107,7 +107,6 @@ class CNSolverPW std::vector states_; SequenceState sequence_; std::unique_ptr> linear_solver_; - LinearWorkspace band_products_; ct::Tensor rhs_; ct::Tensor inverse_kinetic_; ct::Tensor response_workspace_; diff --git a/source/source_pw/module_proj/onsite_proj_tools.h b/source/source_pw/module_proj/onsite_proj_tools.h index 4e1ef4f47b3..e88cc374c54 100644 --- a/source/source_pw/module_proj/onsite_proj_tools.h +++ b/source/source_pw/module_proj/onsite_proj_tools.h @@ -63,11 +63,9 @@ class Onsite_Proj_tools * @brief calculate the becp = for all beta functions */ void cal_becp(int ik, int npm, std::complex* becp_in = nullptr, const std::complex* ppsi_in = nullptr, int npwx = 0); - /** @brief Gauge-covariant projection at the explicit endpoint vector potential (inverse Bohr). */ - void cal_becp(int ik, int npm, std::complex* becp_in, - const std::complex* ppsi_in, int npwx, - const ModuleBase::Vector3& vector_potential); - /** @brief Also retain the pool-reduced overlaps in the caller's host buffer. */ + /** @brief Gauge-covariant projection at the explicit endpoint vector potential (inverse Bohr). + * @param host_becp Optional caller-owned host buffer for the pool-reduced overlaps; may be nullptr. + */ void cal_becp(int ik, int npm, std::complex* becp_in, const std::complex* ppsi_in, int npwx, const ModuleBase::Vector3& vector_potential, diff --git a/source/source_pw/module_proj/onsite_proj_tools_becp.cpp b/source/source_pw/module_proj/onsite_proj_tools_becp.cpp index 1199ed4dff2..d9ae7a18105 100644 --- a/source/source_pw/module_proj/onsite_proj_tools_becp.cpp +++ b/source/source_pw/module_proj/onsite_proj_tools_becp.cpp @@ -1,12 +1,12 @@ -#include "source_pw/module_proj/onsite_proj_tools.h" - #include "source_base/math_polyint.h" #include "source_base/math_ylmreal.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" #include "source_base/tool_title.h" #include "source_io/module_parameter/parameter.h" +#include "source_pw/module_proj/onsite_proj_tools.h" #include "source_pw/module_pwdft/nonlocal_maths.h" + #include // cal_becp @@ -19,13 +19,13 @@ // because the atom selection task is operator-specific. template void hamilt::Onsite_Proj_tools::cal_becp(int ik, - int npm, - std::complex* becp_in, - const std::complex* ppsi_in, - int npwx) + int npm, + std::complex* becp_in, + const std::complex* ppsi_in, + int npwx) { const ModuleBase::Vector3 zero; - cal_becp(ik, npm, becp_in, ppsi_in, npwx, zero); + cal_becp(ik, npm, becp_in, ppsi_in, npwx, zero, nullptr); } template @@ -47,20 +47,13 @@ void hamilt::Onsite_Proj_tools::invalidate_proj() } template -void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, - std::complex* becp_in, - const std::complex* ppsi_in, int npwx, - const ModuleBase::Vector3& vector_potential) -{ - cal_becp(ik, npm, becp_in, ppsi_in, npwx, vector_potential, nullptr); -} - -template -void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, - std::complex* becp_in, - const std::complex* ppsi_in, int npwx, - const ModuleBase::Vector3& vector_potential, - std::complex* host_becp) +void hamilt::Onsite_Proj_tools::cal_becp(int ik, + int npm, + std::complex* becp_in, + const std::complex* ppsi_in, + int npwx, + const ModuleBase::Vector3& vector_potential, + std::complex* host_becp) { ModuleBase::TITLE("Onsite_Proj_tools", "cal_becp"); ModuleBase::timer::start("Onsite_Proj_tools", "cal_becp"); @@ -235,19 +228,19 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, } else { - gemm_op()(transa, - transb, - this->nkb, - npm_npol, // nbands(occ)*npol - npw, - &ModuleBase::ONE, - this->ppcell_vkb, - npw, - ppsi, - npwx > 0 ? npwx : this->max_npw, - &ModuleBase::ZERO, - becp_tmp, - this->nkb); + gemm_op()(transa, + transb, + this->nkb, + npm_npol, // nbands(occ)*npol + npw, + &ModuleBase::ONE, + this->ppcell_vkb, + npw, + ppsi, + npwx > 0 ? npwx : this->max_npw, + &ModuleBase::ZERO, + becp_tmp, + this->nkb); } if (this->device == base_device::GpuDevice) @@ -285,24 +278,34 @@ void hamilt::Onsite_Proj_tools::cal_becp(int ik, int npm, } // explicit method instantiation -template void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&, std::complex*); +template void hamilt::Onsite_Proj_tools::cal_becp(int, + int, + std::complex*, + const std::complex*, + int, + const ModuleBase::Vector3&, + std::complex*); template void hamilt::Onsite_Proj_tools::refresh_table(); template void hamilt::Onsite_Proj_tools::invalidate_proj(); -template void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&); -template -void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int); +template void hamilt::Onsite_Proj_tools::cal_becp(int, + int, + std::complex*, + const std::complex*, + int); #if ((defined __CUDA) || (defined __ROCM)) -template void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&, std::complex*); +template void hamilt::Onsite_Proj_tools::cal_becp(int, + int, + std::complex*, + const std::complex*, + int, + const ModuleBase::Vector3&, + std::complex*); template void hamilt::Onsite_Proj_tools::refresh_table(); template void hamilt::Onsite_Proj_tools::invalidate_proj(); -template void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int, const ModuleBase::Vector3&); -template -void hamilt::Onsite_Proj_tools::cal_becp( - int, int, std::complex*, const std::complex*, int); +template void hamilt::Onsite_Proj_tools::cal_becp(int, + int, + std::complex*, + const std::complex*, + int); #endif diff --git a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT index aeb667f2c08..341b231dc79 100644 --- a/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT +++ b/tests/05_rtTDDFT/32_PW_ETRS_lanczos_TDDFT/INPUT @@ -47,12 +47,6 @@ td_gauss_phase 1.5707963267948966 td_gauss_sigma 0.04 td_gauss_t0 0 td_gauss_amp 0.1 -lin_solver bicgstab -lin_precond kinetic -lin_thr 1e-13 -lin_maxiter 100 -td_cn_init 0 -lin_reconstruct 0 td_out_torque 1 td_out_spincur 1 out_current 1 diff --git a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT index 486d149481f..f3624af9950 100644 --- a/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT +++ b/tests/05_rtTDDFT/33_PW_ETRS_taylor_TDDFT/INPUT @@ -41,12 +41,6 @@ td_gauss_phase 0 td_gauss_sigma 0.04 td_gauss_t0 4 td_gauss_amp 0.001 -lin_solver bicgstab -lin_precond kinetic -lin_thr 1e-14 -lin_maxiter 100 -td_cn_init 0 -lin_reconstruct 0 out_current 1 out_level ie td_propagator 2 diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT index d8f2a0ff486..914ad487b66 100644 --- a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/INPUT @@ -41,12 +41,6 @@ td_gauss_phase 0 td_gauss_sigma 0.04 td_gauss_t0 4 td_gauss_amp 0.1 -lin_solver bicgstab -lin_precond kinetic -lin_thr 1e-14 -lin_maxiter 100 -td_cn_init 0 -lin_reconstruct 0 out_current 1 out_level ie td_propagator 2 From 1947ef7016ec470d4d0a201f47b3a0c1fbd9c788 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 16:39:25 +0800 Subject: [PATCH 12/33] Refactor test fixtures to isolate parameter state and remove unnecessary global dependencies --- .../test/test_hsolver_pw_tddft.cpp | 18 -------- .../source_pw/module_rt/test/td_test_utils.h | 17 +++++--- .../module_rt/test/test_td_obs_pw.cpp | 41 ++++++++++++------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp index db36e9037cb..12f370bbb1e 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp @@ -2,7 +2,6 @@ #include "source_hsolver/module_rt_pw/cn_solver_pw.h" #include "source_hsolver/module_rt_pw/td_orbital_ops.h" #include "source_hsolver/module_rt_pw/test/linear_test_utils.h" -#include "source_io/module_parameter/parameter.h" #include #include @@ -12,22 +11,6 @@ #include #include -class TestParameters -{ - public: - TestParameters() : saved_spin_(PARAM.inp.nspin) - { - PARAM.input.nspin = 4; - } - ~TestParameters() - { - PARAM.input.nspin = saved_spin_; - } - - private: - int saved_spin_; -}; - namespace { using Device = base_device::DEVICE_CPU; @@ -172,7 +155,6 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) { using T = std::complex; using Wide = linear_test::Complex; - TestParameters spinor_parameters; const bool single = std::is_same::value; const double tolerance = single ? 1e-4 : 1e-10; const std::vector sizes{5, 3}; diff --git a/source/source_pw/module_rt/test/td_test_utils.h b/source/source_pw/module_rt/test/td_test_utils.h index 840db4602af..59f8817fe89 100644 --- a/source/source_pw/module_rt/test/td_test_utils.h +++ b/source/source_pw/module_rt/test/td_test_utils.h @@ -1,21 +1,27 @@ #ifndef TD_TEST_UTILS_H #define TD_TEST_UTILS_H +#include "source_io/module_parameter/parameter.h" #include "source_pw/module_rt/td_spin_pw.h" #include "source_pw/module_rt/td_vel_pw.h" #include -/** @brief Restore the global input used by legacy PW initialization. */ +/** @brief Install isolated spinor test settings and restore the supplied parameter object's state. */ class TestParameters { - public: - TestParameters(); - ~TestParameters(); - private: + Parameter& target_; Input_para input_; System_para system_; + + public: + explicit TestParameters(Parameter& target); + ~TestParameters() noexcept; + TestParameters(const TestParameters&) = delete; + TestParameters& operator=(const TestParameters&) = delete; + TestParameters(TestParameters&&) = delete; + TestParameters& operator=(TestParameters&&) = delete; }; namespace td_test @@ -38,6 +44,7 @@ class Observables : public testing::Test elecstate::ElecState state_; std::unique_ptr> psi_; + Observables(); void SetUp() override; void TearDown() override; Vector projectors(const ModuleBase::Vector3& a); diff --git a/source/source_pw/module_rt/test/test_td_obs_pw.cpp b/source/source_pw/module_rt/test/test_td_obs_pw.cpp index 06555a1f390..3baa37b2c80 100644 --- a/source/source_pw/module_rt/test/test_td_obs_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_obs_pw.cpp @@ -3,29 +3,42 @@ #include "source_pw/module_rt/test/td_test_utils.h" #include +#include -TestParameters::TestParameters() : input_(PARAM.inp), system_(PARAM.globalv) +static_assert(noexcept(std::swap(std::declval(), std::declval())), + "Test input installation and restoration must not throw."); +static_assert(noexcept(std::swap(std::declval(), std::declval())), + "Test system installation and restoration must not throw."); + +TestParameters::TestParameters(Parameter& target) : target_(target) { - PARAM.input.nspin = 4; - PARAM.input.noncolin = true; - PARAM.input.lspinorb = true; - PARAM.input.device = "cpu"; - PARAM.input.basis_type = "pw"; - PARAM.input.precision = "double"; - PARAM.sys.nqx = 600; - PARAM.sys.nqxq = 600; - PARAM.sys.has_double_data = true; - PARAM.sys.has_float_data = false; + input_.nspin = 4; + input_.noncolin = true; + input_.lspinorb = true; + input_.device = "cpu"; + input_.basis_type = "pw"; + input_.precision = "double"; + system_.nqx = 600; + system_.nqxq = 600; + system_.has_double_data = true; + system_.has_float_data = false; + // Prepare the complete configuration before installing it; retain the original state in these members. + std::swap(target_.input, input_); + std::swap(target_.sys, system_); } -TestParameters::~TestParameters() +TestParameters::~TestParameters() noexcept { - PARAM.input = input_; - PARAM.sys = system_; + std::swap(target_.input, input_); + std::swap(target_.sys, system_); } namespace td_test { +Observables::Observables() : parameters_(PARAM) +{ +} + void Observables::SetUp() { cell_.ntype = 1; From afb836461f1948e9ca7c7e931f6277838c3aaa99 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 17:07:39 +0800 Subject: [PATCH 13/33] Decouple density restart output from convergence flags and improve TDDFT workflow readability --- source/source_esolver/esolver_fp.cpp | 57 +++------- source/source_esolver/esolver_ks.cpp | 27 +++-- source/source_esolver/esolver_ks_pw_tddft.cpp | 104 +++++++++++++++++- .../module_charge/chg_rhog_io.cpp | 83 ++++++++++---- .../source_estate/module_charge/chg_rhog_io.h | 37 +++++-- 5 files changed, 229 insertions(+), 79 deletions(-) diff --git a/source/source_esolver/esolver_fp.cpp b/source/source_esolver/esolver_fp.cpp index 149f50dc8cb..111bb688577 100644 --- a/source/source_esolver/esolver_fp.cpp +++ b/source/source_esolver/esolver_fp.cpp @@ -278,46 +278,23 @@ void ESolver_FP::iter_finish(UnitCell& ucell, const int istep, int& iter, bool& { if (iter % this->inp_->out_freq_elec == 0 || iter == this->inp_->scf_nmax || conv_esolver) { - for (int is = 0; is < this->inp_->nspin; is++) - { - this->pw_rhod->real2recip(this->chr.rho_save[is], this->chr.rhog_save[is]); - } - // Temporary bridge: use factory until ParaCollection is wired into driver. - Parallel::ParaWorld pw_world = Parallel::make_pw_world(); - // Only pool 0 writes the rhog file (rhog is identical across pools). - if (GlobalV::MY_POOL == 0) - { - module_charge::write_rhog(PARAM.globalv.global_out_dir + this->inp_->suffix + "-CHARGE-DENSITY.restart", - PARAM.globalv.gamma_only_pw, - this->pw_rhod, - this->inp_->nspin, - ucell.GT, - this->chr.rhog_save, - pw_world, - &GlobalV::ofs_warning); - } - - if (XC_Functional::get_ked_flag()) - { - std::vector> kin_g_space(this->inp_->nspin * this->chr.ngmc, {0.0, 0.0}); - std::vector*> kin_g; - for (int is = 0; is < this->inp_->nspin; is++) - { - kin_g.push_back(kin_g_space.data() + is * this->chr.ngmc); - this->pw_rhod->real2recip(this->chr.kin_r_save[is], kin_g[is]); - } - if (GlobalV::MY_POOL == 0) - { - module_charge::write_rhog(PARAM.globalv.global_out_dir + this->inp_->suffix + "-TAU-DENSITY.restart", - PARAM.globalv.gamma_only_pw, - this->pw_rhod, - this->inp_->nspin, - ucell.GT, - kin_g.data(), - pw_world, - &GlobalV::ofs_warning); - } - } + const auto& system = PARAM.globalv; + const bool write_restart = this->inp_->out_chg[0] != -1; + const std::string prefix = system.global_out_dir + this->inp_->suffix; + const bool write_tau = XC_Functional::get_ked_flag(); + const bool writer_pool = GlobalV::MY_POOL == 0; + const Parallel::ParaWorld pw_world = Parallel::make_pw_world(); + module_charge::write_density_restart(this->chr, + *this->pw_rhod, + ucell.GT, + prefix, + this->inp_->nspin, + write_restart, + system.gamma_only_pw, + write_tau, + writer_pool, + pw_world, + &GlobalV::ofs_warning); } } } diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index c19d5656de0..ad8e9015512 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -1,5 +1,6 @@ #include "esolver_ks.h" #include "source_base/timer_wrapper.h" +#include "source_base/module_parallel/para_bridge.h" // for jason output information #include "source_io/module_json/output_info.h" @@ -9,6 +10,7 @@ #include "source_estate/module_charge/chg_drho.h" // module_charge::cal_drho/cal_dkin #include "source_estate/module_charge/chg_init.h" // module_charge::InitRhoCfg #include "source_estate/module_charge/chg_tools.h" // module_charge::check_rho +#include "source_estate/module_charge/chg_rhog_io.h" #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" @@ -405,16 +407,27 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & p_chgmix->get_mixing_config(), ucell.omega); } - // Iter finish + // Iter finish const bool output_charge = iter % this->inp_->out_freq_elec == 0 || conv_esolver || iter == iter_limit; if (!this->defer_iter_output(istep) && output_charge) { - // The FP hook only writes density backups. Select output here using the - // active limit so its legacy scf_nmax check cannot trigger extra writes. - // This local flag forces output without changing actual convergence. - // Revisit this call if the base hook gains responsibilities beyond output. - bool force_output = true; - ESolver_FP::iter_finish(ucell, istep, iter, force_output); + const auto& system = PARAM.globalv; + const bool write_restart = this->inp_->out_chg[0] != -1; + const std::string prefix = system.global_out_dir + this->inp_->suffix; + const bool write_tau = XC_Functional::get_ked_flag(); + const bool writer_pool = GlobalV::MY_POOL == 0; + const Parallel::ParaWorld pw_world = Parallel::make_pw_world(); + module_charge::write_density_restart(this->chr, + *this->pw_rhod, + ucell.GT, + prefix, + this->inp_->nspin, + write_restart, + system.gamma_only_pw, + write_tau, + writer_pool, + pw_world, + &GlobalV::ofs_warning); } // the end, print time diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 83df000aaf8..14bc6e78a41 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -3,12 +3,14 @@ #include "source_base/constants.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" +#include "source_base/module_parallel/para_bridge.h" #include "source_base/parallel_comm.h" #include "source_base/parallel_device.h" #include "source_base/parallel_reduce.h" #include "source_base/timer.h" #include "source_estate/elecstate_pw.h" #include "source_estate/elecstate_tools.h" +#include "source_estate/module_charge/chg_rhog_io.h" #include "source_estate/module_charge/chg_symm.h" #include "source_hamilt/hamilt_hs_adapter.h" #include "source_hamilt/module_xc/xc_functional.h" @@ -66,21 +68,26 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons } ESolver_KS_PW::before_all_runners(ucell, inp); + // The parent resolves the XC functional, including pseudopotential defaults. const int xc_type = XC_Functional::get_func_type(); pw::check_td_input(inp, ucell, xc_type, this->general_exx_info_.cal_exx); + spin_options_.method = inp.td_out_mag; spin_options_.orbital_dir = inp.orbital_dir; spin_options_.radius = inp.onsite_radius; spin_options_.torque = inp.td_out_torque; + vel_request_.charge_total = inp.out_current == 1; vel_request_.charge_per_k = vel_request_.charge_total && inp.out_current_k; vel_request_.spin_current = inp.nspin == 4 && inp.td_out_spincur; vel_.initialize(ucell, *this->pw_wfc, this->kv, this->ppcell); + if (inp.nspin == 4) { spin_.initialize(ucell, *this->pw_rhod, *this->pw_wfc, this->kv, this->sf, this->ppcell, spin_options_); } + this->pelec->pot->set_td_field_manager(this->td_field_manager_); #ifdef __MPI const hsolver::diag_comm_info comm(this->pw_wfc->pool_world, this->pw_wfc->poolrank, this->pw_wfc->poolnproc); @@ -91,6 +98,7 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons const pw::TDPropOpts options = pw::td_prop_options(inp, npol, this->kv.ik2iktot); this->td_prop_.reset(new pw::TDPropPW(*this->pw_wfc, options, comm, log_)); this->history_.prepare(*this->pelec->pot); + // Preserve existing field history until input validation and initialization succeed. if (GlobalV::MY_RANK == 0) { @@ -126,6 +134,7 @@ void ESolver_KS_PW_TDDFT::runner(BaseCell& basecell, const int istep) first_step = (istep - 1) * steps_per_md + 1; last_step = istep * steps_per_md; } + for (int electronic_step = first_step; electronic_step <= last_step; ++electronic_step) { if (electronic_step > 0) @@ -159,11 +168,13 @@ typename ESolver_KS_PW_TDDFT::AcceptedState ESolver_KS_PW_TDDFTpelec->f_en; state.eigenvalues = this->pelec->ekb; + state.density.resize(this->chr.nspin * this->chr.nrxx); for (int spin = 0; spin < this->chr.nspin; ++spin) { std::copy(this->chr.rho[spin], this->chr.rho[spin] + this->chr.nrxx, state.density.begin() + spin * this->chr.nrxx); } + return state; } @@ -171,20 +182,24 @@ template void ESolver_KS_PW_TDDFT::restore_state(const AcceptedState& state) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "restore_state"); + *this->stp.template get_psi_t() = this->history_.previous(); this->pelec->f_en = state.energy; this->pelec->ekb = state.eigenvalues; this->pelec->vnew_exist = false; + for (int spin = 0; spin < this->chr.nspin; ++spin) { const double* density = state.density.data() + spin * this->chr.nrxx; std::copy(density, density + this->chr.nrxx, this->chr.rho[spin]); } this->chr.save_rho_before_sum_band(); + // The caller binds the trial field and rebuilds the endpoint potential. The saved // left-endpoint potential used by the corrector is already held in history_. this->td_prop_->invalidate_basis(); this->td_prop_->orbitals().reset_orth_stats(); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "restore_state"); } @@ -201,18 +216,22 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, if (depth > 0) { this->restore_state(accepted); + const elecstate::TDFieldState trial = this->td_field_manager_->subinterval(full, index, parts); this->td_field_manager_->bind_field(trial); + // Length-gauge fields are fixed potential components and must be rebuilt // at the trial endpoint, just as before_scf does for an original step. this->pelec->pot->init_pot(&this->chr); this->prepare_init_hamilt(cell); } + const double left_time = (step - 1 + static_cast(index) / parts) * full.dt * ModuleBase::AU_to_FS; const double right_time = (step - 1 + static_cast(index + 1) / parts) * full.dt * ModuleBase::AU_to_FS; const std::streamsize precision = log_.precision(); log_ << " TD trial: [" << std::setprecision(12) << left_time << ", " << right_time << "] fs, depth=" << depth << '\n'; log_.precision(precision); + std::ostringstream interval; interval << std::setprecision(12) << "[" << left_time << ", " << right_time << "] fs"; const std::string interval_text = interval.str(); @@ -220,9 +239,11 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, { std::cout << " TD substep: " << interval_text << std::endl; } + const int iter_limit = this->scf_iter_limit(step); this->td_prop_->begin_trial(); const bool converged = this->scf_loop(cell, step, iter_limit); + const bool accept_trial = converged || depth == this->inp_->td_max_halvings; log_ << " TD trial result: iterations=" << this->niter << ", residual=" << this->drho << ", converged=" << converged << ", accepted=" << accept_trial << '\n'; @@ -230,20 +251,25 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, { std::cout << " TD retry: SCF not converged; splitting " << interval_text << " into two half steps." << std::endl; this->td_prop_->discard_trial(); + const int next_depth = depth + 1; const int left_index = 2 * index; const int right_index = left_index + 1; + const bool left_converged = advance_interval(cell, step, left_index, next_depth, full, accepted, stats); const bool right_converged = advance_interval(cell, step, right_index, next_depth, full, accepted, stats); return left_converged && right_converged; } + if (!converged) { this->finalize_endpoint(cell); } + const hsolver::TDOrthStats& trial_stats = this->td_prop_->orbitals().orth_stats(); stats.before = std::max(stats.before, trial_stats.before); stats.after = std::max(stats.after, trial_stats.after); + const psi::Psi* current = this->stp.template get_psi_t(); this->history_.save(*current, *this->pelec->pot); this->td_prop_->finish_trial(); @@ -251,6 +277,7 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, { accepted = this->capture_state(); } + return converged; } @@ -259,31 +286,54 @@ void ESolver_KS_PW_TDDFT::run_propagation(UnitCell& cell, int step) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_propagation"); const ModuleBase::TimePoint started = ModuleBase::get_time(); + AcceptedState accepted; if (this->inp_->td_max_halvings > 0) { accepted = this->capture_state(); } + this->before_scf(cell, step); ModuleBase::GlobalFunc::DONE(log_, "INIT SCF"); + const elecstate::TDFieldState full = this->td_field_manager_->field_state(); hsolver::TDOrthStats stats; const bool converged = advance_interval(cell, step, 0, 0, full, accepted, stats); + // Restore original-grid sampling metadata without altering the accepted endpoint. this->td_field_manager_->bind_field(full); this->td_prop_->orbitals().set_orth_stats(stats); this->chr.save_rho_before_sum_band(); - // Taoni add 2026-10-20: ESolver_FP::iter_finish currently only writes density backups. - // Revisit this call if the base hook gains responsibilities beyond output. - bool force_output = true; - ESolver_FP::iter_finish(cell, step, this->niter, force_output); + + const auto& system = PARAM.globalv; + const bool write_restart = this->inp_->out_chg[0] != -1; + const std::string prefix = system.global_out_dir + this->inp_->suffix; + const bool write_tau = XC_Functional::get_ked_flag(); + const bool writer_pool = GlobalV::MY_POOL == 0; + const Parallel::ParaWorld pw_world = Parallel::make_pw_world(); + module_charge::write_density_restart(this->chr, + *this->pw_rhod, + cell.GT, + prefix, + this->inp_->nspin, + write_restart, + system.gamma_only_pw, + write_tau, + writer_pool, + pw_world, + &GlobalV::ofs_warning); + this->write_iter_bands(); ModuleIO::ctrl_iter_pw(step, this->niter, converged, this->stp, this->kv, this->pw_wfc, *this->inp_, true, log_); + const double duration = ModuleBase::get_duration(started, ModuleBase::get_time()); this->record_iteration(cell, duration); + this->output_field(step); + this->after_scf(cell, step, converged); this->conv_esolver = converged; + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_propagation"); } @@ -302,12 +352,14 @@ void ESolver_KS_PW_TDDFT::before_scf(UnitCell& ucell, const int istep this->print_step(istep); this->prep_td_step(istep); this->td_prop_->orbitals().reset_orth_stats(); + const bool basis_updated = ucell.cell_parameter_updated; ESolver_KS_PW::before_scf(ucell, istep); if (basis_updated) { this->td_prop_->invalidate_basis(); } + this->history_.prepare(*this->pelec->pot); } @@ -321,6 +373,7 @@ void ESolver_KS_PW_TDDFT::prepare_init_hamilt(const UnitCell& ucell) const auto& left = this->td_field_manager_->A_left_ha(); const auto& right = this->td_field_manager_->A_right_ha(); pw::ensure_td_vnl(ucell, q_unshifted, right, left, &this->ppcell); + elecstate::Potential& potential = *this->pelec->pot; const Real* veff = potential.template get_veff_smooth_data(); auto* hamiltonian = static_cast*>(this->p_hamilt); @@ -333,11 +386,13 @@ template void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "prep_td_step"); + if (this->td_field_manager_->current_step() == istep) { ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); return; } + if (this->td_field_manager_->gauge() == 0 || istep == 0) { this->td_field_manager_->prepare_sample(istep); @@ -347,6 +402,7 @@ void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) const int interval_left = istep - 1; this->td_field_manager_->prepare_interval(interval_left, istep); } + if (this->td_field_manager_->gauge() == 1) { const ModuleBase::Vector3& A_right_ha = this->td_field_manager_->A_right_ha(); @@ -354,10 +410,12 @@ void ESolver_KS_PW_TDDFT::prep_td_step(const int istep) ModuleBase::GlobalFunc::OUT(log_, "Cartesian vector potential Ay(t)", A_right_ha[1]); ModuleBase::GlobalFunc::OUT(log_, "Cartesian vector potential Az(t)", A_right_ha[2]); } + if (!this->defer_iter_output(istep)) { this->output_field(istep); } + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "prep_td_step"); } @@ -413,12 +471,15 @@ template void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const int istep, const int iter, const double ethr) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); + if (istep == 0) { ESolver_KS_PW::hamilt2rho_single(ucell, istep, iter, ethr); + psi::Psi* current = this->stp.template get_psi_t(); // Padding is a storage invariant even with orthogonalization disabled; clearing it does not change the density. this->td_prop_->orbitals().zero_padding(current); + if (this->inp_->td_orthonormal != "none") { int changed = this->td_prop_->orbitals().orth_initial(current, iter); @@ -429,6 +490,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in hamilt::Hamilt* hamiltonian = static_cast*>(this->p_hamilt); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); + elecstate::calculate_weights(this->pelec->ekb, this->pelec->wg, this->pelec->klist, @@ -438,11 +500,13 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in this->inp_->nbands, this->pelec->skip_weights); elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); + elecstate::ElecStatePW* estate = static_cast*>(this->pelec); estate->psiToRho(*current); module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); } } + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); return; } @@ -465,6 +529,7 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in // The later KS potential update belongs to the next SCF iteration. this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); } @@ -480,6 +545,7 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, { finalize_endpoint(ucell); } + // Endpoint diagnostics use the accepted endpoint, not the propagation midpoint or a mixed trial density. auto* endpoint_hamilt = static_cast*>(this->p_hamilt); const Real* endpoint_veff = this->pelec->pot->template get_veff_smooth_data(); @@ -488,10 +554,13 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, { print_eij(istep); } + ESolver_KS_PW::after_scf(ucell, istep, conv_esolver); + const std::streamsize original_precision = std::cout.precision(); std::cout << " Potential (Ry): " << std::setprecision(15) << this->pelec->f_en.etot << std::endl; std::cout.precision(original_precision); + if (istep >= 0) { // Propagation intervals already saved their accepted endpoint before reaching this hook. @@ -503,6 +572,7 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, sample_endpoint(ucell, istep); } + if (this->inp_->td_out_stat) { report_orth(ucell, istep); @@ -513,6 +583,7 @@ template void ESolver_KS_PW_TDDFT::finalize_endpoint(UnitCell& cell) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "finalize_endpoint"); + psi::Psi* current = this->stp.template get_psi_t(); auto* estate = static_cast*>(this->pelec); // The last SCF iteration may have mixed the density. Keep the actual orbitals and their occupations. @@ -524,14 +595,17 @@ void ESolver_KS_PW_TDDFT::finalize_endpoint(UnitCell& cell) auto* hamiltonian = static_cast*>(this->p_hamilt); const Real* veff = this->pelec->pot->template get_veff_smooth_data(); hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); + hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); this->pelec->f_en.deband = this->pelec->cal_delta_eband(cell); this->pelec->f_en.descf = 0.0; + // Do not reuse a self-consistency force correction from an earlier endpoint. this->pelec->vnew_exist = false; this->pelec->cal_energies(2, this->inp_->imp_sol, this->inp_->sc_mag_switch, this->inp_->dft_plus_u, this->inp_->assume_isolated); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "finalize_endpoint"); } @@ -540,13 +614,16 @@ void ESolver_KS_PW_TDDFT::sample_endpoint(UnitCell& cell, int step) { psi::Psi* current = this->stp.template get_psi_t(); const ModuleBase::Vector3& a = this->td_field_manager_->A_right_ha(); + // All endpoint currents share one velocity action; disabled requests perform no collectives. const pw::TDVelResultPW currents = vel_.calculate(*current, *this->pelec, a, vel_request_); + pw::TDSpinResultPW moments; if (this->inp_->nspin == 4 && (spin_options_.method != "none" || spin_options_.torque)) { moments = spin_.calculate(*current, *this->pelec, a); } + const std::string& directory = PARAM.globalv.global_out_dir; const int rank = GlobalV::MY_RANK; if (rank == 0) @@ -577,6 +654,7 @@ template void ESolver_KS_PW_TDDFT::print_eij(const int istep) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "print_eij"); + psi::Psi* current = this->stp.template get_psi_t(); const int original_k = current->get_current_k(); const int nband = current->get_nbands(); @@ -585,11 +663,13 @@ void ESolver_KS_PW_TDDFT::print_eij(const int istep) const int nk = this->kv.get_nkstot() / (nspin == 2 ? 2 : 1); const Parallel_Kpoints& pools = this->kv.para_k; const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; + auto* hamiltonian = static_cast*>(this->p_hamilt); const Real* veff = this->pelec->pot->template get_veff_smooth_data(); // cal_converged() updates the potential from the final density, after the last band-energy evaluation. hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); + for (int global_k = 0; global_k < this->kv.get_nkstot(); ++global_k) { const int physical_k = global_k % nk; @@ -600,11 +680,13 @@ void ESolver_KS_PW_TDDFT::print_eij(const int istep) owner = pools.whichpool[physical_k]; local_k = physical_k - pools.startk_pool[owner] + spin * pools.nks_pool[owner]; #endif + std::vector> eij; if (pools.my_pool == owner) { eij = this->td_prop_->orbitals().cal_eij(op, *current, local_k); } + #ifdef __MPI // Each pool already has the full matrix; transfer only one copy to the main log. if (owner != 0 && pools.rank_in_pool == 0 && (main_rank || pools.my_pool == owner)) @@ -630,14 +712,17 @@ void ESolver_KS_PW_TDDFT::print_eij(const int istep) } } #endif + if (main_rank) { const ModuleIO::EijOutputInfo info{this->inp_->td_print_eij, istep + 1, this->niter, physical_k + 1, nspin, spin + 1}; ModuleIO::print_eij(eij.data(), nband, nband, info, log_); } } + current->fix_k(original_k); op.update_k(original_k); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "print_eij"); } @@ -645,6 +730,7 @@ template void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int istep) { ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "report_orth"); + double electrons = this->td_prop_->orbitals().wave_electrons(this->pelec->wg); // Norms are replicated within a pool; only its root contributes to the total. if (this->pw_wfc->poolrank != 0) @@ -652,6 +738,7 @@ void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int iste electrons = 0.0; } Parallel_Reduce::reduce_all(electrons); + double rho_electrons = 0.0; const int density_spins = this->chr.nspin == 2 ? 2 : 1; for (int spin = 0; spin < density_spins; ++spin) @@ -663,11 +750,13 @@ void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int iste } rho_electrons *= ucell.omega / this->chr.rhopw->nxyz; Parallel_Reduce::reduce_pool(rho_electrons); + if (istep == 0) { initial_wave_electrons_ = electrons; initial_rho_electrons_ = rho_electrons; } + const hsolver::TDOrthStats& stats = this->td_prop_->orbitals().orth_stats(); const bool full_gram = this->inp_->td_orthonormal != "none"; double maxima[] = {stats.before, stats.after}; @@ -675,24 +764,29 @@ void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int iste { Parallel_Reduce::reduce_max(maxima, 2); } + if (!log_.good()) { ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "report_orth"); return; } + const char* stage = istep == 0 ? "initial state" : "propagation"; const int evolution_step = istep + 1; const double delta_wave = electrons - initial_wave_electrons_; const double delta_rho = rho_electrons - initial_rho_electrons_; + const std::ios::fmtflags saved_flags = log_.flags(); const std::streamsize saved_precision = log_.precision(); log_ << " PW RT-TDDFT conservation: evolution step " << evolution_step << ", " << stage << ", " << this->inp_->td_orthonormal << '\n'; log_ << std::right << std::fixed << std::setprecision(10); ModuleBase::GlobalFunc::OUT(log_, "Npsi", electrons); ModuleBase::GlobalFunc::OUT(log_, "Nrho", rho_electrons); + log_ << std::scientific << std::setprecision(6); ModuleBase::GlobalFunc::OUT(log_, "dNpsi", delta_wave); ModuleBase::GlobalFunc::OUT(log_, "dNrho", delta_rho); + if (full_gram) { if (istep == 0) @@ -705,8 +799,10 @@ void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int iste ModuleBase::GlobalFunc::OUT(log_, "orth_after_step_max", maxima[1]); } } + log_.flags(saved_flags); log_.precision(saved_precision); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "report_orth"); } diff --git a/source/source_estate/module_charge/chg_rhog_io.cpp b/source/source_estate/module_charge/chg_rhog_io.cpp index 9de2dca4a9b..c199db7313a 100644 --- a/source/source_estate/module_charge/chg_rhog_io.cpp +++ b/source/source_estate/module_charge/chg_rhog_io.cpp @@ -1,17 +1,16 @@ +#include "chg_rhog_io.h" + #include "source_base/module_out/binstream.h" -#include "source_base/vector3.h" #include "source_base/module_parallel/para_mpi_func.h" -#include "chg_rhog_io.h" +#include "source_base/vector3.h" + #include #include #include namespace { -inline void warn(std::ostream* os, - const Parallel::ParaWorld& pw_world, - const std::string& file, - const std::string& desc) +inline void warn(std::ostream* os, const Parallel::ParaWorld& pw_world, const std::string& file, const std::string& desc) { if (pw_world.rank() == 0 && os != nullptr) { @@ -20,12 +19,56 @@ inline void warn(std::ostream* os, } } // namespace +void module_charge::write_density_restart(Charge& charge, + ModulePW::PW_Basis& pw_rhod, + const ModuleBase::Matrix3& reciprocal_lattice, + const std::string& prefix, + const int nspin, + const bool enabled, + const bool gamma_only, + const bool write_tau, + const bool writer_pool, + const Parallel::ParaWorld& pw_world, + std::ostream* warning) +{ + if (!enabled) + { + return; + } + + for (int is = 0; is < nspin; ++is) + { + pw_rhod.real2recip(charge.rho_save[is], charge.rhog_save[is]); + } + if (writer_pool) + { + const std::string filename = prefix + "-CHARGE-DENSITY.restart"; + write_rhog(filename, gamma_only, &pw_rhod, nspin, reciprocal_lattice, charge.rhog_save, pw_world, warning); + } + + if (write_tau) + { + std::vector> kin_g_space(nspin * charge.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 * charge.ngmc); + pw_rhod.real2recip(charge.kin_r_save[is], kin_g[is]); + } + if (writer_pool) + { + const std::string filename = prefix + "-TAU-DENSITY.restart"; + write_rhog(filename, gamma_only, &pw_rhod, nspin, reciprocal_lattice, kin_g.data(), pw_world, warning); + } + } +} + bool module_charge::read_rhog(const std::string& filename, - const ModulePW::PW_Basis* pw_rhod, - const int nspin, - std::complex** rhog, - const Parallel::ParaWorld& pw_world, - std::ostream* os_warning) + const ModulePW::PW_Basis* pw_rhod, + const int nspin, + std::complex** rhog, + const Parallel::ParaWorld& pw_world, + std::ostream* os_warning) { if (pw_rhod == nullptr) { @@ -127,7 +170,7 @@ bool module_charge::read_rhog(const std::string& filename, ifs >> size; for (int i = 0; i < npwtot_in; ++i) // loop over rows... { - ifs >> miller[i*3] >> miller[i*3+1] >> miller[i*3+2]; + ifs >> miller[i * 3] >> miller[i * 3 + 1] >> miller[i * 3 + 2]; } ifs >> size; } @@ -210,13 +253,13 @@ bool module_charge::read_rhog(const std::string& filename, } bool module_charge::write_rhog(const std::string& fchg, - const bool gamma_only, - const ModulePW::PW_Basis* pw_rho, - const int nspin, - const ModuleBase::Matrix3& GT, - std::complex** rhog, - const Parallel::ParaWorld& pw_world, - std::ostream* os_warning) + const bool gamma_only, + const ModulePW::PW_Basis* pw_rho, + const int nspin, + const ModuleBase::Matrix3& GT, + std::complex** rhog, + const Parallel::ParaWorld& pw_world, + std::ostream* os_warning) { if (pw_rho == nullptr) { @@ -371,4 +414,4 @@ bool module_charge::write_rhog(const std::string& fchg, // diff = np.array(read) - np.array(write) // print(np.max(np.abs(diff))) // test system: integrated test 118_PW_CHG_BINARY -// yielding error 5.290000000000175e-11 \ No newline at end of file +// yielding error 5.290000000000175e-11 diff --git a/source/source_estate/module_charge/chg_rhog_io.h b/source/source_estate/module_charge/chg_rhog_io.h index 3f3f2d2edad..e6c2220eb6b 100644 --- a/source/source_estate/module_charge/chg_rhog_io.h +++ b/source/source_estate/module_charge/chg_rhog_io.h @@ -1,18 +1,20 @@ #ifndef CHG_RHOG_IO_H #define CHG_RHOG_IO_H -#include +#include "source_base/module_parallel/para_world.h" +#include "source_basis/module_pw/pw_basis.h" +#include "source_estate/module_charge/charge.h" + #include #include -#include "source_basis/module_pw/pw_basis.h" -#include "source_base/module_parallel/para_world.h" +#include /** * I/O free function of rho(G) in binary format * Author: YuLiu98, Kirk0830 - * + * * The designed foramt of the binary file is kept similar to the one in QuantumESPRESSO * Modules/io_base.f90, function write_rhog (non-HDF5): - * + * * /3/ /gammaonly/ /ngm_g/ /nspin/ /3/ ! bool, int, int. ngm_g is the global, total number of G-vectors * /9/ /b11/ /b12/ /b13/ /b21/ /b22/ /b23/ /b31/ /b32/ /b33/ /9/ ! 9 real numbers, the three lattice vectors * /ngm_g*3/ @@ -27,24 +29,43 @@ * !/ngm_g/ ! ngm_g complex numbers, the rho(G) values for the second spin component * !/rhog(1)/ /rhog(2)/ /rhog(3)/ ... * !/ngm_g/ ! ngm_g complex numbers, the rho(G) values for the second spin component - * + * * There are some aspects needed to ensure: * 1. the correspondence between ABACUS PW and QuantumESPRESSO * ABACUS PW QuantumESPRESSO * bij UnitCell::GT b1, b2, b3 * miller index ibox[*] mill * rho Charge::rhog rho - * + * * 2. the unit of each quantity * ABACUS PW QuantumESPRESSO * bij a.u. * miller index - * rho + * rho */ namespace module_charge { +/** + * @brief Write saved charge and optional kinetic-energy densities in reciprocal space. + * + * All pools participate in their density transforms; only the designated writer + * pool writes files. The caller selects the output time and supplies saved + * densities in rho_save and kin_r_save. rhog_save is updated by the transform. + */ +void write_density_restart(Charge& charge, + ModulePW::PW_Basis& pw_rhod, + const ModuleBase::Matrix3& reciprocal_lattice, + const std::string& prefix, + const int nspin, + const bool enabled, + const bool gamma_only, + const bool write_tau, + const bool writer_pool, + const Parallel::ParaWorld& pw_world, + std::ostream* warning); + bool read_rhog(const std::string& filename, const ModulePW::PW_Basis* pw_rhod, const int nspin, From ddd98a0dcb4831f4f116c053d134c23d10d16b84 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 18:11:49 +0800 Subject: [PATCH 14/33] Refactor PW RT-TDDFT solver by extracting state handling, diagnostics, and output control with consistent naming --- source/Makefile.Objects | 4 + source/source_esolver/esolver_ks_pw_tddft.cpp | 425 +++++------------- source/source_esolver/esolver_ks_pw_tddft.h | 21 +- source/source_io/CMakeLists.txt | 2 + .../module_ctrl/ctrl_output_td_pw.cpp | 38 ++ .../source_io/module_ctrl/ctrl_output_td_pw.h | 24 + .../module_current/td_current_pw.cpp | 11 +- .../source_io/module_current/td_current_pw.h | 2 +- source/source_io/module_energy/td_eij_io.cpp | 120 +++++ source/source_io/module_energy/td_eij_io.h | 16 + .../source_io/module_output/td_conserv_io.cpp | 51 +++ .../source_io/module_output/td_conserv_io.h | 20 + source/source_pw/module_rt/CMakeLists.txt | 2 + source/source_pw/module_rt/td_conserv_pw.cpp | 46 ++ source/source_pw/module_rt/td_conserv_pw.h | 26 ++ source/source_pw/module_rt/td_endpoint_pw.cpp | 192 ++++++++ source/source_pw/module_rt/td_endpoint_pw.h | 41 ++ source/source_pw/module_rt/td_history_pw.cpp | 27 ++ source/source_pw/module_rt/td_history_pw.h | 17 + 19 files changed, 750 insertions(+), 335 deletions(-) create mode 100644 source/source_io/module_ctrl/ctrl_output_td_pw.cpp create mode 100644 source/source_io/module_ctrl/ctrl_output_td_pw.h create mode 100644 source/source_io/module_output/td_conserv_io.cpp create mode 100644 source/source_io/module_output/td_conserv_io.h create mode 100644 source/source_pw/module_rt/td_conserv_pw.cpp create mode 100644 source/source_pw/module_rt/td_conserv_pw.h create mode 100644 source/source_pw/module_rt/td_endpoint_pw.cpp create mode 100644 source/source_pw/module_rt/td_endpoint_pw.h diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 3b77bd677c3..a84657a880d 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -388,6 +388,8 @@ OBJS_HAMILT=hamilt_pw.o\ prop_cn_pw.o\ prop_etrs_pw.o\ td_history_pw.o\ + td_conserv_pw.o\ + td_endpoint_pw.o\ td_vel_pw.o\ td_spin_pw.o\ td_pw.o\ @@ -715,6 +717,8 @@ OBJS_IO=module_parameter/input_conv.o\ module_dos/write_dos_pw.o\ module_energy/write_bands.o\ module_energy/td_eij_io.o\ + module_ctrl/ctrl_output_td_pw.o\ + module_output/td_conserv_io.o\ module_dos/cal_dos.o\ module_dos/cal_pdos_gamma.o\ module_dos/cal_pdos_multik.o\ diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 14bc6e78a41..60b8393b3b1 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -4,9 +4,6 @@ #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/module_parallel/para_bridge.h" -#include "source_base/parallel_comm.h" -#include "source_base/parallel_device.h" -#include "source_base/parallel_reduce.h" #include "source_base/timer.h" #include "source_estate/elecstate_pw.h" #include "source_estate/elecstate_tools.h" @@ -15,14 +12,15 @@ #include "source_hamilt/hamilt_hs_adapter.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_ctrl/ctrl_output_pw.h" -#include "source_io/module_current/td_current_pw.h" -#include "source_io/module_current/td_spin_pw_io.h" +#include "source_io/module_ctrl/ctrl_output_td_pw.h" #include "source_io/module_efield/td_efield_io.h" #include "source_io/module_efield/td_vecpot_io.h" #include "source_io/module_energy/td_eij_io.h" #include "source_io/module_output/output_log.h" +#include "source_io/module_output/td_conserv_io.h" #include "source_io/module_parameter/parameter.h" #include "source_pw/module_pwdft/hamilt_pw.h" +#include "source_pw/module_rt/td_endpoint_pw.h" #include "source_pw/module_rt/td_pw.h" #include @@ -141,7 +139,7 @@ void ESolver_KS_PW_TDDFT::runner(BaseCell& basecell, const int istep) { basecell.require_kind(BaseCell::Kind::unitcell, __FUNCTION__); UnitCell& cell = static_cast(basecell); - this->run_propagation(cell, electronic_step); + this->run_prop(cell, electronic_step); } else { @@ -162,60 +160,23 @@ int ESolver_KS_PW_TDDFT::scf_iter_limit(const int istep) const return istep > 0 ? this->inp_->td_scf_nmax : this->inp_->scf_nmax; } -template -typename ESolver_KS_PW_TDDFT::AcceptedState ESolver_KS_PW_TDDFT::capture_state() const -{ - AcceptedState state; - state.energy = this->pelec->f_en; - state.eigenvalues = this->pelec->ekb; - - state.density.resize(this->chr.nspin * this->chr.nrxx); - for (int spin = 0; spin < this->chr.nspin; ++spin) - { - std::copy(this->chr.rho[spin], this->chr.rho[spin] + this->chr.nrxx, state.density.begin() + spin * this->chr.nrxx); - } - - return state; -} - -template -void ESolver_KS_PW_TDDFT::restore_state(const AcceptedState& state) -{ - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "restore_state"); - - *this->stp.template get_psi_t() = this->history_.previous(); - this->pelec->f_en = state.energy; - this->pelec->ekb = state.eigenvalues; - this->pelec->vnew_exist = false; - - for (int spin = 0; spin < this->chr.nspin; ++spin) - { - const double* density = state.density.data() + spin * this->chr.nrxx; - std::copy(density, density + this->chr.nrxx, this->chr.rho[spin]); - } - this->chr.save_rho_before_sum_band(); - - // The caller binds the trial field and rebuilds the endpoint potential. The saved - // left-endpoint potential used by the corrector is already held in history_. - this->td_prop_->invalidate_basis(); - this->td_prop_->orbitals().reset_orth_stats(); - - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "restore_state"); -} - template bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, int step, int index, int depth, const elecstate::TDFieldState& full, - AcceptedState& accepted, + pw::TDElecSnap& accepted, hsolver::TDOrthStats& stats) { const int parts = 1 << depth; if (depth > 0) { - this->restore_state(accepted); + *this->stp.template get_psi_t() = this->history_.previous(); + pw::restore_td(accepted, this->pelec->f_en, this->pelec->ekb, this->chr); + this->pelec->vnew_exist = false; + this->td_prop_->invalidate_basis(); + this->td_prop_->orbitals().reset_orth_stats(); const elecstate::TDFieldState trial = this->td_field_manager_->subinterval(full, index, parts); this->td_field_manager_->bind_field(trial); @@ -263,7 +224,24 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, if (!converged) { - this->finalize_endpoint(cell); + auto& current = *this->stp.template get_psi_t(); + auto& estate = *static_cast*>(this->pelec); + auto& hamilt = *static_cast*>(this->p_hamilt); + const auto& vecpot = this->td_field_manager_->A_right_ha(); + pw::rebuild_td_end(current, + estate, + this->chr, + cell, + hamilt, + this->td_prop_->orbitals(), + *this->pw_wfc, + *this->pw_rhod, + vecpot, + this->inp_->nspin, + this->inp_->imp_sol, + this->inp_->sc_mag_switch, + this->inp_->dft_plus_u, + this->inp_->assume_isolated); } const hsolver::TDOrthStats& trial_stats = this->td_prop_->orbitals().orth_stats(); @@ -275,22 +253,22 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, this->td_prop_->finish_trial(); if (this->inp_->td_max_halvings > 0) { - accepted = this->capture_state(); + accepted = pw::capture_td(this->pelec->f_en, this->pelec->ekb, this->chr); } return converged; } template -void ESolver_KS_PW_TDDFT::run_propagation(UnitCell& cell, int step) +void ESolver_KS_PW_TDDFT::run_prop(UnitCell& cell, int step) { - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_propagation"); + ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "run_prop"); const ModuleBase::TimePoint started = ModuleBase::get_time(); - AcceptedState accepted; + pw::TDElecSnap accepted; if (this->inp_->td_max_halvings > 0) { - accepted = this->capture_state(); + accepted = pw::capture_td(this->pelec->f_en, this->pelec->ekb, this->chr); } this->before_scf(cell, step); @@ -334,7 +312,7 @@ void ESolver_KS_PW_TDDFT::run_propagation(UnitCell& cell, int step) this->after_scf(cell, step, converged); this->conv_esolver = converged; - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_propagation"); + ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "run_prop"); } template @@ -476,36 +454,22 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in { ESolver_KS_PW::hamilt2rho_single(ucell, istep, iter, ethr); - psi::Psi* current = this->stp.template get_psi_t(); - // Padding is a storage invariant even with orthogonalization disabled; clearing it does not change the density. - this->td_prop_->orbitals().zero_padding(current); - - if (this->inp_->td_orthonormal != "none") - { - int changed = this->td_prop_->orbitals().orth_initial(current, iter); - // Occupations and density construction contain collectives across k-point pools. - Parallel_Reduce::reduce_all(changed); - if (changed > 0) - { - hamilt::Hamilt* hamiltonian = static_cast*>(this->p_hamilt); - hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); - - elecstate::calculate_weights(this->pelec->ekb, - this->pelec->wg, - this->pelec->klist, - this->pelec->eferm, - this->pelec->f_en, - this->pelec->nelec_spin, - this->inp_->nbands, - this->pelec->skip_weights); - elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); - - elecstate::ElecStatePW* estate = static_cast*>(this->pelec); - estate->psiToRho(*current); - module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, ucell.symm); - } - } + auto& current = *this->stp.template get_psi_t(); + auto& estate = *static_cast*>(this->pelec); + auto* hamilt = static_cast*>(this->p_hamilt); + hamilt::HamiltHSOperator op(hamilt, this->pw_wfc); + const bool do_orth = this->inp_->td_orthonormal != "none"; + pw::orth_init_pw(this->td_prop_->orbitals(), + op, + current, + estate, + this->chr, + ucell, + *this->pw_rhod, + this->inp_->nspin, + this->inp_->nbands, + iter, + do_orth); ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "hamilt2rho_single"); return; @@ -543,7 +507,24 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, } if (!conv_esolver && istep == 0) { - finalize_endpoint(ucell); + auto& current = *this->stp.template get_psi_t(); + auto& estate = *static_cast*>(this->pelec); + auto& hamilt = *static_cast*>(this->p_hamilt); + const auto& vecpot = this->td_field_manager_->A_right_ha(); + pw::rebuild_td_end(current, + estate, + this->chr, + ucell, + hamilt, + this->td_prop_->orbitals(), + *this->pw_wfc, + *this->pw_rhod, + vecpot, + this->inp_->nspin, + this->inp_->imp_sol, + this->inp_->sc_mag_switch, + this->inp_->dft_plus_u, + this->inp_->assume_isolated); } // Endpoint diagnostics use the accepted endpoint, not the propagation midpoint or a mixed trial density. @@ -552,7 +533,17 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, endpoint_hamilt->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); if (this->inp_->td_print_eij >= 0.0) { - print_eij(istep); + auto& current = *this->stp.template get_psi_t(); + hamilt::HamiltHSOperator op(endpoint_hamilt, this->pw_wfc); + ModuleIO::print_eij_pw(op, + this->td_prop_->orbitals(), + current, + this->kv, + this->inp_->nspin, + istep, + this->niter, + this->inp_->td_print_eij, + log_); } ESolver_KS_PW::after_scf(ucell, istep, conv_esolver); @@ -570,240 +561,52 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, this->history_.save(*current, *this->pelec->pot); } - sample_endpoint(ucell, istep); - } - - if (this->inp_->td_out_stat) - { - report_orth(ucell, istep); - } -} - -template -void ESolver_KS_PW_TDDFT::finalize_endpoint(UnitCell& cell) -{ - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "finalize_endpoint"); - - psi::Psi* current = this->stp.template get_psi_t(); - auto* estate = static_cast*>(this->pelec); - // The last SCF iteration may have mixed the density. Keep the actual orbitals and their occupations. - estate->psiToRho(*current); - module_charge::symmetrize_rho(this->inp_->nspin, this->chr, this->pw_rhod, cell.symm); - this->chr.save_rho_before_sum_band(); - this->pelec->pot->update_from_charge(&this->chr, &cell); - - auto* hamiltonian = static_cast*>(this->p_hamilt); - const Real* veff = this->pelec->pot->template get_veff_smooth_data(); - hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); - - hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - this->td_prop_->orbitals().cal_band_energy(op, *current, &this->pelec->ekb); - elecstate::calEBand(this->pelec->ekb, this->pelec->wg, this->pelec->f_en); - this->pelec->f_en.deband = this->pelec->cal_delta_eband(cell); - this->pelec->f_en.descf = 0.0; - - // Do not reuse a self-consistency force correction from an earlier endpoint. - this->pelec->vnew_exist = false; - this->pelec->cal_energies(2, this->inp_->imp_sol, this->inp_->sc_mag_switch, this->inp_->dft_plus_u, this->inp_->assume_isolated); + const auto& current = *this->stp.template get_psi_t(); + const auto& vecpot = this->td_field_manager_->A_right_ha(); + const pw::TDVelResultPW currents = vel_.calculate(current, *this->pelec, vecpot, vel_request_); - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "finalize_endpoint"); -} - -template -void ESolver_KS_PW_TDDFT::sample_endpoint(UnitCell& cell, int step) -{ - psi::Psi* current = this->stp.template get_psi_t(); - const ModuleBase::Vector3& a = this->td_field_manager_->A_right_ha(); - - // All endpoint currents share one velocity action; disabled requests perform no collectives. - const pw::TDVelResultPW currents = vel_.calculate(*current, *this->pelec, a, vel_request_); - - pw::TDSpinResultPW moments; - if (this->inp_->nspin == 4 && (spin_options_.method != "none" || spin_options_.torque)) - { - moments = spin_.calculate(*current, *this->pelec, a); - } - - const std::string& directory = PARAM.globalv.global_out_dir; - const int rank = GlobalV::MY_RANK; - if (rank == 0) - { - if (this->inp_->out_current == 1) + pw::TDSpinResultPW moments; + if (this->inp_->nspin == 4 && (spin_options_.method != "none" || spin_options_.torque)) { - const int spin_mult = this->inp_->nspin == 2 ? 2 : 1; - const int nk = this->kv.get_nkstot() / spin_mult; - ModuleIO::write_pw_current(currents, step, nk, this->inp_->out_current_k, directory); + moments = spin_.calculate(current, *this->pelec, vecpot); } - if (this->inp_->nspin == 4) - { - const double time = step * this->td_field_manager_->dt_ha() * ModuleBase::AU_to_FS; - ModuleIO::write_td_spin_pw(directory, - step, - time, - cell.orbital_fn, - spin_options_, - vel_request_.spin_current, - moments, - currents, - log_); - } - } -} -template -void ESolver_KS_PW_TDDFT::print_eij(const int istep) -{ - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "print_eij"); - - psi::Psi* current = this->stp.template get_psi_t(); - const int original_k = current->get_current_k(); - const int nband = current->get_nbands(); - const size_t elements = static_cast(nband) * nband; - const int nspin = this->inp_->nspin; - const int nk = this->kv.get_nkstot() / (nspin == 2 ? 2 : 1); - const Parallel_Kpoints& pools = this->kv.para_k; - const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; - - auto* hamiltonian = static_cast*>(this->p_hamilt); - const Real* veff = this->pelec->pot->template get_veff_smooth_data(); - // cal_converged() updates the potential from the final density, after the last band-energy evaluation. - hamiltonian->bind_td_state(veff, this->td_field_manager_->A_right_ha()); - hamilt::HamiltHSOperator op(hamiltonian, this->pw_wfc); - - for (int global_k = 0; global_k < this->kv.get_nkstot(); ++global_k) - { - const int physical_k = global_k % nk; - const int spin = global_k / nk; - int owner = 0; - int local_k = global_k; -#ifdef __MPI - owner = pools.whichpool[physical_k]; - local_k = physical_k - pools.startk_pool[owner] + spin * pools.nks_pool[owner]; -#endif - - std::vector> eij; - if (pools.my_pool == owner) - { - eij = this->td_prop_->orbitals().cal_eij(op, *current, local_k); - } - -#ifdef __MPI - // Each pool already has the full matrix; transfer only one copy to the main log. - if (owner != 0 && pools.rank_in_pool == 0 && (main_rank || pools.my_pool == owner)) - { - if (main_rank) - { - eij.resize(elements); - } - MPI_Comm world = MPI_COMM_WORLD; - const int source = pools.get_startpro_pool(owner); - const size_t max_count = std::numeric_limits::max(); - for (size_t offset = 0; offset < elements; offset += max_count) - { - const int count = static_cast(std::min(max_count, elements - offset)); - if (main_rank) - { - Parallel_Common::recv_data(eij.data() + offset, count, source, 0, world, MPI_STATUS_IGNORE); - } - else - { - Parallel_Common::send_data(eij.data() + offset, count, 0, 0, world); - } - } - } -#endif - - if (main_rank) - { - const ModuleIO::EijOutputInfo info{this->inp_->td_print_eij, istep + 1, this->niter, physical_k + 1, nspin, spin + 1}; - ModuleIO::print_eij(eij.data(), nband, nband, info, log_); - } + const double dt_ha = this->td_field_manager_->dt_ha(); + const int nkstot = this->kv.get_nkstot(); + ModuleIO::ctrl_output_td_pw(currents, + moments, + vel_request_, + spin_options_, + ucell.orbital_fn, + istep, + dt_ha, + this->inp_->nspin, + nkstot, + GlobalV::MY_RANK, + PARAM.globalv.global_out_dir, + log_); } - current->fix_k(original_k); - op.update_k(original_k); - - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "print_eij"); -} - -template -void ESolver_KS_PW_TDDFT::report_orth(const UnitCell& ucell, int istep) -{ - ModuleBase::timer::start("ESolver_KS_PW_TDDFT", "report_orth"); - - double electrons = this->td_prop_->orbitals().wave_electrons(this->pelec->wg); - // Norms are replicated within a pool; only its root contributes to the total. - if (this->pw_wfc->poolrank != 0) - { - electrons = 0.0; - } - Parallel_Reduce::reduce_all(electrons); - - double rho_electrons = 0.0; - const int density_spins = this->chr.nspin == 2 ? 2 : 1; - for (int spin = 0; spin < density_spins; ++spin) - { - for (int ir = 0; ir < this->chr.nrxx; ++ir) - { - rho_electrons += this->chr.rho[spin][ir]; - } - } - rho_electrons *= ucell.omega / this->chr.rhopw->nxyz; - Parallel_Reduce::reduce_pool(rho_electrons); - - if (istep == 0) - { - initial_wave_electrons_ = electrons; - initial_rho_electrons_ = rho_electrons; - } - - const hsolver::TDOrthStats& stats = this->td_prop_->orbitals().orth_stats(); - const bool full_gram = this->inp_->td_orthonormal != "none"; - double maxima[] = {stats.before, stats.after}; - if (full_gram) - { - Parallel_Reduce::reduce_max(maxima, 2); - } - - if (!log_.good()) - { - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "report_orth"); - return; - } - - const char* stage = istep == 0 ? "initial state" : "propagation"; - const int evolution_step = istep + 1; - const double delta_wave = electrons - initial_wave_electrons_; - const double delta_rho = rho_electrons - initial_rho_electrons_; - - const std::ios::fmtflags saved_flags = log_.flags(); - const std::streamsize saved_precision = log_.precision(); - log_ << " PW RT-TDDFT conservation: evolution step " << evolution_step << ", " << stage << ", " << this->inp_->td_orthonormal << '\n'; - log_ << std::right << std::fixed << std::setprecision(10); - ModuleBase::GlobalFunc::OUT(log_, "Npsi", electrons); - ModuleBase::GlobalFunc::OUT(log_, "Nrho", rho_electrons); - - log_ << std::scientific << std::setprecision(6); - ModuleBase::GlobalFunc::OUT(log_, "dNpsi", delta_wave); - ModuleBase::GlobalFunc::OUT(log_, "dNrho", delta_rho); - - if (full_gram) + if (this->inp_->td_out_stat) { + const double local_npsi = this->td_prop_->orbitals().wave_electrons(this->pelec->wg); + const auto& stats = this->td_prop_->orbitals().orth_stats(); + const bool full_gram = this->inp_->td_orthonormal != "none"; + const pw::TDConserv values + = pw::cal_td_conserv(local_npsi, this->chr, ucell.omega, this->pw_wfc->poolrank, stats.before, stats.after, full_gram); if (istep == 0) { - ModuleBase::GlobalFunc::OUT(log_, "initial_orth_error", maxima[1]); - } - else - { - ModuleBase::GlobalFunc::OUT(log_, "orth_before_step_max", maxima[0]); - ModuleBase::GlobalFunc::OUT(log_, "orth_after_step_max", maxima[1]); + initial_wave_electrons_ = values.npsi; + initial_rho_electrons_ = values.nrho; } + ModuleIO::print_conserv(values, + initial_wave_electrons_, + initial_rho_electrons_, + istep, + full_gram, + this->inp_->td_orthonormal, + log_); } - - log_.flags(saved_flags); - log_.precision(saved_precision); - - ModuleBase::timer::end("ESolver_KS_PW_TDDFT", "report_orth"); } template class ESolver_KS_PW_TDDFT, base_device::DEVICE_CPU>; diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index 69824b80faf..a4f6b3b28d1 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -10,7 +10,6 @@ #include #include -#include namespace ModuleESolver { @@ -66,33 +65,19 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW pw::TDHistoryPW history_; double initial_wave_electrons_ = 0.0; double initial_rho_electrons_ = 0.0; - /** @brief Small endpoint data; the accepted wavefunctions remain owned by history_. */ - struct AcceptedState - { - elecstate::fenergy energy; - ModuleBase::matrix eigenvalues; - std::vector density; - }; - AcceptedState capture_state() const; - void restore_state(const AcceptedState& state); - void run_propagation(UnitCell& cell, int step); + + void run_prop(UnitCell& cell, int step); bool advance_interval(UnitCell& cell, int step, int index, int depth, const elecstate::TDFieldState& full, - AcceptedState& accepted, + pw::TDElecSnap& accepted, hsolver::TDOrthStats& stats); void output_field(int step) const; /** @brief Print the one-based electronic step header. */ void print_step(const int istep) const; - /** @brief Rebuild an iteration-limited endpoint from its final orbitals without changing occupations. */ - void finalize_endpoint(UnitCell& cell); - /** @brief Print Eij from the accepted endpoint, collecting all k-point pools on the main rank. */ - void print_eij(const int istep); - void report_orth(const UnitCell& ucell, int istep); void prep_td_step(const int istep); - void sample_endpoint(UnitCell& cell, int step); }; } // namespace ModuleESolver diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 99c4c5876ac..fc92828fb26 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND objects module_ctrl/ctrl_output_fp.cpp module_ctrl/ctrl_output_pw.cpp module_ctrl/ctrl_output_td.cpp + module_ctrl/ctrl_output_td_pw.cpp module_bessel/bessel_basis.cpp module_output/cal_test.cpp module_dos/cal_dos.cpp @@ -39,6 +40,7 @@ list(APPEND objects module_efield/td_vecpot_io.cpp module_chgpot/write_libxc_r.cpp module_output/output_log.cpp + module_output/td_conserv_io.cpp module_json/para_json.cpp parse_args.cpp input_help.cpp diff --git a/source/source_io/module_ctrl/ctrl_output_td_pw.cpp b/source/source_io/module_ctrl/ctrl_output_td_pw.cpp new file mode 100644 index 00000000000..00f34c12c41 --- /dev/null +++ b/source/source_io/module_ctrl/ctrl_output_td_pw.cpp @@ -0,0 +1,38 @@ +#include "source_io/module_ctrl/ctrl_output_td_pw.h" + +#include "source_base/constants.h" +#include "source_io/module_current/td_current_pw.h" +#include "source_io/module_current/td_spin_pw_io.h" + +namespace ModuleIO +{ +void ctrl_output_td_pw(const pw::TDVelResultPW& currents, + const pw::TDSpinResultPW& moments, + const pw::TDVelRequestPW& request, + const pw::TDSpinOptsPW& spin_opts, + const std::vector& orbital_fn, + int step, + double dt_ha, + int nspin, + int nkstot, + int rank, + const std::string& directory, + std::ostream& log) +{ + if (rank != 0) + { + return; + } + if (request.charge_total) + { + const int spin_mult = nspin == 2 ? 2 : 1; + const int nk = nkstot / spin_mult; + write_current_pw(currents, step, nk, request.charge_per_k, directory); + } + if (nspin == 4) + { + const double time = step * dt_ha * ModuleBase::AU_to_FS; + write_td_spin_pw(directory, step, time, orbital_fn, spin_opts, request.spin_current, moments, currents, log); + } +} +} // namespace ModuleIO diff --git a/source/source_io/module_ctrl/ctrl_output_td_pw.h b/source/source_io/module_ctrl/ctrl_output_td_pw.h new file mode 100644 index 00000000000..b0f930a0c6c --- /dev/null +++ b/source/source_io/module_ctrl/ctrl_output_td_pw.h @@ -0,0 +1,24 @@ +#ifndef CTRL_OUTPUT_TD_PW_H +#define CTRL_OUTPUT_TD_PW_H + +#include "source_pw/module_rt/td_obs_pw.h" + +#include + +namespace ModuleIO +{ +/** @brief Write precomputed endpoint observables on the designated output rank. */ +void ctrl_output_td_pw(const pw::TDVelResultPW& currents, + const pw::TDSpinResultPW& moments, + const pw::TDVelRequestPW& request, + const pw::TDSpinOptsPW& spin_opts, + const std::vector& orbital_fn, + int step, + double dt_ha, + int nspin, + int nkstot, + int rank, + const std::string& directory, + std::ostream& log); +} // namespace ModuleIO +#endif diff --git a/source/source_io/module_current/td_current_pw.cpp b/source/source_io/module_current/td_current_pw.cpp index 8facc7c1d02..2648d6dcc0a 100644 --- a/source/source_io/module_current/td_current_pw.cpp +++ b/source/source_io/module_current/td_current_pw.cpp @@ -1,4 +1,5 @@ #include "source_io/module_current/td_current_pw.h" + #include "source_base/tool_quit.h" #include @@ -8,7 +9,7 @@ namespace ModuleIO { -void write_pw_current(const pw::TDVelResultPW& current, +void write_current_pw(const pw::TDVelResultPW& current, const int istep, const int nk_per_spin, const bool out_current_k, @@ -22,7 +23,7 @@ void write_pw_current(const pw::TDVelResultPW& current, fout_tot.open(filename_tot, mode); if (!fout_tot) { - ModuleBase::WARNING_QUIT("write_pw_current", "Cannot open " + filename_tot); + ModuleBase::WARNING_QUIT("write_current_pw", "Cannot open " + filename_tot); } fout_tot << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); for (double value: current.total) @@ -33,7 +34,7 @@ void write_pw_current(const pw::TDVelResultPW& current, fout_tot.close(); if (!fout_tot) { - ModuleBase::WARNING_QUIT("write_pw_current", "Failed to write " + filename_tot); + ModuleBase::WARNING_QUIT("write_current_pw", "Failed to write " + filename_tot); } // Optionally write the contribution from each k point. @@ -50,7 +51,7 @@ void write_pw_current(const pw::TDVelResultPW& current, fout_k.open(filename_k, mode); if (!fout_k) { - ModuleBase::WARNING_QUIT("write_pw_current", "Cannot open " + filename_k); + ModuleBase::WARNING_QUIT("write_current_pw", "Cannot open " + filename_k); } const std::int64_t offset = static_cast(ik) * 3; fout_k << std::right << std::setw(6) << istep + 1 << std::scientific << std::setprecision(12); @@ -62,7 +63,7 @@ void write_pw_current(const pw::TDVelResultPW& current, fout_k.close(); if (!fout_k) { - ModuleBase::WARNING_QUIT("write_pw_current", "Failed to write " + filename_k); + ModuleBase::WARNING_QUIT("write_current_pw", "Failed to write " + filename_k); } } } diff --git a/source/source_io/module_current/td_current_pw.h b/source/source_io/module_current/td_current_pw.h index e6295280ca7..e97fe1045ff 100644 --- a/source/source_io/module_current/td_current_pw.h +++ b/source/source_io/module_current/td_current_pw.h @@ -8,6 +8,6 @@ namespace ModuleIO { /** @brief Write reduced charge currents on the output rank; performs no collectives. */ -void write_pw_current(const pw::TDVelResultPW& current, int istep, int nk_per_spin, bool out_current_k, const std::string& out_dir); +void write_current_pw(const pw::TDVelResultPW& current, int istep, int nk_per_spin, bool out_current_k, const std::string& out_dir); } // namespace ModuleIO #endif diff --git a/source/source_io/module_energy/td_eij_io.cpp b/source/source_io/module_energy/td_eij_io.cpp index 77c7377f7b4..b6f14031c42 100644 --- a/source/source_io/module_energy/td_eij_io.cpp +++ b/source/source_io/module_energy/td_eij_io.cpp @@ -1,7 +1,12 @@ #include "source_io/module_energy/td_eij_io.h" +#include "source_base/parallel_device.h" +#include "source_base/timer.h" + +#include #include #include +#include namespace ModuleIO { @@ -51,4 +56,119 @@ void print_eij(const std::complex* eij, ofs.flags(original_flags); ofs.precision(original_precision); } + +template +void print_eij_pw(hsolver::HSOperator& op, + hsolver::TDOrbOps& orbitals, + psi::Psi& current, + const K_Vectors& kv, + int nspin, + int step, + int iter, + double threshold, + std::ostream& log) +{ + ModuleBase::timer::start("ModuleIO", "print_eij_pw"); + + const int original_k = current.get_current_k(); + const int nband = current.get_nbands(); + const size_t elements = static_cast(nband) * nband; + const int nk = kv.get_nkstot() / (nspin == 2 ? 2 : 1); + const Parallel_Kpoints& pools = kv.para_k; + const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; + + for (int global_k = 0; global_k < kv.get_nkstot(); ++global_k) + { + const int physical_k = global_k % nk; + const int spin = global_k / nk; + int owner = 0; + int local_k = global_k; +#ifdef __MPI + owner = pools.whichpool[physical_k]; + local_k = physical_k - pools.startk_pool[owner] + spin * pools.nks_pool[owner]; +#endif + + std::vector> eij; + if (pools.my_pool == owner) + { + eij = orbitals.cal_eij(op, current, local_k); + } + +#ifdef __MPI + // Each pool already has the full matrix; transfer only one copy to the main log. + if (owner != 0 && pools.rank_in_pool == 0 && (main_rank || pools.my_pool == owner)) + { + if (main_rank) + { + eij.resize(elements); + } + MPI_Comm world = MPI_COMM_WORLD; + const int source = pools.get_startpro_pool(owner); + const size_t max_count = std::numeric_limits::max(); + for (size_t offset = 0; offset < elements; offset += max_count) + { + const int count = static_cast(std::min(max_count, elements - offset)); + if (main_rank) + { + Parallel_Common::recv_data(eij.data() + offset, count, source, 0, world, MPI_STATUS_IGNORE); + } + else + { + Parallel_Common::send_data(eij.data() + offset, count, 0, 0, world); + } + } + } +#endif + + if (main_rank) + { + const EijOutputInfo output{threshold, step + 1, iter, physical_k + 1, nspin, spin + 1}; + print_eij(eij.data(), nband, nband, output, log); + } + } + + current.fix_k(original_k); + op.update_k(original_k); + + ModuleBase::timer::end("ModuleIO", "print_eij_pw"); +} + +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +#if defined(__CUDA) || defined(__ROCM) +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +#endif } // namespace ModuleIO diff --git a/source/source_io/module_energy/td_eij_io.h b/source/source_io/module_energy/td_eij_io.h index eb8944e9306..a713249fecd 100644 --- a/source/source_io/module_energy/td_eij_io.h +++ b/source/source_io/module_energy/td_eij_io.h @@ -1,6 +1,9 @@ #ifndef MODULE_IO_TD_EIJ_IO_H #define MODULE_IO_TD_EIJ_IO_H +#include "source_cell/klist.h" +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" + #include #include @@ -23,5 +26,18 @@ void print_eij(const std::complex* eij, const int leading_dimension, const EijOutputInfo& output_info, std::ostream& ofs); +/** @brief Collect and print Eij from an already bound endpoint Hamiltonian. + * All ranks participate; both k-point cursors are restored before returning. + */ +template +void print_eij_pw(hsolver::HSOperator& op, + hsolver::TDOrbOps& orbitals, + psi::Psi& current, + const K_Vectors& kv, + int nspin, + int step, + int iter, + double threshold, + std::ostream& log); } // namespace ModuleIO #endif diff --git a/source/source_io/module_output/td_conserv_io.cpp b/source/source_io/module_output/td_conserv_io.cpp new file mode 100644 index 00000000000..0e896aca95b --- /dev/null +++ b/source/source_io/module_output/td_conserv_io.cpp @@ -0,0 +1,51 @@ +#include "source_io/module_output/td_conserv_io.h" + +#include "source_base/global_function.h" + +#include + +namespace ModuleIO +{ +void print_conserv(const pw::TDConserv& values, + double npsi0, + double nrho0, + int step, + bool full_gram, + const std::string& orth, + std::ofstream& log) +{ + if (!log.good()) + { + return; + } + + const char* stage = step == 0 ? "initial state" : "propagation"; + const int evolve_step = step + 1; + const double delta_wave = values.npsi - npsi0; + const double delta_rho = values.nrho - nrho0; + const std::ios::fmtflags saved_flags = log.flags(); + const std::streamsize saved_prec = log.precision(); + log << " PW RT-TDDFT conservation: evolution step " << evolve_step << ", " << stage << ", " << orth << '\n'; + log << std::right << std::fixed << std::setprecision(10); + ModuleBase::GlobalFunc::OUT(log, "Npsi", values.npsi); + ModuleBase::GlobalFunc::OUT(log, "Nrho", values.nrho); + + log << std::scientific << std::setprecision(6); + ModuleBase::GlobalFunc::OUT(log, "dNpsi", delta_wave); + ModuleBase::GlobalFunc::OUT(log, "dNrho", delta_rho); + if (full_gram) + { + if (step == 0) + { + ModuleBase::GlobalFunc::OUT(log, "initial_orth_error", values.orth_after); + } + else + { + ModuleBase::GlobalFunc::OUT(log, "orth_before_step_max", values.orth_before); + ModuleBase::GlobalFunc::OUT(log, "orth_after_step_max", values.orth_after); + } + } + log.flags(saved_flags); + log.precision(saved_prec); +} +} // namespace ModuleIO diff --git a/source/source_io/module_output/td_conserv_io.h b/source/source_io/module_output/td_conserv_io.h new file mode 100644 index 00000000000..0f40e687506 --- /dev/null +++ b/source/source_io/module_output/td_conserv_io.h @@ -0,0 +1,20 @@ +#ifndef TD_CONSERV_IO_H +#define TD_CONSERV_IO_H + +#include "source_pw/module_rt/td_conserv_pw.h" + +#include +#include + +namespace ModuleIO +{ +/** @brief Print endpoint conservation relative to the caller's initial counts. */ +void print_conserv(const pw::TDConserv& values, + double npsi0, + double nrho0, + int step, + bool full_gram, + const std::string& orth, + std::ofstream& log); +} // namespace ModuleIO +#endif diff --git a/source/source_pw/module_rt/CMakeLists.txt b/source/source_pw/module_rt/CMakeLists.txt index f95a53bbd18..740c2673b60 100644 --- a/source/source_pw/module_rt/CMakeLists.txt +++ b/source/source_pw/module_rt/CMakeLists.txt @@ -4,6 +4,8 @@ add_library(module_rt_pw OBJECT prop_cn_pw.cpp prop_etrs_pw.cpp td_history_pw.cpp + td_conserv_pw.cpp + td_endpoint_pw.cpp td_pw.cpp td_vel_pw.cpp td_spin_pw.cpp diff --git a/source/source_pw/module_rt/td_conserv_pw.cpp b/source/source_pw/module_rt/td_conserv_pw.cpp new file mode 100644 index 00000000000..e3a06794e88 --- /dev/null +++ b/source/source_pw/module_rt/td_conserv_pw.cpp @@ -0,0 +1,46 @@ +#include "source_pw/module_rt/td_conserv_pw.h" + +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" + +namespace pw +{ +TDConserv cal_td_conserv(double local_npsi, + const Charge& charge, + double omega, + int pool_rank, + double orth_before, + double orth_after, + bool full_gram) +{ + ModuleBase::timer::start("pw", "cal_td_conserv"); + TDConserv result; + // Orbital norms are replicated within each k-point pool. + if (pool_rank == 0) + { + result.npsi = local_npsi; + } + Parallel_Reduce::reduce_all(result.npsi); + + const int density_spins = charge.nspin == 2 ? 2 : 1; + for (int spin = 0; spin < density_spins; ++spin) + { + for (int ir = 0; ir < charge.nrxx; ++ir) + { + result.nrho += charge.rho[spin][ir]; + } + } + result.nrho *= omega / charge.rhopw->nxyz; + Parallel_Reduce::reduce_pool(result.nrho); + + double maxima[] = {orth_before, orth_after}; + if (full_gram) + { + Parallel_Reduce::reduce_max(maxima, 2); + } + result.orth_before = maxima[0]; + result.orth_after = maxima[1]; + ModuleBase::timer::end("pw", "cal_td_conserv"); + return result; +} +} // namespace pw diff --git a/source/source_pw/module_rt/td_conserv_pw.h b/source/source_pw/module_rt/td_conserv_pw.h new file mode 100644 index 00000000000..8354e779502 --- /dev/null +++ b/source/source_pw/module_rt/td_conserv_pw.h @@ -0,0 +1,26 @@ +#ifndef TD_CONSERV_PW_H +#define TD_CONSERV_PW_H + +#include "source_estate/module_charge/charge.h" + +namespace pw +{ +/** @brief Globally reduced electron counts and orthogonality errors. */ +struct TDConserv +{ + double npsi = 0.0; + double nrho = 0.0; + double orth_before = 0.0; + double orth_after = 0.0; +}; + +/** @brief Count each k-point pool once and integrate the charge channels. */ +TDConserv cal_td_conserv(double local_npsi, + const Charge& charge, + double omega, + int pool_rank, + double orth_before, + double orth_after, + bool full_gram); +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/td_endpoint_pw.cpp b/source/source_pw/module_rt/td_endpoint_pw.cpp new file mode 100644 index 00000000000..c8008398519 --- /dev/null +++ b/source/source_pw/module_rt/td_endpoint_pw.cpp @@ -0,0 +1,192 @@ +#include "source_pw/module_rt/td_endpoint_pw.h" + +#include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_estate/elecstate_tools.h" +#include "source_estate/module_charge/chg_symm.h" +#include "source_hamilt/hamilt_hs_adapter.h" + +namespace pw +{ +template +void orth_init_pw(hsolver::TDOrbOps& orbitals, + hsolver::HSOperator& op, + psi::Psi& current, + elecstate::ElecStatePW& estate, + Charge& charge, + UnitCell& cell, + const ModulePW::PW_Basis& rhod, + int nspin, + int nbands, + int iter, + bool do_orth) +{ + ModuleBase::timer::start("pw", "orth_init_pw"); + // Padding remains zero even when physical orbital corrections are disabled. + orbitals.zero_padding(¤t); + if (do_orth) + { + int changed = orbitals.orth_initial(¤t, iter); + // Occupations and density construction contain collectives across pools. + Parallel_Reduce::reduce_all(changed); + if (changed > 0) + { + orbitals.cal_band_energy(op, current, &estate.ekb); + elecstate::calculate_weights(estate.ekb, + estate.wg, + estate.klist, + estate.eferm, + estate.f_en, + estate.nelec_spin, + nbands, + estate.skip_weights); + elecstate::calEBand(estate.ekb, estate.wg, estate.f_en); + + estate.psiToRho(current); + module_charge::symmetrize_rho(nspin, charge, &rhod, cell.symm); + } + } + ModuleBase::timer::end("pw", "orth_init_pw"); +} + +template +void rebuild_td_end(psi::Psi& current, + elecstate::ElecStatePW& estate, + Charge& charge, + UnitCell& cell, + hamilt::HamiltPW& hamilt, + hsolver::TDOrbOps& orbitals, + const ModulePW::PW_Basis_K& basis, + const ModulePW::PW_Basis& rhod, + const ModuleBase::Vector3& vecpot, + int nspin, + int imp_sol, + bool sc_mag, + int dft_u, + const std::string& assume_iso) +{ + ModuleBase::timer::start("pw", "rebuild_td_end"); + // Discard a mixed trial density while keeping the final orbitals and occupations. + estate.psiToRho(current); + module_charge::symmetrize_rho(nspin, charge, &rhod, cell.symm); + charge.save_rho_before_sum_band(); + estate.pot->update_from_charge(&charge, &cell); + + using Real = typename GetTypeReal::type; + const Real* veff = estate.pot->template get_veff_smooth_data(); + hamilt.bind_td_state(veff, vecpot); + hamilt::HamiltHSOperator op(&hamilt, &basis); + orbitals.cal_band_energy(op, current, &estate.ekb); + elecstate::calEBand(estate.ekb, estate.wg, estate.f_en); + estate.f_en.deband = estate.cal_delta_eband(cell); + estate.f_en.descf = 0.0; + + // Do not reuse a force correction from an earlier self-consistent endpoint. + estate.vnew_exist = false; + estate.cal_energies(2, imp_sol, sc_mag, dft_u, assume_iso); + ModuleBase::timer::end("pw", "rebuild_td_end"); +} + +template void orth_init_pw(hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + hsolver::HSOperator, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + elecstate::ElecStatePW, base_device::DEVICE_CPU>&, + Charge&, + UnitCell&, + const ModulePW::PW_Basis&, + int, + int, + int, + bool); +template void rebuild_td_end(psi::Psi, base_device::DEVICE_CPU>&, + elecstate::ElecStatePW, base_device::DEVICE_CPU>&, + Charge&, + UnitCell&, + hamilt::HamiltPW, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + const ModulePW::PW_Basis_K&, + const ModulePW::PW_Basis&, + const ModuleBase::Vector3&, + int, + int, + bool, + int, + const std::string&); +template void orth_init_pw(hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + hsolver::HSOperator, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + elecstate::ElecStatePW, base_device::DEVICE_CPU>&, + Charge&, + UnitCell&, + const ModulePW::PW_Basis&, + int, + int, + int, + bool); +template void rebuild_td_end(psi::Psi, base_device::DEVICE_CPU>&, + elecstate::ElecStatePW, base_device::DEVICE_CPU>&, + Charge&, + UnitCell&, + hamilt::HamiltPW, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + const ModulePW::PW_Basis_K&, + const ModulePW::PW_Basis&, + const ModuleBase::Vector3&, + int, + int, + bool, + int, + const std::string&); +#if defined(__CUDA) || defined(__ROCM) +template void orth_init_pw(hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + hsolver::HSOperator, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + elecstate::ElecStatePW, base_device::DEVICE_GPU>&, + Charge&, + UnitCell&, + const ModulePW::PW_Basis&, + int, + int, + int, + bool); +template void rebuild_td_end(psi::Psi, base_device::DEVICE_GPU>&, + elecstate::ElecStatePW, base_device::DEVICE_GPU>&, + Charge&, + UnitCell&, + hamilt::HamiltPW, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + const ModulePW::PW_Basis_K&, + const ModulePW::PW_Basis&, + const ModuleBase::Vector3&, + int, + int, + bool, + int, + const std::string&); +template void orth_init_pw(hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + hsolver::HSOperator, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + elecstate::ElecStatePW, base_device::DEVICE_GPU>&, + Charge&, + UnitCell&, + const ModulePW::PW_Basis&, + int, + int, + int, + bool); +template void rebuild_td_end(psi::Psi, base_device::DEVICE_GPU>&, + elecstate::ElecStatePW, base_device::DEVICE_GPU>&, + Charge&, + UnitCell&, + hamilt::HamiltPW, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + const ModulePW::PW_Basis_K&, + const ModulePW::PW_Basis&, + const ModuleBase::Vector3&, + int, + int, + bool, + int, + const std::string&); +#endif +} // namespace pw diff --git a/source/source_pw/module_rt/td_endpoint_pw.h b/source/source_pw/module_rt/td_endpoint_pw.h new file mode 100644 index 00000000000..f30a847c036 --- /dev/null +++ b/source/source_pw/module_rt/td_endpoint_pw.h @@ -0,0 +1,41 @@ +#ifndef TD_ENDPOINT_PW_H +#define TD_ENDPOINT_PW_H + +#include "source_estate/elecstate_pw.h" +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_pw/module_pwdft/hamilt_pw.h" + +namespace pw +{ +/** @brief Clean initial orbitals and rebuild occupations and density if orthogonalization changes them. */ +template +void orth_init_pw(hsolver::TDOrbOps& orbitals, + hsolver::HSOperator& op, + psi::Psi& current, + elecstate::ElecStatePW& estate, + Charge& charge, + UnitCell& cell, + const ModulePW::PW_Basis& rhod, + int nspin, + int nbands, + int iter, + bool do_orth); + +/** @brief Rebuild an accepted endpoint from its orbitals while retaining all occupations. */ +template +void rebuild_td_end(psi::Psi& current, + elecstate::ElecStatePW& estate, + Charge& charge, + UnitCell& cell, + hamilt::HamiltPW& hamilt, + hsolver::TDOrbOps& orbitals, + const ModulePW::PW_Basis_K& basis, + const ModulePW::PW_Basis& rhod, + const ModuleBase::Vector3& vecpot, + int nspin, + int imp_sol, + bool sc_mag, + int dft_u, + const std::string& assume_iso); +} // namespace pw +#endif diff --git a/source/source_pw/module_rt/td_history_pw.cpp b/source/source_pw/module_rt/td_history_pw.cpp index cece1a71d33..a5470d32616 100644 --- a/source/source_pw/module_rt/td_history_pw.cpp +++ b/source/source_pw/module_rt/td_history_pw.cpp @@ -36,6 +36,33 @@ void save_potential(const ModuleBase::matrix& shape, const Real* current, ct::Te } // namespace +TDElecSnap capture_td(const elecstate::fenergy& energy, const ModuleBase::matrix& eigenvalues, const Charge& charge) +{ + TDElecSnap state; + state.energy = energy; + state.eigenvalues = eigenvalues; + state.density.resize(charge.nspin * charge.nrxx); + for (int spin = 0; spin < charge.nspin; ++spin) + { + std::copy(charge.rho[spin], charge.rho[spin] + charge.nrxx, state.density.begin() + spin * charge.nrxx); + } + return state; +} + +void restore_td(const TDElecSnap& state, elecstate::fenergy& energy, ModuleBase::matrix& eigenvalues, Charge& charge) +{ + ModuleBase::timer::start("pw", "restore_td"); + energy = state.energy; + eigenvalues = state.eigenvalues; + for (int spin = 0; spin < charge.nspin; ++spin) + { + const double* density = state.density.data() + spin * charge.nrxx; + std::copy(density, density + charge.nrxx, charge.rho[spin]); + } + charge.save_rho_before_sum_band(); + ModuleBase::timer::end("pw", "restore_td"); +} + template void TDHistoryPW::prepare(const elecstate::Potential& potential) { diff --git a/source/source_pw/module_rt/td_history_pw.h b/source/source_pw/module_rt/td_history_pw.h index 04b607fc64d..f9066b4fe0c 100644 --- a/source/source_pw/module_rt/td_history_pw.h +++ b/source/source_pw/module_rt/td_history_pw.h @@ -2,14 +2,31 @@ #define TD_HISTORY_PW_H #include "source_base/module_container/ATen/core/tensor.h" +#include "source_estate/fp_energy.h" +#include "source_estate/module_charge/charge.h" #include "source_estate/module_pot/potential_new.h" #include "source_psi/psi.h" #include +#include namespace pw { +/** @brief Electronic data at an accepted endpoint; orbitals and potential stay in TDHistoryPW. */ +struct TDElecSnap +{ + elecstate::fenergy energy; + ModuleBase::matrix eigenvalues; + std::vector density; +}; + +/** @brief Copy energy, band expectations and density without advancing orbital history. */ +TDElecSnap capture_td(const elecstate::fenergy& energy, const ModuleBase::matrix& eigenvalues, const Charge& charge); + +/** @brief Restore electronic data on the unchanged density grid and refresh its mixing backup. */ +void restore_td(const TDElecSnap& state, elecstate::fenergy& energy, ModuleBase::matrix& eigenvalues, Charge& charge); + /** @brief Accepted endpoint wavefunctions and potential snapshots. */ template class TDHistoryPW From c63211d24a06b1b42bbab9eafca25f58e1f1991e Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 18:17:28 +0800 Subject: [PATCH 15/33] Separate PW-specific Eij output from shared formatting to fix LCAO test linking --- source/Makefile.Objects | 1 + source/source_esolver/esolver_ks_pw_tddft.cpp | 2 +- source/source_io/CMakeLists.txt | 1 + source/source_io/module_energy/td_eij_io.cpp | 119 ---------------- source/source_io/module_energy/td_eij_io.h | 16 --- source/source_io/module_energy/td_eij_pw.cpp | 127 ++++++++++++++++++ source/source_io/module_energy/td_eij_pw.h | 25 ++++ 7 files changed, 155 insertions(+), 136 deletions(-) create mode 100644 source/source_io/module_energy/td_eij_pw.cpp create mode 100644 source/source_io/module_energy/td_eij_pw.h diff --git a/source/Makefile.Objects b/source/Makefile.Objects index a84657a880d..2252c43c326 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -717,6 +717,7 @@ OBJS_IO=module_parameter/input_conv.o\ module_dos/write_dos_pw.o\ module_energy/write_bands.o\ module_energy/td_eij_io.o\ + module_energy/td_eij_pw.o\ module_ctrl/ctrl_output_td_pw.o\ module_output/td_conserv_io.o\ module_dos/cal_dos.o\ diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 60b8393b3b1..d0b5148e07a 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -15,7 +15,7 @@ #include "source_io/module_ctrl/ctrl_output_td_pw.h" #include "source_io/module_efield/td_efield_io.h" #include "source_io/module_efield/td_vecpot_io.h" -#include "source_io/module_energy/td_eij_io.h" +#include "source_io/module_energy/td_eij_pw.h" #include "source_io/module_output/output_log.h" #include "source_io/module_output/td_conserv_io.h" #include "source_io/module_parameter/parameter.h" diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index fc92828fb26..3be275fcdf1 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -16,6 +16,7 @@ list(APPEND objects module_energy/write_bands.cpp module_energy/write_eig_occ.cpp module_energy/td_eij_io.cpp + module_energy/td_eij_pw.cpp module_bessel/numerical_basis.cpp module_bessel/numerical_basis_overlap.cpp module_bessel/numerical_basis_output.cpp diff --git a/source/source_io/module_energy/td_eij_io.cpp b/source/source_io/module_energy/td_eij_io.cpp index b6f14031c42..6448fa672f3 100644 --- a/source/source_io/module_energy/td_eij_io.cpp +++ b/source/source_io/module_energy/td_eij_io.cpp @@ -1,12 +1,7 @@ #include "source_io/module_energy/td_eij_io.h" -#include "source_base/parallel_device.h" -#include "source_base/timer.h" - -#include #include #include -#include namespace ModuleIO { @@ -57,118 +52,4 @@ void print_eij(const std::complex* eij, ofs.precision(original_precision); } -template -void print_eij_pw(hsolver::HSOperator& op, - hsolver::TDOrbOps& orbitals, - psi::Psi& current, - const K_Vectors& kv, - int nspin, - int step, - int iter, - double threshold, - std::ostream& log) -{ - ModuleBase::timer::start("ModuleIO", "print_eij_pw"); - - const int original_k = current.get_current_k(); - const int nband = current.get_nbands(); - const size_t elements = static_cast(nband) * nband; - const int nk = kv.get_nkstot() / (nspin == 2 ? 2 : 1); - const Parallel_Kpoints& pools = kv.para_k; - const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; - - for (int global_k = 0; global_k < kv.get_nkstot(); ++global_k) - { - const int physical_k = global_k % nk; - const int spin = global_k / nk; - int owner = 0; - int local_k = global_k; -#ifdef __MPI - owner = pools.whichpool[physical_k]; - local_k = physical_k - pools.startk_pool[owner] + spin * pools.nks_pool[owner]; -#endif - - std::vector> eij; - if (pools.my_pool == owner) - { - eij = orbitals.cal_eij(op, current, local_k); - } - -#ifdef __MPI - // Each pool already has the full matrix; transfer only one copy to the main log. - if (owner != 0 && pools.rank_in_pool == 0 && (main_rank || pools.my_pool == owner)) - { - if (main_rank) - { - eij.resize(elements); - } - MPI_Comm world = MPI_COMM_WORLD; - const int source = pools.get_startpro_pool(owner); - const size_t max_count = std::numeric_limits::max(); - for (size_t offset = 0; offset < elements; offset += max_count) - { - const int count = static_cast(std::min(max_count, elements - offset)); - if (main_rank) - { - Parallel_Common::recv_data(eij.data() + offset, count, source, 0, world, MPI_STATUS_IGNORE); - } - else - { - Parallel_Common::send_data(eij.data() + offset, count, 0, 0, world); - } - } - } -#endif - - if (main_rank) - { - const EijOutputInfo output{threshold, step + 1, iter, physical_k + 1, nspin, spin + 1}; - print_eij(eij.data(), nband, nband, output, log); - } - } - - current.fix_k(original_k); - op.update_k(original_k); - - ModuleBase::timer::end("ModuleIO", "print_eij_pw"); -} - -template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, - hsolver::TDOrbOps, base_device::DEVICE_CPU>&, - psi::Psi, base_device::DEVICE_CPU>&, - const K_Vectors&, - int, - int, - int, - double, - std::ostream&); -template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, - hsolver::TDOrbOps, base_device::DEVICE_CPU>&, - psi::Psi, base_device::DEVICE_CPU>&, - const K_Vectors&, - int, - int, - int, - double, - std::ostream&); -#if defined(__CUDA) || defined(__ROCM) -template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, - hsolver::TDOrbOps, base_device::DEVICE_GPU>&, - psi::Psi, base_device::DEVICE_GPU>&, - const K_Vectors&, - int, - int, - int, - double, - std::ostream&); -template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, - hsolver::TDOrbOps, base_device::DEVICE_GPU>&, - psi::Psi, base_device::DEVICE_GPU>&, - const K_Vectors&, - int, - int, - int, - double, - std::ostream&); -#endif } // namespace ModuleIO diff --git a/source/source_io/module_energy/td_eij_io.h b/source/source_io/module_energy/td_eij_io.h index a713249fecd..eb8944e9306 100644 --- a/source/source_io/module_energy/td_eij_io.h +++ b/source/source_io/module_energy/td_eij_io.h @@ -1,9 +1,6 @@ #ifndef MODULE_IO_TD_EIJ_IO_H #define MODULE_IO_TD_EIJ_IO_H -#include "source_cell/klist.h" -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" - #include #include @@ -26,18 +23,5 @@ void print_eij(const std::complex* eij, const int leading_dimension, const EijOutputInfo& output_info, std::ostream& ofs); -/** @brief Collect and print Eij from an already bound endpoint Hamiltonian. - * All ranks participate; both k-point cursors are restored before returning. - */ -template -void print_eij_pw(hsolver::HSOperator& op, - hsolver::TDOrbOps& orbitals, - psi::Psi& current, - const K_Vectors& kv, - int nspin, - int step, - int iter, - double threshold, - std::ostream& log); } // namespace ModuleIO #endif diff --git a/source/source_io/module_energy/td_eij_pw.cpp b/source/source_io/module_energy/td_eij_pw.cpp new file mode 100644 index 00000000000..8f824845d81 --- /dev/null +++ b/source/source_io/module_energy/td_eij_pw.cpp @@ -0,0 +1,127 @@ +#include "source_io/module_energy/td_eij_pw.h" + +#include "source_base/parallel_device.h" +#include "source_base/timer.h" +#include "source_io/module_energy/td_eij_io.h" + +#include +#include +#include + +namespace ModuleIO +{ +template +void print_eij_pw(hsolver::HSOperator& op, + hsolver::TDOrbOps& orbitals, + psi::Psi& current, + const K_Vectors& kv, + int nspin, + int step, + int iter, + double threshold, + std::ostream& log) +{ + ModuleBase::timer::start("ModuleIO", "print_eij_pw"); + + const int original_k = current.get_current_k(); + const int nband = current.get_nbands(); + const size_t elements = static_cast(nband) * nband; + const int nk = kv.get_nkstot() / (nspin == 2 ? 2 : 1); + const Parallel_Kpoints& pools = kv.para_k; + const bool main_rank = pools.my_pool == 0 && pools.rank_in_pool == 0; + + for (int global_k = 0; global_k < kv.get_nkstot(); ++global_k) + { + const int physical_k = global_k % nk; + const int spin = global_k / nk; + int owner = 0; + int local_k = global_k; +#ifdef __MPI + owner = pools.whichpool[physical_k]; + local_k = physical_k - pools.startk_pool[owner] + spin * pools.nks_pool[owner]; +#endif + + std::vector> eij; + if (pools.my_pool == owner) + { + eij = orbitals.cal_eij(op, current, local_k); + } + +#ifdef __MPI + // Each pool already has the full matrix; transfer only one copy to the main log. + if (owner != 0 && pools.rank_in_pool == 0 && (main_rank || pools.my_pool == owner)) + { + if (main_rank) + { + eij.resize(elements); + } + MPI_Comm world = MPI_COMM_WORLD; + const int source = pools.get_startpro_pool(owner); + const size_t max_count = std::numeric_limits::max(); + for (size_t offset = 0; offset < elements; offset += max_count) + { + const int count = static_cast(std::min(max_count, elements - offset)); + if (main_rank) + { + Parallel_Common::recv_data(eij.data() + offset, count, source, 0, world, MPI_STATUS_IGNORE); + } + else + { + Parallel_Common::send_data(eij.data() + offset, count, 0, 0, world); + } + } + } +#endif + + if (main_rank) + { + const EijOutputInfo output{threshold, step + 1, iter, physical_k + 1, nspin, spin + 1}; + print_eij(eij.data(), nband, nband, output, log); + } + } + + current.fix_k(original_k); + op.update_k(original_k); + + ModuleBase::timer::end("ModuleIO", "print_eij_pw"); +} + +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_CPU>&, + hsolver::TDOrbOps, base_device::DEVICE_CPU>&, + psi::Psi, base_device::DEVICE_CPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +#if defined(__CUDA) || defined(__ROCM) +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +template void print_eij_pw(hsolver::HSOperator, base_device::DEVICE_GPU>&, + hsolver::TDOrbOps, base_device::DEVICE_GPU>&, + psi::Psi, base_device::DEVICE_GPU>&, + const K_Vectors&, + int, + int, + int, + double, + std::ostream&); +#endif +} // namespace ModuleIO diff --git a/source/source_io/module_energy/td_eij_pw.h b/source/source_io/module_energy/td_eij_pw.h new file mode 100644 index 00000000000..e9dff7f8419 --- /dev/null +++ b/source/source_io/module_energy/td_eij_pw.h @@ -0,0 +1,25 @@ +#ifndef TD_EIJ_PW_H +#define TD_EIJ_PW_H + +#include "source_cell/klist.h" +#include "source_hsolver/module_rt_pw/td_orbital_ops.h" + +#include + +namespace ModuleIO +{ +/** @brief Collect and print Eij from an already bound endpoint Hamiltonian. + * All ranks participate; both k-point cursors are restored before returning. + */ +template +void print_eij_pw(hsolver::HSOperator& op, + hsolver::TDOrbOps& orbitals, + psi::Psi& current, + const K_Vectors& kv, + int nspin, + int step, + int iter, + double threshold, + std::ostream& log); +} // namespace ModuleIO +#endif From d829d0b1dc14963b2c7bc05c4d77df72d79f1303 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 19:15:28 +0800 Subject: [PATCH 16/33] Refactor PW RT-TDDFT interfaces to replace redundant parameter bundles with explicit dependencies and clarify naming --- source/source_esolver/esolver_ks_pw_tddft.cpp | 36 ++++--- source/source_esolver/esolver_ks_pw_tddft.h | 3 +- .../module_rt_pw/cn_solver_pw.cpp | 78 ++++++++------- .../module_rt_pw/cn_solver_pw.h | 29 +++--- .../module_rt_pw/exp_action.cpp | 29 +++--- .../source_hsolver/module_rt_pw/exp_action.h | 12 +-- .../module_rt_pw/exp_lanczos.cpp | 9 +- .../source_hsolver/module_rt_pw/exp_lanczos.h | 2 +- .../module_rt_pw/exp_taylor.cpp | 6 +- .../source_hsolver/module_rt_pw/exp_taylor.h | 2 +- .../module_rt_pw/hsolver_linear.cpp | 25 ++--- .../module_rt_pw/hsolver_linear.h | 8 +- .../module_rt_pw/linear_gmres.cpp | 23 +++-- .../module_rt_pw/linear_gmres.h | 7 +- .../module_rt_pw/linear_solver_types.h | 11 +-- .../module_rt_pw/td_orbital_ops.cpp | 71 ++++++------- .../module_rt_pw/td_orbital_ops.h | 20 ++-- .../module_rt_pw/test/test_exp_action.cpp | 42 ++++---- .../module_rt_pw/test/test_hsolver_linear.cpp | 50 +++++----- .../test/test_hsolver_pw_tddft.cpp | 81 +++++++-------- .../module_ctrl/ctrl_output_td_pw.cpp | 20 +++- .../source_io/module_ctrl/ctrl_output_td_pw.h | 8 +- .../module_current/td_spin_pw_io.cpp | 17 ++-- .../source_io/module_current/td_spin_pw_io.h | 6 +- source/source_pw/module_rt/prop_cn_pw.cpp | 43 +++++--- source/source_pw/module_rt/prop_cn_pw.h | 14 ++- source/source_pw/module_rt/prop_etrs_pw.cpp | 66 ++++++++----- source/source_pw/module_rt/prop_etrs_pw.h | 21 +++- source/source_pw/module_rt/td_obs_pw.h | 12 +-- source/source_pw/module_rt/td_prop_pw.cpp | 99 ++++++++++++------- source/source_pw/module_rt/td_prop_pw.h | 57 +++++------ source/source_pw/module_rt/td_spin_pw.cpp | 24 +++-- source/source_pw/module_rt/td_spin_pw.h | 15 ++- source/source_pw/module_rt/td_vel_pw.cpp | 2 +- source/source_pw/module_rt/td_vel_pw.h | 2 +- .../module_rt/test/test_td_spin_pw.cpp | 11 +-- .../module_rt/test/test_td_vel_pw.cpp | 2 +- 37 files changed, 528 insertions(+), 435 deletions(-) diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index d0b5148e07a..77d7b91a9c8 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -71,19 +71,23 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons const int xc_type = XC_Functional::get_func_type(); pw::check_td_input(inp, ucell, xc_type, this->general_exx_info_.cal_exx); - spin_options_.method = inp.td_out_mag; - spin_options_.orbital_dir = inp.orbital_dir; - spin_options_.radius = inp.onsite_radius; - spin_options_.torque = inp.td_out_torque; - - vel_request_.charge_total = inp.out_current == 1; - vel_request_.charge_per_k = vel_request_.charge_total && inp.out_current_k; - vel_request_.spin_current = inp.nspin == 4 && inp.td_out_spincur; + current_req_.charge_total = inp.out_current == 1; + current_req_.charge_per_k = current_req_.charge_total && inp.out_current_k; + current_req_.spin_current = inp.nspin == 4 && inp.td_out_spincur; vel_.initialize(ucell, *this->pw_wfc, this->kv, this->ppcell); if (inp.nspin == 4) { - spin_.initialize(ucell, *this->pw_rhod, *this->pw_wfc, this->kv, this->sf, this->ppcell, spin_options_); + spin_.initialize(ucell, + *this->pw_rhod, + *this->pw_wfc, + this->kv, + this->sf, + this->ppcell, + inp.td_out_mag, + inp.orbital_dir, + inp.onsite_radius, + inp.td_out_torque); } this->pelec->pot->set_td_field_manager(this->td_field_manager_); @@ -93,8 +97,7 @@ void ESolver_KS_PW_TDDFT::before_all_runners(BaseCell& basecell, cons const hsolver::diag_comm_info comm(0, 1); #endif const int npol = ucell.get_npol(); - const pw::TDPropOpts options = pw::td_prop_options(inp, npol, this->kv.ik2iktot); - this->td_prop_.reset(new pw::TDPropPW(*this->pw_wfc, options, comm, log_)); + this->td_prop_ = pw::make_td_prop(*this->pw_wfc, inp, npol, this->kv.ik2iktot, comm, log_); this->history_.prepare(*this->pelec->pot); // Preserve existing field history until input validation and initialization succeed. @@ -563,10 +566,10 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const auto& current = *this->stp.template get_psi_t(); const auto& vecpot = this->td_field_manager_->A_right_ha(); - const pw::TDVelResultPW currents = vel_.calculate(current, *this->pelec, vecpot, vel_request_); + const pw::TDVelResultPW currents = vel_.calculate(current, *this->pelec, vecpot, current_req_); pw::TDSpinResultPW moments; - if (this->inp_->nspin == 4 && (spin_options_.method != "none" || spin_options_.torque)) + if (this->inp_->nspin == 4 && (this->inp_->td_out_mag != "none" || this->inp_->td_out_torque)) { moments = spin_.calculate(current, *this->pelec, vecpot); } @@ -575,8 +578,11 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, const int nkstot = this->kv.get_nkstot(); ModuleIO::ctrl_output_td_pw(currents, moments, - vel_request_, - spin_options_, + current_req_, + this->inp_->td_out_mag, + this->inp_->orbital_dir, + this->inp_->onsite_radius, + this->inp_->td_out_torque, ucell.orbital_fn, istep, dt_ha, diff --git a/source/source_esolver/esolver_ks_pw_tddft.h b/source/source_esolver/esolver_ks_pw_tddft.h index a4f6b3b28d1..cd7af6fcfd0 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.h +++ b/source/source_esolver/esolver_ks_pw_tddft.h @@ -59,8 +59,7 @@ class ESolver_KS_PW_TDDFT : public ESolver_KS_PW std::shared_ptr td_field_manager_; pw::TDVelPW vel_; pw::TDSpinPW spin_; - pw::TDVelRequestPW vel_request_; - pw::TDSpinOptsPW spin_options_; + pw::CurrentRequest current_req_; pw::TDHistoryPW history_; double initial_wave_electrons_ = 0.0; diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp index b5df2a13652..e8b9146d05d 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp @@ -97,10 +97,12 @@ PWPreconditioner parse_pw_precond(const std::string& name) template CNSolverPW::CNSolverPW(const ModulePW::PW_Basis_K& basis, - const PWLinearOptions& options, + const CNControls& cn_ctrl, + int npol, + const std::vector& global_k, const diag_comm_info& comm, std::ostream& log) - : basis_(basis), comm_(comm), options_(options), algebra_(comm), log_(log) + : basis_(basis), comm_(comm), cn_ctrl_(cn_ctrl), npol_(npol), global_k_(global_k), algebra_(comm), log_(log) { initialize(); } @@ -111,7 +113,7 @@ void CNSolverPW::initialize() const char* method = nullptr; for (const LinearMethodName& entry: linear_method_names) { - if (options_.linear.method == entry.method) + if (cn_ctrl_.linear.solver == entry.method) { method = entry.name; break; @@ -125,7 +127,7 @@ void CNSolverPW::initialize() const char* preconditioner = nullptr; for (const PWPrecondName& entry: pw_precond_names) { - if (options_.preconditioner == entry.preconditioner) + if (cn_ctrl_.preconditioner == entry.preconditioner) { preconditioner = entry.name; break; @@ -136,18 +138,18 @@ void CNSolverPW::initialize() ModuleBase::WARNING_QUIT("CNSolverPW", "Unsupported preconditioner."); return; } - if (options_.linear.method != LinearMethod::gmres) + if (cn_ctrl_.linear.solver != LinearMethod::gmres) { // Ignore the GMRES-only option before creating solver state or reporting effective settings. - options_.linear.reconstruct = false; + cn_ctrl_.linear.reconstruct = false; } - linear_solver_.reset(new HSolverLinear(options_.linear, comm_)); + linear_solver_.reset(new HSolverLinear(cn_ctrl_.linear, comm_)); std::ostringstream info; info << " PW RT-TDDFT linear solver: " << method << "; tolerance: " << std::scientific << std::setprecision(6) - << linear_solver_->tolerance() << "; maximum iterations: " << options_.linear.max_iterations - << "; restart: " << options_.linear.restart << '\n' - << " Preconditioner: " << preconditioner << "; CN initial guess: " << options_.cn_init - << "; residual reconstruction: " << options_.linear.reconstruct << '\n'; + << linear_solver_->tolerance() << "; maximum iterations: " << cn_ctrl_.linear.max_iterations + << "; restart: " << cn_ctrl_.linear.restart << '\n' + << " Preconditioner: " << preconditioner << "; CN initial guess: " << cn_ctrl_.cn_init + << "; residual reconstruction: " << cn_ctrl_.linear.reconstruct << '\n'; log_ << info.str(); } @@ -186,12 +188,12 @@ void CNSolverPW::update_precond(const int ik, for (int ig = 0; ig < dim; ++ig) { // The two spin components share the same kinetic diagonal; padding keeps an identity preconditioner. - const int physical_g = options_.npol == 2 ? ig % basis_.npwk_max : ig; + const int physical_g = npol_ == 2 ? ig % basis_.npwk_max : ig; if (physical_g >= basis_.npwk[ik]) { continue; } - const double kinetic = options_.kinetic_enabled ? ModulePW::shifted_kinetic(basis_, ik, physical_g, momentum_shift) : 0.0; + const double kinetic = cn_ctrl_.kinetic_enabled ? ModulePW::shifted_kinetic(basis_, ik, physical_g, momentum_shift) : 0.0; inverse[ig] = T(1) / (T(1) + coefficient * static_cast(kinetic)); } if (dim > 0) @@ -206,7 +208,7 @@ void CNSolverPW::update_precond(const int ik, template bool CNSolverPW::tracks_state() const { - return options_.preconditioner == PWPreconditioner::kinetic_recycle || options_.linear.reconstruct; + return cn_ctrl_.preconditioner == PWPreconditioner::kinetic_recycle || cn_ctrl_.linear.reconstruct; } template @@ -244,7 +246,7 @@ void CNSolverPW::invalidate_basis() template bool CNSolverPW::require_audit(int step, KPointState* state) const { - if (!options_.linear.reconstruct) + if (!cn_ctrl_.linear.reconstruct) { return true; } @@ -257,15 +259,15 @@ bool CNSolverPW::require_audit(int step, KPointState* state) const template void CNSolverPW::retry_kinetic(const LinearOperator& op, T* current, - const SolveBatch& batch, + const BatchLayout& batch, LinearSolveResult* result) { - const int remaining_iterations = options_.linear.max_iterations - result->iterations; - const LinearSolveControl control{remaining_iterations, false}; + const int remaining_iter = cn_ctrl_.linear.max_iterations - result->iterations; const T* inverse = inverse_kinetic_.template data(); const LinearLowRank diagonal(algebra_, inverse, batch.dim); const T* rhs = rhs_.template data(); - LinearSolveResult retry = linear_solver_->solve(op, diagonal, batch.ld, batch.bands, batch.dim, current, rhs, nullptr, true, control); + LinearSolveResult retry + = linear_solver_->solve(op, diagonal, batch.ld, batch.bands, batch.dim, current, rhs, nullptr, true, remaining_iter, false); retry.iterations += result->iterations; retry.restarts += result->restarts; retry.operator_calls += result->operator_calls; @@ -279,7 +281,7 @@ template void CNSolverPW::update_state(KPointState* state, const SolveDetails& details, int step, - const SolveBatch& batch, + const BatchLayout& batch, const T* current) { if (!state) @@ -294,7 +296,7 @@ void CNSolverPW::update_state(KPointState* state, { state->independent_step = step; } - if (options_.preconditioner == PWPreconditioner::kinetic_recycle && details.projected + if (cn_ctrl_.preconditioner == PWPreconditioner::kinetic_recycle && details.projected && details.linear.status == LinearSolveStatus::converged && details.linear.reconstruction_fallbacks == 0 && !details.retried) { const T* seed = projection_.seed(); @@ -308,18 +310,18 @@ template typename CNSolverPW::SolveDetails CNSolverPW::solve_kpoint(const LinearOperator& op, const T* previous, T* current, - const SolveBatch& batch, + const BatchLayout& batch, int step, int iteration, KPointState* state) { SolveDetails details; const T* rhs = rhs_.template data(); - const bool need_projection = (options_.cn_init && iteration == 1) || options_.preconditioner == PWPreconditioner::kinetic_recycle - || options_.preconditioner == PWPreconditioner::kinetic_subspace; + const bool need_projection = (cn_ctrl_.cn_init && iteration == 1) || cn_ctrl_.preconditioner == PWPreconditioner::kinetic_recycle + || cn_ctrl_.preconditioner == PWPreconditioner::kinetic_subspace; details.projected = need_projection && projection_.prepare(algebra_, batch.ld, batch.dim, batch.bands, previous, rhs); const T* initial_residual = nullptr; - if (options_.cn_init && iteration == 1 && details.projected) + if (cn_ctrl_.cn_init && iteration == 1 && details.projected) { const T* seed = projection_.seed(); if (batch.dim > 0) @@ -330,15 +332,15 @@ typename CNSolverPW::SolveDetails CNSolverPW::solve_kpoint details.cn_initial = true; } const bool force_check = require_audit(step, state); - const T* inverse = options_.preconditioner == PWPreconditioner::none ? nullptr : inverse_kinetic_.template data(); + const T* inverse = cn_ctrl_.preconditioner == PWPreconditioner::none ? nullptr : inverse_kinetic_.template data(); LinearLowRank preconditioner(algebra_, inverse, batch.dim); - if (options_.preconditioner == PWPreconditioner::kinetic_subspace && details.projected) + if (cn_ctrl_.preconditioner == PWPreconditioner::kinetic_subspace && details.projected) { const T* image = projection_.image(); const LinearSmallLU& factor = projection_.factor(); preconditioner.prepare_subspace(batch.ld, batch.bands, previous, image, factor, &correction_workspace_); } - else if (options_.preconditioner == PWPreconditioner::kinetic_recycle && state->response.rank() > 0) + else if (cn_ctrl_.preconditioner == PWPreconditioner::kinetic_recycle && state->response.rank() > 0) { const int rank = state->response.rank(); const T* directions = state->response.directions(); @@ -349,7 +351,7 @@ typename CNSolverPW::SolveDetails CNSolverPW::solve_kpoint details.linear = linear_solver_->solve(op, preconditioner, batch.ld, batch.bands, batch.dim, current, rhs, initial_residual, force_check); if (details.linear.status != LinearSolveStatus::converged && details.coarse_rank > 0 - && details.linear.iterations < options_.linear.max_iterations) + && details.linear.iterations < cn_ctrl_.linear.max_iterations) { retry_kinetic(op, current, batch, &details.linear); details.retried = true; @@ -365,7 +367,7 @@ void CNSolverPW::report_solve(const SolveDetails& details, int ik, in const int evolution_step = step + 1; std::ostringstream record; record << std::scientific << std::setprecision(6) << " PW RT-TDDFT linear solve: evolution_step=" << evolution_step - << " scf_iter=" << iteration << " global_k=" << options_.global_k_indices.at(ik) + 1 << '\n' + << " scf_iter=" << iteration << " global_k=" << global_k_.at(ik) + 1 << '\n' << " iterations=" << result.iterations << " restarts=" << result.restarts << " operator_calls=" << result.operator_calls << " operator_columns=" << result.operator_columns << " pre_orth_residual=" << result.max_residual << " residual_kind=" << (result.reconstructed ? "reconstructed" : "independent") << " true_checks=" << result.true_checks << '\n' @@ -401,23 +403,23 @@ void CNSolverPW::solve(HSOperator& op, previous.fix_k(ik); const int npw = current->get_ngk(ik); // A spinor spans both npwx-strided blocks, including their explicitly zeroed padding. - const int dim = options_.npol == 2 ? ld : npw; + const int dim = npol_ == 2 ? ld : npw; KPointState* state = tracks_state() ? &states_[ik] : nullptr; - const ShiftedHOperator rhs_op(op, rhs_coefficient, dim, npw, options_.npol); - const ShiftedHOperator lhs_op(op, coefficient, dim, npw, options_.npol); - const OrbLayout layout{ld, npw, options_.npol, bands}; + const ShiftedHOperator rhs_op(op, rhs_coefficient, dim, npw, npol_); + const ShiftedHOperator lhs_op(op, coefficient, dim, npw, npol_); + const OrbLayout layout{ld, npw, npol_, bands}; const T* previous_data = clean_orbs(previous.get_pointer(), layout, &clean_); T* current_data = current->get_pointer(); - if (options_.npol == 2) + if (npol_ == 2) { zero_pad(current_data, layout); } rhs_op.apply(previous_data, rhs, ld, bands); - if (options_.preconditioner != PWPreconditioner::none) + if (cn_ctrl_.preconditioner != PWPreconditioner::none) { update_precond(ik, dim, coefficient, momentum_shift); } - const SolveBatch batch{ld, dim, bands}; + const BatchLayout batch{ld, dim, bands}; const SolveDetails details = solve_kpoint(lhs_op, previous_data, current_data, batch, istep, iter, state); if (detailed_output && log_.good()) { @@ -434,7 +436,7 @@ void CNSolverPW::solve(HSOperator& op, band_label = std::to_string(result.failed_band + 1); } message << "PW RT-TDDFT linear solve failed at electronic evolution step " << evolution_step << ", SCF iteration " << iter - << ", global_k=" << options_.global_k_indices.at(ik) + 1 << ", band " << band_label << ", after " << result.iterations + << ", global_k=" << global_k_.at(ik) + 1 << ", band " << band_label << ", after " << result.iterations << " iterations: " << linear_status_name(result.status) << "; residual = " << result.max_residual; ModuleBase::WARNING_QUIT("CNSolverPW", message.str()); } diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.h b/source/source_hsolver/module_rt_pw/cn_solver_pw.h index 75d34525820..73b35a9f0d6 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.h +++ b/source/source_hsolver/module_rt_pw/cn_solver_pw.h @@ -24,15 +24,13 @@ enum class PWPreconditioner kinetic_subspace }; -/** @brief Numerical and propagation options, resolved once at the input boundary. */ -struct PWLinearOptions +/** @brief Crank-Nicolson linear solve and preconditioner controls. */ +struct CNControls { - int npol = 1; ///< Spin components per orbital; fixed throughout propagation. - LinearSolveOptions linear; + LinearControls linear; PWPreconditioner preconditioner = PWPreconditioner::kinetic; bool cn_init = false; bool kinetic_enabled = true; - std::vector global_k_indices; ///< Required zero-based global index for every local k point. }; LinearMethod parse_linear_method(const std::string& name); @@ -43,8 +41,13 @@ template class CNSolverPW { public: - /** @brief Configure propagation using typed, explicit solver options. */ - CNSolverPW(const ModulePW::PW_Basis_K& basis, const PWLinearOptions& options, const diag_comm_info& comm, std::ostream& log); + /** @brief Configure propagation with explicit numerical controls and orbital layout. */ + CNSolverPW(const ModulePW::PW_Basis_K& basis, + const CNControls& cn_ctrl, + int npol, + const std::vector& global_k, + const diag_comm_info& comm, + std::ostream& log); /** @brief Invalidate basis-dependent history and restart independent residual checks without releasing buffers. * Call on every rank in the pool after changing the basis or its distribution, including @@ -83,7 +86,7 @@ class CNSolverPW int ld = -1; int bands = -1; }; - struct SolveBatch + struct BatchLayout { int ld; int dim; @@ -99,7 +102,9 @@ class CNSolverPW }; const ModulePW::PW_Basis_K& basis_; const diag_comm_info comm_; - PWLinearOptions options_; + CNControls cn_ctrl_; + const int npol_; + const std::vector global_k_; ct::Tensor clean_; LinearAlgebra algebra_; std::ostream& log_; @@ -119,12 +124,12 @@ class CNSolverPW SolveDetails solve_kpoint(const LinearOperator& op, const T* previous, T* current, - const SolveBatch& batch, + const BatchLayout& batch, int step, int iteration, KPointState* state); - void retry_kinetic(const LinearOperator& op, T* current, const SolveBatch& batch, LinearSolveResult* result); - void update_state(KPointState* state, const SolveDetails& details, int step, const SolveBatch& batch, const T* current); + void retry_kinetic(const LinearOperator& op, T* current, const BatchLayout& batch, LinearSolveResult* result); + void update_state(KPointState* state, const SolveDetails& details, int step, const BatchLayout& batch, const T* current); void report_solve(const SolveDetails& details, int ik, int step, int iteration) const; void prepare_buffers(const int nbands, const int nbasis); void update_precond(const int ik, const int dim, const T coefficient, const ModuleBase::Vector3& momentum_shift); diff --git a/source/source_hsolver/module_rt_pw/exp_action.cpp b/source/source_hsolver/module_rt_pw/exp_action.cpp index 5eb7c06d993..31d4c769550 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/exp_action.cpp @@ -9,37 +9,42 @@ namespace hsolver { template -ExpAction::ExpAction(const ExpOpts& exp_opts, const diag_comm_info& comm) : exp_opts_(exp_opts), algebra_(comm), work_(comm, 1) +ExpAction::ExpAction(const ExpControls& exp_ctrl, const diag_comm_info& comm) + : exp_ctrl_(exp_ctrl), algebra_(comm), work_(comm, 1) { using Real = typename T::value_type; - if (exp_opts_.rel_tol == 0.0) + if (exp_ctrl_.rel_tol == 0.0) { - exp_opts_.rel_tol = std::max(1e-10, 100.0 * std::numeric_limits::epsilon()); + exp_ctrl_.rel_tol = std::max(1e-10, 100.0 * std::numeric_limits::epsilon()); } - if (exp_opts_.taylor_order < 2 || exp_opts_.krylov_max < 2 || !std::isfinite(exp_opts_.rel_tol) || exp_opts_.rel_tol <= 0) + if (exp_ctrl_.taylor_order < 2 || exp_ctrl_.krylov_max < 2 || !std::isfinite(exp_ctrl_.rel_tol) || exp_ctrl_.rel_tol <= 0) { throw std::invalid_argument("Invalid exponential controls."); } } template -std::unique_ptr> make_exp_action(const ExpOpts& exp_opts, const diag_comm_info& comm) +std::unique_ptr> make_exp_action(const ExpControls& exp_ctrl, const diag_comm_info& comm) { - if (exp_opts.method == ExpMethod::taylor) + if (exp_ctrl.expansion == ExpMethod::taylor) { - return std::unique_ptr>(new ExpTaylor(exp_opts, comm)); + return std::unique_ptr>(new ExpTaylor(exp_ctrl, comm)); } - return std::unique_ptr>(new ExpLanczos(exp_opts, comm)); + return std::unique_ptr>(new ExpLanczos(exp_ctrl, comm)); } template class ExpAction, base_device::DEVICE_CPU>; template class ExpAction, base_device::DEVICE_CPU>; -template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); -template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpControls&, + const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_CPU>> make_exp_action(const ExpControls&, + const diag_comm_info&); #if defined(__CUDA) || defined(__ROCM) template class ExpAction, base_device::DEVICE_GPU>; template class ExpAction, base_device::DEVICE_GPU>; -template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); -template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpOpts&, const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpControls&, + const diag_comm_info&); +template std::unique_ptr, base_device::DEVICE_GPU>> make_exp_action(const ExpControls&, + const diag_comm_info&); #endif } // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/exp_action.h b/source/source_hsolver/module_rt_pw/exp_action.h index dceefe9f115..4762aba85d6 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.h +++ b/source/source_hsolver/module_rt_pw/exp_action.h @@ -17,9 +17,9 @@ enum class ExpMethod }; /** @brief Explicit controls for a frozen Hermitian Hamiltonian exponential. */ -struct ExpOpts +struct ExpControls { - ExpMethod method = ExpMethod::lanczos; + ExpMethod expansion = ExpMethod::lanczos; int taylor_order = 4; double rel_tol = 0.0; int krylov_max = 32; @@ -52,17 +52,17 @@ template class ExpAction { protected: - ExpOpts exp_opts_; + ExpControls exp_ctrl_; LinearAlgebra algebra_; LinearWorkspace work_; public: - ExpAction(const ExpOpts& exp_opts, const diag_comm_info& comm); + ExpAction(const ExpControls& exp_ctrl, const diag_comm_info& comm); virtual ~ExpAction() = default; /** @brief Effective relative tolerance after resolving the precision-dependent default. */ double rel_tol() const { - return exp_opts_.rel_tol; + return exp_ctrl_.rel_tol; } virtual ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) = 0; virtual ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) @@ -71,6 +71,6 @@ class ExpAction /** @brief Construct a backend without reading global input parameters. */ template -std::unique_ptr> make_exp_action(const ExpOpts& exp_opts, const diag_comm_info& comm); +std::unique_ptr> make_exp_action(const ExpControls& exp_ctrl, const diag_comm_info& comm); } // namespace hsolver #endif diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp index 7952893eac6..a615109f767 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -32,7 +32,8 @@ ExpResult failed_band(ExpResult result, int band, bool retry) } // namespace template -ExpLanczos::ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm), comm_(comm) +ExpLanczos::ExpLanczos(const ExpControls& exp_ctrl, const diag_comm_info& comm) + : ExpAction(exp_ctrl, comm), comm_(comm) { } @@ -50,7 +51,7 @@ int ExpLanczos::batch_size(const OrbLayout& layout) const rows = *std::max_element(sizes.begin(), sizes.end()); } #endif - const double vectors = static_cast(this->exp_opts_.krylov_max) + 6.0; + const double vectors = static_cast(this->exp_ctrl_.krylov_max) + 6.0; const double bytes_per_band = sizeof(T) * rows * vectors; const double budget = 256.0 * 1024.0 * 1024.0; const int memory_limit = static_cast(std::min(64.0, budget / bytes_per_band)); @@ -137,7 +138,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, const int bands = layout.bands; const int dim = layout.npol == 2 ? ld : layout.npw; const int stride = ld * bands; - const int limit = this->exp_opts_.krylov_max; + const int limit = this->exp_ctrl_.krylov_max; const int64_t size = std::max(1, stride); const double time = full ? 2.0 * std::abs(alpha) : std::abs(alpha); *lost_orthogonality = false; @@ -512,7 +513,7 @@ ExpResult ExpLanczos::evaluate(const HSOperator& op, const T* src = input + offset; T* dst = output + offset; T* full_dst = full ? full + offset : nullptr; - const ExpResult local = interval(op, batch, alpha, this->exp_opts_.rel_tol, src, dst, full_dst, 0); + const ExpResult local = interval(op, batch, alpha, this->exp_ctrl_.rel_tol, src, dst, full_dst, 0); result.has_err_est = result.has_err_est && local.has_err_est; add_work(result, local); result.rel_err_est = std::max(result.rel_err_est, local.rel_err_est); diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.h b/source/source_hsolver/module_rt_pw/exp_lanczos.h index a1ba4ac06a9..fc19f00f4f4 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.h +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.h @@ -43,7 +43,7 @@ class ExpLanczos final : public ExpAction ExpResult evaluate(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output, T* full); public: - ExpLanczos(const ExpOpts& exp_opts, const diag_comm_info& comm); + ExpLanczos(const ExpControls& exp_ctrl, const diag_comm_info& comm); ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) override; /** @brief Share one Krylov construction for alpha and twice alpha. */ ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) override; diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.cpp b/source/source_hsolver/module_rt_pw/exp_taylor.cpp index 72f3c2ecbdf..78123c4d884 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.cpp +++ b/source/source_hsolver/module_rt_pw/exp_taylor.cpp @@ -6,7 +6,7 @@ namespace hsolver { template -ExpTaylor::ExpTaylor(const ExpOpts& exp_opts, const diag_comm_info& comm) : ExpAction(exp_opts, comm) +ExpTaylor::ExpTaylor(const ExpControls& exp_ctrl, const diag_comm_info& comm) : ExpAction(exp_ctrl, comm) { } @@ -52,10 +52,10 @@ ExpResult ExpTaylor::evaluate(const HSOperator& op, base_device::memory::synchronize_memory_op()(full, term, count); } ExpResult result; - result.order_or_dim = this->exp_opts_.taylor_order; + result.order_or_dim = this->exp_ctrl_.taylor_order; // Scale at the longer requested time; half-step weights cannot overflow at high order. const double series_alpha = full ? 2.0 * alpha : alpha; - for (int k = 1; k <= this->exp_opts_.taylor_order; ++k) + for (int k = 1; k <= this->exp_ctrl_.taylor_order; ++k) { op.hpsi(term, image, ld, bands); zero_pad(image, layout); diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.h b/source/source_hsolver/module_rt_pw/exp_taylor.h index c4fab74e31f..46c398f03d4 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.h +++ b/source/source_hsolver/module_rt_pw/exp_taylor.h @@ -13,7 +13,7 @@ class ExpTaylor final : public ExpAction ExpResult evaluate(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full); public: - ExpTaylor(const ExpOpts& exp_opts, const diag_comm_info& comm); + ExpTaylor(const ExpControls& exp_ctrl, const diag_comm_info& comm); ExpResult apply(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* output) override; ExpResult apply_pair(const HSOperator& op, const OrbLayout& layout, double alpha, const T* input, T* half, T* full) override; }; diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/hsolver_linear.cpp index e9e7a91b684..2f313a32acb 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp +++ b/source/source_hsolver/module_rt_pw/hsolver_linear.cpp @@ -23,23 +23,23 @@ const char* linear_status_name(const LinearSolveStatus status) } template -HSolverLinear::HSolverLinear(const LinearSolveOptions& options, const diag_comm_info& comm) - : default_control_{options.max_iterations, options.reconstruct} +HSolverLinear::HSolverLinear(const LinearControls& linear_ctrl, const diag_comm_info& comm) + : max_iter_(linear_ctrl.max_iterations), reconstruct_(linear_ctrl.reconstruct) { using Real = typename GetTypeReal::type; const double roundoff_tolerance = 100.0 * std::numeric_limits::epsilon(); - tolerance_ = options.tolerance == 0.0 ? std::max(1e-10, roundoff_tolerance) : options.tolerance; - if (options.method == LinearMethod::bicgstab) + tolerance_ = linear_ctrl.tolerance == 0.0 ? std::max(1e-10, roundoff_tolerance) : linear_ctrl.tolerance; + if (linear_ctrl.solver == LinearMethod::bicgstab) { bicgstab_.reset(new LinearBiCGSTAB(tolerance_, comm)); } - else if (options.method == LinearMethod::cgs) + else if (linear_ctrl.solver == LinearMethod::cgs) { cgs_.reset(new LinearCGS(tolerance_, comm)); } - else if (options.method == LinearMethod::gmres) + else if (linear_ctrl.solver == LinearMethod::gmres) { - gmres_.reset(new LinearGMRES(tolerance_, options, comm)); + gmres_.reset(new LinearGMRES(tolerance_, linear_ctrl.restart, linear_ctrl.max_iterations, comm)); } else { @@ -58,7 +58,7 @@ LinearSolveResult HSolverLinear::solve(const LinearOperator @@ -71,19 +71,20 @@ LinearSolveResult HSolverLinear::solve(const LinearOperatorsolve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, control.max_iterations); + return bicgstab_->solve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, max_iter); } else if (cgs_) { - return cgs_->solve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, control.max_iterations); + return cgs_->solve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, max_iter); } else if (gmres_) { - return gmres_->solve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, force_check, control); + return gmres_->solve(op, preconditioner, ld, nvec, dim, x, b, initial_residual, force_check, max_iter, reconstruct); } else { diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.h b/source/source_hsolver/module_rt_pw/hsolver_linear.h index 678c47cebb3..f52da86a61d 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.h +++ b/source/source_hsolver/module_rt_pw/hsolver_linear.h @@ -17,10 +17,11 @@ class HSolverLinear std::unique_ptr> cgs_; std::unique_ptr> gmres_; double tolerance_ = 0.0; - const LinearSolveControl default_control_; + const int max_iter_; + const bool reconstruct_; public: - HSolverLinear(const LinearSolveOptions& options, const diag_comm_info& comm); + HSolverLinear(const LinearControls& linear_ctrl, const diag_comm_info& comm); /** @brief Return the effective tolerance after resolving the precision-dependent default. */ double tolerance() const { @@ -46,7 +47,8 @@ class HSolverLinear const T* b, const T* initial_residual, bool force_check, - const LinearSolveControl& control); + int max_iter, + bool reconstruct); }; } // namespace hsolver #endif diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.cpp b/source/source_hsolver/module_rt_pw/linear_gmres.cpp index 2a7bd7ad38a..a26c8684f7f 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.cpp +++ b/source/source_hsolver/module_rt_pw/linear_gmres.cpp @@ -6,11 +6,11 @@ namespace hsolver { template -LinearGMRES::LinearGMRES(double tolerance, const LinearSolveOptions& options, const diag_comm_info& comm) +LinearGMRES::LinearGMRES(double tolerance, int restart, int max_iter, const diag_comm_info& comm) : tolerance_(tolerance), restart_(0), work_(comm, 2), algebra_(comm) { - const int iter_limit = std::max(1, options.max_iterations); - restart_ = std::min(options.restart, iter_limit); + const int iter_limit = std::max(1, max_iter); + restart_ = std::min(restart, iter_limit); if (restart_ <= 0 || restart_ > (std::numeric_limits::max() - 1) / 3) { throw std::invalid_argument("Invalid GMRES restart dimension."); @@ -229,7 +229,7 @@ bool LinearGMRES::cycle(const LinearOperator& op, const LinearOperator& preconditioner, const std::vector& threshold, bool reconstruct, - int max_iterations, + int max_iter, LinearSolveResult* result) { std::vector coeff(bands_); @@ -237,7 +237,7 @@ bool LinearGMRES::cycle(const LinearOperator& op, { return false; } - for (int j = 0; j < restart_ && active_ > 0 && result->iterations < max_iterations; ++j) + for (int j = 0; j < restart_ && active_ > 0 && result->iterations < max_iter; ++j) { T* z = direction(j); T* raw = image(j); @@ -263,7 +263,7 @@ bool LinearGMRES::cycle(const LinearOperator& op, return false; } done[b] = std::abs(g_[b][j + 1]) <= 0.8 * threshold[order_[b]] || norm <= std::numeric_limits::min() || j + 1 == restart_ - || result->iterations == max_iterations; + || result->iterations == max_iter; coeff[b] = done[b] ? T(0) : T(1.0 / norm); if (!done[b]) { @@ -298,11 +298,11 @@ LinearSolveResult LinearGMRES::solve(const LinearOperator& const T* b, const T* initial_residual, bool force_check, - const LinearSolveControl& control) + int max_iter, + bool reconstruct) { const LinearSolveTimer timer("LinearGMRES"); - const int max_iterations = control.max_iterations; - work_.prepare(ld, dim, nvec, x, b, tolerance_, max_iterations); + work_.prepare(ld, dim, nvec, x, b, tolerance_, max_iter); work_.reset_statistics(); ld_ = ld; dim_ = dim; @@ -319,7 +319,6 @@ LinearSolveResult LinearGMRES::solve(const LinearOperator& const double rhs_norm = linear_norm(rhs_norms[i]); threshold[i] = tolerance_ * std::max(1.0, rhs_norm); } - bool reconstruct = control.reconstruct; force_check = force_check || tolerance_ < 100 * std::numeric_limits::epsilon(); LinearSolveResult result; work_.clear(); @@ -339,7 +338,7 @@ LinearSolveResult LinearGMRES::solve(const LinearOperator& bool regular = false; try { - regular = cycle(op, preconditioner, threshold, reconstruct, max_iterations, &result); + regular = cycle(op, preconditioner, threshold, reconstruct, max_iter, &result); result.status = regular ? LinearSolveStatus::max_iterations : LinearSolveStatus::breakdown; } catch (const LinearPreconditionerError&) @@ -414,7 +413,7 @@ LinearSolveResult LinearGMRES::solve(const LinearOperator& return result; } const bool retry_initial = initial_residual && result.restarts == 0; - if (result.iterations >= max_iterations || (result.iterations == start && !retry_initial) || !std::isfinite(result.max_residual)) + if (result.iterations >= max_iter || (result.iterations == start && !retry_initial) || !std::isfinite(result.max_residual)) { return result; } diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.h b/source/source_hsolver/module_rt_pw/linear_gmres.h index a6b80ab1856..f51d3f1141f 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.h +++ b/source/source_hsolver/module_rt_pw/linear_gmres.h @@ -50,11 +50,11 @@ class LinearGMRES const LinearOperator& preconditioner, const std::vector& threshold, bool reconstruct, - int max_iterations, + int max_iter, LinearSolveResult* result); public: - LinearGMRES(double tolerance, const LinearSolveOptions& options, const diag_comm_info& comm); + LinearGMRES(double tolerance, int restart, int max_iter, const diag_comm_info& comm); /** @brief Use the per-call budget and reconstruction policy; force_check requests an independent final application. */ LinearSolveResult solve(const LinearOperator& op, const LinearOperator& preconditioner, @@ -65,7 +65,8 @@ class LinearGMRES const T* b, const T* initial_residual, bool force_check, - const LinearSolveControl& control); + int max_iter, + bool reconstruct); }; } // namespace hsolver #endif diff --git a/source/source_hsolver/module_rt_pw/linear_solver_types.h b/source/source_hsolver/module_rt_pw/linear_solver_types.h index 030f014682f..2d26f6400f8 100644 --- a/source/source_hsolver/module_rt_pw/linear_solver_types.h +++ b/source/source_hsolver/module_rt_pw/linear_solver_types.h @@ -38,22 +38,15 @@ enum class LinearMethod }; /** @brief Numerical policy; tolerance zero selects the precision-aware default. */ -struct LinearSolveOptions +struct LinearControls { - LinearMethod method = LinearMethod::bicgstab; + LinearMethod solver = LinearMethod::bicgstab; double tolerance = 0.0; int max_iterations = 500; int restart = 20; bool reconstruct = false; }; -/** @brief Explicit policy for one solve; does not change the solver's defaults. */ -struct LinearSolveControl -{ - int max_iterations; - bool reconstruct; -}; - const char* linear_status_name(const LinearSolveStatus status); } // namespace hsolver diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp b/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp index fc562843c1b..1f13816df30 100644 --- a/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp +++ b/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp @@ -12,10 +12,16 @@ namespace hsolver { template -TDOrbOps::TDOrbOps(const TDOrbOpts& options, const diag_comm_info& comm, std::ostream& log) - : comm_(comm), options_(options), algebra_(comm), orthonormal_(comm, algebra_), log_(log), band_products_(comm, 9) +TDOrbOps::TDOrbOps(int npol, + OrthMethod orth_method, + bool out_stat, + const std::vector& global_k, + const diag_comm_info& comm, + std::ostream& log) + : comm_(comm), npol_(npol), orth_method_(orth_method), out_stat_(out_stat), global_k_(global_k), algebra_(comm), + orthonormal_(comm, algebra_), log_(log), band_products_(comm, 9) { - log_ << " PW RT-TDDFT orthonormalization: " << orth_method_name(options_.orthonormal) << '\n'; + log_ << " PW RT-TDDFT orthonormalization: " << orth_method_name(orth_method_) << '\n'; } template @@ -29,7 +35,7 @@ void TDOrbOps::prepare_buffers(int nbands, int nbasis) template void TDOrbOps::correct(psi::Psi* current, int step, int iter, bool detailed_output) { - if (options_.out_stat) + if (out_stat_) { orth_norms_.resize(current->get_nk()); } @@ -38,7 +44,7 @@ void TDOrbOps::correct(psi::Psi* current, int step, int it { current->fix_k(ik); const int ld = current->get_nbasis(); - const int dim = options_.npol == 2 ? ld : current->get_ngk(ik); + const int dim = npol_ == 2 ? ld : current->get_ngk(ik); const int bands = current->get_nbands(); T* data = current->get_pointer(); correct_orbitals(data, ld, dim, bands, ik, step, iter, detailed_output); @@ -59,11 +65,11 @@ void TDOrbOps::cal_band_energy(HSOperator& op, const psi:: op.update_k(ik); current.fix_k(ik); const int npw = current.get_ngk(ik); - const int dim = options_.npol == 2 ? ld : npw; - const OrbLayout layout{ld, npw, options_.npol, nband}; + const int dim = npol_ == 2 ? ld : npw; + const OrbLayout layout{ld, npw, npol_, nband}; const T* data = clean_orbs(current.get_pointer(), layout, &clean_); op.hpsi(data, hpsi, ld, nband); - if (options_.npol == 2) + if (npol_ == 2) { zero_pad(hpsi, layout); } @@ -83,15 +89,15 @@ std::vector> TDOrbOps::cal_eij(HSOperator(current.get_pointer(), layout, &clean_); T* hpsi = hpsi_.template data(); op.hpsi(psi, hpsi, ld, nband); - if (options_.npol == 2) + if (npol_ == 2) { zero_pad(hpsi, layout); } @@ -103,7 +109,7 @@ std::vector> TDOrbOps::cal_eij(HSOperator void TDOrbOps::reset_orth_stats() { - if (options_.out_stat) + if (out_stat_) { orth_stats_ = TDOrthStats(); } @@ -114,14 +120,14 @@ template void TDOrbOps::correct_orbitals(T* current, int ld, int dim, int bands, int ik, int istep, int iter, bool detailed_output) { ModuleBase::timer::start("TDOrbOps", "correct_orbitals"); - const OrthResult result = orthonormal_.apply(current, ld, dim, bands, options_.orthonormal, options_.out_stat); + const OrthResult result = orthonormal_.apply(current, ld, dim, bands, orth_method_, out_stat_); record_orth(result, ik, istep, iter); if (detailed_output && result.gram_checked && log_.good()) { const int evolution_step = istep + 1; std::ostringstream record; record << std::scientific << std::setprecision(6) << " PW RT-TDDFT orth: evolution_step=" << evolution_step << " scf_iter=" << iter - << " global_k=" << options_.global_k_indices.at(ik) + 1 << " requested=" << orth_method_name(options_.orthonormal) + << " global_k=" << global_k_.at(ik) + 1 << " requested=" << orth_method_name(orth_method_) << " orth_before=" << result.before << " orth_after=" << result.after << '\n'; log_ << record.str(); } @@ -137,8 +143,7 @@ void TDOrbOps::record_orth(const OrthResult& result, int ik, int iste std::ostringstream message; const int evolution_step = istep + 1; message << std::setprecision(16) << " PW RT-TDDFT orbital validation failed (" << stage << "): evolution_step=" << evolution_step - << " scf_iter=" << iter << " global_k=" << options_.global_k_indices.at(ik) + 1 - << " requested=" << orth_method_name(options_.orthonormal) << '\n' + << " scf_iter=" << iter << " global_k=" << global_k_.at(ik) + 1 << " requested=" << orth_method_name(orth_method_) << '\n' << " last_attempted=" << orth_method_name(result.actual) << " passes=" << result.passes << " fallbacks=" << result.fallbacks << '\n' << " " << orth_failure_name(result.failure) << "; " << result.reason; @@ -159,7 +164,7 @@ void TDOrbOps::record_orth(const OrthResult& result, int ik, int iste ModuleBase::WARNING_QUIT("TDOrbOps", message.str()); } report_orth_warning(result, ik, istep, iter); - if (options_.out_stat) + if (out_stat_) { orth_norms_[ik] = result.norms; if (result.gram_checked) @@ -195,8 +200,8 @@ void TDOrbOps::report_orth_warning(const OrthResult& result, int ik, const int evolution_step = istep + 1; std::ostringstream message; message << std::setprecision(16) << " PW RT-TDDFT orbital warning: evolution_step=" << evolution_step << " scf_iter=" << iter; - message << " global_k=" << options_.global_k_indices.at(ik) + 1; - message << " requested=" << orth_method_name(options_.orthonormal) << '\n' + message << " global_k=" << global_k_.at(ik) + 1; + message << " requested=" << orth_method_name(orth_method_) << '\n' << " before=" << result.before << " after=" << result.after << " tolerance=" << orth_tolerance() << '\n' << " "; message << "last_attempted=" << orth_method_name(result.actual) << ". "; @@ -223,7 +228,7 @@ template void TDOrbOps::zero_padding(psi::Psi* current) { ModuleBase::timer::start("TDOrbOps", "zero_padding"); - if (options_.npol != 2) + if (npol_ != 2) { ModuleBase::timer::end("TDOrbOps", "zero_padding"); return; @@ -235,7 +240,7 @@ void TDOrbOps::zero_padding(psi::Psi* current) const int npw = current->get_ngk(ik); const int ld = current->get_nbasis(); const int bands = current->get_nbands(); - const OrbLayout layout{ld, npw, options_.npol, bands}; + const OrbLayout layout{ld, npw, npol_, bands}; zero_pad(current->get_pointer(), layout); } current->fix_k(old_k); @@ -247,9 +252,9 @@ bool TDOrbOps::orth_initial(psi::Psi* current, int iter) { ModuleBase::timer::start("TDOrbOps", "orth_initial"); bool changed = false; - if (options_.orthonormal != OrthMethod::none) + if (orth_method_ != OrthMethod::none) { - if (options_.out_stat) + if (out_stat_) { // Initial diagnostics describe this SCF state, not earlier discarded iterates. orth_stats_ = TDOrthStats(); @@ -258,13 +263,9 @@ bool TDOrbOps::orth_initial(psi::Psi* current, int iter) for (int ik = 0; ik < current->get_nk(); ++ik) { current->fix_k(ik); - const int dim = options_.npol == 2 ? current->get_nbasis() : current->get_ngk(ik); - const OrthResult result = orthonormal_.apply(current->get_pointer(), - current->get_nbasis(), - dim, - current->get_nbands(), - options_.orthonormal, - options_.out_stat); + const int dim = npol_ == 2 ? current->get_nbasis() : current->get_ngk(ik); + const OrthResult result + = orthonormal_.apply(current->get_pointer(), current->get_nbasis(), dim, current->get_nbands(), orth_method_, out_stat_); record_orth(result, ik, 0, iter); changed = changed || result.status == OrthStatus::accepted; } @@ -277,11 +278,11 @@ template void TDOrbOps::check_initial(const psi::Psi& current, int iter) { ModuleBase::timer::start("TDOrbOps", "check_initial"); - const bool full_gram = options_.orthonormal != OrthMethod::none; + const bool full_gram = orth_method_ != OrthMethod::none; const int ld = current.get_nbasis(); const int bands = current.get_nbands(); prepare_buffers(bands, ld); - if (options_.out_stat) + if (out_stat_) { orth_norms_.resize(current.get_nk()); } @@ -289,10 +290,10 @@ void TDOrbOps::check_initial(const psi::Psi& current, int { current.fix_k(ik); const int npw = current.get_ngk(ik); - const int dim = options_.npol == 2 ? ld : npw; - const OrbLayout layout{ld, npw, options_.npol, bands}; + const int dim = npol_ == 2 ? ld : npw; + const OrbLayout layout{ld, npw, npol_, bands}; const T* data = clean_orbs(current.get_pointer(), layout, &clean_); - const OrthResult result = orthonormal_.inspect(data, current.get_nbasis(), dim, current.get_nbands(), full_gram, options_.out_stat); + const OrthResult result = orthonormal_.inspect(data, current.get_nbasis(), dim, current.get_nbands(), full_gram, out_stat_); record_orth(result, ik, 0, iter); } ModuleBase::timer::end("TDOrbOps", "check_initial"); diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.h b/source/source_hsolver/module_rt_pw/td_orbital_ops.h index e333bbcbd18..8207324b0de 100644 --- a/source/source_hsolver/module_rt_pw/td_orbital_ops.h +++ b/source/source_hsolver/module_rt_pw/td_orbital_ops.h @@ -10,14 +10,6 @@ namespace hsolver { -/** @brief Method-independent orbital layout and diagnostics. */ -struct TDOrbOpts -{ - int npol = 1; - OrthMethod orthonormal = OrthMethod::cholesky; - bool out_stat = false; - std::vector global_k_indices; -}; /** @brief Independent orthogonality maxima over accepted trial iterations and local k points. */ struct TDOrthStats { @@ -30,7 +22,10 @@ class TDOrbOps { private: const diag_comm_info comm_; - TDOrbOpts options_; + const int npol_; + const OrthMethod orth_method_; + const bool out_stat_; + const std::vector global_k_; ct::Tensor clean_; ct::Tensor hpsi_; LinearAlgebra algebra_; @@ -47,7 +42,12 @@ class TDOrbOps public: /** @brief Configure common orbital processing independently of a propagator. */ - TDOrbOps(const TDOrbOpts& options, const diag_comm_info& comm, std::ostream& log); + TDOrbOps(int npol, + OrthMethod orth_method, + bool out_stat, + const std::vector& global_k, + const diag_comm_info& comm, + std::ostream& log); /** @brief Apply common postprocessing to a completed predictor or corrector. */ void correct(psi::Psi* current, int step, int iter, bool detailed_output); /** @brief Reset diagnostics without invalidating preconditioner history. */ diff --git a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp index 476cf5e1b18..3bebe3634b2 100644 --- a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp @@ -179,8 +179,8 @@ TYPED_TEST(ExpTest, LanczosMatchesFullExponential) const hsolver::OrbLayout layout{10, 6 / npol, npol, 10}; const auto input = pack_input(op, layout); std::vector output(input.size()); - hsolver::ExpOpts opts; - auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + hsolver::ExpControls exp_ctrl; + auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); const auto result = exp->apply(op, layout, 0.4, input.data(), output.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_TRUE(result.has_err_est); @@ -203,18 +203,18 @@ TYPED_TEST(ExpTest, TaylorPairMatchesBothPolynomials) const auto input = pack_input(op, layout); std::vector half(input.size()); std::vector full(input.size()); - hsolver::ExpOpts opts; - opts.method = hsolver::ExpMethod::taylor; - opts.taylor_order = 6; - auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + hsolver::ExpControls exp_ctrl; + exp_ctrl.expansion = hsolver::ExpMethod::taylor; + exp_ctrl.taylor_order = 6; + auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); const auto result = exp->apply_pair(op, layout, 0.15, input.data(), half.data(), full.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_FALSE(result.has_err_est); - EXPECT_EQ(op.calls, opts.taylor_order); + EXPECT_EQ(op.calls, exp_ctrl.taylor_order); EXPECT_EQ(result.hpsi_calls, op.calls); - EXPECT_EQ(result.hpsi_cols, opts.taylor_order * layout.bands); - check_output(op, layout, input, half, 0.15, opts.taylor_order, tol); - check_output(op, layout, input, full, 0.3, opts.taylor_order, tol); + EXPECT_EQ(result.hpsi_cols, exp_ctrl.taylor_order * layout.bands); + check_output(op, layout, input, half, 0.15, exp_ctrl.taylor_order, tol); + check_output(op, layout, input, full, 0.3, exp_ctrl.taylor_order, tol); } } @@ -224,21 +224,21 @@ TEST(ExpAction, LanczosSubdividesOrReportsExhaustion) const hsolver::OrbLayout layout{8, 6, 1, 2}; const auto input = pack_input(op, layout); std::vector output(input.size()); - hsolver::ExpOpts opts; - opts.krylov_max = 4; - opts.rel_tol = 1e-8; - auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + hsolver::ExpControls exp_ctrl; + exp_ctrl.krylov_max = 4; + exp_ctrl.rel_tol = 1e-8; + auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); const auto result = exp->apply(op, layout, 1.0, input.data(), output.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_GT(result.bisect_count, 0); - EXPECT_LE(result.rel_err_est, opts.rel_tol); + EXPECT_LE(result.rel_err_est, exp_ctrl.rel_tol); EXPECT_EQ(result.hpsi_calls, op.calls); EXPECT_EQ(result.hpsi_cols, op.cols); check_output(op, layout, input, output, 1.0, 0, 5e-8); - opts.krylov_max = 2; - opts.rel_tol = 1e-14; - exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + exp_ctrl.krylov_max = 2; + exp_ctrl.rel_tol = 1e-14; + exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); const auto failed = exp->apply(op, layout, 1.0, input.data(), output.data()); EXPECT_EQ(failed.state, hsolver::ExpState::failed); EXPECT_GT(failed.bisect_count, 0); @@ -255,9 +255,9 @@ TEST(ExpAction, NonfiniteInputReportsOriginalBand) std::vector output(input.size()); for (auto method: {hsolver::ExpMethod::lanczos, hsolver::ExpMethod::taylor}) { - hsolver::ExpOpts opts; - opts.method = method; - auto exp = hsolver::make_exp_action(opts, linear_test::world_comm()); + hsolver::ExpControls exp_ctrl; + exp_ctrl.expansion = method; + auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); const auto result = exp->apply(op, layout, 0.1, input.data(), output.data()); EXPECT_EQ(result.state, hsolver::ExpState::failed); EXPECT_EQ(result.failed_band, 9); diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp index 275f5b14921..77e923709ad 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp @@ -121,11 +121,11 @@ TYPED_TEST(LinearSolveTest, DenseReferenceAndReusedBatches) { for (const bool preconditioned: {false, true}) { - hsolver::LinearSolveOptions options; - options.method = method; - options.tolerance = tolerance; - options.restart = 2; - hsolver::HSolverLinear solver(options, comm); + hsolver::LinearControls linear_ctrl; + linear_ctrl.solver = method; + linear_ctrl.tolerance = tolerance; + linear_ctrl.restart = 2; + hsolver::HSolverLinear solver(linear_ctrl, comm); // Shrink, grow, and use zero local rows without replacing the solver. for (const int n: {11, 5, 17, 1}) { @@ -271,11 +271,11 @@ TEST(LinearSolveFailure, ReportsOriginalColumnAndTrueResidual) std::fill(a.begin(), a.end(), T(0)); } DenseOperator op(a, n, start, dim, comm); - hsolver::LinearSolveOptions options; - options.method = method; - options.tolerance = 1e-13; - options.max_iterations = singular ? 10 : 1; - hsolver::HSolverLinear solver(options, comm); + hsolver::LinearControls linear_ctrl; + linear_ctrl.solver = method; + linear_ctrl.tolerance = 1e-13; + linear_ctrl.max_iterations = singular ? 10 : 1; + hsolver::HSolverLinear solver(linear_ctrl, comm); std::vector x(3 * ld, T(0)); std::vector b(3 * ld, T(0)); // Retire column 0; column 2 is swapped forward but failure must still name column 1. @@ -289,7 +289,7 @@ TEST(LinearSolveFailure, ReportsOriginalColumnAndTrueResidual) = singular ? hsolver::LinearSolveStatus::breakdown : hsolver::LinearSolveStatus::max_iterations; EXPECT_EQ(result.status, expected); EXPECT_EQ(result.failed_band, 1); - EXPECT_LE(result.iterations, options.max_iterations); + EXPECT_LE(result.iterations, linear_ctrl.max_iterations); std::vector ax(3 * ld, T(0)); op.apply(x.data(), ax.data(), ld, 3); double residual2 = 0.0; @@ -301,7 +301,7 @@ TEST(LinearSolveFailure, ReportsOriginalColumnAndTrueResidual) Parallel_Common::reduce_data(&residual2, 1, comm.comm); #endif EXPECT_NEAR(result.max_residual, std::sqrt(residual2), 1e-12); - EXPECT_GT(result.max_residual, options.tolerance); + EXPECT_GT(result.max_residual, linear_ctrl.tolerance); } } } @@ -340,23 +340,23 @@ void check_reconstruction(const bool corrupt_initial) b[band * ld + i] = rhs[band * n + start + i]; } } - hsolver::LinearSolveOptions options; - options.method = hsolver::LinearMethod::gmres; + hsolver::LinearControls linear_ctrl; + linear_ctrl.solver = hsolver::LinearMethod::gmres; // Above 100*epsilon in float: exercise reconstructed acceptance, not an automatic audit. - options.tolerance = std::is_same>::value ? 2e-5 : 1e-11; - options.restart = 2; - options.max_iterations = 100; - options.reconstruct = true; + linear_ctrl.tolerance = std::is_same>::value ? 2e-5 : 1e-11; + linear_ctrl.restart = 2; + linear_ctrl.max_iterations = 100; + linear_ctrl.reconstruct = true; DenseOperator op(matrix, n, start, dim, comm); const IdentityOperator identity(dim); - hsolver::HSolverLinear solver(options, comm); + hsolver::HSolverLinear solver(linear_ctrl, comm); const T* initial = corrupt_initial ? bad_residual.data() : nullptr; const hsolver::LinearSolveResult result = solver.solve(op, identity, ld, bands, dim, x.data(), b.data(), initial, corrupt_initial); ASSERT_EQ(result.status, hsolver::LinearSolveStatus::converged); EXPECT_EQ(result.failed_band, -1); EXPECT_GT(result.restarts, 0); EXPECT_GT(result.true_checks, 0); - EXPECT_LE(result.iterations, options.max_iterations); + EXPECT_LE(result.iterations, linear_ctrl.max_iterations); EXPECT_EQ(result.reconstructed, !corrupt_initial); EXPECT_EQ(result.reconstruction_fallbacks, corrupt_initial ? 1 : 0); std::vector ax(ld * bands, T(0)); @@ -369,24 +369,24 @@ void check_reconstruction(const bool corrupt_initial) { error += std::norm(linear_test::Complex(ax[band * ld + i]) - linear_test::Complex(b[band * ld + i])); norm += std::norm(linear_test::Complex(b[band * ld + i])); - EXPECT_LT(std::abs(linear_test::Complex(x[band * ld + i]) - reference[band * n + start + i]), 10 * options.tolerance); + EXPECT_LT(std::abs(linear_test::Complex(x[band * ld + i]) - reference[band * n + start + i]), 10 * linear_ctrl.tolerance); } #ifdef __MPI Parallel_Common::reduce_data(&error, 1, comm.comm); Parallel_Common::reduce_data(&norm, 1, comm.comm); #endif - EXPECT_LE(std::sqrt(error), 1.2 * options.tolerance * std::max(1.0, std::sqrt(norm))); + EXPECT_LE(std::sqrt(error), 1.2 * linear_ctrl.tolerance * std::max(1.0, std::sqrt(norm))); } if (corrupt_initial) { // Recovery shares the total iteration budget rather than starting a fresh allowance. - options.max_iterations = 1; - hsolver::HSolverLinear limited(options, comm); + linear_ctrl.max_iterations = 1; + hsolver::HSolverLinear limited(linear_ctrl, comm); std::fill(x.begin(), x.end(), T(0)); const hsolver::LinearSolveResult exhausted = limited.solve(op, identity, ld, bands, dim, x.data(), b.data(), initial, true); EXPECT_EQ(exhausted.status, hsolver::LinearSolveStatus::max_iterations); EXPECT_EQ(exhausted.failed_band, 1); - EXPECT_EQ(exhausted.iterations, options.max_iterations); + EXPECT_EQ(exhausted.iterations, linear_ctrl.max_iterations); EXPECT_EQ(exhausted.reconstruction_fallbacks, 1); } } diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp index 12f370bbb1e..d854fdca3de 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp @@ -166,24 +166,21 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) const std::vector dimensions{2 * sizes[0], 2 * sizes[1]}; DenseHamiltonian op(dimensions); op.spinor = true; - hsolver::PWLinearOptions options; - hsolver::TDOrbOpts orb_opts; - options.npol = 2; - options.global_k_indices = std::vector{0, 1}; - options.linear.method = hsolver::LinearMethod::bicgstab; - options.linear.tolerance = single ? 2e-6 : 1e-13; - options.linear.max_iterations = 100; - options.kinetic_enabled = true; + hsolver::CNControls cn_ctrl; + const int npol = 2; + const std::vector global_k{0, 1}; + cn_ctrl.linear.solver = hsolver::LinearMethod::bicgstab; + cn_ctrl.linear.tolerance = single ? 2e-6 : 1e-13; + cn_ctrl.linear.max_iterations = 100; + cn_ctrl.kinetic_enabled = true; #ifdef __MPI const hsolver::diag_comm_info comm(MPI_COMM_SELF, 0, 1); #else const hsolver::diag_comm_info comm(0, 1); #endif std::ostringstream log; - orb_opts.npol = options.npol; - orb_opts.global_k_indices = options.global_k_indices; - hsolver::TDOrbOps orbitals(orb_opts, comm, log); - hsolver::CNSolverPW solver(basis, options, comm, log); + hsolver::TDOrbOps orbitals(npol, hsolver::OrthMethod::cholesky, false, global_k, comm, log); + hsolver::CNSolverPW solver(basis, cn_ctrl, npol, global_k, comm, log); const int ld = 2 * npwx; psi::Psi previous(2, bands, ld, sizes, true); std::vector> packed(2); @@ -264,19 +261,16 @@ TYPED_TEST(PWTDDFTTest, ComplexEijExcludesPadding) ModulePW::PW_Basis_K basis; initialize_basis(sizes, &basis); DenseHamiltonian op(sizes); - hsolver::PWLinearOptions options; - hsolver::TDOrbOpts orb_opts; - options.global_k_indices = std::vector{0}; + hsolver::CNControls cn_ctrl; + const std::vector global_k{0}; #ifdef __MPI const hsolver::diag_comm_info comm(MPI_COMM_SELF, 0, 1); #else const hsolver::diag_comm_info comm(0, 1); #endif std::ostringstream log; - orb_opts.npol = options.npol; - orb_opts.global_k_indices = options.global_k_indices; - hsolver::TDOrbOps orbitals(orb_opts, comm, log); - hsolver::CNSolverPW solver(basis, options, comm, log); + hsolver::TDOrbOps orbitals(1, hsolver::OrthMethod::cholesky, false, global_k, comm, log); + hsolver::CNSolverPW solver(basis, cn_ctrl, 1, global_k, comm, log); psi::Psi current(1, bands, ld, sizes, true); const T phases[]{T(1), T(0, 1), T(-1)}; const T padding(std::numeric_limits::quiet_NaN()); @@ -335,22 +329,19 @@ TYPED_TEST(PWTDDFTTest, DenseCNCorrectorsAndConservation) initialize_basis(sizes, &basis); DenseHamiltonian op(sizes); std::ostringstream log; - hsolver::PWLinearOptions options; - hsolver::TDOrbOpts orb_opts; - options.global_k_indices = std::vector{0, 1}; + hsolver::CNControls cn_ctrl; + const std::vector global_k{0, 1}; const std::string solver_name = cn_variant ? method.substr(0, method.size() - 3) : method; - options.linear.method = hsolver::parse_linear_method(solver_name); - options.linear.tolerance = single_precision ? 2e-6 : 1e-13; - options.linear.max_iterations = 100; - options.preconditioner = hsolver::parse_pw_precond(precond); - options.kinetic_enabled = true; - options.linear.restart = 2; - options.linear.reconstruct = method == "gmres" && recycle; - options.cn_init = method == "gmres" || cn_variant; - orb_opts.npol = options.npol; - orb_opts.global_k_indices = options.global_k_indices; - hsolver::TDOrbOps orbitals(orb_opts, comm, log); - hsolver::CNSolverPW solver(basis, options, comm, log); + cn_ctrl.linear.solver = hsolver::parse_linear_method(solver_name); + cn_ctrl.linear.tolerance = single_precision ? 2e-6 : 1e-13; + cn_ctrl.linear.max_iterations = 100; + cn_ctrl.preconditioner = hsolver::parse_pw_precond(precond); + cn_ctrl.kinetic_enabled = true; + cn_ctrl.linear.restart = 2; + cn_ctrl.linear.reconstruct = method == "gmres" && recycle; + cn_ctrl.cn_init = method == "gmres" || cn_variant; + hsolver::TDOrbOps orbitals(1, hsolver::OrthMethod::cholesky, false, global_k, comm, log); + hsolver::CNSolverPW solver(basis, cn_ctrl, 1, global_k, comm, log); psi::Psi previous(2, bands, ld, sizes, true); for (int ik = 0; ik < 2; ++ik) { @@ -489,19 +480,15 @@ TYPED_TEST(PWTDDFTTest, UnequalOccupationsAndDiagnosticsPreservePropagation) for (const bool out_stat: {false, true}) { std::ostringstream log; - hsolver::PWLinearOptions options; - hsolver::TDOrbOpts orb_opts; - options.global_k_indices = std::vector{0}; - options.linear.method = hsolver::LinearMethod::gmres; - options.linear.tolerance = single ? 2e-6 : 1e-13; - options.linear.max_iterations = 100; - options.linear.restart = n; - options.preconditioner = hsolver::PWPreconditioner::none; - orb_opts.out_stat = out_stat; - orb_opts.npol = options.npol; - orb_opts.global_k_indices = options.global_k_indices; - hsolver::TDOrbOps orbitals(orb_opts, comm, log); - hsolver::CNSolverPW solver(basis, options, comm, log); + hsolver::CNControls cn_ctrl; + const std::vector global_k{0}; + cn_ctrl.linear.solver = hsolver::LinearMethod::gmres; + cn_ctrl.linear.tolerance = single ? 2e-6 : 1e-13; + cn_ctrl.linear.max_iterations = 100; + cn_ctrl.linear.restart = n; + cn_ctrl.preconditioner = hsolver::PWPreconditioner::none; + hsolver::TDOrbOps orbitals(1, hsolver::OrthMethod::cholesky, out_stat, global_k, comm, log); + hsolver::CNSolverPW solver(basis, cn_ctrl, 1, global_k, comm, log); psi::Psi previous(1, bands, ld, sizes, true); std::fill_n(previous.get_pointer(), ld * bands, T(0)); std::vector reference(n * bands, Wide(0)); diff --git a/source/source_io/module_ctrl/ctrl_output_td_pw.cpp b/source/source_io/module_ctrl/ctrl_output_td_pw.cpp index 00f34c12c41..d84a88ca85e 100644 --- a/source/source_io/module_ctrl/ctrl_output_td_pw.cpp +++ b/source/source_io/module_ctrl/ctrl_output_td_pw.cpp @@ -8,8 +8,11 @@ namespace ModuleIO { void ctrl_output_td_pw(const pw::TDVelResultPW& currents, const pw::TDSpinResultPW& moments, - const pw::TDVelRequestPW& request, - const pw::TDSpinOptsPW& spin_opts, + const pw::CurrentRequest& request, + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque, const std::vector& orbital_fn, int step, double dt_ha, @@ -32,7 +35,18 @@ void ctrl_output_td_pw(const pw::TDVelResultPW& currents, if (nspin == 4) { const double time = step * dt_ha * ModuleBase::AU_to_FS; - write_td_spin_pw(directory, step, time, orbital_fn, spin_opts, request.spin_current, moments, currents, log); + write_td_spin_pw(directory, + step, + time, + orbital_fn, + mag_partition, + orbital_dir, + onsite_radius, + calc_torque, + request.spin_current, + moments, + currents, + log); } } } // namespace ModuleIO diff --git a/source/source_io/module_ctrl/ctrl_output_td_pw.h b/source/source_io/module_ctrl/ctrl_output_td_pw.h index b0f930a0c6c..f996c71dbc0 100644 --- a/source/source_io/module_ctrl/ctrl_output_td_pw.h +++ b/source/source_io/module_ctrl/ctrl_output_td_pw.h @@ -4,14 +4,18 @@ #include "source_pw/module_rt/td_obs_pw.h" #include +#include namespace ModuleIO { /** @brief Write precomputed endpoint observables on the designated output rank. */ void ctrl_output_td_pw(const pw::TDVelResultPW& currents, const pw::TDSpinResultPW& moments, - const pw::TDVelRequestPW& request, - const pw::TDSpinOptsPW& spin_opts, + const pw::CurrentRequest& request, + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque, const std::vector& orbital_fn, int step, double dt_ha, diff --git a/source/source_io/module_current/td_spin_pw_io.cpp b/source/source_io/module_current/td_spin_pw_io.cpp index 2d36db90c3b..4dbfe79a04e 100644 --- a/source/source_io/module_current/td_spin_pw_io.cpp +++ b/source/source_io/module_current/td_spin_pw_io.cpp @@ -71,23 +71,26 @@ void write_td_spin_pw(const std::string& directory, int step, double time_fs, const std::vector& orbitals, - const pw::TDSpinOptsPW& options, + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque, bool spin_current, const pw::TDSpinResultPW& spin, const pw::TDVelResultPW& current, std::ostream& log) { - if (options.method != "none") + if (mag_partition != "none") { - std::string header = "# method=" + options.method + "; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign"; - if (options.method == "onsite") + std::string header = "# method=" + mag_partition + "; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign"; + if (mag_partition == "onsite") { std::ostringstream radius; - radius << std::scientific << std::setprecision(12) << options.radius; + radius << std::scientific << std::setprecision(12) << onsite_radius; header += "\n# onsite_radius(Bohr)=" + radius.str() + "; projector=q(k+G+A); atom_phase_cancels"; for (int it = 0; it < orbitals.size(); ++it) { - header += "\n# orbital=" + options.orbital_dir + orbitals[it]; + header += "\n# orbital=" + orbital_dir + orbitals[it]; } } std::ostringstream total_columns; @@ -121,7 +124,7 @@ void write_td_spin_pw(const std::string& directory, finish_series(&total); write_atoms(&atoms, step, time_fs, spin.atom_mag); } - if (options.torque) + if (calc_torque) { std::ostringstream total_columns; total_columns << std::left << std::setw(6) << "#step" << " " << std::right << std::setw(10) << "time_fs"; diff --git a/source/source_io/module_current/td_spin_pw_io.h b/source/source_io/module_current/td_spin_pw_io.h index edb315f3886..d2fa021c823 100644 --- a/source/source_io/module_current/td_spin_pw_io.h +++ b/source/source_io/module_current/td_spin_pw_io.h @@ -3,6 +3,7 @@ #include "source_pw/module_rt/td_obs_pw.h" #include +#include namespace ModuleIO { /** @@ -14,7 +15,10 @@ void write_td_spin_pw(const std::string& directory, int step, double time_fs, const std::vector& orbitals, - const pw::TDSpinOptsPW& options, + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque, bool spin_current, const pw::TDSpinResultPW& spin, const pw::TDVelResultPW& current, diff --git a/source/source_pw/module_rt/prop_cn_pw.cpp b/source/source_pw/module_rt/prop_cn_pw.cpp index 0bc08078129..20bdb99fab8 100644 --- a/source/source_pw/module_rt/prop_cn_pw.cpp +++ b/source/source_pw/module_rt/prop_cn_pw.cpp @@ -6,41 +6,54 @@ namespace pw { template CNPropPW::CNPropPW(const ModulePW::PW_Basis_K& basis, - const hsolver::PWLinearOptions& cn_opts, + const hsolver::CNControls& cn_ctrl, + int npol, + const std::vector& global_k, const hsolver::diag_comm_info& comm, std::ostream& log) - : solver_(basis, cn_opts, comm, log) + : solver_(basis, cn_ctrl, npol, global_k, comm, log) { } + template void CNPropPW::invalidate_basis() { solver_.invalidate_basis(); } + template -void CNPropPW::evolve(PropArgs& args, psi::Psi* current) +void CNPropPW::evolve(TDHamiltPW& hamilt, + const TDHistoryPW& history, + elecstate::Potential& potential, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) { using Real = typename T::value_type; - const Real* potential = args.potential.template get_veff_smooth_data(); - if (args.scf_iter > 1) + const Real* veff = potential.template get_veff_smooth_data(); + if (scf_iter > 1) { - const ModuleBase::matrix& shape = args.potential.get_veff_smooth(); + const ModuleBase::matrix& shape = potential.get_veff_smooth(); const int size = shape.nr * shape.nc; hsolver::linear_buffer(&veff_mid_, size); Real* buffer = veff_mid_.template data(); - const Real* previous = args.history.left_potential(); - ModuleBase::vector_add_vector_op()(size, buffer, previous, Real(0.5), potential, Real(0.5)); - potential = buffer; + const Real* previous = history.left_potential(); + ModuleBase::vector_add_vector_op()(size, buffer, previous, Real(0.5), veff, Real(0.5)); + veff = buffer; } + ModuleBase::Vector3 vecpot_ha(0, 0, 0); - if (args.field.gauge() == 1) + if (field.gauge() == 1) { - vecpot_ha = args.field.A_prop_ha(); + vecpot_ha = field.A_prop_ha(); } - args.hamilt.bind(potential, vecpot_ha); - const auto& previous = args.history.previous(); - const double dt = args.field.interval_dt_ha(); - solver_.solve(args.hamilt, previous, current, dt, vecpot_ha, args.elec_step, args.scf_iter, args.log_detail); + + hamilt.bind(veff, vecpot_ha); + const auto& previous = history.previous(); + const double dt = field.interval_dt_ha(); + solver_.solve(hamilt, previous, current, dt, vecpot_ha, elec_step, scf_iter, log_detail); } template class CNPropPW, base_device::DEVICE_CPU>; template class CNPropPW, base_device::DEVICE_CPU>; diff --git a/source/source_pw/module_rt/prop_cn_pw.h b/source/source_pw/module_rt/prop_cn_pw.h index 353d40b6de1..75fb3ae9eac 100644 --- a/source/source_pw/module_rt/prop_cn_pw.h +++ b/source/source_pw/module_rt/prop_cn_pw.h @@ -1,5 +1,6 @@ #ifndef PW_PROP_CN_PW_H #define PW_PROP_CN_PW_H +#include "source_hsolver/module_rt_pw/cn_solver_pw.h" #include "source_pw/module_rt/td_prop_pw.h" namespace pw @@ -13,7 +14,9 @@ class CNPropPW final : public PWProp public: CNPropPW(const ModulePW::PW_Basis_K& basis, - const hsolver::PWLinearOptions& cn_opts, + const hsolver::CNControls& cn_ctrl, + int npol, + const std::vector& global_k, const hsolver::diag_comm_info& comm, std::ostream& log); void begin_trial() override @@ -23,7 +26,14 @@ class CNPropPW final : public PWProp { } void invalidate_basis() override; - void evolve(PropArgs& args, psi::Psi* current) override; + void evolve(TDHamiltPW& hamilt, + const TDHistoryPW& history, + elecstate::Potential& potential, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) override; }; } // namespace pw #endif diff --git a/source/source_pw/module_rt/prop_etrs_pw.cpp b/source/source_pw/module_rt/prop_etrs_pw.cpp index af2886d7920..7ac632d8320 100644 --- a/source/source_pw/module_rt/prop_etrs_pw.cpp +++ b/source/source_pw/module_rt/prop_etrs_pw.cpp @@ -9,49 +9,57 @@ namespace pw { template -ETRSPropPW::ETRSPropPW(const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log) - : pool_comm_(comm), orb_opts_(prop_opts.orb), log_(log) +ETRSPropPW::ETRSPropPW(const hsolver::ExpControls& exp_ctrl, + int npol, + const std::vector& global_k, + const hsolver::diag_comm_info& comm, + std::ostream& log) + : pool_comm_(comm), npol_(npol), global_k_(global_k), log_(log) { - exponential_ = hsolver::make_exp_action(prop_opts.exp, comm); + exponential_ = hsolver::make_exp_action(exp_ctrl, comm); log_ << " PW RT-TDDFT propagator: ETRS; exponential: "; - if (prop_opts.exp.method == hsolver::ExpMethod::taylor) + if (exp_ctrl.expansion == hsolver::ExpMethod::taylor) { - log_ << "taylor; order=" << prop_opts.exp.taylor_order << "; predictor=shared powers\n"; + log_ << "taylor; order=" << exp_ctrl.taylor_order << "; predictor=shared powers\n"; } else { const double rel_tol = exponential_->rel_tol(); - log_ << "lanczos; tolerance=" << rel_tol << "; maximum dimension=" << prop_opts.exp.krylov_max << '\n'; + log_ << "lanczos; tolerance=" << rel_tol << "; maximum dimension=" << exp_ctrl.krylov_max << '\n'; } } + template void ETRSPropPW::begin_trial() { half_ready_ = false; } + template void ETRSPropPW::discard_trial() { half_ready_ = false; } + template void ETRSPropPW::invalidate_basis() { half_ready_ = false; } + template -void ETRSPropPW::report(const hsolver::ExpResult& result, const PropArgs& args, int ik) const +void ETRSPropPW::report(const hsolver::ExpResult& result, int ik, int elec_step, int scf_iter, bool log_detail) const { std::ostringstream text; - text << " PW ETRS exponential: step=" << args.elec_step << " scf_iter=" << args.scf_iter - << " global_k=" << orb_opts_.global_k_indices.at(ik) + 1 << " order_or_dimension=" << result.order_or_dim - << " operator_calls=" << result.hpsi_calls << " operator_columns=" << result.hpsi_cols << " subdivisions=" << result.bisect_count + text << " PW ETRS exponential: step=" << elec_step << " scf_iter=" << scf_iter << " global_k=" << global_k_.at(ik) + 1 + << " order_or_dimension=" << result.order_or_dim << " operator_calls=" << result.hpsi_calls + << " operator_columns=" << result.hpsi_cols << " subdivisions=" << result.bisect_count << " reorthogonalizations=" << result.reorth_count; if (result.has_err_est) { text << " estimated_error=" << result.rel_err_est; } - if (args.log_detail) + if (log_detail) { log_ << text.str() << '\n'; } @@ -72,12 +80,20 @@ void ETRSPropPW::report(const hsolver::ExpResult& result, const PropA ModuleBase::WARNING_QUIT("ETRSPropPW", message); } } + template -void ETRSPropPW::evolve(PropArgs& args, psi::Psi* current) +void ETRSPropPW::evolve(TDHamiltPW& hamilt, + const TDHistoryPW& history, + elecstate::Potential& potential, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) { using Real = typename T::value_type; - const bool predictor = args.scf_iter == 1; - const auto& previous = args.history.previous(); + const bool predictor = scf_iter == 1; + const auto& previous = history.previous(); const int ld = current->get_nbasis(); const int bands = current->get_nbands(); const int64_t stride = static_cast(ld) * bands; @@ -90,38 +106,38 @@ void ETRSPropPW::evolve(PropArgs& args, psi::Psi(&half_psi_, size); - left_gen_ = args.history.generation(); - args.hamilt.bind(args.history.left_potential(), args.field.A_left_ha()); + left_gen_ = history.generation(); + hamilt.bind(history.left_potential(), field.A_left_ha()); } else { - if (!half_ready_ || left_gen_ != args.history.generation()) + if (!half_ready_ || left_gen_ != history.generation()) { ModuleBase::WARNING_QUIT("ETRSPropPW", "Missing left half-step for the current trial."); } - const Real* endpoint = args.potential.template get_veff_smooth_data(); - args.hamilt.bind(endpoint, args.field.A_right_ha()); + const Real* endpoint = potential.template get_veff_smooth_data(); + hamilt.bind(endpoint, field.A_right_ha()); } - const double alpha = args.field.interval_dt_ha() / 4.0; + const double alpha = field.interval_dt_ha() / 4.0; for (int ik = 0; ik < current->get_nk(); ++ik) { - args.hamilt.update_k(ik); + hamilt.update_k(ik); current->fix_k(ik); previous.fix_k(ik); - const hsolver::OrbLayout layout{ld, current->get_ngk(ik), orb_opts_.npol, bands}; + const hsolver::OrbLayout layout{ld, current->get_ngk(ik), npol_, bands}; T* output = current->get_pointer(); T* half = half_psi_.template data() + ik * stride; hsolver::ExpResult result; if (predictor) { const T* input = previous.get_pointer(); - result = exponential_->apply_pair(args.hamilt, layout, alpha, input, half, output); + result = exponential_->apply_pair(hamilt, layout, alpha, input, half, output); } else { - result = exponential_->apply(args.hamilt, layout, alpha, half, output); + result = exponential_->apply(hamilt, layout, alpha, half, output); } - report(result, args, ik); + report(result, ik, elec_step, scf_iter, log_detail); } half_ready_ = true; } diff --git a/source/source_pw/module_rt/prop_etrs_pw.h b/source/source_pw/module_rt/prop_etrs_pw.h index 2cc838e2a66..91b1e533a58 100644 --- a/source/source_pw/module_rt/prop_etrs_pw.h +++ b/source/source_pw/module_rt/prop_etrs_pw.h @@ -1,6 +1,7 @@ #ifndef PW_PROP_ETRS_PW_H #define PW_PROP_ETRS_PW_H #include "source_hsolver/diag_comm_info.h" +#include "source_hsolver/module_rt_pw/exp_action.h" #include "source_pw/module_rt/td_prop_pw.h" namespace pw @@ -12,18 +13,30 @@ class ETRSPropPW final : public PWProp const hsolver::diag_comm_info pool_comm_; std::unique_ptr> exponential_; ct::Tensor half_psi_; - hsolver::TDOrbOpts orb_opts_; + const int npol_; + const std::vector global_k_; std::ostream& log_; bool half_ready_ = false; unsigned long long left_gen_ = 0; - void report(const hsolver::ExpResult& result, const PropArgs& args, int ik) const; + void report(const hsolver::ExpResult& result, int ik, int elec_step, int scf_iter, bool log_detail) const; public: - ETRSPropPW(const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log); + ETRSPropPW(const hsolver::ExpControls& exp_ctrl, + int npol, + const std::vector& global_k, + const hsolver::diag_comm_info& comm, + std::ostream& log); void begin_trial() override; void discard_trial() override; void invalidate_basis() override; - void evolve(PropArgs& args, psi::Psi* current) override; + void evolve(TDHamiltPW& hamilt, + const TDHistoryPW& history, + elecstate::Potential& potential, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) override; }; } // namespace pw #endif diff --git a/source/source_pw/module_rt/td_obs_pw.h b/source/source_pw/module_rt/td_obs_pw.h index abdccfa03e1..baaa91e87db 100644 --- a/source/source_pw/module_rt/td_obs_pw.h +++ b/source/source_pw/module_rt/td_obs_pw.h @@ -2,13 +2,12 @@ #define TD_OBS_PW_H #include -#include #include namespace pw { /** @brief Independent requests; all ranks must use the same choices. */ -struct TDVelRequestPW +struct CurrentRequest { bool charge_total = false; bool charge_per_k = false; @@ -23,15 +22,6 @@ struct TDVelResultPW std::array spin{}; ///< Spin angular momentum flux, [flow][spin], hbar/(Bohr^2*atomic time). }; -/** @brief Magnetic choices fixed at initialization, independent of ionic positions. */ -struct TDSpinOptsPW -{ - std::string method = "none"; - std::string orbital_dir; - double radius = 0.0; ///< Onsite cutoff in Bohr. - bool torque = false; -}; - /** @brief Endpoint observables replicated on all ranks after the appropriate reductions. */ struct TDSpinResultPW { diff --git a/source/source_pw/module_rt/td_prop_pw.cpp b/source/source_pw/module_rt/td_prop_pw.cpp index 662c65af7f3..46499b43924 100644 --- a/source/source_pw/module_rt/td_prop_pw.cpp +++ b/source/source_pw/module_rt/td_prop_pw.cpp @@ -3,59 +3,59 @@ #include "source_pw/module_rt/prop_cn_pw.h" #include "source_pw/module_rt/prop_etrs_pw.h" +#include + namespace pw { -TDPropOpts td_prop_options(const Input_para& input, int npol, const std::vector& global_k) +template +std::unique_ptr> make_td_prop(const ModulePW::PW_Basis_K& basis, + const Input_para& input, + int npol, + const std::vector& global_k, + const hsolver::diag_comm_info& comm, + std::ostream& log) { - TDPropOpts prop_opts; - prop_opts.orb.npol = npol; - prop_opts.orb.global_k_indices = global_k; - prop_opts.orb.orthonormal = hsolver::parse_orth_method(input.td_orthonormal); - prop_opts.orb.out_stat = input.td_out_stat; + const hsolver::OrthMethod orth_method = hsolver::parse_orth_method(input.td_orthonormal); + std::unique_ptr> orb_ops( + new hsolver::TDOrbOps(npol, orth_method, input.td_out_stat, global_k, comm, log)); + std::unique_ptr> prop; if (input.propagator == 0) { - prop_opts.prop_kind = PropKind::cn; - prop_opts.cn.npol = npol; - prop_opts.cn.global_k_indices = global_k; - prop_opts.cn.linear.method = hsolver::parse_linear_method(input.lin_solver); - prop_opts.cn.linear.tolerance = input.lin_thr; - prop_opts.cn.linear.max_iterations = input.lin_maxiter; - prop_opts.cn.linear.restart = input.lin_gmres_restart; - prop_opts.cn.linear.reconstruct = input.lin_reconstruct; - prop_opts.cn.preconditioner = hsolver::parse_pw_precond(input.lin_precond); - prop_opts.cn.cn_init = input.td_cn_init; - prop_opts.cn.kinetic_enabled = input.t_in_h; + hsolver::CNControls cn_ctrl; + cn_ctrl.linear.solver = hsolver::parse_linear_method(input.lin_solver); + cn_ctrl.linear.tolerance = input.lin_thr; + cn_ctrl.linear.max_iterations = input.lin_maxiter; + cn_ctrl.linear.restart = input.lin_gmres_restart; + cn_ctrl.linear.reconstruct = input.lin_reconstruct; + cn_ctrl.preconditioner = hsolver::parse_pw_precond(input.lin_precond); + cn_ctrl.cn_init = input.td_cn_init; + cn_ctrl.kinetic_enabled = input.t_in_h; + prop.reset(new CNPropPW(basis, cn_ctrl, npol, global_k, comm, log)); } else { - prop_opts.prop_kind = PropKind::etrs; + hsolver::ExpControls exp_ctrl; if (input.td_exp_method == "taylor") { - prop_opts.exp.method = hsolver::ExpMethod::taylor; + exp_ctrl.expansion = hsolver::ExpMethod::taylor; } - prop_opts.exp.taylor_order = input.td_exp_order; - prop_opts.exp.rel_tol = input.td_exp_thr; - prop_opts.exp.krylov_max = input.td_exp_krylov_dim; + exp_ctrl.taylor_order = input.td_exp_order; + exp_ctrl.rel_tol = input.td_exp_thr; + exp_ctrl.krylov_max = input.td_exp_krylov_dim; + prop.reset(new ETRSPropPW(exp_ctrl, npol, global_k, comm, log)); } - return prop_opts; + + return std::unique_ptr>(new TDPropPW(basis, std::move(orb_ops), std::move(prop))); } template TDPropPW::TDPropPW(const ModulePW::PW_Basis_K& basis, - const TDPropOpts& prop_opts, - const hsolver::diag_comm_info& comm, - std::ostream& log) - : basis_(basis), orb_ops_(prop_opts.orb, comm, log) + std::unique_ptr> orb_ops, + std::unique_ptr> prop) + : basis_(basis), orb_ops_(std::move(orb_ops)), prop_(std::move(prop)) { - if (prop_opts.prop_kind == PropKind::cn) - { - prop_.reset(new CNPropPW(basis, prop_opts.cn, comm, log)); - } - else - { - prop_.reset(new ETRSPropPW(prop_opts, comm, log)); - } } + template PropStage TDPropPW::stage(int scf_iter) const { @@ -93,17 +93,40 @@ void TDPropPW::evolve(hamilt::HamiltPW& hamilt, { using Real = typename T::value_type; TDHamiltPW td_hamilt(hamilt, basis_); - PropArgs args{td_hamilt, history, potential, field, elec_step, scf_iter, log_detail}; - prop_->evolve(args, current); - orb_ops_.correct(current, elec_step, scf_iter, log_detail); + prop_->evolve(td_hamilt, history, potential, field, current, elec_step, scf_iter, log_detail); + orb_ops_->correct(current, elec_step, scf_iter, log_detail); const Real* endpoint = potential.template get_veff_smooth_data(); const auto& vecpot_ha = field.A_right_ha(); td_hamilt.bind(endpoint, vecpot_ha); } template class TDPropPW, base_device::DEVICE_CPU>; +template std::unique_ptr, base_device::DEVICE_CPU>> make_td_prop(const ModulePW::PW_Basis_K&, + const Input_para&, + int, + const std::vector&, + const hsolver::diag_comm_info&, + std::ostream&); template class TDPropPW, base_device::DEVICE_CPU>; +template std::unique_ptr, base_device::DEVICE_CPU>> make_td_prop(const ModulePW::PW_Basis_K&, + const Input_para&, + int, + const std::vector&, + const hsolver::diag_comm_info&, + std::ostream&); #if defined(__CUDA) || defined(__ROCM) template class TDPropPW, base_device::DEVICE_GPU>; +template std::unique_ptr, base_device::DEVICE_GPU>> make_td_prop(const ModulePW::PW_Basis_K&, + const Input_para&, + int, + const std::vector&, + const hsolver::diag_comm_info&, + std::ostream&); template class TDPropPW, base_device::DEVICE_GPU>; +template std::unique_ptr, base_device::DEVICE_GPU>> make_td_prop(const ModulePW::PW_Basis_K&, + const Input_para&, + int, + const std::vector&, + const hsolver::diag_comm_info&, + std::ostream&); #endif } // namespace pw diff --git a/source/source_pw/module_rt/td_prop_pw.h b/source/source_pw/module_rt/td_prop_pw.h index 16a91af3e3e..79a452e09fb 100644 --- a/source/source_pw/module_rt/td_prop_pw.h +++ b/source/source_pw/module_rt/td_prop_pw.h @@ -1,8 +1,6 @@ #ifndef PW_TD_PROP_PW_H #define PW_TD_PROP_PW_H #include "source_estate/module_pot/td_field_manager.h" -#include "source_hsolver/module_rt_pw/cn_solver_pw.h" -#include "source_hsolver/module_rt_pw/exp_action.h" #include "source_hsolver/module_rt_pw/td_orbital_ops.h" #include "source_io/module_parameter/input_parameter.h" #include "source_pw/module_rt/td_hamilt_pw.h" @@ -15,34 +13,6 @@ enum class PropStage predictor, corrector }; -enum class PropKind -{ - cn, - etrs -}; - -/** @brief Resolved input controls, passed explicitly to numerical services. */ -struct TDPropOpts -{ - PropKind prop_kind = PropKind::cn; - hsolver::TDOrbOpts orb; - hsolver::PWLinearOptions cn; - hsolver::ExpOpts exp; -}; -TDPropOpts td_prop_options(const Input_para& input, int npol, const std::vector& global_k); - -/** @brief Borrowed state for a single predictor or endpoint iteration. */ -template -struct PropArgs -{ - TDHamiltPW& hamilt; - const TDHistoryPW& history; - elecstate::Potential& potential; - const elecstate::TDFieldManager& field; - int elec_step; - int scf_iter; - bool log_detail; -}; /** @brief Method-specific wavefunction map; density and acceptance stay above it. */ template @@ -53,7 +23,14 @@ class PWProp virtual void begin_trial() = 0; virtual void discard_trial() = 0; virtual void invalidate_basis() = 0; - virtual void evolve(PropArgs& args, psi::Psi* current) = 0; + virtual void evolve(TDHamiltPW& hamilt, + const TDHistoryPW& history, + elecstate::Potential& potential, + const elecstate::TDFieldManager& field, + psi::Psi* current, + int elec_step, + int scf_iter, + bool log_detail) = 0; }; /** @brief Sole PW propagation entry point, independent of the selected method. */ @@ -62,11 +39,14 @@ class TDPropPW { private: const ModulePW::PW_Basis_K& basis_; - hsolver::TDOrbOps orb_ops_; + std::unique_ptr> orb_ops_; std::unique_ptr> prop_; public: - TDPropPW(const ModulePW::PW_Basis_K& basis, const TDPropOpts& prop_opts, const hsolver::diag_comm_info& comm, std::ostream& log); + /** @brief Take ownership of the initialized orbital service and propagator. */ + TDPropPW(const ModulePW::PW_Basis_K& basis, + std::unique_ptr> orb_ops, + std::unique_ptr> prop); PropStage stage(int scf_iter) const; void begin_trial(); void finish_trial(); @@ -74,7 +54,7 @@ class TDPropPW void invalidate_basis(); hsolver::TDOrbOps& orbitals() { - return orb_ops_; + return *orb_ops_; } /** @brief Restore the endpoint sample before returning to density construction. */ void evolve(hamilt::HamiltPW& hamilt, @@ -86,5 +66,14 @@ class TDPropPW int scf_iter, bool log_detail); }; + +/** @brief Resolve input controls and construct only the selected propagation method. */ +template +std::unique_ptr> make_td_prop(const ModulePW::PW_Basis_K& basis, + const Input_para& input, + int npol, + const std::vector& global_k, + const hsolver::diag_comm_info& comm, + std::ostream& log); } // namespace pw #endif diff --git a/source/source_pw/module_rt/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp index 3821de4a333..50f941d3786 100644 --- a/source/source_pw/module_rt/td_spin_pw.cpp +++ b/source/source_pw/module_rt/td_spin_pw.cpp @@ -93,13 +93,16 @@ void TDSpinPW::initialize(const UnitCell& cell, const K_Vectors& kv, Structure_Factor& sf, pseudopot_cell_vnl& pp, - const TDSpinOptsPW& options) + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque) { if (cell_ != nullptr || cell.get_npol() != 2) { ModuleBase::WARNING_QUIT("TDSpinPW", "Initialize once with a spinor basis."); } - if (options.method != "none" && options.method != "voronoi" && options.method != "onsite") + if (mag_partition != "none" && mag_partition != "voronoi" && mag_partition != "onsite") { ModuleBase::WARNING_QUIT("TDSpinPW", "Unknown magnetic partition."); } @@ -109,7 +112,10 @@ void TDSpinPW::initialize(const UnitCell& cell, kv_ = &kv; sf_ = &sf; pp_ = &pp; - options_ = options; + mag_partition_ = mag_partition; + orbital_dir_ = orbital_dir; + onsite_radius_ = onsite_radius; + calc_torque_ = calc_torque; } template @@ -149,8 +155,7 @@ void TDSpinPW::density(const elecstate::ElecState& state, TDSpinRe { const UnitCell& cell = *cell_; const ModulePW::PW_Basis& grid = *grid_; - const TDSpinOptsPW& options = options_; - const bool voronoi = options.method == "voronoi"; + const bool voronoi = mag_partition_ == "voronoi"; if (voronoi && labels_.size() != static_cast(grid.nrxx)) { labels_ = td_voronoi_pw(cell, grid); @@ -175,7 +180,7 @@ void TDSpinPW::density(const elecstate::ElecState& state, TDSpinRe { result->atom_mag[3 * labels_[ir] + a] += m * volume; } - if (options.torque) + if (calc_torque_) { // The effective spin field is in Ry; the angular-momentum torque here uses Hartree time. const int b = (a + 1) % 3; @@ -298,7 +303,6 @@ void TDSpinPW::onsite(const psi::Psi& psi, const ModulePW::PW_Basis_K& basis = *basis_; const K_Vectors& kv = *kv_; Structure_Factor& sf = *sf_; - const TDSpinOptsPW& options = options_; // Onsite owns its atomic result; the independently integrated total moment remains unchanged. result->atom_mag.assign(3 * cell.nat, 0.0); const double dq = pp_->table_dq(); @@ -312,7 +316,7 @@ void TDSpinPW::onsite(const psi::Psi& psi, onsite_projector_.reset(new projectors::OnsiteProjector()); q_bound_ = td_momentum_bound(basis, cell.tpiba); const int nq = static_cast(std::ceil(q_bound_ / dq)) + 4; - onsite_projector_->init(options.orbital_dir, &cell, *onsite_psi_, kv, basis, sf, options.radius, nq, dq, state.wg, state.ekb); + onsite_projector_->init(orbital_dir_, &cell, *onsite_psi_, kv, basis, sf, onsite_radius_, nq, dq, state.wg, state.ekb); } auto* projector = onsite_projector_.get(); if (onsite_psi_->get_nbands() != nbands || onsite_psi_->get_nbasis() != ld_psi) @@ -383,11 +387,11 @@ TDSpinResultPW TDSpinPW::calculate(const psi::Psi const int old_k = psi.get_current_k(); TDSpinResultPW result; density(state, &result); - if (options_.method == "onsite") + if (mag_partition_ == "onsite") { onsite(psi, state, a, &result); } - if (options_.torque) + if (calc_torque_) { soc(psi, state.wg, a, &result); } diff --git a/source/source_pw/module_rt/td_spin_pw.h b/source/source_pw/module_rt/td_spin_pw.h index 3774265fba5..84b6f286c6a 100644 --- a/source/source_pw/module_rt/td_spin_pw.h +++ b/source/source_pw/module_rt/td_spin_pw.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace pw @@ -28,7 +29,10 @@ class TDSpinPW const K_Vectors* kv_ = nullptr; Structure_Factor* sf_ = nullptr; pseudopot_cell_vnl* pp_ = nullptr; - TDSpinOptsPW options_; + std::string mag_partition_; + std::string orbital_dir_; + double onsite_radius_ = 0.0; + bool calc_torque_ = false; std::vector> pos_cache_; std::vector labels_; hamilt::NonlocalWorkspace nonlocal_; @@ -54,6 +58,10 @@ class TDSpinPW public: /** @brief Bind once; dependencies outlive this object and retain their cell and layout. * The caller must refresh structure factors and electronic data before sampling moved ions. + * @param mag_partition Atomic moment partition: none, voronoi, or onsite. + * @param orbital_dir Directory containing the onsite projector orbitals. + * @param onsite_radius Onsite projector cutoff in Bohr. + * @param calc_torque Whether to calculate XC and SOC spin torques. */ void initialize(const UnitCell& cell, const ModulePW::PW_Basis& grid, @@ -61,7 +69,10 @@ class TDSpinPW const K_Vectors& kv, Structure_Factor& sf, pseudopot_cell_vnl& pp, - const TDSpinOptsPW& options); + const std::string& mag_partition, + const std::string& orbital_dir, + double onsite_radius, + bool calc_torque); /** @brief Sample all ranks and restore the k cursor; coordinate changes invalidate geometric caches. */ TDSpinResultPW calculate(const psi::Psi& psi, const elecstate::ElecState& state, const ModuleBase::Vector3& a); }; diff --git a/source/source_pw/module_rt/td_vel_pw.cpp b/source/source_pw/module_rt/td_vel_pw.cpp index 05dcd1bdd61..851cda67814 100644 --- a/source/source_pw/module_rt/td_vel_pw.cpp +++ b/source/source_pw/module_rt/td_vel_pw.cpp @@ -27,7 +27,7 @@ template TDVelResultPW TDVelPW::calculate(const psi::Psi& psi, const elecstate::ElecState& state, const ModuleBase::Vector3& a, - const TDVelRequestPW& request) + const CurrentRequest& request) { ModuleBase::timer::start("TDVelPW", "calculate"); if (cell_ == nullptr) diff --git a/source/source_pw/module_rt/td_vel_pw.h b/source/source_pw/module_rt/td_vel_pw.h index 927f45b7123..7ca602f0540 100644 --- a/source/source_pw/module_rt/td_vel_pw.h +++ b/source/source_pw/module_rt/td_vel_pw.h @@ -38,7 +38,7 @@ class TDVelPW TDVelResultPW calculate(const psi::Psi& psi, const elecstate::ElecState& state, const ModuleBase::Vector3& a, - const TDVelRequestPW& request); + const CurrentRequest& request); }; } // namespace pw #endif diff --git a/source/source_pw/module_rt/test/test_td_spin_pw.cpp b/source/source_pw/module_rt/test/test_td_spin_pw.cpp index b72c02d6517..c60b855a5e6 100644 --- a/source/source_pw/module_rt/test/test_td_spin_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_spin_pw.cpp @@ -8,10 +8,9 @@ namespace td_test TEST_F(Observables, SOCTorqueMatchesCommutator) { const ModuleBase::Vector3 a(0.13, -0.07, 0.09); - pw::TDSpinOptsPW options; - options.torque = true; + const std::string mag_partition = "none"; pw::TDSpinPW observer; - observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, options); + observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, mag_partition, "", 0.0, true); const auto actual = observer.calculate(*psi_, state_, a); const Vector beta = projectors(a); const int n = psi_->get_nbasis(); @@ -88,11 +87,9 @@ TEST_F(Observables, DensityMomentsAndPeriodicVoronoi) state_.pot->get_eff_v()(2, ir) = 0.2; state_.pot->get_eff_v()(3, ir) = -0.4; } - pw::TDSpinOptsPW options; - options.method = "voronoi"; - options.torque = true; + const std::string mag_partition = "voronoi"; pw::TDSpinPW observer; - observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, options); + observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, mag_partition, "", 0.0, true); const ModuleBase::Vector3 zero(0, 0, 0); const auto actual = observer.calculate(*psi_, state_, zero); const double integral = cell_.omega * (1.0 + 0.005 * (basis_.nxyz - 1)); diff --git a/source/source_pw/module_rt/test/test_td_vel_pw.cpp b/source/source_pw/module_rt/test/test_td_vel_pw.cpp index fe7d1d07023..bb9214b607f 100644 --- a/source/source_pw/module_rt/test/test_td_vel_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_vel_pw.cpp @@ -9,7 +9,7 @@ TEST_F(Observables, CurrentsMatchPauliAndHamiltonianDerivative) const ModuleBase::Vector3 a(0.13, -0.07, 0.09); pw::TDVelPW observer; observer.initialize(cell_, basis_, kv_, pp_); - pw::TDVelRequestPW request; + pw::CurrentRequest request; request.charge_total = true; request.charge_per_k = true; request.spin_current = true; From f486f02b03341ce6c2d3d0fa25293f2e06e13f22 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 19:40:10 +0800 Subject: [PATCH 17/33] Fix PW RT-TDDFT workspace initialization for single-band calculations and empty plane-wave partitions --- source/source_pw/module_rt/td_spin_pw.cpp | 6 ++++-- source/source_pw/module_rt/td_vel_pw.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/source/source_pw/module_rt/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp index 50f941d3786..f91ce6ca33a 100644 --- a/source/source_pw/module_rt/td_spin_pw.cpp +++ b/source/source_pw/module_rt/td_spin_pw.cpp @@ -222,14 +222,16 @@ void TDSpinPW::soc(const psi::Psi& psi, return; } using CtDevice = typename ct::PsiToContainer::type; + const ct::DeviceType device = ct::DeviceTypeToEnum::value; const int nbands = psi.get_nbands(); // Projection kernels count spin-component columns, not physical bands. const int ncols = 2 * nbands; const int ld_psi = psi.get_nbasis(); const std::int64_t size = std::max(1, static_cast(pp.nkb) * basis.npwk_max); - if (beta_.NumElements() < size) + // Empty local PW partitions still need a correctly typed buffer before projector reductions. + if (beta_.NumElements() < size || beta_.data_type() != ct::DataTypeToEnum::value || beta_.device_type() != device) { - beta_ = ct::Tensor(ct::DataTypeToEnum::value, ct::DeviceTypeToEnum::value, {size}); + beta_ = ct::Tensor(ct::DataTypeToEnum::value, device, {size}); } Complex* beta = beta_.template data(); const size_t count = static_cast(pp.nkb) * ncols; diff --git a/source/source_pw/module_rt/td_vel_pw.cpp b/source/source_pw/module_rt/td_vel_pw.cpp index 851cda67814..17deb9fce21 100644 --- a/source/source_pw/module_rt/td_vel_pw.cpp +++ b/source/source_pw/module_rt/td_vel_pw.cpp @@ -63,11 +63,12 @@ TDVelResultPW TDVelPW::calculate(const psi::Psi& const std::int64_t vel_size = 3 * comp_size; const std::int64_t capacity = std::max(1, vel_size); const std::int64_t dot_size = std::max(1, ncols); - if (vpsi_.NumElements() < capacity) + // Default tensors contain one CPU float element; capacity alone does not establish a usable buffer. + if (vpsi_.NumElements() < capacity || vpsi_.data_type() != ct::DataTypeToEnum::value || vpsi_.device_type() != device) { vpsi_ = ct::Tensor(ct::DataTypeToEnum::value, device, {capacity}); } - if (dots_.NumElements() < dot_size) + if (dots_.NumElements() < dot_size || dots_.data_type() != ct::DataTypeToEnum::value || dots_.device_type() != device) { dots_ = ct::Tensor(ct::DataTypeToEnum::value, device, {dot_size}); } From f481af3c03845b460742b4cb63d78b27d29b580f Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 19:50:07 +0800 Subject: [PATCH 18/33] Move exponential workspace into Lanczos and remove unused TD history include --- source/source_hsolver/module_rt_pw/exp_action.cpp | 3 +-- source/source_hsolver/module_rt_pw/exp_action.h | 2 -- source/source_hsolver/module_rt_pw/exp_lanczos.cpp | 2 +- source/source_hsolver/module_rt_pw/exp_lanczos.h | 2 ++ source/source_pw/module_rt/td_history_pw.cpp | 1 - 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/source/source_hsolver/module_rt_pw/exp_action.cpp b/source/source_hsolver/module_rt_pw/exp_action.cpp index 31d4c769550..56c9bc71387 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/exp_action.cpp @@ -9,8 +9,7 @@ namespace hsolver { template -ExpAction::ExpAction(const ExpControls& exp_ctrl, const diag_comm_info& comm) - : exp_ctrl_(exp_ctrl), algebra_(comm), work_(comm, 1) +ExpAction::ExpAction(const ExpControls& exp_ctrl, const diag_comm_info& comm) : exp_ctrl_(exp_ctrl), algebra_(comm) { using Real = typename T::value_type; if (exp_ctrl_.rel_tol == 0.0) diff --git a/source/source_hsolver/module_rt_pw/exp_action.h b/source/source_hsolver/module_rt_pw/exp_action.h index 4762aba85d6..39b32639b7d 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.h +++ b/source/source_hsolver/module_rt_pw/exp_action.h @@ -3,7 +3,6 @@ #include "source_hsolver/hs_operator.h" #include "source_hsolver/module_rt_pw/linear_algebra.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" #include "source_hsolver/module_rt_pw/orb_buffer.h" #include @@ -54,7 +53,6 @@ class ExpAction protected: ExpControls exp_ctrl_; LinearAlgebra algebra_; - LinearWorkspace work_; public: ExpAction(const ExpControls& exp_ctrl, const diag_comm_info& comm); diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp index a615109f767..af362843f21 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -33,7 +33,7 @@ ExpResult failed_band(ExpResult result, int band, bool retry) template ExpLanczos::ExpLanczos(const ExpControls& exp_ctrl, const diag_comm_info& comm) - : ExpAction(exp_ctrl, comm), comm_(comm) + : ExpAction(exp_ctrl, comm), comm_(comm), work_(comm, 1) { } diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.h b/source/source_hsolver/module_rt_pw/exp_lanczos.h index fc19f00f4f4..c404d19b16d 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.h +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.h @@ -1,6 +1,7 @@ #ifndef HSOLVER_EXP_LANCZOS_H #define HSOLVER_EXP_LANCZOS_H #include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_rt_pw/linear_workspace.h" namespace hsolver { @@ -9,6 +10,7 @@ class ExpLanczos final : public ExpAction { private: const diag_comm_info comm_; + LinearWorkspace work_; ct::Tensor basis_; ct::Tensor image_; ct::Tensor packed_; diff --git a/source/source_pw/module_rt/td_history_pw.cpp b/source/source_pw/module_rt/td_history_pw.cpp index a5470d32616..7809e5451f9 100644 --- a/source/source_pw/module_rt/td_history_pw.cpp +++ b/source/source_pw/module_rt/td_history_pw.cpp @@ -1,6 +1,5 @@ #include "source_pw/module_rt/td_history_pw.h" -#include "source_base/kernels/math_kernel_op.h" #include "source_base/module_device/memory_op.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" From 0a73667f64beb45fc5d62950e0445fb441a00911 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 20:22:34 +0800 Subject: [PATCH 19/33] Set a per-case current tolerance of 5e-10 for the PW ETRS retry regression test --- .../34_PW_ETRS_retry_TDDFT/threshold | 2 ++ .../integrate/validation_tools/CompareFile.py | 22 +++++++++++++++---- .../integrate/validation_tools/props_tddft.sh | 10 ++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/threshold diff --git a/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/threshold b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/threshold new file mode 100644 index 00000000000..0eefe69df0a --- /dev/null +++ b/tests/05_rtTDDFT/34_PW_ETRS_retry_TDDFT/threshold @@ -0,0 +1,2 @@ +# Parallel-layout and threading sweeps measured current deviations up to 3.005e-10. +current_threshold 5e-10 diff --git a/tests/integrate/validation_tools/CompareFile.py b/tests/integrate/validation_tools/CompareFile.py index 4aa6cb13547..ea425babcc3 100755 --- a/tests/integrate/validation_tools/CompareFile.py +++ b/tests/integrate/validation_tools/CompareFile.py @@ -1,8 +1,9 @@ #!/usr/bin/env python +import math import sys usage = ''' -python CompareFile.py file1 file2 [accuracy] [-abs 0] [-com_type 1] +python CompareFile.py file1 file2 [accuracy] [-abs 0] [-com_type 1] [-tol value] accuracy - the accuracy of two float value. default value is 8, which means @@ -12,6 +13,8 @@ -com_type - 0: assure the relative difference of two float value is smaller than accuracy - 1: assure the absolute difference of two float value is smaller than accuracy - Default is 1 +-tol - finite positive comparison tolerance, overriding accuracy + in either absolute or relative difference mode This script is used to compare whether two files are the same. If compared two strings are different OR the difference @@ -23,17 +26,28 @@ ''' def ReadParam(): - if len(sys.argv) not in [3,4,5,6,7,8]: + if len(sys.argv) < 3: print(usage) sys.exit(1) file1 = sys.argv[1] file2 = sys.argv[2] accur = 8 + tolerance = None useAbsoluteVal = False compareAbsoluteDiff = True argi = 3 while argi < len(sys.argv): - if sys.argv[argi] == '-abs': + if sys.argv[argi] == '-tol': + argi += 1 + if argi >= len(sys.argv): + sys.exit('Error: -tol requires a finite positive number.') + try: + tolerance = float(sys.argv[argi]) + except ValueError: + sys.exit('Error: -tol requires a finite positive number.') + if not math.isfinite(tolerance) or tolerance <= 0: + sys.exit('Error: -tol requires a finite positive number.') + elif sys.argv[argi] == '-abs': argi += 1 useAbsoluteVal = bool(int(sys.argv[argi])) elif sys.argv[argi] == '-com_type': @@ -42,7 +56,7 @@ def ReadParam(): else: accur = int(sys.argv[argi]) argi += 1 - epsilon = 0.1**accur + epsilon = 0.1**accur if tolerance is None else tolerance return file1,file2,epsilon,useAbsoluteVal,compareAbsoluteDiff def is_two_data_diff(data1,data2,epsilon,compareAbsoluteDiff): diff --git a/tests/integrate/validation_tools/props_tddft.sh b/tests/integrate/validation_tools/props_tddft.sh index fa9a85efe9c..d3b30991fda 100644 --- a/tests/integrate/validation_tools/props_tddft.sh +++ b/tests/integrate/validation_tools/props_tddft.sh @@ -24,7 +24,15 @@ done if ! test -z "$out_current" && [ $out_current ]; then current1ref=current_tot.txt.ref current1cal=OUT.autotest/current_tot.txt - python3 $COMPARE_SCRIPT $current1ref $current1cal 10 + local current_tol=1e-10 + if [ -f threshold ]; then + local configured_tol + configured_tol=$(get_input_key_value current_threshold threshold) + if [ -n "$configured_tol" ]; then + current_tol=$configured_tol + fi + fi + python3 "$COMPARE_SCRIPT" "$current1ref" "$current1cal" -tol "$current_tol" echo "CompareCurrent_pass $?" >>$props_result_file fi From 529d4644376a8281005ad334201c8ebfb4ab2885 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 21:24:46 +0800 Subject: [PATCH 20/33] Improve PW regression accuracy and tolerances, refresh references and timings, and replace CUDA triangular inversion with cuBLAS TRSM --- .../ATen/kernels/cuda/blas.cu | 86 + .../ATen/kernels/cuda/lapack.cu | 21 - .../base/third_party/cublas.h | 44 + .../base/third_party/cusolver.h | 42 - tests/01_PW/087_PW_get_pchg_kpar_bndpar/INPUT | 3 +- .../pchgi4s1.cube.ref | 1944 ++++++++--------- .../087_PW_get_pchg_kpar_bndpar/result.ref | 6 +- .../087_PW_get_pchg_kpar_bndpar/threshold | 1 + tests/01_PW/089_PW_get_wf_kpar_bndpar/INPUT | 3 +- .../089_PW_get_wf_kpar_bndpar/result.ref | 110 +- .../01_PW/089_PW_get_wf_kpar_bndpar/threshold | 1 + tests/01_PW/scf_uspp_bpcg/INPUT | 3 +- tests/01_PW/scf_uspp_bpcg/result.ref | 8 +- tests/01_PW/scf_uspp_bpcg/threshold | 1 + tests/11_PW_GPU/scf_bpcg/result.ref | 2 +- tests/integrate/Autotest.sh | 6 +- .../integrate/validation_tools/props_cube.sh | 15 +- 17 files changed, 1193 insertions(+), 1103 deletions(-) create mode 100644 tests/01_PW/087_PW_get_pchg_kpar_bndpar/threshold create mode 100644 tests/01_PW/089_PW_get_wf_kpar_bndpar/threshold create mode 100644 tests/01_PW/scf_uspp_bpcg/threshold diff --git a/source/source_base/module_container/ATen/kernels/cuda/blas.cu b/source/source_base/module_container/ATen/kernels/cuda/blas.cu index 7f794768ad5..3068ed155e0 100644 --- a/source/source_base/module_container/ATen/kernels/cuda/blas.cu +++ b/source/source_base/module_container/ATen/kernels/cuda/blas.cu @@ -1,9 +1,15 @@ #include +#include #include #include #include +#include +#include +#include +#include + namespace container { namespace kernels { @@ -22,6 +28,86 @@ void destroyGpuBlasHandle() { } } +namespace { +template +__global__ void set_identity(T* matrix, const int n) +{ + const size_t count = static_cast(n) * n; + const size_t stride = static_cast(blockDim.x) * gridDim.x; + for (size_t i = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; i < count; i += stride) + { + matrix[i] = i % n == i / n ? T(1) : T(0); + } +} + +template +__global__ void copy_triangle(const T* inverse, T* matrix, const int n, const int lda, const bool upper) +{ + const size_t count = static_cast(n) * n; + const size_t stride = static_cast(blockDim.x) * gridDim.x; + for (size_t i = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; i < count; i += stride) + { + const size_t row = i % n; + const size_t column = i / n; + if (upper ? row <= column : row >= column) + { + matrix[column * lda + row] = inverse[i]; + } + } +} +} // namespace + +template +struct lapack_trtri { + void operator()(const char& uplo, const char& diag, const int& n, T* matrix, const int& lda) + { + if (n < 0 || lda < std::max(1, n)) + { + throw std::invalid_argument("trtri: invalid matrix dimensions"); + } + const bool upper = cublas_fill_mode(uplo) == CUBLAS_FILL_MODE_UPPER; + const bool unit_diag = cublas_diag_type(diag) == CUBLAS_DIAG_UNIT; + if (n == 0) + { + return; + } + if (!unit_diag) + { + // TRSM does not report singularity; preserve TRTRI's zero-diagonal rejection. + std::vector diagonal(n); + const size_t pitch = (static_cast(lda) + 1) * sizeof(T); + CHECK_CUDA(cudaMemcpy2D(diagonal.data(), sizeof(T), matrix, pitch, sizeof(T), n, cudaMemcpyDeviceToHost)); + for (int i = 0; i < n; ++i) + { + if (diagonal[i] == T(0)) + { + throw std::runtime_error("trtri: zero diagonal at index " + std::to_string(i + 1)); + } + } + } + + // Solve A * inverse = I without the Xtrtri workspace overrun seen in CUDA 11.8/12.1. + Tensor inverse(DataTypeToEnum::value, DeviceType::GpuDevice, {n, n}); + T* data = inverse.data(); + using CudaType = typename GetTypeThrust::type; + CudaType* cuda_data = reinterpret_cast(data); + CudaType* cuda_matrix = reinterpret_cast(matrix); + const size_t count = static_cast(n) * n; + const size_t blocks_needed = (count + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + const int blocks = std::min(65535, blocks_needed); + set_identity<<>>(cuda_data, n); + CHECK_CUDA(cudaGetLastError()); + cuBlasConnector::trsm(cublas_handle, uplo, diag, n, matrix, lda, data); + copy_triangle<<>>(cuda_data, cuda_matrix, n, lda, upper); + CHECK_CUDA_SYNC(); + } +}; + +template struct lapack_trtri; +template struct lapack_trtri; +template struct lapack_trtri, DEVICE_GPU>; +template struct lapack_trtri, DEVICE_GPU>; + template struct blas_nrm2 { using Real = typename GetTypeReal::type; diff --git a/source/source_base/module_container/ATen/kernels/cuda/lapack.cu b/source/source_base/module_container/ATen/kernels/cuda/lapack.cu index 4c69973b6be..722ffb99452 100644 --- a/source/source_base/module_container/ATen/kernels/cuda/lapack.cu +++ b/source/source_base/module_container/ATen/kernels/cuda/lapack.cu @@ -67,22 +67,6 @@ struct set_matrix { // --- 1. Matrix Decomposition --- -template -struct lapack_trtri { - void operator()( - const char& uplo, - const char& diag, - const int& dim, - T* Mat, - const int& lda) - { - // TODO: trtri is not implemented in this method yet - // Cause the trtri in cuSolver is not stable for ABACUS! - cuSolverConnector::trtri(cusolver_handle, uplo, diag, dim, Mat, lda); - // cuSolverConnector::potri(cusolver_handle, uplo, diag, dim, Mat, lda); - } -}; - template struct lapack_potrf { void operator()( @@ -343,11 +327,6 @@ template struct set_matrix; template struct set_matrix, DEVICE_GPU>; template struct set_matrix, DEVICE_GPU>; -template struct lapack_trtri; -template struct lapack_trtri; -template struct lapack_trtri, DEVICE_GPU>; -template struct lapack_trtri, DEVICE_GPU>; - template struct lapack_potrf; template struct lapack_potrf; template struct lapack_potrf, DEVICE_GPU>; diff --git a/source/source_base/module_container/base/third_party/cublas.h b/source/source_base/module_container/base/third_party/cublas.h index fabc32e9836..bbf8c6f34bf 100644 --- a/source/source_base/module_container/base/third_party/cublas.h +++ b/source/source_base/module_container/base/third_party/cublas.h @@ -8,6 +8,50 @@ namespace container { namespace cuBlasConnector { +static inline +void trsm(cublasHandle_t& handle, const char uplo, const char diag, const int n, + const float* matrix, const int lda, float* inverse) +{ + const float one = 1.0f; + const cublasFillMode_t triangle = cublas_fill_mode(uplo); + const cublasDiagType_t diagonal = cublas_diag_type(diag); + CHECK_CUBLAS(cublasStrsm(handle, CUBLAS_SIDE_LEFT, triangle, CUBLAS_OP_N, diagonal, n, n, &one, matrix, lda, inverse, n)); +} + +static inline +void trsm(cublasHandle_t& handle, const char uplo, const char diag, const int n, + const double* matrix, const int lda, double* inverse) +{ + const double one = 1.0; + const cublasFillMode_t triangle = cublas_fill_mode(uplo); + const cublasDiagType_t diagonal = cublas_diag_type(diag); + CHECK_CUBLAS(cublasDtrsm(handle, CUBLAS_SIDE_LEFT, triangle, CUBLAS_OP_N, diagonal, n, n, &one, matrix, lda, inverse, n)); +} + +static inline +void trsm(cublasHandle_t& handle, const char uplo, const char diag, const int n, + const std::complex* matrix, const int lda, std::complex* inverse) +{ + const cuComplex one = make_cuComplex(1.0f, 0.0f); + const cublasFillMode_t triangle = cublas_fill_mode(uplo); + const cublasDiagType_t diagonal = cublas_diag_type(diag); + const cuComplex* cuda_matrix = reinterpret_cast(matrix); + cuComplex* cuda_inverse = reinterpret_cast(inverse); + CHECK_CUBLAS(cublasCtrsm(handle, CUBLAS_SIDE_LEFT, triangle, CUBLAS_OP_N, diagonal, n, n, &one, cuda_matrix, lda, cuda_inverse, n)); +} + +static inline +void trsm(cublasHandle_t& handle, const char uplo, const char diag, const int n, + const std::complex* matrix, const int lda, std::complex* inverse) +{ + const cuDoubleComplex one = make_cuDoubleComplex(1.0, 0.0); + const cublasFillMode_t triangle = cublas_fill_mode(uplo); + const cublasDiagType_t diagonal = cublas_diag_type(diag); + const cuDoubleComplex* cuda_matrix = reinterpret_cast(matrix); + cuDoubleComplex* cuda_inverse = reinterpret_cast(inverse); + CHECK_CUBLAS(cublasZtrsm(handle, CUBLAS_SIDE_LEFT, triangle, CUBLAS_OP_N, diagonal, n, n, &one, cuda_matrix, lda, cuda_inverse, n)); +} + static inline void copy(cublasHandle_t& handle, const int& n, const float *x, const int& incx, float *y, const int& incy) { diff --git a/source/source_base/module_container/base/third_party/cusolver.h b/source/source_base/module_container/base/third_party/cusolver.h index ac370679b0c..968db605071 100644 --- a/source/source_base/module_container/base/third_party/cusolver.h +++ b/source/source_base/module_container/base/third_party/cusolver.h @@ -29,48 +29,6 @@ struct CudaDeleter { template using unique_cuda_ptr = std::unique_ptr; -struct HostDeleter { - void operator()(void* ptr) const noexcept { - if (ptr) free(ptr); - } -}; - -template -using unique_host_ptr = std::unique_ptr; - -#if CUDA_VERSION >= 11000 -// Generic API (CUDA 11.0+) -template -static inline -void trtri (cusolverDnHandle_t& cusolver_handle, const char& uplo, const char& diag, const int& n, T* A, const int& lda) -{ - size_t d_lwork = 0, h_lwork = 0; - using Type = typename GetTypeThrust::type; - CHECK_CUSOLVER(cusolverDnXtrtri_bufferSize(cusolver_handle, cublas_fill_mode(uplo), cublas_diag_type(diag), n, GetTypeCuda::cuda_data_type, reinterpret_cast(A), lda, &d_lwork, &h_lwork)); - void* raw_d_work = nullptr; - CHECK_CUDA(cudaMalloc((void**)&raw_d_work, d_lwork)); - unique_cuda_ptr d_work(raw_d_work); - unique_host_ptr h_work; - if (h_lwork) { - void* raw_h_work = malloc(h_lwork); - if (raw_h_work == nullptr) { - throw std::bad_alloc(); - } - h_work.reset(raw_h_work); - } - int h_info = 0; - int* raw_d_info = nullptr; - CHECK_CUDA(cudaMalloc((void**)&raw_d_info, sizeof(int))); - unique_cuda_ptr d_info(raw_d_info); - CHECK_CUSOLVER(cusolverDnXtrtri(cusolver_handle, cublas_fill_mode(uplo), cublas_diag_type(diag), n, GetTypeCuda::cuda_data_type, reinterpret_cast(A), n, d_work.get(), d_lwork, h_work.get(), h_lwork, d_info.get())); - CHECK_CUDA(cudaMemcpy(&h_info, d_info.get(), sizeof(int), cudaMemcpyDeviceToHost)); - if (h_info != 0) { - throw std::runtime_error("trtri: failed to invert matrix"); - } -} -#else -#error "CUDA version < 11.0 is not supported. cusolverDnXtrtri (CUDA 11.0+) is required." -#endif static inline void potri (cusolverDnHandle_t& cusolver_handle, const char& uplo, const char& diag, const int& n, float * A, const int& lda) diff --git a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/INPUT b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/INPUT index 5a387517cad..6d8ae4f25eb 100644 --- a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/INPUT +++ b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/INPUT @@ -10,7 +10,8 @@ pseudo_dir ../../PP_ORB #Parameters (2.Iteration) ecutwfc 20 -scf_thr 1e-9 +scf_thr 1e-13 +pw_diag_thr 1e-12 scf_nmax 100 #Parameters (3.Basis) diff --git a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/pchgi4s1.cube.ref b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/pchgi4s1.cube.ref index 49c2d24afab..982b1c0a79a 100644 --- a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/pchgi4s1.cube.ref +++ b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/pchgi4s1.cube.ref @@ -6,975 +6,975 @@ Ionic_Step 1 Cubefile created from ABACUS. Inner loop is z, followed by y and x 18 0.247222 0.247222 0.000000 14 4.000000 0.000000 0.000000 0.000000 14 4.000000 2.581000 2.403000 2.225000 - 4.19011399368e-07 2.16803846536e-03 9.82791492926e-03 1.98308309354e-02 2.65725792908e-02 2.84310762946e-02 - 2.69944146948e-02 2.34236764017e-02 1.81730323932e-02 1.22120626491e-02 6.88841887249e-03 3.24502986743e-03 - 1.80070909648e-03 2.11659392567e-03 2.83284209532e-03 2.90741618148e-03 2.22657082227e-03 9.82176485309e-04 - 2.68014873221e-03 1.25909541325e-02 2.67753232651e-02 3.73920695598e-02 4.08067082948e-02 3.92484681158e-02 - 3.51353079861e-02 2.87927446984e-02 2.06258697996e-02 1.24491015397e-02 6.22328579623e-03 2.87757157410e-03 - 2.20335331684e-03 2.94964712071e-03 3.50859567428e-03 2.95054978010e-03 1.32926317902e-03 3.67495229894e-05 - 1.10789055788e-02 2.74713201135e-02 4.24682178545e-02 4.95468916190e-02 4.97416642851e-02 4.65036037121e-02 - 4.05502205105e-02 3.13099517866e-02 2.03325477443e-02 1.08911638785e-02 5.09646927372e-03 2.94477872128e-03 - 3.14867895602e-03 3.94775681549e-03 3.82996660100e-03 2.19765970380e-03 1.82253761747e-04 1.61579821535e-03 - 2.13966890593e-02 3.83519119286e-02 4.98310483892e-02 5.39480993008e-02 5.35367005583e-02 4.99032174792e-02 - 4.17562283176e-02 2.93983274780e-02 1.68232010579e-02 8.18901200731e-03 4.38681742403e-03 3.78801123540e-03 - 4.30460441940e-03 4.31193381354e-03 2.94381690938e-03 6.91022573166e-04 4.37030170446e-04 6.83460048949e-03 - 2.80687583514e-02 4.16054275606e-02 4.97900514387e-02 5.32213736187e-02 5.31039109921e-02 4.80821269045e-02 - 3.69226754614e-02 2.27992987863e-02 1.15780629520e-02 6.10944479216e-03 4.88016836446e-03 5.07012073149e-03 - 4.81600271190e-03 3.42909873455e-03 1.24142306562e-03 4.05013183150e-05 3.17841758106e-03 1.32220784722e-02 - 2.95047322583e-02 3.94534559074e-02 4.58159289369e-02 4.88435940966e-02 4.75144975441e-02 3.98368768216e-02 - 2.70322463312e-02 1.47921351332e-02 7.93269221088e-03 6.16087909551e-03 6.27932737801e-03 5.84974584191e-03 - 4.19949229248e-03 1.85626973622e-03 1.51380075900e-04 1.15550258159e-03 6.89886924054e-03 1.73807070542e-02 - 2.73305562495e-02 3.45440279380e-02 3.92246267647e-02 4.06218001378e-02 3.70732767248e-02 2.81852199368e-02 - 1.75667729895e-02 1.03569347052e-02 7.96336880031e-03 7.87341879261e-03 7.39238013442e-03 5.56980359132e-03 - 2.90508497645e-03 6.48524239098e-04 2.89474899524e-04 3.10153678749e-03 9.48688276578e-03 1.83311581766e-02 - 2.31493639904e-02 2.79809058286e-02 3.06640570712e-02 3.03319316350e-02 2.62277165245e-02 1.95984163522e-02 - 1.38659338805e-02 1.11829899273e-02 1.04348215151e-02 9.49888007189e-03 7.42517482246e-03 4.47849981821e-03 - 1.65946667344e-03 2.28667008709e-04 1.12259014553e-03 4.59229934307e-03 1.01921204841e-02 1.68486551223e-02 - 1.80794183092e-02 2.09941567811e-02 2.24268459883e-02 2.21022796989e-02 2.01248501404e-02 1.76421827517e-02 - 1.58841218471e-02 1.45873821882e-02 1.26871017844e-02 9.82729646775e-03 6.38895710438e-03 3.08323113112e-03 - 8.32328125658e-04 3.98613776514e-04 1.94197847915e-03 5.11194784961e-03 9.37240901140e-03 1.39909201451e-02 - 1.33847167363e-02 1.54268415134e-02 1.71058885560e-02 1.85089397834e-02 1.95898662127e-02 2.01213897689e-02 - 1.94245388731e-02 1.69128380923e-02 1.29717963349e-02 8.60448525534e-03 4.66423949130e-03 1.79693362078e-03 - 4.88896696612e-04 8.04516293324e-04 2.38024877340e-03 4.81524982455e-03 7.77937944545e-03 1.07983769677e-02 - 1.01238820788e-02 1.24623780903e-02 1.54276149340e-02 1.88076087599e-02 2.16837740444e-02 2.26234706290e-02 - 2.06383052110e-02 1.62272324676e-02 1.09714445120e-02 6.26202843765e-03 2.83101201380e-03 9.58279963442e-04 - 5.43775024378e-04 1.16997965415e-03 2.43656490983e-03 4.14815069037e-03 6.12438450081e-03 8.12162853655e-03 - 8.79710982005e-03 1.22228049129e-02 1.65683981090e-02 2.08374281405e-02 2.32708734645e-02 2.23785095629e-02 - 1.83050590811e-02 1.27619107674e-02 7.57680326530e-03 3.75214716498e-03 1.54078112407e-03 7.21531878939e-04 - 8.16515388624e-04 1.40690280559e-03 2.30934078215e-03 3.45294989732e-03 4.78590363722e-03 6.43659954150e-03 - 9.42174177592e-03 1.40297002299e-02 1.89956228301e-02 2.24652763861e-02 2.25243356294e-02 1.89229263434e-02 - 1.34179491872e-02 8.15530538518e-03 4.31262900389e-03 2.08747253842e-03 1.14569788284e-03 9.70586768099e-04 - 1.16528813484e-03 1.56340223386e-03 2.11173208071e-03 2.83554077007e-03 3.98981023355e-03 6.05459610346e-03 - 1.10238512126e-02 1.60727711104e-02 2.03822468842e-02 2.15762170118e-02 1.86201593346e-02 1.31871418678e-02 - 7.95085798210e-03 4.37597392972e-03 2.51285007652e-03 1.78585846198e-03 1.57274562826e-03 1.51881092385e-03 - 1.54527015168e-03 1.64061896544e-03 1.84605726408e-03 2.46347104099e-03 4.00312353280e-03 6.83629875475e-03 - 1.15623176026e-02 1.58175087216e-02 1.82062563277e-02 1.66620141929e-02 1.19651793488e-02 7.11969250642e-03 - 4.12716992874e-03 2.98855123013e-03 2.82610203978e-03 2.82242590552e-03 2.61277118391e-03 2.25762513719e-03 - 1.88464596054e-03 1.57849674548e-03 1.64828575546e-03 2.57401320382e-03 4.55643294303e-03 7.56883344761e-03 - 9.60375722133e-03 1.18903284388e-02 1.17264914772e-02 8.72360341925e-03 5.36141133611e-03 3.84068780130e-03 - 4.05204709537e-03 4.83665730860e-03 5.25917963414e-03 4.94222971196e-03 4.07345819016e-03 3.02587727437e-03 - 2.03656824521e-03 1.44274169603e-03 1.73319920912e-03 2.94488281802e-03 4.68873853017e-03 6.88784247043e-03 - 5.75323598357e-03 5.58191135531e-03 3.95146747575e-03 2.83745388251e-03 4.03126095241e-03 6.62226102446e-03 - 8.82941247313e-03 9.78262777907e-03 9.31553519921e-03 7.71458069891e-03 5.60361454784e-03 3.53179382756e-03 - 1.95441597866e-03 1.42855304294e-03 2.03712895903e-03 3.05774157489e-03 3.95864454987e-03 4.87779293380e-03 - 1.87886591085e-03 6.84495483026e-04 1.44923585418e-03 5.93364628402e-03 1.18928981385e-02 1.60178523332e-02 - 1.74088227564e-02 1.65773833950e-02 1.40226105348e-02 1.04173008518e-02 6.67365952333e-03 3.57426757138e-03 - 1.79676178063e-03 1.63972172819e-03 2.37416801179e-03 2.90858022104e-03 2.96137794973e-03 2.70515259251e-03 - 4.35427191104e-03 1.45213811355e-02 2.87925004070e-02 3.90023455715e-02 4.16426771655e-02 3.92600864231e-02 - 3.44630846746e-02 2.76828613383e-02 1.93986709620e-02 1.14086041742e-02 5.58746884194e-03 2.77602240859e-03 - 2.67102979506e-03 3.91885988188e-03 4.83632683418e-03 4.47493732542e-03 2.91471097592e-03 1.61564330874e-03 - 1.40680755483e-02 3.22138464156e-02 4.80303168495e-02 5.43448195275e-02 5.29712660350e-02 4.83232059127e-02 - 4.13932989596e-02 3.14584965587e-02 1.99797900804e-02 1.03247801696e-02 4.68103000884e-03 2.96374333645e-03 - 3.68364932782e-03 4.87944174276e-03 4.94484793550e-03 3.33331355863e-03 1.36946830216e-03 3.28009582510e-03 - 2.81344718244e-02 4.77198544938e-02 5.93626936572e-02 6.16812872151e-02 5.93097982381e-02 5.43875423738e-02 - 4.51784007809e-02 3.14288341019e-02 1.74043901315e-02 7.91526939170e-03 4.04273140632e-03 3.87122901729e-03 - 4.92559230203e-03 5.30886640163e-03 4.06237635487e-03 1.83445556190e-03 2.09191463478e-03 1.03650307728e-02 - 3.80686801686e-02 5.35677113531e-02 6.10891801206e-02 6.29531498339e-02 6.18556564735e-02 5.60432468825e-02 - 4.30046141707e-02 2.59230384809e-02 1.21601950602e-02 5.59342860785e-03 4.46172435916e-03 5.24889570810e-03 - 5.57705574566e-03 4.50013957114e-03 2.36564915015e-03 1.41578041214e-03 5.99444209766e-03 1.93318064966e-02 - 4.03665184113e-02 5.14679523737e-02 5.75972551370e-02 6.05136459446e-02 5.91687258713e-02 4.98950351768e-02 - 3.31893909057e-02 1.66328658903e-02 7.29148095552e-03 5.10400790346e-03 5.82843730884e-03 6.15325860184e-03 - 5.00023518742e-03 2.81910469652e-03 1.21466861355e-03 2.97623320059e-03 1.09893192692e-02 2.50839278720e-02 - 3.74102116851e-02 4.57291776648e-02 5.11690781179e-02 5.32032980712e-02 4.87182667438e-02 3.60012459978e-02 - 2.01367099418e-02 9.29487242756e-03 5.95997793483e-03 6.52127233095e-03 7.02887222003e-03 5.93241037844e-03 - 3.59213704631e-03 1.43846230572e-03 1.42951326332e-03 5.47709131931e-03 1.43151180616e-02 2.61620545189e-02 - 3.19432302750e-02 3.79385625401e-02 4.14221324251e-02 4.06442105563e-02 3.35194898773e-02 2.19222369247e-02 - 1.21174736401e-02 8.08065103489e-03 7.98354798317e-03 8.29076898702e-03 7.25718792148e-03 4.87322885044e-03 - 2.23202008285e-03 9.70773003965e-04 2.48467822788e-03 7.36238933641e-03 1.50917819948e-02 2.39687211939e-02 - 2.51552506579e-02 2.87171575021e-02 2.99549165888e-02 2.77447669020e-02 2.21321468176e-02 1.60197854736e-02 - 1.25668283319e-02 1.15624179043e-02 1.08383364308e-02 9.19554801608e-03 6.57459902550e-03 3.56812230273e-03 - 1.37243362055e-03 1.19116826319e-03 3.47882083534e-03 7.94127128756e-03 1.38222161749e-02 1.99553108317e-02 - 1.83162004188e-02 2.03279921468e-02 2.11747412336e-02 2.07720017138e-02 1.96642198416e-02 1.87181111260e-02 - 1.76959935429e-02 1.56511225678e-02 1.25126495620e-02 8.87819403625e-03 5.27127761291e-03 2.36614483934e-03 - 1.02219798596e-03 1.63496604922e-03 3.90918820086e-03 7.33461557054e-03 1.13623053503e-02 1.52456256992e-02 - 1.32148749890e-02 1.53748308521e-02 1.79200965320e-02 2.08081615681e-02 2.33327337190e-02 2.40600453051e-02 - 2.18727346519e-02 1.73405536353e-02 1.21046304257e-02 7.35513021605e-03 3.67126132907e-03 1.50417668134e-03 - 1.04416714573e-03 1.95902507438e-03 3.74596770848e-03 6.08806045886e-03 8.67110519432e-03 1.10875879041e-02 - 1.09674040701e-02 1.45665971161e-02 1.93467053070e-02 2.43351650216e-02 2.74199650698e-02 2.66274426100e-02 - 2.20344865922e-02 1.57090157382e-02 9.74043270162e-03 5.16735930717e-03 2.32205205689e-03 1.15788015706e-03 - 1.24858060094e-03 2.05295353247e-03 3.27666181765e-03 4.81335879285e-03 6.54128067936e-03 8.46779662007e-03 - 1.16346713118e-02 1.69688691818e-02 2.30101149909e-02 2.76591058216e-02 2.84764490615e-02 2.47813202711e-02 - 1.83774534849e-02 1.18132637219e-02 6.64826390976e-03 3.33405612396e-03 1.70820565702e-03 1.27329597192e-03 - 1.47804030999e-03 2.00789065057e-03 2.78978085096e-03 3.85875897209e-03 5.39596522605e-03 7.82238408794e-03 - 1.44134465359e-02 2.08163031436e-02 2.64273337892e-02 2.85498445662e-02 2.58293333207e-02 1.97253736253e-02 - 1.30674408547e-02 7.79122614873e-03 4.40775620113e-03 2.62167384868e-03 1.86977949017e-03 1.64862248455e-03 - 1.68997393417e-03 1.91896052440e-03 2.39915777853e-03 3.41940781103e-03 5.49237698777e-03 9.10518673971e-03 - 1.73463949503e-02 2.34688630958e-02 2.70092127645e-02 2.56919438250e-02 2.02369229413e-02 1.37242664018e-02 - 8.62242156925e-03 5.53785576550e-03 3.95460300111e-03 3.13046532005e-03 2.57643653217e-03 2.15399812007e-03 - 1.87286955047e-03 1.80071138741e-03 2.23981689190e-03 3.73123599490e-03 6.71065036058e-03 1.13299886512e-02 - 1.78244417476e-02 2.21183721275e-02 2.27480771171e-02 1.90850794546e-02 1.36962617639e-02 9.42352771056e-03 - 7.09172155218e-03 6.06755379703e-03 5.43233756001e-03 4.62808245034e-03 3.64160321673e-03 2.69641050741e-03 - 1.96178614997e-03 1.73465641932e-03 2.53368356200e-03 4.65451909584e-03 8.01793000935e-03 1.25396411164e-02 - 1.44555236795e-02 1.59225376896e-02 1.45807356380e-02 1.19168597768e-02 1.01593706078e-02 9.72629563399e-03 - 9.80020597352e-03 9.56496845755e-03 8.54828331798e-03 6.81070319834e-03 4.84094655417e-03 3.09087713238e-03 - 1.93956884059e-03 1.90641417397e-03 3.21754818118e-03 5.43740276395e-03 8.14062382749e-03 1.13067477936e-02 - 8.44189080345e-03 8.06969374890e-03 8.42740785775e-03 1.09193011558e-02 1.42523750483e-02 1.63695603636e-02 - 1.66695588856e-02 1.53325649859e-02 1.26188126786e-02 9.14701721299e-03 5.76937267351e-03 3.16811136765e-03 - 1.93513965223e-03 2.37498303694e-03 3.90210489667e-03 5.52498066567e-03 6.87713371745e-03 7.98758367612e-03 - 3.40819300690e-03 5.58371507101e-03 1.25351953287e-02 2.11807758734e-02 2.66200337970e-02 2.76495044554e-02 - 2.57332843455e-02 2.18812286514e-02 1.65959985749e-02 1.08955933289e-02 6.04683484282e-03 2.97538860274e-03 - 2.12663410351e-03 3.05087037720e-03 4.40690146936e-03 5.11198259281e-03 4.95916437418e-03 4.10318088994e-03 - 1.54321021681e-02 3.18034255948e-02 4.61691463859e-02 5.17631831747e-02 4.99620320523e-02 4.47541109680e-02 - 3.73303309429e-02 2.74745387414e-02 1.67399279875e-02 8.06896425023e-03 3.29574237219e-03 2.31875663393e-03 - 3.74756609229e-03 5.66159482721e-03 6.45040091689e-03 5.54489119064e-03 4.07816030803e-03 5.81594384521e-03 - 3.04669290365e-02 4.97757867368e-02 6.07620065379e-02 6.19784612664e-02 5.82669442185e-02 5.21276171307e-02 - 4.22316265572e-02 2.85104906289e-02 1.49836042614e-02 6.10220880315e-03 2.84168328004e-03 3.33809523364e-03 - 5.11184538180e-03 6.19570414944e-03 5.57613301484e-03 3.86521585173e-03 4.44427548209e-03 1.27914679694e-02 - 4.38634072727e-02 5.95996663878e-02 6.59130298816e-02 6.59745257749e-02 6.33559236801e-02 5.65183418239e-02 - 4.27108261571e-02 2.49401690903e-02 1.07167391539e-02 4.12903599823e-03 3.41552478678e-03 4.89382153174e-03 - 5.98854142821e-03 5.60213835854e-03 4.02403993118e-03 3.60669253647e-03 9.02711521516e-03 2.37507560048e-02 - 4.86763161894e-02 5.95799497133e-02 6.44052430120e-02 6.62368367922e-02 6.43496668573e-02 5.42027479422e-02 - 3.55758773152e-02 1.67368521135e-02 5.99709165574e-03 3.63067303056e-03 4.91344230084e-03 6.03073764971e-03 - 5.63303425787e-03 4.06163593879e-03 3.00667656233e-03 5.65064012755e-03 1.53867374416e-02 3.18140122845e-02 - 4.60583283660e-02 5.42603345154e-02 5.94810125186e-02 6.17676422332e-02 5.69526604627e-02 4.18708371195e-02 - 2.21626187567e-02 8.26259051133e-03 3.87759575111e-03 4.82831683066e-03 6.20466429163e-03 5.95518396796e-03 - 4.28817843029e-03 2.67197625802e-03 3.34084742169e-03 8.68898195453e-03 1.96572511763e-02 3.37172937844e-02 - 3.98549235196e-02 4.63246561895e-02 5.04180189093e-02 4.96433522699e-02 4.03048881144e-02 2.43330266734e-02 - 1.04988850520e-02 4.77248146358e-03 4.98966865904e-03 6.39079579300e-03 6.46926818086e-03 4.94564430196e-03 - 2.89938997647e-03 2.16374875695e-03 4.49790812743e-03 1.08157026669e-02 2.04692900458e-02 3.10128324699e-02 - 3.18437733222e-02 3.58582044909e-02 3.69166756526e-02 3.27166632453e-02 2.30096120207e-02 1.27308809021e-02 - 7.35340977409e-03 6.72868336256e-03 7.41870610666e-03 7.31378424217e-03 5.94190331948e-03 3.74093643792e-03 - 2.03256901092e-03 2.35982904961e-03 5.50130727645e-03 1.12660634127e-02 1.86251966888e-02 2.59281080286e-02 - 2.29793534624e-02 2.45476223837e-02 2.37559811508e-02 2.01886965909e-02 1.55093433587e-02 1.25105798669e-02 - 1.15608691759e-02 1.08815514229e-02 9.51894223106e-03 7.51978449275e-03 5.04238605339e-03 2.70561660930e-03 - 1.68217575574e-03 2.75317958740e-03 5.79547757390e-03 1.02262640418e-02 1.52207071774e-02 1.97319409438e-02 - 1.53901055917e-02 1.64017624176e-02 1.70572727437e-02 1.77640060838e-02 1.87626405378e-02 1.91362609143e-02 - 1.75521818052e-02 1.42428206687e-02 1.04776175683e-02 6.94120602455e-03 3.89954929389e-03 1.94315547371e-03 - 1.66066906654e-03 2.95895152800e-03 5.31854593408e-03 8.27563141022e-03 1.12914533748e-02 1.37462413953e-02 - 1.12893637388e-02 1.39318580703e-02 1.78674350028e-02 2.24899573974e-02 2.56773840630e-02 2.51197294206e-02 - 2.08420315115e-02 1.50789154736e-02 9.75865260855e-03 5.56841386916e-03 2.76550228623e-03 1.56111831311e-03 - 1.76436678289e-03 2.83558592997e-03 4.37987608132e-03 6.17401742895e-03 7.93266253190e-03 9.51767870380e-03 - 1.12647427393e-02 1.63348021420e-02 2.26882783736e-02 2.81617349199e-02 2.97711329937e-02 2.63845042000e-02 - 1.99007062010e-02 1.31319944198e-02 7.68616805197e-03 4.01207400497e-03 2.08614105362e-03 1.55280908302e-03 - 1.82956536125e-03 2.49466391412e-03 3.41417230618e-03 4.54853450246e-03 5.94542807817e-03 7.96521797259e-03 - 1.44564543627e-02 2.12492543121e-02 2.77418050708e-02 3.09974921872e-02 2.91214097508e-02 2.31639047044e-02 - 1.59949828390e-02 9.86053327856e-03 5.58671644161e-03 3.12726868208e-03 2.03889556984e-03 1.74318726373e-03 - 1.81970690130e-03 2.12196079847e-03 2.69122179385e-03 3.74043654006e-03 5.70824766177e-03 9.15540262516e-03 - 1.91061519349e-02 2.60817777184e-02 3.05276401908e-02 3.00462601221e-02 2.49543232787e-02 1.79860031606e-02 - 1.17593228565e-02 7.35651870074e-03 4.69051352541e-03 3.22869572426e-03 2.43677958939e-03 1.99140574422e-03 - 1.77296177308e-03 1.81362754806e-03 2.38107769714e-03 4.00394686319e-03 7.23107926606e-03 1.23516145302e-02 - 2.25928828460e-02 2.80606490158e-02 2.93428590280e-02 2.57236663000e-02 1.95092955194e-02 1.36309787803e-02 - 9.47836617572e-03 6.94808014366e-03 5.35553653276e-03 4.12869838113e-03 3.08036995702e-03 2.24514840951e-03 - 1.69343862589e-03 1.67412005315e-03 2.72391784666e-03 5.34927653305e-03 9.73163907216e-03 1.57476879870e-02 - 2.22729139052e-02 2.50771432233e-02 2.38697233370e-02 1.98511226887e-02 1.55652247098e-02 1.24388603781e-02 - 1.04611457132e-02 8.98263688247e-03 7.39749436153e-03 5.58657467040e-03 3.83216724368e-03 2.41150776553e-03 - 1.60213905767e-03 1.90746636900e-03 3.75299345135e-03 7.07054396715e-03 1.15867505140e-02 1.70146963488e-02 - 1.74080067458e-02 1.79920029047e-02 1.73156632679e-02 1.67894861677e-02 1.65980742064e-02 1.61482537405e-02 - 1.50760578237e-02 1.31637293326e-02 1.04028401006e-02 7.27032129350e-03 4.42658378510e-03 2.37503360300e-03 - 1.63101370537e-03 2.59972711461e-03 5.00570553973e-03 8.11171010217e-03 1.15055573447e-02 1.49192382603e-02 - 1.07295065242e-02 1.24622488610e-02 1.68355728561e-02 2.19581482703e-02 2.48091582259e-02 2.46743489520e-02 - 2.23850463233e-02 1.85221480216e-02 1.36001703668e-02 8.59581440458e-03 4.53894324485e-03 2.17115024884e-03 - 1.94039352315e-03 3.57361891125e-03 5.95072606387e-03 8.04376284738e-03 9.52550165322e-03 1.03137782644e-02 - 8.23680192316e-03 1.64175860251e-02 2.78493399603e-02 3.59553375412e-02 3.78057018731e-02 3.52573101594e-02 - 3.03568920797e-02 2.36894665416e-02 1.60118803824e-02 8.96630180472e-03 4.07328378221e-03 2.03414550290e-03 - 2.62761235607e-03 4.63820443504e-03 6.45719445876e-03 7.11578539261e-03 6.56881951706e-03 5.91338893028e-03 - 2.72725283108e-02 4.34656913175e-02 5.31726260270e-02 5.44368514841e-02 5.06556347067e-02 4.40444992605e-02 - 3.42819940973e-02 2.20414785214e-02 1.06823563524e-02 3.52403735823e-03 1.23859814559e-03 2.34129125187e-03 - 4.73110664187e-03 6.57956358597e-03 6.82918477738e-03 5.84575952471e-03 6.34741934086e-03 1.29322760956e-02 - 4.15959311461e-02 5.60715783762e-02 6.17253549409e-02 6.09144969449e-02 5.69059770552e-02 4.89852005704e-02 - 3.55570539025e-02 1.95762326291e-02 7.28186024629e-03 1.90968851167e-03 1.87165177347e-03 3.93774935799e-03 - 5.73753493842e-03 6.14734054926e-03 5.29823912551e-03 5.22897953670e-03 1.01504573883e-02 2.33527940404e-02 - 4.98147617969e-02 5.99249066283e-02 6.34615868927e-02 6.36034212465e-02 6.01259829331e-02 4.92780300458e-02 - 3.11477846657e-02 1.33628852563e-02 3.48388623838e-03 1.68957670786e-03 3.50794483522e-03 5.27599118986e-03 - 5.63448959218e-03 4.82149706164e-03 4.38234925372e-03 7.37590415364e-03 1.71601114148e-02 3.33866853327e-02 - 4.97127591711e-02 5.70107399930e-02 6.10120265982e-02 6.22221362673e-02 5.65967206900e-02 4.08199012092e-02 - 2.04256338480e-02 6.04076820933e-03 1.63189492368e-03 2.98726116077e-03 4.98001136019e-03 5.43076492643e-03 - 4.49573453709e-03 3.58731844851e-03 4.93046173351e-03 1.10141939118e-02 2.28170299098e-02 3.74891886451e-02 - 4.43949992779e-02 5.04013618387e-02 5.41780494662e-02 5.30612648939e-02 4.25954155058e-02 2.45348192256e-02 - 8.65782502689e-03 1.97155195986e-03 2.34339558938e-03 4.43364355831e-03 5.29892924276e-03 4.53639418516e-03 - 3.21308141552e-03 3.19059922687e-03 6.34113460909e-03 1.36975672251e-02 2.44549856172e-02 3.56506136373e-02 - 3.63499398266e-02 4.03431702656e-02 4.11331729791e-02 3.55392230932e-02 2.29787670428e-02 9.72950917765e-03 - 2.88163922922e-03 2.40704585976e-03 4.05708788888e-03 5.08990355905e-03 4.76366690020e-03 3.43015877773e-03 - 2.43751717232e-03 3.46895279010e-03 7.46780238434e-03 1.42668498565e-02 2.25711944005e-02 3.03659744443e-02 - 2.64715885411e-02 2.74286356928e-02 2.48779459362e-02 1.80447922194e-02 9.82753689629e-03 5.00224654920e-03 - 4.39004645855e-03 5.21522768062e-03 5.62614867615e-03 5.25500053327e-03 4.05477463097e-03 2.58369006207e-03 - 2.17827894788e-03 3.87633690512e-03 7.68203956894e-03 1.29368719026e-02 1.85688875677e-02 2.33344456787e-02 - 1.66506627031e-02 1.58556341186e-02 1.34964270869e-02 1.07204956843e-02 9.43315190154e-03 9.64379032241e-03 - 9.52391567131e-03 8.41072323790e-03 6.88877731282e-03 5.18191748770e-03 3.33361122862e-03 2.02880354812e-03 - 2.19878056990e-03 3.99795172442e-03 6.92376808667e-03 1.03824659074e-02 1.36248791174e-02 1.58521916475e-02 - 1.00205625778e-02 1.04261504507e-02 1.18083467407e-02 1.44879776901e-02 1.70343665959e-02 1.70936170826e-02 - 1.43813042462e-02 1.07173885138e-02 7.40971822591e-03 4.64973599644e-03 2.59754910911e-03 1.75473684993e-03 - 2.26160878485e-03 3.66789553239e-03 5.51453464309e-03 7.44346683528e-03 8.96722902951e-03 9.75425100177e-03 - 8.40075684441e-03 1.18562201699e-02 1.71095003277e-02 2.23323975575e-02 2.43375407807e-02 2.17386871101e-02 - 1.64332500350e-02 1.10429101578e-02 6.74203374869e-03 3.72466681283e-03 2.08265489818e-03 1.72803799958e-03 - 2.18765513076e-03 3.00792215214e-03 3.99108040193e-03 4.96029802621e-03 5.76526883778e-03 6.65860101917e-03 - 1.11803444071e-02 1.72499457492e-02 2.38020781372e-02 2.77892585265e-02 2.68351130776e-02 2.16753656500e-02 - 1.51453680304e-02 9.46113261220e-03 5.40664307615e-03 3.01554354605e-03 1.99066432181e-03 1.79379757182e-03 - 1.95493322080e-03 2.29483719729e-03 2.79052825795e-03 3.49232774892e-03 4.69671761202e-03 7.03018153910e-03 - 1.64244738627e-02 2.32641111819e-02 2.82463009122e-02 2.88203971245e-02 2.47678898091e-02 1.83673693362e-02 - 1.21800408895e-02 7.51241757910e-03 4.55872389724e-03 2.96683186268e-03 2.19814242238e-03 1.82368377636e-03 - 1.66384317084e-03 1.71887016927e-03 2.15023176857e-03 3.35699407984e-03 5.90461892039e-03 1.02635905431e-02 - 2.17354881465e-02 2.73622898395e-02 2.91911823063e-02 2.63877166458e-02 2.06927858838e-02 1.46909138046e-02 - 9.95540868142e-03 6.78693654417e-03 4.79673730853e-03 3.47290328641e-03 2.50853297057e-03 1.81056620781e-03 - 1.38381877226e-03 1.39745351646e-03 2.30610919356e-03 4.68468243116e-03 8.89637995593e-03 1.48729809430e-02 - 2.45050122367e-02 2.76825987223e-02 2.66633782309e-02 2.24262049255e-02 1.73118011544e-02 1.30222813559e-02 - 9.99147152644e-03 7.83236591295e-03 6.00682510501e-03 4.30997888061e-03 2.84694443485e-03 1.74033412927e-03 - 1.15704139824e-03 1.53197340006e-03 3.40901710779e-03 7.03087155880e-03 1.22700863590e-02 1.85849686680e-02 - 2.28684455548e-02 2.37904662452e-02 2.21969727476e-02 1.95504443497e-02 1.69585873969e-02 1.47427832056e-02 - 1.27228414544e-02 1.05001678768e-02 7.92465980296e-03 5.31229751963e-03 3.08598579711e-03 1.54799043506e-03 - 1.11503594596e-03 2.28223737991e-03 5.12688002950e-03 9.25040750085e-03 1.41807172676e-02 1.92033314842e-02 - 1.75383774512e-02 1.85250273848e-02 1.99543817901e-02 2.13223681023e-02 2.15213669402e-02 2.02860220266e-02 - 1.78229501979e-02 1.42872197352e-02 1.01195054574e-02 6.11581492589e-03 2.98303077930e-03 1.26215044829e-03 - 1.43222617422e-03 3.49742477138e-03 6.72683903892e-03 1.02531196249e-02 1.35461004107e-02 1.60956312207e-02 - 1.29980886089e-02 1.81845027841e-02 2.52011069281e-02 3.00568326619e-02 3.08634015427e-02 2.85002507001e-02 - 2.40884771671e-02 1.82243948060e-02 1.18243195568e-02 6.20290744923e-03 2.42836496237e-03 1.09115162879e-03 - 2.18269760447e-03 4.82797689825e-03 7.66594335973e-03 9.72184529740e-03 1.07063090944e-02 1.11488465986e-02 - 1.55788939798e-02 2.76248703336e-02 3.85493523160e-02 4.31208400183e-02 4.17356091647e-02 3.69697644156e-02 - 2.99550945280e-02 2.11714804500e-02 1.22111562467e-02 5.26958275422e-03 1.65163021831e-03 1.35392641862e-03 - 3.34471783399e-03 5.98064329874e-03 7.74126404572e-03 7.95506011759e-03 7.32912155618e-03 8.65067208271e-03 - 3.39303965803e-02 4.55816250415e-02 5.05962202033e-02 4.98069964889e-02 4.53496583288e-02 3.73706319050e-02 - 2.57909537204e-02 1.32907811338e-02 4.13420902338e-03 3.47348505113e-04 7.43199590612e-04 2.95562317847e-03 - 5.11628128932e-03 6.19557282856e-03 6.11521489610e-03 6.34294992372e-03 1.00841253416e-02 1.99687299687e-02 - 4.35312709064e-02 5.24232301260e-02 5.51915460030e-02 5.39357096419e-02 4.88731950122e-02 3.81416914633e-02 - 2.27658895936e-02 8.68847555605e-03 1.25148769125e-03 2.98021842000e-04 2.28055026792e-03 4.27116878277e-03 - 5.12708483860e-03 4.97684975030e-03 5.02217179994e-03 7.74524154299e-03 1.59390850129e-02 2.95018924206e-02 - 4.66887257408e-02 5.29271531910e-02 5.53421849804e-02 5.44875015671e-02 4.76176550432e-02 3.28583490182e-02 - 1.52325813049e-02 3.32234206110e-03 5.24671208178e-05 1.68406262919e-03 3.80304993773e-03 4.56000567974e-03 - 4.14193661139e-03 3.80390226037e-03 5.47118359605e-03 1.12998233387e-02 2.21613313934e-02 3.55679759833e-02 - 4.39118929260e-02 4.88119068638e-02 5.11379654838e-02 4.87232058328e-02 3.79701890683e-02 2.08468916944e-02 - 6.22387876143e-03 2.71059858679e-04 9.00464962140e-04 3.11234108665e-03 4.16501578079e-03 3.75882916076e-03 - 2.95488750417e-03 3.48889212517e-03 7.05389889421e-03 1.45511612224e-02 2.51436424897e-02 3.58975480259e-02 - 3.72969452704e-02 4.06085770675e-02 4.06256114853e-02 3.42636407549e-02 2.11402288417e-02 7.65406088136e-03 - 7.70379491364e-04 3.42114736156e-04 2.14536506823e-03 3.45121806346e-03 3.51397567463e-03 2.68699468929e-03 - 2.28420433171e-03 3.92526427808e-03 8.48305991169e-03 1.57115692161e-02 2.41951756821e-02 3.18115060701e-02 - 2.78854038396e-02 2.82283042484e-02 2.45069027872e-02 1.59317286316e-02 6.15448331255e-03 7.28165467927e-04 - 3.38280970089e-04 1.70570909547e-03 2.78937846446e-03 3.17796918496e-03 2.74023966285e-03 1.95890143791e-03 - 2.18347856848e-03 4.49839876180e-03 8.89097066256e-03 1.46065684392e-02 2.04364558466e-02 2.50898488472e-02 - 1.71143970126e-02 1.47479188648e-02 9.79600473039e-03 4.18352525793e-03 1.29498529695e-03 1.68391301904e-03 - 2.82099825227e-03 3.29624052976e-03 3.34467819035e-03 3.01114958834e-03 2.20594292017e-03 1.62002250016e-03 - 2.36207495549e-03 4.70290254360e-03 8.10484549520e-03 1.18712922787e-02 1.51472966539e-02 1.70742589250e-02 - 8.23332812457e-03 5.99829376151e-03 4.12893306963e-03 4.22527159069e-03 5.95644001900e-03 6.97014566771e-03 - 6.34004321653e-03 5.13372965996e-03 4.02794440694e-03 2.88596992248e-03 1.81685900499e-03 1.55443743929e-03 - 2.51598180824e-03 4.31163014536e-03 6.42037926069e-03 8.39408536993e-03 9.61762156021e-03 9.58396691388e-03 - 4.35527903993e-03 5.12911440456e-03 7.90994658286e-03 1.17236776924e-02 1.37220047271e-02 1.24468395001e-02 - 9.43440841281e-03 6.55682263102e-03 4.28832886876e-03 2.54926187480e-03 1.58569916566e-03 1.64689060176e-03 - 2.41748407045e-03 3.42809459077e-03 4.43473260839e-03 5.15148524842e-03 5.23931400239e-03 4.75501093454e-03 - 5.89457404223e-03 1.00870549612e-02 1.54777797051e-02 1.92604279440e-02 1.89738866541e-02 1.52421106252e-02 - 1.05906695838e-02 6.67522362675e-03 3.87496426229e-03 2.21058307815e-03 1.61824279574e-03 1.71375700185e-03 - 2.03762603879e-03 2.40683723393e-03 2.74438630538e-03 2.92771197628e-03 3.06668010059e-03 3.75510304058e-03 - 1.08683728387e-02 1.66723009882e-02 2.14924957215e-02 2.27247283438e-02 1.98114745287e-02 1.46908301858e-02 - 9.64680306160e-03 5.82096772535e-03 3.43028808342e-03 2.25265475669e-03 1.80434888703e-03 1.62623971836e-03 - 1.54449483062e-03 1.55880786311e-03 1.70250982642e-03 2.17602904126e-03 3.48230563067e-03 6.26053478602e-03 - 1.66944320298e-02 2.18012290061e-02 2.39719828391e-02 2.21939389913e-02 1.76519379943e-02 1.24931929624e-02 - 8.22384259650e-03 5.33235032306e-03 3.62886297253e-03 2.63276989018e-03 1.94450507208e-03 1.42656415326e-03 - 1.09298261544e-03 1.03571069425e-03 1.53747287966e-03 3.09392697674e-03 6.17617553484e-03 1.09125115369e-02 - 2.10945589337e-02 2.40363165452e-02 2.33788098449e-02 1.98148838999e-02 1.52031904517e-02 1.10841116930e-02 - 8.06166609721e-03 5.99042264133e-03 4.43348761153e-03 3.10845290356e-03 2.00280220867e-03 1.18337541394e-03 - 7.49018692298e-04 1.00123685740e-03 2.43916027870e-03 5.45815737834e-03 1.00847852423e-02 1.57705220284e-02 - 2.23343205452e-02 2.30949295577e-02 2.11646697399e-02 1.79068027108e-02 1.46191853755e-02 1.19104999840e-02 - 9.72667031285e-03 7.68487735293e-03 5.58411754207e-03 3.59693483579e-03 1.97571280953e-03 8.87192952145e-04 - 6.23140078957e-04 1.65011568080e-03 4.26527039409e-03 8.37226337300e-03 1.35148545263e-02 1.87043248773e-02 - 2.00121517841e-02 2.01919716870e-02 1.95611959913e-02 1.85286820404e-02 1.71226372377e-02 1.53310959802e-02 - 1.30215101225e-02 1.01061071622e-02 6.90140172729e-03 3.96955184934e-03 1.73785667589e-03 5.66472807982e-04 - 8.97491308583e-04 2.93631572146e-03 6.36178607768e-03 1.05617620161e-02 1.48362957927e-02 1.82603674531e-02 - 1.63250724283e-02 1.87752638656e-02 2.16954377207e-02 2.34052280934e-02 2.30869363929e-02 2.09600950825e-02 - 1.73737146221e-02 1.27704964379e-02 7.97163641604e-03 3.88822807317e-03 1.19938568383e-03 3.99318341872e-04 - 1.65626179475e-03 4.48400646899e-03 7.92450468345e-03 1.10980620206e-02 1.34422126209e-02 1.48944639791e-02 - 1.59370472447e-02 2.30974676194e-02 2.95996309165e-02 3.23843246845e-02 3.13043255442e-02 2.74943010791e-02 - 2.17342070894e-02 1.48252320983e-02 8.12780450420e-03 3.07272238402e-03 5.30198166447e-04 6.46227754013e-04 - 2.81411593198e-03 5.82898965033e-03 8.42100449126e-03 9.84707831750e-03 1.03512968041e-02 1.16232871057e-02 - 2.24875796029e-02 3.37722330188e-02 4.10268831080e-02 4.23356472933e-02 3.92799061712e-02 3.33213403143e-02 - 2.49305178956e-02 1.52891088083e-02 6.82917348025e-03 1.64360464113e-03 1.86562149136e-04 1.52168307387e-03 - 4.13565877440e-03 6.52864573310e-03 7.67444230152e-03 7.63112583774e-03 8.24519885098e-03 1.27525336135e-02 - 3.37296901728e-02 4.06678061829e-02 4.28369292367e-02 4.10897596693e-02 3.58712154374e-02 2.69657019121e-02 - 1.58371968408e-02 6.27201589113e-03 1.30767631352e-03 6.95975956318e-04 2.13885613626e-03 3.69307192980e-03 - 4.56833500015e-03 4.80839352292e-03 5.20491420049e-03 7.48330853076e-03 1.35571431549e-02 2.33759228978e-02 - 3.86599027285e-02 4.37561278107e-02 4.49477457631e-02 4.26333440173e-02 3.57345867038e-02 2.40581173911e-02 - 1.14226011294e-02 3.24213630394e-03 1.06234225115e-03 2.18940881846e-03 3.57585634420e-03 3.98391127876e-03 - 3.65019810926e-03 3.57632367794e-03 5.20479642137e-03 1.00826286952e-02 1.87930896440e-02 2.95291211013e-02 - 3.87309237740e-02 4.24255216603e-02 4.31291779387e-02 3.96526145437e-02 3.02519481436e-02 1.70449905394e-02 - 6.33854098633e-03 2.06540331681e-03 2.39107539830e-03 3.64068514342e-03 3.92065749553e-03 3.18904048766e-03 - 2.43886885700e-03 3.12396283502e-03 6.52798075281e-03 1.32245754755e-02 2.24545728784e-02 3.18169522228e-02 - 3.45757945183e-02 3.69211742293e-02 3.61049296851e-02 3.01658921977e-02 1.94007291239e-02 8.91182148958e-03 - 3.59185314089e-03 2.86138101893e-03 3.49913462474e-03 3.68656233607e-03 3.05369464711e-03 2.00629253113e-03 - 1.72235072441e-03 3.55594500418e-03 8.10235760504e-03 1.49820314895e-02 2.28723997347e-02 2.98321435078e-02 - 2.70255749641e-02 2.70546046573e-02 2.34643753982e-02 1.59848051822e-02 8.03732612993e-03 3.89265998914e-03 - 3.27978109135e-03 3.36141859901e-03 3.12023556081e-03 2.68005979080e-03 1.92342072039e-03 1.19389530186e-03 - 1.68195995658e-03 4.29603050935e-03 8.87354363587e-03 1.45849811373e-02 2.02345595657e-02 2.45923218560e-02 - 1.71300832549e-02 1.44980037829e-02 9.38840434433e-03 4.09525147994e-03 1.93305776643e-03 2.63433662580e-03 - 3.17610165134e-03 2.71090277149e-03 2.20073262284e-03 1.80385051394e-03 1.19776986788e-03 9.14890040248e-04 - 2.01944369523e-03 4.72490225998e-03 8.39962868843e-03 1.22777725638e-02 1.55072922459e-02 1.72926040992e-02 - 7.49135183427e-03 4.08833764664e-03 9.13396696572e-04 2.84522835348e-04 2.05968688821e-03 3.36636779629e-03 - 3.02213995911e-03 2.28022180928e-03 1.85836510124e-03 1.41057221990e-03 8.65753815670e-04 1.00349084531e-03 - 2.34869062908e-03 4.48470434402e-03 6.80504644624e-03 8.81848850797e-03 9.90872460039e-03 9.53644996601e-03 - 1.60032437534e-03 3.22096493942e-04 1.13596401095e-03 3.81100538224e-03 5.72519343130e-03 5.30982317412e-03 - 3.85736183191e-03 2.74531531556e-03 1.96544520357e-03 1.20663129458e-03 7.98965222105e-04 1.25432260198e-03 - 2.36071403557e-03 3.58847529408e-03 4.64343073578e-03 5.20667676379e-03 4.86275280446e-03 3.50580533070e-03 - 1.16578853167e-03 3.22223291139e-03 6.90229600545e-03 9.86363553509e-03 9.88470481167e-03 7.63121415038e-03 - 5.14829015320e-03 3.29277953377e-03 1.93615183336e-03 1.08362901817e-03 9.71102996585e-04 1.42326707670e-03 - 1.97259010475e-03 2.40172067388e-03 2.60947122969e-03 2.38391531369e-03 1.68600081032e-03 9.74002263626e-04 - 4.95548499978e-03 9.13882867487e-03 1.30278255496e-02 1.42193418223e-02 1.22075516780e-02 8.74289753029e-03 - 5.55491777347e-03 3.22605222260e-03 1.80129648987e-03 1.23804449324e-03 1.23460583288e-03 1.34341324587e-03 - 1.37873849744e-03 1.34692200137e-03 1.20418392729e-03 9.92677734961e-04 1.10525376936e-03 2.22168079217e-03 - 1.02990003090e-02 1.44096355935e-02 1.64538401652e-02 1.53621512813e-02 1.20254108134e-02 8.20994902081e-03 - 5.09888866787e-03 3.07972776989e-03 2.06060758237e-03 1.63568431411e-03 1.36048846122e-03 1.07300273265e-03 - 8.21756192681e-04 6.58777676519e-04 7.08268495958e-04 1.33040660393e-03 3.01678791895e-03 6.08571361405e-03 - 1.48992024043e-02 1.73233989999e-02 1.69915920968e-02 1.43171616050e-02 1.07136192511e-02 7.43969635489e-03 - 5.08665780364e-03 3.64698936616e-03 2.74358908247e-03 1.99845364289e-03 1.30858469570e-03 7.51417369189e-04 - 4.17374914722e-04 4.79418583581e-04 1.29745568715e-03 3.26974669188e-03 6.54511055441e-03 1.07769038769e-02 - 1.74056895799e-02 1.78577646408e-02 1.60737958453e-02 1.31845390644e-02 1.02997573528e-02 7.99333514459e-03 - 6.29416497433e-03 4.89350064979e-03 3.52905978523e-03 2.22715872363e-03 1.15214285024e-03 4.32413285559e-04 - 2.57525140913e-04 9.73195479131e-04 2.90914320695e-03 6.13634231690e-03 1.03188015515e-02 1.45418309823e-02 - 1.74136565717e-02 1.69573394037e-02 1.54527698527e-02 1.36487019592e-02 1.19151741791e-02 1.02844038936e-02 - 8.52861538976e-03 6.47226159515e-03 4.28875247290e-03 2.33747407903e-03 8.83084312047e-04 1.61266782607e-04 - 5.04544062804e-04 2.15732075959e-03 5.06045296295e-03 8.86363372377e-03 1.28824073710e-02 1.60470549784e-02 - 1.58267021467e-02 1.63753245199e-02 1.67623754613e-02 1.66311997508e-02 1.57469800207e-02 1.40185432650e-02 - 1.14044109594e-02 8.17204856921e-03 4.91818400531e-03 2.21522143435e-03 4.81943140265e-04 9.50464728678e-05 - 1.25183920078e-03 3.73257867634e-03 7.00127426879e-03 1.04088018049e-02 1.32421659032e-02 1.49871106130e-02 - 1.51529661785e-02 1.84806444609e-02 2.13181909661e-02 2.23597524510e-02 2.13672840792e-02 1.85918784080e-02 - 1.44287370976e-02 9.60308326237e-03 5.08103449100e-03 1.71979689730e-03 1.02394643703e-04 4.33408752474e-04 - 2.38578163893e-03 5.20566487137e-03 8.03804052422e-03 1.02086368679e-02 1.15609850291e-02 1.28325486770e-02 - 1.82580537099e-02 2.46926241846e-02 2.89364529077e-02 2.97465177652e-02 2.75358124478e-02 2.29946994668e-02 - 1.68009350202e-02 1.00958341387e-02 4.40719731787e-03 9.45132716516e-04 6.68610205368e-05 1.28088878030e-03 - 3.62558565396e-03 6.06631957400e-03 7.78257459243e-03 8.61168656751e-03 9.58066314439e-03 1.25604326212e-02 - 2.55453599910e-02 3.33962381411e-02 3.71052173863e-02 3.65492947127e-02 3.27156458330e-02 2.61444864172e-02 - 1.75775028346e-02 9.00482842134e-03 2.88622565948e-03 3.53897241711e-04 7.26271071079e-04 2.53519431044e-03 - 4.52800505753e-03 5.91071083909e-03 6.47187027698e-03 7.09870172763e-03 9.87012837469e-03 1.64328746734e-02 - 2.87933177142e-02 3.26075839679e-02 3.32340036005e-02 3.09568818669e-02 2.58532765825e-02 1.85190289839e-02 - 1.11262612872e-02 6.26479305834e-03 4.55093152535e-03 4.50337335753e-03 4.51237045033e-03 4.02793349234e-03 - 3.35322452334e-03 3.20110215581e-03 4.52152243125e-03 8.23893713824e-03 1.45408609885e-02 2.21661350822e-02 - 3.07563342999e-02 3.35128462045e-02 3.35107523701e-02 3.05758406508e-02 2.45142679719e-02 1.68745361886e-02 - 1.08027052714e-02 7.93915867323e-03 7.07770824306e-03 6.33783824197e-03 4.97144281157e-03 3.27054087858e-03 - 2.09376831952e-03 2.50499050005e-03 5.30100483593e-03 1.06588801916e-02 1.78714368175e-02 2.52247894116e-02 - 2.92188550696e-02 3.09755490461e-02 3.03114954964e-02 2.67321062823e-02 2.09282889798e-02 1.55775540652e-02 - 1.25367889297e-02 1.08253313234e-02 8.89730394555e-03 6.50526609359e-03 4.01015177384e-03 1.93040897794e-03 - 1.18890078968e-03 2.70269969505e-03 6.67154869956e-03 1.25587248207e-02 1.92536139318e-02 2.52093073117e-02 - 2.44413294646e-02 2.49340151339e-02 2.33404764250e-02 2.00937468483e-02 1.72582310912e-02 1.61223456794e-02 - 1.49068723101e-02 1.19178789750e-02 8.12563275597e-03 4.88456931476e-03 2.39387305363e-03 8.31664321628e-04 - 1.00731359667e-03 3.42565229697e-03 7.68291216181e-03 1.29203960429e-02 1.80878864900e-02 2.21142587363e-02 - 1.71449642935e-02 1.61923882394e-02 1.43928749447e-02 1.36663563486e-02 1.52139743460e-02 1.64398800100e-02 - 1.41657317531e-02 9.53287299437e-03 5.49269112877e-03 2.85792244908e-03 1.09293573055e-03 3.56808342615e-04 - 1.35505816684e-03 4.04863868863e-03 7.68738021042e-03 1.14879193761e-02 1.46848546466e-02 1.66476787717e-02 - 9.02582452524e-03 7.53977690448e-03 7.33884023741e-03 9.98928830235e-03 1.34961476195e-02 1.36059034280e-02 - 9.91225414898e-03 5.75021868034e-03 3.06624796636e-03 1.44662322195e-03 3.97327936001e-04 4.04358105930e-04 - 1.81282809597e-03 4.08760696610e-03 6.53621892597e-03 8.65549979924e-03 9.93842290190e-03 1.00448369490e-02 - 2.77352531491e-03 2.50014903777e-03 4.78431178628e-03 8.80482302566e-03 1.08363581925e-02 9.01202431421e-03 - 5.63278982253e-03 3.13675920302e-03 1.69344851219e-03 6.73423261722e-04 1.88840141540e-04 7.12457822913e-04 - 1.99230176389e-03 3.40532697307e-03 4.59296083000e-03 5.26260361416e-03 5.09655699295e-03 4.08352610426e-03 - 5.04014507355e-04 2.36958286961e-03 6.02273553487e-03 8.88133123141e-03 8.53337522081e-03 5.95566477331e-03 - 3.53363308396e-03 2.02432820576e-03 9.94283781574e-04 3.14017620910e-04 3.33223729513e-04 9.77900482741e-04 - 1.72521142177e-03 2.27761150611e-03 2.51802894441e-03 2.25823134307e-03 1.46787274143e-03 5.73710932775e-04 - 2.14784776072e-03 5.42852310833e-03 8.62068246284e-03 9.41387160044e-03 7.54323065925e-03 4.89280068529e-03 - 2.84866940516e-03 1.47808147808e-03 6.08430634744e-04 3.53758915799e-04 6.23498278249e-04 9.79935089522e-04 - 1.16489907279e-03 1.15849679383e-03 9.06412736699e-04 4.13654006868e-04 1.54829745589e-05 3.60032306854e-04 - 5.79208270834e-03 8.94033550436e-03 1.05015150129e-02 9.51854694394e-03 6.93816618884e-03 4.30967028143e-03 - 2.35618509013e-03 1.16015948217e-03 6.82394364394e-04 6.97027876008e-04 7.90845572211e-04 7.45829608206e-04 - 5.94242597398e-04 3.73770050447e-04 1.42862937039e-04 1.79710725993e-04 9.41331839663e-04 2.81457583834e-03 - 9.33307330067e-03 1.11309308744e-02 1.08368854716e-02 8.74317460461e-03 6.04702459913e-03 3.73785767064e-03 - 2.21420691166e-03 1.47197557983e-03 1.21278751439e-03 1.03309393143e-03 7.54160151858e-04 4.41633711866e-04 - 1.89000256924e-04 1.02000108192e-04 4.34201168612e-04 1.52743836257e-03 3.56749903953e-03 6.40073357079e-03 - 1.15606184893e-02 1.17009407374e-02 1.01599975116e-02 7.83977390043e-03 5.63854636290e-03 4.01769466596e-03 - 3.02690100116e-03 2.40862826941e-03 1.83516544638e-03 1.19808902190e-03 6.00498335273e-04 1.71574682898e-04 - 5.04478039690e-05 4.76241460831e-04 1.71922640229e-03 3.87624321883e-03 6.72855869815e-03 9.63077409346e-03 - 1.22370225844e-02 1.13984727669e-02 9.75910090928e-03 8.07008964708e-03 6.68739366290e-03 5.61801210572e-03 - 4.64409420980e-03 3.54674458796e-03 2.34248178615e-03 1.22804233104e-03 3.91206635043e-04 8.79025581719e-06 - 3.13194674414e-04 1.49830297385e-03 3.58105037444e-03 6.36051631787e-03 9.31239161969e-03 1.15389654699e-02 - 1.19170944319e-02 1.14025053907e-02 1.07545653382e-02 1.00924400047e-02 9.31325477945e-03 8.20839756581e-03 - 6.61884269317e-03 4.67325307258e-03 2.73571700225e-03 1.14764675956e-03 1.81055021820e-04 1.03528237408e-04 - 1.05329424178e-03 2.91507515053e-03 5.42267085168e-03 8.17905927150e-03 1.05296015334e-02 1.18041904408e-02 - 1.18780927868e-02 1.29042023189e-02 1.37276417106e-02 1.38885978900e-02 1.31299530793e-02 1.13419777359e-02 - 8.69546541900e-03 5.70387136993e-03 2.96792026575e-03 9.85720856542e-04 1.48042612111e-04 6.08896208183e-04 - 2.14099158585e-03 4.31069763199e-03 6.67902494157e-03 8.76620182360e-03 1.01823472010e-02 1.10393137670e-02 - 1.36915127174e-02 1.67101686772e-02 1.86920088963e-02 1.90002773762e-02 1.75488857931e-02 1.45547597272e-02 - 1.05691653148e-02 6.41945352250e-03 2.97690619321e-03 9.30465421511e-04 5.44866095709e-04 1.52913681570e-03 - 3.28451920664e-03 5.23994934297e-03 6.91555700634e-03 8.07065722542e-03 9.09194477371e-03 1.08604978885e-02 - 1.80779527092e-02 2.25124993304e-02 2.47282045568e-02 2.43618112814e-02 2.16909343884e-02 1.72229588873e-02 - 1.17776579808e-02 6.57760558775e-03 2.88480106537e-03 1.31572795042e-03 1.53206286863e-03 2.69755291322e-03 - 4.10601710377e-03 5.30389297022e-03 6.09724654645e-03 6.92763659341e-03 8.93531278140e-03 1.29105044866e-02 - 2.39158403900e-02 2.85401272681e-02 3.00743096125e-02 2.86464672889e-02 2.46895594620e-02 1.87131221485e-02 - 1.19151068052e-02 6.24495630174e-03 3.15674062506e-03 2.50005166860e-03 3.04013608643e-03 3.72988368134e-03 - 4.18196265379e-03 4.43026071664e-03 4.93286587353e-03 6.74495423499e-03 1.09318643802e-02 1.72885625802e-02 - 2.21774032521e-02 2.43605545788e-02 2.46333447022e-02 2.33987706719e-02 2.10922945682e-02 1.83594859041e-02 - 1.59909257039e-02 1.41416341491e-02 1.21599125174e-02 9.55453633409e-03 6.56597333855e-03 3.82954126500e-03 - 2.05017882267e-03 1.90630321725e-03 3.82067000135e-03 7.68910989012e-03 1.28242992993e-02 1.80750661953e-02 - 2.27285864571e-02 2.45105681312e-02 2.50518716786e-02 2.48158469085e-02 2.42784181560e-02 2.37770576999e-02 - 2.27689185970e-02 2.01024573484e-02 1.56310202331e-02 1.04935612564e-02 5.86388379420e-03 2.47048766135e-03 - 9.58016776488e-04 1.78351498114e-03 4.80401843189e-03 9.36697209080e-03 1.45739030672e-02 1.93381711138e-02 - 2.09264372193e-02 2.26541320377e-02 2.42231749510e-02 2.63848897160e-02 2.93861904362e-02 3.15698209479e-02 - 2.99420364230e-02 2.38345097134e-02 1.59473580042e-02 9.04538146681e-03 4.03733612704e-03 1.08339902850e-03 - 5.08457260777e-04 2.30655446675e-03 5.82686165293e-03 1.02269694221e-02 1.46776547614e-02 1.83698500366e-02 - 1.72420137629e-02 1.93702114800e-02 2.29378046511e-02 2.88659132789e-02 3.54561989137e-02 3.76218114563e-02 - 3.21528721010e-02 2.21236646139e-02 1.27517150635e-02 6.18414156484e-03 2.10785527872e-03 2.50342581323e-04 - 6.88616707871e-04 2.97385467405e-03 6.24137385549e-03 9.75669318970e-03 1.29200963382e-02 1.53519138335e-02 - 1.26671004880e-02 1.57755137735e-02 2.20108125390e-02 3.09881339310e-02 3.76537120706e-02 3.59949224984e-02 - 2.67018138576e-02 1.60384831070e-02 8.23332474468e-03 3.43870953773e-03 7.65793657283e-04 5.57124080500e-05 - 1.13981270285e-03 3.26948763221e-03 5.69629891885e-03 7.95454199301e-03 9.73617356779e-03 1.10893376658e-02 - 8.58549126530e-03 1.30701203954e-02 2.10307728137e-02 2.94777764005e-02 3.21604450651e-02 2.66205094792e-02 - 1.73110423216e-02 9.46782506222e-03 4.47444400395e-03 1.51151974057e-03 1.17411239800e-04 2.64801032497e-04 - 1.44300178111e-03 2.92467814397e-03 4.30045035244e-03 5.35617098551e-03 6.04401663542e-03 6.77367423854e-03 - 6.21237734688e-03 1.15908568005e-02 1.87549873310e-02 2.34015342066e-02 2.18504091251e-02 1.57025350665e-02 - 9.30291821225e-03 4.81764464644e-03 2.01254369162e-03 4.11083030832e-04 1.45497292943e-05 5.40524660231e-04 - 1.35128358668e-03 2.06430025815e-03 2.55321883367e-03 2.75891712171e-03 2.89273302323e-03 3.65762508872e-03 - 5.81075284356e-03 1.07732725002e-02 1.51726043311e-02 1.58979340214e-02 1.26384344151e-02 8.10417098520e-03 - 4.49180892472e-03 2.09192081936e-03 6.02108831196e-04 1.71521513626e-05 1.93380151392e-04 6.16604906359e-04 - 9.32561996333e-04 1.07415321021e-03 1.02541120826e-03 9.00473125393e-04 1.15952248629e-03 2.55090346844e-03 - 6.61076397347e-03 1.00164143384e-02 1.15205427708e-02 1.00843805528e-02 6.96378574838e-03 4.01353562893e-03 - 1.92971698699e-03 6.34729993749e-04 7.49724935279e-05 1.15865350942e-04 3.47717473062e-04 4.67223771992e-04 - 4.44105484508e-04 3.10805513911e-04 1.57577183455e-04 3.01933088122e-04 1.22476181951e-03 3.32905569483e-03 - 7.56379915179e-03 9.06153569382e-03 8.56222444584e-03 6.40415728250e-03 3.88750633530e-03 1.92956385433e-03 - 7.34366965705e-04 2.36982835325e-04 2.29203409171e-04 3.46700264980e-04 3.48695732272e-04 2.40711827381e-04 - 9.47293947307e-05 8.66347045297e-06 2.25148261856e-04 1.06732298321e-03 2.70910806770e-03 5.05914355237e-03 - 8.01287778692e-03 7.88030454920e-03 6.35145441267e-03 4.25650221311e-03 2.44649516454e-03 1.28985780716e-03 - 7.77583817417e-04 6.72014755952e-04 6.36554882331e-04 4.85877140187e-04 2.59321517386e-04 6.29687565033e-05 - 1.46953059708e-05 3.18085841471e-04 1.20449168861e-03 2.73167615253e-03 4.71526427874e-03 6.72145163847e-03 - 7.82944475985e-03 6.75408122608e-03 5.11618962587e-03 3.62161353573e-03 2.60437730776e-03 2.05250944817e-03 - 1.75126407242e-03 1.43805132932e-03 1.00547815546e-03 5.34049934977e-04 1.59883633669e-04 2.95474852402e-05 - 3.29995499377e-04 1.21819910886e-03 2.68784334319e-03 4.54908039443e-03 6.42618374403e-03 7.71352767880e-03 - 7.35455191875e-03 6.31048512851e-03 5.34191131072e-03 4.68336569765e-03 4.24004404942e-03 3.77854123091e-03 - 3.10462118566e-03 2.22238526570e-03 1.30570772041e-03 5.49006894650e-04 1.42890759392e-04 2.90113148849e-04 - 1.08781574672e-03 2.43697830818e-03 4.14278044905e-03 5.94470307681e-03 7.37206198956e-03 7.87680637841e-03 - 7.31557096543e-03 7.19131690445e-03 7.22450507120e-03 7.20523735690e-03 6.84423151359e-03 5.93803701236e-03 - 4.55110377163e-03 2.99234517340e-03 1.59542435123e-03 6.45631389985e-04 4.05808657637e-04 9.67330037404e-04 - 2.12994130721e-03 3.60559787015e-03 5.18681778808e-03 6.58694232187e-03 7.40727171752e-03 7.52823707701e-03 - 8.52349545552e-03 9.68676206385e-03 1.05495359125e-02 1.07188207332e-02 9.94455879694e-03 8.25672215235e-03 - 6.03655679151e-03 3.80430061667e-03 2.02896592596e-03 1.09849706091e-03 1.16722283967e-03 1.99263434843e-03 - 3.16285211789e-03 4.41127230810e-03 5.54690073963e-03 6.36153552080e-03 6.88873298522e-03 7.51827676044e-03 - 1.13551308461e-02 1.35922321789e-02 1.48192674381e-02 1.46810842567e-02 1.31476975109e-02 1.05704029268e-02 - 7.56973014130e-03 4.82772839628e-03 2.95262399821e-03 2.24437105086e-03 2.47582642867e-03 3.13673325326e-03 - 3.88390669981e-03 4.56589560680e-03 5.09082044666e-03 5.63647998305e-03 6.75284531736e-03 8.79292117447e-03 - 1.53771439886e-02 1.81296966851e-02 1.91853088057e-02 1.84090477976e-02 1.60913326652e-02 1.28063549345e-02 - 9.31005759761e-03 6.45379303479e-03 4.81513960694e-03 4.26103352338e-03 4.16642660012e-03 4.09279212514e-03 - 3.98398378759e-03 3.91828371035e-03 4.15481206136e-03 5.30110890794e-03 7.87399016176e-03 1.15860845561e-02 - 1.94313613775e-02 2.20813814455e-02 2.26558326800e-02 2.13558824788e-02 1.86515134072e-02 1.51577133084e-02 - 1.17651236806e-02 9.33851970450e-03 7.97246154126e-03 6.98286298557e-03 5.79448712543e-03 4.43835072788e-03 - 3.28241465272e-03 2.78751419075e-03 3.54172784103e-03 6.05036009918e-03 1.02127245127e-02 1.51150618777e-02 - 1.64009966583e-02 1.84881135923e-02 2.03427709498e-02 2.26136291427e-02 2.53424047301e-02 2.76571456446e-02 - 2.80245396944e-02 2.52650520412e-02 1.97520925187e-02 1.32278660001e-02 7.39768017578e-03 3.18795953139e-03 - 1.02814678262e-03 1.06145178646e-03 2.99784679711e-03 6.19154672476e-03 9.93147588622e-03 1.35251897294e-02 - 1.71049550301e-02 2.01593612248e-02 2.43624877869e-02 3.02384607775e-02 3.67593649243e-02 4.09156194995e-02 - 3.94611310038e-02 3.21153548069e-02 2.20314252186e-02 1.27125423933e-02 5.81856268370e-03 1.69599482257e-03 - 3.18545617272e-04 1.29977739268e-03 3.86676260322e-03 7.25509587205e-03 1.08680088174e-02 1.41990368959e-02 - 1.70191649768e-02 2.20878442797e-02 3.00553693139e-02 4.07233867326e-02 5.05653109407e-02 5.35677645024e-02 - 4.66822131444e-02 3.34560667308e-02 2.00790272117e-02 1.00022512405e-02 3.64126540132e-03 5.56112289193e-04 - 2.39484104635e-04 1.86210590347e-03 4.50615161645e-03 7.55175599216e-03 1.06183110032e-02 1.35990100423e-02 - 1.67776568383e-02 2.46818253485e-02 3.68977143071e-02 5.11649187124e-02 6.04486230271e-02 5.79009783691e-02 - 4.45736714745e-02 2.82112318495e-02 1.50455403831e-02 6.49512297228e-03 1.76054690679e-03 4.73208163193e-05 - 5.55784650687e-04 2.29401067159e-03 4.51120600606e-03 6.85282019358e-03 9.26386998526e-03 1.21863846457e-02 - 1.67707265144e-02 2.73179227825e-02 4.19038298834e-02 5.52586906450e-02 5.89853554670e-02 4.99656192144e-02 - 3.41511708415e-02 1.95219419054e-02 9.42725033292e-03 3.46102579696e-03 5.98301984536e-04 4.95388862792e-05 - 8.85588491142e-04 2.26926208993e-03 3.79076950032e-03 5.38430533718e-03 7.36257825939e-03 1.06472646170e-02 - 1.68258726787e-02 2.82129376633e-02 4.10905019063e-02 4.88084390889e-02 4.59402337178e-02 3.45207858598e-02 - 2.14243739034e-02 1.13174105129e-02 4.90816790405e-03 1.41789403405e-03 1.29068443036e-04 2.44892739824e-04 - 9.41645711475e-04 1.77149991945e-03 2.63853908858e-03 3.71124297296e-03 5.61200127565e-03 9.50530183775e-03 - 1.62968932634e-02 2.57879837058e-02 3.36118314483e-02 3.49436602731e-02 2.89355949576e-02 1.96169126303e-02 - 1.12659720694e-02 5.43965874288e-03 1.96330252857e-03 3.79696381120e-04 8.60400195450e-05 3.42160203240e-04 - 7.01348905192e-04 1.06655713071e-03 1.51615102611e-03 2.41383739305e-03 4.54662560334e-03 8.93329336116e-03 - 1.46349417947e-02 2.04292107913e-02 2.30267149637e-02 2.07440905242e-02 1.52025966070e-02 9.35382589093e-03 - 4.84443121555e-03 1.93883491929e-03 4.73926174186e-04 7.29843667197e-05 1.40283492900e-04 2.62433480124e-04 - 3.55706055002e-04 4.74020741261e-04 8.24262057889e-04 1.90729878206e-03 4.37699797082e-03 8.69716083587e-03 - 1.19898112537e-02 1.41995212284e-02 1.35864946507e-02 1.05049879454e-02 6.69269992971e-03 3.52409301152e-03 - 1.41602701614e-03 3.33448930601e-04 3.27121015760e-05 7.47562251899e-05 1.23612053629e-04 1.16279720576e-04 - 1.09457801388e-04 2.41992303020e-04 8.39296661471e-04 2.30783214177e-03 4.85853045235e-03 8.34573041423e-03 - 9.07727305895e-03 8.91808178686e-03 7.08091706064e-03 4.51084926669e-03 2.26670942059e-03 8.10351563715e-04 - 1.33322932960e-04 4.16893716535e-06 7.64979043271e-05 1.06018393266e-04 6.75234875560e-05 3.16376627409e-05 - 1.14187902864e-04 5.45908726171e-04 1.58696239122e-03 3.28872668246e-03 5.44724776462e-03 7.63489890467e-03 - 6.52810808770e-03 5.25314319513e-03 3.39284322659e-03 1.72557565555e-03 6.76886801725e-04 2.50373332193e-04 - 2.12579376217e-04 2.65075528176e-04 2.33307967804e-04 1.31268880800e-04 5.21267574630e-05 1.15418624576e-04 - 5.01998431404e-04 1.37887411328e-03 2.71769853804e-03 4.28227030677e-03 5.74417022052e-03 6.65771977098e-03 - 4.65726743689e-03 3.27858478268e-03 2.06155964155e-03 1.33248099840e-03 1.05338743062e-03 9.92564942954e-04 - 9.08410256855e-04 7.00479371817e-04 4.27620040329e-04 2.07104618488e-04 1.78699158780e-04 5.11219739098e-04 - 1.30230084896e-03 2.45600861300e-03 3.74228526901e-03 4.91219385774e-03 5.64895773329e-03 5.58985089210e-03 - 3.73767487494e-03 3.00657273011e-03 2.66992142504e-03 2.61635228110e-03 2.57294277692e-03 2.31595147437e-03 - 1.81989111905e-03 1.23031756636e-03 7.20907131037e-04 4.61855214325e-04 6.38701671867e-04 1.31829212827e-03 - 2.32734173422e-03 3.40523137282e-03 4.37974523429e-03 5.06534727187e-03 5.18207588236e-03 4.62928181051e-03 - 4.09135544474e-03 4.32062934442e-03 4.69293722426e-03 4.88796222990e-03 4.63226099256e-03 3.89390325399e-03 - 2.90678254008e-03 1.96761029344e-03 1.31957600920e-03 1.17862846165e-03 1.61971429391e-03 2.42610765367e-03 - 3.27165718561e-03 3.99507185836e-03 4.52520528133e-03 4.71855859452e-03 4.52242794866e-03 4.19344523040e-03 - 5.80658667709e-03 6.86642431161e-03 7.59873902168e-03 7.67706373717e-03 6.98317811484e-03 5.74550344255e-03 - 4.38133023648e-03 3.26241698654e-03 2.66410532114e-03 2.68363825734e-03 3.10264353975e-03 3.56087670055e-03 - 3.88692461760e-03 4.07553219629e-03 4.10528650099e-03 4.04140915754e-03 4.18193584617e-03 4.79786508616e-03 - 8.55542956795e-03 1.01354792395e-02 1.09177642115e-02 1.07026890655e-02 9.64409355627e-03 8.18053717555e-03 - 6.77031621651e-03 5.75661683248e-03 5.26996523088e-03 5.09678670852e-03 4.86769399345e-03 4.45456319040e-03 - 3.97161795052e-03 3.52227946852e-03 3.26123484872e-03 3.56212656661e-03 4.72872470960e-03 6.57576203829e-03 - 1.17119304031e-02 1.35120032725e-02 1.42008841680e-02 1.38798210558e-02 1.29666890014e-02 1.18887217290e-02 - 1.09295506835e-02 1.01793421219e-02 9.40309135278e-03 8.21355985727e-03 6.56740286827e-03 4.82944872473e-03 - 3.37389976450e-03 2.47777816626e-03 2.51977925262e-03 3.82395334269e-03 6.22888791813e-03 9.09748714217e-03 - 1.45371408591e-02 1.63834189167e-02 1.72320869854e-02 1.75532768049e-02 1.77737930438e-02 1.79635184259e-02 - 1.78608346375e-02 1.69416130303e-02 1.47230776015e-02 1.13488359054e-02 7.62568201205e-03 4.40191670406e-03 - 2.22274471029e-03 1.48020767121e-03 2.41626758138e-03 4.85419414581e-03 8.18635486639e-03 1.16510006931e-02 - 1.32213940089e-02 1.70182419833e-02 2.23914301815e-02 2.94003785990e-02 3.65878050096e-02 4.10760667517e-02 - 4.01221907803e-02 3.34221850373e-02 2.36058451046e-02 1.40486330140e-02 6.75272414829e-03 2.25036553521e-03 - 3.76803857475e-04 6.13988686064e-04 2.23766605879e-03 4.62075580376e-03 7.35987645146e-03 1.02051201906e-02 - 1.58900565333e-02 2.26119273482e-02 3.23853888912e-02 4.41983599915e-02 5.42599988386e-02 5.73374722104e-02 - 5.09058096754e-02 3.78111933225e-02 2.36187154895e-02 1.22363941402e-02 4.80022444411e-03 1.00281121273e-03 - 5.90013492073e-05 9.76190386392e-04 2.89735951926e-03 5.35360866701e-03 8.16828516169e-03 1.14568826233e-02 - 1.94530493601e-02 3.02125817831e-02 4.50419025935e-02 6.07124563723e-02 7.02979576370e-02 6.77174068816e-02 - 5.38116006984e-02 3.56355347787e-02 1.98417112130e-02 8.97773723858e-03 2.80146016904e-03 2.73546582563e-04 - 1.82574632568e-04 1.40309833632e-03 3.25579428565e-03 5.54435759555e-03 8.44833202038e-03 1.26576630409e-02 - 2.38388952519e-02 3.85478487852e-02 5.67279696139e-02 7.21216288143e-02 7.62094526352e-02 6.58666258502e-02 - 4.69053688502e-02 2.80502652656e-02 1.41073047009e-02 5.58059642806e-03 1.34860222156e-03 6.68186493022e-05 - 4.47952779487e-04 1.58372127560e-03 3.13033839493e-03 5.22115565060e-03 8.49137201837e-03 1.41685957477e-02 - 2.79077815755e-02 4.44089874489e-02 6.15080198982e-02 7.12863733443e-02 6.75840614305e-02 5.24402945875e-02 - 3.39278858491e-02 1.86229603388e-02 8.51564472592e-03 2.93495763556e-03 5.81013065098e-04 1.37092828581e-04 - 5.76903553934e-04 1.41240343986e-03 2.64121677666e-03 4.72001563739e-03 8.66913681619e-03 1.59485784302e-02 - 2.97511614714e-02 4.42882064181e-02 5.56979555661e-02 5.77349083108e-02 4.90216559814e-02 3.45467970946e-02 - 2.06115439873e-02 1.04357576526e-02 4.30762800320e-03 1.32671444999e-03 3.03866361214e-04 2.03968601862e-04 - 4.81786530467e-04 1.03931489696e-03 2.12047747397e-03 4.42980941605e-03 9.12578722178e-03 1.74163857559e-02 - 2.78751901228e-02 3.73735336542e-02 4.17655034191e-02 3.84999251424e-02 2.94793657415e-02 1.90639262307e-02 - 1.04889097724e-02 4.82370284402e-03 1.80476081540e-03 5.78615348624e-04 2.09219603297e-04 1.49731214031e-04 - 2.81207157319e-04 7.22430811474e-04 1.88393680488e-03 4.56311685120e-03 9.66947747517e-03 1.76825888151e-02 - 2.25276554018e-02 2.65621933470e-02 2.60887692330e-02 2.13584659406e-02 1.47561740853e-02 8.66616411855e-03 - 4.27411113731e-03 1.75523525842e-03 6.60899016734e-04 2.78330576988e-04 1.12375727665e-04 4.58803997345e-05 - 1.57271799099e-04 6.82768517804e-04 2.11743626523e-03 5.06165374583e-03 9.83374688929e-03 1.61177476506e-02 - 1.56743505858e-02 1.59649621794e-02 1.35812918299e-02 9.69264082153e-03 5.83704157273e-03 2.93411919359e-03 - 1.22174499051e-03 4.78163462578e-04 2.33454947931e-04 1.13054002666e-04 2.03741803066e-05 1.92855145430e-05 - 2.67020996242e-04 1.07333317598e-03 2.80090354370e-03 5.58533446174e-03 9.19921456583e-03 1.29745044009e-02 - 9.46122277767e-03 8.05678234340e-03 5.65079779207e-03 3.23029280734e-03 1.46375073826e-03 5.04324326495e-04 - 1.60199473019e-04 9.58300005159e-05 6.21254773570e-05 1.22663620737e-05 1.34779252301e-05 1.86793076393e-04 - 7.40739025963e-04 1.88931574766e-03 3.63825919236e-03 5.74325663492e-03 7.81805482284e-03 9.28976592598e-03 - 4.97803509205e-03 3.30121284009e-03 1.67151685080e-03 5.70359398750e-04 8.16764716980e-05 3.95365422467e-06 - 5.49510255476e-05 6.18538731566e-05 2.46123958592e-05 3.03929886963e-05 1.96102067791e-04 6.78110166943e-04 - 1.59378625043e-03 2.86694881652e-03 4.24027990875e-03 5.43151254497e-03 6.14572278217e-03 6.04233856174e-03 - 2.38524903345e-03 1.23705489246e-03 5.47554678634e-04 3.25593796984e-04 3.59590432176e-04 4.06806700875e-04 - 3.52614415912e-04 2.39648860324e-04 1.77910832431e-04 2.95835243210e-04 7.36911881328e-04 1.56017108080e-03 - 2.61968187582e-03 3.64721446005e-03 4.42882198781e-03 4.79631095927e-03 4.55826971386e-03 3.65796885746e-03 - 1.52939409807e-03 1.20104803061e-03 1.23338887527e-03 1.38326037713e-03 1.40716464207e-03 1.22506316954e-03 - 9.42851464247e-04 7.24534692494e-04 7.17187801006e-04 1.05856177101e-03 1.78252137919e-03 2.69592234521e-03 - 3.50406337967e-03 4.03570565502e-03 4.21847523599e-03 3.95382857393e-03 3.22351294069e-03 2.28063520125e-03 - 2.14110591590e-03 2.52131822385e-03 2.92853224332e-03 3.08871676724e-03 2.88300539743e-03 2.44705116101e-03 - 2.03828560488e-03 1.86361414815e-03 2.04857478867e-03 2.59026329631e-03 3.27223208373e-03 3.79725245411e-03 - 4.02967197570e-03 3.97378711910e-03 3.61934611686e-03 3.00630221760e-03 2.37738407982e-03 2.04983281896e-03 - 3.80475910203e-03 4.64999081044e-03 5.19480379672e-03 5.25731236209e-03 4.92004434942e-03 4.48781535996e-03 - 4.24816413919e-03 4.32986579164e-03 4.65975799784e-03 4.96222062778e-03 4.95939479192e-03 4.61873718109e-03 - 4.07215047268e-03 3.40247583235e-03 2.70740823684e-03 2.26007565015e-03 2.33973754961e-03 2.93772682074e-03 - 6.06394639092e-03 7.22278406035e-03 7.88920456494e-03 8.09392204754e-03 8.11722934794e-03 8.24746842885e-03 - 8.56926221030e-03 8.89411479459e-03 8.80676070638e-03 7.98506007710e-03 6.56266134380e-03 4.97467821036e-03 - 3.52954053589e-03 2.38998582190e-03 1.82485387210e-03 2.10727067742e-03 3.16490393741e-03 4.61307879159e-03 - 8.52704527839e-03 1.00225462701e-02 1.11610792301e-02 1.22584655982e-02 1.35700672989e-02 1.49927956645e-02 - 1.60329089754e-02 1.59532973731e-02 1.42379665242e-02 1.11711472199e-02 7.70161258886e-03 4.66896603331e-03 - 2.46441034612e-03 1.31725291359e-03 1.41461145372e-03 2.64060827205e-03 4.54192729369e-03 6.62654964459e-03 - 1.09117064472e-02 1.31049895816e-02 1.56327640918e-02 1.89092835394e-02 2.26943266973e-02 2.59187316025e-02 - 2.70340123408e-02 2.49064388892e-02 1.98510225106e-02 1.35698592730e-02 7.85407000515e-03 3.65338371620e-03 - 1.25327477480e-03 6.65339441725e-04 1.62339948248e-03 3.60023742203e-03 6.05913405011e-03 8.58544693456e-03 - 1.34249052373e-02 2.04066452953e-02 2.99579293320e-02 4.06452255063e-02 4.91884277872e-02 5.16907583520e-02 - 4.63497495595e-02 3.52195244454e-02 2.26403961256e-02 1.21285814543e-02 5.06081356682e-03 1.30157713027e-03 - 6.53579493009e-05 4.22417877912e-04 1.66748810478e-03 3.46462271140e-03 5.76439782021e-03 8.84011270980e-03 - 1.95466019191e-02 3.10308800184e-02 4.55752359759e-02 5.97164283340e-02 6.77259669972e-02 6.51217051175e-02 - 5.26830512132e-02 3.59563308402e-02 2.06967977993e-02 9.74640972631e-03 3.34717037357e-03 5.36412189963e-04 - 2.59507143393e-05 7.44396912754e-04 2.14410557722e-03 4.15953649471e-03 7.12322610462e-03 1.18560070406e-02 - 2.74971321709e-02 4.36424744879e-02 6.19153833017e-02 7.61746674807e-02 7.93057265949e-02 6.90731998092e-02 - 5.04847298310e-02 3.12491246371e-02 1.63088408215e-02 6.83716623174e-03 1.97195473618e-03 2.23812474768e-04 - 1.87886604123e-04 9.91530567140e-04 2.40091102413e-03 4.72347740873e-03 8.80511355753e-03 1.59622255447e-02 - 3.57076790949e-02 5.46224271699e-02 7.27081966294e-02 8.21730074516e-02 7.76340806828e-02 6.12869770138e-02 - 4.08940992105e-02 2.32947993660e-02 1.11930270825e-02 4.28846826789e-03 1.14274049118e-03 1.95516823555e-04 - 3.16490567160e-04 1.02973731561e-03 2.46735056303e-03 5.35217890654e-03 1.09652584438e-02 2.07858104504e-02 - 4.13552522538e-02 5.91103457353e-02 7.21246488361e-02 7.39055786196e-02 6.33021399882e-02 4.57162447003e-02 - 2.82324355183e-02 1.49972334429e-02 6.76813365559e-03 2.51920415263e-03 7.43891137863e-04 2.12332519311e-04 - 2.97086669001e-04 9.30476922862e-04 2.56968432783e-03 6.25636472015e-03 1.34003649840e-02 2.51458565577e-02 - 4.16779201083e-02 5.43042011532e-02 5.98485638344e-02 5.54306144181e-02 4.33477599890e-02 2.89709099456e-02 - 1.67288154909e-02 8.40205985519e-03 3.72401236890e-03 1.49632959357e-03 5.24438472414e-04 1.42866519868e-04 - 1.95249391147e-04 9.01621246191e-04 2.94151192271e-03 7.43472280825e-03 1.54787223534e-02 2.73836683912e-02 - 3.59632650536e-02 4.20437081583e-02 4.16174014740e-02 3.49677606488e-02 2.51737937203e-02 1.56814727159e-02 - 8.54813773288e-03 4.21285002268e-03 2.00413955050e-03 9.21650959552e-04 3.19683791542e-04 3.18355406160e-05 - 1.74496437675e-04 1.13531434192e-03 3.65185396596e-03 8.57001690347e-03 1.62972821021e-02 2.61854445373e-02 - 2.63724187217e-02 2.74181652970e-02 2.43244382417e-02 1.85714119267e-02 1.23235268773e-02 7.18784462283e-03 - 3.82075274993e-03 2.01380795154e-03 1.09739914582e-03 5.26666563255e-04 1.37199822365e-04 1.26714115944e-05 - 3.95035491891e-04 1.72630185323e-03 4.53696362026e-03 9.11756964952e-03 1.51952161361e-02 2.16285115313e-02 - 1.63783028503e-02 1.49482543147e-02 1.17355772776e-02 8.02669030761e-03 4.86236548158e-03 2.71584277148e-03 - 1.53470415008e-03 9.33177709162e-04 5.42232705834e-04 2.34293063274e-04 7.04923637522e-05 2.23081057313e-04 - 9.71993815002e-04 2.61037670588e-03 5.24325548216e-03 8.68371516646e-03 1.23907765104e-02 1.53542695408e-02 - 8.47980014812e-03 6.52583867077e-03 4.30420785985e-03 2.49213679889e-03 1.35177967058e-03 7.86845235025e-04 - 5.25156826429e-04 3.39906929593e-04 1.74485153194e-04 9.55264907844e-05 2.35160633722e-04 7.82236845581e-04 - 1.88986020922e-03 3.52272886866e-03 5.46100883437e-03 7.40099825924e-03 8.90195364436e-03 9.37719488129e-03 - 3.45220326582e-03 1.97981554405e-03 8.94434425687e-04 3.33738364174e-04 1.62281512458e-04 1.38378591888e-04 - 9.77708289499e-05 3.36938066275e-05 3.81615761942e-05 2.38464453630e-04 7.74319488952e-04 1.70992470584e-03 - 2.91979745498e-03 4.14615088870e-03 5.16016388778e-03 5.76252633112e-03 5.71074336482e-03 4.86718311961e-03 - 9.93587710006e-04 2.99922183142e-04 3.31264313523e-05 3.80936897831e-05 1.02109151681e-04 9.98019873553e-05 - 5.56029560418e-05 8.32379539999e-05 3.16939827237e-04 8.77186013807e-04 1.77683887914e-03 2.82833513984e-03 - 3.74576563982e-03 4.33550699037e-03 4.49945950593e-03 4.14349351879e-03 3.25787040510e-03 2.07572302922e-03 - 4.34832383669e-04 3.84491629202e-04 5.19584735381e-04 6.33474910094e-04 6.18995564195e-04 5.36645085970e-04 - 5.42428055392e-04 7.82773577198e-04 1.34929527619e-03 2.20909730137e-03 3.13922953811e-03 3.84804170006e-03 - 4.17767985556e-03 4.09783194751e-03 3.58936794550e-03 2.69923099447e-03 1.67167780347e-03 8.51504011596e-04 - 1.08641684151e-03 1.42683981991e-03 1.70514068980e-03 1.79329605041e-03 1.75071839789e-03 1.78488842017e-03 - 2.08426304549e-03 2.70452449909e-03 3.52070320607e-03 4.24644149033e-03 4.61334364521e-03 4.55673965194e-03 - 4.14886587662e-03 3.44030901867e-03 2.51147102868e-03 1.60755646531e-03 1.02905100923e-03 8.89378324656e-04 - 2.43864810738e-03 3.04830519919e-03 3.48184792280e-03 3.76701926279e-03 4.10890698246e-03 4.71746125607e-03 - 5.61336108469e-03 6.53756098602e-03 7.04029408733e-03 6.81655708962e-03 5.97172517358e-03 4.83207985450e-03 - 3.61031899248e-03 2.42406852996e-03 1.50078271332e-03 1.10263181952e-03 1.26171948099e-03 1.78192054734e-03 - 4.23105247157e-03 5.22456947806e-03 6.20010239050e-03 7.36343922619e-03 8.87160210549e-03 1.06205069074e-02 - 1.21414745064e-02 1.26970264424e-02 1.17707225200e-02 9.59871628156e-03 6.97452926902e-03 4.55306877234e-03 - 2.61163852850e-03 1.32875867147e-03 8.83962000550e-04 1.23754594793e-03 2.09759547478e-03 3.15904753952e-03 - 6.42199648766e-03 8.27744177326e-03 1.07040205048e-02 1.38858589003e-02 1.75075053831e-02 2.06795438332e-02 - 2.21257713447e-02 2.08383316668e-02 1.69882274214e-02 1.19598381589e-02 7.26186162558e-03 3.68597678746e-03 - 1.44609752976e-03 5.47116089490e-04 8.02755339048e-04 1.82852664014e-03 3.23976430828e-03 4.79536609480e-03 - 9.27106079514e-03 1.29886383468e-02 1.82518157571e-02 2.47615861047e-02 3.11225991425e-02 3.50900678662e-02 - 3.45958721066e-02 2.93125764560e-02 2.11856341172e-02 1.29891084374e-02 6.56254983575e-03 2.46081441545e-03 - 5.21858889193e-04 2.75922994422e-04 1.14042973776e-03 2.63751064518e-03 4.49294636045e-03 6.62854147545e-03 - 1.68223705906e-02 2.75391161378e-02 4.04664469346e-02 5.22490269685e-02 5.82895727993e-02 5.55126102684e-02 - 4.49336638971e-02 3.09524560683e-02 1.80831290832e-02 8.72280771196e-03 3.19202493212e-03 6.67104649838e-04 - 7.84015665147e-06 3.39527383053e-04 1.25929918177e-03 2.75362729653e-03 5.20945295891e-03 9.52752404976e-03 - 2.69810656597e-02 4.27665339963e-02 5.95277704486e-02 7.14837213670e-02 7.30014266330e-02 6.30564813233e-02 - 4.62561734279e-02 2.89925220155e-02 1.54291926604e-02 6.72379977706e-03 2.17731257107e-03 3.93394101570e-04 - 9.18407485959e-05 5.53905277765e-04 1.64775214622e-03 3.75630231557e-03 7.82589683071e-03 1.52117709522e-02 - 3.86824828654e-02 5.80683619484e-02 7.52715044047e-02 8.29669107313e-02 7.71266938230e-02 6.06755801668e-02 - 4.08522729448e-02 2.37295412303e-02 1.17978581448e-02 4.86606853609e-03 1.56247995272e-03 3.57117502066e-04 - 1.88885152993e-04 6.69436447972e-04 2.04488480582e-03 5.19948450447e-03 1.15528833895e-02 2.25474444163e-02 - 4.85444887016e-02 6.77400500683e-02 8.05783072227e-02 8.09259055471e-02 6.86643390348e-02 4.97810075339e-02 - 3.13027495816e-02 1.72279731998e-02 8.29776236043e-03 3.48177051070e-03 1.24875342233e-03 3.56858086321e-04 - 1.81717740395e-04 7.26420296905e-04 2.64152952345e-03 7.21228981320e-03 1.59861809039e-02 2.99935534351e-02 - 5.25404647831e-02 6.70517640030e-02 7.25379077391e-02 6.64429787539e-02 5.19826704209e-02 3.52784264198e-02 - 2.11135332418e-02 1.13336111097e-02 5.58136824644e-03 2.55700887283e-03 1.01898790415e-03 2.59439958900e-04 - 1.08792887701e-04 9.13347707041e-04 3.61967457606e-03 9.60826559459e-03 2.00841719699e-02 3.51427293494e-02 - 4.85399829267e-02 5.60845502976e-02 5.51393187772e-02 4.64524874502e-02 3.39859991615e-02 2.19606350507e-02 - 1.28294275368e-02 7.03943355638e-03 3.76063246920e-03 1.88873575919e-03 7.34728440902e-04 9.73846887630e-05 - 1.34281094385e-04 1.41838858942e-03 4.95175697391e-03 1.18017845731e-02 2.24174111750e-02 3.57501385902e-02 - 3.81223982864e-02 3.97804969262e-02 3.57510714426e-02 2.80347262483e-02 1.94858893938e-02 1.22827853617e-02 - 7.32901978750e-03 4.35679445209e-03 2.55682830322e-03 1.31064070581e-03 4.31551734292e-04 2.56563732818e-05 - 4.38238458960e-04 2.28807511564e-03 6.30607632056e-03 1.29418280250e-02 2.18325235633e-02 3.12325940825e-02 - 2.54515496099e-02 2.40031958169e-02 1.98539378190e-02 1.46741211520e-02 9.91866165572e-03 6.38486939639e-03 - 4.13102050964e-03 2.70794505832e-03 1.65646205016e-03 8.08651172104e-04 2.47208641832e-04 2.23777080753e-04 - 1.12566156413e-03 3.35842647053e-03 7.16171249339e-03 1.24228898116e-02 1.83600819789e-02 2.33224203641e-02 - 1.43505835785e-02 1.21655378553e-02 9.28174403569e-03 6.58364939901e-03 4.53273718567e-03 3.17477388707e-03 - 2.27246331162e-03 1.56050773839e-03 9.37686185136e-04 4.66962120561e-04 3.37654361148e-04 8.22556227759e-04 - 2.13396520583e-03 4.29448862138e-03 7.16744394103e-03 1.04271627487e-02 1.33394728873e-02 1.48581120938e-02 - 6.60504888935e-03 4.91279036721e-03 3.44802052119e-03 2.44840678066e-03 1.85011000837e-03 1.44608105781e-03 - 1.07369338329e-03 7.12281616706e-04 4.46181037912e-04 4.25174478886e-04 8.40446479276e-04 1.79717271315e-03 - 3.18920229493e-03 4.79306474738e-03 6.40904123878e-03 7.76341225887e-03 8.40105227185e-03 7.96441210427e-03 - 2.22883566628e-03 1.34583499470e-03 8.66220677705e-04 6.85780279779e-04 6.01096045306e-04 4.70432507529e-04 - 3.03519775642e-04 2.15723940430e-04 3.57605754412e-04 8.70232696926e-04 1.77966700951e-03 2.90318643566e-03 - 3.96242612482e-03 4.77366419882e-03 5.22782698395e-03 5.17448747028e-03 4.51205321594e-03 3.39990872944e-03 - 3.74787329927e-04 1.12560612514e-04 8.26840521250e-05 1.12218019913e-04 8.41490923278e-05 1.88527796747e-05 - 4.57193075236e-05 3.20529466411e-04 9.55785582890e-04 1.92069475173e-03 2.97905725755e-03 3.82560001861e-03 - 4.28724270582e-03 4.31703442315e-03 3.88356012573e-03 3.01964285925e-03 1.94579712158e-03 9.91649644230e-04 - 4.43028947088e-05 9.59475070751e-05 1.73207833253e-04 1.82217135168e-04 1.66870464868e-04 2.84273367330e-04 - 6.95630358326e-04 1.46925788489e-03 2.50103451278e-03 3.50417523226e-03 4.17489473940e-03 4.38434757984e-03 - 4.14896901156e-03 3.50172124417e-03 2.52761528428e-03 1.47013429489e-03 6.35274335813e-04 1.75725189340e-04 - 5.21635072254e-04 7.23251602732e-04 8.61474573948e-04 9.89414053440e-04 1.29380442867e-03 1.95535476122e-03 - 2.99201620448e-03 4.17423829159e-03 5.09509486217e-03 5.43902899985e-03 5.19067435608e-03 4.51784287104e-03 - 3.55622937733e-03 2.42761422630e-03 1.36587768440e-03 6.35412885075e-04 3.26800527820e-04 3.41449395897e-04 - 1.49742061870e-03 1.94081928890e-03 2.46810536038e-03 3.27751170622e-03 4.53276900856e-03 6.17689774707e-03 - 7.81259627274e-03 8.79518875184e-03 8.66410978042e-03 7.53080197746e-03 5.89386687290e-03 4.17024825305e-03 - 2.58214438068e-03 1.33593063791e-03 6.26296566782e-04 4.62823181379e-04 6.70710864371e-04 1.05779818173e-03 - 3.00052294093e-03 4.16485974963e-03 5.91629345683e-03 8.39181068997e-03 1.13596023519e-02 1.41418898981e-02 - 1.57137086800e-02 1.52449172960e-02 1.28219370189e-02 9.41703460986e-03 6.07133384411e-03 3.35376590879e-03 - 1.48426729616e-03 5.29655961198e-04 3.74459748073e-04 7.46095350710e-04 1.38131306616e-03 2.13340893873e-03 - 5.44193583881e-03 8.35214454422e-03 1.26345000288e-02 1.79135714846e-02 2.30461406848e-02 2.63358080405e-02 - 2.62428867281e-02 2.25199161773e-02 1.65870390463e-02 1.04710865069e-02 5.54800186564e-03 2.28547144475e-03 - 6.03914676341e-04 1.54363063000e-04 4.99344365405e-04 1.28601337470e-03 2.32021207291e-03 3.60560067107e-03 - 9.71171399200e-03 1.58322306943e-02 2.40429015853e-02 3.28582876465e-02 3.96063474598e-02 4.14464159653e-02 - 3.72110776864e-02 2.85131568525e-02 1.86036110146e-02 1.01948943014e-02 4.44961435874e-03 1.30933295616e-03 - 1.24733233788e-04 1.46044426337e-04 8.42772091710e-04 1.96599662315e-03 3.51222138209e-03 5.83863333877e-03 - 2.23596822056e-02 3.62779501174e-02 5.07678830582e-02 6.05102169967e-02 6.08168396665e-02 5.15231032915e-02 - 3.71316762893e-02 2.29871986264e-02 1.21931835700e-02 5.41755816198e-03 1.92230743377e-03 4.88273826972e-04 - 1.07037242284e-04 2.76758882879e-04 9.27960840299e-04 2.41603071691e-03 5.66754844261e-03 1.19970281279e-02 - 3.60541936865e-02 5.45818006755e-02 7.03937740958e-02 7.63409725318e-02 6.93588681395e-02 5.32973725587e-02 - 3.52452477204e-02 2.03296420198e-02 1.02437584412e-02 4.48791607506e-03 1.70665007508e-03 5.53793058473e-04 - 1.92182142535e-04 3.72217880810e-04 1.38300164881e-03 4.12383281638e-03 1.00222940521e-02 2.05124223854e-02 - 4.98375285240e-02 6.95727207694e-02 8.18111835636e-02 8.04417282807e-02 6.65588990634e-02 4.72404074504e-02 - 2.94401539966e-02 1.64075478410e-02 8.29762134496e-03 3.88381400738e-03 1.68972879941e-03 6.18053758672e-04 - 1.71989370972e-04 4.52195524933e-04 2.20888624426e-03 6.80733799528e-03 1.58474641502e-02 3.04396494296e-02 - 5.85092808404e-02 7.43660562419e-02 7.92837135586e-02 7.10731469206e-02 5.44575626366e-02 3.66023349398e-02 - 2.21980206638e-02 1.24932492265e-02 6.71644754619e-03 3.48464477064e-03 1.63364498292e-03 5.28554097612e-04 - 6.45944748800e-05 7.18609369114e-04 3.61038999008e-03 1.02470980021e-02 2.19521707452e-02 3.89121318704e-02 - 5.79034649594e-02 6.65413929758e-02 6.45721618883e-02 5.35829690083e-02 3.89245467176e-02 2.55201308468e-02 - 1.56570157014e-02 9.33856292271e-03 5.49972399419e-03 3.05253165863e-03 1.37632833762e-03 2.97565773257e-04 - 5.07854319394e-05 1.39386361630e-03 5.52858909329e-03 1.36714094010e-02 2.63928795582e-02 4.25070694792e-02 - 4.83284816677e-02 5.03515169198e-02 4.50884152177e-02 3.54198733620e-02 2.51038652348e-02 1.66584918350e-02 - 1.08169286065e-02 7.04129290489e-03 4.44013633087e-03 2.45869192052e-03 9.65311363004e-04 1.09115121479e-04 - 3.58737061986e-04 2.52466043792e-03 7.50930141159e-03 1.58888538808e-02 2.72748177396e-02 3.94334982513e-02 - 3.43235175753e-02 3.27833369272e-02 2.76483324594e-02 2.11459929088e-02 1.51727417387e-02 1.06590804664e-02 - 7.55057568985e-03 5.27736497450e-03 3.38590262507e-03 1.77833224161e-03 6.04599528422e-04 2.09165507836e-04 - 1.11618199660e-03 3.86696953210e-03 8.81764446680e-03 1.58980964587e-02 2.40853977847e-02 3.10865548478e-02 - 2.09397115436e-02 1.86143107042e-02 1.51702118125e-02 1.17299015770e-02 8.92789905071e-03 6.85327649244e-03 - 5.22185211296e-03 3.74591068661e-03 2.35952583645e-03 1.19269913358e-03 5.34907005351e-04 7.79038034750e-04 - 2.22655613423e-03 4.96345008705e-03 8.91588091344e-03 1.36955787198e-02 1.82203152283e-02 2.09426942020e-02 - 1.09462498459e-02 9.22769737394e-03 7.54888028094e-03 6.21352826950e-03 5.19604848349e-03 4.29537326234e-03 - 3.35559914344e-03 2.38074965359e-03 1.49112936574e-03 9.09677928695e-04 9.43717411168e-04 1.78505092404e-03 - 3.35135465856e-03 5.44542207836e-03 7.88608730259e-03 1.02757906691e-02 1.18677593830e-02 1.20535357153e-02 - 4.78732134765e-03 3.96077708602e-03 3.45906192297e-03 3.16973859369e-03 2.86472892354e-03 2.40310315141e-03 - 1.82972149630e-03 1.29637443397e-03 9.93039649899e-04 1.13489321527e-03 1.82712500484e-03 2.91347284000e-03 - 4.11578762214e-03 5.26670354236e-03 6.23958571585e-03 6.76910913452e-03 6.59520342813e-03 5.79740311407e-03 - 1.65213642090e-03 1.44438437225e-03 1.44388608464e-03 1.44478968694e-03 1.29188212916e-03 1.00868759838e-03 - 7.62472806555e-04 7.52983679542e-04 1.13920828151e-03 1.93357088829e-03 2.91712181060e-03 3.78133392418e-03 - 4.35546100692e-03 4.59227434779e-03 4.42923198591e-03 3.83897550801e-03 2.98733734961e-03 2.18173787043e-03 - 3.74661205825e-04 4.03079999778e-04 4.57030229489e-04 4.14975313164e-04 2.91054748816e-04 2.44733873332e-04 - 4.77231234091e-04 1.11383525908e-03 2.09129857678e-03 3.12394879443e-03 3.88071516706e-03 4.20602331340e-03 - 4.09965283672e-03 3.58133343864e-03 2.72354690134e-03 1.75782425092e-03 9.76408345378e-04 5.25845002751e-04 - 3.56874437487e-05 7.19277000073e-05 4.57532286351e-05 1.68550814748e-07 1.13896637113e-04 5.87690717860e-04 - 1.49765383807e-03 2.68714205183e-03 3.78834657710e-03 4.44933521683e-03 4.55546374240e-03 4.17934711646e-03 - 3.41632972795e-03 2.39339586472e-03 1.35728824517e-03 5.77358013399e-04 1.56237387323e-04 2.02915955860e-05 - 1.92937267310e-04 2.48237166345e-04 3.48564054699e-04 7.09619123319e-04 1.54564558124e-03 2.88003501498e-03 - 4.41995701110e-03 5.63319647552e-03 6.09314327857e-03 5.76527788812e-03 4.89001191540e-03 3.70839746252e-03 - 2.42238012524e-03 1.28235139548e-03 5.07654040157e-04 1.35289645360e-04 4.91341791994e-05 1.07023281288e-04 - 8.13557923482e-04 1.25186191081e-03 2.13985303663e-03 3.69008215256e-03 5.84222224353e-03 8.12361920964e-03 - 9.71939525989e-03 9.95684445387e-03 8.83310865634e-03 6.91213771127e-03 4.79067958837e-03 2.85627432727e-03 - 1.37627047710e-03 4.97098270022e-04 1.56302415360e-04 1.58300288903e-04 3.26201957625e-04 5.51373233986e-04 - 2.28579640056e-03 4.00487026954e-03 6.81315616062e-03 1.05203119367e-02 1.43529883638e-02 1.70381050874e-02 - 1.73905756503e-02 1.52252192163e-02 1.15150112248e-02 7.55196641006e-03 4.20773437381e-03 1.86776382018e-03 - 5.75436350388e-04 1.08966484858e-04 1.45305914815e-04 4.34953209368e-04 8.43104394508e-04 1.37695239919e-03 - 5.56281086000e-03 9.96982280830e-03 1.60480209583e-02 2.26602568864e-02 2.77739991801e-02 2.92413991106e-02 - 2.62743620233e-02 2.01726812995e-02 1.32676368255e-02 7.38720887492e-03 3.32226942709e-03 1.06367114081e-03 - 1.49772760321e-04 2.97086761622e-05 3.28306772379e-04 8.63074056152e-04 1.63350246510e-03 2.97285841919e-03 - 1.19622517989e-02 2.06104458920e-02 3.10120275062e-02 4.03111933824e-02 4.47884631805e-02 4.22220390008e-02 - 3.37893702529e-02 2.30823552802e-02 1.34435003557e-02 6.52412848193e-03 2.47611619138e-03 6.12578551069e-04 - 3.98559245158e-05 1.29394157049e-04 6.05630829977e-04 1.45383344810e-03 3.03088036597e-03 6.20212821257e-03 - 2.76401094892e-02 4.30681330534e-02 5.64478192711e-02 6.11169834053e-02 5.43891182395e-02 4.03602933427e-02 - 2.56715119664e-02 1.43794255632e-02 7.25430118293e-03 3.42244617426e-03 1.59953447293e-03 7.40357010084e-04 - 3.09198141182e-04 1.93071481832e-04 6.60754347813e-04 2.50189204551e-03 6.92135004970e-03 1.51110950845e-02 - 4.31965558513e-02 6.17451810117e-02 7.30977428612e-02 7.08614604392e-02 5.67725412235e-02 3.87159228181e-02 - 2.33808256140e-02 1.30061725959e-02 6.95402803860e-03 3.74218999195e-03 2.02214635667e-03 9.69203260838e-04 - 2.99060483828e-04 1.95153378061e-04 1.38987485245e-03 5.11119879254e-03 1.27602888411e-02 2.55265818344e-02 - 5.64623724185e-02 7.29700903983e-02 7.75288202501e-02 6.78701044280e-02 5.01919392011e-02 3.27272264362e-02 - 1.97808366115e-02 1.16424321382e-02 6.93716727985e-03 4.17102729253e-03 2.33083511614e-03 9.73448017772e-04 - 1.33611273177e-04 3.81860172444e-04 2.84238198888e-03 8.89859930535e-03 1.98876619675e-02 3.64608379179e-02 - 6.09994836774e-02 7.06705314077e-02 6.78205461249e-02 5.48748654411e-02 3.89021264583e-02 2.54659427732e-02 - 1.62756960621e-02 1.05716228012e-02 6.94681559292e-03 4.34048768310e-03 2.27671531970e-03 7.09474196153e-04 - 5.61049520879e-07 1.03599551071e-03 5.04055883158e-03 1.32051777423e-02 2.63503094725e-02 4.37387150336e-02 - 5.44155422230e-02 5.68261120599e-02 5.03586614169e-02 3.90661119545e-02 2.78206823294e-02 1.92436654347e-02 - 1.35199545301e-02 9.64303264568e-03 6.62020510431e-03 4.02092199093e-03 1.85111342598e-03 3.69849735195e-04 - 1.92861309927e-04 2.27272781409e-03 7.55565985027e-03 1.66811354331e-02 2.94769543146e-02 4.36880774363e-02 - 4.07500136379e-02 3.91771713814e-02 3.32218931473e-02 2.58615466554e-02 1.94321528718e-02 1.47398548890e-02 - 1.13575169799e-02 8.50503833396e-03 5.79194674218e-03 3.29423600951e-03 1.29928897370e-03 2.77272616894e-04 - 9.10121506323e-04 3.85605005156e-03 9.50458179654e-03 1.78029317662e-02 2.76732674411e-02 3.64266813929e-02 - 2.63736443323e-02 2.41346888386e-02 2.04730551194e-02 1.67830033220e-02 1.37991975188e-02 1.14674954686e-02 - 9.30475776847e-03 6.99236301891e-03 4.60571265693e-03 2.45156995619e-03 9.70856782416e-04 7.05057040057e-04 - 2.07705803151e-03 5.22009935026e-03 1.00400833329e-02 1.60488723077e-02 2.19245764328e-02 2.57657926733e-02 - 1.51856437590e-02 1.38083001778e-02 1.23140204205e-02 1.10428954743e-02 9.94124145352e-03 8.70264196403e-03 - 7.11372469289e-03 5.24433721703e-03 3.35456904160e-03 1.83424029693e-03 1.16696444907e-03 1.67220354014e-03 - 3.30205113631e-03 5.83964509232e-03 9.04613960742e-03 1.23743375877e-02 1.48644424670e-02 1.57701956770e-02 - 7.96792883125e-03 7.60772485233e-03 7.45450526335e-03 7.33642498936e-03 6.94903782257e-03 6.11024106815e-03 - 4.90183592778e-03 3.55713168612e-03 2.37997659214e-03 1.75341739168e-03 1.94007861521e-03 2.83251093721e-03 - 4.12323399777e-03 5.61537890126e-03 7.12638869007e-03 8.27147036948e-03 8.68898076015e-03 8.44766630467e-03 - 3.93367700109e-03 4.17525113167e-03 4.52511660476e-03 4.67896419199e-03 4.41272725195e-03 3.76732253331e-03 - 2.97674471444e-03 2.31285812292e-03 2.04060352822e-03 2.30671146511e-03 2.96683073255e-03 3.70562688665e-03 - 4.33351507669e-03 4.78856185666e-03 4.96066057109e-03 4.75998880610e-03 4.33096800885e-03 3.98555849523e-03 - 1.89159856023e-03 2.25818006029e-03 2.56406366512e-03 2.59356274009e-03 2.32076174609e-03 1.94517526671e-03 - 1.74208166040e-03 1.91841506264e-03 2.49228091812e-03 3.22588608156e-03 3.79549083323e-03 4.04938665330e-03 - 3.99133562829e-03 3.61914902177e-03 2.97002361508e-03 2.26351707366e-03 1.79281967121e-03 1.68718433778e-03 - 8.48143691959e-04 1.05264525989e-03 1.11572947773e-03 1.01210791191e-03 9.06217536072e-04 1.05534150087e-03 - 1.63302034214e-03 2.56925088612e-03 3.52181589824e-03 4.11279919248e-03 4.20925662985e-03 3.88558938117e-03 - 3.22491750428e-03 2.32731798536e-03 1.43249245995e-03 8.21205106593e-04 5.89401103552e-04 6.44737419368e-04 - 2.67331827956e-04 2.54830697388e-04 1.86049035408e-04 2.50112959133e-04 6.96769902287e-04 1.65219111420e-03 - 2.95393460347e-03 4.15222421828e-03 4.80032604653e-03 4.76917899382e-03 4.20388801949e-03 3.28758904170e-03 - 2.19862700262e-03 1.19027027233e-03 5.01303627585e-04 1.84791921216e-04 1.29450838606e-04 1.96491685906e-04 - 2.56111302080e-05 2.46207040985e-05 2.91449315781e-04 1.09765344245e-03 2.53340966613e-03 4.32333000886e-03 - 5.83738926327e-03 6.48600588885e-03 6.15477269717e-03 5.13688495945e-03 3.77820784513e-03 2.36543824583e-03 - 1.18660262489e-03 4.37620243580e-04 9.55488585500e-05 4.95852881280e-06 2.45285392405e-05 5.10079184455e-05 - 3.92063973253e-04 1.06423577578e-03 2.50145798935e-03 4.75084613533e-03 7.41250988460e-03 9.57814513504e-03 - 1.03077206911e-02 9.39201218663e-03 7.41210628704e-03 5.10511228916e-03 2.98526080823e-03 1.39079538476e-03 - 4.65436476056e-04 8.44425665062e-05 4.14771796630e-06 4.81202645712e-05 1.17932417736e-04 1.84926507608e-04 - 2.15906282220e-03 4.60857456624e-03 8.27631178162e-03 1.25928663675e-02 1.62320402669e-02 1.75770618793e-02 - 1.59847823655e-02 1.23586288352e-02 8.21224401846e-03 4.62902830939e-03 2.10265303276e-03 6.94815597433e-04 - 1.23603288127e-04 2.02919587919e-07 6.94077789502e-05 2.11021168517e-04 4.12261618032e-04 9.00167329398e-04 - 6.56646648073e-03 1.21777965637e-02 1.91923951954e-02 2.57366197631e-02 2.90353756183e-02 2.73319156082e-02 - 2.15798305976e-02 1.44807144888e-02 8.28511588861e-03 3.95389967643e-03 1.50167973443e-03 4.19111365898e-04 - 7.46384348596e-05 5.93552064264e-05 2.03455666363e-04 4.89345274239e-04 1.19244138907e-03 2.98264105858e-03 - 1.48788665653e-02 2.50995166593e-02 3.60440174723e-02 4.34814097729e-02 4.34829355344e-02 3.60689305078e-02 - 2.52027643226e-02 1.51038363271e-02 7.81039639907e-03 3.47688193865e-03 1.35667446862e-03 4.88481460871e-04 - 1.85567625430e-04 1.55807749073e-04 3.64934080403e-04 1.09692449602e-03 3.12670034990e-03 7.48106790627e-03 - 2.90123542488e-02 4.30854359789e-02 5.21171725963e-02 5.01992991048e-02 3.88090068492e-02 2.51119122322e-02 - 1.45614576406e-02 8.22448918631e-03 4.94034170484e-03 3.28383127731e-03 2.24418877678e-03 1.36351580642e-03 - 5.69632036424e-04 8.15775547275e-05 5.44724577350e-04 2.87394298868e-03 7.89371068059e-03 1.64685849815e-02 - 4.35915676032e-02 5.81929538603e-02 6.22624912756e-02 5.33036643534e-02 3.78170173870e-02 2.38418042366e-02 - 1.45986507860e-02 9.39269759591e-03 6.52628563561e-03 4.64640003478e-03 3.04956641594e-03 1.56744505391e-03 - 3.78621722441e-04 8.09930940244e-05 1.64212614761e-03 5.99662767919e-03 1.40883437216e-02 2.69320847868e-02 - 5.31499581860e-02 6.28506028140e-02 5.99281755002e-02 4.72017603978e-02 3.26635948933e-02 2.16515773473e-02 - 1.48612645146e-02 1.08514963075e-02 8.09098999291e-03 5.68195528489e-03 3.38835598790e-03 1.36460222689e-03 - 1.02983631757e-04 5.12751761960e-04 3.58838515182e-03 1.01482481368e-02 2.11073196544e-02 3.66180332357e-02 - 5.19671021018e-02 5.47434337505e-02 4.80609353654e-02 3.69232299855e-02 2.67771554787e-02 1.97454482551e-02 - 1.52782268559e-02 1.20261037556e-02 9.00515208638e-03 5.96224904326e-03 3.11832493176e-03 9.08984167996e-04 - 8.13278599879e-05 1.59293340636e-03 6.17777625550e-03 1.43426727716e-02 2.63276512482e-02 4.05485970475e-02 - 4.14586506595e-02 4.02010064404e-02 3.44181056502e-02 2.76263616809e-02 2.21456275990e-02 1.83265007788e-02 - 1.53466046909e-02 1.22934397552e-02 8.88347303314e-03 5.43276421273e-03 2.45341630245e-03 5.78360990334e-04 - 6.13184374875e-04 3.21756973565e-03 8.63760284411e-03 1.68035699261e-02 2.69093982571e-02 3.63864898911e-02 - 2.83650519882e-02 2.68059498618e-02 2.39067275588e-02 2.10610588979e-02 1.88221864268e-02 1.68637905522e-02 - 1.44925268983e-02 1.13792295403e-02 7.81866044890e-03 4.41582804109e-03 1.83254982300e-03 7.60593230066e-04 - 1.72481940935e-03 4.83313247057e-03 9.84656600181e-03 1.61895038690e-02 2.25725704133e-02 2.70725725053e-02 - 1.78244681820e-02 1.75114610654e-02 1.70418226691e-02 1.66068383397e-02 1.60088603914e-02 1.47606675233e-02 - 1.25398295880e-02 9.51858282482e-03 6.24312046036e-03 3.40731284544e-03 1.71007293887e-03 1.59359967148e-03 - 3.03389891803e-03 5.72024505479e-03 9.25073643926e-03 1.29633875898e-02 1.59204249779e-02 1.74911396503e-02 - 1.09524304328e-02 1.17447539955e-02 1.25842481980e-02 1.31573472545e-02 1.30268158675e-02 1.18939940159e-02 - 9.83840499714e-03 7.25296748096e-03 4.71848206626e-03 2.91128044913e-03 2.28509737436e-03 2.76429678142e-03 - 3.96517625310e-03 5.58626931020e-03 7.34652651096e-03 8.82511892184e-03 9.75503086150e-03 1.03361843522e-02 - 6.88053954745e-03 8.15713501646e-03 9.34434061500e-03 9.98922830786e-03 9.76078305527e-03 8.67477348077e-03 - 7.02519849773e-03 5.23364007868e-03 3.81650115997e-03 3.17474906484e-03 3.25857463110e-03 3.68947626794e-03 - 4.19968611647e-03 4.69164198058e-03 5.03642744758e-03 5.17301994730e-03 5.33740816265e-03 5.87579508880e-03 - 4.49231674321e-03 5.69142020189e-03 6.62513026829e-03 6.94157082510e-03 6.56607929875e-03 5.73023614703e-03 - 4.78230787324e-03 4.07054620776e-03 3.81623096406e-03 3.91541893524e-03 4.04089795782e-03 4.00171006166e-03 - 3.79948529161e-03 3.43451202207e-03 2.94614330072e-03 2.59517713326e-03 2.71794578554e-03 3.40594594923e-03 - 2.93397502718e-03 3.73364629077e-03 4.20242649487e-03 4.21566992998e-03 3.92707357324e-03 3.64935248959e-03 - 3.65481936157e-03 3.99897667713e-03 4.42211645836e-03 4.55528485445e-03 4.27844876202e-03 3.71480067764e-03 - 2.96797414553e-03 2.11567266260e-03 1.39779760992e-03 1.12975875644e-03 1.40560285577e-03 2.07707194984e-03 - 1.73170809550e-03 2.05715922820e-03 2.15803776641e-03 2.16091251531e-03 2.33243438438e-03 2.89439143328e-03 - 3.81148156163e-03 4.69330849464e-03 5.05464171347e-03 4.74593904176e-03 3.97624316304e-03 2.98358625571e-03 - 1.92383590167e-03 1.01431050263e-03 5.08053277087e-04 4.76677740219e-04 7.84129121082e-04 1.25427977251e-03 - 7.35581514658e-04 7.47425355428e-04 8.52913102584e-04 1.31494394028e-03 2.30143436701e-03 3.69480850434e-03 - 5.00129714556e-03 5.62892290753e-03 5.36561990229e-03 4.45007476432e-03 3.23137408512e-03 1.98092402452e-03 - 9.51052693666e-04 3.37216896367e-04 1.45604632839e-04 2.29829284693e-04 4.33682521713e-04 6.34414287969e-04 - 1.04358621413e-04 2.84776829527e-04 9.93901459284e-04 2.38433574802e-03 4.27813215920e-03 6.03570962727e-03 - 6.87232414527e-03 6.50474577691e-03 5.28010685420e-03 3.70953114324e-03 2.17926605285e-03 9.90564943205e-04 - 3.10240582644e-04 6.46307822580e-05 5.59328892707e-05 1.30820876239e-04 1.87255302805e-04 1.56402340255e-04 - 4.16142103095e-04 1.51107820276e-03 3.45941194098e-03 6.08643350096e-03 8.60818299942e-03 9.83332266595e-03 - 9.17732424410e-03 7.18134315041e-03 4.79873801756e-03 2.67941311281e-03 1.15900895320e-03 3.43307675170e-04 - 6.26402931921e-05 3.22143839648e-05 6.82158631734e-05 8.01745924232e-05 2.60820321760e-05 2.84591624616e-05 - 2.53862780464e-03 5.38410712294e-03 9.25222353105e-03 1.32590729111e-02 1.55905975022e-02 1.48456296754e-02 - 1.15566131951e-02 7.51317116454e-03 4.09061255113e-03 1.79471992174e-03 6.04626330248e-04 1.82569652746e-04 - 1.02490111094e-04 1.07913120155e-04 1.00161656600e-04 5.59651498646e-05 1.57155026203e-04 8.49958989845e-04 - 7.39406660839e-03 1.31137073903e-02 1.97012935826e-02 2.45770982629e-02 2.47934451136e-02 2.01310891697e-02 - 1.34178186832e-02 7.53812299421e-03 3.62529101666e-03 1.55188196124e-03 7.06239819790e-04 4.24006850794e-04 - 3.00061733315e-04 1.96391553844e-04 9.84383305921e-05 2.20434922265e-04 1.12888702052e-03 3.41280880332e-03 - 1.60625726864e-02 2.60069437038e-02 3.52219667292e-02 3.86264409743e-02 3.38075903369e-02 2.39830002491e-02 - 1.43550780536e-02 7.63624386523e-03 3.87601942609e-03 2.11590076780e-03 1.34310820822e-03 8.92844044468e-04 - 5.15116811367e-04 1.90586876220e-04 1.44646857072e-04 1.00980200330e-03 3.57243468264e-03 8.43677130733e-03 - 2.39508360172e-02 3.33179086561e-02 3.59026408329e-02 2.96688184433e-02 1.98496156402e-02 1.23091079586e-02 - 8.40373884976e-03 6.75007296082e-03 5.87714611052e-03 4.93579177062e-03 3.67767221362e-03 2.21323335507e-03 - 8.03886802060e-04 1.59688086493e-05 6.06944169509e-04 2.91202362076e-03 7.12071343127e-03 1.40702684932e-02 - 3.42297525408e-02 4.14704016073e-02 3.91302045285e-02 2.99428833329e-02 2.08106286095e-02 1.51521560791e-02 - 1.22898113724e-02 1.06004682611e-02 8.95918065889e-03 6.90365681803e-03 4.52862465326e-03 2.18512333726e-03 - 4.39696692930e-04 1.34524028627e-04 1.86205782760e-03 5.74637582557e-03 1.23448830375e-02 2.24592506314e-02 - 3.79932521341e-02 4.04142680650e-02 3.53583222758e-02 2.78050399447e-02 2.20352949610e-02 1.86094329006e-02 - 1.63598575325e-02 1.41549184454e-02 1.13639602907e-02 8.01850937900e-03 4.59635493060e-03 1.71684968901e-03 - 1.95990005296e-04 8.34954022986e-04 3.87862099457e-03 9.37746086208e-03 1.78109645931e-02 2.86611844087e-02 - 3.31072543243e-02 3.26796818784e-02 2.92666547812e-02 2.58410663057e-02 2.34274053172e-02 2.15548374545e-02 - 1.94231474417e-02 1.63860942127e-02 1.23717866015e-02 7.96564400467e-03 3.96910250413e-03 1.18637308542e-03 - 4.44289862006e-04 2.19684862974e-03 6.29378128787e-03 1.25165781064e-02 2.05240163354e-02 2.85003147779e-02 - 2.44047081823e-02 2.46867525713e-02 2.46043715407e-02 2.45225943782e-02 2.41546095218e-02 2.30290175264e-02 - 2.06073353111e-02 1.66824380571e-02 1.17909313222e-02 6.94152701535e-03 3.10441433613e-03 1.07839824904e-03 - 1.36135231235e-03 3.86414776957e-03 8.04877920388e-03 1.32776779283e-02 1.85988582260e-02 2.25928064613e-02 - 1.72782570712e-02 1.93888175797e-02 2.15202682837e-02 2.30677956930e-02 2.35495115269e-02 2.24939115300e-02 - 1.95756532893e-02 1.50871773825e-02 1.00098815536e-02 5.53775121470e-03 2.61403629456e-03 1.69838490699e-03 - 2.68031085254e-03 5.03379100858e-03 8.09677502274e-03 1.11751041664e-02 1.36678889191e-02 1.55098493316e-02 - 1.29365646907e-02 1.60452594748e-02 1.89147807041e-02 2.08556228886e-02 2.13231482410e-02 1.99212260710e-02 - 1.66812423658e-02 1.22708521473e-02 7.83923703175e-03 4.53194491312e-03 2.92971192036e-03 2.85349677722e-03 - 3.74452968412e-03 5.10538393018e-03 6.52960975568e-03 7.71163170757e-03 8.78748128227e-03 1.03908877661e-02 - 1.02472238724e-02 1.34077121594e-02 1.60994805951e-02 1.76675965137e-02 1.76525388070e-02 1.59528003439e-02 - 1.29104581785e-02 9.32343947960e-03 6.26246632220e-03 4.47607601508e-03 3.88388494076e-03 3.88623997927e-03 - 4.04651219101e-03 4.22173456484e-03 4.35017833705e-03 4.57908590604e-03 5.43056222839e-03 7.36862051056e-03 - 8.21643685028e-03 1.08644910264e-02 1.28800003728e-02 1.37515809716e-02 1.33091570952e-02 1.17424290467e-02 - 9.51450821414e-03 7.34057458164e-03 5.87721329412e-03 5.17874552278e-03 4.75837201662e-03 4.23670673209e-03 - 3.59430061478e-03 2.92874034445e-03 2.41396100837e-03 2.46367650196e-03 3.50965380025e-03 5.56665853890e-03 - 6.39405023433e-03 8.26550399301e-03 9.47106843978e-03 9.78159940719e-03 9.31796856334e-03 8.40206512394e-03 - 7.43948771776e-03 6.77230486315e-03 6.36623521199e-03 5.83738803420e-03 4.95539620238e-03 3.84050200655e-03 - 2.68662848754e-03 1.66244498882e-03 1.08820850115e-03 1.34304376138e-03 2.50815847581e-03 4.32234965583e-03 - 4.64637600320e-03 5.72534118535e-03 6.30585891024e-03 6.45821510584e-03 6.44900075157e-03 6.55010199653e-03 - 6.85646803510e-03 7.10331638128e-03 6.81331499867e-03 5.81638716541e-03 4.40471133511e-03 2.93903593546e-03 - 1.62742094931e-03 6.95494057943e-04 4.24203293261e-04 8.98356118621e-04 1.93663495023e-03 3.27774128027e-03 - 2.98389599101e-03 3.46067136834e-03 3.82328524636e-03 4.30788348791e-03 5.11036583646e-03 6.20391667427e-03 - 7.17191190619e-03 7.37902322590e-03 6.54965055098e-03 5.01964078614e-03 3.32399627578e-03 1.81377768604e-03 - 7.06776643009e-04 1.75407119774e-04 2.39159141047e-04 7.40747037080e-04 1.48352014678e-03 2.29149435218e-03 - 1.53326329779e-03 1.84611178052e-03 2.53573688064e-03 3.74952009005e-03 5.37262544690e-03 6.89055920482e-03 - 7.55348973841e-03 6.97719301771e-03 5.46628364257e-03 3.63894820994e-03 1.98048598761e-03 7.78131469797e-04 - 1.56061571629e-04 4.40573150365e-05 2.57024555992e-04 6.28415055451e-04 1.02442021667e-03 1.32645519489e-03 - 7.19813689388e-04 1.46265110500e-03 2.89590350875e-03 4.90589815021e-03 6.90737528971e-03 7.94220745368e-03 - 7.45391080871e-03 5.80532258342e-03 3.78988541959e-03 2.00273588795e-03 7.56477547911e-04 1.41295064953e-04 - 9.20360964380e-06 1.17970505609e-04 3.06434685159e-04 4.71444270863e-04 5.22989834028e-04 5.12469371217e-04 - 1.11571709309e-03 2.67445718870e-03 4.99023009616e-03 7.59579812895e-03 9.25218298360e-03 8.86855088572e-03 - 6.74689786138e-03 4.14501390825e-03 2.01251119724e-03 6.74730003656e-04 1.01718949984e-04 3.27253766017e-05 - 1.46202680935e-04 2.64471162673e-04 3.12527134590e-04 2.31885065816e-04 1.18137705356e-04 3.08493981963e-04 - 2.97197779700e-03 5.64769794227e-03 9.08150490610e-03 1.19334613975e-02 1.21910231282e-02 9.50656061405e-03 - 5.71453576212e-03 2.66012746041e-03 9.20966048989e-04 2.74039481938e-04 2.44578480132e-04 3.76906186311e-04 - 4.45312532357e-04 3.97085905430e-04 2.08497623903e-04 1.84130931501e-05 2.42173009179e-04 1.20627720687e-03 - 6.69841459323e-03 1.13594376345e-02 1.62063605507e-02 1.81959759304e-02 1.54580082974e-02 9.96813483910e-03 - 5.09618510508e-03 2.29551769392e-03 1.21877797044e-03 1.04597497914e-03 1.09172076281e-03 1.01923421931e-03 - 7.80557363105e-04 3.95957072480e-04 3.44336114103e-05 1.92943162888e-04 1.30269410651e-03 3.41397682897e-03 - 1.35633433417e-02 2.11508899494e-02 2.64099318745e-02 2.51549815935e-02 1.82368258605e-02 1.06801051560e-02 - 5.84080653674e-03 3.70593972471e-03 3.02932349044e-03 2.76562671631e-03 2.36906555392e-03 1.73920955080e-03 - 9.51571112560e-04 2.09367732544e-04 6.10113007469e-05 1.10327785658e-03 3.48536632547e-03 7.43784711676e-03 - 1.39384055066e-02 1.69130809053e-02 1.49768262245e-02 1.07162085657e-02 8.47524628685e-03 8.81424880177e-03 - 9.83200966971e-03 1.02004768521e-02 9.48846853546e-03 7.76018865894e-03 5.42965637115e-03 2.98228896111e-03 - 9.85626819877e-04 1.81564419576e-04 8.06483967713e-04 2.33563150533e-03 4.72806745741e-03 8.75710216940e-03 - 1.81313352910e-02 1.90535678913e-02 1.68008352563e-02 1.52937691172e-02 1.59077001326e-02 1.69532670637e-02 - 1.70441296216e-02 1.58195663322e-02 1.32631624615e-02 9.73614657233e-03 5.94292843215e-03 2.63261900644e-03 - 6.21057744656e-04 4.84939149231e-04 1.91723155990e-03 4.35710056202e-03 8.11624452984e-03 1.34136493350e-02 - 1.78054431025e-02 1.85057380207e-02 1.96112334424e-02 2.20485586489e-02 2.41487225921e-02 2.45664625786e-02 - 2.32117162274e-02 2.01607168923e-02 1.56140228198e-02 1.03747059300e-02 5.52596142275e-03 2.02231687171e-03 - 6.12475949304e-04 1.38895622543e-03 3.64995224412e-03 6.85506474298e-03 1.09746792620e-02 1.52316871518e-02 - 1.50010063676e-02 1.85122443350e-02 2.35902988677e-02 2.80270992949e-02 3.00534502568e-02 2.96091440242e-02 - 2.69665619010e-02 2.21760530771e-02 1.59025831802e-02 9.56892171015e-03 4.53403043356e-03 1.66209485433e-03 - 1.24989420601e-03 2.81363247545e-03 5.39397963084e-03 8.26075965853e-03 1.09232906914e-02 1.29851461054e-02 - 1.35825447839e-02 1.99515000622e-02 2.65507066788e-02 3.09546199292e-02 3.25137670985e-02 3.14228534022e-02 - 2.76121071109e-02 2.13726605709e-02 1.41369603820e-02 7.81302054093e-03 3.66393588999e-03 2.01670740212e-03 - 2.46124332602e-03 4.09576877339e-03 5.93657492686e-03 7.31508412361e-03 8.21512918899e-03 9.73075458150e-03 - 1.39008127830e-02 2.09444630949e-02 2.70084335057e-02 3.07102631838e-02 3.17529074967e-02 2.99021020091e-02 - 2.50325056605e-02 1.80954731628e-02 1.11686644104e-02 6.14442647940e-03 3.63419588831e-03 3.10159315368e-03 - 3.62070802667e-03 4.38044011656e-03 4.82756600145e-03 4.90984086228e-03 5.52366637080e-03 8.28085808113e-03 - 1.41211685580e-02 2.02789926843e-02 2.51622457086e-02 2.79421825320e-02 2.81592444643e-02 2.54629472341e-02 - 2.01637480630e-02 1.38205575268e-02 8.58071049791e-03 5.62797196872e-03 4.56104036613e-03 4.27488442995e-03 - 4.01459911625e-03 3.55005953147e-03 2.97106209303e-03 2.85446870253e-03 4.33209462254e-03 8.25384887982e-03 - 1.32734467761e-02 1.80808002968e-02 2.16314423956e-02 2.32703177434e-02 2.26149695104e-02 1.96342803915e-02 - 1.50873019846e-02 1.06219057492e-02 7.70249268894e-03 6.39594457672e-03 5.69060748720e-03 4.76370970264e-03 - 3.51385279851e-03 2.23954002189e-03 1.43160908410e-03 1.83412279079e-03 4.11093568475e-03 8.21603713725e-03 - 1.14607874302e-02 1.48707135677e-02 1.71020628898e-02 1.77782186536e-02 1.68480128824e-02 1.45945287662e-02 - 1.18442289076e-02 9.67121502084e-03 8.41712529382e-03 7.45085315784e-03 6.10645962717e-03 4.33809607694e-03 - 2.50402855750e-03 1.06690863226e-03 5.84076149330e-04 1.54994013157e-03 4.02706590451e-03 7.57337363525e-03 - 9.12510315949e-03 1.12872106357e-02 1.25428472762e-02 1.28837848630e-02 1.25048034853e-02 1.17268503863e-02 - 1.09648043739e-02 1.03173731005e-02 9.34875669411e-03 7.68477435237e-03 5.50589242358e-03 3.27632509098e-03 - 1.41151822715e-03 3.11829109935e-04 3.40883991374e-04 1.56991219896e-03 3.73324101074e-03 6.41979090410e-03 - 6.71382020937e-03 7.97355671897e-03 8.83862927767e-03 9.52578714267e-03 1.02147338283e-02 1.09255459704e-02 - 1.13539018491e-02 1.08927575813e-02 9.21994515829e-03 6.73257263264e-03 4.14892951115e-03 1.98530753087e-03 - 5.42266206394e-04 2.47857012942e-05 4.44571463289e-04 1.59520752503e-03 3.21736529420e-03 5.03864199461e-03 - 4.53732598549e-03 5.41957479602e-03 6.52850840047e-03 8.00481155987e-03 9.71609063562e-03 1.11381570359e-02 - 1.14518992216e-02 1.01721684695e-02 7.68111397700e-03 4.87610881577e-03 2.48254152734e-03 8.52018605664e-04 - 8.45631208541e-05 8.29938777821e-05 6.26403074796e-04 1.51313209809e-03 2.57765979484e-03 3.62714047948e-03 - 2.94520634373e-03 4.09338298450e-03 5.89573440241e-03 8.15226108265e-03 1.02247946201e-02 1.11435101174e-02 - 1.02770671445e-02 7.95264131795e-03 5.13602613828e-03 2.67653174468e-03 1.00234932431e-03 1.84491430162e-04 - 3.66157122970e-05 2.88839395807e-04 7.56878029564e-04 1.31883373997e-03 1.84389279588e-03 2.30807269794e-03 - 2.39824741510e-03 4.16584552081e-03 6.61190315682e-03 9.13927622464e-03 1.05842797149e-02 9.99760193050e-03 - 7.66347140832e-03 4.78449501097e-03 2.37342275280e-03 8.39967189467e-04 1.67570752917e-04 8.15303061164e-05 - 2.58803585143e-04 5.24795196685e-04 7.96994160661e-04 9.76818732050e-04 1.08538377336e-03 1.42974660874e-03 - 2.80781679507e-03 4.97830702546e-03 7.67062719958e-03 9.78416602786e-03 9.75647516917e-03 7.38185688386e-03 - 4.19302194334e-03 1.71154271980e-03 4.04147054021e-04 4.88495638362e-05 1.99146847909e-04 4.50944524514e-04 - 6.35858089276e-04 7.28548232608e-04 6.76448726859e-04 5.34600813077e-04 6.49261213656e-04 1.37398724788e-03 - 3.52671506668e-03 5.92786080458e-03 8.58513083907e-03 9.57435669709e-03 7.58590396872e-03 4.02223837362e-03 - 1.24174496914e-03 1.00796405896e-04 1.40568384544e-04 6.31375434247e-04 1.03321525214e-03 1.16270805649e-03 - 1.07090826887e-03 7.81989697491e-04 3.90441365971e-04 2.93465343801e-04 8.26942583201e-04 1.90677988513e-03 - 4.87439991338e-03 7.89309894236e-03 1.00900578877e-02 9.01607195016e-03 5.21119126193e-03 1.85096705993e-03 - 6.70927364981e-04 1.07341963778e-03 1.91482779281e-03 2.45367508114e-03 2.45381782758e-03 2.04237560937e-03 - 1.37133761898e-03 5.94679022480e-04 1.64409766634e-04 5.09759455394e-04 1.44497183893e-03 2.76189451435e-03 - 8.35559979822e-03 1.20668035362e-02 1.25987240137e-02 9.01439584896e-03 4.83208300271e-03 3.17927959967e-03 - 3.70630913973e-03 4.76815681163e-03 5.33146912294e-03 5.06070414830e-03 4.10031499095e-03 2.76481406042e-03 - 1.33274541803e-03 2.99950289289e-04 2.65348498302e-04 1.15343849298e-03 2.53516313914e-03 4.75021433623e-03 - 4.12397557795e-03 3.37306591358e-03 3.34612968679e-03 6.67953696907e-03 1.19915380077e-02 1.59841646523e-02 - 1.74370866885e-02 1.67084009573e-02 1.42435457575e-02 1.06369418208e-02 6.74152538065e-03 3.35997102288e-03 - 1.25520261482e-03 8.22957691189e-04 1.40872505554e-03 2.03455504358e-03 2.65995208516e-03 3.60808678392e-03 - 4.51341003998e-03 6.17521617271e-03 1.17380566051e-02 1.95804437039e-02 2.52951898234e-02 2.71164673697e-02 - 2.59932470751e-02 2.27547821164e-02 1.77839698932e-02 1.19931057453e-02 6.66111019039e-03 2.83721705350e-03 - 1.15341137147e-03 1.36312192805e-03 2.31218660196e-03 3.13270130874e-03 3.81590544431e-03 4.31343164657e-03 - 5.96136922938e-03 1.32433520951e-02 2.39790903711e-02 3.26544025864e-02 3.61599806995e-02 3.55472300150e-02 - 3.22991639732e-02 2.66686876892e-02 1.92221534547e-02 1.16651661380e-02 5.74266208704e-03 2.37329101721e-03 - 1.57440837006e-03 2.41380736764e-03 3.52977501931e-03 4.06697779506e-03 3.89310626968e-03 3.72879477030e-03 - 1.02995833061e-02 2.20958212906e-02 3.37181732405e-02 4.03927334532e-02 4.19730310103e-02 4.01548907478e-02 - 3.52915016139e-02 2.73407478975e-02 1.79425317822e-02 9.79610990743e-03 4.60197719259e-03 2.49589336313e-03 - 2.60965239040e-03 3.57679400175e-03 4.10195659122e-03 3.54484918279e-03 2.53169089516e-03 3.67225817822e-03 - 1.59169455472e-02 2.81158466530e-02 3.75995501888e-02 4.23813244559e-02 4.32850082327e-02 4.06894129995e-02 - 3.40232195118e-02 2.41743539178e-02 1.43126700487e-02 7.41461219227e-03 4.15550805311e-03 3.43957203403e-03 - 3.77633260745e-03 3.94407030191e-03 3.22495511760e-03 1.85928722383e-03 1.75188998713e-03 6.02759951645e-03 - 1.95738822295e-02 2.95860441574e-02 3.67214688645e-02 4.04059250969e-02 4.06571132960e-02 3.66932527478e-02 - 2.83971821246e-02 1.83079573022e-02 1.02752975452e-02 6.13545378616e-03 4.91208684921e-03 4.69843940918e-03 - 4.23774885534e-03 3.11283836546e-03 1.56897626232e-03 7.79923233263e-04 2.88284008707e-03 9.50676201531e-03 - 2.01652191020e-02 2.76754112986e-02 3.29303796193e-02 3.53849089502e-02 3.43197438251e-02 2.90642992734e-02 - 2.08057260172e-02 1.29501742587e-02 8.31023301244e-03 6.74654458652e-03 6.27689702235e-03 5.35998988279e-03 - 3.68124950210e-03 1.72362467756e-03 4.11347201589e-04 1.05918738043e-03 4.86157225236e-03 1.17982272463e-02 - 1.84099461306e-02 2.37340838124e-02 2.71926423527e-02 2.81830190033e-02 2.61251404689e-02 2.11739822956e-02 - 1.52439931390e-02 1.09546917129e-02 9.09875363729e-03 8.31968018661e-03 7.09474940558e-03 5.00264940099e-03 - 2.54317547902e-03 6.06347123805e-04 1.64820542047e-04 1.98171482661e-03 6.19861369802e-03 1.21156664909e-02 - 1.53057262817e-02 1.87613724520e-02 2.07286352395e-02 2.09441968860e-02 1.93375852231e-02 1.65046539604e-02 - 1.38292818119e-02 1.21849402810e-02 1.09993563491e-02 9.27624928325e-03 6.75643356407e-03 3.88276439333e-03 - 1.39287810966e-03 7.31695767239e-05 4.91344361186e-04 2.73144168182e-03 6.42258075281e-03 1.09050331785e-02 - 1.17936367956e-02 1.39276752046e-02 1.52501324516e-02 1.58726309986e-02 1.59235148425e-02 1.56592997382e-02 - 1.52033438467e-02 1.41157328296e-02 1.19045086068e-02 8.77105933821e-03 5.40206052116e-03 2.47658660799e-03 - 5.60596314887e-04 4.64717379967e-05 9.59408527748e-04 3.00255269971e-03 5.80667610248e-03 8.93186885467e-03 - 8.66584207354e-03 1.02479061277e-02 1.18557558842e-02 1.36318128407e-02 1.54067448707e-02 1.66367221549e-02 - 1.64682180031e-02 1.43744573315e-02 1.08213752944e-02 6.90364960043e-03 3.53811083354e-03 1.22837953040e-03 - 1.79398445160e-04 3.03298910439e-04 1.29462763478e-03 2.87324851828e-03 4.82755522548e-03 6.85815325051e-03 - 6.42329675045e-03 8.25915723541e-03 1.07469949192e-02 1.36438259923e-02 1.61551855970e-02 1.70971381024e-02 - 1.56567285259e-02 1.22072777523e-02 8.03794435117e-03 4.35820050534e-03 1.79567954334e-03 4.80958263221e-04 - 2.07898433499e-04 6.19616680946e-04 1.44766452172e-03 2.55439199828e-03 3.79504440101e-03 5.04587051309e-03 - 5.46498949013e-03 8.10528689165e-03 1.14824747929e-02 1.47431342845e-02 1.64959559252e-02 1.56494085413e-02 - 1.24538277926e-02 8.31353031432e-03 4.61521393370e-03 2.05795077841e-03 7.28468481686e-04 3.37549018237e-04 - 4.82628438772e-04 9.00742775670e-04 1.48029518643e-03 2.12645515250e-03 2.81335393588e-03 3.78315022072e-03 - 5.75266409372e-03 9.04522399376e-03 1.26695620141e-02 1.51598449236e-02 1.49272072843e-02 1.18922243965e-02 - 7.70632117570e-03 4.11274719582e-03 1.84124055659e-03 8.06521798028e-04 5.69924307367e-04 6.73262835086e-04 - 8.80986547273e-04 1.13449258958e-03 1.37186166091e-03 1.60231303032e-03 2.13035208890e-03 3.42348896618e-03 - 6.15518199318e-03 9.34637458048e-03 1.23037380072e-02 1.30681688724e-02 1.06180656804e-02 6.48040459731e-03 - 3.01881423005e-03 1.22472344337e-03 7.80678973959e-04 9.77905653392e-04 1.23704540159e-03 1.34339704162e-03 - 1.33983295734e-03 1.24237309768e-03 1.09000818966e-03 1.21146959777e-03 2.03096227877e-03 3.67951082823e-03 - 5.46846231784e-03 7.88029276816e-03 9.36208795300e-03 8.04633980387e-03 4.54487899204e-03 1.56323375524e-03 - 5.67796965171e-04 1.04237842558e-03 1.93408719805e-03 2.50896070558e-03 2.54866786681e-03 2.22670403781e-03 - 1.71624715527e-03 1.12400203346e-03 8.09605011282e-04 1.19092885424e-03 2.18076924248e-03 3.54976231800e-03 - 4.15390743218e-03 5.43175893770e-03 4.91454140245e-03 2.32451301762e-03 2.35995447599e-04 5.06071227928e-04 - 2.32391388974e-03 4.15347850844e-03 5.13301695500e-03 5.06681008689e-03 4.24237018780e-03 3.06647689165e-03 - 1.80518442576e-03 8.66236607940e-04 7.62515514786e-04 1.35343622743e-03 2.05139835939e-03 2.85406210910e-03 - 3.67827506346e-03 3.59686031474e-03 1.74117679172e-03 5.27235887042e-04 2.24935659755e-03 5.71187826889e-03 - 8.59913745216e-03 9.91643146930e-03 9.61991020892e-03 8.05673816738e-03 5.83276513538e-03 3.49967986123e-03 - 1.57739892397e-03 7.06596545695e-04 9.57113561673e-04 1.52668348675e-03 1.97047918722e-03 2.68350892101e-03 + 4.19796127760e-07 2.16785886153e-03 9.82780714783e-03 1.98314099013e-02 2.65738526290e-02 2.84325358303e-02 + 2.69957310273e-02 2.34248028415e-02 1.81739754247e-02 1.22128123871e-02 6.88896490865e-03 3.24534074330e-03 + 1.80078795971e-03 2.11652009522e-03 2.83266308314e-03 2.90713887507e-03 2.22630047545e-03 9.82082916676e-04 + 2.67982867041e-03 1.25901943401e-02 2.67750092712e-02 3.73927831636e-02 4.08080253988e-02 3.92498238923e-02 + 3.51365518936e-02 2.87939138564e-02 2.06269471162e-02 1.24500091124e-02 6.22391147441e-03 2.87783704519e-03 + 2.20330924837e-03 2.94938180961e-03 3.50814054906e-03 2.95005079423e-03 1.32902267629e-03 3.67522844661e-05 + 1.10779622580e-02 2.74702510133e-02 4.24680096881e-02 4.95475424977e-02 4.97426124804e-02 4.65046117739e-02 + 4.05513452152e-02 3.13111891211e-02 2.03337577650e-02 1.08921397609e-02 5.09701497710e-03 2.94485925341e-03 + 3.14841971649e-03 3.94725898613e-03 3.82935697181e-03 2.19726061877e-03 1.82221854139e-04 1.61558790082e-03 + 2.13956092811e-02 3.83511402787e-02 4.98309121334e-02 5.39482825366e-02 5.35370172151e-02 4.99037888042e-02 + 4.17571346814e-02 2.93994565428e-02 1.68243176274e-02 8.18980384097e-03 4.38707510375e-03 3.78780569163e-03 + 4.30413396660e-03 4.31135316918e-03 2.94335397572e-03 6.90902218897e-04 4.36986424085e-04 6.83400732546e-03 + 2.80680315686e-02 4.16048736052e-02 4.97895562440e-02 5.32208545985e-02 5.31035530431e-02 4.80821280515e-02 + 3.69230770847e-02 2.28000212867e-02 1.15788209991e-02 6.10982644518e-03 4.88003290692e-03 5.06967069873e-03 + 4.81547654111e-03 3.42866256692e-03 1.24123396700e-03 4.05081219813e-05 3.17822651912e-03 1.32214634989e-02 + 2.95041338109e-02 3.94526429695e-02 4.58148579582e-02 4.88424258357e-02 4.75134531252e-02 3.98361394093e-02 + 2.70320199568e-02 1.47924102680e-02 7.93301411007e-03 6.16080820688e-03 6.27889917975e-03 5.84923853484e-03 + 4.19908838532e-03 1.85605429835e-03 1.51359207333e-04 1.15549807388e-03 6.89864838333e-03 1.73802565206e-02 + 2.73297052092e-02 3.45427945780e-02 3.92231006193e-02 4.06201235708e-02 3.70716335978e-02 2.81839910459e-02 + 1.75663101184e-02 1.03570023777e-02 7.96334774926e-03 7.87306500531e-03 7.39189652111e-03 5.56940797037e-03 + 2.90485266079e-03 6.48460231856e-04 2.89502183498e-04 3.10149056433e-03 9.48663452308e-03 1.83306478382e-02 + 2.31482333643e-02 2.79794228179e-02 3.06623017610e-02 3.03300195074e-02 2.62260034691e-02 1.95974260262e-02 + 1.38657373864e-02 1.11830271091e-02 1.04346242129e-02 9.49848582844e-03 7.42479704869e-03 4.47824607974e-03 + 1.65935818009e-03 2.28672460710e-04 1.12260208912e-03 4.59217649355e-03 1.01917439193e-02 1.68479251078e-02 + 1.80782080284e-02 2.09926732676e-02 2.24251843165e-02 2.21006955137e-02 2.01238064974e-02 1.76419150884e-02 + 1.58842648602e-02 1.45874061568e-02 1.26868754876e-02 9.82697955765e-03 6.38869115249e-03 3.08307792588e-03 + 8.32295654334e-04 3.98641250371e-04 1.94195223847e-03 5.11174570108e-03 9.37191444362e-03 1.39900582175e-02 + 1.33836413196e-02 1.54256399243e-02 1.71047535609e-02 1.85081939452e-02 1.95897392795e-02 2.01216863756e-02 + 1.94247995602e-02 1.69128253914e-02 1.29715856260e-02 8.60423155661e-03 4.66405167461e-03 1.79686053286e-03 + 4.88914607083e-04 8.04547464768e-04 2.38020029383e-03 4.81501549014e-03 7.77885822831e-03 1.07975434693e-02 + 1.01231377849e-02 1.24617236710e-02 1.54272665135e-02 1.88077260422e-02 2.16842447509e-02 2.26239394500e-02 + 2.06385039479e-02 1.62271555551e-02 1.09712330062e-02 6.26182927635e-03 2.83091273930e-03 9.58282315606e-04 + 5.43823611322e-04 1.17001234028e-03 2.43650874038e-03 4.14791259013e-03 6.12390906619e-03 8.12095891990e-03 + 8.79682913234e-03 1.22227960873e-02 1.65687695519e-02 2.08380898572e-02 2.32715294103e-02 2.23788913960e-02 + 1.83051154931e-02 1.27617648585e-02 7.57662355619e-03 3.75205006241e-03 1.54078524747e-03 7.21589802285e-04 + 8.16578574622e-04 1.40692706056e-03 2.30926285873e-03 3.45271877037e-03 4.78554192689e-03 6.43620785694e-03 + 9.42195251705e-03 1.40302743797e-02 1.89964839595e-02 2.24661316842e-02 2.25248845553e-02 1.89230923001e-02 + 1.34178672299e-02 8.15516862432e-03 4.31256819522e-03 2.08751172021e-03 1.14579004604e-03 9.70682636577e-04 + 1.16535345992e-03 1.56339792950e-03 2.11162809404e-03 2.83535880259e-03 3.98963285632e-03 6.05454283613e-03 + 1.10244983349e-02 1.60737445898e-02 2.03832655583e-02 2.15769133238e-02 1.86204017109e-02 1.31870947483e-02 + 7.95076569104e-03 4.37597978466e-03 2.51296888421e-03 1.78603614604e-03 1.57292321845e-03 1.51894429288e-03 + 1.54532537290e-03 1.64058417149e-03 1.84596425049e-03 2.46339325073e-03 4.00316350998e-03 6.83658622338e-03 + 1.15631757191e-02 1.58184902126e-02 1.82069915417e-02 1.66622865708e-02 1.19651379753e-02 7.11964018100e-03 + 4.12727404283e-03 2.98880475201e-03 2.82643354967e-03 2.82276541307e-03 2.61305079548e-03 2.25779070908e-03 + 1.88468924841e-03 1.57846272936e-03 1.64824693288e-03 2.57405776955e-03 4.55667402435e-03 7.56937549219e-03 + 9.60443077479e-03 1.18908936756e-02 1.17267254384e-02 8.72357230284e-03 5.36141986902e-03 3.84094115633e-03 + 4.05251270285e-03 4.83721968695e-03 5.25975709415e-03 4.94274316779e-03 4.07382100273e-03 3.02606044560e-03 + 2.03662100195e-03 1.44273960529e-03 1.73321864983e-03 2.94502052581e-03 4.68907312345e-03 6.88838909093e-03 + 5.75349170951e-03 5.58204220056e-03 3.95146781196e-03 2.83753475819e-03 4.03167966136e-03 6.62301116298e-03 + 8.83029477799e-03 9.78349951991e-03 9.31632485462e-03 7.71520345991e-03 5.60401453581e-03 3.53200296581e-03 + 1.95450275615e-03 1.42857702490e-03 2.03717346991e-03 3.05788077139e-03 3.95887818269e-03 4.87807589635e-03 + 1.87887990258e-03 6.84507106271e-04 1.44927260915e-03 5.93402466510e-03 1.18938157127e-02 1.60190730759e-02 + 1.74100309389e-02 1.65784527374e-02 1.40234988043e-02 1.04179651042e-02 6.67410463304e-03 3.57453279373e-03 + 1.79687227131e-03 1.63973311459e-03 2.37416380702e-03 2.90857819730e-03 2.96135375334e-03 2.70512563790e-03 + 4.35383130227e-03 1.45209223920e-02 2.87930084915e-02 3.90040473581e-02 4.16448196395e-02 3.92620289550e-02 + 3.44647270049e-02 2.76842694383e-02 1.93998119748e-02 1.14094157193e-02 5.58793886283e-03 2.77619583168e-03 + 2.67102040148e-03 3.91877995202e-03 4.83622075736e-03 4.47486368768e-03 2.91473549344e-03 1.61559059411e-03 + 1.40667154477e-02 3.22128724245e-02 4.80307891736e-02 5.43463850344e-02 5.29730444556e-02 4.83248616727e-02 + 4.13948947016e-02 3.14600195431e-02 1.99810812371e-02 1.03256838159e-02 4.68148007453e-03 2.96382099220e-03 + 3.68352883653e-03 4.87923879505e-03 4.94465370150e-03 3.33326843532e-03 1.36944433838e-03 3.27948125335e-03 + 2.81326530908e-02 4.77189615841e-02 5.93630551323e-02 6.16822402125e-02 5.93109036114e-02 5.43888357267e-02 + 4.51799124036e-02 3.14303713061e-02 1.74056784802e-02 7.91607677465e-03 4.04299523577e-03 3.87112200829e-03 + 4.92533756717e-03 5.30860102620e-03 4.06224362076e-03 1.83445574864e-03 2.09150749120e-03 1.03636222460e-02 + 3.80672664373e-02 5.35670512666e-02 6.10890383582e-02 6.29532218041e-02 6.18560109302e-02 5.60439977424e-02 + 4.30056679741e-02 2.59241939283e-02 1.21611608289e-02 5.59389598498e-03 4.46167740101e-03 5.24860109399e-03 + 5.57674832371e-03 4.49994572781e-03 2.36561611243e-03 1.41562828128e-03 5.99359315713e-03 1.93302436934e-02 + 4.03655289097e-02 5.14670778218e-02 5.75963633541e-02 6.05128442636e-02 5.91681756735e-02 4.98947886929e-02 + 3.31895420192e-02 1.66333970526e-02 7.29196239691e-03 5.10403838009e-03 5.82811663121e-03 6.15289966606e-03 + 5.00000172962e-03 2.81902463608e-03 1.21462917733e-03 2.97589876457e-03 1.09884556178e-02 2.50828062318e-02 + 3.74091389542e-02 4.57278200014e-02 5.11675200016e-02 5.32016515393e-02 4.87166082914e-02 3.59999205076e-02 + 2.01362021216e-02 9.29501018844e-03 5.96004213196e-03 6.52097383875e-03 7.02846506513e-03 5.93213145512e-03 + 3.59201722495e-03 1.43843527799e-03 1.42942098696e-03 5.47673439800e-03 1.43144637356e-02 2.61612013068e-02 + 3.19418711649e-02 3.79368405884e-02 4.14200926047e-02 4.06418572968e-02 3.35171661403e-02 2.19207293047e-02 + 1.21170794692e-02 8.08068076447e-03 7.98333950060e-03 8.29036576173e-03 7.25686243648e-03 4.87306488376e-03 + 2.23196928445e-03 9.70750108626e-04 2.48455413534e-03 7.36204972601e-03 1.50911704047e-02 2.39677667061e-02 + 2.51537590812e-02 2.87153120446e-02 2.99527033184e-02 2.77423992966e-02 2.21303356410e-02 1.60190950025e-02 + 1.25668501331e-02 1.15623625925e-02 1.08380150531e-02 9.19520281017e-03 6.57438183252e-03 3.56802908152e-03 + 1.37241365306e-03 1.19113790416e-03 3.47866750012e-03 7.94089328373e-03 1.38215151041e-02 1.99542093007e-02 + 1.83147987285e-02 2.03263245631e-02 2.11729502760e-02 2.07705593171e-02 1.96636426182e-02 1.87182443770e-02 + 1.76961460188e-02 1.56509558133e-02 1.25123315120e-02 8.87792908998e-03 5.27112627784e-03 2.36609670799e-03 + 1.02220119308e-03 1.63492877010e-03 3.90901426436e-03 7.33420861367e-03 1.13615703467e-02 1.52445339832e-02 + 1.32137799981e-02 1.53737135485e-02 1.79192435307e-02 2.08079121483e-02 2.33330467357e-02 2.40604129080e-02 + 2.18727704626e-02 1.73403206326e-02 1.21043473992e-02 7.35492414370e-03 3.67117157700e-03 1.50418016985e-03 + 1.04418942079e-03 1.95898713387e-03 3.74579696061e-03 6.08766783807e-03 8.67042838130e-03 1.10866559431e-02 + 1.09668057772e-02 1.45662351422e-02 1.93467750779e-02 2.43356424290e-02 2.74204969219e-02 2.66276732862e-02 + 2.20343846249e-02 1.57087651957e-02 9.74020517583e-03 5.16724396714e-03 2.32205226787e-03 1.15793041181e-03 + 1.24860959997e-03 2.05291258413e-03 3.27649104497e-03 4.81299621942e-03 6.54073133844e-03 8.46715259344e-03 + 1.16346301730e-02 1.69691746147e-02 2.30107325501e-02 2.76597592078e-02 2.84768247265e-02 2.47813437511e-02 + 1.83772726268e-02 1.18130658472e-02 6.64816913431e-03 3.33408469704e-03 1.70829441835e-03 1.27337078653e-03 + 1.47806193617e-03 2.00782752477e-03 2.78959631766e-03 3.85845689628e-03 5.39562115242e-03 7.82211970976e-03 + 1.44138943329e-02 2.08170382992e-02 2.64281005481e-02 2.85503235828e-02 2.58294313772e-02 1.97252490353e-02 + 1.30673059454e-02 7.79120998524e-03 4.40787372268e-03 2.62185393686e-03 1.86993759426e-03 1.64871305148e-03 + 1.68997744220e-03 1.91886643199e-03 2.39898674099e-03 3.41922938820e-03 5.49229322446e-03 9.10531721717e-03 + 1.73471552665e-02 2.34696937981e-02 2.70097603083e-02 2.56920671228e-02 2.02368146587e-02 1.37242025972e-02 + 8.62253920865e-03 5.53813972181e-03 3.95496043851e-03 3.13079626654e-03 2.57667307829e-03 2.15410807459e-03 + 1.87285435078e-03 1.80061388509e-03 2.23971464173e-03 3.73122022347e-03 6.71082540599e-03 1.13304556679e-02 + 1.78251806268e-02 2.21189088407e-02 2.27482070487e-02 1.90849841904e-02 1.36962955822e-02 9.42385513897e-03 + 7.09227358595e-03 6.06819194257e-03 5.43295181469e-03 4.62858543916e-03 3.64192495006e-03 2.69653475880e-03 + 1.96177010255e-03 1.73459693037e-03 2.53367324945e-03 4.65465016609e-03 8.01829233619e-03 1.25402618894e-02 + 1.44559313384e-02 1.59226705828e-02 1.45807026945e-02 1.19170401367e-02 1.01599990447e-02 9.72725556709e-03 + 9.80125398817e-03 9.56596059744e-03 8.54914699951e-03 6.81134979442e-03 4.84131525620e-03 3.09100954028e-03 + 1.93957690916e-03 1.90640298700e-03 3.21760722638e-03 5.43761167251e-03 8.14100873656e-03 1.13072413154e-02 + 8.44200148259e-03 8.06972223289e-03 8.42765956568e-03 1.09201450628e-02 1.42537838938e-02 1.63711461252e-02 + 1.66710192097e-02 1.53338215280e-02 1.26198270664e-02 9.14771603162e-03 5.76975131630e-03 3.16826390185e-03 + 1.93517601513e-03 2.37499687649e-03 3.90218248324e-03 5.52515571820e-03 6.87737198130e-03 7.98780553715e-03 + 3.40818871948e-03 5.58372318324e-03 1.25357221637e-02 2.11822074231e-02 2.66220309743e-02 2.76514608933e-02 + 2.57349390652e-02 2.18825899749e-02 1.65970584216e-02 1.08963137491e-02 6.04724686960e-03 2.97557002762e-03 + 2.12667268050e-03 3.05086808028e-03 4.40692202639e-03 5.11202700384e-03 4.95922822270e-03 4.10324880800e-03 + 1.54313029090e-02 3.18035357534e-02 4.61708008469e-02 5.17656923900e-02 4.99644577426e-02 4.47561637690e-02 + 3.73321010461e-02 2.74760189009e-02 1.67409936576e-02 8.06957034862e-03 3.29597070970e-03 2.31876896217e-03 + 3.74753245294e-03 5.66160796688e-03 6.45048872089e-03 5.54503348284e-03 4.07813240533e-03 5.81538891719e-03 + 3.04654718198e-02 4.97757409810e-02 6.07634044985e-02 6.19803661426e-02 5.82687977520e-02 5.21294251502e-02 + 4.22334024126e-02 2.85120282236e-02 1.49846845777e-02 6.10276388029e-03 2.84181732017e-03 3.33803306610e-03 + 5.11177640398e-03 6.19570526940e-03 5.57622306981e-03 3.86522055678e-03 4.44365044715e-03 1.27899450316e-02 + 4.38620139354e-02 5.95995519805e-02 6.59137248386e-02 6.59754903601e-02 6.33571102407e-02 5.65197930412e-02 + 4.27123436023e-02 2.49414654239e-02 1.07176018051e-02 4.12937729889e-03 3.41548936590e-03 4.89368365875e-03 + 5.98846625870e-03 5.60215986872e-03 4.02406232733e-03 3.60628736439e-03 9.02574663225e-03 2.37487500098e-02 + 4.86752574239e-02 5.95794426847e-02 6.44049906469e-02 6.62368344223e-02 6.43499977945e-02 5.42033089620e-02 + 3.55765508495e-02 1.67375535148e-02 5.99755583837e-03 3.63071840184e-03 4.91325519438e-03 6.03058535793e-03 + 5.63299575781e-03 4.06164511155e-03 3.00649242134e-03 5.64983194708e-03 1.53851930747e-02 3.18123685991e-02 + 4.60572175278e-02 5.42591946691e-02 5.94798861968e-02 6.17666052228e-02 5.69516563468e-02 4.18700018251e-02 + 2.21623447341e-02 8.26279572360e-03 3.87770573329e-03 4.82813010872e-03 6.20443388480e-03 5.95508452906e-03 + 4.28816537072e-03 2.67190604351e-03 3.34048359444e-03 8.68812870953e-03 1.96560557655e-02 3.37161023230e-02 + 3.98534975973e-02 4.63229917361e-02 5.04161452709e-02 4.96411634014e-02 4.03025784082e-02 2.43314082773e-02 + 1.04984146399e-02 4.77252594145e-03 4.98953599319e-03 6.39051657847e-03 6.46909230542e-03 4.94559643017e-03 + 2.89935806548e-03 2.16360458513e-03 4.49751344480e-03 1.08150070581e-02 2.04683620587e-02 3.10116837757e-02 + 3.18421447321e-02 3.58562416652e-02 3.69142552665e-02 3.27138723634e-02 2.30072642003e-02 1.27298239532e-02 + 7.35331193337e-03 6.72862307682e-03 7.41842649398e-03 7.31352814177e-03 5.94179048825e-03 3.74089812992e-03 + 2.03251103908e-03 2.35966322750e-03 5.50094399110e-03 1.12654498952e-02 1.86242812016e-02 2.59268288933e-02 + 2.29777437567e-02 2.45456479389e-02 2.37536724313e-02 2.01866025783e-02 1.55082690657e-02 1.25104888022e-02 + 1.15609174398e-02 1.08813134936e-02 9.51862138171e-03 7.51958114746e-03 5.04229832488e-03 2.70557673263e-03 + 1.68211826603e-03 2.75302094477e-03 5.79513495369e-03 1.02256630632e-02 1.52197719296e-02 1.97306528484e-02 + 1.53887399235e-02 1.64002435941e-02 1.70559192948e-02 1.77633516626e-02 1.87627510423e-02 1.91364889831e-02 + 1.75520467647e-02 1.42424708855e-02 1.04773228593e-02 6.94103899581e-03 3.89948076359e-03 1.94313819426e-03 + 1.66062986541e-03 2.95880901360e-03 5.31823203677e-03 8.27506986091e-03 1.12905904676e-02 1.37451018281e-02 + 1.12884911481e-02 1.39311741239e-02 1.78672331578e-02 2.24902880278e-02 2.56777979714e-02 2.51197643465e-02 + 2.08417310608e-02 1.50785676849e-02 9.75841079446e-03 5.56829917992e-03 2.76548833701e-03 1.56113813198e-03 + 1.76434103359e-03 2.83546110791e-03 4.37959709262e-03 6.17352076787e-03 7.93194509807e-03 9.51682232306e-03 + 1.12644867926e-02 1.63349016998e-02 2.26887444805e-02 2.81622638226e-02 2.97713382358e-02 2.63843305360e-02 + 1.99003842789e-02 1.31317274524e-02 7.68603425021e-03 4.01206991502e-03 2.08620124292e-03 1.55284890677e-03 + 1.82953783473e-03 2.49454085879e-03 3.41391112116e-03 4.54812131318e-03 5.94493148237e-03 7.96476521460e-03 + 1.44567254685e-02 2.12498012603e-02 2.77423959177e-02 3.09978085118e-02 2.91213582334e-02 2.31636609270e-02 + 1.59947614545e-02 9.86044763503e-03 5.58677600729e-03 3.12740248678e-03 2.03900855544e-03 1.74322880035e-03 + 1.81966548036e-03 2.12182311077e-03 2.69098780820e-03 3.74016315439e-03 5.70804216044e-03 9.15538519941e-03 + 1.91067499768e-02 2.60824190172e-02 3.05280192303e-02 3.00462675286e-02 2.49541381954e-02 1.79858693030e-02 + 1.17593651071e-02 7.35673099330e-03 4.69080377479e-03 3.22895170949e-03 2.43693672639e-03 1.99145268307e-03 + 1.77290536949e-03 1.81349103926e-03 2.38092245025e-03 4.00386551919e-03 7.23117195491e-03 1.23519649923e-02 + 2.25935386479e-02 2.80610678778e-02 2.93428872383e-02 2.57235088240e-02 1.95092737965e-02 1.36312345383e-02 + 9.47884448232e-03 6.94864444303e-03 5.35605462201e-03 4.12908469965e-03 3.08059036838e-03 2.24520792586e-03 + 1.69338053144e-03 1.67402419594e-03 2.72387743950e-03 5.34938255282e-03 9.73197422258e-03 1.57482662916e-02 + 2.22733360617e-02 2.50772100061e-02 2.38696177933e-02 1.98512609062e-02 1.55658072841e-02 1.24397599681e-02 + 1.04621312250e-02 8.98354824242e-03 7.39824492456e-03 5.58710246513e-03 3.83244310376e-03 2.41157316343e-03 + 1.60209779006e-03 1.90743041982e-03 3.75305587276e-03 7.07078700762e-03 1.15872098355e-02 1.70152717119e-02 + 1.74081333866e-02 1.79919939971e-02 1.73159684780e-02 1.67904453380e-02 1.65995488173e-02 1.61498443086e-02 + 1.50774940382e-02 1.31649357363e-02 1.04037780826e-02 7.27092953346e-03 4.42686521645e-03 2.37509533345e-03 + 1.63099520693e-03 2.59973870085e-03 5.00583457705e-03 8.11200476869e-03 1.15059727233e-02 1.49196028102e-02 + 1.07295460873e-02 1.24625153675e-02 1.68366065611e-02 2.19600543477e-02 2.48114276638e-02 2.46764318358e-02 + 2.23867610797e-02 1.85235187840e-02 1.36011760276e-02 8.59641394379e-03 4.53920003265e-03 2.17120820882e-03 + 1.94038812124e-03 3.57365634649e-03 5.95087349751e-03 8.04401475090e-03 9.52577262967e-03 1.03139381155e-02 + 8.23667869751e-03 1.64179531532e-02 2.78508768694e-02 3.59578413577e-02 3.78083291128e-02 3.52595279024e-02 + 3.03586733937e-02 2.36908828102e-02 1.60128922291e-02 8.96688701937e-03 4.07353069584e-03 2.03419465779e-03 + 2.62760335674e-03 4.63824175825e-03 6.45731280519e-03 7.11596403491e-03 6.56898066031e-03 5.91338512685e-03 + 2.72722481830e-02 4.34668231220e-02 5.31748914086e-02 5.44393345828e-02 5.06578372332e-02 4.40464058654e-02 + 3.42836001592e-02 2.20426386902e-02 1.06829910987e-02 3.52424984961e-03 1.23859762156e-03 2.34128502623e-03 + 4.73121032347e-03 6.57978207810e-03 6.82944348182e-03 5.84584539982e-03 6.34700822132e-03 1.29314631167e-02 + 4.15954701008e-02 5.60724757660e-02 6.17269847305e-02 6.09162469885e-02 5.69077293831e-02 4.89869342500e-02 + 3.55585458380e-02 1.95772416982e-02 7.28234485511e-03 1.90979093676e-03 1.87161560762e-03 3.93778134856e-03 + 5.73768877506e-03 6.14755206676e-03 5.29833131530e-03 5.22857067502e-03 1.01492706067e-02 2.33513874823e-02 + 4.98142962192e-02 5.99252386088e-02 6.34622789743e-02 6.36043574114e-02 6.01271586924e-02 4.92792252056e-02 + 3.11487264070e-02 1.33634738831e-02 3.48411694273e-03 1.68955877540e-03 3.50791382954e-03 5.27608797853e-03 + 5.63466402025e-03 4.82159472775e-03 4.38208350601e-03 7.37491474340e-03 1.71584965893e-02 3.33852838467e-02 + 4.97120711380e-02 5.70103054536e-02 6.10118086351e-02 6.22221613844e-02 5.65968175336e-02 4.08199278518e-02 + 2.04257211322e-02 6.04092938697e-03 1.63192829008e-03 2.98719130185e-03 4.98003637287e-03 5.43090779252e-03 + 4.49584573428e-03 3.58719844302e-03 4.92985892584e-03 1.10129984344e-02 2.28155955961e-02 3.74880633094e-02 + 4.43938919905e-02 5.04002608576e-02 5.41769807096e-02 5.30600633612e-02 4.25940433522e-02 2.45337800396e-02 + 8.65750512201e-03 1.97157068729e-03 2.34333270278e-03 4.43357771168e-03 5.29899981766e-03 4.53650690795e-03 + 3.21306117910e-03 3.19029724111e-03 6.34043559892e-03 1.36965305547e-02 2.44538533480e-02 3.56495164596e-02 + 3.63485162566e-02 4.03415925415e-02 4.11312748584e-02 3.55369420499e-02 2.29767385509e-02 9.72854112582e-03 + 2.88152116968e-03 2.40701356864e-03 4.05694581568e-03 5.08984531448e-03 4.76372236675e-03 3.43018435406e-03 + 2.43739033129e-03 3.46859587370e-03 7.46717148987e-03 1.42659845790e-02 2.25701423617e-02 3.03647237937e-02 + 2.64700621827e-02 2.74267962895e-02 2.48757110021e-02 1.80426385088e-02 9.82633019429e-03 5.00204115280e-03 + 4.39004462314e-03 5.21503402550e-03 5.62594816085e-03 5.25493420669e-03 4.05477566763e-03 2.58364543135e-03 + 2.17812225384e-03 3.87600597086e-03 7.68148601254e-03 1.29360769606e-02 1.85678296685e-02 2.33331372196e-02 + 1.66492506513e-02 1.58540036377e-02 1.34948599578e-02 1.07196093803e-02 9.43308364403e-03 9.64386822793e-03 + 9.52368447303e-03 8.41038292152e-03 6.88856330758e-03 5.18183265197e-03 3.33357122257e-03 2.02874622976e-03 + 2.19864700292e-03 3.99766887640e-03 6.92328458737e-03 1.03817420486e-02 1.36238973995e-02 1.58509882718e-02 + 1.00195546197e-02 1.04252787797e-02 1.18079590319e-02 1.44881622369e-02 1.70345982062e-02 1.70934194436e-02 + 1.43808496722e-02 1.07170198638e-02 7.40951822277e-03 4.64964731127e-03 2.59751788538e-03 1.75470950419e-03 + 2.26151057315e-03 3.66766768647e-03 5.51413327139e-03 7.44284927163e-03 8.96640481890e-03 9.75329220406e-03 + 8.40036643925e-03 1.18561901378e-02 1.71098604395e-02 2.23327762518e-02 2.43374867946e-02 2.17382435529e-02 + 1.64327790920e-02 1.10426033413e-02 6.74188479040e-03 3.72463084556e-03 2.08267715007e-03 1.72804115210e-03 + 2.18758117978e-03 3.00773890768e-03 3.99074743440e-03 4.95979936833e-03 5.76467203230e-03 6.65803179471e-03 + 1.11805028747e-02 1.72503765026e-02 2.38025326536e-02 2.77893726296e-02 2.68348135407e-02 2.16749201932e-02 + 1.51450264833e-02 9.46096477031e-03 5.40662569422e-03 3.01561429668e-03 1.99073557330e-03 1.79380872597e-03 + 1.95486623357e-03 2.29467342306e-03 2.79025245899e-03 3.49198635306e-03 4.69642463898e-03 7.03006325686e-03 + 1.64249327908e-02 2.32645954925e-02 2.82465223372e-02 2.88202439794e-02 2.47675467596e-02 1.83670889950e-02 + 1.21799423261e-02 7.51249480973e-03 4.55889987468e-03 2.96700154550e-03 2.19823758046e-03 1.82369446215e-03 + 1.66377346772e-03 1.71872243816e-03 2.15004567824e-03 3.35686157381e-03 5.90464236518e-03 1.02638398838e-02 + 2.17359931543e-02 2.73625680289e-02 2.91911060418e-02 2.63874563497e-02 2.06926199474e-02 1.46909844089e-02 + 9.95569384365e-03 6.78732451097e-03 4.79709681227e-03 3.47315147059e-03 2.50865697138e-03 1.81058323441e-03 + 1.38375122545e-03 1.39734891429e-03 2.30604979735e-03 4.68475755238e-03 8.89665429016e-03 1.48734476137e-02 + 2.45053355538e-02 2.76825724990e-02 2.66631767890e-02 2.24261840306e-02 1.73121434822e-02 1.30229153101e-02 + 9.99221431288e-03 7.83304956635e-03 6.00735104534e-03 4.31031844186e-03 2.84710825338e-03 1.74035647969e-03 + 1.15698756606e-03 1.53193315382e-03 3.40907963127e-03 7.03110870573e-03 1.22705163068e-02 1.85854747536e-02 + 2.28685011784e-02 2.37903481158e-02 2.21971196951e-02 1.95511449403e-02 1.69597300764e-02 1.47440564848e-02 + 1.27240007478e-02 1.05011061224e-02 7.92534368083e-03 5.31271481006e-03 3.08615976887e-03 1.54800257695e-03 + 1.11500084524e-03 2.28225905954e-03 5.12704084111e-03 9.25075037228e-03 1.41811772857e-02 1.92036916371e-02 + 1.75383665162e-02 1.85252769240e-02 1.99553605358e-02 2.13240626046e-02 2.15233253190e-02 2.02878131896e-02 + 1.78244090696e-02 1.42883388826e-02 1.01202756501e-02 6.11622965752e-03 2.98316296082e-03 1.26214128753e-03 + 1.43221140143e-03 3.49750449437e-03 6.72707535429e-03 1.02534942708e-02 1.35464682967e-02 1.60957937931e-02 + 1.29982235185e-02 1.81853247318e-02 2.52029640500e-02 3.00593478391e-02 3.08658784798e-02 2.85022993310e-02 + 2.40900705767e-02 1.82255746081e-02 1.18250673616e-02 6.20325057178e-03 2.42843959467e-03 1.09112861897e-03 + 2.18271203134e-03 4.82811446253e-03 7.66622174675e-03 9.72218718415e-03 1.07065547423e-02 1.11489047543e-02 + 1.55789644742e-02 2.76260025845e-02 3.85516952838e-02 4.31236511903e-02 4.17381382555e-02 3.69717996217e-02 + 2.99567002214e-02 2.11726529864e-02 1.22118470478e-02 5.26985830826e-03 1.65166521544e-03 1.35390489821e-03 + 3.34477322687e-03 5.98082930978e-03 7.74155229830e-03 7.95533873629e-03 7.32919863820e-03 8.65049702618e-03 + 3.39308828874e-02 4.55831356657e-02 5.05982033757e-02 4.98089618820e-02 4.53514482296e-02 3.73721534582e-02 + 2.57920286291e-02 1.32913156694e-02 4.13433206313e-03 3.47319301413e-04 7.43249854746e-04 2.95585244150e-03 + 5.11664328434e-03 6.19594059399e-03 6.11540705414e-03 6.34274657797e-03 1.00835288204e-02 1.99682986084e-02 + 4.35315939015e-02 5.24242725980e-02 5.51928867192e-02 5.39371788296e-02 4.88746918718e-02 3.81429492974e-02 + 2.27666510924e-02 8.68874736399e-03 1.25149221931e-03 2.98033730779e-04 2.28075147607e-03 4.27154408455e-03 + 5.12747780191e-03 4.97707215225e-03 5.02199741600e-03 7.74450043482e-03 1.59380382530e-02 2.95012822598e-02 + 4.66887080052e-02 5.29275213092e-02 5.53428429951e-02 5.44884039789e-02 4.76185455919e-02 3.28589191765e-02 + 1.52327971034e-02 3.32235665029e-03 5.24554846758e-05 1.68421673535e-03 3.80343130798e-03 4.56045409091e-03 + 4.14222301006e-03 3.80383288393e-03 5.47058566204e-03 1.12987283863e-02 2.21601978653e-02 3.55673538143e-02 + 4.39114109595e-02 4.88116531839e-02 5.11379492779e-02 4.87232326401e-02 3.79700170060e-02 2.08466236859e-02 + 6.22374094533e-03 2.71032104961e-04 9.00542801343e-04 3.11263645378e-03 4.16546720478e-03 3.75918937455e-03 + 2.95494708242e-03 3.48853175294e-03 7.05307790632e-03 1.45500612543e-02 2.51426296199e-02 3.58968223531e-02 + 3.72960895207e-02 4.06078202690e-02 4.06248070653e-02 3.42626144979e-02 2.11392469484e-02 7.65358250294e-03 + 7.70313960778e-04 3.42126726994e-04 2.14547093757e-03 3.45151484506e-03 3.51432154193e-03 2.68715452771e-03 + 2.28405374141e-03 3.92476500583e-03 8.48224403883e-03 1.57105943632e-02 2.41942033352e-02 3.18105783515e-02 + 2.78843436888e-02 2.82271344014e-02 2.45054908338e-02 1.59303528455e-02 6.15373979888e-03 7.28057460423e-04 + 3.38262588986e-04 1.70561976472e-03 2.78940928358e-03 3.17815183803e-03 2.74040374478e-03 1.95888743112e-03 + 2.18323300539e-03 4.49789945965e-03 8.89023782351e-03 1.46056791298e-02 2.04354684102e-02 2.50888068473e-02 + 1.71132925710e-02 1.47466620909e-02 9.79479014948e-03 4.18285195863e-03 1.29491998898e-03 1.68388083124e-03 + 2.82073651887e-03 3.29600524345e-03 3.34462832131e-03 3.01120875240e-03 2.20596717065e-03 1.61993293523e-03 + 2.36183304422e-03 4.70246449772e-03 8.10421179778e-03 1.18704946684e-02 1.51463699005e-02 1.70732484847e-02 + 8.23244112018e-03 5.99751484917e-03 4.12857025945e-03 4.22535810983e-03 5.95644065976e-03 6.96973039511e-03 + 6.33953314438e-03 5.13343337952e-03 4.02784526343e-03 2.88594540726e-03 1.81682563508e-03 1.55435435851e-03 + 2.51578979079e-03 4.31128370783e-03 6.41987234694e-03 8.39342307287e-03 9.61683530059e-03 9.58311024050e-03 + 4.35488425190e-03 5.12904440304e-03 7.91019113971e-03 1.17237924350e-02 1.37215823066e-02 1.24461211498e-02 + 9.43385303103e-03 6.55653902306e-03 4.28820928916e-03 2.54921637775e-03 1.58568426930e-03 1.64684837980e-03 + 2.41735268419e-03 3.42784594274e-03 4.43434860920e-03 5.15096734378e-03 5.23872615740e-03 4.75445668359e-03 + 5.89467509619e-03 1.00873820384e-02 1.54780245312e-02 1.92601906663e-02 1.89732054806e-02 1.52414150967e-02 + 1.05902246652e-02 6.67500715871e-03 3.87489087194e-03 2.21059646406e-03 1.61827835312e-03 1.71374771634e-03 + 2.03754005425e-03 2.40665582289e-03 2.74409569560e-03 2.92735476407e-03 3.06636613107e-03 3.75495115169e-03 + 1.08687236212e-02 1.66726223282e-02 2.14924777428e-02 2.27242745402e-02 1.98108516261e-02 1.46903474480e-02 + 9.64655393206e-03 5.82090932574e-03 3.43035159173e-03 2.25275676594e-03 1.80441517384e-03 1.62624244123e-03 + 1.54442861462e-03 1.55866540302e-03 1.70231784873e-03 2.17587560707e-03 3.48229206078e-03 6.26071835246e-03 + 1.66947854513e-02 2.18013463468e-02 2.39717358059e-02 2.21934803426e-02 1.76515483444e-02 1.24930277226e-02 + 8.22389757118e-03 5.33254237790e-03 3.62908157624e-03 2.63292889590e-03 1.94458134793e-03 1.42656874454e-03 + 1.09292404824e-03 1.03561419682e-03 1.53740819967e-03 3.09397888364e-03 6.17639093760e-03 1.09128657716e-02 + 2.10947497620e-02 2.40361871701e-02 2.33784814709e-02 1.98146423034e-02 1.52032213707e-02 1.10844091399e-02 + 8.06210872946e-03 5.99086128303e-03 4.43381701837e-03 3.10864893187e-03 2.00288867622e-03 1.18337847560e-03 + 7.48971997560e-04 1.00120444120e-03 2.43922061564e-03 5.45836331560e-03 1.00851297161e-02 1.57708942963e-02 + 2.23342829325e-02 2.30947008364e-02 2.11645871444e-02 1.79071095272e-02 1.46198547321e-02 1.19113368611e-02 + 9.72746641966e-03 7.68550024826e-03 5.58453232504e-03 3.59716593187e-03 1.97579928150e-03 8.87185224742e-04 + 6.23112115182e-04 1.65015148282e-03 4.26543600930e-03 8.37257756526e-03 1.35152385255e-02 1.87045818073e-02 + 2.00120351505e-02 2.01920280654e-02 1.95617825137e-02 1.85298003007e-02 1.71239996142e-02 1.53323916781e-02 + 1.30225596761e-02 1.01068610351e-02 6.90187396912e-03 3.96977989631e-03 1.73790747675e-03 5.66446524933e-04 + 8.97493732185e-04 2.93642968917e-03 6.36205338790e-03 1.05621413436e-02 1.48366229936e-02 1.82604499807e-02 + 1.63251811780e-02 1.87759563846e-02 2.16968837196e-02 2.34071241800e-02 2.30888204086e-02 2.09616736112e-02 + 1.73749056741e-02 1.27713063183e-02 7.97208766645e-03 3.88838607316e-03 1.19937806761e-03 3.99290094162e-04 + 1.65632380316e-03 4.48421973173e-03 7.92485914101e-03 1.10984410235e-02 1.34424293583e-02 1.48944716002e-02 + 1.59374842807e-02 2.30987773374e-02 2.96017331751e-02 3.23866727820e-02 3.13064247316e-02 2.74959653809e-02 + 2.17354248714e-02 1.48260038723e-02 8.12815961338e-03 3.07278615546e-03 5.30156398664e-04 6.46239654878e-04 + 2.81427180213e-03 5.82930224637e-03 8.42139808536e-03 9.84739595901e-03 1.03514048510e-02 1.16233144209e-02 + 2.24881357227e-02 3.37738380002e-02 4.10291813363e-02 4.23379875376e-02 3.92819221059e-02 3.33229566351e-02 + 2.49316943509e-02 1.52897901130e-02 6.82941628110e-03 1.64360296672e-03 1.86536759214e-04 1.52178244328e-03 + 4.13592420937e-03 6.52902239704e-03 7.67480256126e-03 7.63129143663e-03 8.24507916408e-03 1.27524209614e-02 + 3.37303660845e-02 4.06689624249e-02 4.28382906763e-02 4.10911580734e-02 3.58724769638e-02 2.69665913859e-02 + 1.58375932365e-02 6.27205740708e-03 1.30766341481e-03 6.96164446409e-04 2.13931416362e-03 3.69367704551e-03 + 4.56889090739e-03 4.80872524351e-03 5.20488875936e-03 7.48292963979e-03 1.35567163488e-02 2.33759348885e-02 + 3.86602952038e-02 4.37568680892e-02 4.49487361321e-02 4.26344668348e-02 3.57355569647e-02 2.40586550021e-02 + 1.14227149340e-02 3.24210856840e-03 1.06250188660e-03 2.18992050930e-03 3.57661401132e-03 3.98464241674e-03 + 3.65066587786e-03 3.57638747633e-03 5.20439767590e-03 1.00819147147e-02 1.87924799589e-02 2.95289990335e-02 + 3.87309473488e-02 4.24258542513e-02 4.31297933708e-02 3.96532893831e-02 3.02523641993e-02 1.70450733258e-02 + 6.33849472118e-03 2.06550362073e-03 2.39154279307e-03 3.64151687998e-03 3.92156995864e-03 3.18969659598e-03 + 2.43908895103e-03 3.12369919012e-03 6.52728936195e-03 1.32237122943e-02 2.24539010523e-02 3.18166462157e-02 + 3.45755183610e-02 3.69211591678e-02 3.61050475979e-02 3.01659040951e-02 1.94006005208e-02 8.91172443644e-03 + 3.59189063488e-03 2.86166119385e-03 3.49978193477e-03 3.68744967045e-03 3.05447164185e-03 2.00668321031e-03 + 1.72228471635e-03 3.55545628560e-03 8.10156427257e-03 1.49811711951e-02 2.28716826444e-02 2.98316330698e-02 + 2.70251097164e-02 2.70542358337e-02 2.34639628284e-02 1.59844002237e-02 8.03714442319e-03 3.89265925535e-03 + 3.27982126572e-03 3.36164637343e-03 3.12077019187e-03 2.68071175302e-03 1.92388245811e-03 1.19400230125e-03 + 1.68171160755e-03 4.29547048480e-03 8.87277628638e-03 1.45841681488e-02 2.02338127453e-02 2.45916971488e-02 + 1.71295020739e-02 1.44974068939e-02 9.38787764618e-03 4.09502584793e-03 1.93303651824e-03 2.63416654306e-03 + 3.17586004028e-03 2.71091742623e-03 2.20102631237e-03 1.80419742580e-03 1.19794858178e-03 9.14826170273e-04 + 2.01913986446e-03 4.72437281140e-03 8.39895157214e-03 1.22770522739e-02 1.55065977141e-02 1.72919749310e-02 + 7.49080881899e-03 4.08789756474e-03 9.13232556247e-04 2.84518245615e-04 2.05937433404e-03 3.36570944570e-03 + 3.02162736704e-03 2.28008982241e-03 1.85846824240e-03 1.41069509823e-03 8.65770891562e-04 1.00337413251e-03 + 2.34841497574e-03 4.48427268608e-03 6.80451633612e-03 8.81791494383e-03 9.90814450566e-03 9.53588608868e-03 + 1.60005516474e-03 3.22055282442e-04 1.13603496812e-03 3.81070701971e-03 5.72430773218e-03 5.30885038533e-03 + 3.85679265526e-03 2.74512870861e-03 1.96542592229e-03 1.20663017904e-03 7.98933790285e-04 1.25422730520e-03 + 2.36051563831e-03 3.58817909818e-03 4.64305946318e-03 5.20624969208e-03 4.86231128703e-03 3.50540895079e-03 + 1.16585358860e-03 3.22239364312e-03 6.90216902161e-03 9.86286780471e-03 9.88356013607e-03 7.63029053419e-03 + 5.14780304137e-03 3.29258086122e-03 1.93607715716e-03 1.08360937452e-03 9.71098702990e-04 1.42322247591e-03 + 1.97247715226e-03 2.40153648663e-03 2.60921544521e-03 2.38362150228e-03 1.68575011966e-03 9.73881682342e-04 + 4.95570194232e-03 9.13888839388e-03 1.30273955958e-02 1.42184159031e-02 1.22065612981e-02 8.74221381443e-03 + 5.55456421805e-03 3.22590677378e-03 1.80127419540e-03 1.23808677516e-03 1.23464534048e-03 1.34340771384e-03 + 1.37867686808e-03 1.34679880494e-03 1.20401958873e-03 9.92545176017e-04 1.10523586617e-03 2.22181155793e-03 + 1.02991704657e-02 1.44094989406e-02 1.64532767151e-02 1.53613633034e-02 1.20247388394e-02 8.20955473031e-03 + 5.09874270286e-03 3.07974949562e-03 2.06071218869e-03 1.63578839360e-03 1.36054596262e-03 1.07300763493e-03 + 8.21710947915e-04 6.58699033636e-04 7.08214101472e-04 1.33044815243e-03 3.01695152919e-03 6.08595376850e-03 + 1.48992289223e-02 1.73231001785e-02 1.69910543010e-02 1.43166480355e-02 1.07133343326e-02 7.43967344950e-03 + 5.08682086338e-03 3.64722376822e-03 2.74379105506e-03 1.99857714444e-03 1.30863653152e-03 7.51414939716e-04 + 4.17339558943e-04 4.79398661355e-04 1.29751485575e-03 3.26991489274e-03 6.54535731191e-03 1.07771266625e-02 + 1.74055472478e-02 1.78574104839e-02 1.60734722935e-02 1.31844664786e-02 1.02999823829e-02 7.99375955143e-03 + 6.29463379925e-03 4.89388125299e-03 3.52929970374e-03 2.22727750806e-03 1.15217808422e-03 4.32399294697e-04 + 2.57510614302e-04 9.73243170388e-04 2.90929508323e-03 6.13658900693e-03 1.03190630155e-02 1.45419554774e-02 + 1.74134452921e-02 1.69571851210e-02 1.54529365559e-02 1.36492459141e-02 1.19159575500e-02 1.02852186120e-02 + 8.52928713443e-03 6.47271271205e-03 4.28899935059e-03 2.33757184197e-03 8.83090682262e-04 1.61246503801e-04 + 5.04570021629e-04 2.15745002282e-03 5.06069865237e-03 8.86393793239e-03 1.28826276515e-02 1.60470442306e-02 + 1.58266712943e-02 1.63756665309e-02 1.67632054261e-02 1.66323621730e-02 1.57482078299e-02 1.40196066690e-02 + 1.14051860006e-02 8.17251954210e-03 4.91840237924e-03 2.21526821751e-03 4.81916297652e-04 9.50474321937e-05 + 1.25194606782e-03 3.73282304017e-03 7.00161888220e-03 1.04091217005e-02 1.32422997628e-02 1.49870386351e-02 + 1.51533131558e-02 1.84815654956e-02 2.13195987279e-02 2.23613431339e-02 2.13687577410e-02 1.85930482646e-02 + 1.44295301217e-02 9.60351050570e-03 5.08117378354e-03 1.71977900946e-03 1.02367516823e-04 4.33487416405e-04 + 2.38601764660e-03 5.20603565117e-03 8.03844132991e-03 1.02089083307e-02 1.15610449936e-02 1.28325672094e-02 + 1.82587240965e-02 2.46939353738e-02 2.89381581395e-02 2.97482575652e-02 2.75373289068e-02 2.29958543110e-02 + 1.68016640060e-02 1.00961563359e-02 4.40723913099e-03 9.45088328829e-04 6.69040853761e-05 1.28110704268e-03 + 3.62596824367e-03 6.06677282564e-03 7.78294960411e-03 8.61185245009e-03 9.58066075320e-03 1.25605727330e-02 + 2.55461385455e-02 3.33976264247e-02 3.71068795027e-02 3.65509264665e-02 3.27170653578e-02 2.61455487013e-02 + 1.75781055385e-02 9.00501505462e-03 2.88620076135e-03 3.53907444542e-04 7.26472508474e-04 2.53559827936e-03 + 4.52850927544e-03 5.91116334916e-03 6.47211644949e-03 7.09866363130e-03 9.86996240478e-03 1.64330009926e-02 + 2.87937546547e-02 3.26082580779e-02 3.32348371955e-02 3.09577264520e-02 2.58539030773e-02 1.85193059066e-02 + 1.11263059393e-02 6.26490292410e-03 4.55137004579e-03 4.50418095073e-03 4.51334852122e-03 4.02880995416e-03 + 3.35380602632e-03 3.20129385330e-03 4.52133776956e-03 8.23856898021e-03 1.45406277021e-02 2.21662438327e-02 + 3.07565377171e-02 3.35133046416e-02 3.35114052509e-02 3.05764705683e-02 2.45146356444e-02 1.68746347518e-02 + 1.08028025958e-02 7.93960351472e-03 7.07866456020e-03 6.33912583747e-03 4.97267402393e-03 3.27140703389e-03 + 2.09415171796e-03 2.50489630857e-03 5.30055211307e-03 1.06583375627e-02 1.78710839377e-02 2.52247273419e-02 + 2.92188717562e-02 3.09758301299e-02 3.03119103356e-02 2.67324205785e-02 2.09284085051e-02 1.55776308475e-02 + 1.25371208677e-02 1.08261944086e-02 8.89868007116e-03 6.50676564406e-03 4.01132373924e-03 1.93103667719e-03 + 1.18899216027e-03 2.70235115983e-03 6.67093885126e-03 1.25581054799e-02 1.92531637205e-02 2.52090750816e-02 + 2.44412380101e-02 2.49340970741e-02 2.33405846774e-02 2.00937992929e-02 1.72582701927e-02 1.61224665978e-02 + 1.49073045652e-02 1.19188485754e-02 8.12698013879e-03 4.88583272697e-03 2.39469075941e-03 8.31956680508e-04 + 1.00716373757e-03 3.42517352397e-03 7.68226949962e-03 1.29197736646e-02 1.80873920150e-02 2.21139454594e-02 + 1.71447894888e-02 1.61922775693e-02 1.43928059315e-02 1.36663321615e-02 1.52138728227e-02 1.64397060008e-02 + 1.41658651127e-02 9.53354409192e-03 5.49363233311e-03 2.85871508179e-03 1.09334977422e-03 3.56844406542e-04 + 1.35478793092e-03 4.04814397514e-03 7.68679429503e-03 1.14873703541e-02 1.46844116271e-02 1.66473756751e-02 + 9.02561259350e-03 7.53963471583e-03 7.33876837864e-03 9.98904777686e-03 1.34954731478e-02 1.36051270833e-02 + 9.91198043096e-03 5.75051322792e-03 3.06675046297e-03 1.44700485940e-03 3.97464784749e-04 4.04266744282e-04 + 1.81253797573e-03 4.08718326208e-03 6.53576731254e-03 8.65509332305e-03 9.93808702708e-03 1.00445711212e-02 + 2.77339544790e-03 2.50008926185e-03 4.78407893613e-03 8.80398075335e-03 1.08349893343e-02 9.01088446511e-03 + 5.63234353441e-03 3.13681377747e-03 1.69364497878e-03 6.73546708496e-04 1.88841719030e-04 7.12336445626e-04 + 1.99206802661e-03 3.40503825591e-03 4.59267338284e-03 5.26233619714e-03 5.09631622942e-03 4.08332572445e-03 + 5.04008819064e-04 2.36946523781e-03 6.02207645825e-03 8.87992998712e-03 8.53177876066e-03 5.95459367556e-03 + 3.53320350817e-03 2.02424836664e-03 9.94296330020e-04 3.14021555844e-04 3.33193974560e-04 9.77814004437e-04 + 1.72507621331e-03 2.27745422092e-03 2.51785823237e-03 2.25805716040e-03 1.46772911419e-03 5.73637201847e-04 + 2.14785317499e-03 5.42818164611e-03 8.61968678385e-03 9.41239204093e-03 7.54188900158e-03 4.89198767693e-03 + 2.84830259776e-03 1.47793891364e-03 6.08384075050e-04 3.53757920201e-04 6.23496814010e-04 9.79904931661e-04 + 1.16484064383e-03 1.15841058185e-03 9.06307741555e-04 4.13571540612e-04 1.54687886896e-05 3.60084369379e-04 + 5.79198514687e-03 8.93980887316e-03 1.05004944969e-02 9.51735448533e-03 6.93722356059e-03 4.30911542633e-03 + 2.35592139656e-03 1.16007202508e-03 6.82408654843e-04 6.97075378309e-04 7.90875887867e-04 7.45828949222e-04 + 5.94211106153e-04 3.73717961329e-04 1.42829753565e-04 1.79742669398e-04 9.41427617586e-04 2.81466185279e-03 + 9.33285526360e-03 1.11303438032e-02 1.08360341113e-02 8.74235576362e-03 6.04646411377e-03 3.73758586507e-03 + 2.21415125373e-03 1.47204481358e-03 1.21289068791e-03 1.03316903152e-03 7.54192083429e-04 4.41629945476e-04 + 1.88976709203e-04 1.01993536089e-04 4.34255934307e-04 1.52755807835e-03 3.56762951719e-03 6.40076883760e-03 + 1.15603067952e-02 1.17003922826e-02 1.01594109423e-02 7.83936822988e-03 5.63840805931e-03 4.01777926046e-03 + 3.02710184183e-03 2.40883205838e-03 1.83530292005e-03 1.19815270712e-03 6.00510371959e-04 1.71561466892e-04 + 5.04465908242e-05 4.76296363279e-04 1.71935199783e-03 3.87640275886e-03 6.72867264593e-03 9.63072703780e-03 + 1.22366932586e-02 1.13981120344e-02 9.75891775141e-03 8.07017991586e-03 6.68771215268e-03 5.61843098956e-03 + 4.64447286191e-03 3.54699712223e-03 2.34260406914e-03 1.22807567701e-03 3.91196832529e-04 8.78376635615e-06 + 3.13240416492e-04 1.49842913576e-03 3.58124038452e-03 6.36070507257e-03 9.31247235432e-03 1.15388363679e-02 + 1.19169146861e-02 1.14025226048e-02 1.07548883058e-02 1.00930193908e-02 9.31394535816e-03 8.20902985918e-03 + 6.61929417576e-03 4.67349658690e-03 2.73580154836e-03 1.14764509804e-03 1.81038868576e-04 1.03563810318e-04 + 1.05342603612e-03 2.91530415150e-03 5.42294384115e-03 8.17927041325e-03 1.05296360366e-02 1.18040369418e-02 + 1.18782215965e-02 1.29046672382e-02 1.37284137262e-02 1.38895325686e-02 1.31308659421e-02 1.13427026756e-02 + 8.69591746390e-03 5.70407004499e-03 2.96795288208e-03 9.85696214277e-04 1.48063203650e-04 6.09033159640e-04 + 2.14126186376e-03 4.31105507767e-03 6.67936448121e-03 8.76639538747e-03 1.01823476975e-02 1.10392564905e-02 + 1.36919522426e-02 1.67109705640e-02 1.86930549009e-02 1.90013888745e-02 1.75498748901e-02 1.45554762332e-02 + 1.05695551699e-02 6.41957522015e-03 2.97689237455e-03 9.30466693471e-04 5.44997511112e-04 1.52943895242e-03 + 3.28495032763e-03 5.24040232287e-03 6.91589404869e-03 8.07079012023e-03 9.09194844199e-03 1.08606109265e-02 + 1.80785606705e-02 2.25134394193e-02 2.47293206267e-02 2.43629275882e-02 2.16918731164e-02 1.72235804655e-02 + 1.17779343789e-02 6.57764510704e-03 2.88479296031e-03 1.31584832436e-03 1.53239392587e-03 2.69805837661e-03 + 4.10657822928e-03 5.30436127846e-03 6.09749953170e-03 6.92766829744e-03 8.93530437563e-03 1.29107289583e-02 + 2.39164393014e-02 2.85410031861e-02 3.00753219871e-02 2.86474692169e-02 2.46903684522e-02 1.87135920049e-02 + 1.19152503262e-02 6.24496162837e-03 3.15685511355e-03 2.50043069581e-03 3.04076043105e-03 3.73060002129e-03 + 4.18258830140e-03 4.43064873947e-03 4.93294557314e-03 6.74481171524e-03 1.09317651765e-02 1.72887774544e-02 + 2.21776244618e-02 2.43609109238e-02 2.46337602542e-02 2.33991009544e-02 2.10924591106e-02 1.83595925548e-02 + 1.59912340968e-02 1.41423855358e-02 1.21611248138e-02 9.55594996473e-03 6.56724608813e-03 3.83045776542e-03 + 2.05066383643e-03 1.90636487418e-03 3.82043510118e-03 7.68882304515e-03 1.28241631178e-02 1.80751252626e-02 + 2.27286825084e-02 2.45108219701e-02 2.50521675401e-02 2.48160452949e-02 2.42785363201e-02 2.37773247032e-02 + 2.27696591477e-02 2.01038424569e-02 1.56328341747e-02 1.04953196709e-02 5.86520329421e-03 2.47124942251e-03 + 9.58258575780e-04 1.78334205368e-03 4.80362941474e-03 9.36659991752e-03 1.45736724225e-02 1.93380979084e-02 + 2.09264645666e-02 2.26542847867e-02 2.42233248819e-02 2.63849792145e-02 2.93863270622e-02 3.15702751276e-02 + 2.99431368439e-02 2.38362906119e-02 1.59493659002e-02 9.04704556937e-03 4.03838119565e-03 1.08385182479e-03 + 5.08445936483e-04 2.30622988925e-03 5.82641576957e-03 1.02265640474e-02 1.46773588148e-02 1.83696998997e-02 + 1.72419950112e-02 1.93702432288e-02 2.29378150848e-02 2.88658755133e-02 3.54561681124e-02 3.76220958928e-02 + 3.21538652508e-02 2.21252692497e-02 1.27533539746e-02 6.18533000488e-03 2.10847527005e-03 2.50502212841e-04 + 6.88447505703e-04 2.97348703525e-03 6.24095295697e-03 9.75632014891e-03 1.29198200282e-02 1.53517699083e-02 + 1.26670328687e-02 1.57754418364e-02 2.20106417184e-02 3.09876847452e-02 3.76530404015e-02 3.59946020820e-02 + 2.67023074499e-02 1.60395288461e-02 8.23434550915e-03 3.43936902949e-03 7.66067413270e-04 5.56938704008e-05 + 1.13958727870e-03 3.26915931020e-03 5.69597367846e-03 7.95427601201e-03 9.73598570607e-03 1.10892262924e-02 + 8.58538753515e-03 1.30699141960e-02 2.10301801893e-02 2.94765489118e-02 3.21589816875e-02 2.66196583261e-02 + 1.73110855616e-02 9.46833854972e-03 4.47494785377e-03 1.51180884620e-03 1.17485925516e-04 2.64705463392e-04 + 1.44279692741e-03 2.92445233759e-03 4.30026186525e-03 5.35602702452e-03 6.04390499855e-03 6.77357888867e-03 + 6.21221520193e-03 1.15903704276e-02 1.87537936854e-02 2.33996673894e-02 2.18486414294e-02 1.57015853750e-02 + 9.30274525008e-03 4.81780918910e-03 2.01272281032e-03 4.11169406150e-04 1.45346689477e-05 5.40426364765e-04 + 1.35115583196e-03 2.06419124290e-03 2.55313328611e-03 2.75883988559e-03 2.89265674941e-03 3.65753963311e-03 + 5.81045661274e-03 1.07724476030e-02 1.51710461085e-02 1.58960220929e-02 1.26369157174e-02 8.10338605038e-03 + 4.49155509459e-03 2.09188673536e-03 6.02123788714e-04 1.71568981581e-05 1.93351194944e-04 6.16552865997e-04 + 9.32512609288e-04 1.07410956233e-03 1.02536386217e-03 9.00425711005e-04 1.15947681115e-03 2.55081428046e-03 + 6.61030809056e-03 1.00154162940e-02 1.15190503659e-02 1.00828526575e-02 6.96268514815e-03 4.01294066812e-03 + 1.92945341409e-03 6.34634372007e-04 7.49557216745e-05 1.15869866528e-04 3.47714247767e-04 4.67213032125e-04 + 4.44087829979e-04 3.10779410209e-04 1.57556583378e-04 3.01930479826e-04 1.22474433139e-03 3.32891895261e-03 + 7.56324657636e-03 9.06057351075e-03 8.56102924730e-03 6.40308044751e-03 3.88677181116e-03 1.92916408716e-03 + 7.34199558083e-04 2.36952784588e-04 2.29230350353e-04 3.46728443666e-04 3.48707456941e-04 2.40707878875e-04 + 9.47165954482e-05 8.66298811160e-06 2.25180573030e-04 1.06736406963e-03 2.70908004643e-03 5.05892800482e-03 + 8.01230588668e-03 7.87948841508e-03 6.35060151281e-03 4.25583941135e-03 2.44611231705e-03 1.28971641581e-03 + 7.77595749084e-04 6.72086093891e-04 6.36616665550e-04 4.85905924848e-04 2.59323735059e-04 6.29607107760e-05 + 1.47034460966e-05 3.18136086776e-04 1.20457158637e-03 2.73172307310e-03 4.71519336008e-03 6.72116812715e-03 + 7.82893033400e-03 6.75349806175e-03 5.11572410353e-03 3.62137728197e-03 2.60436330214e-03 2.05263492169e-03 + 1.75142502111e-03 1.43817040454e-03 1.00553289598e-03 5.34058175670e-04 1.59875148700e-04 2.95559536663e-05 + 3.30051661440e-04 1.21830377075e-03 2.68795166297e-03 4.54912117281e-03 6.42607938150e-03 7.71321376852e-03 + 7.35420874306e-03 6.31024080664e-03 5.34187252089e-03 4.68353276850e-03 4.24033652910e-03 3.77884526013e-03 + 3.10484318539e-03 2.22249483929e-03 1.30573275364e-03 5.48997161845e-04 1.42896046784e-04 2.90174446126e-04 + 1.08794709870e-03 2.43715196755e-03 4.14293478108e-03 5.94475977002e-03 7.37195209888e-03 7.87652673742e-03 + 7.31549810689e-03 7.19144011495e-03 7.22483291669e-03 7.20569907445e-03 6.84471147320e-03 5.93841684105e-03 + 4.55132099545e-03 2.99241829740e-03 1.59542159592e-03 6.45630444064e-04 4.05874389562e-04 9.67495275322e-04 + 2.13019149061e-03 3.60587479630e-03 5.18703656012e-03 6.58701990586e-03 7.40718596642e-03 7.52807874547e-03 + 8.52368668609e-03 9.68716461499e-03 1.05500997018e-02 1.07194457591e-02 9.94511355027e-03 8.25709689095e-03 + 6.03672737436e-03 3.80433127456e-03 2.02896183849e-03 1.09855993551e-03 1.16741735815e-03 1.99296264927e-03 + 3.16325404964e-03 4.41165035708e-03 5.54715377358e-03 6.36161325302e-03 6.88869890722e-03 7.51828850511e-03 + 1.13554899876e-02 1.35927824520e-02 1.48199325319e-02 1.46817476041e-02 1.31482219440e-02 1.05707033754e-02 + 7.56982998411e-03 4.82773842601e-03 2.95268230371e-03 2.24458532773e-03 2.47622612599e-03 3.13725758818e-03 + 3.88444092763e-03 4.56631812745e-03 5.09104781897e-03 5.63652917221e-03 6.75285721161e-03 8.79306676243e-03 + 1.53775558008e-02 1.81302665477e-02 1.91859496338e-02 1.84096380909e-02 1.60917454567e-02 1.28065448968e-02 + 9.31010448321e-03 6.45385772542e-03 4.81538026625e-03 4.26152025060e-03 4.16709969847e-03 4.09350771714e-03 + 3.98459337950e-03 3.91867568976e-03 4.15494850833e-03 5.30108207247e-03 7.87399641135e-03 1.15862806476e-02 + 1.94317156340e-02 2.20818635155e-02 2.26563665530e-02 2.13563447701e-02 1.86517906370e-02 1.51578150734e-02 + 1.17652072335e-02 9.33880158386e-03 7.97307934125e-03 6.98376929778e-03 5.79548164631e-03 4.43922172003e-03 + 3.28302239419e-03 2.78778815645e-03 3.54169835385e-03 6.05020451612e-03 1.02126726307e-02 1.51152257904e-02 + 1.64011126976e-02 1.84882583545e-02 2.03428832374e-02 2.26137034442e-02 2.53425652713e-02 2.76576163841e-02 + 2.80255298082e-02 2.52665766026e-02 1.97538675690e-02 1.32294873705e-02 7.39889996530e-03 3.18872546785e-03 + 1.02848052968e-03 1.06142924201e-03 2.99764851647e-03 6.19137543603e-03 9.93142094392e-03 1.35252362371e-02 + 1.71050024080e-02 2.01594390193e-02 2.43625406350e-02 3.02385457126e-02 3.67596850055e-02 4.09164602305e-02 + 3.94626842492e-02 3.21174469506e-02 2.20335329058e-02 1.27142008113e-02 5.81962773375e-03 1.69652924485e-03 + 3.18653543688e-04 1.29960599452e-03 3.86650604815e-03 7.25489032868e-03 1.08678875869e-02 1.41990018236e-02 + 1.70191620545e-02 2.20878382169e-02 3.00553383112e-02 4.07234206578e-02 5.05656363706e-02 5.35687292562e-02 + 4.66839742672e-02 3.34582388543e-02 2.00809582147e-02 1.00035691470e-02 3.64197696414e-03 5.56365203173e-04 + 2.39421205997e-04 1.86187931120e-03 4.50589860792e-03 7.55154181906e-03 1.06181590645e-02 1.35989425952e-02 + 1.67775857601e-02 2.46816890662e-02 3.68974709324e-02 5.11645950606e-02 6.04485110246e-02 5.79015845790e-02 + 4.45750971878e-02 2.82129287167e-02 1.50468991468e-02 6.49593626011e-03 1.76090853325e-03 4.73748069485e-05 + 5.55650299710e-04 2.29380230151e-03 4.51100114057e-03 6.85264854480e-03 9.26375018931e-03 1.21863148785e-02 + 1.67705384183e-02 2.73175373130e-02 4.19030851866e-02 5.52576183515e-02 5.89845081297e-02 4.99656353245e-02 + 3.41519872257e-02 1.95229541217e-02 9.42800466126e-03 3.46143238835e-03 5.98440331818e-04 4.94959529374e-05 + 8.85453276204e-04 2.26911630302e-03 3.79064869130e-03 5.38421120062e-03 7.36250015616e-03 1.06471651215e-02 + 1.68254863059e-02 2.82121300600e-02 4.10890728250e-02 4.88066641815e-02 4.59389074609e-02 3.45204013574e-02 + 2.14246683432e-02 1.13178691216e-02 4.90850753014e-03 1.41806335288e-03 1.29091146516e-04 2.44821321205e-04 + 9.41555096729e-04 1.77143405044e-03 2.63849306168e-03 3.71119219109e-03 5.61191209839e-03 9.50511823248e-03 + 1.62962435323e-02 2.57867413674e-02 3.36099747130e-02 3.49417224063e-02 2.89342803710e-02 1.96164012522e-02 + 1.12659486442e-02 5.43978619015e-03 1.96341818536e-03 3.79743652405e-04 8.60158144335e-05 3.42110503010e-04 + 7.01315795678e-04 1.06653935518e-03 1.51612354038e-03 2.41377294165e-03 4.54648190837e-03 8.93298483644e-03 + 1.46340746120e-02 2.04277817674e-02 2.30249188099e-02 2.07424619893e-02 1.52015414307e-02 9.35332781263e-03 + 4.84426432924e-03 1.93881467460e-03 4.73944523050e-04 7.29839629344e-05 1.40264506543e-04 2.62419780431e-04 + 3.55698994344e-04 4.74004706047e-04 8.24223779860e-04 1.90721275560e-03 4.37679699978e-03 8.69672053639e-03 + 1.19888677225e-02 1.41981896782e-02 1.35850444133e-02 1.05037883082e-02 6.69192602571e-03 3.52368848278e-03 + 1.41586251461e-03 3.33410664964e-04 3.27135341192e-05 7.47571568536e-05 1.23611292251e-04 1.16277593103e-04 + 1.09449296550e-04 2.41979193857e-04 8.39271441488e-04 2.30774159185e-03 4.85827635143e-03 8.34518992215e-03 + 9.07637997488e-03 8.91698588464e-03 7.07985938792e-03 4.51004547556e-03 2.26622083452e-03 8.10121075017e-04 + 1.33255785214e-04 4.17326189674e-06 7.65133595202e-05 1.06026091426e-04 6.75232411454e-05 3.16338226120e-05 + 1.14193222718e-04 5.45930062566e-04 1.58696200371e-03 3.28861930232e-03 5.44694057069e-03 7.63431340427e-03 + 6.52735301689e-03 5.25234272134e-03 3.39217942468e-03 1.72515158252e-03 6.76693455170e-04 2.50336877489e-04 + 2.12612255941e-04 2.65112899907e-04 2.33325162181e-04 1.31269215162e-04 5.21241211142e-05 1.15433599571e-04 + 5.02046458613e-04 1.37893319496e-03 2.71769953409e-03 4.28213342832e-03 5.74383454955e-03 6.65715445896e-03 + 4.65673770439e-03 3.27813225457e-03 2.06128496186e-03 1.33239567942e-03 1.05343366471e-03 9.92660663244e-04 + 9.08489315276e-04 7.00515720982e-04 4.27623561860e-04 2.07101572964e-04 1.78719995947e-04 5.11286829904e-04 + 1.30240452873e-03 2.45609727013e-03 3.74229032013e-03 4.91205807069e-03 5.64864823143e-03 5.58938422244e-03 + 3.73742709611e-03 3.00646009917e-03 2.66996352066e-03 2.61650545853e-03 2.57313079189e-03 2.31610160014e-03 + 1.81996827436e-03 1.23033402480e-03 7.20903924415e-04 4.61879943880e-04 6.38790321418e-04 1.31844741350e-03 + 2.32752380816e-03 3.40537683661e-03 4.37979344092e-03 5.06525722551e-03 5.18184967595e-03 4.62898825173e-03 + 4.09135850798e-03 4.32076679744e-03 4.69318213761e-03 4.88824805958e-03 4.63250589433e-03 3.89405052582e-03 + 2.90683262810e-03 1.96761318524e-03 1.31960102192e-03 1.17873621932e-03 1.61992903495e-03 2.42639950185e-03 + 3.27195671709e-03 3.99530399321e-03 4.52531429218e-03 4.71853046295e-03 4.52231340661e-03 4.19334489536e-03 + 5.80675403488e-03 6.86670755332e-03 7.59908542689e-03 7.67739028889e-03 6.98340574559e-03 5.74560674939e-03 + 4.38135307871e-03 3.26244387803e-03 2.66422462166e-03 2.68390631721e-03 3.10304948708e-03 3.56134205702e-03 + 3.88734840413e-03 4.07583486757e-03 4.10543194038e-03 4.04142491616e-03 4.18191385713e-03 4.79790996887e-03 + 8.55568262478e-03 1.01358195545e-02 1.09181163176e-02 1.07029669383e-02 9.64424641931e-03 8.18058876849e-03 + 6.77035356312e-03 5.75674994359e-03 5.27028413708e-03 5.09730887158e-03 4.86833767247e-03 4.45519573573e-03 + 3.97212824727e-03 3.52260495612e-03 3.26136956105e-03 3.56214081969e-03 4.72874420659e-03 6.57588716129e-03 + 1.17121965212e-02 1.35123136059e-02 1.42011612578e-02 1.38800009001e-02 1.29667670362e-02 1.18887752002e-02 + 1.09297081847e-02 1.01797300917e-02 9.40376250795e-03 8.21443236381e-03 6.56830029907e-03 4.83021365631e-03 + 3.37444398046e-03 2.47806463352e-03 2.51983828485e-03 3.82391308071e-03 6.22890694124e-03 9.09764148136e-03 + 1.45373471963e-02 1.63836445055e-02 1.72322694842e-02 1.75533783012e-02 1.77738614626e-02 1.79636961148e-02 + 1.78612988525e-02 1.69424757530e-02 1.47242721945e-02 1.13501204126e-02 7.62680673205e-03 4.40275040294e-03 + 2.22324210518e-03 1.48036960338e-03 2.41619379046e-03 4.85407916409e-03 8.18635015851e-03 1.16511277189e-02 + 1.32214190630e-02 1.70182371454e-02 2.23914346298e-02 2.94005341373e-02 3.65883134017e-02 4.10771061480e-02 + 4.01237907162e-02 3.34240994180e-02 2.36076522012e-02 1.40500332982e-02 6.75365834626e-03 2.25088494174e-03 + 3.76975540731e-04 6.13933628779e-04 2.23755063116e-03 4.62068987662e-03 7.35987361584e-03 1.02051495518e-02 + 1.58900192853e-02 2.26118578774e-02 3.23853665475e-02 4.41985743405e-02 5.42606917622e-02 5.73388367325e-02 + 5.09077709744e-02 3.78133073842e-02 2.36204801963e-02 1.22375985232e-02 4.80091803726e-03 1.00310586764e-03 + 5.90161514082e-05 9.76074566842e-04 2.89723662549e-03 5.35352266955e-03 8.16823506164e-03 1.14568566797e-02 + 1.94529153898e-02 3.02123813564e-02 4.50417270437e-02 6.07124954502e-02 7.02985119513e-02 6.77187149759e-02 + 5.38134679499e-02 3.56373795480e-02 1.98430799910e-02 8.97855478432e-03 2.80185055807e-03 2.73646131008e-04 + 1.82511456518e-04 1.40298487435e-03 3.25568500020e-03 5.54426372674e-03 8.44825874064e-03 1.26575872343e-02 + 2.38385853595e-02 3.85473717324e-02 5.67273677570e-02 7.21211274410e-02 7.62095092245e-02 6.58675039683e-02 + 4.69067314436e-02 2.80515191463e-02 1.41081429396e-02 5.58103947065e-03 1.34876862907e-03 6.68171422544e-05 + 4.47881107532e-04 1.58363970481e-03 3.13026027142e-03 5.22108077150e-03 8.49128311407e-03 1.41684304690e-02 + 2.79071785579e-02 4.44080417781e-02 6.15067717055e-02 7.12852197898e-02 6.75835874317e-02 5.24406381653e-02 + 3.39286153411e-02 1.86236154182e-02 8.51606269948e-03 2.93516121467e-03 5.81061062209e-04 1.37058960411e-04 + 5.76855370265e-04 1.41236350319e-03 2.64117424075e-03 4.71994442247e-03 8.66897657164e-03 1.59482397285e-02 + 2.97501907954e-02 4.42867611798e-02 5.56962132111e-02 5.77334216963e-02 4.90209039697e-02 3.45467227113e-02 + 2.06117704701e-02 1.04360100492e-02 4.30780636347e-03 1.32679053985e-03 3.03860804524e-04 2.03940394979e-04 + 4.81770679169e-04 1.03930098797e-03 2.12043260760e-03 4.42967840460e-03 9.12548254654e-03 1.74158054835e-02 + 2.78739321918e-02 3.73718412510e-02 4.17637009214e-02 3.84985092848e-02 2.94785728069e-02 1.90636229222e-02 + 1.04888661314e-02 4.82376281187e-03 1.80482157670e-03 5.78627424839e-04 2.09205377711e-04 1.49724976222e-04 + 2.81205260633e-04 7.22405338384e-04 1.88384671010e-03 4.56289081761e-03 9.66901580784e-03 1.76817819576e-02 + 2.25263155750e-02 2.65605864927e-02 2.60872336677e-02 2.13573141732e-02 1.47554791997e-02 8.66582672258e-03 + 4.27400333312e-03 1.75523476412e-03 6.60909699853e-04 2.78327360241e-04 1.12375150392e-04 4.58824074622e-05 + 1.57259240401e-04 6.82720759625e-04 2.11730881828e-03 5.06135836310e-03 9.83317762172e-03 1.61168112631e-02 + 1.56731157781e-02 1.59636332536e-02 1.35801298523e-02 9.69180807087e-03 5.83654794422e-03 2.93389216434e-03 + 1.22168131524e-03 4.78160140368e-04 2.33457307527e-04 1.13056900591e-04 2.03772103850e-05 1.92815120762e-05 + 2.67006752224e-04 1.07329143891e-03 2.80076766154e-03 5.58500163354e-03 9.19859563930e-03 1.29735543466e-02 + 9.46020891377e-03 8.05580519181e-03 5.65002898561e-03 3.22979937882e-03 1.46350232193e-03 5.04240902167e-04 + 1.60191027688e-04 9.58365167024e-05 6.21297433108e-05 1.22679167967e-05 1.34779380540e-05 1.86799142778e-04 + 7.40751669769e-04 1.88929227335e-03 3.63811143838e-03 5.74289898791e-03 7.81743942747e-03 9.28890115566e-03 + 4.97731915059e-03 3.30061405321e-03 1.67112059926e-03 5.70163115932e-04 8.16197162412e-05 3.96098189336e-06 + 5.49678012011e-05 6.18608218424e-05 2.46119654347e-05 3.03948039932e-05 1.96121257923e-04 6.78155849917e-04 + 1.59383231465e-03 2.86692715110e-03 4.24011738011e-03 5.43116391673e-03 6.14517968726e-03 6.04164917805e-03 + 2.38485472598e-03 1.23679420062e-03 5.47442865571e-04 3.25590526276e-04 3.59633271458e-04 4.06845410151e-04 + 3.52629900781e-04 2.39648399836e-04 1.77914475257e-04 2.95867929730e-04 7.36990216848e-04 1.56027870375e-03 + 2.61976364172e-03 3.64720743875e-03 4.42868549234e-03 4.79603189706e-03 4.55786993721e-03 3.65752002099e-03 + 1.52926161999e-03 1.20102150072e-03 1.23344748689e-03 1.38335314600e-03 1.40724023470e-03 1.22509785477e-03 + 9.42855596591e-04 7.24540094545e-04 7.17233913205e-04 1.05867932841e-03 1.78270642628e-03 2.69612498316e-03 + 3.50421561693e-03 4.03575878019e-03 4.21841145755e-03 3.95366015476e-03 3.22328624576e-03 2.28042275532e-03 + 2.14113749290e-03 2.52142549073e-03 2.92867409920e-03 3.08883670935e-03 2.88306865164e-03 2.44706610707e-03 + 2.03829385095e-03 1.86367042242e-03 2.04872962448e-03 2.59053538526e-03 3.27258092822e-03 3.79759296344e-03 + 4.02992640816e-03 3.97391933537e-03 3.61936140552e-03 3.00623748582e-03 2.37729788247e-03 2.04978644409e-03 + 3.80488225673e-03 4.65015890666e-03 5.19495556690e-03 5.25740014441e-03 4.92007039828e-03 4.48782799524e-03 + 4.24823029243e-03 4.33005297422e-03 4.66011247850e-03 4.96272421857e-03 4.95994948306e-03 4.61922238201e-03 + 4.07249322730e-03 3.40266526949e-03 2.70747335897e-03 2.26006944424e-03 2.33972826540e-03 2.93777399273e-03 + 6.06410758101e-03 7.22294358234e-03 7.88930346614e-03 8.09395269674e-03 8.11724365326e-03 8.24754664505e-03 + 8.56948649589e-03 8.89455437834e-03 8.80742605829e-03 7.98585308339e-03 6.56341729047e-03 4.97527293695e-03 + 3.52993535325e-03 2.39019521685e-03 1.82491975538e-03 2.10726915800e-03 3.16492692833e-03 4.61317888434e-03 + 8.52718757253e-03 1.00226472966e-02 1.11611142851e-02 1.22584771096e-02 1.35701494201e-02 1.49930568984e-02 + 1.60334458750e-02 1.59541502050e-02 1.42390447767e-02 1.11722405526e-02 7.70252852926e-03 4.66962599119e-03 + 2.46481354505e-03 1.31742443877e-03 1.41462207266e-03 2.64058162437e-03 4.54196519442e-03 6.62666715427e-03 + 1.09117918518e-02 1.31050307938e-02 1.56327714911e-02 1.89093432867e-02 2.26945812508e-02 2.59193289069e-02 + 2.70350435225e-02 2.49078403710e-02 1.98525378093e-02 1.35711923171e-02 7.85506925631e-03 3.65403616848e-03 + 1.25360483289e-03 6.65404139599e-04 1.62333209862e-03 3.60018787953e-03 6.05916724865e-03 8.58553549874e-03 + 1.34248171854e-02 2.04065556638e-02 2.99579895854e-02 4.06456343356e-02 4.91893404948e-02 5.16922031093e-02 + 4.63515394048e-02 3.52212919248e-02 2.26418218727e-02 1.21295618760e-02 5.06139638499e-03 1.30184372110e-03 + 6.54077156887e-05 4.22372083279e-04 1.66743900192e-03 3.46459893693e-03 5.76438338536e-03 8.84007338033e-03 + 1.95464004871e-02 3.10306774569e-02 4.55752230602e-02 5.97168227024e-02 6.77269338345e-02 6.51232330029e-02 + 5.26848394944e-02 3.59579444108e-02 2.06979749470e-02 9.74713525917e-03 3.34753470849e-03 5.36523624947e-04 + 2.59298523394e-05 7.44342318882e-04 2.14405699154e-03 4.15949293899e-03 7.12316750585e-03 1.18558891665e-02 + 2.74967269466e-02 4.36420232584e-02 6.19150718789e-02 7.61747408934e-02 7.93064139979e-02 6.90744644757e-02 + 5.04861773255e-02 3.12503260206e-02 1.63096301498e-02 6.83759035477e-03 1.97211915404e-03 2.23829747855e-04 + 1.87852994275e-04 9.91489749719e-04 2.40086461174e-03 4.72341369718e-03 8.80498973279e-03 1.59619685939e-02 + 3.57069378134e-02 5.46215318997e-02 7.27073454591e-02 8.21725528929e-02 7.76343126598e-02 6.12877704276e-02 + 4.08950133065e-02 2.32955036574e-02 1.11934504018e-02 4.28866464406e-03 1.14279128815e-03 1.95507334097e-04 + 3.16471819237e-04 1.02971303674e-03 2.46730513050e-03 5.35207217745e-03 1.09649994805e-02 2.07853112891e-02 + 4.13540774225e-02 5.91089280522e-02 7.21232821266e-02 7.39047065310e-02 6.33019672420e-02 4.57165366255e-02 + 2.82328301965e-02 1.49975433682e-02 6.76831924421e-03 2.51927620062e-03 7.43897458891e-04 2.12327540652e-04 + 2.97084662728e-04 9.30458510693e-04 2.56960785683e-03 6.25614051117e-03 1.33998714818e-02 2.51450203520e-02 + 4.16763830930e-02 5.43024572829e-02 5.98469959084e-02 5.54295929438e-02 4.33473198176e-02 2.89708220480e-02 + 1.67288742656e-02 8.40215729808e-03 3.72407819847e-03 1.49634406461e-03 5.24437011147e-04 1.42873692211e-04 + 1.95249976383e-04 9.01576210579e-04 2.94134670541e-03 7.43431553293e-03 1.54779610635e-02 2.73825069665e-02 + 3.59616014341e-02 4.20419738355e-02 4.16159507566e-02 3.49667929620e-02 2.51732573408e-02 1.56812321019e-02 + 8.54808242344e-03 4.21287020546e-03 2.00415228670e-03 9.21650367714e-04 3.19691846459e-04 3.18442711627e-05 + 1.74475752795e-04 1.13521512331e-03 3.65158157725e-03 8.56944870389e-03 1.62963344943e-02 2.61841003415e-02 + 2.63708741167e-02 2.74166884138e-02 2.43232755542e-02 1.85706364175e-02 1.23230843643e-02 7.18765354396e-03 + 3.82071041505e-03 2.01380850232e-03 1.09739912579e-03 5.26673434555e-04 1.37210281033e-04 1.26655301409e-05 + 3.94989163510e-04 1.72616075420e-03 4.53662363939e-03 9.11692433674e-03 1.51942116571e-02 2.16271673838e-02 + 1.63770431635e-02 1.49471560716e-02 1.17347785492e-02 8.02619821395e-03 4.86212204656e-03 2.71576292665e-03 + 1.53469236072e-03 9.33177102700e-04 5.42236927936e-04 2.34301156594e-04 7.04931452675e-05 2.23066694400e-04 + 9.71942768010e-04 2.61021849603e-03 5.24288835727e-03 8.68306433372e-03 1.23898230409e-02 1.53530753636e-02 + 8.47891610692e-03 6.52515153077e-03 4.30376961185e-03 2.49191374164e-03 1.35170074227e-03 7.86831919390e-04 + 5.25156358570e-04 3.39907337321e-04 1.74488306603e-04 9.55296694379e-05 2.35164834530e-04 7.82237489532e-04 + 1.88981405186e-03 3.52255559546e-03 5.46063883479e-03 7.40040074721e-03 8.90114418843e-03 9.37626500506e-03 + 3.45169668488e-03 1.97947808654e-03 8.94263024123e-04 3.33682714425e-04 1.62278433362e-04 1.38382313866e-04 + 9.77688590934e-05 3.36924091294e-05 3.81678443316e-05 2.38486915864e-04 7.74360919219e-04 1.70995550218e-03 + 2.91975412865e-03 4.14597761613e-03 5.15983837723e-03 5.76205179511e-03 5.71015719071e-03 4.86657810835e-03 + 9.93367325712e-04 2.99812132466e-04 3.31032606638e-05 3.81102001502e-05 1.02123667796e-04 9.98015726909e-05 + 5.55994677548e-05 8.32502579855e-05 3.16986939871e-04 8.77278618219e-04 1.77695538262e-03 2.82841679709e-03 + 3.74575618204e-03 4.33538686558e-03 4.49923671156e-03 4.14318919008e-03 3.25752852484e-03 2.07541182860e-03 + 4.34780372224e-04 3.84503037790e-04 5.19626904962e-04 6.33506637119e-04 6.19000773618e-04 5.36641503703e-04 + 5.42448009711e-04 7.82849093950e-04 1.34944966609e-03 2.20932237388e-03 3.13946866204e-03 3.84821963159e-03 + 4.17775363332e-03 4.09780379534e-03 3.58926353856e-03 2.69908293383e-03 1.67152477990e-03 8.51387333216e-04 + 1.08645167963e-03 1.42690283172e-03 1.70518891162e-03 1.79330688645e-03 1.75071402873e-03 1.78491551982e-03 + 2.08436638467e-03 2.70474073398e-03 3.52104541563e-03 4.24686577035e-03 4.61375194893e-03 4.55704089392e-03 + 4.14902904326e-03 3.44035899689e-03 2.51145180029e-03 1.60750582890e-03 1.02900237496e-03 8.89364125708e-04 + 2.43871613194e-03 3.04836094495e-03 3.48185988198e-03 3.76700996505e-03 4.10893804463e-03 4.71759146107e-03 + 5.61363648661e-03 6.53801535281e-03 7.04090939788e-03 6.81722659707e-03 5.97230263790e-03 4.83247590230e-03 + 3.61053622951e-03 2.42416068271e-03 1.50080259898e-03 1.10262025814e-03 1.26171749419e-03 1.78195773951e-03 + 4.23110746574e-03 5.22458194847e-03 6.20008495029e-03 7.36346360248e-03 8.87174982624e-03 1.06208392403e-02 + 1.21420392357e-02 1.26978314869e-02 1.17716702997e-02 9.59961674929e-03 6.97522228228e-03 4.55351208645e-03 + 2.61187901541e-03 1.32886053102e-03 8.83980611600e-04 1.23753991515e-03 2.09761565530e-03 3.15910378215e-03 + 6.42201300043e-03 8.27741900663e-03 1.07040190235e-02 1.38859847792e-02 1.75078593286e-02 2.06802000486e-02 + 2.21267580103e-02 2.08395648827e-02 1.69894878413e-02 1.19608923775e-02 7.26260416559e-03 3.68643042940e-03 + 1.44632640874e-03 5.47185416275e-04 8.02744715677e-04 1.82851664667e-03 3.23979252973e-03 4.79541143701e-03 + 9.27103425332e-03 1.29885944609e-02 1.82518550148e-02 2.47618576445e-02 3.11232387611e-02 3.50911472916e-02 + 3.45973341484e-02 2.93141866755e-02 2.11870815960e-02 1.29902000102e-02 6.56326663720e-03 2.46121599225e-03 + 5.22015189670e-04 2.75927164133e-04 1.14039021038e-03 2.63749714918e-03 4.49296367921e-03 6.62855310192e-03 + 1.68221764596e-02 2.75390241118e-02 4.04666704279e-02 5.22497308971e-02 5.82907788216e-02 5.55141492198e-02 + 4.49352270228e-02 3.09537617171e-02 1.80840542292e-02 8.72336838805e-03 3.19229787488e-03 6.67190030790e-04 + 7.83802368547e-06 3.39503688133e-04 1.25927604608e-03 2.75359631848e-03 5.20938150280e-03 9.52737603732e-03 + 2.69806716640e-02 4.27662419131e-02 5.95278042899e-02 7.14842462854e-02 7.30024566107e-02 6.30578103184e-02 + 4.62574680116e-02 2.89935352643e-02 1.54298540736e-02 6.72415064923e-03 2.17744292802e-03 3.93411965319e-04 + 9.18267394395e-05 5.53887343941e-04 1.64772442561e-03 3.75623634389e-03 7.82573244582e-03 1.52114611050e-02 + 3.86817428132e-02 5.80676571044e-02 7.52710747474e-02 8.29669846451e-02 7.71273168759e-02 6.06765090709e-02 + 4.08531626887e-02 2.37301904380e-02 1.17982354600e-02 4.86622929480e-03 1.56251776272e-03 3.57118322689e-04 + 1.88883424506e-04 6.69424900259e-04 2.04483771083e-03 5.19933864493e-03 1.15525394617e-02 2.25468585240e-02 + 4.85432625318e-02 6.77387936050e-02 8.05773400491e-02 8.09255235203e-02 6.86645396924e-02 4.97814892006e-02 + 3.13032123918e-02 1.72282890684e-02 8.29792172624e-03 3.48181991974e-03 1.24876351772e-03 3.56870911456e-04 + 1.81728206183e-04 7.26400616963e-04 2.64142045293e-03 7.21197271181e-03 1.59855357604e-02 2.99925647742e-02 + 5.25387903798e-02 6.70500986128e-02 7.25366358738e-02 6.64423333091e-02 5.19825343637e-02 3.52785201899e-02 + 2.11136696314e-02 1.13337135639e-02 5.58141229051e-03 2.55701857995e-03 1.01900311873e-03 2.59464709288e-04 + 1.08799104418e-04 9.13282288699e-04 3.61942604041e-03 9.60767969539e-03 2.00831575666e-02 3.51413174204e-02 + 4.85381160892e-02 5.60828149943e-02 5.51380487649e-02 4.64517653115e-02 3.39856653402e-02 2.19605150822e-02 + 1.28294156042e-02 7.03944848926e-03 3.76063505693e-03 1.88874334695e-03 7.34754196730e-04 9.74061313919e-05 + 1.34254177692e-04 1.41822846263e-03 4.95131770464e-03 1.18009312521e-02 2.24161123141e-02 3.57484661782e-02 + 3.81206397226e-02 3.97789809252e-02 3.57500042913e-02 2.80340852800e-02 1.94855524326e-02 1.22826510874e-02 + 7.32899061692e-03 4.35678754944e-03 2.55682718546e-03 1.31065943412e-03 4.31576805774e-04 2.56534902457e-05 + 4.38155249967e-04 2.28780672639e-03 6.30548399319e-03 1.29408296000e-02 2.18311325716e-02 3.12309087766e-02 + 2.54501112940e-02 2.40020573628e-02 1.98531838859e-02 1.46736889129e-02 9.91845938557e-03 6.38480573271e-03 + 4.13100496926e-03 2.70793781930e-03 1.65647107904e-03 8.08672105937e-04 2.47215854393e-04 2.23741792948e-04 + 1.12552592912e-03 3.35808210408e-03 7.16105729264e-03 1.24218904847e-02 1.83587742652e-02 2.33209276398e-02 + 1.43495764870e-02 1.21648230860e-02 9.28132148647e-03 6.58344387895e-03 4.53266463496e-03 3.17475450028e-03 + 2.27245161829e-03 1.56050525861e-03 9.37697113489e-04 4.66970848604e-04 3.37644377713e-04 8.22500472309e-04 + 2.13379227526e-03 4.29410693549e-03 7.16680408588e-03 1.04262681199e-02 1.33383763482e-02 1.48569553272e-02 + 6.60446800316e-03 4.91243059304e-03 3.44784370745e-03 2.44834433500e-03 1.85009455033e-03 1.44606886476e-03 + 1.07368083715e-03 7.12280097930e-04 4.46188636244e-04 4.25183582022e-04 8.40443538058e-04 1.79710952109e-03 + 3.18900429836e-03 4.79268815127e-03 6.40848968275e-03 7.76271081912e-03 8.40026200227e-03 7.96365929153e-03 + 2.22856691495e-03 1.34569673414e-03 8.66175143925e-04 6.85775550530e-04 6.01090419239e-04 4.70417164479e-04 + 3.03510901791e-04 2.15735156768e-04 3.57641426424e-04 8.70286781961e-04 1.77970146135e-03 2.90313354827e-03 + 3.96224314329e-03 4.77336252596e-03 5.22743716742e-03 5.17403598081e-03 4.51158952231e-03 3.39951265779e-03 + 3.74694622193e-04 1.12535123639e-04 8.26931701037e-05 1.12223443513e-04 8.41380057421e-05 1.88454027053e-05 + 4.57441361991e-05 3.20604819650e-04 9.55914451883e-04 1.92084745499e-03 2.97916591395e-03 3.82560543107e-03 + 4.28714277241e-03 4.31686635641e-03 3.88335443830e-03 3.01941750858e-03 1.94558147739e-03 9.91483447602e-04 + 4.42964637545e-05 9.59678471026e-05 1.73221855004e-04 1.82210530620e-04 1.66868648776e-04 2.84319152761e-04 + 6.95755029875e-04 1.46947419227e-03 2.50132565125e-03 3.50447796384e-03 4.17512478322e-03 4.38446041939e-03 + 4.14898145458e-03 3.50167566829e-03 2.52754130777e-03 1.47004749016e-03 6.35194148216e-04 1.75676078030e-04 + 5.21657268098e-04 7.23266016193e-04 8.61466967034e-04 9.89415109390e-04 1.29387103134e-03 1.95552993099e-03 + 2.99232054943e-03 4.17467113765e-03 5.09560902032e-03 5.43952220653e-03 5.19104462656e-03 4.51805384299e-03 + 3.55631440324e-03 2.42762979030e-03 1.36586209378e-03 6.35385413134e-04 3.26780006375e-04 3.41452518997e-04 + 1.49742782168e-03 1.94080513341e-03 2.46810159643e-03 3.27758849410e-03 4.53298536084e-03 6.17728291709e-03 + 7.81316294243e-03 8.79591266064e-03 8.66488758273e-03 7.53147907223e-03 5.89433413399e-03 4.17050006406e-03 + 2.58224917480e-03 1.33596234634e-03 6.26297335640e-04 4.62815337871e-04 6.70712246943e-04 1.05781216974e-03 + 3.00050179971e-03 4.16484019366e-03 5.91635085905e-03 8.39203111418e-03 1.13600415169e-02 1.41425753413e-02 + 1.57146312567e-02 1.52459807688e-02 1.28229528012e-02 9.41782220764e-03 6.07182706379e-03 3.35401673152e-03 + 1.48437111273e-03 5.29686260603e-04 3.74458534366e-04 7.46089375176e-04 1.38131462718e-03 2.13340757877e-03 + 5.44189303916e-03 8.35214030988e-03 1.26346441757e-02 1.79139755978e-02 2.30468765183e-02 2.63368827304e-02 + 2.62442031546e-02 2.25212613557e-02 1.65881755268e-02 1.04718832284e-02 5.54847051531e-03 2.28570169675e-03 + 6.04000568971e-04 1.54375288803e-04 4.99331558559e-04 1.28600231196e-03 2.32020342700e-03 3.60557596462e-03 + 9.71162682964e-03 1.58322140630e-02 2.40431179047e-02 3.28588901511e-02 3.96074019427e-02 4.14478461592e-02 + 3.72126558504e-02 2.85145944271e-02 1.86047096205e-02 1.01956121189e-02 4.45001319444e-03 1.30950642600e-03 + 1.24774780309e-04 1.46031282136e-04 8.42750620757e-04 1.96597959110e-03 3.51219389669e-03 5.83857042160e-03 + 2.23594738546e-02 3.62779734079e-02 5.07683353254e-02 6.05111713430e-02 6.08181459381e-02 5.15244485525e-02 + 3.71327929925e-02 2.29879850681e-02 1.21936555003e-02 5.41778169125e-03 1.92237854802e-03 4.88284317956e-04 + 1.07035238816e-04 2.76753722399e-04 9.27942989217e-04 2.41596773596e-03 5.66739414067e-03 1.19967886887e-02 + 3.60536506595e-02 5.45814265095e-02 7.03937936079e-02 7.63414975218e-02 6.93597604478e-02 5.32983268813e-02 + 3.52460246638e-02 2.03301584548e-02 1.02440297615e-02 4.48801255702e-03 1.70666828511e-03 5.53800293951e-04 + 1.92193169154e-04 3.72218936873e-04 1.38295705356e-03 4.12367200218e-03 1.00219507826e-02 2.05119152736e-02 + 4.98364107820e-02 6.95716968250e-02 8.18105836900e-02 8.04417284504e-02 6.65593386489e-02 4.72409596581e-02 + 2.94405950813e-02 1.64078086149e-02 8.29772480571e-03 3.88383558644e-03 1.68974301411e-03 6.18083307153e-04 + 1.72016135285e-04 4.52182255406e-04 2.20875589911e-03 6.80697484643e-03 1.58467903430e-02 3.04386888555e-02 + 5.85075533588e-02 7.43644835987e-02 7.92827016571e-02 7.10728104092e-02 5.44576557810e-02 3.66025477218e-02 + 2.21981877316e-02 1.24933277019e-02 6.71646374861e-03 3.48465748288e-03 1.63368518916e-03 5.28604789756e-04 + 6.46165909733e-05 7.18531099617e-04 3.61007710507e-03 1.02464053944e-02 2.19510383236e-02 3.89106112875e-02 + 5.79014409674e-02 6.65396853545e-02 6.45710938983e-02 5.35825042856e-02 3.89244205228e-02 2.55201272949e-02 + 1.56570322109e-02 9.33856362438e-03 5.49972602133e-03 3.05256628444e-03 1.37638881480e-03 2.97614442691e-04 + 5.07623780114e-05 1.39364804345e-03 5.52800851282e-03 1.36703448925e-02 2.63913325048e-02 4.25051516087e-02 + 4.83265798268e-02 5.03500402380e-02 4.50875179631e-02 3.54194293631e-02 2.51036771353e-02 1.66584281865e-02 + 1.08169074740e-02 7.04128147043e-03 4.44015218877e-03 2.45874348334e-03 9.65368821172e-04 1.09130271137e-04 + 3.58623429044e-04 2.52426390190e-03 7.50847141081e-03 1.58875479000e-02 2.72731027854e-02 3.94315362200e-02 + 3.43220002239e-02 3.27822570379e-02 2.76477066038e-02 2.11456839912e-02 1.51726158777e-02 1.06590383981e-02 + 7.55055256119e-03 5.27735741188e-03 3.38593104403e-03 1.77838178830e-03 6.04629853624e-04 2.09122070981e-04 + 1.11595845469e-03 3.86642183125e-03 8.81669194540e-03 1.58967655644e-02 2.40837841428e-02 3.10848426132e-02 + 2.09386738414e-02 1.86136516892e-02 1.51698682289e-02 1.17297529814e-02 8.92784530565e-03 6.85324487410e-03 + 5.22182239703e-03 3.74590704540e-03 2.35955335176e-03 1.19272761065e-03 5.34898858357e-04 7.78931074355e-04 + 2.22623926386e-03 4.96282765400e-03 8.91494534540e-03 1.36943882658e-02 1.82189720392e-02 2.09413874366e-02 + 1.09456620102e-02 9.22737565408e-03 7.54873916749e-03 6.21347680218e-03 5.19601741202e-03 4.29533131224e-03 + 3.35556413366e-03 2.38074474520e-03 1.49114441241e-03 9.09686505992e-04 9.43681663596e-04 1.78489156363e-03 + 3.35097980673e-03 5.44480629194e-03 7.88527483175e-03 1.02748465993e-02 1.18667832858e-02 1.20526872491e-02 + 4.78705229787e-03 3.96065518533e-03 3.45902260578e-03 3.16972129560e-03 2.86469397273e-03 2.40305382667e-03 + 1.82968909931e-03 1.29637465977e-03 9.93063911622e-04 1.13491646156e-03 1.82708601993e-03 2.91328492323e-03 + 4.11541388550e-03 5.26618582317e-03 6.23898685617e-03 6.76847979827e-03 6.59461669218e-03 5.79695336752e-03 + 1.65204039960e-03 1.44435889860e-03 1.44388740181e-03 1.44477539881e-03 1.29184122103e-03 1.00865012591e-03 + 7.62475315086e-04 7.53039550106e-04 1.13930437585e-03 1.93365606976e-03 2.91711026294e-03 3.78117100804e-03 + 4.35517642123e-03 4.59193923617e-03 4.42889033567e-03 3.83864639279e-03 2.98705568899e-03 2.18154474203e-03 + 3.74647149042e-04 4.03092021298e-04 4.57032236329e-04 4.14951217546e-04 2.91032908959e-04 2.44764122441e-04 + 4.77344253129e-04 1.11402798165e-03 2.09152820661e-03 3.12413157387e-03 3.88077407408e-03 4.20595485328e-03 + 4.09951956095e-03 3.58119208000e-03 2.72341007109e-03 1.75769160159e-03 9.76298765812e-04 5.25781774323e-04 + 3.56988799359e-05 7.19321438699e-05 4.57392321665e-05 1.67544373334e-07 1.13968125233e-04 5.87877141789e-04 + 1.49795893306e-03 2.68753074954e-03 3.78874159238e-03 4.44964076523e-03 4.55562884334e-03 4.17939681866e-03 + 3.41632356393e-03 2.39337045097e-03 1.35725009427e-03 5.77314042839e-04 1.56205913022e-04 2.02855200658e-05 + 1.92934527061e-04 2.48224571369e-04 3.48578196820e-04 7.09727500467e-04 1.54590119435e-03 2.88044986324e-03 + 4.42050565122e-03 5.63381215391e-03 6.09371836438e-03 5.76571151460e-03 4.89026980524e-03 3.70851598842e-03 + 2.42241992643e-03 1.28235581332e-03 5.07644356891e-04 1.35279024729e-04 4.91317660182e-05 1.07027358353e-04 + 8.13540981583e-04 1.25187750238e-03 2.13997419456e-03 3.69037826581e-03 5.84272507921e-03 8.12431941773e-03 + 9.72023616843e-03 9.95770888011e-03 8.83385056223e-03 6.91265720244e-03 4.79096777280e-03 2.85639432413e-03 + 1.37630406045e-03 4.97102192012e-04 1.56300252119e-04 1.58297581819e-04 3.26197326082e-04 5.51361031029e-04 + 2.28579257697e-03 4.00495584135e-03 6.81342458095e-03 1.05208395210e-02 1.43538036912e-02 1.70391633429e-02 + 1.73917401081e-02 1.52262964361e-02 1.15158411879e-02 7.55249045745e-03 4.20799545527e-03 1.86785993650e-03 + 5.75460424775e-04 1.08969310777e-04 1.45301696181e-04 4.34942427888e-04 8.43085573674e-04 1.37692802111e-03 + 5.56281011020e-03 9.96996297391e-03 1.60484383982e-02 2.26610552738e-02 2.77751785018e-02 2.92428101330e-02 + 2.62757520521e-02 2.01738246237e-02 1.32684299313e-02 7.38766745207e-03 3.32248059027e-03 1.06374333455e-03 + 1.49788604544e-04 2.97057139122e-05 3.28294487043e-04 8.63054094313e-04 1.63347267590e-03 2.97282175203e-03 + 1.19621939720e-02 2.06105901434e-02 3.10125588349e-02 4.03122115451e-02 4.47898808898e-02 4.22235832154e-02 + 3.37907380699e-02 2.30833773639e-02 1.34441575244e-02 6.52448220454e-03 2.47626285519e-03 6.12619057170e-04 + 3.98584129979e-05 1.29385117851e-04 6.05615452229e-04 1.45380533560e-03 3.03081929272e-03 6.20203132130e-03 + 2.76399541934e-02 4.30682398622e-02 5.64483708586e-02 6.11179799037e-02 5.43902614433e-02 4.03612349559e-02 + 2.56721240349e-02 1.43797557410e-02 7.25443704239e-03 3.42248104460e-03 1.59954789012e-03 7.40379415119e-04 + 3.09223914621e-04 1.93086024367e-04 6.60723855918e-04 2.50176720863e-03 6.92112694610e-03 1.51108460239e-02 + 4.31957387133e-02 6.17445174060e-02 7.30975777050e-02 7.08618585022e-02 5.67731834434e-02 3.87164696435e-02 + 2.33811602913e-02 1.30063220575e-02 6.95406514366e-03 3.74219962957e-03 2.02217851510e-03 9.69256079202e-04 + 2.99110584413e-04 1.95157834626e-04 1.38975455739e-03 5.11088113161e-03 1.27597601309e-02 2.55258668004e-02 + 5.64606784653e-02 7.29686478208e-02 7.75280973123e-02 6.78700778011e-02 5.01922065994e-02 3.27274692661e-02 + 1.97809606242e-02 1.16424606641e-02 6.93716875206e-03 4.17106298787e-03 2.33091267904e-03 9.73537427798e-04 + 1.33666146838e-04 3.81791315826e-04 2.84206333585e-03 8.89793567975e-03 1.98866037273e-02 3.64593746789e-02 + 6.09972695746e-02 7.06688437313e-02 6.78197110032e-02 5.48746650290e-02 3.89021635600e-02 2.54659930472e-02 + 1.62757039593e-02 1.05716130594e-02 6.94684262767e-03 4.34057379906e-03 2.27683224380e-03 7.09574680197e-04 + 5.65800526198e-07 1.03575988756e-03 5.03992470913e-03 1.32040475870e-02 2.63486416703e-02 4.37365843277e-02 + 5.44134639286e-02 5.68247078530e-02 5.03579904306e-02 3.90658861666e-02 2.78206243646e-02 1.92436431009e-02 + 1.35199319436e-02 9.64303554335e-03 6.62026972814e-03 4.02103726066e-03 1.85123406704e-03 3.69913395971e-04 + 1.92745133664e-04 2.27225033253e-03 7.55468301924e-03 1.66796196502e-02 2.94749448901e-02 4.36857942023e-02 + 4.07484644887e-02 3.91762281505e-02 3.32214580945e-02 2.58613870385e-02 1.94320982340e-02 1.47398183049e-02 + 1.13574844524e-02 8.50504973561e-03 5.79202228167e-03 3.29434303932e-03 1.29937438706e-03 2.77251447553e-04 + 9.09837989766e-04 3.85534273907e-03 9.50339503068e-03 1.78013157822e-02 2.76713540210e-02 3.64247527421e-02 + 2.63726711423e-02 2.41341652758e-02 2.04728376934e-02 1.67829251960e-02 1.37991476673e-02 1.14674279537e-02 + 9.30470041635e-03 6.99236563336e-03 4.60577324574e-03 2.45164267465e-03 9.70880828350e-04 7.04924479658e-04 + 2.07661772729e-03 5.21926480418e-03 1.00388908019e-02 1.60474265144e-02 2.19230374295e-02 2.57644144204e-02 + 1.51851368243e-02 1.38080726899e-02 1.23139360729e-02 1.10428424365e-02 9.94115810235e-03 8.70253215584e-03 + 7.11364530862e-03 5.24432491401e-03 3.35460249735e-03 1.83427141462e-03 1.16692131575e-03 1.67196566912e-03 + 3.30151190024e-03 5.83880626364e-03 9.04509213113e-03 1.23731963131e-02 1.48633567796e-02 1.57693490755e-02 + 7.96772049772e-03 7.60764916938e-03 7.45446686871e-03 7.33635890143e-03 6.94892149818e-03 6.11011031050e-03 + 4.90174981042e-03 3.55711051411e-03 2.37999622819e-03 1.75342740340e-03 1.93998590513e-03 2.83220342327e-03 + 4.12267685771e-03 5.61464357983e-03 7.12558053051e-03 8.27068594905e-03 8.68832827510e-03 8.44723722070e-03 + 3.93361474245e-03 4.17523589746e-03 4.52508974131e-03 4.67888488060e-03 4.41259923854e-03 3.76720091362e-03 + 2.97668515533e-03 2.31287349905e-03 2.04066450965e-03 2.30674325143e-03 2.96672243833e-03 3.70531276820e-03 + 4.33303908699e-03 4.78802713955e-03 4.96014765932e-03 4.75954888981e-03 4.33065054515e-03 3.98538587735e-03 + 1.89159777326e-03 2.25818430515e-03 2.56402875287e-03 2.59347253226e-03 2.32065672831e-03 1.94512636223e-03 + 1.74213192297e-03 1.91855493982e-03 2.49244786184e-03 3.22597331652e-03 3.79540981343e-03 4.04914707082e-03 + 3.99103003411e-03 3.61886057282e-03 2.96977917950e-03 2.26332421826e-03 1.79269663811e-03 1.68713538652e-03 + 8.48155257953e-04 1.05263225289e-03 1.11567585491e-03 1.01204776795e-03 9.06224940895e-04 1.05547405991e-03 + 1.63327884324e-03 2.56957756266e-03 3.52210753788e-03 4.11295282088e-03 4.20924577948e-03 3.88548122213e-03 + 3.22479819449e-03 2.32721987137e-03 1.43240736497e-03 8.21137107528e-04 5.89367741321e-04 6.44738849819e-04 + 2.67320498244e-04 2.54798814601e-04 1.86034578323e-04 2.50190925189e-04 6.97004412716e-04 1.65258965004e-03 + 2.95444069056e-03 4.15273343977e-03 4.80072263646e-03 4.76940113328e-03 4.20396502746e-03 3.28759784858e-03 + 2.19861830767e-03 1.19025159053e-03 5.01278108907e-04 1.84775279615e-04 1.29450993638e-04 1.96496572381e-04 + 2.55915322274e-05 2.46400291222e-05 2.91582796660e-04 1.09797235985e-03 2.53393787967e-03 4.32402262496e-03 + 5.83813754875e-03 6.48667128293e-03 6.15525287723e-03 5.13716471752e-03 3.77834160197e-03 2.36549050618e-03 + 1.18661361944e-03 4.37613967386e-04 9.55429380802e-05 4.95847074885e-06 2.45261720709e-05 5.09930074221e-05 + 3.92094703888e-04 1.06438460742e-03 2.50180912228e-03 4.75145893539e-03 7.41337682862e-03 9.57916164656e-03 + 1.03087068943e-02 9.39280307700e-03 7.41263483943e-03 5.10540711136e-03 2.98539096169e-03 1.39083154059e-03 + 4.65436395546e-04 8.44394100960e-05 4.14720982601e-06 4.81138232368e-05 1.17914997000e-04 1.84911800890e-04 + 2.15916790700e-03 4.60887009683e-03 8.27690829975e-03 1.25938272372e-02 1.62333015691e-02 1.75784044539e-02 + 1.59859427096e-02 1.23594597343e-02 8.21274791876e-03 4.62927999637e-03 2.10274258165e-03 6.94830579758e-04 + 1.23602952848e-04 2.03091782620e-07 6.94011435470e-05 2.11003802303e-04 4.12244619404e-04 9.00180709309e-04 + 6.56661086880e-03 1.21782192645e-02 1.91932402373e-02 2.57379218731e-02 2.90369508707e-02 2.73334140279e-02 + 2.15809756270e-02 1.44814482730e-02 8.28551821101e-03 3.95407501317e-03 1.50173027690e-03 4.19121119276e-04 + 7.46434105332e-05 5.93543094293e-05 2.03444373090e-04 4.89326739662e-04 1.19242273020e-03 2.98265598557e-03 + 1.48789730311e-02 2.50999567189e-02 3.60449362861e-02 4.34827869831e-02 4.34844834452e-02 3.60702620700e-02 + 2.52036836956e-02 1.51043759800e-02 7.81066315137e-03 3.47697927674e-03 1.35669858601e-03 4.88492768760e-04 + 1.85577799263e-04 1.55811144618e-04 3.64924880503e-04 1.09688597851e-03 3.12662494737e-03 7.48101490195e-03 + 2.90120465805e-02 4.30853284811e-02 5.21175408623e-02 5.02000656867e-02 3.88097463970e-02 2.51123525819e-02 + 1.45616372413e-02 8.22453682227e-03 4.94035630723e-03 3.28387303784e-03 2.24426507919e-03 1.36360315623e-03 + 5.69709816408e-04 8.16077367919e-05 5.44647341807e-04 2.87375035075e-03 7.89345414468e-03 1.64682859167e-02 + 4.35901829695e-02 5.81918774803e-02 6.22621890421e-02 5.33039441720e-02 3.78173638677e-02 2.38419814204e-02 + 1.45986981313e-02 9.39270548250e-03 6.52632326243e-03 4.64649367492e-03 3.04969673746e-03 1.56758317861e-03 + 3.78722619620e-04 8.09626434922e-05 1.64187696752e-03 5.99613746937e-03 1.40875674170e-02 2.69309354929e-02 + 5.31477254174e-02 6.28491028750e-02 5.99276806772e-02 4.72018102582e-02 3.26637034799e-02 2.16516109269e-02 + 1.48612633227e-02 1.08515221674e-02 8.09108601200e-03 5.68211604375e-03 3.38854641618e-03 1.36477777943e-03 + 1.03047331129e-04 5.12557017721e-04 3.58782508736e-03 1.01472429661e-02 2.11057371412e-02 3.66158748505e-02 + 5.19649026232e-02 5.47422182952e-02 4.80605621656e-02 3.69231914580e-02 2.67771572800e-02 1.97454361036e-02 + 1.52782273369e-02 1.20261675403e-02 9.00530273271e-03 5.96245745379e-03 3.11854436183e-03 9.09137341960e-04 + 8.12599914572e-05 1.59246943536e-03 6.17680281514e-03 1.43410924331e-02 2.63254153962e-02 4.05460046417e-02 + 4.14571221859e-02 4.02002918258e-02 3.44178942374e-02 2.76263199491e-02 2.21456053736e-02 1.83264630426e-02 + 1.53465892430e-02 1.22935089701e-02 8.88363395116e-03 5.43297152676e-03 2.45360690265e-03 5.78412825440e-04 + 6.12905889523e-04 3.21680622942e-03 8.63629238376e-03 1.68017067415e-02 2.69071336873e-02 3.63843027435e-02 + 2.83642214672e-02 2.68056217438e-02 2.39066458158e-02 2.10610284007e-02 1.88221118892e-02 1.68636734419e-02 + 1.44924509817e-02 1.13792630317e-02 7.81878857360e-03 4.41599118004e-03 1.83265758813e-03 7.60485339739e-04 + 1.72431957871e-03 4.83216828002e-03 9.84517612818e-03 1.61878020082e-02 2.25708133952e-02 2.70711519917e-02 + 1.78241120029e-02 1.75113568648e-02 1.70417863153e-02 1.66067453926e-02 1.60086718204e-02 1.47604482574e-02 + 1.25396904239e-02 9.51857110990e-03 6.24320009186e-03 3.40740898518e-03 1.71006530283e-03 1.59332364024e-03 + 3.03324816152e-03 5.71924826419e-03 9.24951003792e-03 1.29620954658e-02 1.59192939637e-02 1.74903801402e-02 + 1.09523235908e-02 1.17447199902e-02 1.25841652079e-02 1.31571536713e-02 1.30265314469e-02 1.18937171121e-02 + 9.83823408326e-03 7.25292452762e-03 4.71851997409e-03 2.91130852086e-03 2.28497838118e-03 2.76390093572e-03 + 3.96448339059e-03 5.58537756938e-03 7.34557817869e-03 8.82426365770e-03 9.75441036390e-03 1.03358586324e-02 + 6.88051704182e-03 8.15708656846e-03 9.34419985195e-03 9.98897560498e-03 9.76046632566e-03 8.67449437873e-03 + 7.02503782333e-03 5.23360560603e-03 3.81653520614e-03 3.17474130801e-03 3.25838986272e-03 3.68904318297e-03 + 4.19905779072e-03 4.69094701298e-03 5.03579201586e-03 5.17253974451e-03 5.33713335979e-03 5.87569587132e-03 + 4.49230662737e-03 5.69134895434e-03 6.62495847078e-03 6.94130281880e-03 6.56578748676e-03 5.73002588109e-03 + 4.78224051661e-03 4.07060405417e-03 3.81632768224e-03 3.91542462175e-03 4.04070777532e-03 4.00132730334e-03 + 3.79901364950e-03 3.43406883220e-03 2.94579977844e-03 2.59496310461e-03 2.71785700678e-03 3.40593342349e-03 + 2.93395216015e-03 3.73354624820e-03 4.20223802111e-03 4.21544205262e-03 3.92690806015e-03 3.64933585129e-03 + 3.65496176041e-03 3.99920772029e-03 4.42231284056e-03 4.55532626517e-03 4.27830082614e-03 3.71453856313e-03 + 2.96770755673e-03 2.11546293736e-03 1.39765570939e-03 1.12968670673e-03 1.40559160399e-03 2.07708219386e-03 + 1.73164945820e-03 2.05703898335e-03 2.15790105939e-03 2.16085256311e-03 2.33254042095e-03 2.89468325650e-03 + 3.81189177871e-03 4.69371170943e-03 5.05490830862e-03 4.74600996535e-03 3.97616844295e-03 2.98346859145e-03 + 1.92373895359e-03 1.01423946346e-03 5.08006713379e-04 4.76665522352e-04 7.84140490299e-04 1.25427557108e-03 + 7.35510819877e-04 7.47372166137e-04 8.52965511812e-04 1.31518946741e-03 2.30190176436e-03 3.69543295303e-03 + 5.00193893760e-03 5.62942980160e-03 5.36590780904e-03 4.45017200825e-03 3.23137719868e-03 1.98090698652e-03 + 9.51032086670e-04 3.37194479473e-04 1.45595143144e-04 2.29835826428e-04 4.33680980723e-04 6.34376563063e-04 + 1.04363259493e-04 2.84907132700e-04 9.94248201380e-04 2.38495354371e-03 4.27898289975e-03 6.03664111853e-03 + 6.87313179615e-03 6.50529203111e-03 5.28039238466e-03 3.70965058942e-03 2.17930967539e-03 9.90572963509e-04 + 3.10231330687e-04 6.46246133334e-05 5.59366715520e-05 1.30818926071e-04 1.87229699795e-04 1.56365000478e-04 + 4.16289854059e-04 1.51144814959e-03 3.46010115688e-03 6.08746794965e-03 8.60943467858e-03 9.83452122573e-03 + 9.17822224485e-03 7.18187633814e-03 4.79900082331e-03 2.67952432145e-03 1.15904091907e-03 3.43304867115e-04 + 6.26364293216e-05 3.22189829831e-05 6.82157163539e-05 8.01572039943e-05 2.60615440106e-05 2.84843277750e-05 + 2.53892301825e-03 5.38473210717e-03 9.25328358301e-03 1.32605295181e-02 1.55921780061e-02 1.48469518056e-02 + 1.15574696038e-02 7.51362028547e-03 4.09081230223e-03 1.79478670354e-03 6.04634248872e-04 1.82571363138e-04 + 1.02501743084e-04 1.07921265873e-04 1.00154261214e-04 5.59511612204e-05 1.57166997861e-04 8.50059554231e-04 + 7.39446959603e-03 1.31145180968e-02 1.97025901120e-02 2.45787481578e-02 2.47950525028e-02 2.01322637316e-02 + 1.34184784062e-02 7.53842495314e-03 3.62540127981e-03 1.55190673037e-03 7.06250879293e-04 4.24032192258e-04 + 3.00088357925e-04 1.96403903701e-04 9.84358832072e-05 2.20425168354e-04 1.12890379899e-03 3.41294788231e-03 + 1.60628726446e-02 2.60075865888e-02 3.52230432000e-02 3.86278083733e-02 3.38088371050e-02 2.39838128094e-02 + 1.43554751750e-02 7.63639482653e-03 3.87605910054e-03 2.11591751711e-03 1.34314617562e-03 8.92896175879e-04 + 5.15162162710e-04 1.90614461510e-04 1.44637804683e-04 1.00974589153e-03 3.57238945733e-03 8.43684618133e-03 + 2.39501384635e-02 3.33174859001e-02 3.59027838527e-02 2.96692333224e-02 1.98498753631e-02 1.23091727361e-02 + 8.40376268176e-03 6.75014559847e-03 5.87728795009e-03 4.93598044839e-03 3.67786847327e-03 2.21341767860e-03 + 8.04027613092e-04 1.59887380787e-05 6.06808849388e-04 2.91178285931e-03 7.12035623595e-03 1.40697022014e-02 + 3.42280507364e-02 4.14694415455e-02 3.91301009462e-02 2.99430537544e-02 2.08106981993e-02 1.51521779858e-02 + 1.22898894631e-02 1.06006266102e-02 8.95940518454e-03 6.90391420001e-03 4.52889190756e-03 2.18537294660e-03 + 4.39830384030e-04 1.34419453908e-04 1.86167993936e-03 5.74568484533e-03 1.23437190242e-02 2.24575744430e-02 + 3.79913728917e-02 4.04135000342e-02 3.53582606830e-02 2.78050794822e-02 2.20353020243e-02 1.86094890617e-02 + 1.63599934032e-02 1.41551349570e-02 1.13642435878e-02 8.01883259633e-03 4.59669129910e-03 1.71711457333e-03 + 1.96015692353e-04 8.34606382264e-04 3.87782902404e-03 9.37607959775e-03 1.78088774251e-02 2.86587493459e-02 + 3.31060135036e-02 3.26793305796e-02 2.92666291330e-02 2.58410643714e-02 2.34274221447e-02 2.15548916765e-02 + 1.94232624194e-02 1.63863059943e-02 1.23720897807e-02 7.96600218830e-03 3.96944801363e-03 1.18654876008e-03 + 4.44097695772e-04 2.19616782753e-03 6.29252296490e-03 1.25146494565e-02 2.05216046421e-02 2.84981294434e-02 + 2.44041619370e-02 2.46866533939e-02 2.46043791835e-02 2.45225898710e-02 2.41545572602e-02 2.30289421382e-02 + 2.06073371487e-02 1.66825860597e-02 1.17912048980e-02 6.94185855014e-03 3.10467513127e-03 1.07838225171e-03 + 1.36088816579e-03 3.86317448436e-03 8.04728607992e-03 1.32757687425e-02 1.85969518947e-02 2.25914862075e-02 + 1.72781081808e-02 1.93888337247e-02 2.15202656266e-02 2.30676712481e-02 2.35492508171e-02 2.24936348478e-02 + 1.95755185412e-02 1.50872387991e-02 1.00100894031e-02 5.53799483487e-03 2.61413140403e-03 1.69813296949e-03 + 2.67962790183e-03 5.03271628376e-03 8.09541547056e-03 1.11736902140e-02 1.36667793938e-02 1.55092738172e-02 + 1.29365735137e-02 1.60452573109e-02 1.89146340008e-02 2.08552733145e-02 2.13226707661e-02 1.99208008554e-02 + 1.66810128711e-02 1.22708403340e-02 7.83936573961e-03 4.53205878736e-03 2.92961213985e-03 2.85305544920e-03 + 3.74376532223e-03 5.10441227635e-03 6.52859553200e-03 7.71078686755e-03 8.78697957577e-03 1.03907242659e-02 + 1.02472192466e-02 1.34075780969e-02 1.60991447096e-02 1.76670744349e-02 1.76519536394e-02 1.59523214651e-02 + 1.29101961609e-02 9.32339285969e-03 6.26252638257e-03 4.47606412682e-03 3.88364178036e-03 3.88572284724e-03 + 4.04580230057e-03 4.22097529149e-03 4.34952724538e-03 4.57866901892e-03 5.43040269212e-03 7.36861601657e-03 + 8.21634431071e-03 1.08642288543e-02 1.28795513517e-02 1.37509987622e-02 1.33085714570e-02 1.17419927690e-02 + 9.51430515104e-03 7.34056559346e-03 5.87725681509e-03 5.17867415497e-03 4.75808586982e-03 4.23622425740e-03 + 3.59373323654e-03 2.92822781849e-03 2.41361119023e-03 2.46352229018e-03 3.50963754916e-03 5.56666243126e-03 + 6.39388494969e-03 8.26517283633e-03 9.47058900180e-03 9.78105668381e-03 9.31750724324e-03 8.40181459346e-03 + 7.43947140501e-03 6.77242398374e-03 6.36633090172e-03 5.83732694907e-03 4.95514083015e-03 3.84012341219e-03 + 2.68624972556e-03 1.66216525550e-03 1.08806481359e-03 1.34301460290e-03 2.50817420887e-03 4.32231476261e-03 + 4.64616297678e-03 5.72499619873e-03 6.30544934870e-03 6.45786534130e-03 6.44884236825e-03 6.55018635910e-03 + 6.85672504746e-03 7.10359750370e-03 6.81347217951e-03 5.81634822989e-03 4.40451671111e-03 2.93879526431e-03 + 1.62722671683e-03 6.95379254448e-04 4.24165011003e-04 8.98370759739e-04 1.93664350094e-03 3.27766494936e-03 + 2.98367941142e-03 3.46042054687e-03 3.82311194246e-03 4.30790616595e-03 5.11063944458e-03 6.20438083853e-03 + 7.17241532764e-03 7.37940258012e-03 6.54981287580e-03 5.01961007270e-03 3.32387867479e-03 1.81366766463e-03 + 7.06704613311e-04 1.75372783674e-04 2.39164195540e-04 7.40769646083e-04 1.48350078183e-03 2.29137696883e-03 + 1.53314870093e-03 1.84610256873e-03 2.53594330367e-03 3.75000623930e-03 5.37334400202e-03 6.89134273280e-03 + 7.55413295321e-03 6.97756911737e-03 5.46640704113e-03 3.63893367707e-03 1.98044230747e-03 7.78097683267e-04 + 1.56039651725e-04 4.40568997236e-05 2.57044846501e-04 6.28420284683e-04 1.02436513775e-03 1.32633874146e-03 + 7.19903482351e-04 1.46297216516e-03 2.89654946479e-03 4.90686674823e-03 6.90849938419e-03 7.94320783679e-03 + 7.45457365611e-03 5.80563076872e-03 3.78997301635e-03 2.00274363364e-03 7.56468496382e-04 1.41283314652e-04 + 9.20384791516e-06 1.17988814994e-04 3.06449127554e-04 4.71423061922e-04 5.22935133873e-04 5.12439265410e-04 + 1.11604851579e-03 2.67514186804e-03 4.99133844967e-03 7.59722397641e-03 9.25360168648e-03 8.86960521443e-03 + 6.74746507622e-03 4.14522989170e-03 2.01256904922e-03 6.74736905086e-04 1.01713216941e-04 3.27312442073e-05 + 1.46227704277e-04 2.64496081803e-04 3.12527126912e-04 2.31862120160e-04 1.18137825469e-04 3.08601349151e-04 + 2.97254489858e-03 5.64870848338e-03 9.08296300698e-03 1.19351253795e-02 1.21924507008e-02 9.50744095938e-03 + 5.71491633034e-03 2.66024220099e-03 9.20986573311e-04 2.74040044455e-04 2.44597107831e-04 3.76951577142e-04 + 4.45360909911e-04 3.97111725549e-04 2.08498054399e-04 1.84134283331e-05 2.42236757059e-04 1.20652054041e-03 + 6.69906085105e-03 1.13604779428e-02 1.62077518895e-02 1.81974263603e-02 1.54590933546e-02 9.96868246164e-03 + 5.09636322734e-03 2.29555094729e-03 1.21878621791e-03 1.04601202440e-03 1.09179877945e-03 1.01932358485e-03 + 7.80625995526e-04 3.95995177117e-04 3.44409863193e-05 1.92945998171e-04 1.30278986819e-03 3.41429667333e-03 + 1.35636077516e-02 2.11513860932e-02 2.64107382318e-02 2.51558778833e-02 1.82374367088e-02 1.06803441251e-02 + 5.84085425648e-03 3.70595620585e-03 3.02937936022e-03 2.76573932377e-03 2.36920571355e-03 1.73933849482e-03 + 9.51672719469e-04 2.09419676226e-04 6.09868350370e-05 1.10322524602e-03 3.48538637918e-03 7.43798090788e-03 + 1.39376331350e-02 1.69127654979e-02 1.49769196684e-02 1.07162975343e-02 8.47525926614e-03 8.81437361311e-03 + 9.83229494765e-03 1.02008542348e-02 9.48886903210e-03 7.76056155069e-03 5.42998844432e-03 2.98257607661e-03 + 9.85800807160e-04 1.81551648153e-04 8.06315962624e-04 2.33533142403e-03 4.72754018793e-03 8.75630987176e-03 + 1.81302940474e-02 1.90533281411e-02 1.68008907247e-02 1.52937481580e-02 1.59077655692e-02 1.69535529606e-02 + 1.70445546180e-02 1.58200330749e-02 1.32636241342e-02 9.73659208124e-03 5.94335917410e-03 2.63296477703e-03 + 6.21175028169e-04 4.84758972561e-04 1.91675038920e-03 4.35621515405e-03 8.11486883519e-03 1.34120892006e-02 + 1.78048351998e-02 1.85057067653e-02 1.96111864138e-02 2.20485146589e-02 2.41488926714e-02 2.45668099334e-02 + 2.32121378824e-02 2.01611836221e-02 1.56145293597e-02 1.03752420933e-02 5.52646513913e-03 2.02262179254e-03 + 6.12418709633e-04 1.38848110896e-03 3.64898479161e-03 6.85350348990e-03 1.09727554873e-02 1.52301421904e-02 + 1.50008567926e-02 1.85122233959e-02 2.35902193495e-02 2.80271220394e-02 3.00535908476e-02 2.96093369548e-02 + 2.69668391885e-02 2.21764633484e-02 1.59031110990e-02 9.56949621362e-03 4.53448957310e-03 1.66221968956e-03 + 1.24956054146e-03 2.81280658231e-03 5.39261303124e-03 8.25897846311e-03 1.09216594101e-02 1.29842931460e-02 + 1.35825601861e-02 1.99514904929e-02 2.65506735915e-02 3.09545539663e-02 3.25136273950e-02 3.14227406161e-02 + 2.76121809270e-02 2.13729699906e-02 1.41374399177e-02 7.81351564680e-03 3.66420429266e-03 2.01655155920e-03 + 2.46062789957e-03 4.09473388846e-03 5.93521407886e-03 7.31372562879e-03 8.21425844618e-03 9.73050494537e-03 + 1.39008728119e-02 2.09444466445e-02 2.70082604756e-02 3.07098685481e-02 3.17523898285e-02 2.99016967391e-02 + 2.50323820037e-02 1.80956453354e-02 1.11690185294e-02 6.14472603634e-03 3.63418092973e-03 3.10116623899e-03 + 3.61994416238e-03 4.37946432498e-03 4.82656877112e-03 4.90911289501e-03 5.52338070999e-03 8.28087145979e-03 + 1.41211570863e-02 2.02787818991e-02 2.51617492097e-02 2.79414396260e-02 2.81584556575e-02 2.54623467378e-02 + 2.01634734401e-02 1.38206037457e-02 8.58090167143e-03 5.62802488758e-03 4.56077115562e-03 4.27431888227e-03 + 4.01387188573e-03 3.54932177285e-03 2.97048887177e-03 2.85419449760e-03 4.33208079043e-03 8.25391771966e-03 + 1.32732336011e-02 1.80803094841e-02 2.16306727318e-02 2.32693821517e-02 2.26140643491e-02 1.96336114406e-02 + 1.50869944524e-02 1.06219012878e-02 7.70254746555e-03 6.39581512891e-03 5.69022757602e-03 4.76316539307e-03 + 3.51327615688e-03 2.23906786538e-03 1.43134967947e-03 1.83407832021e-03 4.11098476771e-03 8.21601970828e-03 + 1.14603792687e-02 1.48700391488e-02 1.71011799657e-02 1.77772567468e-02 1.68471662055e-02 1.45939939960e-02 + 1.18440644827e-02 9.67126227150e-03 8.41713371411e-03 7.45067626615e-03 6.10610522586e-03 4.33765900356e-03 + 2.50363165477e-03 1.06665779401e-03 5.84001417900e-04 1.54996982689e-03 4.02706712861e-03 7.57321401029e-03 + 9.12459803425e-03 1.12864940650e-02 1.25420167245e-02 1.28830004800e-02 1.25042621527e-02 1.17266690611e-02 + 1.09649054064e-02 1.03175375964e-02 9.34879567884e-03 7.68462626122e-03 5.50560856946e-03 3.27601541766e-03 + 1.41129024315e-03 3.11731976851e-04 3.40891357724e-04 1.56994130933e-03 3.73317784835e-03 6.41952984784e-03 + 6.71332345225e-03 7.97295539642e-03 8.83806375092e-03 9.52542566034e-03 1.02146950176e-02 1.09257951563e-02 + 1.13542562873e-02 1.08930158185e-02 9.22000092978e-03 6.73244341066e-03 4.14871813185e-03 1.98512519846e-03 + 5.42168783236e-04 2.47712879770e-05 4.44606212492e-04 1.59522161804e-03 3.21725775258e-03 5.03833677141e-03 + 4.53696878441e-03 5.41927287301e-03 6.52840495085e-03 8.00501035413e-03 9.71657041701e-03 1.11387500818e-02 + 1.14523906706e-02 1.01724220411e-02 7.68112840025e-03 4.87598994750e-03 2.48241244276e-03 8.51940877148e-04 + 8.45407249449e-05 8.30176453145e-05 6.26445265586e-04 1.51312274360e-03 2.57752032729e-03 3.62685542010e-03 + 2.94511729787e-03 4.09350335195e-03 5.89617065745e-03 8.15301187265e-03 1.02256913612e-02 1.11442919391e-02 + 1.02775459051e-02 7.95280147634e-03 5.13599155978e-03 2.67644958481e-03 1.00229525196e-03 1.84473592480e-04 + 3.66319170585e-05 2.88882905231e-04 7.56908851666e-04 1.31879190717e-03 1.84375836529e-03 2.30790453763e-03 + 2.39848530650e-03 4.16642642439e-03 6.61287639564e-03 9.14049964706e-03 1.05854298051e-02 9.99838109577e-03 + 7.66381324915e-03 4.78454896666e-03 2.37338000795e-03 8.39931095061e-04 1.67561352273e-04 8.15495981761e-05 + 2.58850180740e-04 5.24842668493e-04 7.96999910467e-04 9.76767156147e-04 1.08532127198e-03 1.42977082804e-03 + 2.80839032745e-03 4.97931969058e-03 7.67202066037e-03 9.78562967387e-03 9.75759123040e-03 7.38242706605e-03 + 4.19317861400e-03 1.71153422228e-03 4.04123739449e-04 4.88522496584e-05 1.99183841763e-04 4.51012819931e-04 + 6.35929911154e-04 7.28587442280e-04 6.76442851119e-04 5.34582026431e-04 6.49308468939e-04 1.37422445357e-03 + 3.52751404593e-03 5.92905812085e-03 8.58655784113e-03 9.57562452361e-03 7.58666801100e-03 4.02250133383e-03 + 1.24176529122e-03 1.00783605569e-04 1.40592723549e-04 6.31450836021e-04 1.03333410488e-03 1.16283184155e-03 + 1.07099609808e-03 7.82028966574e-04 3.90447944053e-04 2.93490088853e-04 8.27096460031e-04 1.90720320301e-03 + 4.87505972449e-03 7.89398002452e-03 1.00910165440e-02 9.01681183719e-03 5.21152124093e-03 1.85101441507e-03 + 6.70926844938e-04 1.07347966654e-03 1.91496313700e-03 2.45387057704e-03 2.45402452681e-03 2.04254185619e-03 + 1.37144785339e-03 5.94734031064e-04 1.64419885712e-04 5.09802732965e-04 1.44517092877e-03 2.76231554550e-03 + 8.35563225402e-03 1.20669831186e-02 1.25990812684e-02 9.01468567443e-03 4.83215906545e-03 3.17929380547e-03 + 3.70641445135e-03 4.76836979215e-03 5.33175502239e-03 5.06100866681e-03 4.10058228765e-03 2.76502694806e-03 + 1.33289628590e-03 3.00002876531e-04 2.65316574894e-04 1.15341702151e-03 2.53518792535e-03 4.75023723186e-03 + 4.12368548933e-03 3.37306388143e-03 3.34610664515e-03 6.67954373977e-03 1.19918970688e-02 1.59848713841e-02 + 1.74379020831e-02 1.67091714427e-02 1.42442187476e-02 1.06375053547e-02 6.74199309547e-03 3.36031710579e-03 + 1.25535327017e-03 8.22912714846e-04 1.40854295750e-03 2.03421376429e-03 2.65942072377e-03 3.60752069339e-03 + 4.51331902058e-03 6.17516417595e-03 1.17378157448e-02 1.95804799359e-02 2.52957622525e-02 2.71173172791e-02 + 2.59941057893e-02 2.27555777496e-02 1.77847067327e-02 1.19937890097e-02 6.66169655785e-03 2.83758131037e-03 + 1.15346346066e-03 1.36288481501e-03 2.31165294259e-03 3.13183850888e-03 3.81492345271e-03 4.31281316929e-03 + 5.96134556949e-03 1.32429968266e-02 2.39787366828e-02 3.26545687339e-02 3.61605709708e-02 3.55479373292e-02 + 3.22998996260e-02 2.66694798543e-02 1.92229917609e-02 1.16659750018e-02 5.74327755930e-03 2.37353664799e-03 + 1.57424432669e-03 2.41326003560e-03 3.52882548095e-03 4.06579418505e-03 3.89221853874e-03 3.72856190105e-03 + 1.02993701055e-02 2.20953807119e-02 3.37179786810e-02 4.03928775935e-02 4.19732980827e-02 4.01552400206e-02 + 3.52920405867e-02 2.73415006756e-02 1.79434014323e-02 9.79690113811e-03 4.60243411781e-03 2.49587526328e-03 + 2.60920069718e-03 3.57597048464e-03 4.10087235933e-03 3.54389751122e-03 2.53130732077e-03 3.67225998458e-03 + 1.59167089976e-02 2.81155811637e-02 3.75993586740e-02 4.23810878340e-02 4.32847505534e-02 4.06893476199e-02 + 3.40234907738e-02 2.41749343595e-02 1.43133933818e-02 7.41517604801e-03 4.15563620919e-03 3.43923441993e-03 + 3.77566812108e-03 3.94321828547e-03 3.22414329745e-03 1.85884453280e-03 1.75185518295e-03 6.02758092813e-03 + 1.95737068215e-02 2.95857281099e-02 3.67209170369e-02 4.04051596531e-02 4.06563672027e-02 3.66927765543e-02 + 2.83971027238e-02 1.83082586530e-02 1.02757396512e-02 6.13565241376e-03 4.91185277988e-03 4.69788761743e-03 + 4.23707457860e-03 3.11220359900e-03 1.56856713769e-03 7.79834183726e-04 2.88289884632e-03 9.50672599419e-03 + 2.01648783008e-02 2.76747489676e-02 3.29293858145e-02 3.53837385084e-02 3.43186324178e-02 2.90634789457e-02 + 2.08053828527e-02 1.29502630084e-02 8.31038436101e-03 6.74640549539e-03 6.27645105570e-03 5.35942564110e-03 + 3.68072891500e-03 1.72326340600e-03 4.11220520635e-04 1.05923318293e-03 4.86161044113e-03 1.17981175238e-02 + 1.84093023722e-02 2.37330889784e-02 2.71913787538e-02 2.81816396468e-02 2.61238657902e-02 2.11731079400e-02 + 1.52436850279e-02 1.09547270715e-02 9.09871389781e-03 8.31938504998e-03 7.09430244513e-03 5.00220568978e-03 + 2.54284114549e-03 6.06190023334e-04 1.64827344651e-04 1.98176221086e-03 6.19854413088e-03 1.21153542974e-02 + 1.53048824846e-02 1.87602423888e-02 2.07273252279e-02 2.09428756986e-02 1.93365352204e-02 1.65041392276e-02 + 1.38292485273e-02 1.21850229525e-02 1.09992654280e-02 9.27597029515e-03 6.75608371207e-03 3.88245608400e-03 + 1.39269429434e-03 7.31334512584e-05 4.91385657771e-04 2.73142868257e-03 6.42237559541e-03 1.09045273671e-02 + 1.17927741869e-02 1.39266304703e-02 1.52490522982e-02 1.58717409072e-02 1.59230614876e-02 1.56593233720e-02 + 1.52035820930e-02 1.41158715399e-02 1.19044343599e-02 8.77083377689e-03 5.40179842392e-03 2.47639252786e-03 + 5.60523009018e-04 4.64939928678e-05 9.59443515652e-04 3.00248820143e-03 5.80639087152e-03 8.93128545725e-03 + 8.66513735619e-03 1.02471825616e-02 1.18551946666e-02 1.36316032639e-02 1.54069440628e-02 1.66371481307e-02 + 1.64685826568e-02 1.43745915671e-02 1.08212875312e-02 6.90345115081e-03 3.53792871809e-03 1.22828826368e-03 + 1.79397681911e-04 3.03345778839e-04 1.29465588360e-03 2.87315885619e-03 4.82724869706e-03 6.85760768150e-03 + 6.42291103144e-03 8.25893377018e-03 1.07470815621e-02 1.36442705476e-02 1.61558403433e-02 1.70977321742e-02 + 1.56570628009e-02 1.22073264490e-02 8.03781717615e-03 4.35804598915e-03 1.79559159705e-03 4.80947599290e-04 + 2.07938798123e-04 6.19675210675e-04 1.44767994042e-03 2.55428000068e-03 3.79476386176e-03 5.04547537048e-03 + 5.46500921949e-03 8.10561505040e-03 1.14831696109e-02 1.47440623913e-02 1.64968215556e-02 1.56499592243e-02 + 1.24540129443e-02 8.31347573666e-03 4.61509537486e-03 2.05788053466e-03 7.28465317529e-04 3.37591043242e-04 + 4.82694841036e-04 9.00797590406e-04 1.48028048963e-03 2.12633571899e-03 2.81316744218e-03 3.78299950545e-03 + 5.75310388715e-03 9.04606843221e-03 1.26707221921e-02 1.51609996344e-02 1.49280053090e-02 1.18925542698e-02 + 7.70633322142e-03 4.11266422443e-03 1.84119760539e-03 8.06544126958e-04 5.69991497064e-04 6.73353239016e-04 + 8.81069546015e-04 1.13452632092e-03 1.37182352140e-03 1.60223398176e-03 2.13031628844e-03 3.42361883850e-03 + 6.15596677412e-03 9.34753074518e-03 1.23050102887e-02 1.30691478765e-02 1.06185242812e-02 6.48046746567e-03 + 3.01875608645e-03 1.22471526065e-03 7.80750806818e-04 9.78034967128e-04 1.23720306784e-03 1.34354087950e-03 + 1.33992010182e-03 1.24238958869e-03 1.08998225602e-03 1.21146727737e-03 2.03109161448e-03 3.67991121669e-03 + 5.46929412944e-03 7.88130438297e-03 9.36297968784e-03 8.04682489196e-03 4.54496845012e-03 1.56319200428e-03 + 5.67835430828e-04 1.04253322517e-03 1.93432377986e-03 2.50923996477e-03 2.54892965808e-03 2.22688813124e-03 + 1.71633865474e-03 1.12402783631e-03 8.09611273434e-04 1.19100164105e-03 2.18103036991e-03 3.55029935650e-03 + 4.15437447032e-03 5.43223433246e-03 4.91487153366e-03 2.32460092521e-03 2.35976882732e-04 5.06171591309e-04 + 2.32419244671e-03 4.15387052265e-03 5.13345959551e-03 5.06723538287e-03 4.24270255829e-03 3.06669100213e-03 + 1.80530292414e-03 8.66281526153e-04 7.62536507912e-04 1.35353740268e-03 2.05164180565e-03 2.85443603945e-03 + 3.67820710717e-03 3.59689688604e-03 1.74123318750e-03 5.27236161405e-04 2.24948409541e-03 5.71227730402e-03 + 8.59972032333e-03 9.91706511634e-03 9.62051235246e-03 8.05724276122e-03 5.83314421670e-03 3.49994970909e-03 + 1.57755458636e-03 7.06630094501e-04 9.57095016309e-04 1.52667736327e-03 1.97045489908e-03 2.68343038842e-03 diff --git a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/result.ref b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/result.ref index 40989e00449..9fda46ebbbd 100644 --- a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/result.ref +++ b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/result.ref @@ -1,8 +1,8 @@ -etotref -197.4062591802142 -etotperatomref -98.7031295901 +etotref -197.4062600935836 +etotperatomref -98.7031300468 pchgi4s1_cube_compare 0 pchgi1s1.cube 1.999994607 pchgi2s1.cube 1.999994607 pchgi3s1.cube 1.999994607 pchgi4s1.cube 1.999994607 -totaltimeref 0.23 +totaltimeref 0.34 diff --git a/tests/01_PW/087_PW_get_pchg_kpar_bndpar/threshold b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/threshold new file mode 100644 index 00000000000..b8c87bb4db4 --- /dev/null +++ b/tests/01_PW/087_PW_get_pchg_kpar_bndpar/threshold @@ -0,0 +1 @@ +pchg_threshold 1e-6 diff --git a/tests/01_PW/089_PW_get_wf_kpar_bndpar/INPUT b/tests/01_PW/089_PW_get_wf_kpar_bndpar/INPUT index 9833e7da833..e810ba0a24a 100644 --- a/tests/01_PW/089_PW_get_wf_kpar_bndpar/INPUT +++ b/tests/01_PW/089_PW_get_wf_kpar_bndpar/INPUT @@ -10,7 +10,8 @@ pseudo_dir ../../PP_ORB #Parameters (2.Iteration) ecutwfc 20 -scf_thr 1e-9 +scf_thr 1e-13 +pw_diag_thr 1e-12 scf_nmax 100 #Parameters (3.Basis) diff --git a/tests/01_PW/089_PW_get_wf_kpar_bndpar/result.ref b/tests/01_PW/089_PW_get_wf_kpar_bndpar/result.ref index a4417941264..89cd25c6df5 100644 --- a/tests/01_PW/089_PW_get_wf_kpar_bndpar/result.ref +++ b/tests/01_PW/089_PW_get_wf_kpar_bndpar/result.ref @@ -1,67 +1,67 @@ -etotref -203.8996350848297 -etotperatomref -101.9498175424 -wfi1s1k1.cube 15.53408854 -wfi1s1k2.cube 13.88248279 -wfi4s1k1.cube 12.07618293 -wfi4s1k2.cube 12.56532441 +etotref -203.8996350929646 +etotperatomref -101.9498175465 +wfi1s1k1.cube 15.53408972 +wfi1s1k2.cube 13.88248579 +wfi4s1k1.cube 12.0762079 +wfi4s1k2.cube 12.56531145 wfi4s1k1_wfc_fp_components 1 wfi4s1k1_wfc_fp_nx 24 wfi4s1k1_wfc_fp_ny 24 wfi4s1k1_wfc_fp_nz 24 wfi4s1k1_wfc_fp_voxel 1.919140625000e-02 wfi4s1k1_wfc_fp_rms 6.139453834168e-02 -wfi4s1k1_wfc_fp_power_0 2.019077861556e+00 -wfi4s1k1_wfc_fp_power_1 1.008593934742e+00 -wfi4s1k1_wfc_fp_power_2 2.661152084727e-01 -wfi4s1k1_wfc_fp_power_3 3.885797455412e+00 -wfi4s1k1_wfc_fp_power_4 1.232001753595e+00 -wfi4s1k1_wfc_fp_power_5 2.270953097645e-01 -wfi4s1k1_wfc_fp_power_6 4.552630852640e-01 -wfi4s1k1_wfc_fp_power_7 2.690420391410e-01 -wfi4s1k1_wfc_fp_cross_0_re -1.424657687089e+00 -wfi4s1k1_wfc_fp_cross_0_im -8.234172427052e-02 -wfi4s1k1_wfc_fp_cross_1_re 3.694861343575e-01 -wfi4s1k1_wfc_fp_cross_1_im -3.631558642313e-01 -wfi4s1k1_wfc_fp_cross_2_re 2.962066809499e-01 -wfi4s1k1_wfc_fp_cross_2_im 9.727956630712e-01 -wfi4s1k1_wfc_fp_cross_3_re 2.168938439364e+00 -wfi4s1k1_wfc_fp_cross_3_im -2.881238022710e-01 -wfi4s1k1_wfc_fp_cross_4_re -9.812917447141e-02 -wfi4s1k1_wfc_fp_cross_4_im -5.197619503011e-01 -wfi4s1k1_wfc_fp_cross_5_re -2.602989780532e-01 -wfi4s1k1_wfc_fp_cross_5_im 1.887658692583e-01 -wfi4s1k1_wfc_fp_cross_6_re -3.450029786543e-01 -wfi4s1k1_wfc_fp_cross_6_im -5.880351626178e-02 -wfi4s1k1_wfc_fp_cross_7_re 1.616174873993e-01 -wfi4s1k1_wfc_fp_cross_7_im -7.190943003698e-01 +wfi4s1k1_wfc_fp_power_0 2.019155944307e+00 +wfi4s1k1_wfc_fp_power_1 1.008552114608e+00 +wfi4s1k1_wfc_fp_power_2 2.661108024561e-01 +wfi4s1k1_wfc_fp_power_3 3.885874531110e+00 +wfi4s1k1_wfc_fp_power_4 1.231986575798e+00 +wfi4s1k1_wfc_fp_power_5 2.271122236067e-01 +wfi4s1k1_wfc_fp_power_6 4.552680486160e-01 +wfi4s1k1_wfc_fp_power_7 2.690593727247e-01 +wfi4s1k1_wfc_fp_cross_0_re -1.424655357949e+00 +wfi4s1k1_wfc_fp_cross_0_im -8.234748583495e-02 +wfi4s1k1_wfc_fp_cross_1_re 3.694676488310e-01 +wfi4s1k1_wfc_fp_cross_1_im -3.631532307505e-01 +wfi4s1k1_wfc_fp_cross_2_re 2.961760664596e-01 +wfi4s1k1_wfc_fp_cross_2_im 9.728067266287e-01 +wfi4s1k1_wfc_fp_cross_3_re 2.168938458165e+00 +wfi4s1k1_wfc_fp_cross_3_im -2.881860896261e-01 +wfi4s1k1_wfc_fp_cross_4_re -9.809732557572e-02 +wfi4s1k1_wfc_fp_cross_4_im -5.197846913847e-01 +wfi4s1k1_wfc_fp_cross_5_re -2.602975948365e-01 +wfi4s1k1_wfc_fp_cross_5_im 1.887911570509e-01 +wfi4s1k1_wfc_fp_cross_6_re -3.450116759282e-01 +wfi4s1k1_wfc_fp_cross_6_im -5.883093621080e-02 +wfi4s1k1_wfc_fp_cross_7_re 1.615823138646e-01 +wfi4s1k1_wfc_fp_cross_7_im -7.191411458503e-01 wfi4s1k2_wfc_fp_components 1 wfi4s1k2_wfc_fp_nx 24 wfi4s1k2_wfc_fp_ny 24 wfi4s1k2_wfc_fp_nz 24 wfi4s1k2_wfc_fp_voxel 1.919140625000e-02 wfi4s1k2_wfc_fp_rms 6.139453834168e-02 -wfi4s1k2_wfc_fp_power_0 2.210775444366e+00 -wfi4s1k2_wfc_fp_power_1 4.422482386207e-01 -wfi4s1k2_wfc_fp_power_2 1.311183666610e+00 -wfi4s1k2_wfc_fp_power_3 2.081129905576e-01 -wfi4s1k2_wfc_fp_power_4 2.107277316883e-01 -wfi4s1k2_wfc_fp_power_5 1.578952024869e-01 -wfi4s1k2_wfc_fp_power_6 5.694009207009e-02 -wfi4s1k2_wfc_fp_power_7 1.993940126276e-01 -wfi4s1k2_wfc_fp_cross_0_re 4.090872140258e-01 -wfi4s1k2_wfc_fp_cross_0_im -9.001995320913e-01 -wfi4s1k2_wfc_fp_cross_1_re 5.885539949613e-01 -wfi4s1k2_wfc_fp_cross_1_im 4.831902959306e-01 -wfi4s1k2_wfc_fp_cross_2_re -4.528984441414e-01 -wfi4s1k2_wfc_fp_cross_2_im -2.603024266555e-01 -wfi4s1k2_wfc_fp_cross_3_re 2.093553872554e-01 -wfi4s1k2_wfc_fp_cross_3_im -5.049778433485e-03 -wfi4s1k2_wfc_fp_cross_4_re 1.419206862935e-01 -wfi4s1k2_wfc_fp_cross_4_im 1.145923935805e-01 -wfi4s1k2_wfc_fp_cross_5_re -3.483374662841e-02 -wfi4s1k2_wfc_fp_cross_5_im -8.818830683746e-02 -wfi4s1k2_wfc_fp_cross_6_re 3.471382993746e-02 -wfi4s1k2_wfc_fp_cross_6_im -1.007395823315e-01 -wfi4s1k2_wfc_fp_cross_7_re 5.180925250259e-01 -wfi4s1k2_wfc_fp_cross_7_im -4.152053978252e-01 -totaltimeref 0.29 +wfi4s1k2_wfc_fp_power_0 2.210759823090e+00 +wfi4s1k2_wfc_fp_power_1 4.422431009108e-01 +wfi4s1k2_wfc_fp_power_2 1.311114257940e+00 +wfi4s1k2_wfc_fp_power_3 2.080940511216e-01 +wfi4s1k2_wfc_fp_power_4 2.107287456296e-01 +wfi4s1k2_wfc_fp_power_5 1.579032306523e-01 +wfi4s1k2_wfc_fp_power_6 5.694111320769e-02 +wfi4s1k2_wfc_fp_power_7 1.993934094083e-01 +wfi4s1k2_wfc_fp_cross_0_re 4.091016883918e-01 +wfi4s1k2_wfc_fp_cross_0_im -9.001828081491e-01 +wfi4s1k2_wfc_fp_cross_1_re 5.885509597910e-01 +wfi4s1k2_wfc_fp_cross_1_im 4.831552574574e-01 +wfi4s1k2_wfc_fp_cross_2_re -4.528490376534e-01 +wfi4s1k2_wfc_fp_cross_2_im -2.603129395829e-01 +wfi4s1k2_wfc_fp_cross_3_re 2.093462595369e-01 +wfi4s1k2_wfc_fp_cross_3_im -5.053907767741e-03 +wfi4s1k2_wfc_fp_cross_4_re 1.419227050410e-01 +wfi4s1k2_wfc_fp_cross_4_im 1.145979734554e-01 +wfi4s1k2_wfc_fp_cross_5_re -3.484498639901e-02 +wfi4s1k2_wfc_fp_cross_5_im -8.818737242535e-02 +wfi4s1k2_wfc_fp_cross_6_re 3.472128797451e-02 +wfi4s1k2_wfc_fp_cross_6_im -1.007378521678e-01 +wfi4s1k2_wfc_fp_cross_7_re 5.180876053465e-01 +wfi4s1k2_wfc_fp_cross_7_im -4.152061797410e-01 +totaltimeref 0.33 diff --git a/tests/01_PW/089_PW_get_wf_kpar_bndpar/threshold b/tests/01_PW/089_PW_get_wf_kpar_bndpar/threshold new file mode 100644 index 00000000000..145f85786f9 --- /dev/null +++ b/tests/01_PW/089_PW_get_wf_kpar_bndpar/threshold @@ -0,0 +1 @@ +wfc_threshold 5e-6 diff --git a/tests/01_PW/scf_uspp_bpcg/INPUT b/tests/01_PW/scf_uspp_bpcg/INPUT index c938c190ad9..9dd30779c5e 100644 --- a/tests/01_PW/scf_uspp_bpcg/INPUT +++ b/tests/01_PW/scf_uspp_bpcg/INPUT @@ -11,7 +11,8 @@ pseudo_dir ../../PP_ORB # Parameters (2.Iteration) ecutwfc 8 ecutrho 60 -scf_thr 1e-9 +scf_thr 1e-13 +pw_diag_thr 1e-12 scf_nmax 100 # Parameters (3.Basis) diff --git a/tests/01_PW/scf_uspp_bpcg/result.ref b/tests/01_PW/scf_uspp_bpcg/result.ref index 4805a3840b0..da7d2217bcf 100644 --- a/tests/01_PW/scf_uspp_bpcg/result.ref +++ b/tests/01_PW/scf_uspp_bpcg/result.ref @@ -1,5 +1,5 @@ -etotref -673.8349347374721674 -etotperatomref -673.8349347375 +etotref -673.8349349713665788 +etotperatomref -673.8349349714 totalforceref 0.000000 -totalstressref 66620.137229 -totaltimeref 0.97 +totalstressref 66620.229810 +totaltimeref 1.97 diff --git a/tests/01_PW/scf_uspp_bpcg/threshold b/tests/01_PW/scf_uspp_bpcg/threshold new file mode 100644 index 00000000000..b7aedbc7af4 --- /dev/null +++ b/tests/01_PW/scf_uspp_bpcg/threshold @@ -0,0 +1 @@ +stress_threshold 1e-2 diff --git a/tests/11_PW_GPU/scf_bpcg/result.ref b/tests/11_PW_GPU/scf_bpcg/result.ref index a4c2417f26a..d5fb85432ea 100644 --- a/tests/11_PW_GPU/scf_bpcg/result.ref +++ b/tests/11_PW_GPU/scf_bpcg/result.ref @@ -5,4 +5,4 @@ totalstressref 37241.465646 pointgroupref C_1 spacegroupref C_1 nksibzref 8 -totaltimeref 10.28 +totaltimeref 8.47 diff --git a/tests/integrate/Autotest.sh b/tests/integrate/Autotest.sh index 4f49b3ade9b..950febe09ac 100755 --- a/tests/integrate/Autotest.sh +++ b/tests/integrate/Autotest.sh @@ -181,6 +181,7 @@ check_out(){ stress_thr=$4 fatal_thr=$5 descriptor_thr=$6 + wfc_thr=${7:-$thr} #------------------------------------------------------ # outfile = result.out @@ -240,6 +241,8 @@ check_out(){ compare_thr=$thr if [[ $key == ml_desc_mean_* ]]; then compare_thr=$descriptor_thr + elif [[ $key == wfi*.cube || $key == *_wfc_fp_rms || $key == *_wfc_fp_power_* || $key == *_wfc_fp_cross_* ]]; then + compare_thr=$wfc_thr fi if [ $(check_deviation_pass $deviation $compare_thr) = 0 ]; then if [ $key == "totalforceref" ]; then @@ -384,7 +387,8 @@ run_case() my_stress_threshold=$(get_threshold $threshold_file "stress_threshold" $stress_threshold) my_fatal_threshold=$(get_threshold $threshold_file "fatal_threshold" $fatal_threshold) my_descriptor_threshold=$(get_threshold $threshold_file "descriptor_threshold" $descriptor_threshold) - check_out result.out $my_threshold $my_force_threshold $my_stress_threshold $my_fatal_threshold $my_descriptor_threshold + my_wfc_threshold=$(get_threshold $threshold_file "wfc_threshold" $my_threshold) + check_out result.out $my_threshold $my_force_threshold $my_stress_threshold $my_fatal_threshold $my_descriptor_threshold $my_wfc_threshold fi else bash -e ../../integrate/validation_tools/catch_properties.sh result.ref diff --git a/tests/integrate/validation_tools/props_cube.sh b/tests/integrate/validation_tools/props_cube.sh index 1dfd83270ff..68134aa6ffb 100644 --- a/tests/integrate/validation_tools/props_cube.sh +++ b/tests/integrate/validation_tools/props_cube.sh @@ -197,10 +197,23 @@ fi # 2. out_wfc_norm/out_wfc_re_im/out_pchg (PW) #-------------------------------------------- shopt -s nullglob +local pchg_tol=1e-6 +if [ -f threshold ]; then + local configured_tol + configured_tol=$(get_input_key_value pchg_threshold threshold) + if [ -n "$configured_tol" ]; then + pchg_tol=$configured_tol + fi +fi for pchg_ref in pchgi*.cube.ref; do pchg_cube=${pchg_ref%.ref} pchg_key=$(sanitize_result_key "${pchg_cube}_compare") - record_compare_result "$props_result_file" "$pchg_key" "$pchg_ref" "OUT.autotest/$pchg_cube" 6 + if [ ! -f "OUT.autotest/$pchg_cube" ]; then + echo "$pchg_key 1" >> "$props_result_file" + continue + fi + python3 "$COMPARE_SCRIPT" "$pchg_ref" "OUT.autotest/$pchg_cube" -tol "$pchg_tol" + echo "$pchg_key $?" >> "$props_result_file" done shopt -u nullglob From 6a5264d6e49e10dffa16e2bea3f4b644faeac771 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 22:19:50 +0800 Subject: [PATCH 21/33] Resolve CI/CD failure by setting a per-case current tolerance for `31_PW_retry_TDDFT` --- tests/05_rtTDDFT/31_PW_retry_TDDFT/threshold | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/05_rtTDDFT/31_PW_retry_TDDFT/threshold diff --git a/tests/05_rtTDDFT/31_PW_retry_TDDFT/threshold b/tests/05_rtTDDFT/31_PW_retry_TDDFT/threshold new file mode 100644 index 00000000000..a8bf1288dee --- /dev/null +++ b/tests/05_rtTDDFT/31_PW_retry_TDDFT/threshold @@ -0,0 +1,2 @@ +# Allow platform-dependent residual currents in the CN half-step retry regression. +current_threshold 5e-10 From 93b4e1edf36ddc0ff99ab7d5819b15123576db00 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Tue, 27 Oct 2026 22:44:22 +0800 Subject: [PATCH 22/33] Retrigger CI check From 1ad957b0b79552cb333a1128d3960a329b390754 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 00:21:08 +0800 Subject: [PATCH 23/33] Strengthen CUDA TRTRI and magnetic PW RT-TDDFT integration test coverage --- .../ATen/kernels/test/lapack_test.cpp | 135 ++++++++++++++++++ tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT | 24 ++-- .../05_rtTDDFT/30_PW_soc_onsite_TDDFT/README | 2 +- tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU | 18 +-- .../current_tot.txt.ref | 12 +- .../05_rtTDDFT/30_PW_soc_onsite_TDDFT/eig.txt | 85 +++++++++++ .../30_PW_soc_onsite_TDDFT/mag_atom.txt.ref | 26 ++-- .../30_PW_soc_onsite_TDDFT/mag_tot.txt.ref | 14 +- .../30_PW_soc_onsite_TDDFT/result.ref | 6 +- .../spincur_tot.txt.ref | 12 +- .../torque_atom.txt.ref | 24 ++-- .../30_PW_soc_onsite_TDDFT/torque_tot.txt.ref | 12 +- .../30_PW_soc_onsite_TDDFT/wfk1s4_pw.dat | Bin 0 -> 311860 bytes .../30_PW_soc_onsite_TDDFT/wfk2s4_pw.dat | Bin 0 -> 311860 bytes 14 files changed, 296 insertions(+), 74 deletions(-) create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/eig.txt create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/wfk1s4_pw.dat create mode 100644 tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/wfk2s4_pw.dat diff --git a/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp b/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp index a318bd8c6b8..8e402d3ee6a 100644 --- a/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp +++ b/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp @@ -4,6 +4,11 @@ #include #include +#include +#include +#include +#include + namespace container { namespace kernels { @@ -55,6 +60,136 @@ TYPED_TEST(LapackTest, Trtri) { EXPECT_EQ(C, I); } +#if defined(__CUDA) +namespace { +template +T trtri_value(double real, double imag) +{ + return static_cast(real); +} + +template <> +std::complex trtri_value(double real, double imag) +{ + return std::complex(real, imag); +} + +template <> +std::complex trtri_value(double real, double imag) +{ + return std::complex(real, imag); +} +} // namespace + +template +class CudaTrtriTest : public LapackTest {}; + +using CudaTrtriTypes = ::testing::Types, std::complex>; +TYPED_TEST_SUITE(CudaTrtriTest, CudaTrtriTypes); + +TYPED_TEST(CudaTrtriTest, InverseAndUntouchedStorage) +{ + using T = TypeParam; + using Real = typename GetTypeReal::type; + for (const int n : {1, 3, 7}) + { + const int padded = n + 3; + for (const int lda : {n, padded}) + { + for (const char uplo : {'U', 'L'}) + { + for (const char diag : {'N', 'U'}) + { + SCOPED_TRACE(::testing::Message() << "n=" << n << " lda=" << lda << " uplo=" << uplo << " diag=" << diag); + Tensor input(DataTypeToEnum::value, {lda, n}); + T* data = input.data(); + std::vector> matrix(n * n, 0.0); + for (int col = 0; col < n; ++col) + { + for (int row = 0; row < lda; ++row) + { + data[col * lda + row] = T(-9); + const bool selected = uplo == 'U' ? row <= col : row >= col; + if (row < n && selected) + { + const double real = row == col ? 2.0 + row : 0.05 * (row + col + 1); + const double imag = row == col ? 0.0 : 0.03 * (row - col); + const T value = trtri_value(real, imag); + const bool implicit = row == col && diag == 'U'; + // Zero stored diagonals prove that unit-diagonal TRSM ignores them. + data[col * lda + row] = implicit ? T(0) : value; + matrix[col * n + row] = implicit ? T(1) : value; + } + } + } + Tensor device = input.to_device(); + lapack_trtri()(uplo, diag, n, device.data(), lda); + const Tensor result = device.to_device(); + const T* inverse = result.data(); + std::vector> unpacked(n * n, 0.0); + for (int col = 0; col < n; ++col) + { + for (int row = 0; row < lda; ++row) + { + const bool selected = uplo == 'U' ? row <= col : row >= col; + if (row < n && selected) + { + const bool implicit = row == col && diag == 'U'; + unpacked[col * n + row] = implicit ? T(1) : inverse[col * lda + row]; + } + else + { + EXPECT_EQ(inverse[col * lda + row], data[col * lda + row]); + } + } + } + const double tolerance = 50 * n * std::numeric_limits::epsilon(); + for (int col = 0; col < n; ++col) + { + for (int row = 0; row < n; ++row) + { + std::complex left = 0.0; + std::complex right = 0.0; + // Independent host products, without calling the BLAS implementation under test. + for (int k = 0; k < n; ++k) + { + left += matrix[k * n + row] * unpacked[col * n + k]; + right += unpacked[k * n + row] * matrix[col * n + k]; + } + const double expected = row == col ? 1.0 : 0.0; + EXPECT_LE(std::abs(left - expected), tolerance); + EXPECT_LE(std::abs(right - expected), tolerance); + } + } + } + } + } + } +} + +TYPED_TEST(CudaTrtriTest, RejectsSingularDiagonal) +{ + using T = TypeParam; + const int n = 3; + const int lda = 6; + const char diag = 'N'; + for (const char uplo : {'U', 'L'}) + { + for (const int zero : {0, n - 1}) + { + Tensor input(DataTypeToEnum::value, {lda, n}); + input.zero(); + for (int i = 0; i < n; ++i) + { + input.data()[i * lda + i] = i == zero ? T(0) : T(2); + } + Tensor device = input.to_device(); + EXPECT_THROW((lapack_trtri()(uplo, diag, n, device.data(), lda)), std::runtime_error); + } + } +} +#endif + TYPED_TEST(LapackTest, Potrf) { using Type = typename std::tuple_element<0, decltype(TypeParam())>::type; using Device = typename std::tuple_element<1, decltype(TypeParam())>::type; diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT index 5910b7f62cb..231830bf5c2 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/INPUT @@ -8,28 +8,30 @@ calculation md device cpu precision double dft_functional PBE -gga_grad 1 ecutwfc 20 ecutrho 80 -nbands 12 +nbands 40 kpar 2 nspin 4 noncolin 1 lspinorb 1 symmetry -1 -init_wfc atomic+random -pw_seed 1 +init_wfc file binary +init_chg wfc +read_file_dir ./ ks_solver cg -pw_diag_thr 1e-13 -scf_thr 1e-12 -scf_nmax 150 +pw_diag_thr 1e-8 +scf_thr 1e-8 +# Accept the saved magnetic state after one initial SCF iteration. +scf_nmax 1 +td_scf_nmax 100 mixing_type broyden -mixing_beta 0.5 -mixing_beta_mag 0.5 +mixing_beta 0.2 +mixing_beta_mag 0.2 mixing_gg0 0 mixing_gg0_mag 0 -smearing_method fd -smearing_sigma 0.002 +smearing_method gauss +smearing_sigma 0.01 md_type nve init_vel 1 td_dt 0.002 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README index 2bc0417ecdd..6d117912bb4 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/README @@ -1 +1 @@ -Test nspin=4 PW RT-TDDFT with SOC in the velocity gauge using onsite magnetic moments. +Test magnetic Fe PW RT-TDDFT from saved spinors with one initial SCF iteration, SOC, onsite moments, spin currents, torques, and default gga_grad=2. diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU index 139b8c551ed..bfb07d6c4e3 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/STRU @@ -1,21 +1,21 @@ ATOMIC_SPECIES -Si 28 Si_dojo_soc.upf upf201 +Fe 55.845 Fe.upf upf201 NUMERICAL_ORBITAL -Si_dojo_6au.orb +Fe_gga_6au_100Ry_4s2p2d1f.orb LATTICE_CONSTANT -10.2 +5.423505388 LATTICE_VECTORS -0.0 0.5 0.5 -0.5 0.0 0.5 -0.5 0.5 0.0 +1 0 0 +0 1 0 +0 0 1 ATOMIC_POSITIONS Direct -Si +Fe 0.0 2 -0.00 0.00 0.00 0 0 0 v 0 0 0 -0.25 0.25 0.25 0 0 0 v 0 0 0 +0.00 0.00 0.00 0 0 0 mag 1.32 1.056 1.408 v 0 0 0 +0.503 0.502 0.499 0 0 0 mag 1.32 1.056 1.408 v 0 0 0 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref index a27bd87d37f..cb6e43ade7c 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/current_tot.txt.ref @@ -1,6 +1,6 @@ - 1 -6.847139035727e-13 3.153189564873e-14 -9.600988197277e-13 - 2 -5.747743492724e-08 1.230373526210e-13 7.787470761806e-12 - 3 -2.291372667484e-07 -4.370960711008e-13 3.327548964774e-11 - 4 -5.127178441324e-07 -3.987766754068e-12 7.337570647441e-11 - 5 -9.046018498177e-07 -1.398263237042e-11 1.248606891581e-10 - 6 -1.400015393069e-06 -3.450823362661e-11 1.837914207926e-10 + 1 1.667223544724e-08 -1.907183457471e-08 -9.789339696873e-10 + 2 -2.231802487713e-07 -2.065310142550e-08 -1.500332021533e-09 + 3 -1.206223312222e-06 -2.039502062807e-08 -8.713934652629e-11 + 4 -2.890823316261e-06 -1.945776271036e-08 2.640895051575e-09 + 5 -5.200804782617e-06 -1.977848103787e-08 5.599453551769e-09 + 6 -8.036923610234e-06 -2.365415764288e-08 7.512132076906e-09 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/eig.txt b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/eig.txt new file mode 100644 index 00000000000..543fdd8a5c6 --- /dev/null +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/eig.txt @@ -0,0 +1,85 @@ + Electronic state energy (eV) and occupations + Spin number 4 + spin=1 k-point=1/2 Cartesian=0.17000000 0.11000000 0.070000000 (241 plane wave) + 1 -103.9078833243627 0.5000000000000000 + 2 -103.8854764766162 0.5000000000000000 + 3 -103.2256496595237 0.5000000000000000 + 4 -103.1959453039100 0.5000000000000000 + 5 -48.93341254797411 0.5000000000000000 + 6 -47.95642695804461 0.5000000000000000 + 7 -47.36874565615719 0.5000000000000000 + 8 -46.46247354343243 0.5000000000000000 + 9 -46.17470988875323 0.5000000000000000 + 10 -45.91537808882194 0.5000000000000000 + 11 -45.65769953674243 0.5000000000000000 + 12 -45.08397573701200 0.5000000000000000 + 13 -44.57331936998738 0.5000000000000000 + 14 -44.42748857372715 0.5000000000000000 + 15 -43.84137557100731 0.5000000000000000 + 16 -42.87814707649200 0.5000000000000000 + 17 10.53396047873180 0.5000000000000000 + 18 11.30687387042842 0.5000000000000000 + 19 17.73964008910495 0.5000000000000000 + 20 17.96801319562652 0.5000000000000000 + 21 18.83289370704967 0.5000000000000000 + 22 19.18126072343309 0.5000000000000000 + 23 19.44489182879610 0.5000000000000000 + 24 19.83721119680212 0.5000000000000000 + 25 20.40163573729119 0.5000000000000000 + 26 22.05746081789694 0.5000000000000000 + 27 22.49028420913443 0.5000000000000000 + 28 23.98740818932617 0.5000000000000000 + 29 24.12410525745961 0.5000000000000000 + 30 24.39415331423380 0.5000000000000000 + 31 24.41696976212447 0.5000000000000000 + 32 25.50145054806425 0.4972978624366441 + 33 25.99187533487810 0.002702500630739632 + 34 26.24026477829464 7.220140136999545e-08 + 35 26.44769856398476 7.939029176750601e-14 + 36 26.65239203769734 0.000000000000000 + 37 27.49805904945316 0.000000000000000 + 38 27.67235947107097 0.000000000000000 + 39 28.55674943722372 0.000000000000000 + 40 29.58027259681982 0.000000000000000 + + spin=1 k-point=2/2 Cartesian=-0.17000000 -0.11000000 -0.070000000 (241 plane wave) + 1 -103.9078833560056 0.5000000000000000 + 2 -103.8854763904223 0.5000000000000000 + 3 -103.2256497240819 0.5000000000000000 + 4 -103.1959453193953 0.5000000000000000 + 5 -48.93342148595356 0.5000000000000000 + 6 -47.95642352154820 0.5000000000000000 + 7 -47.36868020094718 0.5000000000000000 + 8 -46.46255738822804 0.5000000000000000 + 9 -46.17468741564642 0.5000000000000000 + 10 -45.91542301367310 0.5000000000000000 + 11 -45.65760662269145 0.5000000000000000 + 12 -45.08410652846837 0.5000000000000000 + 13 -44.57312612224105 0.5000000000000000 + 14 -44.42760140975067 0.5000000000000000 + 15 -43.84137930297437 0.5000000000000000 + 16 -42.87813890569722 0.5000000000000000 + 17 10.53396130857515 0.5000000000000000 + 18 11.30687377574026 0.5000000000000000 + 19 17.73964028731759 0.5000000000000000 + 20 17.96801315983495 0.5000000000000000 + 21 18.83289274945842 0.5000000000000000 + 22 19.18125908409684 0.5000000000000000 + 23 19.44489465285591 0.5000000000000000 + 24 19.83721432500865 0.5000000000000000 + 25 20.40163810461241 0.5000000000000000 + 26 22.05745519750796 0.5000000000000000 + 27 22.49028479580295 0.5000000000000000 + 28 23.98740175831127 0.5000000000000000 + 29 24.12411601754403 0.5000000000000000 + 30 24.39412520558515 0.5000000000000000 + 31 24.41699316319984 0.5000000000000000 + 32 25.50146383668007 0.4972967920817186 + 33 25.99187285378963 0.002702700469629673 + 34 26.24027021292831 7.217973523159532e-08 + 35 26.44769374955129 7.941977293577905e-14 + 36 26.65238908228155 0.000000000000000 + 37 27.49806794541136 0.000000000000000 + 38 27.67235593055364 0.000000000000000 + 39 28.55671826770196 0.000000000000000 + 40 29.58027547094388 0.000000000000000 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref index ff611fba3e9..59debd4df00 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_atom.txt.ref @@ -1,16 +1,16 @@ # method=onsite; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign # onsite_radius(Bohr)=3.000000000000e+00; projector=q(k+G+A); atom_phase_cancels -# orbital=../../PP_ORB/Si_dojo_6au.orb +# orbital=../../PP_ORB/Fe_gga_6au_100Ry_4s2p2d1f.orb #step time_fs atom Mx My Mz - 1 0.000000 1 8.053904765326e-11 -2.590379299949e-12 2.695932525831e-09 - 1 0.000000 2 -1.374186042735e-10 3.752895563758e-11 -2.670477963762e-09 - 2 0.002000 1 -1.733702764684e-10 1.959491596759e-12 2.764673004418e-09 - 2 0.002000 2 1.233804308720e-10 6.657203954763e-11 -2.648813383910e-09 - 3 0.004000 1 -9.423045815860e-10 -1.784173156438e-11 2.872179761387e-09 - 3 0.004000 2 9.030423075984e-10 1.221199252832e-10 -2.681691077533e-09 - 4 0.006000 1 -2.228605938681e-09 -6.084489509450e-11 3.011140913001e-09 - 4 0.006000 2 2.204617974289e-09 2.051458551644e-10 -2.775666130966e-09 - 5 0.008000 1 -4.035709946781e-09 -1.259454034119e-10 3.175208544010e-09 - 5 0.008000 2 4.031702471874e-09 3.163418381413e-10 -2.935733298820e-09 - 6 0.010000 1 -6.367397373253e-09 -2.121199257910e-10 3.359621560117e-09 - 6 0.010000 2 6.387599005286e-09 4.559797643633e-10 -3.164769165481e-09 + 1 0.000000 1 8.853507019723e-01 7.101962618766e-01 9.388102141133e-01 + 1 0.000000 2 8.821497466354e-01 7.074788394935e-01 9.355889220868e-01 + 2 0.002000 1 8.853506747607e-01 7.101961385004e-01 9.388103355654e-01 + 2 0.002000 2 8.821497160899e-01 7.074787186621e-01 9.355890397806e-01 + 3 0.004000 1 8.853506427236e-01 7.101960208834e-01 9.388104625409e-01 + 3 0.004000 2 8.821496772577e-01 7.074785994255e-01 9.355891578494e-01 + 4 0.006000 1 8.853506042325e-01 7.101959111672e-01 9.388105971534e-01 + 4 0.006000 2 8.821496261681e-01 7.074784821072e-01 9.355892759383e-01 + 5 0.008000 1 8.853505583444e-01 7.101958112816e-01 9.388107409538e-01 + 5 0.008000 2 8.821495595372e-01 7.074783668488e-01 9.355893931777e-01 + 6 0.010000 1 8.853505065629e-01 7.101957217336e-01 9.388108941318e-01 + 6 0.010000 2 8.821494766630e-01 7.074782524705e-01 9.355895075212e-01 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref index 37d5d6f66fc..04b71ff006b 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/mag_tot.txt.ref @@ -1,10 +1,10 @@ # method=onsite; total=density_integral; M=ABACUS_Pauli_moment(mu_B); no_electron_minus_sign # onsite_radius(Bohr)=3.000000000000e+00; projector=q(k+G+A); atom_phase_cancels -# orbital=../../PP_ORB/Si_dojo_6au.orb +# orbital=../../PP_ORB/Fe_gga_6au_100Ry_4s2p2d1f.orb #step time_fs Mx My Mz M_norm M_abs_integral N_elec - 1 0.000000 1.214529056807e-12 1.353857486371e-12 6.594936585360e-14 1.819989077464e-12 5.655579705540e-09 8.000000000000e+00 - 2 0.002000 -8.743307074901e-12 1.201800440839e-12 5.532947808767e-13 8.842843322154e-12 6.304278379745e-09 8.000000000000e+00 - 3 0.004000 -1.869532759260e-11 1.050334617645e-12 1.039838784347e-12 1.875365941092e-11 7.463987294102e-09 8.000000000000e+00 - 4 0.006000 -2.863535903604e-11 9.001715900077e-13 1.526237762642e-12 2.869012892482e-11 8.924820380402e-09 8.000000000000e+00 - 5 0.008000 -3.856000509796e-11 7.514442616677e-13 2.012452748025e-12 3.861979580081e-11 1.146611739707e-08 8.000000000000e+00 - 6 0.010000 -4.846632928944e-11 6.050931672159e-13 2.498409941345e-12 4.853445440888e-11 1.690400989176e-08 8.000000000000e+00 + 1 0.000000 2.393521343807e+00 1.918240587757e+00 2.541936337072e+00 3.983720838300e+00 3.989052149093e+00 3.200000000000e+01 + 2 0.002000 2.393521277797e+00 1.918240275644e+00 2.541936634873e+00 3.983720838372e+00 3.989052149265e+00 3.200000000000e+01 + 3 0.004000 2.393521211784e+00 1.918239963548e+00 2.541936932663e+00 3.983720838443e+00 3.989052148929e+00 3.200000000000e+01 + 4 0.006000 2.393521145768e+00 1.918239651473e+00 2.541937230437e+00 3.983720838514e+00 3.989052148163e+00 3.200000000000e+01 + 5 0.008000 2.393521079752e+00 1.918239339415e+00 2.541937528199e+00 3.983720838584e+00 3.989052147237e+00 3.200000000000e+01 + 6 0.010000 2.393521013734e+00 1.918239027362e+00 2.541937825959e+00 3.983720838653e+00 3.989052146436e+00 3.200000000000e+01 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref index 3da526f03f6..947946ac86e 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/result.ref @@ -1,9 +1,9 @@ -etotref -218.3114043373177 -etotperatomref -109.1557021687 +etotref -6386.641176765657 +etotperatomref -3193.3205883828 Comparemag_tot_txt_pass 0 Comparemag_atom_txt_pass 0 Comparetorque_tot_txt_pass 0 Comparetorque_atom_txt_pass 0 Comparespincur_tot_txt_pass 0 CompareCurrent_pass 0 -totaltimeref 1.74 +totaltimeref 0.95 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref index 802b7dd5d90..2e8dae631d8 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/spincur_tot.txt.ref @@ -1,8 +1,8 @@ # S=M/2; unit=hbar/(Bohr^2*Hartree_atomic_time); flow_index_first #step time_fs Jxx Jxy Jxz Jyx Jyy Jyz Jzx Jzy Jzz - 1 0.000000 1.267919086754e-13 5.145198692921e-14 -1.931316544630e-13 6.015257225557e-14 -2.843939266804e-14 1.241297853503e-13 2.242746640684e-13 -7.321375110977e-14 -2.773781395967e-13 - 2 0.002000 1.265191674762e-13 5.027723637210e-14 -2.111502018772e-13 6.301063841080e-14 -4.353736297079e-14 -5.019406639149e-13 2.238088766720e-13 4.769497155670e-13 -2.840764609462e-13 - 3 0.004000 1.241979771888e-13 5.005964562469e-14 -2.287128140430e-13 7.384543326544e-14 -1.308171867044e-13 -3.695184459769e-12 2.204639195477e-13 3.250686596645e-12 -2.343787538517e-13 - 4 0.006000 1.199354674595e-13 5.051304223258e-14 -2.457402062605e-13 9.362456624868e-14 -3.694391656904e-13 -1.208259910631e-11 2.112346292449e-13 1.054715434315e-11 -6.400333836950e-14 - 5 0.008000 1.140969671213e-13 5.136125791370e-14 -2.621834659735e-13 1.205675153723e-13 -8.524092920212e-13 -2.838206911878e-11 1.967367985310e-13 2.478945666354e-11 3.050166876471e-13 - 6 0.010000 1.048518041856e-13 5.354724230295e-14 -2.781142930194e-13 1.494849350185e-13 -1.691153805006e-12 -5.537884770040e-11 1.803633849414e-13 4.851983981969e-11 9.702624997229e-13 + 1 0.000000 -7.602185574873e-09 8.593057712214e-09 -1.263186510480e-08 -8.145880523770e-09 1.946906005781e-08 1.430565358403e-08 -3.358093369857e-09 2.918986468760e-09 4.374482462116e-09 + 2 0.002000 5.705694443641e-09 1.935388319769e-08 1.644694083772e-09 -6.994933104519e-09 2.036532161758e-08 1.554332632098e-08 -2.970479362501e-09 3.255664140579e-09 4.783326800826e-09 + 3 0.004000 5.329709056954e-08 5.757504927961e-08 5.234332894293e-08 -5.334943694092e-09 2.168610329214e-08 1.717896226127e-08 -2.535982368921e-09 3.735577971231e-09 5.244810202112e-09 + 4 0.006000 1.338779952304e-07 1.222087563349e-07 1.380878722254e-07 -2.917673966995e-09 2.361350584819e-08 1.942364137901e-08 -1.970582465381e-09 4.515568020639e-09 5.866643223112e-09 + 5 0.008000 2.449649740174e-07 2.112568941935e-07 2.562414689176e-07 6.758340209514e-10 2.645913225180e-08 2.270178821134e-08 -1.118231684103e-09 5.785378037743e-09 6.831186208857e-09 + 6 0.010000 3.832047679173e-07 3.220277360726e-07 4.032469121097e-07 5.995959475555e-09 3.063570032078e-08 2.760236926138e-08 2.273116647381e-10 7.753618287229e-09 8.372038643110e-09 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref index 3ae96b8f797..15aeb161fb0 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_atom.txt.ref @@ -1,15 +1,15 @@ # SOC_pseudopotential_atom_decomposition; unit=hbar/Hartree_atomic_time # not_the_derivative_of_atomic_moments #step time_fs atom tau_soc_x tau_soc_y tau_soc_z - 1 0.000000 1 -3.009732175824e-11 -4.108455146649e-13 1.592854534076e-12 - 1 0.000000 2 -3.013959072163e-11 -5.077888556064e-13 1.361201150127e-12 - 2 0.002000 1 -3.008101003142e-11 -4.060717081249e-13 1.589232899020e-12 - 2 0.002000 2 -3.013862395211e-11 -5.111015811344e-13 1.359790199913e-12 - 3 0.004000 1 -3.000253710448e-11 -4.006229281343e-13 1.580779967604e-12 - 3 0.004000 2 -3.016147570840e-11 -5.124207604108e-13 1.363333315015e-12 - 4 0.006000 1 -2.982498689633e-11 -3.939800790217e-13 1.564088146610e-12 - 4 0.006000 2 -3.025383100391e-11 -5.112950942815e-13 1.376052808984e-12 - 5 0.008000 1 -2.951828189043e-11 -3.842421478030e-13 1.536275135384e-12 - 5 0.008000 2 -3.045570157440e-11 -5.084908527962e-13 1.402227800791e-12 - 6 0.010000 1 -2.906499726380e-11 -3.679581121867e-13 1.493728904225e-12 - 6 0.010000 2 -3.079180567089e-11 -5.073317593587e-13 1.447025652201e-12 + 1 0.000000 1 -1.881970328497e-07 -9.309314936335e-07 9.547393774714e-07 + 1 0.000000 2 -2.115217317654e-07 -9.592262877870e-07 8.487126132886e-07 + 2 0.002000 1 -1.882056398037e-07 -9.309067155825e-07 9.547401976288e-07 + 2 0.002000 2 -2.115473358855e-07 -9.592169193106e-07 8.487097034251e-07 + 3 0.004000 1 -1.882106057428e-07 -9.308858680836e-07 9.547407353859e-07 + 3 0.004000 2 -2.115761487202e-07 -9.592031904027e-07 8.487071020804e-07 + 4 0.006000 1 -1.882128749321e-07 -9.308706490943e-07 9.547411441746e-07 + 4 0.006000 2 -2.116056008691e-07 -9.591888396507e-07 8.487074644192e-07 + 5 0.008000 1 -1.882142905166e-07 -9.308595806842e-07 9.547407223264e-07 + 5 0.008000 2 -2.116302286989e-07 -9.591793997900e-07 8.487125619965e-07 + 6 0.010000 1 -1.882181317617e-07 -9.308472706832e-07 9.547378595347e-07 + 6 0.010000 2 -2.116427322731e-07 -9.591821153713e-07 8.487231376542e-07 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref index 5a13499a2db..d1fba354c19 100644 --- a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref +++ b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/torque_tot.txt.ref @@ -1,8 +1,8 @@ # torque=dS/dt; S=M/2; unit=hbar/Hartree_atomic_time #step time_fs tau_xc_x tau_xc_y tau_xc_z tau_soc_x tau_soc_y tau_soc_z - 1 0.000000 2.426514743516e-38 -6.124579624877e-38 -1.069739881488e-38 -6.023691247987e-11 -9.186343702713e-13 2.954055684203e-12 - 2 0.002000 2.422536136501e-37 9.349947222335e-39 -1.494907395682e-38 -6.021963398353e-11 -9.171732892593e-13 2.949023098933e-12 - 3 0.004000 8.495004323470e-37 -8.238992634422e-38 1.930734755893e-38 -6.016401281288e-11 -9.130436885451e-13 2.944113282619e-12 - 4 0.006000 9.953905345459e-37 -2.330657888322e-37 -1.101676296253e-37 -6.007881790024e-11 -9.052751733032e-13 2.940140955594e-12 - 5 0.008000 1.543717346039e-36 4.410489552227e-37 8.114869589385e-37 -5.997398346483e-11 -8.927330005992e-13 2.938502936176e-12 - 6 0.010000 -1.719112621135e-36 2.527790279275e-36 -2.394175326990e-36 -5.985680293469e-11 -8.752898715454e-13 2.940754556426e-12 + 1 0.000000 -3.510254781217e-18 5.880494794026e-19 2.832312219438e-18 -3.997187646151e-07 -1.890157781421e-06 1.803451990760e-06 + 2 0.002000 -1.410431426153e-18 -7.427038165335e-20 1.538219221622e-18 -3.997529756893e-07 -1.890123634893e-06 1.803449901054e-06 + 3 0.004000 -2.306964507027e-18 3.602498050136e-19 1.776139515398e-18 -3.997867544630e-07 -1.890089058486e-06 1.803447837466e-06 + 4 0.006000 2.482140430757e-19 -2.221538828401e-18 1.000716467945e-18 -3.998184758012e-07 -1.890059488745e-06 1.803448608594e-06 + 5 0.008000 -8.732496375496e-19 5.656086023583e-19 4.135207780210e-19 -3.998445192155e-07 -1.890038980474e-06 1.803453284323e-06 + 6 0.010000 1.487624396363e-18 3.642230911274e-18 -4.028771362904e-18 -3.998608640348e-07 -1.890029386054e-06 1.803460997189e-06 diff --git a/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/wfk1s4_pw.dat b/tests/05_rtTDDFT/30_PW_soc_onsite_TDDFT/wfk1s4_pw.dat new file mode 100644 index 0000000000000000000000000000000000000000..1c8e2b9a6cb4f980127937607eda2aa5d9024bde GIT binary patch literal 311860 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%0ASfX?#4y z7I=APK%E*|mmD}e-6C_1V`uT?~&7d zSVQxMqo9@vZ4(5C+4Zl3wp6%`yGlJKdWbIE`6>YYvY)6D4oIT3Yx{sCeIax!dt|Aw z#iGNdSplo>te~)3{KE2v2t-`RHO1{hc;n0UcYk?2O7joB3rc%GL?_B6A8D;l@OWl}50s1M-x`@GN^oC|w3 zhD?fXM50Em!__$j1!BL}J+sX%kIQM4ryW!BVN@eubpCua+Bht5P-HO@^URCkTJObi zjY(Qfjc1dzNc-=c*)K{w8>dw1Cqnq=3LiTh6eRll`cVl($;`msO!swDTMMoH`9Bu4 zw?m^_++~^R)5t;2bXx2^KYp3|EO+XiIi4;GJ2^3P8tR0{3Wto+@j$KB_~Bt1oH?)T zY^N-aLZPPK`}UTAQ9Siy;>L_T4&MUzKGlPyE|cOTwI+l&{+`{_OFeLU7rBwba1(xc zD561?%LfN`UAUR_L;*Uj_1Gy-65g*rLGwT6YS4j&>jA%&I7mem$8`@-;3vK-oE0{C z@Y;`Ym)7BEyw-cO*Zo^OFx{1kGH3Niwy}rLbWK?B9)$`^!3|-6aaO94<85$ssjJJQ zn3#*{P5ML^2tv7;PkX<<7@kcvDq9&l4{hG@2YNn~;_TOBF#eqTZC-$D=y#hq>6cZD5(#$SH1;HCq&Y;r;cT?(M* z_b<-&I|NrSGsd&clN_p4OquRDE5g4Y219ztH`0K-?*&KYxuaN<8&m(o zD;4~Djek1dJ&KM_Oks`-+~6rS+hg=l2iUX^U2^YkgZ&jj)uaZ(>-;inki$_MUeYID zv%h5wk2?0~UpRh_==pUCdEYBRYBw9EHhpFc-LRbd+9Ckj2hUcdQwV`s0zoyCQGxSX z_f8nK6vLcX&0KMHB35fmn@bO}gV|G6rlmN25U}#1o88C(YGXS-=FD zG&UX&HMyb4V2Jmmvn=c~8Ql4}qzXTsC8%qzWI@1$P)mtqB<3_`H>=kRf#gx-4>TdxF5PaRu9SA1CW6yaOP!49Jt1ZybC7tMePQ!PaL(($g;RI`0^Ydo}{_;TZriG za*AWklc1xR%{L)b{5T$s-+5}$NBh7qO_j#)33~XD)tmKUhzF)ljt4tt5j^JH|NcJF zH3CM(grw+%ZBm1BY=X!p6~UAKBgKE6=mF0}Rz=waV~uvFtll;yI7S^$02UDsOctZzxhhi?<2FUxf0u=n->% zODmZtkz`1=W^+0&#|ZPTj(V>vwE!pH4CzK~3IN4OL}cKt8JRaP7CG2iLji*9ktuP)z$SO~-sJcoxR_Em8)*S3Ft` z@ZAG@M&B;XnB0JQw<=%P^>f%dBP=N3q>8c?X)E^{^)UQb%7r^+=K&8FmfF{pVlAW6 z{Xd=4Lmim`4_Ulr5h4!@$gJ7p2IrFjt&;%Zmup0$#28@OkvsS>J1^c|tKF-<|2PON zOZ|Rs;)S*qUu;r$y%?$wf6L}Y^elh7q%AvC`9bU%=O(pp=Gd=eP94o=f(P^sBHf+j zVZr$%CGAB%)GzG0L$1t;xgo=>m)Hy7eGa{$wSEjHQOtJS&fEjtn>Vg1Z5yJ^gK6b& z#?_#=_bu0vfeZ{@qvw_?M?@!zNI z@W)O3K&+J`3#U#4UEWhoO6yE~6j7c?N+7{D+3!d8mki)+-^=$ZepjG=^0PCySv9_0 z5V3prTmek({o5?akcEi%Ho$*@n46Iq&8lW*AUP##Oqk7Y(vCN`&*GiAp`|RXA#I{3 z$eTH}ol5vvXe6!ne4Qu!uO6R;e$a6ec{E!FnCXDL(zd7XJ8E!K*N^q~K^`1t6FhJK zTprq%JC8@7zYa$Wl)Ow*>hOh1)lSZ5Hk?YyVL$6(h9y4=qb|4?Latuv^|Wvz-zNP> zo1I=5!l`c+Gl{i2*Uv6&O{K35 z-6Q|=FIqGjbc~x$K#@0*zY@5!%Z(QdAMrR{+tLP&N)DD=WA(7KIn~AJl#h2Vwx9TZ zTN^6)Z*#;vHh_+$s>G{BXW%olIsHew98_$(X6d&6fs`il^@QdvarmjVGO@Rd=>4%T zUs#gz#Lu%vk(>WSVSh=o%$pWDxbg1b$&8Q_@MA$rUMDdM=i&uS*@=EaqU_DQzk!0_ z_K#g#YbFt{AG&D}Efj$EbZKi>yLL!!MJpD@SK094KmdR3?^7^5AxVKm|Cna-ZOhja ze~0+fsz{A@L@|Gx$>@r90=%fE;rL1Tc*>aNw$5$|!oILvSidR&iz~Mm0xz70d!G%S z|EfEM&QTkllHI~E6!nPmg18CX-YdqkS8o75N$y_DUv0sn?@#ys=@Y^$pISJ6=W@WS zeTg-cvrWKiPbY9;wh-ArlFQ{N(8A;NGh^+y`B5bC#je$Nyg;RtBA3`F4;P<+P~}7o zjOM=YOrZ-^+1W0QmA}0XM%LEju#frOK>jyh{2B9@ zxspi&wlA7*%;XdAs~4O`bmk4P6|tUFCOhHaLQ3Xm% zS9NytWn=J%__z`G4U&x8R+8x+X<*eJ5Bfv+BEvXXj=_~!R2ZTU-HM_D@|`cL-G7y! z_SJQ#J4OxQsmEPYn0OMIyMDzu>#Pm6^J=WwM)AVH50>q3vDqMc`t6}!(Fhz~b`aX! z&VXU%GDG!$$MAaN`q&P!$3K23#n2^Q66STQ8vV|s0E6zzz5~udc7GBa8{}rr&J8R^UOvh#M5$lh*{_b4ZKHt@Ue>nmT zq?PwGEU;r~lK8!zzjCPefkgFSAqpj!b&j$Nx`Y2$`k~iX4grsmkIwS&Zs;aIFaNQJ z@Gp{`fq+XS9zS{bEH8irVBx)4xMewFj*~Nz@aCvzI3O^K#MR&d9O9--YqY$5ifB2?=~K zJ33prmQVPqfz0e=48AFtJ;eSv1-@n+oh}*=#6WYFr?kbNh8n;6|9HN5`>;KFJTSCMOHh`qLbq1uR|F}#(gHC28`kjM{se(XPW?}Wg zbnMvB>DFJCMf0C`eNs2IvG=B`hxd<2D4z~=pw@RquJTCDnD}(~tQvW0^jk18J>dJc zRVD&5Pn(`Efi&(tHZWekrwIZ%ejPbmR)C++ah$UO&nZoHNGLI6t<&b)bfjHmN9+S*TY={J|Oyj|~WS$t< zx}n;kOp6?HJ*#{>qEP8}xQOR@3&ifz9}#|Bio!bc1OBqoKsh7+;^hVtJUzYm!{c!) z%{NIF82Mro`^4!W#b)ms zI;Ftnilp@MvOwhLwOe#yV21XG>@gPi4j*vJz=WRL?0pA zUVa^-2Y<%SVu?Ntq92S4PyRWA6BZd z{GP@86!)Sqz@g!$mW@3`U-#^OPk#x_`Y*YzULS`8*Q|>D&ZBqLZ&#xidS2_obn3zG5)4}j;SR5E1=XKzktAw9X z;XXPE(eNSz!lyJ&Lj{xnbXtQ8*k(puu<=pC0|(AWPtayTm{YYwE@vy)WGqGd-q(ko z8Zqn9UI7?<^4_XDF&gM*h8h#xD`EVE@jO;Wf{^>Af!6^92jGcT-mrl;{Kq<;wyA4@ z%Qx3~E6;BhY` z@+Y7EJCj*BCIWwWvZY3d-)~;=QPkyq8MGbBW=lOL0iWOK@p=kNp_4deq(Y`Uo;#C! zc-kZr6W_bif2i??qmhuU`%DYfrWZbFJ?%ybkzID0CeN|z?EZUH5l+Z*o+6EGz!t1) z&FQ~PAA#eJ9hYU^roreW4RvdS5XPo2Z(DQk3_Yu=t9WtsFti;sla+m>hQn%auk26s zg>IU2rlVaMga@ea?ZK=gP!>g#HE3#wb#{$!Y15rSGl+jy%`Y8(sQuQOv?Yh~%p(4Q zf83z;=fdTQ@^Dx>ed1L1)e?~NlRj|VPXM=70vYeVdPz^GxRuJm+MZ3iULxl)Mk~ZQJe-9yGZT-jUkf4) zx$knl6AgS0qd)helLUL`j#QWb^M||g-oIcV@EZ#3^xNdiG=P$`3k6A%fn(Qm5Coi79*bPG~tv?_T(SM zATUq(+wiN+2D^^QNRj{7fX6=hu!(66V|+iq$zYfYu&9M)v}}JEvbyud%quSzQy$O< z8QK$^bB~mEb|m`jgBC{L4C>LM^}aDydO(W)eVqs4dMNgcT55kn1>t{av`e$Pj;$=W zeUFzMhJfyErT50N_&B8h%bq(4xXCDdbdI?iZ&;ie*7}!=<3ah%XOdcozkj9epKWXC ze#Jg-zb%j9-aqqGMzYcDsm&)>?PkLJkV^C6QyfldF-viDm*Z9aJcgVeTlm!{R1>oZ z#CvGh#vX5?Kk`H<;N*W7FmW)A`Glo2l!X~aXv%6~W)}W-x|oZ(s}8Z$ime!Q%)VsEeFg>0R4ySqL~7)jG{xUi%i?{u(>f1Evx#{V=g zr!Fx9U222?^Kb&jf3#U0JX44DuO41J2 zRvl)tLaT!=N@6Qly1d+k1859h(>6Lo_yP|Y7-}bJ;Q9{}a+!B>V3pzW+|kJpJnjm$ zXj-|!hcS40UeX$GzB$zuOw1>Q_6(i7OJ9m4(Tk^})H?B%`JpsdJx2)Q%qj_Tq?{?tLjVeywoVG&KQ8^ET7hm#d&PFFs|@tTV2q zo}fEiV+NX|Usf!>=7a0-`@8kDm!acvgv;<;80hiW&5$?Rv<0nV`CvCrr+qq=?Ylge(mz1$_rJl#5<_h8N9=9D$r^z?YqhF1cJomYACMQqx9&sz4>FpGv4?|y}PRf2D^A2^?r7M z=;TiCii9|XMC}=P=^_V{zR@`%3PF(Zx@!N`D(Vk^nY@Ge-L9^)q#P2+N7n} zXgq&exZ3b)DY6IOw%&Wr3saOPeMUO%!H|pYv)zmy2K;JbG$ZyW!8T=jiW5S}+M49L zL`es9@`o?o9SVbZ*IbJowF20@Vw5r{YK(lJzO+*qD#ObrvXw7Xu^?dn{@>o@3K(?0 zvtw=TgJwqmat=&cz#G<2DXnA~V1Il0Z?a7j_w{w8qMTwg$>? za*#df{Wq*R7LJ<~?5N&44beHn97f-*fW)*Nytbl-hUUtA^%6(nTv6bs|GEkwHG1$- z;a77w8FpA}bH6$s`qs=B#uo-p7TPHB@joP&)sa!zI393&_$p!8&=U-k zt76?+<3Zm2=UIwgKS=3ZjCC8aLD_9to|`Tuc#(OV6ztxCa^L6Q?H=?cyl5vJ$4?uf zqI1Y2YX3CMa$YeAPisWUq9!$7EmO1%wtg!_c}NaXVLFf_43H~8<6bcaD6xGhs+^guQO1^NS=PA8XOCh+krxRydC3RE!^|!x#vb)0;*JGhg6@c!CNr1c|%45 zJ#s3Q&z|G~qYKtaK_qMNx%|!c^0F3s2ZkS9^GL(-UFQu9sOmAvZTE^hpE4*773+3u z@Z+_1)sy_k!_i=Fy%ij63|kFhWp2js@tyN_^%3z zw-GLAe!g!*tyToO+#N2Et%u;u_G0Z5EXC_O!KZ4Nt?+W|{f}RmMS%a?#JRqWRFp2S zrsHUB!WE@g)jokou+8+$OD$Uig5&<jOFbdtV6mWI`|1%KAA8DK86C-t&;e*47y)xnCOCK6)6m3Hd<(QQx)F1{b{D zdbQ9%K_A9CJ~GR{RfEZ05&pIzfl!f9*5d>p9|JsdF3IviEpy`$)q*urZb|;x;(N*%Zlb@L z@|_-|yTeNSc=Dis$onjkc$5nUE$aV1Mgf;0qbF5iEIBl zVT6`qSD2~~==Pq9h}E>f&ml!h#jn%B%}GT1n`9F#sFEGZ%Qc0Tf70&D@=8eayx2;* zT^|*U1SW29%7g!LA*Pi*=`dX#6_K{k03)9`Z+Lv*1nN2Q#0QdcSV(a=i(x7idTk%Z zn+i2Rvg25*WvBtTwU3IYy^$mKI#P4hbg_{7B5hec{WLI3If*jI7(f-w_t?GX#q7M5 zfclbj2oQ{vp`~qxUm>o=)uvIXBqjPpC71XfXRc@HN@hSv%7;`%)pHQ&Ny^c?D+_Up zY2gYg;_!CE`oflc49M41-F+E$8r=UbJ%5-Mi+e1`&ZaZvgR}Sow)pd-?P-S5E?-74 z8Rtu^6s*U?cR0Tbb9I7}-7Lrb5(T_4ub_OPOBhEMS7|1KYq8{*N$76X4%qU_p9&LE z1kR7z!%6~jkSz0g{ETHhauk=0ocoav%uoF?IJLF0LNZyN-i85Ry^^trBYIh^%gpkg z*$vPTKasd{icq?Wy=|OVw8TO_W?I1Y)N%QTi;pmzNEh(Cb&Bwdzuqt6PULDRYMSYJru31$ zBlOwMUp{G%R{gfi4vWo-=EX#5T%3o|bZ%FkgIU>g6>!X=i4z7uIPs>2$-#kMF zEy1_Q99JG|mjPvy_KW75QTV;bDY-}}3oc5ByW&77=3YA()XPuIH)@7D+nB5&>4VqO zRgi~0G(Rq#dQghVp1(Opqncn}tz1dnK}&d5_Q^}IOdnE=)Q3J_JVkJA{6Z~-n&G(Y zo$Q%STD;vGXt0pL00(qr#$J^XJO`~`GMpa7^Q-c2O`ADCu*E)DZT9^&H1MuCqUD$u z9Ac(f-*+V(39Ns$;XP~UvRzZD?RIOn5fbc z`-i-uDf>UKI3S<*!b$&|;UJME+;6^*8jaYf{$&r*W9V!8#;ycS@MalSvNqy|7hd-~ zmw$U;i(t6R(tpRm^_1ne9~6%8-IgTkxTywVyO`fg-1b7voH;G3ieT8^b0d|(;Mb7j z)q6WbPZbh(SbcSnOw`9(?6VF0@ZA{qBtB;W|KITnb^!>z*scFp+7@7jC;R>H719Z-hSQfzdo!H0y`M$tq*LrcJ9E#D^zD)XPdI_y+} z+6Nl{T%2@-9GMT@jg;CD=Xk_&*U4&-Z>bj4e0CWP8J3bg3E!dG4f;*qKL_EJU%0?< zPZV4a5_jsL%0|y3L+y5VsluQVl-yDk*+=9aE=5cj zTADXv=&CIlPqP|)d9OE2yD9|xPu^8gZi|A%vcqkX+$Zn{Yx8=UU>eB0ymlx!tPESc z_fdb?1_-Hp$}|@)2{A^V8T|>#aQXLv2YpJV$ma8Q+~-FauuR7`reX#jIb!DdiJTf3iInf|T_z|oZTp#n{q2JecUG++UmHB%fX?pqS(4eacyd#9Ck=8Y_gy=9us{y?_%Q*3sG#Bl?wFm z>kszzuTmn|A9VEHDeH3R=1~kVdTpxiM?JHJ&qa`gCN;2ey&|BF>+X#@!E+b&F1K4Xqmg z)3th`gnQQ#hW~xEfaANHi<607L}gtQ(;~qyT5TTYl>bx+!GE;E4}7Y}ytF;a=LJn* z#@-L>o10?s-+PU7gb^DWyIKVR0e!T@}J}s0C~se{-L%gT%BdL+|oS( zdb5S+nsi=ADg_hyhj{}~ zq7{OES`(wKkwm<|QU2{;e?H-jx%Y!RTnR3YPM%%XKLktvywCKRCE>$i%@?_^@_}(? zDp+`VA54pna+xo2z)hWl8m|=Oz_+uyH$sjR${s5#7n>00#j7Oy&l5$!dQ~Iztey_+ zVNaU#GF1e+FI&{~yGrnw;O_J-+6FM{`?u=2EsH#wm}#k|=36nsj<>U#cr z5!^BQ6YlVe3e??Qrh6Nhp{3$f*YIFC263$Ss_19J%2mk;-s6^NM5Mn{8I%yu_4Z9S zp27ng9tHk&=Liq+I}5uC1FRDDb|SmNiJF(#n3t(f;ZeO*!M!({A%W8DV~D;M1R1+t zvq@$Gl`at!bqL4So2>8rWimjFvo9yBN)fiiE@fX-6$7rMmP5^~dD!RhpB&xx84z2w z>XGIRf;+JxTg1Q(wwLuq(f=~amKU66%)begvD9-%sP!;1qrUdc0fJ|Ip<(f9e?E># zED2=2t|j*Lzr)W?KN-5{*!n0}PY}Emn2LNWyzo$Pp|oyG6flbIRMnl-!vjF`%u-1Z zw`o?J$612mj!nA>vAM?R5OL>RIUDRYv3i?t!%J|csoRYb0LZJRDO^)c@Llx6#{~^0 z{LsB{UuS|0n=?iHN{IWJY#;X(+p2hoTf*o76;;e5cPn}wMtJN>Libfvh2xl>R-W3y zOehW1IeK!^8r!JrDEfyKaWJwfj9n%fXZWoDdv=iUw`ls)#zZJ#c)8S*g>XL1zboIu zCmo6Rc5`jeZ)L$Izs^6#F)gggIP%Nq6(72&zUnz$6@mNg3x#|6v*2OaWVOkD5>jT0 z)+?zkjp&hoEF#{F-fV67+;;ZBb?X8j{jhcT^dnf%6rcV?n2E z@s;TuN%&>};Dn}4^GQ2sFp#UySyzP5g2Ig#|K!0L`qJOG4%K3Bu;!DN^VaZX?D-rV zA%VyFl-h2;B)DeJ5H;>rgrYKYp;c-cc>nSo-^08E7-+xyp8cv1o_yiXxAHUwEFF*S zRHS$UIc<{8l7A$gxUjW1^}QiDH4e4(r^|u-_L~3bOcmJZig6cTCA`;9_`Mfo?10gg z?YRDVS+M!V-@&R|0l_C7&T37zB6H-bheDMg&bc_Z3Y>X7a-@=F1kTxZ?NDlYl6o^7@@LM?6RD=&Bf&k1K$X>GgQ(VKJb}@^^TV ze+pK=7~P~OZoz*^qE5Q}BSZGC{ugdjGQhs8FJ~W=ABBPQ!90T{Nm!zP>6O5S0j#q0 z#d6R|zqOUii{`W~C-j}|4RMt}uzL`7sNcTy=eJ1f}PL>$p;g)U;A+aP`i-2#Vn#WnBD1STYSSOG!^&YqcQqVl!Muc_id>`Lj<)NVU}C1Vx<5 zRpwMDc$j;Ay(VwCNMf8u4o{Xn0~S5zukK?ed;mgmev!Yz!FP5$Fu{)m3R3K5U&Dle z?JAX?`I0*(a-ZZrM|kw=B6sAr)%AdCdyTf9R|Hb4Z-!*i&kY?m|6J1^@SEfk9s2f; z#1U*7J)@vI84ETk$8zR=C;?ycjMkhwAG8WG8_cAvldhgH(O^DA4~>fyv6Y{R9`L!J zeFjR=@O9ok%jhEyTGeZ>Petwq#m`PJ*{XgHu_*ki7i#}AG=cAq9q7T*m+IRX5T|A@X`>;3}v26n(l^qvi%BGrF)>v zZM|?RnhPy&_ByzA@?xfjcIDOd3@n^5V!y3d0So>H6LRfN_}kO(7=wc-#+?N&?Vkg1 zzWH@BOT#17-G5vrD4g)64J=RQH!H!EC-s@o_vvV>vP@Bn9fa#}Qa`%`bjta#t8V|ByiSXT$f22fwa?pUYwxbZ}BE~<&8}ooyqJ>097y~*l z{?hQ)PQbc?o8)$i1+WvoOW@ZRMYzD1`yy4H9bT6`C*P!uL~kvx<^l37Fw2jTO1mu& z1wpi?hYb0_?1)wArhf|dE}rG^I#ddBmoHtavhhX65pOXe)J6Z46SZ;=(vaoBM*+R4 zQYcpZSkx>)2H!J(3Ta>ChrH>KFovc`tOy%sr#+SV|J-TPzy$qXBDXd&K+nYy+6iwv zr}Wm{jF93#&C^0rI`AqzOI=0a2HFI5)-NW$hHlQKuV=bAA?4`-**!JN$kN+MD^k>h zR%YaJHJ;OuM=tzPxY!NCGqc8LyR9Iz_y`Y2L>XQx3y>SZPFPWVcUVQl1j)teZnY6S z71wuaR2z9E*oD{B=MQ$mj8~oeJ@CZQ!xa9e4MZR8NniG(j!rnIrAF?Y_7u%G#;0ce zc;Jzb|7&t;PF&jS@L=IdIgUx%1-_x~205CC@69B1!98>FbZ`&{2oBD#EvZGoh5b~z zVZTd}`vNtynVb%|{I0lpS4I(RPdM$>Tq}Zw0}q`IG&^wKk|kw4P#ffjP109$#h`mC zhOI|35FRvA#^p-oqR-dWr9^`uNQLD%k)acKn?_D+P0j$BGb<<7!+0_H__viBi&8ic zcI@Uh!OuS2X8q`{j}4x1rI+(*lOQ-{TP~^=uHgKOqRQ@3H1f`-bQy#bKUw31bA!rZ ziDOTnc9T9-T$)YY6+s6todfI6*_4CA+6Tr9Pdf3)QZ(!DAs@6b;f^a;wnd@Aw&)J2 zBIvium^^ED5#yQY)tzUAp~7tI`mi1cI67&P>R-iU{)3S#qH0x;UjF1GEu$&+ku~(W z)y|W0%yx-=$z4tTQkX%D~4^ zm_@E&-P-Y&=UD-8^!*Vl;+G+ zJklQzLKA^D&12Q5Pp9?tPqh*Z*gKtV`FsG+e0Q1qEkK4IzxAz@^>#xRcSvzjWCR{; z-T9>;Tng=tmXm?@PVj{>jE3R41pKMG{RX*H;RF>wpF~{~)(&$s3OqJ}YQdw|1J2W< zbMu*ci-s7ib-DIfKdAy{8us+cusdU*|0RW}2;$t;f23dS^)SihEBD?nWs=a;>W53Q zj==i+jw^Fw1~$L>l6R8mhpUQ>2-TfB0AGKcSKX-gL<*llXSvo?cvm11qg!kRYkcF1 zzD)7>?lJezcc&$=WcfGsw_qNE*Zt%%&5}My-g`~)wJsbT7N&|59sNKjMK}9lQXc+g z_*}qzRu(Mped^Fs+8{9v%yn~(S;4PQr(P=;B@p-G_a>B{CUED=W)^QE54w-7M++Y` zgtv3|q*6XdpyCya3(8knu$SBZ+n#VLT;tud)Da^OE{n|8)FS&)=TTr=Iq4|K|6DuT zqI41y>n^P9^s2%A$~SCEe3IxI9c346<_Ix9WG`^dg(Ba72llXUmB(HCIJMxVM zlKmVbu|E(+vmcIM7?b=Vz^f;lwwUPYe&w)GA$a2a$x^1$Z0I~x#31qP|OaZ-RAblV;E?BP$uZ+b~)ZqK}+`q*~u4Mi<@oA=N;Dkqyj@z}p&2|NdV ztdAZj>{r7r(M)soTv4PMNu<5GD-*81+x^lmr2##1O}V7oeQ;cf#1!+NA)fdkC~5mB z6*S)q)G=k%;GU?vlh!@j_`va~Sf4RJhFw@)o*EJdy{=Gd&L>7i7xn`D0a#$Tb^pDS7KR!* z+v~K(q53nPi)*Axu(;*XT6b9+ZYxYpM}3k51*3&_yFcD2d!yQMt2!GbX)CD7%{6gs z-sWB0_72HzmVEZ1{Amb{IJ+c#t_N=~e>!_^%>u&eX4A|qc?lj&-mb!gQlO~*n8hD? z4NVf=XtoZ?0ofn(S%#4{Qp^3%u?rocs2H(LdAYF&mfk;Z+@94f9A3l>Pn?ZZ;My$y|U>$WLgd$U%F<^0@z_3OHTTY?R~Ki|5)? z{37i&QE`f+)wuo_=|Ah5cWqUwm?hAoc{|kyTr&ML!*Bl^x+pdy;8e92J}aiO&`_qq znWA;(;A;ebb+NVN_m(DHeSG|A^R9ysFjl{oRFedhTef_*Gj*uH`22$7q$UjaUMrSA zN{Q^VoN=;N8L&IjYg(yBON4|=K>c^b%RVp1K zPh(5Y5*CNGFCCT#?Zb&2_+1w65ksh$PLHs%560j+{`DZ+Jm4EU>9jtt3sZ}`uG*bn zCHWgjC48sIMy)~<-}i;*;9moOnAcV`l0EzQxSqcf4%AaxRf;VVXg6 z8}qs(+IpxSU$TzH+boX8{1jDib(Pgn!NUmGSa%ZKPOp;2MjoHt)f$9nM|JAhb`=2p zFmAY{VS}7m%W6iCIYEKzeMymZ5=`vXe!{`sh*pDzTi=QPzKQ#|fX)g(UJUs8y(2UN z1DcZ84xDno7w zg73Z+@c7vCD5z@B>{jS0$F7Zyug-!>;BHFI-#+tbh;sDGbH2}paNZ&|f&LpS_=Xkse`t8va?cX~tihqTxoW5_x3qJq z!3}k+4?g<(G#VUd6I;)8ssiH^(^iu~QMgIVs5BR@46oD0jU27lNOv{9)33kEhJ)M@ zl%?h^cxWNuZ@H!=I8{SKd)89J~y}4Tz*--=cv~iycXXrASFIZ0at%iA~V36!m zXh|9$HuV2TX*>mfg^L{CZT5K9RZKSi%*wsP3Y?S3IK^I)D z`6-d%qk?rDbWswDF%bFSI$ai71*R?*zLF0!!!2KD*0*`G(9j*1DD9Su4-54Fi!y70 z;8D%wsPh)!N&85AWl|kNAI>k%De%FQwU14z9b9m$C?{djuO6u_=ynfCBEkEuxV?SH z8_bLRlpCW=@PzrCwtXVug4f~FXYr=6E2PF|=5Q0r&o6sT=x5F_tB0KNSucq+{uXNc+Q50RR^NQRkgg0QhN}}4elE+5P;{rm5ytMjM#2P zf5lbL2wJ&h*w%spZZjmgifRjh4qJm@XR92{J`LOdh4(3`N2{N&Jv$p-#CIQ~`rU%1 z?L*~PL=Ho$lj(VyH-AYjd)07YDg!3D?bs7@)G-WpIjsyMkVl}YeAML4Gk zj>MIIf8HF5QWS6!ED})ZfCCDLo`iO|Lh(<2a*HT4u$)Sb_S;*4DSBFW+!8JTr_ucP zYU#5?FE!uv-lqzvB-c@mU)R7x27S7bo?Aoj`_+U-0!C59+r;#)971?e^z9eL{Ge)a zHt#^jLA*g;IL>tA`9#lZu3u_6AEMsI~<`TOSr-SyUJ`{h? z4Z_sGvBxK_bmK#^R)dFB91!u#Aio9QlR`I-T+a1tAo|vo3Mo@nz%cq$cgkD@ESIm1 zio}RR_lBY3)ZIkTHb^agV?_8w?j(w7C31rF@1DA>JULK#!{xa!SOv*%7xx<)mEoFr z@g)iaK3Lbd{KB_W65RCF=BBwKA^nS^jkRS6)=G%xZyb*RXNC{0-M@oSal^0SPN*1W zwlU_nG)iNT=%;a|nkqPCae08RqZA*VYaOVgSHYe?Zic`@@RzSGmn<%L!(eY{dNij4 z(O(%Kq^C(khgvbwq93s^==!<3HC+nolLa2g7fOK7r_$wA*;){tC>dUBtUx-Mw-Yb+ zX`}dSCmzm21ZQ?$NBggO1*rL`I_$0|JO|G^MdayrNKTL1o3|?tfD|07y*;0Sesial zbXbz$$u2|N4p#+ib+Nf3ULlQ98rA_aA!8>b zULBC|>kGWN`y8-k{*R~g{-^qX|9FM$keTeAl|35gviII4d&?|jXYV}<$u2XJ(2z?Z z^j6W3GzbkNQb{R%U*BIof5Exk&N;W&>v>(z$K!r?sptN885Ih*tA^HOAaymMCR2?a zIDQ1Hu(SuEk%rzkGt<)`5@Z^_(?)PfROVK;?()H{vKNZfx#B1mc-F=4h5!~PbiTFr zO@N*nE~k4(WASs{&f{DjQH=8N>1N8{pVe`vkg zmuLC$db-V<0E2h}_2}k%);$^nj+=c58JC8)1!-bGE{Z|KLutLWZ6YrqdrFs0ITkxB zj!VVe5dcTYP2ECTCOGlN;-o6&M(?1}(f0MXqoj7GV)pUWNOTy7^7tC!e)fY(oKH8w zMP<2MtTd=b{LZ`o{MgI{?+d@Wf8I$!xBas=5vlU9FiDqjw@d==KKGf;&x%9R!=+n% zZ4r>>=pWM6;)=U2hQIPWWe+22%3AKn7?JwE>dxh8R=B0&&((Cq7xKamEI)hfg&GM0 z*&&BmP{Ofjhx+b(Zb>@Gn!_~~?ClF8qNIN#U2U+PnUw`cnLQr*Si=kt<^a}}BrZ%6-YI=hHGV4#C-Nrz^v`pH^#xYRcm9kJ zi=@0e)$yR@PRE`7F9NLup4|AVE(Gf4f&8bg^TEe%zltAU*D?L8jv z4=?-MJ&P1Q&~KRRz=;>Kcz#aNYh_u6=w0Gmv1ba__cL-9OvQouoK%8F`C*u;7SQSy z5`lZe4jGx|XYhe|oBy_u58NaFPnI%ogA`&s`tD^K!MixePghF#*J9FmUd?hP!tlO= ze$}tu+KQRa1@j;4ldW7mVnn+&4q?EO9|dy4Yl9L0*t#5A8y^N36?z%j`jO! zfq}g!Z!lXD&Z@*Wse49)H(mQP7DZX2hpAI^RYMhsu1+iagA9ByOYR%BKN(DTxf>S$ zFoHc(U+^nOS}A-*xDo8v##r28`S;}i(rJv zY27J?40zLiY4&kV0!E(vqx!2t3)lKfUBSQvPctR#GR+|N4BxuyT$^H0J>p$ zyU)qtu_=xvCr7O9k5a)b^}v<&qJvNpoOjM>Iv68GY>(Yo@qsYk$yXJ&+PI`|YY_e3 z1Ru|YYhOB33te&!-Ctf@LdgSkzstRikT>piUO=WP_9WdSQU8j^flEo_QTHR^msZK< z`3+7`(?6_4;*o)VmvTWFw?K69p<6qd>kECV=Yp8mL~y}gvqpY{2QTpvV3u>$(8flV z9o$@o+Ah{lI|$yoV(;Ue%yMb?>GSy*XK5M4++YwX4KBh6UnNg&UQS?W7=Fxd!Unc( zzgR!)3rBs5?E9utXTbOOYI<1)Cv<+1xtG0XKP2?*`&YkMhmUD?^-Er;1p6bWN!~Ih zFdK1R?!q(T`77*lIOa`1MEV@+SogSx32EZ|kxi^Xe!9(#t8$yvz3+75bktSs<@msN z6jUNACc|qhJ5*S1q6#KQIwlDNlhmjPL zTU(8qt;*olpUlyRiC(bBdq?@=^(5e|v%KO{BK`lp+mM}FVRWIA@fGiKg_-NIqB3|A zo0lEpUq#4afQUqL>S0DSFOA!(y;co$316tcuUx`!BPUHx#t5O*1%<pbdo4q0;j$|r~bEIRu_a=jTGhkDKcYfR~8J&NgjApyW4>}H9 z95=*7AX8_Mvh!Y#_i5@`czSfh$8`xhz?qKum-M2DP!N+kQmEf!ZST&!>36K4hGQeII* zM{QwbO|yLc+8Nx-@$%9i*FZ?5BXc*LKLih3tEp#mMd0yKtA7a}a?vK-Hau4}4RTl$ zcIA7;qrH7yCPQizRG0_KaNiZjQ5ygKPxB@4_w;6aO@kZG^Kp|2Jhp^lO~b2o5AT!m zcKPt%xj=z)s!Zt3{Ii3KJ;2|P}}$FC)s@M)+Bx~(=x zLl`ByTD+?)?&^Kiy8T!jpIarpeJ~k=Vtvk>T39tDwGUAmZ&tpw?t&wEI=gCN{zCdO7Jp2o% z2d2BODXo=}gU{pAu9RqL_@MmSuKrL2K417z=e9o>W?Ozg9?+sT}(=^ynzzO1B_arFsB2vx|`VkQ1> z##_{)b_6dbq^#w}?SBdAkj>g2SS~f;RX5Yj03Gl zGvI;$saDrpiO3ekQ?>V<2v~9rIt~zX#7FPbDN~n}@ymCiS$zdHyoHO&wF*RTEZ+7z zpLqm^dhz}jHlT`@-d8;MdH}nIelC3O<3p91e}}tAgi$j1jvC8iB+$-nKUYjSiz1dR zJ)7xfSW|r?u#Jy6ACIl8k{u;_0lKl#|IWDJvR&3aW^YzZy*@i<0H1p2T=vU25j@Jn z6wevF26%xgwBqX&G0&cfdG8c^EgoWc_MLkr4Fu=7VmtA#1{RjE3F~d2he5{c@gZyB zxQ9L@r^Z7S+pmoKlm-dm2g%>;jthB6W-&NwK;*u&M_VWlJ(L7xK7T&WrlWB7N)tM8 zE|Nah^E*Vvu)yu%T;^BG4Fq4fy_e6c2A0<+`SvE5L3&x~kvD0|ApP#X)*97i&{(8c z#_@~Dma6>b7q2Y@-%@FHFg!_ka9=%Te&0mo2F|=>xK<0f-Aq{|<)hH3Vx4}NLkBV+ zpRy!trAK9zr7SJ3kG;4oH2nQU2>h0isr2}K8Y8Vp0gvA)fbXsMx8#iEp?>}4lt5Mk z_%HS-4JFrL;$Nw;p)<$9`5IkgIkD$UJb%kQ^6e2+U3C_qdL;#6431qIkNLqZpE zk|-}J4=R=YQm)l0_%ck5AyzOBj>vp5>=8SSTmnKye&@W9q<+7X#94|T(;tYHUnl@s z^GCf|OUjVZbMRgxhXKe<$(GY_7GYo#*(eon_W$mQJK0OR(<9W-JKp4J|63EN{_!yO zD{BEALS3M2lPaI+ z$w`vGkGIFv=hFGB;eIebdG?ZaPhZK z50PCbn?0rHIyPNcQD5QK!5oIW4E46use=(5x0tXvHtx zF17}V(YwzUlW8OK0qI>V&mv|$FZ}Lwv@;)T`&_~(jlIQBspju%=`dc1SC*D_7G)m9O_7*WdK{E_~I zhoI87hWg@d{J}vrZPj0k@6U8zyd|TEr?_PfMkiRnb%s8x@ZVXGxy;$TRTm6ThXN_H zDw!dC+5Bf5lQ~w`4ntY=El}{JA5zV#1;)6|{tjb$jO(1qe@arulRCWHyr~8F$-JwIy=FM4mEtXV+t9jBTUA1OomUA`l_i@GRPvJ;iAcpq0o)DPMQG+}wiO&!ZoU5I!t(0!lT z8t3C)S@s!pzy(!{AJI1DzQLrWo$qO4gO;L?3L-^LrPdI1APLN|6^C4;eS@IIeZfB0H3CZMfTi?lSHrKL3)NI!Osm7U!j zj1E1dyx zri2chx$tf8(0L2Oo1W(|CzlQHO-ioqYdZ^Ffo9^xwp2JFKg%K~Bm^vim)BG0!|^}WZX64o zK$chYXQi_RPV5!%?OHDeI!mGYQ_4p0XwtksvxdmSsJ@-!`tJ(%OOHuiO325S{aT+a z?Fp{m_=)96K_k$PouyOszm9rdb~{PKrTBzxi(O?(1s4P+YUdV>!MDqE(2h9wr_=jQ zvYpR>-9|PC{vZVg#2LM|A3KCJTk+O1eOG}^fcsP;XCYmSL#ql!hE$8%^9|~kVPYESTInBl>HWuLK5=u-lXbL(?i+-J$=nPQ2K^L?_ytHa?zBsI*TdcM(k9}12ft5nD7;?Kf;o93665FlNVzZak;%;# zJk`48aZ^_X&lvwL{2^lw*UoE4(f#N{ddoML!iAcU{>oFQ{HI4St%_Z_+Rqdex7rG* z$Sz`eqqJ>MM*?aZ(`Inq6T_*<=J_6S4RC7VF3n@S2((|mx^YY=z@N*)CI6WTL6cVR z$En|@$k^`O+IYJb8rWTqylTt?-#11>Df}1EgRLWa+aVq`G84|^@6$%U+mAyB@0!EE ze7cJsPE~ke`N`^a>kPc-rCWMAi5+)%ojKGRG;yeZKk=^ZfuWx69y^zM;5=O1_V<$% zEFZnMXZ3(7o@evEDVR|K^#=SexW1-?QD@ADDLNf|wlU$HLT(FR6`#k4nRdh3yfsq# zohrCL_pYV+z8y~fx%_)G-xGvfM11CivcUR9q|JxSGeBqU_W1MOZ=}~06jCMSpL%6Y zXDw!$uVZ$9maW-%37&|+Tfe>7@Z`Aemw3-zm0dF!cL#M9wba zAX@y{Rh7aRyMLwEJvproyGD#xQ~uV1RP4>qw`lUfl4?`mE%+Fb+q_+v_R|DD-3e~t z)o+2_<~n?uI<~%xFR8q)V^1F?*9N7CRxqQT3rU(Z?Tu$o+QEM z1=CI)N@I+@f9`IUu`Adb=oNi5?u5aILq5~F6(B*nYq(*cg|d3?Rc{?nfcPU|UHTh` zZsd!fvZuUoH|LG4ssLdOU6(&+zQ7Fv^Zv}RSqXZATHlVbq`+wN%5~a6Ez}KUVWgV2 zfeiy%*Uh?oD0k+!+inbp$dHdML3Y%bvw!tRQGg(nu>Vp1Zr^~jk;6m(#pWZA!33JWnbuUxB z(e&lXd*KNV&~dzZubZ77P^m;UM2g9Pm?U-7&Qn!1|NVD##Jd(Yz8qqkj?RQypIrw; zYs7I+!l?fomnv8v={R4h77S8R2NL~1I)d44n*SzubD~wd+>8`S5cX6Y{Dz6c_8O*V+a(r`l#Br%kF*?6WJltX~GOw79$rruD^pDBH zRi~U!#y<%jM+Qy7iNb1#tjxVMT9gh!voG3eTPxA&LrCGD?3U7i8x`v)NKp{E={=FWqs! zTCD~qQ%&K%eU};68HVQkjhr>b8C`zorrgCM=pfR6E38!eUPB&WJpaMe6fvy+4GL z2}wWe{)XY`XMuXAB1aru@e?iBdj+fy*UtD}&w*$KiF!{t2Jpj-Q9ZSz_-PLt^JmU_ zY?Yw(csWjR7^m&6n+n;%Hz(Tg&A< z_lEdIX8)n%++a3x=wC2#j@l*jsi;e)25n+g_9S{_;e@X?)6ERj5ZMwz1=3@T6i(m`^E zHSKB(Hx`i(+Okkyf!AABd*fLOV8Ojx`5-aRU3Qu~{Puz+`dU47cx+Y)DaKD0pLJ)# z*xH|fv8rgeL+@2`irNzlPVT=_;7gC3pHs3tS=nGth3RJYL@lgK1-kp~$%BWZBO*`k zn!x(an3jE_PMBsq_4kHEHpF>VYpEO~*qGP9ObXFRqk%@h=*j}YD|l>2{+G)NVx)P> zTVl;XMAP-a9maCP2QPM~%P1N24Cbi?H!RSYzW9U3^Aj*(I4u*kcL?t8CTsa()DDg> zcJZn7orSFDk@J5)#9()xn<&}z2ufaGf%?`CobGx){G8et&mC|2D`4dY!@u2vkG?NP z>!)_UgI;mS#5d2BPjQ9l4^d6Fy5|578^7Bd1|4h~SSY+WWDQv%F@>VD?VuaFU3NaC z6m}i$^1s6%2Z4iQvG+O^FgG%PQgS!ql+mpOhLL*o#k*ZJ=LGOBne@JWx0Rst-`=4! z^>xTSexpdfI~SFGgPs_$R>Pfj$?1Q>X@E(e0-hg}L|#VLqV#cqG{2A3(_ZP=z_)nx z@_Gr|K?MzPv2bQICBMgGfRUo94W?{^9Qkd32PMJK~#6!6KtDnb4WIuMiN zN?P7qiWIVqPhtuaQIbSq!gALH8M8j;u`$?#Ojq9Y=`Ve-8gTO=^KdhKKfiZxn+q+$ z<5}#Z0|xP-@HqQe&b~M=RE#_l=S^V+uZ|t#O6`uqy_-#0h`KxvV*d7e9=F+EXlNV#6B9S0ohg@C3oK@;f2s9Kb&uIvwQ_hhW+J)U(X%so;|I zpGLrTAsi^^B>(;MBnEz)zt$S14`X+1YLd6ELv5^GZqmsfh!u*VX<;@*kDm(d1w`Lt zJE}%<;#55rexohUx9`Bn>QB>~)H;~R82^@rOabF*ZF5dAFpq&cy_ppqW{kmV+Z;u1QJquPuRP0~pV5QssgGOc6k z`w2hFjyog!MGm;Ru6)GDhY{UhZXJ31RT^K7c0gE*0Bl(YaZ;`%A%ntHr&VtO_Vu=& z+b#N>w2wpgFy$B}PW|{%#`Y==w4{HsIad^eH|^QD*@c-Y}zxrn8xP8mL*i~IY`?JC|7T)#1J?-q2Y^NSo5XnQgiPx{O@y!l#IQk5;0=J!(N5+`WYNMTjkP&OS18fk=DS_f0Nu^ zGpQi;#+yJEbx~v{O&Ujbr?GMog(;x3_6F8aGDc*bEP0j zWiG=we7UZ5qKn`{WIg1*v*)lFavL4EcJfdVj1|!4mbZk%#au++F(#}#_O9rTJw0?k zaO!?)E&)?#Xlu9fzxSScG;6W`-VAO&c#Y~02fS?iaQ@f*c)WF>EpA3T3$3lxGgzgKU~6UI+#v5UtXyFCHQw)!kG^*K zbJ9hk$6$I&%9Jd$6>Kvt7wq(=Yv(Sl%GzM}7!cYPn6gU@8;<=}yHF_(_beH$M#xf7 z!&z)Cp{=W5NeYl)Bj}$(A|M>_W%X8N-%$9=W z($W0bV+pWrRTvX6l>Dohj$)RwXb69$r8856RTE)_*0}pv>2=BE*7-imDRCtcy zxxTrV8o5glRZ=Hh6_jeQFvGL&tkiXsi;+#0OVmWuU#yq4z7u?*kAYri-z8ydu^_iU zu}|n8S- z$!FJURJdlg{;#R?9^XEOwmLKWq6of%=T|cee#>g4eYMhUm2(aE)ZNZ~kwx4K z-FeaQ@tqX(A22T+N;-#A?k-%9s*-U1k6DIYuN0OK*)_cL<_9rTink@CFc?YhSmxNB z1bWlA9Vo?@|ILwI}sDW@=%G>gKTFrZsNgR~D=~r4DM}+PdDx`@zY%h4a3~p%B!W`Xiui zn^djN->mwY28-wpz08Ow`ad`7JifmvM^Tz`v2}KK>|ClWicltlkNnR*zEV#?rr#6P zznAkdz96)~If{g&W%q?qYB?wy+!^uM)rk~*(t>-R-Y5KmT*sxHI6;9?_&wDzRdhKR zkm6^511{Dub0pnDI6f->U&be5P5d`0xFVhb6*pe6sCOhn7+TX9zsiSHTZ|lCI)+sW zPyd`Ea#aPvRmG=Tvmi9*Nbh)Z39PusvOm3NjvBTvoUgHHL)gPOr*+d3Fngk$Fn*y9 zm{!K5RWGX`yU<|DA1Og-e~`Xwnamj{C`#_AlY8LB^AB{Fa-}ggQv4K8FBdR84EmSf z77M0{Y-tRuX;4)B*(G|H8FFRi(F}@cfO1sM1U>Hs$QuoJ-KMC4IyC{c#0+iJq`suM z^iv7W-S!InW#B>ZaY7<&-Unb2&+=#wvZLC_e!iygU%ew#oPvsraWMHg&3yQI2Dr>! zKRuMIg+Bd@|JAtN!K3_4{q;c67kG|_(M`1OY4W>30F3)@=adkRq;NMbFBuiaIwmq%4@|<3#0O? z31{$sT{1KF)(rjAjJp03J-7w!qxP2`SK%AupKBh(+;U@ieu#aV0&^?+*3;C>K%w>N z1Dc~(K@e}qvYwH^$wLLE87VuYxHo(Tk&1E@ zw@z(EIy)#du%Dn<)WEEX4s`W1gv5%c$sSvV5MWu-q&Oo61vjXw_v-xZ-TzDA_f6L% zU^1?)WFYoY0t52JzUGJUFXglz1@U}fC7pnKw@kr-x!_OHT_;HDWf}X+&WeKdS0)u@ zc7u0ayV5x#XV8(_7QCpK49~_RT>jo>hh4iUs?RHv1I2Uooq_Xluy;+B<04xo(QAEn zB%C5&hIu(wzgFx&e#ICDYn*$|v#Jkd$zb92kN{CG{snc-Mj0;ECb`~3iD#!f+)f<{A^%5 z2v}$Pe{*g{fxe(b%#&qha48yaRJqNFWCsqY8(&I9yOZ7w%by7kfph9I)1)FQ{5*kE zREHtoZ9grsBnG{{5gPB}2v95Itmd{*2Cfcky4FXMxW#5JL zLzghD99-~!Sn-F{b3O4${$V?`_?sZVNOD6Ct$0%kr4-wP?Au-xCu2*w=jr_UeG$GPoCiQ6m{ z*eReu9a8B5A82=JW$Oh3r50H;hbJ%m%<%f}3-^9hi71|+KbnAn{(mlNY~^BS#B0Cc zNgZhP)GM2!v;!e=CjU`q31Hvtsv$HkgmL;uWb(qJK=J7&iHkb}B+CBGzRNPiPeEq_ zn6oYM3PZ@_J2z?JG!xCEtG_pT`-SH>_!kr5dLOHbPj3NK82TmLBzV>45w8MW#TD=* z7hiB+Mhxss9aH>G@W}Zo62n=4odm|UGrNZ?eW0FA{Q>t;YLs;L$_w-SOZsj%82QjY z5eAfNhEAvFLn0+b0<-aXTVVMY$?B0*hP+S9;&c-jnk z)6%F@erke^?U=R-JKR$I%jz<|_#n*Ys{?{bokP!gO=;f=XB>YmH% zf_P*t{&heZ6C~9aD%B|_qvNcVR?c=l>ix9U72!#M_hq6i)4Q`l@)yI}1u99@6v|Tk zc!9_-VZ3rfiZ@CQhb7)Tb`FQ42nwLKAUyj;S3C~~aZ@gE%kM-ImPKw$r^w|a-Ir%# z1KSFCwmj8VK}r||IRA_n8ARcQ9n7DsOU4IZt=P9b4bh5k-*Da|ML4-NsoCvb1woNU zCaHv<$1Y$#crl;oFQqnf`gLlpdx|FbzYA1<@M&i1B~!>qn_ecJ(kfbHJiwctn$ebsPh|A9N8oXL7M z=sUrk7x?IMY+;cgLiYUjAP>dYZ&Vb{sCGfbjd-)790kazTYF~{E&y7~N&!u6))>M; zs%yMcK=54J&g`bp!(Oi~Q$5m$UfOZvg-_b{cw+E{+*yNKIKAccC)y(bZNkpU+EWo& z3Cgczv|YY)!F#&X^oja-0lo%Z^}>|DliER7p^q6L5Fi7Lb?qw6ADI929TI zTo$A?grm!Rel}WD6EqtEWSqUi2Q*mf3dkKZWv%6k91P z70^+5)Uu6E8~Sufs_7p%f%(iXH=A@fyg2d6DUjfBrK@i;Ipz`b)K`bv@&Q5A{4*a? z6JdxkpFAt4H zI>UKTWV^Vu^+^_!{$979{qPv zH=g5PnzxM60L$lf6@QOtfM(Gva;msLq|oB+is9#~knDBEOn)#0Q%1F3iN`GV-YR%~ z{C)aXZ%6r>bTy|YEc$d^k7uU@n$V`YJ=waz5TmD_Adru1!f|n(7oDL`@-#_enBbx* z^vi1z`^w#Q8awGfTJif>8{5@hR`klA5Sl_G4V8X`2 zcJECqf-6F8!+fTK72mv06n#)@1~*&?L-gfL>@k=PRH+rmY)`}7mOpDG@4zJM=xY|B zpYSYGu_^;yqliS@CuN|IcfS-=^__IdX^)%3O$ z5qQV8vwef$s1?a7E^~Q5B3&N)T_nb=j^2+q?ArZ^9*tveN$71YU|-a0Zu}gBy?N=6 zSnJgBC3)Z6q&Xu#do&y|xPAng$M(<_vW9?B`!L!4ABLz^-x^VT`A4tFbtTK#PB$<) zKQOeNSBnu0YY|dcPT=<6kIGM?W${~c#?EOPPmr;nQB^!%jcc9N5nY}IJsW+yHIKQ7V86d(6I7QFO z(zxUTPgsAyd}*QyP6h7W>i#UC6#K!_PE;4yh8KR(6LVj&Xos&n-3g3oFEf;~If!OM zh0LP!mUzUPS#&wG6p}jbUj3!-0(;Nio;g}f0aw}Hib`bZqW${^?zhhu!Q(&ggu4tJ zVXnnD@cx5CpjNJOj(f!r&CzpMgDV}d!cHxhc%JVb4_s+uV}Mr_-u&kmP4SqWgo{9V zI^2D5A}^Ff5V{W;b5if4#|r)E)Wy3NxbJVvXW!*axE~!k+jpB6C8)l=?>IP0dgu4$ z%Ggm=*cEZoJcF5R`nPaJ%4e5`r!HB!*_Bau5h>9L3W@Gow-J-*V!ecTQI z{q&rsB<>eP#Is%s>gnNFEpHWF`FL-w;*JylFFTyLS-)$*xd60khY!rlkYOjkPp2IJ zchYgAaLeWaMND~GSQ!_Z4C&9b&oociV%pbP{tRh?QFRbe1AQZ1(wcByQM2T;%U+wVbfccP&i5CKURVvew6KLuh4F^ zj`P3ovJ!^RP3pqaQtpC(m5e_*y&EcIeO$QrM;-UE{hbRH8HHgpOwlMVIu zZ1xVC>uKO(jt#w@N<5IRz8#!6qfYS4-;6%~^qsVp^L(F{hYa>4%GEA-B>^>y)kPOK zTf97E*tcLshl@RHYe)o1v z@W!>(%VBB90oDEH320yd#-6 zh67=+y@}k4$q5Kx$KSZeY!LdxrC^AU=pUBaj=vSE!QFu`s_kj*U_n}uV@iS>49{2? zykatf>j}AYAs=(mAk*X2w!A6o+33?ul?bEODUJT5JWV_p`r&$P%Xzq*{RcG0`QZJq zQXSKw0948|w-)(q0+H*gStb1((0JYQKO0M8e!Vq!z@GCOUU(LLt<~EKBKSt?wr}y` zRlR%GW^pILu!FaYG+K{WXf$$v)GMO~J6UE?Nq_IxtiBV$(meRZrOH>*OcmK0R7al@ z``5**rn17s^Lab+c@ooU8Q_>^wK=MnjK9oh>A9{Exz2BM7t})739h&Q``cO;Fmmom zr@GcvEL4+|de6X$dM6lX+G)su;>|Os{ZsS3(o(4gh93xiEDO#12fAkP_nD%o<3$RP zRp&oiRAdHuQ_NR0Tnmt_V|T=ZVO{W^xpBGT`*iPW{)B+Ds6MRT_qomdH51d$CTo>a zDd4;7_gyT=Xfbb-`89`@4!EnI6X-tsi4<0GI5)pq8-5>sGO;C^i&xX(r5Ys_8fo4hQEeozdtiG( zn|G<8y*BDZ=eY^?Ho^zZbe|mafZ*lsw6xRe%kS`FcZct$E>6~S%Rah zN#{kATnwRLq8{$D#CMKXzE5~4P`dBrc=9zXnEQC>==PN?-1Xsp)d^yLW;mkFb5MG% z_h5w9ujjTToW3u_^7affmT;~fNC=wi9o|HJAvbRj?mD?VAEXHxpGW&f+xEa0Ug;RW zE3D9eL7x3Rg)2Cbf_DvS8-tRz7MD^SHEcN=8N5Bqj77eCd%k=B?9JRmF``$;jUka_ z_Ocddfa;$3@m^}eH~ApsS9IVmXp5$mic(u8*}7b=i`OB87)s%rq<4SF^Xzl~qi-aoa?#f|dpAffr`sAWEapiXca%=c9g#;>&X0b}6ekI9+auAK zU}LPHy7eLJ6eX7GK47$=Tcsl*gF;sK;}vKodMhZN^D)KmM{>^x zr%u}}lP>b#_%9$u8K3-osgU{29)7oFN|o)mMa>sH4dM+v$hk@;QuWmfruiNF++6F> zccD8t=cO6G<_Ijips@!@B4Md&0O*mV%jQzY-$f5qYHNS$J z0`7I!(vuV7z}r=R&F9a%VC_^@v};5Kz+rdO+{rON=VJ@)m9tZdQoHE!&jp{H*IpfXGZYTQ6%Hh6Ur;RGNCN$HyKATE?MS96R z@F#pq17H8WQ5E@MyLaO@&#wnpb@9uFn|9IO0=Ve#M&(be5sK+#P`rH1f`f~kg$LtI z@KNEQ==btVKttbjr{Kj2kf*utXtS3Gt}L#HvYs_SRpB?L`+9T1Z@qn>UQiPz$yODa zed!^nM*b#`s2V;{H}C&8v(-BlxTWa#{s>++a(?qFI0Kq9$Xr%6xu7WF$*IvCR=m$L zOY$2(jt(C*ipD$AV6@>AJ6C`jbp5>K_oJH}KG%(Rw;fy_zv){j0T}1yCrDvAcsN^bq$;s5qy`| zX-nhoW3wyU6){C++|OvmC1kCImV(@`?RH&)GZ`vM>u|e|$8~ZQUX7i` zp>29T$aBHMLkEq0zBIzoHwUdw54E7rlH-9Z%{u55G?l6HKnMT!-noA8Q8r9=ktwR( zBmB0FE1dhJtigdMp!wnhGtj)9mDg%g2JPEFt~c{mB9;D1)Xhp!{9SbDaA%qXe*bC$ zAHHV7_l#@FcH(*X&~nALnlcSImirhEoXEhZJ93``Ypu|SGpT>V))KkW8PBIvmOxg4 zqtFfQGVIRvkL_L;h8M3CmHz4R!}{-%ToHyGX#Vu!$Le-2%3e#_=66ej^v!VmG?jtl z-BOED3vT#C%E;Sb)d2%#YtJu9Tm`0e`X$Gj1~fm*HhTND5nlbK_EuQf978`?JpWqW z1lQmD9TaE0hFg`cd16(1AmuyrgYly=yli)G9xBU$D}|I7*yQsNL{q}VZ_6XEHJ|51 zv>5&qc%=7Q%mCK8zt23TS46>r1N^?Jghx14aq4ZMH}d}ctQIObitRD?uDUXeLFw47 z!=x~GXs#YKLL(Xhboiay3jOCwC&_0Ml&pFWn# zHR-ILk3o0i$QGWWIPiaefqL!oPf}i_@2|tbihO!5s-HM<7e+n)~lhiYIA*>Il$!Am}U_O|w$yE%CCbPkp4{sJ)BUd=DJx`Z+Z zUPT-ESHbIl^@c6khUh+E%=+Cz5BIw~|B+Z+g8v>J8xB*t47;l%LRZoK(1?r78}hR#s8V%%6zJo{l01>+dGsR##;v)RNwr}S*)?2^2?w5Ivk zxB`^}cGDZSRRjI{+qYzcx*%aPUSWPu4{}Z0yoXO}LeOa1lUO!waA~+qZ7NuV`eJ78 z2c8te?_>5|)~iQgFmn2Wzo9fVNG4xMH7vwaKG}yzSBt=FTib4qQWNL2GwRJxs^LYU z02_EJNc4(ZtG`5w!-oN%zcPXem@55b!?r6Cg2Nm}6SKAO=XcTW33hdSd**v_b&DR% zUld54cxwPl?y41)<|+6n(ovhgJ_Slrp9ONs>w~<#_CwD?14y?Gi{Q?P2B|GaiGb2r zEV!%_+DWFrh7bU6_Z;~M1=(eUxS^U(XQ7%YA%yx6X;19cos z#X_&u!G`(Nd=hyOG?0xNS$_(`mhB_V*C#9><3tuOEsG_DeXL2Do2bWK*RHSoGB&~H zY~wk<9vAd_<(c@YmB@iLDyx~V_<`x~vxyT<{`hdfBeeY$R`c;7!JAM2n=j*6 z2v_XxM4F5iV?cIoeI2DcOqVlez!ER|~yFLb`z9X_<7fwPvUjP%qruwY-jio7(z#mWq^t5hk) zj@4h|FVGAHOonMgxXjQwZagWc?kd>F-w~UA+kni%O*+gy7Qm^Jv3R?b@afb9ua9wG zf&%}FvlNZx=%Q#6bX|)HZpNDH`^e7qUc33_8QJM z?g?KovLZO=%-0kD86t-OcgPphEO>Up{kwgCE_&`#pVS`I$JXus3X6Vy^lqk$O=`Oe z%hVYfDXfj?8>ZN|@ID4j!(K8h+Js?$;I549sD4N?d81Q9`w(f#NFSZpeL-O-j{R49 z08F$j#M{X>L(ZhQgrns()D@i(S;>$F)rgP!bBzDT(|N{I{l9f^y`9V6 z$;vJ}D|;4Yi|oDkjK~U&@3{~yDHSCe8i1FA^i7=OMI=*==4I7_VRs3Kvfb+|_m${5|p&`FjGkA%3zgcei0Y@61 zFpCxbNq9n|AHLEz7(Ru!SG_)kRr`Td#CbIvqSqV#`mR+?ls3NCKG z%O5V7Wje=EqH+z>ljpndk6wqZy$Pi|zdGP+@h2{s{~WRDE19!~Iv*$Rb=&3}7lGo( z*eC4DmcW;F-!<#8B`n;(dpJ5Z0pN%ceUVNgPAt4C;2sOajpTpVjDLp0l|j49+6q?4 z-I@1>RBM5!v!mV)?e4@r))b28Vcqb|DR^Pa!U=;%{Yf%@A7dB#C$ zVCY5)CfMD3`HLnK=w!-|i(gm4jR4DLB`bA&nmR>)eV_#kEt=~0Qntfw-Crdgti13i zR;%m~-yS$}XiHaMI|_GKR`z`O91Fv@E-Se%SYy8OLf20^3w(d6j(n=J5O0#k8@ZfH z;ee(5m+Q5rc!Kwd_*l~^1cOly_lay2(c)A)mX-&_Y`3khi34y;AKR>mQ~@696ZA7H zL|)f?`hdX5NnF|~{G_2H7y0({u?h+nLUOjkXqJE|J{UQ#Q6a^RG+%1oQwhW))A{7_ zm+un5a`5)`3nDh~;Y;83l4};A^@mEbS-1{Eibvo=L_M5a`Er$QP75^t-shojREGMW z^JV)6V$kEmAS`9QDyT|*^qGoh=i%!b27Du zJ)qycCa8w%u=Y zwc*i#P8Bd1{CO0P`}cU{j9vg;VfK%$XD%UF;#ymzE_%Ma zH*mv48>v1PTHd?$lzb=n@U_Eu= z;b;Ib-O@dfJQ{>oioS`i%gF)dJ)sb@cTy1C$8sy=KQ8z}LudHBikL55VBR*148ucCXpMnn%oqr%!z4+4@- zdL0oh1bc4-KCcHQD0)0NmxjX0Koj{>W~#a@I#POJ_Y=JYH|UpeFM zYc4&pOOBY>88vUyp9fQyXj_)G3(?@0e$KMpN!)w2KA=5gM3RxRmr1dVb0?V+K!pf*>^FveV+Kgmp;j_ zWT=O?FYK(H9o0s6shx{cT3O&6N_XP=Vm5Zyi6+&VYhv5}pG_=#w6Hy*uY2(uF-J(K zoQXJIiB0R5t0(vZ!6|!>#l0RMxb@+WwaB3wn4J#@pco;1?cV}_Chcc|g}7nktD9UX z7jUI=ERF+R$j+h31pPX%R?BECgE*iF5t_i>{AzCBwv!A$cUnnzLJOBz|SIED!X_17R z#kL~nu*cJB_>)y72zx(lvOXsWl5acm86FaOsL>U14>l#Z_dWAc!dxsCZ;x`FjFKg| zYjxwiN0@;*C5ig}9tX&<3vl_vSAhZ7A}UWCN&@-M+(z`R9T0C)meun>4d|==hian2 z@yIr{%)2-(usJlPT(bWHzIktJRJSIJp&RbMl3#U_+oEebb`81#f3R+*FTePtB!M&-zWSk+8jO)gw7)=Y1Q_RsTO`=`bBbQi-yR|jyvzViC&m~ z5ZOCnnfxQc&ZuC>8+8AkVQ}A9jpsEPrPh?qFmJ~$(F8MMJ{?f^nWn)J-gfJV{YWTA zrMn+0GHkz*OoTG;x9r{rJe%yT95ZGJL1#P{+Tvh&0nMU&WYK7trYx@eC%MJ%t*YHs zcf5Qhd&KB%JqXMmS}S4EK=%jRN_z>{N1A2HxoCe)(44D&uxnxmavk|;KK5M;sSS7X zceEzhzQ# z1V_2vI4j}1EYjLb%xmvn=!p{+R|md$!y9#JbT|;La#~@h9}HC3S6&;g$H=D6mp4|| zNVKW->A^aC@w&_S$lXv4w5jmF;ML;=SBm&9SMnTz@s|w$Rt`|ZV^0pfORfU=MO?sN z`_&GRjv5>dlqCV{+w|O|Z_!xnktv^ldq3`_>9b#tJqqIQ?!LPjrv{pP>|U5sMWGR` znL3LP51yO?HJu;hRZp|fir z9pUfb`=(rZpYWo1F}|9&{6_x7zqS9`j1l;+-J1AuB>_ubzX@>V;fFE9n`8=|MRHcI z^Oa{whG@Q8@H8zq7F4P|H&}NN9z-|JPcQBTf}8HGZ?TPgk*y`xLZE&R-1~Q`Z|_cS zSWS?TxN(XeR-MKQUVUOlv6k2vnO93B-Q%&3xalRZlj6t@I?4ce7d6N<-^~nI{_GOI zSA^qb#XRW`30@ifjmxhduac!i1N~JV9)cy`{HH$mrQzR|y_!xsVnCJlIM!O(50{_E z-FL61fqaW}x1rMy$h^Iu?TR0A17k#Rik&Y%>TMZj@^}kDhG>q#h9$8NVoL0-YTG1T z$v-`?7%PKkb#|*4KKM(L|K&_QD)NOq;@)lca339LMy#l1{QX5HUu72coKXY0>X$Es zE5q=J!0i$8G#3<9W*yuidI&Z7Z!a})NC6Ga?i%l(HmFq2$H*Ef17Ut;+tSbekS@7A zQFM^s4=le-DMkp6;KAaTt0g7Ia9t$icF3`KWXb<7ar-$J^bOy+|GZ&|td|tTNy#jY zx(S&pillY&jS%IfIU^Y;u+@0v72${{rHATLzZ2yDpFiW~2bM_2N9v+BPMcv^*;E|k zwJQ+gyC5`nmmk_wgm#ZyVZ-%50_JPnT1Ydh!TtC!;qgfMGQE_^2X~C#x*TAj#33$; z_V*SJD6}%OCRUOMYZ@WGbONW~>plY`{f9L0x+1*vx}hyH$+3DEUP%XEKZ?FIhh0Rj ze(b~&#Ttn%e0r7`C!*J$rLtTvC&(d-y-V^Ifu6*sD7s<>B0p65uHN7rB&AaI(2tj3 zmfR+5m5B}%gdO5J7q&pgqBd_oC1Y&t()jiku7ET3(a}dcm7s}l7h{|CZurY9N(wMD z#~E8bZ~cAA@NfE^V_0`1Y3@ex9G~}Z^45{krZ5wG=q&u6GW)3lJzsD|Fs2fm$wOn8 zN^bIkN$1O2j~gc;>h0#M*Yx3Nn^RoByIvm*qd13{eov4iRH=TN+_OdpRhTCImkA>D zP_DeE8l5FyR;|i^>B|Twm=}y2z5kM2T(dI`yG3z_tdBNh z`*|2QJUgdFYm6Q=fAk}hxX?KBGNr|M09aU@3+^nh!+Rcwt6#7hqjHp=0XyA$vJman zS~e~VJUoBWp=L77<dd4@@C568PVc zy{7owu79w_OxsV_I^Ji(zKznE-P-DS@Mh>KqkjoNwZs|udRq!li04@H&CnwC@R?8h z%B8V1Us-x-CLWTb_ffc&vExUVJvqUrzmfYV60~KtG?1F&0o^5vIN)xO*4f=EhVBWr zDVp>w=)WTJcD_>|SvRf9qTk1ZfVZ+osJ=A%Ol(Ef|J)`OPcGivzN(1F>r#`VPQ-v0 zd#i|stuodowcBUWE|biTm6n(C>cD5`NYA?ZD9k(>a>j5(31jqw^G1KKk{zn$=bjV2 zB+=t-FN)P;VEo^A5%(o=xZCKo`fubnnd?$zsfmsue3^XyT8KLi=l32sw`@-IjyuZ6 zOj1@!)0BZ54G%OxsbScuJUj+tUUi*j>Zgaj)v2VKv=y>S^6a-#7BRT#*7yDM;v_jq z?WVozusmoyJ|AJn6pSZkeq3<5zzyWGHL8?zpUF@B+yAkRs^IeF3qsLjK47QQepau# zkEFzZC(+P#ll11T!;S|yjx%-xOta~gIl%AY;4lz~YbYuhSgM1Iu6zkosdFZo5n zySRfNR1xwNg&)>2qVCoHeO*_~V7(WhOXw0G`H`(n2pS?PxL!11#9PE@VyW zVdmxA-3>dKK)v4hMF9sL#3|lifqHhRD4=#@Xrcs))SY8u{iiXkZb;SsI>Fb=_C1!s zrix|nEOT!$@5QKYQJqnL8DOSOe?b|qiFTWs z`o59f)#0V`4HITBh)zFrm8WAJ;9vK>yQSwi@z<%FaSY59bspY)%JuKDupIcFiIPtI^MzESyH`xv(F!Jcnk?HKvavnfuTHIj8%F56 z4J@hukWXXE){5|i3uYKDl$;!4bL|f|PM%GzsiP0eYGg2b(n&V&K!0L zzc6RnsR3GHZ{FSYe!=xM^|?#vWePPEKmX z)sKA74PVP)&^P_{CSoo_tf|%wCzH{AjE^SCH61Q(Ub3W|RYo<5yq z9`~{)vr$K0gC37b{m8RQgs-q?`J;#mYG#LTt`p~`yX3+JueEq^h+%W@{5rhweXJS?QPSK-W};u|m%XmIfXUHk#GIfn30n#}o`@LHAzN{7Qu2C|a-WomVj{RbhE)Ru=V)WK4T`B`}=a;#=)@9VK_m zu`MSxgYU$y6E;#dVAe2F^X-!ZBy`J7r*PKb=H`e29<>L?j_85>W$LJX^wz+Uo_QcLqZMAdZF0eV<&|Xw?q8%ar+lAK!yds0}JJPIG~fW=)Pjbuh%5 zz08un6^zo4o+ut(JdFQ+`_-|;$cr-FUHe-WWQqB~30M780fHNrTJ?qKy?-u>S{~k? z0-vZJycCQQf!?ZS?uT|Pm~-dt*zw^6G`!C9E`Z2oG+c~(puSTKIuGhhKHV(@Q7)p_ z=DwuhL}>P|zVr-O7B86Kpi#u@N*9_GLdCI)qHEhrHxtWKmOtO*$b!^MA)@}J#Ca{g zZ}R%{HS$I$?`ez6M_{Ft@5jZv)UfzC*nA`;1wLd;?l?k~f#18XJiND8A63$>#b5uU zN#qHGlW*V3!15T@>AbH5zh?2oP2*T`d{ttwTs(CgUG%~j?)as^KG9DhpUcuPB(Bp{ z^;0;^?T(Vyk&MEpbhAE>h#r;vr{OIFyDhR<2=ArrigmJp(m%uXv#O|R#=_adD+q@d z+X_-=Sb^VV$$H%C0`TcwJMu9s0-ue&c+~lV-~eA~J@f)j;j&>o-?@5XpUS->lHs2S zT$YgZ|N2@E3W!hH^;ZL*9Tj*sSP+7FE(fb)D=xrAlT)6ZcFb6`PUrQjh5?(ZIK3Ye zy@P{;yY2WzACs7ilrH=pRKh(1&y`mu`A})IQFZW_2JV)YKN_|!11ui72lO0>=XhqE zGTHHuylR*kqgBO<*_TJ(cw|yydN5yXXH5)t(O;o6wTJ;Z4!Ou@ebRVu&P3zjD+V+> z6jLLc<%-c8KO1SwonYp(k|oJr4Fjn=h1#7}v2Bh^nw$M7KDRdRkN&kvR-N$VoE;+a z4Lt5D0=qLHXXhN#`Fe32`WId`b^ADSYYyER*97o;cy-NrT^Tf6s0DYu^uQl3dn|uU zxx?hshD{u@WoFguvG%(ZzQ zb98nGxe>V~_CWsMbv7Kxc4n%JqPiAu>2wZHysZT(KGvuj9&@a`*-y)6XN*$015F=Q zGQr~2>V@!InJD&{EbKg>i8qvA8|PbUp-4oP+TE|&@bpc=_18Ocu`=`7@RB?gbZxH+ zn|K`s8m0&Do8lXA+QaG%H(Mi|FdyMf3np^OIR(0{=9b73n^smx^pBptp>K4$9uJJO zhl>jCSYXng-qg`sQiGDz}9B=)F7gDYKDTy|U(ya5j92zu0d(Dh*LNrR?J? zQqb)gr1Im#5wuFwr;R^Ma6(@;p6hN5g2kp|{!~Um$W5apZaioSovC~Oja6BJH>v;S zi-}e&T68ehl4*ki50N|K3)V0c<47^{gWzTes%sdxa@{7-w58!_Ma&(Qn+>!3NsuBF!@h$5$0%VgEYY=+t%1-YNl&t zM&va1`uZHxAH}x7qO&bZV<3CTbO&+U2ma4TCj$7DU^%>~nsK%@4aS_L>Wsy-1bF9uCi7Qa|kd~jf2hAh8w0Qlb#HGMqH z30!(S$B({aLM4j73ti?d=;F;}cj8qm(X0AZU*F0IKbSvh3P0;0ndmUBYMRobYMrec z<_kfne~SI^hr=jF?;O1&=`gZ*U3k}h> zq@^eqf9}2P8_8P$AH%;}#0yH|(h6ha9@As^f|W1!+SdZSbiR7um#>Audqka~iJuc} zWRy9MJvt8lHEAhhta$hj0Flhh=ARRyie{ zqRD?0ocWb1KzUwmhEXjYUNcSE-xbS3z0WKp-6=j0I&niw|HyG@ z2K7fg<|$zK@u?&3RJ-l6GAWit(3F}6>D)c}2$F-T84z|WEkM-9w(9HJB(Z)74 zc>ZRutdF-G42sg{n5z<8d6|A2!~9g7I~l}qZ=Vi4`KagiG*u1ul?d;skH3PoN(>^J ztVvL-UJ}XWrVVK)7(tgt6RHm$Iu$yb1orB2ACLH?VB*r|iG$ZjxSrTMLQyV@cbLp} z9eb9F-@GHe89376MDhKv<8C@g7LsyW5!S+YdExwG!pUeuUw{9yaS9ylyC;41Bs7bzUk+~w#o(t5xh3_xA$O3w*mRB(XNFqt8$}SkvxG$}oRx*}`8ws_ zEClbC%o|YqP#qubmaM8Mx(e9`TpjCjucF19|BP-to*+^EE5A0UzC`-Luu>}S;s_6o zYG(PnoN&CF>Ms3Ne&CI3>U1W_LOyeZ;-T$mi0*RLiQkIG80dsMz*?Pp!G{uE|h84#Heiz_2If8Uk zy$E8@4M+G5NT5?)UiictUNqt(O^B7~!dc3}&&>(?z;`YyU(_TGy#^f26DZSwO;?fg z)ma@F+s)F6aXR2>H&xAaoD=6cbWHB9ON%Xo|b%d2DGYd z<_mgHgQer8QY$BIls7;6kD>h}XgGclbF@1`a7sEtGiF4PKDKQ~`{!e*$7aR!<=zR@ zFn2I`{Wcp`P1riVauYeEMIoPhaXr{hr@E?Q>Y#Gs(mn%>3Fc$$B7GuH$Bwxu zx@?kdUz9z!dC(5D5086`0Vbv)EYJMbk}jJ!xQK5j984%0e*MbadT|jlTHeydCKdOVkPnLIltG# zFHa!h44~p1<3UM*twuUO;(O4$ZRAAc$NpN=ObK38$Eo3em+}b?XVQRz=o6a~)Ds{0 zbj+p<-kfd}91oH}1xJ>+`+bz26M)1{^GNWVic9fwX*F6`(Fe$7(XHu$rN*2G; zsz2H5oC#Ep_rh|WGl_F_q(h)86lAQ_;>99-p_R-1FZ4D+;mQB%4WBe&6Gya?ggeng z=NkT`UuuPHYN0*r{*}Z&#QaCDTjl@niDjAVs?NLZ2j1p2dafgyxM?9L(swNYeIxq( zE1sHzfbck=ZRk4 z2Mca42jGrdR21^DhA{25vDopA5e`2cy?5ZcCPw+DF3bkxVarp?uz5Zmq%#?>D*wt2 zmVxTC3)2pGZfn=Q;3>jb_2@8>Cu9G+ zq=MOKKy%AVzswkWC$}f##D9_wcfLBAOnlGZtP|xJrBi@o8)ehIbYbfGZ>jGI)Ob2@ zfHG5iFLY0D=ajd6BtL_*{jV#Co#d&cb8!W!Kq%oaOC|1BJWvnG1=>jxjlY}YJQ<`VTt@G zYWI=TI|bnGAXUuGxdyEN>CW0dxCB+o|bh=wjnrDmWeluD+R9b#93hzAH=a6ViT=vAx5^ zBDNJdF3<0IqwJ4i?v-yVgqeY3F_rB}o*w$OJety8ipMUM;taJM5-xrE653T*0BL_k zm+d=jpoP6$cwp~dTu&!aKRB!a<2@^W9rCT^V2VB<|CoA5_K8G2Q_G4Bje4ah?H>)1L)fIvCO7u!PL?5eE=`oo*jN{C;vXdy8fW zzWm+)1Z!4^Uh?)n&I>$DM{vv0BUFs=-j=a$i{7o)0hOkju78yAP{N`1BzQ{>-Ua^N zlqjXb*Izyf#Ecrk=?-7+FYA}_LD-q((qeV2w`708SoVj+bEahW%g!b8HRoIJZN~PX ztgyf=jlUA?@hsz$Oz^|KpXy{w&k|gXojXgj+p9>Y=N>oo#S?t0V}Yzv!@tNS=cIGC zcqp(#PI5`2Bm)H4DiS^}5c{r{Lk(>gs8Q(EJKD0#rtmo9^&sWF3&cF-booaOIqZ|{ zy`?QSOJ4h9@XlOO0SdQvzu4~fgo6s-ibKiA;Y-KS!tP^NQR=^*^`c5^ytrJO9PGLq zsV-_pJ$6!skYx5vLB~{hMDx9$@|PSWwu)GPS6wGh(RFsv{p3aU`QJqy5qS`PsQl)m z5eM*Ki0!@9=7}rQKJE+sr*L4slR|8I2bOQx++k8U4m(adb+>kB!>f+2Nna*&(B3td zQym$A>O&tKD%ypR*7>$)t^XEzt~8oO-b4;K>h|6CdK7|hn`|%3N=e`|)0*IC=MTVt zQ$DZ%8687)5$g);-ZIQRd8OmK*EwuXKTRK1#)4T6=Tn%+%)myAyKF5y4>pus6s4+LWrN!m&!oT>q3A?dSt#na70h3edoU`&DVqW>q{omY=aW zl!ZKVXJn$PtuU5}<>awL)W8MtKII9&NtKC)JH(|456w*6^{_HOT=e|j^OgP}{?7mW z^h>QiTzS?G!2t>@gE?0UwJ=GRmuAYL6`6*~r)B8_QJ1#pK~nN>($SAsi^~?Yag*X&iR*F; zc<}sTPd-T4;e>UmmE8m&j?yK@=54QsRS$WZH7YgQYmfxOKS|&YtWf1&l zN*!;!e!?Bak^~*1zI7wdPs7xt;8SJM&t$61X`xLDMU;HCC$FzO4mn1T538o@qwsF_ zr~4THkTM4b1Jn~_U~bZqUpqG&OH8Q0>584jX@jZ>d95q(^6134j=MQ9Hc=k_bowap zi?zfHm22Z2rx(lXpRz&i! z>d88Mp{>kjYz@60;Mx<{w?g0T+H+Zq=TidpE=7?E)fC3qvNS6%Hrm~fBj!}W6)weY^E*Uq88 z{dnx4pg#L*BL2xQ;-oh|0p|s#^c*v^FtsJ=_CcK-+_v*7&y`ww`7bNwGS!rX*I%U#dlqaC*X`4 zjdRoLdy-6vdy;Je?XkZRe0PNh5< z{a#ZyCqEOwxwLM}jXUe4z?g?m#Rg1qa^t8eRctlJcsQk}zth8D?Q99YbV~ej??7YC z16{N;IyXY!6%HIfjAcFiO<`c(Yfudr+(|=ML_gW0T*;8%#UkPTAL=U9xJBs?qC4JD7tH zcqP)dc+D}{@N#NaHZ`o)%*CId5WOG+IoXF?coSsyqmdXTu_U?P9JoF)U{ENh} zxGK`~+dnPM5Qjip9*K0?S0riH95*sQd{T>w< zs=O?}Uh$US z(~RC7V(T#ij=1MAXT@{3Zn9A3dmdS;EwYMYlXrwp0Xkf6w()l{!NkExJzLKM&{g^= zhBs6aDYCZ;u4o(KPlpQ`ER!c;>G!7qsgn_4T91iAf+28mJNgKZCE@E?z0no>EffMZ z-hMwWSb@(q1XTEPN-;=hk(D0;}SyGr`rDfaXyuTsOT4rWK6UXU0$A zp+jZ8_wI83h$~~*P<#HiusZ0S zHI!_AU5+urb>{3ZO3^CzV@9|vH{KmOIH7!)56&fwY{^`%h9aedO~xZtkbPT7q=-Qt zn1_YWa@|r!Gm6gWO$A9D`|20evQq(0*_oZc`=bLP-JPZDayteLEUlPi8l&-)y^*tr z8Kk*~viuxKg?(1VA)OCXLGi<&O6yr^_*5y^+-H8A^+F`Ext55~8# ztd`Mt;a8?t`r789Q2BX!Wv{^nY#%c?5YBiTrfapwQa*RXwGYV|YoTVi*8ltF*;P|8 zrIl+rsF?{i+qNFj!|8CNb413-vKl{}u#&9Kt;AjGr=reXR0o@*ai{i2s-tqLwVaXK zEtILxds}w28|^1A8*fV+!w0*57ptenNWnhWHcoDZ`{TBL+`czpv6w_RPvqJCsWm^R z7_i`t;4$5&H%cM+!-?LVlqJAgNRhs%mIW8y1;+o2NP|tW`%kPYH1OL4x(1$Iy6`KG zQDkj08f1j7Am!gSd$KJAKaAz&?73xN*y3yj(K*6U zOA6Bmc`8t=7;q@Cy$}ob&)lLh&Bs53&o|m!l|Vu(|HM$cA_g4HR+wsu2frg-EvBJy zaDHBp=D+zeG_{>be&kV%U(Yy=r{$gneU8s=S6m(Nlnl$ri{1_teW3WLytWNRqSY&d z+RuT2*Bhq&+#V#d8t?W9q)|>x+j! zul4z~hiGyCZ>dfjzK^6{^SWf80Zm9t>&tJ5)51hemR{>C7vOb4UHR?AU}%0TH58S7 z9Insv2gd&r#-GPR%y~cmCHLg|9%gnV?rCp>vNZ;ZP%4vr_oGWb(ch1>x^~kVp4Pi( zX(XM&T~5{IW~FgB!^>9Hvlfk6{VWPI4C-({_np(i84YX^b@VbV4#Ij#k5k20f{?52 za&ntd4k#uH#M^~rgRCce)0V0(_{y#JJQCAG>NgJ;crWFlEpx$2Luf8iElqRMX$oS? zi)!y`NhxSmxw!dIz7ppqrT1};RS>>yr4)vX!O;6AOY+%~VAw1a?t1@M0=6CHI~RF` z@zY3*O1Ewq`al1*)$zInWA)w|miUB&_|^O2f&@S4Se=e2^<_2qW;|pTpiXdkqK|!E z%#DKf!mU?UJW*hN&spuckvN{wc9LQJa}3h*yy^GM*Ms@jU+tltb)eyS*Qtl#${iaF zdgq;a7I(la$Gz#5I6$v(@6~7p)`?$FYL!;N%%RbgVmEQv@z%hZ|578$8x#~!FI~gf zt!|-5qq-;*mnoqzr3b#1K8J^bOF@Bpr0dh^VhDP4t+3_cBzgJBW=7&}I($icAjT@B z7UHED)V=I$V9NMYYd^mk=sjCJx?F0GUU7o;{h@bI{Nxbp`@^@<>3(Ch72$Wxm5nib z(_{(?oYYyiM=S8kg7U`Xv2x_$pcB&*Ho?AFsiU+nji6UmaE9{fEwE5=tI^Hr2Afa6 z@|15HAU$ux>*Pi=_}rLJGuqvav2KbiE!S^iqp0G=&Sg{FpZsU0M?n{6d5Kat^KA?X z>iFdz-GgZfRsB29>*IAkv91L+BQTDCmZl|^jjij;eksaX=%%fCPoGy6H@sJSvbMB< zcdxeTR9QM~%bQ*v;YtT~2Rq(vKP9-ExB5na*kjgLUgS0JDutr_e7g@^rJ(bKLo#+X z6NLsB;%CM(P*m$r)Y2Uj%y028R&-g4ymz)jy6{I4w_F#LQfK_?yy6PC5BG^kSH;4U z!Dr&v9ufW9>7nWT1Kc=Ob?KmezXCYq)5Q11H6q&gJ!@L3$K~+v6`pG+(B<#9x&E&b zkjZ67^Cz_e3GumQi(46b1aU7bJut@b^7W@dQl{Wcb%T0s&j|j~zUlp^Y#7(iu7{M} z)5Oa~XJz*V4(fA<(LOXs1umb2r~N_HwN0gPsX!{5x3=^NtE> zF5#shVcGj}xmX8%LYQSv5xj@kHfFs(m1=lT?mL|7R{?ha3cIv5PGe8E!OK1Ews7dn zBZjQ87I<=PGStMg36^{`5}W;1kvqQUPOh~oQ0=8ljnEH4F_-w*z>xr))zVGOT2RKi ztS>+JUL}E{Q*4IzzC1Wy6h@vN%z_WS%2bVU)<{wHl{r{-Mfc^L{y=@Ub@u&NsziBMtSSQ1%HF8M=@&{qh(bJQS4O z7+V0Yp^61;vpMh~c|7dkNfW%l)wO&~*bIV29}TR@Cqn2J<=ldLJXrq-p?U4dfR8xu z`-HdiK^f({gu%EfoE^N;u|;2rmU~r&quy|V?c{Bv4!UvjZ4$kC9Bmjnvc?C7?g~co z&QnPfMMBUX+G~0Ho+LV@$Vj9@u7wLHi{WGR;ahd?%8-!rx{Jd{4qwtA)JXYw z2md=>wslYQ7CLF%OO4SSntNAqxt-Qr=bJ{j4;1G3Bl(ja3=tC`884#RN^Jh_mKQp*D9om+=6*{G+`l{wadxqSK~p2;#QIZ^ z;z;4USj7Z7b~fA1D2j*PHOfpdC&D1jh33|pSTK`U^^m%x3nqtmC{xZG;IRKKgNsdh zKo>y$pO|N8Tj`*j>yw16Mezt#G41TkjdTX@YeY5(~quV)XZ)X})GfH?)GrsLyF*d}59*S;-z88Sv;*Z7CUZD`N?~`0ZyDBJd zG9@>cs-uk5QZZ;=1x`XESh(`*LZLHjE7(82{a1K={b$UNc3z z5_80ocTW_UYczUB?Bm?yzdk$F~>pc|p~SR>KJ7%9vN${k0O}CAusf z(<|VHoQu+#n;fw0^2c3Op2#~`fch(kGGvpr=L{P!#w5X-tM-g}BnSPx9XrGM!D8Rz zO7iqIG~u{Zc=TKY3fAu$N~w;4jjOqv*+kAeT}p8N8W%Un?l9>mgt_>@c*~Wwxd=aO zK5urtT!8ra0P9bJH@eGLxX~@x0Dep9-X7``g_?EU^KAVRX!Q1o!jOIr=;ih$eqFs;K=q9G-77*&c(1w1 zr&Nj!oPxgi(J>Um^~5YD|FcCvjuG{gi?anKBbVM6&uj?(T%MQ~qZ1gWef{?NpgBA` zP}`GvOckHUa*q1VN`mS6qn_@Onm9X?dx~neF*-e5crh`V2RcF#u{^(XAXUhZL)QNc zssuT<`U=?q!%O3}K=uLRdAxD)^~-*kiJ5Z<`4t1MGSB%Vzr~{We{@bs41<^|8mO(z zGJrDo{>RgK##8-=eVnX@y&_vSk(rTmSxNSuWeX)CD|>|^E1S%0%2tXiL@J5U4k<*X zG9s$~{ktFB_XE$*c^&6_zQ61GeBSRUZR0X$Y)vZGo^2t@&J&6Kk=2=~;!_auT`d#y zV+Jm`Zv^=|b(h@^O9yrZ)T7mj^1A`&6o3JP51h3(}2H4q2f4a+M zi}WSOGU|4)1RRl6e)~eM3vC6ecQh3{P(XMhPnYTl?)c}wGv8E)=e$pKlbMKqlvln; z^_T1TT>Ic=4t+6rUe9HC&Qt({979`o>oibwQ0b4lkT!&-y|ReqjzP1d!uGMv;n@1h zaMPqt00P&iJU)D3!bu^s(dF0@l)l>0B7LU_hfKbivC|&I2XD=Hu4n5)Mlz#{Cs{T& z>=RJYxR8b6e-~7~KeoZR?^&F>mUd8Z-9w%4PY1*+Sv5pHY6nB5Vc~Vhf4$nA^DQ&i z*oa)OuuS*K3OHR#v$E%KIk;+7)h`T?kpI*C-ZL*${+~PDW*d6{(U)V$cCje~N%m3qhI0%1Jt3uhAOqhRpX8U1uE{Z6abU7{C z;_L`{DJ!|}U&3JKx%?2;4;X~mA&G_4UVOpuQ9Sp)(e<|c1MCU~EmtPBY;KuH` z+ji0U`02FGxG6F&FYBv=tP*mHev-#AQGe8gxgZiF)6ZX|&5MWP3?&=glfwA5 z)K);#a4)8fZ@1^|xZ!42kYPgCS$LxUT~sKC3WI!x#TV~Nz|+qhp62F6F3j)OsY9OD zP?Z{7LCMR6xH0t&_XqBzLs1+H+PYY)s`i`79p(ToxW2Z%V}FWMmC* zw;|n{JCm1tQyg0UWChLqNd@8KGC4C*nK*X4w5x(q4ENOf6umktfXQyfY&$N$dsWyq zQw(!Nu<$V}O(~5RUP*AD47nc&ojI$GatD>b#O36hQ_~vYGScMZ)=&*|Q^BKbXBzOu z@;ft4b^%=PS6M$Kyi5`o@7Y&am;kH4-ELD3U&exCn}Sjny!hS9>1I(^DKa#a*H{7 z+8o}D4^EldwZS7|kaOYc9h9G&i~T|P0P0;U>8K38lhj4F7uYMJpuO~nr_w+?)|tOc zx3Tj>xS(~`StkT!E*U@j@mn79qoNO}cM)Ft7GcMG619-);kcgZOaUdhI01-oZ~zdT`l%Id4Wm?ylnWdApx+(yiM z-23W_?%+``YQMxxz(2e zJ8L7}f38UZ%?V4@$ zg%>(9!ki|pFz(vwYjcqdJWxLU_qJOOYzU73iX>OS&0}r$PsF6~4BRu~ll$yY0kX9$>X*Z+v0mqklC$eRc=CDa1IGnH;FzRjUa0oMzigiP*8V(9 zkE$5-PnCiCW75&I)f*U}yEb+6B+4fhu>xrCSMORHz+N}HREM#)YjEXk zZe`P<0<4|9-RijaIIN+Y)O@HmY~MZGRrkIde!sY3^?Bw#T4)NB?4KJ$pT)cTnkOt^ z*y-%-k*qve(e7?}e6SeDA|qu!Iy{WUHnT0R8Tp4-AX z+D|c1OiP&Tx-RfEMt5!QP6z>=`Zmxfk!UM(hQsG)W|1t{nJVpHN^UEeXm> zL0-~iqTm==86KdLj7s$XQn@oyp@uOcowypHkh&OC7heFd>|0s6^^yXOY|KPCsU`63 zuMQRNty0w5OgHHLS_yjmTv1AowBfK?ch=}r1t{5Qeh9?-B>NcOkvZD}371?SI=qyJ z4}%(}BzHapdDYm>tW?Zs$l&zZ%7DEMr-auUl+cxNPEVX(5%11ddk9aa;P;XwxiFb5 zup_@&`}~F)=0;D_{!0+YMq#tyMAca2aCqbQnb=>kI$HC7Y~w{2)9N9b`~0ZSTp63d z6^a%5lcP@OV_`~)KRzu(5ZCY1{r-Gc48tkmr~B@ZK5yN`MmXDt)3IO+v^TzUOxjToN`A_ZP&wdPK#S~`&;nJ z!w=#anMbiCuJu#0`3W?bVSbe|;fbT=uS|~L_W{wf--gdL+u@%Bg%Ld$t?~X{mW=lo z+R!t1=%)DdI{@l>Ev9=2e`D9`tFq`o%(#vbz!#)2)T|g zW5b5n3M(O8a;VJao(3v0KB>36r;gm3@%+>7SFux=+a!o48$QZTtsK<0#-4ik#ehaQ zMt*ATVR;r|09SfVP59NW9-v}OEg9o@5736oFA3%&Ac(dpNwW4%2uXd84Ii*X%+bH_Ls`kS7_xi@9*&ACj0}}($o;Po4tOaIC($@!Qw~Malz{`D zs_s#&|6v7W{M>J%cb~(rT{BkC3hZFp@ZgCoX-k+&j55#WD*!tVR;9F|GBi2l%|vxj z4t_i0x9W0H*vW=hqyu?qGo*NTF1{3A^L;n(T6Kn%nW&bD@v-h}T~? znah2W+yBJTDZ!xzgp+yxz4kSn-P z=R;Bv&dw(^jqfRg0oKVg!u}O7X%f~Qol}EIzS&NFftdfaeDHTUZ2{A@G`duKo8i-s zPmXMq?Wp&9m0`0=6R69pzLCiQ?7sgzeQ+@ZZlO-A@KzYwUfO(6K=faJWsRs!^_QV$ zpWyE^FSQ|T^a4v7!*K|6X!@*hG6T*T&YXUwkd5vq&waWala58CDG!O3EJ)k9wqKS) z8}^cSoMBFD+66s^$saVtXpj5#>J>XNt|N%$`A|1lFGRiZ?@YI|c^HL-^* zpU*!VNX&D$E6o}%Vj=U9S`jA-9QotU))IIGU$|PoV;@Px75z@nIfAFRI<*r`TWk)E z$7JUhQ*>ZnZ&*@)qYLAP|9x`4dJnGA{P6LTG=i4?R&VM=@O~*AN?v;taz@lqNqly?lF)j2{-0X*5GntVH>0$JB>opP> zIt4Te+~6n*130C%rjH{5OK!1ASKcL+Ad`o=Ud@uXZl>Bj44 zOziz2?qW>KwOEi6{oSTPO2TjS8v8Bnji5nB#=w=#2qHf2N%~uO6V7m+rRZrU&h7Vn z?Nfgo!Bs%8I8etG3a_%OOdT#EdLaeg4{a*YxP4eZWr!VU>eve@9jRfYJFe=orZ?vQ zdfw~)#2*ZG_o=0(Cqo@K%R&2t8Av`dpp{u`09lcL3vCY=!r8v-O-UAcP%n66PP*ng zp0B#rq^+$AK1q934`0y&W)87@|2KZ1mo=E*>~;YKdn0@=nF-)0+md^aoW-y&XkuiY zQ3tdXR|dJ-)bK_>hmy@vZRF`&N=qR4!@&=)nvU!YV{=yATcw0i7_tbET6K_x*j%B- z&QFK1sjr+a9&h5^$mV}`^{t@#{!M1a7Ax$xo^zI~5k`hz&aV?DpZ7*26f72X0FIn{ zArlo`flb#Jhu1aZKy)^m#`>@r-Z?^=5Bctm4qbyib{{T-V_Z%5zM|7O(6zt5=@;QY z3r|!BeoO2-%okB1m4GVOep5HSa)*$zhne&-EI?=QA#5p38tKT)mXhx$<9)OJm6OCr zxV!z=;Qd8bteCB>oW5d=?Nt&{Cq(NIQqN`=_{2hd*i%dMcv);Ili{c76ec(rZj6RM zGEi)c(@I~@uqi*VCW6>swOxAt(F{M_PTo3h z%#2ZomvdA4n828ApjD$f9(Mf^U3ouo8lQhHy>upw@Du$i5z{9)>_2LKHwzP}z$=n9 zN;a1fPteQldF|(fc6Wc0Kd3(ikNH1E*d*=3sk{Ms3k5l-{jE&HOj8EM4P{Ts8=_Fq z>bGY*0~_A+IY%ZBm#Kkqf12o5+&?=S7g8D}{-hBG%IK$y$_$cWfjJ!Mq|2)j@ z%uhXVzwOE$aH0vuDK0D5m8Rk9r!MhfX#q%_W)V7aR{&ezB!ia5B0p#@L+#)<2op%Jqvt0QI>)BfaHtCrT_nygwX2cCD1qzdHcF zp;Ub---x|R)jB)x;bv$Wr(<2<&A^qhTO*7nAA8p(ZVLNr3jvvI8|##0JUA7Y-HYpT zNBwT@-*gLkxNf~tN_yzJ-!+!p-yf-b1S*J}7dRMR@F6P~#{+h~ZErL5@cyA}P z!Tx1$l%=fwXhud36E}4?~#iFERyPKq&2Lx<* zj@GQPMEr!EzqSazp;i4nd6n8rF3pRns zGAC8TudA38I>c$tE(|Hm5flnqy0Ghx#Jx+Rf|#T}BJkpq7ItQ-l+j1W5nhwsl$)U* zaCtF&HLxZ&`L(aBm4#Gf8>LGpaWO#mU_RWj0E=ViF z?U2>6PspLre`>D+4D_)q(H}Wksy9QAmbYppr zz9{le<>+ZEF|hdAaG0%D391tXetkY%ig#YV23OlicwCtCXg%PB-4$0| zw*pe(74wUm-1p95#7EAD4_>fBSlbCzr2#b%N!k2fm)ZuFRrGgPLbFh5&ilFbN^bA= z^SS!DKLp=&X6l`!MJ5cUoHKg5;DR?~+AqzNQGvgw<68%5MwqrJTYgy-j&6tZeN5+_ zfcDd{JBJWE(RajDF;i}E3)}sT*Ru@7ySR>Lzm3Cv2QP#^)F#1snL6FqoX$Y?kL<|a zJ$>Vy(ecg7A5|ASx#6w6ew4kBoBfVLry_hg#`v?|8+Y= z!B$$a!PgJ`(8PSfFq4l3Qb-!ZbljKlIGvOJ5%ZJKS=5wuJ?3|BVZP=+J6>s|V*N?+ zzx)C@(>A{DO?$XsQc_3U#c{1*Wod~)8Xd2Wx5q4m;hyz7Er%DZz|subo-}UvdZo}D z(y(O4ZQr46`#3*TJgA@@vFHTOouW6qZ`YB&IP6Nc?K^;BD$~VW?ePdV@=lW*d4cqu z>Vx(x2VnB>sepf90snH`CYv1gM+v{tFS;GU@YBXelE+v9pR-7`PakstGMiXJoKyjQ z(~*Bi%#-n;hWntr z7RJX?rIBNFCono?Z0FUsH6E9W>tFa-uLg0Mw3*EDW3^=0s>FQ5Sd3pL7oUAM??^dOJzdXBAHg5mmu!3bb@W%Gu~B5Pj_hML66@p18d}AqK7` z-#p%18Nid;qTI2+eIPpY*qxcL9N)@L`i~otK+VEE?a`70C>U#%ZIxAmnH1~lA%Yh< zx0n<=y+qtowB+TAxL=aCb&K0WK@gRWP0Z4==n?O8>%^)Nk*|@cn0_J<2Py9oxb{jM?X~1F10bCBS9hM-|kQ8Hn$bvB$clh2~_9c^e)^Rqt(z|oi5mZhoc$nhqleBiwhh%C^LISibD>FAeiXNGHFHZbp!$8;hx z@7I{!6(S61WU}b3Yy#~&9~oe&#(u7Ub^6w^LWb3{k|7Am#%j)pa zlL2^r>&>xq;mf@RzO3)Qy6=G}2j%Dc>f^EaM0@s2M>i0Swtd>a#0A{7R52?=??j|A zS=`?*0Nw=BYP>WwCH6FtL8|s2Nmcw#-rLvyk?!Bgn(8L*Ni<|K7kmu7VW5V#j%HaD z+Wgqw*PaJ>Ce}lK)bkpu_i<-`PxOVk2h-1mun=;)8wJj`hNIE}yDB4MB74&INI2{} z0ZS`=l4l)jKviO9YUX1CKKpxc@aG{qcps$6a0wNFzJzUIzQ`Zw*yRe%u8U*z(75c9 z5aBaD^|9?ST)>+KVdIpupGmf>Hes5hYGB|bYMHr~1T-fHdV+)9P-Cn)J^0Nfynf?Q zrAwm=1Vn8yxp%U{Y3IM~7C>+;TWJSqI*-?+0Rafl5~f7t3@g;Mc3&9^ScfE9J~u2nW6NSBr6T~Rs)j8|^QlqVG7 z;;ryMeN=&cgm@J^B>eDBzO-jLxa{I}XD zR-=`PaqQy|Q+yVl=f=O{p7(>Lq_&__1LhdN`NQDt7B5T~c2;|D5czm6x-|b6ccHMo zYEUAz2wO#iHbX1v@yEfotoC2l&=DMU-v3G`4*N1uNB8FfQ2apE@~X)WDckN-WZH}sbblA1AJoc)JX6+VP7A(x{^`u0iQC^v@l(uM z6sgqkrm?;4_ofTJdDzbTZ;u%?(aSa4rN=_Y<4--&w>>aa(TpjCn*lmp_m`Y_rU?f9 zlu9hDnQ*+vtJ-A1508>}_qQ@`_u88}R%=Zwz>HHo8Ly)^=!lzW25rb9L)M&6N&ah+ z+}J656X%hq!<0L#_G)Cl%+e zL)1{RAnm*$@ArgC%1zB$#%8mMsP2Spt0s3*ktoMwe%q`ouu^*?(5k2@{C zbe7G~n+YXnHFhJv5=fa=?a3gkfl2EhvL1o%pitlv){>@#4>yi}+v@NDx+6boCYtl{ z#}k)2?Jg@Mi^+SgCoG45s3XKfm^GmB@mBE%!E8{ZnPT3@;RF{Suf9)|X2Jwr8eJ<2BDd|lw-!pdC zh@m(f32wSa{zn%Jd3?#T-fxkH{kh9k6xe}&;<)+Sq6EAlTb7cLVU4H1RVdwj@{F|c zE3Jg~5j|w)G3-zCAV+zdO;M}!TO^9Fd3t$`L=IrM_B3^!4zP7uvxYonf*I0`PKW|G zUi5EbIP$6j>1NwjwSvy!w*z0#KP&%964!Qc@SvcEU5RH8xh@i%_T)ik&XdOYUe2Fy z`RSkDsm%CRfsRI!MEp%PmwWjj(AV{z?t(FBznlv+UE;*E*DK;L71@KN*n_WMh&<%z ztE1=0HVBWU?r*NOZ7R4LLw%L*p)NkDR`DunuE8HcMjB6!o1uHm_k)3bv%S(bd;AWQ z4ih}Ab0OtEr|{4Bj=9H=g<$;YlW|dPH;|Y#oHh$ogdUYPmMHTb(mlz0{bWS$Z=F2v zVA;1D=sCFSKxLydF*h}M+%_l*S)n;&tvP1syz*wGiM|=r{QAeT69Z7AaKEM{BQslM969gMf_LYl#)7#na*psl>P=kf z%_ez|`L1ffhK=MSTl)wQYpx?ZH+vWk8U$}M-B~43UjFl6Y_vkaTeAqFNUvviT%-X;B*hzexJ2Pk3R zRh{U%=gQbDq^|7tEgPj?ZFWDouZhys55|tXXM&h7_X$jqK7N0ZF-aQo!w2W{s5%bG zV+*h8CX+4UllY)}a^1iNeK;Gr$5x3POyU`?=kZe{FZS!*?}?mf&lwIf;}{oQaW~C= zEJy}pCo-FM9V7?-0da{OeQwmN`M9_iA^_(WS&j__6krWYfAF>%!A*4fHYInA5nhp1 z%FacypzswcPSdMb(Y%I%fuGJ4fAAu_RE`H*K{B2L%%wY3kTFIQaOzhlMNx%II6)dO2%&nOqUP}NQfU-mh)8mEBTX%VFis>Ki|oJJ+{ z)D48zdXtpu{_7ndpxOM7TMV~%Hj-&olTrCszOr|QE?)f?elPPrJ<#+TCJ^jRyubLP zM5wS0_tu^Fvak)rFCrX%Pc#JJ&&G{i=Z|O*&j+`DweBmx18-jZGqtoryR>|oANGT0X38JVTWIku}t^lumFRn=l zIEmt#yko61Y~Z1{tzW+4j6dG0oBw4QK$H0a1KMkG`22{`pCn>#@Q(88VeTy@9J8_* zQKY&8>eaKnCP(c+f?vRo(x31Qo2o^JZqAY3)yf-u2tEgoQ~oU5eN%(hXN(RCavPW&nyR-5%H4g$}UFHw9` z2d|~U9Hp3Zz~jy`u{%-|a*Q3TqG;(c&hPOuRV)p7!F%Bh*Ian+r|o&i%n_>jMLtZ@ zvZAt8@Sj_90$66UGWYaM73i+?I5oU>fz6r-zkXvDG&%ERnf|&Y&idcKeA74y3eq(; zc5Umyhlz=9bMm>~OZ2w$KgXotU-0Fy?{X=SC%v5cuT=rx9yuUg=t2P=nSq_++Hw#w zaO+)lsUKXPIcL7xNeAtxxI5FEcH`9-on!fZLNMkfd*{4uAaER6>@=O%h05nLMZG;6 zy^3B5vgZ}pfVZevJxQMM1DS~mJjgeNJ)g#hJ0b-zK*C-850RrBDe1YYdCCtucbkei zE;z%-IOBycDT?r0tLH-jG1uL7k~Q-PZ2^`ERX*v=KZm>irbzYpkRgNVht_}NWH8Gv z67}^&C~yRw<23nk1b&4o55=p}V$jv)L?^t})ypJC^7_5ge$fQFg=WE{d3W6JKzsbe4^A;KrdN1ty!}#vq)q_iNdI;lMs&qhl#y z1|yA0oXN(&dxwAcuEaO6z^PT6p4^=@V2%k+W4Gl7I-|IO;czC@dO2(2T0`({=h9M7 zbk>57Gat<_4sD3O9zcc!@A!VT$Q%9gQ{cpG`sbZ#9;7g+1{$6>gWP375}6b&zR-Lg zYT2#?jNc`$=LaRjRFuMriBU#;wCT4nd10`Z?=9EJqNqMtNql9WElq{7#P)Zj0)9Bg z-gn`B#h+dyYq;{^hdLa7^Wo;PACb6!E_Fj_MINJvBTwc&WQUOUdA%GfNh~4%d`+3D z6dkUt5|1FRIP9wW{N)Ko80EY2gr$sFB{GS)XzsQI_v4rXE zr=oH&mwuF~-G`*b+f4d4uSOZA+73_7fo z?@lG|uTc|YG}w3&-=~W_TyrSHiQa=I!u?Jd<++^q-~7*B3A;4s|{^%Wt!^wuQW zQaTIV=MOk_vCBfoPp9uUxma-5WP#x4DF^7Zop@qcP!DGxHyqX@vw%9=>zlg>9!Gvc ztF6OX2iVya$6UpqOnCCFB?1%-;8K8%|C|gHGQM{9Z1iD*Uk+bh3v5MVZhV@0m9_w; zdj!P1u4IB0=fSZIMSXnlU2Cm$qYk6jd^#P?+;RWKarIUb7ffwGkJ~9VLggs>3s*z) zF>~+ESuu4R%&UHu6%}*^>p0JSv>8^xq+pMXnOG(uTKzr}?8otK|Mgej>$CCARmJH* zMnep;Y0BIcxkD0*C}A|cvlq6`erC9^l!H|j!*a3}Hn@544P9mZJCe$#!Tz;eT6ore ze}5cx2poQK&s=-&F(^5bbn0s8E0W8LB<08^DSRmTVM%c89MbZ#*BU=&#v_uKEY8dS z>J7f?!Q&J;*(*1ZpIF9|3Z6$lzua85f>nmZZMkc^@bS^edwnYE7}4X!r>mR=`vqV5 zq=`C!h8G|$xp*MV25dd80Vh zfj51g6mVqsr?21E4`USR%i_G9h#*Q;bv+lK4d?EP`3yB->$HIKW@$D!DONiYT{X#v+jO_p2nC~=K zN7hqInF$olP⁡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�)?x!kA>O8ZKKzWx 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%0DfrZM%F}TsOjo_?G~Id*EW;-M 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�o`vPFj) z&>%b&JQt&Xvjb>lbMMtIR$vG)O$c;30aNKV;fsn*5VG&Mgzte0=slopU!&=R-u-tu z6_^iz_Wzs zE!%R7_yEtvdv@`e_OL@JosFR)0PX0%c^zvy2)??HL}d@wLg#>ey0cC(sN6Y}$bUWy z6E=JNW;x3R%9^5mS9e{23%g#`i@h%amj?yTL90O+P;tK7P_Rl%n^x+?qL9BAHDUeiuphzwdgboadQ29Zc% zD|>-nXk!}W0r5t7B9+KLVp@dnM|mW>^gN-vxJ&WNLNzp{8LP%y6@a5iR_7)~Cp;wL z{3Y029CkIngig_Hkc)g9XH*vhl&T*_8mn?JOGZWQVw?>Ws7_ogcD?}@#>w35P1~V@ zogp`iw-GOJtEOC&@`Kb%-n7>#h9IIGlV}4w;TfNr@1f2-%>0_Wo$|a699MZxoBBK- z-dq`W&uveFD5i+;h-n{u_56prESv;=-u-oFo;HJ*puFPD=5ly&U3E$3+IehS7IA!a zEeb+zo1b=Uy9tcfS?k7Pu0ZB)p|?rYHCWxg+V1r_98w!t7WcTf0e_`2Q+Z_-uv>|| z3qKNo(NPC-w+^Yn%a~uYOs-+TyS}CH>540O#e9}JsStx%YgZL?`>dgF}aSHNi8-p#QBXE0RKqb7GQ0Lo+{P8q#w1KP@MQeShb;A1dZ)n}{(-&TKZ)M zly4T6I1OWA$L@PSjyd`Q-A8twgUgX18k^uA!r%$c?%9tHMg^d@N|fuory*R&hCB16 zC9pykN$ur*8qU0J66Wp>#6Bg@bpQPZP(|l?Sbc=)>0sZnso6L+dL{~CF^A3Evh zgGG&l;KVU@{}TqT5cOL1t8>69yj(cAoor{v2IQ`+i&KhE@8|-7^9T5!s8(T1|F&JC-@Rc)>~8#+N(&tJ)X89F zu7t6|qEPBeUu-`l^Vp4167DZE#&zAdM;+l(gYF&5kViqWg)HDa811}$GT=roq!%1@ zdsbG2e>7NBc(3^bV}{btRgFGC!F{)0ja-6)@2Un5*v{Z?E+OwwWV2a+n%Ctf`chc1P6Mk-c!;O;iec{?71O9#1cUVUl{ zz8{8|A4;{siX}m{NXJaRfulUW1%JhQXc(l#D@f?hsbOvf6jg0X1?Ts*W1VgZFUc+}mrBuoo09@4Pq#E44yC+oX-r zZ0+%0W(zWSe!ijWTYoCNjte>^_%srDGZrd$iv?gA?_0j%rk$9jbX`jJQU>7hzx-Lx zV!%T1ZX+GH8Jg75{N8zMJ9aIa>Shf)qJ*iC=gG6onB?>7tOI>21RRN#dh#q1m<3sr zXZ|>$uTHYQ_Jw_@xZPf5XdxDSPR`uUI^zeEtm1Zy=R>f@@B6(mBNdpOKh2hVvmDf$ z<1KFA&4x}xHu1^jP|T67e#!q(4wNzVtL@$-@EBco+x9FNC^$Deg%kyVs^>m=@54?Y zN^LxHR^1kb7qs>fD zV3*mQ-EkA{5KCq7<&B&_Zr(jNv3+?v_PWb3aPk#{Wx!|kGS4)av-$gM>0|)Dt1=n+ zHX(`=;(xrV9!EpZT5nFLn;!&!aMvAL^~Vc8KBfMg;fA@q)op9WHE?*W!=Sjd2;{#g zh}9-%;+c9niAU!HA7(-tLv3v}Sqwa(Nm8Gn%Rz^GlD>jj=J4c#ch0`X0dVr*LhOl6!QW&Dp+M5y@$Fa#^eOKz9{-bzZ^bI9=3Lc4;DXZX zS-O0XnZL;zN1g<%l+bF;=MOsjIbZ$Ybbxy^CWDOmVc2)X+|0>E7cNhmNAq59gpZHd z>*kEk0T-1ORheHt?r^_$lHbG;Y}4%)d&ruhovCQ8nWF;czfG+BRYfAZVELBk zwkW7dQLd2n)&jj{$IBI!VrZ>arOEtSiVIaixm!9-pwzHu%O=qQ=-wRJt~GoaTIB~` zTrN6?*JjDKRqRlOGK&-JTU+blLbsnOL&iCvzWLquZ=xqEDZBbCbZx<0Of5Di?{qY zV7Gv!zu06kKK~i?`nz~GzBy8&f2*JnD)k2L=^w5KhZBazzkfR8&UoI?LtAXIh@ZKl zv(N|DVn)QO=VE~-#(%x*S`6wxz4rTbp&vH+gw|TvoCmge{-H}|9k9#v&~NKYC3qw1 zfwS6$EL65(xhK|M4VqpTCeLeifmOLW_1f=PbRS5xww??^cGVKo7M=honmQL-%$p30 zectavhjsDp*f&>ZMMY$ec=E@hF#zh$C4{FE?s(4W0uFBYZMXbND z?a|gERs1!}JE?tI7GD-l?;Caq2GM}6dzl#0pmluWp_Wk?j@jCH6-xV|@afyWYvQ?} z7<(xDZ%PgLCM&0P358>aZ#$pojsWcL3gal72m^8Bzo}P_WJ4KC;Om_p>PU5~X?!M3 z88b8MhTePo!rsBD-K%ABFnNnZJ~lEIZ%LWY$Bp?R+`Q}iNU0jg$=&_!cXdK*8RO$< z#AIGm;PvXp~^7wXw48 zE*x?*L3Zt{GksfB(Ic-%x+KF6cox4lZu{X2Dlslq`eXWVCx$|{aGMio&+gRuQ-X1arDh9Vv2W;F#Fzm$}IezhChwN?hGI zZVi2193mRJ>V=2*aci{tR7o?+9SU4(Qr%pxsUR2gDe$g^5kBHbDek$fhD-1A z`p@)O1FZm`djCx?P?zwHPDcmKd1U!W=dLd9(GgoIHw=TJy;0iIuT$a9o2k^fTt{4t zTH%y`qK_&)*Zh}??V#e$=j-fdesF>>Sj5%V4t!_NMYXB;!ols4b?@R$vAN5X!cs;R z*_x|c?}gceC?lMtjNi&`C7Oq{nWDCCs(*GFl`Xhm>s~VfVx5%*Cp>JpJ=$;&XZQS%f>U^iM*I z>$V|jH+%57cq#SayQBEZbg}xgtSoBm)h~MRT6yFCME7!eoeYvcPkn!LUm#>W&yUO9 zavDPQTRfJjLNIz#@Q3yV-;L*yV#IncWP@kQB5NaWHIyp8xVY3Df=AZZIaEO;mgEsv|LJ?Dsn z_j$Hd+DG!?olOs8Jii_ey)SApKA?t&az1x(u6ls6>{d1>lW6ce(KdBkF#@L!uWK8R z`r_*c%WlGP6)=`@`upC+Hi(X1($J+XMDdyBQkwbAD8Cq95`Q0Ti)9oqL(^LgaNuiAhr z)#gpt-ui%?FIV@-&hK4_@3wF~)K%HW-U!dW&C0(}tc|8CN8&eGdBIG;nzqQ+g!FOQl~dPpg(XN4Yx`b z6hx!|-?5=Z$K?_bVq$&j&ytSZdyb@@uG)C6V)j;=oBny&N;$xIvbzH!pXaWx%4J}j zhsjB@NL>6<7RuD2?px zWh1HCf@!V=ulWOovqYFfJQ{A?D9|;4xR}*fIC?VhPJ6eU7fZjG1TVde@Lg3hmt-_jk zBSw+UBf8O$d+bj$=vF{%_*nbi~vi`mQ_R(?Nx#PQJcD*&2Mhc33@$`isnK#;F zvK-K6*X<%vJu~cYPBXXG0E+;>?z;U(iGcxw}5VXu2 zab+qHq_m$U|7p{MUuA>*AHO?5`>UrxwnAD+H7=}KYNm*fe3gzpY%+jrFB{+ZTy_RC zX%lyO2L(JTnt9z+#JZ}UTKC-Nt6YUALgNkk;2MiDfL z$9>&-#s+WQI?GF~Vu75lJU#ZJ+R)L_ayfz09u8Um7}fW22N_Sj9V;u5uq)`wC0d$r zJRRp^OPB78v0N^foiocJt`;8t)@Xx!->ylDe9gy$ClxRAuwjDO zT$e#jpbX|+zbH2bZanX2{N#&uwKaOT+ubtnFowPF_fUIyZrqPBx!KzBzyOS>?|u6i z>IAV4QQh_B8~fpo>XU@rlNd0RysSU%2j5`cCg))SNCgWkZE3PZ*L$w4+P^oB(*j{C zZ=adKsL{dFqVMi7c*BL`#EL%l*YWD6%4_0fULpSHZ1GUCh3m-=<3bouuApM|PDCMl zqqLpHQP@evAo!4@49JQb&YalP2HI~9{&L=zgxy=NmHl}egO%eGY}-c?q4bvd9qY3t zV7X~##O{G1y42c?FK?U&knR&7e8a&%*bsn>4r?Q8k%>q}Wtp^lH5Go_yn3%Hkq*v- z&m_|~{;zjgJoOeYT*0$)EqsX%wO}E1krMna0K4(=wy#nb(U7c2YFls*YJ4lNW17l< zc14$X$3NLn&Hndn^_CFyub_CpemxbZ3Ul;_|2P8~cdm{2`HlN%n<-n4CS>F7iOl@a zKNWcWV{BEfbP3$Q)TDI$Qwex~d$%Vuq8?e!s#*EG?7-TVVOTcuV+?~AJU$;5B_C^xbAum_9#r)~#n-A2u1tGE zU#$fH!1 zsPV!M<}XDVQB^sE@h&y?tA-|c&aI(HFu?J0ak`g>I1 zXn;&d#c5wS<*ku+w=WPr8eevw-)WAleQg(Wy}hu(`<_wF0awViY#V=R$*a-WQ>J|YK%L&JH-n{uGdv@c_{t{SJ5 zf4f{D@jHH`+1H`DDxU`5A#t^+&sYk!rLMSJ^zdP8q%NyYM*(^ z1Uc2=ZA3MBO|Kc`XR#;1K@&(58T!sr7KEJ}+H6|q60nPlZ1rJlAhdlpJns`j=(H7^ zdU?&k*!Es&oJRI8{_;tEJ4Rv&DMzNgk}}Mp2c!FT59`2o@_zMUTTRHYIsW-hx+U&1 zXte1Q@1fy>V*#=Wy5PC>u>DkyHpn}@aCg6;0BU=M`Hx*xggyJy3K!?}FyN-;@sOwX zIIy!PJ-jh8o;REDb$r$pnZ>jHC+ zV;KC191)Wh2!q4d^J&^ElktP{^lmM@r;)##F%2tkl2E5t_x0Gs+%hBa)wq0ryg-)zr1hw z#dYFGAktfLcvvwepz$Gjk+o_UI63qx_yVB|_;1(uZ?ainf7VTZ4-yZQth-?>t)LCT z`mc3TzG}eIljh*qm#(O%_sEi}I|OOD6Amf0)4Myz7Bpwb`oz zr}cp$TWf|p{1$9xPb=Tvs{v{Y*Vt}ns)1*WgVWjXPPl$3v5wOy2+Q`LF}!H&1l43I zFKIU%z@u37InQrrw3*doG(QxAB$6)TgAJNM@jg}US&b$nu}g6>w#vg<^LCy79$7Gq z&_B4iVTygMdOkW!9!Ry@weQ3^V|YM!Vrn$V9J+m^A89=`!!yh6omF05IAETS8F3s61(`K??pC-`an zn*X$sJ9HE-_d&=ltogQbikZtE`{t_dayqyoXB+E+zj`=wn~toUJ?00C!!3FLxdsv3 zUq_wIgE+kDU19P?xd10Oe#(zH`$N@Yk$kjF2wdN;P}#%mj+QHnBN?($xOwF0f}pNE zyb2t6!_lP-VTF&1{c9~TZBLby*&LB4Vk-%dycP&%>%k9haR)>A%MPy2*I8&2@}0kX zx*DCBczn(nX8;}_-fTLS4Jxsrhifn7VDNJHos&On@d)LlQEXi>L?-rWSvZ7&o$Aix z(p$E~`>JPdwIu-Oq%Si6+;569n_Mx)vK~kqd~Cul>^2^ZpWaCl)3h$;yhfe;(7dm-v>5R0z4uWf+30JNh(>WD;C;@G2Y;aMDs&ms)zr{ zfRw~YN|m%C2zFmNd$jg8-WshaPru`Ul-cr2wWgjxBiMFLxxxb)GDic)_>ytNokr(U zXfck=-fv@^ybJF}}58GbWif}^F)usz3j0V(ii#ofY;_;=yDp! zwRS6~SY$%&Us>Y^8Rht=#y#WV`*t+*NwXV2n*lGgBMM?ZWar!QD$V1Z?ZFwKmF{7w=c!>J4}XweklCTdm0KB9psz> zs)e|5KKX>KO+B*j5MmQm0sKe08+V`~AHeHJ^s^g9c;f)io8SL6pj!RSfra@9kb9Ot z)E*ZJtE$8Esrk;hDQnK5Pw2a`*@KTCgsVfK%!G~${*H`3*gJ@D^apKQZEYa^t40v0L`Bw1}$7Q16T!5{4#ob6S9@6iew+lpT z?xkm^eOaDoNl>d@aKt;;NziKN6Iqd5U34FiwSSnVzod}tv^a{ zQwDnS;%sH)uYv5^_$JSq1x8k??prpA5$Wz}B%GMZOdESNPn>U{RD7)@g77QCFW?Q(8VAWru^%)JlEev^@&7ek;&=5QM`SW5@H~8KJn2Y`)%>BiiJ(@bZOw!-*+P^Q`Bd zVEb$$`$m2W3cSti;0i0j;~%I+P2!W``}Q#R$j>CmF-xFW8%V|c6))X$%w<@({&eOi zs~;Fr)wBvb`N774Z@u5d&5+yr@#(+++|cDG4Ow2V-(aXYL4Krrxk>o=U@ooog(#fn~^d?Z^pIr)Y>7AB?$mH41cY zCI>#~2|_T7jC0yd#J;F05hF=$82qg0oyDaMl1eLNk`MJ@zQUX$egy}3cRHTQQ@OLpTmvuRre$D$_E7RQj?mWuwMfrgIW#_7;6B{W+^8B zRmY;=Cx6`CXMw}p!U6-F2B0@WFK}Ag6rLJ(+|FTlM&Bb>Z6b8T&{2p>;MsXY;LPt2 z3wALFlPa2?D@>Y5Usa1DeYcOg<3CobhXZdTa4F>UFXJaJ z(9+A7m8{?nY@bxQjaJ<;^1-6NvVIimPpFxTn(ILPa(#*#^KCGGxxKU~b{*DF9!ItH!LPM}_Q)q=pF6941HMNJdc`#C-^zjxLcG_Ki ziRK2>NcsxfuMs)LyQT|8{7&dUb(>kDJQT~ZG@zQ$$CL`(rJoEqgE6fE8`*VdyeJnU zlJF}GRk>Q$KKxMy?d~l0^*(J-88vl#c-a}vsg?MOT0`+r(_YivE>GC=-_NvHJ^o;} zmJRkU(fHh&EAH9nTqM4n_v~5lf&m7_l8&q(sHy0cB7f+Dt9v-=xI>9vi+bk=W0$2t zM2cFK)?5MXH2-WrdTs*u=3^O4Z=1p4t-qv~tK(40K3vR!IUgB6C(Js(O$C9%+ueo) z@4EBXUU;LT45Nf(`-a%s@!t20P0yV)nDp~=ai`4&Qiq_c`|J|1W+UH6+knWw$X%^Z zcCdh?k6)Ctvu#0E)%T3sraWwh{cQfta2?i$I<*e%^F->e98GGgk;wE|zi!||Fl)g7Z%P>f!|Rz8&~@>z@{^I zWTGSkC#Dus?<-eh>S?_~CvJa$peQ}z$q*QaReI0;R`}IYwRT3t506x7)Tpm{;{6E5 z1?I9$IB!SlI$vdv=B*VpP6SWRc(BB_>qRvRU)>k7MkH44R74a$j&sgKa< z%i$BE;&sT7-ArXkGYUUmB=t#*8>8cke<9cQRN|udznpZ3QJNO7(@kmW)Kgi$csOYt6pLMBm9@B4wML*}j}u4aPPjUP+4d;(#_Axk|+ zL=S0I2Dxi!e1XpJ2eoaTG*w zUrS5ydz}Uq^F#{r_{{H>GH8UFSW=N>v3+=mm#3t*^$u$I80<>Tnj$xYG>zK(VBk8d z^4OP5180hiK6urI;6W9-D^g#Rke<|7@!-j3aDT4cqZA*46X&H}8;YCJUq0&dg^v69 zoNw_^LFjW()uAk{@d`xy&woNco3`To^Dx^p+Ue+RVo&##x(%wBz2k*-iCm8WgI=Lb zBrXj~H-i!f-p!qfpbI75yWJ(PrT=*0y_D&pp8iZYnRQb!dR7+=+01gz#%7|UJIRd@ zf}4IjV@`LTy$KA@%&<3)6Z!t0^RMP(vr*b=)$rhx0PLw{@BfyN3zbYy`p;<}#qQ@P z6-|b$@Wets=^&w3Sl!P(R43(!aSp%z^Sb=-sb#>4w-1Y;imZQGJKP>~+yv=*3)Ao{ zXPJ^ZOAxL&9=vQoS^^10?xofA8t5p$AS2Trgx_)-#7^rg;05Z;wq!SFEKJFET|Z}r zLkrs{ota}mdx*)M&h0uLPP%)0cg6)hvXpuTzn(+BOM$y|Rc6@tJnHyK9%DTB$xP#n zaWG6h7#OW4KZ~Ur+?U_6SYo26(}fMnE67^%{J(fJ3w)9+QBt%-+*_PiJ(s+4L3$6- zR`ApoKcvw=^PI{><`d--ADB|{?`flbdB<9zuBcv`wZIA!PXA~PWGcYTDaw`%)*$T2 zPcYTADufIDa^J~M@uSqmpldJd?6JN<*5$}$V{Ey4WrzGxIJgCbM3bE|!u$0RI#)BJ zuvXCMebuBt-Y}Tc-OVC6vzW7b2^zPt=JWZtJ7HY(-AE;$yyvnz1K$yPV|BZ|qObBU1XPT^wjtg@yRg1lWi@w{HmFTB3=~Mq{a|~@@dgC=?L-d&}UJtzy2M=$^KR#1tfNyBH!dg6tb8F)fg9mp3 zQgQP0y)Hm_{`^TzGofQ1bp5J!IIR(njcFG7DHh>vv*qmd#U5}^sDcq4r7+f zIT)#X?QKed3vT}X@73?3yI{o?6JFhGi_z5sY4T~rz4&c;-czl;jZGPc@l#L~DB9&5Yls`mGLbv9UKl7W7xTp`4b~F2zEgCa#CHYO zrq=wa_?+2AP1GV8b5{h_=e+7+Acn_6qCyxt`+pwo|Cokj>||y&68@M%ol<+)BNG&r z{q_zf(1DTDYGd<16BL>L{(?jAHqyJkqPF1;0fxpsZ!}Yk(IoL$E|2Y9jQPY<@%$L^ ze)o`96E-|kyenU5z@}Pq>JWMt`xeT zl2Q874YE-ObNuytY^IMR0$i_fNFSGy#=^~#y$Y8DuyNq^%qN*7RA85yI~Upjj$>n4 z<&;j?J9zwJyk!HfwNKOV_S7K9t$mN{Xr6)vr{CI}dUH&B)km`DRU?*b5)6V|8d}rs z4rWs}!$Ys~cypT5c(m@Ggf79e7=OFE^hm}Ic`Le9D&+2h_DxOSU!G1#`>ys#S${4{ zTCUm&<`?3BR9)|_cY7hJT;HwF_uoo^XG^xKBT591^HJxvQK>a*aSx>Q5;^h5vcZp? z^$Or-yt?^1kwg1t8Nm8ttN>4BEHcnyDzd3@A*Xmd3_FKTMICa%2XD^{9HT2jTCSJs zv+skjsAr9r=}i%k*B)@@ixt7Jo9l{Vtxm`cwnIZTxEr%3JO4oE&+Bu`& z>4pj2Hb%U@AYYLFzz9R8#S1?!*;4IPiL)-d#E>fUpwBWqCCio76&2CCWHq z(rd;x0p~j~_JQ@8zFY>Njy&Npo#@n%S(8#Ku- zoP}*JO4b9!Mw^S~8~r-i17G*bKYAhTjnCVEWG+xN*F3*=T=erW?Xij9u(B( z`QUj!#Y-D>g)qCY^!refJ5u_2U%cR7h_AGgel2aMq7;x{PCnQHkwW=wY20qO$uCm( zthWfA$v(5l@&u!SW6bO+zhXFRJ(qJRSRCvBDA=CLc19EFIVIj7k~rY$@VjGl{Yqp=KfY=6vi(L3P;9>euq~U(ih?+O;C-th3%zg7)-6ve-rt63E%M-vR#+7 z!-D6}T%&h&ktOYU{8u|KsG`1Qdf%A|QYZA7PK0|SqxzYtD@ES;3|mTljM8A>%NNz- zu>w#u?v+W7OVlqa_sj8XQR%BL{~KWKQ`IU&hyi-u*M=HteZu+2UW2CagS(4Dq( zYQ+mjqC1)y`|L5q(DEtcNEDR5?=Icm;m2b87QvusXLR;aX-l2-M#}L9t9JvL@cV6~ z^yX6;9Q`^u8g(xoYyRFJ$k*`0^k0i74`0iMpVX(nZPlH^I1xb`G9h==|JUzv?g~3* ztJfJQeKN(t?LS?-ZANJ1qsF|(9}KBDvMfu(jB%`Wd~>IWJbwJSe~&y2@K*hSq?^fJ zu=4(U-)Bimh(1!M$=+&-w(qliZ}fSg-Z%Byo$OS&BrLuC4#gm4=Og(I&PY5`8?Won zEMvg4=!cgg1Ig$hiw67{J{`S!mSGY39t>;rg^ICx_86ykaDQUfb z-aiGa57qSE2khWcBd#;hyOMSsBQg9vLh+l7{C};YnVfA7*jxyi2aifbON% zrHDpG!Y`2t)F?DYs_CV(uh}JVal}P+e$EehHiiwpGZXjk%)15iM;hQa@wPf#P=Z#FO^g)@5*|4o~IbWdB zgwVh1j8Cm(;lIRDHA$Bg3@JCMbTS7EFYik|IuwBai3qhO z*5|?70>14kPA2TqeYdo~*_!D6>SiN-brIbMo46OY&GGAOz1~M_2Q29qO*nD!F6c8g zm$*|aW1ZkRK02Q;jQX#YEl=7RPoIqGICv=us(JRSKF#34mTl_FB2^o-OW1E1_ud*~ z2h#Ql--?EjS7(al8|9HGA}G=-OvQkPmwvvy`51Ak;%=7vLpXMW>FotiWqiKBnc=r_ zJ)!U9IPtd@qJd#Wk)dNZoT{2MES%$j8}m8CG~AiE=LaKQ+bI_;n|qSK$&>*9^NCpp zw_KPtI5C`V-$_$NcTiFIG_W)p1Fh8M3jxgKF!C*VueP=}3bE7)tmjq0?Bd{x({LG- z{9AmRw$=-shb0)|wbW5q*nBzH{T_sCh0M`M5j{-z)jqWSz6Vv?&Wlz~Cosy2DnusL z8`d9r91TdcfRok7S`6t!L3rU&#B&8Xq!kz*@MF3QqU7)P^8Cq!xR2sYI+87r<++t+ zv8a#69(f;bT&x0JrF|>gquEg4;+AGb-v(RZnUP=g#nG#X<{G0?GVH4$HB$+(h01yP z8|UewpuB4M*3$jcxYxgC&z7SR|G zby^HF#r9x#tRbFrJql`BCq_Qm(V_I41;dv!CJ^A_*A@EfYq#K1TMe<=;GkdXDb+5b z*UG?;-c~LUXe?zm=Rd4;uMH>#wavJ~?%BF;vU;YlcIrTu;SJNc)o9jX8-LnX#BwdX(}{W5Mkolx^xHDX!^xM7hNEP;p@#M@w?z<8!fn?H7HSu zd#)&Y=);q~r<6tEmav|o`l{ziC`?;i8TmLPk8F)SzQyTxfk$4M!;>N#d|q^a&LC}t z`4?&xmdwVu_^KgP^kOyW*8DaS-Ioj6-!7YP{%Hd--TW(6eo`px&(E*?JsE`Qjy)f{ zZwDtXU-_fF69opRIpziYWZ~?e&eF_#Cg3!GXo%8|6*EW$a(Fb|q1!aR^s7!FTyOaJ zPsJu1G;@B(N_AYqjT5!>g0~VuD7n0O&-Gv+Wse${Ao7Y=g=q`akDkEee^0SeX?j89 zFV(Xin@Ujm^sM@|ae_axq;=e#BXl^cE%nMYe=x23cTC|3aZhRtV0)Su0IQWB89$u$ z2g%v+`Le-u*#CMT`AInmeD0VeLsJw4q)vkQbbjW*J77G0av&IXN1P|^yS1R6o@Jd! znDFf%yVL$v;zUY+oAjro7QnT3)_TkB7AP4F@dggM0#9pl_O`-x5KCMySv+h3U*@Vl z(N>V7MAMw~iKluHjWx%%%o6CXhUZyjUBsIv zolf?5yukUcyp!(d!(7MA&*^P>)IX{rdHZ5An3e3d`6q-yQIVlVK~W*F z{L%@~%0G{%zaMui_~#2O3j5=^SJXkx>b!<7!8cdmGhbUbWyQ6|ock&&CSd4B5_-7Q z5l%HmEIgWy27&xtrLpT*@sSeW&%25NAkZN>CS&0YbQ8-tOrK+6<=AjT+$k1(GhWDk z_lh|@{~5HkK(7hndAIgbtvUgdkxFFMWg+bFtKZF!Z~+7F|LRpaT|oS2m*BOmIN+~I zcQh0hM5YsNGo=%*koC==<$~ueSlYMcX@1-dOnTKqR_3&!W|f`%$F2*=ZI8}Qx^iRT zh0UhX2@B{NlwNavr~@yQ45SO|-N4tp<=lFf8Yn0q;d{bFtUERyw+P1bSR1q1;A7|r znZgF&tmGVEZ;Mz8xo|Y7H@*7LbcG9BlldwvZaKruM;6YZdpChZi!QXs#Ti~&1#yNv zl7pWeKX&c(O`vIulp_BA*X}^!DizWhGbqm3As@SM48{6?19BV!;YXgpM`g)bi3x6< z_e>`(K(wblExSVo77qVzGv+jf;+gp?|Gk!ip=pa+-Yz3xNG_>s9{Ae*{t@HO@nmfv z4O*7JP$ml^EB;bsNv6;h?6lsnvLT`Sx@OL`48&jMb zqdhnrv_HG19G<;Q!22+m268w~6b0>ecFdlB-adcvR?GM|BlbA&}vOr-+p<3te z=k9~XYtweLL<@_rj-aseb=cf|mRw9~2?KO^Ok>X}>>ezp?6BzxMCYEJ}f`z!wAWx5mKrl)Pq_e-HkAC-aM! z$enEdOfCvh_J-(BCzG^JrUPZ;q){CIH;Jhp2U^blSXlc=B~-`e3p^S>d#X-kLdnw_ zW?r8I@Xgt@gY=~vc&tXvY0Aj~Wrjy2^Mo0!)@1!X`GO7?c)wCzH8%jth#`l{6?ah9 z7RZ^iPXb|4qRTi$0hM)DF5W3gf{bjQN|m)l;7iPma{F2Z(|;Z-_gNA;U1{2f3=viYMNRub6E4Jb0;fy8oi zscSRn_?G&tYHWOOm1%xif}sI0$=-aJ?yM#(oT{Q<{NxOzYXAA)lhz>o$FEhi zx(<*7Qlq{k0=PME`Y6QK8S-mrv>NoCfWh>@fp^g{u$R_FPki74{&Iliy*@5rt!`Dh z$WEO5l@)iN5jkSTo`0G$w`AZ)LMUA?l?m{<=e(BtO@W!64Q866mN2|f@L;ar3{I`d zi7RG?fBGRYzb1fmcI;ZPI!5REix)pYSf}s2jS2pifHe}DXZ(Rye#{S5+j}0?&;8}^#?&L-$EC@A;Z$z}fueD3dtZVZ4 z^#|RvhlP2d>diKBlsOd0r?N`I>kB}d?JL!O)?=6;x18}w+5xV%^e0`KI*nIrJm|ht z8^T`e=SQXrJ>kp3lSfmzgiaZ>)`DIVIMM8}KC)5+M?FHU zjGriB!6nz$0M=B9)QlQA`pgH?{4Qw)_oV^VtGdS(S7~s^f4`dGj43?h;_fJVqz1)~ z6H%Uf?I2`cMz%au3`JOUrpm{BLCSGu`>bjNI6VH8HEUi3R@ra8uHM(c6x!I21OKH0 z_g>|Xtpkx@TRW}EJzfk4g4p-{47q}CtfwBl&I}~(TQ_cBC;mTy1%5%TabL=qf)Y@!&y9w?}b9o{-Mw@oBch9flcr3#$f_fW%k#Y9|i|4%U`E zX>;%fRh#(J=`U{s39lUE)g>R8s&S~D)Kvwh(8LgP4qFH+jUUsZrNg@4-0~J1#Ceg? z{X6fHI)r64FiszI0H@*6mSZj-yQQM}yjT8e5V_vGntv*{fp>LmN@2?vYK0HiS%3N` z@wCFr)mg^{GFDH{DC{)`S{km%w=)57XutI`#r&oOH{W=+&59P>sM}ZeTxdh0{)4ji z9E&MX@mmf0fdOc8xQQg*@dd>z2Dv9P_rYaH=}C4;GY}hLV;c+Agj*jYhD85!fxR8$ zf0mk=P?;sVBT~*CTHfBw=PPo7zk~f-AtZ^Qyz-fhcl88TTGfA0b@PCglMnh5+I3*B z%tAuYcMsqtcRjjNArDo`R|fYVqlCYz~W z-IB0{{RQixm!hL#*vY@;o(mPs7CGCVe&hnD?!-lL=%~XP-kgDbj?Um`sZUq#^P^k5 zKxKRFk24(CIQqTa#0x&VL{h1nq=8!fUx`bZ#~@_{>z?3%kqh5gwto_}v`fE&Zpc1*h#v2zx=WQDK|Hn#y4S*$>c|OF6SCB^)}C_P?^D# zh>HNV+!$kcIz6n^1H|XTUOx~HhOCov5TQI)y`9$6Q8xVR&AmF^L zC7e_my<1s+77x!<|D&w6h1A-+y=P;sp@A-_S!y^O&I|3f{9t3o^A5M&ORw1jjlDUG z(SR!4{L;#?kJ%1ne3?No;UM-R!Jri8k-Tcx#^HBeBS3(kg^C2@Li785Hm$|*u;u8KVL~ge@GG2MldC6w z=WO_~Jx!=QcWOzGwFexZSdt6Lyg+lKa~gyC4PdY`JSbbxj8O^s%oo(kp_;QdirJ_W z1;K`?H7OWgr~S<}APdL*Yu|PyB?5tg2%P;1NybT`rK*RK>2TS{Zu4k!7EVszZ>U^o z0Oy_sJ(K1EoRiCya%FA=iwJSqmVGVAbUqm4a+{#j!}!E}>>$cAk-*2pSuo>pFy(n% z9+Hu7le-f6lc7t$7cG1;(Dlx$J&l7UTxU4_iN4nwc?{sGxos%0JnIrEm`uk{B*roK zYC<9Ws3LdCpKx@wXXl&$6Grs(L<=_%zGVl4?{?BRYgicI_%iQhkH(JrYMkr_VEL$+ zcIFF_*USx9$h+wa4fWPj?%4@A@T&SsD8bD!RlRl7+8>1W&queOxCMa1is60Sx=Z-o zFJDvLB6@quas8K#IWp#!Hh4(hhP^QdImks#Fk|ztZIf~o_ym2*yc(2)K8k;jyAj`+ ztwo~OvtFd5UsK?@=HOzW@}m)RENnr=E8!FHHS zTQMQ|m?-e__X(SDkoTR7y=aw*w@9Vf$);|D{p3f+oh=tMRo70cJnj!`wyNUodcjx$ zE&uq!(}91LJkVyY3UgY|DIFvHn7Vs(@%v4)P|=ISfA?M{RC&}H`?A-fw$+=a*I_|G zncQi>MiPc%WNC)^DwgoUa?e;WmoJ7VR^&SIdqKgM+^X5QJ9y(mD4&jQ1mwEwJ#1r) zLZ`3D`=cnC=%pAvW)oO}bW;&_Pg5dc*v2{a@?lEoXWKa99HEE9I#$v;44d3~JiCaVX0@a4 z+a+!uh$4OWx4cckfB8mEN0J3T?=Gm9&@+cSY>6zx;@&7`{;OZC!xY%^wZx7Hdg0w` zVwY%2f`Lt@Xg;wl6k8{%zdIIYfzO^Gkx;ujG#Ah&S$E2UH)reRb4c^>WpV!f$lV+; zUt7KC`mY|JZOnF9{{MQ`3{mwdi9o%5*Qpzh*n;*1WsH?&5b9N$>eC#zfg~^9rRpEH zNHKC~kVZ8eA`FG*#`ChU&)($c(${bZwj~ww`4fq!&$I#Sg-9YFIS?Ayn1gidbs-^T zM2`%cnLpi<6EUwq>!C2(z9zC$E7p{m_u_u|(w?6;$SV@~(~AFQ z1?OVOF=^WPeSSbEl4bSJGXQ0(<45e|oj~5o{M%}E81|6T@P{tg!(5Ps*l0y476)3f zYW6t6IfklVL(9$>vf+8@{Jax9eLKpdV-$|3{yj3t7Pf@o0?tPlh+bBU=(jjp?EybT zl&f6cd0~XXf>|d;I82c4!u|P7eEGxij|;UIoXT0aT=d2V6VFV=Up2QQa^j93f6WoS zh&!oO`IH7w^40{}ez+h<(MsG}rY*$3QIBX>b;gm+8B(??A|L(uov2e|0v25#H;kCE zgQ*2sISYc9>`7wWCOv2j5gcER@Tg@IPQ$0pC%+03=QG$fr-R? zVvdU==`!=zi^lL#b+3Wkpex20^;dM9(t%6WvzjkP4AJ4FSlBxqeZbk}k6~)YxVO-X z)BC>&Fu!%nBhQuac}NOgZ}%pF!|L>h<<2zx5UWWo>{SHjZ%6G7g__aZmhmfVeln0; zx%^;3AOoYRf3|$}BK-EV2W8`K<|CPK_`QK3W7tSj8`peqjvXpLf15=6z^Zj_#2JDI zvCgSw*DXo~)~hRv(qfh9es^@?k4y?Y$|$Cgx5>cQs{C|G@#*l<|5E0TXcY$AGq&*( z`Hqoo?pnZ|yrVkh^ z`6PNIWuMw$Alr;%EX+Yr% zS&K4`fxf{g{35|;&s`G`Wd2F=aN7-wjrxnCIn9A@WQ?8L-Vzx#Tc<=x?m!XC!i5(z z1TVam`f7wY$7w!hKG8driQjH!#I+P@*Tm~PR<;2TD8Nw6gRH66!`+ROjq)mg+%O)Ne*#sN`cd9 zQH?p=87P*L@Na>t9^w!E3;IFbhhDRaH@^wg0af+znf!Z=Xlc$8iNy_I^;eLUqO%`e zp;vzJR~l5PU0fP}n2mfD9iBzsLx|qLa@7;dsW^5s$NQqF8!#j?R5(j|phfSWAtUK5 zV9WjQ`rGSu_#*ZTw-{GB+}@7Hk~&ndG-l@ynCm;Ik5sxYSa&Y z`do#y!#k|oD|t}zK0>vbs}awUJ^l5i$`f|p)qG>o_QgV#@!42a@DRGF7wji`B1FEiVbKUAv)Sl;FAZvPb+m z`|V+xanaJ`LkQO16Fo?O#j4bXvfH&pK?X+ zHy-z|Hm8GZ*?jG#@M_$G)W}4J63{SfvQjup%zOMiUpp(;2x1>36MI(&Ff_j3JXoa^ zF3lSU)vr`wd0wFG@~IqPuC!pgOJ9$vN1vrrOu7O4OOVeS48q zE1Z2JZSt@s0aE0i8Pl?qpi~#<&Yrw%C}Vsj(_58?tsTD$P9Ld+Q#P|hdiT0;^GzfL zuT(C`4Q=vokQAcaVn>gvcrujL22_p7m7~M-uowLWTj-N5ZU4#Sh-p$KXB7WgKw&vq zwC-y^grUG&%raphUb+_2^fw$E?jE}nmX-@3&l>S{sR1`L^R6=|=Rvl*s&=4JA)d>N zq5gHg03?g3OTK6|VeRdY6)i$KL!Sq{FBg1}2==mOa#qKEB}iXsDL0*IXqF4a0)j8}8gwsWV0p|?rQ$*?pG zZCcNY9i{YyyIsc*W*Wz05!Zi-l?}x2$C#fbde(Y>=MLE>Ha$ zasOOAtxEnd0FRuGkW-7$g-fBA>8CCkVS7fgo>Q9_wElUs@r)x5N5tq_>nA)xGGI%) ze8?aB$)0(zc=-auV#Vy+y$P5fO=jN~V*qTYGx=$*nWBZ_i2dq&D@Z?dC+RVrBQ}f< z9!|;if}1ZjT+f%r;=@&2d7g|Ky*_{kUQ>Iv(u2w&I z)T@6w%O)6Q?G6b0mc)Whi}Vn$PXW?d-soK*4FG1Uz%e`D5Ts#k`@%xv2I9`jBleP! z$P-mC`h?d6L|7R617B7V6jQtmX z>Ogw4ck#JyBQ)9b$C>Vj1vJ!rnpa`>!6D_D6M9EH;oM&!k$B*T{(kW8^1}%D$Y3yT zSdxkM1_PRNGBo*2OBZuK2|RMoTv~`u!>Hq(L;c3~(qw&p?BRUJpsmOQ)iMfkA zo%QDhQ-h(ItYt&HE(|s7SVGL-Tf&skt^QYM{BT?3jVDE#Dg4v!6rmXQ#3W7E9ijWK zU^x(T>a4T}O3UT_3J(o|+`i} zVh-1>jM}0pdOJwF}R zH0)R%oF_SS?b^{^{3j>xQs7?*`o?4VvL>Y%Ff_m8b2$UzndJXkKd8brJ}-F}24`rr zY z%{%Y$4Z<2$;IMrYJR4Ys|2^O#2jTlC{5(RLZ}-r#(`Bp$cA}G6S_=^ z9DMAcj-y^jD_Vvwp#FTZ?#pSSrzEgN?&xw1Om{qEUZqxsh2oz1we%y%NmiX)U9y0KYEvV~m*oB1%eq6F1dQ&QLO_ z;VTo?(ktF+pwxbsD^!d$h%LLAMMj|C6J?@Tq{9*CM$+ zF|Q7^Le^J~T;~QZ-qYk{hLvy;9-~G{HSV;PyzqT31nYJ4`u!}&ad*!NG5$FbIJ(u+ zE&87)#)WV2PSo?vX=curnau?9v^FHY{EWT}oxJZoToWq+ANF9SmLl>d67w8XwMv zhPNP1kUQ#$4+3|2BAsigF&HmfG&Ad{0_o7X2j{LeqU*3}>yP*NCfF9}K zueAb@r}{?!Uu*zw{wb2}m5)Y!<2?=vuE|InY0OEPmIptjo;o{&6wq@$xv|Jk{1>D)^xlZMQ{-$fr9dA#w?g zt5Zx^IG)v?ApE0yckrh#-%2Yce>iejF5wX*wzriCxrjo4@$cW(`z7J!e&waFYpwXN z%vnjyYp|_n#|ej)8(tqiDoos`L-t>^Ou}WCz@WS@MD93=RfPMxBuWXpQLM%Y zqI;Dsxs!Y`R_+q$p;rTt*Dt?2=pu}1bMu!Q|A}J_ejb!LUW}dcH7-VC_u&5RpS3Sm zmL!^N-`#|UGdTS-`WL@Q7*d4$wK28lL;s^HZR@vJFxV>ZQ}-_ctZylPxz3tO%=P$w zZOpO)`Z?yGziT=P8X|w@HvGAsl~Mkkh@D*olCoj^s3+dVSolw&!U5a0)bC z90?NSIRlg27nysXj!MiNeEF>1ND9So_ESnNhCr@!gS9zk;GxIg*|XF_zsh7fTx%8#GRiAL{R?JnU9OqHpZ_KPdU6iFeII9$dQk)4 zv&0lxx2R(FiW7F40yfMl}wA!kTXjBjy@ydmngYTjOubDTCEJ1{lCueJNn6AHJ<@vi?$hf`UU}9?;>8vL1)8e!FRd zpAGw6H6AnowYSpx=3*B$VFn6QFZA*PFTc3U*$O7pfUlf&>pz+p***{O0*Yi{p?SOneTk{riL+glD@K zpLNLqRlIstabyuD>AwwOwa{Qm!VI`4lj->?rOEh~!3 z-X&XBMoxS0y;nwNWK-EP8us3MC7B^ki9#jc%7_*bB}xNPdhX|k=lK(SUiW>wuJe4q zkHeqzT?xMOTX`>M>w)pQ;-T?gX2kQMb!1gQ2`oiqd=|%oiCj)kLvm#l-iTHZduJj6 zMxTB$F61ABe9GCn%JUVVPJXw@>`o%#hppJ&k+gzCEw$x;N@?Ji33ZJkcON(pQ|nu& z-NVNNIk!mS)q$??`dHh26{Iju?b@(zfG0Vms`OQ@NG9{vo6hb8&OE62{3KNr^Qh!v z@Aurq$%|#aO;e@Ng2C6NeyYLPe+m`MW~N{@wQ_VGS_#j5Vgp%>lmgtF7Op6NRA3*@6$Z zi{Ob*6j`x(0g(BfzU!@S1N6M(pWkU6LSLVp`BTy9u)t1tv?-_xuUval#gqM^kFjD} zeZYhTzt-pn%uq$Z&U>G+$gmiwJ9PBt;hP%Z;bF(v8h09qKH8kty(;M0xcbxa)Ln(C zKVd=ZJkH?TL#{xq!JuquVRl%)0a)4Y^d=K|-tF{RJJNj`#2hq+N%;Aa!UsnKSHbN> zm|RRJqx+GGBidT5&v>pw{Y87K^$s(niNFw2Zvz;3FwL@gPabN180n?{ErVWP)3=R1 zc^J9$k3tF!;k1*|j@E!Ua6Jw_J*SWXk6&ii-c-#&nc^1G2e)%jA0NIZ@hgV!K7;*I zZwNlYz}PPHs6IUWwnJL5T8ocNZ@o`=TML7SH;Uxm$-)lFvl}LY#?X7#=6E7QF7iH8 zAD~N31uD*OpDz1xf_^UNRQ4fW5FBW9H>SId8|xH73@hF6SaJMGjIJp>S3m7+bix8s zPIoxZ&^^SbkB@)+*gFEu|Ajo+Vo?TT?REWAPYAw}oFe!7lScGeVEm-DRRC=$j&&=^wq)Jk3bchs*@=7+lDSViG5F;|?vVSx(u#vfnr)ykJi&y513gDN0kgxc%x}mHqVzr678%daP?s5sKbkoM6!0 z?CZUJFP)Lz0)I9*>N4i~q1LMFZOgnethQjGSJbPA=S@2ET|VLvGuc+M#tJYyAj$bD zzZI*`Xl5T4C31AGirz(yGN4JV__f?$6VfT)I%&Ku!|qZW_^nY4+y_qhCdw7#gAI$oXQRtEQm4qrMWsEJaSKL(#`Qo<4n zB^OGf_b4CzWZ%_8r6}7FuU8r2iszCX3(EtoQ1AGte*4K{xLxIdB&D@@zpm8<=j}0D z-2KHXPD7mUx>TT9WQO- aCuq=M15rECVSH{i3fkiAw>Ac;91;E=#vh?psAs*HLkHU0762(^e z=$2KLFd_DVs)B0?aI9LHWpq~H;-9A7ZY>48HBq-9IVuOS8{a!th<%HC36p)P*9A~) zJbjkyi#D1?$@e^x6oyxUY=@jT!jXCCb4jr>;h*QA^6XNu!!MJ=ufFCVhW{!fJ348I zK4fKt!yM@iVh)`)>6@yCmjVWZ&6)%-&|>fL;!T1VD?b&Ic{T^Np~21bln>5N%eqOh z8luhN7z5Xt44D1!Da*a@7DlHvpYW-5#Sg81yyr(W@U~sK+;megq@8o1ZIH^uuyFyU ziNrui#>J;SmXUCiM6*h9TNJ%L^vSJ%%c1IGj@vz*M)0b6ltETohbDy&+6tDmSQ+HIR=?)~Uxz`(yGZ-gwAVsB(c4N-!l>30Kp4xF3nHj5W3 zM@43yuC>u@=uegEh)+$!RFMaTE&F6)IybW8SQ`iGs_FN7Ni+ZxMVsr0<}I9FDW#Nz zFACIUlBt~cWw8z4onP821eu1G-BFtgl*(Rmy%&5O?w=JH=w09dir-g5Pvu?3g&Wnb?Q=OrQb==`*-0`q+b^9 zOYCrvyIuy8kE1Qfl?{Panf=Z7D`i-|X7`Rx@*?~|wMM~iXPEW$EER}1M>!gqZTxDC zoLcWj>QV|IZ&zjuFIHgq2sNcvl`6WVe)>tapnz4y{}$GIc;VNYj`XZy60`-WT(I9& zBzPRjq{1%Jpg;f1fJUVn^v5LxE7J4eH|G(P>PBe@w^T;EA0il$KE=Qudl!NnGhM2py0Q z+n)~(b4Vt@tphDpl6yQ~oPsWv%6rY`vOw8=kI{&@m#9|WE{!`8j}*rqKBrjiQON42 z3=&e^55~5+T<5KL;FS5#8(JU(HL&rxxeDJDZ3Gi(ma;F4h6f=p%Zci~B(+VGorrn>Mr@{=r-`UxmE` z2^M=Lvhl|2tps*T4gCG=R#|x+E6P@;g+-RBLO`==v#9Si+~Zj)*e`Jt58ZSWc{Lsd zI!z4wqj&@$MdAMPk1O&(VjIBMGTV%5u|=Y`L|!jkMRcaLZy!o5uGB{l8-Zmt|H{3R zM*KU=`+(O*9j}zkRuM;Coc-GKDyBmkwvrRs-)~2ad44NcM|LQ(K5t}DQ~ zTHn1TwXDCi5xv0MJx?3274NgGW3zE@7p-g>H@(GUkt}>mU%)!NZBHjS>#7w&+#rB_qVKA>cHEqT^EW!%R>#% zD0!n-=f%#O+(h5y^LNdCs{v5WMnN;uw%GUVsfuR+&o_l7AIW>=DK7Zs^M zah!|V$;8>}re^Ps7;woc4UsqF#D3~yvvZF{@d(KanF_T^T#h++#+bN24{xxa-Wp`b zVcw0S!p}I7_rtRkbWX*?x=(_n>MfCR(CADuOA5xZwG`jp>jmG24^X&pP{Gkf#~Yr# znmE!WT>a!jF?>F9^m!9cAQsK2OKqJvjYnqMeD<)Kpx8qW>WHp%__ObR(zBE>{Mcna z@J2ojw6~Kue;qSM^6RORPU2J;+j?-2GDZ!r?A9!mBxS*|x+8LD=L2zbC$~oSH9cmx z#<$8pl*9+liS1Wc`CuqOQ*>|7QM^)6wYM%S1tfky&x}>L1X|bf|6P?9feMM_19`Nj zc;-W#;f&KQD0Va3wE7ZQu;{xsB9eyvqu zau!+5!}E3~G*Bw0X|Hetk4c7De~oZcDsT3x5XpF<9x6Ky6c8;<*4mRImzw zuMUowN>V<^^|cx1*yK)#3uj_}>eTPWeJZ%i;6c)xWsbret3SC4(?I1B^Ml~+DBQJh z^1PoW3@^h)LpEhd0cM_)h$rMhYucN`?8M*2WE;d*(y0R97&=9%g0--miS*w4Btl(w zmLz+3G6StYp0TCcuY=v?!B2D~&*7Wu9_sA2CeUFH`Lb8582=ec@gC+?z!P>d^v*xj z@xEzXFr7CG%&$E1^)QeFy}UAh&okHYmOB=EWmrIhy!)SgaY5uPksNRRM0naO=S1iU zzVj0cvG1qK&*Rt2?#=4ik_3-Y*yvK5G$f7uC{&`z!L~g{)W1FBz^~-V9i>oC9OBuQ zQcjctkr(%0CDagez?D7=o@*slGIM2BZt{#aD_az|mtZdp(%WLdo-t-Z&zE|AF?l(b(QL zs4A)aQ!Q~7N7=@O<2T;+{oM|yxms`(Z;_6Pi>(lSn&jcTW?fkr_+ctcrJEPQ$~xr? z10PE3+v{2v+(bW5MP(Mh6p*3MjS!$!fh!U$^-46Vuo%{q(GlN*LCP3=ovQ*0@7_@! zk~@htlTEGhi~8__k!FKFpaMnIcRxNopN;GFIc?h(#@O9hvsbwPG%9piRy`r+acR1Z zXSW%X(Kj+Zto;l*v|W&{Hq2oL$LxbNKI18<67g5CdMY23lP{-ac*p_2iIk^VgE}Zh zealR13xvI99-;ZmA<#&Xd8@mZ6UEEOuJIia12TD4lj*?>$cZHTX2hS5kr4}BpLB(= zC;ILFyz^|B7G5J7I3^6KrUO>#C6|CDV!OUFnF&JIqYwWKl7^vMe-mF4d;QQyg{x6a zS3!fMJ7O$X0v^s;^0R)_f<7zfBa?OIKzZGqFL6B;Nu;@0tB(KcEBo|3Mm>)c&c0t) zae5UGPU);kRwOZalhncMtyUPw7oRMPZuJ9$Ke8chRGdJ;#N-?C}}V7HHP^sdb^46ZVuies4B!z-m3FW+jVa;LZMb_LH$RnEqsA zRFu^Rt)%?Is27MfB!%VsQgfj$U5KsDNF7}mf0q_~Q-@b)A74K2Rg6!{M9lTKxTk~UnHG^KkTn*j{BhDc(31kuL9Hr_9CL&0NG{qwu(Of++#zp>sB zk4wfG%RU0!I4H4sAd!g__mwE^|Hc`O%&9J-Oo7Cn#dT!sWCjU(-$R5(Z1&;qa3t{60UA zzaKN-3pbURCMzg4fZc2_ubJ{?^tp1FnWDH5gZj_Rxf@)CiGvp;>sdDy{KV~}|8o=t z^7TtO=K~rsnyT&5ogCshsS7bwOXJ0VF=0-V9g=XT|55i$QZ}Z@Plz5q<$_nYIchZD z=b-2NBDsKnmw=ZeyPap54yWRu(7t^|g5gR^Yqwuz;iY@h{&Ca}AQc*&nRAT;mhD?V z-YJsCI#wHHCN08m#c6xMWt;=IH11`Yc%MWW`KSWXGE)@alc3C{nFm|HcboS;x{Tg* zUCVyC(nzM1+&ylmhe-_TJ}qwh;X{k1fu4vG7S4TEX4sbk`-d3JMBkpn(O{p_$L}PF z{M2HV=@~s72{3vM^63y$Y2U{w;|PM%{14+RIAI@OwiMX`Wu(lS@)uFBhx~$!fDDyr zWYBC;*HjY036Y!ii%LeAt@q&2W42;Ar&*9#$sdpNMgAmXG#S8Sym_S3zyyCnv!{ys zhChfcO~Hv_ENK+C^tR=QvG_VlmSHV^v~tE zUs8VX@VS%v)(H-nv<=fN;-EmEWXG3c%@XK#BR@?=TMy&7D{>N(WKdOwbx`u798UEd zZa5)9^f~tEo##&u#`{qw``8T#Uu}%|xauQbtg++LXc>qD2i8MA-@7itANFkf+o|F> zOi^yHYGsNKUB=dL+|7a6%!rQZ&?LOyD0KLJnL2*)_mE!6GDMji>9$*C#cCSUg~53 z0m6{~vWLiR4$D)^ED(>8Zl1iX4Vs0t;^`AoA`@@&XSJ^<|UMY+ES0`Mwxj4n^VuJL} z-w!WPn&Q9ji>f@&QXt!(mt@Q$i}30Ga>eAw;8Y)@V zU^Pr_ol2*=)&M`>R=W=pee$@4n3j1ICwl#2nmZlm@QYLWUK+|!cxvUkAtB%a9@@SI zUgRp^sa@0X@scZe2^+NVoom9#FVyAiHCfPAB-GIKQVdL{jvwawWdg5Hk;m{*XMvGs zQJLZyC%8?!o$Q#(j(wB?Zt4vBSP-q5`gOPt5{KP6`>+9GE?LZ(_A5f%JJ*7PS|ZS) zlyXZhvkA@4tguxP{=!g>y}MS+YVcu~`+IGq9k^#C2P^vLVfT95!p*ChFuL5x-Al|d ztt(FVaprCJU08g=alZZ%bk+^c=$tEt2C3(>KOPb1Pua-*j*r-ZoW4EJYAFue=c6^- z%qw7D9H$gL%^n1IYXP}wF=%6)UC6JL!;XBq)1J5Gk^Uf4+<~2Fcv3u&k+fZaD#L!d zdpGsby4s6xgH9A_lF6Bb?)l*736`RlPxG*JW^>T2^ej%Eb|gRaRRSWNE#Ad^6vW$O zfqy3=70}dK`(7mRf3$anR>kLBDOTE?axa%N#rWk%O2)G$m{zjVE}urAIlt)FN%h>p znQ!G}samGEshHjR)?1bE1+!GYrncDztF!jEq>m&zMMRDjlv|5#U2 z4qW(lBWl7U6<>;#)@`cID7@=#QF<(?g^J_aefA5@V6j0tSWw@FeXUWQ9YzvZCUa(o zVpj;h4)L52I97{6QWT5v^0ingc_ZmO-zSC2y7j2W9cxfm`5YZJN#rU^q}-L9Op(7e z_fH2|0(d76iHz?{Md54LY-1y3aQ}|Cu-=*qk!Re$N7OhM#G*FL1*Y$yQB_K}&QBA3 z8EaHK{YVUDB{foX=VCE+&3`zJuNhCjtTDc8qKpAH2lw~&sew~m^FhsrF*xA$kdJXX z0sm?#Q7c5Jz>kGf2E!YU$h7J?ZP0WKy=Y}xmCuQSLH0QR(?oX|V?REUyb%g2_lEf5 zgNZ##<5QQF%-x-ZvQu(mxCF98MrGgh`z|(1NvX`snC|P zo3?M>2)mw*I%WrCBkjd8>BPZAus=rWLCTee?i1usZ7!dJ2N&3~Gbsg$xz=qi35r4( zGX7aVZQY94Jj}i5vZWv_$f&WO<(}cK z0`w`H-3o8xuu&)GX<>Q-(*7=fN%sB>bZ)zh3aN8I;|H^Eev!!#rCE91m!b@NpD60| zo>qpxeYdRYq(p(#rS)OedLW+4bn@3{FT&uUi?2Ty@MnOhJeQ0E>OiiMrjjh2El)8ptCoeoW3?=Y?Qwte%O@(QIJ$ zwwOP$mk%H?RL^*Pr%&PUB*{~UFs zyaXC!xLA*^C_&G+BRp%jHQ@e1ljil;@(}Hj{GYR!8hUMBb8B)T-UDk>u2%VaIA7B2 z;jv%`_q!)4MMnN9)co2y%=IP*eFdI~ux&OI?6kR?3qmH)^|M2iMaBTGGiZ#>&tAo% zK}{u(!ZM()lpy8OA11$T_D} zPp1sFLr!I`c6wmQxElJ8M;Te;R_$UJ&tmDsdz!(ERuFzwEz6`f4G$ZgRWJ)tLK+sm zuPVg6v-tzNGtWjKzNnY~R;+UwO>Ct8u*S$EZ^PslJ61gu6VJTD`0xgNSs%W7!Z;uQ zTW49kM5Buf^Y*{L>dWGlfXdaJOUW4AnQ!@$kJ#gzKYQ|uUI^(*enh))>ArSQ|MYV482L~^5V!*8=nYDPrXE@`xdZQr+vJ;XM6}2Uh&XYH@ zSMW6IH0%HPN$QItA43A0<2&GQSq1HO1UEzk({-N;&;g5+$EpS`3Xw zx(ttS!=G2?&X$}2Kb$xO3s^IuWml4(g@!mcwS1A=yrYCg{$w#1$E@(_&xPu-iF&x% zs9^Bn3bF4J^!BT;vqU*$pmx7Q@bscLCK`7uk(G7tb!)nMB=xd#IzJ$Uf-!8Gi~H1`Ez=XJ@3#)g_x)0^)zNvv2JEOco2(k_SqU}|(mqM3#o`|w{Wl4n zJZNnYEJvPu8l#<+s&wcgq0@si+xXu#AXl;cl~eq!PpwSvphJ)aRQOLB+Qe7llFGv6 z29Hc|X3g$D@`M3iPF!pfb|cQ0YgrruiC4j?QQAu~&I;5oxD~%z6+lvoiYL#<<`jx9 ziYL^#X2U&Kb1}``T^BT%<&F~3>-CPo0&kLHor*#$fDKlPnd=>@IaYK#wWfYY6UHtHgNd~)_f9gl* zvck*TZEbWCv~c0sIMt|fK1eZhJC5f_;i+CNbD_`tz!!GwobTxvRBii^c}*Y{&P?US zafC$y$=jsCizFGCBVK%DtymfN>Zd8L#z+&Mz2|NfHGa@no;=5zQ;vp<8MhLf)v)Be z;6)2#Hqh{7Q?erF!#R3OmH|DfXudP^Cjamrydg`*p!!z^E&fbLHBVGP6lK3pQc(l` zr=ZTL{7xMU-#G^OC!64jJ0FExbkg88_xHrD-G1cPWp#h^O%ER(ppW|FWCF4=J1M?q zrMMul@&2P@Ii4B&EBl_F1MBv({GwvfgfaJd>a8w5f^Q|MpqBT!PgmOh-^YL=$hrIR zY5!z7ejPw-ds*Us|1c#jpS3CV#1RQsEkR}(4>Y4VzKTFQ#xSG5?ZBc6r>&+Qxe zuO?wa#M576IR_!PW$tCStrDOH0S!q_#GH5DD_T1mFe!b9&ht(({GbksQ5+=l->GJE z4cB>5EI+J!zDXGCMb*ys#YaF{5!a+OaSkSb{yHxrNE&xEZuB1M6@@b!iH9}}qLAXt zl52@u9!jcD?#UGohd=JGuCfj-nh~ z^0@fS%wGy7;_|O^HcO++!WE8Bj|gv<=vemmjdUV+;apUc;eCl~oB`9UW?of8>Id{-RP& zkx9Vuu`q|_rW$x2bQ5K#lZKG%gxWSZ9QqDST-mRm37JoWcrI|NzRUAp zRBEyHKAx3^fX>#2?fw+57KXA^3@)dGLVV-1!kO(2C2n-7pV!PYZ3 zhYHyov@rHN9U4G*$b2HJZe&KoogsHkqxu-Aouu7lvLuHt@!ZbcV{#bMI`fOEDG|1q zX~U^pZ$oC6Wb#jC4X9|X_z?LL%`ewkDST3ayf@cxnJcw0slVO#G3@BrlL84CS zj<*|R67@zZA;6*f9osrL6n`l{xTPWr9U7W9BW7aoilK0U_RSpF|J!MhtA-gBk6%gF zZdXSKyCUlO_5v^vESXz4kcc{(RDV_jByff^Yx$81k*B{~71k)Ig7Nz6K@M*(BcrZL z<%!c$u*JZ1Z(6||&QZ1PAGunJTh%^q`lBxsJ=4c)!p4Fy!+hQ%d{ZB=mh9&^Jr@K| zF!Rok3W7L{pz}QEUwK>=jJY@t+dHVkQOMHiTuX%5b}}C2Q+CL% z8nIWxK^Nz%0t=cAWS z-T7%YF`)LX{dTB;_HpR0cM-c%KA9)Gz{pRWl1We}|5S%K#9rSa1; zn6+d$aJz-T)4D|>jbvgkpUvoXJ z%cX%lHWsF?My!h?Gx|soXWq2#=n%^-al&u*IHc@haV>h?qjZyY3xZ*7rfCjloSK9hUc#a zduYNF%E5C#?iApm#cKJ92gLVt+ft^>UKQ9bxKOD(tHGY_Cf;wdJxG0)sc_?O2bB4K zaBTf<3px#bVne-Fkhx=o930Qtob@j}2IRcOoI4+sU{hpMNCk;!$ zufO(FNfmIx@hVo%W?envUQ;|oWgQJ8XFtb|j9$euuN?smUI)U@k-9J9b2KJOM47V( zN`k(!T(IdsW#H^&@}E_Yf|SNt#&VBq_*Kf5clu=+e$!)j)Uq!I?ZVgx{44Un5pSpR z02JZc?SIiU2aAdQs4F>Rej#|qskJb@Ex|tcH1mZ#8(uk?cdbbBVZFw|^Jm!h!M?*k zGNVlTk!o6(WBcx1$mJ2fn^{T$v>8X)nz(hLqwQA+xyM}$j;M5|&1i=j?&t$jtn9#d zE`NBO$kUawJuK)e{dB);E|I-d#g5ds1zU$Bt=-v8dP!a!K+Nu4oUlb>3ik3BM36H>J7NsdeE?BAUEtb6azhf$fbf;a-C-TUR8p+J`A*NWa+}g`_ubjw3 z?iQb7EWq^t7_O_VQ=!a2sL}ToL#$xdWIgD`h*n=~oT^pDkUp?;RAVC(Y#{w8jaeG@ zoM&%-tYnM}GFnBYF79Zk{P@K|(;jHq!)bcpz#ZhesZdCM-w7$z1I3O^1fqqra#yJO z9jHnPxBjzOj-KX1RYjEQc%75%>dHPVOdC#jxOhPk7wN7C4pr(R1?1A-GC zpJXWX@vb3!9y3gLAioFaj{CHn$m_*7)a$csR)9Btk$t8l6$9qCFP{90X@PAc>zC|O zO;{~@{e2SYX&l`gZs+$yb7h0WOb1OjIM)+&yH9K%PepHZixek&ezs*%VyM;ma z*IIuE?Dl<6VKnk!P=W5*1`#>i3}}2lc9uHwCQ@gnhR?J+Ljad|)$|U*t6=?O#rXRy zB(Zl??2FX`Cw;aWt@C-{!_35MdbAu@6u zk<)s>q1sTmw5kq@uQ*uiE~jI^OP*YnN*4S>$*3jTV?gng?`Qcpeu&oKVI i`{M| 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≤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

1KrlF$E^!SZus- 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�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 XGSjy@9UZaiuyDmSqTk^E-NgR`{gfQM literal 0 HcmV?d00001 From 5ebbcd21a42ef858f454097a4173e5d32dc94e8e Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 00:30:44 +0800 Subject: [PATCH 24/33] Reduce Charge header dependencies and name intermediate values --- source/source_estate/module_charge/chg_rhog_io.cpp | 1 + source/source_estate/module_charge/chg_rhog_io.h | 3 ++- source/source_hsolver/module_rt_pw/exp_taylor.cpp | 5 +++-- source/source_pw/module_rt/td_conserv_pw.cpp | 1 + source/source_pw/module_rt/td_conserv_pw.h | 2 +- source/source_pw/module_rt/td_spin_pw.cpp | 3 ++- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/source/source_estate/module_charge/chg_rhog_io.cpp b/source/source_estate/module_charge/chg_rhog_io.cpp index c199db7313a..70cc45cddba 100644 --- a/source/source_estate/module_charge/chg_rhog_io.cpp +++ b/source/source_estate/module_charge/chg_rhog_io.cpp @@ -3,6 +3,7 @@ #include "source_base/module_out/binstream.h" #include "source_base/module_parallel/para_mpi_func.h" #include "source_base/vector3.h" +#include "source_estate/module_charge/charge.h" #include #include diff --git a/source/source_estate/module_charge/chg_rhog_io.h b/source/source_estate/module_charge/chg_rhog_io.h index e6c2220eb6b..03cb0b34a8f 100644 --- a/source/source_estate/module_charge/chg_rhog_io.h +++ b/source/source_estate/module_charge/chg_rhog_io.h @@ -3,7 +3,6 @@ #include "source_base/module_parallel/para_world.h" #include "source_basis/module_pw/pw_basis.h" -#include "source_estate/module_charge/charge.h" #include #include @@ -44,6 +43,8 @@ * rho */ +class Charge; + namespace module_charge { diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.cpp b/source/source_hsolver/module_rt_pw/exp_taylor.cpp index 78123c4d884..31857bb7a5d 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.cpp +++ b/source/source_hsolver/module_rt_pw/exp_taylor.cpp @@ -38,6 +38,7 @@ ExpResult ExpTaylor::evaluate(const HSOperator& op, ModuleBase::timer::start("ExpTaylor", "evaluate"); const int ld = layout.ld; const int bands = layout.bands; + const int stride = ld * bands; const int dim = layout.npol == 2 ? ld : layout.npw; const int64_t count = static_cast(ld) * bands; linear_buffer(&term_, count); @@ -70,11 +71,11 @@ ExpResult ExpTaylor::evaluate(const HSOperator& op, ++result.hpsi_calls; result.hpsi_cols += bands; } - const auto norms = this->algebra_.dots(ld, dim, bands, 1, ld * bands, half, half); + const auto norms = this->algebra_.dots(ld, dim, bands, 1, stride, half, half); std::vector> full_norms; if (full) { - full_norms = this->algebra_.dots(ld, dim, bands, 1, ld * bands, full, full); + full_norms = this->algebra_.dots(ld, dim, bands, 1, stride, full, full); } for (int b = 0; b < bands; ++b) { diff --git a/source/source_pw/module_rt/td_conserv_pw.cpp b/source/source_pw/module_rt/td_conserv_pw.cpp index e3a06794e88..71db266b136 100644 --- a/source/source_pw/module_rt/td_conserv_pw.cpp +++ b/source/source_pw/module_rt/td_conserv_pw.cpp @@ -2,6 +2,7 @@ #include "source_base/parallel_reduce.h" #include "source_base/timer.h" +#include "source_estate/module_charge/charge.h" namespace pw { diff --git a/source/source_pw/module_rt/td_conserv_pw.h b/source/source_pw/module_rt/td_conserv_pw.h index 8354e779502..af2a31bedc9 100644 --- a/source/source_pw/module_rt/td_conserv_pw.h +++ b/source/source_pw/module_rt/td_conserv_pw.h @@ -1,7 +1,7 @@ #ifndef TD_CONSERV_PW_H #define TD_CONSERV_PW_H -#include "source_estate/module_charge/charge.h" +class Charge; namespace pw { diff --git a/source/source_pw/module_rt/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp index f91ce6ca33a..b1bb642d295 100644 --- a/source/source_pw/module_rt/td_spin_pw.cpp +++ b/source/source_pw/module_rt/td_spin_pw.cpp @@ -40,7 +40,8 @@ double image_dist(const ModuleBase::Vector3& delta, const ModuleBase::Ma for (int k = lower[2]; k <= upper[2]; ++k) { const ModuleBase::Vector3 image(delta.x + i, delta.y + j, delta.z + k); - best = std::min(best, (image * lattice).norm2()); + const double distance_sq = (image * lattice).norm2(); + best = std::min(best, distance_sq); } } } From 6b8d28397f0e45661c42f19701de29f3533cdb87 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 00:46:11 +0800 Subject: [PATCH 25/33] Retrigger CI checks From c5508caf8e83971499108d5fe319a2b851753df4 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 01:03:09 +0800 Subject: [PATCH 26/33] Retrigger CI checks From a9318a9468fc433053d46c9e1f8fef9ec4f4fa7a Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 11:31:24 +0800 Subject: [PATCH 27/33] Revert CUDA trtri unit test --- .../ATen/kernels/test/lapack_test.cpp | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp b/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp index 8e402d3ee6a..a318bd8c6b8 100644 --- a/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp +++ b/source/source_base/module_container/ATen/kernels/test/lapack_test.cpp @@ -4,11 +4,6 @@ #include #include -#include -#include -#include -#include - namespace container { namespace kernels { @@ -60,136 +55,6 @@ TYPED_TEST(LapackTest, Trtri) { EXPECT_EQ(C, I); } -#if defined(__CUDA) -namespace { -template -T trtri_value(double real, double imag) -{ - return static_cast(real); -} - -template <> -std::complex trtri_value(double real, double imag) -{ - return std::complex(real, imag); -} - -template <> -std::complex trtri_value(double real, double imag) -{ - return std::complex(real, imag); -} -} // namespace - -template -class CudaTrtriTest : public LapackTest {}; - -using CudaTrtriTypes = ::testing::Types, std::complex>; -TYPED_TEST_SUITE(CudaTrtriTest, CudaTrtriTypes); - -TYPED_TEST(CudaTrtriTest, InverseAndUntouchedStorage) -{ - using T = TypeParam; - using Real = typename GetTypeReal::type; - for (const int n : {1, 3, 7}) - { - const int padded = n + 3; - for (const int lda : {n, padded}) - { - for (const char uplo : {'U', 'L'}) - { - for (const char diag : {'N', 'U'}) - { - SCOPED_TRACE(::testing::Message() << "n=" << n << " lda=" << lda << " uplo=" << uplo << " diag=" << diag); - Tensor input(DataTypeToEnum::value, {lda, n}); - T* data = input.data(); - std::vector> matrix(n * n, 0.0); - for (int col = 0; col < n; ++col) - { - for (int row = 0; row < lda; ++row) - { - data[col * lda + row] = T(-9); - const bool selected = uplo == 'U' ? row <= col : row >= col; - if (row < n && selected) - { - const double real = row == col ? 2.0 + row : 0.05 * (row + col + 1); - const double imag = row == col ? 0.0 : 0.03 * (row - col); - const T value = trtri_value(real, imag); - const bool implicit = row == col && diag == 'U'; - // Zero stored diagonals prove that unit-diagonal TRSM ignores them. - data[col * lda + row] = implicit ? T(0) : value; - matrix[col * n + row] = implicit ? T(1) : value; - } - } - } - Tensor device = input.to_device(); - lapack_trtri()(uplo, diag, n, device.data(), lda); - const Tensor result = device.to_device(); - const T* inverse = result.data(); - std::vector> unpacked(n * n, 0.0); - for (int col = 0; col < n; ++col) - { - for (int row = 0; row < lda; ++row) - { - const bool selected = uplo == 'U' ? row <= col : row >= col; - if (row < n && selected) - { - const bool implicit = row == col && diag == 'U'; - unpacked[col * n + row] = implicit ? T(1) : inverse[col * lda + row]; - } - else - { - EXPECT_EQ(inverse[col * lda + row], data[col * lda + row]); - } - } - } - const double tolerance = 50 * n * std::numeric_limits::epsilon(); - for (int col = 0; col < n; ++col) - { - for (int row = 0; row < n; ++row) - { - std::complex left = 0.0; - std::complex right = 0.0; - // Independent host products, without calling the BLAS implementation under test. - for (int k = 0; k < n; ++k) - { - left += matrix[k * n + row] * unpacked[col * n + k]; - right += unpacked[k * n + row] * matrix[col * n + k]; - } - const double expected = row == col ? 1.0 : 0.0; - EXPECT_LE(std::abs(left - expected), tolerance); - EXPECT_LE(std::abs(right - expected), tolerance); - } - } - } - } - } - } -} - -TYPED_TEST(CudaTrtriTest, RejectsSingularDiagonal) -{ - using T = TypeParam; - const int n = 3; - const int lda = 6; - const char diag = 'N'; - for (const char uplo : {'U', 'L'}) - { - for (const int zero : {0, n - 1}) - { - Tensor input(DataTypeToEnum::value, {lda, n}); - input.zero(); - for (int i = 0; i < n; ++i) - { - input.data()[i * lda + i] = i == zero ? T(0) : T(2); - } - Tensor device = input.to_device(); - EXPECT_THROW((lapack_trtri()(uplo, diag, n, device.data(), lda)), std::runtime_error); - } - } -} -#endif - TYPED_TEST(LapackTest, Potrf) { using Type = typename std::tuple_element<0, decltype(TypeParam())>::type; using Device = typename std::tuple_element<1, decltype(TypeParam())>::type; From d603b2f6b51851dec0f5a804b67d799a8a336536 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Wed, 28 Oct 2026 22:26:55 +0800 Subject: [PATCH 28/33] Restore direct Charge header includes to follow ABACUS coding guidelines --- source/source_estate/module_charge/chg_rhog_io.cpp | 1 - source/source_estate/module_charge/chg_rhog_io.h | 3 +-- source/source_pw/module_rt/td_conserv_pw.cpp | 1 - source/source_pw/module_rt/td_conserv_pw.h | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/source/source_estate/module_charge/chg_rhog_io.cpp b/source/source_estate/module_charge/chg_rhog_io.cpp index 70cc45cddba..c199db7313a 100644 --- a/source/source_estate/module_charge/chg_rhog_io.cpp +++ b/source/source_estate/module_charge/chg_rhog_io.cpp @@ -3,7 +3,6 @@ #include "source_base/module_out/binstream.h" #include "source_base/module_parallel/para_mpi_func.h" #include "source_base/vector3.h" -#include "source_estate/module_charge/charge.h" #include #include diff --git a/source/source_estate/module_charge/chg_rhog_io.h b/source/source_estate/module_charge/chg_rhog_io.h index 03cb0b34a8f..e6c2220eb6b 100644 --- a/source/source_estate/module_charge/chg_rhog_io.h +++ b/source/source_estate/module_charge/chg_rhog_io.h @@ -3,6 +3,7 @@ #include "source_base/module_parallel/para_world.h" #include "source_basis/module_pw/pw_basis.h" +#include "source_estate/module_charge/charge.h" #include #include @@ -43,8 +44,6 @@ * rho */ -class Charge; - namespace module_charge { diff --git a/source/source_pw/module_rt/td_conserv_pw.cpp b/source/source_pw/module_rt/td_conserv_pw.cpp index 71db266b136..e3a06794e88 100644 --- a/source/source_pw/module_rt/td_conserv_pw.cpp +++ b/source/source_pw/module_rt/td_conserv_pw.cpp @@ -2,7 +2,6 @@ #include "source_base/parallel_reduce.h" #include "source_base/timer.h" -#include "source_estate/module_charge/charge.h" namespace pw { diff --git a/source/source_pw/module_rt/td_conserv_pw.h b/source/source_pw/module_rt/td_conserv_pw.h index af2a31bedc9..8354e779502 100644 --- a/source/source_pw/module_rt/td_conserv_pw.h +++ b/source/source_pw/module_rt/td_conserv_pw.h @@ -1,7 +1,7 @@ #ifndef TD_CONSERV_PW_H #define TD_CONSERV_PW_H -class Charge; +#include "source_estate/module_charge/charge.h" namespace pw { From 8e686b4a9f7930a727a80b374072144a00f85fc1 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Thu, 29 Oct 2026 14:40:00 +0800 Subject: [PATCH 29/33] Replace auto with explicit types --- source/source_esolver/esolver_fp.cpp | 2 +- source/source_esolver/esolver_ks.cpp | 2 +- source/source_esolver/esolver_ks_pw_tddft.cpp | 38 +++++----- source/source_estate/test/td_field_test.cpp | 8 +-- .../module_rt_pw/exp_lanczos.cpp | 12 ++-- .../module_rt_pw/exp_taylor.cpp | 2 +- .../module_rt_pw/test/test_exp_action.cpp | 28 ++++---- .../test/test_hsolver_pw_tddft.cpp | 4 +- source/source_psi/setup_psi_pw.cpp | 70 ++++++++----------- source/source_pw/module_rt/prop_cn_pw.cpp | 2 +- source/source_pw/module_rt/prop_etrs_pw.cpp | 2 +- source/source_pw/module_rt/td_prop_pw.cpp | 2 +- source/source_pw/module_rt/td_spin_pw.cpp | 2 +- .../module_rt/test/test_td_obs_pw.cpp | 4 +- .../module_rt/test/test_td_spin_pw.cpp | 8 +-- .../module_rt/test/test_td_vel_pw.cpp | 6 +- 16 files changed, 91 insertions(+), 101 deletions(-) diff --git a/source/source_esolver/esolver_fp.cpp b/source/source_esolver/esolver_fp.cpp index 111bb688577..070e860f388 100644 --- a/source/source_esolver/esolver_fp.cpp +++ b/source/source_esolver/esolver_fp.cpp @@ -278,7 +278,7 @@ void ESolver_FP::iter_finish(UnitCell& ucell, const int istep, int& iter, bool& { if (iter % this->inp_->out_freq_elec == 0 || iter == this->inp_->scf_nmax || conv_esolver) { - const auto& system = PARAM.globalv; + const System_para& system = PARAM.globalv; const bool write_restart = this->inp_->out_chg[0] != -1; const std::string prefix = system.global_out_dir + this->inp_->suffix; const bool write_tau = XC_Functional::get_ked_flag(); diff --git a/source/source_esolver/esolver_ks.cpp b/source/source_esolver/esolver_ks.cpp index ad8e9015512..d81f3bf435c 100644 --- a/source/source_esolver/esolver_ks.cpp +++ b/source/source_esolver/esolver_ks.cpp @@ -411,7 +411,7 @@ void ESolver_KS::iter_finish(UnitCell& ucell, const int istep, int& iter, bool & const bool output_charge = iter % this->inp_->out_freq_elec == 0 || conv_esolver || iter == iter_limit; if (!this->defer_iter_output(istep) && output_charge) { - const auto& system = PARAM.globalv; + const System_para& system = PARAM.globalv; const bool write_restart = this->inp_->out_chg[0] != -1; const std::string prefix = system.global_out_dir + this->inp_->suffix; const bool write_tau = XC_Functional::get_ked_flag(); diff --git a/source/source_esolver/esolver_ks_pw_tddft.cpp b/source/source_esolver/esolver_ks_pw_tddft.cpp index 77d7b91a9c8..cec0c4f8e1d 100644 --- a/source/source_esolver/esolver_ks_pw_tddft.cpp +++ b/source/source_esolver/esolver_ks_pw_tddft.cpp @@ -227,10 +227,10 @@ bool ESolver_KS_PW_TDDFT::advance_interval(UnitCell& cell, if (!converged) { - auto& current = *this->stp.template get_psi_t(); - auto& estate = *static_cast*>(this->pelec); - auto& hamilt = *static_cast*>(this->p_hamilt); - const auto& vecpot = this->td_field_manager_->A_right_ha(); + psi::Psi& current = *this->stp.template get_psi_t(); + elecstate::ElecStatePW& estate = *static_cast*>(this->pelec); + hamilt::HamiltPW& hamilt = *static_cast*>(this->p_hamilt); + const ModuleBase::Vector3& vecpot = this->td_field_manager_->A_right_ha(); pw::rebuild_td_end(current, estate, this->chr, @@ -286,7 +286,7 @@ void ESolver_KS_PW_TDDFT::run_prop(UnitCell& cell, int step) this->td_prop_->orbitals().set_orth_stats(stats); this->chr.save_rho_before_sum_band(); - const auto& system = PARAM.globalv; + const System_para& system = PARAM.globalv; const bool write_restart = this->inp_->out_chg[0] != -1; const std::string prefix = system.global_out_dir + this->inp_->suffix; const bool write_tau = XC_Functional::get_ked_flag(); @@ -351,8 +351,8 @@ void ESolver_KS_PW_TDDFT::prepare_init_hamilt(const UnitCell& ucell) { // Both endpoints also bound any interpolated sample inside the interval. const double q_unshifted = pw::td_momentum_bound(*this->pw_wfc, ucell.tpiba); - const auto& left = this->td_field_manager_->A_left_ha(); - const auto& right = this->td_field_manager_->A_right_ha(); + const ModuleBase::Vector3& left = this->td_field_manager_->A_left_ha(); + const ModuleBase::Vector3& right = this->td_field_manager_->A_right_ha(); pw::ensure_td_vnl(ucell, q_unshifted, right, left, &this->ppcell); elecstate::Potential& potential = *this->pelec->pot; @@ -457,9 +457,9 @@ void ESolver_KS_PW_TDDFT::hamilt2rho_single(UnitCell& ucell, const in { ESolver_KS_PW::hamilt2rho_single(ucell, istep, iter, ethr); - auto& current = *this->stp.template get_psi_t(); - auto& estate = *static_cast*>(this->pelec); - auto* hamilt = static_cast*>(this->p_hamilt); + psi::Psi& current = *this->stp.template get_psi_t(); + elecstate::ElecStatePW& estate = *static_cast*>(this->pelec); + hamilt::Hamilt* hamilt = static_cast*>(this->p_hamilt); hamilt::HamiltHSOperator op(hamilt, this->pw_wfc); const bool do_orth = this->inp_->td_orthonormal != "none"; pw::orth_init_pw(this->td_prop_->orbitals(), @@ -510,10 +510,10 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, } if (!conv_esolver && istep == 0) { - auto& current = *this->stp.template get_psi_t(); - auto& estate = *static_cast*>(this->pelec); - auto& hamilt = *static_cast*>(this->p_hamilt); - const auto& vecpot = this->td_field_manager_->A_right_ha(); + psi::Psi& current = *this->stp.template get_psi_t(); + elecstate::ElecStatePW& estate = *static_cast*>(this->pelec); + hamilt::HamiltPW& hamilt = *static_cast*>(this->p_hamilt); + const ModuleBase::Vector3& vecpot = this->td_field_manager_->A_right_ha(); pw::rebuild_td_end(current, estate, this->chr, @@ -531,12 +531,12 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, } // Endpoint diagnostics use the accepted endpoint, not the propagation midpoint or a mixed trial density. - auto* endpoint_hamilt = static_cast*>(this->p_hamilt); + hamilt::HamiltPW* endpoint_hamilt = static_cast*>(this->p_hamilt); const Real* endpoint_veff = this->pelec->pot->template get_veff_smooth_data(); endpoint_hamilt->bind_td_state(endpoint_veff, this->td_field_manager_->A_right_ha()); if (this->inp_->td_print_eij >= 0.0) { - auto& current = *this->stp.template get_psi_t(); + psi::Psi& current = *this->stp.template get_psi_t(); hamilt::HamiltHSOperator op(endpoint_hamilt, this->pw_wfc); ModuleIO::print_eij_pw(op, this->td_prop_->orbitals(), @@ -564,8 +564,8 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, this->history_.save(*current, *this->pelec->pot); } - const auto& current = *this->stp.template get_psi_t(); - const auto& vecpot = this->td_field_manager_->A_right_ha(); + const psi::Psi& current = *this->stp.template get_psi_t(); + const ModuleBase::Vector3& vecpot = this->td_field_manager_->A_right_ha(); const pw::TDVelResultPW currents = vel_.calculate(current, *this->pelec, vecpot, current_req_); pw::TDSpinResultPW moments; @@ -596,7 +596,7 @@ void ESolver_KS_PW_TDDFT::after_scf(UnitCell& ucell, const int istep, if (this->inp_->td_out_stat) { const double local_npsi = this->td_prop_->orbitals().wave_electrons(this->pelec->wg); - const auto& stats = this->td_prop_->orbitals().orth_stats(); + const hsolver::TDOrthStats& stats = this->td_prop_->orbitals().orth_stats(); const bool full_gram = this->inp_->td_orthonormal != "none"; const pw::TDConserv values = pw::cal_td_conserv(local_npsi, this->chr, ucell.omega, this->pw_wfc->poolrank, stats.before, stats.after, full_gram); diff --git a/source/source_estate/test/td_field_test.cpp b/source/source_estate/test/td_field_test.cpp index 5fe3827eb71..08b10312d86 100644 --- a/source/source_estate/test/td_field_test.cpp +++ b/source/source_estate/test/td_field_test.cpp @@ -120,7 +120,7 @@ TEST(TDFieldManagerTest, SubintervalsPreserveFieldAndOriginalGrid) for (bool from_file: {false, true}) { SCOPED_TRACE(from_file); - auto manager = elecstate::create_td_field_manager(input); + std::shared_ptr manager = elecstate::create_td_field_manager(input); if (from_file) { const std::vector> samples = {{0, 0, 0}, {2, 0, 0}, {5, 0, 0}}; @@ -128,10 +128,10 @@ TEST(TDFieldManagerTest, SubintervalsPreserveFieldAndOriginalGrid) } manager->prepare_sample(0); manager->prepare_interval(0, 1); - const auto full = manager->field_state(); - const auto left = manager->subinterval(full, 0, 2); + const elecstate::TDFieldState full = manager->field_state(); + const elecstate::TDFieldState left = manager->subinterval(full, 0, 2); manager->bind_field(left); - const auto right = manager->subinterval(full, 1, 2); + const elecstate::TDFieldState right = manager->subinterval(full, 1, 2); EXPECT_DOUBLE_EQ(left.dt, 0.5); EXPECT_DOUBLE_EQ(right.dt, 0.5); EXPECT_DOUBLE_EQ(left.left.x, full.left.x); diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp index af362843f21..c58a0f6431d 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -168,7 +168,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, } base_device::memory::synchronize_memory_op()(q, input, stride); zero_pad(q, layout); - const auto norms = this->algebra_.dots(ld, dim, bands, 1, stride, q, q); + const std::vector> norms = this->algebra_.dots(ld, dim, bands, 1, stride, q, q); std::vector scale(bands); std::vector factors(bands); std::vector done(bands, false); @@ -221,7 +221,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, zero_pad(w, layout); ++result.hpsi_calls; result.hpsi_cols += count; - const auto projection = this->algebra_.dots(ld, dim, bands, 1, stride, column, w); + const std::vector> projection = this->algebra_.dots(ld, dim, bands, 1, stride, column, w); const int m = j + 1; result.order_or_dim = m; const int first = std::max(0, j - 1); @@ -247,7 +247,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, std::vector removed(bands, 0.0); for (int pass = 0; pass < 2; ++pass) { - const auto projections = this->algebra_.dots(ld, dim, bands, m, stride, q, w); + const std::vector> projections = this->algebra_.dots(ld, dim, bands, m, stride, q, w); std::vector correction(m * bands, T(0)); for (int i = 0; i < m; ++i) { @@ -271,7 +271,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, correction_sq[b] += removed[b] * removed[b]; } } - const auto residuals = this->algebra_.dots(ld, dim, bands, 1, stride, w, w); + const std::vector> residuals = this->algebra_.dots(ld, dim, bands, 1, stride, w, w); std::vector candidates; for (int b: active) { @@ -303,7 +303,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, } if (!candidates.empty()) { - const auto gram = basis_gram(layout, m, q); + const std::vector> gram = basis_gram(layout, m, q); // The defect bound follows the three-term relation, not Q-adjoint*Q=I. // Rebuilding for insignificant loss of orthogonality wastes H applications. // Check the actual output norm separately at the requested precision. @@ -381,7 +381,7 @@ ExpResult ExpLanczos::attempt(const HSOperator& op, { combine(layout, m, q, full_coefficients, full); } - const auto output_norms = this->algebra_.dots(ld, dim, bands, 1, stride, output, output); + const std::vector> output_norms = this->algebra_.dots(ld, dim, bands, 1, stride, output, output); std::vector full_norms; if (full) { diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.cpp b/source/source_hsolver/module_rt_pw/exp_taylor.cpp index 31857bb7a5d..f58ea75769c 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.cpp +++ b/source/source_hsolver/module_rt_pw/exp_taylor.cpp @@ -71,7 +71,7 @@ ExpResult ExpTaylor::evaluate(const HSOperator& op, ++result.hpsi_calls; result.hpsi_cols += bands; } - const auto norms = this->algebra_.dots(ld, dim, bands, 1, stride, half, half); + const std::vector> norms = this->algebra_.dots(ld, dim, bands, 1, stride, half, half); std::vector> full_norms; if (full) { diff --git a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp index 3bebe3634b2..9a036f18b97 100644 --- a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp @@ -177,11 +177,11 @@ TYPED_TEST(ExpTest, LanczosMatchesFullExponential) SCOPED_TRACE(npol); DenseExp op(npol); const hsolver::OrbLayout layout{10, 6 / npol, npol, 10}; - const auto input = pack_input(op, layout); + const std::vector input = pack_input(op, layout); std::vector output(input.size()); hsolver::ExpControls exp_ctrl; - auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); - const auto result = exp->apply(op, layout, 0.4, input.data(), output.data()); + std::unique_ptr> exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); + const hsolver::ExpResult result = exp->apply(op, layout, 0.4, input.data(), output.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_TRUE(result.has_err_est); EXPECT_LE(result.rel_err_est, exp->rel_tol()); @@ -200,14 +200,14 @@ TYPED_TEST(ExpTest, TaylorPairMatchesBothPolynomials) SCOPED_TRACE(npol); DenseExp op(npol); const hsolver::OrbLayout layout{10, 6 / npol, npol, 3}; - const auto input = pack_input(op, layout); + const std::vector input = pack_input(op, layout); std::vector half(input.size()); std::vector full(input.size()); hsolver::ExpControls exp_ctrl; exp_ctrl.expansion = hsolver::ExpMethod::taylor; exp_ctrl.taylor_order = 6; - auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); - const auto result = exp->apply_pair(op, layout, 0.15, input.data(), half.data(), full.data()); + std::unique_ptr> exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); + const hsolver::ExpResult result = exp->apply_pair(op, layout, 0.15, input.data(), half.data(), full.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_FALSE(result.has_err_est); EXPECT_EQ(op.calls, exp_ctrl.taylor_order); @@ -222,13 +222,13 @@ TEST(ExpAction, LanczosSubdividesOrReportsExhaustion) { DenseExp op(1); const hsolver::OrbLayout layout{8, 6, 1, 2}; - const auto input = pack_input(op, layout); + const std::vector input = pack_input(op, layout); std::vector output(input.size()); hsolver::ExpControls exp_ctrl; exp_ctrl.krylov_max = 4; exp_ctrl.rel_tol = 1e-8; - auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); - const auto result = exp->apply(op, layout, 1.0, input.data(), output.data()); + std::unique_ptr> exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); + const hsolver::ExpResult result = exp->apply(op, layout, 1.0, input.data(), output.data()); ASSERT_EQ(result.state, hsolver::ExpState::success); EXPECT_GT(result.bisect_count, 0); EXPECT_LE(result.rel_err_est, exp_ctrl.rel_tol); @@ -239,7 +239,7 @@ TEST(ExpAction, LanczosSubdividesOrReportsExhaustion) exp_ctrl.krylov_max = 2; exp_ctrl.rel_tol = 1e-14; exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); - const auto failed = exp->apply(op, layout, 1.0, input.data(), output.data()); + const hsolver::ExpResult failed = exp->apply(op, layout, 1.0, input.data(), output.data()); EXPECT_EQ(failed.state, hsolver::ExpState::failed); EXPECT_GT(failed.bisect_count, 0); EXPECT_GE(failed.failed_band, 0); @@ -250,15 +250,15 @@ TEST(ExpAction, NonfiniteInputReportsOriginalBand) { DenseExp op(2); const hsolver::OrbLayout layout{10, 3, 2, 10}; - auto input = pack_input(op, layout); + std::vector input = pack_input(op, layout); input[9 * layout.ld] = Wide(std::numeric_limits::quiet_NaN(), 0); std::vector output(input.size()); - for (auto method: {hsolver::ExpMethod::lanczos, hsolver::ExpMethod::taylor}) + for (hsolver::ExpMethod method: {hsolver::ExpMethod::lanczos, hsolver::ExpMethod::taylor}) { hsolver::ExpControls exp_ctrl; exp_ctrl.expansion = method; - auto exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); - const auto result = exp->apply(op, layout, 0.1, input.data(), output.data()); + std::unique_ptr> exp = hsolver::make_exp_action(exp_ctrl, linear_test::world_comm()); + const hsolver::ExpResult result = exp->apply(op, layout, 0.1, input.data(), output.data()); EXPECT_EQ(result.state, hsolver::ExpState::failed); EXPECT_EQ(result.failed_band, 9); } diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp index d854fdca3de..67cfb74a8ab 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp @@ -211,8 +211,8 @@ TYPED_TEST(PWTDDFTTest, SpinorCNMatchesDenseSolve) { current.fix_k(ik); const int n = dimensions[ik]; - const auto reference = dense_cn(op.matrices[ik], packed[ik], n, bands, dt); - const auto eij = orbitals.cal_eij(op, current, ik); + const std::vector reference = dense_cn(op.matrices[ik], packed[ik], n, bands, dt); + const std::vector> eij = orbitals.cal_eij(op, current, ik); for (int band = 0; band < bands; ++band) { for (int spin = 0; spin < 2; ++spin) diff --git a/source/source_psi/setup_psi_pw.cpp b/source/source_psi/setup_psi_pw.cpp index 1f4bfd4239d..a07296676fb 100644 --- a/source/source_psi/setup_psi_pw.cpp +++ b/source/source_psi/setup_psi_pw.cpp @@ -118,8 +118,8 @@ void Setup_Psi_pw::update_psi_d_impl() { if (this->precision_type_ == PrecisionType::ComplexFloat) { - const auto* native = this->get_psi_t(); - auto* mirror = this->get_psi_d(); + const psi::Psi* native = this->get_psi_t(); + psi::Psi, Device>* mirror = this->get_psi_d(); if (mirror == nullptr) { this->psi_d = new psi::Psi, Device>(*native); @@ -264,19 +264,13 @@ void Setup_Psi_pw::sync_cpu() template void Setup_Psi_pw::castmem_d2h_impl(std::complex* dst, const std::complex* src, const std::size_t size) { - base_device::memory::cast_memory_op, std::complex, base_device::DEVICE_CPU, Device>()( - dst, - src, - size); + base_device::memory::cast_memory_op, std::complex, base_device::DEVICE_CPU, Device>()(dst, src, size); } template void Setup_Psi_pw::castmem_d2h_impl(std::complex* dst, const std::complex* src, const std::size_t size) { - base_device::memory::cast_memory_op, std::complex, base_device::DEVICE_CPU, Device>()( - dst, - src, - size); + base_device::memory::cast_memory_op, std::complex, base_device::DEVICE_CPU, Device>()(dst, src, size); } template @@ -326,21 +320,19 @@ void Setup_Psi_pw::clean() template class psi::PSIPrepare, base_device::DEVICE_CPU>; template class psi::PSIPrepare, base_device::DEVICE_CPU>; -template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_CPU>( - const UnitCell&, - const K_Vectors&, - const Structure_Factor&, - const ModulePW::PW_Basis_K&, - const int&, - const Input_para&); - -template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_CPU>( - const UnitCell&, - const K_Vectors&, - const Structure_Factor&, - const ModulePW::PW_Basis_K&, - const int&, - const Input_para&); +template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_CPU>(const UnitCell&, + const K_Vectors&, + const Structure_Factor&, + const ModulePW::PW_Basis_K&, + const int&, + const Input_para&); + +template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_CPU>(const UnitCell&, + const K_Vectors&, + const Structure_Factor&, + const ModulePW::PW_Basis_K&, + const int&, + const Input_para&); template void Setup_Psi_pw::init_impl, base_device::DEVICE_CPU>( hamilt::Hamilt, base_device::DEVICE_CPU>*); @@ -376,21 +368,19 @@ template void Setup_Psi_pw::castmem_d2h_impl, base_device:: template class psi::PSIPrepare, base_device::DEVICE_GPU>; template class psi::PSIPrepare, base_device::DEVICE_GPU>; -template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_GPU>( - const UnitCell&, - const K_Vectors&, - const Structure_Factor&, - const ModulePW::PW_Basis_K&, - const int&, - const Input_para&); - -template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_GPU>( - const UnitCell&, - const K_Vectors&, - const Structure_Factor&, - const ModulePW::PW_Basis_K&, - const int&, - const Input_para&); +template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_GPU>(const UnitCell&, + const K_Vectors&, + const Structure_Factor&, + const ModulePW::PW_Basis_K&, + const int&, + const Input_para&); + +template void Setup_Psi_pw::before_runner_impl, base_device::DEVICE_GPU>(const UnitCell&, + const K_Vectors&, + const Structure_Factor&, + const ModulePW::PW_Basis_K&, + const int&, + const Input_para&); template void Setup_Psi_pw::init_impl, base_device::DEVICE_GPU>( hamilt::Hamilt, base_device::DEVICE_GPU>*); diff --git a/source/source_pw/module_rt/prop_cn_pw.cpp b/source/source_pw/module_rt/prop_cn_pw.cpp index 20bdb99fab8..92a5b2d0312 100644 --- a/source/source_pw/module_rt/prop_cn_pw.cpp +++ b/source/source_pw/module_rt/prop_cn_pw.cpp @@ -51,7 +51,7 @@ void CNPropPW::evolve(TDHamiltPW& hamilt, } hamilt.bind(veff, vecpot_ha); - const auto& previous = history.previous(); + const psi::Psi& previous = history.previous(); const double dt = field.interval_dt_ha(); solver_.solve(hamilt, previous, current, dt, vecpot_ha, elec_step, scf_iter, log_detail); } diff --git a/source/source_pw/module_rt/prop_etrs_pw.cpp b/source/source_pw/module_rt/prop_etrs_pw.cpp index 7ac632d8320..5fbe94452fc 100644 --- a/source/source_pw/module_rt/prop_etrs_pw.cpp +++ b/source/source_pw/module_rt/prop_etrs_pw.cpp @@ -93,7 +93,7 @@ void ETRSPropPW::evolve(TDHamiltPW& hamilt, { using Real = typename T::value_type; const bool predictor = scf_iter == 1; - const auto& previous = history.previous(); + const psi::Psi& previous = history.previous(); const int ld = current->get_nbasis(); const int bands = current->get_nbands(); const int64_t stride = static_cast(ld) * bands; diff --git a/source/source_pw/module_rt/td_prop_pw.cpp b/source/source_pw/module_rt/td_prop_pw.cpp index 46499b43924..2409c5816e7 100644 --- a/source/source_pw/module_rt/td_prop_pw.cpp +++ b/source/source_pw/module_rt/td_prop_pw.cpp @@ -96,7 +96,7 @@ void TDPropPW::evolve(hamilt::HamiltPW& hamilt, prop_->evolve(td_hamilt, history, potential, field, current, elec_step, scf_iter, log_detail); orb_ops_->correct(current, elec_step, scf_iter, log_detail); const Real* endpoint = potential.template get_veff_smooth_data(); - const auto& vecpot_ha = field.A_right_ha(); + const ModuleBase::Vector3& vecpot_ha = field.A_right_ha(); td_hamilt.bind(endpoint, vecpot_ha); } template class TDPropPW, base_device::DEVICE_CPU>; diff --git a/source/source_pw/module_rt/td_spin_pw.cpp b/source/source_pw/module_rt/td_spin_pw.cpp index b1bb642d295..7c64a621326 100644 --- a/source/source_pw/module_rt/td_spin_pw.cpp +++ b/source/source_pw/module_rt/td_spin_pw.cpp @@ -321,7 +321,7 @@ void TDSpinPW::onsite(const psi::Psi& psi, const int nq = static_cast(std::ceil(q_bound_ / dq)) + 4; onsite_projector_->init(orbital_dir_, &cell, *onsite_psi_, kv, basis, sf, onsite_radius_, nq, dq, state.wg, state.ekb); } - auto* projector = onsite_projector_.get(); + projectors::OnsiteProjector* projector = onsite_projector_.get(); if (onsite_psi_->get_nbands() != nbands || onsite_psi_->get_nbasis() != ld_psi) { ModuleBase::WARNING_QUIT("TDSpinPW", "The onsite wavefunction layout changed after initialization."); diff --git a/source/source_pw/module_rt/test/test_td_obs_pw.cpp b/source/source_pw/module_rt/test/test_td_obs_pw.cpp index 3baa37b2c80..293ba741d6a 100644 --- a/source/source_pw/module_rt/test/test_td_obs_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_obs_pw.cpp @@ -54,7 +54,7 @@ void Observables::SetUp() cell_.omega = std::pow(cell_.lat0, 3) * cell_.latvec.Det(); cell_.tpiba = ModuleBase::TWO_PI / cell_.lat0; cell_.tpiba2 = cell_.tpiba * cell_.tpiba; - for (const auto& position: cell_.atoms[0].taud) + for (const ModuleBase::Vector3& position: cell_.atoms[0].taud) { cell_.atoms[0].tau.push_back(position * cell_.latvec); } @@ -181,7 +181,7 @@ Vector Observables::hamiltonian(const ModuleBase::Vector3& a, const Vect const int npw = basis_.npwk[0]; for (int g = 0; g < npw; ++g) { - const auto q = basis_.getgpluskcar(0, g) * cell_.tpiba + a; + const ModuleBase::Vector3 q = basis_.getgpluskcar(0, g) * cell_.tpiba + a; for (int s = 0; s < 2; ++s) { y[s * npw + g] += 0.5 * q.norm2() * x[s * npw + g]; diff --git a/source/source_pw/module_rt/test/test_td_spin_pw.cpp b/source/source_pw/module_rt/test/test_td_spin_pw.cpp index c60b855a5e6..8b2493aa258 100644 --- a/source/source_pw/module_rt/test/test_td_spin_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_spin_pw.cpp @@ -11,7 +11,7 @@ TEST_F(Observables, SOCTorqueMatchesCommutator) const std::string mag_partition = "none"; pw::TDSpinPW observer; observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, mag_partition, "", 0.0, true); - const auto actual = observer.calculate(*psi_, state_, a); + const pw::TDSpinResultPW actual = observer.calculate(*psi_, state_, a); const Vector beta = projectors(a); const int n = psi_->get_nbasis(); double signal = 0; @@ -43,7 +43,7 @@ TEST_F(Observables, SOCTorqueMatchesCommutator) TEST_F(Observables, DensityMomentsAndPeriodicVoronoi) { - const auto labels = pw::td_voronoi_pw(cell_, basis_); + const std::vector labels = pw::td_voronoi_pw(cell_, basis_); std::vector atom_integrals(cell_.nat, 0.0); const double dv = cell_.omega / basis_.nxyz; for (int ir = 0; ir < basis_.nrxx; ++ir) @@ -64,7 +64,7 @@ TEST_F(Observables, DensityMomentsAndPeriodicVoronoi) for (int k = -3; k <= 3; ++k) { const ModuleBase::Vector3 image(i, j, k); - const auto delta = (point - cell_.atoms[0].taud[atom] + image) * cell_.latvec; + const ModuleBase::Vector3 delta = (point - cell_.atoms[0].taud[atom] + image) * cell_.latvec; const double distance = delta.norm2(); if (distance < best - 1e-12) { @@ -91,7 +91,7 @@ TEST_F(Observables, DensityMomentsAndPeriodicVoronoi) pw::TDSpinPW observer; observer.initialize(cell_, basis_, basis_, kv_, sf_, pp_, mag_partition, "", 0.0, true); const ModuleBase::Vector3 zero(0, 0, 0); - const auto actual = observer.calculate(*psi_, state_, zero); + const pw::TDSpinResultPW actual = observer.calculate(*psi_, state_, zero); const double integral = cell_.omega * (1.0 + 0.005 * (basis_.nxyz - 1)); EXPECT_NEAR(actual.electrons, 2 * integral, 1e-10); EXPECT_NEAR(actual.mag_abs, std::sqrt(0.38) * integral, 1e-10); diff --git a/source/source_pw/module_rt/test/test_td_vel_pw.cpp b/source/source_pw/module_rt/test/test_td_vel_pw.cpp index bb9214b607f..6106a28f2b8 100644 --- a/source/source_pw/module_rt/test/test_td_vel_pw.cpp +++ b/source/source_pw/module_rt/test/test_td_vel_pw.cpp @@ -13,7 +13,7 @@ TEST_F(Observables, CurrentsMatchPauliAndHamiltonianDerivative) request.charge_total = true; request.charge_per_k = true; request.spin_current = true; - const auto actual = observer.calculate(*psi_, state_, a, request); + const pw::TDVelResultPW actual = observer.calculate(*psi_, state_, a, request); hamilt::Velocity velocity(&basis_, kv_.isk.data(), &pp_, &cell_, true, nullptr, 0, 0); velocity.init(0, a); const int n = psi_->get_nbasis(); @@ -29,8 +29,8 @@ TEST_F(Observables, CurrentsMatchPauliAndHamiltonianDerivative) for (int direction = 0; direction < 3; ++direction) { const Vector component(vx.begin() + direction * n, vx.begin() + (direction + 1) * n); - auto plus = a; - auto minus = a; + ModuleBase::Vector3 plus = a; + ModuleBase::Vector3 minus = a; plus[direction] += epsilon; minus[direction] -= epsilon; const Vector hp = hamiltonian(plus, x); From 3de32becf3b1ab7a43e58e222c82c52109a1923b Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Thu, 29 Oct 2026 15:14:13 +0800 Subject: [PATCH 30/33] Standardize unit test directories and filenames --- source/source_cell/cif_io.h | 4 +- source/source_estate/CMakeLists.txt | 2 +- .../{test => unittests}/CMakeLists.txt | 16 +++--- .../{test => unittests}/sccs_cell_test.h | 0 .../{test => unittests}/support/H.pz-vbc.UPF | 0 .../{test => unittests}/support/KPT | 0 .../support/LOWF_GAMMA_S1.dat | 0 .../{test => unittests}/support/LOWF_K_1.dat | 0 .../{test => unittests}/support/LOWF_K_2.dat | 0 .../{test => unittests}/support/LOWF_K_3.dat | 0 .../support/NOW_SPIN1_CHG.cube | 0 .../support/OLD1_SPIN1_CHG.cube | 0 .../{test => unittests}/support/STRU | 0 .../{test => unittests}/support/Si.upf | 0 .../support/Si_ONCV_PBE-1.0.upf | 0 .../support/Si_gga_8au_60Ry_2s2p1d.orb | 0 .../{test => unittests}/support/WAVEFUNC1.txt | 0 .../{test => unittests}/support/chg.cube | 0 .../support/rho_gamma_ref.dat | 0 .../{test => unittests}/support/rho_k_ref.dat | 0 .../{test => unittests}/support/si2.STRU | 0 .../test_charge_moments.cpp | 0 .../test_elecstate.cpp} | 0 .../test_elecstate_energy.cpp} | 0 .../test_elecstate_print.cpp} | 0 .../test_elecstate_pw.cpp} | 0 .../test_fp_energy.cpp} | 0 .../{test => unittests}/test_occ_comput.cpp | 0 .../{test => unittests}/test_occ_matrix.cpp | 0 .../{test => unittests}/test_occ_mixer.cpp | 0 .../test_occupy.cpp} | 0 .../{test => unittests}/test_pcc_0d.cpp | 0 .../{test => unittests}/test_pcc_2d.cpp | 0 .../test_pot_base.cpp} | 0 .../{test => unittests}/test_pot_pcc.cpp | 0 .../{test => unittests}/test_pot_sccs.cpp | 0 .../test_potential_new.cpp} | 0 .../{test => unittests}/test_sccs_pcc_0d.cpp | 0 .../{test => unittests}/test_sccs_pcc_2d.cpp | 0 .../test_td_field.cpp} | 0 .../module_sccs/test/CMakeLists.txt | 6 +-- source/source_hsolver/CMakeLists.txt | 2 +- source/source_hsolver/hsolver_pw.h | 2 +- .../module_rt_pw/CMakeLists.txt | 2 +- .../{test => unittests}/CMakeLists.txt | 2 +- .../{test => unittests}/linear_test_utils.h | 0 .../test_cn_solver_pw.cpp} | 2 +- .../{test => unittests}/test_cn_subspace.cpp | 2 +- .../{test => unittests}/test_exp_action.cpp | 2 +- .../test_hsolver_linear.cpp | 2 +- .../test_linear_low_rank.cpp | 2 +- .../{test => unittests}/test_orthonormal.cpp | 0 .../{test => unittests}/CMakeLists.txt | 30 +++++------ .../GammaOnly-Si2-Solution.dat | 0 .../GammaOnly-Si64-Solution.dat | 0 .../{test => unittests}/H-GammaOnly-Si2.dat | 0 .../{test => unittests}/H-GammaOnly-Si64.dat | 0 .../{test => unittests}/H-KPoints-Si2.dat | 0 .../{test => unittests}/H-KPoints-Si64.dat | 0 .../KPoints-Si2-Solution.dat | 0 .../KPoints-Si64-Solution.dat | 0 .../PEXSI-DM-GammaOnly-Si2.dat | 0 .../PEXSI-H-GammaOnly-Si2.dat | 0 .../PEXSI-S-GammaOnly-Si2.dat | 0 .../{test => unittests}/S-GammaOnly-Si2.dat | 0 .../{test => unittests}/S-GammaOnly-Si64.dat | 0 .../{test => unittests}/S-KPoints-Si2.dat | 0 .../{test => unittests}/S-KPoints-Si64.dat | 0 .../diago_cg_parallel_test.sh | 0 .../diago_david_parallel_test.sh | 0 .../{test => unittests}/diago_elpa_utils.h | 0 .../diago_lcao_parallel_test.sh | 0 .../{test => unittests}/diago_mock.h | 0 .../diago_pexsi_parallel_test.sh | 0 .../{test => unittests}/hsolver_pw_sup.h | 0 .../hsolver_supplementary_mock.h | 0 .../{test => unittests}/parallel_k2d_test.sh | 0 .../test_diag_hs_para.cpp} | 0 .../test_diago_bpcg.cpp} | 0 .../test_diago_cg.cpp} | 0 .../test_diago_cg_float.cpp} | 0 .../test_diago_cg_real.cpp} | 0 .../test_diago_cusolver.cpp} | 4 +- .../{test => unittests}/test_diago_david.cpp | 0 .../test_diago_david_double.cpp} | 0 .../test_diago_david_float.cpp} | 0 .../test_diago_david_real.cpp} | 0 .../test_diago_lapack.cpp} | 4 +- .../test_diago_lcao.cpp} | 4 +- .../test_diago_pexsi.cpp} | 2 +- .../{test => unittests}/test_hsolver.cpp | 0 .../{test => unittests}/test_hsolver_pw.cpp | 0 .../test_para_lin_tf.cpp} | 0 .../test_parallel_k2d.cpp} | 0 source/source_io/CMakeLists.txt | 2 +- .../module_dm/test/write_dmk_test.cpp | 2 +- .../{test => unittests}/CMakeLists.txt | 48 +++++++++--------- source/source_io/{test => unittests}/INPUTs | 0 .../source_io/{test => unittests}/dos_test.h | 0 .../{test => unittests}/for_testing_klist.h | 0 .../output_mulliken_mock.cpp | 0 .../{test => unittests}/prepare_unitcell.h | 0 .../BesselBasis_UnitTest_C4_AtomType0.html | 0 .../{test => unittests}/support/DMK_nspin1 | 0 .../{test => unittests}/support/DMK_nspin2 | 0 .../{test => unittests}/support/INPUT | 0 .../support/INPUT.diago_proc_full | 0 .../support/INPUT.diago_proc_subset | 0 .../{test => unittests}/support/INPUT.plumed | 0 .../support/INPUT.td_field_mixed | 0 .../source_io/{test => unittests}/support/KPT | 0 .../support/QO_ovlpR_0.dat | 0 .../{test => unittests}/support/SK_nspin1 | 0 .../{test => unittests}/support/SK_nspin2 | 0 .../{test => unittests}/support/SR.csr | 0 .../{test => unittests}/support/STRU | 0 .../{test => unittests}/support/Si.upf | 0 .../{test => unittests}/support/WINPUT | 0 .../support/charge-density.dat | Bin .../{test => unittests}/support/dm_nao.txt | 0 .../{test => unittests}/support/dmk1_nao.txt | 0 .../{test => unittests}/support/istate.info | 0 .../{test => unittests}/support/kpoints | 0 .../{test => unittests}/support/sc.json | 0 .../{test => unittests}/support/wf_nao.txt | 0 .../{test => unittests}/support/wfs1_nao.txt | 0 .../support/wfs1k1_nao.txt | 0 .../{test => unittests}/support/wfs1k1_pw.dat | Bin .../support/wfs1k2_nao.txt | 0 .../support/wfs1k3_nao.txt | 0 .../support/wfs1k4_nao.txt | 0 .../{test => unittests}/support/wfs2_nao.txt | 0 .../test_bessel_basis.cpp} | 0 .../test_input_help.cpp} | 0 .../test_numerical_basis.cpp} | 0 .../test_orb_io.cpp} | 0 .../test_output.cpp} | 0 .../test_output_log.cpp} | 0 .../test_output_mulliken.cpp} | 0 .../test_parse_args.cpp} | 0 .../test_print_info.cpp} | 0 .../test_read_exit_file.cpp} | 0 .../{test => unittests}/test_read_input.cmake | 0 .../{test => unittests}/test_read_input.cpp | 0 .../test_read_input_parallel.cpp} | 2 +- .../test_read_wf2rho_pw.cpp} | 0 .../test_read_wfc_lcao.cpp} | 0 .../test_read_wfc_nao.cpp} | 0 .../test_read_wfc_pw.cpp} | 0 .../test_restart_exx_csr.cpp} | 0 .../{test => unittests}/test_spin_tag.cpp | 0 .../test_td_efield_io.cpp} | 0 .../test_td_vecpot_io.cpp} | 0 .../test_to_qo.cpp} | 0 .../test_write_bands.cpp} | 0 .../test_write_eig_occ.cpp} | 0 .../test_write_elf_logic.cpp} | 0 .../test_write_orb_info.cpp} | 0 .../test_write_wfc_nao.cpp} | 0 .../{test => unittests}/tmp_mocks.cpp | 0 .../{test => unittests}/write_wfc_nao_para.sh | 0 source/source_pw/module_pwdft/CMakeLists.txt | 2 +- .../{test => unittests}/CMakeLists.txt | 6 +-- .../test_dftu_base.cpp} | 0 .../test_dftu_pw_tools.cpp | 0 .../test_pw_hybrid_source.cpp | 0 .../soc_test.cpp => unittests/test_soc.cpp} | 0 .../test_stru_fac.cpp} | 0 source/source_pw/module_rt/CMakeLists.txt | 2 +- .../{test => unittests}/CMakeLists.txt | 2 +- .../td_test_utils.cpp} | 2 +- .../{test => unittests}/td_test_utils.h | 0 .../{test => unittests}/test_td_spin_pw.cpp | 2 +- .../{test => unittests}/test_td_vel_pw.cpp | 2 +- .../module_stodft/test/CMakeLists.txt | 2 +- .../test/test_sto_hsolver_pw.cpp | 4 +- 176 files changed, 84 insertions(+), 84 deletions(-) rename source/source_estate/{test => unittests}/CMakeLists.txt (89%) rename source/source_estate/{test => unittests}/sccs_cell_test.h (100%) rename source/source_estate/{test => unittests}/support/H.pz-vbc.UPF (100%) rename source/source_estate/{test => unittests}/support/KPT (100%) rename source/source_estate/{test => unittests}/support/LOWF_GAMMA_S1.dat (100%) rename source/source_estate/{test => unittests}/support/LOWF_K_1.dat (100%) rename source/source_estate/{test => unittests}/support/LOWF_K_2.dat (100%) rename source/source_estate/{test => unittests}/support/LOWF_K_3.dat (100%) rename source/source_estate/{test => unittests}/support/NOW_SPIN1_CHG.cube (100%) rename source/source_estate/{test => unittests}/support/OLD1_SPIN1_CHG.cube (100%) rename source/source_estate/{test => unittests}/support/STRU (100%) rename source/source_estate/{test => unittests}/support/Si.upf (100%) rename source/source_estate/{test => unittests}/support/Si_ONCV_PBE-1.0.upf (100%) rename source/source_estate/{test => unittests}/support/Si_gga_8au_60Ry_2s2p1d.orb (100%) rename source/source_estate/{test => unittests}/support/WAVEFUNC1.txt (100%) rename source/source_estate/{test => unittests}/support/chg.cube (100%) rename source/source_estate/{test => unittests}/support/rho_gamma_ref.dat (100%) rename source/source_estate/{test => unittests}/support/rho_k_ref.dat (100%) rename source/source_estate/{test => unittests}/support/si2.STRU (100%) rename source/source_estate/{test => unittests}/test_charge_moments.cpp (100%) rename source/source_estate/{test/elecstate_base_test.cpp => unittests/test_elecstate.cpp} (100%) rename source/source_estate/{test/elecstate_energy_test.cpp => unittests/test_elecstate_energy.cpp} (100%) rename source/source_estate/{test/elecstate_print_test.cpp => unittests/test_elecstate_print.cpp} (100%) rename source/source_estate/{test/elecstate_pw_test.cpp => unittests/test_elecstate_pw.cpp} (100%) rename source/source_estate/{test/elecstate_fp_energy_test.cpp => unittests/test_fp_energy.cpp} (100%) rename source/source_estate/{test => unittests}/test_occ_comput.cpp (100%) rename source/source_estate/{test => unittests}/test_occ_matrix.cpp (100%) rename source/source_estate/{test => unittests}/test_occ_mixer.cpp (100%) rename source/source_estate/{test/elecstate_occupy_test.cpp => unittests/test_occupy.cpp} (100%) rename source/source_estate/{test => unittests}/test_pcc_0d.cpp (100%) rename source/source_estate/{test => unittests}/test_pcc_2d.cpp (100%) rename source/source_estate/{test/potentials_base_test.cpp => unittests/test_pot_base.cpp} (100%) rename source/source_estate/{test => unittests}/test_pot_pcc.cpp (100%) rename source/source_estate/{test => unittests}/test_pot_sccs.cpp (100%) rename source/source_estate/{test/potential_new_test.cpp => unittests/test_potential_new.cpp} (100%) rename source/source_estate/{test => unittests}/test_sccs_pcc_0d.cpp (100%) rename source/source_estate/{test => unittests}/test_sccs_pcc_2d.cpp (100%) rename source/source_estate/{test/td_field_test.cpp => unittests/test_td_field.cpp} (100%) rename source/source_hsolver/module_rt_pw/{test => unittests}/CMakeLists.txt (93%) rename source/source_hsolver/module_rt_pw/{test => unittests}/linear_test_utils.h (100%) rename source/source_hsolver/module_rt_pw/{test/test_hsolver_pw_tddft.cpp => unittests/test_cn_solver_pw.cpp} (99%) rename source/source_hsolver/module_rt_pw/{test => unittests}/test_cn_subspace.cpp (98%) rename source/source_hsolver/module_rt_pw/{test => unittests}/test_exp_action.cpp (99%) rename source/source_hsolver/module_rt_pw/{test => unittests}/test_hsolver_linear.cpp (99%) rename source/source_hsolver/module_rt_pw/{test => unittests}/test_linear_low_rank.cpp (98%) rename source/source_hsolver/module_rt_pw/{test => unittests}/test_orthonormal.cpp (100%) rename source/source_hsolver/{test => unittests}/CMakeLists.txt (87%) rename source/source_hsolver/{test => unittests}/GammaOnly-Si2-Solution.dat (100%) rename source/source_hsolver/{test => unittests}/GammaOnly-Si64-Solution.dat (100%) rename source/source_hsolver/{test => unittests}/H-GammaOnly-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/H-GammaOnly-Si64.dat (100%) rename source/source_hsolver/{test => unittests}/H-KPoints-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/H-KPoints-Si64.dat (100%) rename source/source_hsolver/{test => unittests}/KPoints-Si2-Solution.dat (100%) rename source/source_hsolver/{test => unittests}/KPoints-Si64-Solution.dat (100%) rename source/source_hsolver/{test => unittests}/PEXSI-DM-GammaOnly-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/PEXSI-H-GammaOnly-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/PEXSI-S-GammaOnly-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/S-GammaOnly-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/S-GammaOnly-Si64.dat (100%) rename source/source_hsolver/{test => unittests}/S-KPoints-Si2.dat (100%) rename source/source_hsolver/{test => unittests}/S-KPoints-Si64.dat (100%) rename source/source_hsolver/{test => unittests}/diago_cg_parallel_test.sh (100%) rename source/source_hsolver/{test => unittests}/diago_david_parallel_test.sh (100%) rename source/source_hsolver/{test => unittests}/diago_elpa_utils.h (100%) rename source/source_hsolver/{test => unittests}/diago_lcao_parallel_test.sh (100%) rename source/source_hsolver/{test => unittests}/diago_mock.h (100%) rename source/source_hsolver/{test => unittests}/diago_pexsi_parallel_test.sh (100%) rename source/source_hsolver/{test => unittests}/hsolver_pw_sup.h (100%) rename source/source_hsolver/{test => unittests}/hsolver_supplementary_mock.h (100%) rename source/source_hsolver/{test => unittests}/parallel_k2d_test.sh (100%) rename source/source_hsolver/{test/test_diago_hs_para.cpp => unittests/test_diag_hs_para.cpp} (100%) rename source/source_hsolver/{test/diago_bpcg_test.cpp => unittests/test_diago_bpcg.cpp} (100%) rename source/source_hsolver/{test/diago_cg_test.cpp => unittests/test_diago_cg.cpp} (100%) rename source/source_hsolver/{test/diago_cg_float_test.cpp => unittests/test_diago_cg_float.cpp} (100%) rename source/source_hsolver/{test/diago_cg_real_test.cpp => unittests/test_diago_cg_real.cpp} (100%) rename source/source_hsolver/{test/diago_lcao_cusolver_test.cpp => unittests/test_diago_cusolver.cpp} (98%) rename source/source_hsolver/{test => unittests}/test_diago_david.cpp (100%) rename source/source_hsolver/{test/diago_david_test.cpp => unittests/test_diago_david_double.cpp} (100%) rename source/source_hsolver/{test/diago_david_float_test.cpp => unittests/test_diago_david_float.cpp} (100%) rename source/source_hsolver/{test/diago_david_real_test.cpp => unittests/test_diago_david_real.cpp} (100%) rename source/source_hsolver/{test/diago_lapack_test.cpp => unittests/test_diago_lapack.cpp} (99%) rename source/source_hsolver/{test/diago_lcao_test.cpp => unittests/test_diago_lcao.cpp} (99%) rename source/source_hsolver/{test/diago_pexsi_test.cpp => unittests/test_diago_pexsi.cpp} (99%) rename source/source_hsolver/{test => unittests}/test_hsolver.cpp (100%) rename source/source_hsolver/{test => unittests}/test_hsolver_pw.cpp (100%) rename source/source_hsolver/{test/test_para_linear_trans.cpp => unittests/test_para_lin_tf.cpp} (100%) rename source/source_hsolver/{test/parallel_k2d_test.cpp => unittests/test_parallel_k2d.cpp} (100%) rename source/source_io/{test => unittests}/CMakeLists.txt (86%) rename source/source_io/{test => unittests}/INPUTs (100%) rename source/source_io/{test => unittests}/dos_test.h (100%) rename source/source_io/{test => unittests}/for_testing_klist.h (100%) rename source/source_io/{test => unittests}/output_mulliken_mock.cpp (100%) rename source/source_io/{test => unittests}/prepare_unitcell.h (100%) rename source/source_io/{test => unittests}/support/BesselBasis_UnitTest_C4_AtomType0.html (100%) rename source/source_io/{test => unittests}/support/DMK_nspin1 (100%) rename source/source_io/{test => unittests}/support/DMK_nspin2 (100%) rename source/source_io/{test => unittests}/support/INPUT (100%) rename source/source_io/{test => unittests}/support/INPUT.diago_proc_full (100%) rename source/source_io/{test => unittests}/support/INPUT.diago_proc_subset (100%) rename source/source_io/{test => unittests}/support/INPUT.plumed (100%) rename source/source_io/{test => unittests}/support/INPUT.td_field_mixed (100%) rename source/source_io/{test => unittests}/support/KPT (100%) rename source/source_io/{test => unittests}/support/QO_ovlpR_0.dat (100%) rename source/source_io/{test => unittests}/support/SK_nspin1 (100%) rename source/source_io/{test => unittests}/support/SK_nspin2 (100%) rename source/source_io/{test => unittests}/support/SR.csr (100%) rename source/source_io/{test => unittests}/support/STRU (100%) rename source/source_io/{test => unittests}/support/Si.upf (100%) rename source/source_io/{test => unittests}/support/WINPUT (100%) rename source/source_io/{test => unittests}/support/charge-density.dat (100%) rename source/source_io/{test => unittests}/support/dm_nao.txt (100%) rename source/source_io/{test => unittests}/support/dmk1_nao.txt (100%) rename source/source_io/{test => unittests}/support/istate.info (100%) rename source/source_io/{test => unittests}/support/kpoints (100%) rename source/source_io/{test => unittests}/support/sc.json (100%) rename source/source_io/{test => unittests}/support/wf_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs1_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs1k1_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs1k1_pw.dat (100%) rename source/source_io/{test => unittests}/support/wfs1k2_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs1k3_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs1k4_nao.txt (100%) rename source/source_io/{test => unittests}/support/wfs2_nao.txt (100%) rename source/source_io/{test/bessel_basis_test.cpp => unittests/test_bessel_basis.cpp} (100%) rename source/source_io/{test/input_help_test.cpp => unittests/test_input_help.cpp} (100%) rename source/source_io/{test/numerical_basis_test.cpp => unittests/test_numerical_basis.cpp} (100%) rename source/source_io/{test/orb_io_test.cpp => unittests/test_orb_io.cpp} (100%) rename source/source_io/{test/output_test.cpp => unittests/test_output.cpp} (100%) rename source/source_io/{test/outputlog_test.cpp => unittests/test_output_log.cpp} (100%) rename source/source_io/{test/output_mulliken_test.cpp => unittests/test_output_mulliken.cpp} (100%) rename source/source_io/{test/parse_args_test.cpp => unittests/test_parse_args.cpp} (100%) rename source/source_io/{test/print_info_test.cpp => unittests/test_print_info.cpp} (100%) rename source/source_io/{test/read_exit_file_test.cpp => unittests/test_read_exit_file.cpp} (100%) rename source/source_io/{test => unittests}/test_read_input.cmake (100%) rename source/source_io/{test => unittests}/test_read_input.cpp (100%) rename source/source_io/{test/read_input_ptest.cpp => unittests/test_read_input_parallel.cpp} (99%) rename source/source_io/{test/read_wf2rho_pw_test.cpp => unittests/test_read_wf2rho_pw.cpp} (100%) rename source/source_io/{test/read_wfc_lcao_test.cpp => unittests/test_read_wfc_lcao.cpp} (100%) rename source/source_io/{test/read_wfc_nao_test.cpp => unittests/test_read_wfc_nao.cpp} (100%) rename source/source_io/{test/read_wfc_pw_test.cpp => unittests/test_read_wfc_pw.cpp} (100%) rename source/source_io/{test/restart_exx_csr_test.cpp => unittests/test_restart_exx_csr.cpp} (100%) rename source/source_io/{test => unittests}/test_spin_tag.cpp (100%) rename source/source_io/{test/td_efield_io_test.cpp => unittests/test_td_efield_io.cpp} (100%) rename source/source_io/{test/test_vecpot_io.cpp => unittests/test_td_vecpot_io.cpp} (100%) rename source/source_io/{test/to_qo_test.cpp => unittests/test_to_qo.cpp} (100%) rename source/source_io/{test/write_bands_test.cpp => unittests/test_write_bands.cpp} (100%) rename source/source_io/{test/write_eig_occ_test.cpp => unittests/test_write_eig_occ.cpp} (100%) rename source/source_io/{test/write_elf_logic_test.cpp => unittests/test_write_elf_logic.cpp} (100%) rename source/source_io/{test/write_orb_info_test.cpp => unittests/test_write_orb_info.cpp} (100%) rename source/source_io/{test/write_wfc_nao_test.cpp => unittests/test_write_wfc_nao.cpp} (100%) rename source/source_io/{test => unittests}/tmp_mocks.cpp (100%) rename source/source_io/{test => unittests}/write_wfc_nao_para.sh (100%) rename source/source_pw/module_pwdft/{test => unittests}/CMakeLists.txt (98%) rename source/source_pw/module_pwdft/{test/dftu_base_test.cpp => unittests/test_dftu_base.cpp} (100%) rename source/source_pw/module_pwdft/{test => unittests}/test_dftu_pw_tools.cpp (100%) rename source/source_pw/module_pwdft/{test => unittests}/test_pw_hybrid_source.cpp (100%) rename source/source_pw/module_pwdft/{test/soc_test.cpp => unittests/test_soc.cpp} (100%) rename source/source_pw/module_pwdft/{test/structure_factor_test.cpp => unittests/test_stru_fac.cpp} (100%) rename source/source_pw/module_rt/{test => unittests}/CMakeLists.txt (97%) rename source/source_pw/module_rt/{test/test_td_obs_pw.cpp => unittests/td_test_utils.cpp} (99%) rename source/source_pw/module_rt/{test => unittests}/td_test_utils.h (100%) rename source/source_pw/module_rt/{test => unittests}/test_td_spin_pw.cpp (98%) rename source/source_pw/module_rt/{test => unittests}/test_td_vel_pw.cpp (98%) diff --git a/source/source_cell/cif_io.h b/source/source_cell/cif_io.h index 538a04a678c..6c6e3e426c9 100644 --- a/source/source_cell/cif_io.h +++ b/source/source_cell/cif_io.h @@ -65,7 +65,7 @@ namespace ModuleIO * ... * NOTE1: only keys in table will have value with length larger than 1, otherwise all words will be * saved in the only element in std::vector. For example please see unittest of this - * class at source/source_io/test/cif_io_test.cpp. + * class at source/source_cell/test/cif_io_test.cpp. * NOTE2: One should note that for some cif files, the number will be in the format like * "0.00000000(1)", which means the uncertainty of the number is 1 in the last digit. * In this case, user should be careful to convert the string to double by its own. @@ -191,4 +191,4 @@ namespace ModuleIO std::map> raw_; }; } // namespace ModuleIO -#endif // MODULE_IO_CIF_IO_H \ No newline at end of file +#endif // MODULE_IO_CIF_IO_H diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index b78f8422663..e1e481a5ac9 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -97,7 +97,7 @@ endif() if(BUILD_TESTING) if(ENABLE_MPI) - add_subdirectory(test) + add_subdirectory(unittests) add_subdirectory(module_charge/unittests) endif() endif() diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/unittests/CMakeLists.txt similarity index 89% rename from source/source_estate/test/CMakeLists.txt rename to source/source_estate/unittests/CMakeLists.txt index 6a4036fe388..9cb03fbcc36 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/unittests/CMakeLists.txt @@ -56,19 +56,19 @@ endif() AddTest( TARGET MODULE_ESTATE_elecstate_occupy LIBS parameter base device - SOURCES elecstate_occupy_test.cpp ../occupy.cpp + SOURCES test_occupy.cpp ../occupy.cpp ) AddTest( TARGET MODULE_ESTATE_elecstate_fp_energy LIBS parameter base device - SOURCES elecstate_fp_energy_test.cpp ../fp_energy.cpp + SOURCES test_fp_energy.cpp ../fp_energy.cpp ) AddTest( TARGET MODULE_ESTATE_elecstate_print LIBS parameter base device symmetry - SOURCES elecstate_print_test.cpp ../elecstate_print.cpp ../occupy.cpp + SOURCES test_elecstate_print.cpp ../elecstate_print.cpp ../occupy.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp ) @@ -76,7 +76,7 @@ AddTest( TARGET MODULE_ESTATE_elecstate_base KEEP_FEATURE_DEFINITIONS __MPI LIBS parameter base device symmetry - SOURCES elecstate_base_test.cpp ../elecstate.cpp ../elecstate_tools.cpp ../occupy.cpp ../../source_psi/psi.cpp + SOURCES test_elecstate.cpp ../elecstate.cpp ../elecstate_tools.cpp ../occupy.cpp ../../source_psi/psi.cpp ../../source_base/module_fft/fft_bundle.cpp ../../source_base/module_fft/fft_cpu.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp ) @@ -84,7 +84,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_elecstate_pw LIBS parameter planewave_serial base device symmetry - SOURCES elecstate_pw_test.cpp + SOURCES test_elecstate_pw.cpp ../elecstate_pw.cpp ../uspp_density.cpp ../elecstate_pw_cal_tau.cpp @@ -101,7 +101,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_elecstate_energy LIBS parameter base device planewave_serial symmetry - SOURCES elecstate_energy_test.cpp + SOURCES test_elecstate_energy.cpp ../elecstate_energy.cpp ../fp_energy.cpp ../makov_payne.cpp @@ -111,7 +111,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_potentials_base - SOURCES potentials_base_test.cpp + SOURCES test_pot_base.cpp ) AddTest( @@ -135,7 +135,7 @@ AddTest( AddTest( TARGET MODULE_ESTATE_potentials_new LIBS parameter base device planewave_serial - SOURCES potential_new_test.cpp td_field_test.cpp ../module_pot/potential_new.cpp + SOURCES test_potential_new.cpp test_td_field.cpp ../module_pot/potential_new.cpp ../module_pot/td_field.cpp ../module_pot/td_field_profiles.cpp ../module_pot/td_field_manager.cpp ${pcc_test_sources} ) diff --git a/source/source_estate/test/sccs_cell_test.h b/source/source_estate/unittests/sccs_cell_test.h similarity index 100% rename from source/source_estate/test/sccs_cell_test.h rename to source/source_estate/unittests/sccs_cell_test.h diff --git a/source/source_estate/test/support/H.pz-vbc.UPF b/source/source_estate/unittests/support/H.pz-vbc.UPF similarity index 100% rename from source/source_estate/test/support/H.pz-vbc.UPF rename to source/source_estate/unittests/support/H.pz-vbc.UPF diff --git a/source/source_estate/test/support/KPT b/source/source_estate/unittests/support/KPT similarity index 100% rename from source/source_estate/test/support/KPT rename to source/source_estate/unittests/support/KPT diff --git a/source/source_estate/test/support/LOWF_GAMMA_S1.dat b/source/source_estate/unittests/support/LOWF_GAMMA_S1.dat similarity index 100% rename from source/source_estate/test/support/LOWF_GAMMA_S1.dat rename to source/source_estate/unittests/support/LOWF_GAMMA_S1.dat diff --git a/source/source_estate/test/support/LOWF_K_1.dat b/source/source_estate/unittests/support/LOWF_K_1.dat similarity index 100% rename from source/source_estate/test/support/LOWF_K_1.dat rename to source/source_estate/unittests/support/LOWF_K_1.dat diff --git a/source/source_estate/test/support/LOWF_K_2.dat b/source/source_estate/unittests/support/LOWF_K_2.dat similarity index 100% rename from source/source_estate/test/support/LOWF_K_2.dat rename to source/source_estate/unittests/support/LOWF_K_2.dat diff --git a/source/source_estate/test/support/LOWF_K_3.dat b/source/source_estate/unittests/support/LOWF_K_3.dat similarity index 100% rename from source/source_estate/test/support/LOWF_K_3.dat rename to source/source_estate/unittests/support/LOWF_K_3.dat diff --git a/source/source_estate/test/support/NOW_SPIN1_CHG.cube b/source/source_estate/unittests/support/NOW_SPIN1_CHG.cube similarity index 100% rename from source/source_estate/test/support/NOW_SPIN1_CHG.cube rename to source/source_estate/unittests/support/NOW_SPIN1_CHG.cube diff --git a/source/source_estate/test/support/OLD1_SPIN1_CHG.cube b/source/source_estate/unittests/support/OLD1_SPIN1_CHG.cube similarity index 100% rename from source/source_estate/test/support/OLD1_SPIN1_CHG.cube rename to source/source_estate/unittests/support/OLD1_SPIN1_CHG.cube diff --git a/source/source_estate/test/support/STRU b/source/source_estate/unittests/support/STRU similarity index 100% rename from source/source_estate/test/support/STRU rename to source/source_estate/unittests/support/STRU diff --git a/source/source_estate/test/support/Si.upf b/source/source_estate/unittests/support/Si.upf similarity index 100% rename from source/source_estate/test/support/Si.upf rename to source/source_estate/unittests/support/Si.upf diff --git a/source/source_estate/test/support/Si_ONCV_PBE-1.0.upf b/source/source_estate/unittests/support/Si_ONCV_PBE-1.0.upf similarity index 100% rename from source/source_estate/test/support/Si_ONCV_PBE-1.0.upf rename to source/source_estate/unittests/support/Si_ONCV_PBE-1.0.upf diff --git a/source/source_estate/test/support/Si_gga_8au_60Ry_2s2p1d.orb b/source/source_estate/unittests/support/Si_gga_8au_60Ry_2s2p1d.orb similarity index 100% rename from source/source_estate/test/support/Si_gga_8au_60Ry_2s2p1d.orb rename to source/source_estate/unittests/support/Si_gga_8au_60Ry_2s2p1d.orb diff --git a/source/source_estate/test/support/WAVEFUNC1.txt b/source/source_estate/unittests/support/WAVEFUNC1.txt similarity index 100% rename from source/source_estate/test/support/WAVEFUNC1.txt rename to source/source_estate/unittests/support/WAVEFUNC1.txt diff --git a/source/source_estate/test/support/chg.cube b/source/source_estate/unittests/support/chg.cube similarity index 100% rename from source/source_estate/test/support/chg.cube rename to source/source_estate/unittests/support/chg.cube diff --git a/source/source_estate/test/support/rho_gamma_ref.dat b/source/source_estate/unittests/support/rho_gamma_ref.dat similarity index 100% rename from source/source_estate/test/support/rho_gamma_ref.dat rename to source/source_estate/unittests/support/rho_gamma_ref.dat diff --git a/source/source_estate/test/support/rho_k_ref.dat b/source/source_estate/unittests/support/rho_k_ref.dat similarity index 100% rename from source/source_estate/test/support/rho_k_ref.dat rename to source/source_estate/unittests/support/rho_k_ref.dat diff --git a/source/source_estate/test/support/si2.STRU b/source/source_estate/unittests/support/si2.STRU similarity index 100% rename from source/source_estate/test/support/si2.STRU rename to source/source_estate/unittests/support/si2.STRU diff --git a/source/source_estate/test/test_charge_moments.cpp b/source/source_estate/unittests/test_charge_moments.cpp similarity index 100% rename from source/source_estate/test/test_charge_moments.cpp rename to source/source_estate/unittests/test_charge_moments.cpp diff --git a/source/source_estate/test/elecstate_base_test.cpp b/source/source_estate/unittests/test_elecstate.cpp similarity index 100% rename from source/source_estate/test/elecstate_base_test.cpp rename to source/source_estate/unittests/test_elecstate.cpp diff --git a/source/source_estate/test/elecstate_energy_test.cpp b/source/source_estate/unittests/test_elecstate_energy.cpp similarity index 100% rename from source/source_estate/test/elecstate_energy_test.cpp rename to source/source_estate/unittests/test_elecstate_energy.cpp diff --git a/source/source_estate/test/elecstate_print_test.cpp b/source/source_estate/unittests/test_elecstate_print.cpp similarity index 100% rename from source/source_estate/test/elecstate_print_test.cpp rename to source/source_estate/unittests/test_elecstate_print.cpp diff --git a/source/source_estate/test/elecstate_pw_test.cpp b/source/source_estate/unittests/test_elecstate_pw.cpp similarity index 100% rename from source/source_estate/test/elecstate_pw_test.cpp rename to source/source_estate/unittests/test_elecstate_pw.cpp diff --git a/source/source_estate/test/elecstate_fp_energy_test.cpp b/source/source_estate/unittests/test_fp_energy.cpp similarity index 100% rename from source/source_estate/test/elecstate_fp_energy_test.cpp rename to source/source_estate/unittests/test_fp_energy.cpp diff --git a/source/source_estate/test/test_occ_comput.cpp b/source/source_estate/unittests/test_occ_comput.cpp similarity index 100% rename from source/source_estate/test/test_occ_comput.cpp rename to source/source_estate/unittests/test_occ_comput.cpp diff --git a/source/source_estate/test/test_occ_matrix.cpp b/source/source_estate/unittests/test_occ_matrix.cpp similarity index 100% rename from source/source_estate/test/test_occ_matrix.cpp rename to source/source_estate/unittests/test_occ_matrix.cpp diff --git a/source/source_estate/test/test_occ_mixer.cpp b/source/source_estate/unittests/test_occ_mixer.cpp similarity index 100% rename from source/source_estate/test/test_occ_mixer.cpp rename to source/source_estate/unittests/test_occ_mixer.cpp diff --git a/source/source_estate/test/elecstate_occupy_test.cpp b/source/source_estate/unittests/test_occupy.cpp similarity index 100% rename from source/source_estate/test/elecstate_occupy_test.cpp rename to source/source_estate/unittests/test_occupy.cpp diff --git a/source/source_estate/test/test_pcc_0d.cpp b/source/source_estate/unittests/test_pcc_0d.cpp similarity index 100% rename from source/source_estate/test/test_pcc_0d.cpp rename to source/source_estate/unittests/test_pcc_0d.cpp diff --git a/source/source_estate/test/test_pcc_2d.cpp b/source/source_estate/unittests/test_pcc_2d.cpp similarity index 100% rename from source/source_estate/test/test_pcc_2d.cpp rename to source/source_estate/unittests/test_pcc_2d.cpp diff --git a/source/source_estate/test/potentials_base_test.cpp b/source/source_estate/unittests/test_pot_base.cpp similarity index 100% rename from source/source_estate/test/potentials_base_test.cpp rename to source/source_estate/unittests/test_pot_base.cpp diff --git a/source/source_estate/test/test_pot_pcc.cpp b/source/source_estate/unittests/test_pot_pcc.cpp similarity index 100% rename from source/source_estate/test/test_pot_pcc.cpp rename to source/source_estate/unittests/test_pot_pcc.cpp diff --git a/source/source_estate/test/test_pot_sccs.cpp b/source/source_estate/unittests/test_pot_sccs.cpp similarity index 100% rename from source/source_estate/test/test_pot_sccs.cpp rename to source/source_estate/unittests/test_pot_sccs.cpp diff --git a/source/source_estate/test/potential_new_test.cpp b/source/source_estate/unittests/test_potential_new.cpp similarity index 100% rename from source/source_estate/test/potential_new_test.cpp rename to source/source_estate/unittests/test_potential_new.cpp diff --git a/source/source_estate/test/test_sccs_pcc_0d.cpp b/source/source_estate/unittests/test_sccs_pcc_0d.cpp similarity index 100% rename from source/source_estate/test/test_sccs_pcc_0d.cpp rename to source/source_estate/unittests/test_sccs_pcc_0d.cpp diff --git a/source/source_estate/test/test_sccs_pcc_2d.cpp b/source/source_estate/unittests/test_sccs_pcc_2d.cpp similarity index 100% rename from source/source_estate/test/test_sccs_pcc_2d.cpp rename to source/source_estate/unittests/test_sccs_pcc_2d.cpp diff --git a/source/source_estate/test/td_field_test.cpp b/source/source_estate/unittests/test_td_field.cpp similarity index 100% rename from source/source_estate/test/td_field_test.cpp rename to source/source_estate/unittests/test_td_field.cpp diff --git a/source/source_hamilt/module_sccs/test/CMakeLists.txt b/source/source_hamilt/module_sccs/test/CMakeLists.txt index 3a8e88d9dfe..ad98a0503c2 100644 --- a/source/source_hamilt/module_sccs/test/CMakeLists.txt +++ b/source/source_hamilt/module_sccs/test/CMakeLists.txt @@ -7,9 +7,9 @@ AddTest(TARGET MODULE_HAMILT_sccs_parameters AddTest(TARGET MODULE_HAMILT_sccs_poisson LIBS parameter planewave device base SOURCES test_sccs_lowpass.cpp test_sccs_cav_deriv.cpp test_sccs_coul_0d.cpp test_sccs_coul_2d.cpp test_sccs_pw_coulomb.cpp test_sccs_ion_charge.cpp test_sccs_ion_force.cpp test_sccs_response.cpp test_sccs_functional.cpp - ../../../source_estate/test/test_pot_sccs.cpp - ../../../source_estate/test/test_sccs_pcc_0d.cpp - ../../../source_estate/test/test_sccs_pcc_2d.cpp + ../../../source_estate/unittests/test_pot_sccs.cpp + ../../../source_estate/unittests/test_sccs_pcc_0d.cpp + ../../../source_estate/unittests/test_sccs_pcc_2d.cpp ../../../source_estate/module_pot/pot_sccs.cpp ../../../source_estate/module_pot/sccs_pcc_0d.cpp ../../../source_estate/module_pot/sccs_pcc_2d.cpp diff --git a/source/source_hsolver/CMakeLists.txt b/source/source_hsolver/CMakeLists.txt index 69cd57b0461..be31407f1ae 100644 --- a/source/source_hsolver/CMakeLists.txt +++ b/source/source_hsolver/CMakeLists.txt @@ -83,7 +83,7 @@ if(ENABLE_ELPA) endif() if(BUILD_TESTING) - add_subdirectory(test) + add_subdirectory(unittests) if(ENABLE_MPI) add_subdirectory(kernels/test) endif() diff --git a/source/source_hsolver/hsolver_pw.h b/source/source_hsolver/hsolver_pw.h index d5cbb772a8c..db6764154c8 100644 --- a/source/source_hsolver/hsolver_pw.h +++ b/source/source_hsolver/hsolver_pw.h @@ -9,7 +9,7 @@ #include #include -class TestHSolverPW; // unit-test fixture, see source_hsolver/test/ +class TestHSolverPW; // unit-test fixture, see source_hsolver/unittests/ namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/CMakeLists.txt b/source/source_hsolver/module_rt_pw/CMakeLists.txt index c970b287e0a..83af817c833 100644 --- a/source/source_hsolver/module_rt_pw/CMakeLists.txt +++ b/source/source_hsolver/module_rt_pw/CMakeLists.txt @@ -22,5 +22,5 @@ if(ENABLE_COVERAGE) endif() if(BUILD_TESTING) - add_subdirectory(test) + add_subdirectory(unittests) endif() diff --git a/source/source_hsolver/module_rt_pw/test/CMakeLists.txt b/source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt similarity index 93% rename from source/source_hsolver/module_rt_pw/test/CMakeLists.txt rename to source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt index af40d55dd96..5456fa4e0fc 100644 --- a/source/source_hsolver/module_rt_pw/test/CMakeLists.txt +++ b/source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt @@ -20,7 +20,7 @@ AddTest( AddTest( TARGET MODULE_HSOLVER_pw_tddft LIBS parameter base device container psi planewave - SOURCES test_hsolver_pw_tddft.cpp test_orthonormal.cpp test_exp_action.cpp + SOURCES test_cn_solver_pw.cpp test_orthonormal.cpp test_exp_action.cpp ../exp_action.cpp ../exp_lanczos.cpp ../exp_taylor.cpp ../cn_solver_pw.cpp ../td_orbital_ops.cpp ../orb_buffer.cpp ../orthonormal.cpp ../orthonormal_small.cpp ${TD_LINEAR_TEST_SOURCES} ) diff --git a/source/source_hsolver/module_rt_pw/test/linear_test_utils.h b/source/source_hsolver/module_rt_pw/unittests/linear_test_utils.h similarity index 100% rename from source/source_hsolver/module_rt_pw/test/linear_test_utils.h rename to source/source_hsolver/module_rt_pw/unittests/linear_test_utils.h diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp b/source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp index 67cfb74a8ab..9c7c206f13e 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_pw_tddft.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp @@ -1,7 +1,7 @@ #include "source_base/parallel_global.h" #include "source_hsolver/module_rt_pw/cn_solver_pw.h" #include "source_hsolver/module_rt_pw/td_orbital_ops.h" -#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp b/source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp index 366b8b8cf08..91e65dc9beb 100644 --- a/source/source_hsolver/module_rt_pw/test/test_cn_subspace.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp @@ -1,6 +1,6 @@ #include "source_base/parallel_device.h" #include "source_hsolver/module_rt_pw/cn_subspace.h" -#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp b/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/test/test_exp_action.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp index 9a036f18b97..67684539cb6 100644 --- a/source/source_hsolver/module_rt_pw/test/test_exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp @@ -1,5 +1,5 @@ #include "source_hsolver/module_rt_pw/exp_action.h" -#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp b/source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp index 77e923709ad..4052abbf61c 100644 --- a/source/source_hsolver/module_rt_pw/test/test_hsolver_linear.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp @@ -1,7 +1,7 @@ #include "source_base/parallel_device.h" #include "source_base/parallel_global.h" #include "source_hsolver/module_rt_pw/hsolver_linear.h" -#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp b/source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp index 48ebb146bab..fd61e385634 100644 --- a/source/source_hsolver/module_rt_pw/test/test_linear_low_rank.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp @@ -1,5 +1,5 @@ #include "source_hsolver/module_rt_pw/linear_low_rank.h" -#include "source_hsolver/module_rt_pw/test/linear_test_utils.h" +#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp b/source/source_hsolver/module_rt_pw/unittests/test_orthonormal.cpp similarity index 100% rename from source/source_hsolver/module_rt_pw/test/test_orthonormal.cpp rename to source/source_hsolver/module_rt_pw/unittests/test_orthonormal.cpp diff --git a/source/source_hsolver/test/CMakeLists.txt b/source/source_hsolver/unittests/CMakeLists.txt similarity index 87% rename from source/source_hsolver/test/CMakeLists.txt rename to source/source_hsolver/unittests/CMakeLists.txt index 7b70f32d34b..d013a06ed42 100644 --- a/source/source_hsolver/test/CMakeLists.txt +++ b/source/source_hsolver/unittests/CMakeLists.txt @@ -7,49 +7,49 @@ if (ENABLE_MPI) AddTest( TARGET MODULE_HSOLVER_parak2d_test LIBS parameter base device MPI::MPI_CXX - SOURCES parallel_k2d_test.cpp ../parallel_k2d.cpp ../../source_cell/parallel_kpoints.cpp + SOURCES test_parallel_k2d.cpp ../parallel_k2d.cpp ../../source_cell/parallel_kpoints.cpp ) AddTest( TARGET MODULE_HSOLVER_bpcg LIBS parameter base psi device container - SOURCES diago_bpcg_test.cpp ../diago_bpcg.cpp ../para_lin_tf.cpp ../diago_iter_assist.cpp + SOURCES test_diago_bpcg.cpp ../diago_bpcg.cpp ../para_lin_tf.cpp ../diago_iter_assist.cpp ../../source_basis/module_pw/test/test_tool.cpp ) AddTest( TARGET MODULE_HSOLVER_cg LIBS parameter base psi device container - SOURCES diago_cg_test.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_cg.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) AddTest( TARGET MODULE_HSOLVER_cg_float LIBS parameter base psi device container - SOURCES diago_cg_float_test.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_cg_float.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) AddTest( TARGET MODULE_HSOLVER_dav LIBS parameter base psi device - SOURCES diago_david_test.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_david_double.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) AddTest( TARGET MODULE_HSOLVER_dav_float LIBS parameter base psi device - SOURCES diago_david_float_test.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_david_float.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) if(ENABLE_LCAO) AddTest( TARGET MODULE_HSOLVER_cg_real LIBS parameter base psi device container - SOURCES diago_cg_float_test.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_cg_float.cpp ../diago_cg.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) AddTest( TARGET MODULE_HSOLVER_dav_real LIBS parameter base psi device - SOURCES diago_david_real_test.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp + SOURCES test_diago_david_real.cpp ../diago_david.cpp ../diago_iter_assist.cpp ../diag_const_nums.cpp ../../source_basis/module_pw/test/test_tool.cpp ) endif() @@ -73,13 +73,13 @@ if (ENABLE_MPI) AddTest( TARGET MODULE_HSOLVER_LCAO LIBS parameter ELPA::ELPA base genelpa psi device - SOURCES diago_lcao_test.cpp ../diago_elpa.cpp ../diago_elpa_native.cpp ../diago_scalapack.cpp ../diago_lapack.cpp + SOURCES test_diago_lcao.cpp ../diago_elpa.cpp ../diago_elpa_native.cpp ../diago_scalapack.cpp ../diago_lapack.cpp ) else() AddTest( TARGET MODULE_HSOLVER_LCAO LIBS parameter base psi device - SOURCES diago_lcao_test.cpp ../diago_scalapack.cpp ../diago_lapack.cpp + SOURCES test_diago_lcao.cpp ../diago_scalapack.cpp ../diago_lapack.cpp ) endif() @@ -87,7 +87,7 @@ if (ENABLE_MPI) AddTest( TARGET MODULE_HSOLVER_LCAO_PEXSI LIBS parameter PEXSI::PEXSI base psi device pexsi - SOURCES diago_pexsi_test.cpp ../diago_pexsi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp + SOURCES test_diago_pexsi.cpp ../diago_pexsi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp ) endif() endif() @@ -95,7 +95,7 @@ if (ENABLE_MPI) AddTest( TARGET MODULE_HSOLVER_LCAO_cusolver LIBS parameter base psi device - SOURCES diago_lcao_cusolver_test.cpp ../diago_cusolver.cpp ../diago_scalapack.cpp + SOURCES test_diago_cusolver.cpp ../diago_cusolver.cpp ../diago_scalapack.cpp ../kernels/hegvd_op.cpp ../kernels/cuda/diag_cusolver.cu ) @@ -130,7 +130,7 @@ if(ENABLE_MPI) TARGET MODULE_HSOLVER_diago_hs_parallel LIBS parameter ELPA::ELPA base device MPI::MPI_CXX genelpa psi SOURCES - test_diago_hs_para.cpp + test_diag_hs_para.cpp ../diag_hs_para.cpp ../diago_pxxxgvx.cpp ../diago_elpa.cpp @@ -141,7 +141,7 @@ if(ENABLE_MPI) TARGET MODULE_HSOLVER_diago_hs_parallel LIBS parameter base device MPI::MPI_CXX psi SOURCES - test_diago_hs_para.cpp + test_diag_hs_para.cpp ../diag_hs_para.cpp ../diago_pxxxgvx.cpp ../diago_scalapack.cpp @@ -151,7 +151,7 @@ if(ENABLE_MPI) AddTest( TARGET MODULE_HSOLVER_linear_trans LIBS parameter base device MPI::MPI_CXX - SOURCES test_para_linear_trans.cpp ../para_lin_tf.cpp + SOURCES test_para_lin_tf.cpp ../para_lin_tf.cpp ) add_test( diff --git a/source/source_hsolver/test/GammaOnly-Si2-Solution.dat b/source/source_hsolver/unittests/GammaOnly-Si2-Solution.dat similarity index 100% rename from source/source_hsolver/test/GammaOnly-Si2-Solution.dat rename to source/source_hsolver/unittests/GammaOnly-Si2-Solution.dat diff --git a/source/source_hsolver/test/GammaOnly-Si64-Solution.dat b/source/source_hsolver/unittests/GammaOnly-Si64-Solution.dat similarity index 100% rename from source/source_hsolver/test/GammaOnly-Si64-Solution.dat rename to source/source_hsolver/unittests/GammaOnly-Si64-Solution.dat diff --git a/source/source_hsolver/test/H-GammaOnly-Si2.dat b/source/source_hsolver/unittests/H-GammaOnly-Si2.dat similarity index 100% rename from source/source_hsolver/test/H-GammaOnly-Si2.dat rename to source/source_hsolver/unittests/H-GammaOnly-Si2.dat diff --git a/source/source_hsolver/test/H-GammaOnly-Si64.dat b/source/source_hsolver/unittests/H-GammaOnly-Si64.dat similarity index 100% rename from source/source_hsolver/test/H-GammaOnly-Si64.dat rename to source/source_hsolver/unittests/H-GammaOnly-Si64.dat diff --git a/source/source_hsolver/test/H-KPoints-Si2.dat b/source/source_hsolver/unittests/H-KPoints-Si2.dat similarity index 100% rename from source/source_hsolver/test/H-KPoints-Si2.dat rename to source/source_hsolver/unittests/H-KPoints-Si2.dat diff --git a/source/source_hsolver/test/H-KPoints-Si64.dat b/source/source_hsolver/unittests/H-KPoints-Si64.dat similarity index 100% rename from source/source_hsolver/test/H-KPoints-Si64.dat rename to source/source_hsolver/unittests/H-KPoints-Si64.dat diff --git a/source/source_hsolver/test/KPoints-Si2-Solution.dat b/source/source_hsolver/unittests/KPoints-Si2-Solution.dat similarity index 100% rename from source/source_hsolver/test/KPoints-Si2-Solution.dat rename to source/source_hsolver/unittests/KPoints-Si2-Solution.dat diff --git a/source/source_hsolver/test/KPoints-Si64-Solution.dat b/source/source_hsolver/unittests/KPoints-Si64-Solution.dat similarity index 100% rename from source/source_hsolver/test/KPoints-Si64-Solution.dat rename to source/source_hsolver/unittests/KPoints-Si64-Solution.dat diff --git a/source/source_hsolver/test/PEXSI-DM-GammaOnly-Si2.dat b/source/source_hsolver/unittests/PEXSI-DM-GammaOnly-Si2.dat similarity index 100% rename from source/source_hsolver/test/PEXSI-DM-GammaOnly-Si2.dat rename to source/source_hsolver/unittests/PEXSI-DM-GammaOnly-Si2.dat diff --git a/source/source_hsolver/test/PEXSI-H-GammaOnly-Si2.dat b/source/source_hsolver/unittests/PEXSI-H-GammaOnly-Si2.dat similarity index 100% rename from source/source_hsolver/test/PEXSI-H-GammaOnly-Si2.dat rename to source/source_hsolver/unittests/PEXSI-H-GammaOnly-Si2.dat diff --git a/source/source_hsolver/test/PEXSI-S-GammaOnly-Si2.dat b/source/source_hsolver/unittests/PEXSI-S-GammaOnly-Si2.dat similarity index 100% rename from source/source_hsolver/test/PEXSI-S-GammaOnly-Si2.dat rename to source/source_hsolver/unittests/PEXSI-S-GammaOnly-Si2.dat diff --git a/source/source_hsolver/test/S-GammaOnly-Si2.dat b/source/source_hsolver/unittests/S-GammaOnly-Si2.dat similarity index 100% rename from source/source_hsolver/test/S-GammaOnly-Si2.dat rename to source/source_hsolver/unittests/S-GammaOnly-Si2.dat diff --git a/source/source_hsolver/test/S-GammaOnly-Si64.dat b/source/source_hsolver/unittests/S-GammaOnly-Si64.dat similarity index 100% rename from source/source_hsolver/test/S-GammaOnly-Si64.dat rename to source/source_hsolver/unittests/S-GammaOnly-Si64.dat diff --git a/source/source_hsolver/test/S-KPoints-Si2.dat b/source/source_hsolver/unittests/S-KPoints-Si2.dat similarity index 100% rename from source/source_hsolver/test/S-KPoints-Si2.dat rename to source/source_hsolver/unittests/S-KPoints-Si2.dat diff --git a/source/source_hsolver/test/S-KPoints-Si64.dat b/source/source_hsolver/unittests/S-KPoints-Si64.dat similarity index 100% rename from source/source_hsolver/test/S-KPoints-Si64.dat rename to source/source_hsolver/unittests/S-KPoints-Si64.dat diff --git a/source/source_hsolver/test/diago_cg_parallel_test.sh b/source/source_hsolver/unittests/diago_cg_parallel_test.sh similarity index 100% rename from source/source_hsolver/test/diago_cg_parallel_test.sh rename to source/source_hsolver/unittests/diago_cg_parallel_test.sh diff --git a/source/source_hsolver/test/diago_david_parallel_test.sh b/source/source_hsolver/unittests/diago_david_parallel_test.sh similarity index 100% rename from source/source_hsolver/test/diago_david_parallel_test.sh rename to source/source_hsolver/unittests/diago_david_parallel_test.sh diff --git a/source/source_hsolver/test/diago_elpa_utils.h b/source/source_hsolver/unittests/diago_elpa_utils.h similarity index 100% rename from source/source_hsolver/test/diago_elpa_utils.h rename to source/source_hsolver/unittests/diago_elpa_utils.h diff --git a/source/source_hsolver/test/diago_lcao_parallel_test.sh b/source/source_hsolver/unittests/diago_lcao_parallel_test.sh similarity index 100% rename from source/source_hsolver/test/diago_lcao_parallel_test.sh rename to source/source_hsolver/unittests/diago_lcao_parallel_test.sh diff --git a/source/source_hsolver/test/diago_mock.h b/source/source_hsolver/unittests/diago_mock.h similarity index 100% rename from source/source_hsolver/test/diago_mock.h rename to source/source_hsolver/unittests/diago_mock.h diff --git a/source/source_hsolver/test/diago_pexsi_parallel_test.sh b/source/source_hsolver/unittests/diago_pexsi_parallel_test.sh similarity index 100% rename from source/source_hsolver/test/diago_pexsi_parallel_test.sh rename to source/source_hsolver/unittests/diago_pexsi_parallel_test.sh diff --git a/source/source_hsolver/test/hsolver_pw_sup.h b/source/source_hsolver/unittests/hsolver_pw_sup.h similarity index 100% rename from source/source_hsolver/test/hsolver_pw_sup.h rename to source/source_hsolver/unittests/hsolver_pw_sup.h diff --git a/source/source_hsolver/test/hsolver_supplementary_mock.h b/source/source_hsolver/unittests/hsolver_supplementary_mock.h similarity index 100% rename from source/source_hsolver/test/hsolver_supplementary_mock.h rename to source/source_hsolver/unittests/hsolver_supplementary_mock.h diff --git a/source/source_hsolver/test/parallel_k2d_test.sh b/source/source_hsolver/unittests/parallel_k2d_test.sh similarity index 100% rename from source/source_hsolver/test/parallel_k2d_test.sh rename to source/source_hsolver/unittests/parallel_k2d_test.sh diff --git a/source/source_hsolver/test/test_diago_hs_para.cpp b/source/source_hsolver/unittests/test_diag_hs_para.cpp similarity index 100% rename from source/source_hsolver/test/test_diago_hs_para.cpp rename to source/source_hsolver/unittests/test_diag_hs_para.cpp diff --git a/source/source_hsolver/test/diago_bpcg_test.cpp b/source/source_hsolver/unittests/test_diago_bpcg.cpp similarity index 100% rename from source/source_hsolver/test/diago_bpcg_test.cpp rename to source/source_hsolver/unittests/test_diago_bpcg.cpp diff --git a/source/source_hsolver/test/diago_cg_test.cpp b/source/source_hsolver/unittests/test_diago_cg.cpp similarity index 100% rename from source/source_hsolver/test/diago_cg_test.cpp rename to source/source_hsolver/unittests/test_diago_cg.cpp diff --git a/source/source_hsolver/test/diago_cg_float_test.cpp b/source/source_hsolver/unittests/test_diago_cg_float.cpp similarity index 100% rename from source/source_hsolver/test/diago_cg_float_test.cpp rename to source/source_hsolver/unittests/test_diago_cg_float.cpp diff --git a/source/source_hsolver/test/diago_cg_real_test.cpp b/source/source_hsolver/unittests/test_diago_cg_real.cpp similarity index 100% rename from source/source_hsolver/test/diago_cg_real_test.cpp rename to source/source_hsolver/unittests/test_diago_cg_real.cpp diff --git a/source/source_hsolver/test/diago_lcao_cusolver_test.cpp b/source/source_hsolver/unittests/test_diago_cusolver.cpp similarity index 98% rename from source/source_hsolver/test/diago_lcao_cusolver_test.cpp rename to source/source_hsolver/unittests/test_diago_cusolver.cpp index bab49060153..8c8d48a78ec 100644 --- a/source/source_hsolver/test/diago_lcao_cusolver_test.cpp +++ b/source/source_hsolver/unittests/test_diago_cusolver.cpp @@ -1,6 +1,6 @@ #include "source_base/matrix_block.h" #include "source_hsolver/diago_scalapack.h" -#include "source_hsolver/test/diago_elpa_utils.h" +#include "source_hsolver/unittests/diago_elpa_utils.h" #include "mpi.h" #include "string.h" @@ -29,7 +29,7 @@ * - hsolver::DiagoScalapack::diag (for Scalapack) * * The 2d block cyclic distribution of H/S matrix is done by - * self-realized functions in source_hsolver/test/diago_elpa_utils.h + * self-realized functions in source_hsolver/unittests/diago_elpa_utils.h */ /// Minimal H(k)/S(k) supplier. The LCAO eigensolvers take the matrix blocks diff --git a/source/source_hsolver/test/test_diago_david.cpp b/source/source_hsolver/unittests/test_diago_david.cpp similarity index 100% rename from source/source_hsolver/test/test_diago_david.cpp rename to source/source_hsolver/unittests/test_diago_david.cpp diff --git a/source/source_hsolver/test/diago_david_test.cpp b/source/source_hsolver/unittests/test_diago_david_double.cpp similarity index 100% rename from source/source_hsolver/test/diago_david_test.cpp rename to source/source_hsolver/unittests/test_diago_david_double.cpp diff --git a/source/source_hsolver/test/diago_david_float_test.cpp b/source/source_hsolver/unittests/test_diago_david_float.cpp similarity index 100% rename from source/source_hsolver/test/diago_david_float_test.cpp rename to source/source_hsolver/unittests/test_diago_david_float.cpp diff --git a/source/source_hsolver/test/diago_david_real_test.cpp b/source/source_hsolver/unittests/test_diago_david_real.cpp similarity index 100% rename from source/source_hsolver/test/diago_david_real_test.cpp rename to source/source_hsolver/unittests/test_diago_david_real.cpp diff --git a/source/source_hsolver/test/diago_lapack_test.cpp b/source/source_hsolver/unittests/test_diago_lapack.cpp similarity index 99% rename from source/source_hsolver/test/diago_lapack_test.cpp rename to source/source_hsolver/unittests/test_diago_lapack.cpp index f5dead8b518..d12941bfeab 100644 --- a/source/source_hsolver/test/diago_lapack_test.cpp +++ b/source/source_hsolver/unittests/test_diago_lapack.cpp @@ -1,5 +1,5 @@ // Author: Zhang Xiaoyang -// A modified version of diago_lcao_test.cpp +// A modified version of test_diago_lcao.cpp // Remove some useless functions and dependencies. Serialized the full code // and refactored some function. @@ -293,4 +293,4 @@ int main(int argc, char** argv) { return 0; } -} \ No newline at end of file +} diff --git a/source/source_hsolver/test/diago_lcao_test.cpp b/source/source_hsolver/unittests/test_diago_lcao.cpp similarity index 99% rename from source/source_hsolver/test/diago_lcao_test.cpp rename to source/source_hsolver/unittests/test_diago_lcao.cpp index 18b4a30fb0c..dc19df5d6a3 100644 --- a/source/source_hsolver/test/diago_lcao_test.cpp +++ b/source/source_hsolver/unittests/test_diago_lcao.cpp @@ -1,7 +1,7 @@ #include "source_base/matrix_block.h" #include "source_hsolver/diago_scalapack.h" #include "source_hsolver/diago_lapack.h" -#include "source_hsolver/test/diago_elpa_utils.h" +#include "source_hsolver/unittests/diago_elpa_utils.h" #include "mpi.h" #include "string.h" @@ -29,7 +29,7 @@ * - hsolver::DiagoScalapack::diag (for Scalapack) * * The 2d block cyclic distribution of H/S matrix is done by - * self-realized functions in source_hsolver/test/diago_elpa_utils.h + * self-realized functions in source_hsolver/unittests/diago_elpa_utils.h */ /// Minimal H(k)/S(k) supplier. The LCAO eigensolvers take the matrix blocks diff --git a/source/source_hsolver/test/diago_pexsi_test.cpp b/source/source_hsolver/unittests/test_diago_pexsi.cpp similarity index 99% rename from source/source_hsolver/test/diago_pexsi_test.cpp rename to source/source_hsolver/unittests/test_diago_pexsi.cpp index 14b23d05b0d..b92e42311f8 100644 --- a/source/source_hsolver/test/diago_pexsi_test.cpp +++ b/source/source_hsolver/unittests/test_diago_pexsi.cpp @@ -6,7 +6,7 @@ #include "source_base/parallel_global.h" #include "source_basis/module_ao/parallel_orbitals.h" #include "source_hsolver/module_pexsi/pexsi_solver.h" -#include "source_hsolver/test/diago_elpa_utils.h" +#include "source_hsolver/unittests/diago_elpa_utils.h" #include #include diff --git a/source/source_hsolver/test/test_hsolver.cpp b/source/source_hsolver/unittests/test_hsolver.cpp similarity index 100% rename from source/source_hsolver/test/test_hsolver.cpp rename to source/source_hsolver/unittests/test_hsolver.cpp diff --git a/source/source_hsolver/test/test_hsolver_pw.cpp b/source/source_hsolver/unittests/test_hsolver_pw.cpp similarity index 100% rename from source/source_hsolver/test/test_hsolver_pw.cpp rename to source/source_hsolver/unittests/test_hsolver_pw.cpp diff --git a/source/source_hsolver/test/test_para_linear_trans.cpp b/source/source_hsolver/unittests/test_para_lin_tf.cpp similarity index 100% rename from source/source_hsolver/test/test_para_linear_trans.cpp rename to source/source_hsolver/unittests/test_para_lin_tf.cpp diff --git a/source/source_hsolver/test/parallel_k2d_test.cpp b/source/source_hsolver/unittests/test_parallel_k2d.cpp similarity index 100% rename from source/source_hsolver/test/parallel_k2d_test.cpp rename to source/source_hsolver/unittests/test_parallel_k2d.cpp diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 3be275fcdf1..622d2d6b07b 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -176,7 +176,7 @@ if(BUILD_TESTING) add_subdirectory(module_parameter/test) add_subdirectory(module_hs/unittests) add_subdirectory(test_serial) - add_subdirectory(test) + add_subdirectory(unittests) add_subdirectory(module_dos/unittests) endif() diff --git a/source/source_io/module_dm/test/write_dmk_test.cpp b/source/source_io/module_dm/test/write_dmk_test.cpp index 4d6a2a8f1bc..8a2998c58c0 100644 --- a/source/source_io/module_dm/test/write_dmk_test.cpp +++ b/source/source_io/module_dm/test/write_dmk_test.cpp @@ -8,7 +8,7 @@ #include "source_io/module_dm/write_dmk.h" #include "source_base/global_variable.h" -#include "../../test/prepare_unitcell.h" +#include "../../unittests/prepare_unitcell.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/unittests/CMakeLists.txt similarity index 86% rename from source/source_io/test/CMakeLists.txt rename to source/source_io/unittests/CMakeLists.txt index 945e8e92cf9..d80b96b75bb 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/unittests/CMakeLists.txt @@ -6,7 +6,7 @@ abacus_disable_feature_definitions(__EXX) AddTest( TARGET MODULE_IO_write_bands LIBS parameter base device symmetry - SOURCES write_bands_test.cpp ../module_energy/write_bands.cpp + SOURCES test_write_bands.cpp ../module_energy/write_bands.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp ) @@ -55,7 +55,7 @@ abacus_copy_test_files( AddTest( TARGET MODULE_IO_input_test_para LIBS parameter base device io_input - SOURCES read_input_ptest.cpp + SOURCES test_read_input_parallel.cpp ) add_test(NAME MODULE_IO_input_test_para_4 @@ -66,7 +66,7 @@ add_test(NAME MODULE_IO_input_test_para_4 AddTest( TARGET MODULE_IO_read_exit_file_test LIBS parameter base device - SOURCES read_exit_file_test.cpp + SOURCES test_read_exit_file.cpp ) add_test(NAME MODULE_IO_read_exit_file_test_para_4 @@ -77,13 +77,13 @@ add_test(NAME MODULE_IO_read_exit_file_test_para_4 AddTest( TARGET MODULE_IO_output_test LIBS parameter base device - SOURCES output_test.cpp + SOURCES test_output.cpp ) AddTest( TARGET MODULE_IO_td_efield_io_test LIBS parameter base device - SOURCES td_efield_io_test.cpp ../module_efield/td_efield_io.cpp + SOURCES test_td_efield_io.cpp ../module_efield/td_efield_io.cpp ../../source_estate/module_pot/td_field.cpp ../../source_estate/module_pot/td_field_profiles.cpp ../../source_estate/module_pot/td_field_manager.cpp ) @@ -91,13 +91,13 @@ AddTest( AddTest( TARGET MODULE_IO_td_vecpot_io_test LIBS parameter base device - SOURCES test_vecpot_io.cpp ../module_efield/td_vecpot_io.cpp + SOURCES test_td_vecpot_io.cpp ../module_efield/td_vecpot_io.cpp ) AddTest( TARGET MODULE_IO_write_eig_occ_test LIBS parameter base device symmetry - SOURCES write_eig_occ_test.cpp ../module_energy/write_eig_occ.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp + SOURCES test_write_eig_occ.cpp ../module_energy/write_eig_occ.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/cif_io.cpp ../../source_cell/reciprocal_grid.cpp ) @@ -106,13 +106,13 @@ AddTest( AddTest( TARGET MODULE_IO_print_info LIBS parameter base device symmetry cell_info - SOURCES print_info_test.cpp ../module_output/print_info.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp + SOURCES test_print_info.cpp ../module_output/print_info.cpp ../../source_cell/klist.cpp ../../source_cell/klist_io.cpp ../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp ) AddTest( TARGET MODULE_IO_write_wfc_nao LIBS parameter base psi device - SOURCES write_wfc_nao_test.cpp ../module_wf/write_wfc_nao.cpp ../../source_basis/module_ao/parallel_orbitals.cpp + SOURCES test_write_wfc_nao.cpp ../module_wf/write_wfc_nao.cpp ../../source_basis/module_ao/parallel_orbitals.cpp ) abacus_copy_test_files(write_wfc_nao_para.sh) @@ -125,31 +125,31 @@ add_test(NAME MODULE_IO_write_wfc_nao_para AddTest( TARGET MODULE_IO_write_orb_info LIBS parameter base device cell_info - SOURCES write_orb_info_test.cpp ../module_output/write_orb_info.cpp + SOURCES test_write_orb_info.cpp ../module_output/write_orb_info.cpp ) AddTest( TARGET MODULE_IO_parse_args LIBS parameter base device io_input - SOURCES parse_args_test.cpp ../parse_args.cpp ../input_help.cpp + SOURCES test_parse_args.cpp ../parse_args.cpp ../input_help.cpp ) AddTest( TARGET MODULE_IO_input_help_test LIBS parameter base device io_input - SOURCES input_help_test.cpp ../input_help.cpp + SOURCES test_input_help.cpp ../input_help.cpp ) AddTest( TARGET MODULE_IO_bessel_basis_test LIBS parameter base device - SOURCES bessel_basis_test.cpp ../module_bessel/bessel_basis.cpp + SOURCES test_bessel_basis.cpp ../module_bessel/bessel_basis.cpp ) AddTest( TARGET MODULE_IO_output_log_test LIBS parameter base device - SOURCES ../module_output/output_log.cpp outputlog_test.cpp ../../source_basis/module_pw/test/test_tool.cpp + SOURCES ../module_output/output_log.cpp test_output_log.cpp ../../source_basis/module_pw/test/test_tool.cpp ) if(ENABLE_LCAO) @@ -157,7 +157,7 @@ AddTest( TARGET MODULE_IO_to_qo_test LIBS parameter base device numerical_atomic_orbitals nao_smoothgen container orb SOURCES - to_qo_test.cpp + test_to_qo.cpp ../module_qo/to_qo_kernel.cpp ../module_qo/to_qo_mpi.cpp ../module_qo/to_qo_structures.cpp @@ -171,7 +171,7 @@ endif() AddTest( TARGET MODULE_IO_read_wfc_pw_test LIBS parameter base device planewave - SOURCES read_wfc_pw_test.cpp ../module_wf/read_wfc_pw.cpp ../../source_basis/module_pw/test/test_tool.cpp + SOURCES test_read_wfc_pw.cpp ../module_wf/read_wfc_pw.cpp ../../source_basis/module_pw/test/test_tool.cpp ) add_test(NAME MODULE_IO_read_wfc_pw_test_parallel @@ -182,7 +182,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.cpp ../../source_estate/module_charge/chg_tools.cpp ../../source_estate/module_charge/chg_parallel.cpp ../module_wf/write_wfc_pw.cpp + SOURCES test_read_wf2rho_pw.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.cpp ../../source_estate/module_charge/chg_tools.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 ) @@ -196,7 +196,7 @@ if(ENABLE_LCAO) AddTest( TARGET MODULE_IO_numerical_basis_test LIBS parameter base device numerical_atomic_orbitals nao_smoothgen container orb - SOURCES numerical_basis_test.cpp + SOURCES test_numerical_basis.cpp ../module_bessel/numerical_basis_jyjy.cpp ../../source_lcao/center2orb.cpp ) @@ -206,7 +206,7 @@ endif() AddTest( TARGET MODULE_IO_mulliken_test LIBS parameter base device - SOURCES output_mulliken_test.cpp output_mulliken_mock.cpp ../module_mulliken/output_mulliken.cpp + SOURCES test_output_mulliken.cpp output_mulliken_mock.cpp ../module_mulliken/output_mulliken.cpp ../../source_cell/cell_index.cpp ../../source_basis/module_ao/parallel_orbitals.cpp ) @@ -215,7 +215,7 @@ AddTest( #AddTest( # TARGET MODULE_IO_read_wfc_lcao_test # LIBS parameter base device -# SOURCES read_wfc_lcao_test.cpp ../read_wfc_lcao.cpp +# SOURCES test_read_wfc_lcao.cpp ../read_wfc_lcao.cpp #) #add_test(NAME MODULE_IO_read_wfc_lcao_test_parallel @@ -228,7 +228,7 @@ AddTest( AddTest( TARGET MODULE_IO_orb_io_test LIBS parameter base device - SOURCES orb_io_test.cpp + SOURCES test_orb_io.cpp ) add_test(NAME MODULE_IO_orb_io_test_parallel @@ -261,7 +261,7 @@ endif() AddTest( TARGET MODULE_IO_read_wfc_nao_test LIBS parameter base device - SOURCES read_wfc_nao_test.cpp ../module_wf/read_wfc_nao.cpp ../module_wf/write_wfc_nao.cpp ../../source_psi/psi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp + SOURCES test_read_wfc_nao.cpp ../module_wf/read_wfc_nao.cpp ../module_wf/write_wfc_nao.cpp ../../source_psi/psi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp ) add_test( @@ -275,7 +275,7 @@ AddTest( TARGET MODULE_IO_restart_exx_csr_test LIBS parameter base device SOURCES - restart_exx_csr_test.cpp + test_restart_exx_csr.cpp ../module_restart/restart_exx_csr.cpp ../module_hs/hs_sparse_io.cpp ../module_hs/lat_r_csr.cpp @@ -285,5 +285,5 @@ endif() AddTest( TARGET MODULE_IO_write_elf_logic_test - SOURCES write_elf_logic_test.cpp + SOURCES test_write_elf_logic.cpp ) diff --git a/source/source_io/test/INPUTs b/source/source_io/unittests/INPUTs similarity index 100% rename from source/source_io/test/INPUTs rename to source/source_io/unittests/INPUTs diff --git a/source/source_io/test/dos_test.h b/source/source_io/unittests/dos_test.h similarity index 100% rename from source/source_io/test/dos_test.h rename to source/source_io/unittests/dos_test.h diff --git a/source/source_io/test/for_testing_klist.h b/source/source_io/unittests/for_testing_klist.h similarity index 100% rename from source/source_io/test/for_testing_klist.h rename to source/source_io/unittests/for_testing_klist.h diff --git a/source/source_io/test/output_mulliken_mock.cpp b/source/source_io/unittests/output_mulliken_mock.cpp similarity index 100% rename from source/source_io/test/output_mulliken_mock.cpp rename to source/source_io/unittests/output_mulliken_mock.cpp diff --git a/source/source_io/test/prepare_unitcell.h b/source/source_io/unittests/prepare_unitcell.h similarity index 100% rename from source/source_io/test/prepare_unitcell.h rename to source/source_io/unittests/prepare_unitcell.h diff --git a/source/source_io/test/support/BesselBasis_UnitTest_C4_AtomType0.html b/source/source_io/unittests/support/BesselBasis_UnitTest_C4_AtomType0.html similarity index 100% rename from source/source_io/test/support/BesselBasis_UnitTest_C4_AtomType0.html rename to source/source_io/unittests/support/BesselBasis_UnitTest_C4_AtomType0.html diff --git a/source/source_io/test/support/DMK_nspin1 b/source/source_io/unittests/support/DMK_nspin1 similarity index 100% rename from source/source_io/test/support/DMK_nspin1 rename to source/source_io/unittests/support/DMK_nspin1 diff --git a/source/source_io/test/support/DMK_nspin2 b/source/source_io/unittests/support/DMK_nspin2 similarity index 100% rename from source/source_io/test/support/DMK_nspin2 rename to source/source_io/unittests/support/DMK_nspin2 diff --git a/source/source_io/test/support/INPUT b/source/source_io/unittests/support/INPUT similarity index 100% rename from source/source_io/test/support/INPUT rename to source/source_io/unittests/support/INPUT diff --git a/source/source_io/test/support/INPUT.diago_proc_full b/source/source_io/unittests/support/INPUT.diago_proc_full similarity index 100% rename from source/source_io/test/support/INPUT.diago_proc_full rename to source/source_io/unittests/support/INPUT.diago_proc_full diff --git a/source/source_io/test/support/INPUT.diago_proc_subset b/source/source_io/unittests/support/INPUT.diago_proc_subset similarity index 100% rename from source/source_io/test/support/INPUT.diago_proc_subset rename to source/source_io/unittests/support/INPUT.diago_proc_subset diff --git a/source/source_io/test/support/INPUT.plumed b/source/source_io/unittests/support/INPUT.plumed similarity index 100% rename from source/source_io/test/support/INPUT.plumed rename to source/source_io/unittests/support/INPUT.plumed diff --git a/source/source_io/test/support/INPUT.td_field_mixed b/source/source_io/unittests/support/INPUT.td_field_mixed similarity index 100% rename from source/source_io/test/support/INPUT.td_field_mixed rename to source/source_io/unittests/support/INPUT.td_field_mixed diff --git a/source/source_io/test/support/KPT b/source/source_io/unittests/support/KPT similarity index 100% rename from source/source_io/test/support/KPT rename to source/source_io/unittests/support/KPT diff --git a/source/source_io/test/support/QO_ovlpR_0.dat b/source/source_io/unittests/support/QO_ovlpR_0.dat similarity index 100% rename from source/source_io/test/support/QO_ovlpR_0.dat rename to source/source_io/unittests/support/QO_ovlpR_0.dat diff --git a/source/source_io/test/support/SK_nspin1 b/source/source_io/unittests/support/SK_nspin1 similarity index 100% rename from source/source_io/test/support/SK_nspin1 rename to source/source_io/unittests/support/SK_nspin1 diff --git a/source/source_io/test/support/SK_nspin2 b/source/source_io/unittests/support/SK_nspin2 similarity index 100% rename from source/source_io/test/support/SK_nspin2 rename to source/source_io/unittests/support/SK_nspin2 diff --git a/source/source_io/test/support/SR.csr b/source/source_io/unittests/support/SR.csr similarity index 100% rename from source/source_io/test/support/SR.csr rename to source/source_io/unittests/support/SR.csr diff --git a/source/source_io/test/support/STRU b/source/source_io/unittests/support/STRU similarity index 100% rename from source/source_io/test/support/STRU rename to source/source_io/unittests/support/STRU diff --git a/source/source_io/test/support/Si.upf b/source/source_io/unittests/support/Si.upf similarity index 100% rename from source/source_io/test/support/Si.upf rename to source/source_io/unittests/support/Si.upf diff --git a/source/source_io/test/support/WINPUT b/source/source_io/unittests/support/WINPUT similarity index 100% rename from source/source_io/test/support/WINPUT rename to source/source_io/unittests/support/WINPUT diff --git a/source/source_io/test/support/charge-density.dat b/source/source_io/unittests/support/charge-density.dat similarity index 100% rename from source/source_io/test/support/charge-density.dat rename to source/source_io/unittests/support/charge-density.dat diff --git a/source/source_io/test/support/dm_nao.txt b/source/source_io/unittests/support/dm_nao.txt similarity index 100% rename from source/source_io/test/support/dm_nao.txt rename to source/source_io/unittests/support/dm_nao.txt diff --git a/source/source_io/test/support/dmk1_nao.txt b/source/source_io/unittests/support/dmk1_nao.txt similarity index 100% rename from source/source_io/test/support/dmk1_nao.txt rename to source/source_io/unittests/support/dmk1_nao.txt diff --git a/source/source_io/test/support/istate.info b/source/source_io/unittests/support/istate.info similarity index 100% rename from source/source_io/test/support/istate.info rename to source/source_io/unittests/support/istate.info diff --git a/source/source_io/test/support/kpoints b/source/source_io/unittests/support/kpoints similarity index 100% rename from source/source_io/test/support/kpoints rename to source/source_io/unittests/support/kpoints diff --git a/source/source_io/test/support/sc.json b/source/source_io/unittests/support/sc.json similarity index 100% rename from source/source_io/test/support/sc.json rename to source/source_io/unittests/support/sc.json diff --git a/source/source_io/test/support/wf_nao.txt b/source/source_io/unittests/support/wf_nao.txt similarity index 100% rename from source/source_io/test/support/wf_nao.txt rename to source/source_io/unittests/support/wf_nao.txt diff --git a/source/source_io/test/support/wfs1_nao.txt b/source/source_io/unittests/support/wfs1_nao.txt similarity index 100% rename from source/source_io/test/support/wfs1_nao.txt rename to source/source_io/unittests/support/wfs1_nao.txt diff --git a/source/source_io/test/support/wfs1k1_nao.txt b/source/source_io/unittests/support/wfs1k1_nao.txt similarity index 100% rename from source/source_io/test/support/wfs1k1_nao.txt rename to source/source_io/unittests/support/wfs1k1_nao.txt diff --git a/source/source_io/test/support/wfs1k1_pw.dat b/source/source_io/unittests/support/wfs1k1_pw.dat similarity index 100% rename from source/source_io/test/support/wfs1k1_pw.dat rename to source/source_io/unittests/support/wfs1k1_pw.dat diff --git a/source/source_io/test/support/wfs1k2_nao.txt b/source/source_io/unittests/support/wfs1k2_nao.txt similarity index 100% rename from source/source_io/test/support/wfs1k2_nao.txt rename to source/source_io/unittests/support/wfs1k2_nao.txt diff --git a/source/source_io/test/support/wfs1k3_nao.txt b/source/source_io/unittests/support/wfs1k3_nao.txt similarity index 100% rename from source/source_io/test/support/wfs1k3_nao.txt rename to source/source_io/unittests/support/wfs1k3_nao.txt diff --git a/source/source_io/test/support/wfs1k4_nao.txt b/source/source_io/unittests/support/wfs1k4_nao.txt similarity index 100% rename from source/source_io/test/support/wfs1k4_nao.txt rename to source/source_io/unittests/support/wfs1k4_nao.txt diff --git a/source/source_io/test/support/wfs2_nao.txt b/source/source_io/unittests/support/wfs2_nao.txt similarity index 100% rename from source/source_io/test/support/wfs2_nao.txt rename to source/source_io/unittests/support/wfs2_nao.txt diff --git a/source/source_io/test/bessel_basis_test.cpp b/source/source_io/unittests/test_bessel_basis.cpp similarity index 100% rename from source/source_io/test/bessel_basis_test.cpp rename to source/source_io/unittests/test_bessel_basis.cpp diff --git a/source/source_io/test/input_help_test.cpp b/source/source_io/unittests/test_input_help.cpp similarity index 100% rename from source/source_io/test/input_help_test.cpp rename to source/source_io/unittests/test_input_help.cpp diff --git a/source/source_io/test/numerical_basis_test.cpp b/source/source_io/unittests/test_numerical_basis.cpp similarity index 100% rename from source/source_io/test/numerical_basis_test.cpp rename to source/source_io/unittests/test_numerical_basis.cpp diff --git a/source/source_io/test/orb_io_test.cpp b/source/source_io/unittests/test_orb_io.cpp similarity index 100% rename from source/source_io/test/orb_io_test.cpp rename to source/source_io/unittests/test_orb_io.cpp diff --git a/source/source_io/test/output_test.cpp b/source/source_io/unittests/test_output.cpp similarity index 100% rename from source/source_io/test/output_test.cpp rename to source/source_io/unittests/test_output.cpp diff --git a/source/source_io/test/outputlog_test.cpp b/source/source_io/unittests/test_output_log.cpp similarity index 100% rename from source/source_io/test/outputlog_test.cpp rename to source/source_io/unittests/test_output_log.cpp diff --git a/source/source_io/test/output_mulliken_test.cpp b/source/source_io/unittests/test_output_mulliken.cpp similarity index 100% rename from source/source_io/test/output_mulliken_test.cpp rename to source/source_io/unittests/test_output_mulliken.cpp diff --git a/source/source_io/test/parse_args_test.cpp b/source/source_io/unittests/test_parse_args.cpp similarity index 100% rename from source/source_io/test/parse_args_test.cpp rename to source/source_io/unittests/test_parse_args.cpp diff --git a/source/source_io/test/print_info_test.cpp b/source/source_io/unittests/test_print_info.cpp similarity index 100% rename from source/source_io/test/print_info_test.cpp rename to source/source_io/unittests/test_print_info.cpp diff --git a/source/source_io/test/read_exit_file_test.cpp b/source/source_io/unittests/test_read_exit_file.cpp similarity index 100% rename from source/source_io/test/read_exit_file_test.cpp rename to source/source_io/unittests/test_read_exit_file.cpp diff --git a/source/source_io/test/test_read_input.cmake b/source/source_io/unittests/test_read_input.cmake similarity index 100% rename from source/source_io/test/test_read_input.cmake rename to source/source_io/unittests/test_read_input.cmake diff --git a/source/source_io/test/test_read_input.cpp b/source/source_io/unittests/test_read_input.cpp similarity index 100% rename from source/source_io/test/test_read_input.cpp rename to source/source_io/unittests/test_read_input.cpp diff --git a/source/source_io/test/read_input_ptest.cpp b/source/source_io/unittests/test_read_input_parallel.cpp similarity index 99% rename from source/source_io/test/read_input_ptest.cpp rename to source/source_io/unittests/test_read_input_parallel.cpp index 2f57e82b2d7..3c8ce4c54bd 100644 --- a/source/source_io/test/read_input_ptest.cpp +++ b/source/source_io/unittests/test_read_input_parallel.cpp @@ -15,7 +15,7 @@ #include "source_basis/module_pw/test/test_tool.h" // #endif /************************************************ - * unit test of read_input_test.cpp + * unit test of read_input.cpp ***********************************************/ /** diff --git a/source/source_io/test/read_wf2rho_pw_test.cpp b/source/source_io/unittests/test_read_wf2rho_pw.cpp similarity index 100% rename from source/source_io/test/read_wf2rho_pw_test.cpp rename to source/source_io/unittests/test_read_wf2rho_pw.cpp diff --git a/source/source_io/test/read_wfc_lcao_test.cpp b/source/source_io/unittests/test_read_wfc_lcao.cpp similarity index 100% rename from source/source_io/test/read_wfc_lcao_test.cpp rename to source/source_io/unittests/test_read_wfc_lcao.cpp diff --git a/source/source_io/test/read_wfc_nao_test.cpp b/source/source_io/unittests/test_read_wfc_nao.cpp similarity index 100% rename from source/source_io/test/read_wfc_nao_test.cpp rename to source/source_io/unittests/test_read_wfc_nao.cpp diff --git a/source/source_io/test/read_wfc_pw_test.cpp b/source/source_io/unittests/test_read_wfc_pw.cpp similarity index 100% rename from source/source_io/test/read_wfc_pw_test.cpp rename to source/source_io/unittests/test_read_wfc_pw.cpp diff --git a/source/source_io/test/restart_exx_csr_test.cpp b/source/source_io/unittests/test_restart_exx_csr.cpp similarity index 100% rename from source/source_io/test/restart_exx_csr_test.cpp rename to source/source_io/unittests/test_restart_exx_csr.cpp diff --git a/source/source_io/test/test_spin_tag.cpp b/source/source_io/unittests/test_spin_tag.cpp similarity index 100% rename from source/source_io/test/test_spin_tag.cpp rename to source/source_io/unittests/test_spin_tag.cpp diff --git a/source/source_io/test/td_efield_io_test.cpp b/source/source_io/unittests/test_td_efield_io.cpp similarity index 100% rename from source/source_io/test/td_efield_io_test.cpp rename to source/source_io/unittests/test_td_efield_io.cpp diff --git a/source/source_io/test/test_vecpot_io.cpp b/source/source_io/unittests/test_td_vecpot_io.cpp similarity index 100% rename from source/source_io/test/test_vecpot_io.cpp rename to source/source_io/unittests/test_td_vecpot_io.cpp diff --git a/source/source_io/test/to_qo_test.cpp b/source/source_io/unittests/test_to_qo.cpp similarity index 100% rename from source/source_io/test/to_qo_test.cpp rename to source/source_io/unittests/test_to_qo.cpp diff --git a/source/source_io/test/write_bands_test.cpp b/source/source_io/unittests/test_write_bands.cpp similarity index 100% rename from source/source_io/test/write_bands_test.cpp rename to source/source_io/unittests/test_write_bands.cpp diff --git a/source/source_io/test/write_eig_occ_test.cpp b/source/source_io/unittests/test_write_eig_occ.cpp similarity index 100% rename from source/source_io/test/write_eig_occ_test.cpp rename to source/source_io/unittests/test_write_eig_occ.cpp diff --git a/source/source_io/test/write_elf_logic_test.cpp b/source/source_io/unittests/test_write_elf_logic.cpp similarity index 100% rename from source/source_io/test/write_elf_logic_test.cpp rename to source/source_io/unittests/test_write_elf_logic.cpp diff --git a/source/source_io/test/write_orb_info_test.cpp b/source/source_io/unittests/test_write_orb_info.cpp similarity index 100% rename from source/source_io/test/write_orb_info_test.cpp rename to source/source_io/unittests/test_write_orb_info.cpp diff --git a/source/source_io/test/write_wfc_nao_test.cpp b/source/source_io/unittests/test_write_wfc_nao.cpp similarity index 100% rename from source/source_io/test/write_wfc_nao_test.cpp rename to source/source_io/unittests/test_write_wfc_nao.cpp diff --git a/source/source_io/test/tmp_mocks.cpp b/source/source_io/unittests/tmp_mocks.cpp similarity index 100% rename from source/source_io/test/tmp_mocks.cpp rename to source/source_io/unittests/tmp_mocks.cpp diff --git a/source/source_io/test/write_wfc_nao_para.sh b/source/source_io/unittests/write_wfc_nao_para.sh similarity index 100% rename from source/source_io/test/write_wfc_nao_para.sh rename to source/source_io/unittests/write_wfc_nao_para.sh diff --git a/source/source_pw/module_pwdft/CMakeLists.txt b/source/source_pw/module_pwdft/CMakeLists.txt index 10308dc0c7a..fbe33d10327 100644 --- a/source/source_pw/module_pwdft/CMakeLists.txt +++ b/source/source_pw/module_pwdft/CMakeLists.txt @@ -91,7 +91,7 @@ endif() if(BUILD_TESTING) if(ENABLE_MPI) - add_subdirectory(test) + add_subdirectory(unittests) add_subdirectory(kernels/test) endif() endif() diff --git a/source/source_pw/module_pwdft/test/CMakeLists.txt b/source/source_pw/module_pwdft/unittests/CMakeLists.txt similarity index 98% rename from source/source_pw/module_pwdft/test/CMakeLists.txt rename to source/source_pw/module_pwdft/unittests/CMakeLists.txt index 9fb5e50b28c..6404f938a7e 100644 --- a/source/source_pw/module_pwdft/test/CMakeLists.txt +++ b/source/source_pw/module_pwdft/unittests/CMakeLists.txt @@ -10,7 +10,7 @@ addtest( LIBS parameter SOURCES - soc_test.cpp + test_soc.cpp ../soc.cpp ../../../source_base/global_variable.cpp ../../../source_base/global_function.cpp @@ -36,7 +36,7 @@ addtest( base device SOURCES - dftu_base_test.cpp + test_dftu_base.cpp ../dftu_base.cpp ../dftu_base_io.cpp ../dftu_base_io_read.cpp @@ -84,7 +84,7 @@ addtest( device planewave SOURCES - structure_factor_test.cpp + test_stru_fac.cpp ../stru_fac.cpp ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp diff --git a/source/source_pw/module_pwdft/test/dftu_base_test.cpp b/source/source_pw/module_pwdft/unittests/test_dftu_base.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/dftu_base_test.cpp rename to source/source_pw/module_pwdft/unittests/test_dftu_base.cpp diff --git a/source/source_pw/module_pwdft/test/test_dftu_pw_tools.cpp b/source/source_pw/module_pwdft/unittests/test_dftu_pw_tools.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/test_dftu_pw_tools.cpp rename to source/source_pw/module_pwdft/unittests/test_dftu_pw_tools.cpp diff --git a/source/source_pw/module_pwdft/test/test_pw_hybrid_source.cpp b/source/source_pw/module_pwdft/unittests/test_pw_hybrid_source.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/test_pw_hybrid_source.cpp rename to source/source_pw/module_pwdft/unittests/test_pw_hybrid_source.cpp diff --git a/source/source_pw/module_pwdft/test/soc_test.cpp b/source/source_pw/module_pwdft/unittests/test_soc.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/soc_test.cpp rename to source/source_pw/module_pwdft/unittests/test_soc.cpp diff --git a/source/source_pw/module_pwdft/test/structure_factor_test.cpp b/source/source_pw/module_pwdft/unittests/test_stru_fac.cpp similarity index 100% rename from source/source_pw/module_pwdft/test/structure_factor_test.cpp rename to source/source_pw/module_pwdft/unittests/test_stru_fac.cpp diff --git a/source/source_pw/module_rt/CMakeLists.txt b/source/source_pw/module_rt/CMakeLists.txt index 740c2673b60..70458ae7e01 100644 --- a/source/source_pw/module_rt/CMakeLists.txt +++ b/source/source_pw/module_rt/CMakeLists.txt @@ -18,5 +18,5 @@ if(ENABLE_COVERAGE) endif() if(BUILD_TESTING AND ENABLE_MPI) - add_subdirectory(test) + add_subdirectory(unittests) endif() diff --git a/source/source_pw/module_rt/test/CMakeLists.txt b/source/source_pw/module_rt/unittests/CMakeLists.txt similarity index 97% rename from source/source_pw/module_rt/test/CMakeLists.txt rename to source/source_pw/module_rt/unittests/CMakeLists.txt index a7b241b2973..f6fd7925ee4 100644 --- a/source/source_pw/module_rt/test/CMakeLists.txt +++ b/source/source_pw/module_rt/unittests/CMakeLists.txt @@ -7,7 +7,7 @@ AddTest( TARGET MODULE_PW_td_observables LIBS parameter base device container psi planewave nao_smoothgen symmetry SOURCES - test_td_obs_pw.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp + td_test_utils.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp ../../module_pwdft/op_pw_vel.cpp ../../module_pwdft/projector_gradient.cpp ../../module_pwdft/velocity_workspace.cpp ../../module_pwdft/nonlocal_workspace.cpp ../../module_pwdft/vnl_pw.cpp ../../module_pwdft/vnl_pw_init_vnl.cpp ../../module_pwdft/vnl_pw_getvnl.cpp ../../module_pwdft/vnl_pw_deeq.cpp diff --git a/source/source_pw/module_rt/test/test_td_obs_pw.cpp b/source/source_pw/module_rt/unittests/td_test_utils.cpp similarity index 99% rename from source/source_pw/module_rt/test/test_td_obs_pw.cpp rename to source/source_pw/module_rt/unittests/td_test_utils.cpp index 293ba741d6a..ac473caf9fb 100644 --- a/source/source_pw/module_rt/test/test_td_obs_pw.cpp +++ b/source/source_pw/module_rt/unittests/td_test_utils.cpp @@ -1,6 +1,6 @@ #include "source_base/parallel_global.h" #include "source_cell/read_pp.h" -#include "source_pw/module_rt/test/td_test_utils.h" +#include "source_pw/module_rt/unittests/td_test_utils.h" #include #include diff --git a/source/source_pw/module_rt/test/td_test_utils.h b/source/source_pw/module_rt/unittests/td_test_utils.h similarity index 100% rename from source/source_pw/module_rt/test/td_test_utils.h rename to source/source_pw/module_rt/unittests/td_test_utils.h diff --git a/source/source_pw/module_rt/test/test_td_spin_pw.cpp b/source/source_pw/module_rt/unittests/test_td_spin_pw.cpp similarity index 98% rename from source/source_pw/module_rt/test/test_td_spin_pw.cpp rename to source/source_pw/module_rt/unittests/test_td_spin_pw.cpp index 8b2493aa258..6675715dc39 100644 --- a/source/source_pw/module_rt/test/test_td_spin_pw.cpp +++ b/source/source_pw/module_rt/unittests/test_td_spin_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_rt/test/td_test_utils.h" +#include "source_pw/module_rt/unittests/td_test_utils.h" #include #include diff --git a/source/source_pw/module_rt/test/test_td_vel_pw.cpp b/source/source_pw/module_rt/unittests/test_td_vel_pw.cpp similarity index 98% rename from source/source_pw/module_rt/test/test_td_vel_pw.cpp rename to source/source_pw/module_rt/unittests/test_td_vel_pw.cpp index 6106a28f2b8..d8c685e179f 100644 --- a/source/source_pw/module_rt/test/test_td_vel_pw.cpp +++ b/source/source_pw/module_rt/unittests/test_td_vel_pw.cpp @@ -1,4 +1,4 @@ -#include "source_pw/module_rt/test/td_test_utils.h" +#include "source_pw/module_rt/unittests/td_test_utils.h" #include diff --git a/source/source_pw/module_stodft/test/CMakeLists.txt b/source/source_pw/module_stodft/test/CMakeLists.txt index 07d8e07f60d..8abb1f159a0 100644 --- a/source/source_pw/module_stodft/test/CMakeLists.txt +++ b/source/source_pw/module_stodft/test/CMakeLists.txt @@ -1,6 +1,6 @@ abacus_disable_feature_definitions(__MPI) # These tests exercise CPU code only and link no GPU kernels, so build them -# without the device instantiations (as source_hsolver/test does). +# without the device instantiations (as source_hsolver/unittests does). abacus_disable_feature_definitions(__CUDA) abacus_disable_feature_definitions(__ROCM) diff --git a/source/source_pw/module_stodft/test/test_sto_hsolver_pw.cpp b/source/source_pw/module_stodft/test/test_sto_hsolver_pw.cpp index 22292183184..0a361000f7d 100644 --- a/source/source_pw/module_stodft/test/test_sto_hsolver_pw.cpp +++ b/source/source_pw/module_stodft/test/test_sto_hsolver_pw.cpp @@ -2,8 +2,8 @@ #include #include -#include "source_hsolver/test/hsolver_pw_sup.h" -#include "source_hsolver/test/hsolver_supplementary_mock.h" +#include "source_hsolver/unittests/hsolver_pw_sup.h" +#include "source_hsolver/unittests/hsolver_supplementary_mock.h" #include "source_base/parallel_comm.h" #include "source_estate/elecstate_pw.h" #include "source_hsolver/hsolver_pw.h" From 29a507754c06b56e2eadcad708c07d68ae6e0e9c Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Thu, 29 Oct 2026 17:50:50 +0800 Subject: [PATCH 31/33] Use ABACUS math wrappers and share the cuBLAS handle across translation units --- .../module_container/base/third_party/blas.h | 9 ++ .../base/third_party/lapack.h | 34 ++++++ .../module_external/blacs_connector.h | 25 +++++ .../module_external/blas_connector.h | 2 +- .../module_external/blas_connector_base.cpp | 4 +- .../module_external/scalapack_connector.h | 8 ++ .../module_rt_pw/exp_lanczos.cpp | 2 +- .../module_rt_pw/orthonormal_small.cpp | 18 +++- .../unittests/test_exp_action.cpp | 3 +- .../unittests/diago_elpa_utils.h | 16 +-- .../unittests/test_diago_bpcg.cpp | 4 +- .../unittests/test_diago_cg.cpp | 4 +- .../unittests/test_diago_cg_float.cpp | 4 +- .../unittests/test_diago_cg_real.cpp | 6 +- .../unittests/test_diago_cusolver.cpp | 2 +- .../unittests/test_diago_david_double.cpp | 4 +- .../unittests/test_diago_david_float.cpp | 4 +- .../unittests/test_diago_david_real.cpp | 6 +- .../unittests/test_diago_lcao.cpp | 4 +- .../unittests/test_diago_pexsi.cpp | 2 +- source/source_pw/module_pwdft/vnl_pw_deeq.cpp | 100 +++++++----------- 21 files changed, 167 insertions(+), 94 deletions(-) diff --git a/source/source_base/module_container/base/third_party/blas.h b/source/source_base/module_container/base/third_party/blas.h index 1fdbac67b2b..1345a442cab 100644 --- a/source/source_base/module_container/base/third_party/blas.h +++ b/source/source_base/module_container/base/third_party/blas.h @@ -141,6 +141,15 @@ namespace container { // Usage example: BlasConnector::functionname(parameter list). namespace BlasConnector { +// Column-major rank-one update; preserve the supplied vector strides and leading dimension. +static inline +void ger(const int m, const int n, const double alpha, + const double* x, const int incx, const double* y, const int incy, + double* a, const int lda) +{ + dger_(&m, &n, &alpha, x, &incx, y, &incy, a, &lda); +} + static inline void axpy( const int& n, const float& alpha, const float *x, const int& incx, float *y, const int& incy) { diff --git a/source/source_base/module_container/base/third_party/lapack.h b/source/source_base/module_container/base/third_party/lapack.h index f413cb31dec..1bb3a25bebc 100644 --- a/source/source_base/module_container/base/third_party/lapack.h +++ b/source/source_base/module_container/base/third_party/lapack.h @@ -48,10 +48,22 @@ int ilaenv_(const int* ispec, const char* name, const char* opts, // Symmetric/Hermitian eigenproblems (QR algorithm) void dsyev_(const char* jobz, const char* uplo, const int* n, double* a, const int* lda, double* w, double* work, const int* lwork, int* info); +void cheev_(const char* jobz, const char* uplo, const int* n, std::complex* a, + const int* lda, float* w, std::complex* work, const int* lwork, + float* rwork, int* info); void zheev_(const char* jobz, const char* uplo, const int* n, std::complex* a, const int* lda, double* w, std::complex* work, const int* lwork, double* rwork, int* info); +// Generalized symmetric/Hermitian eigenproblems (QR algorithm) +void dsygv_(const int* itype, const char* jobz, const char* uplo, const int* n, + double* a, const int* lda, double* b, const int* ldb, double* w, + double* work, const int* lwork, int* info); +void zhegv_(const int* itype, const char* jobz, const char* uplo, const int* n, + std::complex* a, const int* lda, std::complex* b, + const int* ldb, double* w, std::complex* work, + const int* lwork, double* rwork, int* info); + // General nonsymmetric eigenproblems void dgeev_(const char* jobvl, const char* jobvr, const int* n, double* a, const int* lda, double* wr, double* wi, double* vl, const int* ldvl, double* vr, @@ -381,12 +393,34 @@ void heev(const char jobz, const char uplo, const int n, double* a, const int ld dsyev_(&jobz, &uplo, &n, a, &lda, w, work, &lwork, &info); } static inline +void heev(const char jobz, const char uplo, const int n, std::complex* a, const int lda, + float* w, std::complex* work, const int lwork, float* rwork, int& info) +{ + cheev_(&jobz, &uplo, &n, a, &lda, w, work, &lwork, rwork, &info); +} +static inline void heev(const char jobz, const char uplo, const int n, std::complex* a, const int lda, double* w, std::complex* work, const int lwork, double* rwork, int& info) { zheev_(&jobz, &uplo, &n, a, &lda, w, work, &lwork, rwork, &info); } +// Column-major generalized eigenproblems; preserve uplo, workspace queries, and info. +static inline +void hegv(const int itype, const char jobz, const char uplo, const int n, + double* a, const int lda, double* b, const int ldb, double* w, + double* work, const int lwork, int& info) +{ + dsygv_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w, work, &lwork, &info); +} +static inline +void hegv(const int itype, const char jobz, const char uplo, const int n, + std::complex* a, const int lda, std::complex* b, const int ldb, + double* w, std::complex* work, const int lwork, double* rwork, int& info) +{ + zhegv_(&itype, &jobz, &uplo, &n, a, &lda, b, &ldb, w, work, &lwork, rwork, &info); +} + // wrap function of fortran lapack routine zheevx. static inline void heevx(const char jobz, const char range, const char uplo, const int n, diff --git a/source/source_base/module_external/blacs_connector.h b/source/source_base/module_external/blacs_connector.h index 4a60c94141f..d24c2667849 100644 --- a/source/source_base/module_external/blacs_connector.h +++ b/source/source_base/module_external/blacs_connector.h @@ -64,6 +64,31 @@ extern "C" void Cigsum2d(int ConTxt, char *scope, char *top, int m, int n, int *A, int lda, int rdest, int cdest); } +class BlacsConnector +{ +public: + static inline void get(int context, int what, int& value) + { + Cblacs_get(context, what, &value); + } + + static inline void pinfo(int& rank, int& size) + { + Cblacs_pinfo(&rank, &size); + } + + // BLACS reads usermap in column-major order with the supplied leading dimension. + static inline void gridmap(int& context, int* usermap, int ldumap, int nprow, int npcol) + { + Cblacs_gridmap(&context, usermap, ldumap, nprow, npcol); + } + + static inline void gridinfo(int context, int& nprow, int& npcol, int& myprow, int& mypcol) + { + Cblacs_gridinfo(context, &nprow, &npcol, &myprow, &mypcol); + } +}; + // unified interface for broadcast template void Cxgebs2d(int ConTxt, char *scope, char *top, int m, int n, T *A, int lda) diff --git a/source/source_base/module_external/blas_connector.h b/source/source_base/module_external/blas_connector.h index 22f9f99212d..e78c4a00843 100644 --- a/source/source_base/module_external/blas_connector.h +++ b/source/source_base/module_external/blas_connector.h @@ -454,7 +454,7 @@ class BlasConnector namespace BlasUtils{ - static cublasHandle_t cublas_handle = nullptr; + extern cublasHandle_t cublas_handle; void createGpuBlasHandle(); // Create a cublas/hipblas handle. diff --git a/source/source_base/module_external/blas_connector_base.cpp b/source/source_base/module_external/blas_connector_base.cpp index be0bc342c00..0c041753e25 100644 --- a/source/source_base/module_external/blas_connector_base.cpp +++ b/source/source_base/module_external/blas_connector_base.cpp @@ -11,6 +11,8 @@ namespace BlasUtils{ + cublasHandle_t cublas_handle = nullptr; + void createGpuBlasHandle(){ if (cublas_handle == nullptr) { CHECK_CUBLAS(cublasCreate(&cublas_handle)); @@ -74,4 +76,4 @@ namespace BlasUtils{ } // namespace BlasUtils -#endif \ No newline at end of file +#endif diff --git a/source/source_base/module_external/scalapack_connector.h b/source/source_base/module_external/scalapack_connector.h index bf1928fb972..b9fc79c0fee 100644 --- a/source/source_base/module_external/scalapack_connector.h +++ b/source/source_base/module_external/scalapack_connector.h @@ -232,6 +232,14 @@ extern "C" class ScalapackConnector { public: + // Preserve the block-cyclic descriptor layout and local column-major leading dimension. + static inline + void descinit(int* desc, const int m, const int n, const int mb, const int nb, + const int irsrc, const int icsrc, const int ictxt, const int lld, int& info) + { + descinit_(desc, &m, &n, &mb, &nb, &irsrc, &icsrc, &ictxt, &lld, &info); + } + static inline void geadd( const char transa, diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp index c58a0f6431d..122096aab61 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_rt_pw/exp_lanczos.cpp @@ -117,7 +117,7 @@ bool ExpLanczos::diagonalize(const std::vector& diagonal, const char job = 'V'; const char upper = 'U'; int info = 0; - dsyev_(&job, &upper, &count, small_vectors_.data(), &count, small_values_.data(), small_work_.data(), &work_size, &info); + ct::lapackConnector::heev(job, upper, count, small_vectors_.data(), count, small_values_.data(), small_work_.data(), work_size, info); ModuleBase::timer::end("ExpLanczos", "diagonalize"); return info == 0; } diff --git a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp b/source/source_hsolver/module_rt_pw/orthonormal_small.cpp index a34c3ad83a0..41a152e05da 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp +++ b/source/source_hsolver/module_rt_pw/orthonormal_small.cpp @@ -37,10 +37,10 @@ bool cholesky(const std::vector& g, int n, std::vector* c) const char upper = 'U'; const char nonunit = 'N'; int info = 0; - zpotrf_(&upper, &n, c->data(), &n, &info); + ct::lapackConnector::potrf(upper, n, c->data(), n, info); if (info == 0) { - ztrtri_(&upper, &nonunit, &n, c->data(), &n, &info); + ct::lapackConnector::trtri(upper, nonunit, n, c->data(), n, info); } for (int j = 0; j < n; ++j) { @@ -67,7 +67,19 @@ bool lowdin(const std::vector& g, int n, std::vector* c) std::vector rwork(lrwork); std::vector iwork(liwork); int info = 0; - zheevd_(&vectors, &upper, &n, u.data(), &n, values.data(), work.data(), &lwork, rwork.data(), &lrwork, iwork.data(), &liwork, &info); + ct::lapackConnector::heevd(vectors, + upper, + n, + u.data(), + n, + values.data(), + work.data(), + lwork, + rwork.data(), + lrwork, + iwork.data(), + liwork, + info); bool valid = info == 0; for (double value: values) { diff --git a/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp b/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp index 67684539cb6..6ac860cf956 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp +++ b/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp @@ -1,3 +1,4 @@ +#include "source_base/module_container/base/third_party/lapack.h" #include "source_hsolver/module_rt_pw/exp_action.h" #include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" @@ -82,7 +83,7 @@ std::vector exact_exp(const std::vector& matrix, const std::vector rwork(3 * n); std::vector values(n); int status = 0; - zheev_(&job, &upper, &n, vectors.data(), &n, values.data(), work.data(), &lwork, rwork.data(), &status); + ct::lapackConnector::heev(job, upper, n, vectors.data(), n, values.data(), work.data(), lwork, rwork.data(), status); EXPECT_EQ(status, 0); std::vector output(n, Wide(0)); for (int k = 0; k < n; ++k) diff --git a/source/source_hsolver/unittests/diago_elpa_utils.h b/source/source_hsolver/unittests/diago_elpa_utils.h index 843c53b6a51..60325e38df3 100644 --- a/source/source_hsolver/unittests/diago_elpa_utils.h +++ b/source/source_hsolver/unittests/diago_elpa_utils.h @@ -1,5 +1,5 @@ #include "source_base/module_external/blacs_connector.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "mpi.h" #include @@ -38,10 +38,10 @@ void process_2d(int &nprows, int &npcols, int &myprow, int &mypcol, int &icontxt } int what = 0, ic = 0; - Cblacs_get(ic, what, &icontxt); - Cblacs_pinfo(&mypnum, &nprocs); - Cblacs_gridmap(&icontxt, usermap, nprows, nprows, npcols); - Cblacs_gridinfo(icontxt, &nprows, &npcols, &myprow, &mypcol); + BlacsConnector::get(ic, what, icontxt); + BlacsConnector::pinfo(mypnum, nprocs); + BlacsConnector::gridmap(icontxt, usermap, nprows, nprows, npcols); + BlacsConnector::gridinfo(icontxt, nprows, npcols, myprow, mypcol); delete[] usermap; } @@ -62,7 +62,7 @@ void distribute_data(T *hmatrix, T *new_matrix, int &nFull, int &nblk, int &na_r int mypnum; MPI_Comm_rank(MPI_COMM_WORLD, &mypnum); int nprows, npcols, myprow, mypcol; - Cblacs_gridinfo(icontxt, &nprows, &npcols, &myprow, &mypcol); + BlacsConnector::gridinfo(icontxt, nprows, npcols, myprow, mypcol); int SENDPROW = 0, SENDPCOL = 0, tag = 0; T *tmp = new T[nblk]; @@ -177,7 +177,7 @@ void lapack_diago(double *hmatrix, double *smatrix, double *e, int &nFull) b[i] = smatrix[i]; } - dsygv_(&itype, &jobz, &uplo, &nFull, a, &nFull, b, &nFull, e, ev, &lwork, &info); + container::lapackConnector::hegv(itype, jobz, uplo, nFull, a, nFull, b, nFull, e, ev, lwork, info); if (info != 0) { std::cout << "ERROR: solvered by LAPACK error, info=" << info << std::endl; @@ -206,7 +206,7 @@ void lapack_diago(std::complex *hmatrix, std::complex *smatrix, b[i] = smatrix[i]; } - zhegv_(&itype, &jobz, &uplo, &nFull, a, &nFull, b, &nFull, e, ev, &lwork, rwork, &info); + container::lapackConnector::hegv(itype, jobz, uplo, nFull, a, nFull, b, nFull, e, ev, lwork, rwork, info); if (info != 0) { std::cout << "ERROR: solvered by LAPACK error, info=" << info << std::endl; diff --git a/source/source_hsolver/unittests/test_diago_bpcg.cpp b/source/source_hsolver/unittests/test_diago_bpcg.cpp index 34bed8b4735..147f75f4b30 100644 --- a/source/source_hsolver/unittests/test_diago_bpcg.cpp +++ b/source/source_hsolver/unittests/test_diago_bpcg.cpp @@ -1,5 +1,5 @@ #include "source_base/inverse_matrix.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "source_base/parallel_comm.h" #include "source_psi/psi.h" #include "../diago_iter_assist.h" @@ -43,7 +43,7 @@ void lapackEigen(int &npw, std::vector> &hm, double *e, boo double *rwork = new double[3 * npw - 2]; int info = 0; char tmp_c1 = 'V', tmp_c2 = 'U'; - zheev_(&tmp_c1, &tmp_c2, &npw, hm.data(), &npw, e, work2, &lwork, rwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, hm.data(), npw, e, work2, lwork, rwork, info); end = clock(); if (outtime) { std::cout << "Lapack Run time: " << (double)(end - start) / CLOCKS_PER_SEC << " S" << std::endl; diff --git a/source/source_hsolver/unittests/test_diago_cg.cpp b/source/source_hsolver/unittests/test_diago_cg.cpp index 853b51c9f71..34e136f9b19 100644 --- a/source/source_hsolver/unittests/test_diago_cg.cpp +++ b/source/source_hsolver/unittests/test_diago_cg.cpp @@ -6,7 +6,7 @@ #include "mpi.h" #include "source_base/parallel_comm.h" #include "source_base/inverse_matrix.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "source_basis/module_pw/test/test_tool.h" #include "source_psi/psi.h" @@ -45,7 +45,7 @@ void lapackEigen(int &npw, std::vector> &hm, double *e, boo double *rwork = new double[3 * npw - 2]; int info = 0; char tmp_c1 = 'V', tmp_c2 = 'U'; - zheev_(&tmp_c1, &tmp_c2, &npw, hm.data(), &npw, e, work2, &lwork, rwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, hm.data(), npw, e, work2, lwork, rwork, info); end = clock(); if (outtime) { std::cout << "Lapack Run time: " << (double)(end - start) / CLOCKS_PER_SEC << " S" << std::endl; diff --git a/source/source_hsolver/unittests/test_diago_cg_float.cpp b/source/source_hsolver/unittests/test_diago_cg_float.cpp index 0651daca593..b6c5eee488e 100644 --- a/source/source_hsolver/unittests/test_diago_cg_float.cpp +++ b/source/source_hsolver/unittests/test_diago_cg_float.cpp @@ -5,7 +5,7 @@ #include "mpi.h" #include "source_base/parallel_comm.h" #include "source_base/inverse_matrix.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "source_basis/module_pw/test/test_tool.h" #include "source_psi/psi.h" @@ -44,7 +44,7 @@ void lapackEigen(int &npw, std::vector> &hm, float *e, bool float *rwork = new float[3 * npw - 2]; int info = 0; char tmp_c1 = 'V', tmp_c2 = 'U'; - cheev_(&tmp_c1, &tmp_c2, &npw, hm.data(), &npw, e, work2, &lwork, rwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, hm.data(), npw, e, work2, lwork, rwork, info); end = clock(); if (outtime) { std::cout << "Lapack Run time: " << (float)(end - start) / CLOCKS_PER_SEC << " S" << std::endl; diff --git a/source/source_hsolver/unittests/test_diago_cg_real.cpp b/source/source_hsolver/unittests/test_diago_cg_real.cpp index f1afbd99a45..65d7958fe32 100644 --- a/source/source_hsolver/unittests/test_diago_cg_real.cpp +++ b/source/source_hsolver/unittests/test_diago_cg_real.cpp @@ -5,7 +5,7 @@ #include "mpi.h" #include "source_base/parallel_comm.h" #include "source_base/inverse_matrix.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "source_basis/module_pw/test/test_tool.h" #include "source_psi/psi.h" @@ -45,11 +45,11 @@ void lapackEigen(int& npw, std::vector& hm, double* e, bool outtime = fa double work_tmp; constexpr int minus_one = -1; - dsyev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, &work_tmp, &minus_one, &info); // get best lwork + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, &work_tmp, minus_one, info); // get best lwork const int lwork = work_tmp; double* work2 = new double[lwork]; - dsyev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, work2, &lwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, work2, lwork, info); end = clock(); if (info) { std::cout << "ERROR: Lapack solver, info=" << info << std::endl; } diff --git a/source/source_hsolver/unittests/test_diago_cusolver.cpp b/source/source_hsolver/unittests/test_diago_cusolver.cpp index 8c8d48a78ec..562ff36b7f1 100644 --- a/source/source_hsolver/unittests/test_diago_cusolver.cpp +++ b/source/source_hsolver/unittests/test_diago_cusolver.cpp @@ -178,7 +178,7 @@ class DiagoPrepare npcols, mypcol); // the number of column of the new_matrix in each process int ISRC = 0, info; - descinit_(hmtest.desc, &nlocal, &nlocal, &nb2d, &nb2d, &ISRC, &ISRC, &icontxt, &(hmtest.nrow), &info); + ScalapackConnector::descinit(hmtest.desc, nlocal, nlocal, nb2d, nb2d, ISRC, ISRC, icontxt, hmtest.nrow, info); if (info != 0) { printf("Invalid blacs-distribution. Abort!\n"); diff --git a/source/source_hsolver/unittests/test_diago_david_double.cpp b/source/source_hsolver/unittests/test_diago_david_double.cpp index 7d4b8cbfea0..9f1153d4438 100644 --- a/source/source_hsolver/unittests/test_diago_david_double.cpp +++ b/source/source_hsolver/unittests/test_diago_david_double.cpp @@ -6,7 +6,7 @@ #include "source_psi/psi.h" #include"gtest/gtest.h" #include "source_base/inverse_matrix.h" -#include "source_base/module_external/lapack_connector.h" +#include "source_base/module_container/base/third_party/lapack.h" #include "source_basis/module_pw/test/test_tool.h" #include"mpi.h" @@ -47,7 +47,7 @@ void lapackEigen(int& npw, std::vector>& hm, double* e, boo clock_t start,end; start = clock(); char tmp_c1 = 'V', tmp_c2 = 'U'; - zheev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, work2, &lwork, rwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, work2, lwork, rwork, info); end = clock(); if(info) { std::cout << "ERROR: Lapack solver, info=" << info <> &hm, float * e, bool clock_t start,end; start = clock(); char tmp_c1 = 'V', tmp_c2 = 'U'; - cheev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, work2, &lwork, rwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, work2, lwork, rwork, info); end = clock(); if(info) { std::cout << "ERROR: Lapack solver, info=" << info <& hm, double* e, bool outtime = fa double work_tmp; constexpr int minus_one = -1; - dsyev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, &work_tmp, &minus_one, &info); // get best lwork + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, &work_tmp, minus_one, info); // get best lwork const int lwork = work_tmp; double* work2 = new double[lwork]; - dsyev_(&tmp_c1, &tmp_c2, &npw, tmp.data(), &npw, e, work2, &lwork, &info); + ct::lapackConnector::heev(tmp_c1, tmp_c2, npw, tmp.data(), npw, e, work2, lwork, info); end = clock(); if (info) { std::cout << "ERROR: Lapack solver, info=" << info << std::endl; } diff --git a/source/source_hsolver/unittests/test_diago_lcao.cpp b/source/source_hsolver/unittests/test_diago_lcao.cpp index dc19df5d6a3..0d07ac4c092 100644 --- a/source/source_hsolver/unittests/test_diago_lcao.cpp +++ b/source/source_hsolver/unittests/test_diago_lcao.cpp @@ -146,7 +146,7 @@ class DiagoPrepare int npcols; int myprow; int mypcol; - Cblacs_gridinfo(icontxt, &nprows, &npcols, &myprow, &mypcol); + BlacsConnector::gridinfo(icontxt, nprows, npcols, myprow, mypcol); for (int col = 0; col < hmtest.ncol; ++col) { const int global_col = (col / nb2d * npcols + mypcol) * nb2d + col % nb2d; @@ -207,7 +207,7 @@ class DiagoPrepare npcols, mypcol); // the number of column of the new_matrix in each process int ISRC = 0, info; - descinit_(hmtest.desc, &nlocal, &nlocal, &nb2d, &nb2d, &ISRC, &ISRC, &icontxt, &(hmtest.nrow), &info); + ScalapackConnector::descinit(hmtest.desc, nlocal, nlocal, nb2d, nb2d, ISRC, ISRC, icontxt, hmtest.nrow, info); if (info != 0) { printf("Invalid blacs-distribution. Abort!\n"); diff --git a/source/source_hsolver/unittests/test_diago_pexsi.cpp b/source/source_hsolver/unittests/test_diago_pexsi.cpp index b92e42311f8..98b9cde8ca0 100644 --- a/source/source_hsolver/unittests/test_diago_pexsi.cpp +++ b/source/source_hsolver/unittests/test_diago_pexsi.cpp @@ -133,7 +133,7 @@ class PexsiPrepare mypcol); // the number of column of the new_matrix in each process int ISRC = 0, info; - descinit_(hmtest.desc, &nlocal, &nlocal, &nb2d, &nb2d, &ISRC, &ISRC, &icontxt, &(hmtest.nrow), &info); + ScalapackConnector::descinit(hmtest.desc, nlocal, nlocal, nb2d, nb2d, ISRC, ISRC, icontxt, hmtest.nrow, info); if (info != 0) { printf("Invalid blacs-distribution. Abort!\n"); diff --git a/source/source_pw/module_pwdft/vnl_pw_deeq.cpp b/source/source_pw/module_pwdft/vnl_pw_deeq.cpp index 352a8d8f450..681fa9dc414 100644 --- a/source/source_pw/module_pwdft/vnl_pw_deeq.cpp +++ b/source/source_pw/module_pwdft/vnl_pw_deeq.cpp @@ -1,14 +1,14 @@ -#include "vnl_pw.h" - -#include "source_io/module_parameter/parameter.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/math_ylmreal.h" -#include "source_base/timer.h" -#include "source_base/module_external/blas_connector.h" +#include "source_base/module_container/base/third_party/blas.h" #include "source_base/module_device/device.h" +#include "source_base/module_external/blas_connector.h" #include "source_base/parallel_reduce.h" +#include "source_base/timer.h" +#include "source_io/module_parameter/parameter.h" #include "source_pw/module_pwdft/kernels/vnl_op.h" +#include "vnl_pw.h" #include @@ -35,9 +35,7 @@ * @param rho_basis plane-wave basis for charge density FFT * @param cell unit cell (read-only, but passed by reference for compatibility) */ -void pseudopot_cell_vnl::cal_effective_D(const ModuleBase::matrix& veff, - const ModulePW::PW_Basis* rho_basis, - UnitCell& cell) +void pseudopot_cell_vnl::cal_effective_D(const ModuleBase::matrix& veff, const ModulePW::PW_Basis* rho_basis, UnitCell& cell) { ModuleBase::TITLE("pseudopot_cell_vnl", "cal_effective_D"); @@ -144,31 +142,19 @@ void pseudopot_cell_vnl::cal_effective_D(const ModuleBase::matrix& veff, { if (PARAM.globalv.has_float_data) { - castmem_d2s_h2d_op()(this->s_deeq, - this->deeq.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); - castmem_z2c_h2d_op()(this->c_deeq_nc, - this->deeq_nc.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + castmem_d2s_h2d_op()(this->s_deeq, this->deeq.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + castmem_z2c_h2d_op()(this->c_deeq_nc, this->deeq_nc.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); } // Keep the double-precision force view current in single-precision runs. - syncmem_z2z_h2d_op()(this->z_deeq_nc, - this->deeq_nc.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); - syncmem_d2d_h2d_op()(this->d_deeq, - this->deeq.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + syncmem_z2z_h2d_op()(this->z_deeq_nc, this->deeq_nc.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + syncmem_d2d_h2d_op()(this->d_deeq, this->deeq.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); } else { if (PARAM.globalv.has_float_data) { - castmem_d2s_h2h_op()(this->s_deeq, - this->deeq.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); - castmem_z2c_h2h_op()(this->c_deeq_nc, - this->deeq_nc.ptr, - PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + castmem_d2s_h2h_op()(this->s_deeq, this->deeq.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); + castmem_z2c_h2h_op()(this->c_deeq_nc, this->deeq_nc.ptr, PARAM.inp.nspin * cell.nat * this->nhm * this->nhm); } // There's no need to synchronize double precision pointers while in a CPU environment. } @@ -261,23 +247,23 @@ void pseudopot_cell_vnl::newq(const ModuleBase::matrix& veff, const ModulePW::PW double* aux_ptr = reinterpret_cast(aux.c); BlasConnector::gemm(transb, - transa, - natom, - nij, - complex_npw, - fact, - aux_ptr, - complex_npw, - qg_ptr, - complex_npw, - zero, - deeaux.c, - nij); + transa, + natom, + nij, + complex_npw, + fact, + aux_ptr, + complex_npw, + qg_ptr, + complex_npw, + zero, + deeaux.c, + nij); // I'm not sure if this is correct for gamma_only if (rho_basis->gamma_only && rho_basis->ig_gge0 >= 0) { const double neg = -1.0; - dger_(&nij, &natom, &neg, qg_ptr, &complex_npw, aux_ptr, &complex_npw, deeaux.c, &nij); + container::BlasConnector::ger(nij, natom, neg, qg_ptr, complex_npw, aux_ptr, complex_npw, deeaux.c, nij); } for (int ia = 0; ia < natom; ia++) @@ -302,15 +288,15 @@ void pseudopot_cell_vnl::newq(const ModuleBase::matrix& veff, const ModulePW::PW } #ifdef __MPI - Parallel_Reduce::reduce_pool(deeq.ptr,deeq.getSize()); + Parallel_Reduce::reduce_pool(deeq.ptr, deeq.getSize()); #endif } /** * @brief Rotate deeq into deeq_nc for spin-orbit coupling case. * - * Uses the SOC rotation coefficients soc.fcoef to transform - * the spin-diagonal deeq into the full 2x2 spinor deeq_nc. + * Uses the SOC rotation coefficients soc.fcoef to transform the scalar + * and magnetic (x, y, z) components of deeq into the full 2x2 spinor deeq_nc. * * @param iat atom index * @param cell unit cell @@ -336,28 +322,24 @@ void pseudopot_cell_vnl::newd_so(const int& iat, UnitCell& cell) { for (int lh = 0; lh < upf->nh; lh++) { + const std::complex spin_uu = soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 0, is2, lh, jh); + const std::complex spin_dd = soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 1, is2, lh, jh); + const std::complex scalar_term = deeq(0, iat, kh, lh) * (spin_uu + spin_dd); + if (PARAM.globalv.domag) { - deeq_nc(ijs, iat, ih, jh) - += deeq(0, iat, kh, lh) - * (soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 0, is2, lh, jh) - + soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 1, is2, lh, jh)) - + deeq(1, iat, kh, lh) - * (soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 1, is2, lh, jh) - + soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 0, is2, lh, jh)) - + ModuleBase::NEG_IMAG_UNIT * deeq(2, iat, kh, lh) - * (soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 1, is2, lh, jh) - - soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 0, is2, lh, jh)) - + deeq(3, iat, kh, lh) - * (soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 0, is2, lh, jh) - - soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 1, is2, lh, jh)); + const std::complex spin_ud = soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 1, is2, lh, jh); + const std::complex spin_du = soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 0, is2, lh, jh); + const std::complex mag_x_term = deeq(1, iat, kh, lh) * (spin_ud + spin_du); + const std::complex mag_y_term + = ModuleBase::NEG_IMAG_UNIT * deeq(2, iat, kh, lh) * (spin_ud - spin_du); + const std::complex mag_z_term = deeq(3, iat, kh, lh) * (spin_uu - spin_dd); + + deeq_nc(ijs, iat, ih, jh) += scalar_term + mag_x_term + mag_y_term + mag_z_term; } else { - deeq_nc(ijs, iat, ih, jh) - += deeq(0, iat, kh, lh) - * (soc.fcoef(it, is1, 0, ih, kh) * soc.fcoef(it, 0, is2, lh, jh) - + soc.fcoef(it, is1, 1, ih, kh) * soc.fcoef(it, 1, is2, lh, jh)); + deeq_nc(ijs, iat, ih, jh) += scalar_term; } } } From bdf6738b38c0c59b7b2d5937a68f1cc1b7d0b7e4 Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Thu, 29 Oct 2026 18:11:51 +0800 Subject: [PATCH 32/33] Organize `HSolver` numerical algorithms under `module_linalg` and move PW propagation helpers to `module_rt` --- source/CMakeLists.txt | 8 +- source/Makefile.Objects | 4 +- source/source_hsolver/CMakeLists.txt | 2 +- .../CMakeLists.txt | 6 +- .../cn_subspace.cpp | 6 +- .../cn_subspace.h | 2 +- .../exp_action.cpp | 6 +- .../exp_action.h | 4 +- .../exp_lanczos.cpp | 4 +- .../exp_lanczos.h | 4 +- .../exp_taylor.cpp | 4 +- .../exp_taylor.h | 2 +- .../hsolver_linear.cpp | 2 +- .../hsolver_linear.h | 6 +- .../kernels/cuda/linear_op.cu | 2 +- .../kernels/linear_op.cpp | 2 +- .../kernels/linear_op.h | 0 .../kernels/rocm/linear_op.hip.cu | 2 +- .../linear_algebra.cpp | 4 +- .../linear_algebra.h | 0 .../linear_bicgstab.cpp | 4 +- .../linear_bicgstab.h | 2 +- .../linear_cgs.cpp | 2 +- .../linear_cgs.h | 2 +- .../linear_gmres.cpp | 2 +- .../linear_gmres.h | 4 +- .../linear_low_rank.cpp | 6 +- .../linear_low_rank.h | 4 +- .../linear_operator.h | 0 .../linear_solver_types.h | 0 .../linear_workspace.cpp | 4 +- .../linear_workspace.h | 4 +- .../orb_buffer.cpp | 2 +- .../orb_buffer.h | 0 .../orthonormal.cpp | 2 +- .../orthonormal.h | 2 +- .../orthonormal_small.cpp | 2 +- .../module_linalg/unittests/CMakeLists.txt | 34 +++++++ .../unittests/linear_test_utils.h | 0 .../unittests/test_cn_subspace.cpp | 4 +- .../unittests/test_exp_action.cpp | 4 +- .../unittests/test_hsolver_linear.cpp | 4 +- .../unittests/test_linear_low_rank.cpp | 4 +- .../unittests/test_orthonormal.cpp | 2 +- .../module_rt_pw/unittests/CMakeLists.txt | 34 ------- source/source_io/module_energy/td_eij_pw.h | 2 +- source/source_pw/module_rt/CMakeLists.txt | 4 +- .../module_rt}/cn_solver_pw.cpp | 6 +- .../module_rt}/cn_solver_pw.h | 6 +- source/source_pw/module_rt/prop_cn_pw.h | 2 +- source/source_pw/module_rt/prop_etrs_pw.h | 2 +- source/source_pw/module_rt/td_endpoint_pw.h | 2 +- .../module_rt}/td_orbital_ops.cpp | 4 +- .../module_rt}/td_orbital_ops.h | 4 +- source/source_pw/module_rt/td_prop_pw.h | 2 +- source/source_pw/module_rt/td_vel_pw.cpp | 2 +- .../module_rt/unittests/CMakeLists.txt | 90 +++++++++++-------- .../unittests/test_cn_solver_pw.cpp | 6 +- 58 files changed, 175 insertions(+), 155 deletions(-) rename source/source_hsolver/{module_rt_pw => module_linalg}/CMakeLists.txt (78%) rename source/source_hsolver/{module_rt_pw => module_linalg}/cn_subspace.cpp (91%) rename source/source_hsolver/{module_rt_pw => module_linalg}/cn_subspace.h (93%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_action.cpp (93%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_action.h (94%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_lanczos.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_lanczos.h (95%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_taylor.cpp (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/exp_taylor.h (93%) rename source/source_hsolver/{module_rt_pw => module_linalg}/hsolver_linear.cpp (98%) rename source/source_hsolver/{module_rt_pw => module_linalg}/hsolver_linear.h (92%) rename source/source_hsolver/{module_rt_pw => module_linalg}/kernels/cuda/linear_op.cu (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/kernels/linear_op.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/kernels/linear_op.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/kernels/rocm/linear_op.hip.cu (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_algebra.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_algebra.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_bicgstab.cpp (98%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_bicgstab.h (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_cgs.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_cgs.h (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_gmres.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_gmres.h (95%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_low_rank.cpp (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_low_rank.h (96%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_operator.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_solver_types.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_workspace.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/linear_workspace.h (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/orb_buffer.cpp (98%) rename source/source_hsolver/{module_rt_pw => module_linalg}/orb_buffer.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/orthonormal.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/orthonormal.h (98%) rename source/source_hsolver/{module_rt_pw => module_linalg}/orthonormal_small.cpp (99%) create mode 100644 source/source_hsolver/module_linalg/unittests/CMakeLists.txt rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/linear_test_utils.h (100%) rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/test_cn_subspace.cpp (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/test_exp_action.cpp (98%) rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/test_hsolver_linear.cpp (99%) rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/test_linear_low_rank.cpp (97%) rename source/source_hsolver/{module_rt_pw => module_linalg}/unittests/test_orthonormal.cpp (99%) delete mode 100644 source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt rename source/{source_hsolver/module_rt_pw => source_pw/module_rt}/cn_solver_pw.cpp (99%) rename source/{source_hsolver/module_rt_pw => source_pw/module_rt}/cn_solver_pw.h (96%) rename source/{source_hsolver/module_rt_pw => source_pw/module_rt}/td_orbital_ops.cpp (99%) rename source/{source_hsolver/module_rt_pw => source_pw/module_rt}/td_orbital_ops.h (96%) rename source/{source_hsolver/module_rt_pw => source_pw/module_rt}/unittests/test_cn_solver_pw.cpp (99%) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index d2d0e6e99a9..5467ae3b311 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -641,7 +641,7 @@ list( source_basis/module_pw/kernels/pw_op.cpp source_hsolver/kernels/hegvd_op.cpp source_hsolver/kernels/bpcg_kernel_op.cpp - source_hsolver/module_rt_pw/kernels/linear_op.cpp + source_hsolver/module_linalg/kernels/linear_op.cpp source_estate/kernels/elecstate_op.cpp source_base/module_device/device.cpp source_base/module_device/device_helpers.cpp @@ -675,7 +675,7 @@ if(USE_CUDA) source_basis/module_pw/kernels/cuda/pw_op.cu source_hsolver/kernels/cuda/hegvd_op.cu source_hsolver/kernels/cuda/bpcg_kernel_op.cu - source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu + source_hsolver/module_linalg/kernels/cuda/linear_op.cu source_estate/kernels/cuda/elecstate_op.cu source_base/module_device/cuda/memory_op.cu source_pw/module_pwdft/kernels/cuda/force_op.cu @@ -714,7 +714,7 @@ if(USE_ROCM) source_basis/module_pw/kernels/rocm/pw_op.hip.cu source_hsolver/kernels/rocm/hegvd_op.hip.cu source_hsolver/kernels/rocm/bpcg_kernel_op.hip.cu - source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu + source_hsolver/module_linalg/kernels/rocm/linear_op.hip.cu source_estate/kernels/rocm/elecstate_op.hip.cu source_base/module_device/rocm/memory_op.hip.cu source_pw/module_pwdft/kernels/rocm/force_op.hip.cu @@ -757,7 +757,7 @@ target_link_libraries( driver esolver hsolver - hsolver_rt + hsolver_linalg hamilt_general elecstate module_pwdft diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 2252c43c326..9145af6c2cd 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -36,8 +36,8 @@ VPATH=./src_global:\ ./source_base/module_fft:\ ./source_esolver:\ ./source_hsolver:\ -./source_hsolver/module_rt_pw:\ -./source_hsolver/module_rt_pw/kernels:\ +./source_hsolver/module_linalg:\ +./source_hsolver/module_linalg/kernels:\ ./source_hsolver/kernels:\ ./source_hsolver/module_genelpa:\ ./source_hsolver/module_pexsi:\ diff --git a/source/source_hsolver/CMakeLists.txt b/source/source_hsolver/CMakeLists.txt index be31407f1ae..adca70290f7 100644 --- a/source/source_hsolver/CMakeLists.txt +++ b/source/source_hsolver/CMakeLists.txt @@ -90,4 +90,4 @@ if(BUILD_TESTING) message(STATUS "Building tests") endif() -add_subdirectory(module_rt_pw) +add_subdirectory(module_linalg) diff --git a/source/source_hsolver/module_rt_pw/CMakeLists.txt b/source/source_hsolver/module_linalg/CMakeLists.txt similarity index 78% rename from source/source_hsolver/module_rt_pw/CMakeLists.txt rename to source/source_hsolver/module_linalg/CMakeLists.txt index 83af817c833..d24fac5250f 100644 --- a/source/source_hsolver/module_rt_pw/CMakeLists.txt +++ b/source/source_hsolver/module_linalg/CMakeLists.txt @@ -1,6 +1,4 @@ -add_library(hsolver_rt OBJECT - cn_solver_pw.cpp - td_orbital_ops.cpp +add_library(hsolver_linalg OBJECT orb_buffer.cpp exp_action.cpp exp_taylor.cpp @@ -18,7 +16,7 @@ add_library(hsolver_rt OBJECT ) if(ENABLE_COVERAGE) - add_coverage(hsolver_rt) + add_coverage(hsolver_linalg) endif() if(BUILD_TESTING) diff --git a/source/source_hsolver/module_rt_pw/cn_subspace.cpp b/source/source_hsolver/module_linalg/cn_subspace.cpp similarity index 91% rename from source/source_hsolver/module_rt_pw/cn_subspace.cpp rename to source/source_hsolver/module_linalg/cn_subspace.cpp index 24080b156c9..640f0b9dc77 100644 --- a/source/source_hsolver/module_rt_pw/cn_subspace.cpp +++ b/source/source_hsolver/module_linalg/cn_subspace.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/module_rt_pw/cn_subspace.h" +#include "source_hsolver/module_linalg/cn_subspace.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/cn_subspace.h b/source/source_hsolver/module_linalg/cn_subspace.h similarity index 93% rename from source/source_hsolver/module_rt_pw/cn_subspace.h rename to source/source_hsolver/module_linalg/cn_subspace.h index ffc062025ff..a7492d30163 100644 --- a/source/source_hsolver/module_rt_pw/cn_subspace.h +++ b/source/source_hsolver/module_linalg/cn_subspace.h @@ -1,6 +1,6 @@ #ifndef HSOLVER_CN_SUBSPACE_H #define HSOLVER_CN_SUBSPACE_H -#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_linalg/linear_algebra.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/exp_action.cpp b/source/source_hsolver/module_linalg/exp_action.cpp similarity index 93% rename from source/source_hsolver/module_rt_pw/exp_action.cpp rename to source/source_hsolver/module_linalg/exp_action.cpp index 56c9bc71387..eb34865d733 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.cpp +++ b/source/source_hsolver/module_linalg/exp_action.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_linalg/exp_action.h" -#include "source_hsolver/module_rt_pw/exp_lanczos.h" -#include "source_hsolver/module_rt_pw/exp_taylor.h" +#include "source_hsolver/module_linalg/exp_lanczos.h" +#include "source_hsolver/module_linalg/exp_taylor.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/exp_action.h b/source/source_hsolver/module_linalg/exp_action.h similarity index 94% rename from source/source_hsolver/module_rt_pw/exp_action.h rename to source/source_hsolver/module_linalg/exp_action.h index 39b32639b7d..974deaba102 100644 --- a/source/source_hsolver/module_rt_pw/exp_action.h +++ b/source/source_hsolver/module_linalg/exp_action.h @@ -2,8 +2,8 @@ #define HSOLVER_EXP_ACTION_H #include "source_hsolver/hs_operator.h" -#include "source_hsolver/module_rt_pw/linear_algebra.h" -#include "source_hsolver/module_rt_pw/orb_buffer.h" +#include "source_hsolver/module_linalg/linear_algebra.h" +#include "source_hsolver/module_linalg/orb_buffer.h" #include diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp b/source/source_hsolver/module_linalg/exp_lanczos.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/exp_lanczos.cpp rename to source/source_hsolver/module_linalg/exp_lanczos.cpp index 122096aab61..9c020595748 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.cpp +++ b/source/source_hsolver/module_linalg/exp_lanczos.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/module_rt_pw/exp_lanczos.h" +#include "source_hsolver/module_linalg/exp_lanczos.h" #include "source_base/module_container/base/third_party/lapack.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include diff --git a/source/source_hsolver/module_rt_pw/exp_lanczos.h b/source/source_hsolver/module_linalg/exp_lanczos.h similarity index 95% rename from source/source_hsolver/module_rt_pw/exp_lanczos.h rename to source/source_hsolver/module_linalg/exp_lanczos.h index c404d19b16d..e1a451f4258 100644 --- a/source/source_hsolver/module_rt_pw/exp_lanczos.h +++ b/source/source_hsolver/module_linalg/exp_lanczos.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_EXP_LANCZOS_H #define HSOLVER_EXP_LANCZOS_H -#include "source_hsolver/module_rt_pw/exp_action.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/exp_action.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.cpp b/source/source_hsolver/module_linalg/exp_taylor.cpp similarity index 97% rename from source/source_hsolver/module_rt_pw/exp_taylor.cpp rename to source/source_hsolver/module_linalg/exp_taylor.cpp index f58ea75769c..4acb3108419 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.cpp +++ b/source/source_hsolver/module_linalg/exp_taylor.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/module_rt_pw/exp_taylor.h" +#include "source_hsolver/module_linalg/exp_taylor.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/exp_taylor.h b/source/source_hsolver/module_linalg/exp_taylor.h similarity index 93% rename from source/source_hsolver/module_rt_pw/exp_taylor.h rename to source/source_hsolver/module_linalg/exp_taylor.h index 46c398f03d4..dc0f1014ce1 100644 --- a/source/source_hsolver/module_rt_pw/exp_taylor.h +++ b/source/source_hsolver/module_linalg/exp_taylor.h @@ -1,6 +1,6 @@ #ifndef HSOLVER_EXP_TAYLOR_H #define HSOLVER_EXP_TAYLOR_H -#include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_linalg/exp_action.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp b/source/source_hsolver/module_linalg/hsolver_linear.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/hsolver_linear.cpp rename to source/source_hsolver/module_linalg/hsolver_linear.cpp index 2f313a32acb..9a90963d534 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.cpp +++ b/source/source_hsolver/module_linalg/hsolver_linear.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/hsolver_linear.h" +#include "source_hsolver/module_linalg/hsolver_linear.h" #include "source_base/tool_quit.h" diff --git a/source/source_hsolver/module_rt_pw/hsolver_linear.h b/source/source_hsolver/module_linalg/hsolver_linear.h similarity index 92% rename from source/source_hsolver/module_rt_pw/hsolver_linear.h rename to source/source_hsolver/module_linalg/hsolver_linear.h index f52da86a61d..47c43057788 100644 --- a/source/source_hsolver/module_rt_pw/hsolver_linear.h +++ b/source/source_hsolver/module_linalg/hsolver_linear.h @@ -1,8 +1,8 @@ #ifndef HSOLVER_LINEAR_H #define HSOLVER_LINEAR_H -#include "source_hsolver/module_rt_pw/linear_bicgstab.h" -#include "source_hsolver/module_rt_pw/linear_cgs.h" -#include "source_hsolver/module_rt_pw/linear_gmres.h" +#include "source_hsolver/module_linalg/linear_bicgstab.h" +#include "source_hsolver/module_linalg/linear_cgs.h" +#include "source_hsolver/module_linalg/linear_gmres.h" #include diff --git a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu b/source/source_hsolver/module_linalg/kernels/cuda/linear_op.cu similarity index 99% rename from source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu rename to source/source_hsolver/module_linalg/kernels/cuda/linear_op.cu index f5fa9a6d729..9a984234f79 100644 --- a/source/source_hsolver/module_rt_pw/kernels/cuda/linear_op.cu +++ b/source/source_hsolver/module_linalg/kernels/cuda/linear_op.cu @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp b/source/source_hsolver/module_linalg/kernels/linear_op.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/kernels/linear_op.cpp rename to source/source_hsolver/module_linalg/kernels/linear_op.cpp index 15a4e1a4120..c5b194f1c22 100644 --- a/source/source_hsolver/module_rt_pw/kernels/linear_op.cpp +++ b/source/source_hsolver/module_linalg/kernels/linear_op.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/kernels/linear_op.h b/source/source_hsolver/module_linalg/kernels/linear_op.h similarity index 100% rename from source/source_hsolver/module_rt_pw/kernels/linear_op.h rename to source/source_hsolver/module_linalg/kernels/linear_op.h diff --git a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu b/source/source_hsolver/module_linalg/kernels/rocm/linear_op.hip.cu similarity index 99% rename from source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu rename to source/source_hsolver/module_linalg/kernels/rocm/linear_op.hip.cu index fe69e067489..27d5ca079bf 100644 --- a/source/source_hsolver/module_rt_pw/kernels/rocm/linear_op.hip.cu +++ b/source/source_hsolver/module_linalg/kernels/rocm/linear_op.hip.cu @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_algebra.cpp b/source/source_hsolver/module_linalg/linear_algebra.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/linear_algebra.cpp rename to source/source_hsolver/module_linalg/linear_algebra.cpp index eea45efc116..5f35a19a2be 100644 --- a/source/source_hsolver/module_rt_pw/linear_algebra.cpp +++ b/source/source_hsolver/module_linalg/linear_algebra.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_linalg/linear_algebra.h" #include "source_base/kernels/math_kernel_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_algebra.h b/source/source_hsolver/module_linalg/linear_algebra.h similarity index 100% rename from source/source_hsolver/module_rt_pw/linear_algebra.h rename to source/source_hsolver/module_linalg/linear_algebra.h diff --git a/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp b/source/source_hsolver/module_linalg/linear_bicgstab.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/linear_bicgstab.cpp rename to source/source_hsolver/module_linalg/linear_bicgstab.cpp index 3fd8f822ae0..5ba240e92f0 100644 --- a/source/source_hsolver/module_rt_pw/linear_bicgstab.cpp +++ b/source/source_hsolver/module_linalg/linear_bicgstab.cpp @@ -1,6 +1,6 @@ -#include "source_hsolver/module_rt_pw/linear_bicgstab.h" +#include "source_hsolver/module_linalg/linear_bicgstab.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include diff --git a/source/source_hsolver/module_rt_pw/linear_bicgstab.h b/source/source_hsolver/module_linalg/linear_bicgstab.h similarity index 97% rename from source/source_hsolver/module_rt_pw/linear_bicgstab.h rename to source/source_hsolver/module_linalg/linear_bicgstab.h index 4bf7c34b05e..3131c7b10b5 100644 --- a/source/source_hsolver/module_rt_pw/linear_bicgstab.h +++ b/source/source_hsolver/module_linalg/linear_bicgstab.h @@ -1,6 +1,6 @@ #ifndef MODULE_HSOLVER_LINEAR_BICGSTAB_H_ #define MODULE_HSOLVER_LINEAR_BICGSTAB_H_ -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { /** @brief Right-preconditioned BiCGSTAB for independent complex right-hand sides. */ diff --git a/source/source_hsolver/module_rt_pw/linear_cgs.cpp b/source/source_hsolver/module_linalg/linear_cgs.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/linear_cgs.cpp rename to source/source_hsolver/module_linalg/linear_cgs.cpp index 1c2006aa406..1ec12b9047a 100644 --- a/source/source_hsolver/module_rt_pw/linear_cgs.cpp +++ b/source/source_hsolver/module_linalg/linear_cgs.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/linear_cgs.h" +#include "source_hsolver/module_linalg/linear_cgs.h" #include diff --git a/source/source_hsolver/module_rt_pw/linear_cgs.h b/source/source_hsolver/module_linalg/linear_cgs.h similarity index 97% rename from source/source_hsolver/module_rt_pw/linear_cgs.h rename to source/source_hsolver/module_linalg/linear_cgs.h index d5bc08669de..dc2f96c92ba 100644 --- a/source/source_hsolver/module_rt_pw/linear_cgs.h +++ b/source/source_hsolver/module_linalg/linear_cgs.h @@ -1,6 +1,6 @@ #ifndef HSOLVER_LINEAR_CGS_H #define HSOLVER_LINEAR_CGS_H -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.cpp b/source/source_hsolver/module_linalg/linear_gmres.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/linear_gmres.cpp rename to source/source_hsolver/module_linalg/linear_gmres.cpp index a26c8684f7f..a5772b16505 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.cpp +++ b/source/source_hsolver/module_linalg/linear_gmres.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/linear_gmres.h" +#include "source_hsolver/module_linalg/linear_gmres.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_gmres.h b/source/source_hsolver/module_linalg/linear_gmres.h similarity index 95% rename from source/source_hsolver/module_rt_pw/linear_gmres.h rename to source/source_hsolver/module_linalg/linear_gmres.h index f51d3f1141f..241e06e4a5b 100644 --- a/source/source_hsolver/module_rt_pw/linear_gmres.h +++ b/source/source_hsolver/module_linalg/linear_gmres.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_LINEAR_GMRES_H #define HSOLVER_LINEAR_GMRES_H -#include "source_hsolver/module_rt_pw/linear_algebra.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/linear_algebra.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_low_rank.cpp b/source/source_hsolver/module_linalg/linear_low_rank.cpp similarity index 97% rename from source/source_hsolver/module_rt_pw/linear_low_rank.cpp rename to source/source_hsolver/module_linalg/linear_low_rank.cpp index 4037669d4d6..cbbed22817c 100644 --- a/source/source_hsolver/module_rt_pw/linear_low_rank.cpp +++ b/source/source_hsolver/module_linalg/linear_low_rank.cpp @@ -1,7 +1,7 @@ -#include "source_hsolver/module_rt_pw/linear_low_rank.h" +#include "source_hsolver/module_linalg/linear_low_rank.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" +#include "source_hsolver/module_linalg/linear_workspace.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_low_rank.h b/source/source_hsolver/module_linalg/linear_low_rank.h similarity index 96% rename from source/source_hsolver/module_rt_pw/linear_low_rank.h rename to source/source_hsolver/module_linalg/linear_low_rank.h index d5edd570e57..7c3cc483a4f 100644 --- a/source/source_hsolver/module_rt_pw/linear_low_rank.h +++ b/source/source_hsolver/module_linalg/linear_low_rank.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_LINEAR_LOW_RANK_H #define HSOLVER_LINEAR_LOW_RANK_H -#include "source_hsolver/module_rt_pw/linear_algebra.h" -#include "source_hsolver/module_rt_pw/linear_operator.h" +#include "source_hsolver/module_linalg/linear_algebra.h" +#include "source_hsolver/module_linalg/linear_operator.h" namespace hsolver { diff --git a/source/source_hsolver/module_rt_pw/linear_operator.h b/source/source_hsolver/module_linalg/linear_operator.h similarity index 100% rename from source/source_hsolver/module_rt_pw/linear_operator.h rename to source/source_hsolver/module_linalg/linear_operator.h diff --git a/source/source_hsolver/module_rt_pw/linear_solver_types.h b/source/source_hsolver/module_linalg/linear_solver_types.h similarity index 100% rename from source/source_hsolver/module_rt_pw/linear_solver_types.h rename to source/source_hsolver/module_linalg/linear_solver_types.h diff --git a/source/source_hsolver/module_rt_pw/linear_workspace.cpp b/source/source_hsolver/module_linalg/linear_workspace.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/linear_workspace.cpp rename to source/source_hsolver/module_linalg/linear_workspace.cpp index f25883a3a62..e4fe906459e 100644 --- a/source/source_hsolver/module_rt_pw/linear_workspace.cpp +++ b/source/source_hsolver/module_linalg/linear_workspace.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/module_rt_pw/linear_workspace.h" +#include "source_hsolver/module_linalg/linear_workspace.h" #include "source_base/module_device/memory_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/linear_workspace.h b/source/source_hsolver/module_linalg/linear_workspace.h similarity index 97% rename from source/source_hsolver/module_rt_pw/linear_workspace.h rename to source/source_hsolver/module_linalg/linear_workspace.h index fde45f4c30e..7b955f7e484 100644 --- a/source/source_hsolver/module_rt_pw/linear_workspace.h +++ b/source/source_hsolver/module_linalg/linear_workspace.h @@ -4,8 +4,8 @@ #include "source_base/macros.h" #include "source_base/module_container/ATen/core/tensor.h" #include "source_hsolver/diag_comm_info.h" -#include "source_hsolver/module_rt_pw/linear_operator.h" -#include "source_hsolver/module_rt_pw/linear_solver_types.h" +#include "source_hsolver/module_linalg/linear_operator.h" +#include "source_hsolver/module_linalg/linear_solver_types.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/orb_buffer.cpp b/source/source_hsolver/module_linalg/orb_buffer.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/orb_buffer.cpp rename to source/source_hsolver/module_linalg/orb_buffer.cpp index 20e84515b18..4edc782e9ed 100644 --- a/source/source_hsolver/module_rt_pw/orb_buffer.cpp +++ b/source/source_hsolver/module_linalg/orb_buffer.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/orb_buffer.h" +#include "source_hsolver/module_linalg/orb_buffer.h" #include diff --git a/source/source_hsolver/module_rt_pw/orb_buffer.h b/source/source_hsolver/module_linalg/orb_buffer.h similarity index 100% rename from source/source_hsolver/module_rt_pw/orb_buffer.h rename to source/source_hsolver/module_linalg/orb_buffer.h diff --git a/source/source_hsolver/module_rt_pw/orthonormal.cpp b/source/source_hsolver/module_linalg/orthonormal.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/orthonormal.cpp rename to source/source_hsolver/module_linalg/orthonormal.cpp index 3e2af196f43..942056a1000 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal.cpp +++ b/source/source_hsolver/module_linalg/orthonormal.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/orthonormal.h" +#include "source_hsolver/module_linalg/orthonormal.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" diff --git a/source/source_hsolver/module_rt_pw/orthonormal.h b/source/source_hsolver/module_linalg/orthonormal.h similarity index 98% rename from source/source_hsolver/module_rt_pw/orthonormal.h rename to source/source_hsolver/module_linalg/orthonormal.h index 3248a54e3be..aea343b2d91 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal.h +++ b/source/source_hsolver/module_linalg/orthonormal.h @@ -1,7 +1,7 @@ #ifndef HSOLVER_ORTHONORMAL_H #define HSOLVER_ORTHONORMAL_H -#include "source_hsolver/module_rt_pw/linear_algebra.h" +#include "source_hsolver/module_linalg/linear_algebra.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp b/source/source_hsolver/module_linalg/orthonormal_small.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/orthonormal_small.cpp rename to source/source_hsolver/module_linalg/orthonormal_small.cpp index 41a152e05da..b982b347cba 100644 --- a/source/source_hsolver/module_rt_pw/orthonormal_small.cpp +++ b/source/source_hsolver/module_linalg/orthonormal_small.cpp @@ -1,7 +1,7 @@ #include "source_base/kernels/math_kernel_op.h" #include "source_base/module_container/base/third_party/lapack.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/orthonormal.h" +#include "source_hsolver/module_linalg/orthonormal.h" #include #include diff --git a/source/source_hsolver/module_linalg/unittests/CMakeLists.txt b/source/source_hsolver/module_linalg/unittests/CMakeLists.txt new file mode 100644 index 00000000000..3fcadde5a51 --- /dev/null +++ b/source/source_hsolver/module_linalg/unittests/CMakeLists.txt @@ -0,0 +1,34 @@ +abacus_disable_feature_definitions(__CUDA) +abacus_disable_feature_definitions(__ROCM) +abacus_disable_feature_definitions(__EXX) + +set(LINALG_TEST_SOURCES + ../hsolver_linear.cpp + ../linear_bicgstab.cpp + ../linear_cgs.cpp + ../linear_gmres.cpp + ../linear_algebra.cpp + ../cn_subspace.cpp + ../linear_low_rank.cpp + ../linear_workspace.cpp + ../exp_action.cpp + ../exp_lanczos.cpp + ../exp_taylor.cpp + ../orb_buffer.cpp + ../orthonormal.cpp + ../orthonormal_small.cpp +) +AddTest( + TARGET MODULE_HSOLVER_linalg + LIBS parameter base device container + SOURCES test_hsolver_linear.cpp test_cn_subspace.cpp test_linear_low_rank.cpp + test_orthonormal.cpp test_exp_action.cpp ${LINALG_TEST_SOURCES} +) +if(ENABLE_MPI) + add_test( + NAME MODULE_HSOLVER_linalg_parallel + COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} + $ ${MPIEXEC_POSTFLAGS} + ) + set_tests_properties(MODULE_HSOLVER_linalg_parallel PROPERTIES PROCESSORS 2 TIMEOUT 120) +endif() diff --git a/source/source_hsolver/module_rt_pw/unittests/linear_test_utils.h b/source/source_hsolver/module_linalg/unittests/linear_test_utils.h similarity index 100% rename from source/source_hsolver/module_rt_pw/unittests/linear_test_utils.h rename to source/source_hsolver/module_linalg/unittests/linear_test_utils.h diff --git a/source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp b/source/source_hsolver/module_linalg/unittests/test_cn_subspace.cpp similarity index 97% rename from source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp rename to source/source_hsolver/module_linalg/unittests/test_cn_subspace.cpp index 91e65dc9beb..db79d3be884 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_cn_subspace.cpp +++ b/source/source_hsolver/module_linalg/unittests/test_cn_subspace.cpp @@ -1,6 +1,6 @@ #include "source_base/parallel_device.h" -#include "source_hsolver/module_rt_pw/cn_subspace.h" -#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" +#include "source_hsolver/module_linalg/cn_subspace.h" +#include "source_hsolver/module_linalg/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp b/source/source_hsolver/module_linalg/unittests/test_exp_action.cpp similarity index 98% rename from source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp rename to source/source_hsolver/module_linalg/unittests/test_exp_action.cpp index 6ac860cf956..af15cdabca0 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_exp_action.cpp +++ b/source/source_hsolver/module_linalg/unittests/test_exp_action.cpp @@ -1,6 +1,6 @@ #include "source_base/module_container/base/third_party/lapack.h" -#include "source_hsolver/module_rt_pw/exp_action.h" -#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" +#include "source_hsolver/module_linalg/exp_action.h" +#include "source_hsolver/module_linalg/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp b/source/source_hsolver/module_linalg/unittests/test_hsolver_linear.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp rename to source/source_hsolver/module_linalg/unittests/test_hsolver_linear.cpp index 4052abbf61c..f8009bd9ef8 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_hsolver_linear.cpp +++ b/source/source_hsolver/module_linalg/unittests/test_hsolver_linear.cpp @@ -1,7 +1,7 @@ #include "source_base/parallel_device.h" #include "source_base/parallel_global.h" -#include "source_hsolver/module_rt_pw/hsolver_linear.h" -#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" +#include "source_hsolver/module_linalg/hsolver_linear.h" +#include "source_hsolver/module_linalg/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp b/source/source_hsolver/module_linalg/unittests/test_linear_low_rank.cpp similarity index 97% rename from source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp rename to source/source_hsolver/module_linalg/unittests/test_linear_low_rank.cpp index fd61e385634..4639d920a9f 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_linear_low_rank.cpp +++ b/source/source_hsolver/module_linalg/unittests/test_linear_low_rank.cpp @@ -1,5 +1,5 @@ -#include "source_hsolver/module_rt_pw/linear_low_rank.h" -#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" +#include "source_hsolver/module_linalg/linear_low_rank.h" +#include "source_hsolver/module_linalg/unittests/linear_test_utils.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/unittests/test_orthonormal.cpp b/source/source_hsolver/module_linalg/unittests/test_orthonormal.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/unittests/test_orthonormal.cpp rename to source/source_hsolver/module_linalg/unittests/test_orthonormal.cpp index 49a354945ae..d664559b6d1 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_orthonormal.cpp +++ b/source/source_hsolver/module_linalg/unittests/test_orthonormal.cpp @@ -1,4 +1,4 @@ -#include "source_hsolver/module_rt_pw/orthonormal.h" +#include "source_hsolver/module_linalg/orthonormal.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt b/source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt deleted file mode 100644 index 5456fa4e0fc..00000000000 --- a/source/source_hsolver/module_rt_pw/unittests/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -abacus_disable_feature_definitions(__CUDA) -abacus_disable_feature_definitions(__ROCM) -abacus_disable_feature_definitions(__EXX) - -set(TD_LINEAR_TEST_SOURCES - ../hsolver_linear.cpp - ../linear_bicgstab.cpp - ../linear_cgs.cpp - ../linear_gmres.cpp - ../linear_algebra.cpp - ../cn_subspace.cpp - ../linear_low_rank.cpp - ../linear_workspace.cpp -) -AddTest( - TARGET MODULE_HSOLVER_linear - LIBS parameter base device container - SOURCES test_hsolver_linear.cpp test_cn_subspace.cpp test_linear_low_rank.cpp ${TD_LINEAR_TEST_SOURCES} -) -AddTest( - TARGET MODULE_HSOLVER_pw_tddft - LIBS parameter base device container psi planewave - SOURCES test_cn_solver_pw.cpp test_orthonormal.cpp test_exp_action.cpp - ../exp_action.cpp ../exp_lanczos.cpp ../exp_taylor.cpp - ../cn_solver_pw.cpp ../td_orbital_ops.cpp ../orb_buffer.cpp ../orthonormal.cpp ../orthonormal_small.cpp ${TD_LINEAR_TEST_SOURCES} -) -if(ENABLE_MPI) - add_test( - NAME MODULE_HSOLVER_linear_parallel - COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} - $ ${MPIEXEC_POSTFLAGS} - ) - set_tests_properties(MODULE_HSOLVER_linear_parallel PROPERTIES PROCESSORS 2 TIMEOUT 120) -endif() diff --git a/source/source_io/module_energy/td_eij_pw.h b/source/source_io/module_energy/td_eij_pw.h index e9dff7f8419..ca917c3fba1 100644 --- a/source/source_io/module_energy/td_eij_pw.h +++ b/source/source_io/module_energy/td_eij_pw.h @@ -2,7 +2,7 @@ #define TD_EIJ_PW_H #include "source_cell/klist.h" -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_pw/module_rt/td_orbital_ops.h" #include diff --git a/source/source_pw/module_rt/CMakeLists.txt b/source/source_pw/module_rt/CMakeLists.txt index 70458ae7e01..50ed81fef19 100644 --- a/source/source_pw/module_rt/CMakeLists.txt +++ b/source/source_pw/module_rt/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(module_rt_pw OBJECT + cn_solver_pw.cpp + td_orbital_ops.cpp td_prop_pw.cpp td_hamilt_pw.cpp prop_cn_pw.cpp @@ -17,6 +19,6 @@ if(ENABLE_COVERAGE) add_coverage(module_rt_pw) endif() -if(BUILD_TESTING AND ENABLE_MPI) +if(BUILD_TESTING) add_subdirectory(unittests) endif() diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp b/source/source_pw/module_rt/cn_solver_pw.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/cn_solver_pw.cpp rename to source/source_pw/module_rt/cn_solver_pw.cpp index e8b9146d05d..b3844e5cf9c 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.cpp +++ b/source/source_pw/module_rt/cn_solver_pw.cpp @@ -1,12 +1,12 @@ -#include "source_hsolver/module_rt_pw/cn_solver_pw.h" +#include "source_pw/module_rt/cn_solver_pw.h" #include "source_base/module_device/memory_op.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_basis/module_pw/pw_momentum.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" -#include "source_hsolver/module_rt_pw/orb_buffer.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" +#include "source_hsolver/module_linalg/orb_buffer.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/cn_solver_pw.h b/source/source_pw/module_rt/cn_solver_pw.h similarity index 96% rename from source/source_hsolver/module_rt_pw/cn_solver_pw.h rename to source/source_pw/module_rt/cn_solver_pw.h index 73b35a9f0d6..06609d8fdd2 100644 --- a/source/source_hsolver/module_rt_pw/cn_solver_pw.h +++ b/source/source_pw/module_rt/cn_solver_pw.h @@ -4,9 +4,9 @@ #include "source_base/matrix.h" #include "source_basis/module_pw/pw_basis_k.h" #include "source_hsolver/hs_operator.h" -#include "source_hsolver/module_rt_pw/cn_subspace.h" -#include "source_hsolver/module_rt_pw/hsolver_linear.h" -#include "source_hsolver/module_rt_pw/linear_low_rank.h" +#include "source_hsolver/module_linalg/cn_subspace.h" +#include "source_hsolver/module_linalg/hsolver_linear.h" +#include "source_hsolver/module_linalg/linear_low_rank.h" #include "source_psi/psi.h" #include diff --git a/source/source_pw/module_rt/prop_cn_pw.h b/source/source_pw/module_rt/prop_cn_pw.h index 75fb3ae9eac..2c3b8a745ed 100644 --- a/source/source_pw/module_rt/prop_cn_pw.h +++ b/source/source_pw/module_rt/prop_cn_pw.h @@ -1,6 +1,6 @@ #ifndef PW_PROP_CN_PW_H #define PW_PROP_CN_PW_H -#include "source_hsolver/module_rt_pw/cn_solver_pw.h" +#include "source_pw/module_rt/cn_solver_pw.h" #include "source_pw/module_rt/td_prop_pw.h" namespace pw diff --git a/source/source_pw/module_rt/prop_etrs_pw.h b/source/source_pw/module_rt/prop_etrs_pw.h index 91b1e533a58..2e06f9dc0c2 100644 --- a/source/source_pw/module_rt/prop_etrs_pw.h +++ b/source/source_pw/module_rt/prop_etrs_pw.h @@ -1,7 +1,7 @@ #ifndef PW_PROP_ETRS_PW_H #define PW_PROP_ETRS_PW_H #include "source_hsolver/diag_comm_info.h" -#include "source_hsolver/module_rt_pw/exp_action.h" +#include "source_hsolver/module_linalg/exp_action.h" #include "source_pw/module_rt/td_prop_pw.h" namespace pw diff --git a/source/source_pw/module_rt/td_endpoint_pw.h b/source/source_pw/module_rt/td_endpoint_pw.h index f30a847c036..babf5046f6e 100644 --- a/source/source_pw/module_rt/td_endpoint_pw.h +++ b/source/source_pw/module_rt/td_endpoint_pw.h @@ -2,7 +2,7 @@ #define TD_ENDPOINT_PW_H #include "source_estate/elecstate_pw.h" -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_pw/module_rt/td_orbital_ops.h" #include "source_pw/module_pwdft/hamilt_pw.h" namespace pw diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp b/source/source_pw/module_rt/td_orbital_ops.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/td_orbital_ops.cpp rename to source/source_pw/module_rt/td_orbital_ops.cpp index 1f13816df30..278211f1bc1 100644 --- a/source/source_hsolver/module_rt_pw/td_orbital_ops.cpp +++ b/source/source_pw/module_rt/td_orbital_ops.cpp @@ -1,9 +1,9 @@ -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_pw/module_rt/td_orbital_ops.h" #include "source_base/parallel_device.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" -#include "source_hsolver/module_rt_pw/orb_buffer.h" +#include "source_hsolver/module_linalg/orb_buffer.h" #include #include diff --git a/source/source_hsolver/module_rt_pw/td_orbital_ops.h b/source/source_pw/module_rt/td_orbital_ops.h similarity index 96% rename from source/source_hsolver/module_rt_pw/td_orbital_ops.h rename to source/source_pw/module_rt/td_orbital_ops.h index 8207324b0de..a78aa1f891c 100644 --- a/source/source_hsolver/module_rt_pw/td_orbital_ops.h +++ b/source/source_pw/module_rt/td_orbital_ops.h @@ -2,8 +2,8 @@ #define HSOLVER_TD_ORBITAL_OPS_H #include "source_base/matrix.h" #include "source_hsolver/hs_operator.h" -#include "source_hsolver/module_rt_pw/linear_workspace.h" -#include "source_hsolver/module_rt_pw/orthonormal.h" +#include "source_hsolver/module_linalg/linear_workspace.h" +#include "source_hsolver/module_linalg/orthonormal.h" #include "source_psi/psi.h" #include diff --git a/source/source_pw/module_rt/td_prop_pw.h b/source/source_pw/module_rt/td_prop_pw.h index 79a452e09fb..cb056cc77d6 100644 --- a/source/source_pw/module_rt/td_prop_pw.h +++ b/source/source_pw/module_rt/td_prop_pw.h @@ -1,7 +1,7 @@ #ifndef PW_TD_PROP_PW_H #define PW_TD_PROP_PW_H #include "source_estate/module_pot/td_field_manager.h" -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" +#include "source_pw/module_rt/td_orbital_ops.h" #include "source_io/module_parameter/input_parameter.h" #include "source_pw/module_rt/td_hamilt_pw.h" #include "source_pw/module_rt/td_history_pw.h" diff --git a/source/source_pw/module_rt/td_vel_pw.cpp b/source/source_pw/module_rt/td_vel_pw.cpp index 17deb9fce21..345d249c363 100644 --- a/source/source_pw/module_rt/td_vel_pw.cpp +++ b/source/source_pw/module_rt/td_vel_pw.cpp @@ -2,7 +2,7 @@ #include "source_base/parallel_reduce.h" #include "source_base/timer.h" -#include "source_hsolver/module_rt_pw/kernels/linear_op.h" +#include "source_hsolver/module_linalg/kernels/linear_op.h" #include #include diff --git a/source/source_pw/module_rt/unittests/CMakeLists.txt b/source/source_pw/module_rt/unittests/CMakeLists.txt index f6fd7925ee4..7bc58db6f3a 100644 --- a/source/source_pw/module_rt/unittests/CMakeLists.txt +++ b/source/source_pw/module_rt/unittests/CMakeLists.txt @@ -3,43 +3,63 @@ abacus_disable_feature_definitions(__CUDA) abacus_disable_feature_definitions(__ROCM) abacus_disable_feature_definitions(__EXX) +set(LINALG_DIR ${PROJECT_SOURCE_DIR}/source/source_hsolver/module_linalg) AddTest( - TARGET MODULE_PW_td_observables - LIBS parameter base device container psi planewave nao_smoothgen symmetry - SOURCES - td_test_utils.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp - ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp - ../../module_pwdft/op_pw_vel.cpp ../../module_pwdft/projector_gradient.cpp ../../module_pwdft/velocity_workspace.cpp ../../module_pwdft/nonlocal_workspace.cpp - ../../module_pwdft/vnl_pw.cpp ../../module_pwdft/vnl_pw_init_vnl.cpp ../../module_pwdft/vnl_pw_getvnl.cpp ../../module_pwdft/vnl_pw_deeq.cpp - ../../module_pwdft/vnl_pw_grad.cpp ../../module_pwdft/vnl_pw_table.cpp ../../module_pwdft/vnl_pw_qrad.cpp - ../../module_pwdft/stru_fac.cpp ../../module_pwdft/stru_fac_k.cpp ../../module_pwdft/soc.cpp ../../module_pwdft/nonlocal_maths.cpp - ../../module_proj/onsite_proj.cpp ../../module_proj/onsite_proj_init.cpp - ../../module_proj/onsite_proj_overlap.cpp ../../module_proj/onsite_proj_tools.cpp - ../../module_proj/onsite_proj_tools_becp.cpp ../../module_proj/radial_proj.cpp - ../../module_proj/onsite_proj_print.cpp - ../../../source_basis/module_ao/orb_gaunt_table.cpp - ../../../source_cell/klist.cpp ../../../source_cell/reciprocal_grid.cpp - ../../../source_cell/klist_io.cpp - ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp - ../../../source_cell/atom_spec.cpp ../../../source_cell/atom_pseudo.cpp ../../../source_cell/pseudo.cpp - ../../../source_cell/magnetism.cpp ../../../source_cell/sep.cpp ../../../source_cell/sep_cell.cpp - ../../../source_cell/read_pp.cpp ../../../source_cell/read_pp_complete.cpp - ../../../source_cell/read_pp_upf100.cpp ../../../source_cell/read_pp_upf201.cpp - ../../../source_cell/read_pp_vwr.cpp ../../../source_cell/read_pp_blps.cpp - ../../../source_estate/elecstate.cpp ../../../source_estate/module_charge/charge.cpp - ../../../source_estate/module_pot/potential_new.cpp - ../../../source_hamilt/module_xc/xc_functional.cpp + TARGET MODULE_PW_cn_solver + LIBS parameter base device container psi planewave + SOURCES test_cn_solver_pw.cpp ../cn_solver_pw.cpp ../td_orbital_ops.cpp + ${LINALG_DIR}/hsolver_linear.cpp + ${LINALG_DIR}/linear_bicgstab.cpp + ${LINALG_DIR}/linear_cgs.cpp + ${LINALG_DIR}/linear_gmres.cpp + ${LINALG_DIR}/linear_algebra.cpp + ${LINALG_DIR}/cn_subspace.cpp + ${LINALG_DIR}/linear_low_rank.cpp + ${LINALG_DIR}/linear_workspace.cpp + ${LINALG_DIR}/orb_buffer.cpp + ${LINALG_DIR}/orthonormal.cpp + ${LINALG_DIR}/orthonormal_small.cpp ) -target_compile_definitions(MODULE_PW_td_observables PRIVATE TD_TEST_PP_DIR="${PROJECT_SOURCE_DIR}/tests/PP_ORB") -# Keep real PW initialization and observable implementations without linking the SCF driver. -if(MSVC) - target_compile_options(MODULE_PW_td_observables PRIVATE /Gy /Gw) - target_link_options(MODULE_PW_td_observables PRIVATE /OPT:REF) -else() - target_compile_options(MODULE_PW_td_observables PRIVATE -ffunction-sections -fdata-sections) - if(APPLE) - target_link_options(MODULE_PW_td_observables PRIVATE -Wl,-dead_strip) + +if(ENABLE_MPI) + AddTest( + TARGET MODULE_PW_td_observables + LIBS parameter base device container psi planewave nao_smoothgen symmetry + SOURCES + td_test_utils.cpp test_td_spin_pw.cpp test_td_vel_pw.cpp + ../td_spin_pw.cpp ../td_vel_pw.cpp ../td_pw.cpp + ../../module_pwdft/op_pw_vel.cpp ../../module_pwdft/projector_gradient.cpp ../../module_pwdft/velocity_workspace.cpp ../../module_pwdft/nonlocal_workspace.cpp + ../../module_pwdft/vnl_pw.cpp ../../module_pwdft/vnl_pw_init_vnl.cpp ../../module_pwdft/vnl_pw_getvnl.cpp ../../module_pwdft/vnl_pw_deeq.cpp + ../../module_pwdft/vnl_pw_grad.cpp ../../module_pwdft/vnl_pw_table.cpp ../../module_pwdft/vnl_pw_qrad.cpp + ../../module_pwdft/stru_fac.cpp ../../module_pwdft/stru_fac_k.cpp ../../module_pwdft/soc.cpp ../../module_pwdft/nonlocal_maths.cpp + ../../module_proj/onsite_proj.cpp ../../module_proj/onsite_proj_init.cpp + ../../module_proj/onsite_proj_overlap.cpp ../../module_proj/onsite_proj_tools.cpp + ../../module_proj/onsite_proj_tools_becp.cpp ../../module_proj/radial_proj.cpp + ../../module_proj/onsite_proj_print.cpp + ../../../source_basis/module_ao/orb_gaunt_table.cpp + ../../../source_cell/klist.cpp ../../../source_cell/reciprocal_grid.cpp + ../../../source_cell/klist_io.cpp + ../../../source_cell/unitcell.cpp ../../../source_cell/unitcell_index.cpp + ../../../source_cell/atom_spec.cpp ../../../source_cell/atom_pseudo.cpp ../../../source_cell/pseudo.cpp + ../../../source_cell/magnetism.cpp ../../../source_cell/sep.cpp ../../../source_cell/sep_cell.cpp + ../../../source_cell/read_pp.cpp ../../../source_cell/read_pp_complete.cpp + ../../../source_cell/read_pp_upf100.cpp ../../../source_cell/read_pp_upf201.cpp + ../../../source_cell/read_pp_vwr.cpp ../../../source_cell/read_pp_blps.cpp + ../../../source_estate/elecstate.cpp ../../../source_estate/module_charge/charge.cpp + ../../../source_estate/module_pot/potential_new.cpp + ../../../source_hamilt/module_xc/xc_functional.cpp + ) + target_compile_definitions(MODULE_PW_td_observables PRIVATE TD_TEST_PP_DIR="${PROJECT_SOURCE_DIR}/tests/PP_ORB") + # Keep real PW initialization and observable implementations without linking the SCF driver. + if(MSVC) + target_compile_options(MODULE_PW_td_observables PRIVATE /Gy /Gw) + target_link_options(MODULE_PW_td_observables PRIVATE /OPT:REF) else() - target_link_options(MODULE_PW_td_observables PRIVATE -Wl,--gc-sections) + target_compile_options(MODULE_PW_td_observables PRIVATE -ffunction-sections -fdata-sections) + if(APPLE) + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,-dead_strip) + else() + target_link_options(MODULE_PW_td_observables PRIVATE -Wl,--gc-sections) + endif() endif() endif() diff --git a/source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp b/source/source_pw/module_rt/unittests/test_cn_solver_pw.cpp similarity index 99% rename from source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp rename to source/source_pw/module_rt/unittests/test_cn_solver_pw.cpp index 9c7c206f13e..9f08e3d0aba 100644 --- a/source/source_hsolver/module_rt_pw/unittests/test_cn_solver_pw.cpp +++ b/source/source_pw/module_rt/unittests/test_cn_solver_pw.cpp @@ -1,7 +1,7 @@ #include "source_base/parallel_global.h" -#include "source_hsolver/module_rt_pw/cn_solver_pw.h" -#include "source_hsolver/module_rt_pw/td_orbital_ops.h" -#include "source_hsolver/module_rt_pw/unittests/linear_test_utils.h" +#include "source_pw/module_rt/cn_solver_pw.h" +#include "source_pw/module_rt/td_orbital_ops.h" +#include "source_hsolver/module_linalg/unittests/linear_test_utils.h" #include #include From d988f28e138460c848abfafa25e265d26098290c Mon Sep 17 00:00:00 2001 From: AsTonyshment Date: Thu, 29 Oct 2026 18:42:45 +0800 Subject: [PATCH 33/33] Trigger GitHub CI