Skip to content

DFT+U refactor, step 6 - #7879

Merged
mohanchen merged 44 commits into
deepmodeling:developfrom
mohanchen:2026-08-29
Sep 2, 2026
Merged

DFT+U refactor, step 6#7879
mohanchen merged 44 commits into
deepmodeling:developfrom
mohanchen:2026-08-29

Conversation

@mohanchen

@mohanchen mohanchen commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

DFT+U Slimming: PW occ-path free-function refactor + LCAO occ signature collapse

Overview

This PR continues the DFT+U refactoring by moving the remaining
occupation-matrix member functions of Plus_U_Base/Plus_U out into
namespace free functions, and by collapsing the over-long
cal_occ_mat_k/gamma parameter lists. It further shrinks the class
interface and reduces header coupling.

It consists of four independently compilable commits, each verified by a
full build before committing.

Per-commit breakdown

1. c90489cf2 — slim dftu_lcao.h and turn pot_uterm_HR into free functions

Header slimming (forward declarations)

  • dftu_lcao.h: drop 7 transitive
    includes (klist/unitcell/parallel_orbitals/orb_read/hamilt/hcontainer/ density_matrix), replaced with forward declarations for UnitCell,
    Parallel_Orbitals, LCAO_Orbitals, hamilt::HContainer<T> and
    elecstate::DensityMatrix<TK,TR>.
  • Add direct includes where the removed transitives were actually used:
    dftu_lcao.cpp, dftu_lcao_pots.cpp, dftu_lcao_energy.cpp,
    force_stress_lcao.h, write_hs_r.h, spar_u.cpp.

Free-function refactor

  • Move Plus_U::cal_eff_pot_mat_R_double / cal_eff_pot_mat_R_complex_double
    out of the class into namespace DFTU_LCAO, renamed to pot_uterm_HR_real /
    pot_uterm_HR_complex to match the existing pot_uterm_real/complex family.
  • spar_u.cpp now calls DFTU_LCAO::pot_uterm_HR_real/complex(dftu, ...).

Note: this commit merges the originally planned step 1 (free functions) and
step 2 (header slimming). The two are tightly coupled in dftu_lcao.h
(removing the member declarations + forward-declaration change in the same
file, and spar_u.cpp relies on the header's Parallel_Orbitals forward
declaration), so splitting them would have produced a non-compiling
intermediate state. Merging keeps the history clean and buildable.

2. 8f2d19a83 — sink occupation mixing to source_estate and drop forwarding methods

  • Add free function elecstate::mix_occ_with_save() in
    occ_matrix.h /
    occ_matrix.cpp:
    occ = beta*occ + (1-beta)*occ_save applied to each atom's correlated
    orbital, nspin-aware (nspin=4 mixes the single Pauli block, nspin=1/2 mixes
    both spin channels).
  • LCAO cal_occ_mat_k/gamma: replace the two duplicated hand-written mixing
    loops with a single mix_occ_with_save() call (~60 lines removed).
  • Plus_U_Base: delete the pure-forwarding methods copy_occ_mat /
    zero_occ_mat / set_occ_mat and inline their call sites
    (init_base, cal_occ_pw) to call occmat_.copy_to_save / write_save_to_flat / zero / read_from_flat directly.

3. 816f3020d — turn Plus_U_Base PW occ path into DFTU_BASE free functions

Move the remaining PW occupation-matrix members out of Plus_U_Base into
namespace DFTU_BASE (declared in
dftu_base_tools.h, defined in
dftu_base_occ.cpp):

  • reduce_occ_mat(cell, nspin, kpar, orbital_corr, occmat): the k-pool reduce
    still goes through Parallel_Reduce::reduce_double_allpool (MPI wrapper
    unchanged).
  • compute_eff_pot_and_energycompute_pot_uterm_and_energy(...), renamed to
    match the pot_uterm_* family; writes energy_u by reference.
  • accumulate_occ_one_k<Device>: templated free function, with explicit
    instantiations for DEVICE_CPU (and DEVICE_GPU) moved to the .cpp.
  • sync_occ_to_uom removed; cal_occ_pw now calls occmat_.write_to_flat
    directly.

Plus_U_Base::cal_occ_pw remains as the thin orchestration wrapper.

