Ejemplo n.º 1
0
def gf_k(interfaces, parent, ps, qs, kwargs, return_ncalls=False):
    """K-dependent Green's function."""
    ncalls = 0
    result_gf = []
    log = interfaces[0].logger
    log.note("Starting a K-GF calculation n_k = {:d}".format(
        len(interfaces), ))
    ps = np.array(ps)
    qs = np.array(qs)
    for interface in interfaces:
        log.note("Running K-GF @ id(k)={:d} ...".format(len(result_gf)))
        idx = padding_k_idx(interface.cc, kind="joint")[interface.k]
        ps_physical = np.any(ps[:, np.newaxis] == idx[np.newaxis, :], axis=1)
        qs_physical = np.any(qs[:, np.newaxis] == idx[np.newaxis, :], axis=1)
        kwargs.update(
            dict(
                return_ncalls=return_ncalls,
                interface=interface,
                ps=ps[ps_physical],
                qs=qs[qs_physical],
            ))
        result = parent(**kwargs)
        if return_ncalls:
            ncalls += result[1]
            result = result[0]
        result_gf.append(result)

    gfvals = np.array(result_gf)

    if return_ncalls:
        return gfvals, ncalls
    else:
        return gfvals
Ejemplo n.º 2
0
    def init_amps(self, eris):
        time0 = time.clock(), time.time()
        nocc = self.nocc
        nvir = self.nmo - nocc
        nkpts = self.nkpts
        t1 = np.zeros((nkpts, nocc, nvir), dtype=eris.fock.dtype)
        t2 = np.empty((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir),
                      dtype=eris.fock.dtype)
        mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)]
        mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)]

        # Get location of padded elements in occupied and virtual space
        nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split")

        emp2 = 0
        kconserv = self.khelper.kconserv
        touched = np.zeros((nkpts, nkpts, nkpts), dtype=bool)
        for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
            if touched[ki, kj, ka]:
                continue

            kb = kconserv[ki, ka, kj]
            # For discussion of LARGE_DENOM, see t1new update above
            eia = LARGE_DENOM * np.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
            eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]

            ejb = LARGE_DENOM * np.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
            ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb]
            eijab = eia[:, None, :, None] + ejb[:, None, :]

            eris_ijab = eris.oovv[ki, kj, ka]
            eris_ijba = eris.oovv[ki, kj, kb]
            t2[ki, kj, ka] = eris_ijab.conj() / eijab
            woovv = 2 * eris_ijab - eris_ijba.transpose(0, 1, 3, 2)
            emp2 += np.einsum('ijab,ijab', t2[ki, kj, ka], woovv)

            if ka != kb:
                eijba = eijab.transpose(0, 1, 3, 2)
                t2[ki, kj, kb] = eris_ijba.conj() / eijba
                woovv = 2 * eris_ijba - eris_ijab.transpose(0, 1, 3, 2)
                emp2 += np.einsum('ijab,ijab', t2[ki, kj, kb], woovv)

            touched[ki, kj, ka] = touched[ki, kj, kb] = True

        self.emp2 = emp2.real / nkpts
        logger.info(
            self, 'Init t2, MP2 energy (with fock eigenvalue shift) = %.15g',
            self.emp2)
        logger.timer(self, 'init mp2', *time0)
        return self.emp2, t1, t2
Ejemplo n.º 3
0
def iter_12(cc, k):
    """Iterates over IP index slices."""
    o, v = padding_k_idx(cc, kind="split")
    kconserv = cc.khelper.kconserv

    yield (o[k],)

    for ki in range(cc.nkpts):
        for kj in range(cc.nkpts):
            kb = kconserv[ki, k, kj]
            yield (ki,), (kj,), o[ki], o[kj], v[kb]
Ejemplo n.º 4
0
def iter_12(cc, k):
    """Iterates over EA index slices."""
    o, v = padding_k_idx(cc, kind="split")
    kconserv = cc.khelper.kconserv

    yield (v[k], )

    for ki in range(cc.nkpts):
        for ka in range(cc.nkpts):
            kb = kconserv[k, ka, ki]
            yield (ki, ), (ka, ), o[ki], v[ka], v[kb]
Ejemplo n.º 5
0
def iter_12(cc_or_eom, k):
    """Iterates over IP index slices."""
    if isinstance(cc_or_eom, kccsd_rhf.RCCSD):
        cc = cc_or_eom
    else:
        cc = cc_or_eom._cc
    o, v = padding_k_idx(cc, kind="split")
    kconserv = cc.khelper.kconserv

    yield (o[k],)

    for ki in range(cc.nkpts):
        for kj in range(cc.nkpts):
            kb = kconserv[ki, k, kj]
            yield (ki,), (kj,), o[ki], o[kj], v[kb]
Ejemplo n.º 6
0
    def init_amps(self, eris):
        time0 = time.clock(), time.time()
        nocc = self.nocc
        nvir = self.nmo - nocc
        nkpts = self.nkpts
        mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)]
        mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)]
        t1 = numpy.zeros((nkpts, nocc, nvir), dtype=numpy.complex128)
        t2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir),
                         dtype=numpy.complex128)
        self.emp2 = 0
        foo = eris.fock[:, :nocc, :nocc].copy()
        fvv = eris.fock[:, nocc:, nocc:].copy()
        fov = eris.fock[:, :nocc, nocc:].copy()
        eris_oovv = eris.oovv.copy()

        # Get location of padded elements in occupied and virtual space
        nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split")

        kconserv = kpts_helper.get_kconserv(self._scf.cell, self.kpts)
        for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
            kb = kconserv[ki, ka, kj]
            # For LARGE_DENOM, see t1new update above
            eia = LARGE_DENOM * numpy.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
            eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]

            ejb = LARGE_DENOM * numpy.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
            ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb]
            eijab = eia[:, None, :, None] + ejb[:, None, :]

            t2[ki, kj, ka] = eris_oovv[ki, kj, ka] / eijab

        t2 = numpy.conj(t2)
        self.emp2 = 0.25 * numpy.einsum('pqrijab,pqrijab', t2, eris_oovv).real
        self.emp2 /= nkpts

        logger.info(self, 'Init t2, MP2 energy = %.15g', self.emp2.real)
        logger.timer(self, 'init mp2', *time0)
        return self.emp2, t1, t2
Ejemplo n.º 7
0
def mask_frozen(cc, vector, k, const=LARGE_DENOM):
    '''Replaces all frozen orbital indices of `vector` with the value `const`.'''
    r1, r2 = vector_to_amplitudes(cc, vector, k)
    nkpts, nocc, nvir = cc.t1.shape
    kconserv = cc.khelper.kconserv

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    new_r1 = const * np.ones_like(r1)
    new_r2 = const * np.ones_like(r2)

    new_r1[nonzero_opadding[k]] = r1[nonzero_opadding[k]]
    for ki in range(nkpts):
        for kj in range(nkpts):
            kb = kconserv[ki, k, kj]
            idx = np.ix_([ki], [kj], nonzero_opadding[ki], nonzero_opadding[kj], nonzero_vpadding[kb])
            new_r2[idx] = r2[idx]

    return amplitudes_to_vector(cc, new_r1, new_r2, k)
Ejemplo n.º 8
0
    def init_amps(self, eris):
        time0 = time.clock(), time.time()
        nocc = self.nocc
        nvir = self.nmo - nocc
        nkpts = self.nkpts
        mo_e_o = [eris.mo_energy[k][:nocc] for k in range(nkpts)]
        mo_e_v = [eris.mo_energy[k][nocc:] for k in range(nkpts)]
        t1 = numpy.zeros((nkpts, nocc, nvir), dtype=numpy.complex128)
        t2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=numpy.complex128)
        self.emp2 = 0
        foo = eris.fock[:, :nocc, :nocc].copy()
        fvv = eris.fock[:, nocc:, nocc:].copy()
        fov = eris.fock[:, :nocc, nocc:].copy()
        eris_oovv = eris.oovv.copy()

        # Get location of padded elements in occupied and virtual space
        nonzero_opadding, nonzero_vpadding = padding_k_idx(self, kind="split")

        kconserv = kpts_helper.get_kconserv(self._scf.cell, self.kpts)
        for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
            kb = kconserv[ki, ka, kj]
            # For LARGE_DENOM, see t1new update above
            eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
            eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia]

            ejb = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
            ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb]
            eijab = eia[:, None, :, None] + ejb[:, None, :]

            t2[ki, kj, ka] = eris_oovv[ki, kj, ka] / eijab

        t2 = numpy.conj(t2)
        self.emp2 = 0.25 * numpy.einsum('pqrijab,pqrijab', t2, eris_oovv).real
        self.emp2 /= nkpts

        logger.info(self, 'Init t2, MP2 energy = %.15g', self.emp2.real)
        logger.timer(self, 'init mp2', *time0)
        return self.emp2, t1, t2
Ejemplo n.º 9
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:]

    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    # Set up class for k-point conservation
    kconserv = kpts_helper.get_kconserv(cell, kpts)

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split")

    energy_t = 0.0

    for ki in range(nkpts):
        for kj in range(ki + 1):
            for kk in range(kj + 1):
                # eigenvalue denominator: e(i) + e(j) + e(k)
                eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding],
                                 [0, nocc, kj, mo_e_o, nonzero_opadding],
                                 [0, nocc, kk, mo_e_o, nonzero_opadding])

                # Factors to include for permutational symmetry among k-points for occupied space
                if ki == kj and kj == kk:
                    symm_ijk_kpt = 1.  # only one degeneracy
                elif ki == kj or kj == kk:
                    symm_ijk_kpt = 3.  # 3 degeneracies when only one k-point is unique
                else:
                    symm_ijk_kpt = 6.  # 3! combinations of arranging 3 distinct k-points

                for ka in range(nkpts):
                    for kb in range(ka + 1):

                        # Find momentum conservation condition for triples
                        # amplitude t3ijkabc
                        kc = kpts_helper.get_kconserv3(cell, kpts,
                                                       [ki, kj, kk, ka, kb])
                        if kc not in range(kb + 1):
                            continue

                        # -1.0 so the LARGE_DENOM does not cancel with the one from eijk
                        eabc = _get_epqr(
                            [0, nvir, ka, mo_e_v, nonzero_vpadding],
                            [0, nvir, kb, mo_e_v, nonzero_vpadding],
                            [0, nvir, kc, mo_e_v, nonzero_vpadding],
                            fac=[-1., -1., -1.])

                        # Factors to include for permutational symmetry among k-points for virtual space
                        if ka == kb and kb == kc:
                            symm_abc_kpt = 1.  # one unique combination of (ka, kb, kc)
                        elif ka == kb or kb == kc:
                            symm_abc_kpt = 3.  # 3 unique combinations of (ka, kb, kc)
                        else:
                            symm_abc_kpt = 6.  # 6 unique combinations of (ka, kb, kc)

                        # Determine the a, b, c indices we will loop over as
                        # determined by the k-point symmetry.
                        abc_indices = cartesian_prod([range(nvir)] * 3)
                        symm_3d = symm_2d_ab = symm_2d_bc = False
                        if ka == kc:  # == kb from lower triangular summation
                            abc_indices = tril_product(
                                range(nvir), repeat=3,
                                tril_idx=[0, 1, 2])  # loop a >= b >= c
                            symm_3d = True
                        elif ka == kb:
                            abc_indices = tril_product(
                                range(nvir), repeat=3,
                                tril_idx=[0, 1])  # loop a >= b
                            symm_2d_ab = True
                        elif kb == kc:
                            abc_indices = tril_product(
                                range(nvir), repeat=3,
                                tril_idx=[1, 2])  # loop b >= c
                            symm_2d_bc = True

                        for a, b, c in abc_indices:
                            # See symm_3d and abc_indices above for description of factors
                            symm_abc = 1.
                            if symm_3d:
                                if a == b == c:
                                    symm_abc = 1.
                                elif a == b or b == c:
                                    symm_abc = 3.
                                else:
                                    symm_abc = 6.
                            elif symm_2d_ab:
                                if a == b:
                                    symm_abc = 1.
                                else:
                                    symm_abc = 2.
                            elif symm_2d_bc:
                                if b == c:
                                    symm_abc = 1.
                                else:
                                    symm_abc = 2.

                            # Form energy denominator
                            eijkabc = (eijk[:, :, :] + eabc[a, b, c])

                            # Form connected triple excitation amplitude
                            t3c = np.zeros((nocc, nocc, nocc), dtype=dtype)

                            # First term: 1 - p(ij) - p(ik)
                            ke = kconserv[kj, ka, kk]
                            t3c = t3c + einsum(
                                'jke,ie->ijk', t2[kj, kk, ka, :, :, a, :],
                                -eris.ovvv[ki, ke, kc, :, :, c, b].conj())
                            ke = kconserv[ki, ka, kk]
                            t3c = t3c - einsum(
                                'ike,je->ijk', t2[ki, kk, ka, :, :, a, :],
                                -eris.ovvv[kj, ke, kc, :, :, c, b].conj())
                            ke = kconserv[kj, ka, ki]
                            t3c = t3c - einsum(
                                'jie,ke->ijk', t2[kj, ki, ka, :, :, a, :],
                                -eris.ovvv[kk, ke, kc, :, :, c, b].conj())

                            km = kconserv[kb, ki, kc]
                            t3c = t3c - einsum(
                                'mi,jkm->ijk', t2[km, ki, kb, :, :, b, c],
                                eris.ooov[kj, kk, km, :, :, :, a].conj())
                            km = kconserv[kb, kj, kc]
                            t3c = t3c + einsum(
                                'mj,ikm->ijk', t2[km, kj, kb, :, :, b, c],
                                eris.ooov[ki, kk, km, :, :, :, a].conj())
                            km = kconserv[kb, kk, kc]
                            t3c = t3c + einsum(
                                'mk,jim->ijk', t2[km, kk, kb, :, :, b, c],
                                eris.ooov[kj, ki, km, :, :, :, a].conj())

                            # Second term: - p(ab) + p(ab) p(ij) + p(ab) p(ik)
                            ke = kconserv[kj, kb, kk]
                            t3c = t3c - einsum(
                                'jke,ie->ijk', t2[kj, kk, kb, :, :, b, :],
                                -eris.ovvv[ki, ke, kc, :, :, c, a].conj())
                            ke = kconserv[ki, kb, kk]
                            t3c = t3c + einsum(
                                'ike,je->ijk', t2[ki, kk, kb, :, :, b, :],
                                -eris.ovvv[kj, ke, kc, :, :, c, a].conj())
                            ke = kconserv[kj, kb, ki]
                            t3c = t3c + einsum(
                                'jie,ke->ijk', t2[kj, ki, kb, :, :, b, :],
                                -eris.ovvv[kk, ke, kc, :, :, c, a].conj())

                            km = kconserv[ka, ki, kc]
                            t3c = t3c + einsum(
                                'mi,jkm->ijk', t2[km, ki, ka, :, :, a, c],
                                eris.ooov[kj, kk, km, :, :, :, b].conj())
                            km = kconserv[ka, kj, kc]
                            t3c = t3c - einsum(
                                'mj,ikm->ijk', t2[km, kj, ka, :, :, a, c],
                                eris.ooov[ki, kk, km, :, :, :, b].conj())
                            km = kconserv[ka, kk, kc]
                            t3c = t3c - einsum(
                                'mk,jim->ijk', t2[km, kk, ka, :, :, a, c],
                                eris.ooov[kj, ki, km, :, :, :, b].conj())

                            # Third term: - p(ac) + p(ac) p(ij) + p(ac) p(ik)
                            ke = kconserv[kj, kc, kk]
                            t3c = t3c - einsum(
                                'jke,ie->ijk', t2[kj, kk, kc, :, :, c, :],
                                -eris.ovvv[ki, ke, ka, :, :, a, b].conj())
                            ke = kconserv[ki, kc, kk]
                            t3c = t3c + einsum(
                                'ike,je->ijk', t2[ki, kk, kc, :, :, c, :],
                                -eris.ovvv[kj, ke, ka, :, :, a, b].conj())
                            ke = kconserv[kj, kc, ki]
                            t3c = t3c + einsum(
                                'jie,ke->ijk', t2[kj, ki, kc, :, :, c, :],
                                -eris.ovvv[kk, ke, ka, :, :, a, b].conj())

                            km = kconserv[kb, ki, ka]
                            t3c = t3c + einsum(
                                'mi,jkm->ijk', t2[km, ki, kb, :, :, b, a],
                                eris.ooov[kj, kk, km, :, :, :, c].conj())
                            km = kconserv[kb, kj, ka]
                            t3c = t3c - einsum(
                                'mj,ikm->ijk', t2[km, kj, kb, :, :, b, a],
                                eris.ooov[ki, kk, km, :, :, :, c].conj())
                            km = kconserv[kb, kk, ka]
                            t3c = t3c - einsum(
                                'mk,jim->ijk', t2[km, kk, kb, :, :, b, a],
                                eris.ooov[kj, ki, km, :, :, :, c].conj())

                            # Form disconnected triple excitation amplitude contribution
                            t3d = np.zeros((nocc, nocc, nocc), dtype=dtype)

                            # First term: 1 - p(ij) - p(ik)
                            if ki == ka:
                                t3d = t3d + einsum(
                                    'i,jk->ijk', t1[ki, :, a],
                                    -eris.oovv[kj, kk, kb, :, :, b, c].conj())
                                t3d = t3d + einsum('i,jk->ijk', -fov[ki, :, a],
                                                   t2[kj, kk, kb, :, :, b, c])

                            if kj == ka:
                                t3d = t3d - einsum(
                                    'j,ik->ijk', t1[kj, :, a],
                                    -eris.oovv[ki, kk, kb, :, :, b, c].conj())
                                t3d = t3d - einsum('j,ik->ijk', -fov[kj, :, a],
                                                   t2[ki, kk, kb, :, :, b, c])

                            if kk == ka:
                                t3d = t3d - einsum(
                                    'k,ji->ijk', t1[kk, :, a],
                                    -eris.oovv[kj, ki, kb, :, :, b, c].conj())
                                t3d = t3d - einsum('k,ji->ijk', -fov[kk, :, a],
                                                   t2[kj, ki, kb, :, :, b, c])

                            # Second term: - p(ab) + p(ab) p(ij) + p(ab) p(ik)
                            if ki == kb:
                                t3d = t3d - einsum(
                                    'i,jk->ijk', t1[ki, :, b],
                                    -eris.oovv[kj, kk, ka, :, :, a, c].conj())
                                t3d = t3d - einsum('i,jk->ijk', -fov[ki, :, b],
                                                   t2[kj, kk, ka, :, :, a, c])

                            if kj == kb:
                                t3d = t3d + einsum(
                                    'j,ik->ijk', t1[kj, :, b],
                                    -eris.oovv[ki, kk, ka, :, :, a, c].conj())
                                t3d = t3d + einsum('j,ik->ijk', -fov[kj, :, b],
                                                   t2[ki, kk, ka, :, :, a, c])

                            if kk == kb:
                                t3d = t3d + einsum(
                                    'k,ji->ijk', t1[kk, :, b],
                                    -eris.oovv[kj, ki, ka, :, :, a, c].conj())
                                t3d = t3d + einsum('k,ji->ijk', -fov[kk, :, b],
                                                   t2[kj, ki, ka, :, :, a, c])

                            # Third term: - p(ac) + p(ac) p(ij) + p(ac) p(ik)
                            if ki == kc:
                                t3d = t3d - einsum(
                                    'i,jk->ijk', t1[ki, :, c],
                                    -eris.oovv[kj, kk, kb, :, :, b, a].conj())
                                t3d = t3d - einsum('i,jk->ijk', -fov[ki, :, c],
                                                   t2[kj, kk, kb, :, :, b, a])

                            if kj == kc:
                                t3d = t3d + einsum(
                                    'j,ik->ijk', t1[kj, :, c],
                                    -eris.oovv[ki, kk, kb, :, :, b, a].conj())
                                t3d = t3d + einsum('j,ik->ijk', -fov[kj, :, c],
                                                   t2[ki, kk, kb, :, :, b, a])

                            if kk == kc:
                                t3d = t3d + einsum(
                                    'k,ji->ijk', t1[kk, :, c],
                                    -eris.oovv[kj, ki, kb, :, :, b, a].conj())
                                t3d = t3d + einsum('k,ji->ijk', -fov[kk, :, c],
                                                   t2[kj, ki, kb, :, :, b, a])

                            t3c_plus_d = t3c + t3d
                            t3c_plus_d /= eijkabc

                            energy_t += symm_abc_kpt * symm_ijk_kpt * symm_abc * einsum(
                                'ijk,ijk', t3c, t3c_plus_d.conj())

    energy_t = (1. / 36) * energy_t / nkpts

    if abs(energy_t.imag) > 1e-4:
        log.warn('Non-zero imaginary part of CCSD(T) energy was found %s',
                 energy_t.imag)
    log.note('CCSD(T) correction per cell = %.15g', energy_t.real)
    return energy_t.real
