Source code for pythia.bonds

"""
This module computes relatively simple descriptors based on
nearest-neighbor bonds, with few additional transformations.
"""

import numpy as np
import freud

from .internal import cite


def _nlist_nn_helper(fbox, positions, neighbors, rmax_guess, exclude_ii=True):
    if isinstance(neighbors, int):
        aq = freud.AABBQuery(fbox, positions)
        result = aq.query(positions, {'num_neighbors': neighbors,
                                      'r_guess': rmax_guess,
                                      'exclude_ii': exclude_ii})
        neighbors = result.toNeighborList(sort_by_distance=True)

    return neighbors


[docs]@cite('freud2016') def normalized_radial_distance(box, positions, neighbors, rmax_guess=2.): """Returns the ratio of the euclidean distance of each near-neighbor to that of the nearest neighbor for each particle. """ fbox = freud.box.Box.from_box(box) nlist = _nlist_nn_helper(fbox, positions, neighbors, rmax_guess, True) rijs = positions[nlist.point_indices] - positions[nlist.query_point_indices] rijs = fbox.wrap(rijs) rs = np.linalg.norm(rijs, axis=-1) reference_rs = rs[nlist.segments] normalization = np.repeat(reference_rs, nlist.neighbor_counts) rs /= normalization # skip the shortest bond since that gets normalized down to 1 return rs.reshape((positions.shape[0], -1))[:, 1:]
def _get_neighborhood_distance_matrix(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise distances between `r_j - r_i` and `r_k - r_i` for all neighbors j and k of each particle i. """ fbox = freud.box.Box.from_box(box) nlist = _nlist_nn_helper(fbox, positions, neighbors, rmax_guess, True) neighbor_indices = nlist.point_indices.reshape((positions.shape[0], -1)) # (Np, Nn, Nn, 3) distance matrix rijs = positions[neighbor_indices[:, :, np.newaxis]] - \ positions[neighbor_indices[:, np.newaxis, :]] rijs = fbox.wrap(rijs.reshape((-1, 3))).reshape( (len(positions), neighbors, neighbors, 3)) # (Np, Nn, Nn) distance matrix rs = np.linalg.norm(rijs, axis=-1) # (0, 0) should be ri - ri == 0; (1, 0) should be the actual # nearest neighbor distance normalization = rs[:, 1, 0] rs /= normalization[:, np.newaxis, np.newaxis] return rs
[docs]@cite('freud2016') def neighborhood_distance_singvals(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise distances filled with `|r_k - r_j|` for all neighbors j and k(==j) of each particle i. Returns the singular values of this matrix to fix permutation invariance. """ rs = _get_neighborhood_distance_matrix(box, positions, neighbors, rmax_guess) svals = np.linalg.svd(rs, compute_uv=False) return svals
[docs]@cite('freud2016') def neighborhood_range_distance_singvals(box, positions, neigh_min, neigh_max, rmax_guess=2.): """Construct a matrix of pairwise distances filled with `|r_k - r_j|` for all neighbors j and k(==j) of each particle i, for a range of neighborhood sizes from neigh_min to neigh_max (inclusive). Returns the singular values of this matrix to fix permutation invariance. """ result = [] for neighbors in range(neigh_min, neigh_max + 1): result.append(neighborhood_distance_singvals(box, positions, neighbors, rmax_guess)) return np.hstack(result)
[docs]@cite('freud2016') def neighborhood_distance_sorted(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise distances filled with `|r_k - r_j|` for all neighbors j and k(==j) of each particle i. Returns the sorted contents of this matrix to fix permutation invariance. """ rs = _get_neighborhood_distance_matrix(box, positions, neighbors, rmax_guess) rs = rs.reshape((rs.shape[0], -1)) np.sort(rs) return rs
def _get_neighborhood_angle_matrix(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise angles between `r_j - r_i` and `r_k - r_i` for all neighbors j and k of each particle i. """ fbox = freud.box.Box.from_box(box) nlist = _nlist_nn_helper(fbox, positions, neighbors, rmax_guess, True) neighbor_indices = nlist.point_indices.reshape((positions.shape[0], -1)) # (Np, Nn, 3) distance matrix rijs = positions[neighbor_indices] - positions[:, np.newaxis, :] rijs = fbox.wrap(rijs.reshape((-1, 3))).reshape( (len(positions), neighbors, 3)) # (Np, Nn) distances rs = np.linalg.norm(rijs, axis=-1) rijs /= rs[:, :, np.newaxis] # (Np, Nn, Nn) dot products of distances dots = np.sum(rijs[:, :, np.newaxis]*rijs[:, np.newaxis, :], axis=-1) dots = np.clip(dots, -1, 1) thetas = np.arccos(dots) thetas[np.isnan(thetas)] = 0 return thetas
[docs]@cite('freud2016') def neighborhood_angle_singvals(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise angles between `(rk - ri)` and `(rj - ri)` for all neighbors j and k(==j) of each particle i, for a particular number of neighbors. Returns the singular values of this matrix to fix permutation invariance. """ thetas = _get_neighborhood_angle_matrix(box, positions, neighbors, rmax_guess=2.) svals = np.linalg.svd(thetas, compute_uv=False) return svals
[docs]@cite('freud2016') def neighborhood_range_angle_singvals(box, positions, neigh_min, neigh_max, rmax_guess=2.): """Construct a matrix of pairwise angles between `(rk - ri)` and `(rj - ri)` for all neighbors j and k(==j) of each particle i, for a range of neighborhood sizes from neigh_min to neigh_max (inclusive). Returns the singular values of this matrix to fix permutation invariance. """ result = [] for neighbors in range(neigh_min, neigh_max + 1): result.append(neighborhood_angle_singvals(box, positions, neighbors, rmax_guess)) return np.hstack(result)
[docs]@cite('freud2016') def neighborhood_angle_sorted(box, positions, neighbors, rmax_guess=2.): """Construct a matrix of pairwise angles between `(rk - ri)` and `(rj - ri)` for all neighbors j and k(==j) of each particle i, for a particular number of neighbors. Returns the sorted values of this matrix to fix permutation invariance. """ thetas = _get_neighborhood_angle_matrix(box, positions, neighbors, rmax_guess=2.) thetas = thetas.reshape((thetas.shape[0], -1)) np.sort(thetas) return thetas