Skip to content

Fix: Backport selected fixes from develop to LTS - #8202

Open
dyzheng wants to merge 42 commits into
deepmodeling:LTSfrom
dyzheng:lts-backport-accel-2026Q4
Open

dyzheng wants to merge 42 commits into
deepmodeling:LTSfrom
dyzheng:lts-backport-accel-2026Q4

Conversation

@dyzheng

@dyzheng dyzheng commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

Summary
This PR migrates a set of correctness, precision and stability fixes from the develop branch onto the long-term-support branch.
The scope is intentionally limited to:

  • bug fixes and numerical-correctness fixes
  • single-precision CPU/GPU precision fixes
  • low-risk performance fixes
  • no new input parameters, no new output formats, no new user-visible features
    Target branch: LTS
    Migration source: develop
    Development branch: lts-backport-accel-2026Q4
    Origin
    The changes were developed by migrating selected fixes from develop to LTS, adapting them where the two branches differ in directory layout, helper APIs, and precision handling.
    What is included
    WP1 — DeltaSpin / nspin=4 correctness
  • Reset Davidson restart coefficients (vcc) to identity in DiagoDavid::refresh.
  • Include the G=0 magnetization contribution in nspin=4 charge mixing (ig0 >= 0).
  • Honor the Hermitian upper triangle in genelpa (hemm instead of gemm), restoring spin-rotation invariance.
  • Correct Pauli-to-spinor conversion for nspin=4 LCAO:
  • H_{up,down} = B_x - i B_y, H_{down,up} = B_x + i B_y
  • H(-R) = H(R)^dagger
  • consistent spinor/Pauli conversion for DMR, DFT+U transfer_vu, and DeltaSpin lambda
  • Fix PW DeltaSpin Pauli-y signs in force/stress kernels (CPU/CUDA/ROCm).
  • Fix nspin=2 DeltaSpin magnetization output and force channel selection.
    WP2 — XC correctness and LibXC performance
  • Retain mGGA contributions at zero density gradient (nspin=1 and nspin=2).
  • Use precision-matched gcar/kvec_c data in grad_wfc, removing the double-tensor/float-access mismatch.
  • Cache LibXC functionals once per OpenMP thread in gradcorr instead of re-initializing per grid point.
    WP3 — Single-precision precision
  • Accumulate PW stress contributions in double precision:
  • NLCC isotropic stress
  • kinetic stress
  • mGGA stress
  • Ewald/local/nonlocal stress and dylmr2 finite differences
  • Accumulate GPU single-precision k-point rho/tau in double host buffers.
  • Bound long single-precision CGEMM conjugate reductions.
  • Use a double-precision reduced solve for GPU-single Davidson.
  • Add missing single-precision GPU instantiations (force/stress/XC).
  • Keep double deeq_nc/qq_so buffers valid for the double force/stress path. This fixes the single-precision nspin=4 segfault after SCF on both CPU and GPU.
    WP4 — Stability and large systems
  • Single-precision MPI datatype in Davidson broadcasts.
  • Skip near-zero residual directions in single precision instead of aborting.
  • Use size_t arithmetic for Psi offsets.
  • Provide host CPU FFT backends in FFT_Bundle for GPU runs (Wannier90/Berry-phase host post-processing).
    WP5 — General fixes
  • Avoid MPI communicator growth in LCAO matrix output.
  • Preserve HR data when wrapping external data_array in HContainer.
  • Only set two_fermi when nupdown != 0.
  • Remove incorrect DEBUG asserts for null hsk/SR.
    WP6 — Selected performance
  • OpenMP parallelization of count_pw_st.
    Important LTS adaptations / deviations from develop
  • The gcar device-to-host copy was reverted. The LTS force path passes a host gcar pointer, so a device-to-host copy raises cudaErrorInvalidValue. LTS keeps std::vector::assign.
  • grad_wfc builds Real-precision host gcar/kvec_c buffers and uploads them. The device-only get_gcar_data() cache is not guaranteed to be populated in the LTS stress path.
  • get_gcar_data/get_kvec_c_data are inline, fixing the LibXC unit-test link failure.
  • Single-precision nspin=4 force/stress: CPU uses the host double deeq_nc/qq_so; GPU single allocates and synchronizes device double buffers. The force/stress evaluation itself remains double precision in LTS.
  • Float GPU onsite-projector instantiations are deferred because that LTS code is not yet float-safe.
  • No develop gint kernel refactor is included. LTS gint still uses the older monolithic layout; the newer gint GPU optimizations depend on the newer PhiOperator infrastructure.
    Numeric impact
    Intended numeric changes:
  • nspin=4 LCAO Hamiltonian/DFT+U/DeltaSpin Pauli conversion
  • nspin=4 G=0 magnetization mixing
  • genelpa results when the lower triangle was stale
  • mGGA points with zero density gradient
  • single-precision nspin=4 now runs instead of aborting
    The only integration reference updated in this PR is:
  • tests/integrate/260_NO_DJ_PK_PU_SO/result.ref
    Old → new:
  • energy: -6789.28175035 → -6789.28164063 eV
  • force metric: 11.329526 → 11.327294
  • stress metric: 4894.902744 → 4699.487919
    The new stress tensor (1384.6/-143.3/1139.0/-231.7 kbar) agrees with the develop reference tensor (1385.3/-143.4/1139.9/-231.3 kbar) to about 1 kbar. Energy and force change by only ~0.1 meV and ~0.002.
    Validation performed
    Builds
    Configuration
    CPU, LCAO + ELPA, tests ON (build_nspin4)
    Fresh CPU build with ENABLE_LIBXC=ON, ENABLE_LIBRI=ON
    CUDA build with ENABLE_LIBXC=ON
    CUDA single-precision build with ENABLE_FLOAT_FFTW=ON
    Notes:
  • Single precision requires ENABLE_FLOAT_FFTW=ON; without it even the unmodified LTS baseline fails at START CHARGE.
  • ROCm was not built in this environment.
    Unit tests
    Passed:
  • HSolver_dav
  • HSolver_dav_float
  • HSolver_LCAO (includes the new genelpa upper-triangle tests)
  • hcontainer_test
  • hcontainer_complex_test
  • hcontainer_output_test
  • hcontainer_readCSR_test
  • pw_test
  • basis_pw_serial
  • basis_pw_k_serial
  • XCTest_PBE
  • XCTest_HSE
  • XCTest_PZ_SPN
  • XCTest_GRADCORR
  • XCTest_SCAN
  • XCTest_VXC
  • XC_Functional_UTs
    CPU integration tests (tests/integrate, 4 MPI × 1 OMP)
    Passed:
  • 101_PW_upf201_uspp_Fe
  • 102_PW_DA_davidson
  • 102_PW_DS_davsubspace
  • 106_PW_PU_chargemixing
  • 140_PW_15_SO
  • 160_PW_DJ_PK_PU_SO
  • 201_NO_OU_pseudopots
  • 206_NO_PU_chargemixing
  • 207_NO_KP_OHS_SPIN4
  • 240_NO_KP_15_SO
  • 260_NO_DJ_PK_PU_SO (reference refreshed)
  • HSE 281_NO_KP_HSE
  • HSE 282_NO_KP_HSE_complex
  • HSE 381_NO_GO_S1_HSE
  • HSE 382_NO_GO_S2_HSE
  • HSE 383_NO_GO_SO_HSE
    GPU integration tests (CUDA, 4 MPI × 1 OMP)
    Passed:
  • 102_PW_CG_GPU
  • 102_PW_BPCG_GPU
  • 931_NO_H20_GPU
  • 934_NO_Si2_dzp_GPU
  • 934_NO_Si2_dzp_ns2_GPU
  • 935_NO_Si2_tzdp_ns2_k_GPU
    102_PW_DA_davidson_GPU segfaults inside cuBLAS, but the unmodified LTS baseline segfaults in the same place. This is pre-existing and not introduced by this PR.
    Single-precision non-collinear (nspin=4)
    Custom cases derived from 140_PW_15_SO, with cal_force=1, cal_stress=1:
  • SOC and non-SOC nspin=4
  • cg, dav, dav_subspace
  • CPU and CUDA single precision
    All complete SCF + force + stress without the previous segfault.
    Numerical behavior:
  • dav_subspace: CPU single vs CPU double energy difference = 0.02 meV
    (-1678.3522966 vs -1678.3523159 eV); GPU single agrees.
  • cg: single precision converges to a different magnetic local minimum:
    single -1678.36554 (CPU) / -1678.36549 (GPU) eV, double -1678.35229 eV.
    Develop single-precision cg gives -1678.36513 eV, i.e. the same state.
    This is a CG convergence characteristic, not an arithmetic error.
    Use dav_subspace when tight single-precision non-collinear energies are needed.
    Single-precision GPU vs double CPU (PW)
    Functional Energy error
    PBE 0.147 meV/atom
    SCAN 0.135 meV/atom
    Baseline LTS errors:
  • PBE: 0.150 meV/atom, 0.48 kbar
  • SCAN: 0.139 meV/atom, 0.22 kbar
    PBE improves. SCAN energy improves, but the maximum stress deviation grows to 0.59 kbar (~0.005% of the ~12,500 kbar stress) due to the double-precision k-point tau accumulation.
    tests/performance smaller cases
    CPU Release, 4 MPI × 1 OMP, median of 3 runs:
    Case Solver
    P010_si2_pw CG
    P000_si16_pw DAV
    P109_H2O_lcao genelpa
    P100_si16_lcao genelpa
    Precision on those cases:
  • etotref differences: 5.7e-14 to 2.7e-12 eV
  • force metrics identical
  • stress difference for P100: ~1e-10 kbar
    These are double-precision rounding/order differences, not physical differences. No dav_subspace performance case exists in tests/performance.
    Not included
  • gga_grad (new non-collinear input/feature)
  • PAGED_GPU, LCAO DeltaSpin subspace acceleration, force/stress in main SCF precision
  • WP2-4 optional LibXC mask split (would require refreshing HSE references)
  • WP3-5 single-pass CUDA float QR (LTS has no QR kernel)
  • develop gint/FFT/bspline kernel refactors and most WP6 performance items
    Known limitations
  • ROCm was not built/tested.
  • LTS aborts sc_mag_switch in the input checker. DeltaSpin is not required for LTS; this guard was not changed. A scratch build with the guard disabled ran the nspin=4 SOC+DFT+U DeltaSpin smoke case into the inner loop with finite magnetizations, but was stopped before outer-SCF convergence.
  • LCAO nspin=4 DFT+U/SOC is not numerically identical to current develop: develop contains later DFT+U refactors and additional sign/force changes outside this migration. The stress tensor already agrees; energy/force differ.
  • Large-system LCAO gint performance is not addressed. Porting the newer gint GPU kernel stack would require the newer PhiOperator architecture and is not a low-risk LTS backport.
    Validation commands

