def ring_system_decomposed_atom_keys(rsy, rng_keys=None, check=True): """ decomposed atom keys for a polycyclic ring system in a graph The ring system is decomposed into a ring and a series of arcs that can be used to successively construct the system :param rsy: the ring system :param rng_keys: keys for the first ring in the decomposition; if None, the smallest ring in the system will be chosen """ if rng_keys is None: rng = sorted(rings(rsy), key=atom_count)[0] rng_keys = sorted_ring_atom_keys(rng) # check the arguments, if requested if check: # check that the graph is connected assert is_connected(rsy), "Ring system can't be disconnected." # check that the graph is actually a ring system assert is_ring_system(rsy), ( "This is not a ring system graph:\n{:s}".format(string(rsy))) # check that rng is a subgraph of rsy assert set(rng_keys) <= atom_keys(rsy), ( "{}\n^ Rings system doesn't contain ring as subgraph:\n{}".format( string(rsy, one_indexed=False), str(rng_keys))) bnd_keys = list(mit.windowed(rng_keys + rng_keys[:1], 2)) # Remove bonds for the ring rsy = remove_bonds(rsy, bnd_keys) keys_lst = [rng_keys] done_keys = set(rng_keys) while bond_keys(rsy): # Determine shortest paths for the graph with one more ring/arc deleted sp_dct = atom_shortest_paths(rsy) # The shortest path will be the next shortest arc in the system arc_keys = min((sp_dct[i][j] for i, j in itertools.combinations(done_keys, 2) if j in sp_dct[i]), key=len) # Add this arc to the list keys_lst.append(arc_keys) # Add these keys to the list of done keys done_keys |= set(arc_keys) # Delete tbond keys for the new arc and continue to the next iteration bnd_keys = list(map(frozenset, mit.windowed(arc_keys, 2))) rsy = remove_bonds(rsy, bnd_keys) keys_lst = tuple(map(tuple, keys_lst)) return keys_lst
def shortest_path_between_groups(gra, keys1, keys2): """ shortest path between two groups of atoms Returns the atom pair from these groups that are nearest to each other and returns the path between them. """ assert not set(keys1) & set(keys2), ("{:s} overlaps with {:s}".format( *map(str, [keys1, keys2]))) sp_dct = atom_shortest_paths(gra) keys = None for key1 in keys1: for key2 in keys2: if key2 in sp_dct[key1]: if keys is None or len(keys) > len(sp_dct[key1][key2]): keys = sp_dct[key1][key2] return keys
def ts_distance_bounds_matrices(gra, keys, frm_bnds_dct, rct_geos=None, relax_torsions=False, sp_dct=None): """ distance bounds matrices for a transition state :param gra: molecular graph :param keys: atom keys specifying the order of indices in the matrix :param frm_bnds_dct: bounds for bonds formed in the reaction; the keys are bond keys and the values are lower and upper distance bounds for those forming bonds :param rct_geos: reactant geometries; must follow the same order as the atoms in `keys :param relax_torsions: whether or not to allow torsions to change from their value in the reactant geometries :param sp_dct: a 2d dictionary giving the shortest path between any pair of atoms in the graph """ sp_dct = atom_shortest_paths(gra) if sp_dct is None else sp_dct natms = len(keys) lmat, umat = distance_bounds_matrices(gra, keys) # save the current values so that we can overwrite the fixed torsions below lmat_old = numpy.copy(lmat) umat_old = numpy.copy(umat) # 2. set known geometric parameters if rct_geos: xmats = [geom_coordinates(geo, angstrom=True) for geo in rct_geos] dmats = list(map(embed.distance_matrix_from_coordinates, xmats)) start = 0 for dmat in dmats: dim, _ = numpy.shape(dmat) end = start + dim lmat[start:end, start:end] = dmat umat[start:end, start:end] = dmat start = end # 3. reopen bounds on the torsions from the reactant if relax_torsions: tors_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 4] tors_ijs += list(map(list, map(reversed, tors_ijs))) tors_idxs = tuple(map(list, zip(*tors_ijs))) lmat[tors_idxs] = lmat_old[tors_idxs] umat[tors_idxs] = umat_old[tors_idxs] # 1. set distance bounds for the forming bonds for bnd, (ldist, udist) in frm_bnds_dct.items(): idx1 = tuple(bnd) idx2 = tuple(reversed(idx1)) lmat[idx1] = lmat[idx2] = ldist umat[idx1] = umat[idx2] = udist return lmat, umat
def join_distance_bounds_matrices(gra, keys, dist_range_dct, geos=None, relax_angles=False, relax_torsions=False, sp_dct=None, angstrom=True): """ distance bounds matrices for joining multiple geometries :param gra: molecular graph:wq :param keys: atom keys specifying the order of indices in the matrix :param dist_range_dct: distance ranges for specific atoms in the graph :param geos: (optional) geometries which will be used to fix the bond angles, bond distances, and chiralities of all connected atoms in the graph; if `relax_torsions` is False, the 4-atom dihedral angles will be allowed to vary as well :param relax_torsions: whether or not to allow torsions to change from their value in the reactant geometries :param sp_dct: a 2d dictionary giving the shortest path between any pair of atoms in the graph """ sp_dct = atom_shortest_paths(gra) if sp_dct is None else sp_dct natms = len(keys) lmat, umat = distance_bounds_matrices(gra, keys) # save the current values so that we can overwrite the fixed torsions below lmat_old = numpy.copy(lmat) umat_old = numpy.copy(umat) # 1. set known geometric parameters if geos: xmats = [geom_coordinates(geo, angstrom=angstrom) for geo in geos] dmats = list(map(embed.distance_matrix_from_coordinates, xmats)) start = 0 for dmat in dmats: dim, _ = numpy.shape(dmat) end = start + dim lmat[start:end, start:end] = dmat umat[start:end, start:end] = dmat start = end # 2. reopen bounds on the torsions from the reactant if relax_torsions: tors_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 4] tors_ijs += list(map(list, map(reversed, tors_ijs))) tors_idxs = tuple(map(list, zip(*tors_ijs))) lmat[tors_idxs] = lmat_old[tors_idxs] umat[tors_idxs] = umat_old[tors_idxs] # 3. reopen bounds on the angles from the reactant if relax_angles: ang_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 3] ang_ijs += list(map(list, map(reversed, ang_ijs))) ang_idxs = tuple(map(list, zip(*ang_ijs))) lmat[ang_idxs] = lmat_old[ang_idxs] umat[ang_idxs] = umat_old[ang_idxs] # 4. set distance bounds for the forming bonds for bnd, (ldist, udist) in dist_range_dct.items(): idx1 = tuple(bnd) idx2 = tuple(reversed(idx1)) lmat[idx1] = lmat[idx2] = ldist umat[idx1] = umat[idx2] = udist return lmat, umat