Skip to content

Fix GPU single-precision DAV convergence, stress accuracy and meta-GGA XC contributions - #8204

Open
dyzheng wants to merge 16 commits into
deepmodeling:developfrom
dyzheng:develop-single-precision-fixes
Open

dyzheng wants to merge 16 commits into
deepmodeling:developfrom
dyzheng:develop-single-precision-fixes

Conversation

@dyzheng

@dyzheng dyzheng commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fix GPU single-precision DAV restart failures, unstable basis orthogonalization, insufficient reduced-eigensolve accuracy, and rounding errors in density and stress accumulation. Also correct omitted meta-GGA XC contributions in the shared CPU/GPU path.

This PR contains 8 commits across 6 source files, with 101 insertions and 6 deletions. Wavefunctions and FFTs remain single precision; selected accumulations and the existing reduced eigensolve use double precision. No failure-triggered rerun is added, and scientific INPUT settings and convergence criteria are unchanged.

Overall results

Of the original 500 structures, 472 have converged CPU-double references. The 75 structures with anomalous GPU DAV results were retested; previously passing structures retain their original results.

Metric Before Cumulative after repair
GPU execution completed with SCF convergence 456/472 (96.61%) 472/472 (100%)
All four accuracy criteria met against the original CPU references 397/472 (84.11%) 465/472 (98.52%)
GPU errors / unconverged runs 3 / 13 0 / 0

68 of the 75 anomalous structures now meet all four accuracy criteria against the original CPU references. Four additional structures meet the criteria against corrected candidate CPU references after addressing the shared XC omission, bringing the cumulative count to 469/472 (99.36%). These four are reference corrections, not evidence of improved GPU accuracy alone.

The screening criteria used in this campaign are energy error ≤1 meV/atom, maximum per-atom force-vector error ≤0.05 eV/Å, maximum stress-component error ≤0.5 kbar, and absolute pressure error ≤0.5 kbar. These are campaign screening criteria, not universal physical-accuracy guarantees.

Changes and measured effects

The following are per-change validation results. Each timing change uses its stated comparator; the percentages are not additive and the intermediate results should not be interpreted as measurements of the final combined version.

Change Problem addressed and measured effect Performance impact
1. Double-precision NLCC stress accumulation POSCAR0224272: maximum stress error 0.420 → 0.017 kbar; pressure error 0.403 → 0.001 kbar. Runtime approximately unchanged; 23.1× CPU-double speedup.
2. Clear stale DAV restart coefficients POSCAR0448336: a first-SCF-step failure becomes convergence in 24 steps. Complete runtime 486.5 s; 19.2× CPU-double speedup. No timing improvement is claimed against the failed run.
3. Double-precision kinetic-stress accumulation POSCAR0159503: maximum stress error 1.312 → 0.140 kbar. Runtime approximately unchanged; 8.4× CPU-double speedup.
4. Block long single-precision conjugate inner products POSCAR1762323: energy error 3.014 → 0.0376 meV/atom. Runtime +9.7%; still 23.3× faster than CPU double.
5. Double-precision density accumulation across k-points POSCAR0317955: maximum stress error 0.564 → 0.108 kbar; pressure error 0.529 → 0.099 kbar. Single-precision FFTs are retained. Runtime approximately unchanged; 12.1× CPU-double speedup.
6. Single-pass block QR for DAV bases POSCAR0058587: an SCF-step-20 failure becomes a converged calculation, avoiding cancellation in the previous orthogonalization path. Runtime −9.6% versus the previously converged reorthogonalization implementation. POSCAR0030651: −28.2% versus the version containing fixes 1–5, with increases in some numerical errors.
7. Double-precision DAV reduced eigensolve POSCAR0056851: unconverged after 100 steps → converged in 68 steps. POSCAR0186922: 73 → 33 steps. The same reduced problem is solved once, then its eigenpairs are cast back to single precision. Versus the previously converged single-precision Jacobi implementation: POSCAR0186922 runtime −47.6%; POSCAR1729347 runtime +6.2%.
8. Retain meta-GGA XC contributions at zero gradient POSCAR0130197 and POSCAR0395353: restore approximately −0.267 eV and −0.371 eV of omitted CPU XC contributions, respectively. GPU runtimes approximately unchanged in both cases. This correction affects the shared CPU/GPU path.

Overall performance

CPU references use 11 MPI ranks × 1 OMP thread. GPU runs use one NVIDIA L20, 1 MPI rank × 1 OMP thread. CPU references generally use dav_subspace, while GPU runs use dav; the ratios describe these execution configurations, not a pure hardware or cost comparison.

  • Across the 75 GPU retests, mean GPU runtime is 423.9 s, versus 6724.9 s for the corresponding original CPU references: an aggregate CPU/GPU runtime ratio of approximately 15.9×. This set still includes structures outside the accuracy criteria.
  • For the same 59 structures whose old and new GPU runs both converged, mean GPU runtime decreases from 415.5 to 400.2 s, approximately 3.7% less time.
  • The 16 previously failed or unconverged runs are excluded from before/after performance comparisons.

