diff --git a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp index 91125f64637..626903d6583 100644 --- a/source/source_io/module_ctrl/ctrl_scf_lcao.cpp +++ b/source/source_io/module_ctrl/ctrl_scf_lcao.cpp @@ -38,6 +38,27 @@ #include "source_lcao/rho_tau_lcao.h" // mohan add 2025-10-24 #include "source_lcao/module_operator_lcao/overlap.h" // use hamilt::Overlap for NAMD +#ifdef __EXX +namespace +{ +// C++11/14-compatible stand-in for `if constexpr (std::is_same::value)`. +// WriteDHParams/WriteHParams hold the gamma (TK==double) exx interfaces on purpose (see +// write_H_terms.h), so the assignment below is only well-formed for TK==double; overload +// resolution on Exx_NAO discards the ill-formed case instead of instantiating it. +// At multi-k the pointers stay null and the writers quit with an explicit message. +template +void set_exx_interfaces(TParams& params, Exx_NAO& exx_nao) +{ + if (exx_nao.exd) { params.exd = exx_nao.exd.get(); } + if (exx_nao.exc) { params.exc = exx_nao.exc.get(); } +} +template +void set_exx_interfaces(TParams& /*params*/, Exx_NAO>& /*exx_nao*/) +{ +} +} // namespace +#endif + template void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, const Input_para& inp, @@ -359,13 +380,9 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, #ifdef __EXX // dV^EXX/dR output is wired for the gamma (TK==double) exx interfaces. exd/exc are // mutually exclusive (real vs complex Hexx); write_dH_exx picks by info_ri.real_number. - if constexpr (std::is_same::value) + if (GlobalC::exx_info.info_global.cal_exx) { - if (GlobalC::exx_info.info_global.cal_exx) - { - if (exx_nao.exd) { dh_params.exd = exx_nao.exd.get(); } - if (exx_nao.exc) { dh_params.exc = exx_nao.exc.get(); } - } + set_exx_interfaces(dh_params, exx_nao); } #endif ModuleIO::write_dH_components(dh_params); @@ -418,16 +435,10 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell, #ifdef __EXX if (inp.out_mat_h_exx[0] && GlobalC::exx_info.info_global.cal_exx) { - // V^EXX(R) output is wired for the gamma (TK==double) exx interfaces. - if constexpr (std::is_same::value) - { - if (GlobalC::exx_info.info_global.cal_exx) - { - if (exx_nao.exd) { h_params.exd = exx_nao.exd.get(); } - if (exx_nao.exc) { h_params.exc = exx_nao.exc.get(); } - ModuleIO::write_h_exx(h_params); - } - } + // V^EXX(R) output is wired for the gamma (TK==double) exx interfaces; at multi-k + // this leaves exd/exc null and write_h_exx quits with an explicit message. + set_exx_interfaces(h_params, exx_nao); + ModuleIO::write_h_exx(h_params); } #endif } diff --git a/source/source_io/module_dhs/write_dH.cpp b/source/source_io/module_dhs/write_dH.cpp index 37a13058552..ea062bf45a4 100644 --- a/source/source_io/module_dhs/write_dH.cpp +++ b/source/source_io/module_dhs/write_dH.cpp @@ -8,6 +8,9 @@ #include "source_io/module_parameter/parameter.h" #include "source_lcao/module_hcontainer/hcontainer_funcs.h" #include "source_lcao/module_hcontainer/output_hcontainer.h" +#ifdef __EXX +#include "source_hamilt/module_xc/exx_info.h" +#endif #include #include @@ -138,6 +141,20 @@ void write_dH_components(WriteDHParams& params) "nspin=4 (noncollinear) yet; only nspin=1 and nspin=2."); } +#ifdef __EXX + // The EXX interfaces carried by WriteDHParams are gamma-only (see write_dH.h): at multi-k + // dH^EXX would be the derivative with respect to every mirror atom, which this output is + // not meant for. Quit instead of writing a dH sum that silently omits the EXX term. + if (GlobalC::exx_info.info_global.cal_exx && !PARAM.globalv.gamma_only_local + && (PARAM.inp.out_mat_dh[0] || PARAM.inp.out_mat_dh_exx[0])) + { + ModuleBase::WARNING_QUIT("write_dH_components", + "out_mat_dh (the dH sum) and out_mat_dh_exx are only supported for gamma-only when EXX is on. " + "Use gamma_only, or request the individual non-EXX terms (out_mat_dh_t, " + "out_mat_dh_vnl, out_mat_dh_vl, out_mat_dh_vh, out_mat_dh_vxc)."); + } +#endif + GlobalV::ofs_running << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << std::endl; GlobalV::ofs_running << " | |" << std::endl; GlobalV::ofs_running << " | #Print out dH/dR components# |" << std::endl; diff --git a/source/source_io/module_dhs/write_dH.h b/source/source_io/module_dhs/write_dH.h index 870a19f7e30..0fc944f426e 100644 --- a/source/source_io/module_dhs/write_dH.h +++ b/source/source_io/module_dhs/write_dH.h @@ -54,8 +54,15 @@ struct WriteDHParams std::vector*> dmR; const Charge* chg = nullptr; // ground-state charge for XC Hellmann-Feynman (FDM) #ifdef __EXX - // gamma (TK==double) exx interfaces used by write_dH_exx; exactly one is set depending on - // GlobalC::exx_info.info_ri.real_number (exd: real Hexx, exc: complex Hexx). + // The gamma-only (TK==double) exx interfaces used by write_dH_exx. + // Deliberately NOT templated on TK, for two reasons: + // 1. Physics: at multi-k the derivative would be taken with respect to every mirror + // atom of the periodic images, which is not what this output is used for. + // 2. Cost: templating these pointers on TK would force WriteHParams, WriteDHParams and + // every free function taking them to become templates as well -- a large, purely + // mechanical change for a case nobody needs. + // Multi-k + EXX is therefore rejected up front (see write_dH_components) + // instead of silently producing output with the EXX term missing. Exx_LRI_Interface* exd = nullptr; Exx_LRI_Interface>* exc = nullptr; #endif diff --git a/source/source_io/module_hs/write_H_terms.cpp b/source/source_io/module_hs/write_H_terms.cpp index 6378710b160..02148b1f7ea 100644 --- a/source/source_io/module_hs/write_H_terms.cpp +++ b/source/source_io/module_hs/write_H_terms.cpp @@ -2,6 +2,7 @@ #include "source_base/parallel_reduce.h" #include "source_base/timer.h" +#include "source_base/tool_quit.h" #include "source_estate/module_pot/H_Hartree_pw.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_hs/write_HS.h" @@ -405,6 +406,15 @@ void write_h_exx(WriteHParams& params) ModuleBase::TITLE("ModuleIO", "write_h_exx"); ModuleBase::timer::start("ModuleIO", "write_h_exx"); + // The EXX interfaces carried by WriteHParams are gamma-only (see write_H_terms.h), so at + // multi-k exd/exc are both null. Quit with a clear message instead of writing nothing. + if (!PARAM.globalv.gamma_only_local) + { + ModuleBase::WARNING_QUIT("write_h_exx", + "out_mat_h_exx is only supported for gamma-only: the V^EXX(R) " + "output is not available at multi-k. Use gamma_only."); + } + const UnitCell& ucell = *params.ucell; const Parallel_Orbitals& pv = *params.pv; const K_Vectors& kv = *params.kv; diff --git a/source/source_io/module_hs/write_H_terms.h b/source/source_io/module_hs/write_H_terms.h index 05a88c70a63..7dbede427a8 100644 --- a/source/source_io/module_hs/write_H_terms.h +++ b/source/source_io/module_hs/write_H_terms.h @@ -38,8 +38,12 @@ struct WriteHParams int nat = 0; bool also_hR = false; // H(k) is always written; H(R) (CSR) only when this is true #ifdef __EXX - // gamma (TK==double) exx interfaces used by write_h_exx; exactly one is set depending on - // GlobalC::exx_info.info_ri.real_number (exd: real Hexx, exc: complex Hexx). + // The gamma-only (TK==double) exx interfaces used by write_h_exx. + // Deliberately NOT templated on TK, because it would force WriteHParams, WriteDHParams and + // every free function taking them to become templates as well -- a large, purely + // mechanical change for a case nobody needs. + // Multi-k + EXX is therefore rejected up front (see write_h_exx) + // instead of silently producing output with the EXX term missing. Exx_LRI_Interface* exd = nullptr; Exx_LRI_Interface>* exc = nullptr; #endif