ligandparam 0.3.5__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.
Files changed (71) hide show
  1. ligandparam-0.3.5/PKG-INFO +55 -0
  2. ligandparam-0.3.5/README.md +21 -0
  3. ligandparam-0.3.5/pyproject.toml +69 -0
  4. ligandparam-0.3.5/setup.cfg +4 -0
  5. ligandparam-0.3.5/src/ligandparam/__init__.py +4 -0
  6. ligandparam-0.3.5/src/ligandparam/cli/__init__.py +0 -0
  7. ligandparam-0.3.5/src/ligandparam/cli/cli_lighfix.py +50 -0
  8. ligandparam-0.3.5/src/ligandparam/cli/ligandparam_getparam.py +237 -0
  9. ligandparam-0.3.5/src/ligandparam/cli/mol2_to_sage.py +20 -0
  10. ligandparam-0.3.5/src/ligandparam/cli/smiles_to_pdb.py +40 -0
  11. ligandparam-0.3.5/src/ligandparam/deprecated/stagegausrotation.py +52 -0
  12. ligandparam-0.3.5/src/ligandparam/deprecated/stagegaussian.py +43 -0
  13. ligandparam-0.3.5/src/ligandparam/deprecated/stagegaussiantomol2.py +63 -0
  14. ligandparam-0.3.5/src/ligandparam/deprecated/stageinitialize.py +41 -0
  15. ligandparam-0.3.5/src/ligandparam/deprecated/stagelazyresp.py +33 -0
  16. ligandparam-0.3.5/src/ligandparam/deprecated/stageleap.py +39 -0
  17. ligandparam-0.3.5/src/ligandparam/deprecated/stagenormalizecharges.py +48 -0
  18. ligandparam-0.3.5/src/ligandparam/deprecated/stageparmchk.py +27 -0
  19. ligandparam-0.3.5/src/ligandparam/driver.py +209 -0
  20. ligandparam-0.3.5/src/ligandparam/interfaces.py +278 -0
  21. ligandparam-0.3.5/src/ligandparam/io/__init__.py +0 -0
  22. ligandparam-0.3.5/src/ligandparam/io/coordinates.py +254 -0
  23. ligandparam-0.3.5/src/ligandparam/io/gaussianIO.py +313 -0
  24. ligandparam-0.3.5/src/ligandparam/io/leapIO.py +79 -0
  25. ligandparam-0.3.5/src/ligandparam/io/smiles.py +234 -0
  26. ligandparam-0.3.5/src/ligandparam/log.py +91 -0
  27. ligandparam-0.3.5/src/ligandparam/multiresp/__init__.py +0 -0
  28. ligandparam-0.3.5/src/ligandparam/multiresp/endstate.py +570 -0
  29. ligandparam-0.3.5/src/ligandparam/multiresp/functions.py +481 -0
  30. ligandparam-0.3.5/src/ligandparam/multiresp/intermolequiv.py +195 -0
  31. ligandparam-0.3.5/src/ligandparam/multiresp/mdinutils.py +654 -0
  32. ligandparam-0.3.5/src/ligandparam/multiresp/parmhelper.py +1443 -0
  33. ligandparam-0.3.5/src/ligandparam/multiresp/residueresp.py +241 -0
  34. ligandparam-0.3.5/src/ligandparam/multiresp/respfunctions.py +366 -0
  35. ligandparam-0.3.5/src/ligandparam/parametrization.py +135 -0
  36. ligandparam-0.3.5/src/ligandparam/recipes/__init__.py +6 -0
  37. ligandparam-0.3.5/src/ligandparam/recipes/buildligand.py +39 -0
  38. ligandparam-0.3.5/src/ligandparam/recipes/dpfreeligand.py +363 -0
  39. ligandparam-0.3.5/src/ligandparam/recipes/dplazyligand.py +304 -0
  40. ligandparam-0.3.5/src/ligandparam/recipes/freeligand.py +398 -0
  41. ligandparam-0.3.5/src/ligandparam/recipes/lazierligand.py +162 -0
  42. ligandparam-0.3.5/src/ligandparam/recipes/lazyligand.py +320 -0
  43. ligandparam-0.3.5/src/ligandparam/recipes/optligand.py +293 -0
  44. ligandparam-0.3.5/src/ligandparam/recipes/rnaligand.py +31 -0
  45. ligandparam-0.3.5/src/ligandparam/stages/__init__.py +16 -0
  46. ligandparam-0.3.5/src/ligandparam/stages/abstractstage.py +258 -0
  47. ligandparam-0.3.5/src/ligandparam/stages/build_system.py +352 -0
  48. ligandparam-0.3.5/src/ligandparam/stages/charge.py +279 -0
  49. ligandparam-0.3.5/src/ligandparam/stages/deepmd.py +445 -0
  50. ligandparam-0.3.5/src/ligandparam/stages/displacemol.py +129 -0
  51. ligandparam-0.3.5/src/ligandparam/stages/gaussian.py +852 -0
  52. ligandparam-0.3.5/src/ligandparam/stages/generate_sage_params.py +87 -0
  53. ligandparam-0.3.5/src/ligandparam/stages/initialize.py +199 -0
  54. ligandparam-0.3.5/src/ligandparam/stages/leap.py +152 -0
  55. ligandparam-0.3.5/src/ligandparam/stages/lighfix.py +347 -0
  56. ligandparam-0.3.5/src/ligandparam/stages/parmchk.py +118 -0
  57. ligandparam-0.3.5/src/ligandparam/stages/pdb_names.py +311 -0
  58. ligandparam-0.3.5/src/ligandparam/stages/resp.py +226 -0
  59. ligandparam-0.3.5/src/ligandparam/stages/sdfconverters.py +440 -0
  60. ligandparam-0.3.5/src/ligandparam/stages/smilestopdb.py +301 -0
  61. ligandparam-0.3.5/src/ligandparam/stages/teststage.py +22 -0
  62. ligandparam-0.3.5/src/ligandparam/stages/typematching.py +290 -0
  63. ligandparam-0.3.5/src/ligandparam/stages/utilsstages.py +29 -0
  64. ligandparam-0.3.5/src/ligandparam/utils.py +161 -0
  65. ligandparam-0.3.5/src/ligandparam.egg-info/PKG-INFO +55 -0
  66. ligandparam-0.3.5/src/ligandparam.egg-info/SOURCES.txt +69 -0
  67. ligandparam-0.3.5/src/ligandparam.egg-info/dependency_links.txt +1 -0
  68. ligandparam-0.3.5/src/ligandparam.egg-info/entry_points.txt +5 -0
  69. ligandparam-0.3.5/src/ligandparam.egg-info/requires.txt +25 -0
  70. ligandparam-0.3.5/src/ligandparam.egg-info/top_level.txt +1 -0
  71. ligandparam-0.3.5/tests/test_module.py +13 -0
