Source code for pyscf.pbc.dft.multigrid.multigrid

#!/usr/bin/env python
# Copyright 2014-2024 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>
#

'''Multigrid to compute DFT integrals'''

import ctypes
import numpy
import numpy as np
import scipy.linalg

from pyscf import __config__
from pyscf import lib
from pyscf.lib import logger
from pyscf.gto import ATOM_OF, ANG_OF, NPRIM_OF, PTR_EXP, PTR_COEFF
from pyscf.dft.numint import libdft, BLKSIZE, MGGA_DENSITY_LAPL, LibXCMixin
from pyscf.pbc import tools
from pyscf.pbc import gto
from pyscf.pbc.gto import pseudo
from pyscf.pbc.gto.pseudo import pp_int
from pyscf.pbc.dft import numint, gen_grid
from pyscf.pbc.scf.khf import KSCF
from pyscf.pbc.df.df_jk import (
    _format_dms,
    _format_kpts_band,
    _format_jks,
)
from pyscf.pbc.lib.kpts_helper import gamma_point
from pyscf.pbc.lib.kpts import KPoints
from pyscf.pbc.df import fft, ft_ao, aft
from pyscf.pbc.dft.multigrid.utils import (
    _take_4d,
    _take_5d,
    _takebak_4d,
    _takebak_5d,
)

#sys.stderr.write('WARN: multigrid is an experimental feature. It is still in '
#                 'testing\nFeatures and APIs may be changed in the future.\n')

EXTRA_PREC = getattr(__config__, 'pbc_gto_eval_gto_extra_precision', 1e-2)
TO_EVEN_GRIDS = getattr(__config__, 'pbc_dft_multigrid_to_even', False)
RMAX_FACTOR_ORTH = getattr(__config__, 'pbc_dft_multigrid_rmax_factor_orth', 1.1)
RMAX_FACTOR_NONORTH = getattr(__config__, 'pbc_dft_multigrid_rmax_factor_nonorth', 0.5)
RMAX_RATIO = getattr(__config__, 'pbc_dft_multigrid_rmax_ratio', 0.7)
R_RATIO_SUBLOOP = getattr(__config__, 'pbc_dft_multigrid_r_ratio_subloop', 0.6)
INIT_MESH_ORTH = getattr(__config__, 'pbc_dft_multigrid_init_mesh_orth', (12,12,12))
INIT_MESH_NONORTH = getattr(__config__, 'pbc_dft_multigrid_init_mesh_nonorth', (32,32,32))
KE_RATIO = getattr(__config__, 'pbc_dft_multigrid_ke_ratio', 1.3)
TASKS_TYPE = getattr(__config__, 'pbc_dft_multigrid_tasks_type', 'ke_cut') # 'rcut'

# RHOG_HIGH_ORDER=True will compute the high order derivatives of electron
# density in real space and FT to reciprocal space.  Set RHOG_HIGH_ORDER=False
# to approximate the density derivatives in reciprocal space (without
# evaluating the high order derivatives in real space).
RHOG_HIGH_ORDER = getattr(__config__, 'pbc_dft_multigrid_rhog_high_order', False)

PTR_EXPDROP = 16
EXPDROP = getattr(__config__, 'pbc_dft_multigrid_expdrop', 1e-12)
IMAG_TOL = 1e-9


