Пример #1
0
def _decompose_ring_system_atom_keys(rsy):
    """ decompose a ring system into a ring and a series of arcs
    """
    # sort from smallest to largest
    rngs_pool = sorted(
        rings(rsy), key=lambda x: atom_count(x, with_implicit=False))

    decomp = ()
    decomp_bnd_keys = set({})

    rng = rngs_pool.pop(0)
    bnd_keys = bond_keys(rng)
    atm_keys = _sorted_ring_atom_keys(bnd_keys)

    decomp += (atm_keys,)
    decomp_bnd_keys.update(bnd_keys)

    while rngs_pool:
        decomp_rsy = bond_induced_subgraph(rsy, decomp_bnd_keys)
        for idx, rng in enumerate(rngs_pool):
            arcs = ring_arc_complement_atom_keys(decomp_rsy, rng)
            if arcs:
                rngs_pool.pop(idx)
                decomp += arcs
                decomp_bnd_keys.update(bond_keys(rng))

    return decomp
Пример #2
0
def substitution(rct_gras, prd_gras):
    """ find an substitution transformation

    Substitutions are identified by breaking one bond in the reactants and one
    bond from the products and checking for isomorphism.
    """
    _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) == 2 and len(prd_gras) == 2 and not is_triv:
        rct_gra = union_from_sequence(rct_gras)
        prd_gra = union_from_sequence(prd_gras)

        rct_bnd_keys = bond_keys(rct_gra)
        prd_bnd_keys = bond_keys(prd_gra)
        for rct_bnd_key, prd_bnd_key in itertools.product(
                rct_bnd_keys, prd_bnd_keys):
            rct_gra_ = remove_bonds(rct_gra, [rct_bnd_key])
            prd_gra_ = remove_bonds(prd_gra, [prd_bnd_key])

            inv_atm_key_dct = full_isomorphism(prd_gra_, rct_gra_)
            if inv_atm_key_dct:
                brk_bnd_key = rct_bnd_key
                frm_bnd_key = frozenset(
                    map(inv_atm_key_dct.__getitem__, prd_bnd_key))

                tra = trans.from_data(
                    rxn_class=par.REACTION_CLASS.SUBSTITUTION,
                    frm_bnd_keys=[frm_bnd_key],
                    brk_bnd_keys=[brk_bnd_key])
                tras.append(tra)

                rct_idxs = _argsort_reactants(rct_gras)
                prd_idxs = _argsort_reactants(prd_gras)

    tras = tuple(set(tras))
    return tras, rct_idxs, prd_idxs
Пример #3
0
def rings_bond_keys(gra):
    """ bond keys for each ring in the graph (minimal basis)
    """
    bnd_keys = bond_keys(gra)

    def _ring_bond_keys(rng_atm_keys):
        return frozenset(filter(lambda x: x <= rng_atm_keys, bnd_keys))

    nxg = _networkx.from_graph(gra)
    rng_atm_keys_lst = _networkx.minimum_cycle_basis(nxg)
    rng_bnd_keys_lst = frozenset(map(_ring_bond_keys, rng_atm_keys_lst))
    return rng_bnd_keys_lst
Пример #4
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
Пример #5
0
def torsion_leading_atom(zma, key1, key2, zgra=None):
    """ Obtain the leading atom for a torsion coordinate about a torsion axis.

        The leading atom is the atom whose dihedral defines the torsional
        coordinate, which must always be the first dihedral coordinate
        for this bond.

        A bond is properly decoupled if all other dihedrals along this
        bond depend on the leading atom.

        :param zma: the z-matrix
        :type zma: automol Z-Matrix data structure
        :param key1: the first key in the torsion axis (rotational bond)
        :type key1: int
        :param key2: the second key in the torsion axis (rotational bond)
        :type key2: int
        :param gra: an automol graph data structure, aligned to the z-matrix;
            used to check connectivity when necessary
        :rtype: int
    """

    key_mat = key_matrix(zma)
    krs1 = [(key, row) for key, row in enumerate(key_mat)
            if row[:2] == (key1, key2)]
    krs2 = [(key, row) for key, row in enumerate(key_mat)
            if row[:2] == (key2, key1)]

    lead_key_candidates = []

    for krs in (krs1, krs2):
        if krs:
            keys, rows = zip(*krs)
            start_key = keys[0]
            assert all(row[-1] == start_key for row in rows[1:]), (
                "Torsion coordinate along bond {:d}-{:d} not decoupled:\n{}".
                format(key1, key2, string(zma, one_indexed=False)))
            if rows[0][-1] is not None:
                lead_key_candidates.append(start_key)

    if not lead_key_candidates:
        lead_key = None
    elif len(lead_key_candidates) == 1:
        lead_key = lead_key_candidates[0]
    else:
        # If we get to this point, then the z-matrix includes dihedrals across
        # the key1-key2 bond in both directions and we have to choose which
        # dihedral to use. This mans there will be two lead_key_candidates.
        zgra = automol.convert.zmat.graph(zma) if zgra is None else zgra

        # Let key0 be the lead key and let (key1, key2, key3) be its key row in
        # the z-matrix. For the torsion coordinate, key0-key1-key2-key3 should
        # all be connected in a line. For subsidiary dihedral coordinates, key3
        # will be connected to key1 instead of key2.
        # A simple solution is therefore to choose the lead key based on
        # whether or not key2 and key3 are connected, which is what this code
        # does.
        bnd_keys = bond_keys(zgra)
        lead_key = next((k for k in lead_key_candidates
                         if frozenset(key_mat[k][-2:]) in bnd_keys), None)

        # If that fails, choose the key that appears earlier. It's possible
        # that it would be better to choose the later one, in which case we
        # would replace the min() here with a max().
        if lead_key is None:
            lead_key = min(lead_key_candidates)

    return lead_key
Пример #6
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