def proton_migration(xgr1, xgr2): """ find a proton migration transformation """ assert xgr1 == _explicit(xgr1) and xgr2 == _explicit(xgr2) tras = [] xgrs1 = _connected_components(xgr1) xgrs2 = _connected_components(xgr2) h_atm_key1 = max(_atom_keys(xgr1)) + 1 h_atm_key2 = max(_atom_keys(xgr2)) + 1 if len(xgrs1) == 1 and len(xgrs2) == 1: xgr1, = xgrs1 xgr2, = xgrs2 atm_keys1 = _unsaturated_atom_keys(xgr1) atm_keys2 = _unsaturated_atom_keys(xgr2) for atm_key1, atm_key2 in itertools.product(atm_keys1, atm_keys2): xgr1_h = _add_atom_explicit_hydrogen_keys(xgr1, {atm_key1: [h_atm_key1]}) xgr2_h = _add_atom_explicit_hydrogen_keys(xgr2, {atm_key2: [h_atm_key2]}) inv_atm_key_dct = _full_isomorphism(xgr2_h, xgr1_h) if inv_atm_key_dct: tras.append( from_data( 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] }])) if len(tras) < 1: tras = None return tras
def longest_chain(xgr): """ longest chain in the graph """ atm_keys = _atom_keys(xgr) max_chain = max((_longest_chain(xgr, atm_key) for atm_key in atm_keys), key=len) return max_chain
def heuristic_geometry(xgr): """ heuristic geometry for this molecular graph """ xgr = _explicit(xgr) atm_keys = sorted(_atom_keys(xgr)) syms = dict_.values_by_key(_atom_symbols(xgr), atm_keys) xyzs = dict_.values_by_key(atom_stereo_coordinates(xgr), atm_keys) geo = automol.create.geom.from_data(syms, xyzs) return geo
def _correct_bond_stereo_coordinates(xgr, bnd_ste_par_dct, atm_xyz_dct): atm_ngb_keys_dct = _atom_neighbor_keys(xgr) bnd_keys = list(bnd_ste_par_dct.keys()) for bnd_key in bnd_keys: par = bnd_ste_par_dct[bnd_key] curr_par = _bond_stereo_parity_from_coordinates( xgr, bnd_key, atm_xyz_dct) if curr_par != par: atm1_key, atm2_key = bnd_key atm1_xyz = atm_xyz_dct[atm1_key] atm2_xyz = atm_xyz_dct[atm2_key] atm1_ngb_keys = atm_ngb_keys_dct[atm1_key] - {atm2_key} atm2_ngb_keys = atm_ngb_keys_dct[atm2_key] - {atm1_key} atm1_ngb_keys = stereo_sorted_atom_neighbor_keys( xgr, atm1_key, atm1_ngb_keys) atm2_ngb_keys = stereo_sorted_atom_neighbor_keys( xgr, atm2_key, atm2_ngb_keys) rot_axis = numpy.subtract(atm2_xyz, atm1_xyz) rot_ = cart.vec.rotate_(rot_axis, numpy.pi, orig_xyz=atm1_xyz) rot_atm_keys = _atom_keys( _branch(xgr, atm2_key, {atm2_key, atm2_ngb_keys[0]})) if len(atm2_ngb_keys) > 1: assert len(atm2_ngb_keys) == 2 rot_atm_keys |= _atom_keys( _branch(xgr, atm2_key, {atm2_key, atm2_ngb_keys[1]})) rot_atm_keys = list(rot_atm_keys) rot_atm_xyzs = list( map(rot_, map(atm_xyz_dct.__getitem__, rot_atm_keys))) atm_xyz_dct.update(dict(zip(rot_atm_keys, rot_atm_xyzs))) assert _bond_stereo_parity_from_coordinates(xgr, bnd_key, atm_xyz_dct) == par xgr = _set_bond_stereo_parities(xgr, {bnd_key: par}) return xgr, atm_xyz_dct
def resonance_dominant_atom_hybridizations(rgr): """ resonance-dominant atom hybridizations, by atom """ 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_longest_chains(xgr): """ longest chains, by atom """ atm_keys = _atom_keys(xgr) long_chain_dct = { atm_key: _longest_chain(xgr, atm_key) for atm_key in atm_keys } return long_chain_dct
def _correct_atom_stereo_coordinates(xgr, atm_ste_par_dct, atm_xyz_dct): ring_atm_keys = set(itertools.chain(*_rings_sorted_atom_keys(xgr))) atm_ngb_keys_dct = _atom_neighbor_keys(xgr) atm_keys = list(atm_ste_par_dct.keys()) for atm_key in atm_keys: par = atm_ste_par_dct[atm_key] curr_par = _atom_stereo_parity_from_coordinates( xgr, atm_key, atm_xyz_dct) atm_ngb_keys = atm_ngb_keys_dct[atm_key] if curr_par != par: # for now, we simply exclude rings from the pivot keys # (will not work for stereo atom at the intersection of two rings) atm_piv_keys = list(atm_ngb_keys - ring_atm_keys)[:2] assert len(atm_piv_keys) == 2 atm3_key, atm4_key = atm_piv_keys atm_xyz = atm_xyz_dct[atm_key] atm3_xyz = atm_xyz_dct[atm3_key] atm4_xyz = atm_xyz_dct[atm4_key] rot_axis = cart.vec.unit_bisector(atm3_xyz, atm4_xyz, orig_xyz=atm_xyz) rot_ = cart.vec.rotate_(rot_axis, numpy.pi, orig_xyz=atm_xyz) rot_atm_keys = list( _atom_keys(_branch(xgr, atm_key, {atm_key, atm3_key})) | _atom_keys(_branch(xgr, atm_key, {atm_key, atm4_key}))) rot_atm_xyzs = list( map(rot_, map(atm_xyz_dct.__getitem__, rot_atm_keys))) atm_xyz_dct.update(dict(zip(rot_atm_keys, rot_atm_xyzs))) new_par = _atom_stereo_parity_from_coordinates(xgr, atm_key, atm_xyz_dct) assert new_par == par xgr = _set_atom_stereo_parities(xgr, {atm_key: par}) return xgr, atm_xyz_dct
def sing_res_dom_radical_atom_keys(rgr): """ resonance-dominant radical atom keys,for one resonance """ 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 (keys of resonance-dominant radical sites) """ 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_hybridizations(rgr): """ atom hybridizations, by atom """ 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 _partial_hydrogen_abstraction(qh_xgr, q_xgr): tra = None h_atm_key = max(_atom_keys(q_xgr)) + 1 #rad_atm_keys = _resonance_dominant_radical_atom_keys(q_xgr) uns_atm_keys = automol.graph.unsaturated_atom_keys(q_xgr) for atm_key in uns_atm_keys: #for atm_key in rad_atm_keys: q_xgr_h = _add_atom_explicit_hydrogen_keys(q_xgr, {atm_key: [h_atm_key]}) inv_atm_key_dct = _full_isomorphism(q_xgr_h, qh_xgr) if inv_atm_key_dct: brk_bnd_keys = [ frozenset( {inv_atm_key_dct[atm_key], inv_atm_key_dct[h_atm_key]}) ] tra = from_data(frm_bnd_keys=[], brk_bnd_keys=brk_bnd_keys) return tra
def subresonances(rgr): """ this connected graph and its lower-spin (more pi-bonded) resonances """ def _inc_range(bnd_cap): return tuple(range(0, bnd_cap+1)) add_pi_bonds_ = functools.partial(_add_pi_bonds, rgr) atm_keys = list(_atom_keys(rgr)) bnd_keys = list(_bond_keys(rgr)) atm_unsat_vlcs = dict_.values_by_key( _atom_unsaturated_valences(rgr), atm_keys) atm_bnd_keys_lst = dict_.values_by_key(_atom_bond_keys(rgr), atm_keys) bnd_caps = dict_.values_by_key(_bond_capacities(rgr), bnd_keys) bnd_ord_dct = _bond_orders(rgr) def _is_valid(bnd_ord_inc_dct): # check if pi bonds exceed unsaturated valences def __tally(atm_bnd_keys): return sum(dict_.values_by_key(bnd_ord_inc_dct, atm_bnd_keys)) atm_unsat_vlc_decs = tuple(map(__tally, atm_bnd_keys_lst)) enough_elecs = numpy.all( numpy.less_equal(atm_unsat_vlc_decs, atm_unsat_vlcs)) # check if all bond orders are less than 4 (should only affect C2) bnd_inc_keys = bnd_ord_inc_dct.keys() bnd_incs = dict_.values_by_key(bnd_ord_inc_dct, bnd_inc_keys) bnd_ords = dict_.values_by_key(bnd_ord_dct, bnd_inc_keys) new_bnd_ords = numpy.add(bnd_ords, bnd_incs) not_too_many = numpy.all(numpy.less(new_bnd_ords, 4)) return enough_elecs and not_too_many def _bond_value_dictionary(bnd_vals): return dict(zip(bnd_keys, bnd_vals)) bnd_ord_incs_itr = itertools.product(*map(_inc_range, bnd_caps)) bnd_ord_inc_dct_itr = map(_bond_value_dictionary, bnd_ord_incs_itr) bnd_ord_inc_dct_itr = filter(_is_valid, bnd_ord_inc_dct_itr) rgrs = tuple(sorted(map(add_pi_bonds_, bnd_ord_inc_dct_itr), key=_frozen)) return rgrs
def _connected_graph_atom_coordinates(sgr): """ non-stereo-specific coordinates for a connected molecular graph (currently assumes a with at most one ring -- fix that) """ assert sgr == _explicit(sgr) atm_keys = _atom_keys(sgr) rng_atm_keys_lst = _rings_sorted_atom_keys(sgr) if len(atm_keys) == 1: atm1_key, = atm_keys atm_xyz_dct = {} atm_xyz_dct[atm1_key] = (0., 0., 0.) elif len(atm_keys) == 2: atm1_key, atm2_key = atm_keys atm1_xyz = (0., 0., 0.) dist = _bond_distance(sgr, atm2_key, atm1_key) atm2_xyz = cart.vec.from_internals(dist=dist, xyz1=atm1_xyz) atm_xyz_dct = {} atm_xyz_dct[atm1_key] = tuple(atm1_xyz) atm_xyz_dct[atm2_key] = tuple(atm2_xyz) elif not rng_atm_keys_lst: atm_ngb_keys_dct = _atom_neighbor_keys(sgr) # start assigning coordinates along the longest chain max_chain = longest_chain(sgr) atm2_key, atm3_key = max_chain[:2] # add a dummy atom to start the chain atm1_key = max(atm_keys) + 1 sgr = _add_atoms(sgr, {atm1_key: 'X'}) atm1_xyz = (0., 0., -5.) atm2_xyz = (0., 0., 0.) dist = _bond_distance(sgr, atm3_key, atm2_key) ang = numpy.pi / 2. atm3_xyz = cart.vec.from_internals(dist=dist, xyz1=atm2_xyz, ang=ang, xyz2=atm1_xyz) atm_xyz_dct = {} atm_xyz_dct[atm1_key] = tuple(atm1_xyz) atm_xyz_dct[atm2_key] = tuple(atm2_xyz) atm_xyz_dct[atm3_key] = tuple(atm3_xyz) atm_xyz_dct = _extend_atom_coordinates(sgr, atm1_key, atm2_key, atm3_key, atm_xyz_dct) atm_ngb_keys_dct = _atom_neighbor_keys(sgr) atm4_keys = sorted(atm_ngb_keys_dct[atm3_key] - {atm2_key}) for atm4_key in atm4_keys: atm_xyz_dct = _extend_atom_coordinates(sgr, atm4_key, atm3_key, atm2_key, atm_xyz_dct) # we don't actually need to remove it from sgr, but fwiw sgr = _remove_atoms(sgr, {atm1_key}) atm_xyz_dct.pop(atm1_key) elif len(rng_atm_keys_lst) == 1: # for now, we'll assume only one ring rng_atm_keys, = rng_atm_keys_lst rng_atm_xyzs = _polygon_coordinates(num=len(rng_atm_keys)) atm_xyz_dct = dict(zip(rng_atm_keys, rng_atm_xyzs)) assert len(rng_atm_keys) >= 3 trip_iter = mit.windowed(rng_atm_keys[-2:] + rng_atm_keys, 3) for atm1_key, atm2_key, atm3_key in trip_iter: atm_xyz_dct = _extend_atom_coordinates(sgr, atm1_key, atm2_key, atm3_key, atm_xyz_dct) else: raise NotImplementedError("This algorithm is currently not implemented" "for more than one ring.") return atm_xyz_dct