Coverage for biobb_pmx/pmxbiobb/pmxatom_mapping.py: 60%

122 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-23 10:10 +0000

1#!/usr/bin/env python3 

2 

3"""Module containing the PMX atom_mapping class and the command line interface.""" 

4 

5import argparse 

6import os 

7import shutil 

8import sys 

9from pathlib import Path 

10from typing import Optional 

11 

12from biobb_common.configuration import settings 

13from biobb_common.generic.biobb_object import BiobbObject 

14from biobb_common.tools.file_utils import launchlogger 

15 

16 

17class Pmxatom_mapping(BiobbObject): 

18 """ 

19 | biobb_pmx Pmxatom_mapping 

20 | Wrapper class for the `PMX atom_mapping <https://github.com/deGrootLab/pmx>`_ module. 

21 | Perform atom mapping between two ligand structures. 

22 

23 Args: 

24 input_structure1_path (str): Path to the input ligand structure file 1. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

25 input_structure2_path (str): Path to the input ligand structure file 2. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

26 output_pairs1_path (str): Path to the output pairs for the ligand structure 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_mapping_pairs.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330). 

27 output_pairs2_path (str): Path to the output pairs for the ligand structure 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_mapping_pairs.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330). 

28 output_log_path (str): Path to the log file. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/atom_mapping.log>`_. Accepted formats: log (edam:format_2330), txt (edam:format_2330), out (edam:format_2330). 

29 output_structure1_path (str) (Optional): Path to the superimposed structure for the ligand structure 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/superimposed_ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

30 output_structure2_path (str) (Optional): Path to the superimposed structure for the ligand structure 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/superimposed_ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

31 output_morph1_path (str) (Optional): Path to the morphable atoms for the ligand structure 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/superimposed_ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

32 output_morph2_path (str) (Optional): Path to the morphable atoms for the ligand structure 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/superimposed_ligand.pdb>`_. Accepted formats: pdb (edam:format_1476). 

33 output_scaffold1_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/atoms_to_consider.ndx>`_. Accepted formats: ndx (edam:format_2033). 

34 output_scaffold2_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/atoms_to_consider.ndx>`_. Accepted formats: ndx (edam:format_2033). 

35 output_score_path (str) (Optional): Path to the morphing score. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/morph_score.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330). 

36 

37 properties (dic): 

38 * **noalignment** (*bool*) - (False) Should the alignment method be disabled. 

39 * **nomcs** (*bool*) - (False) Should the MCS method be disabled. 

40 * **noH2H** (*bool*) - (True) Should non-polar hydrogens be discarded from morphing into any other hydrogen. 

41 * **H2Hpolar** (*bool*) - (False) Should polar hydrogens be morphed into polar hydrogens. 

42 * **H2Heavy** (*bool*) - (False) Should hydrogen be morphed into a heavy atom. 

43 * **RingsOnly** (*bool*) - (False) Should rings only be used in the MCS search and alignemnt. 

44 * **dMCS** (*bool*) - (False) Should the distance criterium be also applied in the MCS based search. 

45 * **swap** (*bool*) - (False) Try swapping the molecule order which would be a cross-check and require double execution time. 

46 * **nochirality** (*bool*) - (True) Perform chirality check for MCS mapping. 

47 * **distance** (*float*) - (0.05) Distance (nm) between atoms to consider them morphable for alignment approach. 

48 * **timeout** (*int*) - (10) Maximum time (s) for an MCS search. 

49 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files. 

50 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist. 

51 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory. 

52 * **container_path** (*str*) - (None) Path to the binary executable of your container. 

53 * **container_image** (*str*) - (None) Container Image identifier. 

54 * **container_volume_path** (*str*) - ("/inout") Path to an internal directory in the container. 

55 * **container_working_dir** (*str*) - (None) Path to the internal CWD in the container. 

56 * **container_user_id** (*str*) - (None) User number id to be mapped inside the container. 

57 * **container_shell_path** (*str*) - ("/bin/bash") Path to the binary executable of the container shell. 

58 

59 Examples: 

60 This is a use example of how to use the building block from Python:: 

61 

62 from biobb_pmx.pmxbiobb.pmxatom_mapping import pmxatom_mapping 

63 prop = { 

64 'no-alignment' : True, 

65 'distance': 0.05 

66 } 

67 pmxatom_mapping(input_structure1_path='/path/to/myStructure1.pdb', 

68 input_structure2_path='/path/to/myStructure2.pdb', 

69 output_pairs1_path='/path/to/myPairs1.dat', 

70 output_pairs2_path='/path/to/myPairs2.dat', 

71 output_log_path='/path/to/myLog.log', 

72 properties=prop) 

73 

74 Info: 

75 * wrapped_software: 

76 * name: PMX atom_mapping 

77 * version: >=1.0.1 

78 * license: GNU 

79 * ontology: 

80 * name: EDAM 

81 * schema: http://edamontology.org/EDAM.owl 

82 

83 """ 

