示例#1
0
def _connected_inchi_with_graph_stereo(ich, gra, nums):
    """ For a connected inchi/graph, check if the inchi is missing stereo; If so,
    add stereo based on the graph.

    Currently only checks for missing bond stereo, since this is all we have
    seen so far, but could be generalized.

    :param ich: the inchi string
    :param gra: the graph
    :param nums: graph indices to backbone atoms in canonical inchi order
    :type nums: tuple[int]
    """
    # First, do a check to see if the InChI is missing bond stereo
    # relative to the graph.
    ich_ste_keys = automol.inchi.stereo_bonds(ich)
    our_ste_keys = bond_stereo_keys(gra)

    miss_ich_ste_keys = automol.inchi.unassigned_stereo_bonds(ich)

    if len(ich_ste_keys) > len(our_ste_keys):
        raise Exception("Our code is missing stereo bonds")

    if len(ich_ste_keys) < len(our_ste_keys) or miss_ich_ste_keys:
        # Convert to implicit graph and relabel based on InChI sort
        atm_key_dct = dict(map(reversed, enumerate(nums)))
        gra = relabel(gra, atm_key_dct)
        gra = explicit(gra)
        exp_h_keys = explicit_hydrogen_keys(gra)
        exp_h_key_dct = {k: -k for k in exp_h_keys}
        gra = relabel(gra, exp_h_key_dct)

        # Translate internal stereo parities into InChI stereo parities
        # and generate the appropriate b-layer string for the InChI
        ste_dct = bond_stereo_parities(gra)
        ste_keys = tuple(
            sorted(tuple(reversed(sorted(k))) for k in bond_stereo_keys(gra)))
        blyr_strs = []
        for atm1_key, atm2_key in ste_keys:
            our_par = ste_dct[frozenset({atm1_key, atm2_key})]
            our_srt1, our_srt2 = bond_stereo_sorted_neighbor_atom_keys(
                gra, atm1_key, atm2_key)
            ich_srt1 = tuple(reversed(sorted(our_srt1)))
            ich_srt2 = tuple(reversed(sorted(our_srt2)))
            if not ((our_srt1 != ich_srt1) ^ (our_srt2 != ich_srt2)):
                ich_par = our_par
            else:
                ich_par = not our_par

            blyr_strs.append(
                f"{atm1_key+1}-{atm2_key+1}{'-' if ich_par else '+'}")

        # After forming the b-layer string, generate the new InChI
        blyr_str = ','.join(blyr_strs)
        ste_dct = {'b': blyr_str}
        # print(ste_dct)
        ich = automol.inchi.standard_form(ich, ste_dct=ste_dct)
        # print('out:', ich)

    return ich
示例#2
0
def path_distance_bounds_(gra):
    """ upper distance bound between two ends of a path

    :param gra: molecular graph
    :param path: the shortest path between two atoms
    :type path: list or tuple
    """

    hyb_dct = resonance_dominant_atom_hybridizations(gra)
    rng_keys_lst = rings_atom_keys(gra)
    atm_ngb_keys = atoms_neighbor_atom_keys(gra)

    ste_bnd_keys = bond_stereo_keys(gra)
    bnd_par_dct = bond_stereo_parities(gra)

    def _distance_bounds(path):
        # if the path is 0, the atoms are disconnected and could be arbitrarily
        # far apart
        rsz = shared_ring_size(path, rng_keys_lst)
        if len(path) == 1:
            ldist = udist = 0
        elif len(path) == 2:
            ldist = udist = heuristic_bond_distance(gra, *path)
        elif len(path) == 3:
            if rsz == 0:
                ldist = udist = heuristic_bond_angle_distance(gra,
                                                              *path,
                                                              hyb_dct=hyb_dct)
            else:
                a123 = (rsz - 2.) * 180. / rsz
                la123 = a123 - 10.
                ua123 = a123 + 10.
                lrdist = heuristic_bond_angle_distance(gra, *path, a123=la123)
                urdist = heuristic_bond_angle_distance(gra, *path, a123=ua123)
                odist = heuristic_bond_angle_distance(gra,
                                                      *path,
                                                      hyb_dct=hyb_dct)
                ldist = min(lrdist, odist)
                udist = max(urdist, odist)
        elif len(path) == 4:
            if rsz == 0:
                key2, key3 = path[1:3]
                bnd_key23 = frozenset({key2, key3})
                # handle bond stereo here
                if bnd_key23 in ste_bnd_keys:
                    key2_ngbs = atom_stereo_sorted_neighbor_atom_keys(
                        gra, key2, atm_ngb_keys[key2] - {key3})
                    key3_ngbs = atom_stereo_sorted_neighbor_atom_keys(
                        gra, key3, atm_ngb_keys[key3] - {key2})
                    pos2 = key2_ngbs.index(path[0])
                    pos3 = key3_ngbs.index(path[-1])
                    cis = bnd_par_dct[bnd_key23] != (pos2 != pos3)
                    dih = 0. if cis else 180.
                    ldist = udist = heuristic_torsion_angle_distance(
                        gra, *path, d1234=dih, degree=True, hyb_dct=hyb_dct)
                else:
                    ldist = heuristic_torsion_angle_distance(gra,
                                                             *path,
                                                             d1234=0.,
                                                             degree=True,
                                                             hyb_dct=hyb_dct)
                    udist = heuristic_torsion_angle_distance(gra,
                                                             *path,
                                                             d1234=180.,
                                                             degree=True,
                                                             hyb_dct=hyb_dct)
            else:
                ang = (rsz - 2.) * 180. / rsz
                rdist = heuristic_torsion_angle_distance(gra,
                                                         *path,
                                                         d1234=0.,
                                                         degree=True,
                                                         a123=ang,
                                                         a234=ang)
                cdist = heuristic_torsion_angle_distance(gra,
                                                         *path,
                                                         d1234=0.,
                                                         degree=True,
                                                         hyb_dct=hyb_dct)
                tdist = heuristic_torsion_angle_distance(gra,
                                                         *path,
                                                         d1234=180.,
                                                         degree=True,
                                                         hyb_dct=hyb_dct)
                ldist = min(rdist, cdist)
                udist = max(rdist, tdist)
        # otherwise, just do the sum of the distances between atoms along the
        # path
        else:
            # we can't handle disconnected points, because in that case the
            # path is [] and there is no way to recover the keys
            assert len(path) > 2
            ldist = closest_approach(gra, path[0], path[-1])
            udist = 999.

        return ldist, udist

    return _distance_bounds
