Exemplo n.º 1
0
    def get_occ(self, mo_energy, mo_coeff=None):
        ''' We cannot assume default mo_energy value, because the orbital
        energies are sorted after doing SCF.  But in this function, we need
        the orbital energies are grouped by symmetry irreps
        '''
        mol = self.mol
        nirrep = len(mol.symm_orb)
        if mo_coeff is not None:
            orbsym = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb,
                                         mo_coeff, self.get_ovlp(), False)
            orbsym = numpy.asarray(orbsym)
        else:
            orbsym = [numpy.repeat(ir, mol.symm_orb[i].shape[1])
                      for i, ir in enumerate(mol.irrep_id)]
            orbsym = numpy.hstack(orbsym)

        mo_occ = numpy.zeros_like(mo_energy)
        mo_e_left = []
        idx_e_left = []
        nelec_fix = 0
        for i, ir in enumerate(mol.irrep_id):
            irname = mol.irrep_name[i]
            ir_idx = numpy.where(orbsym == ir)[0]
            if irname in self.irrep_nelec:
                n = self.irrep_nelec[irname]
                e_idx = numpy.argsort(mo_energy[ir_idx])
                mo_occ[ir_idx[e_idx[:n//2]]] = 2
                nelec_fix += n
            else:
                idx_e_left.append(ir_idx)
        nelec_float = mol.nelectron - nelec_fix
        assert(nelec_float >= 0)
        if nelec_float > 0:
            idx_e_left = numpy.hstack(idx_e_left)
            mo_e_left = mo_energy[idx_e_left]
            mo_e_sort = numpy.argsort(mo_e_left)
            occ_idx = idx_e_left[mo_e_sort[:(nelec_float//2)]]
            mo_occ[occ_idx] = 2

        viridx = (mo_occ==0)
        if self.verbose < logger.INFO or viridx.sum() == 0:
            return mo_occ
        ehomo = max(mo_energy[mo_occ>0 ])
        elumo = min(mo_energy[mo_occ==0])
        noccs = []
        for i, ir in enumerate(mol.irrep_id):
            irname = mol.irrep_name[i]
            ir_idx = (orbsym == ir)

            noccs.append(int(mo_occ[ir_idx].sum()))
            if ehomo in mo_energy[ir_idx]:
                irhomo = irname
            if elumo in mo_energy[ir_idx]:
                irlumo = irname
        logger.info(self, 'H**O (%s) = %.15g  LUMO (%s) = %.15g',
                    irhomo, ehomo, irlumo, elumo)
        if self.verbose >= logger.DEBUG:
            logger.debug(self, 'irrep_nelec = %s', noccs)
            _dump_mo_energy(mol, mo_energy, mo_occ, ehomo, elumo, orbsym)
        return mo_occ
Exemplo n.º 2
0
        def kernel(self, mo_coeff=None, mo_occ=None):
            if mo_coeff is None:
                mo_coeff = self.mo_coeff
            if mo_occ is None:
                mo_occ = self.mo_occ
            cput0 = (time.clock(), time.time())

            self.build(self.mol)
            self.dump_flags()

            if mo_coeff is None or mo_occ is None:
                logger.debug(self, 'Initial guess orbitals not given. '
                             'Generating initial guess from %s density matrix',
                             self.init_guess)
                dm = mf.get_init_guess(self.mol, self.init_guess)
                mo_coeff, mo_occ = self.from_dm(dm)
            # save initial guess because some methods may access them
            self.mo_coeff = mo_coeff
            self.mo_occ = mo_occ

            self.converged, self.e_tot, \
                    self.mo_energy, self.mo_coeff, self.mo_occ = \
                    kernel(self, mo_coeff, mo_occ, conv_tol=self.conv_tol,
                           conv_tol_grad=self.conv_tol_grad,
                           max_cycle=self.max_cycle,
                           callback=self.callback, verbose=self.verbose)

            logger.timer(self, 'Second order SCF', *cput0)
            self._finalize()
            return self.e_tot
Exemplo n.º 3
0
def get_occ(mf, mo_energy=None, mo_coeff=None):
    '''Label the occupancies for each orbital.
    NOTE the occupancies are not assigned based on the orbital energy ordering.
    The first N orbitals are assigned to be occupied orbitals.

    Examples:

    >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1)
    >>> mf = scf.hf.SCF(mol)
    >>> energy = numpy.array([-10., -1., 1, -2., 0, -3])
    >>> mf.get_occ(energy)
    array([2, 2, 2, 2, 1, 0])
    '''

    if mo_energy is None: mo_energy = mf.mo_energy
    if getattr(mo_energy, 'mo_ea', None) is not None:
        mo_ea = mo_energy.mo_ea
        mo_eb = mo_energy.mo_eb
    else:
        mo_ea = mo_eb = mo_energy
    nmo = mo_ea.size
    mo_occ = numpy.zeros(nmo)
    if getattr(mf, 'nelec', None) is None:
        nelec = mf.mol.nelec
    else:
        nelec = mf.nelec
    ncore = nelec[1]
    nocc  = nelec[0]
    nopen = abs(nocc - ncore)
    mo_occ = _fill_rohf_occ(mo_energy, mo_ea, mo_eb, ncore, nopen)

    if mf.verbose >= logger.INFO and nocc < nmo and ncore > 0:
        ehomo = max(mo_energy[mo_occ> 0])
        elumo = min(mo_energy[mo_occ==0])
        if ehomo+1e-3 > elumo:
            logger.warn(mf, 'H**O %.15g >= LUMO %.15g', ehomo, elumo)
        else:
            logger.info(mf, '  H**O = %.15g  LUMO = %.15g', ehomo, elumo)
        if nopen > 0 and mf.verbose >= logger.DEBUG:
            core_idx = mo_occ == 2
            open_idx = mo_occ == 1
            vir_idx = mo_occ == 0
            logger.debug(mf, '                  Roothaan           | alpha              | beta')
            logger.debug(mf, '  Highest 2-occ = %18.15g | %18.15g | %18.15g',
                         max(mo_energy[core_idx]),
                         max(mo_ea[core_idx]), max(mo_eb[core_idx]))
            logger.debug(mf, '  Lowest 0-occ =  %18.15g | %18.15g | %18.15g',
                         min(mo_energy[vir_idx]),
                         min(mo_ea[vir_idx]), min(mo_eb[vir_idx]))
            for i in numpy.where(open_idx)[0]:
                logger.debug(mf, '  1-occ =         %18.15g | %18.15g | %18.15g',
                             mo_energy[i], mo_ea[i], mo_eb[i])

        if mf.verbose >= logger.DEBUG:
            numpy.set_printoptions(threshold=nmo)
            logger.debug(mf, '  Roothaan mo_energy =\n%s', mo_energy)
            logger.debug1(mf, '  alpha mo_energy =\n%s', mo_ea)
            logger.debug1(mf, '  beta  mo_energy =\n%s', mo_eb)
            numpy.set_printoptions(threshold=1000)
    return mo_occ
Exemplo n.º 4
0
 def get_occ(self, mo_energy=None, mo_coeff=None):
     if mo_energy is None:
         mo_energy = self.mo_energy
     mol = self.mol
     n4c = len(mo_energy)
     n2c = n4c // 2
     mo_occ = numpy.zeros(n2c * 2)
     if mo_energy[n2c] > -1.999 * mol.light_speed ** 2:
         mo_occ[n2c : n2c + mol.nelectron] = 1
     else:
         n = 0
         for i, e in enumerate(mo_energy):
             if e > -1.999 * mol.light_speed ** 2 and n < mol.nelectron:
                 mo_occ[i] = 1
                 n += 1
     if self.verbose >= logger.INFO:
         logger.info(
             self,
             "H**O %d = %.12g, LUMO %d = %.12g,",
             (n2c + mol.nelectron) // 2,
             mo_energy[n2c + mol.nelectron - 1],
             (n2c + mol.nelectron) // 2 + 1,
             mo_energy[n2c + mol.nelectron],
         )
         logger.debug(self, "NES  mo_energy = %s", mo_energy[:n2c])
         logger.debug(self, "PES  mo_energy = %s", mo_energy[n2c:])
     return mo_occ
Exemplo n.º 5
0
Arquivo: uks.py Projeto: v1j4y/pyscf
def energy_elec(ks, dm, h1e=None, vhf=None):
    if h1e is None:
        h1e = ks.get_hcore()
    e1 = numpy.einsum('ij,ij', h1e.conj(), dm[0]+dm[1])
    tot_e = e1 + ks._ecoul + ks._exc
    logger.debug(ks, 'Ecoul = %s  Exc = %s', ks._ecoul, ks._exc)
    return tot_e, ks._ecoul+ks._exc
Exemplo n.º 6
0
def remove_linear_dep_(mf, threshold=LINEAR_DEP_THRESHOLD,
                       lindep=LINEAR_DEP_TRIGGER):
    '''
    Args:
        threshold : float
            The threshold under which the eigenvalues of the overlap matrix are
            discarded to avoid numerical instability.
        lindep : float
            The threshold that triggers the special treatment of the linear
            dependence issue.
    '''
    s = mf.get_ovlp()
    cond = numpy.max(lib.cond(s))
    if cond < 1./lindep:
        return mf

    logger.info(mf, 'Applying remove_linear_dep_ on SCF obejct.')
    logger.debug(mf, 'Overlap condition number %g', cond)
    def eigh(h, s):
        d, t = numpy.linalg.eigh(s)
        x = t[:,d>threshold] / numpy.sqrt(d[d>threshold])
        xhx = reduce(numpy.dot, (x.T.conj(), h, x))
        e, c = numpy.linalg.eigh(xhx)
        c = numpy.dot(x, c)
        return e, c
    mf._eigh = eigh
    return mf
Exemplo n.º 7
0
def grad_elec(mfg, mo_energy=None, mo_coeff=None, mo_occ=None):
    t0 = (time.clock(), time.time())
    mf = mfg._scf
    mol = mfg.mol
    if mo_energy is None: mo_energy = mf.mo_energy
    if mo_occ is None:    mo_occ = mf.mo_occ
    if mo_coeff is None:  mo_coeff = mf.mo_coeff
    h1 = mfg.get_hcore(mol)
    s1 = mfg.get_ovlp(mol)
    dm0 = mf.make_rdm1(mf.mo_coeff, mf.mo_occ)
    vhf = mfg.get_veff(mol, dm0)
    log.timer(mfg, 'gradients of 2e part', *t0)
    f1 = h1 + vhf
    dme0 = mfg.make_rdm1e(mf.mo_energy, mf.mo_coeff, mf.mo_occ)
    gs = numpy.empty((mol.natm,3))
    for ia in range(mol.natm):
# h1, s1, vhf are \nabla <i|h|j>, the nuclear gradients = -\nabla
        f =-(mfg.matblock_by_atom(mol, ia, f1) + mfg._grad_rinv(mol, ia))
        s = -mfg.matblock_by_atom(mol, ia, s1)
        v = numpy.einsum('ij,kji->k', dm0, f) \
          - numpy.einsum('ij,kji->k', dme0, s)
        gs[ia] = 2 * v.real
    log.debug(mfg, 'gradients of electronic part')
    log.debug(mfg, str(gs))
    return gs
Exemplo n.º 8
0
    def build_(self):
        mol = self.mol
        self.orth_coeff = self.get_orth_ao(mol)
        self.bas_on_frag = select_ao_on_fragment(mol, self.imp_atoms, \
                                                 self.imp_basidx)
        c_inv = numpy.dot(self.orth_coeff.T, self.entire_scf.get_ovlp(self.mol))
        mo_a, mo_b = self.entire_scf.mo_coeff
        occ_a, occ_b = self.entire_scf.mo_occ
        mo_orth_a = numpy.dot(c_inv, mo_a[:,self.entire_scf.mo_occ[0]>1e-15])
        mo_orth_b = numpy.dot(c_inv, mo_b[:,self.entire_scf.mo_occ[1]>1e-15])
        # self.imp_site, self.bath_orb, self.env_orb are based on orth-orbitals
        self.imp_site, self.bath_orb, self.env_orb = \
                self.decompose_orbital((mo_orth_a,mo_orth_b))
        ovlp = numpy.dot(self.bath_orb[0].T,self.bath_orb[1])[:4,:4]
        for i,c in enumerate(ovlp):
            log.debug(self, ('<bath_alpha_%d|bath_beta> = ' % i) \
                      + '%10.5f'*len(c), *c)
        self.impbas_coeff = self.cons_impurity_basis()
        self.nelectron_alpha = self.entire_scf.nelectron_alpha \
                - self.env_orb[0].shape[1]
        self.nelectron_beta = mol.nelectron \
                - self.entire_scf.nelectron_alpha \
                - self.env_orb[1].shape[1]
        log.info(self, 'alpha / beta electrons = %d / %d', \
                 self.nelectron_alpha, self.nelectron_beta)

        self.energy_by_env, self._vhf_env = self.init_vhf_env(self.env_orb)
Exemplo n.º 9
0
def energy_elec(mf, dm=None, h1e=None, vhf=None):
    if dm is None: dm = mf.make_rdm1()
    elif isinstance(dm, numpy.ndarray) and dm.ndim == 2:
        dm = numpy.array((dm*.5, dm*.5))
    ee, ecoul = uhf.energy_elec(mf, dm, h1e, vhf)
    logger.debug(mf, 'Ecoul = %.15g', ecoul)
    return ee, ecoul
Exemplo n.º 10
0
    def init_embsys(self, mol):
        eff_scf = self.entire_scf
        #embs = self.init_embs(mol, self.entire_scf, self.orth_coeff)
        emb = self.OneImp(eff_scf)
        emb.occ_env_cutoff = 1e-14
        emb.orth_coeff = self.orth_coeff
        emb.verbose = self.emb_verbose
        emb.imp_scf()
        embs = [emb]
        sc = numpy.dot(eff_scf.get_ovlp(mol), eff_scf.mo_coeff)
        fock0 = numpy.dot(sc*eff_scf.mo_energy, sc.T.conj())
        emb._project_fock = emb.mat_ao2impbas(fock0)
        nimp = len(emb.bas_on_frag)
        emb._pure_hcore = emb.get_hcore() \
                - emb._vhf_env # exclude HF[core] and correlation potential
        cimp = numpy.dot(emb.impbas_coeff[:,:nimp].T,
                         eff_scf.mo_coeff[:,eff_scf.mo_occ>0])
        emb._project_nelec_frag = numpy.linalg.norm(cimp)**2*2
        log.debug(emb, 'nelec of imp from lattice HF %.8g',
                  emb._project_nelec_frag)
        log.debug(emb, 'nelec of imp from embedding HF %.8g',
                  numpy.linalg.norm(emb.mo_coeff_on_imp[:nimp,:emb.nelectron/2])**2*2)
#X        embs = self.update_embs(mol, embs, self.entire_scf, self.orth_coeff)
        emb.vfit_mf = numpy.zeros_like(emb._pure_hcore)
        emb.vfit_ci = numpy.zeros_like(emb._pure_hcore)
        embs = self.update_embs_vfit_ci(mol, embs, [0])
#X        embs = self.update_embs_vfit_mf(mol, embs, [0])
        self.embs = embs
        return [0], [0]
Exemplo n.º 11
0
    def update_embs(self, mol, embs, eff_scf, orth_coeff):
        sc = numpy.dot(eff_scf.get_ovlp(), eff_scf.mo_coeff)
        c_inv = numpy.dot(eff_scf.get_ovlp(), orth_coeff).T
        fock0 = numpy.dot(sc*eff_scf.mo_energy, sc.T.conj())
        hcore = eff_scf.get_hcore()

        nocc = int(eff_scf.mo_occ.sum()) / 2

        for ifrag, emb in enumerate(embs):
            mo_orth = numpy.dot(c_inv, eff_scf.mo_coeff[:,eff_scf.mo_occ>1e-15])
            emb.imp_site, emb.bath_orb, emb.env_orb = \
                    dmet_hf.decompose_orbital(emb, mo_orth, emb.bas_on_frag)
            emb.impbas_coeff = emb.cons_impurity_basis()
            emb.nelectron = mol.nelectron - emb.env_orb.shape[1] * 2
            log.debug(emb, 'nelec of emb %d = %d', ifrag, emb.nelectron)
            emb._eri = emb.eri_on_impbas(mol)
            emb.energy_by_env, emb._vhf_env = emb.init_vhf_env(emb.env_orb)

            emb._project_fock = emb.mat_ao2impbas(fock0)
            emb.mo_energy, emb.mo_coeff_on_imp = scipy.linalg.eigh(emb._project_fock)
            emb.mo_coeff = numpy.dot(emb.impbas_coeff, emb.mo_coeff_on_imp)
            emb.mo_occ = numpy.zeros_like(emb.mo_energy)
            emb.mo_occ[:emb.nelectron/2] = 2
            emb.hf_energy = 0
            nimp = emb.imp_site.shape[1]
            cimp = numpy.dot(emb.impbas_coeff[:,:nimp].T, sc[:,:nocc])
            emb._pure_hcore = emb.mat_ao2impbas(hcore)
            emb._project_nelec_frag = numpy.linalg.norm(cimp)**2*2
            log.debug(self, 'project_nelec_frag = %f', emb._project_nelec_frag)

#            if isinstance(self.vfit_mf, numpy.ndarray):
#                v1 = emb.mat_orthao2impbas(self.vfit_mf)
#                v1[:nimp,:nimp] = 0
#                emb._vhf_env += v1
        return embs
Exemplo n.º 12
0
    def get_occ(self, mo_energy=None, mo_coeff=None):
        '''Label the occupancies for each orbital

        Kwargs:
            mo_energy : 1D ndarray
                Obital energies

            mo_coeff : 2D ndarray
                Obital coefficients

        Examples:

        >>> from pyscf import gto, scf
        >>> mol = gto.M(atom='H 0 0 0; F 0 0 1.1')
        >>> mf = scf.hf.SCF(mol)
        >>> mf.get_occ(numpy.arange(mol.nao_nr()))
        array([2, 2, 2, 2, 2, 0])
        '''
        if mo_energy is None: mo_energy = self.mo_energy
        mo_occ = numpy.zeros_like(mo_energy)
        nocc = self.mol.nelectron // 2
        mo_occ[:nocc] = 2
        if nocc < mo_occ.size:
            logger.info(self, 'H**O = %.12g, LUMO = %.12g,',
                        mo_energy[nocc-1], mo_energy[nocc])
            if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]:
                logger.warn(self, '!! H**O %.12g == LUMO %.12g',
                            mo_energy[nocc-1], mo_energy[nocc])
        else:
            logger.info(self, 'H**O = %.12g,', mo_energy[nocc-1])
        if self.verbose >= logger.DEBUG:
            numpy.set_printoptions(threshold=len(mo_energy))
            logger.debug(self, '  mo_energy = %s', mo_energy)
            numpy.set_printoptions()
        return mo_occ
Exemplo n.º 13
0
    def fupdate(t1, t2, istep, normt, de, adiis):
        nocc, nvir = t1.shape
        nov = nocc*nvir
        moidx = numpy.ones(mycc.mo_energy.size, dtype=numpy.bool)
        if isinstance(mycc.frozen, (int, numpy.integer)):
            moidx[:mycc.frozen] = False
        else:
            moidx[mycc.frozen] = False
        mo_e = mycc.mo_energy[moidx]
        eia = mo_e[:nocc,None] - mo_e[None,nocc:]
        if (istep > mycc.diis_start_cycle and
            abs(de) < mycc.diis_start_energy_diff):
            if mycc.t1 is None:
                mycc.t1 = t1
                mycc.t2 = t2
            else:
                tbuf = numpy.empty(nov*(nov+1))
                tbuf[:nov] = ((t1-mycc.t1)*eia).ravel()
                pbuf = tbuf[nov:].reshape(nocc,nocc,nvir,nvir)
                for i in range(nocc):
                    djba = (eia.reshape(-1,1) + eia[i].reshape(1,-1)).reshape(-1)
                    pbuf[i] = (t2[i]-mycc.t2[i]) * djba.reshape(nocc,nvir,nvir)
                adiis.push_err_vec(tbuf)
                tbuf = numpy.empty(nov*(nov+1))
                tbuf[:nov] = t1.ravel()
                tbuf[nov:] = t2.ravel()
                t1.data = tbuf.data # release memory
                t2.data = tbuf.data

                tbuf = adiis.update(tbuf)
                mycc.t1 = t1 = tbuf[:nov].reshape(nocc,nvir)
                mycc.t2 = t2 = tbuf[nov:].reshape(nocc,nocc,nvir,nvir)
            logger.debug(mycc, 'DIIS for step %d', istep)
        return t1, t2
Exemplo n.º 14
0
    def __debug_hessian( self ):

        hessian_analytic = self.__hessian()

        original_umatrix = np.array( self.u, copy=True )

        stepsize = 1e-8
        gradient_ref = self.__gradient()
        hessian_numerical = np.zeros( [ self.numVars, self.numVars ], dtype=float )
        for counter in range( self.numVars ):
            self.u = np.array( original_umatrix, copy=True )
            flatx = np.zeros( [ self.numVars ], dtype=float )
            flatx[counter] = stepsize
            self.__update_unitary( flatx )
            gradient_step = self.__gradient()
            hessian_numerical[ :, counter ] = ( gradient_step - gradient_ref ) / stepsize

        self.u = np.array( original_umatrix, copy=True )
        flatx = np.zeros( [ self.numVars ], dtype=float )
        self.__update_unitary( flatx )

        hessian_numerical = 0.5 * ( hessian_numerical + hessian_numerical.T )

        logger.debug(self, "2-norm( hessian difference ) = %.g", np.linalg.norm( hessian_analytic - hessian_numerical ))
        logger.debug(self, "2-norm( hessian )            = %.g", np.linalg.norm( hessian_analytic ))
Exemplo n.º 15
0
def kernel(mp, mo_energy, mo_coeff, nocc, ioblk=256, verbose=None):
    nmo = mo_coeff.shape[1]
    nvir = nmo - nocc
    auxmol = df.incore.format_aux_basis(mp.mol, mp.auxbasis)
    naoaux = auxmol.nao_nr()

    iolen = max(int(ioblk*1e6/8/(nvir*nocc)), 160)

    eia = lib.direct_sum('i-a->ia', mo_energy[:nocc], mo_energy[nocc:])
    t2 = None
    emp2 = 0
    with mp.ao2mo(mo_coeff, nocc) as fov:
        for p0, p1 in prange(0, naoaux, iolen):
            logger.debug(mp, 'Load cderi block %d:%d', p0, p1)
            qov = numpy.array(fov[p0:p1], copy=False)
            for i in range(nocc):
                buf = numpy.dot(qov[:,i*nvir:(i+1)*nvir].T,
                                qov).reshape(nvir,nocc,nvir)
                gi = numpy.array(buf, copy=False)
                gi = gi.reshape(nvir,nocc,nvir).transpose(1,2,0)
                t2i = gi/lib.direct_sum('jb+a->jba', eia, eia[i])
                # 2*ijab-ijba
                theta = gi*2 - gi.transpose(0,2,1)
                emp2 += numpy.einsum('jab,jab', t2i, theta)

    return emp2, t2
Exemplo n.º 16
0
def kernel(mp, mo_energy=None, mo_coeff=None, eris=None, with_t2=WITH_T2,
           verbose=logger.NOTE):
    if mo_energy is None or mo_coeff is None:
        mo_coeff = mp2._mo_without_core(mp, mp.mo_coeff)
        mo_energy = mp2._mo_energy_without_core(mp, mp.mo_energy)
    else:
        # For backward compatibility.  In pyscf-1.4 or earlier, mp.frozen is
        # not supported when mo_energy or mo_coeff is given.
        assert(mp.frozen is 0 or mp.frozen is None)

    nocc = mp.nocc
    nvir = mp.nmo - nocc
    eia = mo_energy[:nocc,None] - mo_energy[None,nocc:]

    t2 = None
    emp2 = 0
    for istep, qov in enumerate(mp.loop_ao2mo(mo_coeff, nocc)):
        logger.debug(mp, 'Load cderi step %d', istep)
        for i in range(nocc):
            buf = numpy.dot(qov[:,i*nvir:(i+1)*nvir].T,
                            qov).reshape(nvir,nocc,nvir)
            gi = numpy.array(buf, copy=False)
            gi = gi.reshape(nvir,nocc,nvir).transpose(1,0,2)
            t2i = gi/lib.direct_sum('jb+a->jba', eia, eia[i])
            emp2 += numpy.einsum('jab,jab', t2i, gi) * 2
            emp2 -= numpy.einsum('jab,jba', t2i, gi)

    return emp2, t2
Exemplo n.º 17
0
def fit_solver(embsys, fock0, nocc, nimp, dm_ref_alpha):
    #fitp = DmFitObj(fock0, nocc, nimp, dm_ref_alpha, v_V, dm_V)
    def _decompress(vfit):
        idx = numpy.tril_indices(nimp)
        v1 = numpy.zeros((nimp, nimp))
        v1[idx] = vfit
        v1[idx[1],idx[0]] = vfit
        return v1

    ec = [0, 0]
    def diff_dm(vfit):
        f = fock0.copy()
        f[:nimp,:nimp] += _decompress(vfit)
        e, c = scipy.linalg.eigh(f)
        dm0 = numpy.dot(c[:nimp,:nocc], c[:nimp,:nocc].T)
        ddm = dm0 - dm_ref_alpha[:nimp,:nimp]
        ec[:] = (e, c)
        return ddm.flatten()

    def jac_ddm(vfit, *args):
        e, c = ec
        x = mat_v_to_mat_dm1(e, c, nocc, nimp, nimp)
        usymm = symm_trans_mat_for_hermit(nimp)
        nn = usymm.shape[0]
        return numpy.dot(x.reshape(-1,nn), usymm)

    x = scipy.optimize.leastsq(diff_dm, numpy.zeros(nimp*(nimp+1)/2),
                               Dfun=jac_ddm, ftol=1e-8)[0]
    log.debug(embsys, 'ddm %s', diff_dm(x))
    return _decompress(x)
Exemplo n.º 18
0
def fit_chemical_potential(mol, emb, embsys):
# correlation potential of embedded-HF is not added to correlated-solver
    import scipy.optimize
    nimp = len(emb.bas_on_frag)
    nelec_frag = emb._project_nelec_frag

# change chemical potential to get correct number of electrons
    def nelec_diff(v):
        vmat = emb.vfit_ci.copy()
        vmat[:nimp,:nimp] = numpy.eye(nimp) * v
        dm = embsys.solver.run(emb, emb._eri, vmat, True, False)[2]
        #print 'ddm ',nelec_frag,dm[:nimp].trace(), nelec_frag - dm[:nimp].trace()
        return nelec_frag - dm[:nimp].trace()
#    chem_pot0 = emb.vfit_ci[0,0]
#OPTIMIZE ME, approximate chemical potential
#    sol = scipy.optimize.root(nelec_diff, chem_pot0, tol=1e-3, \
#                              method='lm', options={'ftol':1e-3, 'maxiter':12})
#    nemb = emb.impbas_coeff.shape[1]
#    vmat = emb.vfit_ci.copy()
#    for i in range(nimp):
#        vmat[i,i] = sol.x
#    log.debug(embsys, 'scipy.optimize summary %s', sol)
#    log.debug(embsys, 'chem potential = %.11g, nelec error = %.11g', \
#              sol.x, sol.fun)
#    log.debug(embsys, '        ncall = %d, scipy.optimize success: %s', \
#              sol.nfev, sol.success)

    v1 = scipy.optimize.newton(nelec_diff, emb.vfit_ci[0,0], maxiter=500)
    vmat = emb.vfit_ci.copy()
    for i in range(nimp):
        vmat[i,i] = v1
    if embsys.verbose >= log.DEBUG:
        log.debug(embsys, 'electron number diff %s', nelec_diff(v1))

    return vmat
Exemplo n.º 19
0
    def assemble_frag_energy(self, mol):
        e_tot = 0
        nelec = 0
        e_corr = 0

        last_frag = -1
        for m, _, _ in self.all_frags:
            if m != last_frag:
                emb = self.embs[m]
                nimp = len(emb.bas_on_frag)
                _, e2frag, dm1 = \
                        self.solver.run(emb, emb._eri, emb.vfit_ci,
                                        with_1pdm=True, with_e2frag=nimp)
                e_frag, nelec_frag = \
                        self.extract_frag_energy(emb, dm1, e2frag)

                log.debug(self, 'fragment %d FCI-in-HF, frag energy = %.12g, E_corr = %.12g, nelec = %.9g', \
                          m, e_frag, e_frag-emb._ehfinhf, nelec_frag)
            e_corr += e_frag-emb._ehfinhf
            e_tot += e_frag
            nelec += nelec_frag
            last_frag = m
        log.info(self, 'sum(e_frag), e_tot = %.9g, nelec_tot = %.9g', \
                  e_tot, nelec)
        return e_tot, e_corr, nelec
Exemplo n.º 20
0
    def kernel(self, h1e, eri, norb, nelec, ci0=None, **kwargs):
        if self.verbose > logger.QUIET:
            pyscf.gto.mole.check_sanity(self, self._keys, self.stdout)

        wfnsym = _id_wfnsym(self, norb, nelec, self.wfnsym)
        if 'verbose' in kwargs:
            if isinstance(kwargs['verbose'], logger.Logger):
                log = kwargs['verbose']
            else:
                log = logger.Logger(self.stdout, kwargs['verbose'])
            log.debug('total symmetry = %s',
                      symm.irrep_id2name(self.mol.groupname, wfnsym))
        else:
            logger.debug(self, 'total symmetry = %s',
                         symm.irrep_id2name(self.mol.groupname, wfnsym))
        e, c = direct_spin1.kernel_ms1(self, h1e, eri, norb, nelec, ci0,
                                       **kwargs)
        if self.wfnsym is not None:
            # should I remove the non-symmetric contributions in each
            # call of contract_2e?
            if self.nroots > 1:
                c = [addons.symmetrize_wfn(ci, norb, nelec, self.orbsym, wfnsym)
                     for ci in c]
            else:
                c = addons.symmetrize_wfn(c, norb, nelec, self.orbsym, wfnsym)
        return e, c
Exemplo n.º 21
0
    def __debug_hessian_matvec( self ):

        hessian_analytic = np.zeros( [ self.numVars, self.numVars ], dtype=float )
        
        for cnt in range( self.numVars ):
            vector = np.zeros( [ self.numVars ], dtype=float )
            vector[ cnt ] = 1.0
            hessian_analytic[ :, cnt ] = self.__hessian_matvec( vector )

        original_umatrix = np.array( self.u, copy=True )

        stepsize = 1e-8
        self.__set_gradient()
        gradient_ref = np.array( self.gradient, copy=True )
        hessian_numerical = np.zeros( [ self.numVars, self.numVars ], dtype=float )
        for counter in range( self.numVars ):
            self.u = np.array( original_umatrix, copy=True )
            flatx = np.zeros( [ self.numVars ], dtype=float )
            flatx[counter] = stepsize
            self.__update_unitary( flatx )
            self.__set_gradient()
            hessian_numerical[ :, counter ] = ( self.gradient - gradient_ref ) / stepsize

        self.u = np.array( original_umatrix, copy=True )
        flatx = np.zeros( [ self.numVars ], dtype=float )
        self.__update_unitary( flatx )

        hessian_numerical = 0.5 * ( hessian_numerical + hessian_numerical.T )

        logger.debug(self, "2-norm( hessian difference ) = %g", np.linalg.norm( hessian_analytic - hessian_numerical ))
        logger.debug(self, "2-norm( hessian )            = %g", np.linalg.norm( hessian_analytic ))
Exemplo n.º 22
0
Arquivo: hf.py Projeto: sunqm/pyscf
    def get_j(self, cell=None, dm=None, hermi=1, kpt=None, kpts_band=None):
        r'''Compute J matrix for the given density matrix and k-point (kpt).
        When kpts_band is given, the J matrices on kpts_band are evaluated.

            J_{pq} = \sum_{rs} (pq|rs) dm[s,r]

        where r,s are orbitals on kpt. p and q are orbitals on kpts_band
        if kpts_band is given otherwise p and q are orbitals on kpt.
        '''
        #return self.get_jk(cell, dm, hermi, kpt, kpts_band)[0]
        if cell is None: cell = self.cell
        if dm is None: dm = self.make_rdm1()
        if kpt is None: kpt = self.kpt

        cpu0 = (time.clock(), time.time())
        dm = np.asarray(dm)
        nao = dm.shape[-1]

        if (kpts_band is None and
            (self._eri is not None or cell.incore_anyway or
             (not self.direct_scf and self._is_mem_enough()))):
            if self._eri is None:
                logger.debug(self, 'Building PBC AO integrals incore')
                self._eri = self.with_df.get_ao_eri(kpt, compact=True)
            vj, vk = mol_hf.dot_eri_dm(self._eri, dm.reshape(-1,nao,nao), hermi)
        else:
            vj = self.with_df.get_jk(dm.reshape(-1,nao,nao), hermi,
                                     kpt, kpts_band, with_k=False)[0]
        logger.timer(self, 'vj', *cpu0)
        return _format_jks(vj, dm, kpts_band)
Exemplo n.º 23
0
 def max_stepsize_scheduler(self, envs):
     if envs['de'] < self.conv_tol or self._max_stepsize is None:
         self._max_stepsize = self.max_stepsize
     else:
         self._max_stepsize *= .5
         logger.debug(self, 'set max_stepsize to %g', self._max_stepsize)
     return self._max_stepsize
Exemplo n.º 24
0
 def get_occ(mo_energy, mo_coeff=None):
     mol = mf.mol
     mo_occ = numpy.zeros_like(mo_energy)
     nocc = mol.nelectron // 2
     mo_occ[:nocc] = 2
     if abs(mo_energy[nocc-1] - mo_energy[nocc]) < tol:
         lst = abs(mo_energy - mo_energy[nocc-1]) < tol
         nocc_left = int(lst[:nocc].sum())
         ndocc = nocc - nocc_left
         mo_occ[ndocc:nocc] = 0
         i = ndocc
         nmo = len(mo_energy)
         logger.info(mf, 'symm_allow_occ [:%d] = 2', ndocc)
         while i < nmo and nocc_left > 0:
             deg = (abs(mo_energy[i:i+5]-mo_energy[i]) < tol).sum()
             if deg <= nocc_left:
                 mo_occ[i:i+deg] = 2
                 nocc_left -= deg
                 logger.info(mf, 'symm_allow_occ [%d:%d] = 2, energy = %.12g',
                             i, i+nocc_left, mo_energy[i])
                 break
             else:
                 i += deg
     logger.info(mf, 'H**O = %.12g, LUMO = %.12g,',
                 mo_energy[nocc-1], mo_energy[nocc])
     logger.debug(mf, '  mo_energy = %s', mo_energy)
     return mo_occ
Exemplo n.º 25
0
Arquivo: hf.py Projeto: eronca/pyscf
    def get_jk(self, cell=None, dm=None, hermi=1, kpt=None, kpt_band=None):
        '''Get Coulomb (J) and exchange (K) following :func:`scf.hf.RHF.get_jk_`.

        Note the incore version, which initializes an _eri array in memory.
        '''
        if cell is None: cell = self.cell
        if dm is None: dm = self.make_rdm1()
        if kpt is None: kpt = self.kpt

        cpu0 = (time.clock(), time.time())

        if (kpt_band is None and
            (self.exxdiv == 'ewald' or self.exxdiv is None) and
            (self._eri is not None or cell.incore_anyway or self._is_mem_enough())):
            if self._eri is None:
                logger.debug(self, 'Building PBC AO integrals incore')
                self._eri = self.with_df.get_ao_eri(kpt, compact=True)
            vj, vk = dot_eri_dm(self._eri, dm, hermi)

            if self.exxdiv == 'ewald':
                from pyscf.pbc.df.df_jk import _ewald_exxdiv_for_G0
                # G=0 is not inculded in the ._eri integrals
                _ewald_exxdiv_for_G0(self.cell, kpt, [dm], [vk])
        else:
            vj, vk = self.with_df.get_jk(dm, hermi, kpt, kpt_band,
                                         exxdiv=self.exxdiv)

        logger.timer(self, 'vj and vk', *cpu0)
        return vj, vk
Exemplo n.º 26
0
    def get_occ(self, mo_energy=None, mo_coeff=None):
        '''Label the occupancies for each orbital.
        NOTE the occupancies are not assigned based on the orbital energy ordering.
        The first N orbitals are assigned to be occupied orbitals.

        Examples:

        >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1)
        >>> mf = scf.hf.SCF(mol)
        >>> energy = numpy.array([-10., -1., 1, -2., 0, -3])
        >>> mf.get_occ(energy)
        array([2, 2, 2, 2, 1, 0])
        '''

        if mo_energy is None: mo_energy = self.mo_energy
        mo_occ = numpy.zeros_like(mo_energy)
        ncore = self.nelec[1]
        nopen = self.nelec[0] - ncore
        nocc = ncore + nopen
        mo_occ[:ncore] = 2
        mo_occ[ncore:nocc] = 1
        if nocc < len(mo_energy):
            logger.info(self, 'H**O = %.12g  LUMO = %.12g',
                        mo_energy[nocc-1], mo_energy[nocc])
            if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]:
                logger.warn(self.mol, '!! H**O %.12g == LUMO %.12g',
                            mo_energy[nocc-1], mo_energy[nocc])
        else:
            logger.info(self, 'H**O = %.12g  no LUMO', mo_energy[nocc-1])
        if nopen > 0:
            for i in range(ncore, nocc):
                logger.debug(self, 'singly occupied orbital energy = %.12g',
                             mo_energy[i])
        logger.debug(self, '  mo_energy = %s', mo_energy)
        return mo_occ
Exemplo n.º 27
0
    def project(mfmo, init_mo, ncore, s):
        nocc = ncore + casscf.ncas
        mo0core = init_mo[:,:ncore]
        s1 = reduce(numpy.dot, (mfmo.T, s, mo0core))
        idx = numpy.argsort(numpy.einsum('ij,ij->i', s1, s1))
        logger.debug(casscf, 'Core indices %s', str(numpy.sort(idx[-ncore:])))
        # take HF core
        mocore = mfmo[:,numpy.sort(idx[-ncore:])]

        # take projected CAS space
        mocas = init_mo[:,ncore:nocc] \
              - reduce(numpy.dot, (mocore, mocore.T, s, init_mo[:,ncore:nocc]))
        mocc = lo.orth.vec_lowdin(numpy.hstack((mocore, mocas)), s)

        # remove core and active space from rest
        mou = init_mo[:,nocc:] \
            - reduce(numpy.dot, (mocc, mocc.T, s, init_mo[:,nocc:]))
        mo = lo.orth.vec_lowdin(numpy.hstack((mocc, mou)), s)

        if casscf.verbose >= logger.DEBUG:
            s1 = reduce(numpy.dot, (mo[:,ncore:nocc].T, s, mfmo))
            idx = numpy.argwhere(abs(s1) > 0.4)
            for i,j in idx:
                logger.debug(casscf, 'Init guess <mo-CAS|mo-hf>  %d  %d  %12.8f',
                             ncore+i+1, j+1, s1[i,j])
        return mo
Exemplo n.º 28
0
def label_orb_symm(mol, irrep_name, symm_orb, mo, s=None, check=True):
    '''Label the symmetry of given orbitals

    irrep_name can be either the symbol or the ID of the irreducible
    representation.  If the ID is provided, it returns the numeric code
    associated with XOR operator, see :py:meth:`symm.param.IRREP_ID_TABLE`

    Args:
        mol : an instance of :class:`Mole`

        irrep_name : list of str or int
            A list of irrep ID or name,  it can be either mol.irrep_id or
            mol.irrep_name.  It can affect the return "label".
        symm_orb : list of 2d array
            the symmetry adapted basis
        mo : 2d array
            the orbitals to label

    Returns:
        list of symbols or integers to represent the irreps for the given
        orbitals

    Examples:

    >>> from pyscf import gto, scf, symm
    >>> mol = gto.M(atom='H 0 0 0; H 0 0 1', basis='ccpvdz',verbose=0, symmetry=1)
    >>> mf = scf.RHF(mol)
    >>> mf.kernel()
    >>> symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mf.mo_coeff)
    ['Ag', 'B1u', 'Ag', 'B1u', 'B2u', 'B3u', 'Ag', 'B2g', 'B3g', 'B1u']
    >>> symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mf.mo_coeff)
    [0, 5, 0, 5, 6, 7, 0, 2, 3, 5]
    '''
    nmo = mo.shape[1]
    if s is None:
        s = mol.intor_symmetric('cint1e_ovlp_sph')
    mo_s = numpy.dot(mo.T, s)
    norm = numpy.empty((len(irrep_name), nmo))
    for i,ir in enumerate(irrep_name):
        moso = numpy.dot(mo_s, symm_orb[i])
        norm[i] = numpy.einsum('ij,ij->i', moso, moso)
    iridx = numpy.argmax(norm, axis=0)
    orbsym = [irrep_name[i] for i in iridx]
    logger.debug(mol, 'irreps of each MO %s', str(orbsym))
    if check:
        norm[iridx,numpy.arange(nmo)] = 0
        orbidx = numpy.where(norm > THRESHOLD)
        if orbidx[1].size > 0:
            idx = numpy.where(norm > THRESHOLD*1e2)
            if idx[1].size > 0:
                logger.error(mol, 'orbitals %s not symmetrized, norm = %s',
                             idx[1], norm[idx])
                raise ValueError('orbitals %s not symmetrized' % idx[1])
            else:
                logger.warn(mol, 'orbitals %s not strictly symmetrized.',
                            orbidx[1])
                logger.warn(mol, 'They can be symmetrized with '
                            'pyscf.symm.symmetrize_orb function.')
                logger.debug(mol, 'norm = %s', norm[orbidx])
    return orbsym
Exemplo n.º 29
0
def make_modchg_basis(auxcell, smooth_eta, l_max=3):
# * chgcell defines smooth gaussian functions for each angular momentum for
#   auxcell. The smooth functions may be used to carry the charge
    chgcell = copy.copy(auxcell)  # smooth model density for coulomb integral to carry charge
    half_sph_norm = .5/numpy.sqrt(numpy.pi)
    chg_bas = []
    chg_env = [smooth_eta]
    ptr_eta = auxcell._env.size
    ptr = ptr_eta + 1
    for ia in range(auxcell.natm):
        for l in set(auxcell._bas[auxcell._bas[:,gto.ATOM_OF]==ia, gto.ANG_OF]):
            if l <= l_max:
                norm = half_sph_norm/gto.mole._gaussian_int(l*2+2, smooth_eta)
                chg_bas.append([ia, l, 1, 1, 0, ptr_eta, ptr, 0])
                chg_env.append(norm)
                ptr += 1

    chgcell._atm = auxcell._atm
    chgcell._bas = numpy.asarray(chg_bas, dtype=numpy.int32).reshape(-1,gto.BAS_SLOTS)
    chgcell._env = numpy.hstack((auxcell._env, chg_env))
    chgcell.nimgs = auxcell.nimgs
    chgcell._built = True
    logger.debug(auxcell, 'smooth basis, num shells = %d, num cGTO = %d',
                 chgcell.nbas, chgcell.nao_nr())
    return chgcell
Exemplo n.º 30
0
    def vfit_mf_method(self, mol, embsys):
        dm_ref = []
        for m,emb in enumerate(self.embs):
            nimp = len(emb.bas_on_frag)
            dv = numpy.eye(nimp) * embsys.vfit_ci[0]
            dmci = embsys.solver.run(emb, emb._eri, dv, True, False)[2]
            log.debug(embsys, 'dm_ref %d = %s', m, dmci)
            dm_ref.append(dmci*.5)
        if self.translational:
            dm_ref = [dm_ref[0] for i in self.basidx_group]

        sc = reduce(numpy.dot, (self.orth_coeff.T,
                                self.entire_scf.get_ovlp(),
                                self.entire_scf.mo_coeff))
        # this fock matrix includes the previous fitting potential
        fock0 = numpy.dot(sc*self.entire_scf.mo_energy, sc.T.conj())
        nocc = mol.nelectron // 2

        dv = embsys.fit_solver(fock0, nocc, dm_ref)
        v = [embsys.vfit_mf[m]+dv[m] for m,emb in enumerate(self.embs)]
        log.debug(self, 'vfit_mf = ')
        try:
            v_group = self.pack(v)
            for vi in v_group:
                pyscf.tools.dump_mat.dump_tri(self.stdout, vi)
        except:
            self.stdout.write('%s\n' % str(v))
        return v
Exemplo n.º 31
0
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1):
    '''Coulomb + XC functional for UKS.  See pyscf/dft/rks.py
    :func:`get_veff` fore more details.
    '''
    if mol is None: mol = ks.mol
    if dm is None: dm = ks.make_rdm1()
    if not isinstance(dm, numpy.ndarray):
        dm = numpy.asarray(dm)
    if dm.ndim == 2:  # RHF DM
        dm = numpy.asarray((dm * .5, dm * .5))
    ground_state = (dm.ndim == 3 and dm.shape[0] == 2)

    t0 = (time.clock(), time.time())

    if ks.grids.coords is None:
        ks.grids.build(with_non0tab=True)
        if ks.small_rho_cutoff > 1e-20 and ground_state:
            ks.grids = rks.prune_small_rho_grids_(ks, mol, dm[0] + dm[1],
                                                  ks.grids)
        t0 = logger.timer(ks, 'setting up grids', *t0)
    if ks.nlc != '':
        if ks.nlcgrids.coords is None:
            ks.nlcgrids.build(with_non0tab=True)
            if ks.small_rho_cutoff > 1e-20 and ground_state:
                ks.nlcgrids = rks.prune_small_rho_grids_(
                    ks, mol, dm[0] + dm[1], ks.nlcgrids)
            t0 = logger.timer(ks, 'setting up nlc grids', *t0)

    ni = ks._numint
    if hermi == 2:  # because rho = 0
        n, exc, vxc = (0, 0), 0, 0
    else:
        max_memory = ks.max_memory - lib.current_memory()[0]
        n, exc, vxc = ni.nr_uks(mol,
                                ks.grids,
                                ks.xc,
                                dm,
                                max_memory=max_memory)
        if ks.nlc != '':
            assert ('VV10' in ks.nlc.upper())
            _, enlc, vnlc = ni.nr_rks(mol,
                                      ks.nlcgrids,
                                      ks.xc + '__' + ks.nlc,
                                      dm[0] + dm[1],
                                      max_memory=max_memory)
            exc += enlc
            vxc += vnlc
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    #enabling range-separated hybrids
    omega, alpha, hyb = ni.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin)

    if abs(hyb) < 1e-10 and abs(alpha) < 1e-10:
        vk = None
        if (ks._eri is None and ks.direct_scf
                and getattr(vhf_last, 'vj', None) is not None):
            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
            vj = ks.get_j(mol, ddm[0] + ddm[1], hermi)
            vj += vhf_last.vj
        else:
            vj = ks.get_j(mol, dm[0] + dm[1], hermi)
        vxc += vj
    else:
        if (ks._eri is None and ks.direct_scf
                and getattr(vhf_last, 'vk', None) is not None):
            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
            vj, vk = ks.get_jk(mol, ddm, hermi)
            vk *= hyb
            if abs(omega) > 1e-10:
                vklr = rks._get_k_lr(mol, ddm, omega, hermi)
                vklr *= (alpha - hyb)
                vk += vklr
            vj = vj[0] + vj[1] + vhf_last.vj
            vk += vhf_last.vk
        else:
            vj, vk = ks.get_jk(mol, dm, hermi)
            vj = vj[0] + vj[1]
            vk *= hyb
            if abs(omega) > 1e-10:
                vklr = rks._get_k_lr(mol, dm, omega, hermi)
                vklr *= (alpha - hyb)
                vk += vklr
        vxc += vj - vk

        if ground_state:
            exc -= (numpy.einsum('ij,ji', dm[0], vk[0]) +
                    numpy.einsum('ij,ji', dm[1], vk[1])) * .5
    if ground_state:
        ecoul = numpy.einsum('ij,ji', dm[0] + dm[1], vj) * .5
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk)
    return vxc
Exemplo n.º 32
0
 def get_init_guess(self, bvec, Adiag, Aop, precond):
     ''' Initial guess should solve the problem for SA-SA rotations '''
     ci_arr = np.asarray(self.base.ci).reshape(self.nroots, -1)
     ndet = ci_arr.shape[-1]
     b_ci = bvec[self.ngorb:].reshape(self.nroots, ndet)
     x0 = np.zeros_like(bvec)
     if self.nroots > 1:
         b_sa = np.dot(ci_arr.conjugate(), b_ci[self.iroot])
         A_sa = 2 * self.weights[self.iroot] * (self.e_mcscf -
                                                self.e_mcscf[self.iroot])
         A_sa[self.iroot] = 1
         b_sa[self.iroot] = 0
         x0_sa = -b_sa / A_sa  # Hessian is diagonal so: easy
         ovlp = ci_arr.conjugate() @ b_ci.T
         logger.debug(self, 'Linear response SA-SA part:\n{}'.format(ovlp))
         logger.debug(
             self, 'Linear response SA-CI norms:\n{}'.format(
                 linalg.norm(b_ci.T - ci_arr.T @ ovlp, axis=1)))
         logger.debug(
             self, 'Linear response orbital norms:\n{}'.format(
                 linalg.norm(bvec[:self.ngorb])))
         logger.debug(
             self, 'SA-SA Lagrange multiplier for root {}:\n{}'.format(
                 self.iroot, x0_sa))
         x0[self.ngorb:][ndet * self.iroot:][:ndet] = np.dot(x0_sa, ci_arr)
     r0 = bvec + Aop(x0)
     r0_ci = r0[self.ngorb:].reshape(self.nroots, ndet)
     ovlp = ci_arr.conjugate() @ r0_ci.T
     logger.debug(
         self, 'Lagrange residual SA-SA part after solving SA-SA part:\n{}'.
         format(ovlp))
     logger.debug(
         self,
         'Lagrange residual SA-CI norms after solving SA-SA part:\n{}'.
         format(linalg.norm(r0_ci.T - ci_arr.T @ ovlp, axis=1)))
     logger.debug(
         self,
         'Lagrange residual orbital norms after solving SA-SA part:\n{}'.
         format(linalg.norm(r0[:self.ngorb])))
     x0 += precond(-r0)
     r1 = bvec + Aop(x0)
     r1_ci = r1[self.ngorb:].reshape(self.nroots, ndet)
     ovlp = ci_arr.conjugate() @ r1_ci.T
     logger.debug(
         self, 'Lagrange residual SA-SA part after first precondition:\n{}'.
         format(ovlp))
     logger.debug(
         self,
         'Lagrange residual SA-CI norms after first precondition:\n{}'.
         format(linalg.norm(r1_ci.T - ci_arr.T @ ovlp, axis=1)))
     logger.debug(
         self,
         'Lagrange residual orbital norms after first precondition:\n{}'.
         format(linalg.norm(r1[:self.ngorb])))
     return x0
Exemplo n.º 33
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
Exemplo n.º 34
0
def kernel(gw,
           mo_energy,
           mo_coeff,
           Lpq=None,
           orbs=None,
           nw=None,
           vhf_df=False,
           verbose=logger.NOTE):
    '''
    GW-corrected quasiparticle orbital energies
    Returns:
        A list :  converged, mo_energy, mo_coeff
    '''
    mf = gw._scf
    if gw.frozen is None:
        frozen = 0
    else:
        frozen = gw.frozen

    # only support frozen core
    assert (isinstance(frozen, int))
    assert (frozen < gw.nocc)

    if Lpq is None:
        Lpq = gw.ao2mo(mo_coeff)
    if orbs is None:
        orbs = range(gw.nmo)
    else:
        orbs = [x - frozen for x in orbs]
        if orbs[0] < 0:
            logger.warn(gw, 'GW orbs must be larger than frozen core!')
            raise RuntimeError

    # v_xc
    v_mf = mf.get_veff() - mf.get_j()
    v_mf = reduce(numpy.dot, (mo_coeff.T, v_mf, mo_coeff))

    nocc = gw.nocc
    nmo = gw.nmo
    nvir = nmo - nocc

    # v_hf from DFT/HF density
    if vhf_df and frozen == 0:
        # density fitting for vk
        vk = -einsum('Lni,Lim->nm', Lpq[:, :, :nocc], Lpq[:, :nocc, :])
    else:
        # exact vk without density fitting
        dm = mf.make_rdm1()
        rhf = scf.RHF(gw.mol)
        vk = rhf.get_veff(gw.mol, dm) - rhf.get_j(gw.mol, dm)
        vk = reduce(numpy.dot, (mo_coeff.T, vk, mo_coeff))

    # Grids for integration on imaginary axis
    freqs, wts = _get_scaled_legendre_roots(nw)

    # Compute self-energy on imaginary axis i*[0,iw_cutoff]
    sigmaI, omega = get_sigma_diag(gw, orbs, Lpq, freqs, wts, iw_cutoff=5.)

    # Analytic continuation
    if gw.ac == 'twopole':
        coeff = AC_twopole_diag(sigmaI, omega, orbs, nocc)
    elif gw.ac == 'pade':
        coeff, omega_fit = AC_pade_thiele_diag(sigmaI, omega)

    conv = True
    mf_mo_energy = mo_energy.copy()
    ef = (mo_energy[nocc - 1] + mo_energy[nocc]) / 2.
    mo_energy = np.zeros_like(gw._scf.mo_energy)
    for p in orbs:
        if gw.linearized:
            # linearized G0W0
            de = 1e-6
            ep = mf_mo_energy[p]
            #TODO: analytic sigma derivative
            if gw.ac == 'twopole':
                sigmaR = two_pole(ep - ef, coeff[:, p - orbs[0]]).real
                dsigma = two_pole(ep - ef + de,
                                  coeff[:, p - orbs[0]]).real - sigmaR.real
            elif gw.ac == 'pade':
                sigmaR = pade_thiele(ep - ef, omega_fit[p - orbs[0]],
                                     coeff[:, p - orbs[0]]).real
                dsigma = pade_thiele(ep - ef + de, omega_fit[p - orbs[0]],
                                     coeff[:, p - orbs[0]]).real - sigmaR.real
            zn = 1.0 / (1.0 - dsigma / de)
            e = ep + zn * (sigmaR.real + vk[p, p] - v_mf[p, p])
            mo_energy[p + frozen] = e
        else:
            # self-consistently solve QP equation
            def quasiparticle(omega):
                if gw.ac == 'twopole':
                    sigmaR = two_pole(omega - ef, coeff[:, p - orbs[0]]).real
                elif gw.ac == 'pade':
                    sigmaR = pade_thiele(omega - ef, omega_fit[p - orbs[0]],
                                         coeff[:, p - orbs[0]]).real
                return omega - mf_mo_energy[p] - (sigmaR.real + vk[p, p] -
                                                  v_mf[p, p])

            try:
                e = newton(quasiparticle,
                           mf_mo_energy[p],
                           tol=1e-6,
                           maxiter=100)
                mo_energy[p + frozen] = e
            except RuntimeError:
                conv = False

    if gw.verbose >= logger.DEBUG:
        numpy.set_printoptions(threshold=nmo)
        logger.debug(gw, '  GW mo_energy =\n%s', mo_energy)
        numpy.set_printoptions(threshold=1000)

    return conv, mo_energy, mo_coeff
Exemplo n.º 35
0
def kernel(mf,
           mo_coeff=None,
           mo_occ=None,
           dm=None,
           conv_tol=1e-10,
           conv_tol_grad=None,
           max_cycle=50,
           dump_chk=True,
           callback=None,
           verbose=logger.NOTE):
    cput0 = (time.clock(), time.time())
    log = logger.new_logger(mf, verbose)
    mol = mf._scf.mol
    if mol != mf.mol:
        logger.warn(
            mf, 'dual-basis SOSCF is an experimental feature. It is '
            'still in testing.')

    if conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(conv_tol)
        log.info('Set conv_tol_grad to %g', conv_tol_grad)

# call mf._scf.get_hcore, mf._scf.get_ovlp because they might be overloaded
    h1e = mf._scf.get_hcore(mol)
    s1e = mf._scf.get_ovlp(mol)

    if mo_coeff is not None and mo_occ is not None:
        dm = mf.make_rdm1(mo_coeff, mo_occ)
        # call mf._scf.get_veff, to avoid "newton().density_fit()" polluting get_veff
        vhf = mf._scf.get_veff(mol, dm)
        fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0)
        mo_energy, mo_tmp = mf.eig(fock, s1e)
        mf.get_occ(mo_energy, mo_tmp)
        mo_tmp = None

    else:
        if dm is None:
            logger.debug(
                mf, 'Initial guess density matrix is not given. '
                'Generating initial guess from %s', mf.init_guess)
            dm = mf.get_init_guess(mf._scf.mol, mf.init_guess)
        vhf = mf._scf.get_veff(mol, dm)
        fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0)
        mo_energy, mo_coeff = mf.eig(fock, s1e)
        mo_occ = mf.get_occ(mo_energy, mo_coeff)
        dm, dm_last = mf.make_rdm1(mo_coeff, mo_occ), dm
        vhf = mf._scf.get_veff(mol, dm, dm_last=dm_last, vhf_last=vhf)

    # Save mo_coeff and mo_occ because they are needed by function rotate_mo
    mf.mo_coeff, mf.mo_occ = mo_coeff, mo_occ

    e_tot = mf._scf.energy_tot(dm, h1e, vhf)
    fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0)
    log.info('Initial guess E= %.15g  |g|= %g', e_tot,
             numpy.linalg.norm(mf._scf.get_grad(mo_coeff, mo_occ, fock)))

    if dump_chk and mf.chkfile:
        chkfile.save_mol(mol, mf.chkfile)

