"""
XRD_GUI_libモジュール
概要: XRD GUIアプリケーションのコア機能を提供するライブラリ。
詳細説明:
このモジュールは、X線回折(XRD)データを読み込み、解析し、
結晶構造情報(CIFファイルなど)から理論的な回折パターンを生成するための
主要なインターフェースを提供します。
ファイルフィルタープラグインの動的なロードと適用を通じて、
多様なデータ形式に対応します。
関連リンク: :doc:`XRD_GUI_lib_usage`
"""
import os
import sys
import glob
import numpy as np
from tklib.tkapplication import tkApplication
print()
print("XRD_GUI_lib loaded")
[ドキュメント]
def error_message(message: str):
"""
概要: エラーメッセージをコンソールに出力する。
詳細説明:
指定されたエラーメッセージを整形し、標準出力に表示する。
これは主に、アプリケーションの初期設定やクリティカルな問題が発生した際に
ユーザーに情報を提供するために使用される。
:param message: 表示するエラーメッセージの文字列。
:type message: str
:returns: None
:rtype: None
"""
print()
print("#############################################")
print(f"Error in XRD_GUI_lib: {message}")
print("#############################################")
print()
root_dir = os.getenv('tkprog_X_path', None)
if root_dir is None:
error_message("Environment variable tkprog_X_path must be specified")
input("Pree ENTER to terminate>>\n")
exit()
filter_dir = os.path.join(root_dir, "xrd", "filter")
app = tkApplication()
cparams = app.get_params()
cparams.debug = 0
cparams.findvalidstructure = True
cparams.plugin_dir = 'filter'
cparams.mode = 'plot'
cparams.infile = '*.txt'
cparams.cif_files = 'data/*.*'
cparams.beam = 'X-ray'
cparams.wavelength = "CuKa"
cparams.xmin = 20.0
cparams.xmax = 120.0
cparams.xstep = 0.02
cparams.fwhm = 0.2
cparams.Gfraction = 0.5
cparams.fwhm_smear = 0.0
cparams.Gfraction_smear = 0.0
cparams.yscale = 'linear'
cparams.BGorder = 3
cparams.alpha = 0.1
print(f"Load modules from {filter_dir}")
module_names, modules = app.load_modules(filter_dir, "*.py", target = "read_data", is_print = True)
for m in modules:
input_type = m.get_input_type(app = app, cparams = cparams)
output_type = m.get_output_type(app = app, cparams = cparams)
print(f" {m.name}: input_type={input_type} output_type={output_type}")
[ドキュメント]
def set_two_theta_range(xmin=None, xmax=None, xstep=None):
"""
概要: 2θ範囲とステップ幅を設定する。
詳細説明:
GUIアプリケーションで使用される2θ(回折角)の最小値、最大値、
およびステップ幅をグローバルな設定パラメータ `cparams` に設定する。
各引数に `None` が指定された場合、そのパラメータは変更されない。
入力値は浮動小数点数として解釈される。
:param xmin: 2θの最小値。Noneの場合は変更しない。
:type xmin: float or None
:param xmax: 2θの最大値。Noneの場合は変更しない。
:type xmax: float or None
:param xstep: 2θのステップ幅。Noneの場合は変更しない。
:type xstep: float or None
:returns: None
:rtype: None
"""
global cparams
if xmin is not None:
cparams.xmin = float(xmin)
if xmax is not None:
cparams.xmax = float(xmax)
if xstep is not None:
cparams.xstep = float(xstep)
[ドキュメント]
def set_wavelength(wavelength=None):
"""
概要: 使用するX線波長を設定する。
詳細説明:
GUIアプリケーションで使用されるX線の波長をグローバルな設定パラメータ
`cparams` に設定する。`None` が指定された場合、波長は変更されない。
入力値は文字列として格納される。
:param wavelength: 設定するX線波長の名称(例: "CuKa")。Noneの場合は変更しない。
:type wavelength: str or None
:returns: None
:rtype: None
"""
global cparams
if wavelength is not None:
cparams.wavelength = str(wavelength)
[ドキュメント]
def parse_xrd(path: str):
"""
概要: 指定されたパスのXRD実験データを読み込み、解析する。
詳細説明:
`filter_dir` で指定されたディレクトリからロードされたフィルターモジュールを走査し、
入力ファイルのタイプに合致する最初のモジュールを使用してデータを読み込む。
読み込んだデータはサンプル名、2θ値(またはQ2値)、および強度値のNumPy配列として返す。
:param path: 読み込むXRDデータファイルのパス。
:type path: str
:returns:
サンプル名、2θ(またはQ2)値の配列、観測強度値の配列。
- `sample_name` (str): サンプル名。
- `xQ2_infile` (numpy.ndarray): 2θまたはQ2値のNumPy配列。
- `yobs_infile` (numpy.ndarray): 観測強度値のNumPy配列。
:rtype: tuple[str, numpy.ndarray, numpy.ndarray]
:raises ValueError: 対応するファイルタイプを見つけられない場合、またはファイルの読み込みに失敗した場合。
"""
print(f"Read {path} in XRD_GUI_lib.parse_xrd() using filters in {filter_dir}")
# raise ValueError("")
module = None
for i in range(len(modules)):
name = module_names[i]
m = modules[i]
file_type = m.check_file_type(path, app = app, cparams = cparams)
print(f"try [{name}] for [{path}]: file_type={file_type}")
if file_type is not None and 'Error' not in file_type:
print(" type matched.")
module = m
break
if module is None:
raise ValueError(f"Failed to find modules in {filter_dir}")
# return None, None, None
inf = module.read_data(path, app = app, cparams = cparams)
if not inf:
raise ValueError(f"Failed to read {path} by modules in {filter_dir}")
# module_input.print_data(inf_input)
inf_input = module.convert(inf, cparams = cparams)
data_list = inf_input["data_list"][0]
if type(data_list[0]) is float or type(data_list[0]) is int:
data_list = inf_input["data_list"]
sample_name = inf_input["sample_name"]
xQ2_infile = data_list[0]
yobs_infile = data_list[1]
# if len(inf_input["data_list"]) >= 3:
# ysim_infile = inf_input["data_list"][2]
# else:
# ysim_infile = None
# print("xQ2_infile=", xQ2_infile)
# print("yobs_infile=", yobs_infile)
return sample_name, np.array(xQ2_infile), np.array(yobs_infile)
def _to_hkl_str_and_raw(hkl: tuple):
"""
概要: HKL指数タプルを "(h k l)" 形式の文字列と生のインデックス辞書に変換する。
詳細説明:
pymatgenのようなライブラリで使用されるHKL指数タプル(例: (h, k, l) や (h, k, i, l))を受け取り、
人間が読みやすい "(h k l)" または "(h k i l)" 形式の文字列と、
h, k, l, i の各指数を含む辞書形式の生のインデックスを返す。
変換に失敗した場合は、元のタプルを文字列化したものとデフォルトの辞書を返す。
:param hkl: HKL指数を表すタプル。3指数または4指数が期待される。
:type hkl: tuple
:returns:
HKL指数を表す文字列と、各指数を含む辞書。
- `hkl_str` (str): HKL指数を表現する文字列(例: "(1 0 0)")。
- `raw_indices` (dict): 'h', 'k', 'l', 'i' のキーを持つ辞書。
:rtype: tuple[str, dict]
"""
try:
if len(hkl) == 3:
h, k, l = int(hkl[0]), int(hkl[1]), int(hkl[2])
return f"({h} {k} {l})", {"h": h, "k": k, "l": l, "i": -(h + k)}
elif len(hkl) == 4:
h, k, i_val, l = int(hkl[0]), int(hkl[1]), int(hkl[2]), int(hkl[3])
return f"({h} {k} {i_val} {l})", {"h": h, "k": k, "l": l, "i": i_val}
except:
pass
return str(hkl), {"h": 0, "k": 0, "l": 0, "i": 0}
[ドキュメント]
def parse_reference(path: str, xmin: float = None, xmax: float = None, xstep: float = None, wavelength: str = None, normalize: bool = True):
"""
概要: CIFファイルなどの参照ファイルから理論的なXRD回折ピーク情報を生成する。
詳細説明:
指定された参照ファイルパス(例: CIFファイル)を読み込み、
X線回折シミュレーションに基づいて理論的な回折ピークの位置、強度、
および対応するHKL指数を抽出する。
2θ範囲、X線波長、強度の正規化(0-1の範囲)をオプションで設定できる。
これは主にGUIで参照パターンを表示するために使用される。
:param path: 読み込む参照ファイルのパス(例: CIFファイル)。
:type path: str
:param xmin: 2θの最小値。Noneの場合は現在の設定を使用。
:type xmin: float or None
:param xmax: 2θの最大値。Noneの場合は現在の設定を使用。
:type xmax: float or None
:param xstep: 2θのステップ幅。Noneの場合は現在の設定を使用。
:type xstep: float or None
:param wavelength: 使用するX線波長の名称(例: "CuKα1 (1.5406 Å)")。Noneの場合は現在の設定を使用。
:type wavelength: str or None
:param normalize: 強度を0から1の範囲で正規化するかどうか。デフォルトはTrue。
:type normalize: bool
:returns:
参照パターンに関する情報。
- `reference_name` (str): 参照元のファイル名(拡張子なし)。
- `positions` (list[float]): 回折ピークの2θ(またはQ2)位置のリスト。
- `intensities` (list[float]): 回折ピークの相対強度のリスト。正規化されている場合がある。
- `hkls` (list[str]): 各ピークに対応するHKL指数の文字列リスト(例: "(1 0 0)")。
- `raw_indices` (list[dict]): 各ピークに対応するHKL指数の生の辞書リスト(例: `{'h': 1, 'k': 0, 'l': 0, 'i': -1}`)。
:rtype: tuple[str, list, list, list, list]
:raises ValueError: 対応するファイルタイプを見つけられない場合、またはファイルの読み込みに失敗した場合。
"""
# GUIから範囲指定があればここで反映
if xmin is not None or xmax is not None or xstep is not None:
set_two_theta_range(xmin=xmin, xmax=xmax, xstep=xstep)
if wavelength is not None:
set_wavelength(wavelength=wavelength)
print(f"Read {path} in XRD_GUI_lib.parse_reference() using filters in {filter_dir}")
module = None
for i in range(len(modules)):
name = module_names[i]
m = modules[i]
file_type = m.check_file_type(path, app=app, cparams=cparams)
print(f"try [{name}] for [{path}]: file_type={file_type}")
if file_type is not None and 'Error' not in file_type:
print(" type matched.")
module = m
break
if module is None:
raise ValueError(f"Failed to find modules in {filter_dir}")
inf = module.read_data(path, app=app, cparams=cparams)
if not inf:
raise ValueError(f"Failed to read {path} by modules in {filter_dir}")
# GUI から渡された波長("CuKα1" など)を内部用の形式("CuKa")に変換
if wavelength:
# GUI側の文字列に対応させるマッピング
wl_map = {
"CuKα1 (1.5406 Å)": "CuKa",
"CuKα 平均 (1.5418 Å)": "CuKa",
"CuKα2": "CuKa2",
"CuKβ": "CuKb"
}
internal_wl = wl_map.get(wavelength, "CuKa")
set_wavelength(wavelength=internal_wl)
inf_input = module.convert(inf, cparams=cparams)
print(f"DEBUG: inf_input keys = {inf_input.keys()}")
diff = inf_input.get("diffractions", {})
# キーが 'Q2' ではない可能性を考慮してフォールバックを用意
positions = diff.get("Q2", diff.get("2theta", diff.get("angles", [])))
intensities = diff.get("intensity", diff.get("I", []))
hkls_raw = diff.get("hkl", [])
if len(positions) == 0:
print(f"[WARN] XRD_GUI_lib: No peaks found in 'diffractions'. Available keys: {diff.keys()}")
# hkls を文字列化し raw_indices も作る
hkls = []
raw_indices = []
for hkl in hkls_raw:
s, r = _to_hkl_str_and_raw(hkl)
hkls.append(s)
raw_indices.append(r)
# 強度の正規化(ref描画用に0〜1へ)
if normalize and intensities:
maxI = max(intensities)
if maxI > 0:
intensities = [float(I) / float(maxI) for I in intensities]
else:
intensities = [0.0 for _ in intensities]
reference_name = os.path.splitext(os.path.basename(path))[0]
return reference_name, positions, intensities, hkls, raw_indices
[ドキュメント]
def get_supported_file_filters() -> str:
"""
概要: ロードされたプラグインがサポートするファイル形式のフィルター文字列を生成する。
詳細説明:
アプリケーションにロードされているすべてのデータフィルタープラグインに対し、
それぞれが対応するファイルタイプの説明と拡張子を取得する。
これらの情報をもとに、ファイル選択ダイアログなどで使用できる
統一されたフィルター文字列(例: "CIFファイル (*.cif);;テキストファイル (*.txt);;全てのファイル (*.*)")
を生成して返す。重複するフィルターは排除され、プラグインのロード順に基づいた一意なフィルターのみが提供される。
:returns:
サポートされるファイルタイプを記述したセミコロン区切りのフィルター文字列。
プラグインがロードされていない場合は、デフォルトのフィルター文字列を返す。
:rtype: str
"""
if not modules:
return "テキストファイル (*.txt);;全てのファイル (*.*)"
filters = []
for m in modules:
try:
input_type_dict = m.get_input_type(app=app, cparams=cparams)
if not isinstance(input_type_dict, dict):
continue
desc, ext = None, None
if 'file_type' in input_type_dict:
file_type_str = input_type_dict['file_type'].strip()
last_space_index = file_type_str.rfind(' ')
if last_space_index != -1 and '.' in file_type_str[last_space_index:]:
desc = file_type_str[:last_space_index].strip()
ext_part = file_type_str[last_space_index+1:].strip()
ext = f"*{ext_part}"
else:
desc = file_type_str
# ファイルタイプ文字列から拡張子を推測
ext_candidate = file_type_str.split()[-1].lower().lstrip('.')
if ext_candidate and not ext_candidate.startswith('*.'):
ext = f"*.{ext_candidate}"
else:
ext = f"*.{ext_candidate.lstrip('*').lstrip('.')}" # Fallback
elif 'description' in input_type_dict and 'extension' in input_type_dict:
desc = input_type_dict['description']
ext = input_type_dict['extension']
if desc and ext:
filters.append(f"{desc} ({ext})")
except Exception as e:
print(f"フィルター取得エラー ({m.name}): {e}")
if filters:
# 挿入順を保持しつつユニークなフィルターを生成 (Python 3.7+ 辞書の順序保持を利用)
unique_filters = list(dict.fromkeys(filters))
return ";;".join(unique_filters) + ";;全てのファイル (*.*)"
else:
return "全てのファイル (*.*)"
[ドキュメント]
def main():
"""
概要: モジュールのテスト実行エントリポイント。
詳細説明:
`XRD_GUI_lib.py` が直接Pythonインタープリタで実行された場合に呼び出される関数。
サンプルデータファイルの読み込みと解析のテストを行うためのコードが含まれている。
現在はコメントアウトされたテストパスが含まれており、必要に応じてパスを修正して実行できる。
:returns: None
:rtype: None
"""
# infile = "D:/git/tkProg/tkprog_COE/XRD/data/phase1.cif"
# infile = "D:/git/tkProg/tkprog_COE/XRD/data/Bi_R-3m.xlsx"
infile = "D:/git/tkProg/tkprog_COE/XRD/data/240219_AlScN_300_5h_oradw_25_Al100.txt"
parse_xrd(infile)
# infile = "D:/git/tkProg/tkprog_COE/XRD/data/phase1.cif"
# parse_reference(infile)
exit()
if __name__ == "__main__":
main()