Coverage for biobb_chemistry/babelm/babel_convert.py: 94%

80 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-06 12:40 +0000

1#!/usr/bin/env python3 

2 

3"""Module containing the BabelConvert class and the command line interface.""" 

4 

5from typing import Optional 

6 

7from biobb_common.generic.biobb_object import BiobbObject 

8from biobb_common.tools.file_utils import launchlogger 

9 

10from biobb_chemistry.babelm.common import ( 

11 _from_string_to_list, 

12 check_input_path, 

13 check_output_path, 

14 get_coordinates, 

15 get_input_format, 

16 get_output_format, 

17 get_ph, 

18 remove_non_pdbqt_records, 

19) 

20 

21 

22class BabelConvert(BiobbObject): 

23 """ 

24 | biobb_chemistry BabelConvert 

25 | This class is a wrapper of the Open Babel tool. 

26 | Small molecule format conversion for structures or trajectories. Open Babel is a chemical toolbox designed to speak the many languages of chemical data. It's an open, collaborative project allowing anyone to search, convert, analyze, or store data from molecular modeling, chemistry, solid-state materials, biochemistry, or related areas. `Visit the official page <http://openbabel.org/wiki/Main_Page>`_. 

27 

28 Args: 

29 input_path (str): Path to the input file. File type: input. `Sample file <https://github.com/bioexcel/biobb_chemistry/raw/master/biobb_chemistry/test/data/babelm/babel.smi>`_. Accepted formats: dat (edam:format_1637), ent (edam:format_1476), fa (edam:format_1929), fasta (edam:format_1929), gro (edam:format_2033), inp (edam:format_3878), log (edam:format_2030), mcif (edam:format_1477), mdl (edam:format_3815), mmcif (edam:format_1477), mol (edam:format_3815), mol2 (edam:format_3816), pdb (edam:format_1476), pdbqt (edam:format_1476), png (edam:format_3603), sdf (edam:format_3814), smi (edam:format_1196), smiles (edam:format_1196), txt (edam:format_2033), xml (edam:format_2332), xtc (edam:format_3875). 

30 output_path (str): Path to the output file. File type: output. `Sample file <https://github.com/bioexcel/biobb_chemistry/raw/master/biobb_chemistry/test/reference/babelm/ref_babel.convert.mol2>`_. Accepted formats: ent (edam:format_1476), fa (edam:format_1929), fasta (edam:format_1929), gro (edam:format_2033), inp (edam:format_3878), mcif (edam:format_1477), mdl (edam:format_3815), mmcif (edam:format_1477), mol (edam:format_3815), mol2 (edam:format_3816), pdb (edam:format_1476), pdbqt (edam:format_1476), png (edam:format_3603), sdf (edam:format_3814), smi (edam:format_1196), smiles (edam:format_1196), txt (edam:format_2033). 

31 properties (dic - Python dictionary object containing the tool parameters, not input/output files): 

32 * **input_format** (*str*) - (None) Format of input file. If not provided, input_path extension will be taken. Values: dat (Information represented in a data record), ent (Protein Data Bank format), fa (FASTA sequence format), fasta (FASTA sequence format), gro (GROMACS structure), inp (AMBER trajectory format), log (Events file), mcif (Entry format of PDB database in mmCIF format), mdl (file format for holding information about the atoms; bonds; connectivity and coordinates of a molecule), mmcif (Entry format of PDB database in mmCIF format), mol (file format for holding information about the atoms; bonds; connectivity and coordinates of a molecule), mol2 (Complete and portable representation of a SYBYL molecule), pdb (Protein Data Bank format), pdbqt (Protein Data Bank format with charges), png (File format for image compression), sdf (One of a family of chemical-data file formats developed by MDL Information Systems), smi (Chemical structure specified in Simplified Molecular Input Line Entry System line notation.), smiles (Chemical structure specified in Simplified Molecular Input Line Entry System line notation.), txt (Textual format), xml (eXtensible Markup Language), xtc (Portable binary format for trajectories produced by GROMACS package). 

33 * **output_format** (*str*) - (None) Format of output file. If not provided, output_path extension will be taken. Values: ent (Protein Data Bank format), fa (FASTA sequence format), fasta (FASTA sequence format), gro (GROMACS structure), inp (AMBER trajectory format), mcif (Entry format of PDB database in mmCIF format), mdl (file format for holding information about the atoms; bonds; connectivity and coordinates of a molecule), mmcif (Entry format of PDB database in mmCIF format), mol (file format for holding information about the atoms; bonds; connectivity and coordinates of a molecule), mol2 (Complete and portable representation of a SYBYL molecule), pdb (Protein Data Bank format), pdbqt (Protein Data Bank format with charges), png (File format for image compression), sdf (One of a family of chemical-data file formats developed by MDL Information Systems), smi (Chemical structure specified in Simplified Molecular Input Line Entry System line notation.), smiles (Chemical structure specified in Simplified Molecular Input Line Entry System line notation.), txt (Textual format), xtc (Portable binary format for trajectories produced by GROMACS package). 

34 * **fs_input** (*list*) - (None) Format-specific input options. Values: b (disable automatic bonding), d (input file is in dlg -AutoDock docking log- format). 

35 * **fs_output** (*list*) - (None) Format-specific output options. Values: b (enable automatic bonding), r (output as a rigid molecule), c (combine separate molecular pieces of input into a single rigid molecule), s (output as a flexible residue), p (preserve atom indices from input file), h (preserve hydrogens), n (preserve atom names). 

36 * **coordinates** (*int*) - (None) Type of coordinates: 2D or 3D. Values: 2 (2D coordinates), 3 (3D coordinates). 

37 * **effort** (*str*) - ("medium") Computational effort wanted to dedicate for the conformer generation coordinates calculations, only for 3D coordinates. Values: fastest (only generate coordinates, no force field or conformer search), fast (perform quick forcefield optimization), medium (forcefield optimization + fast conformer search), better (more optimization + fast conformer search), best (more optimization + significant conformer search). 

38 * **ph** (*float*) - (7.4) [0~14|0.1] Add hydrogens appropriate for pH. 

39 * **flex** (*bool*) - (False) Remove all but the largest contiguous fragment (strip salts). 

40 * **binary_path** (*str*) - ("obabel") Path to the obabel executable binary. 

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

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

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

44 * **container_path** (*str*) - (None) Container path definition. 

45 * **container_image** (*str*) - ('informaticsmatters/obabel:latest') Container image definition. 

46 * **container_volume_path** (*str*) - ('/tmp') Container volume path definition. 

47 * **container_working_dir** (*str*) - (None) Container working directory definition. 

48 * **container_user_id** (*str*) - (None) Container user_id definition. 

49 * **container_shell_path** (*str*) - ('/bin/bash') Path to default shell inside the container. 

50 

51 Examples: 

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

53 

54 from biobb_chemistry.babelm.babel_convert import babel_convert 

55 prop = { 

56 'input_format': 'smi', 

57 'output_format': 'mol2', 

58 'coordinates': 3, 

59 'ph': 7.4 

60 } 

61 babel_convert(input_path='/path/to/my2DMolecule.smi', 

62 output_path='/path/to/new3DMolecule.mol2', 

63 properties=prop) 

64 

65 Info: 

66 * wrapped_software: 

67 * name: Open Babel 

68 * version: 2.4.1 

69 * license: GNU 

70 * ontology: 

71 * name: EDAM 

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

73 

74 """ 

