Repository: pyscf/pyscf — pull request #2172 (python)
Title: Improve to_hf for pbc dft

* Fix to_hf method for k-point dft methods
* FIx old compiler compatibility issue

Resolved issues:
title: KeyError in pyscf.pbc.df.df_jk?
body: With
```
from pyscf.pbc import gto, scf

cell = gto.Cell()
cell.atom='''
C 0.000000000000   0.000000000000   0.000000000000
C 1.685068664391   1.685068664391   1.685068664391
'''
cell.basis = 'gth-szv'
cell.pseudo = 'gth-pade'
cell.a = '''
0.000000000, 3.370137329, 3.370137329
3.370137329, 0.000000000, 3.370137329
3.370137329, 3.370137329, 0.000000000'''
cell.unit = 'B'
cell.verbose = 5
cell.build()

#
# KDFT and KGW with 2x2x2 k-points
#
kpts = cell.make_kpts([2,2,2])
kmf = scf.KRKS(cell, xc='pbe').density_fit()
kmf.kpts = kpts
emf = kmf.kernel()

myrhf = kmf.to_hf()
veff = myrhf.get_veff()

```

I get the following error:

```
Traceback (most recent call last):
  File "/home/chillenb/src/gwscripts/kpttest.py", line 28, in <module>
    veff = myrhf.get_veff()
           ^^^^^^^^^^^^^^^^
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/scf/khf.py", line 541, in get_veff
    vj, vk = self.get_jk(cell, dm_kpts, hermi, kpts, kpts_band)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/scf/khf.py", line 529, in get_jk
    vj, vk = self.with_df.get_jk(dm_kpts, hermi, kpts, kpts_band,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df.py", line 443, in get_jk
    vk = df_jk.get_k_kpts(self, dm, hermi, kpts, kpts_band, exxdiv)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df_jk.py", line 613, in get_k_kpts
    make_kpt(ki, kj, True)
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df_jk.py", line 446, in make_kpt
    for LpqR, LpqI, sign in mydf.sr_loop((kpti,kptj), max_memory, False):
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df.py", line 355, in sr_loop
    with _load3c(self._cderi, self._dataname, kpti_kptj) as j3c:
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df.py", line 692, in __enter__
    return self.getitem(*self.kpti_kptj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/chillenb/mambaforge/envs/rttddft/lib/python3.11/site-packages/pyscf/pbc/df/df.py", line 746, in getitem
    raise KeyError(f'Key {key} not found')
KeyError: 'Key j3c/8 not found'
```


The fact that the 3c2e integrals should now be computed, and `_j_only` reset, is not getting detected. Is there a recommended way to do this manually?

Would it be bad design to add an option to scf.KRKS.density_fit() so that, if I need to calculate Vk later, I can override the default value of _j_only?


(PySCF 2.5.0 installed from conda-forge)



Hints:

Fix patch:
diff --git a/pyscf/lib/dft/utils.c b/pyscf/lib/dft/utils.c
index 04ef8e5b2f..fc27b4a48f 100644
--- a/pyscf/lib/dft/utils.c
+++ b/pyscf/lib/dft/utils.c
@@ -52,11 +52,20 @@ void get_gga_vrho_gs(double complex *out, double complex *vrho_gs, double comple
     int i;
     int ngrid2 = 2 * ngrid;
     double complex fac = -2. * _Complex_I;
-    #pragma omp parallel for simd schedule(static)
+#pragma omp parallel
+{
+    double complex v;
+// ensure OpenMP 4.0
+#if defined _OPENMP && _OPENMP >= 201307
+    #pragma omp for simd schedule(static)
+#else
+    #pragma omp for schedule(static)
+#endif
     for (i = 0; i < ngrid; i++) {
-        out[i] = ( Gv[i*3]   * vsigma1_gs[i]
-                  +Gv[i*3+1] * vsigma1_gs[i+ngrid]
-                  +Gv[i*3+2] * vsigma1_gs[i+ngrid2]) * fac + vrho_gs[i];
-        out[i] *= weight;
+        v = ( Gv[i*3]   * vsigma1_gs[i]
+             +Gv[i*3+1] * vsigma1_gs[i+ngrid]
+             +Gv[i*3+2] * vsigma1_gs[i+ngrid2]) * fac + vrho_gs[i];
+        out[i] = v * weight;
     }
 }
+}
diff --git a/pyscf/pbc/dft/kgks.py b/pyscf/pbc/dft/kgks.py
index 7774f2e36e..fb9e1f2f68 100644
--- a/pyscf/pbc/dft/kgks.py
+++ b/pyscf/pbc/dft/kgks.py
@@ -146,7 +146,14 @@ def nuc_grad_method(self):
 
     def to_hf(self):
         '''Convert to KGHF object.'''
