Coverage for biobb_pmx / pmxbiobb / pmxanalyse.py: 83%
107 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 17:32 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 17:32 +0000
1#!/usr/bin/env python3
3"""Module containing the PMX analyse class and the command line interface."""
5import shutil
6from pathlib import Path
7from typing import Optional
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
14class Pmxanalyse(BiobbObject):
15 """
16 | biobb_pmx Pmxanalyse
17 | Wrapper class for the `PMX analyse <https://github.com/deGrootLab/pmx>`_ module.
18 | Analyze the work values from the dgdl.xvg files of the A and B states to calculate the free energy difference between two states.
20 Args:
21 input_a_xvg_zip_path (str): Path the zip file containing the dgdl.xvg files of the A state. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/xvg_A.zip>`_. Accepted formats: zip (edam:format_3987).
22 input_b_xvg_zip_path (str): Path the zip file containing the dgdl.xvg files of the B state. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/xvg_B.zip>`_. Accepted formats: zip (edam:format_3987).
23 output_result_path (str): Path to the TXT results file. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_result.txt>`_. Accepted formats: txt (edam:format_2330).
24 output_work_plot_path (str): Path to the PNG plot results file. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_plot.png>`_. Accepted formats: png (edam:format_3603).
25 properties (dic):
26 * **method** (*str*) - ("CGI BAR JARZ") Choose one or more estimators to use. Values: CGI (Crooks Gaussian Intersection), BAR (Bennet Acceptance Ratio), JARZ (Jarzynski's estimator).
27 * **temperature** (*float*) - (298.15) [0~1000|0.05] Temperature in Kelvin.
28 * **nboots** (*int*) - (0) [0~1000|1] Number of bootstrap samples to use for the bootstrap estimate of the standard errors.
29 * **nblocks** (*int*) - (1) [0~1000|1] Number of blocks to divide the data into for an estimate of the standard error.
30 * **integ_only** (*bool*) - (False) Whether to do integration only.
31 * **reverseB** (*bool*) - (False) Whether to reverse the work values for the backward (B->A) transformation.
32 * **skip** (*int*) - (1) [0~1000|1] Skip files.
33 * **slice** (*str*) - (None) Subset of trajectories to analyze. Provide list slice, e.g. "10 50" will result in selecting dhdl_files[10:50].
34 * **rand** (*int*) - (None) [0~1000|1] Take a random subset of trajectories. Default is None (do not take random subset).
35 * **index** (*str*) - (None) Zero-based index of files to analyze (e.g. "0 10 20 50 60"). It keeps the dhdl.xvg files according to their position in the list, sorted according to the filenames.
36 * **prec** (*int*) - (2) [0~100|1] The decimal precision of the screen/file output.
37 * **units** (*str*) - ("kJ") The units of the output. Values: kJ (Kilojoules), kcal (Kilocalories), kT (the product of the Boltzmann constant k and the temperature).
38 * **no_ks** (*bool*) - (False) Whether to do a Kolmogorov-Smirnov test to check whether the Gaussian assumption for CGI holds.
39 * **nbins** (*int*) - (20) [0~1000|1] Number of histograms bins for the plot.
40 * **dpi** (*int*) - (300) [72~2048|1] Resolution of the plot.
41 * **binary_path** (*str*) - ("pmx") Path to the PMX command line interface.
42 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files.
43 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist.
44 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory.
45 * **container_path** (*str*) - (None) Path to the binary executable of your container.
46 * **container_image** (*str*) - ("gromacs/gromacs:latest") Container Image identifier.
47 * **container_volume_path** (*str*) - ("/data") Path to an internal directory in the container.
48 * **container_working_dir** (*str*) - (None) Path to the internal CWD in the container.
49 * **container_user_id** (*str*) - (None) User number id to be mapped inside the container.
50 * **container_shell_path** (*str*) - ("/bin/bash") Path to the binary executable of the container shell.
52 Examples:
53 This is a use example of how to use the building block from Python::
55 from biobb_pmx.pmxbiobb.pmxanalyse import pmxanalyse
56 prop = {
57 'method': 'CGI BAR JARZ',
58 'temperature': 298.15,
59 'dpi': 600
60 }
61 pmxanalyse(input_a_xvg_zip_path='/path/to/myAStateFiles.zip',
62 input_b_xvg_zip_path='/path/to/myBStateFiles.zip',
63 output_result_path='/path/to/newResults.txt',
64 output_work_plot_path='/path/to/newResults.png',
65 properties=prop)
67 Info:
68 * wrapped_software:
69 * name: PMX analyse
70 * version: >=1.0.1
71 * license: GNU
72 * ontology:
73 * name: EDAM
74 * schema: http://edamontology.org/EDAM.owl
76 """
78 def __init__(
79 self,
80 input_a_xvg_zip_path: str,
81 input_b_xvg_zip_path: str,
82 output_result_path: str,
83 output_work_plot_path: str,
84 properties: Optional[dict] = None,
85 **kwargs,
86 ) -> None:
87 properties = properties or {}
89 # Call parent class constructor
90 super().__init__(properties)
91 self.locals_var_dict = locals().copy()
93 # Input/Output files
94 self.io_dict = {
95 "in": {},
96 "out": {
97 "output_result_path": output_result_path,
98 "output_work_plot_path": output_work_plot_path,
99 },
100 }
101 # Should not be copied inside container
102 self.input_a_xvg_zip_path = input_a_xvg_zip_path
103 self.input_b_xvg_zip_path = input_b_xvg_zip_path
105 # Properties specific for BB
106 self.method = properties.get("method", "CGI BAR JARZ")
107 self.temperature = properties.get("temperature", 298.15)
108 self.nboots = properties.get("nboots", 0)
109 self.nblocks = properties.get("nblocks", 1)
110 self.integ_only = properties.get("integ_only", False)
111 self.reverseB = properties.get("reverseB", False)
112 self.skip = properties.get("skip", 1)
113 self.slice = properties.get("slice", None)
114 self.rand = properties.get("rand", None)
115 self.index = properties.get("index", None)
116 self.prec = properties.get("prec", 2)
117 self.units = properties.get("units", "kJ")
118 self.no_ks = properties.get("no_ks", False)
119 self.nbins = properties.get("nbins", 20)
120 self.dpi = properties.get("dpi", 300)
122 # Properties common in all PMX BB
123 self.binary_path = properties.get("binary_path", "pmx")
125 # Check the properties
126 self.check_properties(properties)
127 self.check_arguments()
129 @launchlogger
130 def launch(self) -> int:
131 """Execute the :class:`Pmxanalyse <pmx.pmxanalyse.Pmxanalyse>` pmx.pmxanalyse.Pmxanalyse object."""
133 # Setup Biobb
134 if self.check_restart():
135 return 0
136 self.stage_files()
138 # Check if executable is exists
139 if not self.container_path:
140 if not Path(self.binary_path).is_file():
141 if not shutil.which(self.binary_path):
142 raise FileNotFoundError(
143 "Executable %s not found. Check if it is installed in your system and correctly defined in the properties"
144 % self.binary_path
145 )
147 list_a_dir = fu.create_unique_dir()
148 list_b_dir = fu.create_unique_dir()
149 list_a = list(
150 filter(
151 lambda f: Path(f).exists() and Path(f).stat().st_size > 10,
152 fu.unzip_list(self.input_a_xvg_zip_path, list_a_dir, self.out_log),
153 )
154 )
155 list_b = list(
156 filter(
157 lambda f: Path(f).exists() and Path(f).stat().st_size > 10,
158 fu.unzip_list(self.input_b_xvg_zip_path, list_b_dir, self.out_log),
159 )
160 )
161 string_a = " ".join(list_a)
162 string_b = " ".join(list_b)
164 # Copy extra files to container: two directories containing the xvg files
165 if self.container_path:
166 shutil.copytree(
167 list_a_dir,
168 Path(self.stage_io_dict.get("unique_dir", "")).joinpath(
169 Path(list_a_dir).name
170 ),
171 )
172 shutil.copytree(
173 list_b_dir,
174 Path(self.stage_io_dict.get("unique_dir", "")).joinpath(
175 Path(list_b_dir).name
176 ),
177 )
178 container_volume = " " + self.container_volume_path + "/"
179 string_a = self.container_volume_path + "/" + container_volume.join(list_a)
180 string_b = self.container_volume_path + "/" + container_volume.join(list_b)
182 self.cmd = [
183 self.binary_path,
184 "analyse",
185 "-fA",
186 string_a,
187 "-fB",
188 string_b,
189 "-o",
190 self.stage_io_dict["out"]["output_result_path"],
191 "-w",
192 self.stage_io_dict["out"]["output_work_plot_path"],
193 ]
195 if self.method:
196 self.cmd.append("-m")
197 self.cmd.append(self.method)
198 if self.temperature:
199 self.cmd.append("-t")
200 self.cmd.append(str(self.temperature))
201 if self.nboots:
202 self.cmd.append("-b")
203 self.cmd.append(str(self.nboots))
204 if self.nblocks:
205 self.cmd.append("-n")
206 self.cmd.append(str(self.nblocks))
207 if self.integ_only:
208 self.cmd.append("--integ_only")
209 if self.reverseB:
210 self.cmd.append("--reverseB")
211 if self.skip:
212 self.cmd.append("--skip")
213 self.cmd.append(str(self.skip))
214 if self.slice:
215 self.cmd.append("--slice")
216 self.cmd.append(self.slice)
217 if self.rand:
218 self.cmd.append("--rand")
219 if self.index:
220 self.cmd.append("--index")
221 self.cmd.append(self.index)
222 if self.prec:
223 self.cmd.append("--prec")
224 self.cmd.append(str(self.prec))
225 if self.units:
226 self.cmd.append("--units")
227 self.cmd.append(self.units)
228 if self.no_ks:
229 self.cmd.append("--no_ks")
230 if self.nbins:
231 self.cmd.append("--nbins")
232 self.cmd.append(str(self.nbins))
233 if self.dpi:
234 self.cmd.append("--dpi")
235 self.cmd.append(str(self.dpi))
237 # Run Biobb block
238 self.run_biobb()
240 # Copy files to host
241 self.copy_to_host()
243 self.tmp_files.extend([list_a_dir, list_b_dir])
244 self.remove_tmp_files()
246 self.check_arguments(output_files_created=True, raise_exception=False)
247 return self.return_code
250def pmxanalyse(
251 input_a_xvg_zip_path: str,
252 input_b_xvg_zip_path: str,
253 output_result_path: str,
254 output_work_plot_path: str,
255 properties: Optional[dict] = None,
256 **kwargs,
257) -> int:
258 """Create the :class:`Pmxanalyse <pmx.pmxanalyse.Pmxanalyse>` class and
259 execute the :meth:`launch() <pmx.pmxanalyse.Pmxanalyse.launch> method."""
260 return Pmxanalyse(**dict(locals())).launch()
263pmxanalyse.__doc__ = Pmxanalyse.__doc__
264main = Pmxanalyse.get_main(pmxanalyse, "Wrapper class for the PMX analyse module.")
266if __name__ == "__main__":
267 main()