Architecture: pdb2reaction¶
1. Overview¶
This page is for pdb2reaction developers: it describes the package layers, where each file lives, and the constraints to respect while developing. After a change, run the checks in the gate cycle of CONTRIBUTING. To run calculations instead, start from Getting Started.
pdb2reaction is a Python CLI for enzymatic reaction-path analysis on an
active-site cluster model. It uses built-in MLIPs or a custom ASE calculator
for geometry/path stages and can add an optional PySCF/GPU4PySCF DFT
single-point stage.
2. Layered structure (6 core layers)¶
2.1 Layer table¶
The six layers are L1–L5, with L4 split into L4a and L4b.
layer |
dir |
responsibility |
may depend on |
|---|---|---|---|
L1 Interface |
|
Click root group, shared option-decorator factories ( |
|
L2 Application |
|
per-subcommand orchestration and shared stage helpers ( |
|
L3 Domain |
|
chemistry-aware helper logic (bond change detection, bond summary, element-info propagation) |
|
L4a Infra (MLIP) |
|
MLIP backend dispatcher + per-backend adapter (UMA / Orb / MACE / AIMNet2) |
|
L4b Infra (I/O) |
|
output layout, summary, trajectory, PDB fix, energy diagram, Hessian cache |
|
L5 Foundation |
|
defaults (primary source for shared defaults), utilities (structure / coordinate / plot helpers), logging, output, and result publication |
(none, by design intent) |
(bundle, not a layer) |
|
repo-internal forks (optimizer / thermochemistry) |
(sibling, layer-external) |
Dependency direction (design goal): L1 → L2 → {L3, L4} → L5, as in the last column of the table. Some imports break it today: workflows/* → cli; cli/common_options.py → backends; core/utils.py → domain, io, backends, cli and core/defaults.py → backends; domain/add_elem_info.py, domain/bond_summary.py, and several io/ modules → cli; io/charge.py and io/structure_formats.py → domain; io/trj2fig.py → backends. Bundled forks sit outside the layer graph and may be imported from any layer (from pysisyphus.X import Y).
CI checks only part of this direction. Run each script from the repository root with python; exit code 0 means it passes.
.github/scripts/check_import_graph.pyforbids import cycles amongpdb2reaction/*modules, imports ofworkflows/*fromcore/ordomain/, and imports ofpdb2reactionfrompysisyphus/..github/scripts/check_engineering_markers.pychecks the# CHEMISTRY-RULEand# DOMAIN_PUREmarkers (§5.1) and that the MLIP SDKs (fairchem,orb_models,mace,aimnet) are imported only underbackends/.
2.2 ASCII map of the package tree¶
pdb2reaction/ [GH: t-0hmura/pdb2reaction]
├── pyproject.toml packages.find = ["pdb2reaction*", ...] (glob, frozen)
├── README.md / CONTRIBUTING.md / CHANGELOG.md
├── docs/
│ ├── architecture.md ← this file
│ └──... (Sphinx documentation site)
├── pdb2reaction/ ← package body, 6-layer physical dir
│ ├── __init__.py PEP 562 lazy: _LAZY_SYMBOLS / _LAZY_MODULES + __getattr__
│ ├── __main__.py `from .cli import cli`
│ ├── _version.py / py.typed
│ │
│ ├── cli/ # === L1 Interface ===
│ │ ├── app.py Click group + _LAZY_SUBCOMMANDS registry (absolute paths)
│ │ ├── common_options.py @add_print_every_option / @add_irc_pos_def_option / @add_precision_option / @add_coord_type_option / @add_ml_charge_spin_options
│ │ ├── decorators.py resolve_yaml_sources / load_merged_yaml_cfg / _write_error_json
│ │ ├── help_pages.py --help-advanced pager
│ │ ├── bool_compat.py --flag / --no-flag normalization
│ │ └── default_group.py subcommand resolver, lazy module import
│ │
│ ├── workflows/ # === L2 Application ===
│ │ ├── all.py full pipeline orchestrator (extract → … → DFT)
│ │ ├── path_search.py / path_opt.py MEP search / COS wrapper
│ │ ├── tsopt.py / freq.py / irc.py / dft.py per-stage runners
│ │ ├── opt.py / sp.py / scan.py / scan2d.py /
│ │ │ scan3d.py / scan_common.py geometry opt / single point / scans
│ │ ├── extract.py active-site extraction CLI
│ │ ├── restraints.py restraint helpers
│ │ └── align_freeze.py Kabsch + frozen-subset rmsd
│ │
│ ├── domain/ # === L3 Domain ===
│ │ ├── bond_changes.py R↔P bond detection
│ │ ├── bond_summary.py post-IRC diagnostic
│ │ ├── add_elem_info.py PDB element column normalizer
│ │ └── residue_data.py residue/ion charge tables
│ │
│ ├── backends/ # === L4a Infra (MLIP) ===
│ │ ├── __init__.py backend dispatch + registry
│ │ ├── base.py MLIPCalculator protocol
│ │ ├── custom.py custom ASE-calculator adapter
│ │ ├── _determinism.py deterministic reduction shim
│ │ ├── pyscf_dft.py optional PySCF/GPU4PySCF adapter
│ │ └── uma.py / orb.py / mace.py / aimnet2.py MLIP adapters
│ │
│ ├── io/ # === L4b Infra (I/O) ===
│ │ ├── summary.py summary.log writer
│ │ ├── energy_diagram.py Plotly diagram
│ │ ├── trj2fig.py trajectory → PNG / JPEG / SVG / PDF / HTML / CSV
│ │ ├── pdb_fix.py altloc resolution
│ │ ├── altloc.py altloc identity/selection helpers
│ │ ├── charge.py residue-aware charge resolution
│ │ ├── structure_formats.py PDB/mmCIF conversion + identifier restoration
│ │ └── hessian_cache.py in-memory Hessian cache
│ │
│ └── core/ # === L5 Foundation ===
│ ├── defaults.py primary source for shared defaults
│ ├── dft_settings.py DFT option resolution (CHEMISTRY-RULE:4)
│ ├── logging.py -v / --verbose wiring
│ ├── utils.py PDB / XYZ / plot helpers
│ ├── output.py / result_commit.py output/result ownership
│ └── pes_composition.py energy-component composition
│
├── tests/ smoke / unit
├── .github/ workflows/ + scripts/ (CI, release, engineering, and documentation checks)
└── (repo-top sibling, layer-external bundled forks)
pysisyphus/ 98 files, repo-internal fork (only the numerical features used here)
thermoanalysis/ 7 files, repo-internal fork
2.3 Per-layer responsibility detail¶
L1 cli/ owns the root group, lazy registry, argv normalization, progressive help, and shared option-decorator factories. Workflow and utility modules own their actual @click.command() objects and may define command-local options inline; the root resolves and invokes those objects. Every _LAZY_SUBCOMMANDS entry uses an absolute module path.
L2 workflows/ contains compute-command modules plus shared stage helpers such as scan_common.py, restraints.py, and align_freeze.py. A command module normally exposes one @click.command() named cli; utility commands also live in io/ and domain/, so neither “one file per subcommand” nor “every workflow file is a command” is an invariant.
L3 domain/. Chemistry-aware helper logic that may import torch / numpy / pysisyphus.constants (numeric backends), but may not import MLIP SDKs. Domain helpers are reusable by any L2 stage runner.
L4a backends/. MLIP backend dispatcher plus one adapter per supported MLIP. The dispatcher also exposes the custom ASE-calculator boundary.
L4b io/. Output-side I/O concerns: per-stage summary writer, energy diagram, trajectory rendering, PDB altloc fix, PDB/mmCIF conversion and template identifier restoration, and in-memory Hessian cache. Output format is owned here and consumed by stage runners; current non-foundation imports are listed in §2.1.
L5 core/ is the lowest layer. defaults.py is the primary source for shared numerical and CLI defaults; grep it first, then inspect justified command-local defaults (for example, path-engine choices). utils.py contains shared configuration, structure, coordinate, and plotting helpers.
2.4 Lazy-import mechanism (conceptual diagram)¶
External consumer Package root Layer dir
--------------------------------------- ---------------------- ---------
from pdb2reaction.core.utils import x ──► (direct dotted import) ──► pdb2reaction/core/utils.py
import pdb2reaction.io.trj2fig ──► (direct dotted import) ──► pdb2reaction/io/trj2fig.py
pdb2reaction myaction ──► pdb2reaction/cli/app.py
_LAZY_SUBCOMMANDS["myaction"]
= ("pdb2reaction.workflows.myaction", "cli", "...")
└─► importlib.import_module(absolute path)
└─► getattr(module, "cli") → Click command
pdb2reaction/__init__.py also defines two PEP 562 registries for root-level re-exports, _LAZY_SYMBOLS and _LAZY_MODULES. Both are empty, so import symbols from their layer module, as in the first two lines above.
4. File index — “where does this concern live?”¶
4.1 CLI / entry (L1 cli/)¶
concern |
file |
|---|---|
Click root group + subcommand dispatch |
|
Subcommand resolver (lazy import) |
|
|
|
YAML source resolution + standardized exception handling |
|
|
|
Bool flag compat ( |
|
Shared option-decorator factories ( |
|
4.2 Workflow stage runners (L2 workflows/)¶
concern |
file |
|---|---|
Full pipeline orchestrator |
|
Geometry optimization (L-BFGS / RFO) |
|
Scan and 2D/3D energy-landscape grids + shared |
|
MEP search (GSM / DMF) |
|
MEP optimizer core (pysisyphus COS) |
|
TS optimization (RS-P-RFO / RS-I-RFO / TRIM / Dimer + Bofill) |
|
Vibrational analysis (PHVA + backend-agnostic active-DOF Hessian handling) |
|
IRC integration |
|
Single-point DFT (PySCF / GPU4PySCF, in-process) |
|
Active-site extraction (cluster cap) |
|
Restraint helpers |
|
Kabsch / frozen-subset alignment |
|
4.3 Chemistry helpers (L3 domain/)¶
concern |
file |
|---|---|
R↔P bond change detection |
|
Post-IRC bond summary |
|
PDB element column normalizer |
|
Residue and ion charge tables |
|
4.4 MLIP backends (L4a backends/)¶
concern |
file |
|---|---|
Backend dispatch + registry |
|
|
|
Custom ASE-calculator adapter |
|
Deterministic reduction shim |
|
Optional DFT calculator adapter |
|
Per-backend adapters |
|
To add a backend, follow recipe 3.2 “Add an MLIP backend” in CONTRIBUTING.md.
4.5 I/O (L4b io/)¶
concern |
file |
|---|---|
|
|
Plotly energy diagram |
|
Trajectory → PNG / JPEG / SVG / PDF / HTML / CSV |
|
PDB altloc resolution |
|
Altloc identity and coherent selection |
|
Residue-aware charge resolution |
|
PDB/mmCIF conversion + identifier restoration |
|
In-memory Hessian cache (per-run TTL) |
|
4.6 Foundation (L5 core/)¶
concern |
file |
|---|---|
Shared/default numerical settings (primary source; verify command-local exceptions) |
|
PDB / XYZ / plot helpers |
|
|
|
Output/result ownership helpers |
|
Energy-component composition |
|
4.7 Repo-internal bundled forks¶
dir |
role |
selected divergent files (see the directory README for the complete list) |
|---|---|---|
|
optimizer / TS / IRC engine |
|
|
thermochemistry (ΔG, ZPE, partition functions) |
|
6. Bundled forks (repo-internal)¶
pdb2reaction ships two repo-internal modules at the repo top:
dir |
upstream PyPI? |
purpose |
scope of edits allowed |
|---|---|---|---|
|
NO — fork, do not |
optimizer, TS, IRC, COS, calculators |
logic edits need a demonstrated defect or approved numerical feature, a focused regression test, and the relevant numerical/GPU benchmark |
|
NO — fork (branding/I/O diff) |
ΔG, ZPE, partition functions, |
logic edits need a demonstrated I/O or numerical need and the thermochemistry golden tests |
Each directory carries a README.md listing its divergent files and its change
policy.
7. Recommended deeper reading order¶
After the five steps in §3, follow this depth-first reading order:
pdb2reaction/core/defaults.py— the primary source for shared defaults (§2.3).pdb2reaction/workflows/extract.py— active-site cluster cap.pdb2reaction/backends/__init__.py+base.py— MLIP dispatcher and per-backend adapter contract.pdb2reaction/workflows/tsopt.py— TS optimization drivers and the Dimer flatten-loop Bofill update (CHEMISTRY-RULE:7).pdb2reaction/workflows/freq.py— vibrational analysis on the cluster model.pdb2reaction/workflows/irc.py— VRAM hygiene + IRC integration.pdb2reaction/workflows/dft.py— single-point DFT with PySCF / GPU4PySCF (CHEMISTRY-RULE:5).pdb2reaction/core/utils.py— shared PDB / XYZ / plot helpers.