Coverage for biobb_gromacs/gromacs/editconf.py: 76%

68 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-25 09:23 +0000

1#!/usr/bin/env python3 

2 

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

4 

5import argparse 

6from typing import Optional 

7 

8from biobb_common.configuration import settings 

9from biobb_common.generic.biobb_object import BiobbObject 

10from biobb_common.tools import file_utils as fu 

11from biobb_common.tools.file_utils import launchlogger 

12 

13from biobb_gromacs.gromacs.common import get_gromacs_version 

14 

15 

16class Editconf(BiobbObject): 

17 """ 

18 | biobb_gromacs Editconf 

19 | Wrapper class for the `GROMACS editconf <http://manual.gromacs.org/current/onlinehelp/gmx-editconf.html>`_ module. 

20 | The GROMACS solvate module generates a box around the selected structure. 

21 

22 Args: 

23 input_gro_path (str): Path to the input GRO file. File type: input. `Sample file <https://github.com/bioexcel/biobb_gromacs/raw/master/biobb_gromacs/test/data/gromacs/editconf.gro>`_. Accepted formats: gro (edam:format_2033), pdb (edam:format_1476). 

24 output_gro_path (str): Path to the output GRO file. File type: output. `Sample file <https://github.com/bioexcel/biobb_gromacs/raw/master/biobb_gromacs/test/reference/gromacs/ref_editconf.gro>`_. Accepted formats: pdb (edam:format_1476), gro (edam:format_2033). 

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

26 * **distance_to_molecule** (*float*) - (1.0) [0~100|0.1] Distance of the box from the outermost atom in nm. ie 1.0nm = 10 Angstroms. 

27 * **box_vector_lenghts** (*str*) - (None) Array of floats defining the box vector lenghts ie "0.5 0.5 0.5". If this option is used the distance_to_molecule property will be ignored. 

28 * **box_type** (*str*) - ("cubic") Geometrical shape of the solvent box. Values: cubic (rectangular box with all sides equal), triclinic (triclinic box), dodecahedron (rhombic dodecahedron), octahedron (truncated octahedron). 

29 * **center_molecule** (*bool*) - (True) Center molecule in the box. 

30 * **gmx_lib** (*str*) - (None) Path set GROMACS GMXLIB environment variable. 

31 * **binary_path** (*str*) - ("gmx") Path to the GROMACS executable binary. 

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

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

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

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

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

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

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

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

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

41 

42 Examples: 

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

44 

45 from biobb_gromacs.gromacs.editconf import editconf 

46 prop = { 'distance_to_molecule': 1.0, 

47 'box_type': 'cubic'} 

48 editconf(input_gro_path='/path/to/structure.gro', 

49 output_gro_path='/path/to/newStructure.gro', 

50 properties=prop) 

51 

52 Info: 

53 * wrapped_software: 

54 * name: GROMACS Solvate 

55 * version: 2024.5 

56 * license: LGPL 2.1 

57 * ontology: 

58 * name: EDAM 

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

60 """ 

61 

62 def __init__( 

63 self, 

64 input_gro_path: str, 

65 output_gro_path: str, 

66 properties: Optional[dict] = None, 

67 **kwargs, 

68 ) -> None: 

69 properties = properties or {} 

70 

71 # Call parent class constructor 

72 super().__init__(properties) 

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

74 

75 # Input/Output files 

76 self.io_dict = { 

77 "in": {"input_gro_path": input_gro_path}, 

78 "out": {"output_gro_path": output_gro_path}, 

79 } 

80 

81 # Properties specific for BB 

82 self.distance_to_molecule = properties.get("distance_to_molecule", 1.0) 

83 self.box_vector_lenghts = properties.get("box_vector_lenghts") 

84 self.box_type = properties.get("box_type", "cubic") 

85 self.center_molecule = properties.get("center_molecule", True) 

86 

87 # Properties common in all GROMACS BB 

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

