Example #1
0
def kernel(mf, mo_coeff, mo_occ, conv_tol=1e-10, conv_tol_grad=None,
           max_cycle=50, dump_chk=True,
           callback=None, verbose=logger.NOTE):
    cput0 = (time.clock(), time.time())
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mf.stdout, verbose)
    mol = mf.mol
    if conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(conv_tol)
        logger.info(mf, 'Set conv_tol_grad to %g', conv_tol_grad)
    scf_conv = False
    e_tot = mf.e_tot

    h1e = mf.get_hcore(mol)
    s1e = mf.get_ovlp(mol)
    dm = mf.make_rdm1(mo_coeff, mo_occ)
# call mf._scf.get_veff, to avoid density_fit module polluting get_veff function
    vhf = mf._scf.get_veff(mol, dm)
    fock = mf.get_fock(h1e, s1e, vhf, dm, 0, None)
    mo_energy = mf.get_mo_energy(fock, s1e, dm)[0]
    mf.get_occ(mo_energy, mo_coeff)

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

    rotaiter = rotate_orb_cc(mf, mo_coeff, mo_occ, fock, h1e, conv_tol_grad, log)
    u, g_orb, jkcount = rotaiter.next()
    jktot = jkcount
    cput1 = log.timer('initializing second order scf', *cput0)

    for imacro in range(max_cycle):
        dm_last = dm
        last_hf_e = e_tot
        norm_gorb = numpy.linalg.norm(g_orb)
        mo_coeff = mf.update_mo_coeff(mo_coeff, u)
        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, imacro, None)
        mo_energy = mf.get_mo_energy(fock, s1e, dm)[0]
        mf.get_occ(mo_energy, mo_coeff)
# 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 JK',
                 imacro, e_tot, e_tot-last_hf_e, norm_gorb,
                 jkcount)
        cput1 = log.timer('cycle= %d'%(imacro+1), *cput1)

        if (abs((e_tot-last_hf_e)/e_tot)*1e2 < 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

        u, g_orb, jkcount = rotaiter.send((mo_coeff, mo_occ, fock))
        jktot += jkcount

    rotaiter.close()
    mo_energy, mo_coeff = mf.get_mo_energy(fock, s1e, dm)
    mo_occ = mf.get_occ(mo_energy, mo_coeff)
    log.info('macro X = %d  E=%.15g  |g|= %g  total %d JK',
             imacro+1, e_tot, norm_gorb, jktot)
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Example #2
0
def kernel(mf,
           mo_coeff,
           mo_occ,
           conv_tol=1e-10,
           conv_tol_grad=None,
           max_cycle=50,
           dump_chk=True,
           callback=None,
           verbose=logger.NOTE):
    cput0 = (time.clock(), time.time())
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mf.stdout, verbose)
    mol = mf._scf.mol
    if conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(conv_tol)
        log.info('Set conv_tol_grad to %g', conv_tol_grad)
    scf_conv = False
    e_tot = mf.e_tot

    # 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)
    dm = mf.make_rdm1(mo_coeff, mo_occ)
    # call mf._scf.get_veff, to avoid density_fit module polluting get_veff function
    vhf = mf._scf.get_veff(mol, dm)
    fock = mf.get_fock(h1e, s1e, vhf, dm, 0, None)
    # NOTE: DO NOT change the initial guess mo_occ, mo_coeff
    mo_energy, mo_tmp = mf.eig(fock, s1e)
    mf.get_occ(mo_energy, mo_tmp)

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

    rotaiter = rotate_orb_cc(mf, mo_coeff, mo_occ, fock, h1e, conv_tol_grad,
                             log)
    u, g_orb, jkcount = next(rotaiter)
    jktot = jkcount
    cput1 = log.timer('initializing second order scf', *cput0)

    for imacro in range(max_cycle):
        dm_last = dm
        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, imacro, None)
        # 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 JK', imacro,
                 e_tot, e_tot - last_hf_e, norm_gorb, jkcount)
        cput1 = log.timer('cycle= %d' % (imacro + 1), *cput1)

        if (abs((e_tot - last_hf_e) / e_tot) * 1e2 < 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

        u, g_orb, jkcount = rotaiter.send((mo_coeff, mo_occ, fock))
        jktot += jkcount

    rotaiter.close()
    if mf.canonicalization:
        log.info('Canonicalize SCF orbitals')
        mo_energy, mo_coeff = mf._scf.canonicalize(mo_coeff, mo_occ, fock)
    else:
        mo_energy = mf._scf.canonicalize(mo_coeff, mo_occ, fock)[0]
    log.info('macro X = %d  E=%.15g  |g|= %g  total %d JK', imacro + 1, e_tot,
             norm_gorb, jktot)
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Example #3
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) /
                 e_tot) * 1e3 < 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
Example #4
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
Example #5
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 = numpy.linalg.cond(s1e)
    if 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.', 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:
        # dump mol after reading initialized DM
        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)
    logger.timer(mf, 'scf_cycle', *cput0)
    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
Example #6
0
def kernel(mf,
           mo_coeff,
           mo_occ,
           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)
    scf_conv = False
    e_tot = mf.e_tot

    # 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)
    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)
    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)))
    # NOTE: DO NOT change the initial guess mo_occ, mo_coeff
    mo_energy, mo_tmp = mf.eig(fock, s1e)
    mf.get_occ(mo_energy, mo_tmp)

    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 == mf.mol and not getattr(mf, 'with_df', None):
        mf._eri = mf._scf._eri

    rotaiter = rotate_orb_cc(mf, mo_coeff, mo_occ, fock, h1e, conv_tol_grad,
                             log)
    u, g_orb, kfcount, jkcount = next(rotaiter)
    kftot = kfcount + 1
    jktot = jkcount
    cput1 = log.timer('initializing second order scf', *cput0)

    for imacro in range(max_cycle):
        dm_last = dm
        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 (abs((e_tot - last_hf_e) / e_tot) * 1e2 < 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

        u, g_orb, kfcount, jkcount = rotaiter.send((mo_coeff, mo_occ, fock))
        kftot += kfcount + 1
        jktot += jkcount

    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, jktot)
    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
Example #7
0
File: hf.py Project: 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
Example #8
0
def kernel(mf, mo_coeff, mo_occ, 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)
    scf_conv = False
    e_tot = mf.e_tot

# 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)
    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)
    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)))
# NOTE: DO NOT change the initial guess mo_occ, mo_coeff
    mo_energy, mo_tmp = mf.eig(fock, s1e)
    mf.get_occ(mo_energy, mo_tmp)

    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 == mf.mol and not getattr(mf, 'with_df', None):
        mf._eri = mf._scf._eri

    rotaiter = rotate_orb_cc(mf, mo_coeff, mo_occ, fock, h1e, conv_tol_grad, log)
    u, g_orb, kfcount, jkcount = next(rotaiter)
    kftot = kfcount + 1
    jktot = jkcount
    cput1 = log.timer('initializing second order scf', *cput0)

    for imacro in range(max_cycle):
        dm_last = dm
        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 (abs((e_tot-last_hf_e)/e_tot)*1e2 < 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

        u, g_orb, kfcount, jkcount = rotaiter.send((mo_coeff, mo_occ, fock))
        kftot += kfcount + 1
        jktot += jkcount

    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, jktot)
    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