-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nuclear Gradients #124
base: master
Are you sure you want to change the base?
Nuclear Gradients #124
Changes from all commits
db8001b
5e2e7b3
a72f71d
7e5c14d
87599e7
678a6d4
8fb606c
999ef3f
8fccd86
1ff8973
6ba9bc5
854d459
299ee5b
f718601
60444a7
65bfcc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
|
||
from libadcc import HartreeFockProvider | ||
from adcc.misc import cached_property | ||
from adcc.gradients import GradientComponents | ||
|
||
import psi4 | ||
|
||
|
@@ -32,6 +33,72 @@ | |
from ..ExcitedStates import EnergyCorrection | ||
|
||
|
||
class Psi4GradientProvider: | ||
def __init__(self, wfn): | ||
self.wfn = wfn | ||
self.mol = self.wfn.molecule() | ||
self.backend = "psi4" | ||
self.mints = psi4.core.MintsHelper(self.wfn) | ||
|
||
def correlated_gradient(self, g1_ao, w_ao, g2_ao_1, g2_ao_2): | ||
""" | ||
g1_ao: relaxed one-particle density matrix | ||
w_ao: energy-weighted density matrix | ||
g2_ao_1, g2_ao_2: relaxed two-particle density matrices | ||
""" | ||
natoms = self.mol.natom() | ||
Gradient = {} | ||
Gradient["N"] = np.zeros((natoms, 3)) | ||
Gradient["S"] = np.zeros((natoms, 3)) | ||
Gradient["T"] = np.zeros((natoms, 3)) | ||
Gradient["V"] = np.zeros((natoms, 3)) | ||
Gradient["OEI"] = np.zeros((natoms, 3)) | ||
Gradient["TEI"] = np.zeros((natoms, 3)) | ||
Gradient["Total"] = np.zeros((natoms, 3)) | ||
|
||
# 1st Derivative of Nuclear Repulsion | ||
Gradient["N"] = psi4.core.Matrix.to_array( | ||
self.mol.nuclear_repulsion_energy_deriv1([0, 0, 0]) | ||
) | ||
# Build Integral Derivatives | ||
cart = ['_X', '_Y', '_Z'] | ||
oei_dict = {"S": "OVERLAP", "T": "KINETIC", "V": "POTENTIAL"} | ||
|
||
deriv1_mat = {} | ||
deriv1_np = {} | ||
# 1st Derivative of OEIs | ||
for atom in range(natoms): | ||
for key in oei_dict: | ||
string = key + str(atom) | ||
deriv1_mat[string] = self.mints.ao_oei_deriv1(oei_dict[key], atom) | ||
for p in range(3): | ||
map_key = string + cart[p] | ||
deriv1_np[map_key] = np.asarray(deriv1_mat[string][p]) | ||
if key == "S": | ||
Gradient["S"][atom, p] = np.sum(w_ao * deriv1_np[map_key]) | ||
else: | ||
Gradient[key][atom, p] = np.sum(g1_ao * deriv1_np[map_key]) | ||
# Build Total OEI Gradient | ||
Gradient["OEI"] = Gradient["T"] + Gradient["V"] + Gradient["S"] | ||
|
||
# Build TE contributions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TEI contributions? |
||
for atom in range(natoms): | ||
deriv2 = self.mints.ao_tei_deriv1(atom) | ||
for p in range(3): | ||
deriv2_np = np.asarray(deriv2[p]) | ||
Gradient["TEI"][atom, p] += np.einsum( | ||
'pqrs,prqs->', g2_ao_1, deriv2_np, optimize=True | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not much to optimize here ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, also copy/pasted from the tutorial 😄 |
||
) | ||
Gradient["TEI"][atom, p] -= np.einsum( | ||
'pqrs,psqr->', g2_ao_2, deriv2_np, optimize=True | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same ... not much to optimize |
||
) | ||
ret = GradientComponents( | ||
natoms, Gradient["N"], Gradient["S"], | ||
Gradient["T"] + Gradient["V"], Gradient["TEI"] | ||
) | ||
return ret | ||
|
||
|
||
class Psi4OperatorIntegralProvider: | ||
def __init__(self, wfn): | ||
self.wfn = wfn | ||
|
@@ -116,6 +183,7 @@ def __init__(self, wfn): | |
wfn.nalpha(), wfn.nbeta(), | ||
self.restricted) | ||
self.operator_integral_provider = Psi4OperatorIntegralProvider(self.wfn) | ||
self.gradient_provider = Psi4GradientProvider(self.wfn) | ||
|
||
self.environment = None | ||
self.environment_implementation = None | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,15 +24,76 @@ | |
|
||
from libadcc import HartreeFockProvider | ||
from adcc.misc import cached_property | ||
from adcc.gradients import GradientComponents | ||
|
||
from .EriBuilder import EriBuilder | ||
from ..exceptions import InvalidReference | ||
from ..ExcitedStates import EnergyCorrection | ||
|
||
from pyscf import ao2mo, gto, scf | ||
from pyscf import ao2mo, gto, scf, grad | ||
from pyscf.solvent import ddcosmo | ||
|
||
|
||
class PyScfGradientProvider: | ||
def __init__(self, scfres): | ||
self.scfres = scfres | ||
self.mol = self.scfres.mol | ||
self.backend = "pyscf" | ||
|
||
def correlated_gradient(self, g1_ao, w_ao, g2_ao_1, g2_ao_2): | ||
natoms = self.mol.natm | ||
Gradient = {} | ||
Gradient["N"] = np.zeros((natoms, 3)) | ||
Gradient["S"] = np.zeros((natoms, 3)) | ||
Gradient["T+V"] = np.zeros((natoms, 3)) | ||
Gradient["OEI"] = np.zeros((natoms, 3)) | ||
Gradient["TEI"] = np.zeros((natoms, 3)) | ||
Gradient["Total"] = np.zeros((natoms, 3)) | ||
|
||
# TODO: does RHF/UHF matter here? | ||
gradient = grad.RHF(self.scfres) | ||
Comment on lines
+53
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there no |
||
hcore_deriv = gradient.hcore_generator() | ||
Sx = -1.0 * self.mol.intor('int1e_ipovlp', aosym='s1') | ||
ERIx = -1.0 * self.mol.intor('int2e_ip1', aosym='s1') | ||
|
||
ao_slices = self.mol.aoslice_by_atom() | ||
for ia in range(natoms): | ||
# TODO: only contract/compute with specific slices | ||
# of density matrices (especially TPDM) | ||
# this requires a lot of work however... | ||
k0, k1 = ao_slices[ia, 2:] | ||
|
||
# derivative of the overlap matrix | ||
Sx_a = np.zeros_like(Sx) | ||
Sx_a[:, k0:k1] = Sx[:, k0:k1] | ||
Sx_a += Sx_a.transpose(0, 2, 1) | ||
Gradient["S"][ia] += np.einsum("xpq,pq->x", Sx_a, w_ao) | ||
|
||
# derivative of the core Hamiltonian | ||
Hx_a = hcore_deriv(ia) | ||
Gradient["T+V"][ia] += np.einsum("xpq,pq->x", Hx_a, g1_ao) | ||
|
||
# derivatives of the ERIs | ||
ERIx_a = np.zeros_like(ERIx) | ||
ERIx_a[:, k0:k1] = ERIx[:, k0:k1] | ||
ERIx_a += ( | ||
+ ERIx_a.transpose(0, 2, 1, 4, 3) | ||
+ ERIx_a.transpose(0, 3, 4, 1, 2) | ||
+ ERIx_a.transpose(0, 4, 3, 2, 1) | ||
) | ||
Gradient["TEI"][ia] += np.einsum( | ||
"pqrs,xprqs->x", g2_ao_1, ERIx_a, optimize=True | ||
) | ||
Gradient["TEI"][ia] -= np.einsum( | ||
"pqrs,xpsqr->x", g2_ao_2, ERIx_a, optimize=True | ||
) | ||
ret = GradientComponents( | ||
natoms, gradient.grad_nuc(), Gradient["S"], | ||
Gradient["T+V"], Gradient["TEI"] | ||
) | ||
return ret | ||
|
||
|
||
class PyScfOperatorIntegralProvider: | ||
def __init__(self, scfres): | ||
self.scfres = scfres | ||
|
@@ -111,7 +172,7 @@ def coefficients(self): | |
|
||
def compute_mo_eri(self, blocks, spins): | ||
coeffs = tuple(self.coefficients[blocks[i] + spins[i]] for i in range(4)) | ||
# TODO Pyscf usse HDF5 internal to do the AO2MO here we read it all | ||
# TODO Pyscf uses HDF5 internal to do the AO2MO here we read it all | ||
# into memory. This wastes memory and could be avoided if temporary | ||
# files were used instead. These could be deleted on the call | ||
# to `flush_cache` automatically. | ||
|
@@ -138,6 +199,7 @@ def __init__(self, scfres): | |
self.operator_integral_provider = PyScfOperatorIntegralProvider( | ||
self.scfres | ||
) | ||
self.gradient_provider = PyScfGradientProvider(self.scfres) | ||
if not self.restricted: | ||
assert self.scfres.mo_coeff[0].shape[1] == \ | ||
self.scfres.mo_coeff[1].shape[1] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why capitalised?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shamelessly copied from some
psi4numpy
tutorial, will change this 😄