Exemple #1
0
def kernel(mf,
           conv_tol=1e-10,
           conv_tol_grad=None,
           dump_chk=True,
           dm0=None,
           callback=None,
           **kwargs):
    '''kernel: the SCF driver.

    Args:
        mf : an instance of SCF class
            To hold the flags to control SCF.  Besides the control parameters,
            one can modify its function members to change the behavior of SCF.
            The member functions which are called in kernel are

            | mf.get_init_guess
            | mf.get_hcore
            | mf.get_ovlp
            | mf.get_fock
            | mf.get_grad
            | mf.eig
            | mf.get_occ
            | mf.make_rdm1
            | mf.energy_tot
            | mf.dump_chk

    Kwargs:
        conv_tol : float
            converge threshold.
        conv_tol_grad : float
            gradients converge threshold.
        dump_chk : bool
            Whether to save SCF intermediate results in the checkpoint file
        dm0 : ndarray
            Initial guess density matrix.  If not given (the default), the kernel
            takes the density matrix generated by ``mf.get_init_guess``.
        callback : function(envs_dict) => None
            callback function takes one dict as the argument which is
            generated by the builtin function :func:`locals`, so that the
            callback function can access all local variables in the current
            envrionment.

    Returns:
        A list :   scf_conv, e_tot, mo_energy, mo_coeff, mo_occ

        scf_conv : bool
            True means SCF converged
        e_tot : float
            Hartree-Fock energy of last iteration
        mo_energy : 1D float array
            Orbital energies.  Depending the eig function provided by mf
            object, the orbital energies may NOT be sorted.
        mo_coeff : 2D array
            Orbital coefficients.
        mo_occ : 1D array
            Orbital occupancies.  The occupancies may NOT be sorted from large
            to small.

    Examples:

    >>> from pyscf import gto, scf
    >>> mol = gto.M(atom='H 0 0 0; H 0 0 1.1', basis='cc-pvdz')
    >>> conv, e, mo_e, mo, mo_occ = scf.hf.kernel(scf.hf.SCF(mol), dm0=numpy.eye(mol.nao_nr()))
    >>> print('conv = %s, E(HF) = %.12f' % (conv, e))
    conv = True, E(HF) = -1.081170784378
    '''
    if 'init_dm' in kwargs:
        raise RuntimeError('''
You see this error message because of the API updates in pyscf v0.11.
Keyword argument "init_dm" is replaced by "dm0"''')
    cput0 = (time.clock(), time.time())
    if conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(conv_tol)
        logger.info(mf, 'Set gradient conv threshold to %g', conv_tol_grad)

    mol = mf.mol
    if dm0 is None:
        dm = mf.get_init_guess(mol, mf.init_guess)
    else:
        dm = dm0

    h1e = mf.get_hcore(mol)
    s1e = mf.get_ovlp(mol)

    cond = lib.cond(s1e)
    logger.debug(mf, 'cond(S) = %s', cond)
    if numpy.max(cond) * 1e-17 > conv_tol:
        logger.warn(
            mf,
            'Singularity detected in overlap matrix (condition number = %4.3g). '
            'SCF may be inaccurate and hard to converge.', numpy.max(cond))

    if isinstance(mf.diis, lib.diis.DIIS):
        mf_diis = mf.diis
    elif mf.diis:
        mf_diis = diis.SCF_DIIS(mf, mf.diis_file)
        mf_diis.space = mf.diis_space
        mf_diis.rollback = mf.diis_space_rollback
    else:
        mf_diis = None

    vhf = mf.get_veff(mol, dm)
    e_tot = mf.energy_tot(dm, h1e, vhf)
    logger.info(mf, 'init E= %.15g', e_tot)

    if dump_chk:
        # Explicit overwrite the mol object in chkfile
        # Note in pbc.scf, mf.mol == mf.cell, cell is saved under key "mol"
        chkfile.save_mol(mol, mf.chkfile)

    scf_conv = False
    cycle = 0
    cput1 = logger.timer(mf, 'initialize scf', *cput0)
    while not scf_conv and cycle < max(1, mf.max_cycle):
        dm_last = dm
        last_hf_e = e_tot

        fock = mf.get_fock(h1e, s1e, vhf, dm, cycle, mf_diis)
        mo_energy, mo_coeff = mf.eig(fock, s1e)
        mo_occ = mf.get_occ(mo_energy, mo_coeff)
        dm = mf.make_rdm1(mo_coeff, mo_occ)
        dm = _attach_mo(dm, mo_coeff,
                        mo_occ)  # to improve DFT get_veff efficiency
        vhf = mf.get_veff(mol, dm, dm_last, vhf)
        e_tot = mf.energy_tot(dm, h1e, vhf)

        norm_gorb = numpy.linalg.norm(mf.get_grad(mo_coeff, mo_occ, h1e + vhf))
        norm_ddm = numpy.linalg.norm(dm - dm_last)
        logger.info(
            mf, 'cycle= %d E= %.15g  delta_E= %4.3g  |g|= %4.3g  |ddm|= %4.3g',
            cycle + 1, e_tot, e_tot - last_hf_e, norm_gorb, norm_ddm)

        if (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())

        cput1 = logger.timer(mf, 'cycle= %d' % (cycle + 1), *cput1)
        cycle += 1

    # An extra diagonalization, to remove level shift
    fock = mf.get_fock(h1e, s1e, vhf, dm, cycle, None, 0, 0, 0)
    norm_gorb = numpy.linalg.norm(mf.get_grad(mo_coeff, mo_occ, h1e + vhf))
    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
    dm = _attach_mo(dm, mo_coeff, mo_occ)  # to improve DFT get_veff efficiency
    vhf = mf.get_veff(mol, dm, dm_last, vhf)
    e_tot, last_hf_e = mf.energy_tot(dm, h1e, vhf), e_tot
    norm_ddm = numpy.linalg.norm(dm - dm_last)
    logger.info(
        mf, 'Extra cycle  E= %.15g  delta_E= %4.3g  |g|= %4.3g  |ddm|= %4.3g',
        e_tot, e_tot - last_hf_e, norm_gorb, norm_ddm)
    if dump_chk:
        mf.dump_chk(locals())
    logger.timer(mf, 'scf_cycle', *cput0)
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Exemple #2
0
    def initfockbuild(self):
        """
        Using Roothan's equation to build a Initial Fock matrix and
        Transformation Matrices

        Returns:
            fmat: float or complex
                Fock matrix in Lowdin AO basis
            c_am: float
                Transformation Matrix |AO><MO|
            v_lm: float
                Transformation Matrix |LAO><MO|
        """
        start = time.time()
        n_occ = int(sum(self.ks.mo_occ) / 2)
        err = 100
        it = 0
        self.h = self.ks.get_hcore()
        s = self.s.copy()
        x = self.x.copy()
        sx = np.dot(s, x)
        dm_lao = 0.5*transmat(self.ks.get_init_guess(self.ks.mol, \
        self.ks.init_guess), sx).astype(complex)

        if isinstance(self.ks.diis, lib.diis.DIIS):
            self.adiis = self.ks.diis
        elif self.ks.diis:
            self.adiis = diis.SCF_DIIS(self.ks, self.ks.diis_file)
            self.adiis.space = self.ks.diis_space
            self.adiis.rollback = self.ks.diis_space_rollback
        else:
            self.adiis = None

        fmat, jmat, kmat = self.fockbuild(dm_lao)
        etot = self.energy(dm_lao, fmat, jmat, kmat) + self.enuc

        while (err > self.conv_tol):
            # Diagonalize F in the lowdin basis
            eigs, v_lm = np.linalg.eig(fmat)
            idx = eigs.argsort()
            eigs.sort()
            v_lm = v_lm[:, idx].copy()
            # Fill up the density in the MO basis and then Transform back
            rho = 0.5 * np.diag(self.ks.mo_occ).astype(complex)
            dm_lao = transmat(rho, v_lm, -1)
            etot_old = etot
            etot = self.energy(dm_lao, fmat, jmat, kmat)
            fmat, jmat, kmat = self.fockbuild(dm_lao, it)
            err = abs(etot - etot_old)
            logger.debug(self, "Ne: %f", np.trace(rho))
            logger.debug(
                self, "Iteration: %d         Energy: %.11f      \
            Error = %.11f", it, etot, err)
            it += 1
            if it > self.ks.max_cycle:
                logger.log(
                    self,
                    "Max cycle of SCF reached: %d\n Exiting TDSCF. Please raise ks.max_cycle",
                    it)
                quit()
        rho = 0.5 * np.diag(self.ks.mo_occ).astype(complex)
        dm_lao = transmat(rho, v_lm, -1)
        c_am = np.dot(self.x, v_lm)
        logger.log(self, "Ne: %f", np.trace(rho))
        logger.log(self, "Converged Energy: %f", etot)
        # logger.log(self, "Eigenvalues: %f", eigs.real)
        # print "Eigenvalues: ", eigs.real
        end = time.time()
        logger.info(self, "Initial Fock Built time: %f", end - start)
        return fmat, c_am, v_lm