84 

85 def __init__( 

86 self, 

87 input_structure1_path: str, 

88 input_structure2_path: str, 

89 output_pairs1_path: str, 

90 output_pairs2_path: str, 

91 output_log_path: str, 

92 output_structure1_path: Optional[str] = None, 

93 output_structure2_path: Optional[str] = None, 

94 output_morph1_path: Optional[str] = None, 

95 output_morph2_path: Optional[str] = None, 

96 output_scaffold1_path: Optional[str] = None, 

97 output_scaffold2_path: Optional[str] = None, 

98 output_score_path: Optional[str] = None, 

99 properties: Optional[dict] = None, 

100 **kwargs, 

101 ) -> None: 

102 properties = properties or {} 

103 

104 # Call parent class constructor 

105 super().__init__(properties) 

106 self.locals_var_dict = locals().copy() 

107 

108 # Input/Output files 

109 self.io_dict = { 

110 "in": { 

111 "input_structure1_path": input_structure1_path, 

112 "input_structure2_path": input_structure2_path, 

113 }, 

114 "out": { 

115 "output_pairs1_path": output_pairs1_path, 

116 "output_pairs2_path": output_pairs2_path, 

117 "output_log_path": output_log_path, 

118 "output_structure1_path": output_structure1_path, 

119 "output_structure2_path": output_structure2_path, 

120 "output_morph1_path": output_morph1_path, 

121 "output_morph2_path": output_morph2_path, 

122 "output_scaffold1_path": output_scaffold1_path, 

123 "output_scaffold2_path": output_scaffold2_path, 

124 "output_score_path": output_score_path, 

125 }, 

126 } 

127 

128 # Properties specific for BB 

129 # self.noalignment = properties.get('noalignment', False) 

130 # self.nomcs = properties.get('nomcs', False) 

131 # self.noH2H = properties.get('noH2H', True) 

132 # self.H2Hpolar = properties.get('H2Hpolar', False) 

133 # self.H2Heavy = properties.get('H2Heavy', False) 

134 # self.RingsOnly = properties.get('RingsOnly', False) 

135 # self.dMCS = properties.get('dMCS', False) 

136 # self.swap = properties.get('swap', False) 

137 # self.nochirality = properties.get('nochirality', True) 

138 # self.distance = properties.get('distance', 0.05) 

139 # self.timeout = properties.get('timeout', 10) 

140 

141 self.noalignment = properties.get("noalignment") 

142 self.nomcs = properties.get("nomcs") 

143 self.noH2H = properties.get("noH2H") 

144 self.H2Hpolar = properties.get("H2Hpolar") 

145 self.H2Heavy = properties.get("H2Heavy") 

146 self.RingsOnly = properties.get("RingsOnly") 

147 self.dMCS = properties.get("dMCS") 

148 self.swap = properties.get("swap") 

149 self.nochirality = properties.get("nochirality") 

150 self.distance = properties.get("distance") 

151 self.timeout = properties.get("timeout") 

152 

153 # Properties common in all PMX BB 

154 self.gmx_lib = properties.get("gmx_lib", None) 

155 if not self.gmx_lib and os.environ.get("CONDA_PREFIX", ""): 

156 python_version = f"{sys.version_info.major}.{sys.version_info.minor}" 

157 self.gmx_lib = str( 

158 Path(os.environ.get("CONDA_PREFIX", "")).joinpath( 

159 f"lib/python{python_version}/site-packages/pmx/data/mutff/" 

160 ) 

161 ) 

162 if properties.get("container_path"): 

163 self.gmx_lib = str( 

164 Path("/usr/local/").joinpath( 

165 "lib/python3.7/site-packages/pmx/data/mutff/" 

166 ) 

167 ) 

168 self.binary_path = properties.get("binary_path", "pmx") 

169 

170 # Check the properties 

171 self.check_properties(properties) 

172 self.check_arguments() 

173 

174 @launchlogger 

175 def launch(self) -> int: 

176 """Execute the :class:`Pmxmutate <pmx.pmxmutate.Pmxmutate>` pmx.pmxmutate.Pmxmutate object.""" 

177 

178 # Setup Biobb 

179 if self.check_restart(): 