4. 8045f3691 — collapse cal_occ_mat_k/gamma signatures onto Plus_U&

  • cal_occ_mat_k: 13 params → 9 (add Plus_U& dftu, drop
    nspin/npol/nlocal/ks_solver/iatlnmipol2iwt/orbital_corr/occ_mat/ occ_mat_save/occ_mat_initialized).
  • cal_occ_mat_gamma: 13 params → 7 (add Plus_U& dftu).
  • Both now read all occupation-matrix state from dftu.occmat()
    (mat/mat_save/data/data_save/iatlnmipol2iwt/nspin/npol) and the
    Plus_U_Base accessors (get_orbital_corr_vec / is_occ_mat_initialized / mark_occ_mat_initialized), and reuse occmat().copy_to_save()/zero()
    instead of the duplicated hand-written copy/zero loops.
  • The intermediate cal_occ_mat template forwarders shrink to a single direct
    call.

Note: the MPI_Allreduce(MPI_COMM_WORLD) inside the accumulation loop is
intentionally left unchanged — it has no exact Parallel_Reduce counterpart
and was kept per the "only replace what maps cleanly" rule.

Verification

Each commit was verified before committing with:

This PR reorganizes the DFT+U force and stress implementation in the LCAO module, with two main goals: splitting the monolithic force/stress file into focused, well-documented units, and renaming all LCAO-specific files to explicitly indicate the NAO (numerical atomic orbital) basis set.

First, the original dftu_fs.cpp, which contained the entire force and stress calculation in a single file, is split into three new files. dftu_nao_fs_r.h/cpp provides the unified entry point cal_fs_nao_r(), handling OpenMP parallelization over atom pairs and the final MPI reduction. dftu_nao_for_r.h/cpp contains the single atom-pair force kernel cal_for_IJR_nao_r(). dftu_nao_str_r.h/cpp contains the single atom-pair stress kernel cal_str_IJR_nao_r(). Each file header includes the full mathematical formulas for the Hubbard correction energy, forces, and stress tensor, and explicitly documents that the implementation operates in real space using the real-space density matrix DMR. Because DMR already contains the Brillouin-zone integration, this implementation is unified for both gamma-only and multi-k-point calculations, eliminating the need for separate gamma and k-space branches.

abacus_fixer added 2 commits August 29, 2026 21:40
- Remove dead members with no callers: get_u_target,
  get_orbital_corr_data, deprecated get_pot_uterm_pw(iat),
  element-wise set_occ_mat, and mix_occ_mat (occupation mixing now
  goes through Charge_Mixing::mix_uom + sync_occ_to_uom)
- Merge duplicate occ_mat_initialized accessors: keep the is_*/mark_*
  pair and update dftu_lcao_occ.cpp call sites accordingly
- Replace unitcell.h/charge_mixing.h includes with forward
  declarations in dftu_base.h; add explicit includes to units that
  relied on them transitively (dftu_base.cpp, dftu_cal_occ_pw.cpp,
  dftu_output.cpp) and parallel_comm.h to stodft/hsolver units that
  used BP_WORLD/POOL_WORLD/INT_BGROUP indirectly

Verified: full incremental build of all targets passes with no
errors; repo-wide grep confirms no remaining references to the
removed symbols.
test_hsolver_sdft.cpp and diago_bpcg_test.cpp use BP_WORLD without
including source_base/parallel_comm.h, which broke the MPI build.
@mohanchen mohanchen added DFT+U Issues related to DFT plus U function Refactor Refactor ABACUS codes labels Aug 29, 2026
abacus_fixer and others added 20 commits August 29, 2026 22:39
- Rename dftu_output.{h,cpp} -> dftu_base_io.{h,cpp},
  dftu_tools_pw.{h,cpp} -> dftu_base_tools.{h,cpp},
  dftu_cal_occ_pw.cpp -> dftu_base_occ.cpp (git mv)
- Unify all DFTU-related free functions in module_pwdft under a single
  namespace DFTU_BASE (formerly dftu_io, dftu_pw, and pw::iter_init_dftu_pw)
- Extract read_occup_m and local_occup_bcast from Plus_U_Base as
  DFTU_BASE:: free functions declared in dftu_base_io.h; dependencies
  (occ_mat, orbital_corr, occ_mat_ctrl) are now passed explicitly
- Replace element-wise MPI_Bcast in local_occup_bcast with whole-matrix
  Parallel_Common::bcast_double calls (rule: no direct MPI usage)
