Пример #1
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = int(round(2 * frag.target_MS))
    mol.verbose = 0 if frag.mol_output is None else 4
    mol.output = frag.mol_output
    mol.build()
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    #mol.incore_anyway = True
    #mf.get_hcore = lambda *args: OEI
    #mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    #mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    h1e = OEI
    eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)

    ed = fci.FCI(mol, singlet=(frag.target_S == 0))
    if frag.target_S != 0:
        s2_eval = frag.target_S * (frag.target_S + 1)
        fix_spin_(ed, ss=s2_eval)

    # Guess vector
    ci = None
    if len(frag.imp_cache) == 1:
        ci = frag.imp_cache[0]
        print("Taking initial ci vector from cache")

    t_start = time.time()
    ed.conv_tol = 1e-12
    E_FCI, ci = ed.kernel(h1e, eri, frag.norbs_imp, frag.nelec_imp, ci0=ci)
    assert (ed.converged)
    frag.imp_cache = [ci]
    t_end = time.time()
    print(
        'Impurity FCI energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'
        .format(frag.impham_CONST + E_FCI,
                ed.spin_square(ci, frag.norbs_imp, frag.nelec_imp)[1],
                t_end - t_start))

    # oneRDM and twoCDM
    oneRDM_imp, twoRDM_imp = ed.make_rdm12(ci, frag.norbs_imp, frag.nelec_imp)
    oneRDMs_imp = ed.make_rdm1s(ci, frag.norbs_imp, frag.nelec_imp)
    twoCDM_imp = get_2CDM_from_2RDM(twoRDM_imp, oneRDMs_imp)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDM_imp, frag.imp2loc))
    frag.twoCDM_imp = symmetrize_tensor(twoCDM_imp)
    frag.E_imp = frag.impham_CONST + E_FCI + np.einsum('ab,ab->', chempot_imp,
                                                       oneRDM_imp)

    return None
Пример #2
0
 def __init__(self, mc):
     oneRDMs = mc.make_rdm1s ()
     casdm1s = mc.fcisolver.make_rdm1s (mc.ci, mc.ncas, mc.nelecas)
     casdm1, casdm2 = mc.fcisolver.make_rdm12 (mc.ci, mc.ncas, mc.nelecas)
     twoCDM = get_2CDM_from_2RDM (casdm2, casdm1s)
     ao2amo = mc.mo_coeff[:,mc.ncore:][:,:mc.ncas]
     self.calculator = HessianCalculator (mc._scf, oneRDMs, twoCDM, ao2amo)
     self.cas = mc
     self.cas_mo = mc.mo_coeff
     self.ncore, self.ncas, self.nelecas = mc.ncore, mc.ncas, mc.nelecas
     self.nocc = self.ncore + self.ncas
     self.nmo = self.cas_mo.shape[1]
     self.hop, self.hdiag = gen_g_hop (mc, self.cas_mo, 1, casdm1, casdm2, mc.ao2mo (self.cas_mo))[2:]
Пример #3
0
 def load_amo_from_aobasis(self, ao2amo, dm, twoRDM=None, twoCDM=None):
     print("Attempting to load the provided active orbitals to fragment {}".
           format(self.frag_name))
     self.loc2amo = reduce(
         np.dot,
         (self.ints.ao2loc.conjugate().T, self.ints.ao_ovlp, ao2amo))
     self.oneRDMas_loc = represent_operator_in_basis(
         dm,
         self.loc2amo.conjugate().T)
     if twoRDM is not None:
         self.twoCDMimp_amo = get_2CDM_from_2RDM(twoRDM, dm)
     elif twoCDM is not None:
         self.twoCDMimp_amo = twoCDM
     else:
         self.twoCDMimp_amo = np.zeros([self.norbs_as for i in range(4)])
Пример #4
0
def _get_e_decomp (mc, ot, mo_coeff, ci, e_mcscf, e_nuc, h, xfnal, cfnal):
    ncore, ncas, nelecas = mc.ncore, mc.ncas, mc.nelecas
    _rdms = mcscf.CASCI (mc._scf, ncas, nelecas)
    _rdms.fcisolver = fci.solver (mc._scf.mol, singlet = False, symm = False)
    _rdms.mo_coeff = mo_coeff
    _rdms.ci = ci
    _casdms = _rdms.fcisolver
    dm1s = np.stack (_rdms.make_rdm1s (), axis=0)
    dm1 = dm1s[0] + dm1s[1]
    j = _rdms._scf.get_j (dm=dm1)
    e_core = np.tensordot (h, dm1, axes=2)
    e_coul = np.tensordot (j, dm1, axes=2) / 2
    adm1s = np.stack (_casdms.make_rdm1s (ci, ncas, nelecas), axis=0)
    adm2 = get_2CDM_from_2RDM (_casdms.make_rdm12 (_rdms.ci, ncas, nelecas)[1], adm1s)
    mo_cas = mo_coeff[:,ncore:][:,:ncas]
    e_otx = get_E_ot (xfnal, dm1s, adm2, mo_cas, max_memory=mc.max_memory)
    e_otc = get_E_ot (cfnal, dm1s, adm2, mo_cas, max_memory=mc.max_memory)
    e_wfnxc = e_mcscf - e_nuc - e_core - e_coul
    return e_core, e_coul, e_otx, e_otc, e_wfnxc
Пример #5
0
def _get_e_decomp(mc, ot, mo_coeff, ci, e_mcscf, e_nuc, h, xfnal, cfnal):
    mc_1root = mcscf.CASCI(mc._scf, mc.ncas, mc.nelecas)
    mc_1root.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False)
    mc_1root.mo_coeff = mo_coeff
    mc_1root.ci = ci
    dm1s = np.stack(mc_1root.make_rdm1s(), axis=0)
    dm1 = dm1s[0] + dm1s[1]
    j = mc_1root._scf.get_j(dm=dm1)
    e_core = np.tensordot(h, dm1, axes=2)
    e_coul = np.tensordot(j, dm1, axes=2) / 2
    adm1s = np.stack(mc_1root.fcisolver.make_rdm1s(ci, mc.ncas, mc.nelecas),
                     axis=0)
    adm2 = get_2CDM_from_2RDM(
        mc_1root.fcisolver.make_rdm12(mc_1root.ci, mc.ncas, mc.nelecas)[1],
        adm1s)
    mo_cas = mo_coeff[:, mc.ncore:][:, :mc.ncas]
    e_otx = get_E_ot(xfnal, dm1s, adm2, mo_cas)
    e_otc = get_E_ot(cfnal, dm1s, adm2, mo_cas)
    e_wfnxc = e_mcscf - e_nuc - e_core - e_coul
    return e_core, e_coul, e_otx, e_otc, e_wfnxc
Пример #6
0
def get_fragcasscf(frag, mf, loc2mo):
    ''' Obsolete function, left here just for reference to how I use constrCASSCF object '''

    norbs_amo = frag.active_space[1]
    nelec_amo = frag.active_space[0]
    mo = np.dot(frag.imp2loc, loc2mo)

    mf2 = hf_as.RHF(mf.mol)
    mf2.wo_coeff = np.eye(mf.get_ovlp().shape[0])
    mf2.get_hcore = lambda *args: mf.get_hcore()
    mf2.get_ovlp = lambda *args: mf.get_ovlp()
    mf2._eri = mf._eri
    mf2.scf(mf.make_rdm1())

    mc = my_mcscf.constrCASSCF(mf2,
                               norbs_amo,
                               nelec_amo,
                               cas_ao=list(range(frag.norbs_frag)))
    norbs_cmo = mc.ncore
    norbs_occ = norbs_cmo + norbs_amo
    t_start = time.time()
    E_fragCASSCF = mc.kernel(mo)[0]
    E_fragCASSCF = mc.kernel()[0]
    t_end = time.time()
    assert (mc.converged)
    print('Impurity fragCASSCF energy (incl chempot): {0}; time to solve: {1}'.
          format(frag.impham_CONST + E_fragCASSCF, t_end - t_start))

    casci = mcscf.CASCI(mf2, norbs_amo, nelec_amo)
    E_testfragCASSCF = casci.kernel(mc.mo_coeff)[0]
    assert (abs(E_testfragCASSCF - E_fragCASSCF) < 1e-8), E_testfragCASSCF

    loc2mo = np.dot(frag.loc2imp, mc.mo_coeff)
    loc2amo = loc2mo[:, norbs_cmo:norbs_occ]

    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12(mc.ci, norbs_amo,
                                                     nelec_amo)
    oneRDMs_amo = mc.fcisolver.make_rdm1s(mc.ci, mc.ncas, mc.nelecas)
    twoCDM_amo = get_2CDM_from_2RDM(twoRDM_amo, oneRDMs_amo)
    return oneRDM_amo, twoCDM_amo, loc2mo, loc2amo
Пример #7
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Get the RHF solution
    mol = gto.Mole()
    mol.build(verbose=0)
    mol.atom.append(('C', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf.scf(guess_1RDM)
    DMloc = np.dot(np.dot(mf.mo_coeff, np.diag(mf.mo_occ)), mf.mo_coeff.T)
    if (mf.converged == False):
        mf = mf.newton()
        mf.kernel()

    # Get the MP2 solution
    mp2 = mp.MP2(mf)
    mp2.kernel()
    imp2mo = mf.mo_coeff
    mo2imp = imp2mo.conjugate().T
    oneRDMimp_imp = mf.make_rdm1()
    twoRDMimp_mo = mp2.make_rdm2()
    twoRDMimp_imp = represent_operator_in_basis(twoRDMimp_mo, mo2imp)
    twoCDM_imp = get_2CDM_from_2RDM(twoRDMimp_imp, oneRDMimp_imp)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDMimp_imp, frag.imp2loc))
    frag.twoCDM_imp = symmetrize_tensor(twoCDM_imp)
    frag.E_imp = frag.impham_CONST + mp2.e_tot + np.einsum(
        'ab,ab->', oneRDMimp_imp, chempot_imp)

    return None
