Source code for prolint2.computers.contacts

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

from collections import defaultdict

from MDAnalysis.lib.nsgrid import FastNS

from prolint2.computers.base import ContactComputerBase
from prolint2.utils.utils import fast_unique_comparison

import os
import configparser

# Getting the config file
config = configparser.ConfigParser(allow_no_value=True)
config.read(os.path.join(os.path.abspath(os.path.dirname(__file__)), "../config.ini"))
parameters_config = config["Parameters"]


[docs]class SerialContacts(ContactComputerBase): """ 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 object. :type universe: MDAnalysis.Universe :param query: AtomGroup representing the query. :type query: MDAnalysis.AtomGroup :param database: AtomGroup representing the database. :type database: MDAnalysis.AtomGroup :param cutoff: The distance cutoff (default from parameters_config). :type cutoff: float :param kwargs: Additional keyword arguments. :type kwargs: dict """ def __init__( self, universe, query, database, cutoff=float(parameters_config["cutoff"]), **kwargs ): """ Initialize the SerialContacts object. :param universe: The MDAnalysis Universe object. :param query: AtomGroup representing the query. :param database: AtomGroup representing the database. :param cutoff: The distance cutoff (default from parameters_config). :param kwargs: Additional keyword arguments. This method initializes the SerialContacts object. It sets up the query, database, and cutoff, and performs input validation. :raises ValueError: If the input selection is empty or the cutoff is not greater than 0. """ super().__init__(universe.universe.trajectory, **kwargs) self.query = query self.database = database self.cutoff = cutoff self.q_resids = self.query.resids self.db_resids = self.database.resids self.db_resnames = self.database.resnames self.contacts = None self.contact_frames = defaultdict(lambda: defaultdict(list)) self._validate_inputs() def _validate_inputs(self): """ Validate the inputs. This method validates the inputs by checking if the query and database selections are not empty and if the cutoff is greater than 0. :raises ValueError: If the input selection is empty or the cutoff is not greater than 0. """ # Raise if selection doesn't exist if len(self.query) == 0 or len(self.database) == 0: raise ValueError("Invalid selection. Empty AtomGroup(s).") if self.cutoff <= 0: raise ValueError("The cutoff must be greater than 0.") def _get_residue_lipid_info(self, pair): """ Get the residue and lipid information for a given pair. :param pair: A pair of indices. :return: A tuple containing the residue ID, lipid ID, and lipid name. :rtype: tuple """ residue_id = self.q_resids[pair[0]] lipid_id = self.db_resids[pair[1]] lipid_name = self.db_resnames[pair[1]] return residue_id, lipid_id, lipid_name def _compute_pairs(self): """ Compute the pairs of residues and lipids that are within the cutoff distance. :return: An array of pairs. :rtype: numpy.ndarray """ gridsearch = FastNS( self.cutoff, self.database.positions, box=self.database.dimensions, pbc=True ) result = gridsearch.search(self.query.positions) pairs = result.get_pairs() return pairs def _single_frame(self): """ Compute the contacts for a single frame. This method computes contacts for a single frame by iterating through pairs of residues and lipids within the cutoff distance. It updates the `contact_frames` attribute. """ pairs = self._compute_pairs() q_resid_indices = pairs[:, 0] db_resid_indices = pairs[:, 1] residue_ids = self.q_resids[q_resid_indices] lipid_ids = self.db_resids[db_resid_indices] lipid_names = self.db_resnames[db_resid_indices] residue_ids, lipid_ids, lipid_names = fast_unique_comparison( residue_ids, lipid_ids, lipid_names ) existing_pairs = set() for unique_data in zip(residue_ids, lipid_ids, lipid_names): residue_id, lipid_id, _ = unique_data if (residue_id, lipid_id) not in existing_pairs: existing_pairs.add((residue_id, lipid_id)) self.contact_frames[residue_id][lipid_id].append(self._frame_index)
# def _conclude(self): # contacts = ExactContacts(self.query.universe, self.contact_frames) # contacts.run() # self.contacts = contacts