-        from pyscf.pbc import scf
-        return self._transfer_attrs_(scf.KGHF(self.cell, self.kpts))
+        from pyscf.pbc import scf, df
+        out = self._transfer_attrs_(scf.KGHF(self.cell, self.kpts))
+
+        # Pure functionals only construct J-type integrals. Enable all integrals for KHF.
+        if (not self._numint.libxc.is_hybrid_xc(self.xc) and
+            len(self.kpts) > 1 and getattr(self.with_df, '_j_only', False)):
+            out.with_df._j_only = False
+            out.with_df.reset()
+        return out
 
     to_gpu = lib.to_gpu
diff --git a/pyscf/pbc/dft/krks.py b/pyscf/pbc/dft/krks.py
index 6278605de9..d690647879 100644
--- a/pyscf/pbc/dft/krks.py
+++ b/pyscf/pbc/dft/krks.py
@@ -181,25 +181,13 @@ def nuc_grad_method(self):
 
     def to_hf(self):
         '''Convert to KRHF object.'''
-        from pyscf.pbc import scf
-        return self._transfer_attrs_(scf.KRHF(self.cell, self.kpts))
+        from pyscf.pbc import scf, df
+        out = self._transfer_attrs_(scf.KRHF(self.cell, self.kpts))
+        # Pure functionals only construct J-type integrals. Enable all integrals for KHF.
+        if (not self._numint.libxc.is_hybrid_xc(self.xc) and
+            len(self.kpts) > 1 and getattr(self.with_df, '_j_only', False)):
+            out.with_df._j_only = False
+            out.with_df.reset()
+        return out
 
     to_gpu = lib.to_gpu
-
-
-if __name__ == '__main__':
-    from pyscf.pbc import gto
-    cell = gto.Cell()
-    cell.unit = 'A'
-    cell.atom = 'C 0.,  0.,  0.; C 0.8917,  0.8917,  0.8917'
-    cell.a = '''0.      1.7834  1.7834
-                1.7834  0.      1.7834
-                1.7834  1.7834  0.    '''
-
-    cell.basis = 'gth-szv'
-    cell.pseudo = 'gth-pade'
-    cell.verbose = 7
-    cell.output = '/dev/null'
-    cell.build()
-    mf = KRKS(cell, cell.make_kpts([2,1,1]))
-    print(mf.kernel())
diff --git a/pyscf/pbc/dft/kroks.py b/pyscf/pbc/dft/kroks.py
index 1d2a1198f8..640bf05e97 100644
--- a/pyscf/pbc/dft/kroks.py
+++ b/pyscf/pbc/dft/kroks.py
@@ -61,25 +61,13 @@ def dump_flags(self, verbose=None):
 
     def to_hf(self):
         '''Convert to KROHF object.'''
-        from pyscf.pbc import scf
-        return self._transfer_attrs_(scf.KROHF(self.cell, self.kpts))
+        from pyscf.pbc import scf, df
+        out = self._transfer_attrs_(scf.KROHF(self.cell, self.kpts))
+        # Pure functionals only construct J-type integrals. Enable all integrals for KHF.
+        if (not self._numint.libxc.is_hybrid_xc(self.xc) and
+            len(self.kpts) > 1 and getattr(self.with_df, '_j_only', False)):
+            out.with_df._j_only = False
+            out.with_df.reset()
+        return out
 
     to_gpu = lib.to_gpu
-
-
-if __name__ == '__main__':
-    from pyscf.pbc import gto
-    cell = gto.Cell()
-    cell.unit = 'A'
-    cell.atom = 'C 0.,  0.,  0.; C 0.8917,  0.8917,  0.8917'
-    cell.a = '''0.      1.7834  1.7834
-                1.7834  0.      1.7834
-                1.7834  1.7834  0.    '''
-
-    cell.basis = 'gth-szv'
-    cell.pseudo = 'gth-pade'
-    cell.verbose = 7
-    cell.output = '/dev/null'
-    cell.build()
-    mf = KROKS(cell, cell.make_kpts([2,1,1]))
-    print(mf.kernel())
diff --git a/pyscf/pbc/dft/kuks.py b/pyscf/pbc/dft/kuks.py
index ac66f973bc..732adcfa32 100644
--- a/pyscf/pbc/dft/kuks.py
+++ b/pyscf/pbc/dft/kuks.py
@@ -157,25 +157,13 @@ def nuc_grad_method(self):
 
     def to_hf(self):
         '''Convert to KUHF object.'''
-        from pyscf.pbc import scf
-        return self._transfer_attrs_(scf.KUHF(self.cell, self.kpts))
+        from pyscf.pbc import scf, df
+        out = self._transfer_attrs_(scf.KUHF(self.cell, self.kpts))
+        # Pure functionals only construct J-type integrals. Enable all integrals for KHF.
+        if (not self._numint.libxc.is_hybrid_xc(self.xc) and
+            len(self.kpts) > 1 and getattr(self.with_df, '_j_only', False)):
+            out.with_df._j_only = False
+            out.with_df.reset()
+        return out
 
     to_gpu = lib.to_gpu