Пример #8
0
def kernel(mc, ot, root=-1):
    ''' Calculate MC-PDFT total energy

        Args:
            mc : an instance of CASSCF or CASCI class
                Note: this function does not currently run the CASSCF or CASCI calculation itself
                prior to calculating the MC-PDFT energy. Call mc.kernel () before passing to this function!
            ot : an instance of on-top density functional class - see otfnal.py

        Kwargs:
            root : int
                If mc describes a state-averaged calculation, select the root (0-indexed)
                Negative number requests state-averaged MC-PDFT results (i.e., using state-averaged density matrices)

        Returns:
            Total MC-PDFT energy including nuclear repulsion energy.
    '''
    t0 = (time.clock(), time.time())
    amo = mc.mo_coeff[:, mc.ncore:mc.ncore + mc.ncas]
    # make_rdm12s returns (a, b), (aa, ab, bb)

    mc_1root = mc
    if isinstance(mc.ci, list) and root >= 0:
        mc_1root = mcscf.CASCI(mc._scf, mc.ncas, mc.nelecas)
        mc_1root.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False)
        mc_1root.mo_coeff = mc.mo_coeff
        mc_1root.ci = mc.ci[root]
        mc_1root.e_tot = mc.e_tot
    dm1s = np.asarray(mc_1root.make_rdm1s())
    adm1s = np.stack(mc_1root.fcisolver.make_rdm1s(mc_1root.ci, mc.ncas,
                                                   mc.nelecas),
                     axis=0)
    adm2 = get_2CDM_from_2RDM(
        mc_1root.fcisolver.make_rdm12(mc_1root.ci, mc.ncas, mc.nelecas)[1],
        adm1s)
    if ot.verbose >= logger.DEBUG:
        adm2s = get_2CDMs_from_2RDMs(
            mc_1root.fcisolver.make_rdm12s(mc_1root.ci, mc.ncas,
                                           mc.nelecas)[1], adm1s)
        adm2s_ss = adm2s[0] + adm2s[2]
        adm2s_os = adm2s[1]
    spin = abs(mc.nelecas[0] - mc.nelecas[1])
    t0 = logger.timer(ot, 'rdms', *t0)

    omega, alpha, hyb = ot._numint.rsh_and_hybrid_coeff(ot.otxc, spin=spin)
    Vnn = mc._scf.energy_nuc()
    h = mc._scf.get_hcore()
    dm1 = dm1s[0] + dm1s[1]
    if ot.verbose >= logger.DEBUG or abs(hyb) > 1e-10:
        vj, vk = mc._scf.get_jk(dm=dm1s)
        vj = vj[0] + vj[1]
    else:
        vj = mc._scf.get_j(dm=dm1)
    Te_Vne = np.tensordot(h, dm1)
    # (vj_a + vj_b) * (dm_a + dm_b)
    E_j = np.tensordot(vj, dm1) / 2
    # (vk_a * dm_a) + (vk_b * dm_b) Mind the difference!
    if ot.verbose >= logger.DEBUG or abs(hyb) > 1e-10:
        E_x = -(np.tensordot(vk[0], dm1s[0]) +
                np.tensordot(vk[1], dm1s[1])) / 2
    else:
        E_x = 0
    logger.debug(ot, 'CAS energy decomposition:')
    logger.debug(ot, 'Vnn = %s', Vnn)
    logger.debug(ot, 'Te + Vne = %s', Te_Vne)
    logger.debug(ot, 'E_j = %s', E_j)
    logger.debug(ot, 'E_x = %s', E_x)
    if ot.verbose >= logger.DEBUG:
        # g_pqrs * l_pqrs / 2
        #if ot.verbose >= logger.DEBUG:
        aeri = ao2mo.restore(1, mc.get_h2eff(mc.mo_coeff), mc.ncas)
        E_c = np.tensordot(aeri, adm2, axes=4) / 2
        E_c_ss = np.tensordot(aeri, adm2s_ss, axes=4) / 2
        E_c_os = np.tensordot(aeri, adm2s_os, axes=4)  # ab + ba -> factor of 2
        logger.info(ot, 'E_c = %s', E_c)
        logger.info(ot, 'E_c (SS) = %s', E_c_ss)
        logger.info(ot, 'E_c (OS) = %s', E_c_os)
        e_err = E_c_ss + E_c_os - E_c
        assert (abs(e_err) < 1e-8), e_err
        if isinstance(mc_1root.e_tot, float):
            e_err = mc_1root.e_tot - (Vnn + Te_Vne + E_j + E_x + E_c)
            assert (abs(e_err) < 1e-8), e_err
    if abs(hyb) > 1e-10:
        logger.debug(ot, 'Adding %s * %s CAS exchange to E_ot', hyb, E_x)
    t0 = logger.timer(ot, 'Vnn, Te, Vne, E_j, E_x', *t0)

    E_ot = get_E_ot(ot, dm1s, adm2, amo)
    t0 = logger.timer(ot, 'E_ot', *t0)
    e_tot = Vnn + Te_Vne + E_j + (hyb * E_x) + E_ot
    logger.info(ot, 'MC-PDFT E = %s, Eot(%s) = %s', e_tot, ot.otxc, E_ot)

    return e_tot, E_ot
Пример #9
0
        def get_pdft_veff(self,
                          mo=None,
                          ci=None,
                          incl_coul=False,
                          paaa_only=False):
            ''' Get the 1- and 2-body MC-PDFT effective potentials for a set of mos and ci vectors

                Kwargs:
                    mo : ndarray of shape (nao,nmo)
                        A full set of molecular orbital coefficients. Taken from self if not provided
                    ci : list or ndarray
                        CI vectors. Taken from self if not provided
                    incl_coul : logical
                        If true, includes the Coulomb repulsion energy in the 1-body effective potential.
                        In practice they always appear together.
                    paaa_only : logical
                        If true, only the paaa 2-body effective potential elements are evaluated; the rest of ppaa are filled with zeros.

                Returns:
                    veff1 : ndarray of shape (nao, nao)
                        1-body effective potential in the AO basis
                        May include classical Coulomb potential term (see incl_coul kwarg)
                    veff2 : pyscf.mcscf.mc_ao2mo._ERIS instance
                        Relevant 2-body effective potential in the MO basis
            '''
            t0 = (time.clock(), time.time())
            if mo is None: mo = self.mo_coeff
            if ci is None: ci = self.ci
            # If ci is not a list and mc is a state-average solver, use a different fcisolver for make_rdm
            mc_1root = self
            if isinstance(self, StateAverageMCSCFSolver) and not isinstance(
                    ci, list):
                mc_1root = mcscf.CASCI(self._scf, self.ncas, self.nelecas)
                mc_1root.fcisolver = fci.solver(self._scf.mol,
                                                singlet=False,
                                                symm=False)
                mc_1root.mo_coeff = mo
                mc_1root.ci = ci
                mc_1root.e_tot = self.e_tot
            dm1s = np.asarray(mc_1root.make_rdm1s())
            adm1s = np.stack(mc_1root.fcisolver.make_rdm1s(
                ci, self.ncas, self.nelecas),
                             axis=0)
            adm2 = get_2CDM_from_2RDM(
                mc_1root.fcisolver.make_rdm12(ci, self.ncas, self.nelecas)[1],
                adm1s)
            mo_cas = mo[:, self.ncore:][:, :self.ncas]
            pdft_veff1, pdft_veff2 = pdft_veff.kernel(
                self.otfnal,
                adm1s,
                adm2,
                mo,
                self.ncore,
                self.ncas,
                max_memory=self.max_memory,
                paaa_only=paaa_only)
            if self.verbose > logger.DEBUG:
                logger.debug(
                    self,
                    'Warning: memory-intensive lazy kernel for pdft_veff initiated for '
                    'testing purposes; reduce verbosity to decrease memory footprint'
                )
                pdft_veff1_test, _pdft_veff2_test = pdft_veff.lazy_kernel(
                    self.otfnal, dm1s, adm2, mo_cas)
                old_eri = self._scf._eri
                self._scf._eri = _pdft_veff2_test
                with temporary_env(self.mol, incore_anyway=True):
                    pdft_veff2_test = mc_ao2mo._ERIS(self, mo, method='incore')
                self._scf._eri = old_eri
                err = linalg.norm(pdft_veff1 - pdft_veff1_test)
                logger.debug(self, 'veff1 error: {}'.format(err))
                err = linalg.norm(pdft_veff2.vhf_c - pdft_veff2_test.vhf_c)
                logger.debug(self, 'veff2.vhf_c error: {}'.format(err))
                err = linalg.norm(pdft_veff2.papa - pdft_veff2_test.papa)
                logger.debug(self, 'veff2.ppaa error: {}'.format(err))
                err = linalg.norm(pdft_veff2.papa - pdft_veff2_test.papa)
                logger.debug(self, 'veff2.papa error: {}'.format(err))
                err = linalg.norm(pdft_veff2.j_pc - pdft_veff2_test.j_pc)
                logger.debug(self, 'veff2.j_pc error: {}'.format(err))
                err = linalg.norm(pdft_veff2.k_pc - pdft_veff2_test.k_pc)
                logger.debug(self, 'veff2.k_pc error: {}'.format(err))

            if incl_coul:
                pdft_veff1 += self.get_jk(self.mol, dm1s[0] + dm1s[1])[0]
            logger.timer(self, 'get_pdft_veff', *t0)
            return pdft_veff1, pdft_veff2
