アーキテクチャ: pdb2reaction

1. 概要

pdb2reaction は、活性部位cluster modelに対して酵素反応経路解析を実行する Python CLIです。geometry/path stageには組み込みMLIPまたはcustom ASE calculatorを 使用し、任意でPySCF/GPU4PySCF DFT 一点計算を追加できます (extract → MEP → tsopt → IRC → freq → dft)。

同梱された 2 つの fork(pysisyphus/thermoanalysis/)は、リポジトリ最上位に repo 内部モジュールとして配置されています。これらは意図的に上流の PyPI 配布版では ありません。本パッケージと並べて PyPI から再インストールすると、ローカルの拡張が気づかないうちに動かなくなります。§6 を参照してください。


2. 階層構造(6 つの計算層)

2.1 階層テーブル

layer

dir

responsibility

may depend on

L1 Interface

pdb2reaction/cli/

Click root group、共有 option-decorator ファクトリ(common_options.py)、--help-advanced、bool flag 正規化、サブコマンドリゾルバ

workflows/, core/

L2 Application

pdb2reaction/workflows/

サブコマンドごとのオーケストレーション; ステージランナー 1 ファイルにつき 1 つ(all.py, path_search.py, tsopt.py, extract.py, irc.py, freq.py, dft.py, …)

domain/, backends/, io/, core/

L3 Domain

pdb2reaction/domain/

化学的に意味を持つヘルパーロジック(結合変化検出、結合サマリ、元素情報伝播)

core/

L4a Infra (MLIP)

pdb2reaction/backends/

MLIP バックエンドディスパッチャ + バックエンドごとのアダプタ(UMA / Orb / MACE / AIMNet2)

core/

L4b Infra (I/O)

pdb2reaction/io/

出力レイアウト、サマリ、軌跡、PDB 修正、エネルギーダイアグラム、Hessian キャッシュ

core/

L5 Foundation

pdb2reaction/core/

defaults(共有defaultの主要source)、utils(structure / coordinate / plot helper)、logging、将来の errors.py / types.py

(none、設計意図)

(bundle, not a layer)

<repo>/pysisyphus/, <repo>/thermoanalysis/

repo 内部 fork(optimizer / thermochemistry)

(sibling, layer-external)

依存方向(設計目標): L1 L2 {L3, L4} L5。全面的な top-to-bottom 方向は機械的には強制していません。強制対象の subset では product module graph を非循環pdb2reaction/* モジュール間に強連結成分が無い)に保ち、core/domain から workflows/* への import を禁止します。CIは2つのゲートで別々の不変条件を検査します。.github/scripts/check_engineering_markers.py# CHEMISTRY-RULE:{4,5,7} マーカーと # DOMAIN_PURE マーカー、MLIP SDK(fairchem/orb_models/mace/aimnet)が backends/ 下のみで import されることを検査し、.github/scripts/check_import_graph.py(静的AST import graph)は product cycleが無いこと・pysisyphus/**pdb2reaction を import しないこと・core workflows / domain workflows back-edgeが無いことを検査します。残る許容される下向きedge / 一方向facadeは workflows/* cli.common_options/cli.decorators/cli.help_pagescore/utils.py domain.add_elem_info/io.structure_formats/io.chargeio/charge.py domain.residue_data/io.structure_formatsio/structure_formats.py domain.add_elem_infoio/trj2fig.py backends です。正準residue table(domain/residue_data.py)、charge engine(io/charge.py)、console-gated charge-summary logger(core/utils.py)は既存importパス維持のため workflows/extract.py から再exportされます。同梱forkはlayer graph外にあり、絶対package path(from pysisyphus.X import Y)で各layerからimportできます。

2.2 パッケージツリーの ASCII マップ

pdb2reaction/ [GH: t-0hmura/pdb2reaction]
├── pyproject.toml packages.find = ["pdb2reaction*", ...] (glob, frozen)
├── README.md / CONTRIBUTING.md / CHANGELOG.md
├── docs/
│ ├── ja/architecture.md ← このファイル
│ └──... (Sphinx ドキュメントサイト)
├── 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 残基・イオン電荷table
│ │
│ ├── backends/ # === L4a Infra (MLIP) ===
│ │ ├── __init__.py backend dispatch + registry
│ │ ├── base.py MLIPCalculator protocol
│ │ ├── custom.py custom ASE-calculator adapter
│ │ ├── _determinism.py deterministic reduction shim
│ │ └── uma.py / orb.py / mace.py / aimnet2.py per-backend adapters
│ │
│ ├── io/ # === L4b Infra (I/O) ===
│ │ ├── summary.py summary.json / summary.log writer
│ │ ├── energy_diagram.py Plotly diagram
│ │ ├── trj2fig.py trajectory → PNG / SVG / PDF / HTML / CSV
│ │ ├── pdb_fix.py altloc resolution
│ │ ├── altloc.py altloc identity/選択helper
│ │ ├── charge.py 残基を考慮した電荷解決
│ │ ├── structure_formats.py PDB/mmCIF bridge + identifier復元
│ │ └── hessian_cache.py in-memory Hessian cache
│ │
│ └── core/ # === L5 Foundation ===
│   ├── defaults.py 共有defaultの主要source
│   ├── utils.py PDB / XYZ / plot helpers
│   ├── output.py / result_commit.py output/result ownership
│   └── pes_composition.py energy成分合成
│
├── tests/ smoke / unit
├── .github/ workflows/ + scripts/ (CI、release、engineering、documentation checks)
└── (repo-top sibling, layer-external bundled forks)
 pysisyphus/ ~90 file, repo-internal fork (slimmed; CLI driver + QM backends + wavefunction + dead optimizers / IRC / NEB variants removed)
 thermoanalysis/ 5 file, repo-internal fork

2.3 階層ごとの責務詳細

L1 cli/ は root group、lazy registry、argv 正規化、段階的help、共有option-decoratorを所有します。実際の @click.command() とcommand固有optionは workflow/utility module側にもあります。rootはそれらを解決して呼び出します。_LAZY_SUBCOMMANDS の各entryは 絶対module path を使います。

L2 workflows/ は計算command moduleに加え、scan_common.pyrestraints.pyalign_freeze.py などの共有stage helperを含みます。command moduleは通常 cli という @click.command() を1つ公開しますが、utility commandは io/domain/ にもあるため、「subcommandごとに必ず1file」「workflow fileはすべてcommand」という規則ではありません。

L3 domain/torch / numpy / pysisyphus.constants(数値バックエンド)を import してよい化学的に意味を持つヘルパーロジックですが、MLIP ランタイム(fairchem, orb_models, mace, aimnet)を import しては いけません。この MLIP 非依存は .github/scripts/check_engineering_markers.py がリポジトリ全体で強制します。なお # DOMAIN_PURE docstring マーカーはこれとは別のゲートで、backend 非依存を保つべき特定の workflow モジュールworkflows/dft.py / workflows/tsopt.py / workflows/sp.py)にのみ付与され、domain/ のファイルには付きません。domain ヘルパーはどの L2 ステージランナーからでも再利用可能です。

L4a backends/。MLIP バックエンドディスパッチャ(__init__.py + base.py)と、サポートする各 MLIP につき1つのadapter(uma.py, orb.py, mace.py, aimnet2.py)があります。

L4b io/。出力側I/O: stage summary、energy diagram、trajectory render、PDB altloc修正、PDB/mmCIF bridge/template identifier復元、in-memory Hessian cache。output formatはここが所有しstage runnerが利用します。foundation外importは上記back-edge一覧を参照してください。

L5 core/ は最下層です。defaults.py は共有される数値/CLI defaultの 主要source です。まずここをgrepし、path engine choiceなど正当なcommand-local defaultも確認します。utils.py は設定・構造・座標・plotの共有helperを含みます。

2.4 遅延 import の仕組み(概念図)

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

from pdb2reaction import <Symbol>     ──► pdb2reaction/__init__.py
                                          __getattr__("<Symbol>")
                                          └─► _LAZY_SYMBOLS["<Symbol>"]
                                              = "pdb2reaction.<layer>.<module>"
                                              └─► importlib.import_module(...)

from pdb2reaction import <module>     ──► pdb2reaction/__init__.py
        (= module attr)                   __getattr__("<module>")
                                          └─► _LAZY_MODULES["<module>"]
                                              = "pdb2reaction.<layer>.<module>"
                                              └─► importlib.import_module(...) returns module

pdb2reaction myaction                 ──► pdb2reaction/cli/app.py
                                          _LAZY_SUBCOMMANDS["myaction"]
                                          = ("pdb2reaction.workflows.myaction", "cli", "...")
                                          └─► importlib.import_module(absolute path)
                                              └─► getattr(module, "cli") → Click command

遅延 import 互換性の 2 階層と CLI ディスパッチ:

  1. Root シンボル属性from pdb2reaction import <Symbol>)— pdb2reaction/__init__.py:_LAZY_SYMBOLS + PEP 562 __getattr__ が処理します。シンボルは初回アクセス時に layer-dir パスから読み込まれ、pdb2reaction import 時の import コストはゼロのまま保たれます。

  2. Root モジュール属性from pdb2reaction import <module>)— _LAZY_MODULES が処理します。__getattr__importlib.import_module を介してモジュールオブジェクト自体を返します。pdb2reaction は現在、参照されるモジュール属性パスは 0 件です(レジストリは空です。root 属性アクセスは将来の拡張のために予約されています)。

CLI サブコマンドリゾルバ(cli/app.py:_LAZY_SUBCOMMANDS)は 絶対 モジュールパス(例: "pdb2reaction.workflows.all")を使うため、default_group.pycli/ に移動してもサブコマンド探索が気づかないうちに動かなくなることはありません(レジストリはもはや __package__ に依存しません)。


3. Fresh-eyes 5 ステップナビゲーション(合計 ≈ 40 分)

リポジトリを初めて開くコントリビュータは、この道筋を上から下へ辿ってください。各ステップで 1 つの関心事を片付けます。

step

minutes

open

what you learn

1

3

README.md

1 段落のエレベーターピッチ + 単一コマンド使用法

2

5

このファイル(docs/architecture.md)§2 + §4

6 階層のディレクトリツリー、依存方向、各関心事の所在

3

5

pdb2reaction/cli/app.py

Click root group、_LAZY_SUBCOMMANDS registry、絶対path解決

4

20

pdb2reaction/workflows/all.py(流し読み)

1つの完全なsubcommandを上から下まで追う

5

7

CONTRIBUTING.md §3 + §4

5 つの add-a-X レシピ + 「触るな」という隠れた制約

ステップ 5 の後は、§4 のファイル索引を辿ることで他のどのファイルでも読めます。本パッケージは意図的に 各階層内でフラット です。pdb2reaction/<layer>/ の下にネストしたパッケージは存在しないため、2 ディレクトリより深く辿る必要は決してありません。


4. ファイル索引 — 「この関心事はどこにあるか?」

4.1 CLI / エントリ(L1 cli/

concern

file

Click root group + サブコマンドディスパッチ

pdb2reaction/cli/app.py

サブコマンドリゾルバ(遅延 import)

pdb2reaction/cli/default_group.py

python -m pdb2reaction エントリ

pdb2reaction/__main__.py

YAML ソース解決 + 標準化された例外処理

pdb2reaction/cli/decorators.py

--help-advanced ページャ

pdb2reaction/cli/help_pages.py

Bool flag 互換(--flag / --no-flag + value style)

pdb2reaction/cli/bool_compat.py

共有 option-decorator ファクトリ(--print-every, --irc-pos-def, --precision, --coord-type, --charge / --ligand-charge / --multiplicity

pdb2reaction/cli/common_options.py

4.2 ワークフローステージランナー(L2 workflows/

concern

file

全パイプラインオーケストレータ

pdb2reaction/workflows/all.py

構造最適化(L-BFGS / RFO)

pdb2reaction/workflows/opt.py

Scanと2D/3D energy-landscape grid + 共有

pdb2reaction/workflows/scan{,2d,3d,_common}.py

MEP 探索(GSM)

pdb2reaction/workflows/path_search.py

MEP optimizer コア(pysisyphus COS)

pdb2reaction/workflows/path_opt.py

TS 最適化(RS-P-RFO + Bofill + macro/micro)

pdb2reaction/workflows/tsopt.py

振動解析(backend-agnostic PHVA + active block)

pdb2reaction/workflows/freq.py

IRC 積分(macro / micro)

pdb2reaction/workflows/irc.py

一点 DFT(gpu4pyscf サブプロセス)

pdb2reaction/workflows/dft.py

活性部位抽出(クラスターキャップ)

pdb2reaction/workflows/extract.py

拘束ヘルパー

pdb2reaction/workflows/restraints.py

Kabsch / frozen-subset アラインメント

pdb2reaction/workflows/align_freeze.py

4.3 化学ヘルパー(L3 domain/

concern

file

R↔P 結合変化検出

pdb2reaction/domain/bond_changes.py

IRC 後の結合サマリ

pdb2reaction/domain/bond_summary.py

PDB 元素列正規化

pdb2reaction/domain/add_elem_info.py

残基・イオン電荷テーブル

pdb2reaction/domain/residue_data.py

4.4 MLIP バックエンド(L4a backends/

concern

file

バックエンドディスパッチ + レジストリ

pdb2reaction/backends/__init__.py

MLIPCalculator プロトコル + base

pdb2reaction/backends/base.py

custom ASE calculator アダプタ

pdb2reaction/backends/custom.py

決定論的 reduction shim

pdb2reaction/backends/_determinism.py

バックエンドごとのアダプタ

pdb2reaction/backends/{uma, orb, mace, aimnet2}.py

add-a-backend レシピは Backends を参照してください。

4.5 I/O(L4b io/

concern

file

summary.json / summary.log ライタ

pdb2reaction/io/summary.py

Plotly エネルギーダイアグラム

pdb2reaction/io/energy_diagram.py

軌跡 → PNG / SVG / PDF / HTML / CSV

pdb2reaction/io/trj2fig.py

PDB altloc 解決

pdb2reaction/io/pdb_fix.py

altloc identity と一貫した選択

pdb2reaction/io/altloc.py

残基を考慮した電荷解決

pdb2reaction/io/charge.py

PDB/mmCIF bridge + identifier 復元

pdb2reaction/io/structure_formats.py

インメモリ Hessian キャッシュ(実行ごとの TTL)

pdb2reaction/io/hessian_cache.py

調和拘束のセットアップ

pdb2reaction/workflows/restraints.py(L2 ステージヘルパー)

4.6 Foundation(L5 core/

concern

file

共有/数値default(主要source。command-local例外も確認)

pdb2reaction/core/defaults.py

PDB / XYZ / plot ヘルパー

pdb2reaction/core/utils.py

-v / --verbose LEVEL logging 配線

pdb2reaction/core/logging.py

output/result ownership helper

pdb2reaction/core/output.pypdb2reaction/core/result_commit.py

energy 成分合成

pdb2reaction/core/pes_composition.py

4.7 repo 内部の同梱 fork

dir

role

主な divergent files(完全な一覧は各ディレクトリの README を参照)

pysisyphus/

optimizer / TS / IRC エンジン

irc/IRC.py(オプトインの require_pos_def_hessian PSD 収束ガード)、optimizers/hessian_updates.py(GPU 常駐の in-place rank-two Bofill 更新と、明示的な PYSIS_BOFILL_CPU_OFFLOAD=1 フォールバック)、tsoptimizers/{RSIRFOptimizer,RSPRFOptimizer,TRIM,TSHessianOptimizer}.pycalculators/{Calculator,Dimer}.py_array.py(torch/numpy バックエンド shim)

thermoanalysis/

thermochemistry(ΔG, ZPE, 分配関数)

QCData.py(上流との branding 差分)

touch 制限の境界については各ディレクトリの README.md を参照してください。


5. 隠れた制約(パッチ前に必読)

5.1 化学ルール(grep レシピ)

正確性に直結する 3 つのルールは workflows/dft.pyworkflows/tsopt.py に実装されています。これらは smoke テストでは検出 されません。ここでの静かな乖離は反応経路の精度を壊します。インラインの # CHEMISTRY-RULE:N マーカーと # DOMAIN_PURE モジュール docstring マーカーがルールを識別し、.github/scripts/check_engineering_markers.py が CI でマーカーの完全性を強制します。

編集前にすべての化学ルールを見つけるには:

# List all rule sites in the repo (host file + line)
grep -rnE '# CHEMISTRY-RULE:[0-9]+' pdb2reaction/

# List every # DOMAIN_PURE marker (= chemistry-rule host modules)
grep -rn '# DOMAIN_PURE' pdb2reaction/

3 つのルール(マーカー ID は連続していません)は次の通りです:

marker

rule

host file

4

gpu4pyscf rks_lowmem triple-guard

pdb2reaction/workflows/dft.py

5

def2 family auto-ECP injection

pdb2reaction/workflows/dft.py

7

bofill_update advanced-indexing scatter

pdb2reaction/workflows/tsopt.py

これらのいずれかを編集するには、[CHEMISTRY-RULE:N] コミットプレフィックス、 maintainer approval、該当する focused regression test、scheduled numerical benchmark が必要です。 CONTRIBUTING.md §4.1 と §5 を参照してください。

5.2 VRAM 管理の不変条件(del チェーンをリファクタしない)

IRC / TSopt / Freq ステージは、CUDA メモリを解放するためにステージ間で GPU 常駐オブジェクト(calc, geom, hess)を明示的に del します。ステージ境界では gc.collect() に加え、CUDA allocation がある場合は torch.cuda.empty_cache() も実行します。これらの解放処理をリファクタで取り除かないでください。大きな活性部位モデルを伴う長時間の all ジョブは、これらがないと OOM します。

5.3 同梱 fork: 上流を並べてインストールしない

同梱された pysisyphus/thermoanalysis/ パッケージは fork です。pip install pysisyphus または pip install thermoanalysis を本パッケージとは別に PyPI 版を再インストールすると、次が気づかないうちに動かなくなります:

  • pysisyphus/irc/IRC.py — 初期変位のメモリ管理 + オプトインの require_pos_def_hessian kwarg

  • pysisyphus/optimizers/hessian_updates.py — GPU 常駐の in-place rank-two Bofill 更新、オプトインの PYSIS_BOFILL_CPU_OFFLOAD=1 フォールバック

  • pysisyphus/tsoptimizers/TSHessianOptimizer.py — RSIRFO kwargs(fork 間でホストパッケージの import パスが分岐)

  • pysisyphus/calculators/{Calculator,Dimer}.py — GPU 対応バックエンドフック(30 以上の QM バックエンドは削除済み。抽象 base + Dimer TS calculator のみ残存)

  • pysisyphus/_array.pyoptimizers/hessian_updates.py(および徐々に他のホットパスファイル)で使われる get_xp / _outer / _dot / _eigh shim

  • thermoanalysis/QCData.py — 上流との branding / I/O 差分

5.4 パッケージ変更には隔離インストール検証が必要

include glob(pdb2reaction*)は新しい層サブパッケージを自動探索するため、通常の内部ファイル追加で package discovery を変える必要はありません。package discovery や runtime dependency を変更する場合は、sdist と wheel を作成し、wheel の内容を検査し、クリーン環境でインストールした後に CLI smoke test を実行してください。これは付随的な refactor ではなく release contract の変更です。

5.5 _LAZY_SUBCOMMANDS レジストリは絶対パスを使う必要がある

pdb2reaction/cli/app.py:_LAZY_SUBCOMMANDS はすべてのサブコマンドを 絶対 モジュールパスで解決します。いずれかのエントリを相対 dotted import(".all" など)に戻すと、default_group.py が移動した際にサブコマンド探索が気づかないうちに動かなくなります。リゾルバの __package__ がパッケージルートから乖離するためです。


6. 同梱 fork(repo 内部)

pdb2reaction はリポジトリ最上位に 2 つ の repo 内部モジュールを同梱しています:

dir

upstream PyPI?

purpose

scope of edits allowed

pysisyphus/

NO — fork、pip install pysisyphus を並べないこと

optimizer, TS, IRC, COS, calculators

logic edit には再現された不具合または承認済み機能、focused regression test、該当する numerical/GPU benchmark が必要

thermoanalysis/

NO — fork(branding 差分)

ΔG, ZPE, 分配関数, QCData

logic edit には I/O または数値上の必要性と thermochemistry golden test が必要

各ディレクトリは分岐ファイルと touch 制限の境界を列挙した独自の README.md を持ちます。階層モデルから見ると、これらの fork は L1..L5 グラフの 外側 に位置します。どの階層も絶対パッケージパス(from pysisyphus.X import Y)でこれらを import でき、L1 L2 {L3, L4} L5 の方向を壊しません。


7. 推奨される深掘りの読み順

Fresh-eyes ツアー(§3)の後は、この深さ優先の読み順に従ってください:

  1. pdb2reaction/core/defaults.py — デフォルト値テーブルを頭に入れる; 下流のすべてはここから読みます。

  2. pdb2reaction/cli/app.py — Click root + _LAZY_SUBCOMMANDS レジストリ。

  3. pdb2reaction/workflows/all.py — 1 つの完全なパイプラインを上から下まで。

  4. pdb2reaction/workflows/extract.py — 活性部位クラスターキャップ。

  5. pdb2reaction/backends/__init__.py + base.py — MLIP ディスパッチャとバックエンドごとのアダプタ契約。

  6. pdb2reaction/workflows/tsopt.py — RS-P-RFO + Bofill scatter(CHEMISTRY-RULE:7)。

  7. pdb2reaction/workflows/freq.py — クラスターモデル上での振動解析。

  8. pdb2reaction/workflows/irc.py — VRAM 管理 + IRC 積分。

  9. pdb2reaction/workflows/dft.py — gpu4pyscf による一点 DFT(CHEMISTRY-RULE:4 + :5)。

  10. pdb2reaction/core/utils.py — 共有 PDB / XYZ / plot ヘルパー。