Ejemplo n.º 10
0
 def get_padding_k_idx(self, cc):
     return padding_k_idx(cc, kind='split')
Ejemplo n.º 11
0
def get_t3p2_imds_slow(cc,
                       t1,
                       t2,
                       eris=None,
                       t3p2_ip_out=None,
                       t3p2_ea_out=None):
    """For a description of arguments, see `get_t3p2_imds_slow` in
    the corresponding `kintermediates.py`.
    """
    from pyscf.pbc.cc.kccsd_t_rhf import _get_epqr
    if eris is None:
        eris = cc.ao2mo()
    fock = eris.fock
    nkpts, nocc, nvir = t1.shape
    kconserv = cc.khelper.kconserv
    dtype = np.result_type(t1, t2)

    fov = fock[:, :nocc, nocc:]
    #foo = [fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)]
    #fvv = [fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)]
    mo_energy_occ = np.array(
        [eris.mo_energy[ki][:nocc] for ki in range(nkpts)])
    mo_energy_vir = np.array(
        [eris.mo_energy[ki][nocc:] for ki in range(nkpts)])

    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    ccsd_energy = cc.energy(t1, t2, eris)

    if t3p2_ip_out is None:
        t3p2_ip_out = np.zeros((nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc),
                               dtype=dtype)
    Wmcik = t3p2_ip_out

    if t3p2_ea_out is None:
        t3p2_ea_out = np.zeros((nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc),
                               dtype=dtype)
    Wacek = t3p2_ea_out

    from itertools import product
    tmp_t3 = np.empty((nkpts, nkpts, nkpts, nkpts, nkpts, nocc, nocc, nocc,
                       nvir, nvir, nvir),
                      dtype=t2.dtype)

    def get_w(ki, kj, kk, ka, kb, kc):
        kf = kconserv[ka, ki, kb]
        ret = lib.einsum('fiba,kjcf->ijkabc', eris.vovv[kf, ki, kb].conj(),
                         t2[kk, kj, kc])
        km = kconserv[kc, kk, kb]
        ret -= lib.einsum('jima,mkbc->ijkabc', eris.ooov[kj, ki, km].conj(),
                          t2[km, kk, kb])
        return ret

    for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5):
        kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts,
                                       [ki, kj, kk, ka, kb])
        tmp_t3[ki, kj, kk, ka, kb] = get_w(ki, kj, kk, ka, kb, kc)
        tmp_t3[ki, kj, kk, ka, kb] += get_w(ki, kk, kj, ka, kc,
                                            kb).transpose(0, 2, 1, 3, 5, 4)
        tmp_t3[ki, kj, kk, ka, kb] += get_w(kj, ki, kk, kb, ka,
                                            kc).transpose(1, 0, 2, 4, 3, 5)
        tmp_t3[ki, kj, kk, ka, kb] += get_w(kj, kk, ki, kb, kc,
                                            ka).transpose(2, 0, 1, 5, 3, 4)
        tmp_t3[ki, kj, kk, ka, kb] += get_w(kk, ki, kj, kc, ka,
                                            kb).transpose(1, 2, 0, 4, 5, 3)
        tmp_t3[ki, kj, kk, ka, kb] += get_w(kk, kj, ki, kc, kb,
                                            ka).transpose(2, 1, 0, 5, 4, 3)

        eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding],
                         [0, nocc, kj, mo_e_o, nonzero_opadding],
                         [0, nocc, kk, mo_e_o, nonzero_opadding])
        eabc = _get_epqr([0, nvir, ka, mo_e_v, nonzero_vpadding],
                         [0, nvir, kb, mo_e_v, nonzero_vpadding],
                         [0, nvir, kc, mo_e_v, nonzero_vpadding],
                         fac=[-1., -1., -1.])
        eijkabc = eijk[:, :, :, None, None, None] + eabc[None, None,
                                                         None, :, :, :]
        tmp_t3[ki, kj, kk, ka, kb] /= eijkabc

    pt1 = np.zeros((nkpts, nocc, nvir), dtype=t2.dtype)
    for ki in range(nkpts):
        for km, kn, ke in product(range(nkpts), repeat=3):
            kf = kconserv[km, ke, kn]
            Soovv = 2. * eris.oovv[km, kn, ke] - eris.oovv[km, kn,
                                                           kf].transpose(
                                                               0, 1, 3, 2)
            St3 = (tmp_t3[ki, km, kn, ki, ke] -
                   tmp_t3[ki, km, kn, ke, ki].transpose(0, 1, 2, 4, 3, 5))
            pt1[ki] += lib.einsum('mnef,imnaef->ia', Soovv, St3)

    pt2 = np.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir),
                   dtype=t2.dtype)
    for ki, kj, ka in product(range(nkpts), repeat=3):
        kb = kconserv[ki, ka, kj]
        for km in range(nkpts):
            for kn in range(nkpts):
                # (ia,jb) -> (ia,jb)
                ke = kconserv[km, kj, kn]
                pt2[ki, kj, ka] += -2. * lib.einsum(
                    'imnabe,mnje->ijab', tmp_t3[ki, km, kn, ka,
                                                kb], eris.ooov[km, kn, kj])
                pt2[ki, kj, ka] += lib.einsum('imnabe,nmje->ijab',
                                              tmp_t3[ki, km, kn, ka, kb],
                                              eris.ooov[kn, km, kj])
                pt2[ki, kj, ka] += lib.einsum('inmeab,mnje->ijab',
                                              tmp_t3[ki, kn, km, ke, ka],
                                              eris.ooov[km, kn, kj])

                # (ia,jb) -> (jb,ia)
                ke = kconserv[km, ki, kn]
                pt2[ki, kj, ka] += -2. * lib.einsum(
                    'jmnbae,mnie->ijab', tmp_t3[kj, km, kn, kb,
                                                ka], eris.ooov[km, kn, ki])
                pt2[ki, kj, ka] += lib.einsum('jmnbae,nmie->ijab',
                                              tmp_t3[kj, km, kn, kb, ka],
                                              eris.ooov[kn, km, ki])
                pt2[ki, kj, ka] += lib.einsum('jnmeba,mnie->ijab',
                                              tmp_t3[kj, kn, km, ke, kb],
                                              eris.ooov[km, kn, ki])

            # (ia,jb) -> (ia,jb)
            pt2[ki, kj, ka] += lib.einsum('ijmabe,me->ijab',
                                          tmp_t3[ki, kj, km, ka, kb], fov[km])
            pt2[ki, kj, ka] -= lib.einsum('ijmaeb,me->ijab',
                                          tmp_t3[ki, kj, km, ka, km], fov[km])

            # (ia,jb) -> (jb,ia)
            pt2[ki, kj, ka] += lib.einsum('jimbae,me->ijab',
                                          tmp_t3[kj, ki, km, kb, ka], fov[km])
            pt2[ki, kj, ka] -= lib.einsum('jimbea,me->ijab',
                                          tmp_t3[kj, ki, km, kb, km], fov[km])

            for ke in range(nkpts):
                # (ia,jb) -> (ia,jb)
                kf = kconserv[km, ke, kb]
                pt2[ki, kj, ka] += 2. * lib.einsum(
                    'ijmaef,bmef->ijab', tmp_t3[ki, kj, km, ka,
                                                ke], eris.vovv[kb, km, ke])
                pt2[ki, kj, ka] -= lib.einsum('ijmaef,bmfe->ijab',
                                              tmp_t3[ki, kj, km, ka, ke],
                                              eris.vovv[kb, km, kf])
                pt2[ki, kj, ka] -= lib.einsum('imjfae,bmef->ijab',
                                              tmp_t3[ki, km, kj, kf, ka],
                                              eris.vovv[kb, km, ke])

                # (ia,jb) -> (jb,ia)
                kf = kconserv[km, ke, ka]
                pt2[ki, kj, ka] += 2. * lib.einsum(
                    'jimbef,amef->ijab', tmp_t3[kj, ki, km, kb,
                                                ke], eris.vovv[ka, km, ke])
                pt2[ki, kj, ka] -= lib.einsum('jimbef,amfe->ijab',
                                              tmp_t3[kj, ki, km, kb, ke],
                                              eris.vovv[ka, km, kf])
                pt2[ki, kj, ka] -= lib.einsum('jmifbe,amef->ijab',
                                              tmp_t3[kj, km, ki, kf, kb],
                                              eris.vovv[ka, km, ke])

    for ki in range(nkpts):
        ka = ki
        eia = LARGE_DENOM * np.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]
        pt1[ki] /= eia

    for ki, ka in product(range(nkpts), repeat=2):
        eia = LARGE_DENOM * np.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]
        for kj in range(nkpts):
            kb = kconserv[ki, ka, kj]
            ejb = LARGE_DENOM * np.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
            ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb]
            eijab = eia[:, None, :, None] + ejb[:, None, :]
            pt2[ki, kj, ka] /= eijab

    pt1 += t1
    pt2 += t2

    for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5):
        kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts,
                                       [ki, kj, kk, ka, kb])
        km = kconserv[kc, ki, ka]

        _oovv = eris.oovv[km, ki, kc]
        Wmcik[km, kb, kk] += 2. * lib.einsum('ijkabc,mica->mbkj',
                                             tmp_t3[ki, kj, kk, ka, kb], _oovv)
        Wmcik[km, kb, kk] -= lib.einsum('jikabc,mica->mbkj',
                                        tmp_t3[kj, ki, kk, ka, kb], _oovv)
        Wmcik[km, kb, kk] -= lib.einsum('kjiabc,mica->mbkj',
                                        tmp_t3[kk, kj, ki, ka, kb], _oovv)

    for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5):
        kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts,
                                       [ki, kj, kk, ka, kb])
        ke = kconserv[ki, ka, kk]

        _oovv = eris.oovv[ki, kk, ka]
        Wacek[kc, kb, ke] -= 2. * lib.einsum('ijkabc,ikae->cbej',
                                             tmp_t3[ki, kj, kk, ka, kb], _oovv)
        Wacek[kc, kb, ke] += lib.einsum('jikabc,ikae->cbej',
                                        tmp_t3[kj, ki, kk, ka, kb], _oovv)
        Wacek[kc, kb, ke] += lib.einsum('kjiabc,ikae->cbej',
                                        tmp_t3[kk, kj, ki, ka, kb], _oovv)

    delta_ccsd_energy = cc.energy(pt1, pt2, eris) - ccsd_energy
    lib.logger.info(cc, 'CCSD energy T3[2] correction : %16.12e',
                    delta_ccsd_energy)

    return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
Ejemplo n.º 12
0
def update_amps(cc, t1, t2, eris):
    time0 = time1 = time.clock(), time.time()
    log = logger.Logger(cc.stdout, cc.verbose)
    nkpts, nocc, nvir = t1.shape
    fock = eris.fock
    mo_e_o = [e[:nocc] for e in eris.mo_energy]
    mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy]

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    fov = fock[:, :nocc, nocc:]
    foo = fock[:, :nocc, :nocc]
    fvv = fock[:, nocc:, nocc:]

    kconserv = cc.khelper.kconserv

    Foo = imdk.cc_Foo(t1, t2, eris, kconserv)
    Fvv = imdk.cc_Fvv(t1, t2, eris, kconserv)
    Fov = imdk.cc_Fov(t1, t2, eris, kconserv)
    Loo = imdk.Loo(t1, t2, eris, kconserv)
    Lvv = imdk.Lvv(t1, t2, eris, kconserv)

    # Move energy terms to the other side
    for k in range(nkpts):
        Foo[k][np.diag_indices(nocc)] -= mo_e_o[k]
        Fvv[k][np.diag_indices(nvir)] -= mo_e_v[k]
        Loo[k][np.diag_indices(nocc)] -= mo_e_o[k]
        Lvv[k][np.diag_indices(nvir)] -= mo_e_v[k]
    time1 = log.timer_debug1('intermediates', *time1)

    # T1 equation
    t1new = np.array(fov).astype(t1.dtype).conj()

    for ka in range(nkpts):
        ki = ka
        # kc == ki; kk == ka
        t1new[ka] += -2. * einsum('kc,ka,ic->ia', fov[ki], t1[ka], t1[ki])
        t1new[ka] += einsum('ac,ic->ia', Fvv[ka], t1[ki])
        t1new[ka] += -einsum('ki,ka->ia', Foo[ki], t1[ka])

        tau_term = np.empty((nkpts, nocc, nocc, nvir, nvir), dtype=t1.dtype)
        for kk in range(nkpts):
            tau_term[kk] = 2 * t2[kk, ki, kk] - t2[ki, kk, kk].transpose(1, 0, 2, 3)
        tau_term[ka] += einsum('ic,ka->kica', t1[ki], t1[ka])

        for kk in range(nkpts):
            kc = kk
            t1new[ka] += einsum('kc,kica->ia', Fov[kc], tau_term[kk])

            t1new[ka] += einsum('akic,kc->ia', 2 * eris.voov[ka, kk, ki], t1[kc])
            t1new[ka] += einsum('kaic,kc->ia', -eris.ovov[kk, ka, ki], t1[kc])

            for kc in range(nkpts):
                kd = kconserv[ka, kc, kk]

                Svovv = 2 * eris.vovv[ka, kk, kc] - eris.vovv[ka, kk, kd].transpose(0, 1, 3, 2)
                tau_term_1 = t2[ki, kk, kc].copy()
                if ki == kc and kk == kd:
                    tau_term_1 += einsum('ic,kd->ikcd', t1[ki], t1[kk])
                t1new[ka] += einsum('akcd,ikcd->ia', Svovv, tau_term_1)

                # kk - ki + kl = kc
                #  => kl = ki - kk + kc
                kl = kconserv[ki, kk, kc]
                Sooov = 2 * eris.ooov[kk, kl, ki] - eris.ooov[kl, kk, ki].transpose(1, 0, 2, 3)
                tau_term_1 = t2[kk, kl, ka].copy()
                if kk == ka and kl == kc:
                    tau_term_1 += einsum('ka,lc->klac', t1[ka], t1[kc])
                t1new[ka] += -einsum('klic,klac->ia', Sooov, tau_term_1)
    time1 = log.timer_debug1('t1', *time1)

    # T2 equation
    t2new = np.empty_like(t2)
    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        t2new[ki, kj, ka] = eris.oovv[ki, kj, ka].conj()

    mem_now = lib.current_memory()[0]
    if (nocc ** 4 * nkpts ** 3) * 16 / 1e6 + mem_now < cc.max_memory * .9:
        Woooo = imdk.cc_Woooo(t1, t2, eris, kconserv)
    else:
        fimd = lib.H5TmpFile()
        Woooo = fimd.create_dataset('oooo', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nocc), t1.dtype.char)
        Woooo = imdk.cc_Woooo(t1, t2, eris, kconserv, Woooo)

    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        # Chemist's notation for momentum conserving t2(ki,kj,ka,kb)
        kb = kconserv[ki, ka, kj]

        t2new_tmp = np.zeros((nocc, nocc, nvir, nvir), dtype=t2.dtype)
        for kl in range(nkpts):
            kk = kconserv[kj, kl, ki]
            tau_term = t2[kk, kl, ka].copy()
            if kl == kb and kk == ka:
                tau_term += einsum('ic,jd->ijcd', t1[ka], t1[kb])
            t2new_tmp += 0.5 * einsum('klij,klab->ijab', Woooo[kk, kl, ki], tau_term)
        t2new[ki, kj, ka] += t2new_tmp
        t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2)
    Woooo = None
    fimd = None
    time1 = log.timer_debug1('t2 oooo', *time1)

    # einsum('abcd,ijcd->ijab', Wvvvv, tau)
    add_vvvv_(cc, t2new, t1, t2, eris)
    time1 = log.timer_debug1('t2 vvvv', *time1)

    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        kb = kconserv[ki, ka, kj]

        t2new_tmp = einsum('ac,ijcb->ijab', Lvv[ka], t2[ki, kj, ka])
        t2new_tmp += einsum('ki,kjab->ijab', -Loo[ki], t2[ki, kj, ka])

        kc = kconserv[ka, ki, kb]
        tmp2 = np.asarray(eris.vovv[kc, ki, kb]).transpose(3, 2, 1, 0).conj() \
               - einsum('kbic,ka->abic', eris.ovov[ka, kb, ki], t1[ka])
        t2new_tmp += einsum('abic,jc->ijab', tmp2, t1[kj])

        kk = kconserv[ki, ka, kj]
        tmp2 = np.asarray(eris.ooov[kj, ki, kk]).transpose(3, 2, 1, 0).conj() \
               + einsum('akic,jc->akij', eris.voov[ka, kk, ki], t1[kj])
        t2new_tmp -= einsum('akij,kb->ijab', tmp2, t1[kb])
        t2new[ki, kj, ka] += t2new_tmp
        t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2)

    mem_now = lib.current_memory()[0]
    if (nocc ** 2 * nvir ** 2 * nkpts ** 3) * 16 / 1e6 * 2 + mem_now < cc.max_memory * .9:
        Wvoov = imdk.cc_Wvoov(t1, t2, eris, kconserv)
        Wvovo = imdk.cc_Wvovo(t1, t2, eris, kconserv)
    else:
        fimd = lib.H5TmpFile()
        Wvoov = fimd.create_dataset('voov', (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), t1.dtype.char)
        Wvovo = fimd.create_dataset('vovo', (nkpts, nkpts, nkpts, nvir, nocc, nvir, nocc), t1.dtype.char)
        Wvoov = imdk.cc_Wvoov(t1, t2, eris, kconserv, Wvoov)
        Wvovo = imdk.cc_Wvovo(t1, t2, eris, kconserv, Wvovo)

    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        kb = kconserv[ki, ka, kj]
        t2new_tmp = np.zeros((nocc, nocc, nvir, nvir), dtype=t2.dtype)
        for kk in range(nkpts):
            kc = kconserv[ka, ki, kk]
            tmp_voov = 2. * Wvoov[ka, kk, ki] - Wvovo[ka, kk, kc].transpose(0, 1, 3, 2)
            t2new_tmp += einsum('akic,kjcb->ijab', tmp_voov, t2[kk, kj, kc])

            kc = kconserv[ka, ki, kk]
            t2new_tmp -= einsum('akic,kjbc->ijab', Wvoov[ka, kk, ki], t2[kk, kj, kb])

            kc = kconserv[kk, ka, kj]
            t2new_tmp -= einsum('bkci,kjac->ijab', Wvovo[kb, kk, kc], t2[kk, kj, ka])

        t2new[ki, kj, ka] += t2new_tmp
        t2new[kj, ki, kb] += t2new_tmp.transpose(1, 0, 3, 2)
    Wvoov = Wvovo = None
    fimd = None
    time1 = log.timer_debug1('t2 voov', *time1)

    for ki in range(nkpts):
        ka = ki
        # Remove zero/padded elements from denominator
        eia = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia]
        t1new[ki] /= eia

    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        kb = kconserv[ki, ka, kj]
        # For LARGE_DENOM, see t1new update above
        eia = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia]

        ejb = LARGE_DENOM * np.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
        ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb]
        eijab = eia[:, None, :, None] + ejb[:, None, :]

        t2new[ki, kj, ka] /= eijab

    time0 = log.timer_debug1('update t1 t2', *time0)

    return t1new, t2new