Пример #10
0
    def get_pdft_veff (self, mo=None, ci=None, casdm1s=None, casdm2=None, 
            incl_coul=False, paaa_only=False, aaaa_only=False):
        ''' Get the 1- and 2-body MC-PDFT effective potentials for a set of mos and ci vectors

            Kwargs:
                mo : ndarray of shape (nao,nmo)
                    A full set of molecular orbital coefficients. Taken from 
                    self if not provided
                ci : list or ndarray
                    CI vectors. Taken from self if not provided
                casdm1s : ndarray of shape (2,ncas,ncas)
                    Spin-separated 1-RDM in the active space. Overrides CI if
                    and only if both this and casdm2 are provided 
                casdm2 : ndarray of shape (ncas,ncas,ncas,ncas)
                    2-RDM in the active space. Overrides CI if and only if both
                    this and casdm1s are provided 
                incl_coul : logical
                    If true, includes the Coulomb repulsion energy in the 1-body
                    effective potential. In practice they always appear together.
                paaa_only : logical
                    If true, only the paaa 2-body effective potential elements
                    are evaluated; the rest of ppaa are filled with zeros.
                aaaa_only : logical
                    If true, only the aaaa 2-body effective potential elements
                    are evaluated; the rest of ppaa are filled with zeros.

            Returns:
                veff1 : ndarray of shape (nao, nao)
                    1-body effective potential in the AO basis
                    May include classical Coulomb potential term (see incl_coul kwarg)
                veff2 : pyscf.mcscf.mc_ao2mo._ERIS instance
                    Relevant 2-body effective potential in the MO basis
        ''' 
        t0 = (time.process_time (), time.time ())
        if mo is None: mo = self.mo_coeff
        if ci is None: ci = self.ci
        ncore, ncas, nelecas = self.ncore, self.ncas, self.nelecas
        nocc = ncore + ncas

        if (casdm1s is not None) and (casdm2 is not None):
            mo_core = mo[:,:ncore]
            mo_cas = mo[:,ncore:nocc]
            dm1s = np.dot (mo_cas, casdm1s).transpose (1,0,2)
            dm1s = np.dot (dm1s, mo_cas.conj ().T)
            dm1s += (mo_core @ mo_core.conj ().T)[None,:,:]
            adm1s = casdm1s
            adm2 = get_2CDM_from_2RDM (casdm2, casdm1s)
        else:
            dm_list = self.make_rdms_mcpdft (mo_coeff=mo, ci=ci)
            dm1s, (adm1s, (adm2, _ss, _os)) = dm_list

        mo_cas = mo[:,ncore:][:,:ncas]
        pdft_veff1, pdft_veff2 = pdft_veff.kernel (self.otfnal, adm1s, 
            adm2, mo, ncore, ncas, max_memory=self.max_memory, 
            paaa_only=paaa_only, aaaa_only=aaaa_only)
        if self.verbose > logger.DEBUG:
            logger.debug (self, 'Warning: memory-intensive lazy kernel for pdft_veff initiated for '
                'testing purposes; reduce verbosity to decrease memory footprint')
            pdft_veff1_test, _pdft_veff2_test = pdft_veff.lazy_kernel (self.otfnal, dm1s, adm2, mo_cas)
            old_eri = self._scf._eri
            self._scf._eri = _pdft_veff2_test
            with temporary_env (self.mol, incore_anyway=True):
                pdft_veff2_test = mc_ao2mo._ERIS (self, mo, method='incore')
            self._scf._eri = old_eri
            err = linalg.norm (pdft_veff1 - pdft_veff1_test)
            logger.debug (self, 'veff1 error: {}'.format (err))
            err = linalg.norm (pdft_veff2.vhf_c - pdft_veff2_test.vhf_c)
            logger.debug (self, 'veff2.vhf_c error: {}'.format (err))
            err = linalg.norm (pdft_veff2.papa - pdft_veff2_test.papa)
            logger.debug (self, 'veff2.ppaa error: {}'.format (err))
            err = linalg.norm (pdft_veff2.papa - pdft_veff2_test.papa)
            logger.debug (self, 'veff2.papa error: {}'.format (err))
            err = linalg.norm (pdft_veff2.j_pc - pdft_veff2_test.j_pc)
            logger.debug (self, 'veff2.j_pc error: {}'.format (err))
            err = linalg.norm (pdft_veff2.k_pc - pdft_veff2_test.k_pc)
            logger.debug (self, 'veff2.k_pc error: {}'.format (err))
        
        if incl_coul:
            pdft_veff1 += self._scf.get_j (self.mol, dm1s[0] + dm1s[1])
        logger.timer (self, 'get_pdft_veff', *t0)
        return pdft_veff1, pdft_veff2
