Coverage for biobb_pmx/pmxbiobb/pmxmutate.py: 72%

75 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 mutate 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 import file_utils as fu 

15from biobb_common.tools.file_utils import launchlogger 

16 

17from biobb_pmx.pmxbiobb.common import MUTATION_DICT, create_mutations_file 

18 

19 

20class Pmxmutate(BiobbObject): 

21 """ 

22 | biobb_pmx Pmxmutate 

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

24 | Mutate residues in a protein structure. 

25 

26 Args: 

27 input_structure_path (str): Path to the input structure file. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/frame99.pdb>`_. Accepted formats: pdb (edam:format_1476), gro (edam:format_2033). 

28 output_structure_path (str): Path to the output structure file. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_output_structure.pdb>`_. Accepted formats: pdb (edam:format_1476), gro (edam:format_2033). 

29 input_b_structure_path (str) (Optional): Path to the mutated input structure file. File type: input. Accepted formats: pdb (edam:format_1476), gro (edam:format_2033). 

30 properties (dic): 

31 * **mutation_list** (*str*) - ("2Ala") Mutation list in the format "Chain:Resnum MUT_AA_Code" or "Chain:Resnum MUT_NA_Code" (no spaces between the elements) separated by commas. If no chain is provided as chain code all the chains in the pdb file will be mutated. ie: "A:15CYS". Possible MUT_AA_Code: 'ALA', 'ARG', 'ASN', 'ASP', 'ASPH', 'ASPP', 'ASH', 'CYS', 'CYS2', 'CYN', 'CYX', 'CYM', 'CYSH', 'GLU', 'GLUH', 'GLUP', 'GLH', 'GLN', 'GLY', 'HIS', 'HIE', 'HISE', 'HSE', 'HIP', 'HSP', 'HISH', 'HID', 'HSD', 'ILE', 'LEU', 'LYS', 'LYSH', 'LYP', 'LYN', 'LSN', 'MET', 'PHE', 'PRO', 'SER', 'SP1', 'SP2', 'THR', 'TRP', 'TYR', 'VAL'. Possible MUT_NA_Codes: 'A', 'T', 'C', 'G', 'U'. 

32 * **force_field** (*str*) - ("amber99sb-star-ildn-mut") Forcefield to use. 

33 * **resinfo** (*bool*) - (False) Show the list of 3-letter -> 1-letter residues. 

34 * **gmx_lib** (*str*) - ("$CONDA_PREFIX/lib/python3.7/site-packages/pmx/data/mutff/") Path to the GMXLIB folder in your computer. 

35 * **binary_path** (*str*) - ("pmx") Path to the PMX command line interface. 

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

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

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

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

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

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

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

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

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

45 

46 Examples: 

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

48 

49 from biobb_pmx.pmxbiobb.pmxmutate import pmxmutate 

50 prop = { 

51 'mutation_list': '2Ala, 3Val', 

52 'gmx_lib': '/path/to/myGMXLIB/', 

53 'force_field': 'amber99sb-star-ildn-mut' 

54 } 

55 pmxmutate(input_structure_path='/path/to/myStructure.pdb', 

56 output_structure_path='/path/to/newStructure.pdb', 

57 input_b_structure_path='/path/to/myStructureB.pdb' 

58 properties=prop) 

59 

60 Info: 

61 * wrapped_software: 

62 * name: PMX mutate 

63 * version: >=1.0.1 

64 * license: GNU 

65 * ontology: 

66 * name: EDAM 

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

68 

69 """ 

70 

71 def __init__( 

72 self, 

73 input_structure_path: str, 

74 output_structure_path: str, 

75 input_b_structure_path: Optional[str] = None, 

76 properties: Optional[dict] = None, 

77 **kwargs, 

78 ) -> None: 

79 properties = properties or {} 

80 

81 # Call parent class constructor 

82 super().__init__(properties) 

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

84 

85 # Input/Output files 

86 self.io_dict = { 

87 "in": { 

88 "input_structure_path": input_structure_path, 

89 "input_b_structure_path": input_b_structure_path, 

90 }, 

91 "out": {"output_structure_path": output_structure_path}, 

92 } 

93 

94 # Properties specific for BB 

95 self.force_field = properties.get("force_field", "amber99sb-star-ildn-mut") 

96 self.resinfo = properties.get("resinfo", False) 

97 self.mutation_list = properties.get("mutation_list", "2Ala") 

98 self.input_mutations_file = properties.get("mutations_file") 

99 

