# tkfilter マニュアル

## 1. 役割と責務分離

`tkfilter` は、装置・ソフトウェア固有のデータreaderをpluginとして読み込み、入力ファイルに適合するpluginを優先順に選ぶパッケージです。

| 要素 | 責務 |
|---|---|
| filter plugin | 自分が読める形式か判定し、ファイルを読み、`inf` 辞書を返す |
| `tkFilterManager` | pluginのロード、優先順位、適合判定、複数ファイル処理 |
| アプリケーション | `inf` を解析・表示・保存する |

新規コードでは「pluginは読む、managerは探す、アプリは使う」という分離を維持します。

## 2. 推奨import

```python
from tkfilter import load_filters, read_data, read_files, tkFilterManager
```

## 3. 単一ファイル

```python
from tkfilter import read_data

inf = read_data(
    "sample.ras",
    plugin_dir="filter",
    manifest="filter/plugin_order.txt",
)
```

## 4. managerの再利用

```python
from tkfilter import load_filters

manager = load_filters(
    plugin_dir="filter",
    manifest="filter/plugin_order.txt",
    include_unlisted=False,
    is_print=False,
)

inf1 = manager.read_data("sample1.ras")
inf2 = manager.read_data("sample2.ras")
```

大量ファイルでは、毎回pluginをimportし直さずmanagerを再利用します。

## 5. 複数ファイル

```python
results = manager.read_files(
    "data/*.*",
    skip_temporary=True,
    skip_output=True,
    raise_error=False,
)
```

既定では、`~` で始まる一時ファイルと、名前に `-out.` を含む出力ファイルを除外します。

## 6. plugin優先順位

`manifest` / `order_file` に1行1pluginを書きます。

```text
# vendor-specific readers first
miniflex_ras2xrd.py
bruker2xrd.py

# generic readers last
txt2xrd.py
csv2xrd.py
```

- 空行と `#` コメントは無視されます。
- `.py` あり・なしを利用できます。
- 専用readerを先、汎用readerを後に置きます。
- `include_unlisted=True` のときだけ未掲載pluginを最後に追加します。
- manifest未指定では `plugin_dir/fmask` をファイル名順で読みます。

## 7. pluginの最小契約

新規pluginで必須に近い関数は次の2つです。

```python
def check_file_type(infile, inf=None, app=None, cparams=None):
    # 対応する場合は情報を返し、非対応ならNone
    return {"file_type": input_type}


def read_data(infile, app=None, cparams=None, is_print=False):
    return inf
```

任意フック:

```python
def initialize(app=None, cparams=None): ...
def update_vars(app=None, cparams=None): ...
def convert(inf, app=None, cparams=None): return inf
def get_input_type(inf=None, app=None, cparams=None): ...
def get_output_type(inf=None, app=None, cparams=None): ...
def get_output_path(infile, inf=None, app=None, cparams=None): ...
def print_data(inf, app=None, cparams=None): ...
def plot_data(inf, app=None, cparams=None): ...
def save_data(outfiles, inf, app=None, cparams=None, is_print=False): ...
```

通常の形式不一致では例外を出さず `None` を返します。対応形式と判定した後の読み込み失敗は、`ValueError` 等をraiseする設計を推奨します。

## 8. 標準 `inf` 辞書

単一region・単一spectrumの推奨形:

```python
inf = {
    "filename": str(infile),
    "sample_name": sample_name,
    "data_list_type": "[[x, y]]",
    "meta": meta,
    "labels": [[r"2$\theta$ (degree)", "Intensity"]],
    "data_list": [[x, y]],
    "nregion": 1,
    "nspectrum": 1,
    "ndata": len(x),
    "xmin": float(min(x)),
    "xmax": float(max(x)),
    "xstep": float(x[1] - x[0]),
    "yscale": "linear",
}
```

複数spectrumでは1region内を `[x, y1, y2, ...]`、複数regionでは `data_list` の外側listを増やします。特殊情報は `diffractions`, `conv_data` 等の追加キーに格納できます。

