Source code for schrodinger.application.prime.residue_interaction

"""
A Python interface to Prime residue interaction data

Copyright Schrodinger, LLC. All rights reserved.
"""

from schrodinger import structure
from schrodinger.infra import mm


[docs]class InteractionReader(object): """ Read the Prime residue interaction data from a structure :ivar _available_res: A set of residue names for which Prime interaction data has been read :vartype _available_res: set :ivar _energy: A dictionary of the interaction energy, stored as [energy type][res1][res2]. Note that this dictionary is not symmetric: [energy type][res1][res2] is stored but [energy type][res2][res1] is not. In `_buildEnergyDict` and `getEnergy`, res1 is arbitrarily assigned to be less than res2. :vartype _energy: dict """ BLOCK_NAME = "m_psp_residue_interaction_energies"
[docs] def __init__(self, struc): """ Read the Prime residue interaction data :param struc: The structure to read interaction data from :type struc: `schrodinger.structure.Structure` :raise ValueError: If the structure does not have any Prime residue interaction data """ # According to Ken Borelli, this copy is necessary due to an issue with # the m2io infrastructure struc = struc.copy() data_names, ninteract, data_handle = self._readEnergyMetaData(struc) available_res, energy = self._readEnergyData(data_names, ninteract, data_handle) self._available_res = available_res self._energy = energy
[docs] @classmethod def hasData(cls, struc): """ Check if the specified structure contains interaction data :param struc: The structure to check :type struc: `schrodinger.structure.Structure` :return: True if the structure has interaction data. False otherwise. :rtype: bool """ struc = struc.copy() try: cls._openDataHandle(struc) except ValueError: return False else: return True
def _readEnergyMetaData(self, struc): """ Get metadata about the stored interaction data :param struc: The structure to read interaction data from :type struc: `schrodinger.structure.Structure` :return: A tuple of - The names of the stored data (i.e. column headers) (list) - The number of interactions (i.e. row count) (int) - An open m2io handle to the data (int) :rtype: tuple """ data_handle = self._openDataHandle(struc) mm.m2io_goto_block(data_handle, self.BLOCK_NAME, 1) ninteract = mm.m2io_get_index_dimension(data_handle) data_names = mm.m2io_unrequested_data_names(data_handle, mm.M2IO_ALL_TYPES) return data_names, ninteract, data_handle @classmethod def _openDataHandle(cls, struc): """ Open an m2io handle to the interaction data :param struc: The structure to read interaction data from :type struc: `schrodinger.structure.Structure` :return: An open m2io handle to the data :rtype: int :raise ValueError: If the structure does not have any Prime residue interaction data """ try: data_handle = mm.mmct_ct_m2io_get_unrequested_handle(struc) except mm.MmException: data_handle = mm.mmct_ct_get_or_open_additional_data(struc, True) num_block = mm.m2io_get_number_blocks(data_handle, cls.BLOCK_NAME) if num_block == 0: err = ("Structure %s does not have Prime residue interaction data" % struc.title) raise ValueError(err) return data_handle def _readEnergyData(self, data_names, ninteract, data_handle): """ Read in the interaction data :param data_names: The names of the stored data (i.e. column headers) :type data_names: list :param ninteract: The number of interactions (i.e. row count) :type ninteract: int :param data_handle: An open m2iohandle to the data :type data_handle: int :return: A tuple of (1) The residues for which data was read (set) (2) The interaction data (dict). See `InteractionReader._energy` documentation for an explanation of the data format. :rtype: tuple """ # The first two data names are for the residues, not energy types energy_names = ["r%s" % real_name[1:] for real_name in data_names[2:]] available_res = set() energy = {name: {} for name in energy_names} for i in range(1, ninteract + 1): output = mm.m2io_get_string_indexed(data_handle, i, data_names) res1 = output.pop(0).strip() res2 = output.pop(0).strip() available_res.update({res1, res2}) if res1 > res2: res1, res2 = res2, res1 output = list(map(float, output)) for cur_energy, cur_name in zip(output, energy_names): # Since the values are very sparse, don't bother to store zeroes if cur_energy == 0.0: continue energy[cur_name].setdefault(res1, {})[res2] = cur_energy return available_res, energy
[docs] def getEnergy(self, energy_type, res1, res2): """ Get the interaction energy between the specified residues :param energy_type: The name of the energy to retrieve, such as "r_psp_Prime_Energy" or "r_psp_Prime_Covalent" :type energy_type: str :param res1: The first residue or residue key (i.e. the string value returned by `resKey`) :type res1: `schrodinger.structure._Residue` or str :param res2: The second residue or residue key (i.e. the string value returned by `resKey`) :type res2: `schrodinger.structure._Residue` or str :return: The interaction energy between the specified residues :rtype: float :raise ValueError: If no data is found for the given energy type or residues """ if energy_type not in list(self._energy): err = "Energy type %s not found" % energy_type raise ValueError(err) res1_key = self._getResKey(res1) res2_key = self._getResKey(res2) res_err = "No interaction energies found for residue %s" if res1_key not in self._available_res: raise ValueError(res_err % res1_key) elif res2_key not in self._available_res: raise ValueError(res_err % res2_key) if res1_key > res2_key: res1_key, res2_key = res2_key, res1_key try: return self._energy[energy_type][res1_key][res2_key] except KeyError: return 0.0
def _getResKey(self, res): """ Get the Prime-formatted residue name for the specified residue :param res: The residue to get the name for or the residue key :type res: `schrodinger.structure._Residue` or str :return: The formatted name :rtype: str """ if isinstance(res, structure._Residue): return self.resKey(res) else: return res.strip()
[docs] @staticmethod def resKey(res): """ Get the Prime-formatted residue name for the specified residue :param res: The residue to get the name for :type res: `schrodinger.structure._Residue` :return: The formatted name :rtype: str """ chain = res.chain if chain == " ": chain = "_" resname = res.pdbres.strip() inscode = res.inscode.strip() return "%s:%s_%i%s" % (chain, resname, res.resnum, inscode)