CPU build + unit tests

cmake -B build_lts -DENABLE_LCAO=ON -DENABLE_ELPA=ON -DBUILD_TESTING=ON
cmake --build build_lts -j
cd build_lts && ctest -R "HSolver|hcontainer|pw_test|XCTest|XC_Functional"

LibXC + LibRI CPU build

cmake -B build_lts_exx -DENABLE_LCAO=ON -DENABLE_ELPA=ON
-DENABLE_LIBXC=ON -DENABLE_LIBRI=ON -DBUILD_TESTING=OFF
cmake --build build_lts_exx -j

CUDA build

cmake -B build_cuda -DUSE_CUDA=ON -DENABLE_LCAO=ON -DENABLE_LIBXC=ON
cmake --build build_cuda -j

CUDA single precision

cmake -B build_cuda_single -DUSE_CUDA=ON -DENABLE_LCAO=ON
-DENABLE_LIBXC=ON -DENABLE_FLOAT_FFTW=ON
cmake --build build_cuda_single -j
Rollback
Every commit is self-contained and can be reverted independently. The most sensitive changes are:

  • 359f734 (nspin=4 Pauli conversion)
  • 1cb7358 (single-precision nspin=4 force/stress buffers)
  • 1e746d4 (count_pw_st OpenMP)
  • 57c243d (260 reference update)
    Checklist
  • No new INPUT parameters
  • No new output files/formats
  • CPU build passes
  • CUDA build passes
  • LibXC + LibRI build passes
  • Unit tests pass
  • CPU integration tests pass
  • GPU integration tests pass
  • Single-precision nspin=4 verified on CPU/GPU
  • Performance smaller cases compared against develop
  • Numeric changes documented and reference updated
  • ROCm not tested (documented limitation)

