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
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)
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
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