def test_intor_cross(self): mol1 = gto.M(atom='He', basis={'He': [(2,(1.,1))]}, cart=True) s0 = gto.intor_cross('int1e_ovlp', mol1, mol0) self.assertEqual(s0.shape, (6, 34)) s0 = gto.intor_cross('int1e_ovlp', mol0, mol1) self.assertEqual(s0.shape, (34, 6)) s0 = gto.intor_cross('int1e_ovlp_cart', mol0, mol1) self.assertEqual(s0.shape, (36, 6))
def test_intor_cross(self): mol1 = gto.M(atom='He', basis={'He': [(2, (1., 1))]}, cart=True) s0 = gto.intor_cross('int1e_ovlp', mol1, mol0) self.assertEqual(s0.shape, (6, 34)) s0 = gto.intor_cross('int1e_ovlp', mol0, mol1) self.assertEqual(s0.shape, (34, 6)) s0 = gto.intor_cross('int1e_ovlp_cart', mol0, mol1) self.assertEqual(s0.shape, (36, 6))
def core2(xmol, opt, libint = False, clean = True): if libint: # use libint integrals # dir binpath, tmppath = get_dir() binpath += 'hf2' # label labelpath = tmppath + '2' + str(opt) with open(labelpath, 'wb') as f: f.write(str(opt)) # atom atmpath = labelpath + 'a' with open(atmpath, 'wb') as f: f.write(atom2xyz(xmol.mol)) # basis bs1 = basis2g94(xmol.mol.basis, xmol.dic) bs2 = basis2g94(xmol.Vmol.basis, xmol.dic) # run cmd00 = make_command((binpath, str(opt), atmpath, bs1, bs1, labelpath + 'o00')) cprint(cmd00) os.system(cmd00) cmd01 = make_command((binpath, str(opt), atmpath, bs1, bs2, labelpath + 'o01')) cprint(cmd01) os.system(cmd01) '''cmd10 = make_command((binpath, str(opt), atmpath, bs2, bs1, labelpath + 'o10')) os.system(cmd10)''' cmd11 = make_command((binpath, str(opt), atmpath, bs2, bs2, labelpath + 'o11')) cprint(cmd11) os.system(cmd11) # load nh = xmol.nHAO nc = xmol.nCAO n = nh + nc AO00 = loadmatrix2((nh, nh), labelpath + 'o00') AO01 = loadmatrix2((nh, nc), labelpath + 'o01') AO11 = loadmatrix2((nc, nc), labelpath + 'o11') # clean if clean: shutil.rmtree(tmppath) # return out = np.zeros((n, n)) out[:nh, :nh] = AO00 out[:nh, nh:] = AO01 out[nh:, :nh] = HerConj(AO01) out[nh:, nh:] = AO11 return out else: # use pyscf f00 = gto.intor_cross(coredic2[opt], xmol.mol, xmol.mol) f01 = gto.intor_cross(coredic2[opt], xmol.mol, xmol.Vmol) f11 = gto.intor_cross(coredic2[opt], xmol.Vmol, xmol.Vmol) if opt == 1: # nuc integral from pyscf counts twice return np.hstack((np.vstack((f00, HerConj(f01))), np.vstack((f01, f11)))) * 0.5 else: return np.hstack((np.vstack((f00, HerConj(f01))), np.vstack((f01, f11))))
def test_mo_1to1map(self): mol1 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='6-31g') mol2 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='ccpvdz') s = gto.intor_cross('cint1e_ovlp_sph', mol1, mol2) idx = mo_mapping.mo_1to1map(s) self.assertTrue(numpy.allclose(idx, [0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22]))
def ecp_ano_det_ovlp(atm_ecp, atm_ano, ecpcore): ecp_ao_loc = atm_ecp.ao_loc_nr() ano_ao_loc = atm_ano.ao_loc_nr() ecp_ao_dim = ecp_ao_loc[1:] - ecp_ao_loc[:-1] ano_ao_dim = ano_ao_loc[1:] - ano_ao_loc[:-1] ecp_bas_l = [[atm_ecp.bas_angular(i)] * d for i, d in enumerate(ecp_ao_dim)] ano_bas_l = [[atm_ano.bas_angular(i)] * d for i, d in enumerate(ano_ao_dim)] ecp_bas_l = numpy.hstack(ecp_bas_l) ano_bas_l = numpy.hstack(ano_bas_l) ecp_idx = [] ano_idx = [] for l in range(4): nocc, nfrac = atom_hf.frac_occ(stdsymb, l) if nfrac > 1e-15: nocc += 1 if nocc == 0: break i0 = ecpcore[l] * (2 * l + 1) i1 = nocc * (2 * l + 1) ecp_idx.append(numpy.where(ecp_bas_l == l)[0][:i1 - i0]) ano_idx.append(numpy.where(ano_bas_l == l)[0][i0:i1]) ecp_idx = numpy.hstack(ecp_idx) ano_idx = numpy.hstack(ano_idx) s12 = gto.intor_cross('int1e_ovlp', atm_ecp, atm_ano)[ecp_idx][:, ano_idx] return numpy.linalg.det(s12)
def core2hf_(mf, opt, dic = None, libint = False, clean = True): if libint: # dir binpath, tmppath = get_dir() binpath += 'hf2' # label labelpath = tmppath + '2' + str(opt) with open(labelpath, 'wb') as f: f.write(str(opt)) # atom atmpath = labelpath + 'a' with open(atmpath, 'wb') as f: f.write(atom2xyz(mf.mol)) # basis bs1 = basis2g94(mf.mol.basis, dic) # run cmd00 = make_command((binpath, str(opt), atmpath, bs1, bs1, labelpath + 'o00')) cprint(cmd00) os.system(cmd00) # load n = mf.mo_coeff.shape[0] AO00 = loadmatrix2((n, n), labelpath + 'o00') # clean if clean: shutil.rmtree(tmppath) # return return AO00 else: f00 = gto.intor_cross(coredic2[opt], mf.mol, mf.mol) if opt == 1: return 0.5 * f00 else: return f00
def mo_map(mol1, mo1, mol2, mo2, base=1, tol=.5): s = gto.intor_cross('cint1e_ovlp_sph', mol1, mol2) s = reduce(numpy.dot, (mo1.T, s, mo2)) idx = numpy.argwhere(abs(s) > tol) for i,j in idx: logger.info(mol1, '<mo-1|mo-2> %d %d %12.8f', i+base, j+base, s[i,j]) return idx, s
def __init__(self, mf, CABSbasis = None, CABSp = False, name = 'xMole'): self.dic = dictg94() self.f12 = (-0.27070, -0.19532, -0.30552, -0.81920, -0.18297, -2.85917, -0.10986, -9.50073, -0.06810, -35.69989, -0.04224, -197.79328) self._vhf = mf self.mol = mf.mol molname = name.replace(' ','').replace('/','').replace('\\','') if molname == '': self.molname = 'xMole' self.molname = molname # TODO: use multi bases if CABSbasis is None: CABSbasis = mf.mol.basis + '_optri' self.Vmol = gto.M(atom = mf.mol.atom, basis = CABSbasis) self.CABSp = CABSp # use CABS+ or CABS if CABSp: ovlp1 = gto.intor_cross('cint1e_ovlp_sph', self.mol, self.mol) ovlp2 = gto.intor_cross('cint1e_ovlp_sph', self.Vmol, self.mol) #S12t = np.dot(np.vstack((ovlp1, ovlp2)), mf.mo_coeff) # There are not differences whether to use <AO|AO> matrix or <MO|AO> S12t = np.vstack((ovlp1, ovlp2)) Vt, S, Ut = np.linalg.svd(S12t) rnk = rankcount(S) (self.nHAO, self.nHMO) = np.shape(mf.mo_coeff) (self.nCAO, self.nCMO) = np.shape(Vt) self.nCAO -= self.nHAO self.nCMO -= rnk self.Xmo_coeff = np.hstack((np.vstack((mf.mo_coeff, np.zeros((self.nCAO, self.nHMO)))), Vt[:, rnk:])) else: ovlpx = gto.intor_cross('cint1e_ovlp_sph', self.Vmol, self.mol) Vt, S, Ut = np.linalg.svd(ovlpx) rnk = rankcount(S) C = Vt[:, rnk:] (self.nHAO, self.nHMO) = np.shape(mf.mo_coeff) (self.nCAO, self.nCMO) = np.shape(C) self.Xmo_coeff = np.zeros((self.nHAO + self.nCAO, self.nHMO + self.nCMO)) self.Xmo_coeff[:self.nHAO, :self.nHMO] = mf.mo_coeff self.Xmo_coeff[self.nHAO:, self.nHMO:] = C # mo numbers self.no = mf.mol.nelectron // 2 self.nv = self.nHMO - self.no self.nc = self.nCMO cprint(self.Xmo_coeff.shape)
def get_locorb(mf, localize='pm', pair=True): mol = mf.mol mo = mf.mo_coeff nbf = mf.mo_coeff.shape[0] nif = mf.mo_coeff.shape[1] mol2 = mf.mol.copy() mol2.basis = 'sto-6g' mol2.build() mf2 = scf.RHF(mol2) mf2.max_cycle = 150 #dm = mf2.from_chk('loc_rhf_proj.chk') mf2.kernel() mo2 = mf2.mo_coeff #nbf2 = mf2.mo_coeff.shape[0] #nif2 = mf2.mo_coeff.shape[1] idx = np.count_nonzero(mf.mo_occ) cross_S = gto.intor_cross('int1e_ovlp', mol, mol2) print(idx, mo.shape, mo2.shape, cross_S.shape) vir_cross = einsum('ji,jk,kl->il', mo[:, idx:], cross_S, mo2[:, idx:]) u, s, v = scipy.linalg.svd(vir_cross) print('SVD', s) projmo = np.dot(mo[:, idx:], u) mf.mo_coeff[:, idx:] = projmo npair = np.sum(mf2.mo_occ == 0) print('npair', npair) idx2 = np.count_nonzero(mf.mo_occ) ncore = chemcore(mol) idx1 = min(idx2 - npair, ncore) idx3 = idx2 + npair print('MOs after projection') dump_mat.dump_mo(mf.mol, mf.mo_coeff[:, idx1:idx3], ncol=10) occ_idx = slice(idx1, idx2) vir_idx = slice(idx2, idx3) if localize: mf = loc(mf, occ_idx, vir_idx, localize) if pair: mo_dipole = dipole_integral(mol, mf.mo_coeff) #ncore = idx1 nopen = np.sum(mf.mo_occ == 1) nalpha = idx2 #nvir_lmo = npair alpha_coeff = pair_by_tdm(ncore, npair, nopen, nalpha, npair, nbf, nif, mf.mo_coeff, mo_dipole) mf.mo_coeff = alpha_coeff.copy() print('MOs after pairing') ncore = idx2 - npair dump_mat.dump_mo(mf.mol, mf.mo_coeff[:, idx1:idx3], ncol=10) return mf, mf.mo_coeff, npair, ncore
def ecp_ano_det_ovlp(atm_ecp, atm_ano, ecpcore): ecp_ao_loc = atm_ecp.ao_loc_nr() ano_ao_loc = atm_ano.ao_loc_nr() ecp_ao_dim = ecp_ao_loc[1:] - ecp_ao_loc[:-1] ano_ao_dim = ano_ao_loc[1:] - ano_ao_loc[:-1] ecp_bas_l = [[atm_ecp.bas_angular(i)] * d for i, d in enumerate(ecp_ao_dim)] ano_bas_l = [[atm_ano.bas_angular(i)] * d for i, d in enumerate(ano_ao_dim)] ecp_bas_l = numpy.hstack(ecp_bas_l) ano_bas_l = numpy.hstack(ano_bas_l) nelec_core = 0 ecp_occ_tmp = [] ecp_idx = [] ano_idx = [] for l in range(4): nocc, frac = atom_hf.frac_occ(stdsymb, l) l_occ = [2] * ((nocc - ecpcore[l]) * (2 * l + 1)) if frac > 1e-15: l_occ.extend([frac] * (2 * l + 1)) nocc += 1 if nocc == 0: break nelec_core += 2 * ecpcore[l] * (2 * l + 1) i0 = ecpcore[l] * (2 * l + 1) i1 = nocc * (2 * l + 1) ecp_idx.append(numpy.where(ecp_bas_l == l)[0][:i1 - i0]) ano_idx.append(numpy.where(ano_bas_l == l)[0][i0:i1]) ecp_occ_tmp.append(l_occ[:i1 - i0]) ecp_idx = numpy.hstack(ecp_idx) ano_idx = numpy.hstack(ano_idx) ecp_occ = numpy.zeros(atm_ecp.nao_nr()) ecp_occ[ecp_idx] = numpy.hstack(ecp_occ_tmp) nelec_valence_left = int( gto.charge(stdsymb) - nelec_core - sum(ecp_occ[ecp_idx])) if nelec_valence_left > 0: logger.warn( mol, 'Characters of %d valence electrons are not identified.\n' 'It can affect the "meta-lowdin" localization method ' 'and the population analysis of SCF method.\n' 'Adjustment to the core/valence partition may be needed ' '(see function lo.nao.set_atom_conf)\nto get reasonable ' 'local orbitals or Mulliken population.\n', nelec_valence_left) # Return 0 to force the projection to ANO basis return 0 else: s12 = gto.intor_cross('int1e_ovlp', atm_ecp, atm_ano)[ecp_idx][:, ano_idx] return numpy.linalg.det(s12)
def mo_map(mol1, mo1, mol2, mo2, base=BASE, tol=.5): '''Given two orbitals, based on their overlap <i|j>, search all orbital-pairs which have significant overlap. Returns: Two lists. First list is the orbital-pair indices, second is the overlap value. ''' s = gto.intor_cross('int1e_ovlp', mol1, mol2) s = reduce(numpy.dot, (mo1.T, s, mo2)) idx = numpy.argwhere(abs(s) > tol) + base for i, j in idx: logger.info(mol1, '<mo-1|mo-2> %d %d %12.8f', i, j, s[i, j]) return idx, s
def mo_map(mol1, mo1, mol2, mo2, base=BASE, tol=.5): '''Given two orbitals, based on their overlap <i|j>, search all orbital-pairs which have significant overlap. Returns: Two lists. First list is the orbital-pair indices, second is the overlap value. ''' s = gto.intor_cross('int1e_ovlp', mol1, mol2) s = reduce(numpy.dot, (mo1.T, s, mo2)) idx = numpy.argwhere(abs(s) > tol) + base for i,j in idx: logger.info(mol1, '<mo-1|mo-2> %d %d %12.8f', i, j, s[i,j]) return idx, s
def ecp_ano_det_ovlp(atm_ecp, atm_ano, ecpcore): ecp_ao_loc = atm_ecp.ao_loc_nr() ano_ao_loc = atm_ano.ao_loc_nr() ecp_ao_dim = ecp_ao_loc[1:] - ecp_ao_loc[:-1] ano_ao_dim = ano_ao_loc[1:] - ano_ao_loc[:-1] ecp_bas_l = [[atm_ecp.bas_angular(i)]*d for i,d in enumerate(ecp_ao_dim)] ano_bas_l = [[atm_ano.bas_angular(i)]*d for i,d in enumerate(ano_ao_dim)] ecp_bas_l = numpy.hstack(ecp_bas_l) ano_bas_l = numpy.hstack(ano_bas_l) nelec_core = 0 ecp_occ_tmp = [] ecp_idx = [] ano_idx = [] for l in range(4): nocc, frac = atom_hf.frac_occ(stdsymb, l) l_occ = [2] * ((nocc-ecpcore[l])*(2*l+1)) if frac > 1e-15: l_occ.extend([frac] * (2*l+1)) nocc += 1 if nocc == 0: break nelec_core += 2 * ecpcore[l] * (2*l+1) i0 = ecpcore[l] * (2*l+1) i1 = nocc * (2*l+1) ecp_idx.append(numpy.where(ecp_bas_l==l)[0][:i1-i0]) ano_idx.append(numpy.where(ano_bas_l==l)[0][i0:i1]) ecp_occ_tmp.append(l_occ[:i1-i0]) ecp_idx = numpy.hstack(ecp_idx) ano_idx = numpy.hstack(ano_idx) ecp_occ = numpy.zeros(atm_ecp.nao_nr()) ecp_occ[ecp_idx] = numpy.hstack(ecp_occ_tmp) nelec_valence_left = int(gto.mole.charge(stdsymb) - nelec_core - sum(ecp_occ[ecp_idx])) if nelec_valence_left > 0: logger.warn(mol, 'Characters of %d valence electrons are not identified.\n' 'It can affect the "meta-lowdin" localization method ' 'and the population analysis of SCF method.\n' 'Adjustment to the core/valence partition may be needed ' '(see function lo.nao.set_atom_conf)\nto get reasonable ' 'local orbitals or Mulliken population.\n', nelec_valence_left) # Return 0 to force the projection to ANO basis return 0 else: s12 = gto.intor_cross('int1e_ovlp', atm_ecp, atm_ano)[ecp_idx][:,ano_idx] return numpy.linalg.det(s12)
def get_hcore(self, mol=None): '''1-component X2c Foldy-Wouthuysen (FW Hamiltonian (spin-free part only) ''' if mol is None: mol = self.mol if mol.has_ecp(): raise NotImplementedError xmol, contr_coeff = self.get_xmol(mol) c = lib.param.LIGHT_SPEED assert ('1E' in self.approx.upper()) t = xmol.intor_symmetric('int1e_kin') v = xmol.intor_symmetric('int1e_nuc') s = xmol.intor_symmetric('int1e_ovlp') w = xmol.intor_symmetric('int1e_pnucp') if 'get_xmat' in self.__dict__: # If the get_xmat method is overwritten by user, build the X # matrix with the external get_xmat method x = self.get_xmat(xmol) h1 = x2c._get_hcore_fw(t, v, w, s, x, c) elif 'ATOM' in self.approx.upper(): atom_slices = xmol.offset_nr_by_atom() nao = xmol.nao_nr() x = numpy.zeros((nao, nao)) for ia in range(xmol.natm): ish0, ish1, p0, p1 = atom_slices[ia] shls_slice = (ish0, ish1, ish0, ish1) t1 = xmol.intor('int1e_kin', shls_slice=shls_slice) s1 = xmol.intor('int1e_ovlp', shls_slice=shls_slice) with xmol.with_rinv_at_nucleus(ia): z = -xmol.atom_charge(ia) v1 = z * xmol.intor('int1e_rinv', shls_slice=shls_slice) w1 = z * xmol.intor('int1e_prinvp', shls_slice=shls_slice) x[p0:p1, p0:p1] = x2c._x2c1e_xmatrix(t1, v1, w1, s1, c) h1 = x2c._get_hcore_fw(t, v, w, s, x, c) else: h1 = x2c._x2c1e_get_hcore(t, v, w, s, c) if self.basis is not None: s22 = xmol.intor_symmetric('int1e_ovlp') s21 = gto.intor_cross('int1e_ovlp', xmol, mol) c = lib.cho_solve(s22, s21) h1 = reduce(numpy.dot, (c.T, h1, c)) if self.xuncontract and contr_coeff is not None: h1 = reduce(numpy.dot, (contr_coeff.T, h1, contr_coeff)) return h1
def hcore_hess_generator(x2cobj, mol=None): '''nuclear gradients of 1-component X2c hcore Hamiltonian (spin-free part only) ''' if mol is None: mol = x2cobj.mol xmol, contr_coeff = x2cobj.get_xmol(mol) if x2cobj.basis is not None: s22 = xmol.intor_symmetric('int1e_ovlp') s21 = gto.intor_cross('int1e_ovlp', xmol, mol) contr_coeff = lib.cho_solve(s22, s21) get_h1_xmol = gen_sf_hfw(xmol, x2cobj.approx) def hcore_deriv(ia, ja): h1 = get_h1_xmol(ia, ja) if contr_coeff is not None: h1 = lib.einsum('pi,xypq,qj->xyij', contr_coeff, h1, contr_coeff) return numpy.asarray(h1) return hcore_deriv
def gen_proj(mol, intor = 'ovlp', verbose = False) : natm = mol.natm mole_coords = mol.atom_coords(unit="Ang") test_mol = gto.Mole() if verbose : test_mol.verbose = 4 else : test_mol.verbose = 0 test_mol.atom = [["Ne", coord] for coord in mole_coords] test_mol.basis = BASIS test_mol.spin = 0 test_mol.build(0,0,unit="Ang") proj = gto.intor_cross(f'int1e_{intor}_sph', mol, test_mol) def proj_func(mo): proj_coeff = np.matmul(mo, proj).reshape(*mo.shape[:2], natm, -1) if verbose: print('shape of coeff data ', proj_coeff.shape) # res : nframe x nocc/nvir x natm x nproj return proj_coeff, proj_coeff.shape[-1] return proj_func
def proj_intor(self, intor): """1-electron integrals between origin and projected basis""" proj = gto.intor_cross(intor, self.mol, self._pmol) return proj
print 'Total number of HF MOs is equal to ', N_total N_occ = 0 for i in range(N_total): if HF_occ[i] > 0: N_occ += 1 print 'Number of occupied HF MOs is equal to ', N_occ labels = mol.spheric_labels(0) N_AO = len(labels) labels = mol2.spheric_labels(0) print 'Number of occupied AOs is equal to ', N_AO S_11 = mol.intor_symmetric("cint1e_ovlp_sph") S_22 = mol2.intor_symmetric("cint1e_ovlp_sph") S_21 = gto.intor_cross('cint1e_ovlp_sph', mol2, mol) #==================================================================== # CHOOSE AOs for Projector #==================================================================== AO = AOset(mol2, 0, 2, 3) # choose 3d orbs for Fe, atom with id0 for i in range(1, 6): AO.append(mol2.search_ao_nr(i, 1, 1, 2)) #add pz orbs of C from the 1st ring for j in range(11, 16): AO.append(mol2.search_ao_nr(j, 1, 1, 2)) #add pz orbs of C from the 2nd ring Np_AO = len(AO) for i in range(Np_AO):
#!/usr/bin/env python # # Author: Qiming Sun <*****@*****.**> # ''' Transform FCI wave functions according to the underlying one-particle basis transformations. ''' from functools import reduce import numpy from pyscf import gto, scf, fci myhf1 = gto.M(atom='H 0 0 0; F 0 0 1.1', basis='6-31g', verbose=0).apply(scf.RHF).run() e1, ci1 = fci.FCI(myhf1.mol, myhf1.mo_coeff).kernel() print('FCI energy of mol1', e1) myhf2 = gto.M(atom='H 0 0 0; F 0 0 1.2', basis='6-31g', verbose=0).apply(scf.RHF).run() s12 = gto.intor_cross('cint1e_ovlp_sph', myhf1.mol, myhf2.mol) s12 = reduce(numpy.dot, (myhf1.mo_coeff.T, s12, myhf2.mo_coeff)) norb = myhf2.mo_energy.size nelec = myhf2.mol.nelectron ci2 = fci.addons.transform_ci_for_orbital_rotation(ci1, norb, nelec, s12) print('alpha-string, beta-string, CI coefficients') for c, stra, strb in fci.addons.large_ci(ci2, norb, nelec): print(stra, strb, c)
s1 = mol.intor('cint1e_ipovlp_sph', comp=3) t1 = mol.intor('cint1e_ipkin_sph' , comp=3) v1 = mol.intor('cint1e_ipnuc_sph' , comp=3) print('Dipole %s' % numpy.einsum('xij,ij->x', mol.intor('cint1e_r_sph', comp=3), dm)) # # AO overlap between two molecules # mol1 = gto.M( verbose = 0, atom = 'H 0 1 0; H 1 0 0', basis = 'ccpvdz' ) s = gto.intor_cross('cint1e_ovlp_sph', mol, mol1) print('overlap shape (%d, %d)' % s.shape) # # 2e integrals (New in PySCF-1.1). keyword aosym is required to specify the # permutation symmetry in the AO integral matrix. s8 means 8-fold symmetry, # s2kl means 2-fold symmetry for the symmetry between kl only # eri = mol.intor('cint2e_sph', aosym='s8') # # 2e gradient integrals on first atom only # eri = mol.intor('cint2e_ip1_sph', aosym='s2kl') # # 2e integral gradients on certain atom
s1 = mol.intor('int1e_ipovlp') # (3,N,N) array, 3 for x,y,z components t1 = mol.intor('int1e_ipkin' ) # (3,N,N) array, 3 for x,y,z components v1 = mol.intor('int1e_ipnuc' ) # (3,N,N) array, 3 for x,y,z components mol.set_common_origin([0,0,0]) # Set gauge origin before computing dipole integrals print('Dipole %s' % numpy.einsum('xij,ji->x', mol.intor('int1e_r'), dm)) # # AO overlap between two molecules # mol1 = gto.M( verbose = 0, atom = 'H 0 1 0; H 1 0 0', basis = 'ccpvdz' ) s = gto.intor_cross('int1e_ovlp', mol, mol1) print('overlap shape (%d, %d)' % s.shape) # # 2e integrals. Keyword aosym is to specify the permutation symmetry in the # AO integral matrix. s8 means 8-fold symmetry, s2kl means 2-fold symmetry # for the symmetry between kl in (ij|kl) # eri = mol.intor('int2e', aosym='s8') # # 2e gradient integrals (against electronic coordinates) on bra of first atom. # aosym=s2kl indicates that the permutation symmetry is used on k,l indices of # (ij|kl). The resultant eri is a 3-dimension array (3, N*N, N*(N+1)/2) where # N is the number of AO orbitals. #
rdm2_mp2 = 2.0*einsum('iajb,iajb->iajb', eri_mo, e_denom) rdm2_mp2 -= einsum('ibja,iajb->iajb', eri_mo, e_denom) e_mp2 = numpy.einsum('iajb,iajb->', eri_mo, rdm2_mp2, optimize=True) lib.logger.info(mf,"E(MP2): %12.8f" % e_mp2) lib.logger.info(mf,"E(HF+MP2): %12.8f" % (e_mp2+ehf)) pmol = mol.copy() pmol.atom = mol._atom pmol.unit = 'B' pmol.symmetry = False pmol.basis = 'sto-6g' pmol.build(False, False) sb = mol.intor('int1e_ovlp') sbinv = numpy.linalg.inv(sb) sbs = gto.intor_cross('int1e_ovlp', mol, pmol) ss = reduce(numpy.dot, (sbs.T,sbinv,sbs)) ssinv = numpy.linalg.inv(ss) sslow = scipy.linalg.sqrtm(ss) h = numpy.dot(mf.mo_coeff*mf.mo_energy, mf.mo_coeff.T) hb = reduce(numpy.dot, (sb,h,sb)) hs = reduce(numpy.dot, (sbs.T,h,sbs)) mo_energy, mo_coeff = mf.eig(hs, ss) # Composite basis big_prim x small_states cp = reduce(numpy.dot, (sbinv, sbs, mo_coeff)) nocc = mol.nelectron//2 for i in range(nocc): cp[:,i] = mf.mo_coeff[:,i] sp = reduce(numpy.dot, (cp.T, sb, cp)) hp = reduce(numpy.dot, (cp.T, hb, cp))
v = mol.intor('int1e_nuc') # Overlap, kinetic, nuclear attraction gradients (against electron coordinates # on bra) s1 = mol.intor('int1e_ipovlp') # (3,N,N) array, 3 for x,y,z components t1 = mol.intor('int1e_ipkin') # (3,N,N) array, 3 for x,y,z components v1 = mol.intor('int1e_ipnuc') # (3,N,N) array, 3 for x,y,z components mol.set_common_origin([0, 0, 0 ]) # Set gauge origin before computing dipole integrals print('Dipole %s' % numpy.einsum('xij,ji->x', mol.intor('int1e_r'), dm)) # # AO overlap between two molecules # mol1 = gto.M(verbose=0, atom='H 0 1 0; H 1 0 0', basis='ccpvdz') s = gto.intor_cross('int1e_ovlp', mol, mol1) print('overlap shape (%d, %d)' % s.shape) # # 2e integrals. Keyword aosym is to specify the permutation symmetry in the # AO integral matrix. s8 means 8-fold symmetry, s2kl means 2-fold symmetry # for the symmetry between kl in (ij|kl) # eri = mol.intor('int2e', aosym='s8') # # 2e gradient integrals (against electronic coordinates) on bra of first atom. # aosym=s2kl indicates that the permutation symmetry is used on k,l indices of # (ij|kl). The resultant eri is a 3-dimension array (3, N*N, N*(N+1)/2) where # N is the number of AO orbitals. #
def kernel(mf, locc, lvir, threshold_occ=THRESHOLD_OCC, threshold_vir=THRESHOLD_VIR, minao=MINAO, with_iao=WITH_IAO, openshell_option=OPENSHELL_OPTION, canonicalize=CANONICALIZE, ncore=NCORE, localize=None, verbose=None): from pyscf.tools import mo_mapping if isinstance(verbose, logger.Logger): log = verbose elif verbose is not None: log = logger.Logger(mf.stdout, verbose) else: log = logger.Logger(mf.stdout, mf.verbose) mol = mf.mol log.info('\n** AVAS **') if isinstance(mf, scf.uhf.UHF): raise NotImplementedError else: nao, nmo = mf.mo_coeff.shape nocc = mol.nelectron // 2 - ncore nvir = nmo - nocc - ncore mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ mo_energy = mf.mo_energy mo_coeff_core = mf.mo_coeff[:, :ncore] mo_coeff_occ = mf.mo_coeff[:, ncore:ncore + nocc] mo_coeff_vir = mf.mo_coeff[:, ncore + nocc:] mo_occ_core = mf.mo_occ[:ncore] if ncore > 0: mofreeze = mo_coeff_core mo_occ_occ = mf.mo_occ[ncore:ncore + nocc] mo_occ_vir = mf.mo_occ[ncore + nocc:] mo_energy_core = mf.mo_occ[:ncore] mo_enery_occ = mf.mo_occ[ncore:ncore + nocc] mo_energy_vir = mf.mo_occ[ncore + nocc:] ovlp = mol.intor_symmetric('int1e_ovlp') log.info('Total number of HF MOs is equal to %d', mf.mo_coeff.shape[1]) log.info('Number of occupied HF MOs is equal to %d', nocc) log.info('Number of core HF MOs is equal to %d', ncore) mol = mf.mol pmol = mol.copy() pmol.atom = mol._atom pmol.unit = 'B' pmol.symmetry = False pmol.basis = minao pmol.build(False, False) baslstocc = pmol.search_ao_label(locc) log.info('reference occ AO indices for %s %s: %s', minao, locc, baslstocc) baslstvir = pmol.search_ao_label(lvir) print lvir, pmol.search_ao_label(lvir) log.info('reference vir AO indices for %s %s: %s', minao, lvir, baslstvir) s2occ = pmol.intor_symmetric('int1e_ovlp')[baslstocc][:, baslstocc] s21occ = gto.intor_cross('int1e_ovlp', pmol, mol)[baslstocc] s21occ = numpy.dot(s21occ, mo_coeff_occ) s2vir = pmol.intor_symmetric('int1e_ovlp')[baslstvir][:, baslstvir] s21vir = gto.intor_cross('int1e_ovlp', pmol, mol)[baslstvir] s21vir = numpy.dot(s21vir, mo_coeff_vir) saocc = s21occ.T.dot(scipy.linalg.solve(s2occ, s21occ, sym_pos=True)) savir = s21vir.T.dot(scipy.linalg.solve(s2vir, s21vir, sym_pos=True)) log.info('Threshold_occ %s', threshold_occ) wocc, u = numpy.linalg.eigh(saocc) log.info('projected occ eig %s', wocc[::-1]) ncas_occ = (wocc > threshold_occ).sum() log.info('Active from occupied = %d , eig %s', ncas_occ, wocc[wocc > threshold_occ][::-1]) nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold_occ).sum() * 2 mocore = mo_coeff_occ.dot(u[:, wocc < threshold_occ]) log.info('Inactive from occupied = %d', mocore.shape[1]) mocas = mo_coeff_occ.dot(u[:, wocc > threshold_occ]) log.info('Threshold_vir %s', threshold_vir) wvir, u = numpy.linalg.eigh(savir) log.debug('projected vir eig %s', wvir[::-1]) ncas_vir = (wvir > threshold_vir).sum() log.info('Active from unoccupied = %d , eig %s', ncas_vir, wvir[wvir > threshold_vir][::-1]) mocas = numpy.hstack((mocas, mo_coeff_vir.dot(u[:, wvir > threshold_vir]))) movir = mo_coeff_vir.dot(u[:, wvir < threshold_vir]) log.info('Inactive from unoccupied = %d', movir.shape[1]) ncas = mocas.shape[1] log.info('Dimensions of active %d', ncas) nalpha = (nelecas + mol.spin) // 2 log.info('# of alpha electrons %d', nalpha) nbeta = nelecas - nalpha log.info('# of beta electrons %d', nbeta) if canonicalize: from pyscf.mcscf import dmet_cas def trans(c): if c.shape[1] == 0: return c else: csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) fock = numpy.dot(csc * mo_energy, csc.T) e, u = scipy.linalg.eigh(fock) return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log) if ncore > 0: mo = numpy.hstack( [trans(mofreeze), trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)]) else: if ncore > 0: mo = numpy.hstack((mofreeze, mocore, mocas, movir)) else: mo = numpy.hstack((mocore, mocas, movir)) return ncas, nelecas, mo
def kernel(mf, aolabels, threshold=THRESHOLD, minao=MINAO, with_iao=WITH_IAO, openshell_option=OPENSHELL_OPTION, canonicalize=CANONICALIZE, ncore=0, verbose=None): '''AVAS method to construct mcscf active space. Ref. arXiv:1701.07862 [physics.chem-ph] Args: mf : an :class:`SCF` object aolabels : string or a list of strings AO labels for AO active space Kwargs: threshold : float Tructing threshold of the AO-projector above which AOs are kept in the active space. minao : str A reference AOs for AVAS. with_iao : bool Whether to use IAO localization to construct the reference active AOs. openshell_option : int How to handle singly-occupied orbitals in the active space. The singly-occupied orbitals are projected as part of alpha orbitals if openshell_option=2, or completely kept in active space if openshell_option=3. See Section III.E option 2 or 3 of the reference paper for more details. canonicalize : bool Orbitals defined in AVAS method are local orbitals. Symmetrizing the core, active and virtual space. ncore : integer Number of core orbitals to exclude from the AVAS method. Returns: active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF Examples: >>> from pyscf import gto, scf, mcscf >>> from pyscf.mcscf import avas >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz') >>> mf = scf.RHF(mol).run() >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s']) >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo) ''' from pyscf.tools import mo_mapping if isinstance(verbose, logger.Logger): log = verbose elif verbose is not None: log = logger.Logger(mf.stdout, verbose) else: log = logger.Logger(mf.stdout, mf.verbose) mol = mf.mol log.info('\n** AVAS **') if isinstance(mf, scf.uhf.UHF): log.note('UHF/UKS object is found. AVAS takes alpha orbitals only') mo_coeff = mf.mo_coeff[0] mo_occ = mf.mo_occ[0] mo_energy = mf.mo_energy[0] assert (openshell_option != 1) else: mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ mo_energy = mf.mo_energy nocc = numpy.count_nonzero(mo_occ != 0) ovlp = mol.intor_symmetric('int1e_ovlp') log.info(' Total number of HF MOs is equal to %d', mo_coeff.shape[1]) log.info(' Number of occupied HF MOs is equal to %d', nocc) mol = mf.mol pmol = mol.copy() pmol.atom = mol._atom pmol.unit = 'B' pmol.symmetry = False pmol.basis = minao pmol.build(False, False) baslst = pmol.search_ao_label(aolabels) log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst) if with_iao: from pyscf.lo import iao c = iao.iao(mol, mo_coeff[:, ncore:nocc], minao)[:, baslst] s2 = reduce(numpy.dot, (c.T, ovlp, c)) s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:])) else: s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:, baslst] s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst] s21 = numpy.dot(s21, mo_coeff[:, ncore:]) sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True)) from pyscf.symm import label_orb_symm symm = label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo_coeff, tol=1e-5) if openshell_option == 2: wocc, u = numpy.linalg.eigh(sa[:(nocc - ncore), :(nocc - ncore)]) log.info('Option 2: threshold %s', threshold) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 if ncore > 0: mofreeze = mo_coeff[:, :ncore] mocore = mo_coeff[:, ncore:nocc].dot(u[:, wocc < threshold]) mocas = mo_coeff[:, ncore:nocc].dot(u[:, wocc > threshold]) mask = (wocc > threshold).tolist() #print nocc-ncore,mocore.shape,wocc<threshold #print nocc-ncore,mocas.shape,wocc>threshold #print 'BM',mask,len(mask) wvir, u = numpy.linalg.eigh(sa[(nocc - ncore):, (nocc - ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack( (mocas, mo_coeff[:, nocc:].dot(u[:, wvir > threshold]))) movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold]) ncas = mocas.shape[1] mask += (wvir > threshold).tolist() #print mo_coeff.shape[0]-nocc,mocas.shape,wvir>threshold #print mo_coeff.shape[0]-nocc,movir.shape,wvir<threshold #print 'BM',mask,len(mask) elif openshell_option == 3: docc = nocc - mol.spin wocc, u = numpy.linalg.eigh(sa[:(docc - ncore), :(docc - ncore)]) log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 if ncore > 0: mofreeze = mo_coeff[:, :ncore] mocore = mo_coeff[:, ncore:docc].dot(u[:, wocc < threshold]) mocas = mo_coeff[:, ncore:docc].dot(u[:, wocc > threshold]) wvir, u = numpy.linalg.eigh(sa[(nocc - ncore):, (nocc - ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:, docc:nocc], mo_coeff[:, nocc:].dot(u[:, wvir > threshold]))) movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold]) ncas = mocas.shape[1] log.debug('projected occ eig %s', wocc[::-1]) log.debug('projected vir eig %s', wvir[::-1]) log.info('Active from occupied = %d , eig %s', ncas_occ, wocc[wocc > threshold][::-1]) log.info('Inactive from occupied = %d', mocore.shape[1]) log.info('Active from unoccupied = %d , eig %s', ncas_vir, wvir[wvir > threshold][::-1]) log.info('Inactive from unoccupied = %d', movir.shape[1]) log.info('Dimensions of active %d', ncas) nalpha = (nelecas + mol.spin) // 2 nbeta = nelecas - nalpha log.info('# of alpha electrons %d', nalpha) log.info('# of beta electrons %d', nbeta) selected = [i for i in range(len(mask)) if mask[i]] list_of_sym = [symm[i] for i in selected] print selected print list_of_sym for elt in set(symm): n = 0 for i in list_of_sym: if i == elt: n += 1 print elt, n if canonicalize: from pyscf.mcscf import dmet_cas def trans(c): if c.shape[1] == 0: return c else: csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) fock = numpy.dot(csc * mo_energy, csc.T) e, u = scipy.linalg.eigh(fock) return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log) if ncore > 0: mo = numpy.hstack( [trans(mofreeze), trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack((mocore, mocas, movir)) return ncas, nelecas, mo
print 'Total number of HF MOs is equal to ' ,N_total N_occ=0 for i in range (N_total): if HF_occ[i] > 0: N_occ+=1 print 'Number of occupied HF MOs is equal to ', N_occ labels = mol.spheric_labels(0) N_AO = len (labels) labels = mol2.spheric_labels(0) print 'Number of occupied AOs is equal to ', N_AO S_11=mol.intor_symmetric("cint1e_ovlp_sph") S_22=mol2.intor_symmetric("cint1e_ovlp_sph") S_21 = gto.intor_cross('cint1e_ovlp_sph', mol2, mol) #==================================================================== # CHOOSE AOs for Projector #==================================================================== AO = AOset(mol2, 0, 2, 3) # choose 3d orbs for Fe, atom with id0 for i in range (1,6): AO.append(mol2.search_ao_nr(i,1,1,2)) #add pz orbs of C from the 1st ring for j in range (11,16): AO.append(mol2.search_ao_nr(j,1,1,2)) #add pz orbs of C from the 2nd ring Np_AO=len(AO) for i in range (Np_AO): idx= AO[i] print idx, ' ', labels[idx]
def kernel(mf, aolabels, threshold=THRESHOLD, minao=MINAO, with_iao=WITH_IAO, openshell_option=OPENSHELL_OPTION, canonicalize=CANONICALIZE, ncore=0, verbose=None): '''AVAS method to construct mcscf active space. Ref. arXiv:1701.07862 [physics.chem-ph] Args: mf : an :class:`SCF` object aolabels : string or a list of strings AO labels for AO active space Kwargs: threshold : float Tructing threshold of the AO-projector above which AOs are kept in the active space. minao : str A reference AOs for AVAS. with_iao : bool Whether to use IAO localization to construct the reference active AOs. openshell_option : int How to handle singly-occupied orbitals in the active space. The singly-occupied orbitals are projected as part of alpha orbitals if openshell_option=2, or completely kept in active space if openshell_option=3. See Section III.E option 2 or 3 of the reference paper for more details. canonicalize : bool Orbitals defined in AVAS method are local orbitals. Symmetrizing the core, active and virtual space. ncore : integer Number of core orbitals to exclude from the AVAS method. Returns: active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF Examples: >>> from pyscf import gto, scf, mcscf >>> from pyscf.mcscf import avas >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz') >>> mf = scf.RHF(mol).run() >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s']) >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo) ''' from pyscf.tools import mo_mapping if isinstance(verbose, logger.Logger): log = verbose elif verbose is not None: log = logger.Logger(mf.stdout, verbose) else: log = logger.Logger(mf.stdout, mf.verbose) mol = mf.mol log.info('\n** AVAS **') if isinstance(mf, scf.uhf.UHF): log.note('UHF/UKS object is found. AVAS takes alpha orbitals only') mo_coeff = mf.mo_coeff[0] mo_occ = mf.mo_occ[0] mo_energy = mf.mo_energy[0] assert(openshell_option != 1) else: mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ mo_energy = mf.mo_energy nocc = numpy.count_nonzero(mo_occ != 0) ovlp = mol.intor_symmetric('int1e_ovlp') log.info(' Total number of HF MOs is equal to %d' ,mo_coeff.shape[1]) log.info(' Number of occupied HF MOs is equal to %d', nocc) mol = mf.mol pmol = mol.copy() pmol.atom = mol._atom pmol.unit = 'B' pmol.symmetry = False pmol.basis = minao pmol.build(False, False) baslst = pmol.search_ao_label(aolabels) log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst) if with_iao: from pyscf.lo import iao c = iao.iao(mol, mo_coeff[:,ncore:nocc], minao)[:,baslst] s2 = reduce(numpy.dot, (c.T, ovlp, c)) s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:])) else: s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:,baslst] s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst] s21 = numpy.dot(s21, mo_coeff[:, ncore:]) sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True)) if openshell_option == 2: wocc, u = numpy.linalg.eigh(sa[:(nocc-ncore), :(nocc-ncore)]) log.info('Option 2: threshold %s', threshold) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 if ncore > 0: mofreeze = mo_coeff[:,:ncore] mocore = mo_coeff[:,ncore:nocc].dot(u[:,wocc<threshold]) mocas = mo_coeff[:,ncore:nocc].dot(u[:,wocc>threshold]) wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:,nocc:].dot(u[:,wvir>threshold]))) movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold]) ncas = mocas.shape[1] elif openshell_option == 3: docc = nocc - mol.spin wocc, u = numpy.linalg.eigh(sa[:(docc-ncore),:(docc-ncore)]) log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 if ncore > 0: mofreeze = mo_coeff[:,:ncore] mocore = mo_coeff[:,ncore:docc].dot(u[:,wocc<threshold]) mocas = mo_coeff[:,ncore:docc].dot(u[:,wocc>threshold]) wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:,docc:nocc], mo_coeff[:,nocc:].dot(u[:,wvir>threshold]))) movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold]) ncas = mocas.shape[1] log.debug('projected occ eig %s', wocc[::-1]) log.debug('projected vir eig %s', wvir[::-1]) log.info('Active from occupied = %d , eig %s', ncas_occ, wocc[wocc>threshold][::-1]) log.info('Inactive from occupied = %d', mocore.shape[1]) log.info('Active from unoccupied = %d , eig %s', ncas_vir, wvir[wvir>threshold][::-1]) log.info('Inactive from unoccupied = %d', movir.shape[1]) log.info('Dimensions of active %d', ncas) nalpha = (nelecas + mol.spin) // 2 nbeta = nelecas - nalpha log.info('# of alpha electrons %d', nalpha) log.info('# of beta electrons %d', nbeta) if canonicalize: from pyscf.mcscf import dmet_cas def trans(c): if c.shape[1] == 0: return c else: csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) fock = numpy.dot(csc*mo_energy, csc.T) e, u = scipy.linalg.eigh(fock) return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log) if ncore > 0: mo = numpy.hstack([trans(mofreeze), trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack((mocore, mocas, movir)) return ncas, nelecas, mo
def MakePiOS(mol, mf, PiAtomsList, nPiOcc=None, nPiVirt=None): np.set_printoptions(precision=4, linewidth=10000, edgeitems=3, suppress=False) print( "================================CONSTRUCTING PI-ORBITALS===========================================" ) # PiAtoms list contains the set of main-group atoms involved in the pi-system of the chromophores(?) # indices are 1-based. mol2 = mol.copy() mol2.basis = 'MINAO' mol2.build() # make a minimal AO basis for our atoms. We load the basis from a library # just to have access to its shell-composition. Need that to find the indices # of all atoms, and the AO indices of the px/py/pz functions. Elements, Coords = Atoms_w_Coords(mol2) Shells = MakeShells(mol2, Elements) def AssignTag(iAt, Element, Tag): assert (Elements[iAt - 1] == Element) Elements[iAt - 1] = Element + Tag # fix type of atom for the donor-acceptor which are exchanging the hydrogens. # During the process, the hydrogens are moving and the Huckel-Theory # formal number of contributed pi-electrons may change. # These settings override the auto-detection of number of pi electrons # based on atomic connectivity in GetNumPiElec() below. # AssignTag(50, "N", "1e") OrbBasis = mol.basis C = mf.mo_coeff S1 = mol.intor_symmetric("cint1e_ovlp_sph") S2 = mol2.intor_symmetric("cint1e_ovlp_sph") S12 = gto.intor_cross('cint1e_ovlp_sph', mol, mol2) SMo = mdot(C.T, S1, C) print(" MO deviation from orthogonality {:8.2e} \n".format( rmsd(SMo - np.eye(SMo.shape[0])))) # make arrays of occupation numbers and orbital eigenvalues. Occ = mf.mo_occ Eps = mf.mo_energy nOrb = C.shape[1] if (mol.spin == 0): nOcc = np.sum(Occ == 2) else: nOcc = np.sum(Occ == 2) + np.sum(Occ == 1) n1 = np.sum(Occ == 1) print(" Number of singly occupied orbitals {} ".format(n1)) nVir = np.sum(Occ == 0) assert (nOcc + nVir == nOrb) print(" Number of occupied orbitals {} ".format(nOcc)) print(" Number of unoccupied orbitals {} ".format(nVir)) # Compute Fock matrix from orbital eigenvalues (SCF has to be fully converged) Fock = mdot(S1, C, np.diag(Eps), C.T, S1.T) Rdm = mdot(C, np.diag(Occ), C.T) COcc = C[:, :nOcc] CVir = C[:, nOcc:] Smh1 = MakeSmh(S1) Sh1 = np.dot(S1, Smh1) # Compute IAO basis (non-orthogonal). Will be used to identify the # pi-MO-space of the target atom groups. CIb = MakeIaosRaw(COcc, S1, S2, S12) CIbOcc = np.linalg.lstsq(CIb, COcc)[0] Err = np.dot(Sh1, np.dot(CIb, CIbOcc) - COcc) # check orthogonality of IAO basis occupied orbitals SIb = mdot(CIb.T, S1, CIb) SIbOcc = mdot(CIbOcc.T, SIb, CIbOcc) nIb = SIb.shape[0] if 0: # make the a representation of the virtual valence space. SmhIb = MakeSmh(SIb) nIbVir = nIb - nOcc # number of virtual valence orbitals CTargetIb = CIb STargetIb = mdot(CTargetIb.T, S1, CTargetIb) SmhTargetIb = MakeSmh(STargetIb) STargetIbVir = mdot(SmhTargetIb, CTargetIb.T, S1, CVir) U, sig, Vt = np.linalg.svd(STargetIbVir, full_matrices=False) print(" Number of target MINAO basis fn {}\n".format( CTargetIb.shape[1])) print("SIbVir Singular Values (n={})".format(len(sig))) print(" [{}]".format(', '.join('{:.4f}'.format(k) for k in sig))) assert (np.abs(sig[nIbVir - 1] - 1.0) < 1e-4) assert (np.abs(sig[nIbVir] - 0.0) < 1e-4) # for pi systems: do it like here -^ for the virtuals, but # for COcc and CVir both. What we should do is: # - instead of CIb, we use CTargetIb = CIb * (AO-linear-comb-matrix) # - instead of SmhIb, we use its corresponding target-AO overlap matrix: # SmhTargetIb = MakeSmh(mdot(CTargetIb.T, S1, CTargetIb)) # - instead of using nOcc/nVir/nIb to determine the target number # of orbitals, we obtain the target number of pi electrons by counting # their subset from the selected main group atoms (see CHEM 408 u11). # From this determine the number of occupied pi-orbitals this system # is supposed to have, and virtual orbitals as nTargetIb - nPiOcc # The AoMix matrix is made from the pi-system's inertial tensor to get the # local z-direction, and then linearly-combining this onto the highest-N p-AOs. CActOcc = [] CActVir = [] # add pi-HOMOs and pi-LUMOs CFragOcc, CFragVir, nOccOrbExpected, nVirtOrbExpected = MakePiSystemOrbitals( "Pi-System", PiAtomsList, None, Elements, Coords, CIb, Shells, S1, S12, S2, Fock, COcc, CVir) if (nPiOcc is None): for i in range(1, nOccOrbExpected + 1): CActOcc.append(CFragOcc[:, -i]) else: for i in range(1, nPiOcc + 1): CActOcc.append(CFragOcc[:, -i]) if (nPiVirt is None): for j in range(nVirtOrbExpected): CActVir.append(CFragVir[:, j]) else: for j in range(nPiVirt): CActVir.append(CFragVir[:, j]) nActOcc = len(CActOcc) nActVir = len(CActVir) print("\n -- Joining active spaces") if (mol.spin == 0): nElec = 2 * len(CActOcc) else: nElec = 2 * len(CActOcc) - n1 CAct = np.array(CActOcc + CActVir).T if 0: # orthogonalize and semi-canonicalize SAct = mdot(CAct.T, S1, CAct) ew, ev = np.linalg.eigh(SAct) print( " CAct initial overlap (if all ~approx 1, then the initial active orbitals are near-orthogonal. That is good.)" ) print(" [{}]".format(', '.join('{:.4f}'.format(k) for k in ew))) CAct = np.dot(CAct, MakeSmh(SAct)) CAct = SemiCanonicalize(CAct, Fock, S1, "active") print(" Number of Active Electrons {} ".format(nElec)) print(" Number of Active Orbitals {} ".format(CAct.shape[1])) # make new non-active occupied and non-active virtual orbitals, # in order to rebuild a full MO matrix. def MakeInactiveSpace(Name, CActList, COrb1): CAct = np.array(CActList).T SAct = mdot(CAct.T, S1, CAct) CAct = np.dot(CAct, MakeSmh(SAct)) SActMo = mdot(COrb1.T, S1.T, CAct, CAct.T, S1, COrb1) ew, ev = np.linalg.eigh( SActMo) # small ews first (orbs not overlapping with active orbs). nRest = COrb1.shape[1] - CAct.shape[1] if 0: for i in range(len(ew)): print("{:4} {:15.6f} {}".format(i, ew[i], i == nRest)) assert (np.abs(ew[nRest - 1] - 0.0) < 1e-8) assert (np.abs(ew[nRest] - 1.0) < 1e-8) CNewOrb1 = np.dot(COrb1, ev[:, :nRest]) return SemiCanonicalize(CNewOrb1, Fock, S1, Name, Print=False) CNewClo = MakeInactiveSpace("NewClo", CActOcc, COcc) CNewExt = MakeInactiveSpace("NewVir", CActVir, CVir) nNewClo = CNewClo.shape[1] nNewExt = CNewExt.shape[1] # re-orthogonalize (should be orthogonal already, but just to be sure). COrbNew = np.hstack([CAct]) if 1: COrbNew = np.hstack([CNewClo, CAct, CNewExt]) SMo = mdot(COrbNew.T, S1, COrbNew) COrbNew = np.dot(COrbNew, MakeSmh(SMo)) print(" Number of Core Orbitals {} ".format(CNewClo.shape[1])) print(" Number of Virtual Orbitals {} ".format(CNewExt.shape[1])) print(" Total Number of Orbitals {} ".format(COrbNew.shape[1])) return CNewClo.shape[1], CAct.shape[1], CNewExt.shape[1], nElec, COrbNew
from functools import reduce import numpy from pyscf import gto, scf, ci # # RCISD wavefunction overlap # myhf1 = gto.M(atom='H 0 0 0; F 0 0 1.1', basis='6-31g', verbose=0).apply(scf.RHF).run() ci1 = ci.CISD(myhf1).run() print('CISD energy of mol1', ci1.e_tot) myhf2 = gto.M(atom='H 0 0 0; F 0 0 1.2', basis='6-31g', verbose=0).apply(scf.RHF).run() ci2 = ci.CISD(myhf2).run() print('CISD energy of mol2', ci2.e_tot) s12 = gto.intor_cross('cint1e_ovlp_sph', myhf1.mol, myhf2.mol) s12 = reduce(numpy.dot, (myhf1.mo_coeff.T, s12, myhf2.mo_coeff)) nmo = myhf2.mo_energy.size nocc = myhf2.mol.nelectron // 2 print('<CISD-mol1|CISD-mol2> = ', ci.cisd.overlap(ci1.ci, ci2.ci, nmo, nocc, s12)) # # UCISD wavefunction overlap # myhf1 = gto.M(atom='H 0 0 0; F 0 0 1.1', basis='6-31g', spin=2, verbose=0).apply(scf.UHF).run() ci1 = ci.UCISD(myhf1).run() print('CISD energy of mol1', ci1.e_tot) myhf2 = gto.M(atom='H 0 0 0; F 0 0 1.2', basis='6-31g', spin=2, verbose=0).apply(scf.UHF).run() ci2 = ci.UCISD(myhf2).run() print('CISD energy of mol2', ci2.e_tot)
def kernel(mf, aolabels, threshold=.2, minao='minao', with_iao=False, openshelloption=2, canonicalize=True, verbose=None): '''AVAS method to construct mcscf active space. Ref. arXiv:1701.07862 [physics.chem-ph] Args: mf : an :class:`SCF` object aolabels : string or a list of strings AO labels for AO active space Kwargs: threshold : float Tructing threshold of the AO-projector above which AOs are kept in the active space. minao : str A reference AOs for AVAS. with_iao : bool Whether to use IAO localization to construct the reference active AOs. openshelloption : int How to handle singly-occupied orbitals in the active space. The singly-occupied orbitals are projected as part of alpha orbitals if openshelloption=2, or completely kept in active space if openshelloption=3. See Section III.E option 2 or 3 of the reference paper for more details. canonicalize : bool Orbitals defined in AVAS method are local orbitals. Symmetrizing the core, active and virtual space. Returns: active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF Examples: >>> from pyscf import gto, scf, mcscf >>> from pyscf.mcscf import avas >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz') >>> mf = scf.RHF(mol).run() >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s']) >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo) ''' if isinstance(verbose, logger.Logger): log = verbose elif verbose is not None: log = logger.Logger(mf.stdout, verbose) else: log = logger.Logger(mf.stdout, mf.verbose) mol = mf.mol log.info('\n** AVAS **') if isinstance(mf, scf.uhf.UHF): log.note('UHF/UKS object is found. AVAS takes alpha orbitals only') mo_coeff = mf.mo_coeff[0] mo_occ = mf.mo_occ[0] mo_energy = mf.mo_energy[0] assert (openshelloption != 1) else: mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ mo_energy = mf.mo_energy nocc = numpy.count_nonzero(mo_occ != 0) ovlp = mol.intor_symmetric('cint1e_ovlp_sph') log.info(' Total number of HF MOs is equal to %d', mo_coeff.shape[1]) log.info(' Number of occupied HF MOs is equal to %d', nocc) mol = mf.mol pmol = mol.copy() pmol.atm = mol._atm pmol.basis = minao pmol.build(False, False) if isinstance(aolabels, str): aolabels = re.sub(' +', ' ', aolabels.strip(), count=1) baslst = [ i for i, s in enumerate(pmol.spherical_labels(1)) if aolabels in s ] elif isinstance(aolabels[0], str): aolabels = [re.sub(' +', ' ', x.strip(), count=1) for x in aolabels] baslst = [ i for i, s in enumerate(pmol.spherical_labels(1)) if any(x in s for x in aolabels) ] else: raise RuntimeError baslst = numpy.asarray(baslst) log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst) if with_iao: from pyscf.lo import iao c = iao.iao(mol, mo_coeff[:, :nocc], minao)[:, baslst] s2 = reduce(numpy.dot, (c.T, ovlp, c)) s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) else: s2 = pmol.intor_symmetric('cint1e_ovlp_sph')[baslst][:, baslst] s21 = gto.intor_cross('cint1e_ovlp_sph', pmol, mol)[baslst] s21 = numpy.dot(s21, mo_coeff) sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True)) if openshelloption == 2: wocc, u = numpy.linalg.eigh(sa[:nocc, :nocc]) log.info('Option 2: threshold %s', threshold) ncas_occ = (wocc > threshold).sum() nelecas = mol.nelectron - (wocc < threshold).sum() * 2 mocore = mo_coeff[:, :nocc].dot(u[:, wocc < threshold]) mocas = mo_coeff[:, :nocc].dot(u[:, wocc > threshold]) wvir, u = numpy.linalg.eigh(sa[nocc:, nocc:]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack( (mocas, mo_coeff[:, nocc:].dot(u[:, wvir > threshold]))) movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold]) ncas = mocas.shape[1] elif openshelloption == 3: docc = nocc - mol.spin wocc, u = numpy.linalg.eigh(sa[:docc, :docc]) log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin) ncas_occ = (wocc > threshold).sum() nelecas = mol.nelectron - (wocc < threshold).sum() * 2 mocore = mo_coeff[:, :docc].dot(u[:, wocc < threshold]) mocas = mo_coeff[:, :docc].dot(u[:, wocc > threshold]) wvir, u = numpy.linalg.eigh(sa[nocc:, nocc:]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:, docc:nocc], mo_coeff[:, nocc:].dot(u[:, wvir > threshold]))) movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold]) ncas = mocas.shape[1] log.debug('projected occ eig %s', wocc[::-1]) log.debug('projected vir eig %s', wvir[::-1]) log.info('Active from occupied = %d , eig %s', ncas_occ, wocc[wocc > threshold][::-1]) log.info('Inactive from occupied = %d', mocore.shape[1]) log.info('Active from unoccupied = %d , eig %s', ncas_vir, wvir[wvir > threshold][::-1]) log.info('Inactive from unoccupied = %d', movir.shape[1]) log.info('Dimensions of active %d', ncas) nalpha = (nelecas + mol.spin) // 2 nbeta = nelecas - nalpha log.info('# of alpha electrons %d', nalpha) log.info('# of beta electrons %d', nbeta) if canonicalize: from pyscf.mcscf import dmet_cas def trans(c): if c.shape[1] == 0: return c else: csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) fock = numpy.dot(csc * mo_energy, csc.T) e, u = scipy.linalg.eigh(fock) return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log) mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)]) else: mo = numpy.hstack((mocore, mocas, movir)) return ncas, nelecas, mo
s = mol.intor('cint1e_ovlp_sph') t = mol.intor('cint1e_kin_sph') v = mol.intor('cint1e_nuc_sph') # Overlap, kinetic, nuclear attraction gradients (against electron coordinates) s1 = mol.intor('cint1e_ipovlp_sph', comp=3) t1 = mol.intor('cint1e_ipkin_sph', comp=3) v1 = mol.intor('cint1e_ipnuc_sph', comp=3) print('Dipole %s' % numpy.einsum('xij,ij->x', mol.intor('cint1e_r_sph', comp=3), dm)) # # AO overlap between two molecules # mol1 = gto.M(verbose=0, atom='H 0 1 0; H 1 0 0', basis='ccpvdz') s = gto.intor_cross('cint1e_ovlp_sph', mol, mol1) print('overlap shape (%d, %d)' % s.shape) # # 2e integrals. Keyword aosym is to specify the permutation symmetry in the # AO integral matrix. s8 means 8-fold symmetry, s2kl means 2-fold symmetry # for the symmetry between kl in (ij|kl) # eri = mol.intor('cint2e_sph', aosym='s8') # # 2e gradient integrals on first atom only # eri = mol.intor('cint2e_ip1_sph', aosym='s2kl') # # 2e integral gradients on certain atom
def _kernel(avas_obj): mf = avas_obj._scf mol = mf.mol log = logger.new_logger(avas_obj) log.info('\n** AVAS **') assert avas_obj.openshell_option != 1 if isinstance(mf, scf.uhf.UHF): log.note('UHF/UKS object is found. AVAS takes alpha orbitals only') mo_coeff = mf.mo_coeff[0] mo_occ = mf.mo_occ[0] mo_energy = mf.mo_energy[0] else: mo_coeff = mf.mo_coeff mo_occ = mf.mo_occ mo_energy = mf.mo_energy ncore = avas_obj.ncore nocc = numpy.count_nonzero(mo_occ != 0) ovlp = mol.intor_symmetric('int1e_ovlp') log.info(' Total number of HF MOs is equal to %d' ,mo_coeff.shape[1]) log.info(' Number of occupied HF MOs is equal to %d', nocc) mol = mf.mol pmol = mol.copy() pmol.atom = mol._atom pmol.unit = 'B' pmol.symmetry = False pmol.basis = avas_obj.minao pmol.build(False, False) baslst = pmol.search_ao_label(avas_obj.aolabels) log.info('reference AO indices for %s %s: %s', avas_obj.minao, avas_obj.aolabels, baslst) if avas_obj.with_iao: from pyscf.lo import iao c = iao.iao(mol, mo_coeff[:,ncore:nocc], avas_obj.minao)[:,baslst] s2 = reduce(numpy.dot, (c.T, ovlp, c)) s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:])) else: s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:,baslst] s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst] s21 = numpy.dot(s21, mo_coeff[:, ncore:]) sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True)) threshold = avas_obj.threshold if avas_obj.openshell_option == 2: wocc, u = numpy.linalg.eigh(sa[:(nocc-ncore), :(nocc-ncore)]) log.info('Option 2: threshold %s', threshold) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 mocore = mo_coeff[:,ncore:nocc].dot(u[:,wocc<threshold]) mocas = mo_coeff[:,ncore:nocc].dot(u[:,wocc>=threshold]) wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:,nocc:].dot(u[:,wvir>=threshold]))) movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold]) ncas = mocas.shape[1] occ_weights = numpy.hstack([wocc[wocc<threshold], wocc[wocc>=threshold]]) vir_weights = numpy.hstack([wvir[wvir>=threshold], wvir[wvir<threshold]]) elif avas_obj.openshell_option == 3: docc = nocc - mol.spin wocc, u = numpy.linalg.eigh(sa[:(docc-ncore),:(docc-ncore)]) log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin) ncas_occ = (wocc > threshold).sum() nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2 mocore = mo_coeff[:,ncore:docc].dot(u[:,wocc<threshold]) mocas = mo_coeff[:,ncore:docc].dot(u[:,wocc>=threshold]) wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):]) ncas_vir = (wvir > threshold).sum() mocas = numpy.hstack((mocas, mo_coeff[:,docc:nocc], mo_coeff[:,nocc:].dot(u[:,wvir>=threshold]))) movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold]) ncas = mocas.shape[1] occ_weights = numpy.hstack([wocc[wocc<threshold], numpy.ones(nocc-docc), wocc[wocc>=threshold]]) vir_weights = numpy.hstack([wvir[wvir>=threshold], wvir[wvir<threshold]]) else: raise RuntimeError(f'Unknown option openshell_option {avas_obj.openshell_option}') log.debug('projected occ eig %s', occ_weights) log.debug('projected vir eig %s', vir_weights) log.info('Active from occupied = %d , eig %s', ncas_occ, occ_weights[occ_weights>=threshold]) log.info('Inactive from occupied = %d', mocore.shape[1]) log.info('Active from unoccupied = %d , eig %s', ncas_vir, vir_weights[vir_weights>=threshold]) log.info('Inactive from unoccupied = %d', movir.shape[1]) log.info('Dimensions of active %d', ncas) nalpha = (nelecas + mol.spin) // 2 nbeta = nelecas - nalpha log.info('# of alpha electrons %d', nalpha) log.info('# of beta electrons %d', nbeta) mofreeze = mo_coeff[:,:ncore] if avas_obj.canonicalize: from pyscf.mcscf import dmet_cas def trans(c): if c.shape[1] == 0: return c else: csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff)) fock = numpy.dot(csc*mo_energy, csc.T) e, u = scipy.linalg.eigh(fock) return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log) if ncore > 0: mofreeze = trans(mofreeze) mocore = trans(mocore) mocas = trans(mocas) movir = trans(movir) mo = numpy.hstack((mofreeze, mocore, mocas, movir)) return ncas, nelecas, mo, occ_weights, vir_weights