#!/usr/bin/env python
# Copyright 2014-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>
#
'''
JK with analytic Fourier transformation
'''
import ctypes
import numpy
import numpy as np
from pyscf import lib
from pyscf import gto
from pyscf.lib import logger
from pyscf.lib import zdotNN, zdotCN, zdotNC
from pyscf.pbc import gto as pbcgto
from pyscf.gto.ft_ao import ft_aopair
from pyscf.pbc.df import ft_ao
from pyscf.pbc.df.df_jk import (_format_dms, _format_kpts_band, _format_jks,
_ewald_exxdiv_for_G0)
from pyscf.pbc.lib.kpts_helper import (is_zero, group_by_conj_pairs,
kk_adapted_iter)
from pyscf.pbc.tools import k2gamma
from pyscf.pbc.df.incore import libpbc
[docs]
def get_j_kpts(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), kpts_band=None):
if kpts_band is not None:
return get_j_for_bands(mydf, dm_kpts, hermi, kpts, kpts_band)
dm_kpts = lib.asarray(dm_kpts, order='C')
dms = _format_dms(dm_kpts, kpts)
n_dm, nkpts, nao = dms.shape[:3]
vj_kpts = numpy.zeros((n_dm,nkpts,nao,nao), dtype=numpy.complex128)
kpt_allow = numpy.zeros(3)
mesh = mydf.mesh
coulG = mydf.weighted_coulG(kpt_allow, False, mesh)
max_memory = (mydf.max_memory - lib.current_memory()[0]) * .8
weight = 1./len(kpts)
for Gpq, p0, p1 in mydf.ft_loop(mesh, kpt_allow, kpts, max_memory=max_memory,
return_complex=False):
_update_vj_(vj_kpts, Gpq, dms, coulG[p0:p1], weight)
Gpq = None
if is_zero(kpts):
vj_kpts = vj_kpts.real.copy()
return _format_jks(vj_kpts, dm_kpts, kpts_band, kpts)
def _update_vj_(vj_kpts, Gpq, dms, coulG, weight=None):
r'''Compute the Coulomb matrix
J_{kl} = \sum_{ij} \sum_G 4\pi/G^2 * FT(\rho_{ij}) IFT(\rho_{kl}) dm_{ji}
for analytical FT tensor FT(\rho_{ij})
'''
# FT(\rho_{ij, kpt}) = \int exp(-i G*r) conj(\psi_{i,kpt}) \psi_{j,kpt} dr
# IFT(\rho_{ij, kpt}) = \int exp(i G*r) conj(\psi_{i,kpt}) \psi_{j,kpt} dr
# IFT(\rho_{ij, kpt}) = conj(FT(\rho_{ji, kpt}))
# rhoG[k] = einsum('ij,gji->g', dms[i,k], IFT(\rho))
# = einsum('ij,gji->g', dms[i,k], aoao.conj().transpose(0,2,1))
# = einsum('ij,gij->g', dms[i,k].conj(), aoao).conj()
# vG = sum_k rhoG[k] * coulG
# vj[k] = einsum('g,gji->g', vG, aoao[k])
is_real = vj_kpts.dtype == np.double
GpqR, GpqI = Gpq
rhoR = np.einsum('nkij,kgij->ng', dms.real, GpqR)
rhoI = -np.einsum('nkij,kgij->ng', dms.real, GpqI)
if not is_real:
rhoR += np.einsum('nkij,kgij->ng', dms.imag, GpqI)
rhoI += np.einsum('nkij,kgij->ng', dms.imag, GpqR)
if weight is not None:
coulG = coulG * weight
vR = coulG * rhoR
vI = coulG * rhoI
vj_kpts.real += np.einsum('ng,kgij->nkij', vR, GpqR)
vj_kpts.real -= np.einsum('ng,kgij->nkij', vI, GpqI)
if not is_real:
vj_kpts.imag += np.einsum('ng,kgij->nkij', vR, GpqI)
vj_kpts.imag += np.einsum('ng,kgij->nkij', vI, GpqR)
return vj_kpts
[docs]
def get_j_for_bands(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), kpts_band=None):
log = logger.Logger(mydf.stdout, mydf.verbose)
t1 = (logger.process_clock(), logger.perf_counter())
dm_kpts = lib.asarray(dm_kpts, order='C')
dms = _format_dms(dm_kpts, kpts)
nset, nkpts, nao = dms.shape[:3]
kpt_allow = numpy.zeros(3)
mesh = mydf.mesh
coulG = mydf.weighted_coulG(kpt_allow, False, mesh)
ngrids = len(coulG)
rhoG = numpy.zeros((nset,ngrids), dtype=numpy.complex128)
max_memory = (mydf.max_memory - lib.current_memory()[0]) * .8
for aoaoks, p0, p1 in mydf.ft_loop(mesh, kpt_allow, kpts, max_memory=max_memory):
for k, aoao in enumerate(aoaoks):
rhoG[:,p0:p1] += numpy.einsum('nij,Lij->nL', dms[:,k].conj(),
aoao.reshape(-1,nao,nao)).conj()
aoao = aoaoks = p0 = p1 = None
weight = 1./len(kpts)
vG = rhoG * coulG * weight
t1 = log.timer_debug1('get_j pass 1 to compute J(G)', *t1)
kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band
nband = len(kpts_band)
vj_kpts = numpy.zeros((nset,nband,nao,nao), dtype=numpy.complex128)
for aoaoks, p0, p1 in mydf.ft_loop(mesh, kpt_allow, kpts_band,
max_memory=max_memory):
for k, aoao in enumerate(aoaoks):
vj_kpts[:,k] += numpy.einsum('nL,Lij->nij', vG[:,p0:p1],
aoao.reshape(-1,nao,nao))
aoao = aoaoks = p0 = p1 = None
if is_zero(kpts_band):
vj_kpts = vj_kpts.real.copy()
t1 = log.timer_debug1('get_j pass 2', *t1)
return _format_jks(vj_kpts, dm_kpts, input_band, kpts)
[docs]
def get_k_kpts(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), kpts_band=None,
exxdiv=None):
if kpts_band is not None:
return get_k_for_bands(mydf, dm_kpts, hermi, kpts, kpts_band, exxdiv)
cpu0 = cpu1 = logger.process_clock(), logger.perf_counter()
log = logger.new_logger(mydf)
cell = mydf.cell
mesh = mydf.mesh
ngrids = np.prod(mesh)
mo_coeff = getattr(dm_kpts, 'mo_coeff', None)
mo_occ = getattr(dm_kpts, 'mo_occ', None)
dm_kpts = np.asarray(dm_kpts)
dms = _format_dms(dm_kpts, kpts)
n_dm, nkpts, nao = dms.shape[:3]
vkR = np.zeros((n_dm,nkpts,nao,nao))
vkI = np.zeros((n_dm,nkpts,nao,nao))
vk = [vkR, vkI]
weight = 1. / nkpts
aosym = 's1'
bvk_kmesh = k2gamma.kpts_to_kmesh(cell, kpts)
rcut = ft_ao.estimate_rcut(cell)
supmol = ft_ao.ExtendedMole.from_cell(cell, bvk_kmesh, rcut.max())
supmol = supmol.strip_basis(rcut)
t_rev_pairs = group_by_conj_pairs(cell, kpts, return_kpts_pairs=False)
try:
t_rev_pairs = np.asarray(t_rev_pairs, dtype=np.int32, order='F')
except TypeError:
t_rev_pairs = [[k, k] if k_conj is None else [k, k_conj]
for k, k_conj in t_rev_pairs]
t_rev_pairs = np.asarray(t_rev_pairs, dtype=np.int32, order='F')
log.debug1('Num time-reversal pairs %d', len(t_rev_pairs))
time_reversal_symmetry = mydf.time_reversal_symmetry
if time_reversal_symmetry:
for k, k_conj in t_rev_pairs:
if k != k_conj and abs(dms[:,k_conj] - dms[:,k].conj()).max() > 1e-6:
time_reversal_symmetry = False
log.debug2('Disable time_reversal_symmetry')
break
if time_reversal_symmetry:
k_to_compute = np.zeros(nkpts, dtype=np.int8)
k_to_compute[t_rev_pairs[:,0]] = 1
else:
k_to_compute = np.ones(nkpts, dtype=np.int8)
contract_mo_early = False
if mo_coeff is None:
dmsR = np.asarray(dms.real, order='C')
dmsI = np.asarray(dms.imag, order='C')
dm = [dmsR, dmsI]
dm_factor = None
if np.count_nonzero(k_to_compute) >= 2 * lib.num_threads():
update_vk = _update_vk1_
else:
update_vk = _update_vk_
log.debug2('set update_vk to %s', update_vk)
else:
# dm ~= dm_factor * dm_factor.T
n_dm, nkpts, nao = dms.shape[:3]
# mo_coeff, mo_occ are not a list of aligned array if
# remove_lin_dep was applied to scf object
if dm_kpts.ndim == 4: # KUHF
nocc = max(max(np.count_nonzero(x > 0) for x in z) for z in mo_occ)
dm_factor = [[x[:,:nocc] for x in mo] for mo in mo_coeff]
occs = [[x[:nocc] for x in z] for z in mo_occ]
else: # KRHF
nocc = max(np.count_nonzero(x > 0) for x in mo_occ)
dm_factor = [[mo[:,:nocc] for mo in mo_coeff]]
occs = [[x[:nocc] for x in mo_occ]]
dm_factor = np.array(dm_factor, dtype=np.complex128, order='C')
dm_factor *= np.sqrt(np.array(occs, dtype=np.double))[:,:,None]
bvk_ncells, rs_nbas, nimgs = supmol.bas_mask.shape
s_nao = supmol.nao
contract_mo_early = (time_reversal_symmetry and
bvk_ncells*nao*4 > s_nao*nocc*n_dm)
log.debug2('time_reversal_symmetry = %s bvk_ncells = %d '
's_nao = %d nocc = %d n_dm = %d',
time_reversal_symmetry, bvk_ncells, s_nao, nocc, n_dm)
log.debug2('Use algorithm contract_mo_early = %s', contract_mo_early)
if contract_mo_early:
s_nao = supmol.nao
moR, moI = _mo_k2gamma(supmol, dm_factor, kpts, t_rev_pairs)
if abs(moI).max() < 1e-5:
dm = [moR, None]
dm_factor = moR = moI = None
ft_kern = _gen_ft_kernel_fake_gamma(cell, supmol, aosym)
update_vk = _update_vk_fake_gamma
else:
moR = moI = None
contract_mo_early = False
if not contract_mo_early:
dm = [np.asarray(dm_factor.real, order='C'),
np.asarray(dm_factor.imag, order='C')]
dm_factor = None
if np.count_nonzero(k_to_compute) >= 2 * lib.num_threads():
update_vk = _update_vk1_dmf
else:
update_vk = _update_vk_dmf
log.debug2('set update_vk to %s with dm_factor', update_vk)
if not contract_mo_early:
ft_kern = supmol.gen_ft_kernel(aosym, return_complex=False,
kpts=kpts, verbose=log)
Gv, Gvbase, kws = cell.get_Gv_weights(mesh)
Gv = np.asarray(Gv, order='F')
gxyz = lib.cartesian_prod([np.arange(len(x)) for x in Gvbase])
mem_now = lib.current_memory()[0]
max_memory = max(2000, (mydf.max_memory - mem_now))
log.debug1('max_memory = %d MB (%d in use)', max_memory+mem_now, mem_now)
if contract_mo_early:
Gblksize = max(24, int((max_memory*1e6/16-nkpts*nao**2*3)/
(nao*s_nao+nao*nkpts*nocc))//8*8)
Gblksize = min(Gblksize, ngrids, 200000)
log.debug1('Gblksize = %d', Gblksize)
buf = np.empty(Gblksize*s_nao*nao*2)
else:
Gblksize = max(24, int(max_memory*1e6/16/nao**2/(nkpts+3))//8*8)
Gblksize = min(Gblksize, ngrids, 200000)
log.debug1('Gblksize = %d', Gblksize)
buf = np.empty(nkpts*Gblksize*nao**2*2)
for group_id, (kpt, ki_idx, kj_idx, self_conj) \
in enumerate(kk_adapted_iter(cell, kpts)):
vkcoulG = mydf.weighted_coulG(kpt, exxdiv, mesh)
for p0, p1 in lib.prange(0, ngrids, Gblksize):
log.debug3('update_vk [%s:%s]', p0, p1)
Gpq = ft_kern(Gv[p0:p1], gxyz[p0:p1], Gvbase, kpt, out=buf)
update_vk(vk, Gpq, dm, vkcoulG[p0:p1] * weight, ki_idx, kj_idx,
not self_conj, k_to_compute, t_rev_pairs)
Gpq = None
cpu1 = log.timer_debug1(f'get_k_kpts group {group_id}', *cpu1)
if is_zero(kpts) and not numpy.iscomplexobj(dm_kpts):
vk_kpts = vkR
else:
vk_kpts = vkR + vkI * 1j
# Add ewald_exxdiv contribution because G=0 was not included in the
# non-uniform grids
if (exxdiv == 'ewald' and
(cell.dimension < 2 or # 0D and 1D are computed with inf_vacuum
(cell.dimension == 2 and cell.low_dim_ft_type == 'inf_vacuum'))):
_ewald_exxdiv_for_G0(cell, kpts, dms, vk_kpts, kpts)
if time_reversal_symmetry:
for k, k_conj in t_rev_pairs:
if k != k_conj:
vk_kpts[:,k_conj] = vk_kpts[:,k].conj()
log.timer_debug1('get_k_kpts', *cpu0)
return vk_kpts.reshape(dm_kpts.shape)
[docs]
def get_k_for_bands(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), kpts_band=None,
exxdiv=None):
cell = mydf.cell
log = logger.Logger(mydf.stdout, mydf.verbose)
t1 = (logger.process_clock(), logger.perf_counter())
mesh = mydf.mesh
dm_kpts = lib.asarray(dm_kpts, order='C')
dms = _format_dms(dm_kpts, kpts)
nset, nkpts, nao = dms.shape[:3]
swap_2e = (kpts_band is None)
kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band
nband = len(kpts_band)
kk_table = kpts_band.reshape(-1,1,3) - kpts.reshape(1,-1,3)
kk_todo = numpy.ones(kk_table.shape[:2], dtype=bool)
k_to_compute = np.ones(nkpts+len(kpts_band), dtype=np.int8)
vkR = numpy.zeros((nset,nband,nao,nao))
vkI = numpy.zeros((nset,nband,nao,nao))
dmsR = numpy.asarray(dms.real, order='C')
dmsI = numpy.asarray(dms.imag, order='C')
mem_now = lib.current_memory()[0]
max_memory = max(2000, (mydf.max_memory - mem_now)) * .8
log.debug1('max_memory = %d MB (%d in use)', max_memory, mem_now)
# K_pq = ( p{k1} i{k2} | i{k2} q{k1} )
def make_kpt(kpt): # kpt = kptj - kpti
# search for all possible ki and kj that has ki-kj+kpt=0
kk_match = numpy.einsum('ijx->ij', abs(kk_table + kpt)) < 1e-9
kpti_idx, kptj_idx = numpy.where(kk_todo & kk_match)
nkptj = len(kptj_idx)
log.debug1('kpt = %s', kpt)
log.debug2('kpti_idx = %s', kpti_idx)
log.debug2('kptj_idx = %s', kptj_idx)
kk_todo[kpti_idx,kptj_idx] = False
if swap_2e and not is_zero(kpt):
kk_todo[kptj_idx,kpti_idx] = False
max_memory1 = max_memory * (nkptj+1)/(nkptj+5)
#blksize = max(int(max_memory1*4e6/(nkptj+5)/16/nao**2), 16)
#bufR = numpy.empty((blksize*nao**2))
#bufI = numpy.empty((blksize*nao**2))
# Use DF object to mimic KRHF/KUHF object in function get_coulG
vkcoulG = mydf.weighted_coulG(kpt, exxdiv, mesh)
weight = 1./len(kpts)
perm_sym = swap_2e and not is_zero(kpt)
for Gpq, p0, p1 in mydf.ft_loop(mesh, kpt, kpts, max_memory=max_memory1,
return_complex=False):
_update_vk_((vkR, vkI), Gpq, (dmsR, dmsI), vkcoulG[p0:p1]*weight,
kpti_idx, kptj_idx, perm_sym, k_to_compute, None)
for ki, kpti in enumerate(kpts_band):
for kj, kptj in enumerate(kpts):
if kk_todo[ki,kj]:
make_kpt(kptj-kpti)
t1 = log.timer_debug1('get_k_kpts: make_kpt (%d,*)'%ki, *t1)
if (is_zero(kpts) and is_zero(kpts_band) and
not numpy.iscomplexobj(dm_kpts)):
vk_kpts = vkR
else:
vk_kpts = vkR + vkI * 1j
# Add ewald_exxdiv contribution because G=0 was not included in the
# non-uniform grids
if (exxdiv == 'ewald' and
(cell.dimension < 2 or # 0D and 1D are computed with inf_vacuum
(cell.dimension == 2 and cell.low_dim_ft_type == 'inf_vacuum'))):
_ewald_exxdiv_for_G0(cell, kpts, dms, vk_kpts, kpts_band)
return _format_jks(vk_kpts, dm_kpts, input_band, kpts)
def _update_vk_(vk, Gpq, dms, wcoulG, kpti_idx, kptj_idx, swap_2e,
k_to_compute, t_rev_pairs):
'''
contraction for exchange matrices:
vk += np.einsum('ngij,njk,nglk,g->nil', Gpq, dm, Gpq.conj(), coulG)
vk += np.einsum('ngij,nli,nglk,g->nkj', Gpq, dm, Gpq.conj(), coulG)
'''
vkR, vkI = vk
GpqR, GpqI = Gpq
dmsR, dmsI = dms
nG = len(wcoulG)
n_dm, nkpts, nao = vkR.shape[:3]
bufR = np.empty((nG*nao**2))
bufI = np.empty((nG*nao**2))
buf1R = np.empty((nG*nao**2))
buf1I = np.empty((nG*nao**2))
iLkR = np.ndarray((nao,nG,nao), buffer=buf1R)
iLkI = np.ndarray((nao,nG,nao), buffer=buf1I)
for k, (ki, kj) in enumerate(zip(kpti_idx, kptj_idx)):
# case 1: k_pq = (pi|iq)
#:v4 = np.einsum('ijL,lkL->ijkl', pqk, pqk.conj())
#:vk += np.einsum('ijkl,jk->il', v4, dm)
pLqR = np.ndarray((nao,nG,nao), buffer=bufR)
pLqI = np.ndarray((nao,nG,nao), buffer=bufI)
pLqR[:] = GpqR[kj].transpose(1,0,2)
pLqI[:] = GpqI[kj].transpose(1,0,2)
if k_to_compute[ki]:
for i in range(n_dm):
zdotNN(pLqR.reshape(-1,nao), pLqI.reshape(-1,nao),
dmsR[i,kj], dmsI[i,kj], 1,
iLkR.reshape(-1,nao), iLkI.reshape(-1,nao))
iLkR *= wcoulG.reshape(1,nG,1)
iLkI *= wcoulG.reshape(1,nG,1)
zdotNC(iLkR.reshape(nao,-1), iLkI.reshape(nao,-1),
pLqR.reshape(nao,-1).T, pLqI.reshape(nao,-1).T,
1, vkR[i,ki], vkI[i,ki], 1)
# case 2: k_pq = (iq|pi)
#:v4 = np.einsum('iLj,lLk->ijkl', pqk, pqk.conj())
#:vk += np.einsum('ijkl,li->kj', v4, dm)
# <r|-G+k_rs|s> = conj(<s|G-k_rs|r>) = conj(<s|G+k_sr|r>)
if swap_2e and k_to_compute[kj]:
for i in range(n_dm):
zdotNN(dmsR[i,ki], dmsI[i,ki], pLqR.reshape(nao,-1),
pLqI.reshape(nao,-1), 1,
iLkR.reshape(nao,-1), iLkI.reshape(nao,-1))
iLkR *= wcoulG.reshape(1,nG,1)
iLkI *= wcoulG.reshape(1,nG,1)
zdotCN(pLqR.reshape(-1,nao).T, pLqI.reshape(-1,nao).T,
iLkR.reshape(-1,nao), iLkI.reshape(-1,nao),
1, vkR[i,kj], vkI[i,kj], 1)
def _update_vk1_(vk, Gpq, dms, wcoulG, kpti_idx, kptj_idx, swap_2e,
k_to_compute, t_rev_pairs):
'''
contraction for exchange matrices:
vk += np.einsum('ngij,njk,nglk,g->nil', Gpq, dm, Gpq.conj(), coulG)
vk += np.einsum('ngij,nli,nglk,g->nkj', Gpq, dm, Gpq.conj(), coulG)
'''
vkR, vkI = vk
GpqR, GpqI = Gpq
dmsR, dmsI = dms
nG = len(wcoulG)
n_dm, nkpts, nao = vkR.shape[:3]
nkptj = len(kptj_idx)
assert GpqR.transpose(0,2,3,1).flags.c_contiguous
assert vkR.flags.c_contiguous
assert dmsR.flags.c_contiguous
assert kpti_idx.dtype == np.int32
assert kptj_idx.dtype == np.int32
assert k_to_compute.dtype == np.int8
assert k_to_compute.size == nkpts
libpbc.PBC_kcontract(
vkR.ctypes.data_as(ctypes.c_void_p),
vkI.ctypes.data_as(ctypes.c_void_p),
dmsR.ctypes.data_as(ctypes.c_void_p),
dmsI.ctypes.data_as(ctypes.c_void_p),
GpqR.ctypes.data_as(ctypes.c_void_p),
GpqI.ctypes.data_as(ctypes.c_void_p),
wcoulG.ctypes.data_as(ctypes.c_void_p),
kpti_idx.ctypes.data_as(ctypes.c_void_p),
kptj_idx.ctypes.data_as(ctypes.c_void_p),
k_to_compute.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(swap_2e), ctypes.c_int(n_dm), ctypes.c_int(nao),
ctypes.c_int(nG), ctypes.c_int(nkpts), ctypes.c_int(nkptj))
def _update_vk_dmf(vk, Gpq, dmf, wcoulG, kpti_idx, kptj_idx, swap_2e,
k_to_compute, t_rev_pairs):
'''
dmf is the factorized dm, dm = dmf * dmf.conj().T
Computing exchange matrices with dmf:
vk += np.einsum('ngij,njp,nkp,nglk,g->nil', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
vk += np.einsum('ngij,nlp,nip,nglk,g->nkj', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
'''
vkR, vkI = vk
GpqR, GpqI = Gpq
dmfR, dmfI = dmf
nG = len(wcoulG)
n_dm, nkpts, nao, nocc = dmfR.shape
bufR = np.empty((nG*nao**2))
bufI = np.empty((nG*nao**2))
bufR1 = np.empty((nG*nao*nocc))
bufI1 = np.empty((nG*nao*nocc))
for k, (ki, kj) in enumerate(zip(kpti_idx, kptj_idx)):
# case 1: k_pq = (pi|iq)
#:v4 = np.einsum('ijL,lkL->ijkl', pqk, pqk.conj())
#:vk += np.einsum('ijkl,jk->il', v4, dm)
pLqR = np.ndarray((nao,nG,nao), buffer=bufR)
pLqI = np.ndarray((nao,nG,nao), buffer=bufI)
pLqR[:] = GpqR[kj].transpose(1,0,2)
pLqI[:] = GpqI[kj].transpose(1,0,2)
if k_to_compute[ki]:
iLkR = np.ndarray((nao,nG,nocc), buffer=bufR1)
iLkI = np.ndarray((nao,nG,nocc), buffer=bufI1)
for i in range(n_dm):
zdotNN(pLqR.reshape(-1,nao), pLqI.reshape(-1,nao),
dmfR[i,kj], dmfI[i,kj], 1,
iLkR.reshape(-1,nocc), iLkI.reshape(-1,nocc))
iLkR1 = iLkR * wcoulG[:,None]
iLkI1 = iLkI * wcoulG[:,None]
zdotNC(iLkR1.reshape(nao,-1), iLkI1.reshape(nao,-1),
iLkR.reshape(nao,-1).T, iLkI.reshape(nao,-1).T,
1, vkR[i,ki], vkI[i,ki], 1)
# case 2: k_pq = (iq|pi)
#:v4 = np.einsum('iLj,lLk->ijkl', pqk, pqk.conj())
#:vk += np.einsum('ijkl,li->kj', v4, dm)
# <r|-G+k_rs|s> = conj(<s|G-k_rs|r>) = conj(<s|G+k_sr|r>)
if swap_2e and k_to_compute[kj]:
iLkR = np.ndarray((nocc,nG,nao), buffer=bufR1)
iLkI = np.ndarray((nocc,nG,nao), buffer=bufI1)
for i in range(n_dm):
zdotCN(dmfR[i,ki].T, dmfI[i,ki].T, pLqR.reshape(nao,-1),
pLqI.reshape(nao,-1), 1,
iLkR.reshape(nocc,-1), iLkI.reshape(nocc,-1))
iLkR1 = iLkR * wcoulG[:,None]
iLkI1 = iLkI * wcoulG[:,None]
zdotCN(iLkR.reshape(-1,nao).T, iLkI.reshape(-1,nao).T,
iLkR1.reshape(-1,nao), iLkI1.reshape(-1,nao),
1, vkR[i,kj], vkI[i,kj], 1)
def _update_vk1_dmf(vk, Gpq, dmf, wcoulG, kpti_idx, kptj_idx, swap_2e,
k_to_compute, t_rev_pairs):
'''
dmf is the factorized dm, dm = dmf * dmf.conj().T
Computing exchange matrices with dmf:
vk += np.einsum('ngij,njp,nkp,nglk,g->nil', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
vk += np.einsum('ngij,nlp,nip,nglk,g->nkj', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
'''
vkR, vkI = vk
GpqR, GpqI = Gpq
dmfR, dmfI = dmf
nG = len(wcoulG)
n_dm, nkpts, nao, nocc = dmfR.shape
nkptj = len(kptj_idx)
assert GpqR.transpose(0,2,3,1).flags.c_contiguous
assert vkR.flags.c_contiguous
assert dmfI.flags.c_contiguous
assert kpti_idx.dtype == np.int32
assert kptj_idx.dtype == np.int32
assert k_to_compute.dtype == np.int8
assert k_to_compute.size == nkpts
libpbc.PBC_kcontract_dmf(
vkR.ctypes.data_as(ctypes.c_void_p),
vkI.ctypes.data_as(ctypes.c_void_p),
dmfR.ctypes.data_as(ctypes.c_void_p),
dmfI.ctypes.data_as(ctypes.c_void_p),
GpqR.ctypes.data_as(ctypes.c_void_p),
GpqI.ctypes.data_as(ctypes.c_void_p),
wcoulG.ctypes.data_as(ctypes.c_void_p),
kpti_idx.ctypes.data_as(ctypes.c_void_p),
kptj_idx.ctypes.data_as(ctypes.c_void_p),
k_to_compute.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(swap_2e),
ctypes.c_int(n_dm), ctypes.c_int(nao), ctypes.c_int(nocc),
ctypes.c_int(nG), ctypes.c_int(nkpts), ctypes.c_int(nkptj))
def _update_vk_fake_gamma(vk, Gpq, dmf, wcoulG, kpti_idx, kptj_idx, swap_2e,
k_to_compute, t_rev_pairs):
'''
dmf is the factorized dm, dm = dmf * dmf.conj().T
Computing exchange matrices with dmf:
vk += np.einsum('ngij,njp,nkp,nglk,g->nil', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
vk += np.einsum('ngij,nlp,nip,nglk,g->nkj', Gpq, dmf, dmf.conj(), Gpq.conj(), coulG)
'''
vkR, vkI = vk
GpqR, GpqI = Gpq
dmfR, dmfI = dmf
nG = len(wcoulG)
n_dm, s_nao, nkpts, nocc = dmfR.shape
nao = vkR.shape[-1]
GpqR = GpqR.transpose(2,1,0)
assert GpqR.flags.c_contiguous
assert kpti_idx.dtype == np.int32
assert kptj_idx.dtype == np.int32
assert k_to_compute.dtype == np.int8
assert k_to_compute.size == nkpts
assert kptj_idx.size == nkpts
for i in range(n_dm):
libpbc.PBC_kcontract_fake_gamma(
vkR[i].ctypes.data_as(ctypes.c_void_p),
vkI[i].ctypes.data_as(ctypes.c_void_p),
dmfR[i].ctypes.data_as(ctypes.c_void_p),
GpqR.ctypes.data_as(ctypes.c_void_p),
GpqI.ctypes.data_as(ctypes.c_void_p),
wcoulG.ctypes.data_as(ctypes.c_void_p),
kpti_idx.ctypes.data_as(ctypes.c_void_p),
kptj_idx.ctypes.data_as(ctypes.c_void_p),
k_to_compute.ctypes.data_as(ctypes.c_void_p),
t_rev_pairs.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(t_rev_pairs.shape[0]),
ctypes.c_int(swap_2e), ctypes.c_int(s_nao), ctypes.c_int(nao),
ctypes.c_int(nocc), ctypes.c_int(nG), ctypes.c_int(nkpts))
def _mo_k2gamma(supmol, dm_factor, kpts, t_rev_pairs):
'''Transform to MO of supcell at gamma point'''
sh_loc = supmol.sh_loc # maps the bvk shell Id to supmol shell Id
ao_loc = supmol.ao_loc
rs_cell = supmol.rs_cell
cell0_ao_loc = rs_cell.ao_loc
nbasp = rs_cell.ref_cell.nbas
bvk_ncells, _, nimgs = supmol.bas_mask.shape
expLk = np.exp(1j*np.dot(supmol.bvkmesh_Ls, kpts.T))
expLk = np.asarray(expLk, order='C')
s_nao = supmol.nao
n_dm, nkpts, nao, nocc = dm_factor.shape
moR = np.empty((n_dm,s_nao,nkpts,nocc))
moI = np.empty((n_dm,s_nao,nkpts,nocc))
t_rev_pairs = np.asarray(t_rev_pairs, dtype=np.int32, order='F')
for i_dm in range(n_dm):
libpbc.PBCmo_k2gamma(
moR[i_dm].ctypes.data_as(ctypes.c_void_p),
moI[i_dm].ctypes.data_as(ctypes.c_void_p),
dm_factor[i_dm].ctypes.data_as(ctypes.c_void_p),
expLk.ctypes.data_as(ctypes.c_void_p),
sh_loc.ctypes.data_as(ctypes.c_void_p),
ao_loc.ctypes.data_as(ctypes.c_void_p),
cell0_ao_loc.ctypes.data_as(ctypes.c_void_p),
t_rev_pairs.ctypes.data_as(ctypes.c_void_p),
ctypes.c_int(t_rev_pairs.shape[0]),
ctypes.c_int(bvk_ncells), ctypes.c_int(nbasp),
ctypes.c_int(s_nao), ctypes.c_int(nao), ctypes.c_int(nocc),
ctypes.c_int(nkpts), ctypes.c_int(nimgs))
return moR, moI
def _gen_ft_kernel_fake_gamma(cell, supmol, aosym='s1'):
ovlp_mask = supmol.get_ovlp_mask()
ovlp_mask = np.asarray(ovlp_mask, dtype=np.int8, order='F')
cell = supmol.rs_cell
supmol1 = cell.to_mol() + supmol
shls_slice = (0, cell.nbas, cell.nbas, supmol1.nbas)
b = cell.reciprocal_vectors()
def ft_kern(Gv, gxyz=None, Gvbase=None, q=None, kpts=None, out=None):
return ft_aopair(supmol1, Gv, shls_slice, aosym, b,
gxyz=gxyz, Gvbase=Gvbase, out=out,
q=q, return_complex=False, ovlp_mask=ovlp_mask)
return ft_kern
##################################################
#
# Single k-point
#
##################################################
[docs]
def get_jk(mydf, dm, hermi=1, kpt=numpy.zeros(3),
kpts_band=None, with_j=True, with_k=True, exxdiv=None):
'''JK for given k-point'''
vj = vk = None
if kpts_band is not None and abs(kpt-kpts_band).sum() > 1e-9:
kpt = numpy.reshape(kpt, (1,3))
if with_k:
vk = get_k_kpts(mydf, dm, hermi, kpt, kpts_band, exxdiv)
if with_j:
vj = get_j_kpts(mydf, dm, hermi, kpt, kpts_band)
return vj, vk
cell = mydf.cell
log = logger.Logger(mydf.stdout, mydf.verbose)
t1 = (logger.process_clock(), logger.perf_counter())
dm = numpy.asarray(dm, order='C')
dms = _format_dms(dm, [kpt])
nset, _, nao = dms.shape[:3]
dms = dms.reshape(nset,nao,nao)
j_real = is_zero(kpt)
k_real = is_zero(kpt) and not numpy.iscomplexobj(dms)
mesh = mydf.mesh
kptii = numpy.asarray((kpt,kpt))
kpt_allow = numpy.zeros(3)
if with_j:
vjcoulG = mydf.weighted_coulG(kpt_allow, False, mesh)
vjR = numpy.zeros((nset,nao,nao))
vjI = numpy.zeros((nset,nao,nao))
if with_k:
vkcoulG = mydf.weighted_coulG(kpt_allow, exxdiv, mesh)
vkR = numpy.zeros((nset,nao,nao))
vkI = numpy.zeros((nset,nao,nao))
dmsR = numpy.asarray(dms.real.reshape(nset,nao,nao), order='C')
dmsI = numpy.asarray(dms.imag.reshape(nset,nao,nao), order='C')
mem_now = lib.current_memory()[0]
max_memory = max(2000, (mydf.max_memory - mem_now)) * .8
log.debug1('max_memory = %d MB (%d in use)', max_memory, mem_now)
t2 = t1
# rho_rs(-G+k_rs) is computed as conj(rho_{rs^*}(G-k_rs))
# == conj(transpose(rho_sr(G+k_sr), (0,2,1)))
#blksize = max(int(max_memory*.25e6/16/nao**2), 16)
pLqR = pLqI = None
for pqkR, pqkI, p0, p1 in mydf.pw_loop(mesh, kptii, max_memory=max_memory):
t2 = log.timer_debug1('%d:%d ft_aopair'%(p0,p1), *t2)
pqkR = pqkR.reshape(nao,nao,-1)
pqkI = pqkI.reshape(nao,nao,-1)
if with_j:
#:v4 = numpy.einsum('ijL,lkL->ijkl', pqk, pqk.conj())
#:vj += numpy.einsum('ijkl,lk->ij', v4, dm)
for i in range(nset):
rhoR = numpy.einsum('pq,pqk->k', dmsR[i], pqkR)
rhoR+= numpy.einsum('pq,pqk->k', dmsI[i], pqkI)
rhoI = numpy.einsum('pq,pqk->k', dmsI[i], pqkR)
rhoI-= numpy.einsum('pq,pqk->k', dmsR[i], pqkI)
rhoR *= vjcoulG[p0:p1]
rhoI *= vjcoulG[p0:p1]
vjR[i] += numpy.einsum('pqk,k->pq', pqkR, rhoR)
vjR[i] -= numpy.einsum('pqk,k->pq', pqkI, rhoI)
if not j_real:
vjI[i] += numpy.einsum('pqk,k->pq', pqkR, rhoI)
vjI[i] += numpy.einsum('pqk,k->pq', pqkI, rhoR)
#t2 = log.timer_debug1(' with_j', *t2)
if with_k:
#:v4 = numpy.einsum('ijL,lkL->ijkl', pqk, pqk.conj())
#:vk += numpy.einsum('ijkl,jk->il', v4, dm)
pLqR = lib.transpose(pqkR, axes=(0,2,1), out=pLqR).reshape(-1,nao)
pLqI = lib.transpose(pqkI, axes=(0,2,1), out=pLqI).reshape(-1,nao)
nG = p1 - p0
iLkR = numpy.ndarray((nao,nG,nao), buffer=pqkR)
iLkI = numpy.ndarray((nao,nG,nao), buffer=pqkI)
for i in range(nset):
if k_real:
lib.dot(pLqR, dmsR[i], 1, iLkR.reshape(nao*nG,nao))
lib.dot(pLqI, dmsR[i], 1, iLkI.reshape(nao*nG,nao))
iLkR *= vkcoulG[p0:p1].reshape(1,nG,1)
iLkI *= vkcoulG[p0:p1].reshape(1,nG,1)
lib.dot(iLkR.reshape(nao,-1), pLqR.reshape(nao,-1).T, 1, vkR[i], 1)
lib.dot(iLkI.reshape(nao,-1), pLqI.reshape(nao,-1).T, 1, vkR[i], 1)
else:
zdotNN(pLqR, pLqI, dmsR[i], dmsI[i], 1,
iLkR.reshape(-1,nao), iLkI.reshape(-1,nao))
iLkR *= vkcoulG[p0:p1].reshape(1,nG,1)
iLkI *= vkcoulG[p0:p1].reshape(1,nG,1)
zdotNC(iLkR.reshape(nao,-1), iLkI.reshape(nao,-1),
pLqR.reshape(nao,-1).T, pLqI.reshape(nao,-1).T,
1, vkR[i], vkI[i], 1)
#t2 = log.timer_debug1(' with_k', *t2)
pqkR = pqkI = pLqR = pLqI = iLkR = iLkI = None
#t2 = log.timer_debug1('%d:%d'%(p0,p1), *t2)
t1 = log.timer_debug1('aft_jk.get_jk', *t1)
if with_j:
if j_real:
vj = vjR
else:
vj = vjR + vjI * 1j
vj = vj.reshape(dm.shape)
if with_k:
if k_real:
vk = vkR
else:
vk = vkR + vkI * 1j
# Add ewald_exxdiv contribution because G=0 was not included in the
# non-uniform grids
if (exxdiv == 'ewald' and
(cell.dimension < 2 or # 0D and 1D are computed with inf_vacuum
(cell.dimension == 2 and cell.low_dim_ft_type == 'inf_vacuum'))):
_ewald_exxdiv_for_G0(cell, kpt, dms, vk)
vk = vk.reshape(dm.shape)
return vj, vk