Module deeporigin.src.structures.pocket_data

Classes

class PocketData (pocket: List | Pocket | Ligand,
xref_protein: Protein,
box_size: List | None = None,
fit_box: bool | None = None,
padding: int | None = 2)
Expand source code
class PocketData:
    def __init__(
        self,
        pocket: List | Pocket | Ligand,
        xref_protein: Protein,
        box_size: Optional[List] = None,
        fit_box: Optional[bool] = None,
        padding: Optional[int] = 2,
    ):
        """
        Initialize a PocketBox object.
        This class handles the creation and manipulation of pocket boxes for protein structures.
        It supports both direct coordinate input and structure-based pocket definitions.

        Args:
            pocket (List | Pocket | Ligand): Either a list of 3D coordinates [x,y,z] or a Pocket/Ligand object
            xref_protein (Protein): Reference protein structure
            box_size (List, optional): Custom box dimensions [x,y,z] in Angstroms. Defaults to None
            fit_box (bool, optional): Whether to fit box to pocket dimensions. Defaults to None
            padding (int, optional): Padding around pocket when fitting box. Defaults to 2

        Raises:
            ValueError: If both box_size and fit_box are specified
            ValueError: If pocket is a list but doesn't contain 3 numeric elements
            ValueError: If attempting to fit box to coordinate list
            ValueError: If pocket is invalid type

        Note:
            - If neither box_size nor fit_box specified, defaults to 24Å cubic box
            - When using Pocket/Ligand objects, PCA alignment is automatically performed
        """
        if box_size and fit_box:
            raise ValueError("Cannot specify both box_size and [fit_box and padding]")

        if not box_size and not fit_box:
            DEFAULT_LOGGER.log_warning("No box size or fit box specified. Defaulting to 24Å box.")
            box_size = [24, 24, 24]

        self.__pocket = deepcopy(pocket)
        if isinstance(self.__pocket, list):
            if len(self.__pocket) != 3:
                raise ValueError("If pocket is a list, it must have 3 elements (x, y, z)")

            for i in self.__pocket:
                if not isinstance(i, (int, float, np.int32, np.int64, np.float32, np.float64)):
                    raise ValueError("All elements in pocket must be integers or floats")

            self.__aligner = None

            if fit_box:
                raise ValueError("Cannot fit box to list of coordinates")

        elif isinstance(pocket, (Pocket, Ligand)):
            self.__aligner = StructureAligner()
            self.__aligner.calculate_pca(self.__pocket.coordinates)

            pocket_transformed_coords = self.transform(self.__pocket.coordinates)
            self.__pocket.update_coordinates(pocket_transformed_coords)
        else:
            raise ValueError("Invalid pocket type")

        self.__padding = padding
        self.__fit_box = fit_box
        self.__xref_protein = xref_protein
        self.__box_center = self._get_center(self.__pocket)
        self.__box_min_coords, self.__box_max_coords, self.__box_size = self.calculate_box_params(box_size)

    @classmethod
    def create_from_residues(
        cls,
        xref_protein: Protein,
        residue_ids: List[str],
        box_size: Optional[List] = None,
        fit_box: Optional[bool] = None,
        padding: Optional[int] = 2,
    ):
        """
        Creates a pocket data instance from a list of residue IDs.

        Args:
            xref_protein (Protein): The reference protein structure.
            residue_ids (List[str]): List of residue IDs that define the pocket.
            box_size (Optional[List], optional): Custom box dimensions. Defaults to None.
            fit_box (Optional[bool], optional): Whether to fit the box to the pocket. Defaults to None.
            padding (Optional[int], optional): Padding size around the pocket. Defaults to 2.

        Returns:
            PocketData: A new pocket data instance centered on the specified residues.

        Raises:
            None explicitly, but may log warnings if issues are found with residue selection.
        """
        center, warning, _ = xref_protein.get_center_by_residues(residue_ids)
        if warning:
            DEFAULT_LOGGER.log_warning(warning)

        return cls(pocket=center, xref_protein=xref_protein, box_size=box_size, fit_box=fit_box, padding=padding)

    @property
    def pocket(self):
        """
        Gets the pocket data.

        Returns:
            The pocket data stored in this instance.
        """
        return self.__pocket

    @property
    def xref_protein(self):
        """
        Get the cross-reference protein identifier.

        Returns:
            str: The cross-reference protein identifier associated with this pocket.
        """
        return self.__xref_protein

    @property
    def padding(self):
        """
        Gets the padding value used for this pocket data.

        Returns:
            float: The padding value used for spatial calculations around the pocket
        """
        return self.__padding

    @property
    def fit_box(self):
        """
        Gets the fit_box value used for this pocket data.

        Returns:
            bool: Whether the box is fitted to the pocket dimensions
        """
        return self.__fit_box

    @property
    def box_size(self):
        """
        Returns the size of the box.

        Returns:
            float: The size of the box representing the pocket's bounding box dimensions.
        """
        return self.__box_size

    @property
    def box_center(self):
        """
        Get the center coordinates of the binding site box.

        Returns:
            numpy.ndarray: The 3D coordinates [x, y, z] representing the center of the binding site box.
        """
        return self.__box_center

    @property
    def aligner(self):
        """
        Get the aligner instance associated with this pocket data.

        Returns:
            object: The aligner object used for structural alignment.
        """
        return self.__aligner

    @property
    def box_min_coords(self):
        """
        Returns the minimum coordinates of the box.

        Returns:
            list: A list containing the minimum x, y, z coordinates of the box.
        """
        return self.__box_min_coords

    @property
    def box_max_coords(self):
        """
        Gets the maximum coordinates of the box defining the pocket.

        Returns:
            tuple: A 3D coordinate tuple (x, y, z) representing the maximum bounds of the pocket box.
        """
        return self.__box_max_coords

    def _get_center(self, pocket: str | Pocket | List[float]):
        """
        Get the center coordinates of a pocket or ligand.

        Args:
            pocket (Union[str, Pocket, List[float]]): Input pocket data. Can be:
                - List[float]: Direct coordinates [x, y, z]
                - Pocket: Pocket object with get_center() method
                - Ligand: Ligand object with get_center() method

        Returns:
            List[float]: Center coordinates as [x, y, z]

        Raises:
            ValueError: If input pocket type is not supported
        """
        if isinstance(pocket, list):
            pocket = [float(x) for x in pocket]
        elif isinstance(pocket, (Pocket, Ligand)):
            pocket = pocket.get_center()
        else:
            raise ValueError("Invalid pocket type.")

        return pocket

    def transform(self, coords: np.ndarray) -> np.ndarray:
        """
        Transform coordinates using the aligner if available.

        Args:
            coords (np.ndarray): Input coordinates to be transformed.

        Returns:
            np.ndarray: Transformed coordinates if aligner exists, original coordinates otherwise.
        """
        if self.aligner is None:
            return coords

        return self.aligner.align_structure(coords)

    def inverse_transform(self, coords: np.ndarray) -> np.ndarray:
        """
        Inverse transform coordinates from the PCA-aligned space.

        Args:
            coords: Coordinates to inverse transform.

        Returns:
            Inverse transformed coordinates.
        """
        if self.aligner is None:
            return coords

        return self.aligner.restore_structure(coords)

    def match_protein(self, protein: Protein) -> bool:
        """
        Check if the protein matches the xref protein.

        Args:
            protein: Protein to check.

        Returns:
            True if the proteins match, False otherwise.
        """
        if protein != self.xref_protein:
            raise ValueError("Provided protein does not match xref protein.")

    def calculate_box_params(self, box_size: Optional[List] = None):
        if self.fit_box:
            result = create_bounding_box(self.pocket, padding=self.padding, around_ligand=True)
            box_min_coords, box_max_coords = list(result["min_coords"]), list(result["max_coords"])
        else:
            box_min_coords, box_max_coords = calculate_box_min_max(box_center=self.box_center, box_dimensions=box_size)

        if self.fit_box:
            box_size = calculate_box_dimensions(box_min_coords, box_max_coords)

        return box_min_coords, box_max_coords, box_size

    def from_xyz(self):
        """
        Determines if the pocket data is in XYZ format.

        Returns:
            bool: True if the pocket data is a list (XYZ format), False if it's a Ligand or Pocket object

        Raises:
            ValueError: If the pocket type is invalid
        """
        if isinstance(self.pocket, (Ligand, Pocket)):
            return False
        elif isinstance(self.pocket, list):
            return True
        else:
            raise ValueError("Invalid pocket type")

    @jupyter_visualization
    def show_box(self, protein: Protein = None, raise_for_protein_mismatch: bool = True) -> str:
        """
        Generates an HTML visualization of the protein structure with a bounding box representation of the pocket.
        This method creates a visual representation using MolStar viewer, showing either:
        1. The protein structure with a ligand and its bounding box (for non-XYZ derived pockets)
        2. The protein structure with just the bounding box (for XYZ derived pockets)

        Args:
            protein (Protein, optional): The protein structure to visualize. If None, uses the cross-referenced protein.
                Defaults to None.
            raise_for_protein_mismatch (bool, optional): Whether to raise an error if the protein doesn't match
                the pocket's reference protein. Defaults to True.

        Returns:
            str: HTML string containing the MolStar viewer visualization.

        Raises:
            ProteinMismatchError: If raise_for_protein_mismatch is True and the provided protein
                doesn't match the pocket's reference protein.
        """
        if protein is None:
            protein = self.xref_protein

        if raise_for_protein_mismatch:
            self.match_protein(protein)

        protein = deepcopy(protein)
        aligned_protein_coords = self.transform(protein.structure.coord)
        protein.update_coordinates(aligned_protein_coords)

        if not self.from_xyz():
            with tempfile.TemporaryDirectory() as temp_dir:
                tmp_protein_file = Path(temp_dir) / "protein.pdb"
                tmp_structure_file = Path(temp_dir) / "structure.sdf"

                protein.write_to_file(str(tmp_protein_file))
                self.pocket.write_to_file(str(tmp_structure_file), output_format="sdf")

                html = DockingMolstarViewer().render_ligand_with_bounding_box(
                    protein_data=str(tmp_protein_file),
                    protein_format="pdb",
                    ligand_data=str(tmp_structure_file),
                    ligand_format="sdf",
                    box={"min": self.box_min_coords, "max": self.box_max_coords},
                )
        else:
            protein_file_path = str(protein.file_path)
            protein_format = getattr(protein, "block_type", "pdb")
            html = DockingMolstarViewer().render_bounding_box(
                protein_data=protein_file_path,
                protein_format=protein_format,
                box_center=self.box_center,
                box_size=self.box_size,
            )

        return html

