def distance_bounds_matrices(gra, keys, sp_dct=None): """ initial distance bounds matrices :param gra: molecular graph :param keys: atom keys specifying the order of indices in the matrix :param sp_dct: a 2d dictionary giving the shortest path between any pair of atoms in the graph """ assert set(keys) <= set(atom_keys(gra)) sub_gra = subgraph(gra, keys, stereo=True) sp_dct = atom_shortest_paths(sub_gra) if sp_dct is None else sp_dct bounds_ = path_distance_bounds_(gra) natms = len(keys) umat = numpy.zeros((natms, natms)) lmat = numpy.zeros((natms, natms)) for (idx1, key1), (idx2, key2) in itertools.combinations(enumerate(keys), 2): if key2 in sp_dct[key1]: path = sp_dct[key1][key2] ldist, udist = bounds_(path) lmat[idx1, idx2] = lmat[idx2, idx1] = ldist umat[idx1, idx2] = umat[idx2, idx1] = udist else: # they are disconnected lmat[idx1, idx2] = lmat[idx2, idx1] = closest_approach(gra, key1, key2) umat[idx1, idx2] = umat[idx2, idx1] = 999 assert lmat[idx1, idx2] <= umat[idx1, idx2], ( "Lower bound exceeds upper bound. This is a bug!\n" f"{string(gra, one_indexed=False)}\npath: {str(path)}\n") return lmat, umat
def ts_distance_bounds_matrices(gra, keys, frm_bnds_dct, rct_geos=None, relax_torsions=False, sp_dct=None): """ distance bounds matrices for a transition state :param gra: molecular graph :param keys: atom keys specifying the order of indices in the matrix :param frm_bnds_dct: bounds for bonds formed in the reaction; the keys are bond keys and the values are lower and upper distance bounds for those forming bonds :param rct_geos: reactant geometries; must follow the same order as the atoms in `keys :param relax_torsions: whether or not to allow torsions to change from their value in the reactant geometries :param sp_dct: a 2d dictionary giving the shortest path between any pair of atoms in the graph """ sp_dct = atom_shortest_paths(gra) if sp_dct is None else sp_dct natms = len(keys) lmat, umat = distance_bounds_matrices(gra, keys) # save the current values so that we can overwrite the fixed torsions below lmat_old = numpy.copy(lmat) umat_old = numpy.copy(umat) # 2. set known geometric parameters if rct_geos: xmats = [ automol.geom.base.coordinates(geo, angstrom=True) for geo in rct_geos ] dmats = list(map(embed.distance_matrix_from_coordinates, xmats)) start = 0 for dmat in dmats: dim, _ = numpy.shape(dmat) end = start + dim lmat[start:end, start:end] = dmat umat[start:end, start:end] = dmat start = end # 3. reopen bounds on the torsions from the reactant if relax_torsions: tors_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 4] tors_ijs += list(map(list, map(reversed, tors_ijs))) tors_idxs = tuple(map(list, zip(*tors_ijs))) lmat[tors_idxs] = lmat_old[tors_idxs] umat[tors_idxs] = umat_old[tors_idxs] # 1. set distance bounds for the forming bonds for bnd, (ldist, udist) in frm_bnds_dct.items(): idx1 = tuple(bnd) idx2 = tuple(reversed(idx1)) lmat[idx1] = lmat[idx2] = ldist umat[idx1] = umat[idx2] = udist return lmat, umat
def join_distance_bounds_matrices(gra, keys, dist_range_dct, geos=None, relax_angles=False, relax_torsions=False, sp_dct=None, angstrom=True): """ distance bounds matrices for joining multiple geometries :param gra: molecular graph: :param keys: atom keys specifying the order of indices in the matrix :param dist_range_dct: distance ranges for specific atoms in the graph :param geos: (optional) geometries which will be used to fix the bond angles, bond distances, and chiralities of all connected atoms in the graph; if `relax_torsions` is False, the 4-atom dihedral angles will be allowed to vary as well :param relax_torsions: whether or not to allow torsions to change from their value in the reactant geometries :param sp_dct: a 2d dictionary giving the shortest path between any pair of atoms in the graph """ sp_dct = atom_shortest_paths(gra) if sp_dct is None else sp_dct natms = len(keys) lmat, umat = distance_bounds_matrices(gra, keys) # save the current values so that we can overwrite the fixed torsions below lmat_old = numpy.copy(lmat) umat_old = numpy.copy(umat) # 1. set known geometric parameters if geos: xmats = [ automol.geom.base.coordinates(geo, angstrom=angstrom) for geo in geos ] dmats = list(map(embed.distance_matrix_from_coordinates, xmats)) start = 0 for dmat in dmats: dim, _ = numpy.shape(dmat) end = start + dim lmat[start:end, start:end] = dmat umat[start:end, start:end] = dmat start = end # 2. reopen bounds on the torsions from the reactant if relax_torsions: tors_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 4] tors_ijs += list(map(list, map(reversed, tors_ijs))) tors_idxs = tuple(map(list, zip(*tors_ijs))) lmat[tors_idxs] = lmat_old[tors_idxs] umat[tors_idxs] = umat_old[tors_idxs] # 3. reopen bounds on the angles from the reactant if relax_angles: ang_ijs = [[i, j] for i, j in itertools.combinations(range(natms), 2) if j in sp_dct[i] and len(sp_dct[i][j]) >= 3] ang_ijs += list(map(list, map(reversed, ang_ijs))) ang_idxs = tuple(map(list, zip(*ang_ijs))) lmat[ang_idxs] = lmat_old[ang_idxs] umat[ang_idxs] = umat_old[ang_idxs] # 4. set distance bounds for the forming bonds for bnd, (ldist, udist) in dist_range_dct.items(): idx1 = tuple(bnd) idx2 = tuple(reversed(idx1)) lmat[idx1] = lmat[idx2] = ldist umat[idx1] = umat[idx2] = udist return lmat, umat