Coverage for biobb_pmx/pmxbiobb/pmxligand_hybrid.py: 63%

97 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 ligand_hybrid 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 Pmxligand_hybrid(BiobbObject): 

18 """ 

19 | biobb_pmx Pmxligand_hybrid 

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

21 | Create a hybrid topology and structure based on 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 input_topology1_path (str): Path to the input ligand topology file 1. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/ligand.itp>`_. Accepted formats: itp (edam:format_3883). 

27 input_topology2_path (str): Path to the input ligand topology file 2. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/ligand.itp>`_. Accepted formats: itp (edam:format_3883). 

28 input_pairs_path (str) (Optional): Path to the input atom pair mapping. File type: input. `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). 

29 input_scaffold1_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 1. File type: input. `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). 

30 input_scaffold2_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 2. File type: input. `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). 

31 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). 

32 output_structure1_path (str): Path to the output hybrid structure based on the ligand 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). 

33 output_structure2_path (str): Path to the output hybrid structure based on the ligand 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). 

34 output_topology_path (str): Path to the output hybrid topology. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ligand_hybrid.itp>`_. Accepted formats: itp (edam:format_3883). 

35 output_atomtypes_path (str): Path to the atom types for the output hybrid topology. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ligand_hybrid_atomtypes.itp>`_. Accepted formats: itp (edam:format_3883). 

36 

37 properties (dic): 

38 * **fit** (*bool*) - (False) Fit ligand structure 1 onto ligand structure 2 (Only used if input_pairs_path is provided). 

39 * **split** (*bool*) - (False) Split the topology into separate transitions. 

40 * **scDUMm** (*float*) - (1.0) Scale dummy masses using the counterpart atoms. 

41 * **scDUMa** (*float*) - (1.0) Scale bonded dummy angle parameters. 

42 * **scDUMd** (*float*) - (1.0) Scale bonded dummy dihedral parameters. 

43 * **deAng** (*bool*) - (False) Decouple angles composed of 1 dummy and 2 non-dummies. 

44 * **distance** (*float*) - (0.05) Distance (nm) between atoms to consider them morphable for alignment approach (Only used if input_pairs_path is not provided). 

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

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

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

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

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

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

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

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

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

54 

55 Examples: 

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

57 

58 from biobb_pmx.pmxbiobb.pmxligand_hybrid import pmxligand_hybrid 

59 prop = { 

60 'fit' : True, 

61 'distance': 0.05 

62 } 

63 pmxligand_hybrid(input_structure1_path='/path/to/myStructure1.pdb', 

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

65 input_topology1_path='/path/to/myTopology1.pdb', 

66 input_topology2_path='/path/to/myTopology2.pdb', 

67 input_pairs_path='/path/to/myPairs.dat', 

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

69 output_structure1_path='/path/to/myStructureOutput1.pdb', 

70 output_structure2_path='/path/to/myStructureOutput2.pdb', 

71 output_topology_path='/path/to/myTopologyOutput.pdb', 

72 output_atomtypes_path='/path/to/myAtomTypesOutput.pdb', 

73 properties=prop) 

74 

75 Info: 

76 * wrapped_software: 

77 * name: PMX ligand_hybrid 

78 * version: >=1.0.1 

79 * license: GNU 

80 * ontology: 

81 * name: EDAM 

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

83 

84 """ 

85 

86 def __init__( 

87 self, 

88 input_structure1_path: str, 

89 input_structure2_path: str, 

90 input_topology1_path: str, 

91 input_topology2_path: str, 

92 output_log_path: str, 

93 output_structure1_path: str, 

94 output_structure2_path: str, 

95 output_topology_path: str, 

96 output_atomtypes_path: str, 

97 input_scaffold1_path: Optional[str] = None, 

98 input_scaffold2_path: Optional[str] = None, 

99 input_pairs_path: Optional[str] = None, 

100 properties: Optional[dict] = None, 

101 **kwargs, 

102 ) -> None: 

103 properties = properties or {} 

104 

105 # Call parent class constructor 

106 super().__init__(properties) 

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

108 

109 # Input/Output files 