Initialize a PocketBox object. This class handles the creation and manipulation of pocket boxes for protein structures. It supports both direct coordinate input and structure-based pocket definitions.

Args

pocket : List | Pocket | Ligand
Either a list of 3D coordinates [x,y,z] or a Pocket/Ligand object
xref_protein : Protein
Reference protein structure
box_size : List, optional
Custom box dimensions [x,y,z] in Angstroms. Defaults to None
fit_box : bool, optional
Whether to fit box to pocket dimensions. Defaults to None
padding : int, optional
Padding around pocket when fitting box. Defaults to 2

Raises

ValueError
If both box_size and fit_box are specified
ValueError
If pocket is a list but doesn't contain 3 numeric elements
ValueError
If attempting to fit box to coordinate list
ValueError
If pocket is invalid type

Note

  • If neither box_size nor fit_box specified, defaults to 24Å cubic box
  • When using Pocket/Ligand objects, PCA alignment is automatically performed

Static methods

def create_from_residues(xref_protein: Protein,
residue_ids: List[str],
box_size: List | None = None,
fit_box: bool | None = None,
padding: int | None = 2)

Creates a pocket data instance from a list of residue IDs.

Args

xref_protein : Protein
The reference protein structure.
residue_ids : List[str]
List of residue IDs that define the pocket.
box_size : Optional[List], optional
Custom box dimensions. Defaults to None.
fit_box : Optional[bool], optional
Whether to fit the box to the pocket. Defaults to None.
padding : Optional[int], optional
Padding size around the pocket. Defaults to 2.

