22# under the 3-Clause BSD License. Please see 'LICENSE.rst' for further
33# information.
44
5+ from __future__ import annotations
6+
57__name__ = "biotite.application.autodock"
68__author__ = "Patrick Kunzmann"
79__all__ = ["VinaApp" ]
810
911import copy
12+ from os import PathLike
1013from tempfile import NamedTemporaryFile
14+ from typing import Any
1115import numpy as np
1216from biotite .application .application import AppState , requires_state
1317from biotite .application .localapp import LocalApp , cleanup_tempfile
18+ from biotite .structure .atoms import AtomArray , AtomArrayStack
1419from biotite .structure .connect import find_connected
1520from biotite .structure .error import BadStructureError
1621from biotite .structure .io .pdbqt import PDBQTFile
1722from biotite .structure .residues import get_residue_masks , get_residue_starts_for
23+ from biotite .typing import XYZ , K , M , N , NDArray1 , NDArray3
1824
1925
2026class VinaApp (LocalApp ):
@@ -24,7 +30,7 @@ class VinaApp(LocalApp):
2430 Parameters
2531 ----------
2632 ligand : AtomArray
27- The structure of the receptor molecule.
33+ The structure of the ligand molecule.
2834 Must have an associated :class:`BondList`.
2935 An associated ``charge`` annotation is recommended for proper
3036 calculation of partial charges.
@@ -63,25 +69,33 @@ class VinaApp(LocalApp):
6369 ... )
6470 """
6571
66- def __init__ (self , ligand , receptor , center , size , flexible = None , bin_path = "vina" ):
72+ def __init__ (
73+ self ,
74+ ligand : AtomArray [Any ],
75+ receptor : AtomArray [N ],
76+ center : NDArray1 [XYZ , np .floating ],
77+ size : NDArray1 [XYZ , np .floating ],
78+ flexible : NDArray1 [N , np .bool_ ] | None = None ,
79+ bin_path : PathLike [str ] | str = "vina" ,
80+ ) -> None :
6781 super ().__init__ (bin_path )
6882
6983 if ligand .bonds is None :
7084 raise ValueError ("The ligand has no associated BondList" )
7185 if receptor .bonds is None :
7286 raise ValueError ("The receptor has no associated BondList" )
7387
74- self ._ligand = ligand .copy ()
75- self ._receptor = receptor .copy ()
76- self ._center = copy .deepcopy (center )
77- self ._size = copy .deepcopy (size )
78- self ._is_flexible = flexible is not None
79- self ._seed = None
80- self ._exhaustiveness = None
81- self ._number = None
82- self ._energy_range = None
83-
84- if self . _is_flexible :
88+ self ._ligand : AtomArray [ Any ] = ligand .copy ()
89+ self ._receptor : AtomArray [ Any ] = receptor .copy ()
90+ self ._center : NDArray1 [ Any , np . floating ] = copy .deepcopy (center )
91+ self ._size : NDArray1 [ Any , np . floating ] = copy .deepcopy (size )
92+ self ._is_flexible : bool = flexible is not None
93+ self ._seed : int | None = None
94+ self ._exhaustiveness : int | None = None
95+ self ._number : int | None = None
96+ self ._energy_range : float | None = None
97+
98+ if flexible is not None :
8599 flexible_indices = np .where (flexible )[0 ]
86100 self ._flex_res_starts = np .unique (
87101 get_residue_starts_for (receptor , flexible_indices )
@@ -95,7 +109,7 @@ def __init__(self, ligand, receptor, center, size, flexible=None, bin_path="vina
95109 self ._out_file = NamedTemporaryFile ("r" , suffix = ".pdbqt" , delete = False )
96110
97111 @requires_state (AppState .CREATED )
98- def set_seed (self , seed ) :
112+ def set_seed (self , seed : int ) -> None :
99113 """
100114 Fix the seed for the random number generator to get
101115 reproducible results.
@@ -110,7 +124,7 @@ def set_seed(self, seed):
110124 self ._seed = seed
111125
112126 @requires_state (AppState .CREATED )
113- def set_exhaustiveness (self , exhaustiveness ) :
127+ def set_exhaustiveness (self , exhaustiveness : int ) -> None :
114128 """
115129 Set the *exhaustiveness* parameter for *Vina*.
116130
@@ -127,7 +141,7 @@ def set_exhaustiveness(self, exhaustiveness):
127141 self ._exhaustiveness = exhaustiveness
128142
129143 @requires_state (AppState .CREATED )
130- def set_max_number_of_models (self , number ) :
144+ def set_max_number_of_models (self , number : int ) -> None :
131145 """
132146 Set the maximum number of binding modes to generate.
133147
@@ -143,7 +157,7 @@ def set_max_number_of_models(self, number):
143157 self ._number = number
144158
145159 @requires_state (AppState .CREATED )
146- def set_energy_range (self , energy_range ) :
160+ def set_energy_range (self , energy_range : float ) -> None :
147161 """
148162 Set the maximum energy range of the generated models.
149163
@@ -158,7 +172,7 @@ def set_energy_range(self, energy_range):
158172 """
159173 self ._energy_range = energy_range
160174
161- def run (self ):
175+ def run (self ) -> None :
162176 # Use different atom ID ranges for atoms in ligand and receptor
163177 # for unambiguous assignment, if the receptor contains flexible
164178 # residues
@@ -258,7 +272,7 @@ def run(self):
258272 self .set_arguments (arguments )
259273 super ().run ()
260274
261- def evaluate (self ):
275+ def evaluate (self ) -> None :
262276 super ().evaluate ()
263277 out_file = PDBQTFile .read (self ._out_file )
264278
@@ -276,15 +290,15 @@ def evaluate(self):
276290 [float (remark [12 :].split ()[0 ]) for remark in remarks ]
277291 )
278292
279- def clean_up (self ):
293+ def clean_up (self ) -> None :
280294 super ().clean_up ()
281295 cleanup_tempfile (self ._ligand_file )
282296 cleanup_tempfile (self ._receptor_file )
283297 cleanup_tempfile (self ._receptor_flex_file )
284298 cleanup_tempfile (self ._out_file )
285299
286300 @requires_state (AppState .JOINED )
287- def get_energies (self ):
301+ def get_energies (self ) -> NDArray1 [ K , np . floating ] :
288302 """
289303 Get the predicted binding energy for each generated binding
290304 mode.
@@ -298,7 +312,7 @@ def get_energies(self):
298312 return self ._energies
299313
300314 @requires_state (AppState .JOINED )
301- def get_ligand_models (self ):
315+ def get_ligand_models (self ) -> AtomArrayStack [ Any , Any ] :
302316 """
303317 Get the ligand structure with the conformations for each
304318 generated binding mode.
@@ -321,7 +335,7 @@ def get_ligand_models(self):
321335 return self ._ligand_models
322336
323337 @requires_state (AppState .JOINED )
324- def get_ligand_coord (self ):
338+ def get_ligand_coord (self ) -> NDArray3 [ M , N , XYZ , np . floating ] :
325339 """
326340 Get the ligand coordinates for each generated binding mode.
327341
@@ -342,7 +356,7 @@ def get_ligand_coord(self):
342356 return coord
343357
344358 @requires_state (AppState .JOINED )
345- def get_flexible_residue_models (self ):
359+ def get_flexible_residue_models (self ) -> AtomArrayStack [ Any , Any ] :
346360 """
347361 Get the structure for the flexible side chains with the
348362 conformations for each generated binding mode.
@@ -368,7 +382,7 @@ def get_flexible_residue_models(self):
368382 return self ._flex_models
369383
370384 @requires_state (AppState .JOINED )
371- def get_receptor_coord (self ):
385+ def get_receptor_coord (self ) -> NDArray3 [ M , N , XYZ , np . floating ] :
372386 """
373387 Get the get_receptor_coord coordinates for each generated
374388 binding mode.
@@ -401,7 +415,9 @@ def get_receptor_coord(self):
401415 coord [:, self ._receptor_mask ] = self ._flex_models .coord
402416 return coord
403417
404- def _get_flexible_residue (self , residue_start ):
418+ def _get_flexible_residue (
419+ self , residue_start : int
420+ ) -> tuple [np .ndarray , np .ndarray , int ]:
405421 residue_indices = np .where (
406422 get_residue_masks (self ._receptor , [residue_start ])[0 ]
407423 )[0 ]
@@ -417,6 +433,8 @@ def _get_flexible_residue(self, residue_start):
417433
418434 # Find the index of the atom connected to root on the flexible
419435 # side chain (CB)
436+ if self ._receptor .bonds is None :
437+ raise ValueError ("The receptor has no associated BondList" )
420438 root_connect_indices , _ = self ._receptor .bonds .get_bonds (root_index )
421439 connected_index = None
422440 try :
@@ -450,7 +468,14 @@ def _get_flexible_residue(self, residue_start):
450468 return flex_mask , rigid_mask , root_index
451469
452470 @staticmethod
453- def dock (ligand , receptor , center , size , flexible = None , bin_path = "vina" ):
471+ def dock (
472+ ligand : AtomArray [Any ],
473+ receptor : AtomArray [N ],
474+ center : NDArray1 [XYZ , np .floating ],
475+ size : NDArray1 [XYZ , np .floating ],
476+ flexible : NDArray1 [N , np .bool_ ] | None = None ,
477+ bin_path : PathLike [str ] | str = "vina" ,
478+ ) -> tuple [NDArray3 [M , N , XYZ , np .floating ], NDArray1 [K , np .floating ]]:
454479 """
455480 Dock a ligand to a receptor molecule using *AutoDock Vina*.
456481
@@ -460,7 +485,7 @@ def dock(ligand, receptor, center, size, flexible=None, bin_path="vina"):
460485 Parameters
461486 ----------
462487 ligand : AtomArray
463- The structure of the receptor molecule.
488+ The structure of the ligand molecule.
464489 Must have an associated :class:`BondList`.
465490 An associated ``charge`` annotation is recommended for proper
466491 calculation of partial charges.
0 commit comments