100 # Properties common in all PMX BB 

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

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

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

104 self.gmx_lib = str( 

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

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

107 ) 

108 ) 

109 if properties.get("container_path"): 

110 self.gmx_lib = str( 

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

112 "lib/python3.8/site-packages/pmx/data/mutff/" 

113 ) 

114 ) 

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

116 

117 # Check the properties 

118 self.check_properties(properties) 

119 self.check_arguments() 

120 

121 @launchlogger 

122 def launch(self) -> int: 

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

124 

125 # Setup Biobb 

126 if self.check_restart(): 

127 return 0 

128 self.stage_files() 

129 

130 # Check if executable exists 

131 if not self.container_path: 

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

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

134 raise FileNotFoundError( 

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

136 % self.binary_path 

137 ) 

138 

139 # Generate mutations file 

140 

141 mutations_dir = fu.create_unique_dir() 

142 self.input_mutations_file = create_mutations_file( 

143 input_mutations_path=str(Path(mutations_dir).joinpath("mutations.txt")), 

144 mutation_list=self.mutation_list, 

145 mutation_dict=MUTATION_DICT, 

146 ) 

147 

148 # Copy extra files to container: mutations file 

149 if self.container_path: 

150 fu.log("Container execution enabled", self.out_log) 

151 

152 shutil.copy2( 

153 self.input_mutations_file, self.stage_io_dict.get("unique_dir", "") 

154 ) 

155 self.input_mutations_file = str( 

156 Path(self.container_volume_path).joinpath( 

157 Path(self.input_mutations_file).name 

158 ) 

159 ) 

160 

161 self.cmd = [ 

162 self.binary_path, 

163 "mutate", 

164 "-f", 

165 self.stage_io_dict["in"]["input_structure_path"], 

166 "-o", 

167 self.stage_io_dict["out"]["output_structure_path"], 

168 "-ff", 

169 self.force_field, 

170 "--script", 

171 self.input_mutations_file, 

172 ] 

173 

174 if self.stage_io_dict["in"].get("input_b_structure_path"): 

175 self.cmd.append("-fB") 

176 self.cmd.append(self.stage_io_dict["in"]["input_b_structure_path"]) 

177 if self.resinfo: 

178 self.cmd.append("-resinfo") 

179 

180 if self.gmx_lib: 

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

182 

183 # Run Biobb block 

184 self.run_biobb() 

185 

186 # Copy files to host 

187 self.copy_to_host() 

188 

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

190 self.tmp_files.extend([mutations_dir]) 

191 self.remove_tmp_files() 

192 

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

194 return self.return_code 

195 

196 

197def pmxmutate( 

198 input_structure_path: str, 

199 output_structure_path: str, 

200 input_b_structure_path: Optional[str] = None, 

201 properties: Optional[dict] = None, 

202 **kwargs, 

203) -> int: 

204 """Execute the :class:`Pmxmutate <pmx.pmxmutate.Pmxmutate>` class and 

205 execute the :meth:`launch() <pmx.pmxmutate.Pmxmutate.launch> method.""" 

206 

207 return Pmxmutate( 

208 input_structure_path=input_structure_path, 

209 output_structure_path=output_structure_path, 

210 input_b_structure_path=input_b_structure_path, 

211 properties=properties, 

212 **kwargs, 

213 ).launch() 

214 

215 pmxmutate.__doc__ = Pmxmutate.__doc__ 

216 

217 

218def main(): 

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

220 parser = argparse.ArgumentParser( 

221 description="Run PMX mutate module", 

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

223 ) 

224 parser.add_argument( 

225 "-c", 

226 "--config", 

227 required=False, 

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

229 ) 

230 

231 # Specific args of each building block 

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

233 required_args.add_argument( 

234 "--input_structure_path", required=True, help="Path to the input structure file" 

235 ) 

236 required_args.add_argument( 

237 "--output_structure_path", 

238 required=True, 

239 help="Path to the output structure file", 

240 ) 

241 parser.add_argument( 

242 "--input_b_structure_path", 

243 required=False, 

244 help="Path to the mutated input structure file", 

245 ) 

246 

247 args = parser.parse_args() 

248 config = args.config if args.config else None 

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

250 

251 # Specific call of each building block 

252 pmxmutate( 

253 input_structure_path=args.input_structure_path, 

254 output_structure_path=args.output_structure_path, 

255 input_b_structure_path=args.input_b_structure_path, 

256 properties=properties, 

257 ) 

258 

259 

260if __name__ == "__main__": 

261 main()