Skip to content

Refactor DeltaSpin step 2: add Tests and refactor - #7883

Open
mohanchen wants to merge 11 commits into
deepmodeling:developfrom
mohanchen:2026-08-30-line1
Open

Refactor DeltaSpin step 2: add Tests and refactor#7883
mohanchen wants to merge 11 commits into
deepmodeling:developfrom
mohanchen:2026-08-30-line1

Conversation

@mohanchen

Copy link
Copy Markdown
Collaborator

Add four fast, reproducible DeltaSpin integration cases to cover the spin-constrained (deltaspin) code paths ahead of the planned SpinConstrain refactor. All cases compute force and stress in addition to total energy.

New cases:

  • tests/01_PW/scf_deltaspin2: PW basis, collinear (nspin=2), Z magnetization constraint, iterative lambda optimization to target. kpar=2, pw_seed=1.
  • tests/01_PW/scf_deltaspin4: PW basis, noncollinear (nspin=4), Z-only constraint (verifies no unphysical XY components). kpar=2, pw_seed=1.
  • tests/03_NAO_multik/scf_deltaspin2: LCAO basis, collinear (nspin=2), Z constraint, Gamma k-point. Single MPI process.
  • tests/03_NAO_multik/scf_deltaspin4: LCAO basis, noncollinear (nspin=4), Z-only constraint, 2x2x2 Monkhorst-Pack. Exercises the LCAO lambda loop.

Each case carries INPUT/KPT/STRU/result.ref/README. result.ref records etotref/etotperatomref plus totalforceref/totalstressref (sum-of-absolutes, matching tests/integrate/tools/catch_properties.sh). Cases were each run three times (OMP_NUM_THREADS=1) to confirm bit-identical etot before the reference was written.

Registration:

  • tests/01_PW/CASES_CPU.txt and CASES_GPU.txt: scf_deltaspin2/4 (PW runs on GPU)
  • tests/03_NAO_multik/CASES_CPU.txt: scf_deltaspin2/4

Note: an LCAO gamma_only=1 DeltaSpin variant was evaluated for tests/02_NAO_Gamma but is excluded because it hits a pre-existing latent bug (SpinConstrain::cal_mi_lcao is an empty stub, so print_Mi reads an empty Mi_ vector and segfaults); that bug is out of scope for this change and should be fixed separately. The LCAO coverage here uses multik instead.

Verification: abacus_max_para v3.11.0-beta8 (build_max_para_test, make -j 30); each of the four cases run 3x with bit-identical FINAL_ETOT; force/stress references extracted from the same runs.

…spin=2/4)

Add four fast, reproducible DeltaSpin integration cases to cover the
spin-constrained (deltaspin) code paths ahead of the planned SpinConstrain
refactor. All cases compute force and stress in addition to total energy.

New cases:
- tests/01_PW/scf_deltaspin2: PW basis, collinear (nspin=2), Z magnetization
  constraint, iterative lambda optimization to target. kpar=2, pw_seed=1.
- tests/01_PW/scf_deltaspin4: PW basis, noncollinear (nspin=4), Z-only
  constraint (verifies no unphysical XY components). kpar=2, pw_seed=1.
- tests/03_NAO_multik/scf_deltaspin2: LCAO basis, collinear (nspin=2),
  Z constraint, Gamma k-point. Single MPI process.
- tests/03_NAO_multik/scf_deltaspin4: LCAO basis, noncollinear (nspin=4),
  Z-only constraint, 2x2x2 Monkhorst-Pack. Exercises the LCAO lambda loop.

Each case carries INPUT/KPT/STRU/result.ref/README. result.ref records
etotref/etotperatomref plus totalforceref/totalstressref (sum-of-absolutes,
matching tests/integrate/tools/catch_properties.sh). Cases were each run
three times (OMP_NUM_THREADS=1) to confirm bit-identical etot before the
reference was written.

Registration:
- tests/01_PW/CASES_CPU.txt and CASES_GPU.txt: scf_deltaspin2/4 (PW runs on GPU)
- tests/03_NAO_multik/CASES_CPU.txt: scf_deltaspin2/4

Note: an LCAO gamma_only=1 DeltaSpin variant was evaluated for
tests/02_NAO_Gamma but is excluded because it hits a pre-existing latent bug
(SpinConstrain<double>::cal_mi_lcao is an empty stub, so print_Mi reads an
empty Mi_ vector and segfaults); that bug is out of scope for this change and
should be fixed separately. The LCAO coverage here uses multik instead.

