def gf_k(interfaces, parent, ps, qs, kwargs, return_ncalls=False): """K-dependent Green's function.""" ncalls = 0 result_gf = [] log = interfaces[0].logger log.note("Starting a K-GF calculation n_k = {:d}".format( len(interfaces), )) ps = np.array(ps) qs = np.array(qs) for interface in interfaces: log.note("Running K-GF @ id(k)={:d} ...".format(len(result_gf))) idx = padding_k_idx(interface.cc, kind="joint")[interface.k] ps_physical = np.any(ps[:, np.newaxis] == idx[np.newaxis, :], axis=1) qs_physical = np.any(qs[:, np.newaxis] == idx[np.newaxis, :], axis=1) kwargs.update( dict( return_ncalls=return_ncalls, interface=interface, ps=ps[ps_physical], qs=qs[qs_physical], )) result = parent(**kwargs) if return_ncalls: ncalls += result[1] result = result[0] result_gf.append(result) gfvals = np.array(result_gf) if return_ncalls: return gfvals, ncalls else: return gfvals
def init_amps(self, eris): time0 = time.clock(), time.time() nocc = self.nocc nvir = self.nmo - nocc nkpts = self.nkpts t1 = np.zeros((nkpts, nocc, nvir), dtype=eris.fock.dtype) t2 = np.empty((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=eris.fock.dtype) mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)] mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)] # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split") emp2 = 0 kconserv = self.khelper.kconserv touched = np.zeros((nkpts, nkpts, nkpts), dtype=bool) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): if touched[ki, kj, ka]: continue kb = kconserv[ki, ka, kj] # For discussion of LARGE_DENOM, see t1new update above eia = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] eris_ijab = eris.oovv[ki, kj, ka] eris_ijba = eris.oovv[ki, kj, kb] t2[ki, kj, ka] = eris_ijab.conj() / eijab woovv = 2 * eris_ijab - eris_ijba.transpose(0, 1, 3, 2) emp2 += np.einsum('ijab,ijab', t2[ki, kj, ka], woovv) if ka != kb: eijba = eijab.transpose(0, 1, 3, 2) t2[ki, kj, kb] = eris_ijba.conj() / eijba woovv = 2 * eris_ijba - eris_ijab.transpose(0, 1, 3, 2) emp2 += np.einsum('ijab,ijab', t2[ki, kj, kb], woovv) touched[ki, kj, ka] = touched[ki, kj, kb] = True self.emp2 = emp2.real / nkpts logger.info( self, 'Init t2, MP2 energy (with fock eigenvalue shift) = %.15g', self.emp2) logger.timer(self, 'init mp2', *time0) return self.emp2, t1, t2
def iter_12(cc, k): """Iterates over IP index slices.""" o, v = padding_k_idx(cc, kind="split") kconserv = cc.khelper.kconserv yield (o[k],) for ki in range(cc.nkpts): for kj in range(cc.nkpts): kb = kconserv[ki, k, kj] yield (ki,), (kj,), o[ki], o[kj], v[kb]
def iter_12(cc, k): """Iterates over EA index slices.""" o, v = padding_k_idx(cc, kind="split") kconserv = cc.khelper.kconserv yield (v[k], ) for ki in range(cc.nkpts): for ka in range(cc.nkpts): kb = kconserv[k, ka, ki] yield (ki, ), (ka, ), o[ki], v[ka], v[kb]
def iter_12(cc_or_eom, k): """Iterates over IP index slices.""" if isinstance(cc_or_eom, kccsd_rhf.RCCSD): cc = cc_or_eom else: cc = cc_or_eom._cc o, v = padding_k_idx(cc, kind="split") kconserv = cc.khelper.kconserv yield (o[k],) for ki in range(cc.nkpts): for kj in range(cc.nkpts): kb = kconserv[ki, k, kj] yield (ki,), (kj,), o[ki], o[kj], v[kb]
def init_amps(self, eris): time0 = time.clock(), time.time() nocc = self.nocc nvir = self.nmo - nocc nkpts = self.nkpts mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)] mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)] t1 = numpy.zeros((nkpts, nocc, nvir), dtype=numpy.complex128) t2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=numpy.complex128) self.emp2 = 0 foo = eris.fock[:, :nocc, :nocc].copy() fvv = eris.fock[:, nocc:, nocc:].copy() fov = eris.fock[:, :nocc, nocc:].copy() eris_oovv = eris.oovv.copy() # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split") kconserv = kpts_helper.get_kconserv(self._scf.cell, self.kpts) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] # For LARGE_DENOM, see t1new update above eia = LARGE_DENOM * numpy.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * numpy.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] t2[ki, kj, ka] = eris_oovv[ki, kj, ka] / eijab t2 = numpy.conj(t2) self.emp2 = 0.25 * numpy.einsum('pqrijab,pqrijab', t2, eris_oovv).real self.emp2 /= nkpts logger.info(self, 'Init t2, MP2 energy = %.15g', self.emp2.real) logger.timer(self, 'init mp2', *time0) return self.emp2, t1, t2
def mask_frozen(cc, vector, k, const=LARGE_DENOM): '''Replaces all frozen orbital indices of `vector` with the value `const`.''' r1, r2 = vector_to_amplitudes(cc, vector, k) nkpts, nocc, nvir = cc.t1.shape kconserv = cc.khelper.kconserv # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") new_r1 = const * np.ones_like(r1) new_r2 = const * np.ones_like(r2) new_r1[nonzero_opadding[k]] = r1[nonzero_opadding[k]] for ki in range(nkpts): for kj in range(nkpts): kb = kconserv[ki, k, kj] idx = np.ix_([ki], [kj], nonzero_opadding[ki], nonzero_opadding[kj], nonzero_vpadding[kb]) new_r2[idx] = r2[idx] return amplitudes_to_vector(cc, new_r1, new_r2, k)
def init_amps(self, eris): time0 = time.clock(), time.time() nocc = self.nocc nvir = self.nmo - nocc nkpts = self.nkpts mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)] mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)] t1 = numpy.zeros((nkpts, nocc, nvir), dtype=numpy.complex128) t2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=numpy.complex128) self.emp2 = 0 foo = eris.fock[:, :nocc, :nocc].copy() fvv = eris.fock[:, nocc:, nocc:].copy() fov = eris.fock[:, :nocc, nocc:].copy() eris_oovv = eris.oovv.copy() # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split") kconserv = kpts_helper.get_kconserv(self._scf.cell, self.kpts) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] # For LARGE_DENOM, see t1new update above eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] t2[ki, kj, ka] = eris_oovv[ki, kj, ka] / eijab t2 = numpy.conj(t2) self.emp2 = 0.25 * numpy.einsum('pqrijab,pqrijab', t2, eris_oovv).real self.emp2 /= nkpts logger.info(self, 'Init t2, MP2 energy = %.15g', self.emp2.real) logger.timer(self, 'init mp2', *time0) return self.emp2, t1, t2
def kernel(mycc, eris, t1=None, t2=None, max_memory=2000, verbose=logger.INFO): '''Returns the CCSD(T) for general spin-orbital integrals with k-points. Note: Returns real part of the CCSD(T) energy, raises warning if there is a complex part. Args: mycc (:class:`GCCSD`): Coupled-cluster object storing results of a coupled-cluster calculation. eris (:class:`_ERIS`): Integral object holding the relevant electron- repulsion integrals and Fock matrix elements t1 (:obj:`ndarray`): t1 coupled-cluster amplitudes t2 (:obj:`ndarray`): t2 coupled-cluster amplitudes max_memory (float): Maximum memory used in calculation (NOT USED) verbose (int, :class:`Logger`): verbosity of calculation Returns: energy_t (float): The real-part of the k-point CCSD(T) energy. ''' assert isinstance(mycc, pyscf.pbc.cc.kccsd.GCCSD) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mycc.stdout, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if t1 is None or t2 is None: raise TypeError( 'Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe ' 'need to run `.ccsd()` on the ccsd object?)') cell = mycc._scf.cell kpts = mycc.kpts # The dtype of any local arrays that will be created dtype = t1.dtype nkpts, nocc, nvir = t1.shape mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)] mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)] fov = eris.fock[:, :nocc, nocc:] mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") energy_t = 0.0 for ki in range(nkpts): for kj in range(ki + 1): for kk in range(kj + 1): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nocc, kj, mo_e_o, nonzero_opadding], [0, nocc, kk, mo_e_o, nonzero_opadding]) # Factors to include for permutational symmetry among k-points for occupied space if ki == kj and kj == kk: symm_ijk_kpt = 1. # only one degeneracy elif ki == kj or kj == kk: symm_ijk_kpt = 3. # 3 degeneracies when only one k-point is unique else: symm_ijk_kpt = 6. # 3! combinations of arranging 3 distinct k-points for ka in range(nkpts): for kb in range(ka + 1): # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) if kc not in range(kb + 1): continue # -1.0 so the LARGE_DENOM does not cancel with the one from eijk eabc = _get_epqr( [0, nvir, ka, mo_e_v, nonzero_vpadding], [0, nvir, kb, mo_e_v, nonzero_vpadding], [0, nvir, kc, mo_e_v, nonzero_vpadding], fac=[-1., -1., -1.]) # Factors to include for permutational symmetry among k-points for virtual space if ka == kb and kb == kc: symm_abc_kpt = 1. # one unique combination of (ka, kb, kc) elif ka == kb or kb == kc: symm_abc_kpt = 3. # 3 unique combinations of (ka, kb, kc) else: symm_abc_kpt = 6. # 6 unique combinations of (ka, kb, kc) # Determine the a, b, c indices we will loop over as # determined by the k-point symmetry. abc_indices = cartesian_prod([range(nvir)] * 3) symm_3d = symm_2d_ab = symm_2d_bc = False if ka == kc: # == kb from lower triangular summation abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[0, 1, 2]) # loop a >= b >= c symm_3d = True elif ka == kb: abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[0, 1]) # loop a >= b symm_2d_ab = True elif kb == kc: abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[1, 2]) # loop b >= c symm_2d_bc = True for a, b, c in abc_indices: # See symm_3d and abc_indices above for description of factors symm_abc = 1. if symm_3d: if a == b == c: symm_abc = 1. elif a == b or b == c: symm_abc = 3. else: symm_abc = 6. elif symm_2d_ab: if a == b: symm_abc = 1. else: symm_abc = 2. elif symm_2d_bc: if b == c: symm_abc = 1. else: symm_abc = 2. # Form energy denominator eijkabc = (eijk[:, :, :] + eabc[a, b, c]) # Form connected triple excitation amplitude t3c = np.zeros((nocc, nocc, nocc), dtype=dtype) # First term: 1 - p(ij) - p(ik) ke = kconserv[kj, ka, kk] t3c = t3c + einsum( 'jke,ie->ijk', t2[kj, kk, ka, :, :, a, :], -eris.ovvv[ki, ke, kc, :, :, c, b].conj()) ke = kconserv[ki, ka, kk] t3c = t3c - einsum( 'ike,je->ijk', t2[ki, kk, ka, :, :, a, :], -eris.ovvv[kj, ke, kc, :, :, c, b].conj()) ke = kconserv[kj, ka, ki] t3c = t3c - einsum( 'jie,ke->ijk', t2[kj, ki, ka, :, :, a, :], -eris.ovvv[kk, ke, kc, :, :, c, b].conj()) km = kconserv[kb, ki, kc] t3c = t3c - einsum( 'mi,jkm->ijk', t2[km, ki, kb, :, :, b, c], eris.ooov[kj, kk, km, :, :, :, a].conj()) km = kconserv[kb, kj, kc] t3c = t3c + einsum( 'mj,ikm->ijk', t2[km, kj, kb, :, :, b, c], eris.ooov[ki, kk, km, :, :, :, a].conj()) km = kconserv[kb, kk, kc] t3c = t3c + einsum( 'mk,jim->ijk', t2[km, kk, kb, :, :, b, c], eris.ooov[kj, ki, km, :, :, :, a].conj()) # Second term: - p(ab) + p(ab) p(ij) + p(ab) p(ik) ke = kconserv[kj, kb, kk] t3c = t3c - einsum( 'jke,ie->ijk', t2[kj, kk, kb, :, :, b, :], -eris.ovvv[ki, ke, kc, :, :, c, a].conj()) ke = kconserv[ki, kb, kk] t3c = t3c + einsum( 'ike,je->ijk', t2[ki, kk, kb, :, :, b, :], -eris.ovvv[kj, ke, kc, :, :, c, a].conj()) ke = kconserv[kj, kb, ki] t3c = t3c + einsum( 'jie,ke->ijk', t2[kj, ki, kb, :, :, b, :], -eris.ovvv[kk, ke, kc, :, :, c, a].conj()) km = kconserv[ka, ki, kc] t3c = t3c + einsum( 'mi,jkm->ijk', t2[km, ki, ka, :, :, a, c], eris.ooov[kj, kk, km, :, :, :, b].conj()) km = kconserv[ka, kj, kc] t3c = t3c - einsum( 'mj,ikm->ijk', t2[km, kj, ka, :, :, a, c], eris.ooov[ki, kk, km, :, :, :, b].conj()) km = kconserv[ka, kk, kc] t3c = t3c - einsum( 'mk,jim->ijk', t2[km, kk, ka, :, :, a, c], eris.ooov[kj, ki, km, :, :, :, b].conj()) # Third term: - p(ac) + p(ac) p(ij) + p(ac) p(ik) ke = kconserv[kj, kc, kk] t3c = t3c - einsum( 'jke,ie->ijk', t2[kj, kk, kc, :, :, c, :], -eris.ovvv[ki, ke, ka, :, :, a, b].conj()) ke = kconserv[ki, kc, kk] t3c = t3c + einsum( 'ike,je->ijk', t2[ki, kk, kc, :, :, c, :], -eris.ovvv[kj, ke, ka, :, :, a, b].conj()) ke = kconserv[kj, kc, ki] t3c = t3c + einsum( 'jie,ke->ijk', t2[kj, ki, kc, :, :, c, :], -eris.ovvv[kk, ke, ka, :, :, a, b].conj()) km = kconserv[kb, ki, ka] t3c = t3c + einsum( 'mi,jkm->ijk', t2[km, ki, kb, :, :, b, a], eris.ooov[kj, kk, km, :, :, :, c].conj()) km = kconserv[kb, kj, ka] t3c = t3c - einsum( 'mj,ikm->ijk', t2[km, kj, kb, :, :, b, a], eris.ooov[ki, kk, km, :, :, :, c].conj()) km = kconserv[kb, kk, ka] t3c = t3c - einsum( 'mk,jim->ijk', t2[km, kk, kb, :, :, b, a], eris.ooov[kj, ki, km, :, :, :, c].conj()) # Form disconnected triple excitation amplitude contribution t3d = np.zeros((nocc, nocc, nocc), dtype=dtype) # First term: 1 - p(ij) - p(ik) if ki == ka: t3d = t3d + einsum( 'i,jk->ijk', t1[ki, :, a], -eris.oovv[kj, kk, kb, :, :, b, c].conj()) t3d = t3d + einsum('i,jk->ijk', -fov[ki, :, a], t2[kj, kk, kb, :, :, b, c]) if kj == ka: t3d = t3d - einsum( 'j,ik->ijk', t1[kj, :, a], -eris.oovv[ki, kk, kb, :, :, b, c].conj()) t3d = t3d - einsum('j,ik->ijk', -fov[kj, :, a], t2[ki, kk, kb, :, :, b, c]) if kk == ka: t3d = t3d - einsum( 'k,ji->ijk', t1[kk, :, a], -eris.oovv[kj, ki, kb, :, :, b, c].conj()) t3d = t3d - einsum('k,ji->ijk', -fov[kk, :, a], t2[kj, ki, kb, :, :, b, c]) # Second term: - p(ab) + p(ab) p(ij) + p(ab) p(ik) if ki == kb: t3d = t3d - einsum( 'i,jk->ijk', t1[ki, :, b], -eris.oovv[kj, kk, ka, :, :, a, c].conj()) t3d = t3d - einsum('i,jk->ijk', -fov[ki, :, b], t2[kj, kk, ka, :, :, a, c]) if kj == kb: t3d = t3d + einsum( 'j,ik->ijk', t1[kj, :, b], -eris.oovv[ki, kk, ka, :, :, a, c].conj()) t3d = t3d + einsum('j,ik->ijk', -fov[kj, :, b], t2[ki, kk, ka, :, :, a, c]) if kk == kb: t3d = t3d + einsum( 'k,ji->ijk', t1[kk, :, b], -eris.oovv[kj, ki, ka, :, :, a, c].conj()) t3d = t3d + einsum('k,ji->ijk', -fov[kk, :, b], t2[kj, ki, ka, :, :, a, c]) # Third term: - p(ac) + p(ac) p(ij) + p(ac) p(ik) if ki == kc: t3d = t3d - einsum( 'i,jk->ijk', t1[ki, :, c], -eris.oovv[kj, kk, kb, :, :, b, a].conj()) t3d = t3d - einsum('i,jk->ijk', -fov[ki, :, c], t2[kj, kk, kb, :, :, b, a]) if kj == kc: t3d = t3d + einsum( 'j,ik->ijk', t1[kj, :, c], -eris.oovv[ki, kk, kb, :, :, b, a].conj()) t3d = t3d + einsum('j,ik->ijk', -fov[kj, :, c], t2[ki, kk, kb, :, :, b, a]) if kk == kc: t3d = t3d + einsum( 'k,ji->ijk', t1[kk, :, c], -eris.oovv[kj, ki, kb, :, :, b, a].conj()) t3d = t3d + einsum('k,ji->ijk', -fov[kk, :, c], t2[kj, ki, kb, :, :, b, a]) t3c_plus_d = t3c + t3d t3c_plus_d /= eijkabc energy_t += symm_abc_kpt * symm_ijk_kpt * symm_abc * einsum( 'ijk,ijk', t3c, t3c_plus_d.conj()) energy_t = (1. / 36) * energy_t / nkpts if abs(energy_t.imag) > 1e-4: log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag) log.note('CCSD(T) correction per cell = %.15g', energy_t.real) return energy_t.real
def get_padding_k_idx(self, cc): return padding_k_idx(cc, kind='split')
def get_t3p2_imds_slow(cc, t1, t2, eris=None, t3p2_ip_out=None, t3p2_ea_out=None): """For a description of arguments, see `get_t3p2_imds_slow` in the corresponding `kintermediates.py`. """ from pyscf.pbc.cc.kccsd_t_rhf import _get_epqr if eris is None: eris = cc.ao2mo() fock = eris.fock nkpts, nocc, nvir = t1.shape kconserv = cc.khelper.kconserv dtype = np.result_type(t1, t2) fov = fock[:, :nocc, nocc:] #foo = [fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)] #fvv = [fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)] mo_energy_occ = np.array( [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]) mo_energy_vir = np.array( [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]) mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") ccsd_energy = cc.energy(t1, t2, eris) if t3p2_ip_out is None: t3p2_ip_out = np.zeros((nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc), dtype=dtype) Wmcik = t3p2_ip_out if t3p2_ea_out is None: t3p2_ea_out = np.zeros((nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=dtype) Wacek = t3p2_ea_out from itertools import product tmp_t3 = np.empty((nkpts, nkpts, nkpts, nkpts, nkpts, nocc, nocc, nocc, nvir, nvir, nvir), dtype=t2.dtype) def get_w(ki, kj, kk, ka, kb, kc): kf = kconserv[ka, ki, kb] ret = lib.einsum('fiba,kjcf->ijkabc', eris.vovv[kf, ki, kb].conj(), t2[kk, kj, kc]) km = kconserv[kc, kk, kb] ret -= lib.einsum('jima,mkbc->ijkabc', eris.ooov[kj, ki, km].conj(), t2[km, kk, kb]) return ret for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5): kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts, [ki, kj, kk, ka, kb]) tmp_t3[ki, kj, kk, ka, kb] = get_w(ki, kj, kk, ka, kb, kc) tmp_t3[ki, kj, kk, ka, kb] += get_w(ki, kk, kj, ka, kc, kb).transpose(0, 2, 1, 3, 5, 4) tmp_t3[ki, kj, kk, ka, kb] += get_w(kj, ki, kk, kb, ka, kc).transpose(1, 0, 2, 4, 3, 5) tmp_t3[ki, kj, kk, ka, kb] += get_w(kj, kk, ki, kb, kc, ka).transpose(2, 0, 1, 5, 3, 4) tmp_t3[ki, kj, kk, ka, kb] += get_w(kk, ki, kj, kc, ka, kb).transpose(1, 2, 0, 4, 5, 3) tmp_t3[ki, kj, kk, ka, kb] += get_w(kk, kj, ki, kc, kb, ka).transpose(2, 1, 0, 5, 4, 3) eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nocc, kj, mo_e_o, nonzero_opadding], [0, nocc, kk, mo_e_o, nonzero_opadding]) eabc = _get_epqr([0, nvir, ka, mo_e_v, nonzero_vpadding], [0, nvir, kb, mo_e_v, nonzero_vpadding], [0, nvir, kc, mo_e_v, nonzero_vpadding], fac=[-1., -1., -1.]) eijkabc = eijk[:, :, :, None, None, None] + eabc[None, None, None, :, :, :] tmp_t3[ki, kj, kk, ka, kb] /= eijkabc pt1 = np.zeros((nkpts, nocc, nvir), dtype=t2.dtype) for ki in range(nkpts): for km, kn, ke in product(range(nkpts), repeat=3): kf = kconserv[km, ke, kn] Soovv = 2. * eris.oovv[km, kn, ke] - eris.oovv[km, kn, kf].transpose( 0, 1, 3, 2) St3 = (tmp_t3[ki, km, kn, ki, ke] - tmp_t3[ki, km, kn, ke, ki].transpose(0, 1, 2, 4, 3, 5)) pt1[ki] += lib.einsum('mnef,imnaef->ia', Soovv, St3) pt2 = np.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=t2.dtype) for ki, kj, ka in product(range(nkpts), repeat=3): kb = kconserv[ki, ka, kj] for km in range(nkpts): for kn in range(nkpts): # (ia,jb) -> (ia,jb) ke = kconserv[km, kj, kn] pt2[ki, kj, ka] += -2. * lib.einsum( 'imnabe,mnje->ijab', tmp_t3[ki, km, kn, ka, kb], eris.ooov[km, kn, kj]) pt2[ki, kj, ka] += lib.einsum('imnabe,nmje->ijab', tmp_t3[ki, km, kn, ka, kb], eris.ooov[kn, km, kj]) pt2[ki, kj, ka] += lib.einsum('inmeab,mnje->ijab', tmp_t3[ki, kn, km, ke, ka], eris.ooov[km, kn, kj]) # (ia,jb) -> (jb,ia) ke = kconserv[km, ki, kn] pt2[ki, kj, ka] += -2. * lib.einsum( 'jmnbae,mnie->ijab', tmp_t3[kj, km, kn, kb, ka], eris.ooov[km, kn, ki]) pt2[ki, kj, ka] += lib.einsum('jmnbae,nmie->ijab', tmp_t3[kj, km, kn, kb, ka], eris.ooov[kn, km, ki]) pt2[ki, kj, ka] += lib.einsum('jnmeba,mnie->ijab', tmp_t3[kj, kn, km, ke, kb], eris.ooov[km, kn, ki]) # (ia,jb) -> (ia,jb) pt2[ki, kj, ka] += lib.einsum('ijmabe,me->ijab', tmp_t3[ki, kj, km, ka, kb], fov[km]) pt2[ki, kj, ka] -= lib.einsum('ijmaeb,me->ijab', tmp_t3[ki, kj, km, ka, km], fov[km]) # (ia,jb) -> (jb,ia) pt2[ki, kj, ka] += lib.einsum('jimbae,me->ijab', tmp_t3[kj, ki, km, kb, ka], fov[km]) pt2[ki, kj, ka] -= lib.einsum('jimbea,me->ijab', tmp_t3[kj, ki, km, kb, km], fov[km]) for ke in range(nkpts): # (ia,jb) -> (ia,jb) kf = kconserv[km, ke, kb] pt2[ki, kj, ka] += 2. * lib.einsum( 'ijmaef,bmef->ijab', tmp_t3[ki, kj, km, ka, ke], eris.vovv[kb, km, ke]) pt2[ki, kj, ka] -= lib.einsum('ijmaef,bmfe->ijab', tmp_t3[ki, kj, km, ka, ke], eris.vovv[kb, km, kf]) pt2[ki, kj, ka] -= lib.einsum('imjfae,bmef->ijab', tmp_t3[ki, km, kj, kf, ka], eris.vovv[kb, km, ke]) # (ia,jb) -> (jb,ia) kf = kconserv[km, ke, ka] pt2[ki, kj, ka] += 2. * lib.einsum( 'jimbef,amef->ijab', tmp_t3[kj, ki, km, kb, ke], eris.vovv[ka, km, ke]) pt2[ki, kj, ka] -= lib.einsum('jimbef,amfe->ijab', tmp_t3[kj, ki, km, kb, ke], eris.vovv[ka, km, kf]) pt2[ki, kj, ka] -= lib.einsum('jmifbe,amef->ijab', tmp_t3[kj, km, ki, kf, kb], eris.vovv[ka, km, ke]) for ki in range(nkpts): ka = ki eia = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] pt1[ki] /= eia for ki, ka in product(range(nkpts), repeat=2): eia = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] for kj in range(nkpts): kb = kconserv[ki, ka, kj] ejb = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] pt2[ki, kj, ka] /= eijab pt1 += t1 pt2 += t2 for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5): kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts, [ki, kj, kk, ka, kb]) km = kconserv[kc, ki, ka] _oovv = eris.oovv[km, ki, kc] Wmcik[km, kb, kk] += 2. * lib.einsum('ijkabc,mica->mbkj', tmp_t3[ki, kj, kk, ka, kb], _oovv) Wmcik[km, kb, kk] -= lib.einsum('jikabc,mica->mbkj', tmp_t3[kj, ki, kk, ka, kb], _oovv) Wmcik[km, kb, kk] -= lib.einsum('kjiabc,mica->mbkj', tmp_t3[kk, kj, ki, ka, kb], _oovv) for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5): kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts, [ki, kj, kk, ka, kb]) ke = kconserv[ki, ka, kk] _oovv = eris.oovv[ki, kk, ka] Wacek[kc, kb, ke] -= 2. * lib.einsum('ijkabc,ikae->cbej', tmp_t3[ki, kj, kk, ka, kb], _oovv) Wacek[kc, kb, ke] += lib.einsum('jikabc,ikae->cbej', tmp_t3[kj, ki, kk, ka, kb], _oovv) Wacek[kc, kb, ke] += lib.einsum('kjiabc,ikae->cbej', tmp_t3[kk, kj, ki, ka, kb], _oovv) delta_ccsd_energy = cc.energy(pt1, pt2, eris) - ccsd_energy lib.logger.info(cc, 'CCSD energy T3[2] correction : %16.12e', delta_ccsd_energy) return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
def update_amps(cc, t1, t2, eris): time0 = time1 = time.clock(), time.time() log = logger.Logger(cc.stdout, cc.verbose) nkpts, nocc, nvir = t1.shape fock = eris.fock mo_e_o = [e[:nocc] for e in eris.mo_energy] mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy] # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") fov = fock[:, :nocc, nocc:] foo = fock[:, :nocc, :nocc] fvv = fock[:, nocc:, nocc:] kconserv = cc.khelper.kconserv Foo = imdk.cc_Foo(t1, t2, eris, kconserv) Fvv = imdk.cc_Fvv(t1, t2, eris, kconserv) Fov = imdk.cc_Fov(t1, t2, eris, kconserv) Loo = imdk.Loo(t1, t2, eris, kconserv) Lvv = imdk.Lvv(t1, t2, eris, kconserv) # Move energy terms to the other side for k in range(nkpts): Foo[k][np.diag_indices(nocc)] -= mo_e_o[k] Fvv[k][np.diag_indices(nvir)] -= mo_e_v[k] Loo[k][np.diag_indices(nocc)] -= mo_e_o[k] Lvv[k][np.diag_indices(nvir)] -= mo_e_v[k] time1 = log.timer_debug1('intermediates', *time1) # T1 equation t1new = np.array(fov).astype(t1.dtype).conj() for ka in range(nkpts): ki = ka # kc == ki; kk == ka t1new[ka] += -2. * einsum('kc,ka,ic->ia', fov[ki], t1[ka], t1[ki]) t1new[ka] += einsum('ac,ic->ia', Fvv[ka], t1[ki]) t1new[ka] += -einsum('ki,ka->ia', Foo[ki], t1[ka]) tau_term = np.empty((nkpts, nocc, nocc, nvir, nvir), dtype=t1.dtype) for kk in range(nkpts): tau_term[kk] = 2 * t2[kk, ki, kk] - t2[ki, kk, kk].transpose(1, 0, 2, 3) tau_term[ka] += einsum('ic,ka->kica', t1[ki], t1[ka]) for kk in range(nkpts): kc = kk t1new[ka] += einsum('kc,kica->ia', Fov[kc], tau_term[kk]) t1new[ka] += einsum('akic,kc->ia', 2 * eris.voov[ka, kk, ki], t1[kc]) t1new[ka] += einsum('kaic,kc->ia', -eris.ovov[kk, ka, ki], t1[kc]) for kc in range(nkpts): kd = kconserv[ka, kc, kk] Svovv = 2 * eris.vovv[ka, kk, kc] - eris.vovv[ka, kk, kd].transpose(0, 1, 3, 2) tau_term_1 = t2[ki, kk, kc].copy() if ki == kc and kk == kd: tau_term_1 += einsum('ic,kd->ikcd', t1[ki], t1[kk]) t1new[ka] += einsum('akcd,ikcd->ia', Svovv, tau_term_1) # kk - ki + kl = kc # => kl = ki - kk + kc kl = kconserv[ki, kk, kc] Sooov = 2 * eris.ooov[kk, kl, ki] - eris.ooov[kl, kk, ki].transpose(1, 0, 2, 3) tau_term_1 = t2[kk, kl, ka].copy() if kk == ka and kl == kc: tau_term_1 += einsum('ka,lc->klac', t1[ka], t1[kc]) t1new[ka] += -einsum('klic,klac->ia', Sooov, tau_term_1) time1 = log.timer_debug1('t1', *time1) # T2 equation t2new = np.empty_like(t2) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): t2new[ki, kj, ka] = eris.oovv[ki, kj, ka].conj() mem_now = lib.current_memory()[0] if (nocc ** 4 * nkpts ** 3) * 16 / 1e6 + mem_now < cc.max_memory * .9: Woooo = imdk.cc_Woooo(t1, t2, eris, kconserv) else: fimd = lib.H5TmpFile() Woooo = fimd.create_dataset('oooo', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nocc), t1.dtype.char) Woooo = imdk.cc_Woooo(t1, t2, eris, kconserv, Woooo) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): # Chemist's notation for momentum conserving t2(ki,kj,ka,kb) kb = kconserv[ki, ka, kj] t2new_tmp = np.zeros((nocc, nocc, nvir, nvir), dtype=t2.dtype) for kl in range(nkpts): kk = kconserv[kj, kl, ki] tau_term = t2[kk, kl, ka].copy() if kl == kb and kk == ka: tau_term += einsum('ic,jd->ijcd', t1[ka], t1[kb]) t2new_tmp += 0.5 * einsum('klij,klab->ijab', Woooo[kk, kl, ki], tau_term) t2new[ki, kj, ka] += t2new_tmp t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2) Woooo = None fimd = None time1 = log.timer_debug1('t2 oooo', *time1) # einsum('abcd,ijcd->ijab', Wvvvv, tau) add_vvvv_(cc, t2new, t1, t2, eris) time1 = log.timer_debug1('t2 vvvv', *time1) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] t2new_tmp = einsum('ac,ijcb->ijab', Lvv[ka], t2[ki, kj, ka]) t2new_tmp += einsum('ki,kjab->ijab', -Loo[ki], t2[ki, kj, ka]) kc = kconserv[ka, ki, kb] tmp2 = np.asarray(eris.vovv[kc, ki, kb]).transpose(3, 2, 1, 0).conj() \ - einsum('kbic,ka->abic', eris.ovov[ka, kb, ki], t1[ka]) t2new_tmp += einsum('abic,jc->ijab', tmp2, t1[kj]) kk = kconserv[ki, ka, kj] tmp2 = np.asarray(eris.ooov[kj, ki, kk]).transpose(3, 2, 1, 0).conj() \ + einsum('akic,jc->akij', eris.voov[ka, kk, ki], t1[kj]) t2new_tmp -= einsum('akij,kb->ijab', tmp2, t1[kb]) t2new[ki, kj, ka] += t2new_tmp t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2) mem_now = lib.current_memory()[0] if (nocc ** 2 * nvir ** 2 * nkpts ** 3) * 16 / 1e6 * 2 + mem_now < cc.max_memory * .9: Wvoov = imdk.cc_Wvoov(t1, t2, eris, kconserv) Wvovo = imdk.cc_Wvovo(t1, t2, eris, kconserv) else: fimd = lib.H5TmpFile() Wvoov = fimd.create_dataset('voov', (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), t1.dtype.char) Wvovo = fimd.create_dataset('vovo', (nkpts, nkpts, nkpts, nvir, nocc, nvir, nocc), t1.dtype.char) Wvoov = imdk.cc_Wvoov(t1, t2, eris, kconserv, Wvoov) Wvovo = imdk.cc_Wvovo(t1, t2, eris, kconserv, Wvovo) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] t2new_tmp = np.zeros((nocc, nocc, nvir, nvir), dtype=t2.dtype) for kk in range(nkpts): kc = kconserv[ka, ki, kk] tmp_voov = 2. * Wvoov[ka, kk, ki] - Wvovo[ka, kk, kc].transpose(0, 1, 3, 2) t2new_tmp += einsum('akic,kjcb->ijab', tmp_voov, t2[kk, kj, kc]) kc = kconserv[ka, ki, kk] t2new_tmp -= einsum('akic,kjbc->ijab', Wvoov[ka, kk, ki], t2[kk, kj, kb]) kc = kconserv[kk, ka, kj] t2new_tmp -= einsum('bkci,kjac->ijab', Wvovo[kb, kk, kc], t2[kk, kj, ka]) t2new[ki, kj, ka] += t2new_tmp t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2) Wvoov = Wvovo = None fimd = None time1 = log.timer_debug1('t2 voov', *time1) for ki in range(nkpts): ka = ki # Remove zero/padded elements from denominator eia = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia] t1new[ki] /= eia for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] # For LARGE_DENOM, see t1new update above eia = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] t2new[ki, kj, ka] /= eijab time0 = log.timer_debug1('update t1 t2', *time0) return t1new, t2new
def __init__(self, cc, mo_coeff=None, method='incore'): from pyscf.pbc import df from pyscf.pbc import tools from pyscf.pbc.cc.ccsd import _adjust_occ log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) moidx = get_frozen_mask(cc) cell = cc._scf.cell kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc # if any(nocc != np.count_nonzero(cc._scf.mo_occ[k]>0) # for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff dtype = mo_coeff[0].dtype mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with lib.temporary_env(cc._scf, exxdiv=None): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) self.fock = np.asarray([reduce(np.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(mo_coeff)]) self.mo_energy = [self.fock[k].diagonal().real for k in range(nkpts)] # Add HFX correction in the self.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) self.mo_energy = [_adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(self.mo_energy)] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cc, per_kpoint=True) nonzero_padding = padding_k_idx(cc, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [self.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = np.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[np.sum(nocc_per_kpt)] - mo_e[np.sum(nocc_per_kpt)-1] if gap < 1e-5: logger.warn(cc, 'H**O-LUMO gap %s too small for KCCSD. ' 'May cause issues in convergence.', gap) mem_incore, mem_outcore, mem_basic = _mem_usage(nkpts, nocc, nvir) mem_now = lib.current_memory()[0] fao2mo = cc._scf.with_df.ao2mo kconserv = cc.khelper.kconserv khelper = cc.khelper orbo = np.asarray(mo_coeff[:,:,:nocc], order='C') orbv = np.asarray(mo_coeff[:,:,nocc:], order='C') if (method == 'incore' and (mem_incore + mem_now < cc.max_memory) or cell.incore_anyway): log.info('using incore ERI storage') self.oooo = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nocc,nocc), dtype=dtype) self.ooov = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nocc,nvir), dtype=dtype) self.oovv = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nvir,nvir), dtype=dtype) self.ovov = np.empty((nkpts,nkpts,nkpts,nocc,nvir,nocc,nvir), dtype=dtype) self.voov = np.empty((nkpts,nkpts,nkpts,nvir,nocc,nocc,nvir), dtype=dtype) self.vovv = np.empty((nkpts,nkpts,nkpts,nvir,nocc,nvir,nvir), dtype=dtype) #self.vvvv = np.empty((nkpts,nkpts,nkpts,nvir,nvir,nvir,nvir), dtype=dtype) self.vvvv = cc._scf.with_df.ao2mo_7d(orbv, factor=1./nkpts).transpose(0,2,1,3,5,4,6) for (ikp,ikq,ikr) in khelper.symm_map.keys(): iks = kconserv[ikp,ikq,ikr] eri_kpt = fao2mo((mo_coeff[ikp],mo_coeff[ikq],mo_coeff[ikr],mo_coeff[iks]), (kpts[ikp],kpts[ikq],kpts[ikr],kpts[iks]), compact=False) if dtype == np.float: eri_kpt = eri_kpt.real eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: eri_kpt_symm = khelper.transform_symm(eri_kpt, kp, kq, kr).transpose(0, 2, 1, 3) self.oooo[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, :nocc, :nocc] / nkpts self.ooov[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, :nocc, nocc:] / nkpts self.oovv[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, nocc:, nocc:] / nkpts self.ovov[kp, kr, kq] = eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] / nkpts self.voov[kp, kr, kq] = eri_kpt_symm[nocc:, :nocc, :nocc, nocc:] / nkpts self.vovv[kp, kr, kq] = eri_kpt_symm[nocc:, :nocc, nocc:, nocc:] / nkpts #self.vvvv[kp, kr, kq] = eri_kpt_symm[nocc:, nocc:, nocc:, nocc:] / nkpts self.dtype = dtype else: log.info('using HDF5 ERI storage') self.feri1 = lib.H5TmpFile() self.oooo = self.feri1.create_dataset('oooo', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nocc), dtype.char) self.ooov = self.feri1.create_dataset('ooov', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nvir), dtype.char) self.oovv = self.feri1.create_dataset('oovv', (nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype.char) self.ovov = self.feri1.create_dataset('ovov', (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype.char) self.voov = self.feri1.create_dataset('voov', (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype.char) self.vovv = self.feri1.create_dataset('vovv', (nkpts, nkpts, nkpts, nvir, nocc, nvir, nvir), dtype.char) vvvv_required = ((not cc.direct) # cc._scf.with_df needs to be df.GDF only (not MDF) or type(cc._scf.with_df) is not df.GDF # direct-vvvv for pbc-2D is not supported so far or cell.dimension == 2) if vvvv_required: self.vvvv = self.feri1.create_dataset('vvvv', (nkpts,nkpts,nkpts,nvir,nvir,nvir,nvir), dtype.char) # <ij|pq> = (ip|jq) cput1 = time.clock(), time.time() for kp in range(nkpts): for kq in range(nkpts): for kr in range(nkpts): ks = kconserv[kp, kq, kr] orbo_p = mo_coeff[kp][:, :nocc] orbo_r = mo_coeff[kr][:, :nocc] buf_kpt = fao2mo((orbo_p, mo_coeff[kq], orbo_r, mo_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape(nocc, nmo, nocc, nmo).transpose(0, 2, 1, 3) self.dtype = buf_kpt.dtype self.oooo[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, :nocc] / nkpts self.ooov[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, nocc:] / nkpts self.oovv[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, nocc:, nocc:] / nkpts cput1 = log.timer_debug1('transforming oopq', *cput1) # <ia|pq> = (ip|aq) cput1 = time.clock(), time.time() for kp in range(nkpts): for kq in range(nkpts): for kr in range(nkpts): ks = kconserv[kp, kq, kr] orbo_p = mo_coeff[kp][:, :nocc] orbv_r = mo_coeff[kr][:, nocc:] buf_kpt = fao2mo((orbo_p, mo_coeff[kq], orbv_r, mo_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape(nocc, nmo, nvir, nmo).transpose(0, 2, 1, 3) self.ovov[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, nocc:] / nkpts # TODO: compute vovv on the fly self.vovv[kr, kp, ks, :, :, :, :] = buf_kpt[:, :, nocc:, nocc:].transpose(1, 0, 3, 2) / nkpts self.voov[kr, kp, ks, :, :, :, :] = buf_kpt[:, :, nocc:, :nocc].transpose(1, 0, 3, 2) / nkpts cput1 = log.timer_debug1('transforming ovpq', *cput1) ## Without k-point symmetry # cput1 = time.clock(), time.time() # for kp in range(nkpts): # for kq in range(nkpts): # for kr in range(nkpts): # ks = kconserv[kp,kq,kr] # orbv_p = mo_coeff[kp][:,nocc:] # orbv_q = mo_coeff[kq][:,nocc:] # orbv_r = mo_coeff[kr][:,nocc:] # orbv_s = mo_coeff[ks][:,nocc:] # for a in range(nvir): # orbva_p = orbv_p[:,a].reshape(-1,1) # buf_kpt = fao2mo((orbva_p,orbv_q,orbv_r,orbv_s), # (kpts[kp],kpts[kq],kpts[kr],kpts[ks]), compact=False) # if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real # buf_kpt = buf_kpt.reshape((1,nvir,nvir,nvir)).transpose(0,2,1,3) # self.vvvv[kp,kr,kq,a,:,:,:] = buf_kpt[:] / nkpts # cput1 = log.timer_debug1('transforming vvvv', *cput1) cput1 = time.clock(), time.time() mem_now = lib.current_memory()[0] if not vvvv_required: _init_df_eris(cc, self) elif nvir ** 4 * 16 / 1e6 + mem_now < cc.max_memory: for (ikp, ikq, ikr) in khelper.symm_map.keys(): iks = kconserv[ikp, ikq, ikr] orbv_p = mo_coeff[ikp][:, nocc:] orbv_q = mo_coeff[ikq][:, nocc:] orbv_r = mo_coeff[ikr][:, nocc:] orbv_s = mo_coeff[iks][:, nocc:] # unit cell is small enough to handle vvvv in-core buf_kpt = fao2mo((orbv_p,orbv_q,orbv_r,orbv_s), kpts[[ikp,ikq,ikr,iks]], compact=False) if dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape((nvir, nvir, nvir, nvir)) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: buf_kpt_symm = khelper.transform_symm(buf_kpt, kp, kq, kr).transpose(0, 2, 1, 3) self.vvvv[kp, kr, kq] = buf_kpt_symm / nkpts else: raise MemoryError('Minimal memory requirements %s MB' % (mem_now + nvir ** 4 / 1e6 * 16 * 2)) for (ikp, ikq, ikr) in khelper.symm_map.keys(): for a in range(nvir): orbva_p = orbv_p[:, a].reshape(-1, 1) buf_kpt = fao2mo((orbva_p, orbv_q, orbv_r, orbv_s), (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]), compact=False) if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape((1, nvir, nvir, nvir)).transpose(0, 2, 1, 3) self.vvvv[ikp, ikr, ikq, a, :, :, :] = buf_kpt[0, :, :, :] / nkpts # Store symmetric permutations self.vvvv[ikr, ikp, iks, :, a, :, :] = buf_kpt.transpose(1, 0, 3, 2)[:, 0, :, :] / nkpts self.vvvv[ikq, iks, ikp, :, :, a, :] = buf_kpt.transpose(2, 3, 0, 1).conj()[:, :, 0, :] / nkpts self.vvvv[iks, ikq, ikr, :, :, :, a] = buf_kpt.transpose(3, 2, 1, 0).conj()[:, :, :, 0] / nkpts cput1 = log.timer_debug1('transforming vvvv', *cput1) log.timer('CCSD integral transformation', *cput0)
def __init__(self, cc, mo_coeff=None): from pyscf.pbc.cc.ccsd import _adjust_occ import pyscf.pbc.tools.pbc as tools if mo_coeff is None: mo_coeff = cc.mo_coeff cput0 = (time.clock(), time.time()) symlib = cc.symlib log = Logger(cc.stdout, cc.verbose) self.lib = lib nocc, nmo, nkpts = cc.nocc, cc.nmo, cc.nkpts nvir = nmo - nocc cell, kpts = cc._scf.cell, cc.kpts gvec = cell.reciprocal_vectors() sym1 = ['+-', [kpts,]*2, None, gvec] sym2 = ['+-+-', [kpts,]*4, None, gvec] mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff) nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") madelung = tools.madelung(cell, kpts) fock = None if rank==0: dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with pyscflib.temporary_env(cc._scf, exxdiv=None): fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) fock = np.asarray([reduce(np.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(mo_coeff)]) fock = comm.bcast(fock, root=0) self.dtype = dtype = np.result_type(*(mo_coeff, fock)).char self.foo = zeros([nocc,nocc], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) self.fov = zeros([nocc,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) self.fvv = zeros([nvir,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) self.eia = zeros([nocc,nvir], np.float64, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) self._foo = zeros([nocc,nocc], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) self._fvv = zeros([nvir,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE) foo = fock[:,:nocc,:nocc] fov = fock[:,:nocc,nocc:] fvv = fock[:,nocc:,nocc:] mo_energy = [fock[k].diagonal().real for k in range(nkpts)] mo_energy = [_adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(mo_energy)] mo_e_o = [e[:nocc] for e in mo_energy] mo_e_v = [e[nocc:] + cc.level_shift for e in mo_energy] foo_ = np.asarray([np.diag(e) for e in mo_e_o]) fvv_ = np.asarray([np.diag(e) for e in mo_e_v]) eia = np.zeros([nkpts,nocc,nvir]) for ki in range(nkpts): eia[ki] = _get_epq([0,nocc,ki,mo_e_o,nonzero_opadding], [0,nvir,ki,mo_e_v,nonzero_vpadding], fac=[1.0,-1.0]) if rank ==0: self.foo.write(range(foo.size), foo.ravel()) self.fov.write(range(fov.size), fov.ravel()) self.fvv.write(range(fvv.size), fvv.ravel()) self.eia.write(range(eia.size), eia.ravel()) self._foo.write(range(foo_.size), foo_.ravel()) self._fvv.write(range(fvv_.size), fvv_.ravel()) else: self.foo.write([],[]) self.fov.write([],[]) self.fvv.write([],[]) self.eia.write([],[]) self._foo.write([],[]) self._fvv.write([],[]) self.eijab = zeros([nocc,nocc,nvir,nvir], np.float64, sym2, symlib=symlib, verbose=cc.SYMVERBOSE) kconserv = cc.khelper.kconserv khelper = cc.khelper idx_oovv = np.arange(nocc*nocc*nvir*nvir) jobs = list(khelper.symm_map.keys()) tasks = static_partition(jobs) ntasks = max(comm.allgather(len(tasks))) nwrite = 0 for itask in tasks: ikp, ikq, ikr = itask pqr = np.asarray(khelper.symm_map[(ikp,ikq,ikr)]) nwrite += len(np.unique(pqr, axis=0)) nwrite_max = max(comm.allgather(nwrite)) write_count = 0 for itask in range(ntasks): if itask >= len(tasks): continue ikp, ikq, ikr = tasks[itask] iks = kconserv[ikp,ikq,ikr] done = np.zeros([nkpts,nkpts,nkpts]) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: if done[kp,kq,kr]: continue ks = kconserv[kp,kq,kr] eia = _get_epq([0,nocc,kp,mo_e_o,nonzero_opadding], [0,nvir,kq,mo_e_v,nonzero_vpadding], fac=[1.0,-1.0]) ejb = _get_epq([0,nocc,kr,mo_e_o,nonzero_opadding], [0,nvir,ks,mo_e_v,nonzero_vpadding], fac=[1.0,-1.0]) eijab = eia[:,None,:,None] + ejb[None,:,None,:] off = kp * nkpts**2 + kr * nkpts + kq self.eijab.write(off*idx_oovv.size+idx_oovv, eijab.ravel()) done[kp,kq,kr] = 1 write_count += 1 for i in range(nwrite_max-write_count): self.eijab.write([], []) if type(cc._scf.with_df) is df.FFTDF: _make_fftdf_eris(cc, self) else: from cc_sym import mpigdf if type(cc._scf.with_df) is mpigdf.GDF: _make_df_eris(cc, self) elif type(cc._scf.with_df) is df.GDF: log.warn("GDF converted to an MPIGDF object") cc._scf.with_df = mpigdf.from_serial(cc._scf.with_df) _make_df_eris(cc, self) else: raise NotImplementedError("DF object not recognized") log.timer("ao2mo transformation", *cput0)
def get_full_t3p2(mycc, t1, t2, eris): '''Build the entire T3[2] array in memory. ''' nkpts = mycc.nkpts nocc = mycc.nocc nmo = mycc.nmo nvir = nmo - nocc kconserv = mycc.khelper.kconserv def get_wijkabc(ki, kj, kk, ka, kb, kc): '''Build T3[2] for `ijkabc` at a given set of k-points''' km = kconserv[kc, kk, kb] kf = kconserv[kk, kc, kj] ret = einsum('kjcf,ifab->ijkabc', t2[kk, kj, kc], eris.ovvv[ki, kf, ka].conj()) ret = ret - einsum('jima,mkbc->ijkabc', eris.ooov[kj, ki, km].conj(), t2[km, kk, kb]) return ret #fock = eris.fock #fov = fock[:, :nocc, nocc:] #foo = numpy.array([fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)]) #fvv = numpy.array([fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)]) mo_energy_occ = numpy.array( [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]) mo_energy_vir = numpy.array( [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]) mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") t3 = numpy.empty((nkpts, nkpts, nkpts, nkpts, nkpts, nocc, nocc, nocc, nvir, nvir, nvir), dtype=t2.dtype) for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5): kc = kpts_helper.get_kconserv3(mycc._scf.cell, mycc.kpts, [ki, kj, kk, ka, kb]) # Perform P(abc) t3[ki, kj, kk, ka, kb] = get_wijkabc(ki, kj, kk, ka, kb, kc) t3[ki, kj, kk, ka, kb] += get_wijkabc(ki, kj, kk, kb, kc, ka).transpose(0, 1, 2, 5, 3, 4) t3[ki, kj, kk, ka, kb] += get_wijkabc(ki, kj, kk, kc, ka, kb).transpose(0, 1, 2, 4, 5, 3) # Perform P(ijk) t3 = (t3.transpose(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + t3.transpose(1, 2, 0, 3, 4, 6, 7, 5, 8, 9, 10) + t3.transpose(2, 0, 1, 3, 4, 7, 5, 6, 8, 9, 10)) for ki, kj, kk in product(range(nkpts), repeat=3): eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nocc, kj, mo_e_o, nonzero_opadding], [0, nocc, kk, mo_e_o, nonzero_opadding]) for ka, kb in product(range(nkpts), repeat=2): kc = kpts_helper.get_kconserv3(mycc._scf.cell, mycc.kpts, [ki, kj, kk, ka, kb]) eabc = _get_epqr([0, nvir, ka, mo_e_v, nonzero_vpadding], [0, nvir, kb, mo_e_v, nonzero_vpadding], [0, nvir, kc, mo_e_v, nonzero_vpadding], fac=[-1., -1., -1.]) eijkabc = eijk[:, :, :, None, None, None] + eabc[None, None, None, :, :, :] t3[ki, kj, kk, ka, kb] /= eijkabc return t3
def update_amps(cc, t1, t2, eris): time0 = time.clock(), time.time() log = logger.Logger(cc.stdout, cc.verbose) nkpts, nocc, nvir = t1.shape fock = eris.fock mo_e_o = [e[:nocc] for e in eris.mo_energy] mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy] # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") fov = fock[:, :nocc, nocc:].copy() foo = fock[:, :nocc, :nocc].copy() fvv = fock[:, nocc:, nocc:].copy() # Get the momentum conservation array # Note: chemist's notation for momentum conserving t2(ki,kj,ka,kb), even though # integrals are in physics notation kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts) tau = imdk.make_tau(cc, t2, t1, t1, kconserv) Fvv = imdk.cc_Fvv(cc, t1, t2, eris, kconserv) Foo = imdk.cc_Foo(cc, t1, t2, eris, kconserv) Fov = imdk.cc_Fov(cc, t1, t2, eris, kconserv) Woooo = imdk.cc_Woooo(cc, t1, t2, eris, kconserv) Wvvvv = imdk.cc_Wvvvv(cc, t1, t2, eris, kconserv) Wovvo = imdk.cc_Wovvo(cc, t1, t2, eris, kconserv) # Move energy terms to the other side for k in range(nkpts): Foo[k][numpy.diag_indices(nocc)] -= mo_e_o[k] Fvv[k][numpy.diag_indices(nvir)] -= mo_e_v[k] eris_ovvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nvir, nvir, nocc), dtype=t2.dtype) eris_oovo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nocc, nvir, nocc), dtype=t2.dtype) eris_vvvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=t2.dtype) for km, kb, ke in kpts_helper.loop_kkk(nkpts): kj = kconserv[km, ke, kb] # <mb||je> -> -<mb||ej> eris_ovvo[km, kb, ke] = -eris.ovov[km, kb, kj].transpose(0, 1, 3, 2) # <mn||je> -> -<mn||ej> # let kb = kn as a dummy variable eris_oovo[km, kb, ke] = -eris.ooov[km, kb, kj].transpose(0, 1, 3, 2) # <ma||be> -> - <be||am>* # let kj = ka as a dummy variable kj = kconserv[km, ke, kb] eris_vvvo[ke, kj, kb] = -eris.ovvv[km, kb, ke].transpose(2, 3, 1, 0).conj() # T1 equation t1new = numpy.zeros(shape=t1.shape, dtype=t1.dtype) for ka in range(nkpts): ki = ka t1new[ka] += numpy.array(fov[ka, :, :]).conj() t1new[ka] += einsum('ie,ae->ia', t1[ka], Fvv[ka]) t1new[ka] += -einsum('ma,mi->ia', t1[ka], Foo[ka]) for km in range(nkpts): t1new[ka] += einsum('imae,me->ia', t2[ka, km, ka], Fov[km]) t1new[ka] += -einsum('nf,naif->ia', t1[km], eris.ovov[km, ka, ki]) for kn in range(nkpts): ke = kconserv[km, ki, kn] t1new[ka] += -0.5 * einsum('imef,maef->ia', t2[ki, km, ke], eris.ovvv[km, ka, ke]) t1new[ka] += -0.5 * einsum('mnae,nmei->ia', t2[km, kn, ka], eris_oovo[kn, km, ke]) # T2 equation t2new = numpy.array(eris.oovv).conj() for ki, kj, ka in kpts_helper.loop_kkk(nkpts): # Chemist's notation for momentum conserving t2(ki,kj,ka,kb) kb = kconserv[ki, ka, kj] Ftmp = Fvv[kb] - 0.5 * einsum('mb,me->be', t1[kb], Fov[kb]) tmp = einsum('ijae,be->ijab', t2[ki, kj, ka], Ftmp) t2new[ki, kj, ka] += tmp #t2new[ki,kj,kb] -= tmp.transpose(0,1,3,2) Ftmp = Fvv[ka] - 0.5 * einsum('ma,me->ae', t1[ka], Fov[ka]) tmp = einsum('ijbe,ae->ijab', t2[ki, kj, kb], Ftmp) t2new[ki, kj, ka] -= tmp Ftmp = Foo[kj] + 0.5 * einsum('je,me->mj', t1[kj], Fov[kj]) tmp = einsum('imab,mj->ijab', t2[ki, kj, ka], Ftmp) t2new[ki, kj, ka] -= tmp #t2new[kj,ki,ka] += tmp.transpose(1,0,2,3) Ftmp = Foo[ki] + 0.5 * einsum('ie,me->mi', t1[ki], Fov[ki]) tmp = einsum('jmab,mi->ijab', t2[kj, ki, ka], Ftmp) t2new[ki, kj, ka] += tmp for km in range(nkpts): # Wminj # - km - kn + ka + kb = 0 # => kn = ka - km + kb kn = kconserv[ka, km, kb] t2new[ki, kj, ka] += 0.5 * einsum( 'mnab,mnij->ijab', tau[km, kn, ka], Woooo[km, kn, ki]) ke = km t2new[ki, kj, ka] += 0.5 * einsum( 'ijef,abef->ijab', tau[ki, kj, ke], Wvvvv[ka, kb, ke]) # Wmbej # - km - kb + ke + kj = 0 # => ke = km - kj + kb ke = kconserv[km, kj, kb] tmp = einsum('imae,mbej->ijab', t2[ki, km, ka], Wovvo[km, kb, ke]) # - km - kb + ke + kj = 0 # => ke = km - kj + kb # # t[i,e] => ki = ke # t[m,a] => km = ka if km == ka and ke == ki: tmp -= einsum('ie,ma,mbej->ijab', t1[ki], t1[km], eris_ovvo[km, kb, ke]) t2new[ki, kj, ka] += tmp t2new[ki, kj, kb] -= tmp.transpose(0, 1, 3, 2) t2new[kj, ki, ka] -= tmp.transpose(1, 0, 2, 3) t2new[kj, ki, kb] += tmp.transpose(1, 0, 3, 2) ke = ki tmp = einsum('ie,abej->ijab', t1[ki], eris_vvvo[ka, kb, ke]) t2new[ki, kj, ka] += tmp # P(ij) term ke = kj tmp = einsum('je,abei->ijab', t1[kj], eris_vvvo[ka, kb, ke]) t2new[ki, kj, ka] -= tmp km = ka tmp = einsum('ma,mbij->ijab', t1[ka], eris.ovoo[km, kb, ki]) t2new[ki, kj, ka] -= tmp # P(ab) term km = kb tmp = einsum('mb,maij->ijab', t1[kb], eris.ovoo[km, ka, ki]) t2new[ki, kj, ka] += tmp for ki in range(nkpts): ka = ki # Remove zero/padded elements from denominator eia = LARGE_DENOM * numpy.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] t1new[ki] /= eia kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] # For LARGE_DENOM, see t1new update above eia = LARGE_DENOM * numpy.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * numpy.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] t2new[ki, kj, ka] /= eijab time0 = log.timer_debug1('update t1 t2', *time0) return t1new, t2new
def get_t3p2_imds(mycc, t1, t2, eris=None, t3p2_ip_out=None, t3p2_ea_out=None): """For a description of arguments, see `get_t3p2_imds_slow` in the corresponding `kintermediates.py`. """ from pyscf.pbc.cc.kccsd_t_rhf import _get_epqr cpu1 = cpu0 = (logger.process_clock(), logger.perf_counter()) if eris is None: eris = mycc.ao2mo() fock = eris.fock nkpts, nocc, nvir = t1.shape cell = mycc._scf.cell kpts = mycc.kpts kconserv = mycc.khelper.kconserv dtype = np.result_type(t1, t2) fov = fock[:, :nocc, nocc:] #foo = np.asarray([fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)]) #fvv = np.asarray([fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)]) mo_energy_occ = np.array( [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]) mo_energy_vir = np.array( [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]) mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir ccsd_energy = mycc.energy(t1, t2, eris) if t3p2_ip_out is None: t3p2_ip_out = np.zeros((nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc), dtype=dtype) Wmcik = t3p2_ip_out if t3p2_ea_out is None: t3p2_ea_out = np.zeros((nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=dtype) Wacek = t3p2_ea_out # Create necessary temporary eris for fast read from pyscf.pbc.cc.kccsd_t_rhf import create_t3_eris, get_data_slices feri_tmp, t2T, eris_vvop, eris_vooo_C = create_t3_eris( mycc, kconserv, [eris.vovv, eris.oovv, eris.ooov, t2]) #t1T = np.array([x.T for x in t1], dtype=np.complex, order='C') #fvo = np.array([x.T for x in fov], dtype=np.complex, order='C') cpu1 = logger.timer_debug1(mycc, 'CCSD(T) tmp eri creation', *cpu1) def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1): '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts Function copied for `kccsd_t_rhf.py`''' km = kconserv[kc, kk, kb] kf = kconserv[kk, kc, kj] out = einsum('cfjk,abif->abcijk', t2T[kc, kf, kj, c0:c1, :, :, :], eris_vvop[ka, kb, ki, a0:a1, b0:b1, :, nocc:]) out = out - einsum('cbmk,aijm->abcijk', t2T[kc, kb, km, c0:c1, b0:b1, :, :], eris_vooo_C[ka, ki, kj, a0:a1, :, :, :]) return out def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices): '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper Function copied for `kccsd_t_rhf.py`''' a0, a1, b0, b1, c0, c1 = orb_indices out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1) out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2, 0, 1, 5, 3, 4) out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1, 2, 0, 4, 5, 3) out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0, 2, 1, 3, 5, 4) out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2, 1, 0, 5, 4, 3) out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1, 0, 2, 4, 3, 5) return out def get_data(kpt_indices): idx_args = get_data_slices(kpt_indices, task, kconserv) vvop_indices, vooo_indices, t2T_vvop_indices, t2T_vooo_indices = idx_args vvop_data = [eris_vvop[tuple(x)] for x in vvop_indices] vooo_data = [eris_vooo_C[tuple(x)] for x in vooo_indices] t2T_vvop_data = [t2T[tuple(x)] for x in t2T_vvop_indices] t2T_vooo_data = [t2T[tuple(x)] for x in t2T_vooo_indices] data = [vvop_data, vooo_data, t2T_vvop_data, t2T_vooo_data] return data def add_and_permute(kpt_indices, orb_indices, data): '''Performs permutation and addition of t3 temporary arrays.''' ki, kj, kk, ka, kb, kc = kpt_indices a0, a1, b0, b1, c0, c1 = orb_indices tmp_t3Tv_ijk = np.asarray(data[0], dtype=dtype, order='C') tmp_t3Tv_jik = np.asarray(data[1], dtype=dtype, order='C') tmp_t3Tv_kji = np.asarray(data[2], dtype=dtype, order='C') #out_ijk = np.empty(data[0].shape, dtype=dtype, order='C') #drv = _ccsd.libcc.MPICCadd_and_permute_t3T #drv(ctypes.c_int(nocc), ctypes.c_int(nvir), # ctypes.c_int(0), # out_ijk.ctypes.data_as(ctypes.c_void_p), # tmp_t3Tv_ijk.ctypes.data_as(ctypes.c_void_p), # tmp_t3Tv_jik.ctypes.data_as(ctypes.c_void_p), # tmp_t3Tv_kji.ctypes.data_as(ctypes.c_void_p), # mo_offset.ctypes.data_as(ctypes.c_void_p), # slices.ctypes.data_as(ctypes.c_void_p)) return (2. * tmp_t3Tv_ijk - tmp_t3Tv_jik.transpose(0, 1, 2, 4, 3, 5) - tmp_t3Tv_kji.transpose(0, 1, 2, 5, 4, 3)) #return out_ijk # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") mem_now = lib.current_memory()[0] max_memory = max(0, mycc.max_memory - mem_now) blkmin = 4 # temporary t3 array is size: nkpts**3 * blksize**3 * nocc**3 * 16 vir_blksize = min( nvir, max(blkmin, int( (max_memory * .9e6 / 16 / nocc**3 / nkpts**3)**(1. / 3)))) tasks = [] logger.debug(mycc, 'max_memory %d MB (%d MB in use)', max_memory, mem_now) logger.debug(mycc, 'virtual blksize = %d (nvir = %d)', vir_blksize, nvir) for a0, a1 in lib.prange(0, nvir, vir_blksize): for b0, b1 in lib.prange(0, nvir, vir_blksize): for c0, c1 in lib.prange(0, nvir, vir_blksize): tasks.append((a0, a1, b0, b1, c0, c1)) eaa = [] for ka in range(nkpts): eaa.append(mo_e_o[ka][:, None] - mo_e_v[ka][None, :]) pt1 = np.zeros((nkpts, nocc, nvir), dtype=dtype) pt2 = np.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=dtype) for ka, kb in product(range(nkpts), repeat=2): for task_id, task in enumerate(tasks): cput2 = (logger.process_clock(), logger.perf_counter()) a0, a1, b0, b1, c0, c1 = task my_permuted_w = np.zeros( (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype) for ki, kj, kk in product(range(nkpts), repeat=3): # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) kpt_indices = [ki, kj, kk, ka, kb, kc] #data = get_data(kpt_indices) my_permuted_w[ki, kj, kk] = get_permuted_w(ki, kj, kk, ka, kb, kc, task) for ki, kj, kk in product(range(nkpts), repeat=3): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nocc, kj, mo_e_o, nonzero_opadding], [0, nocc, kk, mo_e_o, nonzero_opadding]) # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) eabc = _get_epqr([a0, a1, ka, mo_e_v, nonzero_vpadding], [b0, b1, kb, mo_e_v, nonzero_vpadding], [c0, c1, kc, mo_e_v, nonzero_vpadding], fac=[-1., -1., -1.]) kpt_indices = [ki, kj, kk, ka, kb, kc] eabcijk = (eijk[None, None, None, :, :, :] + eabc[:, :, :, None, None, None]) tmp_t3Tv_ijk = my_permuted_w[ki, kj, kk] tmp_t3Tv_jik = my_permuted_w[kj, ki, kk] tmp_t3Tv_kji = my_permuted_w[kk, kj, ki] Ptmp_t3Tv = add_and_permute( kpt_indices, task, (tmp_t3Tv_ijk, tmp_t3Tv_jik, tmp_t3Tv_kji)) Ptmp_t3Tv /= eabcijk # Contribution to T1 amplitudes if ki == ka and kc == kconserv[kj, kb, kk]: eris_Soovv = ( 2. * eris.oovv[kj, kk, kb, :, :, b0:b1, c0:c1] - eris.oovv[kj, kk, kc, :, :, c0:c1, b0:b1].transpose( 0, 1, 3, 2)) pt1[ka, :, a0:a1] += 0.5 * einsum('abcijk,jkbc->ia', Ptmp_t3Tv, eris_Soovv) # Contribution to T2 amplitudes if ki == ka and kc == kconserv[kj, kb, kk]: tmp = einsum('abcijk,ia->jkbc', Ptmp_t3Tv, 0.5 * fov[ki, :, a0:a1]) _add_pt2(pt2, nkpts, kconserv, [kj, kk, kb], [None, None, (b0, b1), (c0, c1)], tmp) kd = kconserv[ka, ki, kb] eris_vovv = eris.vovv[kd, ki, kb, :, :, b0:b1, a0:a1] tmp = einsum('abcijk,diba->jkdc', Ptmp_t3Tv, eris_vovv) _add_pt2(pt2, nkpts, kconserv, [kj, kk, kd], [None, None, None, (c0, c1)], tmp) km = kconserv[kc, kk, kb] eris_ooov = eris.ooov[kj, ki, km, :, :, :, a0:a1] tmp = einsum('abcijk,jima->mkbc', Ptmp_t3Tv, eris_ooov) _add_pt2(pt2, nkpts, kconserv, [km, kk, kb], [None, None, (b0, b1), (c0, c1)], -1. * tmp) # Contribution to Wovoo array km = kconserv[ka, ki, kc] eris_oovv = eris.oovv[km, ki, kc, :, :, c0:c1, a0:a1] tmp = einsum('abcijk,mica->mbkj', Ptmp_t3Tv, eris_oovv) Wmcik[km, kb, kk, :, b0:b1, :, :] += tmp # Contribution to Wvvoo array ke = kconserv[ki, ka, kk] eris_oovv = eris.oovv[ki, kk, ka, :, :, a0:a1, :] tmp = einsum('abcijk,ikae->cbej', Ptmp_t3Tv, eris_oovv) Wacek[kc, kb, ke, c0:c1, b0:b1, :, :] -= tmp logger.timer_debug1( mycc, 'EOM-CCSD T3[2] ka,kb,vir=(%d,%d,%d/%d) [total=%d]' % (ka, kb, task_id, len(tasks), nkpts**5), *cput2) for ki in range(nkpts): ka = ki eia = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] pt1[ki] /= eia for ki, ka in product(range(nkpts), repeat=2): eia = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia] for kj in range(nkpts): kb = kconserv[ki, ka, kj] ejb = LARGE_DENOM * np.ones( (nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] pt2[ki, kj, ka] /= eijab pt1 += t1 pt2 += t2 logger.timer(mycc, 'EOM-CCSD(T) imds', *cpu0) delta_ccsd_energy = mycc.energy(pt1, pt2, eris) - ccsd_energy logger.info(mycc, 'CCSD energy T3[2] correction : %16.12e', delta_ccsd_energy) return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
def kernel(mycc, eris, t1=None, t2=None, max_memory=2000, verbose=logger.INFO): '''Returns the CCSD(T) for restricted closed-shell systems with k-points. Note: Returns real part of the CCSD(T) energy, raises warning if there is a complex part. Args: mycc (:class:`RCCSD`): Coupled-cluster object storing results of a coupled-cluster calculation. eris (:class:`_ERIS`): Integral object holding the relevant electron- repulsion integrals and Fock matrix elements t1 (:obj:`ndarray`): t1 coupled-cluster amplitudes t2 (:obj:`ndarray`): t2 coupled-cluster amplitudes max_memory (float): Maximum memory used in calculation (NOT USED) verbose (int, :class:`Logger`): verbosity of calculation Returns: energy_t (float): The real-part of the k-point CCSD(T) energy. ''' assert isinstance(mycc, pyscf.pbc.cc.kccsd_rhf.RCCSD) cpu1 = cpu0 = (logger.process_clock(), logger.perf_counter()) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mycc.stdout, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if eris is None: raise TypeError('Electron repulsion integrals, `eris`, must be passed in ' 'to the CCSD(T) kernel or created in the cc object for ' 'the k-point CCSD(T) to run!') if t1 is None or t2 is None: raise TypeError('Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe ' 'need to run `.ccsd()` on the ccsd object?)') cell = mycc._scf.cell kpts = mycc.kpts # The dtype of any local arrays that will be created dtype = t1.dtype nkpts, nocc, nvir = t1.shape mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)] mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)] fov = eris.fock[:, :nocc, nocc:] # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) cpu1 = log.timer_debug1('CCSD(T) tmp eri creation', *cpu1) def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1, out=None): '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts''' km = kconserv[ki, ka, kj] kf = kconserv[kk, kc, kj] ret = einsum('kjcf,fiba->abcijk', t2[kk,kj,kc,:,:,c0:c1,:], eris.vovv[kf,ki,kb,:,:,b0:b1,a0:a1].conj()) ret = ret - einsum('mkbc,jima->abcijk', t2[km,kk,kb,:,:,b0:b1,c0:c1], eris.ooov[kj,ki,km,:,:,:,a0:a1].conj()) return ret def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices): '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1) out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2,0,1,5,3,4) out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1,2,0,4,5,3) out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0,2,1,3,5,4) out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2,1,0,5,4,3) out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1,0,2,4,3,5) return out def get_rw(ki, kj, kk, ka, kb, kc, orb_indices): '''R operating on Wijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices ret = (4. * get_permuted_w(ki,kj,kk,ka,kb,kc,orb_indices) + 1. * get_permuted_w(kj,kk,ki,ka,kb,kc,orb_indices).transpose(0,1,2,5,3,4) + 1. * get_permuted_w(kk,ki,kj,ka,kb,kc,orb_indices).transpose(0,1,2,4,5,3) - 2. * get_permuted_w(ki,kk,kj,ka,kb,kc,orb_indices).transpose(0,1,2,3,5,4) - 2. * get_permuted_w(kk,kj,ki,ka,kb,kc,orb_indices).transpose(0,1,2,5,4,3) - 2. * get_permuted_w(kj,ki,kk,ka,kb,kc,orb_indices).transpose(0,1,2,4,3,5)) return ret def get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1): '''Vijkabc intermediate as described in Scuseria paper''' out = np.zeros((a1-a0,b1-b0,c1-c0) + (nocc,)*3, dtype=dtype) if kk == kc: out = out + einsum('kc,ijab->abcijk', t1[kk,:,c0:c1], eris.oovv[ki,kj,ka,:,:,a0:a1,b0:b1].conj()) out = out + einsum('kc,ijab->abcijk', fov[kk,:,c0:c1], t2[ki,kj,ka,:,:,a0:a1,b0:b1]) return out def get_permuted_v(ki, kj, kk, ka, kb, kc, orb_indices): '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices ret = get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1) ret = ret + get_v(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2,0,1,5,3,4) ret = ret + get_v(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1,2,0,4,5,3) ret = ret + get_v(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0,2,1,3,5,4) ret = ret + get_v(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2,1,0,5,4,3) ret = ret + get_v(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1,0,2,4,3,5) return ret energy_t = 0.0 # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") mem_now = lib.current_memory()[0] max_memory = max(0, mycc.max_memory - mem_now) blkmin = 4 # temporary t3 array is size: blksize**3 * nocc**3 * 16 vir_blksize = min(nvir, max(blkmin, int((max_memory*.9e6/16/nocc**3)**(1./3)))) tasks = [] log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now) log.debug('virtual blksize = %d (nvir = %d)', nvir, vir_blksize) for a0, a1 in lib.prange(0, nvir, vir_blksize): for b0, b1 in lib.prange(0, nvir, vir_blksize): for c0, c1 in lib.prange(0, nvir, vir_blksize): tasks.append((a0,a1,b0,b1,c0,c1)) for ka in range(nkpts): for kb in range(ka+1): for ki, kj, kk in product(range(nkpts), repeat=3): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = LARGE_DENOM * np.ones((nocc,)*3, dtype=mo_energy_occ[0].dtype) n0_ovp_ijk = np.ix_(nonzero_opadding[ki], nonzero_opadding[kj], nonzero_opadding[kk]) eijk[n0_ovp_ijk] = lib.direct_sum('i,j,k->ijk', mo_energy_occ[ki], mo_energy_occ[kj], mo_energy_occ[kk])[n0_ovp_ijk] # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) if not (ka >= kb and kb >= kc): continue if ka == kb and kb == kc: symm_kpt = 1. elif ka == kb or kb == kc: symm_kpt = 3. else: symm_kpt = 6. eabc = LARGE_DENOM * np.ones((nvir,)*3, dtype=mo_energy_occ[0].dtype) n0_ovp_abc = np.ix_(nonzero_vpadding[ka], nonzero_vpadding[kb], nonzero_vpadding[kc]) eabc[n0_ovp_abc] = lib.direct_sum('i,j,k->ijk', mo_energy_vir[ka], mo_energy_vir[kb], mo_energy_vir[kc])[n0_ovp_abc] for task_id, task in enumerate(tasks): eijkabc = (eijk[None,None,None,:,:,:] - eabc[a0:a1,b0:b1,c0:c1,None,None,None]) pwijk = (get_permuted_w(ki,kj,kk,ka,kb,kc,task) + get_permuted_v(ki,kj,kk,ka,kb,kc,task) * 0.5) rwijk = get_rw(ki,kj,kk,ka,kb,kc,task) / eijkabc energy_t += symm_kpt * einsum('abcijk,abcijk', pwijk, rwijk.conj()) energy_t *= (1. / 3) energy_t /= nkpts if abs(energy_t.imag) > 1e-4: log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag) log.timer('CCSD(T)', *cpu0) log.note('CCSD(T) correction per cell = %.15g', energy_t.real) log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag) return energy_t.real
def update_amps(cc, t1, t2, eris): time0 = time.clock(), time.time() log = logger.Logger(cc.stdout, cc.verbose) nkpts, nocc, nvir = t1.shape fock = eris.fock mo_e_o = [e[:nocc] for e in eris.mo_energy] mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy] # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") fov = fock[:, :nocc, nocc:].copy() foo = fock[:, :nocc, :nocc].copy() fvv = fock[:, nocc:, nocc:].copy() # Get the momentum conservation array # Note: chemist's notation for momentum conserving t2(ki,kj,ka,kb), even though # integrals are in physics notation kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts) tau = imdk.make_tau(cc, t2, t1, t1, kconserv) Fvv = imdk.cc_Fvv(cc, t1, t2, eris, kconserv) Foo = imdk.cc_Foo(cc, t1, t2, eris, kconserv) Fov = imdk.cc_Fov(cc, t1, t2, eris, kconserv) Woooo = imdk.cc_Woooo(cc, t1, t2, eris, kconserv) Wvvvv = imdk.cc_Wvvvv(cc, t1, t2, eris, kconserv) Wovvo = imdk.cc_Wovvo(cc, t1, t2, eris, kconserv) # Move energy terms to the other side for k in range(nkpts): Foo[k][numpy.diag_indices(nocc)] -= mo_e_o[k] Fvv[k][numpy.diag_indices(nvir)] -= mo_e_v[k] eris_ovvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nvir, nvir, nocc), dtype=t2.dtype) eris_oovo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nocc, nvir, nocc), dtype=t2.dtype) eris_vvvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=t2.dtype) for km, kb, ke in kpts_helper.loop_kkk(nkpts): kj = kconserv[km, ke, kb] # <mb||je> -> -<mb||ej> eris_ovvo[km, kb, ke] = -eris.ovov[km, kb, kj].transpose(0, 1, 3, 2) # <mn||je> -> -<mn||ej> # let kb = kn as a dummy variable eris_oovo[km, kb, ke] = -eris.ooov[km, kb, kj].transpose(0, 1, 3, 2) # <ma||be> -> - <be||am>* # let kj = ka as a dummy variable kj = kconserv[km, ke, kb] eris_vvvo[ke, kj, kb] = -eris.ovvv[km, kb, ke].transpose(2, 3, 1, 0).conj() # T1 equation t1new = numpy.zeros(shape=t1.shape, dtype=t1.dtype) for ka in range(nkpts): ki = ka t1new[ka] += numpy.array(fov[ka, :, :]).conj() t1new[ka] += einsum('ie,ae->ia', t1[ka], Fvv[ka]) t1new[ka] += -einsum('ma,mi->ia', t1[ka], Foo[ka]) for km in range(nkpts): t1new[ka] += einsum('imae,me->ia', t2[ka, km, ka], Fov[km]) t1new[ka] += -einsum('nf,naif->ia', t1[km], eris.ovov[km, ka, ki]) for kn in range(nkpts): ke = kconserv[km, ki, kn] t1new[ka] += -0.5 * einsum('imef,maef->ia', t2[ki, km, ke], eris.ovvv[km, ka, ke]) t1new[ka] += -0.5 * einsum('mnae,nmei->ia', t2[km, kn, ka], eris_oovo[kn, km, ke]) # T2 equation t2new = numpy.array(eris.oovv).conj() for ki, kj, ka in kpts_helper.loop_kkk(nkpts): # Chemist's notation for momentum conserving t2(ki,kj,ka,kb) kb = kconserv[ki, ka, kj] Ftmp = Fvv[kb] - 0.5 * einsum('mb,me->be', t1[kb], Fov[kb]) tmp = einsum('ijae,be->ijab', t2[ki, kj, ka], Ftmp) t2new[ki, kj, ka] += tmp #t2new[ki,kj,kb] -= tmp.transpose(0,1,3,2) Ftmp = Fvv[ka] - 0.5 * einsum('ma,me->ae', t1[ka], Fov[ka]) tmp = einsum('ijbe,ae->ijab', t2[ki, kj, kb], Ftmp) t2new[ki, kj, ka] -= tmp Ftmp = Foo[kj] + 0.5 * einsum('je,me->mj', t1[kj], Fov[kj]) tmp = einsum('imab,mj->ijab', t2[ki, kj, ka], Ftmp) t2new[ki, kj, ka] -= tmp #t2new[kj,ki,ka] += tmp.transpose(1,0,2,3) Ftmp = Foo[ki] + 0.5 * einsum('ie,me->mi', t1[ki], Fov[ki]) tmp = einsum('jmab,mi->ijab', t2[kj, ki, ka], Ftmp) t2new[ki, kj, ka] += tmp for km in range(nkpts): # Wminj # - km - kn + ka + kb = 0 # => kn = ka - km + kb kn = kconserv[ka, km, kb] t2new[ki, kj, ka] += 0.5 * einsum('mnab,mnij->ijab', tau[km, kn, ka], Woooo[km, kn, ki]) ke = km t2new[ki, kj, ka] += 0.5 * einsum('ijef,abef->ijab', tau[ki, kj, ke], Wvvvv[ka, kb, ke]) # Wmbej # - km - kb + ke + kj = 0 # => ke = km - kj + kb ke = kconserv[km, kj, kb] tmp = einsum('imae,mbej->ijab', t2[ki, km, ka], Wovvo[km, kb, ke]) # - km - kb + ke + kj = 0 # => ke = km - kj + kb # # t[i,e] => ki = ke # t[m,a] => km = ka if km == ka and ke == ki: tmp -= einsum('ie,ma,mbej->ijab', t1[ki], t1[km], eris_ovvo[km, kb, ke]) t2new[ki, kj, ka] += tmp t2new[ki, kj, kb] -= tmp.transpose(0, 1, 3, 2) t2new[kj, ki, ka] -= tmp.transpose(1, 0, 2, 3) t2new[kj, ki, kb] += tmp.transpose(1, 0, 3, 2) ke = ki tmp = einsum('ie,abej->ijab', t1[ki], eris_vvvo[ka, kb, ke]) t2new[ki, kj, ka] += tmp # P(ij) term ke = kj tmp = einsum('je,abei->ijab', t1[kj], eris_vvvo[ka, kb, ke]) t2new[ki, kj, ka] -= tmp km = ka tmp = einsum('ma,mbij->ijab', t1[ka], eris.ovoo[km, kb, ki]) t2new[ki, kj, ka] -= tmp # P(ab) term km = kb tmp = einsum('mb,maij->ijab', t1[kb], eris.ovoo[km, ka, ki]) t2new[ki, kj, ka] += tmp for ki in range(nkpts): ka = ki # Remove zero/padded elements from denominator eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia] t1new[ki] /= eia kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] # For LARGE_DENOM, see t1new update above eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka]) eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia] ejb = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype) n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb]) ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb] eijab = eia[:, None, :, None] + ejb[:, None, :] t2new[ki, kj, ka] /= eijab time0 = log.timer_debug1('update t1 t2', *time0) return t1new, t2new
def _make_eris_incore(cc, mo_coeff=None): from pyscf.pbc import tools from pyscf.pbc.cc.ccsd import _adjust_occ log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) eris = gccsd._PhysicistsERIs() cell = cc._scf.cell kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc eris.nocc = nocc #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff nao = mo_coeff[0].shape[0] dtype = mo_coeff[0].dtype moidx = get_frozen_mask(cc) nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True)) nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True)) padded_moidx = [] for k in range(nkpts): kpt_nocc = nocc_per_kpt[k] kpt_nvir = nmo_per_kpt[k] - kpt_nocc kpt_padded_moidx = numpy.concatenate((numpy.ones(kpt_nocc, dtype=numpy.bool), numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool), numpy.ones(kpt_nvir, dtype=numpy.bool))) padded_moidx.append(kpt_padded_moidx) eris.mo_coeff = [] eris.orbspin = [] # Generate the molecular orbital coefficients with the frozen orbitals masked. # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall # spin of each MO. # # Here we will work with two index arrays; one is for our original (small) moidx # array while the next is for our new (large) padded array. for k in range(nkpts): kpt_moidx = moidx[k] kpt_padded_moidx = padded_moidx[k] mo = numpy.zeros((nao, nmo), dtype=dtype) mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx] if getattr(mo_coeff[k], 'orbspin', None) is not None: orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype orbspin = numpy.zeros(nmo, dtype=orbspin_dtype) orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx] mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) # FIXME: What if the user freezes all up spin orbitals in # an RHF calculation? The number of electrons will still be # even. else: # guess orbital spin - assumes an RHF calculation assert (numpy.count_nonzero(kpt_moidx) % 2 == 0) orbspin = numpy.zeros(mo.shape[1], dtype=int) orbspin[1::2] = 1 mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) eris.mo_coeff.append(mo) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with lib.temporary_env(cc._scf, exxdiv=None): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) eris.fock = numpy.asarray([reduce(numpy.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(eris.mo_coeff)]) eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)] # Add HFX correction in the eris.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) eris.mo_energy = [_adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(eris.mo_energy)] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cc, per_kpoint=True) nonzero_padding = padding_k_idx(cc, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = numpy.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt)-1] if gap < 1e-5: logger.warn(cc, 'H**O-LUMO gap %s too small for KCCSD. ' 'May cause issues in convergence.', gap) kconserv = kpts_helper.get_kconserv(cell, kpts) if getattr(mo_coeff[0], 'orbspin', None) is None: # The bottom nao//2 coefficients are down (up) spin while the top are up (down). mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff] mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo( (mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo( (mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo( (mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo( (mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt else: mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo( (mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0 eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0 eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt # Check some antisymmetrized properties of the integrals if DEBUG: check_antisymm_3412(cc, cc.kpts, eri) # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to # (rq|ps); done since we aren't tracking the kpoint of orbital 's' eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6) # Chemist -> physics notation eri = eri.transpose(0, 2, 1, 3, 5, 4, 6) # Set the various integrals eris.dtype = eri.dtype eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts log.timer('CCSD integral transformation', *cput0) return eris
def _make_eris_incore(cc, mo_coeff=None): from pyscf.pbc import tools from pyscf.pbc.cc.ccsd import _adjust_occ log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) eris = gccsd._PhysicistsERIs() cell = cc._scf.cell kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc eris.nocc = nocc #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff nao = mo_coeff[0].shape[0] dtype = mo_coeff[0].dtype moidx = get_frozen_mask(cc) nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True)) nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True)) padded_moidx = [] for k in range(nkpts): kpt_nocc = nocc_per_kpt[k] kpt_nvir = nmo_per_kpt[k] - kpt_nocc kpt_padded_moidx = numpy.concatenate( (numpy.ones(kpt_nocc, dtype=numpy.bool), numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool), numpy.ones(kpt_nvir, dtype=numpy.bool))) padded_moidx.append(kpt_padded_moidx) eris.mo_coeff = [] eris.orbspin = [] # Generate the molecular orbital coefficients with the frozen orbitals masked. # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall # spin of each MO. # # Here we will work with two index arrays; one is for our original (small) moidx # array while the next is for our new (large) padded array. for k in range(nkpts): kpt_moidx = moidx[k] kpt_padded_moidx = padded_moidx[k] mo = numpy.zeros((nao, nmo), dtype=dtype) mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx] if getattr(mo_coeff[k], 'orbspin', None) is not None: orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype orbspin = numpy.zeros(nmo, dtype=orbspin_dtype) orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx] mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) # FIXME: What if the user freezes all up spin orbitals in # an RHF calculation? The number of electrons will still be # even. else: # guess orbital spin - assumes an RHF calculation assert (numpy.count_nonzero(kpt_moidx) % 2 == 0) orbspin = numpy.zeros(mo.shape[1], dtype=int) orbspin[1::2] = 1 mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) eris.mo_coeff.append(mo) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with lib.temporary_env(cc._scf, exxdiv=None): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) eris.fock = numpy.asarray([ reduce(numpy.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(eris.mo_coeff) ]) eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)] # Add HFX correction in the eris.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) eris.mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(eris.mo_energy) ] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cc, per_kpoint=True) nonzero_padding = padding_k_idx(cc, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = numpy.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt) - 1] if gap < 1e-5: logger.warn( cc, 'H**O-LUMO gap %s too small for KCCSD. ' 'May cause issues in convergence.', gap) kconserv = kpts_helper.get_kconserv(cell, kpts) if getattr(mo_coeff[0], 'orbspin', None) is None: # The bottom nao//2 coefficients are down (up) spin while the top are up (down). mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff] mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt else: mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0 eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0 eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt # Check some antisymmetrized properties of the integrals if DEBUG: check_antisymm_3412(cc, cc.kpts, eri) # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to # (rq|ps); done since we aren't tracking the kpoint of orbital 's' eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6) # Chemist -> physics notation eri = eri.transpose(0, 2, 1, 3, 5, 4, 6) # Set the various integrals eris.dtype = eri.dtype eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts log.timer('CCSD integral transformation', *cput0) return eris
def assert_non_padded(cc, p, kp): if p not in padding_k_idx(cc, kind="joint")[kp]: raise PaddingError("The index p={:d} at k={:d} is padded".format( p, kp))
def kernel(mycc, eris, t1=None, t2=None, max_memory=2000, verbose=logger.INFO): '''Returns the CCSD(T) for restricted closed-shell systems with k-points. Note: Returns real part of the CCSD(T) energy, raises warning if there is a complex part. Args: mycc (:class:`RCCSD`): Coupled-cluster object storing results of a coupled-cluster calculation. eris (:class:`_ERIS`): Integral object holding the relevant electron- repulsion integrals and Fock matrix elements t1 (:obj:`ndarray`): t1 coupled-cluster amplitudes t2 (:obj:`ndarray`): t2 coupled-cluster amplitudes max_memory (float): Maximum memory used in calculation (NOT USED) verbose (int, :class:`Logger`): verbosity of calculation Returns: energy_t (float): The real-part of the k-point CCSD(T) energy. ''' assert isinstance(mycc, pyscf.pbc.cc.kccsd_rhf.RCCSD) cpu1 = cpu0 = (time.clock(), time.time()) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mycc.stdout, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if eris is None: raise TypeError( 'Electron repulsion integrals, `eris`, must be passed in ' 'to the CCSD(T) kernel or created in the cc object for ' 'the k-point CCSD(T) to run!') if t1 is None or t2 is None: raise TypeError( 'Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe ' 'need to run `.ccsd()` on the ccsd object?)') cell = mycc._scf.cell kpts = mycc.kpts # The dtype of any local arrays that will be created dtype = t1.dtype nkpts, nocc, nvir = t1.shape mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)] mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)] mo_energy = np.asarray([eris.mo_energy[ki] for ki in range(nkpts)], dtype=np.float, order='C') fov = eris.fock[:, :nocc, nocc:] mo_e = mo_energy mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) # Create necessary temporary eris for fast read feri_tmp, t2T, eris_vvop, eris_vooo_C = create_t3_eris( mycc, kconserv, [eris.vovv, eris.oovv, eris.ooov, t2]) t1T = np.array([x.T for x in t1], dtype=np.complex, order='C') fvo = np.array([x.T for x in fov], dtype=np.complex, order='C') cpu1 = log.timer_debug1('CCSD(T) tmp eri creation', *cpu1) #def get_w_old(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1, out=None): # '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts''' # km = kconserv[kc, kk, kb] # kf = kconserv[kk, kc, kj] # ret = einsum('kjcf,fiba->abcijk', t2[kk,kj,kc,:,:,c0:c1,:], eris.vovv[kf,ki,kb,:,:,b0:b1,a0:a1].conj()) # ret = ret - einsum('mkbc,jima->abcijk', t2[km,kk,kb,:,:,b0:b1,c0:c1], eris.ooov[kj,ki,km,:,:,:,a0:a1].conj()) # return ret def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1): '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts Uses tranposed eris for fast data access.''' km = kconserv[kc, kk, kb] kf = kconserv[kk, kc, kj] out = einsum('cfjk,abif->abcijk', t2T[kc, kf, kj, c0:c1, :, :, :], eris_vvop[ka, kb, ki, a0:a1, b0:b1, :, nocc:]) out = out - einsum('cbmk,aijm->abcijk', t2T[kc, kb, km, c0:c1, b0:b1, :, :], eris_vooo_C[ka, ki, kj, a0:a1, :, :, :]) return out def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices): '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1) out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2, 0, 1, 5, 3, 4) out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1, 2, 0, 4, 5, 3) out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0, 2, 1, 3, 5, 4) out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2, 1, 0, 5, 4, 3) out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1, 0, 2, 4, 3, 5) return out def get_rw(ki, kj, kk, ka, kb, kc, orb_indices): '''R operating on Wijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices ret = (4. * get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices) + 1. * get_permuted_w(kj, kk, ki, ka, kb, kc, orb_indices).transpose(0, 1, 2, 5, 3, 4) + 1. * get_permuted_w(kk, ki, kj, ka, kb, kc, orb_indices).transpose(0, 1, 2, 4, 5, 3) - 2. * get_permuted_w(ki, kk, kj, ka, kb, kc, orb_indices).transpose(0, 1, 2, 3, 5, 4) - 2. * get_permuted_w(kk, kj, ki, ka, kb, kc, orb_indices).transpose(0, 1, 2, 5, 4, 3) - 2. * get_permuted_w(kj, ki, kk, ka, kb, kc, orb_indices).transpose(0, 1, 2, 4, 3, 5)) return ret #def get_v_old(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1): # '''Vijkabc intermediate as described in Scuseria paper''' # km = kconserv[ki,ka,kj] # kf = kconserv[ki,ka,kj] # out = np.zeros((a1-a0,b1-b0,c1-c0) + (nocc,)*3, dtype=dtype) # if kk == kc: # out = out + einsum('kc,ijab->abcijk', 0.5*t1[kk,:,c0:c1], eris.oovv[ki,kj,ka,:,:,a0:a1,b0:b1].conj()) # out = out + einsum('kc,ijab->abcijk', 0.5*fov[kk,:,c0:c1], t2[ki,kj,ka,:,:,a0:a1,b0:b1]) # return out def get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1): '''Vijkabc intermediate as described in Scuseria paper''' km = kconserv[ki, ka, kj] kf = kconserv[ki, ka, kj] out = np.zeros((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype) if kk == kc: out = out + einsum('ck,baji->abcijk', 0.5 * t1T[kk, c0:c1, :], eris_vvop[kb, ka, kj, b0:b1, a0:a1, :, :nocc]) # We see this is the same t2T term needed for the `w` contraction: # einsum('cbmk,aijm->abcijk', t2T[kc,kb,km,c0:c1,b0:b1], eris_vooo_C[ka,ki,kj,a0:a1]) # # For the kpoint indices [kk,ki,kj,kc,ka,kb] we have that we need # t2T[kb,ka,km], where km = kconserv[kb,kj,ka] # The remaining k-point not used in t2T, i.e. kc, has the condition kc == kk in the case of # get_v. So, we have from 3-particle conservation # (kk-kc) + ki + kj - ka - kb = 0, # i.e. ki = km. out = out + einsum('ck,baij->abcijk', 0.5 * fvo[kk, c0:c1, :], t2T[kb, ka, ki, b0:b1, a0:a1, :, :]) return out def get_permuted_v(ki, kj, kk, ka, kb, kc, orb_indices): '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper''' a0, a1, b0, b1, c0, c1 = orb_indices tmp = np.zeros((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype) ret = get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1) ret = ret + get_v(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2, 0, 1, 5, 3, 4) ret = ret + get_v(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1, 2, 0, 4, 5, 3) ret = ret + get_v(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0, 2, 1, 3, 5, 4) ret = ret + get_v(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2, 1, 0, 5, 4, 3) ret = ret + get_v(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1, 0, 2, 4, 3, 5) return ret def contract_t3Tv(kpt_indices, orb_indices, data): '''Calculate t3T(ransposed) array using C driver.''' ki, kj, kk, ka, kb, kc = kpt_indices a0, a1, b0, b1, c0, c1 = orb_indices slices = np.array([a0, a1, b0, b1, c0, c1], dtype=np.int32) mo_offset = np.array([ki, kj, kk, ka, kb, kc], dtype=np.int32) vvop_ab = np.asarray(data[0][0], dtype=np.complex, order='C') vvop_ac = np.asarray(data[0][1], dtype=np.complex, order='C') vvop_ba = np.asarray(data[0][2], dtype=np.complex, order='C') vvop_bc = np.asarray(data[0][3], dtype=np.complex, order='C') vvop_ca = np.asarray(data[0][4], dtype=np.complex, order='C') vvop_cb = np.asarray(data[0][5], dtype=np.complex, order='C') vooo_aj = np.asarray(data[1][0], dtype=np.complex, order='C') vooo_ak = np.asarray(data[1][1], dtype=np.complex, order='C') vooo_bi = np.asarray(data[1][2], dtype=np.complex, order='C') vooo_bk = np.asarray(data[1][3], dtype=np.complex, order='C') vooo_ci = np.asarray(data[1][4], dtype=np.complex, order='C') vooo_cj = np.asarray(data[1][5], dtype=np.complex, order='C') t2T_cj = np.asarray(data[2][0], dtype=np.complex, order='C') t2T_bk = np.asarray(data[2][1], dtype=np.complex, order='C') t2T_ci = np.asarray(data[2][2], dtype=np.complex, order='C') t2T_ak = np.asarray(data[2][3], dtype=np.complex, order='C') t2T_bi = np.asarray(data[2][4], dtype=np.complex, order='C') t2T_aj = np.asarray(data[2][5], dtype=np.complex, order='C') t2T_cb = np.asarray(data[3][0], dtype=np.complex, order='C') t2T_bc = np.asarray(data[3][1], dtype=np.complex, order='C') t2T_ca = np.asarray(data[3][2], dtype=np.complex, order='C') t2T_ac = np.asarray(data[3][3], dtype=np.complex, order='C') t2T_ba = np.asarray(data[3][4], dtype=np.complex, order='C') t2T_ab = np.asarray(data[3][5], dtype=np.complex, order='C') data = [ vvop_ab, vvop_ac, vvop_ba, vvop_bc, vvop_ca, vvop_cb, vooo_aj, vooo_ak, vooo_bi, vooo_bk, vooo_ci, vooo_cj, t2T_cj, t2T_cb, t2T_bk, t2T_bc, t2T_ci, t2T_ca, t2T_ak, t2T_ac, t2T_bi, t2T_ba, t2T_aj, t2T_ab ] data_ptrs = [x.ctypes.data_as(ctypes.c_void_p) for x in data] data_ptrs = (ctypes.c_void_p * 24)(*data_ptrs) a0, a1, b0, b1, c0, c1 = task t3Tw = np.empty((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=np.complex, order='C') t3Tv = np.empty((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=np.complex, order='C') drv = _ccsd.libcc.CCsd_zcontract_t3T drv(t3Tw.ctypes.data_as(ctypes.c_void_p), t3Tv.ctypes.data_as(ctypes.c_void_p), mo_e.ctypes.data_as(ctypes.c_void_p), t1T.ctypes.data_as(ctypes.c_void_p), fvo.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nocc), ctypes.c_int(nvir), ctypes.c_int(nkpts), mo_offset.ctypes.data_as(ctypes.c_void_p), slices.ctypes.data_as(ctypes.c_void_p), data_ptrs) return t3Tw, t3Tv def get_data(kpt_indices): idx_args = get_data_slices(kpt_indices, task, kconserv) vvop_indices, vooo_indices, t2T_vvop_indices, t2T_vooo_indices = idx_args vvop_data = [eris_vvop[tuple(x)] for x in vvop_indices] vooo_data = [eris_vooo_C[tuple(x)] for x in vooo_indices] t2T_vvop_data = [t2T[tuple(x)] for x in t2T_vvop_indices] t2T_vooo_data = [t2T[tuple(x)] for x in t2T_vooo_indices] data = [vvop_data, vooo_data, t2T_vvop_data, t2T_vooo_data] return data energy_t = 0.0 # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") mem_now = lib.current_memory()[0] max_memory = max(0, mycc.max_memory - mem_now) blkmin = 4 # temporary t3 array is size: 2 * nkpts**3 * blksize**3 * nocc**3 * 16 vir_blksize = min( nvir, max(blkmin, int((max_memory * .9e6 / 16 / nocc**3 / nkpts**3 / 2)**(1. / 3)))) tasks = [] log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now) log.debug('virtual blksize = %d (nvir = %d)', nvir, vir_blksize) for a0, a1 in lib.prange(0, nvir, vir_blksize): for b0, b1 in lib.prange(0, nvir, vir_blksize): for c0, c1 in lib.prange(0, nvir, vir_blksize): tasks.append((a0, a1, b0, b1, c0, c1)) for ka in range(nkpts): for kb in range(ka + 1): for task_id, task in enumerate(tasks): a0, a1, b0, b1, c0, c1 = task my_permuted_w = np.zeros( (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype) my_permuted_v = np.zeros( (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype) for ki, kj, kk in product(range(nkpts), repeat=3): # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) if not (ka >= kb and kb >= kc): continue kpt_indices = [ki, kj, kk, ka, kb, kc] data = get_data(kpt_indices) t3Tw, t3Tv = contract_t3Tv(kpt_indices, task, data) my_permuted_w[ki, kj, kk] = t3Tw my_permuted_v[ki, kj, kk] = t3Tv #my_permuted_w[ki,kj,kk] = get_permuted_w(ki,kj,kk,ka,kb,kc,task) #my_permuted_v[ki,kj,kk] = get_permuted_v(ki,kj,kk,ka,kb,kc,task) for ki, kj, kk in product(range(nkpts), repeat=3): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nocc, kj, mo_e_o, nonzero_opadding], [0, nocc, kk, mo_e_o, nonzero_opadding]) # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) if not (ka >= kb and kb >= kc): continue if ka == kb and kb == kc: symm_kpt = 1. elif ka == kb or kb == kc: symm_kpt = 3. else: symm_kpt = 6. eabc = _get_epqr([a0, a1, ka, mo_e_v, nonzero_vpadding], [b0, b1, kb, mo_e_v, nonzero_vpadding], [c0, c1, kc, mo_e_v, nonzero_vpadding], fac=[-1., -1., -1.]) eijkabc = (eijk[None, None, None, :, :, :] + eabc[:, :, :, None, None, None]) pwijk = my_permuted_w[ki, kj, kk] + my_permuted_v[ki, kj, kk] rwijk = ( 4. * my_permuted_w[ki, kj, kk] + 1. * my_permuted_w[kj, kk, ki].transpose(0, 1, 2, 5, 3, 4) + 1. * my_permuted_w[kk, ki, kj].transpose(0, 1, 2, 4, 5, 3) - 2. * my_permuted_w[ki, kk, kj].transpose(0, 1, 2, 3, 5, 4) - 2. * my_permuted_w[kk, kj, ki].transpose(0, 1, 2, 5, 4, 3) - 2. * my_permuted_w[kj, ki, kk].transpose(0, 1, 2, 4, 3, 5)) rwijk = rwijk / eijkabc energy_t += symm_kpt * einsum('abcijk,abcijk', rwijk, pwijk.conj()) energy_t *= (1. / 3) energy_t /= nkpts if abs(energy_t.imag) > 1e-4: log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag) log.timer('CCSD(T)', *cpu0) log.note('CCSD(T) correction per cell = %.15g', energy_t.real) log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag) return energy_t.real
def __init__(self, cc, mo_coeff=None): from pyscf.pbc.cc.ccsd import _adjust_occ import pyscf.pbc.tools.pbc as tools if mo_coeff is None: mo_coeff = cc.mo_coeff cput0 = (time.clock(), time.time()) log = Logger(cc.stdout, cc.verbose) self.lib = lib nocc, nmo, nkpts = cc.nocc, cc.nmo, cc.nkpts nvir = nmo - nocc cell, kpts = cc._scf.cell, cc.kpts gvec = cell.reciprocal_vectors() sym1 = ['+-', [ kpts, ] * 2, None, gvec] sym2 = ['+-+-', [ kpts, ] * 4, None, gvec] mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff) nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") madelung = tools.madelung(cell, kpts) dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with pyscflib.temporary_env(cc._scf, exxdiv=None): fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm) fock = np.asarray([ reduce(np.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(mo_coeff) ]) self.dtype = dtype = np.result_type(*fock).char self.foo = tensor(fock[:, :nocc, :nocc], sym1) self.fov = tensor(fock[:, :nocc, nocc:], sym1) self.fvv = tensor(fock[:, nocc:, nocc:], sym1) mo_energy = [fock[k].diagonal().real for k in range(nkpts)] mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(mo_energy) ] mo_e_o = [e[:nocc] for e in mo_energy] mo_e_v = [e[nocc:] + cc.level_shift for e in mo_energy] foo_ = np.asarray([np.diag(e) for e in mo_e_o]) fvv_ = np.asarray([np.diag(e) for e in mo_e_v]) self._foo = tensor(foo_, sym1) self._fvv = tensor(fvv_, sym1) eia = np.zeros([nkpts, nocc, nvir]) for ki in range(nkpts): eia[ki] = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nvir, ki, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) self.eia = tensor(eia, sym1) self.oooo = zeros([nocc, nocc, nocc, nocc], dtype, sym2) self.ooov = zeros([nocc, nocc, nocc, nvir], dtype, sym2) self.ovov = zeros([nocc, nvir, nocc, nvir], dtype, sym2) self.oovv = zeros([nocc, nocc, nvir, nvir], dtype, sym2) self.ovvo = zeros([nocc, nvir, nvir, nocc], dtype, sym2) self.ovvv = zeros([nocc, nvir, nvir, nvir], dtype, sym2) self.vvvv = zeros([nvir, nvir, nvir, nvir], dtype, sym2) self.eijab = zeros([nocc, nocc, nvir, nvir], np.float64, sym2) with_df = cc._scf.with_df fao2mo = cc._scf.with_df.ao2mo kconserv = cc.khelper.kconserv khelper = cc.khelper jobs = list(khelper.symm_map.keys()) for itask in jobs: ikp, ikq, ikr = itask iks = kconserv[ikp, ikq, ikr] eri_kpt = fao2mo( (mo_coeff[ikp], mo_coeff[ikq], mo_coeff[ikr], mo_coeff[iks]), (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]), compact=False) if dtype == np.float: eri_kpt = eri_kpt.real eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) / nkpts done = np.zeros([nkpts, nkpts, nkpts]) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: if done[kp, kq, kr]: continue eri_kpt_symm = khelper.transform_symm(eri_kpt, kp, kq, kr) oooo = eri_kpt_symm[:nocc, :nocc, :nocc, :nocc] ooov = eri_kpt_symm[:nocc, :nocc, :nocc, nocc:] ovov = eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] oovv = eri_kpt_symm[:nocc, :nocc, nocc:, nocc:] ovvo = eri_kpt_symm[:nocc, nocc:, nocc:, :nocc] ovvv = eri_kpt_symm[:nocc, nocc:, nocc:, nocc:] vvvv = eri_kpt_symm[nocc:, nocc:, nocc:, nocc:] ks = kconserv[kp, kq, kr] eia = _get_epq([0, nocc, kp, mo_e_o, nonzero_opadding], [0, nvir, kq, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) ejb = _get_epq([0, nocc, kr, mo_e_o, nonzero_opadding], [0, nvir, ks, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) eijab = eia[:, None, :, None] + ejb[None, :, None, :] self.oooo.array[kp, kq, kr] = oooo self.ooov.array[kp, kq, kr] = ooov self.ovov.array[kp, kq, kr] = ovov self.oovv.array[kp, kq, kr] = oovv self.ovvo.array[kp, kq, kr] = ovvo self.ovvv.array[kp, kq, kr] = ovvv self.vvvv.array[kp, kq, kr] = vvvv self.eijab.array[kp, kr, kq] = eijab done[kp, kq, kr] = 1 log.timer("ao2mo transformation", *cput0)
def get_t3p2_imds_slow(cc, t1, t2, eris=None, t3p2_ip_out=None, t3p2_ea_out=None): """Calculates T1, T2 amplitudes corrected by second-order T3 contribution and intermediates used in IP/EA-CCSD(T)a Args: cc (:obj:`KGCCSD`): Object containing coupled-cluster results. t1 (:obj:`ndarray`): T1 amplitudes. t2 (:obj:`ndarray`): T2 amplitudes from which the T3[2] amplitudes are formed. eris (:obj:`_PhysicistsERIs`): Antisymmetrized electron-repulsion integrals in physicist's notation. t3p2_ip_out (:obj:`ndarray`): Store results of the intermediate used in IP-EOM-CCSD(T)a. t3p2_ea_out (:obj:`ndarray`): Store results of the intermediate used in EA-EOM-CCSD(T)a. Returns: delta_ccsd (float): Difference of perturbed and unperturbed CCSD ground-state energy, energy(T1 + T1[2], T2 + T2[2]) - energy(T1, T2) pt1 (:obj:`ndarray`): Perturbatively corrected T1 amplitudes. pt2 (:obj:`ndarray`): Perturbatively corrected T2 amplitudes. Reference: D. A. Matthews, J. F. Stanton "A new approach to approximate..." JCP 145, 124102 (2016); DOI:10.1063/1.4962910, Equation 14 Shavitt and Bartlett "Many-body Methods in Physics and Chemistry" 2009, Equation 10.33 """ if eris is None: eris = cc.ao2mo() fock = eris.fock nkpts, nocc, nvir = t1.shape kconserv = cc.khelper.kconserv fov = [fock[ikpt, :nocc, nocc:] for ikpt in range(nkpts)] #foo = [fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)] #fvv = [fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)] mo_energy_occ = numpy.array( [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]) mo_energy_vir = numpy.array( [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]) # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split") mo_e_o = mo_energy_occ mo_e_v = mo_energy_vir ccsd_energy = cc.energy(t1, t2, eris) dtype = numpy.result_type(t1, t2) if t3p2_ip_out is None: t3p2_ip_out = numpy.zeros( (nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc), dtype=dtype) Wmcik = t3p2_ip_out if t3p2_ea_out is None: t3p2_ea_out = numpy.zeros( (nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=dtype) Wacek = t3p2_ea_out t3 = get_full_t3p2(cc, t1, t2, eris) pt1 = numpy.zeros((nkpts, nocc, nvir), dtype=dtype) for ki in range(nkpts): ka = ki for km, kn, ke in product(range(nkpts), repeat=3): pt1[ki] += 0.25 * lib.einsum( 'mnef,imnaef->ia', eris.oovv[km, kn, ke], t3[ki, km, kn, ka, ke]) eia = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nvir, ka, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) pt1[ki] /= eia pt2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=dtype) for ki, kj, ka in product(range(nkpts), repeat=3): kb = kconserv[ki, ka, kj] for km in range(nkpts): pt2[ki, kj, ka] += lib.einsum('ijmabe,me->ijab', t3[ki, kj, km, ka, kb], fov[km]) for ke in range(nkpts): kf = kconserv[km, ke, kb] pt2[ki, kj, ka] += 0.5 * lib.einsum( 'ijmaef,mbfe->ijab', t3[ki, kj, km, ka, ke], eris.ovvv[km, kb, kf]) kf = kconserv[km, ke, ka] pt2[ki, kj, ka] -= 0.5 * lib.einsum( 'ijmbef,mafe->ijab', t3[ki, kj, km, kb, ke], eris.ovvv[km, ka, kf]) for kn in range(nkpts): pt2[ki, kj, ka] -= 0.5 * lib.einsum( 'inmabe,nmje->ijab', t3[ki, kn, km, ka, kb], eris.ooov[kn, km, kj]) pt2[ki, kj, ka] += 0.5 * lib.einsum( 'jnmabe,nmie->ijab', t3[kj, kn, km, ka, kb], eris.ooov[kn, km, ki]) eia = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding], [0, nvir, ka, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) ejb = _get_epq([0, nocc, kj, mo_e_o, nonzero_opadding], [0, nvir, kb, mo_e_v, nonzero_vpadding], fac=[1.0, -1.0]) eijab = eia[:, None, :, None] + ejb[:, None, :] pt2[ki, kj, ka] /= eijab pt1 += t1 pt2 += t2 for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5): kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts, [ki, kj, kk, ka, kb]) tmp = t3[ki, kj, kk, ka, kb] km = kconserv[ki, kc, kk] ke = kconserv[ka, kk, kc] Wmcik[km, kc, ki] += 0.5 * lib.einsum('ijkabc,mjab->mcik', tmp, eris.oovv[km, kj, ka]) Wacek[ka, kc, ke] += -0.5 * lib.einsum('ijkabc,ijeb->acek', tmp, eris.oovv[ki, kj, ke]) delta_ccsd_energy = cc.energy(pt1, pt2, eris) - ccsd_energy logger.info(cc, 'CCSD energy T3[2] correction : %14.8e', delta_ccsd_energy) return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
def kernel(mycc, eris, t1=None, t2=None, max_memory=2000, verbose=logger.INFO): '''Returns the CCSD(T) for restricted closed-shell systems with k-points. Note: Returns real part of the CCSD(T) energy, raises warning if there is a complex part. Args: mycc (:class:`RCCSD`): Coupled-cluster object storing results of a coupled-cluster calculation. eris (:class:`_ERIS`): Integral object holding the relevant electron- repulsion integrals and Fock matrix elements t1 (:obj:`ndarray`): t1 coupled-cluster amplitudes t2 (:obj:`ndarray`): t2 coupled-cluster amplitudes max_memory (float): Maximum memory used in calculation (NOT USED) verbose (int, :class:`Logger`): verbosity of calculation Returns: energy_t (float): The real-part of the k-point CCSD(T) energy. ''' assert isinstance(mycc, pyscf.pbc.cc.kccsd_rhf.RCCSD) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mycc.stdout, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if eris is None: raise TypeError('Electron repulsion integrals, `eris`, must be passed in ' 'to the CCSD(T) kernel or created in the cc object for ' 'the k-point CCSD(T) to run!') if t1 is None or t2 is None: raise TypeError('Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe ' 'need to run `.ccsd()` on the ccsd object?)') cell = mycc._scf.cell kpts = mycc.kpts # The dtype of any local arrays that will be created dtype = t1.dtype nkpts, nocc, nvir = t1.shape mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)] mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)] fov = eris.fock[:, :nocc, nocc:] # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) def get_w(ki, kj, kk, ka, kb, kc, a, b, c): '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts''' km = kconserv[ki, ka, kj] kf = kconserv[kk, kc, kj] ret = einsum('kjf,fi->ijk', t2[kk, kj, kc, :, :, c, :], eris.vovv[kf, ki, kb, :, :, b, a].conj()) ret = ret - einsum('mk,jim->ijk', t2[km, kk, kb, :, :, b, c], eris.ooov[kj, ki, km, :, :, :, a].conj()) return ret def get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c): '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper''' ret = get_w(ki, kj, kk, ka, kb, kc, a, b, c) ret = ret + get_w(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) ret = ret + get_w(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) ret = ret + get_w(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) ret = ret + get_w(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) ret = ret + get_w(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) return ret def get_rw(ki, kj, kk, ka, kb, kc, a, b, c): '''R operating on Wijkabc intermediate as described in Scuseria paper''' ret = (4. * get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c) + 1. * get_permuted_w(kk, ki, kj, ka, kb, kc, a, b, c).transpose(1, 2, 0) + 1. * get_permuted_w(kj, kk, ki, ka, kb, kc, a, b, c).transpose(2, 0, 1) - 2. * get_permuted_w(kk, kj, ki, ka, kb, kc, a, b, c).transpose(2, 1, 0) - 2. * get_permuted_w(ki, kk, kj, ka, kb, kc, a, b, c).transpose(0, 2, 1) - 2. * get_permuted_w(kj, ki, kk, ka, kb, kc, a, b, c).transpose(1, 0, 2)) return ret def get_v(ki, kj, kk, ka, kb, kc, a, b, c): '''Vijkabc intermediate as described in Scuseria paper''' km = kconserv[ki, ka, kj] kf = kconserv[ki, ka, kj] ret = np.zeros((nocc, nocc, nocc), dtype=dtype) if kk == kc: ret = ret + einsum('k,ij->ijk', t1[kk, :, c], eris.oovv[ki, kj, ka, :, :, a, b].conj()) ret = ret + einsum('k,ij->ijk', fov[kk, :, c], t2[ki, kj, ka, :, :, a, b]) return ret def get_permuted_v(ki, kj, kk, ka, kb, kc, a, b, c): '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper''' ret = get_v(ki, kj, kk, ka, kb, kc, a, b, c) ret = ret + get_v(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) ret = ret + get_v(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) ret = ret + get_v(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) ret = ret + get_v(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) ret = ret + get_v(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) return ret energy_t = 0.0 # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") for ki in range(nkpts): for kj in range(ki + 1): for kk in range(kj + 1): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = LARGE_DENOM * np.ones((nocc,)*3, dtype=mo_energy_occ[0].dtype) n0_ovp_ijk = np.ix_(nonzero_opadding[ki], nonzero_opadding[kj], nonzero_opadding[kk]) eijk[n0_ovp_ijk] = lib.direct_sum('i,j,k->ijk', mo_energy_occ[ki], mo_energy_occ[kj], mo_energy_occ[kk])[n0_ovp_ijk] for ka in range(nkpts): for kb in range(nkpts): # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) ia_index = ki * nkpts + ka jb_index = kj * nkpts + kb kc_index = kk * nkpts + kc if not (ia_index >= jb_index and jb_index >= kc_index): continue # Factors to include for permutational symmetry among k-points if (ia_index == jb_index and jb_index == kc_index): symm_kpt = 1. # only one unique [ia, jb, kc] index elif (ia_index == jb_index or jb_index == kc_index): symm_kpt = 3. # three unique permutations of [ia, jb, kc] else: symm_kpt = 6. # six unique permutations of [ia, jb, kc] # Determine the a, b, c indices we will loop over as # determined by the k-point symmetry. abc_indices = cartesian_prod([range(nvir)] * 3) symm_3d = symm_2d_ab = symm_2d_bc = False if ia_index == jb_index == kc_index: # ka == kb == kc symm_3d = True abc_indices = tril_product(range(nvir), repeat=3, tril_idx=[0, 1, 2]) # loop a >= b >= c symm_3d = True elif ia_index == jb_index: # ka == kb abc_indices = tril_product(range(nvir), repeat=3, tril_idx=[0, 1]) # loop a >= b symm_2d_ab = True elif jb_index == kc_index: abc_indices = tril_product(range(nvir), repeat=3, tril_idx=[1, 2]) # loop b >= c symm_2d_bc = True for a, b, c in abc_indices: # Form energy denominator # Make sure we only loop over non-frozen and/or padded elements if( not ((a in nonzero_vpadding[ka]) and (b in nonzero_vpadding[kb]) and (c in nonzero_vpadding[kc]))): continue eijkabc = (eijk - mo_energy_vir[ka][a] - mo_energy_vir[kb][b] - mo_energy_vir[kc][c]) # See symm_3d and abc_indices above for description of factors symm_abc = 1. if symm_3d: if a == b == c: symm_abc = 1. elif a == b or b == c: symm_abc = 3. else: symm_abc = 6. elif symm_2d_ab: if a == b: symm_abc = 1. else: symm_abc = 2. elif symm_2d_bc: if b == c: symm_abc = 1. else: symm_abc = 2. # The simplest written algorithm can be accomplished with the following four lines #pwijk = ( get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c) + # 0.5 * get_permuted_v(ki, kj, kk, ka, kb, kc, a, b, c) ) #rwijk = get_rw(ki, kj, kk, ka, kb, kc, a, b, c) / eijkabc #energy_t += symm_fac * einsum('ijk,ijk', pwijk, rwijk.conj()) # Creating permuted W_ijkabc intermediate w_int0 = get_w(ki, kj, kk, ka, kb, kc, a, b, c) w_int1 = get_w(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) w_int2 = get_w(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) w_int3 = get_w(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) w_int4 = get_w(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) w_int5 = get_w(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) # Creating permuted V_ijkabc intermediate v_int0 = get_v(ki, kj, kk, ka, kb, kc, a, b, c) v_int1 = get_v(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) v_int2 = get_v(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) v_int3 = get_v(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) v_int4 = get_v(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) v_int5 = get_v(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) # Creating permuted W_ijkabc + 0.5 * V_ijkabc intermediate pwijk = w_int0 + 0.5 * v_int0 pwijk += w_int1 + 0.5 * v_int1 pwijk += w_int2 + 0.5 * v_int2 pwijk += w_int3 + 0.5 * v_int3 pwijk += w_int4 + 0.5 * v_int4 pwijk += w_int5 + 0.5 * v_int5 # Creating R[W] intermediate rwijk = np.zeros((nocc, nocc, nocc), dtype=dtype) # Adding in contribution 4. * P[(i, j, k) -> (i, j, k)] rwijk += 4. * w_int0 rwijk += 4. * w_int1 rwijk += 4. * w_int2 rwijk += 4. * w_int3 rwijk += 4. * w_int4 rwijk += 4. * w_int5 # Adding in contribution 1. * P[(i, j, k) -> (k, i, j)] rwijk += 1. * get_w(kk, ki, kj, ka, kb, kc, a, b, c).transpose(1, 2, 0) rwijk += 1. * get_w(ki, kj, kk, kb, kc, ka, b, c, a).transpose(2, 0, 1).transpose(1, 2, 0) rwijk += 1. * get_w(kj, kk, ki, kc, ka, kb, c, a, b).transpose(1, 2, 0).transpose(1, 2, 0) rwijk += 1. * get_w(kk, kj, ki, ka, kc, kb, a, c, b).transpose(0, 2, 1).transpose(1, 2, 0) rwijk += 1. * get_w(kj, ki, kk, kc, kb, ka, c, b, a).transpose(2, 1, 0).transpose(1, 2, 0) rwijk += 1. * get_w(ki, kk, kj, kb, ka, kc, b, a, c).transpose(1, 0, 2).transpose(1, 2, 0) # Adding in contribution 1. * P[(i, j, k) -> (j, k, i)] rwijk += 1. * get_w(kj, kk, ki, ka, kb, kc, a, b, c).transpose(2, 0, 1) rwijk += 1. * get_w(kk, ki, kj, kb, kc, ka, b, c, a).transpose(2, 0, 1).transpose(2, 0, 1) rwijk += 1. * get_w(ki, kj, kk, kc, ka, kb, c, a, b).transpose(1, 2, 0).transpose(2, 0, 1) rwijk += 1. * get_w(kj, ki, kk, ka, kc, kb, a, c, b).transpose(0, 2, 1).transpose(2, 0, 1) rwijk += 1. * get_w(ki, kk, kj, kc, kb, ka, c, b, a).transpose(2, 1, 0).transpose(2, 0, 1) rwijk += 1. * get_w(kk, kj, ki, kb, ka, kc, b, a, c).transpose(1, 0, 2).transpose(2, 0, 1) # Adding in contribution -2. * P[(i, j, k) -> (k, j, i)] rwijk += -2. * get_w(kk, kj, ki, ka, kb, kc, a, b, c).transpose(2, 1, 0) rwijk += -2. * get_w(kj, ki, kk, kb, kc, ka, b, c, a).transpose(2, 0, 1).transpose(2, 1, 0) rwijk += -2. * get_w(ki, kk, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0).transpose(2, 1, 0) rwijk += -2. * get_w(kk, ki, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1).transpose(2, 1, 0) rwijk += -2. * get_w(ki, kj, kk, kc, kb, ka, c, b, a).transpose(2, 1, 0).transpose(2, 1, 0) rwijk += -2. * get_w(kj, kk, ki, kb, ka, kc, b, a, c).transpose(1, 0, 2).transpose(2, 1, 0) # Adding in contribution -2. * P[(i, j, k) -> (i, k, j)] rwijk += -2. * get_w(ki, kk, kj, ka, kb, kc, a, b, c).transpose(0, 2, 1) rwijk += -2. * get_w(kk, kj, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1).transpose(0, 2, 1) rwijk += -2. * get_w(kj, ki, kk, kc, ka, kb, c, a, b).transpose(1, 2, 0).transpose(0, 2, 1) rwijk += -2. * get_w(ki, kj, kk, ka, kc, kb, a, c, b).transpose(0, 2, 1).transpose(0, 2, 1) rwijk += -2. * get_w(kj, kk, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0).transpose(0, 2, 1) rwijk += -2. * get_w(kk, ki, kj, kb, ka, kc, b, a, c).transpose(1, 0, 2).transpose(0, 2, 1) # Adding in contribution -2. * P[(i, j, k) -> (j, i, k)] rwijk += -2. * get_w(kj, ki, kk, ka, kb, kc, a, b, c).transpose(1, 0, 2) rwijk += -2. * get_w(ki, kk, kj, kb, kc, ka, b, c, a).transpose(2, 0, 1).transpose(1, 0, 2) rwijk += -2. * get_w(kk, kj, ki, kc, ka, kb, c, a, b).transpose(1, 2, 0).transpose(1, 0, 2) rwijk += -2. * get_w(kj, kk, ki, ka, kc, kb, a, c, b).transpose(0, 2, 1).transpose(1, 0, 2) rwijk += -2. * get_w(kk, ki, kj, kc, kb, ka, c, b, a).transpose(2, 1, 0).transpose(1, 0, 2) rwijk += -2. * get_w(ki, kj, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2).transpose(1, 0, 2) rwijk /= eijkabc energy_t += symm_abc * symm_kpt * einsum('ijk,ijk', pwijk, rwijk.conj()) energy_t *= (1. / 3) energy_t /= nkpts if abs(energy_t.imag) > 1e-4: log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag) log.note('CCSD(T) correction per cell = %.15g', energy_t.real) log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag) return energy_t.real
def __init__(self, cis, mo_coeff=None, method="incore"): log = logger.Logger(cis.stdout, cis.verbose) cput0 = (time.clock(), time.time()) moidx = get_frozen_mask(cis) cell = cis._scf.cell nocc = cis.nocc nmo = cis.nmo nvir = nmo - nocc nkpts = cis.nkpts kpts = cis.kpts if mo_coeff is None: mo_coeff = cis.mo_coeff dtype = mo_coeff[0].dtype mo_coeff = self.mo_coeff = padded_mo_coeff(cis, mo_coeff) # Re-make our fock MO matrix elements from density and fock AO dm = cis._scf.make_rdm1(cis.mo_coeff, cis.mo_occ) exxdiv = cis._scf.exxdiv if cis.keep_exxdiv else None with lib.temporary_env(cis._scf, exxdiv=exxdiv): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. fockao = cis._scf.get_hcore() + cis._scf.get_veff(cell, dm) self.fock = np.asarray([ reduce(np.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(mo_coeff) ]) self.mo_energy = [self.fock[k].diagonal().real for k in range(nkpts)] if not cis.keep_exxdiv: # Add HFX correction in the self.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) self.mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(self.mo_energy) ] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cis, per_kpoint=True) nonzero_padding = padding_k_idx(cis, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [self.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = np.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[np.sum(nocc_per_kpt)] - mo_e[np.sum(nocc_per_kpt) - 1] if gap < 1e-5: logger.warn( cis, "H**O-LUMO gap %s too small for KCCSD. " "May cause issues in convergence.", gap, ) memory_needed = (nkpts**3 * nocc**2 * nvir**2) * 16 / 1e6 # CIS only needs two terms: <aj|ib> and <aj|bi>; another factor of two for safety memory_needed *= 4 memory_now = lib.current_memory()[0] fao2mo = cis._scf.with_df.ao2mo kconserv = cis.khelper.kconserv khelper = cis.khelper if cis.direct and type(cis._scf.with_df) is not df.GDF: raise ValueError("CIS direct method must be used with GDF") if (cis.direct and type(cis._scf.with_df) is df.GDF and cell.dimension != 2): # cis._scf.with_df needs to be df.GDF only (not MDF) _init_cis_df_eris(cis, self) else: if (method == "incore" and (memory_needed + memory_now < cis.max_memory) or cell.incore_anyway): log.info("using incore ERI storage") self.ovov = np.empty( (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype=dtype) self.voov = np.empty( (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype=dtype) for (ikp, ikq, ikr) in khelper.symm_map.keys(): iks = kconserv[ikp, ikq, ikr] eri_kpt = fao2mo( (mo_coeff[ikp], mo_coeff[ikq], mo_coeff[ikr], mo_coeff[iks]), (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]), compact=False, ) if dtype == np.float: eri_kpt = eri_kpt.real eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]: eri_kpt_symm = khelper.transform_symm( eri_kpt, kp, kq, kr).transpose(0, 2, 1, 3) self.ovov[kp, kr, kq] = ( eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] / nkpts) self.voov[kp, kr, kq] = ( eri_kpt_symm[nocc:, :nocc, :nocc, nocc:] / nkpts) self.dtype = dtype else: log.info("using HDF5 ERI storage") self.feri1 = lib.H5TmpFile() self.ovov = self.feri1.create_dataset( "ovov", (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype.char) self.voov = self.feri1.create_dataset( "voov", (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype.char) # <ia|pq> = (ip|aq) cput1 = time.clock(), time.time() for kp in range(nkpts): for kq in range(nkpts): for kr in range(nkpts): ks = kconserv[kp, kq, kr] orbo_p = mo_coeff[kp][:, :nocc] orbv_r = mo_coeff[kr][:, nocc:] buf_kpt = fao2mo( (orbo_p, mo_coeff[kq], orbv_r, mo_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False, ) if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real buf_kpt = buf_kpt.reshape(nocc, nmo, nvir, nmo).transpose( 0, 2, 1, 3) self.dtype = buf_kpt.dtype self.ovov[kp, kr, kq, :, :, :, :] = ( buf_kpt[:, :, :nocc, nocc:] / nkpts) self.voov[kr, kp, ks, :, :, :, :] = ( buf_kpt[:, :, nocc:, :nocc].transpose( 1, 0, 3, 2) / nkpts) cput1 = log.timer_debug1("transforming ovpq", *cput1) log.timer("CIS integral transformation", *cput0)
def kernel(mycc, eris, t1=None, t2=None, max_memory=2000, verbose=logger.INFO): '''Returns the CCSD(T) for restricted closed-shell systems with k-points. Note: Returns real part of the CCSD(T) energy, raises warning if there is a complex part. Args: mycc (:class:`RCCSD`): Coupled-cluster object storing results of a coupled-cluster calculation. eris (:class:`_ERIS`): Integral object holding the relevant electron- repulsion integrals and Fock matrix elements t1 (:obj:`ndarray`): t1 coupled-cluster amplitudes t2 (:obj:`ndarray`): t2 coupled-cluster amplitudes max_memory (float): Maximum memory used in calculation (NOT USED) verbose (int, :class:`Logger`): verbosity of calculation Returns: energy_t (float): The real-part of the k-point CCSD(T) energy. ''' assert isinstance(mycc, pyscf.pbc.cc.kccsd_rhf.RCCSD) if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mycc.stdout, verbose) if t1 is None: t1 = mycc.t1 if t2 is None: t2 = mycc.t2 if eris is None: raise TypeError( 'Electron repulsion integrals, `eris`, must be passed in ' 'to the CCSD(T) kernel or created in the cc object for ' 'the k-point CCSD(T) to run!') if t1 is None or t2 is None: raise TypeError( 'Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe ' 'need to run `.ccsd()` on the ccsd object?)') cell = mycc._scf.cell kpts = mycc.kpts # The dtype of any local arrays that will be created dtype = t1.dtype nkpts, nocc, nvir = t1.shape mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)] mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)] fov = eris.fock[:, :nocc, nocc:] # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) def get_w(ki, kj, kk, ka, kb, kc, a, b, c): '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts''' km = kconserv[ki, ka, kj] kf = kconserv[kk, kc, kj] ret = einsum('kjf,fi->ijk', t2[kk, kj, kc, :, :, c, :], eris.vovv[kf, ki, kb, :, :, b, a].conj()) ret = ret - einsum('mk,jim->ijk', t2[km, kk, kb, :, :, b, c], eris.ooov[kj, ki, km, :, :, :, a].conj()) return ret def get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c): '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper''' ret = get_w(ki, kj, kk, ka, kb, kc, a, b, c) ret = ret + get_w(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) ret = ret + get_w(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) ret = ret + get_w(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) ret = ret + get_w(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) ret = ret + get_w(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) return ret def get_rw(ki, kj, kk, ka, kb, kc, a, b, c): '''R operating on Wijkabc intermediate as described in Scuseria paper''' ret = ( 4. * get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c) + 1. * get_permuted_w(kk, ki, kj, ka, kb, kc, a, b, c).transpose(1, 2, 0) + 1. * get_permuted_w(kj, kk, ki, ka, kb, kc, a, b, c).transpose( 2, 0, 1) - 2. * get_permuted_w(kk, kj, ki, ka, kb, kc, a, b, c).transpose(2, 1, 0) - 2. * get_permuted_w(ki, kk, kj, ka, kb, kc, a, b, c).transpose( 0, 2, 1) - 2. * get_permuted_w(kj, ki, kk, ka, kb, kc, a, b, c).transpose(1, 0, 2)) return ret def get_v(ki, kj, kk, ka, kb, kc, a, b, c): '''Vijkabc intermediate as described in Scuseria paper''' km = kconserv[ki, ka, kj] kf = kconserv[ki, ka, kj] ret = np.zeros((nocc, nocc, nocc), dtype=dtype) if kk == kc: ret = ret + einsum('k,ij->ijk', t1[kk, :, c], eris.oovv[ki, kj, ka, :, :, a, b].conj()) ret = ret + einsum('k,ij->ijk', fov[kk, :, c], t2[ki, kj, ka, :, :, a, b]) return ret def get_permuted_v(ki, kj, kk, ka, kb, kc, a, b, c): '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper''' ret = get_v(ki, kj, kk, ka, kb, kc, a, b, c) ret = ret + get_v(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) ret = ret + get_v(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) ret = ret + get_v(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) ret = ret + get_v(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) ret = ret + get_v(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) return ret energy_t = 0.0 # Get location of padded elements in occupied and virtual space nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split") for ki in range(nkpts): for kj in range(ki + 1): for kk in range(kj + 1): # eigenvalue denominator: e(i) + e(j) + e(k) eijk = LARGE_DENOM * np.ones( (nocc, ) * 3, dtype=mo_energy_occ[0].dtype) n0_ovp_ijk = np.ix_(nonzero_opadding[ki], nonzero_opadding[kj], nonzero_opadding[kk]) eijk[n0_ovp_ijk] = lib.direct_sum( 'i,j,k->ijk', mo_energy_occ[ki], mo_energy_occ[kj], mo_energy_occ[kk])[n0_ovp_ijk] for ka in range(nkpts): for kb in range(nkpts): # Find momentum conservation condition for triples # amplitude t3ijkabc kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb]) ia_index = ki * nkpts + ka jb_index = kj * nkpts + kb kc_index = kk * nkpts + kc if not (ia_index >= jb_index and jb_index >= kc_index): continue # Factors to include for permutational symmetry among k-points if (ia_index == jb_index and jb_index == kc_index): symm_kpt = 1. # only one unique [ia, jb, kc] index elif (ia_index == jb_index or jb_index == kc_index): symm_kpt = 3. # three unique permutations of [ia, jb, kc] else: symm_kpt = 6. # six unique permutations of [ia, jb, kc] # Determine the a, b, c indices we will loop over as # determined by the k-point symmetry. abc_indices = cartesian_prod([range(nvir)] * 3) symm_3d = symm_2d_ab = symm_2d_bc = False if ia_index == jb_index == kc_index: # ka == kb == kc symm_3d = True abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[0, 1, 2]) # loop a >= b >= c symm_3d = True elif ia_index == jb_index: # ka == kb abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[0, 1]) # loop a >= b symm_2d_ab = True elif jb_index == kc_index: abc_indices = tril_product( range(nvir), repeat=3, tril_idx=[1, 2]) # loop b >= c symm_2d_bc = True for a, b, c in abc_indices: # Form energy denominator # Make sure we only loop over non-frozen and/or padded elements if (not ((a in nonzero_vpadding[ka]) and (b in nonzero_vpadding[kb]) and (c in nonzero_vpadding[kc]))): continue eijkabc = (eijk - mo_energy_vir[ka][a] - mo_energy_vir[kb][b] - mo_energy_vir[kc][c]) # See symm_3d and abc_indices above for description of factors symm_abc = 1. if symm_3d: if a == b == c: symm_abc = 1. elif a == b or b == c: symm_abc = 3. else: symm_abc = 6. elif symm_2d_ab: if a == b: symm_abc = 1. else: symm_abc = 2. elif symm_2d_bc: if b == c: symm_abc = 1. else: symm_abc = 2. # The simplest written algorithm can be accomplished with the following four lines #pwijk = ( get_permuted_w(ki, kj, kk, ka, kb, kc, a, b, c) + # 0.5 * get_permuted_v(ki, kj, kk, ka, kb, kc, a, b, c) ) #rwijk = get_rw(ki, kj, kk, ka, kb, kc, a, b, c) / eijkabc #energy_t += symm_fac * einsum('ijk,ijk', pwijk, rwijk.conj()) # Creating permuted W_ijkabc intermediate w_int0 = get_w(ki, kj, kk, ka, kb, kc, a, b, c) w_int1 = get_w(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) w_int2 = get_w(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) w_int3 = get_w(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) w_int4 = get_w(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) w_int5 = get_w(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) # Creating permuted V_ijkabc intermediate v_int0 = get_v(ki, kj, kk, ka, kb, kc, a, b, c) v_int1 = get_v(kj, kk, ki, kb, kc, ka, b, c, a).transpose(2, 0, 1) v_int2 = get_v(kk, ki, kj, kc, ka, kb, c, a, b).transpose(1, 2, 0) v_int3 = get_v(ki, kk, kj, ka, kc, kb, a, c, b).transpose(0, 2, 1) v_int4 = get_v(kk, kj, ki, kc, kb, ka, c, b, a).transpose(2, 1, 0) v_int5 = get_v(kj, ki, kk, kb, ka, kc, b, a, c).transpose(1, 0, 2) # Creating permuted W_ijkabc + 0.5 * V_ijkabc intermediate pwijk = w_int0 + 0.5 * v_int0 pwijk += w_int1 + 0.5 * v_int1 pwijk += w_int2 + 0.5 * v_int2 pwijk += w_int3 + 0.5 * v_int3 pwijk += w_int4 + 0.5 * v_int4 pwijk += w_int5 + 0.5 * v_int5 # Creating R[W] intermediate rwijk = np.zeros((nocc, nocc, nocc), dtype=dtype) # Adding in contribution 4. * P[(i, j, k) -> (i, j, k)] rwijk += 4. * w_int0 rwijk += 4. * w_int1 rwijk += 4. * w_int2 rwijk += 4. * w_int3 rwijk += 4. * w_int4 rwijk += 4. * w_int5 # Adding in contribution 1. * P[(i, j, k) -> (k, i, j)] rwijk += 1. * get_w(kk, ki, kj, ka, kb, kc, a, b, c).transpose(1, 2, 0) rwijk += 1. * get_w( ki, kj, kk, kb, kc, ka, b, c, a).transpose( 2, 0, 1).transpose(1, 2, 0) rwijk += 1. * get_w( kj, kk, ki, kc, ka, kb, c, a, b).transpose( 1, 2, 0).transpose(1, 2, 0) rwijk += 1. * get_w( kk, kj, ki, ka, kc, kb, a, c, b).transpose( 0, 2, 1).transpose(1, 2, 0) rwijk += 1. * get_w( kj, ki, kk, kc, kb, ka, c, b, a).transpose( 2, 1, 0).transpose(1, 2, 0) rwijk += 1. * get_w( ki, kk, kj, kb, ka, kc, b, a, c).transpose( 1, 0, 2).transpose(1, 2, 0) # Adding in contribution 1. * P[(i, j, k) -> (j, k, i)] rwijk += 1. * get_w(kj, kk, ki, ka, kb, kc, a, b, c).transpose(2, 0, 1) rwijk += 1. * get_w( kk, ki, kj, kb, kc, ka, b, c, a).transpose( 2, 0, 1).transpose(2, 0, 1) rwijk += 1. * get_w( ki, kj, kk, kc, ka, kb, c, a, b).transpose( 1, 2, 0).transpose(2, 0, 1) rwijk += 1. * get_w( kj, ki, kk, ka, kc, kb, a, c, b).transpose( 0, 2, 1).transpose(2, 0, 1) rwijk += 1. * get_w( ki, kk, kj, kc, kb, ka, c, b, a).transpose( 2, 1, 0).transpose(2, 0, 1) rwijk += 1. * get_w( kk, kj, ki, kb, ka, kc, b, a, c).transpose( 1, 0, 2).transpose(2, 0, 1) # Adding in contribution -2. * P[(i, j, k) -> (k, j, i)] rwijk += -2. * get_w(kk, kj, ki, ka, kb, kc, a, b, c).transpose(2, 1, 0) rwijk += -2. * get_w( kj, ki, kk, kb, kc, ka, b, c, a).transpose( 2, 0, 1).transpose(2, 1, 0) rwijk += -2. * get_w( ki, kk, kj, kc, ka, kb, c, a, b).transpose( 1, 2, 0).transpose(2, 1, 0) rwijk += -2. * get_w( kk, ki, kj, ka, kc, kb, a, c, b).transpose( 0, 2, 1).transpose(2, 1, 0) rwijk += -2. * get_w( ki, kj, kk, kc, kb, ka, c, b, a).transpose( 2, 1, 0).transpose(2, 1, 0) rwijk += -2. * get_w( kj, kk, ki, kb, ka, kc, b, a, c).transpose( 1, 0, 2).transpose(2, 1, 0) # Adding in contribution -2. * P[(i, j, k) -> (i, k, j)] rwijk += -2. * get_w(ki, kk, kj, ka, kb, kc, a, b, c).transpose(0, 2, 1) rwijk += -2. * get_w( kk, kj, ki, kb, kc, ka, b, c, a).transpose( 2, 0, 1).transpose(0, 2, 1) rwijk += -2. * get_w( kj, ki, kk, kc, ka, kb, c, a, b).transpose( 1, 2, 0).transpose(0, 2, 1) rwijk += -2. * get_w( ki, kj, kk, ka, kc, kb, a, c, b).transpose( 0, 2, 1).transpose(0, 2, 1) rwijk += -2. * get_w( kj, kk, ki, kc, kb, ka, c, b, a).transpose( 2, 1, 0).transpose(0, 2, 1) rwijk += -2. * get_w( kk, ki, kj, kb, ka, kc, b, a, c).transpose( 1, 0, 2).transpose(0, 2, 1) # Adding in contribution -2. * P[(i, j, k) -> (j, i, k)] rwijk += -2. * get_w(kj, ki, kk, ka, kb, kc, a, b, c).transpose(1, 0, 2) rwijk += -2. * get_w( ki, kk, kj, kb, kc, ka, b, c, a).transpose( 2, 0, 1).transpose(1, 0, 2) rwijk += -2. * get_w( kk, kj, ki, kc, ka, kb, c, a, b).transpose( 1, 2, 0).transpose(1, 0, 2) rwijk += -2. * get_w( kj, kk, ki, ka, kc, kb, a, c, b).transpose( 0, 2, 1).transpose(1, 0, 2) rwijk += -2. * get_w( kk, ki, kj, kc, kb, ka, c, b, a).transpose( 2, 1, 0).transpose(1, 0, 2) rwijk += -2. * get_w( ki, kj, kk, kb, ka, kc, b, a, c).transpose( 1, 0, 2).transpose(1, 0, 2) rwijk /= eijkabc energy_t += symm_abc * symm_kpt * einsum( 'ijk,ijk', pwijk, rwijk.conj()) energy_t *= (1. / 3) energy_t /= nkpts if abs(energy_t.imag) > 1e-4: log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag) log.note('CCSD(T) correction per cell = %.15g', energy_t.real) log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag) return energy_t.real