Receptors

In the Docking Toolkit receptor objects contain the structure of a target protein and information about the location and characteristics of the binding pocket. The receptor can be used in both docking (see Docking) and scoring (see Scoring).

A receptor is an OEMolBase object with the structure of a target protein that has additional information tagged to it describing the characteristics of the active site. The structure of the target protein is accessible via the standard OEChem::OEMolBase API. The additional active site information is accessible via an API comprised of a set of free functions (see receptor functions section).

A receptor always contains:

  • The structure of a target protein
  • A negative image that describes the shape of the active site

A receptor may contain:

  • The structure of a ligand bound to the active site
  • Docking constraints specifying required protein-ligand interactions
  • Extra molecules that do not affect either docking or scoring
    • Generally water or other solvent molecules

Receptor I/O

Within the Docking Toolkit receptors are OEGraphMol objects (or any other OEMolBase object). On disk receptors are stored in OEB format (and thus have .oeb or .oeb.gz extensions).

The following code snippet reads a receptor

OEGraphMol receptor;
string inputReceptorFilename = argv[1];
OEReadReceptorFile(receptor, inputReceptorFilename);

Note

A receptor can also be read using the oechem OEReadMolecule function. The difference in behavior is that OEReadReceptorFile will fail and return false if the molecule being read is not a receptor.

A receptor can be written to disk as follows

string outputReceptorFilename = argv[2];
OEWriteReceptorFile(receptor, outputReceptorFilename);

Note

A receptor can also be written using the oechem OEWriteMolecule command, however writing to any format besides OEBinary will result in all receptor information being lost except for the protein structure.

See also

Receptor functions API documentation

Creating a Receptor

Within the Docking toolkit the OEMakeReceptor function is used to create a receptor from the structure of a target protein and one of the following:

Box

  oemolistream imstr(argv[1]);
  OEGraphMol proteinStructure; 
  OEReadMolecule(imstr, proteinStructure);

  OEBox box(xmax, ymax, zmax, xmin, ymin, zmin);
  OEGraphMol receptor;
  OEMakeReceptor(receptor, proteinStructure, box);

The box should enclose the active site. All molecules docked into the receptor or being scored will be required to fit within the box (i.e. all heavy atom centers must be within the box).

Bound Ligand

  OEGraphMol protein; 
  OEGraphMol ligand; 
  OEGraphMol receptor;

  OEReadMolecule(proteinMolStream, protein);
  OEReadMolecule( ligandMolStream, ligand);

  OEMakeReceptor(receptor, protein, ligand);

The created receptor will include the ligand as a bound ligand.

Hint coordinate

  OEGraphMol protein; 
  oemolistream imstr(argv[1]);
  OEReadMolecule(imstr, protein);

  float hintX, hintY, hintZ;
  OEStringToNumber(argv[2], hintX);
  OEStringToNumber(argv[3], hintY);
  OEStringToNumber(argv[4], hintZ);

  OEGraphMol receptor;
  OEMakeReceptor(receptor, protein, hintX, hintY, hintZ);

The hint coordinate should be in or near the receptor pocket.

The protein structure should only include molecules the ligand is expected to interact with. In general crystallographic waters, other solvents and the bound ligand should be stripped from the protein structure passed to the OEMakeReceptor function, although in certain cases, the user may wish to retain certain key molecules as part of the protein structure (e.g. a crystallographic water).

Note

Receptors can also be created with the FRED application or the Fred Receptor GUI wizard. Receptor creation using either the FRED application or GUI wizard is covered by the FRED documentation.

Contents of a Receptor

Protein Structure

The protein structure includes all molecules that a ligand being docked or scored interacts with. This generally only includes the protein, but may also include key cofactors or solvent molecules (e.g. a crystallographic water). All parts of the receptor structure are static during both docking and scoring and any cofactors or solvent molecules in the receptor structure are not displaceable.

Note

A receptor may contain bound ligands and extra molecules. These molecules are part of the receptor object as a whole, but not the protein structure.

