Source code for waterEntropy.analysis.RAD

"""
These functions calculate coordination shells using RAD the relative
angular distance.
"""

import numpy as np

from waterEntropy.analysis.shells import ShellCollection
import waterEntropy.maths.trig as Trig


[docs] def get_RAD_neighbours(i_coords, sorted_indices, sorted_distances, system): # pylint: disable=too-many-locals r""" For a given set of atom coordinates, find its RAD shell from the distance sorted atom list, truncated to the closests 30 atoms. This function calculates coordination shells using RAD the relative angular distance, as defined first in DOI:10.1063/1.4961439 where united atoms (heavy atom + bonded Hydrogens) are defined as neighbours if they fulfil the following condition: .. math:: \Bigg(\frac{1}{r_{ij}}\Bigg)^2>\Bigg(\frac{1}{r_{ik}}\Bigg)^2 \cos \theta_{jik} For a given particle :math:`i`, neighbour :math:`j` is in its coordination shell if :math:`k` is not blocking particle :math:`j`. In this implementation of RAD, we enforce symmetry, whereby neighbouring particles must be in each others coordination shells. :param i_coords: xyz coordinates of atom :math:`i` :param sorted_indices: list of atom indices sorted from closest to furthest from atom :math:`i` :param sorted_distances: list of atom distances sorted from closest to furthest from atom :math:`i` :param system: mdanalysis instance of atoms in a frame """ # 1. truncate neighbour list to closest 25 united atoms range_limit = min(len(sorted_distances), 25) shell = [] count = -1 # 2. iterate through neighbours from closest to furthest for y in sorted_indices[:range_limit]: count += 1 y_idx = np.where(sorted_indices == y)[0][0] j = system.atoms.indices[y] j_coords = system.atoms.positions[y] rij = sorted_distances[y_idx] blocked = False # 3. iterate through neighbours than atom j and check if they block # it from atom i for z in sorted_indices[:count]: # only closer UAs can block z_idx = np.where(sorted_indices == z)[0][0] k_coords = system.atoms.positions[z] rik = sorted_distances[z_idx] # 4. find the angle jik costheta_jik = Trig.get_angle( j_coords, i_coords, k_coords, system.dimensions[:3] ) if np.isnan(costheta_jik): break # 5. check if k blocks j from i LHS = (1 / rij) ** 2 RHS = ((1 / rik) ** 2) * costheta_jik if LHS < RHS: blocked = True break # 6. if j is not blocked from i by k, then its in i's shell if blocked is False: shell.append(j) return shell
[docs] def get_RAD_shell( UA, system, shells: ShellCollection, sorted_indices=None, sorted_distances=None ): """ For a given united atom, find its RAD shell, returning the atom indices for the heavy atoms that are in its shell. :param UA: mdanalysis instance of a united atom in a frame :param system: mdanalysis instance of atoms in a frame :param shells: ShellCollection instance """ # 1. first check if a shell has already been found for this UA shell = shells.find_shell(UA.index) if not shell: # 2. get the nearest neighbours for the UA, sorted from closest to # furthest if sorted_indices is None: sorted_indices, sorted_distances = Trig.get_sorted_neighbours( UA.index, system ) # 3. now find the RAD shell of the UA shell_indices = get_RAD_neighbours( UA.position, sorted_indices, sorted_distances, system ) # 4. populate the class instance for RAD shells shells.add_data(UA.index, shell_indices) shell = shells.find_shell(UA.index) return shell