def get_occ(self, mo_energy, mo_coeff=None): ''' We cannot assume default mo_energy value, because the orbital energies are sorted after doing SCF. But in this function, we need the orbital energies are grouped by symmetry irreps ''' mol = self.mol nirrep = len(mol.symm_orb) if mo_coeff is not None: orbsym = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb, mo_coeff, self.get_ovlp(), False) orbsym = numpy.asarray(orbsym) else: orbsym = [numpy.repeat(ir, mol.symm_orb[i].shape[1]) for i, ir in enumerate(mol.irrep_id)] orbsym = numpy.hstack(orbsym) mo_occ = numpy.zeros_like(mo_energy) mo_e_left = [] idx_e_left = [] nelec_fix = 0 for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] ir_idx = numpy.where(orbsym == ir)[0] if irname in self.irrep_nelec: n = self.irrep_nelec[irname] e_idx = numpy.argsort(mo_energy[ir_idx]) mo_occ[ir_idx[e_idx[:n//2]]] = 2 nelec_fix += n else: idx_e_left.append(ir_idx) nelec_float = mol.nelectron - nelec_fix assert(nelec_float >= 0) if nelec_float > 0: idx_e_left = numpy.hstack(idx_e_left) mo_e_left = mo_energy[idx_e_left] mo_e_sort = numpy.argsort(mo_e_left) occ_idx = idx_e_left[mo_e_sort[:(nelec_float//2)]] mo_occ[occ_idx] = 2 viridx = (mo_occ==0) if self.verbose < logger.INFO or viridx.sum() == 0: return mo_occ ehomo = max(mo_energy[mo_occ>0 ]) elumo = min(mo_energy[mo_occ==0]) noccs = [] for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] ir_idx = (orbsym == ir) noccs.append(int(mo_occ[ir_idx].sum())) if ehomo in mo_energy[ir_idx]: irhomo = irname if elumo in mo_energy[ir_idx]: irlumo = irname logger.info(self, 'H**O (%s) = %.15g LUMO (%s) = %.15g', irhomo, ehomo, irlumo, elumo) if self.verbose >= logger.DEBUG: logger.debug(self, 'irrep_nelec = %s', noccs) _dump_mo_energy(mol, mo_energy, mo_occ, ehomo, elumo, orbsym) return mo_occ
def kernel(self, mo_coeff=None, mo_occ=None): if mo_coeff is None: mo_coeff = self.mo_coeff if mo_occ is None: mo_occ = self.mo_occ cput0 = (time.clock(), time.time()) self.build(self.mol) self.dump_flags() if mo_coeff is None or mo_occ is None: logger.debug(self, 'Initial guess orbitals not given. ' 'Generating initial guess from %s density matrix', self.init_guess) dm = mf.get_init_guess(self.mol, self.init_guess) mo_coeff, mo_occ = self.from_dm(dm) # save initial guess because some methods may access them self.mo_coeff = mo_coeff self.mo_occ = mo_occ self.converged, self.e_tot, \ self.mo_energy, self.mo_coeff, self.mo_occ = \ kernel(self, mo_coeff, mo_occ, conv_tol=self.conv_tol, conv_tol_grad=self.conv_tol_grad, max_cycle=self.max_cycle, callback=self.callback, verbose=self.verbose) logger.timer(self, 'Second order SCF', *cput0) self._finalize() return self.e_tot
def get_occ(mf, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital. NOTE the occupancies are not assigned based on the orbital energy ordering. The first N orbitals are assigned to be occupied orbitals. Examples: >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1) >>> mf = scf.hf.SCF(mol) >>> energy = numpy.array([-10., -1., 1, -2., 0, -3]) >>> mf.get_occ(energy) array([2, 2, 2, 2, 1, 0]) ''' if mo_energy is None: mo_energy = mf.mo_energy if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb else: mo_ea = mo_eb = mo_energy nmo = mo_ea.size mo_occ = numpy.zeros(nmo) if getattr(mf, 'nelec', None) is None: nelec = mf.mol.nelec else: nelec = mf.nelec ncore = nelec[1] nocc = nelec[0] nopen = abs(nocc - ncore) mo_occ = _fill_rohf_occ(mo_energy, mo_ea, mo_eb, ncore, nopen) if mf.verbose >= logger.INFO and nocc < nmo and ncore > 0: ehomo = max(mo_energy[mo_occ> 0]) elumo = min(mo_energy[mo_occ==0]) if ehomo+1e-3 > elumo: logger.warn(mf, 'H**O %.15g >= LUMO %.15g', ehomo, elumo) else: logger.info(mf, ' H**O = %.15g LUMO = %.15g', ehomo, elumo) if nopen > 0 and mf.verbose >= logger.DEBUG: core_idx = mo_occ == 2 open_idx = mo_occ == 1 vir_idx = mo_occ == 0 logger.debug(mf, ' Roothaan | alpha | beta') logger.debug(mf, ' Highest 2-occ = %18.15g | %18.15g | %18.15g', max(mo_energy[core_idx]), max(mo_ea[core_idx]), max(mo_eb[core_idx])) logger.debug(mf, ' Lowest 0-occ = %18.15g | %18.15g | %18.15g', min(mo_energy[vir_idx]), min(mo_ea[vir_idx]), min(mo_eb[vir_idx])) for i in numpy.where(open_idx)[0]: logger.debug(mf, ' 1-occ = %18.15g | %18.15g | %18.15g', mo_energy[i], mo_ea[i], mo_eb[i]) if mf.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(mf, ' Roothaan mo_energy =\n%s', mo_energy) logger.debug1(mf, ' alpha mo_energy =\n%s', mo_ea) logger.debug1(mf, ' beta mo_energy =\n%s', mo_eb) numpy.set_printoptions(threshold=1000) return mo_occ
def get_occ(self, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = self.mo_energy mol = self.mol n4c = len(mo_energy) n2c = n4c // 2 mo_occ = numpy.zeros(n2c * 2) if mo_energy[n2c] > -1.999 * mol.light_speed ** 2: mo_occ[n2c : n2c + mol.nelectron] = 1 else: n = 0 for i, e in enumerate(mo_energy): if e > -1.999 * mol.light_speed ** 2 and n < mol.nelectron: mo_occ[i] = 1 n += 1 if self.verbose >= logger.INFO: logger.info( self, "H**O %d = %.12g, LUMO %d = %.12g,", (n2c + mol.nelectron) // 2, mo_energy[n2c + mol.nelectron - 1], (n2c + mol.nelectron) // 2 + 1, mo_energy[n2c + mol.nelectron], ) logger.debug(self, "NES mo_energy = %s", mo_energy[:n2c]) logger.debug(self, "PES mo_energy = %s", mo_energy[n2c:]) return mo_occ
def energy_elec(ks, dm, h1e=None, vhf=None): if h1e is None: h1e = ks.get_hcore() e1 = numpy.einsum('ij,ij', h1e.conj(), dm[0]+dm[1]) tot_e = e1 + ks._ecoul + ks._exc logger.debug(ks, 'Ecoul = %s Exc = %s', ks._ecoul, ks._exc) return tot_e, ks._ecoul+ks._exc
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
def grad_elec(mfg, mo_energy=None, mo_coeff=None, mo_occ=None): t0 = (time.clock(), time.time()) mf = mfg._scf mol = mfg.mol if mo_energy is None: mo_energy = mf.mo_energy if mo_occ is None: mo_occ = mf.mo_occ if mo_coeff is None: mo_coeff = mf.mo_coeff h1 = mfg.get_hcore(mol) s1 = mfg.get_ovlp(mol) dm0 = mf.make_rdm1(mf.mo_coeff, mf.mo_occ) vhf = mfg.get_veff(mol, dm0) log.timer(mfg, 'gradients of 2e part', *t0) f1 = h1 + vhf dme0 = mfg.make_rdm1e(mf.mo_energy, mf.mo_coeff, mf.mo_occ) gs = numpy.empty((mol.natm,3)) for ia in range(mol.natm): # h1, s1, vhf are \nabla <i|h|j>, the nuclear gradients = -\nabla f =-(mfg.matblock_by_atom(mol, ia, f1) + mfg._grad_rinv(mol, ia)) s = -mfg.matblock_by_atom(mol, ia, s1) v = numpy.einsum('ij,kji->k', dm0, f) \ - numpy.einsum('ij,kji->k', dme0, s) gs[ia] = 2 * v.real log.debug(mfg, 'gradients of electronic part') log.debug(mfg, str(gs)) return gs
def build_(self): mol = self.mol self.orth_coeff = self.get_orth_ao(mol) self.bas_on_frag = select_ao_on_fragment(mol, self.imp_atoms, \ self.imp_basidx) c_inv = numpy.dot(self.orth_coeff.T, self.entire_scf.get_ovlp(self.mol)) mo_a, mo_b = self.entire_scf.mo_coeff occ_a, occ_b = self.entire_scf.mo_occ mo_orth_a = numpy.dot(c_inv, mo_a[:,self.entire_scf.mo_occ[0]>1e-15]) mo_orth_b = numpy.dot(c_inv, mo_b[:,self.entire_scf.mo_occ[1]>1e-15]) # self.imp_site, self.bath_orb, self.env_orb are based on orth-orbitals self.imp_site, self.bath_orb, self.env_orb = \ self.decompose_orbital((mo_orth_a,mo_orth_b)) ovlp = numpy.dot(self.bath_orb[0].T,self.bath_orb[1])[:4,:4] for i,c in enumerate(ovlp): log.debug(self, ('<bath_alpha_%d|bath_beta> = ' % i) \ + '%10.5f'*len(c), *c) self.impbas_coeff = self.cons_impurity_basis() self.nelectron_alpha = self.entire_scf.nelectron_alpha \ - self.env_orb[0].shape[1] self.nelectron_beta = mol.nelectron \ - self.entire_scf.nelectron_alpha \ - self.env_orb[1].shape[1] log.info(self, 'alpha / beta electrons = %d / %d', \ self.nelectron_alpha, self.nelectron_beta) self.energy_by_env, self._vhf_env = self.init_vhf_env(self.env_orb)
def energy_elec(mf, dm=None, h1e=None, vhf=None): if dm is None: dm = mf.make_rdm1() elif isinstance(dm, numpy.ndarray) and dm.ndim == 2: dm = numpy.array((dm*.5, dm*.5)) ee, ecoul = uhf.energy_elec(mf, dm, h1e, vhf) logger.debug(mf, 'Ecoul = %.15g', ecoul) return ee, ecoul
def init_embsys(self, mol): eff_scf = self.entire_scf #embs = self.init_embs(mol, self.entire_scf, self.orth_coeff) emb = self.OneImp(eff_scf) emb.occ_env_cutoff = 1e-14 emb.orth_coeff = self.orth_coeff emb.verbose = self.emb_verbose emb.imp_scf() embs = [emb] sc = numpy.dot(eff_scf.get_ovlp(mol), eff_scf.mo_coeff) fock0 = numpy.dot(sc*eff_scf.mo_energy, sc.T.conj()) emb._project_fock = emb.mat_ao2impbas(fock0) nimp = len(emb.bas_on_frag) emb._pure_hcore = emb.get_hcore() \ - emb._vhf_env # exclude HF[core] and correlation potential cimp = numpy.dot(emb.impbas_coeff[:,:nimp].T, eff_scf.mo_coeff[:,eff_scf.mo_occ>0]) emb._project_nelec_frag = numpy.linalg.norm(cimp)**2*2 log.debug(emb, 'nelec of imp from lattice HF %.8g', emb._project_nelec_frag) log.debug(emb, 'nelec of imp from embedding HF %.8g', numpy.linalg.norm(emb.mo_coeff_on_imp[:nimp,:emb.nelectron/2])**2*2) #X embs = self.update_embs(mol, embs, self.entire_scf, self.orth_coeff) emb.vfit_mf = numpy.zeros_like(emb._pure_hcore) emb.vfit_ci = numpy.zeros_like(emb._pure_hcore) embs = self.update_embs_vfit_ci(mol, embs, [0]) #X embs = self.update_embs_vfit_mf(mol, embs, [0]) self.embs = embs return [0], [0]
def update_embs(self, mol, embs, eff_scf, orth_coeff): sc = numpy.dot(eff_scf.get_ovlp(), eff_scf.mo_coeff) c_inv = numpy.dot(eff_scf.get_ovlp(), orth_coeff).T fock0 = numpy.dot(sc*eff_scf.mo_energy, sc.T.conj()) hcore = eff_scf.get_hcore() nocc = int(eff_scf.mo_occ.sum()) / 2 for ifrag, emb in enumerate(embs): mo_orth = numpy.dot(c_inv, eff_scf.mo_coeff[:,eff_scf.mo_occ>1e-15]) emb.imp_site, emb.bath_orb, emb.env_orb = \ dmet_hf.decompose_orbital(emb, mo_orth, emb.bas_on_frag) emb.impbas_coeff = emb.cons_impurity_basis() emb.nelectron = mol.nelectron - emb.env_orb.shape[1] * 2 log.debug(emb, 'nelec of emb %d = %d', ifrag, emb.nelectron) emb._eri = emb.eri_on_impbas(mol) emb.energy_by_env, emb._vhf_env = emb.init_vhf_env(emb.env_orb) emb._project_fock = emb.mat_ao2impbas(fock0) emb.mo_energy, emb.mo_coeff_on_imp = scipy.linalg.eigh(emb._project_fock) emb.mo_coeff = numpy.dot(emb.impbas_coeff, emb.mo_coeff_on_imp) emb.mo_occ = numpy.zeros_like(emb.mo_energy) emb.mo_occ[:emb.nelectron/2] = 2 emb.hf_energy = 0 nimp = emb.imp_site.shape[1] cimp = numpy.dot(emb.impbas_coeff[:,:nimp].T, sc[:,:nocc]) emb._pure_hcore = emb.mat_ao2impbas(hcore) emb._project_nelec_frag = numpy.linalg.norm(cimp)**2*2 log.debug(self, 'project_nelec_frag = %f', emb._project_nelec_frag) # if isinstance(self.vfit_mf, numpy.ndarray): # v1 = emb.mat_orthao2impbas(self.vfit_mf) # v1[:nimp,:nimp] = 0 # emb._vhf_env += v1 return embs
def get_occ(self, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital Kwargs: mo_energy : 1D ndarray Obital energies mo_coeff : 2D ndarray Obital coefficients Examples: >>> from pyscf import gto, scf >>> mol = gto.M(atom='H 0 0 0; F 0 0 1.1') >>> mf = scf.hf.SCF(mol) >>> mf.get_occ(numpy.arange(mol.nao_nr())) array([2, 2, 2, 2, 2, 0]) ''' if mo_energy is None: mo_energy = self.mo_energy mo_occ = numpy.zeros_like(mo_energy) nocc = self.mol.nelectron // 2 mo_occ[:nocc] = 2 if nocc < mo_occ.size: logger.info(self, 'H**O = %.12g, LUMO = %.12g,', mo_energy[nocc-1], mo_energy[nocc]) if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]: logger.warn(self, '!! H**O %.12g == LUMO %.12g', mo_energy[nocc-1], mo_energy[nocc]) else: logger.info(self, 'H**O = %.12g,', mo_energy[nocc-1]) if self.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=len(mo_energy)) logger.debug(self, ' mo_energy = %s', mo_energy) numpy.set_printoptions() return mo_occ
def fupdate(t1, t2, istep, normt, de, adiis): nocc, nvir = t1.shape nov = nocc*nvir moidx = numpy.ones(mycc.mo_energy.size, dtype=numpy.bool) if isinstance(mycc.frozen, (int, numpy.integer)): moidx[:mycc.frozen] = False else: moidx[mycc.frozen] = False mo_e = mycc.mo_energy[moidx] eia = mo_e[:nocc,None] - mo_e[None,nocc:] if (istep > mycc.diis_start_cycle and abs(de) < mycc.diis_start_energy_diff): if mycc.t1 is None: mycc.t1 = t1 mycc.t2 = t2 else: tbuf = numpy.empty(nov*(nov+1)) tbuf[:nov] = ((t1-mycc.t1)*eia).ravel() pbuf = tbuf[nov:].reshape(nocc,nocc,nvir,nvir) for i in range(nocc): djba = (eia.reshape(-1,1) + eia[i].reshape(1,-1)).reshape(-1) pbuf[i] = (t2[i]-mycc.t2[i]) * djba.reshape(nocc,nvir,nvir) adiis.push_err_vec(tbuf) tbuf = numpy.empty(nov*(nov+1)) tbuf[:nov] = t1.ravel() tbuf[nov:] = t2.ravel() t1.data = tbuf.data # release memory t2.data = tbuf.data tbuf = adiis.update(tbuf) mycc.t1 = t1 = tbuf[:nov].reshape(nocc,nvir) mycc.t2 = t2 = tbuf[nov:].reshape(nocc,nocc,nvir,nvir) logger.debug(mycc, 'DIIS for step %d', istep) return t1, t2
def __debug_hessian( self ): hessian_analytic = self.__hessian() original_umatrix = np.array( self.u, copy=True ) stepsize = 1e-8 gradient_ref = self.__gradient() hessian_numerical = np.zeros( [ self.numVars, self.numVars ], dtype=float ) for counter in range( self.numVars ): self.u = np.array( original_umatrix, copy=True ) flatx = np.zeros( [ self.numVars ], dtype=float ) flatx[counter] = stepsize self.__update_unitary( flatx ) gradient_step = self.__gradient() hessian_numerical[ :, counter ] = ( gradient_step - gradient_ref ) / stepsize self.u = np.array( original_umatrix, copy=True ) flatx = np.zeros( [ self.numVars ], dtype=float ) self.__update_unitary( flatx ) hessian_numerical = 0.5 * ( hessian_numerical + hessian_numerical.T ) logger.debug(self, "2-norm( hessian difference ) = %.g", np.linalg.norm( hessian_analytic - hessian_numerical )) logger.debug(self, "2-norm( hessian ) = %.g", np.linalg.norm( hessian_analytic ))
def kernel(mp, mo_energy, mo_coeff, nocc, ioblk=256, verbose=None): nmo = mo_coeff.shape[1] nvir = nmo - nocc auxmol = df.incore.format_aux_basis(mp.mol, mp.auxbasis) naoaux = auxmol.nao_nr() iolen = max(int(ioblk*1e6/8/(nvir*nocc)), 160) eia = lib.direct_sum('i-a->ia', mo_energy[:nocc], mo_energy[nocc:]) t2 = None emp2 = 0 with mp.ao2mo(mo_coeff, nocc) as fov: for p0, p1 in prange(0, naoaux, iolen): logger.debug(mp, 'Load cderi block %d:%d', p0, p1) qov = numpy.array(fov[p0:p1], copy=False) for i in range(nocc): buf = numpy.dot(qov[:,i*nvir:(i+1)*nvir].T, qov).reshape(nvir,nocc,nvir) gi = numpy.array(buf, copy=False) gi = gi.reshape(nvir,nocc,nvir).transpose(1,2,0) t2i = gi/lib.direct_sum('jb+a->jba', eia, eia[i]) # 2*ijab-ijba theta = gi*2 - gi.transpose(0,2,1) emp2 += numpy.einsum('jab,jab', t2i, theta) return emp2, t2
def kernel(mp, mo_energy=None, mo_coeff=None, eris=None, with_t2=WITH_T2, verbose=logger.NOTE): if mo_energy is None or mo_coeff is None: mo_coeff = mp2._mo_without_core(mp, mp.mo_coeff) mo_energy = mp2._mo_energy_without_core(mp, mp.mo_energy) else: # For backward compatibility. In pyscf-1.4 or earlier, mp.frozen is # not supported when mo_energy or mo_coeff is given. assert(mp.frozen is 0 or mp.frozen is None) nocc = mp.nocc nvir = mp.nmo - nocc eia = mo_energy[:nocc,None] - mo_energy[None,nocc:] t2 = None emp2 = 0 for istep, qov in enumerate(mp.loop_ao2mo(mo_coeff, nocc)): logger.debug(mp, 'Load cderi step %d', istep) for i in range(nocc): buf = numpy.dot(qov[:,i*nvir:(i+1)*nvir].T, qov).reshape(nvir,nocc,nvir) gi = numpy.array(buf, copy=False) gi = gi.reshape(nvir,nocc,nvir).transpose(1,0,2) t2i = gi/lib.direct_sum('jb+a->jba', eia, eia[i]) emp2 += numpy.einsum('jab,jab', t2i, gi) * 2 emp2 -= numpy.einsum('jab,jba', t2i, gi) return emp2, t2
def fit_solver(embsys, fock0, nocc, nimp, dm_ref_alpha): #fitp = DmFitObj(fock0, nocc, nimp, dm_ref_alpha, v_V, dm_V) def _decompress(vfit): idx = numpy.tril_indices(nimp) v1 = numpy.zeros((nimp, nimp)) v1[idx] = vfit v1[idx[1],idx[0]] = vfit return v1 ec = [0, 0] def diff_dm(vfit): f = fock0.copy() f[:nimp,:nimp] += _decompress(vfit) e, c = scipy.linalg.eigh(f) dm0 = numpy.dot(c[:nimp,:nocc], c[:nimp,:nocc].T) ddm = dm0 - dm_ref_alpha[:nimp,:nimp] ec[:] = (e, c) return ddm.flatten() def jac_ddm(vfit, *args): e, c = ec x = mat_v_to_mat_dm1(e, c, nocc, nimp, nimp) usymm = symm_trans_mat_for_hermit(nimp) nn = usymm.shape[0] return numpy.dot(x.reshape(-1,nn), usymm) x = scipy.optimize.leastsq(diff_dm, numpy.zeros(nimp*(nimp+1)/2), Dfun=jac_ddm, ftol=1e-8)[0] log.debug(embsys, 'ddm %s', diff_dm(x)) return _decompress(x)
def fit_chemical_potential(mol, emb, embsys): # correlation potential of embedded-HF is not added to correlated-solver import scipy.optimize nimp = len(emb.bas_on_frag) nelec_frag = emb._project_nelec_frag # change chemical potential to get correct number of electrons def nelec_diff(v): vmat = emb.vfit_ci.copy() vmat[:nimp,:nimp] = numpy.eye(nimp) * v dm = embsys.solver.run(emb, emb._eri, vmat, True, False)[2] #print 'ddm ',nelec_frag,dm[:nimp].trace(), nelec_frag - dm[:nimp].trace() return nelec_frag - dm[:nimp].trace() # chem_pot0 = emb.vfit_ci[0,0] #OPTIMIZE ME, approximate chemical potential # sol = scipy.optimize.root(nelec_diff, chem_pot0, tol=1e-3, \ # method='lm', options={'ftol':1e-3, 'maxiter':12}) # nemb = emb.impbas_coeff.shape[1] # vmat = emb.vfit_ci.copy() # for i in range(nimp): # vmat[i,i] = sol.x # log.debug(embsys, 'scipy.optimize summary %s', sol) # log.debug(embsys, 'chem potential = %.11g, nelec error = %.11g', \ # sol.x, sol.fun) # log.debug(embsys, ' ncall = %d, scipy.optimize success: %s', \ # sol.nfev, sol.success) v1 = scipy.optimize.newton(nelec_diff, emb.vfit_ci[0,0], maxiter=500) vmat = emb.vfit_ci.copy() for i in range(nimp): vmat[i,i] = v1 if embsys.verbose >= log.DEBUG: log.debug(embsys, 'electron number diff %s', nelec_diff(v1)) return vmat
def assemble_frag_energy(self, mol): e_tot = 0 nelec = 0 e_corr = 0 last_frag = -1 for m, _, _ in self.all_frags: if m != last_frag: emb = self.embs[m] nimp = len(emb.bas_on_frag) _, e2frag, dm1 = \ self.solver.run(emb, emb._eri, emb.vfit_ci, with_1pdm=True, with_e2frag=nimp) e_frag, nelec_frag = \ self.extract_frag_energy(emb, dm1, e2frag) log.debug(self, 'fragment %d FCI-in-HF, frag energy = %.12g, E_corr = %.12g, nelec = %.9g', \ m, e_frag, e_frag-emb._ehfinhf, nelec_frag) e_corr += e_frag-emb._ehfinhf e_tot += e_frag nelec += nelec_frag last_frag = m log.info(self, 'sum(e_frag), e_tot = %.9g, nelec_tot = %.9g', \ e_tot, nelec) return e_tot, e_corr, nelec
def kernel(self, h1e, eri, norb, nelec, ci0=None, **kwargs): if self.verbose > logger.QUIET: pyscf.gto.mole.check_sanity(self, self._keys, self.stdout) wfnsym = _id_wfnsym(self, norb, nelec, self.wfnsym) if 'verbose' in kwargs: if isinstance(kwargs['verbose'], logger.Logger): log = kwargs['verbose'] else: log = logger.Logger(self.stdout, kwargs['verbose']) log.debug('total symmetry = %s', symm.irrep_id2name(self.mol.groupname, wfnsym)) else: logger.debug(self, 'total symmetry = %s', symm.irrep_id2name(self.mol.groupname, wfnsym)) e, c = direct_spin1.kernel_ms1(self, h1e, eri, norb, nelec, ci0, **kwargs) if self.wfnsym is not None: # should I remove the non-symmetric contributions in each # call of contract_2e? if self.nroots > 1: c = [addons.symmetrize_wfn(ci, norb, nelec, self.orbsym, wfnsym) for ci in c] else: c = addons.symmetrize_wfn(c, norb, nelec, self.orbsym, wfnsym) return e, c
def __debug_hessian_matvec( self ): hessian_analytic = np.zeros( [ self.numVars, self.numVars ], dtype=float ) for cnt in range( self.numVars ): vector = np.zeros( [ self.numVars ], dtype=float ) vector[ cnt ] = 1.0 hessian_analytic[ :, cnt ] = self.__hessian_matvec( vector ) original_umatrix = np.array( self.u, copy=True ) stepsize = 1e-8 self.__set_gradient() gradient_ref = np.array( self.gradient, copy=True ) hessian_numerical = np.zeros( [ self.numVars, self.numVars ], dtype=float ) for counter in range( self.numVars ): self.u = np.array( original_umatrix, copy=True ) flatx = np.zeros( [ self.numVars ], dtype=float ) flatx[counter] = stepsize self.__update_unitary( flatx ) self.__set_gradient() hessian_numerical[ :, counter ] = ( self.gradient - gradient_ref ) / stepsize self.u = np.array( original_umatrix, copy=True ) flatx = np.zeros( [ self.numVars ], dtype=float ) self.__update_unitary( flatx ) hessian_numerical = 0.5 * ( hessian_numerical + hessian_numerical.T ) logger.debug(self, "2-norm( hessian difference ) = %g", np.linalg.norm( hessian_analytic - hessian_numerical )) logger.debug(self, "2-norm( hessian ) = %g", np.linalg.norm( hessian_analytic ))
def get_j(self, cell=None, dm=None, hermi=1, kpt=None, kpts_band=None): r'''Compute J matrix for the given density matrix and k-point (kpt). When kpts_band is given, the J matrices on kpts_band are evaluated. J_{pq} = \sum_{rs} (pq|rs) dm[s,r] where r,s are orbitals on kpt. p and q are orbitals on kpts_band if kpts_band is given otherwise p and q are orbitals on kpt. ''' #return self.get_jk(cell, dm, hermi, kpt, kpts_band)[0] if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() if kpt is None: kpt = self.kpt cpu0 = (time.clock(), time.time()) dm = np.asarray(dm) nao = dm.shape[-1] if (kpts_band is None and (self._eri is not None or cell.incore_anyway or (not self.direct_scf and self._is_mem_enough()))): if self._eri is None: logger.debug(self, 'Building PBC AO integrals incore') self._eri = self.with_df.get_ao_eri(kpt, compact=True) vj, vk = mol_hf.dot_eri_dm(self._eri, dm.reshape(-1,nao,nao), hermi) else: vj = self.with_df.get_jk(dm.reshape(-1,nao,nao), hermi, kpt, kpts_band, with_k=False)[0] logger.timer(self, 'vj', *cpu0) return _format_jks(vj, dm, kpts_band)
def max_stepsize_scheduler(self, envs): if envs['de'] < self.conv_tol or self._max_stepsize is None: self._max_stepsize = self.max_stepsize else: self._max_stepsize *= .5 logger.debug(self, 'set max_stepsize to %g', self._max_stepsize) return self._max_stepsize
def get_occ(mo_energy, mo_coeff=None): mol = mf.mol mo_occ = numpy.zeros_like(mo_energy) nocc = mol.nelectron // 2 mo_occ[:nocc] = 2 if abs(mo_energy[nocc-1] - mo_energy[nocc]) < tol: lst = abs(mo_energy - mo_energy[nocc-1]) < tol nocc_left = int(lst[:nocc].sum()) ndocc = nocc - nocc_left mo_occ[ndocc:nocc] = 0 i = ndocc nmo = len(mo_energy) logger.info(mf, 'symm_allow_occ [:%d] = 2', ndocc) while i < nmo and nocc_left > 0: deg = (abs(mo_energy[i:i+5]-mo_energy[i]) < tol).sum() if deg <= nocc_left: mo_occ[i:i+deg] = 2 nocc_left -= deg logger.info(mf, 'symm_allow_occ [%d:%d] = 2, energy = %.12g', i, i+nocc_left, mo_energy[i]) break else: i += deg logger.info(mf, 'H**O = %.12g, LUMO = %.12g,', mo_energy[nocc-1], mo_energy[nocc]) logger.debug(mf, ' mo_energy = %s', mo_energy) return mo_occ
def get_jk(self, cell=None, dm=None, hermi=1, kpt=None, kpt_band=None): '''Get Coulomb (J) and exchange (K) following :func:`scf.hf.RHF.get_jk_`. Note the incore version, which initializes an _eri array in memory. ''' if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() if kpt is None: kpt = self.kpt cpu0 = (time.clock(), time.time()) if (kpt_band is None and (self.exxdiv == 'ewald' or self.exxdiv is None) and (self._eri is not None or cell.incore_anyway or self._is_mem_enough())): if self._eri is None: logger.debug(self, 'Building PBC AO integrals incore') self._eri = self.with_df.get_ao_eri(kpt, compact=True) vj, vk = dot_eri_dm(self._eri, dm, hermi) if self.exxdiv == 'ewald': from pyscf.pbc.df.df_jk import _ewald_exxdiv_for_G0 # G=0 is not inculded in the ._eri integrals _ewald_exxdiv_for_G0(self.cell, kpt, [dm], [vk]) else: vj, vk = self.with_df.get_jk(dm, hermi, kpt, kpt_band, exxdiv=self.exxdiv) logger.timer(self, 'vj and vk', *cpu0) return vj, vk
def get_occ(self, mo_energy=None, mo_coeff=None): '''Label the occupancies for each orbital. NOTE the occupancies are not assigned based on the orbital energy ordering. The first N orbitals are assigned to be occupied orbitals. Examples: >>> mol = gto.M(atom='H 0 0 0; O 0 0 1.1', spin=1) >>> mf = scf.hf.SCF(mol) >>> energy = numpy.array([-10., -1., 1, -2., 0, -3]) >>> mf.get_occ(energy) array([2, 2, 2, 2, 1, 0]) ''' if mo_energy is None: mo_energy = self.mo_energy mo_occ = numpy.zeros_like(mo_energy) ncore = self.nelec[1] nopen = self.nelec[0] - ncore nocc = ncore + nopen mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = 1 if nocc < len(mo_energy): logger.info(self, 'H**O = %.12g LUMO = %.12g', mo_energy[nocc-1], mo_energy[nocc]) if mo_energy[nocc-1]+1e-3 > mo_energy[nocc]: logger.warn(self.mol, '!! H**O %.12g == LUMO %.12g', mo_energy[nocc-1], mo_energy[nocc]) else: logger.info(self, 'H**O = %.12g no LUMO', mo_energy[nocc-1]) if nopen > 0: for i in range(ncore, nocc): logger.debug(self, 'singly occupied orbital energy = %.12g', mo_energy[i]) logger.debug(self, ' mo_energy = %s', mo_energy) return mo_occ
def project(mfmo, init_mo, ncore, s): nocc = ncore + casscf.ncas mo0core = init_mo[:,:ncore] s1 = reduce(numpy.dot, (mfmo.T, s, mo0core)) idx = numpy.argsort(numpy.einsum('ij,ij->i', s1, s1)) logger.debug(casscf, 'Core indices %s', str(numpy.sort(idx[-ncore:]))) # take HF core mocore = mfmo[:,numpy.sort(idx[-ncore:])] # take projected CAS space mocas = init_mo[:,ncore:nocc] \ - reduce(numpy.dot, (mocore, mocore.T, s, init_mo[:,ncore:nocc])) mocc = lo.orth.vec_lowdin(numpy.hstack((mocore, mocas)), s) # remove core and active space from rest mou = init_mo[:,nocc:] \ - reduce(numpy.dot, (mocc, mocc.T, s, init_mo[:,nocc:])) mo = lo.orth.vec_lowdin(numpy.hstack((mocc, mou)), s) if casscf.verbose >= logger.DEBUG: s1 = reduce(numpy.dot, (mo[:,ncore:nocc].T, s, mfmo)) idx = numpy.argwhere(abs(s1) > 0.4) for i,j in idx: logger.debug(casscf, 'Init guess <mo-CAS|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s1[i,j]) return mo
def label_orb_symm(mol, irrep_name, symm_orb, mo, s=None, check=True): '''Label the symmetry of given orbitals irrep_name can be either the symbol or the ID of the irreducible representation. If the ID is provided, it returns the numeric code associated with XOR operator, see :py:meth:`symm.param.IRREP_ID_TABLE` Args: mol : an instance of :class:`Mole` irrep_name : list of str or int A list of irrep ID or name, it can be either mol.irrep_id or mol.irrep_name. It can affect the return "label". symm_orb : list of 2d array the symmetry adapted basis mo : 2d array the orbitals to label Returns: list of symbols or integers to represent the irreps for the given orbitals Examples: >>> from pyscf import gto, scf, symm >>> mol = gto.M(atom='H 0 0 0; H 0 0 1', basis='ccpvdz',verbose=0, symmetry=1) >>> mf = scf.RHF(mol) >>> mf.kernel() >>> symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mf.mo_coeff) ['Ag', 'B1u', 'Ag', 'B1u', 'B2u', 'B3u', 'Ag', 'B2g', 'B3g', 'B1u'] >>> symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mf.mo_coeff) [0, 5, 0, 5, 6, 7, 0, 2, 3, 5] ''' nmo = mo.shape[1] if s is None: s = mol.intor_symmetric('cint1e_ovlp_sph') mo_s = numpy.dot(mo.T, s) norm = numpy.empty((len(irrep_name), nmo)) for i,ir in enumerate(irrep_name): moso = numpy.dot(mo_s, symm_orb[i]) norm[i] = numpy.einsum('ij,ij->i', moso, moso) iridx = numpy.argmax(norm, axis=0) orbsym = [irrep_name[i] for i in iridx] logger.debug(mol, 'irreps of each MO %s', str(orbsym)) if check: norm[iridx,numpy.arange(nmo)] = 0 orbidx = numpy.where(norm > THRESHOLD) if orbidx[1].size > 0: idx = numpy.where(norm > THRESHOLD*1e2) if idx[1].size > 0: logger.error(mol, 'orbitals %s not symmetrized, norm = %s', idx[1], norm[idx]) raise ValueError('orbitals %s not symmetrized' % idx[1]) else: logger.warn(mol, 'orbitals %s not strictly symmetrized.', orbidx[1]) logger.warn(mol, 'They can be symmetrized with ' 'pyscf.symm.symmetrize_orb function.') logger.debug(mol, 'norm = %s', norm[orbidx]) return orbsym
def make_modchg_basis(auxcell, smooth_eta, l_max=3): # * chgcell defines smooth gaussian functions for each angular momentum for # auxcell. The smooth functions may be used to carry the charge chgcell = copy.copy(auxcell) # smooth model density for coulomb integral to carry charge half_sph_norm = .5/numpy.sqrt(numpy.pi) chg_bas = [] chg_env = [smooth_eta] ptr_eta = auxcell._env.size ptr = ptr_eta + 1 for ia in range(auxcell.natm): for l in set(auxcell._bas[auxcell._bas[:,gto.ATOM_OF]==ia, gto.ANG_OF]): if l <= l_max: norm = half_sph_norm/gto.mole._gaussian_int(l*2+2, smooth_eta) chg_bas.append([ia, l, 1, 1, 0, ptr_eta, ptr, 0]) chg_env.append(norm) ptr += 1 chgcell._atm = auxcell._atm chgcell._bas = numpy.asarray(chg_bas, dtype=numpy.int32).reshape(-1,gto.BAS_SLOTS) chgcell._env = numpy.hstack((auxcell._env, chg_env)) chgcell.nimgs = auxcell.nimgs chgcell._built = True logger.debug(auxcell, 'smooth basis, num shells = %d, num cGTO = %d', chgcell.nbas, chgcell.nao_nr()) return chgcell
def vfit_mf_method(self, mol, embsys): dm_ref = [] for m,emb in enumerate(self.embs): nimp = len(emb.bas_on_frag) dv = numpy.eye(nimp) * embsys.vfit_ci[0] dmci = embsys.solver.run(emb, emb._eri, dv, True, False)[2] log.debug(embsys, 'dm_ref %d = %s', m, dmci) dm_ref.append(dmci*.5) if self.translational: dm_ref = [dm_ref[0] for i in self.basidx_group] sc = reduce(numpy.dot, (self.orth_coeff.T, self.entire_scf.get_ovlp(), self.entire_scf.mo_coeff)) # this fock matrix includes the previous fitting potential fock0 = numpy.dot(sc*self.entire_scf.mo_energy, sc.T.conj()) nocc = mol.nelectron // 2 dv = embsys.fit_solver(fock0, nocc, dm_ref) v = [embsys.vfit_mf[m]+dv[m] for m,emb in enumerate(self.embs)] log.debug(self, 'vfit_mf = ') try: v_group = self.pack(v) for vi in v_group: pyscf.tools.dump_mat.dump_tri(self.stdout, vi) except: self.stdout.write('%s\n' % str(v)) return v
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): '''Coulomb + XC functional for UKS. See pyscf/dft/rks.py :func:`get_veff` fore more details. ''' if mol is None: mol = ks.mol if dm is None: dm = ks.make_rdm1() if not isinstance(dm, numpy.ndarray): dm = numpy.asarray(dm) if dm.ndim == 2: # RHF DM dm = numpy.asarray((dm * .5, dm * .5)) ground_state = (dm.ndim == 3 and dm.shape[0] == 2) t0 = (time.clock(), time.time()) if ks.grids.coords is None: ks.grids.build(with_non0tab=True) if ks.small_rho_cutoff > 1e-20 and ground_state: ks.grids = rks.prune_small_rho_grids_(ks, mol, dm[0] + dm[1], ks.grids) t0 = logger.timer(ks, 'setting up grids', *t0) if ks.nlc != '': if ks.nlcgrids.coords is None: ks.nlcgrids.build(with_non0tab=True) if ks.small_rho_cutoff > 1e-20 and ground_state: ks.nlcgrids = rks.prune_small_rho_grids_( ks, mol, dm[0] + dm[1], ks.nlcgrids) t0 = logger.timer(ks, 'setting up nlc grids', *t0) ni = ks._numint if hermi == 2: # because rho = 0 n, exc, vxc = (0, 0), 0, 0 else: max_memory = ks.max_memory - lib.current_memory()[0] n, exc, vxc = ni.nr_uks(mol, ks.grids, ks.xc, dm, max_memory=max_memory) if ks.nlc != '': assert ('VV10' in ks.nlc.upper()) _, enlc, vnlc = ni.nr_rks(mol, ks.nlcgrids, ks.xc + '__' + ks.nlc, dm[0] + dm[1], max_memory=max_memory) exc += enlc vxc += vnlc logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) #enabling range-separated hybrids omega, alpha, hyb = ni.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin) if abs(hyb) < 1e-10 and abs(alpha) < 1e-10: vk = None if (ks._eri is None and ks.direct_scf and getattr(vhf_last, 'vj', None) is not None): ddm = numpy.asarray(dm) - numpy.asarray(dm_last) vj = ks.get_j(mol, ddm[0] + ddm[1], hermi) vj += vhf_last.vj else: vj = ks.get_j(mol, dm[0] + dm[1], hermi) vxc += vj else: if (ks._eri is None and ks.direct_scf and getattr(vhf_last, 'vk', None) is not None): ddm = numpy.asarray(dm) - numpy.asarray(dm_last) vj, vk = ks.get_jk(mol, ddm, hermi) vk *= hyb if abs(omega) > 1e-10: vklr = rks._get_k_lr(mol, ddm, omega, hermi) vklr *= (alpha - hyb) vk += vklr vj = vj[0] + vj[1] + vhf_last.vj vk += vhf_last.vk else: vj, vk = ks.get_jk(mol, dm, hermi) vj = vj[0] + vj[1] vk *= hyb if abs(omega) > 1e-10: vklr = rks._get_k_lr(mol, dm, omega, hermi) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk if ground_state: exc -= (numpy.einsum('ij,ji', dm[0], vk[0]) + numpy.einsum('ij,ji', dm[1], vk[1])) * .5 if ground_state: ecoul = numpy.einsum('ij,ji', dm[0] + dm[1], vj) * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk) return vxc
def get_init_guess(self, bvec, Adiag, Aop, precond): ''' Initial guess should solve the problem for SA-SA rotations ''' ci_arr = np.asarray(self.base.ci).reshape(self.nroots, -1) ndet = ci_arr.shape[-1] b_ci = bvec[self.ngorb:].reshape(self.nroots, ndet) x0 = np.zeros_like(bvec) if self.nroots > 1: b_sa = np.dot(ci_arr.conjugate(), b_ci[self.iroot]) A_sa = 2 * self.weights[self.iroot] * (self.e_mcscf - self.e_mcscf[self.iroot]) A_sa[self.iroot] = 1 b_sa[self.iroot] = 0 x0_sa = -b_sa / A_sa # Hessian is diagonal so: easy ovlp = ci_arr.conjugate() @ b_ci.T logger.debug(self, 'Linear response SA-SA part:\n{}'.format(ovlp)) logger.debug( self, 'Linear response SA-CI norms:\n{}'.format( linalg.norm(b_ci.T - ci_arr.T @ ovlp, axis=1))) logger.debug( self, 'Linear response orbital norms:\n{}'.format( linalg.norm(bvec[:self.ngorb]))) logger.debug( self, 'SA-SA Lagrange multiplier for root {}:\n{}'.format( self.iroot, x0_sa)) x0[self.ngorb:][ndet * self.iroot:][:ndet] = np.dot(x0_sa, ci_arr) r0 = bvec + Aop(x0) r0_ci = r0[self.ngorb:].reshape(self.nroots, ndet) ovlp = ci_arr.conjugate() @ r0_ci.T logger.debug( self, 'Lagrange residual SA-SA part after solving SA-SA part:\n{}'. format(ovlp)) logger.debug( self, 'Lagrange residual SA-CI norms after solving SA-SA part:\n{}'. format(linalg.norm(r0_ci.T - ci_arr.T @ ovlp, axis=1))) logger.debug( self, 'Lagrange residual orbital norms after solving SA-SA part:\n{}'. format(linalg.norm(r0[:self.ngorb]))) x0 += precond(-r0) r1 = bvec + Aop(x0) r1_ci = r1[self.ngorb:].reshape(self.nroots, ndet) ovlp = ci_arr.conjugate() @ r1_ci.T logger.debug( self, 'Lagrange residual SA-SA part after first precondition:\n{}'. format(ovlp)) logger.debug( self, 'Lagrange residual SA-CI norms after first precondition:\n{}'. format(linalg.norm(r1_ci.T - ci_arr.T @ ovlp, axis=1))) logger.debug( self, 'Lagrange residual orbital norms after first precondition:\n{}'. format(linalg.norm(r1[:self.ngorb]))) return x0
def mcpdft_HellmanFeynman_grad(mc, ot, veff1, veff2, mo_coeff=None, ci=None, atmlst=None, mf_grad=None, verbose=None): ''' Modification of pyscf.grad.casscf.kernel to compute instead the Hellman-Feynman gradient terms of MC-PDFT. From the differentiated Hamiltonian matrix elements, only the core and Coulomb energy parts remain. For the renormalization terms, the effective Fock matrix is as in CASSCF, but with the same Hamiltonian substutition that is used for the energy response terms. ''' if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if mf_grad is None: mf_grad = mc._scf.nuc_grad_method() if mc.frozen is not None: raise NotImplementedError t0 = (time.clock(), time.time()) mol = mc.mol ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nao, nmo = mo_coeff.shape nao_pair = nao * (nao + 1) // 2 mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:nocc] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63 dm_core = np.dot(mo_core, mo_core.T) * 2 dm_cas = reduce(np.dot, (mo_cas, casdm1, mo_cas.T)) # MRH: I need to replace aapa with the equivalent array from veff2 # I'm not sure how the outcore file-paging system works, but hopefully I can do this # I also need to generate vhf_c and vhf_a from veff2 rather than the molecule's actual integrals # The true Coulomb repulsion should already be in veff1, but I need to generate the "fake" # vj - vk/2 from veff2 h1e_mo = mo_coeff.T @ (mc.get_hcore() + veff1) @ mo_coeff + veff2.vhf_c aapa = np.zeros((ncas, ncas, nmo, ncas), dtype=h1e_mo.dtype) vhf_a = np.zeros((nmo, nmo), dtype=h1e_mo.dtype) for i in range(nmo): jbuf = veff2.ppaa[i] kbuf = veff2.papa[i] aapa[:, :, i, :] = jbuf[ncore:nocc, :, :] vhf_a[i] = np.tensordot(jbuf, casdm1, axes=2) vhf_a *= 0.5 # for this potential, vj = vk: vj - vk/2 = vj - vj/2 = vj/2 gfock = np.zeros((nmo, nmo)) gfock[:, :ncore] = (h1e_mo[:, :ncore] + vhf_a[:, :ncore]) * 2 gfock[:, ncore:nocc] = h1e_mo[:, ncore:nocc] @ casdm1 gfock[:, ncore:nocc] += np.einsum('uviw,vuwt->it', aapa, casdm2) dme0 = reduce(np.dot, (mo_coeff, (gfock + gfock.T) * .5, mo_coeff.T)) aapa = vhf_a = h1e_mo = gfock = None t0 = logger.timer(mc, 'PDFT HlFn gfock', *t0) dm1 = dm_core + dm_cas # MRH: vhf1c and vhf1a should be the TRUE vj_c and vj_a (no vk!) vj = mf_grad.get_jk(dm=dm1)[0] hcore_deriv = mf_grad.hcore_generator(mol) s1 = mf_grad.get_ovlp(mol) if atmlst is None: atmlst = range(mol.natm) aoslices = mol.aoslice_by_atom() de_hcore = np.zeros((len(atmlst), 3)) de_renorm = np.zeros((len(atmlst), 3)) de_coul = np.zeros((len(atmlst), 3)) de_xc = np.zeros((len(atmlst), 3)) de_grid = np.zeros((len(atmlst), 3)) de_wgt = np.zeros((len(atmlst), 3)) de = np.zeros((len(atmlst), 3)) # MRH: Now I have to compute the gradient of the exchange-correlation energy # This involves derivatives of the orbitals that construct rho and Pi and therefore another # set of potentials. It also involves the derivatives of quadrature grid points which # propagate through the densities and therefore yet another set of potentials. # The orbital-derivative part includes all the grid points and some of the orbitals (- sign); # the grid-derivative part includes all of the orbitals and some of the grid points (+ sign). # I'll do a loop over grid sections and make arrays of type (3,nao, nao) and (3,nao, ncas, ncas, ncas). # I'll contract them within the grid loop for the grid derivatives and in the following # orbital loop for the xc derivatives dm1s = mc.make_rdm1s() casdm1s = np.stack(mc.fcisolver.make_rdm1s(ci, ncas, nelecas), axis=0) twoCDM = get_2CDM_from_2RDM(casdm2, casdm1s) casdm1s = None make_rho = tuple( ot._numint._gen_rho_evaluator(mol, dm1s[i], 1) for i in range(2)) make_rho_c = ot._numint._gen_rho_evaluator(mol, dm_core, 1) make_rho_a = ot._numint._gen_rho_evaluator(mol, dm_cas, 1) dv1 = np.zeros( (3, nao, nao)) # Term which should be contracted with the whole density matrix dv1_a = np.zeros( (3, nao, nao) ) # Term which should only be contracted with the core density matrix dv2 = np.zeros((3, nao)) idx = np.array([[1, 4, 5, 6], [2, 5, 7, 8], [3, 6, 8, 9]], dtype=np.int_) # For addressing particular ao derivatives if ot.xctype == 'LDA': idx = idx[:, 0] # For LDAs no second derivatives diag_idx = np.arange(ncas) # for puvx diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx casdm2_pack = (casdm2 + casdm2.transpose(0, 1, 3, 2)).reshape( ncas**2, ncas, ncas) casdm2_pack = pack_tril(casdm2_pack).reshape(ncas, ncas, -1) casdm2_pack[:, :, diag_idx] *= 0.5 diag_idx = np.arange(ncore, dtype=np.int_) * (ncore + 1) # for pqii full_atmlst = -np.ones(mol.natm, dtype=np.int_) t1 = logger.timer(mc, 'PDFT HlFn quadrature setup', *t0) for k, ia in enumerate(atmlst): full_atmlst[ia] = k for ia, (coords, w0, w1) in enumerate(rks_grad.grids_response_cc(ot.grids)): # For the xc potential derivative, I need every grid point in the entire molecule regardless of atmlist. (Because that's about orbitals.) # For the grid and weight derivatives, I only need the gridpoints that are in atmlst mask = gen_grid.make_mask(mol, coords) ao = ot._numint.eval_ao( mol, coords, deriv=ot.dens_deriv + 1, non0tab=mask) # Need 1st derivs for LDA, 2nd for GGA, etc. if ot.xctype == 'LDA': # Might confuse the rho and Pi generators if I don't slice this down aoval = ao[:1] elif ot.xctype == 'GGA': aoval = ao[:4] rho = np.asarray([m[0](0, aoval, mask, ot.xctype) for m in make_rho]) Pi = get_ontop_pair_density(ot, rho, aoval, dm1s, twoCDM, mo_cas, ot.dens_deriv) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} rho/Pi calc'.format(ia), *t1) moval_occ = np.tensordot(aoval, mo_occ, axes=1) moval_core = moval_occ[..., :ncore] moval_cas = moval_occ[..., ncore:] t1 = logger.timer(mc, 'PDFT HlFn quadrature atom {} ao2mo grid'.format(ia), *t1) eot, vrho, vot = ot.eval_ot(rho, Pi, weights=w0) ndpi = vot.shape[0] # Weight response de_wgt += np.tensordot(eot, w1[atmlst], axes=(0, 2)) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} weight response'.format(ia), *t1) # Find the atoms that are a part of the atomlist - grid correction shouldn't be added if they aren't there # The last stuff to vectorize is in get_veff_2body! k = full_atmlst[ia] # Vpq + Vpqii vrho = _contract_vot_rho(vot, make_rho_c[0](0, aoval, mask, ot.xctype), add_vrho=vrho) tmp_dv = np.stack([ ot.get_veff_1body(rho, Pi, [ao[ix], aoval], w0, kern=vrho) for ix in idx ], axis=0) if k >= 0: de_grid[k] += 2 * np.tensordot(tmp_dv, dm1.T, axes=2) # Grid response dv1 -= tmp_dv # XC response t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} Vpq + Vpqii'.format(ia), *t1) # Viiuv * Duv vrho_a = _contract_vot_rho(vot, make_rho_a[0](0, aoval, mask, ot.xctype)) tmp_dv = np.stack([ ot.get_veff_1body(rho, Pi, [ao[ix], aoval], w0, kern=vrho_a) for ix in idx ], axis=0) if k >= 0: de_grid[k] += 2 * np.tensordot(tmp_dv, dm_core.T, axes=2) # Grid response dv1_a -= tmp_dv # XC response t1 = logger.timer(mc, 'PDFT HlFn quadrature atom {} Viiuv'.format(ia), *t1) # Vpuvx tmp_dv = ot.get_veff_2body_kl(rho, Pi, moval_cas, moval_cas, w0, symm=True, kern=vot) # ndpi,ngrids,ncas*(ncas+1)//2 tmp_dv = np.tensordot(tmp_dv, casdm2_pack, axes=(-1, -1)) # ndpi, ngrids, ncas, ncas tmp_dv[0] = (tmp_dv[:ndpi] * moval_cas[:ndpi, :, None, :]).sum( 0) # Chain and product rule tmp_dv[1:ndpi] *= moval_cas[0, :, None, :] # Chain and product rule tmp_dv = tmp_dv.sum(-1) # ndpi, ngrids, ncas tmp_dv = np.tensordot(ao[idx[:, :ndpi]], tmp_dv, axes=((1, 2), (0, 1))) # comp, nao (orb), ncas (dm2) tmp_dv = np.einsum('cpu,pu->cp', tmp_dv, mo_cas) # comp, ncas if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1) dv2 -= tmp_dv # XC response t1 = logger.timer(mc, 'PDFT HlFn quadrature atom {} Vpuvx'.format(ia), *t1) for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] h1ao = hcore_deriv(ia) # MRH: this should be the TRUE hcore de_hcore[k] += np.einsum('xij,ij->x', h1ao, dm1) de_renorm[k] -= np.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2 de_coul[k] += np.einsum('xij,ij->x', vj[:, p0:p1], dm1[p0:p1]) * 2 de_xc[k] += np.einsum( 'xij,ij->x', dv1[:, p0:p1], dm1[p0:p1]) * 2 # Full quadrature, only some orbitals de_xc[k] += np.einsum('xij,ij->x', dv1_a[:, p0:p1], dm_core[p0:p1]) * 2 # Ditto de_xc[k] += dv2[:, p0:p1].sum(1) * 2 # Ditto de_nuc = mf_grad.grad_nuc(mol, atmlst) logger.debug(mc, "MC-PDFT Hellmann-Feynman nuclear :\n{}".format(de_nuc)) logger.debug( mc, "MC-PDFT Hellmann-Feynman hcore component:\n{}".format(de_hcore)) logger.debug( mc, "MC-PDFT Hellmann-Feynman coulomb component:\n{}".format(de_coul)) logger.debug(mc, "MC-PDFT Hellmann-Feynman xc component:\n{}".format(de_xc)) logger.debug( mc, "MC-PDFT Hellmann-Feynman quadrature point component:\n{}".format( de_grid)) logger.debug( mc, "MC-PDFT Hellmann-Feynman quadrature weight component:\n{}".format( de_wgt)) logger.debug( mc, "MC-PDFT Hellmann-Feynman renorm component:\n{}".format(de_renorm)) de = de_nuc + de_hcore + de_coul + de_renorm + de_xc + de_grid + de_wgt t1 = logger.timer(mc, 'PDFT HlFn total', *t0) return de
def kernel(gw, mo_energy, mo_coeff, Lpq=None, orbs=None, nw=None, vhf_df=False, verbose=logger.NOTE): ''' GW-corrected quasiparticle orbital energies Returns: A list : converged, mo_energy, mo_coeff ''' mf = gw._scf if gw.frozen is None: frozen = 0 else: frozen = gw.frozen # only support frozen core assert (isinstance(frozen, int)) assert (frozen < gw.nocc) if Lpq is None: Lpq = gw.ao2mo(mo_coeff) if orbs is None: orbs = range(gw.nmo) else: orbs = [x - frozen for x in orbs] if orbs[0] < 0: logger.warn(gw, 'GW orbs must be larger than frozen core!') raise RuntimeError # v_xc v_mf = mf.get_veff() - mf.get_j() v_mf = reduce(numpy.dot, (mo_coeff.T, v_mf, mo_coeff)) nocc = gw.nocc nmo = gw.nmo nvir = nmo - nocc # v_hf from DFT/HF density if vhf_df and frozen == 0: # density fitting for vk vk = -einsum('Lni,Lim->nm', Lpq[:, :, :nocc], Lpq[:, :nocc, :]) else: # exact vk without density fitting dm = mf.make_rdm1() rhf = scf.RHF(gw.mol) vk = rhf.get_veff(gw.mol, dm) - rhf.get_j(gw.mol, dm) vk = reduce(numpy.dot, (mo_coeff.T, vk, mo_coeff)) # Grids for integration on imaginary axis freqs, wts = _get_scaled_legendre_roots(nw) # Compute self-energy on imaginary axis i*[0,iw_cutoff] sigmaI, omega = get_sigma_diag(gw, orbs, Lpq, freqs, wts, iw_cutoff=5.) # Analytic continuation if gw.ac == 'twopole': coeff = AC_twopole_diag(sigmaI, omega, orbs, nocc) elif gw.ac == 'pade': coeff, omega_fit = AC_pade_thiele_diag(sigmaI, omega) conv = True mf_mo_energy = mo_energy.copy() ef = (mo_energy[nocc - 1] + mo_energy[nocc]) / 2. mo_energy = np.zeros_like(gw._scf.mo_energy) for p in orbs: if gw.linearized: # linearized G0W0 de = 1e-6 ep = mf_mo_energy[p] #TODO: analytic sigma derivative if gw.ac == 'twopole': sigmaR = two_pole(ep - ef, coeff[:, p - orbs[0]]).real dsigma = two_pole(ep - ef + de, coeff[:, p - orbs[0]]).real - sigmaR.real elif gw.ac == 'pade': sigmaR = pade_thiele(ep - ef, omega_fit[p - orbs[0]], coeff[:, p - orbs[0]]).real dsigma = pade_thiele(ep - ef + de, omega_fit[p - orbs[0]], coeff[:, p - orbs[0]]).real - sigmaR.real zn = 1.0 / (1.0 - dsigma / de) e = ep + zn * (sigmaR.real + vk[p, p] - v_mf[p, p]) mo_energy[p + frozen] = e else: # self-consistently solve QP equation def quasiparticle(omega): if gw.ac == 'twopole': sigmaR = two_pole(omega - ef, coeff[:, p - orbs[0]]).real elif gw.ac == 'pade': sigmaR = pade_thiele(omega - ef, omega_fit[p - orbs[0]], coeff[:, p - orbs[0]]).real return omega - mf_mo_energy[p] - (sigmaR.real + vk[p, p] - v_mf[p, p]) try: e = newton(quasiparticle, mf_mo_energy[p], tol=1e-6, maxiter=100) mo_energy[p + frozen] = e except RuntimeError: conv = False if gw.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(gw, ' GW mo_energy =\n%s', mo_energy) numpy.set_printoptions(threshold=1000) return conv, mo_energy, mo_coeff
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) < 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 convert_to_ghf(mf, out=None, remove_df=False): '''Convert the given mean-field object to the generalized HF/KS object Note this conversion only changes the class of the mean-field object. The total energy and wave-function are the same as them in the input mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer will be discarded. Its underlying SCF object mf._scf will be converted. Args: mf : SCF object Kwargs remove_df : bool Whether to convert the DF-SCF object to the normal SCF object. This conversion is not applied by default. Returns: An generalized SCF object ''' from pyscf import scf from pyscf import dft from pyscf.soscf import newton_ah assert (isinstance(mf, hf.SCF)) logger.debug(mf, 'Converting %s to GHF', mf.__class__) def update_mo_(mf, mf1): if mf.mo_energy is not None: if isinstance(mf, scf.hf.RHF): # RHF nao, nmo = mf.mo_coeff.shape orbspin = get_ghf_orbspin(mf.mo_energy, mf.mo_occ, True) mf1.mo_energy = numpy.empty(nmo * 2) mf1.mo_energy[orbspin == 0] = mf.mo_energy mf1.mo_energy[orbspin == 1] = mf.mo_energy mf1.mo_occ = numpy.empty(nmo * 2) mf1.mo_occ[orbspin == 0] = mf.mo_occ > 0 mf1.mo_occ[orbspin == 1] = mf.mo_occ == 2 mo_coeff = numpy.zeros((nao * 2, nmo * 2), dtype=mf.mo_coeff.dtype) mo_coeff[:nao, orbspin == 0] = mf.mo_coeff mo_coeff[nao:, orbspin == 1] = mf.mo_coeff if getattr(mf.mo_coeff, 'orbsym', None) is not None: orbsym = numpy.zeros_like(orbspin) orbsym[orbspin == 0] = mf.mo_coeff.orbsym orbsym[orbspin == 1] = mf.mo_coeff.orbsym mo_coeff = lib.tag_array(mo_coeff, orbsym=orbsym) mf1.mo_coeff = lib.tag_array(mo_coeff, orbspin=orbspin) else: # UHF nao, nmo = mf.mo_coeff[0].shape orbspin = get_ghf_orbspin(mf.mo_energy, mf.mo_occ, False) mf1.mo_energy = numpy.empty(nmo * 2) mf1.mo_energy[orbspin == 0] = mf.mo_energy[0] mf1.mo_energy[orbspin == 1] = mf.mo_energy[1] mf1.mo_occ = numpy.empty(nmo * 2) mf1.mo_occ[orbspin == 0] = mf.mo_occ[0] mf1.mo_occ[orbspin == 1] = mf.mo_occ[1] mo_coeff = numpy.zeros((nao * 2, nmo * 2), dtype=mf.mo_coeff[0].dtype) mo_coeff[:nao, orbspin == 0] = mf.mo_coeff[0] mo_coeff[nao:, orbspin == 1] = mf.mo_coeff[1] if getattr(mf.mo_coeff[0], 'orbsym', None) is not None: orbsym = numpy.zeros_like(orbspin) orbsym[orbspin == 0] = mf.mo_coeff[0].orbsym orbsym[orbspin == 1] = mf.mo_coeff[1].orbsym mo_coeff = lib.tag_array(mo_coeff, orbsym=orbsym) mf1.mo_coeff = lib.tag_array(mo_coeff, orbspin=orbspin) return mf1 if out is not None: assert (isinstance(out, scf.ghf.GHF)) out = _update_mf_without_soscf(mf, out, remove_df) elif isinstance(mf, scf.ghf.GHF): if getattr(mf, '_scf', None): return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df) else: return copy.copy(mf) else: known_cls = { scf.hf.RHF: scf.ghf.GHF, scf.rohf.ROHF: scf.ghf.GHF, scf.uhf.UHF: scf.ghf.GHF, scf.hf_symm.RHF: scf.ghf_symm.GHF, scf.hf_symm.ROHF: scf.ghf_symm.GHF, scf.uhf_symm.UHF: scf.ghf_symm.GHF, dft.rks.RKS: None, dft.roks.ROKS: None, dft.uks.UKS: None, dft.rks_symm.RKS: None, dft.rks_symm.ROKS: None, dft.uks_symm.UKS: None } out = _object_without_soscf(mf, known_cls, remove_df) return update_mo_(mf, out)
def kernel(self, tolerance="default", maxiter=30): """ Performs a self-consistent DMET calculation. Args: tolerance (float): convergence criterion; maxiter (int): maximal number of iterations; """ if tolerance == "default": tolerance = self.conv_tol self.convergence_history = [] # mixers = dict((k, diis.DIIS()) for k in self.__domains__.keys()) while True: logger.info(self.__mol__, "DMET step {:d}".format( len(self.convergence_history), )) mf = self.run_mf_kernel() umat = {} logger.debug1(self.__mol__, "Mean-field solver total energy E = {:.10f}".format( mf.e_tot, )) self.e_tot = 0 total_occupation = 0 domain_ids = [] embedded_solvers = [] replica_numbers = [] schmidt_bases = [] # Build embedded solvers logger.info(self.__mol__, "Building embedded solvers ...") for domain_id, domain_basis, schmidt_basis in self.iter_schmidt_basis(): domain_ids.append(domain_id) if self.__style__ == "interacting-bath": embedded_solvers.append(self.get_embedded_solver(schmidt_basis)) elif self.__style__ == "non-interacting-bath": embedded_solvers.append(self.get_embedded_solver(schmidt_basis, kind=domain_id)) else: raise ValueError("Internal error: unknown style '{}'".format(self.__style__)) replica_numbers.append(len(self.__domains__[domain_id])) schmidt_bases.append(schmidt_basis[2:]) # Fit chemical potential logger.info(self.__mol__, "Fitting chemical potential ...") GlobalChemicalPotentialFit( embedded_solvers, replica_numbers, self.__mol__.nelectron, log=self.__mol__, ).kernel() # Fit the u-matrix logger.info(self.__mol__, "Fitting the u-matrix ...") for domain_id, embedded_solver, schmidt_basis, nreplica in zip(domain_ids, embedded_solvers, schmidt_bases, replica_numbers): logger.debug(self.__mol__, "Domain {}".format(domain_id)) logger.debug1(self.__mol__, "Primary basis: {}".format(self.__domains__[domain_id][0])) if len(self.__domains__[domain_id]) > 1: for i, b in enumerate(self.__domains__[domain_id][1:]): logger.debug1(self.__mol__, "Secondary basis {:d}: {}".format(i, b)) logger.debug1(self.__mol__, "Correlated solver total energy E = {:.10f}".format( embedded_solver.e_tot, )) n_active_domain = schmidt_basis[0].shape[1] # TODO: fix this; no need to recalculate hcore partial_energy = embedded_solver.partial_etot( slice(n_active_domain), transform( self.__mf_solver__.get_hcore(), self.__orthogonal_basis_inv__.T.dot(numpy.concatenate(schmidt_basis, axis=1)), ), ) self.e_tot += nreplica * partial_energy logger.debug1(self.__mol__, "Correlated solver partial energy E = {:.10f}".format( partial_energy, )) partial_occupation = embedded_solver.partial_nelec(slice(n_active_domain)) total_occupation += nreplica * partial_occupation logger.debug1(self.__mol__, "Correlated solver partial occupation N = {:.10f}".format( partial_occupation, )) logger.debug2(self.__mol__, "Correlated solver density matrix: {}".format(embedded_solver.make_rdm1())) if tolerance is not None: # Continue with self-consistency nscf_mf = NonSelfConsistentMeanField(mf) nscf_mf.kernel() sc = self.__self_consistency__( nscf_mf, self.__orthogonal_basis__.dot(numpy.concatenate(schmidt_basis, axis=1)), embedded_solver, log=self.__mol__, ) sc.kernel(x0=None) local_umat = sc.parametrize_umat_full(sc.final_parameters) umat[domain_id] = local_umat logger.debug(self.__mol__, "Parameters: {}".format( sc.final_parameters, )) self.e_tot += mf.energy_nuc() if tolerance is not None: self.convergence_history.append(self.convergence_measure(umat)) self.umat = umat # self.umat = dict((k, mixers[k].update(umat[k])) for k in self.__domains__.keys()) logger.info(self.__mol__, "E = {:.10f} delta = {:.3e} q = {:.3e} max(umat) = {:.3e}".format( self.e_tot, self.convergence_history[-1], self.__mol__.nelectron - total_occupation, max(v.max() for v in self.umat.values()), )) else: logger.info(self.__mol__, "E = {:.10f} q = {:.3e}".format( self.e_tot, self.__mol__.nelectron - total_occupation, )) if tolerance is None or self.convergence_history[-1] < tolerance: return self.e_tot if maxiter is not None and len(self.convergence_history) >= maxiter: raise RuntimeError("The maximal number of iterations {:d} reached. The error {:.3e} is still above the requested tolerance of {:.3e}".format( maxiter, self.convergence_history[-1], tolerance, ))
def get_occ(self, mo_energy=None, mo_coeff=None, orbsym=None): ''' We assumed mo_energy are grouped by symmetry irreps, (see function self.eig). The orbitals are sorted after SCF. ''' if mo_energy is None: mo_energy = self.mo_energy mol = self.mol if not mol.symmetry: return uhf.UHF.get_occ(self, mo_energy, mo_coeff) if orbsym is None: if mo_coeff is not None: # due to linear-dep ovlp_ao = self.get_ovlp() orbsyma = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb, mo_coeff[0], ovlp_ao, False) orbsymb = symm.label_orb_symm(self, mol.irrep_id, mol.symm_orb, mo_coeff[1], ovlp_ao, False) orbsyma = numpy.asarray(orbsyma) orbsymb = numpy.asarray(orbsymb) else: ovlp_ao = None orbsyma = [ numpy.repeat(ir, mol.symm_orb[i].shape[1]) for i, ir in enumerate(mol.irrep_id) ] orbsyma = orbsymb = numpy.hstack(orbsyma) else: orbsyma = numpy.asarray(orbsym[0]) orbsymb = numpy.asarray(orbsym[1]) assert (mo_energy[0].size == orbsyma.size) mo_occ = numpy.zeros_like(mo_energy) idx_ea_left = [] idx_eb_left = [] neleca_fix = nelecb_fix = 0 for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] ir_idxa = numpy.where(orbsyma == ir)[0] ir_idxb = numpy.where(orbsymb == ir)[0] if irname in self.irrep_nelec: if isinstance(self.irrep_nelec[irname], (int, numpy.integer)): nelecb = self.irrep_nelec[irname] // 2 neleca = self.irrep_nelec[irname] - nelecb else: neleca, nelecb = self.irrep_nelec[irname] ea_idx = numpy.argsort(mo_energy[0][ir_idxa].round(9)) eb_idx = numpy.argsort(mo_energy[1][ir_idxb].round(9)) mo_occ[0, ir_idxa[ea_idx[:neleca]]] = 1 mo_occ[1, ir_idxb[eb_idx[:nelecb]]] = 1 neleca_fix += neleca nelecb_fix += nelecb else: idx_ea_left.append(ir_idxa) idx_eb_left.append(ir_idxb) neleca_float = self.nelec[0] - neleca_fix nelecb_float = self.nelec[1] - nelecb_fix assert (neleca_float >= 0) assert (nelecb_float >= 0) if len(idx_ea_left) > 0: idx_ea_left = numpy.hstack(idx_ea_left) ea_left = mo_energy[0][idx_ea_left] ea_sort = numpy.argsort(ea_left.round(9)) occ_idx = idx_ea_left[ea_sort][:neleca_float] mo_occ[0][occ_idx] = 1 if len(idx_eb_left) > 0: idx_eb_left = numpy.hstack(idx_eb_left) eb_left = mo_energy[1][idx_eb_left] eb_sort = numpy.argsort(eb_left.round(9)) occ_idx = idx_eb_left[eb_sort][:nelecb_float] mo_occ[1][occ_idx] = 1 vir_idx = (mo_occ[0] == 0) if self.verbose >= logger.INFO and numpy.count_nonzero(vir_idx) > 0: ehomoa = max(mo_energy[0][mo_occ[0] > 0]) elumoa = min(mo_energy[0][mo_occ[0] == 0]) ehomob = max(mo_energy[1][mo_occ[1] > 0]) elumob = min(mo_energy[1][mo_occ[1] == 0]) noccsa = [] noccsb = [] p0 = 0 for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] ir_idxa = orbsyma == ir ir_idxb = orbsymb == ir noccsa.append(numpy.count_nonzero(mo_occ[0][ir_idxa])) noccsb.append(numpy.count_nonzero(mo_occ[1][ir_idxb])) if ehomoa in mo_energy[0][ir_idxa]: irhomoa = irname if elumoa in mo_energy[0][ir_idxa]: irlumoa = irname if ehomob in mo_energy[1][ir_idxb]: irhomob = irname if elumob in mo_energy[1][ir_idxb]: irlumob = irname logger.info(self, 'alpha H**O (%s) = %.15g LUMO (%s) = %.15g', irhomoa, ehomoa, irlumoa, elumoa) logger.info(self, 'beta H**O (%s) = %.15g LUMO (%s) = %.15g', irhomob, ehomob, irlumob, elumob) ehomo = max(ehomoa, ehomob) elumo = min(elumoa, elumob) logger.debug(self, 'alpha irrep_nelec = %s', noccsa) logger.debug(self, 'beta irrep_nelec = %s', noccsb) hf_symm._dump_mo_energy(mol, mo_energy[0], mo_occ[0], ehomo, elumo, orbsyma, 'alpha-', verbose=self.verbose) hf_symm._dump_mo_energy(mol, mo_energy[1], mo_occ[1], ehomo, elumo, orbsymb, 'beta-', verbose=self.verbose) if mo_coeff is not None and self.verbose >= logger.DEBUG: if ovlp_ao is None: ovlp_ao = self.get_ovlp() ss, s = self.spin_square((mo_coeff[0][:, mo_occ[0] > 0], mo_coeff[1][:, mo_occ[1] > 0]), ovlp_ao) logger.debug(self, 'multiplicity <S^2> = %.8g 2S+1 = %.8g', ss, s) return mo_occ
def DMRG_COMPRESS_NEVPT(mc, maxM=500, root=0, nevptsolver=None, tol=1e-7): if (isinstance(mc, str)): mol = chkfile.load_mol(mc) fh5 = h5py.File(mc, 'r') ncas = fh5['mc/ncas'].value ncore = fh5['mc/ncore'].value nvirt = fh5['mc/nvirt'].value nelecas = fh5['mc/nelecas'].value nroots = fh5['mc/nroots'].value wfnsym = fh5['mc/wfnsym'].value fh5.close() mc_chk = mc else: mol = mc.mol ncas = mc.ncas ncore = mc.ncore nvirt = mc.mo_coeff.shape[1] - mc.ncas - mc.ncore nelecas = mc.nelecas nroots = mc.fcisolver.nroots wfnsym = mc.fcisolver.wfnsym mc_chk = 'nevpt_perturb_integral' write_chk(mc, root, mc_chk) if nevptsolver is None: nevptsolver = default_nevpt_schedule(mol, maxM, tol) nevptsolver.wfnsym = wfnsym nevptsolver.block_extra_keyword = mc.fcisolver.block_extra_keyword nevptsolver.nroots = nroots from pyscf.dmrgscf import settings nevptsolver.executable = settings.BLOCKEXE_COMPRESS_NEVPT scratch = nevptsolver.scratchDirectory nevptsolver.scratchDirectory = '' dmrgci.writeDMRGConfFile( nevptsolver, nelecas, False, with_2pdm=False, extraline=['fullrestart', 'nevpt_state_num %d' % root]) nevptsolver.scratchDirectory = scratch if nevptsolver.verbose >= logger.DEBUG1: inFile = os.path.join(nevptsolver.runtimeDir, nevptsolver.configFile) logger.debug1(nevptsolver, 'Block Input conf') logger.debug1(nevptsolver, open(inFile, 'r').read()) t0 = (time.clock(), time.time()) cmd = ' '.join( (nevptsolver.mpiprefix, '%s/nevpt_mpi.py' % os.path.dirname(os.path.realpath(__file__)), mc_chk, nevptsolver.executable, os.path.join(nevptsolver.runtimeDir, nevptsolver.configFile), nevptsolver.outputFile, nevptsolver.scratchDirectory)) logger.debug(nevptsolver, 'DMRG_COMPRESS_NEVPT cmd %s', cmd) try: output = subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError as err: logger.error(nevptsolver, cmd) raise err if nevptsolver.verbose >= logger.DEBUG1: logger.debug1( nevptsolver, open(os.path.join(nevptsolver.scratchDirectory, '0/dmrg.out')).read()) fh5 = h5py.File('Perturbation_%d' % root, 'r') Vi_e = fh5['Vi/energy'].value Vr_e = fh5['Vr/energy'].value fh5.close() logger.note(nevptsolver, 'Nevpt Energy:') logger.note(nevptsolver, 'Sr Subspace: E = %.14f' % (Vr_e)) logger.note(nevptsolver, 'Si Subspace: E = %.14f' % (Vi_e)) logger.timer(nevptsolver, 'MPS NEVPT calculation time', *t0)
def get_occ(self, mo_energy=None, mo_coeff=None): if mo_energy is None: mo_energy = self.mo_energy mol = self.mol if not self.mol.symmetry: return rohf.ROHF.get_occ(self, mo_energy, mo_coeff) if getattr(mo_energy, 'mo_ea', None) is not None: mo_ea = mo_energy.mo_ea mo_eb = mo_energy.mo_eb else: mo_ea = mo_eb = mo_energy nmo = mo_ea.size mo_occ = numpy.zeros(nmo) orbsym = get_orbsym(self.mol, mo_coeff) rest_idx = numpy.ones(mo_occ.size, dtype=bool) neleca_fix = 0 nelecb_fix = 0 for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] if irname in self.irrep_nelec: ir_idx = numpy.where(orbsym == ir)[0] if isinstance(self.irrep_nelec[irname], (int, numpy.integer)): nelecb = self.irrep_nelec[irname] // 2 neleca = self.irrep_nelec[irname] - nelecb else: neleca, nelecb = self.irrep_nelec[irname] if neleca > nelecb: ncore, nopen = nelecb, neleca - nelecb else: ncore, nopen = neleca, nelecb - neleca mo_occ[ir_idx] = rohf._fill_rohf_occ(mo_energy[ir_idx], mo_ea[ir_idx], mo_eb[ir_idx], ncore, nopen) neleca_fix += neleca nelecb_fix += nelecb rest_idx[ir_idx] = False nelec_float = mol.nelectron - neleca_fix - nelecb_fix assert (nelec_float >= 0) if len(rest_idx) > 0: rest_idx = numpy.where(rest_idx)[0] nopen = abs(mol.spin - (neleca_fix - nelecb_fix)) ncore = (nelec_float - nopen) // 2 mo_occ[rest_idx] = rohf._fill_rohf_occ(mo_energy[rest_idx], mo_ea[rest_idx], mo_eb[rest_idx], ncore, nopen) nocc, ncore = self.nelec nopen = nocc - ncore vir_idx = (mo_occ == 0) if self.verbose >= logger.INFO and nocc < nmo and ncore > 0: ehomo = max(mo_energy[~vir_idx]) elumo = min(mo_energy[vir_idx]) ndoccs = [] nsoccs = [] for i, ir in enumerate(mol.irrep_id): irname = mol.irrep_name[i] ir_idx = (orbsym == ir) ndoccs.append(numpy.count_nonzero(mo_occ[ir_idx] == 2)) nsoccs.append(numpy.count_nonzero(mo_occ[ir_idx] == 1)) if ehomo in mo_energy[ir_idx]: irhomo = irname if elumo in mo_energy[ir_idx]: irlumo = irname # to help self.eigh compute orbital energy self._irrep_doccs = ndoccs self._irrep_soccs = nsoccs logger.info(self, 'H**O (%s) = %.15g LUMO (%s) = %.15g', irhomo, ehomo, irlumo, elumo) logger.debug(self, 'double occ irrep_nelec = %s', ndoccs) logger.debug(self, 'single occ irrep_nelec = %s', nsoccs) #_dump_mo_energy(mol, mo_energy, mo_occ, ehomo, elumo, orbsym, # verbose=self.verbose) if nopen > 0: core_idx = mo_occ == 2 open_idx = mo_occ == 1 vir_idx = mo_occ == 0 logger.debug( self, ' Roothaan | alpha | beta' ) logger.debug(self, ' Highest 2-occ = %18.15g | %18.15g | %18.15g', max(mo_energy[core_idx]), max(mo_ea[core_idx]), max(mo_eb[core_idx])) logger.debug(self, ' Lowest 0-occ = %18.15g | %18.15g | %18.15g', min(mo_energy[vir_idx]), min(mo_ea[vir_idx]), min(mo_eb[vir_idx])) for i in numpy.where(open_idx)[0]: logger.debug( self, ' 1-occ = %18.15g | %18.15g | %18.15g', mo_energy[i], mo_ea[i], mo_eb[i]) numpy.set_printoptions(threshold=nmo) logger.debug(self, ' Roothaan mo_energy =\n%s', mo_energy) logger.debug1(self, ' alpha mo_energy =\n%s', mo_ea) logger.debug1(self, ' beta mo_energy =\n%s', mo_eb) numpy.set_printoptions(threshold=1000) return mo_occ
def label_orb_symm(mol, irrep_name, symm_orb, mo, s=None, check=True, tol=1e-9): '''Label the symmetry of given orbitals irrep_name can be either the symbol or the ID of the irreducible representation. If the ID is provided, it returns the numeric code associated with XOR operator, see :py:meth:`symm.param.IRREP_ID_TABLE` Args: mol : an instance of :class:`Mole` irrep_name : list of str or int A list of irrep ID or name, it can be either mol.irrep_id or mol.irrep_name. It can affect the return "label". symm_orb : list of 2d array the symmetry adapted basis mo : 2d array the orbitals to label Returns: list of symbols or integers to represent the irreps for the given orbitals Examples: >>> from pyscf import gto, scf, symm >>> mol = gto.M(atom='H 0 0 0; H 0 0 1', basis='ccpvdz',verbose=0, symmetry=1) >>> mf = scf.RHF(mol) >>> mf.kernel() >>> symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mf.mo_coeff) ['Ag', 'B1u', 'Ag', 'B1u', 'B2u', 'B3u', 'Ag', 'B2g', 'B3g', 'B1u'] >>> symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, mf.mo_coeff) [0, 5, 0, 5, 6, 7, 0, 2, 3, 5] ''' nmo = mo.shape[1] if s is None: s = mol.intor_symmetric('int1e_ovlp') mo_s = numpy.dot(mo.T, s) norm = numpy.zeros((len(irrep_name), nmo)) for i, csym in enumerate(symm_orb): moso = numpy.dot(mo_s, csym) ovlpso = reduce(numpy.dot, (csym.T, s, csym)) try: norm[i] = numpy.einsum('ik,ki->i', moso, lib.cho_solve(ovlpso, moso.T)) except: ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12 norm[i] = numpy.einsum('ik,ki->i', moso, lib.cho_solve(ovlpso, moso.T)) norm /= numpy.sum(norm, axis=0) # for orbitals which are not normalized iridx = numpy.argmax(norm, axis=0) orbsym = numpy.asarray([irrep_name[i] for i in iridx]) logger.debug(mol, 'irreps of each MO %s', orbsym) if check: largest_norm = norm[iridx, numpy.arange(nmo)] orbidx = numpy.where(largest_norm < 1 - tol)[0] if orbidx.size > 0: idx = numpy.where(largest_norm < 1 - tol * 1e2)[0] if idx.size > 0: raise ValueError('orbitals %s not symmetrized, norm = %s' % (idx, largest_norm[idx])) else: logger.warn(mol, 'orbitals %s not strictly symmetrized.', numpy.unique(orbidx)) logger.warn( mol, 'They can be symmetrized with ' 'pyscf.symm.symmetrize_space function.') logger.debug(mol, 'norm = %s', largest_norm[orbidx]) return orbsym
def update_casdm(self, mo, u, fcivec, e_ci, eris, envs={}): nmo = mo.shape[1] rmat = u - numpy.eye(nmo) #g = hessian_co(self, mo, rmat, fcivec, e_ci, eris) ### hessian_co part start ### ncas = self.ncas nelecas = self.nelecas ncore = self.ncore nocc = ncore + ncas uc = u[:, :ncore] ua = u[:, ncore:nocc].copy() ra = rmat[:, ncore:nocc].copy() h1e_mo = reduce(numpy.dot, (mo.T, self.get_hcore(), mo)) ddm = numpy.dot(uc, uc.T) * 2 ddm[numpy.diag_indices(ncore)] -= 2 if self.with_dep4: mo1 = numpy.dot(mo, u) mo1_cas = mo1[:, ncore:nocc] dm_core = numpy.dot(mo1[:, :ncore], mo1[:, :ncore].T) * 2 vj, vk = self._scf.get_jk(self.mol, dm_core) h1 = (reduce(numpy.dot, (ua.T, h1e_mo, ua)) + reduce(numpy.dot, (mo1_cas.T, vj - vk * .5, mo1_cas))) eris._paaa = self._exact_paaa(mo, u) h2 = eris._paaa[ncore:nocc] vj = vk = None else: p1aa = numpy.empty((nmo, ncas, ncas**2)) paa1 = numpy.empty((nmo, ncas**2, ncas)) jk = reduce(numpy.dot, (ua.T, eris.vhf_c, ua)) for i in range(nmo): jbuf = eris.ppaa[i] kbuf = eris.papa[i] jk += (numpy.einsum('quv,q->uv', jbuf, ddm[i]) - numpy.einsum('uqv,q->uv', kbuf, ddm[i]) * .5) p1aa[i] = lib.dot(ua.T, jbuf.reshape(nmo, -1)) paa1[i] = lib.dot(kbuf.transpose(0, 2, 1).reshape(-1, nmo), ra) h1 = reduce(numpy.dot, (ua.T, h1e_mo, ua)) + jk aa11 = lib.dot(ua.T, p1aa.reshape(nmo, -1)).reshape((ncas, ) * 4) aaaa = eris.ppaa[ncore:nocc, ncore:nocc, :, :] aa11 = aa11 + aa11.transpose(2, 3, 0, 1) - aaaa a11a = numpy.dot(ra.T, paa1.reshape(nmo, -1)).reshape((ncas, ) * 4) a11a = a11a + a11a.transpose(1, 0, 2, 3) a11a = a11a + a11a.transpose(0, 1, 3, 2) h2 = aa11 + a11a jbuf = kbuf = p1aa = paa1 = aaaa = aa11 = a11a = None # pure core response # response of (1/2 dm * vhf * dm) ~ ddm*vhf # Should I consider core response as a part of CI gradients? ecore = (numpy.einsum('pq,pq->', h1e_mo, ddm) + numpy.einsum('pq,pq->', eris.vhf_c, ddm)) ### hessian_co part end ### ci1, g = self.solve_approx_ci(h1, h2, fcivec, ecore, e_ci, envs) if g is not None: # So state average CI, DMRG etc will not be applied ovlp = numpy.dot(fcivec.ravel(), ci1.ravel()) norm_g = numpy.linalg.norm(g) if 1 - abs(ovlp) > norm_g * self.ci_grad_trust_region: logger.debug( self, '<ci1|ci0>=%5.3g |g|=%5.3g, ci1 out of trust region', ovlp, norm_g) ci1 = fcivec.ravel() + g ci1 *= 1 / numpy.linalg.norm(ci1) casdm1, casdm2 = self.fcisolver.make_rdm12(ci1, ncas, nelecas) return casdm1, casdm2, g, ci1
def kernel(gw, mo_energy, mo_coeff, orbs=None, kptlist=None, nw=None, verbose=logger.NOTE): '''GW-corrected quasiparticle orbital energies Returns: A list : converged, mo_energy, mo_coeff ''' mf = gw._scf if gw.frozen is None: frozen = 0 else: frozen = gw.frozen assert (frozen == 0) if orbs is None: orbs = range(gw.nmo) if kptlist is None: kptlist = range(gw.nkpts) nkpts = gw.nkpts nklist = len(kptlist) # v_xc dm = np.array(mf.make_rdm1()) v_mf = np.array(mf.get_veff()) - np.array(mf.get_j(dm_kpts=dm)) for k in range(nkpts): v_mf[k] = reduce(numpy.dot, (mo_coeff[k].T.conj(), v_mf[k], mo_coeff[k])) nocc = gw.nocc nmo = gw.nmo # v_hf from DFT/HF density if gw.fc: exxdiv = 'ewald' else: exxdiv = None rhf = scf.KRHF(gw.mol, gw.kpts, exxdiv=exxdiv) rhf.with_df = gw.with_df if getattr(gw.with_df, '_cderi', None) is None: raise RuntimeError('Found incompatible integral scheme %s.' 'KGWAC can be only used with GDF integrals' % gw.with_df.__class__) vk = rhf.get_veff(gw.mol, dm_kpts=dm) - rhf.get_j(gw.mol, dm_kpts=dm) for k in range(nkpts): vk[k] = reduce(numpy.dot, (mo_coeff[k].T.conj(), vk[k], mo_coeff[k])) # Grids for integration on imaginary axis freqs, wts = _get_scaled_legendre_roots(nw) # Compute self-energy on imaginary axis i*[0,iw_cutoff] sigmaI, omega = get_sigma_diag(gw, orbs, kptlist, freqs, wts, iw_cutoff=5.) # Analytic continuation coeff = [] if gw.ac == 'twopole': for k in range(nklist): coeff.append(AC_twopole_diag(sigmaI[k], omega, orbs, nocc)) elif gw.ac == 'pade': for k in range(nklist): coeff_tmp, omega_fit = AC_pade_thiele_diag(sigmaI[k], omega) coeff.append(coeff_tmp) coeff = np.array(coeff) conv = True # This code does not support metals h**o = -99. lumo = 99. for k in range(nkpts): if h**o < mf.mo_energy[k][nocc - 1]: h**o = mf.mo_energy[k][nocc - 1] if lumo > mf.mo_energy[k][nocc]: lumo = mf.mo_energy[k][nocc] ef = (h**o + lumo) / 2. mo_energy = np.zeros_like(np.array(mf.mo_energy)) for k in range(nklist): kn = kptlist[k] for p in orbs: if gw.linearized: # linearized G0W0 de = 1e-6 ep = mf.mo_energy[kn][p] #TODO: analytic sigma derivative if gw.ac == 'twopole': sigmaR = two_pole(ep - ef, coeff[k, :, p - orbs[0]]).real dsigma = two_pole(ep - ef + de, coeff[k, :, p - orbs[0]]).real - sigmaR.real elif gw.ac == 'pade': sigmaR = pade_thiele(ep - ef, omega_fit[p - orbs[0]], coeff[k, :, p - orbs[0]]).real dsigma = pade_thiele(ep - ef + de, omega_fit[p - orbs[0]], coeff[k, :, p - orbs[0]]).real - sigmaR.real zn = 1.0 / (1.0 - dsigma / de) e = ep + zn * (sigmaR.real + vk[kn, p, p].real - v_mf[kn, p, p].real) mo_energy[kn, p] = e else: # self-consistently solve QP equation def quasiparticle(omega): if gw.ac == 'twopole': sigmaR = two_pole(omega - ef, coeff[k, :, p - orbs[0]]).real elif gw.ac == 'pade': sigmaR = pade_thiele(omega - ef, omega_fit[p - orbs[0]], coeff[k, :, p - orbs[0]]).real return omega - mf.mo_energy[kn][p] - ( sigmaR.real + vk[kn, p, p].real - v_mf[kn, p, p].real) try: e = newton(quasiparticle, mf.mo_energy[kn][p], tol=1e-6, maxiter=100) mo_energy[kn, p] = e except RuntimeError: conv = False mo_coeff = mf.mo_coeff if gw.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) for k in range(nkpts): logger.debug(gw, ' GW mo_energy @ k%d =\n%s', k, mo_energy[k]) numpy.set_printoptions(threshold=1000) return conv, mo_energy, mo_coeff
def solve_approx_ci(self, h1, h2, ci0, ecore, e_ci, envs): ''' Solve CI eigenvalue/response problem approximately ''' ncas = self.ncas nelecas = self.nelecas ncore = self.ncore nocc = ncore + ncas if 'norm_gorb' in envs: tol = max(self.conv_tol, envs['norm_gorb']**2 * .1) else: tol = None if hasattr(self.fcisolver, 'approx_kernel'): fn = self.fcisolver.approx_kernel ci1 = fn(h1, h2, ncas, nelecas, ci0=ci0, tol=tol, max_memory=self.max_memory)[1] return ci1, None elif not (hasattr(self.fcisolver, 'contract_2e') and hasattr(self.fcisolver, 'absorb_h1e')): fn = self.fcisolver.kernel ci1 = fn(h1, h2, ncas, nelecas, ci0=ci0, tol=tol, max_memory=self.max_memory, max_cycle=self.ci_response_space)[1] return ci1, None h2eff = self.fcisolver.absorb_h1e(h1, h2, ncas, nelecas, .5) hc = self.fcisolver.contract_2e(h2eff, ci0, ncas, nelecas).ravel() g = hc - (e_ci - ecore) * ci0.ravel() if self.ci_response_space > 7: logger.debug(self, 'CI step by full response') # full response max_memory = max(400, self.max_memory - lib.current_memory()[0]) e, ci1 = self.fcisolver.kernel(h1, h2, ncas, nelecas, ci0=ci0, tol=tol, max_memory=max_memory) else: nd = min(max(self.ci_response_space, 2), ci0.size) logger.debug(self, 'CI step by %dD subspace response', nd) xs = [ci0.ravel()] ax = [hc] heff = numpy.empty((nd, nd)) seff = numpy.empty((nd, nd)) heff[0, 0] = numpy.dot(xs[0], ax[0]) seff[0, 0] = 1 for i in range(1, nd): xs.append(ax[i - 1] - xs[i - 1] * e_ci) ax.append( self.fcisolver.contract_2e(h2eff, xs[i], ncas, nelecas).ravel()) for j in range(i + 1): heff[i, j] = heff[j, i] = numpy.dot(xs[i], ax[j]) seff[i, j] = seff[j, i] = numpy.dot(xs[i], xs[j]) e, v = lib.safe_eigh(heff, seff)[:2] ci1 = xs[0] * v[0, 0] for i in range(1, nd): ci1 += xs[i] * v[i, 0] return ci1, g
def gen_atomic_grids(mol, atom_grid={}, radi_method=radi.gauss_chebyshev, level=3, prune=nwchem_prune): '''Generate number of radial grids and angular grids for the given molecule. Returns: A dict, with the atom symbol for the dict key. For each atom type, the dict value has two items: one is the meshgrid coordinates wrt the atom center; the second is the volume of that grid. ''' if isinstance(atom_grid, (list, tuple)): atom_grid = dict([(mol.atom_symbol(ia), atom_grid) for ia in range(mol.natm)]) atom_grids_tab = {} for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb not in atom_grids_tab: chg = gto.mole._charge(symb) if symb in atom_grid: n_rad, n_ang = atom_grid[symb] if n_ang not in LEBEDEV_NGRID: if n_ang in LEBEDEV_ORDER: n_ang = LEBEDEV_ORDER[n_ang] else: raise ValueError('Unsupported angular grids %d' % n_ang) else: n_rad = _default_rad(chg, level) n_ang = _default_ang(chg, level) rad, dr = radi_method(n_rad, chg) rad_weight = 4 * numpy.pi * rad * rad * dr # atomic_scale = 1 # rad *= atomic_scale # rad_weight *= atomic_scale if callable(prune): angs = prune(chg, rad, n_ang) else: angs = [n_ang] * n_rad logger.debug(mol, 'atom %s rad-grids = %d, ang-grids = %s', symb, n_rad, angs) angs = numpy.array(angs) coords = [] vol = [] for n in sorted(set(angs)): grid = numpy.empty((n, 4)) libdft.MakeAngularGrid(grid.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(n)) idx = numpy.where(angs == n)[0] for i0, i1 in prange(0, len(idx), 12): # 12 radi-grids as a group coords.append( numpy.einsum('i,jk->jik', rad[idx[i0:i1]], grid[:, :3]).reshape(-1, 3)) vol.append( numpy.einsum('i,j->ji', rad_weight[idx[i0:i1]], grid[:, 3]).ravel()) atom_grids_tab[symb] = (numpy.vstack(coords), numpy.hstack(vol)) return atom_grids_tab
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): '''Coulomb + XC functional .. note:: This function will change the ks object. Args: ks : an instance of :class:`RKS` XC functional are controlled by ks.xc attribute. Attribute ks.grids might be initialized. dm : ndarray or list of ndarrays A density matrix or a list of density matrices Kwargs: dm_last : ndarray or a list of ndarrays or 0 The density matrix baseline. If not 0, this function computes the increment of HF potential w.r.t. the reference HF potential matrix. vhf_last : ndarray or a list of ndarrays or 0 The reference Vxc potential matrix. hermi : int Whether J, K matrix is hermitian | 0 : no hermitian or symmetric | 1 : hermitian | 2 : anti-hermitian Returns: matrix Veff = J + Vxc. Veff can be a list matrices, if the input dm is a list of density matrices. ''' if mol is None: mol = ks.mol if dm is None: dm = ks.make_rdm1() t0 = (time.clock(), time.time()) ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2) if ks.grids.coords is None: ks.grids.build(with_non0tab=True) if ks.small_rho_cutoff > 1e-20 and ground_state: ks.grids = rks.prune_small_rho_grids_(ks, mol, dm, ks.grids) t0 = logger.timer(ks, 'setting up grids', *t0) if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: max_memory = ks.max_memory - lib.current_memory()[0] n, exc, vxc = ks._numint.r_vxc(mol, ks.grids, ks.xc, dm, hermi=hermi, max_memory=max_memory) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin) if abs(hyb) < 1e-10: vk = None if (ks._eri is None and ks.direct_scf and getattr(vhf_last, 'vj', None) is not None): ddm = numpy.asarray(dm) - numpy.asarray(dm_last) vj = ks.get_j(mol, ddm, hermi) vj += vhf_last.vj else: vj = ks.get_j(mol, dm, hermi) vxc += vj else: # if (ks._eri is None and ks.direct_scf and # getattr(vhf_last, 'vk', None) is not None): # ddm = numpy.asarray(dm) - numpy.asarray(dm_last) # vj, vk = ks.get_jk(mol, ddm, hermi) # vj += vhf_last.vj # vk += vhf_last.vk # else: # vj, vk = ks.get_jk(mol, dm, hermi) # vxc += vj - vk * hyb # # if ground_state: # exc -= numpy.einsum('ij,ji', dm, vk).real * hyb * .5 raise NotImplementedError if ground_state: ecoul = numpy.einsum('ij,ji', dm, vj).real * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk) return vxc
def get_veff(ks, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpts=None, kpts_band=None): '''Coulomb + XC functional .. note:: This is a replica of pyscf.dft.rks.get_veff with kpts added. This function will change the ks object. Args: ks : an instance of :class:`RKS` XC functional are controlled by ks.xc attribute. Attribute ks.grids might be initialized. dm : ndarray or list of ndarrays A density matrix or a list of density matrices Returns: Veff : (nkpts, nao, nao) or (*, nkpts, nao, nao) ndarray Veff = J + Vxc. ''' if cell is None: cell = ks.cell if dm is None: dm = ks.make_rdm1() if kpts is None: kpts = ks.kpts t0 = (logger.process_clock(), logger.perf_counter()) omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin) hybrid = abs(hyb) > 1e-10 or abs(alpha) > 1e-10 if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF): n, exc, vxc = multigrid.nr_rks(ks.with_df, ks.xc, dm, hermi, kpts, kpts_band, with_j=True, return_j=False) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) return vxc # ndim = 3 : dm.shape = (nkpts, nao, nao) ground_state = (isinstance(dm, np.ndarray) and dm.ndim == 3 and kpts_band is None) # For UniformGrids, grids.coords does not indicate whehter grids are initialized if ks.grids.non0tab is None: ks.grids.build(with_non0tab=True) if (isinstance(ks.grids, gen_grid.BeckeGrids) and ks.small_rho_cutoff > 1e-20 and ground_state): ks.grids = rks.prune_small_rho_grids_(ks, cell, dm, ks.grids, kpts) t0 = logger.timer(ks, 'setting up grids', *t0) if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: n, exc, vxc = ks._numint.nr_rks(cell, ks.grids, ks.xc, dm, 0, kpts, kpts_band) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) weight = 1. / len(kpts) if not hybrid: vj = ks.get_j(cell, dm, hermi, kpts, kpts_band) vxc += vj else: if getattr(ks.with_df, '_j_only', False): # for GDF and MDF ks.with_df._j_only = False vj, vk = ks.get_jk(cell, dm, hermi, kpts, kpts_band) vk *= hyb if abs(omega) > 1e-10: vklr = ks.get_k(cell, dm, hermi, kpts, kpts_band, omega=omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk * .5 if ground_state: exc -= np.einsum('Kij,Kji', dm, vk).real * .5 * .5 * weight if ground_state: ecoul = np.einsum('Kij,Kji', dm, vj).real * .5 * weight else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None) return vxc
def get_sigma_diag(gw, orbs, kptlist, freqs, wts, iw_cutoff=None, max_memory=8000): ''' Compute GW correlation self-energy (diagonal elements) in MO basis on imaginary axis ''' mo_energy = np.array(gw._scf.mo_energy) mo_coeff = np.array(gw._scf.mo_coeff) nocc = gw.nocc nmo = gw.nmo nkpts = gw.nkpts kpts = gw.kpts nklist = len(kptlist) nw = len(freqs) norbs = len(orbs) mydf = gw.with_df # possible kpts shift center kscaled = gw.mol.get_scaled_kpts(kpts) kscaled -= kscaled[0] # This code does not support metals h**o = -99. lumo = 99. for k in range(nkpts): if h**o < mo_energy[k][nocc - 1]: h**o = mo_energy[k][nocc - 1] if lumo > mo_energy[k][nocc]: lumo = mo_energy[k][nocc] if (lumo - h**o) < 1e-3: logger.warn(gw, 'This GW-AC code is not supporting metals!') ef = (h**o + lumo) / 2. # Integration on numerical grids if iw_cutoff is not None: nw_sigma = sum(iw < iw_cutoff for iw in freqs) + 1 else: nw_sigma = nw + 1 # Compute occ for -iw and vir for iw separately # to avoid branch cuts in analytic continuation omega_occ = np.zeros((nw_sigma), dtype=np.complex128) omega_vir = np.zeros((nw_sigma), dtype=np.complex128) omega_occ[1:] = -1j * freqs[:(nw_sigma - 1)] omega_vir[1:] = 1j * freqs[:(nw_sigma - 1)] orbs_occ = [i for i in orbs if i < nocc] norbs_occ = len(orbs_occ) emo_occ = np.zeros((nkpts, nmo, nw_sigma), dtype=np.complex128) emo_vir = np.zeros((nkpts, nmo, nw_sigma), dtype=np.complex128) for k in range(nkpts): emo_occ[k] = omega_occ[None, :] + ef - mo_energy[k][:, None] emo_vir[k] = omega_vir[None, :] + ef - mo_energy[k][:, None] sigma = np.zeros((nklist, norbs, nw_sigma), dtype=np.complex128) omega = np.zeros((norbs, nw_sigma), dtype=np.complex128) for p in range(norbs): orbp = orbs[p] if orbp < nocc: omega[p] = omega_occ.copy() else: omega[p] = omega_vir.copy() if gw.fc: # Set up q mesh for q->0 finite size correction q_pts = np.array([1e-3, 0, 0]).reshape(1, 3) q_abs = gw.mol.get_abs_kpts(q_pts) # Get qij = 1/sqrt(Omega) * < psi_{ik} | e^{iqr} | psi_{ak-q} > at q: (nkpts, nocc, nvir) qij = get_qij(gw, q_abs[0], mo_coeff) for kL in range(nkpts): # Lij: (ki, L, i, j) for looping every kL Lij = [] # kidx: save kj that conserves with kL and ki (-ki+kj+kL=G) # kidx_r: save ki that conserves with kL and kj (-ki+kj+kL=G) kidx = np.zeros((nkpts), dtype=np.int64) kidx_r = np.zeros((nkpts), dtype=np.int64) for i, kpti in enumerate(kpts): for j, kptj in enumerate(kpts): # Find (ki,kj) that satisfies momentum conservation with kL kconserv = -kscaled[i] + kscaled[j] + kscaled[kL] is_kconserv = np.linalg.norm(np.round(kconserv) - kconserv) < 1e-12 if is_kconserv: kidx[i] = j kidx_r[j] = i logger.debug( gw, "Read Lpq (kL: %s / %s, ki: %s, kj: %s)" % (kL + 1, nkpts, i, j)) Lij_out = None # Read (L|pq) and ao2mo transform to (L|ij) Lpq = [] for LpqR, LpqI, sign \ in mydf.sr_loop([kpti, kptj], max_memory=0.1*gw._scf.max_memory, compact=False): Lpq.append(LpqR + LpqI * 1.0j) # support uneqaul naux on different k points Lpq = np.vstack(Lpq).reshape(-1, nmo**2) tao = [] ao_loc = None moij, ijslice = _conc_mos(mo_coeff[i], mo_coeff[j])[2:] Lij_out = _ao2mo.r_e2(Lpq, moij, ijslice, tao, ao_loc, out=Lij_out) Lij.append(Lij_out.reshape(-1, nmo, nmo)) Lij = np.asarray(Lij) naux = Lij.shape[1] if kL == 0: for w in range(nw): # body dielectric matrix eps_body Pi = get_rho_response(gw, freqs[w], mo_energy, Lij, kL, kidx) eps_body_inv = np.linalg.inv(np.eye(naux) - Pi) if gw.fc: # head dielectric matrix eps_00 Pi_00 = get_rho_response_head(gw, freqs[w], mo_energy, qij) eps_00 = 1. - 4. * np.pi / np.linalg.norm( q_abs[0])**2 * Pi_00 # wings dielectric matrix eps_P0 Pi_P0 = get_rho_response_wing(gw, freqs[w], mo_energy, Lij, qij) eps_P0 = -np.sqrt(4. * np.pi) / np.linalg.norm( q_abs[0]) * Pi_P0 # inverse dielectric matrix eps_inv_00 = 1. / (eps_00 - np.dot( np.dot(eps_P0.conj(), eps_body_inv), eps_P0)) eps_inv_P0 = -eps_inv_00 * np.dot(eps_body_inv, eps_P0) # head correction Del_00 = 2. / np.pi * (6. * np.pi**2 / gw.mol.vol / nkpts )**(1. / 3.) * (eps_inv_00 - 1.) eps_inv_PQ = eps_body_inv g0_occ = wts[w] * emo_occ / (emo_occ**2 + freqs[w]**2) g0_vir = wts[w] * emo_vir / (emo_vir**2 + freqs[w]**2) for k in range(nklist): kn = kptlist[k] # Find km that conserves with kn and kL (-km+kn+kL=G) km = kidx_r[kn] Qmn = einsum('Pmn,PQ->Qmn', Lij[km][:, :, orbs].conj(), eps_inv_PQ - np.eye(naux)) Wmn = 1. / nkpts * einsum('Qmn,Qmn->mn', Qmn, Lij[km][:, :, orbs]) sigma[k][:norbs_occ] += -einsum( 'mn,mw->nw', Wmn[:, :norbs_occ], g0_occ[km]) / np.pi sigma[k][norbs_occ:] += -einsum( 'mn,mw->nw', Wmn[:, norbs_occ:], g0_vir[km]) / np.pi if gw.fc: # apply head correction assert (kn == km) sigma[k][:norbs_occ] += -Del_00 * g0_occ[kn][ orbs][:norbs_occ] / np.pi sigma[k][norbs_occ:] += -Del_00 * g0_vir[kn][orbs][ norbs_occ:] / np.pi # apply wing correction Wn_P0 = einsum('Pnm,P->nm', Lij[kn], eps_inv_P0).diagonal() Wn_P0 = Wn_P0.real * 2. Del_P0 = np.sqrt(gw.mol.vol / 4. / np.pi**3) * ( 6. * np.pi**2 / gw.mol.vol / nkpts)**(2. / 3.) * Wn_P0[orbs] sigma[k][:norbs_occ] += -einsum( 'n,nw->nw', Del_P0[:norbs_occ], g0_occ[kn][orbs][:norbs_occ]) / np.pi sigma[k][norbs_occ:] += -einsum( 'n,nw->nw', Del_P0[norbs_occ:], g0_vir[kn][orbs][norbs_occ:]) / np.pi else: for w in range(nw): Pi = get_rho_response(gw, freqs[w], mo_energy, Lij, kL, kidx) Pi_inv = np.linalg.inv(np.eye(naux) - Pi) - np.eye(naux) g0_occ = wts[w] * emo_occ / (emo_occ**2 + freqs[w]**2) g0_vir = wts[w] * emo_vir / (emo_vir**2 + freqs[w]**2) for k in range(nklist): kn = kptlist[k] # Find km that conserves with kn and kL (-km+kn+kL=G) km = kidx_r[kn] Qmn = einsum('Pmn,PQ->Qmn', Lij[km][:, :, orbs].conj(), Pi_inv) Wmn = 1. / nkpts * einsum('Qmn,Qmn->mn', Qmn, Lij[km][:, :, orbs]) sigma[k][:norbs_occ] += -einsum( 'mn,mw->nw', Wmn[:, :norbs_occ], g0_occ[km]) / np.pi sigma[k][norbs_occ:] += -einsum( 'mn,mw->nw', Wmn[:, norbs_occ:], g0_vir[km]) / np.pi return sigma, omega
def kernel(gw, mo_energy, mo_coeff, Lpq=None, orbs=None, nw=None, vhf_df=False, verbose=logger.NOTE): ''' GW-corrected quasiparticle orbital energies Returns: A list : converged, mo_energy, mo_coeff ''' mf = gw._scf mol = gw.mol if gw.frozen is None: frozen = 0 else: frozen = gw.frozen assert (isinstance(frozen, int)) nocca, noccb = gw.nocc nmoa, nmob = gw.nmo # only support frozen core assert (frozen < nocca and frozen < noccb) if Lpq is None: Lpq = gw.ao2mo(mo_coeff) if orbs is None: orbs = range(nmoa) else: orbs = [x - frozen for x in orbs] if orbs[0] < 0: logger.warn(gw, 'GW orbs must be larger than frozen core!') raise RuntimeError # v_xc v_mf = mf.get_veff() vj = mf.get_j() v_mf[0] = v_mf[0] - (vj[0] + vj[1]) v_mf[1] = v_mf[1] - (vj[0] + vj[1]) v_mf_frz = np.zeros((2, nmoa - frozen, nmob - frozen)) for s in range(2): v_mf_frz[s] = reduce(numpy.dot, (mo_coeff[s].T, v_mf[s], mo_coeff[s])) v_mf = v_mf_frz # v_hf from DFT/HF density if vhf_df and frozen == 0: # density fitting vk vk = np.zeros_like(v_mf) vk[0] = -einsum('Lni,Lim->nm', Lpq[0, :, :, :nocca], Lpq[0, :, :nocca, :]) vk[1] = -einsum('Lni,Lim->nm', Lpq[1, :, :, :noccb], Lpq[1, :, :noccb, :]) else: # exact vk without density fitting dm = mf.make_rdm1() uhf = scf.UHF(mol) vk = uhf.get_veff(mol, dm) vj = uhf.get_j(mol, dm) vk[0] = vk[0] - (vj[0] + vj[1]) vk[1] = vk[1] - (vj[0] + vj[1]) vk_frz = np.zeros((2, nmoa - frozen, nmob - frozen)) for s in range(2): vk_frz[s] = reduce(numpy.dot, (mo_coeff[s].T, vk[s], mo_coeff[s])) vk = vk_frz # Grids for integration on imaginary axis freqs, wts = _get_scaled_legendre_roots(nw) # Compute self-energy on imaginary axis i*[0,iw_cutoff] sigmaI, omega = get_sigma_diag(gw, orbs, Lpq, freqs, wts, iw_cutoff=5.) # Analytic continuation if gw.ac == 'twopole': coeff_a = AC_twopole_diag(sigmaI[0], omega[0], orbs, nocca) coeff_b = AC_twopole_diag(sigmaI[1], omega[1], orbs, noccb) elif gw.ac == 'pade': coeff_a, omega_fit_a = AC_pade_thiele_diag(sigmaI[0], omega[0]) coeff_b, omega_fit_b = AC_pade_thiele_diag(sigmaI[1], omega[1]) omega_fit = np.asarray((omega_fit_a, omega_fit_b)) coeff = np.asarray((coeff_a, coeff_b)) conv = True h**o = max(mo_energy[0][nocca - 1], mo_energy[1][noccb - 1]) lumo = min(mo_energy[0][nocca], mo_energy[1][noccb]) ef = (h**o + lumo) / 2. mf_mo_energy = mo_energy.copy() mo_energy = np.zeros_like(np.asarray(gw._scf.mo_energy)) for s in range(2): for p in orbs: if gw.linearized: # linearized G0W0 de = 1e-6 ep = mf_mo_energy[s][p] #TODO: analytic sigma derivative if gw.ac == 'twopole': sigmaR = two_pole(ep - ef, coeff[s, :, p - orbs[0]]).real dsigma = two_pole(ep - ef + de, coeff[s, :, p - orbs[0]]).real - sigmaR.real elif gw.ac == 'pade': sigmaR = pade_thiele(ep - ef, omega_fit[s, p - orbs[0]], coeff[s, :, p - orbs[0]]).real dsigma = pade_thiele( ep - ef + de, omega_fit[s, p - orbs[0]], coeff[s, :, p - orbs[0]]).real - sigmaR.real zn = 1.0 / (1.0 - dsigma / de) e = ep + zn * (sigmaR.real + vk[s, p, p] - v_mf[s, p, p]) mo_energy[s, p + frozen] = e else: # self-consistently solve QP equation def quasiparticle(omega): if gw.ac == 'twopole': sigmaR = two_pole(omega - ef, coeff[s, :, p - orbs[0]]).real elif gw.ac == 'pade': sigmaR = pade_thiele(omega - ef, omega_fit[s, p - orbs[0]], coeff[s, :, p - orbs[0]]).real return omega - mf_mo_energy[s][p] - ( sigmaR.real + vk[s, p, p] - v_mf[s, p, p]) try: e = newton(quasiparticle, mf_mo_energy[s][p], tol=1e-6, maxiter=100) mo_energy[s, p + frozen] = e except RuntimeError: conv = False mo_coeff = gw._scf.mo_coeff if gw.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmoa) logger.debug(gw, ' GW mo_energy spin-up =\n%s', mo_energy[0]) logger.debug(gw, ' GW mo_energy spin-down =\n%s', mo_energy[1]) numpy.set_printoptions(threshold=1000) return conv, mo_energy, mo_coeff
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): '''Coulomb + XC functional .. note:: This function will change the ks object. Args: ks : an instance of :class:`RKS` XC functional are controlled by ks.xc attribute. Attribute ks.grids might be initialized. dm : ndarray or list of ndarrays A density matrix or a list of density matrices Kwargs: dm_last : ndarray or a list of ndarrays or 0 The density matrix baseline. If not 0, this function computes the increment of HF potential w.r.t. the reference HF potential matrix. vhf_last : ndarray or a list of ndarrays or 0 The reference Vxc potential matrix. hermi : int Whether J, K matrix is hermitian | 0 : no hermitian or symmetric | 1 : hermitian | 2 : anti-hermitian Returns: matrix Veff = J + Vxc. Veff can be a list matrices, if the input dm is a list of density matrices. ''' if mol is None: mol = ks.mol if dm is None: dm = ks.make_rdm1() t0 = (time.clock(), time.time()) ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2) if ks.grids.coords is None: ks.grids.build(with_non0tab=True) if ks.small_rho_cutoff > 1e-20 and ground_state: # Filter grids the first time setup grids ks.grids = prune_small_rho_grids_(ks, mol, dm, ks.grids) t0 = logger.timer(ks, 'setting up grids', *t0) if ks.nlc != '': if ks.nlcgrids.coords is None: ks.nlcgrids.build(with_non0tab=True) if ks.small_rho_cutoff > 1e-20 and ground_state: # Filter grids the first time setup grids ks.nlcgrids = prune_small_rho_grids_(ks, mol, dm, ks.nlcgrids) t0 = logger.timer(ks, 'setting up nlc grids', *t0) ni = ks._numint if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: n, exc, vxc = ni.nr_rks(mol, ks.grids, ks.xc, dm) if ks.nlc != '': assert ('VV10' in ks.nlc.upper()) _, enlc, vnlc = ni.nr_rks(mol, ks.nlcgrids, ks.xc + '__' + ks.nlc, dm) exc += enlc vxc += vnlc logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) #enabling range-separated hybrids omega, alpha, hyb = ni.rsh_and_hybrid_coeff(ks.xc, spin=mol.spin) if ks.omega is not None: omega = ks.omega if abs(hyb) < 1e-10 and abs(alpha) < 1e-10: vk = None if (ks._eri is None and ks.direct_scf and getattr(vhf_last, 'vj', None) is not None): ddm = numpy.asarray(dm) - numpy.asarray(dm_last) vj = ks.get_j(mol, ddm, hermi) vj += vhf_last.vj else: vj = ks.get_j(mol, dm, hermi) vxc += vj else: if (ks._eri is None and ks.direct_scf and getattr(vhf_last, 'vk', None) is not None): ddm = numpy.asarray(dm) - numpy.asarray(dm_last) vj, vk = ks.get_jk(mol, ddm, hermi) vk *= hyb if abs(omega) > 1e-10: # For range separated Coulomb operator vklr = _get_k_lr(mol, ddm, omega, hermi) vklr *= (alpha - hyb) vk += vklr vj += vhf_last.vj vk += vhf_last.vk else: vj, vk = ks.get_jk(mol, dm, hermi) vk *= hyb if abs(omega) > 1e-10: vklr = _get_k_lr(mol, dm, omega, hermi) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk * .5 if ground_state: exc -= numpy.einsum('ij,ji', dm, vk) * .5 * .5 if ground_state: ecoul = numpy.einsum('ij,ji', dm, vj) * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk) return vxc
def kernel(gw, mo_energy, mo_coeff, td_e, td_xy, eris=None, orbs=None, verbose=logger.NOTE): '''GW-corrected quasiparticle orbital energies Returns: A list : converged, mo_energy, mo_coeff ''' # mf must be DFT; for HF use xc = 'hf' mf = gw._scf assert (isinstance(mf, (dft.rks.RKS, dft.uks.UKS, dft.roks.ROKS, dft.uks.UKS, dft.rks_symm.RKS, dft.uks_symm.UKS, dft.rks_symm.ROKS, dft.uks_symm.UKS))) assert (gw.frozen is 0 or gw.frozen is None) if eris is None: eris = gw.ao2mo(mo_coeff) if orbs is None: orbs = range(gw.nmo) v_mf = mf.get_veff() - mf.get_j() v_mf = reduce(numpy.dot, (mo_coeff.T, v_mf, mo_coeff)) nocc = gw.nocc nmo = gw.nmo nvir = nmo - nocc vk_oo = -np.einsum('piiq->pq', eris.oooo) vk_ov = -np.einsum('piiq->pq', eris.ooov) vk_vv = -np.einsum('ipqi->pq', eris.ovvo).conj() vk = np.array(np.bmat([[vk_oo, vk_ov], [vk_ov.T, vk_vv]])) nexc = len(td_e) # factor of 2 for normalization, see tddft/rhf.py td_xy = 2 * np.asarray(td_xy) # (nexc, 2, nvir, nocc) td_z = np.sum(td_xy, axis=1).reshape(nexc, nvir, nocc) tdm_oo = einsum('vai,iapq->vpq', td_z, eris.ovoo) tdm_ov = einsum('vai,iapq->vpq', td_z, eris.ovov) tdm_vv = einsum('vai,iapq->vpq', td_z, eris.ovvv) tdm = [] for oo, ov, vv in zip(tdm_oo, tdm_ov, tdm_vv): tdm.append(np.array(np.bmat([[oo, ov], [ov.T, vv]]))) tdm = np.asarray(tdm) conv = True mo_energy = np.zeros_like(gw._scf.mo_energy) for p in orbs: tdm_p = tdm[:, :, p] if gw.linearized: de = 1e-6 ep = gw._scf.mo_energy[p] #TODO: analytic sigma derivative sigma = get_sigma_element(gw, ep, tdm_p, tdm_p, td_e).real dsigma = get_sigma_element(gw, ep + de, tdm_p, tdm_p, td_e).real - sigma zn = 1.0 / (1 - dsigma / de) e = ep + zn * (sigma.real + vk[p, p] - v_mf[p, p]) mo_energy[p] = e else: def quasiparticle(omega): sigma = get_sigma_element(gw, omega, tdm_p, tdm_p, td_e) return omega - gw._scf.mo_energy[p] - (sigma.real + vk[p, p] - v_mf[p, p]) try: e = newton(quasiparticle, gw._scf.mo_energy[p], tol=1e-6, maxiter=100) mo_energy[p] = e except RuntimeError: conv = False mo_coeff = gw._scf.mo_coeff if gw.verbose >= logger.DEBUG: numpy.set_printoptions(threshold=nmo) logger.debug(gw, ' GW mo_energy =\n%s', mo_energy) numpy.set_printoptions(threshold=1000) return conv, mo_energy, mo_coeff
def get_occ(mf, mo_energy_kpts=None, mo_coeff_kpts=None): '''Label the occupancies for each orbital for sampled k-points. This is a k-point version of scf.hf.SCF.get_occ ''' if mo_energy_kpts is None: mo_energy_kpts = mf.mo_energy if getattr(mo_energy_kpts[0], 'mo_ea', None) is not None: mo_ea_kpts = [x.mo_ea for x in mo_energy_kpts] mo_eb_kpts = [x.mo_eb for x in mo_energy_kpts] else: mo_ea_kpts = mo_eb_kpts = mo_energy_kpts nocc_a, nocc_b = mf.nelec mo_energy_kpts1 = np.hstack(mo_energy_kpts) mo_energy = np.sort(mo_energy_kpts1) if nocc_b > 0: core_level = mo_energy[nocc_b - 1] else: core_level = -1e9 if nocc_a == nocc_b: fermi = core_level else: mo_ea_kpts1 = np.hstack(mo_ea_kpts) mo_ea = np.sort(mo_ea_kpts1[mo_energy_kpts1 > core_level]) fermi = mo_ea[nocc_a - nocc_b - 1] mo_occ_kpts = [] for k, mo_e in enumerate(mo_energy_kpts): occ = np.zeros_like(mo_e) occ[mo_e <= core_level] = 2 if nocc_a != nocc_b: occ[(mo_e > core_level) & (mo_ea_kpts[k] <= fermi)] = 1 mo_occ_kpts.append(occ) if nocc_a < len(mo_energy): logger.info(mf, 'H**O = %.12g LUMO = %.12g', mo_energy[nocc_a - 1], mo_energy[nocc_a]) else: logger.info(mf, 'H**O = %.12g', mo_energy[nocc_a - 1]) np.set_printoptions(threshold=len(mo_energy)) if mf.verbose >= logger.DEBUG: logger.debug( mf, ' Roothaan | alpha | beta') for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)): core_idx = mo_occ_kpts[k] == 2 open_idx = mo_occ_kpts[k] == 1 vir_idx = mo_occ_kpts[k] == 0 logger.debug(mf, ' kpt %2d (%6.3f %6.3f %6.3f)', k, kpt[0], kpt[1], kpt[2]) if np.count_nonzero(core_idx) > 0: logger.debug(mf, ' Highest 2-occ = %18.15g | %18.15g | %18.15g', max(mo_energy_kpts[k][core_idx]), max(mo_ea_kpts[k][core_idx]), max(mo_eb_kpts[k][core_idx])) if np.count_nonzero(vir_idx) > 0: logger.debug(mf, ' Lowest 0-occ = %18.15g | %18.15g | %18.15g', min(mo_energy_kpts[k][vir_idx]), min(mo_ea_kpts[k][vir_idx]), min(mo_eb_kpts[k][vir_idx])) for i in np.where(open_idx)[0]: logger.debug(mf, ' 1-occ = %18.15g | %18.15g | %18.15g', mo_energy_kpts[k][i], mo_ea_kpts[k][i], mo_eb_kpts[k][i]) logger.debug(mf, ' k-point Roothaan mo_energy') for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)): logger.debug(mf, ' %2d (%6.3f %6.3f %6.3f) %s %s', k, kpt[0], kpt[1], kpt[2], mo_energy_kpts[k][mo_occ_kpts[k] > 0], mo_energy_kpts[k][mo_occ_kpts[k] == 0]) if mf.verbose >= logger.DEBUG1: logger.debug1(mf, ' k-point alpha mo_energy') for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)): logger.debug1(mf, ' %2d (%6.3f %6.3f %6.3f) %s %s', k, kpt[0], kpt[1], kpt[2], mo_ea_kpts[k][mo_occ_kpts[k] > 0], mo_ea_kpts[k][mo_occ_kpts[k] == 0]) logger.debug1(mf, ' k-point beta mo_energy') for k, kpt in enumerate(mf.cell.get_scaled_kpts(mf.kpts)): logger.debug1(mf, ' %2d (%6.3f %6.3f %6.3f) %s %s', k, kpt[0], kpt[1], kpt[2], mo_eb_kpts[k][mo_occ_kpts[k] == 2], mo_eb_kpts[k][mo_occ_kpts[k] != 2]) np.set_printoptions(threshold=1000) return mo_occ_kpts
def mcpdft_HellmanFeynman_grad(mc, ot, veff1, veff2, mo_coeff=None, ci=None, atmlst=None, mf_grad=None, verbose=None, max_memory=None, auxbasis_response=False): ''' Modification of pyscf.grad.casscf.kernel to compute instead the Hellman-Feynman gradient terms of MC-PDFT. From the differentiated Hamiltonian matrix elements, only the core and Coulomb energy parts remain. For the renormalization terms, the effective Fock matrix is as in CASSCF, but with the same Hamiltonian substutition that is used for the energy response terms. ''' if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if mf_grad is None: mf_grad = mc._scf.nuc_grad_method() if mc.frozen is not None: raise NotImplementedError if max_memory is None: max_memory = mc.max_memory t0 = (time.process_time(), time.time()) mol = mc.mol ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas nao, nmo = mo_coeff.shape nao_pair = nao * (nao + 1) // 2 mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:nocc] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) # gfock = Generalized Fock, Adv. Chem. Phys., 69, 63 dm_core = np.dot(mo_core, mo_core.T) * 2 dm_cas = reduce(np.dot, (mo_cas, casdm1, mo_cas.T)) # MRH: I need to replace aapa with the equivalent array from veff2 # I'm not sure how the outcore file-paging system works, but hopefully I can do this # I also need to generate vhf_c and vhf_a from veff2 rather than the molecule's actual integrals # The true Coulomb repulsion should already be in veff1, but I need to generate the "fake" # vj - vk/2 from veff2 h1e_mo = mo_coeff.T @ (mc.get_hcore() + veff1) @ mo_coeff + veff2.vhf_c aapa = np.zeros((ncas, ncas, nmo, ncas), dtype=h1e_mo.dtype) vhf_a = np.zeros((nmo, nmo), dtype=h1e_mo.dtype) for i in range(nmo): jbuf = veff2.ppaa[i] kbuf = veff2.papa[i] aapa[:, :, i, :] = jbuf[ncore:nocc, :, :] vhf_a[i] = np.tensordot(jbuf, casdm1, axes=2) vhf_a *= 0.5 # for this potential, vj = vk: vj - vk/2 = vj - vj/2 = vj/2 gfock = np.zeros((nmo, nmo)) gfock[:, :ncore] = (h1e_mo[:, :ncore] + vhf_a[:, :ncore]) * 2 gfock[:, ncore:nocc] = h1e_mo[:, ncore:nocc] @ casdm1 gfock[:, ncore:nocc] += np.einsum('uviw,vuwt->it', aapa, casdm2) dme0 = reduce(np.dot, (mo_coeff, (gfock + gfock.T) * .5, mo_coeff.T)) aapa = vhf_a = h1e_mo = gfock = None if atmlst is None: atmlst = range(mol.natm) aoslices = mol.aoslice_by_atom() de_hcore = np.zeros((len(atmlst), 3)) de_renorm = np.zeros((len(atmlst), 3)) de_coul = np.zeros((len(atmlst), 3)) de_xc = np.zeros((len(atmlst), 3)) de_grid = np.zeros((len(atmlst), 3)) de_wgt = np.zeros((len(atmlst), 3)) de_aux = np.zeros((len(atmlst), 3)) de = np.zeros((len(atmlst), 3)) t0 = logger.timer(mc, 'PDFT HlFn gfock', *t0) mo_coeff, ci, mo_occup = cas_natorb(mc, mo_coeff=mo_coeff, ci=ci) mo_occ = mo_coeff[:, :nocc] mo_core = mo_coeff[:, :ncore] mo_cas = mo_coeff[:, ncore:nocc] dm1 = dm_core + dm_cas dm1 = tag_array(dm1, mo_coeff=mo_coeff, mo_occ=mo_occup) # MRH: vhf1c and vhf1a should be the TRUE vj_c and vj_a (no vk!) vj = mf_grad.get_jk(dm=dm1)[0] hcore_deriv = mf_grad.hcore_generator(mol) s1 = mf_grad.get_ovlp(mol) if auxbasis_response: de_aux += vj.aux # MRH: Now I have to compute the gradient of the exchange-correlation energy # This involves derivatives of the orbitals that construct rho and Pi and therefore another # set of potentials. It also involves the derivatives of quadrature grid points which # propagate through the densities and therefore yet another set of potentials. # The orbital-derivative part includes all the grid points and some of the orbitals (- sign); # the grid-derivative part includes all of the orbitals and some of the grid points (+ sign). # I'll do a loop over grid sections and make arrays of type (3,nao, nao) and (3,nao, ncas, ncas, ncas). # I'll contract them within the grid loop for the grid derivatives and in the following # orbital loop for the xc derivatives # MRH, 05/09/2020: This just in - the actual spin density doesn't matter at all in PDFT! # I could probably save a fair amount of time by not screwing around with the actual spin density! # Also, the cumulant decomposition can always be defined without the spin-density matrices and # it's still valid! But one thing at a time. mo_n = mo_occ * mo_occup[None, :nocc] casdm1, casdm2 = mc.fcisolver.make_rdm12(ci, ncas, nelecas) twoCDM = get_2CDM_from_2RDM(casdm2, casdm1) dm1s = np.stack((dm1 / 2.0, ) * 2, axis=0) dm1 = tag_array(dm1, mo_coeff=mo_occ, mo_occ=mo_occup[:nocc]) make_rho = ot._numint._gen_rho_evaluator(mol, dm1, 1)[0] dvxc = np.zeros((3, nao)) idx = np.array([[1, 4, 5, 6], [2, 5, 7, 8], [3, 6, 8, 9]], dtype=np.int_) # For addressing particular ao derivatives if ot.xctype == 'LDA': idx = idx[:, 0:1] # For LDAs no second derivatives diag_idx = np.arange(ncas) # for puvx diag_idx = diag_idx * (diag_idx + 1) // 2 + diag_idx casdm2_pack = (twoCDM + twoCDM.transpose(0, 1, 3, 2)).reshape( ncas**2, ncas, ncas) casdm2_pack = pack_tril(casdm2_pack).reshape(ncas, ncas, -1) casdm2_pack[:, :, diag_idx] *= 0.5 diag_idx = np.arange(ncore, dtype=np.int_) * (ncore + 1) # for pqii full_atmlst = -np.ones(mol.natm, dtype=np.int_) t1 = logger.timer(mc, 'PDFT HlFn quadrature setup', *t0) for k, ia in enumerate(atmlst): full_atmlst[ia] = k for ia, (coords, w0, w1) in enumerate(rks_grad.grids_response_cc(ot.grids)): mask = gen_grid.make_mask(mol, coords) # For the xc potential derivative, I need every grid point in the entire molecule regardless of atmlist. (Because that's about orbitals.) # For the grid and weight derivatives, I only need the gridpoints that are in atmlst # It is conceivable that I can make this more efficient by only doing cross-combinations of grids and AOs, but I don't know how "mask" # works yet or how else I could do this. gc.collect() ngrids = coords.shape[0] ndao = (1, 4)[ot.dens_deriv] ndpi = (1, 4)[ot.Pi_deriv] ncols = 1.05 * 3 * (ndao * (nao + nocc) + max(ndao * nao, ndpi * ncas * ncas)) remaining_floats = (max_memory - current_memory()[0]) * 1e6 / 8 blksize = int(remaining_floats / (ncols * BLKSIZE)) * BLKSIZE blksize = max(BLKSIZE, min(blksize, ngrids, BLKSIZE * 1200)) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} mask and memory setup'.format(ia), *t1) for ip0 in range(0, ngrids, blksize): ip1 = min(ngrids, ip0 + blksize) logger.info( mc, 'PDFT gradient atom {} slice {}-{} of {} total'.format( ia, ip0, ip1, ngrids)) ao = ot._numint.eval_ao( mol, coords[ip0:ip1], deriv=ot.dens_deriv + 1, non0tab=mask) # Need 1st derivs for LDA, 2nd for GGA, etc. t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} ao grids'.format(ia), *t1) if ot.xctype == 'LDA': # Might confuse the rho and Pi generators if I don't slice this down aoval = ao[0] if ot.xctype == 'GGA': aoval = ao[:4] rho = make_rho(0, aoval, mask, ot.xctype) / 2.0 rho = np.stack((rho, ) * 2, axis=0) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} rho calc'.format(ia), *t1) Pi = get_ontop_pair_density(ot, rho, aoval, dm1s, twoCDM, mo_cas, ot.dens_deriv, mask) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} Pi calc'.format(ia), *t1) if ot.xctype == 'LDA': # TODO: consistent format requirements for shape of ao grid aoval = ao[:1] moval_occ = _grid_ao2mo(mol, aoval, mo_occ, mask) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} ao2mo grid'.format(ia), *t1) aoval = np.ascontiguousarray([ ao[ix].transpose(0, 2, 1) for ix in idx[:, :ndao] ]).transpose(0, 1, 3, 2) ao = None t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} ao grid reshape'.format(ia), *t1) eot, vot = ot.eval_ot(rho, Pi, weights=w0[ip0:ip1])[:2] vrho, vPi = vot t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} eval_ot'.format(ia), *t1) puvx_mem = 2 * ndpi * (ip1 - ip0) * ncas * ncas * 8 / 1e6 remaining_mem = max_memory - current_memory()[0] logger.info( mc, 'PDFT gradient memory note: working on {} grid points; estimated puvx usage = {:.1f} of {:.1f} remaining MB' .format((ip1 - ip0), puvx_mem, remaining_mem)) # Weight response de_wgt += np.tensordot(eot, w1[atmlst, ..., ip0:ip1], axes=(0, 2)) t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} weight response'.format(ia), *t1) # Find the atoms that are a part of the atomlist - grid correction shouldn't be added if they aren't there # The last stuff to vectorize is in get_veff_2body! k = full_atmlst[ia] # Vpq + Vpqrs * Drs ; I'm not sure why the list comprehension down there doesn't break ao's stride order but I'm not complaining vrho = _contract_vot_rho(vPi, rho.sum(0), add_vrho=vrho) tmp_dv = np.stack([ ot.get_veff_1body( rho, Pi, [ao_i, moval_occ], w0[ip0:ip1], kern=vrho) for ao_i in aoval ], axis=0) tmp_dv = (tmp_dv * mo_occ[None, :, :] * mo_occup[None, None, :nocc]).sum(2) if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1) # Grid response dvxc -= tmp_dv # XC response vrho = tmp_dv = None t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} Vpq + Vpqrs * Drs'.format(ia), *t1) # Vpuvx * Lpuvx ; remember the stupid slowest->fastest->medium stride order of the ao grid arrays moval_cas = moval_occ = np.ascontiguousarray( moval_occ[..., ncore:].transpose(0, 2, 1)).transpose(0, 2, 1) tmp_dv = ot.get_veff_2body_kl( rho, Pi, moval_cas, moval_cas, w0[ip0:ip1], symm=True, kern=vPi) # ndpi,ngrids,ncas*(ncas+1)//2 tmp_dv = np.tensordot(tmp_dv, casdm2_pack, axes=(-1, -1)) # ndpi, ngrids, ncas, ncas tmp_dv[0] = (tmp_dv[:ndpi] * moval_cas[:ndpi, :, None, :]).sum( 0) # Chain and product rule tmp_dv[1:ndpi] *= moval_cas[0, :, None, :] # Chain and product rule tmp_dv = tmp_dv.sum(-1) # ndpi, ngrids, ncas tmp_dv = np.tensordot(aoval[:, :ndpi], tmp_dv, axes=((1, 2), (0, 1))) # comp, nao (orb), ncas (dm2) tmp_dv = np.einsum( 'cpu,pu->cp', tmp_dv, mo_cas ) # comp, ncas (it's ok to not vectorize this b/c the quadrature grid is gone) if k >= 0: de_grid[k] += 2 * tmp_dv.sum(1) # Grid response dvxc -= tmp_dv # XC response tmp_dv = None t1 = logger.timer( mc, 'PDFT HlFn quadrature atom {} Vpuvx * Lpuvx'.format(ia), *t1) rho = Pi = eot = vot = vPi = aoval = moval_occ = moval_cas = None gc.collect() for k, ia in enumerate(atmlst): shl0, shl1, p0, p1 = aoslices[ia] h1ao = hcore_deriv(ia) # MRH: this should be the TRUE hcore de_hcore[k] += np.einsum('xij,ij->x', h1ao, dm1) de_renorm[k] -= np.einsum('xij,ij->x', s1[:, p0:p1], dme0[p0:p1]) * 2 de_coul[k] += np.einsum('xij,ij->x', vj[:, p0:p1], dm1[p0:p1]) * 2 de_xc[k] += dvxc[:, p0:p1].sum( 1) * 2 # Full quadrature, only some orbitals de_nuc = mf_grad.grad_nuc(mol, atmlst) logger.debug(mc, "MC-PDFT Hellmann-Feynman nuclear :\n{}".format(de_nuc)) logger.debug( mc, "MC-PDFT Hellmann-Feynman hcore component:\n{}".format(de_hcore)) logger.debug( mc, "MC-PDFT Hellmann-Feynman coulomb component:\n{}".format(de_coul)) logger.debug(mc, "MC-PDFT Hellmann-Feynman xc component:\n{}".format(de_xc)) logger.debug( mc, "MC-PDFT Hellmann-Feynman quadrature point component:\n{}".format( de_grid)) logger.debug( mc, "MC-PDFT Hellmann-Feynman quadrature weight component:\n{}".format( de_wgt)) logger.debug( mc, "MC-PDFT Hellmann-Feynman renorm component:\n{}".format(de_renorm)) de = de_nuc + de_hcore + de_coul + de_renorm + de_xc + de_grid + de_wgt if auxbasis_response: de += de_aux logger.debug( mc, "MC-PDFT Hellmann-Feynman aux component:\n{}".format(de_aux)) t1 = logger.timer(mc, 'PDFT HlFn total', *t0) return de
def get_veff(ks, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpt=None, kpts_band=None): '''Coulomb + XC functional .. note:: This function will change the ks object. Args: ks : an instance of :class:`RKS` XC functional are controlled by ks.xc attribute. Attribute ks.grids might be initialized. dm : ndarray or list of ndarrays A density matrix or a list of density matrices Returns: matrix Veff = J + Vxc. Veff can be a list matrices, if the input dm is a list of density matrices. ''' if cell is None: cell = ks.cell if dm is None: dm = ks.make_rdm1() if kpt is None: kpt = ks.kpt t0 = (time.clock(), time.time()) omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin) hybrid = abs(hyb) > 1e-10 or abs(alpha) > 1e-10 if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF): n, exc, vxc = multigrid.nr_rks(ks.with_df, ks.xc, dm, hermi, kpt.reshape(1, 3), kpts_band, with_j=True, return_j=False) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) return vxc ground_state = (isinstance(dm, numpy.ndarray) and dm.ndim == 2 and kpts_band is None) # Use grids.non0tab to detect whether grids are initialized. For # UniformGrids, grids.coords as a property cannot indicate whehter grids are # initialized. if ks.grids.non0tab is None: ks.grids.build(with_non0tab=True) if (isinstance(ks.grids, gen_grid.BeckeGrids) and ks.small_rho_cutoff > 1e-20 and ground_state): ks.grids = prune_small_rho_grids_(ks, cell, dm, ks.grids, kpt) t0 = logger.timer(ks, 'setting up grids', *t0) if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: n, exc, vxc = ks._numint.nr_rks(cell, ks.grids, ks.xc, dm, 0, kpt, kpts_band) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) if not hybrid: vj = ks.get_j(cell, dm, hermi, kpt, kpts_band) vxc += vj else: if getattr(ks.with_df, '_j_only', False): # for GDF and MDF ks.with_df._j_only = False vj, vk = ks.get_jk(cell, dm, hermi, kpt, kpts_band) vk *= hyb if abs(omega) > 1e-10: vklr = ks.get_k(cell, dm, hermi, kpt, kpts_band, omega=omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk * .5 if ground_state: exc -= numpy.einsum('ij,ji', dm, vk).real * .5 * .5 if ground_state: ecoul = numpy.einsum('ij,ji', dm, vj).real * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None) return vxc
def get_occ(mo_energy_kpts=None, mo_coeff_kpts=None): '''Label the occupancies for each orbital for sampled k-points. This is a k-point version of scf.hf.SCF.get_occ ''' mo_occ_kpts = mf_class.get_occ(mf, mo_energy_kpts, mo_coeff_kpts) if mf.sigma == 0 or not mf.sigma or not mf.smearing_method: return mo_occ_kpts if is_khf: nkpts = len(mf.kpts) else: nkpts = 1 if is_uhf: nocc = cell_nelec * nkpts mo_es = numpy.append(numpy.hstack(mo_energy_kpts[0]), numpy.hstack(mo_energy_kpts[1])) else: nocc = cell_nelec * nkpts // 2 mo_es = numpy.hstack(mo_energy_kpts) if mf.smearing_method.lower() == 'fermi': # Fermi-Dirac smearing f_occ = fermi_smearing_occ else: # Gaussian smearing f_occ = gaussian_smearing_occ mo_energy = numpy.sort(mo_es.ravel()) fermi = mo_energy[nocc - 1] sigma = mf.sigma def nelec_cost_fn(m): mo_occ_kpts = f_occ(m, mo_es, sigma) if not is_uhf: mo_occ_kpts *= 2 return (mo_occ_kpts.sum() / nkpts - cell_nelec)**2 res = scipy.optimize.minimize(nelec_cost_fn, fermi, method='Powell') mu = res.x mo_occs = f = f_occ(mu, mo_es, sigma) f = f[(f > 0) & (f < 1)] mf.entropy = -(f * numpy.log(f) + (1 - f) * numpy.log(1 - f)).sum() / nkpts if not is_uhf: mo_occs *= 2 mf.entropy *= 2 # DO NOT use numpy.array for mo_occ_kpts and mo_energy_kpts, they may # have different dimensions for different k-points if is_uhf: if is_khf: nao_tot = mo_occs.size // 2 mo_occ_kpts = (partition_occ(mo_occs[:nao_tot], mo_energy_kpts[0]), partition_occ(mo_occs[nao_tot:], mo_energy_kpts[1])) else: mo_occ_kpts = partition_occ(mo_occs, mo_energy_kpts) else: if is_khf: mo_occ_kpts = partition_occ(mo_occs, mo_energy_kpts) else: mo_occ_kpts = mo_occs logger.debug( mf, ' Fermi level %g Sum mo_occ_kpts = %s should equal nelec = %s', fermi, mo_occs.sum() / nkpts, cell_nelec) logger.info(mf, ' sigma = %g Optimized mu = %.12g entropy = %.12g', mf.sigma, mu, mf.entropy) return mo_occ_kpts
def get_veff(ks, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpts=None, kpts_band=None): '''Coulomb + XC functional for UKS. See pyscf/pbc/dft/uks.py :func:`get_veff` fore more details. ''' if cell is None: cell = ks.cell if dm is None: dm = ks.make_rdm1() if kpts is None: kpts = ks.kpts t0 = (time.clock(), time.time()) omega, alpha, hyb = ks._numint.rsh_and_hybrid_coeff(ks.xc, spin=cell.spin) hybrid = abs(hyb) > 1e-10 if not hybrid and isinstance(ks.with_df, multigrid.MultiGridFFTDF): n, exc, vxc = multigrid.nr_uks(ks.with_df, ks.xc, dm, hermi, kpts, kpts_band, with_j=True, return_j=False) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) return vxc # ndim = 4 : dm.shape = ([alpha,beta], nkpts, nao, nao) ground_state = (dm.ndim == 4 and dm.shape[0] == 2 and kpts_band is None) if ks.grids.non0tab is None: ks.grids.build(with_non0tab=True) if (isinstance(ks.grids, gen_grid.BeckeGrids) and ks.small_rho_cutoff > 1e-20 and ground_state): ks.grids = rks.prune_small_rho_grids_(ks, cell, dm, ks.grids, kpts) t0 = logger.timer(ks, 'setting up grids', *t0) if hermi == 2: # because rho = 0 n, exc, vxc = (0, 0), 0, 0 else: n, exc, vxc = ks._numint.nr_uks(cell, ks.grids, ks.xc, dm, 0, kpts, kpts_band) logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) weight = 1. / len(kpts) if not hybrid: vj = ks.get_j(cell, dm[0] + dm[1], hermi, kpts, kpts_band) vxc += vj else: if getattr(ks.with_df, '_j_only', False): # for GDF and MDF ks.with_df._j_only = False vj, vk = ks.get_jk(cell, dm, hermi, kpts, kpts_band) vj = vj[0] + vj[1] vxc += vj - vk * hyb if ground_state: exc -= (np.einsum('Kij,Kji', dm[0], vk[0]) + np.einsum( 'Kij,Kji', dm[1], vk[1])).real * hyb * .5 * weight if ground_state: ecoul = np.einsum('Kij,Kji', dm[0] + dm[1], vj).real * .5 * weight else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None) return vxc
def convert_to_uhf(mf, out=None, remove_df=False): '''Convert the given mean-field object to the unrestricted HF/KS object Note this conversion only changes the class of the mean-field object. The total energy and wave-function are the same as them in the input mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer will be discarded. Its underlying SCF object mf._scf will be converted. Args: mf : SCF object Kwargs remove_df : bool Whether to convert the DF-SCF object to the normal SCF object. This conversion is not applied by default. Returns: An unrestricted SCF object ''' from pyscf import scf from pyscf import dft from pyscf.soscf import newton_ah assert (isinstance(mf, hf.SCF)) logger.debug(mf, 'Converting %s to UHF', mf.__class__) def update_mo_(mf, mf1): if mf.mo_energy is not None: if isinstance(mf, scf.uhf.UHF): mf1.mo_occ = mf.mo_occ mf1.mo_coeff = mf.mo_coeff mf1.mo_energy = mf.mo_energy elif getattr(mf, 'kpts', None) is None: # UHF mf1.mo_occ = numpy.array((mf.mo_occ > 0, mf.mo_occ == 2), dtype=numpy.double) mf1.mo_energy = (mf.mo_energy, mf.mo_energy) mf1.mo_coeff = (mf.mo_coeff, mf.mo_coeff) else: # This to handle KRHF object mf1.mo_occ = ([ numpy.asarray(occ > 0, dtype=numpy.double) for occ in mf.mo_occ ], [ numpy.asarray(occ == 2, dtype=numpy.double) for occ in mf.mo_occ ]) mf1.mo_energy = (mf.mo_energy, mf.mo_energy) mf1.mo_coeff = (mf.mo_coeff, mf.mo_coeff) return mf1 if isinstance(mf, scf.ghf.GHF): raise NotImplementedError elif out is not None: assert (isinstance(out, scf.uhf.UHF)) out = _update_mf_without_soscf(mf, out, remove_df) elif isinstance(mf, scf.uhf.UHF): # Remove with_df for SOSCF method because the post-HF code checks the # attribute .with_df to identify whether an SCF object is DF-SCF method. # with_df in SOSCF is used in orbital hessian approximation only. For the # returned SCF object, whehter with_df exists in SOSCF has no effects on the # mean-field energy and other properties. if getattr(mf, '_scf', None): return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df) else: return copy.copy(mf) else: known_cls = { scf.hf.RHF: scf.uhf.UHF, scf.rohf.ROHF: scf.uhf.UHF, scf.hf_symm.RHF: scf.uhf_symm.UHF, scf.hf_symm.ROHF: scf.uhf_symm.UHF, dft.rks.RKS: dft.uks.UKS, dft.roks.ROKS: dft.uks.UKS, dft.rks_symm.RKS: dft.uks_symm.UKS, dft.rks_symm.ROKS: dft.uks_symm.UKS } out = _object_without_soscf(mf, known_cls, remove_df) return update_mo_(mf, out)
def convert_to_rhf(mf, out=None, remove_df=False): '''Convert the given mean-field object to the restricted HF/KS object Note this conversion only changes the class of the mean-field object. The total energy and wave-function are the same as them in the input mf object. If mf is an second order SCF (SOSCF) object, the SOSCF layer will be discarded. Its underlying SCF object mf._scf will be converted. Args: mf : SCF object Kwargs remove_df : bool Whether to convert the DF-SCF object to the normal SCF object. This conversion is not applied by default. Returns: An unrestricted SCF object ''' from pyscf import scf from pyscf import dft from pyscf.soscf import newton_ah assert (isinstance(mf, hf.SCF)) logger.debug(mf, 'Converting %s to RHF', mf.__class__) def update_mo_(mf, mf1): if mf.mo_energy is not None: if isinstance(mf, scf.hf.RHF): # RHF/ROHF/KRHF/KROHF mf1.mo_occ = mf.mo_occ mf1.mo_coeff = mf.mo_coeff mf1.mo_energy = mf.mo_energy elif getattr(mf, 'kpts', None) is None: # UHF mf1.mo_occ = mf.mo_occ[0] + mf.mo_occ[1] mf1.mo_energy = mf.mo_energy[0] mf1.mo_coeff = mf.mo_coeff[0] if getattr(mf.mo_coeff[0], 'orbsym', None) is not None: mf1.mo_coeff = lib.tag_array(mf1.mo_coeff, orbsym=mf.mo_coeff[0].orbsym) else: # KUHF mf1.mo_occ = [occa + occb for occa, occb in zip(*mf.mo_occ)] mf1.mo_energy = mf.mo_energy[0] mf1.mo_coeff = mf.mo_coeff[0] return mf1 if getattr(mf, 'nelec', None) is None: nelec = mf.mol.nelec else: nelec = mf.nelec if isinstance(mf, scf.ghf.GHF): raise NotImplementedError elif out is not None: assert (isinstance(out, scf.hf.RHF)) out = _update_mf_without_soscf(mf, out, remove_df) elif (isinstance(mf, scf.hf.RHF) or (nelec[0] != nelec[1] and isinstance(mf, scf.rohf.ROHF))): if getattr(mf, '_scf', None): return _update_mf_without_soscf(mf, copy.copy(mf._scf), remove_df) else: return copy.copy(mf) else: if nelec[0] == nelec[1]: known_cls = { scf.uhf.UHF: scf.hf.RHF, scf.uhf_symm.UHF: scf.hf_symm.RHF, dft.uks.UKS: dft.rks.RKS, dft.uks_symm.UKS: dft.rks_symm.RKS, scf.rohf.ROHF: scf.hf.RHF, scf.hf_symm.ROHF: scf.hf_symm.RHF, dft.roks.ROKS: dft.rks.RKS, dft.rks_symm.ROKS: dft.rks_symm.RKS } else: known_cls = { scf.uhf.UHF: scf.rohf.ROHF, scf.uhf_symm.UHF: scf.hf_symm.ROHF, dft.uks.UKS: dft.roks.ROKS, dft.uks_symm.UKS: dft.rks_symm.ROKS } out = _object_without_soscf(mf, known_cls, remove_df) return update_mo_(mf, out)
def DMRG_COMPRESS_NEVPT(mc, maxM=500, root=0, nevptsolver=None, tol=1e-7, nevpt_integral=None): if isinstance(nevpt_integral, str) and h5py.is_hdf5(nevpt_integral): nevpt_integral_file = os.path.abspath(nevpt_integral) fh5 = h5py.File(nevpt_integral_file, 'r') nelecas = fh5['mc/nelecas'][()] nroots = fh5['mc/nroots'][()] wfnsym = fh5['mc/wfnsym'][()] fh5.close() else : nelecas = mc.nelecas nroots = mc.fcisolver.nroots wfnsym = mc.fcisolver.wfnsym nevpt_integral_file = None if nevptsolver is None: nevptsolver = default_nevpt_schedule(mc.fcisolver, maxM, tol) #nevptsolver.__dict__.update(mc.fcisolver.__dict__) nevptsolver.wfnsym = wfnsym nevptsolver.block_extra_keyword = mc.fcisolver.block_extra_keyword nevptsolver.nroots = nroots nevptsolver.executable = settings.BLOCKEXE_COMPRESS_NEVPT if nevptsolver.executable == getattr(mc.fcisolver, 'executable', None): logger.warn(mc, 'DMRG executable file for nevptsolver is the same ' 'to the executable file for DMRG solver. If they are ' 'both compiled by MPI compilers, they may cause error or ' 'random results in DMRG-NEVPT calculation.') nevpt_scratch = os.path.abspath(nevptsolver.scratchDirectory) dmrg_scratch = os.path.abspath(mc.fcisolver.scratchDirectory) # Integrals are not given by the kwarg nevpt_integral if nevpt_integral_file is None: nevpt_integral_file = os.path.join(nevpt_scratch, 'nevpt_perturb_integral') write_chk(mc, root, nevpt_integral_file) conf = dmrgci.writeDMRGConfFile(nevptsolver, nelecas, False, with_2pdm=False, extraline=['fullrestart','nevpt_state_num %d'%root]) with open(conf, 'r') as f: block_conf = f.readlines() block_conf = [l for l in block_conf if 'prefix' not in l] block_conf = ''.join(block_conf) with h5py.File(nevpt_integral_file, 'a') as fh5: if 'dmrg.conf' in fh5: del(fh5['dmrg.conf']) fh5['dmrg.conf'] = block_conf if nevptsolver.verbose >= logger.DEBUG1: logger.debug1(nevptsolver, 'Block Input conf') logger.debug1(nevptsolver, block_conf) t0 = (time.clock(), time.time()) # function nevpt_integral_mpi is called in this cmd cmd = ' '.join((nevptsolver.mpiprefix, os.path.realpath(os.path.join(__file__, '..', 'nevpt_mpi.py')), nevpt_integral_file, nevptsolver.executable, dmrg_scratch, nevpt_scratch)) logger.debug(nevptsolver, 'DMRG_COMPRESS_NEVPT cmd %s', cmd) try: subprocess.check_call(cmd, shell=True) except subprocess.CalledProcessError as err: logger.error(nevptsolver, cmd) raise err if nevptsolver.verbose >= logger.DEBUG1: logger.debug1(nevptsolver, open(os.path.join(nevpt_scratch, 'nevpt2_0', 'dmrg.out')).read()) perturb_file = os.path.join(nevpt_scratch, 'Perturbation_%d'%root) fh5 = h5py.File(perturb_file, 'r') Vi_e = fh5['Vi/energy'][()] Vr_e = fh5['Vr/energy'][()] fh5.close() logger.note(nevptsolver,'Nevpt Energy:') logger.note(nevptsolver,'Sr Subspace: E = %.14f'%( Vr_e)) logger.note(nevptsolver,'Si Subspace: E = %.14f'%( Vi_e)) logger.timer(nevptsolver,'MPS NEVPT calculation time', *t0) return perturb_file
def get_occ(mo_energy_kpts=None, mo_coeff_kpts=None): '''Label the occupancies for each orbital for sampled k-points. This is a k-point version of scf.hf.SCF.get_occ ''' mo_occ_kpts = mf_class.get_occ(mf, mo_energy_kpts, mo_coeff_kpts) if mf.sigma is None or mf.sigma == 0: return mo_occ_kpts if is_uhf: nkpts = len(mo_energy_kpts[0]) nocc = mf.cell.nelectron * nkpts else: nkpts = len(mo_energy_kpts) nocc = (mf.cell.nelectron * nkpts) // 2 mo_energy = numpy.sort(mo_energy_kpts.ravel()) fermi = mo_energy[nocc - 1] if method.lower() == 'fermi': # Fermi-Dirac smearing # Optimize mu to give correct electron number def fermi_occ(m): occ = numpy.zeros_like(mo_energy_kpts) de = (mo_energy_kpts - m) / mf.sigma occ[de < 40] = 1. / (numpy.exp(de[de < 40]) + 1.) return occ def nelec_cost_fn(m): mo_occ_kpts = fermi_occ(m) if not is_uhf: mo_occ_kpts *= 2 return (mo_occ_kpts.sum() / nkpts - mf.cell.nelectron)**2 res = scipy.optimize.minimize(nelec_cost_fn, fermi, method='Powell') mu = res.x mo_occ_kpts = f = fermi_occ(mu) f = f[(f > 0) & (f < 1)] mf.entropy = -(f * numpy.log(f) + (1 - f) * numpy.log(1 - f)).sum() if not is_uhf: mo_occ_kpts *= 2 mf.entropy *= 2 else: # Gaussian smearing def nelec_cost_fn(m): mo_occ_kpts = 1 - scipy.special.erf( (mo_energy_kpts - m) / mf.sigma) if is_uhf: mo_occ_kpts *= .5 return (mo_occ_kpts.sum() / nkpts - mf.cell.nelectron)**2 res = scipy.optimize.minimize(nelec_cost_fn, fermi, method='Powell') mu = res.x mo_occ_kpts = 1 - scipy.special.erf( (mo_energy_kpts - mu) / mf.sigma) if is_uhf: mo_occ_kpts *= .5 # Is the entropy correct for spin unrestricted case? mf.entropy = numpy.exp(-( (mo_energy_kpts - mu) / mf.sigma)**2).sum() / numpy.sqrt( numpy.pi) logger.debug(mf, ' Sum mo_occ_kpts = %s should equal nelec = %s', mo_occ_kpts.sum() / nkpts, mf.cell.nelectron) logger.info(mf, ' sigma = %g Optimized mu = %.12g entropy = %.12g', mf.sigma, mu, mf.entropy) return mo_occ_kpts