Пример #11
0
def solve (frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Do I need to get the full RHF solution?
    guess_orbs_av = len (frag.imp_cache) == 2 or frag.norbs_as > 0 

    # Get the RHF solution
    mol = gto.Mole()
    abs_2MS = int (round (2 * abs (frag.target_MS)))
    abs_2S = int (round (2 * abs (frag.target_S)))
    sign_MS = int (np.sign (frag.target_MS)) or 1
    mol.spin = abs_2MS
    mol.verbose = 0 
    if frag.mol_stdout is None:
        mol.output = frag.mol_output
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    if frag.enforce_symmetry:
        mol.groupname  = frag.symmetry
        mol.symm_orb   = get_subspace_symmetry_blocks (frag.loc2imp, frag.loc2symm)
        mol.irrep_name = frag.ir_names
        mol.irrep_id   = frag.ir_ids
    mol.max_memory = frag.ints.max_memory
    mol.build ()
    if frag.mol_stdout is None:
        frag.mol_stdout = mol.stdout
    else:
        mol.stdout = frag.mol_stdout
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    if frag.enforce_symmetry: mol.symmetry = True
    #mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf.energy_nuc = lambda *args: frag.impham_CONST
    if frag.impham_CDERI is not None:
        mf = mf.density_fit ()
        mf.with_df._cderi = frag.impham_CDERI
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf = fix_my_RHF_for_nonsinglet_env (mf, frag.impham_OEI_S)
    mf.__dict__.update (frag.mf_attr)
    if guess_orbs_av: mf.max_cycle = 2
    mf.scf (guess_1RDM)
    if (not mf.converged) and (not guess_orbs_av):
        if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
            raise NotImplementedError('Gradient and Hessian fixes for nonsinglet environment of Newton-descent ROHF algorithm')
        print ("CASSCF RHF-step not converged on fixed-point iteration; initiating newton solver")
        mf = mf.newton ()
        mf.kernel ()

    # Instability check and repeat
    if not guess_orbs_av:
        for i in range (frag.num_mf_stab_checks):
            if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
                raise NotImplementedError('ROHF stability-check fixes for nonsinglet environment')
            mf.mo_coeff = mf.stability ()[0]
            guess_1RDM = mf.make_rdm1 ()
            mf = scf.RHF(mol)
            mf.get_hcore = lambda *args: OEI
            mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
            mf = fix_my_RHF_for_nonsinglet_env (mf, frag.impham_OEI_S)
            mf.scf (guess_1RDM)
            if not mf.converged:
                mf = mf.newton ()
                mf.kernel ()

    E_RHF = mf.e_tot
    print ("CASSCF RHF-step energy: {}".format (E_RHF))

    # Get the CASSCF solution
    CASe = frag.active_space[0]
    CASorb = frag.active_space[1] 
    checkCAS =  (CASe <= frag.nelec_imp) and (CASorb <= frag.norbs_imp)
    if (checkCAS == False):
        CASe = frag.nelec_imp
        CASorb = frag.norbs_imp
    if (abs_2MS > abs_2S):
        CASe = ((CASe + sign_MS * abs_2S) // 2, (CASe - sign_MS * abs_2S) // 2)
    else:
        CASe = ((CASe + sign_MS * abs_2MS) // 2, (CASe - sign_MS * abs_2MS) // 2)
    if frag.impham_CDERI is not None:
        mc = mcscf.DFCASSCF(mf, CASorb, CASe)
    else:
        mc = mcscf.CASSCF(mf, CASorb, CASe)
    smult = abs_2S + 1 if frag.target_S is not None else (frag.nelec_imp % 2) + 1
    mc.fcisolver = csf_solver (mf.mol, smult, symm=frag.enforce_symmetry)
    if frag.enforce_symmetry: mc.fcisolver.wfnsym = frag.wfnsym
    mc.max_cycle_macro = 50 if frag.imp_maxiter is None else frag.imp_maxiter
    mc.conv_tol = min (1e-9, frag.conv_tol_grad**2)  
    mc.ah_start_tol = mc.conv_tol / 10
    mc.ah_conv_tol = mc.conv_tol / 10
    mc.__dict__.update (frag.corr_attr)
    mc = fix_my_CASSCF_for_nonsinglet_env (mc, frag.impham_OEI_S)
    norbs_amo = mc.ncas
    norbs_cmo = mc.ncore
    norbs_imo = frag.norbs_imp - norbs_amo
    nelec_amo = sum (mc.nelecas)
    norbs_occ = norbs_amo + norbs_cmo
    #mc.natorb = True

    # Guess orbitals
    ci0 = None
    dm_imp = frag.get_oneRDM_imp ()
    fock_imp = mf.get_fock (dm=dm_imp)
    if len (frag.imp_cache) == 2:
        imp2mo, ci0 = frag.imp_cache
        print ("Taking molecular orbitals and ci vector from cache")
    elif frag.norbs_as > 0:
        nelec_imp_guess = int (round (np.trace (frag.oneRDMas_loc)))
        norbs_cmo_guess = (frag.nelec_imp - nelec_imp_guess) // 2
        print ("Projecting stored amos (frag.loc2amo; spanning {} electrons) onto the impurity basis and filling the remainder with default guess".format (nelec_imp_guess))
        imp2mo, my_occ = project_amo_manually (frag.loc2imp, frag.loc2amo, fock_imp, norbs_cmo_guess, dm=frag.oneRDMas_loc)
    elif frag.loc2amo_guess is not None:
        print ("Projecting stored amos (frag.loc2amo_guess) onto the impurity basis (no amo dm available)")
        imp2mo, my_occ = project_amo_manually (frag.loc2imp, frag.loc2amo_guess, fock_imp, norbs_cmo, dm=None)
        frag.loc2amo_guess = None
    else:
        dm_imp = np.asarray (mf.make_rdm1 ())
        while dm_imp.ndim > 2:
            dm_imp = dm_imp.sum (0)
        imp2mo = mf.mo_coeff
        fock_imp = mf.get_fock (dm=dm_imp)
        fock_mo = represent_operator_in_basis (fock_imp, imp2mo)
        _, evecs = matrix_eigen_control_options (fock_mo, sort_vecs=1)
        imp2mo = imp2mo @ evecs
        my_occ = ((dm_imp @ imp2mo) * imp2mo).sum (0)
        print ("No stored amos; using mean-field canonical MOs as initial guess")
    # Guess orbital processing
    if callable (frag.cas_guess_callback):
        mo = reduce (np.dot, (frag.ints.ao2loc, frag.loc2imp, imp2mo))
        mo = frag.cas_guess_callback (frag.ints.mol, mc, mo)
        imp2mo = reduce (np.dot, (frag.imp2loc, frag.ints.ao2loc.conjugate ().T, frag.ints.ao_ovlp, mo))
        frag.cas_guess_callback = None

    # Guess CI vector
    if len (frag.imp_cache) != 2 and frag.ci_as is not None:
        loc2amo_guess = np.dot (frag.loc2imp, imp2mo[:,norbs_cmo:norbs_occ])
        metric = np.arange (CASorb) + 1
        gOc = np.dot (loc2amo_guess.conjugate ().T, (frag.ci_as_orb * metric[None,:]))
        umat_g, svals, umat_c = matrix_svd_control_options (gOc, sort_vecs=1, only_nonzero_vals=True)
        if (svals.size == norbs_amo):
            print ("Loading ci guess despite shifted impurity orbitals; singular value error sum: {}".format (np.sum (svals - metric)))
            imp2mo[:,norbs_cmo:norbs_occ] = np.dot (imp2mo[:,norbs_cmo:norbs_occ], umat_g)
            ci0 = transform_ci_for_orbital_rotation (frag.ci_as, CASorb, CASe, umat_c)
        else:
            print ("Discarding stored ci guess because orbitals are too different (missing {} nonzero svals)".format (norbs_amo-svals.size))

    # Symmetry align if possible
    imp2unac = frag.align_imporbs_symm (np.append (imp2mo[:,:norbs_cmo], imp2mo[:,norbs_occ:], axis=1), sorting_metric=fock_imp,
        sort_vecs=1, orbital_type='guess unactive', mol=mol)[0]
    imp2mo[:,:norbs_cmo] = imp2unac[:,:norbs_cmo]
    imp2mo[:,norbs_occ:] = imp2unac[:,norbs_cmo:]
    #imp2mo[:,:norbs_cmo] = frag.align_imporbs_symm (imp2mo[:,:norbs_cmo], sorting_metric=fock_imp, sort_vecs=1, orbital_type='guess inactive', mol=mol)[0]
    imp2mo[:,norbs_cmo:norbs_occ], umat = frag.align_imporbs_symm (imp2mo[:,norbs_cmo:norbs_occ], sorting_metric=fock_imp,
        sort_vecs=1, orbital_type='guess active', mol=mol)
    #imp2mo[:,norbs_occ:] = frag.align_imporbs_symm (imp2mo[:,norbs_occ:], sorting_metric=fock_imp, sort_vecs=1, orbital_type='guess external', mol=mol)[0]
    if frag.enforce_symmetry:
        imp2mo = cleanup_subspace_symmetry (imp2mo, mol.symm_orb)
        err_symm = measure_subspace_blockbreaking (imp2mo, mol.symm_orb)
        err_orth = measure_basis_nonorthonormality (imp2mo)
        print ("Initial symmetry error after cleanup = {}".format (err_symm))
        print ("Initial orthonormality error after cleanup = {}".format (err_orth))
    if ci0 is not None: ci0 = transform_ci_for_orbital_rotation (ci0, CASorb, CASe, umat)
        

    # Guess orbital printing
    if frag.mfmo_printed == False and frag.ints.mol.verbose:
        ao2mfmo = reduce (np.dot, [frag.ints.ao2loc, frag.loc2imp, imp2mo])
        print ("Writing {} {} orbital molden".format (frag.frag_name, 'CAS guess'))
        molden.from_mo (frag.ints.mol, frag.filehead + frag.frag_name + '_mfmorb.molden', ao2mfmo, occ=my_occ)
        frag.mfmo_printed = True
    elif len (frag.active_orb_list) > 0: # This is done AFTER everything else so that the _mfmorb.molden always has consistent ordering
        print('Applying caslst: {}'.format (frag.active_orb_list))
        imp2mo = mc.sort_mo(frag.active_orb_list, mo_coeff=imp2mo)
        frag.active_orb_list = []
    if len (frag.frozen_orb_list) > 0:
        mc.frozen = copy.copy (frag.frozen_orb_list)
        print ("Applying frozen-orbital list (this macroiteration only): {}".format (frag.frozen_orb_list))
        frag.frozen_orb_list = []

    if frag.enforce_symmetry: imp2mo = lib.tag_array (imp2mo, orbsym=label_orb_symm (mol, mol.irrep_id, mol.symm_orb, imp2mo, s=mf.get_ovlp (), check=False))

    t_start = time.time()
    E_CASSCF = mc.kernel(imp2mo, ci0)[0]
    if (not mc.converged) and np.all (np.abs (frag.impham_OEI_S) < 1e-8):
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        print ('Assuming ci vector is poisoned; discarding...')
        imp2mo = mc.mo_coeff.copy ()
        mc = mcscf.CASSCF(mf, CASorb, CASe)
        smult = abs_2S + 1 if frag.target_S is not None else (frag.nelec_imp % 2) + 1
        mc.fcisolver = csf_solver (mf.mol, smult)
        E_CASSCF = mc.kernel(imp2mo)[0]
        if not mc.converged:
            if np.any (np.abs (frag.impham_OEI_S) > 1e-8):
                raise NotImplementedError('Gradient and Hessian fixes for nonsinglet environment of Newton-descent CASSCF algorithm')
            mc = mc.newton ()
            E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    assert (mc.converged)

    '''
    mc.conv_tol = 1e-12
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-12
    E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    #assert (mc.converged)
    '''
    
    # Get twoRDM + oneRDM. cs: MC-SCF core, as: MC-SCF active space
    # I'm going to need to keep some representation of the active-space orbitals

    # Symmetry align if possible
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12 (mc.ci, mc.ncas, mc.nelecas)
    fock_imp = mc.get_fock ()
    mc.mo_coeff[:,:norbs_cmo] = frag.align_imporbs_symm (mc.mo_coeff[:,:norbs_cmo], sorting_metric=fock_imp, sort_vecs=1, orbital_type='optimized inactive', mol=mol)[0]
    mc.mo_coeff[:,norbs_cmo:norbs_occ], umat = frag.align_imporbs_symm (mc.mo_coeff[:,norbs_cmo:norbs_occ],
        sorting_metric=oneRDM_amo, sort_vecs=-1, orbital_type='optimized active', mol=mol)
    mc.mo_coeff[:,norbs_occ:] = frag.align_imporbs_symm (mc.mo_coeff[:,norbs_occ:], sorting_metric=fock_imp, sort_vecs=1, orbital_type='optimized external', mol=mol)[0]
    if frag.enforce_symmetry:
        amo2imp = mc.mo_coeff[:,norbs_cmo:norbs_occ].conjugate ().T
        mc.mo_coeff = cleanup_subspace_symmetry (mc.mo_coeff, mol.symm_orb)
        umat = umat @ (amo2imp @ mc.mo_coeff[:,norbs_cmo:norbs_occ])
        err_symm = measure_subspace_blockbreaking (mc.mo_coeff, mol.symm_orb)
        err_orth = measure_basis_nonorthonormality (mc.mo_coeff)
        print ("Final symmetry error after cleanup = {}".format (err_symm))
        print ("Final orthonormality error after cleanup = {}".format (err_orth))
    mc.ci = transform_ci_for_orbital_rotation (mc.ci, CASorb, CASe, umat)

    # Cache stuff
    imp2mo = mc.mo_coeff #mc.cas_natorb()[0]
    loc2mo = np.dot (frag.loc2imp, imp2mo)
    imp2amo = imp2mo[:,norbs_cmo:norbs_occ]
    loc2amo = loc2mo[:,norbs_cmo:norbs_occ]
    frag.imp_cache = [mc.mo_coeff, mc.ci]
    frag.ci_as = mc.ci
    frag.ci_as_orb = loc2amo.copy ()
    t_end = time.time()

    # oneRDM
    oneRDM_imp = mc.make_rdm1 ()

    # twoCDM
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12 (mc.ci, mc.ncas, mc.nelecas)
    oneRDMs_amo = np.stack (mc.fcisolver.make_rdm1s (mc.ci, mc.ncas, mc.nelecas), axis=0)
    oneSDM_amo = oneRDMs_amo[0] - oneRDMs_amo[1] if frag.target_MS >= 0 else oneRDMs_amo[1] - oneRDMs_amo[0]
    oneSDM_imp = represent_operator_in_basis (oneSDM_amo, imp2amo.conjugate ().T)
    print ("Norm of spin density: {}".format (linalg.norm (oneSDM_amo)))
    # Note that I do _not_ do the *real* cumulant decomposition; I do one assuming oneSDM_amo = 0.
    # This is fine as long as I keep it consistent, since it is only in the orbital gradients for this impurity that
    # the spin density matters. But it has to stay consistent!
    twoCDM_amo = get_2CDM_from_2RDM (twoRDM_amo, oneRDM_amo)
    twoCDM_imp = represent_operator_in_basis (twoCDM_amo, imp2amo.conjugate ().T)
    print('Impurity CASSCF energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'.format (E_CASSCF, spin_square (mc)[1], t_end - t_start))

    # Active-space RDM data
    frag.oneRDMas_loc  = symmetrize_tensor (represent_operator_in_basis (oneRDM_amo, loc2amo.conjugate ().T))
    frag.oneSDMas_loc  = symmetrize_tensor (represent_operator_in_basis (oneSDM_amo, loc2amo.conjugate ().T))
    frag.twoCDMimp_amo = twoCDM_amo
    frag.loc2mo  = loc2mo
    frag.loc2amo = loc2amo
    frag.E2_cum  = np.tensordot (ao2mo.restore (1, mc.get_h2eff (), mc.ncas), twoCDM_amo, axes=4) / 2
    frag.E2_cum += (mf.get_k (dm=oneSDM_imp) * oneSDM_imp).sum () / 4
    # The second line compensates for my incorrect cumulant decomposition. Anything to avoid changing the checkpoint files...

    # General impurity data
    frag.oneRDM_loc = frag.oneRDMfroz_loc + symmetrize_tensor (represent_operator_in_basis (oneRDM_imp, frag.imp2loc))
    frag.oneSDM_loc = frag.oneSDMfroz_loc + frag.oneSDMas_loc
    frag.twoCDM_imp = None # Experiment: this tensor is huge. Do I actually need to keep it? In principle, of course not.
    frag.E_imp      = E_CASSCF + np.einsum ('ab,ab->', chempot_imp, oneRDM_imp)

    return None
Пример #12
0
def mcpdft_HellmanFeynman_grad(mc,
                               ot,
                               veff1,
                               veff2,
                               mo_coeff=None,
                               ci=None,
                               atmlst=None,
                               mf_grad=None,
                               verbose=None):
    ''' Modification of pyscf.grad.casscf.kernel to compute instead the Hellman-Feynman gradient
        terms of MC-PDFT. From the differentiated Hamiltonian matrix elements, only the core and
        Coulomb energy parts remain. For the renormalization terms, the effective Fock matrix is as in
        CASSCF, but with the same Hamiltonian substutition that is used for the energy response terms. '''
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    if mf_grad is None: mf_grad = mc._scf.nuc_grad_method()
    if mc.frozen is not None:
        raise NotImplementedError
    t0 = (time.clock(), time.time())

    mol = mc.mol
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    nao, nmo = mo_coeff.shape
    nao_pair = nao * (nao + 1) // 2

    mo_occ = mo_coeff[:, :nocc]
    mo_core = mo_coeff[:, :ncore]
    mo_cas = mo_coeff[:, ncore:nocc]

    casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas)

    # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63
    dm_core = np.dot(mo_core, mo_core.T) * 2
    dm_cas = reduce(np.dot, (mo_cas, casdm1, mo_cas.T))
    # MRH: I need to replace aapa with the equivalent array from veff2
    # I'm not sure how the outcore file-paging system works, but hopefully I can do this
    # I also need to generate vhf_c and vhf_a from veff2 rather than the molecule's actual integrals
    # The true Coulomb repulsion should already be in veff1, but I need to generate the "fake"
    # vj - vk/2 from veff2
    h1e_mo = mo_coeff.T @ (mc.get_hcore() + veff1) @ mo_coeff + veff2.vhf_c
    aapa = np.zeros((ncas, ncas, nmo, ncas), dtype=h1e_mo.dtype)
    vhf_a = np.zeros((nmo, nmo), dtype=h1e_mo.dtype)
    for i in range(nmo):
        jbuf = veff2.ppaa[i]
        kbuf = veff2.papa[i]
        aapa[:, :, i, :] = jbuf[ncore:nocc, :, :]
        vhf_a[i] = np.tensordot(jbuf, casdm1, axes=2)
    vhf_a *= 0.5
    # for this potential, vj = vk: vj - vk/2 = vj - vj/2 = vj/2
    gfock = np.zeros((nmo, nmo))
    gfock[:, :ncore] = (h1e_mo[:, :ncore] + vhf_a[:, :ncore]) * 2
    gfock[:, ncore:nocc] = h1e_mo[:, ncore:nocc] @ casdm1
    gfock[:, ncore:nocc] += np.einsum('uviw,vuwt->it', aapa, casdm2)
    dme0 = reduce(np.dot, (mo_coeff, (gfock + gfock.T) * .5, mo_coeff.T))
    aapa = vhf_a = h1e_mo = gfock = None

    t0 = logger.timer(mc, 'PDFT HlFn gfock', *t0)
    dm1 = dm_core + dm_cas
    # MRH: vhf1c and vhf1a should be the TRUE vj_c and vj_a (no vk!)
    vj = mf_grad.get_jk(dm=dm1)[0]
    hcore_deriv = mf_grad.hcore_generator(mol)
    s1 = mf_grad.get_ovlp(mol)

    if atmlst is None:
        atmlst = range(mol.natm)
    aoslices = mol.aoslice_by_atom()
    de_hcore = np.zeros((len(atmlst), 3))
    de_renorm = np.zeros((len(atmlst), 3))
    de_coul = np.zeros((len(atmlst), 3))
    de_xc = np.zeros((len(atmlst), 3))
    de_grid = np.zeros((len(atmlst), 3))
    de_wgt = np.zeros((len(atmlst), 3))
    de = np.zeros((len(atmlst), 3))

    # MRH: Now I have to compute the gradient of the exchange-correlation energy
    # This involves derivatives of the orbitals that construct rho and Pi and therefore another
    # set of potentials. It also involves the derivatives of quadrature grid points which
    # propagate through the densities and therefore yet another set of potentials.
    # The orbital-derivative part includes all the grid points and some of the orbitals (- sign);
    # the grid-derivative part includes all of the orbitals and some of the grid points (+ sign).
    # I'll do a loop over grid sections and make arrays of type (3,nao, nao) and (3,nao, ncas, ncas, ncas).
    # I'll contract them within the grid loop for the grid derivatives and in the following
    # orbital loop for the xc derivatives
    dm1s = mc.make_rdm1s()
    casdm1s = np.stack(mc.fcisolver.make_rdm1s(ci, ncas, nelecas), axis=0)
    twoCDM = get_2CDM_from_2RDM(casdm2, casdm1s)
    casdm1s = None
    make_rho = tuple(
        ot._numint._gen_rho_evaluator(mol, dm1s[i], 1) for i in range(2))
    make_rho_c = ot._numint._gen_rho_evaluator(mol, dm_core, 1)
    make_rho_a = ot._numint._gen_rho_evaluator(mol, dm_cas, 1)
    dv1 = np.zeros(
        (3, nao,
         nao))  # Term which should be contracted with the whole density matrix
    dv1_a = np.zeros(
        (3, nao, nao)
    )  # Term which should only be contracted with the core density matrix
    dv2 = np.zeros((3, nao))
    idx = np.array([[1, 4, 5, 6], [2, 5, 7, 8], [3, 6, 8, 9]],
                   dtype=np.int_)  # For addressing particular ao derivatives
    if ot.xctype == 'LDA': idx = idx[:, 0]  # For LDAs no second derivatives
    diag_idx = np.arange(ncas)  # for puvx
    diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx
    casdm2_pack = (casdm2 + casdm2.transpose(0, 1, 3, 2)).reshape(
        ncas**2, ncas, ncas)
    casdm2_pack = pack_tril(casdm2_pack).reshape(ncas, ncas, -1)
    casdm2_pack[:, :, diag_idx] *= 0.5
    diag_idx = np.arange(ncore, dtype=np.int_) * (ncore + 1)  # for pqii
    full_atmlst = -np.ones(mol.natm, dtype=np.int_)
    t1 = logger.timer(mc, 'PDFT HlFn quadrature setup', *t0)
    for k, ia in enumerate(atmlst):
        full_atmlst[ia] = k
    for ia, (coords, w0,
             w1) in enumerate(rks_grad.grids_response_cc(ot.grids)):
        # For the xc potential derivative, I need every grid point in the entire molecule regardless of atmlist. (Because that's about orbitals.)
        # For the grid and weight derivatives, I only need the gridpoints that are in atmlst
        mask = gen_grid.make_mask(mol, coords)
        ao = ot._numint.eval_ao(
            mol, coords, deriv=ot.dens_deriv + 1,
            non0tab=mask)  # Need 1st derivs for LDA, 2nd for GGA, etc.
        if ot.xctype == 'LDA':  # Might confuse the rho and Pi generators if I don't slice this down
            aoval = ao[:1]
        elif ot.xctype == 'GGA':
            aoval = ao[:4]
        rho = np.asarray([m[0](0, aoval, mask, ot.xctype) for m in make_rho])
        Pi = get_ontop_pair_density(ot, rho, aoval, dm1s, twoCDM, mo_cas,
                                    ot.dens_deriv)

        t1 = logger.timer(
            mc, 'PDFT HlFn quadrature atom {} rho/Pi calc'.format(ia), *t1)
        moval_occ = np.tensordot(aoval, mo_occ, axes=1)
        moval_core = moval_occ[..., :ncore]
        moval_cas = moval_occ[..., ncore:]
        t1 = logger.timer(mc,
                          'PDFT HlFn quadrature atom {} ao2mo grid'.format(ia),
                          *t1)
        eot, vrho, vot = ot.eval_ot(rho, Pi, weights=w0)
        ndpi = vot.shape[0]

        # Weight response
        de_wgt += np.tensordot(eot, w1[atmlst], axes=(0, 2))
        t1 = logger.timer(
            mc, 'PDFT HlFn quadrature atom {} weight response'.format(ia), *t1)

        # Find the atoms that are a part of the atomlist - grid correction shouldn't be added if they aren't there
        # The last stuff to vectorize is in get_veff_2body!
        k = full_atmlst[ia]

        # Vpq + Vpqii
        vrho = _contract_vot_rho(vot,
                                 make_rho_c[0](0, aoval, mask, ot.xctype),
                                 add_vrho=vrho)
        tmp_dv = np.stack([
            ot.get_veff_1body(rho, Pi, [ao[ix], aoval], w0, kern=vrho)
            for ix in idx
        ],
                          axis=0)
        if k >= 0:
            de_grid[k] += 2 * np.tensordot(tmp_dv, dm1.T,
                                           axes=2)  # Grid response
        dv1 -= tmp_dv  # XC response
        t1 = logger.timer(
            mc, 'PDFT HlFn quadrature atom {} Vpq + Vpqii'.format(ia), *t1)

        # Viiuv * Duv
        vrho_a = _contract_vot_rho(vot, make_rho_a[0](0, aoval, mask,
                                                      ot.xctype))
        tmp_dv = np.stack([
            ot.get_veff_1body(rho, Pi, [ao[ix], aoval], w0, kern=vrho_a)
            for ix in idx
        ],
                          axis=0)
        if k >= 0:
            de_grid[k] += 2 * np.tensordot(tmp_dv, dm_core.T,
                                           axes=2)  # Grid response
        dv1_a -= tmp_dv  # XC response
        t1 = logger.timer(mc, 'PDFT HlFn quadrature atom {} Viiuv'.format(ia),
                          *t1)

        # Vpuvx
        tmp_dv = ot.get_veff_2body_kl(rho,
                                      Pi,
                                      moval_cas,
                                      moval_cas,
                                      w0,
                                      symm=True,
                                      kern=vot)  # ndpi,ngrids,ncas*(ncas+1)//2
        tmp_dv = np.tensordot(tmp_dv, casdm2_pack,
                              axes=(-1, -1))  # ndpi, ngrids, ncas, ncas
        tmp_dv[0] = (tmp_dv[:ndpi] * moval_cas[:ndpi, :, None, :]).sum(
            0)  # Chain and product rule
        tmp_dv[1:ndpi] *= moval_cas[0, :, None, :]  # Chain and product rule
        tmp_dv = tmp_dv.sum(-1)  # ndpi, ngrids, ncas
        tmp_dv = np.tensordot(ao[idx[:, :ndpi]], tmp_dv,
                              axes=((1, 2),
                                    (0, 1)))  # comp, nao (orb), ncas (dm2)
        tmp_dv = np.einsum('cpu,pu->cp', tmp_dv, mo_cas)  # comp, ncas
        if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1)
        dv2 -= tmp_dv  # XC response
        t1 = logger.timer(mc, 'PDFT HlFn quadrature atom {} Vpuvx'.format(ia),
                          *t1)

    for k, ia in enumerate(atmlst):
        shl0, shl1, p0, p1 = aoslices[ia]
        h1ao = hcore_deriv(ia)  # MRH: this should be the TRUE hcore
        de_hcore[k] += np.einsum('xij,ij->x', h1ao, dm1)
        de_renorm[k] -= np.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2
        de_coul[k] += np.einsum('xij,ij->x', vj[:, p0:p1], dm1[p0:p1]) * 2
        de_xc[k] += np.einsum(
            'xij,ij->x', dv1[:, p0:p1],
            dm1[p0:p1]) * 2  # Full quadrature, only some orbitals
        de_xc[k] += np.einsum('xij,ij->x', dv1_a[:, p0:p1],
                              dm_core[p0:p1]) * 2  # Ditto
        de_xc[k] += dv2[:, p0:p1].sum(1) * 2  # Ditto

    de_nuc = mf_grad.grad_nuc(mol, atmlst)

    logger.debug(mc, "MC-PDFT Hellmann-Feynman nuclear :\n{}".format(de_nuc))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman hcore component:\n{}".format(de_hcore))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman coulomb component:\n{}".format(de_coul))
    logger.debug(mc,
                 "MC-PDFT Hellmann-Feynman xc component:\n{}".format(de_xc))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman quadrature point component:\n{}".format(
            de_grid))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman quadrature weight component:\n{}".format(
            de_wgt))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman renorm component:\n{}".format(de_renorm))

    de = de_nuc + de_hcore + de_coul + de_renorm + de_xc + de_grid + de_wgt

    t1 = logger.timer(mc, 'PDFT HlFn total', *t0)

    return de
