def dominant_resonances(rgr): """ all dominant (minimum spin/maximum pi) resonance graphs """ rgr = without_fractional_bonds(rgr) rgrs = resonances(rgr) mult_min = min(map(maximum_spin_multiplicity, rgrs)) dom_rgrs = tuple(rgr for rgr in rgrs if maximum_spin_multiplicity(rgr) == mult_min) return dom_rgrs
def radical_groups(gra): """ returns a list of lists of groups attached each radical """ gra = without_fractional_bonds(gra) groups = [] rads = sing_res_dom_radical_atom_keys(gra) for rad in rads: groups.append(atom_groups(gra, rad)) return groups
def resonance_dominant_atom_hybridizations(rgr): """ resonance-dominant atom hybridizations, by atom """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_hybs_by_res = [ dict_.values_by_key(atom_hybridizations(dom_rgr), atm_keys) for dom_rgr in dominant_resonances(rgr) ] atm_hybs = [min(hybs) for hybs in zip(*atm_hybs_by_res)] atm_hyb_dct = dict(zip(atm_keys, atm_hybs)) return atm_hyb_dct
def 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) ] bnd_ords_lst = list(map(frozenset, zip(*bnd_ords_by_res))) bnd_dom_res_ords_dct = dict(zip(bnd_keys, bnd_ords_lst)) return bnd_dom_res_ords_dct
def nonresonant_radical_atom_keys(rgr): """ keys for radical atoms that are not in resonance """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_rad_vlcs_by_res = [ dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys) for dom_rgr in dominant_resonances(rgr) ] atm_rad_vlcs = [min(rad_vlcs) for rad_vlcs in zip(*atm_rad_vlcs_by_res)] atm_rad_keys = frozenset( atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs) if atm_rad_vlc) return atm_rad_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 sigma_radical_atom_keys(rgr): """ keys for sigma radical atoms """ rgr = without_fractional_bonds(rgr) atm_rad_keys = nonresonant_radical_atom_keys(rgr) bnd_ords_dct = resonance_dominant_bond_orders(rgr) atm_bnd_keys_dct = atoms_bond_keys(rgr) atm_sig_keys = [] for atm_key in atm_rad_keys: for bnd_key in atm_bnd_keys_dct[atm_key]: if 3 in bnd_ords_dct[bnd_key]: atm_sig_keys.append(atm_key) break atm_sig_keys = frozenset(atm_sig_keys) return atm_sig_keys
def radical_group_dct(gra): """ return a dictionary of lists of groups attached each radical """ gra = without_fractional_bonds(gra) groups = {} rads = list(sing_res_dom_radical_atom_keys(gra)) atms = atoms(gra) for rad in rads: key = atms[rad][0] if key in groups: groups[atms[rad][0]] += atom_groups(gra, rad) else: groups[atms[rad][0]] = atom_groups(gra, rad) return groups
def radical_atom_keys(gra, single_res=False, min_valence=1.): """ Radical atom keys for this molecular graph Radical atoms are based on the lowest-spin resonance structures for this graph. If the `single_res` flag is set, a single low-spin resonance structure will be chosen when there are multiple such structures. This function should eventually replace both `resonance_dominant_radical_atom_keys` and `sing_res_dom_radical_atom_keys` for a more user-friendly interface. Note that this function ignores the bond orders in `gra`. If you wish to identify radical atom keys based on the bond orders in `gra`, this can be done by using the `atom_unsaturated_valences` function. :param gra: the molecular graph :param single_res: only include radical keys for a single (arbitrary) resonance structure, or include all atoms that are radicals in any of the low-spin resonance structures? :type single_res: bool :param min_valence: optionally, specify that only sites with at least a certain number of radical electrons be included :type min_valence: int :returns: the radical atom keys :rtype: frozenset[int] """ gra = without_fractional_bonds(gra) atm_keys = list(atom_keys(gra)) if single_res: atm_rad_vlcs = dict_.values_by_key( atom_unsaturated_valences(dominant_resonance(gra)), atm_keys) else: atm_rad_vlcs_by_res = [ dict_.values_by_key(atom_unsaturated_valences(dom_gra), atm_keys) for dom_gra in dominant_resonances(gra) ] atm_rad_vlcs = [ max(rad_vlcs) for rad_vlcs in zip(*atm_rad_vlcs_by_res) ] atm_rad_keys = frozenset( atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs) if atm_rad_vlc >= min_valence) return atm_rad_keys
def sing_res_dom_radical_atom_keys(rgr): """ resonance-dominant radical atom keys,for one resonance TODO: DEPRECATE """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_rad_vlcs_by_res = [ dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys) for dom_rgr in dominant_resonances(rgr) ] first_atm_rad_val = [atm_rad_vlcs_by_res[0]] atm_rad_vlcs = [max(rad_vlcs) for rad_vlcs in zip(*first_atm_rad_val)] atm_rad_keys = frozenset( atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs) if atm_rad_vlc) return atm_rad_keys
def resonance_dominant_atom_centered_cumulene_keys(rgr): """ resonance dominant keys for atom-centered cumulenes the bond-centered cumulenes are described by (frozenset({end_atm_key1, end_atm_key2}), cent_atm_key) where the first pair contains the sp2 atoms at the cumulene ends and `cent_atm_key` is the key of the central atom """ rgr = without_fractional_bonds(rgr) cum_chains = _cumulene_chains(rgr) cum_keys = set() for cum_chain in cum_chains: size = len(cum_chain) if size % 2 == 1: cum_keys.add((frozenset({cum_chain[0], cum_chain[-1]}), cum_chain[size // 2])) cum_keys = frozenset(cum_keys) return cum_keys
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, stereo=False): if isomorphism(group, pgra1, backbone_only=True): 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 resonance_dominant_radical_atom_keys(rgr): """ resonance-dominant radical atom keys TODO: DEPRECATE (keys of resonance-dominant radical sites) """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_rad_vlcs_by_res = [ dict_.values_by_key(atom_unsaturated_valences(dom_rgr), atm_keys) for dom_rgr in dominant_resonances(rgr) ] atm_rad_vlcs = [max(rad_vlcs) for rad_vlcs in zip(*atm_rad_vlcs_by_res)] atm_rad_keys = frozenset( atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs) if atm_rad_vlc) return atm_rad_keys
def sigma_radical_atom_keys(rgr): """ keys for sigma radical atoms :param rgr: the molecular graph :returns: the sigma radical atom keys :rtype: frozenset[int] """ rgr = without_fractional_bonds(rgr) atm_rad_keys = nonresonant_radical_atom_keys(rgr) bnd_ords_dct = resonance_dominant_bond_orders(rgr) atm_bnd_keys_dct = atoms_bond_keys(rgr) atm_sig_keys = [] for atm_key in atm_rad_keys: for bnd_key in atm_bnd_keys_dct[atm_key]: if 3 in bnd_ords_dct[bnd_key]: atm_sig_keys.append(atm_key) break atm_sig_keys = frozenset(atm_sig_keys) return atm_sig_keys
def linear_atom_keys(rgr, dummy=True): """ atoms forming linear bonds, based on their hybridization :param rgr: the graph :param dummy: whether or not to consider atoms connected to dummy atoms as linear, if different from what would be predicted based on their hybridization :returns: the linear atom keys :rtype: tuple[int] """ rgr = without_fractional_bonds(rgr) atm_hyb_dct = resonance_dominant_atom_hybridizations(implicit(rgr)) lin_atm_keys = set(dict_.keys_by_value(atm_hyb_dct, lambda x: x == 1)) if dummy: dum_ngb_key_dct = dummy_atoms_neighbor_atom_key(rgr) lin_atm_keys |= set(dum_ngb_key_dct.values()) lin_atm_keys = tuple(sorted(lin_atm_keys)) return lin_atm_keys
def subresonances(rgr): """ this connected graph and its lower-spin (more pi-bonded) resonances """ rgr = without_fractional_bonds(rgr) # get the bond capacities (room for increasing bond order), filtering out # the negative ones to avoid complications with hypervalent atoms in TSs bnd_cap_dct = dict_.by_value(_bond_capacities(rgr), lambda x: x > 0) ret_rgrs = [] if bnd_cap_dct: bnd_keys, bnd_caps = zip(*bnd_cap_dct.items()) atm_keys = list(functools.reduce(frozenset.union, bnd_keys)) # Loop over all possible combinations of bond order increments (amounts # by which to increase the bond order), filtering out combinations that # exceed the valences of the atoms involved. # (Note that we are only testing the bonds with available pi electrons, # so this is compatible with having hypervalent atoms elsewhere in the # molecule) bnd_ord_inc_ranges = [range(bnd_cap + 1) for bnd_cap in bnd_caps] for bnd_ord_incs in itertools.product(*bnd_ord_inc_ranges): bnd_ord_inc_dct = dict(zip(bnd_keys, bnd_ord_incs)) ret_rgr = _add_pi_bonds(rgr, bnd_ord_inc_dct) max_bnd_ord = max(bond_orders(ret_rgr).values()) atm_unsat_vlcs = dict_.values_by_key( atom_unsaturated_valences(ret_rgr), atm_keys) if not any(atm_unsat_vlc < 0 for atm_unsat_vlc in atm_unsat_vlcs): if max_bnd_ord < 4: ret_rgrs.append(ret_rgr) if not ret_rgrs: ret_rgrs = (rgr, ) else: ret_rgrs = tuple(ret_rgrs) return ret_rgrs
def radical_atom_keys_from_resonance(rgr, min_valence=1.): """ Radical atom keys for a resonance molecular graph Assumes the graph has already been assinged to a resonance structure. :param rgr: a resonance-structure molecular graph :param min_valence: optionally, specify that only sites with at least a certain number of radical electrons be included :type min_valence: int :returns: the radical atom keys :rtype: frozenset[int] """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_rad_vlcs = dict_.values_by_key(atom_unsaturated_valences(rgr), atm_keys) atm_rad_keys = frozenset( atm_key for atm_key, atm_rad_vlc in zip(atm_keys, atm_rad_vlcs) if atm_rad_vlc >= min_valence) return atm_rad_keys
def radical_dissociation_products(gra, pgra1): """ For a given species, determine the products of a dissociation occuring around a radical site. We assume one of the dissociation products is known, and we attempt to find the corresponding product. Currently, we assume that the input pgra1 is appropriately stereolabeled. :param gra: species undergoing dissociation :type gra: automol.graph object :param pgra1: one of the known products of dissociation :type pgra1: automol.graph object :rtype: tuple(automol.graph.object) """ # Remove gractional bonds for functions to work gra = without_fractional_bonds(gra) # Attempt to find a graph of product corresponding to pgra1 pgra2 = None for rad in sing_res_dom_radical_atom_keys(gra): for adj in atoms_neighbor_atom_keys(gra)[rad]: for group in atom_groups(gra, adj, stereo=False): if isomorphism(group, pgra1, backbone_only=True): pgra2 = remove_atoms(gra, atom_keys(group)) if bond_keys(group) in pgra2: pgra2 = remove_bonds(pgra2, bond_keys(group)) # If pgra2 is ID'd, rebuild the two product graphs with stereo labels if pgra2 is not None: keys2 = atom_keys(pgra2) idx_gra = to_index_based_stereo(gra) idx_pgra2 = subgraph(idx_gra, keys2, stereo=True) pgra2 = from_index_based_stereo(idx_pgra2) return pgra1, pgra2