#!/usr/bin/env python
# Copyright 2014-2018,2021 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>
#
'''Analytic PP integrals. See also pyscf/pbc/gto/pesudo/pp.py
For GTH/HGH PPs, see:
Goedecker, Teter, Hutter, PRB 54, 1703 (1996)
Hartwigsen, Goedecker, and Hutter, PRB 58, 3641 (1998)
'''
import ctypes
import numpy
import numpy as np
import scipy.special
from pyscf import lib
from pyscf import gto
from pyscf import __config__
from pyscf.pbc.lib.kpts_helper import gamma_point
EPS_PPL = getattr(__config__, "pbc_gto_pseudo_eps_ppl", 1e-2)
HL_TABLE_SLOTS = 7
ATOM_OF = 0
ANG_OF = 1
HL_DIM_OF = 2
HL_DATA_OF = 3
HL_OFFSET0 = 4
HF_OFFSET1 = 5
HF_OFFSET2 = 6
libpbc = lib.load_library('libpbc')
[docs]
def get_pp_loc_part1(cell, kpts=None):
'''PRB, 58, 3641 Eq (1), integrals associated to erf
'''
raise NotImplementedError
[docs]
def get_gth_vlocG_part1(cell, Gv):
'''PRB, 58, 3641 Eq (5) first term
'''
from pyscf.pbc import tools
coulG = tools.get_coulG(cell, Gv=Gv)
G2 = numpy.einsum('ix,ix->i', Gv, Gv)
G0idx = numpy.where(G2==0)[0]
if cell.dimension == 3 or cell.dimension == 0 or cell.low_dim_ft_type == 'inf_vacuum':
vlocG = numpy.zeros((cell.natm, len(G2)))
for ia in range(cell.natm):
Zia = cell.atom_charge(ia)
symb = cell.atom_symbol(ia)
# Note the signs -- potential here is positive
vlocG[ia] = Zia * coulG
if symb in cell._pseudo:
pp = cell._pseudo[symb]
rloc, nexp, cexp = pp[1:3+1]
vlocG[ia] *= numpy.exp(-0.5*rloc**2 * G2)
# alpha parameters from the non-divergent Hartree+Vloc G=0 term.
vlocG[ia,G0idx] += -2*numpy.pi*Zia*rloc**2
elif cell.dimension == 2:
# The following 2D ewald summation is taken from:
# Minary, Tuckerman, Pihakari, Martyna J. Chem. Phys. 116, 5351 (2002)
vlocG = numpy.zeros((cell.natm,len(G2)))
b = cell.reciprocal_vectors()
inv_area = numpy.linalg.norm(numpy.cross(b[0], b[1]))/(2*numpy.pi)**2
lzd2 = cell.vol * inv_area / 2
lz = lzd2*2.
G2[G0idx] = 1e200
Gxy = numpy.linalg.norm(Gv[:,:2],axis=1)
Gz = abs(Gv[:,2])
for ia in range(cell.natm):
Zia = cell.atom_charge(ia)
symb = cell.atom_symbol(ia)
if symb not in cell._pseudo:
vlocG[ia] = Zia * coulG
continue
pp = cell._pseudo[symb]
rloc, nexp, cexp = pp[1:3+1]
ew_eta = 1./numpy.sqrt(2)/rloc
JexpG2 = 4*numpy.pi / G2 * numpy.exp(-G2/(4*ew_eta**2))
fac = 4*numpy.pi / G2 * numpy.cos(Gz*lzd2)
JexpG2 -= fac * numpy.exp(-Gxy*lzd2)
eta_z1 = (ew_eta**2 * lz + Gxy) / (2.*ew_eta)
eta_z2 = (ew_eta**2 * lz - Gxy) / (2.*ew_eta)
JexpG2 += fac * 0.5*(numpy.exp(-eta_z1**2)*scipy.special.erfcx(eta_z2) +
numpy.exp(-eta_z2**2)*scipy.special.erfcx(eta_z1))
vlocG[ia,:] = Zia * JexpG2
JexpG0 = ( - numpy.pi * lz**2 / 2. * scipy.special.erf( ew_eta * lzd2 )
+ numpy.pi/ew_eta**2 * scipy.special.erfc(ew_eta*lzd2)
- numpy.sqrt(numpy.pi)*lz/ew_eta * numpy.exp( - (ew_eta*lzd2)**2 ) )
vlocG[ia,G0idx] = -2*numpy.pi*Zia*rloc**2 + Zia*JexpG0
else:
raise NotImplementedError('Low dimension ft_type %s'
' not implemented for dimension %d' %
(cell.low_dim_ft_type, cell.dimension))
return vlocG
# part2 Vnuc - Vloc
[docs]
def get_pp_loc_part2(cell, kpts=None):
'''PRB, 58, 3641 Eq (1), integrals associated to C1, C2, C3, C4
'''
if kpts is None or gamma_point(kpts):
vpploc = [get_pp_loc_part2_gamma(cell)]
else:
from pyscf.pbc.df.aft import _IntPPBuilder
vpploc = _IntPPBuilder(cell, kpts).get_pp_loc_part2()
if kpts is None or numpy.shape(kpts) == (3,):
vpploc = vpploc[0]
return vpploc
[docs]
def get_pp_loc_part2_gamma(cell):
from pyscf.pbc.df import incore
from pyscf.pbc.gto import build_neighbor_list_for_shlpairs, free_neighbor_list
fake_cells = {}
for cn in range(1, 5):
fake_cell = fake_cell_vloc(cell, cn)
fake_cell.precision = EPS_PPL
if fake_cell.nbas > 0:
fake_cells[cn] = fake_cell
if not fake_cells:
if any(cell.atom_symbol(ia) in cell._pseudo for ia in range(cell.natm)):
pass
else:
lib.logger.warn(cell, 'cell.pseudo was specified but its elements %s '
'were not found in the system.', cell._pseudo.keys())
return 0
intors = ('int3c2e', 'int3c1e', 'int3c1e_r2_origk',
'int3c1e_r4_origk', 'int3c1e_r6_origk')
kptij_lst = numpy.zeros((1,2,3))
Ls = cell.get_lattice_Ls()
buf = None
for i, (cn, fake_cell) in enumerate(fake_cells.items()):
neighbor_list = build_neighbor_list_for_shlpairs(fake_cell, cell, Ls)
v = incore.aux_e2_sum_auxbas(cell, fake_cell, intors[cn], aosym='s2', comp=1,
kptij_lst=kptij_lst, neighbor_list=neighbor_list)
if i == 0:
buf = v
else:
buf = numpy.add(buf, v, out=buf)
v = None
free_neighbor_list(neighbor_list)
vpploc = lib.unpack_tril(buf)
return vpploc
# TODO add k-point sampling
[docs]
def vpploc_part2_nuc_grad(cell, dm, kpts=None):
'''
Nuclear gradients of the 2nd part of the local part of
the GTH pseudo potential, contracted with the density matrix.
'''
from pyscf.pbc.df import incore
from pyscf.pbc.gto import build_neighbor_list_for_shlpairs, free_neighbor_list
if kpts is not None and not gamma_point(kpts):
raise NotImplementedError("k-point sampling not available")
if kpts is None:
kpts_lst = numpy.zeros((1,3))
else:
kpts_lst = numpy.reshape(kpts, (-1,3))
kptij_lst = numpy.hstack((kpts_lst,kpts_lst)).reshape(-1,2,3)
intors = ('int3c2e_ip1', 'int3c1e_ip1', 'int3c1e_ip1_r2_origk',
'int3c1e_ip1_r4_origk', 'int3c1e_ip1_r6_origk')
Ls = cell.get_lattice_Ls()
count = 0
grad = 0
for cn in range(1, 5):
fakecell = fake_cell_vloc(cell, cn)
fakecell.precision = EPS_PPL
if fakecell.nbas > 0:
neighbor_list = build_neighbor_list_for_shlpairs(fakecell, cell, Ls)
buf = incore.int3c1e_nuc_grad(cell, fakecell, dm, intors[cn],
kptij_lst=kptij_lst, neighbor_list=neighbor_list)
if count == 0:
grad = buf
else:
grad = numpy.add(grad, buf, out=grad)
buf = None
count += 1
free_neighbor_list(neighbor_list)
grad *= -2
return grad
def _prepare_hl_data(fakecell, hl_blocks):
offset = [0] * 3
hl_table = numpy.empty((len(hl_blocks),HL_TABLE_SLOTS), order='C', dtype=numpy.int32)
hl_data = []
ptr = 0
for ib, hl in enumerate(hl_blocks):
hl_table[ib,ATOM_OF] = fakecell._bas[ib,0]
hl_table[ib,ANG_OF] = l = fakecell.bas_angular(ib)
hl_dim = hl.shape[0]
hl_table[ib,HL_DIM_OF], hl_table[ib,HL_DATA_OF] = hl_dim, ptr
ptr += hl_dim**2
hl_data.extend(list(hl.ravel()))
nd = 2 * l + 1
for i in range(hl_dim):
hl_table[ib, i+HL_OFFSET0] = offset[i]
offset[i] += nd
hl_data = numpy.asarray(hl_data, order='C', dtype=numpy.double)
return hl_table, hl_data
# TODO add k-point sampling
def _contract_ppnl(cell, fakecell, hl_blocks, ppnl_half, comp=1, kpts=None):
from pyscf.pbc.gto import NeighborListOpt
if kpts is None:
kpts_lst = numpy.zeros((1,3))
else:
kpts_lst = numpy.reshape(kpts, (-1,3))
hl_table, hl_data = _prepare_hl_data(fakecell, hl_blocks)
opt = NeighborListOpt(fakecell)
opt.build(fakecell, cell)
shls_slice = (0, cell.nbas, 0, cell.nbas)
key = 'cart' if cell.cart else 'sph'
ao_loc = gto.moleintor.make_loc(cell._bas, key)
ppnl = []
nao = cell.nao_nr()
nao_pair = nao * (nao+1) // 2
for k, kpt in enumerate(kpts_lst):
ppnl_half0 = ppnl_half1 = ppnl_half2 = None
if len(ppnl_half[0]) > 0:
ppnl_half0 = ppnl_half[0][k]
if len(ppnl_half[1]) > 0:
ppnl_half1 = ppnl_half[1][k]
if len(ppnl_half[2]) > 0:
ppnl_half2 = ppnl_half[2][k]
if gamma_point(kpt):
if ppnl_half0 is not None:
ppnl_half0 = ppnl_half0.real
if ppnl_half1 is not None:
ppnl_half1 = ppnl_half1.real
if ppnl_half2 is not None:
ppnl_half2 = ppnl_half2.real
buf = numpy.empty([nao_pair], order='C', dtype=numpy.double)
fill = getattr(libpbc, 'ppnl_fill_gs2')
else:
buf = numpy.empty([nao_pair], order='C', dtype=numpy.complex128)
raise NotImplementedError
ppnl_half0 = numpy.asarray(ppnl_half0, order='C')
ppnl_half1 = numpy.asarray(ppnl_half1, order='C')
ppnl_half2 = numpy.asarray(ppnl_half2, order='C')
drv = getattr(libpbc, "contract_ppnl", None)
try:
drv(fill, buf.ctypes.data_as(ctypes.c_void_p),
ppnl_half0.ctypes.data_as(ctypes.c_void_p),
ppnl_half1.ctypes.data_as(ctypes.c_void_p),
ppnl_half2.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(comp), (ctypes.c_int*4)(*shls_slice),
ao_loc.ctypes.data_as(ctypes.c_void_p),
hl_table.ctypes.data_as(ctypes.c_void_p),
hl_data.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(len(hl_blocks)), opt._this)
except Exception as e:
raise RuntimeError(f"Failed to compute non-local pseudo-potential.\n{e}")
ppnl_k = lib.unpack_tril(buf)
ppnl.append(ppnl_k)
if kpts is None or numpy.shape(kpts) == (3,):
ppnl = ppnl[0]
return ppnl
# TODO add k-point sampling
def _contract_ppnl_nuc_grad(cell, fakecell, dms, hl_blocks, ppnl_half, ppnl_half_ip2,
comp=3, kpts=None, hl_table=None, hl_data=None):
from pyscf.pbc.gto import NeighborListOpt
if kpts is None:
kpts_lst = numpy.zeros((1,3))
else:
kpts_lst = numpy.reshape(kpts, (-1,3))
if hl_table is None:
hl_table, hl_data = _prepare_hl_data(fakecell, hl_blocks)
opt = NeighborListOpt(fakecell)
opt.build(fakecell, cell)
nkpts = len(kpts_lst)
nao = cell.nao
dms = dms.reshape(nkpts, nao, nao)
shls_slice = (0, cell.nbas, 0, cell.nbas)
bas = numpy.asarray(cell._bas, order='C', dtype=numpy.int32)
key = 'cart' if cell.cart else 'sph'
ao_loc = gto.moleintor.make_loc(bas, key)
grad = []
for k, kpt in enumerate(kpts_lst):
dm = dms[k]
naux = [0] * 3
ppnl_half0 = ppnl_half1 = ppnl_half2 = None
if len(ppnl_half[0]) > 0:
ppnl_half0 = ppnl_half[0][k]
naux[0] = ppnl_half0.shape[0]
if len(ppnl_half[1]) > 0:
ppnl_half1 = ppnl_half[1][k]
naux[1] = ppnl_half1.shape[0]
if len(ppnl_half[2]) > 0:
ppnl_half2 = ppnl_half[2][k]
naux[2] = ppnl_half2.shape[0]
ppnl_half_ip2_0 = ppnl_half_ip2_1 = ppnl_half_ip2_2 = None
if len(ppnl_half_ip2[0]) > 0:
ppnl_half_ip2_0 = ppnl_half_ip2[0][k]
assert naux[0] == ppnl_half_ip2_0.shape[1]
if len(ppnl_half_ip2[1]) > 0:
ppnl_half_ip2_1 = ppnl_half_ip2[1][k]
assert naux[1] == ppnl_half_ip2_1.shape[1]
if len(ppnl_half_ip2[2]) > 0:
ppnl_half_ip2_2 = ppnl_half_ip2[2][k]
assert naux[2] == ppnl_half_ip2_2.shape[1]
naux = numpy.asarray(naux, dtype=numpy.int32)
if gamma_point(kpt):
dm = dm.real
if ppnl_half0 is not None:
ppnl_half0 = ppnl_half0.real
ppnl_half_ip2_0 = ppnl_half_ip2_0.real
if ppnl_half1 is not None:
ppnl_half1 = ppnl_half1.real
ppnl_half_ip2_1 = ppnl_half_ip2_1.real
if ppnl_half2 is not None:
ppnl_half2 = ppnl_half2.real
ppnl_half_ip2_2 = ppnl_half_ip2_2.real
grad_k = numpy.zeros([cell.natm, comp], order='C', dtype=numpy.double)
fill = getattr(libpbc, 'ppnl_nuc_grad_fill_gs1')
else:
grad_k = numpy.empty([cell.natm, comp], order='C', dtype=numpy.complex128)
raise NotImplementedError
dm = numpy.asarray(dm, order='C')
ppnl_half0 = numpy.asarray(ppnl_half0, order='C')
ppnl_half1 = numpy.asarray(ppnl_half1, order='C')
ppnl_half2 = numpy.asarray(ppnl_half2, order='C')
ppnl_half_ip2_0 = numpy.asarray(ppnl_half_ip2_0, order='C')
ppnl_half_ip2_1 = numpy.asarray(ppnl_half_ip2_1, order='C')
ppnl_half_ip2_2 = numpy.asarray(ppnl_half_ip2_2, order='C')
drv = getattr(libpbc, "contract_ppnl_nuc_grad", None)
try:
drv(fill,
grad_k.ctypes.data_as(ctypes.c_void_p),
dm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(comp),
ppnl_half0.ctypes.data_as(ctypes.c_void_p),
ppnl_half1.ctypes.data_as(ctypes.c_void_p),
ppnl_half2.ctypes.data_as(ctypes.c_void_p),
ppnl_half_ip2_0.ctypes.data_as(ctypes.c_void_p),
ppnl_half_ip2_1.ctypes.data_as(ctypes.c_void_p),
ppnl_half_ip2_2.ctypes.data_as(ctypes.c_void_p),
hl_table.ctypes.data_as(ctypes.c_void_p),
hl_data.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(len(hl_blocks)),
naux.ctypes.data_as(ctypes.c_void_p),
(ctypes.c_int*4)(*shls_slice),
ao_loc.ctypes.data_as(ctypes.c_void_p),
bas.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(cell.natm), opt._this)
except Exception as e:
raise RuntimeError(f"Failed to compute non-local pp nuclear gradient.\n{e}")
grad.append(grad_k)
grad_tot = 0
if nkpts == 1:
grad_tot = grad[0]
else:
for k in range(nkpts):
grad_tot += grad[k]
grad_tot = grad_tot.real
return grad_tot
[docs]
def get_pp_nl(cell, kpts=None):
if kpts is None:
kpts_lst = numpy.zeros((1,3))
else:
kpts_lst = numpy.reshape(kpts, (-1,3))
nkpts = len(kpts_lst)
fakecell, hl_blocks = fake_cell_vnl(cell)
ppnl_half = _int_vnl(cell, fakecell, hl_blocks, kpts_lst)
nao = cell.nao_nr()
if gamma_point(kpts_lst):
return _contract_ppnl(cell, fakecell, hl_blocks, ppnl_half, kpts=kpts)
buf = numpy.empty((3*9*nao), dtype=numpy.complex128)
# We set this equal to zeros in case hl_blocks loop is skipped
# and ppnl is returned
ppnl = numpy.zeros((nkpts,nao,nao), dtype=numpy.complex128)
for k, kpt in enumerate(kpts_lst):
offset = [0] * 3
for ib, hl in enumerate(hl_blocks):
l = fakecell.bas_angular(ib)
nd = 2 * l + 1
hl_dim = hl.shape[0]
ilp = numpy.ndarray((hl_dim,nd,nao), dtype=numpy.complex128, buffer=buf)
for i in range(hl_dim):
p0 = offset[i]
ilp[i] = ppnl_half[i][k][p0:p0+nd]
offset[i] = p0 + nd
ppnl[k] += numpy.einsum('ilp,ij,jlq->pq', ilp.conj(), hl, ilp)
if abs(kpts_lst).sum() < 1e-9: # gamma_point:
ppnl = ppnl.real
if kpts is None or numpy.shape(kpts) == (3,):
ppnl = ppnl[0]
return ppnl
[docs]
def vppnl_nuc_grad(cell, dm, kpts=None):
'''
Nuclear gradients of the non-local part of the GTH pseudo potential,
contracted with the density matrix.
'''
if kpts is None:
kpts_lst = numpy.zeros((1,3))
else:
kpts_lst = numpy.reshape(kpts, (-1,3))
fakecell, hl_blocks = fake_cell_vnl(cell)
intors = ('int1e_ipovlp', 'int1e_r2_origi_ip2', 'int1e_r4_origi_ip2')
ppnl_half = _int_vnl(cell, fakecell, hl_blocks, kpts_lst)
ppnl_half_ip2 = _int_vnl(cell, fakecell, hl_blocks, kpts_lst, intors, comp=3)
# int1e_ipovlp computes ip1 so multiply -1 to get ip2
if len(ppnl_half_ip2[0]) > 0:
for k, kpt in enumerate(kpts_lst):
ppnl_half_ip2[0][k] *= -1
grad = _contract_ppnl_nuc_grad(cell, fakecell, dm, hl_blocks,
ppnl_half, ppnl_half_ip2, kpts=kpts)
grad *= -2
return grad
[docs]
def fake_cell_vloc(cell, cn=0, atm_id=None):
'''Generate fake cell for V_{loc}.
Each term of V_{loc} (erf, C_1, C_2, C_3, C_4) is a gaussian type
function. The integral over V_{loc} can be transfered to the 3-center
integrals, in which the auxiliary basis is given by the fake cell.
The kwarg cn indiciates which term to generate for the fake cell.
If cn = 0, the erf term is generated. C_1,..,C_4 are generated with cn = 1..4
'''
if atm_id is None:
atm_id = numpy.arange(cell.natm)
else:
atm_id = numpy.asarray(atm_id)
natm = len(atm_id)
fake_env = [cell.atom_coords()[atm_id].ravel()]
fake_atm = cell._atm[atm_id].copy().reshape(natm,-1)
fake_atm[:,gto.PTR_COORD] = numpy.arange(0, natm*3, 3)
ptr = natm * 3
fake_bas = []
half_sph_norm = .5/numpy.pi**.5
for ia, atm in enumerate(atm_id):
if cell.atom_charge(atm) == 0: # pass ghost atoms
continue
symb = cell.atom_symbol(atm)
if cn == 0:
if symb in cell._pseudo:
pp = cell._pseudo[symb]
rloc, nexp, cexp = pp[1:3+1]
alpha = .5 / rloc**2
else:
alpha = 1e16
norm = half_sph_norm / gto.gaussian_int(2, alpha)
fake_env.append([alpha, norm])
fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr+1, 0])
ptr += 2
elif symb in cell._pseudo:
pp = cell._pseudo[symb]
rloc, nexp, cexp = pp[1:3+1]
if cn <= nexp:
alpha = .5 / rloc**2
norm = cexp[cn-1]/rloc**(cn*2-2) / half_sph_norm
fake_env.append([alpha, norm])
fake_bas.append([ia, 0, 1, 1, 0, ptr, ptr+1, 0])
ptr += 2
fakecell = cell.copy(deep=False)
fakecell._atm = numpy.asarray(fake_atm, dtype=numpy.int32).reshape(-1, gto.ATM_SLOTS)
fakecell._bas = numpy.asarray(fake_bas, dtype=numpy.int32).reshape(-1, gto.BAS_SLOTS)
fakecell._env = numpy.asarray(numpy.hstack(fake_env), dtype=numpy.double)
return fakecell
# sqrt(Gamma(l+1.5)/Gamma(l+2i+1.5))
_PLI_FAC = 1/numpy.sqrt(numpy.array((
(1, 3.75 , 59.0625 ), # l = 0,
(1, 8.75 , 216.5625 ), # l = 1,
(1, 15.75, 563.0625 ), # l = 2,
(1, 24.75, 1206.5625), # l = 3,
(1, 35.75, 2279.0625), # l = 4,
(1, 48.75, 3936.5625), # l = 5,
(1, 63.75, 6359.0625), # l = 6,
(1, 80.75, 9750.5625))))# l = 7,
[docs]
def fake_cell_vnl(cell):
'''Generate fake cell for V_{nl}.
gaussian function p_i^l Y_{lm}
'''
fake_env = [cell.atom_coords().ravel()]
fake_atm = cell._atm.copy()
fake_atm[:,gto.PTR_COORD] = numpy.arange(0, cell.natm*3, 3)
ptr = cell.natm * 3
fake_bas = []
hl_blocks = []
for ia in range(cell.natm):
if cell.atom_charge(ia) == 0: # pass ghost atoms
continue
symb = cell.atom_symbol(ia)
if symb in cell._pseudo:
pp = cell._pseudo[symb]
# nproj_types = pp[4]
for l, (rl, nl, hl) in enumerate(pp[5:]):
if nl > 0:
alpha = .5 / rl**2
norm = gto.gto_norm(l, alpha)
fake_env.append([alpha, norm])
fake_bas.append([ia, l, 1, 1, 0, ptr, ptr+1, 0])
#
# Function p_i^l (PRB, 58, 3641 Eq 3) is (r^{2(i-1)})^2 square normalized to 1.
# But here the fake basis is square normalized to 1. A factor ~ p_i^l / p_1^l
# is attached to h^l_ij (for i>1,j>1) so that (factor * fake-basis * r^{2(i-1)})
# is normalized to 1. The factor is
# r_l^{l+(4-1)/2} sqrt(Gamma(l+(4-1)/2)) sqrt(Gamma(l+3/2))
# ------------------------------------------ = ----------------------------------
# r_l^{l+(4i-1)/2} sqrt(Gamma(l+(4i-1)/2)) sqrt(Gamma(l+2i-1/2)) r_l^{2i-2}
#
fac = numpy.array([_PLI_FAC[l,i]/rl**(i*2) for i in range(nl)])
hl = numpy.einsum('i,ij,j->ij', fac, numpy.asarray(hl), fac)
hl_blocks.append(hl)
ptr += 2
fakecell = cell.copy(deep=False)
fakecell._atm = numpy.asarray(fake_atm, dtype=numpy.int32)
fakecell._bas = numpy.asarray(fake_bas, dtype=numpy.int32).reshape(-1, gto.BAS_SLOTS)
fakecell._env = numpy.asarray(numpy.hstack(fake_env), dtype=numpy.double)
return fakecell, hl_blocks
def _int_vnl(cell, fakecell, hl_blocks, kpts, intors=None, comp=1):
'''Vnuc - Vloc'''
if intors is None:
intors = ['int1e_ovlp', 'int1e_r2_origi', 'int1e_r4_origi']
rcut = max(cell.rcut, fakecell.rcut)
Ls = cell.get_lattice_Ls(rcut=rcut)
nimgs = len(Ls)
expkL = numpy.asarray(numpy.exp(1j*numpy.dot(kpts, Ls.T)), order='C')
nkpts = len(kpts)
fill = getattr(libpbc, 'PBCnr2c_fill_ks1')
# TODO add screening
cintopt = lib.c_null_ptr()
def int_ket(_bas, intor):
if len(_bas) == 0:
return []
intor = cell._add_suffix(intor)
atm, bas, env = gto.conc_env(cell._atm, cell._bas, cell._env,
fakecell._atm, _bas, fakecell._env)
atm = numpy.asarray(atm, dtype=numpy.int32)
bas = numpy.asarray(bas, dtype=numpy.int32)
env = numpy.asarray(env, dtype=numpy.double)
natm = len(atm)
nbas = len(bas)
shls_slice = (cell.nbas, nbas, 0, cell.nbas)
ao_loc = gto.moleintor.make_loc(bas, intor)
ni = ao_loc[shls_slice[1]] - ao_loc[shls_slice[0]]
nj = ao_loc[shls_slice[3]] - ao_loc[shls_slice[2]]
if comp == 1:
out = numpy.empty((nkpts,ni,nj), dtype=numpy.complex128)
else:
out = numpy.empty((nkpts,comp,ni,nj), dtype=numpy.complex128)
fintor = getattr(gto.moleintor.libcgto, intor)
drv = libpbc.PBCnr2c_drv
drv(fintor, fill, out.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(nkpts), ctypes.c_int(comp), ctypes.c_int(nimgs),
Ls.ctypes.data_as(ctypes.c_void_p),
expkL.ctypes.data_as(ctypes.c_void_p),
(ctypes.c_int*4)(*(shls_slice[:4])),
ao_loc.ctypes.data_as(ctypes.c_void_p), cintopt,
atm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(natm),
bas.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nbas),
env.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(env.size))
return out
hl_dims = numpy.asarray([len(hl) for hl in hl_blocks])
out = (int_ket(fakecell._bas[hl_dims>0], intors[0]),
int_ket(fakecell._bas[hl_dims>1], intors[1]),
int_ket(fakecell._bas[hl_dims>2], intors[2]))
return out