A receptor is a OEMolBase object with the structure of a target protein. The protein structure is accessible via the API of OEMolBase, while all other receptor information is accessed via a specialized set of free functions (see receptor functions section). For example, calling the method OEMolBase::GetAtoms on a receptor will return atoms of the protein structure, but not the atoms of the bound ligand or extra molecules.

Warning

Modification to the receptor structure will not automatically update all other parts of the receptor. If receptor modifications change the shape of the active site the negative image will no longer correctly describe the shape of the active site. Also, if the coordinate system of the receptor structure is changed, neither the negative image nor any custom constraints will be valid.

Negative Image

The negative image describes the shape of the active site. It is stored as a potential grid surrounding the active site. Potential values are always greater than or equal to zero. The negative image has high potentials where ligand atoms make many contacts with atoms of the active site without clashing and in positions some ligand atoms are likely to occupy when other atoms of the ligand make good contacts with the receptor (e.g. bridging positions ligand atoms will likely occupy when a ligand is stretched between two pockets).

During docking two shapes are created by contouring the negative image potential grid. The two shapes control the docking process as follows:

Outer Contour Shape

During docking any pose examined by the exhaustive search that does not fit within this shape will be rejected. A pose is considered to fit if the center of every heavy atom is within this shape. The volume of this shape is typically between 500 and 2000 cubic Angstroms.

Inner Contour Shape

During docking any pose examined by the exhaustive search that does not touch this shape is rejected. A pose is considered to touch if the center of at least one heavy atom falls within this shape. The volume of this shape is typically 50 to 100 cubic angstroms.

While neither the outer contour nor the inner contour shape are required, it is recommended that the outer contour always be used (docking speed will be dramatically slower without the outer contour). Using the inner contour improves docking speed slightly at the expense of sampling.

The negative image of the receptor is setup when the receptor is created with the OEMakeReceptor function (see Creating a Receptor section). The size of the outer contour and inner contour shapes are controllable by setting the contour level used to create the shape from the negative image. The OEMakeReceptor function will automatically set a reasonable contour level for both the inner contour and outer contour, however the inner contour will be disabled by setting the value to be negative (see below).

The negative image grid has only positive values, thus only contour levels that are positive create a shape. When either the outer contour level or inner contour level are negative that contour shape will be disabled (i.e. ignored during the docking process). Thus either the inner or outer contour can be toggled on and off by multiplying the respective level by -1 as shown in the following code snippet.

float innerContourLevel = OEReceptorGetInnerContourLevel(receptor);
OEReceptorSetInnerContourLevel(receptor, -innerContourLevel);

The above example assumes that the absolute contour level is set to a reasonable value.

The volume of the outer contour shape has a significant effect on docking speed, while the volume of the inner contour shape has a modest effect on docking speed. In both cases the smaller volumes increase docking speed by reducing the number of poses that are scored. In general an outer and inner contour volume of between 500-2000 and 50-100 cubic Angstroms respectively is recommended.

The following code example reports the volume of the outer contour.

OEScalarGrid negativeImagePotential = OEReceptorGetNegativeImageGrid(receptor);
float outerContourLevel = OEReceptorGetOuterContourLevel(receptor);

unsigned int outerCount = 0;
for (unsigned int i=0 ; i<negativeImagePotential.GetSize() ; ++i)
  if (negativeImagePotential[i] >= outerContourLevel)
    ++outerCount;

float countToVolume = powf(negativeImagePotential.GetSpacing(), 3.0f);

cout << (float)outerCount * countToVolume << " cubic angstroms" << endl;

This example sets the outer contour volume to a specified volume in cubic angstroms.

OEScalarGrid negativeImagePotential = OEReceptorGetNegativeImageGrid(receptor);

vector<float> gridElement;
for (unsigned int i=0 ; i<negativeImagePotential.GetSize() ; ++i)
  gridElement.push_back(negativeImagePotential[i]);
sort(gridElement.begin(), gridElement.end(), greater<float>());