# Copy the integral file to soscf object to avoid the integrals being cached
# twice.
    if mol is mf.mol and not getattr(mf, 'with_df', None):
        mf._eri = mf._scf._eri
        # If different direct_scf_cutoff is assigned to newton_ah mf.opt
        # object, mf.opt should be different to mf._scf.opt
        #mf.opt = mf._scf.opt

    rotaiter = _rotate_orb_cc(mf, h1e, s1e, conv_tol_grad, verbose=log)
    next(rotaiter)  # start the iterator
    kftot = jktot = 0
    scf_conv = False
    cput1 = log.timer('initializing second order scf', *cput0)

    for imacro in range(max_cycle):
        u, g_orb, kfcount, jkcount, dm_last, vhf = \
                rotaiter.send((mo_coeff, mo_occ, dm, vhf, e_tot))
        kftot += kfcount + 1
        jktot += jkcount + 1

        last_hf_e = e_tot
        norm_gorb = numpy.linalg.norm(g_orb)
        mo_coeff = mf.rotate_mo(mo_coeff, u, log)
        dm = mf.make_rdm1(mo_coeff, mo_occ)
        vhf = mf._scf.get_veff(mol, dm, dm_last=dm_last, vhf_last=vhf)
        fock = mf.get_fock(h1e, s1e, vhf, dm, level_shift_factor=0)
        # NOTE: DO NOT change the initial guess mo_occ, mo_coeff
        if mf.verbose >= logger.DEBUG:
            mo_energy, mo_tmp = mf.eig(fock, s1e)
            mf.get_occ(mo_energy, mo_tmp)