Ejemplo n.º 13
0
    def __init__(self, cc, mo_coeff=None, method='incore'):
        from pyscf.pbc import df
        from pyscf.pbc import tools
        from pyscf.pbc.cc.ccsd import _adjust_occ
        log = logger.Logger(cc.stdout, cc.verbose)
        cput0 = (time.clock(), time.time())
        moidx = get_frozen_mask(cc)
        cell = cc._scf.cell
        kpts = cc.kpts
        nkpts = cc.nkpts
        nocc = cc.nocc
        nmo = cc.nmo
        nvir = nmo - nocc

        # if any(nocc != np.count_nonzero(cc._scf.mo_occ[k]>0)
        #       for k in range(nkpts)):
        #    raise NotImplementedError('Different occupancies found for different k-points')

        if mo_coeff is None:
            mo_coeff = cc.mo_coeff
        dtype = mo_coeff[0].dtype

        mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff)

        # Re-make our fock MO matrix elements from density and fock AO
        dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ)
        with lib.temporary_env(cc._scf, exxdiv=None):
            # _scf.exxdiv affects eris.fock. HF exchange correction should be
            # excluded from the Fock matrix.
            fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm)
        self.fock = np.asarray([reduce(np.dot, (mo.T.conj(), fockao[k], mo))
                                for k, mo in enumerate(mo_coeff)])

        self.mo_energy = [self.fock[k].diagonal().real for k in range(nkpts)]
        # Add HFX correction in the self.mo_energy to improve convergence in
        # CCSD iteration. It is useful for the 2D systems since their occupied and
        # the virtual orbital energies may overlap which may lead to numerical
        # issue in the CCSD iterations.
        # FIXME: Whether to add this correction for other exxdiv treatments?
        # Without the correction, MP2 energy may be largely off the correct value.
        madelung = tools.madelung(cell, kpts)
        self.mo_energy = [_adjust_occ(mo_e, nocc, -madelung)
                          for k, mo_e in enumerate(self.mo_energy)]

        # Get location of padded elements in occupied and virtual space.
        nocc_per_kpt = get_nocc(cc, per_kpoint=True)
        nonzero_padding = padding_k_idx(cc, kind="joint")

        # Check direct and indirect gaps for possible issues with CCSD convergence.
        mo_e = [self.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)]
        mo_e = np.sort([y for x in mo_e for y in x])  # Sort de-nested array
        gap = mo_e[np.sum(nocc_per_kpt)] - mo_e[np.sum(nocc_per_kpt)-1]
        if gap < 1e-5:
            logger.warn(cc, 'H**O-LUMO gap %s too small for KCCSD. '
                            'May cause issues in convergence.', gap)

        mem_incore, mem_outcore, mem_basic = _mem_usage(nkpts, nocc, nvir)
        mem_now = lib.current_memory()[0]
        fao2mo = cc._scf.with_df.ao2mo

        kconserv = cc.khelper.kconserv
        khelper = cc.khelper
        orbo = np.asarray(mo_coeff[:,:,:nocc], order='C')
        orbv = np.asarray(mo_coeff[:,:,nocc:], order='C')

        if (method == 'incore' and (mem_incore + mem_now < cc.max_memory)
                or cell.incore_anyway):
            log.info('using incore ERI storage')
            self.oooo = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nocc,nocc), dtype=dtype)
            self.ooov = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nocc,nvir), dtype=dtype)
            self.oovv = np.empty((nkpts,nkpts,nkpts,nocc,nocc,nvir,nvir), dtype=dtype)
            self.ovov = np.empty((nkpts,nkpts,nkpts,nocc,nvir,nocc,nvir), dtype=dtype)
            self.voov = np.empty((nkpts,nkpts,nkpts,nvir,nocc,nocc,nvir), dtype=dtype)
            self.vovv = np.empty((nkpts,nkpts,nkpts,nvir,nocc,nvir,nvir), dtype=dtype)
            #self.vvvv = np.empty((nkpts,nkpts,nkpts,nvir,nvir,nvir,nvir), dtype=dtype)
            self.vvvv = cc._scf.with_df.ao2mo_7d(orbv, factor=1./nkpts).transpose(0,2,1,3,5,4,6)

            for (ikp,ikq,ikr) in khelper.symm_map.keys():
                iks = kconserv[ikp,ikq,ikr]
                eri_kpt = fao2mo((mo_coeff[ikp],mo_coeff[ikq],mo_coeff[ikr],mo_coeff[iks]),
                                 (kpts[ikp],kpts[ikq],kpts[ikr],kpts[iks]), compact=False)
                if dtype == np.float: eri_kpt = eri_kpt.real
                eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
                for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]:
                    eri_kpt_symm = khelper.transform_symm(eri_kpt, kp, kq, kr).transpose(0, 2, 1, 3)
                    self.oooo[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, :nocc, :nocc] / nkpts
                    self.ooov[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, :nocc, nocc:] / nkpts
                    self.oovv[kp, kr, kq] = eri_kpt_symm[:nocc, :nocc, nocc:, nocc:] / nkpts
                    self.ovov[kp, kr, kq] = eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] / nkpts
                    self.voov[kp, kr, kq] = eri_kpt_symm[nocc:, :nocc, :nocc, nocc:] / nkpts
                    self.vovv[kp, kr, kq] = eri_kpt_symm[nocc:, :nocc, nocc:, nocc:] / nkpts
                    #self.vvvv[kp, kr, kq] = eri_kpt_symm[nocc:, nocc:, nocc:, nocc:] / nkpts

            self.dtype = dtype
        else:
            log.info('using HDF5 ERI storage')
            self.feri1 = lib.H5TmpFile()

            self.oooo = self.feri1.create_dataset('oooo', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nocc), dtype.char)
            self.ooov = self.feri1.create_dataset('ooov', (nkpts, nkpts, nkpts, nocc, nocc, nocc, nvir), dtype.char)
            self.oovv = self.feri1.create_dataset('oovv', (nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype.char)
            self.ovov = self.feri1.create_dataset('ovov', (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype.char)
            self.voov = self.feri1.create_dataset('voov', (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype.char)
            self.vovv = self.feri1.create_dataset('vovv', (nkpts, nkpts, nkpts, nvir, nocc, nvir, nvir), dtype.char)

            vvvv_required = ((not cc.direct)
                             # cc._scf.with_df needs to be df.GDF only (not MDF)
                             or type(cc._scf.with_df) is not df.GDF
                             # direct-vvvv for pbc-2D is not supported so far
                             or cell.dimension == 2)
            if vvvv_required:
                self.vvvv = self.feri1.create_dataset('vvvv', (nkpts,nkpts,nkpts,nvir,nvir,nvir,nvir), dtype.char)

            # <ij|pq>  = (ip|jq)
            cput1 = time.clock(), time.time()
            for kp in range(nkpts):
                for kq in range(nkpts):
                    for kr in range(nkpts):
                        ks = kconserv[kp, kq, kr]
                        orbo_p = mo_coeff[kp][:, :nocc]
                        orbo_r = mo_coeff[kr][:, :nocc]
                        buf_kpt = fao2mo((orbo_p, mo_coeff[kq], orbo_r, mo_coeff[ks]),
                                         (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False)
                        if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real
                        buf_kpt = buf_kpt.reshape(nocc, nmo, nocc, nmo).transpose(0, 2, 1, 3)
                        self.dtype = buf_kpt.dtype
                        self.oooo[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, :nocc] / nkpts
                        self.ooov[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, nocc:] / nkpts
                        self.oovv[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, nocc:, nocc:] / nkpts
            cput1 = log.timer_debug1('transforming oopq', *cput1)

            # <ia|pq> = (ip|aq)
            cput1 = time.clock(), time.time()
            for kp in range(nkpts):
                for kq in range(nkpts):
                    for kr in range(nkpts):
                        ks = kconserv[kp, kq, kr]
                        orbo_p = mo_coeff[kp][:, :nocc]
                        orbv_r = mo_coeff[kr][:, nocc:]
                        buf_kpt = fao2mo((orbo_p, mo_coeff[kq], orbv_r, mo_coeff[ks]),
                                         (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False)
                        if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real
                        buf_kpt = buf_kpt.reshape(nocc, nmo, nvir, nmo).transpose(0, 2, 1, 3)
                        self.ovov[kp, kr, kq, :, :, :, :] = buf_kpt[:, :, :nocc, nocc:] / nkpts
                        # TODO: compute vovv on the fly
                        self.vovv[kr, kp, ks, :, :, :, :] = buf_kpt[:, :, nocc:, nocc:].transpose(1, 0, 3, 2) / nkpts
                        self.voov[kr, kp, ks, :, :, :, :] = buf_kpt[:, :, nocc:, :nocc].transpose(1, 0, 3, 2) / nkpts
            cput1 = log.timer_debug1('transforming ovpq', *cput1)

            ## Without k-point symmetry
            # cput1 = time.clock(), time.time()
            # for kp in range(nkpts):
            #    for kq in range(nkpts):
            #        for kr in range(nkpts):
            #            ks = kconserv[kp,kq,kr]
            #            orbv_p = mo_coeff[kp][:,nocc:]
            #            orbv_q = mo_coeff[kq][:,nocc:]
            #            orbv_r = mo_coeff[kr][:,nocc:]
            #            orbv_s = mo_coeff[ks][:,nocc:]
            #            for a in range(nvir):
            #                orbva_p = orbv_p[:,a].reshape(-1,1)
            #                buf_kpt = fao2mo((orbva_p,orbv_q,orbv_r,orbv_s),
            #                                 (kpts[kp],kpts[kq],kpts[kr],kpts[ks]), compact=False)
            #                if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real
            #                buf_kpt = buf_kpt.reshape((1,nvir,nvir,nvir)).transpose(0,2,1,3)
            #                self.vvvv[kp,kr,kq,a,:,:,:] = buf_kpt[:] / nkpts
            # cput1 = log.timer_debug1('transforming vvvv', *cput1)

            cput1 = time.clock(), time.time()
            mem_now = lib.current_memory()[0]
            if not vvvv_required:
                _init_df_eris(cc, self)

            elif nvir ** 4 * 16 / 1e6 + mem_now < cc.max_memory:
                for (ikp, ikq, ikr) in khelper.symm_map.keys():
                    iks = kconserv[ikp, ikq, ikr]
                    orbv_p = mo_coeff[ikp][:, nocc:]
                    orbv_q = mo_coeff[ikq][:, nocc:]
                    orbv_r = mo_coeff[ikr][:, nocc:]
                    orbv_s = mo_coeff[iks][:, nocc:]
                    # unit cell is small enough to handle vvvv in-core
                    buf_kpt = fao2mo((orbv_p,orbv_q,orbv_r,orbv_s),
                                     kpts[[ikp,ikq,ikr,iks]], compact=False)
                    if dtype == np.float: buf_kpt = buf_kpt.real
                    buf_kpt = buf_kpt.reshape((nvir, nvir, nvir, nvir))
                    for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]:
                        buf_kpt_symm = khelper.transform_symm(buf_kpt, kp, kq, kr).transpose(0, 2, 1, 3)
                        self.vvvv[kp, kr, kq] = buf_kpt_symm / nkpts
            else:
                raise MemoryError('Minimal memory requirements %s MB'
                                  % (mem_now + nvir ** 4 / 1e6 * 16 * 2))
                for (ikp, ikq, ikr) in khelper.symm_map.keys():
                    for a in range(nvir):
                        orbva_p = orbv_p[:, a].reshape(-1, 1)
                        buf_kpt = fao2mo((orbva_p, orbv_q, orbv_r, orbv_s),
                                         (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]), compact=False)
                        if mo_coeff[0].dtype == np.float: buf_kpt = buf_kpt.real
                        buf_kpt = buf_kpt.reshape((1, nvir, nvir, nvir)).transpose(0, 2, 1, 3)

                        self.vvvv[ikp, ikr, ikq, a, :, :, :] = buf_kpt[0, :, :, :] / nkpts
                        # Store symmetric permutations
                        self.vvvv[ikr, ikp, iks, :, a, :, :] = buf_kpt.transpose(1, 0, 3, 2)[:, 0, :, :] / nkpts
                        self.vvvv[ikq, iks, ikp, :, :, a, :] = buf_kpt.transpose(2, 3, 0, 1).conj()[:, :, 0, :] / nkpts
                        self.vvvv[iks, ikq, ikr, :, :, :, a] = buf_kpt.transpose(3, 2, 1, 0).conj()[:, :, :, 0] / nkpts
            cput1 = log.timer_debug1('transforming vvvv', *cput1)

        log.timer('CCSD integral transformation', *cput0)
Ejemplo n.º 14
0
    def __init__(self, cc, mo_coeff=None):
        from pyscf.pbc.cc.ccsd import _adjust_occ
        import pyscf.pbc.tools.pbc as tools
        if mo_coeff is None:
            mo_coeff = cc.mo_coeff
        cput0 = (time.clock(), time.time())
        symlib = cc.symlib
        log = Logger(cc.stdout, cc.verbose)
        self.lib = lib
        nocc, nmo, nkpts = cc.nocc, cc.nmo, cc.nkpts
        nvir = nmo - nocc
        cell, kpts = cc._scf.cell, cc.kpts
        gvec = cell.reciprocal_vectors()
        sym1 = ['+-', [kpts,]*2, None, gvec]
        sym2 = ['+-+-', [kpts,]*4, None, gvec]
        mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff)
        nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")
        madelung = tools.madelung(cell, kpts)
        fock = None
        if rank==0:
            dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ)
            with pyscflib.temporary_env(cc._scf, exxdiv=None):
                fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm)
            fock = np.asarray([reduce(np.dot, (mo.T.conj(), fockao[k], mo))
                                for k, mo in enumerate(mo_coeff)])
        fock = comm.bcast(fock, root=0)
        self.dtype = dtype = np.result_type(*(mo_coeff, fock)).char
        self.foo = zeros([nocc,nocc], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)
        self.fov = zeros([nocc,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)
        self.fvv = zeros([nvir,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)
        self.eia = zeros([nocc,nvir], np.float64, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)
        self._foo = zeros([nocc,nocc], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)
        self._fvv = zeros([nvir,nvir], dtype, sym1, symlib=symlib, verbose=cc.SYMVERBOSE)

        foo = fock[:,:nocc,:nocc]
        fov = fock[:,:nocc,nocc:]
        fvv = fock[:,nocc:,nocc:]

        mo_energy = [fock[k].diagonal().real for k in range(nkpts)]
        mo_energy = [_adjust_occ(mo_e, nocc, -madelung)
                          for k, mo_e in enumerate(mo_energy)]

        mo_e_o = [e[:nocc] for e in mo_energy]
        mo_e_v = [e[nocc:] + cc.level_shift for e in mo_energy]
        foo_ = np.asarray([np.diag(e) for e in mo_e_o])
        fvv_ = np.asarray([np.diag(e) for e in mo_e_v])
        eia = np.zeros([nkpts,nocc,nvir])
        for ki in range(nkpts):
            eia[ki] = _get_epq([0,nocc,ki,mo_e_o,nonzero_opadding],
                        [0,nvir,ki,mo_e_v,nonzero_vpadding],
                        fac=[1.0,-1.0])
        if rank ==0:
            self.foo.write(range(foo.size), foo.ravel())
            self.fov.write(range(fov.size), fov.ravel())
            self.fvv.write(range(fvv.size), fvv.ravel())
            self.eia.write(range(eia.size), eia.ravel())
            self._foo.write(range(foo_.size), foo_.ravel())
            self._fvv.write(range(fvv_.size), fvv_.ravel())
        else:
            self.foo.write([],[])
            self.fov.write([],[])
            self.fvv.write([],[])
            self.eia.write([],[])
            self._foo.write([],[])
            self._fvv.write([],[])

        self.eijab = zeros([nocc,nocc,nvir,nvir], np.float64, sym2, symlib=symlib, verbose=cc.SYMVERBOSE)

        kconserv = cc.khelper.kconserv
        khelper = cc.khelper

        idx_oovv = np.arange(nocc*nocc*nvir*nvir)
        jobs = list(khelper.symm_map.keys())
        tasks = static_partition(jobs)
        ntasks = max(comm.allgather(len(tasks)))
        nwrite = 0
        for itask in tasks:
            ikp, ikq, ikr = itask
            pqr = np.asarray(khelper.symm_map[(ikp,ikq,ikr)])
            nwrite += len(np.unique(pqr, axis=0))

        nwrite_max = max(comm.allgather(nwrite))
        write_count = 0
        for itask in range(ntasks):
            if itask >= len(tasks):
                continue
            ikp, ikq, ikr = tasks[itask]
            iks = kconserv[ikp,ikq,ikr]
            done = np.zeros([nkpts,nkpts,nkpts])
            for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]:
                if done[kp,kq,kr]: continue
                ks = kconserv[kp,kq,kr]
                eia = _get_epq([0,nocc,kp,mo_e_o,nonzero_opadding],
                               [0,nvir,kq,mo_e_v,nonzero_vpadding],
                               fac=[1.0,-1.0])
                ejb = _get_epq([0,nocc,kr,mo_e_o,nonzero_opadding],
                               [0,nvir,ks,mo_e_v,nonzero_vpadding],
                               fac=[1.0,-1.0])
                eijab = eia[:,None,:,None] + ejb[None,:,None,:]
                off = kp * nkpts**2 + kr * nkpts + kq
                self.eijab.write(off*idx_oovv.size+idx_oovv, eijab.ravel())
                done[kp,kq,kr] = 1
                write_count += 1

        for i in range(nwrite_max-write_count):
            self.eijab.write([], [])

        if type(cc._scf.with_df) is df.FFTDF:
            _make_fftdf_eris(cc, self)
        else:
            from cc_sym import mpigdf
            if type(cc._scf.with_df) is mpigdf.GDF:
                _make_df_eris(cc, self)
            elif type(cc._scf.with_df) is df.GDF:
                log.warn("GDF converted to an MPIGDF object")
                cc._scf.with_df = mpigdf.from_serial(cc._scf.with_df)
                _make_df_eris(cc, self)
            else:
                raise NotImplementedError("DF object not recognized")
        log.timer("ao2mo transformation", *cput0)
Ejemplo n.º 15
0
def get_full_t3p2(mycc, t1, t2, eris):
    '''Build the entire T3[2] array in memory.
    '''
    nkpts = mycc.nkpts
    nocc = mycc.nocc
    nmo = mycc.nmo
    nvir = nmo - nocc
    kconserv = mycc.khelper.kconserv

    def get_wijkabc(ki, kj, kk, ka, kb, kc):
        '''Build T3[2] for `ijkabc` at a given set of k-points'''
        km = kconserv[kc, kk, kb]
        kf = kconserv[kk, kc, kj]
        ret = einsum('kjcf,ifab->ijkabc', t2[kk, kj, kc], eris.ovvv[ki, kf,
                                                                    ka].conj())
        ret = ret - einsum('jima,mkbc->ijkabc', eris.ooov[kj, ki, km].conj(),
                           t2[km, kk, kb])
        return ret

    #fock = eris.fock
    #fov = fock[:, :nocc, nocc:]
    #foo = numpy.array([fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)])
    #fvv = numpy.array([fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)])
    mo_energy_occ = numpy.array(
        [eris.mo_energy[ki][:nocc] for ki in range(nkpts)])
    mo_energy_vir = numpy.array(
        [eris.mo_energy[ki][nocc:] for ki in range(nkpts)])

    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split")

    t3 = numpy.empty((nkpts, nkpts, nkpts, nkpts, nkpts, nocc, nocc, nocc,
                      nvir, nvir, nvir),
                     dtype=t2.dtype)
    for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5):
        kc = kpts_helper.get_kconserv3(mycc._scf.cell, mycc.kpts,
                                       [ki, kj, kk, ka, kb])
        # Perform P(abc)
        t3[ki, kj, kk, ka, kb] = get_wijkabc(ki, kj, kk, ka, kb, kc)
        t3[ki, kj, kk, ka, kb] += get_wijkabc(ki, kj, kk, kb, kc,
                                              ka).transpose(0, 1, 2, 5, 3, 4)
        t3[ki, kj, kk, ka, kb] += get_wijkabc(ki, kj, kk, kc, ka,
                                              kb).transpose(0, 1, 2, 4, 5, 3)

    # Perform P(ijk)
    t3 = (t3.transpose(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +
          t3.transpose(1, 2, 0, 3, 4, 6, 7, 5, 8, 9, 10) +
          t3.transpose(2, 0, 1, 3, 4, 7, 5, 6, 8, 9, 10))

    for ki, kj, kk in product(range(nkpts), repeat=3):
        eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding],
                         [0, nocc, kj, mo_e_o, nonzero_opadding],
                         [0, nocc, kk, mo_e_o, nonzero_opadding])
        for ka, kb in product(range(nkpts), repeat=2):
            kc = kpts_helper.get_kconserv3(mycc._scf.cell, mycc.kpts,
                                           [ki, kj, kk, ka, kb])
            eabc = _get_epqr([0, nvir, ka, mo_e_v, nonzero_vpadding],
                             [0, nvir, kb, mo_e_v, nonzero_vpadding],
                             [0, nvir, kc, mo_e_v, nonzero_vpadding],
                             fac=[-1., -1., -1.])
            eijkabc = eijk[:, :, :, None, None, None] + eabc[None, None,
                                                             None, :, :, :]
            t3[ki, kj, kk, ka, kb] /= eijkabc

    return t3
