def save_step(): """function is called after each minimization""" scan_coord = self.IC.coordinate_value(xi, IJKL) print("current value of scan coordinate : %s" % scan_coord) if i == 0: mode = "w" else: mode = "a" # save relaxed geometry of step i XYZ.write_xyz(self.xyz_scan, [atomlist], title="charge=%s energy=%s" % (self.geom_kwds.get("charge", 0), self.enI), mode=mode) # save table with energies along scan fh = open(self.dat_scan, mode) if i == 0: # write header print("# Relaxed scan along %s defined by atoms %s" % (coord_type[typ], [I + 1 for I in IJKL]), file=fh) print("# state of interest: %d" % self.state, file=fh) print("# ", file=fh) print("# Scan coordinate Energies ", file=fh) print("# %s Hartree " % units[typ], file=fh) print(" %8.5f " % scan_coord, end=' ', file=fh) for en in self.energies: print(" %e " % en, end=' ', file=fh) print("", file=fh) fh.close()
def eclipsed_oligomers(): # build eclipsed oligomers for n in [2, 3, 4, 5, 6, 7, 8]: distances = 2.5 / AtomicData.bohr_to_angs * np.ones(n) angles = np.zeros(n) Fn = build_polyfluoerene(distances, angles) XYZ.write_xyz("F%d_eclipsed.xyz" % n, [Fn])
def capped_uff_nanotube(cnt,NcapN=0, NcapS=0, optimize_uff=1, out_xyz="/tmp/cnt.xyz"): """ build capped nanotube and optimize it with UFF """ nC,nT = cnt.NrCapAtoms() print "Expected number of Thomson points nT = %s" % nT print "If the caps have holes or very close atoms, you should try increasing" print "or reducing the number of Thomson points (by changing the options NcapN or NcapS)" if NcapN == 0: NcapN = nT if NcapS == 0: NcapS = nT """ Different number of Thomson points are tested to find a pair (NcapN,NcapS) """ dcap = [] dnmin=0 dnmax=1 ntrial=5 for dnN in range(dnmin, dnmax): for dnS in range(dnmin, dnmax): # try for 5 times for n in range(0, ntrial): dcap.append( (dnN,dnS) ) dcap = sorted(dcap, key=lambda (a,b): abs(a)+abs(b)) for (dnN,dnS) in dcap: print "north cap: %s points, south cap: %s points" % (NcapN+dnN,NcapS+dnS) atomlist_carbons = capped_nanotube(cnt,NcapN+dnN,NcapS+dnS) if optimize_uff == 1: print "CNT will be optimized further with the Universal Force Field of G09" tmp_dir="/tmp" com_file = join(tmp_dir, "cnt_uff_opt.com") chk_file = join(tmp_dir, "cnt_uff_opt.chk") fchk_file = chk_file.replace(".chk", ".fchk") Gaussian.write_input(com_file, atomlist_carbons, \ route="# UFF Opt", \ title="Optimize (%s,%s)-Nanotube with UFF" % (cnt.n1,cnt.n2), chk=chk_file,mem=1000) try: Gaussian.run(com_file) # format checkpoint file Gaussian.formchk(chk_file, fchk_file) Data = Checkpoint.parseCheckpointFile(fchk_file) pos = Data["_Current_cartesian_coordinates"] atomlist_uff = XYZ.vector2atomlist(pos, atomlist_carbons) atomlist_carbons = atomlist_uff except Gaussian.GaussianError: print "UFF-optimization failed!" continue if check_connectivity(atomlist_carbons) == True: # no holes, correct connectivity break XYZ.write_xyz("/tmp/trials.xyz", [atomlist_carbons], mode="a") else: print "" XYZ.write_xyz(expandvars(expanduser(out_xyz)), [atomlist_carbons]) print "Geometry of capped CNT written to %s" % out_xyz
def flakes_to_xyz(grown_flakes, out_xyz, cc1=2.56, substitutions=[]): generations = grown_flakes.keys() generations.sort() lat, unitcell, meso, beta = zn_porphene_lattice( cc1=cc1, substitutions=substitutions) a1, a2, a3 = lat.getLatticeVectors("real") if out_xyz[-4:] == ".xyz": try: os.remove(opts.out_xyz) except OSError as e: print e for gen in generations: print "Generation %s (%s flakes)" % (gen, len(grown_flakes[gen])) print "=========================" for fl in grown_flakes[gen]: print "weight = %s" % fl.weight print fl flake_atomlist = fl.build_xyz(a1, a2, a3, unitcell, meso, beta) # reorder atomlist so that all hydrogens are placed at the end of the file flake_atomlist = XYZ.hydrogens_to_end(flake_atomlist) XYZ.write_xyz(expandvars(expanduser(out_xyz)), [flake_atomlist], title="weight=%d" % fl.weight, mode="a")
def run_gaussian(atomlist, directory="."): """ run Gaussian input script in `neb.gjf` and read energy and gradient from checkpoing file `grad.fchk`. Parameters ---------- atomlist: list of tuples with atomic numbers and cartesian positions (Zat,[x,y,z]) for each atom directory: directory where the calculation should be performed Returns ------- en : total energies of ground state (in Hartree) grad : gradient of total energy (in a.u.) """ # create directory if it does not exist already os.system("mkdir -p %s" % directory) os.system("cp neb.gjf %s/neb.gjf" % directory) # update geometry XYZ.write_xyz("%s/geometry.xyz" % directory, [atomlist]) # remove number of atoms and comment os.system("cd %s; tail -n +3 geometry.xyz > geom" % directory) # calculate electronic structure ret = os.system(r"cd %s; g09 < neb.gjf > neb.out" % directory) ret &= os.system(r"cd %s; formchk grad.chk >> neb.out" % directory) assert ret == 0, "Return status = %s, error in Gaussian calculation, see %s/neb.out!" % ( ret, directory) # read checkpoint files data = Checkpoint.parseCheckpointFile("%s/grad.fchk" % directory) en = data["_Total_Energy"] grad = data["_Cartesian_Gradient"] return en, grad
def alternately_rotated_oligomers(): # neighbouring fluorenes are rotated by +-10.5 degrees for n in [2, 3, 4, 5, 6, 7, 8]: distances = 2.5 / AtomicData.bohr_to_angs * np.ones(n) # alternate fluorenes with + or - 10.5 degrees angles = 21.0 / 2.0 * np.pi / 180.0 * pow(-1, np.arange(n)) Fn = build_polyfluoerene(distances, angles) XYZ.write_xyz("F%d_rotated.xyz" % n, [Fn])
def scan_angle_F2(): n = 2 distances = 2.5 / AtomicData.bohr_to_angs * np.ones(n) scan_geometries = [] for a in np.linspace(0.0, np.pi, 80.0): print(a * 180.0 / np.pi) angles = [a / 2.0, -a / 2.0] F2 = build_polyfluoerene(distances, angles) scan_geometries.append(F2) XYZ.write_xyz("F2_angle_scan.xyz", scan_geometries)
def plot(self, images=[], energies=[], istep=0): geometries = [XYZ.vector2atomlist(im, self.atomlist) for im in images] xyz_out = "neb_%s_%d.xyz" % (self.name, istep) XYZ.write_xyz(xyz_out, geometries) print("wrote path for iteration %d to %s" % (istep, xyz_out)) if energies != []: # first column: index of image # second column: energy of image data = np.vstack((np.arange(0, len(images)), energies)).transpose() np.savetxt("path_energies_%s_%d.dat" % (self.name, istep), data)
def addCap(self): # x_cap = initial_distribution_cap( self.Ncap) # positions of Thomson points on the cap # for points in the cap x[i,:] = (theta_i, phi_i) # and for points in the tube x[i,:] = (z_i, phi_i) s_cap = ["C" for i in range(0, self.Ncap)] # segment of carbo-nanotube # to which the Thomson point belongs, 'C' for cap and 'T' for tube o_cap = np.ones(self.Ncap, dtype=int) # o[i] == 1, position of i-th Thomson point is optimized # o[i] == 0, position is held constant # x_tube, o_tube, s_tube = thomson_tube(self.cnt.chiral_vector, self.cnt.tube_vector, self.cnt.a1, self.cnt.a2) # These unit vectors define the orientation of the # coordinate system ex = np.array([1, 0, 0]) ey = np.array([0, 1, 0]) ez = np.array([0, 0, 1]) self.axes = [ex, ey, ez] self.Z0 = 0.0 # combine Thomson point of cap with those of the tube self.x = np.vstack((x_cap, x_tube)) self.s = np.array(s_cap + s_tube) self.o = np.hstack((o_cap, o_tube)) self.N = len(self.x) if self.north_south == "N": pass elif self.north_south == "S": self.x = reflect_tube_xy(self.x, self.s, self.cnt.L) atomlist = self.thomson2atomlist() XYZ.write_xyz("/tmp/thomson_opt.xyz", [atomlist], mode="w") active, inactive = active_layers(self.x, self.s, self.cnt.a1, self.cnt.a2) def savexyz(): atomlist = self.thomson2atomlist() XYZ.write_xyz("/tmp/thomson_opt.xyz", [atomlist], mode="a") self.x[active, :], self.s[active], self.o[ active], self.en, self.grad = optimize_thomson(self.x[active, :], self.s[active], self.o[active], R=self.cnt.R, callback=savexyz) return (self.en, self.grad)
def f(x): thomson_cap = XYZ.vector2atomlist(x, thomson_cap_ref) # thomson_cap = enforce_constaints(thomson_cap) print "thomson_cap" print thomson_cap thomson_pts = thomson_tube + thomson_cap XYZ.write_xyz("/tmp/thomson_minimization.xyz", [thomson_cap], mode="a") en = potential_energy(thomson_pts) # gradient of potential energy grad = XYZ.atomlist2vector(gradient_energy(thomson_pts)) print "en = %s" % en return en #, grad
def save_xyz(x, mode="a"): global step if step % opts.save_every != 0: step += 1 return step += 1 atomlist_opt = XYZ.vector2atomlist(x, atomlist) atomlist_opt, dummy = enlarged_unitcell(atomlist_opt, lattice_vectors, nmax=opts.nr_unit_cells) XYZ.write_xyz(xyz_opt, [atomlist_opt], \ mode=mode)
def build_dimer(output_dir): cluster_template = XYZ.read_xyz("water_dimer.xyz")[0] names = ["S0->D0", "S0->D1", "S0->D2", "S0->D3", "S0->D4", "S0->D5"] ionization_energies = [11.55, 11.86, 13.43, 13.98, 17.71, 18.03] orbitals_list = [["1b1", "1b1"], ["1b1", "1b1"], ["3a1", "3a1"], ["3a1", "3a1"], ["1b2", "1b2"], ["1b2", "1b2"]] phases_list = [[ -1 , -1 ], [ +1, -1 ], [ +1 , +1 ], [ -1 , +1 ], [ 0, +1 ], [ +1 , 0 ]] dyson_orbs = [] for name,IE,orbitals,phases in zip(names, ionization_energies,orbitals_list,phases_list): cluster, orb = build_water_cluster(cluster_template, orbitals, phases) dyson_orbs.append(orb) # save geometry of cluster XYZ.write_xyz(join(output_dir, "water_cluster_2.xyz"), [cluster]) # and dyson orbitals dyson_orbs = np.array(dyson_orbs).transpose() save_dyson_orbitals(join(output_dir, "water_cluster_2.dyson"), names, ionization_energies, dyson_orbs)
def build_monomer(output_dir): cluster_template = XYZ.read_xyz("water_monomer.xyz")[0] names = ["1b1", "3a1", "1b2", "2a1"] ionization_energies = [12.5, 14.8, 18.5, 32.6] orbitals_list = [["1b1"], ["3a1"], ["1b2"], ["2a1"]] phases_list = [[ +1 ], [ +1 ], [ +1 ], [ +1 ]] dyson_orbs = [] for name,IE,orbitals,phases in zip(names, ionization_energies,orbitals_list,phases_list): cluster, orb = build_water_cluster(cluster_template, orbitals, phases) dyson_orbs.append(orb) # save geometry of cluster XYZ.write_xyz(join(output_dir, "water_cluster_1.xyz"), [cluster]) # and dyson orbitals dyson_orbs = np.array(dyson_orbs).transpose() save_dyson_orbitals(join(output_dir, "water_cluster_1.dyson"), names, ionization_energies, dyson_orbs)
def dupliverts(xyz_file, frag_links_file, out_xyz_file): """ """ fragments = load_fragments(frag_links_file) fragnames = fragments.keys() # copy keywords like charge, etc. kwds = XYZ.extract_keywords_xyz(xyz_file) title = reduce(lambda a, b: a + " " + b, ["%s=%s " % (k, v) for (k, v) in kwds.iteritems()], "") # for i, fraglist in enumerate(read_xyz_rotation_it(xyz_file)): if i == 0: mode = 'w' else: mode = 'a' XYZ.write_xyz(out_xyz_file, [substitute_fragments(fraglist, fragnames, fragments)], title=title, mode=mode)
def generate_scaled_geometries(self, scale_min=0.1, scale_max=1.9, Nscale=100): """ Parameters: =========== scale_min: scale factor for smallest structure scale_max: scale factor for largest structure Nscale: number of points in the interval [scale_min, scale_max] """ geometry = XYZ.read_xyz(self.xyz_in_file)[0] scaled_geoms = [] for s in linspace(scale_min, scale_max, Nscale): scaled_atomlist = [] for (Zi, (xi, yi, zi)) in geometry: scaled_atomlist.append((Zi, (xi * s, yi * s, zi * s))) scaled_geoms.append(scaled_atomlist) XYZ.write_xyz(self.xyz_scaled_file, scaled_geoms, title="scaled geometries")
cut_atomlist = [] for i,(Zi,posi) in enumerate(atomlist): if i in removed: pass else: cut_atomlist.append( (Zi, posi) ) return cut_atomlist if __name__ == "__main__": import sys from os.path import expandvars, expanduser from DFTB import XYZ usage = "python %s <in .xyz> <out .xyz> <radius in bohr>\n" % sys.argv[0] usage += " removes all atoms outside sphere of radius R and all connected atoms\n" if len(sys.argv) < 4: print(usage) exit(-1) xyz_in = expandvars(expanduser(sys.argv[1])) xyz_out = expandvars(expanduser(sys.argv[2])) R = float(sys.argv[3]) atomlist = XYZ.read_xyz(xyz_in)[0] cut_atomlist = cut_sphere(atomlist, R) print("Removed %d atoms" % (len(atomlist) - len(cut_atomlist))) XYZ.write_xyz(xyz_out, [cut_atomlist])
ps[3 * A:3 * (A + 1), i] *= 0.001 # # STATISTICS # kinetic energy Ekin = np.zeros(opts.Nsample) for i in range(0, opts.Nsample): Ekin[i] = np.sum(ps[:, i]**2 / (2 * np.array(masses))) # temperature (3*N-6)/2 k T = Ekin f = len(masses) - 6 # degrees of freedom minus rotation and translation T = Ekin * 2.0 / (f * AtomicData.kBoltzmann) print "Initial Condition Statistics" print "============================" print "averages are over %d trajectories" % opts.Nsample print "kinetic energy = (%2.7f +- %2.7f) Hartree" % (np.mean(Ekin), np.std(Ekin)) print "temperature = (%2.7f +- %2.7f) Kelvin" % (np.mean(T), np.std(T)) print "" # geometries only wigner_xyz = join(opts.outdir, "wigner.xyz") geometries = [ XYZ.vector2atomlist(qs[:, i], atomlist) for i in range(0, opts.Nsample) ] XYZ.write_xyz(wigner_xyz, geometries) print "Wigner ensemble written to file %s" % wigner_xyz # initial conditions HarmonicApproximation.save_initial_conditions(atomlist, qs, ps, opts.outdir, "dynamics")
def transform_turbomole_orbitals(tm_dir, dyson_file, method="atomwise", selected_orbitals=None): """ The molecular orbitals from a Turbomole calculation are transformed into the basis used by DFTB. Since in DFTB there are much less atomic orbitals this transformation is not bijective and does not conserve the normalization and orthogonality among the MOs. Parameters: =========== tm_dir: directory with coord, basis and mos files dyson_file: transformed MO coefficients for DFTB are written to this file method: Method used to find the coefficients in the minimal basis 'atomwise': overlaps are only calculated between orbitals on the same atom (very fast). 'grid': the DFT orbital mo(r) and the minimal DFT atomic orbitals ao_i(r) are calculated on a grid and the coefficients are obtained by minimizing the deviation error(c_i) = integral |mo(r) - sum_i c_i ao_i(r)|^2 dr in a least square sense (slower, but more accurate). selected_orbitals: list of indeces (starting at 1, e.g. '[1,2,3]') of orbitals that should be transformed. If not set all orbitals are transformed. """ print "Reading geometry and basis from Turbomole directory %s" % tm_dir Data = Turbomole.parseTurbomole(tm_dir + "/coord") atomlist = Data["coord"] Data = Turbomole.parseTurbomole(tm_dir + "/basis") basis_data = Data["basis"] bs_gaussian = GaussianBasisSet(atomlist, basis_data) # load Turbomole orbitals try: Data = Turbomole.parseTurbomole(tm_dir + "/mos") orbe,orbs_turbomole = Data["scfmo"] except IOError: print "'mos' file not found! Maybe this is an open-shell calculation. Trying to read file 'alpha'" Data = Turbomole.parseTurbomole(tm_dir + "/alpha") orbe,orbs_turbomole = Data["uhfmo_alpha"] # Which orbitals should be transformed? if selected_orbitals == None: selected_mos = np.array(range(0, len(orbe)), dtype=int) else: selected_mos = np.array(eval(selected_orbitals), dtype=int)-1 nmo = len(selected_mos) # transform them to DFTB basis if method == "atomwise": T = bs_gaussian.getTransformation_GTO_to_DFTB() orbs = np.dot(T, orbs_turbomole[:,selected_mos]) elif method == "grid": # define grid for integration (xmin,xmax),(ymin,ymax),(zmin,zmax) = Cube.get_bbox(atomlist, dbuff=7.0) dx,dy,dz = xmax-xmin,ymax-ymin,zmax-zmin ppb = 5.0 # Points per bohr nx,ny,nz = int(dx*ppb),int(dy*ppb),int(dz*ppb) x,y,z = np.mgrid[xmin:xmax:nx*1j, ymin:ymax:ny*1j, zmin:zmax:nz*1j] grid = (x,y,z) dV = dx/float(nx-1) * dy/float(ny-1) * dz/float(nz-1) # numerical atomic DFTB orbitals on the grid bs_atomic = AtomicBasisSet(atomlist) aos = [bf.amp(x,y,z) for bf in bs_atomic.bfs] nao = len(aos) # overlaps S2_mn = <m|n> S2 = np.zeros((nao,nao)) for m in range(0, nao): S2[m,m] = np.sum(aos[m]*aos[m]*dV) for n in range(m+1,nao): S2[m,n] = np.sum(aos[m]*aos[n]*dV) S2[n,m] = S2[m,n] # DFT molecular orbitals on the grid mos = [Cube.orbital_amplitude(grid, bs_gaussian.bfs, orbs_turbomole[:,i]).real for i in selected_mos] # overlaps S1_mi = <m|i>, m is an atomic DFTB orbital, i a molecular DFT orbital S1 = np.zeros((nao,nmo)) for m in range(0, nao): for i in range(0, nmo): S1[m,i] = np.sum(aos[m]*mos[i]*dV) # Linear regression leads to the matrix equation # sum_n S2_mn c_ni = S1_mi # which has to be solved for the coefficients c_ni. orbs = la.solve(S2, S1) else: raise ValueError("Method should be 'atomwise' or 'grid' but not '%s'!" % method) # normalize orbitals, due to the incomplete transformation they are not necessarily # orthogonal dftb2 = DFTB2(atomlist) dftb2.setGeometry(atomlist) S = dftb2.getOverlapMatrix() for i in range(0, nmo): norm2 = np.dot(orbs[:,i], np.dot(S, orbs[:,i])) orbs[:,i] /= np.sqrt(norm2) # save orbitals xyz_file = "geometry.xyz" XYZ.write_xyz(xyz_file, [atomlist]) print "Molecular geometry was written to '%s'." % xyz_file ionization_energies = -orbe[selected_mos] save_dyson_orbitals(dyson_file, ["%d" % (i+1) for i in selected_mos], ionization_energies, orbs, mode="w") print "MO coefficients for DFTB were written to '%s'." % dyson_file
coord_name = "DIHEDRAL(%d-%d-%d-%d)" % (I, J, K, L) else: raise ValueError("Format of scan '%s' not understood!" % scan) # shift indices to programmer's style (starting at 0) IJKL = map(lambda I: I - 1, IJKL) IC = InternalValenceCoords(atomlist0, freeze=freeze, verbose=opts.verbose) # cartesian coordinates along the scan scan_geometries = [atomlist0] # value of internal coordinate IJKL along the scan val0 = IC.coordinate_value(x0, IJKL) scan_coords = [val0] x1 = x0 for i in range(0, nsteps): # cartesian coordinates at displaced geometry x1 = IC.internal_step(x1, IJKL, incr) # new value of internal coordinate, should be approximately # val0 + incr val1 = IC.coordinate_value(x1, IJKL) atomlist1 = XYZ.vector2atomlist(x1, atomlist0) scan_geometries.append(atomlist1) scan_coords.append(val1) print "step %d %s = %10.6f" % (i + 1, coord_name, val1) XYZ.write_xyz(xyz_out, scan_geometries, mode="w") print "displaced geometries were saved to '%s'" % xyz_out
""" split an xyz file that contains many structures into separate files """ from DFTB import XYZ if __name__ == "__main__": import sys import optparse usage = "Usage: python %s <xyz file with many structures> <output prefix>\n" % sys.argv[ 0] usage += " splits the input file into output prefix_####.xyz" parser = optparse.OptionParser(usage) parser.add_option( "--step", dest="step", help="Only every N-th geometry is extracted [default: %default]", type=int, default=1) (opts, args) = parser.parse_args() if len(args) < 2: print(usage) exit(-1) xyz_file = sys.argv[1] prefix = sys.argv[2] structures = XYZ.read_xyz(xyz_file) for i, atomlist in enumerate(structures): if (i + 1) % opts.step == 0 or i == 0: XYZ.write_xyz(prefix + "%.4d.xyz" % (i + 1), [atomlist])
tmp_com = tmp_dir + "gaussian.com" tmp_out = tmp_dir + "gaussian.log" tmp_chk = tmp_dir + "gaussian.chk" if i == 0: guess="" else: # read starting MOs from checkpoint file guess="Guess=Read" Gaussian.write_input(tmp_com, atomlist, \ route="# %s/%s %s Force Integral=(Grid=Ultrafine) Geom=NoCrowd" % (options.method, options.basis_set, guess), \ chk_file=tmp_chk, \ title="Gradients for fitting repulsive potential", \ charge=options.charge, multiplicity=options.multiplicity) # compute forces with gaussian os.system("g09 < %s > %s" % (tmp_com, tmp_out)) en_tot = Gaussian.read_total_energy(tmp_out) print("Structure %d enTot= %s Hartree" % (i, en_tot)) forces_dic = Gaussian.read_forces(tmp_out) forces = [] for center in list(forces_dic.keys()): forces.append(forces_dic[center]) print("%2.7f" % en_tot, file=fh_en) XYZ.write_xyz(force_file, [forces], title="forces with %s/%s, total_energy=%2.7f" % (options.method, options.basis_set, en_tot), units="hartree/bohr", mode=mode) mode = 'a' fh_en.close()
usage += " To see a list of all available fragments run the program without the list parameter.\n" usage += " Type --help to see all options." parser = optparse.OptionParser(usage) parser.add_option( "--filter_mode", dest="filter_mode", help= "Determines whether the filter should 'keep' or 'delete' the selected fragments [default: %default]", default="keep") parser.add_option( "--out_xyz", dest="out_xyz", help="Filtered geometry is written to this file [default: %default]", default="filtered.xyz") (opts, args) = parser.parse_args() if len(args) < 1: print usage exit(-1) xyz_in = args[0] selected_fragments = args[1:] atomlist_full = XYZ.read_xyz(xyz_in)[-1] atomlist_filtered = filter_fragments(atomlist_full, selected_fragments, filter_mode=opts.filter_mode) if len(selected_fragments) > 0: XYZ.write_xyz(opts.out_xyz, [atomlist_filtered]) print "filtered geometry written to %s" % opts.out_xyz
import sys atomlist0 = XYZ.read_xyz(sys.argv[1])[-1] x0 = XYZ.atomlist2vector(atomlist0) IC = InternalValenceCoords(atomlist0, verbose=3) test_wilson_bmatrix(IC.force_field, x0) # 1) transform cartesian to internal coordinates, x0 ~ q0 q0 = IC.cartesian2internal(x0) # 2) Take a step along the last internal coordinate, # this will only work if the coordinate is not coupled # to any other. dq = np.zeros(len(q0)) dq[-1] = 0.1 q = q0 + dq # 3) and transform back x ~ q x = IC.internal2cartesian(q) # veriy that really x corresponds to internal coordinate q q_test = IC.cartesian2internal(x) err = la.norm(q - q_test) # assert err < 1.0e-10, "|q(x(q)) - q|= %e" % err # 4) save initial and displaced geometries atomlist = XYZ.vector2atomlist(x, atomlist0) XYZ.write_xyz("/tmp/test_internal_coords.xyz", [atomlist0, atomlist])
def save_xyz(x, mode="a"): atomlist_opt = XYZ.vector2atomlist(x, atomlist) XYZ.write_xyz(xyz_trace, [atomlist_opt], title="charge=%s" % kwds.get("charge", 0), mode=mode)
# import sys import os.path from DFTB import XYZ from DFTB.Formats.Gaussian2py import Gaussian if len(sys.argv) < 3: print("") print("Usage: %s log-file xyz-file" % os.path.basename(sys.argv[0])) print("") print(" extract all geometries and SCF energies from Gaussian log-file") print(" and write them to an xyz-file.") print("") exit(-1) # input file log_file = sys.argv[1] # output file xyz_file = sys.argv[2] atomlists = Gaussian.read_geometries_it(log_file) energies = Gaussian.read_scf_energies_it(log_file) titles = ["ENERGY= %f" % en for en in energies] XYZ.write_xyz(xyz_file, atomlists, title=titles) print("geometries written to '%s'" % xyz_file)
# interpolation parameter rs = np.linspace(0.0, 1.0, N) geometries_interp = [] if opts.coord_system == "cartesian": for r in rs: xr = x0 + r * (x1 - x0) geometries_interp.append(XYZ.vector2atomlist(xr, atomlist0)) elif opts.coord_system == "internal": # explicit bonds, shift atom indices from 1- to 0-indexing explicit_bonds = [(I - 1, J - 1) for (I, J) in eval(opts.explicit_bonds)] IC = InternalValenceCoords(atomlist0, explicit_bonds=explicit_bonds) # initial and final geometry in internal coordinates q1 = IC.cartesian2internal(x1) q0 = IC.cartesian2internal(x0) for r in rs: qr = q0 + r * (q1 - q0) xr = IC.internal2cartesian(qr) geometries_interp.append(XYZ.vector2atomlist(xr, atomlist0)) else: raise ValueError( "Coordinate system '%s' not understood, valid options are 'internal' and 'cartesian'" % opts.coord_system) XYZ.write_xyz(xyz_interp, geometries_interp) print "Interpolated geometries written to %s" % xyz_interp
x = XYZ.atomlist2vector(atomlist) pes.getEnergies(x) for i, atomlist in enumerate(XYZ.read_xyz(geom_file)): # compute electronic ground state forces with DFTB x = XYZ.atomlist2vector(atomlist) en = pes.getEnergy_S0(x) # total ground state energy including repulsive potential en_tot = en[0] print("Structure %d enTot= %s Hartree" % (i, en_tot)) # electronic energy without repulsive potential en_elec = pes.tddftb.dftb2.getEnergies()[2] gradVrep, gradE0, gradExc = pes.grads.gradient(I=0) # exclude repulsive potential from forces grad = gradE0 + gradExc forces = XYZ.vector2atomlist(-grad, atomlist) if i == 0: mode = 'w' else: mode = 'a' print("%2.7f" % en_elec, file=fh_en) XYZ.write_xyz( force_file, [forces], title="forces with DFTB, total_energy=%2.7f elec_energy=%2.7f" % (en_tot, en_elec), units="hartree/bohr", mode=mode) fh_en.close()
def opt(self, max_iter=5000, step_size=1.0, gtol=0.005): """ optimize MECI by following the gradient downhill Optional -------- max_iter : maximum number of steps step_size : The geometry is updated by making a step x -> x - step_size * grad gtol : tolerance for the gradient norm """ print("optimize MECI by following the gradient") print(" lower state : %d" % state1) print(" upper state : %d" % state2) print("Intermediate geometries are written to 'meci_path.xyz'") print("and a table with energies is written to 'meci_energies.dat'") # initial geometry x = XYZ.atomlist2vector(self.atomlist0) # overwrite geometries from previous run mode = "w" en_fh = open("meci_energies.dat", "w") print("# optimization of MECI", file=en_fh) print( "# STEP ENERGY(1)/Hartree ENERGY(2)/Hartree ENERGY(3)-EPS/Hartree", file=en_fh) for i in range(0, max_iter): e1, e2, grad = self.getGradient(x) # energy gap en_gap = e2 - e1 self.adjust_shift(en_gap) # save intermediate steps and energies atomlist = XYZ.vector2atomlist(x, self.atomlist0) XYZ.write_xyz("meci_path.xyz", [atomlist], title="ENERGY= %e GAP= %e" % (e2, en_gap), mode=mode) print(" %4.1d %+15.10f %+15.10f %+15.10f" % (i, e1, e2, e2 - self.epsilon), file=en_fh) en_fh.flush() # append to trajectory file mode = "a" # gnorm = la.norm(grad) print(" %4.1d e2= %e e2-e1= %e |grad|= %e (tolerance= %e)" % (i, e2, en_gap, gnorm, gtol)) if gnorm < gtol: break if self.coord_system == "cartesian": # descend along gradient directly in cartesian coordinates x -= step_size * grad elif self.coord_system == "internal": # use internal redundant coordinates # 1) transform cartesian to internal coordinates x -> q q = self.ic.cartesian2internal(x) # 2) transform cartesian gradient to internal coordinates # dE/dx -> dE/dq grad_intern = self.ic.transform_gradient(x, grad) # 3) take step along gradient in internal coordinates q = q - step_size * grad_intern # 4) deduce new cartesian coordinates from new internal coordinates q x = self.ic.internal2cartesian(q) else: print("exceeded maximum number of steps") en_fh.close()
def __init__(self, atomlist, hubbard_U): """ Parameters: =========== bfs: list of numeric basis functions bfs_aux: list of auxiliary basis functions """ self.atomlist = atomlist self.bfs = AtomicBasisSet(atomlist).bfs self.bfs_aux = AuxiliaryBasisSet(atomlist, hubbard_U).bfs # create a list of points that should have the symmetry # of the molecule on which the true density should agree # with the model density Xs,Ys,Zs = [], [], [] # atomic centers for Zi,posi in atomlist: x,y,z = posi Xs.append(x) Ys.append(y) Zs.append(z) # points between atoms for i,(Zi,posi) in enumerate(atomlist): for j,(Zj,posj) in enumerate(atomlist): if i == j: continue Rij = np.array(posj)-np.array(posi) x,y,z = np.array(posi) + 0.33*Rij Xs.append(x) Ys.append(y) Zs.append(z) x,y,z = np.array(posi) - 0.33*Rij Xs.append(x) Ys.append(y) Zs.append(z) # points perpendicular to plane between 3 atoms for i,(Zi,posi) in enumerate(atomlist): for j,(Zj,posj) in enumerate(atomlist): if i == j: continue for k,(Zk,posk) in enumerate(atomlist): if i == k: continue if i == j: continue Rij = np.array(posj) - np.array(posi) Rik = np.array(posk) - np.array(posi) x,y,z = np.array(posi) + 0.25*np.cross(Rij, Rik) Xs.append(x) Ys.append(y) Zs.append(z) Xs,Ys,Zs = np.mgrid[-6.0:6.0:30j,-6.0:6.0:30j,-6.0:6.0:30j] Xs = Xs.ravel() Ys = Ys.ravel() Zs = Zs.ravel() grid = np.array(Xs), np.array(Ys), np.array(Zs) # number of fit parameters self.Nfit = len(self.bfs_aux) assert self.Nfit % 4 == 0 # number of basis functions self.Nbfs = len(self.bfs) # number of sample points self.Npts = len(Xs) assert self.Npts > self.Nfit # save grid to xyz file gridlist = atomlist[:] for i in range(0, self.Npts): gridlist.append( (1, (Xs[i], Ys[i], Zs[i])) ) XYZ.write_xyz("/tmp/fitgrid.xyz", [gridlist]) # evaluate all basis functions on the grid self.bf_grid = np.zeros((self.Npts, self.Nbfs)) for m,bfm in enumerate(self.bfs): self.bf_grid[:,m] = bfm.amp(*grid) # evaluate fit functions on the grid self.bf_aux_grid = np.zeros((self.Npts, self.Nfit)) for m,bfm_aux in enumerate(self.bfs_aux): self.bf_aux_grid[:,m] = bfm_aux.amp(*grid) # M = self.bf_aux_grid self.M = M # constrained C.x - Q = 0 Id4 = np.eye(4) C = np.hstack([Id4 for i in range(0, self.Nfit/4)]) Ct = C.transpose() # Mt = M.transpose() MtM = np.dot(Mt, M) MtMinv = la.inv(MtM) # self.A1 = np.dot(C,np.dot(MtMinv, Mt)) self.A2 = np.dot(C,np.dot(MtMinv, Ct)) self.A3 = np.dot(MtMinv, Ct) self.A4 = np.dot(MtMinv,Mt)
def opt(xyzfile, optionfile): """performs an optimization""" outputfile = open("output_dftb.txt", "a") # redirect output to file sys.stdout = outputfile try: I = 0 # index of electronic state (ground state) atomlist = XYZ.read_xyz(xyzfile)[0] # read atomlist kwds = XYZ.extract_keywords_xyz( xyzfile) # read keywords from xyz-file (charge) options = read_options(optionfile) # read options scf_options = extract_options(options, SCF_OPTIONLIST) # get scf-options # optimization (taken from optimize.py) pes = MyPES(atomlist, options, Nst=max(I + 1, 2), **kwds) x0 = XYZ.atomlist2vector(atomlist) #convert geometry to a vector def f(x): save_xyz(x) # also save geometries from line searches if I == 0 and type(pes.tddftb.XmY) != type(None): # only ground state is needed. However, at the start # a single TD-DFT calculation is performed to initialize # all variables (e.g. X-Y), so that the program does not # complain about non-existing variables. enI, gradI = pes.getEnergyAndGradient_S0(x) else: energies, gradI = pes.getEnergiesAndGradient(x, I) enI = energies[I] print "E = %2.7f" % (enI) return enI, gradI xyz_trace = xyzfile.replace(".xyz", "_trace.xyz") # This is a callback function that is executed by numpy for each optimization step. # It appends the current geometry to an xyz-file. def save_xyz(x, mode="a"): atomlist_opt = XYZ.vector2atomlist(x, atomlist) XYZ.write_xyz(xyz_trace, [atomlist_opt], title="charge=%s" % kwds.get("charge", 0), mode=mode) save_xyz(x0, mode="w") # write original geometry Nat = len(atomlist) min_options = {'gtol': 1.0e-7, 'norm': 2} # The "BFGS" method is probably better than "CG", but the line search in BFGS is expensive. res = optimize.minimize(f, x0, method="CG", jac=True, callback=save_xyz, options=min_options) # res = optimize.minimize(f, x0, method="BFGS", jac=True, callback=save_xyz, options=options) xopt = res.x save_xyz(xopt) print "Intermediate geometries written into file {}".format(xyz_trace) # write optimized geometry into file atomlist_opt = XYZ.vector2atomlist(xopt, atomlist) xyz_opt = xyzfile.replace(".xyz", "_opt.xyz") XYZ.write_xyz(xyz_opt, [atomlist_opt], title="charge=%s" % kwds.get("charge", 0), mode="w") # calculate energy for optimized geometry dftb2 = DFTB2(atomlist_opt, **options) # create dftb object dftb2.setGeometry(atomlist_opt, charge=kwds.get("charge", 0.0)) dftb2.getEnergy(**scf_options) energies = list(dftb2.getEnergies()) # get partial energies if dftb2.long_range_correction == 1: # add long range correction to partial energies energies.append(dftb2.E_HF_x) return str(energies) except: print sys.exc_info() return "error"