# call mf._scf.energy_tot for dft, because the (dft).get_veff step saved _exc in mf._scf
        e_tot = mf._scf.energy_tot(dm, h1e, vhf)

        log.info('macro= %d  E= %.15g  delta_E= %g  |g|= %g  %d KF %d JK',
                 imacro, e_tot, e_tot - last_hf_e, norm_gorb, kfcount + 1,
                 jkcount)
        cput1 = log.timer('cycle= %d' % (imacro + 1), *cput1)

        if callable(mf.check_convergence):
            scf_conv = mf.check_convergence(locals())
        elif abs(e_tot - last_hf_e) < conv_tol and norm_gorb < conv_tol_grad:
            scf_conv = True

        if dump_chk:
            mf.dump_chk(locals())

        if callable(callback):
            callback(locals())

        if scf_conv:
            break

    if callable(callback):
        callback(locals())

    rotaiter.close()
    mo_energy, mo_coeff1 = mf._scf.canonicalize(mo_coeff, mo_occ, fock)
    if mf.canonicalization:
        log.info('Canonicalize SCF orbitals')
        mo_coeff = mo_coeff1
        if dump_chk:
            mf.dump_chk(locals())
    log.info('macro X = %d  E=%.15g  |g|= %g  total %d KF %d JK', imacro + 1,
             e_tot, norm_gorb, kftot + 1, jktot + 1)
    if (numpy.any(mo_occ == 0)
            and mo_energy[mo_occ > 0].max() > mo_energy[mo_occ == 0].min()):
        log.warn('H**O %s > LUMO %s was found in the canonicalized orbitals.',
                 mo_energy[mo_occ > 0].max(), mo_energy[mo_occ == 0].min())
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Exemplo n.º 36
0
def convert_to_ghf(mf, out=None, remove_df=False):
    '''Convert the given mean-field object to the generalized HF/KS object

    Note this conversion only changes the class of the mean-field object.
    The total energy and wave-function are the same as them in the input
    mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer
    will be discarded. Its underlying SCF object mf._scf will be converted.

    Args:
        mf : SCF object

    Kwargs
        remove_df : bool
            Whether to convert the DF-SCF object to the normal SCF object.
            This conversion is not applied by default.

    Returns:
        An generalized SCF object
    '''
    from pyscf import scf
    from pyscf import dft
    from pyscf.soscf import newton_ah
    assert (isinstance(mf, hf.SCF))

    logger.debug(mf, 'Converting %s to GHF', mf.__class__)

    def update_mo_(mf, mf1):
        if mf.mo_energy is not None:
            if isinstance(mf, scf.hf.RHF):  # RHF
                nao, nmo = mf.mo_coeff.shape
                orbspin = get_ghf_orbspin(mf.mo_energy, mf.mo_occ, True)

                mf1.mo_energy = numpy.empty(nmo * 2)
                mf1.mo_energy[orbspin == 0] = mf.mo_energy
                mf1.mo_energy[orbspin == 1] = mf.mo_energy
                mf1.mo_occ = numpy.empty(nmo * 2)
                mf1.mo_occ[orbspin == 0] = mf.mo_occ > 0
                mf1.mo_occ[orbspin == 1] = mf.mo_occ == 2

                mo_coeff = numpy.zeros((nao * 2, nmo * 2),
                                       dtype=mf.mo_coeff.dtype)
                mo_coeff[:nao, orbspin == 0] = mf.mo_coeff
                mo_coeff[nao:, orbspin == 1] = mf.mo_coeff
                if getattr(mf.mo_coeff, 'orbsym', None) is not None:
                    orbsym = numpy.zeros_like(orbspin)
                    orbsym[orbspin == 0] = mf.mo_coeff.orbsym
                    orbsym[orbspin == 1] = mf.mo_coeff.orbsym
                    mo_coeff = lib.tag_array(mo_coeff, orbsym=orbsym)
                mf1.mo_coeff = lib.tag_array(mo_coeff, orbspin=orbspin)

            else:  # UHF
                nao, nmo = mf.mo_coeff[0].shape
                orbspin = get_ghf_orbspin(mf.mo_energy, mf.mo_occ, False)

                mf1.mo_energy = numpy.empty(nmo * 2)
                mf1.mo_energy[orbspin == 0] = mf.mo_energy[0]
                mf1.mo_energy[orbspin == 1] = mf.mo_energy[1]
                mf1.mo_occ = numpy.empty(nmo * 2)
                mf1.mo_occ[orbspin == 0] = mf.mo_occ[0]
                mf1.mo_occ[orbspin == 1] = mf.mo_occ[1]

                mo_coeff = numpy.zeros((nao * 2, nmo * 2),
                                       dtype=mf.mo_coeff[0].dtype)
                mo_coeff[:nao, orbspin == 0] = mf.mo_coeff[0]
                mo_coeff[nao:, orbspin == 1] = mf.mo_coeff[1]
                if getattr(mf.mo_coeff[0], 'orbsym', None) is not None:
                    orbsym = numpy.zeros_like(orbspin)
                    orbsym[orbspin == 0] = mf.mo_coeff[0].orbsym
                    orbsym[orbspin == 1] = mf.mo_coeff[1].orbsym
                    mo_coeff = lib.tag_array(mo_coeff, orbsym=orbsym)
                mf1.mo_coeff = lib.tag_array(mo_coeff, orbspin=orbspin)
        return mf1

    if out is not None:
        assert (isinstance(out, scf.ghf.GHF))
        out = _update_mf_without_soscf(mf, out, remove_df)

    elif isinstance(mf, scf.ghf.GHF):
        if getattr(mf, '_scf', None):
            return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df)
        else:
            return copy.copy(mf)

    else:
        known_cls = {
            scf.hf.RHF: scf.ghf.GHF,
            scf.rohf.ROHF: scf.ghf.GHF,
            scf.uhf.UHF: scf.ghf.GHF,
            scf.hf_symm.RHF: scf.ghf_symm.GHF,
            scf.hf_symm.ROHF: scf.ghf_symm.GHF,
            scf.uhf_symm.UHF: scf.ghf_symm.GHF,
            dft.rks.RKS: None,
            dft.roks.ROKS: None,
            dft.uks.UKS: None,
            dft.rks_symm.RKS: None,
            dft.rks_symm.ROKS: None,
            dft.uks_symm.UKS: None
        }
        out = _object_without_soscf(mf, known_cls, remove_df)

    return update_mo_(mf, out)
