예제 #1
0
def ring_arc_complement_atom_keys(gra, rng):
    """ non-intersecting arcs from a ring that shares segments with a graph
    """
    gra_atm_bnd_dct = atom_bond_keys(gra)
    rng_atm_bnd_dct = atom_bond_keys(rng)

    # 1. find divergence points, given by the atom at which the divergence
    # occurs and the bond followed by the ring as it diverges
    div_dct = {}

    for atm_key in atom_keys(gra) & atom_keys(rng):
        div = rng_atm_bnd_dct[atm_key] - gra_atm_bnd_dct[atm_key]
        if div:
            bnd_key, = div
            div_dct[atm_key] = bnd_key

    # 2. cycle through the ring atoms; if you meet a starting divergence, start
    # an arc; extend the arc until you meet an ending divergence; repeat until
    # all divergences are accounted for
    atm_keys = _sorted_ring_atom_keys(bond_keys(rng))

    arcs = []
    arc = []
    for atm_key, next_atm_key in mit.windowed(itertools.cycle(atm_keys), 2):
        bnd_key = frozenset({atm_key, next_atm_key})

        # if we haven't started an arc, see if we are at a starting divergence;
        # if so, start the arc now and cross the divergence from our list
        if not arc:
            if atm_key in div_dct and div_dct[atm_key] == bnd_key:
                div_dct.pop(atm_key)

                arc.append(atm_key)
        # if we've started an arc, extend it; then, check if we are at an
        # ending divergence; if so, end the arc and cross the divergence from
        # our list; add it to our list of arcs
        else:
            arc.append(atm_key)

            if next_atm_key in div_dct and div_dct[next_atm_key] == bnd_key:
                div_dct.pop(next_atm_key)

                arc.append(next_atm_key)
                arcs.append(arc)
                arc = []

        # if no divergences are left, break out of the loop
        if not div_dct:
            break

    arcs = tuple(map(tuple, arcs))
    return arcs
예제 #2
0
def _bond_stereo_corrected_geometry(gra, bnd_ste_par_dct, geo, geo_idx_dct):
    """ correct the bond stereo parities of a geometry, for a subset of bonds
    """
    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_geometry(gra, bnd_key, geo,
                                                     geo_idx_dct)

        if curr_par != par:
            xyzs = automol.geom.coordinates(geo)

            atm1_key, atm2_key = bnd_key
            atm1_xyz = xyzs[geo_idx_dct[atm1_key]]
            atm2_xyz = xyzs[geo_idx_dct[atm2_key]]

            rot_axis = numpy.subtract(atm2_xyz, atm1_xyz)

            rot_atm_keys = atom_keys(
                branch(gra, atm1_key, {atm1_key, atm2_key}))

            rot_idxs = list(map(geo_idx_dct.__getitem__, rot_atm_keys))

            geo = automol.geom.rotate(geo,
                                      rot_axis,
                                      numpy.pi,
                                      orig_xyz=atm1_xyz,
                                      idxs=rot_idxs)

        assert _bond_stereo_parity_from_geometry(gra, bnd_key, geo,
                                                 geo_idx_dct) == par
        gra = set_bond_stereo_parities(gra, {bnd_key: par})

    return geo, gra
예제 #3
0
def set_stereo_from_geometry(gra, geo, geo_idx_dct=None):
    """ set graph stereo from a geometry

    (coordinate distances need not match connectivity -- what matters is the
    relative positions at stereo sites)
    """
    gra = without_stereo_parities(gra)
    last_gra = None

    atm_keys = sorted(atom_keys(gra))
    geo_idx_dct = (geo_idx_dct if geo_idx_dct is not None else
                   {atm_key: idx
                    for idx, atm_key in enumerate(atm_keys)})

    # set atom and bond stereo, iterating to self-consistency
    atm_keys = set()
    bnd_keys = set()
    while last_gra != gra:
        last_gra = gra
        atm_keys.update(stereogenic_atom_keys(gra))
        bnd_keys.update(stereogenic_bond_keys(gra))
        gra = _set_atom_stereo_from_geometry(gra, atm_keys, geo, geo_idx_dct)
        gra = _set_bond_stereo_from_geometry(gra, bnd_keys, geo, geo_idx_dct)

    return gra