- dftu_lcao_test links one extra light source (dftu_base_io.cpp) so that
  the heavy PW implementation dftu_base_occ.cpp stays out of the test
  closure
- Move JacobiRotate/CalculateEigenvalues helpers into an anonymous
  namespace and drop an unused local variable during migration

Verification: full incremental build passes in build/ (abacus_basic_para,
100%); in build_max_para_test/ the targets dftu_lcao_test and
dftu_pw_test build and link successfully (100%); grep confirms no
references remain to dftu_io, dftu_pw::, dftu_output, dftu_tools_pw,
dftu_cal_occ_pw, or the removed Plus_U_Base members.
Introduce YukawaScreening to own the Yukawa screening length, Slater
integrals Fk and derived U_Yukawa/J_Yukawa values. The implementation is
moved verbatim from DFTU_LCAO free functions; no call sites are changed
yet, so dftu_yukawa.* stays in place and behavior is unchanged.

Verification: make -j 30 dftu (build_max_para_test) passed.
Move yukawa_screening.* from module_dftu to module_pwdft so the base
layer can own it without depending on LCAO. Plus_U_Base now constructs a
YukawaScreening when use_yukawa_ is set and exposes it via yukawa(); the
legacy lambda/Fk/U_Yukawa/J_Yukawa members are kept in place until call
sites migrate. The cal_slater_Fk orb dependency stays guarded by __LCAO.

Verification: make -j 30 module_pwdft (build_max_para_test) passed.
Replace the DFTU_LCAO free Yukawa functions with the YukawaScreening
member owned by Plus_U_Base. setup_dftu_lcao now drives
yukawa().cal_slater_UJ and writes U-J back to u_current; the pots,
energy and IO paths read U/J through yukawa(). The yukawa_lambda config
is threaded through init_base into YukawaScreening::init, and
dftu_yukawa.h/.cpp are removed.

Verification: make -j 30 (build_max_para_test) passed;
ctest -R dftu: 4/4 passed (dftu_pw_test, dftu_core_test,
dftu_operator_test, dftu_lcao_test).
All Yukawa state (lambda, Slater Fk integrals, derived U/J) now lives
exclusively in YukawaScreening, owned by Plus_U_Base via yukawa_.
use_yukawa() is derived from the pointer instead of a separate flag,
and the redundant yukawa_lambda member/accessor on Plus_U is removed.

Verified: make -j 30 in build_max_para_test passes;
OMP_NUM_THREADS=1 ctest -R dftu passes (4/4).
New class owning the nested occ[iat][l][n][spin] matrices, their saved
copy for mixing, and the iat->(l,n,m,ipol)->iwt lookup table. Provides
element/matrix access, flat (de)serialization and bulk zero/copy/flat
operations covering everything the legacy Plus_U_Base code paths need.
Not yet referenced by existing code; call sites switch in later steps.

Verified: make -j 30 in build_max_para_test passes.
occmat_ is allocated alongside the legacy occ_mat/occ_mat_save/
iatlnmipol2iwt arrays in init_base, and exposed via occmat(). Existing
read/write paths are unchanged; writers switch to occmat_ next.

Verified: make -j 30 in build_max_para_test passes;
OMP_NUM_THREADS=1 ctest -R dftu passes (4/4).
Switch all internal writers and public read accessors of Plus_U_Base from
the legacy occ_mat/occ_mat_save/iatlnmipol2iwt members to the new
occmat_ (OccupationMatrix) object:

- init_base: read_occup_m/local_occup_bcast now fill occmat_.data()
- copy_occ_mat -> occmat_.copy_to_save + write_save_to_flat
- zero_occ_mat  -> occmat_.zero
- set_occ_mat   -> occmat_.read_from_flat
- get/set_occ_mat_flat -> occmat_.get_flat/set_flat
- dftu_base_occ.cpp: reduce_occ_mat, sync_occ_to_uom,
  compute_eff_pot_and_energy, accumulate_occ_one_k use occmat_.mat()
- header accessors get_occ_mat/get_occ_mat_save/get_occ_mat_data/
  get_occ_mat_save_data/get_iatlnmipol2iwt forward to occmat_
- dftu_lcao_test builds and reads the matrices through occmat()