Exemplo n.º 37
0
    def kernel(self, tolerance="default", maxiter=30):
        """
        Performs a self-consistent DMET calculation.
        Args:
            tolerance (float): convergence criterion;
            maxiter (int): maximal number of iterations;
        """
        if tolerance == "default":
            tolerance = self.conv_tol

        self.convergence_history = []

        # mixers = dict((k, diis.DIIS()) for k in self.__domains__.keys())

        while True:

            logger.info(self.__mol__, "DMET step {:d}".format(
                len(self.convergence_history),
            ))

            mf = self.run_mf_kernel()

            umat = {}

            logger.debug1(self.__mol__, "Mean-field solver total energy E = {:.10f}".format(
                mf.e_tot,
            ))

            self.e_tot = 0
            total_occupation = 0

            domain_ids = []
            embedded_solvers = []
            replica_numbers = []
            schmidt_bases = []

            # Build embedded solvers
            logger.info(self.__mol__, "Building embedded solvers ...")
            for domain_id, domain_basis, schmidt_basis in self.iter_schmidt_basis():

                domain_ids.append(domain_id)
                if self.__style__ == "interacting-bath":
                    embedded_solvers.append(self.get_embedded_solver(schmidt_basis))
                elif self.__style__ == "non-interacting-bath":
                    embedded_solvers.append(self.get_embedded_solver(schmidt_basis, kind=domain_id))
                else:
                    raise ValueError("Internal error: unknown style '{}'".format(self.__style__))
                replica_numbers.append(len(self.__domains__[domain_id]))
                schmidt_bases.append(schmidt_basis[2:])

            # Fit chemical potential
            logger.info(self.__mol__, "Fitting chemical potential ...")
            GlobalChemicalPotentialFit(
                embedded_solvers,
                replica_numbers,
                self.__mol__.nelectron,
                log=self.__mol__,
            ).kernel()

            # Fit the u-matrix
            logger.info(self.__mol__, "Fitting the u-matrix ...")
            for domain_id, embedded_solver, schmidt_basis, nreplica in zip(domain_ids, embedded_solvers, schmidt_bases, replica_numbers):

                logger.debug(self.__mol__, "Domain {}".format(domain_id))
                logger.debug1(self.__mol__, "Primary basis: {}".format(self.__domains__[domain_id][0]))
                if len(self.__domains__[domain_id]) > 1:
                    for i, b in enumerate(self.__domains__[domain_id][1:]):
                        logger.debug1(self.__mol__, "Secondary basis {:d}: {}".format(i, b))

                logger.debug1(self.__mol__, "Correlated solver total energy E = {:.10f}".format(
                    embedded_solver.e_tot,
                ))

                n_active_domain = schmidt_basis[0].shape[1]
                # TODO: fix this; no need to recalculate hcore
                partial_energy = embedded_solver.partial_etot(
                    slice(n_active_domain),
                    transform(
                        self.__mf_solver__.get_hcore(),
                        self.__orthogonal_basis_inv__.T.dot(numpy.concatenate(schmidt_basis, axis=1)),
                    ),
                )
                self.e_tot += nreplica * partial_energy

                logger.debug1(self.__mol__, "Correlated solver partial energy E = {:.10f}".format(
                    partial_energy,
                ))

                partial_occupation = embedded_solver.partial_nelec(slice(n_active_domain))
                total_occupation += nreplica * partial_occupation

                logger.debug1(self.__mol__, "Correlated solver partial occupation N = {:.10f}".format(
                    partial_occupation,
                ))

                logger.debug2(self.__mol__, "Correlated solver density matrix: {}".format(embedded_solver.make_rdm1()))

                if tolerance is not None:
                    # Continue with self-consistency
                    nscf_mf = NonSelfConsistentMeanField(mf)
                    nscf_mf.kernel()

                    sc = self.__self_consistency__(
                        nscf_mf,
                        self.__orthogonal_basis__.dot(numpy.concatenate(schmidt_basis, axis=1)),
                        embedded_solver,
                        log=self.__mol__,
                    )
                    sc.kernel(x0=None)

                    local_umat = sc.parametrize_umat_full(sc.final_parameters)
                    umat[domain_id] = local_umat

                    logger.debug(self.__mol__, "Parameters: {}".format(
                        sc.final_parameters,
                    ))

            self.e_tot += mf.energy_nuc()

            if tolerance is not None:
                self.convergence_history.append(self.convergence_measure(umat))
                self.umat = umat
                # self.umat = dict((k, mixers[k].update(umat[k])) for k in self.__domains__.keys())

                logger.info(self.__mol__, "E = {:.10f} delta = {:.3e} q = {:.3e} max(umat) = {:.3e}".format(
                    self.e_tot,
                    self.convergence_history[-1],
                    self.__mol__.nelectron - total_occupation,
                    max(v.max() for v in self.umat.values()),
                ))

            else:

                logger.info(self.__mol__, "E = {:.10f} q = {:.3e}".format(
                    self.e_tot,
                    self.__mol__.nelectron - total_occupation,
                ))

            if tolerance is None or self.convergence_history[-1] < tolerance:
                return self.e_tot

            if maxiter is not None and len(self.convergence_history) >= maxiter:
                raise RuntimeError("The maximal number of iterations {:d} reached. The error {:.3e} is still above the requested tolerance of {:.3e}".format(
                    maxiter,
                    self.convergence_history[-1],
                    tolerance,
                ))
Exemplo n.º 38
0
    def get_occ(self, mo_energy=None, mo_coeff=None, orbsym=None):
        ''' We assumed mo_energy are grouped by symmetry irreps, (see function
        self.eig). The orbitals are sorted after SCF.
        '''
        if mo_energy is None: mo_energy = self.mo_energy
        mol = self.mol
        if not mol.symmetry:
            return uhf.UHF.get_occ(self, mo_energy, mo_coeff)

        if orbsym is None:
            if mo_coeff is not None:  # due to linear-dep
                ovlp_ao = self.get_ovlp()
                orbsyma = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb,
                                              mo_coeff[0], ovlp_ao, False)
                orbsymb = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb,
                                              mo_coeff[1], ovlp_ao, False)
                orbsyma = numpy.asarray(orbsyma)
                orbsymb = numpy.asarray(orbsymb)
            else:
                ovlp_ao = None
                orbsyma = [
                    numpy.repeat(ir, mol.symm_orb[i].shape[1])
                    for i, ir in enumerate(mol.irrep_id)
                ]
                orbsyma = orbsymb = numpy.hstack(orbsyma)
        else:
            orbsyma = numpy.asarray(orbsym[0])
            orbsymb = numpy.asarray(orbsym[1])
        assert (mo_energy[0].size == orbsyma.size)

        mo_occ = numpy.zeros_like(mo_energy)
        idx_ea_left = []
        idx_eb_left = []
        neleca_fix = nelecb_fix = 0
        for i, ir in enumerate(mol.irrep_id):
            irname = mol.irrep_name[i]
            ir_idxa = numpy.where(orbsyma == ir)[0]
            ir_idxb = numpy.where(orbsymb == ir)[0]
            if irname in self.irrep_nelec:
                if isinstance(self.irrep_nelec[irname], (int, numpy.integer)):
                    nelecb = self.irrep_nelec[irname] // 2
                    neleca = self.irrep_nelec[irname] - nelecb
                else:
                    neleca, nelecb = self.irrep_nelec[irname]
                ea_idx = numpy.argsort(mo_energy[0][ir_idxa].round(9))
                eb_idx = numpy.argsort(mo_energy[1][ir_idxb].round(9))
                mo_occ[0, ir_idxa[ea_idx[:neleca]]] = 1
                mo_occ[1, ir_idxb[eb_idx[:nelecb]]] = 1
                neleca_fix += neleca
                nelecb_fix += nelecb
            else:
                idx_ea_left.append(ir_idxa)
                idx_eb_left.append(ir_idxb)

        neleca_float = self.nelec[0] - neleca_fix
        nelecb_float = self.nelec[1] - nelecb_fix
        assert (neleca_float >= 0)
        assert (nelecb_float >= 0)
        if len(idx_ea_left) > 0:
            idx_ea_left = numpy.hstack(idx_ea_left)
            ea_left = mo_energy[0][idx_ea_left]
            ea_sort = numpy.argsort(ea_left.round(9))
            occ_idx = idx_ea_left[ea_sort][:neleca_float]
            mo_occ[0][occ_idx] = 1
        if len(idx_eb_left) > 0:
            idx_eb_left = numpy.hstack(idx_eb_left)
            eb_left = mo_energy[1][idx_eb_left]
            eb_sort = numpy.argsort(eb_left.round(9))
            occ_idx = idx_eb_left[eb_sort][:nelecb_float]
            mo_occ[1][occ_idx] = 1

        vir_idx = (mo_occ[0] == 0)
        if self.verbose >= logger.INFO and numpy.count_nonzero(vir_idx) > 0:
            ehomoa = max(mo_energy[0][mo_occ[0] > 0])
            elumoa = min(mo_energy[0][mo_occ[0] == 0])
            ehomob = max(mo_energy[1][mo_occ[1] > 0])
            elumob = min(mo_energy[1][mo_occ[1] == 0])
            noccsa = []
            noccsb = []
            p0 = 0
            for i, ir in enumerate(mol.irrep_id):
                irname = mol.irrep_name[i]
                ir_idxa = orbsyma == ir
                ir_idxb = orbsymb == ir

                noccsa.append(numpy.count_nonzero(mo_occ[0][ir_idxa]))
                noccsb.append(numpy.count_nonzero(mo_occ[1][ir_idxb]))
                if ehomoa in mo_energy[0][ir_idxa]:
                    irhomoa = irname
                if elumoa in mo_energy[0][ir_idxa]:
                    irlumoa = irname
                if ehomob in mo_energy[1][ir_idxb]:
                    irhomob = irname
                if elumob in mo_energy[1][ir_idxb]:
                    irlumob = irname

            logger.info(self, 'alpha H**O (%s) = %.15g  LUMO (%s) = %.15g',
                        irhomoa, ehomoa, irlumoa, elumoa)
            logger.info(self, 'beta  H**O (%s) = %.15g  LUMO (%s) = %.15g',
                        irhomob, ehomob, irlumob, elumob)

            ehomo = max(ehomoa, ehomob)
            elumo = min(elumoa, elumob)
            logger.debug(self, 'alpha irrep_nelec = %s', noccsa)
            logger.debug(self, 'beta  irrep_nelec = %s', noccsb)
            hf_symm._dump_mo_energy(mol,
                                    mo_energy[0],
                                    mo_occ[0],
                                    ehomo,
                                    elumo,
                                    orbsyma,
                                    'alpha-',
                                    verbose=self.verbose)
            hf_symm._dump_mo_energy(mol,
                                    mo_energy[1],
                                    mo_occ[1],
                                    ehomo,
                                    elumo,
                                    orbsymb,
                                    'beta-',
                                    verbose=self.verbose)

            if mo_coeff is not None and self.verbose >= logger.DEBUG:
                if ovlp_ao is None:
                    ovlp_ao = self.get_ovlp()
                ss, s = self.spin_square((mo_coeff[0][:, mo_occ[0] > 0],
                                          mo_coeff[1][:, mo_occ[1] > 0]),
                                         ovlp_ao)
                logger.debug(self, 'multiplicity <S^2> = %.8g  2S+1 = %.8g',
                             ss, s)
        return mo_occ
Exemplo n.º 39
0
def DMRG_COMPRESS_NEVPT(mc, maxM=500, root=0, nevptsolver=None, tol=1e-7):
    if (isinstance(mc, str)):
        mol = chkfile.load_mol(mc)

        fh5 = h5py.File(mc, 'r')
        ncas = fh5['mc/ncas'].value
        ncore = fh5['mc/ncore'].value
        nvirt = fh5['mc/nvirt'].value
        nelecas = fh5['mc/nelecas'].value
        nroots = fh5['mc/nroots'].value
        wfnsym = fh5['mc/wfnsym'].value
        fh5.close()
        mc_chk = mc
    else:
        mol = mc.mol
        ncas = mc.ncas
        ncore = mc.ncore
        nvirt = mc.mo_coeff.shape[1] - mc.ncas - mc.ncore
        nelecas = mc.nelecas
        nroots = mc.fcisolver.nroots
        wfnsym = mc.fcisolver.wfnsym
        mc_chk = 'nevpt_perturb_integral'
        write_chk(mc, root, mc_chk)

    if nevptsolver is None:
        nevptsolver = default_nevpt_schedule(mol, maxM, tol)
        nevptsolver.wfnsym = wfnsym
        nevptsolver.block_extra_keyword = mc.fcisolver.block_extra_keyword
    nevptsolver.nroots = nroots
    from pyscf.dmrgscf import settings
    nevptsolver.executable = settings.BLOCKEXE_COMPRESS_NEVPT
    scratch = nevptsolver.scratchDirectory
    nevptsolver.scratchDirectory = ''

    dmrgci.writeDMRGConfFile(
        nevptsolver,
        nelecas,
        False,
        with_2pdm=False,
        extraline=['fullrestart', 'nevpt_state_num %d' % root])
    nevptsolver.scratchDirectory = scratch

    if nevptsolver.verbose >= logger.DEBUG1:
        inFile = os.path.join(nevptsolver.runtimeDir, nevptsolver.configFile)
        logger.debug1(nevptsolver, 'Block Input conf')
        logger.debug1(nevptsolver, open(inFile, 'r').read())

    t0 = (time.clock(), time.time())

    cmd = ' '.join(
        (nevptsolver.mpiprefix,
         '%s/nevpt_mpi.py' % os.path.dirname(os.path.realpath(__file__)),
         mc_chk, nevptsolver.executable,
         os.path.join(nevptsolver.runtimeDir, nevptsolver.configFile),
         nevptsolver.outputFile, nevptsolver.scratchDirectory))
    logger.debug(nevptsolver, 'DMRG_COMPRESS_NEVPT cmd %s', cmd)

    try:
        output = subprocess.check_call(cmd, shell=True)
    except subprocess.CalledProcessError as err:
        logger.error(nevptsolver, cmd)
        raise err

    if nevptsolver.verbose >= logger.DEBUG1:
        logger.debug1(
            nevptsolver,
            open(os.path.join(nevptsolver.scratchDirectory,
                              '0/dmrg.out')).read())

    fh5 = h5py.File('Perturbation_%d' % root, 'r')
    Vi_e = fh5['Vi/energy'].value
    Vr_e = fh5['Vr/energy'].value
    fh5.close()
    logger.note(nevptsolver, 'Nevpt Energy:')
    logger.note(nevptsolver, 'Sr Subspace:  E = %.14f' % (Vr_e))
    logger.note(nevptsolver, 'Si Subspace:  E = %.14f' % (Vi_e))

    logger.timer(nevptsolver, 'MPS NEVPT calculation time', *t0)
