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

76 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-22 17:32 +0000

1#!/usr/bin/env python3 

2 

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

4 

5import os 

6import shutil 

7import sys 

8from pathlib import Path 

9from typing import Optional 

10 

11from biobb_common.generic.biobb_object import BiobbObject 

12from biobb_common.tools.file_utils import launchlogger 

13 

14 

15class Pmxligand_hybrid(BiobbObject): 

16 """ 

17 | biobb_pmx Pmxligand_hybrid 

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

19 | Create a hybrid topology and structure based on two ligand structures. 

20 

21 Args: 

22 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/lig1.pdb>`_. Accepted formats: pdb (edam:format_1476). 

23 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/lig2.pdb>`_. Accepted formats: pdb (edam:format_1476). 

24 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/topoLig1.itp>`_. Accepted formats: itp (edam:format_3883). 

25 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/topoLig2.itp>`_. Accepted formats: itp (edam:format_3883). 

26 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/data/pmx/myPairs1.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330). 

27 input_scaffold1_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 1. File type: input. Accepted formats: ndx (edam:format_2033). 

28 input_scaffold2_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 2. File type: input. Accepted formats: ndx (edam:format_2033). 

29 output_log_path (str): Path to the log file. File type: output. Accepted formats: log (edam:format_2330), txt (edam:format_2330), out (edam:format_2330). 

30 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/ref_hybridStructure1.pdb>`_. Accepted formats: pdb (edam:format_1476). 

31 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/ref_hybridStructure2.pdb>`_. Accepted formats: pdb (edam:format_1476). 

32 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/ref_hybridTopology.itp>`_. Accepted formats: itp (edam:format_3883). 

33 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/ref_hybridAtomTypes.itp>`_. Accepted formats: itp (edam:format_3883). 

34 

35 properties (dic): 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

52 

53 Examples: 

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

55 

56 from biobb_pmx.pmxbiobb.pmxligand_hybrid import pmxligand_hybrid 

57 prop = { 

58 'fit' : True, 

59 'distance': 0.05 

60 } 

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

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

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

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

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

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

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

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

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

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

71 properties=prop) 

72 

73 Info: 

74 * wrapped_software: 

75 * name: PMX ligand_hybrid 

76 * version: >=1.0.1 

77 * license: GNU 

78 * ontology: 

79 * name: EDAM 

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

81 

82 """ 

83 

84 def __init__( 

85 self, 

86 input_structure1_path: str, 

87 input_structure2_path: str, 

88 input_topology1_path: str, 

89 input_topology2_path: str, 

90 output_log_path: str, 

91 output_structure1_path: str, 

92 output_structure2_path: str, 

93 output_topology_path: str, 

94 output_atomtypes_path: str, 

95 input_scaffold1_path: Optional[str] = None, 

96 input_scaffold2_path: Optional[str] = None, 

97 input_pairs_path: Optional[str] = None, 

98 properties: Optional[dict] = None, 

99 **kwargs, 

100 ) -> None: 

101 properties = properties or {} 

102 

103 # Call parent class constructor 

104 super().__init__(properties) 

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

106 

107 # Input/Output files 

108 self.io_dict = { 

109 "in": { 

110 "input_structure1_path": input_structure1_path, 

111 "input_structure2_path": input_structure2_path, 

112 "input_topology1_path": input_topology1_path, 

113 "input_topology2_path": input_topology2_path, 

114 "input_scaffold1_path": input_scaffold1_path, 

115 "input_scaffold2_path": input_scaffold2_path, 

116 "input_pairs_path": input_pairs_path, 

117 }, 

118 "out": { 

119 "output_structure1_path": output_structure1_path, 

120 "output_structure2_path": output_structure2_path, 

121 "output_topology_path": output_topology_path, 

122 "output_atomtypes_path": output_atomtypes_path, 

123 "output_log_path": output_log_path, 

124 }, 

125 } 

126 

127 # Properties specific for BB 

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

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

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

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

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

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

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

135 

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

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

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

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

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

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

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

143 

144 # Properties common in all PMX BB 

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

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

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

148 self.gmx_lib = str( 

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

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

151 ) 

152 ) 

153 if properties.get("container_path"): 

154 self.gmx_lib = str( 

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

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

157 ) 

158 ) 

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

160 

161 # Check the properties 

162 self.check_properties(properties) 

163 self.check_arguments() 

164 

165 @launchlogger 

166 def launch(self) -> int: 

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

168 

169 # Setup Biobb 

170 if self.check_restart(): 

171 return 0 

172 self.stage_files() 

173 

174 # Check if executable exists 

175 if not self.container_path: 

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

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

178 raise FileNotFoundError( 

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

180 % self.binary_path 

181 ) 

182 

183 self.cmd = [ 

184 self.binary_path, 

185 "ligandHybrid", 

186 "-i1", 

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

188 "-i2", 

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

190 "-itp1", 

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

192 "-itp2", 

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

194 "-pairs", 

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

196 "-oA", 

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

198 "-oB", 

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

200 "-oitp", 

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

202 "-offitp", 

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

204 "-log", 

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

206 ] 

207 

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

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

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

211 

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

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

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

215 

216 if self.fit: 

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

218 if self.split: 

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

220 if self.deAng: 

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

222 if self.distance: 

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

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

225 if self.scDUMm: 

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

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

228 if self.scDUMa: 

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

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

231 if self.scDUMd: 

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

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

234 

235 if self.gmx_lib: 

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

237 

238 # Run Biobb block 

239 self.run_biobb() 

240 

241 # Copy files to host 

242 self.copy_to_host() 

243 

244 self.remove_tmp_files() 

245 

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

247 return self.return_code 

248 

249 

250def pmxligand_hybrid( 

251 input_structure1_path: str, 

252 input_structure2_path: str, 

253 input_topology1_path: str, 

254 input_topology2_path: str, 

255 output_log_path: str, 

256 output_structure1_path: str, 

257 output_structure2_path: str, 

258 output_topology_path: str, 

259 output_atomtypes_path: str, 

260 input_scaffold1_path: Optional[str] = None, 

261 input_scaffold2_path: Optional[str] = None, 

262 input_pairs_path: Optional[str] = None, 

263 properties: Optional[dict] = None, 

264 **kwargs, 

265) -> int: 

266 """Create the :class:`Pmxligand_hybrid <pmx.pmxmutate.Pmxligand_hybrid>` class and 

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

268 return Pmxligand_hybrid(**dict(locals())).launch() 

269 

270 

271pmxligand_hybrid.__doc__ = Pmxligand_hybrid.__doc__ 

272main = Pmxligand_hybrid.get_main(pmxligand_hybrid, "Run PMX ligand hybrid module") 

273 

274if __name__ == "__main__": 

275 main()