예제 #1
0
파일: _conv.py 프로젝트: snelliott/automol
def linear_atoms(geo, gra=None, tol=5.):
    """ find linear atoms in a geometry (atoms with 180 degree bond angle)

        :param geo: the geometry
        :type geo: automol geometry data structure
        :param gra: the graph describing connectivity; if None, a connectivity
            graph will be generated using default distance thresholds
        :type gra: automol graph data structure
        :param tol: the tolerance threshold for linearity, in degrees
        :type tol: float
        :rtype: tuple(int)
    """

    gra = connectivity_graph(geo) if gra is None else gra
    ngb_idxs_dct = automol.graph.atoms_neighbor_atom_keys(gra)

    lin_idxs = []
    for idx in range(count(geo)):
        nidxs = ngb_idxs_dct[idx]
        if len(nidxs) >= 2:
            for nidx1, nidx2 in itertools.combinations(nidxs, 2):
                ang = central_angle(geo, nidx1, idx, nidx2, degree=True)
                if numpy.abs(ang - 180.) < tol:
                    lin_idxs.append(idx)

    lin_idxs = tuple(lin_idxs)

    return lin_idxs
예제 #2
0
def hydrogen_bonded_idxs(geo, dist_thresh=5.3, angle_thresh=1.92, grxn=None):
    """ Compare bond lengths in structure to determine if there
        is a hydrogen bond.

        :param geo: geometry object
        :type geo: geo object (tuple of tuples)
        :param grxn: reaction object (geo indexing)
        :type grxn: automol.reac.Reaction object
        :param dist_thresh: cutoff value for hbond length (Bohr)
        :type dist_thresh: float
        :param angle_thresh: cutoff value for hbond angle (Radian)
        :type angle_thresh: float
        :rtype: tuple
    """
    # Initialize the hydrogen bond list to None
    hydrogen_bond = None
    if count(geo) > 1:
        # Get the forming/breaking bond idxs if possible
        if grxn is not None:
            frm_bnd_keys = automol.graph.ts.forming_bond_keys(
                grxn.forward_ts_graph)
            brk_bnd_keys = automol.graph.ts.breaking_bond_keys(
                grxn.forward_ts_graph)
            rxn_keys = set()
            for key in frm_bnd_keys:
                rxn_keys = rxn_keys | key
            for key in brk_bnd_keys:
                rxn_keys = rxn_keys | key
            rxn_h_idxs = tuple(rxn_keys)
        else:
            rxn_h_idxs = ()

        # Get all potential indices for HB interactions
        gra = graph(geo)
        dist_mat = distance_matrix(geo)
        adj_atm_dct = automol.graph.atoms_neighbor_atom_keys(gra)
        h_idxs = automol.graph.atom_keys(gra, sym='H')
        acceptor_idxs = list(
            automol.graph.resonance_dominant_radical_atom_keys(gra))
        acceptor_idxs.extend(list(automol.graph.atom_keys(gra, sym='O')))
        # Loop over indices, ignoring H-idxs in reacting bonds
        hb_idxs = tuple(idx for idx in h_idxs if idx not in rxn_h_idxs)
        for h_idx in hb_idxs:
            for acceptor_idx in acceptor_idxs:
                donor_idx = list(adj_atm_dct[h_idx])[0]
                if acceptor_idx in adj_atm_dct[donor_idx]:
                    continue
                if dist_mat[h_idx][acceptor_idx] < dist_thresh:
                    ang = central_angle(geo, donor_idx, h_idx, acceptor_idx)
                    if ang > angle_thresh:
                        hydrogen_bond = (
                            donor_idx,
                            h_idx,
                            acceptor_idx,
                        )
                        dist_thresh = dist_mat[h_idx][acceptor_idx]
    return hydrogen_bond
예제 #3
0
파일: ts.py 프로젝트: snelliott/automol
def angle_distances(geos, angstrom=True):
    """ return a dictionary of distances between ends of a bond angle for a
    sequence of reactant geometries, shifting the keys as needed

    :param geos: the reactant geometries
    :param angstrom: return the distances in angstroms?
    """
    dist_dct = {}

    shift = 0
    for geo in geos:
        gra = connectivity_graph(geo)
        pairs = [(k1, k3) for k1, _, k3 in automol.graph.angle_keys(gra)]
        keys = [frozenset({k1 + shift, k2 + shift}) for k1, k2 in pairs]
        dists = [distance(geo, *p, angstrom=angstrom) for p in pairs]
        dist_dct.update(dict(zip(keys, dists)))

        shift += count(geo)

    return dist_dct
