Skip to content

Fix: preserve exact-origin spherical harmonics in GPU Gint (Useful Information to print out exactly the same numbers with both CPU and GPU) - #7901

Open
GenZeal-Lin wants to merge 1 commit into
deepmodeling:developfrom
GenZeal-Lin:fix/gint-exact-origin-sph-harm-pr
Open

Fix: preserve exact-origin spherical harmonics in GPU Gint (Useful Information to print out exactly the same numbers with both CPU and GPU)#7901
GenZeal-Lin wants to merge 1 commit into
deepmodeling:developfrom
GenZeal-Lin:fix/gint-exact-origin-sph-harm-pr

Conversation

@GenZeal-Lin

Copy link
Copy Markdown

Reminder

  • I have read AGENTS.md and docs/developers_guide/agent_governance.md.
  • I have linked an issue or explained why this PR does not need one.
  • I have added adequate unit tests and/or case tests, or explained why not.
  • I have listed the exact verification commands run and their results.
  • I have described user-visible behavior changes, including INPUT parameter changes.
  • I have explained core-module impact for ESolver, HSolver, ElecState, Hamilt, Operator, Psi, or other source/ changes.
  • I have requested any needed governance exception below.

Linked Issue

Fixes #7860

Related: #7856

Unit Tests and/or Case Tests for my changes

  • Commands run:
# Final CUDA configure/build in the clean verification worktree.
VERIFY="$HOME/abacus-work/abacus-gint-origin-verify-c1790ea39"
BUILD="$VERIFY/build-gpu"

cd "$VERIFY" || exit 1

cmake -S . -B "$BUILD" \
  -DENABLE_MPI=ON \
  -DENABLE_LCAO=ON \
  -DENABLE_ELPA=ON \
  -DENABLE_LIBXC=OFF \
  -DENABLE_DFTD4=OFF \
  -DUSE_CUDA=ON \
  -DUSE_CUDA_MPI=OFF \
  -DCMAKE_CUDA_COMPILER="$(which nvcc)"

CONFIG_RC=$?
echo "CONFIG_RC=$CONFIG_RC"

if [ "$CONFIG_RC" -ne 0 ]; then
    exit 40
fi

set -o pipefail

cmake --build "$BUILD" \
  --target abacus_basic_gpu \
  -j8 \
  2>&1 | tee "$HOME/abacus-work/gint-origin-final-build.log"

BUILD_RC=${PIPESTATUS[0]}
echo "BUILD_RC=$BUILD_RC"

if [ "$BUILD_RC" -ne 0 ]; then
    exit 41
fi

BIN="$BUILD/abacus_basic_gpu"
test -x "$BIN" || exit 42
echo "FINAL_BUILD_OK=1"
# Target regression setup and run.
VERIFY="$HOME/abacus-work/abacus-gint-origin-verify-c1790ea39"
BIN="$VERIFY/build-gpu/abacus_basic_gpu"
TEST="$VERIFY/tests/03_NAO_multik"

WRAP="/tmp/abacus_gint_origin_final_gpu.sh"
SMOKE="CASES_GINT_ORIGIN_FINAL_SMOKE.txt"
LOG="$HOME/abacus-work/gint-origin-final-two-case.log"

cat > "$WRAP" <<EOF
#!/usr/bin/env bash
exec timeout --signal=TERM --kill-after=15s 450 \
"$BIN" "\$@"
EOF
chmod +x "$WRAP"

cd "$TEST" || exit 1

printf '%s\n' \
  scf_out_hsr_spin4 \
  nscf_out_hsr_tr_rr \
  > "$SMOKE"

for c in \
  scf_out_hsr_spin4 \
  nscf_out_hsr_tr_rr
do
    if grep -qE \
      '^[[:space:]]*device[[:space:]]+' \
      "$c/INPUT"
    then
        sed -i -E \
          's/^[[:space:]]*device[[:space:]]+.*/device                  gpu/' \
          "$c/INPUT"
    else
        printf '\ndevice                  gpu\n' >> "$c/INPUT"
    fi
done

set -o pipefail

OMP_NUM_THREADS=1 \
CUDA_VISIBLE_DEVICES=0 \
bash ../integrate/Autotest.sh \
  -n 2 \
  -a "$WRAP" \
  -f "$SMOKE" \
  2>&1 | tee "$LOG"