Exemplo n.º 40
0
    def get_occ(self, mo_energy=None, mo_coeff=None):
        if mo_energy is None: mo_energy = self.mo_energy
        mol = self.mol
        if not self.mol.symmetry:
            return rohf.ROHF.get_occ(self, mo_energy, mo_coeff)

        if getattr(mo_energy, 'mo_ea', None) is not None:
            mo_ea = mo_energy.mo_ea
            mo_eb = mo_energy.mo_eb
        else:
            mo_ea = mo_eb = mo_energy
        nmo = mo_ea.size
        mo_occ = numpy.zeros(nmo)

        orbsym = get_orbsym(self.mol, mo_coeff)

        rest_idx = numpy.ones(mo_occ.size, dtype=bool)
        neleca_fix = 0
        nelecb_fix = 0
        for i, ir in enumerate(mol.irrep_id):
            irname = mol.irrep_name[i]
            if irname in self.irrep_nelec:
                ir_idx = numpy.where(orbsym == ir)[0]
                if isinstance(self.irrep_nelec[irname], (int, numpy.integer)):
                    nelecb = self.irrep_nelec[irname] // 2
                    neleca = self.irrep_nelec[irname] - nelecb
                else:
                    neleca, nelecb = self.irrep_nelec[irname]
                if neleca > nelecb:
                    ncore, nopen = nelecb, neleca - nelecb
                else:
                    ncore, nopen = neleca, nelecb - neleca
                mo_occ[ir_idx] = rohf._fill_rohf_occ(mo_energy[ir_idx],
                                                     mo_ea[ir_idx],
                                                     mo_eb[ir_idx], ncore,
                                                     nopen)
                neleca_fix += neleca
                nelecb_fix += nelecb
                rest_idx[ir_idx] = False

        nelec_float = mol.nelectron - neleca_fix - nelecb_fix
        assert (nelec_float >= 0)
        if len(rest_idx) > 0:
            rest_idx = numpy.where(rest_idx)[0]
            nopen = abs(mol.spin - (neleca_fix - nelecb_fix))
            ncore = (nelec_float - nopen) // 2
            mo_occ[rest_idx] = rohf._fill_rohf_occ(mo_energy[rest_idx],
                                                   mo_ea[rest_idx],
                                                   mo_eb[rest_idx], ncore,
                                                   nopen)

        nocc, ncore = self.nelec
        nopen = nocc - ncore
        vir_idx = (mo_occ == 0)
        if self.verbose >= logger.INFO and nocc < nmo and ncore > 0:
            ehomo = max(mo_energy[~vir_idx])
            elumo = min(mo_energy[vir_idx])
            ndoccs = []
            nsoccs = []
            for i, ir in enumerate(mol.irrep_id):
                irname = mol.irrep_name[i]
                ir_idx = (orbsym == ir)

                ndoccs.append(numpy.count_nonzero(mo_occ[ir_idx] == 2))
                nsoccs.append(numpy.count_nonzero(mo_occ[ir_idx] == 1))
                if ehomo in mo_energy[ir_idx]:
                    irhomo = irname
                if elumo in mo_energy[ir_idx]:
                    irlumo = irname

            # to help self.eigh compute orbital energy
            self._irrep_doccs = ndoccs
            self._irrep_soccs = nsoccs

            logger.info(self, 'H**O (%s) = %.15g  LUMO (%s) = %.15g', irhomo,
                        ehomo, irlumo, elumo)

            logger.debug(self, 'double occ irrep_nelec = %s', ndoccs)
            logger.debug(self, 'single occ irrep_nelec = %s', nsoccs)
            #_dump_mo_energy(mol, mo_energy, mo_occ, ehomo, elumo, orbsym,
            #                verbose=self.verbose)
            if nopen > 0:
                core_idx = mo_occ == 2
                open_idx = mo_occ == 1
                vir_idx = mo_occ == 0
                logger.debug(
                    self,
                    '                  Roothaan           | alpha              | beta'
                )
                logger.debug(self,
                             '  Highest 2-occ = %18.15g | %18.15g | %18.15g',
                             max(mo_energy[core_idx]), max(mo_ea[core_idx]),
                             max(mo_eb[core_idx]))
                logger.debug(self,
                             '  Lowest 0-occ =  %18.15g | %18.15g | %18.15g',
                             min(mo_energy[vir_idx]), min(mo_ea[vir_idx]),
                             min(mo_eb[vir_idx]))
                for i in numpy.where(open_idx)[0]:
                    logger.debug(
                        self, '  1-occ =         %18.15g | %18.15g | %18.15g',
                        mo_energy[i], mo_ea[i], mo_eb[i])

            numpy.set_printoptions(threshold=nmo)
            logger.debug(self, '  Roothaan mo_energy =\n%s', mo_energy)
            logger.debug1(self, '  alpha mo_energy =\n%s', mo_ea)
            logger.debug1(self, '  beta  mo_energy =\n%s', mo_eb)
            numpy.set_printoptions(threshold=1000)
        return mo_occ
Exemplo n.º 41
0
def label_orb_symm(mol,
                   irrep_name,
                   symm_orb,
                   mo,
                   s=None,
                   check=True,
                   tol=1e-9):
    '''Label the symmetry of given orbitals

    irrep_name can be either the symbol or the ID of the irreducible
    representation.  If the ID is provided, it returns the numeric code
    associated with XOR operator, see :py:meth:`symm.param.IRREP_ID_TABLE`

    Args:
        mol : an instance of :class:`Mole`

        irrep_name : list of str or int
            A list of irrep ID or name,  it can be either mol.irrep_id or
            mol.irrep_name.  It can affect the return "label".
        symm_orb : list of 2d array
            the symmetry adapted basis
        mo : 2d array
            the orbitals to label

    Returns:
        list of symbols or integers to represent the irreps for the given
        orbitals

    Examples:

    >>> from pyscf import gto, scf, symm
    >>> mol = gto.M(atom='H 0 0 0; H 0 0 1', basis='ccpvdz',verbose=0, symmetry=1)
    >>> mf = scf.RHF(mol)
    >>> mf.kernel()
    >>> symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mf.mo_coeff)
    ['Ag', 'B1u', 'Ag', 'B1u', 'B2u', 'B3u', 'Ag', 'B2g', 'B3g', 'B1u']
    >>> symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mf.mo_coeff)
    [0, 5, 0, 5, 6, 7, 0, 2, 3, 5]
    '''
    nmo = mo.shape[1]
    if s is None:
        s = mol.intor_symmetric('int1e_ovlp')
    mo_s = numpy.dot(mo.T, s)
    norm = numpy.zeros((len(irrep_name), nmo))
    for i, csym in enumerate(symm_orb):
        moso = numpy.dot(mo_s, csym)
        ovlpso = reduce(numpy.dot, (csym.T, s, csym))
        try:
            norm[i] = numpy.einsum('ik,ki->i', moso,
                                   lib.cho_solve(ovlpso, moso.T))
        except:
            ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12
            norm[i] = numpy.einsum('ik,ki->i', moso,
                                   lib.cho_solve(ovlpso, moso.T))
    norm /= numpy.sum(norm, axis=0)  # for orbitals which are not normalized
    iridx = numpy.argmax(norm, axis=0)
    orbsym = numpy.asarray([irrep_name[i] for i in iridx])
    logger.debug(mol, 'irreps of each MO %s', orbsym)
    if check:
        largest_norm = norm[iridx, numpy.arange(nmo)]
        orbidx = numpy.where(largest_norm < 1 - tol)[0]
        if orbidx.size > 0:
            idx = numpy.where(largest_norm < 1 - tol * 1e2)[0]
            if idx.size > 0:
                raise ValueError('orbitals %s not symmetrized, norm = %s' %
                                 (idx, largest_norm[idx]))
            else:
                logger.warn(mol, 'orbitals %s not strictly symmetrized.',
                            numpy.unique(orbidx))
                logger.warn(
                    mol, 'They can be symmetrized with '
                    'pyscf.symm.symmetrize_space function.')
                logger.debug(mol, 'norm = %s', largest_norm[orbidx])
    return orbsym
Exemplo n.º 42
0
    def update_casdm(self, mo, u, fcivec, e_ci, eris, envs={}):
        nmo = mo.shape[1]
        rmat = u - numpy.eye(nmo)

        #g = hessian_co(self, mo, rmat, fcivec, e_ci, eris)
        ### hessian_co part start ###
        ncas = self.ncas
        nelecas = self.nelecas
        ncore = self.ncore
        nocc = ncore + ncas
        uc = u[:, :ncore]
        ua = u[:, ncore:nocc].copy()
        ra = rmat[:, ncore:nocc].copy()
        h1e_mo = reduce(numpy.dot, (mo.T, self.get_hcore(), mo))
        ddm = numpy.dot(uc, uc.T) * 2
        ddm[numpy.diag_indices(ncore)] -= 2
        if self.with_dep4:
            mo1 = numpy.dot(mo, u)
            mo1_cas = mo1[:, ncore:nocc]
            dm_core = numpy.dot(mo1[:, :ncore], mo1[:, :ncore].T) * 2
            vj, vk = self._scf.get_jk(self.mol, dm_core)
            h1 = (reduce(numpy.dot, (ua.T, h1e_mo, ua)) +
                  reduce(numpy.dot, (mo1_cas.T, vj - vk * .5, mo1_cas)))
            eris._paaa = self._exact_paaa(mo, u)
            h2 = eris._paaa[ncore:nocc]
            vj = vk = None
        else:
            p1aa = numpy.empty((nmo, ncas, ncas**2))
            paa1 = numpy.empty((nmo, ncas**2, ncas))
            jk = reduce(numpy.dot, (ua.T, eris.vhf_c, ua))
            for i in range(nmo):
                jbuf = eris.ppaa[i]
                kbuf = eris.papa[i]
                jk += (numpy.einsum('quv,q->uv', jbuf, ddm[i]) -
                       numpy.einsum('uqv,q->uv', kbuf, ddm[i]) * .5)
                p1aa[i] = lib.dot(ua.T, jbuf.reshape(nmo, -1))
                paa1[i] = lib.dot(kbuf.transpose(0, 2, 1).reshape(-1, nmo), ra)
            h1 = reduce(numpy.dot, (ua.T, h1e_mo, ua)) + jk
            aa11 = lib.dot(ua.T, p1aa.reshape(nmo, -1)).reshape((ncas, ) * 4)
            aaaa = eris.ppaa[ncore:nocc, ncore:nocc, :, :]
            aa11 = aa11 + aa11.transpose(2, 3, 0, 1) - aaaa

            a11a = numpy.dot(ra.T, paa1.reshape(nmo, -1)).reshape((ncas, ) * 4)
            a11a = a11a + a11a.transpose(1, 0, 2, 3)
            a11a = a11a + a11a.transpose(0, 1, 3, 2)
            h2 = aa11 + a11a
            jbuf = kbuf = p1aa = paa1 = aaaa = aa11 = a11a = None

        # pure core response
        # response of (1/2 dm * vhf * dm) ~ ddm*vhf
# Should I consider core response as a part of CI gradients?
        ecore = (numpy.einsum('pq,pq->', h1e_mo, ddm) +
                 numpy.einsum('pq,pq->', eris.vhf_c, ddm))
        ### hessian_co part end ###

        ci1, g = self.solve_approx_ci(h1, h2, fcivec, ecore, e_ci, envs)
        if g is not None:  # So state average CI, DMRG etc will not be applied
            ovlp = numpy.dot(fcivec.ravel(), ci1.ravel())
            norm_g = numpy.linalg.norm(g)
            if 1 - abs(ovlp) > norm_g * self.ci_grad_trust_region:
                logger.debug(
                    self, '<ci1|ci0>=%5.3g |g|=%5.3g, ci1 out of trust region',
                    ovlp, norm_g)
                ci1 = fcivec.ravel() + g
                ci1 *= 1 / numpy.linalg.norm(ci1)
        casdm1, casdm2 = self.fcisolver.make_rdm12(ci1, ncas, nelecas)

        return casdm1, casdm2, g, ci1
Exemplo n.º 43
0
def kernel(gw,
           mo_energy,
           mo_coeff,
           orbs=None,
           kptlist=None,
           nw=None,
           verbose=logger.NOTE):
    '''GW-corrected quasiparticle orbital energies

    Returns:
        A list :  converged, mo_energy, mo_coeff
    '''
    mf = gw._scf
    if gw.frozen is None:
        frozen = 0
    else:
        frozen = gw.frozen
    assert (frozen == 0)

    if orbs is None:
        orbs = range(gw.nmo)
    if kptlist is None:
        kptlist = range(gw.nkpts)
    nkpts = gw.nkpts
    nklist = len(kptlist)

    # v_xc
    dm = np.array(mf.make_rdm1())
    v_mf = np.array(mf.get_veff()) - np.array(mf.get_j(dm_kpts=dm))
    for k in range(nkpts):
        v_mf[k] = reduce(numpy.dot,
                         (mo_coeff[k].T.conj(), v_mf[k], mo_coeff[k]))

    nocc = gw.nocc
    nmo = gw.nmo

    # v_hf from DFT/HF density
    if gw.fc:
        exxdiv = 'ewald'
    else:
        exxdiv = None
    rhf = scf.KRHF(gw.mol, gw.kpts, exxdiv=exxdiv)
    rhf.with_df = gw.with_df
    if getattr(gw.with_df, '_cderi', None) is None:
        raise RuntimeError('Found incompatible integral scheme %s.'
                           'KGWAC can be only used with GDF integrals' %
                           gw.with_df.__class__)

    vk = rhf.get_veff(gw.mol, dm_kpts=dm) - rhf.get_j(gw.mol, dm_kpts=dm)
    for k in range(nkpts):
        vk[k] = reduce(numpy.dot, (mo_coeff[k].T.conj(), vk[k], mo_coeff[k]))

    # Grids for integration on imaginary axis
    freqs, wts = _get_scaled_legendre_roots(nw)

    # Compute self-energy on imaginary axis i*[0,iw_cutoff]
    sigmaI, omega = get_sigma_diag(gw, orbs, kptlist, freqs, wts, iw_cutoff=5.)

    # Analytic continuation
    coeff = []
    if gw.ac == 'twopole':
        for k in range(nklist):
            coeff.append(AC_twopole_diag(sigmaI[k], omega, orbs, nocc))
    elif gw.ac == 'pade':
        for k in range(nklist):
            coeff_tmp, omega_fit = AC_pade_thiele_diag(sigmaI[k], omega)
            coeff.append(coeff_tmp)
    coeff = np.array(coeff)

    conv = True
    # This code does not support metals
    h**o = -99.
    lumo = 99.
    for k in range(nkpts):
        if h**o < mf.mo_energy[k][nocc - 1]:
            h**o = mf.mo_energy[k][nocc - 1]
        if lumo > mf.mo_energy[k][nocc]:
            lumo = mf.mo_energy[k][nocc]
    ef = (h**o + lumo) / 2.

    mo_energy = np.zeros_like(np.array(mf.mo_energy))
    for k in range(nklist):
        kn = kptlist[k]
        for p in orbs:
            if gw.linearized:
                # linearized G0W0
                de = 1e-6
                ep = mf.mo_energy[kn][p]
                #TODO: analytic sigma derivative
                if gw.ac == 'twopole':
                    sigmaR = two_pole(ep - ef, coeff[k, :, p - orbs[0]]).real
                    dsigma = two_pole(ep - ef + de,
                                      coeff[k, :,
                                            p - orbs[0]]).real - sigmaR.real
                elif gw.ac == 'pade':
                    sigmaR = pade_thiele(ep - ef, omega_fit[p - orbs[0]],
                                         coeff[k, :, p - orbs[0]]).real
                    dsigma = pade_thiele(ep - ef + de, omega_fit[p - orbs[0]],
                                         coeff[k, :,
                                               p - orbs[0]]).real - sigmaR.real
                zn = 1.0 / (1.0 - dsigma / de)
                e = ep + zn * (sigmaR.real + vk[kn, p, p].real -
                               v_mf[kn, p, p].real)
                mo_energy[kn, p] = e
            else:
                # self-consistently solve QP equation
                def quasiparticle(omega):
                    if gw.ac == 'twopole':
                        sigmaR = two_pole(omega - ef, coeff[k, :,
                                                            p - orbs[0]]).real
                    elif gw.ac == 'pade':
                        sigmaR = pade_thiele(omega - ef,
                                             omega_fit[p - orbs[0]],
                                             coeff[k, :, p - orbs[0]]).real
                    return omega - mf.mo_energy[kn][p] - (
                        sigmaR.real + vk[kn, p, p].real - v_mf[kn, p, p].real)

                try:
                    e = newton(quasiparticle,
                               mf.mo_energy[kn][p],
                               tol=1e-6,
                               maxiter=100)
                    mo_energy[kn, p] = e
                except RuntimeError:
                    conv = False
    mo_coeff = mf.mo_coeff

    if gw.verbose >= logger.DEBUG:
        numpy.set_printoptions(threshold=nmo)
        for k in range(nkpts):
            logger.debug(gw, '  GW mo_energy @ k%d =\n%s', k, mo_energy[k])
        numpy.set_printoptions(threshold=1000)

    return conv, mo_energy, mo_coeff
Exemplo n.º 44
0
    def solve_approx_ci(self, h1, h2, ci0, ecore, e_ci, envs):
        ''' Solve CI eigenvalue/response problem approximately
        '''
        ncas = self.ncas
        nelecas = self.nelecas
        ncore = self.ncore
        nocc = ncore + ncas
        if 'norm_gorb' in envs:
            tol = max(self.conv_tol, envs['norm_gorb']**2 * .1)
        else:
            tol = None
        if hasattr(self.fcisolver, 'approx_kernel'):
            fn = self.fcisolver.approx_kernel
            ci1 = fn(h1,
                     h2,
                     ncas,
                     nelecas,
                     ci0=ci0,
                     tol=tol,
                     max_memory=self.max_memory)[1]
            return ci1, None
        elif not (hasattr(self.fcisolver, 'contract_2e')
                  and hasattr(self.fcisolver, 'absorb_h1e')):
            fn = self.fcisolver.kernel
            ci1 = fn(h1,
                     h2,
                     ncas,
                     nelecas,
                     ci0=ci0,
                     tol=tol,
                     max_memory=self.max_memory,
                     max_cycle=self.ci_response_space)[1]
            return ci1, None

        h2eff = self.fcisolver.absorb_h1e(h1, h2, ncas, nelecas, .5)
        hc = self.fcisolver.contract_2e(h2eff, ci0, ncas, nelecas).ravel()

        g = hc - (e_ci - ecore) * ci0.ravel()
        if self.ci_response_space > 7:
            logger.debug(self, 'CI step by full response')
            # full response
            max_memory = max(400, self.max_memory - lib.current_memory()[0])
            e, ci1 = self.fcisolver.kernel(h1,
                                           h2,
                                           ncas,
                                           nelecas,
                                           ci0=ci0,
                                           tol=tol,
                                           max_memory=max_memory)
        else:
            nd = min(max(self.ci_response_space, 2), ci0.size)
            logger.debug(self, 'CI step by %dD subspace response', nd)
            xs = [ci0.ravel()]
            ax = [hc]
            heff = numpy.empty((nd, nd))
            seff = numpy.empty((nd, nd))
            heff[0, 0] = numpy.dot(xs[0], ax[0])
            seff[0, 0] = 1
            for i in range(1, nd):
                xs.append(ax[i - 1] - xs[i - 1] * e_ci)
                ax.append(
                    self.fcisolver.contract_2e(h2eff, xs[i], ncas,
                                               nelecas).ravel())
                for j in range(i + 1):
                    heff[i, j] = heff[j, i] = numpy.dot(xs[i], ax[j])
                    seff[i, j] = seff[j, i] = numpy.dot(xs[i], xs[j])
            e, v = lib.safe_eigh(heff, seff)[:2]
            ci1 = xs[0] * v[0, 0]
            for i in range(1, nd):
                ci1 += xs[i] * v[i, 0]
        return ci1, g