managerは辞書に `filter_name` と `file_type` がなければ補います。

## 9. `return_filter` とエラー制御

```python
inf, filt, file_type = manager.read_data(
    "sample.ras",
    return_filter=True,
    raise_error=True,
)
```

一致pluginがない場合:

- `raise_error=True`: `RuntimeError`
- `raise_error=False`: `None`（`return_filter=True` なら `(None, None, None)`）

## 10. 同梱plugin群

同梱ソースには次のカテゴリがあります。

- `alamode`: ALAMODE出力・構造変換
- `atlas`: Silvaco ATLASデータ
- `crystal`: JSON結晶構造→CIF
- `electrical`: 電気特性測定表
- `pes`: PES/HAXPES/ARPES
- `thermal`: 熱物性データ
- `vasp`: POSCAR、vasprun、DOS、band等
- `xrd`: 各社・各形式の粉末XRD
- `xrf`: XRF出力

個別pluginの正確な関数シグネチャと `input_type` / `output_type` はAPIリファレンスを参照してください。

## 10A. 同梱pluginカタログ（ソース定数から抽出）

| 分類 | plugin | 拡張子 | 入力形式 | 出力形式 |
|---|---|---|---|---|
| `alamode` | `alamode_property2xlsx` | `.in` | ALMODE anphon input .in | Excel .xlsx |
| `alamode` | `alamode_xyz2cif` | `.xyz` | ALAMODE .xyz | CIF |
| `atlas` | `atlas_dat2xlsx` | `.dat` | ATLAS output .dat | MS-Excel .xlsx |
| `atlas` | `atlas_dos2xlsx` | `.out` | ATLAS output .out | MS-Excel .xlsx |
| `atlas` | `atlas_tft2dat` | `.log` | ATLAS output .log | MS-Excel .xlsx |
| `atlas` | `atlas_tft2xlsx` | `.log` | ATLAS output .log | MS-Excel .xlsx |
| `crystal` | `json2cif` | `.json` | JSON crystal structure | CIF |
| `electrical` | `S-sigma-T2xlsx` | `.xlsx` | S-sigma-T .xlsx | MS-Excel .xlsx |
| `electrical` | `resitest8300_csv_T2xlsx` | `.csv` | Resitest8300 .csv | Hall T |
| `electrical` | `resitest8300_hdt_T2xlsx` | `.hdt` | Resitest8300 .hdt | Hall T |
| `electrical` | `resitest8400_T2xlsx` | `.xlsx` | Resitest8400 .xlsx | Hall T |
| `pes` | `MDR_haxpes2xlsx` | `.csv` | MDR HAXPES .csv | MSExcel .xlsx |
| `pes` | `PIX2xlsx` | `.xlsx` | Photo ionization cross-section HAXPES .xlsx | MS-Excel .xlsx |
| `pes` | `arpes_pxt2xlsx` | `.pxt` | KEK ARPES .pxt | MS-Excel .xlsx |
| `pes` | `spring8_haxpes2xlsx` | `.txt` | SPring-8 HAXPES .txt | MS-Excel .xlsx |
| `pes` | `vasp2pes` | `` | VASP output | PES |
| `thermal` | `picotr2xlsx` | `.csv` | CSV | MSExcel .xlsx |
| `vasp` | `poscar2cif` | `` | VASP POSCAR | CIF |
| `vasp` | `vasp2band` | `` | VASP output | Band |
| `vasp` | `vasp2cif` | `` | VASP POSCAR/vasprun.xml | CIF |
| `vasp` | `vasp2dos` | `` | VASP output | DOS |
| `vasp` | `vasp2dos - #U30b3#U30d4#U30fc` | `` | VASP output | DOS |
| `vasp` | `vaspkit2dos` | `.dat` | VASPKIT output | DOS |
| `vasp` | `vasprun2inputs` | `.xml` | vasprun.xml | VASP input files |
| `xrd` | `bruker2xrd` | `.txt` | Bruker Lynkx .txt | pXRD |
| `xrd` | `cif2xrd` | `.cif` | CIF | pXRD |
| `xrd` | `csv2xrd` | `.csv` | CSV | pXRD |
| `xrd` | `miniflex_ras2xrd` | `.ras` | MiniFlex .ras | pXRD |
| `xrd` | `pdf_xlsx2xrd` | `.xlsx` | PDF2 data copied to .xlsx | pXRD |
| `xrd` | `rietan_int2xrd` | `.int` | RIETAN .int | pXRD |
| `xrd` | `rietan_pat2xrd` | `.pat` | RIETAN .pat | pXRD |
| `xrd` | `rint2xrd` | `.asc` | RINT2000 .asc | pXRD |
| `xrd` | `topas_pro2xrd` | `` | TOPAS .pro | pXRD |
| `xrd` | `topas_txt2EVA` | `` | TOPAS .txt | EVA xy .txt |
| `xrd` | `topas_txt2xrd` | `` | TOPAS .txt | pXRD |
| `xrd` | `txt2xrd` | `.txt` | Text .txt | pXRD |
| `xrd` | `xlsx2xrd` | `` | MSExcel .xlsx | pXRD |
| `xrd` | `xy2xrd` | `.xy` | xy data .xy | pXRD |
| `xrf` | `bruker_tiger2xlsx` | `.txt` | Bruker S8 Tiger .txt | xlsx |

