def test__atoms_neighbor_atom_keys(): """ test graph.atoms_neighbor_atom_keys """ assert graph.atoms_neighbor_atom_keys(C8H13O_CGR) == { 0: frozenset({3}), 1: frozenset({4}), 2: frozenset({6}), 3: frozenset({0, 5}), 4: frozenset({1, 6}), 5: frozenset({3, 7}), 6: frozenset({2, 4, 7}), 7: frozenset({8, 5, 6}), 8: frozenset({7}) }
def eliminations(rct_gras, viable_only=True): """ find all possible elimination reactions for these reactants :param rct_gras: graphs for the reactants, without stereo and without overlapping keys :param viable_only: Filter out reactions with non-viable products? :type viable_only: bool :returns: a list of Reaction objects :rtype: tuple[Reaction] Eliminations are enumerated by forming a bond between an attacking heavy atom and another atom not initially bonded to it, forming a ring. The bond adjacent to the attacked atom is then broken, along with a second bond in the ring, downstream from the attacking heavy atom, away from the attacked atom. """ assert_is_valid_reagent_graph_list(rct_gras) rxns = [] if len(rct_gras) == 1: rct_gra, = rct_gras ngb_keys_dct = atoms_neighbor_atom_keys(rct_gra) # frm1_keys = atom_keys(rct_gra, excl_syms=('H',)) frm1_keys = unsaturated_atom_keys(rct_gra) rct_symbs = atom_symbols(rct_gra) frm1_keys_o = frozenset(key for key in frm1_keys if rct_symbs[key] == 'O') frm2_keys = atom_keys(rct_gra) bnd_keys = bond_keys(rct_gra) frm_bnd_keys = [(frm1_key, frm2_key) for frm1_key, frm2_key in itertools.product( frm1_keys_o, frm2_keys) if frm1_key != frm2_key and not frozenset({frm1_key, frm2_key}) in bnd_keys] for frm1_key, frm2_key in frm_bnd_keys: # Bond the radical atom to the hydrogen atom prds_gra = add_bonds(rct_gra, [(frm2_key, frm1_key)]) # Get keys to the ring formed by this extra bond rng_keys = next((ks for ks in rings_atom_keys(prds_gra) if frm2_key in ks and frm1_key in ks), None) # Eliminations (as far as I can tell) only happen through TSs with # 3- or 4-membered rings if rng_keys is not None and len(rng_keys) < 5: frm1_ngb_key, = ngb_keys_dct[frm1_key] & set(rng_keys) frm2_ngb_key, = ngb_keys_dct[frm2_key] & set(rng_keys) # Break the bonds on either side of the newly formed bond prds_gra = remove_bonds(prds_gra, [(frm1_key, frm1_ngb_key)]) prds_gra = remove_bonds(prds_gra, [(frm2_key, frm2_ngb_key)]) prd_gras = connected_components(prds_gra) if len(prd_gras) == 2: forw_tsg = ts.graph(rct_gra, frm_bnd_keys=[(frm1_key, frm2_key)], brk_bnd_keys=[(frm1_key, frm1_ngb_key), (frm2_key, frm2_ngb_key) ]) back_tsg = ts.graph(prds_gra, frm_bnd_keys=[(frm1_key, frm1_ngb_key), (frm2_key, frm2_ngb_key) ], brk_bnd_keys=[(frm1_key, frm2_key)]) rcts_atm_keys = list(map(atom_keys, rct_gras)) prds_atm_keys = list(map(atom_keys, prd_gras)) if frm2_key not in prds_atm_keys[1]: prds_atm_keys = list(reversed(prds_atm_keys)) # Create the reaction object rxns.append( Reaction( rxn_cls=par.ReactionClass.Typ.ELIMINATION, forw_tsg=forw_tsg, back_tsg=back_tsg, rcts_keys=rcts_atm_keys, prds_keys=prds_atm_keys, )) if viable_only: rxns = filter_viable_reactions(rxns) return ts_unique(rxns)
def eliminations(rct_gras, prd_gras): """ find eliminations consistent with these reactants and products :param rct_gras: reactant graphs (must have non-overlapping keys) :param prd_gras: product graphs (must have non-overlapping keys) Eliminations are identified by forming a bond between an attacking heavy atom and another atom not initially bonded to it, forming a ring. The bond adjacent to the attacked atom is then broken, along with a second bond in the ring, downstream of the attacking heavy atom, away from the attacked atom. """ _assert_is_valid_reagent_graph_list(rct_gras) _assert_is_valid_reagent_graph_list(prd_gras) rxns = [] if len(rct_gras) == 1 and len(prd_gras) == 2: rct_gra, = rct_gras prds_gra = union_from_sequence(prd_gras) ngb_keys_dct = atoms_neighbor_atom_keys(rct_gra) frm1_keys = atom_keys(rct_gra, excl_syms=('H', )) frm2_keys = atom_keys(rct_gra) bnd_keys = bond_keys(rct_gra) frm_bnd_keys = [ (frm1_key, frm2_key) for frm1_key, frm2_key in itertools.product(frm1_keys, frm2_keys) if frm1_key != frm2_key and not frozenset({frm1_key, frm2_key}) in bnd_keys ] for frm1_key, frm2_key in frm_bnd_keys: # Bond the radical atom to the hydrogen atom gra_ = add_bonds(rct_gra, [(frm2_key, frm1_key)]) # Get keys to the ring formed by this extra bond rng_keys = next((ks for ks in rings_atom_keys(gra_) if frm2_key in ks and frm1_key in ks), None) # Eliminations (as far as I can tell) only happen through TSs with # 3- or 4-membered rings if rng_keys is not None and len(rng_keys) < 5: frm1_ngb_key, = ngb_keys_dct[frm1_key] & set(rng_keys) frm2_ngb_key, = ngb_keys_dct[frm2_key] & set(rng_keys) # Break the bonds on either side of the newly formed bond gra_ = remove_bonds(gra_, [(frm1_key, frm1_ngb_key)]) gra_ = remove_bonds(gra_, [(frm2_key, frm2_ngb_key)]) inv_dct = isomorphism(gra_, prds_gra) if inv_dct: f_frm_bnd_key = (frm1_key, frm2_key) f_brk_bnd_key1 = (frm1_key, frm1_ngb_key) f_brk_bnd_key2 = (frm2_key, frm2_ngb_key) inv_ = inv_dct.__getitem__ b_frm_bnd_key1 = tuple(map(inv_, f_brk_bnd_key1)) b_frm_bnd_key2 = tuple(map(inv_, f_brk_bnd_key2)) b_brk_bnd_key = tuple(map(inv_, f_frm_bnd_key)) forw_tsg = ts.graph( rct_gra, frm_bnd_keys=[f_frm_bnd_key], brk_bnd_keys=[f_brk_bnd_key1, f_brk_bnd_key2]) back_tsg = ts.graph( prds_gra, frm_bnd_keys=[b_frm_bnd_key1, b_frm_bnd_key2], brk_bnd_keys=[b_brk_bnd_key]) rcts_atm_keys = list(map(atom_keys, rct_gras)) prds_atm_keys = list(map(atom_keys, prd_gras)) if inv_dct[frm2_key] not in prds_atm_keys[1]: prds_atm_keys = list(reversed(prds_atm_keys)) # Create the reaction object rxns.append( Reaction( rxn_cls=par.ReactionClass.ELIMINATION, forw_tsg=forw_tsg, back_tsg=back_tsg, rcts_keys=rcts_atm_keys, prds_keys=prds_atm_keys, )) return ts_unique(rxns)
def beta_scissions(rct_gras, viable_only=True): """ find all possible beta scission reactions for these reactants :param rct_gras: graphs for the reactants, without stereo and without overlapping keys :param viable_only: Filter out reactions with non-viable products? :type viable_only: bool :returns: a list of Reaction objects :rtype: tuple[Reaction] FIX DESCRIPTION: Beta scissions are enumerated by identifying all pure single bonds (single bonds with no resonances), and looping over the results of breaking each of them. If this gives rise to two distinct fragments, the reaction is added to the list. """ assert_is_valid_reagent_graph_list(rct_gras) rxns = [] if len(rct_gras) == 1: rct_gra, = rct_gras # Identify all atom keys that neighbor radical sites rad_neighs = frozenset({}) neigh_dct = atoms_neighbor_atom_keys(rct_gra) for rad_key in radical_atom_keys(rct_gra): rad_neighs = rad_neighs | neigh_dct[rad_key] # Identify all pure single bonds involving radical site neighbor avg_bnd_ord_dct = resonance_avg_bond_orders(rct_gra) brk_bnd_keys = dict_.keys_by_value(avg_bnd_ord_dct, lambda x: x == 1) beta_bnd_keys = () for brk_bnd_key in brk_bnd_keys: if brk_bnd_key & rad_neighs: beta_bnd_keys += (brk_bnd_key, ) for brk_bnd_key in beta_bnd_keys: prds_gra = remove_bonds(rct_gra, [brk_bnd_key]) prd_gras = connected_components(prds_gra) if len(prd_gras) == 2: prd_gras = sort_reagents(prd_gras) forw_tsg = ts.graph(rct_gra, frm_bnd_keys=[], brk_bnd_keys=[brk_bnd_key]) back_tsg = ts.graph(prds_gra, frm_bnd_keys=[brk_bnd_key], brk_bnd_keys=[]) # Create the reaction object rxns.append( Reaction( rxn_cls=par.ReactionClass.Typ.BETA_SCISSION, forw_tsg=forw_tsg, back_tsg=back_tsg, rcts_keys=list(map(atom_keys, rct_gras)), prds_keys=list(map(atom_keys, prd_gras)), )) if viable_only: rxns = filter_viable_reactions(rxns) return ts_unique(rxns)
def _rotor_counts(gra, symbs): """ Count up various types of bonds for a structure. :param gra: molecular graph of species :type gra: automol graph data structure :param symbs: atomic symbols of species :type symbs: tuple(str) :rtype: tuple(float) """ # Initialize the rotor counts n_pp, n_ps, n_pt, n_pq = 0, 0, 0, 0 n_ss, n_st, n_sq = 0, 0, 0 n_tt, n_tq = 0, 0 n_qq = 0 n_co, n_oo = 0, 0 n_ss_ring, n_rings = 0, 0 # Get the rings and the number rings = _rings(gra) ring_keys = set(ring_idxs(_rings(gra))) n_rings = len(rings) # Loop over the bonds and count the number of atoms neighbors = atoms_neighbor_atom_keys(gra) for bnd in bond_keys(gra): key1, key2 = bnd spair = (symbs[key1], symbs[key2]) if spair == ('C', 'C'): # Figure out which neighbors are not hydrogen and count the number atom1_neighbors = neighbors[key1] numc1 = 0 for neighbor1 in atom1_neighbors: if symbs[neighbor1] != 'H': numc1 += 1 atom2_neighbors = neighbors[key2] numc2 = 0 for neighbor2 in atom2_neighbors: if symbs[neighbor2] != 'H': numc2 += 1 # Determine appropriate term to increment npair = (numc1, numc2) if npair == (1, 1): n_pp += 1 elif npair in ((1, 2), (2, 1)): n_ps += 1 elif npair in ((1, 3), (3, 1)): n_pt += 1 elif npair in ((1, 4), (4, 1)): n_pq += 1 elif npair == (2, 2): if {key1, key2} <= ring_keys: n_ss_ring += 1 else: n_ss += 1 elif npair in ((2, 3), (3, 2)): n_st += 1 elif npair in ((2, 4), (4, 2)): n_sq += 1 elif npair == (3, 3): n_tt += 1 elif npair in ((3, 4), (4, 3)): n_tq += 1 elif npair == (4, 4): n_qq += 1 elif spair in (('C', 'O'), ('O', 'C')): n_co += 1 elif spair == ('O', 'O'): n_oo += 1 # Compile counts into a tuple return (n_pp, n_ps, n_pt, n_pq, n_ss, n_st, n_sq, n_tt, n_tq, n_qq, n_co, n_oo, n_ss_ring, n_rings)