mdprep 0.2.0__py3-none-any.whl

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 (100) hide show
  1. mdprep/__init__.py +7 -0
  2. mdprep/_selftest_data/basic_protein.yaml +33 -0
  3. mdprep/_selftest_data/ligand_sub.mol2 +11 -0
  4. mdprep/ambertools/__init__.py +2 -0
  5. mdprep/ambertools/antechamber.py +110 -0
  6. mdprep/ambertools/commands.py +33 -0
  7. mdprep/ambertools/mol2.py +415 -0
  8. mdprep/ambertools/parmchk2.py +89 -0
  9. mdprep/ambertools/resp.py +570 -0
  10. mdprep/charges/__init__.py +1 -0
  11. mdprep/charges/esp_grid.py +190 -0
  12. mdprep/cli.py +451 -0
  13. mdprep/config/__init__.py +9 -0
  14. mdprep/config/loader.py +146 -0
  15. mdprep/config/models.py +1100 -0
  16. mdprep/external/__init__.py +14 -0
  17. mdprep/external/discovery.py +14 -0
  18. mdprep/external/runner.py +55 -0
  19. mdprep/leap/__init__.py +2 -0
  20. mdprep/leap/builder.py +476 -0
  21. mdprep/leap/forcefields.py +93 -0
  22. mdprep/leap/ions.py +112 -0
  23. mdprep/leap/log_parser.py +170 -0
  24. mdprep/leap/report.py +99 -0
  25. mdprep/leap/residues.py +653 -0
  26. mdprep/leap/runner.py +65 -0
  27. mdprep/ligands/__init__.py +2 -0
  28. mdprep/ligands/extract.py +355 -0
  29. mdprep/ligands/identity.py +39 -0
  30. mdprep/ligands/pyscf_charges.py +242 -0
  31. mdprep/ligands/report.py +146 -0
  32. mdprep/ligands/workflow.py +838 -0
  33. mdprep/md/__init__.py +5 -0
  34. mdprep/md/roe_brooks.py +650 -0
  35. mdprep/metals/__init__.py +2 -0
  36. mdprep/metals/c4.py +157 -0
  37. mdprep/metals/coordination.py +313 -0
  38. mdprep/metals/gxtb_hessian.py +467 -0
  39. mdprep/metals/hydrogenate.py +344 -0
  40. mdprep/metals/mace_polar1_hessian.py +357 -0
  41. mdprep/metals/mace_polar1_worker.py +295 -0
  42. mdprep/metals/mcpb.py +949 -0
  43. mdprep/metals/mcpb_compat.py +125 -0
  44. mdprep/metals/nonbonded.py +294 -0
  45. mdprep/metals/parameter_comparison.py +563 -0
  46. mdprep/metals/pyscf_mcpb.py +1388 -0
  47. mdprep/metals/report.py +119 -0
  48. mdprep/metals/workflow.py +320 -0
  49. mdprep/protonation/__init__.py +15 -0
  50. mdprep/protonation/apply.py +968 -0
  51. mdprep/protonation/disulfide_states.py +141 -0
  52. mdprep/protonation/histidine_geometry.py +950 -0
  53. mdprep/protonation/histidine_xtb.py +390 -0
  54. mdprep/protonation/overrides.py +64 -0
  55. mdprep/protonation/pka_rules.py +165 -0
  56. mdprep/protonation/propka.py +174 -0
  57. mdprep/protonation/propka_parser.py +140 -0
  58. mdprep/protonation/report.py +187 -0
  59. mdprep/protonation/states.py +52 -0
  60. mdprep/protonation/temporary_hydrogenation.py +238 -0
  61. mdprep/protonation/xtb_parser.py +79 -0
  62. mdprep/protonation/xtb_runner.py +108 -0
  63. mdprep/qm/__init__.py +2 -0
  64. mdprep/qm/point_charges.py +233 -0
  65. mdprep/qm/pyscf_esp.py +70 -0
  66. mdprep/qm/pyscf_runner.py +570 -0
  67. mdprep/refinement/__init__.py +5 -0
  68. mdprep/refinement/ash.py +541 -0
  69. mdprep/refinement/coordinates.py +210 -0
  70. mdprep/refinement/mapping.py +163 -0
  71. mdprep/refinement/report.py +66 -0
  72. mdprep/refinement/selection.py +436 -0
  73. mdprep/refinement/workflow.py +188 -0
  74. mdprep/reports/__init__.py +8 -0
  75. mdprep/reports/structure_report.py +92 -0
  76. mdprep/structure/__init__.py +14 -0
  77. mdprep/structure/classify.py +92 -0
  78. mdprep/structure/disulfides.py +51 -0
  79. mdprep/structure/inspect.py +105 -0
  80. mdprep/structure/models.py +86 -0
  81. mdprep/structure/normalize.py +363 -0
  82. mdprep/structure/pdb.py +511 -0
  83. mdprep/structure/repair.py +566 -0
  84. mdprep/structure/selectors.py +204 -0
  85. mdprep/structure/writer.py +84 -0
  86. mdprep/validation/__init__.py +2 -0
  87. mdprep/validation/openmm_check.py +86 -0
  88. mdprep/validation/parmed_check.py +43 -0
  89. mdprep/validation/topology.py +239 -0
  90. mdprep/workflows/__init__.py +6 -0
  91. mdprep/workflows/init.py +242 -0
  92. mdprep/workflows/inspect.py +21 -0
  93. mdprep/workflows/prepare.py +763 -0
  94. mdprep/workflows/selftest.py +220 -0
  95. mdprep/workflows/validate.py +30 -0
  96. mdprep-0.2.0.dist-info/METADATA +264 -0
  97. mdprep-0.2.0.dist-info/RECORD +100 -0
  98. mdprep-0.2.0.dist-info/WHEEL +4 -0
  99. mdprep-0.2.0.dist-info/entry_points.txt +2 -0
  100. mdprep-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,220 @@