Build, validation scope, and remaining issues

The final commit was clean-built on L20; the archived executable and recorded source files match the submitted source. The principal validated configuration is CUDA, GPU single-precision DAV, non-USPP, and one MPI rank. Block QR is restricted to that configuration. The restart reset, stress accumulator types, and XC correction also touch shared code; comprehensive regression coverage of other execution paths is not established by these records.

Residual accuracy issues remain for three structures: stress in POSCAR0955272 and POSCAR0131516, and forces in POSCAR1574549.

The original 397 passing structures were not rerun with the final executable, so the overall figures are cumulative validation results rather than a full final-version rerun. The four corrected CPU references remain candidate references without independent external numerical certification. The results demonstrate the combined effect of the fixes, not a full per-commit ablation study.

@dyzheng dyzheng added Bugs Bugs that only solvable with sufficient knowledge of DFT Performance Issues related to fail running ABACUS GPU & DCU & HPC GPU and DCU and HPC related any issues labels Oct 23, 2026
dyzheng and others added 16 commits October 23, 2026 11:53
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. Build
Real-precision host copies from the PW_Basis_K vectors and upload them,
consistent with the SCF meta-GGA operator. Also instantiate
grad_wfc<complex<float>, GPU, float>.

Validation: precision single + mGGA stress, CPU/GPU.
Use a double scalar for the NLCC diagonal stress reduction to avoid
single-precision accumulation error in the isotropic contribution.

Previously compiled and validated on POSCAR 0224272 against CPU double;
pressure error fell from approximately 0.403 to 0.00099 kbar, with
essentially unchanged energy, forces and observed runtime. Additional
11 MPI x 1 OMP checks on POSCAR 0743323 and 0251308 reduced stress and
pressure errors while preserving the original scientific inputs.

This commit records the tested one-line source change only.
Keep the nine kinetic-stress accumulators in double precision to avoid
rounding loss when summing contributions over bands and k-points.
Wavefunctions and per-band GPU dot products retain their chosen precision.

Validated POSCAR 0159503 with DAV, GPU 1 MPI x 1 OMP and unchanged INPUT,
using the converged CPU-double 11 MPI x 1 OMP result as the reference.
Maximum stress error decreases from 1.31174 to 0.14036 kbar and pressure
error from 0.37485 to 0.13039 kbar. The SCF energy/density history and final
energy are identical. Both runs converge in 24 steps; wall time is
522.92 versus 518.23 seconds, with no observed performance regression.

Rebuilt all three consumers of the changed header before runtime validation.
Accumulate per-k-point rho and kinetic density into the existing double
host fields for GPU single precision. Keep wavefunctions, FFTs and each
k-point density in single precision, reuse one host buffer and clear the
device density workspace after each k point.

Validated structure 0317955 with unchanged INPUT and solver stopping
conditions, GPU 1 MPI x 1 OMP, 18 converged SCF steps and all 100 k points
free of occupied zero states. Reuse the converged CPU-double reference.
Maximum stress error decreases from 0.56409 to 0.10767 kbar and pressure
error from 0.52878 to 0.09912 kbar. Energy and forces remain comparable.
Wall time is effectively unchanged (483.15 to 483.49 s), about 12.1x the
recorded CPU-double speed. Saved-density electron-count error improves
about 72x; rho/tau L2 differences against auxiliary GPU double improve
about 6.7x/20.9x.
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 their
existing call and single-precision storage is unchanged.

Validated with a complete 17-step SCF run of 1762323 on L20 using
1 MPI x 1 OMP, dav and unchanged INPUT/stopping criteria. No occupied
anomalous zero states were found. Reused the converged CPU-double
11 MPI reference.

Against CPU double, energy error falls from 3.0136 to 0.0376 meV/atom
and maximum stress error from 0.02996 to 0.01653 kbar. Force improves
slightly and pressure error stays approximately unchanged. Wall time
rises from 315.3 to 345.9 s (+9.7%), retaining 23.3x CPU-double speed.
Solve non-USPP GPU-float reduced matrices once in double precision and
return the requested eigenpairs in single precision. Large wavefunction
operations and stopping thresholds retain their existing precision policy.

Replace the float-prefix Jacobi policy while retaining the preceding six
validated repairs. The previous dav tip is retained on a protection branch.

Validated on L20 with GPU 1 MPI x 1 OMP, unchanged INPUT and reused CPU
double references (11 MPI x 1 OMP):
- 0056851: recover convergence in 68 SCF steps, 1140.5 s, ~14.0x CPU.
- 0186922: 73 -> 33 steps, 1080.2 -> 566.3 s, ~16.7x CPU.
- 1729347: 22 -> 21 steps, 262.9 -> 279.2 s, ~8.9x CPU.