示例#3
0
def qualitative_convergence_checker_(gra,
                                     keys,
                                     rqq_bond_max=1.8,
                                     rqh_bond_max=1.3,
                                     rhh_bond_max=1.1,
                                     bond_nobond_diff=0.3):
    """ a convergence checker for error minimization, checking that the
    geometry is qualitatively correct (correct connectivity and stereo)
    """
    symb_dct = atom_symbols(gra)
    pairs = set(map(frozenset, itertools.combinations(keys, 2)))

    bnd_keys = pairs & bond_keys(gra)
    nob_keys = pairs - bond_keys(gra)

    nob_symbs = tuple(
        tuple(map(symb_dct.__getitem__, nob_key)) for nob_key in nob_keys)
    bnd_symbs = tuple(
        tuple(map(symb_dct.__getitem__, bnd_key)) for bnd_key in bnd_keys)
    nob_idxs = tuple(tuple(map(keys.index, nob_key)) for nob_key in nob_keys)
    bnd_idxs = tuple(tuple(map(keys.index, bnd_key)) for bnd_key in bnd_keys)

    bnd_udists = tuple(
        (rqq_bond_max if 'H' not in symb else rhh_bond_max if set(symb) ==
         {'H'} else rqh_bond_max) for symb in bnd_symbs)

    diff = bond_nobond_diff
    nob_ldists = tuple(
        (rqq_bond_max + diff if 'H' not in symb else rhh_bond_max +
         diff if set(symb) == {'H'} else rqh_bond_max + diff)
        for symb in nob_symbs)

    bnd_idxs += tuple(map(tuple, map(reversed, bnd_idxs)))
    bnd_idx_vecs = tuple(map(list, zip(*bnd_idxs)))
    bnd_udists *= 2

    nob_idxs += tuple(map(tuple, map(reversed, nob_idxs)))
    nob_idx_vecs = tuple(map(list, zip(*nob_idxs)))
    nob_ldists *= 2

    symbs = tuple(map(symb_dct.__getitem__, keys))
    geo_idx_dct = dict(map(reversed, enumerate(keys)))
    atm_ste_keys = atom_stereo_keys(gra) & set(keys)
    bnd_ste_keys = bond_stereo_keys(gra) & bnd_keys
    atm_ste_par_dct = atom_stereo_parities(gra)
    bnd_ste_par_dct = bond_stereo_parities(gra)

    def _is_converged(xmat, err, grad):
        assert err and numpy.any(grad)
        xyzs = xmat[:, :3]
        dmat = embed.distance_matrix_from_coordinates(xyzs)

        # check for correct connectivity
        connectivity_check = ((numpy.all(
            dmat[bnd_idx_vecs] < bnd_udists) if bnd_udists else True)
                              and (numpy.all(dmat[nob_idx_vecs] > nob_ldists)
                                   if nob_ldists else True))

        # check for correct stereo parities
        geo = automol.geom.base.from_data(symbs, xyzs, angstrom=True)
        atom_stereo_check = all((atom_stereo_parity_from_geometry(
            gra, atm_key, geo, geo_idx_dct) == atm_ste_par_dct[atm_key])
                                for atm_key in atm_ste_keys)

        bond_stereo_check = all((bond_stereo_parity_from_geometry(
            gra, bnd_key, geo, geo_idx_dct) == bnd_ste_par_dct[bnd_key])
                                for bnd_key in bnd_ste_keys)

        return connectivity_check and atom_stereo_check and bond_stereo_check

    return _is_converged