Coverage for biobb_chemistry/babelm/babel_add_hydrogens.py: 95%

77 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 BabelAddHydrogens 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 BabelAddHydrogens(BiobbObject): 

23 """ 

24 | biobb_chemistry BabelAddHydrogens 

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

26 | Adds hydrogens to a given structure or trajectory. 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.no.H.pdb>`_. 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.hydrogens.pdb>`_. 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*) - (["h"]) 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 * **binary_path** (*str*) - ("obabel") Path to the obabel executable binary. 

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

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

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

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

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

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

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

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

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

49 

50 Examples: 

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

52 

53 from biobb_chemistry.babelm.babel_add_hydrogens import babel_add_hydrogens 

54 prop = { 

55 'input_format': 'pdb', 

56 'output_format': 'pdb', 

57 'coordinates': 3, 

58 'ph': 7.4 

59 } 

60 babel_add_hydrogens(input_path='/path/to/myStructure.pdb', 

61 output_path='/path/to/newStructure.pdb', 

62 properties=prop) 

63 

64 Info: 

65 * wrapped_software: 

66 * name: Open Babel 

67 * version: 2.4.1 

68 * license: GNU 

69 * ontology: 

70 * name: EDAM 

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

72 

73 """ 

74 

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

76 properties = properties or {} 

77 

78 # Call parent class constructor 

79 super().__init__(properties) 

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

81 

82 # Input/Output files 

83 self.io_dict = { 

84 "in": {"input_path": input_path}, 

85 "out": {"output_path": output_path}, 

86 } 

87 

88 # Properties specific for BB 

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

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

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

92 self.fs_output = _from_string_to_list(properties.get("fs_output", ["h"])) 

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

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

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

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

97 self.properties = properties 

98 

99 # Check the properties 

100 self.check_properties(properties) 

101 self.check_arguments() 

102 

103 def check_data_params(self, out_log, err_log): 

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

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

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

107 ) 

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

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

110 ) 

111 

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

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

114 instructions_list = [] 

115 

116 # executable path 

117 instructions_list.append(self.binary_path) 

118 

119 # generating input 

120 infr = get_input_format( 

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

122 ) 

123 iformat = "-i" + infr 

124 instructions_list.append(iformat) 

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

126 instructions_list.append(ipath) 

127 

128 # generating output 

129 oufr = get_output_format( 

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

131 ) 

132 oformat = "-o" + oufr 

133 instructions_list.append(oformat) 

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

135 instructions_list.append(opath) 

136 

137 # adding coordinates 

138 crd = get_coordinates(self.coordinates, out_log) 

139 coordinates = "" 

140 if crd: 

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

142 instructions_list.append(coordinates) 

143 

144 # checking pH 

145 p = get_ph(self.ph, out_log) 

146 

147 # adding H 

148 hydrogens = "-h" 

149 

150 # adding pH 

151 ph = "" 

152 if p: 

153 ph = "-p " + p 

154 instructions_list.append(ph) 

155 else: 

156 instructions_list.append(hydrogens) 

157 

158 # fs_input 

159 if self.fs_input is not None: 

160 for fsi in self.fs_input: 

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

162 

163 # fs_output 

164 if self.fs_output is not None: 

165 for fso in self.fs_output: 

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

167 

168 # adding effort (only for 3D coordinates) 

169 if crd == "3": 

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

171 

172 return instructions_list 

173 

174 @launchlogger 

175 def launch(self) -> int: 

176 """Execute the :class:`BabelAddHydrogens <babelm.babel_add_hydrogens.BabelAddHydrogens>` babelm.babel_add_hydrogens.BabelAddHydrogens object.""" 

177 

178 # check input/output paths and parameters 

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

180 

181 # Setup Biobb 

182 if self.check_restart(): 

183 return 0 

184 self.stage_files() 

185 

186 # create command line instruction 

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

188 

189 # Run Biobb block 

190 self.run_biobb() 

191 

192 # Copy files to host 

193 self.copy_to_host() 

194 

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

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

197 

198 # remove temporary folder(s) 

199 self.remove_tmp_files() 

200 

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

202 

203 return self.return_code 

204 

205 

206def babel_add_hydrogens( 

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

208) -> int: 

209 """Create the :class:`BabelAddHydrogens <babelm.babel_add_hydrogens.BabelAddHydrogens>` class and 

210 execute the :meth:`launch() <babelm.babel_add_hydrogens.BabelAddHydrogens.launch>` method.""" 

211 return BabelAddHydrogens(**dict(locals())).launch() 

212 

213 

214babel_add_hydrogens.__doc__ = BabelAddHydrogens.__doc__ 

215main = BabelAddHydrogens.get_main(babel_add_hydrogens, "Adds hydrogen atoms to small molecules.") 

216 

217 

218if __name__ == "__main__": 

219 main()