TARGET_RC=${PIPESTATUS[0]}
echo "TARGET_AUTOTEST_RC=$TARGET_RC"

The temporary wrapper above only runs the validated CUDA binary under a
450-second timeout. GPU mode was forced separately by the temporary
device gpu edits shown above. The INPUT changes and smoke-list file were
removed after the test and are not part of this PR.

# Strict CSR checks exactly as used in the final target validation.
for c in \
  scf_out_hsr_spin4 \
  nscf_out_hsr_tr_rr
do
    REF="$c/hrs1_nao.csr.ref"
    GPU="$c/OUT.autotest/hrs1_nao.csr"

    cmp -s "$REF" "$GPU"
    CMP_RC=$?

    REF_NNZ=$(
      awk '
      $1==0 && $2==0 && $3==0 && NF==4 {
          print $4
          exit
      }' "$REF"
    )

    GPU_NNZ=$(
      awk '
      $1==0 && $2==0 && $3==0 && NF==4 {
          print $4
          exit
      }' "$GPU"
    )

    echo "REF_NNZ=$REF_NNZ"
    echo "GPU_NNZ=$GPU_NNZ"
    echo "CMP_RC=$CMP_RC"

    sha256sum "$REF" "$GPU"
done
# Existing NAO GPU guard suites, launched in parallel during final validation.
RC12_FILE="/tmp/gint_guard12.rc"
RC13_FILE="/tmp/gint_guard13.rc"
rm -f "$RC12_FILE" "$RC13_FILE"

(
    cd "$VERIFY/tests/12_NAO_Gamma_GPU" || exit 90
    set -o pipefail

    OMP_NUM_THREADS=1 \
    CUDA_VISIBLE_DEVICES=0 \
    bash ../integrate/Autotest.sh \
      -n 2 \
      -a "$WRAP" \
      -f CASES_GPU.txt \
      2>&1 | tee \
      "$HOME/abacus-work/gint-origin-final-12_NAO_Gamma_GPU.log"

    RC=${PIPESTATUS[0]}
    echo "$RC" > "$RC12_FILE"
    exit "$RC"
) &
PID12=$!

(
    cd "$VERIFY/tests/13_NAO_multik_GPU" || exit 91
    set -o pipefail

    OMP_NUM_THREADS=1 \
    CUDA_VISIBLE_DEVICES=1 \
    bash ../integrate/Autotest.sh \
      -n 2 \
      -a "$WRAP" \
      -f CASES_GPU.txt \
      2>&1 | tee \
      "$HOME/abacus-work/gint-origin-final-13_NAO_multik_GPU.log"

    RC=${PIPESTATUS[0]}
    echo "$RC" > "$RC13_FILE"
    exit "$RC"
) &
PID13=$!

wait "$PID12"
WAIT12=$?

wait "$PID13"
WAIT13=$?

RC12=$(cat "$RC12_FILE" 2>/dev/null || echo 99)
RC13=$(cat "$RC13_FILE" 2>/dev/null || echo 99)

echo "12_NAO_Gamma_GPU_RC=$RC12"
echo "13_NAO_multik_GPU_RC=$RC13"
echo "WAIT12_RC=$WAIT12"
echo "WAIT13_RC=$WAIT13"
# Final PR patch check.
git diff --check upstream/develop..HEAD
git diff --name-only upstream/develop..HEAD
  • Result summary:

    • Validated upstream base: c1790ea39532f9630b8917972131afdf1fb08dfa.
    • CUDA configure: PASS (CONFIG_RC=0).
    • CUDA build: PASS (BUILD_RC=0, FINAL_BUILD_OK=1).
    • Target two-case Autotest: PASS (TARGET_AUTOTEST_RC=0).
    • scf_out_hsr_spin4:
      • reference central H(R) NNZ: 42
      • GPU central H(R) NNZ before the fix: 50
      • GPU central H(R) NNZ after the fix: 42
      • hrs1_nao.csr is byte-identical to the reference (CMP_RC=0)
    • nscf_out_hsr_tr_rr:
      • reference central H(R) NNZ: 22
      • GPU central H(R) NNZ before the fix: 24
      • GPU central H(R) NNZ after the fix: 22
      • hrs1_nao.csr is byte-identical to the reference (CMP_RC=0)
    • 12_NAO_Gamma_GPU: PASS (RC=0).
    • 13_NAO_multik_GPU: PASS (RC=0).
    • Final git diff --check: PASS.
    • Final PR diff contains only:
      • source/source_base/kernels/cuda/sph_harm_gpu.cuh
      • source/source_hamilt/module_gint/kernel/phi_operator_kernel.cuh
      • tests/03_NAO_multik/CASES_GPU.txt
  • Checks not run, with reason:

    • The full repository-wide CPU/unit-test matrix was not re-run as part of
      the final submission gate. This change is confined to the CUDA
      spherical-harmonic/Gint path, so final validation focused on the two
      directly affected integration cases and both existing NAO GPU guard
      suites.
    • No standalone kernel unit test was added. The two existing integration
      cases reproduce the affected H(R) output end-to-end and are re-enabled
      here as strict regression coverage.
    • No reference outputs or numerical tolerances were changed.

