def process(self, line): """an element name in the first column indicates the start of a basis set""" if (line[0].isalpha()): self.found_basis = True self.ncgbf = 0 (atom, self.basis_set) = line.strip().split(' ', 1) self.atno = atomic_number(atom) self.basis_data[self.atno] = [] elif (line[0] == ' '): if (self.found_basis): (a, b) = line.strip().split() if (self.ncgbf == 0): """read number of contractions and angular momentum, e.g. 5 s""" self.ncgbf = int(a) self.sym = b.strip().upper() self.prims = [] else: """read self.ncgbf exponent and coefficient pairs""" exp, coef = float(a), float(b) self.prims.append((exp, coef)) self.ncgbf -= 1 if (self.ncgbf == 0): """no more coefficients to read, save contraction""" self.basis_data[self.atno].append( (self.sym, self.prims)) """decrease the number of coefficients which are still to be read"""
def process(self, line): if "cycle" in line: """rest, we only want the converged gradient""" self.atomlist = [] self.coordvector = [] self.gradient = [] return parts = line.strip().split() if len(parts) == 4: x, y, z = float(parts[0].replace("D", "E")), float( parts[1].replace("D", "E")), float(parts[2].replace("D", "E")) atno = atomic_number(parts[3]) self.atomlist.append((atno, (x, y, z))) self.coordvector += [x, y, z] elif len(parts) == 3: gradx, grady, gradz = float(parts[0].replace("D", "E")), float( parts[1].replace("D", "E")), float(parts[2].replace("D", "E")) self.gradient += [gradx, grady, gradz]
def charge_fluctuation_functions(atom_name='h', confined=True): """ create charge fluctuation functions for valence shells Parameters ---------- atom_name : string with atom name Optional -------- confined : controls where confined atoms (True) or free atoms (False) are used Returns ------- ls : list of angular momenta of the valence shells Fs : list of charge fluctuation functions for each shell callable, Fs[i](x,y,z) evaluates the spherically averaged charged fluctuation function for the shell with angular momentum ls[i] """ Zat = atomic_number(atom_name) atomlist = [(Zat, (0, 0, 0))] # load atomic valence orbitals basis = AtomicBasisSet(atomlist, confined=confined) # group basis functions by angular momentum ls = [] shells = [] # contains list of basis functions for each shell for bf in basis.bfs: if len(ls) == 0 or bf.l > ls[-1]: # new shell begins ls.append(bf.l) # create new shell with current basis function as first element shells.append([bf]) else: # append basis function to current shell shells[-1].append(bf) # define charge fluctuation function for each shell Fs = [] # list of charge fluctuation functions for l, shell in zip(ls, shells): assert len(shell) == 2 * l + 1 Fs.append(spherically_averaged_density(shell, l)) return ls, Fs
def plot_charge_fluctuation_functions(atom_names, confined=True): """ plot numerical charge fluctuation functions computed from valence orbital density """ import matplotlib.pyplot as plt from DFTB.Parameters import get_hubbard_parameters plt.title("charge fluctuation functions") plt.xlabel("r / bohr", fontsize=17) plt.ylabel("numerical F(r)") r = np.linspace(0.01, 4.0, 200) x = r y = 0 * r z = 0 * r for atom_name in atom_names: lsA, FsA_numerical = charge_fluctuation_functions(atom_name, confined=confined) for la, Fa_numerical in zip(lsA, FsA_numerical): line, = plt.plot(r, Fa_numerical(x, y, z), lw=2, label=r"$F_{%s,%s}(r)$ (numerical)" % (atom_name.upper(), l2spec[la])) # compare with Gaussian fluctuation function for atom_name in atom_names: Zat = atomic_number(atom_name) hubbard_U = get_hubbard_parameters(None) # load Gaussian fluctuation functions, width is defined by the Hubbard parameter aux_basis = AuxiliaryBasisSet([(Zat, (0, 0, 0))], hubbard_U) # only the s-function is used Fa_gaussian = aux_basis.bfs[0] plt.plot(r, Fa_gaussian.amp(x, y, z), lw=2, label=r"$F_{%s}(r)$ (Gaussian)" % atom_name.upper(), ls="-.") plt.legend(ncol=2) plt.show()
def onsite_integrals(atom_name='h', confined=True): """ compute on-site Coulomb and exchange integrals for an atom Parameters ---------- atom_name : name of atom, e.g. 'h', 'c', etc. Optional -------- confined : controls where confined atoms (True) or free atoms (False) are used """ print( "computing on-site Coulomb and exchange integrals between valence orbitals of '%s'" % atom_name) Zat = atomic_number(atom_name) atomlist = [(Zat, (0, 0, 0))] basis = AtomicBasisSet(atomlist, confined=confined) density = AtomicDensitySuperposition(atomlist, confined=confined) #xc_functional = XCFunctionals.libXCFunctional("lda_x", "lda_c_pw") xc_functional = XCFunctionals.libXCFunctional("gga_x_pbe", "gga_c_pbe") for a, bfA in enumerate(basis.bfs): stra = "%d%s" % (bfA.n, angmom_to_xyz[(bfA.l, bfA.m)] ) # name of valence orbital, e.g. 1s, 2px, etc. for b, bfB in enumerate(basis.bfs): strb = "%d%s" % (bfB.n, angmom_to_xyz[(bfB.l, bfB.m)]) # Coulomb integral (aa|(1/r12 + fxc[rho0])|bb) eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB, bfB, density, xc_functional) print("Coulomb (%s,%s|{1/r12+f_xc[rho0]}|%s,%s)= %e" % (stra, stra, strb, strb, eri_aabb)) if a != b: # exchange integral (ab|(1/r12 + fxc[rho0])|ab) eri_abab = electron_repulsion_integral(atomlist, bfA, bfB, bfA, bfB, density, xc_functional) print("exchange (%s,%s|{1/r12+f_xc[rho0]}|%s,%s)= %e" % (stra, strb, stra, strb, eri_abab))
def onsite_integrals_unique(atom_name='h', confined=True): """ compute the 5 unique on-site electron integrals for an atom. Only s- and p-valence orbitals are considered. The 5 integrals are (ss|ss) (sp|sp) = (s px|s px) = (s py|s py) = (s pz|s pz) (ss|pp) = (s s |pxpx) = (s s |pypy) = (s s |pzpz) (pp|pp) = (pxpx|pxpx) = (pypy|pypy) = (pzpz|pzpz) (pp'|pp') = (pxpy|pxpy) = (pxpz|pxpz) = (pypz|pypz) Optional -------- confined : controls where confined atoms (True) or free atoms (False) are used Returns ------- unique_integrals : numpy array with unique on-site integrals [(ss|ss), (sp|sp), (ss|pp), (pp|pp), (pp'|pp')] References ---------- [1] http://openmopac.net/manual/1c2e.html """ print( "computing unique on-site electron integrals between valence orbitals of '%s'" % atom_name) print("only s- and p-functions are considered") Zat = atomic_number(atom_name) atomlist = [(Zat, (0, 0, 0))] basis = AtomicBasisSet(atomlist, confined=confined) density = AtomicDensitySuperposition(atomlist, confined=confined) #xc_functional = XCFunctionals.libXCFunctional("lda_x", "lda_c_pw") xc_functional = XCFunctionals.libXCFunctional("gga_x_pbe", "gga_c_pbe") unique_integrals = np.zeros(5) for a, bfA in enumerate(basis.bfs): for b, bfB in enumerate(basis.bfs): # choose index into unique_integrals to which the integrals is written if (bfA.l == 0 and bfB.l == 0): # (ss|ss) i = 0 elif (bfA.l == 0 and bfB.l == 1 and bfB.m == 0): # (sp|sp) and (ss|pp) i = 1 elif (bfA.l == 1 and bfA.m == 0 and bfB.l == 1 and bfB.m == 0): # (pp|pp) i = 3 elif (bfA.l == 1 and bfA.m == 0 and bfB.l == 1 and bfB.m == 1): # (pp'|pp') i = 4 else: continue # compute Coulomb integral (ab|(1/r12 + fxc[rho0])|ab) eri_abab = electron_repulsion_integral(atomlist, bfA, bfB, bfA, bfB, density, xc_functional) unique_integrals[i] = eri_abab if (i == 1): # in addition to (sp|sp) we also need to calculated (ss|pp) eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB, bfB, density, xc_functional) unique_integrals[2] = eri_aabb elif (i == 4): # in addition to (pp'|pp') we also compute (pp|p'p') and verify the identity # (pp|p'p') = (pp|pp) - 2 (pp'|pp') eri_aabb = electron_repulsion_integral(atomlist, bfA, bfA, bfB, bfB, density, xc_functional) assert ( eri_aabb - (unique_integrals[3] - 2 * unique_integrals[4])) < 1.0e-5 print("unique 1-center integrals (in eV) for atom '%s'" % atom_name) print(" (ss|ss) (sp|sp) (ss|pp) (pp|pp) (pp'|pp')") print(" %+.7f %+.7f %+.7f %+.7f %+.7f" % tuple(unique_integrals * hartree_to_eV)) return unique_integrals
#!/usr/bin/env python """ create a sequence of structures with different bond lengths between the two dimer atoms. """ from DFTB import XYZ from DFTB.AtomicData import bohr_to_angs, atomic_number from numpy import linspace if __name__ == "__main__": import sys if len(sys.argv) < 7: print("\nUsage: %s <atom 1> <atom 2> Rmin Rmax NR <dimer geometries>\n" % sys.argv[0]) print("create dimer geometries with NR different bond lengths in the interval [Rmin,Rmax].") print("Rmin and Rmax are in Angstrom.") exit(-1) ZA = int(atomic_number(sys.argv[1])) ZB = int(atomic_number(sys.argv[2])) Rmin, Rmax, NR = float(sys.argv[3]), float(sys.argv[4]), int(sys.argv[5]) dimer_file = sys.argv[6] def dimer(ZA, ZB, bond_length): atomA = (ZA, [0.0, 0.0, 0.0]) atomB = (ZB, [0.0, 0.0, bond_length]) return [atomA, atomB] bond_lengths = linspace(Rmin,Rmax,NR) XYZ.write_xyz(dimer_file, [dimer(ZA,ZB,bl/bohr_to_angs) for bl in bond_lengths], title="dimer trajectory")
print >> fh, "# repulsive potential in hartree/bohr" print >> fh, "Vrep = \\\n%s" % pp.pformat(self.Vrep) fh.close() if __name__ == "__main__": import sys usage = "Usage: %s <element1> <element2>\n" % sys.argv[0] usage += "element1 and element2 are the elements in the atom pair for which the repulsive potential should be fitted (e.g. h and c)." if len(sys.argv) < 3: print usage exit(-1) parser = utils.OptionParserFuncWrapper(ReppotFitter.fit, usage) (options, args) = parser.parse_args() Z1, Z2 = atomic_number(args[0]), atomic_number(args[1]) Fitter = ReppotFitter(Z1, Z2) print "Files with geometries and forces are read from stdin." print "Each line should be of the form:" print "<identifier> <xyz file with geometry> <xyz file with electronic dftb forces> <xyz file with forces from different method> \\" print " weight=<weight, positive float> max_error=<max. error per atom> \\" print " (active_atoms=<list of atoms IDs, e.g. 0,1,2>)" for line in sys.stdin.readlines(): if line.strip()[0] == "#": # ignore comments continue molname, geom_file, dftb_force_file, force_file, keywords_str = line.strip( ).split(None, 4) molname = molname.replace("__", " ") keywords = dict(
def process(self, line): parts = line.strip().split() x, y, z = float(parts[0]), float(parts[1]), float(parts[2]) atno = atomic_number(parts[3]) self.atomlist.append((atno, (x, y, z)))
if __name__ == "__main__": import sys import os.path if len(sys.argv) < 3: print("Usage: %s <atom A> <atom B>" % os.path.basename(sys.argv[0])) print(" plot gamma-integrals for atom combination A-B.") print( " <atom A> and <atom B> should be the names of the atoms, i.e. 'h' or 'c' etc." ) exit(-1) atom_nameA = sys.argv[1].upper() atom_nameB = sys.argv[2].upper() Za = atomic_number(atom_nameA) Zb = atomic_number(atom_nameB) # plot gamma functions for # ... confined pseudo atoms #from DFTB.SlaterKoster.confined_pseudo_atoms import gamma_integrals # ... free pseudo atoms from DFTB.SlaterKoster.free_pseudo_atoms import gamma_integrals try: gamma_dic = gamma_integrals.gamma_integrals[(Za, Zb)] except KeyError as e: print( "ERROR: No numerical gamma-functions found for atom pair %s-%s." % (atom_nameA, atom_nameB)) print(
def tabulate_gamma_integrals(atom_names, filename, confined=True): """ Compute the gamma integrals gamma_{A,lA,B,lB} = (F_{A,lA}|1/r12 + f_xc[rho0A+rho0B]|F_{B,lB}) for a range of distances for each atom combination and save them to a python file that can be imported Parameters ---------- atom_names : list of atom names, e.g. ['h','c',...] filename : path to a python file, where the integrals will be stored in a human-readable form Optional -------- confined : controls where confined atoms (True) or free atoms (False) are used Returns ------- nothing, data is written to a file """ import pprint import sys # interatomic distances for which gamma-function is computed numerically, # values in between need to be interpolated distances = np.linspace(0.001, 6.0, 60) gamma_integrals_dic = {} # enumerate all unique atom combinations n = len(atom_names) for a in range(0, n): for b in range(a, n): print("computing gamma integrals for atom combination %s-%s" % (atom_names[a].upper(), atom_names[b].upper())) gamma_dic = numerical_gamma_integrals(atom_names[a], atom_names[b], distances, confined=confined) Za = atomic_number(atom_names[a]) Zb = atomic_number(atom_names[b]) if Za > Zb: # swap atom, so that Za <= Zb tmp = Zb Zb = Za Za = tmp gamma_integrals_dic[(Za, Zb)] = gamma_dic fh = open(filename, "w") np.set_printoptions(threshold=sys.maxsize) pp = pprint.PrettyPrinter(depth=10) print("# This file has been generated automatically by %s." % sys.argv[0], file=fh) print("from numpy import array", file=fh) print("", file=fh) print("# atoms for which gamma-integrals are available", file=fh) print("atom_names = %s" % atom_names, file=fh) print("# distances in bohr for which gamma-integrals are tabulated", file=fh) print("r = %s" % pp.pformat(distances), file=fh) print( "# gamma-integrals gamma_{A,lA,B,lB} = (F_{A,lA}|1/r12 + f_xc[rho0A+rho0B]|F_{B,lB}) for each", file=fh) print( "# atom combination A-B. The integrals are stored in a nested dictionary, the keys", file=fh) print( "# on the first level are the atomic numbers (Za,Zb) with Za <= Zb, the keys on the", file=fh) print( "# second level are the angular momenta of the valence shell on atom A and B, i.e. (la,lb)", file=fh) print( "# For example the integral between the s-shell on carbon and the p-shell on nitrogen", file=fh) print("# would be stored in gamma_integrals[(6,7)][(0,1)]", file=fh) print("# | | ", file=fh) print("# (Za,Zb) (lA,lB)", file=fh) print("gamma_integrals = \\\n%s" % pp.pformat(gamma_integrals_dic), file=fh) print("gamma integrals for atoms %s written to '%s'" % (" ".join(atom_names), filename)) fh.close()
def numerical_gamma_integrals(atom_nameA, atom_nameB, distances, confined=True): """ compute the integrals gamma_{A,lA,B,lB} = (F_{A,lA}|1/r12 + f_xc[rho0A+rho0B]|F_{B,lB}) numerically on a multicenter grid Parameters ---------- atom_nameA, atom_nameB : names of interacting atoms, e.g. 'h' or 'c' distances : numpy array with interatomic separations (in bohr) for which the gamma integrals should be calculated Optional -------- confined : controls where confined atoms (True) or free atoms (False) are used Returns ------- gamma_dic : dictionary with values of gamma integrals on the distance grid, gamma_dic[(lA,lB)] is a numpy array holding the integrals between shell lA on atom A and shell lB on atom B """ # atomic numbers Za = atomic_number(atom_nameA) Zb = atomic_number(atom_nameB) # charge fluctuation functions for each shell lsA, FsA = charge_fluctuation_functions(atom_nameA, confined=confined) lsB, FsB = charge_fluctuation_functions(atom_nameB, confined=confined) xc_functional = XCFunctionals.libXCFunctional("lda_x", "lda_c_pw") gamma_dic = {} for la, Fa in zip(lsA, FsA): for lb, Fb in zip(lsB, FsB): print(" integral between %s-shell and %s-shell" % (l2spec[la], l2spec[lb])) gamma_ab = np.zeros(len(distances)) for i, r_ab in enumerate(distances): print(" %3.d of %d interatomic distance : %4.7f bohr" % (i + 1, len(distances), r_ab)) # atoms A and B are placed symmetrically on the z-axis, # separated by a distance of r_ab atomlist = [(Za, (0, 0, -0.5 * r_ab)), (Zb, (0, 0, +0.5 * r_ab))] # define displaced charge fluctuation functions def rhoAB(x, y, z): return Fa(x, y, z - 0.5 * r_ab) def rhoCD(x, y, z): return Fb(x, y, z + 0.5 * r_ab) # rho0 = AtomicDensitySuperposition(atomlist, confined=confined) # evaluate integral gamma_ab[i] = electron_repulsion_integral_rho( atomlist, rhoAB, rhoCD, rho0, xc_functional) # save gamma integral for the interaction of a shell with angular momentum la on atom A # and a shell with angular momentum lb on atom B gamma_dic[(la, lb)] = gamma_ab return gamma_dic