Accessing Interaction Hint Information

Problem

You want to perceive protein-ligand interaction hints and retrieve interaction information.

Ingredients

  • OEChem TK - cheminformatics toolkit (including OEBio TK)

Difficulty Level

../_images/chilly.png ../_images/chilly.png

Solution

This recipe discusses several examples that show how to perceive and access interaction information.

Hint

If you would like to learn about interaction API before diving into the examples, please see the Overview of OEChem TK Interaction API subsection first.

Perceiving Interaction Hints

The example below shows how to initialize an interaction container (OEInteractionHintContainer) with a ligand and a protein and then perceive interaction hints between them by calling the OEPerceiveInteractionHints function. If the container can not be initialized with the given molecules, the OEIsValidActiveSite function returns False.

def perceive_interation_hints(protein, ligand):
    """
    :type protein: oechem.OEMol
    :type ligand: oechem.OEMol
    :rtype: oechem.OEInteractionHintContainer
    """
    asite = oechem.OEInteractionHintContainer()
    asite.AddMolecule(protein, oechem.OEProteinInteractionHintComponent())
    asite.AddMolecule(ligand, oechem.OELigandInteractionHintComponent())
    if not oechem.OEIsValidActiveSite(asite):
        oechem.OEThrow.Fatal("Cannot initialize active site!")

    oechem.OEPerceiveInteractionHints(asite)

    return asite

See also

See Table 1 for list of interaction types that are currently perceived in OEChem TK.

The previous example shows how to perceive protein-ligand interaction hints using the default geometric constraints. These parameters can be customized by using the OEPerceiveInteractionOptions class. The example below illustrates how to reduce the default distance constraints used to determine Pi and T stacking by 0.5 Ångströms.

def perceive_interation_hints_user_def_params(protein, ligand):
    """
    :type protein: oechem.OEMol
    :type ligand: oechem.OEMol
    :rtype: oechem.OEInteractionHintContainer
    """
    asite = oechem.OEInteractionHintContainer()
    asite.AddMolecule(protein, oechem.OEProteinInteractionHintComponent())
    asite.AddMolecule(ligand, oechem.OELigandInteractionHintComponent())
    if not oechem.OEIsValidActiveSite(asite):
        oechem.OEThrow.Fatal("Cannot initialize active site!")

    opts = oechem.OEPerceiveInteractionOptions()
    opts.SetMaxPiStackDistance(opts.GetMaxPiStackDistance() - 0.5)
    opts.SetMaxTStackDistance(opts.GetMaxTStackDistance() - 0.5)
    oechem.OEPerceiveInteractionHints(asite, opts)

    return asite

Accessing Interactions

This subsection show several examples about how to iterate over interactions and access information from them.

The first simple code snippet shows how to loop over the interactions and count them. The OEInteractionHintContainer.GetInteractions method returns an iterator over all the OEInteractionHint object stored in the container.