180 return 0 

181 self.stage_files() 

182 

183 # Check if executable exists 

184 if not self.container_path: 

185 if not Path(self.binary_path).is_file(): 

186 if not shutil.which(self.binary_path): 

187 raise FileNotFoundError( 

188 "Executable %s not found. Check if it is installed in your system and correctly defined in the properties" 

189 % self.binary_path 

190 ) 

191 

192 self.cmd = [ 

193 self.binary_path, 

194 "atomMapping", 

195 "-i1", 

196 self.stage_io_dict["in"]["input_structure1_path"], 

197 "-i2", 

198 self.stage_io_dict["in"]["input_structure2_path"], 

199 "-o1", 

200 self.stage_io_dict["out"]["output_pairs1_path"], 

201 "-o2", 

202 self.stage_io_dict["out"]["output_pairs2_path"], 

203 "-log", 

204 self.stage_io_dict["out"]["output_log_path"], 

205 ] 

206 

207 if self.stage_io_dict["out"].get("output_structure1_path"): 

208 self.cmd.append("-opdb1") 

209 self.cmd.append(self.stage_io_dict["out"]["output_structure1_path"]) 

210 

211 if self.stage_io_dict["out"].get("output_structure2_path"): 

212 self.cmd.append("-opdb2") 

213 self.cmd.append(self.stage_io_dict["out"]["output_structure2_path"]) 

214 

215 if self.stage_io_dict["out"].get("output_morph1_path"): 

216 self.cmd.append("-opdbm1") 

217 self.cmd.append(self.stage_io_dict["out"]["output_morph1_path"]) 

218 

219 if self.stage_io_dict["out"].get("output_morph2_path"): 

220 self.cmd.append("-opdbm2") 

221 self.cmd.append(self.stage_io_dict["out"]["output_morph2_path"]) 

222 

223 if self.stage_io_dict["out"].get("output_scaffold1_path"): 

224 self.cmd.append("-n1") 

225 self.cmd.append(self.stage_io_dict["out"]["output_scaffold1_path"]) 

226 

227 if self.stage_io_dict["out"].get("output_scaffold2_path"): 

228 self.cmd.append("-n2") 

229 self.cmd.append(self.stage_io_dict["out"]["output_scaffold2_path"]) 

230 

231 if self.stage_io_dict["out"].get("output_score_path"): 

232 self.cmd.append("-score") 

233 self.cmd.append(self.stage_io_dict["out"]["output_score_path"]) 

234 

235 if self.noalignment: 

236 self.cmd.append("--no-alignment") 

237 if self.nomcs: 

238 self.cmd.append("--no-mcs") 

239 if self.noH2H: 

240 self.cmd.append("--no-H2H") 

241 if self.H2Hpolar: 

242 self.cmd.append("--H2Hpolar") 

243 if self.H2Heavy: 

244 self.cmd.append("--H2Heavy") 

245 if self.RingsOnly: 

246 self.cmd.append("--RingsOnly") 

247 if self.dMCS: 

248 self.cmd.append("--dMCS") 

249 if self.swap: 

250 self.cmd.append("--swap") 

251 if self.nochirality: 

252 self.cmd.append("--no-chirality") 

253 if self.distance: 

254 self.cmd.append("--d") 

255 self.cmd.append(str(self.distance)) 

256 if self.timeout: 

257 self.cmd.append("--timeout") 

258 self.cmd.append(str(self.timeout)) 

259 

260 if self.gmx_lib: 

261 self.env_vars_dict["GMXLIB"] = self.gmx_lib 

262 

263 # Run Biobb block 

264 self.run_biobb() 

265 

266 # Copy files to host 

267 self.copy_to_host() 

268 

269 # self.tmp_files.append(self.stage_io_dict.get("unique_dir", "")) 

270 self.remove_tmp_files() 

271 

272 self.check_arguments(output_files_created=True, raise_exception=False) 

273 return self.return_code 

274 

275 

276def pmxatom_mapping( 

277 input_structure1_path: str, 

278 input_structure2_path: str, 

279 output_pairs1_path: str, 

280 output_pairs2_path: str, 

281 output_log_path: str, 

282 output_structure1_path: Optional[str] = None, 

283 output_structure2_path: Optional[str] = None, 

284 output_morph1_path: Optional[str] = None, 

285 output_morph2_path: Optional[str] = None, 

286 output_scaffold1_path: Optional[str] = None, 

287 output_scaffold2_path: Optional[str] = None, 

288 output_score_path: Optional[str] = None, 

289 properties: Optional[dict] = None, 

290 **kwargs, 

291) -> int: 