Пример #13
0
def kernel(mc, nroots):

    #   lroots=nroots+1
    for i in range(0, nroots):
        ci_coeff = mc.ci[i]
        print("ci", i, nroots, ci_coeff)

    amo = mc.mo_coeff[:, mc.ncore:mc.ncore + mc.ncas]
    # make_rdm12s returns (a, b), (aa, ab, bb)

    mc_1root = mc
    mc_1root = mcscf.CASCI(mc._scf, mc.ncas, mc.nelecas)
    mc_1root.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False)
    mc_1root.mo_coeff = mc.mo_coeff
    nao, nmo = mc.mo_coeff.shape

    #    dm1s = np.asarray (mc_1root.state_average.states_make_rdm1s ())
    adm1s = np.stack(mc.fcisolver.make_rdm1s(mc.ci, mc_1root.ncas,
                                             mc_1root.nelecas),
                     axis=0)
    dm1 = mc.states_make_rdm1()
    dm1_hold = np.ones((nroots, nao, nao))

    print("dm1_hold", np.shape(dm1_hold))
    print("dm1", np.shape(dm1))
    print("adms", np.shape(adm1s))
    print("dm1", dm1[1])
    print("dm1", dm1[2])
    print("dm1", dm1[3])
    print("dm1", dm1[4])

    #    adm1s = np.stack (mc_1root.fcisolver.make_rdm1s (mc.ci, mc.ncas, mc.nelecas), axis=0)
    #    adm2 = get_2CDM_from_2RDM (mc_1root.fcisolver.make_rdm12 (mc_1root.ci, mc.ncas, mc.nelecas)[1], adm1s)
    #    adm2s = get_2CDMs_from_2RDMs (mc_1root.fcisolver.make_rdm12s (mc_1root.ci, mc.ncas, mc.nelecas)[1], adm1s)

    #    adm1 = dm1s[0] + dm1s[1]

    print("dm1s", dm1)

    #   lroots=nroots+1
    for i in range(0, nroots):
        ci_coeff = mc.ci[i]
        print("ci", i, nroots, ci_coeff)

