# ログと出力

本ガイドのあらゆる実行は、静かに**トランスクリプト** — システム要約、SCF サイクル表、収束チェック — を生んで
きました。本章はその出力を*見る・再生する・保存する*ことについてです:実況で流す、事後に描画する、機械可読の
データとして得る、そして計算全体をディスクに永続化する方法です。

## すべての背後にある 1 つのイベントストリーム

qc-rs のログと表示は**単一の構造化イベントストリーム**を共有します。実行が進むにつれ型付きイベント — *System*
要約、*plan*、サイクルごとの SCF レコード、*result* — を出し、出力を見るあらゆる方法は、その 1 つのストリームの
異なる描画にすぎません。だから実況ログ・再生トランスクリプト・生 JSON はすべて*同じ*情報を示します:同じイベントを
違う形式にしただけです。構造化されている（自由テキストの `print` ではない）ので、人間向けトランスクリプトとしても
**データ**としても扱えます。

## 実況ログ:`run(log=...)`

`.run()` は**既定で沈黙**します — 計算して返します。実行の進行に合わせてトランスクリプトを流すには **`log=`** を
渡します（[クイックスタート](../00-intro/quickstart.md) で導入）:

```python
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"

m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run(log="stdout")
```

`log=` の対象は次のいずれか:

| `log=` | 効果 |
|---|---|
| `"stdout"` | 実行進行に合わせてターミナル / ノートブックセルに流す |
| **ファイルパス** | 描画したトランスクリプトをそのファイルに書く |
| **ファイル様オブジェクト** | 渡した任意のストリームに書く |

さらに 2 つの制御が実況出力を整えます:

- **`log_style="modern"`**（既定）または **`"orca"`** — トランスクリプトの見た目のスタイル（コンパクトな
  モダンレイアウト、または ORCA 出力を思わせるもの）。
- **`plot=True`** — 実行中に SCF 収束プロットをインライン描画（matplotlib が必要;[可視化](visualization.md)参照）。

:::{tip} MPI 実行
並列実行では、`log_rank="root"`（既定）はランク 0 だけをログします;`"all"`, `"gather"`、またはランクのリストで
どのランクが報告するかを制御します。多ランク実行が出力で溢れるのを防ぎます。並列性そのものは
[Part IV](../30-hpc/index.md) です。
:::

## 完了した実行を再生する:`log()` と `show()`

トランスクリプトは**チェックポイントに保存**されるので、再計算なしに実行*後*に描画できます:

```python
m.log()                    # トランスクリプトをテキストで再生
m.log(format="markdown")   # … Markdown で（ノートブックで見やすい）
m.log(format="jsonl")      # … 1 行 1 JSON オブジェクトで
m.show("result")           # 現在の状態 + 結果の描画スナップショット
```

- **`log(format=...)`** は保存されたイベントストリームを再描画します — `"text"`（既定）, `"markdown"`,
  `"jsonl"`。
- **`show(...)`** は実行中のトランスクリプトではなく*スナップショット*（状態と結果）を描画します — 「この
  チェックポイントは今何を持っているか」を手早く見るのに便利です。

これらは*保存された*イベントを読むので、呼び出しにコストはかからず、毎回同一のトランスクリプトを返します —
出力をもう一度見るために `log="stdout"` で再実行する必要はありません。

## データとしての出力:`run_events()`

プログラム的な用途 — テスト、ダッシュボード、数値の収集 — には **`run_events()`** が、生のイベントストリームを
**JSON 文字列**のリスト（1 イベント 1 つ）として返します:

```python
import json
events = m.run_events()             # list[str]、各々 JSON オブジェクト
len(events)                         # 18

first = json.loads(events[0])       # System 要約イベント
first["event"]                      # 例 "system"
first["nao"], first["nuclear_repulsion"]   # 構造化フィールド、そのまま使える
[json.loads(e).get("kind") for e in events]   # 例 自動挿入された 'sad' 推定がここに現れる
```

各イベントは型を表す **`event`** フィールドと、その型の構造化ペイロード（原子リスト、基底サイズ、サイクルごとの
エネルギー…）を持つ dict です。これが人間向けトランスクリプトを描画する*元*のストリームなので、ログに表示される
ものはデータからも隠されていません。

## 計算の保存と読み込み

チェックポイント — 分子、現在の電子状態、結果、トランスクリプト — は **HDF5 `.qch5`** ファイルに永続化され、
問い合わせや拡張ができるチェックポイントとして読み戻せます:

```python
m.save("water.qch5")                 # チェックポイント全体を永続化
r = qc.chk.load("water.qch5")        # 復元

r.scf.energy                         # -76.026794   結果は往復で保持される
r.scf(xc="b3lyp").run()              # … 読み込んだチェックポイントにさらにステップを追加できる
```

`save()`/`load()` は作業を**中断・再開**する方法です:高価な SCF を一度走らせ、保存し、後で読み込んで物性計算・
最適化・再開する — 再計算なしです。（これは [初期推定の章](initial-guess.md) の `guess("read", source=...)` の
背後のファイルでもあります。）

## 総合例:静かに実行し、後で確認する

```python
import qc, json
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"

m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()   # 沈黙

m.log()                              # … 今トランスクリプトを描画
n_events = len(m.run_events())       # 18 個の構造化イベントがデータとして利用可能
m.save("water.qch5")                 # 次回のために保存
print("events:", n_events, "| E:", round(m.scf.energy, 6))
```

:::{exercise}
:label: ex-logging

1. `m = chk.scf(ref="r").run()`（`log=` なし）を実行し、今 SCF サイクル表を、もう一度 SCF を走らせる代償なしに
   見たいです。何を呼びますか。
2. 自分でプロットを作るため、Python スクリプトでサイクルごとのエネルギーが必要です。どのメソッドがデータを与え、
   どんな形式ですか。
3. 高価な最適化が終わりました。二度と繰り返さずに済ませる方法と、その収束軌道を後でより大きな基底の実行の初期
   推定として再利用する方法は。
:::

:::{solution} ex-logging
:class: dropdown

1. **`m.log()`** — チェックポイントに保存されたトランスクリプトを、計算コストなしで再生します（Markdown/JSONL
   なら `log(format=...)`）。
2. **`m.run_events()`** — JSON 文字列のリスト;各々を `json.loads` し、SCF サイクルイベント（`scf_iteration`）から
   サイクルごとのフィールドを取り出します。
3. **`m.save("opt.qch5")`** で永続化;後で `qc.chk.load("opt.qch5")` が結果を保ったまま復元します。軌道を再利用
   するには `qc.chk.new(..., ao="<より大きい>").guess("read", source="opt.qch5").scf(...).run()`。
:::

これで日常のワークフローは完結です。Part III の残りは大規模な[分子物性スイート](properties/index.md) — 収束した
波動関数を、電荷・結合次数・トポロジー解析、そして[可視化](visualization.md)で出会った実空間場に変えます。
