Source code for pyscf.hessian.uks

#!/usr/bin/env python
# Copyright 2014-2019 The PySCF Developers. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Qiming Sun <osirpt.sun@gmail.com>
#

'''
Non-relativistic UKS analytical Hessian
'''


import numpy
from pyscf import lib
from pyscf.lib import logger
from pyscf.grad import rks as rks_grad
from pyscf.hessian import rhf as rhf_hess
from pyscf.hessian import uhf as uhf_hess
from pyscf.hessian import rks as rks_hess
from pyscf.dft import numint
_get_jk = rhf_hess._get_jk


[docs] def partial_hess_elec(hessobj, mo_energy=None, mo_coeff=None, mo_occ=None, atmlst=None, max_memory=4000, verbose=None): log = logger.new_logger(hessobj, verbose) time0 = t1 = (logger.process_clock(), logger.perf_counter()) mol = hessobj.mol mf = hessobj.base ni = mf._numint if mf.nlc or ni.libxc.is_nlc(mf.xc): raise NotImplementedError('RKS Hessian for NLC functional') if mo_energy is None: mo_energy = mf.mo_energy if mo_occ is None: mo_occ = mf.mo_occ if mo_coeff is None: mo_coeff = mf.mo_coeff if atmlst is None: atmlst = range(mol.natm) nao, nmo = mo_coeff[0].shape mocca = mo_coeff[0][:,mo_occ[0]>0] moccb = mo_coeff[1][:,mo_occ[1]>0] dm0a = numpy.dot(mocca, mocca.T) dm0b = numpy.dot(moccb, moccb.T) # Energy weighted density matrix mo_ea = mo_energy[0][mo_occ[0]>0] mo_eb = mo_energy[1][mo_occ[1]>0] dme0 = numpy.einsum('pi,qi,i->pq', mocca, mocca, mo_ea) dme0+= numpy.einsum('pi,qi,i->pq', moccb, moccb, mo_eb) omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) hybrid = ni.libxc.is_hybrid_xc(mf.xc) de2, ej, ek = uhf_hess._partial_hess_ejk(hessobj, mo_energy, mo_coeff, mo_occ, atmlst, max_memory, verbose, with_k=hybrid) de2 += ej - hyb * ek # (A,B,dR_A,dR_B) mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory*.9-mem_now) veffa_diag, veffb_diag = _get_vxc_diag(hessobj, mo_coeff, mo_occ, max_memory) if hybrid and omega != 0: with mol.with_range_coulomb(omega): vk1a, vk1b = _get_jk(mol, 'int2e_ipip1', 9, 's2kl', ['jk->s1il', dm0a, 'jk->s1il', dm0b]) veffa_diag -= (alpha-hyb) * vk1a.reshape(3,3,nao,nao) veffb_diag -= (alpha-hyb) * vk1b.reshape(3,3,nao,nao) vk1a = vk1b = None t1 = log.timer_debug1('contracting int2e_ipip1', *t1) aoslices = mol.aoslice_by_atom() vxca, vxcb = _get_vxc_deriv2(hessobj, mo_coeff, mo_occ, max_memory) for i0, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] veffa = vxca[ia] veffb = vxcb[ia] shls_slice = (shl0, shl1) + (0, mol.nbas)*3 if hybrid and omega != 0: with mol.with_range_coulomb(omega): vk1a, vk1b, vk2a, vk2b = \ _get_jk(mol, 'int2e_ip1ip2', 9, 's1', ['li->s1kj', dm0a[:,p0:p1], 'li->s1kj', dm0b[:,p0:p1], 'lj->s1ki', dm0a , 'lj->s1ki', dm0b ], shls_slice=shls_slice) veffa -= (alpha-hyb) * vk1a.reshape(3,3,nao,nao) veffb -= (alpha-hyb) * vk1b.reshape(3,3,nao,nao) veffa[:,:,:,p0:p1] -= (alpha-hyb) * vk2a.reshape(3,3,nao,p1-p0) veffb[:,:,:,p0:p1] -= (alpha-hyb) * vk2b.reshape(3,3,nao,p1-p0) t1 = log.timer_debug1('range-separated int2e_ip1ip2 for atom %d'%ia, *t1) with mol.with_range_coulomb(omega): vk1a, vk1b = _get_jk(mol, 'int2e_ipvip1', 9, 's2kl', ['li->s1kj', dm0a[:,p0:p1], 'li->s1kj', dm0b[:,p0:p1]], shls_slice=shls_slice) veffa -= (alpha-hyb) * vk1a.transpose(0,2,1).reshape(3,3,nao,nao) veffb -= (alpha-hyb) * vk1b.transpose(0,2,1).reshape(3,3,nao,nao) t1 = log.timer_debug1('range-separated int2e_ipvip1 for atom %d'%ia, *t1) vk1a = vk1b = vk2a = vk2b = None de2[i0,i0] += numpy.einsum('xypq,pq->xy', veffa_diag[:,:,p0:p1], dm0a[p0:p1])*2 de2[i0,i0] += numpy.einsum('xypq,pq->xy', veffb_diag[:,:,p0:p1], dm0b[p0:p1])*2 for j0, ja in enumerate(atmlst[:i0+1]): q0, q1 = aoslices[ja][2:] de2[i0,j0] += numpy.einsum('xypq,pq->xy', veffa[:,:,q0:q1], dm0a[q0:q1])*2 de2[i0,j0] += numpy.einsum('xypq,pq->xy', veffb[:,:,q0:q1], dm0b[q0:q1])*2 for j0 in range(i0): de2[j0,i0] = de2[i0,j0].T log.timer('UKS partial hessian', *time0) return de2
[docs] def make_h1(hessobj, mo_coeff, mo_occ, chkfile=None, atmlst=None, verbose=None): mol = hessobj.mol if atmlst is None: atmlst = range(mol.natm) nao, nmo = mo_coeff[0].shape mocca = mo_coeff[0][:,mo_occ[0]>0] moccb = mo_coeff[1][:,mo_occ[1]>0] dm0a = numpy.dot(mocca, mocca.T) dm0b = numpy.dot(moccb, moccb.T) hcore_deriv = hessobj.base.nuc_grad_method().hcore_generator(mol) mf = hessobj.base ni = mf._numint ni.libxc.test_deriv_order(mf.xc, 2, raise_error=True) omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) hybrid = ni.libxc.is_hybrid_xc(mf.xc) mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory*.9-mem_now) h1aoa, h1aob = _get_vxc_deriv1(hessobj, mo_coeff, mo_occ, max_memory) aoslices = mol.aoslice_by_atom() for i0, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] shls_slice = (shl0, shl1) + (0, mol.nbas)*3 if hybrid: vj1a, vj1b, vj2a, vj2b, vk1a, vk1b, vk2a, vk2b = \ _get_jk(mol, 'int2e_ip1', 3, 's2kl', ['ji->s2kl', -dm0a[:,p0:p1], 'ji->s2kl', -dm0b[:,p0:p1], 'lk->s1ij', -dm0a , 'lk->s1ij', -dm0b , 'li->s1kj', -dm0a[:,p0:p1], 'li->s1kj', -dm0b[:,p0:p1], 'jk->s1il', -dm0a , 'jk->s1il', -dm0b ], shls_slice=shls_slice) vj1 = vj1a + vj1b vj2 = vj2a + vj2b veffa = vj1 - hyb * vk1a veffb = vj1 - hyb * vk1b veffa[:,p0:p1] += vj2 - hyb * vk2a veffb[:,p0:p1] += vj2 - hyb * vk2b if omega != 0: with mol.with_range_coulomb(omega): vk1a, vk1b, vk2a, vk2b = \ _get_jk(mol, 'int2e_ip1', 3, 's2kl', ['li->s1kj', -dm0a[:,p0:p1], 'li->s1kj', -dm0b[:,p0:p1], 'jk->s1il', -dm0a , 'jk->s1il', -dm0b ], shls_slice=shls_slice) veffa -= (alpha-hyb) * vk1a veffb -= (alpha-hyb) * vk1b veffa[:,p0:p1] -= (alpha-hyb) * vk2a veffb[:,p0:p1] -= (alpha-hyb) * vk2b else: vj1a, vj1b, vj2a, vj2b = \ _get_jk(mol, 'int2e_ip1', 3, 's2kl', ['ji->s2kl', -dm0a[:,p0:p1], 'ji->s2kl', -dm0b[:,p0:p1], 'lk->s1ij', -dm0a , 'lk->s1ij', -dm0b ], shls_slice=shls_slice) vj1 = vj1a + vj1b vj2 = vj2a + vj2b veffa = vj1 veffb = vj1.copy() veffa[:,p0:p1] += vj2 veffb[:,p0:p1] += vj2 h1 = hcore_deriv(ia) h1aoa[ia] += h1 + veffa + veffa.transpose(0,2,1) h1aob[ia] += h1 + veffb + veffb.transpose(0,2,1) if chkfile is None: return h1aoa, h1aob else: for ia in atmlst: lib.chkfile.save(chkfile, 'scf_f1ao/0/%d'%ia, h1aoa[ia]) lib.chkfile.save(chkfile, 'scf_f1ao/1/%d'%ia, h1aob[ia]) return chkfile
XX, XY, XZ = 4, 5, 6 YX, YY, YZ = 5, 7, 8 ZX, ZY, ZZ = 6, 8, 9 XXX, XXY, XXZ, XYY, XYZ, XZZ = 10, 11, 12, 13, 14, 15 YYY, YYZ, YZZ, ZZZ = 16, 17, 18, 19 def _get_vxc_diag(hessobj, mo_coeff, mo_occ, max_memory): mol = hessobj.mol mf = hessobj.base if hessobj.grids is not None: grids = hessobj.grids else: grids = mf.grids if grids.coords is None: grids.build(with_non0tab=True) nao, nmo = mo_coeff[0].shape ni = mf._numint xctype = ni._xc_type(mf.xc) shls_slice = (0, mol.nbas) ao_loc = mol.ao_loc_nr() vmata = numpy.zeros((6,nao,nao)) vmatb = numpy.zeros((6,nao,nao)) if xctype == 'LDA': ao_deriv = 2 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[0], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[0], mo_coeff[1], mo_occ[1], mask, xctype) vxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 1, xctype=xctype)[1] wv = weight * vxc[:,0] aowa = numint._scale_ao(ao[0], wv[0]) aowb = numint._scale_ao(ao[0], wv[1]) for i in range(6): vmata[i] += numint._dot_ao_ao(mol, ao[i+4], aowa, mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[i+4], aowb, mask, shls_slice, ao_loc) aowa = aowb = None elif xctype == 'GGA': def contract_(mat, ao, aoidx, wv, mask): aow = numint._scale_ao(ao[aoidx[0]], wv[1]) aow+= numint._scale_ao(ao[aoidx[1]], wv[2]) aow+= numint._scale_ao(ao[aoidx[2]], wv[3]) mat += numint._dot_ao_ao(mol, aow, ao[0], mask, shls_slice, ao_loc) ao_deriv = 3 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:4], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:4], mo_coeff[1], mo_occ[1], mask, xctype) vxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 1, xctype=xctype)[1] wv = weight * vxc aowa = numint._scale_ao(ao[:4], wv[0,:4]) aowb = numint._scale_ao(ao[:4], wv[1,:4]) for i in range(6): vmata[i] += numint._dot_ao_ao(mol, ao[i+4], aowa, mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[i+4], aowb, mask, shls_slice, ao_loc) contract_(vmata[0], ao, [XXX,XXY,XXZ], wv[0], mask) contract_(vmata[1], ao, [XXY,XYY,XYZ], wv[0], mask) contract_(vmata[2], ao, [XXZ,XYZ,XZZ], wv[0], mask) contract_(vmata[3], ao, [XYY,YYY,YYZ], wv[0], mask) contract_(vmata[4], ao, [XYZ,YYZ,YZZ], wv[0], mask) contract_(vmata[5], ao, [XZZ,YZZ,ZZZ], wv[0], mask) contract_(vmatb[0], ao, [XXX,XXY,XXZ], wv[1], mask) contract_(vmatb[1], ao, [XXY,XYY,XYZ], wv[1], mask) contract_(vmatb[2], ao, [XXZ,XYZ,XZZ], wv[1], mask) contract_(vmatb[3], ao, [XYY,YYY,YYZ], wv[1], mask) contract_(vmatb[4], ao, [XYZ,YYZ,YZZ], wv[1], mask) contract_(vmatb[5], ao, [XZZ,YZZ,ZZZ], wv[1], mask) vxc = aowa = aowb = None elif xctype == 'MGGA': def contract_(mat, ao, aoidx, wv, mask): aow = numint._scale_ao(ao[aoidx[0]], wv[1]) aow+= numint._scale_ao(ao[aoidx[1]], wv[2]) aow+= numint._scale_ao(ao[aoidx[2]], wv[3]) mat += numint._dot_ao_ao(mol, aow, ao[0], mask, shls_slice, ao_loc) ao_deriv = 3 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:10], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:10], mo_coeff[1], mo_occ[1], mask, xctype) vxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 1, xctype=xctype)[1] wv = weight * vxc wv[:,4] *= .5 # for the factor 1/2 in tau aowa = numint._scale_ao(ao[:4], wv[0,:4]) aowb = numint._scale_ao(ao[:4], wv[1,:4]) for i in range(6): vmata[i] += numint._dot_ao_ao(mol, ao[i+4], aowa, mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[i+4], aowb, mask, shls_slice, ao_loc) contract_(vmata[0], ao, [XXX,XXY,XXZ], wv[0], mask) contract_(vmata[1], ao, [XXY,XYY,XYZ], wv[0], mask) contract_(vmata[2], ao, [XXZ,XYZ,XZZ], wv[0], mask) contract_(vmata[3], ao, [XYY,YYY,YYZ], wv[0], mask) contract_(vmata[4], ao, [XYZ,YYZ,YZZ], wv[0], mask) contract_(vmata[5], ao, [XZZ,YZZ,ZZZ], wv[0], mask) contract_(vmatb[0], ao, [XXX,XXY,XXZ], wv[1], mask) contract_(vmatb[1], ao, [XXY,XYY,XYZ], wv[1], mask) contract_(vmatb[2], ao, [XXZ,XYZ,XZZ], wv[1], mask) contract_(vmatb[3], ao, [XYY,YYY,YYZ], wv[1], mask) contract_(vmatb[4], ao, [XYZ,YYZ,YZZ], wv[1], mask) contract_(vmatb[5], ao, [XZZ,YZZ,ZZZ], wv[1], mask) aowa = [numint._scale_ao(ao[i], wv[0,4]) for i in range(1, 4)] aowb = [numint._scale_ao(ao[i], wv[1,4]) for i in range(1, 4)] for i, j in enumerate([XXX, XXY, XXZ, XYY, XYZ, XZZ]): vmata[i] += numint._dot_ao_ao(mol, ao[j], aowa[0], mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[j], aowb[0], mask, shls_slice, ao_loc) for i, j in enumerate([XXY, XYY, XYZ, YYY, YYZ, YZZ]): vmata[i] += numint._dot_ao_ao(mol, ao[j], aowa[1], mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[j], aowb[1], mask, shls_slice, ao_loc) for i, j in enumerate([XXZ, XYZ, XZZ, YYZ, YZZ, ZZZ]): vmata[i] += numint._dot_ao_ao(mol, ao[j], aowa[2], mask, shls_slice, ao_loc) vmatb[i] += numint._dot_ao_ao(mol, ao[j], aowb[2], mask, shls_slice, ao_loc) vmata = vmata[[0,1,2, 1,3,4, 2,4,5]].reshape(3,3,nao,nao) vmatb = vmatb[[0,1,2, 1,3,4, 2,4,5]].reshape(3,3,nao,nao) return vmata, vmatb def _get_vxc_deriv2(hessobj, mo_coeff, mo_occ, max_memory): mol = hessobj.mol mf = hessobj.base if hessobj.grids is not None: grids = hessobj.grids else: grids = mf.grids if grids.coords is None: grids.build(with_non0tab=True) nao, nmo = mo_coeff[0].shape ni = mf._numint xctype = ni._xc_type(mf.xc) aoslices = mol.aoslice_by_atom() shls_slice = (0, mol.nbas) ao_loc = mol.ao_loc_nr() dm0a, dm0b = mf.make_rdm1(mo_coeff, mo_occ) vmata = numpy.zeros((mol.natm,3,3,nao,nao)) vmatb = numpy.zeros((mol.natm,3,3,nao,nao)) ipipa = numpy.zeros((3,3,nao,nao)) ipipb = numpy.zeros((3,3,nao,nao)) if xctype == 'LDA': ao_deriv = 1 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[0], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[0], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc[:,0] aow = numpy.einsum('xpi,p->xpi', ao[1:4], wv[0]) rks_hess._d1d2_dot_(ipipa, mol, aow, ao[1:4], mask, ao_loc, False) aow = numpy.einsum('xpi,p->xpi', ao[1:4], wv[1]) rks_hess._d1d2_dot_(ipipb, mol, aow, ao[1:4], mask, ao_loc, False) ao_dm0a = numint._dot_ao_dm(mol, ao[0], dm0a, mask, shls_slice, ao_loc) ao_dm0b = numint._dot_ao_dm(mol, ao[0], dm0b, mask, shls_slice, ao_loc) wf = weight * fxc[:,0,:,0] for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] # *2 for \nabla|ket> in rho1 rho1a = numpy.einsum('xpi,pi->xp', ao[1:,:,p0:p1], ao_dm0a[:,p0:p1]) * 2 rho1b = numpy.einsum('xpi,pi->xp', ao[1:,:,p0:p1], ao_dm0b[:,p0:p1]) * 2 wv = wf[0,:,None] * rho1a wv += wf[1,:,None] * rho1b # aow ~ rho1 ~ d/dR1 aow = numpy.einsum('pi,xp->xpi', ao[0], wv[0]) rks_hess._d1d2_dot_(vmata[ia], mol, ao[1:4], aow, mask, ao_loc, False) aow = numpy.einsum('pi,xp->xpi', ao[0], wv[1]) rks_hess._d1d2_dot_(vmatb[ia], mol, ao[1:4], aow, mask, ao_loc, False) ao_dm0a = ao_dm0b = aow = None for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] vmata[ia,:,:,:,p0:p1] += ipipa[:,:,:,p0:p1] vmatb[ia,:,:,:,p0:p1] += ipipb[:,:,:,p0:p1] elif xctype == 'GGA': ao_deriv = 2 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:4], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:4], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc wv[:,0] *= .5 aow = rks_grad._make_dR_dao_w(ao, wv[0]) rks_hess._d1d2_dot_(ipipa, mol, aow, ao[1:4], mask, ao_loc, False) aow = rks_grad._make_dR_dao_w(ao, wv[1]) rks_hess._d1d2_dot_(ipipb, mol, aow, ao[1:4], mask, ao_loc, False) ao_dm0a = [numint._dot_ao_dm(mol, ao[i], dm0a, mask, shls_slice, ao_loc) for i in range(4)] ao_dm0b = [numint._dot_ao_dm(mol, ao[i], dm0b, mask, shls_slice, ao_loc) for i in range(4)] wf = weight * fxc for ia in range(mol.natm): dR_rho1a = rks_hess._make_dR_rho1(ao, ao_dm0a, ia, aoslices, xctype) dR_rho1b = rks_hess._make_dR_rho1(ao, ao_dm0b, ia, aoslices, xctype) wv = numpy.einsum('xbyg,sxg->bsyg', wf[0], dR_rho1a) wv += numpy.einsum('xbyg,sxg->bsyg', wf[1], dR_rho1b) wv[:,:,0] *= .5 wva, wvb = wv aow = rks_grad._make_dR_dao_w(ao, wva[0]) rks_grad._d1_dot_(vmata[ia,0], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wva[1]) rks_grad._d1_dot_(vmata[ia,1], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wva[2]) rks_grad._d1_dot_(vmata[ia,2], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wva[i,:4]) for i in range(3)] rks_hess._d1d2_dot_(vmata[ia], mol, ao[1:4], aow, mask, ao_loc, False) aow = rks_grad._make_dR_dao_w(ao, wvb[0]) rks_grad._d1_dot_(vmatb[ia,0], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wvb[1]) rks_grad._d1_dot_(vmatb[ia,1], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wvb[2]) rks_grad._d1_dot_(vmatb[ia,2], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wvb[i,:4]) for i in range(3)] rks_hess._d1d2_dot_(vmatb[ia], mol, ao[1:4], aow, mask, ao_loc, False) ao_dm0a = ao_dm0b = aow = None for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] vmata[ia,:,:,:,p0:p1] += ipipa[:,:,:,p0:p1] vmata[ia,:,:,:,p0:p1] += ipipa[:,:,p0:p1].transpose(1,0,3,2) vmatb[ia,:,:,:,p0:p1] += ipipb[:,:,:,p0:p1] vmatb[ia,:,:,:,p0:p1] += ipipb[:,:,p0:p1].transpose(1,0,3,2) elif xctype == 'MGGA': XX, XY, XZ = 4, 5, 6 YX, YY, YZ = 5, 7, 8 ZX, ZY, ZZ = 6, 8, 9 ao_deriv = 2 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:10], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:10], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc wv[:,0] *= .5 wv[:,4] *= .25 aow = rks_grad._make_dR_dao_w(ao, wv[0]) rks_hess._d1d2_dot_(ipipa, mol, aow, ao[1:4], mask, ao_loc, False) aow = rks_grad._make_dR_dao_w(ao, wv[1]) rks_hess._d1d2_dot_(ipipb, mol, aow, ao[1:4], mask, ao_loc, False) aow = [numint._scale_ao(ao[i], wv[0,4]) for i in range(4, 10)] rks_hess._d1d2_dot_(ipipa, mol, [aow[0], aow[1], aow[2]], [ao[XX], ao[XY], ao[XZ]], mask, ao_loc, False) rks_hess._d1d2_dot_(ipipa, mol, [aow[1], aow[3], aow[4]], [ao[YX], ao[YY], ao[YZ]], mask, ao_loc, False) rks_hess._d1d2_dot_(ipipa, mol, [aow[2], aow[4], aow[5]], [ao[ZX], ao[ZY], ao[ZZ]], mask, ao_loc, False) aow = [numint._scale_ao(ao[i], wv[1,4]) for i in range(4, 10)] rks_hess._d1d2_dot_(ipipb, mol, [aow[0], aow[1], aow[2]], [ao[XX], ao[XY], ao[XZ]], mask, ao_loc, False) rks_hess._d1d2_dot_(ipipb, mol, [aow[1], aow[3], aow[4]], [ao[YX], ao[YY], ao[YZ]], mask, ao_loc, False) rks_hess._d1d2_dot_(ipipb, mol, [aow[2], aow[4], aow[5]], [ao[ZX], ao[ZY], ao[ZZ]], mask, ao_loc, False) ao_dm0a = [numint._dot_ao_dm(mol, ao[i], dm0a, mask, shls_slice, ao_loc) for i in range(4)] ao_dm0b = [numint._dot_ao_dm(mol, ao[i], dm0b, mask, shls_slice, ao_loc) for i in range(4)] wf = weight * fxc for ia in range(mol.natm): dR_rho1a = rks_hess._make_dR_rho1(ao, ao_dm0a, ia, aoslices, xctype) dR_rho1b = rks_hess._make_dR_rho1(ao, ao_dm0b, ia, aoslices, xctype) wv = numpy.einsum('xbyg,sxg->bsyg', wf[0], dR_rho1a) wv += numpy.einsum('xbyg,sxg->bsyg', wf[1], dR_rho1b) wv[:,:,0] *= .5 wv[:,:,4] *= .5 wva, wvb = wv aow = rks_grad._make_dR_dao_w(ao, wva[0]) rks_grad._d1_dot_(vmata[ia,0], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wva[1]) rks_grad._d1_dot_(vmata[ia,1], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wva[2]) rks_grad._d1_dot_(vmata[ia,2], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wva[i,:4]) for i in range(3)] rks_hess._d1d2_dot_(vmata[ia], mol, ao[1:4], aow, mask, ao_loc, False) aow = rks_grad._make_dR_dao_w(ao, wvb[0]) rks_grad._d1_dot_(vmatb[ia,0], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wvb[1]) rks_grad._d1_dot_(vmatb[ia,1], mol, aow, ao[0], mask, ao_loc, True) aow = rks_grad._make_dR_dao_w(ao, wvb[2]) rks_grad._d1_dot_(vmatb[ia,2], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wvb[i,:4]) for i in range(3)] rks_hess._d1d2_dot_(vmatb[ia], mol, ao[1:4], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[1], wva[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmata[ia], mol, [ao[XX], ao[XY], ao[XZ]], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[2], wva[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmata[ia], mol, [ao[YX], ao[YY], ao[YZ]], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[3], wva[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmata[ia], mol, [ao[ZX], ao[ZY], ao[ZZ]], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[1], wvb[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmatb[ia], mol, [ao[XX], ao[XY], ao[XZ]], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[2], wvb[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmatb[ia], mol, [ao[YX], ao[YY], ao[YZ]], aow, mask, ao_loc, False) aow = [numint._scale_ao(ao[3], wvb[i,4]) for i in range(3)] rks_hess._d1d2_dot_(vmatb[ia], mol, [ao[ZX], ao[ZY], ao[ZZ]], aow, mask, ao_loc, False) for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] vmata[ia,:,:,:,p0:p1] += ipipa[:,:,:,p0:p1] vmata[ia,:,:,:,p0:p1] += ipipa[:,:,p0:p1].transpose(1,0,3,2) vmatb[ia,:,:,:,p0:p1] += ipipb[:,:,:,p0:p1] vmatb[ia,:,:,:,p0:p1] += ipipb[:,:,p0:p1].transpose(1,0,3,2) return vmata, vmatb def _get_vxc_deriv1(hessobj, mo_coeff, mo_occ, max_memory): mol = hessobj.mol mf = hessobj.base if hessobj.grids is not None: grids = hessobj.grids else: grids = mf.grids if grids.coords is None: grids.build(with_non0tab=True) nao, nmo = mo_coeff[0].shape ni = mf._numint xctype = ni._xc_type(mf.xc) aoslices = mol.aoslice_by_atom() shls_slice = (0, mol.nbas) ao_loc = mol.ao_loc_nr() dm0a, dm0b = mf.make_rdm1(mo_coeff, mo_occ) vmata = numpy.zeros((mol.natm,3,nao,nao)) vmatb = numpy.zeros((mol.natm,3,nao,nao)) max_memory = max(2000, max_memory-(vmata.size+vmatb.size)*8/1e6) if xctype == 'LDA': ao_deriv = 1 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[0], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[0], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc[:,0] ao_dm0a = numint._dot_ao_dm(mol, ao[0], dm0a, mask, shls_slice, ao_loc) ao_dm0b = numint._dot_ao_dm(mol, ao[0], dm0b, mask, shls_slice, ao_loc) aow1a = numpy.einsum('xpi,p->xpi', ao[1:], wv[0]) aow1b = numpy.einsum('xpi,p->xpi', ao[1:], wv[1]) wf = weight * fxc[:,0,:,0] for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] # First order density = rho1 * 2. *2 is not applied because + c.c. in the end rho1a = numpy.einsum('xpi,pi->xp', ao[1:,:,p0:p1], ao_dm0a[:,p0:p1]) rho1b = numpy.einsum('xpi,pi->xp', ao[1:,:,p0:p1], ao_dm0b[:,p0:p1]) wv = wf[0,:,None] * rho1a wv += wf[1,:,None] * rho1b aow = numpy.einsum('pi,xp->xpi', ao[0], wv[0]) aow[:,:,p0:p1] += aow1a[:,:,p0:p1] rks_grad._d1_dot_(vmata[ia], mol, aow, ao[0], mask, ao_loc, True) aow = numpy.einsum('pi,xp->xpi', ao[0], wv[1]) aow[:,:,p0:p1] += aow1b[:,:,p0:p1] rks_grad._d1_dot_(vmatb[ia], mol, aow, ao[0], mask, ao_loc, True) ao_dm0a = ao_dm0b = aow = aow1a = aow1b = None for ia in range(mol.natm): vmata[ia] = -vmata[ia] - vmata[ia].transpose(0,2,1) vmatb[ia] = -vmatb[ia] - vmatb[ia].transpose(0,2,1) elif xctype == 'GGA': ao_deriv = 2 vipa = numpy.zeros((3,nao,nao)) vipb = numpy.zeros((3,nao,nao)) for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:4], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:4], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc wv[:,0] *= .5 rks_grad._gga_grad_sum_(vipa, mol, ao, wv[0], mask, ao_loc) rks_grad._gga_grad_sum_(vipb, mol, ao, wv[1], mask, ao_loc) ao_dm0a = [numint._dot_ao_dm(mol, ao[i], dm0a, mask, shls_slice, ao_loc) for i in range(4)] ao_dm0b = [numint._dot_ao_dm(mol, ao[i], dm0b, mask, shls_slice, ao_loc) for i in range(4)] wf = weight * fxc for ia in range(mol.natm): dR_rho1a = rks_hess._make_dR_rho1(ao, ao_dm0a, ia, aoslices, xctype) dR_rho1b = rks_hess._make_dR_rho1(ao, ao_dm0b, ia, aoslices, xctype) wv = numpy.einsum('xbyg,sxg->bsyg', wf[0], dR_rho1a) wv += numpy.einsum('xbyg,sxg->bsyg', wf[1], dR_rho1b) wv[:,:,0] *= .5 wva, wvb = wv aow = [numint._scale_ao(ao[:4], wva[i,:4]) for i in range(3)] rks_grad._d1_dot_(vmata[ia], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wvb[i,:4]) for i in range(3)] rks_grad._d1_dot_(vmatb[ia], mol, aow, ao[0], mask, ao_loc, True) ao_dm0a = ao_dm0b = aow = None for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] vmata[ia,:,p0:p1] += vipa[:,p0:p1] vmatb[ia,:,p0:p1] += vipb[:,p0:p1] vmata[ia] = -vmata[ia] - vmata[ia].transpose(0,2,1) vmatb[ia] = -vmatb[ia] - vmatb[ia].transpose(0,2,1) elif xctype == 'MGGA': if grids.level < 5: logger.warn(mol, 'MGGA Hessian is sensitive to dft grids.') ao_deriv = 2 vipa = numpy.zeros((3,nao,nao)) vipb = numpy.zeros((3,nao,nao)) for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory): rhoa = ni.eval_rho2(mol, ao[:10], mo_coeff[0], mo_occ[0], mask, xctype) rhob = ni.eval_rho2(mol, ao[:10], mo_coeff[1], mo_occ[1], mask, xctype) vxc, fxc = ni.eval_xc_eff(mf.xc, (rhoa, rhob), 2, xctype=xctype)[1:3] wv = weight * vxc wv[:,0] *= .5 wv[:,4] *= .5 rks_grad._gga_grad_sum_(vipa, mol, ao, wv[0], mask, ao_loc) rks_grad._gga_grad_sum_(vipb, mol, ao, wv[1], mask, ao_loc) rks_grad._tau_grad_dot_(vipa, mol, ao, wv[0,4], mask, ao_loc, True) rks_grad._tau_grad_dot_(vipb, mol, ao, wv[1,4], mask, ao_loc, True) ao_dm0a = [numint._dot_ao_dm(mol, ao[i], dm0a, mask, shls_slice, ao_loc) for i in range(4)] ao_dm0b = [numint._dot_ao_dm(mol, ao[i], dm0b, mask, shls_slice, ao_loc) for i in range(4)] wf = weight * fxc for ia in range(mol.natm): dR_rho1a = rks_hess._make_dR_rho1(ao, ao_dm0a, ia, aoslices, xctype) dR_rho1b = rks_hess._make_dR_rho1(ao, ao_dm0b, ia, aoslices, xctype) wv = numpy.einsum('xbyg,sxg->bsyg', wf[0], dR_rho1a) wv += numpy.einsum('xbyg,sxg->bsyg', wf[1], dR_rho1b) wv[:,:,0] *= .5 wv[:,:,4] *= .25 wva, wvb = wv aow = [numint._scale_ao(ao[:4], wva[i,:4]) for i in range(3)] rks_grad._d1_dot_(vmata[ia], mol, aow, ao[0], mask, ao_loc, True) aow = [numint._scale_ao(ao[:4], wvb[i,:4]) for i in range(3)] rks_grad._d1_dot_(vmatb[ia], mol, aow, ao[0], mask, ao_loc, True) for j in range(1, 4): aow = [numint._scale_ao(ao[j], wva[i,4]) for i in range(3)] rks_grad._d1_dot_(vmata[ia], mol, aow, ao[j], mask, ao_loc, True) aow = [numint._scale_ao(ao[j], wvb[i,4]) for i in range(3)] rks_grad._d1_dot_(vmatb[ia], mol, aow, ao[j], mask, ao_loc, True) for ia in range(mol.natm): p0, p1 = aoslices[ia][2:] vmata[ia,:,p0:p1] += vipa[:,p0:p1] vmatb[ia,:,p0:p1] += vipb[:,p0:p1] vmata[ia] = -vmata[ia] - vmata[ia].transpose(0,2,1) vmatb[ia] = -vmatb[ia] - vmatb[ia].transpose(0,2,1) return vmata, vmatb
[docs] class Hessian(uhf_hess.Hessian): '''Non-relativistic UKS hessian''' _keys = set(['grids', 'grid_response']) def __init__(self, mf): uhf_hess.Hessian.__init__(self, mf) self.grids = None self.grid_response = False partial_hess_elec = partial_hess_elec make_h1 = make_h1
from pyscf import dft dft.uks.UKS.Hessian = dft.uks_symm.UKS.Hessian = lib.class_as_method(Hessian) if __name__ == '__main__': from pyscf import gto from pyscf import dft #xc_code = 'lda,vwn' xc_code = 'wb97x' #xc_code = 'b3lyp' mol = gto.Mole() mol.verbose = 0 mol.output = None mol.atom = [ [1 , (1. , 0. , 0.000)], [1 , (0. , 1. , 0.000)], [1 , (0. , -1.517 , 1.177)], [1 , (0. , 1.517 , 1.177)] ] mol.basis = '631g' mol.unit = 'B' mol.build() mf = dft.UKS(mol) mf.xc = xc_code mf.conv_tol = 1e-14 mf.kernel() n3 = mol.natm * 3 hobj = mf.Hessian() e2 = hobj.kernel().transpose(0,2,1,3).reshape(n3,n3) print(lib.fp(e2) - -0.42286407986042956) print(lib.fp(e2) - -0.45453541215680582) print(lib.fp(e2) - -0.41385170026016327) mol.spin = 2 mf = dft.UKS(mol) mf.conv_tol = 1e-14 mf.xc = xc_code mf.scf() n3 = mol.natm * 3 hobj = Hessian(mf) e2 = hobj.kernel().transpose(0,2,1,3).reshape(n3,n3) def grad_full(ia, inc): coord = mol.atom_coord(ia).copy() ptr = mol._atm[ia,gto.PTR_COORD] de = [] for i in range(3): mol._env[ptr+i] = coord[i] + inc mf = dft.UKS(mol).run(conv_tol=1e-14, xc=xc_code).run() e1a = mf.nuc_grad_method().set(grid_response=True).kernel() mol._env[ptr+i] = coord[i] - inc mf = dft.UKS(mol).run(conv_tol=1e-14, xc=xc_code).run() e1b = mf.nuc_grad_method().set(grid_response=True).kernel() mol._env[ptr+i] = coord[i] de.append((e1a-e1b)/(2*inc)) return de e2ref = [grad_full(ia, .5e-3) for ia in range(mol.natm)] e2ref = numpy.asarray(e2ref).reshape(n3,n3) print(numpy.linalg.norm(e2-e2ref)) print(abs(e2-e2ref).max()) print(numpy.allclose(e2,e2ref,atol=1e-4)) # \partial^2 E / \partial R \partial R' e2 = hobj.partial_hess_elec(mf.mo_energy, mf.mo_coeff, mf.mo_occ) e2 += hobj.hess_nuc(mol) e2 = e2.transpose(0,2,1,3).reshape(n3,n3) def grad_partial_R(ia, inc): coord = mol.atom_coord(ia).copy() ptr = mol._atm[ia,gto.PTR_COORD] de = [] for i in range(3): mol._env[ptr+i] = coord[i] + inc e1a = mf.nuc_grad_method().kernel() mol._env[ptr+i] = coord[i] - inc e1b = mf.nuc_grad_method().kernel() mol._env[ptr+i] = coord[i] de.append((e1a-e1b)/(2*inc)) return de e2ref = [grad_partial_R(ia, .5e-4) for ia in range(mol.natm)] e2ref = numpy.asarray(e2ref).reshape(n3,n3) print(numpy.linalg.norm(e2-e2ref)) print(abs(e2-e2ref).max()) print(numpy.allclose(e2,e2ref,atol=1e-6))