def ccsd(self, t1=None, t2=None, eris=None, **kwargs): if eris is None: eris = self.ao2mo(self.mo_coeff) e_corr, self.t1, self.t2 = ccsd.CCSD.ccsd(self, t1, t2, eris) if getattr(eris, 'orbspin', None) is not None: self.t1 = lib.tag_array(self.t1, orbspin=eris.orbspin) self.t2 = lib.tag_array(self.t2, orbspin=eris.orbspin) return e_corr, self.t1, self.t2
def _common_init_(self, mycc, mo_coeff=None): if mo_coeff is None: mo_coeff = mycc.mo_coeff mo_idx = ccsd.get_frozen_mask(mycc) if getattr(mo_coeff, 'orbspin', None) is not None: self.orbspin = mo_coeff.orbspin[mo_idx] mo_coeff = lib.tag_array(mo_coeff[:,mo_idx], orbspin=self.orbspin) self.mo_coeff = mo_coeff else: orbspin = scf.ghf.guess_orbspin(mo_coeff) self.mo_coeff = mo_coeff = mo_coeff[:,mo_idx] if not np.any(orbspin == -1): self.orbspin = orbspin[mo_idx] self.mo_coeff = lib.tag_array(mo_coeff, orbspin=self.orbspin) # Note: Recomputed fock matrix since SCF may not be fully converged. dm = mycc._scf.make_rdm1(mycc.mo_coeff, mycc.mo_occ) fockao = mycc._scf.get_fock(dm=dm) self.fock = reduce(np.dot, (mo_coeff.conj().T, fockao, mo_coeff)) self.nocc = mycc.nocc mo_e = self.mo_energy = self.fock.diagonal().real gap = abs(mo_e[:self.nocc,None] - mo_e[None,self.nocc:]).min() if gap < 1e-5: logger.warn(mycc, 'H**O-LUMO gap %s too small for GCCSD', gap) return self
def ccsd(self, t1=None, t2=None, eris=None, mbpt2=False): '''Ground-state unrestricted (U)CCSD. Kwargs: mbpt2 : bool Use one-shot MBPT2 approximation to CCSD. ''' if mbpt2: from pyscf.mp import gmp2 pt = gmp2.GMP2(self._scf, self.frozen, self.mo_coeff, self.mo_occ) self.e_corr, self.t2 = pt.kernel(eris=eris) nocc, nvir = self.t2.shape[1:3] self.t1 = np.zeros((nocc,nvir)) return self.e_corr, self.t1, self.t2 # Initialize orbspin so that we can attach it to t1, t2 if getattr(self.mo_coeff, 'orbspin', None) is None: orbspin = scf.ghf.guess_orbspin(self.mo_coeff) if not np.any(orbspin == -1): self.mo_coeff = lib.tag_array(self.mo_coeff, orbspin=orbspin) if eris is None: eris = self.ao2mo(self.mo_coeff) e_corr, self.t1, self.t2 = ccsd.CCSD.ccsd(self, t1, t2, eris) if getattr(eris, 'orbspin', None) is not None: self.t1 = lib.tag_array(self.t1, orbspin=eris.orbspin) self.t2 = lib.tag_array(self.t2, orbspin=eris.orbspin) return e_corr, self.t1, self.t2
def _finalize(self): uhf.UHF._finalize(self) ea = numpy.hstack(self.mo_energy[0]) eb = numpy.hstack(self.mo_energy[1]) # Using mergesort because it is stable. We don't want to change the # ordering of the symmetry labels when two orbitals are degenerated. oa_sort = numpy.argsort(ea[self.mo_occ[0]>0 ].round(9), kind='mergesort') va_sort = numpy.argsort(ea[self.mo_occ[0]==0].round(9), kind='mergesort') ob_sort = numpy.argsort(eb[self.mo_occ[1]>0 ].round(9), kind='mergesort') vb_sort = numpy.argsort(eb[self.mo_occ[1]==0].round(9), kind='mergesort') idxa = numpy.arange(ea.size) idxa = numpy.hstack((idxa[self.mo_occ[0]> 0][oa_sort], idxa[self.mo_occ[0]==0][va_sort])) idxb = numpy.arange(eb.size) idxb = numpy.hstack((idxb[self.mo_occ[1]> 0][ob_sort], idxb[self.mo_occ[1]==0][vb_sort])) self.mo_energy = (ea[idxa], eb[idxb]) orbsyma, orbsymb = get_orbsym(self.mol, self.mo_coeff) self.mo_coeff = (lib.tag_array(self.mo_coeff[0][:,idxa], orbsym=orbsyma[idxa]), lib.tag_array(self.mo_coeff[1][:,idxb], orbsym=orbsymb[idxb])) self.mo_occ = (self.mo_occ[0][idxa], self.mo_occ[1][idxb]) if self.chkfile: chkfile.dump_scf(self.mol, self.chkfile, self.e_tot, self.mo_energy, self.mo_coeff, self.mo_occ, overwrite_mol=False) return self
def eig(self, h, s): mol = self.mol if not mol.symmetry: return self._eigh(h, s) nirrep = mol.symm_orb.__len__() s = symm.symmetrize_matrix(s, mol.symm_orb) ha = symm.symmetrize_matrix(h[0], mol.symm_orb) cs = [] es = [] orbsym = [] for ir in range(nirrep): e, c = self._eigh(ha[ir], s[ir]) cs.append(c) es.append(e) orbsym.append([mol.irrep_id[ir]] * e.size) ea = numpy.hstack(es) ca = hf_symm.so2ao_mo_coeff(mol.symm_orb, cs) ca = lib.tag_array(ca, orbsym=numpy.hstack(orbsym)) hb = symm.symmetrize_matrix(h[1], mol.symm_orb) cs = [] es = [] orbsym = [] for ir in range(nirrep): e, c = self._eigh(hb[ir], s[ir]) cs.append(c) es.append(e) orbsym.append([mol.irrep_id[ir]] * e.size) eb = numpy.hstack(es) cb = hf_symm.so2ao_mo_coeff(mol.symm_orb, cs) cb = lib.tag_array(cb, orbsym=numpy.hstack(orbsym)) return (ea,eb), (ca,cb)
def spatial2spin(tx, orbspin=None): '''Convert T1/T2 of spatial orbital representation to T1/T2 of spin-orbital representation ''' if isinstance(tx, numpy.ndarray) and tx.ndim == 2: # RCCSD t1 amplitudes return spatial2spin((tx,tx), orbspin) elif isinstance(tx, numpy.ndarray) and tx.ndim == 4: # RCCSD t2 amplitudes t2aa = tx - tx.transpose(0,1,3,2) return spatial2spin((t2aa,tx,t2aa), orbspin) elif len(tx) == 2: # t1 t1a, t1b = tx nocc_a, nvir_a = t1a.shape nocc_b, nvir_b = t1b.shape else: t2aa, t2ab, t2bb = tx nocc_a, nocc_b, nvir_a, nvir_b = t2ab.shape if orbspin is None: orbspin = numpy.zeros((nocc_a+nvir_a)*2, dtype=int) orbspin[1::2] = 1 nocc = nocc_a + nocc_b nvir = nvir_a + nvir_b idxoa = numpy.where(orbspin[:nocc] == 0)[0] idxob = numpy.where(orbspin[:nocc] == 1)[0] idxva = numpy.where(orbspin[nocc:] == 0)[0] idxvb = numpy.where(orbspin[nocc:] == 1)[0] if len(tx) == 2: # t1 t1 = numpy.zeros((nocc,nvir), dtype=t1a.dtype) lib.takebak_2d(t1, t1a, idxoa, idxva) lib.takebak_2d(t1, t1b, idxob, idxvb) t1 = lib.tag_array(t1, orbspin=orbspin) return t1 else: t2 = numpy.zeros((nocc**2,nvir**2), dtype=t2aa.dtype) idxoaa = idxoa[:,None] * nocc + idxoa idxoab = idxoa[:,None] * nocc + idxob idxoba = idxob[:,None] * nocc + idxoa idxobb = idxob[:,None] * nocc + idxob idxvaa = idxva[:,None] * nvir + idxva idxvab = idxva[:,None] * nvir + idxvb idxvba = idxvb[:,None] * nvir + idxva idxvbb = idxvb[:,None] * nvir + idxvb t2aa = t2aa.reshape(nocc_a*nocc_a,nvir_a*nvir_a) t2ab = t2ab.reshape(nocc_a*nocc_b,nvir_a*nvir_b) t2bb = t2bb.reshape(nocc_b*nocc_b,nvir_b*nvir_b) lib.takebak_2d(t2, t2aa, idxoaa.ravel() , idxvaa.ravel() ) lib.takebak_2d(t2, t2bb, idxobb.ravel() , idxvbb.ravel() ) lib.takebak_2d(t2, t2ab, idxoab.ravel() , idxvab.ravel() ) lib.takebak_2d(t2, t2ab, idxoba.T.ravel(), idxvba.T.ravel()) abba = -t2ab lib.takebak_2d(t2, abba, idxoab.ravel() , idxvba.T.ravel()) lib.takebak_2d(t2, abba, idxoba.T.ravel(), idxvab.ravel() ) t2 = lib.tag_array(t2, orbspin=orbspin) return t2.reshape(nocc,nocc,nvir,nvir)
def __init__(self, mp, mo_coeff=None): self.orbspin = None if mo_coeff is None: mo_coeff = mp.mo_coeff mo_idx = mp.get_frozen_mask() if getattr(mo_coeff, 'orbspin', None) is not None: self.orbspin = mo_coeff.orbspin[mo_idx] mo_coeff = lib.tag_array(mo_coeff[:,mo_idx], orbspin=self.orbspin) self.mo_coeff = mo_coeff else: orbspin = scf.ghf.guess_orbspin(mo_coeff) self.mo_coeff = mo_coeff = mo_coeff[:,mo_idx] if not numpy.any(orbspin == -1): self.orbspin = orbspin[mo_idx] self.mo_coeff = lib.tag_array(mo_coeff, orbspin=self.orbspin)
def _make_eris_incore(mp, mo_coeff=None, ao2mofn=None, verbose=None): eris = _PhysicistsERIs(mp, mo_coeff) nocc = mp.nocc nao, nmo = eris.mo_coeff.shape nvir = nmo - nocc orbspin = eris.orbspin if callable(ao2mofn): orbo = eris.mo_coeff[:,:nocc] orbv = eris.mo_coeff[:,nocc:] orbo = lib.tag_array(orbo, orbspin=orbspin) eri = ao2mofn((orbo,orbv,orbo,orbv)).reshape(nocc,nvir,nocc,nvir) else: orboa = eris.mo_coeff[:nao//2,:nocc] orbob = eris.mo_coeff[nao//2:,:nocc] orbva = eris.mo_coeff[:nao//2,nocc:] orbvb = eris.mo_coeff[nao//2:,nocc:] if orbspin is None: eri = ao2mo.kernel(mp._scf._eri, (orboa,orbva,orboa,orbva)) eri += ao2mo.kernel(mp._scf._eri, (orbob,orbvb,orbob,orbvb)) eri1 = ao2mo.kernel(mp._scf._eri, (orboa,orbva,orbob,orbvb)) eri += eri1 eri += eri1.T eri = eri.reshape(nocc,nvir,nocc,nvir) else: co = orboa + orbob cv = orbva + orbvb eri = ao2mo.kernel(mp._scf._eri, (co,cv,co,cv)).reshape(nocc,nvir,nocc,nvir) sym_forbid = (orbspin[:nocc,None] != orbspin[nocc:]) eri[sym_forbid,:,:] = 0 eri[:,:,sym_forbid] = 0 eris.oovv = eri.transpose(0,2,1,3) - eri.transpose(0,2,3,1) return eris
def test_uhf_symm_get_occ(self): pmol = n2sym.copy() pmol.spin = 2 mf = scf.UHF(pmol).set(verbose = 0) orbsym = numpy.array([[0 , 5 , 0 , 5 , 6 , 7 , 0 , 2 , 3 , 5 , 0 , 6 , 7 , 0 , 2 , 3 , 5 , 10, 11, 5], [5 , 0 , 6 , 7 , 5 , 10, 11, 0 , 5 , 0 , 5 , 5 , 6 , 7 , 0 , 2 , 3 , 0 , 2 , 3]]) energy = numpy.array([[34, 2 , 54, 43, 42, 33, 20, 61, 29, 26, 62, 52, 13, 51, 18, 78, 85, 49, 84, 7], [29, 26, 13, 54, 18, 78, 85, 49, 84, 62, 42, 74, 20, 61, 51, 34, 2 , 33, 52, 3]]) mf.irrep_nelec = {'A1g':7, 'A1u':3, 'E1ux':2, 'E1uy':2} mo_coeff = lib.tag_array([numpy.eye(energy.size)]*2, orbsym=orbsym) self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [[1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1], [0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]])) mf.irrep_nelec = {'A1g':(5,2), 'A1u':(1,2)} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [[1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1]])) mf.irrep_nelec = {'E1ux':2, 'E1uy':2} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [[0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1], [0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]])) mf.irrep_nelec = {} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [[0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1], [0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1]]))
def bcast_tagged_array(arr): '''Broadcast big nparray or tagged array.''' if comm.bcast(not isinstance(arr, numpy.ndarray)): return comm.bcast(arr) new_arr = bcast(arr) if comm.bcast(isinstance(arr, lib.NPArrayWithTag)): new_arr = lib.tag_array(new_arr) if rank == 0: kv = [] for k, v in arr.__dict__.items(): if isinstance(v, numpy.ndarray) and v.nbytes > 1e5: kv.append((k, 'NPARRAY_TO_BCAST')) else: kv.append((k, v)) comm.bcast(kv) else: kv = comm.bcast(None) new_arr.__dict__.update(kv) for k, v in kv: if v is 'NPARRAY_TO_BCAST': new_arr.k = bcast(v) if rank != 0: arr = new_arr return arr
def rotate_mo(self, mo_coeff, u, log=None): mo = numpy.asarray((numpy.dot(mo_coeff[0], u[0]), numpy.dot(mo_coeff[1], u[1]))) if self._scf.mol.symmetry: orbsym = uhf_symm.get_orbsym(self._scf.mol, mo_coeff) mo = lib.tag_array(mo, orbsym=orbsym) return mo
def eig(self, fock, s): e, c = self._eigh(fock, s) if getattr(fock, 'focka', None) is not None: mo_ea = numpy.einsum('pi,pi->i', c.conj(), fock.focka.dot(c)).real mo_eb = numpy.einsum('pi,pi->i', c.conj(), fock.fockb.dot(c)).real e = lib.tag_array(e, mo_ea=mo_ea, mo_eb=mo_eb) return e, c
def get_roothaan_fock(focka_fockb, dma_dmb, s): '''Roothaan's effective fock. Ref. http://www-theor.ch.cam.ac.uk/people/ross/thesis/node15.html ======== ======== ====== ========= space closed open virtual ======== ======== ====== ========= closed Fc Fb Fc open Fb Fc Fa virtual Fc Fa Fc ======== ======== ====== ========= where Fc = (Fa + Fb) / 2 Returns: Roothaan effective Fock matrix ''' nao = s.shape[0] focka, fockb = focka_fockb dma, dmb = dma_dmb fc = (focka + fockb) * .5 # Projector for core, open-shell, and virtual pc = numpy.dot(dmb, s) po = numpy.dot(dma-dmb, s) pv = numpy.eye(nao) - numpy.dot(dma, s) fock = reduce(numpy.dot, (pc.conj().T, fc, pc)) * .5 fock += reduce(numpy.dot, (po.conj().T, fc, po)) * .5 fock += reduce(numpy.dot, (pv.conj().T, fc, pv)) * .5 fock += reduce(numpy.dot, (po.conj().T, fockb, pc)) fock += reduce(numpy.dot, (po.conj().T, focka, pv)) fock += reduce(numpy.dot, (pv.conj().T, fc, pc)) fock = fock + fock.conj().T fock = lib.tag_array(fock, focka=focka, fockb=fockb) return fock
def test_canonicalize1(self): numpy.random.seed(1) f1 = numpy.random.random(mcr.mo_coeff.shape) u1 = numpy.linalg.svd(f1)[0] mo1 = numpy.dot(mcr.mo_coeff, u1) mo1 = lib.tag_array(mo1, orbsym=mcr.mo_coeff.orbsym) mo, ci, mo_e = mcr.canonicalize(mo1) e1 = numpy.einsum('ji,jk,ki', mo, f1, mo) self.assertAlmostEqual(e1, 44.2658681077, 7) self.assertAlmostEqual(lib.finger(mo_e), 5.1364166175063097, 7) mo, ci, mo_e = mcr.canonicalize(mo1, eris=mcr.ao2mo(mcr.mo_coeff)) e1 = numpy.einsum('ji,jk,ki', mo, f1, mo) self.assertAlmostEqual(e1, 44.2658681077, 7) self.assertAlmostEqual(lib.finger(mo_e), 4.1206025804989173, 7) mcr1 = copy.copy(mcr) mcr1.frozen = 2 mo, ci, mo_e = mcr1.canonicalize(mo1) self.assertAlmostEqual(lib.finger(mo_e), 6.6030999409178577, 7) mcr1.frozen = [0,1] mo, ci, mo_e = mcr1.canonicalize(mo1) self.assertAlmostEqual(lib.finger(mo_e), 6.6030999409178577, 7) mcr1.frozen = [1,12] mo, ci, mo_e = mcr1.canonicalize(mo1) self.assertAlmostEqual(lib.finger(mo_e), 5.2182584355788162, 7)
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): if getattr(dm, 'mo_coeff', None) is not None: mo_coeff = dm.mo_coeff mo_occ_a = (dm.mo_occ > 0).astype(numpy.double) mo_occ_b = (dm.mo_occ ==2).astype(numpy.double) dm = lib.tag_array(dm, mo_coeff=(mo_coeff,mo_coeff), mo_occ=(mo_occ_a,mo_occ_b)) return uks.get_veff(ks, mol, dm, dm_last, vhf_last, hermi)
def get_veff(ks, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpts=None, kpts_band=None): if getattr(dm, 'mo_coeff', None) is not None: mo_coeff = dm.mo_coeff mo_occ_a = [(x > 0).astype(np.double) for x in dm.mo_occ] mo_occ_b = [(x ==2).astype(np.double) for x in dm.mo_occ] dm = lib.tag_array(dm, mo_coeff=(mo_coeff,mo_coeff), mo_occ=(mo_occ_a,mo_occ_b)) return kuks.get_veff(ks, cell, dm, dm_last, vhf_last, hermi, kpts, kpts_band)
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
def get_veff(self, mol=None, dm=None, *args, **kwargs): vhf = oldMF.get_veff(self, mol, dm, *args, **kwargs) with_solvent = self.with_solvent if not with_solvent.frozen: with_solvent.epcm, with_solvent.vpcm = with_solvent.kernel(dm) epcm, vpcm = with_solvent.epcm, with_solvent.vpcm # NOTE: vpcm should not be added to vhf in this place. This is # because vhf is used as the reference for direct_scf in the next # iteration. If vpcm is added here, it may break direct SCF. return lib.tag_array(vhf, epcm=epcm, vpcm=vpcm)
def test_rdm_complex(self): mol = gto.M() mol.verbose = 0 nocc = 6 nvir = 8 mf = scf.GHF(mol) nmo = nocc + nvir numpy.random.seed(1) eri = (numpy.random.random((nmo,nmo,nmo,nmo)) + numpy.random.random((nmo,nmo,nmo,nmo))* 1j - (.5+.5j)) eri = eri + eri.transpose(1,0,3,2).conj() eri = eri + eri.transpose(2,3,0,1) eri *= .1 def get_jk(mol, dm, *args,**kwargs): vj = numpy.einsum('ijkl,lk->ij', eri, dm) vk = numpy.einsum('ijkl,jk->il', eri, dm) return vj, vk def get_veff(mol, dm, *args, **kwargs): vj, vk = get_jk(mol, dm) return vj - vk def ao2mofn(mos): return eri mf.get_jk = get_jk mf.get_veff = get_veff hcore = numpy.random.random((nmo,nmo)) * .2 + numpy.random.random((nmo,nmo))* .2j hcore = hcore + hcore.T.conj() + numpy.diag(range(nmo))*2 mf.get_hcore = lambda *args: hcore mf.get_ovlp = lambda *args: numpy.eye(nmo) orbspin = numpy.zeros(nmo, dtype=int) orbspin[1::2] = 1 mf.mo_coeff = lib.tag_array(numpy.eye(nmo) + 0j, orbspin=orbspin) mf.mo_occ = numpy.zeros(nmo) mf.mo_occ[:nocc] = 1 mf.e_tot = mf.energy_elec(mf.make_rdm1(), hcore)[0] mycc = cc.GCCSD(mf) eris = gccsd._make_eris_incore(mycc, mf.mo_coeff, ao2mofn) mycc.ao2mo = lambda *args, **kwargs: eris mycc.kernel(eris=eris) mycc.solve_lambda(eris=eris) dm1 = mycc.make_rdm1() dm2 = mycc.make_rdm2() e1 = numpy.einsum('ij,ji', hcore, dm1) e1+= numpy.einsum('ijkl,ijkl', eri, dm2) * .5 self.assertAlmostEqual(e1, mycc.e_tot, 7) self.assertAlmostEqual(abs(dm2-dm2.transpose(1,0,3,2).conj()).max(), 0, 9) self.assertAlmostEqual(abs(dm2-dm2.transpose(2,3,0,1) ).max(), 0, 9) self.assertAlmostEqual(abs(dm2+dm2.transpose(2,1,0,3) ).max(), 0, 9) self.assertAlmostEqual(abs(dm2+dm2.transpose(0,3,2,1) ).max(), 0, 9)
def canonicalize(mf, mo_coeff, mo_occ, fock=None): '''Canonicalization diagonalizes the Fock matrix within occupied, open, virtual subspaces separatedly (without change occupancy). ''' if getattr(fock, 'focka', None) is None: dm = mf.make_rdm1(mo_coeff, mo_occ) fock = mf.get_fock(dm=dm) mo_e, mo_coeff = hf.canonicalize(mf, mo_coeff, mo_occ, fock) fa, fb = fock.focka, fock.fockb mo_ea = numpy.einsum('pi,pi->i', mo_coeff.conj(), fa.dot(mo_coeff)).real mo_eb = numpy.einsum('pi,pi->i', mo_coeff.conj(), fb.dot(mo_coeff)).real mo_e = lib.tag_array(mo_e, mo_ea=mo_ea, mo_eb=mo_eb) return mo_e, mo_coeff
def test_multigrid_roks(self): mf = dft.ROKS(cell_he) mf.xc = 'lda,' mo = dm_he[0] nao = cell_he.nao mo_occ = numpy.ones(nao) dm1 = numpy.einsum('pi,i,qi->pq', mo, mo_occ, mo) dm1 = lib.tag_array(numpy.array([dm1,dm1]), mo_coeff=mo, mo_occ=mo_occ*2) ref = mf.get_veff(cell_he, dm1) out = multigrid.multigrid(mf).get_veff(cell_he, dm1) self.assertAlmostEqual(float(abs(ref-out).max()), 0, 9) self.assertAlmostEqual(abs(ref.exc-out.exc).max(), 0, 9) self.assertAlmostEqual(abs(ref.ecoul-out.ecoul).max(), 0, 8)
def test_multigrid_kroks(self): mf = dft.KROKS(cell_he) mf.xc = 'lda,' nao = cell_he.nao mo = dm_he mo_occ = numpy.ones((2,nao)) dm1 = numpy.einsum('kpi,ki,kqi->kpq', mo, mo_occ, mo) dm1 = lib.tag_array(numpy.array([dm1,dm1]), mo_coeff=mo, mo_occ=mo_occ*2) ref = mf.get_veff(cell_he, dm1, kpts=kpts) out = multigrid.multigrid(mf).get_veff(cell_he, dm1, kpts=kpts) self.assertAlmostEqual(float(abs(ref-out).max()), 0, 9) self.assertAlmostEqual(abs(ref.exc-out.exc).max(), 0, 9) self.assertAlmostEqual(abs(ref.ecoul-out.ecoul).max(), 0, 9)
def get_veff(self, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpt=None, kpts_band=None): if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() if kpt is None: kpt = self.kpt if isinstance(dm, np.ndarray) and dm.ndim == 2: dm = np.asarray((dm*.5,dm*.5)) if getattr(dm, 'mo_coeff', None) is not None: mo_coeff = dm.mo_coeff mo_occ_a = (dm.mo_occ > 0).astype(np.double) mo_occ_b = (dm.mo_occ ==2).astype(np.double) dm = lib.tag_array(dm, mo_coeff=(mo_coeff,mo_coeff), mo_occ=(mo_occ_a,mo_occ_b)) vj, vk = self.get_jk(cell, dm, hermi, kpt, kpts_band) vhf = vj[0] + vj[1] - vk return vhf
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
def get_fock(nmrobj, dm0=None, gauge_orig=None): '''First order Fock matrix wrt external magnetic field''' if dm0 is None: dm0 = nmrobj._scf.make_rdm1() if gauge_orig is None: gauge_orig = nmrobj.gauge_orig mol = nmrobj.mol if gauge_orig is None: log = logger.Logger(nmrobj.stdout, nmrobj.verbose) log.debug('First-order GIAO Fock matrix') mf = nmrobj._scf ni = mf._numint omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory*.9-mem_now) # Attach mo_coeff and mo_occ to improve get_vxc_giao efficiency dm0 = lib.tag_array(dm0, mo_coeff=mf.mo_coeff, mo_occ=mf.mo_occ) h1 = -get_vxc_giao(ni, mol, mf.grids, mf.xc, dm0, max_memory=max_memory, verbose=nmrobj.verbose) intor = mol._add_suffix('int2e_ig1') if abs(hyb) > 1e-10: vj, vk = rhf_nmr.get_jk(mol, dm0) h1 += vj[0] + vj[1] - hyb * vk if abs(omega) > 1e-10: with mol.with_range_coulomb(omega): h1 -= (alpha-hyb) * rhf_nmr.get_jk(mol, dm0)[1] else: vj = _vhf.direct_mapdm(intor, 'a4ij', 'lk->s1ij', dm0, 3, mol._atm, mol._bas, mol._env) h1 -= vj[0] + vj[1] h1 -= .5 * mol.intor('int1e_giao_irjxp', 3) h1 -= mol.intor_asymmetric('int1e_ignuc', 3) if mol.has_ecp(): h1 -= mol.intor_asymmetric('ECPscalar_ignuc', 3) h1 -= mol.intor('int1e_igkin', 3) else: with mol.with_common_origin(gauge_orig): h1 = -.5 * mol.intor('int1e_cg_irxp', 3) h1 = (h1, h1) if nmrobj.chkfile: lib.chkfile.dump(nmrobj.chkfile, 'nmr/h1', h1) return h1
def update_mo_(mf, mf1): _keys = mf._keys.union(mf1._keys) mf1.__dict__.update(mf.__dict__) mf1._keys = _keys if mf.mo_energy is not None: mf1.mo_energy = [] mf1.mo_occ = [] mf1.mo_coeff = [] nkpts = len(mf.kpts) is_rhf = isinstance(mf, scf.hf.RHF) for k in range(nkpts): if is_rhf: mo_a = mo_b = mf.mo_coeff[k] ea = eb = mf.mo_energy[k] occa = mf.mo_occ[k] > 0 occb = mf.mo_occ[k] == 2 orbspin = mol_addons.get_ghf_orbspin(ea, mf.mo_occ[k], True) else: mo_a = mf.mo_coeff[0][k] mo_b = mf.mo_coeff[1][k] ea = mf.mo_energy[0][k] eb = mf.mo_energy[1][k] occa = mf.mo_occ[0][k] occb = mf.mo_occ[1][k] orbspin = mol_addons.get_ghf_orbspin((ea, eb), (occa, occb), False) nao, nmo = mo_a.shape mo_energy = numpy.empty(nmo*2) mo_energy[orbspin==0] = ea mo_energy[orbspin==1] = eb mo_occ = numpy.empty(nmo*2) mo_occ[orbspin==0] = occa mo_occ[orbspin==1] = occb mo_coeff = numpy.zeros((nao*2,nmo*2), dtype=mo_a.dtype) mo_coeff[:nao,orbspin==0] = mo_a mo_coeff[nao:,orbspin==1] = mo_b mo_coeff = lib.tag_array(mo_coeff, orbspin=orbspin) mf1.mo_energy.append(mo_energy) mf1.mo_occ.append(mo_occ) mf1.mo_coeff.append(mo_coeff) return mf1
def get_veff(ks_grad, mol=None, dm=None): '''Coulomb + XC functional ''' if mol is None: mol = ks_grad.mol if dm is None: dm = ks_grad.base.make_rdm1() t0 = (time.clock(), time.time()) mf = ks_grad.base ni = mf._numint if ks_grad.grids is not None: grids = ks_grad.grids else: grids = mf.grids if grids.coords is None: grids.build(with_non0tab=True) if mf.nlc != '': raise NotImplementedError #enabling range-separated hybrids omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) mem_now = lib.current_memory()[0] max_memory = max(2000, ks_grad.max_memory*.9-mem_now) if ks_grad.grid_response: exc, vxc = get_vxc_full_response(ni, mol, grids, mf.xc, dm, max_memory=max_memory, verbose=ks_grad.verbose) logger.debug1(ks_grad, 'sum(grids response) %s', exc.sum(axis=0)) else: exc, vxc = get_vxc(ni, mol, grids, mf.xc, dm, max_memory=max_memory, verbose=ks_grad.verbose) t0 = logger.timer(ks_grad, 'vxc', *t0) if abs(hyb) < 1e-10: vj = ks_grad.get_j(mol, dm) vxc += vj[0] + vj[1] else: vj, vk = ks_grad.get_jk(mol, dm) vk *= hyb if abs(omega) > 1e-10: # For range separated Coulomb operator with mol.with_range_coulomb(omega): vk += ks_grad.get_k(mol, dm) * (alpha - hyb) vxc += vj[0] + vj[1] - vk return lib.tag_array(vxc, exc1_grid=exc)
def test_rhf_symm_get_occ(self): mf = scf.RHF(n2sym).set(verbose = 0) orbsym = numpy.array([0 , 5, 0 , 5 , 6 , 7 , 0 , 2 , 3 , 5 , 0 , 6 , 7 , 0 , 2 , 3 , 5 , 10, 11, 5]) energy = numpy.array([34, 2, 54, 43, 42, 33, 20, 61, 29, 26, 62, 52, 13, 51, 18, 78, 85, 49, 84, 7]) mo_coeff = lib.tag_array(numpy.eye(energy.size), orbsym=orbsym) mf.irrep_nelec = {'A1g':6, 'A1u':4, 'E1ux':2, 'E1uy':2} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [2, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2])) mf.irrep_nelec = {'E1ux':2, 'E1uy':2} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [0, 2, 0, 0, 2, 0, 2, 0, 0, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2])) mf.irrep_nelec = {} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [0, 2, 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2])) mo_coeff = numpy.eye(energy.size) self.assertTrue(numpy.allclose(mf.get_occ(energy), [0, 2, 0, 0, 0, 0, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2]))
def test_rohf_symm_get_occ(self): pmol = n2sym.copy() pmol.charge = 0 pmol.spin = 2 mf = scf.ROHF(pmol).set(verbose = 0) orbsym = numpy.array([0 , 5, 0 , 5 , 6 , 7 , 0 , 2 , 3 , 5 , 0 , 6 , 7 , 0 , 2 , 3 , 5 , 10, 11, 5]) energy = numpy.array([34, 2, 54, 43, 42, 33, 20, 61, 29, 26, 62, 52, 13, 51, 18, 78, 85, 49, 84, 7]) mo_coeff = lib.tag_array(numpy.eye(energy.size), orbsym=orbsym) mf.irrep_nelec = {'A1g':7, 'A1u':3, 'E1ux':2, 'E1uy':2} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [2, 2, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 1])) mf.irrep_nelec = {'E1ux':2, 'E1uy':2} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [0, 2, 0, 0, 2, 0, 2, 0, 1, 1, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2])) mf.irrep_nelec = {} self.assertTrue(numpy.allclose(mf.get_occ(energy, mo_coeff), [0, 2, 0, 0, 0, 1, 2, 0, 1, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2])) mf1 = scf.RHF(mol).set(verbose=0).view(scf.hf_symm.ROHF) self.assertTrue(numpy.allclose(mf1.get_occ(energy, mo_coeff), [0 ,2 ,0 ,0 ,0 ,0 ,2 ,0 ,0 ,0 ,0 ,0 ,2 ,0 ,2 ,0 ,0 ,0 ,0 ,2]))
def get_veff(self, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): if mol is None: mol = self.mol if dm is None: dm = self.make_rdm1() if isinstance(dm, numpy.ndarray) and dm.ndim == 2: dm = numpy.array((dm*.5, dm*.5)) if self._eri is not None or not self.direct_scf: if getattr(dm, 'mo_coeff', None) is not None: mo_coeff = dm.mo_coeff mo_occ_a = (dm.mo_occ > 0).astype(numpy.double) mo_occ_b = (dm.mo_occ ==2).astype(numpy.double) dm = lib.tag_array(dm, mo_coeff=(mo_coeff,mo_coeff), mo_occ=(mo_occ_a,mo_occ_b)) vj, vk = self.get_jk(mol, dm, hermi) vhf = vj[0] + vj[1] - vk else: ddm = dm - numpy.asarray(dm_last) vj, vk = self.get_jk(mol, ddm, hermi) vhf = vj[0] + vj[1] - vk vhf += numpy.asarray(vhf_last) return vhf
def get_jk(mf_grad, mol=None, dm=None, hermi=0, with_j=True, with_k=True): if mol is None: mol = mf_grad.mol #if dm is None: dm = mf_grad.base.make_rdm1() #TODO: dm has to be the SCF density matrix in this version. dm should be # extended to any 1-particle density matrix dm = mf_grad.base.make_rdm1() with_df = mf_grad.base.with_df auxmol = with_df.auxmol if auxmol is None: auxmol = df.addons.make_auxmol(with_df.mol, with_df.auxbasis) pmol = mol + auxmol ao_loc = mol.ao_loc nbas = mol.nbas nauxbas = auxmol.nbas get_int3c_s1 = _int3c_wrapper(mol, auxmol, 'int3c2e', 's1') get_int3c_s2 = _int3c_wrapper(mol, auxmol, 'int3c2e', 's2ij') get_int3c_ip1 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip1', 's1') get_int3c_ip2 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip2', 's2ij') nao = mol.nao naux = auxmol.nao dms = numpy.asarray(dm) out_shape = dms.shape[:-2] + (3, ) + dms.shape[-2:] dms = dms.reshape(-1, nao, nao) nset = dms.shape[0] auxslices = auxmol.aoslice_by_atom() aux_loc = auxmol.ao_loc max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = int(min(max(max_memory * .5e6 / 8 / (nao**2 * 3), 20), naux, 240)) ao_ranges = balance_partition(aux_loc, blksize) if not with_k: idx = numpy.arange(nao) dm_tril = dms + dms.transpose(0, 2, 1) dm_tril[:, idx, idx] *= .5 dm_tril = lib.pack_tril(dm_tril) # (i,j|P) rhoj = numpy.empty((nset, naux)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_s2((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] rhoj[:, p0:p1] = numpy.einsum('wp,nw->np', int3c, dm_tril) int3c = None # (P|Q) int2c = auxmol.intor('int2c2e', aosym='s1') rhoj = scipy.linalg.solve(int2c, rhoj.T, sym_pos=True).T int2c = None # (d/dX i,j|P) vj = numpy.zeros((nset, 3, nao, nao)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip1((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] vj += numpy.einsum('xijp,np->nxij', int3c, rhoj[:, p0:p1]) int3c = None if mf_grad.auxbasis_response: # (i,j|d/dX P) vjaux = numpy.empty((3, naux)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip2( (0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] vjaux[:, p0:p1] = numpy.einsum('xwp,mw,np->xp', int3c, dm_tril, rhoj[:, p0:p1]) int3c = None # (d/dX P|Q) int2c_e1 = auxmol.intor('int2c2e_ip1', aosym='s1') vjaux -= numpy.einsum('xpq,mp,nq->xp', int2c_e1, rhoj, rhoj) vjaux = [ -vjaux[:, p0:p1].sum(axis=1) for p0, p1 in auxslices[:, 2:] ] vj = lib.tag_array(-vj.reshape(out_shape), aux=numpy.array(vjaux)) else: vj = -vj.reshape(out_shape) return vj, None mo_coeff = mf_grad.base.mo_coeff mo_occ = mf_grad.base.mo_occ nmo = mo_occ.shape[-1] if isinstance(mf_grad.base, scf.rohf.ROHF): mo_coeff = numpy.vstack((mo_coeff, mo_coeff)) mo_occa = numpy.array(mo_occ > 0, dtype=numpy.double) mo_occb = numpy.array(mo_occ == 2, dtype=numpy.double) assert (mo_occa.sum() + mo_occb.sum() == mo_occ.sum()) mo_occ = numpy.vstack((mo_occa, mo_occb)) mo_coeff = numpy.asarray(mo_coeff).reshape(-1, nao, nmo) mo_occ = numpy.asarray(mo_occ).reshape(-1, nmo) rhoj = numpy.zeros((nset, naux)) f_rhok = lib.H5TmpFile() orbo = [] for i in range(nset): c = numpy.einsum('pi,i->pi', mo_coeff[i][:, mo_occ[i] > 0], numpy.sqrt(mo_occ[i][mo_occ[i] > 0])) nocc = c.shape[1] orbo.append(c) # (P|Q) int2c = scipy.linalg.cho_factor(auxmol.intor('int2c2e', aosym='s1')) max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = max_memory * .5e6 / 8 / (naux * nao) mol_ao_ranges = balance_partition(ao_loc, blksize) nsteps = len(mol_ao_ranges) for istep, (shl0, shl1, nd) in enumerate(mol_ao_ranges): int3c = get_int3c_s1((0, nbas, shl0, shl1, 0, nauxbas)) p0, p1 = ao_loc[shl0], ao_loc[shl1] rhoj += numpy.einsum('nlk,klp->np', dms[:, p0:p1], int3c) for i in range(nset): v = lib.einsum('ko,klp->plo', orbo[i], int3c) v = scipy.linalg.cho_solve(int2c, v.reshape(naux, -1)) f_rhok['%s/%s' % (i, istep)] = v.reshape(naux, p1 - p0, -1) int3c = v = None rhoj = scipy.linalg.cho_solve(int2c, rhoj.T).T int2c = None def load(set_id, p0, p1): nocc = orbo[set_id].shape[1] buf = numpy.empty((p1 - p0, nocc, nao)) col1 = 0 for istep in range(nsteps): dat = f_rhok['%s/%s' % (set_id, istep)][p0:p1] col0, col1 = col1, col1 + dat.shape[1] buf[:p1 - p0, :, col0:col1] = dat.transpose(0, 2, 1) return buf vj = numpy.zeros((nset, 3, nao, nao)) vk = numpy.zeros((nset, 3, nao, nao)) # (d/dX i,j|P) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip1((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] vj += numpy.einsum('xijp,np->nxij', int3c, rhoj[:, p0:p1]) for i in range(nset): tmp = lib.einsum('xijp,jo->xipo', int3c, orbo[i]) rhok = load(i, p0, p1) vk[i] += lib.einsum('xipo,pok->xik', tmp, rhok) tmp = rhok = None int3c = None max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = int(min(max(max_memory * .5e6 / 8 / (nao * nocc), 20), naux)) rhok_oo = [] for i in range(nset): nocc = orbo[i].shape[1] tmp = numpy.empty((naux, nocc, nocc)) for p0, p1 in lib.prange(0, naux, blksize): rhok = load(i, p0, p1) tmp[p0:p1] = lib.einsum('pok,kr->por', rhok, orbo[i]) rhok_oo.append(tmp) rhok = tmp = None if mf_grad.auxbasis_response: vjaux = numpy.zeros((3, naux)) vkaux = numpy.zeros((3, naux)) # (i,j|d/dX P) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip2((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] int3c = int3c.transpose(0, 2, 1).reshape(3 * (p1 - p0), -1) int3c = lib.unpack_tril(int3c) int3c = int3c.reshape(3, p1 - p0, nao, nao) vjaux[:, p0:p1] = numpy.einsum('xpij,mji,np->xp', int3c, dms, rhoj[:, p0:p1]) for i in range(nset): tmp = rhok_oo[i][p0:p1] tmp = lib.einsum('por,ir->pio', tmp, orbo[i]) tmp = lib.einsum('pio,jo->pij', tmp, orbo[i]) vkaux[:, p0:p1] += lib.einsum('xpij,pij->xp', int3c, tmp) int3c = tmp = None # (d/dX P|Q) int2c_e1 = auxmol.intor('int2c2e_ip1') vjaux -= numpy.einsum('xpq,mp,nq->xp', int2c_e1, rhoj, rhoj) for i in range(nset): tmp = lib.einsum('pij,qij->pq', rhok_oo[i], rhok_oo[i]) vkaux -= numpy.einsum('xpq,pq->xp', int2c_e1, tmp) vjaux = [-vjaux[:, p0:p1].sum(axis=1) for p0, p1 in auxslices[:, 2:]] vkaux = [-vkaux[:, p0:p1].sum(axis=1) for p0, p1 in auxslices[:, 2:]] vj = lib.tag_array(-vj.reshape(out_shape), aux=numpy.array(vjaux)) vk = lib.tag_array(-vk.reshape(out_shape), aux=numpy.array(vkaux)) else: vj = -vj.reshape(out_shape) vk = -vk.reshape(out_shape) return vj, vk
def canonicalize(mf, mo_coeff, mo_occ, fock=None): '''Canonicalization diagonalizes the UHF Fock matrix in occupied, virtual subspaces separatedly (without change occupancy). ''' mol = mf.mol if not mol.symmetry: return uhf.canonicalize(mf, mo_coeff, mo_occ, fock) mo_occ = numpy.asarray(mo_occ) assert (mo_occ.ndim == 2) if fock is None: dm = mf.make_rdm1(mo_coeff, mo_occ) fock = mf.get_hcore() + mf.get_veff(mf.mol, dm) occidxa = mo_occ[0] == 1 occidxb = mo_occ[1] == 1 viridxa = ~occidxa viridxb = ~occidxb mo = numpy.empty_like(mo_coeff) mo_e = numpy.empty(mo_occ.shape) if (getattr(mo_coeff, 'orbsym', None) is not None or (getattr(mo_coeff[0], 'orbsym', None) is not None and getattr(mo_coeff[1], 'orbsym', None) is not None)): orbsyma, orbsymb = get_orbsym(mol, mo_coeff) def eig_(fock, mo_coeff, idx, es, cs): if numpy.count_nonzero(idx) > 0: orb = mo_coeff[:, idx] f1 = reduce(numpy.dot, (orb.T.conj(), fock, orb)) e, c = scipy.linalg.eigh(f1) es[idx] = e cs[:, idx] = numpy.dot(mo_coeff[:, idx], c) for ir in set(orbsyma): idx_ir = orbsyma == ir eig_(fock[0], mo_coeff[0], idx_ir & occidxa, mo_e[0], mo[0]) eig_(fock[0], mo_coeff[0], idx_ir & viridxa, mo_e[0], mo[0]) for ir in set(orbsymb): idx_ir = orbsymb == ir eig_(fock[1], mo_coeff[1], idx_ir & occidxb, mo_e[1], mo[1]) eig_(fock[1], mo_coeff[1], idx_ir & viridxb, mo_e[1], mo[1]) else: s = mf.get_ovlp() def eig_(fock, mo_coeff, idx, es, cs): if numpy.count_nonzero(idx) > 0: orb = mo_coeff[:, idx] f1 = reduce(numpy.dot, (orb.T.conj(), fock, orb)) e, c = scipy.linalg.eigh(f1) es[idx] = e c = numpy.dot(mo_coeff[:, idx], c) cs[:, idx] = hf_symm._symmetrize_canonicalization_(mf, e, c, s) eig_(fock[0], mo_coeff[0], occidxa, mo_e[0], mo[0]) eig_(fock[0], mo_coeff[0], viridxa, mo_e[0], mo[0]) eig_(fock[1], mo_coeff[1], occidxb, mo_e[1], mo[1]) eig_(fock[1], mo_coeff[1], viridxb, mo_e[1], mo[1]) orbsyma, orbsymb = get_orbsym(mol, mo, s, False) mo = (lib.tag_array(mo[0], orbsym=orbsyma), lib.tag_array(mo[1], orbsym=orbsymb)) return mo_e, mo
def spatial2spin(tx, orbspin, kconserv): '''Convert T1/T2 of spatial orbital representation to T1/T2 of spin-orbital representation ''' if isinstance(tx, numpy.ndarray) and tx.ndim == 3: # KRCCSD t1 amplitudes return spatial2spin((tx, tx), orbspin, kconserv) elif isinstance(tx, numpy.ndarray) and tx.ndim == 7: # KRCCSD t2 amplitudes t2aa = numpy.zeros_like(tx) nkpts = t2aa.shape[2] for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] t2aa[ki, kj, ka] = tx[ki, kj, ka] - tx[ki, kj, kb].transpose(0, 1, 3, 2) return spatial2spin((t2aa, tx, t2aa), orbspin, kconserv) elif len(tx) == 2: # KUCCSD t1 t1a, t1b = tx nocc_a, nvir_a = t1a.shape[1:] nocc_b, nvir_b = t1b.shape[1:] else: # KUCCSD t2 t2aa, t2ab, t2bb = tx nocc_a, nocc_b, nvir_a, nvir_b = t2ab.shape[3:] nkpts = len(orbspin) nocc = nocc_a + nocc_b nvir = nvir_a + nvir_b idxoa = [numpy.where(orbspin[k][:nocc] == 0)[0] for k in range(nkpts)] idxob = [numpy.where(orbspin[k][:nocc] == 1)[0] for k in range(nkpts)] idxva = [numpy.where(orbspin[k][nocc:] == 0)[0] for k in range(nkpts)] idxvb = [numpy.where(orbspin[k][nocc:] == 1)[0] for k in range(nkpts)] if len(tx) == 2: # t1 t1 = numpy.zeros((nkpts, nocc, nvir), dtype=t1a.dtype) for k in range(nkpts): lib.takebak_2d(t1[k], t1a[k], idxoa[k], idxva[k]) lib.takebak_2d(t1[k], t1b[k], idxob[k], idxvb[k]) t1 = lib.tag_array(t1, orbspin=orbspin) return t1 else: t2 = numpy.zeros((nkpts, nkpts, nkpts, nocc**2, nvir**2), dtype=t2aa.dtype) for ki, kj, ka in kpts_helper.loop_kkk(nkpts): kb = kconserv[ki, ka, kj] idxoaa = idxoa[ki][:, None] * nocc + idxoa[kj] idxoab = idxoa[ki][:, None] * nocc + idxob[kj] idxoba = idxob[kj][:, None] * nocc + idxoa[ki] idxobb = idxob[ki][:, None] * nocc + idxob[kj] idxvaa = idxva[ka][:, None] * nvir + idxva[kb] idxvab = idxva[ka][:, None] * nvir + idxvb[kb] idxvba = idxvb[kb][:, None] * nvir + idxva[ka] idxvbb = idxvb[ka][:, None] * nvir + idxvb[kb] tmp2aa = t2aa[ki, kj, ka].reshape(nocc_a * nocc_a, nvir_a * nvir_a) tmp2bb = t2bb[ki, kj, ka].reshape(nocc_b * nocc_b, nvir_b * nvir_b) tmp2ab = t2ab[ki, kj, ka].reshape(nocc_a * nocc_b, nvir_a * nvir_b) lib.takebak_2d(t2[ki, kj, ka], tmp2aa, idxoaa.ravel(), idxvaa.ravel()) lib.takebak_2d(t2[ki, kj, ka], tmp2bb, idxobb.ravel(), idxvbb.ravel()) lib.takebak_2d(t2[ki, kj, ka], tmp2ab, idxoab.ravel(), idxvab.ravel()) lib.takebak_2d(t2[kj, ki, kb], tmp2ab, idxoba.T.ravel(), idxvba.T.ravel()) abba = -tmp2ab lib.takebak_2d(t2[ki, kj, kb], abba, idxoab.ravel(), idxvba.T.ravel()) lib.takebak_2d(t2[kj, ki, ka], abba, idxoba.T.ravel(), idxvab.ravel()) t2 = t2.reshape(nkpts, nkpts, nkpts, nocc, nocc, nvir, nvir) t2 = lib.tag_array(t2, orbspin=orbspin) return t2
def get_jk(mf_grad, mol=None, dm=None, hermi=0, with_j=True, with_k=True, ishf=True): t0 = (time.process_time(), time.time()) if mol is None: mol = mf_grad.mol if dm is None: dm = mf_grad.base.make_rdm1() with_df = mf_grad.base.with_df auxmol = with_df.auxmol if auxmol is None: auxmol = df.addons.make_auxmol(with_df.mol, with_df.auxbasis) pmol = mol + auxmol ao_loc = mol.ao_loc nbas = mol.nbas nauxbas = auxmol.nbas get_int3c_s1 = _int3c_wrapper(mol, auxmol, 'int3c2e', 's1') get_int3c_s2 = _int3c_wrapper(mol, auxmol, 'int3c2e', 's2ij') get_int3c_ip1 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip1', 's1') get_int3c_ip2 = _int3c_wrapper(mol, auxmol, 'int3c2e_ip2', 's2ij') nao = mol.nao naux = auxmol.nao dms = numpy.asarray(dm) out_shape = dms.shape[:-2] + (3, ) + dms.shape[-2:] dms = dms.reshape(-1, nao, nao) nset = dms.shape[0] idx = numpy.arange(nao) idx = idx * (idx + 1) // 2 + idx dm_tril = dms + dms.transpose(0, 2, 1) dm_tril = lib.pack_tril(dm_tril) dm_tril[:, idx] *= .5 auxslices = auxmol.aoslice_by_atom() aux_loc = auxmol.ao_loc max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = int(min(max(max_memory * .5e6 / 8 / (nao**2 * 3), 20), naux, 240)) ao_ranges = balance_partition(aux_loc, blksize) if not with_k: # (i,j|P) rhoj = numpy.empty((nset, naux)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_s2((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] rhoj[:, p0:p1] = lib.einsum('wp,nw->np', int3c, dm_tril) int3c = None # (P|Q) int2c = auxmol.intor('int2c2e', aosym='s1') rhoj = scipy.linalg.solve(int2c, rhoj.T, sym_pos=True).T int2c = None # (d/dX i,j|P) vj = numpy.zeros((nset, 3, nao, nao)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip1((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] vj += lib.einsum('xijp,np->nxij', int3c, rhoj[:, p0:p1]) int3c = None if mf_grad.auxbasis_response: # (i,j|d/dX P) vjaux = numpy.empty((nset, nset, 3, naux)) for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip2( (0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) p0, p1 = aux_loc[shl0], aux_loc[shl1] vjaux[:, :, :, p0:p1] = lib.einsum('xwp,mw,np->mnxp', int3c, dm_tril, rhoj[:, p0:p1]) int3c = None # (d/dX P|Q) int2c_e1 = auxmol.intor('int2c2e_ip1', aosym='s1') vjaux -= lib.einsum('xpq,mp,nq->mnxp', int2c_e1, rhoj, rhoj) vjaux = numpy.array([ -vjaux[:, :, :, p0:p1].sum(axis=3) for p0, p1 in auxslices[:, 2:] ]) if ishf: vjaux = vjaux.sum((1, 2)) else: vjaux = numpy.ascontiguousarray(vjaux.transpose(1, 2, 0, 3)) vj = lib.tag_array(-vj.reshape(out_shape), aux=numpy.array(vjaux)) else: vj = -vj.reshape(out_shape) logger.timer(mf_grad, 'df vj', *t0) return vj, None if hasattr(dm, 'mo_coeff') and hasattr(dm, 'mo_occ'): mo_coeff = dm.mo_coeff mo_occ = dm.mo_occ elif ishf: mo_coeff = mf_grad.base.mo_coeff mo_occ = mf_grad.base.mo_occ if isinstance(mf_grad.base, scf.rohf.ROHF): mo_coeff = numpy.vstack((mo_coeff, mo_coeff)) mo_occa = numpy.array(mo_occ > 0, dtype=numpy.double) mo_occb = numpy.array(mo_occ == 2, dtype=numpy.double) assert (mo_occa.sum() + mo_occb.sum() == mo_occ.sum()) mo_occ = numpy.vstack((mo_occa, mo_occb)) else: s0 = mol.intor('int1e_ovlp') mo_occ = [] mo_coeff = [] for dm in dms: sdms = reduce(lib.dot, (s0, dm, s0)) n, c = scipy.linalg.eigh(sdms, b=s0) mo_occ.append(n) mo_coeff.append(c) mo_occ = numpy.stack(mo_occ, axis=0) nmo = mo_occ.shape[-1] mo_coeff = numpy.asarray(mo_coeff).reshape(-1, nao, nmo) mo_occ = numpy.asarray(mo_occ).reshape(-1, nmo) rhoj = numpy.zeros((nset, naux)) f_rhok = lib.H5TmpFile() orbor = [] orbol = [] nocc = [] orbor_stack = numpy.zeros((nao, 0), dtype=mo_coeff.dtype, order='F') orbol_stack = numpy.zeros((nao, 0), dtype=mo_coeff.dtype, order='F') offs = 0 for i in range(nset): idx = numpy.abs(mo_occ[i]) > 1e-8 nocc.append(numpy.count_nonzero(idx)) c = mo_coeff[i][:, idx] orbol_stack = numpy.append(orbol_stack, c, axis=1) orbol.append(orbol_stack[:, offs:offs + nocc[-1]]) cn = lib.einsum('pi,i->pi', c, mo_occ[i][idx]) orbor_stack = numpy.append(orbor_stack, cn, axis=1) orbor.append(orbor_stack[:, offs:offs + nocc[-1]]) offs += nocc[-1] # (P|Q) int2c = scipy.linalg.cho_factor(auxmol.intor('int2c2e', aosym='s1')) t1 = (time.process_time(), time.time()) max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = max_memory * .5e6 / 8 / (naux * nao) mol_ao_ranges = balance_partition(ao_loc, blksize) nsteps = len(mol_ao_ranges) t2 = t1 for istep, (shl0, shl1, nd) in enumerate(mol_ao_ranges): int3c = get_int3c_s1((0, nbas, shl0, shl1, 0, nauxbas)) t2 = logger.timer_debug1(mf_grad, 'df grad intor (P|mn)', *t2) p0, p1 = ao_loc[shl0], ao_loc[shl1] for i in range(nset): # MRH 05/21/2020: De-vectorize this because array contiguity -> parallel scaling v = lib.dot(int3c.reshape(nao, -1, order='F').T, orbor[i]).reshape(naux, (p1 - p0) * nocc[i]) t2 = logger.timer_debug1(mf_grad, 'df grad einsum (P|mn) u_ni N_i = v_Pmi', *t2) rhoj[i] += numpy.dot(v, orbol[i][p0:p1].ravel()) t2 = logger.timer_debug1(mf_grad, 'df grad einsum v_Pmi u_mi = rho_P', *t2) v = scipy.linalg.cho_solve(int2c, v) t2 = logger.timer_debug1(mf_grad, 'df grad cho_solve (P|Q) D_Qmi = v_Pmi', *t2) f_rhok['%s/%s' % (i, istep)] = v.reshape(naux, p1 - p0, -1) t2 = logger.timer_debug1( mf_grad, 'df grad cache D_Pmi (m <-> i transpose upon retrieval)', *t2) int3c = v = None rhoj = scipy.linalg.cho_solve(int2c, rhoj.T).T int2c = None t1 = logger.timer_debug1( mf_grad, 'df grad vj and vk AO (P|Q) D_Q = (P|mn) D_mn solve', *t1) def load(set_id, p0, p1): buf = numpy.empty((p1 - p0, nocc[set_id], nao)) col1 = 0 for istep in range(nsteps): dat = f_rhok['%s/%s' % (set_id, istep)][p0:p1] col0, col1 = col1, col1 + dat.shape[1] buf[:p1 - p0, :, col0:col1] = dat.transpose(0, 2, 1) return buf vj = numpy.zeros((nset, 3, nao, nao)) vk = numpy.zeros((nset, 3, nao, nao)) # (d/dX i,j|P) fmmm = _ao2mo.libao2mo.AO2MOmmm_bra_nr_s1 # MO output index slower than AO output index; input AOs are asymmetric fdrv = _ao2mo.libao2mo.AO2MOnr_e2_drv # comp and aux indices are slower ftrans = _ao2mo.libao2mo.AO2MOtranse2_nr_s1 # input is not tril_packed null = lib.c_null_ptr() t2 = t1 for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip1((0, nbas, 0, nbas, shl0, shl1)).transpose(0, 3, 2, 1) # (P|mn'), row-major order t2 = logger.timer_debug1(mf_grad, "df grad intor (P|mn')", *t2) p0, p1 = aux_loc[shl0], aux_loc[shl1] for i in range(nset): # MRH 05/21/2020: De-vectorize this because array contiguity -> parallel scaling vj[i, 0] += numpy.dot(rhoj[i, p0:p1], int3c[0].reshape(p1 - p0, -1)).reshape(nao, nao).T vj[i, 1] += numpy.dot(rhoj[i, p0:p1], int3c[1].reshape(p1 - p0, -1)).reshape(nao, nao).T vj[i, 2] += numpy.dot(rhoj[i, p0:p1], int3c[2].reshape(p1 - p0, -1)).reshape(nao, nao).T t2 = logger.timer_debug1(mf_grad, "df grad einsum rho_P (P|mn') rho_P", *t2) tmp = numpy.empty((3, p1 - p0, nocc[i], nao), dtype=orbol_stack.dtype) fdrv( ftrans, fmmm, # xPmn u_mi -> xPin tmp.ctypes.data_as(ctypes.c_void_p), int3c.ctypes.data_as(ctypes.c_void_p), orbol[i].ctypes.data_as(ctypes.c_void_p), ctypes.c_int(3 * (p1 - p0)), ctypes.c_int(nao), (ctypes.c_int * 4)(0, nocc[i], 0, nao), null, ctypes.c_int(0)) t2 = logger.timer_debug1(mf_grad, "df grad einsum (P|mn') u_mi = dg_Pin", *t2) rhok = load(i, p0, p1) vk[i] += lib.einsum('xpoi,pok->xik', tmp, rhok) t2 = logger.timer_debug1(mf_grad, "df grad einsum D_Pim dg_Pin = v_ij", *t2) rhok = tmp = None int3c = None t1 = logger.timer_debug1(mf_grad, 'df grad vj and vk AO (P|mn) D_P eval', *t1) if mf_grad.auxbasis_response: # Cache (P|uv) D_ui c_vj. Must be include both upper and lower triangles # over nset. max_memory = mf_grad.max_memory - lib.current_memory()[0] blksize = int( min(max(max_memory * .5e6 / 8 / (nao * max(nocc)), 20), naux)) rhok_oo = [] for i, j in product(range(nset), repeat=2): tmp = numpy.empty((naux, nocc[i], nocc[j])) for p0, p1 in lib.prange(0, naux, blksize): rhok = load(i, p0, p1).reshape((p1 - p0) * nocc[i], nao) tmp[p0:p1] = lib.dot(rhok, orbol[j]).reshape(p1 - p0, nocc[i], nocc[j]) rhok_oo.append(tmp) rhok = tmp = None t1 = logger.timer_debug1( mf_grad, 'df grad vj and vk aux d_Pim u_mj = d_Pij eval', *t1) vjaux = numpy.zeros((nset, nset, 3, naux)) vkaux = numpy.zeros((nset, nset, 3, naux)) # (i,j|d/dX P) t2 = t1 fmmm = _ao2mo.libao2mo.AO2MOmmm_bra_nr_s2 # MO output index slower than AO output index; input AOs are symmetric fdrv = _ao2mo.libao2mo.AO2MOnr_e2_drv # comp and aux indices are slower ftrans = _ao2mo.libao2mo.AO2MOtranse2_nr_s2 # input is tril_packed null = lib.c_null_ptr() for shl0, shl1, nL in ao_ranges: int3c = get_int3c_ip2((0, nbas, 0, nbas, shl0, shl1)) # (i,j|P) t2 = logger.timer_debug1(mf_grad, "df grad intor (P'|mn)", *t2) p0, p1 = aux_loc[shl0], aux_loc[shl1] drhoj = lib.dot( int3c.transpose(0, 2, 1).reshape(3 * (p1 - p0), -1), dm_tril.T).reshape(3, p1 - p0, -1) # xpij,mij->xpm vjaux[:, :, :, p0:p1] = lib.einsum('xpm,np->mnxp', drhoj, rhoj[:, p0:p1]) t2 = logger.timer_debug1( mf_grad, "df grad einsum rho_P (P'|mn) D_mn = v_P", *t2) tmp = [ numpy.empty((3, p1 - p0, nocc_i, nao), dtype=orbor_stack.dtype) for nocc_i in nocc ] assert (orbor_stack.flags.f_contiguous), '{} {}'.format( orbor_stack.shape, orbor_stack.strides) for orb, buf, nocc_i in zip(orbol, tmp, nocc): fdrv( ftrans, fmmm, # gPmn u_ni -> gPim buf.ctypes.data_as(ctypes.c_void_p), int3c.ctypes.data_as(ctypes.c_void_p), orb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(3 * (p1 - p0)), ctypes.c_int(nao), (ctypes.c_int * 4)(0, nocc_i, 0, nao), null, ctypes.c_int(0)) int3c = [[ lib.dot(buf.reshape(-1, nao), orb).reshape(3, p1 - p0, -1, norb) for orb, norb in zip(orbor, nocc) ] for buf in tmp] # pim,mj,j -> pij t2 = logger.timer_debug1( mf_grad, "df grad einsum (P'|mn) u_mi u_nj N_j = v_Pmn", *t2) for i, j in product(range(nset), repeat=2): k = (i * nset) + j tmp = rhok_oo[k][p0:p1] vkaux[i, j, :, p0:p1] += lib.einsum('xpij,pij->xp', int3c[i][j], tmp) t2 = logger.timer_debug1(mf_grad, "df grad einsum d_Pij v_Pij = v_P", *t2) int3c = tmp = None t1 = logger.timer_debug1(mf_grad, "df grad vj and vk aux (P'|mn) eval", *t1) # (d/dX P|Q) int2c_e1 = auxmol.intor('int2c2e_ip1') vjaux -= lib.einsum('xpq,mp,nq->mnxp', int2c_e1, rhoj, rhoj) for i, j in product(range(nset), repeat=2): k = (i * nset) + j l = (j * nset) + i tmp = lib.einsum('pij,qji->pq', rhok_oo[k], rhok_oo[l]) vkaux[i, j] -= lib.einsum('xpq,pq->xp', int2c_e1, tmp) t1 = logger.timer_debug1(mf_grad, "df grad vj and vk aux (P'|Q) eval", *t1) vjaux = numpy.array([ -vjaux[:, :, :, p0:p1].sum(axis=3) for p0, p1 in auxslices[:, 2:] ]) vkaux = numpy.array([ -vkaux[:, :, :, p0:p1].sum(axis=3) for p0, p1 in auxslices[:, 2:] ]) if ishf: vjaux = vjaux.sum((1, 2)) idx = numpy.array(list(range(nset))) * (nset + 1) vkaux = vkaux.reshape((nset**2, 3, mol.natm))[idx, :, :].sum(0) else: vjaux = numpy.ascontiguousarray(vjaux.transpose(1, 2, 0, 3)) vkaux = numpy.ascontiguousarray(vkaux.transpose(1, 2, 0, 3)) vj = lib.tag_array(-vj.reshape(out_shape), aux=numpy.array(vjaux)) vk = lib.tag_array(-vk.reshape(out_shape), aux=numpy.array(vkaux)) else: vj = -vj.reshape(out_shape) vk = -vk.reshape(out_shape) logger.timer(mf_grad, 'df grad vj and vk', *t0) return vj, vk
def _make_eris_incore(cc, mo_coeff=None): from pyscf.pbc import tools from pyscf.pbc.cc.ccsd import _adjust_occ log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) eris = gccsd._PhysicistsERIs() cell = cc._scf.cell kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc eris.nocc = nocc #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff nao = mo_coeff[0].shape[0] dtype = mo_coeff[0].dtype moidx = get_frozen_mask(cc) nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True)) nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True)) padded_moidx = [] for k in range(nkpts): kpt_nocc = nocc_per_kpt[k] kpt_nvir = nmo_per_kpt[k] - kpt_nocc kpt_padded_moidx = numpy.concatenate( (numpy.ones(kpt_nocc, dtype=numpy.bool), numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool), numpy.ones(kpt_nvir, dtype=numpy.bool))) padded_moidx.append(kpt_padded_moidx) eris.mo_coeff = [] eris.orbspin = [] # Generate the molecular orbital coefficients with the frozen orbitals masked. # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall # spin of each MO. # # Here we will work with two index arrays; one is for our original (small) moidx # array while the next is for our new (large) padded array. for k in range(nkpts): kpt_moidx = moidx[k] kpt_padded_moidx = padded_moidx[k] mo = numpy.zeros((nao, nmo), dtype=dtype) mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx] if getattr(mo_coeff[k], 'orbspin', None) is not None: orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype orbspin = numpy.zeros(nmo, dtype=orbspin_dtype) orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx] mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) # FIXME: What if the user freezes all up spin orbitals in # an RHF calculation? The number of electrons will still be # even. else: # guess orbital spin - assumes an RHF calculation assert (numpy.count_nonzero(kpt_moidx) % 2 == 0) orbspin = numpy.zeros(mo.shape[1], dtype=int) orbspin[1::2] = 1 mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) eris.mo_coeff.append(mo) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) with lib.temporary_env(cc._scf, exxdiv=None): # _scf.exxdiv affects eris.fock. HF exchange correction should be # excluded from the Fock matrix. vhf = cc._scf.get_veff(cell, dm) fockao = cc._scf.get_hcore() + vhf eris.fock = numpy.asarray([ reduce(numpy.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(eris.mo_coeff) ]) eris.e_hf = cc._scf.energy_tot(dm=dm, vhf=vhf) eris.mo_energy = [eris.fock[k].diagonal().real for k in range(nkpts)] # Add HFX correction in the eris.mo_energy to improve convergence in # CCSD iteration. It is useful for the 2D systems since their occupied and # the virtual orbital energies may overlap which may lead to numerical # issue in the CCSD iterations. # FIXME: Whether to add this correction for other exxdiv treatments? # Without the correction, MP2 energy may be largely off the correct value. madelung = tools.madelung(cell, kpts) eris.mo_energy = [ _adjust_occ(mo_e, nocc, -madelung) for k, mo_e in enumerate(eris.mo_energy) ] # Get location of padded elements in occupied and virtual space. nocc_per_kpt = get_nocc(cc, per_kpoint=True) nonzero_padding = padding_k_idx(cc, kind="joint") # Check direct and indirect gaps for possible issues with CCSD convergence. mo_e = [eris.mo_energy[kp][nonzero_padding[kp]] for kp in range(nkpts)] mo_e = numpy.sort([y for x in mo_e for y in x]) # Sort de-nested array gap = mo_e[numpy.sum(nocc_per_kpt)] - mo_e[numpy.sum(nocc_per_kpt) - 1] if gap < 1e-5: logger.warn( cc, 'H**O-LUMO gap %s too small for KCCSD. ' 'May cause issues in convergence.', gap) kconserv = kpts_helper.get_kconserv(cell, kpts) if getattr(mo_coeff[0], 'orbspin', None) is None: # The bottom nao//2 coefficients are down (up) spin while the top are up (down). mo_a_coeff = [mo[:nao // 2] for mo in eris.mo_coeff] mo_b_coeff = [mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_b_coeff[kr], mo_b_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt += fao2mo((mo_b_coeff[kp], mo_b_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt else: mo_a_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo((mo_a_coeff[kp], mo_a_coeff[kq], mo_a_coeff[kr], mo_a_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0 eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0 eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt # Check some antisymmetrized properties of the integrals if DEBUG: check_antisymm_3412(cc, cc.kpts, eri) # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to # (rq|ps); done since we aren't tracking the kpoint of orbital 's' eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6) # Chemist -> physics notation eri = eri.transpose(0, 2, 1, 3, 5, 4, 6) # Set the various integrals eris.dtype = eri.dtype eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts log.timer('CCSD integral transformation', *cput0) return eris
def kernel(ot, oneCDMs_amo, twoCDM_amo, mo_coeff, ncore, ncas, max_memory=20000, hermi=1, veff2_mo=None, paaa_only=False): ''' Get the 1- and 2-body effective potential from MC-PDFT. Eventually I'll be able to specify mo slices for the 2-body part Args: ot : an instance of otfnal class oneCDMs_amo : ndarray of shape (2, ncas, ncas) containing spin-separated one-body density matrices twoCDM_amo : ndarray of shape (ncas, ncas, ncas, ncas) containing spin-summed two-body cumulant density matrix in an active space ao2amo : ndarray of shape (nao, ncas) containing molecular orbital coefficients for active-space orbitals Kwargs: max_memory : int or float maximum cache size in MB default is 20000 hermi : int 1 if 1CDMs are assumed hermitian, 0 otherwise Returns : float The MC-PDFT on-top exchange-correlation energy ''' if veff2_mo is not None: raise NotImplementedError( 'Molecular orbital slices for the two-body part') nocc = ncore + ncas ni, xctype, dens_deriv = ot._numint, ot.xctype, ot.dens_deriv norbs_ao = mo_coeff.shape[0] mo_core = mo_coeff[:, :ncore] ao2amo = mo_coeff[:, ncore:nocc] npair = norbs_ao * (norbs_ao + 1) // 2 veff1 = np.zeros((norbs_ao, norbs_ao), dtype=oneCDMs_amo.dtype) veff2 = _ERIS(ot.mol, mo_coeff, ncore, ncas, paaa_only=paaa_only, verbose=ot.verbose, stdout=ot.stdout) t0 = (time.clock(), time.time()) dm_core = mo_core @ mo_core.T dm_cas = np.dot(ao2amo, np.dot(oneCDMs_amo, ao2amo.T)).transpose(1, 0, 2) dm1s = dm_cas + dm_core[None, :, :] dm_core *= 2 # Can't trust that NOs are the same for alpha and beta. Have to do this explicitly here # Begin tag block: dm_core imo_occ = np.ones(ncore, dtype=dm_core.dtype) * 2.0 dm_core = tag_array(dm_core, mo_coeff=mo_core, mo_occ=imo_occ) # Begin tag block: dm_cas amo_occ = np.zeros((2, ncas), dtype=dm_cas.dtype) amo_coeff = np.stack([ao2amo.copy(), ao2amo.copy()], axis=0) for i in range(2): amo_occ[i], ua = linalg.eigh(oneCDMs_amo[i]) amo_coeff[i] = amo_coeff[i] @ ua dm_cas = tag_array(dm_cas, mo_coeff=amo_coeff, mo_occ=amo_occ) # Begin tag block: dm1s mo_occ = np.zeros((2, nocc), dtype=dm1s.dtype) mo_occ[:, :ncore] = 1.0 mo_occ[:, ncore:nocc] = amo_occ tag_coeff = np.stack( (mo_coeff[:, :nocc].copy(), mo_coeff[:, :nocc].copy()), axis=0) tag_coeff[:, :, ncore:nocc] = amo_coeff dm1s = tag_array(dm1s, mo_coeff=tag_coeff, mo_occ=mo_occ) # End tag block make_rho_c, nset_c, nao_c = ni._gen_rho_evaluator(ot.mol, dm_core, hermi) make_rho_a, nset_a, nao_a = ni._gen_rho_evaluator(ot.mol, dm_cas, hermi) make_rho, nset, nao = ni._gen_rho_evaluator(ot.mol, dm1s, hermi) gc.collect() remaining_floats = (max_memory - current_memory()[0]) * 1e6 / 8 nderiv_rho = (1, 4, 10)[dens_deriv] # ?? for meta-GGA nderiv_Pi = (1, 4)[ot.Pi_deriv] ncols_v2 = norbs_ao * ncas + ncas**2 if paaa_only else 2 * norbs_ao * ncas ncols = 1 + nderiv_rho * (5 + norbs_ao * 2) + nderiv_Pi * (1 + ncols_v2) pdft_blksize = int( remaining_floats / (ncols * BLKSIZE)) * BLKSIZE # something something indexing if ot.grids.coords is None: ot.grids.build(with_non0tab=True) ngrids = ot.grids.coords.shape[0] pdft_blksize = max(BLKSIZE, min(pdft_blksize, ngrids, BLKSIZE * 1200)) logger.debug( ot, '{} MB used of {} available; block size of {} chosen for grid with {} points' .format(current_memory()[0], max_memory, pdft_blksize, ngrids)) shls_slice = (0, ot.mol.nbas) ao_loc = ot.mol.ao_loc_nr() for ao, mask, weight, coords in ni.block_loop(ot.mol, ot.grids, norbs_ao, dens_deriv, max_memory, blksize=pdft_blksize): rho = np.asarray([make_rho(i, ao, mask, xctype) for i in range(2)]) rho_a = np.asarray([make_rho_a(i, ao, mask, xctype) for i in range(2)]) rho_c = make_rho_c(0, ao, mask, xctype) t0 = logger.timer(ot, 'untransformed densities (core and total)', *t0) Pi = get_ontop_pair_density(ot, rho, ao, dm1s, twoCDM_amo, ao2amo, dens_deriv, mask) t0 = logger.timer(ot, 'on-top pair density calculation', *t0) eot, vrho, vPi = ot.eval_ot(rho, Pi, weights=weight) t0 = logger.timer(ot, 'effective potential kernel calculation', *t0) veff1 += ot.get_veff_1body(rho, Pi, ao, weight, non0tab=mask, shls_slice=shls_slice, ao_loc=ao_loc, hermi=1, kern=vrho) t0 = logger.timer(ot, '1-body effective potential calculation', *t0) #ao[:,:,:] = np.tensordot (ao, mo_coeff, axes=1) #t0 = logger.timer (ot, 'ao2mo grid points', *t0) veff2._accumulate(ot, rho, Pi, ao, weight, rho_c, rho_a, vPi, mask, shls_slice, ao_loc) t0 = logger.timer(ot, '2-body effective potential calculation', *t0) veff2._finalize() t0 = logger.timer(ot, 'Finalizing 2-body effective potential calculation', *t0) return veff1, veff2
def rotate_mo(self, mo, u, log=None): '''Rotate orbitals with the given unitary matrix''' mo = mc1step.CASSCF.rotate_mo(self, mo, u, log) mo = lib.tag_array(mo, orbsym=self.mo_coeff.orbsym) return mo
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 = (logger.process_clock(), logger.perf_counter()) 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 = ks.get_k(mol, ddm, hermi, omega) 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 = ks.get_k(mol, dm, hermi, omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk if ground_state: exc -= (numpy.einsum('ij,ji', dm[0], vk[0]).real + numpy.einsum('ij,ji', dm[1], vk[1]).real) * .5 if ground_state: ecoul = numpy.einsum('ij,ji', dm[0] + dm[1], vj).real * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=vj, vk=vk) return vxc
def canonicalize(mc, mo_coeff=None, ci=None, eris=None, sort=False, cas_natorb=False, casdm1=None, verbose=logger.NOTE, with_meta_lowdin=WITH_META_LOWDIN): '''Canonicalized CASCI/CASSCF orbitals of effecitive Fock matrix and update CI coefficients accordingly. Effective Fock matrix is built with one-particle density matrix (see also :func:`mcscf.casci.get_fock`). For state-average CASCI/CASSCF object, the canonicalized orbitals are based on the state-average density matrix. To obtain canonicalized orbitals for an individual state, you need to pass "casdm1" of the specific state to this function. Args: mc: a CASSCF/CASCI object or RHF object Kwargs: mo_coeff (ndarray): orbitals that span the core, active and external space. ci (ndarray): CI coefficients (or objects to represent the CI wavefunctions in DMRG/QMC-MCSCF calculations). eris: Integrals for the MCSCF object. Input this object to reduce the overhead of computing integrals. It can be generated by :func:`mc.ao2mo` method. sort (bool): Whether the canonicalized orbitals are sorted based on the orbital energy (diagonal part of the effective Fock matrix) within each subspace (core, active, external). If point group symmetry is not available in the system, orbitals are always sorted. When point group symmetry is available, sort=False will preserve the symmetry label of input orbitals and only sort the orbitals in each symmetry sector. sort=True will reorder all orbitals over all symmetry sectors in each subspace and the symmetry labels may be changed. cas_natorb (bool): Whether to transform active orbitals to natual orbitals. If enabled, the output orbitals in active space are transformed to natural orbitals and CI coefficients are updated accordingly. casdm1 (ndarray): 1-particle density matrix in active space. This density matrix is used to build effective fock matrix. Without input casdm1, the density matrix is computed with the input ci coefficients/object. If neither ci nor casdm1 were given, density matrix is computed by :func:`mc.fcisolver.make_rdm1` method. For state-average CASCI/CASCF calculation, this results in a set of canonicalized orbitals of state-average effective Fock matrix. To canonicalize the orbitals for one particular state, you can assign the density matrix of that state to the kwarg casdm1. Returns: A tuple, (natural orbitals, CI coefficients, orbital energies) The orbital energies are the diagonal terms of effective Fock matrix. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.mcscf import addons log = logger.new_logger(mc, verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if casdm1 is None: if (isinstance(ci, (list, tuple)) and not isinstance(mc.fcisolver, addons.StateAverageFCISolver)): log.warn('Mulitple states found in CASCI solver. First state is ' 'used to compute the natural orbitals in active space.') casdm1 = mc.fcisolver.make_rdm1(ci[0], mc.ncas, mc.nelecas) else: casdm1 = mc.fcisolver.make_rdm1(ci, mc.ncas, mc.nelecas) ncore = mc.ncore nocc = ncore + mc.ncas nmo = mo_coeff.shape[1] fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose) if cas_natorb: mo_coeff1, ci, occ = mc.cas_natorb(mo_coeff, ci, eris, sort, casdm1, verbose, with_meta_lowdin) else: # Keep the active space unchanged by default. The rotation in active space # may cause problem for external CI solver eg DMRG. mo_coeff1 = mo_coeff.copy() log.info('Density matrix diagonal elements %s', casdm1.diagonal()) fock = reduce(numpy.dot, (mo_coeff1.T, fock_ao, mo_coeff1)) mo_energy = fock.diagonal().copy() mask = numpy.ones(nmo, dtype=bool) frozen = getattr(mc, 'frozen', None) if frozen is not None: if isinstance(frozen, (int, numpy.integer)): mask[:frozen] = False else: mask[frozen] = False core_idx = numpy.where(mask[:ncore])[0] vir_idx = numpy.where(mask[nocc:])[0] + nocc if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = mo_coeff.orbsym else: orbsym = numpy.zeros(nmo, dtype=int) if len(core_idx) > 0: # note the last two args of ._eig for mc1step_symm # mc._eig function is called to handle symmetry adapated fock w, c1 = mc._eig(fock[core_idx[:, None], core_idx], 0, ncore, orbsym[core_idx]) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:, idx] orbsym[core_idx] = orbsym[core_idx][idx] mo_coeff1[:, core_idx] = numpy.dot(mo_coeff1[:, core_idx], c1) mo_energy[core_idx] = w if len(vir_idx) > 0: w, c1 = mc._eig(fock[vir_idx[:, None], vir_idx], nocc, nmo, orbsym[vir_idx]) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:, idx] orbsym[vir_idx] = orbsym[vir_idx][idx] mo_coeff1[:, vir_idx] = numpy.dot(mo_coeff1[:, vir_idx], c1) mo_energy[vir_idx] = w if getattr(mo_coeff, 'orbsym', None) is not None: mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if log.verbose >= logger.DEBUG: for i in range(nmo): log.debug('i = %d <i|F|i> = %12.8f', i + 1, mo_energy[i]) # still return ci coefficients, in case the canonicalization funciton changed # cas orbitals, the ci coefficients should also be updated. return mo_coeff1, ci, mo_energy
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN): '''Transform active orbitals to natrual orbitals, and update the CI wfn accordingly Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci log = logger.new_logger(mc, verbose) ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(occ.round(9), kind='mergesort') occ = occ[casorb_idx] ucas = ucas[:, casorb_idx] occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ mo_coeff1 = mo_coeff.copy() mo_coeff1[:, ncore:nocc] = numpy.dot(mo_coeff[:, ncore:nocc], ucas) if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) fcivec = None if getattr(mc.fcisolver, 'transform_ci_for_orbital_rotation', None): if isinstance(ci, numpy.ndarray): fcivec = mc.fcisolver.transform_ci_for_orbital_rotation( ci, ncas, nelecas, ucas) elif (isinstance(ci, (tuple, list)) and all(isinstance(x[0], numpy.ndarray) for x in ci)): fcivec = [ mc.fcisolver.transform_ci_for_orbital_rotation( x, ncas, nelecas, ucas) for x in ci ] if fcivec is None: log.info('FCI vector not available, call CASCI to update wavefunction') mocas = mo_coeff1[:, ncore:nocc] hcore = mc.get_hcore() dm_core = numpy.dot(mo_coeff1[:, :ncore] * 2, mo_coeff1[:, :ncore].T) ecore = mc.energy_nuc() ecore += numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) if getattr(eris, 'ppaa', None) is not None: ecore += eris.vhf_c[:ncore, :ncore].trace() h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc, ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc, ncore:nocc, :, :], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: if getattr(mc, 'with_df', None): raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF') corevhf = mc.get_veff(mc.mol, dm_core) ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) # See label_symmetry_ function in casci_symm.py which initialize the # orbital symmetry information in fcisolver. This orbital symmetry # labels should be reordered to match the sorted active space orbitals. if sort and getattr(mo_coeff1, 'orbsym', None) is not None: mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] max_memory = max(400, mc.max_memory - lib.current_memory()[0]) e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CASCI energy = %s', e) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() # where_natorb gives the new locations of the natural orbitals where_natorb = mo_1to1map(ucas) log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) if with_meta_lowdin: log.info( 'Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:, ncore:nocc])) else: log.info('Natural orbital (expansion on AOs) in CAS space') label = mc.mol.ao_labels() mo_cas = mo_coeff1[:, ncore:nocc] dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:, ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s) > .4) for i, j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore + i + 1, j + 1, s[i, j]) return mo_coeff1, fcivec, mo_occ
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) vk *= hyb if abs(omega) > 1e-10: # For range separated Coulomb operator vklr = ks.get_k(mol, ddm, hermi, omega=omega) 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 = ks.get_k(mol, dm, hermi, omega=omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk if ground_state: exc -= numpy.einsum('ij,ji', dm, vk).real * hyb * .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=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 + Hubbard U terms. .. 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 + V_U. """ if cell is None: cell = ks.cell if dm is None: dm = ks.make_rdm1() if kpts is None: kpts = ks.kpts # J + V_xc vxc = krks.get_veff(ks, cell=cell, dm=dm, dm_last=dm_last, vhf_last=vhf_last, hermi=hermi, kpts=kpts, kpts_band=kpts_band) # V_U C_ao_lo = ks.C_ao_lo ovlp = ks.get_ovlp() nkpts = len(kpts) nlo = C_ao_lo.shape[-1] rdm1_lo = np.zeros((nkpts, nlo, nlo), dtype=np.complex128) for k in range(nkpts): C_inv = np.dot(C_ao_lo[k].conj().T, ovlp[k]) rdm1_lo[k] = mdot(C_inv, dm[k], C_inv.conj().T) E_U = 0.0 weight = 1.0 / nkpts logger.info(ks, "-" * 79) with np.printoptions(precision=5, suppress=True, linewidth=1000): for idx, val, lab in zip(ks.U_idx, ks.U_val, ks.U_lab): lab_string = " " for l in lab: lab_string += "%9s" % (l.split()[-1]) lab_sp = lab[0].split() logger.info(ks, "local rdm1 of atom %s: ", " ".join(lab_sp[:2]) + " " + lab_sp[2][:2]) U_mesh = np.ix_(idx, idx) P_loc = 0.0 for k in range(nkpts): S_k = ovlp[k] C_k = C_ao_lo[k][:, idx] P_k = rdm1_lo[k][U_mesh] SC = np.dot(S_k, C_k) vxc[k] += mdot(SC, (np.eye(P_k.shape[-1]) - P_k) * (val * 0.5), SC.conj().T).astype(vxc[k].dtype, copy=False) E_U += (val * 0.5) * (P_k.trace() - np.dot(P_k, P_k).trace() * 0.5) P_loc += P_k P_loc = P_loc.real / nkpts logger.info(ks, "%s\n%s", lab_string, P_loc) logger.info(ks, "-" * 79) E_U *= weight if E_U.real < 0.0 and all(np.asarray(ks.U_val) > 0): logger.warn(ks, "E_U (%s) is negative...", E_U.real) vxc = lib.tag_array(vxc, E_U=E_U) return vxc
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo = mo_coeff[:, mo_occ == 2] orbv = mo_coeff[:, mo_occ == 0] nocc = orbo.shape[1] nvir = orbv.shape[1] cis_t1 = tdobj.xy[state_id][0] # TDDFT (X,Y) has X^2-Y^2=1. # Renormalizing X (X^2=1) to map it to CIS coefficients cis_t1 *= 1. / numpy.linalg.norm(cis_t1) # TODO: Comparing to the NTOs defined in JCP, 142, 244103. JCP, 142, 244103 # provides a method to incorporate the Y matrix in the transition density # matrix. However, it may break the point group symmetry of the NTO orbitals # when the system has degenerated irreducible representations. if mol.symmetry: orbsym = hf_symm.get_orbsym(mol, mo_coeff) o_sym = orbsym[mo_occ == 2] v_sym = orbsym[mo_occ == 0] nto_o = numpy.eye(nocc) nto_v = numpy.eye(nvir) weights_o = numpy.zeros(nocc) weights_v = numpy.zeros(nvir) for ir in set(orbsym): idx = numpy.where(o_sym == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1[idx], cis_t1[idx].T) weights_o[idx], nto_o[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1[:, idx].T, cis_t1[:, idx]) weights_v[idx], nto_v[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) # weights in descending order idx = numpy.argsort(-weights_o) weights_o = weights_o[idx] nto_o = nto_o[:, idx] o_sym = o_sym[idx] idx = numpy.argsort(-weights_v) weights_v = weights_v[idx] nto_v = nto_v[:, idx] v_sym = v_sym[idx] nto_orbsym = numpy.hstack((o_sym, v_sym)) if nocc < nvir: weights = weights_o else: weights = weights_v else: nto_o, w, nto_vT = numpy.linalg.svd(cis_t1) nto_v = nto_vT.conj().T weights = w**2 nto_orbsym = None idx = numpy.argmax(abs(nto_o.real), axis=0) nto_o[:, nto_o[idx, numpy.arange(nocc)].real < 0] *= -1 idx = numpy.argmax(abs(nto_v.real), axis=0) nto_v[:, nto_v[idx, numpy.arange(nvir)].real < 0] *= -1 occupied_nto = numpy.dot(orbo, nto_o) virtual_nto = numpy.dot(orbv, nto_v) nto_coeff = numpy.hstack((occupied_nto, virtual_nto)) if mol.symmetry: nto_coeff = lib.tag_array(nto_coeff, orbsym=nto_orbsym) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights[0]) o_idx = numpy.where(abs(nto_o[:, 0]) > threshold)[0] v_idx = numpy.where(abs(nto_v[:, 0]) > threshold)[0] fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' log.info(' occ-NTO: ' + ' + '.join([(fmt % (nto_o[i, 0], i + MO_BASE)) for i in o_idx])) log.info(' vir-NTO: ' + ' + '.join([(fmt % (nto_v[i, 0], i + MO_BASE + nocc)) for i in v_idx])) return weights, nto_coeff
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 = (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_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) vxc += vj - vk * (hyb * .5) if ground_state: exc -= np.einsum('Kij,Kji', dm, vk).real * .5 * hyb*.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 rotate_mo(self, mo_coeff, u, log=None): mo = numpy.dot(mo_coeff, u) if self._scf.mol.symmetry: orbsym = hf_symm.get_orbsym(self._scf.mol, mo_coeff) mo = lib.tag_array(mo, orbsym=orbsym) return mo
def get_veff(mf, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): t0 = (time.clock(), time.time()) mf.unpack_(comm.bcast(mf.pack())) mol = mf.mol ni = mf._numint if mf.nlc != '': raise NotImplementedError omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) if abs(omega) > 1e-10: # For range separated Coulomb operator raise NotImplementedError # Broadcast the large input arrays here. if any(comm.allgather(isinstance(dm, str) and dm == 'SKIPPED_ARG')): if rank == 0 and dm is None: dm = mf.make_rdm1() dm = mpi.bcast_tagged_array(dm) if any(comm.allgather(isinstance(dm_last, str) and dm_last == 'SKIPPED_ARG')): dm_last = mpi.bcast_tagged_array(dm_last) if any(comm.allgather(isinstance(vhf_last, str) and vhf_last == 'SKIPPED_ARG')): vhf_last = mpi.bcast_tagged_array(vhf_last) ground_state = (dm.ndim == 3 and dm.shape[0] == 2) if mf.grids.coords is None: mpi_rks._setup_grids_(mf, dm[0]+dm[1]) t0 = logger.timer(mf, 'setting up grids', *t0) if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: n, exc, vxc = ni.nr_uks(mol, mf.grids, mf.xc, dm) n = comm.allreduce(n) exc = comm.allreduce(exc) vxc = mpi.reduce(vxc) logger.debug(mf, 'nelec by numeric integration = %s', n) t0 = logger.timer(mf, 'vxc', *t0) if abs(hyb) < 1e-10 and abs(alpha) < 1e-10: vk = None if getattr(vhf_last, 'vj', None) is not None: ddm = numpy.asarray(dm) - dm_last ddm = ddm[0] + ddm[1] vj = mpi.reduce(mf.get_j(mol, ddm, hermi)) vj += vhf_last.vj else: vj = mf.get_j(mol, dm[0]+dm[1], hermi) vj = mpi.reduce(vj) vxc += vj else: if getattr(vhf_last, 'vk', None) is not None: ddm = numpy.asarray(dm) - dm_last vj, vk = mf.get_jk(mol, ddm, hermi) ddm = None vj = mpi.reduce(vj[0] + vj[1]) vk = mpi.reduce(vk) * hyb vj += vhf_last.vj vk += vhf_last.vk else: vj, vk = mf.get_jk(mol, dm, hermi) vj = mpi.reduce(vj[0] + vj[1]) vk = mpi.reduce(vk) * hyb vxc += vj vxc -= 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 kernel(ot, oneCDMs_amo, twoCDM_amo, mo_coeff, ncore, ncas, max_memory=2000, hermi=1, paaa_only=False, aaaa_only=False): ''' Get the 1- and 2-body effective potential from MC-PDFT. Args: ot : an instance of otfnal class oneCDMs_amo : ndarray of shape (2, ncas, ncas) containing spin-separated one-body density matrices twoCDM_amo : ndarray of shape (ncas, ncas, ncas, ncas) containing spin-summed two-body cumulant density matrix in an active space mo_coeff : ndarray of shape (nao, nmo) containing molecular orbital coefficients ncore : integer number of inactive orbitals ncas : integer number of active orbitals Kwargs: max_memory : int or float maximum cache size in MB default is 2000 hermi : int 1 if 1CDMs are assumed hermitian, 0 otherwise paaa_only : logical If true, only compute the paaa range of papa and ppaa (all other elements set to zero) aaaa_only : logical If true, only compute the aaaa range of papa and ppaa (all other elements set to zero; overrides paaa_only) Returns: veff1 : ndarray of shape (nao, nao) 1-body effective potential veff2 : object of class pdft_veff._ERIS 2-body effective potential and related quantities ''' nocc = ncore + ncas ni, xctype, dens_deriv = ot._numint, ot.xctype, ot.dens_deriv nao = mo_coeff.shape[0] mo_core = mo_coeff[:, :ncore] ao2amo = mo_coeff[:, ncore:nocc] npair = nao * (nao + 1) // 2 shls_slice = (0, ot.mol.nbas) ao_loc = ot.mol.ao_loc_nr() veff1 = np.zeros((nao, nao), dtype=oneCDMs_amo.dtype) veff2 = _ERIS(ot.mol, mo_coeff, ncore, ncas, paaa_only=paaa_only, aaaa_only=aaaa_only, verbose=ot.verbose, stdout=ot.stdout) t0 = (time.process_time(), time.time()) # Make density matrices and TAG THEM with their own eigendecompositions # because that speeds up the rho generators! dm_core = mo_core @ mo_core.T dm_cas = np.dot(ao2amo, np.dot(oneCDMs_amo, ao2amo.T)).transpose(1, 0, 2) dm1s = dm_cas + dm_core[None, :, :] dm_core *= 2 # tag dm_core imo_occ = np.ones(ncore, dtype=dm_core.dtype) * 2.0 dm_core = tag_array(dm_core, mo_coeff=mo_core, mo_occ=imo_occ) # tag dm_cas amo_occ = np.zeros((2, ncas), dtype=dm_cas.dtype) amo_coeff = np.stack([ao2amo.copy(), ao2amo.copy()], axis=0) for i in range(2): amo_occ[i], ua = linalg.eigh(oneCDMs_amo[i]) amo_coeff[i] = amo_coeff[i] @ ua dm_cas = tag_array(dm_cas, mo_coeff=amo_coeff, mo_occ=amo_occ) # tag dm1s mo_occ = np.zeros((2, nocc), dtype=dm1s.dtype) mo_occ[:, :ncore] = 1.0 mo_occ[:, ncore:nocc] = amo_occ tag_coeff = np.stack( (mo_coeff[:, :nocc].copy(), mo_coeff[:, :nocc].copy()), axis=0) tag_coeff[:, :, ncore:nocc] = amo_coeff dm1s = tag_array(dm1s, mo_coeff=tag_coeff, mo_occ=mo_occ) # rho generators make_rho_c, nset_c, nao_c = ni._gen_rho_evaluator(ot.mol, dm_core, hermi) make_rho_a, nset_a, nao_a = ni._gen_rho_evaluator(ot.mol, dm_cas, hermi) make_rho, nset, nao_ = ni._gen_rho_evaluator(ot.mol, dm1s, hermi) # memory block size gc.collect() remaining_floats = (max_memory - current_memory()[0]) * 1e6 / 8 nderiv_rho = (1, 4, 10)[dens_deriv] # ?? for meta-GGA nderiv_Pi = (1, 4)[ot.Pi_deriv] ncols = 4 + nderiv_rho * nao # ao, weight, coords ncols += nderiv_rho * 4 + nderiv_Pi # rho, rho_a, rho_c, Pi ncols += 1 + nderiv_rho + nderiv_Pi # eot, vot # Asynchronous part nveff1 = nderiv_rho * (nao + 1) # footprint of get_veff_1body nveff2 = veff2._accumulate_ftpt() * nderiv_Pi ncols += np.amax([nveff1, nveff2]) # asynchronous fns pdft_blksize = int(remaining_floats / (ncols * BLKSIZE)) * BLKSIZE # round up if ot.grids.coords is None: ot.grids.build(with_non0tab=True) ngrids = ot.grids.coords.shape[0] pdft_blksize = max(BLKSIZE, min(pdft_blksize, ngrids, BLKSIZE * 1200)) logger.debug( ot, '{} MB used of {} available; block size of {} chosen for grid with {} points' .format(current_memory()[0], max_memory, pdft_blksize, ngrids)) # The actual loop for ao, mask, weight, coords in ni.block_loop(ot.mol, ot.grids, nao, dens_deriv, max_memory, blksize=pdft_blksize): rho = np.asarray([make_rho(i, ao, mask, xctype) for i in range(2)]) rho_a = sum([make_rho_a(i, ao, mask, xctype) for i in range(2)]) rho_c = make_rho_c(0, ao, mask, xctype) t0 = logger.timer(ot, 'untransformed densities (core and total)', *t0) Pi = get_ontop_pair_density(ot, rho, ao, dm1s, twoCDM_amo, ao2amo, dens_deriv, mask) t0 = logger.timer(ot, 'on-top pair density calculation', *t0) eot, vot = ot.eval_ot(rho, Pi, weights=weight)[:2] vrho, vPi = vot t0 = logger.timer(ot, 'effective potential kernel calculation', *t0) if ao.ndim == 2: ao = ao[None, :, :] # TODO: consistent format req's ao LDA case veff1 += ot.get_veff_1body(rho, Pi, ao, weight, non0tab=mask, shls_slice=shls_slice, ao_loc=ao_loc, hermi=1, kern=vrho) t0 = logger.timer(ot, '1-body effective potential calculation', *t0) veff2._accumulate(ot, rho, Pi, ao, weight, rho_c, rho_a, vPi, mask, shls_slice, ao_loc) t0 = logger.timer(ot, '2-body effective potential calculation', *t0) veff2._finalize() t0 = logger.timer(ot, 'Finalizing 2-body effective potential calculation', *t0) return veff1, veff2
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 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) vxc += vj - vk * (hyb * .5) if ground_state: exc -= numpy.einsum('ij,ji', dm, vk).real * .5 * hyb*.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_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() ks.initialize_grids(mol, dm) t0 = (logger.process_clock(), logger.perf_counter()) ground_state = isinstance(dm, numpy.ndarray) and dm.ndim == 2 if hermi == 2: # because rho = 0 n, exc, vxc = 0, 0, 0 else: max_memory = ks.max_memory - lib.current_memory()[0] ni = ks._numint n, exc, vxc = ni.get_vxc(mol, ks.grids, ks.xc, dm, hermi=hermi, max_memory=max_memory) if ks.nlc != '': assert ('VV10' in ks.nlc.upper()) _, enlc, vnlc = ni.get_vxc(mol, ks.nlcgrids, ks.xc + '__' + ks.nlc, dm, hermi=hermi, 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, 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: vklr = ks.get_k(mol, ddm, hermi, omega=omega) 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 = ks.get_k(mol, dm, hermi, omega=omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk if ground_state: exc -= numpy.einsum('ij,ji', dm, vk).real * .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=vj, vk=vk) return vxc
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None): '''Transform active orbitals to natrual orbitals, and update the CI wfn Args: mc : a CASSCF/CASCI object or RHF object Kwargs: sort : bool Sort natural orbitals wrt the occupancy. Returns: A tuple, the first item is natural orbitals, the second is updated CI coefficients, the third is the natural occupancy associated to the natural orbitals. ''' from pyscf.lo import orth from pyscf.tools import dump_mat from pyscf.tools.mo_mapping import mo_1to1map if isinstance(verbose, logger.Logger): log = verbose else: log = logger.Logger(mc.stdout, mc.verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci ncore = mc.ncore ncas = mc.ncas nocc = ncore + ncas nelecas = mc.nelecas if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas) # orbital symmetry is reserved in this _eig call occ, ucas = mc._eig(-casdm1, ncore, nocc) if sort: casorb_idx = numpy.argsort(occ.round(9), kind='mergesort') occ = occ[casorb_idx] ucas = ucas[:,casorb_idx] # restore phase # where_natorb gives the location of the natural orbital for the input cas # orbitals. gen_strings4orblist map thes sorted strings (on CAS orbital) to # the unsorted determinant strings (on natural orbital). e.g. (3o,2e) system # CAS orbital 1 2 3 # natural orbital 3 1 2 <= by mo_1to1map # CASorb-strings 0b011, 0b101, 0b110 # == (1,2), (1,3), (2,3) # natorb-strings (3,1), (3,2), (1,2) # == 0B101, 0B110, 0B011 <= by gen_strings4orblist # then argsort to translate the string representation to the address # [2(=0B011), 0(=0B101), 1(=0B110)] # to indicate which CASorb-strings address to be loaded in each natorb-strings slot where_natorb = mo_1to1map(ucas) for i, k in enumerate(where_natorb): if ucas[i,k] < 0: ucas[:,k] *= -1 occ = -occ mo_occ = numpy.zeros(mo_coeff.shape[1]) mo_occ[:ncore] = 2 mo_occ[ncore:nocc] = occ mo_coeff1 = mo_coeff.copy() mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas) if hasattr(mo_coeff, 'orbsym'): orbsym = numpy.copy(mo_coeff.orbsym) if sort: orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx] mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if isinstance(ci, numpy.ndarray): fcivec = fci.addons.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas) elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray): # for state-average eigenfunctions fcivec = [fci.addons.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas) for x in ci] else: log.info('FCI vector not available, call CASCI for wavefunction') mocas = mo_coeff1[:,ncore:nocc] hcore = mc.get_hcore() dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].T) ecore = mc._scf.energy_nuc() ecore+= numpy.einsum('ij,ji', hcore, dm_core) h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas)) if eris is not None and hasattr(eris, 'ppaa'): ecore += eris.vhf_c[:ncore,:ncore].trace() h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas)) aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas) aaaa = ao2mo.incore.full(aaaa, ucas) else: corevhf = mc.get_veff(mc.mol, dm_core) ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5 h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas)) aaaa = ao2mo.kernel(mc.mol, mocas) # See label_symmetry_ function in casci_symm.py which initialize the # orbital symmetry information in fcisolver. This orbital symmetry # labels should be reordered to match the sorted active space orbitals. if hasattr(mo_coeff1, 'orbsym') and sort: mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc] max_memory = max(400, mc.max_memory-lib.current_memory()[0]) e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore, max_memory=max_memory, verbose=log) log.debug('In Natural orbital, CASCI energy = %.12g', e) if log.verbose >= logger.INFO: ovlp_ao = mc._scf.get_ovlp() log.debug('where_natorb %s', str(where_natorb)) log.info('Natural occ %s', str(occ)) log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space') label = mc.mol.ao_labels() orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao) mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc])) dump_mat.dump_rec(log.stdout, mo_cas, label, start=1) if mc._scf.mo_coeff is not None: s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) idx = numpy.argwhere(abs(s)>.4) for i,j in idx: log.info('<CAS-nat-orb|mo-hf> %d %d %12.8f', ncore+i+1, j+1, s[i,j]) return mo_coeff1, fcivec, mo_occ
def sort_mo(casscf, mo_coeff, caslst, base=BASE): '''Pick orbitals for CAS space Args: casscf : an :class:`CASSCF` or :class:`CASCI` object mo_coeff : ndarray or a list of ndarray Orbitals for CASSCF initial guess. In the UHF-CASSCF, it's a list of two orbitals, for alpha and beta spin. caslst : list of int or nested list of int A list of orbital indices to represent the CAS space. In the UHF-CASSCF, it's consist of two lists, for alpha and beta spin. Kwargs: base : int 0-based (C-style) or 1-based (Fortran-style) caslst Returns: An reoreded mo_coeff, which put the orbitals given by caslst in the CAS space Examples: >>> from pyscf import gto, scf, mcscf >>> mol = gto.M(atom='N 0 0 0; N 0 0 1', basis='ccpvdz', verbose=0) >>> mf = scf.RHF(mol) >>> mf.scf() >>> mc = mcscf.CASSCF(mf, 4, 4) >>> cas_list = [5,6,8,9] # pi orbitals >>> mo = mc.sort_mo(cas_list) >>> mc.kernel(mo)[0] -109.007378939813691 ''' ncore = casscf.ncore def ext_list(nmo, caslst): mask = numpy.ones(nmo, dtype=bool) mask[caslst] = False idx = numpy.where(mask)[0] if len(idx) + casscf.ncas != nmo: raise ValueError('Active space size is incompatible with caslist. ' 'ncas = %d. caslist %s' % (casscf.ncas, caslst)) return idx if isinstance(ncore, (int, numpy.integer)): nmo = mo_coeff.shape[1] if base != 0: caslst = [i - base for i in caslst] idx = ext_list(nmo, caslst) mo = numpy.hstack((mo_coeff[:, idx[:ncore]], mo_coeff[:, caslst], mo_coeff[:, idx[ncore:]])) if getattr(mo_coeff, 'orbsym', None) is not None: orbsym = mo_coeff.orbsym orbsym = numpy.hstack( (orbsym[idx[:ncore]], orbsym[caslst], orbsym[idx[ncore:]])) mo = lib.tag_array(mo, orbsym=orbsym) return mo else: # UHF-based CASSCF if isinstance(caslst[0], (int, numpy.integer)): if base != 0: caslsta = [i - 1 for i in caslst] caslst = (caslsta, caslsta) else: # two casspace lists, for alpha and beta if base != 0: caslst = ([i - base for i in caslst[0]], [i - base for i in caslst[1]]) nmo = mo_coeff[0].shape[1] idxa = ext_list(nmo, caslst[0]) mo_a = numpy.hstack( (mo_coeff[0][:, idxa[:ncore[0]]], mo_coeff[0][:, caslst[0]], mo_coeff[0][:, idxa[ncore[0]:]])) idxb = ext_list(nmo, caslst[1]) mo_b = numpy.hstack( (mo_coeff[1][:, idxb[:ncore[1]]], mo_coeff[1][:, caslst[1]], mo_coeff[1][:, idxb[ncore[1]:]])) if getattr(mo_coeff[0], 'orbsym', None) is not None: orbsyma, orbsymb = mo_coeff[0].orbsym, mo_coeff[1].orbsym orbsyma = numpy.hstack( (orbsyma[idxa[:ncore[0]]], orbsyma[caslst[0]], orbsyma[idxa[ncore[0]:]])) orbsymb = numpy.hstack( (orbsymb[idxb[:ncore[1]]], orbsymb[caslst[1]], orbsymb[idxb[ncore[1]:]])) mo_a = lib.tag_array(mo_a, orbsym=orbsyma) mo_b = lib.tag_array(mo_b, orbsym=orbsymb) return (mo_a, mo_b)
def _make_eris_incore(cc, mo_coeff=None): log = logger.Logger(cc.stdout, cc.verbose) cput0 = (time.clock(), time.time()) eris = gccsd._PhysicistsERIs() kpts = cc.kpts nkpts = cc.nkpts nocc = cc.nocc nmo = cc.nmo nvir = nmo - nocc eris.nocc = nocc #if any(nocc != numpy.count_nonzero(cc._scf.mo_occ[k] > 0) for k in range(nkpts)): # raise NotImplementedError('Different occupancies found for different k-points') if mo_coeff is None: mo_coeff = cc.mo_coeff #else: # # If mo_coeff is not canonical orbital # # TODO does this work for k-points? changed to conjugate. # raise NotImplementedError nao = mo_coeff[0].shape[0] dtype = mo_coeff[0].dtype moidx = get_frozen_mask(cc) nocc_per_kpt = numpy.asarray(get_nocc(cc, per_kpoint=True)) nmo_per_kpt = numpy.asarray(get_nmo(cc, per_kpoint=True)) padded_moidx = [] for k in range(nkpts): kpt_nocc = nocc_per_kpt[k] kpt_nvir = nmo_per_kpt[k] - kpt_nocc kpt_padded_moidx = numpy.concatenate( (numpy.ones(kpt_nocc, dtype=numpy.bool), numpy.zeros(nmo - kpt_nocc - kpt_nvir, dtype=numpy.bool), numpy.ones(kpt_nvir, dtype=numpy.bool))) padded_moidx.append(kpt_padded_moidx) eris.mo_coeff = [] eris.orbspin = [] # Generate the molecular orbital coefficients with the frozen orbitals masked. # Each MO is tagged with orbspin, a list of 0's and 1's that give the overall # spin of each MO. # # Here we will work with two index arrays; one is for our original (small) moidx # array while the next is for our new (large) padded array. for k in range(nkpts): kpt_moidx = moidx[k] kpt_padded_moidx = padded_moidx[k] mo = numpy.zeros((nao, nmo), dtype=dtype) mo[:, kpt_padded_moidx] = mo_coeff[k][:, kpt_moidx] if hasattr(mo_coeff[k], 'orbspin'): orbspin_dtype = mo_coeff[k].orbspin[kpt_moidx].dtype orbspin = numpy.zeros(nmo, dtype=orbspin_dtype) orbspin[kpt_padded_moidx] = mo_coeff[k].orbspin[kpt_moidx] mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) # FIXME: What if the user freezes all up spin orbitals in # an RHF calculation? The number of electrons will still be # even. else: # guess orbital spin - assumes an RHF calculation assert (numpy.count_nonzero(kpt_moidx) % 2 == 0) orbspin = numpy.zeros(mo.shape[1], dtype=int) orbspin[1::2] = 1 mo = lib.tag_array(mo, orbspin=orbspin) eris.orbspin.append(orbspin) eris.mo_coeff.append(mo) # Re-make our fock MO matrix elements from density and fock AO dm = cc._scf.make_rdm1(cc.mo_coeff, cc.mo_occ) fockao = cc._scf.get_hcore() + cc._scf.get_veff(cc._scf.cell, dm) eris.fock = numpy.asarray([ reduce(numpy.dot, (mo.T.conj(), fockao[k], mo)) for k, mo in enumerate(eris.mo_coeff) ]) kconserv = kpts_helper.get_kconserv(cc._scf.cell, cc.kpts) # The bottom nao//2 coefficients are down (up) spin while the top are up (down). # These are 'spin-less' quantities; spin-conservation will be added manually. so_coeff = [mo[:nao // 2] + mo[nao // 2:] for mo in eris.mo_coeff] eri = numpy.empty((nkpts, nkpts, nkpts, nmo, nmo, nmo, nmo), dtype=numpy.complex128) fao2mo = cc._scf.with_df.ao2mo for kp, kq, kr in kpts_helper.loop_kkk(nkpts): ks = kconserv[kp, kq, kr] eri_kpt = fao2mo( (so_coeff[kp], so_coeff[kq], so_coeff[kr], so_coeff[ks]), (kpts[kp], kpts[kq], kpts[kr], kpts[ks]), compact=False) eri_kpt[(eris.orbspin[kp][:, None] != eris.orbspin[kq]).ravel()] = 0 eri_kpt[:, (eris.orbspin[kr][:, None] != eris.orbspin[ks]).ravel()] = 0 eri_kpt = eri_kpt.reshape(nmo, nmo, nmo, nmo) eri[kp, kq, kr] = eri_kpt # Check some antisymmetrized properties of the integrals if DEBUG: check_antisymm_3412(cc, cc.kpts, eri) # Antisymmetrizing (pq|rs)-(ps|rq), where the latter integral is equal to # (rq|ps); done since we aren't tracking the kpoint of orbital 's' eri = eri - eri.transpose(2, 1, 0, 5, 4, 3, 6) # Chemist -> physics notation eri = eri.transpose(0, 2, 1, 3, 5, 4, 6) # Set the various integrals eris.dtype = eri.dtype eris.oooo = eri[:, :, :, :nocc, :nocc, :nocc, :nocc].copy() / nkpts eris.ooov = eri[:, :, :, :nocc, :nocc, :nocc, nocc:].copy() / nkpts eris.ovoo = eri[:, :, :, :nocc, nocc:, :nocc, :nocc].copy() / nkpts eris.oovv = eri[:, :, :, :nocc, :nocc, nocc:, nocc:].copy() / nkpts eris.ovov = eri[:, :, :, :nocc, nocc:, :nocc, nocc:].copy() / nkpts eris.ovvv = eri[:, :, :, :nocc, nocc:, nocc:, nocc:].copy() / nkpts eris.vvvv = eri[:, :, :, nocc:, nocc:, nocc:, nocc:].copy() / nkpts log.timer('CCSD integral transformation', *cput0) return eris
def canonicalize(mc, mo_coeff=None, ci=None, eris=None, sort=False, cas_natorb=False, casdm1=None, verbose=logger.NOTE): '''Canonicalized CASCI/CASSCF orbitals of effecitve Fock matrix. Effective Fock matrix is built with one-particle density matrix (see also :func:`mcscf.casci.get_fock`) Args: mc : a CASSCF/CASCI object or RHF object Returns: A tuple, (natural orbitals, CI coefficients, orbital energies) The orbital energies are the diagonal terms of general Fock matrix. ''' from pyscf.lo import orth from pyscf.tools import dump_mat log = logger.new_logger(mc, verbose) if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci if casdm1 is None: casdm1 = mc.fcisolver.make_rdm1(ci, mc.ncas, mc.nelecas) ncore = mc.ncore nocc = ncore + mc.ncas nmo = mo_coeff.shape[1] fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose) fock = reduce(numpy.dot, (mo_coeff.T, fock_ao, mo_coeff)) mo_energy = fock.diagonal().copy() if cas_natorb: mo_coeff1, ci, occ = mc.cas_natorb(mo_coeff, ci, eris, sort, casdm1, verbose) ma = mo_coeff1[:,ncore:nocc] mo_energy[ncore:nocc] = numpy.einsum('ji,ji->i', ma, fock_ao.dot(ma)) else: # Keep the active space unchanged by default. The rotation in active space # may cause problem for external CI solver eg DMRG. mo_coeff1 = numpy.empty_like(mo_coeff) mo_coeff1[:,ncore:nocc] = mo_coeff[:,ncore:nocc] if ncore > 0: # note the last two args of ._eig for mc1step_symm # mc._eig function is called to handle symmetry adapated fock w, c1 = mc._eig(fock[:ncore,:ncore], 0, ncore) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:,idx] mo_coeff1[:,:ncore] = numpy.dot(mo_coeff[:,:ncore], c1) mo_energy[:ncore] = w if nmo-nocc > 0: w, c1 = mc._eig(fock[nocc:,nocc:], nocc, nmo) if sort: idx = numpy.argsort(w.round(9), kind='mergesort') w = w[idx] c1 = c1[:,idx] mo_coeff1[:,nocc:] = numpy.dot(mo_coeff[:,nocc:], c1) mo_energy[nocc:] = w if hasattr(mo_coeff, 'orbsym'): if sort: orbsym = symm.label_orb_symm(mc.mol, mc.mol.irrep_id, mc.mol.symm_orb, mo_coeff1) else: orbsym = mo_coeff.orbsym mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym) if log.verbose >= logger.DEBUG: for i in range(nmo): log.debug('i = %d <i|F|i> = %12.8f', i+1, mo_energy[i]) # still return ci coefficients, in case the canonicalization funciton changed # cas orbitals, the ci coefficients should also be updated. return mo_coeff1, ci, mo_energy
def get_nto(tdobj, state=1, threshold=OUTPUT_THRESHOLD, verbose=None): r''' Natural transition orbital analysis. The natural transition density matrix between ground state and excited state :math:`Tia = \langle \Psi_{ex} | i a^\dagger | \Psi_0 \rangle` can be transformed to diagonal form through SVD :math:`T = O \sqrt{\lambda} V^\dagger`. O and V are occupied and virtual natural transition orbitals. The diagonal elements :math:`\lambda` are the weights of the occupied-virtual orbital pair in the excitation. Ref: Martin, R. L., JCP, 118, 4775-4777 Note in the TDHF/TDDFT calculations, the excitation part (X) is interpreted as the CIS coefficients and normalized to 1. The de-excitation part (Y) is ignored. Args: state : int Excited state ID. state = 1 means the first excited state. If state < 0, state ID is counted from the last excited state. Kwargs: threshold : float Above which the NTO coefficients will be printed in the output. Returns: A list (weights, NTOs). NTOs are natural orbitals represented in AO basis. The first N_occ NTOs are occupied NTOs and the rest are virtual NTOs. ''' if state == 0: logger.warn( tdobj, 'Excited state starts from 1. ' 'Set state=1 for first excited state.') state_id = state elif state < 0: state_id = state else: state_id = state - 1 mol = tdobj.mol mo_coeff = tdobj._scf.mo_coeff mo_occ = tdobj._scf.mo_occ orbo_a = mo_coeff[0][:, mo_occ[0] == 1] orbv_a = mo_coeff[0][:, mo_occ[0] == 0] orbo_b = mo_coeff[1][:, mo_occ[1] == 1] orbv_b = mo_coeff[1][:, mo_occ[1] == 0] nocc_a = orbo_a.shape[1] nvir_a = orbv_a.shape[1] nocc_b = orbo_b.shape[1] nvir_b = orbv_b.shape[1] cis_t1a, cis_t1b = tdobj.xy[state_id][0] norm = numpy.linalg.norm(cis_t1a)**2 + numpy.linalg.norm(cis_t1b)**2 cis_t1a *= 1. / norm cis_t1b *= 1. / norm if mol.symmetry: orbsyma, orbsymb = uhf_symm.get_orbsym(mol, mo_coeff) o_sym_a = orbsyma[mo_occ[0] == 1] v_sym_a = orbsyma[mo_occ[0] == 0] o_sym_b = orbsymb[mo_occ[1] == 1] v_sym_b = orbsymb[mo_occ[1] == 0] nto_o_a = numpy.eye(nocc_a) nto_v_a = numpy.eye(nvir_a) nto_o_b = numpy.eye(nocc_b) nto_v_b = numpy.eye(nvir_b) weights_o_a = numpy.zeros(nocc_a) weights_v_a = numpy.zeros(nvir_a) weights_o_b = numpy.zeros(nocc_b) weights_v_b = numpy.zeros(nvir_b) for ir in set(orbsyma): idx = numpy.where(o_sym_a == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1a[idx], cis_t1a[idx].T) weights_o_a[idx], nto_o_a[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_a == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1a[:, idx].T, cis_t1a[:, idx]) weights_v_a[idx], nto_v_a[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) for ir in set(orbsymb): idx = numpy.where(o_sym_b == ir)[0] if idx.size > 0: dm_oo = numpy.dot(cis_t1b[idx], cis_t1b[idx].T) weights_o_b[idx], nto_o_b[idx[:, None], idx] = numpy.linalg.eigh(dm_oo) idx = numpy.where(v_sym_b == ir)[0] if idx.size > 0: dm_vv = numpy.dot(cis_t1b[:, idx].T, cis_t1b[:, idx]) weights_v_b[idx], nto_v_b[idx[:, None], idx] = numpy.linalg.eigh(dm_vv) def sort(weights, nto, sym): # weights in descending order idx = numpy.argsort(-weights) weights = weights[idx] nto = nto[:, idx] sym = sym[idx] return weights, nto, sym weights_o_a, nto_o_a, o_sym_a = sort(weights_o_a, nto_o_a, o_sym_a) weights_v_a, nto_v_a, v_sym_a = sort(weights_v_a, nto_v_a, v_sym_a) weights_o_b, nto_o_b, o_sym_b = sort(weights_o_b, nto_o_b, o_sym_b) weights_v_b, nto_v_b, v_sym_b = sort(weights_v_b, nto_v_b, v_sym_b) nto_orbsyma = numpy.hstack((o_sym_a, v_sym_a)) nto_orbsymb = numpy.hstack((o_sym_b, v_sym_b)) if nocc_a < nvir_a: weights_a = weights_o_a else: weights_a = weights_v_a if nocc_b < nvir_b: weights_b = weights_o_b else: weights_b = weights_v_b else: nto_o_a, w_a, nto_v_aT = numpy.linalg.svd(cis_t1a) nto_o_b, w_b, nto_v_bT = numpy.linalg.svd(cis_t1b) nto_v_a = nto_v_aT.conj().T nto_v_b = nto_v_bT.conj().T weights_a = w_a**2 weights_b = w_b**2 nto_orbsyma = nto_orbsymb = None def _set_phase_(c): idx = numpy.argmax(abs(c.real), axis=0) c[:, c[idx, numpy.arange(c.shape[1])].real < 0] *= -1 _set_phase_(nto_o_a) _set_phase_(nto_o_b) _set_phase_(nto_v_a) _set_phase_(nto_v_b) occupied_nto_a = numpy.dot(orbo_a, nto_o_a) occupied_nto_b = numpy.dot(orbo_b, nto_o_b) virtual_nto_a = numpy.dot(orbv_a, nto_v_a) virtual_nto_b = numpy.dot(orbv_b, nto_v_b) nto_coeff = (numpy.hstack((occupied_nto_a, virtual_nto_a)), numpy.hstack((occupied_nto_b, virtual_nto_b))) if mol.symmetry: nto_coeff = (lib.tag_array(nto_coeff[0], orbsym=nto_orbsyma), lib.tag_array(nto_coeff[1], orbsym=nto_orbsymb)) log = logger.new_logger(tdobj, verbose) if log.verbose >= logger.INFO: log.info('State %d: %g eV NTO largest component %s', state_id + 1, tdobj.e[state_id] * nist.HARTREE2EV, weights_a[0] + weights_b[0]) fmt = '%' + str(lib.param.OUTPUT_DIGITS) + 'f (MO #%d)' o_idx_a = numpy.where(abs(nto_o_a[:, 0]) > threshold)[0] v_idx_a = numpy.where(abs(nto_v_a[:, 0]) > threshold)[0] o_idx_b = numpy.where(abs(nto_o_b[:, 0]) > threshold)[0] v_idx_b = numpy.where(abs(nto_v_b[:, 0]) > threshold)[0] log.info(' alpha occ-NTO: ' + ' + '.join([(fmt % (nto_o_a[i, 0], i + MO_BASE)) for i in o_idx_a])) log.info(' alpha vir-NTO: ' + ' + '.join([(fmt % (nto_v_a[i, 0], i + MO_BASE + nocc_a)) for i in v_idx_a])) log.info(' beta occ-NTO: ' + ' + '.join([(fmt % (nto_o_b[i, 0], i + MO_BASE)) for i in o_idx_b])) log.info(' beta vir-NTO: ' + ' + '.join([(fmt % (nto_v_b[i, 0], i + MO_BASE + nocc_b)) for i in v_idx_b])) return (weights_a, weights_b), nto_coeff
def get_veff(self, mol, dm, *args, **kwargs): vhf = oldMF.get_veff(self, mol, dm) epcm, vpcm = cosmo_solver(dm) vhf += vpcm return lib.tag_array(vhf, epcm=epcm, vpcm=vpcm)
def get_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): '''Coulomb + XC functional for GKS. ''' 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) assert (hermi == 1) dm = numpy.asarray(dm) nso = dm.shape[-1] nao = nso // 2 dm_a = dm[..., :nao, :nao].real dm_b = dm[..., nao:, nao:].real 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_a + dm_b, 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_a + dm_b, ks.nlcgrids) t0 = logger.timer(ks, 'setting up nlc grids', *t0) max_memory = ks.max_memory - lib.current_memory()[0] ni = ks._numint n, exc, vxc = ni.nr_uks(mol, ks.grids, ks.xc, (dm_a, dm_b), 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_a + dm_b, max_memory=max_memory) exc += enlc vxc += vnlc logger.debug(ks, 'nelec by numeric integration = %s', n) t0 = logger.timer(ks, 'vxc', *t0) if vxc.ndim == 4: raise NotImplementedError vxc = numpy.asarray(scipy.linalg.block_diag(*vxc), dtype=dm.dtype) #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, 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: vklr = ks.get_k(mol, ddm, hermi, omega=omega) 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 = ks.get_k(mol, dm, hermi, omega=omega) vklr *= (alpha - hyb) vk += vklr vxc += vj - vk if ground_state: exc -= numpy.einsum('ij,ji', dm, vk).real * .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=vj, vk=vk) return vxc
def get_veff(ks, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpt=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 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_uks(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 # ndim = 3 : dm.shape = ([alpha,beta], nao, nao) ground_state = (dm.ndim == 3 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, kpt) t0 = logger.timer(ks, 'setting up grids', *t0) if not isinstance(dm, numpy.ndarray): dm = numpy.asarray(dm) if dm.ndim == 2: # RHF DM dm = numpy.asarray((dm * .5, dm * .5)) 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, 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[0] + dm[1], 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) vj = vj[0] + vj[1] 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 if ground_state: exc -= (numpy.einsum('ij,ji', dm[0], vk[0]) + numpy.einsum('ij,ji', dm[1], vk[1])).real * .5 if ground_state: ecoul = numpy.einsum('ij,ji', dm[0] + dm[1], vj).real * .5 else: ecoul = None vxc = lib.tag_array(vxc, ecoul=ecoul, exc=exc, vj=None, vk=None) return vxc
def get_veff(ks_grad, mol=None, dm=None): '''Coulomb + XC functional ''' if mol is None: mol = ks_grad.mol if dm is None: dm = ks_grad.base.make_rdm1() t0 = (logger.process_clock(), logger.perf_counter()) mf = ks_grad.base ni = mf._numint if ks_grad.grids is not None: grids = ks_grad.grids else: grids = mf.grids if grids.coords is None: grids.build(with_non0tab=True) if mf.nlc != '': raise NotImplementedError #enabling range-separated hybrids omega, alpha, hyb = ni.rsh_and_hybrid_coeff(mf.xc, spin=mol.spin) mem_now = lib.current_memory()[0] max_memory = max(2000, ks_grad.max_memory * .9 - mem_now) if ks_grad.grid_response: exc, vxc = rks_grad.get_vxc_full_response(ni, mol, grids, mf.xc, dm, max_memory=max_memory, verbose=ks_grad.verbose) logger.debug1(ks_grad, 'sum(grids response) %s', exc.sum(axis=0)) else: exc, vxc = rks_grad.get_vxc(ni, mol, grids, mf.xc, dm, max_memory=max_memory, verbose=ks_grad.verbose) t0 = logger.timer(ks_grad, 'vxc', *t0) if abs(hyb) < 1e-10 and abs(alpha) < 1e-10: vj = ks_grad.get_j(mol, dm) vxc += vj if ks_grad.auxbasis_response: e1_aux = vj.aux else: vj, vk = ks_grad.get_jk(mol, dm) if ks_grad.auxbasis_response: vk_aux = vk.aux * hyb vk *= hyb if abs(omega) > 1e-10: # For range separated Coulomb operator raise NotImplementedError vk_lr = ks_grad.get_k(mol, dm, omega=omega) vk += vk_lr * (alpha - hyb) if ks_grad.auxbasis_response: vk_aux += vk_lr.aux * (alpha - hyb) vxc += vj - vk * .5 if ks_grad.auxbasis_response: e1_aux = vj.aux - vk_aux * .5 if ks_grad.auxbasis_response: logger.debug1(ks_grad, 'sum(auxbasis response) %s', e1_aux.sum(axis=0)) vxc = lib.tag_array(vxc, exc1_grid=exc, aux=e1_aux) else: vxc = lib.tag_array(vxc, exc1_grid=exc) return vxc
def dia(magobj, gauge_orig=None): mol = magobj.mol mf = magobj._scf mo_energy = magobj._scf.mo_energy mo_coeff = magobj._scf.mo_coeff mo_occ = magobj._scf.mo_occ orboa = mo_coeff[0][:,mo_occ[0] > 0] orbob = mo_coeff[1][:,mo_occ[1] > 0] dm0a = lib.tag_array(orboa.dot(orboa.T), mo_coeff=mo_coeff[0], mo_occ=mo_occ[0]) dm0b = lib.tag_array(orbob.dot(orbob.T), mo_coeff=mo_coeff[1], mo_occ=mo_occ[1]) dm0 = dm0a + dm0b dme0a = numpy.dot(orboa * mo_energy[0][mo_occ[0] > 0], orboa.T) dme0b = numpy.dot(orbob * mo_energy[1][mo_occ[1] > 0], orbob.T) dme0 = dme0a + dme0b e2 = rhf_mag._get_dia_1e(magobj, gauge_orig, dm0, dme0) if gauge_orig is not None: return -e2 # Computing the 2nd order Vxc integrals from GIAO grids = mf.grids ni = mf._numint xc_code = mf.xc xctype = ni._xc_type(xc_code) omega, alpha, hyb = ni.rsh_and_hybrid_coeff(xc_code, mol.spin) make_rhoa, nset, nao = ni._gen_rho_evaluator(mol, dm0a, hermi=1) make_rhob = ni._gen_rho_evaluator(mol, dm0b, hermi=1)[0] ngrids = len(grids.weights) mem_now = lib.current_memory()[0] max_memory = max(2000, mf.max_memory*.9-mem_now) BLKSIZE = numint.BLKSIZE blksize = min(int(max_memory/12*1e6/8/nao/BLKSIZE)*BLKSIZE, ngrids) vmata = numpy.zeros((3,3,nao,nao)) vmatb = numpy.zeros((3,3,nao,nao)) if xctype == 'LDA': ao_deriv = 0 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory, blksize=blksize): rho = (make_rhoa(0, ao, mask, 'LDA'), make_rhob(0, ao, mask, 'LDA')) vxc = ni.eval_xc(xc_code, rho, spin=1, deriv=1)[1] vrho = vxc[0] r_ao = numpy.einsum('pi,px->pxi', ao, coords) aow = numpy.einsum('pxi,p,p->pxi', r_ao, weight, vrho[:,0]) vmata += lib.einsum('pxi,pyj->xyij', r_ao, aow) aow = numpy.einsum('pxi,p,p->pxi', r_ao, weight, vrho[:,1]) vmatb += lib.einsum('pxi,pyj->xyij', r_ao, aow) rho = vxc = vrho = aow = None elif xctype == 'GGA': ao_deriv = 1 for ao, mask, weight, coords \ in ni.block_loop(mol, grids, nao, ao_deriv, max_memory, blksize=blksize): rho = (make_rhoa(0, ao, mask, 'GGA'), make_rhob(0, ao, mask, 'GGA')) vxc = ni.eval_xc(xc_code, rho, spin=1, deriv=1)[1] wva, wvb = numint._uks_gga_wv0(rho, vxc, weight) # Computing \nabla (r * AO) = r * \nabla AO + [\nabla,r]_- * AO r_ao = numpy.einsum('npi,px->npxi', ao, coords) r_ao[1,:,0] += ao[0] r_ao[2,:,1] += ao[0] r_ao[3,:,2] += ao[0] aow = numpy.einsum('npxi,np->pxi', r_ao, wva) vmata += lib.einsum('pxi,pyj->xyij', r_ao[0], aow) aow = numpy.einsum('npxi,np->pxi', r_ao, wvb) vmata += lib.einsum('pxi,pyj->xyij', r_ao[0], aow) rho = vxc = vrho = aow = None vmata = vmata + vmata.transpose(0,1,3,2) vmatb = vmatb + vmatb.transpose(0,1,3,2) elif xctype == 'MGGA': raise NotImplementedError('meta-GGA') vmata = rks_mag._add_giao_phase(mol, vmata) vmatb = rks_mag._add_giao_phase(mol, vmatb) e2 += numpy.einsum('qp,xypq->xy', dm0a, vmata) e2 += numpy.einsum('qp,xypq->xy', dm0b, vmatb) vmata = vmatb = None e2 = e2.ravel() # Handle the hybrid functional and the range-separated functional if abs(hyb) > 1e-10: vs = jk.get_jk(mol, [dm0, dm0a, dm0a, dm0b, dm0b], ['ijkl,ji->s2kl', 'ijkl,jk->s1il', 'ijkl,li->s1kj', 'ijkl,jk->s1il', 'ijkl,li->s1kj'], 'int2e_gg1', 's4', 9, hermi=1) e2 += numpy.einsum('xpq,qp->x', vs[0], dm0) e2 -= numpy.einsum('xpq,qp->x', vs[1], dm0a) * .5 * hyb e2 -= numpy.einsum('xpq,qp->x', vs[2], dm0a) * .5 * hyb e2 -= numpy.einsum('xpq,qp->x', vs[3], dm0b) * .5 * hyb e2 -= numpy.einsum('xpq,qp->x', vs[4], dm0b) * .5 * hyb vk = jk.get_jk(mol, [dm0a, dm0b], ['ijkl,jk->s1il', 'ijkl,jk->s1il'], 'int2e_g1g2', 'aa4', 9, hermi=0) e2 -= numpy.einsum('xpq,qp->x', vk[0], dm0a) * hyb e2 -= numpy.einsum('xpq,qp->x', vk[1], dm0b) * hyb if abs(omega) > 1e-10: with mol.with_range_coulomb(omega): vs = jk.get_jk(mol, [dm0a, dm0a, dm0b, dm0b], ['ijkl,jk->s1il', 'ijkl,li->s1kj', 'ijkl,jk->s1il', 'ijkl,li->s1kj'], 'int2e_gg1', 's4', 9, hermi=1) e2 -= numpy.einsum('xpq,qp->x', vs[0], dm0a) * .5 * (alpha-hyb) e2 -= numpy.einsum('xpq,qp->x', vs[1], dm0a) * .5 * (alpha-hyb) e2 -= numpy.einsum('xpq,qp->x', vs[2], dm0b) * .5 * (alpha-hyb) e2 -= numpy.einsum('xpq,qp->x', vs[3], dm0b) * .5 * (alpha-hyb) vk = jk.get_jk(mol, [dm0a, dm0b], ['ijkl,jk->s1il', 'ijkl,jk->s1il'], 'int2e_g1g2', 'aa4', 9, hermi=0) e2 -= numpy.einsum('xpq,qp->x', vk[0], dm0a) * (alpha-hyb) e2 -= numpy.einsum('xpq,qp->x', vk[1], dm0b) * (alpha-hyb) else: vj = jk.get_jk(mol, dm0, 'ijkl,ji->s2kl', 'int2e_gg1', 's4', 9, hermi=1) e2 += numpy.einsum('xpq,qp->x', vj, dm0) return -e2.reshape(3, 3)
def label_symmetry_(mc, mo_coeff, ci0=None): log = logger.Logger(mc.stdout, mc.verbose) #irrep_name = mc.mol.irrep_name irrep_name = mc.mol.irrep_id s = mc._scf.get_ovlp() try: orbsym = scf.hf_symm.get_orbsym(mc._scf.mol, mo_coeff, s, True) except ValueError: log.warn('mc1step_symm symmetrizes input orbitals') ncore = mc.ncore nocc = mc.ncore + mc.ncas mo_cor = symm.symmetrize_space(mc.mol, mo_coeff[:, :ncore], s=s, check=False) mo_act = symm.symmetrize_space(mc.mol, mo_coeff[:,ncore:nocc], s=s, check=False) mo_vir = symm.symmetrize_space(mc.mol, mo_coeff[:,nocc: ], s=s, check=False) mo_coeff = numpy.hstack((mo_cor,mo_act,mo_vir)) orbsym = symm.label_orb_symm(mc.mol, irrep_name, mc.mol.symm_orb, mo_coeff, s=s) mo_coeff_with_orbsym = lib.tag_array(mo_coeff, orbsym=orbsym) active_orbsym = getattr(mc.fcisolver, 'orbsym', []) if (not hasattr(active_orbsym, '__len__')) or len(active_orbsym) == 0: ncore = mc.ncore nocc = mc.ncore + mc.ncas mc.fcisolver.orbsym = orbsym[ncore:nocc] log.debug('Active space irreps %s', str(mc.fcisolver.orbsym)) wfnsym = 0 if getattr(mc.fcisolver, 'wfnsym', None) is not None: wfnsym = mc.fcisolver.wfnsym elif ci0 is None: # Guess wfnsym based on HF determinant. mo_coeff may not be HF # canonical orbitals. Some checks are needed to ensure that mo_coeff # are derived from the symmetry adapted SCF calculations. if mo_coeff is mc._scf.mo_coeff: wfnsym = 0 for ir in orbsym[mc._scf.mo_occ == 1]: wfnsym ^= ir mc.fcisolver.wfnsym = wfnsym log.debug('Set CASCI wfnsym %s based on HF determinant', wfnsym) elif hasattr(mo_coeff, 'orbsym'): # It may be reordered SCF orbitals ncore = mc.ncore nocc = mc.ncore + mc.ncas cas_orb = mo_coeff[:,ncore:nocc] s = reduce(numpy.dot, (cas_orb.T, mc._scf.get_ovlp(), mc._scf.mo_coeff)) if numpy.all(numpy.max(s, axis=1) > 1-1e-9): idx = numpy.argmax(s, axis=1) cas_orbsym = orbsym[ncore:nocc] cas_occ = mc._scf.mo_occ[idx] wfnsym = 0 for ir in cas_orbsym[cas_occ == 1]: wfnsym ^= ir mc.fcisolver.wfnsym = wfnsym log.debug('Active space are constructed from canonical SCF ' 'orbitals %s', idx) log.debug('Set CASCI wfnsym %s based on HF determinant', wfnsym) elif hasattr(mc.fcisolver, 'guess_wfnsym'): wfnsym = mc.fcisolver.guess_wfnsym(mc.ncas, mc.nelecas, ci0, verbose=log) log.debug('CASCI wfnsym %s (based on CI initial guess)', wfnsym) if isinstance(wfnsym, (int, numpy.integer)): wfnsym = symm.irrep_id2name(mc.mol.groupname, wfnsym) log.info('Active space CI wfn symmetry = %s', wfnsym) return mo_coeff_with_orbsym