def calc_localized_orbitals(mf,mol,method='ER',jmol=False): # mf ... pyscf calculation object # mol ... pyscf geometry object # method ... localization method: ER, FB, PM # jmol ... debug option to check furher 3d information files # Localization. # Spin 1. # only occupied orbitals mo_occ = mf.mo_coeff[0][:,mf.mo_occ[0]>0] if method == 'ER': loc1 = edmiston.Edmiston(mol, mo_occ) if method == 'FB': loc1 = boys.Boys(mol, mo_occ) if method == 'PM': loc1 = pipek.PipekMezey(mol, mo_occ) orb1 = loc1.kernel() # Spin 2 # only occupied orbitals mo_occ = mf.mo_coeff[1][:,mf.mo_occ[1]>0] if method == 'ER': loc2 = edmiston.Edmiston(mol, mo_occ) if method == 'FB': loc2 = boys.Boys(mol, mo_occ) if method == 'PM': loc2 = pipek.PipekMezey(mol, mo_occ) orb2 = loc2.kernel() # Write orbitals for jmol format. if jmol == True: # Spin 1. tools.molden.from_mo(mol, 'orb1.molden', orb1) with open('orb1.spt', 'w') as f: f.write('load orb1.molden; isoSurface MO 001;\n') # Spin 2. tools.molden.from_mo(mol, 'orb2.molden', orb2) with open('orb2.spt', 'w') as f: f.write('load orb2.molden; isoSurface MO 001;\n') # Write orbitals in cube format. # Spin 1. occ = len(mf.mo_coeff[0][mf.mo_occ[0] == 1]) for i in range(occ): orbital(mol, str(method)+'_orb_'+str(i)+'spin1.cube', orb1[:,i], nx=80, ny=80, nz=80) # Spin 2. occ = len(mf.mo_coeff[1][mf.mo_occ[1] == 1]) for i in range(occ): orbital(mol, str(method)+'_orb_'+str(i)+'spin2.cube', orb2[:,i], nx=80, ny=80, nz=80)
def relocalize_states (self, loc2bas, fragments, oneRDM_loc, natorb=False, canonicalize=False): '''Do Boys localization on a subspace and assign resulting states to the various fragments using projection operators. Optionally diagonalize either the fock or the density matrix inside each subspace. Canonicalize overrides natorb''' fock_loc = self.loc_rhf_fock_bis (oneRDM_loc) ao2bas = boys.Boys (self.mol, np.dot (self.ao2loc, loc2bas)).kernel () loc2bas = reduce (np.dot, [self.ao2loc.conjugate ().T, self.ao_ovlp, ao2bas]) weights = np.asarray ([np.einsum ('ip,ip->p', loc2bas[f.frag_orb_list,:].conjugate (), loc2bas[f.frag_orb_list,:]) for f in fragments]) frag_assignments = np.argmax (weights, axis=0) loc2bas_assigned = [] for idx, frag in enumerate (fragments): pick_orbs = (frag_assignments == idx) norbs = np.count_nonzero (pick_orbs) print ("{} states found for fragment {}".format (norbs, frag.frag_name)) loc2pick = loc2bas[:,pick_orbs] if canonicalize and norbs: f = represent_operator_in_basis (fock_loc, loc2pick) evals, evecs = matrix_eigen_control_options (f, sort_vecs=1, only_nonzero_vals=False) loc2pick = np.dot (loc2pick, evecs) elif natorb and norbs: f = represent_operator_in_basis (oneRDM_loc, loc2pick) evals, evecs = matrix_eigen_control_options (f, sort_vecs=-1, only_nonzero_vals=False) loc2pick = np.dot (loc2pick, evecs) loc2bas_assigned.append (loc2pick) return loc2bas_assigned
def examine_wmcs (dmet_obj): loc2wmas = np.concatenate ([f.loc2amo for f in dmet_obj.fragments], axis=1) loc2wmcs = get_complementary_states (loc2wmas) print ("Examining whole-molecule active space:") loc2wmas = dmet_obj.ints.compare_basis_to_loc (loc2wmas, dmet_obj.fragments, quiet=False)[0] norbs_wmas = np.array ([f.norbs_as for f in dmet_obj.fragments]) print ("Examining whole-molecule core space:") norbs_wmcs_before = dmet_obj.ints.compare_basis_to_loc (loc2wmcs, dmet_obj.fragments, quiet=True)[1] norbs_before = norbs_wmas + norbs_wmcs_before ao2loc = dmet_obj.ints.ao2loc loc2ao = ao2loc.conjugate ().T ao2wmcs = np.dot (ao2loc, loc2wmcs) ao2wmcs_new = boys.Boys (dmet_obj.ints.mol, ao2wmcs).kernel () aoOao_inv = np.linalg.inv (np.dot (ao2loc, loc2ao)) loc2wmcs_new = reduce (np.dot, [loc2ao, aoOao_inv, ao2wmcs_new]) loc2wmcs_new, norbs_wmcs_after = dmet_obj.ints.compare_basis_to_loc (loc2wmcs_new, dmet_obj.fragments, quiet=False) norbs_after = norbs_wmas + norbs_wmcs_after loc2new = np.append (loc2wmas, loc2wmcs_new, axis=1) active_flag = np.append (np.ones (loc2wmas.shape[1]), np.zeros (loc2wmcs_new.shape[1])) print ("Is the new basis orthonormal and complete? {0}".format (is_basis_orthonormal_and_complete (loc2new))) print ("Fragment-orbital assignment breakdown:") print ("Frag Before After") for frag, bef, aft in zip (dmet_obj.fragments, norbs_before, norbs_after): print ('{:>4s} {:6d} {:5d}'.format (frag.frag_name, int (bef), int (aft))) filename = 'relocalized_basis.molden' mol = dmet_obj.ints.mol.copy () ao2new = np.dot (dmet_obj.ints.ao2loc, loc2new) molden.from_mo (mol, filename, ao2new, occ=active_flag) '''
def test_boys(self): idx = numpy.array([17, 20, 21, 22, 23, 30, 36, 41, 42, 47, 48, 49]) - 1 loc = boys.Boys(mol, mf.mo_coeff[:, idx]) loc.max_cycle = 100 mo = loc.kernel() dip = boys.dipole_integral(mol, mo) z = numpy.einsum('xii,xii->', dip, dip) self.assertAlmostEqual(z, 98.670988758151907, 4)
def localize(mol, mf, method): if (method == "lowdin"): return fractional_matrix_power(mf.get_ovlp(mol), -0.5).T elif (method == "pm"): return pipek.PM(mol).kernel(mf.mo_coeff) elif (method == "pmLowdin"): lowdin = fractional_matrix_power(mf.get_ovlp(mol), -0.5).T return pipek.PM(mol).kernel(lowdin) elif (method == "boys"): return boys.Boys(mol).kernel(mf.mo_coeff)
def localizeValence(mf, mo_coeff, method="iao"): if (method == "iao"): return iao.iao(mf.mol, mo_coeff) elif (method == "ibo"): a = iao.iao(mf.mol, mo_coeff) a = lo.vec_lowdin(a, mf.get_ovlp()) return ibo.ibo(mf.mol, mo_coeff, iaos=a) elif (method == "boys"): return boys.Boys(mf.mol).kernel(mo_coeff) elif (method == "er"): return edmiston.ER(mf.mol).kernel(mo_coeff)
def test_pipek(self): idx = numpy.array([17, 20, 21, 22, 23, 30, 36, 41, 42, 47, 48, 49]) - 1 # Initial guess from Boys localization. Otherwise uncertainty between # two solutions found in PM kernel mo = boys.Boys(mol, mf.mo_coeff[:, idx]).kernel() loc = pipek.PipekMezey(mol, mo) loc.max_cycle = 100 mo = loc.kernel() pop = pipek.atomic_pops(mol, mo) z = numpy.einsum('xii,xii->', pop, pop) self.assertAlmostEqual(z, 12, 4)
def test_boys(self): idx = numpy.array([17, 20, 21, 22, 23, 30, 36, 41, 42, 47, 48, 49]) - 1 loc = boys.Boys(mol) mo = loc.kernel(mf.mo_coeff[:, idx]) dip = boys.dipole_integral(mol, mo) z = numpy.einsum('xii,xii->', dip, dip) self.assertAlmostEqual(z, 98.670988758151907, 9) mo = loc.kernel(mf.mo_coeff[:, idx + 1]) dip = boys.dipole_integral(mol, mo) z = numpy.einsum('xii,xii->', dip, dip) self.assertAlmostEqual(z, 27.481320331665497, 9)
def localizeAllElectron(mf, method="lowdin"): if (method == "lowdin"): return fractional_matrix_power(mf.get_ovlp(), -0.5).T elif (method == "pm"): return pipek.PM(mf.mol).kernel(mf.mo_coeff) elif (method == "boys"): return boys.Boys(mf.mol).kernel(mf.mo_coeff) elif (method == "er"): return edmiston.ER(mf.mol).kernel(mf.mo_coeff) elif (method == "iao"): return iao.iao(mf.mol, mf.mo_coeff) elif (method == "ibo"): a = iao.iao(mf.mol, mf.mo_coeff) a = lo.vec_lowdin(a, mf.get_ovlp()) return ibo.ibo(mf.mol, mf.mo_coeff, iaos=a)
def __init__( self, the_mf, active_orbs, localizationtype, ao_rotation=None, use_full_hessian=True, localization_threshold=1e-6 ): assert (( localizationtype == 'meta_lowdin' ) or ( localizationtype == 'boys' ) or ( localizationtype == 'lowdin' ) or ( localizationtype == 'iao' )) self.num_mf_stab_checks = 0 # Information on the full HF problem self.mol = the_mf.mol self._scf = the_mf self.max_memory = the_mf.max_memory self.get_jk_ao = partial (the_mf.get_jk, self.mol) self.get_veff_ao = partial (the_mf.get_veff, self.mol) self.get_k_ao = partial (the_mf.get_k, self.mol) self.fullovlpao = the_mf.get_ovlp self.fullEhf = the_mf.e_tot self.fullRDM_ao = np.asarray (the_mf.make_rdm1 ()) if self.fullRDM_ao.ndim == 3: self.fullSDM_ao = self.fullRDM_ao[0] - self.fullRDM_ao[1] self.fullRDM_ao = self.fullRDM_ao[0] + self.fullRDM_ao[1] else: self.fullSDM_ao = np.zeros_like (self.fullRDM_ao) self.fullJK_ao = self.get_veff_ao (dm=self.fullRDM_ao, dm_last=0, vhf_last=0, hermi=1) #Last 3 numbers: dm_last, vhf_last, hermi if self.fullJK_ao.ndim == 3: self.fullJK_ao = self.fullJK_ao[0] # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical self.fullFOCK_ao = the_mf.get_hcore () + self.fullJK_ao self.e_tot = the_mf.e_tot self.x2c = isinstance (the_mf, x2c._X2C_SCF) # Active space information self._which = localizationtype self.active = np.zeros( [ self.mol.nao_nr() ], dtype=int ) self.active[ active_orbs ] = 1 self.norbs_tot = np.sum( self.active ) # Number of active space orbitals self.nelec_tot = int(np.rint( self.mol.nelectron - np.sum( the_mf.mo_occ[ self.active==0 ] ))) # Total number of electrons minus frozen part # Localize the orbitals if (( self._which == 'meta_lowdin' ) or ( self._which == 'boys' )): if ( self._which == 'meta_lowdin' ): assert( self.norbs_tot == self.mol.nao_nr() ) # Full active space required if ( self._which == 'boys' ): self.ao2loc = the_mf.mo_coeff[ : , self.active==1 ] if ( self.norbs_tot == self.mol.nao_nr() ): # If you want the full active, do meta-Lowdin nao.AOSHELL[4] = ['1s0p0d0f', '2s1p0d0f'] # redefine the valence shell for Be self.ao2loc = orth.orth_ao( self.mol, 'meta_lowdin' ) if ( ao_rotation != None ): self.ao2loc = np.dot( self.ao2loc, ao_rotation.T ) if ( self._which == 'boys' ): old_verbose = self.mol.verbose self.mol.verbose = 5 loc = boys.Boys (self.mol, self.ao2loc) # loc = localizer.localizer( self.mol, self.ao2loc, self._which, use_full_hessian ) self.mol.verbose = old_verbose # self.ao2loc = loc.optimize( threshold=localization_threshold ) self.ao2loc = loc.kernel () self.TI_OK = False # Check yourself if OK, then overwrite if ( self._which == 'lowdin' ): assert( self.norbs_tot == self.mol.nao_nr() ) # Full active space required ovlp = self.mol.intor('cint1e_ovlp_sph') ovlp_eigs, ovlp_vecs = np.linalg.eigh( ovlp ) assert ( np.linalg.norm( np.dot( np.dot( ovlp_vecs, np.diag( ovlp_eigs ) ), ovlp_vecs.T ) - ovlp ) < 1e-10 ) self.ao2loc = np.dot( np.dot( ovlp_vecs, np.diag( np.power( ovlp_eigs, -0.5 ) ) ), ovlp_vecs.T ) self.TI_OK = False # Check yourself if OK, then overwrite if ( self._which == 'iao' ): assert( self.norbs_tot == self.mol.nao_nr() ) # Full active space assumed self.ao2loc = iao_helper.localize_iao( self.mol, the_mf ) if ( ao_rotation != None ): self.ao2loc = np.dot( self.ao2loc, ao_rotation.T ) self.TI_OK = False # Check yourself if OK, then overwrite #self.molden( 'dump.molden' ) # Debugging mode assert( self.loc_ortho() < 1e-8 ) # Stored inverse overlap matrix self.ao_ovlp_inv = np.dot (self.ao2loc, self.ao2loc.conjugate ().T) self.ao_ovlp = the_mf.get_ovlp () assert (is_matrix_eye (np.dot (self.ao_ovlp, self.ao_ovlp_inv))) # Effective Hamiltonian due to frozen part self.frozenDM_mo = np.array( the_mf.mo_occ, copy=True ) self.frozenDM_mo[ self.active==1 ] = 0 # Only the frozen MO occupancies nonzero self.frozenDM_ao = np.dot(np.dot( the_mf.mo_coeff, np.diag( self.frozenDM_mo )), the_mf.mo_coeff.T ) self.frozenJK_ao = self.get_veff_ao (self.frozenDM_ao, 0, 0, 1 ) #Last 3 numbers: dm_last, vhf_last, hermi if self.frozenJK_ao.ndim == 3: self.frozenJK_ao = self.frozenJK_ao[0] # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical self.frozenOEI_ao = self.fullFOCK_ao - self.fullJK_ao + self.frozenJK_ao # Localized OEI and ERI self.activeCONST = self.mol.energy_nuc() + np.einsum( 'ij,ij->', self.frozenOEI_ao - 0.5*self.frozenJK_ao, self.frozenDM_ao ) self.activeOEI = represent_operator_in_basis (self.frozenOEI_ao, self.ao2loc ) self.activeFOCK = represent_operator_in_basis (self.fullFOCK_ao, self.ao2loc ) self.activeVSPIN = np.zeros_like (self.activeFOCK) # FIXME: correct behavior for ROHF init! self.activeJKidem = self.activeFOCK - self.activeOEI self.activeJKcorr = np.zeros ((self.norbs_tot, self.norbs_tot), dtype=self.activeOEI.dtype) self.oneRDM_loc = self.ao2loc.conjugate ().T @ self.ao_ovlp @ self.fullRDM_ao @ self.ao_ovlp @ self.ao2loc self.oneSDM_loc = self.ao2loc.conjugate ().T @ self.ao_ovlp @ self.fullSDM_ao @ self.ao_ovlp @ self.ao2loc self.oneRDMcorr_loc = np.zeros ((self.norbs_tot, self.norbs_tot), dtype=self.activeOEI.dtype) self.loc2idem = np.eye (self.norbs_tot, dtype=self.activeOEI.dtype) self.nelec_idem = self.nelec_tot self._eri = None self.with_df = None assert (abs (np.trace (self.oneRDM_loc) - self.nelec_tot) < 1e-8), '{} {}'.format (np.trace (self.oneRDM_loc), self.nelec_tot) sys.stdout.flush () def _is_mem_enough (): return 2*(self.norbs_tot**4)/1e6 + current_memory ()[0] < self.max_memory*0.95 # Unfortunately, there is currently no way to do the integral transformation directly on the antisymmetrized two-electron # integrals, at least none already implemented in PySCF. Therefore the smallest possible memory footprint involves # two arrays of fourfold symmetry, which works out to roughly one half of an array with no symmetry if hasattr (the_mf, 'with_df') and hasattr (the_mf.with_df, '_cderi') and the_mf.with_df._cderi is not None: print ("Found density-fitting three-center integrals scf object") loc2ao = self.ao2loc.conjugate ().T locOao = np.dot (loc2ao, self.ao_ovlp) self.with_df = the_mf.with_df self.with_df.loc2eri_bas = lambda x: np.dot (self.ao2loc, x) self.with_df.loc2eri_op = lambda x: reduce (np.dot, (self.ao2loc, x, loc2ao)) self.with_df.eri2loc_bas = lambda x: np.dot (locOao, x) self.with_df.eri2loc_op = lambda x: reduce (np.dot, (loc2ao, x, self.ao2loc)) elif the_mf._eri is not None: print ("Found eris on scf object") loc2ao = self.ao2loc.conjugate ().T locOao = np.dot (loc2ao, self.ao_ovlp) self._eri = the_mf._eri self._eri = tag_array (self._eri, loc2eri_bas = lambda x: np.dot (self.ao2loc, x)) self._eri = tag_array (self._eri, loc2eri_op = lambda x: reduce (np.dot, (self.ao2loc, x, loc2ao))) self._eri = tag_array (self._eri, eri2loc_bas = lambda x: np.dot (locOao, x)) self._eri = tag_array (self._eri, eri2loc_op = lambda x: reduce (np.dot, (loc2ao, x, self.ao2loc))) elif _is_mem_enough (): print ("Storing eris in memory") self._eri = ao2mo.restore (8, ao2mo.outcore.full_iofree (self.mol, self.ao2loc, compact=True), self.norbs_tot) self._eri = tag_array (self._eri, loc2eri_bas = lambda x: x) self._eri = tag_array (self._eri, loc2eri_op = lambda x: x) self._eri = tag_array (self._eri, eri2loc_bas = lambda x: x) self._eri = tag_array (self._eri, eri2loc_op = lambda x: x) else: print ("Direct calculation") sys.stdout.flush () # Symmetry information try: self.loc2symm = [orthonormalize_a_basis (scipy.linalg.solve (self.ao2loc, ao2ir)) for ao2ir in self.mol.symm_orb] self.symmetry = self.mol.groupname self.wfnsym = the_mf.wfnsym self.ir_names = self.mol.irrep_name self.ir_ids = self.mol.irrep_id self.enforce_symmetry = True except (AttributeError, TypeError) as e: if self.mol.symmetry: raise (e) self.loc2symm = [np.eye (self.norbs_tot)] self.symmetry = False self.wfnsym = 'A' self.ir_names = ['A'] self.ir_ids = [0] self.enforce_symmetry = False print ("Initial loc2symm nonorthonormality: {}".format (measure_basis_nonorthonormality (np.concatenate (self.loc2symm, axis=1)))) for loc2ir1, loc2ir2 in itertools.combinations (self.loc2symm, 2): proj = loc2ir1 @ loc2ir1.conjugate ().T loc2ir2[:,:] -= proj @ loc2ir2 for loc2ir in self.loc2symm: loc2ir[:,:] = orthonormalize_a_basis (loc2ir) print ("Final loc2symm nonorthonormality: {}".format (measure_basis_nonorthonormality (np.concatenate (self.loc2symm, axis=1))))
def test_1orbital(self): lmo = boys.Boys(mol, mf.mo_coeff[:, :1]).kernel() self.assertTrue(numpy.all(mf.mo_coeff[:, :1] == lmo)) lmo = boys.Boys(mol, mf.mo_coeff[:, :0]).kernel() self.assertTrue(lmo.size == 0)
mol.basis = '6-31g' mol.symmetry = 0 mol.charge = 0 mol.spin = 0 mol.verbose = 4 mol.build() mf = scf.RHF( mol ) mf.verbose = 4 mf.scf() from pyscf.lo import boys import numpy mo = mf.mo_coeff[:,[17, 20, 21, 22, 23, 30, 36, 41, 42, 47, 48, 49]] nmo = mo.shape[1] u = numpy.linalg.svd(numpy.eye(nmo)+1e-5*numpy.random.random((nmo,nmo)))[0] boys.Boys(mol).kernel(mo.dot(u), verbose=4) #filename_mo = 'benzene-631g-mo.molden' #filename_boys = 'benzene-631g-boys.molden' # #with open( filename_mo, 'w' ) as thefile: # molden.header( mol, thefile ) # molden.orbital_coeff( mol, thefile, mf.mo_coeff ) #print("Molecular orbitals saved in", filename_mo) # ## Localize the pi-type orbitals. Counting starts from 0! 12 orbitals as 6-31G is DZ. #tolocalize = np.array([17, 20, 21, 22, 23, 30, 36, 41, 42, 47, 48, 49]) - 1 #loc = localizer.localizer( mol, mf.mo_coeff[:,tolocalize], 'boys' ) #loc.verbose = param.VERBOSE_DEBUG #new_coeff = loc.optimize() #loc.dump_molden( filename_boys, new_coeff )