Skip to content

Refactor klist, fix a few bugs - #7897

Open
mohanchen wants to merge 22 commits into
deepmodeling:developfrom
mohanchen:2026-09-03-fixk
Open

Refactor klist, fix a few bugs#7897
mohanchen wants to merge 22 commits into
deepmodeling:developfrom
mohanchen:2026-09-03-fixk

Conversation

@mohanchen

@mohanchen mohanchen commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Refactor klist, fix a few bugs

abacus_fixer added 2 commits September 3, 2026 08:34
Rename the private member K_Vectors::nspin -> spin_mult (together with
get_nspin/set_nspin -> get_spin_mult/set_spin_mult). The old name collided
semantically with PARAM.inp.nspin: the member holds the k-point spin
multiplicity (1 or 2; nspin=4 is mapped to 1 at set() time), not the
physical spin index (1/2/4). The new name matches the inherited
ReciprocalGrid::spin_factor() override and the member's actual role as
a k-point doubling factor.

Fix two bugs surfaced by the rename:
- K_Vectors::set() now maps spin_mult directly from the input nspin_in
  (print -> validate -> map) instead of the previous "assign first, then
  remap itself" flow that also left the member written before validation.
- K_Vectors::set_after_vc() no longer accepts an nspin argument and no
  longer calls set_spin_mult(): the spin multiplicity is fixed by set()
  and never changes during a run. The old code passed the raw, unmapped
  input nspin (e.g. 4 for non-collinear) and wrote it into spin_mult,
  silently overwriting the correct mapped value in nspin=4 vc-relax
  calculations. The remaining work in set_after_vc is purely a Cartesian
  coordinate refresh (kvec_d2c) plus logging.

Remove the now-unused set_spin_mult() setter (the only external caller was
set_after_vc itself). Update production and test call sites (esolver_fp,
klist_test, klist_test_para, print_info_test, write_dmk_test) accordingly.

API surface: K_Vectors::set() and the data members exposed through isk/nks/
nkstot remain unchanged. No INPUT or PARAM behavior is altered.
@mohanchen mohanchen added Bugs Bugs that only solvable with sufficient knowledge of DFT Refactor Refactor ABACUS codes labels Sep 3, 2026
abacus_fixer added 19 commits September 3, 2026 09:05
After the rename of K_Vectors::nspin -> spin_mult, two kinds of latent
misuses in the tests became visible and are corrected here:

1. In klist_test_para.cpp, set() calls had been passing kv->spin_mult as
   the nspin_in argument. That was only working by accident (both sides
   happened to be 1). Since set() takes the *physical* nspin (1/2/4,
   which it then maps to spin_mult internally), replace the member
   argument with explicit physical-nspin literals, and remove the now-
   redundant pre-set of the member (set() overwrites it anyway).

2. In klist_test.cpp (SetKupKdown test), the non-collinear case had
   been written as kv->spin_mult = 4, which is not a legal spin_mult
   value (only 1 and 2 are). K_Vectors maps nspin=4 -> 1 (non-
   collinear does not double the k-point list); the 4 was only masking
   a coincidentally matching renew(nkstot*4) size and a set_kup_and_kdw
   default branch that did nothing. Change it to the correct mapped
   value (1), add case A/B/C comments explaining the physical spin
   source for each case, and remove the bogus isk[ik+5/10/15] checks
   that relied on the accidentally oversized container.

All production call sites already pass the physical nspin correctly
(inp.nspin / inp_->nspin), so no source changes are needed there.
…ctions

Step 1 of the klist.cpp quality refactor. Move the two IBZ reduction
table builders out of K_Vectors::reduce_by_symmetry into this-free
helpers KListSymm::ibz_kpt_table / KListSymm::ibz_wk_table defined at
the top of klist.cpp (deliberately not static, not in an anonymous
namespace). This cuts the function's cyclomatic complexity from 22 to
20 and isolates pure string formatting from the symmetry logic.

