def isomorphic_radical_graphs(gra): """ Generate a set of graphs that are isomorphic to a graph of a radical species """ # Determine useful keys symbols = atom_symbols(gra) unsat_keys = unsaturated_atom_keys(gra) unsat_key = next(iter(unsat_keys)) h_atm_key = max(symbols.keys()) + 1 iso_gras = [] for aidx, symbol in enumerate(symbols.values()): # Loop over saturated (non-radical) heavy atoms if symbol != 'H' and aidx != unsat_key: # Add hydrogen atom to radical atom new_graph = add_atom_explicit_hydrogen_keys( gra, {unsat_key: [h_atm_key]}) # Remove hydrogen from saturated atom neighbors = atom_neighbor_keys(new_graph) for neigh in neighbors[aidx]: if symbols[neigh] == 'H': aneighbor = neigh break new_graph = remove_atoms(new_graph, [aneighbor]) # Test to see if new radical species is the same as the original inv_atm_key_dct = full_isomorphism(gra, new_graph) if inv_atm_key_dct: iso_gras.append(new_graph) return iso_gras
def _radical_graph_isomorphisms(gra): """ Generate a set of graphs where the radical has been migrated to a new atom and then calculate the isomorphism between the input graph and all of the new graphs generated by moving the radical """ # Determine useful keys symbols = atom_symbols(gra) unsat_keys = unsaturated_atom_keys(gra) unsat_key = next(iter(unsat_keys)) h_atm_key = max(symbols.keys()) + 1 new_gras, isomorphisms = [], [] for aidx, symbol in enumerate(symbols.values()): # Loop over saturated (non-radical) heavy atoms if symbol != 'H' and aidx != unsat_key: # Add hydrogen atom to radical atom new_graph = add_atom_explicit_hydrogen_keys( gra, {unsat_key: [h_atm_key]}) # Remove hydrogen from saturated atom neighbors = atoms_neighbor_atom_keys(new_graph) for neigh in neighbors[aidx]: if symbols[neigh] == 'H': aneighbor = neigh break new_graph = remove_atoms(new_graph, [aneighbor]) # Build lists to return new_gras.append(new_graph) isomorphisms.append(full_isomorphism(gra, new_graph)) return tuple(zip(new_gras, isomorphisms))
def hydrogen_migration(rct_gras, prd_gras): """ find a hydrogen migration transformation Hydrogen migrations are identified by adding a hydrogen to an unsaturated site of the reactant and adding a hydrogen to an unsaturated site of the product and seeing if they match up. If so, we have a hydrogen migration between these two sites. """ _assert_is_valid_reagent_graph_list(rct_gras) _assert_is_valid_reagent_graph_list(prd_gras) tras = [] rct_idxs = None prd_idxs = None is_triv = is_trivial_reaction(rct_gras, prd_gras) if len(rct_gras) == 1 and len(prd_gras) == 1 and not is_triv: gra1, = rct_gras gra2, = prd_gras h_atm_key1 = max(atom_keys(gra1)) + 1 h_atm_key2 = max(atom_keys(gra2)) + 1 atm_keys1 = unsaturated_atom_keys(gra1) atm_keys2 = unsaturated_atom_keys(gra2) for atm_key1, atm_key2 in itertools.product(atm_keys1, atm_keys2): gra1_h = add_atom_explicit_hydrogen_keys(gra1, {atm_key1: [h_atm_key1]}) gra2_h = add_atom_explicit_hydrogen_keys(gra2, {atm_key2: [h_atm_key2]}) inv_atm_key_dct = full_isomorphism(gra2_h, gra1_h) if inv_atm_key_dct: tra = trans.from_data( rxn_class=par.REACTION_CLASS.HYDROGEN_MIGRATION, frm_bnd_keys=[{atm_key1, inv_atm_key_dct[h_atm_key2]}], brk_bnd_keys=[{ inv_atm_key_dct[atm_key2], inv_atm_key_dct[h_atm_key2] }]) tras.append(tra) rct_idxs = (0, ) prd_idxs = (0, ) tras = tuple(tras) return tras, rct_idxs, prd_idxs
def prod_addition(x_gra, y_gra): """ products of addition """ prod_gras = tuple() shift = len(automol.graph.atoms(x_gra)) y_gra = automol.graph.transform_keys(y_gra, lambda x: x + shift) x_keys = unsaturated_atom_keys(x_gra) y_keys = unsaturated_atom_keys(y_gra) for x_key, y_key in itertools.product(x_keys, y_keys): xy_gra = add_bonds(union(x_gra, y_gra), [{x_key, y_key}]) prod_gras += ((xy_gra, ), ) return _unique_gras(prod_gras)
def addition(rct_gras, prd_gras): """ find an addition transformation Additions are identified by joining an unsaturated site on one reactant to an unsaturated site on the other. If the result matches the products, this is an addition reaction. """ _assert_is_valid_reagent_graph_list(rct_gras) _assert_is_valid_reagent_graph_list(prd_gras) tras = [] rct_idxs = None prd_idxs = None is_triv = is_trivial_reaction(rct_gras, prd_gras) if len(rct_gras) == 2 and len(prd_gras) == 1 and not is_triv: x_gra, y_gra = rct_gras prd_gra, = prd_gras x_atm_keys = unsaturated_atom_keys(x_gra) y_atm_keys = unsaturated_atom_keys(y_gra) for x_atm_key, y_atm_key in itertools.product(x_atm_keys, y_atm_keys): xy_gra = add_bonds(union(x_gra, y_gra), [{x_atm_key, y_atm_key}]) atm_key_dct = full_isomorphism(xy_gra, prd_gra) if atm_key_dct: tra = trans.from_data(rxn_class=par.REACTION_CLASS.ADDITION, frm_bnd_keys=[{x_atm_key, y_atm_key}], brk_bnd_keys=[]) tras.append(tra) # sort the reactants so that the largest species is first rct_idxs = _argsort_reactants(rct_gras) prd_idxs = (0, ) tras = tuple(tras) return tras, rct_idxs, prd_idxs
def ring_forming_scission(rct_gras, prd_gras): """ find a ring forming reaction that eliminates a radical group """ _assert_is_valid_reagent_graph_list(rct_gras) _assert_is_valid_reagent_graph_list(prd_gras) tras = [] rct_idxs = None prd_idxs = None is_triv = is_trivial_reaction(rct_gras, prd_gras) if len(rct_gras) == 1 and len(prd_gras) == 2 and not is_triv: rgra, = rct_gras pgra1, pgra2 = prd_gras pgra = automol.graph.union(pgra1, pgra2) rad_atm_keys = unsaturated_atom_keys(rgra) atms, bnds = rgra ngb_atms = automol.graph.atom_neighbor_keys(rgra) for rad_atm in rad_atm_keys: for xatm in atms: if (xatm != rad_atm and atms[xatm][1] != 'H' and xatm not in ngb_atms[rad_atm] and not tras): for natm in ngb_atms[xatm]: if natm != rad_atm: xgra = atms.copy(), bnds.copy() xgra = add_bonds(xgra, [frozenset({rad_atm, xatm})]) xgra = remove_bonds(xgra, [frozenset({xatm, natm})]) atm_key_dct = full_isomorphism(xgra, pgra) if atm_key_dct: tra = trans.from_data( rxn_class=( par.REACTION_CLASS.RING_FORM_SCISSION), frm_bnd_keys=[{rad_atm, xatm}], brk_bnd_keys=[ {xatm, natm}, ]) tras.append(tra) break # sort the reactants so that the largest species is first rct_idxs = (0, ) prd_idxs = _argsort_reactants(prd_gras) tras = tuple(tras) return tras, rct_idxs, prd_idxs
def _partial_hydrogen_abstraction(qh_gra, q_gra): rets = [] h_atm_key = max(atom_keys(q_gra)) + 1 uns_atm_keys = unsaturated_atom_keys(q_gra) for atm_key in uns_atm_keys: q_gra_h = add_atom_explicit_hydrogen_keys(q_gra, {atm_key: [h_atm_key]}) inv_atm_key_dct = full_isomorphism(q_gra_h, qh_gra) if inv_atm_key_dct: qh_q_atm_key = inv_atm_key_dct[atm_key] qh_h_atm_key = inv_atm_key_dct[h_atm_key] q_q_atm_key = atm_key rets.append((qh_q_atm_key, qh_h_atm_key, q_q_atm_key)) return rets
def rot_permutated_geoms(geo, frm_bnd_keys=(), brk_bnd_keys=()): """ Convert an input geometry to a list of geometries corresponding to the rotational permuations of all the terminal groups. :param geo: molecular geometry :type geo: automol molecular geometry data structure :param frm_bnd_keys: keys denoting atoms forming bond in TS :type frm_bnd_keys: frozenset(int) :param brk_bnd_keys: keys denoting atoms breaking bond in TS :type brk_bnd_keys: frozenset(int) :rtype: tuple(automol geom data structure) """ # Set saddle based on frm and brk keys existing saddle = bool(frm_bnd_keys or brk_bnd_keys) gra = automol.convert.geom.graph(geo, stereo=False) term_atms = {} all_hyds = [] neighbor_dct = atoms_neighbor_atom_keys(gra) ts_atms = [] for bnd in frm_bnd_keys: ts_atms.extend(list(bnd)) for bnd in brk_bnd_keys: ts_atms.extend(list(bnd)) # determine if atom is a part of a double bond unsat_atms = unsaturated_atom_keys(gra) if not saddle: rad_atms = sing_res_dom_radical_atom_keys(gra) res_rad_atms = resonance_dominant_radical_atom_keys(gra) rad_atms = [atm for atm in rad_atms if atm not in res_rad_atms] else: rad_atms = [] gra = gra[0] for atm in gra: if gra[atm][0] == 'H': all_hyds.append(atm) for atm in gra: if atm in unsat_atms and atm not in rad_atms: pass else: if atm not in ts_atms: nonh_neighs = [] h_neighs = [] neighs = neighbor_dct[atm] for nei in neighs: if nei in all_hyds: h_neighs.append(nei) else: nonh_neighs.append(nei) if len(nonh_neighs) < 2 and len(h_neighs) > 1: term_atms[atm] = h_neighs geo_final_lst = [geo] for atm in term_atms: hyds = term_atms[atm] geo_lst = [] for geom in geo_final_lst: geo_lst.extend(_swap_for_one(geom, hyds)) geo_final_lst = geo_lst return geo_final_lst
def end_group_symmetry_factor(geo, frm_bnd_keys=(), brk_bnd_keys=()): """ Determine sym factor for terminal groups in a geometry :param geo: molecular geometry :type geo: automol molecular geometry data structure :param frm_bnd_keys: keys denoting atoms forming bond in TS :type frm_bnd_keys: frozenset(int) :param brk_bnd_keys: keys denoting atoms breaking bond in TS :type brk_bnd_keys: frozenset(int) :rtype: (automol geom data structure, float) """ # Set saddle based on frm and brk keys existing saddle = bool(frm_bnd_keys or brk_bnd_keys) gra = automol.convert.geom.graph(geo, stereo=False) term_atms = {} all_hyds = [] neighbor_dct = atoms_neighbor_atom_keys(gra) ts_atms = [] for bnd in frm_bnd_keys: ts_atms.extend(list(bnd)) for bnd in brk_bnd_keys: ts_atms.extend(list(bnd)) # determine if atom is a part of a double bond unsat_atms = unsaturated_atom_keys(gra) if not saddle: rad_atms = sing_res_dom_radical_atom_keys(gra) res_rad_atms = resonance_dominant_radical_atom_keys(gra) rad_atms = [atm for atm in rad_atms if atm not in res_rad_atms] else: rad_atms = [] gra = gra[0] for atm in gra: if gra[atm][0] == 'H': all_hyds.append(atm) for atm in gra: if atm in unsat_atms and atm not in rad_atms: pass else: if atm not in ts_atms: nonh_neighs = [] h_neighs = [] neighs = neighbor_dct[atm] for nei in neighs: if nei in all_hyds: h_neighs.append(nei) else: nonh_neighs.append(nei) if len(nonh_neighs) == 1 and len(h_neighs) > 1: term_atms[atm] = h_neighs factor = 1. remove_atms = [] for atm in term_atms: hyds = term_atms[atm] if len(hyds) > 1: factor *= len(hyds) remove_atms.extend(hyds) geo = trans.remove_coordinates(geo, remove_atms) return geo, factor, remove_atms