ligandparam 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ligandparam-0.3.0/PKG-INFO +38 -0
- ligandparam-0.3.0/README.md +10 -0
- ligandparam-0.3.0/pyproject.toml +56 -0
- ligandparam-0.3.0/setup.cfg +4 -0
- ligandparam-0.3.0/src/ligandparam/__init__.py +4 -0
- ligandparam-0.3.0/src/ligandparam/cli/__init__.py +0 -0
- ligandparam-0.3.0/src/ligandparam/cli/cli_lighfix.py +50 -0
- ligandparam-0.3.0/src/ligandparam/cli/ligandparam_getparam.py +237 -0
- ligandparam-0.3.0/src/ligandparam/cli/smiles_to_pdb.py +40 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stagegausrotation.py +52 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stagegaussian.py +43 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stagegaussiantomol2.py +63 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stageinitialize.py +41 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stagelazyresp.py +33 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stageleap.py +39 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stagenormalizecharges.py +48 -0
- ligandparam-0.3.0/src/ligandparam/deprecated/stageparmchk.py +27 -0
- ligandparam-0.3.0/src/ligandparam/driver.py +149 -0
- ligandparam-0.3.0/src/ligandparam/interfaces.py +194 -0
- ligandparam-0.3.0/src/ligandparam/io/__init__.py +0 -0
- ligandparam-0.3.0/src/ligandparam/io/coordinates.py +254 -0
- ligandparam-0.3.0/src/ligandparam/io/gaussianIO.py +312 -0
- ligandparam-0.3.0/src/ligandparam/io/leapIO.py +79 -0
- ligandparam-0.3.0/src/ligandparam/io/smiles.py +234 -0
- ligandparam-0.3.0/src/ligandparam/log.py +39 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/__init__.py +0 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/endstate.py +570 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/functions.py +481 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/intermolequiv.py +195 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/mdinutils.py +654 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/parmhelper.py +1443 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/residueresp.py +241 -0
- ligandparam-0.3.0/src/ligandparam/multiresp/respfunctions.py +366 -0
- ligandparam-0.3.0/src/ligandparam/parametrization.py +53 -0
- ligandparam-0.3.0/src/ligandparam/recipes/__init__.py +6 -0
- ligandparam-0.3.0/src/ligandparam/recipes/buildligand.py +39 -0
- ligandparam-0.3.0/src/ligandparam/recipes/dpfreeligand.py +363 -0
- ligandparam-0.3.0/src/ligandparam/recipes/dplazyligand.py +304 -0
- ligandparam-0.3.0/src/ligandparam/recipes/freeligand.py +398 -0
- ligandparam-0.3.0/src/ligandparam/recipes/lazierligand.py +162 -0
- ligandparam-0.3.0/src/ligandparam/recipes/lazyligand.py +320 -0
- ligandparam-0.3.0/src/ligandparam/recipes/optligand.py +293 -0
- ligandparam-0.3.0/src/ligandparam/recipes/rnaligand.py +31 -0
- ligandparam-0.3.0/src/ligandparam/stages/__init__.py +15 -0
- ligandparam-0.3.0/src/ligandparam/stages/abstractstage.py +133 -0
- ligandparam-0.3.0/src/ligandparam/stages/build_system.py +248 -0
- ligandparam-0.3.0/src/ligandparam/stages/charge.py +189 -0
- ligandparam-0.3.0/src/ligandparam/stages/deepmd.py +383 -0
- ligandparam-0.3.0/src/ligandparam/stages/displacemol.py +76 -0
- ligandparam-0.3.0/src/ligandparam/stages/gaussian.py +607 -0
- ligandparam-0.3.0/src/ligandparam/stages/initialize.py +110 -0
- ligandparam-0.3.0/src/ligandparam/stages/leap.py +66 -0
- ligandparam-0.3.0/src/ligandparam/stages/lighfix.py +209 -0
- ligandparam-0.3.0/src/ligandparam/stages/parmchk.py +48 -0
- ligandparam-0.3.0/src/ligandparam/stages/pdb_names.py +165 -0
- ligandparam-0.3.0/src/ligandparam/stages/resp.py +123 -0
- ligandparam-0.3.0/src/ligandparam/stages/sdfconverters.py +278 -0
- ligandparam-0.3.0/src/ligandparam/stages/smilestopdb.py +152 -0
- ligandparam-0.3.0/src/ligandparam/stages/teststage.py +22 -0
- ligandparam-0.3.0/src/ligandparam/stages/typematching.py +159 -0
- ligandparam-0.3.0/src/ligandparam/stages/utilsstages.py +13 -0
- ligandparam-0.3.0/src/ligandparam/utils.py +113 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/PKG-INFO +38 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/SOURCES.txt +67 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/dependency_links.txt +1 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/entry_points.txt +4 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/requires.txt +17 -0
- ligandparam-0.3.0/src/ligandparam.egg-info/top_level.txt +1 -0
- ligandparam-0.3.0/tests/test_module.py +13 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ligandparam
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A ligand parameterization package for Amber
|
|
5
|
+
Author-email: Zeke Piskulich <piskulichz@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/piskulichz/ligandparam
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: numpy<2
|
|
14
|
+
Requires-Dist: pandas
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: MDAnalysis>2.7.0
|
|
17
|
+
Requires-Dist: pathlib
|
|
18
|
+
Requires-Dist: parmed
|
|
19
|
+
Requires-Dist: ase
|
|
20
|
+
Requires-Dist: deepmd-kit
|
|
21
|
+
Requires-Dist: tensorflow
|
|
22
|
+
Requires-Dist: tblite
|
|
23
|
+
Provides-Extra: other
|
|
24
|
+
Requires-Dist: openbabel; extra == "other"
|
|
25
|
+
Provides-Extra: docs
|
|
26
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
27
|
+
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
28
|
+
|
|
29
|
+
# Ligand Parametrization
|
|
30
|
+
|
|
31
|
+
## Overview
|
|
32
|
+
This is a Python package designed to provide a simple workflow for ligand parameterization. It automates many of the
|
|
33
|
+
key features encountered by users, including...
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
The online documentation is located here: https://ligandparam.readthedocs.io/en/latest/
|
|
38
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Ligand Parametrization
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This is a Python package designed to provide a simple workflow for ligand parameterization. It automates many of the
|
|
5
|
+
key features encountered by users, including...
|
|
6
|
+
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
The online documentation is located here: https://ligandparam.readthedocs.io/en/latest/
|
|
10
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ligandparam"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "A ligand parameterization package for Amber"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.6"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Zeke Piskulich", email = "piskulichz@gmail.com"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent"
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"numpy<2",
|
|
22
|
+
"pandas",
|
|
23
|
+
"requests",
|
|
24
|
+
"MDAnalysis>2.7.0",
|
|
25
|
+
"pathlib",
|
|
26
|
+
"parmed",
|
|
27
|
+
"ase",
|
|
28
|
+
"deepmd-kit",
|
|
29
|
+
"tensorflow",
|
|
30
|
+
"tblite"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
urls = { "Homepage" = "https://github.com/piskulichz/ligandparam" }
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
lighfix = "ligandparam.cli.cli_lighfix:lighfix"
|
|
38
|
+
lig-getparam = "ligandparam.cli.ligandparam_getparam:main"
|
|
39
|
+
smiles-to-pdb = "ligandparam.cli.smiles_to_pdb:main"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
other = [
|
|
43
|
+
"openbabel",
|
|
44
|
+
]
|
|
45
|
+
docs = [
|
|
46
|
+
"sphinx",
|
|
47
|
+
"sphinx_rtd_theme",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 120
|
|
52
|
+
indent-width = 4
|
|
53
|
+
|
|
54
|
+
[tool.ruff.format]
|
|
55
|
+
skip-magic-trailing-comma = true
|
|
56
|
+
|
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from ligandparam.stages import LigHFix
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_opts() -> dict:
|
|
10
|
+
parser = argparse.ArgumentParser(description="Fix hydrogenation and bonding in ligands that come from PDB")
|
|
11
|
+
parser.add_argument("-i", "--ligand_id", help="Path to the input PDB file.", required=True)
|
|
12
|
+
parser.add_argument("-p", "--in_pdb", help="Path to the input PDB file.", required=True)
|
|
13
|
+
parser.add_argument("-o", "--out_pdb", help="Path to the output file.", default="output.txt", required=True)
|
|
14
|
+
|
|
15
|
+
args = parser.parse_args()
|
|
16
|
+
opts = {}
|
|
17
|
+
opts["ligand_id"] = args.ligand_id
|
|
18
|
+
opts["in_pdb"] = Path(args.in_pdb)
|
|
19
|
+
opts["out_pdb"] = Path(args.out_pdb)
|
|
20
|
+
|
|
21
|
+
if not opts["in_pdb"].is_file() or opts["in_pdb"].suffix != ".pdb":
|
|
22
|
+
raise ValueError(f"Bad input PDB: {opts['in_pdb']}")
|
|
23
|
+
|
|
24
|
+
if not opts["out_pdb"].parent.is_dir():
|
|
25
|
+
raise ValueError(f"Bad dir for out_pdb: {opts['out_pdb'].parent}")
|
|
26
|
+
|
|
27
|
+
if opts["out_pdb"].suffix != ".pdb":
|
|
28
|
+
raise ValueError(f"Bad output PDB: {opts['out_pdb']}")
|
|
29
|
+
|
|
30
|
+
return opts
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def ligfix():
|
|
34
|
+
opts = get_opts()
|
|
35
|
+
|
|
36
|
+
# Send output to stdout, though it probably won't print anything unless there's an error
|
|
37
|
+
logger = logging.getLogger("mylog")
|
|
38
|
+
logger.setLevel(logging.INFO)
|
|
39
|
+
stream_handler = logging.StreamHandler(sys.stdout)
|
|
40
|
+
stream_handler.setLevel(logging.INFO)
|
|
41
|
+
logger.addHandler(stream_handler)
|
|
42
|
+
|
|
43
|
+
s = LigHFix("liga", main_input=opts["ligand_id"], cwd=opts["out_pdb"].parent, in_pdb=opts["in_pdb"],
|
|
44
|
+
out_pdb=opts["out_pdb"], logger=logger)
|
|
45
|
+
|
|
46
|
+
s.execute()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
ligfix()
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys, shutil
|
|
3
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ligandparam import __version__
|
|
7
|
+
from ligandparam.stages import PDB_Name_Fixer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def set_file_logger(
|
|
12
|
+
logfilename: Path, logname: str = None, filemode: str = "a"
|
|
13
|
+
) -> logging.Logger:
|
|
14
|
+
""" Set up a file logger for the ligand parameterization process.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
logfilename : Path
|
|
19
|
+
The path to the log file where the logs will be written.
|
|
20
|
+
logname : str, optional
|
|
21
|
+
The name of the logger. If None, it will be derived from the log filename.
|
|
22
|
+
filemode : str, optional
|
|
23
|
+
The mode in which the log file will be opened. Default is 'a' (append
|
|
24
|
+
mode). Use 'w' for write mode to overwrite the log file.
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
logger : logging.Logger
|
|
28
|
+
A configured logger instance that writes logs to the specified file.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
if logname is None:
|
|
32
|
+
logname = Path(logfilename).stem
|
|
33
|
+
logger = logging.getLogger(logname)
|
|
34
|
+
logger.setLevel(logging.INFO)
|
|
35
|
+
formatter = logging.Formatter(
|
|
36
|
+
"{asctime} - {levelname} - {version} {message}",
|
|
37
|
+
style="{",
|
|
38
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
39
|
+
defaults={"version": __version__},
|
|
40
|
+
)
|
|
41
|
+
file_handler = logging.FileHandler(filename=logfilename, mode=filemode)
|
|
42
|
+
file_handler.setLevel(logging.INFO)
|
|
43
|
+
file_handler.setFormatter(formatter)
|
|
44
|
+
logger.addHandler(file_handler)
|
|
45
|
+
|
|
46
|
+
return logger
|
|
47
|
+
|
|
48
|
+
def worker(recipe_name: str, mol: str, resname: str, cwd: Path, net_charge: float, atom_type: str = "gaff2", charge_model: str = "bcc", model: str = None, sqm: str = True, data_cwd: str = "param", nprocs: int = 1, mem: int = 1, reference_pdb: str = None) -> Path:
|
|
49
|
+
binder_dir = cwd / data_cwd / resname
|
|
50
|
+
binder_dir.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
binder_pdb = cwd / mol
|
|
52
|
+
logger = set_file_logger(
|
|
53
|
+
binder_dir / f"{resname}.log", filemode="w"
|
|
54
|
+
)
|
|
55
|
+
""" Worker function to execute the ligand parameterization recipe.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
recipe_name : str
|
|
60
|
+
The name of the recipe to be used for ligand parameterization.
|
|
61
|
+
mol : str
|
|
62
|
+
The input PDB file containing the ligand.
|
|
63
|
+
resname : str
|
|
64
|
+
The residue name for the ligand.
|
|
65
|
+
cwd : Path
|
|
66
|
+
The current working directory where the output files will be stored.
|
|
67
|
+
net_charge : float
|
|
68
|
+
The net charge of the ligand.
|
|
69
|
+
atom_type : str, optional
|
|
70
|
+
The atom type for the ligand (default is "gaff2").
|
|
71
|
+
charge_model : str, optional
|
|
72
|
+
The charge model for the ligand (default is "bcc"). Options are "bcc" or
|
|
73
|
+
"abcg2".
|
|
74
|
+
model : str, optional
|
|
75
|
+
The path to the DeepMD model file (optional).
|
|
76
|
+
sqm : bool, optional
|
|
77
|
+
Whether to use SQM calculations for geometry optimization (default is True).
|
|
78
|
+
data_cwd : str, optional
|
|
79
|
+
The directory to store output files (default is "param").
|
|
80
|
+
nprocs : int, optional
|
|
81
|
+
The number of processes to use for parallel execution (default is 1).
|
|
82
|
+
mem : int, optional
|
|
83
|
+
The amount of memory in GB to allocate for the process (default is 1GB).
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
None
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
print("Working on ligand:", resname)
|
|
91
|
+
if not binder_pdb.is_file():
|
|
92
|
+
raise FileNotFoundError(f"Input file {binder_pdb} does not exist. Please provide a valid PDB file.")
|
|
93
|
+
if not binder_dir.is_dir():
|
|
94
|
+
raise NotADirectoryError(f"Output directory {binder_dir} does not exist. Please provide a valid directory.")
|
|
95
|
+
|
|
96
|
+
logger.info(f"Starting ligand parameterization for {resname} using recipe '{recipe_name}'")
|
|
97
|
+
logger.info(f"Input file: {binder_pdb}")
|
|
98
|
+
logger.info(f"Output directory: {binder_dir}")
|
|
99
|
+
logger.info(f"Net charge: {net_charge}")
|
|
100
|
+
logger.info(f"Atom type: {atom_type}")
|
|
101
|
+
logger.info(f"Charge model: {charge_model}")
|
|
102
|
+
if model is not None:
|
|
103
|
+
logger.info(f"Using DeepMD model: {model}")
|
|
104
|
+
if sqm:
|
|
105
|
+
logger.info("Using SQM calculations for geometry optimization.")
|
|
106
|
+
else:
|
|
107
|
+
logger.info("Not using SQM calculations for geometry optimization.")
|
|
108
|
+
logger.info("Starting recipe execution...")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if reference_pdb is not None:
|
|
113
|
+
logger.info(f"Reference PDB file: {reference_pdb}")
|
|
114
|
+
fix_pdb_stage = PDB_Name_Fixer(f"build_{resname}", binder_pdb, binder_dir, out_pdb=f"{binder_pdb.parent}/fix_{binder_pdb.name}", reference_pdb=reference_pdb, align=True, logger=logger)
|
|
115
|
+
fix_pdb_stage.execute(dry_run=False)
|
|
116
|
+
logger.info("PDB name fixing complete.")
|
|
117
|
+
out_pdb = f"{binder_pdb.parent}/fix_{binder_pdb.name}"
|
|
118
|
+
else:
|
|
119
|
+
out_pdb = binder_pdb
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
recipe = recipe_selector(
|
|
123
|
+
recipe_name,
|
|
124
|
+
in_filename = f"{out_pdb}",
|
|
125
|
+
cwd = binder_dir,
|
|
126
|
+
atom_type = atom_type,
|
|
127
|
+
charge_model = charge_model,
|
|
128
|
+
net_charge = net_charge,
|
|
129
|
+
logger = logger,
|
|
130
|
+
molname = resname,
|
|
131
|
+
model = model,
|
|
132
|
+
sqm = sqm,
|
|
133
|
+
nproc = nprocs,
|
|
134
|
+
mem = mem
|
|
135
|
+
)
|
|
136
|
+
logger.info(f"Recipe selected: {recipe_name}")
|
|
137
|
+
recipe.setup()
|
|
138
|
+
recipe.execute()
|
|
139
|
+
logger.info("Recipe execution complete.")
|
|
140
|
+
|
|
141
|
+
def recipe_selector(recipe_name: str, **kwargs):
|
|
142
|
+
""" Selects and returns the appropriate recipe class based on the recipe name.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
recipe_name : str
|
|
147
|
+
The name of the recipe to be used for ligand parameterization.
|
|
148
|
+
**kwargs : dict
|
|
149
|
+
Additional keyword arguments to be passed to the recipe class constructor.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
AbstractStage
|
|
154
|
+
An instance of the selected recipe class.
|
|
155
|
+
|
|
156
|
+
Raises
|
|
157
|
+
------
|
|
158
|
+
ValueError
|
|
159
|
+
If the recipe name is not recognized, a ValueError is raised with a message
|
|
160
|
+
listing the available recipes.
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
if recipe_name == "lazyligand":
|
|
164
|
+
from ligandparam.recipes.lazyligand import LazyLigand
|
|
165
|
+
return LazyLigand(**kwargs)
|
|
166
|
+
elif recipe_name == "lazierligand":
|
|
167
|
+
from ligandparam.recipes.lazierligand import LazierLigand
|
|
168
|
+
return LazierLigand(**kwargs)
|
|
169
|
+
elif recipe_name == "freeligand":
|
|
170
|
+
from ligandparam.recipes.freeligand import FreeLigand
|
|
171
|
+
return FreeLigand(**kwargs)
|
|
172
|
+
elif recipe_name == "dplazyligand":
|
|
173
|
+
from ligandparam.recipes.dplazyligand import DPLigand
|
|
174
|
+
return DPLigand(**kwargs)
|
|
175
|
+
elif recipe_name == "dpfreeligand":
|
|
176
|
+
from ligandparam.recipes.dpfreeligand import DPFreeLigand
|
|
177
|
+
return DPFreeLigand(**kwargs)
|
|
178
|
+
elif recipe_name == "sqmligand":
|
|
179
|
+
from ligandparam.recipes import SQMLigand
|
|
180
|
+
return SQMLigand(**kwargs)
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError(f"Unknown recipe name: {recipe_name}. Available recipes: lazyligand, lazierligand, freeligand, dplazyligand, dpfreeligand.")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main():
|
|
186
|
+
""" Main function to parse command line arguments and execute the ligand parameterization worker.
|
|
187
|
+
|
|
188
|
+
This function uses argparse to handle command line arguments and calls the worker
|
|
189
|
+
function with the parsed arguments. It sets up the current working directory and
|
|
190
|
+
initializes the logger for the ligand parameterization process.
|
|
191
|
+
|
|
192
|
+
Raises
|
|
193
|
+
------
|
|
194
|
+
SystemExit
|
|
195
|
+
If the command line arguments are not provided correctly, argparse will raise
|
|
196
|
+
a SystemExit exception, which will terminate the program with an error message.
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
import argparse
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
parser = argparse.ArgumentParser(description="Ligand parameterization CLI")
|
|
203
|
+
parser.add_argument("-i", "--input", type=str, required=True, help="Input PDB file with ligand")
|
|
204
|
+
parser.add_argument("-r", "--resname", type=str, required=True, help="Residue name for the ligand")
|
|
205
|
+
parser.add_argument("-d", "--data_cwd", type=Path, required=True, help="Directory to store output files")
|
|
206
|
+
parser.add_argument("-a", "--atom_type", type=str, default="gaff2", help="Atom type for the ligand (default: gaff2)")
|
|
207
|
+
parser.add_argument("-cm", "--charge_model", type=str, default="bcc", choices=["bcc", "abcg2"], help="Charge model for the ligand (default: bcc, options: bcc, abcg2)")
|
|
208
|
+
parser.add_argument("-c", "--net_charge", type=float, default=0.0, help="Net charge of the ligand")
|
|
209
|
+
parser.add_argument("-m", "--model", type=str, default=None, help="DeepMD model file path (optional)")
|
|
210
|
+
parser.add_argument("--sqm", action='store_true', help="Use SQM calculations")
|
|
211
|
+
parser.add_argument("-rn", "--recipe_name", type=str, required=True, help="Recipe name for the ligand processing")
|
|
212
|
+
parser.add_argument("-n", "--nproc", type=int, default=1, help="Number of processes to use (default: 1)")
|
|
213
|
+
parser.add_argument("-mem", "--mem", type=int, default=1, help="Memory in GB to allocate for the process (default: 1GB)")
|
|
214
|
+
parser.add_argument("-ref", "--reference_pdb", type=str, default=None, help="Reference PDB file for name fixing (optional)")
|
|
215
|
+
|
|
216
|
+
args = parser.parse_args()
|
|
217
|
+
|
|
218
|
+
cwd = Path.cwd()
|
|
219
|
+
|
|
220
|
+
worker(
|
|
221
|
+
recipe_name=args.recipe_name,
|
|
222
|
+
mol=args.input,
|
|
223
|
+
cwd=cwd,
|
|
224
|
+
resname=args.resname,
|
|
225
|
+
data_cwd=args.data_cwd,
|
|
226
|
+
net_charge=args.net_charge,
|
|
227
|
+
atom_type=args.atom_type,
|
|
228
|
+
charge_model=args.charge_model,
|
|
229
|
+
model=args.model,
|
|
230
|
+
sqm=args.sqm,
|
|
231
|
+
nprocs=args.nproc,
|
|
232
|
+
mem=args.mem,
|
|
233
|
+
reference_pdb=args.reference_pdb
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if __name__ == "__main__":
|
|
237
|
+
main()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from rdkit import Chem
|
|
3
|
+
from rdkit.Chem import AllChem
|
|
4
|
+
|
|
5
|
+
def smiles_to_pdb(smiles, pdb_filename, resname="LIG"):
|
|
6
|
+
# Generate molecule from SMILES
|
|
7
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
8
|
+
if mol is None:
|
|
9
|
+
raise ValueError("Invalid SMILES string.")
|
|
10
|
+
|
|
11
|
+
# Add hydrogens
|
|
12
|
+
mol = Chem.AddHs(mol)
|
|
13
|
+
|
|
14
|
+
# Generate 3D coordinates
|
|
15
|
+
if AllChem.EmbedMolecule(mol, AllChem.ETKDG()) != 0:
|
|
16
|
+
raise RuntimeError("3D coordinate generation failed.")
|
|
17
|
+
|
|
18
|
+
# Minimize with MMFF or UFF
|
|
19
|
+
if AllChem.MMFFHasAllMoleculeParams(mol):
|
|
20
|
+
AllChem.MMFFOptimizeMolecule(mol)
|
|
21
|
+
else:
|
|
22
|
+
AllChem.UFFOptimizeMolecule(mol)
|
|
23
|
+
# Set residue name for all atoms
|
|
24
|
+
for atom in mol.GetAtoms():
|
|
25
|
+
atom.SetProp("resName", resname)
|
|
26
|
+
# Write to PDB
|
|
27
|
+
with open(pdb_filename, 'w') as f:
|
|
28
|
+
f.write(Chem.MolToPDBBlock(mol))
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
parser = argparse.ArgumentParser(description="Convert SMILES to PDB with 3D coordinates and minimization.")
|
|
32
|
+
parser.add_argument("-s", "--smiles", required=True, help="Input SMILES string")
|
|
33
|
+
parser.add_argument("-o", "--output", required=True, help="Output PDB filename")
|
|
34
|
+
parser.add_argument("-rn", "--resname", default="LIG", help="Residue name for the ligand (default: LIG)")
|
|
35
|
+
args = parser.parse_args()
|
|
36
|
+
|
|
37
|
+
smiles_to_pdb(args.smiles, args.output, args.resname)
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import MDAnalysis as mda
|
|
2
|
+
|
|
3
|
+
from ligandparam.abstractstage import AbstractStage
|
|
4
|
+
from ligandparam.coordinates import Coordinates
|
|
5
|
+
from ligandparam.gaussianIO import GaussianWriter, GaussianInput
|
|
6
|
+
|
|
7
|
+
from ligandparam.log import get_logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StageGaussianRotation(AbstractStage):
|
|
12
|
+
def __init__(self, name, alpha = [0.0], beta = [0.0], gamma = [0.0], base_cls=None) -> None:
|
|
13
|
+
self.name = name
|
|
14
|
+
self.alpha = alpha
|
|
15
|
+
self.beta = beta
|
|
16
|
+
self.gamma = gamma
|
|
17
|
+
|
|
18
|
+
if base_cls.coord_object is None:
|
|
19
|
+
raise ValueError(f"Error (Stage {self.name}): Coordinate object not set")
|
|
20
|
+
|
|
21
|
+
if base_cls.name is None:
|
|
22
|
+
raise ValueError(f"Error (Stage {self.name}): Base name not set")
|
|
23
|
+
|
|
24
|
+
if base_cls.header is None:
|
|
25
|
+
raise ValueError(f"Error (Stage {self.name}): Header not set")
|
|
26
|
+
|
|
27
|
+
self.base_cls = base_cls
|
|
28
|
+
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
def _append_stage(self, stage: "AbstractStage") -> "AbstractStage":
|
|
32
|
+
return stage
|
|
33
|
+
|
|
34
|
+
def execute(self, dry_run=False, nproc=1, mem=1) -> Any:
|
|
35
|
+
self.logger.info(f"Executing {self.stage_name} with alpha={self.alpha}, beta={self.beta}, and gamma={self.gamma}")
|
|
36
|
+
|
|
37
|
+
run_apply = print
|
|
38
|
+
|
|
39
|
+
for a in self.alpha:
|
|
40
|
+
for b in self.beta:
|
|
41
|
+
for g in self.gamma:
|
|
42
|
+
#TODO: add elements and header, and make sure they are consistent between steps. Probably initialized with class
|
|
43
|
+
newgau = GaussianWriter('gaussianCalcs/'+self.base_cls.name+f'_rot_{a}_{b}.com')
|
|
44
|
+
|
|
45
|
+
newgau.add_block(GaussianInput(command=f"#P {self.base_cls.theory['low']} OPT(CalcFC)",
|
|
46
|
+
initial_coordinates = self.base_cls.coord_object.rotate(alpha=a, beta=b),
|
|
47
|
+
elements = self.base_cls.coord_object.get_elements(),
|
|
48
|
+
header=self.base_cls.header))
|
|
49
|
+
newgau.write(dry_run=dry_run)
|
|
50
|
+
run_apply(newgau.get_run_command())
|
|
51
|
+
|
|
52
|
+
return
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import MDAnalysis as mda
|
|
4
|
+
|
|
5
|
+
from ligandparam.abstractstage import AbstractStage
|
|
6
|
+
from ligandparam.coordinates import Coordinates
|
|
7
|
+
from ligandparam.gaussianIO import GaussianWriter, GaussianInput
|
|
8
|
+
from ligandparam.interfaces import Gaussian
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StageGaussian(AbstractStage):
|
|
12
|
+
def __init__(self, name, base_cls=None) -> None:
|
|
13
|
+
self.name = name
|
|
14
|
+
self.base_cls = base_cls
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
def _append_stage(self, stage: "AbstractStage") -> "AbstractStage":
|
|
18
|
+
return stage
|
|
19
|
+
|
|
20
|
+
def execute(self, dry_run=False, nproc=1, mem=1) -> Any:
|
|
21
|
+
stageheader = self.base_cls.header
|
|
22
|
+
stageheader.append(f"%chk={self.base_cls.name}.antechamber.chk")
|
|
23
|
+
gau = GaussianWriter(f'gaussianCalcs/{self.base_cls.name}.com')
|
|
24
|
+
gau.add_block(GaussianInput(command=f"#P {self.base_cls.theory['low']} OPT(CalcFC)",
|
|
25
|
+
initial_coordinates=self.base_cls.coord_object.get_coordinates(),
|
|
26
|
+
elements=self.base_cls.coord_object.get_elements(),
|
|
27
|
+
header=stageheader))
|
|
28
|
+
gau.add_block(GaussianInput(command=f"#P {self.base_cls.theory['high']} OPT(CalcFC) GEOM(ALLCheck) Guess(Read)",
|
|
29
|
+
header=stageheader))
|
|
30
|
+
gau.add_block(GaussianInput(
|
|
31
|
+
command=f"#P {self.base_cls.theory['low']} GEOM(AllCheck) Guess(Read) NoSymm Pop=mk IOp(6/33=2) GFInput GFPrint",
|
|
32
|
+
header=stageheader))
|
|
33
|
+
|
|
34
|
+
has_run = gau.write(dry_run=dry_run)
|
|
35
|
+
|
|
36
|
+
if not has_run:
|
|
37
|
+
gau_run = Gaussian()
|
|
38
|
+
gau_run.call(inp_pipe=self.base_cls.name + '.com',
|
|
39
|
+
out_pipe=self.base_cls.name + '.log',
|
|
40
|
+
dry_run=dry_run)
|
|
41
|
+
os.chdir('..')
|
|
42
|
+
|
|
43
|
+
return
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import MDAnalysis as mda
|
|
4
|
+
|
|
5
|
+
from ligandparam.abstractstage import AbstractStage
|
|
6
|
+
from ligandparam.interfaces import Antechamber
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StageGaussiantoMol2(AbstractStage):
|
|
10
|
+
def __init__(self, name, base_cls=None, dry_run=None) -> None:
|
|
11
|
+
self.name = name
|
|
12
|
+
self.base_cls = base_cls
|
|
13
|
+
self.dry_run = dry_run
|
|
14
|
+
|
|
15
|
+
def _append_stage(self, stage: "AbstractStage") -> "AbstractStage":
|
|
16
|
+
return stage
|
|
17
|
+
|
|
18
|
+
def execute(self, dry_run=False, nproc=1, mem=1) -> Any:
|
|
19
|
+
if self.dry_run is not None:
|
|
20
|
+
dry_run = self
|
|
21
|
+
|
|
22
|
+
# Convert from gaussian to mol2
|
|
23
|
+
ante = Antechamber()
|
|
24
|
+
ante.call(
|
|
25
|
+
i=self.base_cls.name + ".log",
|
|
26
|
+
fi="gout",
|
|
27
|
+
o=self.base_cls.name + ".tmp1.mol2",
|
|
28
|
+
fo="mol2",
|
|
29
|
+
pf="y",
|
|
30
|
+
at=self.base_cls.atom_type,
|
|
31
|
+
an="no",
|
|
32
|
+
nc=self.net_charge,
|
|
33
|
+
run=(not dry_run),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Assign the charges
|
|
37
|
+
if not dry_run:
|
|
38
|
+
with warnings.catch_warnings():
|
|
39
|
+
warnings.simplefilter("ignore")
|
|
40
|
+
u1 = mda.Universe(self.base_cls.name + ".antechamber.mol2")
|
|
41
|
+
u2 = mda.Universe(self.base_cls.name + ".tmp1.mol2")
|
|
42
|
+
assert len(u1.atoms) == len(u2.atoms), "Number of atoms in the two files do not match"
|
|
43
|
+
|
|
44
|
+
u2.atoms.charges = u1.atoms.charges
|
|
45
|
+
|
|
46
|
+
ag = u2.select_atoms("all")
|
|
47
|
+
ag.write(self.base_cls.name + ".tmp2.mol2")
|
|
48
|
+
|
|
49
|
+
# Use antechamber to clean up the mol2 format
|
|
50
|
+
ante = Antechamber(self.cwd)
|
|
51
|
+
ante.call(
|
|
52
|
+
i=self.base_cls.name + ".tmp2.mol2",
|
|
53
|
+
fi="mol2",
|
|
54
|
+
o=self.base_cls.name + ".log.mol2",
|
|
55
|
+
fo="mol2",
|
|
56
|
+
pf="y",
|
|
57
|
+
at=self.base_cls.atom_type,
|
|
58
|
+
an="no",
|
|
59
|
+
nc=self.net_charge,
|
|
60
|
+
run=(not dry_run),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Optional, Any
|
|
2
|
+
from ligandparam.abstractstage import AbstractStage
|
|
3
|
+
from ligandparam.interfaces import Antechamber
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StageInitialize(AbstractStage):
|
|
7
|
+
"""This class is used to initialize from pdb to mol2 file using Antechamber.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
name : str
|
|
12
|
+
Name of the stage.
|
|
13
|
+
base_cls : object
|
|
14
|
+
Object of the base class.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, name, base_cls=None) -> None:
|
|
19
|
+
self.name = name
|
|
20
|
+
self.base_cls = base_cls
|
|
21
|
+
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
def _append_stage(self, stage: "AbstractStage") -> "AbstractStage":
|
|
25
|
+
return stage
|
|
26
|
+
|
|
27
|
+
def execute(self, dry_run=False, nproc=1, mem=1) -> Any:
|
|
28
|
+
ante = Antechamber()
|
|
29
|
+
ante.call(
|
|
30
|
+
i=self.base_cls.name + ".pdb",
|
|
31
|
+
fi="pdb",
|
|
32
|
+
o=self.base_cls.name + ".antechamber.mol2",
|
|
33
|
+
fo="mol2",
|
|
34
|
+
c="bcc",
|
|
35
|
+
nc=self.base_cls.net_charge,
|
|
36
|
+
pf="y",
|
|
37
|
+
at=self.base_cls.atom_type,
|
|
38
|
+
an="no",
|
|
39
|
+
dry_run=dry_run,
|
|
40
|
+
)
|
|
41
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import MDAnalysis as mda
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from ligandparam.abstractstage import AbstractStage
|
|
5
|
+
from ligandparam.interfaces import Antechamber
|
|
6
|
+
from ligandparam.log import get_logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StageLazyResp(AbstractStage):
|
|
10
|
+
def __init__(self, name, base_cls=None) -> None:
|
|
11
|
+
self.name = name
|
|
12
|
+
self.base_cls = base_cls
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
def _append_stage(self, stage: "AbstractStage") -> "AbstractStage":
|
|
16
|
+
return stage
|
|
17
|
+
|
|
18
|
+
def execute(self, dry_run=False, nproc=1, mem=1) -> Any:
|
|
19
|
+
self.logger.info(f"Executing {self.name} with netcharge={self.base_cls.net_charge}")
|
|
20
|
+
ante = Antechamber()
|
|
21
|
+
ante.call(
|
|
22
|
+
i=f"gaussianCalcs/{self.base_cls.name}.log",
|
|
23
|
+
fi="gout",
|
|
24
|
+
o=self.base_cls.name + ".resp.mol2",
|
|
25
|
+
fo="mol2",
|
|
26
|
+
gv=0,
|
|
27
|
+
c="resp",
|
|
28
|
+
nc=self.base_cls.net_charge,
|
|
29
|
+
at="gaff2",
|
|
30
|
+
an="no",
|
|
31
|
+
dry_run=dry_run,
|
|
32
|
+
)
|
|
33
|
+
return
|