def is_stereo_compatible(tra, sgr1, sgr2): """ is this transformation compatible with the reactant/product stereo assignments? """ cgr1 = without_stereo_parities(sgr1) cgr2 = without_stereo_parities(sgr2) atm_key_dct = _full_isomorphism(apply(tra, cgr1), cgr2) # determine the stereo centers which are preserved in the transformation sgr1 = _relabel(sgr1, atm_key_dct) atm_keys = sorted(atom_stereo_keys(sgr1) & atom_stereo_keys(sgr2)) bnd_keys = sorted(bond_stereo_keys(sgr1) & bond_stereo_keys(sgr2)) atm_pars1 = dict_.values_by_key(atom_stereo_parities(sgr1), atm_keys) atm_pars2 = dict_.values_by_key(atom_stereo_parities(sgr2), atm_keys) bnd_pars1 = dict_.values_by_key(bond_stereo_parities(sgr1), bnd_keys) bnd_pars2 = dict_.values_by_key(bond_stereo_parities(sgr2), bnd_keys) atm_ngb_keys_dct1 = atom_neighbor_keys(sgr1) atm_ngb_keys_dct2 = atom_neighbor_keys(sgr2) ret = True for atm_key, par1, par2 in zip(atm_keys, atm_pars1, atm_pars2): atm_ngb_keys1 = stereo_sorted_atom_neighbor_keys( sgr1, atm_key, atm_ngb_keys_dct1[atm_key]) atm_ngb_keys2 = stereo_sorted_atom_neighbor_keys( sgr2, atm_key, atm_ngb_keys_dct2[atm_key]) if _permutation_parity(atm_ngb_keys1, atm_ngb_keys2): ret &= (par1 == par2) else: ret &= (par1 != par2) for bnd_key, par1, par2 in zip(bnd_keys, bnd_pars1, bnd_pars2): atm1_key, atm2_key = bnd_key atm1_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm1_key, atm_ngb_keys_dct1[atm1_key] - {atm2_key})[0] atm2_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm2_key, atm_ngb_keys_dct1[atm2_key] - {atm1_key})[0] atm1_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm1_key, atm_ngb_keys_dct2[atm1_key] - {atm2_key})[0] atm2_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm2_key, atm_ngb_keys_dct2[atm2_key] - {atm1_key})[0] if not ((atm1_ngb_key1 != atm1_ngb_key2) ^ (atm2_ngb_key1 != atm2_ngb_key2)): ret &= (par1 == par2) else: ret &= (par1 != par2) return ret
def from_data(fml_str, main_lyr_dct=None, char_lyr_dct=None, ste_lyr_dct=None, iso_lyr_dct=None): """ Build a ChI string from each of the various layers. :param fml_str: formula string, in Hill-sort order :type fml_str: str :param main_lyr_dct: main layers, specifying connectivity and implicit hydrogens, by key ('c' and 'h') :type main_lyr_dct: dict[str: str] :param char_lyr_dct: charge layers, by key ('q' and 'p') :type char_lyr_dct: dict[str: str] :param ste_lyr_dct: stero layers, by key ('b', 't', 'm', and 's') :type ste_lyr_dct: dict[str: str] :param iso_lyr_dct: isotope layers, by key ('i', 'h', 'b', 't', 'm', and 's') :type iso_lyr_dct: dict[str: str] :rtype: str """ main_dct = dict_.empty_if_none(main_lyr_dct) char_dct = dict_.empty_if_none(char_lyr_dct) ste_dct = dict_.empty_if_none(ste_lyr_dct) iso_dct = dict_.empty_if_none(iso_lyr_dct) main_lyrs = [ pfx + lyr for pfx, lyr in zip(MAIN_PFXS, dict_.values_by_key(main_dct, MAIN_PFXS)) if lyr ] char_lyrs = [ pfx + lyr for pfx, lyr in zip(CHAR_PFXS, dict_.values_by_key(char_dct, CHAR_PFXS)) if lyr ] ste_lyrs = [ pfx + lyr for pfx, lyr in zip(STE_PFXS, dict_.values_by_key(ste_dct, STE_PFXS)) if lyr ] iso_lyrs = [ pfx + lyr for pfx, lyr in zip(ISO_PFXS, dict_.values_by_key(iso_dct, ISO_PFXS)) if lyr ] chi = '/'.join(['AMChI=1', fml_str] + main_lyrs + char_lyrs + ste_lyrs + iso_lyrs) return chi
def add_atom_implicit_hydrogen_valences(gra, inc_atm_imp_hyd_vlc_dct): """ add atom imlicit hydrogen valences (increments can be positive or negative) """ atm_keys = list(inc_atm_imp_hyd_vlc_dct.keys()) atm_imp_hyd_vlcs = numpy.add( dict_.values_by_key(atom_implicit_hydrogen_valences(gra), atm_keys), dict_.values_by_key(inc_atm_imp_hyd_vlc_dct, atm_keys)) assert all(atm_imp_hyd_vlc >= 0 for atm_imp_hyd_vlc in atm_imp_hyd_vlcs) atm_imp_hyd_vlc_dct = dict_.transform_values( dict(zip(atm_keys, atm_imp_hyd_vlcs)), int) return set_atom_implicit_hydrogen_valences(gra, atm_imp_hyd_vlc_dct)
def _add_pi_bonds(rgr, bnd_ord_inc_dct): """ add pi bonds to this graph """ bnd_keys = bond_keys(rgr) assert set(bnd_ord_inc_dct.keys()) <= bnd_keys bnd_keys = list(bnd_keys) bnd_ords = dict_.values_by_key(bond_orders(rgr), bnd_keys) bnd_ord_incs = dict_.values_by_key(bnd_ord_inc_dct, bnd_keys, fill_val=0) new_bnd_ords = numpy.add(bnd_ords, bnd_ord_incs) bnd_ord_dct = dict(zip(bnd_keys, new_bnd_ords)) rgr = set_bond_orders(rgr, bnd_ord_dct) return rgr
def atom_hybridizations(rgr): """ atom hybridizations, by atom """ rgr = without_fractional_bonds(rgr) atm_keys = list(atom_keys(rgr)) atm_unsat_vlc_dct = atom_unsaturated_valences(rgr, bond_order=True) atm_bnd_vlc_dct = atom_bond_valences(rgr, bond_order=False) # note!! atm_unsat_vlcs = numpy.array( dict_.values_by_key(atm_unsat_vlc_dct, atm_keys)) atm_bnd_vlcs = numpy.array(dict_.values_by_key(atm_bnd_vlc_dct, atm_keys)) atm_lpcs = numpy.array( dict_.values_by_key(atom_lone_pair_counts(rgr), atm_keys)) atm_hybs = atm_unsat_vlcs + atm_bnd_vlcs + atm_lpcs - 1 atm_hyb_dct = dict_.transform_values(dict(zip(atm_keys, atm_hybs)), int) return atm_hyb_dct
def atom_unsaturated_valences(gra, bond_order=True): """ unsaturated valences, by atom element valences minus bonding valences = pi sites and radical electrons """ atm_keys = list(atom_keys(gra)) if not bond_order: gra = without_bond_orders(gra) atm_bnd_vlcs = dict_.values_by_key(atom_bond_valences(gra), atm_keys) atm_tot_vlcs = dict_.values_by_key(atom_element_valences(gra), atm_keys) atm_rad_vlcs = numpy.subtract(atm_tot_vlcs, atm_bnd_vlcs) atm_unsat_vlc_dct = dict_.transform_values( dict(zip(atm_keys, atm_rad_vlcs)), int) return atm_unsat_vlc_dct
def from_data(fml_slyr, main_lyr_dct=None, char_lyr_dct=None, ste_lyr_dct=None, iso_lyr_dct=None): """ Build an InChI string from each of the various layers. :param fml_slyr: sublayer of InChI string containing molecular formula :type fml_slyr: str :param main_lyr_dct: information for connectivity layer of InChI :type main_lyr_dct: dict[str: str] :param char_lyr_dct: information for charge layer of InChI :type char_lyr_dct: dict[str: str] :param ste_lyr_dct: information for stereochemistry layer of InChI :type ste_lyr_dct: dict[str: str] :param iso_lyr_dct: information for isotope layer of InChI :type iso_lyr_dct: dict[str: str] :rtype: str """ main_dct = dict_.empty_if_none(main_lyr_dct) char_dct = dict_.empty_if_none(char_lyr_dct) ste_dct = dict_.empty_if_none(ste_lyr_dct) iso_dct = dict_.empty_if_none(iso_lyr_dct) main_slyrs = [ pfx + slyr for pfx, slyr in zip( MAIN_PFXS, dict_.values_by_key(main_dct, MAIN_PFXS)) if slyr ] char_slyrs = [ pfx + slyr for pfx, slyr in zip( CHAR_PFXS, dict_.values_by_key(char_dct, CHAR_PFXS)) if slyr ] ste_slyrs = [ pfx + slyr for pfx, slyr in zip(STE_PFXS, dict_.values_by_key(ste_dct, STE_PFXS)) if slyr ] iso_slyrs = [ pfx + slyr for pfx, slyr in zip(ISO_PFXS, dict_.values_by_key(iso_dct, ISO_PFXS)) if slyr ] ich = '/'.join(['InChI=1', fml_slyr] + main_slyrs + char_slyrs + ste_slyrs + iso_slyrs) return ich
def branch_bond_keys(gra, atm_key, bnd_key): """ bond keys for branch extending along `bnd_key` away from `atm_key` """ # bnd_key is the set of atom indices for the bond of interest # atm_bnd_keys_dct is a dictionary of atoms that are connected to each atom bnd_key = frozenset(bnd_key) assert atm_key in bnd_key atm_bnd_keys_dct = atoms_bond_keys(gra) bnch_bnd_keys = {bnd_key} seen_bnd_keys = set() excl_bnd_keys = atm_bnd_keys_dct[atm_key] - {bnd_key} new_bnd_keys = {bnd_key} bnd_ngb_keys_dct = bonds_neighbor_bond_keys(gra) while new_bnd_keys: new_bnd_ngb_keys = set( itertools.chain( *dict_.values_by_key(bnd_ngb_keys_dct, new_bnd_keys))) bnch_bnd_keys.update(new_bnd_ngb_keys - excl_bnd_keys) seen_bnd_keys.update(new_bnd_keys) new_bnd_keys = bnch_bnd_keys - seen_bnd_keys return frozenset(bnch_bnd_keys)
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 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 compatible_reverse_stereomers(ste_tsg): """ Given a TS graph with stereo assignments, expand all possible reverse graphs compatble with the forward graph. :param ste_tsg: The TS graph, with stereo assignments. :returns: All possible reverse TS graphs. """ frm_bnd_keys = forming_bond_keys(ste_tsg) brk_bnd_keys = breaking_bond_keys(ste_tsg) _, des_ste_atm_keys = nonconserved_atom_stereo_keys(ste_tsg) _, des_ste_bnd_keys = nonconserved_bond_stereo_keys(ste_tsg) cons_atm_keys = sorted(atom_stereo_keys(ste_tsg) - des_ste_atm_keys) cons_bnd_keys = sorted(bond_stereo_keys(ste_tsg) - des_ste_bnd_keys) # 1. Determine index-based stereo assignments for conserved stereo centers idx_tsg = to_index_based_stereo(ste_tsg) cons_idx_atm_pars = dict_.values_by_key(atom_stereo_parities(idx_tsg), cons_atm_keys) cons_idx_bnd_pars = dict_.values_by_key(bond_stereo_parities(idx_tsg), cons_bnd_keys) # 2. Determine all possible index-based stereo assignments for the reverse # reaction. prds_gra = without_stereo_parities(products_graph(ste_tsg)) prds_sgrs = _stereomers(prds_gra) prds_idx_sgrs = list(map(_to_index_based_stereo, prds_sgrs)) rev_idx_tsgs_pool = [ graph(p, brk_bnd_keys, frm_bnd_keys) for p in prds_idx_sgrs ] # 3. Find possibilities which match the assignments for the conserved # stereo centers. rev_idx_tsgs = [] for rev_idx_tsg in rev_idx_tsgs_pool: rev_cons_idx_atm_pars = dict_.values_by_key( atom_stereo_parities(rev_idx_tsg), cons_atm_keys) rev_cons_idx_bnd_pars = dict_.values_by_key( bond_stereo_parities(rev_idx_tsg), cons_bnd_keys) if (rev_cons_idx_atm_pars == cons_idx_atm_pars and rev_cons_idx_bnd_pars == cons_idx_bnd_pars): rev_idx_tsgs.append(rev_idx_tsg) # 4. Convert the matching reverse graphs back from index-based stereo # assignments to absolute stereo assignments. rev_ste_tsgs = list(map(from_index_based_stereo, rev_idx_tsgs)) return rev_ste_tsgs
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 atoms_from_data(atm_symb_dct, atm_imp_hyd_vlc_dct=None, atm_ste_par_dct=None): """ Construct an atom dictionary from constituent data. format: atm_dct := {atm_key: (atm_sym, atm_imp_hyd_vlc, atm_ste_par), ...} :param atm_symb_dct: atomic symbols, by atom key :type atm_symb_dct: dict :param atm_imp_hyd_vlc_dct: the no. of implicit hydrogens associated with each atom, by atom key :type atm_imp_hyd_vlc_dct: dict :param atm_ste_par_dct: atom stereo parities, by atom key :type atm_ste_par_dct: dict """ keys = sorted(atm_symb_dct.keys()) symbs = dict_.values_by_key(atm_symb_dct, keys) vlcs = dict_.values_by_key(dict_.empty_if_none(atm_imp_hyd_vlc_dct), keys, fill_val=0) pars = dict_.values_by_key(dict_.empty_if_none(atm_ste_par_dct), keys, fill_val=None) natms = len(symbs) vlcs = [0] * natms if vlcs is None else list(vlcs) pars = [None] * natms if pars is None else list(pars) assert len(vlcs) == natms assert len(pars) == natms symbs = list(map(ptab.to_symbol, symbs)) vlcs = list(map(int, vlcs)) assert all(par in (None, False, True) for par in pars) pars = [bool(par) if par is not None else par for par in pars] atm_dct = dict(zip(keys, zip(symbs, vlcs, pars))) return atm_dct
def bonds_from_data(bnd_keys, bnd_ord_dct=None, bnd_ste_par_dct=None): """ construct bond dictionary graph from data format: bnd_dct := {bnd_key: (bnd_ord, bnd_ste_par), ...} [where bnd_key := frozenset({atm1_key, atm2_key})] :param bnd_keys: bond keys :type bnd_keys: set :param bnd_ord_dct: bond orders, by bond key :type bnd_ord_dct: dict :param bnd_ste_par_dct: bond stereo parities, by bond key :type bnd_ste_par_dct: dict :rtype: dict[frozenset({int}): tuple(str, str))] """ keys = sorted(bnd_keys) assert all(len(key) == 2 for key in keys) ords = dict_.values_by_key(dict_.empty_if_none(bnd_ord_dct), keys, fill_val=1) pars = dict_.values_by_key(dict_.empty_if_none(bnd_ste_par_dct), keys, fill_val=None) nbnds = len(keys) ords = [1] * nbnds if ords is None else list(ords) pars = [None] * nbnds if pars is None else list(pars) assert len(ords) == nbnds assert len(pars) == nbnds keys = list(map(frozenset, keys)) ords = [int(o) if round(o) == o else float(round(o, 1)) for o in ords] assert all(par in (None, False, True) for par in pars) pars = [bool(par) if par is not None else par for par in pars] bnd_dct = dict(zip(keys, zip(ords, pars))) return bnd_dct
def molfile_with_atom_mapping(gra, geo=None, geo_idx_dct=None): """ Generate an MOLFile 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[:] :returns: the MOLFile string, followed by a mapping from MOLFile atoms to atoms in the graph :rtype: (str, dict) """ 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 = automol.geom.base.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) return mlf, key_map_inv
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 from_graph(gra): """ igraph object from a molecular graph """ atm_keys = sorted(atom_keys(gra)) bnd_keys = sorted(bond_keys(gra), key=sorted) atm_vals = dict_.values_by_key(atoms(gra), atm_keys) bnd_vals = dict_.values_by_key(bonds(gra), bnd_keys) atm_colors = list(itertools.starmap(_encode_vertex_attributes, atm_vals)) bnd_colors = list(itertools.starmap(_encode_edge_attributes, bnd_vals)) atm_idx_dct = dict(map(reversed, enumerate(atm_keys))) bnd_idxs = [sorted(map(atm_idx_dct.__getitem__, k)) for k in bnd_keys] igr = igraph.Graph(bnd_idxs) igr.vs['keys'] = atm_keys igr.vs['color'] = atm_colors igr.es['color'] = bnd_colors return igr
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 atom_bond_valences(gra, bond_order=True): """ bond count (bond valence), by atom """ atm_keys = list(atom_keys(gra)) gra = explicit(gra) if not bond_order: gra = without_bond_orders(gra) atm_nbhs = dict_.values_by_key(atom_neighborhoods(gra), atm_keys) atm_bnd_vlcs = [sum(bond_orders(nbh).values()) for nbh in atm_nbhs] atm_bnd_vlc_dct = dict_.transform_values(dict(zip(atm_keys, atm_bnd_vlcs)), int) return atm_bnd_vlc_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 sing_res_dom_radical_atom_keys(rgr): """ resonance-dominant radical atom keys,for one 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) ] 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 terminal_heavy_atom_keys(gra): """ terminal heavy atoms, sorted by atom type and hydrogen count """ gra = implicit(gra) atm_imp_hyd_vlc_dct = atom_implicit_hydrogen_valences(gra) atm_keys = [ key for key, ngb_keys in atoms_neighbor_atom_keys(gra).items() if len(ngb_keys) == 1 ] atm_keys = sorted(atm_keys, key=atm_imp_hyd_vlc_dct.__getitem__, reverse=True) atm_symbs = dict_.values_by_key(atom_symbols(gra), atm_keys) srt = automol.formula.argsort_symbols(atm_symbs, symbs_first=('C', )) atm_keys = tuple(map(atm_keys.__getitem__, srt)) return atm_keys
def resonance_dominant_radical_atom_keys(rgr): """ resonance-dominant radical atom keys (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 atom_stereo_parity_from_geometry(gra, atm_key, geo, geo_idx_dct): """ get the current stereo parity of an atom from its geometry """ atm_ngb_keys_dct = atoms_neighbor_atom_keys(gra) atm_ngb_keys = atm_ngb_keys_dct[atm_key] # sort the neighbor keys by stereo priority atm_ngb_keys = atom_stereo_sorted_neighbor_atom_keys( gra, atm_key, atm_ngb_keys) # determine the parity based on the coordinates xyzs = automol.geom.base.coordinates(geo) atm_ngb_idxs = dict_.values_by_key(geo_idx_dct, atm_ngb_keys) atm_ngb_xyzs = [xyzs[idx] for idx in atm_ngb_idxs] det_mat = numpy.ones((4, 4)) det_mat[:, :3] = atm_ngb_xyzs det_val = numpy.linalg.det(det_mat) assert det_val != 0. # for now, assume no four-atom planes par = det_val > 0. return par
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