def num_interactions(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    nrinters = len([i for i in asite.GetInteractions()])
    return nrinters

The next code snippet shows how to count only the intra-molecular interactions by utilizing the OEInteractionHint.IsIntra method.

def num_intra_interactions(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    nrintras = 0
    for i in asite.GetInteractions():
        if i.IsIntra():
            nrintras += 1
    return nrintras

Alternatively, you can use the OEIsIntraInteractionHint interaction predicates to do the same. If a predicate is passed to the OEInteractionHintContainer.GetInteractions method, it only returns interactions which satisfy the given predicate.

def num_intra_interactions_predicate(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    nrintras = len([i for i in asite.GetInteractions(oechem.OEIsIntraInteractionHint())])
    return nrintras

See also

See the Interaction predicates subsection for the list of interaction predicates available in OEChem TK,

Even though OEChem TK provides a wide selection of built-in interaction predicates, a need might arise to write user-defined predicates. The following two examples show how to implement and utilize your own predicates.

In the first example, the IsCarbonContactInteraction predicate returns True for contact interaction type between two carbon atoms. It uses the OEInteractionHint.GetBgnFragment and OEInteractionHint.GetEndFragment methods to retrieve the two interacting fragments (OEInteractionHintFragment) stored in the OEInteractionHint object.

class IsCarbonContactInteraction(oechem.OEUnaryInteractionHintPred):

    def __call__(self, inter):
        """
        :type inter: oechem.OEInteractionHint
        :rtype: boolean
        """
        # check the type if the interactions
        if inter.GetInteractionType() != oechem.OEContactInteractionHint():
            return False

        # check that the interaction is between two carbon atoms
        for frag in [inter.GetBgnFragment(), inter.GetEndFragment()]:
            catom = frag.GetAtom(oechem.OEIsCarbon())
            if catom is None:
                return False
        return True

A user-defines predicates can be used exactly the same way as the built-in ones:

def num_carbon_contact_interactions(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    nrinters = len([i for i in asite.GetInteractions(IsCarbonContactInteraction())])
    return nrinters

The predicate of the second example is a bit more complicated and shows how the OEHasResidueInteractionHint built-in predicate has been implemented in OEChem TK. The HasResidueInteraction returns True if the OEInteractionHint object stores interaction for the given residue (OEResidue).

class HasResidueInteraction(oechem.OEUnaryInteractionHintPred):
    def __init__(self, res):
        oechem.OEUnaryInteractionHintPred.__init__(self)
        self.residue = res
        self.atompred = oechem.OEAtomIsInResidue(res)

    def __call__(self, inter):
        """
        :type inter: oechem.OEInteractionHint
        :rtype: boolean
        """
        for frag in [inter.GetBgnFragment(), inter.GetEndFragment()]:
            for atom in frag.GetAtoms(self.atompred):
                return True
        return False

    def CreateCopy(self):
        return HasResidueInteraction(self.residue).__disown__()

The following code snippet counts the number of interactions for the ‘TRP-275-A’ residue.

def num_residue_interactions(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    res = oechem.OEResidue()
    res.SetName('TRP')
    res.SetChainID('A')
    res.SetResidueNumber(275)

    nrinters = len([i for i in asite.GetInteractions(HasResidueInteraction(res))])

    return nrinters

OEChem TK predicates can be combined with logical operations. The example below count the number of stacking interaction for the ‘TRP-275-A’ residue.

def num_stacking_residue_interactions(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: int
    """
    res = oechem.OEResidue()
    res.SetName('TRP')
    res.SetChainID('A')
    res.SetResidueNumber(275)

    respred = HasResidueInteraction(res)
    stackingpred = oechem.OEIsStackingInteractionHint()

    nrinters = len([i for i in asite.GetInteractions(oechem.OEAndInteractionHint(respred, stackingpred))])
    return nrinters

Similarly you can use the OEOrInteractionHint and OENotInteractionHint logical operations combine built-in and user-defined predicates to express complex conditions.

Accessing Interaction Atoms

This subsection show several examples about how to access the atom of an interaction. The first two code snippets show how to retrieve ligand atoms that are belong to any interactions stored in the given interaction container.

In the first code snippet, the ligand component of an interaction container is retrieved, then the atoms of the ligand molecule are being looped over and checked whether they belong to any of the interactions stored in the container by calling the OEInteractionHintContainer.HasInteraction method. This method returns True if there is at least one interaction that matches the given predicate. In this example the predicate is OEHasInteractionHint that identifies interactions with the given atom. You can find out more about interaction predicates in the Accessing Interactions subsection.

def get_ligand_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    for atom in ligand.GetAtoms():
        if asite.HasInteraction(oechem.OEHasInteractionHint(atom)):
            atoms.append(atom)
    return atoms

In the second example, the interaction of the container are looped over. Each interaction stores two fragments (OEInteractionHintFragment). If a fragment is belong to the ligand component then its atoms are added to ligand atom set.

It is important to collect the ligand atoms in a set (rather than in a list as in the previous example). While in the previous example, atom can be added to a list only once, in the second example, when looping over interactions, a ligand atom can be encountered more than once since an atom can belong to more than one interactions.

def get_ligand_frag_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = set()
    for inter in asite.GetInteractions():
        for frag in [inter.GetBgnFragment(), inter.GetEndFragment()]:
            if frag.GetComponentType() == oechem.OELigandInteractionHintComponent():
                for atom in frag.GetAtoms():
                    atoms.add(atom)
    return list(atoms)

If you want to access protein atoms, you have to replace the OELigandInteractionHintComponent type with OEProteinInteractionHintComponent in the previous two examples. For example:

def get_protein_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = list()
    protein = asite.GetMolecule(oechem.OEProteinInteractionHintComponent())
    for atom in protein.GetAtoms():
        if asite.HasInteraction(oechem.OEHasInteractionHint(atom)):
            atoms.append(atom)
    return atoms

In the following examples, atom predicates are used to collect only the subset of the ligand atoms. The code snippet below demonstrates how to retrieve only nitrogen ligand atoms. It uses the OEIsNitrogen atom predicate which is one of built-in predicate available in OEChem TK.

def get_ligand_nitrogen_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    for atom in ligand.GetAtoms(oechem.OEIsNitrogen()):
        if asite.HasInteraction(oechem.OEHasInteractionHint(atom)):
            atoms.append(atom)
    return atoms

See also

See the Atom predicates subsection for the list of atom predicates available in OEChem TK

Even though OEChem TK provides a wide selection of built-in atom predicates, a need might arise to write s user-defined predicate. The following examples shows how to implement and utilize such a predicate.

class HasAtomicNumber(oechem.OEUnaryAtomPred):
    def __init__(self, alist):
        oechem.OEUnaryAtomPred.__init__(self)
        self.atomic_list = alist

    def __call__(self, atom):
        """
        :type asite: oechem.OEAtomBase
        :rtype: boolean
        """
        return (atom.GetAtomicNum() in self.atomic_list)

    def CreateCopy(self):
        return HasAtomicNumber(self.atomic_list).__disown__()
def get_ligand_oxygen_nitrogen_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    atompred = HasAtomicNumber([oechem.OEElemNo_O, oechem.OEElemNo_N])
    for atom in ligand.GetAtoms(atompred):
        if asite.HasInteraction(oechem.OEHasInteractionHint(atom)):
            atoms.append(atom)
    return atoms

OEChem TK predicates can be combined with logical operations. In the example below only chain nitrogen that belong to interactions are collected.

def get_ligand_chain_nitrogen_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    for atom in ligand.GetAtoms(oechem.OEAndAtom(oechem.OEIsNitrogen(), oechem.OEAtomIsInChain())):
        if asite.HasInteraction(oechem.OEHasInteractionHint(atom)):
            atoms.append(atom)
    return atoms

Similarly you can use the OEOrAtom and OENotAtom logical operations combine built-in predicates and express complex conditions.

The interaction perceived by the OEPerceiveInteractionHints function are typed. See Table 1 for list of interaction types that are currently perceived in OEChem TK. This means that is quite easy to iterate over atoms and check whether they belong to a specific interaction type.

The code snippet below collects ligand atoms which part of a stacking interaction utilizing the OEIsStackingInteractionHint build-in predicate.

def get_ligand_stacking_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    stackingpred = oechem.OEIsStackingInteractionHint()

    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    for atom in ligand.GetAtoms():
        if asite.HasInteraction(oechem.OEAndInteractionHint(oechem.OEHasInteractionHint(atom), stackingpred)):
            atoms.append(atom)
    return atoms

See also

See the Interaction predicates subsection for the list of interaction predicates available in OEChem TK,

Some interactions type is also associated with a namespace that enables to subtype the interaction. For example, the OEStackingInteractionHintType namespace corresponds to the OEStackingInteractionHint interaction type. The following example shows how to access ligand atoms that are in a Pi-stacking interaction.

def get_ligand_pi_stacking_atoms(asite):
    """
    :type asite: oechem.OEInteractionHintContainer
    :rtype: list[oechem.OEAtomBase]
    """
    stackingsubtype = oechem.OEStackingInteractionHint(oechem.OEStackingInteractionHintType_Pi)
    stackingpred = oechem.OEHasInteractionHintType(stackingsubtype)

    atoms = list()
    ligand = asite.GetMolecule(oechem.OELigandInteractionHintComponent())
    for atom in ligand.GetAtoms():
        if asite.HasInteraction(oechem.OEAndInteractionHint(oechem.OEHasInteractionHint(atom), stackingpred)):
            atoms.append(atom)
    return atoms

Discussion

Overview of OEChem TK Interaction API

OEChem TK provides the following API to perceive protein-ligand interactions:

OEPerceiveInteractionHints
The function that perceives protein-ligand interactions.
OEPerceiveInteractionOptions

The class that stores the parameters that control the interaction perception.

The default geometric parameters have been set based on literature data ([Kumar-2002], [Cavallo-2016], [Bissantz-2010], and [Marcou-2007] ).

Code examples

Figure 1. Schematic representation of interaction container

OEChem TK provides the following API to store and access protein-ligand interactions:

OEInteractionHintContainer
This is the main container that stores typed molecules (OEMolBase), fragments of those molecules (OEInteractionHintFragment) and typed interactions (OEInteractionHint) between the fragments.
OEInteractionHintComponentTypeBase

The abstract class that enables to type molecules stored in the interaction container. Each molecule added to the container has to have a specific type. Currently, there are two built-in component types available:

OEInteractionHintFragment
Set of atoms of a molecule that is stored in the interaction container. These are the atoms that are contribute to an interaction (OEInteractionHint). Some interactions such as hydrogen bonds are between two atoms, other ones define interactions between set of atoms. For example a stacking interaction is between atoms of two aromatic ring systems.
OEInteractionHint
An iteration is defined between two fragments (OEInteractionHintFragment) stored in the container. An interaction can be either inter (OEInteractionHint.IsInter) or intra (OEInteractionHint.IsIntra) molecular. Each interaction has a specific type that is derived from the OEInteractionHintTypeBase class.
OEInteractionHintTypeBase

The abstract class that enables to type interactions stored in the interaction container. The table below shows the specific interaction types currently available in OEChem TK. Some interaction classes are also associated with namespaces that enable to subtype the main interaction types. For example, the stacking interaction have two subtypes:

Table 1. Interaction types currently available in OEChem TK
name corresponding interaction class corresponding interaction type namespace
cation-pi OECationPiInteractionHint OECationPiInteractionHintType
chelator OEChelatorInteractionHint OEChelatorInteractionHintType
clash OEClashInteractionHint None
contact OEContactInteractionHint None
covalent OECovalentInteractionHint None
halogen bond OEHalogenBondInteractionHint OEHalogenBondInteractionHintType
hydrogen bond OEHBondInteractionHint OEHBondInteractionHintType
salt-bridge OESaltBridgeInteractionHint OESaltBridgeInteractionHintType
stacking (T and Pi) OEStackingInteractionHint OEStackingInteractionHintType

Other related API:

OEIsValidActiveSite
The function that checks whether the interaction container initialized correctly as active site i.e. containing two molecules: one typed as ligand and one typed as protein.
OEGetActiveSiteInteractionHintTypes
The function that iterates over all interaction types (including subtypes) that are perceived by the OEPerceiveInteractionHints function.

Atom predicates

OEChem TK provides an extensive set of built-in atom predicates, the most common ones are listed in the following table.

Table 2. Common OEChem TK atom predicates
Access Functor Name
aromatic atoms OEIsAromaticAtom
ring atoms OEAtomIsInRing
chain atoms OEAtomIsInChain
atoms with specified residue OEAtomIsInResidue
atoms with specified atomic number OEHasAtomicNum
hetero atoms OEIsHetero

The following logical operations allow to combine both the built-in and the user defined atom predicates.

Table 3. OEChem TK atom composition functors
Logical operation Composite Functor Name
Logical and OEAndAtom
Logical or OEOrAtom
Logical not OENotAtom

See also

For the full list of atom predicates, see more information in the following sections on the OEChem TK manual:

Code examples

Interaction predicates

The table below list all interaction predicates that are currently available in OEChem TK.

Table 4. OEChem TK interaction predicates
Access interactions Functor Name
with given interaction type OEHasInteractionHintType
with given atom OEHasInteractionHint
with given residue OEHasResidueInteractionHint
contact OEIsContactInteractionHint
hydrogen bond OEIsHBondInteractionHint
inter-molecular hydrogen bond OEIsIntermolecularHBondInteractionHint
intra-molecular hydrogen bond OEIsIntramolecularHBondInteractionHint
chelator OEIsChelatorInteractionHint
inter-molecular chelator OEIsIntermolecularChelatorInteractionHint
intra-molecular chelator OEIsIntramolecularChelatorInteractionHint
covalent OEIsCovalentInteractionHint
clash OEIsClashInteractionHint
salt-bridge OEIsSaltBridgeInteractionHint
T and Pi-stacking OEIsStackingInteractionHint
cation-Pi OEIsCationPiInteractionHint
halogen bond OEIsHalogenBondInteractionHint
inter-molecular OEIsInterInteractionHint
intra-molecular OEIsIntraInteractionHint
unpaired ligand OEIsUnpairedLigandInteractionHint
unpaired protein OEIsUnpairedProteinInteractionHint

The following logical operations allow to combine both the built-in and the user defined interaction predicates.

Table 5. OEChem TK interactions composition functors
Logical operation Composite Functor Name
Logical and OEAndInteractionHint
Logical or OEOrInteractionHint
Logical not OENotInteractionHint

Code examples