CPU-relative energy errors are 0.04733, 0.07405 and 0.09601 meV/atom,
respectively. Force, stress and pressure were independently checked against
native CPU output. This is a local precision-policy correction; it does not
establish a defect in the vendor's single-precision algorithm.

Measured binary SHA256:
7aec892171fb7e401b572d17ae372c6922461c279cf54ac6fc1eb51ecc7211f1
Replace the initial and expansion Schmidt normalization paths with the
existing float Householder QR kernel for norm-conserving CUDA float
calculations with one rank per diagonalization pool.

A frozen 0058587 projection exposed a 3.53e-5 relative error in the
subtracted norm estimate versus 3.15e-8 in the direct float norm. Native
float replay and complete GPU 1-MPI tests validate the replacement without
an added orthogonalization pass or double reduced eigensolve.

0058587 converges in 39 SCF / 348.6 s, 9.6% less time than the prior converged
extra-orthogonalization version. CPU-relative energy/force/stress/pressure
errors are 0.05674 meV/atom, 0.004476 eV/A, 0.01737 kbar, 0.00894 kbar.
0030651 converges in 13 SCF / 295.8 s, 28.2% less than the retained-five-fix
baseline; energy/stress/pressure errors rise slightly to 0.07688 meV/atom,
0.04274 kbar, 0.01662 kbar. Neither failed nor incomplete runs are used
as timing denominators. INPUT, internal stopping and compiler flags stay
fixed. CPU 11-MPI references are reused; actual GPU tests use 1 MPI x 1 OMP.

CUDA preprocessor guards preserve the existing CPU/ROCm code paths. A
recompile of this guarded source produces the byte-identical executable
used in both completed tests. Incremental build, not a clean full build.
Multi-pool/ROCm runtime and 1729347 coverage are not established here.
The accel branch supports `precision single` on GPU, but several
explicit `<float, base_device::DEVICE_GPU>` instantiations were missing
and the DeltaSpin device kernels used `double*` for the lambda array,
so a GPU build failed to compile/link.

- stress_op.{cu,hip.cu}: use FPTYPE* for the DeltaSpin lambda argument
- cal_mw_from_lambda.cpp: diagonalize into a temporary Real buffer and
  copy back to the double-precision pelec->ekb (both GPU paths)
- onsite_proj.cpp / onsite_proj_tools.cpp: instantiate the float GPU
  OnsiteProjector / Onsite_Proj_tools
- force_op.{cu,hip.cu}: instantiate saveVkbValues<float> and
  revertVkbValues<float>
- xc_grad.cpp: instantiate grad_wfc<complex<float>, GPU, float>
The single-precision residual H*c - lambda*S*c suffers catastrophic
cancellation and collapses to numerical noise with a zero/negative norm.
DiagoDavid::SchmidtOrth and DavSubspace::normalize_op (CPU/CUDA/ROCm)
asserted psi_norm > 0.0, aborting single-precision non-collinear (nspin=4)
calculations. Treat such directions as converged and skip normalization
instead of aborting.
The subspace eigenvectors/eigenvalues broadcast in diag_zhegvx used
hardcoded MPI_DOUBLE_COMPLEX / MPI_DOUBLE, which for single-precision
(complex<float>/float) read and write past the buffer (16 vs 8 bytes per
element) and corrupt the eigenvectors. Use Parallel_Reduce::MPI_Type<T>
/ MPI_Type<Real> so float uses MPI_C_FLOAT_COMPLEX / MPI_FLOAT.
In single precision (has_float_data only), z_deeq_nc and z_qq_so were left as
nullptr, but the force/stress driver (Forces<double> / Stress_PW<double>)
always reads the double-precision deeq_nc via get_deeq_nc_data<double>().
Point them at deeq_nc.ptr / qq_so.ptr unconditionally (mirroring d_deeq and
d_qq_nt) so the double force/stress path does not segfault under precision
single.
The single-pass QR path was written against the older spsi_func
callable. Develop now passes an HSOperator, so use op.spsi() for the
batched basis update.
- Use FPTYPE* for the DeltaSpin stress lambda in the kernel declarations,
  matching the FPTYPE lambda buffers built by Onsite_Proj_tools.
- Do not instantiate float GPU OnsiteProjector/Onsite_Proj_tools: that
  path is still double-typed in develop and fails to compile.
The develop VNL initialization only allocated and filled the double
deeq_nc/qq_so buffers when has_double_data was set. Single-precision GPU
runs therefore passed null device pointers to the double force/stress
kernel. Allocate and synchronize those buffers unconditionally on GPU,
and keep the CPU path's host pointers valid as before.
@dyzheng
dyzheng force-pushed the develop-single-precision-fixes branch from 693b7b7 to de17ec2 Compare October 23, 2026 03:58

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

Bugs Bugs that only solvable with sufficient knowledge of DFT GPU & DCU & HPC GPU and DCU and HPC related any issues Performance Issues related to fail running ABACUS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants