def _decompose_ring_system_atom_keys(rsy): """ decompose a ring system into a ring and a series of arcs """ # sort from smallest to largest rngs_pool = sorted(rings(rsy), key=lambda x: atom_count(x, with_implicit=False)) decomp = () decomp_bnd_keys = set({}) rng = rngs_pool.pop(0) bnd_keys = bond_keys(rng) atm_keys = sorted_ring_atom_keys_from_bond_keys(bnd_keys) decomp += (atm_keys, ) decomp_bnd_keys.update(bnd_keys) while rngs_pool: decomp_rsy = bond_induced_subgraph(rsy, decomp_bnd_keys) for idx, rng in enumerate(rngs_pool): arcs = ring_arc_complement_atom_keys(decomp_rsy, rng) if arcs: rngs_pool.pop(idx) decomp += arcs decomp_bnd_keys.update(bond_keys(rng)) return decomp
def insertion(xgr1, xgr2): """ find an insertion transformation """ assert xgr1 == _explicit(xgr1) and xgr2 == _explicit(xgr2) tra = None idxs = None xgrs1 = _connected_components(xgr1) xgrs2 = _connected_components(xgr2) if len(xgrs1) == 2 and len(xgrs2) == 1: xgra, xgrb = xgrs1 atmsa = atoms(xgra) atmsb = atoms(xgrb) neighsa = atom_neighbor_keys(xgra) neighsb = atom_neighbor_keys(xgrb) bndsa = bond_keys(xgra) bndsb = bond_keys(xgrb) tra = _insertion(atmsa, neighsa, bndsa, atmsb, neighsb, xgr1, xgr2) idxs = [0, 1] if not tra: tra = _insertion(atmsb, neighsb, bndsb, atmsa, neighsa, xgr1, xgr2) if tra: idxs = [1, 0] else: idxs = None elif len(xgrs1) == 1 and len(xgrs2) == 1: xgra = xgr1 idxs = [0] atmsa = atoms(xgra) neighsa = atom_neighbor_keys(xgra) bndsa = bond_keys(xgra) tra = _insertion(atmsa, neighsa, bndsa, atmsa, neighsa, xgr1, xgr2) return tra, idxs
def substitution(xgr1, xgr2): """identifies substitution reactions """ assert xgr1 == _explicit(xgr1) and xgr2 == _explicit(xgr2) tra = None idxs = None xgrs1 = _connected_components(xgr1) xgrs2 = _connected_components(xgr2) print('len xgrs test:', len(xgrs1), len(xgrs2)) print('xgrs test:', xgrs1, xgrs2) if len(xgrs1) == 2 and len(xgrs2) == 2: xgra, xgrb = xgrs1 xgrc, xgrd = xgrs2 atmsa = atoms(xgra) neighsa = atom_neighbor_keys(xgra) bndsa = bond_keys(xgra) atmsb = atoms(xgrb) neighsb = atom_neighbor_keys(xgrb) bndsb = bond_keys(xgrb) atmsc = atoms(xgrc) neighsc = atom_neighbor_keys(xgrc) bndsc = bond_keys(xgrc) atmsd = atoms(xgrd) neighsd = atom_neighbor_keys(xgrd) bndsd = bond_keys(xgrd) tra = _substitution(atmsa, neighsa, bndsa, atmsb, xgr1, xgr2) # tra = _substitution( # atmsa, neighsa, bndsa, atmsb, neighsb, xgr1, xgr2) idxs = [[0, 1], [0, 1]] if not tra: tra = _substitution(atmsb, neighsb, bndsb, atmsa, xgr1, xgr2) # atmsb, neighsb, bndsb, atmsa, neighsa, xgr1, xgr2) idxs = [[0, 1], [1, 0]] if not tra: tra = _substitution(atmsc, neighsc, bndsc, atmsd, xgr2, xgr1) # atmsc, neighsc, bndsc, atmsd, neighsd, xgr2, xgr1) idxs = [[1, 0], [0, 1]] if not tra: tra = _substitution(atmsd, neighsd, bndsd, atmsc, xgr2, xgr1) # atmsd, neighsd, bndsd, atmsc, neighsc, xgr2, xgr1) idxs = [[1, 0], [1, 0]] if not tra: idxs = None # return not substitution for radical + unsaturated reactions unsat_atm_keys = _unsaturated_atom_keys(xgra) # print('unsat test:', tra[0][0], unsat_atm_keys) tra_list = list(tra[0]) for key in unsat_atm_keys: if key in tra_list[0]: pass # tra = None # commented out the tra = None and added pass to run CO + HO2 return tra, idxs
def bond_symmetry_numbers(gra, frm_bnd_key=None, brk_bnd_key=None): """ symmetry numbers, by bond the (approximate) symmetry number of the torsional potential for this bond, based on the hydrogen counts for each atom It is reduced to 1 if one of the H atoms in the torsional bond is a neighbor to the special bonding atom (the atom that is being transferred) """ imp_gra = implicit(gra) atm_imp_hyd_vlc_dct = atom_implicit_hydrogen_valences(imp_gra) bnd_keys = bond_keys(imp_gra) tfr_atm = None if frm_bnd_key and brk_bnd_key: for atm_f in list(frm_bnd_key): for atm_b in list(brk_bnd_key): if atm_f == atm_b: tfr_atm = atm_f if tfr_atm: neighbor_dct = atoms_neighbor_atom_keys(gra) nei_tfr = neighbor_dct[tfr_atm] atms = gra[0] all_hyds = [] for atm in atms: if atms[atm][0] == 'H': all_hyds.append(atm) else: nei_tfr = {} bnd_symb_num_dct = {} bnd_symb_nums = [] for bnd_key in bnd_keys: bnd_sym = 1 vlc = max(map(atm_imp_hyd_vlc_dct.__getitem__, bnd_key)) if vlc == 3: bnd_sym = 3 if tfr_atm: for atm in nei_tfr: nei_s = neighbor_dct[atm] h_nei = 0 for nei in nei_s: if nei in all_hyds: h_nei += 1 if h_nei == 3: bnd_sym = 1 bnd_symb_nums.append(bnd_sym) bnd_symb_num_dct = dict(zip(bnd_keys, bnd_symb_nums)) # fill in the rest of the bonds for completeness bnd_symb_num_dct = dict_.by_key(bnd_symb_num_dct, bond_keys(gra), fill_val=1) return bnd_symb_num_dct
def closest_unbonded_atoms(geo, gra=None): """ Determine which pair of unbonded atoms in a molecular geometry are closest together. :param geo: molecular geometry :type geo: automol geometry data structure :param gra: the graph describing connectivity; if None, a connectivity graph will be generated using default distance thresholds :type gra: automol graph data structure :rtype: (frozenset(int), float) """ gra = _connectivity_graph(geo) if gra is None else gra atm_keys = atom_keys(gra) bnd_keys = bond_keys(gra) poss_bnd_keys = set(map(frozenset, itertools.combinations(atm_keys, r=2))) # The set of candidates includes all unbonded pairs of atoms cand_bnd_keys = poss_bnd_keys - bnd_keys min_bnd_key = None min_dist_val = 1000. for bnd_key in cand_bnd_keys: dist_val = distance(geo, *bnd_key) if dist_val < min_dist_val: min_dist_val = dist_val min_bnd_key = bnd_key return min_bnd_key, min_dist_val
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 radical_dissociation_prods(gra, pgra1): """ given a dissociation product, determine the other product """ gra = without_fractional_bonds(gra) pgra2 = None rads = sing_res_dom_radical_atom_keys(gra) adj_atms = atoms_neighbor_atom_keys(gra) # adj_idxs = tuple(adj_atms[rad] for rad in rads) for rad in rads: for adj in adj_atms[rad]: for group in atom_groups(gra, adj): if full_isomorphism(explicit(group), explicit(pgra1)): pgra2 = remove_atoms(gra, atom_keys(group)) # pgra2 = remove_bonds(pgra2, bond_keys(group)) if bond_keys(group) in pgra2: pgra2 = remove_bonds(pgra2, bond_keys(group)) return (pgra1, pgra2)
def rotational_bond_keys(gra, lin_keys=None, with_h_rotors=True): """ get all rotational bonds for a graph :param gra: the graph :param lin_keys: keys to linear atoms in the graph """ gra = explicit(gra) sym_dct = atom_symbols(gra) ngb_keys_dct = atoms_neighbor_atom_keys(gra) bnd_ord_dct = resonance_dominant_bond_orders(gra) rng_bnd_keys = list(itertools.chain(*rings_bond_keys(gra))) def _is_rotational_bond(bnd_key): ngb_keys_lst = [ngb_keys_dct[k] - bnd_key for k in bnd_key] is_single = max(bnd_ord_dct[bnd_key]) <= 1 has_neighbors = all(ngb_keys_lst) not_in_ring = bnd_key not in rng_bnd_keys is_h_rotor = any( set(map(sym_dct.__getitem__, ks)) == {'H'} for ks in ngb_keys_lst) return is_single and has_neighbors and not_in_ring and ( not is_h_rotor or with_h_rotors) rot_bnd_keys = frozenset(filter(_is_rotational_bond, bond_keys(gra))) lin_keys_lst = linear_segments_atom_keys(gra, lin_keys=lin_keys) dum_keys = tuple(atom_keys(gra, sym='X')) for keys in lin_keys_lst: bnd_keys = sorted((k for k in rot_bnd_keys if k & set(keys)), key=sorted) # Check whether there are neighboring atoms on either side of the # linear segment excl_keys = set(keys) | set(dum_keys) end_key1 = atom_neighbor_atom_key(gra, keys[0], excl_atm_keys=excl_keys) excl_keys |= {end_key1} end_key2 = atom_neighbor_atom_key(gra, keys[-1], excl_atm_keys=excl_keys) end_keys = {end_key1, end_key2} ngb_keys_lst = [ngb_keys_dct[k] - excl_keys for k in end_keys] has_neighbors = all(ngb_keys_lst) if not has_neighbors: rot_bnd_keys -= set(bnd_keys) else: rot_bnd_keys -= set(bnd_keys[:-1]) return rot_bnd_keys
def rings_bond_keys(gra): """ bond keys for each ring in the graph (minimal basis) """ bnd_keys = bond_keys(gra) def _ring_bond_keys(rng_atm_keys): return frozenset(filter(lambda x: x <= rng_atm_keys, bnd_keys)) nxg = _networkx.from_graph(gra) rng_atm_keys_lst = _networkx.minimum_cycle_basis(nxg) rng_bnd_keys_lst = frozenset(map(_ring_bond_keys, rng_atm_keys_lst)) return rng_bnd_keys_lst
def ring_arc_complement_atom_keys(gra, rng): """ non-intersecting arcs from a ring that shares segments with a graph """ gra_atm_bnd_dct = atoms_bond_keys(gra) rng_atm_bnd_dct = atoms_bond_keys(rng) # 1. find divergence points, given by the atom at which the divergence # occurs and the bond followed by the ring as it diverges div_dct = {} for atm_key in atom_keys(gra) & atom_keys(rng): div = rng_atm_bnd_dct[atm_key] - gra_atm_bnd_dct[atm_key] if div: bnd_key, = div div_dct[atm_key] = bnd_key # 2. cycle through the ring atoms; if you meet a starting divergence, start # an arc; extend the arc until you meet an ending divergence; repeat until # all divergences are accounted for atm_keys = sorted_ring_atom_keys_from_bond_keys(bond_keys(rng)) arcs = [] arc = [] for atm_key, next_atm_key in mit.windowed(itertools.cycle(atm_keys), 2): bnd_key = frozenset({atm_key, next_atm_key}) # if we haven't started an arc, see if we are at a starting divergence; # if so, start the arc now and cross the divergence from our list if not arc: if atm_key in div_dct and div_dct[atm_key] == bnd_key: div_dct.pop(atm_key) arc.append(atm_key) # if we've started an arc, extend it; then, check if we are at an # ending divergence; if so, end the arc and cross the divergence from # our list; add it to our list of arcs else: arc.append(atm_key) if next_atm_key in div_dct and div_dct[next_atm_key] == bnd_key: div_dct.pop(next_atm_key) arc.append(next_atm_key) arcs.append(arc) arc = [] # if no divergences are left, break out of the loop if not div_dct: break arcs = tuple(map(tuple, arcs)) return arcs
def one_resonance_dominant_bond_orders(rgr): """ resonance-dominant bond orders, by bond """ rgr = without_fractional_bonds(rgr) bnd_keys = list(bond_keys(rgr)) bnd_ords_by_res = [ dict_.values_by_key(bond_orders(dom_rgr), bnd_keys) for dom_rgr in dominant_resonances(rgr) ] first_bnd_ords = [bnd_ords_by_res[0]] bnd_ords_lst = list(map(frozenset, zip(*first_bnd_ords))) bnd_dom_res_ords_dct = dict(zip(bnd_keys, bnd_ords_lst)) return bnd_dom_res_ords_dct
def equivalent_bonds(gra, bnd_key, stereo=True, dummy=True): """ Identify sets of isomorphically equivalent bonds Two bonds are equivalent if they transform into each other under an automorphism :param gra: A graph :param bnd_key: An bond key for the graph, which may be sorted or unsorted :param backbone_only: Compare backbone atoms only? :type stereo: bool :param dummy: Consider dummy atoms? :type dummy: bool :returns: Keys to equivalent bonds :rtype: frozenset """ bnd_key = tuple(bnd_key) bnd_keys = list(map(tuple, map(sorted, bond_keys(gra)))) bnd_keys += list(map(tuple, map(reversed, bnd_keys))) assert bnd_key in bnd_keys, "{} not in {}".format(bnd_key, bnd_keys) atm_symb_dct = atom_symbols(gra) atm_ngbs_dct = atoms_neighbor_atom_keys(gra) def _symbols(bnd_key): return list(map(atm_symb_dct.__getitem__, bnd_key)) def _neighbor_symbols(bnd_key): key1, key2 = bnd_key nsymbs1 = sorted(map(atm_symb_dct.__getitem__, atm_ngbs_dct[key1])) nsymbs2 = sorted(map(atm_symb_dct.__getitem__, atm_ngbs_dct[key2])) return nsymbs1, nsymbs2 # 1. Find bonds with the same atom types bnd_symbs = _symbols(bnd_key) cand_keys = [k for k in bnd_keys if _symbols(k) == bnd_symbs] # 2. Of those, find bonds with the same neighboring atom types bnd_ngb_symbs = _neighbor_symbols(bnd_key) cand_keys = [k for k in cand_keys if _neighbor_symbols(k) == bnd_ngb_symbs] # 3. Find the equivalent bonds from the list of candidates. # Strategy: Change the atom symbols to 'Lv' and 'Ts' and check for # isomorphism. Assumes none of the compounds have element 116 or 117. ref_gra = set_atom_symbols(gra, {bnd_key[0]: 'Lv', bnd_key[1]: 'Ts'}) bnd_keys = [] for key in cand_keys: comp_gra = set_atom_symbols(gra, {key[0]: 'Lv', key[1]: 'Ts'}) if isomorphism(ref_gra, comp_gra, stereo=stereo, dummy=dummy): bnd_keys.append(key) return frozenset(bnd_keys)
def resonance_avg_bond_orders(rgr): """ resonance-dominant bond orders, by bond """ rgr = without_fractional_bonds(rgr) bnd_keys = list(bond_keys(rgr)) bnd_ords_by_res = [ dict_.values_by_key(bond_orders(dom_rgr), bnd_keys) for dom_rgr in dominant_resonances(rgr) ] nres = len(bnd_ords_by_res) bnd_ords_lst = zip(*bnd_ords_by_res) avg_bnd_ord_lst = [sum(bnd_ords) / nres for bnd_ords in bnd_ords_lst] avg_bnd_ord_dct = dict(zip(bnd_keys, avg_bnd_ord_lst)) return avg_bnd_ord_dct
def from_graph(gra): """ networkx graph object from a molecular graph """ nxg = networkx.Graph() nxg.add_nodes_from(atom_keys(gra)) nxg.add_edges_from(bond_keys(gra)) networkx.set_node_attributes(nxg, atom_symbols(gra), 'symbol') networkx.set_node_attributes(nxg, atom_implicit_hydrogen_valences(gra), 'implicit_hydrogen_valence') networkx.set_node_attributes(nxg, atom_stereo_parities(gra), 'stereo_parity') networkx.set_edge_attributes(nxg, bond_orders(gra), 'order') networkx.set_edge_attributes(nxg, bond_stereo_parities(gra), 'stereo_parity') return nxg
def angle_keys(gra): """ triples of keys for pairs of adjacent bonds, with the central atom in the middle """ bnd_keys = bond_keys(gra) ang_keys = [] for bnd_key1, bnd_key2 in itertools.combinations(bnd_keys, r=2): if bnd_key1 != bnd_key2 and bnd_key1 & bnd_key2: atm2_key, = bnd_key1 & bnd_key2 atm1_key, = bnd_key1 - {atm2_key} atm3_key, = bnd_key2 - {atm2_key} ang_keys.append((atm1_key, atm2_key, atm3_key)) ang_keys.append((atm3_key, atm2_key, atm1_key)) return tuple(ang_keys)
def frozen(gra): """ hashable, sortable, immutable container of graph data """ atm_keys = sorted(atom_keys(gra)) bnd_keys = sorted(bond_keys(gra), key=sorted) # make it sortable by replacing Nones with -infinity atm_vals = numpy.array(dict_.values_by_key(atoms(gra), atm_keys), dtype=numpy.object) bnd_vals = numpy.array(dict_.values_by_key(bonds(gra), bnd_keys), dtype=numpy.object) atm_vals[numpy.equal(atm_vals, None)] = -numpy.inf bnd_vals[numpy.equal(bnd_vals, None)] = -numpy.inf frz_atms = tuple(zip(atm_keys, map(tuple, atm_vals))) frz_bnds = tuple(zip(bnd_keys, map(tuple, bnd_vals))) return (frz_atms, frz_bnds)
def inchi_with_sort_from_geometry(gra, geo=None, geo_idx_dct=None): """ Generate an InChI string from a molecular graph. If coordinates are passed in, they are used to determine stereo. :param gra: molecular graph :type gra: automol graph data structure :param geo: molecular geometry :type geo: automol geometry data structure :param geo_idx_dct: :type geo_idx_dct: dict[:] :rtype: (str, tuple(int)) """ gra = without_dummy_atoms(gra) gra = dominant_resonance(gra) atm_keys = sorted(atom_keys(gra)) bnd_keys = list(bond_keys(gra)) atm_syms = dict_.values_by_key(atom_symbols(gra), atm_keys) atm_bnd_vlcs = dict_.values_by_key(atom_bond_valences(gra), atm_keys) atm_rad_vlcs = dict_.values_by_key(atom_unsaturated_valences(gra), atm_keys) bnd_ords = dict_.values_by_key(bond_orders(gra), bnd_keys) if geo is not None: assert geo_idx_dct is not None atm_xyzs = coordinates(geo) atm_xyzs = [ atm_xyzs[geo_idx_dct[atm_key]] if atm_key in geo_idx_dct else (0., 0., 0.) for atm_key in atm_keys ] else: atm_xyzs = None mlf, key_map_inv = _molfile.from_data(atm_keys, bnd_keys, atm_syms, atm_bnd_vlcs, atm_rad_vlcs, bnd_ords, atm_xyzs=atm_xyzs) rdm = _rdkit.from_molfile(mlf) ich, aux_info = _rdkit.to_inchi(rdm, with_aux_info=True) nums = _parse_sort_order_from_aux_info(aux_info) nums = tuple(map(key_map_inv.__getitem__, nums)) return ich, nums
def rotational_groups(gra, key1, key2, dummy=False): """ get the rotational groups for a given rotational axis :param gra: the graph :param key1: the first atom key :param key2: the second atom key """ if not dummy: gra = without_dummy_atoms(gra) bnd_key = frozenset({key1, key2}) assert bnd_key in bond_keys(gra) grp1 = branch_atom_keys(gra, key2, bnd_key) - {key1} grp2 = branch_atom_keys(gra, key1, bnd_key) - {key2} grp1 = tuple(sorted(grp1)) grp2 = tuple(sorted(grp2)) return grp1, grp2
def qualitative_convergence_checker_(gra, keys, rqq_bond_max=1.8, rqh_bond_max=1.3, rhh_bond_max=1.1, bond_nobond_diff=0.3): """ a convergence checker for error minimization, checking that the geometry is qualitatively correct (correct connectivity and stereo) """ symb_dct = atom_symbols(gra) pairs = set(map(frozenset, itertools.combinations(keys, 2))) bnd_keys = pairs & bond_keys(gra) nob_keys = pairs - bond_keys(gra) nob_symbs = tuple(tuple(map(symb_dct.__getitem__, nob_key)) for nob_key in nob_keys) bnd_symbs = tuple(tuple(map(symb_dct.__getitem__, bnd_key)) for bnd_key in bnd_keys) nob_idxs = tuple(tuple(map(keys.index, nob_key)) for nob_key in nob_keys) bnd_idxs = tuple(tuple(map(keys.index, bnd_key)) for bnd_key in bnd_keys) bnd_udists = tuple((rqq_bond_max if 'H' not in symb else rhh_bond_max if set(symb) == {'H'} else rqh_bond_max) for symb in bnd_symbs) diff = bond_nobond_diff nob_ldists = tuple((rqq_bond_max+diff if 'H' not in symb else rhh_bond_max+diff if set(symb) == {'H'} else rqh_bond_max+diff) for symb in nob_symbs) bnd_idxs += tuple(map(tuple, map(reversed, bnd_idxs))) bnd_idx_vecs = tuple(map(list, zip(*bnd_idxs))) bnd_udists *= 2 nob_idxs += tuple(map(tuple, map(reversed, nob_idxs))) nob_idx_vecs = tuple(map(list, zip(*nob_idxs))) nob_ldists *= 2 symbs = tuple(map(symb_dct.__getitem__, keys)) geo_idx_dct = dict(map(reversed, enumerate(keys))) atm_ste_keys = atom_stereo_keys(gra) & set(keys) bnd_ste_keys = bond_stereo_keys(gra) & bnd_keys atm_ste_par_dct = atom_stereo_parities(gra) bnd_ste_par_dct = bond_stereo_parities(gra) def _is_converged(xmat, err, grad): assert err and numpy.any(grad) xyzs = xmat[:, :3] dmat = embed.distance_matrix_from_coordinates(xyzs) # check for correct connectivity connectivity_check = ( (numpy.all(dmat[bnd_idx_vecs] < bnd_udists) if bnd_udists else True) and (numpy.all(dmat[nob_idx_vecs] > nob_ldists) if nob_ldists else True)) # check for correct stereo parities geo = _create.geom.from_data(symbs, xyzs, angstrom=True) atom_stereo_check = all( (_atom_stereo_parity_from_geometry(gra, atm_key, geo, geo_idx_dct) == atm_ste_par_dct[atm_key]) for atm_key in atm_ste_keys) bond_stereo_check = all( (_bond_stereo_parity_from_geometry(gra, bnd_key, geo, geo_idx_dct) == bnd_ste_par_dct[bnd_key]) for bnd_key in bnd_ste_keys) return connectivity_check and atom_stereo_check and bond_stereo_check return _is_converged
def sorted_ring_atom_keys(rng): """ get a ring's atom keys, sorted in order of connectivity """ return sorted_ring_atom_keys_from_bond_keys(bond_keys(rng))
def _neighbor_keys(bnd_key, bnd_nbh): bnd_keys = bond_keys(bnd_nbh) bnd_keys -= {bnd_key} bnd_keys = frozenset(key for key in bnd_keys if key & bnd_key) return bnd_keys
def elimination(xgr1, xgr2): """identifies elimination reactions """ assert xgr1 == _explicit(xgr1) and xgr2 == _explicit(xgr2) tra = None xgrs1 = _connected_components(xgr1) xgrs2 = _connected_components(xgr2) tras = [] if len(xgrs1) == 1 and len(xgrs2) == 2: atms = atoms(xgr1) neighs = atom_neighbor_keys(xgr1) bnds = bond_keys(xgr1) radicals = _resonance_dominant_radical_atom_keys(xgr1) lonepairs = atom_lone_pair_counts(xgr1) for atmi in atms: i_neighs = neighs[atmi] for atmj in i_neighs: bnd_break_key_ij = _get_bnd_key(atmi, atmj, bnds) new_xgr = remove_bonds(xgr1, [bnd_break_key_ij]) new_xgrs = _connected_components(new_xgr) if len(new_xgrs) == 2: xgra, xgrb = new_xgrs atmsa = atoms(xgra) if atmi not in atmsa.keys(): xgrb, xgra = xgra, xgrb atmsa = atoms(xgra) neighsa = atom_neighbor_keys(xgra) atmsb = atoms(xgrb) neighs_i = neighsa[atmi] for atmk in atmsb: if atmk in radicals: for atml in neighs_i: neighs_l = neighsa[atml] if atml != atmj: bnd_break_key_il = _get_bnd_key( atmi, atml, bnds) bnd_form_key_kl = frozenset({atmk, atml}) newnew_xgr = remove_bonds( new_xgr, [bnd_break_key_il]) newnew_xgr = add_bonds( newnew_xgr, [bnd_form_key_kl]) atm_key_dct = _full_isomorphism( newnew_xgr, xgr2) if atm_key_dct: tra = [[bnd_form_key_kl], [ bnd_break_key_ij, bnd_break_key_il ]] return tra for atmm in neighs_l: if atmm != atmi: bnd_break_key_lm = _get_bnd_key( atml, atmm, bnds) bnd_form_key_km = frozenset( {atmk, atmm}) newnew_xgr = remove_bonds( new_xgr, [bnd_break_key_lm]) newnew_xgr = add_bonds( newnew_xgr, [bnd_form_key_km]) atm_key_dct = _full_isomorphism( newnew_xgr, xgr2) if atm_key_dct: tras.append([[bnd_form_key_km], [ bnd_break_key_ij, bnd_break_key_lm ]]) for atmi in atms: i_neighs = neighs[atmi] print('atmi test:', atmi) print('i_neighs test:', i_neighs) for atmj in i_neighs: bnd_break_key_ij = _get_bnd_key(atmi, atmj, bnds) new_xgr = remove_bonds(xgr1, [bnd_break_key_ij]) new_xgrs = _connected_components(new_xgr) if len(new_xgrs) == 2: xgra, xgrb = new_xgrs atmsa = atoms(xgra) if atmi not in atmsa.keys(): xgrb, xgra = xgra, xgrb atmsa = atoms(xgra) neighsa = atom_neighbor_keys(xgra) atmsb = atoms(xgrb) neighs_i = neighsa[atmi] print('len atmsb test:', len(atmsb)) for atmk in atmsb: if lonepairs[atmk] > 0 or len(atmsb) == 1: # if lonepairs[atmk] > 0: for atml in neighs_i: neighs_l = neighsa[atml] if atml != atmj: bnd_break_key_il = _get_bnd_key( atmi, atml, bnds) bnd_form_key_kl = frozenset({atmk, atml}) newnew_xgr = remove_bonds( new_xgr, [bnd_break_key_il]) newnew_xgr = add_bonds( newnew_xgr, [bnd_form_key_kl]) atm_key_dct = _full_isomorphism( newnew_xgr, xgr2) if atm_key_dct: tra = [[bnd_form_key_kl], [ bnd_break_key_ij, bnd_break_key_il ]] return tra for atmm in neighs_l: if atmm != atmi: bnd_break_key_lm = _get_bnd_key( atml, atmm, bnds) bnd_form_key_km = frozenset( {atmk, atmm}) newnew_xgr = remove_bonds( new_xgr, [bnd_break_key_lm]) newnew_xgr = add_bonds( newnew_xgr, [bnd_form_key_km]) atm_key_dct = _full_isomorphism( newnew_xgr, xgr2) if atm_key_dct: tras.append([[bnd_form_key_km], [ bnd_break_key_ij, bnd_break_key_lm ]]) if len(tras) < 1: tras = None return tras