Coverage for biobb_cmip / cmip / cmip_run.py: 51%
134 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 12:26 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 12:26 +0000
1#!/usr/bin/env python3
3"""Module containing the Cmip class and the command line interface."""
4import os
5import json
6from typing import Optional
7from typing import Any
8import shutil
9from pathlib import Path
10from biobb_common.generic.biobb_object import BiobbObject
11from biobb_common.tools import file_utils as fu
12from biobb_common.tools.file_utils import launchlogger
13from biobb_cmip.cmip.common import create_params_file
14from biobb_cmip.cmip.common import params_preset
15from biobb_cmip.cmip.common import get_grid
18class CmipRun(BiobbObject):
19 """
20 | biobb_cmip Titration
21 | Wrapper class for the CMIP cmip module.
22 | The CMIP cmip module. CMIP cmip module compute classical molecular interaction potentials.
24 Args:
25 input_pdb_path (str): Path to the input PDB file. File type: input. `Sample file <https://raw.githubusercontent.com/bioexcel/biobb_cmip/master/biobb_cmip/test/data/cmip/1kim_h.pdb>`_. Accepted formats: pdb (edam:format_1476).
26 input_probe_pdb_path (str): Path to the input probe file in PDB format. File type: input. `Sample file <https://raw.githubusercontent.com/bioexcel/biobb_cmip/master/biobb_cmip/test/data/cmip/RBD-hACE2.RBD.cmip.pdb>`_. Accepted formats: pdb (edam:format_1476).
27 output_pdb_path (str) (Optional): Path to the output PDB file. File type: output. `Sample file <https://raw.githubusercontent.com/bioexcel/biobb_cmip/master/biobb_cmip/test/reference/cmip/1kim_neutral.pdb>`_. Accepted formats: pdb (edam:format_1476).
28 output_grd_path (str) (Optional): Path to the output grid file in GRD format. File type: output. Accepted formats: grd (edam:format_2330).
29 output_cube_path (str) (Optional): Path to the output grid file in cube format. File type: output. Accepted formats: cube (edam:format_2330).
30 output_rst_path (str) (Optional): Path to the output restart file. File type: output. Accepted formats: txt (edam:format_2330).
31 input_rst_path (str) (Optional): Path to the input restart file. File type: input. Accepted formats: txt (edam:format_2330).
32 output_byat_path (str) (Optional): Path to the output atom by atom energy file. File type: output. Accepted formats: txt (edam:format_2330), out (edam:format_2330).
33 output_log_path (str) (Optional): Path to the output CMIP log file LOG. File type: output. Accepted formats: log (edam:format_2330).
34 input_vdw_params_path (str) (Optional): Path to the CMIP input Van der Waals force parameters, if not provided the CMIP conda installation one is used ("$CONDA_PREFIX/share/cmip/dat/vdwprm"). File type: input. Accepted formats: txt (edam:format_2330).
35 input_params_path (str) (Optional): Path to the CMIP input parameters file. File type: input. Accepted formats: txt (edam:format_2330).
36 output_json_box_path (str) (Optional): Path to the output CMIP box in JSON format. File type: output. Accepted formats: json (edam:format_3464).
37 output_json_external_box_path (str) (Optional): Path to the output external CMIP box in JSON format. File type: output. Accepted formats: json (edam:format_3464).
38 input_json_box_path (str) (Optional): Path to the input CMIP box in JSON format. File type: input. Accepted formats: json (edam:format_3464).
39 input_json_external_box_path (str) (Optional): Path to the input CMIP box in JSON format. File type: input. Accepted formats: json (edam:format_3464).
40 properties (dict - Python dictionary object containing the tool parameters, not input/output files):
41 * **execution_type** (*str*) - ("mip_pos") Default options for the params file, each one creates a different params file. Values: check_only (Dry Run of CMIP), mip_pos (MIP O+ Mehler Solmajer dielectric), mip_neg (MIP O- Mehler Solmajer dielectric), mip_neu (MIP Oxygen Mehler Solmajer dielectric), solvation (Solvation & MEP), pb_interaction_energy (Docking Interaction energy calculation. PB electrostatics), docking (Docking Mehler Solmajer dielectric), docking_rst (Docking from restart file).
42 * **params** (*dict*) - ({}) CMIP options specification.
43 * **binary_path** (*str*) - ("cmip") Path to the CMIP cmip executable binary.
44 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files.
45 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist.
46 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory.
47 * **container_path** (*str*) - (None) Path to the binary executable of your container.
48 * **container_image** (*str*) - ("cmip/cmip:latest") Container Image identifier.
49 * **container_volume_path** (*str*) - ("/data") Path to an internal directory in the container.
50 * **container_working_dir** (*str*) - (None) Path to the internal CWD in the container.
51 * **container_user_id** (*str*) - (None) User number id to be mapped inside the container.
52 * **container_shell_path** (*str*) - ("/bin/bash") Path to the binary executable of the container shell.
55 Examples:
56 This is a use example of how to use the building block from Python::
58 from biobb_cmip.cmip.cmip import cmip
59 prop = { 'binary_path': 'cmip' }
60 cmip(input_pdb_path='/path/to/myStructure.pdb',
61 output_pdb_path='/path/to/newStructure.pdb',
62 output_log_path='/path/to/newStructureLog.log',
63 properties=prop)
65 Info:
66 * wrapped_software:
67 * name: CMIP cmip
68 * version: 2.7.0
69 * license: Apache-2.0
70 * ontology:
71 * name: EDAM
72 * schema: http://edamontology.org/EDAM.owl
73 """
75 def __init__(self, input_pdb_path: str, input_probe_pdb_path: Optional[str] = None, output_pdb_path: Optional[str] = None,
76 output_grd_path: Optional[str] = None, output_cube_path: Optional[str] = None, output_rst_path: Optional[str] = None,
77 input_rst_path: Optional[str] = None, output_byat_path: Optional[str] = None, output_log_path: Optional[str] = None,
78 input_vdw_params_path: Optional[str] = None, input_params_path: Optional[str] = None, output_json_box_path: Optional[str] = None,
79 output_json_external_box_path: Optional[str] = None, input_json_box_path: Optional[str] = None,
80 input_json_external_box_path: Optional[str] = None, properties: Optional[dict] = None, **kwargs) -> None:
82 properties = properties or {}
84 # Call parent class constructor
85 super().__init__(properties)
86 self.locals_var_dict = locals().copy()
88 # Input/Output files
89 self.io_dict = {
90 "in": {"input_pdb_path": input_pdb_path, "input_probe_pdb_path": input_probe_pdb_path,
91 "input_vdw_params_path": input_vdw_params_path, "input_params_path": input_params_path,
92 "input_json_box_path": input_json_box_path,
93 "input_json_external_box_path": input_json_external_box_path,
94 "input_rst_path": input_rst_path},
95 "out": {"output_pdb_path": output_pdb_path, "output_grd_path": output_grd_path,
96 "output_cube_path": output_cube_path, "output_rst_path": output_rst_path,
97 "output_byat_path": output_byat_path, "output_log_path": output_log_path,
98 "output_json_box_path": output_json_box_path,
99 "output_json_external_box_path": output_json_external_box_path}
100 }
102 # Properties specific for BB
103 self.binary_path = properties.get('binary_path', 'cmip')
104 self.execution_type = properties.get('execution_type', 'mip_pos')
105 self.params = {k: str(v) for k, v in properties.get('params', dict()).items()}
107 if not self.io_dict['in'].get('input_vdw_params_path'):
108 self.io_dict['in']['input_vdw_params_path'] = f"{os.environ.get('CONDA_PREFIX')}/share/cmip/dat/vdwprm"
109 self.io_dict['in']['combined_params_path'] = properties.get('combined_params_path', 'params')
111 # Check the properties
112 self.check_properties(properties)
113 self.check_arguments()
115 @launchlogger
116 def launch(self) -> int:
117 """Execute the :class:`Cmip <cmip.cmip.Cmip>` object."""
119 # Setup Biobb
120 if self.check_restart():
121 return 0
123 # Check if output_pdb_path ends with ".pdb" and does not contain underscores
124 if self.io_dict['out']['output_pdb_path']:
125 if (not self.io_dict['out']['output_pdb_path'].endswith('.pdb')) or \
126 ("_" in str(Path(self.io_dict['out']['output_pdb_path']).name)):
127 fu.log(f"ERROR: output_pdb_path ({self.io_dict['out']['output_pdb_path']}) "
128 f"name must end in .pdb and not contain underscores", self.out_log, self.global_log)
129 raise ValueError(f"ERROR: output_pdb_path ({self.io_dict['out']['output_pdb_path']})"
130 f"name must end in .pdb and not contain underscores")
132 params_preset_dict: dict[str, Any] = params_preset(execution_type=self.execution_type)
133 if self.io_dict['in']["input_json_external_box_path"]:
134 params_preset_dict["readgrid0"] = 0
135 origin, size, grid_params = get_grid(self.io_dict['in']["input_json_external_box_path"])
136 params_preset_dict['grid_int0'] = \
137 f"INTX0={grid_params['INT'][0]},INTY0={grid_params['INT'][1]},INTZ0={grid_params['INT'][2]}"
138 params_preset_dict['grid_cen0'] = \
139 f"CENX0={grid_params['CEN'][0]},CENY0={grid_params['CEN'][1]},CENZ0={grid_params['CEN'][2]}"
140 params_preset_dict['grid_dim0'] = \
141 f"DIMX0={grid_params['DIM'][0]},DIMY0={grid_params['DIM'][1]},DIMZ0={grid_params['DIM'][2]}"
143 if self.io_dict['in']["input_json_box_path"]:
144 params_preset_dict["readgrid"] = 0
145 origin, size, grid_params = get_grid(self.io_dict['in']["input_json_box_path"])
146 params_preset_dict['grid_int'] = \
147 f"INTX={grid_params['INT'][0]},INTY={grid_params['INT'][1]},INTZ={grid_params['INT'][2]}"
148 params_preset_dict['grid_cen'] = \
149 f"CENX={grid_params['CEN'][0]},CENY={grid_params['CEN'][1]},CENZ={grid_params['CEN'][2]}"
150 params_preset_dict['grid_dim'] = \
151 f"DIMX={grid_params['DIM'][0]},DIMY={grid_params['DIM'][1]},DIMZ={grid_params['DIM'][2]}"
153 if self.io_dict['out']['output_json_box_path'] or self.io_dict['out']['output_json_external_box_path']:
154 params_preset_dict['WRITELOG'] = 1
155 key_value_log_dir = fu.create_unique_dir()
156 self.io_dict['out']['key_value_log_path'] = str(Path(key_value_log_dir).joinpath("key_value_cmip_log.log"))
157 self.tmp_files.append(key_value_log_dir)
159 # Restart OUT
160 if self.io_dict["out"].get("output_rst_path"):
161 params_preset_dict['FULLRST'] = 1 # type: ignore
162 params_preset_dict['OREST'] = 1
164 # Restart IN
165 if self.io_dict['in']["input_rst_path"]:
166 params_preset_dict['IREST'] = 2
167 if not self.io_dict["out"].get("output_rst_path"):
168 self.io_dict["out"]["output_rst_path"] = fu.create_unique_file_path()
169 shutil.copy2(self.io_dict['in']["input_rst_path"], self.io_dict["out"]["output_rst_path"])
171 else:
172 params_preset_dict['IREST'] = 0
174 combined_params_dir = fu.create_unique_dir()
175 self.io_dict['in']['combined_params_path'] = create_params_file(
176 output_params_path=str(Path(combined_params_dir).joinpath(self.io_dict['in']['combined_params_path'])),
177 input_params_path=self.io_dict['in'].get('input_params_path'),
178 params_preset_dict=params_preset_dict,
179 params_properties_dict=self.params)
181 self.stage_files()
183 self.cmd = [self.binary_path,
184 '-i', self.stage_io_dict['in']['combined_params_path'],
185 '-vdw', self.stage_io_dict['in']['input_vdw_params_path'],
186 '-hs', self.stage_io_dict['in']['input_pdb_path']]
188 if self.stage_io_dict["in"].get("input_probe_pdb_path") and Path(
189 self.io_dict["in"].get("input_probe_pdb_path", "")).exists():
190 self.cmd.append('-pr')
191 self.cmd.append(self.stage_io_dict["in"].get("input_probe_pdb_path"))
193 if self.stage_io_dict["out"].get("output_pdb_path"):
194 self.cmd.append('-outpdb')
195 self.cmd.append(self.stage_io_dict['out']['output_pdb_path'])
197 if self.stage_io_dict["out"].get("output_grd_path"):
198 self.cmd.append('-grdout')
199 self.cmd.append(self.stage_io_dict["out"]["output_grd_path"])
201 if self.stage_io_dict["out"].get("output_cube_path"):
202 self.cmd.append('-cube')
203 self.cmd.append(self.stage_io_dict["out"]["output_cube_path"])
205 if self.stage_io_dict["out"].get("output_rst_path"):
206 self.cmd.append('-rst')
207 self.cmd.append(self.stage_io_dict["out"]["output_rst_path"])
209 if self.stage_io_dict["out"].get("output_byat_path"):
210 self.cmd.append('-byat')
211 self.cmd.append(self.stage_io_dict["out"]["output_byat_path"])
213 if self.stage_io_dict["out"].get("output_log_path"):
214 self.cmd.append('-o')
215 self.cmd.append(self.stage_io_dict["out"]["output_log_path"])
217 if self.stage_io_dict['out'].get('output_json_box_path') or self.stage_io_dict['out'].get('output_json_external_box_path'):
218 self.cmd.append('-l')
219 self.cmd.append(self.stage_io_dict["out"]["key_value_log_path"])
221 # Run Biobb block
222 self.run_biobb()
224 # CMIP removes or adds a .pdb extension from pdb output name
225 # manual copy_to_host or unstage
226 if self.io_dict['out'].get('output_pdb_path'):
227 output_pdb_path = str(Path(self.stage_io_dict["unique_dir"]).joinpath(Path(self.io_dict['out'].get('output_pdb_path', '')).name))
228 if Path(output_pdb_path[:-4]).exists():
229 shutil.move(output_pdb_path[:-4], self.io_dict['out'].get('output_pdb_path', ''))
230 elif Path(output_pdb_path + ".pdb").exists():
231 shutil.move(output_pdb_path + ".pdb", self.io_dict['out'].get('output_pdb_path', ''))
232 elif not Path(output_pdb_path).exists():
233 fu.log(f"WARNING: File not found output_pdb_path: {output_pdb_path}", self.out_log, self.global_log)
235 # Replace "ATOMTM" tag for "ATOM "
237 output_pdb_path = self.io_dict['out'].get('output_pdb_path', '')
238 if output_pdb_path:
239 if Path(output_pdb_path).exists():
240 with open(output_pdb_path) as pdb_file:
241 list_pdb_lines = pdb_file.readlines()
242 with open(output_pdb_path, 'w') as pdb_file:
243 for line in list_pdb_lines:
244 pdb_file.write(line.replace('ATOMTM', 'ATOM '))
245 else:
246 fu.log(f"WARNING: File not found output_pdb_path: {output_pdb_path} Abs Path: {Path(output_pdb_path).resolve()}", self.out_log, self.global_log)
248 # Create json_box_path file from CMIP log file
249 if self.io_dict['out'].get('output_json_box_path'):
250 origin, size, grid_params = get_grid(self.stage_io_dict["out"]["output_log_path"])
251 grid_params['DIM'] = (int(grid_params['DIM'][0]),
252 int(grid_params['DIM'][1]),
253 int(grid_params['DIM'][2]))
254 size_dict = {'x': round(grid_params['DIM'][0] * grid_params['INT'][0], 3),
255 'y': round(grid_params['DIM'][1] * grid_params['INT'][1], 3),
256 'z': round(grid_params['DIM'][2] * grid_params['INT'][2], 3)}
257 origin_dict = {'x': round(grid_params['CEN'][0] - size_dict['x'] / 2, 3),
258 'y': round(grid_params['CEN'][1] - size_dict['y'] / 2, 3),
259 'z': round(grid_params['CEN'][0] - size_dict['z'] / 2, 3)}
260 grid_dict = {'origin': origin_dict,
261 'size': size_dict,
262 'params': grid_params}
263 with open(self.io_dict['out'].get('output_json_box_path', ''), 'w') as json_file:
264 json_file.write(json.dumps(grid_dict, indent=4))
266 # Create external_json_box_path file from CMIP log file
267 if self.io_dict['out'].get('output_json_external_box_path'):
268 origin, size, grid_params = get_grid(self.stage_io_dict["out"]["output_log_path"], True)
269 grid_params['DIM'] = (int(grid_params['DIM'][0]),
270 int(grid_params['DIM'][1]),
271 int(grid_params['DIM'][2]))
272 size_dict = {'x': round(grid_params['DIM'][0] * grid_params['INT'][0], 3),
273 'y': round(grid_params['DIM'][1] * grid_params['INT'][1], 3),
274 'z': round(grid_params['DIM'][2] * grid_params['INT'][2], 3)}
275 origin_dict = {'x': round(grid_params['CEN'][0] - size_dict['x'] / 2, 3),
276 'y': round(grid_params['CEN'][1] - size_dict['y'] / 2, 3),
277 'z': round(grid_params['CEN'][0] - size_dict['z'] / 2, 3)}
278 grid_dict = {'origin': origin_dict,
279 'size': size_dict,
280 'params': grid_params}
281 with open(self.io_dict['out'].get('output_json_external_box_path', ''), 'w') as json_file:
282 json_file.write(json.dumps(grid_dict, indent=4))
284 # Copy files to host
285 self.copy_to_host()
287 # remove temporary folder(s)
288 self.tmp_files.append(combined_params_dir)
289 self.remove_tmp_files()
291 self.check_arguments(output_files_created=True, raise_exception=False)
293 return self.return_code
296def cmip_run(input_pdb_path: str, input_probe_pdb_path: Optional[str] = None, output_pdb_path: Optional[str] = None,
297 output_grd_path: Optional[str] = None, output_cube_path: Optional[str] = None, output_rst_path: Optional[str] = None,
298 output_byat_path: Optional[str] = None, output_log_path: Optional[str] = None,
299 input_vdw_params_path: Optional[str] = None, input_params_path: Optional[str] = None, output_json_box_path: Optional[str] = None,
300 output_json_external_box_path: Optional[str] = None, input_json_box_path: Optional[str] = None,
301 input_json_external_box_path: Optional[str] = None, properties: Optional[dict] = None, **kwargs) -> int:
302 """Create :class:`Cmip <cmip.cmip.Cmip>` class and
303 execute the :meth:`launch() <cmip.cmip.Cmip.launch>` method."""
304 return CmipRun(**dict(locals())).launch()
307cmip_run.__doc__ = CmipRun.__doc__
308main = CmipRun.get_main(cmip_run, "Wrapper of the CMIP cmip module.")
310if __name__ == '__main__':
311 main()