# アーキテクチャ設計 (Architecture)

## 概要 (Overview)

SmTIAS-LightSim は次の 2 段階を独立した CLI として提供する．

1. **render**: シーン定義 YAML + STL メッシュを入力に，Mitsuba 3 で擬似撮影画像（PNG）と輝度マップを出力する．
2. **evaluate**: PNG または輝度マップ + ROI 設定を入力に，SmTIAS-Evaluation 互換の均一性指標（CSV）と可視化（PNG）を出力する．

evaluate を独立させることで，**実測 PNG（SmTIAS で撮影したもの）にも同じコマンドが使える**．シミュレーションと実測を同じ物差しで比較できる．

## 全体パイプライン (Pipeline)

```
                         [Fusion 360]
                              ↓ STL export
    ┌─────────────────────────────────────────────────────────────┐
    │  configs/                                                  │
    │   ├── scene_baseline.yaml   (光源・材料・センサ・評価面定義) │
    │   └── meshes/                                              │
    │       └── *.stl                                            │
    └─────────────────────────────────────────────────────────────┘
                              ↓
    ┌─────────────── render ──────────────┐
    │  scene/builder  →  Mitsuba scene    │
    │  render/runner  →  mi.render(...)   │
    │  render/output  →  PNG / luminance  │
    └──────────────────────────────────────┘
                              ↓
                  output/<scene-id>/
                    ├── render.png
                    └── luminance.npy
                              ↓
    ┌─────────────── evaluate ────────────┐
    │  evaluation/roi      →  ROI抽出     │
    │  uniformity.py       →  CoV / std    │
    │  evaluation/zones    →  3ゾーン      │
    │  evaluation/radial   →  放射プロファ  │
    │  evaluation/visualize → 可視化PNG    │
    └──────────────────────────────────────┘
                              ↓
                  output/<scene-id>/
                    ├── metrics.csv
                    └── figures/
                        ├── luminance_map.png
                        ├── histogram.png
                        ├── radial_profile.png
                        └── zone_map.png
```

## モジュール構成 (Modules)

```
src/sm_tias_lightsim/
├── __init__.py
├── geometry/
│   ├── loader.py        # trimesh で STL 読込・三角形配列取得
│   ├── subdivide.py     # 三角形メッシュ均一分割（最大辺長以下に決定論的に分割）
│   └── transforms.py    # 単位変換 (mm→m)・座標系変換
├── scene/
│   ├── schema.py        # YAML スキーマ定義・バリデーション
│   ├── builder.py       # YAML → Mitsuba scene dict 変換の本体
│   ├── lights.py        # LED 光源 (point / area / spot) のシーン要素生成
│   ├── materials.py     # BSDF（拡散・誘電体・薄膜散乱等）の生成
│   ├── sensors.py       # カメラセンサ (perspective / orthographic) の生成
│   └── paths.py         # YAML からの相対メッシュパス（スイープ生成スクリプト用）
├── render/
│   ├── runner.py        # mi.set_variant（ensure_variant）+ mi.render の実行
│   ├── output.py        # HDR film → PNG / luminance.npy 出力
│   └── pipeline.py      # YAML → レンダ → 保存の一括処理（render_yaml．CLI とスイープの共通本体）
├── runtime/
│   ├── llvm_init.py     # LLVM JIT 初期化（DRJIT_LIBLLVM_PATH 自動探索・variant 経路ログ）
│   └── overrides.py     # 環境変数による実行時上書き（SM_RENDER_VARIANT / SM_DRJIT_THREADS）
├── remote/
│   ├── commands.py      # サーバで実行するシェルコマンドの組み立て（純関数）
│   ├── files.py         # push の送信ファイル選定・除外規則・prune 対象の算出
│   └── transfer.py      # tar over ssh の送受信（pull 展開の安全検査）
├── uniformity.py        # SmTIAS-Evaluation からコピー（Rec.709, CoV, max/min）
├── evaluation/
│   ├── roi.py           # 画像 ROI 抽出（image_pixel 型）
│   ├── zones.py         # 3 ゾーン分析（center / middle / periphery）
│   ├── radial.py        # 放射状プロファイル + 動径 min/max 比
│   ├── oetf.py          # sRGB OETF 順方向適用 + 露出アンカー（シミュ用）
│   ├── normalize.py     # linear mean anchor 正規化（OETF と排他）
│   ├── dng_loader.py    # DNG（RAW）読込（linear raw 取得，実測リファレンス用）
│   ├── shape_metrics.py # 形状指標（輝度重心 / 角度-放射分解 / PCA / SSIM / NCC）
│   ├── visualize.py     # matplotlib による可視化 PNG 出力
│   └── pipeline.py      # 画像 + 評価 YAML → 指標 dict + metrics.csv（+ figures/）の一括処理（evaluate_image）
├── visualize/
│   ├── baker.py         # 各 face 中心の照度ベイク（direct / path モード）
│   ├── projection.py    # orthographic センサ投影の純関数（評価板への輝度投影）
│   └── viewer.py        # PyVista による 3D インタラクティブビューア
└── cli/
    ├── render.py        # `sm-render` エントリポイント
    ├── evaluate.py      # `sm-evaluate` エントリポイント
    ├── visualize.py     # `sm-visualize` エントリポイント
    └── remote.py        # `sm-remote` エントリポイント（研究室サーバでの実行）
```