The helpers live in klist.cpp rather than a new TU because dozens of
test targets across the tree compile klist.cpp alone; a separate TU
would need registering in every one of them. File splitting into
self-contained TUs happens in later steps.

Also fix a pre-existing build break in write_dmk_test.cpp: the
'#define private public' placed before write_dmk.h corrupted <sstream>
("__xfer_bufptrs redeclared with different access"). Pre-include the
standard headers so their include guards are set before the macro.

Verified: full build (BUILD_EXIT=0) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)" -> 5/5 passed in
build_max_para_test (MPI build, GNU 11, ENABLE_MPI=ON).
The previous commit pre-included <sstream>/<fstream>/... before
'#define private public', but write_dmk.h reaches <sstream> indirectly
through global_variable.h -> <iomanip> -> bits/quoted_string.h. Without
<iomanip> pre-included, quoted_string.h is first parsed while the
'private'/'public' macro is active, corrupting libstdc++ and failing
CI with "'...__xfer_bufptrs' redeclared with different access".

Pre-include <iomanip> as well so the whole chain is guard-protected
before the macro is defined.

Verified: cmake --build --target MODULE_IO_write_dmk (exit 0) and
ctest -R MODULE_IO_write_dmk -> 2/2 passed in build_max_para_test.
Move the IBZ table formatters and line-mode interpolation out of
klist.cpp into a new source_cell/klist_io.{h,cpp} (namespace KListIO):

- ibz_kpt_table / ibz_wk_table: pure string formatting, unchanged output.
- interp_line: this-free line-mode interpolation returning LineK
  {kpts, segids, nks_total}; K_Vectors::interpolate_k_between becomes a
  thin wrapper that sizes members and copies results back.

klist.cpp: 926 -> 830 lines, quality score 6 -> 12; klist_io.cpp scores
100. reduce_by_symmetry and parse_kfile lose their largest this-free
blocks.

CMake: klist_io.cpp added to the cell library; 16 test targets that
compile klist.cpp without linking cell_info also register klist_io.cpp.

Verified: cmake --build build_max_para_test -j30 (exit 0) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)|MODULE_IO_...|
MODULE_ESTATE_...|MODULE_HSOLVER_pw|MODULE_LCAO_deltaspin" -> 27/27.
Move the this-free triple loop that groups k points into EXX k-stars out
of K_Vectors::reduce_by_symmetry into KListIO::build_kstars. The symmetry
comparison is passed in as a std::function so klist_io.cpp does not depend
on the Symmetry header; k restriction uses ModuleCell::restrict_kpt.

Behavior is unchanged for symm_flag==1 (the only path that reaches this
code). klist.cpp drops ~20 lines; the loop is now isolated and testable.

Verified: cmake --build build_max_para_test -j30 (exit 0, __EXX on) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)" -> 5/5.
The this-free helpers extracted into source_cell/klist_io.cpp are
referenced by klist.cpp, but the Makefile object list was not updated
when the CMake build gained the new TU, breaking the Makefile link with
undefined references to KListIO::ibz_kpt_table/ibz_wk_table/interp_line.
Move the this-free flatten/scatter loops in K_Vectors::mpi_k into
KListIO::pack_kpts (rank-0 -> contiguous broadcast buffers) and
KListIO::unpack_kpts (broadcast buffers -> this pool's k-point slice).
mpi_k keeps the broadcast calls and the renew/distribute orchestration.

pack_kpts sizes the kvec_c_full copy from the vector length so the full
grid is handled independently of nkstot.

Verified: cmake --build build_max_para_test -j30 (exit 0, __MPI on) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)" -> 5/5 (covers the
para1/para4 mpi_k paths).
Remove the GlobalV::ofs_warning dependency from K_Vectors::generate_kfile
by taking the stream as a parameter; the single caller (read_kpoints)
passes GlobalV::ofs_warning. Private signature only, no public interface
change. global_dependency count in klist.cpp drops by 2.

