chemur 0.1.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.
Files changed (51) hide show
  1. chemur-0.1.0/.gitignore +29 -0
  2. chemur-0.1.0/CMakeLists.txt +14 -0
  3. chemur-0.1.0/LICENSE +21 -0
  4. chemur-0.1.0/PKG-INFO +269 -0
  5. chemur-0.1.0/README.md +225 -0
  6. chemur-0.1.0/cpp/chemur_core.cpp +365 -0
  7. chemur-0.1.0/docs/chemistry.md +68 -0
  8. chemur-0.1.0/docs/cli.md +138 -0
  9. chemur-0.1.0/pyproject.toml +132 -0
  10. chemur-0.1.0/src/chemur/__init__.py +34 -0
  11. chemur-0.1.0/src/chemur/__main__.py +5 -0
  12. chemur-0.1.0/src/chemur/_fallback_core.py +235 -0
  13. chemur-0.1.0/src/chemur/api.py +359 -0
  14. chemur-0.1.0/src/chemur/assignment.py +76 -0
  15. chemur-0.1.0/src/chemur/ccd.py +150 -0
  16. chemur-0.1.0/src/chemur/chemistry.py +557 -0
  17. chemur-0.1.0/src/chemur/cli.py +603 -0
  18. chemur-0.1.0/src/chemur/core.py +49 -0
  19. chemur-0.1.0/src/chemur/cosplif.py +1827 -0
  20. chemur-0.1.0/src/chemur/errors.py +15 -0
  21. chemur-0.1.0/src/chemur/features.py +441 -0
  22. chemur-0.1.0/src/chemur/ifm_config.py +239 -0
  23. chemur-0.1.0/src/chemur/ifm_matcher.py +2399 -0
  24. chemur-0.1.0/src/chemur/ifm_models.py +124 -0
  25. chemur-0.1.0/src/chemur/ifm_reporting.py +46 -0
  26. chemur-0.1.0/src/chemur/ifm_scoring.py +159 -0
  27. chemur-0.1.0/src/chemur/interaction_types.py +243 -0
  28. chemur-0.1.0/src/chemur/interactions.py +1184 -0
  29. chemur-0.1.0/src/chemur/models.py +364 -0
  30. chemur-0.1.0/src/chemur/parser.py +436 -0
  31. chemur-0.1.0/src/chemur/profile.py +230 -0
  32. chemur-0.1.0/src/chemur/profiles/__init__.py +2 -0
  33. chemur-0.1.0/src/chemur/profiles/default.yaml +184 -0
  34. chemur-0.1.0/src/chemur/protonation.py +81 -0
  35. chemur-0.1.0/src/chemur/scoring.py +241 -0
  36. chemur-0.1.0/src/chemur/sdf.py +267 -0
  37. chemur-0.1.0/src/chemur/trajectory.py +569 -0
  38. chemur-0.1.0/tests/test_assignment.py +70 -0
  39. chemur-0.1.0/tests/test_candidate_generation.py +179 -0
  40. chemur-0.1.0/tests/test_ccd.py +22 -0
  41. chemur-0.1.0/tests/test_cli_cutoffs.py +14 -0
  42. chemur-0.1.0/tests/test_cli_split_defaults.py +77 -0
  43. chemur-0.1.0/tests/test_core_geometry.py +84 -0
  44. chemur-0.1.0/tests/test_ligand_smiles.py +192 -0
  45. chemur-0.1.0/tests/test_new_interactions.py +682 -0
  46. chemur-0.1.0/tests/test_profile_consistency.py +118 -0
  47. chemur-0.1.0/tests/test_protonation.py +39 -0
  48. chemur-0.1.0/tests/test_scoring.py +138 -0
  49. chemur-0.1.0/tests/test_sdf_ligands.py +57 -0
  50. chemur-0.1.0/tests/test_split_by_ligand.py +44 -0
  51. chemur-0.1.0/tests/test_trajectory.py +109 -0