## 各層の責務 (Module Responsibilities)

| 層 | 責務 | 主要な型・出入力 |
| --- | --- | --- |
| `geometry` | STL を読み，Mitsuba が扱える単位・座標系に揃える．メッシュの妥当性チェック | 入力: STL ファイルパス．出力: 頂点・面配列 + 単位情報 |
| `scene/builder` | YAML 設定全体を Mitsuba scene dict に変換するエントリ．他のサブ層を呼ぶ | 入力: YAML dict．出力: Mitsuba scene |
| `scene/lights` | YAML 光源定義 → Mitsuba の `point` / `area` / etc. emitter | 1 LED 定義 → 1 emitter |
| `scene/materials` | YAML 材料定義 → Mitsuba BSDF（`diffuse` / `dielectric` / `thindielectric`） | 1 材料定義 → 1 BSDF |
| `scene/sensors` | YAML センサ定義 → Mitsuba `perspective` / `orthographic` センサ | 1 センサ定義 → 1 sensor．orthographic は照度マップ取得用 |
| `geometry/subdivide` | 三角形メッシュ → 最大辺長以下に均一分割した三角形メッシュ | 決定論的（同一入力→同一出力）．ベイクの空間解像度を均一化 |
| `render/runner` | Mitsuba scene + パラメータ → 輝度配列 | spp / max_depth / variant 切替 |
| `render/output` | 輝度配列 → PNG / luminance.npy ファイル | tone mapping 等の選択肢を持つ |
| `render/pipeline` | シーン YAML + 出力先 → render.png / luminance.npy | variant 解決（引数 > 環境変数 > YAML）込み．スイープからプロセス内で直接呼べる |
| `runtime/llvm_init` | LLVM JIT 環境初期化 | `DRJIT_LIBLLVM_PATH` 自動探索（Windows / Ubuntu・Debian の `libLLVM-<版>.so` / Fedora 形式）+ variant 経路の INFO/WARNING ログ |
| `runtime/overrides` | 環境変数による実行時上書きの解決と適用 | `SM_RENDER_VARIANT`（variant．`sm-render --variant` が優先）/ `SM_DRJIT_THREADS`（Dr.Jit スレッド数上限．不正値は ValueError） |
| `scene/paths` | メッシュの絶対パス → YAML からの相対パス（POSIX 区切り） | 手元とサーバで同じ YAML を読めるようにする．別ドライブで相対化できないときは警告して絶対パス |
| `remote/commands` | 設定 → サーバで実行するシェルコマンド文字列 | 純関数．クォート・ジョブ名/GPU の検証・終了コード 90〜93 |
| `remote/files` | 手元の作業ツリー → 送信ファイル一覧 / prune 対象 | 除外規則．output/ は送らず，prune は src/ tests/ scripts/ のみ |
| `remote/transfer` | ファイル一覧 ⇄ tar ストリーム | pull 展開は要求パス外・不正な名前・リンクを拒否（パストラバーサル対策） |
| `evaluation/roi` | 画像 + ROI 定義 → ROI 内の輝度配列 | image_pixel 型（scene_face は将来実装） |
| `uniformity` | 輝度配列 → 均一性指標 dict | SmTIAS-Evaluation 仕様準拠 |
| `evaluation/zones` | 輝度配列 → 中心 / 中間 / 周辺の各統計 | 距離正規化を内部で行う |
| `evaluation/radial` | 輝度配列 → 中心からの距離 vs 平均輝度 + 動径 min/max 比 | プロット用データ |
| `evaluation/oetf` | シミュ linear 輝度 → sRGB 符号化値（露出アンカー） | シミュのみに適用．実測 PNG には適用しない |
| `evaluation/normalize` | linear 輝度 → mean を target_mean に揃えた輝度 | 線形ゲイン．形状指標は不変．OETF と排他 |
| `evaluation/dng_loader` | DNG（RAW）→ linear raw 配列 | 実測リファレンスの linear 取得 |
| `evaluation/shape_metrics` | 輝度配列（+ 任意の参照画像）→ 形状指標 | 輝度重心 / 角度-放射分解 / PCA / SSIM / NCC |
| `evaluation/fullimage` | 輝度配列 → 画像全体のノイズ補正付き min/max 比 | 方位に依らない．σ 平滑化 + ノイズ場の極値で補正．純関数 |
| `evaluation/visualize` | 各種統計 → matplotlib 図 PNG | 可視化のみ．データ加工はしない |
| `evaluation/pipeline` | 画像 + 評価 YAML + 出力先 → 指標 dict（metrics.csv を書く） | `save_figures=False` で図を省略．段階別の `EvaluationStepError` を投げる |
| `visualize/baker` | シーン + 光源 → 各 mesh face 中心の照度（W/m²） | direct（直接光）/ path（多重反射込み）モード |
| `visualize/projection` | orthographic センサ + 評価板メッシュ → 頂点 / テクスチャ投影 | 純関数．画像 ↔ 物理の向き整合 |
| `visualize/viewer` | シーン + 照度 → PyVista インタラクティブ 3D ウィンドウ | メッシュ着色 / 光源マーカー / センサ視線 / スライド表示スタイル |
| `cli/render`, `cli/evaluate`, `cli/visualize` | 各ステップの CLI ラッパー | 引数パースと例外 → 「エラー: …」+ 終了コード 1 の変換だけ．本体は `render/pipeline`・`evaluation/pipeline` |
| `cli/remote` | 研究室サーバでの実行（push / run / status / logs / attach / kill / pull） | ssh の起動・サーバ上での実行検出．使い方は ENV_04「研究室サーバでの実行」 |