[docs] def eval_mat(cell, weights, shls_slice=None, comp=1, hermi=0, xctype='LDA', kpts=None, mesh=None, offset=None, submesh=None): assert (all(cell._bas[:,NPRIM_OF] == 1)) if mesh is None: mesh = cell.mesh vol = cell.vol weight_penalty = numpy.prod(mesh) / vol exp_min = numpy.hstack(cell.bas_exps()).min() theta_ij = exp_min / 2 lattice_sum_fac = max(2*numpy.pi*cell.rcut/(vol*theta_ij), 1) precision = cell.precision / weight_penalty / lattice_sum_fac if xctype != 'LDA': precision *= .1 atm, bas, env = gto.conc_env(cell._atm, cell._bas, cell._env, cell._atm, cell._bas, cell._env) env[PTR_EXPDROP] = min(precision*EXTRA_PREC, EXPDROP) ao_loc = gto.moleintor.make_loc(bas, 'cart') if shls_slice is None: shls_slice = (0, cell.nbas, 0, cell.nbas) i0, i1, j0, j1 = shls_slice j0 += cell.nbas j1 += cell.nbas naoi = ao_loc[i1] - ao_loc[i0] naoj = ao_loc[j1] - ao_loc[j0] Ls = gto.eval_gto.get_lattice_Ls(cell) nimgs = len(Ls) weights = numpy.asarray(weights, order='C') assert (weights.dtype == numpy.double) xctype = xctype.upper() n_mat = None if xctype == 'LDA': if weights.ndim == 1: weights = weights.reshape(-1, numpy.prod(mesh)) else: n_mat = weights.shape[0] elif xctype == 'GGA': if hermi == 1: raise RuntimeError('hermi=1 is not supported for GGA functional') if weights.ndim == 2: weights = weights.reshape(-1, 4, numpy.prod(mesh)) else: n_mat = weights.shape[0] else: raise NotImplementedError a = cell.lattice_vectors() b = numpy.linalg.inv(a.T) if offset is None: offset = (0, 0, 0) if submesh is None: submesh = mesh # log_prec is used to estimate the gto_rcut. Add EXTRA_PREC to count # other possible factors and coefficients in the integral. log_prec = numpy.log(precision * EXTRA_PREC) if abs(a-numpy.diag(a.diagonal())).max() < 1e-12: lattice_type = '_orth' else: lattice_type = '_nonorth' eval_fn = 'NUMINTeval_' + xctype.lower() + lattice_type drv = libdft.NUMINT_fill2c def make_mat(weights): mat = numpy.zeros((nimgs,comp,naoj,naoi)) drv(getattr(libdft, eval_fn), weights.ctypes.data_as(ctypes.c_void_p), mat.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(comp), ctypes.c_int(hermi), (ctypes.c_int*4)(i0, i1, j0, j1), ao_loc.ctypes.data_as(ctypes.c_void_p), ctypes.c_double(log_prec), ctypes.c_int(cell.dimension), ctypes.c_int(nimgs), Ls.ctypes.data_as(ctypes.c_void_p), a.ctypes.data_as(ctypes.c_void_p), b.ctypes.data_as(ctypes.c_void_p), (ctypes.c_int*3)(*offset), (ctypes.c_int*3)(*submesh), (ctypes.c_int*3)(*mesh), atm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(atm)), bas.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(bas)), env.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(env))) return mat out = [] for wv in weights: if cell.dimension == 0: mat = make_mat(wv)[0].transpose(0,2,1) if hermi == 1: for i in range(comp): lib.hermi_triu(mat[i], inplace=True) if comp == 1: mat = mat[0] elif kpts is None or gamma_point(kpts): mat = make_mat(wv).sum(axis=0).transpose(0,2,1) if hermi == 1: for i in range(comp): lib.hermi_triu(mat[i], inplace=True) if comp == 1: mat = mat[0] if getattr(kpts, 'ndim', None) == 2: mat = mat[None,:] else: mat = make_mat(wv) expkL = numpy.exp(1j*kpts.reshape(-1,3).dot(Ls.T)) mat = lib.einsum('kr,rcij->kcij', expkL, mat) if hermi == 1: for i in range(comp): for k in range(len(kpts)): lib.hermi_triu(mat[k,i], inplace=True) mat = mat.transpose(0,1,3,2) if comp == 1: mat = mat[:,0] out.append(mat) if n_mat is None: out = out[0] return out
[docs] def eval_rho(cell, dm, shls_slice=None, hermi=0, xctype='LDA', kpts=None, mesh=None, offset=None, submesh=None, ignore_imag=False, out=None): '''Collocate the *real* density (opt. gradients) on the real-space grid. Kwargs: ignore_image : The output density is assumed to be real if ignore_imag=True. ''' assert (all(cell._bas[:,NPRIM_OF] == 1)) if mesh is None: mesh = cell.mesh vol = cell.vol weight_penalty = numpy.prod(mesh) / vol exp_min = numpy.hstack(cell.bas_exps()).min() theta_ij = exp_min / 2 lattice_sum_fac = max(2*numpy.pi*cell.rcut/(vol*theta_ij), 1) precision = cell.precision / weight_penalty / lattice_sum_fac if xctype != 'LDA': precision *= .1 atm, bas, env = gto.conc_env(cell._atm, cell._bas, cell._env, cell._atm, cell._bas, cell._env) env[PTR_EXPDROP] = min(precision*EXTRA_PREC, EXPDROP) ao_loc = gto.moleintor.make_loc(bas, 'cart') if shls_slice is None: shls_slice = (0, cell.nbas, 0, cell.nbas) i0, i1, j0, j1 = shls_slice if hermi == 1: assert (i0 == j0 and i1 == j1) j0 += cell.nbas j1 += cell.nbas naoi = ao_loc[i1] - ao_loc[i0] naoj = ao_loc[j1] - ao_loc[j0] dm = numpy.asarray(dm, order='C') assert (dm.shape[-2:] == (naoi, naoj)) Ls = gto.eval_gto.get_lattice_Ls(cell) if cell.dimension == 0 or kpts is None or gamma_point(kpts): nkpts, nimgs = 1, Ls.shape[0] dm = dm.reshape(-1,1,naoi,naoj).transpose(0,1,3,2) else: expkL = numpy.exp(1j*kpts.reshape(-1,3).dot(Ls.T)) nkpts, nimgs = expkL.shape dm = dm.reshape(-1,nkpts,naoi,naoj).transpose(0,1,3,2) n_dm = dm.shape[0] a = cell.lattice_vectors() b = numpy.linalg.inv(a.T) if offset is None: offset = (0, 0, 0) if submesh is None: submesh = mesh log_prec = numpy.log(precision * EXTRA_PREC) if abs(a-numpy.diag(a.diagonal())).max() < 1e-12: lattice_type = '_orth' else: lattice_type = '_nonorth' xctype = xctype.upper() if xctype == 'LDA': comp = 1 elif xctype == 'GGA': if hermi == 1: raise RuntimeError('hermi=1 is not supported for GGA functional') comp = 4 else: raise NotImplementedError('meta-GGA') if comp == 1: shape = (numpy.prod(submesh),) else: shape = (comp, numpy.prod(submesh)) eval_fn = 'NUMINTrho_' + xctype.lower() + lattice_type drv = libdft.NUMINT_rho_drv def make_rho_(rho, dm, hermi): drv(getattr(libdft, eval_fn), rho.ctypes.data_as(ctypes.c_void_p), dm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(comp), ctypes.c_int(hermi), (ctypes.c_int*4)(i0, i1, j0, j1), ao_loc.ctypes.data_as(ctypes.c_void_p), ctypes.c_double(log_prec), ctypes.c_int(cell.dimension), ctypes.c_int(nimgs), Ls.ctypes.data_as(ctypes.c_void_p), a.ctypes.data_as(ctypes.c_void_p), b.ctypes.data_as(ctypes.c_void_p), (ctypes.c_int*3)(*offset), (ctypes.c_int*3)(*submesh), (ctypes.c_int*3)(*mesh), atm.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(atm)), bas.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(bas)), env.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(env))) return rho rho = [] for i, dm_i in enumerate(dm): if cell.dimension == 0: if ignore_imag: # basis are real. dm.imag can be dropped if ignore_imag dm_i = dm_i.real has_imag = dm_i.dtype == numpy.complex128 if has_imag: dmR = numpy.asarray(dm_i.real, order='C') dmI = numpy.asarray(dm_i.imag, order='C') else: # make a copy because the dm may be overwritten in the # NUMINT_rho_drv inplace dmR = numpy.array(dm_i, order='C', copy=True) elif kpts is None or gamma_point(kpts): if ignore_imag: # basis are real. dm.imag can be dropped if ignore_imag dm_i = dm_i.real has_imag = dm_i.dtype == numpy.complex128 if has_imag: dmR = numpy.repeat(dm_i.real, nimgs, axis=0) dmI = numpy.repeat(dm_i.imag, nimgs, axis=0) else: dmR = numpy.repeat(dm_i, nimgs, axis=0) else: dm_L = lib.dot(expkL.T, dm_i.reshape(nkpts,-1)).reshape(nimgs,naoj,naoi) dmR = numpy.asarray(dm_L.real, order='C') if ignore_imag: has_imag = False else: dmI = numpy.asarray(dm_L.imag, order='C') has_imag = (hermi == 0 and abs(dmI).max() > 1e-8) if (has_imag and xctype == 'LDA' and naoi == naoj and # For hermitian density matrices, the anti-symmetry # character of the imaginary part of the density matrices # can be found by rearranging the repeated images. abs(dm_i - dm_i.conj().transpose(0,2,1)).max() < 1e-8): has_imag = False dm_L = None if has_imag: # complex density cannot be updated inplace directly by # function NUMINT_rho_drv if out is None: rho_i = numpy.empty(shape, numpy.complex128) rho_i.real = make_rho_(numpy.zeros(shape), dmR, 0) rho_i.imag = make_rho_(numpy.zeros(shape), dmI, 0) else: assert out[i].dtype == numpy.complex128 rho_i = out[i].reshape(shape) rho_i.real += make_rho_(numpy.zeros(shape), dmR, 0) rho_i.imag += make_rho_(numpy.zeros(shape), dmI, 0) else: if out is None: # rho_i needs to be initialized to 0 because rho_i is updated # inplace in function NUMINT_rho_drv rho_i = make_rho_(numpy.zeros(shape), dmR, hermi) else: assert out[i].dtype == numpy.double rho_i = out[i].reshape(shape) make_rho_(rho_i, dmR, hermi) dmR = dmI = None rho.append(rho_i) if n_dm == 1: rho = rho[0] return rho
[docs] def get_nuc(mydf, kpts=None): if kpts is None: kpts = numpy.zeros((1, 3)) kpts, is_single_kpt = fft._check_kpts(mydf, kpts) cell = mydf.cell mesh = mydf.mesh charge = -cell.atom_charges() Gv = cell.get_Gv(mesh) #:SI = cell.get_SI(Gv) #:rhoG = numpy.dot(charge, SI) basex, basey, basez = cell.get_Gv_weights(mesh)[1] b = cell.reciprocal_vectors() rb = np.dot(cell.atom_coords(), b.T) SIx = np.exp(-1j*np.einsum('z,g->zg', rb[:,0], basex)) SIy = np.exp(-1j*np.einsum('z,g->zg', rb[:,1], basey)) SIz = np.exp(-1j*np.einsum('z,g->zg', rb[:,2], basez)) rhoG = np.einsum('i,ix,iy,iz->xyz', charge, SIx, SIy, SIz).ravel() coulG = tools.get_coulG(cell, mesh=mesh, Gv=Gv) vneG = rhoG * coulG hermi = 1 vne = _get_j_pass2(mydf, vneG, hermi, kpts)[0] if is_single_kpt: vne = vne[0] return numpy.asarray(vne)
[docs] def get_pp(mydf, kpts=None, max_memory=4000): '''Get the periodic pseudopotential nuc-el AO matrix, with G=0 removed. ''' from pyscf import gto if kpts is None: kpts = numpy.zeros((1, 3)) kpts, is_single_kpt = fft._check_kpts(mydf, kpts) cell = mydf.cell mesh = mydf.mesh Gv = cell.get_Gv(mesh) ngrids = len(Gv) vpplocG = numpy.empty((ngrids,), dtype=numpy.complex128) mem_avail = max(max_memory, mydf.max_memory-lib.current_memory()[0]) blksize = int(mem_avail*1e6/((cell.natm*2)*16)) blksize = min(ngrids, max(21**3, blksize)) for ig0, ig1 in lib.prange(0, ngrids, blksize): vpplocG_batch = pp_int.get_gth_vlocG_part1(cell, Gv[ig0:ig1]) SI = cell.get_SI(Gv[ig0:ig1]) vpplocG[ig0:ig1] = -numpy.einsum('ij,ij->j', SI, vpplocG_batch) hermi = 1 vpp = _get_j_pass2(mydf, vpplocG, hermi, kpts)[0] vpp2 = pp_int.get_pp_loc_part2(cell, kpts) for k, kpt in enumerate(kpts): vpp[k] += vpp2[k] # vppnonloc evaluated in reciprocal space fakemol = gto.Mole() fakemol._atm = numpy.zeros((1,gto.ATM_SLOTS), dtype=numpy.int32) fakemol._bas = numpy.zeros((1,gto.BAS_SLOTS), dtype=numpy.int32) ptr = gto.PTR_ENV_START fakemol._env = numpy.zeros(ptr+10) fakemol._bas[0,gto.NPRIM_OF ] = 1 fakemol._bas[0,gto.NCTR_OF ] = 1 fakemol._bas[0,gto.PTR_EXP ] = ptr+3 fakemol._bas[0,gto.PTR_COEFF] = ptr+4 def vppnl_by_k(kpt): SPG_lm_aoGs = [] for ia in range(cell.natm): symb = cell.atom_symbol(ia) if symb not in cell._pseudo: SPG_lm_aoGs.append(None) continue pp = cell._pseudo[symb] p1 = 0 for l, proj in enumerate(pp[5:]): rl, nl, hl = proj if nl > 0: p1 = p1+nl*(l*2+1) SPG_lm_aoGs.append(numpy.zeros((p1, cell.nao), dtype=numpy.complex128)) mem_avail = max(max_memory, mydf.max_memory-lib.current_memory()[0]) blksize = int(mem_avail*1e6/((48+cell.nao+13+3)*16)) blksize = min(ngrids, max(21**3, blksize)) vppnl = 0 for ig0, ig1 in lib.prange(0, ngrids, blksize): ng = ig1 - ig0 # buf for SPG_lmi upto l=0..3 and nl=3 buf = numpy.empty((48,ng), dtype=numpy.complex128) Gk = Gv[ig0:ig1] + kpt G_rad = numpy.linalg.norm(Gk, axis=1) aokG = ft_ao.ft_ao(cell, Gv[ig0:ig1], kpt=kpt) * (ngrids/cell.vol) for ia in range(cell.natm): symb = cell.atom_symbol(ia) if symb not in cell._pseudo: continue pp = cell._pseudo[symb] p1 = 0 for l, proj in enumerate(pp[5:]): rl, nl, hl = proj if nl > 0: fakemol._bas[0,gto.ANG_OF] = l fakemol._env[ptr+3] = .5*rl**2 fakemol._env[ptr+4] = rl**(l+1.5)*numpy.pi**1.25 pYlm_part = fakemol.eval_gto('GTOval', Gk) p0, p1 = p1, p1+nl*(l*2+1) # pYlm is real, SI[ia] is complex pYlm = numpy.ndarray((nl,l*2+1,ng), dtype=numpy.complex128, buffer=buf[p0:p1]) for k in range(nl): qkl = pseudo.pp._qli(G_rad*rl, l, k) pYlm[k] = pYlm_part.T * qkl #:SPG_lmi = numpy.einsum('g,nmg->nmg', SI[ia].conj(), pYlm) #:SPG_lm_aoG = numpy.einsum('nmg,gp->nmp', SPG_lmi, aokG) #:tmp = numpy.einsum('ij,jmp->imp', hl, SPG_lm_aoG) #:vppnl += numpy.einsum('imp,imq->pq', SPG_lm_aoG.conj(), tmp) if p1 > 0: SPG_lmi = buf[:p1] SPG_lmi *= cell.get_SI(Gv[ig0:ig1], atmlst=[ia,]).conj() SPG_lm_aoGs[ia] += lib.zdot(SPG_lmi, aokG) buf = None for ia in range(cell.natm): symb = cell.atom_symbol(ia) if symb not in cell._pseudo: continue pp = cell._pseudo[symb] p1 = 0 for l, proj in enumerate(pp[5:]): rl, nl, hl = proj if nl > 0: p0, p1 = p1, p1+nl*(l*2+1) hl = numpy.asarray(hl) SPG_lm_aoG = SPG_lm_aoGs[ia][p0:p1].reshape(nl,l*2+1,-1) tmp = numpy.einsum('ij,jmp->imp', hl, SPG_lm_aoG) vppnl += numpy.einsum('imp,imq->pq', SPG_lm_aoG.conj(), tmp) SPG_lm_aoGs=None return vppnl * (1./ngrids**2) for k, kpt in enumerate(kpts): vppnl = vppnl_by_k(kpt) if gamma_point(kpt): vpp[k] = vpp[k].real + vppnl.real else: vpp[k] += vppnl if is_single_kpt: vpp = vpp[0] return numpy.asarray(vpp)
[docs] def get_j_kpts(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), kpts_band=None): '''Get the Coulomb (J) AO matrix at sampled k-points. Args: dm_kpts : (nkpts, nao, nao) ndarray or a list of (nkpts,nao,nao) ndarray Density matrix at each k-point. If a list of k-point DMs, eg, UHF alpha and beta DM, the alpha and beta DMs are contracted separately. kpts : (nkpts, 3) ndarray Kwargs: kpts_band : ``(3,)`` ndarray or ``(*,3)`` ndarray A list of arbitrary "band" k-points at which to evalute the matrix. Returns: vj : (nkpts, nao, nao) ndarray or list of vj if the input dm_kpts is a list of DMs ''' cell = mydf.cell dm_kpts = numpy.asarray(dm_kpts) rhoG = _eval_rhoG(mydf, dm_kpts, hermi, kpts, deriv=0) coulG = tools.get_coulG(cell, mesh=cell.mesh) #:vG = numpy.einsum('ng,g->ng', rhoG[:,0], coulG) vG = rhoG[:,0] vG *= coulG kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band vj_kpts = _get_j_pass2(mydf, vG, hermi, kpts_band) return _format_jks(vj_kpts, dm_kpts, input_band, kpts)
def _eval_rhoG(mydf, dm_kpts, hermi=1, kpts=numpy.zeros((1,3)), deriv=0, rhog_high_order=RHOG_HIGH_ORDER): log = logger.Logger(mydf.stdout, mydf.verbose) cell = mydf.cell dm_kpts = lib.asarray(dm_kpts, order='C') dms = _format_dms(dm_kpts, kpts) nset, nkpts, nao = dms.shape[:3] tasks = getattr(mydf, 'tasks', None) if tasks is None: mydf.tasks = tasks = multi_grids_tasks(cell, mydf.mesh, log) log.debug('Multigrid ntasks %s', len(tasks)) assert (deriv < 2) #hermi = hermi and abs(dms - dms.transpose(0,1,3,2).conj()).max() < 1e-9 gga_high_order = False if deriv == 0: xctype = 'LDA' rhodim = 1 elif deriv == 1: if rhog_high_order: xctype = 'GGA' rhodim = 4 else: # approximate high order derivatives in reciprocal space gga_high_order = True xctype = 'LDA' rhodim = 1 deriv = 0 #if hermi != 1 and not gamma_point(kpts): # raise NotImplementedError elif deriv == 2: # meta-GGA raise NotImplementedError ignore_imag = (hermi == 1) nx, ny, nz = mydf.mesh rhoG = numpy.zeros((nset*rhodim,nx,ny,nz), dtype=numpy.complex128) for grids_dense, grids_sparse in tasks: h_cell = grids_dense.cell mesh = tuple(grids_dense.mesh) ngrids = numpy.prod(mesh) log.debug('mesh %s rcut %g', mesh, h_cell.rcut) if grids_sparse is None: # The first pass handles all diffused functions using the regular # matrix multiplication code. rho = numpy.zeros((nset,rhodim,ngrids), dtype=numpy.complex128) idx_h = grids_dense.ao_idx dms_hh = numpy.asarray(dms[:,:,idx_h[:,None],idx_h], order='C') fftdf = fft.FFTDF(cell, kpts=kpts) for ao_h_etc, p0, p1 in fftdf.aoR_loop(grids_dense, kpts, deriv): ao_h, mask = ao_h_etc[0], ao_h_etc[2] for k in range(nkpts): for i in range(nset): if xctype == 'LDA': ao_dm = lib.dot(ao_h[k], dms_hh[i,k]) rho_sub = numpy.einsum('xi,xi->x', ao_dm, ao_h[k].conj()) else: rho_sub = numint.eval_rho(h_cell, ao_h[k], dms_hh[i,k], mask, xctype, hermi) rho[i,:,p0:p1] += rho_sub ao_h = ao_h_etc = ao_dm = None if ignore_imag: rho = rho.real else: idx_h = grids_dense.ao_idx idx_l = grids_sparse.ao_idx idx_t = numpy.append(idx_h, idx_l) dms_ht = numpy.asarray(dms[:,:,idx_h[:,None],idx_t], order='C') dms_lh = numpy.asarray(dms[:,:,idx_l[:,None],idx_h], order='C') l_cell = grids_sparse.cell h_pcell, h_coeff = h_cell.decontract_basis(to_cart=True, aggregate=True) l_pcell, l_coeff = l_cell.decontract_basis(to_cart=True, aggregate=True) t_cell = h_pcell + l_pcell t_coeff = scipy.linalg.block_diag(h_coeff, l_coeff) nshells_h = _pgto_shells(h_cell) nshells_t = _pgto_shells(t_cell) if deriv == 0: if hermi == 1: naol, naoh = dms_lh.shape[2:] dms_ht[:,:,:,naoh:] += dms_lh.conj().transpose(0,1,3,2) pgto_dms = lib.einsum('nkij,pi,qj->nkpq', dms_ht, h_coeff, t_coeff) shls_slice = (0, nshells_h, 0, nshells_t) #:rho = eval_rho(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, #: offset=None, submesh=None, ignore_imag=True) rho = _eval_rho_bra(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, grids_dense, True, log) else: pgto_dms = lib.einsum('nkij,pi,qj->nkpq', dms_ht, h_coeff, t_coeff) shls_slice = (0, nshells_h, 0, nshells_t) #:rho = eval_rho(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, #: offset=None, submesh=None) rho = _eval_rho_bra(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, grids_dense, ignore_imag, log) pgto_dms = lib.einsum('nkij,pi,qj->nkpq', dms_lh, l_coeff, h_coeff) shls_slice = (nshells_h, nshells_t, 0, nshells_h) #:rho += eval_rho(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, #: offset=None, submesh=None) rho += _eval_rho_ket(t_cell, pgto_dms, shls_slice, 0, 'LDA', kpts, grids_dense, ignore_imag, log) elif deriv == 1: pgto_dms = lib.einsum('nkij,pi,qj->nkpq', dms_ht, h_coeff, t_coeff) shls_slice = (0, nshells_h, 0, nshells_t) #:rho = eval_rho(t_cell, pgto_dms, shls_slice, 0, 'GGA', kpts, #: ignore_imag=ignore_imag) rho = _eval_rho_bra(t_cell, pgto_dms, shls_slice, 0, 'GGA', kpts, grids_dense, ignore_imag, log) pgto_dms = lib.einsum('nkij,pi,qj->nkpq', dms_lh, l_coeff, h_coeff) shls_slice = (nshells_h, nshells_t, 0, nshells_h) #:rho += eval_rho(t_cell, pgto_dms, shls_slice, 0, 'GGA', kpts, #: ignore_imag=ignore_imag) rho += _eval_rho_ket(t_cell, pgto_dms, shls_slice, 0, 'GGA', kpts, grids_dense, ignore_imag, log) if hermi == 1: # \nabla \chi_i DM(i,j) \chi_j was computed above. # *2 for \chi_i DM(i,j) \nabla \chi_j rho[:,1:4] *= 2 else: raise NotImplementedError weight = 1./nkpts * cell.vol/ngrids rho_freq = tools.fft(rho.reshape(nset*rhodim, -1), mesh) rho_freq *= weight gx = numpy.fft.fftfreq(mesh[0], 1./mesh[0]).astype(numpy.int32) gy = numpy.fft.fftfreq(mesh[1], 1./mesh[1]).astype(numpy.int32) gz = numpy.fft.fftfreq(mesh[2], 1./mesh[2]).astype(numpy.int32) #:rhoG[:,gx[:,None,None],gy[:,None],gz] += rho_freq.reshape((-1,)+mesh) _takebak_4d(rhoG, rho_freq.reshape((-1,) + mesh), (None, gx, gy, gz)) rhoG = rhoG.reshape(nset,rhodim,-1) if gga_high_order: Gv = cell.get_Gv(mydf.mesh) rhoG1 = numpy.einsum('np,px->nxp', 1j*rhoG[:,0], Gv) rhoG = numpy.concatenate([rhoG, rhoG1], axis=1) return rhoG def _eval_rho_bra(cell, dms, shls_slice, hermi, xctype, kpts, grids, ignore_imag, log): a = cell.lattice_vectors() rmax = a.max() mesh = numpy.asarray(grids.mesh) rcut = grids.cell.rcut nset = dms.shape[0] if xctype == 'LDA': rhodim = 1 else: rhodim = 4 if rcut > rmax * R_RATIO_SUBLOOP: rho = eval_rho(cell, dms, shls_slice, hermi, xctype, kpts, mesh, ignore_imag=ignore_imag) return numpy.reshape(rho, (nset, rhodim, numpy.prod(mesh))) if hermi == 1 or ignore_imag: rho = numpy.zeros((nset, rhodim) + tuple(mesh)) else: rho = numpy.zeros((nset, rhodim) + tuple(mesh), dtype=numpy.complex128) b = numpy.linalg.inv(a.T) ish0, ish1, jsh0, jsh1 = shls_slice nshells_j = jsh1 - jsh0 pcell = cell.copy(deep=False) rest_dms = [] rest_bas = [] i1 = 0 for atm_id in set(cell._bas[ish0:ish1,ATOM_OF]): atm_bas_idx = numpy.where(cell._bas[ish0:ish1,ATOM_OF] == atm_id)[0] _bas_i = cell._bas[atm_bas_idx] l = _bas_i[:,ANG_OF] i0, i1 = i1, i1 + sum((l+1)*(l+2)//2) sub_dms = dms[:,:,i0:i1] atom_position = cell.atom_coord(atm_id) frac_edge0 = b.dot(atom_position - rcut) frac_edge1 = b.dot(atom_position + rcut) if (numpy.all(0 < frac_edge0) and numpy.all(frac_edge1 < 1)): pcell._bas = numpy.vstack((_bas_i, cell._bas[jsh0:jsh1])) nshells_i = len(atm_bas_idx) sub_slice = (0, nshells_i, nshells_i, nshells_i+nshells_j) offset = (frac_edge0 * mesh).astype(int) mesh1 = numpy.ceil(frac_edge1 * mesh).astype(int) submesh = mesh1 - offset log.debug1('atm %d rcut %f offset %s submesh %s', atm_id, rcut, offset, submesh) rho1 = eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, mesh, offset, submesh, ignore_imag=ignore_imag) #:rho[:,:,offset[0]:mesh1[0],offset[1]:mesh1[1],offset[2]:mesh1[2]] += \ #: numpy.reshape(rho1, (nset, rhodim) + tuple(submesh)) gx = numpy.arange(offset[0], mesh1[0], dtype=numpy.int32) gy = numpy.arange(offset[1], mesh1[1], dtype=numpy.int32) gz = numpy.arange(offset[2], mesh1[2], dtype=numpy.int32) _takebak_5d(rho, numpy.reshape(rho1, (nset,rhodim)+tuple(submesh)), (None, None, gx, gy, gz)) else: log.debug1('atm %d rcut %f over 2 images', atm_id, rcut) #:rho1 = eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, #: mesh, ignore_imag=ignore_imag) #:rho += numpy.reshape(rho1, rho.shape) # or #:eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, #: mesh, ignore_imag=ignore_imag, out=rho) rest_bas.append(_bas_i) rest_dms.append(sub_dms) if rest_bas: pcell._bas = numpy.vstack(rest_bas + [cell._bas[jsh0:jsh1]]) nshells_i = sum(len(x) for x in rest_bas) sub_slice = (0, nshells_i, nshells_i, nshells_i+nshells_j) sub_dms = numpy.concatenate(rest_dms, axis=2) # Update rho inplace eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, mesh, ignore_imag=ignore_imag, out=rho) return rho.reshape((nset, rhodim, numpy.prod(mesh))) def _eval_rho_ket(cell, dms, shls_slice, hermi, xctype, kpts, grids, ignore_imag, log): a = cell.lattice_vectors() rmax = a.max() mesh = numpy.asarray(grids.mesh) rcut = grids.cell.rcut nset = dms.shape[0] if xctype == 'LDA': rhodim = 1 else: rhodim = 4 if rcut > rmax * R_RATIO_SUBLOOP: rho = eval_rho(cell, dms, shls_slice, hermi, xctype, kpts, mesh, ignore_imag=ignore_imag) return numpy.reshape(rho, (nset, rhodim, numpy.prod(mesh))) if hermi == 1 or ignore_imag: rho = numpy.zeros((nset, rhodim) + tuple(mesh)) else: rho = numpy.zeros((nset, rhodim) + tuple(mesh), dtype=numpy.complex128) b = numpy.linalg.inv(a.T) ish0, ish1, jsh0, jsh1 = shls_slice nshells_i = ish1 - ish0 pcell = cell.copy(deep=False) rest_dms = [] rest_bas = [] j1 = 0 for atm_id in set(cell._bas[jsh0:jsh1,ATOM_OF]): atm_bas_idx = numpy.where(cell._bas[jsh0:jsh1,ATOM_OF] == atm_id)[0] _bas_j = cell._bas[atm_bas_idx] l = _bas_j[:,ANG_OF] j0, j1 = j1, j1 + sum((l+1)*(l+2)//2) sub_dms = dms[:,:,:,j0:j1] atom_position = cell.atom_coord(atm_id) frac_edge0 = b.dot(atom_position - rcut) frac_edge1 = b.dot(atom_position + rcut) if (numpy.all(0 < frac_edge0) and numpy.all(frac_edge1 < 1)): pcell._bas = numpy.vstack((cell._bas[ish0:ish1], _bas_j)) nshells_j = len(atm_bas_idx) sub_slice = (0, nshells_i, nshells_i, nshells_i+nshells_j) offset = (frac_edge0 * mesh).astype(int) mesh1 = numpy.ceil(frac_edge1 * mesh).astype(int) submesh = mesh1 - offset log.debug1('atm %d rcut %f offset %s submesh %s', atm_id, rcut, offset, submesh) rho1 = eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, mesh, offset, submesh, ignore_imag=ignore_imag) #:rho[:,:,offset[0]:mesh1[0],offset[1]:mesh1[1],offset[2]:mesh1[2]] += \ #: numpy.reshape(rho1, (nset, rhodim) + tuple(submesh)) gx = numpy.arange(offset[0], mesh1[0], dtype=numpy.int32) gy = numpy.arange(offset[1], mesh1[1], dtype=numpy.int32) gz = numpy.arange(offset[2], mesh1[2], dtype=numpy.int32) _takebak_5d(rho, numpy.reshape(rho1, (nset,rhodim)+tuple(submesh)), (None, None, gx, gy, gz)) else: log.debug1('atm %d rcut %f over 2 images', atm_id, rcut) #:rho1 = eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, #: mesh, ignore_imag=ignore_imag) #:rho += numpy.reshape(rho1, rho.shape) #:eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, #: mesh, ignore_imag=ignore_imag, out=rho) rest_bas.append(_bas_j) rest_dms.append(sub_dms) if rest_bas: pcell._bas = numpy.vstack([cell._bas[ish0:ish1]] + rest_bas) nshells_j = sum(len(x) for x in rest_bas) sub_slice = (0, nshells_i, nshells_i, nshells_i+nshells_j) sub_dms = numpy.concatenate(rest_dms, axis=3) # Update rho inplace eval_rho(pcell, sub_dms, sub_slice, hermi, xctype, kpts, mesh, ignore_imag=ignore_imag, out=rho) return rho.reshape((nset, rhodim, numpy.prod(mesh))) def _get_j_pass2(mydf, vG, hermi=1, kpts=numpy.zeros((1,3)), verbose=None): log = logger.new_logger(mydf, verbose) cell = mydf.cell nkpts = len(kpts) nao = cell.nao_nr() nx, ny, nz = mydf.mesh vG = vG.reshape(-1,nx,ny,nz) nset = vG.shape[0] tasks = getattr(mydf, 'tasks', None) if tasks is None: mydf.tasks = tasks = multi_grids_tasks(cell, mydf.mesh, log) log.debug('Multigrid ntasks %s', len(tasks)) at_gamma_point = gamma_point(kpts) if at_gamma_point: vj_kpts = numpy.zeros((nset,nkpts,nao,nao)) else: vj_kpts = numpy.zeros((nset,nkpts,nao,nao), dtype=numpy.complex128) for grids_dense, grids_sparse in tasks: mesh = grids_dense.mesh ngrids = numpy.prod(mesh) log.debug('mesh %s', mesh) gx = numpy.fft.fftfreq(mesh[0], 1./mesh[0]).astype(numpy.int32) gy = numpy.fft.fftfreq(mesh[1], 1./mesh[1]).astype(numpy.int32) gz = numpy.fft.fftfreq(mesh[2], 1./mesh[2]).astype(numpy.int32) #:sub_vG = vG[:,gx[:,None,None],gy[:,None],gz].reshape(nset,ngrids) sub_vG = _take_4d(vG, (None, gx, gy, gz)).reshape(nset,ngrids) v_rs = tools.ifft(sub_vG, mesh).reshape(nset,ngrids) vR = numpy.asarray(v_rs.real, order='C') vI = numpy.asarray(v_rs.imag, order='C') ignore_vG_imag = hermi == 1 or abs(vI.sum()) < IMAG_TOL if ignore_vG_imag: v_rs = vR elif vj_kpts.dtype == numpy.double: # ensure result complex array if tddft amplitudes are complex while # at gamma point vj_kpts = vj_kpts.astype(numpy.complex128) idx_h = grids_dense.ao_idx if grids_sparse is None: fftdf = fft.FFTDF(cell, kpts=kpts) for ao_h_etc, p0, p1 in fftdf.aoR_loop(grids_dense, kpts): ao_h = ao_h_etc[0] for k in range(nkpts): for i in range(nset): aow = numint._scale_ao(ao_h[k], v_rs[i,p0:p1]) vj_sub = lib.dot(ao_h[k].conj().T, aow) vj_kpts[i,k,idx_h[:,None],idx_h] += vj_sub ao_h = ao_h_etc = None else: idx_h = grids_dense.ao_idx idx_l = grids_sparse.ao_idx # idx_t = numpy.append(idx_h, idx_l) naoh = len(idx_h) h_cell = grids_dense.cell l_cell = grids_sparse.cell h_pcell, h_coeff = h_cell.decontract_basis(to_cart=True, aggregate=True) l_pcell, l_coeff = l_cell.decontract_basis(to_cart=True, aggregate=True) t_cell = h_pcell + l_pcell t_coeff = scipy.linalg.block_diag(h_coeff, l_coeff) nshells_h = _pgto_shells(h_cell) nshells_t = _pgto_shells(t_cell) shls_slice = (0, nshells_h, 0, nshells_t) vp = eval_mat(t_cell, vR, shls_slice, 1, 0, 'LDA', kpts) # Imaginary part may contribute if not ignore_vG_imag: vpI = eval_mat(t_cell, vI, shls_slice, 1, 0, 'LDA', kpts) vp = numpy.asarray(vp) + numpy.asarray(vpI) * 1j vpI = None vp = lib.einsum('nkpq,pi,qj->nkij', vp, h_coeff, t_coeff) vj_kpts[:,:,idx_h[:,None],idx_h] += vp[:,:,:,:naoh] vj_kpts[:,:,idx_h[:,None],idx_l] += vp[:,:,:,naoh:] if hermi == 1: vj_kpts[:,:,idx_l[:,None],idx_h] += \ vp[:,:,:,naoh:].transpose(0,1,3,2).conj() else: shls_slice = (nshells_h, nshells_t, 0, nshells_h) vp = eval_mat(t_cell, vR, shls_slice, 1, 0, 'LDA', kpts) # Imaginary part may contribute if not ignore_vG_imag: vpI = eval_mat(t_cell, vI, shls_slice, 1, 0, 'LDA', kpts) vp = numpy.asarray(vp) + numpy.asarray(vpI) * 1j vpI = None vp = lib.einsum('nkpq,pi,qj->nkij', vp, l_coeff, h_coeff) vj_kpts[:,:,idx_l[:,None],idx_h] += vp return vj_kpts def _get_gga_pass2(mydf, vG, hermi=1, kpts=numpy.zeros((1,3)), verbose=None): #if hermi != 1: # raise NotImplementedError('_get_gga_pass2 assumes hermi=1') log = logger.new_logger(mydf, verbose) cell = mydf.cell nkpts = len(kpts) nao = cell.nao_nr() nx, ny, nz = mydf.mesh vG = vG.reshape(-1,4,nx,ny,nz) nset = vG.shape[0] at_gamma_point = gamma_point(kpts) if at_gamma_point: veff = numpy.zeros((nset,nkpts,nao,nao)) else: veff = numpy.zeros((nset,nkpts,nao,nao), dtype=numpy.complex128) for grids_dense, grids_sparse in mydf.tasks: mesh = grids_dense.mesh ngrids = numpy.prod(mesh) log.debug('mesh %s', mesh) gx = numpy.fft.fftfreq(mesh[0], 1./mesh[0]).astype(numpy.int32) gy = numpy.fft.fftfreq(mesh[1], 1./mesh[1]).astype(numpy.int32) gz = numpy.fft.fftfreq(mesh[2], 1./mesh[2]).astype(numpy.int32) #:sub_vG = vG[:,:,gx[:,None,None],gy[:,None],gz].reshape(-1,ngrids) sub_vG = _take_5d(vG, (None, None, gx, gy, gz)).reshape(-1,ngrids) v_rs = tools.ifft(sub_vG, mesh).reshape(nset,4,ngrids) vR = numpy.asarray(v_rs.real, order='C') vI = numpy.asarray(v_rs.imag, order='C') ignore_vG_imag = hermi == 1 or abs(vI.sum()) < IMAG_TOL if ignore_vG_imag: v_rs = vR elif veff.dtype == numpy.double: # ensure result complex array if tddft amplitudes are complex while # at gamma point veff = veff.astype(numpy.complex128) if grids_sparse is None: idx_h = grids_dense.ao_idx naoh = len(idx_h) fftdf = fft.FFTDF(cell, kpts=kpts) for ao_h_etc, p0, p1 in fftdf.aoR_loop(grids_dense, kpts, deriv=1): ao_h = ao_h_etc[0] for k in range(nkpts): for i in range(nset): aow = numint._scale_ao(ao_h[k], v_rs[i]) v = lib.dot(ao_h[k][0].conj().T, aow) veff[i,k,idx_h[:,None],idx_h] += v if hermi == 1: veff[i,k,idx_h[:,None],idx_h] += v.conj().T else: aow = numint._scale_ao(ao_h[k], v_rs[i].conj()) v = lib.dot(aow.conj().T, ao_h[k][0]) veff[i,k,idx_h[:,None],idx_h] += v ao_h = ao_h_etc = None else: idx_h = grids_dense.ao_idx idx_l = grids_sparse.ao_idx # idx_t = numpy.append(idx_h, idx_l) naoh = len(idx_h) h_cell = grids_dense.cell l_cell = grids_sparse.cell h_pcell, h_coeff = h_cell.decontract_basis(to_cart=True, aggregate=True) l_pcell, l_coeff = l_cell.decontract_basis(to_cart=True, aggregate=True) t_cell = h_pcell + l_pcell t_coeff = scipy.linalg.block_diag(h_coeff, l_coeff) nshells_h = _pgto_shells(h_cell) nshells_t = _pgto_shells(t_cell) shls_slice = (0, nshells_h, 0, nshells_t) vpR = eval_mat(t_cell, vR, shls_slice, 1, 0, 'GGA', kpts) vp = vpR = lib.einsum('nkpq,pi,qj->nkij', vpR, h_coeff, t_coeff) if not ignore_vG_imag: vpI = eval_mat(t_cell, vI, shls_slice, 1, 0, 'GGA', kpts) vpI = lib.einsum('nkpq,pi,qj->nkij', vpI, h_coeff, t_coeff) vp = numpy.asarray(vpR) + numpy.asarray(vpI) * 1j veff[:,:,idx_h[:,None],idx_h] += vp[:,:,:,:naoh] veff[:,:,idx_h[:,None],idx_l] += vp[:,:,:,naoh:] if hermi == 1: veff[:,:,idx_h[:,None],idx_h] += vp[:,:,:,:naoh].conj().transpose(0,1,3,2) veff[:,:,idx_l[:,None],idx_h] += vp[:,:,:,naoh:].conj().transpose(0,1,3,2) else: if not ignore_vG_imag: # eval_mat only supports <nabla i|v|j>. Evaluate <i|v|nabla j> # by conj(<nabla j|conj(v)|>) vp = numpy.asarray(vpR) - numpy.asarray(vpI) * 1j veff[:,:,idx_h[:,None],idx_h] += vp[:,:,:,:naoh].conj().transpose(0,1,3,2) veff[:,:,idx_l[:,None],idx_h] += vp[:,:,:,naoh:].conj().transpose(0,1,3,2) shls_slice = (nshells_h, nshells_t, 0, nshells_h) vpR = eval_mat(t_cell, vR, shls_slice, 1, 0, 'GGA', kpts) vp = vpR = lib.einsum('nkpq,pi,qj->nkij', vpR, l_coeff, h_coeff) if not ignore_vG_imag: vpI = eval_mat(t_cell, vI, shls_slice, 1, 0, 'GGA', kpts) vpI = lib.einsum('nkpq,pi,qj->nkij', vpI, l_coeff, h_coeff) vp = numpy.asarray(vpR) + numpy.asarray(vpI) * 1j veff[:,:,idx_l[:,None],idx_h] += vp if hermi == 1: veff[:,:,idx_h[:,None],idx_l] += vp.conj().transpose(0,1,3,2) else: if not ignore_vG_imag: vp = numpy.asarray(vpR) - numpy.asarray(vpI) * 1j veff[:,:,idx_h[:,None],idx_l] += vp.conj().transpose(0,1,3,2) return veff
[docs] def nr_rks(mydf, xc_code, dm_kpts, hermi=1, kpts=None, kpts_band=None, with_j=False, return_j=False, verbose=None): '''Compute the XC energy and RKS XC matrix at sampled k-points. multigrid version of function pbc.dft.numint.nr_rks. Args: dm_kpts : (nkpts, nao, nao) ndarray or a list of (nkpts,nao,nao) ndarray Density matrix at each k-point. kpts : (nkpts, 3) ndarray Kwargs: kpts_band : ``(3,)`` ndarray or ``(*,3)`` ndarray A list of arbitrary "band" k-points at which to evalute the matrix. Returns: exc : XC energy nelec : number of electrons obtained from the numerical integration veff : (nkpts, nao, nao) ndarray or list of veff if the input dm_kpts is a list of DMs vj : (nkpts, nao, nao) ndarray or list of vj if the input dm_kpts is a list of DMs ''' if kpts is None: kpts = numpy.zeros((1, 3)) elif isinstance(kpts, KPoints): if kpts.kpts.size > 3: # multiple k points dm_kpts = kpts.transform_dm(dm_kpts) kpts = kpts.kpts kpts = kpts.reshape(-1,3) log = logger.new_logger(mydf, verbose) cell = mydf.cell kpts = kpts.reshape(-1, 3) dm_kpts = lib.asarray(dm_kpts, order='C') dms = _format_dms(dm_kpts, kpts) nset, nkpts, nao = dms.shape[:3] assert nset == 1 kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band ni = mydf xctype = ni._xc_type(xc_code) if xctype == 'LDA': deriv = 0 elif xctype == 'GGA': deriv = 1 elif xctype == 'MGGA': deriv = 2 if MGGA_DENSITY_LAPL else 1 raise NotImplementedError rhoG = _eval_rhoG(mydf, dm_kpts, hermi, kpts, deriv) mesh = mydf.mesh ngrids = numpy.prod(mesh) coulG = tools.get_coulG(cell, mesh=mesh) vG = rhoG[0,0] * coulG ecoul = .5 * numpy.vdot(rhoG[0,0], vG).real ecoul /= cell.vol log.debug('Multigrid Coulomb energy %s', ecoul) weight = cell.vol / ngrids # *(1./weight) because rhoR is scaled by weight in _eval_rhoG. When # computing rhoR with IFFT, the weight factor is not needed. rhoR = tools.ifft(rhoG.reshape(-1,ngrids), mesh).real * (1./weight) rhoR = rhoR.reshape(-1,ngrids) nelec = rhoR[0].sum() * weight if xctype == 'LDA': exc, vxc = ni.eval_xc_eff(xc_code, rhoR[0], deriv=1, xctype=xctype)[:2] else: exc, vxc = ni.eval_xc_eff(xc_code, rhoR, deriv=1, xctype=xctype)[:2] excsum = rhoR[0].dot(exc).sum() * weight wv = weight * vxc wv_freq = tools.fft(wv, mesh).reshape(-1,ngrids) rhoR = rhoG = None log.debug('Multigrid exc %s nelec %s', excsum, nelec) if with_j: wv_freq[0] += vG kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band if xctype == 'LDA': veff = _get_j_pass2(mydf, wv_freq, hermi, kpts_band, verbose=log) elif xctype == 'GGA': ## *.5 because v+v.T is always called in _get_gga_pass2 #wv_freq[0] *= .5 #veff = _get_gga_pass2(mydf, wv_freq, hermi, kpts_band, verbose=log) Gv = cell.get_Gv(ni.mesh) wv_freq[0] -= numpy.einsum('xp,px->p', 1j*wv_freq[1:4], Gv) veff = _get_j_pass2(mydf, wv_freq[0], hermi, kpts_band, verbose=log) veff = _format_jks(veff, dm_kpts, input_band, kpts) if return_j: vj = _get_j_pass2(mydf, vG, hermi, kpts_band, verbose=log) vj = _format_jks(veff, dm_kpts, input_band, kpts) else: vj = None shape = list(dm_kpts.shape) if len(shape) == 3 and shape[0] != kpts_band.shape[0]: shape[0] = kpts_band.shape[0] veff = veff.reshape(shape) veff = lib.tag_array(veff, ecoul=ecoul, exc=excsum, vj=vj, vk=None) return nelec, excsum, veff
# Note nr_uks handles only one set of KUKS density matrices (alpha, beta) in # each call (nr_rks supports multiple sets of KRKS density matrices)
[docs] def nr_uks(mydf, xc_code, dm_kpts, hermi=1, kpts=None, kpts_band=None, with_j=False, return_j=False, verbose=None): '''Compute the XC energy and UKS XC matrix at sampled k-points. multigrid version of function pbc.dft.numint.nr_uks Args: dm_kpts : (nkpts, nao, nao) ndarray or a list of (nkpts,nao,nao) ndarray Density matrix at each k-point. kpts : (nkpts, 3) ndarray Kwargs: kpts_band : ``(3,)`` ndarray or ``(*,3)`` ndarray A list of arbitrary "band" k-points at which to evalute the matrix. Returns: exc : XC energy nelec : number of electrons obtained from the numerical integration veff : (2, nkpts, nao, nao) ndarray or list of veff if the input dm_kpts is a list of DMs vj : (nkpts, nao, nao) ndarray or list of vj if the input dm_kpts is a list of DMs ''' if kpts is None: kpts = numpy.zeros((1, 3)) elif isinstance(kpts, KPoints): if kpts.kpts.size > 3: # multiple k points dm_kpts = kpts.transform_dm(dm_kpts) kpts = kpts.kpts kpts = kpts.reshape(-1,3) log = logger.new_logger(mydf, verbose) cell = mydf.cell kpts = kpts.reshape(-1, 3) dm_kpts = lib.asarray(dm_kpts, order='C') dms = _format_dms(dm_kpts, kpts) nset, nkpts, nao = dms.shape[:3] nset //= 2 # Do not support gks assert nset == 1 kpts_band, input_band = _format_kpts_band(kpts_band, kpts), kpts_band ni = mydf xctype = ni._xc_type(xc_code) if xctype == 'LDA': deriv = 0 elif xctype == 'GGA': deriv = 1 elif xctype == 'MGGA': raise NotImplementedError mesh = mydf.mesh ngrids = numpy.prod(mesh) rhoG = _eval_rhoG(mydf, dm_kpts, hermi, kpts, deriv) rhoG = rhoG.reshape(2,-1,ngrids) rhoG_sf = rhoG[0,0] + rhoG[1,0] coulG = tools.get_coulG(cell, mesh=mesh) vG = rhoG_sf * coulG ecoul = .5 * numpy.vdot(rhoG_sf, vG).real ecoul /= cell.vol log.debug('Multigrid Coulomb energy %s', ecoul) weight = cell.vol / ngrids # *(1./weight) because rhoR is scaled by weight in _eval_rhoG. When # computing rhoR with IFFT, the weight factor is not needed. rhoR = tools.ifft(rhoG.reshape(-1,ngrids), mesh).real * (1./weight) rhoR = rhoR.reshape(2,-1,ngrids) nelec = numpy.einsum('sg->s', rhoR[:,0]) * weight wv_freq = [] exc, vxc = ni.eval_xc_eff(xc_code, rhoR, deriv=1, xctype=xctype)[:2] excsum = rhoR[:,0].dot(exc).sum() * weight log.debug('Multigrid exc %g nelec %s', excsum, nelec) wv = weight * vxc wv_freq = tools.fft(wv.reshape(-1, *mesh), mesh).reshape(2,-1,ngrids) rhoR = rhoG = None if with_j: wv_freq[:,0] += vG if xctype == 'LDA': veff = _get_j_pass2(mydf, wv_freq, hermi, kpts_band, verbose=log) elif xctype == 'GGA': ## *.5 because v+v.T is always called in _get_gga_pass2 #wv_freq[:,0] *= .5 #veff = _get_gga_pass2(mydf, wv_freq, hermi, kpts_band, verbose=log) Gv = cell.get_Gv(ni.mesh) wv_freq[:,0] -= numpy.einsum('nxp,px->np', wv_freq[:,1:4], 1j*Gv) veff = _get_j_pass2(mydf, wv_freq[:,0], hermi, kpts_band, verbose=log) veff = _format_jks(veff, dm_kpts, input_band, kpts) veff = veff.reshape(2, len(kpts_band), nao, nao) if return_j: vj = _get_j_pass2(mydf, vG, hermi, kpts_band, verbose=log) vj = _format_jks(veff, dm_kpts, input_band, kpts)[0] else: vj = None shape = list(dm_kpts.shape) if len(shape) == 4 and shape[1] != kpts_band.shape[0]: shape[1] = kpts_band.shape[0] veff = veff.reshape(shape) veff = lib.tag_array(veff, ecoul=ecoul, exc=excsum, vj=vj, vk=None) return nelec, excsum, veff
[docs] def nr_rks_fxc(mydf, xc_code, dm0, dms, hermi=0, with_j=False, rho0=None, vxc=None, fxc=None, kpts=None, verbose=None): '''multigrid version of function pbc.dft.numint.nr_rks_fxc ''' log = logger.new_logger(mydf, verbose) cell = mydf.cell mesh = mydf.mesh ngrids = numpy.prod(mesh) ni = mydf xctype = ni._xc_type(xc_code) if xctype == 'LDA': deriv = 0 elif xctype == 'GGA': deriv = 1 elif xctype == 'MGGA': deriv = 2 if MGGA_DENSITY_LAPL else 1 raise NotImplementedError if fxc is None: fxc = cache_xc_kernel1(mydf, xc_code, dm0, spin=0, kpts=kpts)[2] if kpts is None: kpts = numpy.zeros((1,3)) elif isinstance(kpts, KPoints): kpts = kpts.kpts_ibz kpts = kpts.reshape(-1, 3) dm_kpts = lib.asarray(dms, order='C') dms = _format_dms(dm_kpts, kpts) nset, nkpts, nao = dms.shape[:3] rhoG = _eval_rhoG(mydf, dms, hermi, kpts, deriv) rho1 = tools.ifft(rhoG.reshape(-1,ngrids), mesh) if hermi == 1: rho1 = rho1.real weight = cell.vol / ngrids rho1 *= (1./weight) rho1 = rho1.reshape(nset,-1,ngrids) wv = numpy.einsum('nxg,xyg->nyg', rho1, fxc) wv *= weight wv = tools.fft(wv.reshape(-1,ngrids), mesh).reshape(nset,-1,ngrids) if with_j: coulG = tools.get_coulG(cell, mesh=mesh) vG = rhoG[:,0] * coulG wv[:,0] += vG if xctype == 'LDA': veff = _get_j_pass2(mydf, wv, hermi, kpts, verbose=log) elif xctype == 'GGA': ## *.5 because v+v.T is always called in _get_gga_pass2 #wv[:,0] *= .5 #veff = _get_gga_pass2(mydf, wv, hermi, kpts, verbose=log) Gv = cell.get_Gv(ni.mesh) wv[:,0] -= numpy.einsum('nxp,px->np', wv[:,1:4], 1j*Gv) veff = _get_j_pass2(mydf, wv[:,0], hermi, kpts, verbose=log) return veff.reshape(dm_kpts.shape)
[docs] def nr_rks_fxc_st(mydf, xc_code, dm0, dms_alpha, hermi=1, singlet=True, rho0=None, vxc=None, fxc=None, kpts=None, with_j=False, verbose=None): '''multigrid version of function pbc.dft.numint.nr_rks_fxc_st ''' if fxc is None: fxc = cache_xc_kernel1(mydf, xc_code, dm0, spin=1, kpts=kpts)[2] if singlet: fxc = fxc[0,:,0] + fxc[0,:,1] else: fxc = fxc[0,:,0] - fxc[0,:,1] return nr_rks_fxc(mydf, xc_code, dm0, dms_alpha, hermi=hermi, with_j=with_j, fxc=fxc, kpts=kpts, verbose=verbose)
[docs] def nr_uks_fxc(mydf, xc_code, dm0, dms, hermi=0, with_j=False, rho0=None, vxc=None, fxc=None, kpts=None, verbose=None): '''multigrid version of function pbc.dft.numint.nr_uks_fxc ''' log = logger.new_logger(mydf, verbose) cell = mydf.cell mesh = mydf.mesh ngrids = numpy.prod(mesh) ni = mydf xctype = ni._xc_type(xc_code) if xctype == 'LDA': deriv = 0 elif xctype == 'GGA': deriv = 1 elif xctype == 'MGGA': deriv = 2 if MGGA_DENSITY_LAPL else 1 raise NotImplementedError if fxc is None: fxc = cache_xc_kernel1(mydf, xc_code, dm0, spin=1, kpts=kpts)[2] if kpts is None: kpts = numpy.zeros((1,3)) elif isinstance(kpts, KPoints): kpts = kpts.kpts_ibz kpts = kpts.reshape(-1, 3) dm_kpts = lib.asarray(dms, order='C') dms = _format_dms(dm_kpts, kpts) nset, nkpts, nao = dms.shape[:3] nstates = nset // 2 rhoG = _eval_rhoG(mydf, dms, hermi, kpts, deriv) rho1 = tools.ifft(rhoG.reshape(-1,ngrids), mesh) if hermi == 1: rho1 = rho1.real weight = cell.vol / ngrids rho1 *= (1./weight) # rho1 = (rho1a, rho1b); rho1.shape = (2, nstates, nvar, ngrids) rho1 = rho1.reshape(2,nstates,-1,ngrids) wv = numpy.einsum('anxg,axbyg->bnyg', rho1, fxc) wv *= weight wv = tools.fft(wv.reshape(-1,ngrids), mesh).reshape(2,nstates,-1,ngrids) if with_j: coulG = tools.get_coulG(cell, mesh=mesh) rhoG = rhoG.reshape(2,nstates,-1,ngrids) vG = numpy.einsum('ang,g->ng', rhoG[:,:,0], coulG) wv[:,:,0] += vG if xctype == 'LDA': veff = _get_j_pass2(mydf, wv, hermi, kpts, verbose=log) elif xctype == 'GGA': ## *.5 because v+v.T is always called in _get_gga_pass2 #wv[:,0] *= .5 #veff = _get_gga_pass2(mydf, wv, hermi, kpts, verbose=log) Gv = cell.get_Gv(ni.mesh) wv[:,:,0] -= numpy.einsum('anxp,px->anp', 1j*wv[:,:,1:4], Gv) veff = _get_j_pass2(mydf, wv[:,:,0], hermi, kpts, verbose=log) return veff.reshape(dm_kpts.shape)
[docs] def cache_xc_kernel(mydf, xc_code, mo_coeff, mo_occ, spin=0, kpts=None): if mo_occ[0][0].ndim == 1: # KUHF/KUKS dm = [] for spin in range(len(mo_occ)): dm.append([(c*o).dot(c.conj().T) for c, o in zip(mo_coeff[spin], mo_occ[spin])]) elif mo_occ[0].ndim == 1: # UHF/UKS or KRHF/KRKS dm = [(c*o).dot(c.conj().T) for c, o in zip(mo_coeff, mo_occ)] else: # RHF/RKS dm = (mo_coeff*mo_occ).dot(mo_coeff.conj().T) return cache_xc_kernel1(mydf, xc_code, dm, spin, kpts)
[docs] def cache_xc_kernel1(mydf, xc_code, dm, spin=0, kpts=None): '''Compute the 0th order density, Vxc and fxc. They can be used in TDDFT, DFT hessian module etc. ''' if kpts is None: kpts = numpy.zeros((1,3)) elif isinstance(kpts, KPoints): if kpts.kpts.size > 3: # multiple k points dm = kpts.transform_dm(dm) kpts = kpts.kpts kpts = kpts.reshape(-1,3) cell = mydf.cell mesh = mydf.mesh ngrids = numpy.prod(mesh) ni = mydf xctype = ni._xc_type(xc_code) if xctype == 'LDA': deriv = 0 comp = 1 elif xctype == 'GGA': deriv = 1 comp = 4 elif xctype == 'MGGA': deriv = 2 if MGGA_DENSITY_LAPL else 1 comp = 6 hermi = 1 weight = cell.vol / ngrids rhoG = _eval_rhoG(mydf, dm, hermi, kpts, deriv) rho = tools.ifft(rhoG.reshape(-1,ngrids), mesh).real * (1./weight) rho = rho.reshape(rhoG.shape) n_dm, comp, ngrids = rho.shape if n_dm == 1 and spin == 1: rho = numpy.repeat(rho, 2, axis=0) rho *= .5 if xctype == 'LDA': assert comp == 1 rho = rho[:,0] else: assert comp > 1 if spin == 0: assert n_dm == 1 rho = rho[0] vxc, fxc = ni.eval_xc_eff(xc_code, rho, deriv=2, xctype=xctype)[1:3] return rho, vxc, fxc
[docs] def get_rho(mydf, dm, kpts=None): '''Density in real space ''' if kpts is None: kpts = numpy.zeros((1,3)) elif isinstance(kpts, KPoints): if kpts.kpts.size > 3: # multiple k points dm = kpts.transform_dm(dm) kpts = kpts.kpts kpts = kpts.reshape(-1,3) cell = mydf.cell hermi = 1 rhoG = _eval_rhoG(mydf, numpy.asarray(dm), hermi, kpts, deriv=0) mesh = mydf.mesh ngrids = numpy.prod(mesh) weight = cell.vol / ngrids # *(1./weight) because rhoR is scaled by weight in _eval_rhoG. When # computing rhoR with IFFT, the weight factor is not needed. rhoR = tools.ifft(rhoG.reshape(ngrids), mesh).real * (1./weight) return rhoR
[docs] def multi_grids_tasks(cell, fft_mesh=None, verbose=None): if TASKS_TYPE == 'rcut': return multi_grids_tasks_for_rcut(cell, fft_mesh, verbose) else: return multi_grids_tasks_for_ke_cut(cell, fft_mesh, verbose)
[docs] def multi_grids_tasks_for_rcut(cell, fft_mesh=None, verbose=None): log = logger.new_logger(cell, verbose) if fft_mesh is None: fft_mesh = cell.mesh # Split shells based on rcut rcuts_pgto, kecuts_pgto = _primitive_gto_cutoff(cell) ao_loc = cell.ao_loc_nr() def make_cell_dense_exp(shls_dense, r0, r1): cell_dense = cell.copy(deep=False) cell_dense._bas = cell._bas.copy() cell_dense._env = cell._env.copy() rcut_atom = [0] * cell.natm ke_cutoff = 0 for ib in shls_dense: rc = rcuts_pgto[ib] idx = numpy.where((r1 <= rc) & (rc < r0))[0] np1 = len(idx) cs = cell._libcint_ctr_coeff(ib) np, nc = cs.shape if np1 < np: # no pGTO splitting within the shell pexp = cell._bas[ib,PTR_EXP] pcoeff = cell._bas[ib,PTR_COEFF] cs1 = cs[idx] cell_dense._env[pcoeff:pcoeff+cs1.size] = cs1.T.ravel() cell_dense._env[pexp:pexp+np1] = cell.bas_exp(ib)[idx] cell_dense._bas[ib,NPRIM_OF] = np1 ke_cutoff = max(ke_cutoff, kecuts_pgto[ib][idx].max()) ia = cell.bas_atom(ib) rcut_atom[ia] = max(rcut_atom[ia], rc[idx].max()) cell_dense._bas = cell_dense._bas[shls_dense] ao_idx = numpy.hstack([numpy.arange(ao_loc[i], ao_loc[i+1]) for i in shls_dense]) cell_dense.rcut = max(rcut_atom) return cell_dense, ao_idx, ke_cutoff, rcut_atom def make_cell_sparse_exp(shls_sparse, r0, r1): cell_sparse = cell.copy(deep=False) cell_sparse._bas = cell._bas.copy() cell_sparse._env = cell._env.copy() for ib in shls_sparse: idx = numpy.where(r0 <= rcuts_pgto[ib])[0] np1 = len(idx) cs = cell._libcint_ctr_coeff(ib) np, nc = cs.shape if np1 < np: # no pGTO splitting within the shell pexp = cell._bas[ib,PTR_EXP] pcoeff = cell._bas[ib,PTR_COEFF] cs1 = cs[idx] cell_sparse._env[pcoeff:pcoeff+cs1.size] = cs1.T.ravel() cell_sparse._env[pexp:pexp+np1] = cell.bas_exp(ib)[idx] cell_sparse._bas[ib,NPRIM_OF] = np1 cell_sparse._bas = cell_sparse._bas[shls_sparse] ao_idx = numpy.hstack([numpy.arange(ao_loc[i], ao_loc[i+1]) for i in shls_sparse]) return cell_sparse, ao_idx tasks = [] a = cell.lattice_vectors() if abs(a-numpy.diag(a.diagonal())).max() < 1e-12: rmax = a.max() * RMAX_FACTOR_ORTH else: rmax = a.max() * RMAX_FACTOR_NONORTH n_delimeter = int(numpy.log(0.005/rmax) / numpy.log(RMAX_RATIO)) rcut_delimeter = rmax * (RMAX_RATIO ** numpy.arange(n_delimeter)) for r0, r1 in zip(numpy.append(1e9, rcut_delimeter), numpy.append(rcut_delimeter, 0)): # shells which have high exps (small rcut) shls_dense = [ib for ib, rc in enumerate(rcuts_pgto) if numpy.any((r1 <= rc) & (rc < r0))] if len(shls_dense) == 0: continue cell_dense, ao_idx_dense, ke_cutoff, rcut_atom = \ make_cell_dense_exp(shls_dense, r0, r1) mesh = tools.cutoff_to_mesh(a, ke_cutoff) if TO_EVEN_GRIDS: mesh = (mesh+1)//2 * 2 # to the nearest even number if numpy.all(mesh >= fft_mesh): # Including all rest shells shls_dense = [ib for ib, rc in enumerate(rcuts_pgto) if numpy.any(rc < r0)] cell_dense, ao_idx_dense = make_cell_dense_exp(shls_dense, r0, 0)[:2] cell_dense.mesh = mesh = numpy.min([mesh, fft_mesh], axis=0) grids_dense = gen_grid.UniformGrids(cell_dense) grids_dense.ao_idx = ao_idx_dense #grids_dense.rcuts_pgto = [rcuts_pgto[i] for i in shls_dense] # shells which have low exps (big rcut) shls_sparse = [ib for ib, rc in enumerate(rcuts_pgto) if numpy.any(r0 <= rc)] if len(shls_sparse) == 0: cell_sparse = None ao_idx_sparse = [] else: cell_sparse, ao_idx_sparse = make_cell_sparse_exp(shls_sparse, r0, r1) cell_sparse.mesh = mesh if cell_sparse is None: grids_sparse = None else: grids_sparse = gen_grid.UniformGrids(cell_sparse) grids_sparse.ao_idx = ao_idx_sparse log.debug('mesh %s nao dense/sparse %d %d rcut %g', mesh, len(ao_idx_dense), len(ao_idx_sparse), cell_dense.rcut) tasks.append([grids_dense, grids_sparse]) if numpy.all(mesh >= fft_mesh): break return tasks
[docs] def multi_grids_tasks_for_ke_cut(cell, fft_mesh=None, verbose=None): log = logger.new_logger(cell, verbose) if fft_mesh is None: fft_mesh = cell.mesh # Split shells based on rcut rcuts_pgto, kecuts_pgto = _primitive_gto_cutoff(cell) ao_loc = cell.ao_loc_nr() # cell that needs dense integration grids def make_cell_dense_exp(shls_dense, ke0, ke1): cell_dense = cell.copy(deep=False) cell_dense._bas = cell._bas.copy() cell_dense._env = cell._env.copy() rcut_atom = [0] * cell.natm ke_cutoff = 0 for ib in shls_dense: ke = kecuts_pgto[ib] idx = numpy.where((ke0 < ke) & (ke <= ke1))[0] np1 = len(idx) cs = cell._libcint_ctr_coeff(ib) np, nc = cs.shape if np1 < np: # no pGTO splitting within the shell pexp = cell._bas[ib,PTR_EXP] pcoeff = cell._bas[ib,PTR_COEFF] cs1 = cs[idx] cell_dense._env[pcoeff:pcoeff+cs1.size] = cs1.T.ravel() cell_dense._env[pexp:pexp+np1] = cell.bas_exp(ib)[idx] cell_dense._bas[ib,NPRIM_OF] = np1 ke_cutoff = max(ke_cutoff, ke[idx].max()) ia = cell.bas_atom(ib) rcut_atom[ia] = max(rcut_atom[ia], rcuts_pgto[ib][idx].max()) cell_dense._bas = cell_dense._bas[shls_dense] ao_idx = numpy.hstack([numpy.arange(ao_loc[i], ao_loc[i+1]) for i in shls_dense]) cell_dense.rcut = max(rcut_atom) return cell_dense, ao_idx, ke_cutoff, rcut_atom # cell that needs sparse integration grids def make_cell_sparse_exp(shls_sparse, ke0, ke1): cell_sparse = cell.copy(deep=False) cell_sparse._bas = cell._bas.copy() cell_sparse._env = cell._env.copy() for ib in shls_sparse: idx = numpy.where(kecuts_pgto[ib] <= ke0)[0] np1 = len(idx) cs = cell._libcint_ctr_coeff(ib) np, nc = cs.shape if np1 < np: # no pGTO splitting within the shell pexp = cell._bas[ib,PTR_EXP] pcoeff = cell._bas[ib,PTR_COEFF] cs1 = cs[idx] cell_sparse._env[pcoeff:pcoeff+cs1.size] = cs1.T.ravel() cell_sparse._env[pexp:pexp+np1] = cell.bas_exp(ib)[idx] cell_sparse._bas[ib,NPRIM_OF] = np1 cell_sparse._bas = cell_sparse._bas[shls_sparse] ao_idx = numpy.hstack([numpy.arange(ao_loc[i], ao_loc[i+1]) for i in shls_sparse]) return cell_sparse, ao_idx a = cell.lattice_vectors() if abs(a-numpy.diag(a.diagonal())).max() < 1e-12: init_mesh = INIT_MESH_ORTH else: init_mesh = INIT_MESH_NONORTH ke_cutoff_min = tools.mesh_to_cutoff(cell.lattice_vectors(), init_mesh) ke_cutoff_max = max([ke.max() for ke in kecuts_pgto]) ke1 = ke_cutoff_min.min() ke_delimeter = [0, ke1] while ke1 < ke_cutoff_max: ke1 *= KE_RATIO ke_delimeter.append(ke1) tasks = [] for ke0, ke1 in zip(ke_delimeter[:-1], ke_delimeter[1:]): # shells which have high exps (small rcut) shls_dense = [ib for ib, ke in enumerate(kecuts_pgto) if numpy.any((ke0 < ke) & (ke <= ke1))] if len(shls_dense) == 0: continue mesh = tools.cutoff_to_mesh(a, ke1) if TO_EVEN_GRIDS: mesh = int((mesh+1)//2) * 2 # to the nearest even number if numpy.all(mesh >= fft_mesh): # Including all rest shells shls_dense = [ib for ib, ke in enumerate(kecuts_pgto) if numpy.any(ke0 < ke)] cell_dense, ao_idx_dense = make_cell_dense_exp(shls_dense, ke0, ke_cutoff_max+1)[:2] else: cell_dense, ao_idx_dense, ke_cutoff, rcut_atom = \ make_cell_dense_exp(shls_dense, ke0, ke1) cell_dense.mesh = mesh = numpy.min([mesh, fft_mesh], axis=0) grids_dense = gen_grid.UniformGrids(cell_dense) grids_dense.ao_idx = ao_idx_dense #grids_dense.rcuts_pgto = [rcuts_pgto[i] for i in shls_dense] # shells which have low exps (big rcut) shls_sparse = [ib for ib, ke in enumerate(kecuts_pgto) if numpy.any(ke <= ke0)] if len(shls_sparse) == 0: cell_sparse = None ao_idx_sparse = [] else: cell_sparse, ao_idx_sparse = make_cell_sparse_exp(shls_sparse, ke0, ke1) cell_sparse.mesh = mesh if cell_sparse is None: grids_sparse = None else: grids_sparse = gen_grid.UniformGrids(cell_sparse) grids_sparse.ao_idx = ao_idx_sparse log.debug('mesh %s nao dense/sparse %d %d rcut %g', mesh, len(ao_idx_dense), len(ao_idx_sparse), cell_dense.rcut) tasks.append([grids_dense, grids_sparse]) if numpy.all(mesh >= fft_mesh): break return tasks
def _primitive_gto_cutoff(cell, precision=None): '''Cutoff radius, above which each shell decays to a value less than the required precision''' if precision is None: precision = cell.precision vol = cell.vol weight_penalty = vol precision = cell.precision / max(weight_penalty, 1) omega = cell.omega rcut = [] ke_cutoff = [] for ib in range(cell.nbas): l = cell.bas_angular(ib) es = cell.bas_exp(ib) cs = abs(cell._libcint_ctr_coeff(ib)).max(axis=1) norm_ang = ((2*l+1)/(4*numpy.pi))**.5 fac = 2*numpy.pi/vol * cs*norm_ang/es / precision r = cell.rcut r = (numpy.log(fac * r**(l+1) + 1.) / es)**.5 r = (numpy.log(fac * r**(l+1) + 1.) / es)**.5 ke_guess = gto.cell._estimate_ke_cutoff(es, l, cs, precision, omega) rcut.append(r) ke_cutoff.append(ke_guess) return rcut, ke_cutoff
[docs] class MultiGridNumInt(lib.StreamObject, LibXCMixin): ''' Multigrid Numerical Integration Class for DFT XC-related numerical integration using the multigrid algorithm. This class follows the APIs of the numint.NumInt class. Input Attributes: mesh : list Specifies the number of grids along each axis. xc_with_j : bool Indicates whether to compute and include the Coulomb matrix in the Vxc matrix. Intermediate Attributes (These attributes are generated during computations and should not be modified. Furthermore, they may not be compatible between GPU and CPU implementations): - tasks - _rsh_df ''' _keys = {'cell', 'mesh', 'xc_with_j', 'tasks'} def __init__(self, cell): self.cell = cell self.mesh = cell.mesh self.stdout = cell.stdout self.verbose = cell.verbose self.max_memory = cell.max_memory self.xc_with_j = True # Whether to include Coulomb matrix in Vxc matrix self.tasks = None self._rsh_df = {}
[docs] def build(self): self.tasks = multi_grids_tasks(self.cell, self.mesh, self.verbose) return self
[docs] def reset(self, cell=None): if cell is not None: self.cell = cell self.tasks = None self._rsh_df = {} return self
get_pp = get_pp get_nuc = get_nuc
[docs] def get_jk(self, dm, hermi=1, kpts=None, kpts_band=None, with_j=True, with_k=True, omega=None, exxdiv='ewald'): if omega is not None: # J/K for RSH functionals with self.range_coulomb(omega) as rsh_df: return rsh_df.get_jk(dm, hermi, kpts, kpts_band, with_j, with_k, omega=None, exxdiv=exxdiv) from pyscf.pbc.df import fft_jk if with_k: logger.warn(self, 'MultiGridNumInt does not support HFX. ' 'HFX is computed by FFTDF.get_k_kpts function.') if kpts is None: kpts = numpy.zeros((1, 3)) else: kpts = numpy.asarray(kpts) vj = vk = None if kpts.shape == (3,): if with_k: fftdf = fft.FFTDF(self.cell, kpts=kpts) fftdf.mesh = self.mesh vk = fft_jk.get_jk(fftdf, dm, hermi, kpts, kpts_band, False, True, exxdiv)[1] vj = get_j_kpts(self, dm, hermi, kpts.reshape(-1,3), kpts_band) if kpts_band is None: vj = vj[...,0,:,:] else: if with_k: fftdf = fft.FFTDF(self.cell, kpts=kpts) fftdf.mesh = self.mesh vk = fft_jk.get_k_kpts(fftdf, dm, hermi, kpts, kpts_band, exxdiv) if with_j: vj = get_j_kpts(self, dm, hermi, kpts, kpts_band) return vj, vk
[docs] def get_j(self, dm, hermi=1, kpts=None, kpts_band=None, omega=None): if omega is not None: # J/K for RSH functionals with self.range_coulomb(omega) as rsh_df: return rsh_df.get_j(dm, hermi, kpts, kpts_band, omega=None) if kpts is None: kpts = numpy.zeros((1, 3)) else: kpts = numpy.asarray(kpts) vj = get_j_kpts(self, dm, hermi, kpts.reshape(-1,3), kpts_band) if kpts.ndim == 1 and kpts_band is None: vj = vj[...,0,:,:] return vj
[docs] def nr_vxc(self, cell, grids, xc_code, dms, spin=0, relativity=0, hermi=1, kpts=None, kpts_band=None, max_memory=2000, with_j=False, return_j=False, verbose=None): '''Evaluate RKS/UKS XC functional and potential matrix. See :func:`nr_rks` and :func:`nr_uks` for more details. ''' if spin == 0: return self.nr_rks(cell, grids, xc_code, dms, hermi, relativity, kpts, kpts_band, max_memory, with_j, return_j, verbose) else: return self.nr_uks(cell, grids, xc_code, dms, hermi, relativity, kpts, kpts_band, max_memory, with_j, return_j, verbose)
get_vxc = nr_vxc
[docs] def nr_rks(self, cell, grids, xc_code, dm_kpts, relativity=0, hermi=1, kpts=None, kpts_band=None, max_memory=2000, verbose=None): assert cell is self.cell return_j = False return nr_rks(self, xc_code, dm_kpts, hermi, kpts, kpts_band, self.xc_with_j, return_j, verbose)
[docs] def nr_uks(self, cell, grids, xc_code, dm_kpts, relativity=0, hermi=1, kpts=None, kpts_band=None, max_memory=2000, verbose=None): assert cell is self.cell return_j = False return nr_uks(self, xc_code, dm_kpts, hermi, kpts, kpts_band, self.xc_with_j, return_j, verbose)
[docs] def nr_rks_fxc(self, cell, grids, xc_code, dm0, dms, relativity=0, hermi=0, rho0=None, vxc=None, fxc=None, kpts=None, max_memory=2000, verbose=None): assert cell is self.cell return nr_rks_fxc(self, xc_code, dm0, dms, hermi, self.xc_with_j, rho0, vxc, fxc, kpts, verbose)
[docs] def nr_uks_fxc(self, cell, grids, xc_code, dm0, dms, relativity=0, hermi=0, rho0=None, vxc=None, fxc=None, kpts=None, max_memory=2000, verbose=None): return nr_uks_fxc(self, xc_code, dm0, dms, hermi, self.xc_with_j, rho0, vxc, fxc, kpts, verbose)
[docs] def nr_rks_fxc_st(self, cell, grids, xc_code, dm0, dms_alpha, hermi=1, singlet=True, rho0=None, vxc=None, fxc=None, kpts=None, max_memory=2000, verbose=None): assert cell is self.cell with_j = singlet and self.xc_with_j return nr_rks_fxc_st(self, xc_code, dm0, dms_alpha, hermi, singlet, rho0, vxc, fxc, kpts, with_j, verbose)
[docs] def cache_xc_kernel(self, cell, grids, xc_code, mo_coeff, mo_occ, spin=0, kpts=None, max_memory=2000): assert cell is self.cell return cache_xc_kernel(self, xc_code, mo_coeff, mo_occ, spin, kpts)
[docs] def cache_xc_kernel1(self, cell, grids, xc_code, dm, spin=0, kpts=None, max_memory=2000): assert cell is self.cell return cache_xc_kernel1(self, xc_code, dm, spin, kpts)
[docs] def nr_nlc_vxc(self, cell, grids, xc_code, dms, relativity=0, hermi=1, kpts=None, kpts_band=None, max_memory=2000, verbose=None): raise NotImplementedError(f'MultiGrid for NLC functional {xc_code}')
[docs] def get_rho(self, cell, dm, grids, kpts=numpy.zeros((1,3)), max_memory=2000): assert cell is self.cell return get_rho(self, dm, kpts)
range_coulomb = aft.AFTDF.range_coulomb to_gpu = lib.to_gpu
NumInt = MultiGridNumInt
[docs] def multigrid_fftdf(mf): '''Use MultiGridNumInt to replace the default FFTDF integration method in the DFT object. ''' logger.warn(mf, 'multigrid.multigrid_fftdf is deprecated and will be removed ' 'in a future release. Please call mf.multigrid_numint() instead.') return mf.multigrid_numint()
multigrid = multigrid_fftdf # for backward compatibility def _pgto_shells(cell): return cell._bas[:,NPRIM_OF].sum()