Ejemplo n.º 1
0
def get_ovlp(mf, cell=None, kpts=None):
    '''Get the overlap AO matrices at sampled k-points.

    Args:
        kpts : (nkpts, 3) ndarray

    Returns:
        ovlp_kpts : (nkpts, nao, nao) ndarray
    '''
    if cell is None: cell = mf.cell
    if kpts is None: kpts = mf.kpts
    # Avoid pbcopt's prescreening in the lattice sum, for better accuracy
    s = cell.pbc_intor('int1e_ovlp',
                       hermi=1,
                       kpts=kpts,
                       pbcopt=lib.c_null_ptr())
    cond = np.max(lib.cond(s))
    if cond * cell.precision > 1e2:
        prec = 1e2 / cond
        rmin = max([cell.bas_rcut(ib, prec) for ib in range(cell.nbas)])
        if cell.rcut < rmin:
            logger.warn(
                cell, 'Singularity detected in overlap matrix.  '
                'Integral accuracy may be not enough.\n      '
                'You can adjust  cell.precision  or  cell.rcut  to '
                'improve accuracy.  Recommended values are\n      '
                'cell.precision = %.2g  or smaller.\n      '
                'cell.rcut = %.4g  or larger.', prec, rmin)
    return lib.asarray(s)
Ejemplo n.º 2
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
Ejemplo n.º 3
0
def get_ovlp(cell, kpt=np.zeros(3)):
    '''Get the overlap AO matrix.
    '''
    # Avoid pbcopt's prescreening in the lattice sum, for better accuracy
    s = cell.pbc_intor('int1e_ovlp',
                       hermi=0,
                       kpts=kpt,
                       pbcopt=lib.c_null_ptr())
    s = lib.asarray(s)
    hermi_error = abs(s - np.rollaxis(s.conj(), -1, -2)).max()
    if hermi_error > cell.precision and hermi_error > 1e-12:
        logger.warn(
            cell, '%.4g error found in overlap integrals. '
            'cell.precision  or  cell.rcut  can be adjusted to '
            'improve accuracy.')

    cond = np.max(lib.cond(s))
    if cond * cell.precision > 1e2:
        prec = 1e2 / cond
        rmin = max([cell.bas_rcut(ib, prec) for ib in range(cell.nbas)])
        if cell.rcut < rmin:
            logger.warn(
                cell, 'Singularity detected in overlap matrix.  '
                'Integral accuracy may be not enough.\n      '
                'You can adjust  cell.precision  or  cell.rcut  to '
                'improve accuracy.  Recommended values are\n      '
                'cell.precision = %.2g  or smaller.\n      '
                'cell.rcut = %.4g  or larger.', prec, rmin)
    return s
