def test_one(xyz_fn, checks, reorder=False): mol = XYZFile(xyz_fn).get_molecule() graph = MolecularGraph.from_geometry(mol) zmat_gen = ZMatrixGenerator(graph) if reorder is False: self.assertArraysEqual(zmat_gen.new_index, numpy.arange(mol.size)) self.assertArraysEqual(zmat_gen.old_index, numpy.arange(mol.size)) zmat0 = zmat_gen.cart_to_zmat(mol.coordinates) for field, index, value in checks: self.assertAlmostEqual(zmat0[field][index], value, 2, "%s:%i %f!=%f" % (field,index,zmat0[field][index],value)) numbers0, coordinates0 = zmat_to_cart(zmat0) mol0 = Molecule(numbers0, coordinates0) mol0.write_to_file("output/zmat_%s" % os.path.basename(xyz_fn)) graph0 = MolecularGraph.from_geometry(mol0) zmat_gen0 = ZMatrixGenerator(graph0) self.assertArraysEqual(zmat_gen0.new_index, numpy.arange(mol.size)) self.assertArraysEqual(zmat_gen0.old_index, numpy.arange(mol.size)) zmat1 = zmat_gen0.cart_to_zmat(mol0.coordinates) for field, index, value in checks: self.assertAlmostEqual(zmat1[field][index], value, 2, "%s:%i %f!=%f" % (field,index,zmat1[field][index],value)) numbers1, coordinates1 = zmat_to_cart(zmat1) self.assertArraysEqual(numbers0, numbers1) self.assertArraysAlmostEqual(coordinates0, coordinates1, 1e-5)
def get_system(self, name): if name == "chabazite_octane": molecule = XYZFile("input/chabazite_octane.xyz").get_molecule() unit_cell = UnitCell( numpy.array([ [14.767, 0, 0], [0, 23.686, 0], [0, 0, 13.675], ])*angstrom, numpy.array([True, True, True], bool), ) molecular_graph = MolecularGraph.from_geometry(molecule) return molecule.coordinates, molecular_graph, unit_cell elif name == "sodalite_ethane": molecule = XYZFile("input/sodalite_ethane.xyz").get_molecule() unit_cell = UnitCell( numpy.array([ [8.956, 0, 0], [0, 8.956, 0], [0, 0, 8.956], ])*angstrom, numpy.array([True, True, True], bool), ) molecular_graph = MolecularGraph.from_geometry(molecule) return molecule.coordinates, molecular_graph, unit_cell else: raise Exception("Unknown system.")
def _check_topology(self): ''' Check wether all topology information (bonds, bends, dihedrals, out-of-plane patterns and neighborlist) is present and complete if necessary. ''' assert self.numbers is not None if self.bonds is None: molecule = Molecule(self.numbers, coordinates=self.ref.coords) graph = MolecularGraph.from_geometry(molecule) psf = PSFFile() psf.add_molecule(molecule) self.bonds = np.array(psf.bonds) self.bends = np.array(psf.bends) self.diheds = np.array(psf.dihedrals) self.opdists = find_opdist_patterns(graph) self.nlist = graph.neighbors else: graph = MolecularGraph(self.bonds, self.numbers) psf = PSFFile() psf.add_molecular_graph(graph) if self.bends is None: self.bends = np.array(psf.bends) if self.diheds is None: self.diheds = np.array(psf.dihedrals) if self.opdists is None: self.opdists = find_opdist_patterns(graph) if self.nlist is None: self.nlist = graph.neighbors
def endElement(self, name): #print "END", name if name == 'molecule': if len(self.current_numbers) > 0: self.current_coordinates = np.array(self.current_coordinates)*angstrom molecule = Molecule(self.current_numbers, self.current_coordinates, self.current_title) molecule.extra = self.current_extra molecule.atoms_extra = self.current_atoms_extra name_to_index = {} for counter, name in enumerate(self.current_atom_names): name_to_index[name] = counter edges = set() current_bonds_extra = {} for name1, name2, extra in self.current_bonds: i1 = name_to_index.get(name1) i2 = name_to_index.get(name2) if i1 is not None and i2 is not None: edge = frozenset([i1, i2]) if len(extra) > 0: current_bonds_extra[edge] = extra edges.add(edge) molecule.bonds_extra = current_bonds_extra if len(edges) == 0: molecule.graph = None else: molecule.graph = MolecularGraph(edges, self.current_numbers) del self.current_atom_names del self.current_bonds self.molecules.append(molecule) self.current_title = None
def add_molecule(self, molecule, atom_types=None, charges=None, split=True): molecular_graph = MolecularGraph.from_geometry(molecule) self.add_molecular_graph(molecular_graph, atom_types, charges, split)
def create_molecular_graph(selected_nodes, parent=None): molecule = create_molecule(selected_nodes, parent) atom_indexes = dict((atom,i) for i,atom in enumerate(molecule.atoms)) bonds = list( bond for bond in yield_bonds(selected_nodes) if bond.children[0].target in atom_indexes and bond.children[1].target in atom_indexes ) pairs = [( atom_indexes[bond.children[0].target], atom_indexes[bond.children[1].target], ) for bond in bonds] graph = MolecularGraph(pairs, molecule.numbers) graph.bonds = bonds graph.molecule = molecule return graph
def set_default_graph(self): """Set self.graph to the default graph. This method is equivalent to:: mol.graph = MolecularGraph.from_geometry(mol) with the default options, and only works if the graph object is not present yet. See :meth:`molmod.molecular_graphs.MolecularGraph.from_geometry` for more fine-grained control over the assignment of bonds. """ self.graph = MolecularGraph.from_geometry(self)
def compute_rotsym(self, threshold=1e-3*angstrom): """Compute the rotational symmetry number. Optional argument: | ``threshold`` -- only when a rotation results in an rmsd below the given threshold, the rotation is considered to transform the molecule onto itself. """ # Generate a graph with a more permissive threshold for bond lengths: # (is convenient in case of transition state geometries) graph = MolecularGraph.from_geometry(self, scaling=1.5) try: return compute_rotsym(self, graph, threshold) except ValueError: raise ValueError("The rotational symmetry number can only be computed when the graph is fully connected.")
def test_consistency(self): molecules = [ XYZFile("input/cyclopentane.xyz").get_molecule(), XYZFile("input/funny.xyz").get_molecule(), ] for m in molecules: m.graph = MolecularGraph.from_geometry(m) dump_cml("output/tmp.cml", molecules) check = load_cml("output/tmp.cml") for m1, m2 in zip(molecules, check): self.assertEqual(m1.title, m2.title) self.assert_((m1.numbers==m2.numbers).all()) self.assert_((m1.coordinates==m2.coordinates).all()) self.assertEqual(m1.graph.num_nodes, m2.graph.num_nodes) self.assertEqual(set(m1.graph.pairs), set(m2.graph.pairs))
def add_molecule(self, molecule, atom_types=None, charges=None, split=True): """Add the graph of the molecule to the data structure The molecular graph is estimated from the molecular geometry based on interatomic distances. Argument: | ``molecule`` -- a Molecule instance Optional arguments: | ``atom_types`` -- a list with atom type strings | ``charges`` -- The net atom charges | ``split`` -- When True, the molecule is split into disconnected molecules [default=True] """ molecular_graph = MolecularGraph.from_geometry(molecule) self.add_molecular_graph(molecular_graph, atom_types, charges, split, molecule)
def all_lone_pairs(molecule, singles=[7], doubles=[8], angle=1.910): """ Returns a list with pairs (index, lone), where index indicates the atom and lone is a relative vector with unit length pointing along a lone pair on that atom. Arguments: molecule -- A molecule for which the lone pairs should be calculated. singles -- A list of atom number which should be treated like nitrogen. doubles -- A list of atom numbers which should be treated like oxygen. angle -- The angle between two lone pairs on the same atom. (in rad) Returns: lone_pairs -- a list with pairs (index, lone) """ result = [] mgraph = MolecularGraph(molecule) for index, (number, coordinate) in enumerate( zip(molecule.numbers, molecule.coordinates)): if number in singles: neighbors = mgraph.neighbors[index] result.append( (index, lone_pair_1(molecule.coordinates[neighbors[0]] - coordinate, molecule.coordinates[neighbors[1]] - coordinate, molecule.coordinates[neighbors[2]] - coordinate))) elif number in doubles: neighbors = mgraph.neighbors[index] lone1, lone2 = lone_pair_2( molecule.coordinates[neighbors[0]] - coordinate, molecule.coordinates[neighbors[1]] - coordinate, angle) result.append((index, lone1)) result.append((index, lone2)) return result
def get_molecular_graph(self): """Return the molecular graph represented by the data structure""" return MolecularGraph(self.bonds, self.numbers)
def yield_test_molecules(self): for filename in ["tpa.xyz", "water.xyz", "thf_single.xyz"]: molecule = XYZFile(os.path.join("input", filename)).get_molecule() molecule.filename = filename graph = MolecularGraph.from_geometry(molecule) yield molecule, graph
def __next__(self): """Load the next molecule from the SDF file This method is part of the iterator protocol. """ while True: title = next(self.f) if len(title) == 0: raise StopIteration else: title = title.strip() next(self.f) # skip line next(self.f) # skip empty line words = next(self.f).split() if len(words) < 2: raise FileFormatError( "Expecting at least two numbers at fourth line.") try: num_atoms = int(words[0]) num_bonds = int(words[1]) except ValueError: raise FileFormatError( "Expecting at least two numbers at fourth line.") numbers = np.zeros(num_atoms, int) coordinates = np.zeros((num_atoms, 3), float) for i in range(num_atoms): words = next(self.f).split() if len(words) < 4: raise FileFormatError( "Expecting at least four words on an atom line.") try: coordinates[i, 0] = float(words[0]) coordinates[i, 1] = float(words[1]) coordinates[i, 2] = float(words[2]) except ValueError: raise FileFormatError( "Coordinates must be floating point numbers.") atom = periodic[words[3]] if atom is None: raise FileFormatError("Unrecognized atom symbol: %s" % words[3]) numbers[i] = atom.number coordinates *= angstrom edges = [] orders = np.zeros(num_bonds, int) for i in range(num_bonds): words = next(self.f).split() if len(words) < 3: raise FileFormatError( "Expecting at least three numbers on a bond line.") try: edges.append((int(words[0]) - 1, int(words[1]) - 1)) orders[i] = int(words[2]) except ValueError: raise FileFormatError( "Expecting at least three numbers on a bond line.") formal_charges = np.zeros(len(numbers), int) line = next(self.f) while line != "M END\n": if line.startswith("M CHG"): words = line[6:].split( )[1:] # drop the first number which is the number of charges i = 0 while i < len(words) - 1: try: formal_charges[int(words[i]) - 1] = int(words[i + 1]) except ValueError: raise FileFormatError( "Expecting only integer formal charges.") i += 2 line = next(self.f) # Read on to the next molecule for line in self.f: if line == "$$$$\n": break molecule = Molecule(numbers, coordinates, title) molecule.formal_charges = formal_charges molecule.formal_charges.setflags(write=False) molecule.graph = MolecularGraph(edges, numbers, orders) return molecule
CC30A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,6,6)) CC31A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,6,1)) CC32A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,1,1)) CC33A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,1,1,1)) atom_filters = { "CC30A": CC30A, "CC31A": CC31A, "CC32A": CC32A, "CC33A": CC33A, "HCA1": CritAnd(HasAtomNumber(1), HasNeighbors(CC31A)), "HCA2": CritAnd(HasAtomNumber(1), HasNeighbors(CC32A)), "HCA3": CritAnd(HasAtomNumber(1), HasNeighbors(CC33A)), } def get_atom_type(index, graph): for atom_type, atom_filter in atom_filters.iteritems(): if atom_filter(index, graph): return atom_type raise ValueError("Unrecognized atom (index %i)." % index) args = sys.argv[1:] molecule = XYZFile(args[0]).get_molecule() graph = MolecularGraph.from_geometry(molecule) atom_types = [get_atom_type(index, graph) for index in xrange(molecule.size)] psf = PSFFile() psf.add_molecular_graph(graph, atom_types=atom_types) psf.write_to_file(args[0].replace(".xyz", ".psf"))
CC30A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,6,6)) CC31A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,6,1)) CC32A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,6,1,1)) CC33A = CritAnd(HasAtomNumber(6), HasNeighborNumbers(6,1,1,1)) atom_filters = { "CC30A": CC30A, "CC31A": CC31A, "CC32A": CC32A, "CC33A": CC33A, "HCA1": CritAnd(HasAtomNumber(1), HasNeighbors(CC31A)), "HCA2": CritAnd(HasAtomNumber(1), HasNeighbors(CC32A)), "HCA3": CritAnd(HasAtomNumber(1), HasNeighbors(CC33A)), } def get_atom_type(index, graph): for atom_type, atom_filter in atom_filters.items(): if atom_filter(index, graph): return atom_type raise ValueError("Unrecognized atom (index %i)." % index) args = sys.argv[1:] molecule = XYZFile(args[0]).get_molecule() graph = MolecularGraph.from_geometry(molecule) atom_types = [get_atom_type(index, graph) for index in range(molecule.size)] psf = PSFFile() psf.add_molecular_graph(graph, atom_types=atom_types) psf.write_to_file(args[0].replace(".xyz", ".psf"))
def test_graph(self): molecules = self.get_molecules() for molecule in molecules: molecule.graph = MolecularGraph.from_geometry(molecule) molecule.descriptor = DistanceDescriptor(molecule.graph) self.do_test(molecules, margin=0.2, cutoff=10.0)
def get_molecular_graph(self, labels=None): return MolecularGraph(self.bonds, self.numbers)