Skip to content

Commit 4e389d1

Browse files
authored
Refactor classes (pyscf#132)
* loose cupy requirements * support third-row transition metals * reverse grad/rhf * fixed an issue in unit test * reduce class inheritance from pyscf * linter * fixed a bug in unit test * move high-angular momentum to CPU * delete patch && add coverage * flake8 * be compatible with pyscf * replace numpy with cupy * skip mp2 to_gpu * refactor CCSDBase * update unittest workflow * install pytest cov
1 parent 65bc6a9 commit 4e389d1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1329
-678
lines changed

.coveragerc

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# .coveragerc to control coverage.py
2+
[run]
3+
branch = True
4+
omit = */tests/*
5+
disable_warnings = include-ignored
6+
7+
[report]
8+
# Regexes for lines to exclude from consideration
9+
exclude_lines =
10+
# Have to re-enable the standard pragma
11+
pragma: no cover
12+
13+
# Don't complain about missing debug-only code:
14+
def __repr__
15+
16+
# Don't complain if tests don't hit defensive assertion code:
17+
raise RuntimeError
18+
raise NotImplementedError
19+
pass
20+
21+
# Don't complain if non-runnable code isn't run:
22+
if 0:
23+
if __name__ == .__main__.:
24+
if sys.version_info.*:
25+
if DEBUG:
26+
except ImportError:
27+
28+
ignore_errors = True
29+
30+
[html]
31+
directory = coverage_html_report

.flake8

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ignore =
1515
E701, E731, E741, E275,
1616
F401, C901, W391, W503, W504, W291, W292, W293
1717

18-
exclude = test, tests, .git, __pycache__, build, dist, __init__.py .eggs, *.egg
18+
exclude = test, tests, .git, __pycache__, build, dist, __init__.py .eggs, *.egg, .coveragerc
1919
max-line-length = 160
2020

2121
per-file-ignores =

.github/workflows/unittest.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Install dependencies
2323
run: |
2424
python3 -m pip install --upgrade pip
25-
pip3 install flake8 pytest coverage gpu4pyscf-libxc-cuda11x
25+
pip3 install flake8 pytest coverage gpu4pyscf-libxc-cuda11x pytest-cov
2626
- name: Build GPU4PySCF
2727
run: |
2828
export CUDA_HOME=/usr/local/cuda
@@ -33,4 +33,4 @@ jobs:
3333
run: |
3434
echo $GITHUB_WORKSPACE
3535
export PYTHONPATH="${PYTHONPATH}:$GITHUB_WORKSPACE"
36-
coverage run -m pytest
36+
pytest --cov=$GITHUB_WORKSPACE

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ output/
1818
*.log
1919
*.h5
2020
**/qchem_input.in
21+
.coverage
2122
#*
2223
dockerfiles/manylinux/wheelhouse
2324
gpu4pyscf/lib/bin

gpu4pyscf/__init__.py

-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,3 @@
55
# monkey patch libxc reference due to a bug in nvcc
66
from pyscf.dft import libxc
77
libxc.__reference__ = 'unable to decode the reference due to https://github.com/NVIDIA/cuda-python/issues/29'
8-
9-
from gpu4pyscf.lib.utils import patch_cpu_kernel
10-
from gpu4pyscf.lib.cupy_helper import tag_array
11-
from pyscf import lib
12-
lib.tag_array = tag_array

gpu4pyscf/cc/ccsd_incore.py

+77-4
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,83 @@ def _make_eris_incore(mycc, mo_coeff=None):
529529
cupy.get_default_memory_pool().free_all_blocks()
530530
return eris
531531

532-
class CCSD(ccsd.CCSD):
533-
from gpu4pyscf.lib.utils import to_cpu, to_gpu, device
532+
class CCSDBase(lib.StreamObject):
533+
# attributes
534+
_keys = ccsd.CCSDBase._keys
535+
max_cycle = ccsd.CCSDBase.max_cycle
536+
conv_tol = ccsd.CCSDBase.conv_tol
537+
iterative_damping = ccsd.CCSDBase.iterative_damping
538+
conv_tol_normt = ccsd.CCSDBase.conv_tol_normt
539+
540+
diis = ccsd.CCSDBase.diis
541+
diis_space = ccsd.CCSDBase.diis_space
542+
diis_file = None
543+
diis_start_cycle = ccsd.CCSDBase.diis_start_cycle
544+
diis_start_energy_diff = ccsd.CCSDBase.diis_start_energy_diff
545+
546+
direct = ccsd.CCSDBase.direct
547+
async_io = None
548+
incore_complete = ccsd.CCSDBase.incore_complete
549+
cc2 = ccsd.CCSDBase.cc2
550+
callback = None
551+
552+
# functions
553+
__init__ = ccsd.CCSDBase.__init__
554+
ecc = ccsd.CCSDBase.ecc
555+
e_tot = ccsd.CCSDBase.e_tot
556+
nocc = ccsd.CCSDBase.nocc
557+
nmo = ccsd.CCSDBase.nmo
558+
reset = ccsd.CCSDBase.reset
559+
get_nocc = ccsd.CCSDBase.get_nocc
560+
get_nmo = ccsd.CCSDBase.get_nmo
561+
get_frozen_mask = ccsd.CCSDBase.get_frozen_mask
562+
get_e_hf = ccsd.CCSDBase.get_e_hf
563+
set_frozen = ccsd.CCSDBase.set_frozen
564+
dump_flags = ccsd.CCSDBase.dump_flags
565+
get_init_guess = ccsd.CCSDBase.get_init_guess
566+
init_amps = ccsd.CCSDBase.init_amps
567+
energy = ccsd.CCSDBase.energy
568+
_add_vvvv = ccsd.CCSDBase._add_vvvv
569+
update_amps = update_amps
570+
kernel = ccsd.CCSDBase.kernel
571+
_finalize = ccsd.CCSDBase._finalize
572+
as_scanner = ccsd.CCSDBase.as_scanner
573+
restore_from_diis_ = ccsd.CCSDBase.restore_from_diis_
574+
575+
solve_lambda = NotImplemented
576+
ccsd_t = NotImplemented
577+
ipccsd = NotImplemented
578+
eaccsd = NotImplemented
579+
eeccsd = NotImplemented
580+
eomee_ccsd_singlet = NotImplemented
581+
eomee_ccsd_triplet = NotImplemented
582+
eomsf_ccsd = NotImplemented
583+
eomip_method = NotImplemented
584+
eomea_method = NotImplemented
585+
eomee_method = NotImplemented
586+
make_rdm1 = NotImplemented
587+
make_rdm2 = NotImplemented
588+
ao2mo = _make_eris_incore
589+
run_diis = ccsd.CCSDBase.run_diis
590+
amplitudes_to_vector = ccsd.CCSDBase.amplitudes_to_vector
591+
vector_to_amplitudes = ccsd.CCSDBase.vector_to_amplitudes
592+
dump_chk = None
593+
density_fit = NotImplemented
594+
nuc_grad_method = NotImplemented
595+
596+
# to_cpu can be reused only when __init__ still takes mf
597+
def to_cpu(self):
598+
mf = self._scf.to_cpu()
599+
from importlib import import_module
600+
mod = import_module(self.__module__.replace('gpu4pyscf', 'pyscf'))
601+
cls = getattr(mod, self.__class__.__name__)
602+
obj = cls(mf)
603+
return obj
604+
605+
CCSDBase.ccsd = ccsd.CCSDBase.ccsd
606+
607+
class CCSD(CCSDBase):
608+
from gpu4pyscf.lib.utils import to_gpu, device
534609

535610
def __init__(self, mf, *args, **kwargs):
536611
if hasattr(mf, 'to_cpu'):
@@ -539,5 +614,3 @@ def __init__(self, mf, *args, **kwargs):
539614
lib.logger.warn(mf.mol, 'DF-CCSD not available. Run the standard CCSD.')
540615
ccsd.CCSD.__init__(self, mf, *args, **kwargs)
541616

542-
update_amps = update_amps
543-
ao2mo = _make_eris_incore

gpu4pyscf/cc/tests/test_ccsd.py

+9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ def test_ccsd_incore_kernel(self):
5353
self.assertAlmostEqual(abs(mcc.t1 - ref.t1).max(), 0, 6)
5454
self.assertAlmostEqual(abs(mcc.t2 - ref.t2).max(), 0, 6)
5555

56+
def test_to_gpu(self):
57+
mcc = ccsd_incore.CCSD(mf.to_gpu()).run()
58+
mcc = mcc.run()
59+
e_gpu = mcc.e_tot
60+
mcc = mcc.to_gpu()
61+
mcc = mcc.run()
62+
e_cpu = mcc.e_tot
63+
assert (e_cpu - e_gpu) < 1e-6
64+
5665

5766
if __name__ == '__main__':
5867
print("Full Tests for CCSD")

gpu4pyscf/df/df.py

+25-9
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,38 @@
3232
ALIGNED = getattr(__config__, 'ao_aligned', 32)
3333
LINEAR_DEP_TOL = 1e-7
3434

35-
class DF(df.DF):
35+
class DF(lib.StreamObject):
3636
from gpu4pyscf.lib.utils import to_gpu, device
3737

38-
_keys = {'intopt'}
38+
_keys = {'intopt', 'mol', 'auxmol'}
3939

4040
def __init__(self, mol, auxbasis=None):
41-
super().__init__(mol, auxbasis)
41+
self.mol = mol
42+
self.stdout = mol.stdout
43+
self.verbose = mol.verbose
44+
self.max_memory = mol.max_memory
45+
self._auxbasis = auxbasis
46+
4247
self.auxmol = None
4348
self.intopt = None
4449
self.nao = None
4550
self.naux = None
4651
self.cd_low = None
4752
self._cderi = None
53+
self._rsh_df = {}
54+
55+
@property
56+
def auxbasis(self):
57+
return self._auxbasis
58+
@auxbasis.setter
59+
def auxbasis(self, x):
60+
if self._auxbasis != x:
61+
self.reset()
62+
self._auxbasis = x
4863

4964
def to_cpu(self):
5065
from gpu4pyscf.lib.utils import to_cpu
5166
obj = to_cpu(self)
52-
del obj.intopt, obj.cd_low, obj.nao, obj.naux
5367
return obj.reset()
5468

5569
def build(self, direct_scf_tol=1e-14, omega=None):
@@ -167,15 +181,17 @@ def loop(self, blksize=None, unpack=True):
167181
buf = buf_prefetch
168182

169183
def reset(self, mol=None):
170-
'''
171-
reset object for scanner
172-
'''
173-
super().reset(mol)
184+
'''Reset mol and clean up relevant attributes for scanner mode'''
185+
if mol is not None:
186+
self.mol = mol
187+
self.auxmol = None
188+
self._cderi = None
189+
self._vjopt = None
190+
self._rsh_df = {}
174191
self.intopt = None
175192
self.nao = None
176193
self.naux = None
177194
self.cd_low = None
178-
self._cderi = None
179195
return self
180196

181197
get_ao_eri = get_eri = NotImplemented

gpu4pyscf/df/df_jk.py

+22-40
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _density_fit(mf, auxbasis=None, with_df=None, only_dfj=False):
8585
Examples:
8686
'''
8787

88-
assert isinstance(mf, scf.hf.SCF)
88+
assert isinstance(mf, hf.SCF)
8989

9090
if with_df is None:
9191
if isinstance(mf, dhf.UHF):
@@ -110,7 +110,8 @@ def _density_fit(mf, auxbasis=None, with_df=None, only_dfj=False):
110110
dfmf = _DFHF(mf, with_df, only_dfj)
111111
return lib.set_class(dfmf, (_DFHF, mf.__class__))
112112

113-
class _DFHF(df_jk._DFHF):
113+
from gpu4pyscf.lib import utils
114+
class _DFHF:
114115
'''
115116
Density fitting SCF class
116117
Attributes for density-fitting SCF:
@@ -121,8 +122,8 @@ class _DFHF(df_jk._DFHF):
121122
with_df : DF object
122123
Set mf.with_df = None to switch off density fitting mode.
123124
'''
124-
125-
from gpu4pyscf.lib.utils import to_cpu, to_gpu, device
125+
to_gpu = utils.to_gpu
126+
device = utils.device
126127

127128
_keys = {'rhoj', 'rhok', 'disp', 'screen_tol'}
128129

@@ -181,20 +182,23 @@ def nuc_grad_method(self):
181182
raise NotImplementedError()
182183

183184
def Hessian(self):
184-
from pyscf.dft.rks import KohnShamDFT
185-
if isinstance(self, scf.rhf.RHF):
186-
from gpu4pyscf.df.hessian import rhf, rks
185+
from gpu4pyscf.dft.rks import KohnShamDFT
186+
if isinstance(self, hf.RHF):
187187
if isinstance(self, KohnShamDFT):
188-
return rks.Hessian(self)
188+
from gpu4pyscf.df.hessian import rks as rks_hess
189+
return rks_hess.Hessian(self)
189190
else:
190-
return rhf.Hessian(self)
191-
else:
192-
from gpu4pyscf.df.hessian import uhf, uks
191+
from gpu4pyscf.df.hessian import rhf as rhf_hess
192+
return rhf_hess.Hessian(self)
193+
elif isinstance(self, uhf.UHF):
193194
if isinstance(self, KohnShamDFT):
194-
return uks.Hessian(self)
195+
from gpu4pyscf.df.hessian import uks as uks_hess
196+
return uks_hess.Hessian(self)
195197
else:
196-
return uhf.Hessian(self)
197-
198+
from gpu4pyscf.df.hessian import uhf as uhf_hess
199+
return uhf_hess.Hessian(self)
200+
else:
201+
raise NotImplementedError
198202
@property
199203
def auxbasis(self):
200204
return getattr(self.with_df, 'auxbasis', None)
@@ -207,7 +211,7 @@ def get_veff(self, mol=None, dm=None, dm_last=None, vhf_last=0, hermi=1):
207211
if dm is None: dm = self.make_rdm1()
208212

209213
# for DFT
210-
if isinstance(self, scf.hf.KohnShamDFT):
214+
if isinstance(self, rks.KohnShamDFT):
211215
if dm.ndim == 2:
212216
return rks.get_veff(self, dm=dm)
213217
elif dm.ndim == 3:
@@ -234,33 +238,11 @@ def get_veff(self, mol=None, dm=None, dm_last=None, vhf_last=0, hermi=1):
234238
else:
235239
raise NotImplementedError("Please check the dimension of the density matrix, it should not reach here.")
236240

237-
"""
238-
def energy_tot(self, dm=None, h1e=None, vhf=None):
239-
'''
240-
compute tot energy
241-
'''
242-
nuc = self.energy_nuc()
243-
e_tot = self.energy_elec(dm, h1e, vhf)[0] + nuc
244-
self.scf_summary['nuc'] = nuc.real
245-
return e_tot
246-
"""
247-
'''
248241
def to_cpu(self):
249242
obj = self.undo_df().to_cpu().density_fit()
250-
keys = dir(obj)
251-
obj.__dict__.update(self.__dict__)
252-
for key in set(dir(self)).difference(keys):
253-
print(key)
254-
delattr(obj, key)
255-
256-
for key in keys:
257-
val = getattr(obj, key)
258-
if isinstance(val, cupy.ndarray):
259-
setattr(obj, key, cupy.asnumpy(val))
260-
elif hasattr(val, 'to_cpu'):
261-
setattr(obj, key, val.to_cpu())
262-
return obj
263-
'''
243+
print(type(obj))
244+
return utils.to_cpu(self, obj)
245+
264246

265247
def get_jk(dfobj, dms_tag, hermi=1, with_j=True, with_k=True, direct_scf_tol=1e-14, omega=None):
266248
'''

gpu4pyscf/df/grad/rhf.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pyscf import lib, scf, gto
2222
from gpu4pyscf.df import int3c2e
2323
from gpu4pyscf.lib.cupy_helper import print_mem_info, tag_array, unpack_tril, contract, load_library, take_last2d
24-
from gpu4pyscf.grad.rhf import grad_elec
24+
from gpu4pyscf.grad import rhf as rhf_grad
2525
from gpu4pyscf import __config__
2626
from gpu4pyscf.lib import logger
2727

@@ -237,11 +237,19 @@ def get_jk(mf_grad, mol=None, dm0=None, hermi=0, with_j=True, with_k=True, omega
237237
return vj, vk, vjaux, vkaux
238238

239239

240-
class Gradients(rhf.Gradients):
241-
from gpu4pyscf.lib.utils import to_cpu, to_gpu, device
240+
class Gradients(rhf_grad.Gradients):
241+
from gpu4pyscf.lib.utils import to_gpu, device
242+
243+
_keys = {'with_df', 'auxbasis_response'}
244+
def __init__(self, mf):
245+
# Whether to include the response of DF auxiliary basis when computing
246+
# nuclear gradients of J/K matrices
247+
self.auxbasis_response = True
248+
rhf_grad.Gradients.__init__(self, mf)
242249

243250
get_jk = get_jk
244-
grad_elec = grad_elec
251+
grad_elec = rhf_grad.grad_elec
252+
check_sanity = NotImplemented
245253

246254
def get_j(self, mol=None, dm=None, hermi=0):
247255
vj, _, vjaux, _ = self.get_jk(mol, dm, with_k=False)
@@ -267,3 +275,5 @@ def extra_force(self, atom_id, envs):
267275
return envs['dvhf'].aux[atom_id]
268276
else:
269277
return 0
278+
279+
Grad = Gradients

0 commit comments

Comments
 (0)