Verification: abacus_max_para v3.11.0-beta8 (build_max_para_test, make -j 30);
each of the four cases run 3x with bit-identical FINAL_ETOT; force/stress
references extracted from the same runs.
@mohanchen
mohanchen requested review from hujieting and ieiue August 30, 2026 12:25
@mohanchen mohanchen added Refactor Refactor ABACUS codes The Absolute Zero Reduce the "entropy" of the code to 0 collinear/non-collinear/SOC/delta-spin Issues related to SOC Tests/Examples Issues/PR related to unit tests and integrate tests labels Aug 30, 2026
@mohanchen mohanchen changed the title Refactor and add Tests: add DeltaSpin integration tests with force/stress (PW + LCAO) Refactor DeltaSpin step 2: add Tests Aug 30, 2026
abacus_fixer added 5 commits August 30, 2026 21:01
… init

Split the SpinConstrain god class along functional boundaries as the
first steps of separating PW and LCAO code paths:

- New deltaspin_state.{h,cpp}: non-template ScState owns all
  basis-set-independent constraint data (lambda, Mi, target_mag,
  constrain, atom/orbital indexing maps, lambda-loop parameters) and
  the ~45 setter/getter implementations moved out of spin_constrain.cpp.
  SpinConstrain keeps a ScState value member and its public interface
  becomes thin forwarding shells, so all existing call sites
  (esolvers, operators, tests) are unchanged.
- New deltaspin_init.{h,cpp}: free function init_sc_state() performs
  the UnitCell/STRU-driven state initialization (count maps, nspin=2
  x/y constraint fix, Ry unit conversion) with no dependency on
  solver-side objects. SpinConstrain::init_sc() is now a shell that
  calls init_sc_state() and stores external pointers. init_sc.cpp is
  replaced by deltaspin_init.cpp in CMakeLists.
- Internal implementations (lambda_loop, cal_mw*, deltaspin_pw_impl)
  now access state through the state_ member. Scalars directly
  mutated by the lambda loop are transitional public fields on
  ScState, to be收敛ed to accessors when the loop is extracted.

No INPUT parameter behavior changes; docs update not required.

Verification:
- cmake --build build -j 16 (Release, ENABLE_LCAO=ON): success
- OMP_NUM_THREADS=1 ctest --test-dir build -R deltaspin: 5/5 passed
  (MODULE_LCAO_deltaspin_basic_func_test, spin_constrain_test,
  template_helpers, deltaspin_pw_test, deltaspin_core_test)
- python3 tools/03_code_analysis/agent_governance_check.py --staged:
  no findings
… functions

Move the LCAO-specific Mi computation out of the SpinConstrain class
into a new deltaspin_lcao_mi.{h,cpp} as free functions in namespace
spinconstrain::lcao:

- cal_mi_lcao(state, p_operator, dm, step, print): primary path via the
  DeltaSpin operator on the real-space density matrix (get_DMR_pointer
  + switch_dmr), with the operator and density matrix now passed
  explicitly instead of read from member pointers.
- convert_orbital_matrix / calculate_mw_from_orbitals: the
  orbital-multiplication-matrix alternative path, moved verbatim from
  cal_mw_helper.cpp.
- collect_mw: ScaLAPACK mu*density-matrix accumulation, with
  Parallel_Orbitals passed as an argument.

cal_mw.cpp is reduced to thin member shells (cal_mi_lcao forwarding +
set_operator specializations); the cal_mi_lcao<double> stub stays in
template_helpers.cpp to avoid a duplicate definition. The now-unused
convert/calculate_MW/collect_MW member declarations are removed from
spin_constrain.h (verified no external callers). cal_mw_helper.cpp is
deleted and replaced by deltaspin_lcao_mi.cpp in CMakeLists.

No INPUT parameter behavior changes; docs update not required.

Verification:
- cmake --build build -j 16 (Release, ENABLE_LCAO=ON): success
- OMP_NUM_THREADS=1 ctest --test-dir build -R deltaspin: 5/5 passed
- python3 tools/03_code_analysis/agent_governance_check.py --staged:
  no findings
Step 1 moved tpiba into ScState, but the __CUDA-only branch of
update_psi_charge_pw_gpu in deltaspin_pw_impl.cpp was missed, breaking
the CUDA CI build (no member named 'tpiba'). Route it through state_.

Verification:
- cmake --build build --target module_pwdft (CPU, __LCAO): success
- cmake --build build_pw_gpu --target module_pwdft (CUDA, nvcc 12.9): success
…Cache

Move the three ad-hoc public SpinConstrain members sub_h_save / sub_s_save /
becp_save (raw TK* pointers) plus the lambda_in_sub_ snapshot into a new
spinconstrain::pw::SubspaceCache (deltaspin_pw_cache.h), owned by value as
SpinConstrain::pw_cache_.

The class encapsulates the CPU vs GPU allocation/free difference:
- allocate_cpu()/release_cpu() use new[]/delete[] on the host;
- allocate_gpu()/release_gpu() use base_device resize/delete_memory_op on
  DEVICE_GPU, guarded by #if __CUDA/__ROCM.
