Source code for docktopus.vina_engine

from pathlib import Path
from typing import Optional, Dict, Tuple, List
import logging


[docs] class VinaDockingEngine: """ Vina-specific docking engine implementation using the vina Python interface. This class provides an interface to AutoDock Vina, a popular molecular docking program that uses an empirical scoring function based on the AutoDock 4 force field. Vina is known for its speed and accuracy in structure-based drug design. Vina features: - Empirical scoring function (Vina scoring) - Fast conformational search using iterated local search - Support for flexible ligand docking - Automatic binding site detection - Multiple output poses with binding affinities Attributes: work_dir (Path): Directory for docking outputs receptor_format (str): Expected receptor file format ("pdbqt") ligand_format (str): Expected ligand file format ("pdbqt") exhaustiveness (int): Search exhaustiveness parameter num_modes (int): Number of binding modes to generate cpu (int): Number of CPU cores to use seed (int): Random seed for reproducibility vina: Vina object from the vina Python package logger (logging.Logger): Logger instance for engine events """
[docs] def __init__(self, work_dir: str, exhaustiveness: int = 8, num_modes: int = 9, cpu: int = 4, seed: int = 0): """ Initialize Vina docking engine. Args: work_dir (str): Directory for docking outputs. Will be created if it doesn't exist. exhaustiveness (int, optional): Search exhaustiveness (higher values give more thorough but slower searches). Defaults to 8. num_modes (int, optional): Number of binding modes to generate. Defaults to 9. cpu (int, optional): Number of CPU cores to use for docking. Defaults to 4. seed (int, optional): Random seed for reproducibility. Defaults to 0. Raises: ImportError: If vina Python package is not available ValueError: If invalid parameters are provided Example: >>> engine = VinaDockingEngine( ... work_dir='./docking_results', ... box_center=(15.2, 23.1, 18.7), ... box_size=(25.0, 25.0, 25.0), ... exhaustiveness=16, ... num_modes=20 ... ) Note: Requires the vina Python package to be installed: pip install vina """ self.work_dir = Path(work_dir) self.work_dir.mkdir(parents=True, exist_ok=True) self.box_center = None self.box_size = None self.exhaustiveness = exhaustiveness self.num_modes = num_modes self.cpu = cpu self.seed = seed self.receptor_format = "pdbqt" self.ligand_format = "pdbqt" # Configure logging self.logger = logging.getLogger(__name__) self.logger.setLevel(logging.INFO) try: from vina import Vina except ImportError as e: self.logger.error(f"Failed to import vina package: {e}") # Initialize Vina object self.vina = Vina(sf_name='vina', seed=self.seed, cpu=self.cpu)
[docs] def dock(self, receptor_file: str, box_center: Tuple[float, float, float], ligand_file: str, box_size: Tuple[float, float, float] = (30.0, 30.0, 30.0), output_prefix: Optional[str] = None ) -> Dict[str, float]: """ Perform docking using Vina. This method executes AutoDock Vina docking with the specified parameters and returns results including multiple poses with binding affinities. Args: receptor_file (str): Path to prepared receptor file (PDBQT format) ligand_file (str): Path to prepared ligand file (PDBQT format) box_center (Tuple[float, float, float]): (x,y,z) coordinates of docking box center. If None, uses ligand center. Defaults to None. box_size (Tuple[float, float, float]): (x,y,z) dimensions of search box in Angstroms. Defaults to (30.0, 30.0, 30.0). output_prefix (Optional[str], optional): Prefix for output files. If None, uses the ligand filename stem. Defaults to None. Returns: Dict[str, Any]: Dictionary containing docking results with keys: - output_file: Path to PDBQT file with docked poses - log_file: Path to Vina log file with detailed output - scores: List of dictionaries, each containing scores for one pose: - pose: Pose number (1-based) - affinity: Binding affinity in kcal/mol Raises: FileNotFoundError: If input files are not found RuntimeError: If Vina execution fails ImportError: If vina package is not available Note: - Receptor and ligand must be in PDBQT format - PDBQT format includes atom types, charges, and rotatable bonds - Box parameters are applied as specified during initialization - All poses are saved in a single PDBQT file - Log file contains detailed Vina output and diagnostics """ if output_prefix is None: output_prefix = Path(ligand_file).stem output_pdbqt = self.work_dir / f"{output_prefix}_docked.pdbqt" output_log = self.work_dir / f"{output_prefix}_docked.log" try: # Load receptor self.vina.set_receptor(receptor_file) # Load ligand self.vina.set_ligand_from_file(ligand_file) # Set box parameters if box_center is not None: self.vina.compute_vina_maps( center=box_center, box_size=box_size ) else: # If no box center provided, use ligand center self.logger.info("No box center provided, using ligand center") self.vina.compute_vina_maps( center='ligand', box_size=box_size ) # Run docking self.vina.dock( exhaustiveness=self.exhaustiveness, n_poses=self.num_modes, ) # Write output self.vina.write_poses(str(output_pdbqt), n_poses=self.num_modes, overwrite=True) # Get scores scores = self._get_scores() # self.logger.info(f"Docking completed successfully. Writing output to {output_pdbqt}") # Write log file with scores self._write_log(output_log, scores) return { "output_file": str(output_pdbqt), "log_file": str(output_log), "scores": scores } except Exception as e: self.logger.error(f"Docking failed: {str(e)}") raise
def _get_scores(self) -> List[Dict[str, float]]: """ Get docking scores for all poses. This method extracts binding affinities from the Vina object for all generated poses. The scores are returned in order of binding affinity (best first). Returns: List[Dict[str, float]]: List of score dictionaries, one for each pose. Each dictionary contains: - pose: Pose number (1-based indexing) - affinity: Binding affinity in kcal/mol (lower is better) Note: - Scores are ordered by binding affinity (best first) - Affinity values are in kcal/mol (negative values indicate favorable binding) - Number of poses depends on num_modes parameter """ energies = self.vina.energies() print(energies) scores = [] for i, energy in enumerate(energies): scores.append({ "pose": i + 1, "affinity": energy[0] # Binding affinity in kcal/mol }) return scores def _write_log(self, log_file: Path, scores: List[Dict[str, float]]) -> None: """ Write docking scores to log file. This method creates a human-readable log file containing the docking results and scores for each pose. Args: log_file (Path): Path to output log file scores (List[Dict[str, float]]): List of score dictionaries from _get_scores() Raises: IOError: If log file cannot be written Note: - Creates a simple text format log file - Includes pose numbers and binding affinities - Useful for manual inspection of results """ try: with open(log_file, 'w') as f: f.write("Vina Docking Results\n") f.write("-" * 20 + "\n\n") for score in scores: f.write(f"Pose {score['pose']} affinity: {score['affinity']:.1f} kcal/mol\n") except Exception as e: self.logger.error(f"Failed to write log file: {str(e)}")
[docs] def precheck(self, file_path: str) -> bool: """ Check if the provided file path exists. This method performs a simple file existence check, which is useful for validating input files before attempting docking calculations. Runs automatically before each docking to make sure you have all the files you think you have. It does not check if those files are correct. Args: file_path (str): Path to the file to check Returns: bool: True if the file exists, False otherwise Note: - Only checks file existence, not file validity - Does not verify file format or content - Useful for basic input validation """ return Path(file_path).exists()