def gen_uniform_grids(cell): """Generate a uniform real-space grid consistent w/ samp thm; see MH (3.19). Args: cell : instance of :class:`Cell` Returns: coords : (ngx*ngy*ngz, 3) ndarray The real-space grid point coordinates. """ ngs = 2 * np.asarray(cell.gs) + 1 qv = cartesian_prod([np.arange(x) for x in ngs]) invN = np.diag(1.0 / ngs) coords = np.dot(qv, np.dot(cell._h, invN).T) return coords
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:] # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) 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 = lib.direct_sum('i,j,k->ijk', mo_energy_occ[ki], mo_energy_occ[kj], mo_energy_occ[kk]) # 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 # 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 - mo_energy_vir[ka][a] - mo_energy_vir[kb][b] - mo_energy_vir[kc][c]) # When padding for non-equal nocc per k-point, some fock elements will be zero idx = np.where(abs(eijkabc) < LOOSE_ZERO_TOL)[0] eijkabc[idx] = LARGE_DENOM # 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 kernel(mycc, eris=None, 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 eris is None: eris = mycc.ao2mo() 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.fock[i].diagonal()[:nocc] for i in range(nkpts)] mo_energy_vir = [eris.fock[i].diagonal()[nocc:] for i in range(nkpts)] fov = eris.fock[:, :nocc, nocc:] # Set up class for k-point conservation kconserv = kpts_helper.get_kconserv(cell, kpts) 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 = lib.direct_sum('i,j,k->ijk', mo_energy_occ[ki], mo_energy_occ[kj], mo_energy_occ[kk]) # 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 # 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 == kc: 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 - mo_energy_vir[ka][a] - mo_energy_vir[kb][b] - mo_energy_vir[kc][c]) # When padding for non-equal nocc per k-point, some fock elements will be zero idx = np.where(abs(eijkabc) < LOOSE_ZERO_TOL)[0] eijkabc[idx] = LARGE_DENOM # 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 generate_task_list(chunk_size, array_size): segs = [range(int(numpy.ceil(array_size[i]*1./chunk_size[i]))) for i in range(len(array_size))] task_id = numpy.array(cartesian_prod(segs)) task_ranges_lower = task_id * numpy.array(chunk_size) task_ranges_upper = numpy.minimum((task_id+1)*numpy.array(chunk_size),array_size) return list(numpy.dstack((task_ranges_lower,task_ranges_upper)))
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 ewald(cell, ew_eta, ew_cut, verbose=logger.NOTE): '''Perform real (R) and reciprocal (G) space Ewald sum for the energy. Formulation of Martin, App. F2. Returns: float The Ewald energy consisting of overlap, self, and G-space sum. See Also: pyscf.pbc.gto.get_ewald_params ''' if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(cell.stdout, verbose) chargs = [cell.atom_charge(i) for i in range(len(cell._atm))] coords = [cell.atom_coord(i) for i in range(len(cell._atm))] ewovrl = 0. # set up real-space lattice indices [-ewcut ... ewcut] ewxrange = range(-ew_cut[0],ew_cut[0]+1) ewyrange = range(-ew_cut[1],ew_cut[1]+1) ewzrange = range(-ew_cut[2],ew_cut[2]+1) ewxyz = cartesian_prod((ewxrange,ewyrange,ewzrange)).T # SLOW = True # if SLOW == True: # ewxyz = ewxyz.T # for ic, (ix, iy, iz) in enumerate(ewxyz): # L = np.einsum('ij,j->i', cell._h, ewxyz[ic]) # # prime in summation to avoid self-interaction in unit cell # if (ix == 0 and iy == 0 and iz == 0): # print "L is", L # for ia in range(cell.natm): # qi = chargs[ia] # ri = coords[ia] # #for ja in range(ia): # for ja in range(cell.natm): # if ja != ia: # qj = chargs[ja] # rj = coords[ja] # r = np.linalg.norm(ri-rj) # ewovrl += qi * qj / r * scipy.special.erfc(ew_eta * r) # else: # for ia in range(cell.natm): # qi = chargs[ia] # ri = coords[ia] # for ja in range(cell.natm): # qj=chargs[ja] # rj=coords[ja] # r=np.linalg.norm(ri-rj+L) # ewovrl += qi * qj / r * scipy.special.erfc(ew_eta * r) # # else: nx = len(ewxrange) ny = len(ewyrange) nz = len(ewzrange) Lall = np.einsum('ij,jk->ik', cell._h, ewxyz).reshape(3,nx,ny,nz) #exclude the point where Lall == 0 Lall[:,ew_cut[0],ew_cut[1],ew_cut[2]] = 1e200 Lall = Lall.reshape(3,nx*ny*nz) Lall = Lall.T for ia in range(cell.natm): qi = chargs[ia] ri = coords[ia] for ja in range(ia): qj = chargs[ja] rj = coords[ja] r = np.linalg.norm(ri-rj) ewovrl += 2 * qi * qj / r * scipy.special.erfc(ew_eta * r) for ia in range(cell.natm): qi = chargs[ia] ri = coords[ia] for ja in range(cell.natm): qj = chargs[ja] rj = coords[ja] r1 = ri-rj + Lall r = np.sqrt(np.einsum('ji,ji->j', r1, r1)) ewovrl += (qi * qj / r * scipy.special.erfc(ew_eta * r)).sum() ewovrl *= 0.5 # last line of Eq. (F.5) in Martin ewself = -1./2. * np.dot(chargs,chargs) * 2 * ew_eta / np.sqrt(np.pi) ewself += -1./2. * np.sum(chargs)**2 * np.pi/(ew_eta**2 * cell.vol) # g-space sum (using g grid) (Eq. (F.6) in Martin, but note errors as below) SI = cell.get_SI() ZSI = np.einsum("i,ij->j", chargs, SI) # Eq. (F.6) in Martin is off by a factor of 2, the # exponent is wrong (8->4) and the square is in the wrong place # # Formula should be # 1/2 * 4\pi / Omega \sum_I \sum_{G\neq 0} |ZS_I(G)|^2 \exp[-|G|^2/4\eta^2] # where # ZS_I(G) = \sum_a Z_a exp (i G.R_a) # See also Eq. (32) of ewald.pdf at # http://www.fisica.uniud.it/~giannozz/public/ewald.pdf coulG = tools.get_coulG(cell) absG2 = np.einsum('gi,gi->g', cell.Gv, cell.Gv) ZSIG2 = np.abs(ZSI)**2 expG2 = np.exp(-absG2/(4*ew_eta**2)) JexpG2 = coulG*expG2 ewgI = np.dot(ZSIG2,JexpG2) ewg = .5*np.sum(ewgI) ewg /= cell.vol #log.debug('Ewald components = %.15g, %.15g, %.15g', ewovrl, ewself, ewg) return ewovrl + ewself + ewg
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