@@ -0,0 +1,55 @@
1
+ Metadata-Version: 2.4
2
+ Name: ligandparam
3
+ Version: 0.3.5
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: openff-toolkit
21
+ Provides-Extra: ml
22
+ Requires-Dist: deepmd-kit; extra == "ml"
23
+ Requires-Dist: tensorflow; extra == "ml"
24
+ Requires-Dist: tblite; extra == "ml"
25
+ Provides-Extra: misc
26
+ Requires-Dist: openbabel; extra == "misc"
27
+ Provides-Extra: docs
28
+ Requires-Dist: sphinx; extra == "docs"
29
+ Requires-Dist: sphinx_rtd_theme; extra == "docs"
30
+ Provides-Extra: all
31
+ Requires-Dist: ligandparam[ml]; extra == "all"
32
+ Requires-Dist: ligandparam[misc]; extra == "all"
33
+ Requires-Dist: ligandparam[docs]; extra == "all"
34
+
35
+ # Ligand Parametrization
36
+
37
+ ## Overview
38
+ This is a Python package designed to provide a simple workflow for ligand parameterization. It automates many of the
39
+ key features encountered by users, including...
40
+
41
+ ## Documentation
42
+
43
+ The online documentation is located here: https://ligandparam.readthedocs.io/en/latest/
44
+
45
+
46
+
47
+ ### Developing
48
+
49
+ #### Releasing a new version
50
+
51
+ 1. update `version` at `pyproject.toml`. Eg: `0.3.2`
52
+ 2. Add and commit the change. Then tag the commit: `git tag 0.3.2`
53
+ 3. `git push origin --tags`
54
+
55
+ gitlab's CI/CD will publish the new version automatically
@@ -0,0 +1,21 @@
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
+
11
+
12
+
13
+ ### Developing
14
+
15
+ #### Releasing a new version
16
+
17
+ 1. update `version` at `pyproject.toml`. Eg: `0.3.2`
18
+ 2. Add and commit the change. Then tag the commit: `git tag 0.3.2`
19
+ 3. `git push origin --tags`
20
+
21
+ gitlab's CI/CD will publish the new version automatically
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ligandparam"
7
+ version = "0.3.5"
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
+ "openff-toolkit"
29
+ ]
30
+
31
+ urls = { "Homepage" = "https://github.com/piskulichz/ligandparam" }
32
+
33
+ [project.optional-dependencies]
34
+
35
+ ml = [
36
+ "deepmd-kit",
37
+ "tensorflow",
38
+ "tblite"
39
+ ]
40
+
41
+ misc = [
42
+ "openbabel",
43
+ ]
44
+
45
+ docs = [
46
+ "sphinx",
47
+ "sphinx_rtd_theme",
48
+ ]
49
+
50
+ all = [
51
+ "ligandparam[ml]",
52
+ "ligandparam[misc]",
53
+ "ligandparam[docs]",
54
+ ]
55
+
56
+ [project.scripts]
57
+ lighfix = "ligandparam.cli.cli_lighfix:lighfix"
58
+ lig-getparam = "ligandparam.cli.ligandparam_getparam:main"
59
+ smiles-to-pdb = "ligandparam.cli.smiles_to_pdb:main"
60
+ lig-to-sage = "ligandparam.cli.mol2_to_sage:main"
61
+
62
+
63
+ [tool.ruff]
64
+ line-length = 120
65
+ indent-width = 4
66
+
67
+ [tool.ruff.format]
68
+ skip-magic-trailing-comma = true
69
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ # This file is intentionally left blank.
2
+ __version__ = "0.3.0"
3
+ __logging_name__ = "ligandparam"
4
+
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,20 @@
1
+ from openff.toolkit import Molecule, ForceField, Topology, Quantity
2
+ from openff.interchange import Interchange
3
+ from openff.units import unit
4
+ from rdkit import Chem
5
+ from rdkit.Chem import AllChem
6
+ import os
7
+
8
+ from ligandparam.stages import StageSageCreate
9
+
10
+ def main():
11
+ import argparse
12
+ parser = argparse.ArgumentParser(description="Generate AMBER parameters from a mol2 file.")
13
+ parser.add_argument("input_mol2", type=str, help="Path to the input mol2 file.")
14
+ parser.add_argument("output_tag", type=str, help="Tag for the output files.")
15
+ args = parser.parse_args()
16
+ stage = StageSageCreate("sage_creation", args.input_mol2, os.getcwd(), out_parm=f"{args.output_tag}.parm7")
17
+ stage.execute()
18
+
19
+ if __name__ == "__main__":
20
+ 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