Ejemplo n.º 16
0
def update_amps(cc, t1, t2, eris):
    time0 = time.clock(), time.time()
    log = logger.Logger(cc.stdout, cc.verbose)
    nkpts, nocc, nvir = t1.shape
    fock = eris.fock
    mo_e_o = [e[:nocc] for e in eris.mo_energy]
    mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy]

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    fov = fock[:, :nocc, nocc:].copy()
    foo = fock[:, :nocc, :nocc].copy()
    fvv = fock[:, nocc:, nocc:].copy()

    # Get the momentum conservation array
    # Note: chemist's notation for momentum conserving t2(ki,kj,ka,kb), even though
    # integrals are in physics notation
    kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts)

    tau = imdk.make_tau(cc, t2, t1, t1, kconserv)

    Fvv = imdk.cc_Fvv(cc, t1, t2, eris, kconserv)
    Foo = imdk.cc_Foo(cc, t1, t2, eris, kconserv)
    Fov = imdk.cc_Fov(cc, t1, t2, eris, kconserv)
    Woooo = imdk.cc_Woooo(cc, t1, t2, eris, kconserv)
    Wvvvv = imdk.cc_Wvvvv(cc, t1, t2, eris, kconserv)
    Wovvo = imdk.cc_Wovvo(cc, t1, t2, eris, kconserv)

    # Move energy terms to the other side
    for k in range(nkpts):
        Foo[k][numpy.diag_indices(nocc)] -= mo_e_o[k]
        Fvv[k][numpy.diag_indices(nvir)] -= mo_e_v[k]

    eris_ovvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nvir, nvir,
                                   nocc),
                            dtype=t2.dtype)
    eris_oovo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nocc, nvir,
                                   nocc),
                            dtype=t2.dtype)
    eris_vvvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nvir, nvir, nvir,
                                   nocc),
                            dtype=t2.dtype)
    for km, kb, ke in kpts_helper.loop_kkk(nkpts):
        kj = kconserv[km, ke, kb]
        # <mb||je> -> -<mb||ej>
        eris_ovvo[km, kb, ke] = -eris.ovov[km, kb, kj].transpose(0, 1, 3, 2)
        # <mn||je> -> -<mn||ej>
        # let kb = kn as a dummy variable
        eris_oovo[km, kb, ke] = -eris.ooov[km, kb, kj].transpose(0, 1, 3, 2)
        # <ma||be> -> - <be||am>*
        # let kj = ka as a dummy variable
        kj = kconserv[km, ke, kb]
        eris_vvvo[ke, kj,
                  kb] = -eris.ovvv[km, kb, ke].transpose(2, 3, 1, 0).conj()

    # T1 equation
    t1new = numpy.zeros(shape=t1.shape, dtype=t1.dtype)
    for ka in range(nkpts):
        ki = ka
        t1new[ka] += numpy.array(fov[ka, :, :]).conj()
        t1new[ka] += einsum('ie,ae->ia', t1[ka], Fvv[ka])
        t1new[ka] += -einsum('ma,mi->ia', t1[ka], Foo[ka])
        for km in range(nkpts):
            t1new[ka] += einsum('imae,me->ia', t2[ka, km, ka], Fov[km])
            t1new[ka] += -einsum('nf,naif->ia', t1[km], eris.ovov[km, ka, ki])
            for kn in range(nkpts):
                ke = kconserv[km, ki, kn]
                t1new[ka] += -0.5 * einsum('imef,maef->ia', t2[ki, km, ke],
                                           eris.ovvv[km, ka, ke])
                t1new[ka] += -0.5 * einsum('mnae,nmei->ia', t2[km, kn, ka],
                                           eris_oovo[kn, km, ke])

    # T2 equation
    t2new = numpy.array(eris.oovv).conj()
    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        # Chemist's notation for momentum conserving t2(ki,kj,ka,kb)
        kb = kconserv[ki, ka, kj]

        Ftmp = Fvv[kb] - 0.5 * einsum('mb,me->be', t1[kb], Fov[kb])
        tmp = einsum('ijae,be->ijab', t2[ki, kj, ka], Ftmp)
        t2new[ki, kj, ka] += tmp

        #t2new[ki,kj,kb] -= tmp.transpose(0,1,3,2)
        Ftmp = Fvv[ka] - 0.5 * einsum('ma,me->ae', t1[ka], Fov[ka])
        tmp = einsum('ijbe,ae->ijab', t2[ki, kj, kb], Ftmp)
        t2new[ki, kj, ka] -= tmp

        Ftmp = Foo[kj] + 0.5 * einsum('je,me->mj', t1[kj], Fov[kj])
        tmp = einsum('imab,mj->ijab', t2[ki, kj, ka], Ftmp)
        t2new[ki, kj, ka] -= tmp

        #t2new[kj,ki,ka] += tmp.transpose(1,0,2,3)
        Ftmp = Foo[ki] + 0.5 * einsum('ie,me->mi', t1[ki], Fov[ki])
        tmp = einsum('jmab,mi->ijab', t2[kj, ki, ka], Ftmp)
        t2new[ki, kj, ka] += tmp

        for km in range(nkpts):
            # Wminj
            #   - km - kn + ka + kb = 0
            # =>  kn = ka - km + kb
            kn = kconserv[ka, km, kb]
            t2new[ki, kj, ka] += 0.5 * einsum(
                'mnab,mnij->ijab', tau[km, kn, ka], Woooo[km, kn, ki])
            ke = km
            t2new[ki, kj, ka] += 0.5 * einsum(
                'ijef,abef->ijab', tau[ki, kj, ke], Wvvvv[ka, kb, ke])

            # Wmbej
            #     - km - kb + ke + kj = 0
            #  => ke = km - kj + kb
            ke = kconserv[km, kj, kb]
            tmp = einsum('imae,mbej->ijab', t2[ki, km, ka], Wovvo[km, kb, ke])
            #     - km - kb + ke + kj = 0
            # =>  ke = km - kj + kb
            #
            # t[i,e] => ki = ke
            # t[m,a] => km = ka
            if km == ka and ke == ki:
                tmp -= einsum('ie,ma,mbej->ijab', t1[ki], t1[km],
                              eris_ovvo[km, kb, ke])
            t2new[ki, kj, ka] += tmp
            t2new[ki, kj, kb] -= tmp.transpose(0, 1, 3, 2)
            t2new[kj, ki, ka] -= tmp.transpose(1, 0, 2, 3)
            t2new[kj, ki, kb] += tmp.transpose(1, 0, 3, 2)

        ke = ki
        tmp = einsum('ie,abej->ijab', t1[ki], eris_vvvo[ka, kb, ke])
        t2new[ki, kj, ka] += tmp
        # P(ij) term
        ke = kj
        tmp = einsum('je,abei->ijab', t1[kj], eris_vvvo[ka, kb, ke])
        t2new[ki, kj, ka] -= tmp

        km = ka
        tmp = einsum('ma,mbij->ijab', t1[ka], eris.ovoo[km, kb, ki])
        t2new[ki, kj, ka] -= tmp
        # P(ab) term
        km = kb
        tmp = einsum('mb,maij->ijab', t1[kb], eris.ovoo[km, ka, ki])
        t2new[ki, kj, ka] += tmp

    for ki in range(nkpts):
        ka = ki
        # Remove zero/padded elements from denominator
        eia = LARGE_DENOM * numpy.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]
        t1new[ki] /= eia

    kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts)
    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        kb = kconserv[ki, ka, kj]
        # For LARGE_DENOM, see t1new update above
        eia = LARGE_DENOM * numpy.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]

        ejb = LARGE_DENOM * numpy.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
        ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb]
        eijab = eia[:, None, :, None] + ejb[:, None, :]

        t2new[ki, kj, ka] /= eijab

    time0 = log.timer_debug1('update t1 t2', *time0)

    return t1new, t2new