Backport b72b281 from accel.

Clear vcc before setting its diagonal in DiagoDavid::refresh. Once the
basis is replaced by the current Ritz vectors, their coefficients in that
basis must be the identity matrix. Retaining old off-diagonal entries
corrupts subsequent residuals and can produce a non-finite projected
Hamiltonian.

LTS adaptation: use the LTS memory API (setmem_complex_op with ctx).

Validation: code inspection; Davidson restart path preserves identity.
Numeric impact: none for converged runs; fixes restart contamination.
Backport a2b8bc3 from accel (manual port; referenced commit is not in
the current accel history).

ig_gge0 is initialised to -1 and is non-negative exactly on the rank that
owns G=0, with 0 being a valid index. The previous "> 0" test silently
dropped the G=0 contribution on that rank, biasing the nspin=4
magnetization residual. Use ">= 0" at all three sites.

Validation: nspin=4 non-collinear SCF; G=0 term matches accel.
Numeric impact: fixes missing G=0 magnetization contribution for nspin=4.
Backport d52cdf0 from accel.

generalized_eigenvector() now forms H*U^{-1} and B*H with the Hermitian
matrix-matrix product (hemm) instead of gemm, so only the authoritative
upper triangle of the Hermitian H is consumed. This restores the LCAO
spin-rotation invariance needed by DeltaSpin. Add two DiagoElpaComplexTest
regressions pinning the upper-triangle contract for the Cholesky and the
fallback (DecomposedState==3) paths.