1
+ """Quick package-level self-test."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from importlib import import_module
7
+ from importlib.resources import as_file, files
8
+ from pathlib import Path
9
+ from tempfile import TemporaryDirectory
10
+
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+
14
+ import mdprep
15
+ from mdprep.ambertools.mol2 import Mol2File, read_mol2
16
+ from mdprep.ambertools.resp import write_amber_esp
17
+ from mdprep.charges.esp_grid import generate_merz_kollman_grid
18
+ from mdprep.config.loader import load_manifest
19
+ from mdprep.external.discovery import optional_executable_report
20
+ from mdprep.leap.forcefields import protein_leaprc, water_box, water_leaprc
21
+ from mdprep.qm.pyscf_runner import pyscf_available
22
+ from mdprep.validation.openmm_check import openmm_available
23
+ from mdprep.validation.parmed_check import parmed_available
24
+ import numpy as np
25
+
26
+
27
+ OPTIONAL_EXECUTABLES = [
28
+ "tleap",
29
+ "antechamber",
30
+ "parmchk2",
31
+ "respgen",
32
+ "resp",
33
+ "propka3",
34
+ "propka",
35
+ "xtb",
36
+ ]
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class SelftestSummary:
41
+ passed: bool
42
+ checked_examples: int
43
+
44
+
45
+ def _project_root() -> Path | None:
46
+ candidates = [
47
+ Path.cwd(),
48
+ Path(__file__).resolve().parents[3],
49
+ ]
50
+ for candidate in candidates:
51
+ if (candidate / "examples").is_dir():
52
+ return candidate
53
+ return None
54
+
55
+
56
+ def _validate_example_manifests(root: Path | None) -> tuple[int, list[str]]:
57
+ if root is not None:
58
+ examples = sorted((root / "examples").glob("*.yaml"))
59
+ errors: list[str] = []
60
+ for example in examples:
61
+ try:
62
+ load_manifest(example)
63
+ except Exception as exc:
64
+ errors.append(f"{example.name}: {exc}")
65
+ return len(examples), errors
66
+
67
+ resource = files("mdprep").joinpath("_selftest_data/basic_protein.yaml")
68
+ with as_file(resource) as example:
69
+ try:
70
+ load_manifest(example)
71
+ except Exception as exc:
72
+ return 1, [f"{example.name}: {exc}"]
73
+ return 1, []
74
+
75
+
76
+ def _read_mol2_fixture(root: Path | None) -> Mol2File:
77
+ if root is not None:
78
+ return read_mol2(root / "tests" / "data" / "ligands" / "ligand_sub.good.mol2")
79
+
80
+ resource = files("mdprep").joinpath("_selftest_data/ligand_sub.mol2")
81
+ with as_file(resource) as fixture:
82
+ return read_mol2(fixture)
83
+
84
+
85
+ def _blocked_tokens() -> tuple[str, ...]:
86
+ return (
87
+ "".join(("open", "babel")),
88
+ "".join(("Open", "Babel")),
89
+ "".join(("py", "bel")),
90
+ "".join(("o", "babel")),
91
+ )
92
+
93
+
94
+ def _source_has_blocked_tokens() -> list[Path]:
95
+ source_root = Path(__file__).resolve().parents[1]
96
+ blocked = _blocked_tokens()
97
+ matches: list[Path] = []
98
+ for path in source_root.rglob("*.py"):
99
+ text = path.read_text(encoding="utf-8")
100
+ if any(token in text for token in blocked):
101
+ matches.append(path)
102
+ return matches
103
+
104
+
105
+ def run_selftest(*, quick: bool = False, console: Console | None = None) -> SelftestSummary:
106
+ out = console or Console()
107
+ root = _project_root()
108
+
109
+ out.print(f"mdprep {mdprep.__version__}")
110
+ out.print("Mode: quick" if quick else "Mode: standard")
111
+
112
+ checks: list[tuple[str, bool, str]] = []
113
+
114
+ try:
115
+ import_module("mdprep")
116
+ import_module("mdprep.config.loader")
117
+ import_module("mdprep.external.discovery")
118
+ checks.append(("Python imports", True, "ok"))
119
+ except Exception as exc:
120
+ checks.append(("Python imports", False, str(exc)))
121
+
122
+ example_count, example_errors = _validate_example_manifests(root)
123
+ checks.append(
124
+ (
125
+ "Example manifests",
126
+ not example_errors and example_count > 0,
127
+ f"{example_count} validated" if not example_errors else "; ".join(example_errors),
128
+ )
129
+ )
130
+
131
+ blocked_matches = _source_has_blocked_tokens()
132
+ checks.append(
133
+ (
134
+ "Prohibited toolkit source scan",
135
+ not blocked_matches,
136
+ "ok" if not blocked_matches else ", ".join(str(path) for path in blocked_matches),
137
+ )
138
+ )
139
+
140
+ try:
141
+ mol2 = _read_mol2_fixture(root)
142
+ checks.append(("mol2 parser fixture", len(mol2.atoms) == 2, f"{len(mol2.atoms)} atoms"))
143
+ except Exception as exc:
144
+ checks.append(("mol2 parser fixture", False, str(exc)))
145
+
146
+ try:
147
+ ok = (
148
+ protein_leaprc("ff19SB") == "leaprc.protein.ff19SB"
149
+ and water_leaprc("TIP3P") == "leaprc.water.tip3p"
150
+ and water_box("OPC") == "OPCBOX"
151
+ )
152
+ checks.append(("force-field mappings", ok, "ok" if ok else "unexpected mapping"))
153
+ except Exception as exc:
154
+ checks.append(("force-field mappings", False, str(exc)))
155
+
156
+ checks.append(("ParmEd import", True, "available" if parmed_available() else "not available"))
157
+ checks.append(("OpenMM import", True, "available" if openmm_available() else "not available"))
158
+ checks.append(("PySCF import", True, "available" if pyscf_available() else "not available"))
159
+
160
+ try:
161
+ coords = np.asarray([[0.0, 0.0, 0.0], [0.96, 0.0, 0.0]], dtype=float)
162
+ grid_a = generate_merz_kollman_grid(
163
+ elements=["O", "H"],
164
+ coordinates=coords,
165
+ vdw_scale_factors=[1.4],
166
+ point_density_per_square_angstrom=0.25,
167
+ exclude_inside_vdw_scale=1.4,
168
+ max_points=100,
169
+ )
170
+ grid_b = generate_merz_kollman_grid(
171
+ elements=["O", "H"],
172
+ coordinates=coords,
173
+ vdw_scale_factors=[1.4],
174
+ point_density_per_square_angstrom=0.25,
175
+ exclude_inside_vdw_scale=1.4,
176
+ max_points=100,
177
+ )
178
+ checks.append(("ESP grid determinism", bool(np.allclose(grid_a.points, grid_b.points)), f"{len(grid_a.points)} points"))
179
+ except Exception as exc:
180
+ checks.append(("ESP grid determinism", False, str(exc)))
181
+
182
+ try:
183
+ atom_coords = np.asarray([[0.0, 0.0, 0.0], [2.0, 0.0, 0.0]], dtype=float)
184
+ grid_coords = np.asarray(
185
+ [[0.0, 3.0, 0.0], [2.0, 3.0, 0.0], [1.0, 3.5, 0.0], [1.0, -3.0, 0.0]],
186
+ dtype=float,
187
+ )
188
+ esp = np.asarray([-0.1, 0.1, 0.0, 0.0], dtype=float)
189
+ with TemporaryDirectory(prefix="mdprep-selftest-") as temporary_dir:
190
+ path = write_amber_esp(
191
+ atom_coordinates_bohr=atom_coords,
192
+ grid_coordinates_bohr=grid_coords,
193
+ esp_values_au=esp,
194
+ path=Path(temporary_dir) / "test.esp",
195
+ )
196
+ lines = path.read_text(encoding="ascii").splitlines()
197
+ fixed_width_ok = lines[0] == " 2 4" and len(lines[1]) == 65 and len(lines[3]) == 65
198
+ checks.append(("Amber RESP ESP format", fixed_width_ok, "2 atoms, 4 grid points"))
199
+ except Exception as exc:
200
+ checks.append(("Amber RESP ESP format", False, str(exc)))
201
+
202
+ table = Table(title="mdprep self-test")
203
+ table.add_column("Check")
204
+ table.add_column("Status")
205
+ table.add_column("Detail")
206
+ for name, ok, detail in checks:
207
+ table.add_row(name, "PASS" if ok else "FAIL", detail)
208
+ out.print(table)
209
+
210
+ exec_table = Table(title="Optional executables")
211
+ exec_table.add_column("Executable")
212
+ exec_table.add_column("Path")
213
+ for name, path in optional_executable_report(OPTIONAL_EXECUTABLES).items():
214
+ exec_table.add_row(name, path or "not found")
215
+ out.print(exec_table)
216
+
217
+ return SelftestSummary(
218
+ passed=all(ok for _, ok, _ in checks),
219
+ checked_examples=example_count,
220
+ )
@@ -0,0 +1,30 @@
1
+ """Standalone prepared-system validation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from mdprep.validation.parmed_check import run_parmed_check
8
+
9
+
10
+ def validate_system(prmtop: str | Path, inpcrd: str | Path) -> dict[str, object]:
11
+ topology = Path(prmtop)
12
+ coordinates = Path(inpcrd)
13
+ errors: list[str] = []
14
+ for label, path in [("prmtop", topology), ("inpcrd", coordinates)]:
15
+ if not path.exists():
16
+ errors.append(f"Missing {label}: {path}")
17
+ elif path.stat().st_size == 0:
18
+ errors.append(f"Empty {label}: {path}")
19
+ parmed = None
20
+ if not errors:
21
+ parmed = run_parmed_check(topology, coordinates)
22
+ if parmed.get("status") == "error":
23
+ errors.append(f"ParmEd validation failed: {parmed.get('error')}")
24
+ if errors:
25
+ raise ValueError("; ".join(errors))
26
+ return {
27
+ "prmtop": str(topology),
28
+ "inpcrd": str(coordinates),
29
+ "parmed": parmed,
30
+ }
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.5
2
+ Name: mdprep
3
+ Version: 0.2.0
4
+ Summary: Reproducible Amber preparation for proteins, ligands, metal centers, and OpenMM MD.
5
+ Project-URL: Homepage, https://github.com/miqueleg/mdprep
6
+ Project-URL: Issues, https://github.com/miqueleg/mdprep/issues
7
+ Author: Miquel Estévez Gay
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: amber,ligand,mcpb,metal,molecular-dynamics,openmm,protein,pyscf,qmmm,resp
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
19
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: numpy>=1.24
22
+ Requires-Dist: pandas>=2
23
+ Requires-Dist: pydantic-settings>=2
24
+ Requires-Dist: pydantic>=2
25
+ Requires-Dist: pyyaml>=6
26
+ Requires-Dist: rich>=13
27
+ Requires-Dist: scipy>=1.10
28
+ Requires-Dist: typer>=0.12
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1; extra == 'dev'
31
+ Requires-Dist: mypy>=1.10; extra == 'dev'
32
+ Requires-Dist: pytest>=8; extra == 'dev'
33
+ Requires-Dist: ruff>=0.6; extra == 'dev'
34
+ Requires-Dist: twine>=5; extra == 'dev'
35
+ Provides-Extra: docs
36
+ Requires-Dist: mkdocs>=1.6; extra == 'docs'
37
+ Provides-Extra: md
38
+ Requires-Dist: openmm>=8.1; extra == 'md'
39
+ Provides-Extra: qm
40
+ Requires-Dist: parmed>=4.2; extra == 'qm'
41
+ Requires-Dist: pyscf>=2.6; extra == 'qm'
42
+ Provides-Extra: test
43
+ Requires-Dist: pytest>=8; extra == 'test'
44
+ Description-Content-Type: text/markdown
45
+
46
+ # mdprep
47
+
48
+ `mdprep` 0.2.0 prepares reproducible Amber molecular-dynamics systems from a
49
+ PDB structure and a validated YAML manifest. It supports standard proteins,
50
+ independent ligands and cofactors, explicit metal models, optional active-site
51
+ QM/MM refinement, final `tleap` assembly, validation, and an optional OpenMM
52
+ equilibration/production stage.
53
+
54
+ The manifest is the source of truth. Chemistry-sensitive decisions—ligand
55
+ charge and multiplicity, catalytic protonation, metal oxidation/spin state,
56
+ coordinating bonds, and QM components—must be supplied explicitly and are
57
+ recorded in the output reports.
58
+
59
+ ## Highlights
60
+
61
+ - ff14SB or ff19SB proteins with TIP3P or OPC water.
62
+ - PropKa protonation, manual catalytic overrides, disulfides, and optional
63
+ GFN2-xTB/g-xTB HID–HIE selection.
64
+ - Multiple GAFF or GAFF2 ligands/cofactors with AM1-BCC, user mol2/frcmod,
65
+ PySCF gas-phase RESP, or electrostatically embedded PySCF QMMESP charges.
66
+ - Amber nonbonded metal models or bonded MCPB.py models with explicit
67
+ coordinating atoms, charges, and multiplicities.
68
+ - MCPB Seminario Hessians from PySCF, GFN2-xTB, g-xTB, or MACE-POLAR-1;
69
+ PySCF supplies the embedded large-model ESP/RESP calculation.
70
+ - Optional ASH/OpenMM active-site QM/MM refinement before final
71
+ parameterization.
72
+ - Final `prmtop`, `inpcrd`, and PDB generation through `tleap`, followed by
73
+ topology, coordinate, charge, and optional OpenMM energy validation.
74
+ - Optional Roe–Brooks OpenMM minimization, heating, density equilibration, and
75
+ manifest-sized production MD with GPU-first/CPU-fallback execution.
76
+
77
+ ## Installation
78
+
79
+ Conda or Mamba is recommended because the real workflows use AmberTools,
80
+ PropKa, xTB, OpenMM, ParmEd, and PySCF:
81
+
82
+ ```bash
83
+ git clone https://github.com/miqueleg/mdprep.git
84
+ cd mdprep
85
+ mamba env create -f environment.yml
86
+ conda activate mdprep
87
+ mdprep selftest --quick
88
+ ```
89
+
90
+ For a lighter Python-only installation, which does not install external
91
+ chemistry executables:
92
+
93
+ ```bash
94
+ python -m pip install .
95
+ ```
96
+
97
+ Optional Python extras are available as `.[md]` for OpenMM and `.[qm]` for
98
+ PySCF/ParmEd. External commands are discovered through `PATH`; manifests may
99
+ also name a reviewed relative executable. No developer-machine path is embedded
100
+ in the distributed examples.
101
+
102
+ ## Basic example
103
+
104
+ Start from a standard protein PDB:
105
+
106
+ ```bash
107
+ mdprep inspect input.pdb
108
+ mdprep init input.pdb -o system.yaml
109
+ ```
110
+
111
+ A minimal protein-only `system.yaml` is:
112
+
113
+ ```yaml
114
+ project:
115
+ name: my_system
116
+ input_structure: input.pdb
117
+ output_dir: prepared/my_system
118
+
119
+ structure:
120
+ keep_crystal_waters: true
121
+ altloc_policy: highest_occupancy
122
+ remove_unknown_heterogens: false
123
+ preserve_chain_ids: true
124
+ remove_input_hydrogens: true
125
+
126
+ protein:
127
+ forcefield: ff14SB
128
+ water_model: TIP3P
129
+
130
+ protonation:
131
+ ph: 7.0
132
+ method: manual_only
133
+ overrides: []
134
+
135
+ disulfides:
136
+ auto_detect: true
137
+ detection_cutoff_angstrom: 2.2
138
+ force: []
139
+ forbid: []
140
+
141
+ ligands: []
142
+ metals: []
143
+
144
+ solvation:
145
+ enabled: true
146
+ box: truncated_octahedron
147
+ buffer_angstrom: 10.0
148
+ neutralize: true
149
+ salt_concentration_molar: 0.15
150
+ positive_ion: Na+
151
+ negative_ion: Cl-
152
+
153
+ validation:
154
+ run_openmm_energy_check: true
155
+ fail_on_warnings: false
156
+ fail_on_missing_parameters: true
157
+ fail_on_noninteger_ligand_charge: true
158
+
159
+ molecular_dynamics:
160
+ enabled: false
161
+ ```
162
+
163
+ The same validated manifest is available as
164
+ [`examples/00_basic_protein.yaml`](examples/00_basic_protein.yaml).
165
+
166
+ Validate and prepare it:
167
+
168
+ ```bash
169
+ mdprep config-check system.yaml
170
+ mdprep prepare system.yaml
171
+ mdprep validate \
172
+ prepared/my_system/final/system.prmtop \
173
+ prepared/my_system/final/system.inpcrd
174
+ ```
175
+
176
+ Relative paths are resolved from the manifest directory. Unknown heterogens
177
+ are not deleted or guessed: configure them under `ligands` or `metals`, or
178
+ explicitly request their removal.
179
+
180
+ ## Common workflows
181
+
182
+ Generate a starter manifest using PropKa and ff19SB/OPC:
183
+
184
+ ```bash
185
+ mdprep init input.pdb -o system.yaml \
186
+ --forcefield ff19SB \
187
+ --water-model OPC \
188
+ --protonation-method propka
189
+ ```
190
+
191
+ Stop after a stage while reviewing a complex system:
192
+
193
+ ```bash
194
+ mdprep prepare system.yaml --stop-after structure
195
+ mdprep prepare system.yaml --stop-after protonation --overwrite
196
+ mdprep prepare system.yaml --stop-after refinement --overwrite
197
+ mdprep prepare system.yaml --stop-after ligands --overwrite
198
+ mdprep prepare system.yaml --stop-after metals --overwrite
199
+ mdprep prepare system.yaml --stop-after tleap --overwrite
200
+ ```
201
+
202
+ Run the manifest-configured Roe–Brooks protocol from an existing Amber build:
203
+
204
+ ```bash
205
+ mdprep run-md system.yaml \
206
+ --prmtop prepared/my_system/final/system.prmtop \
207
+ --inpcrd prepared/my_system/final/system.inpcrd
208
+ ```
209
+
210
+ When `molecular_dynamics.enabled: true`, an ordinary `mdprep prepare` continues
211
+ through MD. Production length and trajectory/state/checkpoint intervals are
212
+ specified in steps in the manifest.
213
+
214
+ ## Where to go next
215
+
216
+ | Task | Documentation | Example |
217
+ | --- | --- | --- |
218
+ | Manifest fields | [Manifest reference](docs/manifest_reference.md) | [All examples](examples/README.md) |
219
+ | Protonation and histidines | [Protonation](docs/protonation.md) | `examples/02_manual_catalytic_protonation.yaml` |
220
+ | Ligands, AM1-BCC, RESP, QMMESP | [Ligands](docs/ligands.md) and [QMMESP](docs/qmmesp_pyscf.md) | `examples/03_multi_ligand_am1bcc.yaml`, `04_qmmesp_pyscf_ligand.yaml` |
221
+ | Metal centers and MCPB.py | [Metal centers](docs/metals.md) | `examples/08_nonbonded_zinc_1264.yaml`, `09_bonded_zinc_mcpb_prepare.yaml` |
222
+ | Active-site QM/MM refinement | [QM/MM refinement](docs/qmmm_refinement.md) | `examples/10_qmmm_refinement_ash.yaml` |
223
+ | Roe–Brooks OpenMM MD | [Molecular dynamics](docs/molecular_dynamics.md) | `examples/15_roe_brooks_openmm.yaml` |
224
+ | Complete Fe(III) workflow | [7E07 tutorial](examples/tutorials/7E07_bonded_fe3/README.md) | `examples/tutorials/7E07_bonded_fe3/system.yaml` |
225
+
226
+ All public example manifests are schema-validated in the test suite.
227
+
228
+ ## Outputs and reproducibility
229
+
230
+ A complete run preserves:
231
+
232
+ - the input and locked manifests;
233
+ - normalized/protonated/refined intermediate PDBs;
234
+ - ligand, QM, MCPB.py, and `tleap` inputs and logs;
235
+ - final Amber topology, coordinates, and PDB;
236
+ - structure, protonation, ligand, metal, `tleap`, and validation reports;
237
+ - executable and Python-package version information;
238
+ - optional MD PDBs, density history, trajectory, state log, and restarts.
239
+
240
+ External failures are reported with command, working directory, return code,
241
+ stdout, stderr, and runtime. mdprep does not silently fall back to another
242
+ charge model, protonation, force field, QM method, or metal model.
243
+
244
+ ## Current limitations
245
+
246
+ Version 0.2.0 does not support noncanonical amino acids inside peptide chains,
247
+ covalent ligands, mmCIF input, automatic loop modeling, ORCA, Multiwfn,
248
+ multiple independent bonded MCPB sites, or multi-chain MCPB protein inputs.
249
+ Unsupported chemistry fails explicitly.
250
+
251
+ ## Development and release checks
252
+
253
+ ```bash
254
+ mamba env create -f environment-dev.yml
255
+ conda activate mdprep-dev
256
+ pytest -q
257
+ python -m mdprep.cli --help
258
+ python -m mdprep.cli config-check examples/*.yaml
259
+ python -m mdprep.cli selftest --quick
260
+ python -m hatchling build
261
+ ```
262
+
263
+ See [CONTRIBUTING.md](CONTRIBUTING.md), [CHANGELOG.md](CHANGELOG.md), and
264
+ [CITATION.cff](CITATION.cff). mdprep is distributed under the MIT license.
@@ -0,0 +1,100 @@
1
+ mdprep/__init__.py,sha256=NFWf_wM1aFwOteTgCQZsHzilC14lueT5DwDkYD_Ptrk,108
2
+ mdprep/cli.py,sha256=G8XKYQnmyu3hSoS6qzEANhdrb4-6rNC4AWxmaH_Lx5I,15903
3
+ mdprep/_selftest_data/basic_protein.yaml,sha256=II2s1MHqocorravC0oN9llxTXWDTzxNKKfBgKthLCB4,479
4
+ mdprep/_selftest_data/ligand_sub.mol2,sha256=iYr_D00W0hwMt-fHF8QSaZqYFWHm6cBoEisuaARNb00,268
5
+ mdprep/ambertools/__init__.py,sha256=11IDxd6TOTzyt3V5WKI8KVhtntDlA3YwmgyYUFdO_Xo,36
6
+ mdprep/ambertools/antechamber.py,sha256=42T2J2b8hQUscIi6LMk6IGfYrW0OgnabuZM7C1zoEBU,2943
7
+ mdprep/ambertools/commands.py,sha256=HtpDZkWUUXZutxFt3KdR1kTJvQr-kvOgY9E6S_rWgs4,997
8
+ mdprep/ambertools/mol2.py,sha256=mPMA6FqhsuzVh5-AJAHwUcTRBU1m8gHHi084XaRF3Go,12755
9
+ mdprep/ambertools/parmchk2.py,sha256=l1hpzfDDCzwmeUIL4MnqhEpyD97P2CICYjt8CHpEIHI,2540
10
+ mdprep/ambertools/resp.py,sha256=o0fbYa05NiWNOsNDwUwoKFwmUhRV-yfR4cQKQUoWeaE,20234
11
+ mdprep/charges/__init__.py,sha256=orUoBoYV7N85A0Z2XBp8HHYrbz2ZW3Z5CvHkZoQESc4,38
12
+ mdprep/charges/esp_grid.py,sha256=ShSWr5IQ0iLsuquzyOaX_GZm9vhLyRkLcx1Z9PA-EbM,6430
13
+ mdprep/config/__init__.py,sha256=dgn349blqEXMOMGDbl5GTAy7IcmeNeQi1L7HC6UzqUY,228
14
+ mdprep/config/loader.py,sha256=2H1i4swe6_JqP62Pd_tGy9cbVS5-E9u7YqaMf8l8KcM,5961
15
+ mdprep/config/models.py,sha256=WmYc-Y1UEyQGyUWOabpxMGlLoSEL0paJZxFnofxyqYs,45591
16
+ mdprep/external/__init__.py,sha256=XFwbRVVJr_q6VPdPQBpbeWBYQtFXX-c3gimVZSKPKqQ,328
17
+ mdprep/external/discovery.py,sha256=VEm5kYk4WUP6WmgkZqxjRQkMgM_QzA4iTEFIFcjhvrY,318
18
+ mdprep/external/runner.py,sha256=bXYeyYff3JA9s49Rs3X-fSvnb92V4J6urVkx92D4TLE,1421
19
+ mdprep/leap/__init__.py,sha256=XOmJw8MMe0urNrQfrafCgKz3w0_fR_ZHD15TkDbgRjs,35
20
+ mdprep/leap/builder.py,sha256=-ZgvrGvjesm4fcx3_dz5kuXOllWo-d2g_u_GhqrkYNM,18686
21
+ mdprep/leap/forcefields.py,sha256=YuBZlPU1Bah3gaXy1741PQlDkG1tM7jxuuPx9gHsYhM,2275
22
+ mdprep/leap/ions.py,sha256=hZbyS2OE7dkF9K-W9poVDhcw7rXVmuUKIv5MEnFAH2o,3555
23
+ mdprep/leap/log_parser.py,sha256=aJMfmug3NV2M-eE3iokbbaX6AG56lHy92fVuUwLmIn0,6074
24
+ mdprep/leap/report.py,sha256=a3X6oGoyOR7WtyU4oS_q9Wgx6ME7QgzXg2ei02516ak,3082
25
+ mdprep/leap/residues.py,sha256=B1y6g1ilcczAUy_Ik5uru8zo4rLGkiVVXzyl6-68ofo,25835
26
+ mdprep/leap/runner.py,sha256=QDOL7wksI_OmEx8tH_9sDmYNqyj2q0Zgvi8H2dlq0ZQ,2028
27
+ mdprep/ligands/__init__.py,sha256=2lyVnqZFYfVrTYZe7tvmXynobk8kPGmWBSkgaPQ6XdE,57
28
+ mdprep/ligands/extract.py,sha256=8LG2nvl6-xltbmCHyFRibcetBQsXDy-a2AVFaL5aYZE,13502
29
+ mdprep/ligands/identity.py,sha256=PJGvIIpQ6o1ZyCo-dOkwLFIOkCMeyUiwQ7X5dmrpFV0,1151
30
+ mdprep/ligands/pyscf_charges.py,sha256=iNUbz518lKVHTZQGAklv7CEYkwo15_AUx_XrO8LxoDk,10730
31
+ mdprep/ligands/report.py,sha256=Fy0Z0JM9bWrenKrkzwBN3FBjFNZ93K47hv-HJJ5u0PI,5782
32
+ mdprep/ligands/workflow.py,sha256=B1M3N6lfTbYagfOkZcDF5qw1wvMQZ1utWnJh6smeiAM,34158
33
+ mdprep/md/__init__.py,sha256=IxNFQXkdY5aoFQEQgw_JwHzFFj6lvbycNF36ayP99g8,168
34
+ mdprep/md/roe_brooks.py,sha256=1NVpShAIFdJ-itEM8wHE4muelFU4lZj74d4bXJHuOxE,26358
35
+ mdprep/metals/__init__.py,sha256=9JFeDdF8kzl554wFX93bqmCwpXfY5VSQn490hpiZ6pU,41
36
+ mdprep/metals/c4.py,sha256=6-cBuJ76-6vL8Ejw2ahmn3j3GQtHJJXwXb0XcvlUqpc,5696
37
+ mdprep/metals/coordination.py,sha256=QFTs7akw9blLTanW-QNKMAyY7v-CJZ_scdFWXb-hm0M,11751
38
+ mdprep/metals/gxtb_hessian.py,sha256=cmv_WEeZO_IyLBaIBne9ek2JxDfHFnqnze5_nlEKX8Y,16960
39
+ mdprep/metals/hydrogenate.py,sha256=pxeD-BLg6iVT-PHMKHt7muqHmEtPCDtJ11pBx16p2DI,12982
40
+ mdprep/metals/mace_polar1_hessian.py,sha256=9Y-lcrdM4KKl7gYmPu9M1s6RFYTxRgMx28ciCBfG0Gc,13433
41
+ mdprep/metals/mace_polar1_worker.py,sha256=O9xQQ_npvcY9t03ghhX9Ew9w5sbrQmTuQPjrHuH_jDk,10468
42
+ mdprep/metals/mcpb.py,sha256=Bov0p8LjGH1ON5MPu4s9-HKbHuyMUczoawLesewuTjM,35723
43
+ mdprep/metals/mcpb_compat.py,sha256=h2cWsJt7pGZk1ud8zktHYTVUluWUNKx3bwDdUaO_XVw,4225
44
+ mdprep/metals/nonbonded.py,sha256=ifto_OdB00ainPCTWeztrbvt_Qa-Yk24-wr8TJCeVo8,10245
45
+ mdprep/metals/parameter_comparison.py,sha256=Ygcn0gQaS97VuhtjdmPVklS2TGGLKU-oTm8aH27hxaQ,21189
46
+ mdprep/metals/pyscf_mcpb.py,sha256=DT5WAgAYi15R1y7_4MQIA7HK6MLbkmA7lwiL9yS0gYQ,54718
47
+ mdprep/metals/report.py,sha256=A8zmYUmCPItFNY5w6pMdd6GyuFcNcbVMZ1pbQBsmTAE,4694
48
+ mdprep/metals/workflow.py,sha256=rk0kB4qKxIPSiiwTiSbApWGrwPYrsRHCDgL-F0Jq5Os,12968
49
+ mdprep/protonation/__init__.py,sha256=DU8KQh6gAaM_zfdnvtZLSCtuM2kbtJ2nYv_dNQgjuFQ,465
50
+ mdprep/protonation/apply.py,sha256=svVgbbbiYGabfLrYyQWyelMq0AyVBO_yQn55nVFBdDI,39519
51
+ mdprep/protonation/disulfide_states.py,sha256=0R4yHQXuSfboqKT3Gzv7TRMmoC7A77CtUbJCsvLRgIQ,4949
52
+ mdprep/protonation/histidine_geometry.py,sha256=NzCPbxIwIMWRcaOZiTrvSAGvMtOgmplreTfv4ORMSMU,33581
53
+ mdprep/protonation/histidine_xtb.py,sha256=IXpwlTWw8tL18H7XtN8FJchA0SaHf3HuVl5XySrGPXo,14017
54
+ mdprep/protonation/overrides.py,sha256=DWa4avlXA0hssUNW0QmQHJdjwiYj4eZcd2nWbl0-CwE,2203
55
+ mdprep/protonation/pka_rules.py,sha256=vgfn4d8pmMJwvjG6NjICtl8crUIDBFxumpUNI7g8pvM,5221
56
+ mdprep/protonation/propka.py,sha256=TUauk9MzmVbQZaOrGkYeh3dW3ex_d9UZcwQPoE7KqVg,6188
57
+ mdprep/protonation/propka_parser.py,sha256=pquqx_dnNv0A8ns2xVuNwqxYoDayaLPNrgMV5NLMn7k,3934
58
+ mdprep/protonation/report.py,sha256=6WhEpvla4WhbtowCr8b5r8Hy25PSaEJGG6T-2HVlKAU,6953
59
+ mdprep/protonation/states.py,sha256=W2pT9FskDmErCyq8MrBXt9tilZE6vypv4x-YSlNkttQ,1590
60
+ mdprep/protonation/temporary_hydrogenation.py,sha256=OO9qmCPJ6tUga7SQBATM2he2qVoMB9Vpjcm6LlA5ebs,9413
61
+ mdprep/protonation/xtb_parser.py,sha256=c0SZlYBqikomIUTNxrG_LSGFJpN-s5UKWEFLc5CZ8bw,2430
62
+ mdprep/protonation/xtb_runner.py,sha256=c-jWif4Tw8i_6N67XvuvmHsd9ISEQgUitxpX-rN2dUU,3532
63
+ mdprep/qm/__init__.py,sha256=Q6hS_uPA2LsQRrnr1NDpAmK03iC82JzVRP-hpJWJsNo,27
64
+ mdprep/qm/point_charges.py,sha256=kJjyFZ4zI-xgRyDrabvnDRvMQUS4QjdKqNEo-RF-o6s,9010
65
+ mdprep/qm/pyscf_esp.py,sha256=DiJkgBQav4X5ceV7I5vdQIrGgQ4P-bf5p9OoV2eb-Y8,2936
66
+ mdprep/qm/pyscf_runner.py,sha256=Y5fATm9OMZV-4YADL9SrFNvy1qqP0qRSp2wjdh5BB9E,23192
67
+ mdprep/refinement/__init__.py,sha256=x_BFoDFNzvI1CPaq5SC5FMPpSUC_0Vt0MgIq4wcLV0c,210
68
+ mdprep/refinement/ash.py,sha256=2-UzOx2HAJilNNM51wSUmnltxRyVysziJc_ADtr5aGI,21916
69
+ mdprep/refinement/coordinates.py,sha256=9Ek79aRXZm0tQZCBzc79sh-wKq2wwyyaLjtdjgbkGVc,8609
70
+ mdprep/refinement/mapping.py,sha256=hmtB2eZmydNM-98y5i1ZPwHwmTT8TSknna8DWnEtbBU,5796
71
+ mdprep/refinement/report.py,sha256=y4qgNd8TY_V7TcHwA67JG3jKxlbb9GQLcph-Cw_6xhI,2623
72
+ mdprep/refinement/selection.py,sha256=u5wrLArzBv5WM0DxcPDHw_kwTfNEM8Jy-Kn9XzK6e8I,17177
73
+ mdprep/refinement/workflow.py,sha256=FJk-cTuHBPiv8MEKw9dVTvjNKPPJVsVx7-rFHXr9Ack,6971
74
+ mdprep/reports/__init__.py,sha256=w91ptuL6SzaywtH0VzEzUBitC0jIsEr3lSe6aoXd4vU,167
75
+ mdprep/reports/structure_report.py,sha256=baX3ekllzVUKXQ-KFUQzY-lIwZBFuFbV9CA9ZFRNtlE,3308
76
+ mdprep/structure/__init__.py,sha256=A_yKx-ixlCy01w7eAOZk6FExwr0vRAr-DTTv9LhkQbw,351
77
+ mdprep/structure/classify.py,sha256=-kcjAXzFEU7wFW1-Rgnz_rnjBhPpiCfRrBLdghkpWSM,2013
78
+ mdprep/structure/disulfides.py,sha256=IGlpAClelrzLFvL3_UtUShmaKSdyikHLP3hnD6ewEKk,1539
79
+ mdprep/structure/inspect.py,sha256=XqGMQKf54WwuyMgIWSB3k4qtkMU42U6BQTNT3PyOWA8,4165
80
+ mdprep/structure/models.py,sha256=nQUakPXfjrM1bsTnK1tne18gTkH7ldfCXWSW-PZ0ST4,2316
81
+ mdprep/structure/normalize.py,sha256=8TVB7osyG46K6u6IQx20I2seIvZfeFDl23EDxtNXXS0,15345
82
+ mdprep/structure/pdb.py,sha256=ccRuijS7PyQU2pBnpQyvk-ISrQ5r86Ik2xxxcka8vV0,13286
83
+ mdprep/structure/repair.py,sha256=gWJQ8yzmlezN9t0kOlUAiS7mdEq2cI-zjxt5awnJfpA,20798
84
+ mdprep/structure/selectors.py,sha256=URjI0liedLqrEoh3aYcZsL9EQfzgav_IiX9EdU-19i8,7986
85
+ mdprep/structure/writer.py,sha256=pI-0z30j0WSdVcw-8Le0s-jbUWzG2WU6AbGkeKNesL0,3225
86
+ mdprep/validation/__init__.py,sha256=yjJhITuigb6aUxakVjoxSfwDD_2Ah059nNLos7O9DoI,42
87
+ mdprep/validation/openmm_check.py,sha256=2p3iATKpdUq5VLx-lF1nogBlueAQAlS2ldxvLIc-IFA,3338
88
+ mdprep/validation/parmed_check.py,sha256=vvyveiTpziV_Xdw55_wipvAlsP1j_XQHWqr0kT3ppXA,1384
89
+ mdprep/validation/topology.py,sha256=cy54stpYi210PmHDcXmUhghcGaDoXelEvTHT53Qy1Fk,9276
90
+ mdprep/workflows/__init__.py,sha256=HuHuTGSCYC_VU7Z1a8wq9DK7VQmqMAdPwMJK07FNF4Y,91
91
+ mdprep/workflows/init.py,sha256=mJwaYU7eqomlP3z_LlsGlXUi1DoXXX2_ze42n3L_8J8,8748
92
+ mdprep/workflows/inspect.py,sha256=ZdtxqkZCYrDugvgltmTvDet70lhq7QOCF-NTqb-6JbA,575
93
+ mdprep/workflows/prepare.py,sha256=BRby5x1nVfUSAVYepfQKUB-Ey2N4f6dvwk0FJ-JQ4w0,32406
94
+ mdprep/workflows/selftest.py,sha256=B4d527b_zIfMxAIVZZVlZGAYbop9-hfToW22qtQYdw8,7517
95
+ mdprep/workflows/validate.py,sha256=ug-3xySmGkB1iv-GOR6OOvNu8KD4aNlmCsGELpjQ5-E,974
96
+ mdprep-0.2.0.dist-info/METADATA,sha256=wmsqw690pY4AknbmvIcmZBXg7eihcscjdbUmrAoRmGQ,8933
97
+ mdprep-0.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
98
+ mdprep-0.2.0.dist-info/entry_points.txt,sha256=Ht5zL5TCWAvC9VBPeVeKws5pD0-Ajx8TM08rJSWJl3A,42
99
+ mdprep-0.2.0.dist-info/licenses/LICENSE,sha256=X3UjXiT5AmegEGyA-AeYktV8_ZtheJktrGGcwwa1CgE,1076
100
+ mdprep-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mdprep = mdprep.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miquel Estévez-Gay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.