Selectors#
In ProteoTwirl, a Selector controls which atoms are included when
calculating a property or when visualizing sampled orientations. Selectors
can also be chained together to create more complex selection logic.
Selectors are especially useful for angle-dependent properties. For example, they can be used to select only exposed atoms, atoms within a field of view, or atoms within a defined distance from the rotation center.
Available Selectors#
There are four different types of Selector implemented in ProteoTwirl, the
ClippingPlaneSelector, ClippingRadiusSelector, ExposedAtomSelector
and FieldOfViewSelector.
The examples below use the histamine H4 receptor structure
PDB: 8JXW, loaded as described in Loading Protein. Only
32 orientations are sampled here. This is intentionally low and is
used to keep the documentation examples small.
import ProteoTwirl.ProteinRotation as pt
from ProteoTwirl.Selectors.Selector import (
ClippingPlaneSelector,
ClippingRadiusSelector,
ExposedAtomSelector,
FieldOfViewSelector,
)
protein_structure = pt.ProteinRotation(
protein = "8JXW_H.pdb", # Protein structure to load
selection = "protein and segid E", # Select receptor only; here, segment E
base_rotation = "principle", # Align to the principal axes
center_selection = True, # Center the selection at (0, 0, 0)
atom_radii = "vdw", # Definition of atomic radii to use
# Default values avoid having to re-enter these for each added property
default_rotation_res = 32, # Number of orientations to sample
default_rotation_range = None, # Optional min/max values for theta and phi
default_selectors = None, # Optional orientation-dependent atom selectors
default_sampler = None, # Sampling method; currently Fibonacci sphere only
)
# Show an interactive view of the loaded protein.
protein_structure
Clipping Plane#
ClippingPlaneSelector selects atoms based on their position along the
viewing direction for each sampled orientation. The selector takes a
distance interval, given as (start, stop), and only atoms within that
interval are included.
A value of 0 corresponds to the rotation center. Positive values indicate
distance on the other side of the center from the viewing direction while
negative values are towards the viewing direction. The units are the units of
the protein loaded, usually Ångström.
If either value is None, no limit is applied on that side of the
interval. For example, to select atoms on the side of the protein facing
the viewer, use (0, None).
The visualization below shows the selected atoms for each sampled orientation. Use the slider at the bottom of the visualization to change which orientation is shown.
In this example, we create a RotationCounter object using
protein_structure.iter so that the selector can be visualized. Selector``s
can be provided to ``.add_property in the same manner, example of this can
be found in Using Count Properties
clipping_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ClippingPlaneSelector((0, None))],
)
clipping_rotation_iter.show_3d(height=400)
If both interval values are set, the selector returns atoms within a band across the protein.
clipping_rotation_iter_2 = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ClippingPlaneSelector((-13, 2))],
)
clipping_rotation_iter_2.show_3d(height=400)
Clipping Radius#
ClippingRadiusSelector is not angle-dependent. It works similarly to
ClippingPlaneSelector, but selects atoms based on their radial
distance from one or more center points.
The selector takes a distance interval, given as (start, stop). Only
atoms whose distance from the center falls within this interval are
included. As with clipping-plane selection, either value can be set to
None to remove that bound.
By default, the radius is measured from (0, 0, 0).
clipping_rad_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ClippingRadiusSelector((0, 15))],
)
clipping_rad_rotation_iter.show_3d(height=400)
A custom center can also be provided. Multiple centers can be supplied, either as atom indices or as points in 3D space. If multiple centers are provided, an atom is selected if its distance to at least one of the centers falls within the interval.
In the example below, atoms 35 and 1550 are used as centers. Note
that when using indicies like this, its indicies in ProteinRotation.positions.
clipping_rad2_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ClippingRadiusSelector((0, 15), center=[35, 1550])],
)
clipping_rad2_rotation_iter.show_3d(height=400)
if instead you want to select based on indices in the protein file, you can
set center to a MDAnalysis selection string, for example to select atom
with index 12200 to 13000 you can do center = "index 12200:13000". You can
also select residue and make more complex selection using this method, such as
selecting everything within a radius of 7 from any cystine residue.
clipping_rad2_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ClippingRadiusSelector((0, 7), center="resname CYS")],
)
clipping_rad2_rotation_iter.show_3d(height=400)
Field of View#
FieldOfViewSelector narrows which atoms are considered, similar to
decreasing the field of view of a camera. Technically, this is not a
perspective camera view; ProteoTwirl uses an orthographic projection.
The selector works by creating a tube along the viewing direction and including only atoms that fall within that tube.
fov_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [FieldOfViewSelector(10)],
)
fov_rotation_iter.show_3d(height=400)
Exposed Atoms#
ExposedAtomSelector selects atoms that are visible from a given
orientation. In other words, it selects atoms that are not hidden behind
other atoms from the current viewing direction.
This selector uses the atomic radii defined in the ProteinRotation
object to determine whether one atom occludes another.
exposed_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [ExposedAtomSelector()],
)
exposed_rotation_iter.show_3d(height=400)
Combining Selectors#
Multiple selectors can be provided as a list. They are applied in the order they appear in the list, so selector order can affect the final selection.
One useful application is to analyze the surface of a binding pocket. This can be done by combining:
ClippingPlaneSelectorto select the side of the protein viewed from inside the pocket,FieldOfViewSelectorto restrict the view to a local region, andExposedAtomSelectorto keep only atoms visible from that direction.
Together, these selectors allow each orientation to capture a local view of the binding pocket rather than a view of the entire protein surface.
First, use MDAnalysis to find the center of the ligand. In this example,
the ligand residue name is VCF.
import MDAnalysis as mda
u = mda.Universe("8JXW_H.pdb")
ligand = u.select_atoms("resname VCF")
center = ligand.center_of_geometry()
print(center)
# >>> array([141.21148831, 133.99280492, 108.46190253])
Next, load the protein and set the rotation origin to the ligand center.
Here, center_selection is set to False so that the original
coordinates are preserved.
protein_structure = pt.ProteinRotation(
protein = "8JXW_H.pdb",
selection = "protein and segid E",
center_selection = False,
center = center,
)
The selectors can then be combined to look from the inside of the binding pocket outward.
binding_pocket_rotation_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [
ClippingPlaneSelector((None, 0)),
FieldOfViewSelector(10),
ExposedAtomSelector(),
],
)
binding_pocket_rotation_iter.show_3d(height=400)
Creating Custom Selectors#
Custom selectors can be created by inheriting from the Selector base
class and defining the _select_impl method. You also need to specify
whether the selector is angle-dependent.
A selector is angle-dependent if it may select different atoms for
different protein orientations. ClippingPlaneSelector is
angle-dependent, while ClippingRadiusSelector is angle-independent.
If the selection depends on the rotated atom positions or on the viewing
direction, set angle_dependent=True. If the selection depends only on
static atom information, such as atom names, residue names, or atom
indices, set angle_dependent=False.
As an example, we can create a selector that selects backbone atoms.
import numpy as np
from ProteoTwirl.Selectors.Selector import Selector
class BackboneSelector(Selector):
def __init__(self):
super().__init__(
angle_dependent=False,
)
def _select_impl(
self,
current_points,
current_selection,
current_radii,
rotation,
protein_rotation,
):
backbone_atom_names = {"N", "CA", "C", "O"}
return np.array(
[
index
for index, atom in enumerate(current_selection.atoms)
if atom.name in backbone_atom_names
],
dtype=np.int64,
)
The _select_impl method receives the following values:
current_points: positions of the currently selected atoms;current_radii: radii of the currently selected atoms;current_selection: the current MDAnalysisAtomGroup;rotation: the current rotation;protein_rotation: theProteinRotationobject being analyzed.
The method should return atom indices relative to
current_selection. If the selector is the first selector in the
chain, current_selection contains all atoms in the loaded
ProteinRotation selection. If other selectors have already been
applied, current_selection contains only the atoms that passed the
previous selectors.
The new selector can then be used like the built-in selectors.
custom_iter = protein_structure.iter(
rotation_res = 32,
rotation_range = None,
selectors = [
BackboneSelector(),
],
)
custom_iter.show_3d(height=400)