-
-
-if __name__ == '__main__':
-    from pyscf.pbc import gto
-    cell = gto.Cell()
-    cell.unit = 'A'
-    cell.atom = 'C 0.,  0.,  0.; C 0.8917,  0.8917,  0.8917'
-    cell.a = '''0.      1.7834  1.7834
-                1.7834  0.      1.7834
-                1.7834  1.7834  0.    '''
-
-    cell.basis = 'gth-szv'
-    cell.pseudo = 'gth-pade'
-    cell.verbose = 7
-    cell.output = '/dev/null'
-    cell.build()
-    mf = KUKS(cell, cell.make_kpts([2,1,1]))
-    print(mf.kernel())


Test patch:
diff --git a/pyscf/pbc/dft/test/test_kgks.py b/pyscf/pbc/dft/test/test_kgks.py
index a4fba351c4..6ebd1165ea 100644
--- a/pyscf/pbc/dft/test/test_kgks.py
+++ b/pyscf/pbc/dft/test/test_kgks.py
@@ -22,6 +22,7 @@
 from pyscf import lib
 from pyscf.pbc import gto as gto
 from pyscf.pbc import dft as dft
+from pyscf.pbc import scf as pbcscf
 from pyscf.pbc.df import rsdf_builder, gdf_builder
 try:
     import mcfun
@@ -189,6 +190,19 @@ def test_mcol_x2c_kgks_lda(self):
         mf.run()
         self.assertAlmostEqual(mf.e_tot, -1.4910121442258883, 6)
 
+    def test_to_hf(self):
+        mf = dft.KGKS(cell).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.kghf.KGHF))
+
+        mf = dft.KGKS(cell, kpts=cell.make_kpts([2,1,1])).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(not a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.kghf.KGHF))
+
 
 if __name__ == '__main__':
     print("Full Tests for pbc.dft.kgks")
diff --git a/pyscf/pbc/dft/test/test_krks.py b/pyscf/pbc/dft/test/test_krks.py
index 090711ec54..e8418cb972 100644
--- a/pyscf/pbc/dft/test/test_krks.py
+++ b/pyscf/pbc/dft/test/test_krks.py
@@ -22,6 +22,7 @@
 
 from pyscf.pbc import gto as pbcgto
 from pyscf.pbc import dft as pbcdft
+from pyscf.pbc import scf as pbcscf
 
 
 def build_cell(mesh):
@@ -143,6 +144,31 @@ def test_rsh_df(self):
         mf.kernel()
         self.assertAlmostEqual(mf.e_tot, -2.4766238116030683, 5)
 
+    def test_to_hf(self):
+        mf = pbcdft.KRKS(cell).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.khf.KRHF))
+
+        mf = pbcdft.KRKS(cell, kpts=cell.make_kpts([2,1,1])).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(not a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.khf.KRHF))
+
+        mf = pbcdft.KROKS(cell).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.krohf.KROHF))
+
+        mf = pbcdft.KROKS(cell, kpts=cell.make_kpts([2,1,1])).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(not a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.krohf.KROHF))
+
 # TODO: test the reset method of pbcdft.KRKS, pbcdft.RKS whether the reset
 # methods of all subsequent objects are called
 
diff --git a/pyscf/pbc/dft/test/test_kuks.py b/pyscf/pbc/dft/test/test_kuks.py
index d99fddf7fa..a81ae3051f 100644
--- a/pyscf/pbc/dft/test/test_kuks.py
+++ b/pyscf/pbc/dft/test/test_kuks.py
@@ -19,6 +19,7 @@
 import unittest
 import numpy as np
 from pyscf.pbc import gto as pbcgto
+from pyscf.pbc import scf as pbcscf
 from pyscf.pbc import dft as pbcdft
 
 
@@ -91,6 +92,19 @@ def test_rsh_df(self):
         mf.kernel()
         self.assertAlmostEqual(mf.e_tot, -2.4766238116030683, 7)
 
+    def test_to_hf(self):
+        mf = pbcdft.KUKS(cell).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.kuhf.KUHF))
+
+        mf = pbcdft.KUKS(cell, kpts=cell.make_kpts([2,1,1])).density_fit()
+        mf.with_df._j_only = True
+        a_hf = mf.to_hf()
+        self.assertTrue(not a_hf.with_df._j_only)
+        self.assertTrue(isinstance(a_hf, pbcscf.kuhf.KUHF))
+
 
 if __name__ == '__main__':
     print("Full Tests for pbc.dft.kuks")