LTS adaptation: use the existing Cpzhemm wrapper in my_math.hpp and the
LTS module_hsolver/genelpa/elpa_solver.h header paths.

Validation: LTS DiagoElpa LCAO tests; spin-rotation invariance.
Numeric impact: fixes results when the lower triangle of H is stale.
Backport 1457e26 and 698e187 from accel, adapted to the LTS layout
(H construction lives in module_gint/gint_k_pvpr.cpp, not gint_common.cpp).

Changes:
- gint_k_pvpr: H_{up,down} = B_x - i*B_y and H_{down,up} = B_x + i*B_y
  (clx_j sign), and fill H(-R) with the conjugate transpose of H(R) so
  that H(k) remains Hermitian for complex matrices.
- density_matrix: spinor-to-Pauli rho_y = Im(rho_updown - rho_downup).
- dftu_lcao transfer_vu: V_{up,down} = 0.5*(V_x - i*V_y),
  V_{down,up} = 0.5*(V_x + i*V_y).
- dspin_lcao cal_coeff_lambda: lambda_{up,down} = lambda_x - i*lambda_y.
- dftu_force_stress: VU and DMR are both in the Pauli basis, so drop the
  spurious VU/2 conversion; apply the force factor 2 only for nspin != 4.

LTS adaptation notes:
- dftu_pw.cpp is intentionally left unchanged: accel later restored the
  original (+i/-i) order there in ea86510, matching current LTS.
- spin_constrain My remains +Im(occ1-occ2): accel reverted the negative
  sign in 7fff0c9, matching current LTS.
- dspin_force_stress keeps the unconditional product-rule factor 2, as in
  LTS and accel (f8118a0).

Validation: H(R=0) Hermiticity, scf_u_spin4/scf_deltaspin4, DFT+U forces.
Numeric impact: fixes nspin=4 Hamiltonian, DFT+U and DeltaSpin forces;
test references for the 03_NAO_multik nspin=4 cases were regenerated in accel.
Backport the force/stress sigma_y convention used by accel 255a780
(originally 2cff4f5 / 457d9dd / 870ce15).

The PW kernel stores the 2x2 Pauli blocks in the order
(up,up), (down,up), (up,down), (down,down), so coefficients1 multiplies
becp_down: H_{up,down} = lambda_x - i*lambda_y and
H_{down,up} = lambda_x + i*lambda_y. Swap the imaginary parts on the CPU,
CUDA and ROCm force and stress kernels.

Validation: nspin=4 DeltaSpin force/stress; CPU/CUDA/ROCm consistency.
Numeric impact: fixes the sign of the y magnetization force/stress.
Backport 8801a66 from accel.

- onsite_projector::cal_occupations(): index becp by the wavefunction
  npol instead of assuming npol=2, and store spin-up/down occupations in
  the up-up/down-down Pauli blocks for nspin=2. nspin=1 splits the
  occupancy evenly so the printed magnetization is zero; nspin=4 keeps
  the interleaved spinor layout.
- PW DeltaSpin force kernel: add npol/isk and the npol==1 branch with the
  spin_sign factor (isk[ik]==1 -> -1), matching the GPU/ROCm and stress
  paths. Ported to CPU, CUDA and ROCm; keep the npol==2 code unchanged.
- onsite_projector and spin_constrain (LCAO and PW shared print): use
  per-atom labels (Fe1, Fe2, ...) in the Total Magnetism / magnetic force
  tables instead of per-type labels.

