Source code for prolint2.computers.distances

r""":mod:`prolint2.computers.distances`
==========================================================
:Authors: Daniel P. Ramirez & Besian I. Sejdiu
:Year: 2022
:Copyright: MIT License
"""

import numpy as np

from MDAnalysis.analysis import distances
from MDAnalysis.analysis.base import AnalysisBase


[docs]class SerialDistances(AnalysisBase): r""" Class to get the distance-based contacts starting from two AtomGroups using a *serial* approach. It inherits from the MDAnalysis AnalysisBase class. :param universe: The MDAnalysis Universe. :type universe: MDAnalysis.Universe :param query: AtomGroup to query. :type query: MDAnalysis.AtomGroup :param database: AtomGroup to use as the database. :type database: MDAnalysis.AtomGroup :param lipid_id: Residue ID for the lipid group. :type lipid_id: int :param residue_id: Residue ID for the query group. :type residue_id: int :param frame_filter: List of frame indices to analyze. :type frame_filter: list :param kwargs: Additional keyword arguments. :type kwargs: dict """ def __init__( self, universe, query, database, lipid_id, residue_id, frame_filter, **kwargs ): super().__init__(universe.universe.trajectory, **kwargs) self.query = query self.database = database self.frame_filter = frame_filter frame_range = np.arange(len(self.frame_filter)) self.frame_mapping = {k: v for k, v in zip(self.frame_filter, frame_range)} self.lipid_atomgroup = self.database.select_atoms(f"resid {lipid_id}") self.resid_atomgroup = self.query.select_atoms(f"resid {residue_id}") self.lipid_atomnames = self.lipid_atomgroup.names.tolist() self.resid_atomnames = self.resid_atomgroup.names.tolist() self.result_array = None self.distance_array = None # Raise if selection doesn't exist if len(self.query) == 0 or len(self.database) == 0: raise ValueError("Invalid selection. Empty AtomGroup(s).") def _prepare(self): """ Prepare data structures for storing distance calculations. This method initializes the result_array to store distance results. :return: None """ self.result_array = np.zeros( ( len(self.frame_filter), self.lipid_atomgroup.n_atoms, self.resid_atomgroup.n_atoms, ) ) def _single_frame(self): """ Perform distance calculations for a single frame. This method calculates distances between atoms of lipid and residue groups for the current frame and stores the results in result_array. :return: None """ if self._frame_index in self.frame_filter: r = distances.distance_array( self.lipid_atomgroup.positions, self.resid_atomgroup.positions, box=self.database.universe.dimensions, ) # print ('frame iterator: ', self._frame_index) self.result_array[self.frame_mapping[self._frame_index]] = r def _conclude(self): """ Conclude the distance analysis and compute the mean distance array. This method calculates the mean distance array based on the results stored in result_array and deletes the result_array to free up memory. :return: None """ self.distance_array = np.mean(self.result_array, axis=0) del self.result_array
[docs]class TwoPointDistances(AnalysisBase): """ Initialize the TwoPointDistances analysis. :param universe: The molecular dynamics universe. :type universe: MDAnalysis.Universe :param query: The query object for comparison. :type query: MDAnalysis.AtomGroup :param database: The database object for comparison. :type database: MDAnalysis.AtomGroup :param lipid_id: The ID of the lipid to consider. :type lipid_id: int :param residue_id: The ID of the residue to consider. :type residue_id: int :param lipid_sel: The optional selection for the lipid atom. :type lipid_sel: str :param residue_sel: The optional selection for the residue atom. :type residue_sel: str :param unit: The unit for calculating distances ("frame" or "time"). :type unit: str :param **kwargs: Additional keyword arguments. :type kwargs: dict This method sets up the analysis and initializes necessary attributes. """ def __init__( self, universe, query, database, lipid_id, residue_id, lipid_sel=None, residue_sel=None, unit="frame", **kwargs, ): super().__init__(universe.universe.trajectory, **kwargs) self.query = query self.database = database self.unit = unit if lipid_sel is not None and residue_sel is not None: self.pointA = self.database.select_atoms( f"resid {lipid_id} and name {lipid_sel}" ) self.pointB = self.query.select_atoms( f"resid {residue_id} and name {residue_sel}" ) elif lipid_sel is None and residue_sel is None: self.pointA = self.database.select_atoms( f"resid {lipid_id}" ).center_of_mass(compound="residues") self.pointB = self.query.select_atoms(f"resid {residue_id}").center_of_mass( compound="residues" ) elif lipid_sel is None: self.pointA = self.database.select_atoms( f"resid {lipid_id}" ).center_of_mass(compound="residues") self.pointB = self.query.select_atoms( f"resid {residue_id} and name {residue_sel}" ) elif residue_sel is None: self.pointA = self.database.select_atoms( f"resid {lipid_id} and name {lipid_sel}" ) self.pointB = self.query.select_atoms(f"resid {residue_id}").center_of_mass( compound="residues" ) self.result_array = None self.time_array = None def _prepare(self): """ Prepare the analysis. This method is called before the analysis starts and prepares the result array. """ self.result_array = np.zeros(self.database.universe.trajectory.n_frames) def _single_frame(self): """ Calculate distances for a single frame. This method is called for each frame in the trajectory to calculate distances. """ if isinstance(self.pointA, np.ndarray) and isinstance(self.pointB, np.ndarray): dist = distances.distance_array( self.pointA, self.pointB, box=self.database.universe.dimensions, ) elif isinstance(self.pointA, np.ndarray): dist = distances.distance_array( self.pointA, self.pointB.positions, box=self.database.universe.dimensions, ) elif isinstance(self.pointB, np.ndarray): dist = distances.distance_array( self.pointA.positions, self.pointB, box=self.database.universe.dimensions, ) else: dist = distances.distance_array( self.pointA.positions, self.pointB.positions, box=self.database.universe.dimensions, ) self.result_array[self._frame_index] = float(dist) def _conclude(self): """ Conclude the analysis. This method is called after the analysis is complete and sets the time_array attribute. """ if self.unit == "frame": self.time_array = self.frames elif self.unit == "time": self.time_array = self.times