예제 #4
0
def inchi_with_sort(geo, stereo=True):
    """ Generate an InChI string from a molecular geometry. (Sort?)

        :param geo: molecular geometry
        :type geo: automol geometry data structure
        :param stereo: parameter to include stereochemistry information
        :type stereo: bool
        :rtype: str
    """
    ich = automol.inchi.base.hardcoded_object_to_inchi_by_key(
        'geom', geo, comp=_compare)
    nums_lst = None
    if ich is None:
        gra = connectivity_graph(geo)
        if not stereo:
            geo = None
            geo_idx_dct = None
        else:
            geo_idx_dct = dict(enumerate(range(count(geo))))
        ich, nums_lst = automol.graph.inchi_with_sort_from_geometry(
            gra=gra, geo=geo, geo_idx_dct=geo_idx_dct)

    return ich, nums_lst
예제 #5
0
파일: ts.py 프로젝트: snelliott/automol
def join(geo1,
         geo2,
         key2,
         key3,
         r23,
         a123=85.,
         a234=85.,
         d1234=85.,
         key1=None,
         key4=None,
         angstrom=True,
         degree=True):
    """ join two geometries based on four of their atoms, two on the first
    and two on the second

    Variables set the coordinates for 1-2...3-4 where 1-2 are bonded atoms in
    geo1 and 3-4 are bonded atoms in geo2.
    """
    key3 = key3 - count(geo1)
    a123 *= phycon.DEG2RAD if degree else 1
    a234 *= phycon.DEG2RAD if degree else 1
    d1234 *= phycon.DEG2RAD if degree else 1

    gra1, gra2 = map(connectivity_graph, (geo1, geo2))
    key1 = (automol.graph.atom_neighbor_atom_key(gra1, key2)
            if key1 is None else key1)
    key4 = (automol.graph.atom_neighbor_atom_key(gra2, key3)
            if key4 is None else key4)

    syms1 = symbols(geo1)
    syms2 = symbols(geo2)
    xyzs1 = coordinates(geo1, angstrom=angstrom)
    xyzs2 = coordinates(geo2, angstrom=angstrom)

    xyz1 = xyzs1[key1]
    xyz2 = xyzs1[key2]
    orig_xyz3 = xyzs2[key3]
    if key4 is not None:
        orig_xyz4 = xyzs2[key4]
    else:
        orig_xyz4 = [1., 1., 1.]

    r34 = vec.distance(orig_xyz3, orig_xyz4)

    # Place xyz3 as far away from the atoms in geo1 as possible by optimizing
    # the undetermined dihedral angle
    xyz0 = vec.arbitrary_unit_perpendicular(xyz2, orig_xyz=xyz1)

    def _distance_norm(dih):  # objective function for minimization
        dih, = dih
        xyz3 = vec.from_internals(dist=r23,
                                  xyz1=xyz2,
                                  ang=a123,
                                  xyz2=xyz1,
                                  dih=dih,
                                  xyz3=xyz0)
        dist_norm = numpy.linalg.norm(numpy.subtract(xyzs1, xyz3))
        # return the negative norm so that minimum value gives maximum distance
        return -dist_norm

    res = scipy.optimize.basinhopping(_distance_norm, 0.)
    dih = res.x[0]

    # Now, get the next position with the optimized dihedral angle
    xyz3 = vec.from_internals(dist=r23,
                              xyz1=xyz2,
                              ang=a123,
                              xyz2=xyz1,
                              dih=dih,
                              xyz3=xyz0)

    # Don't use the dihedral angle if 1-2-3 are linear
    if numpy.abs(a123 * phycon.RAD2DEG - 180.) > 5.:
        xyz4 = vec.from_internals(dist=r34,
                                  xyz1=xyz3,
                                  ang=a234,
                                  xyz2=xyz2,
                                  dih=d1234,
                                  xyz3=xyz1)
    else:
        xyz4 = vec.from_internals(dist=r34, xyz1=xyz3, ang=a234, xyz2=xyz2)

    align_ = vec.aligner(orig_xyz3, orig_xyz4, xyz3, xyz4)
    xyzs2 = tuple(map(align_, xyzs2))

    syms = syms1 + syms2
    xyzs = xyzs1 + xyzs2

    geo = from_data(syms, xyzs, angstrom=angstrom)
    return geo
예제 #6
0
파일: _conv.py 프로젝트: snelliott/automol
def insert_dummies_on_linear_atoms(geo,
                                   lin_idxs=None,
                                   gra=None,
                                   dist=1.,
                                   tol=5.):
    """ Insert dummy atoms over linear atoms in the geometry.

        :param geo: the geometry
        :type geo: automol molecular geometry data structure
        :param lin_idxs: the indices of the linear atoms; if None, indices are
            automatically determined from the geometry based on the graph
        :type lin_idxs: tuple(int)
        :param gra: the graph describing connectivity; if None, a connectivity
            graph will be generated using default distance thresholds
        :type gra: automol molecular graph data structure
        :param dist: distance of dummy atom from the linear atom, in angstroms
        :type dist: float
        :param tol: the tolerance threshold for linearity, in degrees
        :type tol: float
        :returns: geometry with dummy atoms inserted, along with a dictionary
            mapping the linear atoms onto their associated dummy atoms
        :rtype: automol molecular geometry data structure
    """

    lin_idxs = linear_atoms(geo) if lin_idxs is None else lin_idxs
    gra = connectivity_graph(geo) if gra is None else gra

    dummy_ngb_idxs = set(
        automol.graph.dummy_atoms_neighbor_atom_key(gra).values())
    assert not dummy_ngb_idxs & set(lin_idxs), (
        "Attempting to add dummy atoms on atoms that already have them: {}".
        format(dummy_ngb_idxs & set(lin_idxs)))

    ngb_idxs_dct = automol.graph.atoms_sorted_neighbor_atom_keys(gra)

    xyzs = coordinates(geo, angstrom=True)

    def _perpendicular_direction(idxs):
        """ find a nice perpendicular direction for a series of linear atoms
        """
        triplets = []
        for idx in idxs:
            for n1idx in ngb_idxs_dct[idx]:
                for n2idx in ngb_idxs_dct[n1idx]:
                    if n2idx != idx:
                        ang = central_angle(geo,
                                            idx,
                                            n1idx,
                                            n2idx,
                                            degree=True)
                        if numpy.abs(ang - 180.) > tol:
                            triplets.append((idx, n1idx, n2idx))

        if triplets:
            idx1, idx2, idx3 = min(triplets, key=lambda x: x[1:])
            xyz1, xyz2, xyz3 = map(xyzs.__getitem__, (idx1, idx2, idx3))
            r12 = util.vec.unit_direction(xyz1, xyz2)
            r23 = util.vec.unit_direction(xyz2, xyz3)
            direc = util.vec.orthogonalize(r12, r23, normalize=True)
        else:
            if len(idxs) > 1:
                idx1, idx2 = idxs[:2]
            else:
                idx1, = idxs
                idx2, = ngb_idxs_dct[idx1]

            xyz1, xyz2 = map(xyzs.__getitem__, (idx1, idx2))
            r12 = util.vec.unit_direction(xyz1, xyz2)
            for i in range(3):
                disp = numpy.zeros((3, ))
                disp[i] = -1.
                alt = numpy.add(r12, disp)
                direc = util.vec.unit_perpendicular(r12, alt)
                if numpy.linalg.norm(direc) > 1e-2:
                    break

        return direc

    # partition the linear atoms into adjacent groups, to be handled together
    lin_idxs_lst = sorted(
        map(
            sorted,
            util.equivalence_partition(lin_idxs,
                                       lambda x, y: x in ngb_idxs_dct[y])))

    dummy_key_dct = {}

    for idxs in lin_idxs_lst:
        direc = _perpendicular_direction(idxs)
        for idx in idxs:
            xyz = numpy.add(xyzs[idx], numpy.multiply(dist, direc))
            dummy_key_dct[idx] = count(geo)

            geo = insert(geo, 'X', xyz, angstrom=True)

    return geo, dummy_key_dct