Ejemplo n.º 17
0
def get_t3p2_imds(mycc, t1, t2, eris=None, t3p2_ip_out=None, t3p2_ea_out=None):
    """For a description of arguments, see `get_t3p2_imds_slow` in
    the corresponding `kintermediates.py`.
    """
    from pyscf.pbc.cc.kccsd_t_rhf import _get_epqr
    cpu1 = cpu0 = (logger.process_clock(), logger.perf_counter())
    if eris is None:
        eris = mycc.ao2mo()
    fock = eris.fock
    nkpts, nocc, nvir = t1.shape
    cell = mycc._scf.cell
    kpts = mycc.kpts
    kconserv = mycc.khelper.kconserv
    dtype = np.result_type(t1, t2)

    fov = fock[:, :nocc, nocc:]
    #foo = np.asarray([fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)])
    #fvv = np.asarray([fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)])
    mo_energy_occ = np.array(
        [eris.mo_energy[ki][:nocc] for ki in range(nkpts)])
    mo_energy_vir = np.array(
        [eris.mo_energy[ki][nocc:] for ki in range(nkpts)])

    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    ccsd_energy = mycc.energy(t1, t2, eris)

    if t3p2_ip_out is None:
        t3p2_ip_out = np.zeros((nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc),
                               dtype=dtype)
    Wmcik = t3p2_ip_out

    if t3p2_ea_out is None:
        t3p2_ea_out = np.zeros((nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc),
                               dtype=dtype)
    Wacek = t3p2_ea_out

    # Create necessary temporary eris for fast read
    from pyscf.pbc.cc.kccsd_t_rhf import create_t3_eris, get_data_slices
    feri_tmp, t2T, eris_vvop, eris_vooo_C = create_t3_eris(
        mycc, kconserv, [eris.vovv, eris.oovv, eris.ooov, t2])
    #t1T = np.array([x.T for x in t1], dtype=np.complex, order='C')
    #fvo = np.array([x.T for x in fov], dtype=np.complex, order='C')
    cpu1 = logger.timer_debug1(mycc, 'CCSD(T) tmp eri creation', *cpu1)

    def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1):
        '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts

        Function copied for `kccsd_t_rhf.py`'''
        km = kconserv[kc, kk, kb]
        kf = kconserv[kk, kc, kj]
        out = einsum('cfjk,abif->abcijk', t2T[kc, kf, kj, c0:c1, :, :, :],
                     eris_vvop[ka, kb, ki, a0:a1, b0:b1, :, nocc:])
        out = out - einsum('cbmk,aijm->abcijk', t2T[kc, kb, km, c0:c1,
                                                    b0:b1, :, :],
                           eris_vooo_C[ka, ki, kj, a0:a1, :, :, :])
        return out

    def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices):
        '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper

        Function copied for `kccsd_t_rhf.py`'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1)
        out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0,
                          a1).transpose(2, 0, 1, 5, 3, 4)
        out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0,
                          b1).transpose(1, 2, 0, 4, 5, 3)
        out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0,
                          b1).transpose(0, 2, 1, 3, 5, 4)
        out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0,
                          a1).transpose(2, 1, 0, 5, 4, 3)
        out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0,
                          c1).transpose(1, 0, 2, 4, 3, 5)
        return out

    def get_data(kpt_indices):
        idx_args = get_data_slices(kpt_indices, task, kconserv)
        vvop_indices, vooo_indices, t2T_vvop_indices, t2T_vooo_indices = idx_args
        vvop_data = [eris_vvop[tuple(x)] for x in vvop_indices]
        vooo_data = [eris_vooo_C[tuple(x)] for x in vooo_indices]
        t2T_vvop_data = [t2T[tuple(x)] for x in t2T_vvop_indices]
        t2T_vooo_data = [t2T[tuple(x)] for x in t2T_vooo_indices]
        data = [vvop_data, vooo_data, t2T_vvop_data, t2T_vooo_data]
        return data

    def add_and_permute(kpt_indices, orb_indices, data):
        '''Performs permutation and addition of t3 temporary arrays.'''
        ki, kj, kk, ka, kb, kc = kpt_indices
        a0, a1, b0, b1, c0, c1 = orb_indices
        tmp_t3Tv_ijk = np.asarray(data[0], dtype=dtype, order='C')
        tmp_t3Tv_jik = np.asarray(data[1], dtype=dtype, order='C')
        tmp_t3Tv_kji = np.asarray(data[2], dtype=dtype, order='C')
        #out_ijk = np.empty(data[0].shape, dtype=dtype, order='C')

        #drv = _ccsd.libcc.MPICCadd_and_permute_t3T
        #drv(ctypes.c_int(nocc), ctypes.c_int(nvir),
        #    ctypes.c_int(0),
        #    out_ijk.ctypes.data_as(ctypes.c_void_p),
        #    tmp_t3Tv_ijk.ctypes.data_as(ctypes.c_void_p),
        #    tmp_t3Tv_jik.ctypes.data_as(ctypes.c_void_p),
        #    tmp_t3Tv_kji.ctypes.data_as(ctypes.c_void_p),
        #    mo_offset.ctypes.data_as(ctypes.c_void_p),
        #    slices.ctypes.data_as(ctypes.c_void_p))
        return (2. * tmp_t3Tv_ijk - tmp_t3Tv_jik.transpose(0, 1, 2, 4, 3, 5) -
                tmp_t3Tv_kji.transpose(0, 1, 2, 5, 4, 3))
        #return out_ijk

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split")

    mem_now = lib.current_memory()[0]
    max_memory = max(0, mycc.max_memory - mem_now)
    blkmin = 4
    # temporary t3 array is size:  nkpts**3 * blksize**3 * nocc**3 * 16
    vir_blksize = min(
        nvir,
        max(blkmin, int(
            (max_memory * .9e6 / 16 / nocc**3 / nkpts**3)**(1. / 3))))
    tasks = []
    logger.debug(mycc, 'max_memory %d MB (%d MB in use)', max_memory, mem_now)
    logger.debug(mycc, 'virtual blksize = %d (nvir = %d)', vir_blksize, nvir)
    for a0, a1 in lib.prange(0, nvir, vir_blksize):
        for b0, b1 in lib.prange(0, nvir, vir_blksize):
            for c0, c1 in lib.prange(0, nvir, vir_blksize):
                tasks.append((a0, a1, b0, b1, c0, c1))

    eaa = []
    for ka in range(nkpts):
        eaa.append(mo_e_o[ka][:, None] - mo_e_v[ka][None, :])

    pt1 = np.zeros((nkpts, nocc, nvir), dtype=dtype)
    pt2 = np.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir), dtype=dtype)
    for ka, kb in product(range(nkpts), repeat=2):
        for task_id, task in enumerate(tasks):
            cput2 = (logger.process_clock(), logger.perf_counter())
            a0, a1, b0, b1, c0, c1 = task
            my_permuted_w = np.zeros(
                (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3,
                dtype=dtype)

            for ki, kj, kk in product(range(nkpts), repeat=3):
                # Find momentum conservation condition for triples
                # amplitude t3ijkabc
                kc = kpts_helper.get_kconserv3(cell, kpts,
                                               [ki, kj, kk, ka, kb])

                kpt_indices = [ki, kj, kk, ka, kb, kc]
                #data = get_data(kpt_indices)
                my_permuted_w[ki, kj,
                              kk] = get_permuted_w(ki, kj, kk, ka, kb, kc,
                                                   task)

            for ki, kj, kk in product(range(nkpts), repeat=3):
                # eigenvalue denominator: e(i) + e(j) + e(k)
                eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding],
                                 [0, nocc, kj, mo_e_o, nonzero_opadding],
                                 [0, nocc, kk, mo_e_o, nonzero_opadding])

                # Find momentum conservation condition for triples
                # amplitude t3ijkabc
                kc = kpts_helper.get_kconserv3(cell, kpts,
                                               [ki, kj, kk, ka, kb])
                eabc = _get_epqr([a0, a1, ka, mo_e_v, nonzero_vpadding],
                                 [b0, b1, kb, mo_e_v, nonzero_vpadding],
                                 [c0, c1, kc, mo_e_v, nonzero_vpadding],
                                 fac=[-1., -1., -1.])

                kpt_indices = [ki, kj, kk, ka, kb, kc]
                eabcijk = (eijk[None, None, None, :, :, :] +
                           eabc[:, :, :, None, None, None])

                tmp_t3Tv_ijk = my_permuted_w[ki, kj, kk]
                tmp_t3Tv_jik = my_permuted_w[kj, ki, kk]
                tmp_t3Tv_kji = my_permuted_w[kk, kj, ki]
                Ptmp_t3Tv = add_and_permute(
                    kpt_indices, task,
                    (tmp_t3Tv_ijk, tmp_t3Tv_jik, tmp_t3Tv_kji))
                Ptmp_t3Tv /= eabcijk

                # Contribution to T1 amplitudes
                if ki == ka and kc == kconserv[kj, kb, kk]:
                    eris_Soovv = (
                        2. * eris.oovv[kj, kk, kb, :, :, b0:b1, c0:c1] -
                        eris.oovv[kj, kk, kc, :, :, c0:c1, b0:b1].transpose(
                            0, 1, 3, 2))
                    pt1[ka, :, a0:a1] += 0.5 * einsum('abcijk,jkbc->ia',
                                                      Ptmp_t3Tv, eris_Soovv)

                # Contribution to T2 amplitudes
                if ki == ka and kc == kconserv[kj, kb, kk]:
                    tmp = einsum('abcijk,ia->jkbc', Ptmp_t3Tv,
                                 0.5 * fov[ki, :, a0:a1])
                    _add_pt2(pt2, nkpts, kconserv, [kj, kk, kb],
                             [None, None, (b0, b1), (c0, c1)], tmp)

                kd = kconserv[ka, ki, kb]
                eris_vovv = eris.vovv[kd, ki, kb, :, :, b0:b1, a0:a1]
                tmp = einsum('abcijk,diba->jkdc', Ptmp_t3Tv, eris_vovv)
                _add_pt2(pt2, nkpts, kconserv, [kj, kk, kd],
                         [None, None, None, (c0, c1)], tmp)

                km = kconserv[kc, kk, kb]
                eris_ooov = eris.ooov[kj, ki, km, :, :, :, a0:a1]
                tmp = einsum('abcijk,jima->mkbc', Ptmp_t3Tv, eris_ooov)
                _add_pt2(pt2, nkpts, kconserv, [km, kk, kb],
                         [None, None, (b0, b1), (c0, c1)], -1. * tmp)

                # Contribution to Wovoo array
                km = kconserv[ka, ki, kc]
                eris_oovv = eris.oovv[km, ki, kc, :, :, c0:c1, a0:a1]
                tmp = einsum('abcijk,mica->mbkj', Ptmp_t3Tv, eris_oovv)
                Wmcik[km, kb, kk, :, b0:b1, :, :] += tmp

                # Contribution to Wvvoo array
                ke = kconserv[ki, ka, kk]
                eris_oovv = eris.oovv[ki, kk, ka, :, :, a0:a1, :]
                tmp = einsum('abcijk,ikae->cbej', Ptmp_t3Tv, eris_oovv)
                Wacek[kc, kb, ke, c0:c1, b0:b1, :, :] -= tmp

            logger.timer_debug1(
                mycc, 'EOM-CCSD T3[2] ka,kb,vir=(%d,%d,%d/%d) [total=%d]' %
                (ka, kb, task_id, len(tasks), nkpts**5), *cput2)

    for ki in range(nkpts):
        ka = ki
        eia = LARGE_DENOM * np.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]
        pt1[ki] /= eia

    for ki, ka in product(range(nkpts), repeat=2):
        eia = LARGE_DENOM * np.ones(
            (nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = np.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:, None] - mo_e_v[ka])[n0_ovp_ia]
        for kj in range(nkpts):
            kb = kconserv[ki, ka, kj]
            ejb = LARGE_DENOM * np.ones(
                (nocc, nvir), dtype=eris.mo_energy[0].dtype)
            n0_ovp_jb = np.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
            ejb[n0_ovp_jb] = (mo_e_o[kj][:, None] - mo_e_v[kb])[n0_ovp_jb]
            eijab = eia[:, None, :, None] + ejb[:, None, :]
            pt2[ki, kj, ka] /= eijab

    pt1 += t1
    pt2 += t2

    logger.timer(mycc, 'EOM-CCSD(T) imds', *cpu0)

    delta_ccsd_energy = mycc.energy(pt1, pt2, eris) - ccsd_energy
    logger.info(mycc, 'CCSD energy T3[2] correction : %16.12e',
                delta_ccsd_energy)

    return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
Ejemplo n.º 18
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)
    cpu1 = cpu0 = (logger.process_clock(), logger.perf_counter())
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mycc.stdout, verbose)

    if t1 is None: t1 = mycc.t1
    if t2 is None: t2 = mycc.t2

    if eris is None:
        raise TypeError('Electron repulsion integrals, `eris`, must be passed in '
                        'to the CCSD(T) kernel or created in the cc object for '
                        'the k-point CCSD(T) to run!')
    if t1 is None or t2 is None:
        raise TypeError('Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe '
                        'need to run `.ccsd()` on the ccsd object?)')

    cell = mycc._scf.cell
    kpts = mycc.kpts

    # The dtype of any local arrays that will be created
    dtype = t1.dtype

    nkpts, nocc, nvir = t1.shape

    mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]
    mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]
    fov = eris.fock[:, :nocc, nocc:]

    # Set up class for k-point conservation
    kconserv = kpts_helper.get_kconserv(cell, kpts)

    cpu1 = log.timer_debug1('CCSD(T) tmp eri creation', *cpu1)

    def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1, out=None):
        '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts'''
        km = kconserv[ki, ka, kj]
        kf = kconserv[kk, kc, kj]
        ret = einsum('kjcf,fiba->abcijk', t2[kk,kj,kc,:,:,c0:c1,:], eris.vovv[kf,ki,kb,:,:,b0:b1,a0:a1].conj())
        ret = ret - einsum('mkbc,jima->abcijk', t2[km,kk,kb,:,:,b0:b1,c0:c1], eris.ooov[kj,ki,km,:,:,:,a0:a1].conj())
        return ret

    def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices):
        '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1)
        out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2,0,1,5,3,4)
        out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1,2,0,4,5,3)
        out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0,2,1,3,5,4)
        out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2,1,0,5,4,3)
        out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1,0,2,4,3,5)
        return out

    def get_rw(ki, kj, kk, ka, kb, kc, orb_indices):
        '''R operating on Wijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        ret = (4. * get_permuted_w(ki,kj,kk,ka,kb,kc,orb_indices) +
               1. * get_permuted_w(kj,kk,ki,ka,kb,kc,orb_indices).transpose(0,1,2,5,3,4) +
               1. * get_permuted_w(kk,ki,kj,ka,kb,kc,orb_indices).transpose(0,1,2,4,5,3) -
               2. * get_permuted_w(ki,kk,kj,ka,kb,kc,orb_indices).transpose(0,1,2,3,5,4) -
               2. * get_permuted_w(kk,kj,ki,ka,kb,kc,orb_indices).transpose(0,1,2,5,4,3) -
               2. * get_permuted_w(kj,ki,kk,ka,kb,kc,orb_indices).transpose(0,1,2,4,3,5))
        return ret

    def get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1):
        '''Vijkabc intermediate as described in Scuseria paper'''
        out = np.zeros((a1-a0,b1-b0,c1-c0) + (nocc,)*3, dtype=dtype)
        if kk == kc:
            out = out + einsum('kc,ijab->abcijk', t1[kk,:,c0:c1], eris.oovv[ki,kj,ka,:,:,a0:a1,b0:b1].conj())
            out = out + einsum('kc,ijab->abcijk', fov[kk,:,c0:c1], t2[ki,kj,ka,:,:,a0:a1,b0:b1])
        return out

    def get_permuted_v(ki, kj, kk, ka, kb, kc, orb_indices):
        '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        ret = get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1)
        ret = ret + get_v(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0, a1).transpose(2,0,1,5,3,4)
        ret = ret + get_v(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0, b1).transpose(1,2,0,4,5,3)
        ret = ret + get_v(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0, b1).transpose(0,2,1,3,5,4)
        ret = ret + get_v(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0, a1).transpose(2,1,0,5,4,3)
        ret = ret + get_v(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0, c1).transpose(1,0,2,4,3,5)
        return ret

    energy_t = 0.0

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split")

    mem_now = lib.current_memory()[0]
    max_memory = max(0, mycc.max_memory - mem_now)
    blkmin = 4
    # temporary t3 array is size:    blksize**3 * nocc**3 * 16
    vir_blksize = min(nvir, max(blkmin, int((max_memory*.9e6/16/nocc**3)**(1./3))))
    tasks = []
    log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now)
    log.debug('virtual blksize = %d (nvir = %d)', nvir, vir_blksize)
    for a0, a1 in lib.prange(0, nvir, vir_blksize):
        for b0, b1 in lib.prange(0, nvir, vir_blksize):
            for c0, c1 in lib.prange(0, nvir, vir_blksize):
                tasks.append((a0,a1,b0,b1,c0,c1))

    for ka in range(nkpts):
        for kb in range(ka+1):

            for ki, kj, kk in product(range(nkpts), repeat=3):
                # eigenvalue denominator: e(i) + e(j) + e(k)
                eijk = LARGE_DENOM * np.ones((nocc,)*3, dtype=mo_energy_occ[0].dtype)
                n0_ovp_ijk = np.ix_(nonzero_opadding[ki], nonzero_opadding[kj], nonzero_opadding[kk])
                eijk[n0_ovp_ijk] = lib.direct_sum('i,j,k->ijk', mo_energy_occ[ki],
                                                  mo_energy_occ[kj], mo_energy_occ[kk])[n0_ovp_ijk]

                # Find momentum conservation condition for triples
                # amplitude t3ijkabc
                kc = kpts_helper.get_kconserv3(cell, kpts, [ki, kj, kk, ka, kb])

                if not (ka >= kb and kb >= kc):
                    continue

                if ka == kb and kb == kc:
                    symm_kpt = 1.
                elif ka == kb or kb == kc:
                    symm_kpt = 3.
                else:
                    symm_kpt = 6.

                eabc = LARGE_DENOM * np.ones((nvir,)*3, dtype=mo_energy_occ[0].dtype)
                n0_ovp_abc = np.ix_(nonzero_vpadding[ka], nonzero_vpadding[kb], nonzero_vpadding[kc])
                eabc[n0_ovp_abc] = lib.direct_sum('i,j,k->ijk', mo_energy_vir[ka],
                                                  mo_energy_vir[kb], mo_energy_vir[kc])[n0_ovp_abc]
                for task_id, task in enumerate(tasks):
                    eijkabc = (eijk[None,None,None,:,:,:] - eabc[a0:a1,b0:b1,c0:c1,None,None,None])
                    pwijk = (get_permuted_w(ki,kj,kk,ka,kb,kc,task) +
                             get_permuted_v(ki,kj,kk,ka,kb,kc,task) * 0.5)
                    rwijk = get_rw(ki,kj,kk,ka,kb,kc,task) / eijkabc
                    energy_t += symm_kpt * einsum('abcijk,abcijk', pwijk, rwijk.conj())

    energy_t *= (1. / 3)
    energy_t /= nkpts

    if abs(energy_t.imag) > 1e-4:
        log.warn('Non-zero imaginary part of CCSD(T) energy was found %s', energy_t.imag)
    log.timer('CCSD(T)', *cpu0)
    log.note('CCSD(T) correction per cell = %.15g', energy_t.real)
    log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag)
    return energy_t.real
Ejemplo n.º 19
0
def update_amps(cc, t1, t2, eris):
    time0 = time.clock(), time.time()
    log = logger.Logger(cc.stdout, cc.verbose)
    nkpts, nocc, nvir = t1.shape
    fock = eris.fock
    mo_e_o = [e[:nocc] for e in eris.mo_energy]
    mo_e_v = [e[nocc:] + cc.level_shift for e in eris.mo_energy]

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    fov = fock[:, :nocc, nocc:].copy()
    foo = fock[:, :nocc, :nocc].copy()
    fvv = fock[:, nocc:, nocc:].copy()

    # Get the momentum conservation array
    # Note: chemist's notation for momentum conserving t2(ki,kj,ka,kb), even though
    # integrals are in physics notation
    kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts)

    tau = imdk.make_tau(cc, t2, t1, t1, kconserv)

    Fvv = imdk.cc_Fvv(cc, t1, t2, eris, kconserv)
    Foo = imdk.cc_Foo(cc, t1, t2, eris, kconserv)
    Fov = imdk.cc_Fov(cc, t1, t2, eris, kconserv)
    Woooo = imdk.cc_Woooo(cc, t1, t2, eris, kconserv)
    Wvvvv = imdk.cc_Wvvvv(cc, t1, t2, eris, kconserv)
    Wovvo = imdk.cc_Wovvo(cc, t1, t2, eris, kconserv)

    # Move energy terms to the other side
    for k in range(nkpts):
        Foo[k][numpy.diag_indices(nocc)] -= mo_e_o[k]
        Fvv[k][numpy.diag_indices(nvir)] -= mo_e_v[k]

    eris_ovvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nvir, nvir, nocc), dtype=t2.dtype)
    eris_oovo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nocc, nocc, nvir, nocc), dtype=t2.dtype)
    eris_vvvo = numpy.zeros(shape=(nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=t2.dtype)
    for km, kb, ke in kpts_helper.loop_kkk(nkpts):
        kj = kconserv[km, ke, kb]
        # <mb||je> -> -<mb||ej>
        eris_ovvo[km, kb, ke] = -eris.ovov[km, kb, kj].transpose(0, 1, 3, 2)
        # <mn||je> -> -<mn||ej>
        # let kb = kn as a dummy variable
        eris_oovo[km, kb, ke] = -eris.ooov[km, kb, kj].transpose(0, 1, 3, 2)
        # <ma||be> -> - <be||am>*
        # let kj = ka as a dummy variable
        kj = kconserv[km, ke, kb]
        eris_vvvo[ke, kj, kb] = -eris.ovvv[km, kb, ke].transpose(2, 3, 1, 0).conj()

    # T1 equation
    t1new = numpy.zeros(shape=t1.shape, dtype=t1.dtype)
    for ka in range(nkpts):
        ki = ka
        t1new[ka] += numpy.array(fov[ka, :, :]).conj()
        t1new[ka] += einsum('ie,ae->ia', t1[ka], Fvv[ka])
        t1new[ka] += -einsum('ma,mi->ia', t1[ka], Foo[ka])
        for km in range(nkpts):
            t1new[ka] += einsum('imae,me->ia', t2[ka, km, ka], Fov[km])
            t1new[ka] += -einsum('nf,naif->ia', t1[km], eris.ovov[km, ka, ki])
            for kn in range(nkpts):
                ke = kconserv[km, ki, kn]
                t1new[ka] += -0.5 * einsum('imef,maef->ia', t2[ki, km, ke], eris.ovvv[km, ka, ke])
                t1new[ka] += -0.5 * einsum('mnae,nmei->ia', t2[km, kn, ka], eris_oovo[kn, km, ke])

    # T2 equation
    t2new = numpy.array(eris.oovv).conj()
    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        # Chemist's notation for momentum conserving t2(ki,kj,ka,kb)
        kb = kconserv[ki, ka, kj]

        Ftmp = Fvv[kb] - 0.5 * einsum('mb,me->be', t1[kb], Fov[kb])
        tmp = einsum('ijae,be->ijab', t2[ki, kj, ka], Ftmp)
        t2new[ki, kj, ka] += tmp

        #t2new[ki,kj,kb] -= tmp.transpose(0,1,3,2)
        Ftmp = Fvv[ka] - 0.5 * einsum('ma,me->ae', t1[ka], Fov[ka])
        tmp = einsum('ijbe,ae->ijab', t2[ki, kj, kb], Ftmp)
        t2new[ki, kj, ka] -= tmp

        Ftmp = Foo[kj] + 0.5 * einsum('je,me->mj', t1[kj], Fov[kj])
        tmp = einsum('imab,mj->ijab', t2[ki, kj, ka], Ftmp)
        t2new[ki, kj, ka] -= tmp

        #t2new[kj,ki,ka] += tmp.transpose(1,0,2,3)
        Ftmp = Foo[ki] + 0.5 * einsum('ie,me->mi', t1[ki], Fov[ki])
        tmp = einsum('jmab,mi->ijab', t2[kj, ki, ka], Ftmp)
        t2new[ki, kj, ka] += tmp

        for km in range(nkpts):
            # Wminj
            #   - km - kn + ka + kb = 0
            # =>  kn = ka - km + kb
            kn = kconserv[ka, km, kb]
            t2new[ki, kj, ka] += 0.5 * einsum('mnab,mnij->ijab', tau[km, kn, ka], Woooo[km, kn, ki])
            ke = km
            t2new[ki, kj, ka] += 0.5 * einsum('ijef,abef->ijab', tau[ki, kj, ke], Wvvvv[ka, kb, ke])

            # Wmbej
            #     - km - kb + ke + kj = 0
            #  => ke = km - kj + kb
            ke = kconserv[km, kj, kb]
            tmp = einsum('imae,mbej->ijab', t2[ki, km, ka], Wovvo[km, kb, ke])
            #     - km - kb + ke + kj = 0
            # =>  ke = km - kj + kb
            #
            # t[i,e] => ki = ke
            # t[m,a] => km = ka
            if km == ka and ke == ki:
                tmp -= einsum('ie,ma,mbej->ijab', t1[ki], t1[km], eris_ovvo[km, kb, ke])
            t2new[ki, kj, ka] += tmp
            t2new[ki, kj, kb] -= tmp.transpose(0, 1, 3, 2)
            t2new[kj, ki, ka] -= tmp.transpose(1, 0, 2, 3)
            t2new[kj, ki, kb] += tmp.transpose(1, 0, 3, 2)

        ke = ki
        tmp = einsum('ie,abej->ijab', t1[ki], eris_vvvo[ka, kb, ke])
        t2new[ki, kj, ka] += tmp
        # P(ij) term
        ke = kj
        tmp = einsum('je,abei->ijab', t1[kj], eris_vvvo[ka, kb, ke])
        t2new[ki, kj, ka] -= tmp

        km = ka
        tmp = einsum('ma,mbij->ijab', t1[ka], eris.ovoo[km, kb, ki])
        t2new[ki, kj, ka] -= tmp
        # P(ab) term
        km = kb
        tmp = einsum('mb,maij->ijab', t1[kb], eris.ovoo[km, ka, ki])
        t2new[ki, kj, ka] += tmp

    for ki in range(nkpts):
        ka = ki
        # Remove zero/padded elements from denominator
        eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia]
        t1new[ki] /= eia

    kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts)
    for ki, kj, ka in kpts_helper.loop_kkk(nkpts):
        kb = kconserv[ki, ka, kj]
        # For LARGE_DENOM, see t1new update above
        eia = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_ia = numpy.ix_(nonzero_opadding[ki], nonzero_vpadding[ka])
        eia[n0_ovp_ia] = (mo_e_o[ki][:,None] - mo_e_v[ka])[n0_ovp_ia]

        ejb = LARGE_DENOM * numpy.ones((nocc, nvir), dtype=eris.mo_energy[0].dtype)
        n0_ovp_jb = numpy.ix_(nonzero_opadding[kj], nonzero_vpadding[kb])
        ejb[n0_ovp_jb] = (mo_e_o[kj][:,None] - mo_e_v[kb])[n0_ovp_jb]
        eijab = eia[:, None, :, None] + ejb[:, None, :]

        t2new[ki, kj, ka] /= eijab

    time0 = log.timer_debug1('update t1 t2', *time0)

    return t1new, t2new
Ejemplo n.º 20
0
def _make_eris_incore(cc, mo_coeff=None):
    from pyscf.pbc import tools
    from pyscf.pbc.cc.ccsd import _adjust_occ

    log = logger.Logger(cc.stdout, cc.verbose)
    cput0 = (time.clock(), time.time())
    eris = gccsd._PhysicistsERIs()
    cell = cc._scf.cell
    kpts = cc.kpts
    nkpts = cc.nkpts
    nocc = cc.nocc
    nmo = cc.nmo
    nvir = nmo - nocc
    eris.nocc = nocc

    #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)):
    #    raise NotImplementedError('Different occupancies found for different k-points')

    if mo_coeff is None:
        mo_coeff = cc.mo_coeff

    nao = mo_coeff[0].shape[0]
    dtype = mo_coeff[0].dtype

    moidx = get_frozen_mask(cc)
    nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True))
    nmo_per_kpt  = numpy.asarray(get_nmo(cc, per_kpoint=True))

    padded_moidx = []
    for k in range(nkpts):
        kpt_nocc = nocc_per_kpt[k]
        kpt_nvir = nmo_per_kpt[k] - kpt_nocc
        kpt_padded_moidx = numpy.concatenate((numpy.ones(kpt_nocc, dtype=numpy.bool),
                                              numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool),
                                              numpy.ones(kpt_nvir, dtype=numpy.bool)))
        padded_moidx.append(kpt_padded_moidx)

    eris.mo_coeff = []
    eris.orbspin = []
    # Generate the molecular orbital coefficients with the frozen orbitals masked.
    # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall
    # spin of each MO.
    #
    # Here we will work with two index arrays; one is for our original (small) moidx
    # array while the next is for our new (large) padded array.
    for k in range(nkpts):
        kpt_moidx = moidx[k]
        kpt_padded_moidx = padded_moidx[k]

        mo = numpy.zeros((nao, nmo), dtype=dtype)
        mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx]
        if getattr(mo_coeff[k], 'orbspin', None) is not None:
            orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype
            orbspin = numpy.zeros(nmo, dtype=orbspin_dtype)
            orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx]
            mo = lib.tag_array(mo, orbspin=orbspin)
            eris.orbspin.append(orbspin)
        # FIXME: What if the user freezes all up spin orbitals in
        # an RHF calculation?  The number of electrons will still be
        # even.
        else:  # guess orbital spin - assumes an RHF calculation
            assert (numpy.count_nonzero(kpt_moidx) % 2 == 0)
            orbspin = numpy.zeros(mo.shape[1], dtype=int)
            orbspin[1::2] = 1
            mo = lib.tag_array(mo, orbspin=orbspin)
            eris.orbspin.append(orbspin)
        eris.mo_coeff.append(mo)

    # Re-make our fock MO matrix elements from density and fock AO
    dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ)
    with lib.temporary_env(cc._scf, exxdiv=None):
        # _scf.exxdiv affects eris.fock. HF exchange correction should be
        # excluded from the Fock matrix.
        fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm)
    eris.fock = numpy.asarray([reduce(numpy.dot, (mo.T.conj(), fockao[k], mo))
                               for k, mo in enumerate(eris.mo_coeff)])

    eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)]
    # Add HFX correction in the eris.mo_energy to improve convergence in
    # CCSD iteration. It is useful for the 2D systems since their occupied and
    # the virtual orbital energies may overlap which may lead to numerical
    # issue in the CCSD iterations.
    # FIXME: Whether to add this correction for other exxdiv treatments?
    # Without the correction, MP2 energy may be largely off the correct value.
    madelung = tools.madelung(cell, kpts)
    eris.mo_energy = [_adjust_occ(mo_e, nocc, -madelung)
                      for k, mo_e in enumerate(eris.mo_energy)]

    # Get location of padded elements in occupied and virtual space.
    nocc_per_kpt = get_nocc(cc, per_kpoint=True)
    nonzero_padding = padding_k_idx(cc, kind="joint")

    # Check direct and indirect gaps for possible issues with CCSD convergence.
    mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)]
    mo_e = numpy.sort([y for x in mo_e for y in x])  # Sort de-nested array
    gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt)-1]
    if gap < 1e-5:
        logger.warn(cc, 'H**O-LUMO gap %s too small for KCCSD. '
                        'May cause issues in convergence.', gap)

    kconserv = kpts_helper.get_kconserv(cell, kpts)
    if getattr(mo_coeff[0], 'orbspin', None) is None:
        # The bottom nao//2 coefficients are down (up) spin while the top are up (down).
        mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff]
        mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff]

        eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128)
        fao2mo = cc._scf.with_df.ao2mo
        for kp, kq, kr in kpts_helper.loop_kkk(nkpts):
            ks = kconserv[kp, kq, kr]
            eri_kpt = fao2mo(
                (mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                compact=False)
            eri_kpt += fao2mo(
                (mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                compact=False)
            eri_kpt += fao2mo(
                (mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                compact=False)
            eri_kpt += fao2mo(
                (mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                compact=False)

            eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
            eri[kp, kq, kr] = eri_kpt
    else:
        mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff]

        eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128)
        fao2mo = cc._scf.with_df.ao2mo
        for kp, kq, kr in kpts_helper.loop_kkk(nkpts):
            ks = kconserv[kp, kq, kr]
            eri_kpt = fao2mo(
                (mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                compact=False)

            eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0
            eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0
            eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
            eri[kp, kq, kr] = eri_kpt

    # Check some antisymmetrized properties of the integrals
    if DEBUG:
        check_antisymm_3412(cc, cc.kpts, eri)

    # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to
    # (rq|ps); done since we aren't tracking the kpoint of orbital 's'
    eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6)
    # Chemist -> physics notation
    eri = eri.transpose(0, 2, 1, 3, 5, 4, 6)

    # Set the various integrals
    eris.dtype = eri.dtype
    eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts
    eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts
    eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts
    eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts
    eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts
    eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts
    eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts

    log.timer('CCSD integral transformation', *cput0)
    return eris
Ejemplo n.º 21
0
def _make_eris_incore(cc, mo_coeff=None):
    from pyscf.pbc import tools
    from pyscf.pbc.cc.ccsd import _adjust_occ

    log = logger.Logger(cc.stdout, cc.verbose)
    cput0 = (time.clock(), time.time())
    eris = gccsd._PhysicistsERIs()
    cell = cc._scf.cell
    kpts = cc.kpts
    nkpts = cc.nkpts
    nocc = cc.nocc
    nmo = cc.nmo
    nvir = nmo - nocc
    eris.nocc = nocc

    #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)):
    #    raise NotImplementedError('Different occupancies found for different k-points')

    if mo_coeff is None:
        mo_coeff = cc.mo_coeff

    nao = mo_coeff[0].shape[0]
    dtype = mo_coeff[0].dtype

    moidx = get_frozen_mask(cc)
    nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True))
    nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True))

    padded_moidx = []
    for k in range(nkpts):
        kpt_nocc = nocc_per_kpt[k]
        kpt_nvir = nmo_per_kpt[k] - kpt_nocc
        kpt_padded_moidx = numpy.concatenate(
            (numpy.ones(kpt_nocc, dtype=numpy.bool),
             numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool),
             numpy.ones(kpt_nvir, dtype=numpy.bool)))
        padded_moidx.append(kpt_padded_moidx)

    eris.mo_coeff = []
    eris.orbspin = []
    # Generate the molecular orbital coefficients with the frozen orbitals masked.
    # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall
    # spin of each MO.
    #
    # Here we will work with two index arrays; one is for our original (small) moidx
    # array while the next is for our new (large) padded array.
    for k in range(nkpts):
        kpt_moidx = moidx[k]
        kpt_padded_moidx = padded_moidx[k]

        mo = numpy.zeros((nao, nmo), dtype=dtype)
        mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx]
        if getattr(mo_coeff[k], 'orbspin', None) is not None:
            orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype
            orbspin = numpy.zeros(nmo, dtype=orbspin_dtype)
            orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx]
            mo = lib.tag_array(mo, orbspin=orbspin)
            eris.orbspin.append(orbspin)
        # FIXME: What if the user freezes all up spin orbitals in
        # an RHF calculation?  The number of electrons will still be
        # even.
        else:  # guess orbital spin - assumes an RHF calculation
            assert (numpy.count_nonzero(kpt_moidx) % 2 == 0)
            orbspin = numpy.zeros(mo.shape[1], dtype=int)
            orbspin[1::2] = 1
            mo = lib.tag_array(mo, orbspin=orbspin)
            eris.orbspin.append(orbspin)
        eris.mo_coeff.append(mo)

    # Re-make our fock MO matrix elements from density and fock AO
    dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ)
    with lib.temporary_env(cc._scf, exxdiv=None):
        # _scf.exxdiv affects eris.fock. HF exchange correction should be
        # excluded from the Fock matrix.
        fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm)
    eris.fock = numpy.asarray([
        reduce(numpy.dot, (mo.T.conj(), fockao[k], mo))
        for k, mo in enumerate(eris.mo_coeff)
    ])

    eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)]
    # Add HFX correction in the eris.mo_energy to improve convergence in
    # CCSD iteration. It is useful for the 2D systems since their occupied and
    # the virtual orbital energies may overlap which may lead to numerical
    # issue in the CCSD iterations.
    # FIXME: Whether to add this correction for other exxdiv treatments?
    # Without the correction, MP2 energy may be largely off the correct value.
    madelung = tools.madelung(cell, kpts)
    eris.mo_energy = [
        _adjust_occ(mo_e, nocc, -madelung)
        for k, mo_e in enumerate(eris.mo_energy)
    ]

    # Get location of padded elements in occupied and virtual space.
    nocc_per_kpt = get_nocc(cc, per_kpoint=True)
    nonzero_padding = padding_k_idx(cc, kind="joint")

    # Check direct and indirect gaps for possible issues with CCSD convergence.
    mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)]
    mo_e = numpy.sort([y for x in mo_e for y in x])  # Sort de-nested array
    gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt) - 1]
    if gap < 1e-5:
        logger.warn(
            cc, 'H**O-LUMO gap %s too small for KCCSD. '
            'May cause issues in convergence.', gap)

    kconserv = kpts_helper.get_kconserv(cell, kpts)
    if getattr(mo_coeff[0], 'orbspin', None) is None:
        # The bottom nao//2 coefficients are down (up) spin while the top are up (down).
        mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff]
        mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff]

        eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo),
                          dtype=numpy.complex128)
        fao2mo = cc._scf.with_df.ao2mo
        for kp, kq, kr in kpts_helper.loop_kkk(nkpts):
            ks = kconserv[kp, kq, kr]
            eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr],
                              mo_a_coeff[ks]),
                             (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                             compact=False)
            eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr],
                               mo_b_coeff[ks]),
                              (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                              compact=False)
            eri_kpt += fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr],
                               mo_b_coeff[ks]),
                              (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                              compact=False)
            eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr],
                               mo_a_coeff[ks]),
                              (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                              compact=False)

            eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
            eri[kp, kq, kr] = eri_kpt
    else:
        mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff]

        eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo),
                          dtype=numpy.complex128)
        fao2mo = cc._scf.with_df.ao2mo
        for kp, kq, kr in kpts_helper.loop_kkk(nkpts):
            ks = kconserv[kp, kq, kr]
            eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr],
                              mo_a_coeff[ks]),
                             (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                             compact=False)

            eri_kpt[(eris.orbspin[kp][:, None] !=
                     eris.orbspin[kq]).ravel()] = 0
            eri_kpt[:,
                    (eris.orbspin[kr][:,
                                      None] != eris.orbspin[ks]).ravel()] = 0
            eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
            eri[kp, kq, kr] = eri_kpt

    # Check some antisymmetrized properties of the integrals
    if DEBUG:
        check_antisymm_3412(cc, cc.kpts, eri)

    # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to
    # (rq|ps); done since we aren't tracking the kpoint of orbital 's'
    eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6)
    # Chemist -> physics notation
    eri = eri.transpose(0, 2, 1, 3, 5, 4, 6)

    # Set the various integrals
    eris.dtype = eri.dtype
    eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts
    eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts
    eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts
    eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts
    eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts
    eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts
    eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts

    log.timer('CCSD integral transformation', *cput0)
    return eris
Ejemplo n.º 22
0
def assert_non_padded(cc, p, kp):
    if p not in padding_k_idx(cc, kind="joint")[kp]:
        raise PaddingError("The index p={:d} at k={:d} is padded".format(
            p, kp))
Ejemplo n.º 23
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)
    cpu1 = cpu0 = (time.clock(), time.time())
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mycc.stdout, verbose)

    if t1 is None: t1 = mycc.t1
    if t2 is None: t2 = mycc.t2

    if eris is None:
        raise TypeError(
            'Electron repulsion integrals, `eris`, must be passed in '
            'to the CCSD(T) kernel or created in the cc object for '
            'the k-point CCSD(T) to run!')
    if t1 is None or t2 is None:
        raise TypeError(
            'Must pass in t1/t2 amplitudes to k-point CCSD(T)! (Maybe '
            'need to run `.ccsd()` on the ccsd object?)')

    cell = mycc._scf.cell
    kpts = mycc.kpts

    # The dtype of any local arrays that will be created
    dtype = t1.dtype

    nkpts, nocc, nvir = t1.shape

    mo_energy_occ = [eris.mo_energy[ki][:nocc] for ki in range(nkpts)]
    mo_energy_vir = [eris.mo_energy[ki][nocc:] for ki in range(nkpts)]
    mo_energy = np.asarray([eris.mo_energy[ki] for ki in range(nkpts)],
                           dtype=np.float,
                           order='C')
    fov = eris.fock[:, :nocc, nocc:]

    mo_e = mo_energy
    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    # Set up class for k-point conservation
    kconserv = kpts_helper.get_kconserv(cell, kpts)

    # Create necessary temporary eris for fast read
    feri_tmp, t2T, eris_vvop, eris_vooo_C = create_t3_eris(
        mycc, kconserv, [eris.vovv, eris.oovv, eris.ooov, t2])
    t1T = np.array([x.T for x in t1], dtype=np.complex, order='C')
    fvo = np.array([x.T for x in fov], dtype=np.complex, order='C')
    cpu1 = log.timer_debug1('CCSD(T) tmp eri creation', *cpu1)

    #def get_w_old(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1, out=None):
    #    '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts'''
    #    km = kconserv[kc, kk, kb]
    #    kf = kconserv[kk, kc, kj]
    #    ret = einsum('kjcf,fiba->abcijk', t2[kk,kj,kc,:,:,c0:c1,:], eris.vovv[kf,ki,kb,:,:,b0:b1,a0:a1].conj())
    #    ret = ret - einsum('mkbc,jima->abcijk', t2[km,kk,kb,:,:,b0:b1,c0:c1], eris.ooov[kj,ki,km,:,:,:,a0:a1].conj())
    #    return ret

    def get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1):
        '''Wijkabc intermediate as described in Scuseria paper before Pijkabc acts

        Uses tranposed eris for fast data access.'''
        km = kconserv[kc, kk, kb]
        kf = kconserv[kk, kc, kj]
        out = einsum('cfjk,abif->abcijk', t2T[kc, kf, kj, c0:c1, :, :, :],
                     eris_vvop[ka, kb, ki, a0:a1, b0:b1, :, nocc:])
        out = out - einsum('cbmk,aijm->abcijk', t2T[kc, kb, km, c0:c1,
                                                    b0:b1, :, :],
                           eris_vooo_C[ka, ki, kj, a0:a1, :, :, :])
        return out

    def get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices):
        '''Pijkabc operating on Wijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        out = get_w(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1)
        out = out + get_w(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0,
                          a1).transpose(2, 0, 1, 5, 3, 4)
        out = out + get_w(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0,
                          b1).transpose(1, 2, 0, 4, 5, 3)
        out = out + get_w(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0,
                          b1).transpose(0, 2, 1, 3, 5, 4)
        out = out + get_w(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0,
                          a1).transpose(2, 1, 0, 5, 4, 3)
        out = out + get_w(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0,
                          c1).transpose(1, 0, 2, 4, 3, 5)
        return out

    def get_rw(ki, kj, kk, ka, kb, kc, orb_indices):
        '''R operating on Wijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        ret = (4. * get_permuted_w(ki, kj, kk, ka, kb, kc, orb_indices) +
               1. * get_permuted_w(kj, kk, ki, ka, kb, kc,
                                   orb_indices).transpose(0, 1, 2, 5, 3, 4) +
               1. * get_permuted_w(kk, ki, kj, ka, kb, kc,
                                   orb_indices).transpose(0, 1, 2, 4, 5, 3) -
               2. * get_permuted_w(ki, kk, kj, ka, kb, kc,
                                   orb_indices).transpose(0, 1, 2, 3, 5, 4) -
               2. * get_permuted_w(kk, kj, ki, ka, kb, kc,
                                   orb_indices).transpose(0, 1, 2, 5, 4, 3) -
               2. * get_permuted_w(kj, ki, kk, ka, kb, kc,
                                   orb_indices).transpose(0, 1, 2, 4, 3, 5))
        return ret

    #def get_v_old(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1):
    #    '''Vijkabc intermediate as described in Scuseria paper'''
    #    km = kconserv[ki,ka,kj]
    #    kf = kconserv[ki,ka,kj]
    #    out = np.zeros((a1-a0,b1-b0,c1-c0) + (nocc,)*3, dtype=dtype)
    #    if kk == kc:
    #        out = out + einsum('kc,ijab->abcijk', 0.5*t1[kk,:,c0:c1], eris.oovv[ki,kj,ka,:,:,a0:a1,b0:b1].conj())
    #        out = out + einsum('kc,ijab->abcijk', 0.5*fov[kk,:,c0:c1], t2[ki,kj,ka,:,:,a0:a1,b0:b1])
    #    return out

    def get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1):
        '''Vijkabc intermediate as described in Scuseria paper'''
        km = kconserv[ki, ka, kj]
        kf = kconserv[ki, ka, kj]
        out = np.zeros((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype)
        if kk == kc:
            out = out + einsum('ck,baji->abcijk', 0.5 * t1T[kk, c0:c1, :],
                               eris_vvop[kb, ka, kj, b0:b1, a0:a1, :, :nocc])
            # We see this is the same t2T term needed for the `w` contraction:
            #     einsum('cbmk,aijm->abcijk', t2T[kc,kb,km,c0:c1,b0:b1], eris_vooo_C[ka,ki,kj,a0:a1])
            #
            # For the kpoint indices [kk,ki,kj,kc,ka,kb] we have that we need
            #     t2T[kb,ka,km], where km = kconserv[kb,kj,ka]
            # The remaining k-point not used in t2T, i.e. kc, has the condition kc == kk in the case of
            # get_v.  So, we have from 3-particle conservation
            #     (kk-kc) + ki + kj - ka - kb = 0,
            # i.e. ki = km.
            out = out + einsum('ck,baij->abcijk', 0.5 * fvo[kk, c0:c1, :],
                               t2T[kb, ka, ki, b0:b1, a0:a1, :, :])
        return out

    def get_permuted_v(ki, kj, kk, ka, kb, kc, orb_indices):
        '''Pijkabc operating on Vijkabc intermediate as described in Scuseria paper'''
        a0, a1, b0, b1, c0, c1 = orb_indices
        tmp = np.zeros((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3, dtype=dtype)
        ret = get_v(ki, kj, kk, ka, kb, kc, a0, a1, b0, b1, c0, c1)
        ret = ret + get_v(kj, kk, ki, kb, kc, ka, b0, b1, c0, c1, a0,
                          a1).transpose(2, 0, 1, 5, 3, 4)
        ret = ret + get_v(kk, ki, kj, kc, ka, kb, c0, c1, a0, a1, b0,
                          b1).transpose(1, 2, 0, 4, 5, 3)
        ret = ret + get_v(ki, kk, kj, ka, kc, kb, a0, a1, c0, c1, b0,
                          b1).transpose(0, 2, 1, 3, 5, 4)
        ret = ret + get_v(kk, kj, ki, kc, kb, ka, c0, c1, b0, b1, a0,
                          a1).transpose(2, 1, 0, 5, 4, 3)
        ret = ret + get_v(kj, ki, kk, kb, ka, kc, b0, b1, a0, a1, c0,
                          c1).transpose(1, 0, 2, 4, 3, 5)
        return ret

    def contract_t3Tv(kpt_indices, orb_indices, data):
        '''Calculate t3T(ransposed) array using C driver.'''
        ki, kj, kk, ka, kb, kc = kpt_indices
        a0, a1, b0, b1, c0, c1 = orb_indices
        slices = np.array([a0, a1, b0, b1, c0, c1], dtype=np.int32)

        mo_offset = np.array([ki, kj, kk, ka, kb, kc], dtype=np.int32)

        vvop_ab = np.asarray(data[0][0], dtype=np.complex, order='C')
        vvop_ac = np.asarray(data[0][1], dtype=np.complex, order='C')
        vvop_ba = np.asarray(data[0][2], dtype=np.complex, order='C')
        vvop_bc = np.asarray(data[0][3], dtype=np.complex, order='C')
        vvop_ca = np.asarray(data[0][4], dtype=np.complex, order='C')
        vvop_cb = np.asarray(data[0][5], dtype=np.complex, order='C')

        vooo_aj = np.asarray(data[1][0], dtype=np.complex, order='C')
        vooo_ak = np.asarray(data[1][1], dtype=np.complex, order='C')
        vooo_bi = np.asarray(data[1][2], dtype=np.complex, order='C')
        vooo_bk = np.asarray(data[1][3], dtype=np.complex, order='C')
        vooo_ci = np.asarray(data[1][4], dtype=np.complex, order='C')
        vooo_cj = np.asarray(data[1][5], dtype=np.complex, order='C')

        t2T_cj = np.asarray(data[2][0], dtype=np.complex, order='C')
        t2T_bk = np.asarray(data[2][1], dtype=np.complex, order='C')
        t2T_ci = np.asarray(data[2][2], dtype=np.complex, order='C')
        t2T_ak = np.asarray(data[2][3], dtype=np.complex, order='C')
        t2T_bi = np.asarray(data[2][4], dtype=np.complex, order='C')
        t2T_aj = np.asarray(data[2][5], dtype=np.complex, order='C')

        t2T_cb = np.asarray(data[3][0], dtype=np.complex, order='C')
        t2T_bc = np.asarray(data[3][1], dtype=np.complex, order='C')
        t2T_ca = np.asarray(data[3][2], dtype=np.complex, order='C')
        t2T_ac = np.asarray(data[3][3], dtype=np.complex, order='C')
        t2T_ba = np.asarray(data[3][4], dtype=np.complex, order='C')
        t2T_ab = np.asarray(data[3][5], dtype=np.complex, order='C')

        data = [
            vvop_ab, vvop_ac, vvop_ba, vvop_bc, vvop_ca, vvop_cb, vooo_aj,
            vooo_ak, vooo_bi, vooo_bk, vooo_ci, vooo_cj, t2T_cj, t2T_cb,
            t2T_bk, t2T_bc, t2T_ci, t2T_ca, t2T_ak, t2T_ac, t2T_bi, t2T_ba,
            t2T_aj, t2T_ab
        ]
        data_ptrs = [x.ctypes.data_as(ctypes.c_void_p) for x in data]
        data_ptrs = (ctypes.c_void_p * 24)(*data_ptrs)

        a0, a1, b0, b1, c0, c1 = task
        t3Tw = np.empty((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3,
                        dtype=np.complex,
                        order='C')
        t3Tv = np.empty((a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3,
                        dtype=np.complex,
                        order='C')

        drv = _ccsd.libcc.CCsd_zcontract_t3T
        drv(t3Tw.ctypes.data_as(ctypes.c_void_p),
            t3Tv.ctypes.data_as(ctypes.c_void_p),
            mo_e.ctypes.data_as(ctypes.c_void_p),
            t1T.ctypes.data_as(ctypes.c_void_p),
            fvo.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nocc),
            ctypes.c_int(nvir), ctypes.c_int(nkpts),
            mo_offset.ctypes.data_as(ctypes.c_void_p),
            slices.ctypes.data_as(ctypes.c_void_p), data_ptrs)
        return t3Tw, t3Tv

    def get_data(kpt_indices):
        idx_args = get_data_slices(kpt_indices, task, kconserv)
        vvop_indices, vooo_indices, t2T_vvop_indices, t2T_vooo_indices = idx_args
        vvop_data = [eris_vvop[tuple(x)] for x in vvop_indices]
        vooo_data = [eris_vooo_C[tuple(x)] for x in vooo_indices]
        t2T_vvop_data = [t2T[tuple(x)] for x in t2T_vvop_indices]
        t2T_vooo_data = [t2T[tuple(x)] for x in t2T_vooo_indices]
        data = [vvop_data, vooo_data, t2T_vvop_data, t2T_vooo_data]
        return data

    energy_t = 0.0

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(mycc, kind="split")

    mem_now = lib.current_memory()[0]
    max_memory = max(0, mycc.max_memory - mem_now)
    blkmin = 4
    # temporary t3 array is size:    2 * nkpts**3 * blksize**3 * nocc**3 * 16
    vir_blksize = min(
        nvir,
        max(blkmin,
            int((max_memory * .9e6 / 16 / nocc**3 / nkpts**3 / 2)**(1. / 3))))
    tasks = []
    log.debug('max_memory %d MB (%d MB in use)', max_memory, mem_now)
    log.debug('virtual blksize = %d (nvir = %d)', nvir, vir_blksize)
    for a0, a1 in lib.prange(0, nvir, vir_blksize):
        for b0, b1 in lib.prange(0, nvir, vir_blksize):
            for c0, c1 in lib.prange(0, nvir, vir_blksize):
                tasks.append((a0, a1, b0, b1, c0, c1))

    for ka in range(nkpts):
        for kb in range(ka + 1):
            for task_id, task in enumerate(tasks):
                a0, a1, b0, b1, c0, c1 = task
                my_permuted_w = np.zeros(
                    (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3,
                    dtype=dtype)
                my_permuted_v = np.zeros(
                    (nkpts, ) * 3 + (a1 - a0, b1 - b0, c1 - c0) + (nocc, ) * 3,
                    dtype=dtype)
                for ki, kj, kk in product(range(nkpts), repeat=3):
                    # Find momentum conservation condition for triples
                    # amplitude t3ijkabc
                    kc = kpts_helper.get_kconserv3(cell, kpts,
                                                   [ki, kj, kk, ka, kb])
                    if not (ka >= kb and kb >= kc):
                        continue

                    kpt_indices = [ki, kj, kk, ka, kb, kc]
                    data = get_data(kpt_indices)
                    t3Tw, t3Tv = contract_t3Tv(kpt_indices, task, data)
                    my_permuted_w[ki, kj, kk] = t3Tw
                    my_permuted_v[ki, kj, kk] = t3Tv
                    #my_permuted_w[ki,kj,kk] = get_permuted_w(ki,kj,kk,ka,kb,kc,task)
                    #my_permuted_v[ki,kj,kk] = get_permuted_v(ki,kj,kk,ka,kb,kc,task)

                for ki, kj, kk in product(range(nkpts), repeat=3):
                    # eigenvalue denominator: e(i) + e(j) + e(k)
                    eijk = _get_epqr([0, nocc, ki, mo_e_o, nonzero_opadding],
                                     [0, nocc, kj, mo_e_o, nonzero_opadding],
                                     [0, nocc, kk, mo_e_o, nonzero_opadding])

                    # Find momentum conservation condition for triples
                    # amplitude t3ijkabc
                    kc = kpts_helper.get_kconserv3(cell, kpts,
                                                   [ki, kj, kk, ka, kb])
                    if not (ka >= kb and kb >= kc):
                        continue

                    if ka == kb and kb == kc:
                        symm_kpt = 1.
                    elif ka == kb or kb == kc:
                        symm_kpt = 3.
                    else:
                        symm_kpt = 6.

                    eabc = _get_epqr([a0, a1, ka, mo_e_v, nonzero_vpadding],
                                     [b0, b1, kb, mo_e_v, nonzero_vpadding],
                                     [c0, c1, kc, mo_e_v, nonzero_vpadding],
                                     fac=[-1., -1., -1.])
                    eijkabc = (eijk[None, None, None, :, :, :] +
                               eabc[:, :, :, None, None, None])

                    pwijk = my_permuted_w[ki, kj, kk] + my_permuted_v[ki, kj,
                                                                      kk]
                    rwijk = (
                        4. * my_permuted_w[ki, kj, kk] + 1. *
                        my_permuted_w[kj, kk, ki].transpose(0, 1, 2, 5, 3, 4) +
                        1. *
                        my_permuted_w[kk, ki, kj].transpose(0, 1, 2, 4, 5, 3) -
                        2. *
                        my_permuted_w[ki, kk, kj].transpose(0, 1, 2, 3, 5, 4) -
                        2. *
                        my_permuted_w[kk, kj, ki].transpose(0, 1, 2, 5, 4, 3) -
                        2. *
                        my_permuted_w[kj, ki, kk].transpose(0, 1, 2, 4, 3, 5))
                    rwijk = rwijk / eijkabc
                    energy_t += symm_kpt * einsum('abcijk,abcijk', rwijk,
                                                  pwijk.conj())

    energy_t *= (1. / 3)
    energy_t /= nkpts

    if abs(energy_t.imag) > 1e-4:
        log.warn('Non-zero imaginary part of CCSD(T) energy was found %s',
                 energy_t.imag)
    log.timer('CCSD(T)', *cpu0)
    log.note('CCSD(T) correction per cell = %.15g', energy_t.real)
    log.note('CCSD(T) correction per cell (imag) = %.15g', energy_t.imag)
    return energy_t.real
Ejemplo n.º 24
0
    def __init__(self, cc, mo_coeff=None):
        from pyscf.pbc.cc.ccsd import _adjust_occ
        import pyscf.pbc.tools.pbc as tools
        if mo_coeff is None:
            mo_coeff = cc.mo_coeff
        cput0 = (time.clock(), time.time())
        log = Logger(cc.stdout, cc.verbose)
        self.lib = lib
        nocc, nmo, nkpts = cc.nocc, cc.nmo, cc.nkpts
        nvir = nmo - nocc
        cell, kpts = cc._scf.cell, cc.kpts
        gvec = cell.reciprocal_vectors()
        sym1 = ['+-', [
            kpts,
        ] * 2, None, gvec]
        sym2 = ['+-+-', [
            kpts,
        ] * 4, None, gvec]
        mo_coeff = self.mo_coeff = padded_mo_coeff(cc, mo_coeff)
        nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")
        madelung = tools.madelung(cell, kpts)

        dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ)
        with pyscflib.temporary_env(cc._scf, exxdiv=None):
            fockao = cc._scf.get_hcore() + cc._scf.get_veff(cell, dm)
        fock = np.asarray([
            reduce(np.dot, (mo.T.conj(), fockao[k], mo))
            for k, mo in enumerate(mo_coeff)
        ])

        self.dtype = dtype = np.result_type(*fock).char
        self.foo = tensor(fock[:, :nocc, :nocc], sym1)
        self.fov = tensor(fock[:, :nocc, nocc:], sym1)
        self.fvv = tensor(fock[:, nocc:, nocc:], sym1)

        mo_energy = [fock[k].diagonal().real for k in range(nkpts)]
        mo_energy = [
            _adjust_occ(mo_e, nocc, -madelung)
            for k, mo_e in enumerate(mo_energy)
        ]

        mo_e_o = [e[:nocc] for e in mo_energy]
        mo_e_v = [e[nocc:] + cc.level_shift for e in mo_energy]
        foo_ = np.asarray([np.diag(e) for e in mo_e_o])
        fvv_ = np.asarray([np.diag(e) for e in mo_e_v])
        self._foo = tensor(foo_, sym1)
        self._fvv = tensor(fvv_, sym1)

        eia = np.zeros([nkpts, nocc, nvir])
        for ki in range(nkpts):
            eia[ki] = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding],
                               [0, nvir, ki, mo_e_v, nonzero_vpadding],
                               fac=[1.0, -1.0])

        self.eia = tensor(eia, sym1)
        self.oooo = zeros([nocc, nocc, nocc, nocc], dtype, sym2)
        self.ooov = zeros([nocc, nocc, nocc, nvir], dtype, sym2)
        self.ovov = zeros([nocc, nvir, nocc, nvir], dtype, sym2)
        self.oovv = zeros([nocc, nocc, nvir, nvir], dtype, sym2)
        self.ovvo = zeros([nocc, nvir, nvir, nocc], dtype, sym2)
        self.ovvv = zeros([nocc, nvir, nvir, nvir], dtype, sym2)
        self.vvvv = zeros([nvir, nvir, nvir, nvir], dtype, sym2)
        self.eijab = zeros([nocc, nocc, nvir, nvir], np.float64, sym2)

        with_df = cc._scf.with_df
        fao2mo = cc._scf.with_df.ao2mo
        kconserv = cc.khelper.kconserv
        khelper = cc.khelper

        jobs = list(khelper.symm_map.keys())
        for itask in jobs:
            ikp, ikq, ikr = itask
            iks = kconserv[ikp, ikq, ikr]
            eri_kpt = fao2mo(
                (mo_coeff[ikp], mo_coeff[ikq], mo_coeff[ikr], mo_coeff[iks]),
                (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]),
                compact=False)
            if dtype == np.float: eri_kpt = eri_kpt.real
            eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) / nkpts
            done = np.zeros([nkpts, nkpts, nkpts])
            for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]:
                if done[kp, kq, kr]: continue
                eri_kpt_symm = khelper.transform_symm(eri_kpt, kp, kq, kr)
                oooo = eri_kpt_symm[:nocc, :nocc, :nocc, :nocc]
                ooov = eri_kpt_symm[:nocc, :nocc, :nocc, nocc:]
                ovov = eri_kpt_symm[:nocc, nocc:, :nocc, nocc:]
                oovv = eri_kpt_symm[:nocc, :nocc, nocc:, nocc:]
                ovvo = eri_kpt_symm[:nocc, nocc:, nocc:, :nocc]
                ovvv = eri_kpt_symm[:nocc, nocc:, nocc:, nocc:]
                vvvv = eri_kpt_symm[nocc:, nocc:, nocc:, nocc:]
                ks = kconserv[kp, kq, kr]
                eia = _get_epq([0, nocc, kp, mo_e_o, nonzero_opadding],
                               [0, nvir, kq, mo_e_v, nonzero_vpadding],
                               fac=[1.0, -1.0])
                ejb = _get_epq([0, nocc, kr, mo_e_o, nonzero_opadding],
                               [0, nvir, ks, mo_e_v, nonzero_vpadding],
                               fac=[1.0, -1.0])
                eijab = eia[:, None, :, None] + ejb[None, :, None, :]

                self.oooo.array[kp, kq, kr] = oooo
                self.ooov.array[kp, kq, kr] = ooov
                self.ovov.array[kp, kq, kr] = ovov
                self.oovv.array[kp, kq, kr] = oovv
                self.ovvo.array[kp, kq, kr] = ovvo
                self.ovvv.array[kp, kq, kr] = ovvv
                self.vvvv.array[kp, kq, kr] = vvvv
                self.eijab.array[kp, kr, kq] = eijab
                done[kp, kq, kr] = 1

        log.timer("ao2mo transformation", *cput0)