The legacy members are still allocated but now dead; they are removed in
the follow-up commit. Behavior is unchanged: make -j 30 and
OMP_NUM_THREADS=1 ctest -R dftu (4/4) pass.
All readers and writers now go through occmat_ (OccupationMatrix), so the
legacy Plus_U_Base members occ_mat, occ_mat_save and iatlnmipol2iwt are
deleted together with their allocation block in init_base (the
pot_uterm_pw_index / num_locale bookkeeping is kept).

The IO free functions read_occup_m / local_occup_bcast now take an
OccupationMatrix& instead of the raw nested vector, and write into it
through the set()/mat() interface. write_occup_m / output already used
the public get_occ_mat() accessors and are unchanged.

Behavior is unchanged: make -j 30 and OMP_NUM_THREADS=1 ctest -R dftu
(4/4) pass.
Lesson from the DFT+U OccupationMatrix refactor (B2-B4): migrate a
base-class nested-vector member in three steps so no commit mixes
old-storage writes with new-storage reads.
OccupationMatrix depends only on UnitCell and ModuleBase::matrix, not on
any PW- or LCAO-specific layer. It is an electronic-state data container,
so source_estate is a more natural home than source_pw/module_pwdft.

Verified with make -j 30 in build_max_para_test and
OMP_NUM_THREADS=1 ctest -R dftu (4/4 passed).
Remove 8 accessors on Plus_U_Base that only forwarded to the
OccupationMatrix member; call sites now use dftu.occmat() directly:
  get_iatlnmipol2iwt, get_occ_mat, get_occ_mat_save, get_occ_mat_data,
  get_occ_mat_save_data, get_occ_mat_flat, set_occ_mat_flat
(get_occ_mat_flat/set_occ_mat_flat definitions in dftu_base.cpp removed too).

Kept occmat(), get_occ_mat_ctrl(), and the copy_occ_mat/zero_occ_mat/
set_occ_mat/sync_occ_to_uom wrappers, which still touch the uom_array /
uom_save / pot_uterm_pw_index base-class members.

Verified with make -j 30 in build_max_para_test and
OMP_NUM_THREADS=1 ctest -R dftu (4/4 passed).
cal_slater_Fk dereferences const LCAO_Orbitals*, but the complete type
(orb_read.h) is only included under #ifdef __LCAO while the function body
was not guarded. PW-only CI builds (without __LCAO) failed with
"invalid use of incomplete type 'const class LCAO_Orbitals'".

Guard the body with #ifdef __LCAO and WARNING_QUIT in the non-LCAO stub,
matching the existing codebase pattern (vnl_pw_alpha.cpp, esolver_factory).

Verified: make -j 30 (LCAO build) passed;
c++ -fsyntax-only -std=gnu++11 (no __LCAO) passed;
OMP_NUM_THREADS=1 ctest -R dftu 4/4 passed.
…tions

Header slimming (forward declarations):
- dftu_lcao.h: drop 7 transitive includes (klist/unitcell/parallel_orbitals/
  orb_read/hamilt/hcontainer/density_matrix), replace with forward
  declarations for UnitCell, Parallel_Orbitals, LCAO_Orbitals,
  hamilt::HContainer<T> and elecstate::DensityMatrix<TK,TR>.
- Add direct includes where the removed transitives were actually used:
  dftu_lcao.cpp, dftu_lcao_pots.cpp, dftu_lcao_energy.cpp,
  force_stress_lcao.h, write_hs_r.h, spar_u.cpp.

Free-function refactor:
- Move Plus_U::cal_eff_pot_mat_R_double / cal_eff_pot_mat_R_complex_double
  out of the class into namespace DFTU_LCAO, renamed to pot_uterm_HR_real /
  pot_uterm_HR_complex to match the existing pot_uterm_real/complex family.
- spar_u.cpp now calls DFTU_LCAO::pot_uterm_HR_real/complex(dftu, ...).

Verified: cmake --build build -j4 (exit 0). This build has no test targets.
…arding methods

- Add free function elecstate::mix_occ_with_save() in occ_matrix.{h,cpp}:
  occ = beta*occ + (1-beta)*occ_save over each atom's correlated orbital,
  nspin-aware (nspin=4 single Pauli block, nspin=1/2 both spin channels).