292 """Execute the :class:`Pmxatom_mapping <pmx.pmxmutate.Pmxatom_mapping>` class and 

293 execute the :meth:`launch() <pmx.pmxatom_mapping.Pmxatom_mapping.launch> method.""" 

294 

295 return Pmxatom_mapping( 

296 input_structure1_path=input_structure1_path, 

297 input_structure2_path=input_structure2_path, 

298 output_pairs1_path=output_pairs1_path, 

299 output_pairs2_path=output_pairs2_path, 

300 output_log_path=output_log_path, 

301 output_structure1_path=output_structure1_path, 

302 output_structure2_path=output_structure2_path, 

303 output_morph1_path=output_morph1_path, 

304 output_morph2_path=output_morph2_path, 

305 output_scaffold1_path=output_scaffold1_path, 

306 output_scaffold2_path=output_scaffold2_path, 

307 output_score_path=output_score_path, 

308 properties=properties, 

309 **kwargs, 

310 ).launch() 

311 

312 pmxatom_mapping.__doc__ = Pmxatom_mapping.__doc__ 

313 

314 

315def main(): 

316 """Command line execution of this building block. Please check the command line documentation.""" 

317 parser = argparse.ArgumentParser( 

318 description="Run PMX atom mapping module", 

319 formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, width=99999), 

320 ) 

321 parser.add_argument( 

322 "-c", 

323 "--config", 

324 required=False, 

325 help="This file can be a YAML file, JSON file or JSON string", 

326 ) 

327 

328 # Specific args of each building block 

329 required_args = parser.add_argument_group("required arguments") 

330 required_args.add_argument( 

331 "--input_structure1_path", 

332 required=True, 

333 help="Path to the input ligand structure file 1", 

334 ) 

335 required_args.add_argument( 

336 "--input_structure2_path", 

337 required=True, 

338 help="Path to the input ligand structure file 2", 

339 ) 

340 required_args.add_argument( 

341 "--output_pairs1_path", 

342 required=True, 

343 help="Path to the output pairs for the ligand structure 1", 

344 ) 

345 required_args.add_argument( 

346 "--output_pairs2_path", 

347 required=True, 

348 help="Path to the output pairs for the ligand structure 2", 

349 ) 

350 required_args.add_argument( 

351 "--output_log_path", required=True, help="Path to the log file" 

352 ) 

353 parser.add_argument( 

354 "--output_structure1_path", 

355 required=False, 

356 help="Path to the superimposed structure for the ligand structure 1", 

357 ) 

358 parser.add_argument( 

359 "--output_structure2_path", 

360 required=False, 

361 help="Path to the superimposed structure for the ligand structure 2", 

362 ) 

363 parser.add_argument( 

364 "--output_morph1_path", 

365 required=False, 

366 help="Path to the morphable atoms for the ligand structure 1", 

367 ) 

368 parser.add_argument( 

369 "--output_morph2_path", 

370 required=False, 

371 help="Path to the morphable atoms for the ligand structure 2", 

372 ) 

373 parser.add_argument( 

374 "--output_scaffold1_path", 

375 required=False, 

376 help="Path to the index of atoms to consider for the ligand structure 1", 

377 ) 

378 parser.add_argument( 

379 "--output_scaffold2_path", 

380 required=False, 

381 help="Path to the index of atoms to consider for the ligand structure 2", 

382 ) 

383 parser.add_argument( 

384 "--output_score_path", 

385 required=False, 

386 help="Path to the morphing score. File type: output", 

387 ) 

388 

389 args = parser.parse_args() 

390 config = args.config if args.config else None 

391 properties = settings.ConfReader(config=config).get_prop_dic() 

392 

393 # Specific call of each building block 

394 pmxatom_mapping( 

395 input_structure1_path=args.input_structure1_path, 

396 input_structure2_path=args.input_structure2_path, 

397 output_pairs1_path=args.output_pairs1_path, 

398 output_pairs2_path=args.output_pairs2_path, 

399 output_log_path=args.output_log_path, 

400 output_structure1_path=args.output_structure1_path, 

401 output_structure2_path=args.output_structure2_path, 

402 output_morph1_path=args.output_morph1_path, 

403 output_morph2_path=args.output_morph2_path, 

404 output_scaffold1_path=args.output_scaffold1_path, 

405 output_scaffold2_path=args.output_scaffold2_path, 

406 output_score_path=args.output_score_path, 

407 properties=properties, 

408 ) 

409 

410 

411if __name__ == "__main__": 

412 main()