Coverage for biobb_structure_utils/utils/closest_residues.py: 80%
84 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-14 19:03 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-14 19:03 +0000
1#!/usr/bin/env python3
3"""Module containing the ClosestResidues class and the command line interface."""
4import argparse
5import Bio.PDB
6from biobb_common.configuration import settings
7from biobb_common.generic.biobb_object import BiobbObject
8from biobb_common.tools import file_utils as fu
9from biobb_common.tools.file_utils import launchlogger
10from biobb_structure_utils.utils.common import check_input_path, check_output_path, create_residues_list, create_biopython_residue, create_output_file
13class ClosestResidues(BiobbObject):
14 """
15 | biobb_structure_utils ClosestResidues
16 | Class to search closest residues from a 3D structure using Biopython.
17 | Return all residues that have at least one atom within radius of center from a list of given residues.
19 Args:
20 input_structure_path (str): Input structure file path. File type: input. `Sample file <https://github.com/bioexcel/biobb_structure_utils/raw/master/biobb_structure_utils/test/data/utils/2vgb.pdb>`_. Accepted formats: pdb (edam:format_1476), pdbqt (edam:format_1476).
21 output_residues_path (str): Output molcules file path. File type: output. `Sample file <https://github.com/bioexcel/biobb_structure_utils/raw/master/biobb_structure_utils/test/reference/utils/ref_closest_residues.pdb>`_. Accepted formats: pdb (edam:format_1476), pdbqt (edam:format_1476).
22 properties (dic - Python dictionary object containing the tool parameters, not input/output files):
23 * **residues** (*list*) - (None) List of comma separated res_id or list of dictionaries with the name | res_id | chain | model of the residues to find the closest neighbours. Format: [{"name": "HIS", "res_id": "72", "chain": "A", "model": "1"}].
24 * **radius** (*float*) - (5) Distance in Ångströms to neighbours of the given list of residues.
25 * **preserve_target** (*bool*) - (True) Whether or not to preserve the target residues in the output structure.
26 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files.
27 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist.
28 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory.
30 Examples:
31 This is a use example of how to use the building block from Python::
33 from biobb_structure_utils.utils.closest_residues import closest_residues
34 prop = {
35 'residues': [
36 {
37 'name': 'HIS',
38 'res_id': '72',
39 'chain': 'A',
40 'model': '1'
41 }
42 ],
43 'radius': 5,
44 'preserve_target': False
45 }
46 closest_residues(input_structure_path='/path/to/myStructure.pdb',
47 output_residues_path='/path/to/newResidues.pdb',
48 properties=prop)
50 Info:
51 * wrapped_software:
52 * name: In house using Biopython
53 * version: >=1.79
54 * license: other
55 * ontology:
56 * name: EDAM
57 * schema: http://edamontology.org/EDAM.owl
59 """
61 def __init__(self, input_structure_path, output_residues_path, properties=None, **kwargs) -> None:
62 properties = properties or {}
64 # Call parent class constructor
65 super().__init__(properties)
66 self.locals_var_dict = locals().copy()
68 # Input/Output files
69 self.io_dict = {
70 "in": {"input_structure_path": input_structure_path},
71 "out": {"output_residues_path": output_residues_path}
72 }
74 # Properties specific for BB
75 self.residues = properties.get('residues', [])
76 self.radius = properties.get('radius', 5)
77 self.preserve_target = properties.get('preserve_target', True)
78 self.properties = properties
80 # Check the properties
81 self.check_properties(properties)
82 self.check_arguments()
84 @launchlogger
85 def launch(self) -> int:
86 """Execute the :class:`ClosestResidues <utils.closest_residues.ClosestResidues>` utils.closest_residues.ClosestResidues object."""
88 self.io_dict['in']['input_structure_path'] = check_input_path(self.io_dict['in']['input_structure_path'],
89 self.out_log, self.__class__.__name__)
90 self.io_dict['out']['output_residues_path'] = check_output_path(self.io_dict['out']['output_residues_path'],
91 self.out_log, self.__class__.__name__)
93 # Setup Biobb
94 if self.check_restart():
95 return 0
96 self.stage_files()
98 # Business code
99 # get list of Residues from properties
100 list_residues = create_residues_list(self.residues, self.out_log)
102 # load input into BioPython structure
103 structure = Bio.PDB.PDBParser(QUIET=True).get_structure('structure', self.stage_io_dict['in']['input_structure_path'])
105 str_residues = []
106 # format selected residues
107 for residue in structure.get_residues():
108 r = create_biopython_residue(residue)
109 if list_residues:
110 for res in list_residues:
111 match = True
112 for code in res['code']:
113 if res[code].strip() != r[code].strip():
114 match = False
115 break
116 if match:
117 str_residues.append(r)
118 else:
119 str_residues.append(r)
121 # get target residues in BioPython format
122 target_residues = []
123 for sr in str_residues:
124 # try for residues, if exception, try as HETATM
125 try:
126 target_residues.append(structure[int(sr['model']) - 1][sr['chain']][int(sr['res_id'])])
127 except KeyError:
128 target_residues.append(structure[int(sr['model']) - 1][sr['chain']]['H_' + sr['name'], int(sr['res_id']), ' '])
129 except Exception:
130 fu.log(self.__class__.__name__ + ': Unable to find residue %s', sr['res_id'], self.out_log)
132 # get all atoms from target_residues
133 target_atoms = Bio.PDB.Selection.unfold_entities(target_residues, 'A')
134 # get all atoms of input structure
135 all_atoms = Bio.PDB.Selection.unfold_entities(structure, 'A')
136 # generate NeighborSearch object
137 ns = Bio.PDB.NeighborSearch(all_atoms)
138 # set comprehension list
139 nearby_residues = {res for center_atom in target_atoms
140 for res in ns.search(center_atom.coord, self.radius, 'R')}
142 # format nearby residues to pure python objects
143 neighbor_residues = []
144 for residue in nearby_residues:
145 r = create_biopython_residue(residue)
146 neighbor_residues.append(r)
148 # if preserve_target == False, don't add the residues of self.residues to the final structure
149 if not self.preserve_target:
150 neighbor_residues = [x for x in neighbor_residues if x not in str_residues]
152 fu.log('Found %d nearby residues' % len(neighbor_residues), self.out_log)
154 if len(neighbor_residues) == 0:
155 fu.log(self.__class__.__name__ + ': No neighbour residues found, exiting', self.out_log)
156 raise SystemExit(self.__class__.__name__ + ': No neighbour residues found, exiting')
158 create_output_file(0, self.stage_io_dict['in']['input_structure_path'], neighbor_residues, self.stage_io_dict['out']['output_residues_path'], self.out_log)
160 self.return_code = 0
162 # Copy files to host
163 self.copy_to_host()
165 # Remove temporal files
166 self.tmp_files.append(self.stage_io_dict.get("unique_dir"))
167 self.remove_tmp_files()
169 self.check_arguments(output_files_created=True, raise_exception=False)
171 return self.return_code
174def closest_residues(input_structure_path: str, output_residues_path: str, properties: dict = None, **kwargs) -> int:
175 """Execute the :class:`ClosestResidues <utils.closest_residues.ClosestResidues>` class and
176 execute the :meth:`launch() <utils.closest_residues.ClosestResidues.launch>` method."""
178 return ClosestResidues(input_structure_path=input_structure_path,
179 output_residues_path=output_residues_path,
180 properties=properties, **kwargs).launch()
183def main():
184 """Command line execution of this building block. Please check the command line documentation."""
185 parser = argparse.ArgumentParser(description="Search closest residues to a list of given residues.", formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, width=99999))
186 parser.add_argument('-c', '--config', required=False, help="This file can be a YAML file, JSON file or JSON string")
188 # Specific args of each building block
189 required_args = parser.add_argument_group('required arguments')
190 required_args.add_argument('-i', '--input_structure_path', required=True, help="Input structure file path. Accepted formats: pdb.")
191 required_args.add_argument('-o', '--output_residues_path', required=True, help="Output residues file path. Accepted formats: pdb.")
193 args = parser.parse_args()
194 config = args.config if args.config else None
195 properties = settings.ConfReader(config=config).get_prop_dic()
197 # Specific call of each building block
198 closest_residues(input_structure_path=args.input_structure_path,
199 output_residues_path=args.output_residues_path,
200 properties=properties)
203if __name__ == '__main__':
204 main()