Exemplo n.º 45
0
def gen_atomic_grids(mol,
                     atom_grid={},
                     radi_method=radi.gauss_chebyshev,
                     level=3,
                     prune=nwchem_prune):
    '''Generate number of radial grids and angular grids for the given molecule.

    Returns:
        A dict, with the atom symbol for the dict key.  For each atom type,
        the dict value has two items: one is the meshgrid coordinates wrt the
        atom center; the second is the volume of that grid.
    '''
    if isinstance(atom_grid, (list, tuple)):
        atom_grid = dict([(mol.atom_symbol(ia), atom_grid)
                          for ia in range(mol.natm)])
    atom_grids_tab = {}
    for ia in range(mol.natm):
        symb = mol.atom_symbol(ia)

        if symb not in atom_grids_tab:
            chg = gto.mole._charge(symb)
            if symb in atom_grid:
                n_rad, n_ang = atom_grid[symb]
                if n_ang not in LEBEDEV_NGRID:
                    if n_ang in LEBEDEV_ORDER:
                        n_ang = LEBEDEV_ORDER[n_ang]
                    else:
                        raise ValueError('Unsupported angular grids %d' %
                                         n_ang)
            else:
                n_rad = _default_rad(chg, level)
                n_ang = _default_ang(chg, level)
            rad, dr = radi_method(n_rad, chg)
            rad_weight = 4 * numpy.pi * rad * rad * dr
            # atomic_scale = 1
            # rad *= atomic_scale
            # rad_weight *= atomic_scale

            if callable(prune):
                angs = prune(chg, rad, n_ang)
            else:
                angs = [n_ang] * n_rad
            logger.debug(mol, 'atom %s rad-grids = %d, ang-grids = %s', symb,
                         n_rad, angs)

            angs = numpy.array(angs)
            coords = []
            vol = []
            for n in sorted(set(angs)):
                grid = numpy.empty((n, 4))
                libdft.MakeAngularGrid(grid.ctypes.data_as(ctypes.c_void_p),
                                       ctypes.c_int(n))
                idx = numpy.where(angs == n)[0]
                for i0, i1 in prange(0, len(idx),
                                     12):  # 12 radi-grids as a group
                    coords.append(
                        numpy.einsum('i,jk->jik', rad[idx[i0:i1]],
                                     grid[:, :3]).reshape(-1, 3))
                    vol.append(
                        numpy.einsum('i,j->ji', rad_weight[idx[i0:i1]],
                                     grid[:, 3]).ravel())
            atom_grids_tab[symb] = (numpy.vstack(coords), numpy.hstack(vol))
    return atom_grids_tab
Exemplo n.º 46
0
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1):
    '''Coulomb + XC functional

    .. note::
        This function will change the ks object.

    Args:
        ks : an instance of :class:`RKS`
            XC functional are controlled by ks.xc attribute.  Attribute
            ks.grids might be initialized.
        dm : ndarray or list of ndarrays
            A density matrix or a list of density matrices

    Kwargs:
        dm_last : ndarray or a list of ndarrays or 0
            The density matrix baseline.  If not 0, this function computes the
            increment of HF potential w.r.t. the reference HF potential matrix.
        vhf_last : ndarray or a list of ndarrays or 0
            The reference Vxc potential matrix.
        hermi : int
            Whether J, K matrix is hermitian

            | 0 : no hermitian or symmetric
            | 1 : hermitian
            | 2 : anti-hermitian

    Returns:
        matrix Veff = J + Vxc.  Veff can be a list matrices, if the input
        dm is a list of density matrices.
    '''
    if mol is None: mol = ks.mol
    if dm is None: dm = ks.make_rdm1()
    t0 = (time.clock(), time.time())

    ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2)

    if ks.grids.coords is None:
        ks.grids.build(with_non0tab=True)
        if ks.small_rho_cutoff > 1e-20 and ground_state:
            ks.grids = rks.prune_small_rho_grids_(ks, mol, dm, ks.grids)
        t0 = logger.timer(ks, 'setting up grids', *t0)

    if hermi == 2:  # because rho = 0
        n, exc, vxc = 0, 0, 0
    else:
        max_memory = ks.max_memory - lib.current_memory()[0]
        n, exc, vxc = ks._numint.r_vxc(mol,
                                       ks.grids,
                                       ks.xc,
                                       dm,
                                       hermi=hermi,
                                       max_memory=max_memory)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin)
    if abs(hyb) < 1e-10:
        vk = None
        if (ks._eri is None and ks.direct_scf
                and getattr(vhf_last, 'vj', None) is not None):
            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
            vj = ks.get_j(mol, ddm, hermi)
            vj += vhf_last.vj
        else:
            vj = ks.get_j(mol, dm, hermi)
        vxc += vj
    else:
        #        if (ks._eri is None and ks.direct_scf and
        #            getattr(vhf_last, 'vk', None) is not None):
        #            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
        #            vj, vk = ks.get_jk(mol, ddm, hermi)
        #            vj += vhf_last.vj
        #            vk += vhf_last.vk
        #        else:
        #            vj, vk = ks.get_jk(mol, dm, hermi)
        #        vxc += vj - vk * hyb
        #
        #        if ground_state:
        #            exc -= numpy.einsum('ij,ji', dm, vk).real * hyb * .5
        raise NotImplementedError

    if ground_state:
        ecoul = numpy.einsum('ij,ji', dm, vj).real * .5
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk)
    return vxc
Exemplo n.º 47
0
def get_veff(ks,
             cell=None,
             dm=None,
             dm_last=0,
             vhf_last=0,
             hermi=1,
             kpts=None,
             kpts_band=None):
    '''Coulomb + XC functional

    .. note::
        This is a replica of pyscf.dft.rks.get_veff with kpts added.
        This function will change the ks object.

    Args:
        ks : an instance of :class:`RKS`
            XC functional are controlled by ks.xc attribute.  Attribute
            ks.grids might be initialized.
        dm : ndarray or list of ndarrays
            A density matrix or a list of density matrices

    Returns:
        Veff : (nkpts, nao, nao) or (*, nkpts, nao, nao) ndarray
        Veff = J + Vxc.
    '''
    if cell is None: cell = ks.cell
    if dm is None: dm = ks.make_rdm1()
    if kpts is None: kpts = ks.kpts
    t0 = (logger.process_clock(), logger.perf_counter())

    omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin)
    hybrid = abs(hyb) > 1e-10 or abs(alpha) > 1e-10

    if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF):
        n, exc, vxc = multigrid.nr_rks(ks.with_df,
                                       ks.xc,
                                       dm,
                                       hermi,
                                       kpts,
                                       kpts_band,
                                       with_j=True,
                                       return_j=False)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)
        return vxc

    # ndim = 3 : dm.shape = (nkpts, nao, nao)
    ground_state = (isinstance(dm, np.ndarray) and dm.ndim == 3
                    and kpts_band is None)

    # For UniformGrids, grids.coords does not indicate whehter grids are initialized
    if ks.grids.non0tab is None:
        ks.grids.build(with_non0tab=True)
        if (isinstance(ks.grids, gen_grid.BeckeGrids)
                and ks.small_rho_cutoff > 1e-20 and ground_state):
            ks.grids = rks.prune_small_rho_grids_(ks, cell, dm, ks.grids, kpts)
        t0 = logger.timer(ks, 'setting up grids', *t0)

    if hermi == 2:  # because rho = 0
        n, exc, vxc = 0, 0, 0
    else:
        n, exc, vxc = ks._numint.nr_rks(cell, ks.grids, ks.xc, dm, 0, kpts,
                                        kpts_band)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    weight = 1. / len(kpts)
    if not hybrid:
        vj = ks.get_j(cell, dm, hermi, kpts, kpts_band)
        vxc += vj
    else:
        if getattr(ks.with_df, '_j_only', False):  # for GDF and MDF
            ks.with_df._j_only = False
        vj, vk = ks.get_jk(cell, dm, hermi, kpts, kpts_band)
        vk *= hyb
        if abs(omega) > 1e-10:
            vklr = ks.get_k(cell, dm, hermi, kpts, kpts_band, omega=omega)
            vklr *= (alpha - hyb)
            vk += vklr
        vxc += vj - vk * .5

        if ground_state:
            exc -= np.einsum('Kij,Kji', dm, vk).real * .5 * .5 * weight

    if ground_state:
        ecoul = np.einsum('Kij,Kji', dm, vj).real * .5 * weight
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None)
    return vxc
Exemplo n.º 48
0
def get_sigma_diag(gw,
                   orbs,
                   kptlist,
                   freqs,
                   wts,
                   iw_cutoff=None,
                   max_memory=8000):
    '''
    Compute GW correlation self-energy (diagonal elements)
    in MO basis on imaginary axis
    '''
    mo_energy = np.array(gw._scf.mo_energy)
    mo_coeff = np.array(gw._scf.mo_coeff)
    nocc = gw.nocc
    nmo = gw.nmo
    nkpts = gw.nkpts
    kpts = gw.kpts
    nklist = len(kptlist)
    nw = len(freqs)
    norbs = len(orbs)
    mydf = gw.with_df

    # possible kpts shift center
    kscaled = gw.mol.get_scaled_kpts(kpts)
    kscaled -= kscaled[0]

    # This code does not support metals
    h**o = -99.
    lumo = 99.
    for k in range(nkpts):
        if h**o < mo_energy[k][nocc - 1]:
            h**o = mo_energy[k][nocc - 1]
        if lumo > mo_energy[k][nocc]:
            lumo = mo_energy[k][nocc]
    if (lumo - h**o) < 1e-3:
        logger.warn(gw, 'This GW-AC code is not supporting metals!')
    ef = (h**o + lumo) / 2.

    # Integration on numerical grids
    if iw_cutoff is not None:
        nw_sigma = sum(iw < iw_cutoff for iw in freqs) + 1
    else:
        nw_sigma = nw + 1

    # Compute occ for -iw and vir for iw separately
    # to avoid branch cuts in analytic continuation
    omega_occ = np.zeros((nw_sigma), dtype=np.complex128)
    omega_vir = np.zeros((nw_sigma), dtype=np.complex128)
    omega_occ[1:] = -1j * freqs[:(nw_sigma - 1)]
    omega_vir[1:] = 1j * freqs[:(nw_sigma - 1)]
    orbs_occ = [i for i in orbs if i < nocc]
    norbs_occ = len(orbs_occ)

    emo_occ = np.zeros((nkpts, nmo, nw_sigma), dtype=np.complex128)
    emo_vir = np.zeros((nkpts, nmo, nw_sigma), dtype=np.complex128)
    for k in range(nkpts):
        emo_occ[k] = omega_occ[None, :] + ef - mo_energy[k][:, None]
        emo_vir[k] = omega_vir[None, :] + ef - mo_energy[k][:, None]

    sigma = np.zeros((nklist, norbs, nw_sigma), dtype=np.complex128)
    omega = np.zeros((norbs, nw_sigma), dtype=np.complex128)
    for p in range(norbs):
        orbp = orbs[p]
        if orbp < nocc:
            omega[p] = omega_occ.copy()
        else:
            omega[p] = omega_vir.copy()

    if gw.fc:
        # Set up q mesh for q->0 finite size correction
        q_pts = np.array([1e-3, 0, 0]).reshape(1, 3)
        q_abs = gw.mol.get_abs_kpts(q_pts)

        # Get qij = 1/sqrt(Omega) * < psi_{ik} | e^{iqr} | psi_{ak-q} > at q: (nkpts, nocc, nvir)
        qij = get_qij(gw, q_abs[0], mo_coeff)

    for kL in range(nkpts):
        # Lij: (ki, L, i, j) for looping every kL
        Lij = []
        # kidx: save kj that conserves with kL and ki (-ki+kj+kL=G)
        # kidx_r: save ki that conserves with kL and kj (-ki+kj+kL=G)
        kidx = np.zeros((nkpts), dtype=np.int64)
        kidx_r = np.zeros((nkpts), dtype=np.int64)
        for i, kpti in enumerate(kpts):
            for j, kptj in enumerate(kpts):
                # Find (ki,kj) that satisfies momentum conservation with kL
                kconserv = -kscaled[i] + kscaled[j] + kscaled[kL]
                is_kconserv = np.linalg.norm(np.round(kconserv) -
                                             kconserv) < 1e-12
                if is_kconserv:
                    kidx[i] = j
                    kidx_r[j] = i
                    logger.debug(
                        gw, "Read Lpq (kL: %s / %s, ki: %s, kj: %s)" %
                        (kL + 1, nkpts, i, j))
                    Lij_out = None
                    # Read (L|pq) and ao2mo transform to (L|ij)
                    Lpq = []
                    for LpqR, LpqI, sign \
                            in mydf.sr_loop([kpti, kptj], max_memory=0.1*gw._scf.max_memory, compact=False):
                        Lpq.append(LpqR + LpqI * 1.0j)
                    # support uneqaul naux on different k points
                    Lpq = np.vstack(Lpq).reshape(-1, nmo**2)
                    tao = []
                    ao_loc = None
                    moij, ijslice = _conc_mos(mo_coeff[i], mo_coeff[j])[2:]
                    Lij_out = _ao2mo.r_e2(Lpq,
                                          moij,
                                          ijslice,
                                          tao,
                                          ao_loc,
                                          out=Lij_out)
                    Lij.append(Lij_out.reshape(-1, nmo, nmo))
        Lij = np.asarray(Lij)
        naux = Lij.shape[1]

        if kL == 0:
            for w in range(nw):
                # body dielectric matrix eps_body
                Pi = get_rho_response(gw, freqs[w], mo_energy, Lij, kL, kidx)
                eps_body_inv = np.linalg.inv(np.eye(naux) - Pi)

                if gw.fc:
                    # head dielectric matrix eps_00
                    Pi_00 = get_rho_response_head(gw, freqs[w], mo_energy, qij)
                    eps_00 = 1. - 4. * np.pi / np.linalg.norm(
                        q_abs[0])**2 * Pi_00

                    # wings dielectric matrix eps_P0
                    Pi_P0 = get_rho_response_wing(gw, freqs[w], mo_energy, Lij,
                                                  qij)
                    eps_P0 = -np.sqrt(4. * np.pi) / np.linalg.norm(
                        q_abs[0]) * Pi_P0

                    # inverse dielectric matrix
                    eps_inv_00 = 1. / (eps_00 - np.dot(
                        np.dot(eps_P0.conj(), eps_body_inv), eps_P0))
                    eps_inv_P0 = -eps_inv_00 * np.dot(eps_body_inv, eps_P0)

                    # head correction
                    Del_00 = 2. / np.pi * (6. * np.pi**2 / gw.mol.vol / nkpts
                                           )**(1. / 3.) * (eps_inv_00 - 1.)

                eps_inv_PQ = eps_body_inv
                g0_occ = wts[w] * emo_occ / (emo_occ**2 + freqs[w]**2)
                g0_vir = wts[w] * emo_vir / (emo_vir**2 + freqs[w]**2)

                for k in range(nklist):
                    kn = kptlist[k]
                    # Find km that conserves with kn and kL (-km+kn+kL=G)
                    km = kidx_r[kn]
                    Qmn = einsum('Pmn,PQ->Qmn', Lij[km][:, :, orbs].conj(),
                                 eps_inv_PQ - np.eye(naux))
                    Wmn = 1. / nkpts * einsum('Qmn,Qmn->mn', Qmn,
                                              Lij[km][:, :, orbs])
                    sigma[k][:norbs_occ] += -einsum(
                        'mn,mw->nw', Wmn[:, :norbs_occ], g0_occ[km]) / np.pi
                    sigma[k][norbs_occ:] += -einsum(
                        'mn,mw->nw', Wmn[:, norbs_occ:], g0_vir[km]) / np.pi

                    if gw.fc:
                        # apply head correction
                        assert (kn == km)
                        sigma[k][:norbs_occ] += -Del_00 * g0_occ[kn][
                            orbs][:norbs_occ] / np.pi
                        sigma[k][norbs_occ:] += -Del_00 * g0_vir[kn][orbs][
                            norbs_occ:] / np.pi

                        # apply wing correction
                        Wn_P0 = einsum('Pnm,P->nm', Lij[kn],
                                       eps_inv_P0).diagonal()
                        Wn_P0 = Wn_P0.real * 2.
                        Del_P0 = np.sqrt(gw.mol.vol / 4. / np.pi**3) * (
                            6. * np.pi**2 / gw.mol.vol /
                            nkpts)**(2. / 3.) * Wn_P0[orbs]
                        sigma[k][:norbs_occ] += -einsum(
                            'n,nw->nw', Del_P0[:norbs_occ],
                            g0_occ[kn][orbs][:norbs_occ]) / np.pi
                        sigma[k][norbs_occ:] += -einsum(
                            'n,nw->nw', Del_P0[norbs_occ:],
                            g0_vir[kn][orbs][norbs_occ:]) / np.pi
        else:
            for w in range(nw):
                Pi = get_rho_response(gw, freqs[w], mo_energy, Lij, kL, kidx)
                Pi_inv = np.linalg.inv(np.eye(naux) - Pi) - np.eye(naux)
                g0_occ = wts[w] * emo_occ / (emo_occ**2 + freqs[w]**2)
                g0_vir = wts[w] * emo_vir / (emo_vir**2 + freqs[w]**2)
                for k in range(nklist):
                    kn = kptlist[k]
                    # Find km that conserves with kn and kL (-km+kn+kL=G)
                    km = kidx_r[kn]
                    Qmn = einsum('Pmn,PQ->Qmn', Lij[km][:, :, orbs].conj(),
                                 Pi_inv)
                    Wmn = 1. / nkpts * einsum('Qmn,Qmn->mn', Qmn,
                                              Lij[km][:, :, orbs])
                    sigma[k][:norbs_occ] += -einsum(
                        'mn,mw->nw', Wmn[:, :norbs_occ], g0_occ[km]) / np.pi
                    sigma[k][norbs_occ:] += -einsum(
                        'mn,mw->nw', Wmn[:, norbs_occ:], g0_vir[km]) / np.pi

    return sigma, omega