110 self.io_dict = { 

111 "in": { 

112 "input_structure1_path": input_structure1_path, 

113 "input_structure2_path": input_structure2_path, 

114 "input_topology1_path": input_topology1_path, 

115 "input_topology2_path": input_topology2_path, 

116 "input_scaffold1_path": input_scaffold1_path, 

117 "input_scaffold2_path": input_scaffold2_path, 

118 "input_pairs_path": input_pairs_path, 

119 }, 

120 "out": { 

121 "output_structure1_path": output_structure1_path, 

122 "output_structure2_path": output_structure2_path, 

123 "output_topology_path": output_topology_path, 

124 "output_atomtypes_path": output_atomtypes_path, 

125 "output_log_path": output_log_path, 

126 }, 

127 } 

128 

129 # Properties specific for BB 

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

131 # self.split = properties.get('split', False) 

132 # self.scDUMm = properties.get('scDUMm', 1.0) 

133 # self.scDUMa = properties.get('scDUMa', 1.0) 

134 # self.scDUMd = properties.get('scDUMd', 1.0) 

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

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

137 

138 self.fit = properties.get("fit") 

139 self.split = properties.get("split") 

140 self.scDUMm = properties.get("scDUMm") 

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

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

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

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

145 

146 # Properties common in all PMX BB 

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

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

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

150 self.gmx_lib = str( 

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

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

153 ) 

154 ) 

155 if properties.get("container_path"): 

156 self.gmx_lib = str( 

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

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

159 ) 

160 ) 

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

162 

163 # Check the properties 

164 self.check_properties(properties) 

165 self.check_arguments() 

166 

167 @launchlogger 

168 def launch(self) -> int: 

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

170 

171 # Setup Biobb 

172 if self.check_restart(): 

173 return 0 

174 self.stage_files() 

175 

176 # Check if executable exists 

177 if not self.container_path: 

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

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

180 raise FileNotFoundError( 

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

182 % self.binary_path 

183 ) 

184 

185 self.cmd = [ 

186 self.binary_path, 

187 "ligandHybrid", 

188 "-i1", 

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

190 "-i2", 

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

192 "-itp1", 

193 self.stage_io_dict["in"]["input_topology1_path"], 

194 "-itp2", 

195 self.stage_io_dict["in"]["input_topology2_path"], 

196 "-pairs", 

197 self.stage_io_dict["in"]["input_pairs_path"], 

198 "-oA", 

199 self.stage_io_dict["out"]["output_structure1_path"], 

200 "-oB", 

201 self.stage_io_dict["out"]["output_structure2_path"], 

202 "-oitp", 

203 self.stage_io_dict["out"]["output_topology_path"], 

204 "-offitp", 

205 self.stage_io_dict["out"]["output_atomtypes_path"], 

206 "-log", 

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

208 ] 

209 

210 if self.stage_io_dict["in"].get("output_scaffold1_path"): 

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

212 self.cmd.append(self.stage_io_dict["in"]["output_scaffold1_path"]) 

213 

214 if self.stage_io_dict["in"].get("output_scaffold2_path"): 

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

216 self.cmd.append(self.stage_io_dict["in"]["output_scaffold2_path"]) 

217 

218 if self.fit: 

219 self.cmd.append("--fit") 

220 if self.split: 

221 self.cmd.append("--split") 

222 if self.deAng: 

223 self.cmd.append("--deAng") 

224 if self.distance: 

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

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

227 if self.scDUMm: 

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

229 self.cmd.append(str(self.scDUMm)) 

230 if self.scDUMa: 

231 self.cmd.append("--scDUMa") 

232 self.cmd.append(str(self.scDUMa)) 

233 if self.scDUMd: 

234 self.cmd.append("--scDUMd") 

235 self.cmd.append(str(self.scDUMd)) 

236 

237 if self.gmx_lib: 

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

239 

240 # Run Biobb block 

241 self.run_biobb() 

242 

243 # Copy files to host 

244 self.copy_to_host() 

245 

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

247 self.remove_tmp_files() 

248 

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

250 return self.return_code 

251 

252 