@@ -0,0 +1,29 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.so
4
+ *.pyd
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ build/
9
+ dist/
10
+ wheelhouse/
11
+ *.egg-info/
12
+ _skbuild/
13
+ test_data/
14
+ wheels/
15
+ *.whl
16
+ *.tar.gz
17
+ .venv/
18
+ venv/
19
+ .DS_Store
20
+ .idea/
21
+ .vscode/
22
+ .coverage
23
+ htmlcov/
24
+
25
+ # Manuscript material lives outside the repo (~/Documents/chemur_paper)
26
+ paper/
27
+
28
+ # Default --split-by-ligand-dir for `chemur analyze`; generated output, never source.
29
+ ligand_outputs/
@@ -0,0 +1,14 @@
1
+ cmake_minimum_required(VERSION 3.18)
2
+ project(chemur_core LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_CXX_EXTENSIONS OFF)
7
+
8
+ find_package(pybind11 CONFIG REQUIRED)
9
+
10
+ pybind11_add_module(_chemur_core cpp/chemur_core.cpp)
11
+ target_compile_features(_chemur_core PRIVATE cxx_std_17)
12
+
13
+ install(TARGETS _chemur_core LIBRARY DESTINATION chemur)
14
+
chemur-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aaron Sweeney
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.
chemur-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: chemur
3
+ Version: 0.1.0
4
+ Summary: Detect non-covalent interactions between proteins, nucleic acids, ligands and metals in PDB/mmCIF structures, with a Python API, a CLI, and a C++ geometry core.
5
+ Keywords: structural-biology,molecular-interactions,protein-ligand,protein-protein,nucleic-acids,hydrogen-bond,cheminformatics,mmcif
6
+ Author-Email: Aaron Sweeney <ucbtasw@ucl.ac.uk>
7
+ Maintainer-Email: Aaron Sweeney <ucbtasw@ucl.ac.uk>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: MacOS :: MacOS X
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: C++
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
22
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
23
+ Project-URL: Homepage, https://github.com/SweeneyAaron/chemur
24
+ Project-URL: Source, https://github.com/SweeneyAaron/chemur
25
+ Project-URL: Issues, https://github.com/SweeneyAaron/chemur/issues
26
+ Project-URL: ChimeraX plugin, https://github.com/SweeneyAaron/chimerax-chemur
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: dimorphite_dl>=2
29
+ Requires-Dist: gemmi>=0.6
30
+ Requires-Dist: numpy>=1.23
31
+ Requires-Dist: PyYAML>=6
32
+ Requires-Dist: rdkit>=2023.9
33
+ Provides-Extra: protonation
34
+ Provides-Extra: trajectory
35
+ Requires-Dist: MDAnalysis>=2.4; extra == "trajectory"
36
+ Provides-Extra: dataframe
37
+ Requires-Dist: pandas>=1.5; extra == "dataframe"
38
+ Provides-Extra: all
39
+ Requires-Dist: chemur[dataframe,protonation,trajectory]; extra == "all"
40
+ Provides-Extra: test
41
+ Requires-Dist: pytest>=7; extra == "test"
42
+ Requires-Dist: chemur[dataframe]; extra == "test"
43
+ Description-Content-Type: text/markdown
44
+
45
+ # Chemur
46
+
47
+ [![PyPI](https://img.shields.io/pypi/v/chemur.svg)](https://pypi.org/project/chemur/)
48
+ [![Python](https://img.shields.io/pypi/pyversions/chemur.svg)](https://pypi.org/project/chemur/)
49
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
50
+
51
+ Find the non-covalent interactions in a PDB or mmCIF structure — between proteins,
52
+ nucleic acids, ligands, metals and solvent, in any combination.
53
+
54
+ ```bash
55
+ pip install chemur
56
+ chemur analyze structure.cif --out interactions.json
57
+ ```
58
+
59
+ - **22 interaction types, each with defined geometry**
60
+ - **Any pair of components.** Protein–ligand, protein–protein, protein–DNA/RNA,
61
+ nucleic acid–ligand, ligand–ligand. Nothing is hard-coded to expect a receptor and
62
+ a small molecule.
63
+ - **Chemistry from templates, geometry from coordinates.** Ligands are typed from
64
+ SMILES, an SDF, or an automatic Chemical Component Dictionary lookup.
65
+ - **MD trajectories** — per-frame analysis with occupancy and time series.
66
+
67
+ A [ChimeraX plugin](https://github.com/SweeneyAaron/chimerax-chemur) builds on this
68
+ library for interactive 3D visualisation.
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ pip install chemur
74
+ ```
75
+
76
+ Wheels are published for CPython 3.10–3.13 on Linux x86_64, macOS (a single
77
+ universal2 build covering Intel and Apple Silicon) and Windows x86_64. Other
78
+ platforms build from the source distribution and need a C++17 compiler and CMake ≥ 3.18.
79
+
80
+ | Extra | Installs | Enables |
81
+ | --- | --- | --- |
82
+ | `trajectory` | `MDAnalysis` | reading trajectory files |
83
+ | `dataframe` | `pandas` | `AnalysisResult.to_dataframe()` |
84
+ | `all` | both | |
85
+
86
+ ```bash
87
+ pip install "chemur[all]"
88
+ ```
89
+
90
+ pH-range ligand protonation (`--protonate`) needs no extra — `dimorphite_dl` is a hard
91
+ dependency. It requires `rdkit<2026`, so a Chemur install is capped there too.
92
+
93
+ > `MDAnalysis` is LGPL-3.0-or-later (it relicensed from GPL at 2.8.0; the `>=2.4` floor
94
+ > still permits older GPL-licensed releases if you pin one). Chemur itself is MIT and
95
+ > neither bundles nor links it, but installing the `trajectory` extra adds
96
+ > copyleft-licensed code to your environment.
97
+
98
+ To confirm you got the compiled core rather than the fallback:
99
+
100
+ ```bash
101
+ python -c "from chemur.core import USING_CPP_CORE; print('C++ core:', USING_CPP_CORE)"
102
+ ```
103
+
104
+ `True` is expected. `False` means the pure-Python fallback loaded — correct results,
105
+ 10–100× slower. From a wheel that should never happen; from a source build it means
106
+ CMake or the compiler failed. Set `CHEMUR_FORCE_PYTHON_CORE=1` to select it deliberately.
107
+
108
+ ## Quick start
109
+
110
+ ```python
111
+ import chemur
112
+
113
+ result = chemur.analyze("structure.cif")
114
+
115
+ for interaction in result.interactions:
116
+ print(interaction.interaction_type, interaction.component_ids)
117
+
118
+ result.to_json("interactions.json")
119
+ ```
120
+
121
+ ```bash
122
+ chemur analyze structure.cif --out interactions.json --csv interactions.csv
123
+ ```
124
+
125
+ ## Any biomolecule, not just protein–ligand
126
+
127
+ `analyze` returns **every** interaction it finds in the structure. There is no receptor
128
+ or ligand role — you pick out the ones you want by component.
129
+
130
+ ```python
131
+ import chemur
132
+ from collections import Counter
133
+
134
+ result = chemur.analyze("complex.cif")
135
+
136
+ # What was found, and between what?
137
+ print(Counter(i.interaction_type for i in result.interactions))
138
+ ```
139
+
140
+ A protein–DNA interface, a protein–protein interface and a ligand binding site are all
141
+ the same query — filter on the components involved:
142
+
143
+ ```python
144
+ def between(result, a, b):
145
+ """Interactions with one atom in component `a` and the other in `b`."""
146
+ return [
147
+ i for i in result.interactions
148
+ if {a, b} <= set(i.component_ids)
149
+ ]
150
+
151
+ interface = between(result, "A:ARG:145", "B:DG:12") # protein side chain to a base
152
+ ```
153
+
154
+ Components are identified by `component_id`; `result.components` lists them all with
155
+ their names, so you can select a chain, a residue range, or a single ligand. Nucleic
156
+ acid phosphate backbones are perceived as anions, protein Arg/Lys as cations and
157
+ Asp/Glu as carboxylate anions, so salt bridges and cation–π across a protein–nucleic
158
+ acid interface are detected the same way as in a binding site.
159
+
160
+ ## What it detects
161
+
162
+ | Family | Types |
163
+ | --- | --- |
164
+ | Hydrogen bonding | `hbond`, `weak_hbond`, `hbond_pi`, `solvent_bridge`, `amide_bridge` |
165
+ | Electrostatic | `salt_bridge`, `metal_coordination`, `cation_pi`, `anion_pi`, `anion_aromatic_edge` |
166
+ | Stacking | `pipi_stack`, `aliphatic_pi_stack`, `aliphatic_stack`, `amide_pi`, `ch_pi` |
167
+ | σ-hole | `halogen_bond`, `chalcogen_bond`, `tetrel_bond`, `halogen_pi`, `chalcogen_pi` |
168
+ | Other | `n_pi_star`, `hydrophobic` |
169
+
170
+ `aliphatic_stack` (saturated–saturated ring stacking) ships disabled; enable it in the
171
+ profile. The σ-hole, stacking and n→π\* geometries follow
172
+ [Adhav & Saikrishnan, *ACS Omega* **2023**, 8, 22268](https://doi.org/10.1021/acsomega.3c00205).
173
+
174
+ Each type has its own geometric criteria, all adjustable — see
175
+ [docs/cli.md](docs/cli.md) for overriding cutoffs and [docs/chemistry.md](docs/chemistry.md)
176
+ for the chemical gates (such as why an ordinary Met sulfur cannot donate a chalcogen bond).
177
+
178
+ ## Ligand chemistry
179
+
180
+ Ligands need a chemical template so protonation, charge, aromaticity and
181
+ donor/acceptor status are correct. Chemur takes one from, in order of precedence:
182
+
183
+ ```bash
184
+ chemur analyze structure.cif --ligand-smiles LIG='CC(=O)N' # explicit SMILES
185
+ chemur analyze receptor.pdb --ligand-sdf docked.sdf # an SDF (coordinates too)
186
+ chemur analyze structure.cif # automatic CCD lookup
187
+ ```
188
+
189
+ The automatic lookup resolves each ligand by its component name against the RCSB
190
+ Chemical Component Dictionary. Add `--protonate` to set the protonation state for a pH
191
+ range with Dimorphite-DL, and `--debug` to print the SMILES actually used.
192
+
193
+ Full detail in [docs/cli.md](docs/cli.md).
194
+
195
+ ## Multiple ligands and docking output
196
+
197
+ Pass several ligands at once, or point at a directory of sdf files.
198
+
199
+ ```bash
200
+ chemur analyze receptor.pdb --ligand-sdf a.sdf --ligand-sdf b.sdf # repeatable
201
+ chemur analyze receptor.pdb --ligand-sdf-dir poses/ # a whole directory
202
+ ```
203
+
204
+ By default all supplied ligands are added to the structure and analysed **together**, as
205
+ one system — right for a cofactor plus a substrate, or two ligands sharing a pocket.
206
+
207
+ Docked poses are the opposite case: each is an alternative for the *same* site, so
208
+ analysing them together would stack overlapping copies into one pocket. Use `--batch` to
209
+ analyse each ligand independently against the same receptor:
210
+
211
+ ```bash
212
+ chemur analyze receptor.pdb --ligand-sdf-dir poses/ --batch --out all_poses.json
213
+ ```
214
+
215
+ Either way you also get one filtered file per ligand under `ligand_outputs/` — in batch
216
+ mode under a subdirectory per pose — while `--out` / `--csv` write the aggregate.
217
+
218
+ SDF coordinates are used directly, and ligand residues already present in the structure
219
+ are skipped so the SDF stays authoritative.
220
+
221
+ ## Trajectories
222
+
223
+ ```bash
224
+ pip install "chemur[trajectory]"
225
+ chemur trajectory topology.pdb trajectory.xtc --out frames.json
226
+ ```
227
+
228
+ Analyses each frame and reports per-interaction occupancy and time series.
229
+
230
+ ## Documentation
231
+
232
+ - [docs/cli.md](docs/cli.md) — full command-line reference
233
+ - [docs/chemistry.md](docs/chemistry.md) — templates, hydrogen policy, σ-hole activation, profiles
234
+
235
+ ## Development
236
+
237
+ ```bash
238
+ git clone https://github.com/SweeneyAaron/chemur
239
+ cd chemur
240
+
241
+ conda create -n chemur-dev -c conda-forge \
242
+ python=3.11 rdkit gemmi numpy pyyaml pandas pytest \
243
+ cmake ninja pybind11 scikit-build-core cxx-compiler
244
+ conda activate chemur-dev
245
+
246
+ pip install -e ".[test,all]" -v
247
+ pytest
248
+ ```
249
+
250
+ On macOS you need the Apple command line tools (`xcode-select --install`). If the build
251
+ fails with `'cstddef' file not found` they are incomplete — repair with
252
+ `sudo rm -rf /Library/Developer/CommandLineTools && xcode-select --install`.
253
+
254
+ This project uses a `src/` layout and has no `conftest.py` on purpose, so `pytest` always
255
+ exercises the *installed* package rather than the source tree. That is what makes the
256
+ wheel-level checks in CI meaningful.
257
+
258
+ ## License
259
+
260
+ MIT — see [LICENSE](LICENSE).
261
+
262
+ ## Citation
263
+
264
+ A manuscript is in preparation. Until then, please cite the repository:
265
+
266
+ ```
267
+ Sweeney, A, Genz, L, Topf, M. Chemur: biomolecular interaction detection.
268
+ https://github.com/SweeneyAaron/chemur
269
+ ```
chemur-0.1.0/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # Chemur
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/chemur.svg)](https://pypi.org/project/chemur/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/chemur.svg)](https://pypi.org/project/chemur/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ Find the non-covalent interactions in a PDB or mmCIF structure — between proteins,
8
+ nucleic acids, ligands, metals and solvent, in any combination.
9
+
10
+ ```bash
11
+ pip install chemur
12
+ chemur analyze structure.cif --out interactions.json
13
+ ```
14
+
15
+ - **22 interaction types, each with defined geometry**
16
+ - **Any pair of components.** Protein–ligand, protein–protein, protein–DNA/RNA,
17
+ nucleic acid–ligand, ligand–ligand. Nothing is hard-coded to expect a receptor and
18
+ a small molecule.
19
+ - **Chemistry from templates, geometry from coordinates.** Ligands are typed from
20
+ SMILES, an SDF, or an automatic Chemical Component Dictionary lookup.
21
+ - **MD trajectories** — per-frame analysis with occupancy and time series.
22
+
23
+ A [ChimeraX plugin](https://github.com/SweeneyAaron/chimerax-chemur) builds on this
24
+ library for interactive 3D visualisation.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install chemur
30
+ ```
31
+
32
+ Wheels are published for CPython 3.10–3.13 on Linux x86_64, macOS (a single
33
+ universal2 build covering Intel and Apple Silicon) and Windows x86_64. Other
34
+ platforms build from the source distribution and need a C++17 compiler and CMake ≥ 3.18.
35
+
36
+ | Extra | Installs | Enables |
37
+ | --- | --- | --- |
38
+ | `trajectory` | `MDAnalysis` | reading trajectory files |
39
+ | `dataframe` | `pandas` | `AnalysisResult.to_dataframe()` |
40
+ | `all` | both | |
41
+
42
+ ```bash
43
+ pip install "chemur[all]"
44
+ ```
45
+
46
+ pH-range ligand protonation (`--protonate`) needs no extra — `dimorphite_dl` is a hard
47
+ dependency. It requires `rdkit<2026`, so a Chemur install is capped there too.
48
+
49
+ > `MDAnalysis` is LGPL-3.0-or-later (it relicensed from GPL at 2.8.0; the `>=2.4` floor
50
+ > still permits older GPL-licensed releases if you pin one). Chemur itself is MIT and
51
+ > neither bundles nor links it, but installing the `trajectory` extra adds
52
+ > copyleft-licensed code to your environment.
53
+
54
+ To confirm you got the compiled core rather than the fallback:
55
+
56
+ ```bash
57
+ python -c "from chemur.core import USING_CPP_CORE; print('C++ core:', USING_CPP_CORE)"
58
+ ```
59
+
60
+ `True` is expected. `False` means the pure-Python fallback loaded — correct results,
61
+ 10–100× slower. From a wheel that should never happen; from a source build it means
62
+ CMake or the compiler failed. Set `CHEMUR_FORCE_PYTHON_CORE=1` to select it deliberately.
63
+
64
+ ## Quick start
65
+
66
+ ```python
67
+ import chemur
68
+
69
+ result = chemur.analyze("structure.cif")
70
+
71
+ for interaction in result.interactions:
72
+ print(interaction.interaction_type, interaction.component_ids)
73
+
74
+ result.to_json("interactions.json")
75
+ ```
76
+
77
+ ```bash
78
+ chemur analyze structure.cif --out interactions.json --csv interactions.csv
79
+ ```
80
+
81
+ ## Any biomolecule, not just protein–ligand
82
+
83
+ `analyze` returns **every** interaction it finds in the structure. There is no receptor
84
+ or ligand role — you pick out the ones you want by component.
85
+
86
+ ```python
87
+ import chemur
88
+ from collections import Counter
89
+
90
+ result = chemur.analyze("complex.cif")
91
+
92
+ # What was found, and between what?
93
+ print(Counter(i.interaction_type for i in result.interactions))
94
+ ```
95
+
96
+ A protein–DNA interface, a protein–protein interface and a ligand binding site are all
97
+ the same query — filter on the components involved:
98
+
99
+ ```python
100
+ def between(result, a, b):
101
+ """Interactions with one atom in component `a` and the other in `b`."""
102
+ return [
103
+ i for i in result.interactions
104
+ if {a, b} <= set(i.component_ids)
105
+ ]
106
+
107
+ interface = between(result, "A:ARG:145", "B:DG:12") # protein side chain to a base
108
+ ```
109
+
110
+ Components are identified by `component_id`; `result.components` lists them all with
111
+ their names, so you can select a chain, a residue range, or a single ligand. Nucleic
112
+ acid phosphate backbones are perceived as anions, protein Arg/Lys as cations and
113
+ Asp/Glu as carboxylate anions, so salt bridges and cation–π across a protein–nucleic
114
+ acid interface are detected the same way as in a binding site.
115
+
116
+ ## What it detects
117
+
118
+ | Family | Types |
119
+ | --- | --- |
120
+ | Hydrogen bonding | `hbond`, `weak_hbond`, `hbond_pi`, `solvent_bridge`, `amide_bridge` |
121
+ | Electrostatic | `salt_bridge`, `metal_coordination`, `cation_pi`, `anion_pi`, `anion_aromatic_edge` |
122
+ | Stacking | `pipi_stack`, `aliphatic_pi_stack`, `aliphatic_stack`, `amide_pi`, `ch_pi` |
123
+ | σ-hole | `halogen_bond`, `chalcogen_bond`, `tetrel_bond`, `halogen_pi`, `chalcogen_pi` |
124
+ | Other | `n_pi_star`, `hydrophobic` |
125
+
126
+ `aliphatic_stack` (saturated–saturated ring stacking) ships disabled; enable it in the
127
+ profile. The σ-hole, stacking and n→π\* geometries follow
128
+ [Adhav & Saikrishnan, *ACS Omega* **2023**, 8, 22268](https://doi.org/10.1021/acsomega.3c00205).
129
+
130
+ Each type has its own geometric criteria, all adjustable — see
131
+ [docs/cli.md](docs/cli.md) for overriding cutoffs and [docs/chemistry.md](docs/chemistry.md)
132
+ for the chemical gates (such as why an ordinary Met sulfur cannot donate a chalcogen bond).
133
+
134
+ ## Ligand chemistry
135
+
136
+ Ligands need a chemical template so protonation, charge, aromaticity and
137
+ donor/acceptor status are correct. Chemur takes one from, in order of precedence:
138
+
139
+ ```bash
140
+ chemur analyze structure.cif --ligand-smiles LIG='CC(=O)N' # explicit SMILES
141
+ chemur analyze receptor.pdb --ligand-sdf docked.sdf # an SDF (coordinates too)
142
+ chemur analyze structure.cif # automatic CCD lookup
143
+ ```
144
+
145
+ The automatic lookup resolves each ligand by its component name against the RCSB
146
+ Chemical Component Dictionary. Add `--protonate` to set the protonation state for a pH
147
+ range with Dimorphite-DL, and `--debug` to print the SMILES actually used.
148
+
149
+ Full detail in [docs/cli.md](docs/cli.md).
150
+
151
+ ## Multiple ligands and docking output
152
+
153
+ Pass several ligands at once, or point at a directory of sdf files.
154
+
155
+ ```bash
156
+ chemur analyze receptor.pdb --ligand-sdf a.sdf --ligand-sdf b.sdf # repeatable
157
+ chemur analyze receptor.pdb --ligand-sdf-dir poses/ # a whole directory
158
+ ```
159
+
160
+ By default all supplied ligands are added to the structure and analysed **together**, as
161
+ one system — right for a cofactor plus a substrate, or two ligands sharing a pocket.
162
+
163
+ Docked poses are the opposite case: each is an alternative for the *same* site, so
164
+ analysing them together would stack overlapping copies into one pocket. Use `--batch` to
165
+ analyse each ligand independently against the same receptor:
166
+
167
+ ```bash
168
+ chemur analyze receptor.pdb --ligand-sdf-dir poses/ --batch --out all_poses.json
169
+ ```
170
+
171
+ Either way you also get one filtered file per ligand under `ligand_outputs/` — in batch
172
+ mode under a subdirectory per pose — while `--out` / `--csv` write the aggregate.
173
+
174
+ SDF coordinates are used directly, and ligand residues already present in the structure
175
+ are skipped so the SDF stays authoritative.
176
+
177
+ ## Trajectories
178
+
179
+ ```bash
180
+ pip install "chemur[trajectory]"
181
+ chemur trajectory topology.pdb trajectory.xtc --out frames.json
182
+ ```
183
+
184
+ Analyses each frame and reports per-interaction occupancy and time series.
185
+
186
+ ## Documentation
187
+
188
+ - [docs/cli.md](docs/cli.md) — full command-line reference
189
+ - [docs/chemistry.md](docs/chemistry.md) — templates, hydrogen policy, σ-hole activation, profiles
190
+
191
+ ## Development
192
+
193
+ ```bash
194
+ git clone https://github.com/SweeneyAaron/chemur
195
+ cd chemur
196
+
197
+ conda create -n chemur-dev -c conda-forge \
198
+ python=3.11 rdkit gemmi numpy pyyaml pandas pytest \
199
+ cmake ninja pybind11 scikit-build-core cxx-compiler
200
+ conda activate chemur-dev
201
+
202
+ pip install -e ".[test,all]" -v
203
+ pytest
204
+ ```
205
+
206
+ On macOS you need the Apple command line tools (`xcode-select --install`). If the build
207
+ fails with `'cstddef' file not found` they are incomplete — repair with
208
+ `sudo rm -rf /Library/Developer/CommandLineTools && xcode-select --install`.
209
+
210
+ This project uses a `src/` layout and has no `conftest.py` on purpose, so `pytest` always
211
+ exercises the *installed* package rather than the source tree. That is what makes the
212
+ wheel-level checks in CI meaningful.
213
+
214
+ ## License
215
+
216
+ MIT — see [LICENSE](LICENSE).
217
+
218
+ ## Citation
219
+
220
+ A manuscript is in preparation. Until then, please cite the repository:
221
+
222
+ ```
223
+ Sweeney, A, Genz, L, Topf, M. Chemur: biomolecular interaction detection.
224
+ https://github.com/SweeneyAaron/chemur
225
+ ```