core-score 0.4.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.

Potentially problematic release.


This version of core-score might be problematic. Click here for more details.

Files changed (40) hide show
  1. core_score-0.4.0/LICENSE +28 -0
  2. core_score-0.4.0/PKG-INFO +382 -0
  3. core_score-0.4.0/README.md +356 -0
  4. core_score-0.4.0/pyproject.toml +42 -0
  5. core_score-0.4.0/setup.cfg +4 -0
  6. core_score-0.4.0/src/core_score/__init__.py +8 -0
  7. core_score-0.4.0/src/core_score/__main__.py +3 -0
  8. core_score-0.4.0/src/core_score/analysis/__init__.py +1 -0
  9. core_score-0.4.0/src/core_score/analysis/core.py +61 -0
  10. core_score-0.4.0/src/core_score/analysis/ost.py +79 -0
  11. core_score-0.4.0/src/core_score/analysis/pairs.py +119 -0
  12. core_score-0.4.0/src/core_score/analysis/posebusters.py +65 -0
  13. core_score-0.4.0/src/core_score/analysis/reference.py +59 -0
  14. core_score-0.4.0/src/core_score/cli.py +90 -0
  15. core_score-0.4.0/src/core_score/inputs/__init__.py +1 -0
  16. core_score-0.4.0/src/core_score/inputs/inputs.py +113 -0
  17. core_score-0.4.0/src/core_score/inputs/make_csv.py +209 -0
  18. core_score-0.4.0/src/core_score/inputs/prepare.py +108 -0
  19. core_score-0.4.0/src/core_score/inputs/sites.py +115 -0
  20. core_score-0.4.0/src/core_score/inputs/structures.py +357 -0
  21. core_score-0.4.0/src/core_score/outputs/__init__.py +1 -0
  22. core_score-0.4.0/src/core_score/outputs/files.py +63 -0
  23. core_score-0.4.0/src/core_score/outputs/plot.py +77 -0
  24. core_score-0.4.0/src/core_score/outputs/report.py +104 -0
  25. core_score-0.4.0/src/core_score/pipeline.py +102 -0
  26. core_score-0.4.0/src/core_score.egg-info/PKG-INFO +382 -0
  27. core_score-0.4.0/src/core_score.egg-info/SOURCES.txt +38 -0
  28. core_score-0.4.0/src/core_score.egg-info/dependency_links.txt +1 -0
  29. core_score-0.4.0/src/core_score.egg-info/entry_points.txt +3 -0
  30. core_score-0.4.0/src/core_score.egg-info/requires.txt +12 -0
  31. core_score-0.4.0/src/core_score.egg-info/top_level.txt +1 -0
  32. core_score-0.4.0/tests/test_chemistry.py +178 -0
  33. core_score-0.4.0/tests/test_cli_entrypoint.py +49 -0
  34. core_score-0.4.0/tests/test_invariants.py +155 -0
  35. core_score-0.4.0/tests/test_make_csv.py +89 -0
  36. core_score-0.4.0/tests/test_radius.py +81 -0
  37. core_score-0.4.0/tests/test_site_preparation.py +67 -0
  38. core_score-0.4.0/tests/test_sites.py +9 -0
  39. core_score-0.4.0/tests/test_workflow.py +264 -0
  40. core_score-0.4.0/tests/test_workflow_inputs.py +108 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Henry Broster
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,382 @@
1
+ Metadata-Version: 2.4
2
+ Name: core-score
3
+ Version: 0.4.0
4
+ Summary: CORE Score: agreement within an ensemble of predicted protein-ligand poses
5
+ Author: Henry Broster
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Repository, https://github.com/James-Broster/core-score
8
+ Keywords: co-folding,protein-ligand,structure prediction,confidence,AlphaFold3
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy>=2
16
+ Requires-Dist: scipy>=1.13
17
+ Requires-Dist: gemmi<0.8,>=0.7.5
18
+ Requires-Dist: rdkit>=2025.3
19
+ Requires-Dist: pandas>=2.2
20
+ Requires-Dist: posebusters<0.7,>=0.6.5
21
+ Provides-Extra: plot
22
+ Requires-Dist: matplotlib>=3.8; extra == "plot"
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest>=8; extra == "test"
25
+ Dynamic: license-file
26
+
27
+ # CORE Score
28
+
29
+ A Python package for prediction-only ensemble agreement. It prepares protein–ligand
30
+ structures, runs PoseBusters `dock` checks, compares every pair of poses with
31
+ OpenStructure, and calculates CORE from the complete pairwise ligand-RMSD matrix.
32
+ Experimental references are optional and never enter the CORE calculation.
33
+
34
+ See [validation](https://github.com/James-Broster/core-score/blob/main/docs/validation.md) for what has been checked.
35
+
36
+ ## CORE Score
37
+
38
+ For each pose *i* in an ensemble of *N* poses, δ*ᵢ,ₘ* is the ligand RMSD to its *m*th
39
+ closest other pose, for its three closest other poses. Each distance is capped at a
40
+ threshold *T* and divided by *T*, so a pose reproduced exactly by its three closest poses
41
+ has penalty 0 and a pose with no other pose within *T* has penalty 1. CORE is one minus
42
+ the mean penalty.
43
+
44
+ ```text
45
+ S = 1 − (1/3N) Σᵢ Σₘ min(δᵢ,ₘ, T) / T
46
+ ```
47
+
48
+ Equivalently S = f (1 − ν/T), where f is the fraction of closest-pose distances δᵢ,ₘ
49
+ within T and ν is the mean of those distances. Using three closest poses means a pose
50
+ scores highly only when at least four poses agree closely. The default T is 2 Å, the RMSD
51
+ threshold for a correct prediction. CORE uses every pose in the ensemble, including
52
+ poses that fail PoseBusters checks, because they still sample the model's output
53
+ distribution. Validity filtering belongs downstream, for example when choosing the
54
+ top-ranked prediction.
55
+
56
+ CORE needs at least four poses per ensemble, so that every pose has three others to be
57
+ scored against. An ensemble with fewer is rejected before anything runs.
58
+
59
+ Each pair of poses is compared once with OpenStructure. Poses are ordered by path, and
60
+ the earlier pose of each pair is the OST model, so the same poses always give the same
61
+ scores whatever order they are listed in.
62
+
63
+ ## Install
64
+
65
+ CORE Score needs OpenStructure 2.12, which is installed with conda. Then install the
66
+ package with pip.
67
+
68
+ ```bash
69
+ conda create -n core-score -c conda-forge -c bioconda python=3.12 openstructure=2.12
70
+ conda activate core-score
71
+ pip install "core-score[plot]"
72
+ core-score --help
73
+ ```
74
+
75
+ `plot` adds matplotlib for `--plot`. Plain `pip install core-score` installs the
76
+ pipeline alone. The package is `core_score`. It installs two commands, `core-score` and
77
+ `core-score-csv`.
78
+
79
+ To work on the package itself, clone the repository and use its environment, which
80
+ installs it in editable mode with the tests.
81
+
82
+ ```bash
83
+ conda env create -f environment.yml
84
+ conda activate core-score
85
+ ```
86
+
87
+ The conda solve has been checked on macOS ARM. Other platforms need an available
88
+ OpenStructure 2.12 build. Dependencies track current releases, including PoseBusters
89
+ 0.6 and pandas 3. OpenStructure is an external executable. `ost` is launched from PATH
90
+ and finds its chemical library itself or through `OST_COMPOUNDS_CHEMLIB`.
91
+
92
+ ## What you give, what you get
93
+
94
+ | You give | You get |
95
+ | --- | --- |
96
+ | At least four predicted structures per ensemble, and the ligand's SMILES | CORE Score per ensemble, and PoseBusters validity per pose |
97
+ | \+ a confidence per pose (read automatically by `core-score-csv`) | \+ the top-ranked pose, the PoseBusters-valid pose with the highest confidence |
98
+ | \+ a ground-truth structure per ensemble | \+ ligand RMSD, lDDT-PLI and whether each pose is correct, and whether the top-ranked pose is |
99
+
100
+ An ensemble is a set of predictions of the same protein–ligand system to compare
101
+ together, usually every seed and sample one model made for one target. Keep different
102
+ models in separate ensembles to score them separately.
103
+
104
+ ## Walkthrough
105
+
106
+ **1. Put each ensemble's predictions in a folder named for it.** Leave the files exactly
107
+ as the model wrote them. Any folders inside are searched.
108
+
109
+ ```text
110
+ predictions/
111
+ 7ly6/
112
+ seed-1_sample-0/7ly6_seed-1_sample-0_model.cif
113
+ seed-1_sample-1/7ly6_seed-1_sample-1_model.cif
114
+ ...
115
+ 7uy4/
116
+ ...
117
+ ```
118
+
119
+ **2. Write each ensemble's ligand SMILES in a CSV.**
120
+
121
+ ```text
122
+ ensemble,smiles
123
+ 7ly6,Cc1cc2nc3c(=O)[nH]c(=O)nc-3n(C[C@H](O)[C@H](O)[C@H](O)COP(=O)(O)O)c2cc1C
124
+ 7uy4,CN[C@@H]1[C@H](O)[C@H](NC)[C@H]2O[C@]3(O)[C@H](O[C@@H]2[C@H]1O)O[C@H](C)CC3(O)O
125
+ ```
126
+
127
+ **3. Make the input CSV.**
128
+
129
+ ```bash
130
+ core-score-csv --predictions predictions/ --smiles smiles.csv --output inputs.csv
131
+ ```
132
+
133
+ This finds every structure, lists a structure found twice with the same coordinates
134
+ once (AlphaFold3 writes a copy of its top model beside the seed folders), and reads each
135
+ pose's confidence from the file the model wrote beside it. It stops and lists every
136
+ problem, such as an ensemble without a SMILES or with fewer than four structures. Add
137
+ `--references references/`, a folder holding `<ensemble>.cif` or `<ensemble>.pdb`, to
138
+ evaluate the predictions against ground truth.
139
+
140
+ The confidence is the ligand's ipTM as in Runs N' Poses, the mean chain-pair ipTM between
141
+ the ligand chain and each receptor chain, also averaged over the ligand's copies when it
142
+ has several. It is read from these files.
143
+
144
+ | Model | Structure | Confidence file |
145
+ | --- | --- | --- |
146
+ | AlphaFold3 | `*model.cif` | `*summary_confidences.json` |
147
+ | Boltz | `<name>_model_<k>.cif` | `confidence_<name>_model_<k>.json` |
148
+ | Chai-1 | `pred.model_idx_<k>.cif` | `scores.model_idx_<k>.npz` |
149
+ | Protenix | `<name>_sample_<k>.cif` | `<name>_summary_confidence_sample_<k>.json` |
150
+ | OpenFold3 | `*model.cif` | `*confidences_aggregated.json` |
151
+
152
+ For any other method, the confidence column is left out and you can add your own.
153
+
154
+ **4. Run CORE Score.**
155
+
156
+ ```bash
157
+ core-score inputs.csv --out results/ --workers 8
158
+ ```
159
+
160
+ **5. Read `results/core_scores.csv`**, one row per ensemble, highest CORE first, and
161
+ `results/poses.csv`, one row per pose. [Outputs](#outputs) describes every column.
162
+
163
+ ## The input CSV
164
+
165
+ `core-score-csv` writes this file, and you can also write it yourself. It needs three
166
+ columns. A row names one structure, or many with a wildcard.
167
+
168
+ ```text
169
+ ensemble,smiles,poses
170
+ target1,CC(=O)Nc1ccc(O)cc1,predictions/target1/*.cif
171
+ ```
172
+
173
+ | Column | Meaning |
174
+ | --- | --- |
175
+ | `ensemble` | The ensemble's name. Rows with the same name form one ensemble. |
176
+ | `smiles` | The ligand's SMILES, the same on every row of an ensemble. |
177
+ | `poses` | A structure file, or a wildcard matching several (`**` searches folders inside). PDB, CIF and mmCIF, one model per file. |
178
+ | `confidence` | Optional. The model's own score for the pose, higher meaning more confident. One pose per row, and given for every pose of an ensemble or for none. |
179
+ | `reference` | Optional. The ground-truth structure, the same on every row of an ensemble. |
180
+ | `ligand_chain` | Optional. The ligand's chain, or chains separated by spaces for several identical copies. |
181
+ | `ligand_sdf` | Optional. The ligand as an SDF in the structure's frame, for tools such as docking that write it separately. One pose per row. |
182
+
183
+ Paths are relative to the CSV. Pose IDs are the structure paths relative to the CSV.
184
+ See [the example](https://github.com/James-Broster/core-score/blob/main/examples/inputs.csv), which uses every column a CSV usually needs.
185
+ Coordinates are never aligned or changed.
186
+
187
+ The ligand is found in each structure by its SMILES heavy-atom composition, and
188
+ `ligand_chain` narrows that down. Missing ligands are reported, not guessed. Composition
189
+ only selects residues. Preparation separately verifies the chemistry against the SMILES.
190
+
191
+ Structure files given directly form one ensemble.
192
+
193
+ ```bash
194
+ core-score poses/*.cif --smiles 'CCO' --out results/
195
+ ```
196
+
197
+ The same runs from Python:
198
+
199
+ ```python
200
+ import core_score
201
+
202
+ core_score.run("inputs.csv", "results/", workers=8)
203
+ ```
204
+
205
+ `core_score.score(matrix, threshold=2.0)` scores a pairwise RMSD matrix you have
206
+ computed yourself. `neighbours=3` is the number of closest poses each pose is scored against.
207
+
208
+ ## Options
209
+
210
+ | Option | Meaning |
211
+ | --- | --- |
212
+ | `--workers N` | At most N simultaneous OST comparisons (default 1). Each OST process uses one numerical-library thread. |
213
+ | `--timeout SECONDS` | Limit per OST call. Default is no limit. |
214
+ | `--rmsd-thresholds 0.5 1 2` | Thresholds T in Å. CORE is calculated at each from the same matrix, and a pose is correct at T when its RMSD to the reference is below T and its lDDT-PLI above 0.8. Default 2. |
215
+ | `--core-cutoffs 0.5 0.7 0.9` | CORE cutoffs to report against. A complex at or above a cutoff is above it. Default 0.5, 0.6, 0.7, 0.8 and 0.9. |
216
+ | `--no-radius-fallback` | Disable the 4 → 6 → 8 Å binding-site recovery. |
217
+ | `--skip-posebusters` | Do not run PoseBusters, which runs by default. CORE is unaffected, but there is no top-ranked pose. |
218
+ | `--plot` | Also save `core_scores.png`, a dot per complex ordered by CORE Score. Needs matplotlib. |
219
+ | `--smiles`, `--ligand-chain`, `--reference` | The ligand's SMILES and chains, and the ground truth, for structure files given directly. A CSV sets its own. |
220
+
221
+ ## PoseBusters
222
+
223
+ PoseBusters runs once per selected ligand copy against its predicted environment,
224
+ using the official API with `config='dock'`. A pose is valid only if every copy passes
225
+ every dock binary check. If none fail but any are unknown, the pose is unknown,
226
+ never valid. Missing chemical information is unknown, not a pass. Each check's result
227
+ for a pose combines its copies the same way. No pose is removed.
228
+
229
+ ## Binding-site recovery
230
+
231
+ When OST cannot score a pair because of a binding-site failure, the comparison is
232
+ retried at 6 Å and then 8 Å. Graph errors and general failures are not retried. The
233
+ ligand and pocket RMSD can come from the larger radius, but lDDT-PLI always stays
234
+ from the original 4 Å call. The standard RMSD, chosen radius and every attempt are
235
+ recorded.
236
+
237
+ ## Multiple identical copies
238
+
239
+ When each prediction contains several copies of the ligand, every pose is superposed
240
+ onto the anchor pose by OST's rigid protein alignment. Constrained k-means then gives
241
+ each copy a site label consistent across the ensemble, and each site gets its own
242
+ matrix and CORE Score. Confidence and ground truth are not used. Different copy
243
+ counts or alignment failures block the ensemble and are reported. Site labels are
244
+ arbitrary and do not prove biological correspondence. Transforms, costs and the
245
+ anchor are saved in `sites.json`. The sites of ensemble `7ly6` are reported as
246
+ `7ly6_site_1`, `7ly6_site_2` and so on.
247
+
248
+ ## Identical chains
249
+
250
+ OpenStructure treats chains of different mmCIF entities as different molecules when it
251
+ maps one structure's chains onto another's. AlphaFold3 writes an entity per chain listed
252
+ in its input, so the two chains of a homodimer could never swap places and pockets
253
+ between them would be scored wrongly. Before comparison, polymer chains with the same
254
+ sequence are given one entity. Only entity IDs change.
255
+
256
+ ## Reference evaluation
257
+
258
+ ```bash
259
+ core-score inputs.csv --out results/ --rmsd-thresholds 0.5 1 1.5 2
260
+ core-score poses/*.cif --smiles 'CCO' --reference experimental.cif --out results/
261
+ ```
262
+
263
+ Predictions are compared with the reference wherever their ensemble has one, set by the
264
+ `reference` column or by `--reference`. Each prediction is compared with OST's
265
+ `compare-ligand-structures`, giving the ligand RMSD after binding-site superposition and
266
+ lDDT-PLI. A pose is **correct** at T when its RMSD is below T and its lDDT-PLI is above
267
+ 0.8, the definition used in the paper. A multi-copy comparison has no label unless OST
268
+ assigns exactly one pair. A failed comparison, or one missing either score, is unknown,
269
+ never wrong.
270
+
271
+ ## Top-ranked pose
272
+
273
+ When confidences are given, the top-ranked pose of each complex is its
274
+ PoseBusters-valid pose with the highest confidence, as in the paper. Ties go to the
275
+ earlier pose. A complex with no valid pose, no confidences, or PoseBusters skipped has
276
+ no top pose, and `note` in `core_scores.csv` says why.
277
+
278
+ ## Running
279
+
280
+ Every run loads and checks the inputs, prepares structures, runs PoseBusters, assigns
281
+ sites where needed, compares every pair, calculates CORE, evaluates references when
282
+ given, and writes the report. Each run is fresh and needs a new or empty
283
+ output directory. A missing or failed comparison prevents that ensemble's CORE
284
+ Score. Nothing is averaged over a subset or filled in. Other ensembles carry on, and
285
+ their results and diagnostics are saved. The exit code is 0 when everything
286
+ completed, 1 when anything is incomplete and 2 for invalid input.
287
+
288
+ ## Outputs
289
+
290
+ A complex is one ensemble, or one ligand site of a multi-copy ensemble.
291
+
292
+ ```text
293
+ results/
294
+ core_scores.csv one row per complex
295
+ poses.csv one row per pose
296
+ core_scores.png every complex ordered by CORE Score, with --plot
297
+ details/ the evidence behind them
298
+ ```
299
+
300
+ **`core_scores.csv`** has one row per complex and T, highest CORE first and unscored
301
+ complexes last.
302
+
303
+ | Column | Meaning |
304
+ | --- | --- |
305
+ | `ensemble` | The complex, either the ensemble's name or `<ensemble>_site_<n>` for each site of a ligand with several copies. |
306
+ | `threshold_angstrom`, `core_score` | T and CORE at T. Blank when CORE could not be calculated. |
307
+ | `above_0.5` … `above_0.9` | True when CORE ≥ that cutoff, one column per cutoff. |
308
+ | `pose_count`, `valid_pose_count` | Poses in the complex and how many are PoseBusters-valid. |
309
+ | `top_pose`, `top_pose_confidence` | The top-ranked pose, when confidences are given. |
310
+ | `fraction_correct`, `top_pose_correct` | With references, the fraction of poses correct at T and whether the top pose is. |
311
+ | `note` | Why CORE or the top pose is missing. |
312
+
313
+ **`poses.csv`** has one row per pose, in path order.
314
+
315
+ | Column | Meaning |
316
+ | --- | --- |
317
+ | `ensemble`, `pose_id` | The pose. |
318
+ | `confidence` | The model's own score, when confidences are given. |
319
+ | `pb_valid` | True when PoseBusters passed every dock check on every ligand copy. Which checks failed is in `details/posebusters.json`. |
320
+ | `rmsd`, `lddt_pli` | To the reference, when there is one. |
321
+ | `correct_2A` | Whether the pose is correct at T, RMSD below T and lDDT-PLI above 0.8, one column per T. |
322
+ | `note` | Why a result is missing. |
323
+
324
+ A blank is unknown, never a pass or a failure.
325
+
326
+ **`core_scores.png`**, with `--plot`, is a dot per complex, ordered by CORE Score, one PNG
327
+ per T, with a dashed line at each cutoff saying how many complexes reach it. It is set in
328
+ Optima where that font is installed, otherwise the nearest humanist sans the machine has.
329
+ It needs matplotlib, from `pip install '.[plot]'`.
330
+
331
+ **`details/`** holds the evidence behind those files.
332
+
333
+ | File | Contents |
334
+ | --- | --- |
335
+ | `pairs.csv` | Ligand RMSD, pocket backbone RMSD, pocket lDDT and lDDT-PLI per pair, with radius recovery fields |
336
+ | `ligand_details.jsonl` | Raw OST results, commands and output for every pair |
337
+ | `posebusters.json` | PoseBusters validity, every dock check and every raw result per pose and copy |
338
+ | `reference_details.json`, `references/` | Raw reference comparisons and prepared references |
339
+ | `prepared_manifest.json`, `prepared/` | Prepared structures, SDFs and receptors |
340
+ | `sites.json` | Site assignments for multi-copy ensembles |
341
+ | `run.json`, `status.json` | Settings and completion counts |
342
+
343
+ Every failure records its stage, error and traceback, plus any evidence such as the
344
+ OST command and output.
345
+
346
+ ## Code layout
347
+
348
+ `pipeline.py` runs the steps in order. Each folder holds one phase of the run.
349
+
350
+ ```text
351
+ src/core_score/
352
+ cli.py the core-score command
353
+ pipeline.py run() and execute(): every step in order
354
+ inputs/
355
+ make_csv.py the core-score-csv command, which writes the input CSV
356
+ inputs.py load and check the CSV or structure inputs
357
+ structures.py read structure records, select ligands, write ligand SDFs
358
+ prepare.py prepare every pose for OST and PoseBusters
359
+ sites.py one ensemble per ligand site
360
+ analysis/
361
+ posebusters.py dock checks for every pose
362
+ ost.py every OpenStructure call
363
+ pairs.py every pair of poses, with binding-site recovery
364
+ core.py RMSD matrix and CORE per threshold
365
+ reference.py optional comparison with the ground truth
366
+ outputs/
367
+ report.py core_scores.csv and poses.csv
368
+ plot.py core_scores.png, with --plot
369
+ files.py output writers, failure records, output folders
370
+ ```
371
+
372
+ [The code map](https://github.com/James-Broster/core-score/blob/main/docs/code-map.md) draws the whole run, then each stage, function by
373
+ function, and lists every check.
374
+
375
+ ## Tests
376
+
377
+ ```bash
378
+ python -m pytest
379
+ ```
380
+
381
+ Real OST tests are separate because they need OST and structure fixtures. See
382
+ [validation](https://github.com/James-Broster/core-score/blob/main/docs/validation.md).