Task: Fixes calculating exchange integrals when only_dfj=True, issue #2456 (pyscf/pyscf#2457)

org: pyscf
repo: pyscf
pr_number: 2457
issue_title: Fixes calculating exchange integrals when only_dfj=True, issue #2456
issue_body: This PR fixes an assertion error in the following minimal example:
```
from pyscf import gto, dft

mol = gto.M(
    atom="""
        O  0.00000000  0.00000000  0.00000000
        H -0.75390364  0.00000000 -0.58783729
        H  0.75390364  0.00000000 -0.58783729
    """
)

mf = dft.RKS(mol).density_fit(only_dfj=True).run()
vk = mf.get_k(mol=mol)
```

resolved_issues: number: 2456
title: Calculating exchange integrals fails when only_dfj=True
body: Hi everyone,
the following minimal example fails due to an assertion error:
```
from pyscf import gto, dft

mol = gto.M(
    atom="""
        O  0.00000000  0.00000000  0.00000000
        H -0.75390364  0.00000000 -0.58783729
        H  0.75390364  0.00000000 -0.58783729
    """
)

mf = dft.RKS(mol).density_fit(only_dfj=True).run()
vk = mf.get_k(mol=mol)
```
It should still be possible to get the regular exchange integrals which are also returned for `get_jk` if `with_j=True` and `with_k=True`. The issue appears to be in the following function:
https://github.com/pyscf/pyscf/blob/410d9608a73ad3a507b3008a6f68549288ebfc33/pyscf/df/df_jk.py#L133-L159

I would propose the following solution:
 ```
   def get_jk(self, mol=None, dm=None, hermi=1, with_j=True, with_k=True,
               omega=None):
        assert (with_j or with_k)
        if dm is None: dm = self.make_rdm1()
        if not self.with_df:
            return super().get_jk(mol, dm, hermi, with_j, with_k, omega)

        with_dfk = with_k and not self.only_dfj
        if with_j:
            if isinstance(self, scf.ghf.GHF):
                def jkbuild(mol, dm, hermi, with_j, with_k, omega=None):
                    vj, vk = self.with_df.get_jk(dm.real, hermi, with_j, with_k,
                                                self.direct_scf_tol, omega)
                    if dm.dtype == numpy.complex128:
                        vjI, vkI = self.with_df.get_jk(dm.imag, hermi, with_j, with_k,
                                                    self.direct_scf_tol, omega)
                        if with_j:
                            vj = vj + vjI * 1j
                        if with_k:
                            vk = vk + vkI * 1j
                    return vj, vk
                vj, vk = scf.ghf.get_jk(mol, dm, hermi, with_j, with_dfk,
                                        jkbuild, omega)
            else:
                vj, vk = self.with_df.get_jk(dm, hermi, with_j, with_dfk,
                                            self.direct_scf_tol, omega)
        else:
            vj = None
        if with_k and not with_dfk:
            vk = super().get_jk(mol, dm, hermi, False, True, omega)[1]
        return vj, vk
```
This change would still require that either the Coulomb or the exchange matrix are requested while ensuring the regular exchange matrix is returned whenever the corresponding density-fitted integrals are not available. I can open a PR if this behavior/fix is desired.

Reference solution:
fix_patch: diff --git a/pyscf/df/df_jk.py b/pyscf/df/df_jk.py
index 2ac82c243c..972b7e2033 100644
--- a/pyscf/df/df_jk.py
+++ b/pyscf/df/df_jk.py
@@ -132,28 +132,31 @@ def reset(self, mol=None):
 
     def get_jk(self, mol=None, dm=None, hermi=1, with_j=True, with_k=True,
                omega=None):
+        assert (with_j or with_k)
         if dm is None: dm = self.make_rdm1()
         if not self.with_df:
             return super().get_jk(mol, dm, hermi, with_j, with_k, omega)
 
+        vj = vk = None
         with_dfk = with_k and not self.only_dfj
-        if isinstance(self, scf.ghf.GHF):
-            def jkbuild(mol, dm, hermi, with_j, with_k, omega=None):
-                vj, vk = self.with_df.get_jk(dm.real, hermi, with_j, with_k,
-                                             self.direct_scf_tol, omega)
-                if dm.dtype == numpy.complex128:
-                    vjI, vkI = self.with_df.get_jk(dm.imag, hermi, with_j, with_k,
-                                                   self.direct_scf_tol, omega)
-                    if with_j:
-                        vj = vj + vjI * 1j
-                    if with_k:
-                        vk = vk + vkI * 1j
-                return vj, vk
-            vj, vk = scf.ghf.get_jk(mol, dm, hermi, with_j, with_dfk,
-                                    jkbuild, omega)
-        else:
-            vj, vk = self.with_df.get_jk(dm, hermi, with_j, with_dfk,
-                                         self.direct_scf_tol, omega)
+        if with_j or with_dfk:
+            if isinstance(self, scf.ghf.GHF):
+                def jkbuild(mol, dm, hermi, with_j, with_k, omega=None):
+                    vj, vk = self.with_df.get_jk(dm.real, hermi, with_j, with_k,
+                                                self.direct_scf_tol, omega)
+                    if dm.dtype == numpy.complex128:
+                        vjI, vkI = self.with_df.get_jk(dm.imag, hermi, with_j, with_k,
+                                                    self.direct_scf_tol, omega)
+                        if with_j:
+                            vj = vj + vjI * 1j
+                        if with_k:
+                            vk = vk + vkI * 1j
+                    return vj, vk
+                vj, vk = scf.ghf.get_jk(mol, dm, hermi, with_j, with_dfk,
+                                        jkbuild, omega)
+            else:
+                vj, vk = self.with_df.get_jk(dm, hermi, with_j, with_dfk,
+                                            self.direct_scf_tol, omega)
         if with_k and not with_dfk:
             vk = super().get_jk(mol, dm, hermi, False, True, omega)[1]
         return vj, vk