Skip to content

Commit 67c243d

Browse files
authored
DFT Hessian grid response (#533)
* DFT Hessian Fock 1st derivative grid response. Only correct result, with horrible memory usage and performance * LDA Hessian energy 2nd derivative grid response. Only correct result, with horrible memory usage and performance * GGA Hessian energy 2nd derivative grid response. Only correct result, with horrible memory usage and performance * MGGA Hessian energy 2nd derivative grid response + 1st round of cleanup. Still only correct result * VV10 Hessian energy 2nd derivative grid response. All grid responses are correct now. Need optimization * Add tests for individual terms of hessian grid response. There's some problem with overall hessian compared to numerical hessian * Second round of cleanup, remove natm*natm*ngrids memory requirement for rho, nabla rho and tau second derivative, remove nao*ngrids operation and some redundant operations in vv10, fix some linter errors * Third round of cleanup, remove all einsum with more than two matrix inputs in energy second derivative terms * Fourth round of cleanup, remove all einsum with more than two matrix inputs in Fock first derivative terms * Split the memory estimation of Becke weight second derivative from other terms, because that is the only remaining natm*natm*ngrids term * Bug fix to remove zero-sized array operations in grid response * Count the maximum amount of memory per grid used in each operation. Certainly overcounted. * Fix xctype==HF test * Turn on large_exponent rks tests, confirm the problem is from grid response * Small bug * Move numerical derivative routine to test files, add log info for grid response * Fix multigpu illegal memory bug * Remove extra computation in d2rho/dAdB grid response * Merge common computation in d2rho/dAdB grid response * Apply similar optimization to d2e and dFock terms * Bug fix on two H test when HOMO LUMO are degenerate * Minor bug fix in previous commit * Minor bug fix in previous commit, again * Accelerate Becke weight second derivative kernel, by moving P_B computation out from the kernel * Another minor improvement for Becke weight second derivative
1 parent 183106a commit 67c243d

File tree

10 files changed

+2950
-423
lines changed

10 files changed

+2950
-423
lines changed

gpu4pyscf/df/hessian/rks.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,7 @@ def partial_hess_elec(hessobj, mo_energy=None, mo_coeff=None, mo_occ=None,
6464

6565
max_memory = None
6666
t1 = log.timer_debug1('computing ej, ek', *t1)
67-
veff_diag = rks_hess._get_vxc_diag(hessobj, mo_coeff, mo_occ, max_memory)
68-
t1 = log.timer_debug1('computing veff_diag', *t1)
69-
aoslices = mol.aoslice_by_atom()
70-
vxc_dm = rks_hess._get_vxc_deriv2(hessobj, mo_coeff, mo_occ, max_memory)
71-
t1 = log.timer_debug1('computing veff_deriv2', *t1)
72-
for i0, ia in enumerate(atmlst):
73-
shl0, shl1, p0, p1 = aoslices[ia]
74-
veff_dm = vxc_dm[ia]
75-
de2[i0,i0] += contract('xypq,pq->xy', veff_diag[:,:,p0:p1], dm0[p0:p1])*2
76-
for j0, ja in enumerate(atmlst[:i0+1]):
77-
q0, q1 = aoslices[ja][2:]
78-
#:contract('xypq,pq->xy', veff[:,:,q0:q1], dm0[q0:q1])*2
79-
de2[i0,j0] += 2.0*cupy.sum(veff_dm[:,:,q0:q1], axis=2)
80-
for j0 in range(i0):
81-
de2[j0,i0] = de2[i0,j0].T
82-
67+
de2 += rks_hess._get_exc_deriv2(hessobj, mo_coeff, mo_occ, dm0, max_memory)
8368
if mf.do_nlc():
8469
de2 += _get_enlc_deriv2(hessobj, mo_coeff, mo_occ, max_memory)
8570

gpu4pyscf/dft/numint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1871,7 +1871,7 @@ def _block_loop(ni, mol, grids, nao=None, deriv=0, max_memory=2000,
18711871
non0tab: dummy argument for compatibility with PySCF
18721872
blksize: if not given, it will be estimated with avail GPU memory.
18731873
buf: dummy argument for compatibility with PySCF
1874-
grid_range: loop [grid_start, grid_end] in grids only.
1874+
grid_range: loop [grid_start, grid_end] in grids only. TODO: Henry 20251006 believes these parameters are not respected.
18751875
'''
18761876
log = logger.new_logger(mol)
18771877
if grids.coords is None:

gpu4pyscf/hessian/rhf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ def solve_mo1(mf, mo_energy, mo_coeff, mo_occ, h1mo,
629629
e_ai = 1 / (e_a[:,None] + level_shift - e_i)
630630
nvir, nocc = e_ai.shape
631631

632+
if cupy.any(cp.isinf(e_ai)) or cupy.any(cp.isnan(e_ai)):
633+
raise ValueError(f"e_ai = {e_ai} contains inf or nan, likely because HOMO-LUMO gap is zero.")
634+
632635
mocc = mo_coeff[:,occidx]
633636
nao, nmo = mo_coeff.shape
634637
natm = mol.natm

gpu4pyscf/hessian/rks.py

Lines changed: 1909 additions & 239 deletions
Large diffs are not rendered by default.

gpu4pyscf/hessian/tests/test_large_exponent.py

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
# limitations under the License.
1414

1515
import unittest
16+
import pytest
1617
import numpy as np
1718
import cupy as cp
1819
import pyscf
1920
from gpu4pyscf import scf, dft
2021

2122
def setUpModule():
22-
global mol_minimal_one_atom, mol_minimal_two_atom, auxbasis_minimal_good, auxbasis_minimal_bad #, mol_real
23+
global mol_minimal_one_atom, mol_minimal_two_atom, auxbasis_minimal_good, auxbasis_minimal_bad
2324
mol_minimal_one_atom = pyscf.M(
2425
atom = """
2526
He 100 200 300
@@ -52,27 +53,12 @@ def setUpModule():
5253
2.0 1.0
5354
"""
5455

55-
# mol_real = pyscf.M(
56-
# atom = """
57-
# C 0.000000 0.000000 0.000000
58-
# Br 0.000000 0.000000 1.940000
59-
# H 1.027662 0.000000 -0.363333
60-
# H -0.513831 0.889981 -0.363333
61-
# H -0.513831 -0.889981 -0.363333
62-
# """,
63-
# basis = "def2-svp",
64-
# verbose = 4,
65-
# output='/dev/null',
66-
# )
67-
6856
def tearDownModule():
69-
global mol_minimal_one_atom, mol_minimal_two_atom #, mol_real
57+
global mol_minimal_one_atom, mol_minimal_two_atom
7058
mol_minimal_one_atom.stdout.close()
7159
del mol_minimal_one_atom
7260
mol_minimal_two_atom.stdout.close()
7361
del mol_minimal_two_atom
74-
# mol_real.stdout.close()
75-
# del mol_real
7662

7763
class KnownValues(unittest.TestCase):
7864
def test_hessian_large_exp_one_atom_rhf(self):
@@ -170,64 +156,81 @@ def test_hessian_large_exp_two_atom_rhf_density_fit_bad_auxbasis(self):
170156

171157
assert np.max(np.abs(translation_invariance)) < 1e-4
172158

173-
# TODO: Fix DFT Hessian
174-
175-
# def test_hessian_large_exp_one_atom_rks(self):
176-
# mf = dft.RKS(mol_minimal_one_atom, xc = "LDA")
177-
# mf.grids.atom_grid = (200,194)
178-
# mf.conv_tol = 1e-10
159+
def test_hessian_large_exp_one_atom_rks(self):
160+
mf = dft.RKS(mol_minimal_one_atom, xc = "r2SCAN")
161+
mf.grids.atom_grid = (50,194)
162+
mf.conv_tol = 1e-10
179163

180-
# mf.kernel()
181-
# assert mf.converged
164+
mf.kernel()
165+
assert mf.converged
182166

183-
# hobj = mf.Hessian()
184-
# hessian = hobj.kernel()
167+
hobj = mf.Hessian()
168+
hobj.grid_response = True
169+
hessian = hobj.kernel()
185170

186-
# natm = mf.mol.natm
187-
# hessian = hessian.transpose(0,2,1,3)
188-
# hessian = hessian.reshape((natm * 3, natm * 3))
189-
# translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
171+
natm = mf.mol.natm
172+
hessian = hessian.transpose(0,2,1,3)
173+
hessian = hessian.reshape((natm * 3, natm * 3))
174+
translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
190175

191-
# # Zero hessian for only one atom
192-
# assert np.max(np.abs(hessian)) < 1e-3
193-
# assert np.max(np.abs(translation_invariance)) < 1e-3
176+
# Zero hessian for only one atom
177+
assert np.max(np.abs(hessian)) < 1e-4
178+
assert np.max(np.abs(translation_invariance)) < 1e-4
194179

195-
# def test_hessian_large_exp_two_atom_rks(self):
196-
# mf = dft.RKS(mol_minimal_two_atom, xc = "LDA")
197-
# mf.grids.atom_grid = (200,194)
198-
# mf.conv_tol = 1e-10
180+
def test_hessian_large_exp_two_atom_rks(self):
181+
mf = dft.RKS(mol_minimal_two_atom, xc = "wB97X")
182+
mf.grids.atom_grid = (50,194)
183+
mf.conv_tol = 1e-10
184+
mf.level_shift = 0.001
199185

200-
# mf.kernel()
201-
# assert mf.converged
186+
mf.kernel()
187+
assert mf.converged
202188

203-
# hobj = mf.Hessian()
204-
# hessian = hobj.kernel()
189+
hobj = mf.Hessian()
190+
hobj.grid_response = True
191+
hessian = hobj.kernel()
205192

206-
# natm = mf.mol.natm
207-
# hessian = hessian.transpose(0,2,1,3)
208-
# hessian = hessian.reshape((natm * 3, natm * 3))
209-
# translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
193+
natm = mf.mol.natm
194+
hessian = hessian.transpose(0,2,1,3)
195+
hessian = hessian.reshape((natm * 3, natm * 3))
196+
translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
210197

211-
# assert np.max(np.abs(translation_invariance)) < 1e-3
198+
assert np.max(np.abs(translation_invariance)) < 1e-4
212199

213-
# def test_hessian_large_exp_methylbromide_rks(self):
214-
# mf = dft.RKS(mol_real, xc = "wB97X")
215-
# mf.grids.atom_grid = (99,590)
216-
# mf = mf.density_fit(auxbasis = "def2-universal-jkfit")
217-
# mf.conv_tol = 1e-10
200+
@pytest.mark.skip("Too slow, functionality covered by corresponding tests above")
201+
def test_hessian_large_exp_methylbromide_rks(self):
202+
mol_real = pyscf.M(
203+
atom = """
204+
C 0.000000 0.000000 0.000000
205+
Br 0.000000 0.000000 1.940000
206+
H 1.027662 0.000000 -0.363333
207+
H -0.513831 0.889981 -0.363333
208+
H -0.513831 -0.889981 -0.363333
209+
""",
210+
basis = "def2-svp",
211+
verbose = 4,
212+
output='/dev/null',
213+
)
214+
215+
mf = dft.RKS(mol_real, xc = "wB97X")
216+
mf.grids.atom_grid = (99,590)
217+
mf = mf.density_fit(auxbasis = "def2-universal-jkfit")
218+
mf.conv_tol = 1e-10
218219

219-
# mf.kernel()
220-
# assert mf.converged
220+
mf.kernel()
221+
assert mf.converged
221222

222-
# hobj = mf.Hessian()
223-
# hessian = hobj.kernel()
223+
hobj = mf.Hessian()
224+
hobj.auxbasis_response = 2
225+
hobj.grid_response = True
226+
hessian = hobj.kernel()
224227

225-
# natm = mf.mol.natm
226-
# hessian = hessian.transpose(0,2,1,3)
227-
# hessian = hessian.reshape((natm * 3, natm * 3))
228-
# translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
228+
natm = mf.mol.natm
229+
hessian = hessian.transpose(0,2,1,3)
230+
hessian = hessian.reshape((natm * 3, natm * 3))
231+
translation_invariance = np.sum(hessian, axis = 0).reshape(natm, 3)
229232

230-
# assert np.max(np.abs(translation_invariance)) < 1e-10
233+
assert np.max(np.abs(translation_invariance)) < 1e-7
231234

232235
if __name__ == "__main__":
233236
print("Edge Case Tests for Hessian Calculation with Large Exponent in Atomic Orbitals")

0 commit comments

Comments
 (0)