Source code for pyscf.adc.radc_ao2mo

# Copyright 2014-2022 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: Abdelrahman Ahmed <>
#         Samragni Banerjee <samragnibanerjee4@gmail.com>
#         James Serna <jamcar456@gmail.com>
#         Terrence Stahl <>
#         Alexander Sokolov <alexander.y.sokolov@gmail.com>

import numpy as np
import pyscf.ao2mo as ao2mo
from pyscf import lib
from pyscf.lib import logger
import h5py
import tempfile

### Incore integral transformation for integrals in Chemists' notation###
[docs] def transform_integrals_incore(myadc): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(myadc.stdout, myadc.verbose) occ = myadc.mo_coeff[:,:myadc._nocc] vir = myadc.mo_coeff[:,myadc._nocc:] nocc = occ.shape[1] nvir = vir.shape[1] eris = lambda:None eris.oooo = ao2mo.general(myadc._scf._eri, (occ, occ, occ, occ), compact=False).reshape(nocc, nocc, nocc, nocc).copy() # noqa: E501 eris.ovoo = ao2mo.general(myadc._scf._eri, (occ, vir, occ, occ), compact=False).reshape(nocc, nvir, nocc, nocc).copy() # noqa: E501 eris.oovv = ao2mo.general(myadc._scf._eri, (occ, occ, vir, vir), compact=False).reshape(nocc, nocc, nvir, nvir).copy() # noqa: E501 eris.ovvo = ao2mo.general(myadc._scf._eri, (occ, vir, vir, occ), compact=False).reshape(nocc, nvir, nvir, nocc).copy() # noqa: E501 eris.ovvv = ao2mo.general(myadc._scf._eri, (occ, vir, vir, vir), compact=True).reshape(nocc, nvir, -1).copy() # noqa: E501 if (myadc.method == "adc(2)-x" and myadc.approx_trans_moments is False) or (myadc.method == "adc(3)"): eris.vvvv = ao2mo.general(myadc._scf._eri, (vir, vir, vir, vir), compact=False).reshape(nvir, nvir, nvir, nvir) eris.vvvv = np.ascontiguousarray(eris.vvvv.transpose(0,2,1,3)) eris.vvvv = eris.vvvv.reshape(nvir*nvir, nvir*nvir) log.timer('ADC integral transformation', *cput0) return eris
### Out-of-core integral transformation for integrals in Chemists' notation###
[docs] def transform_integrals_outcore(myadc): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(myadc.stdout, myadc.verbose) mol = myadc.mol mo_coeff = myadc.mo_coeff nao = mo_coeff.shape[0] nmo = myadc._nmo occ = myadc.mo_coeff[:,:myadc._nocc] vir = myadc.mo_coeff[:,myadc._nocc:] nocc = occ.shape[1] nvir = vir.shape[1] nvpair = nvir * (nvir+1) // 2 eris = lambda:None eris.feri1 = lib.H5TmpFile() eris.oooo = eris.feri1.create_dataset('oooo', (nocc,nocc,nocc,nocc), 'f8') eris.oovv = eris.feri1.create_dataset( 'oovv', (nocc,nocc,nvir,nvir), 'f8', chunks=(nocc,nocc,1,nvir)) eris.ovoo = eris.feri1.create_dataset( 'ovoo', (nocc,nvir,nocc,nocc), 'f8', chunks=(nocc,1,nocc,nocc)) eris.ovvo = eris.feri1.create_dataset( 'ovvo', (nocc,nvir,nvir,nocc), 'f8', chunks=(nocc,1,nvir,nocc)) eris.ovvv = eris.feri1.create_dataset('ovvv', (nocc,nvir,nvpair), 'f8') def save_occ_frac(p0, p1, eri): eri = eri.reshape(p1-p0,nocc,nmo,nmo) eris.oooo[p0:p1] = eri[:,:,:nocc,:nocc] eris.oovv[p0:p1] = eri[:,:,nocc:,nocc:] def save_vir_frac(p0, p1, eri): eri = eri.reshape(p1-p0,nocc,nmo,nmo) eris.ovoo[:,p0:p1] = eri[:,:,:nocc,:nocc].transpose(1,0,2,3) eris.ovvo[:,p0:p1] = eri[:,:,nocc:,:nocc].transpose(1,0,2,3) vvv = lib.pack_tril(eri[:,:,nocc:,nocc:].reshape((p1-p0)*nocc,nvir,nvir)) eris.ovvv[:,p0:p1] = vvv.reshape(p1-p0,nocc,nvpair).transpose(1,0,2) cput1 = logger.process_clock(), logger.perf_counter() fswap = lib.H5TmpFile() max_memory = myadc.max_memory-lib.current_memory()[0] if max_memory <= 0: max_memory = myadc.memorymin int2e = mol._add_suffix('int2e') ao2mo.outcore.half_e1(mol, (mo_coeff,occ), fswap, int2e, 's4', 1, max_memory=max_memory, verbose=log) ao_loc = mol.ao_loc_nr() nao_pair = nao * (nao+1) // 2 blksize = int(min(8e9,max_memory*.5e6)/8/(nao_pair+nmo**2)/nocc) blksize = min(nmo, max(myadc.blkmin, blksize)) log.debug1('blksize %d', blksize) cput2 = cput1 fload = ao2mo.outcore._load_from_h5g buf = np.empty((blksize*nocc,nao_pair)) buf_prefetch = np.empty_like(buf) def load(buf_prefetch, p0, rowmax): if p0 < rowmax: p1 = min(rowmax, p0+blksize) fload(fswap['0'], p0*nocc, p1*nocc, buf_prefetch) outbuf = np.empty((blksize*nocc,nmo**2)) with lib.call_in_background(load, sync=not myadc.async_io) as prefetch: prefetch(buf_prefetch, 0, nocc) for p0, p1 in lib.prange(0, nocc, blksize): buf, buf_prefetch = buf_prefetch, buf prefetch(buf_prefetch, p1, nocc) nrow = (p1 - p0) * nocc dat = ao2mo._ao2mo.nr_e2(buf[:nrow], mo_coeff, (0,nmo,0,nmo), 's4', 's1', out=outbuf, ao_loc=ao_loc) save_occ_frac(p0, p1, dat) cput2 = log.timer_debug1('transforming oopp', *cput2) prefetch(buf_prefetch, nocc, nmo) for p0, p1 in lib.prange(0, nvir, blksize): buf, buf_prefetch = buf_prefetch, buf prefetch(buf_prefetch, nocc+p1, nmo) nrow = (p1 - p0) * nocc dat = ao2mo._ao2mo.nr_e2(buf[:nrow], mo_coeff, (0,nmo,0,nmo), 's4', 's1', out=outbuf, ao_loc=ao_loc) save_vir_frac(p0, p1, dat) cput2 = log.timer_debug1('transforming ovpp [%d:%d]'%(p0,p1), *cput2) cput1 = log.timer_debug1('transforming oppp', *cput1) ############### forming eris_vvvv ######################################## if (myadc.method == "adc(2)-x" and myadc.approx_trans_moments is False) or (myadc.method == "adc(3)"): eris.vvvv = [] cput3 = logger.process_clock(), logger.perf_counter() avail_mem = (myadc.max_memory - lib.current_memory()[0]) * 0.5 chnk_size = calculate_chunk_size(myadc) # Cache vvvv data in an unlinked h5 temporary file. h5cache_vvvv = eris._h5cache_vvvv = lib.H5TmpFile() for p in range(0,vir.shape[1],chnk_size): if chnk_size < vir.shape[1] : orb_slice = vir[:, p:p+chnk_size] else: orb_slice = vir[:, p:] with lib.H5TmpFile() as tmpf: ao2mo.outcore.general(mol, (orb_slice, vir, vir, vir), tmpf, max_memory=avail_mem, ioblk_size=100, compact=False) vvvv = tmpf['eri_mo'][:] vvvv = vvvv.reshape(orb_slice.shape[1], vir.shape[1], vir.shape[1], vir.shape[1]) vvvv = np.ascontiguousarray(vvvv.transpose(0,2,1,3)).reshape(-1, nvir, nvir * nvir) vvvv_p = h5cache_vvvv.create_dataset(str(p), data=vvvv) eris.vvvv.append(vvvv_p) vvvv = None cput3 = log.timer_debug1('transforming vvvv', *cput3) log.timer('ADC integral transformation', *cput0) return eris
### DF integral transformation for integrals in Chemists' notation###
[docs] def transform_integrals_df(myadc): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(myadc.stdout, myadc.verbose) mo_coeff = np.asarray(myadc.mo_coeff, order='F') nocc = myadc._nocc nao, nmo = mo_coeff.shape nvir = myadc._nmo - myadc._nocc with_df = myadc.with_df naux = with_df.get_naoaux() eris = lambda: None eris.vvvv = None eris.ovvv = None eris.ceee = None Loo = np.empty((naux,nocc,nocc)) Lvo = np.empty((naux,nvir,nocc)) eris.Lvv = np.empty((naux,nvir,nvir)) eris.Lov = np.empty((naux,nocc,nvir)) if not isinstance(myadc.ncvs, type(None)) and myadc.ncvs > 0: ncvs = myadc.ncvs eris.Lce = np.empty((naux,ncvs,nvir)) eris.Lee = eris.Lvv ijslice = (0, nmo, 0, nmo) Lpq = None p1 = 0 for eri1 in with_df.loop(): Lpq = ao2mo._ao2mo.nr_e2(eri1, mo_coeff, ijslice, aosym='s2', out=Lpq).reshape(-1,nmo,nmo) p0, p1 = p1, p1 + Lpq.shape[0] Loo[p0:p1] = Lpq[:,:nocc,:nocc] #Lov[p0:p1] = Lpq[:,:nocc,nocc:] eris.Lov[p0:p1] = Lpq[:,:nocc,nocc:] Lvo[p0:p1] = Lpq[:,nocc:,:nocc] eris.Lvv[p0:p1] = Lpq[:,nocc:,nocc:] if not isinstance(myadc.ncvs, type(None)) and myadc.ncvs > 0: ncvs = myadc.ncvs eris.Lce[p0:p1] = Lpq[:,:ncvs,nocc:] eris.Lee = eris.Lvv Loo = Loo.reshape(naux,nocc*nocc) eris.Lov = eris.Lov.reshape(naux,nocc*nvir) Lvo = Lvo.reshape(naux,nocc*nvir) eris.Lvv = eris.Lvv.reshape(naux,nvir*nvir) if not isinstance(myadc.ncvs, type(None)) and myadc.ncvs > 0: ncvs = myadc.ncvs eris.Lee = eris.Lvv eris.Lce = eris.Lce.reshape(naux,myadc.ncvs*nvir) eris.feri1 = lib.H5TmpFile() eris.oooo = eris.feri1.create_dataset('oooo', (nocc,nocc,nocc,nocc), 'f8') eris.oovv = eris.feri1.create_dataset( 'oovv', (nocc,nocc,nvir,nvir), 'f8', chunks=(nocc,nocc,1,nvir)) eris.ovoo = eris.feri1.create_dataset( 'ovoo', (nocc,nvir,nocc,nocc), 'f8', chunks=(nocc,1,nocc,nocc)) eris.ovvo = eris.feri1.create_dataset( 'ovvo', (nocc,nvir,nvir,nocc), 'f8', chunks=(nocc,1,nvir,nocc)) eris.oooo[:] = lib.ddot(Loo.T, Loo).reshape(nocc,nocc,nocc,nocc) eris.ovoo[:] = lib.ddot(eris.Lov.T, Loo).reshape(nocc,nvir,nocc,nocc) eris.oovv[:] = lib.ddot(Loo.T, eris.Lvv).reshape(nocc,nocc,nvir,nvir) eris.ovvo[:] = lib.ddot(eris.Lov.T, Lvo).reshape(nocc,nvir,nvir,nocc) log.timer('DF-ADC integral transformation', *cput0) return eris
[docs] def calculate_chunk_size(myadc): avail_mem = (myadc.max_memory - lib.current_memory()[0]) * 0.5 vvv_mem = (myadc._nvir**3) * 8/1e6 chnk_size = int(avail_mem/vvv_mem) if chnk_size <= 0 : chnk_size = 1 return chnk_size
[docs] def unpack_eri_1(eri, norb): n_oo = norb * (norb + 1) // 2 ind_oo = np.tril_indices(norb) eri_ = None if len(eri.shape) == 3: if (eri.shape[0] == n_oo): eri_ = np.zeros((norb, norb, eri.shape[1], eri.shape[2])) eri_[ind_oo[0], ind_oo[1]] = eri eri_[ind_oo[1], ind_oo[0]] = eri elif (eri.shape[2] == n_oo): eri_ = np.zeros((eri.shape[0], eri.shape[1], norb, norb)) eri_[:, :, ind_oo[0], ind_oo[1]] = eri eri_[:, :, ind_oo[1], ind_oo[0]] = eri else: raise TypeError("ERI dimensions don't match") else: raise RuntimeError("ERI does not have a correct dimension") return eri_
[docs] def unpack_eri_2(eri, norb): n_oo = norb * (norb - 1) // 2 ind_oo = np.tril_indices(norb,k=-1) eri_ = None if len(eri.shape) == 2: if (eri.shape[0] != n_oo or eri.shape[1] != n_oo): raise TypeError("ERI dimensions don't match") temp = np.zeros((n_oo, norb, norb)) temp[:, ind_oo[0], ind_oo[1]] = eri temp[:, ind_oo[1], ind_oo[0]] = -eri eri_ = np.zeros((norb, norb, norb, norb)) eri_[ind_oo[0], ind_oo[1]] = temp eri_[ind_oo[1], ind_oo[0]] = -temp else: raise RuntimeError("ERI does not have a correct dimension") return eri_