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

../_images/interactionhint.svg

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