Exemplo n.º 49
0
def kernel(gw,
           mo_energy,
           mo_coeff,
           Lpq=None,
           orbs=None,
           nw=None,
           vhf_df=False,
           verbose=logger.NOTE):
    '''
    GW-corrected quasiparticle orbital energies
    Returns:
        A list :  converged, mo_energy, mo_coeff
    '''
    mf = gw._scf
    mol = gw.mol
    if gw.frozen is None:
        frozen = 0
    else:
        frozen = gw.frozen

    assert (isinstance(frozen, int))

    nocca, noccb = gw.nocc
    nmoa, nmob = gw.nmo
    # only support frozen core
    assert (frozen < nocca and frozen < noccb)

    if Lpq is None:
        Lpq = gw.ao2mo(mo_coeff)
    if orbs is None:
        orbs = range(nmoa)
    else:
        orbs = [x - frozen for x in orbs]
        if orbs[0] < 0:
            logger.warn(gw, 'GW orbs must be larger than frozen core!')
            raise RuntimeError

    # v_xc
    v_mf = mf.get_veff()
    vj = mf.get_j()
    v_mf[0] = v_mf[0] - (vj[0] + vj[1])
    v_mf[1] = v_mf[1] - (vj[0] + vj[1])
    v_mf_frz = np.zeros((2, nmoa - frozen, nmob - frozen))
    for s in range(2):
        v_mf_frz[s] = reduce(numpy.dot, (mo_coeff[s].T, v_mf[s], mo_coeff[s]))
    v_mf = v_mf_frz

    # v_hf from DFT/HF density
    if vhf_df and frozen == 0:
        # density fitting vk
        vk = np.zeros_like(v_mf)
        vk[0] = -einsum('Lni,Lim->nm', Lpq[0, :, :, :nocca],
                        Lpq[0, :, :nocca, :])
        vk[1] = -einsum('Lni,Lim->nm', Lpq[1, :, :, :noccb],
                        Lpq[1, :, :noccb, :])
    else:
        # exact vk without density fitting
        dm = mf.make_rdm1()
        uhf = scf.UHF(mol)
        vk = uhf.get_veff(mol, dm)
        vj = uhf.get_j(mol, dm)
        vk[0] = vk[0] - (vj[0] + vj[1])
        vk[1] = vk[1] - (vj[0] + vj[1])
        vk_frz = np.zeros((2, nmoa - frozen, nmob - frozen))
        for s in range(2):
            vk_frz[s] = reduce(numpy.dot, (mo_coeff[s].T, vk[s], mo_coeff[s]))
        vk = vk_frz

    # Grids for integration on imaginary axis
    freqs, wts = _get_scaled_legendre_roots(nw)

    # Compute self-energy on imaginary axis i*[0,iw_cutoff]
    sigmaI, omega = get_sigma_diag(gw, orbs, Lpq, freqs, wts, iw_cutoff=5.)

    # Analytic continuation
    if gw.ac == 'twopole':
        coeff_a = AC_twopole_diag(sigmaI[0], omega[0], orbs, nocca)
        coeff_b = AC_twopole_diag(sigmaI[1], omega[1], orbs, noccb)
    elif gw.ac == 'pade':
        coeff_a, omega_fit_a = AC_pade_thiele_diag(sigmaI[0], omega[0])
        coeff_b, omega_fit_b = AC_pade_thiele_diag(sigmaI[1], omega[1])
        omega_fit = np.asarray((omega_fit_a, omega_fit_b))
    coeff = np.asarray((coeff_a, coeff_b))

    conv = True
    h**o = max(mo_energy[0][nocca - 1], mo_energy[1][noccb - 1])
    lumo = min(mo_energy[0][nocca], mo_energy[1][noccb])
    ef = (h**o + lumo) / 2.
    mf_mo_energy = mo_energy.copy()
    mo_energy = np.zeros_like(np.asarray(gw._scf.mo_energy))
    for s in range(2):
        for p in orbs:
            if gw.linearized:
                # linearized G0W0
                de = 1e-6
                ep = mf_mo_energy[s][p]
                #TODO: analytic sigma derivative
                if gw.ac == 'twopole':
                    sigmaR = two_pole(ep - ef, coeff[s, :, p - orbs[0]]).real
                    dsigma = two_pole(ep - ef + de,
                                      coeff[s, :,
                                            p - orbs[0]]).real - sigmaR.real
                elif gw.ac == 'pade':
                    sigmaR = pade_thiele(ep - ef, omega_fit[s, p - orbs[0]],
                                         coeff[s, :, p - orbs[0]]).real
                    dsigma = pade_thiele(
                        ep - ef + de, omega_fit[s, p - orbs[0]],
                        coeff[s, :, p - orbs[0]]).real - sigmaR.real
                zn = 1.0 / (1.0 - dsigma / de)
                e = ep + zn * (sigmaR.real + vk[s, p, p] - v_mf[s, p, p])
                mo_energy[s, p + frozen] = e
            else:
                # self-consistently solve QP equation
                def quasiparticle(omega):
                    if gw.ac == 'twopole':
                        sigmaR = two_pole(omega - ef, coeff[s, :,
                                                            p - orbs[0]]).real
                    elif gw.ac == 'pade':
                        sigmaR = pade_thiele(omega - ef,
                                             omega_fit[s, p - orbs[0]],
                                             coeff[s, :, p - orbs[0]]).real
                    return omega - mf_mo_energy[s][p] - (
                        sigmaR.real + vk[s, p, p] - v_mf[s, p, p])

                try:
                    e = newton(quasiparticle,
                               mf_mo_energy[s][p],
                               tol=1e-6,
                               maxiter=100)
                    mo_energy[s, p + frozen] = e
                except RuntimeError:
                    conv = False
    mo_coeff = gw._scf.mo_coeff

    if gw.verbose >= logger.DEBUG:
        numpy.set_printoptions(threshold=nmoa)
        logger.debug(gw, '  GW mo_energy spin-up =\n%s', mo_energy[0])
        logger.debug(gw, '  GW mo_energy spin-down =\n%s', mo_energy[1])
        numpy.set_printoptions(threshold=1000)

    return conv, mo_energy, mo_coeff
Exemplo n.º 50
0
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1):
    '''Coulomb + XC functional

    .. note::
        This function will change the ks object.

    Args:
        ks : an instance of :class:`RKS`
            XC functional are controlled by ks.xc attribute.  Attribute
            ks.grids might be initialized.
        dm : ndarray or list of ndarrays
            A density matrix or a list of density matrices

    Kwargs:
        dm_last : ndarray or a list of ndarrays or 0
            The density matrix baseline.  If not 0, this function computes the
            increment of HF potential w.r.t. the reference HF potential matrix.
        vhf_last : ndarray or a list of ndarrays or 0
            The reference Vxc potential matrix.
        hermi : int
            Whether J, K matrix is hermitian

            | 0 : no hermitian or symmetric
            | 1 : hermitian
            | 2 : anti-hermitian

    Returns:
        matrix Veff = J + Vxc.  Veff can be a list matrices, if the input
        dm is a list of density matrices.
    '''
    if mol is None: mol = ks.mol
    if dm is None: dm = ks.make_rdm1()
    t0 = (time.clock(), time.time())

    ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2)

    if ks.grids.coords is None:
        ks.grids.build(with_non0tab=True)
        if ks.small_rho_cutoff > 1e-20 and ground_state:
            # Filter grids the first time setup grids
            ks.grids = prune_small_rho_grids_(ks, mol, dm, ks.grids)
        t0 = logger.timer(ks, 'setting up grids', *t0)
    if ks.nlc != '':
        if ks.nlcgrids.coords is None:
            ks.nlcgrids.build(with_non0tab=True)
            if ks.small_rho_cutoff > 1e-20 and ground_state:
                # Filter grids the first time setup grids
                ks.nlcgrids = prune_small_rho_grids_(ks, mol, dm, ks.nlcgrids)
            t0 = logger.timer(ks, 'setting up nlc grids', *t0)

    ni = ks._numint
    if hermi == 2:  # because rho = 0
        n, exc, vxc = 0, 0, 0
    else:
        n, exc, vxc = ni.nr_rks(mol, ks.grids, ks.xc, dm)
        if ks.nlc != '':
            assert ('VV10' in ks.nlc.upper())
            _, enlc, vnlc = ni.nr_rks(mol, ks.nlcgrids, ks.xc + '__' + ks.nlc,
                                      dm)
            exc += enlc
            vxc += vnlc
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    #enabling range-separated hybrids
    omega, alpha, hyb = ni.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin)
    if ks.omega is not None:
        omega = ks.omega

    if abs(hyb) < 1e-10 and abs(alpha) < 1e-10:
        vk = None
        if (ks._eri is None and ks.direct_scf
                and getattr(vhf_last, 'vj', None) is not None):
            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
            vj = ks.get_j(mol, ddm, hermi)
            vj += vhf_last.vj
        else:
            vj = ks.get_j(mol, dm, hermi)
        vxc += vj
    else:
        if (ks._eri is None and ks.direct_scf
                and getattr(vhf_last, 'vk', None) is not None):
            ddm = numpy.asarray(dm) - numpy.asarray(dm_last)
            vj, vk = ks.get_jk(mol, ddm, hermi)
            vk *= hyb
            if abs(omega) > 1e-10:  # For range separated Coulomb operator
                vklr = _get_k_lr(mol, ddm, omega, hermi)
                vklr *= (alpha - hyb)
                vk += vklr
            vj += vhf_last.vj
            vk += vhf_last.vk
        else:
            vj, vk = ks.get_jk(mol, dm, hermi)
            vk *= hyb
            if abs(omega) > 1e-10:
                vklr = _get_k_lr(mol, dm, omega, hermi)
                vklr *= (alpha - hyb)
                vk += vklr
        vxc += vj - vk * .5

        if ground_state:
            exc -= numpy.einsum('ij,ji', dm, vk) * .5 * .5

    if ground_state:
        ecoul = numpy.einsum('ij,ji', dm, vj) * .5
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk)
    return vxc
Exemplo n.º 51
0
def kernel(gw,
           mo_energy,
           mo_coeff,
           td_e,
           td_xy,
           eris=None,
           orbs=None,
           verbose=logger.NOTE):
    '''GW-corrected quasiparticle orbital energies

    Returns:
        A list :  converged, mo_energy, mo_coeff
    '''
    # mf must be DFT; for HF use xc = 'hf'
    mf = gw._scf
    assert (isinstance(mf, (dft.rks.RKS, dft.uks.UKS, dft.roks.ROKS,
                            dft.uks.UKS, dft.rks_symm.RKS, dft.uks_symm.UKS,
                            dft.rks_symm.ROKS, dft.uks_symm.UKS)))
    assert (gw.frozen is 0 or gw.frozen is None)

    if eris is None:
        eris = gw.ao2mo(mo_coeff)
    if orbs is None:
        orbs = range(gw.nmo)

    v_mf = mf.get_veff() - mf.get_j()
    v_mf = reduce(numpy.dot, (mo_coeff.T, v_mf, mo_coeff))

    nocc = gw.nocc
    nmo = gw.nmo
    nvir = nmo - nocc

    vk_oo = -np.einsum('piiq->pq', eris.oooo)
    vk_ov = -np.einsum('piiq->pq', eris.ooov)
    vk_vv = -np.einsum('ipqi->pq', eris.ovvo).conj()
    vk = np.array(np.bmat([[vk_oo, vk_ov], [vk_ov.T, vk_vv]]))

    nexc = len(td_e)
    # factor of 2 for normalization, see tddft/rhf.py
    td_xy = 2 * np.asarray(td_xy)  # (nexc, 2, nvir, nocc)
    td_z = np.sum(td_xy, axis=1).reshape(nexc, nvir, nocc)
    tdm_oo = einsum('vai,iapq->vpq', td_z, eris.ovoo)
    tdm_ov = einsum('vai,iapq->vpq', td_z, eris.ovov)
    tdm_vv = einsum('vai,iapq->vpq', td_z, eris.ovvv)
    tdm = []
    for oo, ov, vv in zip(tdm_oo, tdm_ov, tdm_vv):
        tdm.append(np.array(np.bmat([[oo, ov], [ov.T, vv]])))
    tdm = np.asarray(tdm)

    conv = True
    mo_energy = np.zeros_like(gw._scf.mo_energy)
    for p in orbs:
        tdm_p = tdm[:, :, p]
        if gw.linearized:
            de = 1e-6
            ep = gw._scf.mo_energy[p]
            #TODO: analytic sigma derivative
            sigma = get_sigma_element(gw, ep, tdm_p, tdm_p, td_e).real
            dsigma = get_sigma_element(gw, ep + de, tdm_p, tdm_p,
                                       td_e).real - sigma
            zn = 1.0 / (1 - dsigma / de)
            e = ep + zn * (sigma.real + vk[p, p] - v_mf[p, p])
            mo_energy[p] = e
        else:

            def quasiparticle(omega):
                sigma = get_sigma_element(gw, omega, tdm_p, tdm_p, td_e)
                return omega - gw._scf.mo_energy[p] - (sigma.real + vk[p, p] -
                                                       v_mf[p, p])

            try:
                e = newton(quasiparticle,
                           gw._scf.mo_energy[p],
                           tol=1e-6,
                           maxiter=100)
                mo_energy[p] = e
            except RuntimeError:
                conv = False
    mo_coeff = gw._scf.mo_coeff

    if gw.verbose >= logger.DEBUG:
        numpy.set_printoptions(threshold=nmo)
        logger.debug(gw, '  GW mo_energy =\n%s', mo_energy)
        numpy.set_printoptions(threshold=1000)

    return conv, mo_energy, mo_coeff
Exemplo n.º 52
0
def get_occ(mf, mo_energy_kpts=None, mo_coeff_kpts=None):
    '''Label the occupancies for each orbital for sampled k-points.

    This is a k-point version of scf.hf.SCF.get_occ
    '''

    if mo_energy_kpts is None: mo_energy_kpts = mf.mo_energy
    if getattr(mo_energy_kpts[0], 'mo_ea', None) is not None:
        mo_ea_kpts = [x.mo_ea for x in mo_energy_kpts]
        mo_eb_kpts = [x.mo_eb for x in mo_energy_kpts]
    else:
        mo_ea_kpts = mo_eb_kpts = mo_energy_kpts

    nocc_a, nocc_b = mf.nelec
    mo_energy_kpts1 = np.hstack(mo_energy_kpts)
    mo_energy = np.sort(mo_energy_kpts1)
    if nocc_b > 0:
        core_level = mo_energy[nocc_b - 1]
    else:
        core_level = -1e9
    if nocc_a == nocc_b:
        fermi = core_level
    else:
        mo_ea_kpts1 = np.hstack(mo_ea_kpts)
        mo_ea = np.sort(mo_ea_kpts1[mo_energy_kpts1 > core_level])
        fermi = mo_ea[nocc_a - nocc_b - 1]

    mo_occ_kpts = []
    for k, mo_e in enumerate(mo_energy_kpts):
        occ = np.zeros_like(mo_e)
        occ[mo_e <= core_level] = 2
        if nocc_a != nocc_b:
            occ[(mo_e > core_level) & (mo_ea_kpts[k] <= fermi)] = 1
        mo_occ_kpts.append(occ)

    if nocc_a < len(mo_energy):
        logger.info(mf, 'H**O = %.12g  LUMO = %.12g', mo_energy[nocc_a - 1],
                    mo_energy[nocc_a])
    else:
        logger.info(mf, 'H**O = %.12g', mo_energy[nocc_a - 1])

    np.set_printoptions(threshold=len(mo_energy))
    if mf.verbose >= logger.DEBUG:
        logger.debug(
            mf,
            '                  Roothaan           | alpha              | beta')
        for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)):
            core_idx = mo_occ_kpts[k] == 2
            open_idx = mo_occ_kpts[k] == 1
            vir_idx = mo_occ_kpts[k] == 0
            logger.debug(mf, '  kpt %2d (%6.3f %6.3f %6.3f)', k, kpt[0],
                         kpt[1], kpt[2])
            if np.count_nonzero(core_idx) > 0:
                logger.debug(mf,
                             '  Highest 2-occ = %18.15g | %18.15g | %18.15g',
                             max(mo_energy_kpts[k][core_idx]),
                             max(mo_ea_kpts[k][core_idx]),
                             max(mo_eb_kpts[k][core_idx]))
            if np.count_nonzero(vir_idx) > 0:
                logger.debug(mf,
                             '  Lowest 0-occ =  %18.15g | %18.15g | %18.15g',
                             min(mo_energy_kpts[k][vir_idx]),
                             min(mo_ea_kpts[k][vir_idx]),
                             min(mo_eb_kpts[k][vir_idx]))
            for i in np.where(open_idx)[0]:
                logger.debug(mf,
                             '  1-occ =         %18.15g | %18.15g | %18.15g',
                             mo_energy_kpts[k][i], mo_ea_kpts[k][i],
                             mo_eb_kpts[k][i])

        logger.debug(mf, '     k-point                  Roothaan mo_energy')
        for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)):
            logger.debug(mf, '  %2d (%6.3f %6.3f %6.3f)   %s %s', k, kpt[0],
                         kpt[1], kpt[2], mo_energy_kpts[k][mo_occ_kpts[k] > 0],
                         mo_energy_kpts[k][mo_occ_kpts[k] == 0])

    if mf.verbose >= logger.DEBUG1:
        logger.debug1(mf, '     k-point                  alpha mo_energy')
        for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)):
            logger.debug1(mf, '  %2d (%6.3f %6.3f %6.3f)   %s %s', k, kpt[0],
                          kpt[1], kpt[2], mo_ea_kpts[k][mo_occ_kpts[k] > 0],
                          mo_ea_kpts[k][mo_occ_kpts[k] == 0])
        logger.debug1(mf, '     k-point                  beta  mo_energy')
        for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)):
            logger.debug1(mf, '  %2d (%6.3f %6.3f %6.3f)   %s %s', k, kpt[0],
                          kpt[1], kpt[2], mo_eb_kpts[k][mo_occ_kpts[k] == 2],
                          mo_eb_kpts[k][mo_occ_kpts[k] != 2])
    np.set_printoptions(threshold=1000)

    return mo_occ_kpts
Exemplo n.º 53
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
Exemplo n.º 54
0
def get_veff(ks,
             cell=None,
             dm=None,
             dm_last=0,
             vhf_last=0,
             hermi=1,
             kpt=None,
             kpts_band=None):
    '''Coulomb + XC functional

    .. note::
        This function will change the ks object.

    Args:
        ks : an instance of :class:`RKS`
            XC functional are controlled by ks.xc attribute.  Attribute
            ks.grids might be initialized.
        dm : ndarray or list of ndarrays
            A density matrix or a list of density matrices

    Returns:
        matrix Veff = J + Vxc.  Veff can be a list matrices, if the input
        dm is a list of density matrices.
    '''
    if cell is None: cell = ks.cell
    if dm is None: dm = ks.make_rdm1()
    if kpt is None: kpt = ks.kpt
    t0 = (time.clock(), time.time())

    omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin)
    hybrid = abs(hyb) > 1e-10 or abs(alpha) > 1e-10

    if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF):
        n, exc, vxc = multigrid.nr_rks(ks.with_df,
                                       ks.xc,
                                       dm,
                                       hermi,
                                       kpt.reshape(1, 3),
                                       kpts_band,
                                       with_j=True,
                                       return_j=False)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)
        return vxc

    ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2
                    and kpts_band is None)

    # Use grids.non0tab to detect whether grids are initialized.  For
    # UniformGrids, grids.coords as a property cannot indicate whehter grids are
    # initialized.
    if ks.grids.non0tab is None:
        ks.grids.build(with_non0tab=True)
        if (isinstance(ks.grids, gen_grid.BeckeGrids)
                and ks.small_rho_cutoff > 1e-20 and ground_state):
            ks.grids = prune_small_rho_grids_(ks, cell, dm, ks.grids, kpt)
        t0 = logger.timer(ks, 'setting up grids', *t0)

    if hermi == 2:  # because rho = 0
        n, exc, vxc = 0, 0, 0
    else:
        n, exc, vxc = ks._numint.nr_rks(cell, ks.grids, ks.xc, dm, 0, kpt,
                                        kpts_band)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    if not hybrid:
        vj = ks.get_j(cell, dm, hermi, kpt, kpts_band)
        vxc += vj
    else:
        if getattr(ks.with_df, '_j_only', False):  # for GDF and MDF
            ks.with_df._j_only = False
        vj, vk = ks.get_jk(cell, dm, hermi, kpt, kpts_band)
        vk *= hyb
        if abs(omega) > 1e-10:
            vklr = ks.get_k(cell, dm, hermi, kpt, kpts_band, omega=omega)
            vklr *= (alpha - hyb)
            vk += vklr
        vxc += vj - vk * .5

        if ground_state:
            exc -= numpy.einsum('ij,ji', dm, vk).real * .5 * .5

    if ground_state:
        ecoul = numpy.einsum('ij,ji', dm, vj).real * .5
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None)
    return vxc
