Skip to content

gto/ecp: fix premature convergence in radial quadrature at large exponents#3250

Merged
sunqm merged 2 commits into
pyscf:masterfrom
susilehtola:fix-ecp-large-exponent-quadrature
Jun 13, 2026
Merged

gto/ecp: fix premature convergence in radial quadrature at large exponents#3250
sunqm merged 2 commits into
pyscf:masterfrom
susilehtola:fix-ecp-large-exponent-quadrature

Conversation

@susilehtola

Copy link
Copy Markdown
Contributor

Closes #3249.

Summary

  • The adaptive Gauss–Chebyshev radial quadrature in ECPtype1_cart,
    ECPtype2_cart, and ECPtype_so_cart declared per-primitive-pair
    convergence the first time CLOSE_ENOUGH(plast, prad) held. For
    sharply-peaked integrands at large combined AO + ECP exponent,
    two coarse rules could agree to 1e-12 while both still
    under-sampled the peak — freezing in a wrong answer. Promote the
    per-pair flag to a counter and require two consecutive matches.
  • Rewrite CLOSE_ENOUGH as a clean relative test
    |x − y| ≤ 1e-12 · max(|x|, |y|). The previous 1e-12 absolute
    fallback falsely matched high-l / high-exponent integrals whose
    true magnitude was below it; the new form catches the 0 == 0
    case naturally and has no magic absolute threshold.
  • Add a closed-form regression test exercising both the local and
    semilocal channels across n ∈ {1, 2}, l ∈ {0, 1, 2}, α ∈ {1, 1e2,
    1e4, 3e5}, g ∈ {1e1, 1e3, 1e5, 1e7}.

Before / after

Same-center s-shell with one-term local ECP n · g · 1.0, α = AO
exponent (existing PySCF radial integrator vs. closed form
<s_α|r^(n-2) e^(-g r²)|s_α>):

n α g before after
1 any any ≤ 5e-13 ≤ 5e-13
2 1e2 1e7 2.2e-5 2.1e-13
2 1e4 1e7 2.3e-5 2.1e-13
2 3e5 1e7 5.5e-5 2.9e-13
2 3e5 1e5 5.1e-7 7.6e-14

Closed-form ratio test (I(g₁)/I(g₂) against ((2α+g₂)/(2α+g₁))^((n+2l+1)/2))
across the full sweep — worst-case relative error:

channel before after
local 5.5e-5 4.7e-13
semilocal 1.0e-1 4.7e-13

Test plan

  • New test_large_exponent_ecp_closed_form passes (worst rel. err
    4.7e-13, target 1e-10).
  • Existing pyscf/gto/test/test_ecp.py (14 tests) continues to pass.
  • Existing pyscf/gto/test/test_basis_parser.py,
    pyscf/gto/test/test_moleintor.py, pyscf/grad/test/test_rhf.py
    ECP-touching tests continue to pass.
  • LANL2DZ benchmark (Cu + 6H, 34 AOs, ECPscalar build) shows no
    measurable slowdown — 5.0 vs 5.1 ms, within run-to-run noise.

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

…quadrature

The adaptive Gauss-Chebyshev radial quadrature in ECPtype1_cart,
ECPtype2_cart, and ECPtype_so_cart declared per-primitive-pair
convergence the first time CLOSE_ENOUGH(plast, prad) held between two
successive doubled grids. For sharply-peaked integrands (combined AO+ECP
exponent (2a+g) large, n >= 2, especially at higher AO angular momentum)
two coarse rules can agree to 1e-12 relative while both still
under-sample the peak at r ~ (2a+g)^-1/2, freezing in a wrong answer.
The n=1 channel happened to stay exact because r * exp(-c r^2) is far
smoother under the log-quadratic change of variables used.

Promote the per-pair flag to a counter and require two consecutive
CLOSE_ENOUGH matches before the pair is removed from refinement.

Also rewrite CLOSE_ENOUGH as a clean relative test (max of |x|, |y|
instead of |y| in the denominator, plus the absolute fallback). The
previous 1e-12 absolute floor falsely matched high-l / high-exponent
integrals whose true magnitude was below it; the new form catches the
0 == 0 case naturally and has no magic absolute threshold.

For a Kr atom with a 48-term large-exponent local ECP this removes a
constant ~4e-5 Ha absolute-energy error. Worst-case relative error on a
full alpha x g x n x l sweep (local + semilocal channels, alpha in
{1, 1e2, 1e4, 3e5}, g in {1e1, 1e3, 1e5, 1e7}, n in {1, 2}, l in
{0, 1, 2}) drops from 5.5e-5 to 4.7e-13. LANL2DZ benchmark (Cu + 6H)
shows no measurable slowdown.

Closes pyscf#3249.
…ntegrals

For a same-center single-primitive AO of angular momentum l with a
one-term local or semilocal ECP channel c * r^(n-2) * exp(-g r^2), the
matrix element factorises so that the ratio I(g1) / I(g2) at fixed
alpha is

    ((2 alpha + g2) / (2 alpha + g1)) ** ((n + 2 l + 1) / 2)

independent of the AO normalisation. This is a stringent and cheap
closed-form check on the radial quadrature.

Sweep n in {1, 2}, l in {0, 1, 2}, alpha in {1, 1e2, 1e4, 3e5},
g in {1e1, 1e3, 1e5, 1e7}, local and semilocal channels.

Before the convergence-check fix worst-case rel. err was ~5.5e-5
(local) and ~1e-1 (semilocal at l = 2, large g); now 4.7e-13.

Refs pyscf#3249.
@sunqm sunqm merged commit 322e5fa into pyscf:master Jun 13, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ECP type-1 (local) and type-2 (semilocal) radial integrals lose ~1e-5 relative accuracy at large combined exponents

2 participants