Skip to content

Commit

Permalink
solvent model for uhf, with_lapl=False (pyscf#102)
Browse files Browse the repository at this point in the history
* use solvent model for unrestricted

* set with_lapl=False

* v0.7.1

* fixed unit test
  • Loading branch information
wxj6000 authored Feb 7, 2024
1 parent c64e2b4 commit 212fa37
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 41 deletions.
2 changes: 1 addition & 1 deletion gpu4pyscf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from . import lib, grad, hessian, solvent, scf, dft

__version__ = '0.7.0'
__version__ = '0.7.1'

# monkey patch libxc reference due to a bug in nvcc
from pyscf.dft import libxc
Expand Down
4 changes: 2 additions & 2 deletions gpu4pyscf/df/tests/test_df_grad.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ class KnownValues(unittest.TestCase):

def test_grad_with_grids_response(self):
print("-----testing DF DFT gradient with grids response----")
_check_grad(mol_sph, grid_response=True)
_check_grad(mol_sph, grid_response=True, disp=None)

def test_grad_without_grids_response(self):
print('-----testing DF DFT gradient without grids response----')
_check_grad(mol_sph, grid_response=False)
_check_grad(mol_sph, grid_response=False, disp=None)

def test_grad_lda(self):
print("-----LDA testing-------")
Expand Down
51 changes: 29 additions & 22 deletions gpu4pyscf/dft/numint.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,24 +481,27 @@ def nr_rks(ni, mol, grids, xc_code, dms, relativity=0, hermi=1,
ao_deriv = 0
else:
ao_deriv = 1
with_lapl = MGGA_DENSITY_LAPL
ngrids = grids.weights.size
if xctype == 'LDA':
rho_tot = cupy.empty([nset,1,ngrids])
elif xctype == 'GGA':
rho_tot = cupy.empty([nset,4,ngrids])
else:
rho_tot = cupy.empty([nset,6,ngrids])

if with_lapl:
rho_tot = cupy.empty([nset,6,ngrids])
else:
rho_tot = cupy.empty([nset,5,ngrids])
p0 = p1 = 0
t1 = t0 = log.init_timer()
for ao_mask, idx, weight, _ in ni.block_loop(mol, grids, nao, ao_deriv):
p1 = p0 + weight.size
for i in range(nset):
if mo_coeff is None:
rho_tot[i,:,p0:p1] = eval_rho(mol, ao_mask, dms[i][np.ix_(idx,idx)], xctype=xctype, hermi=1)
rho_tot[i,:,p0:p1] = eval_rho(mol, ao_mask, dms[i][np.ix_(idx,idx)], xctype=xctype, hermi=1, with_lapl=with_lapl)
else:
mo_coeff_mask = mo_coeff[idx,:]
rho_tot[i,:,p0:p1] = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, xctype)
rho_tot[i,:,p0:p1] = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, xctype, with_lapl)
p0 = p1
t1 = log.timer_debug2('eval rho slice', *t1)
t0 = log.timer_debug1('eval rho', *t0)
Expand Down Expand Up @@ -608,17 +611,18 @@ def nr_uks(ni, mol, grids, xc_code, dms, relativity=0, hermi=1,
ao_deriv = 0
else:
ao_deriv = 1
with_lapl = MGGA_DENSITY_LAPL

for ao_mask, idx, weight, _ in ni.block_loop(mol, grids, nao, ao_deriv):
for i in range(nset):
t0 = log.init_timer()
if mo_coeff is None:
rho_a = eval_rho(mol, ao_mask, dma[i][np.ix_(idx,idx)], xctype=xctype, hermi=1)
rho_b = eval_rho(mol, ao_mask, dmb[i][np.ix_(idx,idx)], xctype=xctype, hermi=1)
rho_a = eval_rho(mol, ao_mask, dma[i][np.ix_(idx,idx)], xctype=xctype, hermi=1, with_lapl=with_lapl)
rho_b = eval_rho(mol, ao_mask, dmb[i][np.ix_(idx,idx)], xctype=xctype, hermi=1, with_lapl=with_lapl)
else:
mo_coeff_mask = mo_coeff[:, idx,:]
rho_a = eval_rho2(mol, ao_mask, mo_coeff_mask[0], mo_occ[0], None, xctype)
rho_b = eval_rho2(mol, ao_mask, mo_coeff_mask[1], mo_occ[1], None, xctype)
rho_a = eval_rho2(mol, ao_mask, mo_coeff_mask[0], mo_occ[0], None, xctype, with_lapl)
rho_b = eval_rho2(mol, ao_mask, mo_coeff_mask[1], mo_occ[1], None, xctype, with_lapl)

rho = cupy.stack([rho_a, rho_b], axis=0)
exc, vxc = ni.eval_xc_eff(xc_code, rho, deriv=1, xctype=xctype)[:2]
Expand Down Expand Up @@ -699,6 +703,7 @@ def get_rho(ni, mol, dm, grids, max_memory=2000, verbose=None):
dm = coeff @ cupy.asarray(dm) @ coeff.T
if mo_coeff is not None:
mo_coeff = coeff @ mo_coeff
with_lapl = MGGA_DENSITY_LAPL

ngrids = grids.weights.size
rho = cupy.empty(ngrids)
Expand All @@ -707,10 +712,10 @@ def get_rho(ni, mol, dm, grids, max_memory=2000, verbose=None):
for ao_mask, idx, weight, _ in ni.block_loop(mol, grids, nao, 0):
p1 = p0 + weight.size
if mo_coeff is None:
rho[p0:p1] = eval_rho(mol, ao_mask, dm[np.ix_(idx,idx)], xctype='LDA', hermi=1)
rho[p0:p1] = eval_rho(mol, ao_mask, dm[np.ix_(idx,idx)], xctype='LDA', hermi=1, with_lapl=with_lapl)
else:
mo_coeff_mask = mo_coeff[idx,:]
rho[p0:p1] = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, 'LDA')
rho[p0:p1] = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, 'LDA', with_lapl)
p0 = p1
t1 = log.timer_debug2('eval rho slice', *t1)
t0 = log.timer_debug1('eval rho', *t0)
Expand Down Expand Up @@ -747,6 +752,7 @@ def nr_rks_fxc(ni, mol, grids, xc_code, dm0=None, dms=None, relativity=0, hermi=
ao_deriv = 0
else:
ao_deriv = 1
with_lapl = MGGA_DENSITY_LAPL
p0 = 0
p1 = 0
t1 = t0 = log.init_timer()
Expand All @@ -768,7 +774,7 @@ def nr_rks_fxc(ni, mol, grids, xc_code, dm0=None, dms=None, relativity=0, hermi=
# slow version
rho1 = []
for i in range(nset):
rho_tmp = eval_rho(opt.mol, ao, dms[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=False)
rho_tmp = eval_rho(opt.mol, ao, dms[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=with_lapl)
rho1.append(rho_tmp)
rho1 = cupy.stack(rho1, axis=0)
t1 = log.timer_debug2('eval rho', *t1)
Expand Down Expand Up @@ -867,6 +873,7 @@ def nr_uks_fxc(ni, mol, grids, xc_code, dm0=None, dms=None, relativity=0, hermi=
ao_deriv = 0
else:
ao_deriv = 1
with_lapl = MGGA_DENSITY_LAPL
p0 = 0
p1 = 0
for ao, mask, weights, coords in ni.block_loop(opt.mol, grids, nao, ao_deriv):
Expand All @@ -887,16 +894,16 @@ def nr_uks_fxc(ni, mol, grids, xc_code, dm0=None, dms=None, relativity=0, hermi=
c0_b = contract('nig,io->nog', ao, occ_coeff_b_mask)

if with_mocc:
rho1a = eval_rho4(opt.mol, ao, c0_a, mo1a[:,mask], xctype=xctype, with_lapl=False)
rho1b = eval_rho4(opt.mol, ao, c0_b, mo1b[:,mask], xctype=xctype, with_lapl=False)
rho1a = eval_rho4(opt.mol, ao, c0_a, mo1a[:,mask], xctype=xctype, with_lapl=with_lapl)
rho1b = eval_rho4(opt.mol, ao, c0_b, mo1b[:,mask], xctype=xctype, with_lapl=with_lapl)
else:
# slow version
rho1a = []
rho1b = []
for i in range(nset):
rho_tmp = eval_rho(opt.mol, ao, dma[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=False)
rho_tmp = eval_rho(opt.mol, ao, dma[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=with_lapl)
rho1a.append(rho_tmp)
rho_tmp = eval_rho(opt.mol, ao, dmb[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=False)
rho_tmp = eval_rho(opt.mol, ao, dmb[i][np.ix_(mask,mask)], xctype=xctype, hermi=hermi, with_lapl=with_lapl)
rho1b.append(rho_tmp)
rho1a = cupy.stack(rho1a, axis=0)
rho1b = cupy.stack(rho1b, axis=0)
Expand Down Expand Up @@ -1001,17 +1008,17 @@ def nr_nlc_vxc(ni, mol, grids, xc_code, dms, relativity=0, hermi=1,

if mo_coeff is not None:
mo_coeff = coeff @ mo_coeff

with_lapl = MGGA_DENSITY_LAPL
ao_deriv = 1
vvrho = []
for ao, idx, weight, coords \
in ni.block_loop(mol, grids, nao, ao_deriv, max_memory=max_memory):
#rho = eval_rho(opt.mol, ao, dms[0][np.ix_(mask,mask)], xctype='GGA', hermi=1)
if mo_coeff is None:
rho = eval_rho(mol, ao, dms[0][np.ix_(idx,idx)], xctype='GGA', hermi=1)
rho = eval_rho(mol, ao, dms[0][np.ix_(idx,idx)], xctype='GGA', hermi=1, with_lapl=with_lapl)
else:
mo_coeff_mask = mo_coeff[idx,:]
rho = eval_rho2(mol, ao, mo_coeff_mask, mo_occ, None, 'GGA')
rho = eval_rho2(mol, ao, mo_coeff_mask, mo_occ, None, 'GGA', with_lapl)
vvrho.append(rho)

rho = cupy.hstack(vvrho)
Expand Down Expand Up @@ -1063,7 +1070,7 @@ def cache_xc_kernel(ni, mol, grids, xc_code, mo_coeff, mo_occ, spin=0,
raise NotImplementedError('NLC')
else:
ao_deriv = 0

with_lapl = MGGA_DENSITY_LAPL
opt = getattr(ni, 'gdftopt', None)
if opt is None:
ni.build(mol, grids.coords)
Expand All @@ -1078,7 +1085,7 @@ def cache_xc_kernel(ni, mol, grids, xc_code, mo_coeff, mo_occ, spin=0,
t1 = t0 = log.init_timer()
for ao_mask, idx, weight, _ in ni.block_loop(mol, grids, nao, ao_deriv):
mo_coeff_mask = mo_coeff[idx,:]
rho_slice = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, xctype)
rho_slice = eval_rho2(mol, ao_mask, mo_coeff_mask, mo_occ, None, xctype, with_lapl)
rho.append(rho_slice)
t1 = log.timer_debug2('eval rho slice', *t1)
rho = cupy.hstack(rho)
Expand All @@ -1090,8 +1097,8 @@ def cache_xc_kernel(ni, mol, grids, xc_code, mo_coeff, mo_occ, spin=0,
t1 = t0 = log.init_timer()
for ao_mask, idx, weight, _ in ni.block_loop(mol, grids, nao, ao_deriv):
mo_coeff_mask = mo_coeff[:,idx,:]
rhoa_slice = eval_rho2(mol, ao_mask, mo_coeff_mask[0], mo_occ[0], None, xctype)
rhob_slice = eval_rho2(mol, ao_mask, mo_coeff_mask[1], mo_occ[1], None, xctype)
rhoa_slice = eval_rho2(mol, ao_mask, mo_coeff_mask[0], mo_occ[0], None, xctype, with_lapl)
rhob_slice = eval_rho2(mol, ao_mask, mo_coeff_mask[1], mo_occ[1], None, xctype, with_lapl)
rhoa.append(rhoa_slice)
rhob.append(rhob_slice)
t1 = log.timer_debug2('eval rho in fxc', *t1)
Expand Down
6 changes: 2 additions & 4 deletions gpu4pyscf/dft/tests/test_numint.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,8 @@ def _check_rks_fxc(self, xc, hermi=1):
ni = NumInt()
rho, vxc, fxc = ni.cache_xc_kernel(mol, grids_gpu, xc, cupy.asarray(mo_coeff[0]), cupy.asarray(mo_occ[0]), spin)
v = ni.nr_rks_fxc(mol, grids_gpu, xc, dms=t1, fxc=fxc, hermi=hermi)
if xc == MGGA_M06:
assert cupy.allclose(rho[[0,1,2,3,5]], rho0[[0,1,2,3,5]])
else:
assert cupy.allclose(rho, rho0)

assert cupy.linalg.norm(rho - cupy.asarray(rho0)) < 1e-6 * cupy.linalg.norm(rho)
assert cupy.linalg.norm(vxc - cupy.asarray(vxc0)) < 1e-6 * cupy.linalg.norm(vxc)
assert cupy.linalg.norm(fxc - cupy.asarray(fxc0)) < 1e-6 * cupy.linalg.norm(fxc)
assert cupy.allclose(v, vref)
Expand Down Expand Up @@ -144,6 +141,7 @@ def _check_uks_fxc(self, xc, hermi=1):
mol, grids_cpu, xc, dm0=dm0, dms=t1, rho0=rho_ref, vxc=vxc_ref, fxc=fxc_ref, hermi=hermi)
vxc_ref = np.asarray(vxc_ref)
rho_ref = np.asarray(rho_ref)

assert cupy.linalg.norm(rho - cupy.asarray(rho_ref)) < 1e-6 * cupy.linalg.norm(rho)
assert cupy.linalg.norm(vxc - cupy.asarray(vxc_ref)) < 1e-6 * cupy.linalg.norm(vxc)
assert cupy.linalg.norm(fxc - cupy.asarray(fxc_ref)) < 1e-6 * cupy.linalg.norm(fxc)
Expand Down
2 changes: 1 addition & 1 deletion gpu4pyscf/grad/rks.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def get_nlc_vxc(ni, mol, grids, xc_code, dms, relativity=0, hermi=1,
coeff = cupy.asarray(opt.coeff)
nao, nao0 = coeff.shape
dms = cupy.asarray(dms)
dms = [cupy.einsum('pi,ij,qj->pq', coeff, dm, coeff)
dms = [coeff @ dm @ coeff.T
for dm in dms.reshape(-1,nao0,nao0)]
mo_coeff = coeff @ mo_coeff
nset = len(dms)
Expand Down
11 changes: 3 additions & 8 deletions gpu4pyscf/lib/tests/test_to_gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,39 +96,34 @@ def test_df_RHF(self):
def test_df_b3lyp(self):
mf = rks.RKS(mol, xc='b3lyp').density_fit().to_gpu()
e_tot = mf.to_gpu().kernel()
print('DF b3lyp energy:', e_tot)
assert numpy.abs(e_tot - -75.31295618175646) < 1e-7

mf = rks.RKS(mol, xc='b3lyp').density_fit().run()
gobj = mf.nuc_grad_method().to_gpu()
g = gobj.kernel()
print('DF b3lyp force:', lib.fp(g))
assert numpy.abs(lib.fp(g) - -0.04079190644707999) < 1e-7

mf = rks.RKS(mol, xc='b3lyp').density_fit().run()
hobj = mf.Hessian().to_gpu()
h = hobj.kernel()
print('DF b3lyp hessian:', lib.fp(h))
assert numpy.abs(lib.fp(h) - 2.1527804103141848) < 1e-7

@pytest.mark.skipif(pyscf_24, reason='requires pyscf 2.5 or higher')
def test_df_RKS(self):
mf = rks.RKS(mol, xc='wb97x').density_fit().to_gpu()
e_tot = mf.to_gpu().kernel()
print('DF wb97x energy:', e_tot)
assert numpy.abs(e_tot - -75.30717654021076) < 1e-7

mf = rks.RKS(mol, xc='wb97x').density_fit().run()
gobj = mf.nuc_grad_method().to_gpu()
g = gobj.kernel()
print('DF wb97x force:', lib.fp(g))
assert numpy.abs(lib.fp(g) - -0.043401172511220595) < 1e-7
g -= g.sum(axis=0)/len(g)
assert numpy.abs(lib.fp(g) - -0.034343799164131) < 1e-5

mf = rks.RKS(mol, xc='wb97x').density_fit().run()
hobj = mf.Hessian().to_gpu()
h = hobj.kernel()
print('DF wb97x hessian:', lib.fp(h))
assert numpy.abs(lib.fp(h) - 2.187025544697092) < 1e-7
assert numpy.abs(lib.fp(h) - 2.187025544697092) < 1e-4

# TODO: solvent

Expand Down
5 changes: 3 additions & 2 deletions gpu4pyscf/solvent/pcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def pcm_for_scf(mf, solvent_obj=None, dm=None):
# Inject PCM to SCF, TODO: add it to other methods later
from gpu4pyscf import scf
scf.hf.RHF.PCM = pcm_for_scf

scf.uhf.UHF.PCM = pcm_for_scf
# TABLE II, J. Chem. Phys. 122, 194110 (2005)
XI = {
6: 4.84566077868,
Expand Down Expand Up @@ -313,7 +313,8 @@ def _get_vind(self, dms):

nao = dms.shape[-1]
dms = dms.reshape(-1,nao,nao)

if dms.shape[0] == 2:
dms = (dms[0] + dms[1]).reshape(-1,nao,nao)
K = self._intermediates['K']
R = self._intermediates['R']
v_grids_e = self._get_v(dms)
Expand Down
2 changes: 1 addition & 1 deletion gpu4pyscf/solvent/smd.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def smd_for_scf(mf, solvent_obj=None, dm=None):
# Inject PCM to SCF, TODO: add it to other methods later
from gpu4pyscf import scf
scf.hf.RHF.SMD = smd_for_scf

scf.uhf.UHF.SMD = smd_for_scf
hartree2kcal = 627.5
# see https://pubs.acs.org/doi/epdf/10.1021/jp810292n

Expand Down
10 changes: 10 additions & 0 deletions gpu4pyscf/solvent/tests/test_pcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ def test_SSVPE(self):
print(f"Energy error in SS(V)PE: {numpy.abs(e_tot - -74.9689577454)}")
assert numpy.abs(e_tot - -74.9689577454) < 1e-9

def test_uhf(self):
cm = pcm.PCM(mol)
cm.eps = epsilon
cm.verbose = 0
cm.lebedev_order = 29
cm.method = 'IEF-PCM'
mf = scf.UHF(mol).PCM(cm)
e_tot = mf.kernel()
assert numpy.abs(e_tot - -74.96901113434953) < 1e-9

if __name__ == "__main__":
print("Full Tests for PCMs")
unittest.main()
7 changes: 7 additions & 0 deletions gpu4pyscf/solvent/tests/test_smd.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ def test_smd_water(self):
e_tot = mf.kernel()
assert numpy.abs(e_tot - -76.0756052903) < 2e-4

def test_uhf(self):
mf = scf.UHF(mol)
mf = mf.SMD()
mf.with_solvent.solvent = 'water'
mf.with_solvent.sasa_ng = 590
e_tot = mf.kernel()
assert numpy.abs(e_tot - -76.07550951172617) < 2e-4
# TODO: add more test for other molecules

if __name__ == "__main__":
Expand Down

0 comments on commit 212fa37

Please sign in to comment.