#!/usr/bin/env python
# Copyright 2020 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>
#
'''
ddCOSMO TDA, TDHF, TDDFT gradients
The implementations are based on modules
pyscf.grad.tdrhf
pyscf.grad.tdrks
pyscf.grad.tduhf
pyscf.grad.tduks
'''
from functools import reduce
import numpy
from pyscf import lib
from pyscf.lib import logger
from pyscf import gto
from pyscf import scf
from pyscf import dft
from pyscf import df
from pyscf.dft import numint
from pyscf.solvent import ddcosmo
from pyscf.solvent.grad import ddcosmo_grad
from pyscf.solvent._attach_solvent import _Solvation
from pyscf.grad import rhf as rhf_grad
from pyscf.grad import rks as rks_grad
from pyscf.grad import tdrks as tdrks_grad
from pyscf.grad import tduks as tduks_grad
from pyscf.scf import cphf, ucphf
[docs]
def make_grad_object(td_base_method):
'''For td_method in vacuum, add td of solvent pcmobj'''
# Zeroth order method object must be a solvation-enabled method
if isinstance(td_base_method, rhf_grad.GradientsBase):
# For backward compatibility. The input argument is a gradient object in
# previous implementations.
td_base_method = td_base_method.base
assert isinstance(td_base_method, _Solvation)
if td_base_method.with_solvent.frozen:
raise RuntimeError('Frozen solvent model is not avialbe for energy gradients')
# The nuclear gradients of stable exited states should correspond to a
# fully relaxed solvent. Strictly, the TDDFT exited states should be
# solved using state-specific solvent model. Even if running LR-PCM for
# the zeroth order TDDFT, the wavefunction should be comptued using the
# same dielectric constant as the ground state (the zero-frequency eps).
with_solvent = td_base_method.with_solvent
if not with_solvent.equilibrium_solvation:
raise RuntimeError(
'When computing gradients of PCM-TDDFT, equilibrium solvation should '
'be employed. The PCM TDDFT should be initialized as\n'
' mf.TDDFT(equilibrium_solvation=True)')
td_grad = td_base_method.undo_solvent().Gradients()
td_grad.base = td_base_method
name = with_solvent.__class__.__name__ + td_grad.__class__.__name__
return lib.set_class(WithSolventGrad(td_grad),
(WithSolventGrad, td_grad.__class__), name)
[docs]
class WithSolventGrad:
_keys = {'de_solvent', 'de_solute'}
def __init__(self, grad_method):
self.__dict__.update(grad_method.__dict__)
self.de_solvent = None
self.de_solute = None
[docs]
def undo_solvent(self):
cls = self.__class__
name_mixin = self.base.with_solvent.__class__.__name__
obj = lib.view(self, lib.drop_class(cls, WithSolventGrad, name_mixin))
del obj.de_solvent
del obj.de_solute
return obj
[docs]
def grad_elec(self, xy, singlet, atmlst=None):
if isinstance(self.base._scf, dft.uks.UKS):
return tduks_grad_elec(self, xy, atmlst, self.max_memory, self.verbose)
elif isinstance(self.base._scf, dft.rks.RKS):
return tdrks_grad_elec(self, xy, singlet, atmlst, self.max_memory, self.verbose)
elif isinstance(self.base._scf, scf.uhf.UHF):
return tduhf_grad_elec(self, xy, atmlst, self.max_memory, self.verbose)
elif isinstance(self.base._scf, scf.hf.RHF):
return tdrhf_grad_elec(self, xy, singlet, atmlst, self.max_memory, self.verbose)
[docs]
def kernel(self, *args, dm=None, **kwargs):
if dm is None:
dm = self.base._scf.make_rdm1(ao_repr=True)
self.de_solute = super().kernel(*args, **kwargs)
self.de_solvent = self.base.with_solvent.grad(dm)
self.de = self.de_solute + self.de_solvent
if self.verbose >= logger.NOTE:
logger.note(self, '--------------- %s (+%s) gradients ---------------',
self.base.__class__.__name__,
self.base.with_solvent.__class__.__name__)
self._write(self.mol, self.de, self.atmlst)
logger.note(self, '----------------------------------------------')
return self.de
def _finalize(self):
# disable _finalize. It is called in grad_method.kernel method
# where self.de was not yet initialized.
pass
[docs]
def tdrhf_grad_elec(td_grad, x_y, singlet=True, atmlst=None,
max_memory=2000, verbose=logger.INFO):
'''
See also function pyscf.grad.tdrhf.grad_elec
'''
log = logger.new_logger(td_grad, verbose)
time0 = logger.process_clock(), logger.perf_counter()
mol = td_grad.mol
mf = td_grad.base._scf
mo_coeff = mf.mo_coeff
mo_energy = mf.mo_energy
mo_occ = mf.mo_occ
nao, nmo = mo_coeff.shape
nocc = (mo_occ>0).sum()
nvir = nmo - nocc
x, y = x_y
xpy = (x+y).reshape(nocc,nvir).T
xmy = (x-y).reshape(nocc,nvir).T
orbv = mo_coeff[:,nocc:]
orbo = mo_coeff[:,:nocc]
with_solvent = getattr(td_grad.base, 'with_solvent', mf.with_solvent)
dvv = numpy.einsum('ai,bi->ab', xpy, xpy) + numpy.einsum('ai,bi->ab', xmy, xmy)
doo =-numpy.einsum('ai,aj->ij', xpy, xpy) - numpy.einsum('ai,aj->ij', xmy, xmy)
dmxpy = reduce(numpy.dot, (orbv, xpy, orbo.T))
dmxmy = reduce(numpy.dot, (orbv, xmy, orbo.T))
dmzoo = reduce(numpy.dot, (orbo, doo, orbo.T))
dmzoo+= reduce(numpy.dot, (orbv, dvv, orbv.T))
vj, vk = mf.get_jk(mol, (dmzoo, dmxpy+dmxpy.T, dmxmy-dmxmy.T), hermi=0)
if with_solvent.equilibrium_solvation:
vj[:2] += mf.with_solvent._B_dot_x((dmzoo, dmxpy+dmxpy.T))
else:
vj[0] += mf.with_solvent._B_dot_x(dmzoo)
veff0doo = vj[0] * 2 - vk[0]
wvo = reduce(numpy.dot, (orbv.T, veff0doo, orbo)) * 2
if singlet:
veff = vj[1] * 2 - vk[1]
else:
veff = -vk[1]
veff0mop = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff))
wvo -= numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy) * 2
wvo += numpy.einsum('ac,ai->ci', veff0mop[nocc:,nocc:], xpy) * 2
veff = -vk[2]
veff0mom = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff))
wvo -= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy) * 2
wvo += numpy.einsum('ac,ai->ci', veff0mom[nocc:,nocc:], xmy) * 2
with lib.temporary_env(mf.with_solvent, equilibrium_solvation=True):
# set singlet=None, generate function for CPHF type response kernel
vresp = mf.gen_response(singlet=None, hermi=1,
with_nlc=not td_grad.base.exclude_nlc)
def fvind(x): # For singlet, closed shell ground state
dm = reduce(numpy.dot, (orbv, x.reshape(nvir,nocc)*2, orbo.T))
v1ao = vresp(dm+dm.T)
return reduce(numpy.dot, (orbv.T, v1ao, orbo)).ravel()
z1 = cphf.solve(fvind, mo_energy, mo_occ, wvo,
max_cycle=td_grad.cphf_max_cycle,
tol=td_grad.cphf_conv_tol)[0]
z1 = z1.reshape(nvir,nocc)
time1 = log.timer('Z-vector using CPHF solver', *time0)
z1ao = reduce(numpy.dot, (orbv, z1, orbo.T))
veff = vresp(z1ao+z1ao.T)
im0 = numpy.zeros((nmo,nmo))
im0[:nocc,:nocc] = reduce(numpy.dot, (orbo.T, veff0doo+veff, orbo))
im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mop[nocc:,:nocc], xpy)
im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mom[nocc:,:nocc], xmy)
im0[nocc:,nocc:] = numpy.einsum('ci,ai->ac', veff0mop[nocc:,:nocc], xpy)
im0[nocc:,nocc:]+= numpy.einsum('ci,ai->ac', veff0mom[nocc:,:nocc], xmy)
im0[nocc:,:nocc] = numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy)*2
im0[nocc:,:nocc]+= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy)*2
zeta = lib.direct_sum('i+j->ij', mo_energy, mo_energy) * .5
zeta[nocc:,:nocc] = mo_energy[:nocc]
zeta[:nocc,nocc:] = mo_energy[nocc:]
dm1 = numpy.zeros((nmo,nmo))
dm1[:nocc,:nocc] = doo
dm1[nocc:,nocc:] = dvv
dm1[nocc:,:nocc] = z1
dm1[:nocc,:nocc] += numpy.eye(nocc)*2 # for ground state
im0 = reduce(numpy.dot, (mo_coeff, im0+zeta*dm1, mo_coeff.T))
# Initialize hcore_deriv with the underlying SCF object because some
# extensions (e.g. QM/MM, solvent) modifies the SCF object only.
mf_grad = td_grad.base._scf.nuc_grad_method()
hcore_deriv = mf_grad.hcore_generator(mol)
s1 = mf_grad.get_ovlp(mol)
dmz1doo = z1ao + dmzoo
oo0 = reduce(numpy.dot, (orbo, orbo.T))
vj, vk = td_grad.get_jk(mol, (oo0, dmz1doo+dmz1doo.T, dmxpy+dmxpy.T,
dmxmy-dmxmy.T))
vj = vj.reshape(-1,3,nao,nao)
vk = vk.reshape(-1,3,nao,nao)
if singlet:
vhf1 = vj * 2 - vk
else:
vhf1 = numpy.vstack((vj[:2]*2-vk[:2], -vk[2:]))
time1 = log.timer('2e AO integral derivatives', *time1)
if atmlst is None:
atmlst = range(mol.natm)
offsetdic = mol.offset_nr_by_atom()
de = numpy.zeros((len(atmlst),3))
for k, ia in enumerate(atmlst):
shl0, shl1, p0, p1 = offsetdic[ia]
# Ground state gradients
h1ao = hcore_deriv(ia)
h1ao[:,p0:p1] += vhf1[0,:,p0:p1]
h1ao[:,:,p0:p1] += vhf1[0,:,p0:p1].transpose(0,2,1)
# oo0*2 for doubly occupied orbitals
de[k] = numpy.einsum('xpq,pq->x', h1ao, oo0) * 2
de[k] += numpy.einsum('xpq,pq->x', h1ao, dmz1doo)
de[k] -= numpy.einsum('xpq,pq->x', s1[:,p0:p1], im0[p0:p1])
de[k] -= numpy.einsum('xqp,pq->x', s1[:,p0:p1], im0[:,p0:p1])
de[k] += numpy.einsum('xij,ij->x', vhf1[1,:,p0:p1], oo0[p0:p1])
de[k] += numpy.einsum('xij,ij->x', vhf1[2,:,p0:p1], dmxpy[p0:p1,:]) * 2
de[k] += numpy.einsum('xij,ij->x', vhf1[3,:,p0:p1], dmxmy[p0:p1,:]) * 2
de[k] += numpy.einsum('xji,ij->x', vhf1[2,:,p0:p1], dmxpy[:,p0:p1]) * 2
de[k] -= numpy.einsum('xji,ij->x', vhf1[3,:,p0:p1], dmxmy[:,p0:p1]) * 2
de += _grad_solvent(with_solvent, oo0*2, dmz1doo, dmxpy*2, singlet)
log.timer('TDHF nuclear gradients', *time0)
return de
[docs]
def tdrks_grad_elec(td_grad, x_y, singlet=True, atmlst=None,
max_memory=2000, verbose=logger.INFO):
'''
See also function pyscf.grad.tdrks.grad_elec
'''
log = logger.new_logger(td_grad, verbose)
time0 = logger.process_clock(), logger.perf_counter()
mol = td_grad.mol
mf = td_grad.base._scf
mo_coeff = mf.mo_coeff
mo_energy = mf.mo_energy
mo_occ = mf.mo_occ
nao, nmo = mo_coeff.shape
nocc = (mo_occ>0).sum()
nvir = nmo - nocc
with_solvent = getattr(td_grad.base, 'with_solvent', mf.with_solvent)
x, y = x_y
xpy = (x+y).reshape(nocc,nvir).T
xmy = (x-y).reshape(nocc,nvir).T
orbv = mo_coeff[:,nocc:]
orbo = mo_coeff[:,:nocc]
dvv = numpy.einsum('ai,bi->ab', xpy, xpy) + numpy.einsum('ai,bi->ab', xmy, xmy)
doo =-numpy.einsum('ai,aj->ij', xpy, xpy) - numpy.einsum('ai,aj->ij', xmy, xmy)
dmxpy = reduce(numpy.dot, (orbv, xpy, orbo.T))
dmxmy = reduce(numpy.dot, (orbv, xmy, orbo.T))
dmzoo = reduce(numpy.dot, (orbo, doo, orbo.T))
dmzoo+= reduce(numpy.dot, (orbv, dvv, orbv.T))
mem_now = lib.current_memory()[0]
max_memory = max(2000, td_grad.max_memory*.9-mem_now)
ni = mf._numint
ni.libxc.test_deriv_order(mf.xc, 3, raise_error=True)
omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, mol.spin)
# dm0 = mf.make_rdm1(mo_coeff, mo_occ), but it is not used when computing
# fxc since rho0 is passed to fxc function.
rho0, vxc, fxc = ni.cache_xc_kernel(mf.mol, mf.grids, mf.xc,
[mo_coeff]*2, [mo_occ*.5]*2, spin=1)
f1vo, f1oo, vxc1, k1ao = \
tdrks_grad._contract_xc_kernel(td_grad, mf.xc, dmxpy,
dmzoo, True, True, singlet, max_memory)
if ni.libxc.is_hybrid_xc(mf.xc):
dm = (dmzoo, dmxpy+dmxpy.T, dmxmy-dmxmy.T)
vj, vk = mf.get_jk(mol, dm, hermi=0)
if with_solvent.equilibrium_solvation:
vj[:2] += mf.with_solvent._B_dot_x((dmzoo, dmxpy+dmxpy.T))
else:
vj[0] += mf.with_solvent._B_dot_x(dmzoo)
vk *= hyb
if omega != 0:
vk += mf.get_k(mol, dm, hermi=0, omega=omega) * (alpha-hyb)
veff0doo = vj[0] * 2 - vk[0] + f1oo[0] + k1ao[0] * 2
wvo = reduce(numpy.dot, (orbv.T, veff0doo, orbo)) * 2
if singlet:
veff = vj[1] * 2 - vk[1] + f1vo[0] * 2
else:
veff = -vk[1] + f1vo[0] * 2
veff0mop = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff))
wvo -= numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy) * 2
wvo += numpy.einsum('ac,ai->ci', veff0mop[nocc:,nocc:], xpy) * 2
veff = -vk[2]
veff0mom = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff))
wvo -= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy) * 2
wvo += numpy.einsum('ac,ai->ci', veff0mom[nocc:,nocc:], xmy) * 2
else:
vj = mf.get_j(mol, (dmzoo, dmxpy+dmxpy.T), hermi=1)
if with_solvent.equilibrium_solvation:
vj[:2] += mf.with_solvent._B_dot_x((dmzoo, dmxpy+dmxpy.T))
else:
vj[0] += mf.with_solvent._B_dot_x(dmzoo)
veff0doo = vj[0] * 2 + f1oo[0] + k1ao[0] * 2
wvo = reduce(numpy.dot, (orbv.T, veff0doo, orbo)) * 2
if singlet:
veff = vj[1] * 2 + f1vo[0] * 2
else:
veff = f1vo[0] * 2
veff0mop = reduce(numpy.dot, (mo_coeff.T, veff, mo_coeff))
wvo -= numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy) * 2
wvo += numpy.einsum('ac,ai->ci', veff0mop[nocc:,nocc:], xpy) * 2
veff0mom = numpy.zeros((nmo,nmo))
with lib.temporary_env(mf.with_solvent, equilibrium_solvation=True):
# set singlet=None, generate function for CPHF type response kernel
vresp = mf.gen_response(singlet=None, hermi=1,
with_nlc=not td_grad.base.exclude_nlc)
def fvind(x):
dm = reduce(numpy.dot, (orbv, x.reshape(nvir,nocc)*2, orbo.T))
v1ao = vresp(dm+dm.T)
return reduce(numpy.dot, (orbv.T, v1ao, orbo)).ravel()
z1 = cphf.solve(fvind, mo_energy, mo_occ, wvo,
max_cycle=td_grad.cphf_max_cycle,
tol=td_grad.cphf_conv_tol)[0]
z1 = z1.reshape(nvir,nocc)
time1 = log.timer('Z-vector using CPHF solver', *time0)
z1ao = reduce(numpy.dot, (orbv, z1, orbo.T))
veff = vresp(z1ao+z1ao.T)
im0 = numpy.zeros((nmo,nmo))
im0[:nocc,:nocc] = reduce(numpy.dot, (orbo.T, veff0doo+veff, orbo))
im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mop[nocc:,:nocc], xpy)
im0[:nocc,:nocc]+= numpy.einsum('ak,ai->ki', veff0mom[nocc:,:nocc], xmy)
im0[nocc:,nocc:] = numpy.einsum('ci,ai->ac', veff0mop[nocc:,:nocc], xpy)
im0[nocc:,nocc:]+= numpy.einsum('ci,ai->ac', veff0mom[nocc:,:nocc], xmy)
im0[nocc:,:nocc] = numpy.einsum('ki,ai->ak', veff0mop[:nocc,:nocc], xpy)*2
im0[nocc:,:nocc]+= numpy.einsum('ki,ai->ak', veff0mom[:nocc,:nocc], xmy)*2
zeta = lib.direct_sum('i+j->ij', mo_energy, mo_energy) * .5
zeta[nocc:,:nocc] = mo_energy[:nocc]
zeta[:nocc,nocc:] = mo_energy[nocc:]
dm1 = numpy.zeros((nmo,nmo))
dm1[:nocc,:nocc] = doo
dm1[nocc:,nocc:] = dvv
dm1[nocc:,:nocc] = z1
dm1[:nocc,:nocc] += numpy.eye(nocc)*2 # for ground state
im0 = reduce(numpy.dot, (mo_coeff, im0+zeta*dm1, mo_coeff.T))
# Initialize hcore_deriv with the underlying SCF object because some
# extensions (e.g. QM/MM, solvent) modifies the SCF object only.
mf_grad = td_grad.base._scf.nuc_grad_method()
hcore_deriv = mf_grad.hcore_generator(mol)
s1 = mf_grad.get_ovlp(mol)
dmz1doo = z1ao + dmzoo
oo0 = reduce(numpy.dot, (orbo, orbo.T))
if ni.libxc.is_hybrid_xc(mf.xc):
dm = (oo0, dmz1doo+dmz1doo.T, dmxpy+dmxpy.T, dmxmy-dmxmy.T)
vj, vk = td_grad.get_jk(mol, dm)
vk *= hyb
if omega != 0:
with mol.with_range_coulomb(omega):
vk += td_grad.get_k(mol, dm) * (alpha-hyb)
vj = vj.reshape(-1,3,nao,nao)
vk = vk.reshape(-1,3,nao,nao)
if singlet:
veff1 = vj * 2 - vk
else:
veff1 = numpy.vstack((vj[:2]*2-vk[:2], -vk[2:]))
else:
vj = td_grad.get_j(mol, (oo0, dmz1doo+dmz1doo.T, dmxpy+dmxpy.T))
vj = vj.reshape(-1,3,nao,nao)
veff1 = numpy.zeros((4,3,nao,nao))
if singlet:
veff1[:3] = vj * 2
else:
veff1[:2] = vj[:2] * 2
fxcz1 = tdrks_grad._contract_xc_kernel(td_grad, mf.xc, z1ao, None,
False, False, True, max_memory)[0]
veff1[0] += vxc1[1:]
veff1[1] +=(f1oo[1:] + fxcz1[1:] + k1ao[1:]*2)*2 # *2 for dmz1doo+dmz1oo.T
veff1[2] += f1vo[1:] * 2
time1 = log.timer('2e AO integral derivatives', *time1)
if atmlst is None:
atmlst = range(mol.natm)
offsetdic = mol.offset_nr_by_atom()
de = numpy.zeros((len(atmlst),3))
for k, ia in enumerate(atmlst):
shl0, shl1, p0, p1 = offsetdic[ia]
# Ground state gradients
h1ao = hcore_deriv(ia)
h1ao[:,p0:p1] += veff1[0,:,p0:p1]
h1ao[:,:,p0:p1] += veff1[0,:,p0:p1].transpose(0,2,1)
# oo0*2 for doubly occupied orbitals
e1 = numpy.einsum('xpq,pq->x', h1ao, oo0) * 2
e1 += numpy.einsum('xpq,pq->x', h1ao, dmz1doo)
e1 -= numpy.einsum('xpq,pq->x', s1[:,p0:p1], im0[p0:p1])
e1 -= numpy.einsum('xqp,pq->x', s1[:,p0:p1], im0[:,p0:p1])
e1 += numpy.einsum('xij,ij->x', veff1[1,:,p0:p1], oo0[p0:p1])
e1 += numpy.einsum('xij,ij->x', veff1[2,:,p0:p1], dmxpy[p0:p1,:]) * 2
e1 += numpy.einsum('xij,ij->x', veff1[3,:,p0:p1], dmxmy[p0:p1,:]) * 2
e1 += numpy.einsum('xji,ij->x', veff1[2,:,p0:p1], dmxpy[:,p0:p1]) * 2
e1 -= numpy.einsum('xji,ij->x', veff1[3,:,p0:p1], dmxmy[:,p0:p1]) * 2
de[k] = e1
de += _grad_solvent(with_solvent, oo0*2, dmz1doo, dmxpy*2, singlet)
log.timer('TDDFT nuclear gradients', *time0)
return de
[docs]
def tduhf_grad_elec(td_grad, x_y, atmlst=None, max_memory=2000, verbose=logger.INFO):
'''
See also function pyscf.grad.tduhf.grad_elec
'''
log = logger.new_logger(td_grad, verbose)
time0 = logger.process_clock(), logger.perf_counter()
mol = td_grad.mol
mf = td_grad.base._scf
mo_coeff = mf.mo_coeff
mo_energy = mf.mo_energy
mo_occ = mf.mo_occ
with_solvent = getattr(td_grad.base, 'with_solvent', mf.with_solvent)
occidxa = numpy.where(mo_occ[0]>0)[0]
occidxb = numpy.where(mo_occ[1]>0)[0]
viridxa = numpy.where(mo_occ[0]==0)[0]
viridxb = numpy.where(mo_occ[1]==0)[0]
nocca = len(occidxa)
noccb = len(occidxb)
nvira = len(viridxa)
nvirb = len(viridxb)
orboa = mo_coeff[0][:,occidxa]
orbob = mo_coeff[1][:,occidxb]
orbva = mo_coeff[0][:,viridxa]
orbvb = mo_coeff[1][:,viridxb]
nao = mo_coeff[0].shape[0]
nmoa = nocca + nvira
nmob = noccb + nvirb
(xa, xb), (ya, yb) = x_y
xpya = (xa+ya).reshape(nocca,nvira).T
xpyb = (xb+yb).reshape(noccb,nvirb).T
xmya = (xa-ya).reshape(nocca,nvira).T
xmyb = (xb-yb).reshape(noccb,nvirb).T
dvva = numpy.einsum('ai,bi->ab', xpya, xpya) + numpy.einsum('ai,bi->ab', xmya, xmya)
dvvb = numpy.einsum('ai,bi->ab', xpyb, xpyb) + numpy.einsum('ai,bi->ab', xmyb, xmyb)
dooa =-numpy.einsum('ai,aj->ij', xpya, xpya) - numpy.einsum('ai,aj->ij', xmya, xmya)
doob =-numpy.einsum('ai,aj->ij', xpyb, xpyb) - numpy.einsum('ai,aj->ij', xmyb, xmyb)
dmxpya = reduce(numpy.dot, (orbva, xpya, orboa.T))
dmxpyb = reduce(numpy.dot, (orbvb, xpyb, orbob.T))
dmxmya = reduce(numpy.dot, (orbva, xmya, orboa.T))
dmxmyb = reduce(numpy.dot, (orbvb, xmyb, orbob.T))
dmzooa = reduce(numpy.dot, (orboa, dooa, orboa.T))
dmzoob = reduce(numpy.dot, (orbob, doob, orbob.T))
dmzooa+= reduce(numpy.dot, (orbva, dvva, orbva.T))
dmzoob+= reduce(numpy.dot, (orbvb, dvvb, orbvb.T))
vj, vk = mf.get_jk(mol, (dmzooa, dmxpya+dmxpya.T, dmxmya-dmxmya.T,
dmzoob, dmxpyb+dmxpyb.T, dmxmyb-dmxmyb.T), hermi=0)
vj = vj.reshape(2,3,nao,nao)
vk = vk.reshape(2,3,nao,nao)
if with_solvent.equilibrium_solvation:
dmxpy = dmxpya + dmxpyb
vj[0,:2] += mf.with_solvent._B_dot_x((dmzooa+dmzoob, dmxpy+dmxpy.T))
else:
vj[0,0] += mf.with_solvent._B_dot_x(dmzooa+dmzoob)
veff0doo = vj[0,0]+vj[1,0] - vk[:,0]
wvoa = reduce(numpy.dot, (orbva.T, veff0doo[0], orboa)) * 2
wvob = reduce(numpy.dot, (orbvb.T, veff0doo[1], orbob)) * 2
veff = vj[0,1]+vj[1,1] - vk[:,1]
veff0mopa = reduce(numpy.dot, (mo_coeff[0].T, veff[0], mo_coeff[0]))
veff0mopb = reduce(numpy.dot, (mo_coeff[1].T, veff[1], mo_coeff[1]))
wvoa -= numpy.einsum('ki,ai->ak', veff0mopa[:nocca,:nocca], xpya) * 2
wvob -= numpy.einsum('ki,ai->ak', veff0mopb[:noccb,:noccb], xpyb) * 2
wvoa += numpy.einsum('ac,ai->ci', veff0mopa[nocca:,nocca:], xpya) * 2
wvob += numpy.einsum('ac,ai->ci', veff0mopb[noccb:,noccb:], xpyb) * 2
veff = -vk[:,2]
veff0moma = reduce(numpy.dot, (mo_coeff[0].T, veff[0], mo_coeff[0]))
veff0momb = reduce(numpy.dot, (mo_coeff[1].T, veff[1], mo_coeff[1]))
wvoa -= numpy.einsum('ki,ai->ak', veff0moma[:nocca,:nocca], xmya) * 2
wvob -= numpy.einsum('ki,ai->ak', veff0momb[:noccb,:noccb], xmyb) * 2
wvoa += numpy.einsum('ac,ai->ci', veff0moma[nocca:,nocca:], xmya) * 2
wvob += numpy.einsum('ac,ai->ci', veff0momb[noccb:,noccb:], xmyb) * 2
with lib.temporary_env(mf.with_solvent, equilibrium_solvation=True):
vresp = mf.gen_response(hermi=1, with_nlc=not td_grad.base.exclude_nlc)
def fvind(x):
dm1 = numpy.empty((2,nao,nao))
xa = x[0,:nvira*nocca].reshape(nvira,nocca)
xb = x[0,nvira*nocca:].reshape(nvirb,noccb)
dma = reduce(numpy.dot, (orbva, xa, orboa.T))
dmb = reduce(numpy.dot, (orbvb, xb, orbob.T))
dm1[0] = dma + dma.T
dm1[1] = dmb + dmb.T
v1 = vresp(dm1)
v1a = reduce(numpy.dot, (orbva.T, v1[0], orboa))
v1b = reduce(numpy.dot, (orbvb.T, v1[1], orbob))
return numpy.hstack((v1a.ravel(), v1b.ravel()))
z1a, z1b = ucphf.solve(fvind, mo_energy, mo_occ, (wvoa,wvob),
max_cycle=td_grad.cphf_max_cycle,
tol=td_grad.cphf_conv_tol)[0]
time1 = log.timer('Z-vector using UCPHF solver', *time0)
z1ao = numpy.empty((2,nao,nao))
z1ao[0] = reduce(numpy.dot, (orbva, z1a, orboa.T))
z1ao[1] = reduce(numpy.dot, (orbvb, z1b, orbob.T))
veff = vresp((z1ao+z1ao.transpose(0,2,1)) * .5)
im0a = numpy.zeros((nmoa,nmoa))
im0b = numpy.zeros((nmob,nmob))
im0a[:nocca,:nocca] = reduce(numpy.dot, (orboa.T, veff0doo[0]+veff[0], orboa)) * .5
im0b[:noccb,:noccb] = reduce(numpy.dot, (orbob.T, veff0doo[1]+veff[1], orbob)) * .5
im0a[:nocca,:nocca]+= numpy.einsum('ak,ai->ki', veff0mopa[nocca:,:nocca], xpya) * .5
im0b[:noccb,:noccb]+= numpy.einsum('ak,ai->ki', veff0mopb[noccb:,:noccb], xpyb) * .5
im0a[:nocca,:nocca]+= numpy.einsum('ak,ai->ki', veff0moma[nocca:,:nocca], xmya) * .5
im0b[:noccb,:noccb]+= numpy.einsum('ak,ai->ki', veff0momb[noccb:,:noccb], xmyb) * .5
im0a[nocca:,nocca:] = numpy.einsum('ci,ai->ac', veff0mopa[nocca:,:nocca], xpya) * .5
im0b[noccb:,noccb:] = numpy.einsum('ci,ai->ac', veff0mopb[noccb:,:noccb], xpyb) * .5
im0a[nocca:,nocca:]+= numpy.einsum('ci,ai->ac', veff0moma[nocca:,:nocca], xmya) * .5
im0b[noccb:,noccb:]+= numpy.einsum('ci,ai->ac', veff0momb[noccb:,:noccb], xmyb) * .5
im0a[nocca:,:nocca] = numpy.einsum('ki,ai->ak', veff0mopa[:nocca,:nocca], xpya)
im0b[noccb:,:noccb] = numpy.einsum('ki,ai->ak', veff0mopb[:noccb,:noccb], xpyb)
im0a[nocca:,:nocca]+= numpy.einsum('ki,ai->ak', veff0moma[:nocca,:nocca], xmya)
im0b[noccb:,:noccb]+= numpy.einsum('ki,ai->ak', veff0momb[:noccb,:noccb], xmyb)
zeta_a = (mo_energy[0][:,None] + mo_energy[0]) * .5
zeta_b = (mo_energy[1][:,None] + mo_energy[1]) * .5
zeta_a[nocca:,:nocca] = mo_energy[0][:nocca]
zeta_b[noccb:,:noccb] = mo_energy[1][:noccb]
zeta_a[:nocca,nocca:] = mo_energy[0][nocca:]
zeta_b[:noccb,noccb:] = mo_energy[1][noccb:]
dm1a = numpy.zeros((nmoa,nmoa))
dm1b = numpy.zeros((nmob,nmob))
dm1a[:nocca,:nocca] = dooa * .5
dm1b[:noccb,:noccb] = doob * .5
dm1a[nocca:,nocca:] = dvva * .5
dm1b[noccb:,noccb:] = dvvb * .5
dm1a[nocca:,:nocca] = z1a * .5
dm1b[noccb:,:noccb] = z1b * .5
dm1a[:nocca,:nocca] += numpy.eye(nocca) # for ground state
dm1b[:noccb,:noccb] += numpy.eye(noccb)
im0a = reduce(numpy.dot, (mo_coeff[0], im0a+zeta_a*dm1a, mo_coeff[0].T))
im0b = reduce(numpy.dot, (mo_coeff[1], im0b+zeta_b*dm1b, mo_coeff[1].T))
im0 = im0a + im0b
# Initialize hcore_deriv with the underlying SCF object because some
# extensions (e.g. QM/MM, solvent) modifies the SCF object only.
mf_grad = td_grad.base._scf.nuc_grad_method()
hcore_deriv = mf_grad.hcore_generator(mol)
s1 = mf_grad.get_ovlp(mol)
dmz1dooa = z1ao[0] + dmzooa
dmz1doob = z1ao[1] + dmzoob
oo0a = reduce(numpy.dot, (orboa, orboa.T))
oo0b = reduce(numpy.dot, (orbob, orbob.T))
as_dm1 = oo0a + oo0b + (dmz1dooa + dmz1doob) * .5
vj, vk = td_grad.get_jk(mol, (oo0a, dmz1dooa+dmz1dooa.T, dmxpya+dmxpya.T, dmxmya-dmxmya.T,
oo0b, dmz1doob+dmz1doob.T, dmxpyb+dmxpyb.T, dmxmyb-dmxmyb.T))
vj = vj.reshape(2,4,3,nao,nao)
vk = vk.reshape(2,4,3,nao,nao)
vhf1a, vhf1b = vj[0] + vj[1] - vk
time1 = log.timer('2e AO integral derivatives', *time1)
if atmlst is None:
atmlst = range(mol.natm)
offsetdic = mol.offset_nr_by_atom()
de = numpy.zeros((len(atmlst),3))
for k, ia in enumerate(atmlst):
shl0, shl1, p0, p1 = offsetdic[ia]
# Ground state gradients
h1ao = hcore_deriv(ia)
de[k] = numpy.einsum('xpq,pq->x', h1ao, as_dm1)
de[k] += numpy.einsum('xpq,pq->x', vhf1a[0,:,p0:p1], oo0a[p0:p1])
de[k] += numpy.einsum('xpq,pq->x', vhf1b[0,:,p0:p1], oo0b[p0:p1])
de[k] += numpy.einsum('xpq,qp->x', vhf1a[0,:,p0:p1], oo0a[:,p0:p1])
de[k] += numpy.einsum('xpq,qp->x', vhf1b[0,:,p0:p1], oo0b[:,p0:p1])
de[k] += numpy.einsum('xpq,pq->x', vhf1a[0,:,p0:p1], dmz1dooa[p0:p1]) * .5
de[k] += numpy.einsum('xpq,pq->x', vhf1b[0,:,p0:p1], dmz1doob[p0:p1]) * .5
de[k] += numpy.einsum('xpq,qp->x', vhf1a[0,:,p0:p1], dmz1dooa[:,p0:p1]) * .5
de[k] += numpy.einsum('xpq,qp->x', vhf1b[0,:,p0:p1], dmz1doob[:,p0:p1]) * .5
de[k] -= numpy.einsum('xpq,pq->x', s1[:,p0:p1], im0[p0:p1])
de[k] -= numpy.einsum('xqp,pq->x', s1[:,p0:p1], im0[:,p0:p1])
de[k] += numpy.einsum('xij,ij->x', vhf1a[1,:,p0:p1], oo0a[p0:p1]) * .5
de[k] += numpy.einsum('xij,ij->x', vhf1b[1,:,p0:p1], oo0b[p0:p1]) * .5
de[k] += numpy.einsum('xij,ij->x', vhf1a[2,:,p0:p1], dmxpya[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', vhf1b[2,:,p0:p1], dmxpyb[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', vhf1a[3,:,p0:p1], dmxmya[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', vhf1b[3,:,p0:p1], dmxmyb[p0:p1,:])
de[k] += numpy.einsum('xji,ij->x', vhf1a[2,:,p0:p1], dmxpya[:,p0:p1])
de[k] += numpy.einsum('xji,ij->x', vhf1b[2,:,p0:p1], dmxpyb[:,p0:p1])
de[k] -= numpy.einsum('xji,ij->x', vhf1a[3,:,p0:p1], dmxmya[:,p0:p1])
de[k] -= numpy.einsum('xji,ij->x', vhf1b[3,:,p0:p1], dmxmyb[:,p0:p1])
dm0 = oo0a + oo0b
dmz1doo = (dmz1dooa + dmz1doob) * .5
dmxpy = dmxpya + dmxpyb
de += _grad_solvent(with_solvent, dm0, dmz1doo, dmxpy)
log.timer('TDUHF nuclear gradients', *time0)
return de
[docs]
def tduks_grad_elec(td_grad, x_y, atmlst=None, max_memory=2000, verbose=logger.INFO):
'''
See also function pyscf.grad.tduks.grad_elec
'''
log = logger.new_logger(td_grad, verbose)
time0 = logger.process_clock(), logger.perf_counter()
mol = td_grad.mol
mf = td_grad.base._scf
mo_coeff = mf.mo_coeff
mo_energy = mf.mo_energy
mo_occ = mf.mo_occ
with_solvent = getattr(td_grad.base, 'with_solvent', mf.with_solvent)
occidxa = numpy.where(mo_occ[0]>0)[0]
occidxb = numpy.where(mo_occ[1]>0)[0]
viridxa = numpy.where(mo_occ[0]==0)[0]
viridxb = numpy.where(mo_occ[1]==0)[0]
nocca = len(occidxa)
noccb = len(occidxb)
nvira = len(viridxa)
nvirb = len(viridxb)
orboa = mo_coeff[0][:,occidxa]
orbob = mo_coeff[1][:,occidxb]
orbva = mo_coeff[0][:,viridxa]
orbvb = mo_coeff[1][:,viridxb]
nao = mo_coeff[0].shape[0]
nmoa = nocca + nvira
nmob = noccb + nvirb
(xa, xb), (ya, yb) = x_y
xpya = (xa+ya).reshape(nocca,nvira).T
xpyb = (xb+yb).reshape(noccb,nvirb).T
xmya = (xa-ya).reshape(nocca,nvira).T
xmyb = (xb-yb).reshape(noccb,nvirb).T
dvva = numpy.einsum('ai,bi->ab', xpya, xpya) + numpy.einsum('ai,bi->ab', xmya, xmya)
dvvb = numpy.einsum('ai,bi->ab', xpyb, xpyb) + numpy.einsum('ai,bi->ab', xmyb, xmyb)
dooa =-numpy.einsum('ai,aj->ij', xpya, xpya) - numpy.einsum('ai,aj->ij', xmya, xmya)
doob =-numpy.einsum('ai,aj->ij', xpyb, xpyb) - numpy.einsum('ai,aj->ij', xmyb, xmyb)
dmxpya = reduce(numpy.dot, (orbva, xpya, orboa.T))
dmxpyb = reduce(numpy.dot, (orbvb, xpyb, orbob.T))
dmxmya = reduce(numpy.dot, (orbva, xmya, orboa.T))
dmxmyb = reduce(numpy.dot, (orbvb, xmyb, orbob.T))
dmzooa = reduce(numpy.dot, (orboa, dooa, orboa.T))
dmzoob = reduce(numpy.dot, (orbob, doob, orbob.T))
dmzooa+= reduce(numpy.dot, (orbva, dvva, orbva.T))
dmzoob+= reduce(numpy.dot, (orbvb, dvvb, orbvb.T))
ni = mf._numint
ni.libxc.test_deriv_order(mf.xc, 3, raise_error=True)
omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, mol.spin)
# dm0 = mf.make_rdm1(mo_coeff, mo_occ), but it is not used when computing
# fxc since rho0 is passed to fxc function.
dm0 = None
rho0, vxc, fxc = ni.cache_xc_kernel(mf.mol, mf.grids, mf.xc,
mo_coeff, mo_occ, spin=1)
f1vo, f1oo, vxc1, k1ao = \
tduks_grad._contract_xc_kernel(td_grad, mf.xc, (dmxpya,dmxpyb),
(dmzooa,dmzoob), True, True, max_memory)
if ni.libxc.is_hybrid_xc(mf.xc):
dm = (dmzooa, dmxpya+dmxpya.T, dmxmya-dmxmya.T,
dmzoob, dmxpyb+dmxpyb.T, dmxmyb-dmxmyb.T)
vj, vk = mf.get_jk(mol, dm, hermi=0)
vk *= hyb
if omega != 0:
vk += mf.get_k(mol, dm, hermi=0, omega=omega) * (alpha-hyb)
vj = vj.reshape(2,3,nao,nao)
vk = vk.reshape(2,3,nao,nao)
if with_solvent.equilibrium_solvation:
dmxpy = dmxpya + dmxpyb
vj[0,:2] += mf.with_solvent._B_dot_x((dmzooa+dmzoob, dmxpy+dmxpy.T))
else:
vj[0,0] += mf.with_solvent._B_dot_x(dmzooa+dmzoob)
veff0doo = vj[0,0]+vj[1,0] - vk[:,0] + f1oo[:,0] + k1ao[:,0] * 2
wvoa = reduce(numpy.dot, (orbva.T, veff0doo[0], orboa)) * 2
wvob = reduce(numpy.dot, (orbvb.T, veff0doo[1], orbob)) * 2
veff = vj[0,1]+vj[1,1] - vk[:,1] + f1vo[:,0] * 2
veff0mopa = reduce(numpy.dot, (mo_coeff[0].T, veff[0], mo_coeff[0]))
veff0mopb = reduce(numpy.dot, (mo_coeff[1].T, veff[1], mo_coeff[1]))
wvoa -= numpy.einsum('ki,ai->ak', veff0mopa[:nocca,:nocca], xpya) * 2
wvob -= numpy.einsum('ki,ai->ak', veff0mopb[:noccb,:noccb], xpyb) * 2
wvoa += numpy.einsum('ac,ai->ci', veff0mopa[nocca:,nocca:], xpya) * 2
wvob += numpy.einsum('ac,ai->ci', veff0mopb[noccb:,noccb:], xpyb) * 2
veff = -vk[:,2]
veff0moma = reduce(numpy.dot, (mo_coeff[0].T, veff[0], mo_coeff[0]))
veff0momb = reduce(numpy.dot, (mo_coeff[1].T, veff[1], mo_coeff[1]))
wvoa -= numpy.einsum('ki,ai->ak', veff0moma[:nocca,:nocca], xmya) * 2
wvob -= numpy.einsum('ki,ai->ak', veff0momb[:noccb,:noccb], xmyb) * 2
wvoa += numpy.einsum('ac,ai->ci', veff0moma[nocca:,nocca:], xmya) * 2
wvob += numpy.einsum('ac,ai->ci', veff0momb[noccb:,noccb:], xmyb) * 2
else:
dm = (dmzooa, dmxpya+dmxpya.T,
dmzoob, dmxpyb+dmxpyb.T)
vj = mf.get_j(mol, dm, hermi=1).reshape(2,2,nao,nao)
if with_solvent.equilibrium_solvation:
dmxpy = dmxpya + dmxpyb
vj[0,:2] += mf.with_solvent._B_dot_x((dmzooa+dmzoob, dmxpy+dmxpy.T))
else:
vj[0,0] += mf.with_solvent._B_dot_x(dmzooa+dmzoob)
veff0doo = vj[0,0]+vj[1,0] + f1oo[:,0] + k1ao[:,0] * 2
wvoa = reduce(numpy.dot, (orbva.T, veff0doo[0], orboa)) * 2
wvob = reduce(numpy.dot, (orbvb.T, veff0doo[1], orbob)) * 2
veff = vj[0,1]+vj[1,1] + f1vo[:,0] * 2
veff0mopa = reduce(numpy.dot, (mo_coeff[0].T, veff[0], mo_coeff[0]))
veff0mopb = reduce(numpy.dot, (mo_coeff[1].T, veff[1], mo_coeff[1]))
wvoa -= numpy.einsum('ki,ai->ak', veff0mopa[:nocca,:nocca], xpya) * 2
wvob -= numpy.einsum('ki,ai->ak', veff0mopb[:noccb,:noccb], xpyb) * 2
wvoa += numpy.einsum('ac,ai->ci', veff0mopa[nocca:,nocca:], xpya) * 2
wvob += numpy.einsum('ac,ai->ci', veff0mopb[noccb:,noccb:], xpyb) * 2
veff0moma = numpy.zeros((nmoa,nmoa))
veff0momb = numpy.zeros((nmob,nmob))
with lib.temporary_env(mf.with_solvent, equilibrium_solvation=True):
vresp = mf.gen_response(hermi=1, with_nlc=not td_grad.base.exclude_nlc)
def fvind(x):
dm1 = numpy.empty((2,nao,nao))
xa = x[0,:nvira*nocca].reshape(nvira,nocca)
xb = x[0,nvira*nocca:].reshape(nvirb,noccb)
dma = reduce(numpy.dot, (orbva, xa, orboa.T))
dmb = reduce(numpy.dot, (orbvb, xb, orbob.T))
dm1[0] = dma + dma.T
dm1[1] = dmb + dmb.T
v1 = vresp(dm1)
v1a = reduce(numpy.dot, (orbva.T, v1[0], orboa))
v1b = reduce(numpy.dot, (orbvb.T, v1[1], orbob))
return numpy.hstack((v1a.ravel(), v1b.ravel()))
z1a, z1b = ucphf.solve(fvind, mo_energy, mo_occ, (wvoa,wvob),
max_cycle=td_grad.cphf_max_cycle,
tol=td_grad.cphf_conv_tol)[0]
time1 = log.timer('Z-vector using UCPHF solver', *time0)
z1ao = numpy.empty((2,nao,nao))
z1ao[0] = reduce(numpy.dot, (orbva, z1a, orboa.T))
z1ao[1] = reduce(numpy.dot, (orbvb, z1b, orbob.T))
veff = vresp((z1ao+z1ao.transpose(0,2,1)) * .5)
im0a = numpy.zeros((nmoa,nmoa))
im0b = numpy.zeros((nmob,nmob))
im0a[:nocca,:nocca] = reduce(numpy.dot, (orboa.T, veff0doo[0]+veff[0], orboa)) * .5
im0b[:noccb,:noccb] = reduce(numpy.dot, (orbob.T, veff0doo[1]+veff[1], orbob)) * .5
im0a[:nocca,:nocca]+= numpy.einsum('ak,ai->ki', veff0mopa[nocca:,:nocca], xpya) * .5
im0b[:noccb,:noccb]+= numpy.einsum('ak,ai->ki', veff0mopb[noccb:,:noccb], xpyb) * .5
im0a[:nocca,:nocca]+= numpy.einsum('ak,ai->ki', veff0moma[nocca:,:nocca], xmya) * .5
im0b[:noccb,:noccb]+= numpy.einsum('ak,ai->ki', veff0momb[noccb:,:noccb], xmyb) * .5
im0a[nocca:,nocca:] = numpy.einsum('ci,ai->ac', veff0mopa[nocca:,:nocca], xpya) * .5
im0b[noccb:,noccb:] = numpy.einsum('ci,ai->ac', veff0mopb[noccb:,:noccb], xpyb) * .5
im0a[nocca:,nocca:]+= numpy.einsum('ci,ai->ac', veff0moma[nocca:,:nocca], xmya) * .5
im0b[noccb:,noccb:]+= numpy.einsum('ci,ai->ac', veff0momb[noccb:,:noccb], xmyb) * .5
im0a[nocca:,:nocca] = numpy.einsum('ki,ai->ak', veff0mopa[:nocca,:nocca], xpya)
im0b[noccb:,:noccb] = numpy.einsum('ki,ai->ak', veff0mopb[:noccb,:noccb], xpyb)
im0a[nocca:,:nocca]+= numpy.einsum('ki,ai->ak', veff0moma[:nocca,:nocca], xmya)
im0b[noccb:,:noccb]+= numpy.einsum('ki,ai->ak', veff0momb[:noccb,:noccb], xmyb)
zeta_a = (mo_energy[0][:,None] + mo_energy[0]) * .5
zeta_b = (mo_energy[1][:,None] + mo_energy[1]) * .5
zeta_a[nocca:,:nocca] = mo_energy[0][:nocca]
zeta_b[noccb:,:noccb] = mo_energy[1][:noccb]
zeta_a[:nocca,nocca:] = mo_energy[0][nocca:]
zeta_b[:noccb,noccb:] = mo_energy[1][noccb:]
dm1a = numpy.zeros((nmoa,nmoa))
dm1b = numpy.zeros((nmob,nmob))
dm1a[:nocca,:nocca] = dooa * .5
dm1b[:noccb,:noccb] = doob * .5
dm1a[nocca:,nocca:] = dvva * .5
dm1b[noccb:,noccb:] = dvvb * .5
dm1a[nocca:,:nocca] = z1a * .5
dm1b[noccb:,:noccb] = z1b * .5
dm1a[:nocca,:nocca] += numpy.eye(nocca) # for ground state
dm1b[:noccb,:noccb] += numpy.eye(noccb)
im0a = reduce(numpy.dot, (mo_coeff[0], im0a+zeta_a*dm1a, mo_coeff[0].T))
im0b = reduce(numpy.dot, (mo_coeff[1], im0b+zeta_b*dm1b, mo_coeff[1].T))
im0 = im0a + im0b
# Initialize hcore_deriv with the underlying SCF object because some
# extensions (e.g. QM/MM, solvent) modifies the SCF object only.
mf_grad = td_grad.base._scf.nuc_grad_method()
hcore_deriv = mf_grad.hcore_generator(mol)
s1 = mf_grad.get_ovlp(mol)
dmz1dooa = z1ao[0] + dmzooa
dmz1doob = z1ao[1] + dmzoob
oo0a = reduce(numpy.dot, (orboa, orboa.T))
oo0b = reduce(numpy.dot, (orbob, orbob.T))
as_dm1 = oo0a + oo0b + (dmz1dooa + dmz1doob) * .5
if ni.libxc.is_hybrid_xc(mf.xc):
dm = (oo0a, dmz1dooa+dmz1dooa.T, dmxpya+dmxpya.T, dmxmya-dmxmya.T,
oo0b, dmz1doob+dmz1doob.T, dmxpyb+dmxpyb.T, dmxmyb-dmxmyb.T)
vj, vk = td_grad.get_jk(mol, dm)
vj = vj.reshape(2,4,3,nao,nao)
vk = vk.reshape(2,4,3,nao,nao) * hyb
if omega != 0:
with mol.with_range_coulomb(omega):
vk += td_grad.get_k(mol, dm).reshape(2,4,3,nao,nao) * (alpha-hyb)
veff1 = vj[0] + vj[1] - vk
else:
dm = (oo0a, dmz1dooa+dmz1dooa.T, dmxpya+dmxpya.T,
oo0b, dmz1doob+dmz1doob.T, dmxpyb+dmxpyb.T)
vj = td_grad.get_j(mol, dm).reshape(2,3,3,nao,nao)
veff1 = numpy.zeros((2,4,3,nao,nao))
veff1[:,:3] = vj[0] + vj[1]
fxcz1 = tduks_grad._contract_xc_kernel(td_grad, mf.xc, z1ao, None,
False, False, max_memory)[0]
veff1[:,0] += vxc1[:,1:]
veff1[:,1] +=(f1oo[:,1:] + fxcz1[:,1:] + k1ao[:,1:]*2)*2 # *2 for dmz1doo+dmz1oo.T
veff1[:,2] += f1vo[:,1:] * 2
veff1a, veff1b = veff1
time1 = log.timer('2e AO integral derivatives', *time1)
if atmlst is None:
atmlst = range(mol.natm)
offsetdic = mol.offset_nr_by_atom()
de = numpy.zeros((len(atmlst),3))
for k, ia in enumerate(atmlst):
shl0, shl1, p0, p1 = offsetdic[ia]
# Ground state gradients
h1ao = hcore_deriv(ia)
de[k] = numpy.einsum('xpq,pq->x', h1ao, as_dm1)
de[k] += numpy.einsum('xpq,pq->x', veff1a[0,:,p0:p1], oo0a[p0:p1])
de[k] += numpy.einsum('xpq,pq->x', veff1b[0,:,p0:p1], oo0b[p0:p1])
de[k] += numpy.einsum('xpq,qp->x', veff1a[0,:,p0:p1], oo0a[:,p0:p1])
de[k] += numpy.einsum('xpq,qp->x', veff1b[0,:,p0:p1], oo0b[:,p0:p1])
de[k] += numpy.einsum('xpq,pq->x', veff1a[0,:,p0:p1], dmz1dooa[p0:p1]) * .5
de[k] += numpy.einsum('xpq,pq->x', veff1b[0,:,p0:p1], dmz1doob[p0:p1]) * .5
de[k] += numpy.einsum('xpq,qp->x', veff1a[0,:,p0:p1], dmz1dooa[:,p0:p1]) * .5
de[k] += numpy.einsum('xpq,qp->x', veff1b[0,:,p0:p1], dmz1doob[:,p0:p1]) * .5
de[k] -= numpy.einsum('xpq,pq->x', s1[:,p0:p1], im0[p0:p1])
de[k] -= numpy.einsum('xqp,pq->x', s1[:,p0:p1], im0[:,p0:p1])
de[k] += numpy.einsum('xij,ij->x', veff1a[1,:,p0:p1], oo0a[p0:p1]) * .5
de[k] += numpy.einsum('xij,ij->x', veff1b[1,:,p0:p1], oo0b[p0:p1]) * .5
de[k] += numpy.einsum('xij,ij->x', veff1a[2,:,p0:p1], dmxpya[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', veff1b[2,:,p0:p1], dmxpyb[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', veff1a[3,:,p0:p1], dmxmya[p0:p1,:])
de[k] += numpy.einsum('xij,ij->x', veff1b[3,:,p0:p1], dmxmyb[p0:p1,:])
de[k] += numpy.einsum('xji,ij->x', veff1a[2,:,p0:p1], dmxpya[:,p0:p1])
de[k] += numpy.einsum('xji,ij->x', veff1b[2,:,p0:p1], dmxpyb[:,p0:p1])
de[k] -= numpy.einsum('xji,ij->x', veff1a[3,:,p0:p1], dmxmya[:,p0:p1])
de[k] -= numpy.einsum('xji,ij->x', veff1b[3,:,p0:p1], dmxmyb[:,p0:p1])
dm0 = oo0a + oo0b
dmz1doo = (dmz1dooa + dmz1doob) * .5
dmxpy = dmxpya + dmxpyb
de += _grad_solvent(with_solvent, dm0, dmz1doo, dmxpy)
log.timer('TDUHF nuclear gradients', *time0)
return de
def _grad_solvent(pcmobj, dm0, dmz1doo, dmxpy, singlet=True):
'''Energy derivatives associated to derivatives of B tensor'''
if pcmobj._intermediates is None:
pcmobj.build()
dielectric = pcmobj.eps
if dielectric > 0:
f_epsilon = (dielectric-1.)/dielectric
else:
f_epsilon = 1
r_vdw = pcmobj._intermediates['r_vdw' ]
ylm_1sph = pcmobj._intermediates['ylm_1sph' ]
ui = pcmobj._intermediates['ui' ]
Lmat = pcmobj._intermediates['Lmat' ]
cached_pol = pcmobj._intermediates['cached_pol']
# First order nuclei-solvent-electron contribution
tmp = _grad_ne(pcmobj, dmz1doo,
r_vdw, ui, ylm_1sph, cached_pol, Lmat)
de = .5 * f_epsilon * tmp
# First order electron-solvent-electron contribution
tmp = _grad_ee(pcmobj, (dm0, dmxpy), (dmz1doo, dmxpy),
r_vdw, ui, ylm_1sph, cached_pol, Lmat)
de += .5 * f_epsilon * tmp[0] # (dm0 * dmz1doo)
if singlet and pcmobj.equilibrium_solvation:
de += .5 * f_epsilon * tmp[1] # (dmxpy * dmxpy)
return de
def _grad_nn(pcmobj, r_vdw, ui, ylm_1sph, cached_pol, L):
'''nuclei-solvent-nuclei term'''
mol = pcmobj.mol
natm = mol.natm
coords_1sph, weights_1sph = ddcosmo.make_grids_one_sphere(pcmobj.lebedev_order)
ngrid_1sph = coords_1sph.shape[0]
lmax = pcmobj.lmax
nlm = (lmax+1)**2
atom_coords = mol.atom_coords()
atom_charges = mol.atom_charges()
fi0 = ddcosmo.make_fi(pcmobj, r_vdw)
fi1 = ddcosmo_grad.make_fi1(pcmobj, pcmobj.get_atomic_radii())
fi1[:,:,ui==0] = 0
ui1 = -fi1
de = numpy.zeros((natm,3))
cav_coords = (atom_coords.reshape(natm,1,3)
+ numpy.einsum('r,gx->rgx', r_vdw, coords_1sph))
v_phi = numpy.zeros((natm, ngrid_1sph))
for ia in range(natm):
# Note (-) sign is not applied to atom_charges, because (-) is explicitly
# included in rhs and L matrix
d_rs = atom_coords.reshape(-1,1,3) - cav_coords[ia]
v_phi[ia] = numpy.einsum('z,zp->p', atom_charges, 1./lib.norm(d_rs,axis=2))
phi0 = -numpy.einsum('n,xn,jn,jn->jx', weights_1sph, ylm_1sph, ui, v_phi)
psi0 = numpy.zeros((natm, nlm))
for ia in range(natm):
psi0[ia,0] += numpy.sqrt(4*numpy.pi)/r_vdw[ia] * mol.atom_charge(ia)
v_phi0 = numpy.empty((natm,ngrid_1sph))
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
d_rs = atom_coords.reshape(-1,1,3) - cav_coords
v_phi0[ia] = numpy.einsum('z,zp->p', atom_charges, 1./lib.norm(d_rs,axis=2))
phi1 = -numpy.einsum('n,ln,azjn,jn->azjl', weights_1sph, ylm_1sph, ui1, v_phi0)
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
for ja in range(natm):
rs = atom_coords[ja] - cav_coords
d_rs = lib.norm(rs, axis=1)
v_phi = atom_charges[ja] * numpy.einsum('px,p->px', rs, 1./d_rs**3)
tmp = numpy.einsum('n,ln,n,nx->xl', weights_1sph, ylm_1sph, ui[ia], v_phi)
phi1[ja,:,ia] += tmp # response of the other atoms
phi1[ia,:,ia] -= tmp # response of cavity grids
L1 = ddcosmo_grad.make_L1(pcmobj, r_vdw, ylm_1sph, fi0)
Xvec0 = numpy.linalg.solve(L.reshape(natm*nlm,-1), phi0.ravel())
Xvec0 = Xvec0.reshape(natm,nlm)
phi1 -= numpy.einsum('aziljm,jm->azil', L1, Xvec0)
LS0 = numpy.linalg.solve(L.reshape(natm*nlm,-1).T, psi0.ravel())
LS0 = LS0.reshape(natm,nlm)
de += numpy.einsum('il,azil->az', LS0, phi1)
return de
def _grad_ne(pcmobj, dm, r_vdw, ui, ylm_1sph, cached_pol, L):
'''nuclear charge-electron density cross term'''
mol = pcmobj.mol
natm = mol.natm
coords_1sph, weights_1sph = ddcosmo.make_grids_one_sphere(pcmobj.lebedev_order)
ngrid_1sph = coords_1sph.shape[0]
lmax = pcmobj.lmax
nlm = (lmax+1)**2
nao = mol.nao
atom_coords = mol.atom_coords()
atom_charges = mol.atom_charges()
grids = pcmobj.grids
aoslices = mol.aoslice_by_atom()
#extern_point_idx = ui > 0
fi0 = ddcosmo.make_fi(pcmobj, r_vdw)
fi1 = ddcosmo_grad.make_fi1(pcmobj, pcmobj.get_atomic_radii())
fi1[:,:,ui==0] = 0
ui1 = -fi1
dms = numpy.asarray(dm)
is_single_dm = dms.ndim == 2
dms = dms.reshape(-1,nao,nao)
n_dm = dms.shape[0]
de = numpy.zeros((n_dm,natm,3))
cav_coords = (atom_coords.reshape(natm,1,3)
+ numpy.einsum('r,gx->rgx', r_vdw, coords_1sph))
v_phi = numpy.zeros((natm, ngrid_1sph))
for ia in range(natm):
# Note (-) sign is not applied to atom_charges, because (-) is explicitly
# included in rhs and L matrix
d_rs = atom_coords.reshape(-1,1,3) - cav_coords[ia]
v_phi[ia] = numpy.einsum('z,zp->p', atom_charges, 1./lib.norm(d_rs,axis=2))
phi0 = -numpy.einsum('n,xn,jn,jn->jx', weights_1sph, ylm_1sph, ui, v_phi)
Xvec0 = numpy.linalg.solve(L.reshape(natm*nlm,-1), phi0.ravel())
Xvec0 = Xvec0.reshape(natm,nlm)
v_phi0 = numpy.empty((natm,ngrid_1sph))
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
d_rs = atom_coords.reshape(-1,1,3) - cav_coords
v_phi0[ia] = numpy.einsum('z,zp->p', atom_charges, 1./lib.norm(d_rs,axis=2))
phi1 = -numpy.einsum('n,ln,azjn,jn->azjl', weights_1sph, ylm_1sph, ui1, v_phi0)
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
for ja in range(natm):
rs = atom_coords[ja] - cav_coords
d_rs = lib.norm(rs, axis=1)
v_phi = atom_charges[ja] * numpy.einsum('px,p->px', rs, 1./d_rs**3)
tmp = numpy.einsum('n,ln,n,nx->xl', weights_1sph, ylm_1sph, ui[ia], v_phi)
phi1[ja,:,ia] += tmp # response of the other atoms
phi1[ia,:,ia] -= tmp # response of cavity grids
L1 = ddcosmo_grad.make_L1(pcmobj, r_vdw, ylm_1sph, fi0)
phi1 -= numpy.einsum('aziljm,jm->azil', L1, Xvec0)
Xvec1 = numpy.linalg.solve(L.reshape(natm*nlm,-1), phi1.reshape(-1,natm*nlm).T)
Xvec1 = Xvec1.T.reshape(natm,3,natm,nlm)
for ia, (coords, weight, weight1) in enumerate(rks_grad.grids_response_cc(grids)):
ao = mol.eval_gto('GTOval_sph_deriv1', coords)
fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)]
fac_pol = ddcosmo._vstack_factor_fak_pol(fak_pol, lmax)
scaled_weights = numpy.einsum('azm,mn->azn', Xvec1[:,:,ia], fac_pol)
scaled_weights *= weight
aow = numpy.einsum('gi,azg->azgi', ao[0], scaled_weights)
de -= numpy.einsum('nij,gi,azgj->naz', dms, ao[0], aow)
aow0 = numpy.einsum('gi,g->gi', ao[0], weight)
aow1 = numpy.einsum('gi,zxg->zxgi', ao[0], weight1)
den0 = numpy.einsum('nij,gi,zxgj->nzxg', dms, ao[0], aow1)
de -= numpy.einsum('m,mg,nzxg->nzx', Xvec0[ia], fac_pol, den0)
eta_nj = numpy.einsum('m,mg->g', Xvec0[ia], fac_pol)
dm_ao = lib.einsum('nij,gj->ngi', dms, aow0)
dm_ao += lib.einsum('nji,gj->ngi', dms, aow0)
for ja in range(natm):
shl0, shl1, p0, p1 = aoslices[ja]
den1 = numpy.einsum('ngi,xgi->nxg', dm_ao[:,:,p0:p1], ao[1:,:,p0:p1])
detmp = numpy.einsum('g,nxg->nx', eta_nj, den1)
de[:,ja] += detmp
de[:,ia] -= detmp
psi0 = numpy.zeros((natm, nlm))
for ia in range(natm):
psi0[ia,0] += numpy.sqrt(4*numpy.pi)/r_vdw[ia] * mol.atom_charge(ia)
LS0 = numpy.linalg.solve(L.reshape(natm*nlm,-1).T, psi0.ravel())
LS0 = LS0.reshape(natm,nlm)
LS1 = numpy.einsum('il,aziljm->azjm', LS0, L1)
LS1 = numpy.linalg.solve(L.reshape(natm*nlm,-1).T, LS1.reshape(-1,natm*nlm).T)
LS1 = LS1.T.reshape(natm,3,natm,nlm)
int3c2e = mol._add_suffix('int3c2e')
int3c2e_ip1 = mol._add_suffix('int3c2e_ip1')
cintopt = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, int3c2e)
cintopt_ip1 = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, int3c2e_ip1)
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
#fakemol = gto.fakemol_for_charges(cav_coords[ui[ia]>0])
fakemol = gto.fakemol_for_charges(cav_coords)
wtmp = numpy.einsum('l,n,ln->ln', LS0[ia], weights_1sph, ylm_1sph)
v_nj = df.incore.aux_e2(mol, fakemol, intor=int3c2e, aosym='s1', cintopt=cintopt)
jaux = numpy.einsum('ijg,nij->ng', v_nj, dms)
de -= numpy.einsum('azl,g,lg,g,ng->naz', LS1[:,:,ia], weights_1sph, ylm_1sph, ui[ia], jaux)
de += numpy.einsum('lg,azg,ng->naz', wtmp, ui1[:,:,ia], jaux)
v_e1_nj = df.incore.aux_e2(mol, fakemol, intor=int3c2e_ip1, comp=3,
aosym='s1', cintopt=cintopt_ip1)
for ja in range(natm):
shl0, shl1, p0, p1 = aoslices[ja]
jaux1 = numpy.einsum('xijg,nij->nxg', v_e1_nj[:,p0:p1], dms[:,p0:p1])
jaux1 += numpy.einsum('xijg,nji->nxg', v_e1_nj[:,p0:p1], dms[:,:,p0:p1])
detmp = numpy.einsum('lg,g,nxg->nx', wtmp, ui[ia], jaux1)
de[:,ja] -= detmp
de[:,ia] += detmp
if is_single_dm:
de = de[0]
return de
def _grad_ee(pcmobj, dm1, dm2, r_vdw, ui, ylm_1sph, cached_pol, L):
'''electron density-electorn density term'''
mol = pcmobj.mol
mol = pcmobj.mol
natm = mol.natm
nao = mol.nao
lmax = pcmobj.lmax
nlm = (lmax+1)**2
atom_coords = mol.atom_coords()
aoslices = mol.aoslice_by_atom()
grids = pcmobj.grids
coords_1sph, weights_1sph = ddcosmo.make_grids_one_sphere(pcmobj.lebedev_order)
#extern_point_idx = ui > 0
fi0 = ddcosmo.make_fi(pcmobj, r_vdw)
fi1 = ddcosmo_grad.make_fi1(pcmobj, pcmobj.get_atomic_radii())
fi1[:,:,ui==0] = 0
ui1 = -fi1
dm1s = numpy.asarray(dm1)
dm2s = numpy.asarray(dm2)
is_single_dm = dm1s.ndim == 2
dm1s = dm1s.reshape(-1,nao,nao)
dm2s = dm2s.reshape(-1,nao,nao)
n_dm = dm1s.shape[0]
assert dm2s.shape[0] == n_dm
de = numpy.zeros((n_dm,natm,3))
ni = numint.NumInt()
make_rho, nset, nao = ni._gen_rho_evaluator(mol, dm1s, hermi=0)
den = numpy.empty((n_dm,grids.weights.size))
p1 = 0
for ao, mask, weight, coords in ni.block_loop(mol, grids, nao, 0):
p0, p1 = p1, p1 + weight.size
for i in range(n_dm):
den[i,p0:p1] = make_rho(i, ao, mask, 'LDA') * weight
psi0_dm1 = numpy.zeros((n_dm, natm, nlm))
i1 = 0
for ia in range(natm):
fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)]
fac_pol = ddcosmo._vstack_factor_fak_pol(fak_pol, lmax)
i0, i1 = i1, i1 + fac_pol.shape[1]
psi0_dm1[:,ia] = -numpy.einsum('mg,ng->nm', fac_pol, den[:,i0:i1])
LS0 = numpy.linalg.solve(L.reshape(natm*nlm,-1).T, psi0_dm1.reshape(n_dm,-1).T)
LS0 = LS0.T.reshape(n_dm,natm,nlm)
phi0_dm1 = numpy.zeros((n_dm,natm,nlm))
phi0_dm2 = numpy.zeros((n_dm,natm,nlm))
phi1_dm1 = numpy.zeros((n_dm,natm,3,natm,nlm))
int3c2e = mol._add_suffix('int3c2e')
int3c2e_ip1 = mol._add_suffix('int3c2e_ip1')
cintopt = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, int3c2e)
cintopt_ip1 = gto.moleintor.make_cintopt(mol._atm, mol._bas, mol._env, int3c2e_ip1)
for ia in range(natm):
cav_coords = atom_coords[ia] + r_vdw[ia] * coords_1sph
#fakemol = gto.fakemol_for_charges(cav_coords[ui[ia]>0])
fakemol = gto.fakemol_for_charges(cav_coords)
v_nj = df.incore.aux_e2(mol, fakemol, intor=int3c2e, aosym='s1', cintopt=cintopt)
v_phi = numpy.einsum('ijg,nij->ng', v_nj, dm1s)
phi0_dm1[:,ia] = numpy.einsum('g,lg,g,ng->nl', weights_1sph, ylm_1sph, ui[ia], v_phi)
phi1_dm1[:,:,:,ia] += numpy.einsum('g,lg,azg,ng->nazl', weights_1sph, ylm_1sph, ui1[:,:,ia], v_phi)
jaux = numpy.einsum('ijg,nij->ng', v_nj, dm2s)
de += numpy.einsum('nl,g,lg,azg,ng->naz', LS0[:,ia], weights_1sph, ylm_1sph, ui1[:,:,ia], jaux)
v_e1_nj = df.incore.aux_e2(mol, fakemol, intor=int3c2e_ip1, comp=3,
aosym='s1', cintopt=cintopt_ip1)
wtmp = numpy.einsum('g,lg,g->lg', weights_1sph, ylm_1sph, ui[ia])
phi0_dm2[:,ia] = numpy.einsum('lg,ng->nl', wtmp, jaux)
for ja in range(natm):
shl0, shl1, p0, p1 = aoslices[ja]
jaux1 = numpy.einsum('xijg,nij->nxg', v_e1_nj[:,p0:p1], dm2s[:,p0:p1])
jaux1 += numpy.einsum('xijg,nji->nxg', v_e1_nj[:,p0:p1], dm2s[:,:,p0:p1])
detmp = numpy.einsum('nl,lg,nxg->nx', LS0[:,ia], wtmp, jaux1)
de[:,ja] -= detmp
de[:,ia] += detmp
tmp = numpy.einsum('xijg,nij->nxg', v_e1_nj[:,p0:p1], dm1s[:,p0:p1])
tmp += numpy.einsum('xijg,nji->nxg', v_e1_nj[:,p0:p1], dm1s[:,:,p0:p1])
phitmp = numpy.einsum('lg,nxg->nxl', wtmp, tmp)
phi1_dm1[:,ja,:,ia] -= phitmp
phi1_dm1[:,ia,:,ia] += phitmp
Xvec0 = numpy.linalg.solve(L.reshape(natm*nlm,-1), phi0_dm1.reshape(n_dm,-1).T)
Xvec0 = Xvec0.T.reshape(n_dm,natm,nlm)
L1 = ddcosmo_grad.make_L1(pcmobj, r_vdw, ylm_1sph, fi0)
phi1_dm1 -= numpy.einsum('aziljm,njm->nazil', L1, Xvec0)
Xvec1 = numpy.linalg.solve(L.reshape(natm*nlm,-1), phi1_dm1.reshape(-1,natm*nlm).T)
Xvec1 = Xvec1.T.reshape(n_dm,natm,3,natm,nlm)
psi1_dm1 = numpy.zeros((n_dm,natm,3,natm,nlm))
for ia, (coords, weight, weight1) in enumerate(rks_grad.grids_response_cc(grids)):
ao = mol.eval_gto('GTOval_sph_deriv1', coords)
fak_pol, leak_idx = cached_pol[mol.atom_symbol(ia)]
fac_pol = ddcosmo._vstack_factor_fak_pol(fak_pol, lmax)
scaled_weights = numpy.einsum('nazm,mg->nazg', Xvec1[:,:,:,ia], fac_pol)
scaled_weights *= weight
aow = numpy.einsum('gi,nazg->nazgi', ao[0], scaled_weights)
de -= numpy.einsum('nij,gi,nazgj->naz', dm2s, ao[0], aow)
aow0 = numpy.einsum('gi,g->gi', ao[0], weight)
aow1 = numpy.einsum('gi,zxg->zxgi', ao[0], weight1)
den0 = numpy.einsum('nij,gi,zxgj->nzxg', dm1s, ao[0], aow1)
psi1_dm1[:,:,:,ia] -= numpy.einsum('mg,nzxg->nzxm', fac_pol, den0)
dm_ao = lib.einsum('nij,gj->ngi', dm1s, aow0)
dm_ao += lib.einsum('nji,gj->ngi', dm1s, aow0)
for ja in range(natm):
shl0, shl1, p0, p1 = aoslices[ja]
den1 = numpy.einsum('ngi,xgi->nxg', dm_ao[:,:,p0:p1], ao[1:,:,p0:p1])
psitmp = numpy.einsum('mg,nxg->nxm', fac_pol, den1)
psi1_dm1[:,ja,:,ia] += psitmp
psi1_dm1[:,ia,:,ia] -= psitmp
eta_nj = numpy.einsum('nm,mg->ng', Xvec0[:,ia], fac_pol)
den0 = numpy.einsum('nij,gi,zxgj->nzxg', dm2s, ao[0], aow1)
de -= numpy.einsum('ng,nzxg->nzx', eta_nj, den0)
dm_ao = lib.einsum('nij,gj->ngi', dm2s, aow0)
dm_ao += lib.einsum('nji,gj->ngi', dm2s, aow0)
for ja in range(natm):
shl0, shl1, p0, p1 = aoslices[ja]
den1 = lib.einsum('ngi,xgi->nxg', dm_ao[:,:,p0:p1], ao[1:,:,p0:p1])
detmp = numpy.einsum('ng,nxg->nx', eta_nj, den1)
de[:,ja] += detmp
de[:,ia] -= detmp
psi1_dm1 -= numpy.einsum('nil,aziljm->nazjm', LS0, L1)
LS1 = numpy.linalg.solve(L.reshape(natm*nlm,-1).T, psi1_dm1.reshape(-1,natm*nlm).T)
LS1 = LS1.T.reshape(n_dm,natm,3,natm,nlm)
de += numpy.einsum('nazjx,njx->naz', LS1, phi0_dm2)
if is_single_dm:
de = de[0]
return de