75 

76 def __init__(self, input_path, output_path, properties=None, **kwargs) -> None: 

77 properties = properties or {} 

78 

79 # Call parent class constructor 

80 super().__init__(properties) 

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

82 

83 # Input/Output files 

84 self.io_dict = { 

85 "in": {"input_path": input_path}, 

86 "out": {"output_path": output_path}, 

87 } 

88 

89 # Properties specific for BB 

90 self.input_format = properties.get("input_format", "") 

91 self.output_format = properties.get("output_format", "") 

92 self.fs_input = _from_string_to_list(properties.get("fs_input", None)) 

93 self.fs_output = _from_string_to_list(properties.get("fs_output", None)) 

94 self.coordinates = properties.get("coordinates", "") 

95 self.effort = properties.get("effort", "medium") 

96 self.ph = properties.get("ph", "") 

97 self.flex = properties.get("flex", False) 

98 self.binary_path = properties.get("binary_path", "obabel") 

99 self.properties = properties 

100 

101 # Check the properties 

102 self.check_properties(properties) 

103 self.check_arguments() 

104 

105 def check_data_params(self, out_log, err_log): 

106 """Checks all the input/output paths and parameters""" 

107 self.io_dict["in"]["input_path"] = check_input_path( 

108 self.io_dict["in"]["input_path"], out_log, self.__class__.__name__ 

109 ) 