- LCAO cal_occ_mat_k/gamma: replace the two duplicated hand-written mixing
  loops with a single mix_occ_with_save() call (~60 lines removed).
- Plus_U_Base: delete the pure-forwarding methods copy_occ_mat /
  zero_occ_mat / set_occ_mat and inline their call sites
  (init_base in dftu_base.cpp, cal_occ_pw in dftu_base_occ.cpp) to call
  occmat_.copy_to_save / write_save_to_flat / zero / read_from_flat directly.

Verified: cmake --build build -j4 (exit 0). This build has no test targets.
…tions

Move the remaining PW occupation-matrix members out of Plus_U_Base into
namespace DFTU_BASE (declared in dftu_base_tools.h, defined in
dftu_base_occ.cpp):
- reduce_occ_mat(cell, nspin, kpar, orbital_corr, occmat): k-pool reduce
  still goes through Parallel_Reduce::reduce_double_allpool (unchanged MPI
  wrapper).
- compute_eff_pot_and_energy -> compute_pot_uterm_and_energy(...), renamed to
  match the pot_uterm_* family; takes u_current/orbital_corr/
  pot_uterm_pw_index/occmat/pot_uterm_pw and writes energy_u by reference.
- accumulate_occ_one_k<Device>: templated free function, explicit
  instantiations for DEVICE_CPU (and DEVICE_GPU) moved to the .cpp.
- sync_occ_to_uom removed; cal_occ_pw now calls occmat_.write_to_flat
  directly.

Plus_U_Base::cal_occ_pw remains as the thin orchestration wrapper.

Verified: cmake --build build -j4 (exit 0). This build has no test targets.
cal_occ_mat_k: 13 params -> 9 (add Plus_U& dftu, drop nspin/npol/nlocal/
ks_solver/iatlnmipol2iwt/orbital_corr/occ_mat/occ_mat_save/occ_mat_initialized).
cal_occ_mat_gamma: 13 params -> 7 (add Plus_U& dftu).

Both now read all occupation-matrix state from dftu.occmat()
(mat/mat_save/data/data_save/iatlnmipol2iwt/nspin/npol) and the Plus_U_Base
accessors (get_orbital_corr_vec / is_occ_mat_initialized /
mark_occ_mat_initialized), and use occmat().copy_to_save()/zero() instead of
the duplicated hand-written copy/zero loops. The intermediate cal_occ_mat
template forwarders shrink to a single direct call.

MPI_Allreduce(MPI_COMM_WORLD) inside the accumulation is intentionally left
unchanged: it has no exact Parallel_Reduce counterpart and was kept per the
"only replace what maps cleanly" rule.

Verified: cmake --build build -j4 (exit 0). This build has no test targets.
…ename for clarity

Rename and reorganize DFT+U force/stress implementation:

New files (r-space, NAO basis):
- dftu_nao_fs_r.h/cpp    : unified force+stress entry with full formula docs
- dftu_nao_for_r.h/cpp   : single-pair force core (cal_for_IJR_nao_r)
- dftu_nao_str_r.h/cpp   : single-pair stress core (cal_str_IJR_nao_r)

Renamed files:
- dftu_lcao_op.h/cpp          -> dftu_nao_op.h/cpp
- dftu_lcao_op_legacy.cpp     -> dftu_nao_op_legacy.cpp
- dftu_force.h/cpp (legacy k) -> dftu_nao_fs_k.h/cpp
- dftu_folding.h/cpp          -> dftu_nao_folding.h/cpp

Deleted:
- dftu_fs.cpp (split into dftu_nao_fs_r.cpp + dftu_nao_for_r.cpp + dftu_nao_str_r.cpp)

Updated:
- dftu_nao_op.h: expose getters and cal_pot_onsite/transfer_pot_onsite for free functions
- force_stress_lcao.cpp, hamilt_lcao.cpp, dftu_lcao_occ.cpp: update includes
- test/CMakeLists.txt, test/dftu_lcao_test.cpp: update source list and include

Naming convention:
- nao : NAO/LCAO basis set
- _r  : real-space implementation (uses DMR, k-point independent)
- _k  : k-space legacy implementation (deprecated, broken)
- for : force
- str : stress
- fs  : force+stress combined

Co-Authored-By: TRAE <noreply@trae.ai>
…s clarity

Rename all LCAO-specific DFT+U files in module_dftu to use 'nao' instead
of 'lcao', making the basis-set dependency explicit in the filename:

