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 initfockbuild(self): """ Using Roothan's equation to build a Initial Fock matrix and Transformation Matrices Returns: fmat: float or complex Fock matrix in Lowdin AO basis c_am: float Transformation Matrix |AO><MO| v_lm: float Transformation Matrix |LAO><MO| """ start = time.time() n_occ = int(sum(self.ks.mo_occ) / 2) err = 100 it = 0 self.h = self.ks.get_hcore() s = self.s.copy() x = self.x.copy() sx = np.dot(s, x) dm_lao = 0.5*transmat(self.ks.get_init_guess(self.ks.mol, \ self.ks.init_guess), sx).astype(complex) if isinstance(self.ks.diis, lib.diis.DIIS): self.adiis = self.ks.diis elif self.ks.diis: self.adiis = diis.SCF_DIIS(self.ks, self.ks.diis_file) self.adiis.space = self.ks.diis_space self.adiis.rollback = self.ks.diis_space_rollback else: self.adiis = None fmat, jmat, kmat = self.fockbuild(dm_lao) etot = self.energy(dm_lao, fmat, jmat, kmat) + self.enuc while (err > self.conv_tol): # Diagonalize F in the lowdin basis eigs, v_lm = np.linalg.eig(fmat) idx = eigs.argsort() eigs.sort() v_lm = v_lm[:, idx].copy() # Fill up the density in the MO basis and then Transform back rho = 0.5 * np.diag(self.ks.mo_occ).astype(complex) dm_lao = transmat(rho, v_lm, -1) etot_old = etot etot = self.energy(dm_lao, fmat, jmat, kmat) fmat, jmat, kmat = self.fockbuild(dm_lao, it) err = abs(etot - etot_old) logger.debug(self, "Ne: %f", np.trace(rho)) logger.debug( self, "Iteration: %d Energy: %.11f \ Error = %.11f", it, etot, err) it += 1 if it > self.ks.max_cycle: logger.log( self, "Max cycle of SCF reached: %d\n Exiting TDSCF. Please raise ks.max_cycle", it) quit() rho = 0.5 * np.diag(self.ks.mo_occ).astype(complex) dm_lao = transmat(rho, v_lm, -1) c_am = np.dot(self.x, v_lm) logger.log(self, "Ne: %f", np.trace(rho)) logger.log(self, "Converged Energy: %f", etot) # logger.log(self, "Eigenvalues: %f", eigs.real) # print "Eigenvalues: ", eigs.real end = time.time() logger.info(self, "Initial Fock Built time: %f", end - start) return fmat, c_am, v_lm