#   print ("casdm1 :", casdm1, np.shape(casdm1))
#    print ("dm1s", dm1s)
#    print ("casdm2 :", casdm2)

#Calculates State Pairs
    Pair = 0
    istate = 1
    jstate = 1
    npairs = int(nroots * (nroots - 1) // 2)

    statepair = np.zeros((npairs, 2))
    print("state pairs", statepair)
    print("npairs", npairs, " nroots ", nroots)

    ipair = 0
    for i in range(nroots):
        print("i", i)
        for j in range(i):
            print("j", j)
            print("ipair", ipair, i, j)
            statepair[ipair, 0] = i
            statepair[ipair, 1] = j
            ipair = ipair + 1
    print("state pair", statepair)

    #    nci_sum = ci[0]
    #    nci_sum2 = ci[1]
    #    nci_sum3 = np.append(nci_sum2,ci[0])
    #    nci_sum4 = np.append(nci_sum,ci[1])

    #    print("result",newci)
    #    print("shape",np.shape(newci))
    #    print ("shape ci", np.shape(ci))
    #    print("nci_sum2", nci_sum4)

    trans12_tdm1, trans12_tdm2 = mc.fcisolver.states_trans_rdm12(
        mc.ci, mc.ci, mc_1root.ncas, mc_1root.nelecas)
    print("trans12 tdm1 :", trans12_tdm1)
    #    print("trans12 tdm2 :", trans12_tdm2)

    #Load in the two-electron integrals
    aeri = ao2mo.restore(1, mc.get_h2eff(mc.mo_coeff), mc.ncas)
    #    print("aeri", aeri)
    print("eri shape", np.shape(aeri))

    #Initialize rotation matrix

    u = np.identity(mc.fcisolver.nroots)
    print("U :", u)

    #Rotate the States and Corresponding Density Matrices
    converged = False
    cmsthresh = 1e-06
    cmsiter = 1
    print("nao", nao)
    #    print("dm1s",dm1s[1,1,1])
    adm1s = np.stack(mc.fcisolver.make_rdm1s(mc.ci, mc_1root.ncas,
                                             mc_1root.nelecas),
                     axis=0)
    adm2 = get_2CDM_from_2RDM(
        mc.fcisolver.make_rdm12(mc.ci, mc_1root.ncas, mc_1root.nelecas)[1],
        adm1s)
    E_c = np.tensordot(aeri, adm2, axes=4) / 2
    print("e_c", np.shape(e_c))
    print("e_c", E_c)

    #Calculates old VeeSum

    #    def calcvee(rmat,ddg):
    #        vee=np.zeros(nroots)
    #
    #        for istate in range (nroots):
    #            for j in range (nroots):
    #                for k in range (nroots):
    #                    for l in range (nroots):
    #                        for m in range (nroots):
    #                           print("i,j,k,l,m", i,j,k,l,m)
    #                           vee[i]= vee[i]+rmat[istate,j]*rmat[istate,k]*rmat[istate,l]*rmat[istate,m]*ddg[j,k,l,m]
    #            vee[i]=vee[i]/2

    #    def getddg(eri):
    #        ddg=np.zeros(nroots**4).reshape(nroots,nroots,nroots,nroots)
    #        adm1s = np.stack (mc.fcisolver.make_rdm1s (mc.ci, mc_1root.ncas,mc_1root.nelecas), axis=0)
    #        adm1s = adm1s[0]+adm1s[1]
    #        print("adm1s shape", np.shape(adm1s))
    #        print("aeri",np.shape(aeri))
    #        for i in range (nroots):
    #            for j in range (nroots):
    #                if j > i :
    #                    jj=i
    #                    ii=j1
    #                else:
    #                    ii=i
    #                    jj=j
    #                for k in range (nroots):
    #                    for l in range (nroots):
    #                        if  l > k :
    #                            kk=l
    #                            ll=k
    #                        else:
    #                            kk=k
    #                            ll=l
    #                        for t in range(mc.ncas):
    #                            for u in range(mc.ncas):
    #                                for v in range(mc.ncas):
    #                                    for x in range(mc.ncas):
    #                                        ii = ii*(ii-1)//2+jj
    #                                        kk = kk*(kk-1)//2+ll
    #                                        ddg[i,j,k,l]=aeri[t,u,v,x]
    #                                        ddg[i,j,k,l]=adm1s[ii,t,u]*adm1s[kk,v,x]*aeri[t,u,v,x]
    #                                        print(i,ii,j,jj,k,kk,l,ll,t,u,v,x)
    #                                        print("ddg", ddg[i,j,k,l])
    #                                        print("dm1_hold", dm1_hold[kk,t,u])

    #    ddg=getddg(aeri)

    #    print("ddg", ddg)
    #    veesum = calcvee(u,ddg)
    #    print("vsum",veesum)
    #    dm1s = np.stack (mc.fcisolver.make_rdm1s (mc.ci[1],mc_1root.ncas,mc_1root.nelecas), axis=0)
    #    dm1 = dm1s[0] + dm1s[1]
    #    j = mc_1root._scf.get_j (dm=dm1)
    #    for i in range(nroots):
    j = mc_1root._scf.get_j(dm=dm1)
    e_coul = np.tensordot(j, dm1s, axes=2) / 2
    print("e_coul_1", i, e_coul)
    #Gradient
    #Example
    #np.einsum ('rsij,r->ij', veff_a, las.weights)

    w = aeri * adm1s
    print("w", np.shape(w))
    #    for i in range(statepairs.len()):
    #        trans12_tdm2 = trans12_tdm2 + mc.fcisolver.states_trans_rdm12(mc.ci,mc.ci,mc_1root.ncas,mc_1root.nelecas)

    #    g_noscale = np.tensordot (w,np.appent(trans12_tdm2,axes=4)
    #    print("g_noscale",g_noscale)
    #    g = 4*g_noscale
    #
    #    print ("g",g)

    return
Пример #14
0
def mcpdft_HellmanFeynman_grad(mc,
                               ot,
                               veff1,
                               veff2,
                               mo_coeff=None,
                               ci=None,
                               atmlst=None,
                               mf_grad=None,
                               verbose=None,
                               max_memory=None,
                               auxbasis_response=False):
    ''' Modification of pyscf.grad.casscf.kernel to compute instead the Hellman-Feynman gradient
        terms of MC-PDFT. From the differentiated Hamiltonian matrix elements, only the core and
        Coulomb energy parts remain. For the renormalization terms, the effective Fock matrix is as in
        CASSCF, but with the same Hamiltonian substutition that is used for the energy response terms. '''
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    if mf_grad is None: mf_grad = mc._scf.nuc_grad_method()
    if mc.frozen is not None:
        raise NotImplementedError
    if max_memory is None: max_memory = mc.max_memory
    t0 = (time.process_time(), time.time())

    mol = mc.mol
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    nao, nmo = mo_coeff.shape
    nao_pair = nao * (nao + 1) // 2

    mo_occ = mo_coeff[:, :nocc]
    mo_core = mo_coeff[:, :ncore]
    mo_cas = mo_coeff[:, ncore:nocc]

    casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas)

    # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63
    dm_core = np.dot(mo_core, mo_core.T) * 2
    dm_cas = reduce(np.dot, (mo_cas, casdm1, mo_cas.T))
    # MRH: I need to replace aapa with the equivalent array from veff2
    # I'm not sure how the outcore file-paging system works, but hopefully I can do this
    # I also need to generate vhf_c and vhf_a from veff2 rather than the molecule's actual integrals
    # The true Coulomb repulsion should already be in veff1, but I need to generate the "fake"
    # vj - vk/2 from veff2
    h1e_mo = mo_coeff.T @ (mc.get_hcore() + veff1) @ mo_coeff + veff2.vhf_c
    aapa = np.zeros((ncas, ncas, nmo, ncas), dtype=h1e_mo.dtype)
    vhf_a = np.zeros((nmo, nmo), dtype=h1e_mo.dtype)
    for i in range(nmo):
        jbuf = veff2.ppaa[i]
        kbuf = veff2.papa[i]
        aapa[:, :, i, :] = jbuf[ncore:nocc, :, :]
        vhf_a[i] = np.tensordot(jbuf, casdm1, axes=2)
    vhf_a *= 0.5
    # for this potential, vj = vk: vj - vk/2 = vj - vj/2 = vj/2
    gfock = np.zeros((nmo, nmo))
    gfock[:, :ncore] = (h1e_mo[:, :ncore] + vhf_a[:, :ncore]) * 2
    gfock[:, ncore:nocc] = h1e_mo[:, ncore:nocc] @ casdm1
    gfock[:, ncore:nocc] += np.einsum('uviw,vuwt->it', aapa, casdm2)
    dme0 = reduce(np.dot, (mo_coeff, (gfock + gfock.T) * .5, mo_coeff.T))
    aapa = vhf_a = h1e_mo = gfock = None

    if atmlst is None:
        atmlst = range(mol.natm)
    aoslices = mol.aoslice_by_atom()
    de_hcore = np.zeros((len(atmlst), 3))
    de_renorm = np.zeros((len(atmlst), 3))
    de_coul = np.zeros((len(atmlst), 3))
    de_xc = np.zeros((len(atmlst), 3))
    de_grid = np.zeros((len(atmlst), 3))
    de_wgt = np.zeros((len(atmlst), 3))
    de_aux = np.zeros((len(atmlst), 3))
    de = np.zeros((len(atmlst), 3))

    t0 = logger.timer(mc, 'PDFT HlFn gfock', *t0)
    mo_coeff, ci, mo_occup = cas_natorb(mc, mo_coeff=mo_coeff, ci=ci)
    mo_occ = mo_coeff[:, :nocc]
    mo_core = mo_coeff[:, :ncore]
    mo_cas = mo_coeff[:, ncore:nocc]
    dm1 = dm_core + dm_cas
    dm1 = tag_array(dm1, mo_coeff=mo_coeff, mo_occ=mo_occup)
    # MRH: vhf1c and vhf1a should be the TRUE vj_c and vj_a (no vk!)
    vj = mf_grad.get_jk(dm=dm1)[0]
    hcore_deriv = mf_grad.hcore_generator(mol)
    s1 = mf_grad.get_ovlp(mol)
    if auxbasis_response:
        de_aux += vj.aux

    # MRH: Now I have to compute the gradient of the exchange-correlation energy
    # This involves derivatives of the orbitals that construct rho and Pi and therefore another
    # set of potentials. It also involves the derivatives of quadrature grid points which
    # propagate through the densities and therefore yet another set of potentials.
    # The orbital-derivative part includes all the grid points and some of the orbitals (- sign);
    # the grid-derivative part includes all of the orbitals and some of the grid points (+ sign).
    # I'll do a loop over grid sections and make arrays of type (3,nao, nao) and (3,nao, ncas, ncas, ncas).
    # I'll contract them within the grid loop for the grid derivatives and in the following
    # orbital loop for the xc derivatives
    # MRH, 05/09/2020: This just in - the actual spin density doesn't matter at all in PDFT!
    # I could probably save a fair amount of time by not screwing around with the actual spin density!
    # Also, the cumulant decomposition can always be defined without the spin-density matrices and
    # it's still valid! But one thing at a time.
    mo_n = mo_occ * mo_occup[None, :nocc]
    casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas)
    twoCDM = get_2CDM_from_2RDM(casdm2, casdm1)
    dm1s = np.stack((dm1 / 2.0, ) * 2, axis=0)
    dm1 = tag_array(dm1, mo_coeff=mo_occ, mo_occ=mo_occup[:nocc])
    make_rho = ot._numint._gen_rho_evaluator(mol, dm1, 1)[0]
    dvxc = np.zeros((3, nao))
    idx = np.array([[1, 4, 5, 6], [2, 5, 7, 8], [3, 6, 8, 9]],
                   dtype=np.int_)  # For addressing particular ao derivatives
    if ot.xctype == 'LDA': idx = idx[:, 0:1]  # For LDAs no second derivatives
    diag_idx = np.arange(ncas)  # for puvx
    diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx
    casdm2_pack = (twoCDM + twoCDM.transpose(0, 1, 3, 2)).reshape(
        ncas**2, ncas, ncas)
    casdm2_pack = pack_tril(casdm2_pack).reshape(ncas, ncas, -1)
    casdm2_pack[:, :, diag_idx] *= 0.5
    diag_idx = np.arange(ncore, dtype=np.int_) * (ncore + 1)  # for pqii
    full_atmlst = -np.ones(mol.natm, dtype=np.int_)
    t1 = logger.timer(mc, 'PDFT HlFn quadrature setup', *t0)
    for k, ia in enumerate(atmlst):
        full_atmlst[ia] = k
    for ia, (coords, w0,
             w1) in enumerate(rks_grad.grids_response_cc(ot.grids)):
        mask = gen_grid.make_mask(mol, coords)
        # For the xc potential derivative, I need every grid point in the entire molecule regardless of atmlist. (Because that's about orbitals.)
        # For the grid and weight derivatives, I only need the gridpoints that are in atmlst
        # It is conceivable that I can make this more efficient by only doing cross-combinations of grids and AOs, but I don't know how "mask"
        # works yet or how else I could do this.
        gc.collect()
        ngrids = coords.shape[0]
        ndao = (1, 4)[ot.dens_deriv]
        ndpi = (1, 4)[ot.Pi_deriv]
        ncols = 1.05 * 3 * (ndao *
                            (nao + nocc) + max(ndao * nao, ndpi * ncas * ncas))
        remaining_floats = (max_memory - current_memory()[0]) * 1e6 / 8
        blksize = int(remaining_floats / (ncols * BLKSIZE)) * BLKSIZE
        blksize = max(BLKSIZE, min(blksize, ngrids, BLKSIZE * 1200))
        t1 = logger.timer(
            mc,
            'PDFT HlFn quadrature atom {} mask and memory setup'.format(ia),
            *t1)
        for ip0 in range(0, ngrids, blksize):
            ip1 = min(ngrids, ip0 + blksize)
            logger.info(
                mc, 'PDFT gradient atom {} slice {}-{} of {} total'.format(
                    ia, ip0, ip1, ngrids))
            ao = ot._numint.eval_ao(
                mol, coords[ip0:ip1], deriv=ot.dens_deriv + 1,
                non0tab=mask)  # Need 1st derivs for LDA, 2nd for GGA, etc.
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} ao grids'.format(ia), *t1)
            if ot.xctype == 'LDA':  # Might confuse the rho and Pi generators if I don't slice this down
                aoval = ao[0]
            if ot.xctype == 'GGA':
                aoval = ao[:4]
            rho = make_rho(0, aoval, mask, ot.xctype) / 2.0
            rho = np.stack((rho, ) * 2, axis=0)
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} rho calc'.format(ia), *t1)
            Pi = get_ontop_pair_density(ot, rho, aoval, dm1s, twoCDM, mo_cas,
                                        ot.dens_deriv, mask)
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} Pi calc'.format(ia), *t1)

            if ot.xctype == 'LDA':  # TODO: consistent format requirements for shape of ao grid
                aoval = ao[:1]
            moval_occ = _grid_ao2mo(mol, aoval, mo_occ, mask)
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} ao2mo grid'.format(ia), *t1)
            aoval = np.ascontiguousarray([
                ao[ix].transpose(0, 2, 1) for ix in idx[:, :ndao]
            ]).transpose(0, 1, 3, 2)
            ao = None
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} ao grid reshape'.format(ia),
                *t1)
            eot, vot = ot.eval_ot(rho, Pi, weights=w0[ip0:ip1])[:2]
            vrho, vPi = vot
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} eval_ot'.format(ia), *t1)
            puvx_mem = 2 * ndpi * (ip1 - ip0) * ncas * ncas * 8 / 1e6
            remaining_mem = max_memory - current_memory()[0]
            logger.info(
                mc,
                'PDFT gradient memory note: working on {} grid points; estimated puvx usage = {:.1f} of {:.1f} remaining MB'
                .format((ip1 - ip0), puvx_mem, remaining_mem))

            # Weight response
            de_wgt += np.tensordot(eot, w1[atmlst, ..., ip0:ip1], axes=(0, 2))
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} weight response'.format(ia),
                *t1)

            # Find the atoms that are a part of the atomlist - grid correction shouldn't be added if they aren't there
            # The last stuff to vectorize is in get_veff_2body!
            k = full_atmlst[ia]

            # Vpq + Vpqrs * Drs ; I'm not sure why the list comprehension down there doesn't break ao's stride order but I'm not complaining
            vrho = _contract_vot_rho(vPi, rho.sum(0), add_vrho=vrho)
            tmp_dv = np.stack([
                ot.get_veff_1body(
                    rho, Pi, [ao_i, moval_occ], w0[ip0:ip1], kern=vrho)
                for ao_i in aoval
            ],
                              axis=0)
            tmp_dv = (tmp_dv * mo_occ[None, :, :] *
                      mo_occup[None, None, :nocc]).sum(2)
            if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1)  # Grid response
            dvxc -= tmp_dv  # XC response
            vrho = tmp_dv = None
            t1 = logger.timer(
                mc,
                'PDFT HlFn quadrature atom {} Vpq + Vpqrs * Drs'.format(ia),
                *t1)

            # Vpuvx * Lpuvx ; remember the stupid slowest->fastest->medium stride order of the ao grid arrays
            moval_cas = moval_occ = np.ascontiguousarray(
                moval_occ[..., ncore:].transpose(0, 2, 1)).transpose(0, 2, 1)
            tmp_dv = ot.get_veff_2body_kl(
                rho,
                Pi,
                moval_cas,
                moval_cas,
                w0[ip0:ip1],
                symm=True,
                kern=vPi)  # ndpi,ngrids,ncas*(ncas+1)//2
            tmp_dv = np.tensordot(tmp_dv, casdm2_pack,
                                  axes=(-1, -1))  # ndpi, ngrids, ncas, ncas
            tmp_dv[0] = (tmp_dv[:ndpi] * moval_cas[:ndpi, :, None, :]).sum(
                0)  # Chain and product rule
            tmp_dv[1:ndpi] *= moval_cas[0, :,
                                        None, :]  # Chain and product rule
            tmp_dv = tmp_dv.sum(-1)  # ndpi, ngrids, ncas
            tmp_dv = np.tensordot(aoval[:, :ndpi],
                                  tmp_dv,
                                  axes=((1, 2),
                                        (0, 1)))  # comp, nao (orb), ncas (dm2)
            tmp_dv = np.einsum(
                'cpu,pu->cp', tmp_dv, mo_cas
            )  # comp, ncas (it's ok to not vectorize this b/c the quadrature grid is gone)
            if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1)  # Grid response
            dvxc -= tmp_dv  # XC response
            tmp_dv = None
            t1 = logger.timer(
                mc, 'PDFT HlFn quadrature atom {} Vpuvx * Lpuvx'.format(ia),
                *t1)

            rho = Pi = eot = vot = vPi = aoval = moval_occ = moval_cas = None
            gc.collect()

    for k, ia in enumerate(atmlst):
        shl0, shl1, p0, p1 = aoslices[ia]
        h1ao = hcore_deriv(ia)  # MRH: this should be the TRUE hcore
        de_hcore[k] += np.einsum('xij,ij->x', h1ao, dm1)
        de_renorm[k] -= np.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2
        de_coul[k] += np.einsum('xij,ij->x', vj[:, p0:p1], dm1[p0:p1]) * 2
        de_xc[k] += dvxc[:, p0:p1].sum(
            1) * 2  # Full quadrature, only some orbitals

    de_nuc = mf_grad.grad_nuc(mol, atmlst)

    logger.debug(mc, "MC-PDFT Hellmann-Feynman nuclear :\n{}".format(de_nuc))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman hcore component:\n{}".format(de_hcore))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman coulomb component:\n{}".format(de_coul))
    logger.debug(mc,
                 "MC-PDFT Hellmann-Feynman xc component:\n{}".format(de_xc))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman quadrature point component:\n{}".format(
            de_grid))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman quadrature weight component:\n{}".format(
            de_wgt))
    logger.debug(
        mc, "MC-PDFT Hellmann-Feynman renorm component:\n{}".format(de_renorm))

    de = de_nuc + de_hcore + de_coul + de_renorm + de_xc + de_grid + de_wgt

    if auxbasis_response:
        de += de_aux
        logger.debug(
            mc, "MC-PDFT Hellmann-Feynman aux component:\n{}".format(de_aux))

    t1 = logger.timer(mc, 'PDFT HlFn total', *t0)

    return de