253def pmxligand_hybrid( 

254 input_structure1_path: str, 

255 input_structure2_path: str, 

256 input_topology1_path: str, 

257 input_topology2_path: str, 

258 output_log_path: str, 

259 output_structure1_path: str, 

260 output_structure2_path: str, 

261 output_topology_path: str, 

262 output_atomtypes_path: str, 

263 input_scaffold1_path: Optional[str] = None, 

264 input_scaffold2_path: Optional[str] = None, 

265 input_pairs_path: Optional[str] = None, 

266 properties: Optional[dict] = None, 

267 **kwargs, 

268) -> int: 

269 """Execute the :class:`Pmxligand_hybrid <pmx.pmxmutate.Pmxligand_hybrid>` class and 

270 execute the :meth:`launch() <pmx.pmxligand_hybrid.Pmxligand_hybrid.launch> method.""" 

271 

272 return Pmxligand_hybrid( 

273 input_structure1_path=input_structure1_path, 

274 input_structure2_path=input_structure2_path, 

275 input_topology1_path=input_topology1_path, 

276 input_topology2_path=input_topology2_path, 

277 output_log_path=output_log_path, 

278 output_structure1_path=output_structure1_path, 

279 output_structure2_path=output_structure2_path, 

280 output_topology_path=output_topology_path, 

281 output_atomtypes_path=output_atomtypes_path, 

282 input_scaffold1_path=input_scaffold1_path, 

283 input_scaffold2_path=input_scaffold2_path, 

284 input_pairs_path=input_pairs_path, 

285 properties=properties, 

286 ).launch() 

287 

288 pmxligand_hybrid.__doc__ = Pmxligand_hybrid.__doc__ 

289 

290 

291def main(): 

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

293 parser = argparse.ArgumentParser( 

294 description="Run PMX ligand hybrid module", 

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

296 ) 

297 parser.add_argument( 

298 "-c", 

299 "--config", 

300 required=False, 

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

302 ) 

303 

304 # Specific args of each building block 

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

306 required_args.add_argument( 

307 "--input_structure1_path", 

308 required=True, 

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

310 ) 

311 required_args.add_argument( 

312 "--input_structure2_path", 

313 required=True, 

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

315 ) 

316 required_args.add_argument( 

317 "--input_topology1_path", 

318 required=True, 

319 help="Path to the input ligand topology file 1", 

320 ) 

321 required_args.add_argument( 

322 "--input_topology2_path", 

323 required=True, 

324 help="Path to the input ligand topology file 2", 

325 ) 

326 required_args.add_argument( 

327 "--output_structure1_path", 

328 required=True, 

329 help="Path to the output ligand structure file 1", 

330 ) 

331 required_args.add_argument( 

332 "--output_structure2_path", 

333 required=True, 

334 help="Path to the output ligand structure file 2", 

335 ) 

336 required_args.add_argument( 

337 "--output_topology1_path", 

338 required=True, 

339 help="Path to the output ligand topology file 1", 

340 ) 

341 required_args.add_argument( 

342 "--output_topology2_path", 

343 required=True, 

344 help="Path to the output ligand topology file 2", 

345 ) 

346 required_args.add_argument( 

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

348 ) 

349 parser.add_argument( 

350 "--input_scaffold1_path", 

351 required=False, 

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

353 ) 

354 parser.add_argument( 

355 "--input_scaffold2_path", 

356 required=False, 

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

358 ) 

359 parser.add_argument( 

360 "--input_pairs_path", 

361 required=False, 

362 help="Path to the input atom pair mapping.", 

363 ) 

364 

365 args = parser.parse_args() 

366 config = args.config if args.config else None 

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

368 

369 # Specific call of each building block 

370 pmxligand_hybrid( 

371 input_structure1_path=args.input_structure1_path, 

372 input_structure2_path=args.input_structure2_path, 

373 input_topology1_path=args.input_topology1_path, 

374 input_topology2_path=args.input_topology2_path, 

375 output_log_path=args.output_log_path, 

376 output_structure1_path=args.output_structure1_path, 

377 output_structure2_path=args.output_structure2_path, 

378 output_topology_path=args.output_topology_path, 

379 output_atomtypes_path=args.output_atomtypes_path, 

380 input_scaffold1_path=args.input_scaffold1_path, 

381 input_scaffold2_path=args.input_scaffold2_path, 

382 input_pairs_path=args.input_pairs_path, 

383 properties=properties, 

384 ) 

385 

386 

387if __name__ == "__main__": 

388 main()