Coverage for biobb_pmx/pmxbiobb/pmxligand_hybrid.py: 79%
82 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-29 06:59 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-29 06:59 +0000
1#!/usr/bin/env python3
3"""Module containing the PMX ligand_hybrid class and the command line interface."""
5import os
6import shutil
7import sys
8from pathlib import Path, PurePath
9from typing import Optional
11from biobb_common.generic.biobb_object import BiobbObject
12from biobb_common.tools.file_utils import launchlogger
15class Pmxligand_hybrid(BiobbObject):
16 """
17 | biobb_pmx Pmxligand_hybrid
18 | Wrapper class for the `PMX ligand_hybrid <https://github.com/deGrootLab/pmx>`_ module.
19 | Create a hybrid topology and structure based on 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 input_topology1_path (str): Path to the input ligand topology file 1. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/topoLig1.itp>`_. Accepted formats: itp (edam:format_3883).
25 input_topology2_path (str): Path to the input ligand topology file 2. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/topoLig2.itp>`_. Accepted formats: itp (edam:format_3883).
26 input_pairs_path (str) (Optional): Path to the input atom pair mapping. File type: input. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/data/pmx/myPairs1.dat>`_. Accepted formats: dat (edam:format_1637), txt (edam:format_2330).
27 input_scaffold1_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 1. File type: input. Accepted formats: ndx (edam:format_2033).
28 input_scaffold2_path (str) (Optional): Path to the index of atoms to consider for the ligand structure 2. File type: input. Accepted formats: ndx (edam:format_2033).
29 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).
30 output_structure1_path (str): Path to the output hybrid structure based on the ligand 1. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_hybridStructure1.pdb>`_. Accepted formats: pdb (edam:format_1476).
31 output_structure2_path (str): Path to the output hybrid structure based on the ligand 2. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_hybridStructure2.pdb>`_. Accepted formats: pdb (edam:format_1476).
32 output_topology_path (str): Path to the output hybrid topology. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_hybridTopology.itp>`_. Accepted formats: itp (edam:format_3883).
33 output_atomtypes_path (str): Path to the atom types for the output hybrid topology. File type: output. `Sample file <https://github.com/bioexcel/biobb_pmx/raw/master/biobb_pmx/test/reference/pmx/ref_hybridAtomTypes.itp>`_. Accepted formats: itp (edam:format_3883).
35 properties (dic):
36 * **fit** (*bool*) - (False) Fit ligand structure 1 onto ligand structure 2 (Only used if input_pairs_path is provided).
37 * **split** (*bool*) - (False) Split the topology into separate transitions.
38 * **scDUMm** (*float*) - (1.0) Scale dummy masses using the counterpart atoms.
39 * **scDUMa** (*float*) - (1.0) Scale bonded dummy angle parameters.
40 * **scDUMd** (*float*) - (1.0) Scale bonded dummy dihedral parameters.
41 * **deAng** (*bool*) - (False) Decouple angles composed of 1 dummy and 2 non-dummies.
42 * **distance** (*float*) - (0.05) Distance (nm) between atoms to consider them morphable for alignment approach (Only used if input_pairs_path is not provided).
43 * **binary_path** (*str*) - ("pmx") Path to the PMX command line interface.
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*) - (None) Container Image identifier.
49 * **container_volume_path** (*str*) - ("/inout") 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.
54 Examples:
55 This is a use example of how to use the building block from Python::
57 from biobb_pmx.pmxbiobb.pmxligand_hybrid import pmxligand_hybrid
58 prop = {
59 'fit' : True,
60 'distance': 0.05
61 }
62 pmxligand_hybrid(input_structure1_path='/path/to/myStructure1.pdb',
63 input_structure2_path='/path/to/myStructure2.pdb',
64 input_topology1_path='/path/to/myTopology1.pdb',
65 input_topology2_path='/path/to/myTopology2.pdb',
66 input_pairs_path='/path/to/myPairs.dat',
67 output_log_path='/path/to/myLog.log',
68 output_structure1_path='/path/to/myStructureOutput1.pdb',
69 output_structure2_path='/path/to/myStructureOutput2.pdb',
70 output_topology_path='/path/to/myTopologyOutput.pdb',
71 output_atomtypes_path='/path/to/myAtomTypesOutput.pdb',
72 properties=prop)
74 Info:
75 * wrapped_software:
76 * name: PMX ligand_hybrid
77 * version: >=1.0.1
78 * license: GNU
79 * ontology:
80 * name: EDAM
81 * schema: http://edamontology.org/EDAM.owl
83 """
85 def __init__(
86 self,
87 input_structure1_path: str,
88 input_structure2_path: str,
89 input_topology1_path: str,
90 input_topology2_path: str,
91 output_log_path: str,
92 output_structure1_path: str,
93 output_structure2_path: str,
94 output_topology_path: str,
95 output_atomtypes_path: str,
96 input_scaffold1_path: Optional[str] = None,
97 input_scaffold2_path: Optional[str] = None,
98 input_pairs_path: Optional[str] = None,
99 properties: Optional[dict] = None,
100 **kwargs,
101 ) -> None:
102 properties = properties or {}
104 # Call parent class constructor
105 super().__init__(properties)
106 self.locals_var_dict = locals().copy()
108 # Input/Output files
109 self.io_dict = {
110 "in": {
111 "input_structure1_path": input_structure1_path,
112 "input_structure2_path": input_structure2_path,
113 "input_topology1_path": input_topology1_path,
114 "input_topology2_path": input_topology2_path,
115 "input_scaffold1_path": input_scaffold1_path,
116 "input_scaffold2_path": input_scaffold2_path,
117 "input_pairs_path": input_pairs_path,
118 },
119 "out": {
120 "output_structure1_path": output_structure1_path,
121 "output_structure2_path": output_structure2_path,
122 "output_topology_path": output_topology_path,
123 "output_atomtypes_path": output_atomtypes_path,
124 "output_log_path": output_log_path,
125 },
126 }
128 # Properties specific for BB
129 # self.fit = properties.get('fit', False)
130 # self.split = properties.get('split', False)
131 # self.scDUMm = properties.get('scDUMm', 1.0)
132 # self.scDUMa = properties.get('scDUMa', 1.0)
133 # self.scDUMd = properties.get('scDUMd', 1.0)
134 # self.deAng = properties.get('deAng', False)
135 # self.distance = properties.get('distance', 0.05)
137 self.fit = properties.get("fit")
138 self.split = properties.get("split")
139 self.scDUMm = properties.get("scDUMm")
140 self.scDUMa = properties.get("scDUMa")
141 self.scDUMd = properties.get("scDUMd")
142 self.deAng = properties.get("deAng")
143 self.distance = properties.get("distance")
145 # Properties common in all PMX BB
146 self.gmx_lib = properties.get("gmx_lib", None)
147 if not self.gmx_lib and os.environ.get("CONDA_PREFIX", ""):
148 python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
149 self.gmx_lib = str(
150 Path(os.environ.get("CONDA_PREFIX", "")).joinpath(
151 f"lib/python{python_version}/site-packages/pmx/data/mutff/"
152 )
153 )
154 if properties.get("container_path"):
155 self.gmx_lib = str(
156 Path("/usr/local/").joinpath(
157 "lib/python3.7/site-packages/pmx/data/mutff/"
158 )
159 )
160 self.binary_path = properties.get("binary_path", "pmx")
162 # Check the properties
163 self.check_properties(properties)
164 self.check_arguments()
166 @launchlogger
167 def launch(self) -> int:
168 """Execute the :class:`Pmxmutate <pmx.pmxmutate.Pmxmutate>` pmx.pmxmutate.Pmxmutate object."""
170 # Setup Biobb
171 if self.check_restart():
172 return 0
173 self.stage_files()
175 if self.container_path:
176 working_dir = self.container_volume_path if self.container_volume_path else "/data"
177 else:
178 working_dir = self.stage_io_dict.get("unique_dir", "")
180 # Check if executable exists
181 if not self.container_path:
182 if not Path(self.binary_path).is_file():
183 if not shutil.which(self.binary_path):
184 raise FileNotFoundError(
185 "Executable %s not found. Check if it is installed in your system and correctly defined in the properties"
186 % self.binary_path
187 )
189 self.cmd = [
190 "cd",
191 working_dir,
192 ";",
193 self.binary_path,
194 "ligandHybrid",
195 "-i1",
196 PurePath(self.stage_io_dict["in"]["input_structure1_path"]).name,
197 "-i2",
198 PurePath(self.stage_io_dict["in"]["input_structure2_path"]).name,
199 "-itp1",
200 PurePath(self.stage_io_dict["in"]["input_topology1_path"]).name,
201 "-itp2",
202 PurePath(self.stage_io_dict["in"]["input_topology2_path"]).name,
203 "-oA",
204 PurePath(self.stage_io_dict["out"]["output_structure1_path"]).name,
205 "-oB",
206 PurePath(self.stage_io_dict["out"]["output_structure2_path"]).name,
207 "-oitp",
208 PurePath(self.stage_io_dict["out"]["output_topology_path"]).name,
209 "-offitp",
210 PurePath(self.stage_io_dict["out"]["output_atomtypes_path"]).name,
211 "-log",
212 PurePath(self.stage_io_dict["out"]["output_log_path"]).name,
213 ]
215 if self.stage_io_dict["in"].get("input_pairs_path"):
216 self.cmd.append("-pairs")
217 self.cmd.append(PurePath(self.stage_io_dict["in"]["input_pairs_path"]).name)
219 if self.stage_io_dict["in"].get("input_scaffold1_path"):
220 self.cmd.append("-n1")
221 self.cmd.append(PurePath(self.stage_io_dict["in"]["input_scaffold1_path"]).name)
223 if self.stage_io_dict["in"].get("input_scaffold2_path"):
224 self.cmd.append("-n2")
225 self.cmd.append(PurePath(self.stage_io_dict["in"]["input_scaffold2_path"]).name)
227 if self.fit:
228 self.cmd.append("--fit")
229 if self.split:
230 self.cmd.append("--split")
231 if self.deAng:
232 self.cmd.append("--deAng")
233 if self.distance:
234 self.cmd.append("--d")
235 self.cmd.append(str(self.distance))
236 if self.scDUMm:
237 self.cmd.append("--scDUMm")
238 self.cmd.append(str(self.scDUMm))
239 if self.scDUMa:
240 self.cmd.append("--scDUMa")
241 self.cmd.append(str(self.scDUMa))
242 if self.scDUMd:
243 self.cmd.append("--scDUMd")
244 self.cmd.append(str(self.scDUMd))
246 if self.gmx_lib:
247 self.env_vars_dict["GMXLIB"] = self.gmx_lib
249 # Run Biobb block
250 self.run_biobb()
252 # Copy files to host
253 self.copy_to_host()
255 self.remove_tmp_files()
257 self.check_arguments(output_files_created=True, raise_exception=False)
258 return self.return_code
261def pmxligand_hybrid(
262 input_structure1_path: str,
263 input_structure2_path: str,
264 input_topology1_path: str,
265 input_topology2_path: str,
266 output_log_path: str,
267 output_structure1_path: str,
268 output_structure2_path: str,
269 output_topology_path: str,
270 output_atomtypes_path: str,
271 input_scaffold1_path: Optional[str] = None,
272 input_scaffold2_path: Optional[str] = None,
273 input_pairs_path: Optional[str] = None,
274 properties: Optional[dict] = None,
275 **kwargs,
276) -> int:
277 """Create the :class:`Pmxligand_hybrid <pmx.pmxmutate.Pmxligand_hybrid>` class and
278 execute the :meth:`launch() <pmx.pmxligand_hybrid.Pmxligand_hybrid.launch> method."""
279 return Pmxligand_hybrid(**dict(locals())).launch()
282pmxligand_hybrid.__doc__ = Pmxligand_hybrid.__doc__
283main = Pmxligand_hybrid.get_main(pmxligand_hybrid, "Run PMX ligand hybrid module")
285if __name__ == "__main__":
286 main()