## エントリポイント (CLI Entry Points)

`pyproject.toml` の `[project.scripts]` に登録する：

```toml
[project.scripts]
sm-render = "sm_tias_lightsim.cli.render:main"
sm-evaluate = "sm_tias_lightsim.cli.evaluate:main"
sm-visualize = "sm_tias_lightsim.cli.visualize:main"
sm-remote = "sm_tias_lightsim.cli.remote:main"
```

使い方：

```powershell
# シミュ実行
uv run sm-render --scene configs/scene_baseline.yaml --output output/baseline/

# 評価（シミュ画像）
uv run sm-evaluate --image output/baseline/render.png --config configs/eval_default.yaml --output output/baseline/

# 評価（実測画像）
uv run sm-evaluate --image data/whiteboard_real.png --config configs/eval_default.yaml --output output/real/

# 3D 照度ビューア（キャリブレーション中の調査ツール）
uv run sm-visualize --scene configs/scene_baseline.yaml                       # インタラクティブウィンドウを開く
uv run sm-visualize --scene configs/scene_baseline.yaml --screenshot vis.png  # PNG 保存のみ

# 研究室サーバ（既定 Newton）で回す．手元から送って tmux で実行し，結果を持ち帰る
uv run sm-remote push
uv run sm-remote run --name baseline --gpu 0 -- sm-render --scene configs/scene_baseline.yaml --output output/baseline/
uv run sm-remote pull baseline
```

`sm-render --variant cuda_ad_rgb`（または環境変数 `SM_RENDER_VARIANT`）で YAML の `render.variant` を上書きできる．`sm-remote run` は既定で `cuda_ad_rgb` を渡す．詳細は ENV_04「研究室サーバでの実行」を参照．

`sm-visualize` は上記の他に，ベイク方式（`--bake-mode direct|path`）・平滑化（`--bake-smooth` / `--bake-smooth-radius` / `--bake-denoise`）・三角形分割（`--bake-subdivide`）・キャッシュ（`--bake-cache`）・発表スライド向け表示（`--style slide` / `--cmap`）・マーカー非表示（`--no-markers` 等）など多数のオプションを持つ．全フラグは `uv run sm-visualize --help` を参照．

## ディレクトリ構成（プロジェクトルート）

```
SmTIAS-LightSim/
├── src/sm_tias_lightsim/   # 上述
├── configs/
│   ├── scene_baseline.yaml
│   ├── eval_default.yaml
│   └── meshes/             # STL ファイル群
├── output/                 # シミュ・評価結果（.gitignore 対象）
├── tests/
│   ├── test_geometry.py
│   ├── test_scene_builder.py
│   ├── test_uniformity.py
│   └── fixtures/
└── docs/
```

## キャリブレーション戦略 (Calibration)

実測値（CoV ≈ 0.0426，勾配 ≈ 6%）との一致を確認するための運用：

1. 現行 SmTIAS の構成を `scene_baseline.yaml` に再現
2. `sm-render → sm-evaluate` で指標を算出
3. 実測値と比較し，差分が大きい場合は次の順で疑う：
   - ① 光源の配光（Lambertian / cosine 分布の前提が妥当か）
   - ② 材料パラメータ（拡散反射率・透過散乱）
   - ③ ジオメトリの単位・座標
   - ④ サンプル数（spp）が不足していないか
4. 一致した時点でキャリブレーション完了．以降の改良案はこのベースラインからの相対比較で評価

許容範囲の数値は基準設計のレンダリング結果を見て確定する（`PLAN_02_開発ステップ.md` 内で定義）．

## 既知の前提 (Assumptions)

- 入力 STL は閉じたメッシュ（モンテカルロ光線が漏れない）であること
- 単位は YAML 側で明示する（`unit: mm`）．Fusion 360 の既定 mm 出力を想定
- LED の配光は当初 Lambertian で仮置きし，必要に応じて IES データに置換可能な構造にしておく
- 色再現性は将来課題（要件定義書のスコープ外）
