Changelog¶
All notable changes to OCDocker are documented here. The format follows Keep a Changelog, and the project adheres to Semantic Versioning.
0.16.0 - 2026-09-24¶
This release also covers the changes that were versioned 0.15.5 but never tagged or given an entry of their own.
Added¶
Binding pocket descriptors:
OCDocker/Pocket.pyadds aPocketclass that defines a pocket from a reference ligand (every standard receptor residue with a heavy atom withincutoffangstroms, 8.0 by default, of any reference-ligand heavy atom) and computes descriptors on those residues only: residue countscountA..countV,TotalAALength,countChain,SASA(per residue, in the context of the full receptor),GRAVY,Aromaticity,NetCharge(side chains only, Henderson-Hasselbalch at pH 7.4) and the side-chain hydrogen-bond donor/acceptor atoms lining the pocket. UnlikeReceptor, wherecount*count surface residues, here every pocket residue is counted. Descriptors are cached withto_json()/from_json_descriptors, which also record the cutoff, the reference ligand and the residue identifiers.OCDocker/DB/Models/Pockets.py: apocketstable referencingreceptorsthroughreceptor_id, withReceptors.pocketsas the reverse relationship. A receptor may hold several pockets, so thereceptorstable gains no column and existing databases need no migration;create_tables()creates the new table.scripts/compute_pocket_descriptors.py: computes and caches pocket descriptors, in parallel, for every receptor of a database that has a reference ligand (reference_ligand.pdbor.sdf), and writes a TSV report of the outcome per receptor.Ligand.load_mol()gained awrite_mol2: bool = Trueoption. WithFalse, loading a non-mol2 file no longer writes a derived.mol2next to it.OCScore_models/ships all 22 ablation-study configurations (#01 to #22), up from 4 in 0.15.4. Each is the replica with the highest validation BEDROC of its 5 seeds.manifest.json, the README table and the--configchoices ofexamples/25_ocscore_score_with_shipped_models.pylist them all.Workbench browser characterization tests (
tests/workbench/test_browser.py), run with Playwright;playwright>=1.61.0joins thedevextra, and its Chromium binary is installed separately withpython -m playwright install chromium.
Fixed¶
predict_from_export()scored raw, unstandardized features. DUDEz bundles never persisted a scaler under the defaultscaling_strategy="pdbbind_scaler", andpredict_from_export()did not forwardpdbbind_export_dirtoload_exported_model(), so every prediction bypassed the scaler the network was trained with. On the shipped configurations’ own held-out DUDEz test sets this gave near-random or anti-correlated rankings (#03: ROC-AUC 0.558).load_exported_model()now falls back to the linked PDBbind bundle’sscaler.joblib, andpredict_from_export()forwardspdbbind_export_dir(#03 after the fix: ROC-AUC 0.883).OCScore_models/shipped configuration #05 where the paper’s final candidates are #03, #09, #12 and #16; #16 is now included. The README no longer claims that every shipped configuration usesdudez_use_transfer: true(only #05 does).clean_for_dssp()rewrotereceptor.pdbon everyReceptor()construction, bumping its mtime, so under--rerun-triggers mtimeevery completed target sharing that receptor looked stale. It now skips files that already match its own output.Ligand.get_centroid()no longer writes a.mol2file as a side effect.Receptors prepared with Open Babel were written as flexible ligands:
convert_mols()wrote PDBQT with a torsion tree (ROOT/BRANCH/TORSDOF), which Vina rejects as a rigid receptor. This is the receptor path ofOpenBabelPreparationStrategy, used by Smina and Gnina and by the pipeline whenever MGLToolsprepare_receptor4fails.convert_mols()gainedrigid: bool = False, which for PDBQT output adds Open Babel’sr(no torsion tree) andc(combine all fragments into one molecule) write options. The strategy’s receptor path uses it, andget_receptor_command()now shows the matching-xr -xcflags.
Changed¶
Optuna is pinned to the 5.0 series:
optuna==5.0.*andoptuna-integration==5.0.*, withoptuna-dashboard>=0.21.0(environment.ymlmoves from Optuna 3.6.1, integration 4.0.0 and dashboard 0.16.2). The OCScore Optuna stages now build their TPE sampler withmultivariate=Trueandconstant_liar=Trueset explicitly (_build_tpe_sampler()), instead of inheriting the installed Optuna’s defaults, which changed in 5.0. Studies run under earlier Optuna versions will not reproduce trial for trial.scikit-learnis pinned to==1.8.*, the version the shipped OCScore scalers and calibrators were pickled with, andsqlalchemyis capped at<2.1until the database layer is tested on 2.1.environment.ymlis regenerated from an environment that passed the full test suite. It previously listed conda builds that no longer matched the working environment (e.g. scikit-learn 1.5.2, numpy 1.26.4, pandas 2.2.3); conda now supplies Python and the CUDA runtime, and pip supplies the rest at exact versions.DNNOptimizer.optimize()typesdirectionasLiteral["minimize", "maximize"], the valuesoptuna.create_study()accepts, andConsole.sessiongives IPython’sembeda callable type, so mypy passes against Optuna 5.0 and IPython 9.17.The Workbench dashboard script is split into modules (
app-core.js,app-jobs.js,app-comparison.js,app-plots.js,app-results.js,app-ablation-design.js,app-vs-design.js,app-workspace.js), still loaded as classic scripts in that order.environment.ymlno longer installs ODDT from Conda;scripts/vendor_oddt.shsupplies the required fork before OCDocker is installed.CHANGELOG.mdis the single changelog: the Sphinx page (docs/source/changelog.md) includes it directly, and notes that existed only in the oldchangelog.rstmoved into the 0.15.0 entry.The
Receptordocstring now states thatcountA..countVcount only surface-exposed residues, and drops the nonexistentcleanStructurePathparameter fromcount_surface_AA().
0.15.4 - 2026-08-06¶
Fixed¶
Ligand._try_embed_rdkit()could hang indefinitely (observed in production to exceed 40 minutes on a single call): RDKit’sAllChem.EmbedMolecule()can run for a very long time on molecules whose specified stereochemistry has no geometrically valid strict embedding (e.g. bridged/caged bicyclic bridgeheads in some algorithmically generated decoy SMILES), and a Python-level timeout cannot reliably interrupt it since the interpreter does not regain control until the C call returns. Each embedding attempt now runs in a disposable subprocess (_embed_worker/_embed_attempt_with_timeout) under a hard wall-clock timeout, guaranteeing termination regardless of what RDKit is doing internally. On timeout or failure this returnsFalserather than relaxing stereochemistry as a fallback, since this code has no way to know whether it is processing a benchmark decoy or a real candidate whose specified stereocenters matter.
0.15.3 - 2026-07-29¶
Added¶
LIT-PCBA external validation subset:
OCDocker/DB/LITPCBA.pyadds"litpcba"as a third archive type (alongsidedudez/pdbbind), wired throughConfig.py,Initialise.py,baseDB.py,Prepare.py,Digest.py, andDock.py.scripts/litpcba_validation_subset.pybuilds a leakage-checked, compute-tractable subset by deduplicating LIT-PCBA candidate receptors against local PDBbind/DUDEz via mmseqs2 sequence search, selecting the best-resolution surviving structure per target, and subsampling inactives with a deterministic floor/cap/ratio rule (13/15 targets kept, 2,699 actives, 131,216 sampled inactives).scripts/litpcba_build_archive.pyconverts that subset into the raw archive layoutPrepare/Dockexpect. Seedocs/litpcba_validation_subset.mdfor the full methodology and threshold rationale.Ligand.load_mol()/Ligand.__init__gained aclean: bool = Trueoption: strips known salts/counter-ions and keeps only the largest disconnected fragment, applied uniformly across all three load paths (RDKitMolobject, file load, SMILES load).
Fixed¶
scripts/litpcba_validation_subset.py’s receptor dedup only checked the single highest-identity mmseqs2 hit per candidate against both the identity and coverage thresholds; a true near-duplicate could hide behind an unrelated hit with higher identity but low coverage and go undetected. Now excludes a candidate if any hit clears both thresholds.Its
--refresh-resolution-cachestarted from an empty cache and wrote back only the current run’s candidates, which could silently shrink the shipped 129-structure resolution cache if run against a partial--litpcba-dir. Refresh now always merges into the existing cache.OCDocker.Rescoring.ODDT.run_oddtreloaded each ~1-3s gzipped RF/NN/PLEC scorer pickle for every single ligand; scorer models are now cached per worker thread (threading.local(), not shared/global, since ODDT’sscorer.set_protein()mutates the scorer object in place and Snakemake’s--force-use-threadsruns ligand jobs concurrently in one process).
Changed¶
OCScore.Analysis.Plotting.Statsandexamples/24_ocscore_bedroc_shortcut_risk_scatter.py: ablation eligibility for the shortcut-risk scatter now comes from a formal paired, Holm-corrected significance test rather than being inferred from whether a policy beats the reference on the plotted axis; the x-axis can now split across up to three wide gaps instead of one.
0.15.2 - 2026-07-13¶
Added¶
OCScore_models/: four pretrained, ready-to-score OCScore configurations from the DUDEz ablation study (#03,#05,#09,#12—#03is the configuration recommended in the manuscript). Each ships a DUDEzbest_model/bundle transfer-linked to its PDBbindbest_model/bundle, picked as the seed replica with the highest validation-split BEDROC (not test-split, to keep the selection consistent with the paper’s own methodology). SeeOCScore_models/README.mdfor the full metrics table and usage.examples/25_ocscore_score_with_shipped_models.py: scores a raw pipeline archive with any of the shippedOCScore_models/configurations viapredict_from_export, resolving both linked bundle paths fromOCScore_models/manifest.json.
Fixed¶
ocdocker ocscore validateandocdocker ocscore loadcould not open a DUDEz transfer-model bundle whose linked PDBbind export directory had moved (e.g. a bundle copied to another machine, as inOCScore_models/) — onlyscore/external-blindaccepted a--pdbbind-export-diroverride. Both commands, and the underlyingvalidate_export_bundle/load_exported_model, now accept it too.
Changed¶
CITATION.cffnow cites Zenodo’s concept DOI (10.5281/zenodo.21330171) instead of the version-specific DOI for 0.15.1. The concept DOI always resolves to the latest archived version, so it no longer needs to change on every release; the manuscript keeps citing the version-specific DOI for the release that produced its results.
0.15.1 - 2026-07-13¶
Fixed¶
.zenodo.jsonhad arelated_identifiersentry (the INPI registration number) using aschemevalue Zenodo’s metadata schema doesn’t recognize, which made every Zenodo archival attempt off the GitHub release fail with “Extra metadata load failed”. Removed the entry; the registration is still documented in the description field.Two
tests/cli/test_cli_utilities.py::test_cmd_script_*tests patchedOCDocker.CLI.common._preparse_global_args/_bootstrap_ocdocker_env, butOCDocker.CLI.scriptimports those names directly, so the patches never took effect and the tests silently exercised the real bootstrap path. It only passed locally because of a stray config file outside the repo; CI (no such file) failed withSystemExit(2). Patches now targetOCDocker.CLI.script, wherecmd_scriptactually looks the names up.
0.15.0 - 2026-07-12¶
The release that produced the results reported in the OCScore manuscript. It adds the MCP server, the OCScore Workbench dashboard, the ablation framework’s statistical layer and the SHAP shortcut-risk analysis.
Note on 0.14.0. That version number was carried in the source tree for a while but was never published: no tag, no PyPI release, no archived record. Everything it had accumulated since 0.13.5 ships here, in 0.15.0. There is no 0.14.0 to look for.
Added¶
MCP server (
OCDocker.MCP): exposes 19 tools through the Model Context Protocol, so a language model can design ablations, plan virtual-screening campaigns, submit runs and follow their progress in natural language. Includes Snakemake-backed execution and multi-element runs in virtual-screening mode.OCScore Workbench: a web dashboard for post-ablation analysis, with per-target and cross-validation plots, protocol-similarity clustering, an ablation designer, rank-plot and cluster legends with category filters, collapsible zones, a theme toggle, and UI state persisted to
localStorage.Statistical comparison of ablations: paired t-test by seed against the full model, with Holm-Bonferroni correction applied both per metric family and globally.
SHAP shortcut-risk analysis (
OCScore.Analysis.SHAP.Dominance): per-replica aggregation of family-level importance and of the single dominant feature, plusclassify_policies_by_shortcut_rule, which discards a configuration when it beats the reference model and concentrates more than a threshold share of its SHAP importance in one feature.Plotting (
OCScore.Analysis.Plotting.Stats,SHAP.DominancePlots): performance versus shortcut-risk scatter with an automatically detected broken axis and a shaded discard quadrant, and stacked SHAP family-composition bars. Both take their labels as parameters, so a caller can render them in another language without touching the library.Scheduler-friendly pipeline API and an
OCScoreLayoutmodule for workspace discovery.Ablation-protocol similarity analysis and visualisation.
Documentation on container usage (Docker, Podman, Singularity), database setup and external tools.
Changed¶
The Workbench API moved to FastAPI.
Ablation policies reworked:
receptor_onlyremoved, focused policies added.Licensing aligned across the tree (BSD-3-Clause; UFRJ, Artur Duque Rossi and Pedro Henrique Monteiro Torres as copyright holders).
Fixed¶
The protocol-similarity cluster legend was implemented but never wired into the render path, so its category filter never took effect; the dashboard now renders, binds and applies it.
The JavaScript syntax test stripped a hard-coded list of bootstrap calls from
app.js, which silently rotted whenever a new panel added top-level wiring. It now cuts the file at an explicit// --- BOOTSTRAP ---marker.py-mini-racer, used by the dashboard’s JavaScript syntax test, was imported without being declared; it is now part of thedevextra.
Details¶
The notes below were written during development of this release and kept in the Sphinx changelog page under “Unreleased”; they are preserved here in full.
Snakemake execution engine and structured progress for VS campaigns¶
vs_campaign jobs gained a second execution engine, chosen via
engine="shell" (default, unchanged) or engine="snakemake" on
plan_vs_campaign/run_job/plan_job and the Workbench VS tab’s
Batch mode: real Snakemake DAG orchestration (parallel via cores,
resumable via --rerun-incomplete) using a bundled config-driven
multi-sample Snakefile
(OCDocker.Workbench.Jobs.build_campaign_snakemake_command,
OCDocker/Workbench/Snakefiles/vs_campaign.smk — reads a samples
dict, so discovered receptor/ligand/box files never need to be relocated
into a fixed directory layout). Both engines also gained structured
per-sample progress, parsed from the job’s own log text: new
GET /api/jobs/{job_id}/campaign-progress endpoint
(OCDocker.Workbench.CampaignProgress), MCP tool
get_campaign_progress, and a live progress view in the Workbench Jobs
tab’s log panel. Also fixed: a shared outdir across every campaign row
now correctly nests per sample (<outdir>/<sample>) instead of every row
writing to the same directory.
VS campaign batches (multi-sample)¶
New vs_campaign job kind (OCDocker.Workbench.Jobs.build_campaign_script),
/api/vs-campaign* endpoints, and matching MCP tools
(get_vs_campaign_context, preview_vs_campaign, plan_vs_campaign)
run many receptor/ligand/box samples as one tracked job: discover an
input/{sample}/... layout (or hand-author a manifest), validate every
row, and launch the whole batch with a single run_job(confirm=True) — no
per-row confirmations, no dependency beyond OCDocker itself with the default
shell engine (see above for the optional Snakemake engine). The generated
shell script continues past a failing row and reports an aggregate pass/fail
count, so one bad sample doesn’t abort the rest. The Workbench VS tab gained
a “Single target” / “Batch” mode toggle exposing the same flow in the
browser.
VS/pipeline design assistant¶
New /api/vs-design* endpoints and matching MCP tools
(get_vs_design_context, preview_vs_design, plan_vs_design,
OCDocker.Workbench.VSDesign) mirror the existing ablation-design flow
for single-target vs/pipeline docking runs: discover receptor/ligand/box
candidates in a workspace, validate a draft selection (paths, engine names),
and preview the exact command before launching it through the existing
run_job/Jobs tab. Read-only and unauthenticated, like ablation design.
Covers one receptor/one ligand/one box per draft — for many samples in one
job, see “VS campaign batches” above.
MCP server for LLM-driven OCDocker orchestration¶
ocdocker mcp serve runs a Model Context Protocol server (OCDocker.MCP,
new mcp optional extra) over stdio for LLM clients such as Claude Code and
Claude Desktop. It is a thin adapter over a running ocdocker workbench serve
API: workspace inspection, ablation design preview/plan, protocol similarity,
and job listing/logs are always-available read tools; run_job and
cancel_job require the Workbench job bearer token and an explicit
confirm=True from the calling LLM, so a job is never launched from one
ambiguous instruction. See the optional dependencies page
(optional_dependencies).
Workbench API can launch and track jobs¶
ocdocker workbench serve now exposes /api/jobs* endpoints that launch,
list, poll, tail logs for, and cancel vs, pipeline, ocscore train,
and ocscore reduce runs as tracked local subprocesses
(OCDocker.Workbench.Jobs). Job state is persisted under
<served root>/.ocdocker-jobs/ and survives an API restart. Job-execute
endpoints require a bearer token, auto-generated on first run at
~/.config/ocdocker/workbench_token or set via OCDOCKER_WORKBENCH_TOKEN
(OCDocker.Workbench.Auth); all existing read-only inspection endpoints
are unchanged and remain unauthenticated.
Workbench API migrated to FastAPI¶
ocdocker workbench serve now runs on FastAPI/uvicorn instead of the stdlib
http.server, behind the new api optional extra
(pip install "ocdocker[api]"). All existing endpoints, response shapes, and
the /app browser dashboard are unchanged; the server now also exposes an
OpenAPI schema at /api/openapi.json. See the optional dependencies
(optional_dependencies) and usage (usage) pages.
Console and CLI separation¶
The interactive console lives in OCDocker.Console. Use ocdocker console
or python -m OCDocker.Console to start the REPL; built-in help and exit
commands are supported. The root OCDockerConsole.py wrapper was removed.
See the OCDocker.Console and usage (usage) pages.
Packaging and optional dependencies¶
OCDocker now ships a minimal core install; scientific, ML, and plotting stacks
are optional pip extras (docking, db, ml, analysis, workflow,
all, full, dev). See the optional dependencies page
(optional_dependencies) for the cheat sheet and full reference.
Formatting and readability¶
Priority config and Python modules use expanded multiline formatting (line length
120, one dependency per line in pyproject.toml). Long config defaults such as
reference_column_order live in module-level constants in OCDocker/Config.py.
See the development page (development) for contributor formatting rules.
OCScore feature-reduction API¶
Added a granular feature-reduction API in
OCDocker.OCScore.Utils.FeatureReduction for descriptor datasets. The new API
keeps feature-reduction behavior in reusable Python functions and dataclasses,
with run_feature_reduction_protocol provided only as an orchestration helper.
Highlights:
descriptor block detection for receptor, ligand, and scoring-function columns
Ligand/Receptor descriptor metadata support with configurable pattern fallback
missing-row removal with row-level and block-level reports before data loss
block-wise constant, near-constant, duplicate, and correlation filtering
cross-block correlation and Ridge CV predictability diagnostics
opt-in parallel Ridge CV diagnostics through
CrossBlockDiagnosticsConfig.n_jobsopt-in orchestration progress logging through
FeatureReductionConfig.verbosedisabled-by-default conservative cross-block filtering
reproducibility protocol and stable report filenames
Compatibility and rewiring:
Existing OCScore training, DNN, autoencoder, SHAP, and downstream evaluation paths are not automatically rewired to call this API.
OCDocker.OCScore.Utils.IO.load_datais not changed; the new orchestration helper reads raw CSV input directly so missing rows can be reported before removal.This is additive public API. If released publicly, it fits a minor version bump rather than a major version bump.