예제 #1
0
파일: _prop.py 프로젝트: sjklipp/autochem
def permutation(geo, ref_geo, thresh=1e-4):
    """ Determine the permutation of one geometry that reproduces another
        (if there isn't one -- the geometries are not aligned, return None).

        :param geo: molecular geometry
        :type geo: automol molecular geometry data structure
        :param ref_geo: molecular geometry
        :type ref_geo: automol molecular geometry data structure
        :param thresh: theshold for assessing if permutation exists
        :type thresh: float
        :rtype: tuple(int)
    """

    natms = geom_base.count(geo)
    symbs = geom_base.symbols(geo)
    xyzs = geom_base.coordinates(geo)

    perm_idxs = [None] * natms
    for idx, (symb, xyz) in enumerate(zip(symbs, xyzs)):
        # Loop over atoms in the reference geometry with the same symbol
        ref_idxs = geom_base.atom_indices(ref_geo, symb=symb)
        ref_xyzs = geom_base.coordinates(ref_geo, idxs=ref_idxs)
        perm_idx = next(
            (ref_idx for ref_idx, ref_xyz in zip(ref_idxs, ref_xyzs)
             if util.vec.distance(xyz, ref_xyz) < thresh), None)
        perm_idxs[idx] = perm_idx

    perm_idxs = tuple(perm_idxs)

    if any(perm_idx is None for perm_idx in perm_idxs):
        perm_idxs = None

    return perm_idxs
예제 #2
0
def transform(geo, func, idxs=None):
    """ Transform the coordinates of a geometry by a function.
        A set of `idxs` can be supplied to transform a subset of coordinates.

        :param geo: molecular geometry
        :type geo: automol geometry data structure
        :param func: transformation function
        :type func: function object
        :param idxs: indices representing the subset of atoms
        :type idxs: tuple(int)
    """

    idxs = list(range(geom_base.count(geo))) if idxs is None else idxs
    symbs = geom_base.symbols(geo)
    xyzs = geom_base.coordinates(geo)
    xyzs = [func(xyz) if idx in idxs else xyz for idx, xyz in enumerate(xyzs)]

    return automol.create.geom.from_data(symbs, xyzs)
예제 #3
0
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 = automol.convert.geom.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 = [geom_base.distance(geo, *p, angstrom=angstrom) for p in pairs]
        dist_dct.update(dict(zip(keys, dists)))

        shift += geom_base.count(geo)

    return dist_dct
예제 #4
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 - geom_base.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(automol.convert.geom.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 = geom_base.symbols(geo1)
    syms2 = geom_base.symbols(geo2)
    xyzs1 = geom_base.coordinates(geo1, angstrom=angstrom)
    xyzs2 = geom_base.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
        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 = automol.create.geom.from_data(syms, xyzs, angstrom=angstrom)
    return geo