Ejemplo n.º 25
0
def get_t3p2_imds_slow(cc,
                       t1,
                       t2,
                       eris=None,
                       t3p2_ip_out=None,
                       t3p2_ea_out=None):
    """Calculates T1, T2 amplitudes corrected by second-order T3 contribution
    and intermediates used in IP/EA-CCSD(T)a

    Args:
        cc (:obj:`KGCCSD`):
            Object containing coupled-cluster results.
        t1 (:obj:`ndarray`):
            T1 amplitudes.
        t2 (:obj:`ndarray`):
            T2 amplitudes from which the T3[2] amplitudes are formed.
        eris (:obj:`_PhysicistsERIs`):
            Antisymmetrized electron-repulsion integrals in physicist's notation.
        t3p2_ip_out (:obj:`ndarray`):
            Store results of the intermediate used in IP-EOM-CCSD(T)a.
        t3p2_ea_out (:obj:`ndarray`):
            Store results of the intermediate used in EA-EOM-CCSD(T)a.

    Returns:
        delta_ccsd (float):
            Difference of perturbed and unperturbed CCSD ground-state energy,
                energy(T1 + T1[2], T2 + T2[2]) - energy(T1, T2)
        pt1 (:obj:`ndarray`):
            Perturbatively corrected T1 amplitudes.
        pt2 (:obj:`ndarray`):
            Perturbatively corrected T2 amplitudes.

    Reference:
        D. A. Matthews, J. F. Stanton "A new approach to approximate..."
            JCP 145, 124102 (2016); DOI:10.1063/1.4962910, Equation 14
        Shavitt and Bartlett "Many-body Methods in Physics and Chemistry"
            2009, Equation 10.33
    """
    if eris is None:
        eris = cc.ao2mo()
    fock = eris.fock
    nkpts, nocc, nvir = t1.shape
    kconserv = cc.khelper.kconserv

    fov = [fock[ikpt, :nocc, nocc:] for ikpt in range(nkpts)]
    #foo = [fock[ikpt, :nocc, :nocc].diagonal() for ikpt in range(nkpts)]
    #fvv = [fock[ikpt, nocc:, nocc:].diagonal() for ikpt in range(nkpts)]
    mo_energy_occ = numpy.array(
        [eris.mo_energy[ki][:nocc] for ki in range(nkpts)])
    mo_energy_vir = numpy.array(
        [eris.mo_energy[ki][nocc:] for ki in range(nkpts)])

    # Get location of padded elements in occupied and virtual space
    nonzero_opadding, nonzero_vpadding = padding_k_idx(cc, kind="split")

    mo_e_o = mo_energy_occ
    mo_e_v = mo_energy_vir

    ccsd_energy = cc.energy(t1, t2, eris)
    dtype = numpy.result_type(t1, t2)

    if t3p2_ip_out is None:
        t3p2_ip_out = numpy.zeros(
            (nkpts, nkpts, nkpts, nocc, nvir, nocc, nocc), dtype=dtype)
    Wmcik = t3p2_ip_out

    if t3p2_ea_out is None:
        t3p2_ea_out = numpy.zeros(
            (nkpts, nkpts, nkpts, nvir, nvir, nvir, nocc), dtype=dtype)
    Wacek = t3p2_ea_out

    t3 = get_full_t3p2(cc, t1, t2, eris)

    pt1 = numpy.zeros((nkpts, nocc, nvir), dtype=dtype)
    for ki in range(nkpts):
        ka = ki
        for km, kn, ke in product(range(nkpts), repeat=3):
            pt1[ki] += 0.25 * lib.einsum(
                'mnef,imnaef->ia', eris.oovv[km, kn, ke], t3[ki, km, kn, ka,
                                                             ke])
        eia = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding],
                       [0, nvir, ka, mo_e_v, nonzero_vpadding],
                       fac=[1.0, -1.0])
        pt1[ki] /= eia

    pt2 = numpy.zeros((nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir),
                      dtype=dtype)
    for ki, kj, ka in product(range(nkpts), repeat=3):
        kb = kconserv[ki, ka, kj]
        for km in range(nkpts):
            pt2[ki, kj, ka] += lib.einsum('ijmabe,me->ijab', t3[ki, kj, km, ka,
                                                                kb], fov[km])
            for ke in range(nkpts):
                kf = kconserv[km, ke, kb]
                pt2[ki, kj, ka] += 0.5 * lib.einsum(
                    'ijmaef,mbfe->ijab', t3[ki, kj, km, ka,
                                            ke], eris.ovvv[km, kb, kf])
                kf = kconserv[km, ke, ka]
                pt2[ki, kj, ka] -= 0.5 * lib.einsum(
                    'ijmbef,mafe->ijab', t3[ki, kj, km, kb,
                                            ke], eris.ovvv[km, ka, kf])

            for kn in range(nkpts):
                pt2[ki, kj, ka] -= 0.5 * lib.einsum(
                    'inmabe,nmje->ijab', t3[ki, kn, km, ka,
                                            kb], eris.ooov[kn, km, kj])
                pt2[ki, kj, ka] += 0.5 * lib.einsum(
                    'jnmabe,nmie->ijab', t3[kj, kn, km, ka,
                                            kb], eris.ooov[kn, km, ki])

        eia = _get_epq([0, nocc, ki, mo_e_o, nonzero_opadding],
                       [0, nvir, ka, mo_e_v, nonzero_vpadding],
                       fac=[1.0, -1.0])
        ejb = _get_epq([0, nocc, kj, mo_e_o, nonzero_opadding],
                       [0, nvir, kb, mo_e_v, nonzero_vpadding],
                       fac=[1.0, -1.0])
        eijab = eia[:, None, :, None] + ejb[:, None, :]
        pt2[ki, kj, ka] /= eijab

    pt1 += t1
    pt2 += t2

    for ki, kj, kk, ka, kb in product(range(nkpts), repeat=5):
        kc = kpts_helper.get_kconserv3(cc._scf.cell, cc.kpts,
                                       [ki, kj, kk, ka, kb])
        tmp = t3[ki, kj, kk, ka, kb]
        km = kconserv[ki, kc, kk]
        ke = kconserv[ka, kk, kc]

        Wmcik[km, kc, ki] += 0.5 * lib.einsum('ijkabc,mjab->mcik', tmp,
                                              eris.oovv[km, kj, ka])
        Wacek[ka, kc, ke] += -0.5 * lib.einsum('ijkabc,ijeb->acek', tmp,
                                               eris.oovv[ki, kj, ke])

    delta_ccsd_energy = cc.energy(pt1, pt2, eris) - ccsd_energy
    logger.info(cc, 'CCSD energy T3[2] correction : %14.8e', delta_ccsd_energy)
    return delta_ccsd_energy, pt1, pt2, Wmcik, Wacek