## 11. 互換性と依存関係

多くの旧pluginは `app=None`, `cparams=None` を保持し、旧 `tklib`、pymatgen、NumPy、SciPy、Matplotlib、pandas/openpyxl等に依存します。`tkfilter` コア自体と個別pluginの依存は分けて考えてください。

ファイル名にハイフンや空白を含むpluginは通常の `import package.module` に向きません。managerのファイルパスロードを使います。

## 12. 新規pluginテンプレート

```python
from pathlib import Path
import numpy as np

plugin_ver = "data:0.2"
default_ext = ".txt"
input_type = "Example text"
output_type = "[[x, y]]"


def check_file_type(infile, inf=None, app=None, cparams=None):
    path = Path(infile)
    if path.suffix.lower() != default_ext:
        return None
    with path.open("r", encoding="utf-8", errors="replace") as fh:
        first = fh.readline()
    if "EXAMPLE" not in first:
        return None
    return {"file_type": input_type}


def read_data(infile, app=None, cparams=None, is_print=False):
    data = np.loadtxt(infile, comments="#")
    if data.ndim != 2 or data.shape[1] < 2:
        raise ValueError("Expected at least two numeric columns")
    x = data[:, 0]
    y = data[:, 1]
    return {
        "filename": str(infile),
        "data_list_type": "[[x, y]]",
        "labels": [["x", "y"]],
        "data_list": [[x, y]],
        "nregion": 1,
        "nspectrum": 1,
        "ndata": len(x),
        "xmin": float(np.min(x)),
        "xmax": float(np.max(x)),
        "yscale": "linear",
    }
```

## 13. 生成AI向けコード生成ルール

1. アプリ側から個別pluginを直接import・総当たりせず、`read_data()` または `tkFilterManager` を使う。
2. pluginの検出順はmanifestに置き、pluginコードに優先順位を埋め込まない。
3. `check_file_type()` は非対応時に `None` を返す。通常の不一致を例外にしない。
4. `read_data()` は成功時に辞書を返し、`app.terminate()` を呼ばない。
5. 旧pluginの戻り値・依存を勝手に一括変更しない。
6. `inf["data_list"]` のnestingと `labels` のnestingを対応させる。
7. `app` / `cparams` は互換引数として保持し、不要なら無視する。
8. 一般形式readerはmanifest後方に置く。
9. plugin固有の出力ファイル名や変換仕様は、そのpluginの実装・APIリファレンスで確認する。
