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 _bond_capacities(rgr): """ the number of electron pairs available for further pi-bonding, by bond """ rgr = without_dummy_bonds(rgr) atm_unsat_vlc_dct = atom_unsaturated_valences(rgr) def _pi_capacities(bnd_key): return min(map(atm_unsat_vlc_dct.__getitem__, bnd_key)) bnd_keys = list(bond_keys(rgr)) bnd_caps = tuple(map(_pi_capacities, bnd_keys)) bnd_cap_dct = dict(zip(bnd_keys, bnd_caps)) return bnd_cap_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 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_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 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