Repository: pyscf/pyscf — pull request #2499 (python)
Title: Fix biased implementation for the becke radi method

This fixed the Becke radial integration scheme as described in #2490.

Resolved issues:
title: Becke radial integration scheme does not integrate numbers of electrons correctly
body: From the available radial integration schemes the Becke one seems to not create a proper integration grid as the number of electrons is overestimated:

````python
from pyscf import dft, gto

mol = gto.M(atom="He")

for radi_method in (
   dft.radi.becke,
   dft.radi.treutler_ahlrichs,
   dft.radi.delley,
   dft.radi.gauss_chebyshev,
   dft.radi.mura_knowles,
):
   ks = dft.KS(mol)(xc="pbe", grids=dft.Grids(mol)(radi_method=radi_method), verbose=0)
   ks.kernel()
   rho = ks._numint.get_rho(mol, ks.make_rdm1(), ks.grids)
   nel = (rho * ks.grids.weights).sum()
   print(radi_method.__name__, nel)
````

Output:

````
becke 2.188070936036473
treutler_ahlrichs 1.9999999979072856
delley 1.9999999990380877
gauss_chebyshev 1.9999999994477295
mura_knowles 1.9999999997029547
````

cc Thijs @tvogels, Derk @derkkooi

Hints:

Fix patch:
diff --git a/pyscf/dft/radi.py b/pyscf/dft/radi.py
index 718aea118c..43bfb7b71d 100644
--- a/pyscf/dft/radi.py
+++ b/pyscf/dft/radi.py
@@ -47,14 +47,21 @@
 def murray(n, *args, **kwargs):
     raise RuntimeError('Not implemented')
 
-# Gauss-Chebyshev of the first kind,  and the transformed interval [0,\infty)
 def becke(n, charge, *args, **kwargs):
     '''Becke, JCP 88, 2547 (1988); DOI:10.1063/1.454033'''
     if charge == 1:
         rm = BRAGG_RADII[charge]
     else:
         rm = BRAGG_RADII[charge] * .5
-    t, w = numpy.polynomial.chebyshev.chebgauss(n)
+
+    # Points and weights for Gauss-Chebyshev quadrature points of the second kind
+    # The weights are adjusted to integrate a function on the interval [-1, 1] with uniform weighting
+    # instead of weighted by sqrt(1 - t^2) = sin(i pi / (n+1)).
+    i = numpy.arange(n) + 1
+    t = numpy.cos(i * numpy.pi / (n + 1))
+    w = numpy.pi / (n + 1) * numpy.sin(i * numpy.pi / (n + 1))
+
+    # Change of variables to map the domain to [0, inf)
     r = (1+t)/(1-t) * rm
     w *= 2/(1-t)**2 * rm
     return r, w


Test patch:
diff --git a/pyscf/dft/test/test_grids.py b/pyscf/dft/test/test_grids.py
index ba261f2f36..33dd9c02ec 100644
--- a/pyscf/dft/test/test_grids.py
+++ b/pyscf/dft/test/test_grids.py
@@ -91,7 +91,7 @@ def test_radi(self):
 
         grid.radi_method = radi.becke
         grid.build(with_non0tab=False)
-        self.assertAlmostEqual(lib.fp(grid.weights), 818061.875131255, 7)
+        self.assertAlmostEqual(lib.fp(grid.weights), 780.7183109298, 9)
 
     def test_prune(self):
         grid = gen_grid.Grids(h2o)