It still exposes raw per-k pointers h_k()/s_k()/becp_k() because the hsolver
subspace routines and GPU memcpy ops require raw pointers, so std::vector is
not applicable for the device buffers. The buffer element type is fixed to
std::complex<double> (the PW path is always complex; the TK=double stub never
allocates the cache).

This also fixes a latent bug: the old SpinConstrain destructor called
delete[] on sub_h_save/sub_s_save/becp_save unconditionally. In GPU runs those
pointers are device memory allocated with resize_memory_op<DEVICE_GPU>, so
delete[] on them is undefined behavior (and on some setups an invalid free).
The destructor is now trivial (= default); device buffers are correctly freed
via release_gpu() -> delete_memory_op<DEVICE_GPU>() in
update_psi_charge_pw_gpu(), and host buffers via release_cpu() in
update_psi_charge_pw_cpu(). The singleton lives for the whole program so no
leak is introduced.

Call sites updated: cal_mw_from_lambda.cpp (CPU+GPU allocation points and
per-k views) and deltaspin_pw_impl.cpp (asserts, per-k views, CPU/GPU release,
lambda_in_sub_ access). Buffer layout and reuse semantics are unchanged.

No INPUT parameter behavior changes; docs update not required.

Verification:
- cmake --build build -j 16 (CPU, __LCAO): success
- cmake --build build_pw_gpu --target module_pwdft (CUDA, nvcc 12.9): success
  (covers the __CUDA allocation/free branch)
- OMP_NUM_THREADS=1 ctest --test-dir build -R deltaspin: 5/5 passed
- python3 tools/03_code_analysis/agent_governance_check.py --staged:
  no findings
…taspin

Move the PW-basis DeltaSpin implementation out of
source_pw/module_pwdft/deltaspin_pw_impl.cpp and into
module_deltaspin/deltaspin_pw_mi.{h,cpp} as spinconstrain::pw free functions.

This removes the reverse dependency source_pw -> module_deltaspin for the
implementation layer, keeps the PW path compilable when ENABLE_LCAO=off, and
eliminates the stale member declarations (cal_mi_pw, update_psi_charge_pw,
update_psi_charge_pw_cpu/gpu, calculate_delta_hcc) from SpinConstrain.

The deltaspin_pw_mi.cpp implementation is faithful to the original logic and
is verified by both CPU and CUDA builds plus the 5 deltaspin unit tests.
@mohanchen mohanchen changed the title Refactor DeltaSpin step 2: add Tests Refactor DeltaSpin step 2: add Tests and refactor Aug 31, 2026
abacus_fixer and others added 4 commits August 31, 2026 16:13
…n chaotic stress

The non-converged 100-step SCF trajectory has chaotic final-step stress
across MPI ranks (np>=3) due to ScaLAPACK pzhegvx global reduction
order-dependence, amplified exponentially by transverse spin-density zero
modes. This is a pre-existing property present before the SpinConstrain
refactor (3a96cd7 vs dae8f04 show identical np=4 OMP=1 results).

Add per-case threshold file (threshold 1.0, force_threshold 10.0,
stress_threshold 500.0, fatal_threshold 1000.0) and document the full
mechanism and measured data in README.
Two bugs caused incorrect ONSITE_PROJ force calculation:

1. GPU kernel type mismatch (force_op.cu, force_op.hip.cu):
   The cal_force_onsite kernel declared tpiba as int instead of
   FPTYPE (double). When tpiba = 2*pi/lat0 < 1.0 (typical for most
   lattices), int truncation made tpiba = 0, producing zero force
   on GPU. Fixed in both DFTU and DeltaSpin kernel variants, for
   CUDA and ROCm backends.

2. CPU duplicate for-loop (force_op.cpp):
   The npol==1 path of cal_force_nl_op for DeltaSpin had a duplicate
   'for (int ip = 0; ip < nproj; ip++)' line, causing the force to be
   summed nproj^2 times instead of nproj. With nproj=9 for Fe, the
   CPU ONSITE_PROJ force was 9x too large.

Additionally:
- Updated scf_deltaspin2 result.ref: totalforceref 23.414157 -> 22.696640
- Re-enabled scf_deltaspin2 and scf_deltaspin4 in CASES_GPU.txt

Verified: both tests pass on GPU (8/8 OK), CPU and GPU results match
to ~10 decimal places.

@ieiue ieiue 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.

The force fixes look correct, but the failing Intel compiler check should be addressed in a follow-up PR.

Move extern template declarations to op_pw_exx.h so all translation units
suppress implicit instantiation, and remove the duplicate explicit
instantiation in op_pw_exx_ace.cpp. The single explicit instantiation
in op_pw_exx.cpp remains the sole definition.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

collinear/non-collinear/SOC/delta-spin Issues related to SOC Refactor Refactor ABACUS codes Tests/Examples Issues/PR related to unit tests and integrate tests 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