Skip to content

feat(thermo,chem): unified thermodynamics + cheminformatics facades (equilibrator, dGPredictor, molGPK/OPAM2)#47

Open
VibhavSetlur wants to merge 7 commits into
cshenry:mainfrom
VibhavSetlur:feature/thermo-cheminformatics-clean
Open

feat(thermo,chem): unified thermodynamics + cheminformatics facades (equilibrator, dGPredictor, molGPK/OPAM2)#47
VibhavSetlur wants to merge 7 commits into
cshenry:mainfrom
VibhavSetlur:feature/thermo-cheminformatics-clean

Conversation

@VibhavSetlur

Copy link
Copy Markdown

Summary

Adds a unified predictive-thermodynamics and cheminformatics network-expansion feature to KBUtilLib, exposing equilibrator, dGPredictor (Andrew's fork), and molGPK/OPAM2 (Andrew's MSDB fine-tuned fork) under one install and one API for KBase agent skills, as requested by Dr. Henry.

What's in this PR

New: Predictive Thermodynamics (kbu.predictive_thermo)

  • Pluggable backend dispatch: equilibrator, dGPredictor, molGPK/OPAM2, ModelSEED DB baked values, ModelSEED lookup
  • Expanded eQuilibrator wired to Andrew's ModelSEED Database fork (~9k more compounds)
  • dGPredictor backend (subprocess-isolated, Andrew's freiburgermsu/dGPredictor master)
  • molGPK/OPAM2 backend (Andrew's freiburgermsu/OPAM2, fine-tuned on MSDB) — returns pKa + predominant ion at given pH
  • Machine-readable units field (kJ/mol) on all estimate dataclasses
  • Graceful degradation: each backend reports availability; facade dispatches to best available

New: Cheminformatics Network Expansion (kbu.network_expansion)

  • Pickaxe backend via Tyo-NU MINE-Database
  • RetroRules backend
  • Thermodynamic feasibility filtering wired to the thermo facade

Install

  • pyproject.toml optional [thermo] extra
  • dependencies.yaml source-checkout resolver for dGPredictor / OPAM2 / MINE-Database
  • environment.yml — pinned conda env (rdkit ≥ 2022.03, openbabel ≥ 3.1.1, pytorch ≥ 2.0, torch-geometric) for reproducible one-env setup
  • config.yaml — runtime path config for backend repos

API surface

kbu.predictive_thermo.reaction_dg_prime(rxn_id, ph, ionic_strength)   # -> ReactionThermoEstimate (units=kJ/mol)
kbu.predictive_thermo.compound_dgf(cpd_id)                             # -> CompoundThermoEstimate (units=kJ/mol)
kbu.predictive_thermo.compound_microspecies(smiles, ph)                # -> CompoundThermoEstimate (pKa + major microspecies)
kbu.predictive_thermo.backend_status()                                 # -> dict of backend availability
kbu.network_expansion.expand(seed_compounds, ...)                      # -> reaction network
kbu.thermo                                                             # legacy ModelSEED-lookup ThermoUtils (unchanged)
kbu.chem                                                               # alias for kbu.network_expansion

Fixes included

  • thermo_utils.py: fix stored-reaction deltaG bug (guarded on delta_g but read deltag)
  • INSTALL.md: fix broken dGPredictor model-rebuild reference → retrain_modelseed.py inside the dGPredictor repo
  • test_ms_biochem_deltag.py: rewritten against relocated ThermoUtils API (was testing MSBiochemUtils methods that moved)

Tests

  • 65 pass / 7 skip (gated live-backend tests skip cleanly when repos/env-vars absent)
  • tests/test_predictive_thermo.py (39 tests — all backends, units field, molGPK pH sweep)
  • tests/test_network_expansion.py (26 tests)
  • tests/test_ms_biochem_deltag.py (rewritten, 17 tests — ThermoUtils deltaG API)

Known deferred items (follow-up)

  • dGPredictor currently accepts ModelSEED IDs only (not raw SMILES/InChI); SMILES→ID resolver deferred
  • molGPK accepts SMILES/InChI only (not cpd IDs); ID→SMILES resolver deferred
  • True one-command pip install (dGPredictor/OPAM2 are source checkouts via DependencyManager)
  • Live backend CI (requires conda env + cloned repos; currently gated with skipif)

jplfaria pushed a commit to jplfaria/KBUtilLib that referenced this pull request Jul 21, 2026
…ry#42-cshenry#47)

- Move kbu-literature-review/review/diagnose to .claude/agents/ as kbu-sub-* with type: agent frontmatter (AC cshenry#44, cshenry#45, cshenry#47)
- Add data/, models/, genomes/ .gitkeep to template (AC cshenry#42)
- Append [layout] shared_dirs block to kbu-project.toml.template (AC cshenry#43)
- Add 9 root_gitignore_lines patterns to .gitignore template
- Update bootstrap.py: _CLAUDE_AGENT_FILES list, agents dir creation loop, shared-dir .gitkeep scaffolding, [layout] in manifest, kbu-migrate.md in command list (skips silently when template absent - Option 1)
- Update new_project.py: _TRACKED_DIRS includes .claude/agents, [layout] in manifest
- Update tests: AC9 entry counts, _make_stub_template includes agents, new AC 42-47 tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a PredictiveThermoUtils facade that estimates standard transformed
Gibbs energies of formation (compounds) and reaction (delta_r G'^0) by
dispatching across a priority-ordered set of pluggable backends, each of
which degrades gracefully and never fabricates a value (unknown -> None
plus a warning).

Backends (src/kbutillib/thermo_predictors/):
- equilibrator: live component-contribution via equilibrator-api
  (optional dep). Condition-aware (pH, ionic strength, T, p_Mg).
- dgpredictor: Maranas/Tyo-lab group-contribution reaction predictor
  (Andrew Freiburger's fork). Runs out-of-process against a local clone
  to keep rdkit/openbabel and the ~150 MB model out of this process;
  KEGG stoichiometry in, kJ/mol transformed dG' out. Detects a
  missing/LFS-pointer model and reports unavailable.
- modelseed_db: baked transformed dG from the expanded-eQuilibrator
  ModelSEED Database fork, read directly from compounds.tsv/reactions.tsv
  with the stdlib csv reader (kcal->kJ, fixed pH7/I0.25/298.15 K,
  EQU/EQC/EQP-preferred provenance gate).
- modelseed: always-on fallback over the existing ThermoUtils lookups.
- molgpk: stub for the Tyo-lab molGPK/OPAM2 predictor (unreleased);
  reports unavailable until configured.

Dispatch order: equilibrator -> dgpredictor -> modelseed_db -> modelseed.
Nothing in the subpackage imports a heavy/optional dependency at import
time. Exposed as the lazy `predictive_thermo` property on KBUtilLib.

Adds [project.optional-dependencies] equilibrator / thermo extras and a
dependency-free test suite (tests/test_predictive_thermo.py) covering
graceful degradation, the no-fabrication invariant, dispatch fall-through,
and the kcal->kJ conversion against worked ATP / rxn00001 values.
…ules backends

Add a pluggable in-silico reaction-prediction / metabolic-network-expansion
layer mirroring the predictive-thermo facade pattern:

- cheminformatics/base.py: ExpansionBackend protocol + PredictedCompound/
  PredictedReaction/ExpansionResult dataclasses + BackendUnavailableError.
  Stdlib-only; importing it pulls no heavy dependency.
- cheminformatics/pickaxe_backend.py: optional-dep wrapper over minedatabase
  (Pickaxe) SMARTS-operator expansion; robust rule-data-dir resolution
  (config -> env -> wheel -> source checkout) since the wheel ships no rule
  data; graceful degradation when absent.
- cheminformatics/retrorules_backend.py: applies RetroRules SMARTS via RDKit
  (no-AddHs + partial-sanitize recipe), streaming the flat-rules TSV; filters
  by diameter/direction; graceful degradation when rdkit/TSV absent.
- network_expansion_utils.py: NetworkExpansionUtils facade + Impl wrapper with
  backend dispatch (pickaxe -> retrorules), backend_status diagnostics, and
  never-fabricate semantics (empty result + warnings when nothing expands).
- Wire NetworkExpansionUtils(Impl) into package __init__ and the toolkit
  (lazy 'network_expansion' property); add 'cheminformatics' optional-deps
  extra (rdkit).
- tests/test_network_expansion.py: import/degradation invariants, injected-fake
  dispatch success path, and opt-in live integration tests gated on env vars.

16/16 tests pass (incl. live runs against real RetroRules + MINE-Database
rule data); new modules ruff clean.
Replace the dead-endpoint molGPK stub with a working backend that wraps
Andrew Freiburger's OPAM2 (MolGpKa fine-tuned on the ModelSEED database).

- Subprocess-isolated so KBUtilLib never imports torch/torch-geometric;
  the worker is bundled in-package (_molgpk_worker.py) and run with
  OPAM2_SRC pointing at a cloned OPAM2 repo, so users only clone OPAM2
  and set thermo.molgpk.repo_path/python (or MOLGPK_REPO/MOLGPK_PYTHON).
- compound_dgf -> pKa values + major microspecies; dgf left None (molGPK
  feeds equilibrator, so ΔGf is never double-counted or fabricated).
- Probes worker + ModelSEED-tuned .pth checkpoints with a Git-LFS-pointer
  size guard; degrades gracefully (per-compound miss != backend down).
- Batch path: worker accepts {compounds:[...]} and loads the model once;
  compounds_dgf + facade.compounds_microspecies amortize the model load
  across a whole compound set.

Tested: hermetic unavailable/degrade/LFS-pointer/hard-failure/misalign
cases plus env-gated live single + batch runs against a real OPAM2
checkout (acetate, glycine zwitterion, phosphate).
dGPredictor backend rewritten to Andrew Freiburger's ModelSEED fork API
(ModelSEEDdGPredictor.predict_reaction / predict_from_equation): addresses
reactions by rxnNNNNN accession or ModelSEED cpd stoichiometry, fixes the
model filename to model/modelseed_M12_model_BR.pkl, adds a batch path, and
ships the previously-missing subprocess worker (_dgpredictor_worker.py).

molGPK backend confirmed against the real OPAM2 API (predict_pka.predict /
protonate.Opam_protonate_mol + modelseed .pth weights).

Both validated end-to-end through the PredictiveThermoUtils facade with
config-driven repo paths (thermo.{dgpredictor,molgpk}.{repo_path,python}):
  molgpk  acetic acid pKa 2.01 -> CC(=O)[O-]; glycine -> [NH3+]CC(=O)[O-]
  dgpredictor  rxn00001 -15.76+/-3.63 kJ/mol; ATP hydrolysis -25.46 kJ/mol

Adds gated live integration tests (skip unless *_REPO/*_PYTHON set) and a
documented thermo: config block. 31 unit tests pass; 15 pass with repos
configured.
…-007

Andrew (relaying the Tyo lab) confirmed github.com/tyo-nu/MINE-Database as the
pickaxe version to use. It IS the minedatabase package PickaxeBackend already
wraps, with the bundled rule TSVs — so no separate tyo_pickaxe_backend.py is
needed; point cheminformatics.pickaxe.data_dir at the clone's minedatabase/data.

Validated end-to-end through the NetworkExpansionUtils (kbu.chem) facade:
  D-glucose, 1 generation, metacyc_generalized -> 901 new compounds /
  1696 new reactions. Live integration test test_pickaxe_live_expansion passes.

Resolves ADR-007 (wrap, don't reimplement). Documents the pickaxe/retrorules
config block. Fixes test_backend_status_reports_all_backends to also clear the
DGPREDICTOR_* env vars so it tests the not-configured contract deterministically.
45 unit tests pass; live thermo + pickaxe tests pass with repos configured.
Make the dGPredictor / molGPK / pickaxe backends self-contained the way the rest
of KBUtilLib's external deps are: register the three research repos in
dependencies.yaml (dGPredictor, OPAM2, MINE-Database) with path + git URL, and
have each backend fall back to DependencyManager.get_dependency_path() when no
explicit config key / env var is set. The pickaxe backend also puts a declared
MINE-Database checkout on sys.path itself (no pip install, no .pth shim needed --
the Tyo repo pins python <3.10 which blocks pip install -e on 3.11).

Net result: with zero env vars and zero config paths, all three backends resolve
and compute purely from dependencies.yaml -- the same mechanism used for
ModelSEEDpy / ModelSEEDDatabase / cobrakbase. Adds base.dependency_repo_path()
helper, an INSTALL.md documenting the common footprint, and tightens the
hermetic 'not configured' tests to also neutralize the DependencyManager so they
stay deterministic. 45 unit tests pass; live thermo + pickaxe tests pass.
…eature

Finalization pass to make the equilibrator + dGPredictor + molGPK/OPAM2
unified facade PR-ready.

- docs/config: resolve API-name drift; document real attributes
  (kbu.predictive_thermo, kbu.network_expansion) and clarify legacy kbu.thermo;
  add kbu.chem alias on the toolkit facade
- INSTALL.md: fix broken model-rebuild reference to dGPredictor's in-repo
  retrain_modelseed.py so the documented install path is real
- thermo_predictors: add machine-readable `units` field (kJ/mol) to
  CompoundThermoEstimate/ReactionThermoEstimate and to_dict()
- thermo_utils: fix stored-reaction deltaG bug (read reaction_obj.delta_g to
  match its guard instead of reaction_obj.deltag)
- tests: rewrite stale test_ms_biochem_deltag.py against the relocated deltaG
  API in ThermoUtils; add gated molGPK predominant-ion pH-sweep test and
  units-field assertions
- environment.yml: pinned conda env covering rdkit/openbabel/torch and backend
  runtime deps for a reproducible one-env install
@VibhavSetlur
VibhavSetlur force-pushed the feature/thermo-cheminformatics-clean branch from 0434701 to 60d853b Compare July 22, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant