Coverage for biobb_pmx/pmxbiobb/pmxcreate_top.py: 96%

74 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-05-29 06:59 +0000

1#!/usr/bin/env python3 

2 

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

4 

5import os 

6import shutil 

7import sys 

8from pathlib import Path, PurePath 

9from typing import Optional 

10 

11from biobb_common.generic.biobb_object import BiobbObject 

12from biobb_common.tools import file_utils as fu 

13from biobb_common.tools.file_utils import launchlogger 

14 

15 

16class Pmxcreate_top(BiobbObject): 

17 """ 

18 | biobb_pmx Pmxcreate_top 

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

20 | Create a complete ligand topology file from two input topology files. 

21 

22 Args: 

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

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

25 output_topology_path (str): Path to the complete ligand topology file. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_hybridTopo.zip>`_. Accepted formats: zip (edam:format_3987). 

26 properties (dic): 

27 * **force_field** (*str*) - ("amber99sb-star-ildn-mut.ff") Force-field to be included in the generated topology. 

28 * **water** (*str*) - ("tip3p") Water model to be included in the generated topology. 

29 * **system_name** (*str*) - ("Pmx topology") System name to be included in the generated topology. 

30 * **mols** (*list*) - ([['Protein',1],['Ligand',1]]) Molecules to be included in the generated topology. 

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

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

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

34 

35 Examples: 

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

37 

38 from biobb_pmx.pmxbiobb.pmxcreate_top import pmxcreate_top 

39 prop = { 

40 'remove_tmp' : True 

41 } 

42 pmxcreate_top(input_topology1_path='/path/to/myTopology1.itp', 

43 input_topology2_path='/path/to/myTopology2.itp', 

44 output_topology_path='/path/to/myMergedTopology.zip', 

45 properties=prop) 

46 

47 Info: 

48 * wrapped_software: 

49 * name: PMX create_top 

50 * version: >=1.0.1 

51 * license: GNU 

52 * ontology: 

53 * name: EDAM 

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

55 

56 """ 

57 

58 def __init__( 

59 self, 

60 input_topology1_path: str, 

61 input_topology2_path: str, 

62 output_topology_path: str, 

63 properties: Optional[dict] = None, 

64 **kwargs, 

65 ) -> None: 

66 properties = properties or {} 

67 

68 # Call parent class constructor 

69 super().__init__(properties) 

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

71 

72 # Input/Output files 

73 self.io_dict = { 

74 "in": { 

75 "input_topology1_path": input_topology1_path, 

76 "input_topology2_path": input_topology2_path, 

77 }, 

78 "out": {"output_topology_path": output_topology_path}, 

79 } 

80 

81 # Properties specific for BB 

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

83 self.water = properties.get("water", "tip3p") 

84 self.system_name = properties.get("system_name", "Pmx topology") 

85 self.mols = properties.get("mols", [["Protein", 1], ["Ligand", 1]]) 

86 

87 # Properties common in all PMX BB 

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

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

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

91 self.gmx_lib = str( 

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

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

94 ) 

95 ) 

96 if properties.get("container_path"): 

97 self.gmx_lib = str( 

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

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

100 ) 

101 ) 

102 

103 # Check the properties 

104 self.check_properties(properties) 

105 self.check_arguments() 

106 

107 @launchlogger 

108 def launch(self) -> int: 

109 """Execute the :class:`Pmxcreate_top <pmx.pmxcreate_top.Pmxcreate_top>` pmx.pmxcreate_top.Pmxcreate_top object.""" 

110 # os.chdir("/Users/hospital/BioBB/Notebooks_dev/biobb_wf_pmx_tutorial_ligands/biobb_wf_pmx_tutorial/notebooks") 

111 # Setup Biobb 

112 if self.check_restart(): 

113 return 0 

114 self.stage_files() 

115 

116 fu.log("Running create_top from pmx package...\n", self.out_log) 

117 

118 # Creating temporary folder 

119 tmp_folder = fu.create_unique_dir() 

120 fu.log("Creating %s temporary folder" % tmp_folder, self.out_log) 

121 

122 itp = os.path.basename( 

123 os.path.normpath(self.stage_io_dict["in"]["input_topology1_path"]) 

124 ) 

125 fu.log("Creating %s itp file in temporary folder" % itp, self.out_log) 

126 itp_local = str(PurePath(tmp_folder).joinpath(itp)) 

127 shutil.copyfile(self.io_dict["in"]["input_topology1_path"], itp_local) 

128 

129 itp2 = os.path.basename( 

130 os.path.normpath(self.stage_io_dict["in"]["input_topology2_path"]) 

131 ) 

132 fu.log("Creating %s itp file in temporary folder" % itp2, self.out_log) 

133 itp2_local = str(PurePath(tmp_folder).joinpath(itp2)) 

134 shutil.copyfile(self.io_dict["in"]["input_topology2_path"], itp2_local) 

135 

136 top_local = str(PurePath(tmp_folder).joinpath("topology.top")) 

137 

138 # _create_top function, taken from the pmx AZ tutorial: 

139 # https://github.com/deGrootLab/pmx/blob/develop/tutorials/AZtutorial.py 

140 fp = open(top_local, "w") 

141 # BioBB signature 

142 fp.write("; Topology generated by the BioBB pmxcreate_top building block\n\n") 

143 # ff itp 

144 fp.write('#include "%s/forcefield.itp"\n\n' % self.force_field) 

145 # additional itp 

146 # for i in self.itps: 

147 # fp.write('#include "%s"\n' % i) 

148 fp.write('#include "%s"\n' % itp) 

149 fp.write('#include "%s"\n\n' % itp2) 

150 # water itp 

151 fp.write('#include "%s/%s.itp"\n' % (self.force_field, self.water)) 

152 # ions 

153 fp.write('#include "%s/ions.itp"\n\n' % self.force_field) 

154 # system 

155 fp.write("[ system ]\n") 

156 fp.write("{0}\n\n".format(self.system_name)) 

157 # molecules 

158 fp.write("[ molecules ]\n") 

159 for mol in self.mols: 

160 fp.write("%s %s\n" % (mol[0], mol[1])) 

161 fp.close() 

162 

163 # zip topology 

164 current_cwd = os.getcwd() 

165 top_final = str( 

166 PurePath(current_cwd).joinpath(self.io_dict["out"]["output_topology_path"]) 

167 ) 

168 

169 os.chdir(tmp_folder) 

170 fu.log("Compressing topology to: %s" % top_final, self.out_log, self.global_log) 

171 fu.zip_top(zip_file=top_final, top_file="topology.top", out_log=self.out_log, remove_original_files=self.remove_tmp) 

172 os.chdir(current_cwd) 

173 

174 fu.log("Exit code 0\n", self.out_log) 

175 

176 # Run Biobb block 

177 # self.run_biobb() 

178 

179 # Copy files to host 

180 self.copy_to_host() 

181 

182 self.tmp_files.append(tmp_folder) 

183 self.remove_tmp_files() 

184 

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

186 return self.return_code 

187 

188 

189def pmxcreate_top( 

190 input_topology1_path: str, 

191 input_topology2_path: str, 

192 output_topology_path: str, 

193 properties: Optional[dict] = None, 

194 **kwargs, 

195) -> int: 

196 """Create the :class:`Pmxcreate_top <pmx.pmxcreate_top.Pmxcreate_top>` class and 

197 execute the :meth:`launch() <pmx.pmxmcreate_top.Pmxmcreate_top.launch> method.""" 

198 return Pmxcreate_top(**dict(locals())).launch() 

199 

200 

201pmxcreate_top.__doc__ = Pmxcreate_top.__doc__ 

202main = Pmxcreate_top.get_main(pmxcreate_top, "Run PMX create_top module") 

203 

204if __name__ == "__main__": 

205 main()