Ejemplo n.º 26
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
Ejemplo n.º 27
0
    def __init__(self, cis, mo_coeff=None, method="incore"):
        log = logger.Logger(cis.stdout, cis.verbose)
        cput0 = (time.clock(), time.time())

        moidx = get_frozen_mask(cis)
        cell = cis._scf.cell
        nocc = cis.nocc
        nmo = cis.nmo
        nvir = nmo - nocc
        nkpts = cis.nkpts
        kpts = cis.kpts

        if mo_coeff is None:
            mo_coeff = cis.mo_coeff
        dtype = mo_coeff[0].dtype

        mo_coeff = self.mo_coeff = padded_mo_coeff(cis, mo_coeff)

        # Re-make our fock MO matrix elements from density and fock AO
        dm = cis._scf.make_rdm1(cis.mo_coeff, cis.mo_occ)
        exxdiv = cis._scf.exxdiv if cis.keep_exxdiv else None
        with lib.temporary_env(cis._scf, exxdiv=exxdiv):
            # _scf.exxdiv affects eris.fock. HF exchange correction should be
            # excluded from the Fock matrix.
            fockao = cis._scf.get_hcore() + cis._scf.get_veff(cell, dm)
        self.fock = np.asarray([
            reduce(np.dot, (mo.T.conj(), fockao[k], mo))
            for k, mo in enumerate(mo_coeff)
        ])

        self.mo_energy = [self.fock[k].diagonal().real for k in range(nkpts)]

        if not cis.keep_exxdiv:
            # Add HFX correction in the self.mo_energy to improve convergence in
            # CCSD iteration. It is useful for the 2D systems since their occupied and
            # the virtual orbital energies may overlap which may lead to numerical
            # issue in the CCSD iterations.
            # FIXME: Whether to add this correction for other exxdiv treatments?
            # Without the correction, MP2 energy may be largely off the correct value.
            madelung = tools.madelung(cell, kpts)
            self.mo_energy = [
                _adjust_occ(mo_e, nocc, -madelung)
                for k, mo_e in enumerate(self.mo_energy)
            ]

        # Get location of padded elements in occupied and virtual space.
        nocc_per_kpt = get_nocc(cis, per_kpoint=True)
        nonzero_padding = padding_k_idx(cis, kind="joint")

        # Check direct and indirect gaps for possible issues with CCSD convergence.
        mo_e = [self.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)]
        mo_e = np.sort([y for x in mo_e for y in x])  # Sort de-nested array
        gap = mo_e[np.sum(nocc_per_kpt)] - mo_e[np.sum(nocc_per_kpt) - 1]
        if gap < 1e-5:
            logger.warn(
                cis,
                "H**O-LUMO gap %s too small for KCCSD. "
                "May cause issues in convergence.",
                gap,
            )

        memory_needed = (nkpts**3 * nocc**2 * nvir**2) * 16 / 1e6
        # CIS only needs two terms: <aj|ib> and <aj|bi>; another factor of two for safety
        memory_needed *= 4

        memory_now = lib.current_memory()[0]
        fao2mo = cis._scf.with_df.ao2mo

        kconserv = cis.khelper.kconserv
        khelper = cis.khelper

        if cis.direct and type(cis._scf.with_df) is not df.GDF:
            raise ValueError("CIS direct method must be used with GDF")

        if (cis.direct and type(cis._scf.with_df) is df.GDF
                and cell.dimension != 2):
            # cis._scf.with_df needs to be df.GDF only (not MDF)
            _init_cis_df_eris(cis, self)
        else:
            if (method == "incore" and
                (memory_needed + memory_now < cis.max_memory)
                    or cell.incore_anyway):
                log.info("using incore ERI storage")
                self.ovov = np.empty(
                    (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir), dtype=dtype)
                self.voov = np.empty(
                    (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir), dtype=dtype)

                for (ikp, ikq, ikr) in khelper.symm_map.keys():
                    iks = kconserv[ikp, ikq, ikr]
                    eri_kpt = fao2mo(
                        (mo_coeff[ikp], mo_coeff[ikq], mo_coeff[ikr],
                         mo_coeff[iks]),
                        (kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]),
                        compact=False,
                    )
                    if dtype == np.float:
                        eri_kpt = eri_kpt.real
                    eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo)
                    for (kp, kq, kr) in khelper.symm_map[(ikp, ikq, ikr)]:
                        eri_kpt_symm = khelper.transform_symm(
                            eri_kpt, kp, kq, kr).transpose(0, 2, 1, 3)
                        self.ovov[kp, kr, kq] = (
                            eri_kpt_symm[:nocc, nocc:, :nocc, nocc:] / nkpts)
                        self.voov[kp, kr, kq] = (
                            eri_kpt_symm[nocc:, :nocc, :nocc, nocc:] / nkpts)

                self.dtype = dtype
            else:
                log.info("using HDF5 ERI storage")
                self.feri1 = lib.H5TmpFile()

                self.ovov = self.feri1.create_dataset(
                    "ovov", (nkpts, nkpts, nkpts, nocc, nvir, nocc, nvir),
                    dtype.char)
                self.voov = self.feri1.create_dataset(
                    "voov", (nkpts, nkpts, nkpts, nvir, nocc, nocc, nvir),
                    dtype.char)

                # <ia|pq> = (ip|aq)
                cput1 = time.clock(), time.time()
                for kp in range(nkpts):
                    for kq in range(nkpts):
                        for kr in range(nkpts):
                            ks = kconserv[kp, kq, kr]
                            orbo_p = mo_coeff[kp][:, :nocc]
                            orbv_r = mo_coeff[kr][:, nocc:]
                            buf_kpt = fao2mo(
                                (orbo_p, mo_coeff[kq], orbv_r, mo_coeff[ks]),
                                (kpts[kp], kpts[kq], kpts[kr], kpts[ks]),
                                compact=False,
                            )
                            if mo_coeff[0].dtype == np.float:
                                buf_kpt = buf_kpt.real
                            buf_kpt = buf_kpt.reshape(nocc, nmo, nvir,
                                                      nmo).transpose(
                                                          0, 2, 1, 3)
                            self.dtype = buf_kpt.dtype
                            self.ovov[kp, kr, kq, :, :, :, :] = (
                                buf_kpt[:, :, :nocc, nocc:] / nkpts)
                            self.voov[kr, kp, ks, :, :, :, :] = (
                                buf_kpt[:, :, nocc:, :nocc].transpose(
                                    1, 0, 3, 2) / nkpts)
                cput1 = log.timer_debug1("transforming ovpq", *cput1)

        log.timer("CIS integral transformation", *cput0)
Ejemplo n.º 28
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