예제 #4
0
def _atom_stereo_corrected_geometry(gra, atm_ste_par_dct, geo, geo_idx_dct):
    """ correct the atom stereo parities of a geometry, for a subset of atoms
    """
    ring_atm_keys = set(itertools.chain(*rings_atom_keys(gra)))
    atm_ngb_keys_dct = atom_neighbor_keys(gra)

    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_geometry(gra, atm_key, geo,
                                                     geo_idx_dct)

        if curr_par != par:
            atm_ngb_keys = atm_ngb_keys_dct[atm_key]
            # 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

            # get coordinates
            xyzs = automol.geom.coordinates(geo)
            atm_xyz = xyzs[geo_idx_dct[atm_key]]
            atm3_xyz = xyzs[geo_idx_dct[atm3_key]]
            atm4_xyz = xyzs[geo_idx_dct[atm4_key]]

            # do the rotation
            rot_axis = cart.vec.unit_bisector(atm3_xyz,
                                              atm4_xyz,
                                              orig_xyz=atm_xyz)

            rot_atm_keys = (
                atom_keys(branch(gra, atm_key, {atm_key, atm3_key}))
                | atom_keys(branch(gra, atm_key, {atm_key, atm4_key})))
            rot_idxs = list(map(geo_idx_dct.__getitem__, rot_atm_keys))

            geo = automol.geom.rotate(geo,
                                      rot_axis,
                                      numpy.pi,
                                      orig_xyz=atm_xyz,
                                      idxs=rot_idxs)

        assert _atom_stereo_parity_from_geometry(gra, atm_key, geo,
                                                 geo_idx_dct) == par
        gra = set_atom_stereo_parities(gra, {atm_key: par})

    return geo, gra
예제 #5
0
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
예제 #6
0
def _connected_heuristic_geometry(gra):
    """ stereo-specific coordinates for a connected molecular geometry
    """
    assert gra == explicit(gra)

    atm_keys = sorted(atom_keys(gra))

    zma, zma_key_dct = connected_heuristic_zmatrix(gra)

    geo = automol.zmatrix.geometry(zma)
    idxs = dict_.values_by_key(zma_key_dct, atm_keys)
    geo = automol.geom.from_subset(geo, idxs)
    geo_idx_dct = {atm_key: idx for idx, atm_key in enumerate(atm_keys)}

    return geo, geo_idx_dct
예제 #7
0
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
예제 #8
0
def prod_hydrogen_migration(gra):
    """ products of hydrogen migration
    """

    prod_gras = tuple()

    keys = atom_keys(gra)

    num_keys = len(keys)
    if num_keys > 2:
        rad_idxs = resonance_dominant_radical_atom_keys(gra)
        uni_h_idxs = chem_unique_atoms_of_type(gra, 'H')

        h_atm_key = max(keys) + 1

        for h_idx in uni_h_idxs:
            for rad_idx in rad_idxs:
                gra2 = remove_atoms(gra, [h_idx])
                gra2_h = add_atom_explicit_hydrogen_keys(
                    gra2, {rad_idx: [h_atm_key]})
                if not full_isomorphism(gra, gra2_h):
                    prod_gras += ((gra2_h, ), )

    return _unique_gras(prod_gras)
예제 #9
0
def elimination(rct_gras, prd_gras):
    """ find an elimination transformation

    Eliminations are identified by breaking two bonds from the reactant,
    forming three fragments. This will form one "central fragment" with two
    break sites and two "end fragments" with one break site each. If the
    central fragment plus the two end fragments, joined at their break sites,
    matches the products, this is an elimination 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) == 1 and len(prd_gras) == 2 and not is_triv:
        rct_gra, = rct_gras
        rct_bnd_keys = bond_keys(rct_gra)
        # Loop over pairs of bonds and break them. Then, if this forms three
        # fragments, join the two end fragments and compare the result to the
        # products.
        for brk_bnd_key1, brk_bnd_key2 in itertools.combinations(rct_bnd_keys,
                                                                 r=2):
            rct_gra_ = remove_bonds(rct_gra, [brk_bnd_key1, brk_bnd_key2])

            # Find the central fragment, which is the one connected to both
            # break sites. If there's a loop there may not be a central
            # fragment, in which case this function will return None.
            cent_frag_atm_keys = _central_fragment_atom_keys(
                rct_gra_, brk_bnd_key1, brk_bnd_key2)
            if cent_frag_atm_keys is not None:
                atm1_key, = brk_bnd_key1 - cent_frag_atm_keys
                atm2_key, = brk_bnd_key2 - cent_frag_atm_keys
                frm_bnd_key = frozenset({atm1_key, atm2_key})
                rct_gra_ = add_bonds(rct_gra_, [frm_bnd_key])

                prd_gra = union_from_sequence(prd_gras)
                atm_key_dct = full_isomorphism(rct_gra_, prd_gra)
                if atm_key_dct:
                    tra = trans.from_data(
                        rxn_class=par.REACTION_CLASS.ELIMINATION,
                        frm_bnd_keys=[frm_bnd_key],
                        brk_bnd_keys=[brk_bnd_key1, brk_bnd_key2])
                    tras.append(tra)

                    rct_idxs = (0, )

                    cent_prd_atm_keys = frozenset(
                        map(atm_key_dct.__getitem__, cent_frag_atm_keys))

                    if cent_prd_atm_keys <= atom_keys(prd_gras[0]):
                        prd_idxs = (0, 1)
                    else:
                        assert cent_prd_atm_keys <= atom_keys(prd_gras[1])
                        prd_idxs = (1, 0)

    tras = tuple(tras)
    return tras, rct_idxs, prd_idxs