Verified: cmake --build build_max_para_test -j30 (exit 0) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)" -> 5/5.
Remove the GlobalV::MY_RANK/MY_POOL dependency from K_Vectors::mpi_k by
taking them as parameters; the single caller (set()) passes the GlobalV
values. Private signature only, no public interface change.
global_dependency count in klist.cpp drops by 5.

Verified: cmake --build build_max_para_test -j30 (exit 0, __MPI on) and
ctest -R "MODULE_CELL_(klist|reciprocal_grid|qlist)" -> 5/5.
… KListIO

Move the K_POINTS header scan loop and the Cartesian/Direct explicit
k-point read loop out of K_Vectors::parse_kfile into this-free
KListIO::find_kpoints_header / read_kpt_list. parse_kfile keeps the
renew() sizing calls as a thin member shell. Cyclomatic complexity of
parse_kfile drops 26 -> 20; klist.cpp 802 -> 775 lines.
Move the nkstot == 0 branch of parse_kfile (Gamma/MP keyword dispatch,
mesh size and offset reading, Monkhorst_Pack call) into a private
K_Vectors::read_mp_mesh member. parse_kfile now only records success
and keeps the nkstot > 0 explicit-list branch; its cyclomatic
complexity drops 20 -> 16.
…d_kpoints

Move the nkstot > 0 dispatch of parse_kfile into a private
read_listed_kpoints member, and merge the duplicated Line_Cartesian/
Line_Direct handling into setup_line_kpoints (symmetry check, line
interpolation, unit weights, coordinate-done flag). parse_kfile is now
a thin header/count reader plus two dispatch calls; its cyclomatic
complexity drops 16 -> 7, removing the -16 complexity penalty.
Pass ofs_warning explicitly into read_kpoints/parse_kfile/read_mp_mesh/
read_listed_kpoints and my_rank into read_kpoints (following the
mpi_k/generate_kfile pattern), removing 8 GlobalV references from the
k-point reading path. set() now reads MY_RANK/MY_POOL once into locals
and reuses them. Update the 30+ private-access call sites in
klist_test and print_info_test accordingly. Also wrap three >120-char
lines and reword a WARNING_QUIT message whose "nspin = 1" text tripped
the default-parameter heuristic.
Move the rank-0 -> all kstars broadcast loop out of K_Vectors::mpi_k
into a this-free KListIO helper. The wrappers compile to no-ops in
serial builds, so the helper is guarded only by __EXX at the call site
like the original block.
Move the inversion check and the time-reversal operation expansion
(Theta*g antiunitary coset for magnetic nspin=4, -g doubling
otherwise) out of K_Vectors::reduce_by_symmetry into the this-free
KListIO::append_time_reversal_ops. The physics comment moves with the
logic. reduce_by_symmetry cyclomatic complexity drops 13 -> 7 and the
file loses the Matrix3 'inv' local (brace-initialized in the helper),
removing a 9-parameter false positive; klist.cpp 793 -> 763 lines.
…ng from K_Vectors::set

Move the full k-point Cartesian-coordinate completion (kc/kd branches)
into KListIO::fill_full_kvec and the reciprocal/real lattice mismatch
handling (symmetry_autoclose retry vs WARNING_QUIT) into a private
K_Vectors::handle_symmetry_mismatch member. This drops the set()
cyclomatic complexity penalty; klist.cpp score rises 45 -> 50.
…ate_kfile to KListIO

- cal_ik_global: inlined as KListIO::build_ik2iktot free function (6 params),
  called directly from set(); the private member declaration is removed.
- set_kup_and_kdw: logic body (switch/case 1 and 2) moved to
  KListIO::expand_spin_kpoints free function; the public member shell keeps
  TITLE() plus the spin=2 OUT log lines so existing 5 call sites and tests
  remain source-compatible.
- generate_kfile: this-free body renamed KListIO::write_auto_kfile with a
  UnitCell fwd-decl in klist_io.h (header dep rule 3); private declaration
  is removed from klist.h.

