#!/usr/bin/env python
# Copyright 2023 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>
#
'''
Numerical integration functions for (2-component) GKS and KGKS
Ref:
Phys. Rev. Research 5, 013036
'''
import numpy as np
from pyscf import lib
from pyscf.dft import numint
from pyscf.dft import numint2c
from pyscf.pbc.dft import numint as pnumint
from pyscf.pbc.lib.kpts import KPoints
[docs]
class NumInt2C(lib.StreamObject, numint.LibXCMixin):
'''Numerical integration methods for 2-component basis (used by GKS)'''
collinear = numint2c.NumInt2C.collinear
spin_samples = numint2c.NumInt2C.spin_samples
collinear_thrd = numint2c.NumInt2C.collinear_thrd
collinear_samples = numint2c.NumInt2C.collinear_samples
make_mask = lib.invalid_method('make_mask')
eval_ao = staticmethod(pnumint.eval_ao)
eval_rho = staticmethod(numint2c.eval_rho)
eval_rho2 = numint2c.NumInt2C.eval_rho2
[docs]
def eval_rho1(self, cell, ao, dm, screen_index=None, xctype='LDA', hermi=0,
with_lapl=True, cutoff=None, ao_cutoff=None, pair_mask=None,
verbose=None):
return self.eval_rho(cell, ao, dm, screen_index, xctype, hermi,
with_lapl, verbose=verbose)
[docs]
def cache_xc_kernel(self, cell, grids, xc_code, mo_coeff, mo_occ, spin=0,
kpt=None, max_memory=2000):
'''Compute the 0th order density, Vxc and fxc. They can be used in TDDFT,
DFT hessian module etc.
'''
if kpt is None:
kpt = np.zeros(3)
xctype = self._xc_type(xc_code)
if xctype in ('GGA', 'MGGA'):
ao_deriv = 1
else:
ao_deriv = 0
n2c = mo_coeff.shape[0]
nao = n2c // 2
if self.collinear[0] in ('m', 'n'): # mcol or ncol
with_lapl = False
rho = []
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpt, None,
max_memory):
rho.append(self.eval_rho2(cell, ao_k1, mo_coeff, mo_occ, mask,
xctype, with_lapl))
rho = np.concatenate(rho,axis=-1)
assert rho.dtype == np.double
if self.collinear[0] == 'm': # mcol
eval_xc = self.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = self.eval_xc_eff
vxc, fxc = eval_xc(xc_code, rho, deriv=2, xctype=xctype)[1:3]
else:
# rhoa and rhob must be real
dm = np.dot(mo_coeff * mo_occ, mo_coeff.conj().T)
dm_a = dm[:nao,:nao].copy('C')
dm_b = dm[nao:,nao:].copy('C')
ni = self._to_numint1c()
with_lapl = True
hermi = 1
rhoa = []
rhob = []
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpt, None,
max_memory):
# rhoa and rhob must be real
rhoa.append(ni.eval_rho(cell, ao_k1, dm_a, mask, xctype, hermi, with_lapl))
rhob.append(ni.eval_rho(cell, ao_k1, dm_b, mask, xctype, hermi, with_lapl))
rho = np.stack([np.concatenate(rhoa,axis=-1), np.concatenate(rhob,axis=-1)])
assert rho.dtype == np.double
vxc, fxc = ni.eval_xc_eff(xc_code, rho, deriv=2, xctype=xctype)[1:3]
return rho, vxc, fxc
[docs]
def cache_xc_kernel1(self, cell, grids, xc_code, dm, spin=0,
kpt=None, max_memory=2000):
'''Compute the 0th order density, Vxc and fxc. They can be used in TDDFT,
DFT hessian module etc.
'''
if kpt is None:
kpt = np.zeros(3)
xctype = self._xc_type(xc_code)
if xctype in ('GGA', 'MGGA'):
ao_deriv = 1
else:
ao_deriv = 0
n2c = dm.shape[0]
nao = n2c // 2
if self.collinear[0] in ('m', 'n'): # mcol or ncol
hermi = 1 # rho must be real. We need to assume dm hermitian
with_lapl = False
rho = []
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpt, None,
max_memory):
rho.append(self.eval_rho1(cell, ao_k1, dm, mask, xctype, hermi,
with_lapl))
rho = np.concatenate(rho,axis=-1)
assert rho.dtype == np.double
if self.collinear[0] == 'm': # mcol
eval_xc = self.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = self.eval_xc_eff
vxc, fxc = eval_xc(xc_code, rho, deriv=2, xctype=xctype)[1:3]
else:
hermi = 1
dm_a = dm[:nao,:nao].copy('C')
dm_b = dm[nao:,nao:].copy('C')
ni = self._to_numint1c()
with_lapl = True
rhoa = []
rhob = []
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpt, None,
max_memory):
rhoa.append(ni.eval_rho(cell, ao_k1, dm_a, mask, xctype, hermi, with_lapl))
rhob.append(ni.eval_rho(cell, ao_k1, dm_b, mask, xctype, hermi, with_lapl))
rho = np.stack([np.concatenate(rhoa,axis=-1), np.concatenate(rhob,axis=-1)])
assert rho.dtype == np.double
vxc, fxc = ni.eval_xc_eff(xc_code, rho, deriv=2, xctype=xctype)[1:3]
return rho, vxc, fxc
[docs]
def get_rho(self, cell, dm, grids, kpt=np.zeros((1,3)), max_memory=2000):
'''Density in real space
'''
nao = dm.shape[-1] // 2
dm_a = dm[:nao,:nao]
dm_b = dm[nao:,nao:]
ni = self._to_numint1c()
return ni.get_rho(cell, dm_a+dm_b, grids, kpt, max_memory)
def _gks_mcol_vxc(self, cell, grids, xc_code, dms, relativity=0, hermi=0,
kpt=None, kpts_band=None, max_memory=2000, verbose=None):
if kpt is None:
kpt = np.zeros(3)
xctype = self._xc_type(xc_code)
shls_slice = (0, cell.nbas)
ao_loc = cell.ao_loc_nr()
make_rho, nset, n2c = self._gen_rho_evaluator(cell, dms, hermi, False)
nao = n2c // 2
nelec = np.zeros(nset)
excsum = np.zeros(nset)
vmat = np.zeros((nset,n2c,n2c), dtype=np.complex128)
if xctype in ('LDA', 'GGA', 'MGGA'):
f_eval_mat = {
('LDA' , 'n'): (numint2c._ncol_lda_vxc_mat , 0),
('LDA' , 'm'): (numint2c._mcol_lda_vxc_mat , 0),
('GGA' , 'm'): (numint2c._mcol_gga_vxc_mat , 1),
('MGGA', 'm'): (numint2c._mcol_mgga_vxc_mat, 1),
}
fmat, ao_deriv = f_eval_mat[(xctype, self.collinear[0])]
if self.collinear[0] == 'm': # mcol
eval_xc = self.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = self.eval_xc_eff
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpt, kpts_band,
max_memory):
for i in range(nset):
rho = make_rho(i, ao_k2, mask, xctype)
exc, vxc = eval_xc(xc_code, rho, deriv=1, xctype=xctype)[:2]
if xctype == 'LDA':
den = rho[0] * weight
else:
den = rho[0,0] * weight
nelec[i] += den.sum()
excsum[i] += np.dot(den, exc)
vmat[i] += fmat(cell, ao_k1, weight, rho, vxc, mask, shls_slice,
ao_loc, hermi)
elif xctype == 'HF':
pass
else:
raise NotImplementedError(f'numint2c.get_vxc for functional {xc_code}')
if hermi:
vmat = vmat + vmat.conj().transpose(0,2,1)
if isinstance(dms, np.ndarray) and dms.ndim == 2:
vmat = vmat[0]
nelec = nelec[0]
excsum = excsum[0]
return nelec, excsum, vmat
def _gks_mcol_fxc(self, cell, grids, xc_code, dm0, dms, relativity=0, hermi=0,
rho0=None, vxc=None, fxc=None, kpt=None, max_memory=2000,
verbose=None):
assert self.collinear[0] == 'm' # mcol
if kpt is None:
kpt = np.zeros(3)
xctype = self._xc_type(xc_code)
if fxc is None and xctype in ('LDA', 'GGA', 'MGGA'):
fxc = self.cache_xc_kernel1(cell, grids, xc_code, dm0,
kpt=kpt, max_memory=max_memory)[2]
if xctype == 'MGGA':
fmat, ao_deriv = (numint2c._mcol_mgga_fxc_mat , 1)
elif xctype == 'GGA':
fmat, ao_deriv = (numint2c._mcol_gga_fxc_mat , 1)
else:
fmat, ao_deriv = (numint2c._mcol_lda_fxc_mat , 0)
shls_slice = (0, cell.nbas)
ao_loc = cell.ao_loc_nr()
make_rho1, nset, n2c = self._gen_rho_evaluator(cell, dms, hermi, False)
nao = n2c // 2
vmat = np.zeros((nset,n2c,n2c), dtype=np.complex128)
if xctype in ('LDA', 'GGA', 'MGGA'):
_rho0 = None
p1 = 0
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpt, None,
max_memory):
p0, p1 = p1, p1 + weight.size
_fxc = fxc[:,:,:,:,p0:p1]
for i in range(nset):
rho1 = make_rho1(i, ao_k1, mask, xctype)
vmat[i] += fmat(cell, ao_k1, weight, _rho0, rho1, _fxc,
mask, shls_slice, ao_loc, hermi)
elif xctype == 'HF':
pass
else:
raise NotImplementedError(f'numint2c.get_fxc for functional {xc_code}')
if hermi:
vmat = vmat + vmat.conj().transpose(0,2,1)
if isinstance(dms, np.ndarray) and dms.ndim == 2:
vmat = vmat[0]
return vmat
[docs]
@lib.with_doc(pnumint.NumInt.nr_vxc.__doc__)
def nr_vxc(self, cell, grids, xc_code, dms, spin=0, relativity=0, hermi=1,
kpt=None, kpts_band=None, max_memory=2000, verbose=None):
if self.collinear[0] in ('m', 'n'): # mcol or ncol
n, exc, vmat = self._gks_mcol_vxc(
cell, grids, xc_code, dms, relativity, hermi,
kpt, kpts_band, max_memory, verbose)
else:
nao = dms.shape[-1] // 2
# ground state density is always real
dm_a = dms[...,:nao,:nao].copy('C')
dm_b = dms[...,nao:,nao:].copy('C')
dm1 = (dm_a, dm_b)
ni = self._to_numint1c()
n, exc, v = ni.nr_uks(cell, grids, xc_code, dm1, relativity, hermi,
kpt, kpts_band, max_memory, verbose)
vmat = np.zeros(dms.shape, dtype=np.result_type(*v))
vmat[...,:nao,:nao] = v[0]
vmat[...,nao:,nao:] = v[1]
return n.sum(), exc, vmat
get_vxc = nr_gks_vxc = nr_vxc
[docs]
@lib.with_doc(pnumint.NumInt.nr_fxc.__doc__)
def nr_fxc(self, cell, grids, xc_code, dm0, dms, spin=0, relativity=0, hermi=0,
rho0=None, vxc=None, fxc=None, kpt=None, max_memory=2000,
verbose=None):
if self.collinear[0] not in ('c', 'm'): # col or mcol
raise NotImplementedError('non-collinear fxc')
if self.collinear[0] == 'm': # mcol
fxcmat = self._gks_mcol_fxc(cell, grids, xc_code, dm0, dms,
relativity, hermi, rho0, vxc, fxc,
kpt, max_memory, verbose)
else:
dms = np.asarray(dms)
nao = dms.shape[-1] // 2
if dm0 is not None:
dm0 = np.asarray(dm0)
dm0a = dm0[...,:nao,:nao].copy('C')
dm0b = dm0[...,nao:,nao:].copy('C')
dm0 = (dm0a, dm0b)
dms_a = dms[...,:nao,:nao].copy('C')
dms_b = dms[...,nao:,nao:].copy('C')
dm1 = (dms_a, dms_b)
ni = self._to_numint1c()
vmat = ni.nr_uks_fxc(cell, grids, xc_code, dm0, dm1, relativity,
hermi, rho0, vxc, fxc, kpt, max_memory, verbose)
fxcmat = np.zeros(dms.shape, dtype=np.result_type(*vmat))
fxcmat[...,:nao,:nao] = vmat[0]
fxcmat[...,nao:,nao:] = vmat[1]
return fxcmat
get_fxc = nr_gks_fxc = nr_fxc
eval_xc_eff = numint2c._eval_xc_eff
mcfun_eval_xc_adapter = numint2c.mcfun_eval_xc_adapter
block_loop = pnumint.NumInt.block_loop
_gen_rho_evaluator = pnumint.NumInt._gen_rho_evaluator
def _to_numint1c(self):
'''Converts to the associated class to handle collinear systems'''
return self.view(pnumint.NumInt)
[docs]
class KNumInt2C(lib.StreamObject, numint.LibXCMixin):
def __init__(self, kpts=np.zeros((1,3))):
self.kpts = np.reshape(kpts, (-1,3))
collinear = numint2c.NumInt2C.collinear
spin_samples = numint2c.NumInt2C.spin_samples
collinear_thrd = numint2c.NumInt2C.collinear_thrd
collinear_samples = numint2c.NumInt2C.collinear_samples
make_mask = lib.invalid_method('make_mask')
eval_ao = staticmethod(pnumint.eval_ao_kpts)
[docs]
def eval_rho(self, cell, ao_kpts, dm_kpts, non0tab=None, xctype='LDA',
hermi=0, with_lapl=True, verbose=None):
'''Collocate the density (opt. gradients) on the real-space grid.
Args:
cell : Mole or Cell object
ao_kpts : (nkpts, ngrids, nao) ndarray
AO values at each k-point
dm_kpts: (nkpts, nao, nao) ndarray
Density matrix at each k-point
Returns:
rhoR : (ngrids,) ndarray
'''
eval_rho = numint2c.eval_rho
nkpts = len(ao_kpts)
rho_ks = [eval_rho(cell, ao_kpts[k], dm_kpts[k], non0tab, xctype,
hermi, with_lapl, verbose)
for k in range(nkpts)]
dtype = np.result_type(*rho_ks)
rho = np.zeros(rho_ks[0].shape, dtype=dtype)
for k in range(nkpts):
rho += rho_ks[k]
rho *= 1./nkpts
return rho
[docs]
def eval_rho1(self, cell, ao_kpts, dm_kpts, screen_index=None, xctype='LDA',
hermi=0, with_lapl=True, cutoff=None, ao_cutoff=None,
pair_mask=None, verbose=None):
return self.eval_rho(cell, ao_kpts, dm_kpts, screen_index, xctype,
hermi, with_lapl, verbose=verbose)
[docs]
def eval_rho2(self, cell, ao_kpts, mo_coeff_kpts, mo_occ_kpts,
non0tab=None, xctype='LDA', with_lapl=True, verbose=None):
if self.collinear[0] not in ('n', 'm'):
raise NotImplementedError(self.collinear)
dm = [(mo*occ).dot(mo.conj().T)
for mo, occ in zip(mo_coeff_kpts, mo_occ_kpts)]
hermi = 1
return self.eval_rho(cell, ao_kpts, dm, non0tab, xctype, hermi,
with_lapl, verbose)
[docs]
def cache_xc_kernel(self, cell, grids, xc_code, mo_coeff_kpts, mo_occ_kpts,
spin=0, kpts=None, max_memory=2000):
'''Compute the 0th order density, Vxc and fxc. They can be used in TDDFT,
DFT hessian module etc.
'''
if kpts is None:
kpts = np.zeros((1,3))
elif isinstance(kpts, KPoints):
kpts = kpts.kpts
mo_coeff = kpts.transform_mo_coeff(mo_coeff_kpts)
mo_occ = kpts.transform_mo_occ(mo_occ_kpts)
xctype = self._xc_type(xc_code)
if xctype in ('GGA', 'MGGA'):
ao_deriv = 1
else:
ao_deriv = 0
n2c = mo_coeff[0].shape[0]
nao = n2c // 2
if self.collinear[0] in ('m', 'n'): # mcol or ncol
with_lapl = False
rho = []
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpts, None,
max_memory):
rho.append(self.eval_rho2(cell, ao_k1, mo_coeff, mo_occ, mask,
xctype, with_lapl))
rho = np.concatenate(rho,axis=-1)
assert rho.dtype == np.double
if self.collinear[0] == 'm': # mcol
eval_xc = self.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = self.eval_xc_eff
vxc, fxc = eval_xc(xc_code, rho, deriv=2, xctype=xctype)[1:3]
else:
# rhoa and rhob must be real
dm_a = [(mo[:nao]*occ).dot(mo[:nao].conj().T)
for mo, occ in zip(mo_coeff_kpts, mo_occ_kpts)]
dm_b = [(mo[nao:]*occ).dot(mo[nao:].conj().T)
for mo, occ in zip(mo_coeff_kpts, mo_occ_kpts)]
hermi = 1
ni = self._to_numint1c()
with_lapl = True
hermi = 1
rhoa = []
rhob = []
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpts, None,
max_memory):
rhoa.append(ni.eval_rho(cell, ao_k1, dm_a, mask, xctype, hermi, with_lapl))
rhob.append(ni.eval_rho(cell, ao_k1, dm_b, mask, xctype, hermi, with_lapl))
rho = np.stack([np.concatenate(rhoa,axis=-1), np.concatenate(rhob,axis=-1)])
assert rho.dtype == np.double
vxc, fxc = ni.eval_xc_eff(xc_code, rho, deriv=2, xctype=xctype)[1:3]
return rho, vxc, fxc
[docs]
def cache_xc_kernel1(self, cell, grids, xc_code, dm_kpts, spin=0, kpts=None,
max_memory=2000):
'''Compute the 0th order density, Vxc and fxc. They can be used in TDDFT,
DFT hessian module etc.
'''
if kpts is None:
kpts = np.zeros((1,3))
elif isinstance(kpts, KPoints):
kpts = kpts.kpts
xctype = self._xc_type(xc_code)
if xctype in ('GGA', 'MGGA'):
ao_deriv = 1
else:
ao_deriv = 0
n2c = dm_kpts.shape[0]
nao = n2c // 2
if self.collinear[0] in ('m', 'n'): # mcol or ncol
hermi = 1 # rho must be real. We need to assume dm hermitian
with_lapl = False
rho = []
for ao_k1, ao_k2, mask, weight, coords \
in self.block_loop(cell, grids, nao, ao_deriv, kpts, None,
max_memory):
rho.append(self.eval_rho1(cell, ao_k1, dm_kpts, mask, xctype,
hermi, with_lapl))
rho = np.concatenate(rho,axis=-1)
if self.collinear[0] == 'm': # mcol
eval_xc = self.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = self.eval_xc_eff
vxc, fxc = eval_xc(xc_code, rho, deriv=2, xctype=xctype)[1:3]
else:
hermi = 1
dm_a = dm_kpts[:,:nao,:nao].copy('C')
dm_b = dm_kpts[:,nao:,nao:].copy('C')
ni = self._to_numint1c()
with_lapl = True
rhoa = []
rhob = []
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpts, None,
max_memory):
rhoa.append(ni.eval_rho(cell, ao_k1, dm_a, mask, xctype, hermi, with_lapl))
rhob.append(ni.eval_rho(cell, ao_k1, dm_b, mask, xctype, hermi, with_lapl))
rho = np.stack([np.concatenate(rhoa,axis=-1), np.concatenate(rhob,axis=-1)])
assert rho.dtype == np.double
vxc, fxc = ni.eval_xc_eff(xc_code, rho, deriv=2, xctype=xctype)[1:3]
return rho, vxc, fxc
[docs]
def get_rho(self, cell, dm, grids, kpts=np.zeros((1,3)), max_memory=2000):
'''Density in real space
'''
if dm.ndim != 3:
raise RuntimeError(f'dm dimension error {dm.ndim}')
nao = dm.shape[-1] // 2
dm = [x[:nao,:nao] + x[nao:,nao:] for x in dm]
ni = self._to_numint1c()
return ni.get_rho(cell, dm, grids, kpts, max_memory)
def _gks_mcol_vxc(ni, cell, grids, xc_code, dms, relativity=0, hermi=0,
kpts=None, kpts_band=None, max_memory=2000, verbose=None):
if kpts is None:
kpts = np.zeros((1,3))
elif isinstance(kpts, KPoints):
kpts = kpts.kpts
xctype = ni._xc_type(xc_code)
shls_slice = (0, cell.nbas)
ao_loc = cell.ao_loc_nr()
assert dms.ndim >= 3
make_rho, nset, n2c = ni._gen_rho_evaluator(cell, dms, hermi, False)
nao = n2c // 2
nkpts = len(kpts)
nelec = np.zeros(nset)
excsum = np.zeros(nset)
vmat = np.zeros((nset,nkpts,n2c,n2c), dtype=np.complex128)
if xctype in ('LDA', 'GGA', 'MGGA'):
f_eval_mat = {
('LDA' , 'n'): (numint2c._ncol_lda_vxc_mat , 0),
('LDA' , 'm'): (numint2c._mcol_lda_vxc_mat , 0),
('GGA' , 'm'): (numint2c._mcol_gga_vxc_mat , 1),
('MGGA', 'm'): (numint2c._mcol_mgga_vxc_mat, 1),
}
fmat, ao_deriv = f_eval_mat[(xctype, ni.collinear[0])]
if ni.collinear[0] == 'm': # mcol
eval_xc = ni.mcfun_eval_xc_adapter(xc_code)
else:
eval_xc = ni.eval_xc_eff
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpts, kpts_band,
max_memory):
for i in range(nset):
rho = make_rho(i, ao_k2, mask, xctype)
exc, vxc = eval_xc(xc_code, rho, deriv=1, xctype=xctype)[:2]
if xctype == 'LDA':
den = rho[0] * weight
else:
den = rho[0,0] * weight
nelec[i] += den.sum()
excsum[i] += np.dot(den, exc)
for k in range(nkpts):
vmat[i,k] += fmat(cell, ao_k1[k], weight, rho, vxc,
mask, shls_slice, ao_loc, hermi)
elif xctype == 'HF':
pass
else:
raise NotImplementedError(f'KNUMINT2C.get_vxc for functional {xc_code}')
if dms.ndim == 3:
vmat = vmat[0]
nelec = nelec[0]
excsum = excsum[0]
return nelec, excsum, vmat
def _gks_mcol_fxc(ni, cell, grids, xc_code, dm0, dms, relativity=0, hermi=0,
rho0=None, vxc=None, fxc=None, kpts=None, max_memory=2000,
verbose=None):
assert ni.collinear[0] == 'm' # mcol
xctype = ni._xc_type(xc_code)
if fxc is None and xctype in ('LDA', 'GGA', 'MGGA'):
fxc = ni.cache_xc_kernel1(cell, grids, xc_code, dm0,
kpts=kpts, max_memory=max_memory)[2]
if kpts is None:
kpts = np.zeros((1,3))
elif isinstance(kpts, KPoints):
kpts = kpts.kpts
if xctype == 'MGGA':
fmat, ao_deriv = (numint2c._mcol_mgga_fxc_mat , 1)
elif xctype == 'GGA':
fmat, ao_deriv = (numint2c._mcol_gga_fxc_mat , 1)
else:
fmat, ao_deriv = (numint2c._mcol_lda_fxc_mat , 0)
shls_slice = (0, cell.nbas)
ao_loc = cell.ao_loc_nr()
assert dms.ndim >= 3
make_rho1, nset, n2c = ni._gen_rho_evaluator(cell, dms, hermi, False)
nao = n2c // 2
nkpts = len(kpts)
vmat = np.zeros((nset,nkpts,n2c,n2c), dtype=np.complex128)
if xctype in ('LDA', 'GGA', 'MGGA'):
_rho0 = None
p1 = 0
for ao_k1, ao_k2, mask, weight, coords \
in ni.block_loop(cell, grids, nao, ao_deriv, kpts, None, max_memory):
p0, p1 = p1, p1 + weight.size
_fxc = fxc[:,:,:,:,p0:p1]
for i in range(nset):
rho1 = make_rho1(i, ao_k1, mask, xctype)
for k in range(nkpts):
vmat[i,k] += fmat(cell, ao_k1[k], weight, _rho0, rho1,
_fxc, mask, shls_slice, ao_loc, hermi)
elif xctype == 'HF':
pass
else:
raise NotImplementedError(f'numint2c.get_fxc for functional {xc_code}')
if dms.ndim == 3:
vmat = vmat[0]
return vmat
[docs]
@lib.with_doc(pnumint.KNumInt.nr_vxc.__doc__)
def nr_vxc(self, cell, grids, xc_code, dms, spin=0, relativity=0, hermi=1,
kpts=None, kpts_band=None, max_memory=2000, verbose=None):
if self.collinear[0] in ('m', 'n'): # mcol or ncol
n, exc, vmat = self._gks_mcol_vxc(
cell, grids, xc_code, dms, relativity, hermi,
kpts, kpts_band, max_memory, verbose)
else:
dms = np.asarray(dms)
nao = dms.shape[-1] // 2
# ground state density is always real
dm_a = dms[...,:nao,:nao].copy('C')
dm_b = dms[...,nao:,nao:].copy('C')
dm1 = (dm_a, dm_b)
ni = self._to_numint1c()
n, exc, v = ni.nr_uks(cell, grids, xc_code, dm1, relativity, hermi,
kpts, kpts_band, max_memory, verbose)
vmat = np.zeros(dms.shape, dtype=np.result_type(*v))
vmat[...,:nao,:nao] = v[0]
vmat[...,nao:,nao:] = v[1]
return n.sum(), exc, vmat
get_vxc = nr_gks_vxc = nr_vxc
get_fxc = nr_gks_fxc = nr_fxc = NumInt2C.nr_fxc
eval_xc_eff = numint2c._eval_xc_eff
mcfun_eval_xc_adapter = numint2c.mcfun_eval_xc_adapter
block_loop = pnumint.KNumInt.block_loop
_gen_rho_evaluator = pnumint.KNumInt._gen_rho_evaluator
def _to_numint1c(self):
'''Converts to the associated class to handle collinear systems'''
return self.view(pnumint.KNumInt)