Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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.eris
    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
Exemplo n.º 4
0
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