예제 #7
0
def join(geo1,
         geo2,
         key2,
         key3,
         r23,
         a123=85.,
         a234=85.,
         d1234=85.,
         key1=None,
         key4=None,
         angstrom=True,
         degree=True):
    """ join two geometries based on four of their atoms, two on the first
    and two on the second

    Variables set the coordinates for 1-2...3-4 where 1-2 are bonded atoms in
    geo1 and 3-4 are bonded atoms in geo2.
    """
    key3 = key3 - count(geo1)
    a123 *= phycon.DEG2RAD if degree else 1
    a234 *= phycon.DEG2RAD if degree else 1
    d1234 *= phycon.DEG2RAD if degree else 1

    gra1, gra2 = map(connectivity_graph, (geo1, geo2))
    key1 = (automol.graph.atom_neighbor_atom_key(gra1, key2)
            if key1 is None else key1)
    key4 = (automol.graph.atom_neighbor_atom_key(gra2, key3)
            if key4 is None else key4)

    syms1 = symbols(geo1)
    syms2 = symbols(geo2)
    xyzs1 = coordinates(geo1, angstrom=angstrom)
    xyzs2 = coordinates(geo2, angstrom=angstrom)

    xyz1 = xyzs1[key1] if key1 is not None else None
    xyz2 = xyzs1[key2]
    orig_xyz3 = xyzs2[key3]
    orig_xyz4 = xyzs2[key4] if key4 is not None else [1., 1., 1.]

    if key1 is None:
        # If the first fragment is monatomic, we can place the other one
        # anywhere we want (direction doesn't matter)
        xyz3 = numpy.add(xyz2, [0., 0., r23])
    else:
        # If the first fragment isn't monatomic, we need to take some care in
        # where we place the second one.
        # r23 and a123 are fixed, so the only degree of freedom we have to do
        # this is to optimize a dihedral angle relative to an arbitrary point
        # xyz0.
        # This dihedral angle is optimized to maximize the distance of xyz3
        # from all of the atoms in fragment 1 (xyzs1).
        xyz1 = xyzs1[key1]

        # Place xyz3 as far away from the atoms in geo1 as possible by
        # optimizing the undetermined dihedral angle
        xyz0 = vec.arbitrary_unit_perpendicular(xyz2, orig_xyz=xyz1)

        def _distance_norm(dih):  # objective function for minimization
            dih, = dih
            xyz3 = vec.from_internals(dist=r23,
                                      xyz1=xyz2,
                                      ang=a123,
                                      xyz2=xyz1,
                                      dih=dih,
                                      xyz3=xyz0)
            dist_norm = numpy.linalg.norm(numpy.subtract(xyzs1, xyz3))
            # return the negative norm so that minimum value gives maximum
            # distance
            return -dist_norm

        res = scipy.optimize.basinhopping(_distance_norm, 0.)
        dih = res.x[0]

        # Now, get the next position with the optimized dihedral angle
        xyz3 = vec.from_internals(dist=r23,
                                  xyz1=xyz2,
                                  ang=a123,
                                  xyz2=xyz1,
                                  dih=dih,
                                  xyz3=xyz0)

    r34 = vec.distance(orig_xyz3, orig_xyz4)

    # If 2 doen't have neighbors or 1-2-3 are linear, ignore the dihedral angle
    if key1 is None or numpy.abs(a123 * phycon.RAD2DEG - 180.) < 5.:
        xyz4 = vec.from_internals(dist=r34, xyz1=xyz3, ang=a234, xyz2=xyz2)
    else:
        xyz4 = vec.from_internals(dist=r34,
                                  xyz1=xyz3,
                                  ang=a234,
                                  xyz2=xyz2,
                                  dih=d1234,
                                  xyz3=xyz1)

    align_ = vec.aligner(orig_xyz3, orig_xyz4, xyz3, xyz4)
    xyzs2 = tuple(map(align_, xyzs2))

    syms = syms1 + syms2
    xyzs = xyzs1 + xyzs2

    geo = from_data(syms, xyzs, angstrom=angstrom)
    return geo