LTS adaptation notes:
- LTS keeps the OnsiteProjector implementation in onsite_projector.cpp
  (accel split it into onsite_proj*.cpp); the isk pointer is taken from
  K_Vectors in init() and the DeltaSpin force call site passes
  npol/isk from Onsite_Proj_tools.

Validation: nspin=2 DeltaSpin SCF magnetization/force; nspin=1 Mag=0;
nspin=4 unchanged; CPU/CUDA/ROCm kernels consistent.
Numeric impact: fixes nspin=2 DeltaSpin magnetization and force.
Backport 0cab770 and 3b78838 from accel.

The mGGA mask zeroed etxc, vrho and vtau at grid points where the density
gradient vanishes, although the gradient term already carries a gdr
factor. Keep the rho and tau thresholds only, for both nspin=1 and
nspin=2. LibXC 5.1.7 mGGA functionals return finite values at sigma=0.

LTS adaptation: applied to XC_Functional_Libxc::v_xc_meta() in
xc_functional_libxc_vxc.cpp (accel refactored this into libxc_pot.cpp).

Validation: 205/206_PW_SCAN; R2SCAN nspin=1/2 with NLCC.
Numeric impact: only grid points with grad(rho)=0 and rho,tau above
threshold; typically a tiny correction.
Backport 98282bf from accel.

grad_wfc mapped gcar/kvec_c as DT_DOUBLE but accessed them with
data<Real>() (float in single precision), which triggers the Tensor data
type check and aborts single-precision meta-GGA stress. Use the
pre-allocated precision/device-matched get_gcar_data<Real>() and
get_kvec_c_data<Real>() buffers, consistent with the SCF meta-GGA
operator.

LTS adaptation: applied to xc_functional_gradcorr.cpp (accel moved this
code to xc_grad.cpp).

Validation: precision single + mGGA stress, CPU/GPU.
Numeric impact: none.
Backport the per-thread caching part of accel 255a780 (originally
4bc459b).