- dftu_lcao.h/cpp          -> dftu_nao.h/cpp
- dftu_lcao_occ.h/cpp      -> dftu_nao_occ.h/cpp
- dftu_lcao_pots.h/cpp     -> dftu_nao_pots.h/cpp
- dftu_lcao_energy.h/cpp   -> dftu_nao_energy.h/cpp
- dftu_lcao_op_legacy.h    -> dftu_nao_op_legacy.h

Update all #include references across 29 files in:
  source_lcao, source_esolver, source_estate, source_io, module_dftu

Update CMakeLists.txt source lists accordingly.

Note: setup_dftu_lcao.cpp/h keep their names (they are not in module_dftu
and represent the DFT+U setup workflow, not a basis-specific implementation).

Co-Authored-By: TRAE <noreply@trae.ai>
@mohanchen
mohanchen requested a review from lanshuyue August 30, 2026 14:50
@mohanchen mohanchen added the The Absolute Zero Reduce the "entropy" of the code to 0 label Aug 30, 2026
@lanshuyue

Copy link
Copy Markdown
Collaborator

There seems to be an existing out-of-bounds write for nspin == 1 in write_save_to_flat().

For nspin == 1, init_base() allocates only one occupation-matrix block in uom_save: pot_index is doubled only when nspin == 2. However, write_save_to_flat() handles nspin == 1 and nspin == 2 together and unconditionally writes both spin channels:

const int half_size = uom_save.size() / 2;

uom_save[index[iat] + mm] = ...[0]...;
uom_save[half_size + index[iat] + mm] = ...[1]...;

For example, for a single correlated d orbital, uom_save.size() == 25 when nspin == 1. The first matrix is written to [0, 24], while the second write goes to [12, 36], so part of the first block is overwritten and [25, 36] is out of bounds. Since operator[] is used, this results in undefined behavior rather than a guaranteed crash.

This does not appear to be newly introduced by this PR: the legacy Plus_U_Base::copy_occ_mat() contains the same logic. However, now that the flat serialization has been extracted into OccupationMatrix, I think it would be good to fix the existing inconsistency here.

In particular, write_to_flat() and read_from_flat() already treat the layout differently: they access the second flat block only for nspin == 2. Should write_save_to_flat() follow the same logic, i.e. always write spin 0 for nspin == 1/2, but write spin 1 only when nspin == 2?

abacus_fixer added 3 commits September 1, 2026 15:12
For nspin==1, uom_save is allocated as a single block (pot_index is
doubled only when nspin==2). However, write_save_to_flat previously
wrote both spin channels unconditionally, causing part of the first
block to be overwritten and indices past the vector end to be accessed
(undefined behavior via operator[]).

Fix by guarding the channel-[1] write with `if (nspin_ == 2)`,
consistent with write_to_flat() and read_from_flat() which already
handle the two channels correctly.
# Conflicts:
#	source/source_pw/module_pwdft/onsite_proj.cpp
#	tests/08_EXX/15_KP_HSE_SOC_symm/threshold
Upstream PR deepmodeling#7873 split onsite_proj.cpp and introduced
onsite_proj_force_stress.cpp, which still called the removed
get_orbital_corr_data() (returning const int*). Our refactor PR had
renamed it to get_orbital_corr_vec() (returning const std::vector<int>&).

Fix by appending .data() at both call sites, matching the new interface.
abacus_fixer added 16 commits September 1, 2026 15:43
The DFT+U LCAO files in module_dftu/ were renamed from dftu_lcao_*
to dftu_nao_* (step 6, commit 4fd1529), but source/Makefile.Objects
still referenced the old names. Additionally, dftu_fs.cpp was split
into four focused files (dftu_nao_fs_k/for_r/fs_r/str_r), so the old
single dftu_fs.o entry is no longer valid.

Fix both OBJS_LCAO and OBJS_DFTU sections to use the current source
filenames so the legacy Makefile build (used by the Intel CI) works.
Two related warnings were triggered:
- -Wvariadic-macro-arguments-omitted: memory.h called REQUIRES_OK
  with only one argument (no variadic arg)
- -Wgnu-zero-variadic-macro-arguments: macros.h used GNU extension
  '##__VA_ARGS__' to swallow the comma when __VA_ARGS__ was empty

