Esempio n. 1
0
def heuristic_bond_angle(gra,
                         key1,
                         key2,
                         key3,
                         degree=False,
                         check=False,
                         hyb_dct=None):
    """ heuristic bond angle

    If being reused multiple times, you can speed this up by passing in the
    hybridizations, so they don't need to be recalculated
    """
    if check:
        assert {key1, key3} <= set(atoms_neighbor_atom_keys(gra)[key2])

    if hyb_dct is None:
        hyb_dct = resonance_dominant_atom_hybridizations(gra)

    hyb2 = hyb_dct[key2]
    if hyb2 == 3:
        ang = TET_ANG
    elif hyb2 == 2:
        ang = TRI_ANG
    else:
        assert hyb2 == 1
        ang = LIN_ANG

    ang *= 1 if degree else phycon.DEG2RAD

    return ang
Esempio n. 2
0
def _atoms_missing_neighbors(gra, zma_keys):
    """ get atoms from the list currently in the v-matrix with neighbors that
    are not in the v-matrix
    """
    ngb_keys_dct = atoms_neighbor_atom_keys(gra)
    keys = []
    for key in zma_keys:
        if any(k not in zma_keys for k in ngb_keys_dct[key]):
            keys.append(key)
    keys = tuple(keys)
    return keys
Esempio n. 3
0
def chirality_constraint_bounds(gra, keys):
    """ bounds for enforcing chirality restrictions
    """
    ste_keys = set(atom_stereo_keys(gra)) & set(keys)
    par_dct = atom_stereo_parities(gra)
    ngb_key_dct = atoms_neighbor_atom_keys(gra)

    def _chirality_constraint(key):
        ngb_keys = ngb_key_dct[key]
        ngb_keys = atom_stereo_sorted_neighbor_atom_keys(gra, key, ngb_keys)
        idxs = tuple(map(keys.index, ngb_keys))
        vol_range = (-999., -7.) if par_dct[key] else (+7., +999.)
        return idxs, vol_range

    chi_dct = dict(map(_chirality_constraint, ste_keys))
    return chi_dct
Esempio n. 4
0
def heuristic_bond_distance(gra, key1, key2, angstrom=True, check=False):
    """ heuristic bond distance (in angstroms)
    """
    if check:
        assert key1 in atoms_neighbor_atom_keys(gra)[key2]

    symb_dct = atom_symbols(gra)
    symb1 = symb_dct[key1]
    symb2 = symb_dct[key2]

    if ptab.to_number(symb1) == 1 or ptab.to_number(symb2) == 1:
        dist = XH_DIST
    else:
        dist = XY_DIST

    dist *= 1 if angstrom else phycon.ANG2BOHR

    return dist
Esempio n. 5
0
def planarity_constraint_bounds(gra, keys):
    """ bounds for enforcing planarity restrictions
    """
    ngb_key_dct = atoms_neighbor_atom_keys(gra)
    ngb_dct = bond_neighborhoods(gra)
    bnd_keys = [
        bnd_key for bnd_key in sp2_bond_keys(gra)
        if atom_keys(ngb_dct[bnd_key]) <= set(keys)
    ]

    def _planarity_constraints(bnd_key):
        key1, key2 = sorted(bnd_key)
        key1ab = sorted(ngb_key_dct[key1] - {key2})
        key2ab = sorted(ngb_key_dct[key2] - {key1})

        lst = []

        # I don't think the order of the keys matters, but I tried to be
        # roughly consistent with Figure 8 in the Blaney Dixon paper
        if len(key1ab) == 2 and len(key2ab) == 2:
            lst.append(tuple(map(keys.index, key1ab + key2ab)))
        if len(key1ab) == 2:
            lst.append(tuple(map(keys.index, [key1, key2] + key1ab)))
        if len(key2ab) == 2:
            lst.append(tuple(map(keys.index, [key1, key2] + key2ab)))
        if (len(key1ab) == 2 and len(key2ab) == 1) or (len(key1ab) == 1
                                                       and len(key2ab) == 2):
            lst.append(tuple(map(keys.index, [key1] + key1ab + key2ab)))
            lst.append(tuple(map(keys.index, [key2] + key1ab + key2ab)))
        if len(key1ab) == 1 and len(key2ab) == 1:
            lst.append(tuple(map(keys.index, [key1, key2] + key1ab + key2ab)))

        return tuple(lst)

    const_dct = {
        idxs: (-0.5, +0.5)
        for idxs in itertools.chain(*map(_planarity_constraints, bnd_keys))
    }

    return const_dct
Esempio n. 6
0
def _extend_chain_to_include_terminal_hydrogens(gra, keys,
                                                start=True, end=True):
    """ extend each end of a chain to include terminal hydrogens, if any
    """
    symb_dct = atom_symbols(gra)
    atm_ngb_dct = atoms_neighbor_atom_keys(gra)

    sta_ngbs = atm_ngb_dct[keys[0]] - {keys[1]}
    end_ngbs = atm_ngb_dct[keys[-1]] - {keys[-2]}

    sta_ngb = min((k for k in sta_ngbs if symb_dct[k] == 'H'), default=None)
    end_ngb = min((k for k in end_ngbs if symb_dct[k] == 'H'), default=None)

    keys = tuple(keys)

    if start and sta_ngb is not None:
        keys = (sta_ngb,) + keys

    if end and end_ngb is not None:
        keys = keys + (end_ngb,)

    return keys
Esempio n. 7
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