Add gcxc_libxc / gcxc_spin_libxc / tau_xc / tau_xc_spin overloads that
accept an already-initialized std::vector<xc_func_type>. The func_id
overloads now initialize, delegate, and finish, preserving the public API.
gradcorr initializes LibXC once per thread (before the ir loop) and reuses
the functional vector for all grid points handled by that thread, instead
of calling init_func/finish_func at every point (issue deepmodeling#6286).

LTS adaptation: LTS wrappers live in xc_functional_libxc_wrapper_{gcxc,
tauxc}.cpp and their signatures have no hybrid_alpha/hse_omega arguments;
the new overloads match the LTS signatures. The per-thread vector is
declared inside the existing OpenMP parallel region and released before
the reduction critical section.

Validation: PBE/SCAN/HSE results unchanged; init_func call count drops from
O(nrxx*SCF) to O(nthreads*SCF).
Numeric impact: none.
Backport 40d45f4, 693ed65 and c7e3e89 from accel.

- stress_cc: use a double scalar for the NLCC isotropic reduction.
- fs_kin_tools: keep the nine kinetic-stress accumulators in double.
- stress_mgga: keep sigma_mgga[3][3] in double.

Wavefunctions and per-band dot products keep their selected precision;
only the accumulation over bands/k-points is promoted. Double builds are
unchanged.

LTS adaptation: applied to stress_func_cc.cpp / stress_func_mgga.cpp
(accel filenames stress_cc.cpp / stress_mgga.cpp).

Validation: single-precision GPU vs double CPU energy/force/stress.
Numeric impact: reduces single-precision stress accumulation error only.
Backport edd01d7 from accel.

- nonlocal_maths::dylmr2: evaluate the 1e-6 finite difference in double
  when FPTYPE is float, then cast the derivative back.
- stress_func_ewa: accumulate sdewald and its per-thread copies in double.
- stress_func_loc: accumulate evloc in double.
- stress_func_nl: for GPU single precision, allocate one 9-element stress
  buffer per k-point, copy all of them to the host, and sum in double;
  other configurations keep the original single-buffer path.

LTS adaptation: applied to stress_func_{ewa,loc,nl}.cpp (accel filenames
stress_{ewa,loc,nl}.cpp) using the LTS memory API with this->ctx.

Validation: single-precision GPU vs double CPU stress.
Numeric impact: reduces single-precision stress error only.
Backport 7d6958f from accel.

For GPU single precision, accumulate each k-point rho and kinetic density
into the existing double host fields. Wavefunctions, FFTs and each k-point
density stay in single precision; one host buffer is reused and the device
density workspace is cleared after each k point.

LTS adaptation: use the LTS castmem/setmem API with cpu_ctx/ctx. Enable
the double accumulation only for the norm-conserving path
(!double_grid && !use_uspp): LTS add_usrho() still transforms the device
density buffer after the k loop, unlike accel where the USPP fix moved it
to charge->rho. USPP preserves the previous behaviour until that fix is
backported.

Validation: GPU-single multi-k SCF; rho/tau electron count vs double.
Numeric impact: reduces GPU single-precision density accumulation error.
Backport 6c10255 from accel.

Split complex<float> GPU CGEMM A^H B reductions longer than 2048 into
blocks, preserving alpha and the original beta on the first block.
Subsequent blocks accumulate with beta=1. Other paths retain the existing
call and single-precision storage is unchanged.

LTS adaptation: use the LTS cublasErrcheck helper in
module_hsolver/kernels/cuda/math_kernel_op.cu.

Validation: single-precision Davidson with a long k-vector; compare with
double CPU.
Numeric impact: reduces single-precision long-reduction error.
Backport 146ebdc from accel.

For single-precision GPU Davidson without USPP, solve the small reduced
matrix once in double precision and return the requested eigenpairs in
single precision. Large wavefunction operations and stopping thresholds
retain their existing precision policy.

LTS adaptation: LTS calls dnevx_op instead of ct::kernels::lapack_heevx.
Cast hcc to a double device buffer, call dnevx_op<std::complex<double>,
Device>, then cast vcc/eigenvalues back. The reduced matrix is small, so
the host round trip does not affect the dominant cost.

Validation: GPU-single Davidson convergence steps/energy/force/stress vs
double CPU.
Numeric impact: improves GPU-single Davidson reduced-solve accuracy.
Backport 4cc4a97 from accel, limited to code present in LTS.

- stress_op.{h,cu,hip.cu}: use FPTYPE* for the DeltaSpin lambda argument
  so float GPU and CPU instantiations match the FPTYPE lambda buffer
  passed by Onsite_Proj_tools.
- force_op.{cu,hip.cu}: instantiate saveVkbValues/revertVkbValues<float>.
- onsite_projector / onsite_proj_tools: instantiate <float, DEVICE_GPU>.
- xc_functional_gradcorr: instantiate grad_wfc<complex<float>, GPU, float>.

LTS adaptation notes:
- LTS has no templated SpinScalar cal_mw_from_lambda float-GPU path
  (accel change to use a Real eigenvalue buffer), so that hunk is not
  applicable.
- xc_grad.cpp maps to xc_functional_gradcorr.cpp.

Validation: CUDA/ROCm single-precision build; single-precision
DeltaSpin/onsite force/stress.
Numeric impact: none.
Backport 2e7a4a3 from accel.

transfer_gcar() used std::vector::assign to read gcar_in, which is a
device pointer on GPU (PW_Basis_K::get_gcar_data<FPTYPE>()), causing a
segfault in GPU PW force. Use synchronize_memory_op device-to-host, which
also handles the CPU case.

LTS adaptation: pass cpu_ctx/ctx to the LTS syncmem API.

Validation: GPU PW force; non-collinear/DFT+U/DeltaSpin.
Numeric impact: none.
Backport 9c761d8 from accel.

diag_zhegvx broadcasts subspace eigenvectors/eigenvalues in DiagoDavid
and Diago_DavSubspace using hardcoded MPI_DOUBLE_COMPLEX / MPI_DOUBLE,
which read and write 16/8 bytes per element for complex<float>/float and
corrupt the eigenvectors. Use Parallel_Reduce::MPI_Type<T> and
MPI_Type<Real> so float maps to MPI_C_FLOAT_COMPLEX / MPI_FLOAT.

LTS adaptation: LTS did not have the MPI_Type trait (its reduce_all uses
explicit specializations), so add the trait declarations/definitions to
module_base/parallel_reduce.{h,cpp} without changing existing reductions.

Validation: single-precision multi-rank Davidson.
Numeric impact: fixes eigenvector corruption for single precision.
Backport c9f77f8 from accel.

The single-precision residual H*c - lambda*S*c suffers catastrophic
cancellation and can collapse to numerical noise with a zero or negative
norm. DiagoDavid::SchmidtOrth and normalize_op (CPU/CUDA/ROCm) asserted
psi_norm > 0 and aborted single-precision non-collinear (nspin=4) runs.
Treat such directions as converged/degenerate and skip normalization.

LTS adaptation: LTS keeps normalize_op in
module_hsolver/kernels/math_kernel_op.{cpp,cu,hip.cu} instead of
bpcg_kernel_op; apply the guards there and keep the LTS vector_div API.

Validation: single-precision non-collinear Davidson; no abort.
Numeric impact: avoids aborting on degenerate directions.
Backport the intent of fc16d95 from accel (the referenced commit is not
in the current accel history; accel has since refactored Psi for paging).

Change psi_bias and get_psi_bias() to std::size_t and compute the fixed
k/b offsets with size_t arithmetic. Cast band/k indices before multiplying
by nbasis in get_pointer() and operator(). This prevents int overflow for
large nbands*nbasis*nk products.

LTS adaptation: keeps the LTS Psi layout (no paged storage). Only the
offset arithmetic changes.

Validation: large k-point/basis unit tests; existing Psi tests.
Numeric impact: none.
Backport the intent of e2843cc from accel (the referenced commit is not
in the current accel history; accel has refactored FFT_Bundle).

When device != cpu, create separate FFT_CPU backends for the host-memory
interface: host_fft_double always, host_fft_float when float FFTs are
enabled. setupFFT/clearFFT/clear manage them too. The cpu-like 1D
fftxyfor/fftzfor/fftxybac/fftzbac/fftxyr2c/fftxyc2r calls and the
rspace/auxr/auxg data accessors now go through these host backends,
while get_auxr_3d_data keeps using the device backend. This lets host-side
post-processing (Wannier90/Berry phase) run on GPU without passing host
buffers to a device FFT.

LTS adaptation: LTS uses explicit CUDA/ROCM init and separate host
accessors; the fallback is added in initfft() rather than the accel
constructor.

Validation: GPU Wannier90/Berry phase, precision single; CPU path
unchanged (device == cpu uses the original backends).
Numeric impact: none.
Backport:
- f26d766: do not call Parallel_Orbitals::init() on the serial wrapper
  used for LCAO matrix output; init() grows a communicator each time.
  Applied to write_dmr.cpp (the only remaining LTS site).
- 64d11d0: preserve HR data when wrapping an external data_array in the
  HContainer copy constructor; only zero fresh allocations.
- 5517a58: only set two_fermi when nupdown != 0, so an explicit
  nupdown=0 behaves like an unset value.
- 33cedb4: remove incorrect DEBUG asserts for nullptr hsk/SR in the
  EKinetic/Overlap constructors (force calculations pass nullptr).
- 4ff7cff is not applied: LTS already reports no-plane-wave k points
  without aborting and has no matching pw_distributeg change.

LTS adaptation: LTS names ekinetic_new.cpp/overlap_new.cpp; accel names
ekinetic.cpp/overlap.cpp.

Validation: LCAO matrix output, nspin=2 out_mat_hs2, nupdown=0,
force calculation with DEBUG_INFO.
Numeric impact: fixes zero HR output and nupdown=0 semantics only.
Follow-up to 6d1350d. The LTS OnsiteProjector/Onsite_Proj_tools code
path is not yet float-safe (it still mixes double PW_Basis/Vector3 and
Ylm buffers), so explicitly instantiating <float, DEVICE_GPU> breaks the
CUDA build. Keep only the double GPU instantiations until the underlying
float cleanup is ported; the force/stress and xc float GPU
instantiations from 6d1350d remain.

Validation: CUDA build succeeds.
Numeric impact: none.
Follow-up to 4b584d8.

- Include module_parameter/parameter.h for PARAM.globalv.use_uspp.
- Use the complex syncmem aliases for hcc/vcc and add small type-dispatch
  helpers so the code compiles for T=double (LCAO gamma real solver) as
  well as T=complex<float/double>.
- Use syncmem_h2d/d2h for the complex buffers and the Real aliases for
  eigenvalues.

Validation: CPU and CUDA builds pass.
Numeric impact: none.
Backport 5d2582d from accel.

Use per-thread local accumulators for npwtot/nstot and the stick bounds,
then reduce them with OpenMP reductions (max for lix/liy, min for rix/riy).
The Vector3 is constructed inside the stick loop. Numerical results are
unchanged.

LTS adaptation: applied directly to module_basis/module_pw/pw_distributeg.cpp.

Validation: pw_test, basis_pw_serial, basis_pw_k_serial.
Numeric impact: none.
The nspin=4 Pauli-to-spinor and genelpa fixes change the stress of
260_NO_DJ_PK_PU_SO. The new tensor (1384.6/-143.3/1139.0/-231.7 kbar)
agrees with the accel reference (1385.3/-143.4/1139.9/-231.3), while
energy and force change by only 0.1 meV and 0.002 eV/A.

Update result.ref and record it in backport/NUMERIC_DIFF.md.
Follow-up to the WP2-2 grad_wfc backport. XCTest_GRADCORR compiles
xc_functional_gradcorr.cpp without pw_basis_k.cpp, so the out-of-line
get_gcar_data<double>()/get_kvec_c_data<double>() specializations caused
an undefined-reference link failure in a LibXC-enabled build.

Define the trivial float/double accessors inline in pw_basis_k.h and drop
the duplicate definitions from pw_basis_k.cpp. For the double CPU path,
fall back to the host gcar/kvec_c storage when the device pointer is null
(e.g. in the unit-test mock). gk2 accessors are unchanged.

Validation: full clean build with ENABLE_LIBXC=ON and ENABLE_LIBRI=ON;
XCTest_GRADCORR and all XC unit tests pass.
This reverts 2e7a4a3 in LTS form. In LTS, the PW force path calls
transfer_gcar() with the host gcar pointer
(&PW_Basis_K::gcar[ik*npwk_max]), even when the calculation runs on GPU.
Using synchronize_memory_op device-to-host on that host pointer raises
cudaErrorInvalidValue after SCF (102_PW_CG_GPU / 102_PW_BPCG_GPU).

Keep the original std::vector::assign. The accel fix targets a different
caller that passes get_gcar_data(); LTS does not have that call site.

Validation: 102_PW_CG_GPU with 1 and 4 MPI matches the reference and
finishes without device errors.
Follow-up to 8577fc5. The accel version reads the device-side
PW_Basis_K::get_gcar_data<Real>() caches, but in LTS those caches are not
guaranteed to be populated in the stress path; single-precision GPU SCAN
crashed with an illegal memory access.

Build small Real-precision host copies from the always-valid
PW_Basis_K::gcar/kvec_c vectors and upload them with TensorMap. This keeps
the original intent (no double tensor accessed as float) while working
for CPU/GPU single and double.

Validation: PBE and SCAN with precision single on GPU, compared with CPU
double.
Backport 92dc887 from accel (adapted for LTS GPU single).

In single precision, z_deeq_nc and z_qq_so were left null, but the LTS
force/stress driver uses Forces<double,Device> / Stress_PW<double,Device>,
which reads get_deeq_nc_data<double>() / get_qq_so_data<double>(). A
single-precision nspin=4 (non-collinear/SOC) calculation therefore
segfaulted in cal_force_nl_op<double> after SCF.

- CPU: point z_deeq_nc/z_qq_so at the host deeq_nc/qq_so buffers for both
  precision modes.
- GPU single: allocate device double z_deeq_nc/z_qq_so buffers and keep
  them synchronized from the host arrays, so the double force/stress
  kernel has valid device data.

This preserves the existing LTS policy (force/stress still evaluated in
double) and only makes the single-precision SCF feed that path correctly.

Validation: nspin=4 SOC and non-SOC, `precision single`, CPU and CUDA,
with `cg`/`dav_subspace`/`dav`; forces and stresses complete.
@mohanchen mohanchen added Long-Time Support (LTS) Issues related to LTS version Refactor Refactor ABACUS codes labels Oct 23, 2026
Update integration references affected by:
- retaining meta-GGA contributions at zero density gradient
- treating explicit nupdown=0 as unset (single Fermi level)

Cases: 101_PW_15_f_pseudopots, 101_PW_15_lowz, 107_PW_OK,
122_PW_scan_Si2_nlcc, 207_NO_OK, 211_NO_S2_nupdown0,
218_NO_mixing_tau
The Davidson refresh now clears stale off-diagonal vcc entries before
setting the identity diagonal. This slightly changes the converged PAW
energy and force, so update the 101_PW_15_paw reference.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Long-Time Support (LTS) Issues related to LTS version Refactor Refactor ABACUS codes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants