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, embedding="electrostatic", verbose=0, name="uff", unique_tmp=False): self.embedding = embedding self.verbose = verbose self.atomlist = atomlist # find the scratch directory where Gaussian writes to try: scratch_dir = os.environ["GAUSS_SCRDIR"] except KeyError as e: print("WARNING: Environment variable GAUSS_SCRDIR not set!") print(" Check that g09 is installed correctly!") #raise e scratch_dir="./" if unique_tmp == True: name += "-" + str(uuid.uuid4()) # write input to temporary file self.com_file = join(scratch_dir, "%s.gcom" % name) self.chk_file = join(scratch_dir, "%s.chk" % name) self.fchk_file = join(scratch_dir, "%s.fchk" % name) self.log_file = join(scratch_dir, "%s.log" % name) # determine the connectivity of the atoms. The connectivity should not change # during a dynamics simulation, since this would lead to kinks in the potential # energy ConMat = XYZ.connectivity_matrix(atomlist) self.connectivity = [] # connectivity[i] is a list with atoms bonded to i for i in range(0, len(atomlist)): self.connectivity.append( list(np.where(ConMat[i,:] != 0)[0]) ) # first calculation is done only in order to get the # correct partial charges from Qeq """ if self.embedding == "electrostatic": self.calc(atomlist, do_Qeq=True) """ self.have_charges = False
def check_connectivity(atomlist): """ In a capped carbon nanotube, every atoms should be connected to 6 or 5 neighbours. If this is not the case, there must a hole somewhere. """ Con = XYZ.connectivity_matrix(atomlist) nr_neighbours = np.sum(Con, axis=1) con_min = nr_neighbours.min() con_max = nr_neighbours.max() print "nr_neighbours = %s" % nr_neighbours print "%s <= connectivity <= %s" % (con_min, con_max) if (3 <= con_min) and (con_max <= 3): return True else: print "Not all C-atoms are connected to 3. There must be a hole somewhere!" return False
def atomlist2graph(atomlist, hydrogen_bonds=False, conmat=None): """ Based on the adjacency matrix a molecular graph is constructed. """ Nat = len(atomlist) if conmat is None: # adjacency matrix, conmat[i,j] = 1 if there is a bond between atoms i and j conmat = XYZ.connectivity_matrix(atomlist, thresh=1.3, hydrogen_bonds=hydrogen_bonds) else: # check shape of adjacency matrix assert conmat.shape == (Nat, Nat) # visited flags the atoms that have been added to the graph to avoid # repetitions and to identify disconnected parts visited = [0 for i in range(0, Nat)] # atomic numbers Zs = [Zi for (Zi, posi) in atomlist] # This recursive function starts at one atoms and adds all the connected # atoms as child nodes def make_graph(i): graph = AtomNode(i, Zs[i]) visited[i] = 1 connected = np.where(conmat[i, :] == 1)[0] for j in connected: if visited[j] == 1: continue graph[j] = make_graph(j) visited[j] = 1 return graph # fragment_graphs = [] while True: # find the first atom that has not been visited yet try: start = visited.index(0) except ValueError: # all atoms have been visited break graph = make_graph(start) fragment_graphs.append(graph) #print "number of disconnected fragments: %d" % len(fragment_graphs) return fragment_graphs
def plot_planar_geometry(ax, atomlist, zaxis=2): Con = XYZ.connectivity_matrix(atomlist) ax.set_aspect('equal', 'datalim') ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) # atomic positions for A, (ZA, posA) in enumerate(atomlist): x, y = posA[0:2] atname = AtomicData.atom_names[ZA - 1] if atname in ["h", "c"]: # do not draw the carbon skeleton ax.plot([x], [y], "o", color="black", markersize=AtomicData.covalent_radii[atname] * 3.0) else: ax.text(x, y, "%s" % atname.capitalize(), ha='center', va='center') # bonds Nat = len(atomlist) for A in range(0, Nat): ZA, posA = atomlist[A] xA, yA = posA[0:2] connected_atoms = np.where(Con[A, :] > 0)[0] for Bcon in connected_atoms: ZB, posB = atomlist[Bcon] xB, yB, zB = posB[0:3] # if zB > 0.3: ax.annotate("", xy=(xA, yA), xytext=(xB, yB), arrowprops=dict(arrowstyle="wedge", connectionstyle="arc3", color="gray", lw=4)) elif zB < -0.3: ax.plot([xA, xB], [yA, yB], ls="-.", lw=4, color="gray") else: ax.plot([xA, xB], [yA, yB], ls="-", lw=4, color="gray")
def _update_visualization(self, atomlist): """ only change the underlying data but do not recreate visualization. This is faster and avoids jerky animations. """ vec = XYZ.atomlist2vector(atomlist) x, y, z = vec[::3], vec[1::3], vec[2::3] s = self.atom_radii if self.show_flags["atoms"] == False: # make atoms so small that one does not see them scale_factor = 0.01 self.shown_atoms = False else: scale_factor = 0.3 self.shown_atoms = True # update atom positions self.atoms.mlab_source.set(x=x,y=y,z=z,u=s,v=s,w=s, scalars=self.Zs, scale_factor=scale_factor) # atoms are coloured by their atomic number self.atoms.glyph.color_mode = "color_by_scalar" self.atoms.glyph.glyph_source.glyph_source.center = [0,0,0] self.atoms.module_manager.scalar_lut_manager.lut.table = self.lut self.atoms.module_manager.scalar_lut_manager.data_range = (0.0, 255.0) # update bonds self.bonds.mlab_source.reset(x=x,y=y,z=z, scale_factor=1.0) C = XYZ.connectivity_matrix(atomlist) Nat = len(atomlist) connections = [] for i in range(0, Nat): Zi, posi = atomlist[i] for j in range(i+1,Nat): if C[i,j] == 1: Zj, posj = atomlist[j] connections.append( (i,j) ) self.bonds.mlab_source.dataset.lines = np.array(connections) self.bonds.mlab_source.dataset.modified()
print " This script simply adds a 5th column with the atom type" print " and writes the result to the .ff-file ." print " " print " WARNING: The type assignment and the force field implementation itself probably have lots of bugs!" exit(-1) # laod xyz-file xyz_file = sys.argv[1] atomlist = XYZ.read_xyz(xyz_file)[0] # find total charge kwds = XYZ.extract_keywords_xyz(xyz_file) charge = kwds.get("charge", 0.0) nat = len(atomlist) # determine bond orders print "connectivity matrix" ConMat = XYZ.connectivity_matrix(atomlist, search_neighbours=200) print "bond order assignment" bondsTuples, bond_orders, lone_pairs, formal_charges = BondOrders.assign_bond_orders( atomlist, ConMat, charge=charge) # list of bond orders bond_orders_atomwise = [[] for i in range(0, nat)] # names of atoms connected to each bonded_atomnames = [[] for i in range(0, nat)] for i, (atI, atJ) in enumerate(bondsTuples): BO = bond_orders[i] bond_orders_atomwise[atI].append(BO) bond_orders_atomwise[atJ].append(BO) # name of atoms involved in this bond Zi = atomlist[atI][0] Zj = atomlist[atJ][0] atnameI = AtomicData.atom_names[Zi - 1].capitalize()
def __init__(self, atomlist, freeze=[], explicit_bonds=[], verbose=0): """ setup system of internal coordinates using valence bonds, angles and dihedrals Parameters ---------- atomlist : list of tuples (Z,[x,y,z]) with molecular geometry, connectivity defines the valence coordinates Optional -------- freeze : list of tuples of atom indices (starting at 0) corresponding to internal coordinates that should be frozen explicit_bonds : list of pairs of atom indices (starting at 0) between which artificial bonds should be inserted, i.e. [(0,1), (10,20)]. This allows to connect separate fragments. verbose : write out additional information if > 0 """ self.verbose = verbose self.atomlist = atomlist self.masses = AtomicData.atomlist2masses(self.atomlist) # Bonds, angles and torsions are constructed by the force field. # Atom types, partial charges and lattice vectors # all don't matter, so we assign atom type 6 (C_R, carbon in resonance) # to all atoms. atomtypes = [6 for atom in atomlist] partial_charges = [0.0 for atom in atomlist] lattice_vectors = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] # But since the covalent radii are wrong, we have to provide # the connectivity matrix conmat = XYZ.connectivity_matrix(atomlist, hydrogen_bonds=True) # insert artificial bonds for (I, J) in explicit_bonds: print("explicit bond between atoms %d-%d" % (I + 1, J + 1)) conmat[I, J] = 1 # Internal coordinates only work if the molecule does not # contain disconnected fragments, since there is no way how the # interfragment distance could be expressed in terms of internal coordinates. # We need to check that there is only a single fragment. fragment_graphs = MolecularGraph.atomlist2graph(self.atomlist, conmat=conmat) nr_fragments = len(fragment_graphs) error_msg = "The molecule consists of %d disconnected fragments.\n" % nr_fragments error_msg += "Internal coordinates only work if all atoms in the molecular graph are connected.\n" error_msg += "Disconnected fragments may be joined via an artificial bond using the\n" error_msg += "`explicit_bonds` option.\n" assert nr_fragments == 1, error_msg # Frozen degrees of freedom do not necessarily correspond to physical bonds # or angles. For instance we can freeze the H-H distance in water although there # is no bond between the hydrogens. To allow the definition of such 'unphysical' # internal coordinates, we have to modify the connectivity matrix and introduce # artificial bonds. for IJKL in freeze: if len(IJKL) == 2: I, J = IJKL # create artificial bond between atoms I and J conmat[I, J] = 1 elif len(IJKL) == 3: I, J, K = IJKL # create artifical bonds I-J and J-K so that the valence angle I-J-K exists conmat[I, J] = 1 conmat[J, K] = 1 elif len(IJKL) == 4: I, J, K, L = IJKL # create artifical bonds I-J, J-K and K-L so that the dihedral angle I-J-K-L # exists conmat[I, J] = 1 conmat[J, K] = 1 conmat[K, L] = 1 # cutoff for small singular values when solving the # linear system of equations B.dx = dq in a least square # sense. self.cond_threshold = 1.0e-10 self.force_field = PeriodicForceField(atomlist, atomtypes, partial_charges, lattice_vectors, [], connectivity_matrix=conmat, verbose=1) x0 = XYZ.atomlist2vector(atomlist) # shift molecule to center of mass self.x0 = MolCo.shift_to_com(x0, self.masses) self._selectActiveInternals(freeze=freeze)
def dual_lattice(atomlist): Con = XYZ.connectivity_matrix(atomlist) # place and atom between all closest pass
def find_beta_meso_carbons(atomlist): """ Assuming that `atomlist` contains the geometry of a beta-beta, meso-meso, beta-beta fused porphyrin polyomino, this functions finds the indices of the exterior beta and meso carbons, which can be identified by the two conditions 1) The carbon is bonded to a hydrogen atom 2) There are exactly one (for beta) or exactly two (for meso) nitrogen atoms in the list of second nearest neighbours. Parameters ---------- atomlist : geometry of porphyrin polyomino Returns ------- beta_carbons : list of indeces into atomlist (starting at 0) of beta carbons beta_hydrogens : list of indeces into atomlist (starting at 0) of hydrogens bound to beta carbons meso_carbons : list of indeces into atomlist (starting at 0) of meso carbons beso_hydrogens : list of indeces into atomlist (starting at 0) of hydrogens bound to meso carbons """ C = XYZ.connectivity_matrix(atomlist) # indices (into atomlist) of exterior beta and meso carbon atoms beta_carbons = [] meso_carbons = [] # indices (into atomlist) of beta and meso hydrogens beta_hydrogens = [] meso_hydrogens = [] for i, (Zi, pos) in enumerate(atomlist): # Is this a carbon atom ? if Zi != 6: continue # nearest neighbours neighbours1 = np.where(C[i, :] == 1)[0] for j in neighbours1: Zj = atomlist[j][0] # Is the carbon bonded to a hydrogen atom? if Zj == 1: break else: # No hydrogen was found in the list of nearest neighbours, # so this cannot be a meso carbon. continue # list of second nearest neighbours neighbours2 = [] for k in neighbours1: neighbours2 += list(np.where(C[k, :] == 1)[0]) # delete duplicates in list neighbours2 = list(set(neighbours2)) # Are there exactly two nitrogens in the list of second-nearest neighbour atoms? num_nitrogens = 0 for k in neighbours2: Zk = atomlist[k][0] if Zk == 7: num_nitrogens += 1 if num_nitrogens == 1: beta_carbons.append(i) beta_hydrogens.append(j) elif num_nitrogens == 2: meso_carbons.append(i) meso_hydrogens.append(j) return beta_carbons, beta_hydrogens, meso_carbons, meso_hydrogens
def morgan_ordering(atomlist, hydrogen_bonds=False): """ order atoms canonically using a variant of Morgan's algorithm according to Morgan, H.L., The Generation of a Unique Machine Description for Chemical Structures and "Morgan Revisited" by Figueras,J. J.Chem.Inf.Comput.Sci. 1993, 33, 717-718 Optional: ========= hydrogen_bonds: hydrogen bonds are included in the connectivity matrix Returns: ======== atomlist_can: canonically reordered atoms A_can: adjacency matrix for canonical order of atoms """ Nat = len(atomlist) # adjacency matrix, A[i,j] = 1 if there is a bond between atoms i and j A = XYZ.connectivity_matrix(atomlist, thresh=1.3, hydrogen_bonds=hydrogen_bonds) # initial invariants are the atom types v = [Zi for (Zi, posi) in atomlist] k = len(np.unique(v)) # for i in range(0, 100): #while True: vnext = np.dot(A, v) # count the number of unique invariants knext = len(np.unique(vnext)) v = vnext if knext <= k: # break k = knext else: raise Exception("Morgan algorithm did not converge in %d iterations!" % i) # Symmetry equivalent atoms have same labels in v, while symmetry-inequivalent # ones should have different labels #print "Labels" #print v # Now the molecular graph is traversed starting with the atom with the lowest # label. Nearest neighbours are added in the order of their invariants. # visited flags the atoms that have been added to the graph to avoid # repetitions and to identify disconnected parts visited = [0 for i in range(0, Nat)] def traverse_ordered(i, ordering=[]): # atoms are added as they are encountered while traversing the graph ordering += [i] # mark atom i as visited visited[i] = 1 # sort connected atoms by values of invariants connected = np.where(A[i, :] == 1)[0] sort_indx = np.argsort(v[connected]) # The child nodes are added in depth-first order. Nodes on the same level # are sorted by v. for j in connected[sort_indx]: if visited[j] == 1: continue ordering = traverse_ordered(j, ordering) return ordering # start with the atom with the lowest label istart = np.argmin(v) ordering = traverse_ordered(istart) assert len( ordering ) == Nat, "It seems that the molecule contains disconnected fragments. Try to compute the canonical ordering for each fragment separately." #print "Canonical Ordering" #print ordering # Atoms are reorderd according to the canonical ordering atomlist_can = [] for i in ordering: atomlist_can.append(atomlist[i]) # The rows of the adjacency matrix are also reordered A_can = A[ordering, :][:, ordering] # check """ print "A reordered" print A_can print "A calculated" print XYZ.connectivity_matrix(atomlist_can) """ return atomlist_can, A_can
def _create_visualization(self, atomlist): mlab = self.scene.mlab # draw atoms as balls vec = XYZ.atomlist2vector(atomlist) x, y, z = vec[::3], vec[1::3], vec[2::3] Zs = np.array([Z for (Z,pos) in atomlist]) atom_names = [AtomicData.atom_names[Z-1] for (Z,pos) in atomlist] rads = np.array([AtomicData.covalent_radii.get(atname) for atname in atom_names]) atom_radii = np.sqrt(rads)*1.8 s = atom_radii if self.show_flags["atoms"] == False: # make atoms so small that one does not see them scale_factor = 0.01 self.shown_atoms = False else: scale_factor = 0.45 self.shown_atoms = True atoms = mlab.quiver3d(x,y,z, s,s,s, scalars=Zs.astype(float), mode="sphere", scale_factor=scale_factor, resolution=20, figure=self.scene.mayavi_scene) # atoms are coloured by their atomic number atoms.glyph.color_mode = "color_by_scalar" atoms.glyph.glyph_source.glyph_source.center = [0,0,0] self.lut = atoms.module_manager.scalar_lut_manager.lut.table.to_array() for atname in set(atom_names): Z = AtomicData.atomic_number(atname) self.lut[Z,0:3] = ( np.array( atom_colours_rgb.get(atname, (0.0, 0.75, 0.75)) )*255.0 ).astype('uint8') atoms.module_manager.scalar_lut_manager.lut.table = self.lut atoms.module_manager.scalar_lut_manager.data_range = (0.0, 255.0) # draw bonds C = XYZ.connectivity_matrix(atomlist) Nat = len(atomlist) connections = [] bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20, figure=self.scene.mayavi_scene) for i in range(0, Nat): Zi, posi = atomlist[i] for j in range(i+1,Nat): if C[i,j] == 1: Zj, posj = atomlist[j] connections.append( (i,j) ) bonds.mlab_source.dataset.lines = np.array(connections) bonds.mlab_source.dataset.modified() tube = mlab.pipeline.tube(bonds, tube_radius=0.15, #tube_radius=0.05, figure=self.scene.mayavi_scene) tube.filter.radius_factor = 1.0 surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0), opacity=0.7, figure=self.scene.mayavi_scene) self.atoms = atoms self.bonds = bonds # self.tube = tube # self.surface = surface self.atom_radii = s self.Zs = Zs.astype(float) self.primitives.append(atoms) self.primitives.append(bonds) self.primitives.append(tube) self.primitives.append(surface) # Lewis structure if self.show_flags["Lewis structure"] == True: bondsTuples, bond_orders, lone_pairs, formal_charges = BondOrders.assign_bond_orders(atomlist, C, charge=0) # plot DOUBLE bonds double_bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20, figure=self.scene.mayavi_scene) connections_double = [] for i,bond in enumerate(bondsTuples): if bond_orders[i] == 2: # double bond between atoms I and J connections_double.append( bond ) double_bonds.mlab_source.dataset.lines = np.array(connections_double) double_bonds.mlab_source.dataset.modified() tube = mlab.pipeline.tube(double_bonds, tube_radius=0.35, figure=self.scene.mayavi_scene) tube.filter.radius_factor = 1.0 surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0), figure=self.scene.mayavi_scene) # self.double_bonds = double_bonds self.primitives.append(double_bonds) self.primitives.append(tube) self.primitives.append(surface) # # plot TRIPLE bonds triple_bonds = mlab.points3d(x,y,z, scale_factor=0.15, resolution=20, figure=self.scene.mayavi_scene) connections_triple = [] for i,bond in enumerate(bondsTuples): if bond_orders[i] == 3: # triple bond between atoms I and J connections_triple.append( bond ) triple_bonds.mlab_source.dataset.lines = np.array(connections_triple) triple_bonds.mlab_source.dataset.modified() tube = mlab.pipeline.tube(triple_bonds, tube_radius=0.35, figure=self.scene.mayavi_scene) tube.filter.radius_factor = 1.0 surface = mlab.pipeline.surface(tube, color=(0.8,0.8,0.0), figure=self.scene.mayavi_scene) # self.triple_bonds = triple_bonds self.primitives.append(triple_bonds) self.primitives.append(tube) self.primitives.append(surface) self.shown_lewis_structure = True
lines = fh.readlines() print "LINES = %s" % lines fh.close() for l in lines: print "LINE=%s" % line.strip() if ("The polytope has" in l) and ("vertices." in l): nr_vertices = int(l.split()[3]) elif "The number of lattice points is:" in l: print "##################################################" exit(-1) if len(l.split()) == 9: nr_inside = int(l.split()[7]) else: nr_inside = 0 break else: raise Exception("Latte calculation failed! See output") print "DONE" return nr_inside + nr_vertices if __name__ == "__main__": import sys xyz_file = sys.argv[1] atomlist = XYZ.read_xyz(xyz_file)[0] ConMat = XYZ.connectivity_matrix(atomlist) assign_bond_orders(atomlist, ConMat, latte_file=xyz_file.replace(".xyz", ".hrep.latte"))