def molecular_frame_transformation(atomlist): """ The molecule is shifted to the center of mass and its principle axes of inertia are aligned with the coordinate axes. This standard orientation defines the molecular frame. The translation vector and Euler angles needed to transform the geometry from the molecular frame to the original frame are also returned. Returns: ======== atomlist_std: molecular geometry in standard orientation (a,b,g): Euler angles in z-y-z convention cm: 3D vector with center of mass """ pos = XYZ.atomlist2vector(atomlist) masses = AtomicData.atomlist2masses(atomlist) # shift center of mass to origin Nat = len(atomlist) pos_std = np.zeros(pos.shape) cm = center_of_mass(masses, pos) for i in range(0, Nat): pos_std[3*i:3*i+3] = pos[3*i:3*i+3] - cm (a,b,g) = euler_angles_inertia(masses, pos_std) R = EulerAngles2Rotation(a,b,g) for i in range(0, Nat): pos_std[3*i:3*i+3] = np.dot(R, pos_std[3*i:3*i+3]) atomlist_std = XYZ.vector2atomlist(pos_std, atomlist) # The MolecularCoord module uses a strange convention for Euler angles. # Therefore we extract the Euler angles in z-y-z convention directly # from the rotation matrix, that rotates the molecule from the standard # orientation into the original orientation Rinv = R.transpose() a,b,g = rotation2EulerAngles(Rinv, convention="z-y-z") return atomlist_std, (a,b,g), cm
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 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 velocities_finite_diff(atomlists, dt): """ compute the velocities from finite differences between two time-steps Parameters: =========== atomlists: list of geometries along trajectories for each time step dt: nuclear time step in a.u. Returns: ======== positions: list of vectors with positions for each time step velocities: list of vectors with velocities for each time step """ Nt = len(atomlists) positions = [] velocities = [] pos0 = XYZ.atomlist2vector(atomlists[0]) positions.append(pos0) for i in range(1, Nt): pos1 = XYZ.atomlist2vector(atomlists[i]) vel = (pos1 - pos0) / dt positions.append(pos1) velocities.append(vel) pos0 = pos1 velocities.append(vel) return positions, velocities
def calc_gradients(xyzfile, optionfile): """calculates DFTB energies and gradients for a molecule in the xyz_file with the options given in the optionfile""" outputfile = open("output_dftb.txt", "a") # redirect output to file sys.stdout = outputfile try: options = read_options(optionfile) # read options atomlist = XYZ.read_xyz(xyzfile)[0] # read structure init_options = extract_options(options, TD_INIT_OPTIONLIST) td_options = extract_options(options, TD_OPTIONLIST) grad_options = extract_options(options, GRAD_OPTIONS) kwds = XYZ.extract_keywords_xyz(xyzfile) tddftb = LR_TDDFTB(atomlist, **init_options) # create object tddftb.setGeometry(atomlist, charge=kwds.get("charge", 0.0)) tddftb.getEnergies(**td_options) # calculate energies grad = Gradients(tddftb) # calculate gradients grad.getGradients(**grad_options) energies = list(tddftb.dftb2.getEnergies()) # get partial energies if tddftb.dftb2.long_range_correction == 1: # add long range correction to partial energies energies.append(tddftb.dftb2.E_HF_x) return str(energies) except: print sys.exc_info() return "error"
def _getPES(self): """calculate energies and gradients at the image positions""" self.V = [None for Ri in self.R] # potential energies of each image self.F = [None for Ri in self.R] # true forces acting on each image if self.nr_processors > 1: # parallelize over images atomlists = [XYZ.vector2atomlist(x, self.atomlist) for x in self.R] image_dirs = ["IMAGE_%2.2d" % i for i in range(0, len(self.R))] pool = Pool(self.nr_processors) results = pool.map(run_gaussian_map, zip(atomlists, image_dirs)) for i, (en, grad) in enumerate(results): print "image %4.d energy= %+e |grad|= %e" % (i, en, la.norm(grad)) self.V[i] = en self.F[i] = -grad else: # sequential implementation for i, Ri in enumerate(self.R): atomlist = XYZ.vector2atomlist(Ri, self.atomlist) en, grad = run_gaussian(atomlist, directory="IMAGE_%2.2d" % i) self.V[i] = en self.F[i] = -grad print "image %4.d energy= %+e |grad|= %e" % (i, en, la.norm(grad))
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 get_active_space(xyzfile, optionfile): """calculates the active space for a given molecule and excited state (see http://www.dftbaby.chemie.uni-wuerzburg.de/DFTBaby/mdwiki.html#!WIKI/main_page.md, Active Space) not implemented in CAST because no use for ground state calculations for ground state calculations the active space can be set as small as desired""" outputfile = open("output_dftb.txt", "a") # redirect output to file sys.stdout = outputfile try: options = read_options(optionfile) # read options atomlist = XYZ.read_xyz(xyzfile)[0] # read structure init_options = extract_options(options, TD_INIT_OPTIONLIST) td_options = extract_options(options, TD_OPTIONLIST) kwds = XYZ.extract_keywords_xyz(xyzfile) tddftb = LR_TDDFTB(atomlist, **init_options) # create object tddftb.setGeometry(atomlist, charge=kwds.get("charge", 0.0)) tddftb.getEnergies(**td_options) # calculate energies occ, virt = tddftb.determineActiveSpace() return str((occ, virt)) except: print sys.exc_info() return "error"
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 _overlap_AB_f90(atomlist1, atomlist2, valorbs1, valorbs2, SKT): """ This function calls an external Fortran function that computes the matrix elements. Since hashes and tuples cannot be used easily in Fortran, the arguments are brought into a form that can be fed into the Fortran function. """ atom_type_dic, spline_deg, tab_filled_SH0, tab_filled_D, \ (S_knots, S_coefs, H_knots, H_coefs, D_knots, D_coefs) = \ combine_slako_tables_f90(SKT) # number of atoms Nat1 = len(atomlist1) Nat2 = len(atomlist2) # orbitals atom_indeces1, atom_types1, ls1, ms1 = atomlist2orbitals( atomlist1, valorbs1, atom_type_dic) atom_indeces2, atom_types2, ls2, ms2 = atomlist2orbitals( atomlist2, valorbs2, atom_type_dic) assert atom_indeces1 == atom_indeces2, "atomlist1 and atomlist2 should contain the same atoms in the same order!" # count valence orbitals Norb1 = len(ls1) Norb2 = len(ls2) # distances and direction pos1 = XYZ.atomlist2vector(atomlist1) pos1 = np.reshape( pos1, (Nat1, 3)).transpose() # pos(:,i) is the 3d position of atom i pos2 = XYZ.atomlist2vector(atomlist2) pos2 = np.reshape(pos2, (Nat2, 3)).transpose() r, x, y, z = slako.slako.directional_cosines(pos1, pos2) # call Fortran function S = slako.slako.overlap12(atom_indeces1, atom_types1, ls1, ms1, atom_indeces2, atom_types2, ls2, ms2, r, x, y, z, spline_deg, tab_filled_SH0, S_knots, S_coefs) return S
def calc_energies(xyzfile, optionfile): """calculates DFTB energies for a molecule in the xyz_file with the options given in the optionfile""" outputfile = open("output_dftb.txt", "a") # redirect output to file sys.stdout = outputfile try: options = read_options(optionfile) # read options atomlist = XYZ.read_xyz(xyzfile)[0] # read structure kwds = XYZ.extract_keywords_xyz(xyzfile) dftb2 = DFTB2(atomlist, **options) # create dftb object dftb2.setGeometry(atomlist, charge=kwds.get("charge", 0.0)) scf_options = extract_options(options, SCF_OPTIONLIST) # calculate energy 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"
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 add_polyatomic_curve(self, geometries, forces_wo_frep, forces_with_frep, \ curve_name="polyatomic", weight=1.0, max_error=0.01, active_atoms=None): """ compute generalized forces that act along the bond lengths and calculate repulsive forces for arbitrary non-linear molecule with more than 2 atoms. Parameters: =========== geometries: list of geometries (in atomlist format) forces_wo_frep: list of forces (in atomlist format) containing the DFTB forces only from the electronic hamiltonian forces_with_frep: list of forces (in atomlist format) containing the forces from a higher level method (including nuclear repulsion) curve_name: some identifier for plotting weight: assign a weight of this data set during fitting max_error: for large bond lengths and repulsive potentials that cannot be represented as a sum of pairwise potentials, the error incurred from a pairwise decomposition can be large. Those geometries where the error per atom exceeds max_error are not included in the fit. active_atoms: list of atomic indeces. Only atoms in this list take part in the fit. If set to [], all atoms are included. """ for atomlist, f_wo_rep, f_with_rep in zip(geometries, forces_wo_frep, forces_with_frep): # repulsive potential is the difference between true forces and electronic DFTB forces # in cartesian coordinates frep = XYZ.atomlist2vector(f_with_rep) - XYZ.atomlist2vector( f_wo_rep) forcelist = XYZ.vector2atomlist(frep, atomlist) from numpy.linalg.linalg import LinAlgError if active_atoms == []: # by default all atoms are included into the fit active_atoms = [i for i in range(0, len(atomlist))] try: bond_lengths, pair_forces, error = pairwise_decomposition( atomlist, forcelist) except LinAlgError: print "LINALG ERROR => do not include this geometry" continue if error > max_error: print "Error (%s) from pairwise decomposition too large (threshold: %s) => do not include this geometry!" % ( error, max_error) continue for (i, j) in bond_lengths.keys(): if (i not in active_atoms) or (j not in active_atoms): continue Zi = atomlist[i][0] Zj = atomlist[j][0] if (Zi == self.Z1 and Zj == self.Z2) or (Zi == self.Z2 and Zj == self.Z1): self.nuclear_separation.append(bond_lengths[(i, j)]) self.repulsive_gradient.append(-pair_forces[(i, j)]) # each point is weighted by 1/sigma self.weights.append(weight / max(error, 1.0e-2)) self.curve_ID.append(self.curve_counter) self.curve_names.append(curve_name) self.curve_counter += 1
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 load_fitpaths_it(self, fit_dir): """ load geometries of all fit paths from the folder `fit_dir` and return an iterator to the fit steps. Each fit step consists of the tuple (atomlist, en_rep, force_rep) The meaning of the elements of a fit step are as follows: atomlist : geometry of of fit step, list of tuples (Zi,[xi,yi,zi]) en_rep : energy difference (E_ref - E_elec) in Hartree force_rep: vector with force difference (F_ref - F_elec) in Hartree/bohr, force_rep[3*i:3*(i+1)] is the force [Fx(i),Fy(i),Fz(i)] on atom i. """ self.names = [ os.path.basename(xyz)[:-4] for xyz in glob.glob("%s/FIT_PATHS/*.xyz" % fit_dir) ] for name in self.names: print("Loading fit path '%s' " % name) geom_xyz = os.path.join(fit_dir, "FIT_PATHS", "%s.xyz" % name) en_dftb_dat = os.path.join(fit_dir, "DFTB", "%s.energies.dat" % name) en_ref_dat = os.path.join(fit_dir, "REFERENCE", "%s.energies.dat" % name) forces_dftb_xyz = os.path.join(fit_dir, "DFTB", "%s.forces.xyz" % name) forces_ref_xyz = os.path.join(fit_dir, "REFERENCE", "%s.forces.xyz" % name) try: geometries = XYZ.read_xyz(geom_xyz) energies_dftb = np.loadtxt(en_dftb_dat) energies_ref = np.loadtxt(en_ref_dat) forces_dftb = XYZ.read_xyz(forces_dftb_xyz) forces_ref = XYZ.read_xyz(forces_ref_xyz) for atomlist, en_dftb, en_ref, f_dftb, f_ref in zip( geometries, energies_dftb, energies_ref, forces_dftb, forces_ref): # compute E_rep = E_ref - E_el en_rep = en_ref - en_dftb # compute E_rep = F_ref - F_el force_rep = XYZ.atomlist2vector( f_ref) - XYZ.atomlist2vector(f_dftb) yield atomlist, en_rep, force_rep except IOError as e: print("Loading of fit path '%s' failed!" % name)
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 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 wigner_from_G09_hessian(g09_file, Nsample=100, zero_threshold=1.0e-9): """ create Wigner ensemble based on hessian matrix from Gaussian 09 calculation """ suffix = g09_file.split(".")[-1] if suffix in ["out", "log"]: print("Reading Gaussian 09 log file %s" % g09_file) atomlist = Gaussian.read_geometry(g09_file) forces = Gaussian.read_forces(g09_file) hess = Gaussian.read_force_constants(g09_file) elif suffix in ["fchk"]: print("Reading formatted Gaussian 09 checkpoint file %s" % g09_file) Data = Checkpoint.parseCheckpointFile(g09_file) # cartesian coordinates pos = Data["_Current_cartesian_coordinates"] atnos = Data["_Atomic_numbers"] # forces frc = -Data["_Cartesian_Gradient"] atomlist = [] forces = [] for i, Zi in enumerate(atnos): atomlist.append((Zi, tuple(pos[3 * i:3 * (i + 1)]))) forces.append((Zi, tuple(frc[3 * i:3 * (i + 1)]))) # Hessian hess = Data["_Cartesian_Force_Constants"] masses = np.array(AtomicData.atomlist2masses(atomlist)) x0 = XYZ.atomlist2vector(atomlist) x0 = shift_to_com(x0, masses) grad = -XYZ.atomlist2vector(forces) grad_nrm = la.norm(grad) print(" gradient norm = %s" % grad_nrm) # assert grad_nrm < 1.0e-3, "Gradient norm too large for minimum!" vib_freq, vib_modes = vibrational_analysis(hess, masses, zero_threshold=zero_threshold) Aw, Bw = wigner_distribution(x0, hess, masses, zero_threshold=zero_threshold) gw = GaussianWavepacket.Gaussian(Aw, Bw) qs, ps = gw.sample(Nsample) mx = np.outer(masses, np.ones(Nsample)) * qs avg_com = np.mean(np.sum(mx[::3, :], axis=0)), np.mean( np.sum(mx[1::3, :], axis=0)), np.mean(np.sum(mx[2::3, :], axis=0)) print(avg_com) geometries = [ XYZ.vector2atomlist(qs[:, i], atomlist) for i in range(0, Nsample) ] return geometries
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 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 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 build_xyz(self, a1, a2, a3, unitcell, meso, beta): n, m = self.grid.shape center = (n / 2, m / 2) flake_atomlist = [] unitcell_vec = XYZ.atomlist2vector(unitcell) for i in range(0, n): for j in range(0, m): if self.grid[i, j] == 1: if self.orientation[i, j] != 0: axis, angle = self.rotations[self.orientation[i, j]] Rot = axis_angle2rotation(axis, angle) else: Rot = np.identity(3) R = (i - n / 2) * a1 + (j - m / 2) * a2 + 0.0 * a3 shifted_unitcell_vec = np.zeros(unitcell_vec.shape) for at in range(0, len(unitcell)): # rotate and translate shifted_unitcell_vec[3*at:3*(at+1)] = \ np.dot(Rot, unitcell_vec[3*at:3*(at+1)]) + R flake_atomlist += XYZ.vector2atomlist( shifted_unitcell_vec, unitcell) # hydrogenize or add meso/beta substituents for (lr, tb) in self.moves: i_hyd = i + lr j_hyd = j + tb direction = lr * a1 + tb * a2 direction /= la.norm(direction) if 0 <= i_hyd < n and 0 <= j_hyd < m: if self.grid[i_hyd, j_hyd] != 1: # no neighbouring porphine in that direction => add both meso and beta hydrogens hydrogens = meso + beta # hydrogens can also contain protecting groups, so it's not only H-atoms! elif self.grid[i_hyd,j_hyd] == 1 \ and (((self.orientation[i,j] == 0) and (self.orientation[i_hyd,j_hyd] in [5,6])) \ or ((self.orientation[i_hyd,j_hyd] == 0) and (self.orientation[i,j] in [5,6]))): hydrogens = beta else: hydrogens = [] # add hydrogens hydadd = [] for at in range(0, len(hydrogens)): (Zat, pos) = hydrogens[at] hyddir = np.array(pos) hyddir /= la.norm(hyddir) angle = np.arccos(np.dot(hyddir, direction)) if abs(angle) < np.pi / 4.0: hydadd += [ (Zat, np.dot(Rot, np.array(pos)) + R) ] flake_atomlist += hydadd # meso-meso return flake_atomlist
def changeTrajectory(self): selected = self.trajSelection.selectedItems() trajs = map(int, [s.text() for s in selected]) self.geometries = [] self.nsteps = 0 print "Selected: %s" % map(str, trajs) print "Plot trajectories %s" % trajs for ax in self.axes: ax.clear() for t in trajs: trajdir = self.trajdirs[t] print trajdir try: self.plotTrajectory(trajdir, t) self.geometries.append( XYZ.read_xyz(os.path.join(trajdir, "dynamics.xyz")) ) self.nsteps = max(len(self.geometries[-1]), self.nsteps) except (ValueError, IOError) as e: print "Unable to load %s" % trajdir print "ERROR: %s" % e for canvas in self.canvases: canvas.draw() self.animationSlider.setMinimum(0) self.animationSlider.setMaximum(self.nsteps) self.animationTime.setText("Time: %7.2f fs" % 0.0) self.showMolecules()
def showMolecules(self): tstep = self.animationSlider.value() self.animationTime.setText("Time: %7.2f fs" % (tstep * self.dt)) self.mol.clear() for geoms in self.geometries: geom_time = geoms[min(tstep, len(geoms)-1)] # atoms atoms = [] for (Z,pos) in geom_time: a = self.mol.addAtom() a.pos = np.array(pos, dtype=float) a.atomicNumber = Z atoms.append(a) # bond C = XYZ.connectivity_matrix(geom_time) Nat = len(geom_time) for i in range(0, Nat): for j in range(i+1,Nat): if C[i,j] == 1: bond = self.mol.addBond() bond.setBegin(atoms[i]) bond.setEnd(atoms[j]) bond.order = 1 self.glWidget.mol = self.mol self.glWidget.updateGeometry() Avogadro.toPyQt(self.glWidget).update()
def __init__(self, atomlist): # convert the list of atoms into a list of 3d points nat = len(atomlist) pos = XYZ.atomlist2vector(atomlist) points = [] for i in range(0, nat): points.append(pos[3 * i:3 * (i + 1)]) # QHull needs at least 4 point to construct the initial # simplex. if nat < 4: # If there are less than 4 atoms, # we add the center of mass as an additional point masses = AtomicData.atomlist2masses(atomlist) com = MolCo.center_of_mass(masses, pos) points.append(com) if nat < 3: # If there are less than 3 atoms we add # an arbitrary point on the x-axis points.append(com + np.array([0.0005, 0.0, 0.0])) if nat < 2: # If there is only one atom, we add another arbitrary # point on the y-axis points.append(com + np.array([0.0, 0.0005, 0.0])) # We add small random numbers to the input coordinates, so that # we get a 3D convex hull even if the molecule is planar points = np.array(points) + 0.0001 * np.random.rand(len(points), 3) # find the convex hull using the qhull code hull = ConvexHull(points, qhull_options="QbB Qt") # call the constructor of the parent class (MinimalEnclosingBox) super(MoleculeBox, self).__init__(hull)
def get_unique_atomtypes(fit_dir): """ given a list of xyz-files, determine the atomic numbers that are present in any file. For each atom combination a repulsive potential is fitted. """ xyz_filenames = glob.glob("%s/FIT_PATHS/*.xyz" % fit_dir) unique_atomtypes = [] for f in xyz_filenames: for atomlist in XYZ.read_xyz_it(f): # atomic numbers in first frame for Zi, posi in atomlist: if not Zi in unique_atomtypes: unique_atomtypes.append(Zi) unique_atomtypes = np.sort(unique_atomtypes) print("") print("Atom Types:") print("-----------") for Zi in unique_atomtypes: print(" %s" % AtomicData.atom_names[Zi - 1]) print("") return unique_atomtypes
def _electrostatics(self): """ computes the electrostatic interaction between the MM charges, enCoul = - sum_i<j qi*qj/|Ri-Rj| and the gradient w/r/t to the positions of the individual atoms """ Nat = len(self.atomlist) x = XYZ.atomlist2vector(self.atomlist) # enCoul = 0.0 gradCoul = np.zeros(3 * Nat) # gradient for i in range(0, Nat): for j in range(i + 1, Nat): Rij_vec = x[3 * i:3 * (i + 1)] - x[3 * j:3 * (j + 1)] Rij = la.norm(Rij_vec) # contribution to energy Vij = self.mm_charges[i] * self.mm_charges[j] / Rij enCoul += Vij # contribution to the gradient eij = Rij_vec / Rij # unit vector gij = Vij / Rij * eij gradCoul[3 * i:3 * (i + 1)] -= gij gradCoul[3 * j:3 * (j + 1)] += gij if self.verbose > 0: print "MM electrostatic energy: %e" % enCoul return enCoul, gradCoul
def remove_overlapping_atoms(atomlist, tol=1.0e-4): print("remove overlapping atoms") nat = len(atomlist) # The i-th atom should be removed if duplicate[i] == 1 # Fortran rs = XYZ.atomlist2vector(atomlist) rs = np.reshape(rs, (nat, 3)) duplicate = thomson.thomson.remove_overlapping(rs, tol) # python """ duplicate_py = np.zeros(nat, dtype=int) for i in range(0, nat): for j in range(i+1,nat): posi = np.array(atomlist[i][1]) posj = np.array(atomlist[j][1]) Rij = la.norm(posi-posj) if Rij < tol: duplicate_py[j] = 1 assert np.sum(abs(duplicate - duplicate_py)) < 1.0e-10 """ # only copy unique atoms atomlist_unique = [] for i in range(0, nat): if duplicate[i] == 0: atomlist_unique.append( atomlist[i] ) return atomlist_unique
def load_fragments(frag_links_file): """ Load xyz-files for each fragment. To map the fragment names to the fragment xyz-files a file with the links has to be provided. Example for a fragments.lnk file: C60 C60.xyz Et ethylene.xyz Parameters: =========== frag_links_file: path that links fragment names with geometry definitions Returns: ======== fragments: dictionary with e.g. d["C60"] = (geometry of C60) """ frag_dir = dirname(frag_links_file) fh = open(frag_links_file) fragments = {} for l in fh: frag_name, frag_xyz_path = l.split() if not exists(frag_xyz_path): # not an absolute path, try relative path toe frag_dir frag_xyz_path = join(frag_dir, frag_xyz_path) fragments[frag_name] = XYZ.read_xyz(frag_xyz_path)[0] fh.close() return fragments
def getCenterOfMass(self): pos = XYZ.atomlist2vector(self.atomlist) com = center_of_mass(self.masses, pos) print("Center of Mass") print("==============") print(com) return com