XRD.XRD_GUI.XRD_GUI_lib のソースコード

"""
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()