Fix:
1. Add a message argument to the single-arg REQUIRES_OK call site
2. Drop the '##' GNU extension since all call sites now pass at
   least one variadic argument
The CREF and CREF3 macro definitions each had a trailing semicolon,
and the call sites also added one, resulting in double semicolons
(;;) inside the class that triggered -Wextra-semi warnings.

Remove the semicolons from the macro definitions and from the
destructor definition (~KernelXC() {};) to eliminate the warnings.

Verified: cmake --build . completes with no warnings from xc_kernel.h.
…k_div

Replace redundant explicit instantiation definitions (template class)
with explicit instantiation declarations (extern template class) in
op_pw_exx_pot.cpp. The static data member fock_div is defined and
instantiated in op_pw_exx.cpp; the duplicates in op_pw_exx_pot.cpp
could not instantiate it (out-of-class definition not visible) and
triggered -Wundefined-var-template when get_exx_potential accessed it.
ElecStatePW<T, Device> is a template derived class. Its psiToRho/cal_tau
signatures only match the base-class virtual for some (T, Device) combos,
which prevented simply adding 'override' to the template declaration.

Fix: enumerate all (T, Device) combos (CPU and GPU for complex<float>,
complex<double>, and double) as empty virtuals in the base class ElecState,
then mark the two derived-class template members as 'override'.

This resolves the -Winconsistent-missing-override warning from icpx/clang
without changing any runtime behavior.
Commit 8310580 removed dftu_yukawa.cpp/.h and dropped it from the module_dftu CMakeLists, but source/Makefile.Objects still listed dftu_yukawa.o in OBJS_DFTU. The subsequent rename fix (5f3af73) updated the other DFTU object names but left this orphan entry behind, so the Intel CI Makefile build failed with 'No rule to make target build/obj/dftu_yukawa.o, needed by build/bin/ABACUS.mpi'.

Verified: make -n -f ../Makefile abacus from source/build now exits 0 with no missing-target error; the link line lists the 11 valid DFTU objects and no dftu_yukawa.o. dftu_nao_op.o is already registered in OBJS_HAMILT_LCAO, so no replacement is needed.
In release builds with -DNDEBUG, assert(false) is compiled out, leaving
cal_LxijR and cal_LyijR with control paths that fall off the end without
returning a value. Convert the trailing `if (jm < -1)` to `else` and move
assert(jm < -1) inside as a defensive check, so the compiler can statically
prove all paths return. Also add explicit #include <cassert>.
…xtension

Replace the variable-length array 'double a[3*size]' in update_pos with a
std::vector<double>. The VLA relied on a runtime member 'size' and was a
non-standard Clang extension. The vector's .data() is passed to
unitcell::update_pos_tau, whose 'pos' parameter is 'const double*', so
the conversion is semantically equivalent.
Use the fully-qualified template name after '::~' to satisfy Intel
compiler's -Wdtor-name, matching the pattern already used in
Mix_DMk_2D<Tdata>::~Mix_DMk_2D<Tdata>(). C++11 compatible.
Move the out-of-line destructor definition and its explicit
instantiations into a 'namespace projectors {}' block so the name
after '~' is looked up in the same scope as the class name, satisfying
ISO C++ requirements.
C99 `_Complex` is not valid in C++, causing -Wc99-extensions warnings
when cblacs.h is included from C++ translation units (wrapped in
extern "C" blocks). Replace with `std::complex<double>`, which has
identical memory layout and is compatible with the BLACS C library.
Replace two variable-length arrays (dmR_tmp and tmp) sized by
dftu_op->get_nspin() with std::vector, eliminating the
-Wvla-cxx-extension warnings. Both arrays are now explicitly
zero-initialized with nullptr. Pass tmp.data() to cal_for_IJR_nao_r
and cal_str_IJR_nao_r to match their const BaseMatrix<double>**
parameter signature.
…name

Move Nonlocal and Overlap member function definitions inside `namespace hamilt`
blocks and drop redundant `hamilt::` qualifiers. This resolves the ISO C++
warning:
  warning: ISO C++ requires the name after '::~' to be found in the
           same scope as the name before '::~' [-Wdtor-name]

Affected files:
  - source/source_lcao/module_operator_lcao/nonlocal.cpp
  - source/source_lcao/module_operator_lcao/overlap.cpp