89 self.binary_path: str = properties.get("binary_path", "gmx") 

90 self.gmx_nobackup = properties.get("gmx_nobackup", True) 

91 self.gmx_nocopyright = properties.get("gmx_nocopyright", True) 

92 if self.gmx_nobackup: 

93 self.binary_path += " -nobackup" 

94 if self.gmx_nocopyright: 

95 self.binary_path += " -nocopyright" 

96 if not self.container_path: 

97 self.gmx_version = get_gromacs_version(self.binary_path) 

98 

99 # Check the properties 

100 self.check_properties(properties) 

101 self.check_arguments() 

102 

103 @launchlogger 

104 def launch(self) -> int: 

105 """Execute the :class:`Editconf <gromacs.editconf.Editconf>` object.""" 

106 

107 # Setup Biobb 

108 if self.check_restart(): 

109 return 0 

110 self.stage_files() 

111 

112 # Create command line 

113 self.cmd = [ 

114 self.binary_path, 

115 "editconf", 

116 "-f", 

117 self.stage_io_dict["in"]["input_gro_path"], 

118 "-o", 

119 self.stage_io_dict["out"]["output_gro_path"], 

120 "-bt", 

121 self.box_type, 

122 ] 

123 

124 if self.box_vector_lenghts: 

125 if not isinstance(self.box_vector_lenghts, str): 

126 self.box_vector_lenghts = " ".join(map(str, self.box_vector_lenghts)) 

127 self.cmd.append("-box") 

128 self.cmd.append(self.box_vector_lenghts) 

129 else: 

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

131 self.cmd.append(str(self.distance_to_molecule)) 

132 fu.log( 

133 "Distance of the box to molecule: %6.2f" % self.distance_to_molecule, 

134 self.out_log, 

135 self.global_log, 

136 ) 

137 

138 if self.center_molecule: 

139 self.cmd.append("-c") 

140 fu.log("Centering molecule in the box.", self.out_log, self.global_log) 

141 

142 fu.log("Box type: %s" % self.box_type, self.out_log, self.global_log) 

143 

144 if self.gmx_lib: 

145 self.env_vars_dict = self.gmx_lib 

146 

147 # Run Biobb block 

148 self.run_biobb() 

149 

150 # Copy files to host 

151 self.copy_to_host() 

152 

153 # Remove temporal files 

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

155 self.remove_tmp_files() 

156 

157 # self.check_arguments(output_files_created=True, raise_exception=False) 

158 return self.return_code 

159 

160 

161def editconf( 

162 input_gro_path: str, 

163 output_gro_path: str, 

164 properties: Optional[dict] = None, 

165 **kwargs, 

166) -> int: 

167 """Create :class:`Editconf <gromacs.editconf.Editconf>` class and 

168 execute the :meth:`launch() <gromacs.editconf.Editconf.launch>` method.""" 

169 

170 return Editconf( 

171 input_gro_path=input_gro_path, 

172 output_gro_path=output_gro_path, 

173 properties=properties, 

174 **kwargs, 

175 ).launch() 

176 

177 

178editconf.__doc__ = Editconf.__doc__ 

179 

180 

181def main(): 

182 parser = argparse.ArgumentParser( 

183 description="Wrapper of the GROMACS gmx editconf module.", 

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

185 ) 

186 parser.add_argument( 

187 "-c", 

188 "--config", 

189 required=False, 

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

191 ) 

192 

193 # Specific args of each building block 

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

195 required_args.add_argument("--input_gro_path", required=True) 

196 required_args.add_argument("--output_gro_path", required=True) 

197 

198 args = parser.parse_args() 

199 config = args.config if args.config else None 

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

201 

202 # Specific call of each building block 

203 editconf( 

204 input_gro_path=args.input_gro_path, 

205 output_gro_path=args.output_gro_path, 

206 properties=properties, 

207 ) 

208 

209 

210if __name__ == "__main__": 

211 main()