Returns

PocketData
A new pocket data instance centered on the specified residues.

Raises

None explicitly, but may log warnings if issues are found with residue selection.

Instance variables

prop aligner
Expand source code
@property
def aligner(self):
    """
    Get the aligner instance associated with this pocket data.

    Returns:
        object: The aligner object used for structural alignment.
    """
    return self.__aligner

Get the aligner instance associated with this pocket data.

Returns

object
The aligner object used for structural alignment.
prop box_center
Expand source code
@property
def box_center(self):
    """
    Get the center coordinates of the binding site box.

    Returns:
        numpy.ndarray: The 3D coordinates [x, y, z] representing the center of the binding site box.
    """
    return self.__box_center

Get the center coordinates of the binding site box.

Returns

numpy.ndarray
The 3D coordinates [x, y, z] representing the center of the binding site box.
prop box_max_coords
Expand source code
@property
def box_max_coords(self):
    """
    Gets the maximum coordinates of the box defining the pocket.

    Returns:
        tuple: A 3D coordinate tuple (x, y, z) representing the maximum bounds of the pocket box.
    """
    return self.__box_max_coords

Gets the maximum coordinates of the box defining the pocket.

Returns

tuple
A 3D coordinate tuple (x, y, z) representing the maximum bounds of the pocket box.
prop box_min_coords
Expand source code
@property
def box_min_coords(self):
    """
    Returns the minimum coordinates of the box.

    Returns:
        list: A list containing the minimum x, y, z coordinates of the box.
    """
    return self.__box_min_coords

Returns the minimum coordinates of the box.

Returns

