def get_vector(self, atom0, atom1, distance): if atom0 in atom1.iter_neighbors(): return None bond_type = bonds.bonded(atom0.number, atom1.number, distance) if bond_type is None: return None else: return Bond(bond_type=bond_type, targets=[atom0, atom1])
def detect_bonds(self, exceptions=None): """Initialize the ``bonds`` attribute based on inter-atomic distances **Optional argument:** exceptions: Specify custom threshold for certain pairs of elements. This must be a dictionary with ((num0, num1), threshold) as items. For each pair of elements, a distance threshold is used to detect bonded atoms. The distance threshold is based on a database of known bond lengths. If the database does not contain a record for the given element pair, the threshold is based on the sum of covalent radii. """ with log.section('SYS'): from molmod.bonds import bonds if self.bonds is not None: if log.do_warning: log.warn('Overwriting existing bonds.') work = np.zeros((self.natom*(self.natom-1))/2, float) self.cell.compute_distances(work, self.pos) ishort = (work < bonds.max_length*1.01).nonzero()[0] new_bonds = [] for i in ishort: i0, i1 = _unravel_triangular(i) n0 = self.numbers[i0] n1 = self.numbers[i1] if exceptions is not None: threshold = exceptions.get((n0, n1)) if threshold is None and n0!=n1: threshold = exceptions.get((n1, n0)) if threshold is not None: if work[i] < threshold: new_bonds.append([i0, i1]) continue if bonds.bonded(n0, n1, work[i]): new_bonds.append([i0, i1]) self.bonds = np.array(new_bonds) self._init_derived_bonds()
def detect_bonds(self, exceptions=None): """Initialize the ``bonds`` attribute based on inter-atomic distances **Optional argument:** exceptions: Specify custom threshold for certain pairs of elements. This must be a dictionary with ((num0, num1), threshold) as items. For each pair of elements, a distance threshold is used to detect bonded atoms. The distance threshold is based on a database of known bond lengths. If the database does not contain a record for the given element pair, the threshold is based on the sum of covalent radii. """ with log.section('SYS'): from molmod.bonds import bonds if self.bonds is not None: if log.do_warning: log.warn('Overwriting existing bonds.') work = np.zeros((self.natom * (self.natom - 1)) // 2, float) self.cell.compute_distances(work, self.pos) ishort = (work < bonds.max_length * 1.01).nonzero()[0] new_bonds = [] for i in ishort: i0, i1 = _unravel_triangular(i) n0 = self.numbers[i0] n1 = self.numbers[i1] if exceptions is not None: threshold = exceptions.get((n0, n1)) if threshold is None and n0 != n1: threshold = exceptions.get((n1, n0)) if threshold is not None: if work[i] < threshold: new_bonds.append([i0, i1]) continue if bonds.bonded(n0, n1, work[i]): new_bonds.append([i0, i1]) self.bonds = np.array(new_bonds) self._init_derived_bonds()
def from_geometry(cls, molecule, do_orders=False, scaling=1.0): """Construct a MolecularGraph object based on interatomic distances All short distances are computed with the binning module and compared with a database of bond lengths. Based on this comparison, bonded atoms are detected. Argument: | ``molecule`` -- The molecule to derive the graph from Optional arguments: | ``do_orders`` -- set to True to estimate the bond order | ``scaling`` -- scale the threshold for the connectivity. increase this to 1.5 in case of transition states when a fully connected topology is required. """ from molmod.bonds import bonds unit_cell = molecule.unit_cell pair_search = PairSearchIntra( molecule.coordinates, bonds.max_length*bonds.bond_tolerance, unit_cell ) orders = [] lengths = [] edges = [] for i0, i1, delta, distance in pair_search: bond_order = bonds.bonded(molecule.numbers[i0], molecule.numbers[i1], distance/scaling) if bond_order is not None: if do_orders: orders.append(bond_order) lengths.append(distance) edges.append((i0,i1)) if do_orders: result = cls(edges, molecule.numbers, orders, symbols=molecule.symbols) else: result = cls(edges, molecule.numbers, symbols=molecule.symbols) # run a check on all neighbors. if two bonds point in a direction that # differs only by 45 deg. the longest of the two is discarded. the # double loop over the neighbors is done such that the longest bonds # are eliminated first slated_for_removal = set([]) threshold = 0.5**0.5 for c, ns in result.neighbors.iteritems(): lengths_ns = [] for n in ns: delta = molecule.coordinates[n] - molecule.coordinates[c] if unit_cell is not None: delta = unit_cell.shortest_vector(delta) length = numpy.linalg.norm(delta) lengths_ns.append([length, delta, n]) lengths_ns.sort(reverse=True, cmp=(lambda r0, r1: cmp(r0[0], r1[0]))) for i0, (length0, delta0, n0) in enumerate(lengths_ns): for i1, (length1, delta1, n1) in enumerate(lengths_ns[:i0]): if length1 == 0.0: continue cosine = numpy.dot(delta0, delta1)/length0/length1 if cosine > threshold: # length1 > length0 slated_for_removal.add((c,n1)) lengths_ns[i1][0] = 0.0 # construct a mask mask = numpy.ones(len(edges), bool) for i0, i1 in slated_for_removal: edge_index = result.edge_index.get(frozenset([i0,i1])) if edge_index is None: raise ValueError('Could not find edge that has to be removed: %i %i' % (i0, i1)) mask[edge_index] = False # actual removal edges = [edges[i] for i in xrange(len(edges)) if mask[i]] if do_orders: bond_order = [bond_order[i] for i in xrange(len(bond_order)) if mask[i]] result = cls(edges, molecule.numbers, orders) else: result = cls(edges, molecule.numbers) lengths = [lengths[i] for i in xrange(len(lengths)) if mask[i]] result.bond_lengths = numpy.array(lengths) return result
def from_geometry(cls, molecule, do_orders=False, scaling=1.0): """Construct a MolecularGraph object based on interatomic distances All short distances are computed with the binning module and compared with a database of bond lengths. Based on this comparison, bonded atoms are detected. Argument: | ``molecule`` -- The molecule to derive the graph from Optional arguments: | ``do_orders`` -- set to True to estimate the bond order | ``scaling`` -- scale the threshold for the connectivity. increase this to 1.5 in case of transition states when a fully connected topology is required. """ from molmod.bonds import bonds unit_cell = molecule.unit_cell pair_search = PairSearchIntra(molecule.coordinates, bonds.max_length * bonds.bond_tolerance, unit_cell) orders = [] lengths = [] edges = [] for i0, i1, delta, distance in pair_search: bond_order = bonds.bonded(molecule.numbers[i0], molecule.numbers[i1], distance / scaling) if bond_order is not None: if do_orders: orders.append(bond_order) lengths.append(distance) edges.append((i0, i1)) if do_orders: result = cls(edges, molecule.numbers, orders, symbols=molecule.symbols) else: result = cls(edges, molecule.numbers, symbols=molecule.symbols) # run a check on all neighbors. if two bonds point in a direction that # differs only by 45 deg. the longest of the two is discarded. the # double loop over the neighbors is done such that the longest bonds # are eliminated first slated_for_removal = set([]) threshold = 0.5**0.5 for c, ns in result.neighbors.items(): lengths_ns = [] for n in ns: delta = molecule.coordinates[n] - molecule.coordinates[c] if unit_cell is not None: delta = unit_cell.shortest_vector(delta) length = np.linalg.norm(delta) lengths_ns.append([length, delta, n]) lengths_ns.sort(reverse=True, key=(lambda r: r[0])) for i0, (length0, delta0, n0) in enumerate(lengths_ns): for i1, (length1, delta1, n1) in enumerate(lengths_ns[:i0]): if length1 == 0.0: continue cosine = np.dot(delta0, delta1) / length0 / length1 if cosine > threshold: # length1 > length0 slated_for_removal.add((c, n1)) lengths_ns[i1][0] = 0.0 # construct a mask mask = np.ones(len(edges), bool) for i0, i1 in slated_for_removal: edge_index = result.edge_index.get(frozenset([i0, i1])) if edge_index is None: raise ValueError( 'Could not find edge that has to be removed: %i %i' % (i0, i1)) mask[edge_index] = False # actual removal edges = [edges[i] for i in range(len(edges)) if mask[i]] if do_orders: bond_order = [ bond_order[i] for i in range(len(bond_order)) if mask[i] ] result = cls(edges, molecule.numbers, orders) else: result = cls(edges, molecule.numbers) lengths = [lengths[i] for i in range(len(lengths)) if mask[i]] result.bond_lengths = np.array(lengths) return result