Skip to content

Commit 53f6d43

Browse files
Merge branch 'master' into debug
2 parents 0ebf77b + f7d1769 commit 53f6d43

9 files changed

Lines changed: 212 additions & 66 deletions

File tree

‎pyscf/pbc/pwscf/ao2mo/molint.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def get_molint_from_C(cell, C_ks, kpts, mo_slices=None, exxdiv=None,
7171
eri_mesh = cell.cutoff_to_mesh(ecut_eri)
7272
if (np.array(mesh) == eri_mesh).all():
7373
eri_mesh = None
74-
if (eri_mesh > mesh).any():
74+
elif (eri_mesh > mesh).any():
7575
logger.warn(
7676
cell,
7777
"eri_mesh larger than mesh, so eri_mesh is ignored."

‎pyscf/pbc/pwscf/kccsd_rhf.py‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class PWKRCCSD(cc.kccsd_rhf.RCCSD):
5858
Other CCSD results can be accessed from the underlaying
5959
`mcc` (Periodic RCCSD, which contains e_tot, t-amplitudes, etc.)
6060
after `kernel` is called.
61+
62+
`ecut_eri` can be set to use a sparser grid for ERI evaluation,
63+
reducing computational cost. NOTE that setting `ecut_eri` too
64+
small can cause numerical issues.
6165
"""
6266

6367
_keys = {'ecut_eri'}
@@ -101,15 +105,28 @@ def __init__(self, cc):
101105
self.e_hf = mf.e_tot
102106
self.mo_energy = np.asarray(mo_energy)
103107

104-
if False:#cc.keep_exxdiv:
105-
moe_noewald = self.mo_energy
106-
else:
107-
# remove ewald correction
108+
if cc._scf.exxdiv == "ewald" and not cc.keep_exxdiv:
109+
# remove ewald correction form mo_energy to make self.fock
108110
moe_noewald = np.zeros_like(self.mo_energy)
109111
for k in range(nkpts):
110112
moe = self.mo_energy[k].copy()
111113
moe[mo_occ[k]>THR_OCC] += mf._madelung
112114
moe_noewald[k] = moe
115+
else:
116+
moe_noewald = self.mo_energy
117+
if not cc.keep_exxdiv:
118+
# To match the GTO implementation, we add Madelung correction
119+
# to the mo_energy even if exxdiv=None.
120+
# TODO is this actually the best practice? One might expect
121+
# exxdiv to not be used anywhere if it is explicitly
122+
# set to None by the user. For now, we match the
123+
# behavior of the equivalent GTO CCSD calculation.
124+
# This choice affects initial amplitude guess as well
125+
# as the perturbative triples energy.
126+
from pyscf.pbc.cc.ccsd import _adjust_occ
127+
mf._set_madelung()
128+
self.mo_energy = [_adjust_occ(mo_e, cc.nocc, -mf._madelung)
129+
for k, mo_e in enumerate(self.mo_energy)]
113130
self.fock = np.asarray(
114131
[np.diag(moe.astype(np.complex128)) for moe in moe_noewald]
115132
)

‎pyscf/pbc/pwscf/khf.py‎

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
from pyscf.pbc.pwscf import chkfile
4545
from pyscf.pbc import gto, scf, tools
4646
from pyscf.pbc.pwscf import pw_helper
47-
from pyscf.pbc.pwscf.pw_helper import get_kcomp, set_kcomp
47+
from pyscf.pbc.pwscf.pw_helper import get_kcomp, set_kcomp, ewald_correction
4848
from pyscf.pbc.pwscf import pseudo as pw_pseudo
4949
from pyscf.pbc.pwscf import jk as pw_jk
5050
from pyscf.lib import logger
@@ -1159,22 +1159,6 @@ def apply_Fock_kpt(mf, C_k, kpt, mocc_ks, mesh, Gv, vj_R, exxdiv,
11591159
return Cbar_k
11601160

11611161

1162-
def ewald_correction(moe_ks, mocc_ks, madelung):
1163-
if isinstance(moe_ks[0][0], float): # RHF
1164-
nkpts = len(moe_ks)
1165-
moe_ks_new = [None] * nkpts
1166-
for k in range(nkpts):
1167-
moe_ks_new[k] = moe_ks[k].copy()
1168-
moe_ks_new[k][:] -= 0.5 * mocc_ks[k] * madelung
1169-
else: # UHF
1170-
ncomp = len(moe_ks)
1171-
moe_ks_new = [None] * ncomp
1172-
for comp in range(ncomp):
1173-
moe_ks_new[comp] = ewald_correction(moe_ks[comp], mocc_ks[comp],
1174-
ncomp * madelung)
1175-
return moe_ks_new
1176-
1177-
11781162
def get_mo_energy(mf, C_ks, mocc_ks, mesh=None, Gv=None, exxdiv=None,
11791163
vj_R=None, comp=None, ret_mocc=True, full_ham=False):
11801164
"""

‎pyscf/pbc/pwscf/kmp2.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ def PWKRMP2_from_gtomf(mf, chkfile=None):
426426
class PWKRMP2:
427427
"""
428428
Restriced MP2 perturbation theory in a plane-wave basis.
429+
430+
`ecut_eri` can be set to use a sparser grid for ERI evaluation,
431+
reducing computational cost. NOTE that setting `ecut_eri` too
432+
small can cause numerical issues.
429433
"""
430434
def __init__(self, mf, nvir=None, frozen=None):
431435
self.cell = self.mol = mf.cell

‎pyscf/pbc/pwscf/kump2.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ def kernel_dx_(cell, kpts, chkfile_name, summary, nvir=None, nvir_lst=None,
387387
class PWKUMP2(kmp2.PWKRMP2):
388388
"""
389389
Spin-unrestriced MP2 in a plane-wave basis.
390+
391+
`ecut_eri` can be set to use a sparser grid for ERI evaluation,
392+
reducing computational cost. NOTE that setting `ecut_eri` too
393+
small can cause numerical issues.
390394
"""
391395
def __init__(self, mf, nvir=None):
392396
kmp2.PWKRMP2.__init__(self, mf, nvir=nvir)

‎pyscf/pbc/pwscf/pw_helper.py‎

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,22 @@ def get_basis_data(cell, kpts, ecut_wf, ecut_rho=None, wf_mesh=None,
105105
return np.array(wf_mesh), np.array(xc_mesh), wf2xc, basis_data
106106

107107

108+
def ewald_correction(moe_ks, mocc_ks, madelung):
109+
if isinstance(moe_ks[0][0], float): # RHF
110+
nkpts = len(moe_ks)
111+
moe_ks_new = [None] * nkpts
112+
for k in range(nkpts):
113+
moe_ks_new[k] = moe_ks[k].copy()
114+
moe_ks_new[k][:] -= 0.5 * mocc_ks[k] * madelung
115+
else: # UHF
116+
ncomp = len(moe_ks)
117+
moe_ks_new = [None] * ncomp
118+
for comp in range(ncomp):
119+
moe_ks_new[comp] = ewald_correction(moe_ks[comp], mocc_ks[comp],
120+
ncomp * madelung)
121+
return moe_ks_new
122+
123+
108124
def wf_fft(C_k_R, mesh, basis=None):
109125
"""
110126
Fourier transform a wave function from real to reciprocal space.
@@ -468,9 +484,20 @@ def gto2cpw(cell, basis, kpts, amin=None, amax=None, ke_or_mesh=None, out=None):
468484
return out
469485

470486

487+
def _reduce_fock_to_moe(fockao, mo_coeff):
488+
return [np.einsum("ai,ai->i", mo.conj(), fockao[k].dot(mo)).real
489+
for k, mo in enumerate(mo_coeff)]
490+
491+
471492
def gtomf2pwmf(mf, chkfile=None):
472493
"""
494+
Create a PW HF calculation from a GTO HF calculation.
495+
If mf.exxdiv is "ewald", the returned PW HF object
496+
has exxdiv="ewald", otherwise exxdiv=None.
497+
473498
Args:
499+
mf:
500+
GTO HF calculation
474501
chkfile (str):
475502
A hdf5 file to store chk variables (mo_energy, mo_occ, etc.).
476503
If not provided, a temporary file is generated.
@@ -484,44 +511,79 @@ def gtomf2pwmf(mf, chkfile=None):
484511
nkpts = len(kpts)
485512
# transform GTO MO coeff to PW MO coeff
486513
Cgto_ks = mf.mo_coeff
514+
515+
exxdiv = "ewald" if mf.exxdiv == "ewald" else None
516+
if mf.exxdiv is not None:
517+
dm = mf.make_rdm1(mf.mo_coeff, mf.mo_occ)
518+
with lib.temporary_env(mf, exxdiv=None):
519+
# Calculate veff without the exxdiv
520+
vhf = mf.get_veff(cell, dm)
521+
fockao = mf.get_hcore() + vhf
522+
487523
if isinstance(mf, scf.khf.KRHF):
488-
pwmf = pwscf.KRHF(cell, kpts)
524+
pwmf = pwscf.KRHF(cell, kpts, exxdiv=exxdiv)
489525
nmo_ks = [Cgto_ks[k].shape[1] for k in range(nkpts)]
490526
pwmf.mo_coeff = C_ks = get_C_ks_G(cell, kpts, Cgto_ks, nmo_ks)
491-
pwmf.mo_energy = moe_ks = mf.mo_energy
527+
if mf.exxdiv is None:
528+
pwmf.mo_energy = moe_ks = mf.mo_energy
529+
else:
530+
pwmf.mo_energy = moe_ks = _reduce_fock_to_moe(fockao, mf.mo_coeff)
492531
pwmf.mo_occ = mocc_ks = mf.mo_occ
493532
pwmf.e_tot = mf.e_tot
494533
elif isinstance(mf, scf.kuhf.KUHF):
495-
pwmf = pwscf.KUHF(cell, kpts)
534+
pwmf = pwscf.KUHF(cell, kpts, exxdiv=exxdiv)
496535
C_ks = [None] * 2
497536
for s in [0,1]:
498537
nmo_ks = [Cgto_ks[s][k].shape[1] for k in range(nkpts)]
499538
C_ks[s] = get_C_ks_G(cell, kpts, Cgto_ks[s], nmo_ks)
500539
pwmf.mo_coeff = C_ks
501-
pwmf.mo_energy = moe_ks = mf.mo_energy
540+
if mf.exxdiv is None:
541+
pwmf.mo_energy = moe_ks = mf.mo_energy
542+
else:
543+
pwmf.mo_energy = moe_ks = [
544+
_reduce_fock_to_moe(fockao[0], mf.mo_coeff[0]),
545+
_reduce_fock_to_moe(fockao[1], mf.mo_coeff[1]),
546+
]
502547
pwmf.mo_occ = mocc_ks = mf.mo_occ
503548
pwmf.e_tot = mf.e_tot
504549
elif isinstance(mf, scf.hf.RHF):
505-
pwmf = pwscf.KRHF(cell, kpts)
550+
pwmf = pwscf.KRHF(cell, kpts, exxdiv=exxdiv)
506551
nmo_ks = [Cgto_ks.shape[1]]
507552
C_ks = get_C_ks_G(cell, kpts, [Cgto_ks], nmo_ks)
508553
pwmf.mo_coeff = C_ks
509-
pwmf.mo_energy = moe_ks = [mf.mo_energy]
554+
if mf.exxdiv is None:
555+
pwmf.mo_energy = moe_ks = [mf.mo_energy]
556+
else:
557+
pwmf.mo_energy = moe_ks = _reduce_fock_to_moe(
558+
[fockao], [mf.mo_coeff]
559+
)
510560
pwmf.mo_occ = mocc_ks = [mf.mo_occ]
511561
pwmf.e_tot = mf.e_tot
512562
elif isinstance(mf, scf.uhf.UHF):
513-
pwmf = pwscf.KUHF(cell, kpts)
563+
pwmf = pwscf.KUHF(cell, kpts, exxdiv=exxdiv)
514564
C_ks = [None] * 2
515565
for s in [0,1]:
516566
nmo_ks = [Cgto_ks[s].shape[1]]
517567
C_ks[s] = get_C_ks_G(cell, kpts, [Cgto_ks[s]], nmo_ks)
518568
pwmf.mo_coeff = C_ks
519-
pwmf.mo_energy = moe_ks = [[mf.mo_energy[s]] for s in [0,1]]
569+
if mf.exxdiv is None:
570+
pwmf.mo_energy = moe_ks = [[mf.mo_energy[s]] for s in [0,1]]
571+
else:
572+
pwmf.mo_energy = moe_ks = [
573+
_reduce_fock_to_moe([fockao[0]], [mf.mo_coeff[0]]),
574+
_reduce_fock_to_moe([fockao[1]], [mf.mo_coeff[1]]),
575+
]
520576
pwmf.mo_occ = mocc_ks = [[mf.mo_occ[s]] for s in [0,1]]
521577
pwmf.e_tot = mf.e_tot
522578
else:
523579
raise TypeError
524-
# update chkfile
580+
581+
if exxdiv == "ewald":
582+
pwmf.mo_energy = moe_ks = ewald_correction(
583+
pwmf.mo_energy, pwmf.mo_occ, pwmf.madelung
584+
)
585+
586+
# update chkfile
525587
if chkfile is None:
526588
swapfile = tempfile.NamedTemporaryFile(dir=lib.param.TMPDIR)
527589
chkfile = swapfile.name

‎pyscf/pbc/pwscf/test/test_krccsd.py‎

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
class KnownValues(unittest.TestCase):
2222
def _run_test(self, atom, a, basis, pseudo, ke_cutoff, kmesh, exxdiv,
23-
test_scf=True):
23+
test_scf=True, keep_exxdiv=False):
2424
# cell
2525
cell = gto.Cell(
2626
atom=atom,
@@ -37,33 +37,39 @@ def _run_test(self, atom, a, basis, pseudo, ke_cutoff, kmesh, exxdiv,
3737
gmf = scf.KRHF(cell, kpts)
3838
gmf.exxdiv = exxdiv
3939
gmf.kernel()
40+
assert gmf.converged
4041
gcc = cc.KCCSD(gmf)
42+
gcc.keep_exxdiv = keep_exxdiv
4143
gcc.kernel()
42-
if test_scf:
43-
etrip = gcc.ccsd_t()
44-
ips_ref = gcc.ipccsd()
44+
assert gcc.converged
45+
etrip = gcc.ccsd_t()
46+
ips_ref = gcc.ipccsd()
4547

4648
# PW
4749
pmf = pw_helper.gtomf2pwmf(gmf)
4850
pmf.build()
4951
pmf.update_pp(pmf.mo_coeff)
5052
pmf.update_k(pmf.mo_coeff, pmf.mo_occ)
53+
assert pmf.converged
5154
pcc = pwscf.PWKRCCSD(pmf)
55+
pcc.keep_exxdiv = keep_exxdiv
5256
pcc.kernel()
53-
if test_scf:
54-
# Check CCSD(T)
55-
pcc.ccsd_t()
56-
assert(abs(gcc.e_corr - pcc.e_corr) < 1.e-6)
57-
pcc.ecut_eri = 15
57+
assert pcc.converged
58+
etrip_test = pcc.ccsd_t()
59+
# Check CCSD(T)
60+
assert_allclose(gcc.e_corr, pcc.e_corr, atol=1.e-6, rtol=0)
61+
assert_allclose(etrip, etrip_test, atol=1.e-5, rtol=0)
62+
63+
pcc.ecut_eri = 25
5864
pcc.kernel()
59-
if test_scf:
60-
# Check EOM-CCSD
61-
etrip_test = pcc.ccsd_t()
62-
ips_test = pcc.ipccsd()
63-
assert(abs(gcc.e_corr - pcc.e_corr) < 1.e-4)
64-
assert(abs(etrip - etrip_test) < 1.e-5)
65-
assert(np.max(np.abs(ips_ref[0] - ips_test[0])) < 3.e-5)
66-
assert(np.max(np.abs(ips_ref[1] - ips_test[1])) < 1.e-3)
65+
assert pcc.converged
66+
# Check EOM-CCSD
67+
etrip_test = pcc.ccsd_t()
68+
ips_test = pcc.ipccsd()
69+
assert_allclose(gcc.e_corr, pcc.e_corr, atol=1.e-4, rtol=0)
70+
assert_allclose(etrip, etrip_test, atol=1.e-5, rtol=0)
71+
assert_allclose(ips_ref[0], ips_test[0], atol=3.e-5, rtol=0)
72+
assert_allclose(ips_ref[1], ips_test[1], atol=1.e-3, rtol=0)
6773

6874
if test_scf:
6975
pwmf = pwscf.KRHF(cell, kpts, ecut_wf=20)
@@ -75,8 +81,10 @@ def _run_test(self, atom, a, basis, pseudo, ke_cutoff, kmesh, exxdiv,
7581
# pwmf.damp_type = "simple"
7682
pwmf.damp_factor = 0.0
7783
pwmf.kernel()
84+
assert pwmf.converged
7885
pwcc = pwscf.PWKRCCSD(pwmf)
7986
pwcc.kernel()
87+
assert pwcc.converged
8088
# Just to make sure the code stays consistent
8189
assert_allclose(pwcc.e_corr, -0.03234330656841895, atol=1.e-4, rtol=0)
8290
pwcc.ecut_eri = 15
@@ -87,16 +95,23 @@ def test_krccsd(self):
8795
ke_cutoff = 50
8896
basis = "gth-szv"
8997
pseudo = "gth-pade"
90-
exxdiv = "ewald"
9198
atom = "Li 0 0 0; Li 1.75 1.75 1.75"
9299
a = np.eye(3) * 3.5
93100

94101
# same occ per kpt
95102
kmesh = [2,1,1]
96-
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, exxdiv)
103+
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, "ewald")
104+
kmesh = [3,1,1]
105+
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, "ewald",
106+
test_scf=False, keep_exxdiv=True)
107+
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, None,
108+
test_scf=False, keep_exxdiv=True)
109+
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, None,
110+
test_scf=False)
111+
97112
# diff occ per kpt (i.e., needs padding)
98113
kmesh = [2,2,1]
99-
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, exxdiv,
114+
self._run_test(atom, a, basis, pseudo, ke_cutoff, kmesh, "ewald",
100115
test_scf=False)
101116

102117

‎pyscf/pbc/pwscf/test/test_krmp2.py‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919

2020

2121
class KnownValues(unittest.TestCase):
22-
def test_krmp2(self):
23-
kmesh = [2,1,1]
22+
def _check_krmp2(self, kmesh):
2423
ke_cutoff = 100
2524
pseudo = "gth-pade"
2625
exxdiv = "ewald"
@@ -40,18 +39,29 @@ def test_krmp2(self):
4039
kpts = cell.make_kpts(kmesh)
4140

4241
# GTO
43-
gmf = scf.KRHF(cell, kpts)
44-
gmf.exxdiv = exxdiv
45-
gmf.kernel()
46-
gmp = mp.KMP2(gmf)
47-
gmp.kernel()
42+
if (np.array(kmesh) == 1).all():
43+
gmf = scf.RHF(cell)
44+
gmf.exxdiv = exxdiv
45+
gmf.kernel()
46+
gmp = mp.MP2(gmf)
47+
gmp.kernel()
48+
else:
49+
gmf = scf.KRHF(cell, kpts)
50+
gmf.exxdiv = exxdiv
51+
gmf.kernel()
52+
gmp = mp.KMP2(gmf)
53+
gmp.kernel()
4854

4955
# PW
5056
pmf = pw_helper.gtomf2pwmf(gmf)
5157
pmp = kmp2.PWKRMP2(pmf)
5258
pmp.kernel()
5359
assert(abs(gmp.e_corr - pmp.e_corr) < 1.e-6)
5460

61+
def test_kump2(self):
62+
self._check_krmp2([1, 1, 1])
63+
self._check_krmp2([2, 1, 1])
64+
5565

5666
if __name__ == "__main__":
5767
unittest.main()

0 commit comments

Comments
 (0)