float outerContourLevel = gridElement[gridElement.size()-1];
float countToVolume = powf(negativeImagePotential.GetSpacing(), 3.0f);
unsigned int ilevel = (unsigned int) (outerContourVolume/countToVolume + 0.5f);
if (ilevel < gridElement.size())
  outerContourLevel = gridElement[ilevel];

OEReceptorSetOuterContourLevel(receptor, outerContourLevel);

Bound Ligand

A receptor may optionally contain the structure of a single ligand bound into the active site. A receptor is required to have a bound ligand when using the hybrid docking method (see Hybrid Method section), but it is ignored by all other docking and scoring methods. Typically the structure is obtained experimentally by X-ray crystallography along with the protein structure, although the ligand structure can be determined by other means (e.g. docking).

The following code snippet checks to see if a receptor has a bound ligand

if (OEReceptorHasBoundLigand(receptor))
  cout << "Receptor has a bound ligand" << endl;
else
  cout << "Receptor does not have bound ligand" << endl;

This snippet extracts the bound ligand from the receptor if it is present

OEGraphMol ligand;
if (OEReceptorHasBoundLigand(receptor))
  ligand = OEReceptorGetBoundLigand(receptor);

Setting a new bound ligand is done as follows

OEReceptorSetBoundLigand(receptor, ligand);

Finally a bound ligand can also be deleted from the receptor

OEReceptorClearBoundLigand(receptor);

Extra Molecules

A receptor can contain any number of extra molecules. These molecules have no effect on docking or scoring, but can be retrieved from the receptor object or viewed in VIDA. This provides a mechanism for retaining structural information about water or other solvent molecules that are present in PDB files but typically must be stripped from the protein structure for docking and scoring.

This snippet sets the first extra molecule as the bound ligand of the receptor and removes it from the extra molecules list.

if (OEReceptorHasExtraMols(receptor))
{
  OEIter<const OEMolBase> extraMolIter = OEReceptorGetExtraMols(receptor);
  OEReceptorSetBoundLigand(receptor, *extraMolIter);

  vector<OEGraphMol> newExtraMolList;
  for (++extraMolIter ; extraMolIter ; ++extraMolIter)
    newExtraMolList.push_back(*extraMolIter);

  OEReceptorClearExtraMols(receptor);
  for (unsigned int i=0 ; i<newExtraMolList.size() ; ++i)
    OEReceptorAddExtraMol(receptor, newExtraMolList[i]);
}

Constraints

Constraints are key interactions ligands are required to make when docking into the active site. They are optional and user defined.

Constraints do not affect how a given pose scores, however they do affect how the docking algorithm chooses poses to score during the docking process. Any pose that does not match the docking constraints will be rejected and replaced by the next best scoring pose. If no poses of a ligand match the docking constraints the ligand will not be docked. If multiple constraints are specified every constraint must be satisfied or the pose will be rejected.

Receptors support two general classes of constraints; protein constraints and custom constraints. There may be any number of either class of constraint.

Note

Each individual custom or protein constraint has an enabled flag. A disabled constraint is ignored during the docking process.

Protein Constraints

A protein constraint specifies an interaction that must be made with a heavy atom of the protein structure (i.e. protein constraints cannot be placed on hydrogen atoms). There are five types of protein constraints.

Contact

A contact constraint is satisfied when any ligand heavy atom is within 4 angstroms of the protein heavy atom.

Lipophilic

A lipophilic constraint is satisfied when any non-polar heavy atom on the ligand is within 4 angstroms of the protein heavy atom.

Donor

A donor constraint is satisfied when a donor on the ligand makes a hydrogen bond interaction with the protein heavy atom.

Acceptor

An acceptor constraint is satisfied when an acceptor on the ligand makes a hydrogen bond interaction with the protein heavy atom. Acceptor constraints must be placed on the protein heavy atom the donor hydrogen is interacting with.

Chelator

A chelator constraint is satisfied when a chelator on the ligand makes a metal-chelator interaction with the protein heavy atom.