Ejemplo n.º 4
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
Ejemplo n.º 5
0
def remove_linear_dep_(mf,
                       threshold=LINEAR_DEP_THRESHOLD,
                       lindep=LINEAR_DEP_TRIGGER,
                       cholesky_threshold=CHOLESKY_THRESHOLD,
                       force_pivoted_cholesky=FORCE_PIVOTED_CHOLESKY):
    '''
    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 and not force_pivoted_cholesky:
        return mf

    logger.info(mf, 'Applying remove_linear_dep_ on SCF object.')
    logger.debug(mf, 'Overlap condition number %g', cond)
    if (cond < 1. / numpy.finfo(s.dtype).eps and not force_pivoted_cholesky):
        logger.info(
            mf, 'Using canonical orthogonalization with threshold {}'.format(
                threshold))

        def eigh(h, s):
            x = canonical_orth_(s, 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
    else:
        logger.info(
            mf, 'Using partial Cholesky orthogonalization '
            '(doi:10.1063/1.5139948, doi:10.1103/PhysRevA.101.032504)')
        logger.info(
            mf, 'Using threshold {} for pivoted Cholesky'.format(
                cholesky_threshold))
        logger.info(
            mf, 'Using threshold {} to orthogonalize the subbasis'.format(
                threshold))

        def eigh(h, s):
            x = partial_cholesky_orth_(s,
                                       canthr=threshold,
                                       cholthr=cholesky_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
Ejemplo n.º 6
0
def get_ovlp(cell, kpt=np.zeros(3)):
    '''Get the overlap AO matrix.
    '''
    s = cell.pbc_intor('int1e_ovlp_sph', hermi=1, kpts=kpt)
    cond = np.max(lib.cond(s))
    if cond * cell.precision > 1e2:
        prec = 1e2 / cond
        rmin = max([cell.bas_rcut(ib, prec) for ib in range(cell.nbas)])
        if cell.rcut < rmin:
            logger.warn(
                cell, 'Singularity detected in overlap matrix.  '
                'Integral accuracy may be not enough.\n      '
                'You can adjust  cell.precision  or  cell.rcut  to '
                'improve accuracy.  Recommended values are\n      '
                'cell.precision = %.2g  or smaller.\n      '
                'cell.rcut = %.4g  or larger.', prec, rmin)
    return s
Ejemplo n.º 7
0
Archivo: hf.py Proyecto: sunqm/pyscf
def get_ovlp(cell, kpt=np.zeros(3)):
    '''Get the overlap AO matrix.
    '''
# Avoid pbcopt's prescreening in the lattice sum, for better accuracy
    s = cell.pbc_intor('int1e_ovlp', hermi=1, kpts=kpt,
                       pbcopt=lib.c_null_ptr())
    cond = np.max(lib.cond(s))
    if cond * cell.precision > 1e2:
        prec = 1e2 / cond
        rmin = max([cell.bas_rcut(ib, prec) for ib in range(cell.nbas)])
        if cell.rcut < rmin:
            logger.warn(cell, 'Singularity detected in overlap matrix.  '
                        'Integral accuracy may be not enough.\n      '
                        'You can adjust  cell.precision  or  cell.rcut  to '
                        'improve accuracy.  Recommended values are\n      '
                        'cell.precision = %.2g  or smaller.\n      '
                        'cell.rcut = %.4g  or larger.', prec, rmin)
    return s
Ejemplo n.º 8
0
def kernel(mf,
           conv_tol=1e-10,
           conv_tol_grad=None,
           dump_chk=False,
           dm0e=None,
           dm0n=[],
           callback=None,
           conv_check=True,
           **kwargs):
    cput0 = (logger.process_clock(), logger.perf_counter())
    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 dm0e is None:
        mf.dm_elec = mf.get_init_guess_elec(mol, mf.init_guess)
    else:
        mf.dm_elec = dm0e
    if len(dm0n) < mol.nuc_num:
        mf.dm_nuc = mf.get_init_guess_nuc(mol, mf.init_guess)
        # if mf.init_guess is not 'chkfile', then it only affects the electronic part
    else:
        mf.dm_nuc = dm0n

    h1e = mf.mf_elec.get_hcore(mol.elec)
    vhf_e = mf.mf_elec.get_veff(mol.elec, mf.dm_elec)
    h1n = []
    veff_n = []
    for i in range(mol.nuc_num):
        h1n.append(mf.mf_nuc[i].get_hcore(mol.nuc[i]))
        veff_n.append(mf.mf_nuc[i].get_veff(mol.nuc[i], mf.dm_nuc[i]))
    e_tot = mf.energy_tot(mf.dm_elec, mf.dm_nuc, h1e, vhf_e, h1n, veff_n)
    logger.info(mf, 'init E= %.15g', e_tot)

    scf_conv = False
    mo_energy_e = mo_coeff_e = mo_occ_e = None
    mo_energy_n = [None] * mol.nuc_num
    mo_coeff_n = [None] * mol.nuc_num
    mo_occ_n = [None] * mol.nuc_num
    fock_n = [None] * mol.nuc_num

    s1e = mf.mf_elec.get_ovlp(mol.elec)
    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))
    s1n = []
    for i in range(mol.nuc_num):
        s1n.append(mf.mf_nuc[i].get_ovlp(mol.nuc[i]))

    # Skip SCF iterations. Compute only the total energy of the initial density
    if mf.max_cycle <= 0:
        fock_e = mf.mf_elec.get_fock(h1e, s1e, vhf_e,
                                     mf.dm_elec)  # = h1e + vhf, no DIIS
        mo_energy_e, mo_coeff_e = mf.mf_elec.eig(fock_e, s1e)
        mo_occ_e = mf.mf_elec.get_occ(mo_energy_e, mo_coeff_e)
        mf.mf_elec.mo_energy = mo_energy_e
        mf.mf_elec.mo_coeff = mo_coeff_e
        mf.mf_elec.mo_occ = mo_occ_e
        for i in range(mol.nuc_num):
            fock_n[i] = mf.mf_nuc[i].get_fock(h1n[i], s1n[i], veff_n[i],
                                              mf.dm_nuc[i])
            mo_energy_n[i], mo_coeff_n[i] = mf.mf_nuc[i].eig(fock_n[i], s1n[i])
            mo_occ_n[i] = mf.mf_nuc[i].get_occ(mo_energy_n[i], mo_coeff_e[i])
            mf.mf_nuc[i].mo_energy = mo_energy_n[i]
            mf.mf_nuc[i].mo_coeff = mo_coeff_n[i]
            mf.mf_nuc[i].mo_occ = mo_occ_n[i]
        if mf.dm_elec.ndim > 2:
            mf.dm_elec = mf.dm_elec[0] + mf.dm_elec[1]
        return scf_conv, e_tot, mo_energy_e, mo_coeff_e, mo_occ_e, \
               mo_energy_n, mo_coeff_n, mo_occ_n

    if isinstance(mf.mf_elec.diis, lib.diis.DIIS):
        mf_diis = mf.mf_elec.diis
    elif mf.mf_elec.diis:
        assert issubclass(mf.mf_elec.DIIS, lib.diis.DIIS)
        mf_diis = mf.mf_elec.DIIS(mf.mf_elec, mf.mf_elec.diis_file)
        mf_diis.space = mf.mf_elec.diis_space
        mf_diis.rollback = mf.mf_elec.diis_space_rollback
    else:
        mf_diis = None
    # Nuclei need DIIS when there is epc
    mf_nuc_diis = [None] * mol.nuc_num
    if hasattr(mf, 'epc') and mf.epc is not None:
        for i in range(mol.nuc_num):
            mf_nuc = mf.mf_nuc[i]
            if isinstance(mf_nuc.diis, lib.diis.DIIS):
                mf_nuc_diis[i] = mf_nuc.diis
            elif mf_nuc.diis:
                assert issubclass(mf_nuc.DIIS, lib.diis.DIIS)
                mf_nuc_diis[i] = mf_nuc.DIIS(mf_nuc, mf_nuc.diis_file)
                mf_nuc_diis[i].space = mf_nuc.diis_space
                mf_nuc_diis[i].rollback = mf_nuc.diis_space_rollback
            else:
                mf_nuc_diis[i] = None

    if dump_chk and mf.chkfile:
        # 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)

    # A preprocessing hook before the SCF iteration
    mf.pre_kernel(locals())

    if isinstance(mf, neo.CDFT):
        int1e_r = []
        for i in range(mol.nuc_num):
            int1e_r.append(mf.mf_nuc[i].mol.intor_symmetric('int1e_r', comp=3))

    cput1 = logger.timer(mf, 'initialize scf', *cput0)
    for cycle in range(mf.max_cycle):
        dm_elec_last = numpy.copy(
            mf.dm_elec)  # why didn't pyscf.scf.hf use copy?
        dm_nuc_last = numpy.copy(mf.dm_nuc)
        last_e = e_tot

        # set up the electronic Hamiltonian and diagonalize it
        fock_e = mf.mf_elec.get_fock(h1e, s1e, vhf_e, mf.dm_elec, cycle,
                                     mf_diis)
        mo_energy_e, mo_coeff_e = mf.mf_elec.eig(fock_e, s1e)
        mo_occ_e = mf.mf_elec.get_occ(mo_energy_e, mo_coeff_e)
        mf.dm_elec = mf.mf_elec.make_rdm1(mo_coeff_e, mo_occ_e)
        # attach mo_coeff and mo_occ to dm to improve DFT get_veff efficiency
        mf.dm_elec = lib.tag_array(mf.dm_elec,
                                   mo_coeff=mo_coeff_e,
                                   mo_occ=mo_occ_e)

        # set up the nuclear Hamiltonian and diagonalize it
        for i in range(mol.nuc_num):
            # update nuclear core Hamiltonian after the electron density is updated
            h1n[i] = mf.mf_nuc[i].get_hcore(mf.mf_nuc[i].mol)
            # optimize f in cNEO
            skip = False
            if isinstance(mf, neo.CDFT):
                ia = mf.mf_nuc[i].mol.atom_index
                fx = numpy.einsum('xij,x->ij', int1e_r[i], mf.f[ia])
                opt = scipy.optimize.root(mf.first_order_de,
                                          mf.f[ia],
                                          args=(mf.mf_nuc[i], h1n[i] - fx,
                                                veff_n[i], s1n[i], int1e_r[i]),
                                          method='hybr')
                logger.debug(
                    mf, 'f of %s(%i) atom: %s' %
                    (mf.mf_nuc[i].mol.atom_symbol(ia), ia, mf.f[ia]))
                logger.debug(mf, '1st de of L: %s', opt.fun)
                if mf_nuc_diis[i] is None:
                    # skip the extra diagonalization if epc is not present and DIIS is disabled
                    skip = True
            if not skip:
                fock_n[i] = mf.mf_nuc[i].get_fock(h1n[i], s1n[i], veff_n[i],
                                                  mf.dm_nuc[i], cycle,
                                                  mf_nuc_diis[i])
                mo_energy_n[i], mo_coeff_n[i] = mf.mf_nuc[i].eig(
                    fock_n[i], s1n[i])
                mf.mf_nuc[i].mo_energy, mf.mf_nuc[i].mo_coeff = mo_energy_n[
                    i], mo_coeff_n[i]
                mo_occ_n[i] = mf.mf_nuc[i].get_occ(mo_energy_n[i],
                                                   mo_coeff_n[i])
                mf.mf_nuc[i].mo_occ = mo_occ_n[i]
                mf.dm_nuc[i] = mf.mf_nuc[i].make_rdm1(mo_coeff_n[i],
                                                      mo_occ_n[i])
            # update nuclear veff and possible ep correlation part after the diagonalization
            veff_n[i] = mf.mf_nuc[i].get_veff(mf.mf_nuc[i].mol, mf.dm_nuc[i])
        norm_ddm_n = numpy.linalg.norm(
            numpy.concatenate(mf.dm_nuc, axis=None).ravel() -
            numpy.concatenate(dm_nuc_last, axis=None).ravel())

        # update electronic core Hamiltonian after the nuclear density is updated
        h1e = mf.mf_elec.get_hcore(mol.elec)
        # also update the veff, along with the possible ep correlation part
        vhf_e = mf.mf_elec.get_veff(mol.elec, mf.dm_elec, dm_elec_last, vhf_e)

        # Here Fock matrix is h1e + vhf, without DIIS.  Calling get_fock
        # instead of the statement "fock = h1e + vhf" because Fock matrix may
        # be modified in some methods.
        fock_e = mf.mf_elec.get_fock(h1e, s1e, vhf_e,
                                     mf.dm_elec)  # = h1e + vhf, no DIIS
        norm_gorb_e = numpy.linalg.norm(
            mf.mf_elec.get_grad(mo_coeff_e, mo_occ_e, fock_e))
        if not TIGHT_GRAD_CONV_TOL:
            norm_gorb_e = norm_gorb_e / numpy.sqrt(norm_gorb_e.size)
        norm_ddm_e = numpy.linalg.norm(mf.dm_elec - dm_elec_last)

        e_tot = mf.energy_tot(mf.dm_elec, mf.dm_nuc, h1e, vhf_e, h1n, veff_n)
        logger.info(
            mf,
            'cycle= %d E= %.15g  delta_E= %4.3g  |g_e|= %4.3g  |ddm_e|= %4.3g  |ddm_n|= %4.3g',
            cycle + 1, e_tot, e_tot - last_e, norm_gorb_e, norm_ddm_e,
            norm_ddm_n)

        if abs(e_tot - last_e) < conv_tol and norm_gorb_e < 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)

        if scf_conv:
            break

    if scf_conv and conv_check:
        # An extra diagonalization, to remove level shift
        #fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf
        mo_energy_e, mo_coeff_e = mf.mf_elec.eig(fock_e, s1e)
        mo_occ_e = mf.mf_elec.get_occ(mo_energy_e, mo_coeff_e)
        mf.dm_elec, dm_elec_last = mf.mf_elec.make_rdm1(mo_coeff_e,
                                                        mo_occ_e), mf.dm_elec
        mf.dm_elec = lib.tag_array(mf.dm_elec,
                                   mo_coeff=mo_coeff_e,
                                   mo_occ=mo_occ_e)

        for i in range(mol.nuc_num):
            h1n[i] = mf.mf_nuc[i].get_hcore(mf.mf_nuc[i].mol)
            veff_n[i] = mf.mf_nuc[i].get_veff(mf.mf_nuc[i].mol, mf.dm_nuc[i])
            fock_n[i] = mf.mf_nuc[i].get_fock(h1n[i], s1n[i], veff_n[i],
                                              mf.dm_nuc[i])
            mo_energy_n[i], mo_coeff_n[i] = mf.mf_nuc[i].eig(fock_n[i], s1n[i])
            mf.mf_nuc[i].mo_energy, mf.mf_nuc[i].mo_coeff = mo_energy_n[
                i], mo_coeff_n[i]
            mo_occ_n[i] = mf.mf_nuc[i].get_occ(mo_energy_n[i], mo_coeff_n[i])
            mf.mf_nuc[i].mo_occ = mo_occ_n[i]
            mf.dm_nuc[i], dm_nuc_last[i] = mf.mf_nuc[i].make_rdm1(
                mo_coeff_n[i], mo_occ_n[i]), mf.dm_nuc[i]
        norm_ddm_n = numpy.linalg.norm(
            numpy.concatenate(mf.dm_nuc, axis=None).ravel() -
            numpy.concatenate(dm_nuc_last, axis=None).ravel())

        h1e = mf.mf_elec.get_hcore(mol.elec)
        vhf_e = mf.mf_elec.get_veff(mol.elec, mf.dm_elec, dm_elec_last, vhf_e)
        fock_e = mf.mf_elec.get_fock(h1e, s1e, vhf_e, mf.dm_elec)
        norm_gorb_e = numpy.linalg.norm(
            mf.mf_elec.get_grad(mo_coeff_e, mo_occ_e, fock_e))
        if not TIGHT_GRAD_CONV_TOL:
            norm_gorb_e = norm_gorb_e / numpy.sqrt(norm_gorb_e.size)
        norm_ddm_e = numpy.linalg.norm(mf.dm_elec - dm_elec_last)

        e_tot, last_e = mf.energy_tot(mf.dm_elec, mf.dm_nuc, h1e, vhf_e, h1n,
                                      veff_n), e_tot
        conv_tol = conv_tol * 10
        conv_tol_grad = conv_tol_grad * 3
        if abs(e_tot - last_e) < conv_tol or norm_gorb_e < conv_tol_grad:
            scf_conv = True
        logger.info(
            mf,
            'Extra cycle  E= %.15g  delta_E= %4.3g  |g_e|= %4.3g  |ddm_e|= %4.3g  |ddm_n|= %4.3g',
            e_tot, e_tot - last_e, norm_gorb_e, norm_ddm_e, norm_ddm_n)
        if dump_chk:
            mf.dump_chk(locals())

    logger.timer(mf, 'scf_cycle', *cput0)
    # A post-processing hook before return
    mf.post_kernel(locals())

    if mf.dm_elec.ndim > 2:
        mf.dm_elec = mf.dm_elec[0] + mf.dm_elec[1]
    return scf_conv, e_tot, mo_energy_e, mo_coeff_e, mo_occ_e, \
           mo_energy_n, mo_coeff_n, mo_occ_n
Ejemplo n.º 9
0
Archivo: hf.py Proyecto: berquist/pyscf
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 mf.diis and mf.DIIS:
        adiis = mf.DIIS(mf, mf.diis_file)
        adiis.space = mf.diis_space
        adiis.rollback = mf.diis_space_rollback
    else:
        adiis = 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"
        pyscf.scf.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, adiis)
        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)
        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)
    mo_energy, mo_coeff = mf.eig(fock, s1e)
    mo_occ = mf.get_occ(mo_energy, mo_coeff)
    if dump_chk:
        mf.dump_chk(locals())
    logger.timer(mf, 'scf_cycle', *cput0)
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Ejemplo n.º 10
0
Archivo: hf.py Proyecto: pulkin/pyscf
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