def doexact( self, chempot_imp=0.0 ): OneRDM = self.helper.construct1RDM_loc( self.doSCF, self.umat ) self.energy = 0.0 self.imp_1RDM = [] self.dmetOrbs = [] if ( self.doDET == True ) and ( self.doDET_NO == True ): self.NOvecs = [] self.NOdiag = [] maxiter = len( self.impClust ) if ( self.TransInv == True ): maxiter = 1 remainingOrbs = np.ones( [ len( self.impClust[ 0 ] ) ], dtype=float ) for counter in range( maxiter ): impurityOrbs = self.impClust[ counter ] numImpOrbs = np.sum( impurityOrbs ) if ( self.BATH_ORBS == None ): numBathOrbs = numImpOrbs else: numBathOrbs = self.BATH_ORBS[ counter ] numBathOrbs, loc2dmet, core1RDM_dmet = self.helper.constructbath( OneRDM, impurityOrbs, numBathOrbs ) if ( self.BATH_ORBS == None ): core_cutoff = 0.01 else: core_cutoff = 0.5 for cnt in range(len(core1RDM_dmet)): if ( core1RDM_dmet[ cnt ] < core_cutoff ): core1RDM_dmet[ cnt ] = 0.0 elif ( core1RDM_dmet[ cnt ] > 2.0 - core_cutoff ): core1RDM_dmet[ cnt ] = 2.0 else: print "Bad DMET bath orbital selection: trying to put a bath orbital with occupation", core1RDM_dmet[ cnt ], "into the environment :-(." assert( 0 == 1 ) Norb_in_imp = numImpOrbs + numBathOrbs Nelec_in_imp = int(round(self.ints.Nelec - np.sum( core1RDM_dmet ))) core1RDM_loc = np.dot( np.dot( loc2dmet, np.diag( core1RDM_dmet ) ), loc2dmet.T ) self.dmetOrbs.append( loc2dmet[ :, :Norb_in_imp ] ) # Impurity and bath orbitals only assert( Norb_in_imp <= self.Norb ) dmetOEI = self.ints.dmet_oei( loc2dmet, Norb_in_imp ) dmetFOCK = self.ints.dmet_fock( loc2dmet, Norb_in_imp, core1RDM_loc ) dmetTEI = self.ints.dmet_tei( loc2dmet, Norb_in_imp ) if ( self.NI_hack == True ): dmetTEI[:,:,:,numImpOrbs:]=0.0 dmetTEI[:,:,numImpOrbs:,:]=0.0 dmetTEI[:,numImpOrbs:,:,:]=0.0 dmetTEI[numImpOrbs:,:,:,:]=0.0 umat_rotated = np.dot(np.dot(loc2dmet.T, self.umat), loc2dmet) umat_rotated[:numImpOrbs,:numImpOrbs]=0.0 dmetOEI += umat_rotated[:Norb_in_imp,:Norb_in_imp] dmetFOCK = np.array( dmetOEI, copy=True ) print "DMET::exact : Performing a (", Norb_in_imp, "orb,", Nelec_in_imp, "el ) DMET active space calculation." if ( self.method == 'ED' ): import chemps2 IMP_energy, IMP_1RDM = chemps2.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, chempot_imp ) if ( self.method == 'CC' ): import pyscf_cc assert( Nelec_in_imp % 2 == 0 ) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_cc.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, self.CC_E_TYPE, chempot_imp ) if ( self.method == 'MP2' ): import pyscf_mp2 assert( Nelec_in_imp % 2 == 0 ) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_mp2.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp ) self.energy += IMP_energy self.imp_1RDM.append( IMP_1RDM ) if ( self.doDET == True ) and ( self.doDET_NO == True ): RDMeigenvals, RDMeigenvecs = np.linalg.eigh( IMP_1RDM[ :numImpOrbs, :numImpOrbs ] ) self.NOvecs.append( RDMeigenvecs ) self.NOdiag.append( RDMeigenvals ) remainingOrbs -= impurityOrbs if ( self.doDET == True ) and ( self.doDET_NO == True ): self.NOrotation = self.constructNOrotation() Nelectrons = 0.0 for counter in range( maxiter ): Nelectrons += np.trace( self.imp_1RDM[counter][ :self.imp_size[counter], :self.imp_size[counter] ] ) if ( self.TransInv == True ): Nelectrons = Nelectrons * len( self.impClust ) self.energy = self.energy * len( self.impClust ) remainingOrbs[:] = 0 # When an incomplete impurity tiling is used for the Hamiltonian, self.energy should be augmented with the remaining HF part if ( np.sum( remainingOrbs ) != 0 ): if ( self.CC_E_TYPE == 'CASCI' ): ''' If CASCI is passed as CC energy type, the energy of the one and only full impurity Hamiltonian is returned. The one-electron integrals of this impurity Hamiltonian is the full Fock operator of the CORE orbitals! The constant part of the energy still needs to be added: sum_occ ( 2 * OEI[occ,occ] + JK[occ,occ] ) = einsum( core1RDM_loc, OEI ) + 0.5 * einsum( core1RDM_loc, JK ) = 0.5 * einsum( core1RDM_loc, OEI + FOCK ) ''' assert( maxiter == 1 ) transfo = np.eye( self.Norb, dtype=float ) totalOEI = self.ints.dmet_oei( transfo, self.Norb ) totalFOCK = self.ints.dmet_fock( transfo, self.Norb, core1RDM_loc ) self.energy += 0.5 * np.einsum( 'ij,ij->', core1RDM_loc, totalOEI + totalFOCK ) Nelectrons = np.trace( self.imp_1RDM[ 0 ] ) + np.trace( core1RDM_loc ) # Because full active space is used to compute the energy else: transfo = np.eye( self.Norb, dtype=float ) totalOEI = self.ints.dmet_oei( transfo, self.Norb ) totalFOCK = self.ints.dmet_fock( transfo, self.Norb, OneRDM ) self.energy += 0.5 * np.einsum( 'ij,ij->', OneRDM[remainingOrbs==1,:], \ totalOEI[remainingOrbs==1,:] + totalFOCK[remainingOrbs==1,:] ) Nelectrons += np.trace( (OneRDM[remainingOrbs==1,:])[:,remainingOrbs==1] ) remainingOrbs[ remainingOrbs==1 ] -= 1 assert( np.all( remainingOrbs == 0 ) ) self.energy += self.ints.const() return Nelectrons
def hl_solver (self, chempot=0., threshold=1.0e-12): # energy = self.mol.energy_nuc() energy = 0. nelec = 0. rdm_ao = np.dot(self.cf, self.cf.T) AX_val = np.dot(self.Sf, self.A_val) rdm_val = np.dot(AX_val.T, np.dot(rdm_ao, AX_val)) print ( "shapes" ) print ( "cf",self.cf.shape ) print ( "rdm_ao",rdm_ao.shape ) print ( "AX_val",AX_val.shape ) print ( "rdm_val",rdm_val.shape ) if(not self.parallel): myrange = range(self.nimp) else: from mpi4py import MPI comm = MPI.COMM_WORLD rank = MPI.COMM_WORLD.Get_rank() size = MPI.COMM_WORLD.Get_size() myrange = range(rank,rank+1) for i in myrange: # prepare orbital indexing imp_val = np.zeros((self.nvl,), dtype=bool) imp_val_ = np.zeros((self.nvl,), dtype=bool) if self.nc > 0: imp_core = np.zeros((self.nc,), dtype=bool) imp_core_ = np.zeros((self.nc,), dtype=bool) if self.nvt > 0: imp_virt = np.zeros((self.nvt,), dtype=bool) imp_virt_ = np.zeros((self.nvt,), dtype=bool) for k in range(self.mol.natm): if self.imp_atx[i][k]: imp_val[self.at_val == k] = True if self.nc > 0: imp_core[self.at_core == k] = True if self.nvt > 0: imp_virt[self.at_virt == k] = True if self.imp_at[i][k]: imp_val_[self.at_val == k] = True if self.nc > 0: imp_core_[self.at_core == k] = True if self.nvt > 0: imp_virt_[self.at_virt == k] = True print("imp val", imp_val) # embedding cf_tmp, ncore, nact, ImpOrbs_x = \ embedding.embedding (rdm_val, imp_val, \ threshold=self.thresh, \ transform_imp='hf') print("Doing EMBEDDING") print("cf_tmp", cf_tmp) print("ncore, nact", ncore, nact) print("ImpOrbs_x", ImpOrbs_x) cf_tmp = np.dot(self.A_val, cf_tmp) print("cf_tmp", cf_tmp) # localize imp+bath orbitals if self.method == 'dmrg': XR = np.random.rand(nact,nact) XR -= XR.T XS = sla.expm(0.01*XR) cf_ib = np.dot(cf_tmp[:,ncore:ncore+nact], XS) # loc = localizer.localizer (self.mol, cf_ib, 'boys') # loc.verbose = 5 # cf_ib = loc.optimize(threshold=1.0e-5) # del loc cf_ib = lo.Boys(mol, cd_ib).kernel() R = np.dot(cf_ib.T, \ np.dot(self.Sf, cf_tmp[:,ncore:ncore+nact])) print ( np.allclose(np.dot(cf_tmp[:,ncore:ncore+nact], \ ImpOrbs_x), \ np.dot(cf_ib, np.dot(R, ImpOrbs_x))) ) ImpOrbs_x = np.dot(R, ImpOrbs_x) cf_tmp[:,ncore:ncore+nact] = cf_ib print ( cf_ib ) # prepare ImpOrbs ni_val = nact nj_val = np.count_nonzero(imp_val_) if self.nc > 0: ni_core = np.count_nonzero(imp_core) nj_core = np.count_nonzero(imp_core_) else: ni_core = nj_core = 0 if self.nvt > 0: ni_virt = np.count_nonzero(imp_virt) nj_virt = np.count_nonzero(imp_virt_) else: ni_virt = nj_virt = 0 ii = 0 ImpOrbs = np.zeros((ni_val+ni_core+ni_virt,\ nj_val+nj_core+nj_virt,)) if self.nc > 0: j = 0 for i in range(self.nc): if imp_core[i] and imp_core_[i]: ImpOrbs[j,ii] = 1. ii += 1 if imp_core[i]: j += 1 j = 0 for i in range(self.nvl): if imp_val[i] and imp_val_[i]: ImpOrbs[ni_core:ni_core+ni_val,ii] = ImpOrbs_x[:,j] ii += 1 if imp_val[i]: j += 1 if self.nvt > 0: j = 0 for i in range(self.nvt): if imp_virt[i] and imp_virt_[i]: ImpOrbs[ni_core+ni_val+j,ii] = 1. ii += 1 if imp_virt[i]: j += 1 # prepare orbitals cf_core = cf_virt = None if self.nc > 0: cf_core = self.A_core[:,imp_core] if self.nvt > 0: cf_virt = self.A_virt[:,imp_virt] cf_val = cf_tmp[:,ncore:ncore+nact] if cf_core is not None and cf_virt is not None: cf = np.hstack ((cf_core, cf_val, cf_virt,)) elif cf_core is not None: cf = np.hstack ((cf_core, cf_val,)) elif cf_virt is not None: cf = np.hstack ((cf_val, cf_virt,)) else: cf = cf_val # prepare core if self.nc > 0: Ac_ = self.A_core[:,~(imp_core)] X_core = np.hstack((Ac_, cf_tmp[:,:ncore],)) else: X_core = cf_tmp[:,:ncore] n_orth = cf.shape[1] if cf_virt is not None: n_orth -= cf_virt.shape[1] print("x-core", X_core) print("cf b4 solver", cf) print("imporbs", ImpOrbs) if self.method == 'hf': nel_, en_ = \ pyscf_hf.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth) elif self.method == 'cc': nel_, en_ = \ pyscf_cc.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot) elif self.method == 'ccsd(t)': nel_, en_ = \ pyscf_ccsdt.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth) elif self.method == 'mp2': nel_, en_ = \ pyscf_mp2.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot) elif self.method == 'dfmp2': nel_, en_ = \ pyscf_dfmp2.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot, mf_tot=self.mf_tot) elif self.method == 'dfmp2_testing': nel_, en_ = \ dfmp2_testing.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot, mf_tot=self.mf_tot) elif self.method == 'dfmp2_testing2': # print(self.mol) # print(2*(self.nup-X_core.shape[1])) # print(X_core.shape) # print(cf.shape) # print(ImpOrbs.shape) # print(n_orth) nel_, en_ = \ dfmp2_testing2.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot ) #, mf_tot=self.mf_tot) elif self.method == 'dfmp2_testing3': nel_, en_ = \ dfmp2_testing3.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot, mf_tot=self.mf_tot) elif self.method == 'dfmp2_testing4': nel_, en_ = \ dfmp2_testing4.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth,FrozenPot=self.FrozenPot, mf_tot=self.mf_tot) elif self.method == 'fci': nel_, en_ = \ pyscf_fci.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth) elif self.method == 'dmrg': nel_, en_ = \ dmrg.solve (self.mol, \ 2*(self.nup-X_core.shape[1]), \ X_core, cf, ImpOrbs, chempot=chempot, \ n_orth=n_orth) nelec += nel_ energy += en_ if(self.parallel): nelec_tot = comm.reduce(nelec, op=MPI.SUM,root=0) energy_tot = comm.reduce(energy,op=MPI.SUM,root=0) if(rank==0): energy_tot += self.mol.energy_nuc()+self.e_core nelec = comm.bcast(nelec_tot, root=0) energy = comm.bcast(energy_tot,root=0) comm.barrier() if(rank==0): print ( 'DMET energy = ', energy ) else: energy+=self.mol.energy_nuc()+self.e_core print ( 'DMET energy = ', energy ) return nelec
def doexact( self, chempot_imp=0.0 ): OneRDM = self.helper.construct1RDM_loc( self.doSCF, self.umat ) self.energy = 0.0 self.imp_1RDM = [] self.dmetOrbs = [] if ( self.doDET == True ) and ( self.doDET_NO == True ): self.NOvecs = [] self.NOdiag = [] maxiter = len( self.impClust ) if ( self.TransInv == True ): maxiter = 1 remainingOrbs = np.ones( [ len( self.impClust[ 0 ] ) ], dtype=float ) for counter in range( maxiter ): flag_rhf = np.sum(self.impClust[ counter ]) < 0 impurityOrbs = np.abs(self.impClust[ counter ]) numImpOrbs = np.sum( impurityOrbs ) if ( self.BATH_ORBS == None ): numBathOrbs = numImpOrbs else: numBathOrbs = self.BATH_ORBS[ counter ] numBathOrbs, loc2dmet, core1RDM_dmet = self.helper.constructbath( OneRDM, impurityOrbs, numBathOrbs ) if ( self.BATH_ORBS == None ): core_cutoff = 0.01 else: core_cutoff = 0.5 for cnt in range(len(core1RDM_dmet)): if ( core1RDM_dmet[ cnt ] < core_cutoff ): core1RDM_dmet[ cnt ] = 0.0 elif ( core1RDM_dmet[ cnt ] > 2.0 - core_cutoff ): core1RDM_dmet[ cnt ] = 2.0 else: print "Bad DMET bath orbital selection: trying to put a bath orbital with occupation", core1RDM_dmet[ cnt ], "into the environment :-(." assert( 0 == 1 ) Norb_in_imp = numImpOrbs + numBathOrbs Nelec_in_imp = int(round(self.ints.Nelec - np.sum( core1RDM_dmet ))) core1RDM_loc = np.dot( np.dot( loc2dmet, np.diag( core1RDM_dmet ) ), loc2dmet.T ) self.dmetOrbs.append( loc2dmet[ :, :Norb_in_imp ] ) # Impurity and bath orbitals only assert( Norb_in_imp <= self.Norb ) dmetOEI = self.ints.dmet_oei( loc2dmet, Norb_in_imp ) dmetFOCK = self.ints.dmet_fock( loc2dmet, Norb_in_imp, core1RDM_loc ) dmetTEI = self.ints.dmet_tei( loc2dmet, Norb_in_imp ) if ( self.NI_hack == True ): dmetTEI[:,:,:,numImpOrbs:]=0.0 dmetTEI[:,:,numImpOrbs:,:]=0.0 dmetTEI[:,numImpOrbs:,:,:]=0.0 dmetTEI[numImpOrbs:,:,:,:]=0.0 umat_rotated = np.dot(np.dot(loc2dmet.T, self.umat), loc2dmet) umat_rotated[:numImpOrbs,:numImpOrbs]=0.0 dmetOEI += umat_rotated[:Norb_in_imp,:Norb_in_imp] dmetFOCK = np.array( dmetOEI, copy=True ) # print "DMET::exact : Performing a (", Norb_in_imp, "orb,", Nelec_in_imp, "el ) DMET active space calculation." if ( flag_rhf ): import pyscf_rhf DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_rhf.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp ) elif ( self.method == 'ED' ): print "no chemps2 solver" #import chemps2 #IMP_energy, IMP_1RDM = chemps2.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, chempot_imp ) elif ( self.method == 'CC' ): import pyscf_cc assert( Nelec_in_imp % 2 == 0 ) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_cc.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, self.CC_E_TYPE, chempot_imp ) elif ( self.method == 'FCI' ): import pyscf_fci assert( Nelec_in_imp % 2 == 0) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_fci.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp) elif ( self.method == 'MP2' ): import pyscf_mp2 assert( Nelec_in_imp % 2 == 0 ) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp/2, numImpOrbs, chempot_imp ) IMP_energy, IMP_1RDM = pyscf_mp2.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp ) self.energy += IMP_energy self.imp_1RDM.append( IMP_1RDM ) if ( self.doDET == True ) and ( self.doDET_NO == True ): RDMeigenvals, RDMeigenvecs = np.linalg.eigh( IMP_1RDM[ :numImpOrbs, :numImpOrbs ] ) self.NOvecs.append( RDMeigenvecs ) self.NOdiag.append( RDMeigenvals ) remainingOrbs -= impurityOrbs if ( self.doDET == True ) and ( self.doDET_NO == True ): self.NOrotation = self.constructNOrotation() Nelectrons = 0.0 for counter in range( maxiter ): Nelectrons += np.trace( self.imp_1RDM[counter][ :self.imp_size[counter], :self.imp_size[counter] ] ) if ( self.TransInv == True ): Nelectrons = Nelectrons * len( self.impClust ) self.energy = self.energy * len( self.impClust ) remainingOrbs[:] = 0 # When an incomplete impurity tiling is used for the Hamiltonian, self.energy should be augmented with the remaining HF part if ( np.sum( remainingOrbs ) != 0 ): if ( self.CC_E_TYPE == 'CASCI' ): ''' If CASCI is passed as CC energy type, the energy of the one and only full impurity Hamiltonian is returned. The one-electron integrals of this impurity Hamiltonian is the full Fock operator of the CORE orbitals! The constant part of the energy still needs to be added: sum_occ ( 2 * OEI[occ,occ] + JK[occ,occ] ) = einsum( core1RDM_loc, OEI ) + 0.5 * einsum( core1RDM_loc, JK ) = 0.5 * einsum( core1RDM_loc, OEI + FOCK ) ''' assert( maxiter == 1 ) transfo = np.eye( self.Norb, dtype=float ) totalOEI = self.ints.dmet_oei( transfo, self.Norb ) totalFOCK = self.ints.dmet_fock( transfo, self.Norb, core1RDM_loc ) self.energy += 0.5 * np.einsum( 'ij,ij->', core1RDM_loc, totalOEI + totalFOCK ) Nelectrons = np.trace( self.imp_1RDM[ 0 ] ) + np.trace( core1RDM_loc ) # Because full active space is used to compute the energy else: #transfo = np.eye( self.Norb, dtype=float ) #totalOEI = self.ints.dmet_oei( transfo, self.Norb ) #totalFOCK = self.ints.dmet_fock( transfo, self.Norb, OneRDM ) #self.energy += 0.5 * np.einsum( 'ij,ij->', OneRDM[remainingOrbs==1,:], \ # totalOEI[remainingOrbs==1,:] + totalFOCK[remainingOrbs==1,:] ) #Nelectrons += np.trace( (OneRDM[remainingOrbs==1,:])[:,remainingOrbs==1] ) assert (np.array_equal(self.ints.active, np.ones([self.ints.mol.nao_nr()], dtype=int))) from pyscf import scf from types import MethodType mol_ = self.ints.mol mf_ = scf.RHF(mol_) impOrbs = remainingOrbs==1 xorb = np.dot(mf_.get_ovlp(), self.ints.ao2loc) hc = -chempot_imp * np.dot(xorb[:,impOrbs], xorb[:,impOrbs].T) dm0 = np.dot(self.ints.ao2loc, np.dot(OneRDM, self.ints.ao2loc.T)) def mf_hcore (self, mol=None): if mol is None: mol = self.mol return scf.hf.get_hcore(mol) + hc mf_.get_hcore = MethodType(mf_hcore, mf_) mf_.scf(dm0) assert (mf_.converged) rdm1 = mf_.make_rdm1() jk = mf_.get_veff(dm=rdm1) xorb = np.dot(mf_.get_ovlp(), self.ints.ao2loc) rdm1 = np.dot(xorb.T, np.dot(rdm1, xorb)) oei = np.dot(self.ints.ao2loc.T, np.dot(mf_.get_hcore()-hc, self.ints.ao2loc)) jk = np.dot(self.ints.ao2loc.T, np.dot(jk, self.ints.ao2loc)) ImpEnergy = \ + 0.50 * np.einsum('ji,ij->', rdm1[:,impOrbs], oei[impOrbs,:]) \ + 0.50 * np.einsum('ji,ij->', rdm1[impOrbs,:], oei[:,impOrbs]) \ + 0.25 * np.einsum('ji,ij->', rdm1[:,impOrbs], jk[impOrbs,:]) \ + 0.25 * np.einsum('ji,ij->', rdm1[impOrbs,:], jk[:,impOrbs]) self.energy += ImpEnergy Nelectrons += np.trace(rdm1[np.ix_(impOrbs,impOrbs)]) remainingOrbs[ remainingOrbs==1 ] -= 1 assert( np.all( remainingOrbs == 0 ) ) self.energy += self.ints.const() return Nelectrons
def doexact(self, chempot_imp=0.0): OneRDM = self.helper.construct1RDM_loc( self.doSCF, self.umat ) #HP: construct 1rdm (in AO) for the total system from the Fock matrix in local ao basis. projected on atoms? self.energy = 0.0 #This OneRDM will be used to construct the bath self.imp_1RDM = [] self.dmetOrbs = [] if (self.doDET == True) and (self.doDET_NO == True): self.NOvecs = [] self.NOdiag = [] maxiter = len(self.impClust) if (self.TransInv == True): maxiter = 1 remainingOrbs = np.ones([len(self.impClust[0])], dtype=float) E_frag = [] #HP: to export fragment energies for counter in range(maxiter): flag_rhf = np.sum( self.impClust[counter] ) < 0 # the elements of self.impClust can be diffent to 1 and negative? impurityOrbs = np.abs(self.impClust[counter]) numImpOrbs = np.sum(impurityOrbs) if (self.BATH_ORBS != None and self.BATH_ORBS[counter] != 0): numBathOrbs = self.BATH_ORBS[counter] else: numBathOrbs = numImpOrbs numBathOrbs, loc2dmet, core1RDM_dmet = self.helper.constructbath( OneRDM, impurityOrbs, numBathOrbs) if (self.BATH_ORBS == None): core_cutoff = 0.01 else: core_cutoff = 0.5 for cnt in range(len(core1RDM_dmet)): if (core1RDM_dmet[cnt] < core_cutoff): core1RDM_dmet[cnt] = 0.0 elif (core1RDM_dmet[cnt] > 2.0 - core_cutoff): core1RDM_dmet[cnt] = 2.0 else: print( "Bad DMET bath orbital selection: trying to put a bath orbital with occupation", core1RDM_dmet[cnt], "into the environment :-(.") assert (0 == 1) Norb_in_imp = numImpOrbs + numBathOrbs Nelec_in_imp = int(round(self.ints.Nelec - np.sum(core1RDM_dmet))) core1RDM_loc = np.dot(np.dot(loc2dmet, np.diag(core1RDM_dmet)), loc2dmet.T) self.dmetOrbs.append( loc2dmet[:, :Norb_in_imp]) # Impurity and bath orbitals only assert (Norb_in_imp <= self.Norb) dmetOEI = self.ints.dmet_oei(loc2dmet, Norb_in_imp) dmetFOCK = self.ints.dmet_fock(loc2dmet, Norb_in_imp, core1RDM_loc) dmetTEI = self.ints.dmet_tei(loc2dmet, Norb_in_imp) if (self.NI_hack == True): dmetTEI[:, :, :, numImpOrbs:] = 0.0 dmetTEI[:, :, numImpOrbs:, :] = 0.0 dmetTEI[:, numImpOrbs:, :, :] = 0.0 dmetTEI[numImpOrbs:, :, :, :] = 0.0 umat_rotated = np.dot(np.dot(loc2dmet.T, self.umat), loc2dmet) umat_rotated[:numImpOrbs, :numImpOrbs] = 0.0 dmetOEI += umat_rotated[:Norb_in_imp, :Norb_in_imp] dmetFOCK = np.array(dmetOEI, copy=True) print("DMET::exact : Performing a (", Norb_in_imp, "orb,", Nelec_in_imp, "el ) DMET active space calculation.") if (flag_rhf): import pyscf_rhf DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM = pyscf_rhf.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, self.CC_E_TYPE, chempot_imp) elif (flag_rhf and self.UHF == True): import pyscf_uhf DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM = pyscf_uhf.solve(0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp) elif (self.method == 'ED'): import chemps2 IMP_energy, IMP_1RDM = chemps2.solve(0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, chempot_imp) elif (self.method == 'CC'): import pyscf_cc assert (Nelec_in_imp % 2 == 0) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM = pyscf_cc.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, self.CC_E_TYPE, chempot_imp) elif (self.method == 'MP2'): import pyscf_mp2 assert (Nelec_in_imp % 2 == 0) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM = pyscf_mp2.solve(0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, chempot_imp) elif (self.method == 'CASSCF'): import pyscf_casscf assert (Nelec_in_imp % 2 == 0) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM, MOmf, MO, MOnat, OccNum = pyscf_casscf.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, self.impCAS, self.CASlist, DMguessRHF, self.CC_E_TYPE, chempot_imp) self.MOmf = MOmf self.MO = MO #the MO is updated eveytime the CASSCF solver called self.MOnat = MOnat self.loc2dmet = loc2dmet #similar to MO [:,:Norb_in_imp] self.OccNum = OccNum elif (self.method == 'RHF'): import pyscf_rhf assert (Nelec_in_imp % 2 == 0) DMguessRHF = self.ints.dmet_init_guess_rhf( loc2dmet, Norb_in_imp, Nelec_in_imp // 2, numImpOrbs, chempot_imp) IMP_energy, IMP_1RDM = pyscf_rhf.solve( 0.0, dmetOEI, dmetFOCK, dmetTEI, Norb_in_imp, Nelec_in_imp, numImpOrbs, DMguessRHF, self.CC_E_TYPE, chempot_imp) self.energy += IMP_energy E_frag.append(IMP_energy) self.imp_1RDM.append(IMP_1RDM) if (self.doDET == True) and (self.doDET_NO == True): RDMeigenvals, RDMeigenvecs = np.linalg.eigh( IMP_1RDM[:numImpOrbs, :numImpOrbs]) self.NOvecs.append(RDMeigenvecs) self.NOdiag.append(RDMeigenvals) remainingOrbs -= impurityOrbs if (self.doDET == True) and (self.doDET_NO == True): self.NOrotation = self.constructNOrotation() Nelectrons = 0.0 Nefrag = [] for counter in range(maxiter): Nelectrons += np.trace( self.imp_1RDM[counter] [:self.imp_size[counter], :self.imp_size[counter]]) Nefrag.append( np.trace(self.imp_1RDM[counter] [:self.imp_size[counter], :self.imp_size[counter]])) if (self.TransInv == True): Nelectrons = Nelectrons * len(self.impClust) self.energy = self.energy * len(self.impClust) remainingOrbs[:] = 0 #HungPham print('Fragment energies:', E_frag) print('Fragment electrons:', Nefrag) E1 = self.energy print('DEBUG Energy before adding up envi contribution:', E1) # When an incomplete impurity tiling is used for the Hamiltonian, self.energy should be augmented with the remaining HF part if (np.sum(remainingOrbs) != 0): if (self.CC_E_TYPE == 'CASCI'): ''' If CASCI is passed as CC energy type, the energy of the one and only full impurity Hamiltonian is returned. The one-electron integrals of this impurity Hamiltonian is the full Fock operator of the CORE orbitals! The constant part of the energy still needs to be added: sum_occ ( 2 * OEI[occ,occ] + JK[occ,occ] ) = einsum( core1RDM_loc, OEI ) + 0.5 * einsum( core1RDM_loc, JK ) = 0.5 * einsum( core1RDM_loc, OEI + FOCK ) ''' assert (maxiter == 1) print("-----NOTE: CASCI or Single embedding is used-----") transfo = np.eye(self.Norb, dtype=float) totalOEI = self.ints.dmet_oei(transfo, self.Norb) totalFOCK = self.ints.dmet_fock(transfo, self.Norb, core1RDM_loc) self.energy += 0.5 * np.einsum('ij,ij->', core1RDM_loc, totalOEI + totalFOCK) Nelectrons = np.trace(self.imp_1RDM[0]) + np.trace( core1RDM_loc ) # Because full active space is used to compute the energy else: #transfo = np.eye( self.Norb, dtype=float ) #totalOEI = self.ints.dmet_oei( transfo, self.Norb ) #totalFOCK = self.ints.dmet_fock( transfo, self.Norb, OneRDM ) #self.energy += 0.5 * np.einsum( 'ij,ij->', OneRDM[remainingOrbs==1,:], \ # totalOEI[remainingOrbs==1,:] + totalFOCK[remainingOrbs==1,:] ) #Nelectrons += np.trace( (OneRDM[remainingOrbs==1,:])[:,remainingOrbs==1] ) assert (np.array_equal( self.ints.active, np.ones([self.ints.mol.nao_nr()], dtype=int))) from pyscf import scf from types import MethodType mol_ = self.ints.mol mf_ = scf.RHF(mol_) impOrbs = remainingOrbs == 1 xorb = np.dot(mf_.get_ovlp(), self.ints.ao2loc) hc = -chempot_imp * np.dot(xorb[:, impOrbs], xorb[:, impOrbs].T) dm0 = np.dot(self.ints.ao2loc, np.dot(OneRDM, self.ints.ao2loc.T)) def mf_hcore(self, mol=None): if mol is None: mol = self.mol return scf.hf.get_hcore(mol) + hc mf_.get_hcore = MethodType(mf_hcore, mf_) mf_.scf(dm0) assert (mf_.converged) rdm1 = mf_.make_rdm1() jk = mf_.get_veff(dm=rdm1) xorb = np.dot(mf_.get_ovlp(), self.ints.ao2loc) rdm1 = np.dot(xorb.T, np.dot(rdm1, xorb)) oei = np.dot(self.ints.ao2loc.T, np.dot(mf_.get_hcore() - hc, self.ints.ao2loc)) jk = np.dot(self.ints.ao2loc.T, np.dot(jk, self.ints.ao2loc)) ImpEnergy = \ + 0.50 * np.einsum('ji,ij->', rdm1[:,impOrbs], oei[impOrbs,:]) \ + 0.50 * np.einsum('ji,ij->', rdm1[impOrbs,:], oei[:,impOrbs]) \ + 0.25 * np.einsum('ji,ij->', rdm1[:,impOrbs], jk[impOrbs,:]) \ + 0.25 * np.einsum('ji,ij->', rdm1[impOrbs,:], jk[:,impOrbs]) self.energy += ImpEnergy Nelectrons += np.trace(rdm1[np.ix_(impOrbs, impOrbs)]) remainingOrbs[remainingOrbs == 1] -= 1 assert (np.all(remainingOrbs == 0)) print('Energy decomposition for debug:', E1, self.energy - E1, self.energy) #HP: for debug print('Nuclear potential:', self.ints.const()) self.energy += self.ints.const() return Nelectrons