list
A list containing the minimum x, y, z coordinates of the box.
prop box_size
Expand source code
@property
def box_size(self):
    """
    Returns the size of the box.

    Returns:
        float: The size of the box representing the pocket's bounding box dimensions.
    """
    return self.__box_size

Returns the size of the box.

Returns

float
The size of the box representing the pocket's bounding box dimensions.
prop fit_box
Expand source code
@property
def fit_box(self):
    """
    Gets the fit_box value used for this pocket data.

    Returns:
        bool: Whether the box is fitted to the pocket dimensions
    """
    return self.__fit_box

Gets the fit_box value used for this pocket data.

Returns

bool
Whether the box is fitted to the pocket dimensions
prop padding
Expand source code
@property
def padding(self):
    """
    Gets the padding value used for this pocket data.

    Returns:
        float: The padding value used for spatial calculations around the pocket
    """
    return self.__padding

Gets the padding value used for this pocket data.

Returns

float
The padding value used for spatial calculations around the pocket
prop pocket
Expand source code
@property
def pocket(self):
    """
    Gets the pocket data.

    Returns:
        The pocket data stored in this instance.
    """
    return self.__pocket

Gets the pocket data.

Returns

The pocket data stored in this instance.

prop xref_protein
Expand source code
@property
def xref_protein(self):
    """
    Get the cross-reference protein identifier.

    Returns:
        str: The cross-reference protein identifier associated with this pocket.
    """
    return self.__xref_protein

Get the cross-reference protein identifier.

Returns

str
The cross-reference protein identifier associated with this pocket.

Methods

def calculate_box_params(self, box_size: List | None = None)
Expand source code
def calculate_box_params(self, box_size: Optional[List] = None):
    if self.fit_box:
        result = create_bounding_box(self.pocket, padding=self.padding, around_ligand=True)
        box_min_coords, box_max_coords = list(result["min_coords"]), list(result["max_coords"])
    else:
        box_min_coords, box_max_coords = calculate_box_min_max(box_center=self.box_center, box_dimensions=box_size)

    if self.fit_box:
        box_size = calculate_box_dimensions(box_min_coords, box_max_coords)

    return box_min_coords, box_max_coords, box_size
def from_xyz(self)
Expand source code
def from_xyz(self):
    """
    Determines if the pocket data is in XYZ format.

    Returns:
        bool: True if the pocket data is a list (XYZ format), False if it's a Ligand or Pocket object

    Raises:
        ValueError: If the pocket type is invalid
    """
    if isinstance(self.pocket, (Ligand, Pocket)):
        return False
    elif isinstance(self.pocket, list):
        return True
    else:
        raise ValueError("Invalid pocket type")

Determines if the pocket data is in XYZ format.

Returns

bool
True if the pocket data is a list (XYZ format), False if it's a Ligand or Pocket object

Raises

ValueError
If the pocket type is invalid
def inverse_transform(self, coords: numpy.ndarray) ‑> numpy.ndarray
Expand source code
def inverse_transform(self, coords: np.ndarray) -> np.ndarray:
    """
    Inverse transform coordinates from the PCA-aligned space.

    Args:
        coords: Coordinates to inverse transform.

    Returns:
        Inverse transformed coordinates.
    """
    if self.aligner is None:
        return coords

    return self.aligner.restore_structure(coords)

Inverse transform coordinates from the PCA-aligned space.

Args

coords
Coordinates to inverse transform.

Returns

Inverse transformed coordinates.

def match_protein(self,
protein: Protein) ‑> bool
Expand source code
def match_protein(self, protein: Protein) -> bool:
    """
    Check if the protein matches the xref protein.

    Args:
        protein: Protein to check.

    Returns:
        True if the proteins match, False otherwise.
    """
    if protein != self.xref_protein:
        raise ValueError("Provided protein does not match xref protein.")

Check if the protein matches the xref protein.

Args

protein
Protein to check.

Returns

True if the proteins match, False otherwise.

def show_box(*args, **kwargs)
Expand source code
def wrapper(*args, **kwargs):
    html_visualization = func(*args, **kwargs)
    return JupyterViewer.visualize(html_visualization)
def transform(self, coords: numpy.ndarray) ‑> numpy.ndarray
Expand source code
def transform(self, coords: np.ndarray) -> np.ndarray:
    """
    Transform coordinates using the aligner if available.

    Args:
        coords (np.ndarray): Input coordinates to be transformed.

    Returns:
        np.ndarray: Transformed coordinates if aligner exists, original coordinates otherwise.
    """
    if self.aligner is None:
        return coords

    return self.aligner.align_structure(coords)

Transform coordinates using the aligner if available.

Args

coords : np.ndarray
Input coordinates to be transformed.

Returns

np.ndarray
Transformed coordinates if aligner exists, original coordinates otherwise.