Coverage for biobb_pmx / pmxbiobb / pmxatom_mapping.py: 71%
101 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-19 14:26 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-19 14:26 +0000
1#!/usr/bin/env python3
3"""Module containing the PMX atom_mapping class and the command line interface."""
5import os
6import shutil
7import sys
8from pathlib import Path
9from typing import Optional
11from biobb_common.generic.biobb_object import BiobbObject
12from biobb_common.tools.file_utils import launchlogger
15class Pmxatom_mapping(BiobbObject):
16 """
17 | biobb_pmx Pmxatom_mapping
18 | Wrapper class for the `PMX atom_mapping <https://github.com/deGrootLab/pmx>`_ module.
19 | Perform atom mapping between two ligand structures.
21 Args:
22 input_structure1_path (str): Path to the input ligand structure file 1. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/lig1.pdb>`_. Accepted formats: pdb (edam:format_1476).
23 input_structure2_path (str): Path to the input ligand structure file 2. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/lig2.pdb>`_. Accepted formats: pdb (edam:format_1476).
24 output_pairs1_path (str): Path to the output pairs for the ligand structure 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_pairs1.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330).
25 output_pairs2_path (str): Path to the output pairs for the ligand structure 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_pairs2.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330).
26 output_log_path (str): Path to the log file. File type: output. Accepted formats: log (edam:format_2330), txt (edam:format_2330), out (edam:format_2330).
27 output_structure1_path (str) (Optional): Path to the superimposed structure for the ligand structure 1. File type: output. Accepted formats: pdb (edam:format_1476).
28 output_structure2_path (str) (Optional): Path to the superimposed structure for the ligand structure 2. File type: output. Accepted formats: pdb (edam:format_1476).
29 output_morph1_path (str) (Optional): Path to the morphable atoms for the ligand structure 1. File type: output. Accepted formats: pdb (edam:format_1476).
30 output_morph2_path (str) (Optional): Path to the morphable atoms for the ligand structure 2. File type: output. Accepted formats: pdb (edam:format_1476).
31 output_scaffold1_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 1. File type: output. Accepted formats: ndx (edam:format_2033).
32 output_scaffold2_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 2. File type: output. Accepted formats: ndx (edam:format_2033).
33 output_score_path (str) (Optional): Path to the morphing score. File type: output. Accepted formats: dat (edam:format_1637), txt (edam:format_2330).
35 properties (dic):
36 * **noalignment** (*bool*) - (False) Should the alignment method be disabled.
37 * **nomcs** (*bool*) - (False) Should the MCS method be disabled.
38 * **noH2H** (*bool*) - (True) Should non-polar hydrogens be discarded from morphing into any other hydrogen.
39 * **H2Hpolar** (*bool*) - (False) Should polar hydrogens be morphed into polar hydrogens.
40 * **H2Heavy** (*bool*) - (False) Should hydrogen be morphed into a heavy atom.
41 * **RingsOnly** (*bool*) - (False) Should rings only be used in the MCS search and alignemnt.
42 * **dMCS** (*bool*) - (False) Should the distance criterium be also applied in the MCS based search.
43 * **swap** (*bool*) - (False) Try swapping the molecule order which would be a cross-check and require double execution time.
44 * **nochirality** (*bool*) - (True) Perform chirality check for MCS mapping.
45 * **distance** (*float*) - (0.05) Distance (nm) between atoms to consider them morphable for alignment approach.
46 * **timeout** (*int*) - (10) Maximum time (s) for an MCS search.
47 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files.
48 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist.
49 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory.
50 * **container_path** (*str*) - (None) Path to the binary executable of your container.
51 * **container_image** (*str*) - (None) Container Image identifier.
52 * **container_volume_path** (*str*) - ("/inout") Path to an internal directory in the container.
53 * **container_working_dir** (*str*) - (None) Path to the internal CWD in the container.
54 * **container_user_id** (*str*) - (None) User number id to be mapped inside the container.
55 * **container_shell_path** (*str*) - ("/bin/bash") Path to the binary executable of the container shell.
57 Examples:
58 This is a use example of how to use the building block from Python::
60 from biobb_pmx.pmxbiobb.pmxatom_mapping import pmxatom_mapping
61 prop = {
62 'no-alignment' : True,
63 'distance': 0.05
64 }
65 pmxatom_mapping(input_structure1_path='/path/to/myStructure1.pdb',
66 input_structure2_path='/path/to/myStructure2.pdb',
67 output_pairs1_path='/path/to/myPairs1.dat',
68 output_pairs2_path='/path/to/myPairs2.dat',
69 output_log_path='/path/to/myLog.log',
70 properties=prop)
72 Info:
73 * wrapped_software:
74 * name: PMX atom_mapping
75 * version: >=1.0.1
76 * license: GNU
77 * ontology:
78 * name: EDAM
79 * schema: http://edamontology.org/EDAM.owl
81 """
83 def __init__(
84 self,
85 input_structure1_path: str,
86 input_structure2_path: str,
87 output_pairs1_path: str,
88 output_pairs2_path: str,
89 output_log_path: str,
90 output_structure1_path: Optional[str] = None,
91 output_structure2_path: Optional[str] = None,
92 output_morph1_path: Optional[str] = None,
93 output_morph2_path: Optional[str] = None,
94 output_scaffold1_path: Optional[str] = None,
95 output_scaffold2_path: Optional[str] = None,
96 output_score_path: Optional[str] = None,
97 properties: Optional[dict] = None,
98 **kwargs,
99 ) -> None:
100 properties = properties or {}
102 # Call parent class constructor
103 super().__init__(properties)
104 self.locals_var_dict = locals().copy()
106 # Input/Output files
107 self.io_dict = {
108 "in": {
109 "input_structure1_path": input_structure1_path,
110 "input_structure2_path": input_structure2_path,
111 },
112 "out": {
113 "output_pairs1_path": output_pairs1_path,
114 "output_pairs2_path": output_pairs2_path,
115 "output_log_path": output_log_path,
116 "output_structure1_path": output_structure1_path,
117 "output_structure2_path": output_structure2_path,
118 "output_morph1_path": output_morph1_path,
119 "output_morph2_path": output_morph2_path,
120 "output_scaffold1_path": output_scaffold1_path,
121 "output_scaffold2_path": output_scaffold2_path,
122 "output_score_path": output_score_path,
123 },
124 }
126 # Properties specific for BB
127 # self.noalignment = properties.get('noalignment', False)
128 # self.nomcs = properties.get('nomcs', False)
129 # self.noH2H = properties.get('noH2H', True)
130 # self.H2Hpolar = properties.get('H2Hpolar', False)
131 # self.H2Heavy = properties.get('H2Heavy', False)
132 # self.RingsOnly = properties.get('RingsOnly', False)
133 # self.dMCS = properties.get('dMCS', False)
134 # self.swap = properties.get('swap', False)
135 # self.nochirality = properties.get('nochirality', True)
136 # self.distance = properties.get('distance', 0.05)
137 # self.timeout = properties.get('timeout', 10)
139 self.noalignment = properties.get("noalignment")
140 self.nomcs = properties.get("nomcs")
141 self.noH2H = properties.get("noH2H")
142 self.H2Hpolar = properties.get("H2Hpolar")
143 self.H2Heavy = properties.get("H2Heavy")
144 self.RingsOnly = properties.get("RingsOnly")
145 self.dMCS = properties.get("dMCS")
146 self.swap = properties.get("swap")
147 self.nochirality = properties.get("nochirality")
148 self.distance = properties.get("distance")
149 self.timeout = properties.get("timeout")
151 # Properties common in all PMX BB
152 self.gmx_lib = properties.get("gmx_lib", None)
153 if not self.gmx_lib and os.environ.get("CONDA_PREFIX", ""):
154 python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
155 self.gmx_lib = str(
156 Path(os.environ.get("CONDA_PREFIX", "")).joinpath(
157 f"lib/python{python_version}/site-packages/pmx/data/mutff/"
158 )
159 )
160 if properties.get("container_path"):
161 self.gmx_lib = str(
162 Path("/usr/local/").joinpath(
163 "lib/python3.7/site-packages/pmx/data/mutff/"
164 )
165 )
166 self.binary_path = properties.get("binary_path", "pmx")
168 # Check the properties
169 self.check_properties(properties)
170 self.check_arguments()
172 @launchlogger
173 def launch(self) -> int:
174 """Execute the :class:`Pmxmutate <pmx.pmxmutate.Pmxmutate>` pmx.pmxmutate.Pmxmutate object."""
176 # Setup Biobb
177 if self.check_restart():
178 return 0
179 self.stage_files()
181 # Check if executable exists
182 if not self.container_path:
183 if not Path(self.binary_path).is_file():
184 if not shutil.which(self.binary_path):
185 raise FileNotFoundError(
186 "Executable %s not found. Check if it is installed in your system and correctly defined in the properties"
187 % self.binary_path
188 )
190 self.cmd = [
191 self.binary_path,
192 "atomMapping",
193 "-i1",
194 self.stage_io_dict["in"]["input_structure1_path"],
195 "-i2",
196 self.stage_io_dict["in"]["input_structure2_path"],
197 "-o1",
198 self.stage_io_dict["out"]["output_pairs1_path"],
199 "-o2",
200 self.stage_io_dict["out"]["output_pairs2_path"],
201 "-log",
202 self.stage_io_dict["out"]["output_log_path"],
203 ]
205 if self.stage_io_dict["out"].get("output_structure1_path"):
206 self.cmd.append("-opdb1")
207 self.cmd.append(self.stage_io_dict["out"]["output_structure1_path"])
209 if self.stage_io_dict["out"].get("output_structure2_path"):
210 self.cmd.append("-opdb2")
211 self.cmd.append(self.stage_io_dict["out"]["output_structure2_path"])
213 if self.stage_io_dict["out"].get("output_morph1_path"):
214 self.cmd.append("-opdbm1")
215 self.cmd.append(self.stage_io_dict["out"]["output_morph1_path"])
217 if self.stage_io_dict["out"].get("output_morph2_path"):
218 self.cmd.append("-opdbm2")
219 self.cmd.append(self.stage_io_dict["out"]["output_morph2_path"])
221 if self.stage_io_dict["out"].get("output_scaffold1_path"):
222 self.cmd.append("-n1")
223 self.cmd.append(self.stage_io_dict["out"]["output_scaffold1_path"])
225 if self.stage_io_dict["out"].get("output_scaffold2_path"):
226 self.cmd.append("-n2")
227 self.cmd.append(self.stage_io_dict["out"]["output_scaffold2_path"])
229 if self.stage_io_dict["out"].get("output_score_path"):
230 self.cmd.append("-score")
231 self.cmd.append(self.stage_io_dict["out"]["output_score_path"])
233 if self.noalignment:
234 self.cmd.append("--no-alignment")
235 if self.nomcs:
236 self.cmd.append("--no-mcs")
237 if self.noH2H:
238 self.cmd.append("--no-H2H")
239 if self.H2Hpolar:
240 self.cmd.append("--H2Hpolar")
241 if self.H2Heavy:
242 self.cmd.append("--H2Heavy")
243 if self.RingsOnly:
244 self.cmd.append("--RingsOnly")
245 if self.dMCS:
246 self.cmd.append("--dMCS")
247 if self.swap:
248 self.cmd.append("--swap")
249 if self.nochirality:
250 self.cmd.append("--no-chirality")
251 if self.distance:
252 self.cmd.append("--d")
253 self.cmd.append(str(self.distance))
254 if self.timeout:
255 self.cmd.append("--timeout")
256 self.cmd.append(str(self.timeout))
258 if self.gmx_lib:
259 self.env_vars_dict["GMXLIB"] = self.gmx_lib
261 # Run Biobb block
262 self.run_biobb()
264 # Copy files to host
265 self.copy_to_host()
267 self.remove_tmp_files()
269 self.check_arguments(output_files_created=True, raise_exception=False)
270 return self.return_code
273def pmxatom_mapping(
274 input_structure1_path: str,
275 input_structure2_path: str,
276 output_pairs1_path: str,
277 output_pairs2_path: str,
278 output_log_path: str,
279 output_structure1_path: Optional[str] = None,
280 output_structure2_path: Optional[str] = None,
281 output_morph1_path: Optional[str] = None,
282 output_morph2_path: Optional[str] = None,
283 output_scaffold1_path: Optional[str] = None,
284 output_scaffold2_path: Optional[str] = None,
285 output_score_path: Optional[str] = None,
286 properties: Optional[dict] = None,
287 **kwargs,
288) -> int:
289 """Create the :class:`Pmxatom_mapping <pmx.pmxmutate.Pmxatom_mapping>` class and
290 execute the :meth:`launch() <pmx.pmxatom_mapping.Pmxatom_mapping.launch> method."""
291 return Pmxatom_mapping(**dict(locals())).launch()
294pmxatom_mapping.__doc__ = Pmxatom_mapping.__doc__
295main = Pmxatom_mapping.get_main(pmxatom_mapping, "Run PMX atom mapping module")
297if __name__ == "__main__":
298 main()