Repository: pyscf/pyscf — pull request #1947 (python)
Title: Fix rsdf due to the basis sorting in 923d84c05 (issue #1942)


Resolved issues:
title: Possible bug in RSGDF
body: `RSGDF` fails (produces unreasonably high energies and doesn't converge) in a relatively simple case where `GDF` and `FFTDF` both provide reasonable results:

```
from pyscf.pbc import df, dft, gto
import numpy as np


def main():
    # unit cell
    a = np.array([[5.87704825, 0., 0.],
                  [-2.93852412, 5.08967308, 0.],
                  [0., 0., 9.4108361]])

    atom = '''
    Al   0.000000000000e+00   0.000000000000e+00   0.000000000000e+00;
    N    1.087472380182e-16   3.393115387675e+00   1.129307415351e+00;
    Al   1.087472380182e-16   3.393115387675e+00   4.705418050167e+00;
    N    0.000000000000e+00   0.000000000000e+00   5.834725465518e+00;
    '''
    cell = gto.M(a=a,
                 atom=atom,
                 unit='bohr',
                 basis='gth-szv',
                 pseudo={'Al': 'GTH-LDA', 'N': 'GTH-LDA'},
                 verbose=4,
                 precision=1.0e-12,
                 spin=0)
    cell.build()
    kpts = cell.make_kpts([1, 1, 1])

    # Regular GDF works fine for me (E = -23.5674653795703)
    #gdf = df.GDF(cell, kpts=kpts)

    # RSGDF doesn't work (unphysically high energies)
    gdf = df.RSGDF(cell, kpts=kpts)

    mf = dft.KRKS(cell, kpts=kpts, exxdiv=None, xc='PBE')
    mf.with_df = gdf
    mf.kernel()


if __name__ == '__main__':
    main()
```

Hints:

Fix patch:
diff --git a/pyscf/gto/mole.py b/pyscf/gto/mole.py
index 49f7fe9f34..43a47edaf4 100644
--- a/pyscf/gto/mole.py
+++ b/pyscf/gto/mole.py
@@ -1023,7 +1023,7 @@ def make_env(atoms, basis, pre_env=[], nucmod={}, nucprop={}):
     '''
     _atm = []
     _bas = []
-    _env = []
+    _env = [pre_env]
     ptr_env = len(pre_env)
 
     for ia, atom in enumerate(atoms):
@@ -1092,10 +1092,7 @@ def make_env(atoms, basis, pre_env=[], nucmod={}, nucprop={}):
         _bas = numpy.asarray(numpy.vstack(_bas), numpy.int32).reshape(-1, BAS_SLOTS)
     else:
         _bas = numpy.zeros((0,BAS_SLOTS), numpy.int32)
-    if _env:
-        _env = numpy.hstack((pre_env,numpy.hstack(_env)))
-    else:
-        _env = numpy.array(pre_env, copy=False)
+    _env = numpy.asarray(numpy.hstack(_env), dtype=numpy.float64)
     return _atm, _bas, _env
 
 def make_ecp_env(mol, _atm, ecp, pre_env=[]):
diff --git a/pyscf/pbc/df/rsdf_helper.py b/pyscf/pbc/df/rsdf_helper.py
index 719235e171..7fa0f74e2e 100644
--- a/pyscf/pbc/df/rsdf_helper.py
+++ b/pyscf/pbc/df/rsdf_helper.py
@@ -37,18 +37,21 @@
 """
 class MoleNoBasSort(mol_gto.mole.Mole):
     def build(self, **kwargs):
-        mol_gto.mole.Mole.build(self, **kwargs)
-
-        # sort self._bas
-        _bas = []
-        for iatm in range(self.natm):
-            atm = self.atom_symbol(iatm)
-            basin = self._basis[atm]
-            bas_ls = [b[0] for b in basin]
-            ls_uniq, ls_inv = np.unique(bas_ls, return_inverse=True)
-            order = np.argsort(np.concatenate([np.where(ls_inv==l)[0] for l in ls_uniq]))
-            _bas.append( self._bas[np.where(self._bas[:,0] == iatm)[0][order]] )
-        self._bas = np.vstack(_bas).astype(np.int32)
+        self.atom = kwargs.pop('atom')
+        self.basis = kwargs.pop('basis')
+        self._atom = mol_gto.format_atom(self.atom)
+        if isinstance(self.basis, dict):
+            self._basis = self.basis
+        else:
+            self._basis = {a[0]: self.basis for a in self._atom}
+
+        env = np.zeros(mol_gto.PTR_ENV_START)
+        # _bas should be constructed as it is in the input (see issue #1942).
+        self._atm, self._bas, self._env = self.make_env(
+            self._atom, self._basis, env)
+        self._built = True
+        return self
+
 def _remove_exp_basis_(bold, amin, amax):
     bnew = []
     for b in bold:


Test patch:
diff --git a/pyscf/pbc/df/test/test_rsdf.py b/pyscf/pbc/df/test/test_rsdf.py
index de00bcd0f3..d05a6633de 100644
--- a/pyscf/pbc/df/test/test_rsdf.py
+++ b/pyscf/pbc/df/test/test_rsdf.py
@@ -19,7 +19,7 @@
 from pyscf import ao2mo
 from pyscf.pbc import gto as pgto
 from pyscf.pbc import scf as pscf
-from pyscf.pbc.df import rsdf
+from pyscf.pbc.df import rsdf, df
 #from mpi4pyscf.pbc.df import df
 pyscf.pbc.DEBUG = False
 
@@ -124,6 +124,28 @@ def test_get_eri_0123(self):
         self.assertAlmostEqual(abs(eri0123.imag.sum()), 4.9887958509e-5, 7)
         self.assertAlmostEqual(lib.fp(eri0123), 0.9695261296288074-0.33222740818370966j, 8)
 
+    def test_rsdf_build(self):
+        cell = pgto.M(a=numpy.eye(3)*1.8,
+                      atom='''Li   0.   0.    0.; H    0.   .5   1.2 ''',
+                      basis={'Li': [[0, [5., 1.]], [0, [.6, 1.]], [1, [3., 1.]]],
+                             'H': [[0, [.3, 1.]]]})
+        auxbasis = {'Li': [[0, [5., 1.]], [0, [1.5, 1.]], [1, [.5, 1.]], [2, [2.5, 1.]]],
+                    'H':  [[0, [2., 1.]]]}
+        numpy.random.seed(2)
+        dm = numpy.random.random([cell.nao]*2)
+
+        gdf = df.GDF(cell)
+        gdf.auxbasis = auxbasis
+        jref, kref = gdf.get_jk(dm)
+
+        gdf = rsdf.RSGDF(cell)
+        gdf.auxbasis = auxbasis
+        vj, vk = gdf.get_jk(dm)
+
+        self.assertAlmostEqual(abs(vj - jref).max(), 0, 7)
+        self.assertAlmostEqual(abs(vk - kref).max(), 0, 7)
+        self.assertAlmostEqual(lib.fp(vj), 2.383648833459583, 7)
+        self.assertAlmostEqual(lib.fp(vk), 1.553598328349400, 7)
 
 if __name__ == '__main__':
     print("Full Tests for rsdf")