What's changed?

This PR fixes the GPU-specific H(R) CSR discrepancy reported in #7860 by
aligning GPU Gint with the existing CPU Gint behavior at an exact
atom-grid coincidence.

For an exact displacement (0, 0, 0), CPU Gint evaluates the existing
spherical-harmonic recurrence directly. The general CUDA sph_harm
helper instead normalizes its input direction and falls back to the +z
direction for a near-zero norm. When GPU Gint passed an exact zero
displacement through that helper, the fallback produced different
spherical-harmonic values at this special point. The resulting H(R)
residuals could remain above the fixed sparse-output threshold, changing
the CSR sparsity pattern.

This fix leaves the general CUDA sph_harm semantics unchanged. Its
existing recurrence is factored into a direct helper that performs
neither normalization nor zero-vector fallback. GPU Gint uses that
direct recurrence only when the original atom-grid displacement is
exactly (0, 0, 0), matching the CPU Gint recurrence at that special
point. All nonzero displacements in this Gint path, and all other callers
of sph_harm, retain the pre-existing path.

With this fix, the two cases intentionally left disabled in #7856
because of #7860 are re-enabled in
tests/03_NAO_multik/CASES_GPU.txt:

  • scf_out_hsr_spin4
  • nscf_out_hsr_tr_rr

This restores strict regression coverage without relaxing the CSR
comparator, changing the sparse-output threshold, or updating reference
data.

Governance Notes

  • INPUT/docs changes:
    • None. No INPUT parameter, default, availability, parsing behavior, or
      documentation metadata is changed.
    • The temporary device gpu edits used for local validation were restored
      after testing and are not part of the PR.
    • The only test-list change is re-enabling two existing 03_NAO_multik
      cases in CASES_GPU.txt.
  • Core module impact:
    • source/source_base/kernels/cuda/sph_harm_gpu.cuh factors the existing
      spherical-harmonic recurrence into a direct helper while preserving the
      general GPU sph_harm normalization and near-zero fallback behavior.
    • source/source_hamilt/module_gint/kernel/phi_operator_kernel.cuh changes
      only the exact-origin GPU Gint path to use the direct recurrence.
      Nonzero displacements retain the previous behavior.
    • No ESolver, HSolver, ElecState, Psi, CPU Gint, CSR writer, sparse
      threshold, or reference-data behavior is modified.
  • Exceptions requested:
    • None.

@mohanchen mohanchen changed the title Fix: preserve exact-origin spherical harmonics in GPU Gint Fix: preserve exact-origin spherical harmonics in GPU Gint (Useful Information to print out exactly the same numbers with both CPU and GPU) Sep 3, 2026
@mohanchen mohanchen added GPU & DCU & HPC GPU and DCU and HPC related any issues Refactor Refactor ABACUS codes Bugs Bugs that only solvable with sufficient knowledge of DFT Useful Information Useful information for others to learn/study labels Sep 3, 2026
// Normalize the input direction vector
double r = sqrt(x_in * x_in + y_in * y_in + z_in * z_in);
double x, y, z;
if (r < 1e-10)

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.

On what basis is the cutoff set to 1 × 10⁻¹⁰

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 Refactor Refactor ABACUS codes Useful Information Useful information for others to learn/study

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[GPU][LCAO] H(R) CSR output keeps extra near-zero entries on GPU vs CPU

2 participants