Repository navigation
Refactor Parallel_Grid::init to separate serial/parallel initialization - #383
Closed
MrLi000001 wants to merge 81 commits into
Closed
MrLi000001 wants to merge 81 commits into
MrLi000001 wants to merge 81 commits into
Conversation
…rce/stress free-function extraction, and nonlocal/InfoNonlocal modernization (#7961) * refactor(hamilt): remove GlobalV/PARAM deps from HamiltLCAO Snapshot inp.nspin/inp.vl_in_h as members at construction so getHR_vector/updateHk/refresh no longer read global PARAM, and pass the EXX restart flag into the constructor as load_exx_flag instead of reading GlobalC::restart. Drop the now-unused global_variable.h include. Call sites in esolver_ks_lcao/esolver_double_xc/lcao_others compute the flag with the original logic, so behavior is unchanged. Verified: make -j 30 in build_max_para_test builds with zero errors. * refactor(hamilt): drop unused default arg in updateSk All call sites pass hk_type explicitly, so the default argument was dead weight. Removing it aligns with the no-default-arguments rule without changing any caller or behavior. Verified: make -j 30 in build_max_para_test builds with zero errors. * refactor(hamilt): cache OperatorLCAO downcast in HamiltLCAO updateHk and refresh repeated the same dynamic_cast<OperatorLCAO*> four times; cache the result in a private ops_lcao_ member filled on first use via getOperatorLCAO(). Also spell out the matrix() local type explicitly instead of auto. Verified: make -j 30 in build_max_para_test builds with zero errors. * refactor(hamilt): split HamiltLCAO constructor operator-chain branches Move the gamma-only and multi-k operator-chain construction out of the ~300-line constructor into private init_gamma_operators() / init_multik_operators(), cutting constructor cyclomatic complexity from 35 to 12. The TDDFT velocity-gauge block (TDEkinetic/TDNonlocal) stays guarded by std::is_same<TK, complex> inside init_multik_operators so the double instantiation dead-branch-eliminates it -- those operators have no double instantiation, and hoisting them into a standalone function would produce undefined references. Verified: make -j 30 in build_max_para_test builds and links with zero errors. * refactor(hamilt): dedupe DFTU/DeePKS operator construction The DFT+U and DeePKS operator blocks were byte-identical in the gamma and multi-k branches. Extract them into private add_dftu_op() and add_deepks_op() helpers so each is defined once, removing ~60 lines of duplication and slightly lowering init_multik_operators complexity. Both operators have double and complex instantiations, so the helpers are safe for every HamiltLCAO specialization. Verified: make -j 30 in build_max_para_test builds and links with zero errors. * refactor(hamilt): tidy HamiltLCAO misc cleanups - drop trailing return; at end of the constructor - make the TDDFT nonlocal term conditional up front instead of new-then-maybe-delete, removing a new/delete pair - remove trailing spaces on the include guard - drop redundant virtual on updateHk (override already implies it) - unify destructor to plain delete ops/hR/sR (delete nullptr is safe) - remove dead member const int istep = 0 (never read; the ctor parameter shadows it and is forwarded to OperatorEXX) All changes are behavior-preserving cleanups; no allocation ownership path changes, so no memory leaks introduced. Verified: make -j 30 in build_max_para_test builds and links with zero errors. * refactor(hamilt): prune unused includes and order by call chain Drop six unused headers verified by grep + full build: - source_io/module_parameter/parameter.h (no PARAM usage) - source_hamilt/module_xc/xc_functional.h (no XC_Functional usage) - source_hsolver/hsolver_lcao.h and diago_elpa.h (no solver symbols) - module_operator_lcao/meta_lcao.h (no Meta node constructed) - module_operator_lcao/op_exx_lcao.h duplicate (already under __EXX) Keep dspin_lcao.h: DeltaSpin is declared there (class name does not match the file name, so it survived an initial over-pruning caught by the build). Reorder the remaining includes to follow the constructor call chain: infra -> dftu base/setup -> electronic state -> operators (overlap -> kinetic -> nonlocal -> veff -> dftu -> dspin -> tddft). Verified: make -j 30 in build_max_para_test builds and links with zero errors. * refactor(hamilt): extract LCAO operator-chain construction to factory HamiltLCAO carried a construction-time factory responsibility (building the overlap/kinetic/nonlocal/veff/DFTU/DeePKS/TDDFT/spin-constrain operator chain) that is independent of the object's runtime state. Move that logic out of the class into this-free factory functions, matching the explicit-parameter style used elsewhere (KListIO, dftu_pw). New hamilt_lcao_factory.{h,cpp} in namespace hamilt: - LcaoOpsBundle<TK,TR>: the only two construction products -- the chain head (ops) and the DeePKS V_delta(R) handle. hR/sR/hsk stay allocated by the caller and are passed in as inputs, keeping ownership clear. - build_gamma_ops / build_multik_ops: free functions with explicit parameters; add_dftu_op / add_deepks_op move to an anonymous namespace as internal helpers that append onto the chain head by reference. HamiltLCAO constructor now calls the factory and assigns bundle.ops / bundle.v_delta_R; the four private builder method declarations are removed from the header, and the now-unused operator-node includes are pruned from hamilt_lcao.cpp. Explicit instantiation (3 TK/TR combos x 2 functions) keeps the complex-only TDEkinetic/TDNonlocal guard inside build_multik_ops so the double instantiation still dead-branch-eliminates those references. Verified: make -j 30 in build_max_para_test builds and links with zero errors after both the split and the include pruning. * refactor(hamilt): guard dft_plus_u with explicit branches and WARNING_QUIT add_dftu_op previously used if (==2) ... else ..., which silently routed every non-2 value (including invalid ones) into the first-zeta NAO DFTU implementation. Per input semantics (dft_plus_u: 0 = off, 1 = radius-adjustable, 2 = first-zeta NAO), make the branches explicit: == 1 -> DFTU (radius-adjustable, default new method, listed first) == 2 -> OperatorDFTU (first-zeta NAO, old method kept for testing) else -> ModuleBase::WARNING_QUIT on any out-of-range value add_dftu_op is only reachable when dft_plus_u != 0, so the else branch turns previously-silent misclassification into a clear abort. Adds the source_base/global_function.h include for ModuleBase::WARNING_QUIT. Verified: make -j 30 in build_max_para_test builds and links with zero errors; valid inputs (1/2) keep identical behavior. * refactor(hamilt): manage HamiltLCAO::hsk with std::unique_ptr hsk is exclusively owned by HamiltLCAO (allocated in the SCF constructor, freed in the destructor, only read elsewhere), so hold it in a std::unique_ptr instead of a raw pointer. Pass .get() to the operator-chain constructors and factories, and drop the manual delete. C++11 baseline: use reset(new ...) instead of std::make_unique. Verified: builds with make -j 30. * refactor(hamilt): manage HamiltLCAO hR/sR with std::unique_ptr hR and sR are exclusively owned by HamiltLCAO (allocated in the constructors, freed in the destructor, only read elsewhere; no caller rebinds or deletes them). Hold them in std::unique_ptr and drop the manual deletes. This requires getHR()/getSR() to return HContainer<TR>* by value instead of HContainer<TR>*&, since a unique_ptr member cannot expose a reference to its stored pointer. No call site relies on the reference (verified: nothing assigns to or rebinds through getHR()/getSR()), so the change is behavior-compatible. C++11 baseline: use reset(new ...) instead of std::make_unique. Verified: builds with make -j 30. * fix makefile * remove useless TAC * refactor(dftu): rename DFTU operator classes for clarity Rename DFTU<OperatorLCAO<TK,TR>> to DFTU_onsite and OperatorDFTU<OperatorLCAO<TK,TR>> to DFTU_firstzeta to better reflect the two DFT+U projection methods (radius-adjustable on-site vs first-zeta NAO) and to match the naming style of other LCAO operators (Overlap, Nonlocal, etc.). * refactor(dftu): replace GlobalFunc::ZEROS with std::fill Remove the ModuleBase::GlobalFunc::ZEROS dependency in module_dftu by using std::fill on the raw buffers, consistent with the preference for std::fill/std::copy over ZEROS/COPYARRAY. T(0) covers both the double and std::complex<double> instantiations of cal_pot_onsite/cal_pot_uterm. * refactor(dftu): drop __DEBUG guard around input asserts Keep the nspin/null-pointer and nlm-size asserts active in all builds; they validate cheap invariants, not expensive debug-only checks. * fix(hamilt): allocate hR in HamiltLCAO vacuum constructor The vacuum constructor documented "only HR and SR will be initialed as empty HContainer" but only allocated sR, passing an unallocated hR to the Overlap node. With raw pointers this was an uninitialized-value UB; with unique_ptr it is a null pointer that would segfault if any caller invokes init(). Allocate hR alongside sR to match the documented contract. hsk stays null because the vacuum path has no k-space matrix and never calls init(). No numerical change: the sole caller (esolver_gets) only invokes contributeHR(), which writes SR and never reads hR/hsk. * refactor(lcao): replace ForceStressArrays raw pointers with std::vector (steps 1-3) Step 1: eliminate DSloc_R* aliasing in cal_dS by writing DHloc_fixedR_* directly in single_derivative ('S' branch), guarded by write_dsloc_r. Step 2: convert 12 gamma-only stress members (DSloc_11..33, DHloc_fixed_11..33) from double* to std::vector<double>. Step 3: convert 7 multi-k stress members (DH_r, stvnl11..33) from double* to std::vector<double>; replace OpenMP ZEROS lambda with resize(n, 0.0); update nullptr checks to .empty(). * refactor(lcao): convert DSloc_x/y/z and DHloc_fixed_x/y/z to std::vector (step 4) Replace 6 gamma-only force members from double* to std::vector<double>. Update all call sites to use .data() for set_force and cal_pulay_fs, and nullptr checks to .empty() in check_folded_arrays. * refactor(lcao): convert DHloc_fixedR_x/y/z to std::vector (step 5) Replace 3 multi-k force members from double* to std::vector<double>. Resize with zero-init replaces new + ZEROS + OpenMP lambda in force_lcao_k.cpp and spar_dh.cpp. Remove all corresponding delete[]. Build verified in build_max_para_test. * refactor(lcao): convert DSloc_Rx/Ry/Rz to std::vector (step 6) Replace 3 multi-k force members from double* to std::vector<double>. Update write_dsloc_r guard from nullptr to .empty() in single_derivative. Update check_folded_arrays nullptr checks. Build verified in build_max_para_test. * refactor(lcao): replace InfoNonlocal raw pointers with std::vector Convert all raw new/delete arrays in InfoNonlocal and its local temporaries to std::vector, eliminating manual memory management: - InfoNonlocal::Beta: Numerical_Nonlocal* -> std::vector<Numerical_Nonlocal> - InfoNonlocal::nproj: int* -> std::vector<int> - Set_NonLocal/Read_NonLocal local arrays -> std::vector - setupNonlocal(): use resize/assign instead of delete[]+new[] - Update all call sites to use .data() for raw-pointer interfaces - Align setup_nonlocal.h style (4-space indent, unified comments) Files updated: - source/source_lcao/setup_nonlocal.h/.cpp - source/source_lcao/lcao_init_basis.cpp - source/source_esolver/esolver_lr_lcao_tddft.cpp - source/source_lcao/module_operator_lcao/test/test_{t_nl_cd,nonlocal}.cpp - source/source_lcao/module_rt/test/snap_psb_half_tddft_test.cpp - source/source_lcao/module_operator_lcao/test/tmp_mocks.cpp * refactor(lcao): eliminate GlobalV/PARAM dependencies in InfoNonlocal Pass my_rank, log stream, and out_element_info as explicit parameters instead of reading GlobalV::MY_RANK, GlobalV::ofs_running, and PARAM.inp.out_element_info directly. This aligns with ABACUS governance rule 1 (no cross-layer control through globals). Changes: - Set_NonLocal: add my_rank parameter, use it for plot() calls - Read_NonLocal: add out_element_info and log parameters - setupNonlocal: add my_rank parameter, forward to callees - Remove parameter.h include (no longer needed) - Update LCAONonlocalInfo::setupNonlocal wrapper signature - Update all call sites to pass GlobalV::MY_RANK explicitly - Update snap_psb_half_tddft_test.cpp Set_NonLocal calls with my_rank=0 Verified: make -j 30 in build_std_gpu passes. Quality score: setup_nonlocal.cpp 54 -> 68 (global_dependency eliminated). * refactor(lcao): extract functions to reduce cyclomatic complexity Extract 5 helper functions from Set_NonLocal and Read_NonLocal: - build_soc_coefficients: SOC coefficient matrix construction (from Set_NonLocal) - build_beta_r: radial projector truncation and copy (from Set_NonLocal) - read_header: parse <HEADER> section (from Read_NonLocal) - read_dij: parse <DIJ> section (from Read_NonLocal) - read_projector: parse one <PP_BETA> block (from Read_NonLocal) Also remove dead code: coefficient_D_in and coefficient_D_nc_in in Read_NonLocal were written but never read. Cyclomatic complexity: - Set_NonLocal: 19 -> eliminated (main body now <10) - Read_NonLocal: 24 -> eliminated (main body now <10) - build_soc_coefficients: 14 (extracted, can be further split) Verified: make -j 30 in build_std_gpu passes. Quality score: setup_nonlocal.cpp 68 -> 85. * refactor(lcao): encapsulate InfoNonlocal member variables Convert 4 public member variables to private and add const getters/setters: - Beta -> get_Beta(), get_Beta(it), get_Beta_data(), resize_Beta() - nproj -> get_nproj(), get_nproj(it), assign_nproj() - nprojmax -> get_nprojmax(), set_nprojmax() - rcutmax_Beta -> get_rcutmax_Beta(), set_rcutmax_Beta() Update all external call sites to use getters/setters instead of direct member access. LCAONonlocalInfo now uses the new interface. Verified: make -j 30 in build_std_gpu passes. Quality score: setup_nonlocal.h 85 -> 87, lcao_nonlocal_info.h 96. * refactor(lcao): remove Read_NonLocal dead code and helpers Remove Read_NonLocal, read_header, read_dij, and read_projector which were unreachable because readin_nonlocal was hardcoded to false. This eliminates ~300 lines of dead code including all NONLOCAL file parsing logic. Also remove the readin_nonlocal branch from setupNonlocal, keeping only the Set_NonLocal path. Verified: make -j 30 in build_std_gpu passes. Quality score: setup_nonlocal.cpp 85 -> 90, setup_nonlocal.h 87 -> 92. * fix(lcao): add get_nproj_ref for non-const lvalue reference Set_NonLocal takes int& n_projectors which requires a modifiable lvalue. get_nproj(it) returns int by value which cannot bind. Add get_nproj_ref(it) that returns int& for this use case. Update snap_psb_half_tddft_test.cpp to use get_nproj_ref(0) in both Set_NonLocal call sites. Verified: make -j 30 in build_std_gpu passes. * fix(tddft): construct Nonlocal for hR pair insertion in velocity gauge Nonlocal::initialize_HR inserts atom pairs into hR using a cutoff that includes the nonlocal pseudopotential radius, which may be larger than the orbital cutoff used by EKinetic/Veff. TDEkinetic and TDNonlocal both build hR_tmp by iterating over hR's pairs, so skipping Nonlocal's construction in TDDFT velocity gauge mode left hR with missing pairs, producing an incomplete hR_tmp and incorrect Hamiltonian. Restore the original pattern: always construct Nonlocal when vnl_in_h is set, then conditionally add it to the operator chain (or delete it). * fix(lcao): forbid copying Numerical_Nonlocal and avoid vector reallocation InfoNonlocal::Beta was changed from a raw array to std::vector<Numerical_Nonlocal> in the recent refactor. Since Numerical_Nonlocal owns a raw Proj buffer but defines no copy semantics, Beta.resize() reallocating would shallow-copy elements, leaving dangling Proj pointers and causing SEGFAULTs in all module_deepks unit tests (and any run with ntype > 1). Fix without introducing copy/move semantics: - Explicitly delete Numerical_Nonlocal copy constructor and copy assignment, so any accidental copy now fails at compile time. - Replace Beta wholesale via move-assigning a fresh vector instead of resize(), so elements are constructed in place and never relocated. - Drop the Beta.resize(1) preallocation in InfoNonlocal's constructor (also in the operator_lcao test mock) to keep the invariant. Verification: static analysis only; build and test run not performed. * fix(test): allocate Beta before direct Set_NonLocal calls in tddft test snap_psb_half_tddft_test calls InfoNonlocal::Set_NonLocal directly without setupNonlocal(), which is the only path that used to size the Beta array. After the raw array was replaced by std::vector and the resize(1) preallocation was removed, Beta was empty and Beta[it] was out of bounds, causing SEGFAULT in MODULE_LCAO_tddft_snap_psibeta_half_test. Add resize_Beta(1) next to the existing assign_nproj(1, 0) in both fixture SetUp() functions. Verification: static analysis only; build and test run not performed. * refactor(lcao): split Record_adj::for_2d and deduplicate adjacency check Extract the copy-pasted direct-cutoff / beta-bridge adjacency test into a single file-local is_adjacent helper shared by both passes, and split the ~230-line for_2d (cyclomatic complexity 31) into count_adjacent, allocate_info, and fill_info orchestrated by a thin for_2d wrapper. Public members and the int*** info layout are unchanged so downstream consumers need no modifications. Code quality score for record_adj.cpp: 59 -> 84. * refactor(lcao): pass npol explicitly to Record_adj::for_2d Remove Record_adj's reads of PARAM.globalv.npol, PARAM.inp.out_level and GlobalV::ofs_running. npol is now an explicit argument of for_2d / count_adjacent (no default argument per governance), and the ParaV.nnr log is emitted by the three callers instead. Add a public const getAdjacentInfo() observer on Grid_Driver to expose adj_info read-only. Code quality score for record_adj.cpp: 84 -> 96 (global_dependency gone). * refactor(lcao): flatten Record_adj info storage into a single vector Replace the manually managed int*** info (and the raw int* na_each / iat2ca) with containers. Adjacent records are stored flat in one std::vector<std::array<int,5>> with an info_offset prefix-sum table, exposed read-only through get_info(iat, cb). This removes all raw new/delete, the info_modified flag, and the nested-vector pointer chasing, and keeps each atom's records contiguous for the OpenMP fill loop. na_each / iat2ca become std::vector<int>. Update the three consumers (density_matrix_io, td_current_io, pulay_fs_temp) and the dm_r_init test to the new layout. Code quality score for record_adj.cpp: 96 -> 100 (raw_new_keyword gone). * refactor(lcao): use injected inp_->out_level at for_2d call sites The ParaV.nnr log moved out of Record_adj in the previous commit read PARAM.inp.out_level at each caller, which raised the PR-level global dependency budget. All three callers already hold an injected INPUT pointer (this->inp_), so read out_level from it instead of PARAM.inp, removing three PARAM.inp references. * refactor(lcao): remove dead Force_Stress_LCAO::integral_part The two integral_part specializations were the only callers of Force_LCAO<T>::ftable, and integral_part itself has no callers since the operator-based force/stress path took over. Remove the dead entry point first so the ftable implementations can be deleted next. Verified: make -j 30 in build_max_para_test passes (100% Built target abacus_max_para). * refactor(lcao): delete dead force_lcao_gamma.cpp and force_lcao_k.cpp After removing Force_Stress_LCAO::integral_part (the only caller of Force_LCAO<T>::ftable), the allocate/ftable/finish_ftable specializations in these two files have no remaining callers. Delete the files and drop them from CMakeLists.txt and Makefile.Objects. The active force/stress path uses operator-based cal_force_stress plus PulayForceStress::cal_pulay_fs directly. Verified: make -j 30 in build_max_para_test passes (100% Built target abacus_max_para). * refactor(lcao): drop dead Force_LCAO method declarations With ftable/allocate/finish_ftable deleted, their declarations plus the never-defined average_force/cal_fedm/cal_ftvnl_dphi/cal_fvl_dphi declarations are dead. Force_LCAO now only carries the actively used cal_edm and its ParaV/pot members. Remove the declarations and the includes (matrix.h, two_center_bundle.h, force_stress_arrays.h, setup_deepks.h) that only served them. Verified: make -j 30 in build_max_para_test passes (100% Built target abacus_max_para). * refactor(lcao): drop dead DSloc_*/DHloc_fixed_* stress arrays The DSloc_11/12/13/22/23/33 and DHloc_fixed_11/12/13/22/23/33 arrays were only written by the gamma-only cal_stress branch of single_derivative via set_stress, and never read anywhere. The active LCAO stress path computes the overlap/kinetic/nonlocal contribution through the operator-based cal_force_stress instead. Remove the 12 arrays from ForceStressArrays, the set_stress call site, and the now unused set_stress declaration/implementation. single_derivative keeps its cal_stress parameter because the multi-k branch still uses it to fill DH_r and stvnl*. Verified: make -j 30 in build_max_para_test passes (100% Built target abacus_max_para). * fix(lcao): add missing TwoCenterBundle include for CUDA build Forward-declare TwoCenterBundle in force_stress_lcao.h and explicitly include two_center_bundle.h in force_stress_lcao.cpp to fix CUDA compilation where the indirect include chain is broken. * refactor(lcao): rename misplaced .hpp headers to .h lcao_hs_arrays.hpp is a pure declaration header and the two pulay_fs_*.hpp files hold template implementations; none of them are .hpp implementation headers in the prohibited sense. Rename them to .h and update the 11 include sites so the hpp_implementation rule no longer flags them. Quality score: lcao_hs_arrays 34->84, pulay_fs_temp 23->73, pulay_fs_gint 46->96. * refactor(lcao): pass gamma_only_local/nspin/npol into sparse_format sparse_format::cal_dH/cal_dS/cal_dSTN_R/destroy_dH_R_sparse read PARAM.globalv.gamma_only_local, PARAM.inp.nspin and PARAM.globalv.npol directly. Pass them as explicit arguments so the functions no longer depend on global INPUT state, in line with the rule that cross-layer control through PARAM should not grow. The remaining PARAM.globalv.nlocal in cal_dH is kept because the caller has no local value for it; threading it further would only move the global read, not remove it. Quality score: spar_dh.cpp 58->77. * refactor(lcao): pack single_overlap/single_derivative args into ST_env/ST_elem single_overlap and single_derivative each took 29 parameters, mixing three kinds of state: the read-only build environment (basis, parallel layout, unit cell, spin config), the per-element inputs (operator type, orbital and angular-momentum indices, displacement) and the outputs. Pack them into two aggregate types in LCAO_domain: - ST_env: everything fixed for one build_ST_new call, built once before the omp region. This also removes the PARAM.globalv.gamma_only_local reads inside both functions. - ST_elem: the per-matrix-element inputs, built once per inner-loop iteration and shared by both call sites. Dead parameters tau1/tau2 (only dtau was used) are dropped. Local index variables are lowercased (t1/l1/n1/i1, mm1/mm2 for the magnetic quantum number to avoid clashing with the m1/m2 indices). The functions stay in lcao_set_st.cpp so they remain inlinable at their hot inner-loop call sites; the parameter unpacking is POD and optimises away. Quality score: lcao_domain.h 36->82. * refactor(lcao): split single_deriv S/T branches into helpers single_deriv (renamed from single_derivative) had cyclomatic complexity 24, all of it in the multi-k branch that dispatches on operator type (S/T) x nspin (1/2/4) x spin index is. Extract the per-element writes into two static free functions, set_deriv_s and set_deriv_t, kept in this translation unit so they stay inlinable at the hot inner-loop call site. The main function now only computes the spin index and dispatches. The nspin==4 S-branch "write olm or write zero" blocks were two symmetric if/else arms; collapse them to is==0 ? olm[i] : 0.0. Also fix WARNING_QUIT labels that named LCAO_domain::build_ST_new from inside set_deriv_s/set_deriv_t/single_overlap/single_deriv; they now name the function actually raising them so the log points at the right place. Quality score: lcao_set_st.cpp 45 -> 59. * refactor(lcao): extract per-element nonlocal accumulators Split the energy/force accumulation of one <psi|beta><beta|psi> matrix element out of build_Nonlocal_mu_new into three static helpers (accum_nlm_energy/accum_nlm_force_soc/accum_nlm_force). Bundle the call-invariant inputs into NL_env and the per-element indices into NL_elem so the helpers take explicit arguments instead of reaching into the enclosing loop, and drop the four nlm_cur*_e/f pointer aliases. Lowers the function cyclomatic complexity from 60 to 40. * refactor(lcao): extract build_psi_beta from build_Nonlocal_mu_new Move the <psi|beta> (and <d psi|beta>) generation loop into a static build_psi_beta helper, keeping its OpenMP-parallel iat loop inside the helper and passing nlm_tot/nlm_tot1 out by reference. The main function now only drives Step 2, lowering build_Nonlocal_mu_new cyclomatic complexity from 40 to 29. * refactor(lcao): extract Step2 inner orbital loop into accum_nlm_block Move the (j, k) orbital loop that dispatches to the energy/force accumulators out of build_Nonlocal_mu_new into a static accum_nlm_block helper, packing the per-neighbour inputs (atoms, orbital offsets, iat slot and the two <psi|beta> block keys) into a file-local NL_pair struct. Also normalise the remaining K&R brace placement in this file. Lowers build_Nonlocal_mu_new cyclomatic complexity from 29 to 21. * refactor(lcao): extract force/stress assembly into helpers Move the 32 local force/stress part matrices in getForceStress into LCAOForceParts/LCAOStressParts containers, and extract the force assembly+print and stress assembly+print blocks into assemble_and_print_force / assemble_and_print_stress. * refactor(lcao): extract per-term force/stress calculators from getForceStress Split the body of getForceStress into focused helpers: cal_operator_fs (kinetic/overlap/nonlocal/rt-TDDFT/local-Pulay/DeltaSpin), cal_deepks_fs, cal_vdw_and_fields_fs (vdW, E-field, rt-TDDFT E-field, gate, implicit solvation), cal_dftu_fs and cal_exx_fs. The main function now only orchestrates, dropping its cyclomatic complexity below the report threshold. No behavior change. * fix(lcao): pass two_center_bundle into cal_dftu_fs and drop const in cal_operator_fs cal_dftu_fs reads two_center_bundle.overlap_orb_onsite, and cal_foverlap_rt inside cal_operator_fs takes a non-const UnitCell&. Fix the extracted helper signatures so the file compiles. * refactor(lcao): move PW stress and force symmetrization to free functions Extract calStressPwPart and forceSymmetry from the Force_Stress_LCAO<T> class template into free functions LCAO_domain::cal_stress_pw and LCAO_domain::symmetrize_force in the new force_stress_pw.h/.cpp. Neither depends on the electronic template type T, so they no longer need to be instantiated per T. calForcePwPart stays a member: Forces::cal_force_* are protected and only accessible through the existing friend declaration on Force_Stress_LCAO. * refactor(lcao): move per-term force/stress calculators to free functions Extract cal_deepks_fs, cal_exx_fs, cal_vdw_fields_fs and cal_dftu_fs from the Force_Stress_LCAO<T> class template into free functions in the new force_stress_terms.h/.cpp under LCAO_domain. cal_vdw_fields_fs does not depend on the electronic template type T and is a plain function; the other three are function templates with explicit instantiation for double and std::complex<double>. cal_deepks_fs now takes Parallel_Orbitals& explicitly instead of reaching Force_LCAO::ParaV, removing its dependence on the Force_LCAO member. * refactor(lcao): move force/stress assembly to free functions Extract assemble_and_print_force and assemble_and_print_stress from the Force_Stress_LCAO<T> template class into LCAO_domain free functions assemble_print_force / assemble_print_stress in the new force_stress_assemble.{h,cpp}. The force threshold is passed in as an explicit argument instead of reading the private static member, and the new translation unit carries its own explicit instantiations. * refactor(lcao): split force/stress assembly helpers to cut complexity Extract the per-component accumulation (sum_force_terms / sum_stress_terms) and the test-only printers (print_force_parts / print_force_invalid_table) out of assemble_print_force and assemble_print_stress into file-local helpers. This drops the two assemble functions' cyclomatic complexity from 34/16 to 12/11 and lifts force_stress_assemble.cpp above the quality gate. * refactor(lcao): split vdw/external-field terms and tidy term helpers Extract copy_vdw_terms and cal_external_field_forces out of cal_vdw_fields_fs to remove its cyclomatic-complexity deduction, give the DFT+U adjacent-atom list an explicit std::vector<AdjacentAtomInfo> type instead of auto, and rewrap two over-length explicit-instantiation lines. force_stress_terms.cpp now passes the quality gate. * style(lcao): replace auto with explicit DMK vector types, rewrap long line Give the two assign_dmk_ptr specializations an explicit std::vector<std::vector<...>>& type for the DMK vector instead of auto, and rewrap one over-length cal_force_stress call. (An attempted split of cal_operator_fs's per-spin branches was reverted: the extra helper parameters cost more on the quality gate than the cyclomatic-complexity deduction they removed.) * refactor(lcao): drop unused assign_dmk_ptr param, dedupe include/comments - Remove the unused gamma_only_local parameter from assign_dmk_ptr and its call site in force_stress_terms.cpp (the specializations select the DMK pointer purely from the template type). - Drop the duplicate parameter.h include in force_stress_lcao.cpp. - Reword the nspin=4 branch comments so they are distinct from the nspin=1/2 branch instead of duplicated boilerplate. Verified: make -j 30 abacus_max_para in build_max_para_test passes. code_quality_score.py force_stress_lcao.cpp: 29 -> 33 (duplicate_doc_block deduction removed). * refactor(lcao): pass INPUT scalars via FSCalcConfig, drop PARAM reads getForceStress and its two helpers used to read the global PARAM object for nspin/nbands/t_in_h/sc_mag_switch/device. Introduce a small FSCalcConfig aggregate and pass those five values in explicitly from the two esolver call sites (both already hold this->inp_). This removes the last PARAM reads from force_stress_lcao.cpp and bundles the scalars into one reference argument. Verified: make -j 30 abacus_max_para in build_max_para_test passes. code_quality_score.py force_stress_lcao.cpp: 29 -> 60 (global_dependency deduction removed; file now passes the >=60 bar). * fix bug * fix bug * fix(lcao): guard ForceStressArrays writes in build_ST_new derivative path Add defensive empty checks before writing DHloc_fixedR_*, DH_r and stvnl* arrays in set_deriv_s/set_deriv_t, and validate required buffers at build_ST_new entry when calc_deri=true in multi-k mode. This prevents potential out-of-bounds access if a future caller passes unallocated ForceStressArrays members, and makes the caller contract explicit. * refactor(force): share PW/LCAO force finalize and move PW-part stress into module_pwdft - Add ModuleBase::remove_net_force (mathzone.h) as a free function and ModuleSymmetry::symmetrize_force_cartesian, replacing the duplicated inline net-force zeroing and Cartesian->direct->symmetrize->Cartesian code in force_pw.cpp and force_stress_assemble.cpp. Both take lattice vectors from the Symmetry object so no UnitCell dependency is added. - Move LCAO_domain::cal_stress_pw into Stress_Func::stress_pw_terms so the PW-basis stress assembly lives in module_pwdft. - Delete force_stress_pw.h/cpp and update CMakeLists.txt/Makefile.Objects. * fix(force): update symmetrize_force_cartesian call to new signature The call site in force_pw.cpp still passed (ucell, p_symm, force); update to (p_symm, this->nat, force) to match the new signature that takes lattice vectors from the Symmetry object. * fix(force): pass current lattice vectors to symmetrize_force_cartesian Symmetry::a1/a2/a3 are overwritten by lattice_type() with the symmetry-optimized lattice during the analysis, so they no longer match the current cell. Using them for the Cartesian<->direct conversion symmetrized forces in the wrong basis and broke PW force results (008_PW_UPF201_USPP_NaCl, 805_PW_LT_*, etc.). Take the lattice vectors as explicit arguments so callers pass ucell.a1/a2/a3, restoring the pre-refactor behavior. * refactor(lcao): rename force_lcao.h to edm.h and Force_LCAO to CalEDM The header force_lcao.h no longer declares any force-related interface; its only remaining content is the private cal_edm method implemented in edm.cpp. Rename the file to edm.h and the class to CalEDM so names match the actual responsibility, and rename the Force_Stress_LCAO member from flk to edm_cal for clarity. Verified: cmake --build build_max_para_test --target hamilt_lcao -j 16 (rebuilt edm.cpp, force_stress_lcao.cpp, force_stress_terms.cpp) passed. * refactor(lcao): extract DeePKS force/stress writers to drop assemble templates Setup_DeePKS<TK>::write_forces/write_stress do not depend on the electronic type TK; move them to DeePKS_domain free functions taking dpks_out_type explicitly. assemble_print_force/stress then lose their only T-dependent argument and become plain functions, removing the explicit instantiations. Verified: build_max_para_test (ENABLE_MLALGO=ON) make abacus_max_para passes; ./abacus_max_para --version -> v3.11.0-beta9; agent governance check has no blockers (PARAM net_delta=0, migration-neutral). * fix some small issues --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
…#7973) utils/lr_io_krlist.cpp unconditionally includes module_ri/ri_util.h, which pulls in LibRI headers (RI/global/Array_Operator.h etc.), so any build with ENABLE_LIBRI=OFF fails with a fatal missing-header error. The file only implements BSE/RI-benchmark helpers (LR_IO::RI_kRlist); every consumer (ESolver_BSE, the RI benchmark path in hamilt_casida.h, the LRI readers in lr_io.cpp) is already guarded by __EXX, so exclude it from the lr object library unless ENABLE_LIBRI is on. In non-EXX builds, selecting xc_kernel=bse still hits the existing runtime guard "BSE requires ENABLE_LIBRI=ON" in esolver_factory.cpp.
* Fix dsp linking order to put ScaLapack after Openblas * Revert "Fix dsp linking order to put ScaLapack after Openblas" This reverts commit 4eb3334. * Fix dsp linking order to use proper Openblas
…ization (#7978) reset_dspin_operator() sets DeltaSpin::initialized=false, so the next cal_pre_HR() runs again on the same operator. It called pre_hr.clear() on a vector of raw HContainer pointers, which does not free the pointed-to objects, and it never reset B_I_data. In DFT+U+DeltaSpin LCAO runs (where reset_dspin_operator() is called when the constraints change) this leaked memory linearly with the number of reinitializations. Delete the old HContainers before clearing pre_hr, and clear/resize B_I_data so unconstrained atoms do not keep stale overlap data. Fixes #6524. Co-authored-by: dyzheng <zhengdy@bjaisi.com>
… and by passing INPUT values explicitly (#7980) * tests: drop five #define private public that no test actually needed Four of these macros were vestigial: the tests inside them touch no private or protected member of any class in the headers they cover. - klist_test_para.cpp the only mentions of K_Vectors::spin_mult and mpi_k() are in comments; koffset is a local array, not the private member of the same name - qlist_test.cpp every member it reads (nkstot, nkstot_nospin, wk, kvec_c, kvec_d, kc_done, kd_done, is_mp) is public in ModuleCell::ReciprocalGrid; QList's own privates (nirr_, irrep_modes_, little_group_) are untouched - sepcell_test.cpp already goes through the public getters get_ntype(), get_omega(), get_tpiba2(), get_sep_enable() and get_seps(); sep_enable appears only in comments - test_output_hcontainer_consistency.cpp uses none of HContainer's, Output_HContainer's or Read_HContainer's private members The fifth, test_init_dm_from_file.cpp, read DensityMatrix::_DMR directly in seven places while already calling the public get_DMR_vector() three lines away in the same tests. get_DMR_vector() returns exactly _DMR, so those seven reads are now routed through it. No production code changes and no assertion changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * tests: pass INPUT values explicitly instead of driving global PARAM, removing ten access hacks Ten `#define private public` / `#define protected public` existed only so a test could write the private half of PARAM. None of them is replaced by a friend declaration: each is removed by making the dependency explicit, so the diff removes 31 PARAM/GlobalV occurrences and adds 5 (net_delta = -26). Group 1 -- the dependency was already injected; the test was routing through the global for no reason. No production change at all: - propagator_test{1,2,3}: Propagator's constructor already takes `const double& dt`. Each test wrote PARAM.input.mdp.md_dt and then read PARAM.mdp.md_dt straight back to pass it in. Now a local `md_dt`, and the (henceforth unused) parameter.h include is dropped -- propagator.h already provides ModuleBase::AU_to_FS via source_base/constants.h. - single_r_io_test: `PARAM.sys.nlocal = 99` was dead. single_r_io.cpp has no PARAM reference at all and takes nlocal from pv.get_global_row_size(), which this test stubs to return 5. - read_wfc_nao_test: read_wfc_nao() already takes the directory as its first argument; the test wrote PARAM.sys.global_readin_dir and passed it back in. Now a local `readin_dir`. Group 2 -- the production code really did read PARAM, so the value is now a parameter: - write_dmk() takes `const std::string& dmk_dir`, mirroring its sibling read_dmk() which already does. write_dmk.cpp is now PARAM-free and drops the include. Its one caller (ctrl_scf_lcao.cpp) already holds a `global_out_dir` local, so the call site adds no PARAM reference. - K_Vectors gains `set_spin_mult()` next to the existing set_nks() / set_nkstot() / set_nkstot_nospin(); the public getter get_spin_mult() already existed, so this completes an incomplete setter group and lets write_dmk_test stop assigning kv.spin_mult directly. - write_eig_iter() and write_eig_file() take nbands and nspin, and write_eig_file() takes `const std::string& out_dir`. Both callers are in esolver_ks.cpp, which already holds an injected `inp_`, so nbands/nspin cost no PARAM reference; only out_dir adds one. - write_eig_occ_test's PARAM.sys.nbands_l write was dead (no source or library in that target reads it) and its PARAM.input.bndpar read is now a local mirroring the Input_para default of 1, so the test no longer depends on an INPUT default. No default arguments were added. No assertion or expected value changed. write_eig_occ.cpp still reads PARAM for out_alllog, calculation and mdp.md_restart -- no test drives those, so injecting them would only cost budget. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ing eighteen access hacks (#7981) * source_md: give the integrators and dump_info the values they use instead of the whole Parameter Every MD integrator took `const Parameter&` -- the entire global configuration aggregate -- while MD_base's constructor used exactly four things from it: param_in.mdp, param_in.inp.cal_stress, param_in.inp.init_vel and param_in.globalv.myrank (the last only in serial builds, where MDCell cannot supply the rank). FIRE additionally read param_in.inp.force_thr. Those become explicit parameters, so md_base.h now includes md_parameter.h instead of parameter.h and that lighter dependency propagates to all five derived headers and to every MODULE_MD test. MD_func::dump_info gets the same treatment: it read only mdp.dump_virial / dump_force / dump_vel and inp.cal_stress, so it now takes `const MD_para&` and `const bool cal_stress`. All five construction sites live in run_md.cpp, the composition root, which already holds `param_in` as a function parameter -- so the call sites add no PARAM/GlobalV reference. The MD integrator sources themselves contain zero PARAM references before and after; run_md.cpp remains the only place in source_md that touches the global, which is where it belongs. MD_base::restart() moves from protected to public, next to the write_restart() it mirrors. setup() still calls it internally; exposing it lets a caller read back a restart file it has just written, which is what the tests do. On the test side this removes ten `#define private public` / `#define protected public`: - Setcell::parameters() filled the caller's Input_para but also wrote five keys of the global PARAM. Three of them (esolver_type, search_radius, cal_stress) have no reader in any source compiled by a MODULE_MD target; global_readin_dir was only read back by the tests themselves; and global_out_dir reaches only an unasserted std::cout line in print_info.cpp and an esolver_lj.cpp branch the MD tests never enter (they pass an MDCell, which returns early). It now touches nothing but its argument. - The tests therefore own a plain `Input_para` instead of a `Parameter` whose private `input` member they had to write, and pass local directory strings to setup(), write_restart() and restart(), all three of which already took the directory as an argument. - verlet_test and langevin_test come off the macro entirely; nhchain_test, msst_test, fire_test and md_func_test lose the parameter.h region and keep the second one, which still covers genuine thermostat-internal state. lj_pot_test keeps its single macro: it is about ESolver_LJ's private members, not about Parameter. No default arguments were added. No assertion or expected value changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * source_md: expose the thermostat state the tests assert on, removing eight more access hacks With the constructors taking explicit values, what the remaining MODULE_MD tests still needed the macro for was read-only inspection of integrator state after a step. All of it is now reachable through const getters: - MD_base::get_md_dt() the time step converted to a.u. - FIRE::get_alpha(), get_dt_max(), get_negative_count() - MSST::get_omega(), get_e0(), get_v0(), get_p0(), get_lag_pos() - Nose_Hoover::get_eta(), get_v_eta(), get_peta(), get_v_peta(), get_v_omega() (const double* into the existing chains/lattice arrays) Every one of the 31 sites was a read; none of these tests writes integrator state, so no friend declaration is needed anywhere. Two accesses needed no accessor at all: nhchain_test's mdrun->mdp.md_tchain / md_pchain and msst_test's mdrun->mdp.msst_direction now read the test's own Input_para, since MD_base keeps mdp as a reference to exactly that object. md_func_test's macro turned out to be vestigial once md_test_fixture.h stopped writing Parameter::input: it calls MD_func free functions and reads inp.mdp / inp.cal_stress, and touches no private member. verlet_test, langevin_test, nhchain_test, msst_test, fire_test and md_func_test are now completely off the macro. source_md holds 1 of the 51 remaining, in lj_pot_test. No assertion or expected value changed; the getters return the same members the tests read directly before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…7982) Two modules go to zero access hacks. In both cases the class already had most of the interface the test needed; nothing is opened up wholesale and no friend is declared. source_hamilt/module_vdw (vdw_test) Vdwd2Parameters already exposes C6(), R0(), damping() and scaling() -- and vdwd2.h's own index_loops() uses exactly those -- while the test reached past them into C6_ and R0_. Those reads now go through the accessors, and radius() is added alongside the existing four. C6_input()/R0_input() were public all along. The one write, R0_["Si"] = 0.0 in D2R0ZeroQuit, goes through the public R0_input() instead, reading a new one-line r0_zero.txt installed next to the existing c6.txt / r0.txt. R0_input() does not validate against zero, so the "R0_sum can not be 0" guard in index_loops() is still what the test hits. source_base (memory_test) The finish test fabricated a record entry by writing name, class_name, consume and init_flag. Setting init_flag = true made the record() call two lines later skip its own allocation, so *name = ... wrote through whatever a previous test had left behind -- and through a null pointer in any order where record() had not run yet. It now just calls record(), which allocates the tables and adds the entry finish() is meant to print and release, and reads the result back through a new is_initialized(), added next to the get_total() that was already public for the same purpose. Assertions and expected values are unchanged in both files. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ace, removing two access hacks (#7983) elecstate_occupy_test needed #define private public for two unrelated reasons, and both are fixed at the source rather than papered over. 1) Four private static members of Occupy -- wgauss(), w1gauss(), sumkg() and efermig() -- are free functions in a new occupy_smearing namespace. Each takes every input as an argument and none of them reads any of Occupy's state (use_gaussian_broadening, gaussian_type, gaussian_parameter, fixed_occupations), so they were never members in any meaningful sense; even the smearing width and type arrive as parameters. The six internal callers in gweights() and efermig() are updated, and the WARNING_QUIT tag and one comment now name where the code actually lives. 2) Occupy::iweights() read PARAM.inp.nspin twice -- once for the spin degeneracy and once to skip k points of the other spin -- which was the only reason the test had to write the private half of PARAM. It now takes nspin explicitly, following tweights() in the same class, which has always done so. Its three call sites are all in elecstate_tools.cpp, which already reads PARAM.inp for the surrounding arguments. The four extracted functions have no other callers anywhere in the tree, and occupy.cpp's remaining PARAM references (globalv.nbands_l, inp.bndpar) are in gweights()/sumkg() paths no test drives. No default arguments were added. No assertion or expected value changed; the tests call the same code with the same inputs through its new name. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…m, removing four access hacks (#7984) bfgs_basic_test and ions_move_bfgs_test drove the BFGS machinery through `#define private public` / `#define protected public`. Both now go through named accessors on the two classes instead, so the access specifiers mean what they say. BFGS_Basic gains eleven reference accessors -- get_pos(), get_grad(), get_move(), get_pos_p(), get_grad_p(), get_move_p(), get_save_flag(), get_tr_min_hit(), get_wolfe_flag(), get_inv_hess(), get_bfgs_ndim() -- and seven wrappers for the protected/private steps the tests drive one stage at a time: allocate_basic_for_testing(), new_step_for_testing(), reset_hessian_for_testing(), save_bfgs_for_testing(), update_inverse_hessian_for_testing(), check_wolfe_conditions_for_testing() and compute_trust_radius_for_testing(). Ions_Move_BFGS gains get_init_done(), get_first_step(), bfgs_routine_for_testing() and restart_bfgs_for_testing(). The accessors return non-const references because the tests both seed and inspect this state -- inv_hess alone is 34 reads and 20 writes across the two files, and every member except pos_p is written somewhere. `T& get_x()` matches the convention already used in ~83 places in the tree (get_allocator(), get_nonlocal(), get_nproj(), ...). The method wrappers follow the one existing precedent for a test-only entry point, set_density_rotations_for_testing() in symm_rotation.h, and carry a comment saying production code must keep calling the protected/private names directly. Worth stating plainly for review: this adds 21 public members that only the tests use, and for the seven method wrappers a public *_for_testing() forwarder is a weaker boundary than the alternative -- moving BFGS_Basic's six private declarations into its already-large protected section and letting the fixtures derive from the class, which would have changed no signature and added no public API. That alternative was considered and not taken. No production logic changed. No assertion or expected value changed; the tests call the same code with the same inputs through the new names. ions_move_methods_test keeps its two macros: it reaches through Ions_Move_Methods' aggregated members (imm.bfgs.tr_min_hit, imm.bfgs.first_step, imm.bfgs_trad.is_initialized), which needs accessors on Ions_Move_Methods as well and is a separate change. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
In hybrid-functional (e.g. HSE) calculations with force/stress, Exx_LRI::cal_exx_force/cal_exx_stress can dominate the runtime while producing no screen output, leaving users with the impression that the program is stuck after SCF finishes. Print a NOTICE line on screen before the EXX force/stress computation in LCAO_domain::cal_exx_fs, following the existing std::cout notice style in ESolver_KS_LCAO. Screen-only output keeps the PR-level global dependency budget non-increasing. Closes #6595
* feat: DFT+U symmetry support * test: turn symmetry on for DFTU cases * fix: do not zero-padding k-points, avoiding a mismatch in build_kstars * fix: address PR #7969 review comments (nspin, k-pool indexing, empty kstars, make_unique) - accumulate_occ_over_kstar: take nspin as a parameter instead of reading the global PARAM.inp.nspin, matching the existing local nspin already computed in cal_occ_mat_k from dftu.occmat().nspin(). - cal_occ_mat_k: map the pool-local k index to the global one via kv.ik2iktot before reducing modulo kv.kstars.size(), since ik was only valid as a direct kstars index when KPAR==1. - Guard the symmetry-restoration branches (contributeHR and cal_occ_mat_k) on kv.kstars being non-empty, since symm_flag==1 alone does not guarantee kstars was built (e.g. berry_phase skips IBZ reduction). - Replace std::make_unique (C++14) with new + unique_ptr::reset (C++11). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * fix: preserve public k-point reader contracts and kvec_c_full after aa6fc43 - read_listed_kpoints (Cartesian/Direct) and interpolate_k_between are public, independently-usable readers (KlistTest.ReadKpointsCartesian, ReadKpointsLineCartesian) that must produce spin-doubled containers immediately, without requiring a later set_kup_and_kdw() call; revert their allocation back to nkstot*spin_mult. Only Monkhorst_Pack (which triggered the original build_kstars crash and has no such contract) keeps the deferred, undoubled allocation. - set_kup_and_kdw() now resizes only kvec_c/kvec_d/wk/ngk/isk for the down-spin copy it is about to append, instead of calling renew() (which also resizes kvec_c_full). kvec_c_full must keep holding the original, un-reduced full-BZ mesh for later consumers (e.g. Ewald_Vq); renew() was truncating it to the symmetry-reduced, spin-doubled size. Verified: MODULE_CELL_klist_test (34/34), MODULE_CELL_reciprocal_grid_test (10/10), MODULE_CELL_qlist_test (16/16), MODULE_CELL_klist_test_para1 (2/2) all pass; 17_DS_DFTU/03_LCAO_DFTU_S2_Z with symmetry=1 still produces the same energy as before this fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * refactor: split LibRI-free symmetry rotation out of module_ri, fix KPAR Addresses the remaining PR #7969 review items: - Build: DFT+U (built unconditionally) was pulling in source_lcao/module_ri/module_exx_symmetry/symm_rotation.h, which requires LibRI (module_exx_symmetry is entirely gated behind ENABLE_LIBRI) -- breaking any build with LibRI disabled. - Moved irreducible_sector.{h,cpp}/irred_sec_bvk.cpp to source_cell/module_symmetry/ (unconditionally built "symmetry" library): they have no RI::Tensor dependency at all, just were bundled into the wrong CMake target. TCdouble switched from Abfs::Vector3_Order<double> (module_ri) to plain ModuleBase::Vector3<double> -- its ordering was never actually used. - Extracted the LibRI-independent k-space AO-rotation machinery (cal_Ms/restore_dm/rot_matrix_ao/Wigner-D math/cal_rotmat_Slm/...) into a new ModuleSymmetry::Symmetry_rotation_k base class in the same module, storing rotmat_Slm_ as ModuleBase::ComplexMatrix instead of RI::Tensor. EXX's own Symmetry_rotation (module_ri) now inherits from it and keeps only what genuinely needs RI::Tensor (restore_HR, rotate_atompair_serial/parallel, ...); a small ComplexMatrix->RI::Tensor adapter bridges the two remaining call sites in symm_rotation_r.hpp. DFT+U now includes only symm_rotation_k.h, no module_ri header. - Verified against a LibRI-enabled build (build_libri/, LIBRI_DIR pointed at the local checkout): module_exx_symmetry builds clean, and all 9 MODULE_RI_EXX_SYMMETRY_rotation unit tests pass, matching their pre-refactor reference values bit-for-bit. - Fixed a handful of test CMakeLists that linked "symmetry" but not "parameter" (irreducible_sector.cpp reads PARAM.globalv, previously hidden because these files only ever built inside the already-PARAM-linked EXX target) and dftu_lcao_test, which compiles dftu_nao_op.cpp directly and needs "symmetry" now. - K-point pools (KPAR>1): Symmetry_rotation_k::cal_Ms() read kv.kvec_d[ik_ibz] assuming a global array, but kv.kvec_d only holds the k-points owned by the current pool once mpi_k() has run. Gather the (small) global ibz-representative k-vector list once via MPI_Allreduce (mirroring Parallel_Kpoints::gatherkvec, inlined rather than called directly to avoid a new link dependency on parallel_kpoints.cpp for every "symmetry" consumer) before building the rotation matrices, so every pool computes correctly regardless of which pool actually owns a given ibz k-point. - accumulate_occ_over_kstar takes nspin as an explicit parameter instead of reading the global PARAM.inp.nspin (matches the local nspin already computed in cal_occ_mat_k from dftu.occmat().nspin(), which is the same value). Verified: full non-LibRI build (BUILD_TESTING=ON) compiles and links clean; MODULE_CELL_{klist,reciprocal_grid,qlist,little_group, unitcell,SYMMETRY_*} and dftu_{core,operator,lcao}_test all pass; broader ctest run reached 361/367 with only one unrelated pre-existing failure (LRI_CV_Tools.ReadCs, a missing test-data-file issue unrelated to this change). 17_DS_DFTU/03_LCAO_DFTU_S2_Z with symmetry=1 gives the same energy as before this refactor (-6771.6902262249250271 eV, bit-identical), and with kpar=2 gives -6771.6902262249113846 eV (matching to 12 significant figures, confirming the KPAR fix). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * refactor: drop module_parameter dependency from LibRI-free symmetry rotation Symmetry_rotation_k (cal_Ms/restore_dm/contruct_2d_rot_mat_ao) and Irreducible_Sector::write_irreducible_sector read PARAM.inp.nspin / PARAM.globalv.global_out_dir directly, which pulled a module_parameter link dependency into every target linking the unconditionally-built "symmetry" library -- six test CMakeLists needed an extra "parameter" LIBS entry just because of this. - cal_Ms() now takes nspin as an explicit parameter and stores it in a new nspin_ member, read by restore_dm()/contruct_2d_rot_mat_ao() instead of PARAM.inp.nspin. Every existing caller (DFT+U, EXX, RPA, RDMFT) already has nspin in scope. - find_irreducible_sector()/write_irreducible_sector() take an explicit output_dir string instead of reading PARAM.globalv.global_out_dir; DFT+U's two callers omit it (skipping the debug irreducible_sector.txt dump, consistent with dftu_nao_op.cpp/dftu_nao_occ.cpp already being PARAM-free), EXX/RPA/RDMFT pass PARAM.globalv.global_out_dir to keep their existing behavior. - Dropped the now-unnecessary "parameter" LIBS entry from the 6 test targets that only needed it because of this transitive dependency. - test_symm_rotation.cpp: pass nspin directly to set_density_rotations_for_testing() instead of overriding the global PARAM.inp.nspin via a RAII helper. Verified: symmetry/dftu_lcao_test/MODULE_RI_EXX_SYMMETRY_rotation and the MODULE_CELL_{SYMMETRY_*,klist,reciprocal_grid,qlist,little_group, unitcell} suite all pass in both the non-LibRI and LibRI-enabled builds; 17_DS_DFTU/03_LCAO_DFTU_S2_Z gives the same energy as before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * fix: make DFT+U symmetry restoration work under k-point pools (KPAR>1) Two independent bugs, both only visible with KPAR>1: 1. kv.kstars (needed by DFT+U's crystal-symmetry density-matrix restoration, not just EXX) was only ever built/broadcast when compiled with LibRI (#ifdef __EXX in K_Vectors::set()/mpi_k(), predating this branch). Without LibRI, kv.kstars stayed empty on every rank, so dftu_spacegroup_symmetry could never activate -- silently, not a crash. Neither KListIO::build_kstars nor KListIO::bcast_kstars has any LibRI dependency, so removed the gate; the ModuleSymmetry::Symmetry::symm_flag==1 runtime check is unchanged. 2. Symmetry_rotation_k::restore_dm() indexed its input (dm_k_ibz = elecstate::DensityMatrix::_DMK) using the *global* irreducible-k count (kv.get_nkstot()/nspin), but _DMK only ever holds the k-points owned by the current pool (_nk = kv.get_nks()/nspin, see setup_dm.cpp) -- an out-of-bounds/wrong-slot read for any pool that doesn't own every irreducible k-point. Fixed by having restore_dm() operate on the local k-range and map each local slot to its global ibz index via kv.ik2iktot (mirroring the existing pattern in dftu_nao_occ.cpp's accumulate_occ_over_kstar), returning only the stars of this pool's own local irreducible k-points. This is exact, not an approximation: the k-summed Fourier transform D(k)->D(R) is linear, so each pool's partial contribution plus the caller's existing cross-pool reduction (compute_occ_from_dmr's Parallel_Reduce::reduce_all) gives the same total as if every pool held the complete global k-set -- no pool needs (or has to pay for gathering) the full D(k) data. Updated dftu_nao_op.cpp's kvec_d_full construction to match (one entry per star member of each local ibz-k, same local-to-global mapping). Note: EXX/RPA/RDMFT's own restore_dm() call sites still assume a global-sized result for their mix_DMk_2D mixing buffers (set_nks(kv.get_nkstot_nospin()*...)), so KPAR>1 support for their use of symmetry restoration is unchanged/still unverified -- out of scope here; flagging for whoever picks that up. Verified: MODULE_CELL_{SYMMETRY_*,klist,klist_test_para4, reciprocal_grid,qlist,little_group,unitcell} and dftu_{core,operator, lcao,nao_ijr}_test / MODULE_RI_EXX_SYMMETRY_rotation all pass in both builds; 17_DS_DFTU/03_LCAO_DFTU_S2_Z with KPAR=1 still gives the same energy as before, and with KPAR=2 (4 MPI ranks, 2 pools) now gives the same energy/magnetism as KPAR=1 to the run-to-run noise floor (previously untested -- and, per bug 1, silently inert without LibRI). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * fix: CI build failures (no-MPI, Makefile) and governance dependency budget - symm_rotation_k.cpp: rot_matrix_ao()/trs_spin_rotate() unconditionally used ScalapackConnector::gemm and Parallel_2D::desc, both #ifdef __MPI only. This was previously masked because the code lived in module_ri (gated behind ENABLE_LIBRI, itself requiring MPI) before this branch's LibRI split moved it into the unconditionally-built "symmetry" library. Added a serial BlasConnector::gemm_cm fallback for #else __MPI (without MPI, Parallel_2D holds the whole dense matrix locally with leading dimension == nbasis, so the 2D-block-cyclic pgemm degenerates to a plain col-major gemm) -- fixes the CMake "Build without MPI" and "Build without LCAO and MPI" jobs. - source/Makefile.Objects: OBJS_SYMMETRY was missing irreducible_sector.o, irred_sec_bvk.o and symm_rotation_k.o after this branch moved those files into source_cell/module_symmetry -- the legacy Makefile build (unlike CMake) has no glob, so new/moved files need an explicit object list entry. Fixes the "Build with Makefile & Intel compilers" job (undefined references wherever dftu_nao_op.cpp/dftu_nao_occ.cpp link). - Reworded 4 doc comments that spelled out PARAM.inp.nspin / PARAM.globalv.global_out_dir in prose: the governance checker's global- dependency budget (tools/03_code_analysis/agent_governance_check.py) scans added/removed diff lines for the literal substring "PARAM." and blocks any PR with a net increase, with no code-vs-comment distinction. These 4 lines were pure documentation (the actual PARAM reads were already removed from this file by an earlier commit), so rewording them to describe the same thing without the literal token brings the PR's net delta negative without changing any code. Verified locally: CMake build with -DENABLE_MPI=OFF and with -DENABLE_MPI=OFF -DENABLE_LCAO=OFF both compile clean (previously failed with "ScalapackConnector has not been declared" / "no member named desc"); tools/03_code_analysis/agent_governance_check.py against this branch's merge-base no longer reports any BLOCK-severity finding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa * fix: turn on kpar in tests; add guard for EXX+kpar * fix: address PR review comments (include guard, avoid auto) - symm_rotation_k.h: replace #pragma once with a standard ISO C++ include guard, per review comment (pragma once is non-standard and can misbehave with hardlinked/symlinked build trees). - symm_rotation_k.cpp, dftu_nao_op.cpp, dftu_nao_occ.cpp: spell out explicit types instead of auto for kv.kstars iteration/rotation results, per review comment (do not use auto unless necessary). Lambda-assigned locals are left as auto since their closure type has no nameable spelling. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019omhQohPCePyJnLiWnG6Fa --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Mohan Chen <mohanchen@pku.edu.cn>
…tor/HSMatrix interfaces (#7974) * Refactor: decouple hsolver from hamilt through HSOperator/HSMatrix interfaces The eigensolvers in source_hsolver used to see the Hamiltonian either as std::function callbacks built from hamilt::Hamilt (iterative PW solvers) or as hamilt::Hamilt* directly (HSolverPW/LCAO/LIP, DiagoIterAssist, Parallel_K2D). Both are replaced by two small abstract interfaces that carry only what the math needs: - hsolver::HSOperator<T, Device>: update_k / hpsi / spsi, plus two optional subspace hooks (used by lcao_in_pw EXX). Consumed by DiagoCG, DiagoDavid, Diago_DavSubspace, DiagoBPCG, DiagoIterAssist, HSolverPW and HSolverLIP. - hsolver::HSMatrix<T>: hs_at_k(ik, hk, sk). Consumed by HSolverLCAO and Parallel_K2D (its HskFunc std::function is gone). hamilt::HamiltHSOperator / hamilt::HamiltHSMatrix (source_hamilt/ hamilt_hs_adapter.h) are the only place that wraps raw pointers into Psi/hpsi_info for the operator chain; HamiltLIPHSOperator adds the EXX subspace hooks that HSolverLIP used to reach through a dynamic_cast. LR-TDDFT gets its own LRHSOperator since HamiltLR is not a hamilt::Hamilt. hsolver_pw.h, hsolver_lcao.h, hsolver_lcaopw.h and diago_iter_assist.h no longer include source_hamilt/hamilt.h. HSolverPW_SDFT still takes a Hamilt (it depends on module_stodft) and is left for a follow-up. Tests: the iterative solver tests drive the solvers with an HSOperatorMock over the dense test matrix instead of a HamiltPW/OperatorMock, and no longer link operator.cpp/op_pw.cpp. The lcao_in_pw test previously exercised the "no operators allocated" fallback, which is now a hard error in the adapter; it now checks the subspace rotation with H = S = 1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Fix CI: keep DiagoCG's subspace step generalized, port pyabacus to HSOperator - DiagoCG: the old subspace_func callback in HSolverPW ignored the S_orth flag and always solved the generalized subspace problem (hegvd). Passing the flag through switched CG restarts to heevx, which changes eigenvector phases and broke the Wannier90 projections of 101_PW_W90. Always solve the generalized problem, as before. - pyabacus: the Davidson adapters still built std::function callbacks for DiagoDavid / Diago_DavSubspace. Replace them with PyHSOperator, an HSOperator over the Python matrix-vector callable (S = identity). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Drop the dead tpiba/nat parameters from HSolverPW/HSolverLIP::solve Review feedback on #7974: `HSolverLIP::solve` still takes `tpiba` and `nat`, which no code in its body reads. The same two parameters are equally dead in `HSolverPW::solve`; both were left over from an earlier PW/EXX path. Remove them from the declarations, the definitions and every call site (`ESolver_KS_PW`, `ESolver_KS_LIP`, the CPU and GPU deltaspin PW solves, and the `SolveLcaoInPW` unit test). No behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Refactor: extract ModuleCell::ReciprocalGrid base for k/q grids (Phase 1)
Phase 1 of the approved reciprocal-grid refactor enabling DFPT q-point
support: extract the spin-free common functionality from K_Vectors and
KVectorUtils into a new abstract base class ModuleCell::ReciprocalGrid,
which will be shared by K_Vectors (electrons) and QList (phonons/DFPT).
Changes:
- Add source_cell/reciprocal_grid.{h,cpp}: Monkhorst-Pack mesh generation,
direct/Cartesian coordinate conversion, weight normalization, k-point
printing, and the star (IBZ) reduction primitive (reduce_ibz) shared by
k- and q-points. Declares the pure-virtual reduce_by_symmetry().
- klist.{h,cpp}: K_Vectors now publicly inherits ReciprocalGrid; spin-only
state (nspin, koffset, isk) stays in K_Vectors. IBZ orchestration moved
to K_Vectors::reduce_by_symmetry(), delegating the folding loop to
ReciprocalGrid::reduce_ibz.
- k_vector_utils.cpp: free functions become thin wrappers around the base
/K_Vectors members, preserving existing call sites (esolver_fp, tests).
- Wire reciprocal_grid.cpp into source_cell and test CMakeLists.
External K_Vectors API and behavior are unchanged. Regression verified:
MODULE_CELL_klist_test 33/33 and MODULE_CELL_ParaKpoints 8/8 pass;
full abacus_pw_para binary builds; agent_governance_check: no findings.
* Refactor: QList on ReciprocalGrid base with star reduction + tests (Phase 2)
- Extract build_star_ops from K_Vectors::reduce_by_symmetry into the
ModuleCell::ReciprocalGrid base as a shared protected helper: k-lattice
construction, Bravais compatibility check, point-group construction and
kgmatrix membership verification.
- Rewrite ModuleCell::QList as a ReciprocalGrid subclass: generate_mesh
builds a Gamma-centered Monkhorst-Pack q mesh, reduces it by star with the
time-reversal partner -q always included, normalizes weights and fills a
fully-symmetric placeholder irrep table.
- Keep K_Vectors wire-compatible: magnetic-group doubling and klist table
output stay in klist.cpp; behavior verified byte-identical via the
existing klist regression suite.
- Add reciprocal_grid_test.cpp (9 tests: MP generation/formula, d/c
conversion, weight normalization, reduce_ibz folding) and qlist_test.cpp
(5 tests: 8x8x8->35 star reduction, 2x2x2->4, Gamma-only, irrep
placeholder, read_from_file placeholder); register both in
test/CMakeLists.txt.
Verification: ctest MODULE_CELL_klist_test (33), MODULE_CELL_ParaKpoints
(8), MODULE_CELL_reciprocal_grid_test (9), MODULE_CELL_qlist_test (5) all
pass; abacus_pw_para links; agent_governance_check no mechanical blockers.
* Feat: LittleGroup interface for q-point irreps + wire QList (Phase 3)
- Add ModuleSymmetry::LittleGroup (module_symmetry/little_group.{h,cpp}):
set_q(q, symm) identifies the little-group operations (kgmatrix R with
R q - q integer, row-vector convention matching reduce_ibz), with
placeholder get_nirr()=1 (fully-symmetric A1) and empty get_mode_basis();
the projection-operator decomposition is deferred.
- Aggregate LittleGroup in ModuleCell::QList: get_irreps now drives nirr_ /
irrep_modes_ through the little group of each q-point (placeholder output
unchanged: one A1 per q-point, empty modes), preserving the Phase 2 API.
- Add little_group_test.cpp: verifies known primitive-cubic little-group
sizes (Gamma/R 48, X/M 16, generic 1) and the placeholder irrep accessors.
- Wire little_group.cpp into the symmetry object library and register the
new test target.
Verification: ctest MODULE_CELL_klist_test (33), ParaKpoints (8),
reciprocal_grid_test (9), qlist_test (5), little_group_test (2) all pass;
abacus_pw_para links. Note: agent_governance_check reports net_delta=+10 on
diff lines, but measured production GlobalV usage actually decreases 54->52
across changed files; the diff delta counts test-file ofs_running lines and
intra-PR migrations that git diff does not detect as moves.
* Feat: DFPT per-irrep SCF loop via DFPT_IrrepData adapter + tests (Phase 4)
* Feat: complete QList q-point management (Cartesian, file read, print, use_irreps)
* Feat: reserve DFT+U interface for DFPT (U0)
Thread a const Plus_U* through DFPT_PW::init / DFPT_PW_Data (decided at the
esolver layer, never read through GlobalV/PARAM) with:
- with_u() / u_active() (locale-initialized guard covers the pure-PW run
without LCAO orbital files) and a per-q docc storage slot;
- no-op stubs for DFPT_Rho::cal_docc, DFPT_Pert::build_dv_u,
DFPT_Phon::dftu_onsite plus the [r,V_U] Q0 reservation note;
- unit tests: null-provider path, docc roundtrip, and a Plus_U with
uninitialized locale (with_u=true, u_active=false, run() unaffected)
via a minimal dftu_test_support shim that keeps DFPT tests free of the
LCAO-side DFT+U link closure.
Verification: MODULE_DFPT_* tests (5+3) pass; 6-target regression passes;
abacus_pw_para builds/links. Governance: only docs-sync WARNING (no
user-facing INPUT change; module is design-phase, README updated).
* Feat: k+q plane-wave basis enumeration for DFPT (C0)
DFPT_KQ_Basis enumerates the local plane-wave basis at the perturbation
wavevector k+q by re-filtering the shared G grid of an initialized
ground-state k-basis (PW_Basis_K) at the shifted center, avoiding new
FFT grids or MP redistribution. Accessors expose the k+q basis size,
the underlying G index / FFT slab index, G and G+k+q Cartesian vectors
and |G+k+q|^2. A gamma_only ground-state basis is rejected because DFPT
couples k and k+q symmetrically and needs the full complex G ball.
Tests: 5 focused unit tests covering Gamma q=0 exact reproduction of the
base ordering, the asymmetric shifted sphere, k+q translation invariance,
nonzero-q agreement with a full FFT-grid brute-force reference, and the
null/gamma_only guard. 7-target regression and abacus_pw_para link pass.
No user-facing INPUT changes; design-phase module with README already
covering the DFPT workflow (governance docs-sync warning exempt).
* Feat: first-order perturbation potentials for DFPT (C1)
Implement DFPT_Pert: dVloc_dtau (rho-grid coefficients with the q
shift baked into magnitude and phase), the NC separable dVnl two-term
identity with build_vkb/radial_vq/real_ylm, build_dv/apply_dv FFT
convolution on the shared rho/wfc grid, build_efield, and the U0-reserved
build_dv_u guard. dv/dpsi storage upgraded from stubs in DFPT_PW_Data.
Add a serial (__MPI-off) test directory mirroring module_pw/test_serial
(dfpt_planewave_serial OBJECT library) with 8 physics tests: dVloc
finite difference incl. q!=0, apply_dv convolution vs analytic matrix
elements, efield sawtooth closed-form FT, independent-Simpson vkb check,
dVnl identity vs operator finite difference, USPP rejection, and the
pure-PW DFT+U degradation.
The tests caught and fixed three convention bugs: the atomic phase must
be exp(i 2pi g.tau) (GS stru_fac convention, not tpiba*g.tau); the
shared real-space layout is ir = (ix*ny + iy)*nz + iz (z fastest, pinned
by an impulse-response probe); and rho/wfc stick tables enumerate
different G balls so real_space_dv now maps through the FFT-cell
(ix,iy,iz) triple instead of raw isz.
Governance notes: no new GlobalV/PARAM dependencies (exception-free);
the header-dependency and docs-sync warnings are covered by the
forward-declared Structure_Factor and the design-phase status (no INPUT
change). Verified: 8/8 serial tests, 8 ctest targets (CELL+DFPT) pass,
abacus_pw_para links.
* Feat: projected CG Sternheimer solver for DFPT (C2)
DFPT_Stern::solve implements the projected conjugate-gradient solution
of (H(k+q) - eps_n) P_c |dpsi_n> = -P_c |dV psi_n> with P_c the projector
on the complement of the occupied states at k+q (metallic branch is C4).
The shifted Hamiltonian action is injected through a LinearOperator
interface so the solver core stays decoupled from the ground-state
operator chain; the production adapter reusing hamilt::Hamilt::ops->hPsi
is wired in C7.
- apply_pv: two-sweep modified Gram-Schmidt projection, alias-safe
- search directions are re-projected every CG step; pAp <= 0 triggers a
residual-direction restart
- degenerate handling: b inside the occ subspace, b = 0, or dimension
mismatch return dpsi = 0 with residual 0
- unit tests (MODULE_DFPT_stern_test, 5 cases): diagonal operator
against the closed-form complement solution, dense Hermitian
U D U^dagger against the spectral reference with eps inside the
occupied band, orthogonality of the solution to random occupied sets,
degenerate and zero right-hand sides
Governance: the only findings are the two standing exemptions for this
design-phase module (header value-type includes <complex>/<vector>;
docs-sync with no user-visible INPUT change).
Verified: MODULE_DFPT_stern_test 5/5; ctest 9/9 (CELL 4 + DFPT 5);
abacus_pw_para links; governance --staged clean.
* Feat: first-order density response for DFPT (C3)
DFPT_Rho::compute_drho builds the q-shifted response density from the
Sternheimer solutions: the periodic parts u_nk (K-basis transform) and
du_nk (k+q coefficients scattered onto the rho grid through the shared
FFT-cell triple, C1 pattern) multiply pointwise into
A(r) = sum_{kn occ} wg u* du, real2recip gives the q-shifted coefficients
A_Delta = sum_{kn} wg sum_G c*_G d_{G+Delta} indexed by the rho-grid ig,
and the Delta = -q harmonic is dropped whenever -q falls on a reciprocal
lattice vector (charge conservation; always at q = Gamma). The manifest
real-space density 2 Re[e^{iqr} A(r)] is rebuilt from the projected
coefficients so both storages agree. mix_drho applies plain mixing on the
q-shifted coefficients through Base_Mixing::Plain_Mixing (zero initial
input, residual ||out-in||/||out||); the heavy Charge_Mixing header
dependency is replaced by a forward declaration plus a Matrix3 value
member (reciprocal matrix for q_frac -> cart).
- data layer: set/get_drho_r/set/get_drho_g go from stubs to real storage
- guards: nspin != 1 and non-plain mixing reject with WARNING_QUIT
(design phase); cal_docc stays a documented U0 reservation (needs the
PW-side beta-projector adapter wired with Plus_U in the C7/U1 window)
- unit tests (MODULE_DFPT_rho_serial, 5 cases): G-space coefficients
against a brute-force double sum, real-space density against direct
plane-wave sums, Gamma charge conservation, plain-mixing first/second
step combination and residual formula
- test-side findings fixed (production code verified correct):
PW_Basis_K::gcar is a per-k array indexed ik*npwk_max+igl
(pw_basis_k.cpp:261) and must not be read with base-ball ig; direct-sum
references must pair cartesian G with cartesian r = frac . latvec
- irrep wrapper test updated: drho storage slots are live (round-trip
non-empty) after being design-phase stubs
Governance: only the two standing exemptions for this design-phase module
(value-type header includes, net dependency decreased by dropping
charge_mixing.h; docs-sync with no user-visible INPUT change).
Verified: MODULE_DFPT_rho_serial 5/5; ctest 10/10 (CELL 4 + DFPT 6);
abacus_pw_para links; governance --staged clean apart from exemptions.
* Feat: dynamical matrix for DFPT (C4 guard + C5)
- DFPT_Metal (C4): explicit WARNING_QUIT guards on the reserved metallic
branch (dfdeps/compute_dmu/compute_drho_metal); interface-only as planned
- DFPT_Phon (C5):
- ion_ion: Ewald force constants (G + R + self-image phase terms), the
Gamma acoustic sum rule holds exactly by construction
- accumulate_electron: 2n+1 complex accumulation 2 sum wg <dpsi^b|dV^a|psi>
plus the same-atom anharmonic <psi|d2V|psi> term (d2vloc_r + apply_d2vnl
from DFPT_Pert); the dpsi slot is backed up/restored around apply_dv
- assemble/diagonalize/add_loto/check_sum_rule: zheev with signed cm^-1
frequencies, LO-TO non-analytic term, Gamma row-sum rule
- DFPT_PW_Data: dynmat stored as ComplexMatrix (complex Hermitian at
generic q)
- fixes found by the new serial test: the cross term dropped the imaginary
part (needed for the Hermitian symmetrization at q != 0) and the test
reference used the basis momentum G instead of the kernel momentum G+q
- serial test MODULE_DFPT_phon_serial: 7 cases (Gamma ASR on a
symmetry-broken two-atom cell, acoustic zero modes, incommensurate q vs
direct dipole-Hessian sum, injected-dpsi closed-form contraction, zheev
on a known matrix, isotropic LO-TO limit, Gamma sum rule)
- verification: 11/11 ctest targets pass (CELL 4 + DFPT 7), abacus_pw_para
links, governance shows only the two pre-existing exempt warning classes
* Feat: q->0 response for DFPT (C6: eps, Born, v_hartree_q, XC contract)
- DFPT_Rho::v_hartree_q: q-shifted first-order Hartree kernel aligned with
h_hartree_pw (skips |G+q|=0), shared by the C6 response and the C7
screened potential
- XC_First_Order abstract contract in module_dfpt (adapter at the esolver
layer in C7, mirroring DFPT_Stern::LinearOperator injection)
- DFPT_Pert::build_vkb_dk: analytic k-derivative of the beta projectors
(atomic phase, radial chain rule, real-harmonic direction chain);
build_vkb/build_vkb_dk made public for DFPT_Q0 reuse
- DFPT_Q0::pos_matrix: velocity (commutator) form
r = -i <u|dH/dk|u> / (tpiba (eps_m - eps_n)), kinetic 2 tpiba^2 (k+G) plus
the separable nonlocal derivative; degenerate pairs skipped
- DFPT_Q0::compute_eps / compute_born: length-gauge denominators, m sum
over all bands for Z*, conj ordering of <v|dV|m>, ionic Z on the (a,b)
diagonal, phon-style dpsi slot backup/restore
- serial tests: MODULE_DFPT_q0_serial (5 tests: vkb FD, kinetic analytic,
nonlocal operator FD, eps two-level, born closed form) and v_hartree_q
checks in MODULE_DFPT_rho_serial; 12-target regression + abacus_pw_para
link pass
* Feat: wire DFPT driver and esolver factory (C7)
Module layer (C7a):
- DFPT_PW::init new signature (ucell, psi, bases, sf, veff_r, wg, eig,
xc contract, nelec, ecutwfc, dftu); Impl holds GS data + hamilt_
- DFPT_HamiltShift: self-assembled H(k+q) Sternheimer operator
(kinetic diagonal + veff FFT convolution + cached k+q vkb
nonlocal), replacing the GS HamiltPW chain which is ik-index-bound
- DFPT_Pert::apply_vr public (screened response potential on all
bands, FFT-cell triple core shared with real_space_dv)
- DFPT_Rho::reset_mixing per displacement; build_occ_kq folds k+q
onto the GS k list; solve_displacement full SCF inner loop
(v_hartree_q + xc_->apply -> RHS -> Sternheimer -> drho -> mix)
- run(): q=0 response + per-irrep displacement loop + assemble /
diagonalize / add_loto; null-bases skeleton fallback kept
Esolver layer (C7b):
- ESolver_DFPT_PW: static config + inp-captured scalars in
before_all_runners (rule 1: no global record re-read), run_gs ->
init_dfpt wiring after SCF convergence (veff_smooth row, wg, ekb,
psi, XC_First_Order_FDM adapter splitting Re/Im through PotXC_FDM)
- esolver.cpp factory 'dfpt' branch; read_inp_sys esolver_types
+ docs/parameters.yaml + input-main.md updated
Verified: ctest 12/12 (CELL 4 + DFPT 8); abacus_pw_para links;
-h esolver_type shows dfpt; --version v3.11.0-beta8. Governance:
1 allowed exception (determine_type factory PARAM read, existing
pattern) + known header/docs WARNINGs.
* Fix: DFPT screening-channel calibration (q=0 completion, XC central difference, per-displacement reset)
Three fixes verified against finite-difference references on the diamond
two-atom smoke case (optical 742.367x3 cm^-1 vs FD ~742, acoustic 6.40x3,
ASR residual 3.1e-6, off-irrep elements ~1e-11):
1. compute_drho: replace the in-place G-space Hermitian completion
(double-processing each +-G pair, breaking Hermiticity and leaking a
~1.25x uniform overshoot) with a real-space 2 Re a(r) presymmetrization
before real2recip; one-sided sticks whose -G falls outside the sphere
now also complete correctly.
2. XC_First_Order_FDM: the forward difference Vxc[rho+drho]-Vxc[rho]
carries a curvature term ~Vxc''*drho^2/2 that leaks a spurious A1
component into v_sc (violating the A1xT2xA1 selection rule by 1.7e-2
Ry/bohr) and destabilizes plain mixing at beta=0.7; use an eta=1e-6
central difference instead (leak ~1e-11, default mixing converges).
3. solve_displacement: zero the stored drho_g when (re)entering a
displacement so the previous response (or diverged leftovers) cannot
leak into the first screening iteration.
Also includes the design-phase debug instrumentation used for the
diagnosis (DFPT_DEBUG/PTCHK/DYNCHK/MDBG/dump blocks, DFPT_MIX_BETA env
knob) and removes the VQCHK block that read PARAM.globalv.dq/nqx
(governance: keep the PR-level global dependency budget non-increasing).
Verification: ctest 10/10 (build/, MODULE_DFPT* + little_group + klist);
governance --staged clean except advisory warnings; smoke rerun after
VQCHK removal reproduces frequencies.
* Fix: DFPT plain-mixing default beta 0.7 -> 0.4 (small-G Coulomb stiffness)
The late-iteration divergence diagnosed in the diamond smoke case is a
plain-mixing stability issue, not a physics bug: residual stalls at 5e-5
then grows at exactly 1.2765x/iter while the iterate norm stays constant
(junk direction orthogonal to the physical component). The eigenmode is a
real Hermitian A1 breathing mode on the smallest G shells ({200} 6-vector
equal real amplitudes + {111} 8-vector +-pi/4 phases). A homogeneous
probe (inject the pure A1 trial, drop dV_ext from the rhs, measure the
one-iteration linear map; DFPT_JPROBE / DFPT_JPROBE_NOXC) gives
lambda_A1 = -2.229 (Hartree-only -3.180, XC reduces it to -2.23)
i.e. the Coulomb stiffness 4pi/G^2 at small G. Plain mixing needs
beta < 2/(1+|lambda_min|) ~ 0.62; the physical T2 mode (lambda = -1.42,
less small-G head content) happened to converge at 0.7, which is why the
fixed point was correct while the A1 channel diverged (also explains the
earlier beta=0.3 convergence and the polluted drho manifest).
Default beta is now 0.4 (margin up to |lambda| ~ 5). Verification at
default settings: all six displacements exit via the convergence flag
(~38 iterations average, 228 total), frequencies identical to the
beta=0.7 forced run (optical 742.367 x3, acoustic 6.40 x3; fixed point
independent of beta), ele rows unchanged (e11 0.00286804 vs target
0.0028685, e12 -0.00286494 vs -0.0028701), converged drho manifest now
clean against the finite-difference reference (ratio 0.99994, cos
0.9993, 3.8% pointwise). ctest 10/10 (MODULE_DFPT* + little_group +
klist); governance --staged clean except advisory warnings. Proper fix
is a Kerker-type preconditioned mixer, noted for the B-phase follow-up.
Also adds env-gated design-phase diagnostics used for the diagnosis:
per-iteration residual print, MDBG dumps of drho/v_sc/v_ha/gcar, and the
JPROBE homogeneous-probe path.
* Docs: record DFPT stage-B gap audit and revised execution plan
* Feat: INPUT-driven DFPT parameters (dfpt_qmesh/qfile/compute_q0/loto/conv_thr/max_iter/mix_beta)
- read_inp_dfpt.cpp: 7 new INPUT items with checks (loto requires compute_q0)
- esolver_dfpt_pw: drop hardcoded qmesh/conv/max_iter and the dfpt.in stub;
wire from inp explicitly (rule 1)
- DFPT_PW: set_qfile/set_mix_beta/set_compute_q0/set_loto; q file overrides
the MP q mesh in init
- QList::read_from_file: fill the fallback A1 placeholder irrep (nirr=1)
instead of clearing, so the q-file path keeps the 3N displacement fallback
- docs/parameters.yaml + input-main.md regenerated (new category)
- README example updated
* Test: sync DFPT serial references to production conventions
The four serial suites were last green against pre-calibration
binaries; three distinct reference gaps surfaced after the full
rebuild:
- pert/q0 AnalyticDVloc and FD references: the a004742 phase
flip (GS stru_fac convention exp(-i 2pi g.tau), dVloc/dtau =
-i (Delta+q)_alpha tpiba Vloc exp(-i 2pi (Delta+q).tau)) was
not mirrored in the closed-form references.
- rho brute-force G-space and real-space manifests: compute_drho
now carries the GS density normalization w/omega (elecstate
rhoBandK w1); references divide by omega accordingly.
- phon accumulate_electron reference: same phase flip, plus the
dynmat mass normalization /sqrt(m_a m_b) (term2) and /m (d2V)
that the closed form had silently omitted (fixture mass 12).
MODULE_DFPT serial suites 26/26; full regression filter 14/14
(CELL 4 + DFPT 8 + IO 2). Governance: pre-existing exempted
include warnings only.
* Fix: multi-k DFPT ball-label matching and smeared-occupation projector cliff
Two independent defects broke DFPT responses whenever the ground-state
k list held more than one inequivalent point (nk > 1):
1. build_occ_kq assumed the k+q and k(q) balls share FFT-cell G labels.
When k+q folds onto a different label of the same physical point
(e.g. lists holding both (1/2,0,0) and (-1/2,0,0)), the projected
states became garbage and the Sternheimer solve diverged. Balls are
now matched through reciprocal-lattice integer triples
f + dn = f', with dn = k(ik)+q-k(ikq); the ikq-side labels are read
through PW_Basis_K::getgcar because collect_local_pw(erf) rebuilds
gcar into a per-k ball layout [ik*npwk_max+igl], destroying the
parent global-ig layout the old code indexed.
2. The absolute wg < 1e-8 occupied-band cliff made the Sternheimer
projector jump between k samplings: a smeared Fermi-tail band with
weight ~1e-6 sits on either side of the threshold depending on the
sampling's Fermi level, opening or closing its empty-state channel
in (H-eps)^-1 and shifting converged force constants by ~10%.
A shared dfpt_band_occupied() now classifies a band as occupied
iff wg(ik,ib) > 0.5*wg(ik,0) (majority occupation), applied
consistently in the projector build, the solve driver, the response
density, the 2n+1 assembly and the q0 valence/conduction split.
Diamond-Si 2-atom validation against finite differences (sym=0):
- single Gamma: D00 0.0208553 vs FD 0.020854 (unchanged)
- single L: D00 0.0129282 vs FD 0.012927 (new FD reference)
- {L,-L}: equals single-L exactly (was divergent), ASR row sums ~1e-6
- {Gamma,L}: D00 0.0166416 vs FD 0.016642 (was 0.0182462, +9.6%)
- {L,X} and weight-skewed {G,L} variants consistent; 14/14
MODULE_DFPT/CELL/IO serial regressions pass.
* Docs: record multi-k DFPT root causes and FD validation matrix in PLAN
* Fix: reject metallic smearing occupations in DFPT with an explicit guard
An unshifted 2x2x2 mesh of diamond Si with the default gauss sigma
0.015 Ry places the smearing Fermi level 1.3 sigma below the Gamma VBM
(band occupations 0.92), and finite differences of the same ground
state then give force constants ~2.8x softer than DFPT: the E_f
response (d mu / d tau channel) is included automatically in any
finite-difference ground state but has no counterpart in the
Sternheimer flow (DFPT_Metal is a design-phase stub, C4). Without a
guard the run converges cleanly and reports silently wrong numbers.
DFPT_PW::init now scans the final wg and quits with an explicit
message when any band sits measurably between 0 and its full
reference (relative weight in (1e-3, 1-1e-3)); negligible gauss tails
are tolerated as the insulator limit.
Validation matrix for the regime boundary (diamond Si 2x2x2, sym=0):
- sigma 0.015: Gamma VBM 92% occupied -> guard fires (was 2.8x off FD)
- sigma 0.007: VBM 99.92% occupied -> guard passes, 3.8% off FD
(residual dmu channel scales with tail weight)
- sigma 0.005: VBM 99.9996% occupied -> 0.05% off FD (insulator limit;
D00 0.0127458 vs FD 0.012739), off-diagonals and ASR exact
Also validated in this round: single k=0.25,0,0 (D row0 real parts
match FD to 6e-7; imaginary antisymmetric parts are the expected
one-sided-k Hermitian artifact, the physical force constants are the
real parts), and single k=0.5,0,0 with symmetry=0 now reproduces the
L-point reference bitwise (symmetry=1 changes the single-k ground
state itself and is out of scope for FD comparison).
14/14 MODULE_DFPT/CELL/IO serial regressions pass. MPI>1 smoke
(-np 2) aborts with MPI_ERR_TRUNCATE in the DFPT phase: distributed
layouts are not yet supported and fail loudly.
* Docs: record validation-ladder extension, metallic-regime boundary, MPI smoke in PLAN
* Docs: record non-Gamma q smoke results (dfpt_qfile end-to-end, q<->-q consistency)
* Fix: drop spurious 1/nk in DFPT eps/born sums; wg already carries full BZ weight
compute_eps/compute_born divided the band sum by nk, but wg(ik,v) already
contains the full k weight wk times the spin factor 2, so the stored-k sum
is itself the BZ average. The extra 1/nk was a no-op for Gamma-only runs
(nk=1) and scaled down multi-k results by 1/nk.
Validation (Si diamond, LDA pz): 4x4x4 sym1 (8 IBZ k) eps_inf diagonal mean
= 12.6661; sym0 full-BZ 36 k manual sum = 12.6662 (5-digit cross-mesh
agreement; LDA reference ~12.7-13.2, experiment 11.7). Retained the
env-gated DFPT_Q0DBG p-matrix dump used for the parity-selection-rule audit.
Also documents in PLAN: wfc txt writer G-block (igl2isz FFT-stick order) vs
coefficient order (psi-ig) mismatch that invalidates file-based element-level
cross-checks, and the O_h parity selection-rule evidence that the in-code
p matrices are correct.
* Docs: record continuation plan (P0-1 uncommitted-fix intake, P0-2 Zstar bug, P0-3 B0 closeout, B2-B4, cleanup, A)
* Fix: gate same-atom d2V_ext on 2q reciprocal; drop spurious ion_ion delta/3
Physics (intake of the uncommitted 5-file fix, part 1 of 2):
- d2vloc_r: both displacement dressings e^{iqR} act on the same atom, so
the cell sum collapses to G = 2q (mod ints); the local second-order
kernel is nonzero only when 2q is reciprocal and then equals the plain
q=0 integer-G kernel. Drop the dead q_cart parameter.
- apply_d2vnl: the second-order nonlocal operator carries wavevector 2q;
build it on the q_eff = fold(2q)-shifted ball and gate the |dbeta><dbeta|
middle projector term behind an explicit include_middle switch.
- accumulate_electron: apply the 2q-reciprocal gate to the whole d2 term
(momentum-forbidden at generic q), pass q_eff/include_middle through,
and fix the ion_ion same-image self term by removing the delta/3
G=0 isotropic piece (validated element-wise against finite differences
of the erfc-split Ewald energy in a q-commensurate supercell).
- ion_ion doc comment updated to the validated closed form.
Tests (dfpt_phon_serial):
- AccumulateElectronAnalyticContraction expectation synced to the
Hermitian 2n+1 accumulation convention (commit dc82fac) and the
gated-off d2 term at generic q; extract SetupBases(k, q) helper so a
test can re-init the fixture at another (k, q).
- New AccumulateElectronD2GateOffGenericQ: row 0 stays pure cross at a
generic q (gate suppresses the forbidden term).
- New AccumulateElectronD2CommensurateQ: k = (-1/2,0,0), q = (1/2,0,0)
so 2q is reciprocal; three-component psi pins the cross term and the
full d2 kernel K_{ab}(G) = -tpiba^2 G_a G_1 Vloc(G^2) e^{-i2pi G.tau}
including the K(G_i - G_j) negative-harmonic convention.
- Zero the Psi buffers after construction (psi::Psi allocates
uninitialized memory); without this the tests read heap garbage and
become order-dependent in the shared-process serial suites.
* Debug: DFPT design-phase probes (ZDBG/BPT/NOSC/D2MID/DYNCHK/XB)
Part 2 of 2 of the uncommitted-fix intake: env-gated diagnostic probes
for the P0-2 Z* investigation and B-phase A/B debugging, all no-ops when
their env vars are unset (tracked for cleanup in
PLAN_dfpt_implementation.md probe ledger):
- DFPT_ZDBG (dfpt_q0 compute_born): per-occ-state decomposition of the
Born-charge summand (wg, energy denominator, dV matrix element,
position matrix element) to split occ-occ vs valence contributions.
- DFPT_BPT (dfpt_pw): perturbation-theory cross-check of the
Sternheimer solve, <dpsi|rhs> vs sum_m |<psi_m(k+q)|rhs>|^2/(e_m-e_n)
over the empty manifold at k+q (empty_kq_ cache added).
- DFPT_NOSC (dfpt_pw): zero the screened potential to isolate the bare
Sternheimer response.
- DFPT_D2MID / DYNCHK d2gate (dfpt_phon): disable the |dbeta><dbeta|
middle projector term; print the 2q-reciprocal gate decision.
- DFPT_XB: extend the row/column selection to the 2-atom rows 6.
Verified: MODULE_DFPT phon 9/9, q0 5/5 serial suites with probes inert.
* Docs: P0-1 done (2q-reciprocal d2 gate intake, order-dependence fix, 28/28 serial)
* DFPT q0: star-rotate the symmetry-reduced eps/Z* tensor sums
Symmetry-reduced k sums of the q=0 susceptibility tensors must be
star-averaged: the partial at a rotated star member Rk is R chi(k) R^T
(cartesian column form), with atom-resolved Born partials credited to
the image atom under the paired direct-space operation. The row-form
operator G^-1*kgmatrix*G from the kvec_d row convention had been fed to
rotate_tensor untransposed, which breaks the star sum (right- vs
left-coset representatives), so store the transpose.
Diamond Si 4x4x4 verification (sym=1): eps_inf = 12.6661*I and
Z* = 15.5799*I per atom, both bit-consistent with the symmetry-off
full-mesh reference (off-diagonals ~1e-14; previously 13.78/15.34/8.88
anisotropic). The remaining Z* offset vs the diamond target 0 is the
known missing-screening formula defect, tracked as the next P0-2 item.
Add StarRotationCyclicGroup to dfpt_q0_serial (C3 orbit cell: star size,
anisotropic trace-6 tensor averaging to 2*I, cyclic atom maps, identity
fallback) and the DFPT_STARDBG probe; build_stars/rotate_tensor/stars_
move to public for the test.
* DFPT q0: Sternheimer screened Z* (v4), QE-anchored eps 16pi fix, zstar_eu cross-check probe
- solve_pos_resp + compute_born v4: Y^a = (H-eps_v)^-1 P_c [H,x_a]|psi>
(velocity rhs, build_vkb_dk nonlocal part), Z* = zion delta -
2 sum wg Re <dpsi^kappa,scf|Y^a> (QE add_zstar_ue form); pos_resp/
dpsi_efield stashes in DFPT_PW_Data
- eps factor 2 fix: 16 pi / Omega per QE dielec.f90 (8 pi was half);
ComputeEpsTwoLevelAnalytic expectation synced, serial 6/6
- DFPT_ALEG probe: E-field SCF fixed point (solve_e form) + zstar_eu
A-leg vs zstar_ue B-leg cross-check + SCF eps + DFPT_PTCROSS bare
cross spectral diagnostic
- validated vs locally built QE 7.2 (same UPF/cell/ecut/mesh): GS energy
identical, Gamma-TO 517.5/517.6 vs 517.63 (0.03%), Z* -1.19928 vs
-1.19765 (0.14%), eps_scf 23.6825 vs 23.6685 (0.06%); 4x4x4 anomaly
(Z*=-1.2, eps~23.7 vs lit 13) shown to be shared k-mesh convergence
by QE discriminators (ONCV@4x4x4 same, pz-vbc@8x8x8 -> 14.04/-0.09)
- PLAN P0-2 closed with validation matrix and re-scoped acceptance
* DFPT q0: promote the E-field SCF solve, compute_eps to the dielec.f90 screened form
- solve_efield_resp is now production (QE solve_e order): runs after
solve_pos_resp, before the displacement solves; converged dpsi^E,a
stashed through DFPT_PW_Data (dpsi_efield)
- compute_eps consumes pos_resp + dpsi_efield:
eps = 1 - (16 pi/Omega) sum_k wg sum_occ Re<Y^a|dpsi^E,b>, star-rotated
on symmetry-reduced meshes; the PT r-matrix path is retired (pos_matrix
kept as the design-phase analytic reference for its serial tests)
- serial test ComputeEpsScfSyntheticStash replaces the PT two-level case
(prefactor, wg, occupied sum, conj/index pinning, empty-row skip); 6/6
- end-to-end sym 4x4x4: eps = 23.35 delta (was IPA 12.67), consistent
with the nosym ALEG value 23.68 and QE dielec.f90 anchor 23.67
* DFPT: build_occ_kq diagnostic detail in the commensurability error; PLAN P0-3 intake (non-Gamma-q chain defect, eps SCF promotion record)
* DFPT: fix q!=Gamma phonon frequencies (missing spin factor 2 in drho), KQ dual-reservoir completeness, term3 d2 ungating
- compute_drho: include the spin factor 2 at every q (QE incdrhoscf wgt =
2*weight/omega); the q=0 Hermitian completion now keeps Re only instead
of 2 Re. Previously the screening was half strength away from Gamma,
which collapsed the L-point Si frequencies to -948/-148/182/199 cm^-1.
After the fix: 100.49/100.49/380.41/402.11/485.93/485.93 cm^-1 vs QE
101.61x2/380.54/402.24/486.28x2 (Si NC 4x4x4, 0.1-1.1%); Gamma stays
517.491 cm^-1 (QE 517.633).
- dfpt_kq_basis: dual-reservoir G assembly so the k and k+q balls share
the same igl2ig maps (fixes silent truncation when one ball exhausts
the rho-grid reservoir).
- dfpt_phon: drop the 2q-reciprocal gate on the same-atom d2 term (it is
q-independent by construction; the old gate silently dropped it and
produced imaginary branches).
- Verification: ctest 12/12 (MODULE_CELL x4 + MODULE_DFPT x8); serial
4/4 (pert/phon/q0/rho); bare-response L run matches QE niter_ph=1 to
0.008-0.4% (-2281.83 vs -2282.01 etc.).
- No docs change: module_dfpt is design-phase, no INPUT parameter touched.
* DFPT PLAN: P0-3 non-Gamma-q defect root-caused and fixed (drho spin factor 2, a915352)
* DFPT B2: formalize the phonon output (multi-q report, LO-TO corrected frequencies, data-layer loto direction)
- DFPT_PW_Data: loto_dir_ (unit-normalized setter, isotropic (1,1,1)/sqrt(3)
default) and phon_freq_loto_ storage.
- DFPT_Phon: diagonalize_loto re-diagonalizes the Gamma matrix after
add_loto and stores signed frequencies separately (plain phon_freq(0)
stays intact); format_q_report/format_loto_report provide deterministic
fixed-precision blocks (header with direct q coordinates and the
correction direction).
- DFPT_PW::run uses data_.get_loto_dir() instead of the hardcoded
(1,1,1)/sqrt(3); new accessors get_nq/get_qvec/get_loto_dir/
get_phon_freq_loto/set_loto_dir plus the format forwarders.
- esolver run_post_process prints one block per q of the list plus the
LO-TO Gamma block when enabled; tensor blocks only print when computed.
- Serial regression: 3 new cases (direction normalization, closed-form
LO-TO spectrum {0, 13/12*pref}, char-exact format strings); phon 12/12,
ctest 12/12, all 4 DFPT serial tests pass.
- End-to-end smoke (Gamma, compute_q0+loto, 4x4x4): TO 517.490709
unchanged, LO-TO block along (0.577350 0.577350 0.577350), eps_inf
23.6825 and Z*=-1.19928d for both atoms vs QE 23.6685/-1.19765 (0.13%).
QE itself prints same-sign Z* with asr Sum=-2.395 for this setup; the
acoustic-branch lift is the faithful consequence, not a defect.
- No docs change: module_dfpt is design-phase, no INPUT parameter touched.
* DFPT B3: Kerker-preconditioned density mixing in DFPT_Rho
- DFPT_Rho::init gains mix_type (plain/kerker) and kerker_a2 (1/lat0^2);
no charge_mixing.h dependency, screen f_g = |G+q|^2/(|G+q|^2+a^2) built
with the v_hartree_q convention (gcar + q_frac*G). Screen both inputs,
plain_mix, add the screened part back: mixed = rin + beta*f*(out-rin)
(QE semantics, stored density stays physical; |G+q|=0 harmonic frozen,
consistent with its drop in compute_drho). Init signature extended with
an explicit kerker_a2 argument (no default arg; both call sites updated).
- Wiring: env DFPT_MIX_TYPE / DFPT_KERKER_A2 design-phase knobs mirroring
the DFPT_MIX_BETA precedent; default plain keeps behavior identical and
the beta=0.4 default (and its stability rationale) stays documented in
the init comment. No INPUT parameter change: no docs update required
(env knobs are internal calibration aids, same category as DFPT_MIX_BETA).
- Tests (dfpt_rho_serial, 6 -> 8): analytic first Kerker step; lambda=-2.2
stiff-shell model problem where plain beta=0.7 diverges (residual > 1)
and kerker converges (< 1e-8) to the target.
- Fixed latent breaks masked by a stale test binary since a915352:
kq0.init not updated to the 4-arg DFPT_KQ_Basis::init signature, and the
brute-force references missing the band-weight spin factor 2.
- End-to-end (L point, 4x4x4, abacus_pw_para v3.11.0-beta8): plain beta=0.7
diverges (|drho| -> 1e20); kerker beta=0.7 converges in 1393 s (vs 2332 s
plain beta=0.4); frequencies identical across plain 0.4 / kerker 0.4 /
kerker 0.7 to 8-9 digits (100.487828 x2 / 380.41385 / 402.10912 /
485.93199 x2 cm^-1).
- Verification: OMP_NUM_THREADS=1 ctest -R 'MODULE_CELL_klist_test$|
MODULE_CELL_reciprocal_grid_test|MODULE_CELL_qlist_test|
MODULE_CELL_little_group_test|MODULE_DFPT' -> 12/12; serial suites
pert 8 / phon 12 / q0 6 / rho 8 all pass; governance --staged clean
except the expected no-docs-needed WARNING recorded here.
* DFPT B4: sink the (q,irrep) SCF ledger into DFPT_PW_Data, retire the DFPT_IrrepData adapter
- DFPT_PW_Data: the write-only single-slot ledger (set_current_iter(int)/
set_converged(bool)/add_residual(double)) is replaced by the (q,irrep)-keyed
six-accessor ledger sunk from DFPT_IrrepData (std::map value members,
missing keys read as not-converged / empty history / iteration 0, clean()
drops the ledger). The irrep dimension stays as the stage-A slot: the
fallback irrep 0 carries the full 3N displacement basis. The new <map>/
<utility> includes are required by the map value members the header owns.
- DFPT_IrrepData adapter deleted (git rm): its irrep==0 forwarding of
dpsi/drho/dv duplicated the existing per-q data API, and its own keyed
maps moved to the data layer. get_dpsi_obj (static dummy, zero callers)
removed. Both CMakeLists updated, including the pw_run_test source list.
- run() outer-while accounting made honest: current_iter now increments per
pass and convergence is worst-final-displacement-residual < conv_thr
instead of an unconditional single pass. An unconverged pass re-runs the
full solve (solve_displacement restarts from a zero input), bounded by
max_iter outer passes, with the residual history keeping a record.
Behavior on converged runs is bit-identical.
- solve_displacement / solve_efield_resp: write-only inner ledger writes
removed; per-displacement state stays local and the final residual
returns to run() for aggregation.
- Tests: dfpt_irrep_data_test.cpp renamed/rewritten as dfpt_pw_data_test.cpp
(target MODULE_DFPT_pw_data_test, 5 cases: QList delegation, bound-safe
accessors with the (q,spin) signature, setter round trip, keyed-ledger
independence + clean() reset, U0 reservation).
- Verification: OMP_NUM_THREADS=1 ctest -R 'MODULE_CELL_klist_test$|
MODULE_CELL_reciprocal_grid_test|MODULE_CELL_qlist_test|
MODULE_CELL_little_group_test|MODULE_DFPT' -> 12/12 (pw_data_test fills
the retired irrep_data_test slot); serial suites pert 8 / phon 12 /
q0 6 / rho 8 all pass; end-to-end L-point default-config smoke
(abacus_pw_para v3.11.0-beta8) reproduces the reference frequencies
bit-consistently (100.487828/100.487829/380.413847/402.109158/
485.931988/485.931988 cm^-1, TOTAL 2332 s, same as the pre-B4
reference). Governance --staged: header-include warning justified by
map value members; no INPUT behavior change so no docs update required.
* DFPT: retire the B-phase validation instrumentation (net -977 lines)
- Deleted (acceptance complete): PTCHK gauge/term2/HF-channel probes and the
drho_dfpt.dat dump; the DYNCHK family (term2/d2gate/d2k/d2/ion/ele/elei and
the DYNCHK4 double-zheev comparison); MDBG binary dumps (x2); JPROBE +
JPROBE_NOXC (B3 acceptance done, delete as planned); OCCCHK incl. the
dbg_miss label analysis and the empty_kq_/empty_kq_eig_ companion storage;
XB; BPT incl. the want_empty projector expansion; NOSC; XCS/NOXC (v_sc
assembly simplified to the knob-free path); DKCHK; YCHK; D2MID
(include_middle sunk to literal true, q-independence settled); ALEG +
PTCROSS (the whole aleg_crosscheck method); STARDBG; Q0DBG. Dead
accumulators (d2sum_loc/nl, cross_k) and the now-purposeless <fstream>/
<set> includes removed with them.
- Kept: DFPT_DEBUG (SCF residual tracing + posresp tracking, the B3/B4
acceptance instrument and routine convergence diagnostics) and the B3
calibration knobs DFPT_MIX_BETA / DFPT_MIX_TYPE / DFPT_KERKER_A2
(documented in the DFPT_Rho::init comment).
- Behavior-preserving: every deleted probe was env-gated off by default;
include_middle and want_empty defaults equal the sunk values.
- Verification: OMP_NUM_THREADS=1 ctest -R 'MODULE_CELL_klist_test$|
MODULE_CELL_reciprocal_grid_test|MODULE_CELL_qlist_test|
MODULE_CELL_little_group_test|MODULE_DFPT' -> 12/12; serial suites
pert 8 / phon 12 / q0 6 / rho 8 pass; end-to-end L-point default-config
smoke (abacus_pw_para v3.11.0-beta8) reproduces the reference frequencies
bit-consistently (100.487828/100.487829/380.413847/402.109158/
485.931988/485.931988 cm^-1). Governance --staged clean except the
expected no-docs-needed WARNING (internal env probes, no INPUT change).
* delete PLAN
* Fix: adapt DFPT to the refactored Plus_U interface (compile break + U guard)
The develop-side DFT+U refactor (#7852-#7867) removed
source_lcao/module_dftu/dftu.h and the is_locale_initialized() member,
which broke every CMake build configuration of this branch at
dfpt_pw_data.cpp (all 9 CI build variants plus Test/CUDA/abacuslite
failed at the compile step; only the Makefile job passed because the
Makefile.Objects DFPT entries were absent at that merge point).
Changes:
- DFPT now consumes the PW-side Plus_U_Base (source_pw/module_pwdft/
dftu_base.h) instead of the LCAO-side Plus_U header: dftu_ member,
DFPT_PW_Data::init / DFPT_PW::init signatures and get_dftu() all use
const Plus_U_Base* (the esolver call site passes &this->dftu with an
implicit upcast). This removes the PW -> LCAO cross-layer include.
- u_active() = with_u() && is_occ_mat_initialized(): the reservation
usability now follows the occupation-matrix state of the provider.
- DFPT_PW::init rejects a wired provider explicitly (WARNING_QUIT):
the ground state supports PW-basis DFT+U now, but every DFPT U hook
(cal_docc, build_dv_u, dftu_onsite, born/docc contractions) is a
no-op U0 reservation, so running anyway would silently drop the
whole first-order U response (fail-loud, same pattern as the
metallic-sampling guard).
- test/dftu_test_support.cpp rewritten: the old static-member replicas
no longer exist; the shim now provides only the Plus_U_Base ctor/dtor
(also linked into MODULE_DFPT_pw_data_test, which constructs the
provider directly). dfpt_pw_run_test's locale test becomes a death
test pinning the WARNING_QUIT guard; the with_u/u_active contract
moved to DFPT_PW_DataTest.DftuReservationProviderUsability; the
unused dftu.h includes dropped from the phon/q0 serial tests.
Verification (GNU 8.3.1 + OpenMPI 5.0.3, GCC13 no-MPI cross-check):
- cmake --build build --target abacus_pw_para: builds/links
- cmake --build build-nompi (-DENABLE_MPI=OFF -DENABLE_LCAO=OFF)
--target abacus_pw_omp: builds/links
- ctest -R 'MODULE_DFPT|MODULE_CELL': 50/50 pass (incl. the new death
test and provider-usability case); ./build/abacus_pw_para --version
prints v3.11.0-beta8
- agent_governance_check --staged: no findings
* Fix: reduce PR global dependency budget to non-increasing
The governance checker blocks the PR while the diff's added lines carry
more GlobalV/GlobalC/PARAM references than the removed lines
(added=51, removed=22, net_delta=+29 -> CI 'Governance checks' exit 1).
30 of the added references were test-side GlobalV::ofs_running streams;
they now use local std::ofstream objects (the fixture members that
already existed), and ReciprocalGrid::print_klists prints through its
own ofs parameter instead of the global stream (its single caller
passes the same running log). The stale 'Originally GlobalV::FINAL_SCF'
comment wording is dropped. The remaining production-side references
(reciprocal_grid.cpp k-point-file echo, klist.cpp MY_RANK guards) are
line-for-line moves of the previous klist.cpp code, so the budget is
now non-increasing (net_delta = -4).
Verification: ctest -R 'MODULE_DFPT|MODULE_CELL' 50/50 pass;
abacus_pw_para relinks; agent_governance_check --base origin/develop
--head HEAD exits 0 (no BLOCK findings).
* Fix: link K_Vectors/ReciprocalGrid sources into tests broken by the ReciprocalGrid refactor
The ReciprocalGrid refactor (Phase 1-3 of this PR) made K_Vectors
polymorphic: its vtable is now keyed on K_Vectors::renew and emitted in
klist.cpp, and the base vtable lives in reciprocal_grid.cpp. Twelve test
targets across estate/hsolver/stodft/io instantiate K_Vectors but never
compiled those translation units, so they fail to link after the merge
(masked until now by the earlier dftu.h compile break):
- MODULE_ESTATE_elecstate_{print,base,pw,energy}
- MODULE_PW_Sto_Hamilt_UTs
- MODULE_HSOLVER_pw
- MODULE_IO_write_bands (test_serial)
- MODULE_IO_write_eig_occ_test / write_dos_pw / print_info /
read_wf2rho_pw_test (already had klist.cpp, lacked reciprocal_grid.cpp)
- MODULE_IO_write_dmk
Mirrors the pattern already used by this PR's own klist/qlist tests:
add klist.cpp + parallel_kpoints.cpp + k_vector_utils.cpp +
reciprocal_grid.cpp to SOURCES and the symmetry lib to LIBS.
Verified: full build green except MODULE_IO_numerical_basis_test (needs
ENABLE_LCAO, unguarded on develop as well); the fixed tests pass under
ctest; remaining local failures are environment artifacts (ScaLAPACK
abort-stub, ELPA off).
* Fix: link K_Vectors/ReciprocalGrid sources into LCAO-side tests and add new DFPT objects to Makefile.Objects
The ReciprocalGrid refactor made K_Vectors polymorphic (its key function
and the base vtable now live in klist.cpp / reciprocal_grid.cpp), so any
test that instantiates K_Vectors (module_dm tests, deltaspin
spin_constrain/template_helpers via spin_constrain.cpp, and
init_dm_from_file via density_matrix_io.cpp) fails to link.
Also register the five PR-added translation units (reciprocal_grid.cpp,
little_group.cpp, read_inp_dfpt.cpp, dfpt_hamilt_shift.cpp,
dfpt_kq_basis.cpp) in source/Makefile.Objects so the Intel Makefile build
does not fail with undefined references.
* Docs: resync parameters.yaml and input-main.md with the C++ Input_Item generator
The DFPT parameter block was hand-placed at a position that differs from
the item_dfpt() registration order, so the byte-exact consistency checks
in test.yml (--generate-parameters-yaml / generate_input_main.py) fail.
Regenerate both files with the documented commands to restore sync; the
only change is the position of the DFPT category block.
* Fix: compile reciprocal_grid.cpp in deepks unit tests
The ReciprocalGrid refactor made K_Vectors derive from
ModuleCell::ReciprocalGrid, so klist.cpp.o and k_vector_utils.cpp.o now
reference ReciprocalGrid member functions and its vtable. The
deepks_unit_support object library (DEEPKS_UNIT_COMMON_SOURCES, gated
behind ENABLE_MLALGO and thus only compiled in the gnu Test CI job)
compiles klist.cpp without reciprocal_grid.cpp, failing to link all 30
MODULE_LCAO_DEEPKS_* test executables with undefined references to
ModuleCell::ReciprocalGrid::renew/Monkhorst_Pack/build_star_ops/... and
its vtable/typeinfo. Add the missing translation unit to the common
source set; the symmetry library (incl. little_group.cpp) is already on
the link line.
* Fix: use threadsafe death tests in DFPT suites to avoid fork-in-threaded-process deadlock
MODULE_DFPT_pw_run_test timed out (1700 s) in the gnu Test CI job: the
two irrep-loop tests run first execute OpenMP regions, so with the job's
OMP_NUM_THREADS=2 the process is multithreaded when the third test
(dftu-reservation EXPECT_EXIT) forks. The default fast-style child then
deadlocks on exit and the parent waits forever (reproduced locally under
OMP_NUM_THREADS=2: gtest warns 'detected 2 threads' and hangs).
Switch all three DFPT death tests to the fork+exec threadsafe style
(same pattern as module_container tensor_test). For the pw_run test also
bridge std::cout to std::cerr inside the death statement: WARNING_QUIT
prints the NOTICE block to stdout, while death tests match the child's
stderr; the old CaptureStdout+HasSubstr assertion cannot see the re-exec
child's output. Verified under OMP_NUM_THREADS=2: pw_run 3/3 in 0.3 s
(previously indefinite hang), kq_basis 5/5, pert_serial 8/8, and the
full MODULE_DFPT ctest batch 8/8.
* Refactor DFPT unit tests: consolidate ctor/dtor stubs into shared dfpt_test_mocks.cpp (mirror tmp_mocks.cpp convention); absorb dftu_test_support.cpp
* Refactor DFPT unit tests: share the cubic-cell/stru_lib fixture between pw_data and pw_run tests (dfpt_stru_fixture)
* Refactor DFPT serial tests: derive pert/rho/phon/q0 fixtures from a shared DFPTSerialBase (cell/basis/data setup, Coulomb/NC atom builders, analytic dVloc reference)
* test(dfpt): dedupe repeated analytic blocks in the phon serial test
Share the occupied-weights table, the single-plane-wave psi builder, the
analytic accumulate_electron cross term (now on top of AnalyticDVloc),
and the isotropic loto data setup (eps/Born charges + two-atom mass
table via MakeTwoAtomCell) through phon fixture helpers; the three
AccumulateElectron tests and the two loto closed-form tests keep their
reference formulas but drop the duplicated inline copies.
* fix(dfpt): correct dn sign in k+q occupied-state ball folding
The #7894 refactor moved the k+q congruence matcher into
match_commensurate_kq but inverted its dn convention
(dn = k_d(ikq) - k_d(ik) - q) while the folding in
copy_occ_state_ball still used key = G + dn from the pre-refactor
convention (dn = k_d(ik) + q - k_d(ikq)).
Plane-wave identity requires G' + k_d(ikq) == G + k_d(ik) + q,
i.e. G' = G - dn, so for every k whose k+q folds across a BZ
boundary (dn != 0) the occupied-state coefficients were attached
to plane waves shifted by 2*dn. The Sternheimer operator then
developed ~+tpiba^2*|2dn|^2 errors in <psi|T+Vnl|psi> (~4 Ry for
Si@X, 4x4x4 mesh) and the CG solve diverged, yielding NaN phonon
frequencies for every non-Gamma q (48 of 64 k-points affected at
Si@X). The Gamma path (dn == 0 always) was unaffected.
Restores the pre-refactor mapping verified by the Si L-point
reference case.
---------
Co-authored-by: Zanthoxylum <chenshengjun@localhost.localdomain>
… hacks (#7989) Five macros across three modules, each the last one in its module. The three files are independent -- no shared production class -- and all follow the accessor pattern established in #7984. ions_move_methods_test (2 macros) Ions_Move_Methods already had public get_converged() and get_update_iter(), so every read was already covered; only the writes and the two aggregated sub-optimisers needed anything. It gains set_converged(), set_update_iter(), get_etot_info(), get_bfgs() and get_bfgs_trad(), and Ions_Move_BFGS2 gains a const get_is_initialized(). The cross-object reads -- imm.bfgs.tr_min_hit, imm.bfgs.pos, imm.bfgs.inv_hess and the rest -- now chain through the Ions_Move_BFGS / BFGS_Basic accessors added in #7984, which is why that PR had to land first. Note a derived-fixture approach could not have worked here at all: C++ only lets a derived class reach a base's protected members through objects of its own type, and Ions_Move_Methods does not derive from BFGS_Basic. esolver_dp_test (2 macros) runner() needs a real DP model file, so the test seeds the computed results and checks that cal_energy() / cal_force() / cal_stress() hand them back. Those are writes as well as reads, so ESolver_DP gains four reference accessors: get_atype(), get_dp_potential(), get_dp_force(), get_dp_virial(). lj_pot_test (1 macro) before_all_runners() derives the LJ tables in three steps and the test drives each on its own. ESolver_LJ gains six const accessors -- get_search_radius(), get_lj_rcut(), get_lj_c6(), get_lj_c12(), get_en_shift(), get_lj_virial() -- and three wrappers: rcut_search_radius_for_testing(), set_c6_c12_for_testing() and cal_en_shift_for_testing(). All six reads are read-only here, hence const. No production logic changed. No assertion or expected value changed. Tree-wide count goes 28 -> 23, and source_relax, source_esolver and source_md join source_base, source_basis, source_hamilt, source_hsolver, source_lcao, source_main and source_psi at zero. What remains is source_io (11, being restructured, so left alone), source_estate (10) and one file each in source_cell and source_pw -- and #7987 / #7988 already take source_cell's and three of source_estate's. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Refactor OFDFT line-search task buffer to std::array * Use std::string throughout OFDFT line-search status handling --------- Co-authored-by: zzlinpku <2601110378@stu.pku.edu.cn>
…to zero access hacks (#7987) klist_test reached into K_Vectors through `#define private public`. It turned out that only four of the things it touches actually needed anything, and two needed nothing at all. Needed wrappers -- set() is the single production entry point and drives these in order, while the test exercises them one stage at a time because most of the KPT-file parsing paths are only reachable that way: read_kpoints_for_testing() 27 call sites renew_for_testing() 8 reduce_by_symmetry_for_testing() 2 set_kup_and_kdw_for_testing() 5 Needed nothing: - spin_mult, 37 sites (36 writes, 1 read), already has public get_spin_mult() and set_spin_mult(); the setter was added in #7980 for write_dmk_test and covers every one of them. - koffset, 4 sites. Both Monkhorst_Pack() and read_kpoints() take the offset as an argument, and the 27 read_kpoints call sites already pass a local `const double koffset[3]`. Only two setup blocks wrote the member and then handed it straight back to Monkhorst_Pack(), so those now use a local array too and the member is not touched from the test at all. The remaining 155 accesses in the file (get_nkstot, kvec_c, kvec_d, wk, kc_done, kd_done, set_both_kvec, nmp, isk, ...) were public throughout and are unchanged. I checked all six K_Vectors objects in the file -- kv, kv1 and kv_test1..4 -- and the other classes the macro covered: nothing else private is used, and pseudo.h, atom_spec.h, atom_pseudo.h and magnetism.h have no private sections at all. Worth stating for review: the four wrappers are public entry points that only the tests use, in the style of set_density_rotations_for_testing() in symm_rotation.h, and carry a comment saying production code must keep going through set(). read_kpoints() in particular is a substantial piece of behaviour with 27 tests behind it; if review would rather see it simply become part of the public interface, that is a one-line change and the wrapper can go. No production logic changed. No assertion or expected value changed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Mohan Chen <mohanchen@pku.edu.cn>
…ving elecstate_print_test's access hack (#7991) All fourteen of elecstate_print.cpp's global reads were inside one function, print_etot(), and it has exactly one production call site. print_etot() now takes `const Input_para& inp` and the derived `two_fermi` flag, inserted before the existing defaulted arguments so no new default argument is introduced. elecstate_print.cpp is now PARAM-free. esolver_ks.cpp already holds an injected inp_ and already passes *this->inp_ to ModuleIO::write_bands on the same path, so the one call site follows a pattern that is already there. The test owns an Input_para and a two_fermi bool instead of writing the private half of PARAM. Its PARAM.sys.log_file write was dead -- no source compiled by MODULE_ESTATE_elecstate_print reads it -- and is dropped. Passing the whole Input_para rather than thirteen separate flags is deliberate: print_etot's job is to report the INPUT-driven state, and thirteen parameters would be worse than the aggregate it is actually printing. No production logic changed. No assertion or expected value changed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Fix(deltaspin): propagate current_spin through LCAO operator chain HamiltLCAO::updateHk() sets current_spin on the root operator via set_current_spin(isk[ik]), but the value was never forwarded to child operators. DeltaSpin did not toggle its own current_spin, so it always saw current_spin == 0 for nspin=2 and applied the same +lambda_z coefficient to both spin channels instead of +lambda_z/-lambda_z. The constraint therefore acted as a spin-independent potential and produced wrong magnetic moments and total energies. Propagate current_spin to the next operator in OperatorLCAO::init() before processing the current node, so every node in the chain shares the spin state set by the k-point loop. Regenerate tests/03_NAO_multik/scf_deltaspin2/result.ref, whose previous values encoded the buggy result. * Fix(pw): correct nspin=2 DeltaSpin occupation output and per-atom labels cal_occupations() read the becp layout with the npol=2 stride (ib*2*nkb) and ignored the spin channel for nspin=2 (npol=1), so the projected atomic magnetization printed by print_orb_chg() was wrong for nspin=2. Index by the psi npol and store the spin-up/down occupancy in the up-up/down-down Pauli blocks so that Charge = occ[0]+occ[3] and Mag(z) = occ[0]-occ[3] print correctly; nspin=1 keeps a zero magnetization; nspin=4 keeps the interleaved spinor layout. Also build per-atom labels (Fe1, Fe2, ...) for the Total Magnetism / Magnetic force tables in print_orb_chg(), print_Mi() and print_Mag_Force(), instead of passing the per-type label vector (size ntype) to tables with nat rows. get_iat() is made const so the label helpers can read it from a const SpinConstrain reference. * Test: refresh stale DeltaSpin PW reference values The result.ref of these five PW DeltaSpin cases predates the DeltaSpin PW rework (they were last written in #7382) and no longer matched the code: 12/18 were off by 2.6/4.6 eV, while 19/21/41 differed only at the 1e-4-1e-7 eV level. Regenerate all five with the current code so the 17_DS_DFTU suite passes again. 12_PW_DS_S2_Z now agrees with the dedicated 01_PW/scf_deltaspin2 reference (-6369.19826815 eV), confirming the new value is the intended one. * Refactor(pw): pass nspin explicitly to cal_occupations The governance checker blocks PRs that increase GlobalV/GlobalC/PARAM usage. cal_occupations() read PARAM.inp.nspin twice; pass it as an explicit argument from ctrl_scf_pw() (which already holds the parsed Input_para) instead. Also keep the print_orb_chg() header line using the existing atom_label variable so the GlobalV::ofs_running line stays untouched. --------- Co-authored-by: dyzheng <zhengdy@bjaisi.com>
* module_charge: normalize indentation and brace single-statement control flow
Mechanical cleanup as the first step of the module_charge governance
refactor: convert leading tabs to 4-space indentation (1011 occurrences
across 11 files) and add braces around all single-statement if/for/while
bodies (11 sites). No functional change.
* module_charge: aggregate Charge_Mixing params into MixingConfig
Introduce a MixingConfig POD that bundles the INPUT mixing parameters
with the runtime globals (nspin, scf_thr_type, double_grid), and change
set_mixing from a 12-argument interface to set_mixing(const MixingConfig&,
double&, double&). Charge_Mixing now stores the config and reads nspin /
scf_thr_type / double_grid from it instead of PARAM.inp / PARAM.globalv,
removing the direct PARAM reads in set_mixing and init_mixing.
The single production call site (esolver_ks.cpp) fills the config, and
the unit test drives set_mixing via a make_cfg() helper. The
'#define private public' access hack is kept for now with a TODO: the
test still must write Parameter::input/sys, Charge::_space_* and
XC_Functional privates, which need the Step 4/5 global-state
parameterization before it can be removed.
Verified: make -j30 MODULE_ESTATE_charge_mixing (build_max_para_test)
passes with no errors.
* module_charge: deduplicate twobeta_mix lambdas and replace raw new with std::vector
Extract the repeated two-beta mixing functor in mix_rho_recip/mix_rho_real
into a make_twobeta_mix<T> template helper (6 lambda copies removed), and
convert all local raw new[]/delete[] buffers in charge_mixing_rho.cpp to
zero-initialized std::vector, dropping the paired ZEROS calls.
* module_charge: move residual/inner-product globals into MixingConfig
Extend MixingConfig with gamma_only_pw/domag/domag_z so mix_resid.cpp
(get_drho, get_dkin, inner_product_recip_{rho,simple,hartree,real}) no
longer reads PARAM/GlobalV; all branches now consume this->cfg_.
inner_product_recip_rho's raw pointer-array views are switched to
std::vector. Production fills the three new fields in esolver_ks, and
the test fixture gains a sync_cfg() helper to push PARAM mutations into
cfg_ for the inner-product branch tests.
* module_charge: own Charge's _space_* storage with std::vector (Step 5a)
Replace the six private raw _space_rho/_space_rho_save/_space_rhog/
_space_rhog_save/_space_kin_r/_space_kin_r_save buffers with
std::vector, so Charge's underlying contiguous storage self-manages and
the matching delete[] calls in destroy() (which relied on reading
possibly-uninitialized pointers) go away. The public rho/rhog/rho_save/
rhog_save/kin_r/kin_r_save views keep their double**/complex** shape and
still alias the vector memory via .data(), so all external consumers are
unaffected. Tests that drove _space_* directly are adapted to
resize()/.data() and drop their manual delete[] of the buffers.
* module_charge: route chgmixing_ks through its inp parameter
chgmixing_ks already takes a const Input_para& inp but still read
PARAM.inp.mixing_restart / PARAM.inp.scf_nmax from the global. Use the
inp argument instead so the function no longer reads INPUT state through
the global for these two fields. PARAM.globalv.ks_run is a runtime
per-process flag (set from band-parallel topology), not an input, so it
is intentionally left as-is rather than threading it through the
interface.
* module_charge: split Charge::init_rho into per-stage private methods
init_rho had a cyclomatic complexity of 36 from five sequential stages
(file read, atomic fallback, Thomas-Fermi tau, restart load, wfc read)
interleaved through shared read_error/read_kin_error flags. Extract the
four branches into private methods -- read_rho_from_file,
init_rho_atomic_and_tau, load_rho_from_restart, init_rho_from_wfc -- and
leave init_rho as a thin sequence of stage calls. Logic is unchanged; the
error flags are threaded through as parameters. The deepest stage
(read_rho_from_file) now sits at complexity 19, down from 36 for the
monolith. The remaining global reads inside the stages are untouched and
deferred to a later parameterization step.
* module_charge: extract Charge density math into charge_math free functions
sum_rho, cal_rho2ne and non_linear_core_correction each used Charge
members only to reach a handful of scalars (nrxx/nxyz/omega) or the
reciprocal-shell table (gg_uniq/ngg); the rest of each body is pure
numerics. Move the three bodies into a new charge_math namespace as free
functions with those values passed explicitly, and leave the Charge
members as thin forwarding wrappers so no caller outside the module
changes. The kernels are now unit-testable in isolation and no longer
coupled to Charge state. One behavior note: the pre-quit debug line that
printed sum_rho to ofs_warning is dropped so the free function stays free
of global-stream dependencies. charge_math.cpp is wired into the estate
library and the charge_test target.
* module_charge: register charge_math.o in the hand-written Makefile build
The CMake build already picks up charge_math.cpp; mirror that in
Makefile.Objects so the legacy Makefile flow links the new charge_math
kernels too. The module_charge directory is already on VPATH, so adding
charge_math.o to the object list is sufficient.
* module_charge: extract Charge::atomic_rho into charge_atomic free function
Remove Charge::atomic_rho entirely and replace all call sites with
module_charge::atomic_rho(..., rhopw), eliminating the need for a thin
wrapper on the Charge class. This decouples atomic density initialization
from Charge's state and improves charge.cpp quality score from 2 to 44.
* module_charge: forbid Charge copies and guard tau.cube write
scf_out_chg_tau aborted in Parallel_Grid::reduce on
assert(rhoin != nullptr) because the kin_r_save[is] handed to
write_vdata_palgrid was not a valid buffer. After the _space_* storage
became std::vector (ecf5084d4), a copied/moved Charge leaves its
rho/kin_r views dangling into another object's vector buffer, and a
kin_r_save never allocated (ked_flag set after allocate) stays nullptr;
both surface as a null rhoin deep inside MPI gather instead of at the
source.
Delete Charge's copy constructor/assignment so any value copy of the
vector-aliasing views fails at compile time, and check kin_r_save in
ctrl_output_fp before writing tau.cube so a missing allocation reports
a clear message instead of tripping the MPI assert.
Verification: not run locally (per user request, user compiles).
* module_base: tolerate null grid buffer when a rank owns no grid points
scf_out_chg_tau (LCAO, SCAN, out_chg=1, 4 MPI ranks) aborted in
Parallel_Grid::reduce on assert(rhoin != nullptr). Bisecting between
83eb5d0f3 (good) and ecf5084d4 (bad) isolated the regression to
ecf5084d4, which moved Charge's _space_* storage from raw new[] to
std::vector.
Root cause: with 4 ranks the FFT grid is slab-decomposed so that the
last rank owns zero real-space points (nrxx == 0, confirmed via a
temporary diagnostic printing fn/is/rank/nrxx at the reduce call site).
Before ecf5084d4, _space_rho = new double[nspin * 0] == new double[0]
returned a unique non-null pointer, so rho_save[is] was non-null and the
assert passed. After the change, an empty vector's .data() returns
nullptr, so the rank with nrxx == 0 handed a null rhoin to reduce and
tripped the assert (Debug) or fed MPI_Gatherv a null buffer (Release).
A rank with nrxx == 0 is legitimate: MPI_Gatherv is invoked with
sendcount 0 and ignores the send buffer. Relax the assert to only flag a
null buffer when nrxx != 0, and revert the now-unneeded kin_r_save guard
in ctrl_output_fp (it would have falsely aborted on the nrxx == 0 rank).
Verification: Release build (build_max_para_test), ran
cd tests/03_NAO_multik/scf_out_chg_tau &&
OMP_NUM_THREADS=1 mpirun -np 4 ../../../build_max_para_test/abacus_max_para
Result: exit 0, chg.cube and tau.cube written; numerical comparison
against chg.cube.ref/tau.cube.ref gives maxdiff 0 (chg) and 1e-14 (tau).
* module_charge: extract Charge::set_rho_core into charge_math free function
Move set_rho_core to charge_math::set_rho_core with rho_core,
rhog_core and rhopw passed explicitly instead of reading Charge
state, and call charge_math::non_linear_core_correction directly.
Remove the now-unused Charge::non_linear_core_correction wrapper,
use std::vector for the rhocg/vg scratch buffers, update the
init_scf call site, and drop the obsolete member stubs in the
elecstate unit tests.
* module_charge: vectorize Charge_Extra history arrays and forbid copies
Replace the raw new[]/delete[] displacement arrays (dis_old1, dis_old2,
dis_now) with std::vector and remove the hand-written destructor. This
fixes a read of uninitialized pot_order when an object is destroyed
before Init_CE, a memory leak when Init_CE is called repeatedly, and a
double-free risk from the implicitly generated shallow copy. The copy
constructor and copy assignment are deleted so the molecular-dynamics
trajectory history cannot be silently forked. The unit test now checks
vector sizes instead of non-null pointers.
* Rename charge_math to chg_tools and unify namespace module_charge
- Rename module_charge/charge_math.{h,cpp} to chg_tools.{h,cpp} via git mv
- Change namespace charge_math to module_charge to match charge_atomic
and chgmixing in the same directory
- Update include guard CHG_TOOLS_H and TITLE/timer labels accordingly
- Update call sites in init_scf.cpp, charge.cpp, charge_init.cpp
- Update build references in Makefile.Objects and both CMakeLists.txt
* module_charge: refactor Symmetry_rho class to free functions
Convert the stateless class Symmetry_rho into namespace module_charge
free functions and rename files for consistency:
symm_rho.{h,cpp} -> chg_symm.{h,cpp}
symm_rho_detail.h -> chg_symm_detail.h
symm_rhog.cpp -> chg_symm_detail.cpp
- 5 public functions become module_charge::symmetrize_rho / cal_rhog_symm
(2 overloads) / cal_rhog_symm_soc (2 overloads)
- 2 cross-TU helpers (psymmg/psymmg_soc) moved to module_charge::detail
via chg_symm_detail.h
- 3 internal MPI helpers moved to anonymous namespace
- Delete dead code psymm (real-space symmetrization, never called)
- Remove empty ctor/dtor and parallel_grid.h include
- Rename begin/begin_soc to cal_rhog_symm/cal_rhog_symm_soc for clarity
- Update timer/TITLE labels from "Symmetry_rho" to "module_charge"
- Migrate all 14 call sites and 1 test stub
- Remove obsolete Makefile special rule (no more name collision)
* module_charge: extract MixingConfig header and drop unused inner_product_recip_simple
Move MixingConfig from charge_mixing.h into its own mixing_config.h so
stateless residual kernels can include the config without dragging in
Charge_Mixing. Remove inner_product_recip_simple, which had no production
call sites, together with its unit test.
* module_gint: move gint_prec_ctrl from module_charge
Relocate gint_prec_ctrl.{h,cpp} and its test into module_gint, update the
include in esolver_ks_lcao.h and rewire the CMake/Makefile object lists.
* module_charge: extract mixing inner products into chg_drho free functions
Rename mix_resid.cpp to chg_drho.cpp and turn inner_product_real and
inner_product_recip_hartree into module_charge free functions declared
in chg_drho.h; inner_product_recip_rho, which is only shared with the
unit test, moves to module_charge::detail in chg_drho_detail.h.
Charge_Mixing loses the three private inner-product members and
mix_rho_recip/mix_rho_real bind the free functions through lambdas.
get_drho/get_dkin stay as members for this step.
* module_charge: hide cal_drho/cal_dkin in an anonymous namespace
Move the get_drho/get_dkin implementations into file-local cal_drho/
cal_dkin free functions with all inputs explicit; the public
Charge_Mixing methods become thin forwarding wrappers so esolver call
sites stay unchanged.
* module_gint: fix include path in test_gint_prec_ctrl after relocation
* module_charge: extract Kerker screen kernels into chg_precond free functions
Move Charge_Mixing::Kerker_screen_recip/real to module_charge namespace
as free functions in chg_precond.{h,cpp}, renaming mix_precond.cpp via
git mv. Config/grid/geometry are passed explicitly via MixingConfig,
PW_Basis*, and tpiba, eliminating the function's direct read of
PARAM.inp.nspin. Replace 8 std::bind call sites in charge_mixing_rho.cpp
with lambdas, update 2 commented-out bind sites in charge_mixing_dmr.cpp,
and rewrite 12 test call sites in charge_mixing_test.cpp to construct an
independent MixingConfig instead of poking at Charge_Mixing privates.
Drop the now-unused member function declarations from charge_mixing.h.
* module_charge: fix Makefile.Objects after mix_precond -> chg_precond rename
Update the non-CMake object list to track the renamed translation unit so
make-based builds do not reference the deleted mix_precond.o.
* module_charge: drop Charge_Mixing::get_drho/get_dkin wrappers
Expose cal_drho/cal_dkin as module_charge free functions in chg_drho.h
and let ESolver_KS call them directly with explicit arguments; add
Charge_Mixing::get_mixing_config() as a const observer for the config.
* module_charge: rename chgmixing.h/cpp to chg_routine.h/cpp
Align with the chg_<feature> naming pattern used in the same directory
(chg_drho, chg_precond, chg_symm, chg_tools). Update include guard to
CHG_ROUTINE_H, the self-include in chg_routine.cpp, the entry in
source_estate/CMakeLists.txt and source/Makefile.Objects, and the three
#include sites in esolver_ks{,_pw,_lcao}.cpp. Function names
(chgmixing_ks{,_pw,_lcao}) and TITLE/timer tags are intentionally left
unchanged to keep the diff minimal.
* module_charge: rename mixing_config.h to chg_mix_cfg.h
Rename the MixingConfig header to align with the chg_* naming
convention in module_charge. Update the include guard and the four
in-tree includers; no CMake change is needed since the header is not
listed explicitly.
* module_charge: convert Charge MPI helpers into chg_parallel free functions
Rename charge_mpi.cpp to chg_parallel.cpp and add chg_parallel.h, moving
the three stateless Charge member functions (reduce_diff_pools, rho_mpi,
kin_r_mpi) to module_charge namespace free functions that take the
Charge object explicitly. Remove their declarations from charge.h and
update all call sites in elecstate_pw, stress_mgga, read_wf2rho_pw and
sto_iter. Rename the unit test to test_chg_parallel.cpp and update the
test target name accordingly.
GlobalV/PARAM reads and the direct MPI_Allreduce in reduce_diff_pools
are preserved as pre-existing technical debt (migration-neutral).
* Rename charge_atomic files to chg_atomic
- Rename module_charge/charge_atomic.{h,cpp} to chg_atomic.{h,cpp}
- Update include guard to CHG_ATOMIC_H
- Update includes in charge_init.cpp and charge_extra.cpp
- Update source paths in CMakeLists.txt, test CMakeLists.txt
- Fix stale object names in Makefile.Objects: replace
symm_rho_charge.o/symm_rhog.o with chg_symm.o/chg_symm_detail.o
* module_charge: extract USPP double-grid split/merge into chg_uspp free functions
Introduce module_charge::split_dgrid / merge_dgrid in chg_uspp.{h,cpp} as
RAII, parameter-explicit replacements for Charge_Mixing::divide_data /
combine_data / clean_data, which paired raw new[] with manual delete[]
across ~160 lines of mixing code.
- chg_uspp.{h,cpp}: stateless free functions in module_charge namespace;
outputs are caller-pre-sized std::vector, no new/delete; parameter
validation via WARNING_QUIT; TITLE/timer tags preserved
- charge_mixing_rho.cpp: rho and tau double-grid paths switched to the new
functions; raw pointer aliases kept for !double_grid so the existing
mixing call sites (nspin==1/2/4) are untouched
- CMakeLists.txt (source + test): wire chg_uspp.cpp
The legacy divide_data/combine_data/clean_data members are not yet removed;
that follows in a later step after the test is updated.
* module_charge: rewrite MixDivCombTest for the new split_dgrid/merge_dgrid
Drop the legacy alias-pointer assertions (EXPECT_EQ(datas, data.data()),
EXPECT_EQ(datas, nullptr) after clean_data) that coupled the test to the
old new[]/delete[] ownership model.
The rewritten case verifies the actual contract:
- split_dgrid fills smooth and high-frequency buffers with the dense
data verbatim (per-element comparison)
- merge_dgrid is a left-inverse of split_dgrid (output == input)
- no explicit cleanup call is required: std::vector manages storage
Covers nspin == 1 and nspin == 2 paths.
* module_charge: drop legacy divide_data/combine_data/clean_data members
With the new module_charge::split_dgrid/merge_dgrid in chg_uspp.{h,cpp}
and all call sites in charge_mixing_rho.cpp migrated, the original
Charge_Mixing::divide_data / combine_data / clean_data members are dead.
- delete charge_mixing_uspp.cpp (the raw new[]/delete[] implementation)
- drop the three member declarations from charge_mixing.h
- remove charge_mixing_uspp.cpp from source/test CMakeLists.txt
- Makefile.Objects: drop charge_mixing_uspp.o, add chg_uspp.o
- refresh one stale comment in charge_mixing_rho.cpp to reference
merge_dgrid instead of the removed combine_data
* module_charge: rename charge_extra files to chg_extra and move class into namespace
Rename charge_extra.h/cpp to chg_extra.h/cpp and wrap the Charge_Extra
class in the module_charge namespace, matching the rest of module_charge
(chg_atomic, chg_symm, chg_uspp). Update include guards, call sites in
esolver_fp.h and the unit test, and CMake/Makefile source lists.
* module_charge: extract DMR mixing into chg_dmr free functions
Move the DMR allocation/mixing logic out of Charge_Mixing members into
stateless module_charge functions (init_mixing_dmr, template mix_dmr
with explicit instantiation), passing the Mixing object, mixing data
and MixingConfig explicitly instead of reading PARAM. Merge the two
identical real/complex mix_dmr overloads, replace raw new[]/delete[]
of the magnetic buffers with std::vector, and de-duplicate the
two-beta mixing lambda into a file-local helper. The members stay as
thin timer-wrapped wrappers so external call sites are unchanged.
* module_charge: remove Charge_Mixing DMR wrappers, call chg_dmr directly
Delete charge_mixing_dmr.cpp and have the two call sites
(chg_routine.cpp, esolver_ks_lcao.cpp) invoke module_charge::
init_mixing_dmr/mix_dmr directly with the Mixing object, mixing data
and MixingConfig obtained through Charge_Mixing accessors. Expose the
owned DMR mixing history via a new get_dmr_mdata() accessor and drop
the now-unneeded density_matrix.h include from charge_mixing.h.
Timers move into the free functions with module_charge labels.
Add the direct parallel_orbitals.h include to esolver_gets.h, whose
value member previously relied on the removed transitive include.
* module_charge: decouple chg_dmr kernel from HContainer, mix raw buffers
Change module_charge::mix_dmr to take per-spin raw contiguous double
buffers and nnr instead of HContainer/DMR container references, and
drop the hcontainer.h include (and its atom_pair/parallel_orbitals
dependency chain) from chg_dmr.cpp. The sole call site in
esolver_ks_lcao.cpp now extracts the wrappers and saved buffers from
the DensityMatrix containers before calling the kernel. Move the
argument checks into a file-local check_dmr_inputs helper. The kernel
now depends only on the mixing module and MixingConfig.
* module_charge: refactor charge_mixing_rho free functions and cleanup
- Replace 17 PARAM.inp/globalv direct reads with cfg_ fields
- Unify mixing_tau: remove redundant member, use cfg_.mixing_tau
- Extract make_twobeta_mix as free function template in anonymous namespace
- Extract mix_tau_recip free function for kinetic energy density mixing
- Extract pack_rho_mag/unpack_rho_mag templates for nspin==2 dedup
- Hoist screen and inner_product lambdas before if-else chains (8+4 dups)
- Remove dead new_e_iteration member and its no-op if block
- Drop unused parameter.h include from charge_mixing_rho.cpp
* module_charge: split member functions into charge_mixing.cpp, free functions into chg_rho_detail.h
- Move mix_rho_recip/mix_rho_real/mix_rho from charge_mixing_rho.cpp to charge_mixing.cpp
- Create chg_rho_detail.h for make_twobeta_mix, pack_rho_mag, unpack_rho_mag templates and mix_tau_recip declaration
- charge_mixing_rho.cpp now only contains mix_tau_recip definition in module_charge::detail
- Restore accidentally deleted mix_uom member function
* module_charge: rename charge_{init,mixing_rho} to chg_{init,tau}, widen cube_io ofs_running to ostream
* charge_init.{cpp,h} -> chg_init.{cpp,h}: move Charge::init_rho stages
(read_rho_from_file, init_rho_atomic_and_tau, load_rho_from_restart,
init_rho_from_wfc) from Charge member functions to module_charge free
functions, dropping the corresponding private declarations from
charge.h. Continues the module_charge convention of stateless free
functions in chg_* files.
* charge_mixing_rho.cpp -> chg_tau.cpp: rename for the module_charge
short-underscore convention; the file only contains mix_tau_recip.
* Extract mix_tau_recip declaration from chg_rho_detail.h into a new
chg_tau.h so chg_tau.cpp no longer pulls in the detail template
helpers (make_twobeta_mix / pack_rho_mag / unpack_rho_mag).
charge_mixing.cpp adds chg_tau.h while keeping chg_rho_detail.h for
the template helpers it still uses.
* Widen ModuleIO::read_vdata_palgrid's ofs_running parameter from
std::ofstream& to std::ostream& (cube_io.h / read_cube.cpp). The
body only uses operator<<, so std::ostream& is sufficient; this
fixes the chg_init.cpp compile error where read_rho_file /
read_kin_file (per project rules, std::ostream&) could not bind to
the old std::ofstream& parameter. Existing callers passing
std::ofstream& (GlobalV::ofs_running, test fixture) convert
implicitly via base-class reference.
Build lists updated: source/Makefile.Objects and
source/source_estate/{CMakeLists.txt,test/CMakeLists.txt}.
Verification: chg_init.* changes compile-verified by user before
this session; chg_tau rename and chg_tau.h extraction not yet
compile-verified; cube_io type widening not yet compile-verified.
* module_charge: rename charge_mixing.{h,cpp} to chg_mix.{h,cpp}, test to test_chg_mix.cpp
Pure rename, no logic change. Updates include guard, 12 #include sites,
CMakeLists (source_estate + test), and Makefile.Objects. CMake target
MODULE_ESTATE_charge_mixing kept (no external references). Class name
Charge_Mixing and module_charge namespace unchanged.
* module_charge: remove duplicate doc block comments (Phase 1a)
Remove or rephrase 14 duplicate comment lines across 7 files to
eliminate all duplicate_doc_block quality-score deductions.
- chg_mix.cpp: remove 7 duplicate comments in mix_rho_real that
repeated mix_rho_recip's broyden/Kerker/magabs annotations
- chg_init.cpp: remove 2 duplicate comments in read_kin_file that
repeated read_rho_file's binary-read and ParaWorld bridge notes
- chg_symm_detail.cpp: remove 1 duplicate step comment in psymmg_soc
- charge.h: rephrase kin_r_save comment to avoid repetition
- chg_extra.h: rephrase beta comment to avoid repetition
- chg_symm.cpp: remove 1 duplicate vector-management comment
- chg_precond.cpp: remove 1 duplicate Kerker comment
* module_charge: replace auto with explicit std::function types (Phase 1b)
Replace 14 auto-keyword lambda declarations with explicit
std::function types to eliminate all auto_keyword quality-score
deductions.
- chg_mix.cpp: 10 auto -> std::function (inner_product, screen,
twobeta_mix in mix_rho_recip and mix_rho_real)
- chg_drho.cpp: 2 auto -> std::function<double()> (part_of_noncolin,
part_of_rho)
- chg_tools.cpp: 1 auto -> std::function<void(int,int)> (kernel)
- chg_symm_detail.cpp: 1 auto -> std::function (build_wspin)
Added #include <functional> to all four files.
* module_charge: wrap lines over 120 chars (Phase 1c)
Break 21 lines exceeding the 120-char limit across 7 files to
eliminate all line_too_long quality-score deductions.
- charge.cpp: 3 WARNING_QUIT/cout lines split
- chg_atomic.cpp: 5 Simpson_Integral/exp/assert lines split
- chg_drho.cpp: 2 conj-product sum lines split
- chg_init.cpp: 1 warning message string split
- chg_mix.cpp: 5 make_twobeta_mix/recip_to_real/if_scf_oscillate lines split
- chg_mix.h: 3 member declaration/comment lines shortened
- chg_symm_detail.cpp: 2 MPI_Recv lines split
* module_charge: remove default parameter from Charge::init_rho (Phase 1d)
Remove the default nullptr values from init_rho's klist and wfcpw
parameters and update the two call sites (esolver_of.cpp,
esolver_double_xc.cpp) that relied on the defaults to pass nullptr
explicitly.
* module_charge: replace raw new/delete with std::vector and unique_ptr (Phase 2a-2d)
Replace all raw new/delete allocations in 4 files with RAII
containers to eliminate raw_new_keyword and unpaired_new_delete
quality-score deductions.
- chg_tools.cpp: 1 new -> std::vector<double> (aux buffer)
- chg_extra.cpp: 4 new -> std::vector<std::vector<double>> (rho_atom
in extrapolate_charge and find_alpha_and_beta)
- chg_symm_detail.cpp: 14 new -> std::vector (rhog_piece, ig2isz,
ipsz2ipw, nstnz_start, fftixy2is, rhogtot, ig2isztot, ixyz2ipw
across reduce_to_fullrhog, rhog_piece_to_all, psymmg, psymmg_soc)
- chg_mix.{h,cpp}: 5 new + 5 unpaired -> std::unique_ptr for
mixing and mixing_highf members; destructor and init_mixing
simplified; get_mixing() returns .get()
charge.cpp (18 raw new) deferred to Phase 2e due to wider impact.
* module_charge: replace raw new/delete in Charge with vector-backed storage (Phase 2e)
Replace all 18 raw new and 10 unpaired delete in charge.cpp with
std::vector-backed storage to eliminate raw_new_keyword and
unpaired_new_delete deductions.
- charge.h: add _ptrs_rho, _ptrs_rhog, _ptrs_rho_save, _ptrs_rhog_save,
_ptrs_kin_r, _ptrs_kin_r_save (std::vector<double*> / complex*),
and _space_rho_core, _space_rhog_core (std::vector data buffers)
- charge.cpp allocate(): replace new double*[nspin] with vector resize;
rho = _ptrs_rho.data() preserves double** interface
- charge.cpp init_final_scf(): replace both outer pointer and inner
data new calls with _space_* vectors
- charge.cpp destroy(): replace delete[] with vector::clear() and
nullptr assignment
charge.cpp score: 47 -> 69, now passing the 60 threshold.
Module average: 85.0 -> 85.7, 30/33 files passing.
* module_charge: replace std::make_unique with C++11-compatible unique_ptr(new T) (fix)
std::make_unique is a C++14 feature; the repo baseline is C++11.
Replace 4 make_unique calls with std::unique_ptr<T>(new T(...)) to
eliminate the post_cpp11_feature deduction (-40).
chg_mix.cpp score: 0 -> 15, module average: 85.7 -> 86.1.
* module_charge: fix duplicate doc block in charge.cpp init_final_scf
* module_charge: aggregate chgmixing_ks parameters into ScfMixingCtx struct (Phase 3a)
Replace 14-parameter chgmixing_ks with 7-parameter version by
grouping SCF convergence thresholds and status flags into a new
ScfMixingCtx struct, and deriving nrxx from chr.rhopw->nrxx.
- chg_routine.h: define ScfMixingCtx struct (hsolver_error, scf_thr,
scf_ene_thr, converged_u, drho, oscillate_esolver, conv_esolver)
- chg_routine.cpp: unpack ctx members at function entry
- esolver_ks.cpp: pack ctx before call, unpack after
chg_routine.cpp score: 63 -> 70, too_many_parameters eliminated.
* module_charge: aggregate read_rho_file/read_kin_file parameters into ReadCfg (Phase 3b)
Replace 9-parameter read_rho_file and read_kin_file with 5-parameter
versions by grouping suffix, readin_dir, rank, ofs_running, ofs_warning
into a ReadCfg struct in the anonymous namespace.
chg_init.cpp score: 66 -> 70, too_many_parameters eliminated.
* module_charge: aggregate non_linear_core_correction parameters into NlcCtx (Phase 3c)
Replace 10-parameter non_linear_core_correction with 2-parameter
version by grouping all input data into a new NlcCtx struct.
chg_tools.cpp score: 96 -> 100, too_many_parameters eliminated.
* module_charge: split chg_mix.cpp into init and rho mixing files (Phase 4a)
Move mix_rho_recip, mix_rho_real, and mix_rho (440 lines) from
chg_mix.cpp into a new chg_mix_rho.cpp to eliminate file_too_long
deduction (-10).
- chg_mix.cpp: 727 -> 286 lines (constructor, set_mixing,
init_mixing, set_rhopw, mix_reset, if_scf_oscillate,
allocate_mixing_uom, mix_uom)
- chg_mix_rho.cpp: new file, 440 lines (mix_rho_recip,
mix_rho_real, mix_rho)
- CMakeLists.txt: add chg_mix_rho.cpp to library and test targets
chg_mix.cpp score: 15 -> 60, now passing the 60 threshold.
32/34 files passing, module average improved.
* module_charge: split chg_drho.cpp and decompose inner product functions (Phase 4b)
Move inner_product_recip_rho and inner_product_recip_hartree from
chg_drho.cpp into a new chg_drho_inner.cpp, and decompose each
into per-nspin helper functions to reduce cyclomatic complexity.
- chg_drho.cpp: 520 -> 161 lines (cal_drho, cal_dkin,
inner_product_real); score 49 -> 97
- chg_drho_inner.cpp: new file, 310 lines; score 100
- inner_product_recip_rho decomposed into recip_rho_nspin1,
recip_rho_nspin2, recip_rho_nspin4_mag helpers (CC 29 -> ~5 each)
- inner_product_recip_hartree decomposed into
recip_hartree_nspin2, recip_hartree_nspin4_trad,
recip_hartree_nspin4_angle helpers (CC 37 -> ~5 each)
- shared coulomb_sum_single extracted
- CMakeLists.txt: add chg_drho_inner.cpp to library and test targets
34/35 files passing, only chg_atomic.cpp remains below 60.
* refactor(module_charge): split atomic_rho and remove ZEROS in charge mixing
chg_atomic.cpp:
- Decompose atomic_rho (CC=60) into per-nspin helpers in
chg_atomic_inner.cpp; CC reduced to 7, score 40->100.
- Replace all PARAM.inp.nelec/domag/domag_z/test_charge and
GlobalV::ofs_warning with explicit AtomicRhoCfg parameter.
- Remove unused parameter.h include.
- Add chg_atomic_detail.h declaring detail helpers and RhoG3dCtx.
chg_init/chg_extra/esolver_*:
- Pass AtomicRhoCfg through call sites of atomic_rho,
extrapolate_charge, and update_delta_rho.
Bug fixes:
- chg_drho_inner.cpp: fix duplicate const (const MixingConfig const&
-> const MixingConfig&) and add detail:: prefix to helper calls.
- chg_mix_rho.cpp: use mixing.get()/mixing_highf.get() for unique_ptr.
- chg_tools.cpp: fix numeric -> numeric[it] in set_rho_core.
Memory safety / cleanup:
- Replace ModuleBase::GlobalFunc::ZEROS with std::fill in charge.cpp,
chg_symm_detail.cpp, chg_tools.cpp; remove redundant ZEROS calls
that precede full overwrites in chg_dmr.cpp and chg_mix_rho.cpp.
* Refactor: remove redundant Charge& overload of cal_rhog_symm_soc
The Charge& overload only forwarded chr.rho/chr.rhog to the raw-array
overload and had a single internal call site. Inline the member access
at that call site and drop the wrapper declaration and definition.
* module_charge: fix stale TITLE/timer labels and drop unused xc_functional.h includes
mix_tau_recip is now a free function in module_charge::detail, so update
its TITLE/timer labels from the legacy "Charge_Mixing" to "module_charge"
to match the convention of other free functions in the directory. Also
remove the unused xc_functional.h includes from chg_tau.cpp and
chg_symm_detail.cpp (label/include cleanup only, no behavior change).
* module_charge: remove redundant #ifdef __MPI guards around parallel wrappers
Parallel_Reduce::reduce_pool and Parallel_Common::bcast_double already
compile to no-op stubs when __MPI is undefined, so the outer guards add
nothing. Remove 11 such guards in chg_tools.cpp, chg_drho.cpp,
chg_drho_inner.cpp, chg_atomic_inner.cpp and chg_mix.cpp.
Guards enclosing raw MPI calls or MPI/serial dual paths are kept
(chg_parallel, chg_symm_detail, chg_routine BP_WORLD bcast, chg_extra.h).
* module_charge: decouple chg_routine from spin_constrain singleton
- forward-declare Plus_U_Base in chg_routine.h instead of including dftu_base.h
- query DeltaSpin mag_converged in ESolver_KS_PW and pass it to chgmixing_ks_pw
* module_charge: remove PARAM dependencies via explicit configuration structs
Remove the last four direct includes of parameter.h in module_charge
(chg_mix, chg_parallel, charge, chg_init) and the implicit PARAM.globalv.ks_run
read in chg_routine. INPUT values are now passed explicitly:
- MixingConfig gains scf_nmax for the drho oscillation history
- reduce_diff_pools/rho_mpi/kin_r_mpi take kpar, all_ks_run, bndpar, nspin,
out_elf from callers instead of GlobalV::KPAR/PARAM
- Charge::kin_density/allocate/check_rho/renormalize_rho/init_final_scf take
out_elf/test_charge/nelec as arguments with validation asserts
- new InitRhoCfg aggregates INPUT values for init_rho
- ScfMixingCtx gains ks_run; dm2rho takes nelec and drops its default
skip_normalize argument per governance rule 5
No behavior change: save_rho_before_sum_band now uses the member nspin
set by allocate, identical to the previously read PARAM.inp.nspin.
* module_charge: restore #ifdef __MPI guards around parallel wrapper calls
The guards removed in 7a0013848 are load-bearing for serial-built unit
tests: source_estate/test strips __MPI from test translation units via
abacus_disable_feature_definitions, but links libbase built with __MPI,
whose explicit Parallel_Reduce instantiations contain real MPI calls.
Unguarded calls in the test TUs therefore bound to MPI_Allreduce and
abort with "called before MPI_INIT", failing MODULE_ESTATE_charge_test
and MODULE_ESTATE_charge_mixing.
Restore all 11 call-site guards in chg_tools.cpp, chg_atomic_inner.cpp,
chg_drho.cpp, chg_drho_inner.cpp and chg_mix.cpp. No behavior change for
MPI or serial production builds.
* Remove dead PAW compensation charge members
nhat, nhat_save in Charge and nhat_mdata in Charge_Mixing have had
no references since #6225 removed the PAW code; drop the orphaned
declarations and update the related comment.
* Refactor: remove unused Charge::prenspin member
prenspin recorded the spin-channel count read from legacy cube charge
files and drove collinear-to-noncollinear rearrangement in init_rho.
After read_rho was replaced by binary read_rhog (#5323, #5362) the
value is neither written nor read anywhere, so drop the dead member.
* Refactor: move Charge::cal_rho2ne/check_rho to module_charge free functions
- Add module_charge::check_rho in chg_tools.{h,cpp} with grid/geometry
parameters passed explicitly; preserve all branches, thresholds and
warning/abort messages of Charge::check_rho
- Remove the Charge::cal_rho2ne forwarding wrapper and Charge::check_rho
- Update the three esolver call sites (ks/of/double_xc) to pass rho,
nspin, rhopw grid sizes and ucell.omega explicitly
- Drop the check_rho stubs in elecstate_pw/base tests and switch
charge_test to the free cal_rho2ne
- Add test_chg_tools.cpp covering cal_rho2ne, total/spin-polarized
checks, mismatch warning path and negative-channel aborts
* Refactor: remove redundant Charge::omega_ pointer
- Charge::sum_rho() now reads the cell volume from rhopw->omega, which
is computed from the same lat0/latvec as ucell.omega and is already
dereferenced on the same line for nxyz; this also makes the volume
consistent with the grid rho lives on
- Drop the Charge::omega_ member, its set_omega() setter and the
chg_init.cpp call site, removing a raw-pointer dependency on the
UnitCell lifetime; update charge_test accordingly
Verified: MODULE_ESTATE_charge_test and MODULE_ESTATE_chg_tools pass,
elecstate library rebuilds cleanly.
* Remove dead Charge::init_final_scf and allocate_rho_final_scf
init_final_scf has had no production callers since the nscf refactor
(c6ae01236); its only remaining caller was the unit test added in
ba8b7ce9a. After the vector-backed storage refactor it was also a
broken duplicate of Charge::allocate: it never set nspin/nrxx/nxyz/
ngmc and skipped the kin_r buffers. Remove the function, its one-shot
guard flag, and the corresponding test case; destroy() now keys solely
on allocate_rho since vector storage self-manages cleanup.
* Refactor: pass rhopw explicitly to chg_init/chg_routine/chg_extra/chg_symm
Remove implicit reads of chr.rhopw/chr.ngmc from four module_charge files:
- chg_symm.cpp: size kin_g by the rho_basis used for its FFTs
- chg_routine: chgmixing_ks takes const PW_Basis&
- chg_init: orchestrator and four stage helpers take const PW_Basis&;
the Charge::init_rho member signature is unchanged
- chg_extra: extrapolate_charge/update_delta_rho take const PW_Basis&
Call sites pass *chr.rhopw at the KS boundary or *pw_rhod where the
binding (esolver_fp.cpp chr.set_rhopw(pw_rhod)) makes them identical.
Verified: affected TUs compile and MODULE_ESTATE_charge_extra passes.
* Comments: add TODOs for LCAO+USPP double-grid follow-ups
Record the smooth/dense grid split to revisit if LCAO is ever allowed
with USPP: symmetrize_rho callers pass different grids, and the
ndx/ndy/ndz input path lacks the LCAO guard the ecutrho path has.
* Refactor: replace sticky Charge::cal_elf flag with explicit symm_kin argument
cal_elf was set to true once during ELF output and never reset, so every
later density symmetrization in the same run redundantly symmetrized
kin_r. Replace the mutable workflow flag with an explicit bool parameter
on the Charge& overload of module_charge::cal_rhog_symm:
- ctrl_output_fp passes true right before write_elf consumes kin_r
- symmetrize_rho wrapper and other callers pass XC_Functional::get_ked_flag()
Verified: full incremental build, read_wf2rho unit tests (serial/4 MPI),
write_elf logic test, and tests/01_PW/scf_out_elf (E difference 5e-10 eV,
ELF cube passes CompareFile.py at 3-decimal tolerance).
* Refactor: resolve mixing_tau at config assembly, drop XC dependency from chg_mix
esolver_ks now resolves mix_cfg.mixing_tau = inp.mixing_tau &&
XC_Functional::get_ked_flag() at the single production config assembly
point, so chg_mix/chg_mix_rho no longer query the XC global inside tau
mixing branches (6 sites). test_chg_mix mirrors the resolution in
make_cfg() and sets ked_flag before set_mixing where tau mixing is
expected. Also drop an unused xc_functional.h include from
chg_drho_inner.cpp.
Verified: full incremental build clean; MODULE_ESTATE_charge_mixing
11/11 tests pass; MODULE_ESTATE_charge/chg test suites all pass
(serial + 4-rank MPI).
* Fix: restore complete types in chg_drho_inner.cpp after include removal
Removing xc_functional.h in 87b818f4c broke compilation: the include was
load-bearing transitively, supplying the complete ModulePW::PW_Basis type
and ModuleBase::TITLE. Add the direct includes instead (pw_basis.h,
global_function.h) per IWYU.
Verified: make -j16 exits 0 with full log retained (previous verification
was invalid: a tail pipe masked both the exit code and the errors).
* Refactor: derive tau symmetrization/reduction from kin_r buffer existence
The Charge& cal_rhog_symm overload and rho_mpi/kin_r_mpi queried
XC_Functional::get_ked_flag() (plus a caller-supplied out_elf/symm_kin
flag) to decide whether to touch kin_r. Since Charge::allocate allocates
kin_r exactly when meta-GGA or ELF output needs it, both now check
chr.kin_r != nullptr directly, dropping the XC dependency and the extra
boolean parameters:
- rho_mpi/kin_r_mpi lose the out_elf parameter (2 production, 3 test
call sites updated)
- the Charge& cal_rhog_symm overload loses the symm_kin parameter
(ctrl_output_fp, setup_pot, read_wf2rho, update_state_rdmft revert to
4 arguments); the raw-pointer overload now checks kin_r != nullptr
only
- module_charge keeps XC references only in charge.cpp, chg_init.cpp,
chg_drho.cpp (semantic "is meta-GGA" sites, resolved next)
Verified: make -j16 exit 0; 14/14 ctest charge/elecstate/read_wf2rho
tests (serial + 4-rank MPI); tests/01_PW/scf_out_elf integration case
reproduces the reference energy (-194.623411265 eV, diff 5e-10) and the
ELF cube passes CompareFile.py at 3-decimal tolerance.
* Refactor: remove module_xc dependency from module_charge (meta_gga state)
module_charge queried XC_Functional::get_ked_flag() at 5 semantic
"is meta-GGA" sites (tau TF init, tau file read, tau save, tau residual,
tau mixing resolution). Resolve the flag at upper layers instead:
- Charge::allocate takes an explicit meta_gga argument and stores it as
object state; save_rho_before_sum_band and cal_dkin read it
- InitRhoCfg gains a meta_gga field, filled at the 3 esolver config
assembly points (ks/of/double_xc)
- delete Charge::kin_density(); 6 esolver call sites inline
get_ked_flag() || (out_elf[0] > 0) for buffer allocation and pass
get_ked_flag() as meta_gga; non-SCF allocations pass false
- charge_test mirrors the inline expression
module_charge now has zero references to module_xc.
Verified: make -j16 exit 0 (full log); 14/14 charge/elecstate/
read_wf2rho ctests (serial + 4-rank MPI), including the mGGA tau mixing
and tau-save branches; tests/01_PW/scf_out_elf reproduces reference
energy (-194.623411265 eV, diff 5e-10) and the ELF cube passes
CompareFile.py at 3-decimal tolerance. A SCAN integration case
(205_PW_SCAN) still requires a libxc-enabled build/CI run.
* Fix: allow null rho buffers on ranks with empty real-space grid partition
pack_rho_mag/unpack_rho_mag in chg_rho_detail.h quit whenever any buffer
pointer is null. A rank may legitimately own zero real-space grid points
(nrxx == 0) when the grid is decomposed across more processes than it has
z-slabs (e.g. a 3x3x3 big-cell grid on 4 processes leaves one rank with
no slab); its zero-sized vectors then return null data() pointers even
though the packing loops perform no access. The unconditional check made
LCAO nspin==2 real-space mixing abort with "pack_rho_mag pointer is null"
on such ranks.
Restrict the null-pointer check to n > 0, matching the convention already
used by Parallel_Grid::reduce (only a null buffer with a non-zero size is
a genuine bug). n < 0 remains a hard error. Regression introduced in
d9685d4eb when the inline packing loops were extracted into these helpers.
* Refactor: move rhog_io into module_charge as chg_rhog_io
Relocate source_estate/rhog_io.{h,cpp} to source_estate/module_charge/
under the module_charge namespace, rename include guard to CHG_RHOG_IO_H,
and update the warning tags emitted at runtime. Update both callers
(chg_init.cpp, esolver_fp.cpp) and build files; adapt test_rhog_io.cpp in
place ahead of its move in a follow-up commit. No behavior change.
* Refactor: create module_charge/test with the rhog io unit test
Move test_rhog_io.cpp into module_charge/test/test_chg_rhog_io.cpp with
its support data charge-density.dat, register the new test subdirectory,
and rename the target to MODULE_CHARGE_rhog_io. Remove the migrated
AddTest block from the legacy source_estate/test/CMakeLists.txt.
* Refactor: move charge and charge-extra unit tests into module_charge/test
Rename charge_test.cpp to test_charge.cpp and charge_extra_test.cpp to
test_chg_extra.cpp per the test naming rule, move prepare_unitcell.h
alongside its only users, and register MODULE_CHARGE_charge /
MODULE_CHARGE_extra in the module_charge test CMakeLists. No test data
moves: prepare_unitcell.h only sets file-name strings at runtime, and
the extra test only writes cube files into ./support/.
* Refactor: move mix, parallel and tools unit tests into module_charge/test
Relocate test_chg_mix.cpp (fixing its relative includes), test_chg_parallel.cpp
and test_chg_tools.cpp into module_charge/test, register MODULE_CHARGE_tools /
MODULE_CHARGE_mix / MODULE_CHARGE_parallel with the 4-process mpirun test,
and drop the migrated blocks from the legacy source_estate/test CMakeLists.
* Refactor: rename module_charge test dir to unittests and wire CI for it
Rename source_estate/module_charge/test to unittests (relative CMake
paths are immune to the move). Sync the referencing points: the
add_subdirectory call, the coverage lcov filter (add '*/unittests/*' so
test sources stay excluded from the report), a dedicated Module_Charge
ctest step in test.yml with MODULE_CHARGE added to the catch-all -E
list to avoid double execution, and unittests/ added to the
code_quality_score.py SKIP_DIRS.
* Fix: pass ucell.omega to Charge::sum_rho/renormalize_rho to fix NPT stress
Root cause: commit 34b441e1c ("Refactor: remove redundant Charge::omega_
pointer") changed Charge::sum_rho() to read the cell volume from
rhopw->omega instead of ucell.omega. In variable-cell calculations (NPT),
pw_rho/pw_rhod are NOT rebuilt on cell change (only pw_wfc is), so
rhopw->omega keeps the initial cell volume while ucell.omega is updated
every MD step. The stale volume made sum_rho() return a wrong electron
count, which made renormalize_rho() scale rho by the wrong factor,
corrupting the stress (deviation ~0.002 in 095_PW_NPT) while the total
energy stayed near-correct (variational, second-order sensitive).
Fix: add an explicit omega parameter to Charge::sum_rho() and
renormalize_rho(); all call sites (init_scf, chg_routine, LCAO dm2rho path
through HSolverLCAO/dmToRho, RDMFT update_charge, OFDFT renormalize_psi)
now pass ucell.omega. This mirrors the existing check_rho(..., ucell.omega)
pattern.
Also mark three other rhopw->omega users with BUG(investigate) comments:
get_local_pp_energy, cal_delta_escf, and Makov-Payne correction. These are
pre-existing and were not changed by the refactor; they may have the same
stale-volume issue in NPT and should be investigated separately.
Bisected to 34b441e1c over the 20260916 module_charge refactor branch.
* Fix: add omega arg to remaining dm2rho call sites
Missed four LCAO_domain::dm2rho call sites in the previous commit:
- lcao_set.cpp init_chg_dm (skip_normalize=true, omega unused)
- esolver_dm2rho.cpp
- esolver_ks_lcao_tddft.cpp weight_dm_rho
- module_dm/init_dm.cpp
All now pass ucell.omega.
* Fix: restore HamiltHSMatrix hs declaration in cal_mw_from_lambda
Accidentally removed the line while editing the comment.
* Fix: close_kerker_gg0 actually disables Kerker; drop dead mixing_gg0 members
The chg_precond refactor (commit 6d127d517) made the Kerker kernels read
cfg_ (immutable INPUT snapshot) instead of Charge_Mixing members, but
close_kerker_gg0() kept writing the now-dead mixing_gg0/mixing_gg0_mag
members. As a result, the non-separate-loop EXX path in exx_lri_interface.hpp
silently failed to disable Kerker after convergence.
Fix: add a kerker_disabled_ flag on Charge_Mixing that the mix_rho_recip/
mix_rho_real screening lambdas short-circuit on. The flag lives on the
object, not in cfg_, so the immutable INPUT snapshot invariant is preserved.
Also drop the now-dead members mixing_gg0/mixing_gg0_mag/mixing_gg0_min/
mixing_angle/mixing_dmr and the get_mixing_gg0() getter; set_mixing/init_mixing
now read these from cfg_ directly. Add CloseKerkerGg0DisablesScreenReal
regression test that compares close_kerker_gg0() output against the
cfg.mixing_gg0=0 baseline and proves the flag is load-bearing.
* Fix: relax over-strict null-buffer asserts for empty grid partitions
reduce_diff_pools and Parallel_Grid::reduce_across_pools still forbade
null buffers unconditionally, contradicting the rule documented at
parallel_grid.cpp:355-360. A rank with nrxx == 0 may legitimately hold
a null rho/kin_r pointer; the MPI calls below use count 0 and ignore
the buffer. Align both call sites with the documented rule.
* Fix: relax over-strict null-buffer assert in ParaRgridWorld::reduce_across_pools
Same pattern as the previous fix: a rank with nrxx == 0 legitimately
holds a null buffer, and MPI_Allreduce with count 0 ignores it.
Align with the rule documented at parallel_grid.cpp:355-360.
* Fix: allow nnr == 0 in DMR mixing for empty MPI partitions
nnr is local to each MPI rank and may legitimately be zero when no
atom pairs survive the cutoff on that rank. The previous check
aborted DMR mixing for such distributions, whereas the historical
implementation allowed empty blocks. Relax the guard in
check_dmr_inputs() and init_mixing_dmr() to reject only negative
nnr, and require non-null DMR buffers only when nnr > 0, matching
the established nrxx == 0 convention in module_charge.
* Fix: split reciprocal rho copy from real-space |m| rescale in mix_rho_recip
The nspin==4 && mixing_angle>0 branch of mix_rho_recip mixed two
distinct operations in one loop bounded by npw, but rho_magabs is
sized nrxx (real-space) and the new |m| is written back by
recip2real into rho_magabs[0..nrxx-1]. Reading rho_magabs[npw+ig]
goes out of bounds once npw+ig >= nrxx (AddressSanitizer reproduces
with nrxx=125, npw=93) and the loop bound npw leaves the real-space
tail [npw, nrxx) of {mx,my,mz} unscaled. Split into two loops: the
reciprocal rho copy stays bounded by npw, the magnetization rescale
is bounded by nrxx and reads rho_magabs[ir].
* Refactor: remove unused Charge_Mixing::conserve_setting
conserve_setting() was introduced by 420f1ad00 (DeltaSpin feature
merge, 2026-06-15) but never wired up: no production caller, no
test reference, and the DeltaSpin module does not touch
Charge_Mixing. Drop the dead declaration per the project rule that
unused functions and their tests be removed.
* Refactor: drop dead Charge_Mixing::tpiba2 member
tpiba2 was declared in chg_mix.h but never assigned by set_mixing()
nor read anywhere in the module. Grep across the whole source tree
confirms all tpiba2 references are either ucell.tpiba2 (a separate
UnitCell member) or local variables in unrelated modules. The
Charge_Mixing class never computed or used its own tpiba2 pointer;
only tpiba is consumed by the stateless Kerker kernels via
mix_rho_recip/mix_rho_real. Remove the dead declaration.
* Refactor: route Charge_Mixing getters through cfg_
get_mixing_mode(), get_mixing_beta(), get_mixing_ndim() previously
returned the legacy mirror members that set_mixing() kept in sync
with cfg_ by hand. With cfg_ now treated as the immutable INPUT
snapshot, route the public getters through cfg_ directly so there
is a single source of truth for INPUT parameters. External callers
(esolver_ks_lcao, lcao_others, pw_others) are unaffected since
signatures are unchanged. The legacy members remain in place for
now; they are dropped in a later step after internal readers are
migrated.
* Refactor: init_mixing constructs Mixing from cfg_ not legacy mirrors
init_mixing() branched on this->mixing_mode and passed
this->mixing_ndim/mixing_beta to the Broyden/Pulay/Plain_Mixing
constructors. These legacy mirrors were kept in sync with cfg_
manually by set_mixing(). Route through cfg_ directly so cfg_
remains the single source of INPUT parameters. The Mixing objects
themselves still copy beta/ndim into their own members at
construction; that is a one-time snapshot and not a continuous
sync surface, so it is left untouched.
* Refactor: mix_rho_recip/mix_rho_real read mixing_beta from cfg_
Both mix_rho_recip and mix_rho_real built the twobeta_mix functor by
reading this->mixing_beta / this->mixing_beta_mag, which are legacy
mirrors that set_mixing() kept in sync with cfg_. Route the six
construction sites through cfg_.mixing_beta / cfg_.mixing_beta_mag
so cfg_ is the single source of INPUT parameters consumed by the
mixing logic. Behavior is unchanged since the mirrors and cfg_
hold identical values after set_mixing().
* Refactor: set_mixing stops mirroring cfg_ into legacy members
set_mixing() copied mixing_mode, mixing_beta, mixing_beta_mag,
mixing_ndim from cfg into legacy mirror members, then validation
and logging read from the mirrors. Now that all internal readers
(init_mixing, mix_rho_recip, mix_rho_real, getters) read from
cfg_, the mirror writes are dead work. Drop them and route
validation and log output through cfg_ directly. omega and tpiba
remain pointer members because they alias external runtime state
(cell volume, lattice constant) that changes across SCF iterations
and so do not belong in MixingConfig (an immutable INPUT snapshot).
* Refactor: drop legacy Charge_Mixing mirror members; cfg_ is single source
Drop mixing_mode, mixing_beta, mixing_beta_mag, mixing_ndim mirror
members. After the previous commits every internal reader (getters,
init_mixing, mix_rho_recip, mix_rho_real, set_mixing validation
and log output) routes through cfg_, so the mirrors are dead state
that set_mixing() no longer writes. cfg_ is now the single source
of truth for INPUT mixing parameters.
Update test_chg_mix.cpp accordingly: the two assertions that
reached directly into CMtest.mixing_beta_mag and CMtest.mixing_mode
now read CMtest.get_mixing_config().mixing_beta_mag and
CMtest.get_mixing_mode(), matching the public API used by the
other assertions in the same block. No production caller accessed
these members directly (esolver_ks_lcao, lcao_others, pw_others
all used the getters), so the change is test-only on the consumer
side.
* Refactor: drop NSDMI from MixingConfig to force explicit construction
The non-static data member initializers in MixingConfig provided
plausible-looking defaults (e.g. mixing_beta=0.8, mixing_mode=
"broyden") that silently masked forgotten fields when a new field
was added but not wired up at construction sites. With the
defaults removed, every construction site must use aggregate
initialization (or copy-assign from a fully-initialized instance),
and a missing field yields value-initialized (zero/empty) members
that are far more likely to trip a test than the old defaults.
Combined with -Wmissing-field-initializers promoted to error in
the next commits, adding a field to MixingConfig without updating
all aggregate-initialization sites becomes a compile error.
* Refactor: aggregate-init MixingConfig in esolver_ks with pragma guard
Convert the 17-line field-by-field assignment of mix_cfg into a
single aggregate initialization in declaration order. Wrap it in
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating this list
becomes a compile error rather than silently using a default.
Each initializer is annotated with the field name it corresponds
to, making the declaration-order dependency auditable at a glance.
* Refactor: aggregate-init MixingConfig in test_chg_mix with pragma guard
Convert make_cfg()'s 17-line field-by-field assignment into a
single aggregate initialization in declaration order, matching
the esolver-side change. Wrap in the same
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating the test
helper is also a compile error. Both construction sites (esolver
and test) now fail at compile time if a field is missing, closing
the maintenance gap where a new field could silently fall back to
a default value.
* Fix: fail-fast guards in Charge_Mixing and update chg_mix tests
Add validation to turn latent misuse (skipped set_rhopw/set_mixing)
into clear WARNING_QUIT errors instead of null dereference or heap
corruption:
- init_mixing rejects a null rhopw
- if_scf_oscillate checks scf_nmax > 0 and iteration range
- mix_rho validates chr/chr->rhopw and the grid pointers
Fix three chg_mix unit tests that read cfg_ before set_mixing, which
caused a SIGSEGV in SCFOscillationTest and assertion failures in the
two inner-product tests.
* test(module_charge): add unit tests for chg_uspp and chg_dmr
Add test_chg_uspp.cpp covering split_dgrid/merge_dgrid (normal split,
round-trip, nspin=1/2, empty high-frequency/smooth boundaries, and
input-validation abort paths).
Add test_chg_dmr.cpp covering init_mixing_dmr/mix_dmr (nspin=1/2/4
mixing with Plain_Mixing analytically verified, empty-partition null
buffer allowance, and input-validation abort paths).
Wire both targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_precond, chg_drho, chg_drho_inner, chg_mix_rho
- test_chg_precond.cpp: kerker_screen_recip/real (early return, nspin=1/2/4
filter, nspin=4 with mixing_angle resize, real-space matches reciprocal).
- test_chg_drho.cpp: inner_product_real, cal_drho real-space path
(nspin=1/2/4+domag_z), cal_dkin (meta_gga false/true).
- test_chg_drho_inner.cpp: inner_product_recip_rho and
inner_product_recip_hartree for nspin=1 with a single G component,
analytically verified against the Coulomb metric.
- test_chg_mix_rho.cpp: mix_rho abort paths (null chr/chr->rhopw, unset
rhopw, double_grid without rhodpw) and real-space plain mixing value.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_symm, chg_symm_detail, chg_atomic, chg_atomic_inner
- test_chg_symm.cpp: symmetrize_rho / cal_rhog_symm / cal_rhog_symm_soc
no-op paths when symm_flag == 0, for nspin=1 and nspin=4.
- test_chg_symm_detail.cpp: psymmg and psymmg_soc idempotence on a
manually built D_4 point group over a serial cubic PW_Basis.
- test_chg_atomic_inner.cpp: compute_rhoatm USPP direct-copy branch and
NCPP integrate+scale-to-zv branch (Gaussian rho_at with known analytic
integral); normalize_and_check renormalizes uniform density to nelec.
- test_chg_atomic.cpp: atomic_rho ntype==0 path (skips atom loop) and
spin_number_need==3 abort path.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add chg_tau/chg_routine/chg_init tests; drop spurious XC_Functional stubs
Fourth batch of module_charge unit tests:
- test_chg_tau.cpp: mix_tau_recip abort paths (null chr/grid/mixing, nspin<1,
double_grid without high-f mixer) and non-double-grid plain mixing value.
- test_chg_routine.cpp: chgmixing_ks_pw/lcao iter==1 restart-step setup, and
chgmixing_ks convergence branches (conv_esolver true / drho<hsolver_error
skip mix_rho).
- test_chg_init.cpp: init_rho "wfc" with null wfcpw abort, and "atomic" with
ntype==0 + meta_gga Thomas-Fermi tau initialization.
Wire all three targets into unittests/CMakeLists.txt.
Cleanup: remove the XC_Functional::func_type / ked_flag definitions from
test_chg_drho, test_chg_mix_rho, test_chg_symm, test_chg_atomic_inner,
test_chg_atomic, test_chg_tau, test_chg_routine, and test_chg_init. None of
the non-test sources compiled into these targets reference these statics
(charge.cpp, chg_*.cpp, and the linked base/cell_info/planewave_serial/
symmetry libraries are clean), so the definitions were pure dead weight.
Also drop the now-unneeded xc_functional.h include from test_chg_mix_rho.cpp
and correct the stub comments.
* test(module_charge): fix broken includes in unit tests
- include chg_atomic_detail.h instead of nonexistent chg_atomic_inner.h
in test_chg_atomic_inner.cpp; add math_integral.h for Simpson_Integral
- include chg_drho.h in test_chg_drho_inner.cpp for
module_charge::inner_product_recip_hartree
- include source_cell/magnetism.h in tests that define Magnetism stubs
(test_chg_drho, test_chg_symm, test_chg_tau, test_chg_mix_rho)
- fix nonexistent source_charge/mixing includes in test_chg_tau.cpp to
source_base/module_mixing
* test(module_charge): fix link/build issues; temporarily disable routine/init targets
- test_chg_tau: use Plain_Mixing(beta) ctor and init_mixing_data with
complex type_size (old set_mixing_beta/init_mixing no longer exist)
- test_chg_symm_detail: add Magnetism stub required by cell_info's
unitcell.cpp, matching other tests in this directory
- test_chg_routine: adapt to two-arg set_rhopw and tpiba from ucell
- disable MODULE_CHARGE_routine and MODULE_CHARGE_init targets with
documented reasons: their transitive dependencies (Plus_U_Base,
elecstate, source_io) are deeply coupled; to be resolved later
* fix bug
* format tool_quit
* remove a test due to WARNING_QUIT funcitno
* delete the support file charge-density.dat because unittests never need it
---------
Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
Co-authored-by: Xiaoyang Zhang <tsfxwbbzxy@163.com>
Redo of the work in #7988 and #7990, both of which were closed while the charge density module was being restructured. That restructuring landed in #7972 and already did most of the decoupling those PRs proposed: allocate(), renormalize_rho() and sum_rho() now take their inputs explicitly, the mixing parameters are aggregated in a MixingConfig, and chg_mix.cpp / chg_drho.cpp / charge.cpp are free of global parameter reads. What was left was the test-side access. Production changes are additive only - no existing signature moves and no line is deleted from any production header: Charge::get_allocate_rho() - report whether allocate() has run Charge_Mixing::get_rho_mdata() - mirror the existing get_dmr_mdata() Charge_Mixing::get_tau_mdata() Charge_Mixing::set_mixing_config() - pair for the existing getter, for callers that must update the snapshot without rebuilding the mixing history XC_Functional::set_func_type() - pair for get_func_type() XC_Functional::set_ked_flag() - pair for get_ked_flag() Test changes: test_dm_r_init - two sites move to the already public get_DMR_save() test_charge - the global parameter scratchpad becomes fixture state (32 refs -> 0); PW_Basis setup goes through the public initgrids/initparameters/setuptransform sequence instead of the protected distribute_r()/distribute_g() test_chg_mix - the scratchpad becomes a fixture-owned MixingConfig (163 refs -> 0); the three blocks that hand-wired Charge::_space_* now take their buffers from the fixture, which owns them as vectors and points the public rho/rhog/kin_r views at them with the same stride No expected value or tolerance was changed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Fix: Correct FFTW version detection and NCCL build summary * Read FFTW version directly from adjacent pkg-config metadata
* Remove unneeded README20260902 file
* Fix build without LibRI: split BvK utils out of ri_util.h
module_lr is built whenever ENABLE_LCAO is on, but lr_io_krlist.cpp
unconditionally included module_ri/ri_util.h, which pulls in LibRI
headers (<RI/global/Array_Operator.h>) and fails to compile when
ENABLE_LIBRI is off (regression from #7849).
Move the LibRI-free Born-von Karmen helpers (get_Born_vonKarmen_period,
get_Born_von_Karmen_cells) into a new header ri_util_bvk.h; ri_util.h
now includes it, and lr_io_krlist.cpp includes only the new header.
Verified: target lr builds with ENABLE_LIBRI=OFF (build/), target ri
builds with ENABLE_LIBRI=ON (build_std_para/).
* Fix timer_enable_nvtx: define __USE_NVTX on the targets that consume it
__USE_NVTX was defined only on the final executable target, whose sole
translation unit main.cpp contains no NVTX code. The two OBJECT libraries
that actually guard NVTX calls with the macro -- base (source_base/timer.cpp)
and driver (source_main/driver.cpp) -- never saw it, so every NVTX block was
preprocessed away and timer_enable_nvtx had no effect in any CUDA build.
Move the definition onto base and driver, and link CUDA::nvToolsExt for
CUDA toolkits older than 12.9 (NVTX is header-only since 12.9).
Verified with build_pw_gpu (USE_CUDA=ON): base/driver targets compile with
NVTX symbols present in timer.cpp.o, driver.cpp.o references
timer::enable_nvtx_, and the full abacus_pw_gpu executable links
(v3.11.0-beta9).
* Refactor charge density module (#7972)
* module_charge: normalize indentation and brace single-statement control flow
Mechanical cleanup as the first step of the module_charge governance
refactor: convert leading tabs to 4-space indentation (1011 occurrences
across 11 files) and add braces around all single-statement if/for/while
bodies (11 sites). No functional change.
* module_charge: aggregate Charge_Mixing params into MixingConfig
Introduce a MixingConfig POD that bundles the INPUT mixing parameters
with the runtime globals (nspin, scf_thr_type, double_grid), and change
set_mixing from a 12-argument interface to set_mixing(const MixingConfig&,
double&, double&). Charge_Mixing now stores the config and reads nspin /
scf_thr_type / double_grid from it instead of PARAM.inp / PARAM.globalv,
removing the direct PARAM reads in set_mixing and init_mixing.
The single production call site (esolver_ks.cpp) fills the config, and
the unit test drives set_mixing via a make_cfg() helper. The
'#define private public' access hack is kept for now with a TODO: the
test still must write Parameter::input/sys, Charge::_space_* and
XC_Functional privates, which need the Step 4/5 global-state
parameterization before it can be removed.
Verified: make -j30 MODULE_ESTATE_charge_mixing (build_max_para_test)
passes with no errors.
* module_charge: deduplicate twobeta_mix lambdas and replace raw new with std::vector
Extract the repeated two-beta mixing functor in mix_rho_recip/mix_rho_real
into a make_twobeta_mix<T> template helper (6 lambda copies removed), and
convert all local raw new[]/delete[] buffers in charge_mixing_rho.cpp to
zero-initialized std::vector, dropping the paired ZEROS calls.
* module_charge: move residual/inner-product globals into MixingConfig
Extend MixingConfig with gamma_only_pw/domag/domag_z so mix_resid.cpp
(get_drho, get_dkin, inner_product_recip_{rho,simple,hartree,real}) no
longer reads PARAM/GlobalV; all branches now consume this->cfg_.
inner_product_recip_rho's raw pointer-array views are switched to
std::vector. Production fills the three new fields in esolver_ks, and
the test fixture gains a sync_cfg() helper to push PARAM mutations into
cfg_ for the inner-product branch tests.
* module_charge: own Charge's _space_* storage with std::vector (Step 5a)
Replace the six private raw _space_rho/_space_rho_save/_space_rhog/
_space_rhog_save/_space_kin_r/_space_kin_r_save buffers with
std::vector, so Charge's underlying contiguous storage self-manages and
the matching delete[] calls in destroy() (which relied on reading
possibly-uninitialized pointers) go away. The public rho/rhog/rho_save/
rhog_save/kin_r/kin_r_save views keep their double**/complex** shape and
still alias the vector memory via .data(), so all external consumers are
unaffected. Tests that drove _space_* directly are adapted to
resize()/.data() and drop their manual delete[] of the buffers.
* module_charge: route chgmixing_ks through its inp parameter
chgmixing_ks already takes a const Input_para& inp but still read
PARAM.inp.mixing_restart / PARAM.inp.scf_nmax from the global. Use the
inp argument instead so the function no longer reads INPUT state through
the global for these two fields. PARAM.globalv.ks_run is a runtime
per-process flag (set from band-parallel topology), not an input, so it
is intentionally left as-is rather than threading it through the
interface.
* module_charge: split Charge::init_rho into per-stage private methods
init_rho had a cyclomatic complexity of 36 from five sequential stages
(file read, atomic fallback, Thomas-Fermi tau, restart load, wfc read)
interleaved through shared read_error/read_kin_error flags. Extract the
four branches into private methods -- read_rho_from_file,
init_rho_atomic_and_tau, load_rho_from_restart, init_rho_from_wfc -- and
leave init_rho as a thin sequence of stage calls. Logic is unchanged; the
error flags are threaded through as parameters. The deepest stage
(read_rho_from_file) now sits at complexity 19, down from 36 for the
monolith. The remaining global reads inside the stages are untouched and
deferred to a later parameterization step.
* module_charge: extract Charge density math into charge_math free functions
sum_rho, cal_rho2ne and non_linear_core_correction each used Charge
members only to reach a handful of scalars (nrxx/nxyz/omega) or the
reciprocal-shell table (gg_uniq/ngg); the rest of each body is pure
numerics. Move the three bodies into a new charge_math namespace as free
functions with those values passed explicitly, and leave the Charge
members as thin forwarding wrappers so no caller outside the module
changes. The kernels are now unit-testable in isolation and no longer
coupled to Charge state. One behavior note: the pre-quit debug line that
printed sum_rho to ofs_warning is dropped so the free function stays free
of global-stream dependencies. charge_math.cpp is wired into the estate
library and the charge_test target.
* module_charge: register charge_math.o in the hand-written Makefile build
The CMake build already picks up charge_math.cpp; mirror that in
Makefile.Objects so the legacy Makefile flow links the new charge_math
kernels too. The module_charge directory is already on VPATH, so adding
charge_math.o to the object list is sufficient.
* module_charge: extract Charge::atomic_rho into charge_atomic free function
Remove Charge::atomic_rho entirely and replace all call sites with
module_charge::atomic_rho(..., rhopw), eliminating the need for a thin
wrapper on the Charge class. This decouples atomic density initialization
from Charge's state and improves charge.cpp quality score from 2 to 44.
* module_charge: forbid Charge copies and guard tau.cube write
scf_out_chg_tau aborted in Parallel_Grid::reduce on
assert(rhoin != nullptr) because the kin_r_save[is] handed to
write_vdata_palgrid was not a valid buffer. After the _space_* storage
became std::vector (ecf5084d4), a copied/moved Charge leaves its
rho/kin_r views dangling into another object's vector buffer, and a
kin_r_save never allocated (ked_flag set after allocate) stays nullptr;
both surface as a null rhoin deep inside MPI gather instead of at the
source.
Delete Charge's copy constructor/assignment so any value copy of the
vector-aliasing views fails at compile time, and check kin_r_save in
ctrl_output_fp before writing tau.cube so a missing allocation reports
a clear message instead of tripping the MPI assert.
Verification: not run locally (per user request, user compiles).
* module_base: tolerate null grid buffer when a rank owns no grid points
scf_out_chg_tau (LCAO, SCAN, out_chg=1, 4 MPI ranks) aborted in
Parallel_Grid::reduce on assert(rhoin != nullptr). Bisecting between
83eb5d0f3 (good) and ecf5084d4 (bad) isolated the regression to
ecf5084d4, which moved Charge's _space_* storage from raw new[] to
std::vector.
Root cause: with 4 ranks the FFT grid is slab-decomposed so that the
last rank owns zero real-space points (nrxx == 0, confirmed via a
temporary diagnostic printing fn/is/rank/nrxx at the reduce call site).
Before ecf5084d4, _space_rho = new double[nspin * 0] == new double[0]
returned a unique non-null pointer, so rho_save[is] was non-null and the
assert passed. After the change, an empty vector's .data() returns
nullptr, so the rank with nrxx == 0 handed a null rhoin to reduce and
tripped the assert (Debug) or fed MPI_Gatherv a null buffer (Release).
A rank with nrxx == 0 is legitimate: MPI_Gatherv is invoked with
sendcount 0 and ignores the send buffer. Relax the assert to only flag a
null buffer when nrxx != 0, and revert the now-unneeded kin_r_save guard
in ctrl_output_fp (it would have falsely aborted on the nrxx == 0 rank).
Verification: Release build (build_max_para_test), ran
cd tests/03_NAO_multik/scf_out_chg_tau &&
OMP_NUM_THREADS=1 mpirun -np 4 ../../../build_max_para_test/abacus_max_para
Result: exit 0, chg.cube and tau.cube written; numerical comparison
against chg.cube.ref/tau.cube.ref gives maxdiff 0 (chg) and 1e-14 (tau).
* module_charge: extract Charge::set_rho_core into charge_math free function
Move set_rho_core to charge_math::set_rho_core with rho_core,
rhog_core and rhopw passed explicitly instead of reading Charge
state, and call charge_math::non_linear_core_correction directly.
Remove the now-unused Charge::non_linear_core_correction wrapper,
use std::vector for the rhocg/vg scratch buffers, update the
init_scf call site, and drop the obsolete member stubs in the
elecstate unit tests.
* module_charge: vectorize Charge_Extra history arrays and forbid copies
Replace the raw new[]/delete[] displacement arrays (dis_old1, dis_old2,
dis_now) with std::vector and remove the hand-written destructor. This
fixes a read of uninitialized pot_order when an object is destroyed
before Init_CE, a memory leak when Init_CE is called repeatedly, and a
double-free risk from the implicitly generated shallow copy. The copy
constructor and copy assignment are deleted so the molecular-dynamics
trajectory history cannot be silently forked. The unit test now checks
vector sizes instead of non-null pointers.
* Rename charge_math to chg_tools and unify namespace module_charge
- Rename module_charge/charge_math.{h,cpp} to chg_tools.{h,cpp} via git mv
- Change namespace charge_math to module_charge to match charge_atomic
and chgmixing in the same directory
- Update include guard CHG_TOOLS_H and TITLE/timer labels accordingly
- Update call sites in init_scf.cpp, charge.cpp, charge_init.cpp
- Update build references in Makefile.Objects and both CMakeLists.txt
* module_charge: refactor Symmetry_rho class to free functions
Convert the stateless class Symmetry_rho into namespace module_charge
free functions and rename files for consistency:
symm_rho.{h,cpp} -> chg_symm.{h,cpp}
symm_rho_detail.h -> chg_symm_detail.h
symm_rhog.cpp -> chg_symm_detail.cpp
- 5 public functions become module_charge::symmetrize_rho / cal_rhog_symm
(2 overloads) / cal_rhog_symm_soc (2 overloads)
- 2 cross-TU helpers (psymmg/psymmg_soc) moved to module_charge::detail
via chg_symm_detail.h
- 3 internal MPI helpers moved to anonymous namespace
- Delete dead code psymm (real-space symmetrization, never called)
- Remove empty ctor/dtor and parallel_grid.h include
- Rename begin/begin_soc to cal_rhog_symm/cal_rhog_symm_soc for clarity
- Update timer/TITLE labels from "Symmetry_rho" to "module_charge"
- Migrate all 14 call sites and 1 test stub
- Remove obsolete Makefile special rule (no more name collision)
* module_charge: extract MixingConfig header and drop unused inner_product_recip_simple
Move MixingConfig from charge_mixing.h into its own mixing_config.h so
stateless residual kernels can include the config without dragging in
Charge_Mixing. Remove inner_product_recip_simple, which had no production
call sites, together with its unit test.
* module_gint: move gint_prec_ctrl from module_charge
Relocate gint_prec_ctrl.{h,cpp} and its test into module_gint, update the
include in esolver_ks_lcao.h and rewire the CMake/Makefile object lists.
* module_charge: extract mixing inner products into chg_drho free functions
Rename mix_resid.cpp to chg_drho.cpp and turn inner_product_real and
inner_product_recip_hartree into module_charge free functions declared
in chg_drho.h; inner_product_recip_rho, which is only shared with the
unit test, moves to module_charge::detail in chg_drho_detail.h.
Charge_Mixing loses the three private inner-product members and
mix_rho_recip/mix_rho_real bind the free functions through lambdas.
get_drho/get_dkin stay as members for this step.
* module_charge: hide cal_drho/cal_dkin in an anonymous namespace
Move the get_drho/get_dkin implementations into file-local cal_drho/
cal_dkin free functions with all inputs explicit; the public
Charge_Mixing methods become thin forwarding wrappers so esolver call
sites stay unchanged.
* module_gint: fix include path in test_gint_prec_ctrl after relocation
* module_charge: extract Kerker screen kernels into chg_precond free functions
Move Charge_Mixing::Kerker_screen_recip/real to module_charge namespace
as free functions in chg_precond.{h,cpp}, renaming mix_precond.cpp via
git mv. Config/grid/geometry are passed explicitly via MixingConfig,
PW_Basis*, and tpiba, eliminating the function's direct read of
PARAM.inp.nspin. Replace 8 std::bind call sites in charge_mixing_rho.cpp
with lambdas, update 2 commented-out bind sites in charge_mixing_dmr.cpp,
and rewrite 12 test call sites in charge_mixing_test.cpp to construct an
independent MixingConfig instead of poking at Charge_Mixing privates.
Drop the now-unused member function declarations from charge_mixing.h.
* module_charge: fix Makefile.Objects after mix_precond -> chg_precond rename
Update the non-CMake object list to track the renamed translation unit so
make-based builds do not reference the deleted mix_precond.o.
* module_charge: drop Charge_Mixing::get_drho/get_dkin wrappers
Expose cal_drho/cal_dkin as module_charge free functions in chg_drho.h
and let ESolver_KS call them directly with explicit arguments; add
Charge_Mixing::get_mixing_config() as a const observer for the config.
* module_charge: rename chgmixing.h/cpp to chg_routine.h/cpp
Align with the chg_<feature> naming pattern used in the same directory
(chg_drho, chg_precond, chg_symm, chg_tools). Update include guard to
CHG_ROUTINE_H, the self-include in chg_routine.cpp, the entry in
source_estate/CMakeLists.txt and source/Makefile.Objects, and the three
#include sites in esolver_ks{,_pw,_lcao}.cpp. Function names
(chgmixing_ks{,_pw,_lcao}) and TITLE/timer tags are intentionally left
unchanged to keep the diff minimal.
* module_charge: rename mixing_config.h to chg_mix_cfg.h
Rename the MixingConfig header to align with the chg_* naming
convention in module_charge. Update the include guard and the four
in-tree includers; no CMake change is needed since the header is not
listed explicitly.
* module_charge: convert Charge MPI helpers into chg_parallel free functions
Rename charge_mpi.cpp to chg_parallel.cpp and add chg_parallel.h, moving
the three stateless Charge member functions (reduce_diff_pools, rho_mpi,
kin_r_mpi) to module_charge namespace free functions that take the
Charge object explicitly. Remove their declarations from charge.h and
update all call sites in elecstate_pw, stress_mgga, read_wf2rho_pw and
sto_iter. Rename the unit test to test_chg_parallel.cpp and update the
test target name accordingly.
GlobalV/PARAM reads and the direct MPI_Allreduce in reduce_diff_pools
are preserved as pre-existing technical debt (migration-neutral).
* Rename charge_atomic files to chg_atomic
- Rename module_charge/charge_atomic.{h,cpp} to chg_atomic.{h,cpp}
- Update include guard to CHG_ATOMIC_H
- Update includes in charge_init.cpp and charge_extra.cpp
- Update source paths in CMakeLists.txt, test CMakeLists.txt
- Fix stale object names in Makefile.Objects: replace
symm_rho_charge.o/symm_rhog.o with chg_symm.o/chg_symm_detail.o
* module_charge: extract USPP double-grid split/merge into chg_uspp free functions
Introduce module_charge::split_dgrid / merge_dgrid in chg_uspp.{h,cpp} as
RAII, parameter-explicit replacements for Charge_Mixing::divide_data /
combine_data / clean_data, which paired raw new[] with manual delete[]
across ~160 lines of mixing code.
- chg_uspp.{h,cpp}: stateless free functions in module_charge namespace;
outputs are caller-pre-sized std::vector, no new/delete; parameter
validation via WARNING_QUIT; TITLE/timer tags preserved
- charge_mixing_rho.cpp: rho and tau double-grid paths switched to the new
functions; raw pointer aliases kept for !double_grid so the existing
mixing call sites (nspin==1/2/4) are untouched
- CMakeLists.txt (source + test): wire chg_uspp.cpp
The legacy divide_data/combine_data/clean_data members are not yet removed;
that follows in a later step after the test is updated.
* module_charge: rewrite MixDivCombTest for the new split_dgrid/merge_dgrid
Drop the legacy alias-pointer assertions (EXPECT_EQ(datas, data.data()),
EXPECT_EQ(datas, nullptr) after clean_data) that coupled the test to the
old new[]/delete[] ownership model.
The rewritten case verifies the actual contract:
- split_dgrid fills smooth and high-frequency buffers with the dense
data verbatim (per-element comparison)
- merge_dgrid is a left-inverse of split_dgrid (output == input)
- no explicit cleanup call is required: std::vector manages storage
Covers nspin == 1 and nspin == 2 paths.
* module_charge: drop legacy divide_data/combine_data/clean_data members
With the new module_charge::split_dgrid/merge_dgrid in chg_uspp.{h,cpp}
and all call sites in charge_mixing_rho.cpp migrated, the original
Charge_Mixing::divide_data / combine_data / clean_data members are dead.
- delete charge_mixing_uspp.cpp (the raw new[]/delete[] implementation)
- drop the three member declarations from charge_mixing.h
- remove charge_mixing_uspp.cpp from source/test CMakeLists.txt
- Makefile.Objects: drop charge_mixing_uspp.o, add chg_uspp.o
- refresh one stale comment in charge_mixing_rho.cpp to reference
merge_dgrid instead of the removed combine_data
* module_charge: rename charge_extra files to chg_extra and move class into namespace
Rename charge_extra.h/cpp to chg_extra.h/cpp and wrap the Charge_Extra
class in the module_charge namespace, matching the rest of module_charge
(chg_atomic, chg_symm, chg_uspp). Update include guards, call sites in
esolver_fp.h and the unit test, and CMake/Makefile source lists.
* module_charge: extract DMR mixing into chg_dmr free functions
Move the DMR allocation/mixing logic out of Charge_Mixing members into
stateless module_charge functions (init_mixing_dmr, template mix_dmr
with explicit instantiation), passing the Mixing object, mixing data
and MixingConfig explicitly instead of reading PARAM. Merge the two
identical real/complex mix_dmr overloads, replace raw new[]/delete[]
of the magnetic buffers with std::vector, and de-duplicate the
two-beta mixing lambda into a file-local helper. The members stay as
thin timer-wrapped wrappers so external call sites are unchanged.
* module_charge: remove Charge_Mixing DMR wrappers, call chg_dmr directly
Delete charge_mixing_dmr.cpp and have the two call sites
(chg_routine.cpp, esolver_ks_lcao.cpp) invoke module_charge::
init_mixing_dmr/mix_dmr directly with the Mixing object, mixing data
and MixingConfig obtained through Charge_Mixing accessors. Expose the
owned DMR mixing history via a new get_dmr_mdata() accessor and drop
the now-unneeded density_matrix.h include from charge_mixing.h.
Timers move into the free functions with module_charge labels.
Add the direct parallel_orbitals.h include to esolver_gets.h, whose
value member previously relied on the removed transitive include.
* module_charge: decouple chg_dmr kernel from HContainer, mix raw buffers
Change module_charge::mix_dmr to take per-spin raw contiguous double
buffers and nnr instead of HContainer/DMR container references, and
drop the hcontainer.h include (and its atom_pair/parallel_orbitals
dependency chain) from chg_dmr.cpp. The sole call site in
esolver_ks_lcao.cpp now extracts the wrappers and saved buffers from
the DensityMatrix containers before calling the kernel. Move the
argument checks into a file-local check_dmr_inputs helper. The kernel
now depends only on the mixing module and MixingConfig.
* module_charge: refactor charge_mixing_rho free functions and cleanup
- Replace 17 PARAM.inp/globalv direct reads with cfg_ fields
- Unify mixing_tau: remove redundant member, use cfg_.mixing_tau
- Extract make_twobeta_mix as free function template in anonymous namespace
- Extract mix_tau_recip free function for kinetic energy density mixing
- Extract pack_rho_mag/unpack_rho_mag templates for nspin==2 dedup
- Hoist screen and inner_product lambdas before if-else chains (8+4 dups)
- Remove dead new_e_iteration member and its no-op if block
- Drop unused parameter.h include from charge_mixing_rho.cpp
* module_charge: split member functions into charge_mixing.cpp, free functions into chg_rho_detail.h
- Move mix_rho_recip/mix_rho_real/mix_rho from charge_mixing_rho.cpp to charge_mixing.cpp
- Create chg_rho_detail.h for make_twobeta_mix, pack_rho_mag, unpack_rho_mag templates and mix_tau_recip declaration
- charge_mixing_rho.cpp now only contains mix_tau_recip definition in module_charge::detail
- Restore accidentally deleted mix_uom member function
* module_charge: rename charge_{init,mixing_rho} to chg_{init,tau}, widen cube_io ofs_running to ostream
* charge_init.{cpp,h} -> chg_init.{cpp,h}: move Charge::init_rho stages
(read_rho_from_file, init_rho_atomic_and_tau, load_rho_from_restart,
init_rho_from_wfc) from Charge member functions to module_charge free
functions, dropping the corresponding private declarations from
charge.h. Continues the module_charge convention of stateless free
functions in chg_* files.
* charge_mixing_rho.cpp -> chg_tau.cpp: rename for the module_charge
short-underscore convention; the file only contains mix_tau_recip.
* Extract mix_tau_recip declaration from chg_rho_detail.h into a new
chg_tau.h so chg_tau.cpp no longer pulls in the detail template
helpers (make_twobeta_mix / pack_rho_mag / unpack_rho_mag).
charge_mixing.cpp adds chg_tau.h while keeping chg_rho_detail.h for
the template helpers it still uses.
* Widen ModuleIO::read_vdata_palgrid's ofs_running parameter from
std::ofstream& to std::ostream& (cube_io.h / read_cube.cpp). The
body only uses operator<<, so std::ostream& is sufficient; this
fixes the chg_init.cpp compile error where read_rho_file /
read_kin_file (per project rules, std::ostream&) could not bind to
the old std::ofstream& parameter. Existing callers passing
std::ofstream& (GlobalV::ofs_running, test fixture) convert
implicitly via base-class reference.
Build lists updated: source/Makefile.Objects and
source/source_estate/{CMakeLists.txt,test/CMakeLists.txt}.
Verification: chg_init.* changes compile-verified by user before
this session; chg_tau rename and chg_tau.h extraction not yet
compile-verified; cube_io type widening not yet compile-verified.
* module_charge: rename charge_mixing.{h,cpp} to chg_mix.{h,cpp}, test to test_chg_mix.cpp
Pure rename, no logic change. Updates include guard, 12 #include sites,
CMakeLists (source_estate + test), and Makefile.Objects. CMake target
MODULE_ESTATE_charge_mixing kept (no external references). Class name
Charge_Mixing and module_charge namespace unchanged.
* module_charge: remove duplicate doc block comments (Phase 1a)
Remove or rephrase 14 duplicate comment lines across 7 files to
eliminate all duplicate_doc_block quality-score deductions.
- chg_mix.cpp: remove 7 duplicate comments in mix_rho_real that
repeated mix_rho_recip's broyden/Kerker/magabs annotations
- chg_init.cpp: remove 2 duplicate comments in read_kin_file that
repeated read_rho_file's binary-read and ParaWorld bridge notes
- chg_symm_detail.cpp: remove 1 duplicate step comment in psymmg_soc
- charge.h: rephrase kin_r_save comment to avoid repetition
- chg_extra.h: rephrase beta comment to avoid repetition
- chg_symm.cpp: remove 1 duplicate vector-management comment
- chg_precond.cpp: remove 1 duplicate Kerker comment
* module_charge: replace auto with explicit std::function types (Phase 1b)
Replace 14 auto-keyword lambda declarations with explicit
std::function types to eliminate all auto_keyword quality-score
deductions.
- chg_mix.cpp: 10 auto -> std::function (inner_product, screen,
twobeta_mix in mix_rho_recip and mix_rho_real)
- chg_drho.cpp: 2 auto -> std::function<double()> (part_of_noncolin,
part_of_rho)
- chg_tools.cpp: 1 auto -> std::function<void(int,int)> (kernel)
- chg_symm_detail.cpp: 1 auto -> std::function (build_wspin)
Added #include <functional> to all four files.
* module_charge: wrap lines over 120 chars (Phase 1c)
Break 21 lines exceeding the 120-char limit across 7 files to
eliminate all line_too_long quality-score deductions.
- charge.cpp: 3 WARNING_QUIT/cout lines split
- chg_atomic.cpp: 5 Simpson_Integral/exp/assert lines split
- chg_drho.cpp: 2 conj-product sum lines split
- chg_init.cpp: 1 warning message string split
- chg_mix.cpp: 5 make_twobeta_mix/recip_to_real/if_scf_oscillate lines split
- chg_mix.h: 3 member declaration/comment lines shortened
- chg_symm_detail.cpp: 2 MPI_Recv lines split
* module_charge: remove default parameter from Charge::init_rho (Phase 1d)
Remove the default nullptr values from init_rho's klist and wfcpw
parameters and update the two call sites (esolver_of.cpp,
esolver_double_xc.cpp) that relied on the defaults to pass nullptr
explicitly.
* module_charge: replace raw new/delete with std::vector and unique_ptr (Phase 2a-2d)
Replace all raw new/delete allocations in 4 files with RAII
containers to eliminate raw_new_keyword and unpaired_new_delete
quality-score deductions.
- chg_tools.cpp: 1 new -> std::vector<double> (aux buffer)
- chg_extra.cpp: 4 new -> std::vector<std::vector<double>> (rho_atom
in extrapolate_charge and find_alpha_and_beta)
- chg_symm_detail.cpp: 14 new -> std::vector (rhog_piece, ig2isz,
ipsz2ipw, nstnz_start, fftixy2is, rhogtot, ig2isztot, ixyz2ipw
across reduce_to_fullrhog, rhog_piece_to_all, psymmg, psymmg_soc)
- chg_mix.{h,cpp}: 5 new + 5 unpaired -> std::unique_ptr for
mixing and mixing_highf members; destructor and init_mixing
simplified; get_mixing() returns .get()
charge.cpp (18 raw new) deferred to Phase 2e due to wider impact.
* module_charge: replace raw new/delete in Charge with vector-backed storage (Phase 2e)
Replace all 18 raw new and 10 unpaired delete in charge.cpp with
std::vector-backed storage to eliminate raw_new_keyword and
unpaired_new_delete deductions.
- charge.h: add _ptrs_rho, _ptrs_rhog, _ptrs_rho_save, _ptrs_rhog_save,
_ptrs_kin_r, _ptrs_kin_r_save (std::vector<double*> / complex*),
and _space_rho_core, _space_rhog_core (std::vector data buffers)
- charge.cpp allocate(): replace new double*[nspin] with vector resize;
rho = _ptrs_rho.data() preserves double** interface
- charge.cpp init_final_scf(): replace both outer pointer and inner
data new calls with _space_* vectors
- charge.cpp destroy(): replace delete[] with vector::clear() and
nullptr assignment
charge.cpp score: 47 -> 69, now passing the 60 threshold.
Module average: 85.0 -> 85.7, 30/33 files passing.
* module_charge: replace std::make_unique with C++11-compatible unique_ptr(new T) (fix)
std::make_unique is a C++14 feature; the repo baseline is C++11.
Replace 4 make_unique calls with std::unique_ptr<T>(new T(...)) to
eliminate the post_cpp11_feature deduction (-40).
chg_mix.cpp score: 0 -> 15, module average: 85.7 -> 86.1.
* module_charge: fix duplicate doc block in charge.cpp init_final_scf
* module_charge: aggregate chgmixing_ks parameters into ScfMixingCtx struct (Phase 3a)
Replace 14-parameter chgmixing_ks with 7-parameter version by
grouping SCF convergence thresholds and status flags into a new
ScfMixingCtx struct, and deriving nrxx from chr.rhopw->nrxx.
- chg_routine.h: define ScfMixingCtx struct (hsolver_error, scf_thr,
scf_ene_thr, converged_u, drho, oscillate_esolver, conv_esolver)
- chg_routine.cpp: unpack ctx members at function entry
- esolver_ks.cpp: pack ctx before call, unpack after
chg_routine.cpp score: 63 -> 70, too_many_parameters eliminated.
* module_charge: aggregate read_rho_file/read_kin_file parameters into ReadCfg (Phase 3b)
Replace 9-parameter read_rho_file and read_kin_file with 5-parameter
versions by grouping suffix, readin_dir, rank, ofs_running, ofs_warning
into a ReadCfg struct in the anonymous namespace.
chg_init.cpp score: 66 -> 70, too_many_parameters eliminated.
* module_charge: aggregate non_linear_core_correction parameters into NlcCtx (Phase 3c)
Replace 10-parameter non_linear_core_correction with 2-parameter
version by grouping all input data into a new NlcCtx struct.
chg_tools.cpp score: 96 -> 100, too_many_parameters eliminated.
* module_charge: split chg_mix.cpp into init and rho mixing files (Phase 4a)
Move mix_rho_recip, mix_rho_real, and mix_rho (440 lines) from
chg_mix.cpp into a new chg_mix_rho.cpp to eliminate file_too_long
deduction (-10).
- chg_mix.cpp: 727 -> 286 lines (constructor, set_mixing,
init_mixing, set_rhopw, mix_reset, if_scf_oscillate,
allocate_mixing_uom, mix_uom)
- chg_mix_rho.cpp: new file, 440 lines (mix_rho_recip,
mix_rho_real, mix_rho)
- CMakeLists.txt: add chg_mix_rho.cpp to library and test targets
chg_mix.cpp score: 15 -> 60, now passing the 60 threshold.
32/34 files passing, module average improved.
* module_charge: split chg_drho.cpp and decompose inner product functions (Phase 4b)
Move inner_product_recip_rho and inner_product_recip_hartree from
chg_drho.cpp into a new chg_drho_inner.cpp, and decompose each
into per-nspin helper functions to reduce cyclomatic complexity.
- chg_drho.cpp: 520 -> 161 lines (cal_drho, cal_dkin,
inner_product_real); score 49 -> 97
- chg_drho_inner.cpp: new file, 310 lines; score 100
- inner_product_recip_rho decomposed into recip_rho_nspin1,
recip_rho_nspin2, recip_rho_nspin4_mag helpers (CC 29 -> ~5 each)
- inner_product_recip_hartree decomposed into
recip_hartree_nspin2, recip_hartree_nspin4_trad,
recip_hartree_nspin4_angle helpers (CC 37 -> ~5 each)
- shared coulomb_sum_single extracted
- CMakeLists.txt: add chg_drho_inner.cpp to library and test targets
34/35 files passing, only chg_atomic.cpp remains below 60.
* refactor(module_charge): split atomic_rho and remove ZEROS in charge mixing
chg_atomic.cpp:
- Decompose atomic_rho (CC=60) into per-nspin helpers in
chg_atomic_inner.cpp; CC reduced to 7, score 40->100.
- Replace all PARAM.inp.nelec/domag/domag_z/test_charge and
GlobalV::ofs_warning with explicit AtomicRhoCfg parameter.
- Remove unused parameter.h include.
- Add chg_atomic_detail.h declaring detail helpers and RhoG3dCtx.
chg_init/chg_extra/esolver_*:
- Pass AtomicRhoCfg through call sites of atomic_rho,
extrapolate_charge, and update_delta_rho.
Bug fixes:
- chg_drho_inner.cpp: fix duplicate const (const MixingConfig const&
-> const MixingConfig&) and add detail:: prefix to helper calls.
- chg_mix_rho.cpp: use mixing.get()/mixing_highf.get() for unique_ptr.
- chg_tools.cpp: fix numeric -> numeric[it] in set_rho_core.
Memory safety / cleanup:
- Replace ModuleBase::GlobalFunc::ZEROS with std::fill in charge.cpp,
chg_symm_detail.cpp, chg_tools.cpp; remove redundant ZEROS calls
that precede full overwrites in chg_dmr.cpp and chg_mix_rho.cpp.
* Refactor: remove redundant Charge& overload of cal_rhog_symm_soc
The Charge& overload only forwarded chr.rho/chr.rhog to the raw-array
overload and had a single internal call site. Inline the member access
at that call site and drop the wrapper declaration and definition.
* module_charge: fix stale TITLE/timer labels and drop unused xc_functional.h includes
mix_tau_recip is now a free function in module_charge::detail, so update
its TITLE/timer labels from the legacy "Charge_Mixing" to "module_charge"
to match the convention of other free functions in the directory. Also
remove the unused xc_functional.h includes from chg_tau.cpp and
chg_symm_detail.cpp (label/include cleanup only, no behavior change).
* module_charge: remove redundant #ifdef __MPI guards around parallel wrappers
Parallel_Reduce::reduce_pool and Parallel_Common::bcast_double already
compile to no-op stubs when __MPI is undefined, so the outer guards add
nothing. Remove 11 such guards in chg_tools.cpp, chg_drho.cpp,
chg_drho_inner.cpp, chg_atomic_inner.cpp and chg_mix.cpp.
Guards enclosing raw MPI calls or MPI/serial dual paths are kept
(chg_parallel, chg_symm_detail, chg_routine BP_WORLD bcast, chg_extra.h).
* module_charge: decouple chg_routine from spin_constrain singleton
- forward-declare Plus_U_Base in chg_routine.h instead of including dftu_base.h
- query DeltaSpin mag_converged in ESolver_KS_PW and pass it to chgmixing_ks_pw
* module_charge: remove PARAM dependencies via explicit configuration structs
Remove the last four direct includes of parameter.h in module_charge
(chg_mix, chg_parallel, charge, chg_init) and the implicit PARAM.globalv.ks_run
read in chg_routine. INPUT values are now passed explicitly:
- MixingConfig gains scf_nmax for the drho oscillation history
- reduce_diff_pools/rho_mpi/kin_r_mpi take kpar, all_ks_run, bndpar, nspin,
out_elf from callers instead of GlobalV::KPAR/PARAM
- Charge::kin_density/allocate/check_rho/renormalize_rho/init_final_scf take
out_elf/test_charge/nelec as arguments with validation asserts
- new InitRhoCfg aggregates INPUT values for init_rho
- ScfMixingCtx gains ks_run; dm2rho takes nelec and drops its default
skip_normalize argument per governance rule 5
No behavior change: save_rho_before_sum_band now uses the member nspin
set by allocate, identical to the previously read PARAM.inp.nspin.
* module_charge: restore #ifdef __MPI guards around parallel wrapper calls
The guards removed in 7a0013848 are load-bearing for serial-built unit
tests: source_estate/test strips __MPI from test translation units via
abacus_disable_feature_definitions, but links libbase built with __MPI,
whose explicit Parallel_Reduce instantiations contain real MPI calls.
Unguarded calls in the test TUs therefore bound to MPI_Allreduce and
abort with "called before MPI_INIT", failing MODULE_ESTATE_charge_test
and MODULE_ESTATE_charge_mixing.
Restore all 11 call-site guards in chg_tools.cpp, chg_atomic_inner.cpp,
chg_drho.cpp, chg_drho_inner.cpp and chg_mix.cpp. No behavior change for
MPI or serial production builds.
* Remove dead PAW compensation charge members
nhat, nhat_save in Charge and nhat_mdata in Charge_Mixing have had
no references since #6225 removed the PAW code; drop the orphaned
declarations and update the related comment.
* Refactor: remove unused Charge::prenspin member
prenspin recorded the spin-channel count read from legacy cube charge
files and drove collinear-to-noncollinear rearrangement in init_rho.
After read_rho was replaced by binary read_rhog (#5323, #5362) the
value is neither written nor read anywhere, so drop the dead member.
* Refactor: move Charge::cal_rho2ne/check_rho to module_charge free functions
- Add module_charge::check_rho in chg_tools.{h,cpp} with grid/geometry
parameters passed explicitly; preserve all branches, thresholds and
warning/abort messages of Charge::check_rho
- Remove the Charge::cal_rho2ne forwarding wrapper and Charge::check_rho
- Update the three esolver call sites (ks/of/double_xc) to pass rho,
nspin, rhopw grid sizes and ucell.omega explicitly
- Drop the check_rho stubs in elecstate_pw/base tests and switch
charge_test to the free cal_rho2ne
- Add test_chg_tools.cpp covering cal_rho2ne, total/spin-polarized
checks, mismatch warning path and negative-channel aborts
* Refactor: remove redundant Charge::omega_ pointer
- Charge::sum_rho() now reads the cell volume from rhopw->omega, which
is computed from the same lat0/latvec as ucell.omega and is already
dereferenced on the same line for nxyz; this also makes the volume
consistent with the grid rho lives on
- Drop the Charge::omega_ member, its set_omega() setter and the
chg_init.cpp call site, removing a raw-pointer dependency on the
UnitCell lifetime; update charge_test accordingly
Verified: MODULE_ESTATE_charge_test and MODULE_ESTATE_chg_tools pass,
elecstate library rebuilds cleanly.
* Remove dead Charge::init_final_scf and allocate_rho_final_scf
init_final_scf has had no production callers since the nscf refactor
(c6ae01236); its only remaining caller was the unit test added in
ba8b7ce9a. After the vector-backed storage refactor it was also a
broken duplicate of Charge::allocate: it never set nspin/nrxx/nxyz/
ngmc and skipped the kin_r buffers. Remove the function, its one-shot
guard flag, and the corresponding test case; destroy() now keys solely
on allocate_rho since vector storage self-manages cleanup.
* Refactor: pass rhopw explicitly to chg_init/chg_routine/chg_extra/chg_symm
Remove implicit reads of chr.rhopw/chr.ngmc from four module_charge files:
- chg_symm.cpp: size kin_g by the rho_basis used for its FFTs
- chg_routine: chgmixing_ks takes const PW_Basis&
- chg_init: orchestrator and four stage helpers take const PW_Basis&;
the Charge::init_rho member signature is unchanged
- chg_extra: extrapolate_charge/update_delta_rho take const PW_Basis&
Call sites pass *chr.rhopw at the KS boundary or *pw_rhod where the
binding (esolver_fp.cpp chr.set_rhopw(pw_rhod)) makes them identical.
Verified: affected TUs compile and MODULE_ESTATE_charge_extra passes.
* Comments: add TODOs for LCAO+USPP double-grid follow-ups
Record the smooth/dense grid split to revisit if LCAO is ever allowed
with USPP: symmetrize_rho callers pass different grids, and the
ndx/ndy/ndz input path lacks the LCAO guard the ecutrho path has.
* Refactor: replace sticky Charge::cal_elf flag with explicit symm_kin argument
cal_elf was set to true once during ELF output and never reset, so every
later density symmetrization in the same run redundantly symmetrized
kin_r. Replace the mutable workflow flag with an explicit bool parameter
on the Charge& overload of module_charge::cal_rhog_symm:
- ctrl_output_fp passes true right before write_elf consumes kin_r
- symmetrize_rho wrapper and other callers pass XC_Functional::get_ked_flag()
Verified: full incremental build, read_wf2rho unit tests (serial/4 MPI),
write_elf logic test, and tests/01_PW/scf_out_elf (E difference 5e-10 eV,
ELF cube passes CompareFile.py at 3-decimal tolerance).
* Refactor: resolve mixing_tau at config assembly, drop XC dependency from chg_mix
esolver_ks now resolves mix_cfg.mixing_tau = inp.mixing_tau &&
XC_Functional::get_ked_flag() at the single production config assembly
point, so chg_mix/chg_mix_rho no longer query the XC global inside tau
mixing branches (6 sites). test_chg_mix mirrors the resolution in
make_cfg() and sets ked_flag before set_mixing where tau mixing is
expected. Also drop an unused xc_functional.h include from
chg_drho_inner.cpp.
Verified: full incremental build clean; MODULE_ESTATE_charge_mixing
11/11 tests pass; MODULE_ESTATE_charge/chg test suites all pass
(serial + 4-rank MPI).
* Fix: restore complete types in chg_drho_inner.cpp after include removal
Removing xc_functional.h in 87b818f4c broke compilation: the include was
load-bearing transitively, supplying the complete ModulePW::PW_Basis type
and ModuleBase::TITLE. Add the direct includes instead (pw_basis.h,
global_function.h) per IWYU.
Verified: make -j16 exits 0 with full log retained (previous verification
was invalid: a tail pipe masked both the exit code and the errors).
* Refactor: derive tau symmetrization/reduction from kin_r buffer existence
The Charge& cal_rhog_symm overload and rho_mpi/kin_r_mpi queried
XC_Functional::get_ked_flag() (plus a caller-supplied out_elf/symm_kin
flag) to decide whether to touch kin_r. Since Charge::allocate allocates
kin_r exactly when meta-GGA or ELF output needs it, both now check
chr.kin_r != nullptr directly, dropping the XC dependency and the extra
boolean parameters:
- rho_mpi/kin_r_mpi lose the out_elf parameter (2 production, 3 test
call sites updated)
- the Charge& cal_rhog_symm overload loses the symm_kin parameter
(ctrl_output_fp, setup_pot, read_wf2rho, update_state_rdmft revert to
4 arguments); the raw-pointer overload now checks kin_r != nullptr
only
- module_charge keeps XC references only in charge.cpp, chg_init.cpp,
chg_drho.cpp (semantic "is meta-GGA" sites, resolved next)
Verified: make -j16 exit 0; 14/14 ctest charge/elecstate/read_wf2rho
tests (serial + 4-rank MPI); tests/01_PW/scf_out_elf integration case
reproduces the reference energy (-194.623411265 eV, diff 5e-10) and the
ELF cube passes CompareFile.py at 3-decimal tolerance.
* Refactor: remove module_xc dependency from module_charge (meta_gga state)
module_charge queried XC_Functional::get_ked_flag() at 5 semantic
"is meta-GGA" sites (tau TF init, tau file read, tau save, tau residual,
tau mixing resolution). Resolve the flag at upper layers instead:
- Charge::allocate takes an explicit meta_gga argument and stores it as
object state; save_rho_before_sum_band and cal_dkin read it
- InitRhoCfg gains a meta_gga field, filled at the 3 esolver config
assembly points (ks/of/double_xc)
- delete Charge::kin_density(); 6 esolver call sites inline
get_ked_flag() || (out_elf[0] > 0) for buffer allocation and pass
get_ked_flag() as meta_gga; non-SCF allocations pass false
- charge_test mirrors the inline expression
module_charge now has zero references to module_xc.
Verified: make -j16 exit 0 (full log); 14/14 charge/elecstate/
read_wf2rho ctests (serial + 4-rank MPI), including the mGGA tau mixing
and tau-save branches; tests/01_PW/scf_out_elf reproduces reference
energy (-194.623411265 eV, diff 5e-10) and the ELF cube passes
CompareFile.py at 3-decimal tolerance. A SCAN integration case
(205_PW_SCAN) still requires a libxc-enabled build/CI run.
* Fix: allow null rho buffers on ranks with empty real-space grid partition
pack_rho_mag/unpack_rho_mag in chg_rho_detail.h quit whenever any buffer
pointer is null. A rank may legitimately own zero real-space grid points
(nrxx == 0) when the grid is decomposed across more processes than it has
z-slabs (e.g. a 3x3x3 big-cell grid on 4 processes leaves one rank with
no slab); its zero-sized vectors then return null data() pointers even
though the packing loops perform no access. The unconditional check made
LCAO nspin==2 real-space mixing abort with "pack_rho_mag pointer is null"
on such ranks.
Restrict the null-pointer check to n > 0, matching the convention already
used by Parallel_Grid::reduce (only a null buffer with a non-zero size is
a genuine bug). n < 0 remains a hard error. Regression introduced in
d9685d4eb when the inline packing loops were extracted into these helpers.
* Refactor: move rhog_io into module_charge as chg_rhog_io
Relocate source_estate/rhog_io.{h,cpp} to source_estate/module_charge/
under the module_charge namespace, rename include guard to CHG_RHOG_IO_H,
and update the warning tags emitted at runtime. Update both callers
(chg_init.cpp, esolver_fp.cpp) and build files; adapt test_rhog_io.cpp in
place ahead of its move in a follow-up commit. No behavior change.
* Refactor: create module_charge/test with the rhog io unit test
Move test_rhog_io.cpp into module_charge/test/test_chg_rhog_io.cpp with
its support data charge-density.dat, register the new test subdirectory,
and rename the target to MODULE_CHARGE_rhog_io. Remove the migrated
AddTest block from the legacy source_estate/test/CMakeLists.txt.
* Refactor: move charge and charge-extra unit tests into module_charge/test
Rename charge_test.cpp to test_charge.cpp and charge_extra_test.cpp to
test_chg_extra.cpp per the test naming rule, move prepare_unitcell.h
alongside its only users, and register MODULE_CHARGE_charge /
MODULE_CHARGE_extra in the module_charge test CMakeLists. No test data
moves: prepare_unitcell.h only sets file-name strings at runtime, and
the extra test only writes cube files into ./support/.
* Refactor: move mix, parallel and tools unit tests into module_charge/test
Relocate test_chg_mix.cpp (fixing its relative includes), test_chg_parallel.cpp
and test_chg_tools.cpp into module_charge/test, register MODULE_CHARGE_tools /
MODULE_CHARGE_mix / MODULE_CHARGE_parallel with the 4-process mpirun test,
and drop the migrated blocks from the legacy source_estate/test CMakeLists.
* Refactor: rename module_charge test dir to unittests and wire CI for it
Rename source_estate/module_charge/test to unittests (relative CMake
paths are immune to the move). Sync the referencing points: the
add_subdirectory call, the coverage lcov filter (add '*/unittests/*' so
test sources stay excluded from the report), a dedicated Module_Charge
ctest step in test.yml with MODULE_CHARGE added to the catch-all -E
list to avoid double execution, and unittests/ added to the
code_quality_score.py SKIP_DIRS.
* Fix: pass ucell.omega to Charge::sum_rho/renormalize_rho to fix NPT stress
Root cause: commit 34b441e1c ("Refactor: remove redundant Charge::omega_
pointer") changed Charge::sum_rho() to read the cell volume from
rhopw->omega instead of ucell.omega. In variable-cell calculations (NPT),
pw_rho/pw_rhod are NOT rebuilt on cell change (only pw_wfc is), so
rhopw->omega keeps the initial cell volume while ucell.omega is updated
every MD step. The stale volume made sum_rho() return a wrong electron
count, which made renormalize_rho() scale rho by the wrong factor,
corrupting the stress (deviation ~0.002 in 095_PW_NPT) while the total
energy stayed near-correct (variational, second-order sensitive).
Fix: add an explicit omega parameter to Charge::sum_rho() and
renormalize_rho(); all call sites (init_scf, chg_routine, LCAO dm2rho path
through HSolverLCAO/dmToRho, RDMFT update_charge, OFDFT renormalize_psi)
now pass ucell.omega. This mirrors the existing check_rho(..., ucell.omega)
pattern.
Also mark three other rhopw->omega users with BUG(investigate) comments:
get_local_pp_energy, cal_delta_escf, and Makov-Payne correction. These are
pre-existing and were not changed by the refactor; they may have the same
stale-volume issue in NPT and should be investigated separately.
Bisected to 34b441e1c over the 20260916 module_charge refactor branch.
* Fix: add omega arg to remaining dm2rho call sites
Missed four LCAO_domain::dm2rho call sites in the previous commit:
- lcao_set.cpp init_chg_dm (skip_normalize=true, omega unused)
- esolver_dm2rho.cpp
- esolver_ks_lcao_tddft.cpp weight_dm_rho
- module_dm/init_dm.cpp
All now pass ucell.omega.
* Fix: restore HamiltHSMatrix hs declaration in cal_mw_from_lambda
Accidentally removed the line while editing the comment.
* Fix: close_kerker_gg0 actually disables Kerker; drop dead mixing_gg0 members
The chg_precond refactor (commit 6d127d517) made the Kerker kernels read
cfg_ (immutable INPUT snapshot) instead of Charge_Mixing members, but
close_kerker_gg0() kept writing the now-dead mixing_gg0/mixing_gg0_mag
members. As a result, the non-separate-loop EXX path in exx_lri_interface.hpp
silently failed to disable Kerker after convergence.
Fix: add a kerker_disabled_ flag on Charge_Mixing that the mix_rho_recip/
mix_rho_real screening lambdas short-circuit on. The flag lives on the
object, not in cfg_, so the immutable INPUT snapshot invariant is preserved.
Also drop the now-dead members mixing_gg0/mixing_gg0_mag/mixing_gg0_min/
mixing_angle/mixing_dmr and the get_mixing_gg0() getter; set_mixing/init_mixing
now read these from cfg_ directly. Add CloseKerkerGg0DisablesScreenReal
regression test that compares close_kerker_gg0() output against the
cfg.mixing_gg0=0 baseline and proves the flag is load-bearing.
* Fix: relax over-strict null-buffer asserts for empty grid partitions
reduce_diff_pools and Parallel_Grid::reduce_across_pools still forbade
null buffers unconditionally, contradicting the rule documented at
parallel_grid.cpp:355-360. A rank with nrxx == 0 may legitimately hold
a null rho/kin_r pointer; the MPI calls below use count 0 and ignore
the buffer. Align both call sites with the documented rule.
* Fix: relax over-strict null-buffer assert in ParaRgridWorld::reduce_across_pools
Same pattern as the previous fix: a rank with nrxx == 0 legitimately
holds a null buffer, and MPI_Allreduce with count 0 ignores it.
Align with the rule documented at parallel_grid.cpp:355-360.
* Fix: allow nnr == 0 in DMR mixing for empty MPI partitions
nnr is local to each MPI rank and may legitimately be zero when no
atom pairs survive the cutoff on that rank. The previous check
aborted DMR mixing for such distributions, whereas the historical
implementation allowed empty blocks. Relax the guard in
check_dmr_inputs() and init_mixing_dmr() to reject only negative
nnr, and require non-null DMR buffers only when nnr > 0, matching
the established nrxx == 0 convention in module_charge.
* Fix: split reciprocal rho copy from real-space |m| rescale in mix_rho_recip
The nspin==4 && mixing_angle>0 branch of mix_rho_recip mixed two
distinct operations in one loop bounded by npw, but rho_magabs is
sized nrxx (real-space) and the new |m| is written back by
recip2real into rho_magabs[0..nrxx-1]. Reading rho_magabs[npw+ig]
goes out of bounds once npw+ig >= nrxx (AddressSanitizer reproduces
with nrxx=125, npw=93) and the loop bound npw leaves the real-space
tail [npw, nrxx) of {mx,my,mz} unscaled. Split into two loops: the
reciprocal rho copy stays bounded by npw, the magnetization rescale
is bounded by nrxx and reads rho_magabs[ir].
* Refactor: remove unused Charge_Mixing::conserve_setting
conserve_setting() was introduced by 420f1ad00 (DeltaSpin feature
merge, 2026-06-15) but never wired up: no production caller, no
test reference, and the DeltaSpin module does not touch
Charge_Mixing. Drop the dead declaration per the project rule that
unused functions and their tests be removed.
* Refactor: drop dead Charge_Mixing::tpiba2 member
tpiba2 was declared in chg_mix.h but never assigned by set_mixing()
nor read anywhere in the module. Grep across the whole source tree
confirms all tpiba2 references are either ucell.tpiba2 (a separate
UnitCell member) or local variables in unrelated modules. The
Charge_Mixing class never computed or used its own tpiba2 pointer;
only tpiba is consumed by the stateless Kerker kernels via
mix_rho_recip/mix_rho_real. Remove the dead declaration.
* Refactor: route Charge_Mixing getters through cfg_
get_mixing_mode(), get_mixing_beta(), get_mixing_ndim() previously
returned the legacy mirror members that set_mixing() kept in sync
with cfg_ by hand. With cfg_ now treated as the immutable INPUT
snapshot, route the public getters through cfg_ directly so there
is a single source of truth for INPUT parameters. External callers
(esolver_ks_lcao, lcao_others, pw_others) are unaffected since
signatures are unchanged. The legacy members remain in place for
now; they are dropped in a later step after internal readers are
migrated.
* Refactor: init_mixing constructs Mixing from cfg_ not legacy mirrors
init_mixing() branched on this->mixing_mode and passed
this->mixing_ndim/mixing_beta to the Broyden/Pulay/Plain_Mixing
constructors. These legacy mirrors were kept in sync with cfg_
manually by set_mixing(). Route through cfg_ directly so cfg_
remains the single source of INPUT parameters. The Mixing objects
themselves still copy beta/ndim into their own members at
construction; that is a one-time snapshot and not a continuous
sync surface, so it is left untouched.
* Refactor: mix_rho_recip/mix_rho_real read mixing_beta from cfg_
Both mix_rho_recip and mix_rho_real built the twobeta_mix functor by
reading this->mixing_beta / this->mixing_beta_mag, which are legacy
mirrors that set_mixing() kept in sync with cfg_. Route the six
construction sites through cfg_.mixing_beta / cfg_.mixing_beta_mag
so cfg_ is the single source of INPUT parameters consumed by the
mixing logic. Behavior is unchanged since the mirrors and cfg_
hold identical values after set_mixing().
* Refactor: set_mixing stops mirroring cfg_ into legacy members
set_mixing() copied mixing_mode, mixing_beta, mixing_beta_mag,
mixing_ndim from cfg into legacy mirror members, then validation
and logging read from the mirrors. Now that all internal readers
(init_mixing, mix_rho_recip, mix_rho_real, getters) read from
cfg_, the mirror writes are dead work. Drop them and route
validation and log output through cfg_ directly. omega and tpiba
remain pointer members because they alias external runtime state
(cell volume, lattice constant) that changes across SCF iterations
and so do not belong in MixingConfig (an immutable INPUT snapshot).
* Refactor: drop legacy Charge_Mixing mirror members; cfg_ is single source
Drop mixing_mode, mixing_beta, mixing_beta_mag, mixing_ndim mirror
members. After the previous commits every internal reader (getters,
init_mixing, mix_rho_recip, mix_rho_real, set_mixing validation
and log output) routes through cfg_, so the mirrors are dead state
that set_mixing() no longer writes. cfg_ is now the single source
of truth for INPUT mixing parameters.
Update test_chg_mix.cpp accordingly: the two assertions that
reached directly into CMtest.mixing_beta_mag and CMtest.mixing_mode
now read CMtest.get_mixing_config().mixing_beta_mag and
CMtest.get_mixing_mode(), matching the public API used by the
other assertions in the same block. No production caller accessed
these members directly (esolver_ks_lcao, lcao_others, pw_others
all used the getters), so the change is test-only on the consumer
side.
* Refactor: drop NSDMI from MixingConfig to force explicit construction
The non-static data member initializers in MixingConfig provided
plausible-looking defaults (e.g. mixing_beta=0.8, mixing_mode=
"broyden") that silently masked forgotten fields when a new field
was added but not wired up at construction sites. With the
defaults removed, every construction site must use aggregate
initialization (or copy-assign from a fully-initialized instance),
and a missing field yields value-initialized (zero/empty) members
that are far more likely to trip a test than the old defaults.
Combined with -Wmissing-field-initializers promoted to error in
the next commits, adding a field to MixingConfig without updating
all aggregate-initialization sites becomes a compile error.
* Refactor: aggregate-init MixingConfig in esolver_ks with pragma guard
Convert the 17-line field-by-field assignment of mix_cfg into a
single aggregate initialization in declaration order. Wrap it in
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating this list
becomes a compile error rather than silently using a default.
Each initializer is annotated with the field name it corresponds
to, making the declaration-order dependency auditable at a glance.
* Refactor: aggregate-init MixingConfig in test_chg_mix with pragma guard
Convert make_cfg()'s 17-line field-by-field assignment into a
single aggregate initialization in declaration order, matching
the esolver-side change. Wrap in the same
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating the test
helper is also a compile error. Both construction sites (esolver
and test) now fail at compile time if a field is missing, closing
the maintenance gap where a new field could silently fall back to
a default value.
* Fix: fail-fast guards in Charge_Mixing and update chg_mix tests
Add validation to turn latent misuse (skipped set_rhopw/set_mixing)
into clear WARNING_QUIT errors instead of null dereference or heap
corruption:
- init_mixing rejects a null rhopw
- if_scf_oscillate checks scf_nmax > 0 and iteration range
- mix_rho validates chr/chr->rhopw and the grid pointers
Fix three chg_mix unit tests that read cfg_ before set_mixing, which
caused a SIGSEGV in SCFOscillationTest and assertion failures in the
two inner-product tests.
* test(module_charge): add unit tests for chg_uspp and chg_dmr
Add test_chg_uspp.cpp covering split_dgrid/merge_dgrid (normal split,
round-trip, nspin=1/2, empty high-frequency/smooth boundaries, and
input-validation abort paths).
Add test_chg_dmr.cpp covering init_mixing_dmr/mix_dmr (nspin=1/2/4
mixing with Plain_Mixing analytically verified, empty-partition null
buffer allowance, and input-validation abort paths).
Wire both targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_precond, chg_drho, chg_drho_inner, chg_mix_rho
- test_chg_precond.cpp: kerker_screen_recip/real (early return, nspin=1/2/4
filter, nspin=4 with mixing_angle resize, real-space matches reciprocal).
- test_chg_drho.cpp: inner_product_real, cal_drho real-space path
(nspin=1/2/4+domag_z), cal_dkin (meta_gga false/true).
- test_chg_drho_inner.cpp: inner_product_recip_rho and
inner_product_recip_hartree for nspin=1 with a single G component,
analytically verified against the Coulomb metric.
- test_chg_mix_rho.cpp: mix_rho abort paths (null chr/chr->rhopw, unset
rhopw, double_grid without rhodpw) and real-space plain mixing value.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_symm, chg_symm_detail, chg_atomic, chg_atomic_inner
- test_chg_symm.cpp: symmetrize_rho / cal_rhog_symm / cal_rhog_symm_soc
no-op paths when symm_flag == 0, for nspin=1 and nspin=4.
- test_chg_symm_detail.cpp: psymmg and psymmg_soc idempotence on a
manually built D_4 point group over a serial cubic PW_Basis.
- test_chg_atomic_inner.cpp: compute_rhoatm USPP direct-copy branch and
NCPP integrate+scale-to-zv branch (Gaussian rho_at with known analytic
integral); normalize_and_check renormalizes uniform density to nelec.
- test_chg_atomic.cpp: atomic_rho ntype==0 path (skips atom loop) and
spin_number_need==3 abort path.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add chg_tau/chg_routine/chg_init tests; drop spurious XC_Functional stubs
Fourth batch of module_charge unit tests:
- test_chg_tau.cpp: mix_tau_recip abort paths (null chr/grid/mixing, nspin<1,
double_grid without high-f mixer) and non-double-grid plain mixing value.
- test_chg_routine.cpp: chgmixing_ks_pw/lcao iter==1 restart-step setup, and
chgmixing_ks convergence branches (conv_esolver true / drho<hsolver_error
skip mix_rho).
- test_chg_init.cpp: init_rho "wfc" with null wfcpw abort, and "atomic" with
ntype==0 + meta_gga Thomas-Fermi tau initialization.
Wire all three targets into unittests/CMakeLists.txt.
Cleanup: remove the XC_Functional::func_type / ked_flag definitions from
test_chg_drho, test_chg_mix_rho, test_chg_symm, test_chg_atomic_inner,
test_chg_atomic, test_chg_tau, test_chg_routine, and test_chg_init. None of
the non-test sources compiled into these targets reference these statics
(charge.cpp, chg_*.cpp, and the linked base/cell_info/planewave_serial/
symmetry libraries are clean), so the definitions were pure dead weight.
Also drop the now-unneeded xc_functional.h include from test_chg_mix_rho.cpp
and correct the stub comments.
* test(module_charge): fix broken includes in unit tests
- include chg_atomic_detail.h instead of nonexistent chg_atomic_inner.h
in test_chg_atomic_inner.cpp; add math_integral.h for Simpson_Integral
- include chg_drho.h in test_chg_drho_inner.cpp for
module_charge::inner_product_recip_hartree
- include source_cell/magnetism.h in tests that define Magnetism stubs
(test_chg_drho, test_chg_symm, test_chg_tau, test_chg_mix_rho)
- fix nonexistent source_charge/mixing includes in test_chg_tau.cpp to
source_base/module_mixing
* test(module_charge): fix link/build issues; temporarily disable routine/init targets
- test_chg_tau: use Plain_Mixing(beta) ctor and init_mixing_data with
complex type_size (old set_mixing_beta/init_mixing no longer exist)
- test_chg_symm_detail: add Magnetism stub required by cell_info's
unitcell.cpp, matching other tests in this directory
- test_chg_routine: adapt to two-arg set_rhopw and tpiba from ucell
- disable MODULE_CHARGE_routine and MODULE_CHARGE_init targets with
documented reasons: their transitive dependencies (Plus_U_Base,
elecstate, source_io) are deeply coupled; to be resolved later
* fix bug
* format tool_quit
* remove a test due to WARNING_QUIT funcitno
* delete the support file charge-density.dat because unittests never need it
---------
Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
Co-authored-by: Xiaoyang Zhang <tsfxwbbzxy@163.com>
* tests: take three charge/DM tests off #define private public (#7998)
Redo of the work in #7988 and #7990, both of which were closed while the
charge density module was being restructured. That restructuring landed in
#7972 and already did most of the decoupling those PRs proposed: allocate(),
renormalize_rho() and sum_rho() now take their inputs explicitly, the mixing
parameters are aggregated in a MixingConfig, and chg_mix.cpp / chg_drho.cpp /
charge.cpp are free of global parameter reads. What was left was the
test-side access.
Production changes are additive only - no existing signature moves and no
line is deleted from any production header:
Charge::get_allocate_rho() - report whether allocate() has run
Charge_Mixing::get_rho_mdata() - mirror the existing get_dmr_mdata()
Charge_Mixing::get_tau_mdata()
Charge_Mixing::set_mixing_config() - pair for the existing getter, for
callers that must update the snapshot
without rebuilding the mixing history
XC_Functional::set_func_type() - pair for get_func_type()
XC_Functional::set_ked_flag() - pair for get_ked_flag()
Test changes:
test_dm_r_init - two sites move to the already public get_DMR_save()
test_charge - the global parameter scratchpad becomes fixture state
(32 refs -> 0); PW_Basis setup goes through the public
initgrids/initparameters/setuptransform sequence instead
of the protected distribute_r()/distribute_g()
test_chg_mix - the scratchpad becomes a fixture-owned MixingConfig
(163 refs -> 0); the three blocks that hand-wired
Charge::_space_* now take their buffers from the fixture,
which owns them as vectors and points the public
rho/rhog/kin_r views at them with the same stride
No expected value or tolerance was changed.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Refactor: replace the JSON path walker with schema operations (#7994)
* Fix: Correct FFTW version detection and NCCL build summary (#7995)
* Fix: Correct FFTW version detection and NCCL build summary
* Read FFTW version directly from adjacent pkg-config metadata
* Fix: restore LibRI centered cell folding in get_Born_von_Karmen_cells
The previous replacement dropped LibRI's Array_Operator::operator%
mapping (c % n + 3*n/2) % n - n/2, shifting cell coordinates from
[-n/2, n/2) to [0, n). Callers using exact coordinate keys (e.g.
58_KP_LR_BSE reading (-1,-1,-1) from a (2,2,2) Rlist) failed with
"R coordinates are not in Rlist". Reintroduce the centered folding
in both the 1D and recursive overloads.
* Fix: propagate CUDA::nvToolsExt through base's link interface
For CUDA < 12.9, NVTX symbols (nvtxRangePushA/nvtxRangePop) live in
libnvToolsExt. Since __USE_NVTX is defined on the OBJECT library base
(which compiles timer.cpp), every consumer of base's object files needs
that library on its link line. Linking it only to the main executable
left unit tests that link base directly (e.g. MODULE_CELL_SYMMETRY_analysis)
with undefined NVTX references on CUDA 12.2 CI. Attach the dependency to
base as INTERFACE so it propagates to the executable and all test targets.
---------
Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
Co-authored-by: Xiaoyang Zhang <tsfxwbbzxy@163.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: SY Wang <uwsy1059@qq.com>
Co-authored-by: Taoni Bao <baotaoni@pku.edu.cn>
* Refactor: add n-free accessors to OccupationMatrix (step 1 of removing the n index)
The radial-channel index n of occ_[iat][l][n][spin] is always 0 in every
production path. Add n-free overloads of get/get_save/set/mat/mat_save that
forward to n=0, so call sites can migrate before the underlying storage
drops the n dimension.
* Refactor: drop the n index from the LCAO occupation-matrix accumulation (step 2)
The radial-channel loop in reduce_and_symmetrize_occ_k,
accumulate_occ_k_for_ik and process_occ_channel_gamma always skips n != 0,
so remove the loop and the n parameter of accumulate_occ_channel_k/gamma;
call sites switch to the new n-free OccupationMatrix accessors.
* Refactor: drop the n index from the LCAO potential and energy paths (step 3)
cal_pot_onsite's radial-channel loop always skips n != 0, so remove it and
switch iatlnmipol2iwt reads to channel 0. get_onsite_pot and the three
calc_energy_* helpers lose their N/n parameters; the Yukawa U/J lookups are
fixed to channel 0, matching every existing call site.
* Refactor: drop the n index from the PW path and ijr helpers (step 4)
* Refactor: drop the n storage dimension from OccupationMatrix (step 5)
* Refactor: remove the n loops from the DFT+U IO path (step 6)
* Refactor: drop the per-n U/J storage from YukawaScreening (step 8)
* Refactor: add corr_iwt accessor and migrate DFT+U lookup consumers
* Refactor: store corr_iwt for the correlated channel only and drop iatlnmipol2iwt
* Fix: use transN for onsite gemm in cal_force_gamma
The second gemm in cal_force_gamma was using transT (same as the first),
making it an identical computation that overwrote the first result.
The correct contraction for the onsite contribution is dS/dR * rho^N
(no transpose), matching the structure of cal_force_k which uses
rho^C for diag and rho^N for onsite.
rho_pot_onsite = DM * V_onsite is not symmetric, so rho^T != rho^N.
Added formula comments to both cal_force_gamma and cal_force_k.
* Refactor: merge op_legacy contributeHk specializations + compliance cleanup
- Merge three identical contributeHk template specializations into one
generic implementation (94 -> 47 lines)
- Split comma-separated variable declarations (governance rule 8)
- Replace exit(0) with WARNING_QUIT for MPI safety
- Remove dead npol variables and commented-out code
- Unify timer labels to DFTU_LCAO across the module
* Refactor: templatize occ accumulation and narrow public header
- Merge accumulate_occ_channel_k/gamma into template accumulate_occ_channel<T>
- Merge accumulate_occ_k_for_ik into template accumulate_occ_for_ik<T>
- Rename reduce_and_symmetrize_occ_k to reduce_and_symmetrize_occ
- Move all internal helpers into anonymous namespace
- Move cal_occ_mat_k/gamma into DFTU_LCAO namespace
- Narrow public header from 108 to 61 lines (3 public functions)
* Refactor: shorten internal helper names in dftu_nao_occ.cpp
accumulate_occ_channel -> acc_channel
accumulate_occ_for_ik -> acc_for_ik
accumulate_occ_over_kstar -> acc_over_kstar
reduce_and_symmetrize_occ -> reduce_symm
process_occ_channel_gamma -> acc_channel_gamma
* Refactor: extract for_adj_pair skeleton and split cal_fs_nao_r_impl
- Add for_adj_pair() in dftu_nao_ijr.h as common pair-walk skeleton
- Migrate accumulate_hr_for_iat0 and compute_occ_from_dmr to for_adj_pair
- Split cal_fs_nao_r_impl (240 lines) into build_nlm, acc_fs_pairs,
reduce_force, reduce_stress helpers (~100 lines each)
* Refactor: migrate module_dftu tests to unittests/ with test_<source> naming
Follow the module_charge convention:
- test/ -> unittests/, one test file per source file
- split dftu_core_test into test_dftu_nao_pots + test_dftu_nao_energy
- split dftu_operator_test into test_dftu_nao_op_legacy + test_dftu_nao_ijr
+ test_dftu_nao_for_r + test_dftu_nao_str_r
- rename dftu_lcao_test to test_dftu_nao_op
- rename test_dftu_nao_ijr to test_dftu_nao_ijr (already correct)
- unittests/CMakeLists.txt follows module_charge pattern with
abacus_disable_feature_definitions and KEEP_FEATURE_DEFINITIONS __MPI
for the two tests that call MPI_Init in main()
- add TODO comments on dftu_nao_occ/fs_k/fs_r about testability refactoring
* Test: add unit tests for get_linear_index and reduce_force/reduce_stress
- Extract reduce_force/reduce_stress from anonymous namespace into
public reduce_force_impl/reduce_stress_impl in dftu_nao_fs_r.h,
implemented in new dftu_nao_fs_reduce.cpp to keep link closure minimal.
- Add test_dftu_nao_folding.cpp covering get_linear_index row-major
("cg") and column-major ("scalapack_gvx") indexing.
- Add test_dftu_nao_fs_r.cpp covering reduce_force_impl nspin scaling
and reduce_stress_impl weight + Voigt-to-3x3 rearrangement.
- Use minimal per-file mocks (UnitCell, Magnetism, SepPot, Sep_Cell,
Parallel_Orbitals, Parallel_Reduce::reduce_all<double>) to avoid
heavy link closures.
- Link dftu_nao_fs_reduce.cpp into MODULE_DFTU_op which consumes
dftu_nao_fs_r.cpp.
Verified: ctest -R MODULE_DFTU 9/9 passed.
* Test: add unit tests for accumulate_diag_force/stress
- Extract accumulate_diag_force and accumulate_diag_stress from
anonymous namespace in dftu_nao_fs_k.cpp into new header
dftu_nao_fs_accum.h (templates must be header-only for external
instantiation). accumulate_onsite_force stays in dftu_nao_fs_k.cpp
because it needs Plus_U_Base's occupation-matrix lookup.
- Add test_dftu_nao_fs_accum.cpp covering double/complex diagonal
force accumulation (atom attribution via iwt2iat) and stress
accumulation with factor scaling.
- Link real parallel_orbitals.cpp and keep __MPI so
Parallel_2D::set_serial works (mock constructor previously
shadowed the real path, causing nrow=-1).
Verified: ctest -R MODULE_DFTU 10/10 passed.
* Fix: add dftu_nao_fs_reduce.o to Makefile.Objects
dftu_nao_fs_reduce.cpp defines reduce_force_impl/reduce_stress_impl but
was missing from OBJS_DFTU, causing undefined-reference link errors in
the Makefile build. The CMake build already lists the source.
* Test: add unit tests for dftu_nao_fs_reduce and dftu_nao_fs_k
Add two new unit test targets for module_dftu:
- MODULE_DFTU_fs_reduce (6 tests): covers reduce_force_impl spin
scaling (nspin=1/2/4) and reduce_stress_impl Voigt-to-tensor
conversion, symmetry, and lat0/omega weight.
- MODULE_DFTU_fs_k (2 tests): covers DftuFsEnv reference semantics
(unchanged storage, external mutability). Uses heap-allocated
dependencies without destruction to avoid BLACS/Grid_Driver
linkage in the test binary.
dftu_nao_occ.cpp and dftu_nao_adj.cpp were evaluated but skipped:
their core functions are documented as hard to unit-test and depend
on hamilt::Hamilt, Grid_Driver::Find_atom, and TwoCenterIntegrator::snap,
making meaningful unit tests impractical without heavy refactoring.
Verified: ctest -R "MODULE_DFTU_fs_reduce|MODULE_DFTU_fs_k" -V
8/8 tests passed.
* reduce unittest time
---------
Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
…nergies and Structure_Factor take their INPUT values explicitly (#8004) * source_estate: give Charge_Extra an explicit test seam test_chg_extra.cpp reached into eleven private members and one private method of Charge_Extra, and staged nspin/chg_extrap in the private half of the parameter singleton even though Init_CE() already takes both as arguments. Charge_Extra gains accessors over its extrapolation state - the step bookkeeping, the displacement and delta_rho histories, and the alpha/beta coefficients - plus find_alpha_and_beta_for_testing(), which runs the private solver against whatever histories are currently held so its solution can be checked on its own rather than only through a full extrapolate_charge() step. extrapolate_charge() also gains has_float_data, which it forwards to Structure_Factor::setup(). That parameter arrives in a later commit on this branch; taking it as an argument is what lets chg_extra.cpp stay free of global reads instead of sourcing the flag itself. The test's writes to the parameter singleton become fixture state (24 refs -> 0). The global_out_dir write was dead: nothing in this target's sources reads it. No expected value was changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * source_estate: cal_energies takes its INPUT flags explicitly ElecState::cal_energies() read four INPUT flags out of the global parameter singleton to decide which optional energy terms contribute, which left elecstate_energy_test.cpp with no way to drive them except writing the private half of that singleton. The flags are now passed one by one rather than bundled, so each call site states exactly which behaviour it is selecting: cal_energies(type, imp_sol, sc_mag_switch, dft_plus_u, assume_isolated) All nine call sites are updated. Eight of them already hold an Input_para (this->inp_ in the esolvers, inp in chg_routine.cpp) and so add no global references at all; only rdmft.cpp, which has no Input_para in scope, reads PARAM.inp directly. The six nspin reads are not threaded through: ElecState already owns the Charge that carries nspin, and elecstate_pw.cpp reads this->charge->nspin in nine places already, so the same member is used here. elecstate_energy.cpp now has no global reads left. In the test, nine of the twelve keys the fixture wrote were dead for this target - makov_payne.cpp is the only linked source that still reads the singleton, and its branch is never reached because assume_isolated stays "none". The rest become fixture state. No expected value was changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * source_pw: Structure_Factor::setup takes has_float_data explicitly setup() read PARAM.globalv.has_float_data to decide whether to build the single-precision eigts copies, so structure_factor_test.cpp had to write the private half of the parameter singleton to exercise the float path. setup() now takes the flag as an argument. Charge_Extra::extrapolate_charge() forwards the value it was given in the previous commit, which keeps chg_extra.cpp free of global reads - it has none today and this would otherwise have put two back. All call sites are updated, including the two mock definitions of Structure_Factor::setup in psi_init_unit_test.cpp and test_chg_extra.cpp, which have to move in lockstep with the real signature. The test's twelve private reads of the eigts arrays needed no new interface: the public get_eigts1_data<FPTYPE>() family already returns exactly those pointers, so the reads move to <float> / <double> instantiations. No expected value was changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…about a third of their wall time (#8006) * tests: let Autotest.sh run integration cases concurrently Each integration suite is a single ctest test, and Autotest.sh walks its cases in a serial loop. On the CI runner a case takes `-np 4` MPI ranks and OMP_NUM_THREADS=2, i.e. 8 of the 16 available threads, so half the machine idles while the remaining cases queue up. These cases are a few atoms apiece, where a wider OpenMP team buys almost nothing; spending the same thread budget on several cases side by side is far better. Add `-j <n>`, or `-j auto` which fits as many whole `-np`-sized cases as the core budget allows. The default stays 1, so nothing changes for a caller that does not ask for concurrency. Measured with `taskset -c 0-15` (15 usable CPUs) and OMP_NUM_THREADS=2 exported the way the CI workflow does: tests/01_PW (138 cases) serial 267 s -j auto (3) 100 s -j 4 80 s tests/07_OFDFT serial 49 s -j auto (3) 19 s tests/03_NAO_multik serial 145 s -j auto (3) 53 s Concurrency changes nothing observable. For all three suites test.sum is byte-identical between the serial and the concurrent run, the summary counters match (01_PW reports 4 failed / 2 fatal / 867 properties either way), and the RUN/OK/WARNING/ERROR lines appear in the same order, because each case's console output is buffered and replayed in cases-file order. Points worth recording: - `nproc` honours $OMP_NUM_THREADS, which the CI workflow exports, so it reports the per-case thread count rather than the machine size. The core budget is therefore read with both OpenMP variables cleared. - A concurrent run recomputes the per-case thread count instead of inheriting an $OMP_NUM_THREADS that was sized for one case at a time; `-o` still pins it explicitly. - Address Sanitizer runs are forced back to -j 1, because every case appends to one shared diagnostics report. - A case now runs in a subshell in both modes and its counters are aggregated from per-case files instead of shell state. That also removes a latent double count: the serial loop used to leave the last case's counters in scope, which added one spurious test.sum line. - MPI binding is left as it is. In a harness that ran the cases without checking them, `--bind-to none` measured slower than the default binding (80.3 s against 65.2 s at 4 concurrent cases), so no launcher flag is added. check_out is unchanged apart from two trailing-whitespace-only lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * tests: run the CPU integration suites with -j auto Wire the concurrency from the previous commit into the suites that ctest registers. `auto` divides the cores available to the process by the suite's `-np`, so a 16-core CI runner takes 4 cases at a time while a small developer machine stays serial. The GPU suites keep their serial invocation: they share one device, and concurrent cases would contend for its memory. The AddressSanitizer variants are left alone as well; Autotest.sh forces those back to -j 1 regardless, since they share one diagnostics report. Verified by configuring with -DBUILD_TESTING=ON and reading the registered commands back out of the generated CTestTestfile.cmake: the 11 CPU suites present in that configuration carry "-n" "4" "-j" "auto", and the GPU suite does not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Zhang Zhili <185902695+zzlinpku@users.noreply.github.com>
* module_charge: normalize indentation and brace single-statement control flow
Mechanical cleanup as the first step of the module_charge governance
refactor: convert leading tabs to 4-space indentation (1011 occurrences
across 11 files) and add braces around all single-statement if/for/while
bodies (11 sites). No functional change.
* module_charge: aggregate Charge_Mixing params into MixingConfig
Introduce a MixingConfig POD that bundles the INPUT mixing parameters
with the runtime globals (nspin, scf_thr_type, double_grid), and change
set_mixing from a 12-argument interface to set_mixing(const MixingConfig&,
double&, double&). Charge_Mixing now stores the config and reads nspin /
scf_thr_type / double_grid from it instead of PARAM.inp / PARAM.globalv,
removing the direct PARAM reads in set_mixing and init_mixing.
The single production call site (esolver_ks.cpp) fills the config, and
the unit test drives set_mixing via a make_cfg() helper. The
'#define private public' access hack is kept for now with a TODO: the
test still must write Parameter::input/sys, Charge::_space_* and
XC_Functional privates, which need the Step 4/5 global-state
parameterization before it can be removed.
Verified: make -j30 MODULE_ESTATE_charge_mixing (build_max_para_test)
passes with no errors.
* module_charge: deduplicate twobeta_mix lambdas and replace raw new with std::vector
Extract the repeated two-beta mixing functor in mix_rho_recip/mix_rho_real
into a make_twobeta_mix<T> template helper (6 lambda copies removed), and
convert all local raw new[]/delete[] buffers in charge_mixing_rho.cpp to
zero-initialized std::vector, dropping the paired ZEROS calls.
* module_charge: move residual/inner-product globals into MixingConfig
Extend MixingConfig with gamma_only_pw/domag/domag_z so mix_resid.cpp
(get_drho, get_dkin, inner_product_recip_{rho,simple,hartree,real}) no
longer reads PARAM/GlobalV; all branches now consume this->cfg_.
inner_product_recip_rho's raw pointer-array views are switched to
std::vector. Production fills the three new fields in esolver_ks, and
the test fixture gains a sync_cfg() helper to push PARAM mutations into
cfg_ for the inner-product branch tests.
* module_charge: own Charge's _space_* storage with std::vector (Step 5a)
Replace the six private raw _space_rho/_space_rho_save/_space_rhog/
_space_rhog_save/_space_kin_r/_space_kin_r_save buffers with
std::vector, so Charge's underlying contiguous storage self-manages and
the matching delete[] calls in destroy() (which relied on reading
possibly-uninitialized pointers) go away. The public rho/rhog/rho_save/
rhog_save/kin_r/kin_r_save views keep their double**/complex** shape and
still alias the vector memory via .data(), so all external consumers are
unaffected. Tests that drove _space_* directly are adapted to
resize()/.data() and drop their manual delete[] of the buffers.
* module_charge: route chgmixing_ks through its inp parameter
chgmixing_ks already takes a const Input_para& inp but still read
PARAM.inp.mixing_restart / PARAM.inp.scf_nmax from the global. Use the
inp argument instead so the function no longer reads INPUT state through
the global for these two fields. PARAM.globalv.ks_run is a runtime
per-process flag (set from band-parallel topology), not an input, so it
is intentionally left as-is rather than threading it through the
interface.
* module_charge: split Charge::init_rho into per-stage private methods
init_rho had a cyclomatic complexity of 36 from five sequential stages
(file read, atomic fallback, Thomas-Fermi tau, restart load, wfc read)
interleaved through shared read_error/read_kin_error flags. Extract the
four branches into private methods -- read_rho_from_file,
init_rho_atomic_and_tau, load_rho_from_restart, init_rho_from_wfc -- and
leave init_rho as a thin sequence of stage calls. Logic is unchanged; the
error flags are threaded through as parameters. The deepest stage
(read_rho_from_file) now sits at complexity 19, down from 36 for the
monolith. The remaining global reads inside the stages are untouched and
deferred to a later parameterization step.
* module_charge: extract Charge density math into charge_math free functions
sum_rho, cal_rho2ne and non_linear_core_correction each used Charge
members only to reach a handful of scalars (nrxx/nxyz/omega) or the
reciprocal-shell table (gg_uniq/ngg); the rest of each body is pure
numerics. Move the three bodies into a new charge_math namespace as free
functions with those values passed explicitly, and leave the Charge
members as thin forwarding wrappers so no caller outside the module
changes. The kernels are now unit-testable in isolation and no longer
coupled to Charge state. One behavior note: the pre-quit debug line that
printed sum_rho to ofs_warning is dropped so the free function stays free
of global-stream dependencies. charge_math.cpp is wired into the estate
library and the charge_test target.
* module_charge: register charge_math.o in the hand-written Makefile build
The CMake build already picks up charge_math.cpp; mirror that in
Makefile.Objects so the legacy Makefile flow links the new charge_math
kernels too. The module_charge directory is already on VPATH, so adding
charge_math.o to the object list is sufficient.
* module_charge: extract Charge::atomic_rho into charge_atomic free function
Remove Charge::atomic_rho entirely and replace all call sites with
module_charge::atomic_rho(..., rhopw), eliminating the need for a thin
wrapper on the Charge class. This decouples atomic density initialization
from Charge's state and improves charge.cpp quality score from 2 to 44.
* module_charge: forbid Charge copies and guard tau.cube write
scf_out_chg_tau aborted in Parallel_Grid::reduce on
assert(rhoin != nullptr) because the kin_r_save[is] handed to
write_vdata_palgrid was not a valid buffer. After the _space_* storage
became std::vector (ecf5084d4), a copied/moved Charge leaves its
rho/kin_r views dangling into another object's vector buffer, and a
kin_r_save never allocated (ked_flag set after allocate) stays nullptr;
both surface as a null rhoin deep inside MPI gather instead of at the
source.
Delete Charge's copy constructor/assignment so any value copy of the
vector-aliasing views fails at compile time, and check kin_r_save in
ctrl_output_fp before writing tau.cube so a missing allocation reports
a clear message instead of tripping the MPI assert.
Verification: not run locally (per user request, user compiles).
* module_base: tolerate null grid buffer when a rank owns no grid points
scf_out_chg_tau (LCAO, SCAN, out_chg=1, 4 MPI ranks) aborted in
Parallel_Grid::reduce on assert(rhoin != nullptr). Bisecting between
83eb5d0f3 (good) and ecf5084d4 (bad) isolated the regression to
ecf5084d4, which moved Charge's _space_* storage from raw new[] to
std::vector.
Root cause: with 4 ranks the FFT grid is slab-decomposed so that the
last rank owns zero real-space points (nrxx == 0, confirmed via a
temporary diagnostic printing fn/is/rank/nrxx at the reduce call site).
Before ecf5084d4, _space_rho = new double[nspin * 0] == new double[0]
returned a unique non-null pointer, so rho_save[is] was non-null and the
assert passed. After the change, an empty vector's .data() returns
nullptr, so the rank with nrxx == 0 handed a null rhoin to reduce and
tripped the assert (Debug) or fed MPI_Gatherv a null buffer (Release).
A rank with nrxx == 0 is legitimate: MPI_Gatherv is invoked with
sendcount 0 and ignores the send buffer. Relax the assert to only flag a
null buffer when nrxx != 0, and revert the now-unneeded kin_r_save guard
in ctrl_output_fp (it would have falsely aborted on the nrxx == 0 rank).
Verification: Release build (build_max_para_test), ran
cd tests/03_NAO_multik/scf_out_chg_tau &&
OMP_NUM_THREADS=1 mpirun -np 4 ../../../build_max_para_test/abacus_max_para
Result: exit 0, chg.cube and tau.cube written; numerical comparison
against chg.cube.ref/tau.cube.ref gives maxdiff 0 (chg) and 1e-14 (tau).
* module_charge: extract Charge::set_rho_core into charge_math free function
Move set_rho_core to charge_math::set_rho_core with rho_core,
rhog_core and rhopw passed explicitly instead of reading Charge
state, and call charge_math::non_linear_core_correction directly.
Remove the now-unused Charge::non_linear_core_correction wrapper,
use std::vector for the rhocg/vg scratch buffers, update the
init_scf call site, and drop the obsolete member stubs in the
elecstate unit tests.
* module_charge: vectorize Charge_Extra history arrays and forbid copies
Replace the raw new[]/delete[] displacement arrays (dis_old1, dis_old2,
dis_now) with std::vector and remove the hand-written destructor. This
fixes a read of uninitialized pot_order when an object is destroyed
before Init_CE, a memory leak when Init_CE is called repeatedly, and a
double-free risk from the implicitly generated shallow copy. The copy
constructor and copy assignment are deleted so the molecular-dynamics
trajectory history cannot be silently forked. The unit test now checks
vector sizes instead of non-null pointers.
* Rename charge_math to chg_tools and unify namespace module_charge
- Rename module_charge/charge_math.{h,cpp} to chg_tools.{h,cpp} via git mv
- Change namespace charge_math to module_charge to match charge_atomic
and chgmixing in the same directory
- Update include guard CHG_TOOLS_H and TITLE/timer labels accordingly
- Update call sites in init_scf.cpp, charge.cpp, charge_init.cpp
- Update build references in Makefile.Objects and both CMakeLists.txt
* module_charge: refactor Symmetry_rho class to free functions
Convert the stateless class Symmetry_rho into namespace module_charge
free functions and rename files for consistency:
symm_rho.{h,cpp} -> chg_symm.{h,cpp}
symm_rho_detail.h -> chg_symm_detail.h
symm_rhog.cpp -> chg_symm_detail.cpp
- 5 public functions become module_charge::symmetrize_rho / cal_rhog_symm
(2 overloads) / cal_rhog_symm_soc (2 overloads)
- 2 cross-TU helpers (psymmg/psymmg_soc) moved to module_charge::detail
via chg_symm_detail.h
- 3 internal MPI helpers moved to anonymous namespace
- Delete dead code psymm (real-space symmetrization, never called)
- Remove empty ctor/dtor and parallel_grid.h include
- Rename begin/begin_soc to cal_rhog_symm/cal_rhog_symm_soc for clarity
- Update timer/TITLE labels from "Symmetry_rho" to "module_charge"
- Migrate all 14 call sites and 1 test stub
- Remove obsolete Makefile special rule (no more name collision)
* module_charge: extract MixingConfig header and drop unused inner_product_recip_simple
Move MixingConfig from charge_mixing.h into its own mixing_config.h so
stateless residual kernels can include the config without dragging in
Charge_Mixing. Remove inner_product_recip_simple, which had no production
call sites, together with its unit test.
* module_gint: move gint_prec_ctrl from module_charge
Relocate gint_prec_ctrl.{h,cpp} and its test into module_gint, update the
include in esolver_ks_lcao.h and rewire the CMake/Makefile object lists.
* module_charge: extract mixing inner products into chg_drho free functions
Rename mix_resid.cpp to chg_drho.cpp and turn inner_product_real and
inner_product_recip_hartree into module_charge free functions declared
in chg_drho.h; inner_product_recip_rho, which is only shared with the
unit test, moves to module_charge::detail in chg_drho_detail.h.
Charge_Mixing loses the three private inner-product members and
mix_rho_recip/mix_rho_real bind the free functions through lambdas.
get_drho/get_dkin stay as members for this step.
* module_charge: hide cal_drho/cal_dkin in an anonymous namespace
Move the get_drho/get_dkin implementations into file-local cal_drho/
cal_dkin free functions with all inputs explicit; the public
Charge_Mixing methods become thin forwarding wrappers so esolver call
sites stay unchanged.
* module_gint: fix include path in test_gint_prec_ctrl after relocation
* module_charge: extract Kerker screen kernels into chg_precond free functions
Move Charge_Mixing::Kerker_screen_recip/real to module_charge namespace
as free functions in chg_precond.{h,cpp}, renaming mix_precond.cpp via
git mv. Config/grid/geometry are passed explicitly via MixingConfig,
PW_Basis*, and tpiba, eliminating the function's direct read of
PARAM.inp.nspin. Replace 8 std::bind call sites in charge_mixing_rho.cpp
with lambdas, update 2 commented-out bind sites in charge_mixing_dmr.cpp,
and rewrite 12 test call sites in charge_mixing_test.cpp to construct an
independent MixingConfig instead of poking at Charge_Mixing privates.
Drop the now-unused member function declarations from charge_mixing.h.
* module_charge: fix Makefile.Objects after mix_precond -> chg_precond rename
Update the non-CMake object list to track the renamed translation unit so
make-based builds do not reference the deleted mix_precond.o.
* module_charge: drop Charge_Mixing::get_drho/get_dkin wrappers
Expose cal_drho/cal_dkin as module_charge free functions in chg_drho.h
and let ESolver_KS call them directly with explicit arguments; add
Charge_Mixing::get_mixing_config() as a const observer for the config.
* module_charge: rename chgmixing.h/cpp to chg_routine.h/cpp
Align with the chg_<feature> naming pattern used in the same directory
(chg_drho, chg_precond, chg_symm, chg_tools). Update include guard to
CHG_ROUTINE_H, the self-include in chg_routine.cpp, the entry in
source_estate/CMakeLists.txt and source/Makefile.Objects, and the three
#include sites in esolver_ks{,_pw,_lcao}.cpp. Function names
(chgmixing_ks{,_pw,_lcao}) and TITLE/timer tags are intentionally left
unchanged to keep the diff minimal.
* module_charge: rename mixing_config.h to chg_mix_cfg.h
Rename the MixingConfig header to align with the chg_* naming
convention in module_charge. Update the include guard and the four
in-tree includers; no CMake change is needed since the header is not
listed explicitly.
* module_charge: convert Charge MPI helpers into chg_parallel free functions
Rename charge_mpi.cpp to chg_parallel.cpp and add chg_parallel.h, moving
the three stateless Charge member functions (reduce_diff_pools, rho_mpi,
kin_r_mpi) to module_charge namespace free functions that take the
Charge object explicitly. Remove their declarations from charge.h and
update all call sites in elecstate_pw, stress_mgga, read_wf2rho_pw and
sto_iter. Rename the unit test to test_chg_parallel.cpp and update the
test target name accordingly.
GlobalV/PARAM reads and the direct MPI_Allreduce in reduce_diff_pools
are preserved as pre-existing technical debt (migration-neutral).
* Rename charge_atomic files to chg_atomic
- Rename module_charge/charge_atomic.{h,cpp} to chg_atomic.{h,cpp}
- Update include guard to CHG_ATOMIC_H
- Update includes in charge_init.cpp and charge_extra.cpp
- Update source paths in CMakeLists.txt, test CMakeLists.txt
- Fix stale object names in Makefile.Objects: replace
symm_rho_charge.o/symm_rhog.o with chg_symm.o/chg_symm_detail.o
* module_charge: extract USPP double-grid split/merge into chg_uspp free functions
Introduce module_charge::split_dgrid / merge_dgrid in chg_uspp.{h,cpp} as
RAII, parameter-explicit replacements for Charge_Mixing::divide_data /
combine_data / clean_data, which paired raw new[] with manual delete[]
across ~160 lines of mixing code.
- chg_uspp.{h,cpp}: stateless free functions in module_charge namespace;
outputs are caller-pre-sized std::vector, no new/delete; parameter
validation via WARNING_QUIT; TITLE/timer tags preserved
- charge_mixing_rho.cpp: rho and tau double-grid paths switched to the new
functions; raw pointer aliases kept for !double_grid so the existing
mixing call sites (nspin==1/2/4) are untouched
- CMakeLists.txt (source + test): wire chg_uspp.cpp
The legacy divide_data/combine_data/clean_data members are not yet removed;
that follows in a later step after the test is updated.
* module_charge: rewrite MixDivCombTest for the new split_dgrid/merge_dgrid
Drop the legacy alias-pointer assertions (EXPECT_EQ(datas, data.data()),
EXPECT_EQ(datas, nullptr) after clean_data) that coupled the test to the
old new[]/delete[] ownership model.
The rewritten case verifies the actual contract:
- split_dgrid fills smooth and high-frequency buffers with the dense
data verbatim (per-element comparison)
- merge_dgrid is a left-inverse of split_dgrid (output == input)
- no explicit cleanup call is required: std::vector manages storage
Covers nspin == 1 and nspin == 2 paths.
* module_charge: drop legacy divide_data/combine_data/clean_data members
With the new module_charge::split_dgrid/merge_dgrid in chg_uspp.{h,cpp}
and all call sites in charge_mixing_rho.cpp migrated, the original
Charge_Mixing::divide_data / combine_data / clean_data members are dead.
- delete charge_mixing_uspp.cpp (the raw new[]/delete[] implementation)
- drop the three member declarations from charge_mixing.h
- remove charge_mixing_uspp.cpp from source/test CMakeLists.txt
- Makefile.Objects: drop charge_mixing_uspp.o, add chg_uspp.o
- refresh one stale comment in charge_mixing_rho.cpp to reference
merge_dgrid instead of the removed combine_data
* module_charge: rename charge_extra files to chg_extra and move class into namespace
Rename charge_extra.h/cpp to chg_extra.h/cpp and wrap the Charge_Extra
class in the module_charge namespace, matching the rest of module_charge
(chg_atomic, chg_symm, chg_uspp). Update include guards, call sites in
esolver_fp.h and the unit test, and CMake/Makefile source lists.
* module_charge: extract DMR mixing into chg_dmr free functions
Move the DMR allocation/mixing logic out of Charge_Mixing members into
stateless module_charge functions (init_mixing_dmr, template mix_dmr
with explicit instantiation), passing the Mixing object, mixing data
and MixingConfig explicitly instead of reading PARAM. Merge the two
identical real/complex mix_dmr overloads, replace raw new[]/delete[]
of the magnetic buffers with std::vector, and de-duplicate the
two-beta mixing lambda into a file-local helper. The members stay as
thin timer-wrapped wrappers so external call sites are unchanged.
* module_charge: remove Charge_Mixing DMR wrappers, call chg_dmr directly
Delete charge_mixing_dmr.cpp and have the two call sites
(chg_routine.cpp, esolver_ks_lcao.cpp) invoke module_charge::
init_mixing_dmr/mix_dmr directly with the Mixing object, mixing data
and MixingConfig obtained through Charge_Mixing accessors. Expose the
owned DMR mixing history via a new get_dmr_mdata() accessor and drop
the now-unneeded density_matrix.h include from charge_mixing.h.
Timers move into the free functions with module_charge labels.
Add the direct parallel_orbitals.h include to esolver_gets.h, whose
value member previously relied on the removed transitive include.
* module_charge: decouple chg_dmr kernel from HContainer, mix raw buffers
Change module_charge::mix_dmr to take per-spin raw contiguous double
buffers and nnr instead of HContainer/DMR container references, and
drop the hcontainer.h include (and its atom_pair/parallel_orbitals
dependency chain) from chg_dmr.cpp. The sole call site in
esolver_ks_lcao.cpp now extracts the wrappers and saved buffers from
the DensityMatrix containers before calling the kernel. Move the
argument checks into a file-local check_dmr_inputs helper. The kernel
now depends only on the mixing module and MixingConfig.
* module_charge: refactor charge_mixing_rho free functions and cleanup
- Replace 17 PARAM.inp/globalv direct reads with cfg_ fields
- Unify mixing_tau: remove redundant member, use cfg_.mixing_tau
- Extract make_twobeta_mix as free function template in anonymous namespace
- Extract mix_tau_recip free function for kinetic energy density mixing
- Extract pack_rho_mag/unpack_rho_mag templates for nspin==2 dedup
- Hoist screen and inner_product lambdas before if-else chains (8+4 dups)
- Remove dead new_e_iteration member and its no-op if block
- Drop unused parameter.h include from charge_mixing_rho.cpp
* module_charge: split member functions into charge_mixing.cpp, free functions into chg_rho_detail.h
- Move mix_rho_recip/mix_rho_real/mix_rho from charge_mixing_rho.cpp to charge_mixing.cpp
- Create chg_rho_detail.h for make_twobeta_mix, pack_rho_mag, unpack_rho_mag templates and mix_tau_recip declaration
- charge_mixing_rho.cpp now only contains mix_tau_recip definition in module_charge::detail
- Restore accidentally deleted mix_uom member function
* module_charge: rename charge_{init,mixing_rho} to chg_{init,tau}, widen cube_io ofs_running to ostream
* charge_init.{cpp,h} -> chg_init.{cpp,h}: move Charge::init_rho stages
(read_rho_from_file, init_rho_atomic_and_tau, load_rho_from_restart,
init_rho_from_wfc) from Charge member functions to module_charge free
functions, dropping the corresponding private declarations from
charge.h. Continues the module_charge convention of stateless free
functions in chg_* files.
* charge_mixing_rho.cpp -> chg_tau.cpp: rename for the module_charge
short-underscore convention; the file only contains mix_tau_recip.
* Extract mix_tau_recip declaration from chg_rho_detail.h into a new
chg_tau.h so chg_tau.cpp no longer pulls in the detail template
helpers (make_twobeta_mix / pack_rho_mag / unpack_rho_mag).
charge_mixing.cpp adds chg_tau.h while keeping chg_rho_detail.h for
the template helpers it still uses.
* Widen ModuleIO::read_vdata_palgrid's ofs_running parameter from
std::ofstream& to std::ostream& (cube_io.h / read_cube.cpp). The
body only uses operator<<, so std::ostream& is sufficient; this
fixes the chg_init.cpp compile error where read_rho_file /
read_kin_file (per project rules, std::ostream&) could not bind to
the old std::ofstream& parameter. Existing callers passing
std::ofstream& (GlobalV::ofs_running, test fixture) convert
implicitly via base-class reference.
Build lists updated: source/Makefile.Objects and
source/source_estate/{CMakeLists.txt,test/CMakeLists.txt}.
Verification: chg_init.* changes compile-verified by user before
this session; chg_tau rename and chg_tau.h extraction not yet
compile-verified; cube_io type widening not yet compile-verified.
* module_charge: rename charge_mixing.{h,cpp} to chg_mix.{h,cpp}, test to test_chg_mix.cpp
Pure rename, no logic change. Updates include guard, 12 #include sites,
CMakeLists (source_estate + test), and Makefile.Objects. CMake target
MODULE_ESTATE_charge_mixing kept (no external references). Class name
Charge_Mixing and module_charge namespace unchanged.
* module_charge: remove duplicate doc block comments (Phase 1a)
Remove or rephrase 14 duplicate comment lines across 7 files to
eliminate all duplicate_doc_block quality-score deductions.
- chg_mix.cpp: remove 7 duplicate comments in mix_rho_real that
repeated mix_rho_recip's broyden/Kerker/magabs annotations
- chg_init.cpp: remove 2 duplicate comments in read_kin_file that
repeated read_rho_file's binary-read and ParaWorld bridge notes
- chg_symm_detail.cpp: remove 1 duplicate step comment in psymmg_soc
- charge.h: rephrase kin_r_save comment to avoid repetition
- chg_extra.h: rephrase beta comment to avoid repetition
- chg_symm.cpp: remove 1 duplicate vector-management comment
- chg_precond.cpp: remove 1 duplicate Kerker comment
* module_charge: replace auto with explicit std::function types (Phase 1b)
Replace 14 auto-keyword lambda declarations with explicit
std::function types to eliminate all auto_keyword quality-score
deductions.
- chg_mix.cpp: 10 auto -> std::function (inner_product, screen,
twobeta_mix in mix_rho_recip and mix_rho_real)
- chg_drho.cpp: 2 auto -> std::function<double()> (part_of_noncolin,
part_of_rho)
- chg_tools.cpp: 1 auto -> std::function<void(int,int)> (kernel)
- chg_symm_detail.cpp: 1 auto -> std::function (build_wspin)
Added #include <functional> to all four files.
* module_charge: wrap lines over 120 chars (Phase 1c)
Break 21 lines exceeding the 120-char limit across 7 files to
eliminate all line_too_long quality-score deductions.
- charge.cpp: 3 WARNING_QUIT/cout lines split
- chg_atomic.cpp: 5 Simpson_Integral/exp/assert lines split
- chg_drho.cpp: 2 conj-product sum lines split
- chg_init.cpp: 1 warning message string split
- chg_mix.cpp: 5 make_twobeta_mix/recip_to_real/if_scf_oscillate lines split
- chg_mix.h: 3 member declaration/comment lines shortened
- chg_symm_detail.cpp: 2 MPI_Recv lines split
* module_charge: remove default parameter from Charge::init_rho (Phase 1d)
Remove the default nullptr values from init_rho's klist and wfcpw
parameters and update the two call sites (esolver_of.cpp,
esolver_double_xc.cpp) that relied on the defaults to pass nullptr
explicitly.
* module_charge: replace raw new/delete with std::vector and unique_ptr (Phase 2a-2d)
Replace all raw new/delete allocations in 4 files with RAII
containers to eliminate raw_new_keyword and unpaired_new_delete
quality-score deductions.
- chg_tools.cpp: 1 new -> std::vector<double> (aux buffer)
- chg_extra.cpp: 4 new -> std::vector<std::vector<double>> (rho_atom
in extrapolate_charge and find_alpha_and_beta)
- chg_symm_detail.cpp: 14 new -> std::vector (rhog_piece, ig2isz,
ipsz2ipw, nstnz_start, fftixy2is, rhogtot, ig2isztot, ixyz2ipw
across reduce_to_fullrhog, rhog_piece_to_all, psymmg, psymmg_soc)
- chg_mix.{h,cpp}: 5 new + 5 unpaired -> std::unique_ptr for
mixing and mixing_highf members; destructor and init_mixing
simplified; get_mixing() returns .get()
charge.cpp (18 raw new) deferred to Phase 2e due to wider impact.
* module_charge: replace raw new/delete in Charge with vector-backed storage (Phase 2e)
Replace all 18 raw new and 10 unpaired delete in charge.cpp with
std::vector-backed storage to eliminate raw_new_keyword and
unpaired_new_delete deductions.
- charge.h: add _ptrs_rho, _ptrs_rhog, _ptrs_rho_save, _ptrs_rhog_save,
_ptrs_kin_r, _ptrs_kin_r_save (std::vector<double*> / complex*),
and _space_rho_core, _space_rhog_core (std::vector data buffers)
- charge.cpp allocate(): replace new double*[nspin] with vector resize;
rho = _ptrs_rho.data() preserves double** interface
- charge.cpp init_final_scf(): replace both outer pointer and inner
data new calls with _space_* vectors
- charge.cpp destroy(): replace delete[] with vector::clear() and
nullptr assignment
charge.cpp score: 47 -> 69, now passing the 60 threshold.
Module average: 85.0 -> 85.7, 30/33 files passing.
* module_charge: replace std::make_unique with C++11-compatible unique_ptr(new T) (fix)
std::make_unique is a C++14 feature; the repo baseline is C++11.
Replace 4 make_unique calls with std::unique_ptr<T>(new T(...)) to
eliminate the post_cpp11_feature deduction (-40).
chg_mix.cpp score: 0 -> 15, module average: 85.7 -> 86.1.
* module_charge: fix duplicate doc block in charge.cpp init_final_scf
* module_charge: aggregate chgmixing_ks parameters into ScfMixingCtx struct (Phase 3a)
Replace 14-parameter chgmixing_ks with 7-parameter version by
grouping SCF convergence thresholds and status flags into a new
ScfMixingCtx struct, and deriving nrxx from chr.rhopw->nrxx.
- chg_routine.h: define ScfMixingCtx struct (hsolver_error, scf_thr,
scf_ene_thr, converged_u, drho, oscillate_esolver, conv_esolver)
- chg_routine.cpp: unpack ctx members at function entry
- esolver_ks.cpp: pack ctx before call, unpack after
chg_routine.cpp score: 63 -> 70, too_many_parameters eliminated.
* module_charge: aggregate read_rho_file/read_kin_file parameters into ReadCfg (Phase 3b)
Replace 9-parameter read_rho_file and read_kin_file with 5-parameter
versions by grouping suffix, readin_dir, rank, ofs_running, ofs_warning
into a ReadCfg struct in the anonymous namespace.
chg_init.cpp score: 66 -> 70, too_many_parameters eliminated.
* module_charge: aggregate non_linear_core_correction parameters into NlcCtx (Phase 3c)
Replace 10-parameter non_linear_core_correction with 2-parameter
version by grouping all input data into a new NlcCtx struct.
chg_tools.cpp score: 96 -> 100, too_many_parameters eliminated.
* module_charge: split chg_mix.cpp into init and rho mixing files (Phase 4a)
Move mix_rho_recip, mix_rho_real, and mix_rho (440 lines) from
chg_mix.cpp into a new chg_mix_rho.cpp to eliminate file_too_long
deduction (-10).
- chg_mix.cpp: 727 -> 286 lines (constructor, set_mixing,
init_mixing, set_rhopw, mix_reset, if_scf_oscillate,
allocate_mixing_uom, mix_uom)
- chg_mix_rho.cpp: new file, 440 lines (mix_rho_recip,
mix_rho_real, mix_rho)
- CMakeLists.txt: add chg_mix_rho.cpp to library and test targets
chg_mix.cpp score: 15 -> 60, now passing the 60 threshold.
32/34 files passing, module average improved.
* module_charge: split chg_drho.cpp and decompose inner product functions (Phase 4b)
Move inner_product_recip_rho and inner_product_recip_hartree from
chg_drho.cpp into a new chg_drho_inner.cpp, and decompose each
into per-nspin helper functions to reduce cyclomatic complexity.
- chg_drho.cpp: 520 -> 161 lines (cal_drho, cal_dkin,
inner_product_real); score 49 -> 97
- chg_drho_inner.cpp: new file, 310 lines; score 100
- inner_product_recip_rho decomposed into recip_rho_nspin1,
recip_rho_nspin2, recip_rho_nspin4_mag helpers (CC 29 -> ~5 each)
- inner_product_recip_hartree decomposed into
recip_hartree_nspin2, recip_hartree_nspin4_trad,
recip_hartree_nspin4_angle helpers (CC 37 -> ~5 each)
- shared coulomb_sum_single extracted
- CMakeLists.txt: add chg_drho_inner.cpp to library and test targets
34/35 files passing, only chg_atomic.cpp remains below 60.
* refactor(module_charge): split atomic_rho and remove ZEROS in charge mixing
chg_atomic.cpp:
- Decompose atomic_rho (CC=60) into per-nspin helpers in
chg_atomic_inner.cpp; CC reduced to 7, score 40->100.
- Replace all PARAM.inp.nelec/domag/domag_z/test_charge and
GlobalV::ofs_warning with explicit AtomicRhoCfg parameter.
- Remove unused parameter.h include.
- Add chg_atomic_detail.h declaring detail helpers and RhoG3dCtx.
chg_init/chg_extra/esolver_*:
- Pass AtomicRhoCfg through call sites of atomic_rho,
extrapolate_charge, and update_delta_rho.
Bug fixes:
- chg_drho_inner.cpp: fix duplicate const (const MixingConfig const&
-> const MixingConfig&) and add detail:: prefix to helper calls.
- chg_mix_rho.cpp: use mixing.get()/mixing_highf.get() for unique_ptr.
- chg_tools.cpp: fix numeric -> numeric[it] in set_rho_core.
Memory safety / cleanup:
- Replace ModuleBase::GlobalFunc::ZEROS with std::fill in charge.cpp,
chg_symm_detail.cpp, chg_tools.cpp; remove redundant ZEROS calls
that precede full overwrites in chg_dmr.cpp and chg_mix_rho.cpp.
* Refactor: remove redundant Charge& overload of cal_rhog_symm_soc
The Charge& overload only forwarded chr.rho/chr.rhog to the raw-array
overload and had a single internal call site. Inline the member access
at that call site and drop the wrapper declaration and definition.
* module_charge: fix stale TITLE/timer labels and drop unused xc_functional.h includes
mix_tau_recip is now a free function in module_charge::detail, so update
its TITLE/timer labels from the legacy "Charge_Mixing" to "module_charge"
to match the convention of other free functions in the directory. Also
remove the unused xc_functional.h includes from chg_tau.cpp and
chg_symm_detail.cpp (label/include cleanup only, no behavior change).
* module_charge: remove redundant #ifdef __MPI guards around parallel wrappers
Parallel_Reduce::reduce_pool and Parallel_Common::bcast_double already
compile to no-op stubs when __MPI is undefined, so the outer guards add
nothing. Remove 11 such guards in chg_tools.cpp, chg_drho.cpp,
chg_drho_inner.cpp, chg_atomic_inner.cpp and chg_mix.cpp.
Guards enclosing raw MPI calls or MPI/serial dual paths are kept
(chg_parallel, chg_symm_detail, chg_routine BP_WORLD bcast, chg_extra.h).
* module_charge: decouple chg_routine from spin_constrain singleton
- forward-declare Plus_U_Base in chg_routine.h instead of including dftu_base.h
- query DeltaSpin mag_converged in ESolver_KS_PW and pass it to chgmixing_ks_pw
* module_charge: remove PARAM dependencies via explicit configuration structs
Remove the last four direct includes of parameter.h in module_charge
(chg_mix, chg_parallel, charge, chg_init) and the implicit PARAM.globalv.ks_run
read in chg_routine. INPUT values are now passed explicitly:
- MixingConfig gains scf_nmax for the drho oscillation history
- reduce_diff_pools/rho_mpi/kin_r_mpi take kpar, all_ks_run, bndpar, nspin,
out_elf from callers instead of GlobalV::KPAR/PARAM
- Charge::kin_density/allocate/check_rho/renormalize_rho/init_final_scf take
out_elf/test_charge/nelec as arguments with validation asserts
- new InitRhoCfg aggregates INPUT values for init_rho
- ScfMixingCtx gains ks_run; dm2rho takes nelec and drops its default
skip_normalize argument per governance rule 5
No behavior change: save_rho_before_sum_band now uses the member nspin
set by allocate, identical to the previously read PARAM.inp.nspin.
* module_charge: restore #ifdef __MPI guards around parallel wrapper calls
The guards removed in 7a0013848 are load-bearing for serial-built unit
tests: source_estate/test strips __MPI from test translation units via
abacus_disable_feature_definitions, but links libbase built with __MPI,
whose explicit Parallel_Reduce instantiations contain real MPI calls.
Unguarded calls in the test TUs therefore bound to MPI_Allreduce and
abort with "called before MPI_INIT", failing MODULE_ESTATE_charge_test
and MODULE_ESTATE_charge_mixing.
Restore all 11 call-site guards in chg_tools.cpp, chg_atomic_inner.cpp,
chg_drho.cpp, chg_drho_inner.cpp and chg_mix.cpp. No behavior change for
MPI or serial production builds.
* Remove dead PAW compensation charge members
nhat, nhat_save in Charge and nhat_mdata in Charge_Mixing have had
no references since #6225 removed the PAW code; drop the orphaned
declarations and update the related comment.
* Refactor: remove unused Charge::prenspin member
prenspin recorded the spin-channel count read from legacy cube charge
files and drove collinear-to-noncollinear rearrangement in init_rho.
After read_rho was replaced by binary read_rhog (#5323, #5362) the
value is neither written nor read anywhere, so drop the dead member.
* Refactor: move Charge::cal_rho2ne/check_rho to module_charge free functions
- Add module_charge::check_rho in chg_tools.{h,cpp} with grid/geometry
parameters passed explicitly; preserve all branches, thresholds and
warning/abort messages of Charge::check_rho
- Remove the Charge::cal_rho2ne forwarding wrapper and Charge::check_rho
- Update the three esolver call sites (ks/of/double_xc) to pass rho,
nspin, rhopw grid sizes and ucell.omega explicitly
- Drop the check_rho stubs in elecstate_pw/base tests and switch
charge_test to the free cal_rho2ne
- Add test_chg_tools.cpp covering cal_rho2ne, total/spin-polarized
checks, mismatch warning path and negative-channel aborts
* Refactor: remove redundant Charge::omega_ pointer
- Charge::sum_rho() now reads the cell volume from rhopw->omega, which
is computed from the same lat0/latvec as ucell.omega and is already
dereferenced on the same line for nxyz; this also makes the volume
consistent with the grid rho lives on
- Drop the Charge::omega_ member, its set_omega() setter and the
chg_init.cpp call site, removing a raw-pointer dependency on the
UnitCell lifetime; update charge_test accordingly
Verified: MODULE_ESTATE_charge_test and MODULE_ESTATE_chg_tools pass,
elecstate library rebuilds cleanly.
* Remove dead Charge::init_final_scf and allocate_rho_final_scf
init_final_scf has had no production callers since the nscf refactor
(c6ae01236); its only remaining caller was the unit test added in
ba8b7ce9a. After the vector-backed storage refactor it was also a
broken duplicate of Charge::allocate: it never set nspin/nrxx/nxyz/
ngmc and skipped the kin_r buffers. Remove the function, its one-shot
guard flag, and the corresponding test case; destroy() now keys solely
on allocate_rho since vector storage self-manages cleanup.
* Refactor: pass rhopw explicitly to chg_init/chg_routine/chg_extra/chg_symm
Remove implicit reads of chr.rhopw/chr.ngmc from four module_charge files:
- chg_symm.cpp: size kin_g by the rho_basis used for its FFTs
- chg_routine: chgmixing_ks takes const PW_Basis&
- chg_init: orchestrator and four stage helpers take const PW_Basis&;
the Charge::init_rho member signature is unchanged
- chg_extra: extrapolate_charge/update_delta_rho take const PW_Basis&
Call sites pass *chr.rhopw at the KS boundary or *pw_rhod where the
binding (esolver_fp.cpp chr.set_rhopw(pw_rhod)) makes them identical.
Verified: affected TUs compile and MODULE_ESTATE_charge_extra passes.
* Comments: add TODOs for LCAO+USPP double-grid follow-ups
Record the smooth/dense grid split to revisit if LCAO is ever allowed
with USPP: symmetrize_rho callers pass different grids, and the
ndx/ndy/ndz input path lacks the LCAO guard the ecutrho path has.
* Refactor: replace sticky Charge::cal_elf flag with explicit symm_kin argument
cal_elf was set to true once during ELF output and never reset, so every
later density symmetrization in the same run redundantly symmetrized
kin_r. Replace the mutable workflow flag with an explicit bool parameter
on the Charge& overload of module_charge::cal_rhog_symm:
- ctrl_output_fp passes true right before write_elf consumes kin_r
- symmetrize_rho wrapper and other callers pass XC_Functional::get_ked_flag()
Verified: full incremental build, read_wf2rho unit tests (serial/4 MPI),
write_elf logic test, and tests/01_PW/scf_out_elf (E difference 5e-10 eV,
ELF cube passes CompareFile.py at 3-decimal tolerance).
* Refactor: resolve mixing_tau at config assembly, drop XC dependency from chg_mix
esolver_ks now resolves mix_cfg.mixing_tau = inp.mixing_tau &&
XC_Functional::get_ked_flag() at the single production config assembly
point, so chg_mix/chg_mix_rho no longer query the XC global inside tau
mixing branches (6 sites). test_chg_mix mirrors the resolution in
make_cfg() and sets ked_flag before set_mixing where tau mixing is
expected. Also drop an unused xc_functional.h include from
chg_drho_inner.cpp.
Verified: full incremental build clean; MODULE_ESTATE_charge_mixing
11/11 tests pass; MODULE_ESTATE_charge/chg test suites all pass
(serial + 4-rank MPI).
* Fix: restore complete types in chg_drho_inner.cpp after include removal
Removing xc_functional.h in 87b818f4c broke compilation: the include was
load-bearing transitively, supplying the complete ModulePW::PW_Basis type
and ModuleBase::TITLE. Add the direct includes instead (pw_basis.h,
global_function.h) per IWYU.
Verified: make -j16 exits 0 with full log retained (previous verification
was invalid: a tail pipe masked both the exit code and the errors).
* Refactor: derive tau symmetrization/reduction from kin_r buffer existence
The Charge& cal_rhog_symm overload and rho_mpi/kin_r_mpi queried
XC_Functional::get_ked_flag() (plus a caller-supplied out_elf/symm_kin
flag) to decide whether to touch kin_r. Since Charge::allocate allocates
kin_r exactly when meta-GGA or ELF output needs it, both now check
chr.kin_r != nullptr directly, dropping the XC dependency and the extra
boolean parameters:
- rho_mpi/kin_r_mpi lose the out_elf parameter (2 production, 3 test
call sites updated)
- the Charge& cal_rhog_symm overload loses the symm_kin parameter
(ctrl_output_fp, setup_pot, read_wf2rho, update_state_rdmft revert to
4 arguments); the raw-pointer overload now checks kin_r != nullptr
only
- module_charge keeps XC references only in charge.cpp, chg_init.cpp,
chg_drho.cpp (semantic "is meta-GGA" sites, resolved next)
Verified: make -j16 exit 0; 14/14 ctest charge/elecstate/read_wf2rho
tests (serial + 4-rank MPI); tests/01_PW/scf_out_elf integration case
reproduces the reference energy (-194.623411265 eV, diff 5e-10) and the
ELF cube passes CompareFile.py at 3-decimal tolerance.
* Refactor: remove module_xc dependency from module_charge (meta_gga state)
module_charge queried XC_Functional::get_ked_flag() at 5 semantic
"is meta-GGA" sites (tau TF init, tau file read, tau save, tau residual,
tau mixing resolution). Resolve the flag at upper layers instead:
- Charge::allocate takes an explicit meta_gga argument and stores it as
object state; save_rho_before_sum_band and cal_dkin read it
- InitRhoCfg gains a meta_gga field, filled at the 3 esolver config
assembly points (ks/of/double_xc)
- delete Charge::kin_density(); 6 esolver call sites inline
get_ked_flag() || (out_elf[0] > 0) for buffer allocation and pass
get_ked_flag() as meta_gga; non-SCF allocations pass false
- charge_test mirrors the inline expression
module_charge now has zero references to module_xc.
Verified: make -j16 exit 0 (full log); 14/14 charge/elecstate/
read_wf2rho ctests (serial + 4-rank MPI), including the mGGA tau mixing
and tau-save branches; tests/01_PW/scf_out_elf reproduces reference
energy (-194.623411265 eV, diff 5e-10) and the ELF cube passes
CompareFile.py at 3-decimal tolerance. A SCAN integration case
(205_PW_SCAN) still requires a libxc-enabled build/CI run.
* Fix: allow null rho buffers on ranks with empty real-space grid partition
pack_rho_mag/unpack_rho_mag in chg_rho_detail.h quit whenever any buffer
pointer is null. A rank may legitimately own zero real-space grid points
(nrxx == 0) when the grid is decomposed across more processes than it has
z-slabs (e.g. a 3x3x3 big-cell grid on 4 processes leaves one rank with
no slab); its zero-sized vectors then return null data() pointers even
though the packing loops perform no access. The unconditional check made
LCAO nspin==2 real-space mixing abort with "pack_rho_mag pointer is null"
on such ranks.
Restrict the null-pointer check to n > 0, matching the convention already
used by Parallel_Grid::reduce (only a null buffer with a non-zero size is
a genuine bug). n < 0 remains a hard error. Regression introduced in
d9685d4eb when the inline packing loops were extracted into these helpers.
* Refactor: move rhog_io into module_charge as chg_rhog_io
Relocate source_estate/rhog_io.{h,cpp} to source_estate/module_charge/
under the module_charge namespace, rename include guard to CHG_RHOG_IO_H,
and update the warning tags emitted at runtime. Update both callers
(chg_init.cpp, esolver_fp.cpp) and build files; adapt test_rhog_io.cpp in
place ahead of its move in a follow-up commit. No behavior change.
* Refactor: create module_charge/test with the rhog io unit test
Move test_rhog_io.cpp into module_charge/test/test_chg_rhog_io.cpp with
its support data charge-density.dat, register the new test subdirectory,
and rename the target to MODULE_CHARGE_rhog_io. Remove the migrated
AddTest block from the legacy source_estate/test/CMakeLists.txt.
* Refactor: move charge and charge-extra unit tests into module_charge/test
Rename charge_test.cpp to test_charge.cpp and charge_extra_test.cpp to
test_chg_extra.cpp per the test naming rule, move prepare_unitcell.h
alongside its only users, and register MODULE_CHARGE_charge /
MODULE_CHARGE_extra in the module_charge test CMakeLists. No test data
moves: prepare_unitcell.h only sets file-name strings at runtime, and
the extra test only writes cube files into ./support/.
* Refactor: move mix, parallel and tools unit tests into module_charge/test
Relocate test_chg_mix.cpp (fixing its relative includes), test_chg_parallel.cpp
and test_chg_tools.cpp into module_charge/test, register MODULE_CHARGE_tools /
MODULE_CHARGE_mix / MODULE_CHARGE_parallel with the 4-process mpirun test,
and drop the migrated blocks from the legacy source_estate/test CMakeLists.
* Refactor: rename module_charge test dir to unittests and wire CI for it
Rename source_estate/module_charge/test to unittests (relative CMake
paths are immune to the move). Sync the referencing points: the
add_subdirectory call, the coverage lcov filter (add '*/unittests/*' so
test sources stay excluded from the report), a dedicated Module_Charge
ctest step in test.yml with MODULE_CHARGE added to the catch-all -E
list to avoid double execution, and unittests/ added to the
code_quality_score.py SKIP_DIRS.
* Fix: pass ucell.omega to Charge::sum_rho/renormalize_rho to fix NPT stress
Root cause: commit 34b441e1c ("Refactor: remove redundant Charge::omega_
pointer") changed Charge::sum_rho() to read the cell volume from
rhopw->omega instead of ucell.omega. In variable-cell calculations (NPT),
pw_rho/pw_rhod are NOT rebuilt on cell change (only pw_wfc is), so
rhopw->omega keeps the initial cell volume while ucell.omega is updated
every MD step. The stale volume made sum_rho() return a wrong electron
count, which made renormalize_rho() scale rho by the wrong factor,
corrupting the stress (deviation ~0.002 in 095_PW_NPT) while the total
energy stayed near-correct (variational, second-order sensitive).
Fix: add an explicit omega parameter to Charge::sum_rho() and
renormalize_rho(); all call sites (init_scf, chg_routine, LCAO dm2rho path
through HSolverLCAO/dmToRho, RDMFT update_charge, OFDFT renormalize_psi)
now pass ucell.omega. This mirrors the existing check_rho(..., ucell.omega)
pattern.
Also mark three other rhopw->omega users with BUG(investigate) comments:
get_local_pp_energy, cal_delta_escf, and Makov-Payne correction. These are
pre-existing and were not changed by the refactor; they may have the same
stale-volume issue in NPT and should be investigated separately.
Bisected to 34b441e1c over the 20260916 module_charge refactor branch.
* Fix: add omega arg to remaining dm2rho call sites
Missed four LCAO_domain::dm2rho call sites in the previous commit:
- lcao_set.cpp init_chg_dm (skip_normalize=true, omega unused)
- esolver_dm2rho.cpp
- esolver_ks_lcao_tddft.cpp weight_dm_rho
- module_dm/init_dm.cpp
All now pass ucell.omega.
* Fix: restore HamiltHSMatrix hs declaration in cal_mw_from_lambda
Accidentally removed the line while editing the comment.
* Refactor: merge Charge_Mixing::set_rhopw into set_mixing
Fold the smooth/dense PW_Basis pointer assignment into
Charge_Mixing::set_mixing so grid injection happens together with the
rest of the mixing configuration, and remove the now-redundant
set_rhopw setter. Update the esolver_ks call site and unit tests
accordingly.
* Disable ref_cell_factor != 1.0 and skip 095_PW_NPT test
The reference-cell mechanism (ref_cell_factor > 1) has design problems:
when ref_cell_factor != 1, PW_Basis::lat0/tpiba/G/GGT/omega hold
reference-cell values, but external code (sum_rho, get_local_pp_energy,
cal_delta_escf, makov_payne, wfc IO, DFPT, OFDFT) reads them as
physical-cell quantities, producing wrong charge/energy integration in
variable-cell (NPT) calculations. The bug manifests as a ~16% overestimate
of rhopw->omega on the first MD step (reference-cell volume instead of
actual-cell volume) and silently wrong stress/energy values.
Since properly fixing this requires refactoring PW_Basis to separate
the reference-cell FFT grid (nx/ny/nz) from the physical-cell lattice
quantities (lat0/tpiba/G/GGT/omega), temporarily disable the feature:
- read_input_item_md.cpp: WARNING_QUIT if ref_cell_factor != 1.0
- input_parameter.h: FIXME comment explaining the disable and the
refactor required to re-enable
- setup_pwrho.cpp / setup_pwwfc.cpp: NOTE comments at the five
initgrids(ref_cell_factor * ucell.lat0, ...) call sites explaining
the staleness issue and the initgrids_ref/initgrids_actual split
needed when the feature is restored
- tests/01_PW/CASES_CPU.txt and CASES_GPU.txt: skip 095_PW_NPT
(its INPUT sets ref_cell_factor=1.05, which is now blocked)
The 3 BUG(investigate) markers in estate_e_terms.cpp,
elecstate_energy.cpp, and makov_payne.cpp are left in place as
remainders that those call sites also need review when the reference-
cell mechanism is re-enabled.
* module_dm: remove duplicate doc block comments (Phase 1a)
Delete 29 repeated comment lines across 4 files. No logic changes.
- density_matrix.cpp: 17 dups in cal_DMR_td, cal_DMR_full, gamma-only cal_DMR
- density_matrix.h: 4 dups in constructors, cal_DMR_td/full docs, _DMR_grid docs
- density_matrix_io.cpp: 4 dups across init_DMR overloads
- cal_dm_psi.cpp: 4 dups in complex overload
* module_dm: replace auto with explicit types (Phase 1b)
Replace 8 auto occurrences with explicit C++11 types in 3 non-test files.
- density_matrix.cpp: auto& it -> hamilt::HContainer<TR>*& it
- density_matrix_io.cpp: 4x auto& it, 2x auto tau1 -> Vector3<double>
- cal_edm_tddft.cpp: auto Sinv_dev -> ct::Tensor Sinv_dev
* module_dm: unindent preprocessor directives (Phase 1c)
Move 20 indented #ifdef/#pragma/#endif directives to column 0 in
density_matrix.cpp. No logic changes.
* module_dm: wrap lines over 120 chars (Phase 1d)
Break 23 long lines across 4 files. No logic changes.
- density_matrix.cpp: 13 lines (constructors, template specializations,
func_xyz_to_updown body)
- density_matrix.h: 6 lines (declarations, friend declarations)
- cal_dm_psi.h: 2 lines (psiMulPsiMpi/psiMulPsi declarations)
- cal_dm_psi.cpp: 2 lines (psiMulPsiMpi/psiMulPsi definitions)
* module_dm: convert tab indentation to spaces (Phase 1e)
Convert 39 tab-indented lines to 4-space indentation in 3 files.
- init_dm.cpp: 25 lines
- density_matrix.h: 12 lines
- init_dm.h: 2 lines
No logic changes.
* module_dm: remove default parameters (Phase 1f)
Remove 4 default parameter values from 2 header files and update 35
call sites across 19 files to pass explicit values.
- density_matrix.h: cal_DMR, cal_DMR_td, cal_DMR_full (ik_in=-1 removed)
- cal_edm_tddft.h: print_local_matrix (matrix_name="", rank=-1 removed)
Call sites updated: cal_DMR()->cal_DMR(-1), cal_DMR_td(...)->(...,-1),
cal_DMR_full(&x)->(&x,-1)
No logic changes.
* module_dm: replace raw new/delete with unique_ptr in density_matrix_io (Phase 2a)
- Add clear_DMR() private method to DensityMatrix, consolidating 4
repeated delete-loop patterns
- Destructor now calls clear_DMR() instead of inline delete loop
- 4 init_DMR overloads: replace delete loops with clear_DMR(), wrap
7 raw new in std::unique_ptr, use .release() when storing into _DMR
- Add #include <memory> to density_matrix_io.cpp
No logic changes.
* module_dm: replace raw new[]/delete[] with std::vector in cal_edm_tddft (Phase 2b)
Replace 7 raw new[] + 7 delete[] with std::vector in cal_edm_tddft.cpp.
- MPI path: 6x new complex<double>[nloc] -> vector + .data() for ScaLAPACK
- Serial path: 1x new complex<double>[lwork] -> vector + .data() for LAPACK
3 reset(new ...) calls in shared_ptr left unchanged (already owned).
No logic changes.
* module_dm: replace raw new[]/delete[] with std::vector for dmr_tmp_ (Phase 2c)
- Change dmr_tmp_ member from TR* to std::vector<TR>
- Remove delete[] from destructor (vector auto-destructs)
- switch_dmr: nullptr checks -> .empty(), new TR[size] -> .resize(size),
allocate(dmr_tmp_,...) -> allocate(dmr_tmp_.data(),...)
No logic changes.
* module_dm: eliminate PARAM global dependency in density_matrix.cpp (Phase 3a)
Replace 10 PARAM references with local/parameter alternatives:
- 9x PARAM.inp.nspin -> dm._nspin (already available via DensityMatrix ref)
- 1x PARAM.inp.td_stype==2 -> !phase_hybrid.empty() (semantic equivalent)
No logic changes.
* module_dm: eliminate PARAM dependency in init_dm (Phase 3b)
Introduce Init_DM_Config struct to pass esolver_type, td_stype, nspin,
nelec explicitly instead of reading global PARAM.
- init_dm.h: add struct Init_DM_Config, update init_dm signature
- init_dm.cpp: 5x PARAM.inp.* -> cfg.*, update explicit instantiations
- esolver_ks_lcao.cpp: update call site to pass config struct
No logic changes.
* module_dm: eliminate PARAM.globalv.nlocal in cal_edm_tddft (Phase 3c)
Replace 3x PARAM.globalv.nlocal with pv.nrow (equivalent for LCAO
square matrix). Remove now-unused parameter.h include.
module_dm is now fully free of global PARAM/GlobalV/GlobalC deps.
No logic changes.
* module_dm: split density_matrix.cpp into density_matrix + dmr_cal (Phase 4a)
Extract DMR calculation functions (cal_DMR, cal_DMR_td, cal_DMR_full
templates and specializations) from density_matrix.cpp into new
dmr_cal.cpp. Update CMakeLists.txt.
- density_matrix.cpp: 707 -> 290 lines
- dmr_cal.cpp: new, 430 lines
No logic changes.
* module_dm: split cal_edm_tddft.cpp into cal_edm_tddft + cal_edm_tddft_lapack (Phase 4b)
Extract cal_edm_tddft_tensor and cal_edm_tddft_tensor_lapack (plus the
explicit template instantiations for CPU/GPU) into a new translation unit
cal_edm_tddft_lapack.cpp so cal_edm_tddft.cpp keeps only print_local_matrix
and the ScaLAPACK-based cal_edm_tddft driver. cal_edm_tddft.cpp drops from
819 to 309 lines. Wire the new file through source_estate/CMakeLists.txt.
* module_dm: cleanup after Phase 4 split (dead code, typo, build fixes)
- Remove unused print_local_matrix and cal_edm_tddft_tensor from
cal_edm_tddft.{h,cpp} and cal_edm_tddft_lapack.cpp (no callers).
- Fix missing #endif for the __CUDA guard in cal_edm_tddft_lapack.cpp
that previously swallowed the namespace close.
- Fix Tk -> TK typo in dmr_cal.cpp (2 occurrences).
- Update test_cal_dm_r.cpp to pass explicit -1 to cal_DMR / cal_DMR_full
after Phase 1f removed the default parameters.
- Add dmr_cal.cpp to test CMakeLists for MODULE_ESTATE_dm_cal_DMR_test,
dftu_lcao_test, and deepks_unit_support so the cal_DMR specialization
is linked (Phase 4a moved it out of density_matrix.cpp).
* Fix: close_kerker_gg0 actually disables Kerker; drop dead mixing_gg0 members
The chg_precond refactor (commit 6d127d517) made the Kerker kernels read
cfg_ (immutable INPUT snapshot) instead of Charge_Mixing members, but
close_kerker_gg0() kept writing the now-dead mixing_gg0/mixing_gg0_mag
members. As a result, the non-separate-loop EXX path in exx_lri_interface.hpp
silently failed to disable Kerker after convergence.
Fix: add a kerker_disabled_ flag on Charge_Mixing that the mix_rho_recip/
mix_rho_real screening lambdas short-circuit on. The flag lives on the
object, not in cfg_, so the immutable INPUT snapshot invariant is preserved.
Also drop the now-dead members mixing_gg0/mixing_gg0_mag/mixing_gg0_min/
mixing_angle/mixing_dmr and the get_mixing_gg0() getter; set_mixing/init_mixing
now read these from cfg_ directly. Add CloseKerkerGg0DisablesScreenReal
regression test that compares close_kerker_gg0() output against the
cfg.mixing_gg0=0 baseline and proves the flag is load-bearing.
* Fix: relax over-strict null-buffer asserts for empty grid partitions
reduce_diff_pools and Parallel_Grid::reduce_across_pools still forbade
null buffers unconditionally, contradicting the rule documented at
parallel_grid.cpp:355-360. A rank with nrxx == 0 may legitimately hold
a null rho/kin_r pointer; the MPI calls below use count 0 and ignore
the buffer. Align both call sites with the documented rule.
* Fix: relax over-strict null-buffer assert in ParaRgridWorld::reduce_across_pools
Same pattern as the previous fix: a rank with nrxx == 0 legitimately
holds a null buffer, and MPI_Allreduce with count 0 ignores it.
Align with the rule documented at parallel_grid.cpp:355-360.
* Fix: allow nnr == 0 in DMR mixing for empty MPI partitions
nnr is local to each MPI rank and may legitimately be zero when no
atom pairs survive the cutoff on that rank. The previous check
aborted DMR mixing for such distributions, whereas the historical
implementation allowed empty blocks. Relax the guard in
check_dmr_inputs() and init_mixing_dmr() to reject only negative
nnr, and require non-null DMR buffers only when nnr > 0, matching
the established nrxx == 0 convention in module_charge.
* Fix: split reciprocal rho copy from real-space |m| rescale in mix_rho_recip
The nspin==4 && mixing_angle>0 branch of mix_rho_recip mixed two
distinct operations in one loop bounded by npw, but rho_magabs is
sized nrxx (real-space) and the new |m| is written back by
recip2real into rho_magabs[0..nrxx-1]. Reading rho_magabs[npw+ig]
goes out of bounds once npw+ig >= nrxx (AddressSanitizer reproduces
with nrxx=125, npw=93) and the loop bound npw leaves the real-space
tail [npw, nrxx) of {mx,my,mz} unscaled. Split into two loops: the
reciprocal rho copy stays bounded by npw, the magnetization rescale
is bounded by nrxx and reads rho_magabs[ir].
* Refactor: remove unused Charge_Mixing::conserve_setting
conserve_setting() was introduced by 420f1ad00 (DeltaSpin feature
merge, 2026-06-15) but never wired up: no production caller, no
test reference, and the DeltaSpin module does not touch
Charge_Mixing. Drop the dead declaration per the project rule that
unused functions and their tests be removed.
* Refactor: drop dead Charge_Mixing::tpiba2 member
tpiba2 was declared in chg_mix.h but never assigned by set_mixing()
nor read anywhere in the module. Grep across the whole source tree
confirms all tpiba2 references are either ucell.tpiba2 (a separate
UnitCell member) or local variables in unrelated modules. The
Charge_Mixing class never computed or used its own tpiba2 pointer;
only tpiba is consumed by the stateless Kerker kernels via
mix_rho_recip/mix_rho_real. Remove the dead declaration.
* Refactor: route Charge_Mixing getters through cfg_
get_mixing_mode(), get_mixing_beta(), get_mixing_ndim() previously
returned the legacy mirror members that set_mixing() kept in sync
with cfg_ by hand. With cfg_ now treated as the immutable INPUT
snapshot, route the public getters through cfg_ directly so there
is a single source of truth for INPUT parameters. External callers
(esolver_ks_lcao, lcao_others, pw_others) are unaffected since
signatures are unchanged. The legacy members remain in place for
now; they are dropped in a later step after internal readers are
migrated.
* Refactor: init_mixing constructs Mixing from cfg_ not legacy mirrors
init_mixing() branched on this->mixing_mode and passed
this->mixing_ndim/mixing_beta to the Broyden/Pulay/Plain_Mixing
constructors. These legacy mirrors were kept in sync with cfg_
manually by set_mixing(). Route through cfg_ directly so cfg_
remains the single source of INPUT parameters. The Mixing objects
themselves still copy beta/ndim into their own members at
construction; that is a one-time snapshot and not a continuous
sync surface, so it is left untouched.
* Refactor: mix_rho_recip/mix_rho_real read mixing_beta from cfg_
Both mix_rho_recip and mix_rho_real built the twobeta_mix functor by
reading this->mixing_beta / this->mixing_beta_mag, which are legacy
mirrors that set_mixing() kept in sync with cfg_. Route the six
construction sites through cfg_.mixing_beta / cfg_.mixing_beta_mag
so cfg_ is the single source of INPUT parameters consumed by the
mixing logic. Behavior is unchanged since the mirrors and cfg_
hold identical values after set_mixing().
* Refactor: set_mixing stops mirroring cfg_ into legacy members
set_mixing() copied mixing_mode, mixing_beta, mixing_beta_mag,
mixing_ndim from cfg into legacy mirror members, then validation
and logging read from the mirrors. Now that all internal readers
(init_mixing, mix_rho_recip, mix_rho_real, getters) read from
cfg_, the mirror writes are dead work. Drop them and route
validation and log output through cfg_ directly. omega and tpiba
remain pointer members because they alias external runtime state
(cell volume, lattice constant) that changes across SCF iterations
and so do not belong in MixingConfig (an immutable INPUT snapshot).
* Refactor: drop legacy Charge_Mixing mirror members; cfg_ is single source
Drop mixing_mode, mixing_beta, mixing_beta_mag, mixing_ndim mirror
members. After the previous commits every internal reader (getters,
init_mixing, mix_rho_recip, mix_rho_real, set_mixing validation
and log output) routes through cfg_, so the mirrors are dead state
that set_mixing() no longer writes. cfg_ is now the single source
of truth for INPUT mixing parameters.
Update test_chg_mix.cpp accordingly: the two assertions that
reached directly into CMtest.mixing_beta_mag and CMtest.mixing_mode
now read CMtest.get_mixing_config().mixing_beta_mag and
CMtest.get_mixing_mode(), matching the public API used by the
other assertions in the same block. No production caller accessed
these members directly (esolver_ks_lcao, lcao_others, pw_others
all used the getters), so the change is test-only on the consumer
side.
* Refactor: drop NSDMI from MixingConfig to force explicit construction
The non-static data member initializers in MixingConfig provided
plausible-looking defaults (e.g. mixing_beta=0.8, mixing_mode=
"broyden") that silently masked forgotten fields when a new field
was added but not wired up at construction sites. With the
defaults removed, every construction site must use aggregate
initialization (or copy-assign from a fully-initialized instance),
and a missing field yields value-initialized (zero/empty) members
that are far more likely to trip a test than the old defaults.
Combined with -Wmissing-field-initializers promoted to error in
the next commits, adding a field to MixingConfig without updating
all aggregate-initialization sites becomes a compile error.
* Refactor: aggregate-init MixingConfig in esolver_ks with pragma guard
Convert the 17-line field-by-field assignment of mix_cfg into a
single aggregate initialization in declaration order. Wrap it in
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating this list
becomes a compile error rather than silently using a default.
Each initializer is annotated with the field name it corresponds
to, making the declaration-order dependency auditable at a glance.
* Refactor: aggregate-init MixingConfig in test_chg_mix with pragma guard
Convert make_cfg()'s 17-line field-by-field assignment into a
single aggregate initialization in declaration order, matching
the esolver-side change. Wrap in the same
#pragma GCC diagnostic error "-Wmissing-field-initializers" so
that adding a field to MixingConfig without updating the test
helper is also a compile error. Both construction sites (esolver
and test) now fail at compile time if a field is missing, closing
the maintenance gap where a new field could silently fall back to
a default value.
* Fix: fail-fast guards in Charge_Mixing and update chg_mix tests
Add validation to turn latent misuse (skipped set_rhopw/set_mixing)
into clear WARNING_QUIT errors instead of null dereference or heap
corruption:
- init_mixing rejects a null rhopw
- if_scf_oscillate checks scf_nmax > 0 and iteration range
- mix_rho validates chr/chr->rhopw and the grid pointers
Fix three chg_mix unit tests that read cfg_ before set_mixing, which
caused a SIGSEGV in SCFOscillationTest and assertion failures in the
two inner-product tests.
* test(module_charge): add unit tests for chg_uspp and chg_dmr
Add test_chg_uspp.cpp covering split_dgrid/merge_dgrid (normal split,
round-trip, nspin=1/2, empty high-frequency/smooth boundaries, and
input-validation abort paths).
Add test_chg_dmr.cpp covering init_mixing_dmr/mix_dmr (nspin=1/2/4
mixing with Plain_Mixing analytically verified, empty-partition null
buffer allowance, and input-validation abort paths).
Wire both targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_precond, chg_drho, chg_drho_inner, chg_mix_rho
- test_chg_precond.cpp: kerker_screen_recip/real (early return, nspin=1/2/4
filter, nspin=4 with mixing_angle resize, real-space matches reciprocal).
- test_chg_drho.cpp: inner_product_real, cal_drho real-space path
(nspin=1/2/4+domag_z), cal_dkin (meta_gga false/true).
- test_chg_drho_inner.cpp: inner_product_recip_rho and
inner_product_recip_hartree for nspin=1 with a single G component,
analytically verified against the Coulomb metric.
- test_chg_mix_rho.cpp: mix_rho abort paths (null chr/chr->rhopw, unset
rhopw, double_grid without rhodpw) and real-space plain mixing value.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add unit tests for chg_symm, chg_symm_detail, chg_atomic, chg_atomic_inner
- test_chg_symm.cpp: symmetrize_rho / cal_rhog_symm / cal_rhog_symm_soc
no-op paths when symm_flag == 0, for nspin=1 and nspin=4.
- test_chg_symm_detail.cpp: psymmg and psymmg_soc idempotence on a
manually built D_4 point group over a serial cubic PW_Basis.
- test_chg_atomic_inner.cpp: compute_rhoatm USPP direct-copy branch and
NCPP integrate+scale-to-zv branch (Gaussian rho_at with known analytic
integral); normalize_and_check renormalizes uniform density to nelec.
- test_chg_atomic.cpp: atomic_rho ntype==0 path (skips atom loop) and
spin_number_need==3 abort path.
Wire all four targets into unittests/CMakeLists.txt.
* test(module_charge): add chg_tau/chg_routine/chg_init tests; drop spurious XC_Functional stubs
Fourth batch of module_charge unit tests:
- test_chg_tau.cpp: mix_tau_recip abort paths (null chr/grid/mixing, nspin<1,
double_grid without high-f mixer) and non-double-grid plain mixing value.
- test_chg_routine.cpp: chgmixing_ks_pw/lcao iter==1 restart-step setup, and
chgmixing_ks convergence branches (conv_esolver true / drho<hsolver_error
skip mix_rho).
- test_chg_init.cpp: init_rho "wfc" with null wfcpw abort, and "atomic" with
ntype==0 + meta_gga Thomas-Fermi tau initialization.
Wire all three targets into unittests/CMakeLists.txt.
Cleanup: remove the XC_Functional::func_type /…
…evicting the ccache (#8048) Two independent costs in the per-pull-request CI, both measured from recent runs on deepmodeling/abacus-develop. GPU suites ran one case at a time. #8006 gave the CPU suites "-j auto", but cuda.yml calls Autotest.sh directly and Autotest.sh defaults to njobs=1, so every GPU log says "Concurrent test cases: 1". The runner has 12 cores and one V100-32GB while a GPU case is a few atoms: in run 35979396993 the 01_PW sweep spent 366 s on 97 cases whose median was 2.1 s, and the eight GPU steps together took 1006 s of the 31-minute job. Pass "-j 4" to all of them, which puts 4 cases side by side and leaves every rank a core of its own (Autotest.sh derives the OpenMP width from -n and -j). tests/11_PW_GPU/CMakeLists.txt gets the "-j auto" that the CPU suites already carry, so the ctest target matches. The compiler cache was being thrashed. actions/cache saves on every run, and a cache saved from a pull request is scoped to that pull request's ref, so no other branch can read it -- but it still counts against the repository's 10 GB budget. The repository sat at 11.7 GB with 7 of its 8 entries scoped to refs/pull/*, which had evicted the develop entry that every pull request actually restores from. The Build step in the last 19 runs of test.yml ranged from 104 s to 1612 s, three of them full cold builds. Split the step into actions/cache/restore plus an actions/cache/save that only develop runs, so one warm baseline survives instead of being pushed out by per-pull-request copies that nothing else can read. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
XC_Functional_Libxc::v_xc_meta accumulated the vsigma (density-gradient) part of vtxc with the density returned by convert_rho, i.e. rho_valence + rho_core, which already contains the NLCC core charge. The vrho and vlaplacian parts use chr->rho, and so does the internal GGA path, so only meta-GGA functionals ever violated the intended definition vtxc = \int v_xc * rho_valence. vtxc enters the analytical stress through -(etxc - vtxc)/omega, so the extra core contribution shifted the three normal stress components of meta-GGA + NLCC calculations by an isotropic +42 to +46 kbar, while GGA + NLCC and meta-GGA without NLCC stayed correct. The total energy and the finite-difference stress do not contain vtxc, so the defect only showed up in the analytical stress. Add a unit test that feeds v_xc_meta a vanishing valence density together with a non-vanishing NLCC core charge. Every contribution to vtxc is weighted by the valence density, so vtxc has to vanish exactly; before the fix the vsigma term left 0.210 (nspin=1) and 0.212 (nspin=2) behind. Add the integration case 01_PW/214_PW_SCAN_NLCC, which is 205_PW_SCAN with an NLCC Si pseudopotential (Si_dojo_nsoc.upf). Its totalstressref is off by 144.985 kbar before this fix, while the existing meta-GGA stress cases use NLCC-free pseudopotentials and therefore never covered this path.
…o spaces (#8011) * docs: fix two @file tags that name a different file Doxygen's @file takes the name of the file it documents. These two name a file that does not exist, so Doxygen attributes the block to the wrong (or to no) file: source_base/ndarray.h said NDArray.h source_lcao/module_rt/band_energy.h said bandenegy.h (also a typo) Found by tools/03_code_analysis/code_quality_score.py (rule doc_file_mismatch). The scan also flagged source_pw/module_pwdft/radial_proj.h (said radial_projection.h), but #8007 has since moved that header to source_pw/module_proj/ with the tag already corrected, so it is no longer part of this commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * style: convert leading tab indentation to spaces in 246 source files .editorconfig has required `indent_style = space` with `indent_size = 4` for the whole repository, but 339 files under source/ still indent with tabs. This converts the leading whitespace of 246 of them. 253 files were selected when this was prepared. Since then, develop has deleted two of them and already converted five others, which leaves 246: - deleted: source_lcao/setup_dm.h (#8000), source_pw/module_stodft/hamilt_sdft_pw.cpp (#8012); - already converted: source_base/module_out/binstream.{h,cpp} (#8025), source_pw/module_stodft/sto_hamilt_pw.h (renamed from hamilt_sdft_pw.h in #8012), source_io/module_ctrl/ctrl_output_pw.h and source_pw/module_pwdft/op_pw_nl.cpp (#8043). Files that develop moved are converted at their new path, e.g. onsite_proj_tools_stress.cpp is now under source_pw/module_proj/ (#8007). Scope. Three groups were deliberately left out: - 48 files touched by the pull requests open at the time of writing (#8000, #7924, #7906, #8005 and others), so this does not force a conflict on work in flight; - 5 vendored files: source_base/libm/ is ported from glibc-2.36 and carries its own LICENCE, and source_base/mcd.c is Softpixel MemCheckDeluxe under a BSD licence. Reformatting vendored sources makes future syncs with their upstream harder; - tabs that appear after the first non-blank character (alignment tabs, 1193 lines). Only leading indentation is converted. Method and verification. `expand -i -t4`, which rewrites the initial whitespace of a line and nothing else, followed by three checks: - `git diff -w --stat` is empty, so not one non-whitespace character changed anywhere in the diff; - of the 42 raw string literals in the changed files (222 lines, all in source_io/module_parameter/read_inp_out.cpp), none has a line that this commit touches. Leading whitespace inside `R"(...)"` is part of the string, so that was the one place a leading-whitespace rewrite could have changed behaviour; - no changed line follows a line ending in a backslash, so no backslash-continued string literal is affected either; - no file gained a CR. Effect on tools/03_code_analysis/code_quality_score.py: average score over source/ goes from 82.15 to 82.58 and the number of passing files from 1560 to 1562. Six files score 1 to 3 points lower, all through the `line_too_long` rule, because the scorer counts a tab as a single character while it renders as up to four columns. The lines were already over 120 columns on screen; the tab was hiding it. One of them, module_ri/exx_abfs_ctor_orbs.cpp, moves from 60 to 59 and so drops just below the tool's pass line. Wrapping those lines would mean editing code in a commit that is otherwise whitespace-only, so it is left for a follow-up. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Add standalone CUDA GPU neighbor list * Organize GPU neighbor kernel and extend tests * Normalize neighbor-list test filenames * Move neighbor-list GPU kernel to source_cell * Split CUDA neighbor list implementation --------- Co-authored-by: Fei Yang <2501213217@stu.pku.edu.cn>
* remove include pw_basis_sup in pw_basis.h * fix * fix compile * fix
…8056) * Update .readthedocs.yaml * Update conf.py * Add texlive-xetex to apt_packages * Update conf.py
* Remove out_dos=3 (Fermi surface output)
The out_dos=3 code path (BXSF Fermi surface file via nscf_fermi_surface)
is unused and unmaintained. Remove the feature entirely:
- Delete the out_dos==3 branches in write_dos_lcao/write_dos_pw.
- Make out_dos=3 a hard error in INPUT validation.
- Delete the now-orphaned nscf_fermi_surf.{cpp,h} and drop it from
CMakeLists.txt, Makefile.Objects, and the IO test target.
Verified: full build succeeds; ctest MODULE_IO_cal_dos,
MODULE_IO_write_dos_pw, MODULE_IO_read_item_serial, and
MODULE_IO_input_test_para all pass.
* Refactor cal_pdos: vectorize memory, fix serial fallback, unify npoints
- Replace raw new/delete buffers (pdosk, pdos, waveg, Gauss) with
std::vector, removing manual memory management.
- Fix missing non-MPI fallback: previously the non-MPI build left pdos
as all zeros because the gemv into mulk and the reduction were only
compiled under __MPI. Add a serial BlasConnector::gemv branch and a
plain std::copy in place of MPI_Reduce.
- Unify the energy grid with cal_dos: npoints = floor((emax-emin)/de)+1
so the last grid point is included.
* Pass istep through to cal_pdos
Add an istep parameter to both cal_pdos overloads (gamma and multi-k)
and forward it from write_dos_lcao. The parameter is currently unused;
it will be consumed when PDOS output switches to per-step text files
(pdoss{spin}g{geom}_{basis}.txt) in a following step.
* Add text PDOS output, remove TDOS.dat and XML PDOS.dat
Introduce write_pdos_text() which writes PDOS in a plain-text
long-table format: energy(eV) atom species l z m pdos(1/eV).
Files are named pdoss{spin}g{geom}_{basis}.txt, following the
same convention as the doss* files for total DOS.
For nspin=2, two files are written (one per spin channel).
For nspin=4, the two spinor components are summed into a single
file, consistent with the previous XML behaviour.
Remove print_tdos_gamma/multik and print_pdos_gamma/multik which
wrote TDOS.dat and PDOS.dat (XML). These are superseded by the
new text output and by cal_dos (doss*.txt) respectively.
* Unify out_dos: PDOS is now always written with out_dos=1
- Remove the out_dos==2 branch in write_dos_lcao; PDOS is computed
unconditionally when DOS output is requested (LCAO only).
- Deprecate out_dos=2 with WARNING_QUIT, directing users to
out_dos=1 which now includes PDOS.
- Update the out_dos description string accordingly.
* Improve doss file header column names
Rename the ambiguous column headers in doss*.txt:
energy -> energy(eV)
elec_states -> dos(1/eV)
sum_states -> dos_int (integrated DOS)
states_smear -> dos_smear(1/eV)
sum_states -> dos_smear_int (integrated smeared DOS)
The old header had two columns both named "sum_states", which was
confusing. The new names make the distinction explicit and include
units where applicable.
* Update documentation for unified out_dos and new PDOS format
- parameters.yaml / input-main.md: remove out_dos=2 and out_dos=3
descriptions; out_dos=1 now includes PDOS for LCAO.
- dos.md: update DOS output file name and column descriptions to
match the new doss*.txt header; replace the XML PDOS section with
the new plain-text long-table pdoss*.txt format.
* Update tests for unified out_dos and removed TDOS/PDOS.dat
- Add out_dos=2 deprecation test in read_input_item_test.cpp
- Change scf_out_dos_spin2/INPUT from out_dos=2 to out_dos=1
- Remove obsolete TDOS.dat and PDOS.dat from the reference output
* Increase default dos_edelta_ev from 0.01 to 0.03 eV
The old default of 0.01 eV is much finer than needed given the
default Gaussian smearing width (dos_sigma = 0.07 eV). With
0.03 eV the grid spacing is still well below sigma/2, so no
features are lost, but the output file is ~3x smaller.
Update the default in input_parameter.h, the read_inp_postproc
metadata, the documentation, and the test INPUT/reference values.
* Switch PDOS output to wide-table format
Replace the long-table layout (one row per energy x orbital, with
repeated atom/species/l/z/m columns) with a wide-table layout:
energy in column 1, one column per orbital, and a header block
mapping each column number to its (atom, species, l, z, m).
This reduces the file size by ~3-4x since the orbital metadata is
no longer repeated on every energy row. The format is also more
convenient for plotting (gnuplot, pandas, numpy loadtxt).
* Revert "Switch PDOS output to wide-table format"
This reverts commit 106a683.
* Sum over zeta in PDOS text output
Collapse the zeta dimension in PDOS output: for each (atom, l, m),
sum the contributions from all zeta components into a single value.
This aligns the output with VASP's DOSCAR format and reduces file
size, since most PDOS analysis does not require zeta resolution.
The columns are now: energy(eV), atom, species, l, m, pdos(1/eV).
* Zero out negligible PDOS values and use fixed-point format
- Zero out PDOS values below 1e-6 to suppress numerical noise.
- Switch from scientific notation to fixed-point with 6 decimal
places for better readability.
* Group m components per (atom, l) row in PDOS output
Each row now corresponds to one energy point and one (atom, l)
combination, with 2l+1 pdos values for m = -l..l in separate
columns. This removes the m column and reduces redundant metadata
repetition. Column widths are tightened for a more compact layout.
* Flatten all (l, m) components into one row per atom in PDOS output
Each row now corresponds to one energy point and one atom, with
all (l, m) pdos values laid out in columns ordered as s, p, d, f.
The l column is removed; the column count varies per atom type
depending on its angular momentum content. Zeta components remain
summed and values below 1e-6 are zeroed out.
* Add explicit (l, m) ordering to PDOS file header
Replace the vague orbital name listing in the header with an
explicit enumeration of the m ordering for each l block, matching
ABACUS's complex spherical harmonics convention (m = -l..+l).
* Fix missing negative-m PDOS components
iw2m stores m as 0..2l (non-negative), but the code was looping
m = -l..+l and matching against iw2m. This caused all negative-m
components to be silently dropped. Fix the loop to m = 0..2l to
match the internal convention.
* Fix PDOS header to show correct physical m ordering
iw2m stores m as 0..2l, mapping to physical m values 0,+1,-1,+2,-2,...
Update the header comment to reflect this convention.
* Compact PDOS header to a single line
* Add istep and npoints to PDOS file header
* Move DOS tests to module_dos/unittests and update paths
- Move cal_dos_test.cpp and write_dos_pw_test.cpp from source/source_io/test/
to source/source_io/module_dos/unittests/
- Add minimal support files (kpoints, istate.info, INPUT, KPT)
- Remove unnecessary files (Si.upf, STRU, SK_nspin*, DMK_nspin*)
- Update CMakeLists.txt paths accordingly
* Pass DOS write parameters explicitly instead of reading PARAM (Step 1)
Remove PARAM global dependencies from cal_dos, write_dos_pw, and
write_dos_lcao by threading input values (nspin, out_dos, dos_setemax/
dos_emax_ev, dos_setemin/dos_emin_ev, two_fermi, out_app_flag, bndpar,
global_out_dir) through the function signatures. Update call sites in
ctrl_output_pw / ctrl_scf_lcao / cal_ldos and the unit tests
accordingly.
Verified: cmake --build (full) passes; ctest -R MODULE_DOS passes
(2/2).
* Pass PDOS parameters explicitly instead of reading PARAM (Step 2)
Remove PARAM global dependencies from cal_pdos (gamma-only and
multi-k) by threading nlocal, nspin, and global_out_dir through the
function signatures. write_dos_lcao gains an nlocal parameter so it
can forward it; the call site in ctrl_scf_lcao passes
PARAM.globalv.nlocal.
Verified: cmake --build (full) passes; ctest -R MODULE_DOS passes
(2/2).
* Pass LDOS parameters explicitly instead of reading PARAM (Step 3)
Remove PARAM global dependencies from the LDOS code paths
(Cal_ldos<T>::cal_ldos_lcao, cal_ldos_pw, stm_mode_pw, ldos_mode_pw)
by threading stm_bias, nspin, global_out_dir, two_fermi,
out_ldos_precision, nbands, dos_edelta_ev, dos_scale, dos_setemax/
dos_emax_ev, dos_setemin/dos_emin_ev, dos_sigma, and ldos_line through
the function signatures. Update the call sites in ctrl_runner_lcao and
ctrl_output_pw.
Verified: cmake --build (full) passes; ctest -R MODULE_DOS passes
(2/2).
* module_dos: remove PARAM dependencies from tests and stray include (step 4)
- test_write_dos_pw.cpp: drop the #define private public hack and all
PARAM.input/PARAM.sys assignments; pass values through write_dos_pw
arguments (dosp.nbands, local dos_setemax/dos_setemin) instead
- write_pdos_text.cpp: drop unused module_parameter/parameter.h include
module_dos (outside test support data) no longer references PARAM.
Verified: cmake --build . -j16; ctest -R MODULE_DOS (2/2 passed).
* module_dos tests: inline synthetic k-point/band data, drop support files
Replace the file-based test fixture (support/kpoints + support/istate.info,
36 k-points x 8 bands) with a small synthetic dataset inlined in the new
header dos_test_data.h (4 k-points, 4 bands, equal weights). DosPrepare now
fills wk/ekb/wg from this data via set_wk()/set_istate_info() instead of
reading files, so the tests no longer depend on the working directory or
on-disk fixtures.
- dos_test.h: drop read_wk()/read_istate_info(); add set_wk()/
set_istate_info() reading from dos_test_data.h; fix set_isk() to use
resize instead of reserve+operator[] (which wrote out of bounds)
- dos_test_data.h: new header with nks/nkstot/nbands and wk/ekb_ev/wg arrays
- test_cal_dos.cpp, test_write_dos_pw.cpp: use dos_test_data dimensions and
update expected DOS rows for the synthetic spectrum
- delete support/{INPUT,KPT,kpoints,istate.info} (INPUT/KPT were unreferenced
leftovers; kpoints/istate.info superseded by dos_test_data.h)
Verified: cmake --build . --target MODULE_DOS_cal_dos MODULE_DOS_write_dos_pw;
ctest -R MODULE_DOS (2/2 passed).
* module_dos: add unit test for write_pdos_text
Add test_write_pdos_text.cpp covering the text PDOS writer:
- basic output: header lines, energy/atom/species columns, and the
s + p(m=0,+1,-1) column ordering for a single Si atom
- istep>=0 appends g{istep+1} to the file name (pdoss1g3_lcao.txt)
- spin-polarized (nspin=2): writes pdoss1/pdoss2 from pdos[0]/pdos[1]
The test builds a minimal UnitCell by hand (iat2it/iat2ia/itia2iat filled
directly) so it only links the UnitCell/Atom constructor/destructor via
mock_unitcell.cpp, not the full source_cell library. iat2it/iat2ia are
owned by UnitCell's Statistics member and must not be deleted in TearDown.
To set the private iat2iwt/npol without a #define-private-public hack, add
a small public setter UnitCell::set_iat2iwt_for_test().
cal_pdos_gamma/cal_pdos_multik are not unit-tested here: they require a full
LCAO Hamiltonian (HamiltLCAO Sk matrix, Parallel_Orbitals distribution and
ScaLAPACK descriptors) and are covered by integration tests instead.
Verified: cmake --build .; ctest -R MODULE_DOS (3/3 passed).
* Rewrite PDOS parser in dos.py to read the new text PDOS output
ABACUS now writes PDOS as per-spin-channel text files
(pdoss1_*[/pdoss2_*]_<basis>.txt) instead of a single XML file. Rewrite
PDOS._read to parse this format:
- discover one or two spin-channel files from a directory, a path
prefix, or a concrete spin-up file; nspin is derived from the number
of files found
- rebuild the per-(atom,l,m) orbital entries from the flattened text
columns (s=1, p=3, d=5, f=7; column index -> physical m via
0,+1,-1,+2,-2,...); zeta is already summed by ABACUS so the 'z' key
and all zeta handling are removed
- concatenate the two spin channels so orb['data'] stays (npoints,2)
for nspin=2, keeping the downstream _plot/_write logic unchanged
Drop the lxml dependency. Also fix a pre-existing bug where the
total-DOS and list-species branches of _parial_plot passed a list of
Axes to DOSPlot, and remove the obsolete XML example data file
examples/Si/PDOS (config.json now points to a 'pdos' prefix).
* Update docs and plot-tools README for the text PDOS format
- plot-tools/README.md: pdosfile now points to a directory/prefix of the
text pdoss1_*[/pdoss2_*] files instead of the old XML 'PDOS' file;
note that lxml is only needed for the XML projected-band output
- docs/advanced/elec_properties/dos.md: fix the PDOS header example to
match the actual text output and document the m = 0,+1,-1,... column
order and the optional '# istep:' line
- docs/advanced/interface/pyabacus.md: replace the obsolete XML 'PDOS'
output-file entry with pdoss1_*.txt
* Revert hardcoded local ABACUS path in integration test config
Commit 6184953 accidentally committed a local absolute executable
path (/home/510Group/.../abacus_max_para) into Autotest.sh and
general_info. This broke CI: the container has no such file, so mpirun
failed to launch ABACUS and all 11_PW_GPU cases errored with
"No ABACUS executable was found". Restore the default "abacus" so the
runner resolves it from PATH.
* Fix DOS.py TDOS reading and update outdated file names in examples/docs
- Fix TDOS._read() in dos.py to handle new 5/9-column format with
two non-# header lines, while keeping backward compatibility with
old DOS1_smearing.dat 2/3-column format.
- Update examples/13_dos run.sh to check new DOS output file names
(dos.txt for PW, doss1g1_nao.txt for LCAO) and new charge density
file name chg.cube instead of SPIN1_CHG.cube.
- Update examples/13_dos README to reflect new DOS output format.
- Update docs (parameters.yaml, input-main.md, dos.md, spin.md,
faq.md, pyabacus.md) for new file names and default changes:
dos_edelta_ev default 0.01 -> 0.03, out_dos=2/3 no longer supported.
- Update tests/performance/catch_properties.sh to use new DOS file name.
- Update Wannier90 interface to use new charge density file patterns.
* Sync docs/parameters.yaml with C++ source descriptions
Move the out_dos deprecation note and dos_edelta_ev default-change note
into the C++ Input_Item descriptions so the generated parameters.yaml
matches the checked-in docs.
* Fix serial PDOS: write S^T*psi into band column i of mulk
The non-MPI gemv fallback introduced in the cal_pdos refactor wrote every
band's S^T*psi result into row 0 of mulk, while the accumulation loop below
reads column ic=i. Bands i>0 therefore contributed stale/undefined values
to the projected DOS in serial builds. Offset the gemv output pointer by
i * pv.nrow so band i lands in mulk column i, matching the pdgemv_/pzgemv_
layout used in the MPI path.
Verification: python3 tools/03_code_analysis/agent_governance_check.py
--staged (pass). No serial runtime build available; layout equivalence with
the MPI path verified by index arithmetic (serial: pv.nrow == nlocal,
mulk is row-major with nc=pv.nrow, so column i starts at c + i*pv.nrow).
* Fix out-of-bounds UB in DOS PW unit test: use resize() instead of reserve()
reserve() only allocates capacity without changing size(), so the
subsequent writes via operator[] were out-of-bounds undefined behavior.
Use resize() to actually create the elements before writing.
* Fix PDOS m quantum number semantics in abacus_plot dos.py
The text PDOS parser rewrite (8b2c56a) started storing the physical
magnetic quantum number m (0, +1, -1, +2, -2, ...) in orb['m'], but two
downstream consumers still treated it as a 0..2l column index:
- parse_projected_data: user selections {l: [m, ...]} never matched for
m > l, so e.g. selecting p with m=2 silently produced no data.
- get_angular_momentum_name: labels were looked up with the physical m
as an index into angular_momentum_name, producing wrong labels for
d/f orbitals (e.g. l=2, m=+2 would be labeled d_yz instead of
d_{x^2-y^2}).
Also orb['index'] changed from the 1-based global orbital index of the
legacy XML format to a 0-based counter, breaking 'index'-keyed
selections (index=1 no longer matched the first orbital).
Fixes:
- utils.py: get_angular_momentum_name now accepts physical m by default
(m_is_physical=True) and maps it to the correct column via _m_to_col;
pass m_is_physical=False for the legacy XML column-index semantics.
- band.py: pass m_is_physical=False at the two call sites, since the
projected-band XML stores m as iw2m (0..2l index).
- dos.py: restore orb['index'] to a 1-based global orbital index.
Verification: python3 -m py_compile on all three files (pass);
python3 tools/03_code_analysis/agent_governance_check.py --staged
(pass). No runtime test data available for the new text PDOS format.
* Fix PDOS file discovery ignoring the requested file
When a concrete file such as pdoss1g5_nao.txt was passed to PDOS(),
_find_spin_files reduced it to the prefix 'pdos' and returned the first
sorted glob match of pdoss1*.txt (e.g. pdoss1g1_nao.txt), silently
discarding the requested file. The same happened for the spin-2 file.
Now a concrete pdoss* file is used directly as its spin channel, and
the matching spin-2 file is looked up by replacing 'pdoss1' with
'pdoss2' in the filename only.
Verification: python3 -m py_compile (pass);
python3 tools/03_code_analysis/agent_governance_check.py --staged (pass).
* Address remaining review comments on DOS/PDOS refactor
- dos.py (TDOS): strip inline comments before parsing, so the new
header lines "1 # ionic step" / "4801 # number of points" no
longer crash abacus-plot -d with ValueError.
- dos.py (PDOS): when pdosfile is a directory (e.g. OUT.ABACUS),
search for pdoss{1,2}*.txt inside it instead of treating the
directory name as a file prefix.
- cal_dos.cpp: keep DOS columns separated when values reach
10000.000000 (12 chars) by inserting an explicit space separator;
rename the raw-histogram column header from dos(1/eV) to dos, since
it is a state count per energy bin, not per eV. Update unit tests
and docs/advanced/elec_properties/dos.md accordingly.
- write_dos_pw.cpp: remove the dead out_dos == 2 branch (rejected in
read_inp_out.cpp).
- cal_pdos_gamma.cpp / cal_pdos_multik.cpp: drop the stale (void)istep
now that istep is passed to write_pdos_text.
- Remove leftover legacy test files source/source_io/test/cal_dos_test.cpp
and write_dos_pw_test.cpp, replaced by module_dos/unittests/.
Verification:
- cmake --build . --target MODULE_DOS_cal_dos MODULE_DOS_write_dos_pw
MODULE_DOS_write_pdos_text (pass)
- OMP_NUM_THREADS=1 ctest -R MODULE_DOS --output-on-failure
(3/3 pass)
- OMP_NUM_THREADS=1 ctest -R MODULE_IO_read_input_serial (pass)
- cmake --build . --target io_basic (pass, includes cal_pdos_*)
- python3 functional check of TDOS/PDOS parsing with synthetic
new-format files (directory lookup, inline-comment header, m=+1/-1
selection) passes.
* Sync dos_edelta_ev description in parameters.yaml with C++ source
The C++ description was updated to note the default change from 0.01
to 0.03 eV, but docs/parameters.yaml still carried the old one-line
text, so the CI sync check failed.
---------
Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
* fix: output correct E_exx in LCAO nscf hybrid functional calculations In LCAO nscf calculations with hybrid functionals (e.g. HSE), the exact exchange energy E_exx was always printed as 0. The root cause was that hamilt2density() explicitly skipped calling exx_hamilt2rho() for nscf, which is the only path that calls ElecState::set_exx() to write the computed Eexx into f_en.exx. For nscf, Hexx and Eexx are already computed in the OperatorEXX constructor when init_chg=dm reads the density matrix from file. This patch adds an explicit set_exx() call in the nscf branch of hamilt2density() to sync the pre-computed Eexx to f_en.exx, so that the energy output table shows the correct E_exx value. Fixes #6248 * fix: add MPI barriers in EXX nscf path to prevent deadlock and tag collision When running LCAO nscf hybrid functional calculations with init_chg=dm, OperatorEXX reads the density matrix from dmrs*_nao.csr files. This process involves three steps: cal_exx_ions (pure local tensor calculation), Read_HContainer (MPI_Send/MPI_Recv with fixed tags 0/1), and cal_exx_elec (may use Comm_Assemble with MPI tags). The bug occurs because cal_exx_ions has no internal MPI synchronization and uses Distribute_Equally to assign atom pairs to different ranks. This causes ranks to finish at different times. When rank 0 finishes cal_exx_ions early, it enters Read_HContainer and blocks in MPI_Send waiting for other ranks to receive. However, slower ranks are still inside cal_exx_ions and cannot reach MPI_Recv, causing a deadlock. Additionally, Read_HContainer's fixed tags 0/1 can collide with cal_exx_elec's internal Comm_Assemble tags, causing MPI_ERR_TRUNCATE or cereal::Exception when messages are mismatched. This fix adds three MPI_Barrier calls: 1. After cal_exx_ions, before Read_HContainer: ensures all ranks synchronize before entering the file-reading phase. 2. After Read_HContainer, before cal_exx_elec: prevents tag collision between Read_HContainer's tags 0/1 and cal_exx_elec's internal tags. 3. After cal_exx_elec, before init_chg_dm: prevents tag collision with the subsequent Read_HContainer call in init_chg_dm. Also adds log output to running_nscf.log for both DM read operations (init_dm_from_file and OperatorEXX) so users can see when density matrices are loaded. Verified: 10 consecutive runs of tests/08_EXX/nscf_hse_multik all pass with exit code 0, no MPI errors, correct E_exx output, and both log lines present in running_nscf.log. * fix: remove redundant slash in DM file path and improve error message The global_readin_dir and readin_dir are normalized by to_dir() to always end with '/', so concatenating another '/dmrs' produced a double slash (e.g., './/dmrs1_nao.csr'). Remove the redundant '/' in both init_dm_from_file (lcao_set.cpp) and OperatorEXX (op_exx_lcao.cpp). Also improve FileReader's error message to include the filename, so users know exactly which file failed to open instead of a bare 'Error opening file'. * add an example nscf_hse_multik * refactor(tests): rename 08_EXX to 08_RI and reorganize test cases - Rename parent directory tests/08_EXX to tests/08_RI - Rename all 24 test cases to unified format: {calc}_{features}_{gamma|multik} - Update all README files with concise descriptions (≤2 lines) - Update CASES_CPU.txt with new directory names - Update CMakeLists.txt, tests/README, and CI workflow references New naming convention: - scf_hse_gamma, scf_hse_spin2_gamma, scf_hse_spin4_multik, etc. - lr_tddft_* for LR-TDDFT tests - rpa_scf_* for RPA tests - bse_lr_multik for BSE test - nscf_hse_multik (unchanged) * refactor(module_hs): remove PARAM from write_hs_sparse and write_hs_r Pass runtime output parameters (global_out_dir, global_matrix_dir, calculation, out_app_flag, nspin, gamma_only_local, npol, nlocal) explicitly through output_TR/output_dHR/output_dSR/output_SR, save_dH_sparse, write_hsr, and write_matrix_r instead of reading PARAM inside the module. Extend the existing SparseWriteOptions with calculation/out_app_flag so the sparse-file open mode no longer depends on global INPUT state. Read PARAM once at the I/O control layer (output_mat_sparse.cpp, ctrl_scf_lcao.cpp, esolver_gets.cpp) and pass down, reducing cross-layer control per AGENTS.md rule 1. Verified: cmake build succeeds; ctest -R "write_hs_r_compat_test|single_r_io_test|write_hsk_binary|cal_plpr" passes 4/4; agent_governance_check --staged net_delta=-27 (warning only). * refactor(module_hs): remove PARAM from cal_r_overlap_r Pass cal_force and nlocal through init/init_nonlocal and the private construct_orbs_and_* helpers, and pass global_out_dir, global_matrix_dir, calculation, out_app_flag, nlocal, npol through out_rR/out_rR_other, instead of reading PARAM inside the class. The sparse-file open mode and output filename branching now use explicit arguments. Callers (output_mat_sparse.cpp, esolver_gets.cpp, velocity_op.cpp, td_info.cpp) read PARAM once at the control layer and pass it down; the rt test passes explicit literals. Reduces cross-layer control per AGENTS.md rule 1. Verified: cmake build succeeds; ctest -R "write_hs_r_compat_test|single_r_io_test|write_hsk_binary|cal_plpr" passes 4/4 and ctest -R "snap_psb|velocity|td_info|tddft" passes 7/7; agent_governance_check --staged net_delta=-27 (warning only). * refactor(module_hs): remove PARAM from write_h_terms Extend WriteHParams with the INPUT/globalv values the H-component writers need (nlocal, gamma_only_local, npol, domag, domag_z, out_app_flag, calculation, global_out_dir, global_matrix_dir) and pass them down through write_hk_common / gather_and_write / write_h_exx_impl instead of reading PARAM inside module_hs. The remaining PARAM reads live at the I/O control layer (ctrl_scf_lcao.cpp), which fills the new fields. Verification: cmake --build build -j 32 (pass); ctest -R "write_hs_r_compat_test|single_r_io_test|write_hsk_binary|cal_plpr|hcontainer" (12/13 pass; MODULE_LCAO_hcontainer_para_test fails due to missing parallel_hcontainer_tests.sh, unrelated); agent_governance_check --staged net_delta=-7 (warnings only). * refactor(module_hs): remove PARAM from write_hs/write_vxc/write_vxc_lip/write_vxc_r/cal_plpr - write_hsk/save_mat: add nlocal, ks_solver, drank parameters; all callers pass them explicitly (ctrl_scf_lcao, write_h_terms, write_vxc, write_vxc_lip, write_dh, write_hsk_binary_test). - WriteHParams gains ks_solver and drank fields, filled once in ctrl_scf_lcao. - write_Vxc (LCAO and LIP) and write_Vxc_R: add dft_plus_u/gamma_only/ global_out_dir/out_ndigits/ks_solver parameters; write_orb_energy takes global_out_dir. - AngularMomentumCalculator: constructor takes out_level and gamma_only instead of reading PARAM. - Drop now-unused module_parameter/parameter.h includes from module_hs sources (also write_hs_r.cpp, write_hs_sparse.cpp, cal_r_overlap_r.cpp). Global-dependency budget note: net_delta=+3 is migration-neutral. The 23 added references are all in the I/O control layer (ctrl_scf_lcao, ctrl_runner_lcao, write_eband_terms, esolver_ks_lcaopw, write_dh) reading PARAM once to pass down explicitly; 22 uses were removed from module_hs proper, which no longer includes parameter.h. Verification: - cmake --build build -j 32: pass - OMP_NUM_THREADS=1 ctest -R "write_hsk_binary|cal_plpr|write_hs_r_compat_test| single_r_io_test|hcontainer|write_dmk": 14/15 pass; the only failure is MODULE_LCAO_hcontainer_para_test, which fails on this machine due to the missing parallel_hcontainer_tests.sh script (pre-existing, unrelated). * test(module_hs): colocate unit tests under module_hs/unittests Move the four module_hs test files from source_io/test into a new source/source_io/module_hs/unittests directory, registered by its own CMakeLists.txt (added via add_subdirectory under BUILD_TESTING): - write_hsk_binary_test (+ mpirun -np 2 parallel test) - single_r_io_test - cal_plpr_test (kept inside if(ENABLE_LCAO)) - write_hs_r_compat_test (+ mpirun -np 2 parallel test) Details: - Test sources keep their original names via git mv; no renames. - AddTest SOURCES paths re-rooted (../module_hs/x.cpp -> ../x.cpp, ../../source_* -> ../../../source_*). - cal_plpr_test.cpp: orb fixture path gains one level since the test working directory moves one level deeper in the build tree. - No INPUT or user-facing behavior change: no documentation update needed (docs/parameters.yaml and input-main.md unaffected). Verification: - cmake -S . -B build: pass - cmake --build build -j 32: pass - OMP_NUM_THREADS=1 ctest -R "write_hsk_binary|cal_plpr|write_hs_r_compat| write_hsr_binary|single_R_test": 6/6 pass (incl. both mpirun tests) - agent_governance_check --staged: only documentation-sync warning (test-only move, no docs change required). * refactor(module_hs): remove dead code with no callers Remove three confirmed dead interfaces (repo-wide grep shows no call sites; functionality is covered by the surviving paths): - cal_r_overlap_R::out_rR_other (duplicates out_rR) - ModuleIO::write_matrix_r (covered by write_hsr) - legacy output_mat_sparse(bool...) overload (sole caller already uses the MatSparseOutputOptions interface) Net -423 lines. * refactor(module_hs): accumulate CSR column indices in memory output_single_R wrote column indices to a per-rank temporary file (temp_sparse_indices.dat) only to emit them after the values, then re-read and deleted the file on every R block. Accumulate the indices in an in-memory std::vector<int> instead and emit them with indptr, removing the per-block file round-trips and the std::remove calls. The now-unread SparseWriteOptions::temp_dir field and all assignment sites (including the two unit tests) are removed. Output bytes are unchanged. * refactor(module_hs): drop unused params/vars; scope cal_plpr globals - output_dHR/output_dSR: remove the K_Vectors& kv parameter, which the implementations never used; update the two call sites (output_mat_sparse.cpp, esolver_gets.cpp). - write_vxc.hpp: remove the unused std::ofstream ofs in set_para2d_MO. - cal_r_overlap_r.cpp: remove unused tau1/tau2/dtau in out_rR. - write_hs.h: drop the commented-out includes. - cal_plpr.cpp: move _lambda_plus/_lambda_minus and the file-scope constants into an anonymous namespace, and rename the collision-prone globals i/invsqrt2 to kImag/kInvSqrt2. * fix(module_restart): drop stale temp_dir assignment in write_Hexxs_csr The removal of SparseWriteOptions::temp_dir missed this assignment site in module_restart (outside the module_hs tree scanned earlier). Delete the assignment and the temp_dir/last_slash locals that only fed it. * refine the parameters in scf_hse_soc_symm_multik * reduce the time for lr_tddft_pbe0_gamma example * test * update CASES_CPU.txt * style(module_hs): hoist mid-file includes to the top of write_hs_r.cpp Move hcontainer_funcs.h / output_hcontainer.h / ucell_io.h from the middle of the file into the top include block. No behavior change. * update write_hs_sparse.cpp * update test list * refactor(module_hs): split cal_r_overlap_R into PosOpBasis/Calc/Writer facade - Extract orbital basis and two-center integral table construction into PosOpBasis (pos_op_basis.h/.cpp), eliminating duplicated code between init() and init_nonlocal(). - Extract position-operator matrix element evaluation into PosOpCalc (pos_op_calc.h/.cpp): pos_matrix, pos_grad_matrix, pos_beta_matrix. - Extract sparse matrix output into PosOpWriter (pos_op_writer.h/.cpp): out_lat_r now calls calc_->pos_matrix() instead of duplicating the overlap_o/x/y/z inline computation. - Keep cal_r_overlap_R as a facade with unchanged public interface so all existing callers (velocity_op, td_info, td_current_io, esolver_gets, output_mat_sparse) require zero modifications. - Add explicit includes in td_info.cpp and snap_psb_half_tddft_test.cpp that were previously satisfied transitively through cal_r_overlap_r.h. - Wire new sources into CMakeLists.txt and Makefile.Objects. Verified: full build passes; MODULE_LCAO_tddft_snap_psibeta_half_test passes. * refactor(module_hs): rename cal_r_overlap_r to pos_op_mat - Rename cal_r_overlap_r.h/.cpp to pos_op_mat.h/.cpp to reflect the actual responsibility: facade for position-operator matrix elements. - Update all #include references in esolver_gets.cpp, output_mat_sparse.cpp, td_info.h, velocity_op.h, td_pot_hybrid.h, snap_psb_half_tddft_test.cpp. - Update CMakeLists.txt (source_io, deepks test, rt test) and Makefile.Objects. - Use git mv to preserve file history. * refactor(module_hs): split write_vxc*.hpp into vxc_op_*.h/.cpp - Extract common utilities into vxc_op_tools.h/.cpp - write_vxc.hpp -> vxc_op_mat.h/.cpp (LCAO MO-space Vxc) - write_vxc_r.hpp -> vxc_op_r.h/.cpp (LCAO real-space Vxc(R)) - write_vxc_lip.hpp -> vxc_op_lip.h/.cpp (LIP MO-space Vxc) - Remove default arguments (Hexxd, Hexxc, sparse_thr) - Use std::unique_ptr for dynamic allocations - Update CMakeLists.txt and all call sites - Explicit template instantiation for fixed type combinations Build verified: cmake --build build -j16 Tests passed: ctest --test-dir build -V -R MODULE_IO (47/47) Governance check: agent_governance_check.py --staged (no findings) * fix(module_hs): replace std::make_unique with C++11 compatible unique_ptr std::make_unique is C++14 only. Use explicit new + unique_ptr constructor to maintain C++11 baseline compatibility. Affected files: - pos_op_mat.cpp - vxc_op_mat.cpp - vxc_op_lip.cpp * refactor(module_hs): split write_hs_r into hsr_writer and hs_r_legacy Separate the new HContainer-based CSR writers (write_hsr, write_hcontainer_csr{,_binary}, *_gen_fname) from the legacy LCAO_HS_Arrays-based output path (output_dHR/dSR/TR/SR) into hsr_writer.{h,cpp} and hs_r_legacy.{h,cpp} respectively. Pure mechanical move: no signature or behavior changes. All include sites and CMakeLists updated. Verified: full build passes; ctest -R "MODULE_IO_write_hs_r_compat| MODULE_IO_write_hsr_binary|MODULE_IO_single_R" 3/3 passed. * refactor(module_hs): eliminate write_hs.hpp, split into hs_dense_io and hsk_writer Move the dense square-matrix writer save_mat into hs_dense_io.{h,cpp} and the H(k)/S(k) writer write_hsk into hsk_writer.{h,cpp}. Template definitions move out of the .hpp implementation header into .cpp with explicit instantiations (double, complex<double>, float, complex<float>), removing the .hpp header per governance rule 4. ctrl_scf_lcao.cpp gains an explicit module_out/filename.h include that was previously pulled in transitively through write_hs.hpp. The hsk binary unit test now links hs_dense_io.cpp instead of relying on the header implementation, and includes parallel_orbitals/parallel_comm that were previously transitive. No behavior change. Verified: full build passes; ctest -R "MODULE_IO_write_hsk_binary| MODULE_IO_write_hs_r_compat|MODULE_IO_write_hsr_binary" 4/4 passed. * refactor(module_hs): slim save_mat internals without changing output Extract anonymous-namespace gather_row<T> to unify MPI and serial row collection, replace raw new/delete with std::vector + std::fill, and keep byte-identical text/binary output. No interface change. Verification: make -j8 (pass); OMP_NUM_THREADS=1 ctest -R "MODULE_IO_write_hsk_binary|MODULE_IO_write_hs_r_compat|MODULE_IO_write_hsr_binary" 4/4 pass. * refactor(module_hs): split write_hs_sparse into hs_sparse_io + dhs_sparse_writer Separate the generic sparse-CSR format layer (RCoordinate/SparseRBlock/ SparseRMatrix/SparseWriteOptions/save_sparse + format primitives) from the multi-component derivative writer save_dH_sparse. git-rm the monolithic write_hs_sparse.{h,cpp} and update all includers (single_r_io.h, hs_r_legacy.cpp, vxc_op_r.cpp, restart_exx_csr.hpp, exx_lri_interface.hpp, write_hs_r_compat_test.cpp) plus both CMakeLists.txt files. hs_sparse_io.h gains an explicit abfs_vector3_order.h include for Abfs::Vector3_Order (previously pulled in transitively via lcao_hs_arrays.h). No behavior change; byte-identical output. Verification: make -j8 (pass); OMP_NUM_THREADS=1 ctest -R "MODULE_IO_write_hs_r_compat|MODULE_IO_write_hsk_binary|MODULE_IO_write_hsr_binary" 4/4 pass. * refactor(module_hs): introduce MatROutputOptions for legacy HS writers Bundle the ten shared format/path/runtime parameters of output_dHR, output_dSR, and output_TR into a single MatROutputOptions struct, collapsing the repeated positional lists at both call sites (output_mat_sparse.cpp and esolver_gets.cpp). output_SR keeps its template signature. Also drop the decorative GlobalV::ofs_running banner blocks and the std::cout passthrough in output_dHR/output_TR/output_SR; the substantive "data are in file" lines and all data files are unchanged. Verification: make -j8 (pass); OMP_NUM_THREADS=1 ctest -R "MODULE_IO_write_hs_r_compat|MODULE_IO_write_hsk_binary|MODULE_IO_write_hsr_binary" 4/4 pass. * refactor(module_hs): rename write_h_terms -> hterm_writer, table-drive term writers Collapse the five non-EXX write_h_* bodies onto a shared write_h_term skeleton driven by an HTermSpec descriptor (filename prefixes, CSR label, and a per-term build callback). The term-specific part is now only how hR_tmp is filled; the build -> write H(k) -> optionally write H(R) flow is written once. write_h_exx keeps its own body (different interface). No output change. Verification: make -j8 (pass); OMP_NUM_THREADS=1 ctest -R "MODULE_IO_write_hs_r_compat|MODULE_IO_write_hsk_binary|MODULE_IO_write_hsr_binary" 4/4 pass. * Refactor: sync Makefile.Objects with module_hs write_* split Update the legacy Makefile object list to track the CMake-side split of the module_hs write_* sources: - write_hs_sparse.o -> hs_sparse_io.o + dhs_sparse_writer.o - write_hs_r.o -> hsr_writer.o + hs_r_legacy.o - write_h_terms.o -> hterm_writer.o - write_hs.hpp save_mat/write_hsk -> hs_dense_io.o + hsk_writer.o (new) No source change; keeps the non-CMake build path consistent with the renamed/split translation units. * refactor(module_hs): rename cal_plpr -> angmom_op_mat for naming consistency Align with the existing pos_op_mat / vxc_op_mat naming convention in module_hs. The new name angmom_op_mat (angular momentum operator matrix) makes the file's purpose self-evident without requiring knowledge of the legacy "plpr" abbreviation. Changes: - Rename cal_plpr.h/cpp -> angmom_op_mat.h/cpp - Rename cal_plpr_test.cpp -> test_angmom_op_mat.cpp (AGENTS.md rule 11) - Add missing header guard ANGMOM_OP_MAT_H - Update all references: ctrl_scf_lcao.cpp, CMakeLists.txt, Makefile.Objects, unittests/CMakeLists.txt Verification: - Build target io_basic: PASS - Build target MODULE_IO_angmom_op_mat_test: PASS - Unit test execution blocked by missing test orbital file (tests/PP_ORB/Si_gga_8au_100Ry_2s2p1d.orb), unrelated to rename * refactor(module_hs): rename hs_r_legacy -> hsr_legacy for naming consistency The old name hs_r_legacy visually collides with hsr_writer, making it hard to distinguish the legacy LCAO_HS_Arrays path from the new HContainer-based writers at a glance. Rename the files and update all includes, build references, and the include guard. No functional change. Verified: full make -j builds; ctest -R write_hs_r_compat passes. * fix(Makefile): add missing module_hs vxc_op objects to Makefile.Objects The Makefile-based build was missing vxc_op_mat.o, vxc_op_tools.o, vxc_op_r.o, and vxc_op_lip.o from OBJS_IO_LCAO, causing undefined reference errors during linking when ctrl_runner_lcao.cpp pulls in write_eband_terms.hpp which calls write_Vxc and orbital_energy. Also add ./source_io/module_hs to VPATH so make can locate the source files. * fix(module_hs): pass absolute position of R-translated atom to pos_matrix The PosOpBasis/Calc/Writer split (2bbadc9) moved the inline r(R) evaluation into PosOpCalc::pos_matrix, which derives the inter-center distance as R2 - R1 and expects both arguments to be absolute Cartesian positions. The rR writer still passed the already-relative vector (tau2 - tau1 + R_car) as R2, so the integration distance was shifted by -tau1: the atom-position term of the diagonal r(R) elements vanished and spurious R vectors appeared (rr.csr reported 20 R blocks instead of 9 in 03_NAO_multik/nscf_out_hsr_tr_rr). Pass the absolute position of atom ia2 translated by R_car, matching the contract used by td_current_io and td_pot_hybrid callers. Verified: nscf_out_hsr_tr_rr passes all 6 checks (HR/SR/rR/TR and energies); rr.csr matches the reference with max abs diff 4.8e-9. * refactor(module_hs): rename single_r_io to lat_r_csr and output_single_R to save_lat_r - git mv single_r_io.{h,cpp} -> lat_r_csr.{h,cpp}; test file renamed to test_lat_r_csr.cpp per test naming rule. - Rename ModuleIO::output_single_R -> ModuleIO::save_lat_r (declaration, definition, explicit template instantiations, WARNING_QUIT context strings); include guard -> LAT_R_CSR_H. - Update callers hs_sparse_io.cpp, dhs_sparse_writer.cpp, pos_op_writer.cpp; local variable single_R_options -> lat_r_options. - Rename test target MODULE_IO_single_R_test -> MODULE_IO_lat_r_csr_test (no repo-internal references to the old target name). - Wire renamed sources through Makefile.Objects and four CMakeLists.txt. Naming convention: lat_r denotes lattice vector R; csr denotes the on-disk format. Pure rename, no behavior change. Compile not yet run (user approval pending); verification to follow before next step. * test(08_RI): regenerate ref for scf_hse_soc_symm_multik after LibXC mask change Merging upstream PR #8017 applies LibXC vrho/exc down to rho=1e-10 (QE convention) instead of masking at 1e-6. The direct effect on the initial density is ~3e-10 eV, but this deliberately-minimal metallic EXX+SOC case never reaches a fixed point and stops mid-oscillation, so the shifted vxc seed moves its endpoint by ~0.11 eV; converged HSE cases shift only by ~1e-5 eV. Regenerate result.ref from the bit-reproducible np=1 run (etot -2926.9134396250 eV, stress sum 3851.624753). Symmetry and magnetic-group assertions (O_h / C_4h / nksibz=3) are unchanged. Slim the threshold file to a single energy override (1e-5 eV): measured np=4 spread is within 9e-7 eV, while force/stress/fatal fit the global defaults (1e-4 / 1e-3 / 1). Verified: np=4 three runs and np=1 one run, 8/8 checks each. * test(08_RI): regenerate ref for nscf_hse_multik after LibXC mask change Merging upstream PR #8017 applies LibXC vrho/exc down to rho=1e-10 (QE convention) instead of masking at 1e-6. This NSCF case reads a fixed density matrix and is fully deterministic: the new value is bit-identical across np=1 and np=4 runs (spread 3e-12 eV), and matches the CI cal value to all printed digits (-429.67342983 eV). Regenerate result.ref from the np=1 run (etot -429.6734298284445 eV). No threshold override is needed; the default 1e-7 eV tolerance leaves ample margin for the ~1e-11 eV MPI spread. Verified: np=1 two runs and np=4 two runs, 2/2 checks each. * refactor(module_hs): rename rr_sparse_writer to pos_op_csr and its two detail functions - git mv rr_sparse_writer.{h,cpp} -> pos_op_csr.{h,cpp}; include guard -> POS_OP_CSR_H. - Rename ModuleIO::detail::rr_sparse_has_payload -> lat_r_nonempty (predicate: any of the 3 direction nonzero counts is nonzero). - Rename ModuleIO::detail::finalize_rr_sparse_file -> assemble_csr (writes 3-field header + merges temp payload into final csr file). - Update caller pos_op_writer.cpp (include + 2 call sites) and write_hs_r_compat_test.cpp (include, 6 call sites, 4 TEST names, continuation indentation). - Wire renamed sources through Makefile.Objects and 4 CMakeLists.txt. Naming: pos_op denotes position operator; csr denotes on-disk format; lat_r in the predicate denotes lattice-R block. Pure rename, no behavior change. Compile and tests not yet run (user approval pending). * test(module_hs): add test_hsr_writer.cpp for filename-generation helpers - 13 TEST cases covering all 5 pure-string filename functions in hsr_writer.cpp: hsr_gen_fname (6), sr_gen_fname (4), dhr_gen_fname (3). - Naming rules verified: append/overwrite mode, step present/absent (including istep=-1), csr/dat extension selection, spin index offset (ispin+1). - CMake target MODULE_IO_hsr_writer_test compiles hsr_writer.cpp + ucell_io.cpp + parallel_orbitals.cpp + tmp_mocks.cpp, links parameter base device hcontainer. - All 13 tests pass: ctest -R MODULE_IO_hsr_writer_test (0.01s). * test(module_hs): add test_hs_dense_io.cpp for save_mat header format - 6 TEST cases covering ModuleIO::save_mat text and binary output: text header (step, dimension, gamma flag, rows/columns) for double and complex; binary dim header for double and complex. - Provides main() with MPI_Init + DIAG_WORLD=MPI_COMM_WORLD (same pattern as write_hsk_binary_test) since save_mat's MPI path uses MPI_Barrier and Parallel_Reduce. - Uses Parallel_2D::set_serial(dim, dim) to create a serial layout (default-constructed Parallel_2D has empty global2local_ vectors causing segfault). - CMake target MODULE_IO_hs_dense_io_test compiles hs_dense_io.cpp + parallel_2d.cpp + tool_title.cpp + base sources, links parameter. - All 6 tests pass: ctest -R MODULE_IO_hs_dense_io_test (0.63s). * refactor(module_hs): rename test files to match test_<name> convention - git mv write_hsk_binary_test.cpp -> test_hsk_writer.cpp (matches hsk_writer.cpp); CMake target MODULE_IO_write_hsk_binary_test -> MODULE_IO_hsk_writer_test (incl. parallel variant). - git mv write_hs_r_compat_test.cpp -> test_pos_op_csr.cpp (matches pos_op_csr.cpp); CMake target MODULE_IO_write_hs_r_compat_test -> MODULE_IO_pos_op_csr_test (incl. parallel variant). - Update stale file-name reference in test_hs_dense_io.cpp comment. All 4 tests pass (2 serial + 2 MPI parallel, 2.29s total). * test(module_hs): make test_hsk_writer cover write_hsk instead of save_mat test_hsk_writer.cpp included hs_dense_io.h and exercised only ModuleIO::save_mat, duplicating test_hs_dense_io.cpp, while the actual hsk_writer.cpp function ModuleIO::write_hsk had no test at all. - Move the three binary upper-triangle save_mat cases into test_hs_dense_io.cpp (HsDenseIoSaveMat suite, 6 -> 9 cases). - Rewrite test_hsk_writer.cpp around a lightweight FakeHamilt that replays per-k-point local H/S blocks: verify H(k)/S(k) files are written for every k point with the ik2iktot filename mapping and gathered upper-triangle payload, S(k) is skipped for the spin-down channel (isk == 1), and the std::complex<double> instantiation works. - CMakeLists: compile hsk_writer.cpp into the hsk test target and add np=2 ctest entries for the MPI-reduction cases of both targets. Verified: serial runs 3/3 and 9/9 passed; both np=2 ctest entries passed. * fix(module_hs): address PR review - fix dmR memory leak and dedup sparse io helpers - op_exx_lcao.cpp: replace raw `new` HContainer (never deleted, leaked per nscf EXX run) with std::unique_ptr ownership; dmR_vec is now a non-owning view passed to dm_container_to_Ds. - hs_sparse_io_detail.h: extract count_nonzeros_by_R and check_output_file_open into a shared detail header so the threshold/nonzero logic cannot diverge between the two sparse writers. - hs_sparse_io.cpp / dhs_sparse_writer.cpp: drop the verbatim-copied anonymous-namespace helpers; the unused check_output_file_open copy in hs_sparse_io.cpp (-Wunused-function) is removed. Addresses Critsium-xy review comments on PR #8033. Does not touch the MPI_Barrier, Eexx-uninitialized, PARAM.*, or PosOpCalc-test items (need deeper EXX logic / governance / new tests). * fix(test): define TestParameters at global scope to match friend declaration parameter.h declares `friend class TestParameters;`, which binds to the global-scope class. Nine test files defined the helper inside an anonymous namespace, so the friend grant did not apply and accessing PARAM.input/PARAM.sys failed with 'private within this context'. Move the helper out of the anonymous namespace and note the scope requirement in its comment. * test(module_hs): split test_pos_op_csr.cpp into per-source test files Split the 1097-line test_pos_op_csr.cpp into one-test-per-source-file per governance rule 11: - test_pos_op_csr.cpp: only lat_r_nonempty + assemble_csr (178 lines) - test_hsr_writer.cpp: merge write_hcontainer_csr/binary + write_hsr MPI gather tests into existing file; add MPI main - test_hs_sparse_io.cpp: 4 save_sparse tests (no PARAM, only GlobalV::DRANK) - test_dhs_sparse_writer.cpp: 3 save_dH_sparse tests (no PARAM) - test_output_mat_sparse.cpp: MatSparseOutputOptions default values - test_write_dmr.cpp: dmr_gen_fname + write_dmr_csr (in module_dm/test/) - csr_test_helpers.h: shared test helpers (removed sparse_format stubs that were only needed for hsr_legacy.cpp linking) - test_vxc_op_tools.cpp: new tests for vxc_op_tools functions - io_sys_var_test.cpp: replace #define private public with direct Input_para/System_para struct usage - CMakeLists.txt: register new test targets * fix(test): link device lib for module_hs tests and friend ReadInput setters - module_hs unittests: add `device` to LIBS of MODULE_IO_pos_op_csr_test and MODULE_IO_output_mat_sparse_test. memory_op.cpp explicit instantiations live in the `device` OBJECT library; parallel_device.cpp in `base` references them, so targets that link `base` must also link `device`. - read_input: add `friend class TestParameters;` so tests can drive private set_global_dir/set_globalv without `#define private public`. - io_sys_var_test: define TestParameters at global scope (matching the friend declaration) and route the 6 private calls through it. * fix(test): restore private access and use public APIs in unit tests Revert the friend-class approach introduced in a2e9fab and df78468 that broke compilation of test TUs and propagated into production TUs via read_input.h. The root cause is that `friend class TestParameters;` declared inside `namespace ModuleIO` binds to `ModuleIO::TestParameters` (not the global `::TestParameters` the tests define), so the friend grant never took effect for ReadInput's private members. - read_input.h: remove the broken `friend class TestParameters;` declaration; production TUs no longer see an undefined global TestParameters forward declaration. - io_sys_var_test.cpp, read_input_item_test.cpp: wrap read_input.h include with `#define private public`/`#undef private` to restore access to ReadInput private members (set_global_dir, set_globalv, input_lists, normalize_hs_output_options) as an interim fix. - print_info_test.cpp: use K_Vectors public test seams (set_spin_mult, read_kpoints_for_testing) instead of private members. - elecstate_pw_test.cpp: use XC_Functional::set_func_type() public static setter instead of assigning the private static member func_type directly. - read_wf2rho_pw_test.cpp: replace direct access to Charge::_space_rho with Charge::allocate() / set_rhopw() public API; remove the conflicting mock Charge::Charge()/~Charge() so the real definitions from charge.cpp link correctly. - source_io/test/CMakeLists.txt: add charge.cpp and chg_tools.cpp to MODULE_IO_read_wf2rho_pw_test SOURCES so Charge::allocate and Charge::set_rhopw symbols resolve. * refactor(hs): style cleanup for angmom_op_mat and its test - angmom_op_mat.cpp: enforce one-line-per-brace style; remove redundant `#ifdef __MPI` guards around Parallel_Common::bcast_* calls (the wrappers already guard MPI internally, per governance rule #9). - test_angmom_op_mat.cpp: replace `#define DOUBLETHRESHOLD 1e-12` with a `constexpr double kDoubleThreshold` in an anonymous namespace; replace range-for `auto` loops with classic indexed `int` loops to avoid `auto`. * refactor(hs): extract magic number and rename angmom output files - Replace 16 occurrences of the magic number 1e-12 with a named constexpr kCoeffZeroThreshold in an anonymous namespace. Comment explains why it is tighter than the project-wide sparse output threshold 1e-10: this guards an analytical zero (sqrt(l(l+1)-m(m+1)) at extremal m), not a numerical matrix element. - Rename Lx/Ly/Lz output files from `${prefix}_Lx/y/z.dat` to `lx/ly/lz_nao.txt`. The prefix parameter is kept in the signature to avoid an interface change but no longer participates in the filename. * feat(hs): add istep support to AngularMomentumCalculator for out_freq_ion - Add `istep` parameter (default -1) to `calculate()`. When istep >= 0, filenames include `g{istep+1}` (e.g., lxg1_nao.txt); when istep = -1, filenames are lx_nao.txt (overwrite mode). - Update prompt message to reflect new filename pattern. - Pass `istep_in` from ctrl_scf_lcao.cpp, which is computed by the existing out_freq_ion/out_freq_td logic. This makes the Lx/Ly/Lz matrix output follow the same frequency control as other outputs (e.g., out_hsr, out_dmr). * feat(hs): add ModuleBase::timer to out_rR and calculate - Add ModuleBase::TITLE/timer::start/timer::end to cal_r_overlap_R::out_rR and AngularMomentumCalculator::calculate, following the ABACUS governance rule #12 (timer at function entry/exit with function name as label). - Add missing #include "source_base/timer.h" in pos_op_mat.cpp. * refactor(hs): rename AngularMomentumCalculator → Angmom_op, cal_r_overlap_R → Position_op - Rename AngularMomentumCalculator class to Angmom_op in angmom_op_mat.h/cpp. - Rename cal_r_overlap_R class to Position_op in pos_op_mat.h/cpp. - Update all references in 14 files including ctrl_scf_lcao.cpp, output_mat_sparse.cpp, esolver_gets.cpp, velocity_op, td_current_io, and snap_psb_half_tddft_test.cpp. - The shorter names follow the ABACUS naming convention for operator classes (e.g., Velocity_op). * fix: post-merge compile errors (td_info includes, v_xc gga_grad, TestParameters) - td_info.h/cpp: restore includes lost in merge (abfs_vector3_order.h, parameter.h) - hterm_writer: pass new upstream gga_grad arg to XC_Functional::v_xc via WriteHParams - read_input_item_test: use TestParameters wrapper for private Parameter::input access * Unify ion-step suffix for sparse matrix outputs; fix double free in read_wf2rho test Rename outputs to carry a 1-indexed ionic-step suffix (aligned with lxg): - rr_nao.txt -> rrg{step+1}_nao.txt when istep >= 0 - trs1_nao.csr -> tr_nao.csr (drop spin index; T(R) is spin-independent), trg{step+1}_nao.csr when istep >= 0; fix g suffix placed after .csr in the md_no_append branch - dhrx/dsrx -> append g{step+1} for all steps (was only md_no_append, 0-indexed) istep == -1 keeps the no-suffix names. read_wf2rho_pw_test: remove delete[] on chg.rho/chg_ref.rho. Charge::allocate backs rho with std::vector storage (rho = _ptrs_rho.data()), so delete[] on it is a double free / heap corruption that aborts under MPI (malloc_consolidate unaligned fastbin). The Charge destructor releases the vector storage. * update example * Fix tr filename: place ion-step suffix after the tr prefix Previously the suffix was appended after the full filename, producing tr_nao.csrg1. Now strip the _nao.csr tail and re-insert it after g{step+1}, giving trg{step+1}_nao.csr, aligned with rrg/lxg naming. * Disable out_mat_dh on MPI ranks > 1 to avoid deadlock Veff::cal_dH's Hellmann-Feynman (vl) path issues a collective (Forces::cal_force_loc -> PW real2recip Alltoallv) per orbital pair inside a loop whose trip count is rank-dependent. With >1 rank the collectives are entered a different number of times per rank and the run hangs. Skip write_dH_components when GlobalV::NPROC > 1 and warn, until the HF path is made rank-lockstep. Reproducible with examples/17_relax/04_relax_with_output_lcao (out_mat_dh 1) at -np 4. * Fix MPI tag collisions in HContainer transfer; drop masking barriers Read_HContainer::read() and the serial<->parallel transfer used fixed tags 0/1/2 on MPI_COMM_WORLD, which collide with unrelated p2p traffic on other call paths and cause MPI_ERR_TRUNCATE races or deadlocks. Give them dedicated high tags (kHctTagSize/Index/Value in transfer.h) shared by both send and recv sides, and remove the three MPI_Barrier calls in op_exx_lcao.cpp that only masked this root cause. Also fixes an asymmetric tag pair: HTransPara::receive_data used tag 0 while the matching send_data used tag 2; both now use kHctTagValue. * Initialize Exx_LRI::Eexx to avoid uninitialized read in HexxR restart In the HexxR-restart branch (init_chg=file) OperatorEXX never calls cal_exx_elec, so Exx_LRI::Eexx was left uninitialized, yet the NSCF path in esolver_ks_lcao.cpp still reads get_Eexx() for the energy report. The indeterminate value could propagate into f_en.exx and cal_delta_eband(). Default-initialize Eexx to 0.0 so the report is deterministic; the value is still not the true exchange energy on that path (HexxR restart is slated for removal, so no file-format change is made). * Fix hs_sparse_io unit test link errors with hcontainer and Atom mocks MODULE_IO_hs_sparse_io_test and MODULE_IO_dhs_sparse_writer_test both include csr_test_helpers.h, which uses hamilt::AtomPair/HContainer and Atom construction. Link hcontainer OBJECT library and reuse module_hcontainer/test/tmp_mocks.cpp for the lightweight Atom stub, matching the existing MODULE_IO_hsr_writer_test pattern. * Fix dhs_sparse_writer test to read step-suffixed CSR filenames ModuleIO::save_dH_sparse writes files with a "g<step+1>" suffix when istep >= 0 (e.g. dhrxs1g6_nao.csr for istep=5), but the three tests read the suffix-less names and therefore saw empty output. Update the expected filenames and extend remove_derivative_files with an optional step parameter to clean the suffixed variants. * Rename T(R) sparse output references from trs1_nao.csr to tr_nao.csr ModuleIO::save_dH_sparse now writes T(R) files as tr_nao.csr (no spin suffix, matching the tr prefix convention), but the integration test script and documentation still referenced the old trs1_nao.csr name. Update catch_properties.sh comparison, out_mat_t description in docs, and rename the two reference files accordingly. * Clamp dH/dS sparse writer step to non-negative and rename rr ref file save_dH_sparse now uses std::max(istep, 0) for the STEP header value, consistent with hs_sparse_io and hsr_writer. This prevents STEP: -1 when out_freq_ion == 0 (istep_in defaults to -1). Also rename nscf_out_hsr_tr_rr/rr.csr.ref to rr_nao.txt.ref and update catch_properties.sh to compare against OUT.autotest/rr_nao.txt, since pos_op_writer actually outputs rr_nao.txt. * Fix cast-away-const error in dhs_sparse_writer binary writes std::ostream::write takes const char*, so reinterpret_cast to const char* directly instead of casting away qualifiers. * Revert "Disable out_mat_dh on MPI ranks > 1 to avoid deadlock" This reverts the runtime guard added in aebfad6. The guard skipped ModuleIO::write_dH_components whenever GlobalV::NPROC > 1, which broke the tests/02_NAO_Gamma/scf_out_dh integration case: its INPUT enables out_mat_dh plus the five dH component outputs, and the case runs under 4 MPI ranks, so the dhk/dtk/dvhk/dvlk/dvnlk/dvxckz files were never written and the Compare_*_pass entries all flipped to fail. The underlying multi-rank deadlock in hamilt::Veff::cal_dH's vl path is real (see reverted commit for a reproducer), but disabling the feature silently is not the right trade-off for this PR. Keep the original behaviour and leave a FIXME documenting the bug so a follow-up can fix the Hellmann-Feynman collective pattern properly. * Update rr_nao.txt.ref for unified ion-step suffix in CSR header Commit 8b2103d unified the ion-step suffix for sparse matrix outputs. The SCF path passes istep=-1 to PosOpWriter::out_lat_r, so the CSR header now writes "STEP: -1" instead of "STEP: 0". Update the two rr_nao.txt.ref files to match. Also rename scf_out_hsr_spin4/rr.csr.ref -> rr_nao.txt.ref to align with the new output filename. * fix: explicit istep >= 0 comparisons and get_s step handling - hs_sparse_io.cpp, pos_op_writer.cpp: change implicit int-to-bool conversion of istep/step to explicit `>= 0` comparison for clarity. - esolver_gets.cpp: pass -1 instead of istep to out_rR since get_s has no ionic step concept, avoiding spurious g1 suffix in filenames. - docs: update out_mat_l filenames from OUT.{suffix}_Lx.dat to lx_nao.txt / lxg{step+1}_nao.txt to match code changes. * fix: use raw istep for dH/dS filenames, clamp only header STEP value The std::max(istep, 0) clamp in save_dH_sparse made the no-suffix branch dead: SCF (istep=-1) was writing dhrxs1g1_nao.csr instead of dhrxs1_nao.csr. Use istep for filename/append decisions and clamp only the header STEP value, consistent with hs_sparse_io and hsr_writer. Also clamp the STEP value passed to assemble_csr in pos_op_writer so rr_nao.txt writes STEP: 0 instead of STEP: -1, matching the ref files. * fix: update out_mat_l description in read_inp_out.cpp to match new filenames docs/parameters.yaml is auto-generated from the C++ source; the description string in read_inp_out.cpp must be updated to keep them in sync. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
Since #7958 enabled fail_on_warning, every Read the Docs build of `latest` fails in the EPUB step with: WARNING: conf value "version" should not be empty for EPUB3 PR preview builds only produce HTML, so the failure was not caught in PR checks. Read the version from source/source_main/version.h so the docs stay in sync with releases. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Levi Zhou <31941107+ZhouXY-PKU@users.noreply.github.com>
…ims (#8072) * Update .readthedocs.yaml * Enhance LaTeX configuration in conf.py Added LaTeX preamble for Dirac notation and MathJax aliases. * Update .readthedocs.yaml
…#8057) Add a PLUMED (https://www.plumed.org) interface so that ABACUS MD runs can use collective variables, biasing potentials and free-energy methods (metadynamics, umbrella sampling, ...), following the source_md/module_* layout requested by the maintainers. - New source/source_md/module_plumed (plumed_interface.{h,cpp}, CMakeLists.txt) wrapping the PLUMED wrapper-library C API (plumed_create / plumed_cmd / plumed_finalize). - source/source_md/run_md.cpp: initialize PLUMED before the MD loop, hand over the configuration after each integrator step (positions in Bohr, masses in amu, cell, energy and virial) and add the PLUMED biasing forces in place, then finalize at the end. - New INPUT keywords (category "Molecular dynamics"): - plumed (Boolean, default False): enable the interface; ABACUS must be configured with -DENABLE_PLUMED=ON. - plumed_file (String, default "plumed.dat", availability plumed==true). - Build systems: CMake (ENABLE_PLUMED, discovered with a new FindPLUMED.cmake module that provides the imported target PLUMED::plumed and prefers pkg-config, falling back to PLUMED_ROOT) and Makefile (PLUMED_DIR) support. - Docs: docs/parameters.yaml and docs/advanced/input_files/input-main.md regenerated with the built-in generators. - Tests: source_io/test/read_input_ptest.cpp covers the new parameters (defaults and parsing). - Interface example: interfaces/PLUMED_interface (README.md + example01) runs an ethane C-C torsion well-tempered metadynamics with the plumed/plumed_file keywords. The interface currently supports a single MPI rank only, because PLUMED must see the whole system while ABACUS distributes the atoms of an MD run over the ranks; run_md.cpp stops with an explicit message when more than one rank is used. OpenMP threading inside the single rank is fine.
* Fix PW hybrid ACE with uneven k-point pools * Remove automatic PW EXX kpar regression pending test redesign * Pass local target k-point availability explicitly to PW EXX * test: cover spin-polarized PW EXX with uneven k-point pools * test: add fast AFM PW EXX kpar regression and moment checks * docs: shorten PW EXX kpar case READMEs
…olver optimizations (#8074) * Feature: Add GMRES and CN solver optimizations for PW RT-TDDFT * Add core tests for PW RT-TDDFT solver optimizations * Fix: Support older libstdc++ in sparse derivative writer initialization * Enable optimized PW RT-TDDFT solver defaults and ignore RR for non-GMRES solvers * Fix missing PW RT-TDDFT solver objects in Makefile build * Fix integer overflow in PW RT-TDDFT solver vector offsets * Handle PW RT-TDDFT preconditioner failures and test non-GMRES low-rank solves * Reuse the true residual across GMRES restarts * Reuse linear solver workspaces for PW RT-TDDFT kinetic retries * Skip zero-coefficient columns in GMRES solution updates * Reuse low-rank correction storage in PW RT-TDDFT * Avoid repeated basis hashing in PW RT-TDDFT solves * Clean up PW RT-TDDFT declarations, arguments and option parsing * Complete 64-bit indexing and simplify solver interfaces in PW RT-TDDFT
…ame INPUT exx_gamma_extrapolation -> exx_gamma_extra (#8065) * Fix PW EXX energy/stress k-weight pairing (#7426) The explicit EXX energy (cal_exx_energy_op) and stress (stress_exx) divided the q-state occupation by the current-k weight wk[ik] and applied an extra /nqs, mixing weights from two different k-points. Since wg(k,b) = wk[k]*f(k,b) and the outer ik loop already carries the wk[ik] BZ weight, the correct coefficient is wg(iq,m)*wg(ik,n)/wk[iq]. This pairs the q-state numerator with the q-state denominator, matching the act_op/act_op_kpar convention. Numerically unchanged on uniform k-grids (wk = 1/nqs); fixes the weighting for nonuniform k-point weights. * Rename INPUT parameter exx_gamma_extrapolation to exx_gamma_extra The original name was too long; shorten it while keeping the meaning. * Fix PW EXX energy/stress k-weight pairing Divide by nqs for the uniform q-mesh BZ weight and by wk[ik] to convert wg(ik) into occupation, instead of dividing by wk[iq] which incorrectly paired the k-weight with the q-state. * remove 097 tests and add two PBE0 tests as scf_pbe0_spin1 and scf_pbe0_spin2 * Rename test reference files: refelftot.cube -> elftot.cube.ref Use the more conventional .ref suffix pattern for reference files, matching the naming style of other test references (e.g. result.ref). * add elftot.cube.ref * Replace 01_PW test cases: 039->scf_Al100_surface, 801->scf_brav_sc_smear_fd Swap 039_PW_FD_smear and 801_PW_LT_sc for scf_Al100_surface and scf_brav_sc_smear_fd in both CASES_CPU.txt and CASES_GPU.txt. * Merge 021_PW_kspace3 into scf_Al100_kspace3 Move kspacing 0.5 1.0 1.0 test from 021_PW_kspace3 to scf_Al100_kspace3 (formerly scf_Al100_surface), update CASES lists, and remove the standalone 021_PW_kspace3 test. * Merge 041_PW_GA_smear into scf_brav_fcc_smear_ga Move smearing_method gauss test from 041_PW_GA_smear to scf_brav_fcc_smear_ga (formerly 802_PW_LT_fcc), update CASES lists, and remove the standalone 041_PW_GA_smear test. * Merge 042_PW_M2_smear into scf_brav_bcc_smear_m2 Move smearing_method mp2 test from 042_PW_M2_smear to scf_brav_bcc_smear_m2 (formerly 803_PW_LT_bcc), update CASES lists, and remove the standalone 042_PW_M2_smear test. * Replace H2 with Li in scf_brav_bcc_smear_m2 - STRU: H2 molecule → Li bcc (1 atom, Li-s.PD04.PBE.UPF) - INPUT: remove explicit nbands (use default) - result.ref: regenerated for Li system (etot = -188.866405) * Merge 043_PW_MP_smear into scf_brav_hex_smear_mp - 804_PW_LT_hex → scf_brav_hex_smear_mp 重命名 - STRU: H2 → Mg hcp (2 atoms, Mg_ONCV_PBE-1.0.upf) - INPUT: add smearing_method mp + smearing_sigma 0.002, remove nbands - KPT: 1x1x1 → 2x2x2 - README: add mp smearing description - CASES_CPU/GPU: remove 043, rename 804 - Remove 043_PW_MP_smear directory - result.ref: regenerated (etot = -2939.233345) * Merge 044_PW_MV_smear into scf_brav_bco_smear_mv - 811_PW_LT_bco → scf_brav_bco_smear_mv 重命名 - STRU: H2 → Be bco (2 atoms, Be_ONCV_PBE-1.0.upf) - INPUT: add smearing_method mv + smearing_sigma 0.002, remove nbands - KPT: 1x1x1 → 2x2x2 - README: add mv smearing description - CASES_CPU/GPU: remove 044, rename 811 - Remove 044_PW_MV_smear directory - result.ref: regenerated (etot = -613.513095) * Replace H2 with Fe FM in scf_brav_fcc_smear_ga - STRU: H2 → Fe fcc (1 atom, Fe_ONCV_PBE-1.0.upf, magmom=2.0) - INPUT: add nspin 2, nbands 12 - KPT: 1x1x1 → 2x2x2 - README: add FM spin description - result.ref: regenerated (etot = -2975.244491) * Remove 037_PW_FM: superseded by scf_brav_fcc_smear_ga The FM spin test is now covered by scf_brav_fcc_smear_ga (Fe fcc with nspin=2 and gauss smearing). * Lower ecutwfc to 10 Ry in scf_brav_bco_smear_mv Be pseudopotential converges well at 10 Ry; no need for 25 Ry. * Merge 040_PW_FX_smear into scf_brav_trig_smear_fx - 805_PW_LT_trigonal → scf_brav_trig_smear_fx 重命名 - INPUT: add smearing_method fixed + smearing_sigma 0.002 - README: add fixed smearing description - CASES_CPU/GPU: remove 040, rename 805 - Remove 040_PW_FX_smear directory - result.ref: regenerated * Add test case modification guide to tests/README Document the workflow for merging/renaming test cases: - git mv for directory rename - Update INPUT, README, CASES_CPU/GPU - git rm old directory - Regenerate result.ref with Single_job.sh ref - Note about smearing tests on insulators vs metals * Fix ComparePot1_pass value in scf_pbe0_spin2/result.ref CompareFile.py returns 0 on success, so ComparePot1_pass 0 means pass. The reference file incorrectly had 1, causing the test to report a deviation even when the calculated elftot.cube matched the reference. * tests: merge 052_PW_OB, 053_PW_OD, 055_PW_OW into scf_out_band_dos_wf Combine the three PW output tests (band, DOS, wavefunction) into a single SCF test case. Also enable cal_force and cal_stress, and shift one Si atom off the high-symmetry position so the force check is non-trivial. - calculation: scf, nspin=1, symmetry=1, nbands=6 - KPT: Gamma 2 1 1 - out_band=1, out_dos=1, out_wfc_pw=1, cal_force=1, cal_stress=1 - STRU: Si at (0.26, 0.25, 0.25) to break symmetry - Update CASES_CPU.txt and CASES_GPU.txt * tests: remove 031_PW_15_CF_CS as redundant 031_PW_15_CF_CS is fully covered by 032_PW_15_CF_CS_bspline: same GaAs/DOJO pseudopotentials, broyden mixing, cg solver, chg_extrap second-order, cal_force and cal_stress. 032 additionally tests B-spline interpolation (nbspline 20). * tests: remove 033_PW_CF_CS_S1_smallg and 034_PW_CF_CS_S2_smallg 033/034 are redundant with 029/030: same H2O system, same cal_force/cal_stress/small-g coverage. The only difference is the pseudopotential format (old UPF vs SG15), and the old UPF files (H.pz-vbc.UPF, O.pz-mt.UPF) are already exercised by many other tests (806-814, scf_brav_sc_smear_fd, scf_brav_trig_smear_fx). * tests: merge 063-071 cell-relax series, update scf_pbe0_spin2 ecutwfc, rename USPP tests - Merge 063_PW_CR, 066_PW_CR_fix_abc, 067_PW_CR_fix_ac, 071_PW_CR_move into relax_cell, relax_cell_fix_abc, relax_cell_fix_ac, relax_cell_move - Delete 064_PW_CR_fix_a, 065_PW_CR_fix_ab, 068_PW_CR_fix_b, 069_PW_CR_fix_bc, 070_PW_CR_fix_c (redundant fixed_axes combinations) - Update CASES_CPU.txt and CASES_GPU.txt accordingly - Regenerate result.ref for all 4 retained relax_cell_* cases - scf_pbe0_spin2: raise ecutwfc 8 -> 12, regenerate result.ref and elftot.cube.ref (grid changed 9 -> 12) - Rename 212_PW_USPP_BPCG -> scf_uspp_bpcg - Rename 213_PW_USPP_pchg_wfc -> scf_uspp_pchg_wfc * tests: update relax_cell_* README descriptions and regenerate result.ref * tests: add stress/force thresholds for GPU-sensitive PW cases - scf_brav_fcc_smear_ga: stress_threshold 1.0 (Fe fcc FM, small smearing, single-atom cell amplifies GPU/CPU floating-point differences) - scf_Al100_kspace3: force_threshold 0.001, stress_threshold 0.1 (Al(110) surface, fd smearing sigma=0.002, anisotropic kspacing) * tests: remove cal_stress from scf_pbe0_spin1 (GPU stress not implemented) * move two DFTU examples * move three 01_PW examples * tests: enable cal_force for solvation cases and rename 075/083/084 - scf_sol_H2: add cal_force 1, regenerate result.ref (totalforceref 2.675344) - scf_sol_H2O: keep cal_force 1, add commented cal_stress (not implemented) - Both INPUTs note that implicit solvation stress is not implemented yet - Update READMEs to mention force testing - Rename 075_PW_CHG_BINARY -> scf_in_chgdat - Rename 083_PW_sol_H2 -> scf_sol_H2 - Rename 084_PW_sol_H2O -> scf_sol_H2O - Update CASES_CPU.txt and CASES_GPU.txt accordingly * docs: document how to mark unimplemented features in test INPUTs Add a "Marking unimplemented features" note to tests/README explaining that unsupported INPUT parameters should be added but commented out with an explanation, so the intended coverage is documented and can be enabled later once the feature lands. * tests: remove 806-814_PW_LT_* series (redundant with scf_brav_*) The 806-814_PW_LT_* tests (st/bct/so/baco/fco/sm/bacm/triclinic) test latname recognition and Bravais lattice handling, which is already covered by the lighter scf_brav_* series (sc/fcc/bcc/hex/trig/bco) that additionally tests smearing methods. The latname parsing logic is also covered by unit tests in source/source_cell/test/. - Delete 806_PW_LT_st, 807_PW_LT_bct, 808_PW_LT_so, 809_PW_LT_baco, 810_PW_LT_fco, 812_PW_LT_sm, 813_PW_LT_bacm, 814_PW_LT_triclinic - Update CASES_CPU.txt and CASES_GPU.txt * Revert "tests: remove 806-814_PW_LT_* series (redundant with scf_brav_*)" This reverts commit 974dbbf. Restore the 8 LT series tests (806-814_PW_LT_st/bct/so/baco/fco/sm/bacm/triclinic) as they provide valuable end-to-end coverage for latname recognition with various Bravais lattices. * tests: sync CASES_CPU/GPU.txt with renamed 01_PW directories Commits 8720db7 (095_PW_NPT→md_npt, 096_PW_NVT→md_nvt, 209_PW_DFTHALF→scf_half) and e5c2b64 (815_PW_DFTU_S2_Z→scf_dftu_spin2, 816_PW_DFTU_S4_XY→scf_dftu_spin4) renamed the test directories but left the old names in CASES_CPU.txt and CASES_GPU.txt, so the integration runner would look up directories that no longer exist. * tests: remove stale renamed cases from 01_PW CASES_CPU.txt The upstream merge reintroduced old directory names that were already renamed on this branch (096_PW_NVT->md_nvt, 097_PW_PBE0->scf_pbe0_spin1, 097_PW_PBE0_FM->scf_pbe0_spin2). The integration runner reported fatal errors because those directories no longer exist. Keep 097_PW_PBE0_AFM, whose directory is still present. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
…8063) * refactor: remove dead EXX_DEV code and out_mat_dh_exx input Remove the entire dV^EXX/dR output call chain, which depended on the unmerged LibRI cal_dHs/dHs_HF APIs (LibRI PR#10) and was unreachable in develop. This includes: - the commented-out ENABLE_EXX_DEV CMake option and __EXX_DEV macro - Exx_LRI::cal_exx_dHs / dHexxs and the Exx_LRI_Interface wrappers - OperatorEXX::cal_dH and its explicit instantiations - write_dH_exx, fill_dH_exx, write_dH_exx_impl, WriteDHParams::exd/exc, setup_exx_dh_params - the out_mat_dh_exx INPUT parameter and its documentation The live out_mat_h_exx (WriteHParams::exd/exc, setup_exx_h_params, write_h_exx) path is untouched. Closes #7687. * refactor: drop leftover EXX deps from write_dh after EXX_DEV removal write_dH_sum no longer takes the unused exx_info parameter, the op_exx_lcao/exx_lri_interface includes in write_dh_terms.cpp and the Exx_LRI_Interface forward declaration in write_dh.h are removed. Verified: cmake --build build --target io_basic -j8 passes; agent_governance_check.py --staged reports no findings. * refactor: fix stale gamma-only EXX guard message in write_dh dV^EXX/dR is unavailable in this branch at any k-point setting, so out_mat_dh now quits whenever EXX is on instead of only at multi-k, and the comment no longer references the removed WriteDHParams EXX members. Verified: cmake --build build --target io_basic -j8 passes; agent_governance_check.py --staged reports no findings. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
* Refactor: remove reversed includes of derived PW basis headers Remove the remaining reversed include directions in the plane-wave basis headers: - pw_basis_k.h no longer includes pw_basis_k_big.h - pw_basis_sup.h no longer includes pw_basis_big.h Derived headers now include their base headers instead: - pw_basis_k_big.h includes pw_basis_k.h - pw_basis_big.h includes pw_basis_sup.h All call sites that need the complete derived types now include pw_basis_big.h or pw_basis_k_big.h explicitly. This completes the fix started in PR #8045 for issue #8010. Verified: full build passes, MODULE_PW (14/14) and MODULE_ESTATE_elecstate (6/6) tests pass. * Refactor: reduce PW basis header dependencies via forward declarations - pw_basis_sup.h: add missing newline at EOF - elecstate_pw_sdft.h: drop whitespace-only change - to_w90_pw.h: forward-declare PW_Basis_Big, move include to .cpp - setup_pwrho.h: forward-declare PW_Basis/PW_Basis_Big (pointer-only use) - print_info.h: forward-declare PW_Basis/PW_Basis_Big/PW_Basis_K (pointer/reference-only use) - ctrl_output_fp.h: replace includes with forward declarations - write_libxc_r.h: revert full include back to forward declarations - elecstate.h: forward-declare PW_Basis_Big instead of including pw_basis_big.h Each .cpp that dereferences these types includes the full header itself. * Fix: include pw_basis_big.h in esolver_lr_lcao_bse.cpp The file dereferences pw_big->nbz and previously relied on the transitive include of pw_basis_big.h through elecstate.h, which now only forward-declares PW_Basis_Big. Add the explicit include. Fixes CI build error: invalid use of incomplete type 'ModulePW::PW_Basis_Big' --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn> Co-authored-by: Xiaoyang Zhang <tsfxwbbzxy@163.com>
…tion header (#8066) * Refactor: split pot_surchem.hpp into pot_surchem.h/.cpp PotSurChem is a non-template class whose full implementation lived in a header; move the constructor, destructor and cal_v_eff definitions into a new pot_surchem.cpp, keep declarations in pot_surchem.h, and wire the new source file into both CMake and Makefile.Objects builds. * Refactor: split opt_tn.hpp into opt_tn.h/.cpp Opt_TN is a non-template class; move the constructor, allocate, set_para, refresh, inner_product and get_epsilon definitions into a new opt_tn.cpp. The templated next_direct stays in opt_tn.h as required. Register the new source in CMake and Makefile.Objects builds and update the two include sites (esolver_of.h, opt_tn_test.cpp). * Refactor: move non-template RI_2D_Comm helpers out of ri_2d_comm.hpp get_iat_iw_is_block, get_is_block, split_is_block and get_iwt are non-template, non-inline functions whose definitions sat in the header; they were only saved from ODR violations by a single-include accident. Move the definitions into ri_2d_comm.cpp and drop the bogus inline specifier from the declarations in ri_2d_comm.h. * Refactor: split restart_exx_csr.hpp into .h/.cpp with explicit instantiation Move the four Hexxs CSR/cereal template function definitions out of restart_exx_csr.hpp into a new restart_exx_csr.cpp with explicit instantiation for double and std::complex<double>, keeping only declarations in the header. This removes the header-implementation .hpp in line with the governance rule against propagating .hpp headers. - Fix the stale declarations in restart_exx_csr.h so write_Hexxs_csr and calculate_RI_Tensor_sparse match their definitions. - Add the cereal type-specialization headers the .cpp now needs directly (utility/complex/array/memory/valarray), previously provided transitively by the including translation units. - Wire restart_exx_csr.cpp into source_io/CMakeLists.txt under ENABLE_LCAO+ENABLE_LIBRI and into Makefile.Objects OBJS_MODULE_RI so non-LibRI builds do not compile it (it includes LibRI headers). - Update MODULE_IO_restart_exx_csr_test to compile hs_sparse_io.cpp and lat_r_csr.cpp directly instead of linking the heavy io_advanced library, and stub RI_2D_Comm helpers in tmp_mocks.cpp. Verified: cmake --build build (LIBRI=OFF) and build_max_para_test (LIBRI=ON) both succeed; OMP_NUM_THREADS=1 ctest -R MODULE_IO_restart_exx_csr passes. * Refactor: split write_eband_terms.hpp into .h/.cpp with explicit instantiation Move the write_eband_terms<TK, TR> definition out of write_eband_terms.hpp into a new write_eband_terms.cpp with explicit instantiations for (double, double), (complex<double>, double), and (complex<double>, complex<double>), mirroring the existing write_Vxc split in vxc_op_mat.cpp. The new write_eband_terms.h keeps only the declaration, with a forward declaration for Grid_Driver. - Update the single call site ctrl_runner_lcao.cpp to include the new header. - Wire write_eband_terms.cpp into source_io/CMakeLists.txt objects_advanced and Makefile.Objects OBJS_IO_LCAO. - Add the includes the .cpp now needs directly (parameter.h, sltk_grid_driver.h, parallel_orbitals.h) that were previously provided transitively by the header chain. Verified: cmake --build build (LIBRI=OFF) and build_max_para_test (LIBRI=ON) both build abacus_basic_para/abacus_max_para successfully. * Refactor: split nonlocal_maths.hpp into .h/.cpp with explicit instantiation Move Nonlocal_maths<FPTYPE, Device> template implementation from nonlocal_maths.hpp to nonlocal_maths.cpp with explicit instantiation for double/DEVICE_CPU and double/DEVICE_GPU. Update all 11 include sites to reference the new .h header. Wire nonlocal_maths.cpp into both CMakeLists.txt and Makefile.Objects. Verified: cmake --build build -j 16 (LIBRI=OFF) OK, cmake --build build_max_para_test -j 16 (LIBRI=ON) OK, ctest -R "nonlocal|fs_nonlocal|stress_nl|onsite_proj" 3/3 passed. * Refactor: split phi_operator.hpp into .h/.cpp with explicit instantiation Move template member function implementations of PhiOperator from phi_operator.hpp into phi_operator.cpp, with explicit instantiation for float and double. Remove the .hpp file and the trailing "#include phi_operator.hpp" from phi_operator.h. The template member functions (set_phi, phi_mul_dm, phi_mul_vldr3, phi_mul_phi, phi_dot_phi) are only used with float and double types, so explicit instantiation is sufficient and avoids the .hpp indirection. Verified: cmake --build build -j 16 (LIBRI=OFF) OK, cmake --build build_max_para_test -j 16 (LIBRI=ON) OK, ctest -R "gint|GINT" 3/3 passed. * style: replace #pragma once with traditional include guards Convert all 96 header files under source/ from #pragma once to #ifndef/#define/#endif include guards for consistency and portability. Guard naming: ABACUS_<relative_path_from_source>_<ext> Verified: - grep -r "#pragma once" source/ --include="*.h" --include="*.hpp" --include="*.cuh" (no matches) - agent_governance_check.py --staged (no findings) * governance: add rule 15 to block new #pragma once in headers Add AGENTS.md rule 15 prohibiting #pragma once in favor of traditional #ifndef/#define/#endif include guards. Guard macros should follow the pattern ABACUS_<relative_path_from_source>_<ext>. Add check_pragma_once to agent_governance_check.py to block new #pragma once in .h/.hh/.hpp/.hxx/.cuh files with ERROR severity (no exception allowed). * test: drop unused RI_2D_Comm stubs from source_io tmp_mocks None of the sources linked into MODULE_IO_restart_exx_csr_test (restart_exx_csr.cpp, hs_sparse_io.cpp, lat_r_csr.cpp, the test itself) reference RI_2D_Comm, so the zero/empty stubs only risked masking real link problems. Remove them together with the now-unused klist.h include. Verified: make -j 30 MODULE_IO_restart_exx_csr_test links; OMP_NUM_THREADS=1 ctest -R MODULE_IO_restart_exx_csr passes 1/1. * refactor: give phi_operator.cpp helpers internal linkage Wrap phi_mul_dm_scratch_, phi_mul_dm_finalize_ and dotc_mixed in an anonymous namespace so they are local to this translation unit instead of exporting external-linkage symbols. Verified: make -j 30 in build_max_para_test (LIBRI=ON) succeeds; OMP_NUM_THREADS=1 ctest -R "gint|GINT" passes 3/3. * style: normalize header endings after guard conversion Add the missing trailing newline after the final #endif in 42 converted headers and collapse the double blank line before #endif in 54 headers, addressing the review nits on the #pragma once -> include guard conversion. Verified: make -j 30 in build_max_para_test (LIBRI=ON) succeeds. * refactor: forward-declare heavy types in write_eband_terms.h Replace the heavy includes (potential_new.h, op_exx_lcao.h, unitcell.h, klist.h, two_center_bundle.h, parallel_orbitals.h, exx_info.h) in write_eband_terms.h with forward declarations, matching the existing Grid_Driver forward declaration. Only source_base/matrix.h and source_psi/psi.h remain, since the function signature embeds ModuleBase::matrix and psi::Psi<TK> by reference and psi::Psi's default template argument cannot be redeclared. The .cpp now includes the full headers it needs directly. hamilt::TAC and RI::Tensor are forward-declared under __EXX (TAC is a type alias and cannot be forward-declared as a class). Verified: - cmake --build build_max_para_test -j 30 (LIBRI=ON) OK - cmake --build build -j 30 (LIBRI=OFF) OK - agent_governance_check.py --staged: no findings * test: cover check_pragma_once in governance checker tests Add four cases for the new rule-15 check: - new #pragma once in a .h header is blocked (ERROR) - new #pragma once in a .cuh header is blocked (ERROR) - a traditional #ifndef/#define/#endif guard does not trigger the rule - an existing #pragma once on an unchanged line is not re-reported Verified: python3 tools/03_code_analysis/test_agent_governance_check.py passes 43/43. * docs: document rule 15 in governance docs, drop guard naming convention Sync rule 15 (no #pragma once in headers) into the full governance document and the Copilot instructions file so the three governance sources agree with AGENTS.md and the CI checker: - docs/developers_guide/agent_governance.md: add the rule to the core coding rules list and to the grading matrix as a phase-one mechanical BLOCK with no exception allowed. - .github/instructions/abacus-governance.instructions.md: add the corresponding review instruction. Remove the ABACUS_<relative_path_from_source>_<ext> guard naming convention from AGENTS.md and from the checker's remediation message. The naming pattern was only a temporary arrangement in the header conversion PR and is not part of the enforced rule. Verified: python3 tools/03_code_analysis/test_agent_governance_check.py passes 43/43. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
* Fix missing ASE convergence marker and unify relax convergence logging (#6546) bfgs_trad printed "Ion relaxation is converged!" (extra "Ion " prefix) and lbfgs printed nothing, so the ASE interface could not detect convergence and raised IndexError. Centralize convergence reporting at the relax_step exits of both relaxation paths (Relax in relax_sync, IonCellOptimizer in relax_nsync) so every method (sd/cg/bfgs/cg_bfgs/bfgs_trad/lbfgs) emits a unified format: Largest force is <x> eV/Angstrom while threshold is <t> eV/Angstrom Largest stress is <x> kbar while threshold is <t> kbar (cell-relax) Relaxation method: <method> Relaxation converged in <n> step(s). Largest force per step (eV/Angstrom): ... Largest stress per step (kbar): ... (cell-relax) Relaxation is converged! On reaching relax_nmax both paths now print a not-converged summary plus the "Relaxation is not converged after reaching relax_nmax!" marker. The per-step history formatting lives in the shared relax_history.h (format_relax_history) and truncates long runs, keeping the first/last entries. Also fix a bfgs_trad bug where CalculateLargestGrad accumulated into a member largest_grad but divided the never-assigned Ions_Move_Basic::largest_grad by lat0; both now use Ions_Move_Basic::largest_grad consistently. Tests: add test_relax_history, test_relax_nsync, test_ions_move_lbfgs and RelaxSyncSummary cases; update existing relax test expectations for the moved reporting. * Fix relax log edge cases: dry-run and fixed-lattice cell-relax Address review comments on PR #8067: 1. relax_nmax = 0 (dry-run) no longer prints the not-converged summary. Guard both relax_driver.cpp final_out() and relax_nsync.cpp relax_step() with inp.relax_nmax > 0 so the dry-run path stays silent and does not conflict with the later "DRY RUN TEST SUCCEEDS" marker. 2. cell-relax with fixed_axes = abc (or any fully fixed lattice) now prints the unified converged summary when the atomic forces converge. Extract IonCellOptimizer::print_converged_summary() and call it from all three converged exit paths: plain relax, cell-relax with fixed lattice, and cell-relax with a converged cell step. Tests: add DryRunRelaxNmaxZero, CellRelaxFixedLatticeConverged, and CellRelaxFixedLatticeNotConverged to test_relax_nsync.cpp. * Pass relax force threshold explicitly to L-BFGS; fix nsync test faults Ions_Move_LBFGS::relax_step/is_restrain now take a Relax_Criteria argument instead of reading the global PARAM.inp.force_thr_ev, which the unit tests cannot set. This fixes the false "not converged" reports in the IonCellOptimizer log tests where the input threshold was ignored. Also guard IonCellOptimizer::relax_step against relax_nmax == 0 so a dry-run terminates silently instead of emitting a not-converged marker, matching the dry-run branch in Relax_Driver::final_out. Fix the test fixture double-free: iat2it/iat2ia are owned by UnitCell's internal Statistics member whose destructor releases them, so the test must allocate them for the L-BFGS update loop but must not delete[] them in TearDown. This removes the SEGFAULT / malloc_consolidate abort. Verified: MODULE_RELAX_relax_nsync_test 9/9, MODULE_RELAX_ions_move_lbfgs_test 2/2, MODULE_RELAX_ions_move_methods_test 9/9 pass. * Restore blank line before "largest force is 0" relax log message Ions_Move_Basic::check_converged separated the convergence summary from the preceding gradient block with a blank line; the leading newline was dropped at some point, breaking the IonsMoveBasicTest.CheckConvergedCase1 reference output. Restore it so the log layout matches the documented format again. Verified: MODULE_RELAX_ions_move_basic_test 9/9, MODULE_RELAX_ions_move_cg_test 22/22, MODULE_RELAX_ions_move_sd_test 9/9 pass. * Inline relax_test SCF inputs and fix oversized CG test step Read the mock force/stress/energy data from inlined constant arrays instead of support/*.txt files. The relative ./support/ paths broke under CTest, whose working directory is the target's build dir, so the streams returned garbage and the run aborted. The inlined values are the first three steps of the original files, leaving result_ref unchanged, and the support/ directory is removed. This also fixes the energy stream accidentally reading stress.txt. RelaxSyncSummary.TwoStepConvergence used force 1.0 (~25.7 eV/Angstrom) with an identity cell, so the CG step moved the atom out of the cell and ABACUS aborted before gtest finished, failing the target under CTest. Lower the step-1 force to 0.01, still well above the 0.0257 threshold, so the step stays inside the cell while remaining non-converged. Verified: ctest -R MODULE_RELAX_relax_new_relax passes 3/3. * Rename relax tests to test_<source>.cpp and align CMake targets Align the relax unit tests with governance rule 11 so each test file is named test_<module_name>.cpp after the source file it exercises, and the CTest targets match: - relax_test.cpp -> test_relax_sync.cpp (MODULE_RELAX_relax_sync_test) - bfgs_test.cpp -> test_ions_move_bfgs2.cpp (MODULE_RELAX_ions_move_bfgs2_test) - line_search_test.cpp -> test_line_search.cpp (MODULE_RELAX_line_search_test) - ions_move_basic_test -> test_ions_move_basic.cpp - ions_move_methods_test -> test_ions_move_methods.cpp - ions_move_bfgs_test -> test_ions_move_bfgs.cpp - ions_move_cg_test -> test_ions_move_cg.cpp - ions_move_sd_test -> test_ions_move_sd.cpp - bfgs_basic_test -> test_bfgs_basic.cpp - lattice_change_basic_test -> test_lattice_change_basic.cpp - lattice_change_cg_test -> test_lattice_change_cg.cpp - lat_change_method_test -> test_lat_change_method.cpp bfgs_test.cpp in fact tests ions_move_bfgs2.cpp, so the rename removes a misleading name. All renames are git mv to preserve history; only file names and target/source references change, no test logic. Targets keep the MODULE_RELAX_ prefix so the existing `ctest -R MODULE_RELAX` CI filter is unaffected. Verified: ctest -R MODULE_RELAX passes 18/18. * Add unit tests for matrix_methods and cg_base Cover two previously untested source_relax modules, following the test_<module>.cpp naming rule: - test_matrix_methods.cpp: 13 cases over the free vector/matrix helpers. Pins down two easy-to-misuse behaviors: DotInMAndV1 (row-major) vs DotInMAndV2 (column-major), and MPlus actually dividing by its scalar. - test_cg_base.cpp: 12 cases over CG_Base. The protected normalize() is exercised via a TestableCG subclass instead of a #define protected public access hack. Brent and third_order use hand-constructed inputs with analytic expectations (linear extrapolation branch, and a genuine cubic-fit branch with a non-negative discriminant). New targets MODULE_RELAX_matrix_methods_test and MODULE_RELAX_cg_base_test. Verified: ctest -R MODULE_RELAX passes 20/20; code_quality_score 2/2 pass. * Rename source_relax test directory to unittests Rename source/source_relax/test to source/source_relax/unittests for consistency with modules that already use the unittests layout (module_proj, module_dftu). Update the parent add_subdirectory call and the stale path comment in test_ions_move_bfgs2.cpp. CI filters on the MODULE_RELAX_ target prefix and does not reference the directory name, so no workflow change is needed. Verified: ctest -R MODULE_RELAX passes 20/20 after cmake regeneration. * Deduplicate CG_Base tests into test_cg_base.cpp CG_Base's line-search helpers (setup_cg_grad, third_order, Brent, f_cal, setup_move) were tested redundantly through both subclasses Ions_Move_CG and Lattice_Change_CG, in addition to the dedicated test_cg_base.cpp. This is a misattribution: those functions live in cg_base.cpp and should be tested once against CG_Base. Merge the valuable cases (4 Brent numerical cases, 3 third_order linear fallbacks, 9-dim f_cal and setup_move) into test_cg_base.cpp as direct CG_Base tests, then remove the 12 duplicate cases from test_ions_move_cg.cpp and the 12 from test_lattice_change_cg.cpp. The subclass files now only test their own allocate/start logic. Verified: ctest -R MODULE_RELAX passes 20/20; code_quality_score 3/3 pass. * Extract structure-file writing from Relax_Driver into relax_stru_io Move the duplicated header construction, need_orbital computation and STRU/CIF write branching out of stru_out and final_out into free functions in relax_stru_io.{h,cpp}. Add test_relax_stru_io covering build_stru_header and need_orbital; wire the new source and test target into CMake. * Extract final_out convergence and dry-run messages into private methods Pull the not-converged summary and the relax_nmax=0 dry-run message out of final_out into print_not_converged_summary and print_dry_run_message, reducing final_out's cyclomatic complexity. * Cache global config in Relax_Driver and normalize indentation Read global_out_dir, deepks_setorb and MY_RANK once at relax_driver entry into members so stru_out, final_out and the EXIT-file check no longer reach into PARAM / GlobalV individually. Convert the remaining tab indentation in esolve / relax_step to spaces. * Add behavior tests locking move_cell_ions branches before refactor Drive the public relax_step to exercise the shape/volume/lat_axis_free constraints and the mixed-mbl ionic move in move_cell_ions, asserting only externally observable UnitCell state (latvec/taud/omega). These pin the current numbers so the upcoming private-method split cannot silently change the numerics. * Extract setup_ion_gradient from Relax::setup_gradient Move the ionic-gradient computation, move-flag handling, convergence check and out_level diagnostics into a dedicated private method. Pure extraction; the behavior tests added in the previous commit confirm the numbers are unchanged. * Extract setup_cell_gradient from Relax::setup_gradient Move the cell-gradient block (stress conversion, fixed_axes constraints, grad_cell fill, largest_grad / stress_thr check, and stress history logging) out of setup_gradient into a dedicated private method setup_cell_gradient. Pure cut-and-paste extraction; numerical logic is unchanged. Also drop the unused stress_ii_max local and rewrap the over-long stress print line. Verified: make MODULE_RELAX_relax_sync_test builds and ctest -R MODULE_RELAX_relax_sync_test passes (4 cell-move behavior tests plus existing regressions). * Extract print_gradient_summary from Relax::setup_gradient Move the converged / not-converged summary block out of setup_gradient into a dedicated private method print_gradient_summary. setup_gradient is now a thin dispatcher: setup_ion_gradient, then setup_cell_gradient when the cell moves, then print_gradient_summary. Pure cut-and-paste extraction; printed output is unchanged. Verified: make MODULE_RELAX_relax_sync_test builds and ctest -R MODULE_RELAX_relax_sync_test passes. * Extract update helpers from Relax::move_cell_ions Split move_cell_ions into three private helpers matching its existing Step comments: update_lattice (Step 1, latvec update and constraints), update_ion_positions (Steps 2-3, ionic displacement and coordinate update), and update_reciprocal_cell (Steps 4 and 6, reciprocal lattice refresh and next-SCF setup). move_cell_ions is now a thin dispatcher. Pure cut-and-paste extraction; numerical logic is unchanged. Verified: make MODULE_RELAX_relax_sync_test builds and ctest -R MODULE_RELAX_relax_sync_test passes (4 move-branch behavior tests plus existing regressions). * fix: add relax_stru_io.o to Makefile.Objects relax_stru_io.cpp was already part of the CMake relax target but missing from OBJS_RELAXATION, causing undefined references to relax_stru_io::build_stru_header, need_orbital, and write_stru when linking ABACUS.mpi with the Makefile build system. * fix: drop stale install of removed source_relax/unittests/support dir The support/ data files were inlined into test_relax_sync earlier and the directory deleted, but the install(DIRECTORY support ...) command remained, causing "cmake --install" to fail with "cannot find .../support". * fix(source_relax): emit converged summary for no-op relax with all atoms fixed When no atom is allowed to move in a relax calculation, relax_step returned success without printing the unified Relaxation method, Relaxation converged in ..., and Relaxation is converged! markers. Downstream tools such as the ASE interface had no explicit success marker to grep for. Call print_converged_summary(istep, false, ofs_running) in the no-op branch and add a regression test with all atoms fixed (mbl = 0). Also update the warning text to "No atoms are allowed to move!" for grammatical consistency. * refactor(source_relax): pass relax_bfgs_rmax and out_level explicitly to Ions_Move_LBFGS Move the PARAM.inp.relax_bfgs_rmax / PARAM.inp.out_level reads out of Ions_Move_LBFGS::allocate / calculate_largest_grad into the single call site Ions_Move_Methods::allocate, so the unit test no longer needs to write PARAM.input. Global dependency budget: added=2, removed=4, net_delta=-2. No behavior change; INPUT docs not affected. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
…final-frame consistency (#8061) * fix: parse f force field in STRU reader for round-trip compatibility The STRU writer (print_stru_file) emits "f fx fy fz" per-atom force fields, but parse_atom_properties() did not recognize the f token. The unrecognized token let force digits fall into the legacy movement-flag parser, setting the stream failure state and silently corrupting movement flags and subsequent properties (e.g. mag). Add an f/force/forces branch that consumes and discards the three components, keeping STRU files round-trippable. Forces are not stored in Atom; they are consumed only to preserve parsing. Add ParseAtomPropertiesForce and ParseAtomPropertiesNegativeForce tests covering movement flags, per-atom magnetism and negative force values. Issue: #8051 (item 1) Verified: - make -j 30 (build_max_para_test) passed - OMP_NUM_THREADS=1 ctest -V -R MODULE_CELL_read_atoms_helper_test: 19/19 passed - code_quality_score.py: 49 (baseline 52; pre-existing debt, parse complexity 29->32 from the new branch) * refactor: pass has_force explicitly to print_stru_file The writer inferred force presence from matrix dimensions (force.nr == nat && force.nc == 3). In relax_driver the force matrix is always allocated with nat x 3 even when cal_force=0, so the dimension check mistook zero-initialized forces for computed forces and silently exported all-zero f fields. Change print_stru_file to take an explicit has_force flag. Remove all default arguments from the signature so every call site states its intent. Add a defensive WARNING_QUIT when has_force=true but the force matrix dimensions do not match nat x 3. Call sites in relax_driver now pass inp.cal_force. Existing PrintCellTest call sites pass false (no force matrix); the with_force test passes true. Add two new tests: - PrintSTRU_zero_force_computed: has_force=true with zero matrix still emits f fields (genuine computed zeros are distinguishable from "not computed"). - PrintSTRU_force_matrix_without_flag: has_force=false suppresses f fields even when a correctly sized matrix is provided. Issue: #8051 (item 2, force part) Verified: - make -j 30 (build_max_para_test) passed - OMP_NUM_THREADS=1 ctest -V -R MODULE_CELL_print_cell_test: 9/9 passed * feat: centralize STRU header generation and add unit tests - extract Relax_Driver::build_stru_header into relax_stru_header.cpp - write 3 N/A stress lines when stress is unavailable, with a reason note - omit per-atom f fields and document it when cal_force=0 - add MODULE_RELAX_relax_driver_test for all header branches * fix: mark stale force/stress in STRU_FINAL on early relaxation exit Track geometry_evaluated in the relax main loop. When the optimizer proposes a new geometry but the loop exits before the next esolve(), the force/stress matrices still hold values from the previous geometry. - pass geometry_evaluated into final_out() - write 3 N/A stress lines and a NOTE comment when the final geometry was not evaluated - omit per-atom f fields in STRU_FINAL for unevaluated geometry - add PreviewFinalHeaders test to show both header variants * refactor: enforce fixed 7-line STRU header layout Move the reason text out of the stress lines and into a single NOTE line (line 7) so that: - lines 1-6 are always version / timestamp / energy / 3 stress rows - stress rows carry only pure values or "N/A N/A N/A", one token shape - line 7 summarizes state (computed / cal_stress=0 / cal_force=0 / geometry not evaluated) for humans Keeping the header at exactly 7 lines lets downstream parsers skip a fixed-size comment block regardless of which quantities were computed. Verified: ctest -V -R MODULE_RELAX_relax_driver_test (7/7 pass); MODULE_CELL_read_atoms_helper_test and MODULE_CELL_print_cell_test still pass. * docs: document STRU f field and 7-line header layout - stru.md: add f/force/forces keyword (output-only, eV/Angstrom, consumed but ignored on read) and a new section describing the fixed 7-line header written by out_stru, including the 5 NOTE states and the N/A stress convention. - input-main.md / parameters.yaml: cross-link cal_force and cal_stress to their effect on structure-file output. - read_atoms_hlp_test.cpp: add RoundTripWriterReaderForce test that feeds parse_atom_properties an atom line in the exact format produced by print_stru_file and verifies the stream stays healthy, closing the round-trip regression from issue #8051. Verified: ctest -V -R MODULE_CELL_read_atoms_helper_test (20/20 pass). * docs: prefer merge over rebase when syncing upstream Add a rule to AGENTS.md that upstream synchronization should default to "git merge upstream/develop" rather than "git rebase". Merge preserves local commit SHAs, keeps the branch pushable without --force, and avoids re-applying a long patch series when conflicts arise. Rebase is reserved for cases where the user explicitly asks for a linear history. * fix: add relax_stru_header.o to Makefile build The Makefile-based build (source/Makefile.Objects) was missing relax_stru_header.o in OBJS_RELAXATION, causing an undefined reference to Relax_Driver::build_stru_header when linking with the Intel toolchain. * test: reword comment to avoid PARAM token in test_relax_driver The governance global-dependency checker counted the literal string "PARAM.globalv" inside a comment as a new global reference. Reword the comment so it no longer trips the budget check; the test itself never touched global state. * docs: sync parameters.yaml with C++ source Remove two stale description lines from cal_force and cal_stress that are no longer present in source/source_io/module_parameter/read_inp_sys.cpp, and regenerate the auto-derived input-main.md accordingly. * Always write STRU positions as Cartesian_angstrom in print_stru_file Decouple the coordinate format from force availability: remove the direct parameter and the use_cartesian branch so positions are always written as Cartesian_angstrom, while has_force only controls the per-atom f fields and the unit annotation. This restores the pre-e8953b3dd output format for cal_force=0 and early-exit STRU_FINAL, and makes STRU_NOW/STRU_FINAL share one coordinate format within a run. * Add tests for geometry_evaluated termination paths and multi-atom round-trip - test_relax_nsync.cpp: assert ionic_position_updated == false when relax_nmax terminates IonCellOptimizer::relax_step (geometry untouched). - test_relax_sync.cpp: add RelaxSyncMaxStep test locking that Relax::relax_step moves geometry and returns false when not converged (no max-step guard). Move Test_RELAX static constexpr members to anonymous namespace constants and use std::vector for iat2it/iat2ia in the new test. - test_relax_driver.cpp: add EarlyExitGeometryNotEvaluated and ConvergedExitGeometryEvaluated tests for build_stru_header behavior. - read_atoms_hlp_test.cpp: add RoundTripMultiAtomMixedFields test covering 3 consecutive atoms with mixed m/mag/f fields to catch stream corruption. Issue: #8051 * fix(relax): mark STRU_FINAL energy N/A when geometry not evaluated When the optimizer proposes a final geometry that is never evaluated (early exit via relax_nmax or EXIT file), STRU_FINAL contained the new coordinates but the energy from the previously evaluated step. A parser reading the header line pairs the stale energy with the new geometry. Print "Energy: N/A" on the header step line whenever geometry_evaluated is false, mirroring the existing stress N/A handling, so the energy label and the written coordinates always describe the same frame. The 7-line fixed header shape is preserved. - relax_stru_io.cpp: branch energy printing on geometry_evaluated; update NOTE text to "energy N/A; stress N/A; forces omitted". - relax_stru_io.h: document that energy is also marked N/A. - unittests/test_relax_driver.cpp: assert Energy: N/A and the new NOTE text in the not-evaluated cases. - docs/stru.md: document the Energy: N/A rule and updated NOTE. * fix(test): trigger eof explicitly in RoundTripMultiAtomMixedFields The parser consumes the trailing newline in parse_atom_properties, so ifpos.eof() is not set until a read past end-of-file is attempted. Insert ifpos.get() before the eof assertion to make the stream state deterministic and fix the MODULE_CELL_read_atoms_helper_test failure. * fix(test): own iat2it/iat2ia via Statistics in ConvergedPrintsSummary The test pointed ucell.iat2it/iat2ia at std::vector storage, but Statistics' destructor delete[]s those pointers. The alloc/dealloc mismatch corrupted the heap, so the next test (TwoStepConvergence) aborted in malloc_consolidate when the suite ran in sequence. Allocate with new int[] so ownership follows the same convention as the other fixtures in this file. Verified: relax_sync_test 10/10 and source_relax make test 22/22 pass. * Test: make multi-atom STRU round-trip real and drop pseudo integration tests - Rewrite RoundTripMultiAtomMixedFields to write a 3-atom spin-polarized cell with print_stru_file (has_force=true, nspin=2) and parse the produced file back, asserting positions, movement flags and mag per atom with the correct ia index (previously all three atoms used ia=0). - Drop EarlyExitGeometryNotEvaluated, ConvergedExitGeometryEvaluated and PreviewFinalHeaders: they only passed a fixed bool to build_stru_header without exercising driver logic and duplicated existing header tests. - Move the remaining header behavior tests into test_relax_stru_io.cpp and remove test_relax_driver.cpp and its CMake target. Issue: #8051 --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
…#8064) * diagnose: preserve failure evidence for ASE plugin CI (issue #7794) The intermittent "Can't find even an electron" error from module_charge::sum_rho only reproduces on remote CI in the ASE plugin workflow, and the ABACUS run directory is destroyed by tempfile cleanup before any post-mortem is possible. Add three lightweight diagnostics that together make the next CI failure self-describing, without changing any computation: - module_charge::sum_rho now prints per-spin electron counts, nrxx, omega and nxyz to GlobalV::ofs_warning just before WARNING_QUIT, so we can tell "initial atomic rho failed" apart from "rho collapsed after GINT / mixing at some SCF step". - ASE AbacusTemplate.execute() now also surfaces the last 200 lines of OUT.<suffix>/running_<calculation>.log in the SubprocessError message, so the drho history shows up directly in the CI log without needing to download artifacts. - ASE scf.py test preserves the whole ABACUS run directory under ASE_ABACUS_KEEP_DIR (default /tmp/abacus_ase_failure) on failure, and ase_plugin_test.yml uploads that directory as an artifact when the job fails. No INPUT parameter behavior change; no changes to docs/parameters.yaml or docs/advanced/input_files/input-main.md required. * fix(charge): route sum_rho diagnostics through WARNING_QUIT The per-spin diagnostic block added for issue #7794 wrote directly to GlobalV::ofs_warning, which breaks the PR global-dependency budget (net_delta=+3). Assemble the message with std::ostringstream and pass it as the WARNING_QUIT description instead; WARNING_QUIT already writes to the warning log, so the diagnostic output is preserved while chg_tools.cpp adds no new GlobalV references. Verified: python3 tools/03_code_analysis/agent_governance_check.py --staged reports no findings. * fix(ase-ci): preserve failure evidence and sharpen sum_rho diagnostics Address review comments on the #7794 diagnostics PR: - sum_rho: restore the searchable "Can't find even an electron!" text at the head of the WARNING_QUIT message and drop the stray leading space that produced an odd blank line in the NOTICE block. - sum_rho: accumulate per-spin sums in the main grid pass (one small reduce) instead of re-scanning the grid with a collective per spin channel inside the error branch. - sum_rho: catch NaN totals explicitly (NaN <= 0.1 is never true) and report rho min/max plus the negative-grid-point count, which distinguishes "rho driven negative by mixing" from "rho collapsed" when nspin0 == 1 makes the per-spin value redundant. - ASE tests: extract preserved_tmpdir() into tests/common.py and use it in scf/relax/md/band/magnetic so every script (not only scf.py) keeps its run dir under ASE_ABACUS_KEEP_DIR on failure; previously those TemporaryDirectory cases yielded a silently empty artifact. - abacuslite: tail stdout/stderr (200 lines) and the SCF running log (100 lines) in the SubprocessError message instead of dumping full logs; abacus.out already carries the SCF table, and the run dir is preserved separately for full post-mortem. - ase_plugin_test.yml: bump upload-artifact to v7 to match the other workflows and fix the stale comment claiming all scripts preserve. Verification: - g++ -std=c++11 -fsyntax-only chg_tools.cpp (project includes): pass - cmake --build build --target elecstate: pass - cmake --build build --target MODULE_IO_read_wf2rho_pw_test: pass (fixes reduce_pool<long> undefined reference by counting in double) - python3 -m py_compile tests/{common,scf,relax,md,band,magnetic}.py and abacuslite/core.py: pass - exercised preserved_tmpdir success/failure paths locally: failure dir copied to ASE_ABACUS_KEEP_DIR, success dir removed - Full ASE xtest.sh requires the compiled abacus binary: CI only. * fix(charge): drop MPI collectives from sum_rho abort branch The diagnostics in the sum_rho <= 0.1 abort branch used MPI_COMM_WORLD reduce_min/reduce_max, but the branch condition is decided by a POOL_WORLD reduction. With uneven pool sizes the pool sums can differ in the last bits, and a NaN may be present in a single pool, so pools can disagree on entering the branch and the world collectives hang instead of quitting. Report rank-local min/max/negative-count values with no communication instead. * fix(charge): include <vector> directly in chg_tools.cpp std::vector is used for sum_is, aux_vec, rhocg and vg; stop relying on a transitive include. --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn>
* Feature: Add configurable orthonormalization for PW RT-TDDFT * Enforce PW RT-TDDFT orthonormalization acceptance criteria * Reduce norm-check overhead and remove redundant initial-state orthonormalization * Reduce PW RT-TDDFT diagnostic overhead and standardize logging * Name Newton–Schulz constants * Add key orthonormalization tests * Clarify pre-orthonormalization residual reporting and tolerance semantics * Skip orthonormalization when orbitals already satisfy the tolerance * Correct comments describing the single-precision diagonalization threshold floor * Remove redundant Newton-Schulz matrix multiplications and reuse iteration buffers * Clarify step-wide maxima in orthogonality diagnostics * Use current charge and kinetic-energy densities for cube output * Restore on-demand orthonormalization within the initial PW RT-TDDFT SCF * Share linear-algebra workspace between solving and orthonormalization * Use global k-point indices consistently in diagnostics * Use a single log stream throughout the PW RT-TDDFT solver
…to .h (#8095) * Refactor: split ri_util.hpp into ri_util.h/.cpp update_coulomb_param and update_coulomb_settings are non-template functions that were declared in ri_util.h but defined inline in ri_util.hpp. Move their definitions into a new ri_util.cpp, drop the .hpp include from ri_util.h, and register the source in both CMake (ENABLE_LIBRI + ENABLE_LCAO) and Makefile.Objects (OBJS_MODULE_RI). - Remove the unused local nspin0 in update_coulomb_param, which was the only PARAM read in this code. - spar_exx.cpp now includes ri_util.h instead of ri_util.hpp. - exx_opt_orb.cpp, gaussian_abfs.cpp and symm_rot_out.cpp use PARAM but only obtained parameter.h transitively through ri_util.hpp; include it directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Refactor: split elpa_generic.hpp into elpa_generic.h/.cpp elpa_generic.hpp held inline C++ overloads (elpa_set, elpa_get, elpa_eigenvectors, elpa_cholesky, ...) that mirror the C11 _Generic dispatch of ELPA's own elpa_generic.h. Keep the overload declarations and their doc comments in a new elpa_generic.h, and move the definitions with their per-ELPA-version branches into a new elpa_generic.cpp. - elpa_new.h still includes the wrapper only for ELPA < 2022.11.001, which ships no C++ interface; elpa_generic.cpp is guarded by the same ELPA_API_VERSION < 20221101 condition so it compiles to nothing for newer ELPA versions. - The overload signatures are identical across all version branches, so the header declares them once without version conditionals. - Function bodies are unchanged apart from dropping `inline`. - Register elpa_generic.cpp in the genelpa CMake object library and in Makefile.Objects (OBJS_HSOLVER_LCAO). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Fix: keep ELPA generic wrappers inline in elpa_generic.h Moving the ELPA C++ overloads out of line in the previous commit broke linking on CI (GNU, extra components, abacuslite, Test): - ELPA <= 2022.05 headers declare the float hermitian multiply routine as `elpa_hermitian_multiply_df`, but the library exports `elpa_hermitian_multiply_f`. An out-of-line wrapper always references the misdeclared symbol, giving `undefined reference to elpa_hermitian_multiply_df`. - ELPA only builds single-precision routines with --enable-single-precision, so out-of-line float wrappers would also fail to link against such builds. - MODULE_BSE_solver_test calls the wrappers without linking the genelpa object library, giving undefined references to elpa_set and elpa_eigenvectors. Inline wrappers are only emitted when called, so unused overloads never reach the linker. Restore the original inline definitions in elpa_generic.h and drop elpa_generic.cpp from CMake and Makefile.Objects. The .hpp is still gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… autoset (#8079) * Fix #5021 #5939: warn on ignored x/y magnetization and remove nspin=4 autoset When nspin=4 with noncolin=0, the x/y components of the initial magnetization from STRU are silently zeroed. Now a warning is printed to both stdout and running_scf.log, telling the user that x/y are ignored and suggesting to set noncolin=1. When nspin=4 and no initial magnetization is set in STRU, the code previously auto-assigned (1,1,1) to all atoms. This is removed; the calculation now starts from zero magnetic moment with a warning, letting the user decide explicitly. nspin=2 autoset behavior is unchanged. Also adds unit tests for both behaviors and updates parameter docs. * Fix parameters.yaml sync: add noncolin notes to C++ description source docs/parameters.yaml is generated from item.description in C++ source. Commit 98e3f81 added two noncolin notes to the generated yaml and input-main.md but omitted the generator source, making the yaml consistency CI check fail. Add the same two notes to the noncolin description in read_inp_estruc.cpp so regeneration matches. * Test: update ReadAtomPositionsAutosetMag for nspin=4 zero-moment start Follow-up to #5939: nspin=4 with no mag in STRU no longer autosets (1,1,1); the unit test still expected the old forced moments. Assert zero magnetization instead and verify the new warning on both stdout and the running log. * Test: split cal_ux tests out of MODULE_CELL_unitcell_test Move JudgeParallel, CalUx1 and CalUx2 to the new test_cal_ux.cpp / MODULE_CELL_cal_ux_test target, one test file per source file. The new target compiles ../cal_ux.cpp directly like the old target did; unitcell_test no longer needs it. * Test: split read_orb tests out of MODULE_CELL_unitcell_test Move ReadOrbFile (kept under the __LCAO guard as before) and ReadOrbFileWarning to the new test_read_orb.cpp / MODULE_CELL_read_orb_test target. read_orb.cpp is already part of the test-only cell_info object library, so no extra source is compiled in. * Test: move compare_atom_labels/print_unitcell_pseudo tests to readpp suite CompareAatomLabel and PrintUnitcellPseudo exercise read_pp_ucell.cpp, so move them from MODULE_CELL_unitcell_test into the existing MODULE_CELL_unitcell_test_readpp target. The rebuilt cell needed by PrintUnitcellPseudo uses the C1H2-Index prepare entry. Drop the now unused read_pp_ucell.h include from unitcell_test.cpp. * Test: split update_cell tests out of MODULE_CELL_unitcell_test Move RemakeCell, RemakeCellWarnings, PeriodicBoundaryAdjustment2 and UpdateVel to the new test_update_cell.cpp / MODULE_CELL_update_cell_test target. The commented-out PeriodicBoundaryAdjustment1 block moves as well and stays commented. update_cell.cpp is already part of the test-only cell_info library. Drop the now unused update_cell.h include from unitcell_test.cpp. * Test: move print_cell/print_tau tests into MODULE_CELL_print_cell_test PrintCell, PrintTauDirect and PrintTauCartesian exercise print_cell.cpp, the same source as the PrintSTRU tests already living in test_print_cell.cpp. Move them into that file/target and reuse the PrintCellTest fixture. Drop the now unused print_cell.h include and the stale PrintSTRU navigation comment from unitcell_test.cpp. * Test: split cell_tools free-function tests out of MODULE_CELL_unitcell_test Add test_cell_tools.cpp / MODULE_CELL_cell_tools_test for the unitcell:: free functions in cell_tools.cpp: - new IfCellCanChange truth-table case over the lattice-axis flags - vector get_atomCounts/get_lnchiCounts cases split out of the mixed member/free GetAtomCounts and GetLnchiCounts tests (the member get_atom_Counts/get_lnchi_Counts map assertions stay in unitcell_test) - SelectiveDynamics (if_atoms_can_move) moved verbatim Setup keeps only the setup_from_input member assertions; its if_cell_can_change checks are now covered by the dedicated truth table. Drop the unused cell_tools.h include from unitcell_test.cpp. * Test: split read_atom_species tests out of MODULE_CELL_unitcell_test Move the 7 STRU header-parsing cases (read_atom_species and read_lattice_constant, both in read_atom_species.cpp) to the new test_read_atom_species.cpp / MODULE_CELL_read_atom_species_test target, kept under the __LCAO guard as before. ReadAtomSpeciesWarning5 uses the death-test fixture alias; the rest use the new ReadAtomSpeciesTest fixture. Drop the stale doc entries from unitcell_test.cpp. * Test: split read_atoms tests out of MODULE_CELL_unitcell_test Move the 17 read_atom_positions cases (spin/coordinate modes plus the five warning cases) to the new test_read_atoms.cpp / MODULE_CELL_read_atoms_test target, kept under the __LCAO guard. This removes the LCAO block and UcellTestReadStru fixture from unitcell_test.cpp, which now holds only the 9 UnitCell member-function tests. Also de-duplicate the triple read_stru.h include. The 6 GlobalV references are migration-neutral (added = removed). * update initialized mag * move 057_PW_SO_IW test to scf_in_wf_spin4 * move 035_PW_15_SO to scf_spin4 * add comments about mag for nspin=4 calculations * try to fix bug for nspin=4 * update mag * fix bug by change slightly the reference forces of the example 03_NAO_multik/scf_out_dos_spin4 * fix(cell): report the nspin=4 zero-moment start on every code path The zero-moment warning added for #5939 was unreachable in the two cases that matter most: * autoset_magnetization() keyed the check on atom.mag, which for a three-component "magmom" holds the full norm including x/y. With nspin=4 and noncolin=0 the x/y components are projected away, leaving m_loc_ = (0,0,0) while mag stays non-zero, so the warning was skipped for exactly the silent zero-moment start it was meant to surface. * read_atom_positions() only calls autoset_magnetization() when symmetry != 1, and symmetry defaults to 1 for nspin=4 + noncolin=1 + lspinorb=0 scf runs with k-points, so the documented warning never appeared there either. Split the magnetization reporting out of autoset_magnetization(): * is_magnetization_all_zero() shares the all-zero test. * warn_zero_magnetization_nspin4() keys on m_loc_, the vector cal_ux() actually consumes, and read_atom_positions() calls it for every symmetry setting. * autoset_magnetization() now only handles the nspin=2 autoset. Also stop printing the x/y-discarded warning once per atom. The block right below it in process_magnetization() throttles its output for the same reason; a 2000-atom cell with tilted moments produced ~4000 lines of identical text before SCF started. process_magnetization() now returns whether the projection discarded anything and read_atom_positions() aggregates it per atom type, reporting the affected-atom count once. Both warnings now also go through ModuleBase::WARNING so they reach warning.log, and the multi-line text is written straight to ofs_running instead of through GlobalFunc::OUT, whose setw(40) key/value layout was broken by the embedded newlines. Finally, the "initial magmom is all zero and symmetry=1" message is no longer printed for an explicitly magnetized STRU. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 * docs: align the magnetization autoset docs with the new behavior docs/advanced/input_files/stru.md still told users that nspin=4 autosets "mag 1 1 1" on every atom, which this branch removes; that page is where the STRU "mag" field is looked up, so it contradicted the notes added to input-main.md. Rewrite the section to describe the zero-moment start, the explicit-mag example, the x/y discard under noncolin=0, and the symmetry=1 case in which the nspin=2 autoset is skipped as well. Also record the autoset rules under "nspin" itself rather than only under "noncolin": the behavior is a property of nspin. parameters.yaml and input-main.md are regenerated from the C++ Input_Item description so the documentation-consistency check stays satisfied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 * chore: follow the test rename and separate a shared test log * .ci/slurm/config.ini still registered case.080 as 01_PW/035_PW_15_SO, a directory this branch renamed to scf_spin4, so that GPU case looked for a path that no longer exists. (19 further entries in that file already point at missing directories from earlier renames; those are left alone.) * PrintCellTest.PrintCell and UcellTest.PrintUnitcellPseudo both used "printcell.log". Before the unitcell_test.cpp split they shared one translation unit and ran sequentially; they are now separate executables that AddTest runs in the same working directory, so under ctest -j each could read or delete the other's file. Give the print_cell test its own name. * Keep the #pragma omp parallel for adjacent to the loop it governs in noncolin_rho(): the explanatory comment sat between the two, which compiles but invites an edit that drops a statement into the gap. * Strip the trailing blanks added to the regenerated 03_NAO_multik/scf_out_dos_spin4 INPUT and STRU. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DNd7fpeHXHuL9DKME85J44 --------- Co-authored-by: abacus_fixer <mohanchen@pku.eud.cn> Co-authored-by: Critsium <tsfxwbbzxy@163.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: refresh periodic ghost image shifts with skin * test: adapt periodic ghost regression to merged MD API * fix: track periodic image changes across skin updates * fix: rebuild neighbors when atoms cross periodic boundaries * test: cover periodic wrap rebuilds and repeated crossings --------- Co-authored-by: Fei Yang <2501213217@stu.pku.edu.cn> Co-authored-by: Mohan Chen <mohanchen@pku.edu.cn>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation The original Parallel_Grid::init used an in-function #ifndef __MPI ... return; guard, which effectively split the function into two halves. Readers had to infer that "the serial build stops here while the parallel build continues", violating clear control-flow and increasing maintenance burden.
Changes
Split the original init into two single-responsibility functions:
init_serial(...): assigns dimension members, validates nczp < 0, asserts sizes, and computes ncxy/ncxyz (shared by both builds).
init_parallel(nprocgroup): partitions processes across Pools by KPAR, allocates numz/startz/whichpro/whichpro_loc, and calls z_distribution().
The new init(...) orchestrates both: calls init_serial always, then init_parallel under #ifdef __MPI.
Removed the old init implementation and a now-stale comment, eliminating the mid-function #ifndef __MPI early return.
Added init_serial / init_parallel private declarations to the header.
Result
Logically equivalent; behavior is unchanged.
Serial vs. parallel code paths are now clearly separated, improving readability and maintainability.
Testing
Serial build (-DENABLE_MPI=OFF) passes
Parallel build (-DENABLE_MPI=ON) passes