Only one protein constraint is allowed per protein heavy atom. If a protein constraint is set on a protein atom that already has a protein constraint the original protein constraint will be discarded and replaced by the new constraint.

The following code snippet checks to see what protein constraints, if any, are present

for (OEIter<const OEProteinConstraint> constraint 
       = OEReceptorGetProteinConstraints(receptor) ;
     constraint ;
     ++constraint)
  cout << "Atom " << constraint->GetAtom()->GetIdx() << " has a constraint" << endl;

This snippet provides a more detailed description of each constraint

for (OEIter<const OEProteinConstraint> constraint 
       = OEReceptorGetProteinConstraints(receptor) ;
     constraint ;
     ++constraint)
{
  cout << "Protein constraint on atom " << constraint->GetAtom()->GetIdx() << endl;
  cout << "  Type : " << OEProteinConstraintTypeGetName(constraint->GetType()) << endl;
  cout << "  Name : " << constraint->GetName() << endl;
}

The OEReceptorSetProteinConstraint function is used to set protein constraints as in the following example.

OEProteinConstraint proteinConstraint;
proteinConstraint.SetAtom(&*heavyAtom);
proteinConstraint.SetType(OEProteinConstraintType::Contact);
proteinConstraint.SetName("Example constraint");
OEReceptorSetProteinConstraint(receptor, proteinConstraint);

This example removes all protein constraints

OEReceptorClearProteinConstraints(receptor);

Custom Constraints

A custom constraint consists of one or more spheres within the receptor active site, and optionally a list of SMARTS patterns. A custom constraint is satisfied when a matching atom on the ligand falls within any of the spheres associated with the custom constraint. If no SMARTS patterns are specified any heavy atom on the ligand can satisfy the constraint, otherwise only atoms that match one of the smarts patterns can satisfy the constraint.

The following code snippet checks to see if the receptor has custom constraints.

if (OEReceptorHasCustomConstraints(receptor))
  cout << "Receptor has custom constraints" << endl;
else
  cout << "Receptor does not have custom constraints" << endl;

This snippet provides a more details description of each constraint including what type of constraint it is, if it is enabled and its name.

OECustomConstraints customConstraints;
customConstraints = OEReceptorGetCustomConstraints(receptor);
for (OEIter<OEFeature> feature = customConstraints.GetFeatures() ;
     feature ;
   ++feature)
{
  cout << "Feature " << feature->GetFeatureName() << endl;
  cout << "  Spheres : " << endl;
  for (OEIter<const OESphereBase> sphere = feature->GetSpheres() ;
       sphere ;
     ++sphere)
  {
    cout << "    center (" << sphere->GetX();
    cout << ", " << sphere->GetY();
    cout << ", " << sphere->GetZ();
    cout <<  ")  radius " << sphere->GetRad() << endl;
  }
  OEIter<const string> smarts = feature->GetSmarts();
  if (!smarts)
    cout << "  Constraint is matched by any heavy atom" << endl;
  else
  {
    cout << "  SMARTS:" << endl;
    for ( ; smarts ; ++smarts)
      cout << "    " << *smarts << endl;
  }
}

This example sets a simple custom constraint that is satisfied when any heavy atom is placed within 4 Angstroms of a give protein atom.

string name = "Example protein contact constraint";

OECustomConstraints customConstraints;
customConstraints = OEReceptorGetCustomConstraints(receptor);

OEFeature *feature = customConstraints.AddFeature();
feature->SetFeatureName(name);

float sphereRadius = 4.0f;
float sphereCenter[3];
receptor.GetCoords(&proteinHeavyAtom, sphereCenter);
OESphereBase *sphere = feature->AddSphere();
sphere->SetRad(sphereRadius);
sphere->SetCenter(sphereCenter[0], sphereCenter[1], sphereCenter[2]);  
 
OEReceptorSetCustomConstraints(receptor, customConstraints);

This example deletes all custom constraints

OEReceptorClearCustomConstraints(receptor);