klist.cpp shrinks 771 -> 673 lines; file_too_long deduction drops from
-12 to -8; score raises 50 -> 54.
bee21a3 changed the kvec_c_full loop in pack_kpts from `nkstot` to
`kvec_c_full.size()`, but the aux buffer is still allocated as
`nkstot_nospin * 3` (which equals `nkstot * 3` at the assignment point
in klist.cpp:291, before spin doubling).

For nspin=2 cases (spin_mult=2), kvec_c_full has been resized to
`nkstot * 2` by renew(), so the new loop writes 2x past the end of
kvec_c_full_aux, corrupting the heap and causing `free(): invalid
pointer` when ESolver_FP is destroyed.

Restore the original `nkstot` bound so the behavior matches the
pre-refactor code exactly. The aux buffer (allocated as
`nkstot_nospin * 3`) and bcast length (`nkstot_nospin * 3`) are
unchanged; only the loop upper bound is restored.

Verified: 12 failed 01_PW tests (003, 007, 028, 030, 034, 036, 037,
050, 055, 063, 078, 079, 097x2, 206, 815, scf_deltaspin2) all pass
again; valgrind reports no Invalid write from ABACUS code.
…arning through klist API

Three intertwined changes that must ship together to keep the build green:

1. Rename `nkstot_full` -> `nkstot_nospin` (member, getter, setter) across
   all consumers (source_cell, source_lcao/module_ri, module_bse, module_lr,
   module_rdmft, source_pw/module_pwdft, source_io). The old name was
   ambiguous: EXX/RI/LR code already treats it as "physical k-point count
   WITHOUT spin multiplicity" (see e.g. ri_2d_comm.hpp:
   `ik_full + is_k * nkstot_full`), so the new name makes the convention
   explicit. Comments in reciprocal_grid.h now document both:
   - nkstot: INCLUDING spin multiplicity (after set_kup_and_kdw)
   - nkstot_nospin: physical k-points, WITHOUT spin multiplicity

2. Thread `std::ostream& ofs_warning` through K_Vectors::set,
   setup_line_kpoints, set_both_kvec, update_use_ibz, reduce_by_symmetry,
   handle_symmetry_mismatch, and read_mp_mesh so they no longer read
   `GlobalV::ofs_warning` directly. Internal ModuleBase::WARNING calls
   are replaced with `ofs_warning <<` to keep behavior consistent.
   The klist module itself no longer references GlobalV::ofs_warning;
   outer callers (esolver_fp, esolver_gets, esolver_lr_lcao_tddft,
   deepks_test_prep) pass `GlobalV::ofs_warning` at the boundary.

3. Thread `int my_rank` through update_use_ibz, reduce_by_symmetry,
   handle_symmetry_mismatch to remove `GlobalV::MY_RANK` reads inside
   the klist module.

Also included: dftu_base static-member cleanup (mark_occ_mat_initialized
-> this->occ_mat_initialized = true) which was already in the working
tree and is consistent with the project rule "no static members in
dftu_base.h".

Behavior is unchanged. Build passes with `cmake --build build_max_para_test
-j 30`. All 12 previously-failing 01_PW tests (003, 007, 028, 030, 034,
036, 037, 050, 055, 063, 078, 079, 097x2, 206, 815, scf_deltaspin2)
pass with identical etot; valgrind reports no ABACUS-side Invalid write.
@mohanchen mohanchen changed the title Fix a bug in klist.h and klist.cpp Refactor klist, fix a few bugs Sep 3, 2026
@mohanchen mohanchen added the The Absolute Zero Reduce the "entropy" of the code to 0 label Sep 3, 2026
@mohanchen
mohanchen requested review from Flying-dragon-boxing and removed request for AsTonyshment September 3, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bugs Bugs that only solvable with sufficient knowledge of DFT Refactor Refactor ABACUS codes The Absolute Zero Reduce the "entropy" of the code to 0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant