二電子積分と J/K 戦略#
量子化学計算で最大の性能レバーは、二電子積分をどう扱うかです。それらは \(\mathcal{O}(N^4)\) 個あり、各 SCF
サイクルがそれらをクーロン(J)と交換(K)行列に縮約します。qc-rs はその選択を単一のキーワード
ints(eri=...) で公開し、その選択は前 3 章のあらゆる並列レベルと相互作用します。本章は、正しいものを選べるよう
選択肢を地図化します。
理論:なぜ積分の扱いが支配するのか#
\(N\) 個の基底関数に対し、一意な二電子積分 \((\mu\nu|\lambda\sigma)\) は形式上 \(N^4/8\) 個あります。\(N = 500\) で 約 80 億個 — しばしば保存するには多すぎ、再計算するには高価です。以降のあらゆる手法(SCF, MP2, …)はここで 律速されるので、戦略の問いは実際には:保存するか、再計算するか、ディスクに置くか、近似するか? です。各答えは メモリと CPU 時間を異なる形でトレードオフするので、単一の最良の選択はありません — 分子とハードウェア次第です。
統一された eri= 軸#
すべては ints(...) の 1 キーワードで、2 つのファミリに分かれます。
4 中心ファミリ — 厳密な積分#
真の \((\mu\nu|\lambda\sigma)\) 積分を使い(近似なし)、ビット単位で同一のエネルギーを与え、積分がどこに あるかだけが異なります:
|
戦略 |
適する場面 |
|---|---|---|
|
メモリ梯子を歩く:incore → ramdisk → disk → direct |
とりあえず動く |
|
全積分を RAM に保存 |
小〜中、RAM に余裕(最速) |
|
毎サイクル積分を再計算、何も保存しない |
大、RAM 制限 |
|
積分を一度ディスクに書き、毎サイクルストリーム |
アウトオブコア、速いスクラッチディスク |
|
疎ディスク符号化を RAM に保持 |
大/疎、MPI ランクに分散 |
4c-auto は安全な既定 — 収まる最もメモリを食う選択肢を選び、優雅にフォールバックします。RI(下記)を決して
選びません;RI は明示的にのみ選ばれます。
RI(密度フィッティング)ファミリ — 近似、はるかに安価#
RI(恒等分解、ポスト SCF の章 より)は 4 指標積分を補助基底を介して分解し、 小さく制御された フィッティング誤差と引き換えにコストを劇的に削ります。2 つの副ファミリは、フィットした factor を 保存するか再計算するかで異なります:
|
戦略 |
注記 |
|---|---|---|
|
白色化 RI factor を一度構築し RAM に保持(MPI で μ 分散) |
incore 速度・recompute メモリ — 規模での store- |
|
毎サイクル 3 中心積分を再計算、factor を保存しない |
メモリ倹約の RI |
|
再計算 + より小さな占有 factor をディスクに書く |
アウトオブコア RI |
GPU メンバー(ri-cuda, ri-recomp-cuda, ri-ram-cuda)は GPU の章 にあります。RI は補助基底を
必要とします;rijk= を与えないと、qc-rs は軌道基底から既定の JK-fit 補助を自動導出します。
厳密 対 RI:フィッティング誤差、検証#
4 中心戦略は厳密で最後の桁まで一致します;RI 戦略は小さなフィッティング誤差を持ちますがはるかに安価です。 水/cc-pVDZ(RHF)で:
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
for eri in ("4c-incore", "4c-direct", "ri-ram", "ri-recomp"):
e = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom",
rijk="cc-pvdz-jkfit").ints(eri=eri).scf(ref="r").run().scf.energy
print(f"{eri:11} {e:.6f}")
# 4c-incore -76.026794 ← 厳密
# 4c-direct -76.026794 ← 厳密(incore とビット単位で同一)
# ri-ram -76.026773 ← RI: +2.1e-5 フィッティング誤差
# ri-recomp -76.026773 ← RI: ri-ram と同一
2 つの 4 中心戦略はビット単位で同一(−76.026794);2 つの RI 戦略は互いに同一(−76.026773)で、厳密とは わずか 2.1 × 10⁻⁵ Ha だけ異なります — 化学的精度をはるかに下回る RI フィッティング誤差です。だから RI は 無視できる制御された誤差で、大きな速度/メモリの利得を買います。
戦略の選び方#
実務的な決定ガイド:
既定 / 不明 →
4c-auto。うまく選び、RAM が厳しければフォールバック。小〜中、RAM に余裕、厳密で最速が欲しい →
4c-incore。大、RAM 制限、厳密が欲しい →
4c-direct(再計算)または4c-disk(アウトオブコア)。大、大きな速度/メモリの利得が欲しく ~1e-5 Ha を許容できる → RI ファミリ(factor が RAM に収まれば
ri-ram、収まらなければri-recomp)。規模での通常の選択で、RI-MP2 の唯一の経路。GPU 利用可能 →
4c-cuda/ri-cuda。
RI は MPI とも合成して 1 ノードのメモリの壁を破ります(ri-ram は factor を AO 番号で分散するので、ランク
あたりメモリ ≈ 全体 / ランク数、MPI の章)。
補助基底と関連ノブ#
qc.chk.new(..., rijk="cc-pvdz-jkfit") # RI J/K の JK-fit 補助(既定は自動導出)
qc.chk.new(..., ric="cc-pvdz-ri/mp2fit") # RI-MP2 の相関フィット補助(ポスト SCF の章)
eri="none" は二電子構築を完全に飛ばします(1 電子のみのワークフロー用)。アウトオブコアのノブ(疎レイアウト、
ドロップ許容差、I/O モード)は integral.disk.* 下の iop キーです — IOP リファレンス
参照。
総合例#
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
exact = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").ints(eri="4c-incore").scf(ref="r").run()
ri = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom",
rijk="cc-pvdz-jkfit").ints(eri="ri-ram").scf(ref="r").run()
print(f"exact (4c) : {exact.scf.energy:.6f}") # -76.026794
print(f"RI : {ri.scf.energy:.6f}") # -76.026773
print(f"fitting err: {abs(exact.scf.energy - ri.scf.energy):.2e} Ha") # 2.1e-05
練習 20
大きな分子の cc-pVTZ 計算が
4c-incoreでメモリ不足エラーで落ちます。走らせられる異なるeri=戦略を 2 つ、 各々のトレードオフとともに挙げなさい。ri-ramとri-recompが互いに同じエネルギーを与えるのに、4c-incoreとはわずかに異なるのはなぜですか。RI-MP2 を走らせたいです。どの
eri=ファミリが必須で、どの追加の基底キーワードを設定せねばなりませんか。
解答 練習 20
(a)
4c-direct— 積分を毎サイクル再計算(厳密、保存なし、ただしサイクルあたり CPU が増える);(b)ri-ramまたはri-recomp— 密度フィッティング(はるかに少ないメモリと CPU、~1e-5 Ha のフィッティング誤差で)。 速いスクラッチディスクがあれば4c-diskも第 3 の選択肢。どちらも RI(密度フィッティング) — 同じ補助基底近似を使うので同じフィッティング誤差を共有します; フィットした factor を保存するか再計算するかだけが異なり、結果は変わりません。
4c-incoreは厳密な積分を 使うので、そのフィッティング誤差がありません。RI ファミリが必須です(RI-MP2 に非 RI 経路はない)。JK-fit の
rijk=とは別に、ric=相関フィット 補助基底(例ric="cc-pvdz-ri/mp2fit")を設定せねばなりません。
これで Part IV は完結です。qc-rs 計算を CPU スレッド・クラスタ上の MPI ランク・NVIDIA GPU にわたって スケールさせ、分子とハードウェアに合った積分戦略を選べます。ここから、リファレンス の各節が、あらゆる入力とオプションの正確な詳細を与えます。