Пример #15
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI - chempot_imp

    # Do I need to get the full RHF solution?
    guess_orbs_av = len(frag.imp_cache) == 2 or frag.norbs_as > 0

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = int(round(2 * frag.target_MS))
    mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.output = frag.mol_output
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.build()
    #mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf.energy_nuc = lambda *args: frag.impham_CONST
    if frag.impham_CDERI is not None:
        mf = mf.density_fit()
        mf.with_df._cderi = frag.impham_CDERI
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf.__dict__.update(frag.mf_attr)
    if guess_orbs_av: mf.max_cycle = 2
    mf.scf(guess_1RDM)
    if (not mf.converged) and (not guess_orbs_av):
        print(
            "CASSCF RHF-step not converged on fixed-point iteration; initiating newton solver"
        )
        mf = mf.newton()
        mf.kernel()

    # Instability check and repeat
    if not guess_orbs_av:
        for i in range(frag.num_mf_stab_checks):
            mf.mo_coeff = mf.stability()[0]
            guess_1RDM = mf.make_rdm1()
            mf = scf.RHF(mol)
            mf.get_hcore = lambda *args: OEI
            mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
            mf.scf(guess_1RDM)
            if not mf.converged:
                mf = mf.newton()
                mf.kernel()

    print("CASSCF RHF-step energy: {}".format(mf.e_tot))
    #print(mf.mo_occ)
    '''    
    idx = mf.mo_energy.argsort()
    mf.mo_energy = mf.mo_energy[idx]
    mf.mo_coeff = mf.mo_coeff[:,idx]'''

    # Get the CASSCF solution
    CASe = frag.active_space[0]
    CASorb = frag.active_space[1]
    checkCAS = (CASe <= frag.nelec_imp) and (CASorb <= frag.norbs_imp)
    if (checkCAS == False):
        CASe = frag.nelec_imp
        CASorb = frag.norbs_imp
    if (frag.target_MS > frag.target_S):
        CASe = ((CASe // 2) + frag.target_S, (CASe // 2) - frag.target_S)
    else:
        CASe = ((CASe // 2) + frag.target_MS, (CASe // 2) - frag.target_MS)
    if frag.impham_CDERI is not None:
        mc = mcscf.DFCASSCF(mf, CASorb, CASe)
    else:
        mc = mcscf.CASSCF(mf, CASorb, CASe)
    norbs_amo = mc.ncas
    norbs_cmo = mc.ncore
    norbs_imo = frag.norbs_imp - norbs_amo
    nelec_amo = sum(mc.nelecas)
    norbs_occ = norbs_amo + norbs_cmo
    #mc.natorb = True

    # Guess orbitals
    ci0 = None
    if len(frag.imp_cache) == 2:
        imp2mo, ci0 = frag.imp_cache
        print("Taking molecular orbitals and ci vector from cache")
    elif frag.norbs_as > 0:
        nelec_imp_guess = int(round(np.trace(frag.oneRDMas_loc)))
        norbs_cmo_guess = (frag.nelec_imp - nelec_imp_guess) // 2
        print(
            "Projecting stored amos (frag.loc2amo; spanning {} electrons) onto the impurity basis and filling the remainder with default guess"
            .format(nelec_imp_guess))
        imp2mo, my_occ = project_amo_manually(
            frag.loc2imp,
            frag.loc2amo,
            mf.get_fock(dm=frag.get_oneRDM_imp()),
            norbs_cmo_guess,
            dm=frag.oneRDMas_loc)
    elif frag.loc2amo_guess is not None:
        print(
            "Projecting stored amos (frag.loc2amo_guess) onto the impurity basis (no dm available)"
        )
        imp2mo, my_occ = project_amo_manually(
            frag.loc2imp,
            frag.loc2amo_guess,
            mf.get_fock(dm=frag.get_oneRDM_imp()),
            norbs_cmo,
            dm=None)
        frag.loc2amo_guess = None
    else:
        imp2mo = mc.mo_coeff
        my_occ = mf.mo_occ
        print(
            "No stored amos; using mean-field canonical MOs as initial guess")

    # Guess orbital processing
    if callable(frag.cas_guess_callback):
        mo = reduce(np.dot, (frag.ints.ao2loc, frag.loc2imp, imp2mo))
        mo = frag.cas_guess_callback(frag.ints.mol, mc, mo)
        imp2mo = reduce(np.dot, (frag.imp2loc, frag.ints.ao2loc.conjugate().T,
                                 frag.ints.ao_ovlp, mo))
        frag.cas_guess_callback = None
    elif len(frag.active_orb_list) > 0:
        print('Applying caslst: {}'.format(frag.active_orb_list))
        imp2mo = mc.sort_mo(frag.active_orb_list, mo_coeff=imp2mo)
        frag.active_orb_list = []
    if len(frag.frozen_orb_list) > 0:
        mc.frozen = copy.copy(frag.frozen_orb_list)
        print("Applying frozen-orbital list (this macroiteration only): {}".
              format(frag.frozen_orb_list))
        frag.frozen_orb_list = []

    # Guess orbital printing
    if frag.mfmo_printed == False:
        ao2mfmo = reduce(np.dot, [frag.ints.ao2loc, frag.loc2imp, imp2mo])
        molden.from_mo(frag.ints.mol,
                       frag.filehead + frag.frag_name + '_mfmorb.molden',
                       ao2mfmo,
                       occ=my_occ)
        frag.mfmo_printed = True

    # Guess CI vector
    if len(frag.imp_cache) != 2 and frag.ci_as is not None:
        loc2amo_guess = np.dot(frag.loc2imp, imp2mo[:, norbs_cmo:norbs_occ])
        gOc = np.dot(loc2amo_guess.conjugate().T, frag.ci_as_orb)
        umat_g, svals, umat_c = matrix_svd_control_options(
            gOc, sort_vecs=-1, only_nonzero_vals=True)
        if (svals.size == norbs_amo):
            print(
                "Loading ci guess despite shifted impurity orbitals; singular value sum: {}"
                .format(np.sum(svals)))
            imp2mo[:, norbs_cmo:norbs_occ] = np.dot(
                imp2mo[:, norbs_cmo:norbs_occ], umat_g)
            ci0 = transform_ci_for_orbital_rotation(frag.ci_as, CASorb, CASe,
                                                    umat_c)
        else:
            print(
                "Discarding stored ci guess because orbitals are too different (missing {} nonzero svals)"
                .format(norbs_amo - svals.size))

    t_start = time.time()
    smult = 2 * frag.target_S + 1 if frag.target_S is not None else (
        frag.nelec_imp % 2) + 1
    mc.fcisolver = csf_solver(mf.mol, smult)
    mc.max_cycle_macro = 50 if frag.imp_maxiter is None else frag.imp_maxiter
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-10
    mc.conv_tol = 1e-9
    mc.__dict__.update(frag.corr_attr)
    E_CASSCF = mc.kernel(imp2mo, ci0)[0]
    if not mc.converged:
        mc = mc.newton()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        print('Assuming ci vector is poisoned; discarding...')
        imp2mo = mc.mo_coeff.copy()
        mc = mcscf.CASSCF(mf, CASorb, CASe)
        smult = 2 * frag.target_S + 1 if frag.target_S is not None else (
            frag.nelec_imp % 2) + 1
        mc.fcisolver = csf_solver(mf.mol, smult)
        E_CASSCF = mc.kernel(imp2mo)[0]
        if not mc.converged:
            mc = mc.newton()
            E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    assert (mc.converged)
    '''
    mc.conv_tol = 1e-12
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-12
    E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    #assert (mc.converged)
    '''

    # Get twoRDM + oneRDM. cs: MC-SCF core, as: MC-SCF active space
    # I'm going to need to keep some representation of the active-space orbitals
    imp2mo = mc.mo_coeff  #mc.cas_natorb()[0]
    loc2mo = np.dot(frag.loc2imp, imp2mo)
    imp2amo = imp2mo[:, norbs_cmo:norbs_occ]
    loc2amo = loc2mo[:, norbs_cmo:norbs_occ]
    frag.imp_cache = [mc.mo_coeff, mc.ci]
    frag.ci_as = mc.ci
    frag.ci_as_orb = loc2amo.copy()
    t_end = time.time()
    print(
        'Impurity CASSCF energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'
        .format(E_CASSCF,
                spin_square(mc)[1], t_end - t_start))

    # oneRDM
    oneRDM_imp = mc.make_rdm1()

    # twoCDM
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12(mc.ci, mc.ncas,
                                                     mc.nelecas)
    # Note that I do _not_ do the *real* cumulant decomposition; I do one assuming oneRDMs_amo_alpha = oneRDMs_amo_beta
    # This is fine as long as I keep it consistent, since it is only in the orbital gradients for this impurity that
    # the spin density matters. But it has to stay consistent!
    twoCDM_amo = get_2CDM_from_2RDM(twoRDM_amo, oneRDM_amo)
    twoCDM_imp = represent_operator_in_basis(twoCDM_amo, imp2amo.conjugate().T)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDM_imp, frag.imp2loc))
    frag.twoCDM_imp = None  # Experiment: this tensor is huge. Do I actually need to keep it? In principle, of course not.
    frag.E_imp = E_CASSCF + np.einsum('ab,ab->', chempot_imp, oneRDM_imp)

    # Active-space RDM data
    frag.oneRDMas_loc = symmetrize_tensor(
        represent_operator_in_basis(oneRDM_amo,
                                    loc2amo.conjugate().T))
    frag.twoCDMimp_amo = twoCDM_amo
    frag.loc2mo = loc2mo
    frag.loc2amo = loc2amo
    frag.E2_cum = 0.5 * np.tensordot(
        ao2mo.restore(1, mc.get_h2eff(), mc.ncas), twoCDM_amo, axes=4)

    return None