REQUIRES_OK(expr, ...) is a variadic macro defined in macros.h.
C++14 requires at least one argument be passed for the ... parameter;
omitting it is a C++20 extension that triggers -Wvariadic-macro-arguments-omitted
under icpx -pedantic.

Tensor::sync was the only call site in the whole codebase that omitted the
second argument. Add a descriptive error message to keep the macro usage
consistent with every other REQUIRES_OK call.
…onMatrix::get_flat

occ_matrix.cpp was missing from Makefile.Objects, so the Intel Makefile
build did not compile/link occ_matrix.o, causing an undefined reference
to OccupationMatrix::get_flat in dftu_nao_op.cpp. Add it to OBJS_ELECSTAT
to mirror source_estate/CMakeLists.txt, which already lists occ_matrix.cpp
in the elecstate object library.
…ng link

- Remove 4 duplicate objects (dftu_nao_fs_k/for_r/fs_r/str_r.o) from
  OBJS_HAMILT_LCAO; they are already listed in OBJS_DFTU and both
  lists merge into OBJS_ABACUS, causing "multiple definition" linker errors.
- Add missing yukawa_screening.o to OBJS_SRCPW so YukawaScreening::*
  symbols resolve; it is already present in CMakeLists.txt module_pwdft.

Verified with `make -n` (dry-run): dftu_nao_fs_k.o appears exactly once
in the final link line; yukawa_screening.o is compiled from
source_pw/module_pwdft/yukawa_screening.cpp.

@lanshuyue lanshuyue left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not a blocker, but I think it would be worth fixing while touching this code.

Before this refactor, init_base() always updated the Yukawa state explicitly:

use_yukawa_ = yukawa_potential;

Therefore, after every call to init_base(), the internal state was guaranteed to match the argument, including a possible true -> false re-initialization.

After this refactor, the state is inferred from the lifetime of the object:

bool use_yukawa() const { return yukawa_ != nullptr; }

which is a cleaner design in itself. However, init_base() currently only updates yukawa_ when yukawa_potential is true:

if (yukawa_potential)
{
    yukawa_.reset(new YukawaScreening());
    yukawa_->init(...);
}

When yukawa_potential == false, an existing yukawa_ is not cleared. Therefore the following sequence behaves differently from the old implementation:

init_base(..., true, ...);
init_base(..., false, ...);

Previously, use_yukawa() would become false; after this refactor it would remain true because the old YukawaScreening object is still alive.

I checked the current call path as well. For the PW solver, init_base() is indeed called repeatedly on the same Plus_U object through before_scf() -> setup_pot(). In the normal workflow, however, yukawa_potential comes from the same input configuration, so it appears to stay constant across these repeated calls. Therefore I don't currently see a normal production path that actually performs a true -> false transition, and this may not cause an observable problem today.

Still, this is a small behavioral regression relative to the previous implementation, and init_base() is not strictly a one-shot initialization function. Since the fix is essentially free, I think it would be safer to preserve the old semantics by clearing the pointer in the disabled case, e.g.

if (yukawa_potential)
{
    yukawa_ = std::make_unique<YukawaScreening>();
    yukawa_->init(...);
}
else
{
    yukawa_.reset();
}

or simply reset it before the conditional.

So this is more of a low-cost robustness fix than a confirmed runtime bug in the current workflow, but I think it is worth fixing opportunistically in this refactor.

abacus_fixer and others added 3 commits September 2, 2026 13:21
…awa_potential=false

Before the refactor, use_yukawa_ was assigned on every init_base() call,
so the state always matched the latest argument. Now the state is
inferred from yukawa_, but the pointer was only updated on the true
branch; a true -> false re-initialization left a stale object alive.
PW's before_scf() -> setup_pot() may call init_base() repeatedly on the
same Plus_U object, so clear the pointer in the disabled branch to
preserve the old semantics. Add unit tests for both switch directions
(reverse-verified: they fail with the stale-object behavior).
The test called a nonexistent Plus_U_Base::set_occ_mat_initialized(bool);
the actual API is mark_occ_mat_initialized(). Verified locally:
MODULE_DFPT_pw_data_test 6/6 passed.
@mohanchen
mohanchen merged commit bbd23ca into deepmodeling:develop Sep 2, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DFT+U Issues related to DFT plus U function 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.

2 participants