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
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
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
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
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
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
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
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