110 self.io_dict["out"]["output_path"] = check_output_path( 

111 self.io_dict["out"]["output_path"], out_log, self.__class__.__name__ 

112 ) 

113 

114 def create_cmd(self, container_io_dict, out_log, err_log): 

115 """Creates the command line instruction using the properties file settings""" 

116 instructions_list = [] 

117 

118 # executable path 

119 instructions_list.append(self.binary_path) 

120 

121 # generating input 

122 infr = get_input_format( 

123 self.input_format, container_io_dict["in"]["input_path"], out_log 

124 ) 

125 iformat = "-i" + infr 

126 instructions_list.append(iformat) 

127 ipath = container_io_dict["in"]["input_path"] 

128 instructions_list.append(ipath) 

129 

130 # generating output 

131 oufr = get_output_format( 

132 self.output_format, container_io_dict["out"]["output_path"], out_log 

133 ) 

134 oformat = "-o" + oufr 

135 instructions_list.append(oformat) 

136 opath = "-O" + container_io_dict["out"]["output_path"] 

137 instructions_list.append(opath) 

138 

139 # adding coordinates 

140 crd = get_coordinates(self.coordinates, out_log) 

141 coordinates = "" 

142 if crd: 

143 coordinates = "--gen" + crd + "d" 

144 instructions_list.append(coordinates) 

145 

146 # adding pH 

147 p = get_ph(self.ph, out_log) 

148 ph = "" 

149 if p: 

150 ph = "-p " + p 

151 instructions_list.append(ph) 

152 

153 # flex 

154 flex = "" 

155 if not self.flex: 

156 flex = "-r" 

157 instructions_list.append(flex) 

158 

159 # fs_input 

160 if self.fs_input is not None: 

161 for fsi in self.fs_input: 

162 instructions_list.append("-a" + fsi) 

163 

164 # fs_output 

165 if self.fs_output is not None: 

166 for fso in self.fs_output: 

167 instructions_list.append("-x" + fso) 

168 

169 # adding effort (only for 3D coordinates) 

170 if crd == "3": 

171 instructions_list.append("--" + self.effort) 

172 

173 return instructions_list 

174 

175 @launchlogger 

176 def launch(self) -> int: 

177 """Execute the :class:`BabelConvert <babelm.babel_convert.BabelConvert>` babelm.babel_convert.BabelConvert object.""" 

178 

179 # check input/output paths and parameters 

180 self.check_data_params(self.out_log, self.err_log) 

181 

182 # Setup Biobb 

183 if self.check_restart(): 

184 return 0 

185 self.stage_files() 

186 

187 # create command line instruction 

188 self.cmd = self.create_cmd(self.stage_io_dict, self.out_log, self.err_log) 

189 

190 # Run Biobb block 

191 self.run_biobb() 

192 

193 # Copy files to host 

194 self.copy_to_host() 

195 

196 # remove the input PDB records that Open Babel >= 3.2 copies to a PDBQT output 

197 remove_non_pdbqt_records(self.output_format, self.io_dict["out"]["output_path"], self.out_log) 

198 

199 # remove temporary folder(s) 

200 self.remove_tmp_files() 

201 

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

203 

204 return self.return_code 

205 

206 

207def babel_convert( 

208 input_path: str, output_path: str, properties: Optional[dict] = None, **kwargs 

209) -> int: 

210 """Create the :class:`BabelConvert <babelm.babel_convert.BabelConvert>` class and 

211 execute the :meth:`launch() <babelm.babel_convert.BabelConvert.launch>` method.""" 

212 return BabelConvert(**dict(locals())).launch() 

213 

214 

215babel_convert.__doc__ = BabelConvert.__doc__ 

216main = BabelConvert.get_main(babel_convert, "Small molecule format conversion.") 

217 

218 

219if __name__ == "__main__": 

220 main()