Exemplo n.º 55
0
    def get_occ(mo_energy_kpts=None, mo_coeff_kpts=None):
        '''Label the occupancies for each orbital for sampled k-points.

        This is a k-point version of scf.hf.SCF.get_occ
        '''
        mo_occ_kpts = mf_class.get_occ(mf, mo_energy_kpts, mo_coeff_kpts)
        if mf.sigma == 0 or not mf.sigma or not mf.smearing_method:
            return mo_occ_kpts

        if is_khf:
            nkpts = len(mf.kpts)
        else:
            nkpts = 1
        if is_uhf:
            nocc = cell_nelec * nkpts
            mo_es = numpy.append(numpy.hstack(mo_energy_kpts[0]),
                                 numpy.hstack(mo_energy_kpts[1]))
        else:
            nocc = cell_nelec * nkpts // 2
            mo_es = numpy.hstack(mo_energy_kpts)

        if mf.smearing_method.lower() == 'fermi':  # Fermi-Dirac smearing
            f_occ = fermi_smearing_occ
        else:  # Gaussian smearing
            f_occ = gaussian_smearing_occ

        mo_energy = numpy.sort(mo_es.ravel())
        fermi = mo_energy[nocc - 1]
        sigma = mf.sigma

        def nelec_cost_fn(m):
            mo_occ_kpts = f_occ(m, mo_es, sigma)
            if not is_uhf:
                mo_occ_kpts *= 2
            return (mo_occ_kpts.sum() / nkpts - cell_nelec)**2

        res = scipy.optimize.minimize(nelec_cost_fn, fermi, method='Powell')
        mu = res.x
        mo_occs = f = f_occ(mu, mo_es, sigma)
        f = f[(f > 0) & (f < 1)]
        mf.entropy = -(f * numpy.log(f) +
                       (1 - f) * numpy.log(1 - f)).sum() / nkpts
        if not is_uhf:
            mo_occs *= 2
            mf.entropy *= 2

        # DO NOT use numpy.array for mo_occ_kpts and mo_energy_kpts, they may
        # have different dimensions for different k-points
        if is_uhf:
            if is_khf:
                nao_tot = mo_occs.size // 2
                mo_occ_kpts = (partition_occ(mo_occs[:nao_tot],
                                             mo_energy_kpts[0]),
                               partition_occ(mo_occs[nao_tot:],
                                             mo_energy_kpts[1]))
            else:
                mo_occ_kpts = partition_occ(mo_occs, mo_energy_kpts)
        else:
            if is_khf:
                mo_occ_kpts = partition_occ(mo_occs, mo_energy_kpts)
            else:
                mo_occ_kpts = mo_occs

        logger.debug(
            mf,
            '    Fermi level %g  Sum mo_occ_kpts = %s  should equal nelec = %s',
            fermi,
            mo_occs.sum() / nkpts, cell_nelec)
        logger.info(mf,
                    '    sigma = %g  Optimized mu = %.12g  entropy = %.12g',
                    mf.sigma, mu, mf.entropy)

        return mo_occ_kpts
Exemplo n.º 56
0
def get_veff(ks,
             cell=None,
             dm=None,
             dm_last=0,
             vhf_last=0,
             hermi=1,
             kpts=None,
             kpts_band=None):
    '''Coulomb + XC functional for UKS.  See pyscf/pbc/dft/uks.py
    :func:`get_veff` fore more details.
    '''
    if cell is None: cell = ks.cell
    if dm is None: dm = ks.make_rdm1()
    if kpts is None: kpts = ks.kpts
    t0 = (time.clock(), time.time())

    omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin)
    hybrid = abs(hyb) > 1e-10

    if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF):
        n, exc, vxc = multigrid.nr_uks(ks.with_df,
                                       ks.xc,
                                       dm,
                                       hermi,
                                       kpts,
                                       kpts_band,
                                       with_j=True,
                                       return_j=False)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)
        return vxc

    # ndim = 4 : dm.shape = ([alpha,beta], nkpts, nao, nao)
    ground_state = (dm.ndim == 4 and dm.shape[0] == 2 and kpts_band is None)

    if ks.grids.non0tab is None:
        ks.grids.build(with_non0tab=True)
        if (isinstance(ks.grids, gen_grid.BeckeGrids)
                and ks.small_rho_cutoff > 1e-20 and ground_state):
            ks.grids = rks.prune_small_rho_grids_(ks, cell, dm, ks.grids, kpts)
        t0 = logger.timer(ks, 'setting up grids', *t0)

    if hermi == 2:  # because rho = 0
        n, exc, vxc = (0, 0), 0, 0
    else:
        n, exc, vxc = ks._numint.nr_uks(cell, ks.grids, ks.xc, dm, 0, kpts,
                                        kpts_band)
        logger.debug(ks, 'nelec by numeric integration = %s', n)
        t0 = logger.timer(ks, 'vxc', *t0)

    weight = 1. / len(kpts)

    if not hybrid:
        vj = ks.get_j(cell, dm[0] + dm[1], hermi, kpts, kpts_band)
        vxc += vj
    else:
        if getattr(ks.with_df, '_j_only', False):  # for GDF and MDF
            ks.with_df._j_only = False
        vj, vk = ks.get_jk(cell, dm, hermi, kpts, kpts_band)
        vj = vj[0] + vj[1]
        vxc += vj - vk * hyb

        if ground_state:
            exc -= (np.einsum('Kij,Kji', dm[0], vk[0]) + np.einsum(
                'Kij,Kji', dm[1], vk[1])).real * hyb * .5 * weight

    if ground_state:
        ecoul = np.einsum('Kij,Kji', dm[0] + dm[1], vj).real * .5 * weight
    else:
        ecoul = None

    vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None)
    return vxc
Exemplo n.º 57
0
def convert_to_uhf(mf, out=None, remove_df=False):
    '''Convert the given mean-field object to the unrestricted HF/KS object

    Note this conversion only changes the class of the mean-field object.
    The total energy and wave-function are the same as them in the input
    mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer
    will be discarded. Its underlying SCF object mf._scf will be converted.

    Args:
        mf : SCF object

    Kwargs
        remove_df : bool
            Whether to convert the DF-SCF object to the normal SCF object.
            This conversion is not applied by default.

    Returns:
        An unrestricted SCF object
    '''
    from pyscf import scf
    from pyscf import dft
    from pyscf.soscf import newton_ah
    assert (isinstance(mf, hf.SCF))

    logger.debug(mf, 'Converting %s to UHF', mf.__class__)

    def update_mo_(mf, mf1):
        if mf.mo_energy is not None:
            if isinstance(mf, scf.uhf.UHF):
                mf1.mo_occ = mf.mo_occ
                mf1.mo_coeff = mf.mo_coeff
                mf1.mo_energy = mf.mo_energy
            elif getattr(mf, 'kpts', None) is None:  # UHF
                mf1.mo_occ = numpy.array((mf.mo_occ > 0, mf.mo_occ == 2),
                                         dtype=numpy.double)
                mf1.mo_energy = (mf.mo_energy, mf.mo_energy)
                mf1.mo_coeff = (mf.mo_coeff, mf.mo_coeff)
            else:  # This to handle KRHF object
                mf1.mo_occ = ([
                    numpy.asarray(occ > 0, dtype=numpy.double)
                    for occ in mf.mo_occ
                ], [
                    numpy.asarray(occ == 2, dtype=numpy.double)
                    for occ in mf.mo_occ
                ])
                mf1.mo_energy = (mf.mo_energy, mf.mo_energy)
                mf1.mo_coeff = (mf.mo_coeff, mf.mo_coeff)
        return mf1

    if isinstance(mf, scf.ghf.GHF):
        raise NotImplementedError

    elif out is not None:
        assert (isinstance(out, scf.uhf.UHF))
        out = _update_mf_without_soscf(mf, out, remove_df)

    elif isinstance(mf, scf.uhf.UHF):
        # Remove with_df for SOSCF method because the post-HF code checks the
        # attribute .with_df to identify whether an SCF object is DF-SCF method.
        # with_df in SOSCF is used in orbital hessian approximation only.  For the
        # returned SCF object, whehter with_df exists in SOSCF has no effects on the
        # mean-field energy and other properties.
        if getattr(mf, '_scf', None):
            return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df)
        else:
            return copy.copy(mf)

    else:
        known_cls = {
            scf.hf.RHF: scf.uhf.UHF,
            scf.rohf.ROHF: scf.uhf.UHF,
            scf.hf_symm.RHF: scf.uhf_symm.UHF,
            scf.hf_symm.ROHF: scf.uhf_symm.UHF,
            dft.rks.RKS: dft.uks.UKS,
            dft.roks.ROKS: dft.uks.UKS,
            dft.rks_symm.RKS: dft.uks_symm.UKS,
            dft.rks_symm.ROKS: dft.uks_symm.UKS
        }
        out = _object_without_soscf(mf, known_cls, remove_df)

    return update_mo_(mf, out)
Exemplo n.º 58
0
def convert_to_rhf(mf, out=None, remove_df=False):
    '''Convert the given mean-field object to the restricted HF/KS object

    Note this conversion only changes the class of the mean-field object.
    The total energy and wave-function are the same as them in the input
    mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer
    will be discarded. Its underlying SCF object mf._scf will be converted.

    Args:
        mf : SCF object

    Kwargs
        remove_df : bool
            Whether to convert the DF-SCF object to the normal SCF object.
            This conversion is not applied by default.

    Returns:
        An unrestricted SCF object
    '''
    from pyscf import scf
    from pyscf import dft
    from pyscf.soscf import newton_ah
    assert (isinstance(mf, hf.SCF))

    logger.debug(mf, 'Converting %s to RHF', mf.__class__)

    def update_mo_(mf, mf1):
        if mf.mo_energy is not None:
            if isinstance(mf, scf.hf.RHF):  # RHF/ROHF/KRHF/KROHF
                mf1.mo_occ = mf.mo_occ
                mf1.mo_coeff = mf.mo_coeff
                mf1.mo_energy = mf.mo_energy
            elif getattr(mf, 'kpts', None) is None:  # UHF
                mf1.mo_occ = mf.mo_occ[0] + mf.mo_occ[1]
                mf1.mo_energy = mf.mo_energy[0]
                mf1.mo_coeff = mf.mo_coeff[0]
                if getattr(mf.mo_coeff[0], 'orbsym', None) is not None:
                    mf1.mo_coeff = lib.tag_array(mf1.mo_coeff,
                                                 orbsym=mf.mo_coeff[0].orbsym)
            else:  # KUHF
                mf1.mo_occ = [occa + occb for occa, occb in zip(*mf.mo_occ)]
                mf1.mo_energy = mf.mo_energy[0]
                mf1.mo_coeff = mf.mo_coeff[0]
        return mf1

    if getattr(mf, 'nelec', None) is None:
        nelec = mf.mol.nelec
    else:
        nelec = mf.nelec

    if isinstance(mf, scf.ghf.GHF):
        raise NotImplementedError

    elif out is not None:
        assert (isinstance(out, scf.hf.RHF))
        out = _update_mf_without_soscf(mf, out, remove_df)

    elif (isinstance(mf, scf.hf.RHF)
          or (nelec[0] != nelec[1] and isinstance(mf, scf.rohf.ROHF))):
        if getattr(mf, '_scf', None):
            return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df)
        else:
            return copy.copy(mf)

    else:
        if nelec[0] == nelec[1]:
            known_cls = {
                scf.uhf.UHF: scf.hf.RHF,
                scf.uhf_symm.UHF: scf.hf_symm.RHF,
                dft.uks.UKS: dft.rks.RKS,
                dft.uks_symm.UKS: dft.rks_symm.RKS,
                scf.rohf.ROHF: scf.hf.RHF,
                scf.hf_symm.ROHF: scf.hf_symm.RHF,
                dft.roks.ROKS: dft.rks.RKS,
                dft.rks_symm.ROKS: dft.rks_symm.RKS
            }
        else:
            known_cls = {
                scf.uhf.UHF: scf.rohf.ROHF,
                scf.uhf_symm.UHF: scf.hf_symm.ROHF,
                dft.uks.UKS: dft.roks.ROKS,
                dft.uks_symm.UKS: dft.rks_symm.ROKS
            }
        out = _object_without_soscf(mf, known_cls, remove_df)

    return update_mo_(mf, out)
Exemplo n.º 59
0
def DMRG_COMPRESS_NEVPT(mc, maxM=500, root=0, nevptsolver=None, tol=1e-7,
                        nevpt_integral=None):

    if isinstance(nevpt_integral, str) and h5py.is_hdf5(nevpt_integral):
        nevpt_integral_file = os.path.abspath(nevpt_integral)
        fh5 = h5py.File(nevpt_integral_file, 'r')
        nelecas = fh5['mc/nelecas'][()]
        nroots = fh5['mc/nroots'][()]
        wfnsym = fh5['mc/wfnsym'][()]
        fh5.close()
    else :
        nelecas = mc.nelecas
        nroots = mc.fcisolver.nroots
        wfnsym = mc.fcisolver.wfnsym
        nevpt_integral_file = None

    if nevptsolver is None:
        nevptsolver = default_nevpt_schedule(mc.fcisolver, maxM, tol)
        #nevptsolver.__dict__.update(mc.fcisolver.__dict__)
        nevptsolver.wfnsym = wfnsym
        nevptsolver.block_extra_keyword = mc.fcisolver.block_extra_keyword
    nevptsolver.nroots = nroots
    nevptsolver.executable = settings.BLOCKEXE_COMPRESS_NEVPT
    if nevptsolver.executable == getattr(mc.fcisolver, 'executable', None):
        logger.warn(mc, 'DMRG executable file for nevptsolver is the same '
                    'to the executable file for DMRG solver. If they are '
                    'both compiled by MPI compilers, they may cause error or '
                    'random results in DMRG-NEVPT calculation.')

    nevpt_scratch = os.path.abspath(nevptsolver.scratchDirectory)
    dmrg_scratch = os.path.abspath(mc.fcisolver.scratchDirectory)

    # Integrals are not given by the kwarg nevpt_integral
    if nevpt_integral_file is None:
        nevpt_integral_file = os.path.join(nevpt_scratch, 'nevpt_perturb_integral')
        write_chk(mc, root, nevpt_integral_file)

    conf = dmrgci.writeDMRGConfFile(nevptsolver, nelecas, False, with_2pdm=False,
                                    extraline=['fullrestart','nevpt_state_num %d'%root])
    with open(conf, 'r') as f:
        block_conf = f.readlines()
        block_conf = [l for l in block_conf if 'prefix' not in l]
        block_conf = ''.join(block_conf)

    with h5py.File(nevpt_integral_file, 'a') as fh5:
        if 'dmrg.conf' in fh5:
            del(fh5['dmrg.conf'])
        fh5['dmrg.conf'] = block_conf

    if nevptsolver.verbose >= logger.DEBUG1:
        logger.debug1(nevptsolver, 'Block Input conf')
        logger.debug1(nevptsolver, block_conf)

    t0 = (time.clock(), time.time())

    # function nevpt_integral_mpi is called in this cmd
    cmd = ' '.join((nevptsolver.mpiprefix,
                    os.path.realpath(os.path.join(__file__, '..', 'nevpt_mpi.py')),
                    nevpt_integral_file,
                    nevptsolver.executable,
                    dmrg_scratch, nevpt_scratch))
    logger.debug(nevptsolver, 'DMRG_COMPRESS_NEVPT cmd %s', cmd)

    try:
        subprocess.check_call(cmd, shell=True)
    except subprocess.CalledProcessError as err:
        logger.error(nevptsolver, cmd)
        raise err

    if nevptsolver.verbose >= logger.DEBUG1:
        logger.debug1(nevptsolver, open(os.path.join(nevpt_scratch, 'nevpt2_0', 'dmrg.out')).read())

    perturb_file = os.path.join(nevpt_scratch, 'Perturbation_%d'%root)
    fh5 = h5py.File(perturb_file, 'r')
    Vi_e  =  fh5['Vi/energy'][()]
    Vr_e  =  fh5['Vr/energy'][()]
    fh5.close()
    logger.note(nevptsolver,'Nevpt Energy:')
    logger.note(nevptsolver,'Sr Subspace:  E = %.14f'%( Vr_e))
    logger.note(nevptsolver,'Si Subspace:  E = %.14f'%( Vi_e))

    logger.timer(nevptsolver,'MPS NEVPT calculation time', *t0)
    return perturb_file
Exemplo n.º 60
0
    def get_occ(mo_energy_kpts=None, mo_coeff_kpts=None):
        '''Label the occupancies for each orbital for sampled k-points.

        This is a k-point version of scf.hf.SCF.get_occ
        '''
        mo_occ_kpts = mf_class.get_occ(mf, mo_energy_kpts, mo_coeff_kpts)
        if mf.sigma is None or mf.sigma == 0:
            return mo_occ_kpts

        if is_uhf:
            nkpts = len(mo_energy_kpts[0])
            nocc = mf.cell.nelectron * nkpts
        else:
            nkpts = len(mo_energy_kpts)
            nocc = (mf.cell.nelectron * nkpts) // 2
        mo_energy = numpy.sort(mo_energy_kpts.ravel())
        fermi = mo_energy[nocc - 1]

        if method.lower() == 'fermi':  # Fermi-Dirac smearing
            # Optimize mu to give correct electron number
            def fermi_occ(m):
                occ = numpy.zeros_like(mo_energy_kpts)
                de = (mo_energy_kpts - m) / mf.sigma
                occ[de < 40] = 1. / (numpy.exp(de[de < 40]) + 1.)
                return occ

            def nelec_cost_fn(m):
                mo_occ_kpts = fermi_occ(m)
                if not is_uhf:
                    mo_occ_kpts *= 2
                return (mo_occ_kpts.sum() / nkpts - mf.cell.nelectron)**2

            res = scipy.optimize.minimize(nelec_cost_fn,
                                          fermi,
                                          method='Powell')
            mu = res.x
            mo_occ_kpts = f = fermi_occ(mu)
            f = f[(f > 0) & (f < 1)]
            mf.entropy = -(f * numpy.log(f) + (1 - f) * numpy.log(1 - f)).sum()
            if not is_uhf:
                mo_occ_kpts *= 2
                mf.entropy *= 2

        else:  # Gaussian smearing

            def nelec_cost_fn(m):
                mo_occ_kpts = 1 - scipy.special.erf(
                    (mo_energy_kpts - m) / mf.sigma)
                if is_uhf:
                    mo_occ_kpts *= .5
                return (mo_occ_kpts.sum() / nkpts - mf.cell.nelectron)**2

            res = scipy.optimize.minimize(nelec_cost_fn,
                                          fermi,
                                          method='Powell')
            mu = res.x
            mo_occ_kpts = 1 - scipy.special.erf(
                (mo_energy_kpts - mu) / mf.sigma)
            if is_uhf:
                mo_occ_kpts *= .5


# Is the entropy correct for spin unrestricted case?
            mf.entropy = numpy.exp(-(
                (mo_energy_kpts - mu) / mf.sigma)**2).sum() / numpy.sqrt(
                    numpy.pi)

        logger.debug(mf, '    Sum mo_occ_kpts = %s  should equal nelec = %s',
                     mo_occ_kpts.sum() / nkpts, mf.cell.nelectron)
        logger.info(mf,
                    '    sigma = %g  Optimized mu = %.12g  entropy = %.12g',
                    mf.sigma, mu, mf.entropy)

        return mo_occ_kpts