def _core_val_ryd_list(mol): from pyscf.gto.ecp import core_configuration count = numpy.zeros((mol.natm, 9), dtype=int) core_lst = [] val_lst = [] rydbg_lst = [] k = 0 for ib in range(mol.nbas): ia = mol.bas_atom(ib) # Avoid calling mol.atom_charge because we should include ECP core electrons here nuc = mole.charge(mol.atom_symbol(ia)) l = mol.bas_angular(ib) nc = mol.bas_nctr(ib) symb = mol.atom_symbol(ia) nelec_ecp = mol.atom_nelec_core(ia) ecpcore = core_configuration(nelec_ecp) coreshell = [int(x) for x in AOSHELL[nuc][0][::2]] cvshell = [int(x) for x in AOSHELL[nuc][1][::2]] if mol.cart: deg = (l + 1) * (l + 2) // 2 else: deg = 2 * l + 1 for n in range(nc): if l > 3: rydbg_lst.extend(range(k, k+deg)) elif ecpcore[l]+count[ia,l]+n < coreshell[l]: core_lst.extend(range(k, k+deg)) elif ecpcore[l]+count[ia,l]+n < cvshell[l]: val_lst.extend(range(k, k+deg)) else: rydbg_lst.extend(range(k, k+deg)) k = k + deg count[ia,l] += nc return core_lst, val_lst, rydbg_lst
def _core_val_ryd_list(mol): from pyscf.gto.ecp import core_configuration count = numpy.zeros((mol.natm, 9), dtype=int) core_lst = [] val_lst = [] rydbg_lst = [] k = 0 for ib in range(mol.nbas): ia = mol.bas_atom(ib) # Avoid calling mol.atom_charge because we should include ECP core electrons here nuc = mole.charge(mol.atom_symbol(ia)) l = mol.bas_angular(ib) nc = mol.bas_nctr(ib) nelec_ecp = mol.atom_nelec_core(ia) ecpcore = core_configuration(nelec_ecp) coreshell = [int(x) for x in AOSHELL[nuc][0][::2]] cvshell = [int(x) for x in AOSHELL[nuc][1][::2]] if mol.cart: deg = (l + 1) * (l + 2) // 2 else: deg = 2 * l + 1 for n in range(nc): if l > 3: rydbg_lst.extend(range(k, k + deg)) elif ecpcore[l] + count[ia, l] + n < coreshell[l]: core_lst.extend(range(k, k + deg)) elif ecpcore[l] + count[ia, l] + n < cvshell[l]: val_lst.extend(range(k, k + deg)) else: rydbg_lst.extend(range(k, k + deg)) k = k + deg count[ia, l] += nc return core_lst, val_lst, rydbg_lst
def project_to_atomic_orbitals(mol, basname): '''projected AO = |bas><bas|ANO> ''' from pyscf.scf.addons import project_mo_nr2nr from pyscf.gto.ecp import core_configuration def search_atm_l(atm, l): idx = [] p0 = 0 for ib, b in enumerate(atm._bas): l0 = atm.bas_angular(ib) nctr = atm.bas_nctr(ib) if l0 == l: idx.extend(range(p0,p0+(2*l+1)*nctr)) p0 += (2*l0+1) * nctr return idx aos = {} atm = gto.Mole() atmp = gto.Mole() for symb in mol._basis.keys(): stdsymb = gto.mole._std_symbol(symb) atm._atm, atm._bas, atm._env = \ atm.make_env([[stdsymb,(0,0,0)]], {stdsymb:mol._basis[symb]}, []) if 'GHOST' in symb.upper(): aos[symb] = numpy.eye(atm.nao_nr()) continue s0 = atm.intor_symmetric('cint1e_ovlp_sph') basis_add = gto.basis.load(basname, stdsymb) atmp._atm, atmp._bas, atmp._env = \ atmp.make_env([[stdsymb,(0,0,0)]], {stdsymb:basis_add}, []) ano = project_mo_nr2nr(atmp, 1, atm) rm_ano = numpy.eye(ano.shape[0]) - reduce(numpy.dot, (ano, ano.T, s0)) nelec_ecp = 0 if mol._ecp: if symb in mol._ecp: nelec_ecp = mol._ecp[symb][0] elif stdsymb in mol._ecp: nelec_ecp = mol._ecp[stdsymb][0] ecpcore = core_configuration(nelec_ecp) c = rm_ano.copy() for l in range(param.L_MAX): idx = numpy.asarray(search_atm_l(atm, l)) if len(idx) == 0: break idxp = numpy.asarray(search_atm_l(atmp, l)) if l < 4: idxp = idxp[ecpcore[l]:] if len(idx) > len(idxp) > 0: # For angular l, first place the projected ANO, then the rest AOs. sdiag = reduce(numpy.dot, (rm_ano[:,idx].T, s0, rm_ano[:,idx])).diagonal() nleft = (len(idx) - len(idxp)) // (2*l+1) shell_average = numpy.einsum('ij->i', sdiag.reshape(-1,l*2+1)) shell_rest = numpy.argsort(-shell_average)[:nleft] idx_rest = [] for k in shell_rest: idx_rest.extend(idx[k*(2*l+1):(k+1)*(2*l+1)]) c[:,idx[:len(idxp)]] = ano[:,idxp] c[:,idx[len(idxp):]] = rm_ano[:,idx_rest] elif len(idxp) >= len(idx) > 0: # More ANOs than the mol basis functions c[:,idx] = ano[:,idxp[:len(idx)]] aos[symb] = c nao = mol.nao_nr() c = numpy.zeros((nao,nao)) p0 = 0 for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb in mol._basis: ano = aos[symb] else: symb = mol.atom_pure_symbol(ia) ano = aos[symb] p1 = p0 + ano.shape[1] c[p0:p1,p0:p1] = ano p0 = p1 return c
def project_to_atomic_orbitals(mol, basname): '''projected AO = |bas><bas|ANO> ''' from pyscf.scf.addons import project_mo_nr2nr from pyscf.scf import atom_hf from pyscf.gto.ecp import core_configuration def search_atm_l(atm, l): bas_ang = atm._bas[:, gto.ANG_OF] ao_loc = atm.ao_loc_nr() idx = [] for ib in numpy.where(bas_ang == l)[0]: idx.extend(range(ao_loc[ib], ao_loc[ib + 1])) return idx # Overlap of ANO and ECP basis 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) nelec_ecp_dic = {} for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb not in nelec_ecp_dic: nelec_ecp_dic[symb] = mol.atom_nelec_core(ia) aos = {} atm = gto.Mole() atmp = gto.Mole() for symb in mol._basis.keys(): stdsymb = gto.mole._std_symbol(symb) atm._atm, atm._bas, atm._env = \ atm.make_env([[stdsymb,(0,0,0)]], {stdsymb:mol._basis[symb]}, []) atm.cart = mol.cart s0 = atm.intor_symmetric('int1e_ovlp') if 'GHOST' in symb.upper(): aos[symb] = numpy.diag(1. / numpy.sqrt(s0.diagonal())) continue basis_add = gto.basis.load(basname, stdsymb) atmp._atm, atmp._bas, atmp._env = \ atmp.make_env([[stdsymb,(0,0,0)]], {stdsymb:basis_add}, []) atmp.cart = mol.cart nelec_ecp = nelec_ecp_dic[symb] if nelec_ecp > 0: ecpcore = core_configuration(nelec_ecp) # Comparing to ANO valence basis, to check whether the ECP basis set has # reasonable AO-character contraction. The ANO valence AO should have # significant overlap to ECP basis if the ECP basis has AO-character. if abs(ecp_ano_det_ovlp(atm, atmp, ecpcore)) > .1: aos[symb] = numpy.diag(1. / numpy.sqrt(s0.diagonal())) continue else: ecpcore = [0] * 4 ano = project_mo_nr2nr(atmp, 1, atm) rm_ano = numpy.eye(ano.shape[0]) - reduce(numpy.dot, (ano, ano.T, s0)) c = rm_ano.copy() for l in range(param.L_MAX): idx = numpy.asarray(search_atm_l(atm, l)) nbf_atm_l = len(idx) if nbf_atm_l == 0: break idxp = numpy.asarray(search_atm_l(atmp, l)) if l < 4: idxp = idxp[ecpcore[l]:] nbf_ano_l = len(idxp) if mol.cart: degen = (l + 1) * (l + 2) // 2 else: degen = l * 2 + 1 if nbf_atm_l > nbf_ano_l > 0: # For angular l, first place the projected ANO, then the rest AOs. sdiag = reduce( numpy.dot, (rm_ano[:, idx].T, s0, rm_ano[:, idx])).diagonal() nleft = (nbf_atm_l - nbf_ano_l) // degen shell_average = numpy.einsum('ij->i', sdiag.reshape(-1, degen)) shell_rest = numpy.argsort(-shell_average)[:nleft] idx_rest = [] for k in shell_rest: idx_rest.extend(idx[k * degen:(k + 1) * degen]) c[:, idx[:nbf_ano_l]] = ano[:, idxp] c[:, idx[nbf_ano_l:]] = rm_ano[:, idx_rest] elif nbf_ano_l >= nbf_atm_l > 0: # More ANOs than the mol basis functions c[:, idx] = ano[:, idxp[:nbf_atm_l]] sdiag = numpy.einsum('pi,pq,qi->i', c, s0, c) c *= 1. / numpy.sqrt(sdiag) aos[symb] = c nao = mol.nao_nr() c = numpy.zeros((nao, nao)) p1 = 0 for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb in mol._basis: ano = aos[symb] else: ano = aos[mol.atom_pure_symbol(ia)] p0, p1 = p1, p1 + ano.shape[1] c[p0:p1, p0:p1] = ano return c
def project_to_atomic_orbitals(mol, basname): '''projected AO = |bas><bas|ANO> ''' from pyscf.scf.addons import project_mo_nr2nr from pyscf.gto.ecp import core_configuration def search_atm_l(atm, l): idx = [] p0 = 0 for ib, b in enumerate(atm._bas): l0 = atm.bas_angular(ib) nctr = atm.bas_nctr(ib) if l0 == l: idx.extend(range(p0, p0 + (2 * l + 1) * nctr)) p0 += (2 * l0 + 1) * nctr return idx aos = {} atm = gto.Mole() atmp = gto.Mole() for symb in mol._basis.keys(): stdsymb = gto.mole._std_symbol(symb) atm._atm, atm._bas, atm._env = \ atm.make_env([[stdsymb,(0,0,0)]], {stdsymb:mol._basis[symb]}, []) if 'GHOST' in symb.upper(): aos[symb] = numpy.eye(atm.nao_nr()) continue s0 = atm.intor_symmetric('cint1e_ovlp_sph') basis_add = gto.basis.load(basname, stdsymb) atmp._atm, atmp._bas, atmp._env = \ atmp.make_env([[stdsymb,(0,0,0)]], {stdsymb:basis_add}, []) ano = project_mo_nr2nr(atmp, 1, atm) rm_ano = numpy.eye(ano.shape[0]) - reduce(numpy.dot, (ano, ano.T, s0)) nelec_ecp = 0 if mol._ecp: if symb in mol._ecp: nelec_ecp = mol._ecp[symb][0] elif stdsymb in mol._ecp: nelec_ecp = mol._ecp[stdsymb][0] ecpcore = core_configuration(nelec_ecp) c = rm_ano.copy() for l in range(param.L_MAX): idx = numpy.asarray(search_atm_l(atm, l)) if len(idx) == 0: break idxp = numpy.asarray(search_atm_l(atmp, l)) if l < 4: idxp = idxp[ecpcore[l]:] if len(idx) > len(idxp) > 0: # For angular l, first place the projected ANO, then the rest AOs. sdiag = reduce( numpy.dot, (rm_ano[:, idx].T, s0, rm_ano[:, idx])).diagonal() nleft = (len(idx) - len(idxp)) // (2 * l + 1) shell_average = numpy.einsum('ij->i', sdiag.reshape(-1, l * 2 + 1)) shell_rest = numpy.argsort(-shell_average)[:nleft] idx_rest = [] for k in shell_rest: idx_rest.extend(idx[k * (2 * l + 1):(k + 1) * (2 * l + 1)]) c[:, idx[:len(idxp)]] = ano[:, idxp] c[:, idx[len(idxp):]] = rm_ano[:, idx_rest] elif len(idxp) >= len( idx) > 0: # More ANOs than the mol basis functions c[:, idx] = ano[:, idxp[:len(idx)]] aos[symb] = c nao = mol.nao_nr() c = numpy.zeros((nao, nao)) p0 = 0 for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb in mol._basis: ano = aos[symb] else: symb = mol.atom_pure_symbol(ia) ano = aos[symb] p1 = p0 + ano.shape[1] c[p0:p1, p0:p1] = ano p0 = p1 return c
def project_to_atomic_orbitals(mol, basname): '''projected AO = |bas><bas|ANO> ''' from pyscf.scf.addons import project_mo_nr2nr from pyscf.scf import atom_hf from pyscf.gto.ecp import core_configuration def search_atm_l(atm, l): bas_ang = atm._bas[:, gto.ANG_OF] ao_loc = atm.ao_loc_nr() idx = [] for ib in numpy.where(bas_ang == l)[0]: idx.extend(range(ao_loc[ib], ao_loc[ib + 1])) return idx # Overlap of ANO and ECP basis 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) nelec_ecp_dic = {} for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb not in nelec_ecp_dic: nelec_ecp_dic[symb] = mol.atom_nelec_core(ia) aos = {} atm = gto.Mole() atmp = gto.Mole() for symb in mol._basis.keys(): stdsymb = gto.mole._std_symbol(symb) atm._atm, atm._bas, atm._env = \ atm.make_env([[stdsymb,(0,0,0)]], {stdsymb:mol._basis[symb]}, []) atm.cart = mol.cart atm._built = True s0 = atm.intor_symmetric('int1e_ovlp') if gto.is_ghost_atom(symb): aos[symb] = numpy.diag(1. / numpy.sqrt(s0.diagonal())) continue basis_add = gto.basis.load(basname, stdsymb) atmp._atm, atmp._bas, atmp._env = \ atmp.make_env([[stdsymb,(0,0,0)]], {stdsymb:basis_add}, []) atmp.cart = mol.cart atmp._built = True if symb in nelec_ecp_dic and nelec_ecp_dic[symb] > 0: # If ECP basis has good atomic character, ECP basis can be used in the # localization/population analysis directly. Otherwise project ECP # basis to ANO basis. if not PROJECT_ECP_BASIS: continue ecpcore = core_configuration(nelec_ecp_dic[symb]) # Comparing to ANO valence basis, to check whether the ECP basis set has # reasonable AO-character contraction. The ANO valence AO should have # significant overlap to ECP basis if the ECP basis has AO-character. if abs(ecp_ano_det_ovlp(atm, atmp, ecpcore)) > .1: aos[symb] = numpy.diag(1. / numpy.sqrt(s0.diagonal())) continue else: ecpcore = [0] * 4 # MINAO for heavier elements needs to be used with pseudo potential if (basname.upper() == 'MINAO' and gto.charge(stdsymb) > 36 and symb not in nelec_ecp_dic): raise RuntimeError( 'Basis MINAO has to be used with ecp for heavy elements') ano = project_mo_nr2nr(atmp, numpy.eye(atmp.nao_nr()), atm) rm_ano = numpy.eye(ano.shape[0]) - reduce(numpy.dot, (ano, ano.T, s0)) c = rm_ano.copy() for l in range(param.L_MAX): idx = numpy.asarray(search_atm_l(atm, l)) nbf_atm_l = len(idx) if nbf_atm_l == 0: break idxp = numpy.asarray(search_atm_l(atmp, l)) if l < 4: idxp = idxp[ecpcore[l]:] nbf_ano_l = len(idxp) if mol.cart: degen = (l + 1) * (l + 2) // 2 else: degen = l * 2 + 1 if nbf_atm_l > nbf_ano_l > 0: # For angular l, first place the projected ANO, then the rest AOs. sdiag = reduce( numpy.dot, (rm_ano[:, idx].T, s0, rm_ano[:, idx])).diagonal() nleft = (nbf_atm_l - nbf_ano_l) // degen shell_average = numpy.einsum('ij->i', sdiag.reshape(-1, degen)) shell_rest = numpy.argsort(-shell_average)[:nleft] idx_rest = [] for k in shell_rest: idx_rest.extend(idx[k * degen:(k + 1) * degen]) c[:, idx[:nbf_ano_l]] = ano[:, idxp] c[:, idx[nbf_ano_l:]] = rm_ano[:, idx_rest] elif nbf_ano_l >= nbf_atm_l > 0: # More ANOs than the mol basis functions c[:, idx] = ano[:, idxp[:nbf_atm_l]] sdiag = numpy.einsum('pi,pq,qi->i', c, s0, c) c *= 1. / numpy.sqrt(sdiag) aos[symb] = c nao = mol.nao_nr() c = numpy.zeros((nao, nao)) p1 = 0 for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb in mol._basis: ano = aos[symb] else: ano = aos[mol.atom_pure_symbol(ia)] p0, p1 = p1, p1 + ano.shape[1] c[p0:p1, p0:p1] = ano return c
def project_to_atomic_orbitals(mol, basname): '''projected AO = |bas><bas|ANO> ''' from pyscf.scf.addons import project_mo_nr2nr from pyscf.scf import atom_hf from pyscf.gto.ecp import core_configuration def search_atm_l(atm, l): bas_ang = atm._bas[:,gto.ANG_OF] ao_loc = atm.ao_loc_nr() idx = [] for ib in numpy.where(bas_ang == l)[0]: idx.extend(range(ao_loc[ib], ao_loc[ib+1])) return idx # Overlap of ANO and ECP basis 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) nelec_ecp_dic = {} for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb not in nelec_ecp_dic: nelec_ecp_dic[symb] = mol.atom_nelec_core(ia) aos = {} atm = gto.Mole() atmp = gto.Mole() for symb in mol._basis.keys(): stdsymb = gto.mole._std_symbol(symb) atm._atm, atm._bas, atm._env = \ atm.make_env([[stdsymb,(0,0,0)]], {stdsymb:mol._basis[symb]}, []) atm.cart = mol.cart atm._built = True s0 = atm.intor_symmetric('int1e_ovlp') if gto.is_ghost_atom(symb): aos[symb] = numpy.diag(1./numpy.sqrt(s0.diagonal())) continue basis_add = gto.basis.load(basname, stdsymb) atmp._atm, atmp._bas, atmp._env = \ atmp.make_env([[stdsymb,(0,0,0)]], {stdsymb:basis_add}, []) atmp.cart = mol.cart atmp._built = True if symb in nelec_ecp_dic and nelec_ecp_dic[symb] > 0: if not PROJECT_ECP_BASIS: # If ECP basis has good atomic character, ECP basis can be used in the # localization/population analysis directly. Otherwise project ECP basis to # ANO basis. continue ecpcore = core_configuration(nelec_ecp_dic[symb]) # Comparing to ANO valence basis, to check whether the ECP basis set has # reasonable AO-character contraction. The ANO valence AO should have # significant overlap to ECP basis if the ECP basis has AO-character. if abs(ecp_ano_det_ovlp(atm, atmp, ecpcore)) > .1: aos[symb] = numpy.diag(1./numpy.sqrt(s0.diagonal())) continue else: ecpcore = [0] * 4 ano = project_mo_nr2nr(atmp, numpy.eye(atmp.nao_nr()), atm) rm_ano = numpy.eye(ano.shape[0]) - reduce(numpy.dot, (ano, ano.T, s0)) c = rm_ano.copy() for l in range(param.L_MAX): idx = numpy.asarray(search_atm_l(atm, l)) nbf_atm_l = len(idx) if nbf_atm_l == 0: break idxp = numpy.asarray(search_atm_l(atmp, l)) if l < 4: idxp = idxp[ecpcore[l]:] nbf_ano_l = len(idxp) if mol.cart: degen = (l + 1) * (l + 2) // 2 else: degen = l * 2 + 1 if nbf_atm_l > nbf_ano_l > 0: # For angular l, first place the projected ANO, then the rest AOs. sdiag = reduce(numpy.dot, (rm_ano[:,idx].T, s0, rm_ano[:,idx])).diagonal() nleft = (nbf_atm_l - nbf_ano_l) // degen shell_average = numpy.einsum('ij->i', sdiag.reshape(-1,degen)) shell_rest = numpy.argsort(-shell_average)[:nleft] idx_rest = [] for k in shell_rest: idx_rest.extend(idx[k*degen:(k+1)*degen]) c[:,idx[:nbf_ano_l]] = ano[:,idxp] c[:,idx[nbf_ano_l:]] = rm_ano[:,idx_rest] elif nbf_ano_l >= nbf_atm_l > 0: # More ANOs than the mol basis functions c[:,idx] = ano[:,idxp[:nbf_atm_l]] sdiag = numpy.einsum('pi,pq,qi->i', c, s0, c) c *= 1./numpy.sqrt(sdiag) aos[symb] = c nao = mol.nao_nr() c = numpy.zeros((nao,nao)) p1 = 0 for ia in range(mol.natm): symb = mol.atom_symbol(ia) if symb in mol._basis: ano = aos[symb] else: ano = aos[mol.atom_pure_symbol(ia)] p0, p1 = p1, p1 + ano.shape[1] c[p0:p1,p0:p1] = ano return c